162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org), 462306a36Sopenharmony_ci * IBM Corp. 562306a36Sopenharmony_ci */ 662306a36Sopenharmony_ci 762306a36Sopenharmony_ci#undef DEBUG 862306a36Sopenharmony_ci 962306a36Sopenharmony_ci#include <linux/kernel.h> 1062306a36Sopenharmony_ci#include <linux/pci.h> 1162306a36Sopenharmony_ci#include <linux/delay.h> 1262306a36Sopenharmony_ci#include <linux/string.h> 1362306a36Sopenharmony_ci#include <linux/init.h> 1462306a36Sopenharmony_ci#include <linux/irq.h> 1562306a36Sopenharmony_ci#include <linux/of_irq.h> 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <asm/sections.h> 1862306a36Sopenharmony_ci#include <asm/io.h> 1962306a36Sopenharmony_ci#include <asm/pci-bridge.h> 2062306a36Sopenharmony_ci#include <asm/machdep.h> 2162306a36Sopenharmony_ci#include <asm/iommu.h> 2262306a36Sopenharmony_ci#include <asm/ppc-pci.h> 2362306a36Sopenharmony_ci#include <asm/isa-bridge.h> 2462306a36Sopenharmony_ci 2562306a36Sopenharmony_ci#include "maple.h" 2662306a36Sopenharmony_ci 2762306a36Sopenharmony_ci#ifdef DEBUG 2862306a36Sopenharmony_ci#define DBG(x...) printk(x) 2962306a36Sopenharmony_ci#else 3062306a36Sopenharmony_ci#define DBG(x...) 3162306a36Sopenharmony_ci#endif 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_cistatic struct pci_controller *u3_agp, *u3_ht, *u4_pcie; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_cistatic int __init fixup_one_level_bus_range(struct device_node *node, int higher) 3662306a36Sopenharmony_ci{ 3762306a36Sopenharmony_ci for (; node; node = node->sibling) { 3862306a36Sopenharmony_ci const int *bus_range; 3962306a36Sopenharmony_ci const unsigned int *class_code; 4062306a36Sopenharmony_ci int len; 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci /* For PCI<->PCI bridges or CardBus bridges, we go down */ 4362306a36Sopenharmony_ci class_code = of_get_property(node, "class-code", NULL); 4462306a36Sopenharmony_ci if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && 4562306a36Sopenharmony_ci (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) 4662306a36Sopenharmony_ci continue; 4762306a36Sopenharmony_ci bus_range = of_get_property(node, "bus-range", &len); 4862306a36Sopenharmony_ci if (bus_range != NULL && len > 2 * sizeof(int)) { 4962306a36Sopenharmony_ci if (bus_range[1] > higher) 5062306a36Sopenharmony_ci higher = bus_range[1]; 5162306a36Sopenharmony_ci } 5262306a36Sopenharmony_ci higher = fixup_one_level_bus_range(node->child, higher); 5362306a36Sopenharmony_ci } 5462306a36Sopenharmony_ci return higher; 5562306a36Sopenharmony_ci} 5662306a36Sopenharmony_ci 5762306a36Sopenharmony_ci/* This routine fixes the "bus-range" property of all bridges in the 5862306a36Sopenharmony_ci * system since they tend to have their "last" member wrong on macs 5962306a36Sopenharmony_ci * 6062306a36Sopenharmony_ci * Note that the bus numbers manipulated here are OF bus numbers, they 6162306a36Sopenharmony_ci * are not Linux bus numbers. 6262306a36Sopenharmony_ci */ 6362306a36Sopenharmony_cistatic void __init fixup_bus_range(struct device_node *bridge) 6462306a36Sopenharmony_ci{ 6562306a36Sopenharmony_ci int *bus_range; 6662306a36Sopenharmony_ci struct property *prop; 6762306a36Sopenharmony_ci int len; 6862306a36Sopenharmony_ci 6962306a36Sopenharmony_ci /* Lookup the "bus-range" property for the hose */ 7062306a36Sopenharmony_ci prop = of_find_property(bridge, "bus-range", &len); 7162306a36Sopenharmony_ci if (prop == NULL || prop->value == NULL || len < 2 * sizeof(int)) { 7262306a36Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF\n", 7362306a36Sopenharmony_ci bridge); 7462306a36Sopenharmony_ci return; 7562306a36Sopenharmony_ci } 7662306a36Sopenharmony_ci bus_range = prop->value; 7762306a36Sopenharmony_ci bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); 7862306a36Sopenharmony_ci} 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_cistatic unsigned long u3_agp_cfa0(u8 devfn, u8 off) 8262306a36Sopenharmony_ci{ 8362306a36Sopenharmony_ci return (1 << (unsigned long)PCI_SLOT(devfn)) | 8462306a36Sopenharmony_ci ((unsigned long)PCI_FUNC(devfn) << 8) | 8562306a36Sopenharmony_ci ((unsigned long)off & 0xFCUL); 8662306a36Sopenharmony_ci} 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_cistatic unsigned long u3_agp_cfa1(u8 bus, u8 devfn, u8 off) 8962306a36Sopenharmony_ci{ 9062306a36Sopenharmony_ci return ((unsigned long)bus << 16) | 9162306a36Sopenharmony_ci ((unsigned long)devfn << 8) | 9262306a36Sopenharmony_ci ((unsigned long)off & 0xFCUL) | 9362306a36Sopenharmony_ci 1UL; 9462306a36Sopenharmony_ci} 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_cistatic volatile void __iomem *u3_agp_cfg_access(struct pci_controller* hose, 9762306a36Sopenharmony_ci u8 bus, u8 dev_fn, u8 offset) 9862306a36Sopenharmony_ci{ 9962306a36Sopenharmony_ci unsigned int caddr; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci if (bus == hose->first_busno) { 10262306a36Sopenharmony_ci if (dev_fn < (11 << 3)) 10362306a36Sopenharmony_ci return NULL; 10462306a36Sopenharmony_ci caddr = u3_agp_cfa0(dev_fn, offset); 10562306a36Sopenharmony_ci } else 10662306a36Sopenharmony_ci caddr = u3_agp_cfa1(bus, dev_fn, offset); 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci /* Uninorth will return garbage if we don't read back the value ! */ 10962306a36Sopenharmony_ci do { 11062306a36Sopenharmony_ci out_le32(hose->cfg_addr, caddr); 11162306a36Sopenharmony_ci } while (in_le32(hose->cfg_addr) != caddr); 11262306a36Sopenharmony_ci 11362306a36Sopenharmony_ci offset &= 0x07; 11462306a36Sopenharmony_ci return hose->cfg_data + offset; 11562306a36Sopenharmony_ci} 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_cistatic int u3_agp_read_config(struct pci_bus *bus, unsigned int devfn, 11862306a36Sopenharmony_ci int offset, int len, u32 *val) 11962306a36Sopenharmony_ci{ 12062306a36Sopenharmony_ci struct pci_controller *hose; 12162306a36Sopenharmony_ci volatile void __iomem *addr; 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 12462306a36Sopenharmony_ci if (hose == NULL) 12562306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ci addr = u3_agp_cfg_access(hose, bus->number, devfn, offset); 12862306a36Sopenharmony_ci if (!addr) 12962306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 13062306a36Sopenharmony_ci /* 13162306a36Sopenharmony_ci * Note: the caller has already checked that offset is 13262306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 13362306a36Sopenharmony_ci */ 13462306a36Sopenharmony_ci switch (len) { 13562306a36Sopenharmony_ci case 1: 13662306a36Sopenharmony_ci *val = in_8(addr); 13762306a36Sopenharmony_ci break; 13862306a36Sopenharmony_ci case 2: 13962306a36Sopenharmony_ci *val = in_le16(addr); 14062306a36Sopenharmony_ci break; 14162306a36Sopenharmony_ci default: 14262306a36Sopenharmony_ci *val = in_le32(addr); 14362306a36Sopenharmony_ci break; 14462306a36Sopenharmony_ci } 14562306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 14662306a36Sopenharmony_ci} 14762306a36Sopenharmony_ci 14862306a36Sopenharmony_cistatic int u3_agp_write_config(struct pci_bus *bus, unsigned int devfn, 14962306a36Sopenharmony_ci int offset, int len, u32 val) 15062306a36Sopenharmony_ci{ 15162306a36Sopenharmony_ci struct pci_controller *hose; 15262306a36Sopenharmony_ci volatile void __iomem *addr; 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 15562306a36Sopenharmony_ci if (hose == NULL) 15662306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 15762306a36Sopenharmony_ci 15862306a36Sopenharmony_ci addr = u3_agp_cfg_access(hose, bus->number, devfn, offset); 15962306a36Sopenharmony_ci if (!addr) 16062306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 16162306a36Sopenharmony_ci /* 16262306a36Sopenharmony_ci * Note: the caller has already checked that offset is 16362306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 16462306a36Sopenharmony_ci */ 16562306a36Sopenharmony_ci switch (len) { 16662306a36Sopenharmony_ci case 1: 16762306a36Sopenharmony_ci out_8(addr, val); 16862306a36Sopenharmony_ci break; 16962306a36Sopenharmony_ci case 2: 17062306a36Sopenharmony_ci out_le16(addr, val); 17162306a36Sopenharmony_ci break; 17262306a36Sopenharmony_ci default: 17362306a36Sopenharmony_ci out_le32(addr, val); 17462306a36Sopenharmony_ci break; 17562306a36Sopenharmony_ci } 17662306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 17762306a36Sopenharmony_ci} 17862306a36Sopenharmony_ci 17962306a36Sopenharmony_cistatic struct pci_ops u3_agp_pci_ops = 18062306a36Sopenharmony_ci{ 18162306a36Sopenharmony_ci .read = u3_agp_read_config, 18262306a36Sopenharmony_ci .write = u3_agp_write_config, 18362306a36Sopenharmony_ci}; 18462306a36Sopenharmony_ci 18562306a36Sopenharmony_cistatic unsigned long u3_ht_cfa0(u8 devfn, u8 off) 18662306a36Sopenharmony_ci{ 18762306a36Sopenharmony_ci return (devfn << 8) | off; 18862306a36Sopenharmony_ci} 18962306a36Sopenharmony_ci 19062306a36Sopenharmony_cistatic unsigned long u3_ht_cfa1(u8 bus, u8 devfn, u8 off) 19162306a36Sopenharmony_ci{ 19262306a36Sopenharmony_ci return u3_ht_cfa0(devfn, off) + (bus << 16) + 0x01000000UL; 19362306a36Sopenharmony_ci} 19462306a36Sopenharmony_ci 19562306a36Sopenharmony_cistatic volatile void __iomem *u3_ht_cfg_access(struct pci_controller* hose, 19662306a36Sopenharmony_ci u8 bus, u8 devfn, u8 offset) 19762306a36Sopenharmony_ci{ 19862306a36Sopenharmony_ci if (bus == hose->first_busno) { 19962306a36Sopenharmony_ci if (PCI_SLOT(devfn) == 0) 20062306a36Sopenharmony_ci return NULL; 20162306a36Sopenharmony_ci return hose->cfg_data + u3_ht_cfa0(devfn, offset); 20262306a36Sopenharmony_ci } else 20362306a36Sopenharmony_ci return hose->cfg_data + u3_ht_cfa1(bus, devfn, offset); 20462306a36Sopenharmony_ci} 20562306a36Sopenharmony_ci 20662306a36Sopenharmony_cistatic int u3_ht_root_read_config(struct pci_controller *hose, u8 offset, 20762306a36Sopenharmony_ci int len, u32 *val) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci volatile void __iomem *addr; 21062306a36Sopenharmony_ci 21162306a36Sopenharmony_ci addr = hose->cfg_addr; 21262306a36Sopenharmony_ci addr += ((offset & ~3) << 2) + (4 - len - (offset & 3)); 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci switch (len) { 21562306a36Sopenharmony_ci case 1: 21662306a36Sopenharmony_ci *val = in_8(addr); 21762306a36Sopenharmony_ci break; 21862306a36Sopenharmony_ci case 2: 21962306a36Sopenharmony_ci *val = in_be16(addr); 22062306a36Sopenharmony_ci break; 22162306a36Sopenharmony_ci default: 22262306a36Sopenharmony_ci *val = in_be32(addr); 22362306a36Sopenharmony_ci break; 22462306a36Sopenharmony_ci } 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 22762306a36Sopenharmony_ci} 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_cistatic int u3_ht_root_write_config(struct pci_controller *hose, u8 offset, 23062306a36Sopenharmony_ci int len, u32 val) 23162306a36Sopenharmony_ci{ 23262306a36Sopenharmony_ci volatile void __iomem *addr; 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_ci addr = hose->cfg_addr + ((offset & ~3) << 2) + (4 - len - (offset & 3)); 23562306a36Sopenharmony_ci 23662306a36Sopenharmony_ci if (offset >= PCI_BASE_ADDRESS_0 && offset < PCI_CAPABILITY_LIST) 23762306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 23862306a36Sopenharmony_ci 23962306a36Sopenharmony_ci switch (len) { 24062306a36Sopenharmony_ci case 1: 24162306a36Sopenharmony_ci out_8(addr, val); 24262306a36Sopenharmony_ci break; 24362306a36Sopenharmony_ci case 2: 24462306a36Sopenharmony_ci out_be16(addr, val); 24562306a36Sopenharmony_ci break; 24662306a36Sopenharmony_ci default: 24762306a36Sopenharmony_ci out_be32(addr, val); 24862306a36Sopenharmony_ci break; 24962306a36Sopenharmony_ci } 25062306a36Sopenharmony_ci 25162306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 25262306a36Sopenharmony_ci} 25362306a36Sopenharmony_ci 25462306a36Sopenharmony_cistatic int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, 25562306a36Sopenharmony_ci int offset, int len, u32 *val) 25662306a36Sopenharmony_ci{ 25762306a36Sopenharmony_ci struct pci_controller *hose; 25862306a36Sopenharmony_ci volatile void __iomem *addr; 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 26162306a36Sopenharmony_ci if (hose == NULL) 26262306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 26362306a36Sopenharmony_ci 26462306a36Sopenharmony_ci if (bus->number == hose->first_busno && devfn == PCI_DEVFN(0, 0)) 26562306a36Sopenharmony_ci return u3_ht_root_read_config(hose, offset, len, val); 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci if (offset > 0xff) 26862306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); 27162306a36Sopenharmony_ci if (!addr) 27262306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci /* 27562306a36Sopenharmony_ci * Note: the caller has already checked that offset is 27662306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 27762306a36Sopenharmony_ci */ 27862306a36Sopenharmony_ci switch (len) { 27962306a36Sopenharmony_ci case 1: 28062306a36Sopenharmony_ci *val = in_8(addr); 28162306a36Sopenharmony_ci break; 28262306a36Sopenharmony_ci case 2: 28362306a36Sopenharmony_ci *val = in_le16(addr); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci default: 28662306a36Sopenharmony_ci *val = in_le32(addr); 28762306a36Sopenharmony_ci break; 28862306a36Sopenharmony_ci } 28962306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 29062306a36Sopenharmony_ci} 29162306a36Sopenharmony_ci 29262306a36Sopenharmony_cistatic int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, 29362306a36Sopenharmony_ci int offset, int len, u32 val) 29462306a36Sopenharmony_ci{ 29562306a36Sopenharmony_ci struct pci_controller *hose; 29662306a36Sopenharmony_ci volatile void __iomem *addr; 29762306a36Sopenharmony_ci 29862306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 29962306a36Sopenharmony_ci if (hose == NULL) 30062306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 30162306a36Sopenharmony_ci 30262306a36Sopenharmony_ci if (bus->number == hose->first_busno && devfn == PCI_DEVFN(0, 0)) 30362306a36Sopenharmony_ci return u3_ht_root_write_config(hose, offset, len, val); 30462306a36Sopenharmony_ci 30562306a36Sopenharmony_ci if (offset > 0xff) 30662306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 30762306a36Sopenharmony_ci 30862306a36Sopenharmony_ci addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); 30962306a36Sopenharmony_ci if (!addr) 31062306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 31162306a36Sopenharmony_ci /* 31262306a36Sopenharmony_ci * Note: the caller has already checked that offset is 31362306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 31462306a36Sopenharmony_ci */ 31562306a36Sopenharmony_ci switch (len) { 31662306a36Sopenharmony_ci case 1: 31762306a36Sopenharmony_ci out_8(addr, val); 31862306a36Sopenharmony_ci break; 31962306a36Sopenharmony_ci case 2: 32062306a36Sopenharmony_ci out_le16(addr, val); 32162306a36Sopenharmony_ci break; 32262306a36Sopenharmony_ci default: 32362306a36Sopenharmony_ci out_le32(addr, val); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci } 32662306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 32762306a36Sopenharmony_ci} 32862306a36Sopenharmony_ci 32962306a36Sopenharmony_cistatic struct pci_ops u3_ht_pci_ops = 33062306a36Sopenharmony_ci{ 33162306a36Sopenharmony_ci .read = u3_ht_read_config, 33262306a36Sopenharmony_ci .write = u3_ht_write_config, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_cistatic unsigned int u4_pcie_cfa0(unsigned int devfn, unsigned int off) 33662306a36Sopenharmony_ci{ 33762306a36Sopenharmony_ci return (1 << PCI_SLOT(devfn)) | 33862306a36Sopenharmony_ci (PCI_FUNC(devfn) << 8) | 33962306a36Sopenharmony_ci ((off >> 8) << 28) | 34062306a36Sopenharmony_ci (off & 0xfcu); 34162306a36Sopenharmony_ci} 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic unsigned int u4_pcie_cfa1(unsigned int bus, unsigned int devfn, 34462306a36Sopenharmony_ci unsigned int off) 34562306a36Sopenharmony_ci{ 34662306a36Sopenharmony_ci return (bus << 16) | 34762306a36Sopenharmony_ci (devfn << 8) | 34862306a36Sopenharmony_ci ((off >> 8) << 28) | 34962306a36Sopenharmony_ci (off & 0xfcu) | 1u; 35062306a36Sopenharmony_ci} 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_cistatic volatile void __iomem *u4_pcie_cfg_access(struct pci_controller* hose, 35362306a36Sopenharmony_ci u8 bus, u8 dev_fn, int offset) 35462306a36Sopenharmony_ci{ 35562306a36Sopenharmony_ci unsigned int caddr; 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci if (bus == hose->first_busno) 35862306a36Sopenharmony_ci caddr = u4_pcie_cfa0(dev_fn, offset); 35962306a36Sopenharmony_ci else 36062306a36Sopenharmony_ci caddr = u4_pcie_cfa1(bus, dev_fn, offset); 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci /* Uninorth will return garbage if we don't read back the value ! */ 36362306a36Sopenharmony_ci do { 36462306a36Sopenharmony_ci out_le32(hose->cfg_addr, caddr); 36562306a36Sopenharmony_ci } while (in_le32(hose->cfg_addr) != caddr); 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci offset &= 0x03; 36862306a36Sopenharmony_ci return hose->cfg_data + offset; 36962306a36Sopenharmony_ci} 37062306a36Sopenharmony_ci 37162306a36Sopenharmony_cistatic int u4_pcie_read_config(struct pci_bus *bus, unsigned int devfn, 37262306a36Sopenharmony_ci int offset, int len, u32 *val) 37362306a36Sopenharmony_ci{ 37462306a36Sopenharmony_ci struct pci_controller *hose; 37562306a36Sopenharmony_ci volatile void __iomem *addr; 37662306a36Sopenharmony_ci 37762306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 37862306a36Sopenharmony_ci if (hose == NULL) 37962306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 38062306a36Sopenharmony_ci if (offset >= 0x1000) 38162306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 38262306a36Sopenharmony_ci addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); 38362306a36Sopenharmony_ci if (!addr) 38462306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 38562306a36Sopenharmony_ci /* 38662306a36Sopenharmony_ci * Note: the caller has already checked that offset is 38762306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_ci switch (len) { 39062306a36Sopenharmony_ci case 1: 39162306a36Sopenharmony_ci *val = in_8(addr); 39262306a36Sopenharmony_ci break; 39362306a36Sopenharmony_ci case 2: 39462306a36Sopenharmony_ci *val = in_le16(addr); 39562306a36Sopenharmony_ci break; 39662306a36Sopenharmony_ci default: 39762306a36Sopenharmony_ci *val = in_le32(addr); 39862306a36Sopenharmony_ci break; 39962306a36Sopenharmony_ci } 40062306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 40162306a36Sopenharmony_ci} 40262306a36Sopenharmony_cistatic int u4_pcie_write_config(struct pci_bus *bus, unsigned int devfn, 40362306a36Sopenharmony_ci int offset, int len, u32 val) 40462306a36Sopenharmony_ci{ 40562306a36Sopenharmony_ci struct pci_controller *hose; 40662306a36Sopenharmony_ci volatile void __iomem *addr; 40762306a36Sopenharmony_ci 40862306a36Sopenharmony_ci hose = pci_bus_to_host(bus); 40962306a36Sopenharmony_ci if (hose == NULL) 41062306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 41162306a36Sopenharmony_ci if (offset >= 0x1000) 41262306a36Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 41362306a36Sopenharmony_ci addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); 41462306a36Sopenharmony_ci if (!addr) 41562306a36Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 41662306a36Sopenharmony_ci /* 41762306a36Sopenharmony_ci * Note: the caller has already checked that offset is 41862306a36Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 41962306a36Sopenharmony_ci */ 42062306a36Sopenharmony_ci switch (len) { 42162306a36Sopenharmony_ci case 1: 42262306a36Sopenharmony_ci out_8(addr, val); 42362306a36Sopenharmony_ci break; 42462306a36Sopenharmony_ci case 2: 42562306a36Sopenharmony_ci out_le16(addr, val); 42662306a36Sopenharmony_ci break; 42762306a36Sopenharmony_ci default: 42862306a36Sopenharmony_ci out_le32(addr, val); 42962306a36Sopenharmony_ci break; 43062306a36Sopenharmony_ci } 43162306a36Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 43262306a36Sopenharmony_ci} 43362306a36Sopenharmony_ci 43462306a36Sopenharmony_cistatic struct pci_ops u4_pcie_pci_ops = 43562306a36Sopenharmony_ci{ 43662306a36Sopenharmony_ci .read = u4_pcie_read_config, 43762306a36Sopenharmony_ci .write = u4_pcie_write_config, 43862306a36Sopenharmony_ci}; 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_cistatic void __init setup_u3_agp(struct pci_controller* hose) 44162306a36Sopenharmony_ci{ 44262306a36Sopenharmony_ci /* On G5, we move AGP up to high bus number so we don't need 44362306a36Sopenharmony_ci * to reassign bus numbers for HT. If we ever have P2P bridges 44462306a36Sopenharmony_ci * on AGP, we'll have to move pci_assign_all_buses to the 44562306a36Sopenharmony_ci * pci_controller structure so we enable it for AGP and not for 44662306a36Sopenharmony_ci * HT childs. 44762306a36Sopenharmony_ci * We hard code the address because of the different size of 44862306a36Sopenharmony_ci * the reg address cell, we shall fix that by killing struct 44962306a36Sopenharmony_ci * reg_property and using some accessor functions instead 45062306a36Sopenharmony_ci */ 45162306a36Sopenharmony_ci hose->first_busno = 0xf0; 45262306a36Sopenharmony_ci hose->last_busno = 0xff; 45362306a36Sopenharmony_ci hose->ops = &u3_agp_pci_ops; 45462306a36Sopenharmony_ci hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); 45562306a36Sopenharmony_ci hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci u3_agp = hose; 45862306a36Sopenharmony_ci} 45962306a36Sopenharmony_ci 46062306a36Sopenharmony_cistatic void __init setup_u4_pcie(struct pci_controller* hose) 46162306a36Sopenharmony_ci{ 46262306a36Sopenharmony_ci /* We currently only implement the "non-atomic" config space, to 46362306a36Sopenharmony_ci * be optimised later. 46462306a36Sopenharmony_ci */ 46562306a36Sopenharmony_ci hose->ops = &u4_pcie_pci_ops; 46662306a36Sopenharmony_ci hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); 46762306a36Sopenharmony_ci hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); 46862306a36Sopenharmony_ci 46962306a36Sopenharmony_ci u4_pcie = hose; 47062306a36Sopenharmony_ci} 47162306a36Sopenharmony_ci 47262306a36Sopenharmony_cistatic void __init setup_u3_ht(struct pci_controller* hose) 47362306a36Sopenharmony_ci{ 47462306a36Sopenharmony_ci hose->ops = &u3_ht_pci_ops; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci /* We hard code the address because of the different size of 47762306a36Sopenharmony_ci * the reg address cell, we shall fix that by killing struct 47862306a36Sopenharmony_ci * reg_property and using some accessor functions instead 47962306a36Sopenharmony_ci */ 48062306a36Sopenharmony_ci hose->cfg_data = ioremap(0xf2000000, 0x02000000); 48162306a36Sopenharmony_ci hose->cfg_addr = ioremap(0xf8070000, 0x1000); 48262306a36Sopenharmony_ci 48362306a36Sopenharmony_ci hose->first_busno = 0; 48462306a36Sopenharmony_ci hose->last_busno = 0xef; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci u3_ht = hose; 48762306a36Sopenharmony_ci} 48862306a36Sopenharmony_ci 48962306a36Sopenharmony_cistatic int __init maple_add_bridge(struct device_node *dev) 49062306a36Sopenharmony_ci{ 49162306a36Sopenharmony_ci int len; 49262306a36Sopenharmony_ci struct pci_controller *hose; 49362306a36Sopenharmony_ci char* disp_name; 49462306a36Sopenharmony_ci const int *bus_range; 49562306a36Sopenharmony_ci int primary = 1; 49662306a36Sopenharmony_ci 49762306a36Sopenharmony_ci DBG("Adding PCI host bridge %pOF\n", dev); 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci bus_range = of_get_property(dev, "bus-range", &len); 50062306a36Sopenharmony_ci if (bus_range == NULL || len < 2 * sizeof(int)) { 50162306a36Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF, assume bus 0\n", 50262306a36Sopenharmony_ci dev); 50362306a36Sopenharmony_ci } 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci hose = pcibios_alloc_controller(dev); 50662306a36Sopenharmony_ci if (hose == NULL) 50762306a36Sopenharmony_ci return -ENOMEM; 50862306a36Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0; 50962306a36Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 51062306a36Sopenharmony_ci hose->controller_ops = maple_pci_controller_ops; 51162306a36Sopenharmony_ci 51262306a36Sopenharmony_ci disp_name = NULL; 51362306a36Sopenharmony_ci if (of_device_is_compatible(dev, "u3-agp")) { 51462306a36Sopenharmony_ci setup_u3_agp(hose); 51562306a36Sopenharmony_ci disp_name = "U3-AGP"; 51662306a36Sopenharmony_ci primary = 0; 51762306a36Sopenharmony_ci } else if (of_device_is_compatible(dev, "u3-ht")) { 51862306a36Sopenharmony_ci setup_u3_ht(hose); 51962306a36Sopenharmony_ci disp_name = "U3-HT"; 52062306a36Sopenharmony_ci primary = 1; 52162306a36Sopenharmony_ci } else if (of_device_is_compatible(dev, "u4-pcie")) { 52262306a36Sopenharmony_ci setup_u4_pcie(hose); 52362306a36Sopenharmony_ci disp_name = "U4-PCIE"; 52462306a36Sopenharmony_ci primary = 0; 52562306a36Sopenharmony_ci } 52662306a36Sopenharmony_ci printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n", 52762306a36Sopenharmony_ci disp_name, hose->first_busno, hose->last_busno); 52862306a36Sopenharmony_ci 52962306a36Sopenharmony_ci /* Interpret the "ranges" property */ 53062306a36Sopenharmony_ci /* This also maps the I/O region and sets isa_io/mem_base */ 53162306a36Sopenharmony_ci pci_process_bridge_OF_ranges(hose, dev, primary); 53262306a36Sopenharmony_ci 53362306a36Sopenharmony_ci /* Fixup "bus-range" OF property */ 53462306a36Sopenharmony_ci fixup_bus_range(dev); 53562306a36Sopenharmony_ci 53662306a36Sopenharmony_ci /* Check for legacy IOs */ 53762306a36Sopenharmony_ci isa_bridge_find_early(hose); 53862306a36Sopenharmony_ci 53962306a36Sopenharmony_ci /* create pci_dn's for DT nodes under this PHB */ 54062306a36Sopenharmony_ci pci_devs_phb_init_dynamic(hose); 54162306a36Sopenharmony_ci 54262306a36Sopenharmony_ci return 0; 54362306a36Sopenharmony_ci} 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci 54662306a36Sopenharmony_civoid maple_pci_irq_fixup(struct pci_dev *dev) 54762306a36Sopenharmony_ci{ 54862306a36Sopenharmony_ci DBG(" -> maple_pci_irq_fixup\n"); 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci /* Fixup IRQ for PCIe host */ 55162306a36Sopenharmony_ci if (u4_pcie != NULL && dev->bus->number == 0 && 55262306a36Sopenharmony_ci pci_bus_to_host(dev->bus) == u4_pcie) { 55362306a36Sopenharmony_ci printk(KERN_DEBUG "Fixup U4 PCIe IRQ\n"); 55462306a36Sopenharmony_ci dev->irq = irq_create_mapping(NULL, 1); 55562306a36Sopenharmony_ci if (dev->irq) 55662306a36Sopenharmony_ci irq_set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW); 55762306a36Sopenharmony_ci } 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci /* Hide AMD8111 IDE interrupt when in legacy mode so 56062306a36Sopenharmony_ci * the driver calls pci_get_legacy_ide_irq() 56162306a36Sopenharmony_ci */ 56262306a36Sopenharmony_ci if (dev->vendor == PCI_VENDOR_ID_AMD && 56362306a36Sopenharmony_ci dev->device == PCI_DEVICE_ID_AMD_8111_IDE && 56462306a36Sopenharmony_ci (dev->class & 5) != 5) { 56562306a36Sopenharmony_ci dev->irq = 0; 56662306a36Sopenharmony_ci } 56762306a36Sopenharmony_ci 56862306a36Sopenharmony_ci DBG(" <- maple_pci_irq_fixup\n"); 56962306a36Sopenharmony_ci} 57062306a36Sopenharmony_ci 57162306a36Sopenharmony_cistatic int maple_pci_root_bridge_prepare(struct pci_host_bridge *bridge) 57262306a36Sopenharmony_ci{ 57362306a36Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bridge->bus); 57462306a36Sopenharmony_ci struct device_node *np, *child; 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci if (hose != u3_agp) 57762306a36Sopenharmony_ci return 0; 57862306a36Sopenharmony_ci 57962306a36Sopenharmony_ci /* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We 58062306a36Sopenharmony_ci * assume there is no P2P bridge on the AGP bus, which should be a 58162306a36Sopenharmony_ci * safe assumptions hopefully. 58262306a36Sopenharmony_ci */ 58362306a36Sopenharmony_ci np = hose->dn; 58462306a36Sopenharmony_ci PCI_DN(np)->busno = 0xf0; 58562306a36Sopenharmony_ci for_each_child_of_node(np, child) 58662306a36Sopenharmony_ci PCI_DN(child)->busno = 0xf0; 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_ci return 0; 58962306a36Sopenharmony_ci} 59062306a36Sopenharmony_ci 59162306a36Sopenharmony_civoid __init maple_pci_init(void) 59262306a36Sopenharmony_ci{ 59362306a36Sopenharmony_ci struct device_node *np, *root; 59462306a36Sopenharmony_ci struct device_node *ht = NULL; 59562306a36Sopenharmony_ci 59662306a36Sopenharmony_ci /* Probe root PCI hosts, that is on U3 the AGP host and the 59762306a36Sopenharmony_ci * HyperTransport host. That one is actually "kept" around 59862306a36Sopenharmony_ci * and actually added last as it's resource management relies 59962306a36Sopenharmony_ci * on the AGP resources to have been setup first 60062306a36Sopenharmony_ci */ 60162306a36Sopenharmony_ci root = of_find_node_by_path("/"); 60262306a36Sopenharmony_ci if (root == NULL) { 60362306a36Sopenharmony_ci printk(KERN_CRIT "maple_find_bridges: can't find root of device tree\n"); 60462306a36Sopenharmony_ci return; 60562306a36Sopenharmony_ci } 60662306a36Sopenharmony_ci for_each_child_of_node(root, np) { 60762306a36Sopenharmony_ci if (!of_node_is_type(np, "pci") && !of_node_is_type(np, "ht")) 60862306a36Sopenharmony_ci continue; 60962306a36Sopenharmony_ci if ((of_device_is_compatible(np, "u4-pcie") || 61062306a36Sopenharmony_ci of_device_is_compatible(np, "u3-agp")) && 61162306a36Sopenharmony_ci maple_add_bridge(np) == 0) 61262306a36Sopenharmony_ci of_node_get(np); 61362306a36Sopenharmony_ci 61462306a36Sopenharmony_ci if (of_device_is_compatible(np, "u3-ht")) { 61562306a36Sopenharmony_ci of_node_get(np); 61662306a36Sopenharmony_ci ht = np; 61762306a36Sopenharmony_ci } 61862306a36Sopenharmony_ci } 61962306a36Sopenharmony_ci of_node_put(root); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci /* Now setup the HyperTransport host if we found any 62262306a36Sopenharmony_ci */ 62362306a36Sopenharmony_ci if (ht && maple_add_bridge(ht) != 0) 62462306a36Sopenharmony_ci of_node_put(ht); 62562306a36Sopenharmony_ci 62662306a36Sopenharmony_ci ppc_md.pcibios_root_bridge_prepare = maple_pci_root_bridge_prepare; 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_ci /* Tell pci.c to not change any resource allocations. */ 62962306a36Sopenharmony_ci pci_add_flags(PCI_PROBE_ONLY); 63062306a36Sopenharmony_ci} 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ciint maple_pci_get_legacy_ide_irq(struct pci_dev *pdev, int channel) 63362306a36Sopenharmony_ci{ 63462306a36Sopenharmony_ci struct device_node *np; 63562306a36Sopenharmony_ci unsigned int defirq = channel ? 15 : 14; 63662306a36Sopenharmony_ci unsigned int irq; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (pdev->vendor != PCI_VENDOR_ID_AMD || 63962306a36Sopenharmony_ci pdev->device != PCI_DEVICE_ID_AMD_8111_IDE) 64062306a36Sopenharmony_ci return defirq; 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_ci np = pci_device_to_OF_node(pdev); 64362306a36Sopenharmony_ci if (np == NULL) { 64462306a36Sopenharmony_ci printk("Failed to locate OF node for IDE %s\n", 64562306a36Sopenharmony_ci pci_name(pdev)); 64662306a36Sopenharmony_ci return defirq; 64762306a36Sopenharmony_ci } 64862306a36Sopenharmony_ci irq = irq_of_parse_and_map(np, channel & 0x1); 64962306a36Sopenharmony_ci if (!irq) { 65062306a36Sopenharmony_ci printk("Failed to map onboard IDE interrupt for channel %d\n", 65162306a36Sopenharmony_ci channel); 65262306a36Sopenharmony_ci return defirq; 65362306a36Sopenharmony_ci } 65462306a36Sopenharmony_ci return irq; 65562306a36Sopenharmony_ci} 65662306a36Sopenharmony_ci 65762306a36Sopenharmony_cistatic void quirk_ipr_msi(struct pci_dev *dev) 65862306a36Sopenharmony_ci{ 65962306a36Sopenharmony_ci /* Something prevents MSIs from the IPR from working on Bimini, 66062306a36Sopenharmony_ci * and the driver has no smarts to recover. So disable MSI 66162306a36Sopenharmony_ci * on it for now. */ 66262306a36Sopenharmony_ci 66362306a36Sopenharmony_ci if (machine_is(maple)) { 66462306a36Sopenharmony_ci dev->no_msi = 1; 66562306a36Sopenharmony_ci dev_info(&dev->dev, "Quirk disabled MSI\n"); 66662306a36Sopenharmony_ci } 66762306a36Sopenharmony_ci} 66862306a36Sopenharmony_ciDECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, 66962306a36Sopenharmony_ci quirk_ipr_msi); 67062306a36Sopenharmony_ci 67162306a36Sopenharmony_cistruct pci_controller_ops maple_pci_controller_ops = { 67262306a36Sopenharmony_ci}; 673