1d3d2176aSDavid Gibson /* 2d3d2176aSDavid Gibson * Copyright (C) 2001 Dave Engebretsen, IBM Corporation 3d3d2176aSDavid Gibson * Copyright (C) 2003 Anton Blanchard <anton@au.ibm.com>, IBM 4d3d2176aSDavid Gibson * 5d3d2176aSDavid Gibson * RTAS specific routines for PCI. 6d3d2176aSDavid Gibson * 7d3d2176aSDavid Gibson * Based on code from pci.c, chrp_pci.c and pSeries_pci.c 8d3d2176aSDavid Gibson * 9d3d2176aSDavid Gibson * This program is free software; you can redistribute it and/or modify 10d3d2176aSDavid Gibson * it under the terms of the GNU General Public License as published by 11d3d2176aSDavid Gibson * the Free Software Foundation; either version 2 of the License, or 12d3d2176aSDavid Gibson * (at your option) any later version. 13d3d2176aSDavid Gibson * 14d3d2176aSDavid Gibson * This program is distributed in the hope that it will be useful, 15d3d2176aSDavid Gibson * but WITHOUT ANY WARRANTY; without even the implied warranty of 16d3d2176aSDavid Gibson * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17d3d2176aSDavid Gibson * GNU General Public License for more details. 18d3d2176aSDavid Gibson * 19d3d2176aSDavid Gibson * You should have received a copy of the GNU General Public License 20d3d2176aSDavid Gibson * along with this program; if not, write to the Free Software 21d3d2176aSDavid Gibson * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 22d3d2176aSDavid Gibson */ 23d3d2176aSDavid Gibson 24d3d2176aSDavid Gibson #include <linux/kernel.h> 25d3d2176aSDavid Gibson #include <linux/threads.h> 26d3d2176aSDavid Gibson #include <linux/pci.h> 27d3d2176aSDavid Gibson #include <linux/string.h> 28d3d2176aSDavid Gibson #include <linux/init.h> 29d3d2176aSDavid Gibson #include <linux/bootmem.h> 30d3d2176aSDavid Gibson 31d3d2176aSDavid Gibson #include <asm/io.h> 32d3d2176aSDavid Gibson #include <asm/pgtable.h> 33d3d2176aSDavid Gibson #include <asm/irq.h> 34d3d2176aSDavid Gibson #include <asm/prom.h> 35d3d2176aSDavid Gibson #include <asm/machdep.h> 36d3d2176aSDavid Gibson #include <asm/pci-bridge.h> 37d3d2176aSDavid Gibson #include <asm/iommu.h> 38d3d2176aSDavid Gibson #include <asm/rtas.h> 39d3d2176aSDavid Gibson #include <asm/mpic.h> 40d3d2176aSDavid Gibson #include <asm/ppc-pci.h> 4168a64357SBenjamin Herrenschmidt #include <asm/eeh.h> 42d3d2176aSDavid Gibson 43d3d2176aSDavid Gibson /* RTAS tokens */ 44d3d2176aSDavid Gibson static int read_pci_config; 45d3d2176aSDavid Gibson static int write_pci_config; 46d3d2176aSDavid Gibson static int ibm_read_pci_config; 47d3d2176aSDavid Gibson static int ibm_write_pci_config; 48d3d2176aSDavid Gibson 49d3d2176aSDavid Gibson static inline int config_access_valid(struct pci_dn *dn, int where) 50d3d2176aSDavid Gibson { 51d3d2176aSDavid Gibson if (where < 256) 52d3d2176aSDavid Gibson return 1; 53d3d2176aSDavid Gibson if (where < 4096 && dn->pci_ext_config_space) 54d3d2176aSDavid Gibson return 1; 55d3d2176aSDavid Gibson 56d3d2176aSDavid Gibson return 0; 57d3d2176aSDavid Gibson } 58d3d2176aSDavid Gibson 597684b40cSLinas Vepstas int rtas_read_config(struct pci_dn *pdn, int where, int size, u32 *val) 60d3d2176aSDavid Gibson { 61d3d2176aSDavid Gibson int returnval = -1; 62d3d2176aSDavid Gibson unsigned long buid, addr; 63d3d2176aSDavid Gibson int ret; 64d3d2176aSDavid Gibson 65d3d2176aSDavid Gibson if (!pdn) 66d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 67d3d2176aSDavid Gibson if (!config_access_valid(pdn, where)) 68d3d2176aSDavid Gibson return PCIBIOS_BAD_REGISTER_NUMBER; 69*3409eb4eSGavin Shan #ifdef CONFIG_EEH 70*3409eb4eSGavin Shan if (pdn->edev && pdn->edev->pe && 71*3409eb4eSGavin Shan (pdn->edev->pe->state & EEH_PE_CFG_BLOCKED)) 72*3409eb4eSGavin Shan return PCIBIOS_SET_FAILED; 73*3409eb4eSGavin Shan #endif 74d3d2176aSDavid Gibson 756f3d5d3cSMichael Ellerman addr = rtas_config_addr(pdn->busno, pdn->devfn, where); 76d3d2176aSDavid Gibson buid = pdn->phb->buid; 77d3d2176aSDavid Gibson if (buid) { 78d3d2176aSDavid Gibson ret = rtas_call(ibm_read_pci_config, 4, 2, &returnval, 79d3d2176aSDavid Gibson addr, BUID_HI(buid), BUID_LO(buid), size); 80d3d2176aSDavid Gibson } else { 81d3d2176aSDavid Gibson ret = rtas_call(read_pci_config, 2, 2, &returnval, addr, size); 82d3d2176aSDavid Gibson } 83d3d2176aSDavid Gibson *val = returnval; 84d3d2176aSDavid Gibson 85d3d2176aSDavid Gibson if (ret) 86d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 87d3d2176aSDavid Gibson 88d3d2176aSDavid Gibson return PCIBIOS_SUCCESSFUL; 89d3d2176aSDavid Gibson } 90d3d2176aSDavid Gibson 91d3d2176aSDavid Gibson static int rtas_pci_read_config(struct pci_bus *bus, 92d3d2176aSDavid Gibson unsigned int devfn, 93d3d2176aSDavid Gibson int where, int size, u32 *val) 94d3d2176aSDavid Gibson { 95d3d2176aSDavid Gibson struct device_node *busdn, *dn; 96d0914f50SGavin Shan struct pci_dn *pdn; 97d0914f50SGavin Shan bool found = false; 98d0914f50SGavin Shan int ret; 99d3d2176aSDavid Gibson 100d3d2176aSDavid Gibson /* Search only direct children of the bus */ 101d0914f50SGavin Shan *val = 0xFFFFFFFF; 102d0914f50SGavin Shan busdn = pci_bus_to_OF_node(bus); 103d3d2176aSDavid Gibson for (dn = busdn->child; dn; dn = dn->sibling) { 104d0914f50SGavin Shan pdn = PCI_DN(dn); 105d3d2176aSDavid Gibson if (pdn && pdn->devfn == devfn 106d0914f50SGavin Shan && of_device_is_available(dn)) { 107d0914f50SGavin Shan found = true; 108d0914f50SGavin Shan break; 109d0914f50SGavin Shan } 110d3d2176aSDavid Gibson } 111d3d2176aSDavid Gibson 112d0914f50SGavin Shan if (!found) 113d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 114d0914f50SGavin Shan 115d0914f50SGavin Shan ret = rtas_read_config(pdn, where, size, val); 116d0914f50SGavin Shan if (*val == EEH_IO_ERROR_VALUE(size) && 117d0914f50SGavin Shan eeh_dev_check_failure(of_node_to_eeh_dev(dn))) 118d0914f50SGavin Shan return PCIBIOS_DEVICE_NOT_FOUND; 119d0914f50SGavin Shan 120d0914f50SGavin Shan return ret; 121d3d2176aSDavid Gibson } 122d3d2176aSDavid Gibson 123d3d2176aSDavid Gibson int rtas_write_config(struct pci_dn *pdn, int where, int size, u32 val) 124d3d2176aSDavid Gibson { 125d3d2176aSDavid Gibson unsigned long buid, addr; 126d3d2176aSDavid Gibson int ret; 127d3d2176aSDavid Gibson 128d3d2176aSDavid Gibson if (!pdn) 129d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 130d3d2176aSDavid Gibson if (!config_access_valid(pdn, where)) 131d3d2176aSDavid Gibson return PCIBIOS_BAD_REGISTER_NUMBER; 132*3409eb4eSGavin Shan #ifdef CONFIG_EEH 133*3409eb4eSGavin Shan if (pdn->edev && pdn->edev->pe && 134*3409eb4eSGavin Shan (pdn->edev->pe->state & EEH_PE_CFG_BLOCKED)) 135*3409eb4eSGavin Shan return PCIBIOS_SET_FAILED; 136*3409eb4eSGavin Shan #endif 137d3d2176aSDavid Gibson 1386f3d5d3cSMichael Ellerman addr = rtas_config_addr(pdn->busno, pdn->devfn, where); 139d3d2176aSDavid Gibson buid = pdn->phb->buid; 140d3d2176aSDavid Gibson if (buid) { 141d3d2176aSDavid Gibson ret = rtas_call(ibm_write_pci_config, 5, 1, NULL, addr, 142d3d2176aSDavid Gibson BUID_HI(buid), BUID_LO(buid), size, (ulong) val); 143d3d2176aSDavid Gibson } else { 144d3d2176aSDavid Gibson ret = rtas_call(write_pci_config, 3, 1, NULL, addr, size, (ulong)val); 145d3d2176aSDavid Gibson } 146d3d2176aSDavid Gibson 147d3d2176aSDavid Gibson if (ret) 148d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 149d3d2176aSDavid Gibson 150d3d2176aSDavid Gibson return PCIBIOS_SUCCESSFUL; 151d3d2176aSDavid Gibson } 152d3d2176aSDavid Gibson 153d3d2176aSDavid Gibson static int rtas_pci_write_config(struct pci_bus *bus, 154d3d2176aSDavid Gibson unsigned int devfn, 155d3d2176aSDavid Gibson int where, int size, u32 val) 156d3d2176aSDavid Gibson { 157d3d2176aSDavid Gibson struct device_node *busdn, *dn; 158d0914f50SGavin Shan struct pci_dn *pdn; 159d0914f50SGavin Shan bool found = false; 160d3d2176aSDavid Gibson 161d3d2176aSDavid Gibson /* Search only direct children of the bus */ 162d0914f50SGavin Shan busdn = pci_bus_to_OF_node(bus); 163d3d2176aSDavid Gibson for (dn = busdn->child; dn; dn = dn->sibling) { 164d0914f50SGavin Shan pdn = PCI_DN(dn); 165d3d2176aSDavid Gibson if (pdn && pdn->devfn == devfn 166d0914f50SGavin Shan && of_device_is_available(dn)) { 167d0914f50SGavin Shan found = true; 168d0914f50SGavin Shan break; 169d3d2176aSDavid Gibson } 170d0914f50SGavin Shan } 171d0914f50SGavin Shan 172d0914f50SGavin Shan if (!found) 173d3d2176aSDavid Gibson return PCIBIOS_DEVICE_NOT_FOUND; 174d0914f50SGavin Shan 175*3409eb4eSGavin Shan return rtas_write_config(pdn, where, size, val); 176d3d2176aSDavid Gibson } 177d3d2176aSDavid Gibson 1781c21a293SMichael Ellerman static struct pci_ops rtas_pci_ops = { 1798674e0c9SNathan Lynch .read = rtas_pci_read_config, 1808674e0c9SNathan Lynch .write = rtas_pci_write_config, 181d3d2176aSDavid Gibson }; 182d3d2176aSDavid Gibson 1831c21a293SMichael Ellerman static int is_python(struct device_node *dev) 184d3d2176aSDavid Gibson { 185e2eb6392SStephen Rothwell const char *model = of_get_property(dev, "model", NULL); 186d3d2176aSDavid Gibson 187d3d2176aSDavid Gibson if (model && strstr(model, "Python")) 188d3d2176aSDavid Gibson return 1; 189d3d2176aSDavid Gibson 190d3d2176aSDavid Gibson return 0; 191d3d2176aSDavid Gibson } 192d3d2176aSDavid Gibson 193cc5d0189SBenjamin Herrenschmidt static void python_countermeasures(struct device_node *dev) 194d3d2176aSDavid Gibson { 195cc5d0189SBenjamin Herrenschmidt struct resource registers; 196d3d2176aSDavid Gibson void __iomem *chip_regs; 197d3d2176aSDavid Gibson volatile u32 val; 198d3d2176aSDavid Gibson 199cc5d0189SBenjamin Herrenschmidt if (of_address_to_resource(dev, 0, ®isters)) { 200cc5d0189SBenjamin Herrenschmidt printk(KERN_ERR "Can't get address for Python workarounds !\n"); 201d3d2176aSDavid Gibson return; 202cc5d0189SBenjamin Herrenschmidt } 203d3d2176aSDavid Gibson 204d3d2176aSDavid Gibson /* Python's register file is 1 MB in size. */ 205cc5d0189SBenjamin Herrenschmidt chip_regs = ioremap(registers.start & ~(0xfffffUL), 0x100000); 206d3d2176aSDavid Gibson 207d3d2176aSDavid Gibson /* 208d3d2176aSDavid Gibson * Firmware doesn't always clear this bit which is critical 209d3d2176aSDavid Gibson * for good performance - Anton 210d3d2176aSDavid Gibson */ 211d3d2176aSDavid Gibson 212d3d2176aSDavid Gibson #define PRG_CL_RESET_VALID 0x00010000 213d3d2176aSDavid Gibson 214d3d2176aSDavid Gibson val = in_be32(chip_regs + 0xf6030); 215d3d2176aSDavid Gibson if (val & PRG_CL_RESET_VALID) { 216d3d2176aSDavid Gibson printk(KERN_INFO "Python workaround: "); 217d3d2176aSDavid Gibson val &= ~PRG_CL_RESET_VALID; 218d3d2176aSDavid Gibson out_be32(chip_regs + 0xf6030, val); 219d3d2176aSDavid Gibson /* 220d3d2176aSDavid Gibson * We must read it back for changes to 221d3d2176aSDavid Gibson * take effect 222d3d2176aSDavid Gibson */ 223d3d2176aSDavid Gibson val = in_be32(chip_regs + 0xf6030); 224d3d2176aSDavid Gibson printk("reg0: %x\n", val); 225d3d2176aSDavid Gibson } 226d3d2176aSDavid Gibson 227d3d2176aSDavid Gibson iounmap(chip_regs); 228d3d2176aSDavid Gibson } 229d3d2176aSDavid Gibson 230d3d2176aSDavid Gibson void __init init_pci_config_tokens(void) 231d3d2176aSDavid Gibson { 232d3d2176aSDavid Gibson read_pci_config = rtas_token("read-pci-config"); 233d3d2176aSDavid Gibson write_pci_config = rtas_token("write-pci-config"); 234d3d2176aSDavid Gibson ibm_read_pci_config = rtas_token("ibm,read-pci-config"); 235d3d2176aSDavid Gibson ibm_write_pci_config = rtas_token("ibm,write-pci-config"); 236d3d2176aSDavid Gibson } 237d3d2176aSDavid Gibson 238cad5cef6SGreg Kroah-Hartman unsigned long get_phb_buid(struct device_node *phb) 239d3d2176aSDavid Gibson { 2406506e710SBenjamin Herrenschmidt struct resource r; 241d3d2176aSDavid Gibson 2426506e710SBenjamin Herrenschmidt if (ibm_read_pci_config == -1) 243d3d2176aSDavid Gibson return 0; 2446506e710SBenjamin Herrenschmidt if (of_address_to_resource(phb, 0, &r)) 245d3d2176aSDavid Gibson return 0; 2466506e710SBenjamin Herrenschmidt return r.start; 247d3d2176aSDavid Gibson } 248d3d2176aSDavid Gibson 249d3d2176aSDavid Gibson static int phb_set_bus_ranges(struct device_node *dev, 250d3d2176aSDavid Gibson struct pci_controller *phb) 251d3d2176aSDavid Gibson { 252cf059965SCedric Le Goater const __be32 *bus_range; 253d3d2176aSDavid Gibson unsigned int len; 254d3d2176aSDavid Gibson 255e2eb6392SStephen Rothwell bus_range = of_get_property(dev, "bus-range", &len); 256d3d2176aSDavid Gibson if (bus_range == NULL || len < 2 * sizeof(int)) { 257d3d2176aSDavid Gibson return 1; 258d3d2176aSDavid Gibson } 259d3d2176aSDavid Gibson 260cf059965SCedric Le Goater phb->first_busno = be32_to_cpu(bus_range[0]); 261cf059965SCedric Le Goater phb->last_busno = be32_to_cpu(bus_range[1]); 262d3d2176aSDavid Gibson 263d3d2176aSDavid Gibson return 0; 264d3d2176aSDavid Gibson } 265d3d2176aSDavid Gibson 266cad5cef6SGreg Kroah-Hartman int rtas_setup_phb(struct pci_controller *phb) 267d3d2176aSDavid Gibson { 26844ef3390SStephen Rothwell struct device_node *dev = phb->dn; 2694c9d2800SBenjamin Herrenschmidt 270d3d2176aSDavid Gibson if (is_python(dev)) 271cc5d0189SBenjamin Herrenschmidt python_countermeasures(dev); 272d3d2176aSDavid Gibson 273d3d2176aSDavid Gibson if (phb_set_bus_ranges(dev, phb)) 274d3d2176aSDavid Gibson return 1; 275d3d2176aSDavid Gibson 276d3d2176aSDavid Gibson phb->ops = &rtas_pci_ops; 277d3d2176aSDavid Gibson phb->buid = get_phb_buid(dev); 278d3d2176aSDavid Gibson 279d3d2176aSDavid Gibson return 0; 280d3d2176aSDavid Gibson } 281d3d2176aSDavid Gibson 28236241ce6SStephen Rothwell void __init find_and_init_phbs(void) 283d3d2176aSDavid Gibson { 284d3d2176aSDavid Gibson struct device_node *node; 285d3d2176aSDavid Gibson struct pci_controller *phb; 286d3d2176aSDavid Gibson struct device_node *root = of_find_node_by_path("/"); 287d3d2176aSDavid Gibson 28885e99b9fSStephen Rothwell for_each_child_of_node(root, node) { 289bb53bb3dSJake Moilanen if (node->type == NULL || (strcmp(node->type, "pci") != 0 && 290bb53bb3dSJake Moilanen strcmp(node->type, "pciex") != 0)) 291d3d2176aSDavid Gibson continue; 292d3d2176aSDavid Gibson 293b5166cc2SBenjamin Herrenschmidt phb = pcibios_alloc_controller(node); 294d3d2176aSDavid Gibson if (!phb) 295d3d2176aSDavid Gibson continue; 2964c9d2800SBenjamin Herrenschmidt rtas_setup_phb(phb); 297d3d2176aSDavid Gibson pci_process_bridge_OF_ranges(phb, node, 0); 2983d5134eeSBenjamin Herrenschmidt isa_bridge_find_early(phb); 299d3d2176aSDavid Gibson } 300d3d2176aSDavid Gibson 301d3d2176aSDavid Gibson of_node_put(root); 302d3d2176aSDavid Gibson pci_devs_phb_init(); 303d3d2176aSDavid Gibson 304d3d2176aSDavid Gibson /* 305673c9756SBjorn Helgaas * PCI_PROBE_ONLY and PCI_REASSIGN_ALL_BUS can be set via properties 306d3d2176aSDavid Gibson * in chosen. 307d3d2176aSDavid Gibson */ 308d3d2176aSDavid Gibson if (of_chosen) { 309a7f67bdfSJeremy Kerr const int *prop; 310d3d2176aSDavid Gibson 311e2eb6392SStephen Rothwell prop = of_get_property(of_chosen, 312a7f67bdfSJeremy Kerr "linux,pci-probe-only", NULL); 313673c9756SBjorn Helgaas if (prop) { 314673c9756SBjorn Helgaas if (*prop) 315673c9756SBjorn Helgaas pci_add_flags(PCI_PROBE_ONLY); 316673c9756SBjorn Helgaas else 317673c9756SBjorn Helgaas pci_clear_flags(PCI_PROBE_ONLY); 318673c9756SBjorn Helgaas } 319d3d2176aSDavid Gibson 320fc3fb71cSBenjamin Herrenschmidt #ifdef CONFIG_PPC32 /* Will be made generic soon */ 321e2eb6392SStephen Rothwell prop = of_get_property(of_chosen, 322d3d2176aSDavid Gibson "linux,pci-assign-all-buses", NULL); 323fc3fb71cSBenjamin Herrenschmidt if (prop && *prop) 3240e47ff1cSRob Herring pci_add_flags(PCI_REASSIGN_ALL_BUS); 325fc3fb71cSBenjamin Herrenschmidt #endif /* CONFIG_PPC32 */ 326d3d2176aSDavid Gibson } 327d3d2176aSDavid Gibson } 328