diff options
Diffstat (limited to 'arch/i386/pci')
-rw-r--r-- | arch/i386/pci/Makefile | 2 | ||||
-rw-r--r-- | arch/i386/pci/acpi.c | 2 | ||||
-rw-r--r-- | arch/i386/pci/direct.c | 4 | ||||
-rw-r--r-- | arch/i386/pci/fixup.c | 23 | ||||
-rw-r--r-- | arch/i386/pci/irq.c | 44 | ||||
-rw-r--r-- | arch/i386/pci/mmconfig.c | 65 | ||||
-rw-r--r-- | arch/i386/pci/pci.h | 7 |
7 files changed, 114 insertions, 33 deletions
diff --git a/arch/i386/pci/Makefile b/arch/i386/pci/Makefile index ead6122dd06d..5461d4d5ea1e 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/acpi.c b/arch/i386/pci/acpi.c index 4c4522b43be5..b33aea845f58 100644 --- a/arch/i386/pci/acpi.c +++ b/arch/i386/pci/acpi.c @@ -53,7 +53,7 @@ static int __init pci_acpi_init(void) * don't use pci_enable_device(). */ printk(KERN_INFO "PCI: Routing PCI interrupts for all devices because \"pci=routeirq\" specified\n"); - while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) + for_each_pci_dev(dev) acpi_pci_irq_enable(dev); } else printk(KERN_INFO "PCI: If a device doesn't work, try \"pci=routeirq\". If it helps, post a report\n"); diff --git a/arch/i386/pci/direct.c b/arch/i386/pci/direct.c index 94331d6be7a3..e3ac502bf2fb 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; diff --git a/arch/i386/pci/fixup.c b/arch/i386/pci/fixup.c index eeb1b1f2d548..83c3645ccc43 100644 --- a/arch/i386/pci/fixup.c +++ b/arch/i386/pci/fixup.c @@ -413,6 +413,13 @@ static struct dmi_system_id __devinitdata toshiba_ohci1394_dmi_table[] = { DMI_MATCH(DMI_PRODUCT_VERSION, "PSM4"), }, }, + { + .ident = "Toshiba A40 based laptop", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA"), + DMI_MATCH(DMI_PRODUCT_VERSION, "PSA40U"), + }, + }, { } }; @@ -442,3 +449,19 @@ static void __devinit pci_post_fixup_toshiba_ohci1394(struct pci_dev *dev) } DECLARE_PCI_FIXUP_ENABLE(PCI_VENDOR_ID_TI, 0x8032, pci_post_fixup_toshiba_ohci1394); + + +/* + * Prevent the BIOS trapping accesses to the Cyrix CS5530A video device + * configuration space. + */ +static void __devinit pci_early_fixup_cyrix_5530(struct pci_dev *dev) +{ + u8 r; + /* clear 'F4 Video Configuration Trap' bit */ + pci_read_config_byte(dev, 0x42, &r); + r &= 0xfd; + pci_write_config_byte(dev, 0x42, r); +} +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_CYRIX, PCI_DEVICE_ID_CYRIX_5530_LEGACY, + pci_early_fixup_cyrix_5530); diff --git a/arch/i386/pci/irq.c b/arch/i386/pci/irq.c index 19e6f4871d1e..e715aa930036 100644 --- a/arch/i386/pci/irq.c +++ b/arch/i386/pci/irq.c @@ -78,7 +78,7 @@ static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr) for (i=0; i < rt->size; i++) sum += addr[i]; if (!sum) { - DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + DBG(KERN_DEBUG "PCI: Interrupt Routing Table found at 0x%p\n", rt); return rt; } return NULL; @@ -128,7 +128,7 @@ static void __init pirq_peer_trick(void) #ifdef DEBUG { int j; - DBG("%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); + DBG(KERN_DEBUG "%02x:%02x slot=%02x", e->bus, e->devfn/8, e->slot); for(j=0; j<4; j++) DBG(" %d:%02x/%04x", j, e->irq[j].link, e->irq[j].bitmap); DBG("\n"); @@ -160,10 +160,10 @@ void eisa_set_level_irq(unsigned int irq) return; eisa_irq_mask |= (1 << irq); - printk("PCI: setting IRQ %u as level-triggered\n", irq); + printk(KERN_DEBUG "PCI: setting IRQ %u as level-triggered\n", irq); val = inb(port); if (!(val & mask)) { - DBG(" -> edge"); + DBG(KERN_DEBUG " -> edge"); outb(val | mask, port); } } @@ -677,11 +677,11 @@ static __init int ali_router_probe(struct irq_router *r, struct pci_dev *router, { case PCI_DEVICE_ID_AL_M1533: case PCI_DEVICE_ID_AL_M1563: - printk("PCI: Using ALI IRQ Router\n"); - r->name = "ALI"; - r->get = pirq_ali_get; - r->set = pirq_ali_set; - return 1; + printk(KERN_DEBUG "PCI: Using ALI IRQ Router\n"); + r->name = "ALI"; + r->get = pirq_ali_get; + r->set = pirq_ali_set; + return 1; } return 0; } @@ -749,12 +749,13 @@ static void __init pirq_find_router(struct irq_router *r) r->get = NULL; r->set = NULL; - DBG("PCI: Attempting to find IRQ router for %04x:%04x\n", + DBG(KERN_DEBUG "PCI: Attempting to find IRQ router for %04x:%04x\n", rt->rtr_vendor, rt->rtr_device); pirq_router_dev = pci_find_slot(rt->rtr_bus, rt->rtr_devfn); if (!pirq_router_dev) { - DBG("PCI: Interrupt router not found at %02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); + DBG(KERN_DEBUG "PCI: Interrupt router not found at " + "%02x:%02x\n", rt->rtr_bus, rt->rtr_devfn); return; } @@ -799,7 +800,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) /* Find IRQ pin */ pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); if (!pin) { - DBG(" -> no interrupt pin\n"); + DBG(KERN_DEBUG " -> no interrupt pin\n"); return 0; } pin = pin - 1; @@ -809,16 +810,16 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) if (!pirq_table) return 0; - DBG("IRQ for %s[%c]", pci_name(dev), 'A' + pin); + DBG(KERN_DEBUG "IRQ for %s[%c]", pci_name(dev), 'A' + pin); info = pirq_get_info(dev); if (!info) { - DBG(" -> not found in routing table\n"); + DBG(" -> not found in routing table\n" KERN_DEBUG); return 0; } pirq = info->irq[pin].link; mask = info->irq[pin].bitmap; if (!pirq) { - DBG(" -> not routed\n"); + DBG(" -> not routed\n" KERN_DEBUG); return 0; } DBG(" -> PIRQ %02x, mask %04x, excl %04x", pirq, mask, pirq_table->exclusive_irqs); @@ -846,9 +847,12 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) * reported by the device if possible. */ newirq = dev->irq; - if (!((1 << newirq) & mask)) { + if (newirq && !((1 << newirq) & mask)) { if ( pci_probe & PCI_USE_PIRQ_MASK) newirq = 0; - else printk(KERN_WARNING "PCI: IRQ %i for device %s doesn't match PIRQ mask - try pci=usepirqmask\n", newirq, pci_name(dev)); + else printk("\n" KERN_WARNING + "PCI: IRQ %i for device %s doesn't match PIRQ mask " + "- try pci=usepirqmask\n" KERN_DEBUG, newirq, + pci_name(dev)); } if (!newirq && assign) { for (i = 0; i < 16; i++) { @@ -923,14 +927,14 @@ static void __init pcibios_fixup_irqs(void) struct pci_dev *dev = NULL; u8 pin; - DBG("PCI: IRQ fixup\n"); + DBG(KERN_DEBUG "PCI: IRQ fixup\n"); while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { /* * If the BIOS has set an out of range IRQ number, just ignore it. * Also keep track of which IRQ's are already in use. */ if (dev->irq >= 16) { - DBG("%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq); + DBG(KERN_DEBUG "%s: ignoring bogus IRQ %d\n", pci_name(dev), dev->irq); dev->irq = 0; } /* If the IRQ is already assigned to a PCI device, ignore its ISA use penalty */ @@ -1039,7 +1043,7 @@ static struct dmi_system_id __initdata pciirq_dmi_table[] = { static int __init pcibios_irq_init(void) { - DBG("PCI: IRQ init\n"); + DBG(KERN_DEBUG "PCI: IRQ init\n"); if (pcibios_enable_irq || raw_pci_ops == NULL) return 0; diff --git a/arch/i386/pci/mmconfig.c b/arch/i386/pci/mmconfig.c index dfbf80cff834..4bb4d4b0f73a 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 127d53ad16be..f550781ec310 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); + + |