diff options
Diffstat (limited to 'arch/i386/pci')
-rw-r--r-- | arch/i386/pci/Makefile | 2 | ||||
-rw-r--r-- | arch/i386/pci/common.c | 4 | ||||
-rw-r--r-- | arch/i386/pci/direct.c | 6 | ||||
-rw-r--r-- | arch/i386/pci/fixup.c | 3 | ||||
-rw-r--r-- | arch/i386/pci/i386.c | 7 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig.c | 65 | ||||
-rw-r--r-- | arch/i386/pci/pci.h | 7 |
7 files changed, 75 insertions, 19 deletions
diff --git a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile index ead6122..5461d4d 100644 --- a/arch/i386/pci/Makefile +++ b/arch/i386/pci/Makefile @@ -1,7 +1,7 @@ obj-y := i386.o obj-$(CONFIG_PCI_BIOS) += pcbios.o -obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o +obj-$(CONFIG_PCI_MMCONFIG) += mmconfig.o direct.o obj-$(CONFIG_PCI_DIRECT) += direct.o pci-y := fixup.o diff --git a/arch/i386/pci/common.c b/arch/i386/pci/common.c index c96bea1..f6bc48d 100644 --- a/arch/i386/pci/common.c +++ b/arch/i386/pci/common.c @@ -132,7 +132,7 @@ struct pci_bus * __devinit pcibios_scan_root(int busnum) } } - printk("PCI: Probing PCI hardware (bus %02x)\n", busnum); + printk(KERN_DEBUG "PCI: Probing PCI hardware (bus %02x)\n", busnum); return pci_scan_bus_parented(NULL, busnum, &pci_root_ops, NULL); } @@ -144,7 +144,7 @@ static int __init pcibios_init(void) struct cpuinfo_x86 *c = &boot_cpu_data; if (!raw_pci_ops) { - printk("PCI: System does not support PCI\n"); + printk(KERN_WARNING "PCI: System does not support PCI\n"); return 0; } diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c index 30b7e9b..e3ac502 100644 --- a/arch/i386/pci/direct.c +++ b/arch/i386/pci/direct.c @@ -13,7 +13,7 @@ #define PCI_CONF1_ADDRESS(bus, devfn, reg) \ (0x80000000 | (bus << 16) | (devfn << 8) | (reg & ~3)) -static int pci_conf1_read(unsigned int seg, unsigned int bus, +int pci_conf1_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; @@ -42,7 +42,7 @@ static int pci_conf1_read(unsigned int seg, unsigned int bus, return 0; } -static int pci_conf1_write(unsigned int seg, unsigned int bus, +int pci_conf1_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; @@ -201,7 +201,7 @@ static int __init pci_sanity_check(struct pci_raw_ops *o) return 1; } - DBG("PCI: Sanity check failed\n"); + DBG(KERN_WARNING "PCI: Sanity check failed\n"); return 0; } diff --git a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c index 3984226..eeb1b1f2d 100644 --- a/arch/i386/pci/fixup.c +++ b/arch/i386/pci/fixup.c @@ -433,9 +433,8 @@ static void __devinit pci_post_fixup_toshiba_ohci1394(struct pci_dev *dev) return; /* only applies to certain Toshibas (so far) */ /* Restore config space on Toshiba laptops */ - mdelay(10); pci_write_config_word(dev, PCI_CACHE_LINE_SIZE, toshiba_line_size); - pci_write_config_word(dev, PCI_INTERRUPT_LINE, dev->irq); + pci_read_config_byte(dev, PCI_INTERRUPT_LINE, (u8 *)&dev->irq); pci_write_config_dword(dev, PCI_BASE_ADDRESS_0, pci_resource_start(dev, 0)); pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, diff --git a/arch/i386/pci/i386.c b/arch/i386/pci/i386.c index 6d63385..ed2c8c8 100644 --- a/arch/i386/pci/i386.c +++ b/arch/i386/pci/i386.c @@ -221,6 +221,11 @@ int pcibios_enable_resources(struct pci_dev *dev, int mask) continue; r = &dev->resource[idx]; + if (!(r->flags & (IORESOURCE_IO | IORESOURCE_MEM))) + continue; + if ((idx == PCI_ROM_RESOURCE) && + (!(r->flags & IORESOURCE_ROM_ENABLE))) + continue; if (!r->start && r->end) { printk(KERN_ERR "PCI: Device %s not available because of resource collisions\n", pci_name(dev)); return -EINVAL; @@ -230,8 +235,6 @@ int pcibios_enable_resources(struct pci_dev *dev, int mask) if (r->flags & IORESOURCE_MEM) cmd |= PCI_COMMAND_MEMORY; } - if (dev->resource[PCI_ROM_RESOURCE].start) - cmd |= PCI_COMMAND_MEMORY; if (cmd != old_cmd) { printk("PCI: Enabling device %s (%04x -> %04x)\n", pci_name(dev), old_cmd, cmd); pci_write_config_word(dev, PCI_COMMAND, cmd); diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index dfbf80c..4bb4d4b 100644 --- a/arch/i386/pci/mmconfig.c +++ b/arch/i386/pci/mmconfig.c @@ -19,21 +19,25 @@ /* The base address of the last MMCONFIG device accessed */ static u32 mmcfg_last_accessed_device; +static DECLARE_BITMAP(fallback_slots, 32); + /* * Functions for accessing PCI configuration space with MMCONFIG accesses */ -static u32 get_base_addr(unsigned int seg, int bus) +static u32 get_base_addr(unsigned int seg, int bus, unsigned devfn) { int cfg_num = -1; struct acpi_table_mcfg_config *cfg; + if (seg == 0 && bus == 0 && + test_bit(PCI_SLOT(devfn), fallback_slots)) + return 0; + while (1) { ++cfg_num; if (cfg_num >= pci_mmcfg_config_num) { - /* something bad is going on, no cfg table is found. */ - /* so we fall back to the old way we used to do this */ - /* and just rely on the first entry to be correct. */ - return pci_mmcfg_config[0].base_address; + /* Not found - fallback to type 1 */ + return 0; } cfg = &pci_mmcfg_config[cfg_num]; if (cfg->pci_segment_group_number != seg) @@ -44,9 +48,9 @@ static u32 get_base_addr(unsigned int seg, int bus) } } -static inline void pci_exp_set_dev_base(unsigned int seg, int bus, int devfn) +static inline void pci_exp_set_dev_base(unsigned int base, int bus, int devfn) { - u32 dev_base = get_base_addr(seg, bus) | (bus << 20) | (devfn << 12); + u32 dev_base = base | (bus << 20) | (devfn << 12); if (dev_base != mmcfg_last_accessed_device) { mmcfg_last_accessed_device = dev_base; set_fixmap_nocache(FIX_PCIE_MCFG, dev_base); @@ -57,13 +61,18 @@ static int pci_mmcfg_read(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 *value) { unsigned long flags; + u32 base; if (!value || (bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; + base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_read(seg,bus,devfn,reg,len,value); + spin_lock_irqsave(&pci_config_lock, flags); - pci_exp_set_dev_base(seg, bus, devfn); + pci_exp_set_dev_base(base, bus, devfn); switch (len) { case 1: @@ -86,13 +95,18 @@ static int pci_mmcfg_write(unsigned int seg, unsigned int bus, unsigned int devfn, int reg, int len, u32 value) { unsigned long flags; + u32 base; if ((bus > 255) || (devfn > 255) || (reg > 4095)) return -EINVAL; + base = get_base_addr(seg, bus, devfn); + if (!base) + return pci_conf1_write(seg,bus,devfn,reg,len,value); + spin_lock_irqsave(&pci_config_lock, flags); - pci_exp_set_dev_base(seg, bus, devfn); + pci_exp_set_dev_base(base, bus, devfn); switch (len) { case 1: @@ -116,6 +130,37 @@ static struct pci_raw_ops pci_mmcfg = { .write = pci_mmcfg_write, }; +/* 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 + and assigning suitable _SEGs, but this isn't implemented in some BIOS. + Instead try to discover all devices on bus 0 that are unreachable using MM + and fallback for them. + We only do this for bus 0/seg 0 */ +static __init void unreachable_devices(void) +{ + int i; + unsigned long flags; + + for (i = 0; i < 32; i++) { + u32 val1; + u32 addr; + + pci_conf1_read(0, 0, PCI_DEVFN(i, 0), 0, 4, &val1); + if (val1 == 0xffffffff) + continue; + + /* Locking probably not needed, but safer */ + spin_lock_irqsave(&pci_config_lock, flags); + addr = get_base_addr(0, 0, PCI_DEVFN(i, 0)); + if (addr != 0) + pci_exp_set_dev_base(addr, 0, PCI_DEVFN(i, 0)); + if (addr == 0 || readl((u32 __iomem *)mmcfg_virt_addr) != val1) + set_bit(i, fallback_slots); + spin_unlock_irqrestore(&pci_config_lock, flags); + } +} + static int __init pci_mmcfg_init(void) { if ((pci_probe & PCI_PROBE_MMCONF) == 0) @@ -131,6 +176,8 @@ static int __init pci_mmcfg_init(void) raw_pci_ops = &pci_mmcfg; pci_probe = (pci_probe & ~PCI_PROBE_MASK) | PCI_PROBE_MMCONF; + unreachable_devices(); + out: return 0; } diff --git a/arch/i386/pci/pci.h b/arch/i386/pci/pci.h index 127d53a..f550781 100644 --- a/arch/i386/pci/pci.h +++ b/arch/i386/pci/pci.h @@ -74,3 +74,10 @@ extern spinlock_t pci_config_lock; extern int (*pcibios_enable_irq)(struct pci_dev *dev); extern void (*pcibios_disable_irq)(struct pci_dev *dev); + +extern int pci_conf1_write(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 value); +extern int pci_conf1_read(unsigned int seg, unsigned int bus, + unsigned int devfn, int reg, int len, u32 *value); + + |