From 2f320521a0d2d11fb857be09d05e2fbbf3ef8c13 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 21 Jan 2012 02:08:22 -0800 Subject: PCI: Make rescan bus increase bridge resource size if needed Current rescan will not touch bridge MMIO and IO. Try to reuse pci_assign_unassigned_bridge_resources(bridge) to update bridge resources, if child devices need more resources. Only do that for bridges whose children are all removed already; i.e. don't release resources that could already be in use by drivers on child devices. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 71eac9cd724d..0e84e8c2a6d0 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1666,6 +1666,31 @@ struct pci_bus * __devinit pci_scan_bus(int bus, struct pci_ops *ops, EXPORT_SYMBOL(pci_scan_bus); #ifdef CONFIG_HOTPLUG +/** + * pci_rescan_bus_bridge_resize - scan a PCI bus for devices. + * @bridge: PCI bridge for the bus to scan + * + * Scan a PCI bus and child buses for new devices, add them, + * and enable them, resizing bridge mmio/io resource if necessary + * and possible. The caller must ensure the child devices are already + * removed for resizing to occur. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) +{ + unsigned int max; + struct pci_bus *bus = bridge->subordinate; + + max = pci_scan_child_bus(bus); + + pci_assign_unassigned_bridge_resources(bridge); + + pci_bus_add_devices(bus); + + return max; +} + /** * pci_rescan_bus - scan a PCI bus for devices. * @bus: PCI bus to scan -- cgit v1.2.3 From 9b03088f955552299f50a1f660372698b07ab339 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 21 Jan 2012 02:08:23 -0800 Subject: PCI: Make pci_rescan_bus handle add_list This allows us to allocate resources to hotplug bridges during remove/rescan. We need to move the function to setup-bus.c so it can use __pci_bus_size_bridges and __pci_bus_assign_resources directly to take the add_list resource tracking list. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 32 -------------------------------- drivers/pci/setup-bus.c | 39 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 32 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 0e84e8c2a6d0..aad7d0ff6b08 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1691,38 +1691,6 @@ unsigned int __ref pci_rescan_bus_bridge_resize(struct pci_dev *bridge) return max; } -/** - * pci_rescan_bus - scan a PCI bus for devices. - * @bus: PCI bus to scan - * - * Scan a PCI bus and child buses for new devices, adds them, - * and enables them. - * - * Returns the max number of subordinate bus discovered. - */ -unsigned int __ref pci_rescan_bus(struct pci_bus *bus) -{ - unsigned int max; - struct pci_dev *dev; - - max = pci_scan_child_bus(bus); - - down_read(&pci_bus_sem); - list_for_each_entry(dev, &bus->devices, bus_list) - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || - dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) - if (dev->subordinate) - pci_bus_size_bridges(dev->subordinate); - up_read(&pci_bus_sem); - - pci_bus_assign_resources(bus); - pci_enable_bridges(bus); - pci_bus_add_devices(bus); - - return max; -} -EXPORT_SYMBOL_GPL(pci_rescan_bus); - EXPORT_SYMBOL(pci_add_new_bus); EXPORT_SYMBOL(pci_scan_slot); EXPORT_SYMBOL(pci_scan_bridge); diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 97c1eda96e64..c09c67ab5612 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1357,3 +1357,42 @@ enable_all: pci_enable_bridges(parent); } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); + +#ifdef CONFIG_HOTPLUG +/** + * pci_rescan_bus - scan a PCI bus for devices. + * @bus: PCI bus to scan + * + * Scan a PCI bus and child buses for new devices, adds them, + * and enables them. + * + * Returns the max number of subordinate bus discovered. + */ +unsigned int __ref pci_rescan_bus(struct pci_bus *bus) +{ + unsigned int max; + struct pci_dev *dev; + struct resource_list_x add_list; /* list of resources that + want additional resources */ + + max = pci_scan_child_bus(bus); + + add_list.next = NULL; + down_read(&pci_bus_sem); + list_for_each_entry(dev, &bus->devices, bus_list) + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE || + dev->hdr_type == PCI_HEADER_TYPE_CARDBUS) + if (dev->subordinate) + __pci_bus_size_bridges(dev->subordinate, + &add_list); + up_read(&pci_bus_sem); + __pci_bus_assign_resources(bus, &add_list, NULL); + BUG_ON(add_list.next); + + pci_enable_bridges(bus); + pci_bus_add_devices(bus); + + return max; +} +EXPORT_SYMBOL_GPL(pci_rescan_bus); +#endif -- cgit v1.2.3 From efdc87dab1cdf25ba631181ac0ead3fb2023dd10 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 27 Jan 2012 10:55:10 -0800 Subject: PCI: Separate pci_bus_read_dev_vendor_id from pci_scan_device We can reuse it for pciehp probing. -v2: according to Kenji, fix crs timeout checking, and export the function for later use when pciehp is compiled as a module. Suggested-by: Matthew Wilcox Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 2 ++ drivers/pci/probe.c | 48 +++++++++++++++++++++++++++++++----------------- 2 files changed, 33 insertions(+), 17 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1009a5e88e53..aaf7ff8c517f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -207,6 +207,8 @@ enum pci_bar_type { pci_bar_mem64, /* A 64-bit memory BAR */ }; +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *pl, + int crs_timeout); extern int pci_setup_device(struct pci_dev *dev); extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index aad7d0ff6b08..9f2ff8c5dc2f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1118,40 +1118,54 @@ struct pci_dev *alloc_pci_dev(void) } EXPORT_SYMBOL(alloc_pci_dev); -/* - * Read the config data for a PCI device, sanity-check it - * and fill in the dev structure... - */ -static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +bool pci_bus_read_dev_vendor_id(struct pci_bus *bus, int devfn, u32 *l, + int crs_timeout) { - struct pci_dev *dev; - u32 l; int delay = 1; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* some broken boards return 0 or ~0 if a slot is empty: */ - if (l == 0xffffffff || l == 0x00000000 || - l == 0x0000ffff || l == 0xffff0000) - return NULL; + if (*l == 0xffffffff || *l == 0x00000000 || + *l == 0x0000ffff || *l == 0xffff0000) + return false; /* Configuration request Retry Status */ - while (l == 0xffff0001) { + while (*l == 0xffff0001) { + if (!crs_timeout) + return false; + msleep(delay); delay *= 2; - if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, &l)) - return NULL; + if (pci_bus_read_config_dword(bus, devfn, PCI_VENDOR_ID, l)) + return false; /* Card hasn't responded in 60 seconds? Must be stuck. */ - if (delay > 60 * 1000) { + if (delay > crs_timeout) { printk(KERN_WARNING "pci %04x:%02x:%02x.%d: not " "responding\n", pci_domain_nr(bus), bus->number, PCI_SLOT(devfn), PCI_FUNC(devfn)); - return NULL; + return false; } } + return true; +} +EXPORT_SYMBOL(pci_bus_read_dev_vendor_id); + +/* + * Read the config data for a PCI device, sanity-check it + * and fill in the dev structure... + */ +static struct pci_dev *pci_scan_device(struct pci_bus *bus, int devfn) +{ + struct pci_dev *dev; + u32 l; + + if (!pci_bus_read_dev_vendor_id(bus, devfn, &l, 60*1000)) + return NULL; + dev = alloc_pci_dev(); if (!dev) return NULL; -- cgit v1.2.3 From 2dd8ba921d570fcd016f8038c63fa9668892d16b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sun, 19 Feb 2012 14:50:12 -0800 Subject: PCI: Fix device class print out Found debug print of class is shifted. | pci 0000:f8:15.2: [8086:2b56] type 0 class 0x000600 Code is trying to print class with 6 digits, but use shifted class with 4 digits valid value as variable. Change to original dev->class directly. Also remove not needed calculating of local variable class, because it will be updated after pci_fixup_device(pci_fixup_early...) Also unify type print out when class and header is not matched. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 9f2ff8c5dc2f..aa9b1dec0d3e 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -926,12 +926,10 @@ int pci_setup_device(struct pci_dev *dev) pci_read_config_dword(dev, PCI_CLASS_REVISION, &class); dev->revision = class & 0xff; - class >>= 8; /* upper 3 bytes */ - dev->class = class; - class >>= 8; + dev->class = class >> 8; /* upper 3 bytes */ - dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n", - dev->vendor, dev->device, dev->hdr_type, class); + dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %02x class %#08x\n", + dev->vendor, dev->device, dev->hdr_type, dev->class); /* need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); @@ -1013,8 +1011,8 @@ int pci_setup_device(struct pci_dev *dev) return -EIO; bad: - dev_err(&dev->dev, "ignoring class %02x (doesn't match header " - "type %02x)\n", class, dev->hdr_type); + dev_err(&dev->dev, "ignoring class %#08x (doesn't match header " + "type %02x)\n", dev->class, dev->hdr_type); dev->class = PCI_CLASS_NOT_DEFINED; } -- cgit v1.2.3 From f796841e49fe086176e27ed0e1f3f7a1123a4a6b Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 11 Feb 2012 00:18:30 -0800 Subject: PCI: fix memleak for pci dev removing during hotplug unreferenced object 0xffff880276d17700 (size 64): comm "swapper/0", pid 1, jiffies 4294897182 (age 3976.028s) hex dump (first 32 bytes): 00 00 00 00 00 00 00 00 18 f9 de 76 02 88 ff ff ...........v.... 10 00 00 00 0e 00 00 00 0f 28 40 00 00 00 00 00 .........(@..... backtrace: [] kmemleak_alloc+0x26/0x43 [] __kmalloc+0x121/0x183 [] pci_add_cap_save_buffer+0x35/0x7c [] pci_allocate_cap_save_buffers+0x1d/0x65 [] pci_device_add+0x92/0xf1 [] pci_scan_single_device+0x9f/0xa1 [] pci_scan_slot.part.20+0x21/0x106 [] pci_scan_slot+0x2b/0x35 [] __pci_scan_child_bus+0x51/0x107 [] pci_scan_bridge+0x376/0x6ae [] __pci_scan_child_bus+0xcd/0x107 [] pci_scan_child_bus+0x11/0x2a [] pci_acpi_scan_root+0x18b/0x21c [] acpi_pci_root_add+0x1e1/0x42a [] acpi_device_probe+0x50/0x190 [] really_probe+0x99/0x126 Need to free saved_buffer for capabilities. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 9 +++++++++ drivers/pci/pci.h | 1 + drivers/pci/probe.c | 1 + 3 files changed, 11 insertions(+) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9c89447e7b21..e7dfcd447571 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1916,6 +1916,15 @@ void pci_allocate_cap_save_buffers(struct pci_dev *dev) "unable to preallocate PCI-X save buffer\n"); } +void pci_free_cap_save_buffers(struct pci_dev *dev) +{ + struct pci_cap_saved_state *tmp; + struct hlist_node *pos, *n; + + hlist_for_each_entry_safe(tmp, pos, n, &dev->saved_cap_space, next) + kfree(tmp); +} + /** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index aaf7ff8c517f..586ac9b097e4 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -73,6 +73,7 @@ extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); +void pci_free_cap_save_buffers(struct pci_dev *dev); static inline void pci_wakeup_event(struct pci_dev *dev) { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index aa9b1dec0d3e..dc904bd4b569 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1024,6 +1024,7 @@ static void pci_release_capabilities(struct pci_dev *dev) { pci_vpd_release(dev); pci_iov_release(dev); + pci_free_cap_save_buffers(dev); } /** -- cgit v1.2.3 From a5390aa6dc3646b08bed421944cef0daf78ab994 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:18:59 -0700 Subject: PCI: don't publish new root bus until it's fully initialized When pci_create_root_bus() adds the new struct pci_bus to the global pci_root_buses list, the bus becomes visible to other parts of the kernel, so it should be fully initialized. This patch delays adding the bus to the pci_root_buses list until after all the struct pci_bus initialization is finished. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index dc904bd4b569..e4c0d1c6324d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1569,10 +1569,6 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, goto err_out; } - down_write(&pci_bus_sem); - list_add_tail(&b->node, &pci_root_buses); - up_write(&pci_bus_sem); - dev->parent = parent; dev->release = pci_release_bus_bridge_dev; dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); @@ -1612,6 +1608,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, dev_info(&b->dev, "root bus resource %pR\n", res); } + down_write(&pci_bus_sem); + list_add_tail(&b->node, &pci_root_buses); + up_write(&pci_bus_sem); + return b; class_dev_reg_err: -- cgit v1.2.3 From 5a21d70dbd33d20713fb735ad9381711b0ae2c9b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:18:59 -0700 Subject: PCI: add struct pci_host_bridge and a list of all bridges found This adds a list of all PCI host bridges we find and a way to look up the host bridge from a pci_dev. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 39 ++++++++++++++++++++++++++++++++++----- include/linux/pci.h | 5 +++++ 2 files changed, 39 insertions(+), 5 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e4c0d1c6324d..3a30023a123c 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -15,6 +15,8 @@ #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ #define CARDBUS_RESERVE_BUSNR 3 +static LIST_HEAD(pci_host_bridges); + /* Ugh. Need to stop exporting this to modules. */ LIST_HEAD(pci_root_buses); EXPORT_SYMBOL(pci_root_buses); @@ -42,6 +44,23 @@ int no_pci_devices(void) } EXPORT_SYMBOL(no_pci_devices); +static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) +{ + struct pci_bus *bus; + struct pci_host_bridge *bridge; + + bus = dev->bus; + while (bus->parent) + bus = bus->parent; + + list_for_each_entry(bridge, &pci_host_bridges, list) { + if (bridge->bus == bus) + return bridge; + } + + return NULL; +} + /* * PCI Bus Class */ @@ -1544,20 +1563,23 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { int error, i; + struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; struct pci_bus_resource *bus_res, *n; struct resource *res; + bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); + if (!bridge) + return NULL; + b = pci_alloc_bus(); if (!b) - return NULL; + goto err_bus; dev = kzalloc(sizeof(*dev), GFP_KERNEL); - if (!dev) { - kfree(b); - return NULL; - } + if (!dev) + goto err_dev; b->sysdata = sysdata; b->ops = ops; @@ -1594,6 +1616,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; + bridge->bus = b; + /* Add initial resources to the bus */ list_for_each_entry_safe(bus_res, n, resources, list) list_move_tail(&bus_res->list, &b->resources); @@ -1609,6 +1633,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, } down_write(&pci_bus_sem); + list_add_tail(&bridge->list, &pci_host_bridges); list_add_tail(&b->node, &pci_root_buses); up_write(&pci_bus_sem); @@ -1618,11 +1643,15 @@ class_dev_reg_err: device_unregister(dev); dev_reg_err: down_write(&pci_bus_sem); + list_del(&bridge->list); list_del(&b->node); up_write(&pci_bus_sem); err_out: kfree(dev); +err_dev: kfree(b); +err_bus: + kfree(bridge); return NULL; } diff --git a/include/linux/pci.h b/include/linux/pci.h index bcaa51ca7858..2c946b3bbf77 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,6 +368,11 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +struct pci_host_bridge { + struct list_head list; + struct pci_bus *bus; /* root bus */ +}; + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for -- cgit v1.2.3 From 0efd5aab41e18a1175f72641696cfda154ba6c87 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:00 -0700 Subject: PCI: add struct pci_host_bridge_window with CPU/bus address offset Some PCI host bridges apply an address offset, so bus addresses on PCI are different from CPU addresses. This patch adds a way for architectures to tell the PCI core about this offset. For example: LIST_HEAD(resources); pci_add_resource_offset(&resources, host->io_space, host->io_offset); pci_add_resource_offset(&resources, host->mem_space, host->mem_offset); pci_scan_root_bus(parent, bus, ops, sysdata, &resources); Signed-off-by: Bjorn Helgaas --- drivers/pci/bus.c | 30 +++++++++++++++++++----------- drivers/pci/probe.c | 32 +++++++++++++++++++++++--------- include/linux/pci.h | 9 +++++++++ 3 files changed, 51 insertions(+), 20 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 398f5d859791..4ce5ef2f2826 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -18,28 +18,36 @@ #include "pci.h" -void pci_add_resource(struct list_head *resources, struct resource *res) +void pci_add_resource_offset(struct list_head *resources, struct resource *res, + resource_size_t offset) { - struct pci_bus_resource *bus_res; + struct pci_host_bridge_window *window; - bus_res = kzalloc(sizeof(struct pci_bus_resource), GFP_KERNEL); - if (!bus_res) { - printk(KERN_ERR "PCI: can't add bus resource %pR\n", res); + window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); + if (!window) { + printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); return; } - bus_res->res = res; - list_add_tail(&bus_res->list, resources); + window->res = res; + window->offset = offset; + list_add_tail(&window->list, resources); +} +EXPORT_SYMBOL(pci_add_resource_offset); + +void pci_add_resource(struct list_head *resources, struct resource *res) +{ + pci_add_resource_offset(resources, res, 0); } EXPORT_SYMBOL(pci_add_resource); void pci_free_resource_list(struct list_head *resources) { - struct pci_bus_resource *bus_res, *tmp; + struct pci_host_bridge_window *window, *tmp; - list_for_each_entry_safe(bus_res, tmp, resources, list) { - list_del(&bus_res->list); - kfree(bus_res); + list_for_each_entry_safe(window, tmp, resources, list) { + list_del(&window->list); + kfree(window); } } EXPORT_SYMBOL(pci_free_resource_list); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3a30023a123c..3f07cb6bae32 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1562,12 +1562,15 @@ unsigned int __devinit pci_scan_child_bus(struct pci_bus *bus) struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - int error, i; + int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; struct device *dev; - struct pci_bus_resource *bus_res, *n; + struct pci_host_bridge_window *window, *n; struct resource *res; + resource_size_t offset; + char bus_addr[64]; + char *fmt; bridge = kzalloc(sizeof(*bridge), GFP_KERNEL); if (!bridge) @@ -1617,19 +1620,30 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, b->number = b->secondary = bus; bridge->bus = b; - - /* Add initial resources to the bus */ - list_for_each_entry_safe(bus_res, n, resources, list) - list_move_tail(&bus_res->list, &b->resources); + INIT_LIST_HEAD(&bridge->windows); if (parent) dev_info(parent, "PCI host bridge to bus %s\n", dev_name(&b->dev)); else printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); - pci_bus_for_each_resource(b, res, i) { - if (res) - dev_info(&b->dev, "root bus resource %pR\n", res); + /* Add initial resources to the bus */ + list_for_each_entry_safe(window, n, resources, list) { + list_move_tail(&window->list, &bridge->windows); + res = window->res; + offset = window->offset; + pci_bus_add_resource(b, res, 0); + if (offset) { + if (resource_type(res) == IORESOURCE_IO) + fmt = " (bus address [%#06llx-%#06llx])"; + else + fmt = " (bus address [%#010llx-%#010llx])"; + snprintf(bus_addr, sizeof(bus_addr), fmt, + (unsigned long long) (res->start - offset), + (unsigned long long) (res->end - offset)); + } else + bus_addr[0] = '\0'; + dev_info(&b->dev, "root bus resource %pR%s\n", res, bus_addr); } down_write(&pci_bus_sem); diff --git a/include/linux/pci.h b/include/linux/pci.h index 2c946b3bbf77..419f78f96375 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -368,9 +368,16 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } +struct pci_host_bridge_window { + struct list_head list; + struct resource *res; /* host bridge aperture (CPU address) */ + resource_size_t offset; /* bus address + offset = CPU address */ +}; + struct pci_host_bridge { struct list_head list; struct pci_bus *bus; /* root bus */ + struct list_head windows; /* pci_host_bridge_windows */ }; /* @@ -901,6 +908,8 @@ void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ void pci_add_resource(struct list_head *resources, struct resource *res); +void pci_add_resource_offset(struct list_head *resources, struct resource *res, + resource_size_t offset); void pci_free_resource_list(struct list_head *resources); void pci_bus_add_resource(struct pci_bus *bus, struct resource *res, unsigned int flags); struct resource *pci_bus_resource_n(const struct pci_bus *bus, int n); -- cgit v1.2.3 From 5bfa14ed9f3ca21fcecbcfbf4a848c002b740c41 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:00 -0700 Subject: PCI: convert bus addresses to resource when reading BARs Some PCI host bridges translate CPU addresses to PCI bus addresses. Previously, we initialized pci_dev resources with PCI bus addresses, then converted them to CPU addresses later in arch-specific code (pcibios_fixup_resources()), which leaves a window of time where the pci_dev resources are incorrect. This patch adds support in the core for this address translation. When the arch creates the root bus, it can supply the host bridge address translation information, and the core can use it to set the pci_dev resources correctly from the beginning. This gives us a way to fix the problem that quirks that run between device discovery and pcibios_fixup_resources() fail because they use pci_dev resources that haven't been converted. The reference below is to one such problem that affected ARM and ia64. Note that this patch has no effect until an arch starts using pci_add_resource_offset() with a non-zero offset: before that, all all host bridge windows have a zero offset and pci_bus_to_resource() copies the pci_bus_region directly to the struct resource. Reference: https://lkml.org/lkml/2009/10/12/405 Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 129 ++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 104 insertions(+), 25 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3f07cb6bae32..3539171d8a98 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -61,6 +61,63 @@ static struct pci_host_bridge *pci_host_bridge(struct pci_dev *dev) return NULL; } +static bool resource_contains(struct resource *res1, struct resource *res2) +{ + return res1->start <= res2->start && res1->end >= res2->end; +} + +void pci_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + if (resource_contains(window->res, res)) { + offset = window->offset; + break; + } + } + + region->start = res->start - offset; + region->end = res->end - offset; +} + +static bool region_contains(struct pci_bus_region *region1, + struct pci_bus_region *region2) +{ + return region1->start <= region2->start && region1->end >= region2->end; +} + +void pci_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + struct pci_host_bridge *bridge = pci_host_bridge(dev); + struct pci_host_bridge_window *window; + struct pci_bus_region bus_region; + resource_size_t offset = 0; + + list_for_each_entry(window, &bridge->windows, list) { + if (resource_type(res) != resource_type(window->res)) + continue; + + bus_region.start = window->res->start - window->offset; + bus_region.end = window->res->end - window->offset; + + if (region_contains(&bus_region, region)) { + offset = window->offset; + break; + } + } + + res->start = region->start + offset; + res->end = region->end + offset; +} + /* * PCI Bus Class */ @@ -154,6 +211,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, { u32 l, sz, mask; u16 orig_cmd; + struct pci_bus_region region; mask = type ? PCI_ROM_ADDRESS_MASK : ~0; @@ -233,11 +291,13 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, /* Address above 32-bit boundary; disable the BAR */ pci_write_config_dword(dev, pos, 0); pci_write_config_dword(dev, pos + 4, 0); - res->start = 0; - res->end = sz64; + region.start = 0; + region.end = sz64; + pci_bus_to_resource(dev, res, ®ion); } else { - res->start = l64; - res->end = l64 + sz64; + region.start = l64; + region.end = l64 + sz64; + pci_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -247,8 +307,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, if (!sz) goto fail; - res->start = l; - res->end = l + sz; + region.start = l; + region.end = l + sz; + pci_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -285,7 +346,8 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) struct pci_dev *dev = child->self; u8 io_base_lo, io_limit_lo; unsigned long base, limit; - struct resource *res; + struct pci_bus_region region; + struct resource *res, res2; res = child->resource[0]; pci_read_config_byte(dev, PCI_IO_BASE, &io_base_lo); @@ -303,10 +365,13 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + region.start = base; + region.end = limit + 0xfff; + pci_bus_to_resource(dev, &res2, ®ion); if (!res->start) - res->start = base; + res->start = res2.start; if (!res->end) - res->end = limit + 0xfff; + res->end = res2.end; dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -316,6 +381,7 @@ 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 pci_bus_region region; struct resource *res; res = child->resource[1]; @@ -325,8 +391,9 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) limit = (mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; if (base && base <= limit) { res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pci_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -336,6 +403,7 @@ 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 pci_bus_region region; struct resource *res; res = child->resource[2]; @@ -372,8 +440,9 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) IORESOURCE_MEM | IORESOURCE_PREFETCH; if (res->flags & PCI_PREF_RANGE_TYPE_64) res->flags |= IORESOURCE_MEM_64; - res->start = base; - res->end = limit + 0xfffff; + region.start = base; + region.end = limit + 0xfffff; + pci_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -919,6 +988,8 @@ int pci_setup_device(struct pci_dev *dev) u8 hdr_type; struct pci_slot *slot; int pos = 0; + struct pci_bus_region region; + struct resource *res; if (pci_read_config_byte(dev, PCI_HEADER_TYPE, &hdr_type)) return -EIO; @@ -980,20 +1051,28 @@ int pci_setup_device(struct pci_dev *dev) u8 progif; pci_read_config_byte(dev, PCI_CLASS_PROG, &progif); if ((progif & 1) == 0) { - dev->resource[0].start = 0x1F0; - dev->resource[0].end = 0x1F7; - dev->resource[0].flags = LEGACY_IO_RESOURCE; - dev->resource[1].start = 0x3F6; - dev->resource[1].end = 0x3F6; - dev->resource[1].flags = LEGACY_IO_RESOURCE; + region.start = 0x1F0; + region.end = 0x1F7; + res = &dev->resource[0]; + res->flags = LEGACY_IO_RESOURCE; + pci_bus_to_resource(dev, res, ®ion); + region.start = 0x3F6; + region.end = 0x3F6; + res = &dev->resource[1]; + res->flags = LEGACY_IO_RESOURCE; + pci_bus_to_resource(dev, res, ®ion); } if ((progif & 4) == 0) { - dev->resource[2].start = 0x170; - dev->resource[2].end = 0x177; - dev->resource[2].flags = LEGACY_IO_RESOURCE; - dev->resource[3].start = 0x376; - dev->resource[3].end = 0x376; - dev->resource[3].flags = LEGACY_IO_RESOURCE; + region.start = 0x170; + region.end = 0x177; + res = &dev->resource[2]; + res->flags = LEGACY_IO_RESOURCE; + pci_bus_to_resource(dev, res, ®ion); + region.start = 0x376; + region.end = 0x376; + res = &dev->resource[3]; + res->flags = LEGACY_IO_RESOURCE; + pci_bus_to_resource(dev, res, ®ion); } } break; -- cgit v1.2.3 From 36a66cd6fd0a70ac6848d740d9cf7a4360b5776a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:00 -0700 Subject: PCI: add generic pcibios_resource_to_bus() This replaces the generic versions of pcibios_resource_to_bus() and pcibios_bus_to_resource() in asm-generic/pci.h with versions that use pci_resource_to_bus() and pci_bus_to_resource(). The replacements are equivalent except that they can apply host bridge window offsets when the arch has supplied them by using pci_add_resource_offset(). Each arch can convert to using pci_add_resource_offset() individually by removing its device resource fixups from pcibios_fixup_bus() and supplying ARCH_HAS_GENERIC_PCI_OFFSETS. ARCH_HAS_GENERIC_PCI_OFFSETS can be removed after all have converted. Signed-off-by: Bjorn Helgaas --- drivers/pci/probe.c | 16 ++++++++++++++++ include/asm-generic/pci.h | 24 +----------------------- include/linux/pci.h | 4 ++++ 3 files changed, 21 insertions(+), 23 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 3539171d8a98..a677b1e995de 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -118,6 +118,22 @@ void pci_bus_to_resource(struct pci_dev *dev, struct resource *res, res->end = region->end + offset; } +#ifdef ARCH_HAS_GENERIC_PCI_OFFSETS +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) +{ + pci_resource_to_bus(dev, region, res); +} +EXPORT_SYMBOL(pcibios_resource_to_bus); + +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) +{ + pci_bus_to_resource(dev, res, region); +} +EXPORT_SYMBOL(pcibios_bus_to_resource); +#endif + /* * PCI Bus Class */ diff --git a/include/asm-generic/pci.h b/include/asm-generic/pci.h index 26373cff4546..0410346e2cf6 100644 --- a/include/asm-generic/pci.h +++ b/include/asm-generic/pci.h @@ -6,29 +6,7 @@ #ifndef _ASM_GENERIC_PCI_H #define _ASM_GENERIC_PCI_H -/** - * pcibios_resource_to_bus - convert resource to PCI bus address - * @dev: device which owns this resource - * @region: converted bus-centric region (start,end) - * @res: resource to convert - * - * Convert a resource to a PCI device bus address or bus window. - */ -static inline void -pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - region->start = res->start; - region->end = res->end; -} - -static inline void -pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - res->start = region->start; - res->end = region->end; -} +#define ARCH_HAS_GENERIC_PCI_OFFSETS static inline struct resource * pcibios_select_root(struct pci_dev *pdev, struct resource *res) diff --git a/include/linux/pci.h b/include/linux/pci.h index 419f78f96375..be58a51706cc 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -648,6 +648,10 @@ void pci_fixup_cardbus(struct pci_bus *); /* Generic PCI functions used internally */ +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res); +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region); void pcibios_scan_specific_bus(int busn); extern struct pci_bus *pci_find_bus(int domain, int busnr); void pci_bus_add_devices(const struct pci_bus *bus); -- cgit v1.2.3 From fb127cb9de791d62fb393d6e65fa9869bddd2460 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 23 Feb 2012 20:19:04 -0700 Subject: PCI: collapse pcibios_resource_to_bus Everybody uses the generic pcibios_resource_to_bus() supplied by the core now, so remove the ARCH_HAS_GENERIC_PCI_OFFSETS used during conversion. Signed-off-by: Bjorn Helgaas --- arch/alpha/include/asm/pci.h | 2 -- arch/arm/include/asm/pci.h | 2 -- arch/ia64/include/asm/pci.h | 2 -- arch/microblaze/include/asm/pci.h | 2 -- arch/mips/include/asm/pci.h | 2 -- arch/mn10300/include/asm/pci.h | 2 -- arch/parisc/include/asm/pci.h | 2 -- arch/powerpc/include/asm/pci.h | 2 -- arch/sh/include/asm/pci.h | 2 -- arch/sparc/include/asm/pci_32.h | 2 -- arch/sparc/include/asm/pci_64.h | 2 -- drivers/pci/probe.c | 44 +++++++++++++-------------------------- include/asm-generic/pci.h | 2 -- 13 files changed, 15 insertions(+), 53 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/arch/alpha/include/asm/pci.h b/arch/alpha/include/asm/pci.h index 121c797eb14f..d01afb78919c 100644 --- a/arch/alpha/include/asm/pci.h +++ b/arch/alpha/include/asm/pci.h @@ -100,8 +100,6 @@ static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) return channel ? 15 : 14; } -#define ARCH_HAS_GENERIC_PCI_OFFSETS - #define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index static inline int pci_proc_domain(struct pci_bus *bus) diff --git a/arch/arm/include/asm/pci.h b/arch/arm/include/asm/pci.h index 4748a75ed112..a98a2e112fae 100644 --- a/arch/arm/include/asm/pci.h +++ b/arch/arm/include/asm/pci.h @@ -57,8 +57,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); -#define ARCH_HAS_GENERIC_PCI_OFFSETS - /* * Dummy implementation; always return 0. */ diff --git a/arch/ia64/include/asm/pci.h b/arch/ia64/include/asm/pci.h index 519bb5ce3075..b22e5f5fa593 100644 --- a/arch/ia64/include/asm/pci.h +++ b/arch/ia64/include/asm/pci.h @@ -108,8 +108,6 @@ static inline int pci_proc_domain(struct pci_bus *bus) return (pci_domain_nr(bus) != 0); } -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline struct resource * pcibios_select_root(struct pci_dev *pdev, struct resource *res) { diff --git a/arch/microblaze/include/asm/pci.h b/arch/microblaze/include/asm/pci.h index 8db01f705b3c..a0da88bf70c5 100644 --- a/arch/microblaze/include/asm/pci.h +++ b/arch/microblaze/include/asm/pci.h @@ -94,8 +94,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, */ #define PCI_DMA_BUS_IS_PHYS (1) -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline struct resource *pcibios_select_root(struct pci_dev *pdev, struct resource *res) { diff --git a/arch/mips/include/asm/pci.h b/arch/mips/include/asm/pci.h index 6420e8df4e45..fcd4060f6421 100644 --- a/arch/mips/include/asm/pci.h +++ b/arch/mips/include/asm/pci.h @@ -113,8 +113,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, } #endif -#define ARCH_HAS_GENERIC_PCI_OFFSETS - #define pci_domain_nr(bus) ((struct pci_controller *)(bus)->sysdata)->index static inline int pci_proc_domain(struct pci_bus *bus) diff --git a/arch/mn10300/include/asm/pci.h b/arch/mn10300/include/asm/pci.h index dfe1581c0c9d..8137c25c4e15 100644 --- a/arch/mn10300/include/asm/pci.h +++ b/arch/mn10300/include/asm/pci.h @@ -85,8 +85,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, /* implement the pci_ DMA API in terms of the generic device dma_ one */ #include -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline struct resource * pcibios_select_root(struct pci_dev *pdev, struct resource *res) { diff --git a/arch/parisc/include/asm/pci.h b/arch/parisc/include/asm/pci.h index a8b591fdd564..3234f492d575 100644 --- a/arch/parisc/include/asm/pci.h +++ b/arch/parisc/include/asm/pci.h @@ -215,8 +215,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, } #endif -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline void pcibios_penalize_isa_irq(int irq, int active) { /* We don't need to penalize isa irq's */ diff --git a/arch/powerpc/include/asm/pci.h b/arch/powerpc/include/asm/pci.h index 839178bf2776..201e352d488d 100644 --- a/arch/powerpc/include/asm/pci.h +++ b/arch/powerpc/include/asm/pci.h @@ -154,8 +154,6 @@ extern int pci_mmap_legacy_page_range(struct pci_bus *bus, #endif /* CONFIG_PPC64 */ -#define ARCH_HAS_GENERIC_PCI_OFFSETS - extern void pcibios_claim_one_bus(struct pci_bus *b); extern void pcibios_finish_adding_to_bus(struct pci_bus *bus); diff --git a/arch/sh/include/asm/pci.h b/arch/sh/include/asm/pci.h index 3b5b6abb3aea..bff96c2e7d25 100644 --- a/arch/sh/include/asm/pci.h +++ b/arch/sh/include/asm/pci.h @@ -114,8 +114,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, /* Board-specific fixup routines. */ int pcibios_map_platform_irq(const struct pci_dev *dev, u8 slot, u8 pin); -#define ARCH_HAS_GENERIC_PCI_OFFSETS - #define pci_domain_nr(bus) ((struct pci_channel *)(bus)->sysdata)->index static inline int pci_proc_domain(struct pci_bus *bus) diff --git a/arch/sparc/include/asm/pci_32.h b/arch/sparc/include/asm/pci_32.h index 6384f30429bb..dc503297481f 100644 --- a/arch/sparc/include/asm/pci_32.h +++ b/arch/sparc/include/asm/pci_32.h @@ -52,8 +52,6 @@ static inline void pci_dma_burst_advice(struct pci_dev *pdev, * 64Kbytes by the Host controller. */ -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { return PCI_IRQ_NONE; diff --git a/arch/sparc/include/asm/pci_64.h b/arch/sparc/include/asm/pci_64.h index 2918bd12c19b..1633b718d3bc 100644 --- a/arch/sparc/include/asm/pci_64.h +++ b/arch/sparc/include/asm/pci_64.h @@ -73,8 +73,6 @@ extern int pci_mmap_page_range(struct pci_dev *dev, struct vm_area_struct *vma, enum pci_mmap_state mmap_state, int write_combine); -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline int pci_get_legacy_ide_irq(struct pci_dev *dev, int channel) { return PCI_IRQ_NONE; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index a677b1e995de..36c22032ea14 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -66,8 +66,8 @@ static bool resource_contains(struct resource *res1, struct resource *res2) return res1->start <= res2->start && res1->end >= res2->end; } -void pci_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) +void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, + struct resource *res) { struct pci_host_bridge *bridge = pci_host_bridge(dev); struct pci_host_bridge_window *window; @@ -86,6 +86,7 @@ void pci_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, region->start = res->start - offset; region->end = res->end - offset; } +EXPORT_SYMBOL(pcibios_resource_to_bus); static bool region_contains(struct pci_bus_region *region1, struct pci_bus_region *region2) @@ -93,8 +94,8 @@ static bool region_contains(struct pci_bus_region *region1, return region1->start <= region2->start && region1->end >= region2->end; } -void pci_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) +void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, + struct pci_bus_region *region) { struct pci_host_bridge *bridge = pci_host_bridge(dev); struct pci_host_bridge_window *window; @@ -117,22 +118,7 @@ void pci_bus_to_resource(struct pci_dev *dev, struct resource *res, res->start = region->start + offset; res->end = region->end + offset; } - -#ifdef ARCH_HAS_GENERIC_PCI_OFFSETS -void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, - struct resource *res) -{ - pci_resource_to_bus(dev, region, res); -} -EXPORT_SYMBOL(pcibios_resource_to_bus); - -void pcibios_bus_to_resource(struct pci_dev *dev, struct resource *res, - struct pci_bus_region *region) -{ - pci_bus_to_resource(dev, res, region); -} EXPORT_SYMBOL(pcibios_bus_to_resource); -#endif /* * PCI Bus Class @@ -309,11 +295,11 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, pci_write_config_dword(dev, pos + 4, 0); region.start = 0; region.end = sz64; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); } else { region.start = l64; region.end = l64 + sz64; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -325,7 +311,7 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, region.start = l; region.end = l + sz; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, "reg %x: %pR\n", pos, res); } @@ -383,7 +369,7 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; region.start = base; region.end = limit + 0xfff; - pci_bus_to_resource(dev, &res2, ®ion); + pcibios_bus_to_resource(dev, &res2, ®ion); if (!res->start) res->start = res2.start; if (!res->end) @@ -409,7 +395,7 @@ static void __devinit pci_read_bridge_mmio(struct pci_bus *child) res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; region.start = base; region.end = limit + 0xfffff; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -458,7 +444,7 @@ static void __devinit pci_read_bridge_mmio_pref(struct pci_bus *child) res->flags |= IORESOURCE_MEM_64; region.start = base; region.end = limit + 0xfffff; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); dev_printk(KERN_DEBUG, &dev->dev, " bridge window %pR\n", res); } } @@ -1071,24 +1057,24 @@ int pci_setup_device(struct pci_dev *dev) region.end = 0x1F7; res = &dev->resource[0]; res->flags = LEGACY_IO_RESOURCE; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); region.start = 0x3F6; region.end = 0x3F6; res = &dev->resource[1]; res->flags = LEGACY_IO_RESOURCE; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); } if ((progif & 4) == 0) { region.start = 0x170; region.end = 0x177; res = &dev->resource[2]; res->flags = LEGACY_IO_RESOURCE; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); region.start = 0x376; region.end = 0x376; res = &dev->resource[3]; res->flags = LEGACY_IO_RESOURCE; - pci_bus_to_resource(dev, res, ®ion); + pcibios_bus_to_resource(dev, res, ®ion); } } break; diff --git a/include/asm-generic/pci.h b/include/asm-generic/pci.h index 0410346e2cf6..e80a0495e5b0 100644 --- a/include/asm-generic/pci.h +++ b/include/asm-generic/pci.h @@ -6,8 +6,6 @@ #ifndef _ASM_GENERIC_PCI_H #define _ASM_GENERIC_PCI_H -#define ARCH_HAS_GENERIC_PCI_OFFSETS - static inline struct resource * pcibios_select_root(struct pci_dev *pdev, struct resource *res) { -- cgit v1.2.3 From 2069ecfbe14ebd71a6f98e8a00724e9adf4fe4ee Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 15 Feb 2012 21:40:31 -0800 Subject: PCI: Move "pci reassigndev resource alignment" out of quirks.c This isn't really a quirk; calling it directly from pci_add_device makes more sense. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 62 ++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.h | 5 +--- drivers/pci/probe.c | 3 +++ drivers/pci/quirks.c | 63 ------------------------------------------------- drivers/pci/setup-res.c | 4 ---- 5 files changed, 66 insertions(+), 71 deletions(-) (limited to 'drivers/pci/probe.c') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e9f9dc183cfc..b832f0fece97 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3694,6 +3694,68 @@ int pci_is_reassigndev(struct pci_dev *dev) return (pci_specified_resource_alignment(dev) != 0); } +/* + * This function disables memory decoding and releases memory resources + * of the device specified by kernel's boot parameter 'pci=resource_alignment='. + * It also rounds up size to specified alignment. + * Later on, the kernel will assign page-aligned memory resource back + * to the device. + */ +void pci_reassigndev_resource_alignment(struct pci_dev *dev) +{ + int i; + struct resource *r; + resource_size_t align, size; + u16 command; + + if (!pci_is_reassigndev(dev)) + return; + + if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && + (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { + dev_warn(&dev->dev, + "Can't reassign resources to host bridge.\n"); + return; + } + + dev_info(&dev->dev, + "Disabling memory decoding and releasing memory resources.\n"); + pci_read_config_word(dev, PCI_COMMAND, &command); + command &= ~PCI_COMMAND_MEMORY; + pci_write_config_word(dev, PCI_COMMAND, command); + + align = pci_specified_resource_alignment(dev); + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + size = resource_size(r); + if (size < align) { + size = align; + dev_info(&dev->dev, + "Rounding up size of resource #%d to %#llx.\n", + i, (unsigned long long)size); + } + r->end = size - 1; + r->start = 0; + } + /* Need to disable bridge's resource window, + * to enable the kernel to reassign new resource + * window later on. + */ + if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { + r = &dev->resource[i]; + if (!(r->flags & IORESOURCE_MEM)) + continue; + r->end = resource_size(r) - 1; + r->start = 0; + } + pci_disable_bridge_window(dev); + } +} + ssize_t pci_set_resource_alignment_param(const char *buf, size_t count) { if (count > RESOURCE_ALIGNMENT_PARAM_SIZE - 1) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 1fc63b39f83f..e4943479b234 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -228,11 +228,8 @@ static inline int pci_ari_enabled(struct pci_bus *bus) return bus->self && bus->self->ari_enabled; } -#ifdef CONFIG_PCI_QUIRKS -extern int pci_is_reassigndev(struct pci_dev *dev); -resource_size_t pci_specified_resource_alignment(struct pci_dev *dev); +void pci_reassigndev_resource_alignment(struct pci_dev *dev); extern void pci_disable_bridge_window(struct pci_dev *dev); -#endif /* Single Root I/O Virtualization */ struct pci_sriov { diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 36c22032ea14..944e05a66b97 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1325,6 +1325,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* moved out from quirk header fixup code */ + pci_reassigndev_resource_alignment(dev); + /* Clear the state_saved flag. */ dev->state_saved = false; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e198589d0990..f8f81d4f29ff 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -31,69 +31,6 @@ #include /* isa_dma_bridge_buggy */ #include "pci.h" -/* - * This quirk function disables memory decoding and releases memory resources - * of the device specified by kernel's boot parameter 'pci=resource_alignment='. - * It also rounds up size to specified alignment. - * Later on, the kernel will assign page-aligned memory resource back - * to the device. - */ -static void __devinit quirk_resource_alignment(struct pci_dev *dev) -{ - int i; - struct resource *r; - resource_size_t align, size; - u16 command; - - if (!pci_is_reassigndev(dev)) - return; - - if (dev->hdr_type == PCI_HEADER_TYPE_NORMAL && - (dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) { - dev_warn(&dev->dev, - "Can't reassign resources to host bridge.\n"); - return; - } - - dev_info(&dev->dev, - "Disabling memory decoding and releasing memory resources.\n"); - pci_read_config_word(dev, PCI_COMMAND, &command); - command &= ~PCI_COMMAND_MEMORY; - pci_write_config_word(dev, PCI_COMMAND, command); - - align = pci_specified_resource_alignment(dev); - for (i=0; i < PCI_BRIDGE_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - size = resource_size(r); - if (size < align) { - size = align; - dev_info(&dev->dev, - "Rounding up size of resource #%d to %#llx.\n", - i, (unsigned long long)size); - } - r->end = size - 1; - r->start = 0; - } - /* Need to disable bridge's resource window, - * to enable the kernel to reassign new resource - * window later on. - */ - if (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - for (i = PCI_BRIDGE_RESOURCES; i < PCI_NUM_RESOURCES; i++) { - r = &dev->resource[i]; - if (!(r->flags & IORESOURCE_MEM)) - continue; - r->end = resource_size(r) - 1; - r->start = 0; - } - pci_disable_bridge_window(dev); - } -} -DECLARE_PCI_FIXUP_HEADER(PCI_ANY_ID, PCI_ANY_ID, quirk_resource_alignment); - /* * Decoding should be disabled for a PCI device during BAR sizing to avoid * conflict. But doing so may cause problems on host bridge and perhaps other diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index f968185aa192..eea85dafc763 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -114,7 +114,6 @@ int pci_claim_resource(struct pci_dev *dev, int resource) } EXPORT_SYMBOL(pci_claim_resource); -#ifdef CONFIG_PCI_QUIRKS void pci_disable_bridge_window(struct pci_dev *dev) { dev_info(&dev->dev, "disabling bridge mem windows\n"); @@ -127,9 +126,6 @@ void pci_disable_bridge_window(struct pci_dev *dev) pci_write_config_dword(dev, PCI_PREF_MEMORY_BASE, 0x0000fff0); pci_write_config_dword(dev, PCI_PREF_BASE_UPPER32, 0xffffffff); } -#endif /* CONFIG_PCI_QUIRKS */ - - static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, int resno, resource_size_t size, resource_size_t align) -- cgit v1.2.3 From cf48fb6a2bf2e59990e1438d0dedc706df911996 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Fri, 16 Mar 2012 17:47:59 -0600 Subject: PCI: fix bridge I/O window bus-to-resource conversion In 5bfa14ed9f3c, I forgot to initialize res2.flags before calling pcibios_bus_to_resource(), which depends on the resource type to locate the correct aperture. This bug won't hurt x86, which currently never has an offset between bus and CPU addresses, but will affect other architectures. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- 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 944e05a66b97..5e1ca3c58a7d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -367,6 +367,7 @@ static void __devinit pci_read_bridge_io(struct pci_bus *child) if (base && base <= limit) { res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + res2.flags = res->flags; region.start = base; region.end = limit + 0xfff; pcibios_bus_to_resource(dev, &res2, ®ion); -- cgit v1.2.3