Signed-off-by: Andrew Morton --- 25-akpm/Documentation/kernel-parameters.txt | 4 + 25-akpm/Documentation/pci.txt | 1 25-akpm/Documentation/power/pci.txt | 34 ----------- 25-akpm/arch/i386/pci/common.c | 6 +- 25-akpm/arch/i386/pci/irq.c | 51 ++++++++++++----- 25-akpm/arch/i386/pci/pci.h | 1 25-akpm/arch/x86_64/Kconfig | 2 25-akpm/drivers/pci/bus.c | 60 +++++++++++++++----- 25-akpm/drivers/pci/pci-acpi.c | 2 25-akpm/drivers/pci/pci-sysfs.c | 82 +++++++++++++++++++--------- 25-akpm/drivers/pci/pci.c | 69 +++++++++++++++++++---- 25-akpm/drivers/pci/probe.c | 13 +--- 25-akpm/drivers/pci/proc.c | 1 25-akpm/drivers/pci/quirks.c | 1 14 files changed, 220 insertions(+), 107 deletions(-) diff -puN arch/i386/pci/common.c~gregkh-pci arch/i386/pci/common.c --- 25/arch/i386/pci/common.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/arch/i386/pci/common.c 2005-04-10 17:27:02.000000000 -0700 @@ -25,7 +25,8 @@ unsigned int pci_probe = PCI_PROBE_BIOS int pci_routeirq; int pcibios_last_bus = -1; -struct pci_bus *pci_root_bus = NULL; +unsigned long pirq_table_addr; +struct pci_bus *pci_root_bus; struct pci_raw_ops *raw_pci_ops; static int pci_read(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *value) @@ -188,6 +189,9 @@ char * __devinit pcibios_setup(char *st } else if (!strcmp(str, "biosirq")) { pci_probe |= PCI_BIOS_IRQ_SCAN; return NULL; + } else if (!strncmp(str, "pirqaddr=", 9)) { + pirq_table_addr = simple_strtoul(str+9, NULL, 0); + return NULL; } #endif #ifdef CONFIG_PCI_DIRECT diff -puN arch/i386/pci/irq.c~gregkh-pci arch/i386/pci/irq.c --- 25/arch/i386/pci/irq.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/arch/i386/pci/irq.c 2005-04-10 17:27:02.000000000 -0700 @@ -58,6 +58,35 @@ struct irq_router_handler { int (*pcibios_enable_irq)(struct pci_dev *dev) = NULL; /* + * Check passed address for the PCI IRQ Routing Table signature + * and perform checksum verification. + */ + +static inline struct irq_routing_table * pirq_check_routing_table(u8 *addr) +{ + struct irq_routing_table *rt; + int i; + u8 sum; + + rt = (struct irq_routing_table *) addr; + if (rt->signature != PIRQ_SIGNATURE || + rt->version != PIRQ_VERSION || + rt->size % 16 || + rt->size < sizeof(struct irq_routing_table)) + return NULL; + sum = 0; + for (i=0; i < rt->size; i++) + sum += addr[i]; + if (!sum) { + DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + return rt; + } + return NULL; +} + + + +/* * Search 0xf0000 -- 0xfffff for the PCI IRQ Routing Table. */ @@ -65,23 +94,17 @@ static struct irq_routing_table * __init { u8 *addr; struct irq_routing_table *rt; - int i; - u8 sum; + if (pirq_table_addr) { + rt = pirq_check_routing_table((u8 *) __va(pirq_table_addr)); + if (rt) + return rt; + printk(KERN_WARNING "PCI: PIRQ table NOT found at pirqaddr\n"); + } for(addr = (u8 *) __va(0xf0000); addr < (u8 *) __va(0x100000); addr += 16) { - rt = (struct irq_routing_table *) addr; - if (rt->signature != PIRQ_SIGNATURE || - rt->version != PIRQ_VERSION || - rt->size % 16 || - rt->size < sizeof(struct irq_routing_table)) - continue; - sum = 0; - for(i=0; isize; i++) - sum += addr[i]; - if (!sum) { - DBG("PCI: Interrupt Routing Table found at 0x%p\n", rt); + rt = pirq_check_routing_table(addr); + if (rt) return rt; - } } return NULL; } diff -puN arch/i386/pci/pci.h~gregkh-pci arch/i386/pci/pci.h --- 25/arch/i386/pci/pci.h~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/arch/i386/pci/pci.h 2005-04-10 17:27:02.000000000 -0700 @@ -27,6 +27,7 @@ #define PCI_ASSIGN_ALL_BUSSES 0x4000 extern unsigned int pci_probe; +extern unsigned long pirq_table_addr; /* pci-i386.c */ diff -puN arch/x86_64/Kconfig~gregkh-pci arch/x86_64/Kconfig --- 25/arch/x86_64/Kconfig~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/arch/x86_64/Kconfig 2005-04-10 17:27:02.000000000 -0700 @@ -417,6 +417,8 @@ config UNORDERED_IO source "drivers/pci/pcie/Kconfig" +source "drivers/pci/pcie/Kconfig" + source "drivers/pci/Kconfig" source "drivers/pcmcia/Kconfig" diff -puN Documentation/kernel-parameters.txt~gregkh-pci Documentation/kernel-parameters.txt --- 25/Documentation/kernel-parameters.txt~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/Documentation/kernel-parameters.txt 2005-04-10 17:27:02.000000000 -0700 @@ -1019,6 +1019,10 @@ running once the system is up. irqmask=0xMMMM [IA-32] Set a bit mask of IRQs allowed to be assigned automatically to PCI devices. You can make the kernel exclude IRQs of your ISA cards this way. + pirqaddr=0xAAAAA [IA-32] Specify the physical address + of the PIRQ table (normally generated + by the BIOS) if it is outside the + F0000h-100000h range. lastbus=N [IA-32] Scan all buses till bus #N. Can be useful if the kernel is unable to find your secondary buses and you want to tell it explicitly which ones they are. diff -puN Documentation/pci.txt~gregkh-pci Documentation/pci.txt --- 25/Documentation/pci.txt~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/Documentation/pci.txt 2005-04-10 17:27:02.000000000 -0700 @@ -279,6 +279,7 @@ pci_for_each_dev_reverse() Superseded by pci_for_each_bus() Superseded by pci_find_next_bus() pci_find_device() Superseded by pci_get_device() pci_find_subsys() Superseded by pci_get_subsys() +pci_find_slot() Superseded by pci_get_slot() pcibios_find_class() Superseded by pci_get_class() pci_find_class() Superseded by pci_get_class() pci_(read|write)_*_nodev() Superseded by pci_bus_(read|write)_*() diff -puN Documentation/power/pci.txt~gregkh-pci Documentation/power/pci.txt --- 25/Documentation/power/pci.txt~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/Documentation/power/pci.txt 2005-04-10 17:27:02.000000000 -0700 @@ -165,40 +165,9 @@ Description: These functions are intended for use by individual drivers, and are defined in struct pci_driver: - int (*save_state) (struct pci_dev *dev, u32 state); - int (*suspend) (struct pci_dev *dev, u32 state); + int (*suspend) (struct pci_dev *dev, pm_message_t state); int (*resume) (struct pci_dev *dev); - int (*enable_wake) (struct pci_dev *dev, u32 state, int enable); - - -save_state ----------- - -Usage: - -if (dev->driver && dev->driver->save_state) - dev->driver->save_state(dev,state); - -The driver should use this callback to save device state. It should take into -account the current state of the device and the requested state in order to -avoid any unnecessary operations. - -For example, a video card that supports all 4 states (D0-D3), all controller -context is preserved when entering D1, but the screen is placed into a low power -state (blanked). - -The driver can also interpret this function as a notification that it may be -entering a sleep state in the near future. If it knows that the device cannot -enter the requested state, either because of lack of support for it, or because -the device is middle of some critical operation, then it should fail. - -This function should not be used to set any state in the device or the driver -because the device may not actually enter the sleep state (e.g. another driver -later causes causes a global state transition to fail). - -Note that in intermediate low power states, a device's I/O and memory spaces may -be disabled and may not be available in subsequent transitions to lower power -states. + int (*enable_wake) (struct pci_dev *dev, pci_power_t state, int enable); suspend diff -puN drivers/pci/bus.c~gregkh-pci drivers/pci/bus.c --- 25/drivers/pci/bus.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/bus.c 2005-04-10 17:27:02.000000000 -0700 @@ -18,22 +18,12 @@ #include "pci.h" /** - * pci_bus_alloc_resource - allocate a resource from a parent bus - * @bus: PCI bus - * @res: resource to allocate - * @size: size of resource to allocate - * @align: alignment of resource to allocate - * @min: minimum /proc/iomem address to allocate - * @type_mask: IORESOURCE_* type flags - * @alignf: resource alignment function - * @alignf_data: data argument for resource alignment function + * pci_one_bus_alloc_resource - allocate a resource from one specific bus * - * Given the PCI bus a device resides on, the size, minimum address, - * alignment and type, try to find an acceptable resource allocation - * for a specific device resource. + * Always use pci_bus_alloc_resource() described below. */ -int -pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, +static int +pci_one_bus_alloc_resource(struct pci_bus *bus, struct resource *res, unsigned long size, unsigned long align, unsigned long min, unsigned int type_mask, void (*alignf)(void *, struct resource *, @@ -69,6 +59,48 @@ pci_bus_alloc_resource(struct pci_bus *b } /** + * pci_bus_alloc_resource - allocate a resource from a parent bus + * @bus: PCI bus + * @res: resource to allocate + * @size: size of resource to allocate + * @align: alignment of resource to allocate + * @min: minimum /proc/iomem address to allocate + * @type_mask: IORESOURCE_* type flags + * @alignf: resource alignment function + * @alignf_data: data argument for resource alignment function + * + * Given the PCI bus a device resides on, the size, minimum address, + * alignment and type, try to find an acceptable resource allocation + * for a specific device resource. + */ +int +pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, + unsigned long size, unsigned long align, unsigned long min, + unsigned int type_mask, + void (*alignf)(void *, struct resource *, + unsigned long, unsigned long), + void *alignf_data) +{ + int ret = pci_one_bus_alloc_resource(bus, res, size, align, min, + type_mask, alignf, alignf_data); + + /* + * If allocation from the resources available to this bus failed, + * and there is a transparent parent PCI-PCI bridge, we can check + * the resources of the parent bus as well + */ + while (ret && bus->self && bus->self->transparent) { + bus = bus->self->bus; + if (!bus) + return ret; + ret = pci_one_bus_alloc_resource(bus, res, size, align, min, + type_mask, alignf, alignf_data); + } + return ret; +} + + +/** * add a single device * @dev: device to add * diff -puN drivers/pci/pci-acpi.c~gregkh-pci drivers/pci/pci-acpi.c --- 25/drivers/pci/pci-acpi.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/pci-acpi.c 2005-04-10 17:27:02.000000000 -0700 @@ -21,7 +21,7 @@ static u32 ctrlset_buf[3] = {0, 0, 0}; static u32 global_ctrlsets = 0; -u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; +static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; static acpi_status acpi_query_osc ( diff -puN drivers/pci/pci.c~gregkh-pci drivers/pci/pci.c --- 25/drivers/pci/pci.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/pci.c 2005-04-10 17:27:02.000000000 -0700 @@ -16,6 +16,7 @@ #include #include #include /* isa_dma_bridge_buggy */ +#include "pci.h" /** @@ -189,18 +190,13 @@ int pci_find_ext_capability(struct pci_d } /** - * pci_find_parent_resource - return resource region of parent bus of given region - * @dev: PCI device structure contains resources to be searched - * @res: child resource record for which parent is sought + * pci_bus_find_parent_resource * - * For given resource region of given device, return the resource - * region of parent bus the given region is contained in or where - * it should be allocated from. + * Always use pci_find_parent_resource below. */ -struct resource * -pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) +static struct resource * +pci_bus_find_parent_resource(const struct pci_bus *bus, struct resource *res) { - const struct pci_bus *bus = dev->bus; int i; struct resource *best = NULL; @@ -221,6 +217,54 @@ pci_find_parent_resource(const struct pc } /** + * pci_find_parent_resource - return resource region of parent bus of given region + * @dev: PCI device structure contains resources to be searched + * @res: child resource record for which parent is sought. + * + * For given resource region of given device, return the resource + * region of parent bus the given region is contained in or where + * it should be allocated from. + */ +struct resource * +pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) +{ + struct pci_bus *bus = dev->bus; + struct resource *best, *r; + + best = pci_bus_find_parent_resource(bus, res); + + /* + * If there's a transparent parent PCI-PCI bridge, we check the parent + * bus as well in the following occasions: + * - we didn't find any appropriate resource + * - res->start and res->end were 0. This is used in code which + * then wants to request large amounts of IO, so better not limit + * it to the sub bus. [yenta_socket.c, i2o/iop.c] + * - we did find a sub-optimal resource (no PREFETCH for PREFETCH) + */ + while (bus->self && bus->self->transparent && + (!best || (!res->start && !res->end) + || ((res->flags & IORESOURCE_PREFETCH) && + !(best->flags & IORESOURCE_PREFETCH)))) { + bus = bus->self->bus; + if (!bus) + return best; + r = pci_bus_find_parent_resource(bus, res); + + /* + * If we didn't find an appropriate resource before, use what + * we found in the parent bus; if it's just a matter of + * PREFETCH, only use it if it is optimal. + */ + if (!best || (!res->start && !res->end) + || ((r->flags & IORESOURCE_PREFETCH) && + !(best->flags & IORESOURCE_PREFETCH))) + best = r; + } + return best; +} + +/** * pci_set_power_state - Set the power state of a PCI device * @dev: PCI device to be suspended * @state: PCI power state (D0, D1, D2, D3hot, D3cold) we're entering @@ -412,10 +456,10 @@ pci_enable_device(struct pci_dev *dev) { int err; - dev->is_enabled = 1; if ((err = pci_enable_device_bars(dev, (1 << PCI_NUM_RESOURCES) - 1))) return err; pci_fixup_device(pci_fixup_enable, dev); + dev->is_enabled = 1; return 0; } @@ -441,16 +485,15 @@ pci_disable_device(struct pci_dev *dev) { u16 pci_command; - dev->is_enabled = 0; - dev->is_busmaster = 0; - pci_read_config_word(dev, PCI_COMMAND, &pci_command); if (pci_command & PCI_COMMAND_MASTER) { pci_command &= ~PCI_COMMAND_MASTER; pci_write_config_word(dev, PCI_COMMAND, pci_command); } + dev->is_busmaster = 0; pcibios_disable_device(dev); + dev->is_enabled = 0; } /** diff -puN drivers/pci/pci-sysfs.c~gregkh-pci drivers/pci/pci-sysfs.c --- 25/drivers/pci/pci-sysfs.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/pci-sysfs.c 2005-04-10 17:27:02.000000000 -0700 @@ -91,6 +91,7 @@ pci_read_config(struct kobject *kobj, ch struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = 64; loff_t init_off = off; + u8 *data = (u8*) buf; /* Several chips lock up trying to read undefined config space */ if (capable(CAP_SYS_ADMIN)) { @@ -108,30 +109,47 @@ pci_read_config(struct kobject *kobj, ch size = count; } - while (off & 3) { - unsigned char val; + if ((off & 1) && size) { + u8 val; pci_read_config_byte(dev, off, &val); - buf[off - init_off] = val; + data[off - init_off] = val; off++; - if (--size == 0) - break; + size--; + } + + if ((off & 3) && size > 2) { + u16 val; + pci_read_config_word(dev, off, &val); + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + off += 2; + size -= 2; } while (size > 3) { - unsigned int val; + u32 val; pci_read_config_dword(dev, off, &val); - buf[off - init_off] = val & 0xff; - buf[off - init_off + 1] = (val >> 8) & 0xff; - buf[off - init_off + 2] = (val >> 16) & 0xff; - buf[off - init_off + 3] = (val >> 24) & 0xff; + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + data[off - init_off + 2] = (val >> 16) & 0xff; + data[off - init_off + 3] = (val >> 24) & 0xff; off += 4; size -= 4; } - while (size > 0) { - unsigned char val; + if (size >= 2) { + u16 val; + pci_read_config_word(dev, off, &val); + data[off - init_off] = val & 0xff; + data[off - init_off + 1] = (val >> 8) & 0xff; + off += 2; + size -= 2; + } + + if (size > 0) { + u8 val; pci_read_config_byte(dev, off, &val); - buf[off - init_off] = val; + data[off - init_off] = val; off++; --size; } @@ -145,6 +163,7 @@ pci_write_config(struct kobject *kobj, c struct pci_dev *dev = to_pci_dev(container_of(kobj,struct device,kobj)); unsigned int size = count; loff_t init_off = off; + u8 *data = (u8*) buf; if (off > dev->cfg_size) return 0; @@ -152,26 +171,41 @@ pci_write_config(struct kobject *kobj, c size = dev->cfg_size - off; count = size; } - - while (off & 3) { - pci_write_config_byte(dev, off, buf[off - init_off]); + + if ((off & 1) && size) { + pci_write_config_byte(dev, off, data[off - init_off]); off++; - if (--size == 0) - break; + size--; } + + if ((off & 3) && size > 2) { + u16 val = data[off - init_off]; + val |= (u16) data[off - init_off + 1] << 8; + pci_write_config_word(dev, off, val); + off += 2; + size -= 2; + } while (size > 3) { - unsigned int val = buf[off - init_off]; - val |= (unsigned int) buf[off - init_off + 1] << 8; - val |= (unsigned int) buf[off - init_off + 2] << 16; - val |= (unsigned int) buf[off - init_off + 3] << 24; + u32 val = data[off - init_off]; + val |= (u32) data[off - init_off + 1] << 8; + val |= (u32) data[off - init_off + 2] << 16; + val |= (u32) data[off - init_off + 3] << 24; pci_write_config_dword(dev, off, val); off += 4; size -= 4; } + + if (size >= 2) { + u16 val = data[off - init_off]; + val |= (u16) data[off - init_off + 1] << 8; + pci_write_config_word(dev, off, val); + off += 2; + size -= 2; + } - while (size > 0) { - pci_write_config_byte(dev, off, buf[off - init_off]); + if (size) { + pci_write_config_byte(dev, off, data[off - init_off]); off++; --size; } diff -puN drivers/pci/probe.c~gregkh-pci drivers/pci/probe.c --- 25/drivers/pci/probe.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/probe.c 2005-04-10 17:27:02.000000000 -0700 @@ -9,6 +9,7 @@ #include #include #include +#include "pci.h" #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 @@ -236,16 +237,10 @@ void __devinit pci_read_bridge_bases(str if (!dev) /* It's a host bus, nothing to read */ return; - if (dev->transparent) { - printk(KERN_INFO "PCI: Transparent bridge - %s\n", pci_name(dev)); - for(i = 0; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i]; - return; - } - - for(i=0; i<3; i++) + for (i=0; i<3; i++) child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + /* Resource 0 - IO ports */ res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); pci_read_config_byte(dev, PCI_IO_LIMIT, &io_limit_lo); @@ -266,6 +261,7 @@ void __devinit pci_read_bridge_bases(str res->end = limit + 0xfff; } + /* Resource 1 - nonprefetchable memory resource */ res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); @@ -277,6 +273,7 @@ void __devinit pci_read_bridge_bases(str res->end = limit + 0xfffff; } + /* Resource 2 - prefetchable memory resource */ res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); pci_read_config_word(dev, PCI_PREF_MEMORY_LIMIT, &mem_limit_lo); diff -puN drivers/pci/proc.c~gregkh-pci drivers/pci/proc.c --- 25/drivers/pci/proc.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/proc.c 2005-04-10 17:27:02.000000000 -0700 @@ -15,6 +15,7 @@ #include #include +#include "pci.h" static int proc_initialized; /* = 0 */ diff -puN drivers/pci/quirks.c~gregkh-pci drivers/pci/quirks.c --- 25/drivers/pci/quirks.c~gregkh-pci 2005-04-10 17:27:02.000000000 -0700 +++ 25-akpm/drivers/pci/quirks.c 2005-04-10 17:27:21.000000000 -0700 @@ -19,6 +19,7 @@ #include #include #include +#include "pci.h" /* Deal with broken BIOS'es that neglect to enable passive release, which can cause problems in combination with the 82441FX/PPro MTRRs */ _