diff options
Diffstat (limited to 'arch/i386/pci')
-rw-r--r-- | arch/i386/pci/acpi.c | 32 | ||||
-rw-r--r-- | arch/i386/pci/common.c | 36 | ||||
-rw-r--r-- | arch/i386/pci/fixup.c | 15 | ||||
-rw-r--r-- | arch/i386/pci/irq.c | 6 | ||||
-rw-r--r-- | arch/i386/pci/legacy.c | 2 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig-shared.c | 48 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig.c | 14 | ||||
-rw-r--r-- | arch/i386/pci/numa.c | 15 | ||||
-rw-r--r-- | arch/i386/pci/pcbios.c | 2 | ||||
-rw-r--r-- | arch/i386/pci/pci.h | 43 | ||||
-rw-r--r-- | arch/i386/pci/visws.c | 4 |
11 files changed, 179 insertions, 38 deletions
diff --git a/arch/i386/pci/acpi.c b/arch/i386/pci/acpi.c index b33aea845f58..bc8a44bddaa7 100644 --- a/arch/i386/pci/acpi.c +++ b/arch/i386/pci/acpi.c @@ -8,20 +8,42 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int domain, int busnum) { struct pci_bus *bus; + struct pci_sysdata *sd; + int pxm; + + /* Allocate per-root-bus (not per bus) arch-specific data. + * TODO: leak; this memory is never freed. + * It's arguable whether it's worth the trouble to care. + */ + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) { + printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum); + return NULL; + } if (domain != 0) { printk(KERN_WARNING "PCI: Multiple domains not supported\n"); + kfree(sd); return NULL; } - bus = pcibios_scan_root(busnum); + sd->node = -1; + + pxm = acpi_get_pxm(device->handle); +#ifdef CONFIG_ACPI_NUMA + if (pxm >= 0) + sd->node = pxm_to_node(pxm); +#endif + + bus = pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); + if (!bus) + kfree(sd); + #ifdef CONFIG_ACPI_NUMA if (bus != NULL) { - int pxm = acpi_get_pxm(device->handle); if (pxm >= 0) { - bus->sysdata = (void *)(unsigned long)pxm_to_node(pxm); - printk("bus %d -> pxm %d -> node %ld\n", - busnum, pxm, (long)(bus->sysdata)); + printk("bus %d -> pxm %d -> node %d\n", + busnum, pxm, sd->node); } } #endif diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index 3f78d4d8ecf3..ebc6f3c66340 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -293,6 +293,7 @@ static struct dmi_system_id __devinitdata pciprobe_dmi_table[] = { struct pci_bus * __devinit pcibios_scan_root(int busnum) { struct pci_bus *bus = NULL; + struct pci_sysdata *sd; dmi_check_system(pciprobe_dmi_table); @@ -303,9 +304,19 @@ struct pci_bus * __devinit pcibios_scan_root(int busnum) } } + /* Allocate per-root-bus (not per bus) arch-specific data. + * TODO: leak; this memory is never freed. + * It's arguable whether it's worth the trouble to care. + */ + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) { + printk(KERN_ERR "PCI: OOM, not probing PCI bus %02x\n", busnum); + return NULL; + } + printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum); - return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, NULL); + return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, sd); } extern u8 pci_cache_line_size; @@ -444,3 +455,26 @@ void pcibios_disable_device (struct pci_dev *dev) if (!dev->msi_enabled && pcibios_disable_irq) pcibios_disable_irq(dev); } + +struct pci_bus *pci_scan_bus_with_sysdata(int busno) +{ + struct pci_bus *bus = NULL; + struct pci_sysdata *sd; + + /* + * Allocate per-root-bus (not per bus) arch-specific data. + * TODO: leak; this memory is never freed. + * It's arguable whether it's worth the trouble to care. + */ + sd = kzalloc(sizeof(*sd), GFP_KERNEL); + if (!sd) { + printk(KERN_ERR "PCI: OOM, skipping PCI bus %02x\n", busno); + return NULL; + } + sd->node = -1; + bus = pci_scan_bus(busno, &pci_root_ops, sd); + if (!bus) + kfree(sd); + + return bus; +} diff --git a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c index b95b42950ed4..c82cbf4c7226 100644 --- a/arch/i386/pci/fixup.c +++ b/arch/i386/pci/fixup.c @@ -25,9 +25,9 @@ static void __devinit pci_fixup_i450nx(struct pci_dev *d) pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); if (busno) - pci_scan_bus(busno, &pci_root_ops, NULL); /* Bus A */ + pci_scan_bus_with_sysdata(busno); /* Bus A */ if (suba < subb) - pci_scan_bus(suba+1, &pci_root_ops, NULL); /* Bus B */ + pci_scan_bus_with_sysdata(suba+1); /* Bus B */ } pcibios_last_bus = -1; } @@ -42,7 +42,7 @@ static void __devinit pci_fixup_i450gx(struct pci_dev *d) u8 busno; pci_read_config_byte(d, 0x4a, &busno); printk(KERN_INFO "PCI: i440KX/GX host bridge %s: secondary bus %02x\n", pci_name(d), busno); - pci_scan_bus(busno, &pci_root_ops, NULL); + pci_scan_bus_with_sysdata(busno); pcibios_last_bus = -1; } DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454GX, pci_fixup_i450gx); @@ -118,12 +118,9 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_3, pci static void pci_fixup_via_northbridge_bug(struct pci_dev *d) { u8 v; - u8 revision; int where = 0x55; int mask = 0x1f; /* clear bits 5, 6, 7 by default */ - pci_read_config_byte(d, PCI_REVISION_ID, &revision); - if (d->device == PCI_DEVICE_ID_VIA_8367_0) { /* fix pci bus latency issues resulted by NB bios error it appears on bug free^Wreduced kt266x's bios forces @@ -133,8 +130,8 @@ static void pci_fixup_via_northbridge_bug(struct pci_dev *d) where = 0x95; /* the memory write queue timer register is different for the KT266x's: 0x95 not 0x55 */ } else if (d->device == PCI_DEVICE_ID_VIA_8363_0 && - (revision == VIA_8363_KL133_REVISION_ID || - revision == VIA_8363_KM133_REVISION_ID)) { + (d->revision == VIA_8363_KL133_REVISION_ID || + d->revision == VIA_8363_KM133_REVISION_ID)) { mask = 0x3f; /* clear only bits 6 and 7; clearing bit 5 causes screen corruption on the KL133/KM133 */ } @@ -142,7 +139,7 @@ static void pci_fixup_via_northbridge_bug(struct pci_dev *d) pci_read_config_byte(d, where, &v); if (v & ~mask) { printk(KERN_WARNING "Disabling VIA memory write queue (PCI ID %04x, rev %02x): [%02x] %02x & %02x -> %02x\n", \ - d->device, revision, where, v, mask, v & mask); + d->device, d->revision, where, v, mask, v & mask); v &= mask; pci_write_config_byte(d, where, v); } diff --git a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c index f2cb942f8281..8434f2323b87 100644 --- a/arch/i386/pci/irq.c +++ b/arch/i386/pci/irq.c @@ -138,8 +138,9 @@ static void __init pirq_peer_trick(void) for(i = 1; i < 256; i++) { if (!busmap[i] || pci_find_bus(0, i)) continue; - if (pci_scan_bus(i, &pci_root_ops, NULL)) - printk(KERN_INFO "PCI: Discovered primary peer bus %02x [IRQ]\n", i); + if (pci_scan_bus_with_sysdata(i)) + printk(KERN_INFO "PCI: Discovered primary peer " + "bus %02x [IRQ]\n", i); } pcibios_last_bus = -1; } @@ -549,6 +550,7 @@ static __init int intel_router_probe(struct irq_router *r, struct pci_dev *route case PCI_DEVICE_ID_INTEL_ICH9_3: case PCI_DEVICE_ID_INTEL_ICH9_4: case PCI_DEVICE_ID_INTEL_ICH9_5: + case PCI_DEVICE_ID_INTEL_TOLAPAI_0: r->name = "PIIX/ICH"; r->get = pirq_piix_get; r->set = pirq_piix_set; diff --git a/arch/i386/pci/legacy.c b/arch/i386/pci/legacy.c index 149a9588c256..5565d7016b75 100644 --- a/arch/i386/pci/legacy.c +++ b/arch/i386/pci/legacy.c @@ -26,7 +26,7 @@ static void __devinit pcibios_fixup_peer_bridges(void) l != 0x0000 && l != 0xffff) { DBG("Found device at %02x:%02x [%04x]\n", n, devfn, l); printk(KERN_INFO "PCI: Discovered peer bus %02x\n", n); - pci_scan_bus(n, &pci_root_ops, NULL); + pci_scan_bus_with_sysdata(n); break; } } diff --git a/arch/i386/pci/mmconfig-shared.c b/arch/i386/pci/mmconfig-shared.c index c7cabeed4d7b..4df637e34f81 100644 --- a/arch/i386/pci/mmconfig-shared.c +++ b/arch/i386/pci/mmconfig-shared.c @@ -24,6 +24,9 @@ DECLARE_BITMAP(pci_mmcfg_fallback_slots, 32*PCI_MMCFG_MAX_CHECK_BUS); +/* Indicate if the mmcfg resources have been placed into the resource table. */ +static int __initdata pci_mmcfg_resources_inserted; + /* K8 systems have some devices (typically in the builtin northbridge) that are only accessible using type1 Normally this can be expressed in the MCFG by not listing them @@ -170,7 +173,7 @@ static int __init pci_mmcfg_check_hostbridge(void) return name != NULL; } -static void __init pci_mmcfg_insert_resources(void) +static void __init pci_mmcfg_insert_resources(unsigned long resource_flags) { #define PCI_MMCFG_RESOURCE_NAME_LEN 19 int i; @@ -194,10 +197,13 @@ static void __init pci_mmcfg_insert_resources(void) cfg->pci_segment); res->start = cfg->address; res->end = res->start + (num_buses << 20) - 1; - res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + res->flags = IORESOURCE_MEM | resource_flags; insert_resource(&iomem_resource, res); names += PCI_MMCFG_RESOURCE_NAME_LEN; } + + /* Mark that the resources have been inserted. */ + pci_mmcfg_resources_inserted = 1; } static void __init pci_mmcfg_reject_broken(int type) @@ -267,7 +273,43 @@ void __init pci_mmcfg_init(int type) if (type == 1) unreachable_devices(); if (known_bridge) - pci_mmcfg_insert_resources(); + pci_mmcfg_insert_resources(IORESOURCE_BUSY); pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + } else { + /* + * Signal not to attempt to insert mmcfg resources because + * the architecture mmcfg setup could not initialize. + */ + pci_mmcfg_resources_inserted = 1; } } + +static int __init pci_mmcfg_late_insert_resources(void) +{ + /* + * If resources are already inserted or we are not using MMCONFIG, + * don't insert the resources. + */ + if ((pci_mmcfg_resources_inserted == 1) || + (pci_probe & PCI_PROBE_MMCONF) == 0 || + (pci_mmcfg_config_num == 0) || + (pci_mmcfg_config == NULL) || + (pci_mmcfg_config[0].address == 0)) + return 1; + + /* + * Attempt to insert the mmcfg resources but not with the busy flag + * marked so it won't cause request errors when __request_region is + * called. + */ + pci_mmcfg_insert_resources(0); + + return 0; +} + +/* + * Perform MMCONFIG resource insertion after PCI initialization to allow for + * misprogrammed MCFG tables that state larger sizes but actually conflict + * with other system resources. + */ +late_initcall(pci_mmcfg_late_insert_resources); diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index bb1afd9e589d..1bf5816d34c8 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c @@ -82,16 +82,15 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, switch (len) { case 1: - *value = readb(mmcfg_virt_addr + reg); + *value = mmio_config_readb(mmcfg_virt_addr + reg); break; case 2: - *value = readw(mmcfg_virt_addr + reg); + *value = mmio_config_readw(mmcfg_virt_addr + reg); break; case 4: - *value = readl(mmcfg_virt_addr + reg); + *value = mmio_config_readl(mmcfg_virt_addr + reg); break; } - spin_unlock_irqrestore(&pci_config_lock, flags); return 0; @@ -116,16 +115,15 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, switch (len) { case 1: - writeb(value, mmcfg_virt_addr + reg); + mmio_config_writeb(mmcfg_virt_addr + reg, value); break; case 2: - writew(value, mmcfg_virt_addr + reg); + mmio_config_writew(mmcfg_virt_addr + reg, value); break; case 4: - writel(value, mmcfg_virt_addr + reg); + mmio_config_writel(mmcfg_virt_addr + reg, value); break; } - spin_unlock_irqrestore(&pci_config_lock, flags); return 0; diff --git a/arch/i386/pci/numa.c b/arch/i386/pci/numa.c index adbe17a38f6f..f5f165f69e0c 100644 --- a/arch/i386/pci/numa.c +++ b/arch/i386/pci/numa.c @@ -96,10 +96,14 @@ static void __devinit pci_fixup_i450nx(struct pci_dev *d) pci_read_config_byte(d, reg++, &suba); pci_read_config_byte(d, reg++, &subb); DBG("i450NX PXB %d: %02x/%02x/%02x\n", pxb, busno, suba, subb); - if (busno) - pci_scan_bus(QUADLOCAL2BUS(quad,busno), &pci_root_ops, NULL); /* Bus A */ - if (suba < subb) - pci_scan_bus(QUADLOCAL2BUS(quad,suba+1), &pci_root_ops, NULL); /* Bus B */ + if (busno) { + /* Bus A */ + pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, busno)); + } + if (suba < subb) { + /* Bus B */ + pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, suba+1)); + } } pcibios_last_bus = -1; } @@ -123,8 +127,7 @@ static int __init pci_numa_init(void) continue; printk("Scanning PCI bus %d for quad %d\n", QUADLOCAL2BUS(quad,0), quad); - pci_scan_bus(QUADLOCAL2BUS(quad,0), - &pci_root_ops, NULL); + pci_scan_bus_with_sysdata(QUADLOCAL2BUS(quad, 0)); } return 0; } diff --git a/arch/i386/pci/pcbios.c b/arch/i386/pci/pcbios.c index 5f5193401bea..10ac8c316c46 100644 --- a/arch/i386/pci/pcbios.c +++ b/arch/i386/pci/pcbios.c @@ -412,7 +412,7 @@ struct irq_routing_options { u16 segment; } __attribute__((packed)); -struct irq_routing_table * __devinit pcibios_get_irq_routing_table(void) +struct irq_routing_table * pcibios_get_irq_routing_table(void) { struct irq_routing_options opt; struct irq_routing_table *rt = NULL; diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h index e58bae2076ad..8c66f275756f 100644 --- a/arch/i386/pci/pci.h +++ b/arch/i386/pci/pci.h @@ -104,3 +104,46 @@ extern DECLARE_BITMAP(pci_mmcfg_fallback_slots, 32*PCI_MMCFG_MAX_CHECK_BUS); extern int __init pci_mmcfg_arch_reachable(unsigned int seg, unsigned int bus, unsigned int devfn); extern int __init pci_mmcfg_arch_init(void); + +/* + * AMD Fam10h CPUs are buggy, and cannot access MMIO config space + * on their northbrige except through the * %eax register. As such, you MUST + * NOT use normal IOMEM accesses, you need to only use the magic mmio-config + * accessor functions. + * In fact just use pci_config_*, nothing else please. + */ +static inline unsigned char mmio_config_readb(void __iomem *pos) +{ + u8 val; + asm volatile("movb (%1),%%al" : "=a" (val) : "r" (pos)); + return val; +} + +static inline unsigned short mmio_config_readw(void __iomem *pos) +{ + u16 val; + asm volatile("movw (%1),%%ax" : "=a" (val) : "r" (pos)); + return val; +} + +static inline unsigned int mmio_config_readl(void __iomem *pos) +{ + u32 val; + asm volatile("movl (%1),%%eax" : "=a" (val) : "r" (pos)); + return val; +} + +static inline void mmio_config_writeb(void __iomem *pos, u8 val) +{ + asm volatile("movb %%al,(%1)" :: "a" (val), "r" (pos) : "memory"); +} + +static inline void mmio_config_writew(void __iomem *pos, u16 val) +{ + asm volatile("movw %%ax,(%1)" :: "a" (val), "r" (pos) : "memory"); +} + +static inline void mmio_config_writel(void __iomem *pos, u32 val) +{ + asm volatile("movl %%eax,(%1)" :: "a" (val), "r" (pos) : "memory"); +} diff --git a/arch/i386/pci/visws.c b/arch/i386/pci/visws.c index f1b486d4190b..8ecb1c722594 100644 --- a/arch/i386/pci/visws.c +++ b/arch/i386/pci/visws.c @@ -101,8 +101,8 @@ static int __init pcibios_init(void) "bridge B (PIIX4) bus: %u\n", pci_bus1, pci_bus0); raw_pci_ops = &pci_direct_conf1; - pci_scan_bus(pci_bus0, &pci_root_ops, NULL); - pci_scan_bus(pci_bus1, &pci_root_ops, NULL); + pci_scan_bus_with_sysdata(pci_bus0); + pci_scan_bus_with_sysdata(pci_bus1); pci_fixup_irqs(visws_swizzle, visws_map_irq); pcibios_resource_survey(); return 0; |