diff options
| author | Benjamin Herrenschmidt <benh@kernel.crashing.org> | 2007-12-19 22:54:55 -0500 |
|---|---|---|
| committer | Paul Mackerras <paulus@samba.org> | 2007-12-20 00:18:10 -0500 |
| commit | 50c9bc2fc86fddd39eea6a12ceb81585bc2aafaa (patch) | |
| tree | a6021e68bdc0477ada4bc81d04443e3316cdbb4f | |
| parent | 3fd94c6b1a1158d3e0e505b0a00c3a707b5fcd40 (diff) | |
[POWERPC] fix iSeries PCI resource management
The way iSeries manages PCI IO and Memory resources is a bit strange
and is based on overriding the content of those resources with home
cooked ones afterward.
This changes it a bit to better integrate with the new resource handling
so that the "virtual" tokens that iSeries replaces resources with are
done from the proper per-device fixup hook, and bridge resources are
set to enclose that token space. This fixes various things such as
the output of /proc/iomem & ioports, among others. This also fixes up
various boot messages as well.
Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
Signed-off-by: Paul Mackerras <paulus@samba.org>
| -rw-r--r-- | arch/powerpc/kernel/pci-common.c | 16 | ||||
| -rw-r--r-- | arch/powerpc/kernel/pci_64.c | 19 | ||||
| -rw-r--r-- | arch/powerpc/platforms/iseries/pci.c | 125 | ||||
| -rw-r--r-- | arch/powerpc/platforms/iseries/pci.h | 4 | ||||
| -rw-r--r-- | arch/powerpc/platforms/iseries/setup.c | 37 |
5 files changed, 122 insertions, 79 deletions
diff --git a/arch/powerpc/kernel/pci-common.c b/arch/powerpc/kernel/pci-common.c index d804c8d0be0..f706b7e83d7 100644 --- a/arch/powerpc/kernel/pci-common.c +++ b/arch/powerpc/kernel/pci-common.c | |||
| @@ -190,6 +190,20 @@ int pci_read_irq_line(struct pci_dev *pci_dev) | |||
| 190 | struct of_irq oirq; | 190 | struct of_irq oirq; |
| 191 | unsigned int virq; | 191 | unsigned int virq; |
| 192 | 192 | ||
| 193 | /* The current device-tree that iSeries generates from the HV | ||
| 194 | * PCI informations doesn't contain proper interrupt routing, | ||
| 195 | * and all the fallback would do is print out crap, so we | ||
| 196 | * don't attempt to resolve the interrupts here at all, some | ||
| 197 | * iSeries specific fixup does it. | ||
| 198 | * | ||
| 199 | * In the long run, we will hopefully fix the generated device-tree | ||
| 200 | * instead. | ||
| 201 | */ | ||
| 202 | #ifdef CONFIG_PPC_ISERIES | ||
| 203 | if (firmware_has_feature(FW_FEATURE_ISERIES)) | ||
| 204 | return -1; | ||
| 205 | #endif | ||
| 206 | |||
| 193 | DBG("Try to map irq for %s...\n", pci_name(pci_dev)); | 207 | DBG("Try to map irq for %s...\n", pci_name(pci_dev)); |
| 194 | 208 | ||
| 195 | #ifdef DEBUG | 209 | #ifdef DEBUG |
| @@ -946,7 +960,7 @@ static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) | |||
| 946 | || res->start > res->end) | 960 | || res->start > res->end) |
| 947 | continue; | 961 | continue; |
| 948 | if (bus->parent == NULL) | 962 | if (bus->parent == NULL) |
| 949 | pr = (res->flags & IORESOURCE_IO)? | 963 | pr = (res->flags & IORESOURCE_IO) ? |
| 950 | &ioport_resource : &iomem_resource; | 964 | &ioport_resource : &iomem_resource; |
| 951 | else { | 965 | else { |
| 952 | /* Don't bother with non-root busses when | 966 | /* Don't bother with non-root busses when |
diff --git a/arch/powerpc/kernel/pci_64.c b/arch/powerpc/kernel/pci_64.c index 794359d8686..2ec040b314d 100644 --- a/arch/powerpc/kernel/pci_64.c +++ b/arch/powerpc/kernel/pci_64.c | |||
| @@ -359,7 +359,7 @@ void __devinit scan_phb(struct pci_controller *hose) | |||
| 359 | int i, mode; | 359 | int i, mode; |
| 360 | struct resource *res; | 360 | struct resource *res; |
| 361 | 361 | ||
| 362 | DBG("Scanning PHB %s\n", node ? node->full_name : "<NO NAME>"); | 362 | DBG("PCI: Scanning PHB %s\n", node ? node->full_name : "<NO NAME>"); |
| 363 | 363 | ||
| 364 | /* Create an empty bus for the toplevel */ | 364 | /* Create an empty bus for the toplevel */ |
| 365 | bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node); | 365 | bus = pci_create_bus(hose->parent, hose->first_busno, hose->ops, node); |
| @@ -375,9 +375,22 @@ void __devinit scan_phb(struct pci_controller *hose) | |||
| 375 | pcibios_map_io_space(bus); | 375 | pcibios_map_io_space(bus); |
| 376 | 376 | ||
| 377 | /* Wire up PHB bus resources */ | 377 | /* Wire up PHB bus resources */ |
| 378 | bus->resource[0] = res = &hose->io_resource; | 378 | if (hose->io_resource.flags) { |
| 379 | for (i = 0; i < 3; ++i) | 379 | DBG("PCI: PHB IO resource = %016lx-%016lx [%lx]\n", |
| 380 | hose->io_resource.start, hose->io_resource.end, | ||
| 381 | hose->io_resource.flags); | ||
| 382 | bus->resource[0] = res = &hose->io_resource; | ||
| 383 | } | ||
| 384 | for (i = 0; i < 3; ++i) { | ||
| 385 | DBG("PCI: PHB MEM resource %d = %016lx-%016lx [%lx]\n", i, | ||
| 386 | hose->mem_resources[i].start, | ||
| 387 | hose->mem_resources[i].end, | ||
| 388 | hose->mem_resources[i].flags); | ||
| 380 | bus->resource[i+1] = &hose->mem_resources[i]; | 389 | bus->resource[i+1] = &hose->mem_resources[i]; |
| 390 | } | ||
| 391 | DBG("PCI: PHB MEM offset = %016lx\n", hose->pci_mem_offset); | ||
| 392 | DBG("PCI: PHB IO offset = %08lx\n", | ||
| 393 | (unsigned long)hose->io_base_virt - _IO_BASE); | ||
| 381 | 394 | ||
| 382 | /* Get probe mode and perform scan */ | 395 | /* Get probe mode and perform scan */ |
| 383 | mode = PCI_PROBE_NORMAL; | 396 | mode = PCI_PROBE_NORMAL; |
diff --git a/arch/powerpc/platforms/iseries/pci.c b/arch/powerpc/platforms/iseries/pci.c index 68f248b4c69..30e3d992dc0 100644 --- a/arch/powerpc/platforms/iseries/pci.c +++ b/arch/powerpc/platforms/iseries/pci.c | |||
| @@ -20,6 +20,9 @@ | |||
| 20 | * along with this program; if not, write to the Free Software | 20 | * along with this program; if not, write to the Free Software |
| 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | 21 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
| 22 | */ | 22 | */ |
| 23 | |||
| 24 | #undef DEBUG | ||
| 25 | |||
| 23 | #include <linux/kernel.h> | 26 | #include <linux/kernel.h> |
| 24 | #include <linux/list.h> | 27 | #include <linux/list.h> |
| 25 | #include <linux/string.h> | 28 | #include <linux/string.h> |
| @@ -58,6 +61,7 @@ static int limit_pci_retries = 1; /* Set Retry Error on. */ | |||
| 58 | #define IOMM_TABLE_MAX_ENTRIES 1024 | 61 | #define IOMM_TABLE_MAX_ENTRIES 1024 |
| 59 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL | 62 | #define IOMM_TABLE_ENTRY_SIZE 0x0000000000400000UL |
| 60 | #define BASE_IO_MEMORY 0xE000000000000000UL | 63 | #define BASE_IO_MEMORY 0xE000000000000000UL |
| 64 | #define END_IO_MEMORY 0xEFFFFFFFFFFFFFFFUL | ||
| 61 | 65 | ||
| 62 | static unsigned long max_io_memory = BASE_IO_MEMORY; | 66 | static unsigned long max_io_memory = BASE_IO_MEMORY; |
| 63 | static long current_iomm_table_entry; | 67 | static long current_iomm_table_entry; |
| @@ -68,7 +72,6 @@ static long current_iomm_table_entry; | |||
| 68 | static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; | 72 | static struct device_node *iomm_table[IOMM_TABLE_MAX_ENTRIES]; |
| 69 | static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES]; | 73 | static u8 iobar_table[IOMM_TABLE_MAX_ENTRIES]; |
| 70 | 74 | ||
| 71 | static const char pci_io_text[] = "iSeries PCI I/O"; | ||
| 72 | static DEFINE_SPINLOCK(iomm_table_lock); | 75 | static DEFINE_SPINLOCK(iomm_table_lock); |
| 73 | 76 | ||
| 74 | /* | 77 | /* |
| @@ -279,8 +282,8 @@ out_free: | |||
| 279 | * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet | 282 | * PCI: Bus 0, Device 26, Vendor 0x12AE Frame 1, Card C10 Ethernet |
| 280 | * controller | 283 | * controller |
| 281 | */ | 284 | */ |
| 282 | static void __init iseries_device_information(struct pci_dev *pdev, int count, | 285 | static void __init iseries_device_information(struct pci_dev *pdev, |
| 283 | u16 bus, HvSubBusNumber subbus) | 286 | u16 bus, HvSubBusNumber subbus) |
| 284 | { | 287 | { |
| 285 | u8 frame = 0; | 288 | u8 frame = 0; |
| 286 | char card[4]; | 289 | char card[4]; |
| @@ -290,10 +293,9 @@ static void __init iseries_device_information(struct pci_dev *pdev, int count, | |||
| 290 | ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); | 293 | ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus)); |
| 291 | 294 | ||
| 292 | if (iseries_get_location_code(bus, agent, &frame, card)) { | 295 | if (iseries_get_location_code(bus, agent, &frame, card)) { |
| 293 | printk("%d. PCI: Bus%3d, Device%3d, Vendor %04X Frame%3d, " | 296 | printk(KERN_INFO "PCI: %s, Vendor %04X Frame%3d, " |
| 294 | "Card %4s 0x%04X\n", count, bus, | 297 | "Card %4s 0x%04X\n", pci_name(pdev), pdev->vendor, |
| 295 | PCI_SLOT(pdev->devfn), pdev->vendor, frame, | 298 | frame, card, (int)(pdev->class >> 8)); |
| 296 | card, (int)(pdev->class >> 8)); | ||
| 297 | } | 299 | } |
| 298 | } | 300 | } |
| 299 | 301 | ||
| @@ -323,7 +325,6 @@ static void __init iomm_table_allocate_entry(struct pci_dev *dev, int bar_num) | |||
| 323 | * Set Resource values. | 325 | * Set Resource values. |
| 324 | */ | 326 | */ |
| 325 | spin_lock(&iomm_table_lock); | 327 | spin_lock(&iomm_table_lock); |
| 326 | bar_res->name = pci_io_text; | ||
| 327 | bar_res->start = BASE_IO_MEMORY + | 328 | bar_res->start = BASE_IO_MEMORY + |
| 328 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; | 329 | IOMM_TABLE_ENTRY_SIZE * current_iomm_table_entry; |
| 329 | bar_res->end = bar_res->start + bar_size - 1; | 330 | bar_res->end = bar_res->start + bar_size - 1; |
| @@ -393,61 +394,63 @@ static struct device_node *find_device_node(int bus, int devfn) | |||
| 393 | } | 394 | } |
| 394 | 395 | ||
| 395 | /* | 396 | /* |
| 396 | * iSeries_pci_final_fixup(void) | 397 | * iSeries_pcibios_fixup_resources |
| 398 | * | ||
| 399 | * Fixes up all resources for devices | ||
| 397 | */ | 400 | */ |
| 398 | void __init iSeries_pci_final_fixup(void) | 401 | void __init iSeries_pcibios_fixup_resources(struct pci_dev *pdev) |
| 399 | { | 402 | { |
| 400 | struct pci_dev *pdev = NULL; | 403 | const u32 *agent; |
| 404 | const u32 *sub_bus; | ||
| 405 | unsigned char bus = pdev->bus->number; | ||
| 401 | struct device_node *node; | 406 | struct device_node *node; |
| 402 | int num_dev = 0; | 407 | int i; |
| 403 | 408 | ||
| 404 | /* Fix up at the device node and pci_dev relationship */ | 409 | node = find_device_node(bus, pdev->devfn); |
| 405 | mf_display_src(0xC9000100); | 410 | pr_debug("PCI: iSeries %s, pdev %p, node %p\n", |
| 406 | 411 | pci_name(pdev), pdev, node); | |
| 407 | printk("pcibios_final_fixup\n"); | 412 | if (!node) { |
| 408 | for_each_pci_dev(pdev) { | 413 | printk("PCI: %s disabled, device tree entry not found !\n", |
| 409 | const u32 *agent; | 414 | pci_name(pdev)); |
| 410 | const u32 *sub_bus; | 415 | for (i = 0; i <= PCI_ROM_RESOURCE; i++) |
| 411 | unsigned char bus = pdev->bus->number; | 416 | pdev->resource[i].flags = 0; |
| 412 | 417 | return; | |
| 413 | node = find_device_node(bus, pdev->devfn); | 418 | } |
| 414 | printk("pci dev %p (%x.%x), node %p\n", pdev, bus, | 419 | sub_bus = of_get_property(node, "linux,subbus", NULL); |
| 415 | pdev->devfn, node); | 420 | agent = of_get_property(node, "linux,agent-id", NULL); |
| 416 | if (!node) { | 421 | if (agent && sub_bus) { |
| 417 | printk("PCI: Device Tree not found for 0x%016lX\n", | 422 | u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus); |
| 418 | (unsigned long)pdev); | 423 | int err; |
| 419 | continue; | 424 | |
| 420 | } | 425 | err = HvCallXm_connectBusUnit(bus, *sub_bus, *agent, irq); |
| 421 | 426 | if (err) | |
| 422 | agent = of_get_property(node, "linux,agent-id", NULL); | 427 | pci_log_error("Connect Bus Unit", |
| 423 | sub_bus = of_get_property(node, "linux,subbus", NULL); | 428 | bus, *sub_bus, *agent, err); |
| 424 | if (agent && sub_bus) { | 429 | else { |
| 425 | u8 irq = iSeries_allocate_IRQ(bus, 0, *sub_bus); | 430 | err = HvCallPci_configStore8(bus, *sub_bus, |
| 426 | int err; | ||
| 427 | |||
| 428 | err = HvCallXm_connectBusUnit(bus, *sub_bus, | ||
| 429 | *agent, irq); | ||
| 430 | if (err) | ||
| 431 | pci_log_error("Connect Bus Unit", | ||
| 432 | bus, *sub_bus, *agent, err); | ||
| 433 | else { | ||
| 434 | err = HvCallPci_configStore8(bus, *sub_bus, | ||
| 435 | *agent, PCI_INTERRUPT_LINE, irq); | 431 | *agent, PCI_INTERRUPT_LINE, irq); |
| 436 | if (err) | 432 | if (err) |
| 437 | pci_log_error("PciCfgStore Irq Failed!", | 433 | pci_log_error("PciCfgStore Irq Failed!", |
| 438 | bus, *sub_bus, *agent, err); | 434 | bus, *sub_bus, *agent, err); |
| 439 | else | 435 | else |
| 440 | pdev->irq = irq; | 436 | pdev->irq = irq; |
| 441 | } | ||
| 442 | } | 437 | } |
| 443 | |||
| 444 | num_dev++; | ||
| 445 | pdev->sysdata = node; | ||
| 446 | PCI_DN(node)->pcidev = pdev; | ||
| 447 | allocate_device_bars(pdev); | ||
| 448 | iseries_device_information(pdev, num_dev, bus, *sub_bus); | ||
| 449 | iommu_devnode_init_iSeries(pdev, node); | ||
| 450 | } | 438 | } |
| 439 | |||
| 440 | pdev->sysdata = node; | ||
| 441 | PCI_DN(node)->pcidev = pdev; | ||
| 442 | allocate_device_bars(pdev); | ||
| 443 | iseries_device_information(pdev, bus, *sub_bus); | ||
| 444 | iommu_devnode_init_iSeries(pdev, node); | ||
| 445 | } | ||
| 446 | |||
| 447 | /* | ||
| 448 | * iSeries_pci_final_fixup(void) | ||
| 449 | */ | ||
| 450 | void __init iSeries_pci_final_fixup(void) | ||
| 451 | { | ||
| 452 | /* Fix up at the device node and pci_dev relationship */ | ||
| 453 | mf_display_src(0xC9000100); | ||
| 451 | iSeries_activate_IRQs(); | 454 | iSeries_activate_IRQs(); |
| 452 | mf_display_src(0xC9000200); | 455 | mf_display_src(0xC9000200); |
| 453 | } | 456 | } |
| @@ -894,10 +897,18 @@ void __init iSeries_pcibios_init(void) | |||
| 894 | /* All legacy iSeries PHBs are in domain zero */ | 897 | /* All legacy iSeries PHBs are in domain zero */ |
| 895 | phb->global_number = 0; | 898 | phb->global_number = 0; |
| 896 | 899 | ||
| 897 | phb->pci_mem_offset = bus; | ||
| 898 | phb->first_busno = bus; | 900 | phb->first_busno = bus; |
| 899 | phb->last_busno = bus; | 901 | phb->last_busno = bus; |
| 900 | phb->ops = &iSeries_pci_ops; | 902 | phb->ops = &iSeries_pci_ops; |
| 903 | phb->io_base_virt = (void __iomem *)_IO_BASE; | ||
| 904 | phb->io_resource.flags = IORESOURCE_IO; | ||
| 905 | phb->io_resource.start = BASE_IO_MEMORY; | ||
| 906 | phb->io_resource.end = END_IO_MEMORY; | ||
| 907 | phb->io_resource.name = "iSeries PCI IO"; | ||
| 908 | phb->mem_resources[0].flags = IORESOURCE_MEM; | ||
| 909 | phb->mem_resources[0].start = BASE_IO_MEMORY; | ||
| 910 | phb->mem_resources[0].end = END_IO_MEMORY; | ||
| 911 | phb->mem_resources[0].name = "Series PCI MEM"; | ||
| 901 | } | 912 | } |
| 902 | 913 | ||
| 903 | of_node_put(root); | 914 | of_node_put(root); |
diff --git a/arch/powerpc/platforms/iseries/pci.h b/arch/powerpc/platforms/iseries/pci.h index 180aa74afb2..d9cf974c271 100644 --- a/arch/powerpc/platforms/iseries/pci.h +++ b/arch/powerpc/platforms/iseries/pci.h | |||
| @@ -43,12 +43,16 @@ | |||
| 43 | #define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7) | 43 | #define ISERIES_GET_DEVICE_FROM_SUBBUS(subbus) ((subbus >> 5) & 0x7) |
| 44 | #define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7) | 44 | #define ISERIES_GET_FUNCTION_FROM_SUBBUS(subbus) ((subbus >> 2) & 0x7) |
| 45 | 45 | ||
| 46 | struct pci_dev; | ||
| 47 | |||
| 46 | #ifdef CONFIG_PCI | 48 | #ifdef CONFIG_PCI |
| 47 | extern void iSeries_pcibios_init(void); | 49 | extern void iSeries_pcibios_init(void); |
| 48 | extern void iSeries_pci_final_fixup(void); | 50 | extern void iSeries_pci_final_fixup(void); |
| 51 | extern void iSeries_pcibios_fixup_resources(struct pci_dev *dev); | ||
| 49 | #else | 52 | #else |
| 50 | static inline void iSeries_pcibios_init(void) { } | 53 | static inline void iSeries_pcibios_init(void) { } |
| 51 | static inline void iSeries_pci_final_fixup(void) { } | 54 | static inline void iSeries_pci_final_fixup(void) { } |
| 55 | static inline void iSeries_pcibios_fixup_resources(struct pci_dev *dev) {} | ||
| 52 | #endif | 56 | #endif |
| 53 | 57 | ||
| 54 | #endif /* _PLATFORMS_ISERIES_PCI_H */ | 58 | #endif /* _PLATFORMS_ISERIES_PCI_H */ |
diff --git a/arch/powerpc/platforms/iseries/setup.c b/arch/powerpc/platforms/iseries/setup.c index 5616219a20d..b72120751bb 100644 --- a/arch/powerpc/platforms/iseries/setup.c +++ b/arch/powerpc/platforms/iseries/setup.c | |||
| @@ -639,24 +639,25 @@ static int __init iseries_probe(void) | |||
| 639 | } | 639 | } |
| 640 | 640 | ||
| 641 | define_machine(iseries) { | 641 | define_machine(iseries) { |
| 642 | .name = "iSeries", | 642 | .name = "iSeries", |
| 643 | .setup_arch = iSeries_setup_arch, | 643 | .setup_arch = iSeries_setup_arch, |
| 644 | .show_cpuinfo = iSeries_show_cpuinfo, | 644 | .show_cpuinfo = iSeries_show_cpuinfo, |
| 645 | .init_IRQ = iSeries_init_IRQ, | 645 | .init_IRQ = iSeries_init_IRQ, |
| 646 | .get_irq = iSeries_get_irq, | 646 | .get_irq = iSeries_get_irq, |
| 647 | .init_early = iSeries_init_early, | 647 | .init_early = iSeries_init_early, |
| 648 | .pcibios_fixup = iSeries_pci_final_fixup, | 648 | .pcibios_fixup = iSeries_pci_final_fixup, |
| 649 | .restart = mf_reboot, | 649 | .pcibios_fixup_resources= iSeries_pcibios_fixup_resources, |
| 650 | .power_off = mf_power_off, | 650 | .restart = mf_reboot, |
| 651 | .halt = mf_power_off, | 651 | .power_off = mf_power_off, |
| 652 | .get_boot_time = iSeries_get_boot_time, | 652 | .halt = mf_power_off, |
| 653 | .set_rtc_time = iSeries_set_rtc_time, | 653 | .get_boot_time = iSeries_get_boot_time, |
| 654 | .get_rtc_time = iSeries_get_rtc_time, | 654 | .set_rtc_time = iSeries_set_rtc_time, |
| 655 | .calibrate_decr = generic_calibrate_decr, | 655 | .get_rtc_time = iSeries_get_rtc_time, |
| 656 | .progress = iSeries_progress, | 656 | .calibrate_decr = generic_calibrate_decr, |
| 657 | .probe = iseries_probe, | 657 | .progress = iSeries_progress, |
| 658 | .ioremap = iseries_ioremap, | 658 | .probe = iseries_probe, |
| 659 | .iounmap = iseries_iounmap, | 659 | .ioremap = iseries_ioremap, |
| 660 | .iounmap = iseries_iounmap, | ||
| 660 | /* XXX Implement enable_pmcs for iSeries */ | 661 | /* XXX Implement enable_pmcs for iSeries */ |
| 661 | }; | 662 | }; |
| 662 | 663 | ||
