From f07852d6442c46c50b59c7e2acc8a1b291f9ab6d Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:10:02 -0500 Subject: PCI: Rewrite pci_scan_slot The Alternate Routing-ID Interpretation capability allows a single device to have up to 256 functions. They can be populated sparsely, so the current technique of scanning every eighth function is not guaranteed to find them all. By introducing a 'next_fn' function pointer, we can use the linked list of functions in the ARI capability to scan all the functions which exist. We can then speed up the pci_scan_slot by skipping the scan of subsequent devfns for PCIe devices which are the direct children of Root Ports or Downstream Ports. These devices are only permitted to implement device 0, unless they are ARI devices, in which case they'll be scanned by the ARI code above. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 56 ++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 47 insertions(+), 9 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 446e4a94d7d3..dd64310a735e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1081,6 +1081,37 @@ struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) } EXPORT_SYMBOL(pci_scan_single_device); +static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn) +{ + u16 cap; + unsigned pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); + if (!pos) + return 0; + pci_read_config_word(dev, pos + 4, &cap); + return cap >> 8; +} + +static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn) +{ + return (fn + 1) % 8; +} + +static unsigned no_next_fn(struct pci_dev *dev, unsigned fn) +{ + return 0; +} + +static int only_one_child(struct pci_bus *bus) +{ + struct pci_dev *parent = bus->self; + if (!parent || !pci_is_pcie(parent)) + return 0; + if (parent->pcie_type == PCI_EXP_TYPE_ROOT_PORT || + parent->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) + return 1; + return 0; +} + /** * pci_scan_slot - scan a PCI slot on a bus for devices. * @bus: PCI bus to scan @@ -1094,21 +1125,28 @@ EXPORT_SYMBOL(pci_scan_single_device); */ int pci_scan_slot(struct pci_bus *bus, int devfn) { - int fn, nr = 0; + unsigned fn, nr = 0; struct pci_dev *dev; + unsigned (*next_fn)(struct pci_dev *, unsigned) = no_next_fn; + + if (only_one_child(bus) && (devfn > 0)) + return 0; /* Already scanned the entire slot */ dev = pci_scan_single_device(bus, devfn); if (dev && !dev->is_added) /* new device? */ nr++; - if (dev && dev->multifunction) { - for (fn = 1; fn < 8; fn++) { - dev = pci_scan_single_device(bus, devfn + fn); - if (dev) { - if (!dev->is_added) - nr++; - dev->multifunction = 1; - } + if (pci_ari_enabled(bus)) + next_fn = next_ari_fn; + else if (dev && dev->multifunction) + next_fn = next_trad_fn; + + for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) { + dev = pci_scan_single_device(bus, devfn + fn); + if (dev) { + if (!dev->is_added) + nr++; + dev->multifunction = 1; } } -- cgit v1.2.3 From 3749c51ac6c1560aa1cb1520066bed84c6f8152a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:32 -0500 Subject: PCI: Make current and maximum bus speeds part of the PCI core Move the max_bus_speed and cur_bus_speed into the pci_bus. Expose the values through the PCI slot driver instead of the hotplug slot driver. Update all the hotplug drivers to use the pci_bus instead of their own data structures. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dd64310a735e..51cf8982fec7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -387,10 +387,37 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); INIT_LIST_HEAD(&b->slots); + b->max_bus_speed = PCI_SPEED_UNKNOWN; + b->cur_bus_speed = PCI_SPEED_UNKNOWN; } return b; } +static unsigned char pcie_link_speed[] = { + PCI_SPEED_UNKNOWN, /* 0 */ + PCIE_SPEED_2_5GT, /* 1 */ + PCIE_SPEED_5_0GT, /* 2 */ + PCI_SPEED_UNKNOWN, /* 3 */ + PCI_SPEED_UNKNOWN, /* 4 */ + PCI_SPEED_UNKNOWN, /* 5 */ + PCI_SPEED_UNKNOWN, /* 6 */ + PCI_SPEED_UNKNOWN, /* 7 */ + PCI_SPEED_UNKNOWN, /* 8 */ + PCI_SPEED_UNKNOWN, /* 9 */ + PCI_SPEED_UNKNOWN, /* A */ + PCI_SPEED_UNKNOWN, /* B */ + PCI_SPEED_UNKNOWN, /* C */ + PCI_SPEED_UNKNOWN, /* D */ + PCI_SPEED_UNKNOWN, /* E */ + PCI_SPEED_UNKNOWN /* F */ +}; + +void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) +{ + bus->cur_bus_speed = pcie_link_speed[linksta & 0xf]; +} +EXPORT_SYMBOL_GPL(pcie_update_link_speed); + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { -- cgit v1.2.3 From 9be60ca0497a2563662fde4c9007841c3b79a742 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:33 -0500 Subject: PCI: Add support for detection of PCIe and PCI-X bus speeds Both PCIe and PCI-X bridges report their secondary bus speed in their respective capabilities. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 66 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 66 insertions(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 51cf8982fec7..188ee9cf0605 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -393,6 +393,25 @@ static struct pci_bus * pci_alloc_bus(void) return b; } +static unsigned char pcix_bus_speed[] = { + PCI_SPEED_UNKNOWN, /* 0 */ + PCI_SPEED_66MHz_PCIX, /* 1 */ + PCI_SPEED_100MHz_PCIX, /* 2 */ + PCI_SPEED_133MHz_PCIX, /* 3 */ + PCI_SPEED_UNKNOWN, /* 4 */ + PCI_SPEED_66MHz_PCIX_ECC, /* 5 */ + PCI_SPEED_100MHz_PCIX_ECC, /* 6 */ + PCI_SPEED_133MHz_PCIX_ECC, /* 7 */ + PCI_SPEED_UNKNOWN, /* 8 */ + PCI_SPEED_66MHz_PCIX_266, /* 9 */ + PCI_SPEED_100MHz_PCIX_266, /* A */ + PCI_SPEED_133MHz_PCIX_266, /* B */ + PCI_SPEED_UNKNOWN, /* C */ + PCI_SPEED_66MHz_PCIX_533, /* D */ + PCI_SPEED_100MHz_PCIX_533, /* E */ + PCI_SPEED_133MHz_PCIX_533 /* F */ +}; + static unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCIE_SPEED_2_5GT, /* 1 */ @@ -418,6 +437,51 @@ void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) } EXPORT_SYMBOL_GPL(pcie_update_link_speed); +static void pci_set_bus_speed(struct pci_bus *bus) +{ + struct pci_dev *bridge = bus->self; + int pos; + + pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); + if (pos) { + u16 status; + enum pci_bus_speed max; + pci_read_config_word(bridge, pos + 2, &status); + + if (status & 0x8000) { + max = PCI_SPEED_133MHz_PCIX_533; + } else if (status & 0x4000) { + max = PCI_SPEED_133MHz_PCIX_266; + } else if (status & 0x0002) { + if (((status >> 12) & 0x3) == 2) { + max = PCI_SPEED_133MHz_PCIX_ECC; + } else { + max = PCI_SPEED_133MHz_PCIX; + } + } else { + max = PCI_SPEED_66MHz_PCIX; + } + + bus->max_bus_speed = max; + bus->cur_bus_speed = pcix_bus_speed[(status >> 6) & 0xf]; + + return; + } + + pos = pci_find_capability(bridge, PCI_CAP_ID_EXP); + if (pos) { + u32 linkcap; + u16 linksta; + + pci_read_config_dword(bridge, pos + PCI_EXP_LNKCAP, &linkcap); + bus->max_bus_speed = pcie_link_speed[linkcap & 0xf]; + + pci_read_config_word(bridge, pos + PCI_EXP_LNKSTA, &linksta); + pcie_update_link_speed(bus, linksta); + } +} + + static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, struct pci_dev *bridge, int busnr) { @@ -457,6 +521,8 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->self = bridge; child->bridge = get_device(&bridge->dev); + pci_set_bus_speed(child); + /* Set up default resource pointers and names.. */ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; -- cgit v1.2.3 From 45b4cdd57ef0e57555b2ab61b584784819b39365 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:34 -0500 Subject: PCI: Add support for AGP in cur/max bus speed Take advantage of some gaps in the table to fit in support for AGP speeds. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 188ee9cf0605..2803ab96a98c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -437,11 +437,56 @@ void pcie_update_link_speed(struct pci_bus *bus, u16 linksta) } EXPORT_SYMBOL_GPL(pcie_update_link_speed); +static unsigned char agp_speeds[] = { + AGP_UNKNOWN, + AGP_1X, + AGP_2X, + AGP_4X, + AGP_8X +}; + +static enum pci_bus_speed agp_speed(int agp3, int agpstat) +{ + int index = 0; + + if (agpstat & 4) + index = 3; + else if (agpstat & 2) + index = 2; + else if (agpstat & 1) + index = 1; + else + goto out; + + if (agp3) { + index += 2; + if (index == 5) + index = 0; + } + + out: + return agp_speeds[index]; +} + + static void pci_set_bus_speed(struct pci_bus *bus) { struct pci_dev *bridge = bus->self; int pos; + pos = pci_find_capability(bridge, PCI_CAP_ID_AGP); + if (!pos) + pos = pci_find_capability(bridge, PCI_CAP_ID_AGP3); + if (pos) { + u32 agpstat, agpcmd; + + pci_read_config_dword(bridge, pos + PCI_AGP_STATUS, &agpstat); + bus->max_bus_speed = agp_speed(agpstat & 8, agpstat & 7); + + pci_read_config_dword(bridge, pos + PCI_AGP_COMMAND, &agpcmd); + bus->cur_bus_speed = agp_speed(agpstat & 8, agpcmd & 7); + } + pos = pci_find_capability(bridge, PCI_CAP_ID_PCIX); if (pos) { u16 status; -- cgit v1.2.3 From 9dfd97fe12f79ec8b68feb63912a4ef2f31f571a Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 13 Dec 2009 08:11:35 -0500 Subject: PCI: Add support for reporting PCIe 3.0 speeds Add the 8.0 GT/s speed. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2803ab96a98c..9672760c7ade 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -416,7 +416,7 @@ static unsigned char pcie_link_speed[] = { PCI_SPEED_UNKNOWN, /* 0 */ PCIE_SPEED_2_5GT, /* 1 */ PCIE_SPEED_5_0GT, /* 2 */ - PCI_SPEED_UNKNOWN, /* 3 */ + PCIE_SPEED_8_0GT, /* 3 */ PCI_SPEED_UNKNOWN, /* 4 */ PCI_SPEED_UNKNOWN, /* 5 */ PCI_SPEED_UNKNOWN, /* 6 */ -- cgit v1.2.3 From 4fb88c1a28a8dc302bdc09858e7cdafc97bef794 Mon Sep 17 00:00:00 2001 From: Matthew Wilcox Date: Sun, 17 Jan 2010 14:01:41 -0700 Subject: PCI: Make pci_scan_slot more robust Yinghai pointed out that the new pci_scan_slot() crashes when called on an ARI-capable slot that is empty. Fix this by exiting early from pci_scan_slot if there is no device in the slot. Also make next_ari_func() robust against devices not existing in case the ARI capability is corrupt. ARI also requires that the devices be listed in order, so if we find a function listed that is out of order, stop scanning to prevent loops. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9672760c7ade..233d1c275d96 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1222,11 +1222,19 @@ EXPORT_SYMBOL(pci_scan_single_device); static unsigned next_ari_fn(struct pci_dev *dev, unsigned fn) { u16 cap; - unsigned pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); + unsigned pos, next_fn; + + if (!dev) + return 0; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ARI); if (!pos) return 0; pci_read_config_word(dev, pos + 4, &cap); - return cap >> 8; + next_fn = cap >> 8; + if (next_fn <= fn) + return 0; + return next_fn; } static unsigned next_trad_fn(struct pci_dev *dev, unsigned fn) @@ -1271,12 +1279,14 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) return 0; /* Already scanned the entire slot */ dev = pci_scan_single_device(bus, devfn); - if (dev && !dev->is_added) /* new device? */ + if (!dev) + return 0; + if (!dev->is_added) nr++; if (pci_ari_enabled(bus)) next_fn = next_ari_fn; - else if (dev && dev->multifunction) + else if (dev->multifunction) next_fn = next_trad_fn; for (fn = next_fn(dev, 0); fn > 0; fn = next_fn(dev, fn)) { -- cgit v1.2.3 From cd81e1ea1a4cda94aa5f3e942301cf0da497c262 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 22 Jan 2010 01:02:22 -0800 Subject: PCI: reject mmio ranges starting at 0 on pci_bridge read We already track unassigned resources in struct resource, and this prevents us from overwriting resource flags and info in the unassigned case. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 233d1c275d96..d3009430eab6 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -316,13 +316,17 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) limit |= (io_limit_hi << 16); } - if (base <= limit) { + if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; if (!res->start) res->start = base; if (!res->end) res->end = limit + 0xfff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [io %04lx - %04lx] reg reading\n", + base, limit); } res = child->resource[1]; @@ -330,11 +334,15 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); base = (mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; - if (base <= limit) { + if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; res->start = base; res->end = limit + 0xfffff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [mem 0x%08lx - 0x%08lx] reg reading\n", + base, limit + 0xfffff); } res = child->resource[2]; @@ -366,7 +374,7 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) #endif } } - if (base <= limit) { + if (base && base <= limit) { res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | IORESOURCE_MEM | IORESOURCE_PREFETCH; if (res->flags & PCI_PREF_RANGE_TYPE_64) @@ -374,6 +382,10 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) res->start = base; res->end = limit + 0xfffff; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); + } else { + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window [mem 0x%08lx - %08lx pref] reg reading\n", + base, limit + 0xfffff); } } -- cgit v1.2.3 From fa27b2d108fa49685129867a8c5b968344d6e197 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:21 -0700 Subject: PCI: split up pci_read_bridge_bases() No functional change; this breaks up pci_read_bridge_bases() into separate pieces for the I/O, memory, and prefetchable memory windows, similar to how Yinghai recently split up pci_setup_bridge() in 68e84ff3bdc. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 54 ++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 39 insertions(+), 15 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index d3009430eab6..4b47b4bfb066 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -281,26 +281,12 @@ static void pci_read_bases(struct pci_dev *dev, unsigned int howmany, int rom) } } -void __devinit pci_read_bridge_bases(struct pci_bus *child) +static void __devinit pci_read_bridge_io(struct pci_bus *child) { struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; - u16 mem_base_lo, mem_limit_lo; unsigned long base, limit; struct resource *res; - int i; - - if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ - return; - - dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", - child->secondary, child->subordinate, - dev->transparent ? " (subtractive decode)": ""); - - if (dev->transparent) { - for(i = 3; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i - 3]; - } res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); @@ -328,6 +314,14 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) " bridge window [io %04lx - %04lx] reg reading\n", base, limit); } +} + +static void __devinit pci_read_bridge_mmio(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + struct resource *res; res = child->resource[1]; pci_read_config_word(dev, PCI_MEMORY_BASE, &mem_base_lo); @@ -344,6 +338,14 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) " bridge window [mem 0x%08lx - 0x%08lx] reg reading\n", base, limit + 0xfffff); } +} + +static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + u16 mem_base_lo, mem_limit_lo; + unsigned long base, limit; + struct resource *res; res = child->resource[2]; pci_read_config_word(dev, PCI_PREF_MEMORY_BASE, &mem_base_lo); @@ -389,6 +391,28 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) } } +void __devinit pci_read_bridge_bases(struct pci_bus *child) +{ + struct pci_dev *dev = child->self; + int i; + + if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ + return; + + dev_info(&dev->dev, "PCI bridge to [bus %02x-%02x]%s\n", + child->secondary, child->subordinate, + dev->transparent ? " (subtractive decode)" : ""); + + if (dev->transparent) { + for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) + child->resource[i] = child->parent->resource[i - 3]; + } + + pci_read_bridge_io(child); + pci_read_bridge_mmio(child); + pci_read_bridge_mmio_pref(child); +} + static struct pci_bus * pci_alloc_bus(void) { struct pci_bus *b; -- cgit v1.2.3 From 2adf75160b10bf3f09ed7d3d04e937f923fc557e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:26 -0700 Subject: PCI: read bridge windows before filling in subtractive decode resources No functional change; this fills in the bus subtractive decode resources after reading the bridge window information rather than before. Also, print out the subtractive decode resources as we already do for the positive decode windows. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 4b47b4bfb066..70c4ed2e67cc 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -403,14 +403,19 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) child->secondary, child->subordinate, dev->transparent ? " (subtractive decode)" : ""); - if (dev->transparent) { - for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) - child->resource[i] = child->parent->resource[i - 3]; - } - pci_read_bridge_io(child); pci_read_bridge_mmio(child); pci_read_bridge_mmio_pref(child); + + if (dev->transparent) { + for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) { + child->resource[i] = child->parent->resource[i - 3]; + if (child->resource[i]) + dev_printk(KERN_DEBUG, &dev->dev, + " bridge window %pR (subtractive decode)\n", + child->resource[i]); + } + } } static struct pci_bus * pci_alloc_bus(void) -- cgit v1.2.3 From 2fe2abf896c1e7a0ee65faaf3ef0ce654848abbd Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 23 Feb 2010 10:24:36 -0700 Subject: PCI: augment bus resource table with a list Previously we used a table of size PCI_BUS_NUM_RESOURCES (16) for resources forwarded to a bus by its upstream bridge. We've increased this size several times when the table overflowed. But there's no good limit on the number of resources because host bridges and subtractive decode bridges can forward any number of ranges to their secondary buses. This patch reduces the table to only PCI_BRIDGE_RESOURCE_NUM (4) entries, which corresponds to the number of windows a PCI-to-PCI (3) or CardBus (4) bridge can positively decode. Any additional resources, e.g., PCI host bridge windows or subtractively-decoded regions, are kept in a list. I'd prefer a single list rather than this split table/list approach, but that requires simultaneous changes to every architecture. This approach only requires immediate changes where we set up (a) host bridges with more than four windows and (b) subtractive-decode P2P bridges, and we can incrementally change other architectures to use the list. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70c4ed2e67cc..270d069819f7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -89,6 +89,7 @@ static void release_pcibus_dev(struct device *dev) if (pci_bus->bridge) put_device(pci_bus->bridge); + pci_bus_remove_resources(pci_bus); kfree(pci_bus); } @@ -394,6 +395,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) void __devinit pci_read_bridge_bases(struct pci_bus *child) { struct pci_dev *dev = child->self; + struct resource *res; int i; if (pci_is_root_bus(child)) /* It's a host bus, nothing to read */ @@ -403,17 +405,23 @@ void __devinit pci_read_bridge_bases(struct pci_bus *child) child->secondary, child->subordinate, dev->transparent ? " (subtractive decode)" : ""); + pci_bus_remove_resources(child); + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) + child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; + pci_read_bridge_io(child); pci_read_bridge_mmio(child); pci_read_bridge_mmio_pref(child); if (dev->transparent) { - for (i = 3; i < PCI_BUS_NUM_RESOURCES; i++) { - child->resource[i] = child->parent->resource[i - 3]; - if (child->resource[i]) + pci_bus_for_each_resource(child->parent, res, i) { + if (res) { + pci_bus_add_resource(child, res, + PCI_SUBTRACTIVE_DECODE); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR (subtractive decode)\n", - child->resource[i]); + res); + } } } } @@ -428,6 +436,7 @@ static struct pci_bus * pci_alloc_bus(void) INIT_LIST_HEAD(&b->children); INIT_LIST_HEAD(&b->devices); INIT_LIST_HEAD(&b->slots); + INIT_LIST_HEAD(&b->resources); b->max_bus_speed = PCI_SPEED_UNKNOWN; b->cur_bus_speed = PCI_SPEED_UNKNOWN; } -- cgit v1.2.3 From a1e4d72cd3024999bfb6703092ea271438805c89 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 8 Feb 2010 19:16:33 +0100 Subject: PM: Allow PCI devices to suspend/resume asynchronously Set power.async_suspend for all PCI devices and PCIe port services, so that they can be suspended and resumed in parallel with other devices they don't depend on in a known way (i.e. devices which are not their parents or children). This only affects the "regular" suspend and resume stages, which means in particular that the restoration of the PCI devices' standard configuration registers during resume will still be carried out synchronously (at the "early" resume stage). Signed-off-by: Rafael J. Wysocki --- drivers/pci/probe.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 270d069819f7..2a943090a3b7 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1436,6 +1436,7 @@ struct pci_bus * pci_create_bus(struct device *parent, if (error) goto dev_reg_err; b->bridge = get_device(dev); + device_enable_async_suspend(b->bridge); if (!parent) set_dev_node(b->bridge, pcibus_to_node(b)); -- cgit v1.2.3