diff options
Diffstat (limited to 'drivers/pci')
-rw-r--r-- | drivers/pci/Makefile | 3 | ||||
-rw-r--r-- | drivers/pci/hotplug/acpiphp_glue.c | 28 | ||||
-rw-r--r-- | drivers/pci/hotplug/pci_hotplug_core.c | 3 | ||||
-rw-r--r-- | drivers/pci/hotplug/pciehp_hpc.c | 10 | ||||
-rw-r--r-- | drivers/pci/legacy.c | 34 | ||||
-rw-r--r-- | drivers/pci/msi.c | 283 | ||||
-rw-r--r-- | drivers/pci/pci-sysfs.c | 37 | ||||
-rw-r--r-- | drivers/pci/pci.c | 60 | ||||
-rw-r--r-- | drivers/pci/pci.h | 1 | ||||
-rw-r--r-- | drivers/pci/pcie/aspm.c | 495 | ||||
-rw-r--r-- | drivers/pci/probe.c | 12 | ||||
-rw-r--r-- | drivers/pci/quirks.c | 4 | ||||
-rw-r--r-- | drivers/pci/search.c | 31 | ||||
-rw-r--r-- | drivers/pci/setup-res.c | 1 |
14 files changed, 564 insertions, 438 deletions
diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 1ebd6b4c743b..4a7f11d8f432 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -8,6 +8,9 @@ obj-y += access.o bus.o probe.o remove.o pci.o quirks.o \ obj-$(CONFIG_PROC_FS) += proc.o obj-$(CONFIG_SYSFS) += slot.o +obj-$(CONFIG_PCI_LEGACY) += legacy.o +CFLAGS_legacy.o += -Wno-deprecated-declarations + # Build PCI Express stuff if needed obj-$(CONFIG_PCIEPORTBUS) += pcie/ diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 0cb0f830a993..2e5f25969e11 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -62,6 +62,22 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus); static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus); static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); +static struct pci_bus *pci_bus_from_handle(acpi_handle handle) +{ + struct pci_bus *pbus; + struct acpi_pci_root *root; + + root = acpi_pci_find_root(handle); + if (root) + pbus = root->bus; + else { + struct pci_dev *pdev = acpi_get_pci_dev(handle); + pbus = pdev->subordinate; + pci_dev_put(pdev); + } + return pbus; +} + /* callback routine to check for the existence of a pci dock device */ static acpi_status is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) @@ -1387,16 +1403,7 @@ static void acpiphp_sanitize_bus(struct pci_bus *bus) /* Program resources in newly inserted bridge */ static int acpiphp_configure_bridge (acpi_handle handle) { - struct pci_dev *dev; - struct pci_bus *bus; - - dev = acpi_get_pci_dev(handle); - if (!dev) { - err("cannot get PCI domain and bus number for bridge\n"); - return -EINVAL; - } - - bus = dev->bus; + struct pci_bus *bus = pci_bus_from_handle(handle); pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); @@ -1404,7 +1411,6 @@ static int acpiphp_configure_bridge (acpi_handle handle) acpiphp_set_hpp_values(handle, bus); pci_enable_bridges(bus); acpiphp_configure_ioapics(handle); - pci_dev_put(dev); return 0; } diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 5c5043f239cf..0325d989bb46 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -86,7 +86,8 @@ static char *pci_bus_speed_strings[] = { "66 MHz PCIX 533", /* 0x11 */ "100 MHz PCIX 533", /* 0x12 */ "133 MHz PCIX 533", /* 0x13 */ - "25 GBps PCI-E", /* 0x14 */ + "2.5 GT/s PCI-E", /* 0x14 */ + "5.0 GT/s PCI-E", /* 0x15 */ }; #ifdef CONFIG_HOTPLUG_PCI_CPCI diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 52813257e5bf..271f917b6f2c 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -693,7 +693,10 @@ static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) switch (lnk_cap & 0x000F) { case 1: - lnk_speed = PCIE_2PT5GB; + lnk_speed = PCIE_2_5GB; + break; + case 2: + lnk_speed = PCIE_5_0GB; break; default: lnk_speed = PCIE_LNK_SPEED_UNKNOWN; @@ -772,7 +775,10 @@ static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) switch (lnk_status & PCI_EXP_LNKSTA_CLS) { case 1: - lnk_speed = PCIE_2PT5GB; + lnk_speed = PCIE_2_5GB; + break; + case 2: + lnk_speed = PCIE_5_0GB; break; default: lnk_speed = PCIE_LNK_SPEED_UNKNOWN; diff --git a/drivers/pci/legacy.c b/drivers/pci/legacy.c new file mode 100644 index 000000000000..871f65c15936 --- /dev/null +++ b/drivers/pci/legacy.c @@ -0,0 +1,34 @@ +#include <linux/init.h> +#include <linux/pci.h> +#include <linux/module.h> +#include <linux/interrupt.h> +#include "pci.h" + +/** + * pci_find_device - begin or continue searching for a PCI device by vendor/device id + * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids + * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids + * @from: Previous PCI device found in search, or %NULL for new search. + * + * Iterates through the list of known PCI devices. If a PCI device is found + * with a matching @vendor and @device, a pointer to its device structure is + * returned. Otherwise, %NULL is returned. + * A new search is initiated by passing %NULL as the @from argument. + * Otherwise if @from is not %NULL, searches continue from next device + * on the global list. + * + * NOTE: Do not use this function any more; use pci_get_device() instead, as + * the PCI device returned by this function can disappear at any moment in + * time. + */ +struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, + struct pci_dev *from) +{ + struct pci_dev *pdev; + + pci_dev_get(from); + pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); + pci_dev_put(pdev); + return pdev; +} +EXPORT_SYMBOL(pci_find_device); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d986afb7032b..f9cf3173b23d 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -16,9 +16,8 @@ #include <linux/proc_fs.h> #include <linux/msi.h> #include <linux/smp.h> - -#include <asm/errno.h> -#include <asm/io.h> +#include <linux/errno.h> +#include <linux/io.h> #include "pci.h" #include "msi.h" @@ -272,7 +271,30 @@ void write_msi_msg(unsigned int irq, struct msi_msg *msg) write_msi_msg_desc(desc, msg); } -static int msi_free_irqs(struct pci_dev* dev); +static void free_msi_irqs(struct pci_dev *dev) +{ + struct msi_desc *entry, *tmp; + + list_for_each_entry(entry, &dev->msi_list, list) { + int i, nvec; + if (!entry->irq) + continue; + nvec = 1 << entry->msi_attrib.multiple; + for (i = 0; i < nvec; i++) + BUG_ON(irq_has_action(entry->irq + i)); + } + + arch_teardown_msi_irqs(dev); + + list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { + if (entry->msi_attrib.is_msix) { + if (list_is_last(&entry->list, &dev->msi_list)) + iounmap(entry->mask_base); + } + list_del(&entry->list); + kfree(entry); + } +} static struct msi_desc *alloc_msi_entry(struct pci_dev *dev) { @@ -324,7 +346,7 @@ static void __pci_restore_msix_state(struct pci_dev *dev) if (!dev->msix_enabled) return; BUG_ON(list_empty(&dev->msi_list)); - entry = list_entry(dev->msi_list.next, struct msi_desc, list); + entry = list_first_entry(&dev->msi_list, struct msi_desc, list); pos = entry->msi_attrib.pos; pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); @@ -367,7 +389,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) u16 control; unsigned mask; - pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); msi_set_enable(dev, pos, 0); /* Disable MSI during set up */ pci_read_config_word(dev, msi_control_reg(pos), &control); @@ -376,12 +398,12 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) if (!entry) return -ENOMEM; - entry->msi_attrib.is_msix = 0; - entry->msi_attrib.is_64 = is_64bit_address(control); - entry->msi_attrib.entry_nr = 0; - entry->msi_attrib.maskbit = is_mask_bit_support(control); - entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ - entry->msi_attrib.pos = pos; + entry->msi_attrib.is_msix = 0; + entry->msi_attrib.is_64 = is_64bit_address(control); + entry->msi_attrib.entry_nr = 0; + entry->msi_attrib.maskbit = is_mask_bit_support(control); + entry->msi_attrib.default_irq = dev->irq; /* Save IOAPIC IRQ */ + entry->msi_attrib.pos = pos; entry->mask_pos = msi_mask_reg(pos, entry->msi_attrib.is_64); /* All MSIs are unmasked by default, Mask them all */ @@ -396,7 +418,7 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSI); if (ret) { msi_mask_irq(entry, mask, ~mask); - msi_free_irqs(dev); + free_msi_irqs(dev); return ret; } @@ -409,44 +431,27 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) return 0; } -/** - * msix_capability_init - configure device's MSI-X capability - * @dev: pointer to the pci_dev data structure of MSI-X device function - * @entries: pointer to an array of struct msix_entry entries - * @nvec: number of @entries - * - * Setup the MSI-X capability structure of device function with a - * single MSI-X irq. A return of zero indicates the successful setup of - * requested MSI-X entries with allocated irqs or non-zero for otherwise. - **/ -static int msix_capability_init(struct pci_dev *dev, - struct msix_entry *entries, int nvec) +static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, + unsigned nr_entries) { - struct msi_desc *entry; - int pos, i, j, nr_entries, ret; unsigned long phys_addr; u32 table_offset; - u16 control; u8 bir; - void __iomem *base; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); - pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); - - /* Ensure MSI-X is disabled while it is set up */ - control &= ~PCI_MSIX_FLAGS_ENABLE; - pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); - - /* Request & Map MSI-X table region */ - nr_entries = multi_msix_capable(control); - - pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); + pci_read_config_dword(dev, msix_table_offset_reg(pos), &table_offset); bir = (u8)(table_offset & PCI_MSIX_FLAGS_BIRMASK); table_offset &= ~PCI_MSIX_FLAGS_BIRMASK; - phys_addr = pci_resource_start (dev, bir) + table_offset; - base = ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); - if (base == NULL) - return -ENOMEM; + phys_addr = pci_resource_start(dev, bir) + table_offset; + + return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); +} + +static int msix_setup_entries(struct pci_dev *dev, unsigned pos, + void __iomem *base, struct msix_entry *entries, + int nvec) +{ + struct msi_desc *entry; + int i; for (i = 0; i < nvec; i++) { entry = alloc_msi_entry(dev); @@ -454,41 +459,78 @@ static int msix_capability_init(struct pci_dev *dev, if (!i) iounmap(base); else - msi_free_irqs(dev); + free_msi_irqs(dev); /* No enough memory. Don't try again */ return -ENOMEM; } - j = entries[i].entry; - entry->msi_attrib.is_msix = 1; - entry->msi_attrib.is_64 = 1; - entry->msi_attrib.entry_nr = j; - entry->msi_attrib.default_irq = dev->irq; - entry->msi_attrib.pos = pos; - entry->mask_base = base; + entry->msi_attrib.is_msix = 1; + entry->msi_attrib.is_64 = 1; + entry->msi_attrib.entry_nr = entries[i].entry; + entry->msi_attrib.default_irq = dev->irq; + entry->msi_attrib.pos = pos; + entry->mask_base = base; list_add_tail(&entry->list, &dev->msi_list); } - ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); - if (ret < 0) { - /* If we had some success report the number of irqs - * we succeeded in setting up. */ - int avail = 0; - list_for_each_entry(entry, &dev->msi_list, list) { - if (entry->irq != 0) { - avail++; - } - } + return 0; +} - if (avail != 0) - ret = avail; +static void msix_program_entries(struct pci_dev *dev, + struct msix_entry *entries) +{ + struct msi_desc *entry; + int i = 0; + + list_for_each_entry(entry, &dev->msi_list, list) { + int offset = entries[i].entry * PCI_MSIX_ENTRY_SIZE + + PCI_MSIX_ENTRY_VECTOR_CTRL; + + entries[i].vector = entry->irq; + set_irq_msi(entry->irq, entry); + entry->masked = readl(entry->mask_base + offset); + msix_mask_irq(entry, 1); + i++; } +} - if (ret) { - msi_free_irqs(dev); +/** + * msix_capability_init - configure device's MSI-X capability + * @dev: pointer to the pci_dev data structure of MSI-X device function + * @entries: pointer to an array of struct msix_entry entries + * @nvec: number of @entries + * + * Setup the MSI-X capability structure of device function with a + * single MSI-X irq. A return of zero indicates the successful setup of + * requested MSI-X entries with allocated irqs or non-zero for otherwise. + **/ +static int msix_capability_init(struct pci_dev *dev, + struct msix_entry *entries, int nvec) +{ + int pos, ret; + u16 control; + void __iomem *base; + + pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + pci_read_config_word(dev, pos + PCI_MSIX_FLAGS, &control); + + /* Ensure MSI-X is disabled while it is set up */ + control &= ~PCI_MSIX_FLAGS_ENABLE; + pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); + + /* Request & Map MSI-X table region */ + base = msix_map_region(dev, pos, multi_msix_capable(control)); + if (!base) + return -ENOMEM; + + ret = msix_setup_entries(dev, pos, base, entries, nvec); + if (ret) return ret; - } + + ret = arch_setup_msi_irqs(dev, nvec, PCI_CAP_ID_MSIX); + if (ret) + goto error; /* * Some devices require MSI-X to be enabled before we can touch the @@ -498,16 +540,7 @@ static int msix_capability_init(struct pci_dev *dev, control |= PCI_MSIX_FLAGS_MASKALL | PCI_MSIX_FLAGS_ENABLE; pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); - i = 0; - list_for_each_entry(entry, &dev->msi_list, list) { - entries[i].vector = entry->irq; - set_irq_msi(entry->irq, entry); - j = entries[i].entry; - entry->masked = readl(base + j * PCI_MSIX_ENTRY_SIZE + - PCI_MSIX_ENTRY_VECTOR_CTRL); - msix_mask_irq(entry, 1); - i++; - } + msix_program_entries(dev, entries); /* Set MSI-X enabled bits and unmask the function */ pci_intx_for_msi(dev, 0); @@ -517,6 +550,27 @@ static int msix_capability_init(struct pci_dev *dev, pci_write_config_word(dev, pos + PCI_MSIX_FLAGS, control); return 0; + +error: + if (ret < 0) { + /* + * If we had some success, report the number of irqs + * we succeeded in setting up. + */ + struct msi_desc *entry; + int avail = 0; + + list_for_each_entry(entry, &dev->msi_list, list) { + if (entry->irq != 0) + avail++; + } + if (avail != 0) + ret = avail; + } + + free_msi_irqs(dev); + + return ret; } /** @@ -529,7 +583,7 @@ static int msix_capability_init(struct pci_dev *dev, * to determine if MSI/-X are supported for the device. If MSI/-X is * supported return 0, else return an error code. **/ -static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type) +static int pci_msi_check_device(struct pci_dev *dev, int nvec, int type) { struct pci_bus *bus; int ret; @@ -546,8 +600,9 @@ static int pci_msi_check_device(struct pci_dev* dev, int nvec, int type) if (nvec < 1) return -ERANGE; - /* Any bridge which does NOT route MSI transactions from it's - * secondary bus to it's primary bus must set NO_MSI flag on + /* + * Any bridge which does NOT route MSI transactions from its + * secondary bus to its primary bus must set NO_MSI flag on * the secondary pci_bus. * We expect only arch-specific PCI host bus controller driver * or quirks for specific PCI bridges to be setting NO_MSI. @@ -638,50 +693,16 @@ void pci_msi_shutdown(struct pci_dev *dev) dev->irq = desc->msi_attrib.default_irq; } -void pci_disable_msi(struct pci_dev* dev) +void pci_disable_msi(struct pci_dev *dev) { - struct msi_desc *entry; - if (!pci_msi_enable || !dev || !dev->msi_enabled) return; pci_msi_shutdown(dev); - - entry = list_entry(dev->msi_list.next, struct msi_desc, list); - if (entry->msi_attrib.is_msix) - return; - - msi_free_irqs(dev); + free_msi_irqs(dev); } EXPORT_SYMBOL(pci_disable_msi); -static int msi_free_irqs(struct pci_dev* dev) -{ - struct msi_desc *entry, *tmp; - - list_for_each_entry(entry, &dev->msi_list, list) { - int i, nvec; - if (!entry->irq) - continue; - nvec = 1 << entry->msi_attrib.multiple; - for (i = 0; i < nvec; i++) - BUG_ON(irq_has_action(entry->irq + i)); - } - - arch_teardown_msi_irqs(dev); - - list_for_each_entry_safe(entry, tmp, &dev->msi_list, list) { - if (entry->msi_attrib.is_msix) { - if (list_is_last(&entry->list, &dev->msi_list)) - iounmap(entry->mask_base); - } - list_del(&entry->list); - kfree(entry); - } - - return 0; -} - /** * pci_msix_table_size - return the number of device's MSI-X table entries * @dev: pointer to the pci_dev data structure of MSI-X device function @@ -714,13 +735,13 @@ int pci_msix_table_size(struct pci_dev *dev) * of irqs or MSI-X vectors available. Driver should use the returned value to * re-send its request. **/ -int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) +int pci_enable_msix(struct pci_dev *dev, struct msix_entry *entries, int nvec) { int status, nr_entries; int i, j; if (!entries) - return -EINVAL; + return -EINVAL; status = pci_msi_check_device(dev, nvec, PCI_CAP_ID_MSIX); if (status) @@ -742,7 +763,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) WARN_ON(!!dev->msix_enabled); /* Check whether driver already requested for MSI irq */ - if (dev->msi_enabled) { + if (dev->msi_enabled) { dev_info(&dev->dev, "can't enable MSI-X " "(MSI IRQ already assigned)\n"); return -EINVAL; @@ -752,12 +773,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) } EXPORT_SYMBOL(pci_enable_msix); -static void msix_free_all_irqs(struct pci_dev *dev) -{ - msi_free_irqs(dev); -} - -void pci_msix_shutdown(struct pci_dev* dev) +void pci_msix_shutdown(struct pci_dev *dev) { struct msi_desc *entry; @@ -774,14 +790,14 @@ void pci_msix_shutdown(struct pci_dev* dev) pci_intx_for_msi(dev, 1); dev->msix_enabled = 0; } -void pci_disable_msix(struct pci_dev* dev) + +void pci_disable_msix(struct pci_dev *dev) { if (!pci_msi_enable || !dev || !dev->msix_enabled) return; pci_msix_shutdown(dev); - - msix_free_all_irqs(dev); + free_msi_irqs(dev); } EXPORT_SYMBOL(pci_disable_msix); @@ -794,16 +810,13 @@ EXPORT_SYMBOL(pci_disable_msix); * allocated for this device function, are reclaimed to unused state, * which may be used later on. **/ -void msi_remove_pci_irq_vectors(struct pci_dev* dev) +void msi_remove_pci_irq_vectors(struct pci_dev *dev) { if (!pci_msi_enable || !dev) - return; - - if (dev->msi_enabled) - msi_free_irqs(dev); + return; - if (dev->msix_enabled) - msix_free_all_irqs(dev); + if (dev->msi_enabled || dev->msix_enabled) + free_msi_irqs(dev); } void pci_no_msi(void) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 85ebd02a64a7..0f6382f090ee 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -916,6 +916,24 @@ int __attribute__ ((weak)) pcibios_add_platform_entries(struct pci_dev *dev) return 0; } +static ssize_t reset_store(struct device *dev, + struct device_attribute *attr, const char *buf, + size_t count) +{ + struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + ssize_t result = strict_strtoul(buf, 0, &val); + + if (result < 0) + return result; + + if (val != 1) + return -EINVAL; + return pci_reset_function(pdev); +} + +static struct device_attribute reset_attr = __ATTR(reset, 0200, NULL, reset_store); + static int pci_create_capabilities_sysfs(struct pci_dev *dev) { int retval; @@ -943,7 +961,22 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) /* Active State Power Management */ pcie_aspm_create_sysfs_dev_files(dev); + if (!pci_probe_reset_function(dev)) { + retval = device_create_file(&dev->dev, &reset_attr); + if (retval) + goto error; + dev->reset_fn = 1; + } return 0; + +error: + pcie_aspm_remove_sysfs_dev_files(dev); + if (dev->vpd && dev->vpd->attr) { + sysfs_remove_bin_file(&dev->dev.kobj, dev->vpd->attr); + kfree(dev->vpd->attr); + } + + return retval; } int __must_check pci_create_sysfs_dev_files (struct pci_dev *pdev) @@ -1037,6 +1070,10 @@ static void pci_remove_capabilities_sysfs(struct pci_dev *dev) } pcie_aspm_remove_sysfs_dev_files(dev); + if (dev->reset_fn) { + device_remove_file(&dev->dev, &reset_attr); + dev->reset_fn = 0; + } } /** diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7b70312181d7..bd993351db45 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2262,6 +2262,22 @@ int __pci_reset_function(struct pci_dev *dev) EXPORT_SYMBOL_GPL(__pci_reset_function); /** + * pci_probe_reset_function - check whether the device can be safely reset + * @dev: PCI device to reset + * + * Some devices allow an individual function to be reset without affecting + * other functions in the same device. The PCI device must be responsive + * to PCI config space in order to use this function. + * + * Returns 0 if the device function can be reset or negative if the + * device doesn't support resetting a single function. + */ +int pci_probe_reset_function(struct pci_dev *dev) +{ + return pci_dev_reset(dev, 1); +} + +/** * pci_reset_function - quiesce and reset a PCI device function * @dev: PCI device to reset * @@ -2504,6 +2520,50 @@ int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) return 0; } +/** + * pci_set_vga_state - set VGA decode state on device and parents if requested + * @dev the PCI device + * @decode - true = enable decoding, false = disable decoding + * @command_bits PCI_COMMAND_IO and/or PCI_COMMAND_MEMORY + * @change_bridge - traverse ancestors and change bridges + */ +int pci_set_vga_state(struct pci_dev *dev, bool decode, + unsigned int command_bits, bool change_bridge) +{ + struct pci_bus *bus; + struct pci_dev *bridge; + u16 cmd; + + WARN_ON(command_bits & ~(PCI_COMMAND_IO|PCI_COMMAND_MEMORY)); + + pci_read_config_word(dev, PCI_COMMAND, &cmd); + if (decode == true) + cmd |= command_bits; + else + cmd &= ~command_bits; + pci_write_config_word(dev, PCI_COMMAND, cmd); + + if (change_bridge == false) + return 0; + + bus = dev->bus; + while (bus) { + bridge = bus->self; + if (bridge) { + pci_read_config_word(bridge, PCI_BRIDGE_CONTROL, + &cmd); + if (decode == true) + cmd |= PCI_BRIDGE_CTL_VGA; + else + cmd &= ~PCI_BRIDGE_CTL_VGA; + pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, + cmd); + } + bus = bus->parent; + } + return 0; +} + #define RESOURCE_ALIGNMENT_PARAM_SIZE COMMAND_LINE_SIZE static char resource_alignment_param[RESOURCE_ALIGNMENT_PARAM_SIZE] = {0}; spinlock_t resource_alignment_lock = SPIN_LOCK_UNLOCKED; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5ff4d25bf0e9..73d9d92715a0 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -16,6 +16,7 @@ extern void pci_cleanup_rom(struct pci_dev *dev); extern int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma); #endif +int pci_probe_reset_function(struct pci_dev *dev); /** * struct pci_platform_pm_ops - Firmware PM callbacks diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3d27c97e0486..f289ca9bf18d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -26,6 +26,13 @@ #endif #define MODULE_PARAM_PREFIX "pcie_aspm." +/* Note: those are not register definitions */ +#define ASPM_STATE_L0S_UP (1) /* Upstream direction L0s state */ +#define ASPM_STATE_L0S_DW (2) /* Downstream direction L0s state */ +#define ASPM_STATE_L1 (4) /* L1 state */ +#define ASPM_STATE_L0S (ASPM_STATE_L0S_UP | ASPM_STATE_L0S_DW) +#define ASPM_STATE_ALL (ASPM_STATE_L0S | ASPM_STATE_L1) + struct aspm_latency { u32 l0s; /* L0s latency (nsec) */ u32 l1; /* L1 latency (nsec) */ @@ -40,17 +47,20 @@ struct pcie_link_state { struct list_head link; /* node in parent's children list */ /* ASPM state */ - u32 aspm_support:2; /* Supported ASPM state */ - u32 aspm_enabled:2; /* Enabled ASPM state */ - u32 aspm_default:2; /* Default ASPM state by BIOS */ + u32 aspm_support:3; /* Supported ASPM state */ + u32 aspm_enabled:3; /* Enabled ASPM state */ + u32 aspm_capable:3; /* Capable ASPM state with latency */ + u32 aspm_default:3; /* Default ASPM state by BIOS */ + u32 aspm_disable:3; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ u32 clkpm_enabled:1; /* Current Clock PM state */ u32 clkpm_default:1; /* Default Clock PM state by BIOS */ - /* Latencies */ - struct aspm_latency latency; /* Exit latency */ + /* Exit latencies */ + struct aspm_latency latency_up; /* Upstream direction exit latency */ + struct aspm_latency latency_dw; /* Downstream direction exit latency */ /* * Endpoint acceptable latencies. A pcie downstream port only * has one slot under it, so at most there are 8 functions. @@ -82,7 +92,7 @@ static int policy_to_aspm_state(struct pcie_link_state *link) return 0; case POLICY_POWERSAVE: /* Enable ASPM L0s/L1 */ - return PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + return ASPM_STATE_ALL; case POLICY_DEFAULT: return link->aspm_default; } @@ -164,18 +174,6 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) link->clkpm_capable = (blacklist) ? 0 : capable; } -static bool pcie_aspm_downstream_has_switch(struct pcie_link_state *link) -{ - struct pci_dev *child; - struct pci_bus *linkbus = link->pdev->subordinate; - - list_for_each_entry(child, &linkbus->devices, bus_list) { - if (child->pcie_type == PCI_EXP_TYPE_UPSTREAM) - return true; - } - return false; -} - /* * pcie_aspm_configure_common_clock: check if the 2 ends of a link * could use common clock. If they are, configure them to use the @@ -288,71 +286,133 @@ static u32 calc_l1_acceptable(u32 encoding) return (1000 << encoding); } -static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, - u32 *l0s, u32 *l1, u32 *enabled) +struct aspm_register_info { + u32 support:2; + u32 enabled:2; + u32 latency_encoding_l0s; + u32 latency_encoding_l1; +}; + +static void pcie_get_aspm_reg(struct pci_dev *pdev, + struct aspm_register_info *info) { int pos; u16 reg16; - u32 reg32, encoding; + u32 reg32; - *l0s = *l1 = *enabled = 0; pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_dword(pdev, pos + PCI_EXP_LNKCAP, ®32); - *state = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; - if (*state != PCIE_LINK_STATE_L0S && - *state != (PCIE_LINK_STATE_L1 | PCIE_LINK_STATE_L0S)) - *state = 0; - if (*state == 0) + info->support = (reg32 & PCI_EXP_LNKCAP_ASPMS) >> 10; + /* 00b and 10b are defined as "Reserved". */ + if (info->support == PCIE_LINK_STATE_L1) + info->support = 0; + info->latency_encoding_l0s = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; + info->latency_encoding_l1 = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; + pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); + info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; +} + +static void pcie_aspm_check_latency(struct pci_dev *endpoint) +{ + u32 latency, l1_switch_latency = 0; + struct aspm_latency *acceptable; + struct pcie_link_state *link; + + /* Device not in D0 doesn't need latency check */ + if ((endpoint->current_state != PCI_D0) && + (endpoint->current_state != PCI_UNKNOWN)) return; - encoding = (reg32 & PCI_EXP_LNKCAP_L0SEL) >> 12; - *l0s = calc_l0s_latency(encoding); - if (*state & PCIE_LINK_STATE_L1) { - encoding = (reg32 & PCI_EXP_LNKCAP_L1EL) >> 15; - *l1 = calc_l1_latency(encoding); + link = endpoint->bus->self->link_state; + acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + + while (link) { + /* Check upstream direction L0s latency */ + if ((link->aspm_capable & ASPM_STATE_L0S_UP) && + (link->latency_up.l0s > acceptable->l0s)) + link->aspm_capable &= ~ASPM_STATE_L0S_UP; + + /* Check downstream direction L0s latency */ + if ((link->aspm_capable & ASPM_STATE_L0S_DW) && + (link->latency_dw.l0s > acceptable->l0s)) + link->aspm_capable &= ~ASPM_STATE_L0S_DW; + /* + * Check L1 latency. + * Every switch on the path to root complex need 1 + * more microsecond for L1. Spec doesn't mention L0s. + */ + latency = max_t(u32, link->latency_up.l1, link->latency_dw.l1); + if ((link->aspm_capable & ASPM_STATE_L1) && + (latency + l1_switch_latency > acceptable->l1)) + link->aspm_capable &= ~ASPM_STATE_L1; + l1_switch_latency += 1000; + + link = link->parent; } - pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); - *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); } static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { - u32 support, l0s, l1, enabled; struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; + struct aspm_register_info upreg, dwreg; if (blacklist) { - /* Set support state to 0, so we will disable ASPM later */ - link->aspm_support = 0; - link->aspm_default = 0; - link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + /* Set enabled/disable so that we will disable ASPM later */ + link->aspm_enabled = ASPM_STATE_ALL; + link->aspm_disable = ASPM_STATE_ALL; return; } /* Configure common clock before checking latencies */ pcie_aspm_configure_common_clock(link); - /* upstream component states */ - pcie_aspm_get_cap_device(parent, &support, &l0s, &l1, &enabled); - link->aspm_support = support; - link->latency.l0s = l0s; - link->latency.l1 = l1; - link->aspm_enabled = enabled; - - /* downstream component states, all functions have the same setting */ + /* Get upstream/downstream components' register state */ + pcie_get_aspm_reg(parent, &upreg); child = list_entry(linkbus->devices.next, struct pci_dev, bus_list); - pcie_aspm_get_cap_device(child, &support, &l0s, &l1, &enabled); - link->aspm_support &= support; - link->latency.l0s = max_t(u32, link->latency.l0s, l0s); - link->latency.l1 = max_t(u32, link->latency.l1, l1); + pcie_get_aspm_reg(child, &dwreg); - if (!link->aspm_support) - return; - - link->aspm_enabled &= link->aspm_support; + /* + * Setup L0s state + * + * Note that we must not enable L0s in either direction on a + * given link unless components on both sides of the link each + * support L0s. + */ + if (dwreg.support & upreg.support & PCIE_LINK_STATE_L0S) + link->aspm_support |= ASPM_STATE_L0S; + if (dwreg.enabled & PCIE_LINK_STATE_L0S) + link->aspm_enabled |= ASPM_STATE_L0S_UP; + if (upreg.enabled & PCIE_LINK_STATE_L0S) + link->aspm_enabled |= ASPM_STATE_L0S_DW; + link->latency_up.l0s = calc_l0s_latency(upreg.latency_encoding_l0s); + link->latency_dw.l0s = calc_l0s_latency(dwreg.latency_encoding_l0s); + + /* Setup L1 state */ + if (upreg.support & dwreg.support & PCIE_LINK_STATE_L1) + link->aspm_support |= ASPM_STATE_L1; + if (upreg.enabled & dwreg.enabled & PCIE_LINK_STATE_L1) + link->aspm_enabled |= ASPM_STATE_L1; + link->latency_up.l1 = calc_l1_latency(upreg.latency_encoding_l1); + link->latency_dw.l1 = calc_l1_latency(dwreg.latency_encoding_l1); + + /* Save default state */ link->aspm_default = link->aspm_enabled; - /* ENDPOINT states*/ + /* Setup initial capable state. Will be updated later */ + link->aspm_capable = link->aspm_support; + /* + * If the downstream component has pci bridge function, don't + * do ASPM for now. + */ + list_for_each_entry(child, &linkbus->devices, bus_list) { + if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { + link->aspm_disable = ASPM_STATE_ALL; + break; + } + } + + /* Get and check endpoint acceptable latencies */ list_for_each_entry(child, &linkbus->devices, bus_list) { int pos; u32 reg32, encoding; @@ -365,109 +425,46 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) pos = pci_find_capability(child, PCI_CAP_ID_EXP); pci_read_config_dword(child, pos + PCI_EXP_DEVCAP, ®32); + /* Calculate endpoint L0s acceptable latency */ encoding = (reg32 & PCI_EXP_DEVCAP_L0S) >> 6; acceptable->l0s = calc_l0s_acceptable(encoding); - if (link->aspm_support & PCIE_LINK_STATE_L1) { - encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; - acceptable->l1 = calc_l1_acceptable(encoding); - } - } -} - -/** - * __pcie_aspm_check_state_one - check latency for endpoint device. - * @endpoint: pointer to the struct pci_dev of endpoint device - * - * TBD: The latency from the endpoint to root complex vary per switch's - * upstream link state above the device. Here we just do a simple check - * which assumes all links above the device can be in L1 state, that - * is we just consider the worst case. If switch's upstream link can't - * be put into L0S/L1, then our check is too strictly. - */ -static u32 __pcie_aspm_check_state_one(struct pci_dev *endpoint, u32 state) -{ - u32 l1_switch_latency = 0; - struct aspm_latency *acceptable; - struct pcie_link_state *link; - - link = endpoint->bus->self->link_state; - state &= link->aspm_support; - acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + /* Calculate endpoint L1 acceptable latency */ + encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; + acceptable->l1 = calc_l1_acceptable(encoding); - while (link && state) { - if ((state & PCIE_LINK_STATE_L0S) && - (link->latency.l0s > acceptable->l0s)) - state &= ~PCIE_LINK_STATE_L0S; - if ((state & PCIE_LINK_STATE_L1) && - (link->latency.l1 + l1_switch_latency > acceptable->l1)) - state &= ~PCIE_LINK_STATE_L1; - link = link->parent; - /* - * Every switch on the path to root complex need 1 - * more microsecond for L1. Spec doesn't mention L0s. - */ - l1_switch_latency += 1000; - } - return state; -} - -static u32 pcie_aspm_check_state(struct pcie_link_state *link, u32 state) -{ - pci_power_t power_state; - struct pci_dev *child; - struct pci_bus *linkbus = link->pdev->subordinate; - - /* If no child, ignore the link */ - if (list_empty(&linkbus->devices)) - return state; - - list_for_each_entry(child, &linkbus->devices, bus_list) { - /* - * If downstream component of a link is pci bridge, we - * disable ASPM for now for the link - */ - if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) - return 0; - - if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT && - child->pcie_type != PCI_EXP_TYPE_LEG_END)) - continue; - /* Device not in D0 doesn't need check latency */ - power_state = child->current_state; - if (power_state == PCI_D1 || power_state == PCI_D2 || - power_state == PCI_D3hot || power_state == PCI_D3cold) - continue; - state = __pcie_aspm_check_state_one(child, state); + pcie_aspm_check_latency(child); } - return state; } -static void __pcie_aspm_config_one_dev(struct pci_dev *pdev, unsigned int state) +static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 val) { u16 reg16; int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); pci_read_config_word(pdev, pos + PCI_EXP_LNKCTL, ®16); reg16 &= ~0x3; - reg16 |= state; + reg16 |= val; pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); } -static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) +static void pcie_config_aspm_link(struct pcie_link_state *link, u32 state) { + u32 upstream = 0, dwstream = 0; struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - /* If no child, disable the link */ - if (list_empty(&linkbus->devices)) - state = 0; - /* - * If the downstream component has pci bridge function, don't - * do ASPM now. - */ - list_for_each_entry(child, &linkbus->devices, bus_list) { - if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) - return; + /* Nothing to do if the link is already in the requested state */ + state &= (link->aspm_capable & ~link->aspm_disable); + if (link->aspm_enabled == state) + return; + /* Convert ASPM state to upstream/downstream ASPM register state */ + if (state & ASPM_STATE_L0S_UP) + dwstream |= PCIE_LINK_STATE_L0S; + if (state & ASPM_STATE_L0S_DW) + upstream |= PCIE_LINK_STATE_L0S; + if (state & ASPM_STATE_L1) { + upstream |= PCIE_LINK_STATE_L1; + dwstream |= PCIE_LINK_STATE_L1; } /* * Spec 2.0 suggests all functions should be configured the @@ -475,67 +472,24 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) * upstream component first and then downstream, and vice * versa for disabling ASPM L1. Spec doesn't mention L0S. */ - if (state & PCIE_LINK_STATE_L1) - __pcie_aspm_config_one_dev(parent, state); - + if (state & ASPM_STATE_L1) + pcie_config_aspm_dev(parent, upstream); list_for_each_entry(child, &linkbus->devices, bus_list) - __pcie_aspm_config_one_dev(child, state); - - if (!(state & PCIE_LINK_STATE_L1)) - __pcie_aspm_config_one_dev(parent, state); + pcie_config_aspm_dev(child, dwstream); + if (!(state & ASPM_STATE_L1)) + pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; } -/* Check the whole hierarchy, and configure each link in the hierarchy */ -static void __pcie_aspm_configure_link_state(struct pcie_link_state *link, - u32 state) +static void pcie_config_aspm_path(struct pcie_link_state *link) { - struct pcie_link_state *leaf, *root = link->root; - - state &= (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); - - /* Check all links who have specific root port link */ - list_for_each_entry(leaf, &link_list, sibling) { - if (!list_empty(&leaf->children) || (leaf->root != root)) - continue; - state = pcie_aspm_check_state(leaf, state); - } - /* Check root port link too in case it hasn't children */ - state = pcie_aspm_check_state(root, state); - if (link->aspm_enabled == state) - return; - /* - * We must change the hierarchy. See comments in - * __pcie_aspm_config_link for the order - **/ - if (state & PCIE_LINK_STATE_L1) { - list_for_each_entry(leaf, &link_list, sibling) { - if (leaf->root == root) - __pcie_aspm_config_link(leaf, state); - } - } else { - list_for_each_entry_reverse(leaf, &link_list, sibling) { - if (leaf->root == root) - __pcie_aspm_config_link(leaf, state); - } + while (link) { + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + link = link->parent; } } -/* - * pcie_aspm_configure_link_state: enable/disable PCI express link state - * @pdev: the root port or switch downstream port - */ -static void pcie_aspm_configure_link_state(struct pcie_link_state *link, - u32 state) -{ - down_read(&pci_bus_sem); - mutex_lock(&aspm_lock); - __pcie_aspm_configure_link_state(link, state); - mutex_unlock(&aspm_lock); - up_read(&pci_bus_sem); -} - static void free_link_state(struct pcie_link_state *link) { link->pdev->link_state = NULL; @@ -570,10 +524,9 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) return 0; } -static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) +static struct pcie_link_state *alloc_pcie_link_state(struct pci_dev *pdev) { struct pcie_link_state *link; - int blacklist = !!pcie_aspm_sanity_check(pdev); link = kzalloc(sizeof(*link), GFP_KERNEL); if (!link) @@ -599,15 +552,7 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) link->root = link->parent->root; list_add(&link->sibling, &link_list); - pdev->link_state = link; - - /* Check ASPM capability */ - pcie_aspm_cap_init(link, blacklist); - - /* Check Clock PM capability */ - pcie_clkpm_cap_init(link, blacklist); - return link; } @@ -618,8 +563,8 @@ static struct pcie_link_state *pcie_aspm_setup_link_state(struct pci_dev *pdev) */ void pcie_aspm_init_link_state(struct pci_dev *pdev) { - u32 state; struct pcie_link_state *link; + int blacklist = !!pcie_aspm_sanity_check(pdev); if (aspm_disabled || !pdev->is_pcie || pdev->link_state) return; @@ -637,47 +582,64 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) goto out; mutex_lock(&aspm_lock); - link = pcie_aspm_setup_link_state(pdev); + link = alloc_pcie_link_state(pdev); if (!link) goto unlock; /* - * Setup initial ASPM state - * - * If link has switch, delay the link config. The leaf link - * initialization will config the whole hierarchy. But we must - * make sure BIOS doesn't set unsupported link state. + * Setup initial ASPM state. Note that we need to configure + * upstream links also because capable state of them can be + * update through pcie_aspm_cap_init(). */ - if (pcie_aspm_downstream_has_switch(link)) { - state = pcie_aspm_check_state(link, link->aspm_default); - __pcie_aspm_config_link(link, state); - } else { - state = policy_to_aspm_state(link); - __pcie_aspm_configure_link_state(link, state); - } + pcie_aspm_cap_init(link, blacklist); + pcie_config_aspm_path(link); /* Setup initial Clock PM state */ - state = (link->clkpm_capable) ? policy_to_clkpm_state(link) : 0; - pcie_set_clkpm(link, state); + pcie_clkpm_cap_init(link, blacklist); + pcie_set_clkpm(link, policy_to_clkpm_state(link)); unlock: mutex_unlock(&aspm_lock); out: up_read(&pci_bus_sem); } +/* Recheck latencies and update aspm_capable for links under the root */ +static void pcie_update_aspm_capable(struct pcie_link_state *root) +{ + struct pcie_link_state *link; + BUG_ON(root->parent); + list_for_each_entry(link, &link_list, sibling) { + if (link->root != root) + continue; + link->aspm_capable = link->aspm_support; + } + list_for_each_entry(link, &link_list, sibling) { + struct pci_dev *child; + struct pci_bus *linkbus = link->pdev->subordinate; + if (link->root != root) + continue; + list_for_each_entry(child, &linkbus->devices, bus_list) { + if ((child->pcie_type != PCI_EXP_TYPE_ENDPOINT) && + (child->pcie_type != PCI_EXP_TYPE_LEG_END)) + continue; + pcie_aspm_check_latency(child); + } + } +} + /* @pdev: the endpoint device */ void pcie_aspm_exit_link_state(struct pci_dev *pdev) { struct pci_dev *parent = pdev->bus->self; - struct pcie_link_state *link_state = parent->link_state; + struct pcie_link_state *link, *root, *parent_link; - if (aspm_disabled || !pdev->is_pcie || !parent || !link_state) + if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state) return; - if (parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT && - parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) return; + down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - /* * All PCIe functions are in one slot, remove one function will remove * the whole slot, so just wait until we are the last function left. @@ -685,13 +647,20 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) if (!list_is_last(&pdev->bus_list, &parent->subordinate->devices)) goto out; + link = parent->link_state; + root = link->root; + parent_link = link->parent; + /* All functions are removed, so just disable ASPM for the link */ - __pcie_aspm_config_one_dev(parent, 0); - list_del(&link_state->sibling); - list_del(&link_state->link); + pcie_config_aspm_link(link, 0); + list_del(&link->sibling); + list_del(&link->link); /* Clock PM is for endpoint device */ + free_link_state(link); - free_link_state(link_state); + /* Recheck latencies and configure upstream links */ + pcie_update_aspm_capable(root); + pcie_config_aspm_path(parent_link); out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -700,18 +669,23 @@ out: /* @pdev: the root port or switch downstream port */ void pcie_aspm_pm_state_change(struct pci_dev *pdev) { - struct pcie_link_state *link_state = pdev->link_state; + struct pcie_link_state *link = pdev->link_state; - if (aspm_disabled || !pdev->is_pcie || !pdev->link_state) + if (aspm_disabled || !pdev->is_pcie || !link) return; - if (pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT && - pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) + if ((pdev->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && + (pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) return; /* - * devices changed PM state, we should recheck if latency meets all - * functions' requirement + * Devices changed PM state, we should recheck if latency + * meets all functions' requirement */ - pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); + down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + pcie_update_aspm_capable(link->root); + pcie_config_aspm_path(link); + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); } /* @@ -721,7 +695,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) void pci_disable_link_state(struct pci_dev *pdev, int state) { struct pci_dev *parent = pdev->bus->self; - struct pcie_link_state *link_state; + struct pcie_link_state *link; if (aspm_disabled || !pdev->is_pcie) return; @@ -733,12 +707,16 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); - link_state = parent->link_state; - link_state->aspm_support &= ~state; - __pcie_aspm_configure_link_state(link_state, link_state->aspm_enabled); + link = parent->link_state; + if (state & PCIE_LINK_STATE_L0S) + link->aspm_disable |= ASPM_STATE_L0S; + if (state & PCIE_LINK_STATE_L1) + link->aspm_disable |= ASPM_STATE_L1; + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + if (state & PCIE_LINK_STATE_CLKPM) { - link_state->clkpm_capable = 0; - pcie_set_clkpm(link_state, 0); + link->clkpm_capable = 0; + pcie_set_clkpm(link, 0); } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -748,7 +726,7 @@ EXPORT_SYMBOL(pci_disable_link_state); static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; - struct pcie_link_state *link_state; + struct pcie_link_state *link; for (i = 0; i < ARRAY_SIZE(policy_str); i++) if (!strncmp(val, policy_str[i], strlen(policy_str[i]))) @@ -761,10 +739,9 @@ static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); aspm_policy = i; - list_for_each_entry(link_state, &link_list, sibling) { - __pcie_aspm_configure_link_state(link_state, - policy_to_aspm_state(link_state)); - pcie_set_clkpm(link_state, policy_to_clkpm_state(link_state)); + list_for_each_entry(link, &link_list, sibling) { + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + pcie_set_clkpm(link, policy_to_clkpm_state(link)); } mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -802,18 +779,28 @@ static ssize_t link_state_store(struct device *dev, size_t n) { struct pci_dev *pdev = to_pci_dev(dev); - int state; + struct pcie_link_state *link, *root = pdev->link_state->root; + u32 val = buf[0] - '0', state = 0; - if (n < 1) + if (n < 1 || val > 3) return -EINVAL; - state = buf[0]-'0'; - if (state >= 0 && state <= 3) { - /* setup link aspm state */ - pcie_aspm_configure_link_state(pdev->link_state, state); - return n; - } - return -EINVAL; + /* Convert requested state to ASPM state */ + if (val & PCIE_LINK_STATE_L0S) + state |= ASPM_STATE_L0S; + if (val & PCIE_LINK_STATE_L1) + state |= ASPM_STATE_L1; + + down_read(&pci_bus_sem); + mutex_lock(&aspm_lock); + list_for_each_entry(link, &link_list, sibling) { + if (link->root != root) + continue; + pcie_config_aspm_link(link, state); + } + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); + return n; } static ssize_t clk_ctl_show(struct device *dev, diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 40e75f6a5056..ab52840f4753 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -235,7 +235,10 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->start = l64; res->end = l64 + sz64; dev_printk(KERN_DEBUG, &dev->dev, - "reg %x 64bit mmio: %pR\n", pos, res); + "reg %x %s: %pR\n", pos, + (res->flags & IORESOURCE_PREFETCH) ? + "64bit mmio pref" : "64bit mmio", + res); } res->flags |= IORESOURCE_MEM_64; @@ -249,7 +252,9 @@ int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, res->end = l + sz; dev_printk(KERN_DEBUG, &dev->dev, "reg %x %s: %pR\n", pos, - (res->flags & IORESOURCE_IO) ? "io port" : "32bit mmio", + (res->flags & IORESOURCE_IO) ? "io port" : + ((res->flags & IORESOURCE_PREFETCH) ? + "32bit mmio pref" : "32bit mmio"), res); } @@ -1061,8 +1066,7 @@ int pci_scan_slot(struct pci_bus *bus, int devfn) if (dev && !dev->is_added) /* new device? */ nr++; - if ((dev && dev->multifunction) || - (!dev && pcibios_scan_all_fns(bus, devfn))) { + if (dev && dev->multifunction) { for (fn = 1; fn < 8; fn++) { dev = pci_scan_single_device(bus, devfn + fn); if (dev) { diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 06b965623962..10731373d00e 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1201,6 +1201,7 @@ static void __init asus_hides_smbus_hostbridge(struct pci_dev *dev) switch(dev->subsystem_device) { case 0x00b8: /* Compaq Evo D510 CMT */ case 0x00b9: /* Compaq Evo D510 SFF */ + case 0x00ba: /* Compaq Evo D510 USDT */ /* Motherboard doesn't have Host bridge * subvendor/subdevice IDs and on-board VGA * controller is disabled if an AGP card is @@ -2382,8 +2383,10 @@ static void __devinit nv_msi_ht_cap_quirk_leaf(struct pci_dev *dev) } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk_leaf); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_ANY_ID, nv_msi_ht_cap_quirk_leaf); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AL, PCI_ANY_ID, nv_msi_ht_cap_quirk_all); static void __devinit quirk_msi_intx_disable_bug(struct pci_dev *dev) { @@ -2492,6 +2495,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e6, quirk_i82576_sriov); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e7, quirk_i82576_sriov); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x10e8, quirk_i82576_sriov); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150a, quirk_i82576_sriov); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, 0x150d, quirk_i82576_sriov); #endif /* CONFIG_PCI_IOV */ diff --git a/drivers/pci/search.c b/drivers/pci/search.c index e8cb5051c311..ec415352d9ba 100644 --- a/drivers/pci/search.c +++ b/drivers/pci/search.c @@ -113,37 +113,6 @@ pci_find_next_bus(const struct pci_bus *from) return b; } -#ifdef CONFIG_PCI_LEGACY -/** - * pci_find_device - begin or continue searching for a PCI device by vendor/device id - * @vendor: PCI vendor id to match, or %PCI_ANY_ID to match all vendor ids - * @device: PCI device id to match, or %PCI_ANY_ID to match all device ids - * @from: Previous PCI device found in search, or %NULL for new search. - * - * Iterates through the list of known PCI devices. If a PCI device is found - * with a matching @vendor and @device, a pointer to its device structure is - * returned. Otherwise, %NULL is returned. - * A new search is initiated by passing %NULL as the @from argument. - * Otherwise if @from is not %NULL, searches continue from next device - * on the global list. - * - * NOTE: Do not use this function any more; use pci_get_device() instead, as - * the PCI device returned by this function can disappear at any moment in - * time. - */ -struct pci_dev *pci_find_device(unsigned int vendor, unsigned int device, - struct pci_dev *from) -{ - struct pci_dev *pdev; - - pci_dev_get(from); - pdev = pci_get_subsys(vendor, device, PCI_ANY_ID, PCI_ANY_ID, from); - pci_dev_put(pdev); - return pdev; -} -EXPORT_SYMBOL(pci_find_device); -#endif /* CONFIG_PCI_LEGACY */ - /** * pci_get_slot - locate PCI device for a given PCI slot * @bus: PCI bus on which desired PCI device resides diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 88cdd1a937d6..706f82d8111f 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -119,6 +119,7 @@ int pci_claim_resource(struct pci_dev *dev, int resource) return err; } +EXPORT_SYMBOL(pci_claim_resource); #ifdef CONFIG_PCI_QUIRKS void pci_disable_bridge_window(struct pci_dev *dev) |