From 7eb93b175d4de9438a4b0af3a94a112cb5266944 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Fri, 3 Apr 2009 15:18:11 +0800 Subject: PCI: SR-IOV quirk for Intel 82576 NIC If BIOS doesn't allocate resources for the SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the old Flash Memory Space. Please refer to Intel 82576 Gigabit Ethernet Controller Datasheet section 7.9.2.14.2 for details. http://download.intel.com/design/network/datashts/82576_Datasheet.pdf Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 9b2f0d96900d..51011a24589d 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2411,6 +2411,54 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ +#ifdef CONFIG_PCI_IOV + +/* + * For Intel 82576 SR-IOV NIC, if BIOS doesn't allocate resources for the + * SR-IOV BARs, zero the Flash BAR and program the SR-IOV BARs to use the + * old Flash Memory Space. + */ +static void __devinit quirk_i82576_sriov(struct pci_dev *dev) +{ + int pos, flags; + u32 bar, start, size; + + if (PAGE_SIZE > 0x10000) + return; + + flags = pci_resource_flags(dev, 0); + if ((flags & PCI_BASE_ADDRESS_SPACE) != + PCI_BASE_ADDRESS_SPACE_MEMORY || + (flags & PCI_BASE_ADDRESS_MEM_TYPE_MASK) != + PCI_BASE_ADDRESS_MEM_TYPE_32) + return; + + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_SRIOV); + if (!pos) + return; + + pci_read_config_dword(dev, pos + PCI_SRIOV_BAR, &bar); + if (bar & PCI_BASE_ADDRESS_MEM_MASK) + return; + + start = pci_resource_start(dev, 1); + size = pci_resource_len(dev, 1); + if (!start || size != 0x400000 || start & (size - 1)) + return; + + pci_resource_flags(dev, 1) = 0; + pci_write_config_dword(dev, PCI_BASE_ADDRESS_1, 0); + pci_write_config_dword(dev, pos + PCI_SRIOV_BAR, start); + pci_write_config_dword(dev, pos + PCI_SRIOV_BAR + 12, start + size / 2); + + dev_info(&dev->dev, "use Flash Memory Space for SR-IOV BARs\n"); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10c9, quirk_i82576_sriov); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e6, quirk_i82576_sriov); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e7, quirk_i82576_sriov); + +#endif /* CONFIG_PCI_IOV */ + static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) { -- cgit v1.2.3 From 296ccb086dfb89b5b8d73ef08c795ffdff12a597 Mon Sep 17 00:00:00 2001 From: Yuji Shimada Date: Fri, 3 Apr 2009 16:41:46 +0900 Subject: PCI: Setup disabled bridges even if buses are added This patch sets up disabled bridges even if buses have already been added. pci_assign_unassigned_resources is called after buses are added. pci_assign_unassigned_resources calls pci_bus_assign_resources. pci_bus_assign_resources calls pci_setup_bridge to configure BARs of bridges. Currently pci_setup_bridge returns immediately if the bus have already been added. So pci_assign_unassigned_resources can't configure BARs of bridges that were added in a disabled state; this patch fixes the issue. On logical hot-add, we need to prevent the kernel from re-initializing bridges that have already been initialized. To achieve this, pci_setup_bridge returns immediately if the bridge have already been enabled. We don't need to check whether the specified bus is a root bus or not. pci_setup_bridge is not called on a root bus, because a root bus does not have a bridge. The patch adds a new helper function, pci_is_enabled. I made the function name similar to pci_is_managed. The codes which use enable_cnt directly are changed to use pci_is_enabled. Acked-by: Alex Chiang Signed-off-by: Yuji Shimada Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 2 +- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.c | 4 ++-- drivers/pci/setup-bus.c | 2 +- include/linux/pci.h | 5 +++++ 5 files changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 68f91a252595..97a8194063b5 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -184,7 +184,7 @@ void pci_enable_bridges(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { if (dev->subordinate) { - if (atomic_read(&dev->enable_cnt) == 0) { + if (!pci_is_enabled(dev)) { retval = pci_enable_device(dev); pci_set_master(dev); } diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index e9a8706a6401..cd8e682c04aa 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -148,7 +148,7 @@ static ssize_t is_enabled_store(struct device *dev, return -EPERM; if (!val) { - if (atomic_read(&pdev->enable_cnt) != 0) + if (pci_is_enabled(pdev)) pci_disable_device(pdev); else result = -EIO; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 445fb6f7ea3f..16fd0d4c3166 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -844,7 +844,7 @@ static int do_pci_enable_device(struct pci_dev *dev, int bars) */ int pci_reenable_device(struct pci_dev *dev) { - if (atomic_read(&dev->enable_cnt)) + if (pci_is_enabled(dev)) return do_pci_enable_device(dev, (1 << PCI_NUM_RESOURCES) - 1); return 0; } @@ -1042,7 +1042,7 @@ static void do_pci_disable_device(struct pci_dev *dev) */ void pci_disable_enabled_device(struct pci_dev *dev) { - if (atomic_read(&dev->enable_cnt)) + if (pci_is_enabled(dev)) do_pci_disable_device(dev); } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 334285a8e237..8d9da9d30a61 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -144,7 +144,7 @@ static void pci_setup_bridge(struct pci_bus *bus) struct pci_bus_region region; u32 l, bu, lu, io_upper16; - if (!pci_is_root_bus(bus) && bus->is_added) + if (pci_is_enabled(bridge)) return; dev_info(&bridge->dev, "PCI bridge, secondary bus %04x:%02x\n", diff --git a/include/linux/pci.h b/include/linux/pci.h index a7fe4bbd7ff1..72698d89e767 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -674,6 +674,11 @@ int __must_check pci_reenable_device(struct pci_dev *); int __must_check pcim_enable_device(struct pci_dev *pdev); void pcim_pin_device(struct pci_dev *pdev); +static inline int pci_is_enabled(struct pci_dev *pdev) +{ + return (atomic_read(&pdev->enable_cnt) > 0); +} + static inline int pci_is_managed(struct pci_dev *pdev) { return pdev->is_managed; -- cgit v1.2.3 From 52a8873ba4e82d6e87f8478b3e7f9c12d8b37c38 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 1 Apr 2009 17:45:30 -0700 Subject: PCI-IOV: fix missing kernel-doc Fix PCI iov kernel-doc warning: Warning(drivers/pci/iov.c:638): No description found for parameter 'nr_virtfn' Signed-off-by: Randy Dunlap Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index 7227efc760db..b497daab3d4a 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -631,6 +631,7 @@ int pci_iov_bus_range(struct pci_bus *bus) /** * pci_enable_sriov - enable the SR-IOV capability * @dev: the PCI device + * @nr_virtfn: number of virtual functions to enable * * Returns 0 on success, or negative on failure. */ -- cgit v1.2.3 From 5446a6bdb51e71da7a203e395b0b4c668d559a3a Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Wed, 1 Apr 2009 18:24:12 -0600 Subject: PCI: annotate pci_rescan_bus as __ref, not __devinit pci_rescan_bus was annotated as __devinit, which is wrong, because it will never be part of device initialization. Howevever, we can't simply drop the annotation, because then we get section warnings about calling pci_scan_child_bus (which is correctly marked as __devinit). pci_rescan_bus will only get built when CONFIG_HOTPLUG is set, meaning that __devinit is a nop, so we know that pci_scan_child_bus has not been freed. Annotate as __ref to silence modpost. Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e2f3dd098cfa..8eb50dffb78a 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1220,7 +1220,7 @@ EXPORT_SYMBOL(pci_scan_bus_parented); * * Returns the max number of subordinate bus discovered. */ -unsigned int __devinit pci_rescan_bus(struct pci_bus *bus) +unsigned int __ref pci_rescan_bus(struct pci_bus *bus) { unsigned int max; struct pci_dev *dev; -- cgit v1.2.3 From 0cdbe30f0b04466a1fee11b81b3667bd1b982549 Mon Sep 17 00:00:00 2001 From: Yuji Shimada Date: Mon, 6 Apr 2009 10:24:21 +0900 Subject: PCI: don't corrupt enable_cnt when doing manual resource alignment This patch fixes breakage of of enable_cnt in quirk_resource_alignment. Currently, quirk_resource_alignment calls pci_disable_device. pci_disable_device decrements enable_cnt, so that enable_cnt becomes -1. The patch disables memory decoding, writing command register directly. So enable_cnt is not broken. Signed-off-by: Yuji Shimada Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 51011a24589d..0254741bece0 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -36,17 +36,18 @@ EXPORT_SYMBOL(pcie_mch_quirk); #ifdef CONFIG_PCI_QUIRKS /* - * This quirk function disables the device and releases resources - * which is specified by kernel's boot parameter 'pci=resource_alignment='. + * 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 that device. + * 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; @@ -58,8 +59,11 @@ static void __devinit quirk_resource_alignment(struct pci_dev *dev) return; } - dev_info(&dev->dev, "Disabling device and release resources.\n"); - pci_disable_device(dev); + 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++) { -- cgit v1.2.3 From d6141668c24d7d75c3486499c05a6b382cbc2bbe Mon Sep 17 00:00:00 2001 From: Anton Vorontsov Date: Wed, 1 Apr 2009 02:23:41 +0400 Subject: PCI: Fix oops in pci_vpd_truncate pci_vpd_truncate() should check for dev->vpd->attr, otherwise this might happen: sky2 driver version 1.22 Unable to handle kernel paging request for data at address 0x0000000c Faulting instruction address: 0xc01836fc Oops: Kernel access of bad area, sig: 11 [#1] [...] NIP [c01836fc] pci_vpd_truncate+0x38/0x40 LR [c029be18] sky2_probe+0x14c/0x518 Call Trace: [ef82bde0] [c029bda4] sky2_probe+0xd8/0x518 (unreliable) [ef82be20] [c018a11c] local_pci_probe+0x24/0x34 [ef82be30] [c018a14c] pci_call_probe+0x20/0x30 [ef82be50] [c018a330] __pci_device_probe+0x64/0x78 [ef82be60] [c018a44c] pci_device_probe+0x30/0x58 [ef82be80] [c01aa270] really_probe+0x78/0x1a0 [ef82bea0] [c01aa460] __driver_attach+0xa4/0xa8 [ef82bec0] [c01a96ac] bus_for_each_dev+0x60/0x9c [ef82bef0] [c01aa0b4] driver_attach+0x24/0x34 [ef82bf00] [c01a9e08] bus_add_driver+0x12c/0x1cc [ef82bf20] [c01aa87c] driver_register+0x6c/0x110 [ef82bf30] [c018a770] __pci_register_driver+0x4c/0x9c [ef82bf50] [c03782c8] sky2_init_module+0x30/0x40 [ef82bf60] [c0001dbc] do_one_initcall+0x34/0x1a0 [ef82bfd0] [c0362240] do_initcalls+0x38/0x58 This happens with CONFIG_SKY2=y, and "ip=on" kernel command line, so pci_vpd_truncate() is called before late_initcall(pci_sysfs_init), therefore ->attr isn't yet initialized. Acked-by: Stephen Hemminger Signed-off-by: Anton Vorontsov Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 381444794778..64dd7df90e62 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -356,7 +356,8 @@ int pci_vpd_truncate(struct pci_dev *dev, size_t size) return -EINVAL; dev->vpd->len = size; - dev->vpd->attr->size = size; + if (dev->vpd->attr) + dev->vpd->attr->size = size; return 0; } -- cgit v1.2.3 From c2ac7cdc6707a8a3fc5cdaebb65cb724ba8ecdef Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 30 Mar 2009 10:50:09 -0600 Subject: PCI: allow PCI core hotplug to remove PCI root bus There is no reason to prevent removal of root bus devices. A subsequent rescan will find them just fine. Reviewed-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 4 ---- 1 file changed, 4 deletions(-) (limited to 'drivers') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index cd8e682c04aa..a7eb1b46a5a8 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -277,14 +277,10 @@ remove_store(struct device *dev, struct device_attribute *dummy, { int ret = 0; unsigned long val; - struct pci_dev *pdev = to_pci_dev(dev); if (strict_strtoul(buf, 0, &val) < 0) return -EINVAL; - if (pci_is_root_bus(pdev->bus)) - return -EBUSY; - /* An attribute cannot be unregistered by one of its own methods, * so we have to use this roundabout approach. */ -- cgit v1.2.3 From 5d4a4b25ddc3e864d3a562c024bebdc922118854 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 30 Mar 2009 10:50:14 -0600 Subject: PCI Hotplug: acpiphp: grab refcount on p2p subordinate bus If a logical hot unplug (remove) is performed on a bridge claimed by acpiphp and then acpiphp is unloaded, we will encounter an oops. This is because acpiphp will access the bridge's subordinate bus, which was released by the user's prior hot unplug. The solution is to grab a reference on the subordinate PCI bus. This will prevent the bus from release until acpiphp is unloaded. Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Reported-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) (limited to 'drivers') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 803d9ddd6e75..a33794d9e0dc 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -38,6 +38,8 @@ * - The one in acpiphp_bridge has its refcount elevated by pci_get_slot() * when the bridge is scanned and it loses a refcount when the bridge * is removed. + * - When a P2P bridge is present, we elevate the refcount on the subordinate + * bus. It loses the refcount when the the driver unloads. */ #include @@ -440,6 +442,12 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev) goto err; } + /* + * Grab a ref to the subordinate PCI bus in case the bus is + * removed via PCI core logical hotplug. The ref pins the bus + * (which we access during module unload). + */ + get_device(&bridge->pci_bus->dev); spin_lock_init(&bridge->res_lock); init_bridge_misc(bridge); @@ -619,6 +627,12 @@ static void cleanup_bridge(struct acpiphp_bridge *bridge) slot = next; } + /* + * Only P2P bridges have a pci_dev + */ + if (bridge->pci_dev) + put_device(&bridge->pci_bus->dev); + pci_dev_put(bridge->pci_dev); list_del(&bridge->list); kfree(bridge); -- cgit v1.2.3 From 72800360fdd782eda3489e555adf3b6b3abc064a Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 30 Mar 2009 10:50:19 -0600 Subject: PCI: pci_slot: grab refcount on slot's bus If a logical hot unplug (remove) is performed on a physical PCI slot's parent bridge, and then pci_slot is unloaded, we will encounter an oops: [] kobject_release+0x9a/0x290 [] ? kobject_release+0x0/0x290 [] kref_put+0x37/0x80 [] kobject_put+0x27/0x60 [] ? pci_destroy_slot+0x3c/0xc0 [] pci_destroy_slot+0x45/0xc0 [] acpi_pci_slot_remove+0x5c/0x91 [pci_slot] [] acpi_pci_unregister_driver+0x4b/0x62 [] acpi_pci_slot_exit+0x10/0x12 [pci_slot] [] sys_delete_module+0x161/0x250 We need to grab a reference to the parent PCI bus, which will pin the bus and prevent it from being released until pci_slot is unloaded. Cc: lenb@kernel.org Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Reported-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/acpi/pci_slot.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/acpi/pci_slot.c b/drivers/acpi/pci_slot.c index cd1f4467be7b..12158e0d009b 100644 --- a/drivers/acpi/pci_slot.c +++ b/drivers/acpi/pci_slot.c @@ -164,6 +164,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) list_add(&slot->list, &slot_list); mutex_unlock(&slot_list_lock); + get_device(&pci_bus->dev); + dbg("pci_slot: %p, pci_bus: %x, device: %d, name: %s\n", pci_slot, pci_bus->number, device, name); @@ -310,12 +312,15 @@ static void acpi_pci_slot_remove(acpi_handle handle) { struct acpi_pci_slot *slot, *tmp; + struct pci_bus *pbus; mutex_lock(&slot_list_lock); list_for_each_entry_safe(slot, tmp, &slot_list, list) { if (slot->root_handle == handle) { list_del(&slot->list); + pbus = slot->pci_slot->bus; pci_destroy_slot(slot->pci_slot); + put_device(&pbus->dev); kfree(slot); } } -- cgit v1.2.3