18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (C) 2004 Benjamin Herrenschmuidt (benh@kernel.crashing.org), 48c2ecf20Sopenharmony_ci * IBM Corp. 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#undef DEBUG 88c2ecf20Sopenharmony_ci 98c2ecf20Sopenharmony_ci#include <linux/kernel.h> 108c2ecf20Sopenharmony_ci#include <linux/pci.h> 118c2ecf20Sopenharmony_ci#include <linux/delay.h> 128c2ecf20Sopenharmony_ci#include <linux/string.h> 138c2ecf20Sopenharmony_ci#include <linux/init.h> 148c2ecf20Sopenharmony_ci#include <linux/irq.h> 158c2ecf20Sopenharmony_ci 168c2ecf20Sopenharmony_ci#include <asm/sections.h> 178c2ecf20Sopenharmony_ci#include <asm/io.h> 188c2ecf20Sopenharmony_ci#include <asm/prom.h> 198c2ecf20Sopenharmony_ci#include <asm/pci-bridge.h> 208c2ecf20Sopenharmony_ci#include <asm/machdep.h> 218c2ecf20Sopenharmony_ci#include <asm/iommu.h> 228c2ecf20Sopenharmony_ci#include <asm/ppc-pci.h> 238c2ecf20Sopenharmony_ci#include <asm/isa-bridge.h> 248c2ecf20Sopenharmony_ci 258c2ecf20Sopenharmony_ci#include "maple.h" 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_ci#ifdef DEBUG 288c2ecf20Sopenharmony_ci#define DBG(x...) printk(x) 298c2ecf20Sopenharmony_ci#else 308c2ecf20Sopenharmony_ci#define DBG(x...) 318c2ecf20Sopenharmony_ci#endif 328c2ecf20Sopenharmony_ci 338c2ecf20Sopenharmony_cistatic struct pci_controller *u3_agp, *u3_ht, *u4_pcie; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_cistatic int __init fixup_one_level_bus_range(struct device_node *node, int higher) 368c2ecf20Sopenharmony_ci{ 378c2ecf20Sopenharmony_ci for (; node != 0;node = node->sibling) { 388c2ecf20Sopenharmony_ci const int *bus_range; 398c2ecf20Sopenharmony_ci const unsigned int *class_code; 408c2ecf20Sopenharmony_ci int len; 418c2ecf20Sopenharmony_ci 428c2ecf20Sopenharmony_ci /* For PCI<->PCI bridges or CardBus bridges, we go down */ 438c2ecf20Sopenharmony_ci class_code = of_get_property(node, "class-code", NULL); 448c2ecf20Sopenharmony_ci if (!class_code || ((*class_code >> 8) != PCI_CLASS_BRIDGE_PCI && 458c2ecf20Sopenharmony_ci (*class_code >> 8) != PCI_CLASS_BRIDGE_CARDBUS)) 468c2ecf20Sopenharmony_ci continue; 478c2ecf20Sopenharmony_ci bus_range = of_get_property(node, "bus-range", &len); 488c2ecf20Sopenharmony_ci if (bus_range != NULL && len > 2 * sizeof(int)) { 498c2ecf20Sopenharmony_ci if (bus_range[1] > higher) 508c2ecf20Sopenharmony_ci higher = bus_range[1]; 518c2ecf20Sopenharmony_ci } 528c2ecf20Sopenharmony_ci higher = fixup_one_level_bus_range(node->child, higher); 538c2ecf20Sopenharmony_ci } 548c2ecf20Sopenharmony_ci return higher; 558c2ecf20Sopenharmony_ci} 568c2ecf20Sopenharmony_ci 578c2ecf20Sopenharmony_ci/* This routine fixes the "bus-range" property of all bridges in the 588c2ecf20Sopenharmony_ci * system since they tend to have their "last" member wrong on macs 598c2ecf20Sopenharmony_ci * 608c2ecf20Sopenharmony_ci * Note that the bus numbers manipulated here are OF bus numbers, they 618c2ecf20Sopenharmony_ci * are not Linux bus numbers. 628c2ecf20Sopenharmony_ci */ 638c2ecf20Sopenharmony_cistatic void __init fixup_bus_range(struct device_node *bridge) 648c2ecf20Sopenharmony_ci{ 658c2ecf20Sopenharmony_ci int *bus_range; 668c2ecf20Sopenharmony_ci struct property *prop; 678c2ecf20Sopenharmony_ci int len; 688c2ecf20Sopenharmony_ci 698c2ecf20Sopenharmony_ci /* Lookup the "bus-range" property for the hose */ 708c2ecf20Sopenharmony_ci prop = of_find_property(bridge, "bus-range", &len); 718c2ecf20Sopenharmony_ci if (prop == NULL || prop->value == NULL || len < 2 * sizeof(int)) { 728c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF\n", 738c2ecf20Sopenharmony_ci bridge); 748c2ecf20Sopenharmony_ci return; 758c2ecf20Sopenharmony_ci } 768c2ecf20Sopenharmony_ci bus_range = prop->value; 778c2ecf20Sopenharmony_ci bus_range[1] = fixup_one_level_bus_range(bridge->child, bus_range[1]); 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci 818c2ecf20Sopenharmony_cistatic unsigned long u3_agp_cfa0(u8 devfn, u8 off) 828c2ecf20Sopenharmony_ci{ 838c2ecf20Sopenharmony_ci return (1 << (unsigned long)PCI_SLOT(devfn)) | 848c2ecf20Sopenharmony_ci ((unsigned long)PCI_FUNC(devfn) << 8) | 858c2ecf20Sopenharmony_ci ((unsigned long)off & 0xFCUL); 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic unsigned long u3_agp_cfa1(u8 bus, u8 devfn, u8 off) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci return ((unsigned long)bus << 16) | 918c2ecf20Sopenharmony_ci ((unsigned long)devfn << 8) | 928c2ecf20Sopenharmony_ci ((unsigned long)off & 0xFCUL) | 938c2ecf20Sopenharmony_ci 1UL; 948c2ecf20Sopenharmony_ci} 958c2ecf20Sopenharmony_ci 968c2ecf20Sopenharmony_cistatic volatile void __iomem *u3_agp_cfg_access(struct pci_controller* hose, 978c2ecf20Sopenharmony_ci u8 bus, u8 dev_fn, u8 offset) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci unsigned int caddr; 1008c2ecf20Sopenharmony_ci 1018c2ecf20Sopenharmony_ci if (bus == hose->first_busno) { 1028c2ecf20Sopenharmony_ci if (dev_fn < (11 << 3)) 1038c2ecf20Sopenharmony_ci return NULL; 1048c2ecf20Sopenharmony_ci caddr = u3_agp_cfa0(dev_fn, offset); 1058c2ecf20Sopenharmony_ci } else 1068c2ecf20Sopenharmony_ci caddr = u3_agp_cfa1(bus, dev_fn, offset); 1078c2ecf20Sopenharmony_ci 1088c2ecf20Sopenharmony_ci /* Uninorth will return garbage if we don't read back the value ! */ 1098c2ecf20Sopenharmony_ci do { 1108c2ecf20Sopenharmony_ci out_le32(hose->cfg_addr, caddr); 1118c2ecf20Sopenharmony_ci } while (in_le32(hose->cfg_addr) != caddr); 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci offset &= 0x07; 1148c2ecf20Sopenharmony_ci return hose->cfg_data + offset; 1158c2ecf20Sopenharmony_ci} 1168c2ecf20Sopenharmony_ci 1178c2ecf20Sopenharmony_cistatic int u3_agp_read_config(struct pci_bus *bus, unsigned int devfn, 1188c2ecf20Sopenharmony_ci int offset, int len, u32 *val) 1198c2ecf20Sopenharmony_ci{ 1208c2ecf20Sopenharmony_ci struct pci_controller *hose; 1218c2ecf20Sopenharmony_ci volatile void __iomem *addr; 1228c2ecf20Sopenharmony_ci 1238c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 1248c2ecf20Sopenharmony_ci if (hose == NULL) 1258c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci addr = u3_agp_cfg_access(hose, bus->number, devfn, offset); 1288c2ecf20Sopenharmony_ci if (!addr) 1298c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1308c2ecf20Sopenharmony_ci /* 1318c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 1328c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 1338c2ecf20Sopenharmony_ci */ 1348c2ecf20Sopenharmony_ci switch (len) { 1358c2ecf20Sopenharmony_ci case 1: 1368c2ecf20Sopenharmony_ci *val = in_8(addr); 1378c2ecf20Sopenharmony_ci break; 1388c2ecf20Sopenharmony_ci case 2: 1398c2ecf20Sopenharmony_ci *val = in_le16(addr); 1408c2ecf20Sopenharmony_ci break; 1418c2ecf20Sopenharmony_ci default: 1428c2ecf20Sopenharmony_ci *val = in_le32(addr); 1438c2ecf20Sopenharmony_ci break; 1448c2ecf20Sopenharmony_ci } 1458c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_cistatic int u3_agp_write_config(struct pci_bus *bus, unsigned int devfn, 1498c2ecf20Sopenharmony_ci int offset, int len, u32 val) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci struct pci_controller *hose; 1528c2ecf20Sopenharmony_ci volatile void __iomem *addr; 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 1558c2ecf20Sopenharmony_ci if (hose == NULL) 1568c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1578c2ecf20Sopenharmony_ci 1588c2ecf20Sopenharmony_ci addr = u3_agp_cfg_access(hose, bus->number, devfn, offset); 1598c2ecf20Sopenharmony_ci if (!addr) 1608c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 1618c2ecf20Sopenharmony_ci /* 1628c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 1638c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 1648c2ecf20Sopenharmony_ci */ 1658c2ecf20Sopenharmony_ci switch (len) { 1668c2ecf20Sopenharmony_ci case 1: 1678c2ecf20Sopenharmony_ci out_8(addr, val); 1688c2ecf20Sopenharmony_ci break; 1698c2ecf20Sopenharmony_ci case 2: 1708c2ecf20Sopenharmony_ci out_le16(addr, val); 1718c2ecf20Sopenharmony_ci break; 1728c2ecf20Sopenharmony_ci default: 1738c2ecf20Sopenharmony_ci out_le32(addr, val); 1748c2ecf20Sopenharmony_ci break; 1758c2ecf20Sopenharmony_ci } 1768c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 1778c2ecf20Sopenharmony_ci} 1788c2ecf20Sopenharmony_ci 1798c2ecf20Sopenharmony_cistatic struct pci_ops u3_agp_pci_ops = 1808c2ecf20Sopenharmony_ci{ 1818c2ecf20Sopenharmony_ci .read = u3_agp_read_config, 1828c2ecf20Sopenharmony_ci .write = u3_agp_write_config, 1838c2ecf20Sopenharmony_ci}; 1848c2ecf20Sopenharmony_ci 1858c2ecf20Sopenharmony_cistatic unsigned long u3_ht_cfa0(u8 devfn, u8 off) 1868c2ecf20Sopenharmony_ci{ 1878c2ecf20Sopenharmony_ci return (devfn << 8) | off; 1888c2ecf20Sopenharmony_ci} 1898c2ecf20Sopenharmony_ci 1908c2ecf20Sopenharmony_cistatic unsigned long u3_ht_cfa1(u8 bus, u8 devfn, u8 off) 1918c2ecf20Sopenharmony_ci{ 1928c2ecf20Sopenharmony_ci return u3_ht_cfa0(devfn, off) + (bus << 16) + 0x01000000UL; 1938c2ecf20Sopenharmony_ci} 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_cistatic volatile void __iomem *u3_ht_cfg_access(struct pci_controller* hose, 1968c2ecf20Sopenharmony_ci u8 bus, u8 devfn, u8 offset) 1978c2ecf20Sopenharmony_ci{ 1988c2ecf20Sopenharmony_ci if (bus == hose->first_busno) { 1998c2ecf20Sopenharmony_ci if (PCI_SLOT(devfn) == 0) 2008c2ecf20Sopenharmony_ci return NULL; 2018c2ecf20Sopenharmony_ci return hose->cfg_data + u3_ht_cfa0(devfn, offset); 2028c2ecf20Sopenharmony_ci } else 2038c2ecf20Sopenharmony_ci return hose->cfg_data + u3_ht_cfa1(bus, devfn, offset); 2048c2ecf20Sopenharmony_ci} 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_cistatic int u3_ht_root_read_config(struct pci_controller *hose, u8 offset, 2078c2ecf20Sopenharmony_ci int len, u32 *val) 2088c2ecf20Sopenharmony_ci{ 2098c2ecf20Sopenharmony_ci volatile void __iomem *addr; 2108c2ecf20Sopenharmony_ci 2118c2ecf20Sopenharmony_ci addr = hose->cfg_addr; 2128c2ecf20Sopenharmony_ci addr += ((offset & ~3) << 2) + (4 - len - (offset & 3)); 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci switch (len) { 2158c2ecf20Sopenharmony_ci case 1: 2168c2ecf20Sopenharmony_ci *val = in_8(addr); 2178c2ecf20Sopenharmony_ci break; 2188c2ecf20Sopenharmony_ci case 2: 2198c2ecf20Sopenharmony_ci *val = in_be16(addr); 2208c2ecf20Sopenharmony_ci break; 2218c2ecf20Sopenharmony_ci default: 2228c2ecf20Sopenharmony_ci *val = in_be32(addr); 2238c2ecf20Sopenharmony_ci break; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2278c2ecf20Sopenharmony_ci} 2288c2ecf20Sopenharmony_ci 2298c2ecf20Sopenharmony_cistatic int u3_ht_root_write_config(struct pci_controller *hose, u8 offset, 2308c2ecf20Sopenharmony_ci int len, u32 val) 2318c2ecf20Sopenharmony_ci{ 2328c2ecf20Sopenharmony_ci volatile void __iomem *addr; 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci addr = hose->cfg_addr + ((offset & ~3) << 2) + (4 - len - (offset & 3)); 2358c2ecf20Sopenharmony_ci 2368c2ecf20Sopenharmony_ci if (offset >= PCI_BASE_ADDRESS_0 && offset < PCI_CAPABILITY_LIST) 2378c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci switch (len) { 2408c2ecf20Sopenharmony_ci case 1: 2418c2ecf20Sopenharmony_ci out_8(addr, val); 2428c2ecf20Sopenharmony_ci break; 2438c2ecf20Sopenharmony_ci case 2: 2448c2ecf20Sopenharmony_ci out_be16(addr, val); 2458c2ecf20Sopenharmony_ci break; 2468c2ecf20Sopenharmony_ci default: 2478c2ecf20Sopenharmony_ci out_be32(addr, val); 2488c2ecf20Sopenharmony_ci break; 2498c2ecf20Sopenharmony_ci } 2508c2ecf20Sopenharmony_ci 2518c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2528c2ecf20Sopenharmony_ci} 2538c2ecf20Sopenharmony_ci 2548c2ecf20Sopenharmony_cistatic int u3_ht_read_config(struct pci_bus *bus, unsigned int devfn, 2558c2ecf20Sopenharmony_ci int offset, int len, u32 *val) 2568c2ecf20Sopenharmony_ci{ 2578c2ecf20Sopenharmony_ci struct pci_controller *hose; 2588c2ecf20Sopenharmony_ci volatile void __iomem *addr; 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 2618c2ecf20Sopenharmony_ci if (hose == NULL) 2628c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 2638c2ecf20Sopenharmony_ci 2648c2ecf20Sopenharmony_ci if (bus->number == hose->first_busno && devfn == PCI_DEVFN(0, 0)) 2658c2ecf20Sopenharmony_ci return u3_ht_root_read_config(hose, offset, len, val); 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci if (offset > 0xff) 2688c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); 2718c2ecf20Sopenharmony_ci if (!addr) 2728c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci /* 2758c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 2768c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 2778c2ecf20Sopenharmony_ci */ 2788c2ecf20Sopenharmony_ci switch (len) { 2798c2ecf20Sopenharmony_ci case 1: 2808c2ecf20Sopenharmony_ci *val = in_8(addr); 2818c2ecf20Sopenharmony_ci break; 2828c2ecf20Sopenharmony_ci case 2: 2838c2ecf20Sopenharmony_ci *val = in_le16(addr); 2848c2ecf20Sopenharmony_ci break; 2858c2ecf20Sopenharmony_ci default: 2868c2ecf20Sopenharmony_ci *val = in_le32(addr); 2878c2ecf20Sopenharmony_ci break; 2888c2ecf20Sopenharmony_ci } 2898c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 2908c2ecf20Sopenharmony_ci} 2918c2ecf20Sopenharmony_ci 2928c2ecf20Sopenharmony_cistatic int u3_ht_write_config(struct pci_bus *bus, unsigned int devfn, 2938c2ecf20Sopenharmony_ci int offset, int len, u32 val) 2948c2ecf20Sopenharmony_ci{ 2958c2ecf20Sopenharmony_ci struct pci_controller *hose; 2968c2ecf20Sopenharmony_ci volatile void __iomem *addr; 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 2998c2ecf20Sopenharmony_ci if (hose == NULL) 3008c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3018c2ecf20Sopenharmony_ci 3028c2ecf20Sopenharmony_ci if (bus->number == hose->first_busno && devfn == PCI_DEVFN(0, 0)) 3038c2ecf20Sopenharmony_ci return u3_ht_root_write_config(hose, offset, len, val); 3048c2ecf20Sopenharmony_ci 3058c2ecf20Sopenharmony_ci if (offset > 0xff) 3068c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 3078c2ecf20Sopenharmony_ci 3088c2ecf20Sopenharmony_ci addr = u3_ht_cfg_access(hose, bus->number, devfn, offset); 3098c2ecf20Sopenharmony_ci if (!addr) 3108c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3118c2ecf20Sopenharmony_ci /* 3128c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 3138c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 3148c2ecf20Sopenharmony_ci */ 3158c2ecf20Sopenharmony_ci switch (len) { 3168c2ecf20Sopenharmony_ci case 1: 3178c2ecf20Sopenharmony_ci out_8(addr, val); 3188c2ecf20Sopenharmony_ci break; 3198c2ecf20Sopenharmony_ci case 2: 3208c2ecf20Sopenharmony_ci out_le16(addr, val); 3218c2ecf20Sopenharmony_ci break; 3228c2ecf20Sopenharmony_ci default: 3238c2ecf20Sopenharmony_ci out_le32(addr, val); 3248c2ecf20Sopenharmony_ci break; 3258c2ecf20Sopenharmony_ci } 3268c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 3278c2ecf20Sopenharmony_ci} 3288c2ecf20Sopenharmony_ci 3298c2ecf20Sopenharmony_cistatic struct pci_ops u3_ht_pci_ops = 3308c2ecf20Sopenharmony_ci{ 3318c2ecf20Sopenharmony_ci .read = u3_ht_read_config, 3328c2ecf20Sopenharmony_ci .write = u3_ht_write_config, 3338c2ecf20Sopenharmony_ci}; 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_cistatic unsigned int u4_pcie_cfa0(unsigned int devfn, unsigned int off) 3368c2ecf20Sopenharmony_ci{ 3378c2ecf20Sopenharmony_ci return (1 << PCI_SLOT(devfn)) | 3388c2ecf20Sopenharmony_ci (PCI_FUNC(devfn) << 8) | 3398c2ecf20Sopenharmony_ci ((off >> 8) << 28) | 3408c2ecf20Sopenharmony_ci (off & 0xfcu); 3418c2ecf20Sopenharmony_ci} 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_cistatic unsigned int u4_pcie_cfa1(unsigned int bus, unsigned int devfn, 3448c2ecf20Sopenharmony_ci unsigned int off) 3458c2ecf20Sopenharmony_ci{ 3468c2ecf20Sopenharmony_ci return (bus << 16) | 3478c2ecf20Sopenharmony_ci (devfn << 8) | 3488c2ecf20Sopenharmony_ci ((off >> 8) << 28) | 3498c2ecf20Sopenharmony_ci (off & 0xfcu) | 1u; 3508c2ecf20Sopenharmony_ci} 3518c2ecf20Sopenharmony_ci 3528c2ecf20Sopenharmony_cistatic volatile void __iomem *u4_pcie_cfg_access(struct pci_controller* hose, 3538c2ecf20Sopenharmony_ci u8 bus, u8 dev_fn, int offset) 3548c2ecf20Sopenharmony_ci{ 3558c2ecf20Sopenharmony_ci unsigned int caddr; 3568c2ecf20Sopenharmony_ci 3578c2ecf20Sopenharmony_ci if (bus == hose->first_busno) 3588c2ecf20Sopenharmony_ci caddr = u4_pcie_cfa0(dev_fn, offset); 3598c2ecf20Sopenharmony_ci else 3608c2ecf20Sopenharmony_ci caddr = u4_pcie_cfa1(bus, dev_fn, offset); 3618c2ecf20Sopenharmony_ci 3628c2ecf20Sopenharmony_ci /* Uninorth will return garbage if we don't read back the value ! */ 3638c2ecf20Sopenharmony_ci do { 3648c2ecf20Sopenharmony_ci out_le32(hose->cfg_addr, caddr); 3658c2ecf20Sopenharmony_ci } while (in_le32(hose->cfg_addr) != caddr); 3668c2ecf20Sopenharmony_ci 3678c2ecf20Sopenharmony_ci offset &= 0x03; 3688c2ecf20Sopenharmony_ci return hose->cfg_data + offset; 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_cistatic int u4_pcie_read_config(struct pci_bus *bus, unsigned int devfn, 3728c2ecf20Sopenharmony_ci int offset, int len, u32 *val) 3738c2ecf20Sopenharmony_ci{ 3748c2ecf20Sopenharmony_ci struct pci_controller *hose; 3758c2ecf20Sopenharmony_ci volatile void __iomem *addr; 3768c2ecf20Sopenharmony_ci 3778c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 3788c2ecf20Sopenharmony_ci if (hose == NULL) 3798c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3808c2ecf20Sopenharmony_ci if (offset >= 0x1000) 3818c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 3828c2ecf20Sopenharmony_ci addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); 3838c2ecf20Sopenharmony_ci if (!addr) 3848c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 3878c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 3888c2ecf20Sopenharmony_ci */ 3898c2ecf20Sopenharmony_ci switch (len) { 3908c2ecf20Sopenharmony_ci case 1: 3918c2ecf20Sopenharmony_ci *val = in_8(addr); 3928c2ecf20Sopenharmony_ci break; 3938c2ecf20Sopenharmony_ci case 2: 3948c2ecf20Sopenharmony_ci *val = in_le16(addr); 3958c2ecf20Sopenharmony_ci break; 3968c2ecf20Sopenharmony_ci default: 3978c2ecf20Sopenharmony_ci *val = in_le32(addr); 3988c2ecf20Sopenharmony_ci break; 3998c2ecf20Sopenharmony_ci } 4008c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 4018c2ecf20Sopenharmony_ci} 4028c2ecf20Sopenharmony_cistatic int u4_pcie_write_config(struct pci_bus *bus, unsigned int devfn, 4038c2ecf20Sopenharmony_ci int offset, int len, u32 val) 4048c2ecf20Sopenharmony_ci{ 4058c2ecf20Sopenharmony_ci struct pci_controller *hose; 4068c2ecf20Sopenharmony_ci volatile void __iomem *addr; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci hose = pci_bus_to_host(bus); 4098c2ecf20Sopenharmony_ci if (hose == NULL) 4108c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 4118c2ecf20Sopenharmony_ci if (offset >= 0x1000) 4128c2ecf20Sopenharmony_ci return PCIBIOS_BAD_REGISTER_NUMBER; 4138c2ecf20Sopenharmony_ci addr = u4_pcie_cfg_access(hose, bus->number, devfn, offset); 4148c2ecf20Sopenharmony_ci if (!addr) 4158c2ecf20Sopenharmony_ci return PCIBIOS_DEVICE_NOT_FOUND; 4168c2ecf20Sopenharmony_ci /* 4178c2ecf20Sopenharmony_ci * Note: the caller has already checked that offset is 4188c2ecf20Sopenharmony_ci * suitably aligned and that len is 1, 2 or 4. 4198c2ecf20Sopenharmony_ci */ 4208c2ecf20Sopenharmony_ci switch (len) { 4218c2ecf20Sopenharmony_ci case 1: 4228c2ecf20Sopenharmony_ci out_8(addr, val); 4238c2ecf20Sopenharmony_ci break; 4248c2ecf20Sopenharmony_ci case 2: 4258c2ecf20Sopenharmony_ci out_le16(addr, val); 4268c2ecf20Sopenharmony_ci break; 4278c2ecf20Sopenharmony_ci default: 4288c2ecf20Sopenharmony_ci out_le32(addr, val); 4298c2ecf20Sopenharmony_ci break; 4308c2ecf20Sopenharmony_ci } 4318c2ecf20Sopenharmony_ci return PCIBIOS_SUCCESSFUL; 4328c2ecf20Sopenharmony_ci} 4338c2ecf20Sopenharmony_ci 4348c2ecf20Sopenharmony_cistatic struct pci_ops u4_pcie_pci_ops = 4358c2ecf20Sopenharmony_ci{ 4368c2ecf20Sopenharmony_ci .read = u4_pcie_read_config, 4378c2ecf20Sopenharmony_ci .write = u4_pcie_write_config, 4388c2ecf20Sopenharmony_ci}; 4398c2ecf20Sopenharmony_ci 4408c2ecf20Sopenharmony_cistatic void __init setup_u3_agp(struct pci_controller* hose) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci /* On G5, we move AGP up to high bus number so we don't need 4438c2ecf20Sopenharmony_ci * to reassign bus numbers for HT. If we ever have P2P bridges 4448c2ecf20Sopenharmony_ci * on AGP, we'll have to move pci_assign_all_buses to the 4458c2ecf20Sopenharmony_ci * pci_controller structure so we enable it for AGP and not for 4468c2ecf20Sopenharmony_ci * HT childs. 4478c2ecf20Sopenharmony_ci * We hard code the address because of the different size of 4488c2ecf20Sopenharmony_ci * the reg address cell, we shall fix that by killing struct 4498c2ecf20Sopenharmony_ci * reg_property and using some accessor functions instead 4508c2ecf20Sopenharmony_ci */ 4518c2ecf20Sopenharmony_ci hose->first_busno = 0xf0; 4528c2ecf20Sopenharmony_ci hose->last_busno = 0xff; 4538c2ecf20Sopenharmony_ci hose->ops = &u3_agp_pci_ops; 4548c2ecf20Sopenharmony_ci hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); 4558c2ecf20Sopenharmony_ci hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); 4568c2ecf20Sopenharmony_ci 4578c2ecf20Sopenharmony_ci u3_agp = hose; 4588c2ecf20Sopenharmony_ci} 4598c2ecf20Sopenharmony_ci 4608c2ecf20Sopenharmony_cistatic void __init setup_u4_pcie(struct pci_controller* hose) 4618c2ecf20Sopenharmony_ci{ 4628c2ecf20Sopenharmony_ci /* We currently only implement the "non-atomic" config space, to 4638c2ecf20Sopenharmony_ci * be optimised later. 4648c2ecf20Sopenharmony_ci */ 4658c2ecf20Sopenharmony_ci hose->ops = &u4_pcie_pci_ops; 4668c2ecf20Sopenharmony_ci hose->cfg_addr = ioremap(0xf0000000 + 0x800000, 0x1000); 4678c2ecf20Sopenharmony_ci hose->cfg_data = ioremap(0xf0000000 + 0xc00000, 0x1000); 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_ci u4_pcie = hose; 4708c2ecf20Sopenharmony_ci} 4718c2ecf20Sopenharmony_ci 4728c2ecf20Sopenharmony_cistatic void __init setup_u3_ht(struct pci_controller* hose) 4738c2ecf20Sopenharmony_ci{ 4748c2ecf20Sopenharmony_ci hose->ops = &u3_ht_pci_ops; 4758c2ecf20Sopenharmony_ci 4768c2ecf20Sopenharmony_ci /* We hard code the address because of the different size of 4778c2ecf20Sopenharmony_ci * the reg address cell, we shall fix that by killing struct 4788c2ecf20Sopenharmony_ci * reg_property and using some accessor functions instead 4798c2ecf20Sopenharmony_ci */ 4808c2ecf20Sopenharmony_ci hose->cfg_data = ioremap(0xf2000000, 0x02000000); 4818c2ecf20Sopenharmony_ci hose->cfg_addr = ioremap(0xf8070000, 0x1000); 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci hose->first_busno = 0; 4848c2ecf20Sopenharmony_ci hose->last_busno = 0xef; 4858c2ecf20Sopenharmony_ci 4868c2ecf20Sopenharmony_ci u3_ht = hose; 4878c2ecf20Sopenharmony_ci} 4888c2ecf20Sopenharmony_ci 4898c2ecf20Sopenharmony_cistatic int __init maple_add_bridge(struct device_node *dev) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci int len; 4928c2ecf20Sopenharmony_ci struct pci_controller *hose; 4938c2ecf20Sopenharmony_ci char* disp_name; 4948c2ecf20Sopenharmony_ci const int *bus_range; 4958c2ecf20Sopenharmony_ci int primary = 1; 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci DBG("Adding PCI host bridge %pOF\n", dev); 4988c2ecf20Sopenharmony_ci 4998c2ecf20Sopenharmony_ci bus_range = of_get_property(dev, "bus-range", &len); 5008c2ecf20Sopenharmony_ci if (bus_range == NULL || len < 2 * sizeof(int)) { 5018c2ecf20Sopenharmony_ci printk(KERN_WARNING "Can't get bus-range for %pOF, assume bus 0\n", 5028c2ecf20Sopenharmony_ci dev); 5038c2ecf20Sopenharmony_ci } 5048c2ecf20Sopenharmony_ci 5058c2ecf20Sopenharmony_ci hose = pcibios_alloc_controller(dev); 5068c2ecf20Sopenharmony_ci if (hose == NULL) 5078c2ecf20Sopenharmony_ci return -ENOMEM; 5088c2ecf20Sopenharmony_ci hose->first_busno = bus_range ? bus_range[0] : 0; 5098c2ecf20Sopenharmony_ci hose->last_busno = bus_range ? bus_range[1] : 0xff; 5108c2ecf20Sopenharmony_ci hose->controller_ops = maple_pci_controller_ops; 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ci disp_name = NULL; 5138c2ecf20Sopenharmony_ci if (of_device_is_compatible(dev, "u3-agp")) { 5148c2ecf20Sopenharmony_ci setup_u3_agp(hose); 5158c2ecf20Sopenharmony_ci disp_name = "U3-AGP"; 5168c2ecf20Sopenharmony_ci primary = 0; 5178c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(dev, "u3-ht")) { 5188c2ecf20Sopenharmony_ci setup_u3_ht(hose); 5198c2ecf20Sopenharmony_ci disp_name = "U3-HT"; 5208c2ecf20Sopenharmony_ci primary = 1; 5218c2ecf20Sopenharmony_ci } else if (of_device_is_compatible(dev, "u4-pcie")) { 5228c2ecf20Sopenharmony_ci setup_u4_pcie(hose); 5238c2ecf20Sopenharmony_ci disp_name = "U4-PCIE"; 5248c2ecf20Sopenharmony_ci primary = 0; 5258c2ecf20Sopenharmony_ci } 5268c2ecf20Sopenharmony_ci printk(KERN_INFO "Found %s PCI host bridge. Firmware bus number: %d->%d\n", 5278c2ecf20Sopenharmony_ci disp_name, hose->first_busno, hose->last_busno); 5288c2ecf20Sopenharmony_ci 5298c2ecf20Sopenharmony_ci /* Interpret the "ranges" property */ 5308c2ecf20Sopenharmony_ci /* This also maps the I/O region and sets isa_io/mem_base */ 5318c2ecf20Sopenharmony_ci pci_process_bridge_OF_ranges(hose, dev, primary); 5328c2ecf20Sopenharmony_ci 5338c2ecf20Sopenharmony_ci /* Fixup "bus-range" OF property */ 5348c2ecf20Sopenharmony_ci fixup_bus_range(dev); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci /* Check for legacy IOs */ 5378c2ecf20Sopenharmony_ci isa_bridge_find_early(hose); 5388c2ecf20Sopenharmony_ci 5398c2ecf20Sopenharmony_ci return 0; 5408c2ecf20Sopenharmony_ci} 5418c2ecf20Sopenharmony_ci 5428c2ecf20Sopenharmony_ci 5438c2ecf20Sopenharmony_civoid maple_pci_irq_fixup(struct pci_dev *dev) 5448c2ecf20Sopenharmony_ci{ 5458c2ecf20Sopenharmony_ci DBG(" -> maple_pci_irq_fixup\n"); 5468c2ecf20Sopenharmony_ci 5478c2ecf20Sopenharmony_ci /* Fixup IRQ for PCIe host */ 5488c2ecf20Sopenharmony_ci if (u4_pcie != NULL && dev->bus->number == 0 && 5498c2ecf20Sopenharmony_ci pci_bus_to_host(dev->bus) == u4_pcie) { 5508c2ecf20Sopenharmony_ci printk(KERN_DEBUG "Fixup U4 PCIe IRQ\n"); 5518c2ecf20Sopenharmony_ci dev->irq = irq_create_mapping(NULL, 1); 5528c2ecf20Sopenharmony_ci if (dev->irq) 5538c2ecf20Sopenharmony_ci irq_set_irq_type(dev->irq, IRQ_TYPE_LEVEL_LOW); 5548c2ecf20Sopenharmony_ci } 5558c2ecf20Sopenharmony_ci 5568c2ecf20Sopenharmony_ci /* Hide AMD8111 IDE interrupt when in legacy mode so 5578c2ecf20Sopenharmony_ci * the driver calls pci_get_legacy_ide_irq() 5588c2ecf20Sopenharmony_ci */ 5598c2ecf20Sopenharmony_ci if (dev->vendor == PCI_VENDOR_ID_AMD && 5608c2ecf20Sopenharmony_ci dev->device == PCI_DEVICE_ID_AMD_8111_IDE && 5618c2ecf20Sopenharmony_ci (dev->class & 5) != 5) { 5628c2ecf20Sopenharmony_ci dev->irq = 0; 5638c2ecf20Sopenharmony_ci } 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci DBG(" <- maple_pci_irq_fixup\n"); 5668c2ecf20Sopenharmony_ci} 5678c2ecf20Sopenharmony_ci 5688c2ecf20Sopenharmony_cistatic int maple_pci_root_bridge_prepare(struct pci_host_bridge *bridge) 5698c2ecf20Sopenharmony_ci{ 5708c2ecf20Sopenharmony_ci struct pci_controller *hose = pci_bus_to_host(bridge->bus); 5718c2ecf20Sopenharmony_ci struct device_node *np, *child; 5728c2ecf20Sopenharmony_ci 5738c2ecf20Sopenharmony_ci if (hose != u3_agp) 5748c2ecf20Sopenharmony_ci return 0; 5758c2ecf20Sopenharmony_ci 5768c2ecf20Sopenharmony_ci /* Fixup the PCI<->OF mapping for U3 AGP due to bus renumbering. We 5778c2ecf20Sopenharmony_ci * assume there is no P2P bridge on the AGP bus, which should be a 5788c2ecf20Sopenharmony_ci * safe assumptions hopefully. 5798c2ecf20Sopenharmony_ci */ 5808c2ecf20Sopenharmony_ci np = hose->dn; 5818c2ecf20Sopenharmony_ci PCI_DN(np)->busno = 0xf0; 5828c2ecf20Sopenharmony_ci for_each_child_of_node(np, child) 5838c2ecf20Sopenharmony_ci PCI_DN(child)->busno = 0xf0; 5848c2ecf20Sopenharmony_ci 5858c2ecf20Sopenharmony_ci return 0; 5868c2ecf20Sopenharmony_ci} 5878c2ecf20Sopenharmony_ci 5888c2ecf20Sopenharmony_civoid __init maple_pci_init(void) 5898c2ecf20Sopenharmony_ci{ 5908c2ecf20Sopenharmony_ci struct device_node *np, *root; 5918c2ecf20Sopenharmony_ci struct device_node *ht = NULL; 5928c2ecf20Sopenharmony_ci 5938c2ecf20Sopenharmony_ci /* Probe root PCI hosts, that is on U3 the AGP host and the 5948c2ecf20Sopenharmony_ci * HyperTransport host. That one is actually "kept" around 5958c2ecf20Sopenharmony_ci * and actually added last as it's resource management relies 5968c2ecf20Sopenharmony_ci * on the AGP resources to have been setup first 5978c2ecf20Sopenharmony_ci */ 5988c2ecf20Sopenharmony_ci root = of_find_node_by_path("/"); 5998c2ecf20Sopenharmony_ci if (root == NULL) { 6008c2ecf20Sopenharmony_ci printk(KERN_CRIT "maple_find_bridges: can't find root of device tree\n"); 6018c2ecf20Sopenharmony_ci return; 6028c2ecf20Sopenharmony_ci } 6038c2ecf20Sopenharmony_ci for_each_child_of_node(root, np) { 6048c2ecf20Sopenharmony_ci if (!of_node_is_type(np, "pci") && !of_node_is_type(np, "ht")) 6058c2ecf20Sopenharmony_ci continue; 6068c2ecf20Sopenharmony_ci if ((of_device_is_compatible(np, "u4-pcie") || 6078c2ecf20Sopenharmony_ci of_device_is_compatible(np, "u3-agp")) && 6088c2ecf20Sopenharmony_ci maple_add_bridge(np) == 0) 6098c2ecf20Sopenharmony_ci of_node_get(np); 6108c2ecf20Sopenharmony_ci 6118c2ecf20Sopenharmony_ci if (of_device_is_compatible(np, "u3-ht")) { 6128c2ecf20Sopenharmony_ci of_node_get(np); 6138c2ecf20Sopenharmony_ci ht = np; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci } 6168c2ecf20Sopenharmony_ci of_node_put(root); 6178c2ecf20Sopenharmony_ci 6188c2ecf20Sopenharmony_ci /* Now setup the HyperTransport host if we found any 6198c2ecf20Sopenharmony_ci */ 6208c2ecf20Sopenharmony_ci if (ht && maple_add_bridge(ht) != 0) 6218c2ecf20Sopenharmony_ci of_node_put(ht); 6228c2ecf20Sopenharmony_ci 6238c2ecf20Sopenharmony_ci ppc_md.pcibios_root_bridge_prepare = maple_pci_root_bridge_prepare; 6248c2ecf20Sopenharmony_ci 6258c2ecf20Sopenharmony_ci /* Tell pci.c to not change any resource allocations. */ 6268c2ecf20Sopenharmony_ci pci_add_flags(PCI_PROBE_ONLY); 6278c2ecf20Sopenharmony_ci} 6288c2ecf20Sopenharmony_ci 6298c2ecf20Sopenharmony_ciint maple_pci_get_legacy_ide_irq(struct pci_dev *pdev, int channel) 6308c2ecf20Sopenharmony_ci{ 6318c2ecf20Sopenharmony_ci struct device_node *np; 6328c2ecf20Sopenharmony_ci unsigned int defirq = channel ? 15 : 14; 6338c2ecf20Sopenharmony_ci unsigned int irq; 6348c2ecf20Sopenharmony_ci 6358c2ecf20Sopenharmony_ci if (pdev->vendor != PCI_VENDOR_ID_AMD || 6368c2ecf20Sopenharmony_ci pdev->device != PCI_DEVICE_ID_AMD_8111_IDE) 6378c2ecf20Sopenharmony_ci return defirq; 6388c2ecf20Sopenharmony_ci 6398c2ecf20Sopenharmony_ci np = pci_device_to_OF_node(pdev); 6408c2ecf20Sopenharmony_ci if (np == NULL) { 6418c2ecf20Sopenharmony_ci printk("Failed to locate OF node for IDE %s\n", 6428c2ecf20Sopenharmony_ci pci_name(pdev)); 6438c2ecf20Sopenharmony_ci return defirq; 6448c2ecf20Sopenharmony_ci } 6458c2ecf20Sopenharmony_ci irq = irq_of_parse_and_map(np, channel & 0x1); 6468c2ecf20Sopenharmony_ci if (!irq) { 6478c2ecf20Sopenharmony_ci printk("Failed to map onboard IDE interrupt for channel %d\n", 6488c2ecf20Sopenharmony_ci channel); 6498c2ecf20Sopenharmony_ci return defirq; 6508c2ecf20Sopenharmony_ci } 6518c2ecf20Sopenharmony_ci return irq; 6528c2ecf20Sopenharmony_ci} 6538c2ecf20Sopenharmony_ci 6548c2ecf20Sopenharmony_cistatic void quirk_ipr_msi(struct pci_dev *dev) 6558c2ecf20Sopenharmony_ci{ 6568c2ecf20Sopenharmony_ci /* Something prevents MSIs from the IPR from working on Bimini, 6578c2ecf20Sopenharmony_ci * and the driver has no smarts to recover. So disable MSI 6588c2ecf20Sopenharmony_ci * on it for now. */ 6598c2ecf20Sopenharmony_ci 6608c2ecf20Sopenharmony_ci if (machine_is(maple)) { 6618c2ecf20Sopenharmony_ci dev->no_msi = 1; 6628c2ecf20Sopenharmony_ci dev_info(&dev->dev, "Quirk disabled MSI\n"); 6638c2ecf20Sopenharmony_ci } 6648c2ecf20Sopenharmony_ci} 6658c2ecf20Sopenharmony_ciDECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_IBM, PCI_DEVICE_ID_IBM_OBSIDIAN, 6668c2ecf20Sopenharmony_ci quirk_ipr_msi); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_cistruct pci_controller_ops maple_pci_controller_ops = { 6698c2ecf20Sopenharmony_ci}; 670