From a59b50e995465911ba580df0bd10cf64aa81fc43 Mon Sep 17 00:00:00 2001 From: Joseph Cihula Date: Tue, 30 Jun 2009 19:31:10 -0700 Subject: intel_txt: Force IOMMU on for Intel TXT launch The tboot module will DMA protect all of memory in order to ensure the that kernel will be able to initialize without compromise (from DMA). Consequently, the kernel must enable Intel Virtualization Technology for Directed I/O (VT-d or Intel IOMMU) in order to replace this broad protection with the appropriate page-granular protection. Otherwise DMA devices will be unable to read or write from memory and the kernel will eventually panic. Because runtime IOMMU support is configurable by command line options, this patch will force it to be enabled regardless of the options specified, and will log a message if it was required to force it on. dmar.c | 7 +++++++ intel-iommu.c | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) Signed-off-by: Joseph Cihula Signed-off-by: Shane Wang Cc: David Woodhouse Signed-off-by: H. Peter Anvin --- drivers/pci/dmar.c | 7 +++++++ drivers/pci/intel-iommu.c | 17 +++++++++++++++-- 2 files changed, 22 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 7b287cb38b7..0cbc5fd26c3 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -33,6 +33,7 @@ #include #include #include +#include #undef PREFIX #define PREFIX "DMAR:" @@ -413,6 +414,12 @@ parse_dmar_table(void) */ dmar_table_detect(); + /* + * ACPI tables may not be DMA protected by tboot, so use DMAR copy + * SINIT saved in SinitMleData in TXT heap (which is DMA protected) + */ + dmar_tbl = tboot_get_dmar_table(dmar_tbl); + dmar = (struct acpi_table_dmar *)dmar_tbl; if (!dmar) return -ENODEV; diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index ebc9b8dca88..2dc72a6d741 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "pci.h" @@ -3183,12 +3184,22 @@ static int __init init_iommu_sysfs(void) int __init intel_iommu_init(void) { int ret = 0; + int force_on = 0; - if (dmar_table_init()) + /* VT-d is required for a TXT/tboot launch, so enforce that */ + force_on = tboot_force_iommu(); + + if (dmar_table_init()) { + if (force_on) + panic("tboot: Failed to initialize DMAR table\n"); return -ENODEV; + } - if (dmar_dev_scope_init()) + if (dmar_dev_scope_init()) { + if (force_on) + panic("tboot: Failed to initialize DMAR device scope\n"); return -ENODEV; + } /* * Check the need for DMA-remapping initialization now. @@ -3204,6 +3215,8 @@ int __init intel_iommu_init(void) ret = init_dmars(); if (ret) { + if (force_on) + panic("tboot: Failed to initialize DMARs\n"); printk(KERN_ERR "IOMMU: dmar init failed\n"); put_iova_domain(&reserved_iova_list); iommu_exit_mempool(); -- cgit v1.2.3 From 8150f32b90f630ad3e460f026ce338cb81685bc9 Mon Sep 17 00:00:00 2001 From: Dmitry Torokhov Date: Fri, 24 Jul 2009 22:11:32 -0700 Subject: Driver Core: Make PM operations a const pointer They are not supposed to be modified by drivers, so make them const. Signed-off-by: Dmitry Torokhov Acked-by: Greg Kroah-Hartman Signed-off-by: Rafael J. Wysocki --- drivers/pci/pci-driver.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index d76c4c85367..0c2ea44ae5e 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -575,7 +575,7 @@ static void pci_pm_complete(struct device *dev) static int pci_pm_suspend(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); @@ -613,7 +613,7 @@ static int pci_pm_suspend(struct device *dev) static int pci_pm_suspend_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); @@ -672,7 +672,7 @@ static int pci_pm_resume_noirq(struct device *dev) static int pci_pm_resume(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -711,7 +711,7 @@ static int pci_pm_resume(struct device *dev) static int pci_pm_freeze(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); @@ -780,7 +780,7 @@ static int pci_pm_thaw_noirq(struct device *dev) static int pci_pm_thaw(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; if (pci_has_legacy_pm_support(pci_dev)) @@ -799,7 +799,7 @@ static int pci_pm_thaw(struct device *dev) static int pci_pm_poweroff(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); @@ -872,7 +872,7 @@ static int pci_pm_restore_noirq(struct device *dev) static int pci_pm_restore(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); - struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; + const struct dev_pm_ops *pm = dev->driver ? dev->driver->pm : NULL; int error = 0; /* @@ -910,7 +910,7 @@ static int pci_pm_restore(struct device *dev) #endif /* !CONFIG_HIBERNATION */ -struct dev_pm_ops pci_dev_pm_ops = { +const struct dev_pm_ops pci_dev_pm_ops = { .prepare = pci_pm_prepare, .complete = pci_pm_complete, .suspend = pci_pm_suspend, -- cgit v1.2.3 From 70590ea75b7f9ef4846b0b0f4400e8338dbcc7eb Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Wed, 26 Aug 2009 16:21:54 -0700 Subject: pci/intr_remapping: Allocate irq_iommu on node make it use the node from irq_desc. Signed-off-by: Yinghai Lu Acked-by: Jesse Barnes Cc: Andrew Morton LKML-Reference: <4A95C392.5050903@kernel.org> Signed-off-by: Ingo Molnar --- drivers/pci/intr_remapping.c | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/intr_remapping.c b/drivers/pci/intr_remapping.c index 4f5b8712931..44803644ca0 100644 --- a/drivers/pci/intr_remapping.c +++ b/drivers/pci/intr_remapping.c @@ -55,15 +55,12 @@ static struct irq_2_iommu *irq_2_iommu(unsigned int irq) return desc->irq_2_iommu; } -static struct irq_2_iommu *irq_2_iommu_alloc_node(unsigned int irq, int node) +static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq) { struct irq_desc *desc; struct irq_2_iommu *irq_iommu; - /* - * alloc irq desc if not allocated already. - */ - desc = irq_to_desc_alloc_node(irq, node); + desc = irq_to_desc(irq); if (!desc) { printk(KERN_INFO "can not get irq_desc for %d\n", irq); return NULL; @@ -72,16 +69,11 @@ static struct irq_2_iommu *irq_2_iommu_alloc_node(unsigned int irq, int node) irq_iommu = desc->irq_2_iommu; if (!irq_iommu) - desc->irq_2_iommu = get_one_free_irq_2_iommu(node); + desc->irq_2_iommu = get_one_free_irq_2_iommu(irq_node(irq)); return desc->irq_2_iommu; } -static struct irq_2_iommu *irq_2_iommu_alloc(unsigned int irq) -{ - return irq_2_iommu_alloc_node(irq, cpu_to_node(boot_cpu_id)); -} - #else /* !CONFIG_SPARSE_IRQ */ static struct irq_2_iommu irq_2_iommuX[NR_IRQS]; -- cgit v1.2.3 From 6faf17f6f1ffc586d16efc2f9fa2083a7785ee74 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Fri, 28 Aug 2009 13:00:06 -0700 Subject: PCI SR-IOV: correct broken resource alignment calculations An SR-IOV capable device includes an SR-IOV PCIe capability which describes the Virtual Function (VF) BAR requirements. A typical SR-IOV device can support multiple VFs whose BARs must be in a contiguous region, effectively an array of VF BARs. The BAR reports the size requirement for a single VF. We calculate the full range needed by simply multiplying the VF BAR size with the number of possible VFs and create a resource spanning the full range. This all seems sane enough except it artificially inflates the alignment requirement for the VF BAR. The VF BAR need only be aligned to the size of a single BAR not the contiguous range of VF BARs. This can cause us to fail to allocate resources for the BAR despite the fact that we actually have enough space. This patch adds a thin PCI specific layer over the generic resource_alignment() function which is aware of the special nature of VF BARs and does sorting and allocation based on the smaller alignment requirement. I recognize that while resource_alignment is generic, it's basically a PCI helper. An alternative to this patch is to add PCI VF BAR specific information to struct resource. I opted for the extra layer rather than adding such PCI specific information to struct resource. This does have the slight downside that we don't cache the BAR size and re-read for each alignment query (happens a small handful of times during boot for each VF BAR). Signed-off-by: Chris Wright Cc: Ivan Kokshaysky Cc: Linus Torvalds Cc: Matthew Wilcox Cc: Yu Zhao Cc: stable@kernel.org Signed-off-by: Jesse Barnes --- drivers/pci/iov.c | 23 +++++++++++++++++++++++ drivers/pci/pci.h | 13 +++++++++++++ drivers/pci/setup-bus.c | 4 ++-- drivers/pci/setup-res.c | 8 ++++---- 4 files changed, 42 insertions(+), 6 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index e3a87210e94..e03fe98f061 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -597,6 +597,29 @@ int pci_iov_resource_bar(struct pci_dev *dev, int resno, 4 * (resno - PCI_IOV_RESOURCES); } +/** + * pci_sriov_resource_alignment - get resource alignment for VF BAR + * @dev: the PCI device + * @resno: the resource number + * + * Returns the alignment of the VF BAR found in the SR-IOV capability. + * This is not the same as the resource size which is defined as + * the VF BAR size multiplied by the number of VFs. The alignment + * is just the VF BAR size. + */ +int pci_sriov_resource_alignment(struct pci_dev *dev, int resno) +{ + struct resource tmp; + enum pci_bar_type type; + int reg = pci_iov_resource_bar(dev, resno, &type); + + if (!reg) + return 0; + + __pci_read_base(dev, type, &tmp, reg); + return resource_alignment(&tmp); +} + /** * pci_restore_iov_state - restore the state of the IOV capability * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index f73bcbedf37..5ff4d25bf0e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -243,6 +243,7 @@ extern int pci_iov_init(struct pci_dev *dev); extern void pci_iov_release(struct pci_dev *dev); extern int pci_iov_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); +extern int pci_sriov_resource_alignment(struct pci_dev *dev, int resno); extern void pci_restore_iov_state(struct pci_dev *dev); extern int pci_iov_bus_range(struct pci_bus *bus); @@ -298,4 +299,16 @@ static inline int pci_ats_enabled(struct pci_dev *dev) } #endif /* CONFIG_PCI_IOV */ +static inline int pci_resource_alignment(struct pci_dev *dev, + struct resource *res) +{ +#ifdef CONFIG_PCI_IOV + int resno = res - dev->resource; + + if (resno >= PCI_IOV_RESOURCES && resno <= PCI_IOV_RESOURCE_END) + return pci_sriov_resource_alignment(dev, resno); +#endif + return resource_alignment(res); +} + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index b636e245445..7c443b4583a 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -25,7 +25,7 @@ #include #include #include - +#include "pci.h" static void pbus_assign_resources_sorted(const struct pci_bus *bus) { @@ -384,7 +384,7 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long continue; r_size = resource_size(r); /* For bridges size != alignment */ - align = resource_alignment(r); + align = pci_resource_alignment(dev, r); order = __ffs(align) - 20; if (order > 11) { dev_warn(&dev->dev, "BAR %d bad alignment %llx: " diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 1898c7b4790..88cdd1a937d 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -144,7 +144,7 @@ static int __pci_assign_resource(struct pci_bus *bus, struct pci_dev *dev, size = resource_size(res); min = (res->flags & IORESOURCE_IO) ? PCIBIOS_MIN_IO : PCIBIOS_MIN_MEM; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); /* First, try exact prefetching match.. */ ret = pci_bus_alloc_resource(bus, res, size, align, min, @@ -178,7 +178,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) struct pci_bus *bus; int ret; - align = resource_alignment(res); + align = pci_resource_alignment(dev, res); if (!align) { dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " "alignment) %pR flags %#lx\n", @@ -259,7 +259,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) if (!(r->flags) || r->parent) continue; - r_align = resource_alignment(r); + r_align = pci_resource_alignment(dev, r); if (!r_align) { dev_warn(&dev->dev, "BAR %d: bogus alignment " "%pR flags %#lx\n", @@ -271,7 +271,7 @@ void pdev_sort_resources(struct pci_dev *dev, struct resource_list *head) struct resource_list *ln = list->next; if (ln) - align = resource_alignment(ln->res); + align = pci_resource_alignment(ln->dev, ln->res); if (r_align > align) { tmp = kmalloc(sizeof(*tmp), GFP_KERNEL); -- cgit v1.2.3 From 69575d388603365f2afbf4166df93152df59b165 Mon Sep 17 00:00:00 2001 From: Shane Wang Date: Tue, 1 Sep 2009 18:25:07 -0700 Subject: x86, intel_txt: clean up the impact on generic code, unbreak non-x86 Move tboot.h from asm to linux to fix the build errors of intel_txt patch on non-X86 platforms. Remove the tboot code from generic code init/main.c and kernel/cpu.c. Signed-off-by: Shane Wang Signed-off-by: H. Peter Anvin --- drivers/pci/dmar.c | 2 +- drivers/pci/intel-iommu.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/dmar.c b/drivers/pci/dmar.c index 0cbc5fd26c3..ab99783dcce 100644 --- a/drivers/pci/dmar.c +++ b/drivers/pci/dmar.c @@ -33,7 +33,7 @@ #include #include #include -#include +#include #undef PREFIX #define PREFIX "DMAR:" diff --git a/drivers/pci/intel-iommu.c b/drivers/pci/intel-iommu.c index 2dc72a6d741..833509b5352 100644 --- a/drivers/pci/intel-iommu.c +++ b/drivers/pci/intel-iommu.c @@ -37,8 +37,8 @@ #include #include #include +#include #include -#include #include #include "pci.h" -- cgit v1.2.3 From a7db50405216610c8a0d62b8b400180b6f366733 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Mon, 22 Jun 2009 08:08:07 -0600 Subject: PCI: remove pcibios_scan_all_fns() This was #define'd as 0 on all platforms, so let's get rid of it. This change makes pci_scan_slot() slightly easier to read. Cc: Yoshinori Sato Cc: Tony Luck Cc: David Howells Cc: "David S. Miller" Cc: Jeff Dike Cc: Ingo Molnar Cc: Ivan Kokshaysky Reviewed-by: Matthew Wilcox Acked-by: Russell King Acked-by: Ralf Baechle Acked-by: Kyle McMartin Acked-by: Benjamin Herrenschmidt Acked-by: Paul Mundt Acked-by: Arnd Bergmann Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 40e75f6a505..b9d4e95aafb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1061,8 +1061,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) { -- cgit v1.2.3 From eaa959df299157e2640fcb3321537501b6afd9e6 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Tue, 30 Jun 2009 21:45:44 -0700 Subject: PCI: export pci_claim_resource for driver use yenta needs this for example. Acked-by: Matthew Wilcox Reported-by: Stephen Rothwell Signed-off-by: Jesse Barnes --- drivers/pci/setup-res.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 88cdd1a937d..706f82d8111 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) -- cgit v1.2.3 From 5228a828ee044834d78abdf25306bf46b19dcc4d Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Thu, 23 Jul 2009 17:03:05 -0600 Subject: PCI Hotplug: acpiphp: get pci_bus from acpi handle correctly We cannot simply call acpi_get_pci_dev() on any random ACPI handle and hope that it works, because a PCI root bridge may not have an associated struct pci_dev. This is allowed per the PCI specification, and is referred to as a non-materialized bridge. So, depending on the type of PCI bridge that the handle points to, use the appropriate interface to return the struct pci_bus correctly. Reviewed-by: Bjorn Helgaas Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 0cb0f830a99..2e5f25969e1 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; } -- cgit v1.2.3 From 711d57796f5ce2d02d6e62c9034afbb16aedda31 Mon Sep 17 00:00:00 2001 From: "Michael S. Tsirkin" Date: Mon, 27 Jul 2009 23:37:48 +0300 Subject: PCI: expose function reset capability in sysfs Some devices allow an individual function to be reset without affecting other functions in the same device: that's what pci_reset_function does. For devices that have this support, expose reset attribite in sysfs. This is useful e.g. for virtualization, where a qemu userspace process wants to reset the device when the guest is reset, to emulate machine reboot as closely as possible. Acked-by: Greg Kroah-Hartman Signed-off-by: Michael S. Tsirkin Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 37 +++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 16 ++++++++++++++++ drivers/pci/pci.h | 1 + 3 files changed, 54 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 85ebd02a64a..0f6382f090e 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 7b70312181d..7d55039ffa0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2261,6 +2261,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 diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 5ff4d25bf0e..73d9d92715a 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 -- cgit v1.2.3 From 6b5096e4d4496e185cd1ada5d1b8e1d941c805ed Mon Sep 17 00:00:00 2001 From: Jean Delvare Date: Tue, 28 Jul 2009 11:49:19 +0200 Subject: PCI: Unhide the SMBus on the Compaq Evo D510 USDT One more form factor for Compaq Evo D510, which needs the same quirk as the other form factors. Apparently there's no hardware monitoring chip on that one, but SPD EEPROMs, so it's still worth unhiding the SMBus. Signed-off-by: Jean Delvare Tested-by: Nuzhna Pomoshch Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 06b96562396..8e22aefe3f3 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 -- cgit v1.2.3 From e8b553bf4b19c6a6d72f5eac3a17c43ac8e68390 Mon Sep 17 00:00:00 2001 From: Andi Kleen Date: Fri, 24 Jul 2009 11:41:15 +0200 Subject: PCI: disable pci_find_device warnings when deprecated pci functions are enabled Shut off the long standing linux/drivers/pci/search.c:144: warning: 'pci_find_device' is deprecated (declared at linux/drivers/pci/search.c:136) linux/drivers/pci/search.c:144: warning: 'pci_find_device' is deprecated (declared at linux/drivers/pci/search.c:136) warnings that appear on every build when CONFIG_PCI_LEGACY is enabled. gcc warns for the use in EXPORT_SYMBOL I moved these to a separate file and disabled the warning in the Makefile for that file. Signed-off-by: Andi Kleen Signed-off-by: Jesse Barnes --- drivers/pci/Makefile | 3 +++ drivers/pci/legacy.c | 34 ++++++++++++++++++++++++++++++++++ drivers/pci/search.c | 31 ------------------------------- 3 files changed, 37 insertions(+), 31 deletions(-) create mode 100644 drivers/pci/legacy.c (limited to 'drivers/pci') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index 1ebd6b4c743..4a7f11d8f43 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/legacy.c b/drivers/pci/legacy.c new file mode 100644 index 00000000000..871f65c1593 --- /dev/null +++ b/drivers/pci/legacy.c @@ -0,0 +1,34 @@ +#include +#include +#include +#include +#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/search.c b/drivers/pci/search.c index e8cb5051c31..ec415352d9b 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 -- cgit v1.2.3 From 6dab62ee5a3bf4f71b8320c09db2e6022a19f40e Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 21 Jul 2009 16:08:43 -0700 Subject: PCI: apply nv_msi_ht_cap_quirk on resume too http://bugzilla.kernel.org/show_bug.cgi?id=12542 reports that with the quirk not applied on resume, msi stops working after resuming and mcp78s ahci fails due to IRQ mis-delivery. Apply it on resume too. Signed-off-by: Tejun Heo Cc: Peer Chen Cc: Tj Reported-by: Nicolas Derive Cc: Greg KH Cc: Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 8e22aefe3f3..28d592de09f 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2383,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) { -- cgit v1.2.3 From d0b8cbed648334924728642eea879878bc930b33 Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Fri, 7 Aug 2009 03:53:34 -0700 Subject: PCI: print out pref if mmio is prefetchable We already print it out for pci bridges, so also print it out for pci devices. Signed-off-by: Yinghai Lu Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index b9d4e95aafb..ab52840f475 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); } -- cgit v1.2.3 From c901851fddb82529ddcd443d0778b1dee1386a14 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:31:27 +0900 Subject: PCI MSI: Remove attribute check from pci_disable_msi() The msi_list never have MSI-X's msi_desc while MSI is enabled, and also it never have MSI's msi_desc while MSI-X is enabled. This patch remove check for MSI-X entry from the pci_disable_msi(), referring that pci_disable_msix() does not have any check for MSI entry. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index d986afb7032..a7b72f633d0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -640,17 +640,10 @@ void pci_msi_shutdown(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); } EXPORT_SYMBOL(pci_disable_msi); @@ -774,13 +767,13 @@ 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) { if (!pci_msi_enable || !dev || !dev->msix_enabled) return; pci_msix_shutdown(dev); - msix_free_all_irqs(dev); } EXPORT_SYMBOL(pci_disable_msix); -- cgit v1.2.3 From 9cc8d54815bd5b7c4b516e6be92f036bbcdd8bad Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:32:04 +0900 Subject: PCI MSI: Use list_first_entry() use list_first_entry() instead of list_entry(). Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a7b72f633d0..bdd2ca9df41 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -324,7 +324,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); -- cgit v1.2.3 From f56e4481328071d293306a55a951d83639d8d529 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:32:51 +0900 Subject: PCI MSI: Unify msi_free_irqs() and msix_free_all_irqs() Unify msi_free_irqs() and msix_free_all_irqs(), and rename it to a common void function free_msi_irqs(). And relocate the common function to where the prototype is located now. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 74 +++++++++++++++++++++++-------------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index bdd2ca9df41..a7f2a014046 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -272,7 +272,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) { @@ -396,7 +419,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; } @@ -454,7 +477,7 @@ 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; } @@ -486,7 +509,7 @@ static int msix_capability_init(struct pci_dev *dev, } if (ret) { - msi_free_irqs(dev); + free_msi_irqs(dev); return ret; } @@ -644,37 +667,10 @@ void pci_disable_msi(struct pci_dev* dev) return; pci_msi_shutdown(dev); - 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 @@ -745,11 +741,6 @@ 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) { struct msi_desc *entry; @@ -774,7 +765,7 @@ void pci_disable_msix(struct pci_dev* dev) return; pci_msix_shutdown(dev); - msix_free_all_irqs(dev); + free_msi_irqs(dev); } EXPORT_SYMBOL(pci_disable_msix); @@ -792,11 +783,8 @@ void msi_remove_pci_irq_vectors(struct pci_dev* dev) if (!pci_msi_enable || !dev) return; - if (dev->msi_enabled) - msi_free_irqs(dev); - - 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) -- cgit v1.2.3 From 583871d436bea48cc2204cee0ec8eb7025e03db6 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:33:39 +0900 Subject: PCI MSI: Relocate error path in init_msix_capability() Move it from the middle of the function to the end. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 40 ++++++++++++++++++++++------------------ 1 file changed, 22 insertions(+), 18 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index a7f2a014046..7085d665db0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -494,24 +494,8 @@ static int msix_capability_init(struct pci_dev *dev, } 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++; - } - } - - if (avail != 0) - ret = avail; - } - - if (ret) { - free_msi_irqs(dev); - return ret; - } + if (ret) + goto error; /* * Some devices require MSI-X to be enabled before we can touch the @@ -540,6 +524,26 @@ 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. + */ + 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; } /** -- cgit v1.2.3 From 5a05a9d819a328b3aae750237909aa2097adbeec Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:34:34 +0900 Subject: PCI MSI: MSI-X cleanup, msix_map_region() Cleanup based on the prototype from Matthew Milcox. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 7085d665db0..b0cc82123cc 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -432,6 +432,21 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) return 0; } +static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, + unsigned nr_entries) +{ + unsigned long phys_addr; + u32 table_offset; + u8 bir; + + 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; + + return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); +} + /** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function @@ -446,11 +461,8 @@ static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { struct msi_desc *entry; - int pos, i, j, nr_entries, ret; - unsigned long phys_addr; - u32 table_offset; - u16 control; - u8 bir; + int pos, i, j, ret; + u16 control; void __iomem *base; pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); @@ -461,14 +473,8 @@ static int msix_capability_init(struct pci_dev *dev, 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); - 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) + base = msix_map_region(dev, pos, multi_msix_capable(control)); + if (!base) return -ENOMEM; for (i = 0; i < nvec; i++) { -- cgit v1.2.3 From 75cb3426878d479f792c751a95f5c75f27b13a2f Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:35:10 +0900 Subject: PCI MSI: MSI-X cleanup, msix_program_entries() Cleanup based on the prototype from Matthew Milcox. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 29 +++++++++++++++++++---------- 1 file changed, 19 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index b0cc82123cc..ce08d0bffe0 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -447,6 +447,24 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, return ioremap_nocache(phys_addr, nr_entries * PCI_MSIX_ENTRY_SIZE); } +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++; + } +} + /** * msix_capability_init - configure device's MSI-X capability * @dev: pointer to the pci_dev data structure of MSI-X device function @@ -511,16 +529,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); -- cgit v1.2.3 From d9d7070e6117651ecc8fa0ea60b1ff5b68d4db4f Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Thu, 6 Aug 2009 11:35:48 +0900 Subject: PCI MSI: MSI-X cleanup, msix_setup_entries() Cleanup based on the prototype from Matthew Milcox. Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 59 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 36 insertions(+), 23 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index ce08d0bffe0..14159cf7d38 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -447,6 +447,37 @@ static void __iomem *msix_map_region(struct pci_dev *dev, unsigned pos, 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); + if (!entry) { + if (!i) + iounmap(base); + else + free_msi_irqs(dev); + /* No enough memory. Don't try again */ + return -ENOMEM; + } + + 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); + } + + return 0; +} + static void msix_program_entries(struct pci_dev *dev, struct msix_entry *entries) { @@ -478,8 +509,7 @@ static void msix_program_entries(struct pci_dev *dev, static int msix_capability_init(struct pci_dev *dev, struct msix_entry *entries, int nvec) { - struct msi_desc *entry; - int pos, i, j, ret; + int pos, ret; u16 control; void __iomem *base; @@ -495,27 +525,9 @@ static int msix_capability_init(struct pci_dev *dev, if (!base) return -ENOMEM; - for (i = 0; i < nvec; i++) { - entry = alloc_msi_entry(dev); - if (!entry) { - if (!i) - iounmap(base); - else - 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; - - list_add_tail(&entry->list, &dev->msi_list); - } + 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) @@ -546,6 +558,7 @@ error: * 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) { -- cgit v1.2.3 From 500559a92dd36af7cee95ed2f5b7722fb95a82e7 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 10 Aug 2009 10:14:15 +0900 Subject: PCI MSI: Style cleanups Cleanups (nearly based on checkpatch). Before: total: 11 errors, 2 warnings, 0 checks, 842 lines checked After: total: 0 errors, 0 warnings, 0 checks, 842 lines checked v2: fix it's/its mistakes in comment Reviewed-by: Matthew Wilcox Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 14159cf7d38..f9cf3173b23 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -16,9 +16,8 @@ #include #include #include - -#include -#include +#include +#include #include "pci.h" #include "msi.h" @@ -390,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); @@ -399,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 */ @@ -513,7 +512,7 @@ static int msix_capability_init(struct pci_dev *dev, u16 control; void __iomem *base; - pos = pci_find_capability(dev, PCI_CAP_ID_MSIX); + 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 */ @@ -584,7 +583,7 @@ error: * 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; @@ -601,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. @@ -693,7 +693,7 @@ 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) { if (!pci_msi_enable || !dev || !dev->msi_enabled) return; @@ -735,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) @@ -763,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; @@ -773,7 +773,7 @@ int pci_enable_msix(struct pci_dev* dev, struct msix_entry *entries, int nvec) } EXPORT_SYMBOL(pci_enable_msix); -void pci_msix_shutdown(struct pci_dev* dev) +void pci_msix_shutdown(struct pci_dev *dev) { struct msi_desc *entry; @@ -791,7 +791,7 @@ void pci_msix_shutdown(struct pci_dev* dev) 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; @@ -810,10 +810,10 @@ 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; + return; if (dev->msi_enabled || dev->msix_enabled) free_msi_irqs(dev); -- cgit v1.2.3 From deb2d2ecd43dfc51efe71eed7128fda514da96c6 Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Tue, 11 Aug 2009 15:52:06 +1000 Subject: PCI/GPU: implement VGA arbitration on Linux Background: Graphic devices are accessed through ranges in I/O or memory space. While most modern devices allow relocation of such ranges, some "Legacy" VGA devices implemented on PCI will typically have the same "hard-decoded" addresses as they did on ISA. For more details see "PCI Bus Binding to IEEE Std 1275-1994 Standard for Boot (Initialization Configuration) Firmware Revision 2.1" Section 7, Legacy Devices. The Resource Access Control (RAC) module inside the X server currently does the task of arbitration when more than one legacy device co-exists on the same machine. But the problem happens when these devices are trying to be accessed by different userspace clients (e.g. two server in parallel). Their address assignments conflict. Therefore an arbitration scheme _outside_ of the X server is needed to control the sharing of these resources. This document introduces the operation of the VGA arbiter implemented for Linux kernel. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Tiago Vignatti Signed-off-by: Dave Airlie Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 44 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7d55039ffa0..bd993351db4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2520,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; -- cgit v1.2.3 From 6f1186be4feb3364d3a52cbea81e43e4d5296196 Mon Sep 17 00:00:00 2001 From: Alexander Duyck Date: Thu, 13 Aug 2009 16:57:49 -0700 Subject: PCI quirk: update 82576 device ids in SR-IOV quirks list This patch adds the most recent additions to the list of 82576 device IDs to the list of devices needing the SR-IOV quirk. Signed-off-by: Alexander Duyck Signed-off-by: Jeff Kirsher Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 28d592de09f..10731373d00 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2495,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 */ -- cgit v1.2.3 From b127bd55d9cd9d5b40278b30645669d6d46933bc Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 10:57:31 +0900 Subject: PCI ASPM: do not clear enabled field by support field We must not clear bits in 'aspm_enabled' using 'aspm_support', or 'aspm_enabled' and 'aspm_default' might be different from the actual state. In addtion, 'aspm_default' should be intialized even if 'aspm_support' is 0. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 3d27c97e048..9759714f77e 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -346,12 +346,12 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) link->latency.l0s = max_t(u32, link->latency.l0s, l0s); link->latency.l1 = max_t(u32, link->latency.l1, l1); + /* Save default state */ + link->aspm_default = link->aspm_enabled; + if (!link->aspm_support) return; - link->aspm_enabled &= link->aspm_support; - link->aspm_default = link->aspm_enabled; - /* ENDPOINT states*/ list_for_each_entry(child, &linkbus->devices, bus_list) { int pos; -- cgit v1.2.3 From 8a339e7321f10dc2f28928ffadb69b6c7c2d5c3b Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 10:58:09 +0900 Subject: PCI ASPM: remove redundant list check Remove the following check in __pcie_aspm_config_link() because it nerver be true. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 9759714f77e..638881b00f6 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -458,9 +458,6 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) 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. -- cgit v1.2.3 From fc87e919c0ce8e213edf2ffca17f384f059873d3 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 10:58:46 +0900 Subject: PCI ASPM: fix possible null pointer dereference Fix possible NULL dereference in pcie_aspm_exit_link_state(). This patch also cleanup some code. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 638881b00f6..0800c514682 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -665,16 +665,16 @@ out: 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; - 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) + 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. @@ -682,13 +682,14 @@ 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; + /* 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); + list_del(&link->sibling); + list_del(&link->link); /* Clock PM is for endpoint device */ - - free_link_state(link_state); + free_link_state(link); out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); -- cgit v1.2.3 From f1c0ca29ae72bc0c10282eada66c8a792ee98482 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 10:59:52 +0900 Subject: PCI ASPM: introduce disable flag Introduce 'aspm_disable' flag to manage disabled ASPM state more robust way. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 41 ++++++++++++++++++++++++----------------- 1 file changed, 24 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 0800c514682..8f7884a8bc2 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -43,6 +43,7 @@ struct pcie_link_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_disable:2; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ @@ -322,10 +323,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) struct pci_bus *linkbus = parent->subordinate; if (blacklist) { - /* Set support state to 0, so we will disable ASPM later */ - link->aspm_support = 0; - link->aspm_default = 0; + /* Set enabled/disable so that we will disable ASPM later */ link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + link->aspm_disable = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; return; } @@ -348,6 +348,17 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Save default state */ link->aspm_default = link->aspm_enabled; + /* + * 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 = + PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + break; + } + } if (!link->aspm_support) return; @@ -458,14 +469,10 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - /* - * 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; - } + state &= ~link->aspm_disable; + /* Nothing to do if the link is already in the requested state */ + if (link->aspm_enabled == state) + return; /* * Spec 2.0 suggests all functions should be configured the * same setting for ASPM. Enabling ASPM L1 should be done in @@ -719,7 +726,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; @@ -731,12 +738,12 @@ 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; + link->aspm_disable |= state; + __pcie_aspm_configure_link_state(link, link->aspm_enabled); 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); -- cgit v1.2.3 From 07d92760d2ee542fe932f4e8b5807dd98481d1fd Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 11:00:25 +0900 Subject: PCI ASPM: introduce capable flag Introduce 'aspm_capable' field to maintain the capable ASPM setting of the link. By the 'aspm_capable', we don't need to recheck latency every time ASPM policy is changed. Each bit in 'aspm_capable' is associated to ASPM state (L0S/L1). The bit is set if the associated ASPM state is supported by the link and it satisfies the latency requirement (i.e. exit latency < endpoint acceptable latency). The 'aspm_capable' is updated when - an endpoint device is added (boot time or hot-plug time) - an endpoint device is removed (hot-unplug time) - PCI power state is changed. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 117 +++++++++++++++++++++++++++++++++++------------- 1 file changed, 86 insertions(+), 31 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 8f7884a8bc2..79f6d61798f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -42,6 +42,7 @@ struct pcie_link_state { /* ASPM state */ u32 aspm_support:2; /* Supported ASPM state */ u32 aspm_enabled:2; /* Enabled ASPM state */ + u32 aspm_capable:2; /* Capable ASPM state with latency */ u32 aspm_default:2; /* Default ASPM state by BIOS */ u32 aspm_disable:2; /* Disabled ASPM state */ @@ -316,6 +317,39 @@ static void pcie_aspm_get_cap_device(struct pci_dev *pdev, u32 *state, *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); } +static void pcie_aspm_check_latency(struct pci_dev *endpoint) +{ + u32 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; + + link = endpoint->bus->self->link_state; + acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; + + while (link) { + /* Check L0s latency */ + if ((link->aspm_capable & PCIE_LINK_STATE_L0S) && + (link->latency.l0s > acceptable->l0s)) + link->aspm_capable &= ~PCIE_LINK_STATE_L0S; + /* + * Check L1 latency. + * Every switch on the path to root complex need 1 + * more microsecond for L1. Spec doesn't mention L0s. + */ + if ((link->aspm_capable & PCIE_LINK_STATE_L1) && + (link->latency.l1 + l1_switch_latency > acceptable->l1)) + link->aspm_capable &= ~PCIE_LINK_STATE_L1; + l1_switch_latency += 1000; + + link = link->parent; + } +} + static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { u32 support, l0s, l1, enabled; @@ -348,6 +382,9 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Save default state */ link->aspm_default = link->aspm_enabled; + + /* 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. @@ -376,12 +413,14 @@ 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); - } + /* Calculate endpoint L1 acceptable latency */ + encoding = (reg32 & PCI_EXP_DEVCAP_L1) >> 9; + acceptable->l1 = calc_l1_acceptable(encoding); + + pcie_aspm_check_latency(child); } } @@ -397,27 +436,10 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) */ 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)]; - + struct pcie_link_state *link = endpoint->bus->self->link_state; 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; + state &= link->aspm_capable; 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; } @@ -668,11 +690,35 @@ 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; + struct pcie_link_state *link, *root; if (aspm_disabled || !pdev->is_pcie || !parent || !parent->link_state) return; @@ -690,6 +736,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) goto out; link = parent->link_state; + root = link->root; /* All functions are removed, so just disable ASPM for the link */ __pcie_aspm_config_one_dev(parent, 0); @@ -697,6 +744,9 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) list_del(&link->link); /* Clock PM is for endpoint device */ free_link_state(link); + + /* Recheck latencies and configure upstream links */ + pcie_update_aspm_capable(root); out: mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); @@ -705,18 +755,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_aspm_configure_link_state(link, link->aspm_enabled); + mutex_unlock(&aspm_lock); + up_read(&pci_bus_sem); } /* -- cgit v1.2.3 From b7206cbf024dd43c42f9585e2017db1c1facd566 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 11:01:37 +0900 Subject: PCI ASPM: support partial aspm enablement In the current implementation, ASPM L0s/L1 is disabled for all links in the hierarchy if one of the link doesn't meet latency requirement. But we can partially enable ASPM L0s/L1 on sub-tree in the hierarchy. This patch allows partial L0s/L1 enablement in the hierarchy. And it also reduce the calculation cost of ASPM configuration very much. In the previous implementation, all links were enabled with the same state. With this patch, enabled state for each link is determined simply as follows (the 'requested' is from policy_to_aspm_state()). enabled = requested & (link->aspm_capable & link->aspm_disable) Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 211 +++++++++++------------------------------------- 1 file changed, 47 insertions(+), 164 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 79f6d61798f..08d293f60fe 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -166,18 +166,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 @@ -397,10 +385,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } - if (!link->aspm_support) - return; - - /* ENDPOINT states*/ + /* Get and check endpoint acceptable latencies */ list_for_each_entry(child, &linkbus->devices, bus_list) { int pos; u32 reg32, encoding; @@ -424,58 +409,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } -/** - * __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) -{ - struct pcie_link_state *link = endpoint->bus->self->link_state; - while (link && state) { - state &= link->aspm_capable; - link = link->parent; - } - 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); - } - 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 state) { u16 reg16; int pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); @@ -486,13 +420,13 @@ static void __pcie_aspm_config_one_dev(struct pci_dev *pdev, unsigned int state) 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) { struct pci_dev *child, *parent = link->pdev; struct pci_bus *linkbus = parent->subordinate; - state &= ~link->aspm_disable; /* 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; /* @@ -502,66 +436,25 @@ static void __pcie_aspm_config_link(struct pcie_link_state *link, u32 state) * versa for disabling ASPM L1. Spec doesn't mention L0S. */ if (state & PCIE_LINK_STATE_L1) - __pcie_aspm_config_one_dev(parent, state); + pcie_config_aspm_dev(parent, state); list_for_each_entry(child, &linkbus->devices, bus_list) - __pcie_aspm_config_one_dev(child, state); + pcie_config_aspm_dev(child, state); if (!(state & PCIE_LINK_STATE_L1)) - __pcie_aspm_config_one_dev(parent, state); + pcie_config_aspm_dev(parent, state); 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; @@ -596,10 +489,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) @@ -625,15 +517,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; } @@ -644,8 +528,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; @@ -663,27 +547,20 @@ 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: @@ -718,12 +595,12 @@ static void pcie_update_aspm_capable(struct pcie_link_state *root) void pcie_aspm_exit_link_state(struct pci_dev *pdev) { struct pci_dev *parent = pdev->bus->self; - struct pcie_link_state *link, *root; + struct pcie_link_state *link, *root, *parent_link; 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); @@ -737,9 +614,10 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) 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); + pcie_config_aspm_link(link, 0); list_del(&link->sibling); list_del(&link->link); /* Clock PM is for endpoint device */ @@ -747,6 +625,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* 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); @@ -769,7 +648,7 @@ void pcie_aspm_pm_state_change(struct pci_dev *pdev) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); pcie_update_aspm_capable(link->root); - __pcie_aspm_configure_link_state(link, link->aspm_enabled); + pcie_config_aspm_path(link); mutex_unlock(&aspm_lock); up_read(&pci_bus_sem); } @@ -795,7 +674,8 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) mutex_lock(&aspm_lock); link = parent->link_state; link->aspm_disable |= state; - __pcie_aspm_configure_link_state(link, link->aspm_enabled); + pcie_config_aspm_link(link, policy_to_aspm_state(link)); + if (state & PCIE_LINK_STATE_CLKPM) { link->clkpm_capable = 0; pcie_set_clkpm(link, 0); @@ -808,7 +688,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]))) @@ -821,10 +701,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); @@ -862,18 +741,22 @@ 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 state = buf[0] - '0'; - if (n < 1) + if (n < 1 || state > 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; + 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, -- cgit v1.2.3 From ac18018a414a90d841ea81d38fecb913c0ec1880 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 19 Aug 2009 11:02:13 +0900 Subject: PCI ASPM: support per direction l0s management The L0s state can be managed separately for each direction (upstream direction and downstream direction) of the link. But in the current implementation, those are mixed up. With this patch, L0s for each direction are managed separately. To maintain three states (upstream direction L0s, downstream L0s and L1), 'aspm_support', 'aspm_enabled', 'aspm_capable', 'aspm_disable' and 'aspm_default' fields in struct pcie_link_state are changed to 3-bit from 2-bit. The 'latency' field is separated to two 'latency_up' and 'latency_dw' fields to maintain exit latencies for each direction of the link. For L0, 'latency_up.l0' and 'latency_dw.l0' are used to configure upstream direction L0s and downstream direction L0s respectively. For L1, larger value of 'latency_up.l1' and 'latency_dw.l1' is considered as L1 exit latency. Acked-by: Shaohua Li Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 170 ++++++++++++++++++++++++++++++------------------ 1 file changed, 107 insertions(+), 63 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 08d293f60fe..f289ca9bf18 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,19 +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_capable:2; /* Capable ASPM state with latency */ - u32 aspm_default:2; /* Default ASPM state by BIOS */ - u32 aspm_disable:2; /* Disabled ASPM state */ + 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. @@ -84,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; } @@ -278,36 +286,35 @@ 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) - 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); - } + 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); - *enabled = reg16 & (PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1); + info->enabled = reg16 & PCI_EXP_LNKCTL_ASPMC; } static void pcie_aspm_check_latency(struct pci_dev *endpoint) { - u32 l1_switch_latency = 0; + u32 latency, l1_switch_latency = 0; struct aspm_latency *acceptable; struct pcie_link_state *link; @@ -320,18 +327,24 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) acceptable = &link->acceptable[PCI_FUNC(endpoint->devfn)]; while (link) { - /* Check L0s latency */ - if ((link->aspm_capable & PCIE_LINK_STATE_L0S) && - (link->latency.l0s > acceptable->l0s)) - link->aspm_capable &= ~PCIE_LINK_STATE_L0S; + /* 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. */ - if ((link->aspm_capable & PCIE_LINK_STATE_L1) && - (link->latency.l1 + l1_switch_latency > acceptable->l1)) - link->aspm_capable &= ~PCIE_LINK_STATE_L1; + 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; @@ -340,33 +353,48 @@ static void pcie_aspm_check_latency(struct pci_dev *endpoint) 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 enabled/disable so that we will disable ASPM later */ - link->aspm_enabled = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; - link->aspm_disable = PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + 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); + + /* + * 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; @@ -379,8 +407,7 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) */ list_for_each_entry(child, &linkbus->devices, bus_list) { if (child->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { - link->aspm_disable = - PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1; + link->aspm_disable = ASPM_STATE_ALL; break; } } @@ -409,19 +436,20 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) } } -static void pcie_config_aspm_dev(struct pci_dev *pdev, u32 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_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; @@ -429,20 +457,27 @@ static void pcie_config_aspm_link(struct pcie_link_state *link, u32 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 * same setting for ASPM. Enabling ASPM L1 should be done in * 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_config_aspm_dev(parent, state); - + if (state & ASPM_STATE_L1) + pcie_config_aspm_dev(parent, upstream); list_for_each_entry(child, &linkbus->devices, bus_list) - pcie_config_aspm_dev(child, state); - - if (!(state & PCIE_LINK_STATE_L1)) - pcie_config_aspm_dev(parent, state); + pcie_config_aspm_dev(child, dwstream); + if (!(state & ASPM_STATE_L1)) + pcie_config_aspm_dev(parent, upstream); link->aspm_enabled = state; } @@ -673,7 +708,10 @@ void pci_disable_link_state(struct pci_dev *pdev, int state) down_read(&pci_bus_sem); mutex_lock(&aspm_lock); link = parent->link_state; - link->aspm_disable |= 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) { @@ -742,11 +780,17 @@ static ssize_t link_state_store(struct device *dev, { struct pci_dev *pdev = to_pci_dev(dev); struct pcie_link_state *link, *root = pdev->link_state->root; - u32 state = buf[0] - '0'; + u32 val = buf[0] - '0', state = 0; - if (n < 1 || state > 3) + if (n < 1 || val > 3) 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) { -- cgit v1.2.3 From 6ed6a8dc83dc37d30c8a7ff2eae2ed054e34c91b Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 29 Jul 2009 14:39:07 +0900 Subject: PCI hotplug: fix typo in pcie link speed info Fix typo in PCI-E link speed. Reviewed-by: Matthew Wilcox Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pci_hotplug_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 5c5043f239c..9d44669bf09 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -86,7 +86,7 @@ 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 */ }; #ifdef CONFIG_HOTPLUG_PCI_CPCI -- cgit v1.2.3 From 825c423a35a80a8fd66398a3f9bde7f0b0187a76 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 29 Jul 2009 14:39:58 +0900 Subject: PCI hotplug: add support for 5.0G link speed Add support for PCI-E 5.0 GT/s in max_bus_speed and cur_bus_speed. Reviewed-by: Matthew Wilcox Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pci_hotplug_core.c | 1 + drivers/pci/hotplug/pciehp_hpc.c | 10 ++++++++-- 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pci_hotplug_core.c b/drivers/pci/hotplug/pci_hotplug_core.c index 9d44669bf09..0325d989bb4 100644 --- a/drivers/pci/hotplug/pci_hotplug_core.c +++ b/drivers/pci/hotplug/pci_hotplug_core.c @@ -87,6 +87,7 @@ static char *pci_bus_speed_strings[] = { "100 MHz PCIX 533", /* 0x12 */ "133 MHz PCIX 533", /* 0x13 */ "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 52813257e5b..271f917b6f2 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; -- cgit v1.2.3 From 9dba910e9de2c4aa15ec1286f10052c107ef48ca Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 3 Sep 2009 15:26:36 +0900 Subject: PCI: separate out pci_add_dynid() Separate out pci_add_dynid() from store_new_id() and export it so that in-kernel code can add PCI IDs dynamically. As the function will be available regardless of HOTPLUG, put it and pull pci_free_dynids() outside of CONFIG_HOTPLUG. This will be used by pci-stub to initialize initial IDs via module param. While at it, remove bogus get_driver() failure check. Signed-off-by: Tejun Heo Acked-by: Greg Kroah-Hartman Reviewed-by: Grant Grundler Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 119 +++++++++++++++++++++++++++++------------------ 1 file changed, 74 insertions(+), 45 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index f99bc7f089f..c66dc4341fa 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -19,37 +19,98 @@ #include #include "pci.h" -/* - * Dynamic device IDs are disabled for !CONFIG_HOTPLUG - */ - struct pci_dynid { struct list_head node; struct pci_device_id id; }; -#ifdef CONFIG_HOTPLUG +/** + * pci_add_dynid - add a new PCI device ID to this driver and re-probe devices + * @drv: target pci driver + * @vendor: PCI vendor ID + * @device: PCI device ID + * @subvendor: PCI subvendor ID + * @subdevice: PCI subdevice ID + * @class: PCI class + * @class_mask: PCI class mask + * @driver_data: private driver data + * + * Adds a new dynamic pci device ID to this driver and causes the + * driver to probe for all devices again. @drv must have been + * registered prior to calling this function. + * + * CONTEXT: + * Does GFP_KERNEL allocation. + * + * RETURNS: + * 0 on success, -errno on failure. + */ +int pci_add_dynid(struct pci_driver *drv, + unsigned int vendor, unsigned int device, + unsigned int subvendor, unsigned int subdevice, + unsigned int class, unsigned int class_mask, + unsigned long driver_data) +{ + struct pci_dynid *dynid; + int retval; + + dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); + if (!dynid) + return -ENOMEM; + + dynid->id.vendor = vendor; + dynid->id.device = device; + dynid->id.subvendor = subvendor; + dynid->id.subdevice = subdevice; + dynid->id.class = class; + dynid->id.class_mask = class_mask; + dynid->id.driver_data = driver_data; + spin_lock(&drv->dynids.lock); + list_add_tail(&dynid->node, &drv->dynids.list); + spin_unlock(&drv->dynids.lock); + + get_driver(&drv->driver); + retval = driver_attach(&drv->driver); + put_driver(&drv->driver); + + return retval; +} + +static void pci_free_dynids(struct pci_driver *drv) +{ + struct pci_dynid *dynid, *n; + + spin_lock(&drv->dynids.lock); + list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { + list_del(&dynid->node); + kfree(dynid); + } + spin_unlock(&drv->dynids.lock); +} + +/* + * Dynamic device ID manipulation via sysfs is disabled for !CONFIG_HOTPLUG + */ +#ifdef CONFIG_HOTPLUG /** - * store_new_id - add a new PCI device ID to this driver and re-probe devices + * store_new_id - sysfs frontend to pci_add_dynid() * @driver: target device driver * @buf: buffer for scanning device ID data * @count: input size * - * Adds a new dynamic pci device ID to this driver, - * and causes the driver to probe for all devices again. + * Allow PCI IDs to be added to an existing driver via sysfs. */ static ssize_t store_new_id(struct device_driver *driver, const char *buf, size_t count) { - struct pci_dynid *dynid; struct pci_driver *pdrv = to_pci_driver(driver); const struct pci_device_id *ids = pdrv->id_table; __u32 vendor, device, subvendor=PCI_ANY_ID, subdevice=PCI_ANY_ID, class=0, class_mask=0; unsigned long driver_data=0; int fields=0; - int retval=0; + int retval; fields = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, &subvendor, &subdevice, @@ -72,27 +133,8 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) return retval; } - dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); - if (!dynid) - return -ENOMEM; - - dynid->id.vendor = vendor; - dynid->id.device = device; - dynid->id.subvendor = subvendor; - dynid->id.subdevice = subdevice; - dynid->id.class = class; - dynid->id.class_mask = class_mask; - dynid->id.driver_data = driver_data; - - spin_lock(&pdrv->dynids.lock); - list_add_tail(&dynid->node, &pdrv->dynids.list); - spin_unlock(&pdrv->dynids.lock); - - if (get_driver(&pdrv->driver)) { - retval = driver_attach(&pdrv->driver); - put_driver(&pdrv->driver); - } - + retval = pci_add_dynid(pdrv, vendor, device, subvendor, subdevice, + class, class_mask, driver_data); if (retval) return retval; return count; @@ -145,19 +187,6 @@ store_remove_id(struct device_driver *driver, const char *buf, size_t count) } static DRIVER_ATTR(remove_id, S_IWUSR, NULL, store_remove_id); -static void -pci_free_dynids(struct pci_driver *drv) -{ - struct pci_dynid *dynid, *n; - - spin_lock(&drv->dynids.lock); - list_for_each_entry_safe(dynid, n, &drv->dynids.list, node) { - list_del(&dynid->node); - kfree(dynid); - } - spin_unlock(&drv->dynids.lock); -} - static int pci_create_newid_file(struct pci_driver *drv) { @@ -186,7 +215,6 @@ static void pci_remove_removeid_file(struct pci_driver *drv) driver_remove_file(&drv->driver, &driver_attr_remove_id); } #else /* !CONFIG_HOTPLUG */ -static inline void pci_free_dynids(struct pci_driver *drv) {} static inline int pci_create_newid_file(struct pci_driver *drv) { return 0; @@ -1106,6 +1134,7 @@ static int __init pci_driver_init(void) postcore_initcall(pci_driver_init); +EXPORT_SYMBOL_GPL(pci_add_dynid); EXPORT_SYMBOL(pci_match_id); EXPORT_SYMBOL(__pci_register_driver); EXPORT_SYMBOL(pci_unregister_driver); -- cgit v1.2.3 From b439b1d4e3ae3c36ed94ed233119ff0d145af257 Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Thu, 3 Sep 2009 15:27:27 +0900 Subject: PCI: pci-stub: add pci_stub.ids parameter Add ids module parameter which allows specifying initial IDs for the pci-stub driver. When built into the kernel, pci-stub is linked before any real pci drivers and by setting up IDs from initialization it can prevent built-in drivers from attaching to specific devices. While at it, make pci_stub_probe() print out about devices it grabbed to weed out "but my controller isn't being probed" bug reports. Signed-off-by: Tejun Heo Signed-off-by: Jesse Barnes --- drivers/pci/pci-stub.c | 45 ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 44 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c index 74fbec0bf6c..f7b68ca6cc9 100644 --- a/drivers/pci/pci-stub.c +++ b/drivers/pci/pci-stub.c @@ -19,8 +19,16 @@ #include #include +static char ids[1024] __initdata; + +module_param_string(ids, ids, sizeof(ids), 0); +MODULE_PARM_DESC(ids, "Initial PCI IDs to add to the stub driver, format is " + "\"vendor:device[:subvendor[:subdevice[:class[:class_mask]]]]\"" + " and multiple comma separated entries can be specified"); + static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id) { + dev_printk(KERN_INFO, &dev->dev, "claimed by stub\n"); return 0; } @@ -32,7 +40,42 @@ static struct pci_driver stub_driver = { static int __init pci_stub_init(void) { - return pci_register_driver(&stub_driver); + char *p, *id; + int rc; + + rc = pci_register_driver(&stub_driver); + if (rc) + return rc; + + /* add ids specified in the module parameter */ + p = ids; + while ((id = strsep(&p, ","))) { + unsigned int vendor, device, subvendor = PCI_ANY_ID, + subdevice = PCI_ANY_ID, class=0, class_mask=0; + int fields; + + fields = sscanf(id, "%x:%x:%x:%x:%x:%x", + &vendor, &device, &subvendor, &subdevice, + &class, &class_mask); + + if (fields < 2) { + printk(KERN_WARNING + "pci-stub: invalid id string \"%s\"\n", id); + continue; + } + + printk(KERN_INFO + "pci-stub: add %04X:%04X sub=%04X:%04X cls=%08X/%08X\n", + vendor, device, subvendor, subdevice, class, class_mask); + + rc = pci_add_dynid(&stub_driver, vendor, device, + subvendor, subdevice, class, class_mask, 0); + if (rc) + printk(KERN_WARNING + "pci-stub: failed to add dynamic id (%d)\n", rc); + } + + return 0; } static void __exit pci_stub_exit(void) -- cgit v1.2.3 From c9a918838c07cbef934c8ef818d8f0e719015c3a Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:07:29 +0900 Subject: PCI: pcie, aer: checkpatch style cleanup in pcie/aer/* Before: drivers/pci/pcie/aer/aer_inject.c total: 4 errors, 4 warnings, 473 lines checked drivers/pci/pcie/aer/aerdrv.c total: 5 errors, 2 warnings, 333 lines checked drivers/pci/pcie/aer/aerdrv.h total: 1 errors, 0 warnings, 139 lines checked drivers/pci/pcie/aer/aerdrv_core.c total: 4 errors, 3 warnings, 872 lines checked drivers/pci/pcie/aer/aerdrv_errprint.c total: 12 errors, 11 warnings, 248 lines checked After: drivers/pci/pcie/aer/aer_inject.c total: 0 errors, 0 warnings, 466 lines checked drivers/pci/pcie/aer/aerdrv.c total: 0 errors, 0 warnings, 335 lines checked drivers/pci/pcie/aer/aerdrv.h total: 0 errors, 0 warnings, 139 lines checked drivers/pci/pcie/aer/aerdrv_core.c total: 0 errors, 0 warnings, 869 lines checked drivers/pci/pcie/aer/aerdrv_errprint.c total: 0 errors, 10 warnings, 247 lines checked Signed-off-by: Hidetoshi Seto Reviewed-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aer_inject.c | 25 +++++++---------- drivers/pci/pcie/aer/aerdrv.c | 22 ++++++++------- drivers/pci/pcie/aer/aerdrv.h | 6 ++--- drivers/pci/pcie/aer/aerdrv_core.c | 31 ++++++++++----------- drivers/pci/pcie/aer/aerdrv_errprint.c | 49 +++++++++++++++++----------------- 5 files changed, 62 insertions(+), 71 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aer_inject.c b/drivers/pci/pcie/aer/aer_inject.c index d92ae21a59d..62d15f652bb 100644 --- a/drivers/pci/pcie/aer/aer_inject.c +++ b/drivers/pci/pcie/aer/aer_inject.c @@ -22,11 +22,10 @@ #include #include #include -#include +#include #include "aerdrv.h" -struct aer_error_inj -{ +struct aer_error_inj { u8 bus; u8 dev; u8 fn; @@ -38,8 +37,7 @@ struct aer_error_inj u32 header_log3; }; -struct aer_error -{ +struct aer_error { struct list_head list; unsigned int bus; unsigned int devfn; @@ -55,8 +53,7 @@ struct aer_error u32 source_id; }; -struct pci_bus_ops -{ +struct pci_bus_ops { struct list_head list; struct pci_bus *bus; struct pci_ops *ops; @@ -150,7 +147,7 @@ static u32 *find_pci_config_dword(struct aer_error *err, int where, target = &err->header_log1; break; case PCI_ERR_HEADER_LOG+8: - target = &err->header_log2; + target = &err->header_log2; break; case PCI_ERR_HEADER_LOG+12: target = &err->header_log3; @@ -258,8 +255,7 @@ static int pci_bus_set_aer_ops(struct pci_bus *bus) bus_ops = NULL; out: spin_unlock_irqrestore(&inject_lock, flags); - if (bus_ops) - kfree(bus_ops); + kfree(bus_ops); return 0; } @@ -401,10 +397,8 @@ static int aer_inject(struct aer_error_inj *einj) else ret = -EINVAL; out_put: - if (err_alloc) - kfree(err_alloc); - if (rperr_alloc) - kfree(rperr_alloc); + kfree(err_alloc); + kfree(rperr_alloc); pci_dev_put(dev); return ret; } @@ -458,8 +452,7 @@ static void __exit aer_inject_exit(void) } spin_lock_irqsave(&inject_lock, flags); - list_for_each_entry_safe(err, err_next, - &pci_bus_ops_list, list) { + list_for_each_entry_safe(err, err_next, &pci_bus_ops_list, list) { list_del(&err->list); kfree(err); } diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index 4770f13b3ca..10c0e62bd5a 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -38,7 +38,7 @@ MODULE_AUTHOR(DRIVER_AUTHOR); MODULE_DESCRIPTION(DRIVER_DESC); MODULE_LICENSE("GPL"); -static int __devinit aer_probe (struct pcie_device *dev); +static int __devinit aer_probe(struct pcie_device *dev); static void aer_remove(struct pcie_device *dev); static pci_ers_result_t aer_error_detected(struct pci_dev *dev, enum pci_channel_state error); @@ -47,7 +47,7 @@ static pci_ers_result_t aer_root_reset(struct pci_dev *dev); static struct pci_error_handlers aer_error_handlers = { .error_detected = aer_error_detected, - .resume = aer_error_resume, + .resume = aer_error_resume, }; static struct pcie_port_service_driver aerdriver = { @@ -134,12 +134,12 @@ EXPORT_SYMBOL_GPL(aer_irq); * * Invoked when Root Port's AER service is loaded. **/ -static struct aer_rpc* aer_alloc_rpc(struct pcie_device *dev) +static struct aer_rpc *aer_alloc_rpc(struct pcie_device *dev) { struct aer_rpc *rpc; - if (!(rpc = kzalloc(sizeof(struct aer_rpc), - GFP_KERNEL))) + rpc = kzalloc(sizeof(struct aer_rpc), GFP_KERNEL); + if (!rpc) return NULL; /* @@ -189,26 +189,28 @@ static void aer_remove(struct pcie_device *dev) * * Invoked when PCI Express bus loads AER service driver. **/ -static int __devinit aer_probe (struct pcie_device *dev) +static int __devinit aer_probe(struct pcie_device *dev) { int status; struct aer_rpc *rpc; struct device *device = &dev->device; /* Init */ - if ((status = aer_init(dev))) + status = aer_init(dev); + if (status) return status; /* Alloc rpc data structure */ - if (!(rpc = aer_alloc_rpc(dev))) { + rpc = aer_alloc_rpc(dev); + if (!rpc) { dev_printk(KERN_DEBUG, device, "alloc rpc failed\n"); aer_remove(dev); return -ENOMEM; } /* Request IRQ ISR */ - if ((status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", - dev))) { + status = request_irq(dev->irq, aer_irq, IRQF_SHARED, "aerdrv", dev); + if (status) { dev_printk(KERN_DEBUG, device, "request IRQ failed\n"); aer_remove(dev); return status; diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index bbd7428ca2d..820ea73d25f 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -21,7 +21,7 @@ #define AER_ERROR(d) (d & AER_ERROR_MASK) /* Root Error Status Register Bits */ -#define ROOT_ERR_STATUS_MASKS 0x0f +#define ROOT_ERR_STATUS_MASKS 0x0f #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ @@ -65,7 +65,7 @@ struct aer_err_info { int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ int flags; unsigned int status; /* COR/UNCOR Error Status */ - struct header_log_regs tlp; /* TLP Header */ + struct header_log_regs tlp; /* TLP Header */ }; struct aer_err_source { @@ -136,4 +136,4 @@ static inline int aer_osc_setup(struct pcie_device *pciedev) } #endif -#endif //_AERDRV_H_ +#endif /* _AERDRV_H_ */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 3d8872704a5..d3f6df40b0d 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -49,10 +49,11 @@ int pci_enable_pcie_error_reporting(struct pci_dev *dev) PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE; - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, - reg16); + pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + return 0; } +EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); int pci_disable_pcie_error_reporting(struct pci_dev *dev) { @@ -68,10 +69,11 @@ int pci_disable_pcie_error_reporting(struct pci_dev *dev) PCI_EXP_DEVCTL_NFERE | PCI_EXP_DEVCTL_FERE | PCI_EXP_DEVCTL_URRE); - pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, - reg16); + pci_write_config_word(dev, pos+PCI_EXP_DEVCTL, reg16); + return 0; } +EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) { @@ -92,6 +94,7 @@ int pci_cleanup_aer_uncorrect_error_status(struct pci_dev *dev) return 0; } +EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); #if 0 int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) @@ -110,7 +113,6 @@ int pci_cleanup_aer_correct_error_status(struct pci_dev *dev) } #endif /* 0 */ - static int set_device_error_reporting(struct pci_dev *dev, void *data) { bool enable = *((bool *)data); @@ -164,8 +166,9 @@ static int add_error_device(struct aer_err_info *e_info, struct pci_dev *dev) e_info->dev[e_info->error_dev_num] = dev; e_info->error_dev_num++; return 1; - } else - return 0; + } + + return 0; } @@ -411,8 +414,7 @@ static pci_ers_result_t broadcast_error_message(struct pci_dev *dev, pci_cleanup_aer_uncorrect_error_status(dev); dev->error_state = pci_channel_io_normal; } - } - else { + } else { /* * If the error is reported by an end point, we think this * error is related to the upstream link of the end point. @@ -473,7 +475,7 @@ static pci_ers_result_t reset_link(struct pcie_device *aerdev, if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE) udev = dev; else - udev= dev->bus->self; + udev = dev->bus->self; data.is_downstream = 0; data.aer_driver = NULL; @@ -576,7 +578,7 @@ static pci_ers_result_t do_recovery(struct pcie_device *aerdev, * * Invoked when an error being detected by Root Port. */ -static void handle_error_source(struct pcie_device * aerdev, +static void handle_error_source(struct pcie_device *aerdev, struct pci_dev *dev, struct aer_err_info *info) { @@ -682,7 +684,7 @@ static void disable_root_aer(struct aer_rpc *rpc) * * Invoked by DPC handler to consume an error. */ -static struct aer_err_source* get_e_source(struct aer_rpc *rpc) +static struct aer_err_source *get_e_source(struct aer_rpc *rpc) { struct aer_err_source *e_source; unsigned long flags; @@ -865,8 +867,3 @@ int aer_init(struct pcie_device *dev) return AER_SUCCESS; } - -EXPORT_SYMBOL_GPL(pci_enable_pcie_error_reporting); -EXPORT_SYMBOL_GPL(pci_disable_pcie_error_reporting); -EXPORT_SYMBOL_GPL(pci_cleanup_aer_uncorrect_error_status); - diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 0fc29ae80df..ccaeceb32da 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -33,7 +33,7 @@ #define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT #define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \ - ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER: 0))) + ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER : 0))) #define AER_GET_AGENT(t, e) \ ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \ @@ -44,11 +44,11 @@ #define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR #define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \ (PCI_ERR_UNC_DLP| \ - PCI_ERR_COR_BAD_TLP| \ + PCI_ERR_COR_BAD_TLP| \ PCI_ERR_COR_BAD_DLLP| \ - PCI_ERR_COR_REP_ROLL| \ + PCI_ERR_COR_REP_ROLL| \ ((t == AER_CORRECTABLE) ? \ - PCI_ERR_COR_REP_TIMER: 0)) + PCI_ERR_COR_REP_TIMER : 0)) #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 @@ -58,38 +58,38 @@ ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \ AER_PHYSICAL_LAYER_ERROR : \ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \ - AER_DATA_LINK_LAYER_ERROR : \ + AER_DATA_LINK_LAYER_ERROR : \ AER_TRANSACTION_LAYER_ERROR) /* * AER error strings */ -static char* aer_error_severity_string[] = { +static char *aer_error_severity_string[] = { "Uncorrected (Non-Fatal)", "Uncorrected (Fatal)", "Corrected" }; -static char* aer_error_layer[] = { +static char *aer_error_layer[] = { "Physical Layer", "Data Link Layer", "Transaction Layer" }; -static char* aer_correctable_error_string[] = { - "Receiver Error ", /* Bit Position 0 */ +static char *aer_correctable_error_string[] = { + "Receiver Error ", /* Bit Position 0 */ NULL, NULL, NULL, NULL, NULL, - "Bad TLP ", /* Bit Position 6 */ - "Bad DLLP ", /* Bit Position 7 */ - "RELAY_NUM Rollover ", /* Bit Position 8 */ + "Bad TLP ", /* Bit Position 6 */ + "Bad DLLP ", /* Bit Position 7 */ + "RELAY_NUM Rollover ", /* Bit Position 8 */ NULL, NULL, NULL, - "Replay Timer Timeout ", /* Bit Position 12 */ - "Advisory Non-Fatal ", /* Bit Position 13 */ + "Replay Timer Timeout ", /* Bit Position 12 */ + "Advisory Non-Fatal ", /* Bit Position 13 */ NULL, NULL, NULL, @@ -110,7 +110,7 @@ static char* aer_correctable_error_string[] = { NULL, }; -static char* aer_uncorrectable_error_string[] = { +static char *aer_uncorrectable_error_string[] = { NULL, NULL, NULL, @@ -123,10 +123,10 @@ static char* aer_uncorrectable_error_string[] = { NULL, NULL, NULL, - "Poisoned TLP ", /* Bit Position 12 */ + "Poisoned TLP ", /* Bit Position 12 */ "Flow Control Protocol ", /* Bit Position 13 */ - "Completion Timeout ", /* Bit Position 14 */ - "Completer Abort ", /* Bit Position 15 */ + "Completion Timeout ", /* Bit Position 14 */ + "Completer Abort ", /* Bit Position 15 */ "Unexpected Completion ", /* Bit Position 16 */ "Receiver Overflow ", /* Bit Position 17 */ "Malformed TLP ", /* Bit Position 18 */ @@ -145,19 +145,19 @@ static char* aer_uncorrectable_error_string[] = { NULL, }; -static char* aer_agent_string[] = { +static char *aer_agent_string[] = { "Receiver ID", "Requester ID", "Completer ID", "Transmitter ID" }; -static char * aer_get_error_source_name(int severity, +static char *aer_get_error_source_name(int severity, unsigned int status, char errmsg_buff[]) { int i; - char * errmsg = NULL; + char *errmsg = NULL; for (i = 0; i < 32; i++) { if (!(status & (1 << i))) @@ -183,9 +183,9 @@ static DEFINE_SPINLOCK(logbuf_lock); static char errmsg_buff[100]; void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { - char * errmsg; + char *errmsg; int err_layer, agent; - char * loglevel; + char *loglevel; if (info->severity == AER_CORRECTABLE) loglevel = KERN_WARNING; @@ -196,7 +196,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) printk("%sError Severity\t\t: %s\n", loglevel, aer_error_severity_string[info->severity]); - if ( info->status == 0) { + if (info->status == 0) { printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel); printk("%sUnaccessible Received\t: %s\n", loglevel, info->flags & AER_MULTI_ERROR_VALID_FLAG ? @@ -245,4 +245,3 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) } } } - -- cgit v1.2.3 From bd8fedd045d1d3f4e1f5daca179b0a49949ab538 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:08:14 +0900 Subject: PCI: pcie, aer: AER_PR for printing in aerdrv_errprint.c Add workaround macro to reduce the number of checkpatch warning: WARNING: printk() should include KERN_ facility level Before: total: 0 errors, 10 warnings, 247 lines checked After: total: 0 errors, 1 warnings, 243 lines checked Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_errprint.c | 34 +++++++++++++++------------------- 1 file changed, 15 insertions(+), 19 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index ccaeceb32da..7fb5a2c71c6 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -61,6 +61,10 @@ AER_DATA_LINK_LAYER_ERROR : \ AER_TRANSACTION_LAYER_ERROR) +#define AER_PR(info, fmt, args...) \ + printk("%s" fmt, (info->severity == AER_CORRECTABLE) ? \ + KERN_WARNING : KERN_ERR, ## args) + /* * AER error strings */ @@ -185,46 +189,39 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { char *errmsg; int err_layer, agent; - char *loglevel; - - if (info->severity == AER_CORRECTABLE) - loglevel = KERN_WARNING; - else - loglevel = KERN_ERR; - printk("%s+------ PCI-Express Device Error ------+\n", loglevel); - printk("%sError Severity\t\t: %s\n", loglevel, + AER_PR(info, "+------ PCI-Express Device Error ------+\n"); + AER_PR(info, "Error Severity\t\t: %s\n", aer_error_severity_string[info->severity]); if (info->status == 0) { - printk("%sPCIE Bus Error type\t: (Unaccessible)\n", loglevel); - printk("%sUnaccessible Received\t: %s\n", loglevel, + AER_PR(info, "PCIE Bus Error type\t: (Unaccessible)\n"); + AER_PR(info, "Unaccessible Received\t: %s\n", info->flags & AER_MULTI_ERROR_VALID_FLAG ? "Multiple" : "First"); - printk("%sUnregistered Agent ID\t: %04x\n", loglevel, + AER_PR(info, "Unregistered Agent ID\t: %04x\n", (dev->bus->number << 8) | dev->devfn); } else { err_layer = AER_GET_LAYER_ERROR(info->severity, info->status); - printk("%sPCIE Bus Error type\t: %s\n", loglevel, + AER_PR(info, "PCIE Bus Error type\t: %s\n", aer_error_layer[err_layer]); spin_lock(&logbuf_lock); errmsg = aer_get_error_source_name(info->severity, info->status, errmsg_buff); - printk("%s%s\t: %s\n", loglevel, errmsg, + AER_PR(info, "%s\t: %s\n", errmsg, info->flags & AER_MULTI_ERROR_VALID_FLAG ? "Multiple" : "First"); spin_unlock(&logbuf_lock); agent = AER_GET_AGENT(info->severity, info->status); - printk("%s%s\t\t: %04x\n", loglevel, + AER_PR(info, "%s\t\t: %04x\n", aer_agent_string[agent], (dev->bus->number << 8) | dev->devfn); - printk("%sVendorID=%04xh, DeviceID=%04xh," + AER_PR(info, "VendorID=%04xh, DeviceID=%04xh," " Bus=%02xh, Device=%02xh, Function=%02xh\n", - loglevel, dev->vendor, dev->device, dev->bus->number, @@ -233,10 +230,9 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) if (info->flags & AER_TLP_HEADER_VALID_FLAG) { unsigned char *tlp = (unsigned char *) &info->tlp; - printk("%sTLP Header:\n", loglevel); - printk("%s%02x%02x%02x%02x %02x%02x%02x%02x" + AER_PR(info, "TLP Header:\n"); + AER_PR(info, "%02x%02x%02x%02x %02x%02x%02x%02x" " %02x%02x%02x%02x %02x%02x%02x%02x\n", - loglevel, *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), *(tlp + 11), *(tlp + 10), *(tlp + 9), -- cgit v1.2.3 From f15857569613a982568be88d034555d88eead0aa Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:08:59 +0900 Subject: PCI: pcie, aer: rework MASK macros in aerdrv_errprint.c Definitions of MASK macros in aerdrv_errprint.c are tricky and unsafe. For example, AER_AGENT_TRANSMITTER_MASK(_sev, _stat) does work like: static inline func(int _sev, int _stat) { if (_sev == AER_CORRECTABLE) return (_stat & (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER)); else return (_stat & PCI_ERR_COR_REP_ROLL); } In case of else path here, for uncorrectable errors, testing bits in _stat by PCI_ERR_COR_* does not make sense because _stat should have only PCI_ERR_UNC_* bits originated in uncorrectable error status register. But at this time this is safe because uncorrectable error using bit position same to PCI_ERR_COR_REP_ROLL(= bit position 8) is not defined. Likewise, AER_AGENT_COMPLETER_MASK is always PCI_ERR_UNC_COMP_ABORT but it works because bit 15 of correctable error status is not defined. It means that these MASK macros will turn to be wrong once if new error is defined. (In fact, bit 15 of correctable is now defined in PCIe 2.1) This patch changes these MASK macros to be more strict, not to return PCI_ERR_COR_* bits for uncorrectable error status and vise versa. Signed-off-by: Hidetoshi Seto Reviewed-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_errprint.c | 46 ++++++++++++++++------------------ 1 file changed, 21 insertions(+), 25 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 7fb5a2c71c6..48f70fa7f68 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -27,39 +27,35 @@ #define AER_AGENT_COMPLETER 2 #define AER_AGENT_TRANSMITTER 3 -#define AER_AGENT_REQUESTER_MASK (PCI_ERR_UNC_COMP_TIME| \ - PCI_ERR_UNC_UNSUP) - -#define AER_AGENT_COMPLETER_MASK PCI_ERR_UNC_COMP_ABORT - -#define AER_AGENT_TRANSMITTER_MASK(t, e) (e & (PCI_ERR_COR_REP_ROLL| \ - ((t == AER_CORRECTABLE) ? PCI_ERR_COR_REP_TIMER : 0))) +#define AER_AGENT_REQUESTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : (PCI_ERR_UNC_COMP_TIME|PCI_ERR_UNC_UNSUP)) +#define AER_AGENT_COMPLETER_MASK(t) ((t == AER_CORRECTABLE) ? \ + 0 : PCI_ERR_UNC_COMP_ABORT) +#define AER_AGENT_TRANSMITTER_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_REP_ROLL|PCI_ERR_COR_REP_TIMER) : 0) #define AER_GET_AGENT(t, e) \ - ((e & AER_AGENT_COMPLETER_MASK) ? AER_AGENT_COMPLETER : \ - (e & AER_AGENT_REQUESTER_MASK) ? AER_AGENT_REQUESTER : \ - (AER_AGENT_TRANSMITTER_MASK(t, e)) ? AER_AGENT_TRANSMITTER : \ + ((e & AER_AGENT_COMPLETER_MASK(t)) ? AER_AGENT_COMPLETER : \ + (e & AER_AGENT_REQUESTER_MASK(t)) ? AER_AGENT_REQUESTER : \ + (e & AER_AGENT_TRANSMITTER_MASK(t)) ? AER_AGENT_TRANSMITTER : \ AER_AGENT_RECEIVER) -#define AER_PHYSICAL_LAYER_ERROR_MASK PCI_ERR_COR_RCVR -#define AER_DATA_LINK_LAYER_ERROR_MASK(t, e) \ - (PCI_ERR_UNC_DLP| \ - PCI_ERR_COR_BAD_TLP| \ - PCI_ERR_COR_BAD_DLLP| \ - PCI_ERR_COR_REP_ROLL| \ - ((t == AER_CORRECTABLE) ? \ - PCI_ERR_COR_REP_TIMER : 0)) - #define AER_PHYSICAL_LAYER_ERROR 0 #define AER_DATA_LINK_LAYER_ERROR 1 #define AER_TRANSACTION_LAYER_ERROR 2 -#define AER_GET_LAYER_ERROR(t, e) \ - ((e & AER_PHYSICAL_LAYER_ERROR_MASK) ? \ - AER_PHYSICAL_LAYER_ERROR : \ - (e & AER_DATA_LINK_LAYER_ERROR_MASK(t, e)) ? \ - AER_DATA_LINK_LAYER_ERROR : \ - AER_TRANSACTION_LAYER_ERROR) +#define AER_PHYSICAL_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + PCI_ERR_COR_RCVR : 0) +#define AER_DATA_LINK_LAYER_ERROR_MASK(t) ((t == AER_CORRECTABLE) ? \ + (PCI_ERR_COR_BAD_TLP| \ + PCI_ERR_COR_BAD_DLLP| \ + PCI_ERR_COR_REP_ROLL| \ + PCI_ERR_COR_REP_TIMER) : PCI_ERR_UNC_DLP) + +#define AER_GET_LAYER_ERROR(t, e) \ + ((e & AER_PHYSICAL_LAYER_ERROR_MASK(t)) ? AER_PHYSICAL_LAYER_ERROR : \ + (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ + AER_TRANSACTION_LAYER_ERROR) #define AER_PR(info, fmt, args...) \ printk("%s" fmt, (info->severity == AER_CORRECTABLE) ? \ -- cgit v1.2.3 From 1b4ffcf8432f7945e0bd0571f10a2f2bd1dbd850 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:09:58 +0900 Subject: PCI: pcie, aer: init struct aer_err_info for reuse In case of multiple errors, struct aer_err_info would be reused among all reported devices. So the info->status should be initialized before recycled. Otherwise error of one device might be reported as the error of another device. Also info->flags has similar problem on reporting TLP header. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index d3f6df40b0d..4d67db8dd0d 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -708,6 +708,9 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { int pos; + info->status = 0; + info->flags &= ~AER_TLP_HEADER_VALID_FLAG; + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); /* The device might not support AER */ -- cgit v1.2.3 From 0d465f23502e0810443c90a9cf1cf5686c4af4f2 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:10:40 +0900 Subject: PCI: pcie, aer: fix report of multiple errors The flag AER_MULTI_ERROR_VALID_FLAG in info->flag does mean that the root port receives multiple error messages. Error messages can be posted from different devices, so it does not mean that each reported device has multiple errors. If there are multiple error devices and the root port has valid error source ID, it would be nice to report which device is the error source reported first. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_errprint.c | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 48f70fa7f68..a2a4b3903a7 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -185,6 +185,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { char *errmsg; int err_layer, agent; + int id = ((dev->bus->number << 8) | dev->devfn); AER_PR(info, "+------ PCI-Express Device Error ------+\n"); AER_PR(info, "Error Severity\t\t: %s\n", @@ -192,11 +193,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) if (info->status == 0) { AER_PR(info, "PCIE Bus Error type\t: (Unaccessible)\n"); - AER_PR(info, "Unaccessible Received\t: %s\n", - info->flags & AER_MULTI_ERROR_VALID_FLAG ? - "Multiple" : "First"); - AER_PR(info, "Unregistered Agent ID\t: %04x\n", - (dev->bus->number << 8) | dev->devfn); + AER_PR(info, "Unregistered Agent ID\t: %04x\n", id); } else { err_layer = AER_GET_LAYER_ERROR(info->severity, info->status); AER_PR(info, "PCIE Bus Error type\t: %s\n", @@ -206,15 +203,11 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) errmsg = aer_get_error_source_name(info->severity, info->status, errmsg_buff); - AER_PR(info, "%s\t: %s\n", errmsg, - info->flags & AER_MULTI_ERROR_VALID_FLAG ? - "Multiple" : "First"); + AER_PR(info, "%s\t:\n", errmsg); spin_unlock(&logbuf_lock); agent = AER_GET_AGENT(info->severity, info->status); - AER_PR(info, "%s\t\t: %04x\n", - aer_agent_string[agent], - (dev->bus->number << 8) | dev->devfn); + AER_PR(info, "%s\t\t: %04x\n", aer_agent_string[agent], id); AER_PR(info, "VendorID=%04xh, DeviceID=%04xh," " Bus=%02xh, Device=%02xh, Function=%02xh\n", @@ -236,4 +229,8 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) *(tlp + 13), *(tlp + 12)); } } + + if (info->id && info->error_dev_num > 1 && info->id == id) + AER_PR(info, "Error of this Agent(%04x) is reported first\n", + id); } -- cgit v1.2.3 From 24dbb7beb2a207f423006c46830dfaacca5a1139 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:11:29 +0900 Subject: PCI: pcie, aer: remove spinlock in aerdrv_errprint.c The static buffer errmsg_buff[] is used only for building error message in fixed format, and is protected by a spinlock. This patch removes this buffer and the spinlock. Signed-off-by: Hidetoshi Seto Reviewed-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_errprint.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index a2a4b3903a7..95c3f1ca807 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -152,38 +152,31 @@ static char *aer_agent_string[] = { "Transmitter ID" }; -static char *aer_get_error_source_name(int severity, - unsigned int status, - char errmsg_buff[]) +static void aer_print_error_source(struct aer_err_info *info) { int i; char *errmsg = NULL; for (i = 0; i < 32; i++) { - if (!(status & (1 << i))) + if (!(info->status & (1 << i))) continue; - if (severity == AER_CORRECTABLE) + if (info->severity == AER_CORRECTABLE) errmsg = aer_correctable_error_string[i]; else errmsg = aer_uncorrectable_error_string[i]; - if (!errmsg) { - sprintf(errmsg_buff, "Unknown Error Bit %2d ", i); - errmsg = errmsg_buff; - } + if (errmsg) + AER_PR(info, "%s\t:\n", errmsg); + else + AER_PR(info, "Unknown Error Bit %2d \t:\n", i); break; } - - return errmsg; } -static DEFINE_SPINLOCK(logbuf_lock); -static char errmsg_buff[100]; void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { - char *errmsg; int err_layer, agent; int id = ((dev->bus->number << 8) | dev->devfn); @@ -199,12 +192,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) AER_PR(info, "PCIE Bus Error type\t: %s\n", aer_error_layer[err_layer]); - spin_lock(&logbuf_lock); - errmsg = aer_get_error_source_name(info->severity, - info->status, - errmsg_buff); - AER_PR(info, "%s\t:\n", errmsg); - spin_unlock(&logbuf_lock); + aer_print_error_source(info); agent = AER_GET_AGENT(info->severity, info->status); AER_PR(info, "%s\t\t: %04x\n", aer_agent_string[agent], id); -- cgit v1.2.3 From 0d90c3ac0bb89acfbf481c8b06749b00eade6545 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:12:25 +0900 Subject: PCI: pcie, aer: refer mask state in mask register properly ERR_{,UN}CORRECTABLE_ERROR_MASK are set of error bits which linux know, set of PCI_ERR_COR_* and PCI_ERR_UNC_* defined in linux/pci_regs.h. This masks make aerdrv not to report errors of unknown bit, while aerdrv have ability to report such undefined errors as "Unknown Error Bit %2d". OTOH aerdrv_errprint does not have any check of setting in mask register. So it could report masked wrong error by finding bit in status without knowing that the bit is masked in the mask register. This patch changes aerdrv to use mask state in mask register propely instead of defined/hardcoded ERR_{,UN}CORRECTABLE_ERROR_MASK. This change prevents aerdrv from reporting masked error, and also enable reporting unknown errors. Signed-off-by: Hidetoshi Seto Reviewed-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 4 +--- drivers/pci/pcie/aer/aerdrv_core.c | 28 ++++++++++++---------------- drivers/pci/pcie/aer/aerdrv_errprint.c | 6 ++++-- 3 files changed, 17 insertions(+), 21 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 820ea73d25f..0db530db943 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -47,9 +47,6 @@ #define AER_TLP_HEADER_VALID_FLAG 0x00000001 #define AER_MULTI_ERROR_VALID_FLAG 0x00000002 -#define ERR_CORRECTABLE_ERROR_MASK 0x000031c1 -#define ERR_UNCORRECTABLE_ERROR_MASK 0x001ff010 - struct header_log_regs { unsigned int dw0; unsigned int dw1; @@ -65,6 +62,7 @@ struct aer_err_info { int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ int flags; unsigned int status; /* COR/UNCOR Error Status */ + unsigned int mask; /* COR/UNCOR Error Mask */ struct header_log_regs tlp; /* TLP Header */ }; diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 4d67db8dd0d..38b3933200c 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -236,24 +236,16 @@ static int find_device_iter(struct pci_dev *dev, void *data) status = 0; mask = 0; if (e_info->severity == AER_CORRECTABLE) { - pci_read_config_dword(dev, - pos + PCI_ERR_COR_STATUS, - &status); - pci_read_config_dword(dev, - pos + PCI_ERR_COR_MASK, - &mask); - if (status & ERR_CORRECTABLE_ERROR_MASK & ~mask) { + pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &mask); + if (status & ~mask) { add_error_device(e_info, dev); goto added; } } else { - pci_read_config_dword(dev, - pos + PCI_ERR_UNCOR_STATUS, - &status); - pci_read_config_dword(dev, - pos + PCI_ERR_UNCOR_MASK, - &mask); - if (status & ERR_UNCORRECTABLE_ERROR_MASK & ~mask) { + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &status); + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &mask); + if (status & ~mask) { add_error_device(e_info, dev); goto added; } @@ -720,7 +712,9 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) if (info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, &info->status); - if (!(info->status & ERR_CORRECTABLE_ERROR_MASK)) + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) return AER_UNSUCCESS; } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || info->severity == AER_NONFATAL) { @@ -728,7 +722,9 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) /* Link is still healthy for IO reads */ pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_STATUS, &info->status); - if (!(info->status & ERR_UNCORRECTABLE_ERROR_MASK)) + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, + &info->mask); + if (!(info->status & ~info->mask)) return AER_UNSUCCESS; if (info->status & AER_LOG_TLP_MASKS) { diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 95c3f1ca807..41bd1c753ac 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -154,11 +154,13 @@ static char *aer_agent_string[] = { static void aer_print_error_source(struct aer_err_info *info) { - int i; + int i, status; char *errmsg = NULL; + status = (info->status & ~info->mask); + for (i = 0; i < 32; i++) { - if (!(info->status & (1 << i))) + if (!(status & (1 << i))) continue; if (info->severity == AER_CORRECTABLE) -- cgit v1.2.3 From e7a0d92b19f438011ad76c41755b56ec2ef05f64 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:13:42 +0900 Subject: PCI: pcie, aer: report multiple/first error on a device Multiple bits might be set in the Uncorrectable Error Status register. But aer_print_error_source() only report a error of the lowest bit set in the error status register. So print strings for all bits unmasked and set. And check First Error Pointer to mark the error occured first. This FEP is not valid when the corresponing bit of the Uncorrectable Error Status register is not set, or unimplemented or undefined. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 1 + drivers/pci/pcie/aer/aerdrv_core.c | 6 +++++- drivers/pci/pcie/aer/aerdrv_errprint.c | 8 ++++---- 3 files changed, 10 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 0db530db943..436bf79e273 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -61,6 +61,7 @@ struct aer_err_info { u16 id; int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ int flags; + int first; unsigned int status; /* COR/UNCOR Error Status */ unsigned int mask; /* COR/UNCOR Error Mask */ struct header_log_regs tlp; /* TLP Header */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 38b3933200c..9ba1602aebf 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -698,7 +698,7 @@ static struct aer_err_source *get_e_source(struct aer_rpc *rpc) static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { - int pos; + int pos, temp; info->status = 0; info->flags &= ~AER_TLP_HEADER_VALID_FLAG; @@ -727,6 +727,10 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) if (!(info->status & ~info->mask)) return AER_UNSUCCESS; + /* Get First Error Pointer */ + pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); + info->first = PCI_ERR_CAP_FEP(temp); + if (info->status & AER_LOG_TLP_MASKS) { info->flags |= AER_TLP_HEADER_VALID_FLAG; pci_read_config_dword(dev, diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 41bd1c753ac..0bb91e28d5f 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -169,11 +169,11 @@ static void aer_print_error_source(struct aer_err_info *info) errmsg = aer_uncorrectable_error_string[i]; if (errmsg) - AER_PR(info, "%s\t:\n", errmsg); + AER_PR(info, "%s\t: %s\n", errmsg, + info->first == i ? "First" : ""); else - AER_PR(info, "Unknown Error Bit %2d \t:\n", i); - - break; + AER_PR(info, "Unknown Error Bit %2d \t: %s\n", + i, info->first == i ? "First" : ""); } } -- cgit v1.2.3 From 3472a18773bc6661ea7f8de2b4172db7e00b67e6 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:16:00 +0900 Subject: PCI: pcie, aer: remove unused macros Cleanup. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 436bf79e273..c44d9e12d06 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -16,9 +16,6 @@ #define AER_NONFATAL 0 #define AER_FATAL 1 #define AER_CORRECTABLE 2 -#define AER_UNCORRECTABLE 4 -#define AER_ERROR_MASK 0x001fffff -#define AER_ERROR(d) (d & AER_ERROR_MASK) /* Root Error Status Register Bits */ #define ROOT_ERR_STATUS_MASKS 0x0f -- cgit v1.2.3 From 273024ded7b364e1305a31bf4eb197870284f279 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:16:20 +0900 Subject: PCI: pcie, aer: flags to bits Compact struct and codes. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 19 +++++++++++-------- drivers/pci/pcie/aer/aerdrv_core.c | 12 ++++++------ drivers/pci/pcie/aer/aerdrv_errprint.c | 6 +++--- 3 files changed, 20 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index c44d9e12d06..78c977cec47 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -40,10 +40,6 @@ PCI_ERR_UNC_UNX_COMP| \ PCI_ERR_UNC_MALF_TLP) -/* AER Error Info Flags */ -#define AER_TLP_HEADER_VALID_FLAG 0x00000001 -#define AER_MULTI_ERROR_VALID_FLAG 0x00000002 - struct header_log_regs { unsigned int dw0; unsigned int dw1; @@ -55,10 +51,17 @@ struct header_log_regs { struct aer_err_info { struct pci_dev *dev[AER_MAX_MULTI_ERR_DEVICES]; int error_dev_num; - u16 id; - int severity; /* 0:NONFATAL | 1:FATAL | 2:COR */ - int flags; - int first; + + unsigned int id:16; + + unsigned int severity:2; /* 0:NONFATAL | 1:FATAL | 2:COR */ + unsigned int __pad1:5; + unsigned int multi_error_valid:1; + + unsigned int first_error:5; + unsigned int __pad2:2; + unsigned int tlp_header_valid:1; + unsigned int status; /* COR/UNCOR Error Status */ unsigned int mask; /* COR/UNCOR Error Mask */ struct header_log_regs tlp; /* TLP Header */ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 9ba1602aebf..96264697446 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -196,7 +196,7 @@ static int find_device_iter(struct pci_dev *dev, void *data) * If there is no multiple error, we stop * or continue based on the id comparing. */ - if (!(e_info->flags & AER_MULTI_ERROR_VALID_FLAG)) + if (!e_info->multi_error_valid) return result; /* @@ -254,7 +254,7 @@ static int find_device_iter(struct pci_dev *dev, void *data) return 0; added: - if (e_info->flags & AER_MULTI_ERROR_VALID_FLAG) + if (e_info->multi_error_valid) return 0; else return 1; @@ -701,7 +701,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) int pos, temp; info->status = 0; - info->flags &= ~AER_TLP_HEADER_VALID_FLAG; + info->tlp_header_valid = 0; pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); @@ -729,10 +729,10 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) /* Get First Error Pointer */ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); - info->first = PCI_ERR_CAP_FEP(temp); + info->first_error = PCI_ERR_CAP_FEP(temp); if (info->status & AER_LOG_TLP_MASKS) { - info->flags |= AER_TLP_HEADER_VALID_FLAG; + info->tlp_header_valid = 1; pci_read_config_dword(dev, pos + PCI_ERR_HEADER_LOG, &info->tlp.dw0); pci_read_config_dword(dev, @@ -811,7 +811,7 @@ static void aer_isr_one_error(struct pcie_device *p_device, if (e_src->status & (PCI_ERR_ROOT_MULTI_COR_RCV | PCI_ERR_ROOT_MULTI_UNCOR_RCV)) - e_info->flags |= AER_MULTI_ERROR_VALID_FLAG; + e_info->multi_error_valid = 1; find_source_device(p_device->port, e_info); aer_process_err_devices(p_device, e_info); diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 0bb91e28d5f..f0bdf0ff859 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -170,10 +170,10 @@ static void aer_print_error_source(struct aer_err_info *info) if (errmsg) AER_PR(info, "%s\t: %s\n", errmsg, - info->first == i ? "First" : ""); + info->first_error == i ? "First" : ""); else AER_PR(info, "Unknown Error Bit %2d \t: %s\n", - i, info->first == i ? "First" : ""); + i, info->first_error == i ? "First" : ""); } } @@ -207,7 +207,7 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn)); - if (info->flags & AER_TLP_HEADER_VALID_FLAG) { + if (info->tlp_header_valid) { unsigned char *tlp = (unsigned char *) &info->tlp; AER_PR(info, "TLP Header:\n"); AER_PR(info, "%02x%02x%02x%02x %02x%02x%02x%02x" -- cgit v1.2.3 From 79e4b89be81b5e53bc4cb51788ca7a45cadb4ef3 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:16:45 +0900 Subject: PCI: pcie, aer: change error print format Use dev_printk like format. Sample (real machine + dummy error injected by aer-inject): - Before: +------ PCI-Express Device Error ------+ Error Severity : Corrected PCIE Bus Error type : Data Link Layer Bad TLP : Receiver ID : 2800 VendorID=8086h, DeviceID=1096h, Bus=28h, Device=00h, Function=00h +------ PCI-Express Device Error ------+ Error Severity : Corrected PCIE Bus Error type : Data Link Layer Bad TLP : Bad DLLP : Receiver ID : 2801 VendorID=8086h, DeviceID=1096h, Bus=28h, Device=00h, Function=01h Error of this Agent(2801) is reported first - After: pcieport-driver 0000:00:02.0: AER: Multiple Corrected error received: id=2801 e1000e 0000:28:00.0: PCIE Bus Error: severity=Corrected, type=Data Link Layer, id=2800(Receiver ID) e1000e 0000:28:00.0: device [8086:1096] error status/mask=00000040/00000000 e1000e 0000:28:00.0: [ 6] Bad TLP e1000e 0000:28:00.1: PCIE Bus Error: severity=Corrected, type=Data Link Layer, id=2801(Receiver ID) e1000e 0000:28:00.1: device [8086:1096] error status/mask=000000c0/00000000 e1000e 0000:28:00.1: [ 6] Bad TLP e1000e 0000:28:00.1: [ 7] Bad DLLP e1000e 0000:28:00.1: Error of this Agent(2801) is reported first Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 1 + drivers/pci/pcie/aer/aerdrv_core.c | 2 + drivers/pci/pcie/aer/aerdrv_errprint.c | 68 ++++++++++++++++++---------------- 3 files changed, 39 insertions(+), 32 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 78c977cec47..f9979eb56fb 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -124,6 +124,7 @@ extern void aer_delete_rootport(struct aer_rpc *rpc); extern int aer_init(struct pcie_device *dev); extern void aer_isr(struct work_struct *work); extern void aer_print_error(struct pci_dev *dev, struct aer_err_info *info); +extern void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info); extern irqreturn_t aer_irq(int irq, void *context); #ifdef CONFIG_ACPI diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 96264697446..d9185cd2d90 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -813,6 +813,8 @@ static void aer_isr_one_error(struct pcie_device *p_device, PCI_ERR_ROOT_MULTI_UNCOR_RCV)) e_info->multi_error_valid = 1; + aer_print_port_info(p_device->port, e_info); + find_source_device(p_device->port, e_info); aer_process_err_devices(p_device, e_info); } diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index f0bdf0ff859..44acde72294 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -57,9 +57,10 @@ (e & AER_DATA_LINK_LAYER_ERROR_MASK(t)) ? AER_DATA_LINK_LAYER_ERROR : \ AER_TRANSACTION_LAYER_ERROR) -#define AER_PR(info, fmt, args...) \ - printk("%s" fmt, (info->severity == AER_CORRECTABLE) ? \ - KERN_WARNING : KERN_ERR, ## args) +#define AER_PR(info, pdev, fmt, args...) \ + printk("%s%s %s: " fmt, (info->severity == AER_CORRECTABLE) ? \ + KERN_WARNING : KERN_ERR, dev_driver_string(&pdev->dev), \ + dev_name(&pdev->dev), ## args) /* * AER error strings @@ -152,7 +153,7 @@ static char *aer_agent_string[] = { "Transmitter ID" }; -static void aer_print_error_source(struct aer_err_info *info) +static void __aer_print_error(struct aer_err_info *info, struct pci_dev *dev) { int i, status; char *errmsg = NULL; @@ -169,48 +170,44 @@ static void aer_print_error_source(struct aer_err_info *info) errmsg = aer_uncorrectable_error_string[i]; if (errmsg) - AER_PR(info, "%s\t: %s\n", errmsg, - info->first_error == i ? "First" : ""); + AER_PR(info, dev, " [%2d] %s%s\n", i, errmsg, + info->first_error == i ? " (First)" : ""); else - AER_PR(info, "Unknown Error Bit %2d \t: %s\n", - i, info->first_error == i ? "First" : ""); + AER_PR(info, dev, " [%2d] Unknown Error Bit%s\n", i, + info->first_error == i ? " (First)" : ""); } } void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { - int err_layer, agent; int id = ((dev->bus->number << 8) | dev->devfn); - AER_PR(info, "+------ PCI-Express Device Error ------+\n"); - AER_PR(info, "Error Severity\t\t: %s\n", - aer_error_severity_string[info->severity]); - if (info->status == 0) { - AER_PR(info, "PCIE Bus Error type\t: (Unaccessible)\n"); - AER_PR(info, "Unregistered Agent ID\t: %04x\n", id); + AER_PR(info, dev, + "PCIE Bus Error: severity=%s, type=Unaccessible, " + "id=%04x(Unregistered Agent ID)\n", + aer_error_severity_string[info->severity], id); } else { - err_layer = AER_GET_LAYER_ERROR(info->severity, info->status); - AER_PR(info, "PCIE Bus Error type\t: %s\n", - aer_error_layer[err_layer]); - - aer_print_error_source(info); + int layer, agent; + layer = AER_GET_LAYER_ERROR(info->severity, info->status); agent = AER_GET_AGENT(info->severity, info->status); - AER_PR(info, "%s\t\t: %04x\n", aer_agent_string[agent], id); - AER_PR(info, "VendorID=%04xh, DeviceID=%04xh," - " Bus=%02xh, Device=%02xh, Function=%02xh\n", - dev->vendor, - dev->device, - dev->bus->number, - PCI_SLOT(dev->devfn), - PCI_FUNC(dev->devfn)); + AER_PR(info, dev, + "PCIE Bus Error: severity=%s, type=%s, id=%04x(%s)\n", + aer_error_severity_string[info->severity], + aer_error_layer[layer], id, aer_agent_string[agent]); + + AER_PR(info, dev, + " device [%04x:%04x] error status/mask=%08x/%08x\n", + dev->vendor, dev->device, info->status, info->mask); + + __aer_print_error(info, dev); if (info->tlp_header_valid) { unsigned char *tlp = (unsigned char *) &info->tlp; - AER_PR(info, "TLP Header:\n"); - AER_PR(info, "%02x%02x%02x%02x %02x%02x%02x%02x" + AER_PR(info, dev, " TLP Header:" + " %02x%02x%02x%02x %02x%02x%02x%02x" " %02x%02x%02x%02x %02x%02x%02x%02x\n", *(tlp + 3), *(tlp + 2), *(tlp + 1), *tlp, *(tlp + 7), *(tlp + 6), *(tlp + 5), *(tlp + 4), @@ -221,6 +218,13 @@ void aer_print_error(struct pci_dev *dev, struct aer_err_info *info) } if (info->id && info->error_dev_num > 1 && info->id == id) - AER_PR(info, "Error of this Agent(%04x) is reported first\n", - id); + AER_PR(info, dev, + " Error of this Agent(%04x) is reported first\n", id); +} + +void aer_print_port_info(struct pci_dev *dev, struct aer_err_info *info) +{ + dev_info(&dev->dev, "AER: %s%s error received: id=%04x\n", + info->multi_error_valid ? "Multiple " : "", + aer_error_severity_string[info->severity], info->id); } -- cgit v1.2.3 From b1c089b7caf18905bd1d87136cf7b8c837254932 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Mon, 7 Sep 2009 17:16:59 +0900 Subject: PCI: pcie, aer: report all error before recovery This patch is required not to lost error records by action invoked on error recovery, such as slot reset etc. Following sample (real machine + dummy record injected by aer-inject) shows that record of 28:00.1 could not be retrieved by recovery of 28:00.0: - Before: pcieport-driver 0000:00:02.0: AER: Multiple Uncorrected (Non-Fatal) error received: id=2801 e1000e 0000:28:00.0: PCIE Bus Error: severity=Uncorrected (Non-Fatal), type=Transaction Layer, id=2800(Receiver ID) e1000e 0000:28:00.0: device [8086:1096] error status/mask=00001000/00100000 e1000e 0000:28:00.0: [12] Poisoned TLP (First) e1000e 0000:28:00.0: TLP Header: 00000000 00000001 00000002 00000003 e1000e 0000:28:00.0: broadcast error_detected message e1000e 0000:28:00.0: broadcast slot_reset message e1000e 0000:28:00.0: setting latency timer to 64 e1000e 0000:28:00.0: restoring config space at offset 0x1 (was 0x100547, writing 0x100147) e1000e 0000:28:00.0: PME# disabled e1000e 0000:28:00.0: PME# disabled e1000e 0000:28:00.1: setting latency timer to 64 e1000e 0000:28:00.1: restoring config space at offset 0x1 (was 0x100547, writing 0x100147) e1000e 0000:28:00.1: PME# disabled e1000e 0000:28:00.1: PME# disabled e1000e 0000:28:00.0: broadcast resume message e1000e 0000:28:00.0: AER driver successfully recovered e1000e: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX - After: pcieport-driver 0000:00:02.0: AER: Multiple Uncorrected (Non-Fatal) error received: id=2801 e1000e 0000:28:00.0: PCIE Bus Error: severity=Uncorrected (Non-Fatal), type=Transaction Layer, id=2800(Receiver ID) e1000e 0000:28:00.0: device [8086:1096] error status/mask=00001000/00100000 e1000e 0000:28:00.0: [12] Poisoned TLP (First) e1000e 0000:28:00.0: TLP Header: 00000000 00000001 00000002 00000003 e1000e 0000:28:00.1: PCIE Bus Error: severity=Uncorrected (Non-Fatal), type=Transaction Layer, id=2801(Receiver ID) e1000e 0000:28:00.1: device [8086:1096] error status/mask=00081000/00100000 e1000e 0000:28:00.1: [12] Poisoned TLP (First) e1000e 0000:28:00.1: [19] ECRC e1000e 0000:28:00.1: TLP Header: 00000000 00000001 00000002 00000003 e1000e 0000:28:00.1: Error of this Agent(2801) is reported first e1000e 0000:28:00.0: broadcast error_detected message e1000e 0000:28:00.0: broadcast slot_reset message e1000e 0000:28:00.0: setting latency timer to 64 e1000e 0000:28:00.0: restoring config space at offset 0x1 (was 0x100547, writing 0x100147) e1000e 0000:28:00.0: PME# disabled e1000e 0000:28:00.0: PME# disabled e1000e 0000:28:00.1: setting latency timer to 64 e1000e 0000:28:00.1: restoring config space at offset 0x1 (was 0x100547, writing 0x100147) e1000e 0000:28:00.1: PME# disabled e1000e 0000:28:00.1: PME# disabled e1000e 0000:28:00.0: broadcast resume message e1000e 0000:28:00.0: AER driver successfully recovered e1000e: eth0 NIC Link is Up 1000 Mbps Full Duplex, Flow Control: RX/TX Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.h | 2 -- drivers/pci/pcie/aer/aerdrv_core.c | 29 ++++++++++++++++++----------- 2 files changed, 18 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index f9979eb56fb..bd833ea3ba4 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -29,8 +29,6 @@ #define ERR_COR_ID(d) (d & 0xffff) #define ERR_UNCOR_ID(d) (d >> 16) -#define AER_SUCCESS 0 -#define AER_UNSUCCESS 1 #define AER_ERROR_SOURCES_MAX 100 #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index d9185cd2d90..9f5ccbeb4fa 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -696,6 +696,13 @@ static struct aer_err_source *get_e_source(struct aer_rpc *rpc) return e_source; } +/** + * get_device_error_info - read error status from dev and store it to info + * @dev: pointer to the device expected to have a error record + * @info: pointer to structure to store the error record + * + * Return 1 on success, 0 on error. + */ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) { int pos, temp; @@ -707,7 +714,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) /* The device might not support AER */ if (!pos) - return AER_SUCCESS; + return 1; if (info->severity == AER_CORRECTABLE) { pci_read_config_dword(dev, pos + PCI_ERR_COR_STATUS, @@ -715,7 +722,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, &info->mask); if (!(info->status & ~info->mask)) - return AER_UNSUCCESS; + return 0; } else if (dev->hdr_type & PCI_HEADER_TYPE_BRIDGE || info->severity == AER_NONFATAL) { @@ -725,7 +732,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, &info->mask); if (!(info->status & ~info->mask)) - return AER_UNSUCCESS; + return 0; /* Get First Error Pointer */ pci_read_config_dword(dev, pos + PCI_ERR_CAP, &temp); @@ -744,7 +751,7 @@ static int get_device_error_info(struct pci_dev *dev, struct aer_err_info *info) } } - return AER_SUCCESS; + return 1; } static inline void aer_process_err_devices(struct pcie_device *p_device, @@ -758,14 +765,14 @@ static inline void aer_process_err_devices(struct pcie_device *p_device, e_info->id); } + /* Report all before handle them, not to lost records by reset etc. */ for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { - if (get_device_error_info(e_info->dev[i], e_info) == - AER_SUCCESS) { + if (get_device_error_info(e_info->dev[i], e_info)) aer_print_error(e_info->dev[i], e_info); - handle_error_source(p_device, - e_info->dev[i], - e_info); - } + } + for (i = 0; i < e_info->error_dev_num && e_info->dev[i]; i++) { + if (get_device_error_info(e_info->dev[i], e_info)) + handle_error_source(p_device, e_info->dev[i], e_info); } } @@ -870,5 +877,5 @@ int aer_init(struct pcie_device *dev) if (aer_osc_setup(dev) && !forceload) return -ENXIO; - return AER_SUCCESS; + return 0; } -- cgit v1.2.3 From 0ba379ec0fb182a87b8891c5754abbcd9c035b4f Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Sun, 6 Sep 2009 21:48:35 -0700 Subject: PCI: Simplify hotplug mch quirk. There is a very old quirk for the intel E7502 E7320 and E7525 memory controller hubs that disables usage of msi interrupts on pcie hotplug bridges of those devices, and disables changing the affinity of irqs. Today all we have to do to disable msi on a specific device is to set dev->no_msi, which is much more straightforward than the previous logic. The re-running of this fixup after pci hotplug happens below these devices is totally bogus. All of the state we change is pure software state and we don't change the hardware at all. Which means hotplug on the lower devices doesn't have a chance to change this state. So we can safely remove the special case from the pciehp driver and the pcie portdriver. I suspect the special case was someone's expermental debug code that slipped in. Certainly it isn't mentioned in commit 6fb8880a61510295aece04a542767161f624dffe aka BKrev: 41966101LJ_ogfOU0m2aE6teZfQnuQ where the code first appears. Reviewed-by: Kenji Kaneshige Signed-off-by: "Eric W. Biederman" Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_ctrl.c | 5 ----- drivers/pci/hotplug/pciehp_pci.c | 5 ----- drivers/pci/pci.h | 1 - drivers/pci/pcie/portdrv_core.c | 5 ----- drivers/pci/quirks.c | 5 ++--- 5 files changed, 2 insertions(+), 19 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 8aab8edf123..b97cb4c3e0f 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -246,11 +246,6 @@ static int board_added(struct slot *p_slot) goto err_exit; } - /* - * Some PCI Express root ports require fixup after hot-plug operation. - */ - if (pcie_mch_quirk) - pci_fixup_device(pci_fixup_final, ctrl->pci_dev); if (PWR_LED(ctrl)) p_slot->hpc_ops->green_led_on(p_slot); diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index 10f9566ccee..af295d080a0 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -285,11 +285,6 @@ int pciehp_unconfigure_device(struct slot *p_slot) } pci_dev_put(temp); } - /* - * Some PCI Express root ports require fixup after hot-plug operation. - */ - if (pcie_mch_quirk) - pci_fixup_device(pci_fixup_final, p_slot->ctrl->pci_dev); return rc; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 73d9d92715a..d92d1954a2f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -134,7 +134,6 @@ static inline int pci_no_d1d2(struct pci_dev *dev) return (dev->no_d1d2 || parent_dstates); } -extern int pcie_mch_quirk; extern struct device_attribute pci_dev_attrs[]; extern struct device_attribute dev_attr_cpuaffinity; extern struct device_attribute dev_attr_cpulistaffinity; diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 13ffdc35ea0..af9bca3139e 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -191,10 +191,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) int irq, interrupt_mode = PCIE_PORT_NO_IRQ; int i; - /* Check MSI quirk */ - if (port_data->port_type == PCIE_RC_PORT && pcie_mch_quirk) - goto Fallback; - /* Try to use MSI-X if supported */ if (!pcie_port_enable_msix(dev, vectors, mask)) return PCIE_PORT_MSIX_MODE; @@ -203,7 +199,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (!pci_enable_msi(dev)) interrupt_mode = PCIE_PORT_MSI_MODE; - Fallback: if (interrupt_mode == PCIE_PORT_NO_IRQ && dev->pin) interrupt_mode = PCIE_PORT_INTx_MODE; diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 10731373d00..e3034379a6c 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -31,8 +31,6 @@ int isa_dma_bridge_buggy; EXPORT_SYMBOL(isa_dma_bridge_buggy); int pci_pci_problems; EXPORT_SYMBOL(pci_pci_problems); -int pcie_mch_quirk; -EXPORT_SYMBOL(pcie_mch_quirk); #ifdef CONFIG_PCI_QUIRKS /* @@ -1500,7 +1498,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_EESSC, quirk_a static void __devinit quirk_pcie_mch(struct pci_dev *pdev) { - pcie_mch_quirk = 1; + pci_msi_off(pdev); + pdev->no_msi = 1; } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7520_MCH, quirk_pcie_mch); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_E7320_MCH, quirk_pcie_mch); -- cgit v1.2.3 From 28760489a3f1e136c5ae8581c0fa8f63511f2f4c Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Wed, 9 Sep 2009 14:09:24 -0700 Subject: PCI: pcie: Ensure hotplug ports have a minimum number of resources In general a BIOS may goof or we may hotplug in a hotplug controller. In either case the kernel needs to reserve resources for plugging in more devices in the future instead of creating a minimal resource assignment. We already do this for cardbus bridges I am just adding a variant for pcie bridges. v2: Make testing for pcie hotplug bridges based on a flag. So far we only set the flag for pcie but a header_quirk could easily be added for the non-standard pci hotplug bridges. Signed-off-by: Eric W. Biederman Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 10 ++++++++++ drivers/pci/probe.c | 18 ++++++++++++++++++ drivers/pci/setup-bus.c | 22 +++++++++++++++++----- 3 files changed, 45 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bd993351db4..8c663d628d0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -41,6 +41,12 @@ int pci_domains_supported = 1; unsigned long pci_cardbus_io_size = DEFAULT_CARDBUS_IO_SIZE; unsigned long pci_cardbus_mem_size = DEFAULT_CARDBUS_MEM_SIZE; +#define DEFAULT_HOTPLUG_IO_SIZE (256) +#define DEFAULT_HOTPLUG_MEM_SIZE (2*1024*1024) +/* pci=hpmemsize=nnM,hpiosize=nn can override this */ +unsigned long pci_hotplug_io_size = DEFAULT_HOTPLUG_IO_SIZE; +unsigned long pci_hotplug_mem_size = DEFAULT_HOTPLUG_MEM_SIZE; + /** * pci_bus_max_busnr - returns maximum PCI bus number of given bus' children * @bus: pointer to PCI bus structure to search @@ -2732,6 +2738,10 @@ static int __init pci_setup(char *str) strlen(str + 19)); } else if (!strncmp(str, "ecrc=", 5)) { pcie_ecrc_get_policy(str + 5); + } else if (!strncmp(str, "hpiosize=", 9)) { + pci_hotplug_io_size = memparse(str + 9, &str); + } else if (!strncmp(str, "hpmemsize=", 10)) { + pci_hotplug_mem_size = memparse(str + 10, &str); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index ab52840f475..882383b61d3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -697,6 +697,23 @@ static void set_pcie_port_type(struct pci_dev *pdev) pdev->pcie_type = (reg16 & PCI_EXP_FLAGS_TYPE) >> 4; } +static void set_pcie_hotplug_bridge(struct pci_dev *pdev) +{ + int pos; + u16 reg16; + u32 reg32; + + pos = pci_find_capability(pdev, PCI_CAP_ID_EXP); + if (!pos) + return; + pci_read_config_word(pdev, pos + PCI_EXP_FLAGS, ®16); + if (!(reg16 & PCI_EXP_FLAGS_SLOT)) + return; + pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, ®32); + if (reg32 & PCI_EXP_SLTCAP_HPC) + pdev->is_hotplug_bridge = 1; +} + #define LEGACY_IO_RESOURCE (IORESOURCE_IO | IORESOURCE_PCI_FIXED) /** @@ -804,6 +821,7 @@ int pci_setup_device(struct pci_dev *dev) pci_read_irq(dev); dev->transparent = ((dev->class & 0xff) == 1); pci_read_bases(dev, 2, PCI_ROM_ADDRESS1); + set_pcie_hotplug_bridge(dev); break; case PCI_HEADER_TYPE_CARDBUS: /* CardBus bridge header */ diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7c443b4583a..cb1a027eb55 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -309,7 +309,7 @@ static struct resource *find_free_bus_resource(struct pci_bus *bus, unsigned lon since these windows have 4K granularity and the IO ranges of non-bridge PCI devices are limited to 256 bytes. We must be careful with the ISA aliasing though. */ -static void pbus_size_io(struct pci_bus *bus) +static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size) { struct pci_dev *dev; struct resource *b_res = find_free_bus_resource(bus, IORESOURCE_IO); @@ -336,6 +336,8 @@ static void pbus_size_io(struct pci_bus *bus) size1 += r_size; } } + if (size < min_size) + size = min_size; /* To be fixed in 2.5: we should have sort of HAVE_ISA flag in the struct pci_bus. */ #if defined(CONFIG_ISA) || defined(CONFIG_EISA) @@ -354,7 +356,8 @@ static void pbus_size_io(struct pci_bus *bus) /* Calculate the size of the bus and minimal alignment which guarantees that all child resources fit in this size. */ -static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long type) +static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, + unsigned long type, resource_size_t min_size) { struct pci_dev *dev; resource_size_t min_align, align, size; @@ -404,6 +407,8 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, unsigned long mem64_mask &= r->flags & IORESOURCE_MEM_64; } } + if (size < min_size) + size = min_size; align = 0; min_align = 0; @@ -483,6 +488,7 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) { struct pci_dev *dev; unsigned long mask, prefmask; + resource_size_t min_mem_size = 0, min_io_size = 0; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -512,8 +518,12 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) case PCI_CLASS_BRIDGE_PCI: pci_bridge_check_ranges(bus); + if (bus->self->is_hotplug_bridge) { + min_io_size = pci_hotplug_io_size; + min_mem_size = pci_hotplug_mem_size; + } default: - pbus_size_io(bus); + pbus_size_io(bus, min_io_size); /* If the bridge supports prefetchable range, size it separately. If it doesn't, or its prefetchable window has already been allocated by arch code, try @@ -521,9 +531,11 @@ void __ref pci_bus_size_bridges(struct pci_bus *bus) resources. */ mask = IORESOURCE_MEM; prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pbus_size_mem(bus, prefmask, prefmask)) + if (pbus_size_mem(bus, prefmask, prefmask, min_mem_size)) mask = prefmask; /* Success, size non-prefetch only. */ - pbus_size_mem(bus, mask, IORESOURCE_MEM); + else + min_mem_size += min_mem_size; + pbus_size_mem(bus, mask, IORESOURCE_MEM, min_mem_size); break; } } -- cgit v1.2.3 From 5bcc2fb4e8157d829a38093b98e23329ac8acff7 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Sep 2009 23:12:59 +0200 Subject: PCI PM: Simplify PCI wake-up code Rework the PCI wake-up code so that it's easier to read without changing the functionality. Reviewed-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 10 +++++++--- drivers/pci/pci.c | 25 ++++++++++++++----------- 2 files changed, 21 insertions(+), 14 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index ea15b053745..e798c4736a6 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -111,12 +111,16 @@ static bool acpi_pci_can_wakeup(struct pci_dev *dev) static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) { - int error = acpi_pm_device_sleep_wake(&dev->dev, enable); + int error; + if (!acpi_pci_can_wakeup(dev)) + return 0; + + error = acpi_pm_device_sleep_wake(&dev->dev, enable); if (!error) - dev_printk(KERN_INFO, &dev->dev, - "wake-up capability %s by ACPI\n", + dev_info(&dev->dev, "wake-up capability %s by ACPI\n", enable ? "enabled" : "disabled"); + return error; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 8c663d628d0..84a6d6d30c5 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1220,8 +1220,7 @@ void pci_pme_active(struct pci_dev *dev, bool enable) */ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) { - int error = 0; - bool pme_done = false; + int ret = 0; if (enable && !device_may_wakeup(&dev->dev)) return -EINVAL; @@ -1232,18 +1231,22 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) * enable. To disable wake-up we call the platform first, for symmetry. */ - if (!enable && platform_pci_can_wakeup(dev)) - error = platform_pci_sleep_wake(dev, false); - - if (!enable || pci_pme_capable(dev, state)) { - pci_pme_active(dev, enable); - pme_done = true; - } + if (enable) { + int error; - if (enable && platform_pci_can_wakeup(dev)) + if (pci_pme_capable(dev, state)) + pci_pme_active(dev, true); + else + ret = 1; error = platform_pci_sleep_wake(dev, true); + if (ret) + ret = error; + } else { + platform_pci_sleep_wake(dev, false); + pci_pme_active(dev, false); + } - return pme_done ? 0 : error; + return ret; } /** -- cgit v1.2.3 From df8db91fc3b543d373afa61beef35b072eea1368 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Sep 2009 23:13:49 +0200 Subject: PCI / ACPI PM: Rework some debug messages Move a debug message from acpi_pci_sleep_wake() to acpi_pm_device_sleep_wake() and use the standard dev_*() macros in there. Reviewed-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index e798c4736a6..0bddd787490 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -111,17 +111,8 @@ static bool acpi_pci_can_wakeup(struct pci_dev *dev) static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) { - int error; - - if (!acpi_pci_can_wakeup(dev)) - return 0; - - error = acpi_pm_device_sleep_wake(&dev->dev, enable); - if (!error) - dev_info(&dev->dev, "wake-up capability %s by ACPI\n", - enable ? "enabled" : "disabled"); - - return error; + return acpi_pci_can_wakeup(dev) ? + acpi_pm_device_sleep_wake(&dev->dev, enable) : 0; } static struct pci_platform_pm_ops acpi_pci_platform_pm = { -- cgit v1.2.3 From e80bb09d2c73d76a2a4cd79e4a83802dd901c642 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Sep 2009 23:14:49 +0200 Subject: PCI PM: Introduce device flag wakeup_prepared Introduce a new PCI device flag, wakeup_prepared, to prevent PCI wake-up preparation code from being executed twice in a row for the same device and for the same purpose. Reviewed-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 84a6d6d30c5..dcdfb2212ca 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1225,6 +1225,10 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) if (enable && !device_may_wakeup(&dev->dev)) return -EINVAL; + /* Don't do the same thing twice in a row for one device. */ + if (!!enable == !!dev->wakeup_prepared) + return 0; + /* * According to "PCI System Architecture" 4th ed. by Tom Shanley & Don * Anderson we should be doing PME# wake enable followed by ACPI wake @@ -1241,9 +1245,12 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) error = platform_pci_sleep_wake(dev, true); if (ret) ret = error; + if (!ret) + dev->wakeup_prepared = true; } else { platform_pci_sleep_wake(dev, false); pci_pme_active(dev, false); + dev->wakeup_prepared = false; } return ret; @@ -1365,6 +1372,7 @@ void pci_pm_init(struct pci_dev *dev) int pm; u16 pmc; + dev->wakeup_prepared = false; dev->pm_cap = 0; /* find PCI PM capability in list */ -- cgit v1.2.3 From 0baed8da1ed91b664759f6c7f955b3a804457389 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Tue, 8 Sep 2009 23:16:24 +0200 Subject: PCI / ACPI PM: Propagate wake-up enable for devices w/o ACPI support Some PCI devices (not PCI Express), like PCI add-on cards, can generate PME#, but they don't have any special platform wake-up support. For this reason, even if they generate PME# to wake up the system from a sleep state, wake-up events are not generated by the platform. It turns out that, at least on some systems, PCI bridges and the PCI host bridge have ACPI GPEs associated with them that, if enabled to generate wake-up events, allow the system to wake up if one of the add-on devices asserts PME# while the system is in a sleep state. Following this observation, if a PCI device without direct ACPI wake-up support is prepared to wake up the system during a transition into a sleep state (eg. suspend to RAM), try to configure the bridges on the path from the device to the root bridge to wake-up the system. Reviewed-by: Matthew Garrett Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 0bddd787490..33317df4769 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -109,10 +109,32 @@ static bool acpi_pci_can_wakeup(struct pci_dev *dev) return handle ? acpi_bus_can_wakeup(handle) : false; } +static void acpi_pci_propagate_wakeup_enable(struct pci_bus *bus, bool enable) +{ + while (bus->parent) { + struct pci_dev *bridge = bus->self; + int ret; + + ret = acpi_pm_device_sleep_wake(&bridge->dev, enable); + if (!ret || bridge->is_pcie) + return; + bus = bus->parent; + } + + /* We have reached the root bus. */ + if (bus->bridge) + acpi_pm_device_sleep_wake(bus->bridge, enable); +} + static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable) { - return acpi_pci_can_wakeup(dev) ? - acpi_pm_device_sleep_wake(&dev->dev, enable) : 0; + if (acpi_pci_can_wakeup(dev)) + return acpi_pm_device_sleep_wake(&dev->dev, enable); + + if (!dev->is_pcie) + acpi_pci_propagate_wakeup_enable(dev->bus, enable); + + return 0; } static struct pci_platform_pm_ops acpi_pci_platform_pm = { -- cgit v1.2.3 From e2dd90b1ad4c61ecb52f2424049d91ce6ccc1f17 Mon Sep 17 00:00:00 2001 From: Shane Huang Date: Wed, 29 Jul 2009 11:34:49 +0800 Subject: ahci: Add AMD SB900 SATA/IDE controller device IDs Add AMD SB900 SATA/IDE controller device IDs. Signed-off-by: Shane Huang Signed-off-by: Jeff Garzik --- drivers/pci/quirks.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 06b96562396..85ce23997be 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -992,7 +992,7 @@ DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82454NX, static void __devinit quirk_amd_ide_mode(struct pci_dev *pdev) { - /* set sb600/sb700/sb800 sata to ahci mode */ + /* set SBX00 SATA in IDE mode to AHCI mode */ u8 tmp; pci_read_config_byte(pdev, PCI_CLASS_DEVICE, &tmp); @@ -1011,6 +1011,8 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP600_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_ATI, PCI_DEVICE_ID_ATI_IXP700_SATA, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SATA_IDE, quirk_amd_ide_mode); +DECLARE_PCI_FIXUP_RESUME_EARLY(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SB900_SATA_IDE, quirk_amd_ide_mode); /* * Serverworks CSB5 IDE does not fully support native mode -- cgit v1.2.3 From 9965976a38f66fc170fc0528b3115bf1a8a58b0a Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 11 Sep 2009 08:46:07 -0700 Subject: PCI: pcie portdrv: remove unused variable Remove unused port_data variable left over from the MCH hotplug quirk cleanup. Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index af9bca3139e..52f84fca9f7 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -187,7 +187,6 @@ static int pcie_port_enable_msix(struct pci_dev *dev, int *vectors, int mask) */ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { - struct pcie_port_data *port_data = pci_get_drvdata(dev); int irq, interrupt_mode = PCIE_PORT_NO_IRQ; int i; -- cgit v1.2.3 From 6edd7679db92376ca54f328d6b0f12291c2dab35 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Thu, 10 Sep 2009 12:34:04 -0600 Subject: PCI Hotplug: acpiphp: find bridges the easy way Instead of constantly evaluating _ADR and _SEG over and over again, let's simplify our lives by using: acpi_pci_find_root() for root bridges acpi_get_pci_dev() for p2p bridges This change eliminates some copy 'n paste code and also allows us to simplify some internal interfaces. Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Cc: Bjorn Helgaas Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 81 ++++++++++---------------------------- 1 file changed, 21 insertions(+), 60 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 2e5f25969e1..e72e0adc068 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -277,14 +277,15 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) /* see if it's worth looking at this bridge */ -static int detect_ejectable_slots(struct pci_bus *pbus) +static int detect_ejectable_slots(acpi_handle handle) { - int found = acpi_pci_detect_ejectable(pbus); + int found; + struct pci_bus *pbus; + + pbus = pci_bus_from_handle(handle); + found = acpi_pci_detect_ejectable(pbus); if (!found) { - acpi_handle bridge_handle = acpi_pci_get_bridge_handle(pbus); - if (!bridge_handle) - return 0; - acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, is_pci_dock_device, (void *)&found, NULL); } return found; @@ -415,9 +416,10 @@ static inline void config_p2p_bridge_flags(struct acpiphp_bridge *bridge) /* allocate and initialize host bridge data structure */ -static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus) +static void add_host_bridge(acpi_handle *handle) { struct acpiphp_bridge *bridge; + struct acpi_pci_root *root = acpi_pci_find_root(handle); bridge = kzalloc(sizeof(struct acpiphp_bridge), GFP_KERNEL); if (bridge == NULL) @@ -426,7 +428,7 @@ static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus) bridge->type = BRIDGE_TYPE_HOST; bridge->handle = handle; - bridge->pci_bus = pci_bus; + bridge->pci_bus = root->bus; spin_lock_init(&bridge->res_lock); @@ -435,7 +437,7 @@ static void add_host_bridge(acpi_handle *handle, struct pci_bus *pci_bus) /* allocate and initialize PCI-to-PCI bridge data structure */ -static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev) +static void add_p2p_bridge(acpi_handle *handle) { struct acpiphp_bridge *bridge; @@ -449,8 +451,8 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev) bridge->handle = handle; config_p2p_bridge_flags(bridge); - bridge->pci_dev = pci_dev_get(pci_dev); - bridge->pci_bus = pci_dev->subordinate; + bridge->pci_dev = acpi_get_pci_dev(handle); + bridge->pci_bus = bridge->pci_dev->subordinate; if (!bridge->pci_bus) { err("This is not a PCI-to-PCI bridge!\n"); goto err; @@ -467,7 +469,7 @@ static void add_p2p_bridge(acpi_handle *handle, struct pci_dev *pci_dev) init_bridge_misc(bridge); return; err: - pci_dev_put(pci_dev); + pci_dev_put(bridge->pci_dev); kfree(bridge); return; } @@ -478,39 +480,21 @@ static acpi_status find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) { acpi_status status; - acpi_handle dummy_handle; - unsigned long long tmp; - int device, function; struct pci_dev *dev; - struct pci_bus *pci_bus = context; - - status = acpi_get_handle(handle, "_ADR", &dummy_handle); - if (ACPI_FAILURE(status)) - return AE_OK; /* continue */ - - status = acpi_evaluate_integer(handle, "_ADR", NULL, &tmp); - if (ACPI_FAILURE(status)) { - dbg("%s: _ADR evaluation failure\n", __func__); - return AE_OK; - } - - device = (tmp >> 16) & 0xffff; - function = tmp & 0xffff; - - dev = pci_get_slot(pci_bus, PCI_DEVFN(device, function)); + dev = acpi_get_pci_dev(handle); if (!dev || !dev->subordinate) goto out; /* check if this bridge has ejectable slots */ - if ((detect_ejectable_slots(dev->subordinate) > 0)) { + if ((detect_ejectable_slots(handle) > 0)) { dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); - add_p2p_bridge(handle, dev); + add_p2p_bridge(handle); } /* search P2P bridges under this p2p bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, dev->subordinate, NULL); + find_p2p_bridge, NULL, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge failed (error code = 0x%x)\n", status); @@ -525,9 +509,7 @@ static int add_bridge(acpi_handle handle) { acpi_status status; unsigned long long tmp; - int seg, bus; acpi_handle dummy_handle; - struct pci_bus *pci_bus; /* if the bridge doesn't have _STA, we assume it is always there */ status = acpi_get_handle(handle, "_STA", &dummy_handle); @@ -542,36 +524,15 @@ static int add_bridge(acpi_handle handle) return 0; } - /* get PCI segment number */ - status = acpi_evaluate_integer(handle, "_SEG", NULL, &tmp); - - seg = ACPI_SUCCESS(status) ? tmp : 0; - - /* get PCI bus number */ - status = acpi_evaluate_integer(handle, "_BBN", NULL, &tmp); - - if (ACPI_SUCCESS(status)) { - bus = tmp; - } else { - warn("can't get bus number, assuming 0\n"); - bus = 0; - } - - pci_bus = pci_find_bus(seg, bus); - if (!pci_bus) { - err("Can't find bus %04x:%02x\n", seg, bus); - return 0; - } - /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(pci_bus) > 0) { + if (detect_ejectable_slots(handle) > 0) { dbg("found PCI host-bus bridge with hot-pluggable slots\n"); - add_host_bridge(handle, pci_bus); + add_host_bridge(handle); } /* search P2P bridges under this host bridge */ status = acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - find_p2p_bridge, pci_bus, NULL); + find_p2p_bridge, NULL, NULL); if (ACPI_FAILURE(status)) warn("find_p2p_bridge failed (error code = 0x%x)\n", status); -- cgit v1.2.3 From 7f53866932fd08add06ee2f93ead129949158490 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Thu, 10 Sep 2009 12:34:09 -0600 Subject: PCI Hotplug: convert acpi_pci_detect_ejectable() to take an acpi_handle acpi_pci_detect_ejectable() goes through effort to convert its struct pci_bus arg to an acpi_handle, but every time we use this interface, we already have the handle available. So let's just use the handle instead of converting back and forth. Reviewed-by: Kenji Kaneshige Tested-by: Kenji Kaneshige Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 12 ++++++------ drivers/pci/hotplug/acpiphp_glue.c | 33 +++++++++++---------------------- drivers/pci/hotplug/pciehp_acpi.c | 7 ++++--- 3 files changed, 21 insertions(+), 31 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index eb159587d0b..ec3c039b7eb 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -500,18 +500,18 @@ check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) /** * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots - * @pbus - PCI bus to scan + * @handle - handle of the PCI bus to scan * * Returns 1 if the PCI bus has ACPI based ejectable slots, 0 otherwise. */ -int acpi_pci_detect_ejectable(struct pci_bus *pbus) +int acpi_pci_detect_ejectable(acpi_handle handle) { - acpi_handle handle; int found = 0; - if (!(handle = acpi_pci_get_bridge_handle(pbus))) - return 0; - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, + if (!handle) + return found; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, 1, check_hotplug, (void *)&found, NULL); return found; } diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index e72e0adc068..680c33635b6 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -62,22 +62,6 @@ 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) @@ -279,11 +263,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) /* see if it's worth looking at this bridge */ static int detect_ejectable_slots(acpi_handle handle) { - int found; - struct pci_bus *pbus; - - pbus = pci_bus_from_handle(handle); - found = acpi_pci_detect_ejectable(pbus); + int found = acpi_pci_detect_ejectable(handle); if (!found) { acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, is_pci_dock_device, (void *)&found, NULL); @@ -1364,7 +1344,16 @@ 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_bus *bus = pci_bus_from_handle(handle); + struct pci_bus *bus; + + if (acpi_is_root_bridge(handle)) { + struct acpi_pci_root *root = acpi_pci_find_root(handle); + bus = root->bus; + } else { + struct pci_dev *pdev = acpi_get_pci_dev(handle); + bus = pdev->subordinate; + pci_dev_put(pdev); + } pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 96048010e7d..7163e6a6cfa 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -47,7 +47,7 @@ int pciehp_acpi_slot_detection_check(struct pci_dev *dev) { if (slot_detection_mode != PCIEHP_DETECT_ACPI) return 0; - if (acpi_pci_detect_ejectable(dev->subordinate)) + if (acpi_pci_detect_ejectable(DEVICE_ACPI_HANDLE(&dev->dev))) return 0; return -ENODEV; } @@ -76,9 +76,9 @@ static int __init dummy_probe(struct pcie_device *dev) { int pos; u32 slot_cap; + acpi_handle handle; struct slot *slot, *tmp; struct pci_dev *pdev = dev->port; - struct pci_bus *pbus = pdev->subordinate; /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ if (pciehp_get_hp_hw_control_from_firmware(pdev)) return -ENODEV; @@ -94,7 +94,8 @@ static int __init dummy_probe(struct pcie_device *dev) dup_slot_id++; } list_add_tail(&slot->slot_list, &dummy_slots); - if (!acpi_slot_detected && acpi_pci_detect_ejectable(pbus)) + handle = DEVICE_ACPI_HANDLE(&pdev->dev); + if (!acpi_slot_detected && acpi_pci_detect_ejectable(handle)) acpi_slot_detected = 1; return -ENODEV; /* dummy driver always returns error */ } -- cgit v1.2.3 From e9d8288871efa0d98a1d1d1f17976b5b00a0234d Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 14 Sep 2009 22:25:11 +0200 Subject: PCI / PCIe portdrv: Fix pcie_portdrv_slot_reset() After commit c82f63e411f1b58427c103bd95af2863b1c96dd1 (PCI: check saved state before restore) pcie_portdrv_slot_reset() may not work correctly if dev->error_state is equal to pci_channel_io_frozen, because dev->state_saved need not be set at that time. Fix this issue by setting dev->state_saved before pci_restore_state() is called in pcie_portdrv_slot_reset(). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 091ce70051e..6df5c984a79 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -205,6 +205,7 @@ static pci_ers_result_t pcie_portdrv_slot_reset(struct pci_dev *dev) /* If fatal, restore cfg space for possible link reset at upstream */ if (dev->error_state == pci_channel_io_frozen) { + dev->state_saved = true; pci_restore_state(dev); pcie_portdrv_restore_config(dev); pci_enable_pcie_error_reporting(dev); -- cgit v1.2.3 From fdcdaf6c4feca025de2f5e1b6c8e08ba0649e85a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 14:36:41 -0600 Subject: PCI: use dev_printk in quirk messages Convert quirk printks to dev_printk(). Signed-off-by: Bjorn Helgaas CC: Olaf Dabrunz Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 27 ++++++++++++--------------- 1 file changed, 12 insertions(+), 15 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e3034379a6c..3b693942ebd 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -1567,10 +1567,8 @@ static void quirk_reroute_to_boot_interrupts_intel(struct pci_dev *dev) return; dev->irq_reroute_variant = INTEL_IRQ_REROUTE_VARIANT; - - printk(KERN_INFO "PCI quirk: reroute interrupts for 0x%04x:0x%04x\n", - dev->vendor, dev->device); - return; + dev_info(&dev->dev, "rerouting interrupts for [%04x:%04x]\n", + dev->vendor, dev->device); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_0, quirk_reroute_to_boot_interrupts_intel); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_80333_1, quirk_reroute_to_boot_interrupts_intel); @@ -1612,8 +1610,8 @@ static void quirk_disable_intel_boot_interrupt(struct pci_dev *dev) pci_config_word |= INTEL_6300_DISABLE_BOOT_IRQ; pci_write_config_word(dev, INTEL_6300_IOAPIC_ABAR, pci_config_word); - printk(KERN_INFO "disabled boot interrupt on device 0x%04x:0x%04x\n", - dev->vendor, dev->device); + dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n", + dev->vendor, dev->device); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_10, quirk_disable_intel_boot_interrupt); @@ -1645,8 +1643,8 @@ static void quirk_disable_broadcom_boot_interrupt(struct pci_dev *dev) pci_write_config_dword(dev, BC_HT1000_FEATURE_REG, pci_config_dword); - printk(KERN_INFO "disabled boot interrupts on PCI device" - "0x%04x:0x%04x\n", dev->vendor, dev->device); + dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n", + dev->vendor, dev->device); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_SERVERWORKS, PCI_DEVICE_ID_SERVERWORKS_HT1000SB, quirk_disable_broadcom_boot_interrupt); @@ -1676,8 +1674,8 @@ static void quirk_disable_amd_813x_boot_interrupt(struct pci_dev *dev) pci_config_dword &= ~AMD_813X_NOIOAMODE; pci_write_config_dword(dev, AMD_813X_MISC, pci_config_dword); - printk(KERN_INFO "disabled boot interrupts on PCI device " - "0x%04x:0x%04x\n", dev->vendor, dev->device); + dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n", + dev->vendor, dev->device); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_amd_813x_boot_interrupt); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8132_BRIDGE, quirk_disable_amd_813x_boot_interrupt); @@ -1693,14 +1691,13 @@ static void quirk_disable_amd_8111_boot_interrupt(struct pci_dev *dev) pci_read_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, &pci_config_word); if (!pci_config_word) { - printk(KERN_INFO "boot interrupts on PCI device 0x%04x:0x%04x " - "already disabled\n", - dev->vendor, dev->device); + dev_info(&dev->dev, "boot interrupts on device [%04x:%04x] " + "already disabled\n", dev->vendor, dev->device); return; } pci_write_config_word(dev, AMD_8111_PCI_IRQ_ROUTING, 0); - printk(KERN_INFO "disabled boot interrupts on PCI device " - "0x%04x:0x%04x\n", dev->vendor, dev->device); + dev_info(&dev->dev, "disabled boot interrupts on device [%04x:%04x]\n", + dev->vendor, dev->device); } DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt); DECLARE_PCI_FIXUP_RESUME(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8111_SMBUS, quirk_disable_amd_8111_boot_interrupt); -- cgit v1.2.3 From 999cce4a52d5abdda5d2cec6bac241899bc19e4c Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 Sep 2009 23:51:27 +0200 Subject: PCI PM: Return error codes from pci_pm_resume() Currently pci_pm_resume() always returns 0, which makes the error variable defined in there a bit pointless. Make pci_pm_resume() return error codes obtained from drivers' callbacks. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c66dc4341fa..dbfc93cb5d0 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -722,7 +722,7 @@ static int pci_pm_resume(struct device *dev) pci_pm_reenable_device(pci_dev); } - return 0; + return error; } #else /* !CONFIG_SUSPEND */ -- cgit v1.2.3 From 4b77b0a2ba27d64f58f16d8d4d48d8319dda36ff Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 Sep 2009 23:49:59 +0200 Subject: PCI: Clear saved_state after the state has been restored Some PCI devices fail if their standard configuration registers are restored twice in a row. Prevent this from happening by making pci_restore_state() clear the saved_state flag of the device right after the device's standard configuration registers have been populated with the previously saved values. Simplify PCI PM callbacks by removing the direct clearing of state_saved from them, as it shouldn't be necessary any more (except in pci_pm_thaw(), where it has to be cleared, so that the values saved during the "freeze" phase of hibernation are not used later by mistake). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 11 ++--------- drivers/pci/pci.c | 3 +++ drivers/pci/probe.c | 3 +++ 3 files changed, 8 insertions(+), 9 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index dbfc93cb5d0..ffc15e97d11 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -445,8 +445,6 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; - pci_dev->state_saved = false; - if (drv && drv->suspend) { pci_power_t prev = pci_dev->current_state; int error; @@ -542,7 +540,6 @@ static int pci_restore_standard_config(struct pci_dev *pci_dev) static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) { pci_restore_standard_config(pci_dev); - pci_dev->state_saved = false; pci_fixup_device(pci_fixup_resume_early, pci_dev); } @@ -608,8 +605,6 @@ static int pci_pm_suspend(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); - pci_dev->state_saved = false; - if (!pm) { pci_pm_default_suspend(pci_dev); goto Fixup; @@ -744,8 +739,6 @@ static int pci_pm_freeze(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); - pci_dev->state_saved = false; - if (!pm) { pci_pm_default_suspend(pci_dev); return 0; @@ -821,6 +814,8 @@ static int pci_pm_thaw(struct device *dev) pci_pm_reenable_device(pci_dev); } + pci_dev->state_saved = false; + return error; } @@ -832,8 +827,6 @@ static int pci_pm_poweroff(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); - pci_dev->state_saved = false; - if (!pm) { pci_pm_default_suspend(pci_dev); goto Fixup; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index dcdfb2212ca..6edecff0b41 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -854,6 +854,7 @@ pci_restore_state(struct pci_dev *dev) if (!dev->state_saved) return 0; + /* PCI Express register must be restored first */ pci_restore_pcie_state(dev); @@ -875,6 +876,8 @@ pci_restore_state(struct pci_dev *dev) pci_restore_msi_state(dev); pci_restore_iov_state(dev); + dev->state_saved = false; + return 0; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 882383b61d3..8105e32117f 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1032,6 +1032,9 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) /* Fix up broken headers */ pci_fixup_device(pci_fixup_header, dev); + /* Clear the state_saved flag. */ + dev->state_saved = false; + /* Initialize various capabilities */ pci_init_capabilities(dev); -- cgit v1.2.3 From dffb4bb560ed73937a52c68c304b232c743b49f7 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:05 -0600 Subject: PCI hotplug: acpiphp: remove superfluous _HPP/_HPX evaluation decode_hpp() looks up hotplug PCI parameters from ACPI and saves them in the acpiphp_bridge structure. These parameters (in bridge->hpp) are only used by the acpiphp_set_hpp_values() -> program_hpp() path. In that path, we always call decode_hpp() before program_hpp(), so there's no need to do it in init_bridge_misc(). Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Chiang Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 680c33635b6..e0623373615 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -300,9 +300,6 @@ static void init_bridge_misc(struct acpiphp_bridge *bridge) { acpi_status status; - /* decode ACPI 2.0 _HPP (hot plug parameters) */ - decode_hpp(bridge); - /* must be added to the list prior to calling register_slot */ list_add(&bridge->list, &bridge_list); -- cgit v1.2.3 From fca6825ad7382ae9df8ecda9068ac13ee9e343f4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:10 -0600 Subject: PCI hotplug: acpiphp: don't cache hotplug_params in acpiphp_bridge We always look up hotplug_params with decode_hpp() immediately before using them, so we don't need to save them in the acpiphp_bridge struct. This patch follows the example of program_fw_provided_values() in pciehp_pci.c and shpchp_pci.c by just keeping the params on the stack while we need them. Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Chiang Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp.h | 3 -- drivers/pci/hotplug/acpiphp_glue.c | 58 ++++++++++++++++++-------------------- 2 files changed, 27 insertions(+), 34 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index e68d5f20ffb..7d938df7920 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -91,9 +91,6 @@ struct acpiphp_bridge { /* PCI-to-PCI bridge device */ struct pci_dev *pci_dev; - /* ACPI 2.0 _HPP parameters */ - struct hotplug_params hpp; - spinlock_t res_lock; }; diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index e0623373615..db81c08de8d 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -59,7 +59,7 @@ static DEFINE_SPINLOCK(ioapic_list_lock); static void handle_hotplug_event_bridge (acpi_handle, u32, void *); static void acpiphp_sanitize_bus(struct pci_bus *bus); -static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus); +static void acpiphp_set_hpp_values(struct pci_bus *bus); static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *context); /* callback routine to check for the existence of a pci dock device */ @@ -273,23 +273,23 @@ static int detect_ejectable_slots(acpi_handle handle) /* decode ACPI 2.0 _HPP hot plug parameters */ -static void decode_hpp(struct acpiphp_bridge *bridge) +static void decode_hpp(struct pci_dev *dev, struct hotplug_params *hpp) { acpi_status status; - status = acpi_get_hp_params_from_firmware(bridge->pci_bus, &bridge->hpp); + status = acpi_get_hp_params_from_firmware(dev->bus, hpp); if (ACPI_FAILURE(status) || - !bridge->hpp.t0 || (bridge->hpp.t0->revision > 1)) { + !hpp->t0 || (hpp->t0->revision > 1)) { /* use default numbers */ printk(KERN_WARNING "%s: Could not get hotplug parameters. Use defaults\n", __func__); - bridge->hpp.t0 = &bridge->hpp.type0_data; - bridge->hpp.t0->revision = 0; - bridge->hpp.t0->cache_line_size = 0x10; - bridge->hpp.t0->latency_timer = 0x40; - bridge->hpp.t0->enable_serr = 0; - bridge->hpp.t0->enable_perr = 0; + hpp->t0 = &hpp->type0_data; + hpp->t0->revision = 0; + hpp->t0->cache_line_size = 0x10; + hpp->t0->latency_timer = 0x40; + hpp->t0->enable_serr = 0; + hpp->t0->enable_perr = 0; } } @@ -1037,7 +1037,7 @@ static int __ref enable_device(struct acpiphp_slot *slot) pci_bus_assign_resources(bus); acpiphp_sanitize_bus(bus); - acpiphp_set_hpp_values(slot->bridge->handle, bus); + acpiphp_set_hpp_values(bus); list_for_each_entry(func, &slot->funcs, sibling) acpiphp_configure_ioapics(func->handle); pci_enable_bridges(bus); @@ -1248,7 +1248,7 @@ static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) return retval; } -static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge) +static void program_hpp(struct pci_dev *dev, struct hotplug_params *hpp) { u16 pci_cmd, pci_bctl; struct pci_dev *cdev; @@ -1263,15 +1263,15 @@ static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge) return; pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, - bridge->hpp.t0->cache_line_size); + hpp->t0->cache_line_size); pci_write_config_byte(dev, PCI_LATENCY_TIMER, - bridge->hpp.t0->latency_timer); + hpp->t0->latency_timer); pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); - if (bridge->hpp.t0->enable_serr) + if (hpp->t0->enable_serr) pci_cmd |= PCI_COMMAND_SERR; else pci_cmd &= ~PCI_COMMAND_SERR; - if (bridge->hpp.t0->enable_perr) + if (hpp->t0->enable_perr) pci_cmd |= PCI_COMMAND_PARITY; else pci_cmd &= ~PCI_COMMAND_PARITY; @@ -1280,13 +1280,13 @@ static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge) /* Program bridge control value and child devices */ if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, - bridge->hpp.t0->latency_timer); + hpp->t0->latency_timer); pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (bridge->hpp.t0->enable_serr) + if (hpp->t0->enable_serr) pci_bctl |= PCI_BRIDGE_CTL_SERR; else pci_bctl &= ~PCI_BRIDGE_CTL_SERR; - if (bridge->hpp.t0->enable_perr) + if (hpp->t0->enable_perr) pci_bctl |= PCI_BRIDGE_CTL_PARITY; else pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; @@ -1294,24 +1294,20 @@ static void program_hpp(struct pci_dev *dev, struct acpiphp_bridge *bridge) if (dev->subordinate) { list_for_each_entry(cdev, &dev->subordinate->devices, bus_list) - program_hpp(cdev, bridge); + program_hpp(cdev, hpp); } } } -static void acpiphp_set_hpp_values(acpi_handle handle, struct pci_bus *bus) +static void acpiphp_set_hpp_values(struct pci_bus *bus) { - struct acpiphp_bridge bridge; struct pci_dev *dev; + struct hotplug_params hpp; - memset(&bridge, 0, sizeof(bridge)); - bridge.handle = handle; - bridge.pci_bus = bus; - bridge.pci_dev = bus->self; - decode_hpp(&bridge); - list_for_each_entry(dev, &bus->devices, bus_list) - program_hpp(dev, &bridge); - + list_for_each_entry(dev, &bus->devices, bus_list) { + decode_hpp(dev, &hpp); + program_hpp(dev, &hpp); + } } /* @@ -1355,7 +1351,7 @@ static int acpiphp_configure_bridge (acpi_handle handle) pci_bus_size_bridges(bus); pci_bus_assign_resources(bus); acpiphp_sanitize_bus(bus); - acpiphp_set_hpp_values(handle, bus); + acpiphp_set_hpp_values(bus); pci_enable_bridges(bus); acpiphp_configure_ioapics(handle); return 0; -- cgit v1.2.3 From 6a29172ba90e49c046245610caff9848307bfd6a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:15 -0600 Subject: PCI hotplug: clean up acpi_get_hp_params_from_firmware() interface This patch makes acpi_get_hp_params_from_firmware() take a pci_dev rather than a pci_bus and makes it return a standard int errno rather than acpi_status. Signed-off-by: Bjorn Helgaas Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 14 +++++++------- drivers/pci/hotplug/acpiphp_glue.c | 7 +++---- drivers/pci/hotplug/pciehp.h | 4 +--- drivers/pci/hotplug/shpchp.h | 4 +--- 4 files changed, 12 insertions(+), 17 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index ec3c039b7eb..14d2d8a01f6 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -324,18 +324,18 @@ static acpi_status acpi_run_oshp(acpi_handle handle) /* acpi_get_hp_params_from_firmware * - * @bus - the pci_bus of the bus on which the device is newly added + * @dev - the pci_dev for which we want parameters * @hpp - allocated by the caller */ -acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, +int acpi_get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { - acpi_status status = AE_NOT_FOUND; + acpi_status status; acpi_handle handle, phandle; struct pci_bus *pbus; handle = NULL; - for (pbus = bus; pbus; pbus = pbus->parent) { + for (pbus = dev->bus; pbus; pbus = pbus->parent) { handle = acpi_pci_get_bridge_handle(pbus); if (handle) break; @@ -350,10 +350,10 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, while (handle) { status = acpi_run_hpx(handle, hpp); if (ACPI_SUCCESS(status)) - break; + return 0; status = acpi_run_hpp(handle, hpp); if (ACPI_SUCCESS(status)) - break; + return 0; if (acpi_is_root_bridge(handle)) break; status = acpi_get_parent(handle, &phandle); @@ -361,7 +361,7 @@ acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, break; handle = phandle; } - return status; + return -ENODEV; } EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index db81c08de8d..14e6f1a17f2 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -275,11 +275,10 @@ static int detect_ejectable_slots(acpi_handle handle) /* decode ACPI 2.0 _HPP hot plug parameters */ static void decode_hpp(struct pci_dev *dev, struct hotplug_params *hpp) { - acpi_status status; + int ret; - status = acpi_get_hp_params_from_firmware(dev->bus, hpp); - if (ACPI_FAILURE(status) || - !hpp->t0 || (hpp->t0->revision > 1)) { + ret = acpi_get_hp_params_from_firmware(dev, hpp); + if (ret || !hpp->t0 || (hpp->t0->revision > 1)) { /* use default numbers */ printk(KERN_WARNING "%s: Could not get hotplug parameters. Use defaults\n", diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index e6cf096498b..86cdfd71ed2 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -241,9 +241,7 @@ static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { - if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp))) - return -ENODEV; - return 0; + return acpi_get_hp_params_from_firmware(dev, hpp); } #else #define pciehp_firmware_init() do {} while (0) diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index 974e924ca96..ad6a255cf0a 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -191,9 +191,7 @@ static inline const char *slot_name(struct slot *slot) static inline int get_hp_params_from_firmware(struct pci_dev *dev, struct hotplug_params *hpp) { - if (ACPI_FAILURE(acpi_get_hp_params_from_firmware(dev->bus, hpp))) - return -ENODEV; - return 0; + return acpi_get_hp_params_from_firmware(dev, hpp); } static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) -- cgit v1.2.3 From 8838400db5193c37588813c2eb1249b821781950 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:20 -0600 Subject: PCI hotplug: add pci_configure_slot() This patch adds a new pci_configure_slot() function that programs the PCI bus characteristics for a newly-added device. This is based on code in pciehp_pci.c, but should be generic enough to be used by pciehp, shpchp, and acpiphp. The hotplug_params struct and the program_hpp_typeX() functions are based on the ACPI definitions, but they aren't really ACPI-specific, and there's no alternate implementation, so I don't see the need to abstract them yet. Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Chiang Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/Makefile | 2 +- drivers/pci/hotplug/pcihp_slot.c | 187 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 188 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/hotplug/pcihp_slot.c (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 2aa117c8cd8..3625b094bf7 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -22,7 +22,7 @@ obj-$(CONFIG_HOTPLUG_PCI_SGI) += sgi_hotplug.o # Link this last so it doesn't claim devices that have a real hotplug driver obj-$(CONFIG_HOTPLUG_PCI_FAKE) += fakephp.o -pci_hotplug-objs := pci_hotplug_core.o +pci_hotplug-objs := pci_hotplug_core.o pcihp_slot.o ifdef CONFIG_HOTPLUG_PCI_CPCI pci_hotplug-objs += cpci_hotplug_core.o \ diff --git a/drivers/pci/hotplug/pcihp_slot.c b/drivers/pci/hotplug/pcihp_slot.c new file mode 100644 index 00000000000..cc8ec3aa41a --- /dev/null +++ b/drivers/pci/hotplug/pcihp_slot.c @@ -0,0 +1,187 @@ +/* + * Copyright (C) 1995,2001 Compaq Computer Corporation + * Copyright (C) 2001 Greg Kroah-Hartman (greg@kroah.com) + * Copyright (C) 2001 IBM Corp. + * Copyright (C) 2003-2004 Intel Corporation + * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at + * your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or + * NON INFRINGEMENT. See the GNU General Public License for more + * details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +static struct hpp_type0 pci_default_type0 = { + .revision = 1, + .cache_line_size = 8, + .latency_timer = 0x40, + .enable_serr = 0, + .enable_perr = 0, +}; + +static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) +{ + u16 pci_cmd, pci_bctl; + + if (!hpp) { + /* + * Perhaps we *should* use default settings for PCIe, but + * pciehp didn't, so we won't either. + */ + if (dev->is_pcie) + return; + dev_info(&dev->dev, "using default PCI settings\n"); + hpp = &pci_default_type0; + } + + if (hpp->revision > 1) { + dev_warn(&dev->dev, + "PCI settings rev %d not supported; using defaults\n", + hpp->revision); + hpp = &pci_default_type0; + } + + pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size); + pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer); + pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); + if (hpp->enable_serr) + pci_cmd |= PCI_COMMAND_SERR; + else + pci_cmd &= ~PCI_COMMAND_SERR; + if (hpp->enable_perr) + pci_cmd |= PCI_COMMAND_PARITY; + else + pci_cmd &= ~PCI_COMMAND_PARITY; + pci_write_config_word(dev, PCI_COMMAND, pci_cmd); + + /* Program bridge control value */ + if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { + pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, + hpp->latency_timer); + pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); + if (hpp->enable_serr) + pci_bctl |= PCI_BRIDGE_CTL_SERR; + else + pci_bctl &= ~PCI_BRIDGE_CTL_SERR; + if (hpp->enable_perr) + pci_bctl |= PCI_BRIDGE_CTL_PARITY; + else + pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; + pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); + } +} + +static void program_hpp_type1(struct pci_dev *dev, struct hpp_type1 *hpp) +{ + if (hpp) + dev_warn(&dev->dev, "PCI-X settings not supported\n"); +} + +static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) +{ + int pos; + u16 reg16; + u32 reg32; + + if (!hpp) + return; + + /* Find PCI Express capability */ + pos = pci_find_capability(dev, PCI_CAP_ID_EXP); + if (!pos) + return; + + if (hpp->revision > 1) { + dev_warn(&dev->dev, "PCIe settings rev %d not supported\n", + hpp->revision); + return; + } + + /* Initialize Device Control Register */ + pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16); + reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or; + pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16); + + /* Initialize Link Control Register */ + if (dev->subordinate) { + pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, ®16); + reg16 = (reg16 & hpp->pci_exp_lnkctl_and) + | hpp->pci_exp_lnkctl_or; + pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16); + } + + /* Find Advanced Error Reporting Enhanced Capability */ + pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); + if (!pos) + return; + + /* Initialize Uncorrectable Error Mask Register */ + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32); + reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or; + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32); + + /* Initialize Uncorrectable Error Severity Register */ + pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32); + reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or; + pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32); + + /* Initialize Correctable Error Mask Register */ + pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32); + reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or; + pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32); + + /* Initialize Advanced Error Capabilities and Control Register */ + pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); + reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or; + pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); + + /* + * FIXME: The following two registers are not supported yet. + * + * o Secondary Uncorrectable Error Severity Register + * o Secondary Uncorrectable Error Mask Register + */ +} + +void pci_configure_slot(struct pci_dev *dev) +{ + struct pci_dev *cdev; + struct hotplug_params hpp; + int ret; + + if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || + (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && + (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) + return; + + memset(&hpp, 0, sizeof(hpp)); + ret = pci_get_hp_params(dev, &hpp); + if (ret) + dev_warn(&dev->dev, "no hotplug settings from platform\n"); + + program_hpp_type2(dev, hpp.t2); + program_hpp_type1(dev, hpp.t1); + program_hpp_type0(dev, hpp.t0); + + if (dev->subordinate) { + list_for_each_entry(cdev, &dev->subordinate->devices, + bus_list) + pci_configure_slot(cdev); + } +} +EXPORT_SYMBOL_GPL(pci_configure_slot); -- cgit v1.2.3 From d569c74d78ffcde2f163256e4da934ec3bacff0e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:25 -0600 Subject: PCI hotplug: pciehp: use generic pci_configure_slot() Use the generic pci_configure_slot() rather than the PCIe-specific program_fw_provided_values(). Unlike the previous pciehp-specific code, we now walk through subordinate devices even if there are no settings for the parent. This should be harmless because we won't change anything unless we discover firmware settings farther down. Signed-off-by: Bjorn Helgaas Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp.h | 7 --- drivers/pci/hotplug/pciehp_pci.c | 132 +-------------------------------------- 2 files changed, 1 insertion(+), 138 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 86cdfd71ed2..36faa9a8e18 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -237,15 +237,8 @@ static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) return retval; return pciehp_acpi_slot_detection_check(dev); } - -static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, - struct hotplug_params *hpp) -{ - return acpi_get_hp_params_from_firmware(dev, hpp); -} #else #define pciehp_firmware_init() do {} while (0) #define pciehp_get_hp_hw_control_from_firmware(dev) 0 -#define pciehp_get_hp_params_from_firmware(dev, hpp) (-ENODEV) #endif /* CONFIG_ACPI */ #endif /* _PCIEHP_H */ diff --git a/drivers/pci/hotplug/pciehp_pci.c b/drivers/pci/hotplug/pciehp_pci.c index af295d080a0..02e24d63b3e 100644 --- a/drivers/pci/hotplug/pciehp_pci.c +++ b/drivers/pci/hotplug/pciehp_pci.c @@ -34,136 +34,6 @@ #include "../pci.h" #include "pciehp.h" -static void program_hpp_type0(struct pci_dev *dev, struct hpp_type0 *hpp) -{ - u16 pci_cmd, pci_bctl; - - if (hpp->revision > 1) { - warn("Rev.%d type0 record not supported\n", hpp->revision); - return; - } - - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, hpp->cache_line_size); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp->latency_timer); - pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); - if (hpp->enable_serr) - pci_cmd |= PCI_COMMAND_SERR; - else - pci_cmd &= ~PCI_COMMAND_SERR; - if (hpp->enable_perr) - pci_cmd |= PCI_COMMAND_PARITY; - else - pci_cmd &= ~PCI_COMMAND_PARITY; - pci_write_config_word(dev, PCI_COMMAND, pci_cmd); - - /* Program bridge control value */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, - hpp->latency_timer); - pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (hpp->enable_serr) - pci_bctl |= PCI_BRIDGE_CTL_SERR; - else - pci_bctl &= ~PCI_BRIDGE_CTL_SERR; - if (hpp->enable_perr) - pci_bctl |= PCI_BRIDGE_CTL_PARITY; - else - pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; - pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); - } -} - -static void program_hpp_type2(struct pci_dev *dev, struct hpp_type2 *hpp) -{ - int pos; - u16 reg16; - u32 reg32; - - if (hpp->revision > 1) { - warn("Rev.%d type2 record not supported\n", hpp->revision); - return; - } - - /* Find PCI Express capability */ - pos = pci_find_capability(dev, PCI_CAP_ID_EXP); - if (!pos) - return; - - /* Initialize Device Control Register */ - pci_read_config_word(dev, pos + PCI_EXP_DEVCTL, ®16); - reg16 = (reg16 & hpp->pci_exp_devctl_and) | hpp->pci_exp_devctl_or; - pci_write_config_word(dev, pos + PCI_EXP_DEVCTL, reg16); - - /* Initialize Link Control Register */ - if (dev->subordinate) { - pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, ®16); - reg16 = (reg16 & hpp->pci_exp_lnkctl_and) - | hpp->pci_exp_lnkctl_or; - pci_write_config_word(dev, pos + PCI_EXP_LNKCTL, reg16); - } - - /* Find Advanced Error Reporting Enhanced Capability */ - pos = pci_find_ext_capability(dev, PCI_EXT_CAP_ID_ERR); - if (!pos) - return; - - /* Initialize Uncorrectable Error Mask Register */ - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, ®32); - reg32 = (reg32 & hpp->unc_err_mask_and) | hpp->unc_err_mask_or; - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_MASK, reg32); - - /* Initialize Uncorrectable Error Severity Register */ - pci_read_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, ®32); - reg32 = (reg32 & hpp->unc_err_sever_and) | hpp->unc_err_sever_or; - pci_write_config_dword(dev, pos + PCI_ERR_UNCOR_SEVER, reg32); - - /* Initialize Correctable Error Mask Register */ - pci_read_config_dword(dev, pos + PCI_ERR_COR_MASK, ®32); - reg32 = (reg32 & hpp->cor_err_mask_and) | hpp->cor_err_mask_or; - pci_write_config_dword(dev, pos + PCI_ERR_COR_MASK, reg32); - - /* Initialize Advanced Error Capabilities and Control Register */ - pci_read_config_dword(dev, pos + PCI_ERR_CAP, ®32); - reg32 = (reg32 & hpp->adv_err_cap_and) | hpp->adv_err_cap_or; - pci_write_config_dword(dev, pos + PCI_ERR_CAP, reg32); - - /* - * FIXME: The following two registers are not supported yet. - * - * o Secondary Uncorrectable Error Severity Register - * o Secondary Uncorrectable Error Mask Register - */ -} - -static void program_fw_provided_values(struct pci_dev *dev) -{ - struct pci_dev *cdev; - struct hotplug_params hpp; - - /* Program hpp values for this device */ - if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || - (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) - return; - - if (pciehp_get_hp_params_from_firmware(dev, &hpp)) { - warn("Could not get hotplug parameters\n"); - return; - } - - if (hpp.t2) - program_hpp_type2(dev, hpp.t2); - if (hpp.t0) - program_hpp_type0(dev, hpp.t0); - - /* Program child devices */ - if (dev->subordinate) { - list_for_each_entry(cdev, &dev->subordinate->devices, - bus_list) - program_fw_provided_values(cdev); - } -} - static int __ref pciehp_add_bridge(struct pci_dev *dev) { struct pci_bus *parent = dev->bus; @@ -226,7 +96,7 @@ int pciehp_configure_device(struct slot *p_slot) (dev->hdr_type == PCI_HEADER_TYPE_CARDBUS)) { pciehp_add_bridge(dev); } - program_fw_provided_values(dev); + pci_configure_slot(dev); pci_dev_put(dev); } -- cgit v1.2.3 From 11876e52e9148bf923795d6fcf8abed7f3662aaa Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:30 -0600 Subject: PCI hotplug: shpchp: use generic pci_configure_slot() Use the generic pci_configure_slot() rather than the SHPC-specific program_fw_provided_values(). Unlike the previous SHPC-specific code, pci_configure_slot() programs PCIe settings when an _HPX method provides them, so if it's possible to have an SHPC-managed PCIe device, it can now be configured. Signed-off-by: Bjorn Helgaas Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/shpchp.h | 7 ----- drivers/pci/hotplug/shpchp_pci.c | 62 +--------------------------------------- 2 files changed, 1 insertion(+), 68 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/shpchp.h b/drivers/pci/hotplug/shpchp.h index ad6a255cf0a..bd588eb8e92 100644 --- a/drivers/pci/hotplug/shpchp.h +++ b/drivers/pci/hotplug/shpchp.h @@ -188,19 +188,12 @@ static inline const char *slot_name(struct slot *slot) #ifdef CONFIG_ACPI #include -static inline int get_hp_params_from_firmware(struct pci_dev *dev, - struct hotplug_params *hpp) -{ - return acpi_get_hp_params_from_firmware(dev, hpp); -} - static inline int get_hp_hw_control_from_firmware(struct pci_dev *dev) { u32 flags = OSC_SHPC_NATIVE_HP_CONTROL; return acpi_get_hp_hw_control_from_firmware(dev, flags); } #else -#define get_hp_params_from_firmware(dev, hpp) (-ENODEV) #define get_hp_hw_control_from_firmware(dev) (0) #endif diff --git a/drivers/pci/hotplug/shpchp_pci.c b/drivers/pci/hotplug/shpchp_pci.c index aa315e52529..8c3d3219f22 100644 --- a/drivers/pci/hotplug/shpchp_pci.c +++ b/drivers/pci/hotplug/shpchp_pci.c @@ -34,66 +34,6 @@ #include "../pci.h" #include "shpchp.h" -static void program_fw_provided_values(struct pci_dev *dev) -{ - u16 pci_cmd, pci_bctl; - struct pci_dev *cdev; - struct hotplug_params hpp; - - /* Program hpp values for this device */ - if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || - (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) - return; - - /* use default values if we can't get them from firmware */ - if (get_hp_params_from_firmware(dev, &hpp) || - !hpp.t0 || (hpp.t0->revision > 1)) { - warn("Could not get hotplug parameters. Use defaults\n"); - hpp.t0 = &hpp.type0_data; - hpp.t0->revision = 0; - hpp.t0->cache_line_size = 8; - hpp.t0->latency_timer = 0x40; - hpp.t0->enable_serr = 0; - hpp.t0->enable_perr = 0; - } - - pci_write_config_byte(dev, - PCI_CACHE_LINE_SIZE, hpp.t0->cache_line_size); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, hpp.t0->latency_timer); - pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); - if (hpp.t0->enable_serr) - pci_cmd |= PCI_COMMAND_SERR; - else - pci_cmd &= ~PCI_COMMAND_SERR; - if (hpp.t0->enable_perr) - pci_cmd |= PCI_COMMAND_PARITY; - else - pci_cmd &= ~PCI_COMMAND_PARITY; - pci_write_config_word(dev, PCI_COMMAND, pci_cmd); - - /* Program bridge control value and child devices */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, - hpp.t0->latency_timer); - pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (hpp.t0->enable_serr) - pci_bctl |= PCI_BRIDGE_CTL_SERR; - else - pci_bctl &= ~PCI_BRIDGE_CTL_SERR; - if (hpp.t0->enable_perr) - pci_bctl |= PCI_BRIDGE_CTL_PARITY; - else - pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; - pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); - if (dev->subordinate) { - list_for_each_entry(cdev, &dev->subordinate->devices, - bus_list) - program_fw_provided_values(cdev); - } - } -} - int __ref shpchp_configure_device(struct slot *p_slot) { struct pci_dev *dev; @@ -153,7 +93,7 @@ int __ref shpchp_configure_device(struct slot *p_slot) child->subordinate = pci_do_scan_bus(child); pci_bus_size_bridges(child); } - program_fw_provided_values(dev); + pci_configure_slot(dev); pci_dev_put(dev); } -- cgit v1.2.3 From e81995bb1c0077a312cb621abc406a36f65a986a Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:35 -0600 Subject: PCI hotplug: acpiphp: use generic pci_configure_slot() Use the generic pci_configure_slot() rather than the acpiphp-specific decode_hpp() and program_hpp(). Unlike the previous acpiphp-specific code, pci_configure_slot() programs PCIe settings when an _HPX method provides them, so acpiphp-managed PCIe devices can now be configured. Signed-off-by: Bjorn Helgaas Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 9 ++--- drivers/pci/hotplug/acpiphp_glue.c | 81 +------------------------------------- 2 files changed, 6 insertions(+), 84 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index 14d2d8a01f6..ee24de1c5fa 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -322,13 +322,12 @@ static acpi_status acpi_run_oshp(acpi_handle handle) return status; } -/* acpi_get_hp_params_from_firmware +/* pci_get_hp_params * * @dev - the pci_dev for which we want parameters * @hpp - allocated by the caller */ -int acpi_get_hp_params_from_firmware(struct pci_dev *dev, - struct hotplug_params *hpp) +int pci_get_hp_params(struct pci_dev *dev, struct hotplug_params *hpp) { acpi_status status; acpi_handle handle, phandle; @@ -345,7 +344,7 @@ int acpi_get_hp_params_from_firmware(struct pci_dev *dev, * _HPP settings apply to all child buses, until another _HPP is * encountered. If we don't find an _HPP for the input pci dev, * look for it in the parent device scope since that would apply to - * this pci dev. If we don't find any _HPP, use hardcoded defaults + * this pci dev. */ while (handle) { status = acpi_run_hpx(handle, hpp); @@ -363,7 +362,7 @@ int acpi_get_hp_params_from_firmware(struct pci_dev *dev, } return -ENODEV; } -EXPORT_SYMBOL_GPL(acpi_get_hp_params_from_firmware); +EXPORT_SYMBOL_GPL(pci_get_hp_params); /** * acpi_get_hp_hw_control_from_firmware diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 14e6f1a17f2..58d25a163a8 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -271,29 +271,6 @@ static int detect_ejectable_slots(acpi_handle handle) return found; } - -/* decode ACPI 2.0 _HPP hot plug parameters */ -static void decode_hpp(struct pci_dev *dev, struct hotplug_params *hpp) -{ - int ret; - - ret = acpi_get_hp_params_from_firmware(dev, hpp); - if (ret || !hpp->t0 || (hpp->t0->revision > 1)) { - /* use default numbers */ - printk(KERN_WARNING - "%s: Could not get hotplug parameters. Use defaults\n", - __func__); - hpp->t0 = &hpp->type0_data; - hpp->t0->revision = 0; - hpp->t0->cache_line_size = 0x10; - hpp->t0->latency_timer = 0x40; - hpp->t0->enable_serr = 0; - hpp->t0->enable_perr = 0; - } -} - - - /* initialize miscellaneous stuff for both root and PCI-to-PCI bridge */ static void init_bridge_misc(struct acpiphp_bridge *bridge) { @@ -1247,66 +1224,12 @@ static int acpiphp_check_bridge(struct acpiphp_bridge *bridge) return retval; } -static void program_hpp(struct pci_dev *dev, struct hotplug_params *hpp) -{ - u16 pci_cmd, pci_bctl; - struct pci_dev *cdev; - - /* Program hpp values for this device */ - if (!(dev->hdr_type == PCI_HEADER_TYPE_NORMAL || - (dev->hdr_type == PCI_HEADER_TYPE_BRIDGE && - (dev->class >> 8) == PCI_CLASS_BRIDGE_PCI))) - return; - - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_HOST) - return; - - pci_write_config_byte(dev, PCI_CACHE_LINE_SIZE, - hpp->t0->cache_line_size); - pci_write_config_byte(dev, PCI_LATENCY_TIMER, - hpp->t0->latency_timer); - pci_read_config_word(dev, PCI_COMMAND, &pci_cmd); - if (hpp->t0->enable_serr) - pci_cmd |= PCI_COMMAND_SERR; - else - pci_cmd &= ~PCI_COMMAND_SERR; - if (hpp->t0->enable_perr) - pci_cmd |= PCI_COMMAND_PARITY; - else - pci_cmd &= ~PCI_COMMAND_PARITY; - pci_write_config_word(dev, PCI_COMMAND, pci_cmd); - - /* Program bridge control value and child devices */ - if ((dev->class >> 8) == PCI_CLASS_BRIDGE_PCI) { - pci_write_config_byte(dev, PCI_SEC_LATENCY_TIMER, - hpp->t0->latency_timer); - pci_read_config_word(dev, PCI_BRIDGE_CONTROL, &pci_bctl); - if (hpp->t0->enable_serr) - pci_bctl |= PCI_BRIDGE_CTL_SERR; - else - pci_bctl &= ~PCI_BRIDGE_CTL_SERR; - if (hpp->t0->enable_perr) - pci_bctl |= PCI_BRIDGE_CTL_PARITY; - else - pci_bctl &= ~PCI_BRIDGE_CTL_PARITY; - pci_write_config_word(dev, PCI_BRIDGE_CONTROL, pci_bctl); - if (dev->subordinate) { - list_for_each_entry(cdev, &dev->subordinate->devices, - bus_list) - program_hpp(cdev, hpp); - } - } -} - static void acpiphp_set_hpp_values(struct pci_bus *bus) { struct pci_dev *dev; - struct hotplug_params hpp; - list_for_each_entry(dev, &bus->devices, bus_list) { - decode_hpp(dev, &hpp); - program_hpp(dev, &hpp); - } + list_for_each_entry(dev, &bus->devices, bus_list) + pci_configure_slot(dev); } /* -- cgit v1.2.3 From 5e3573db2bd5db6925159279d99576a4635bdb66 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Mon, 14 Sep 2009 16:35:40 -0600 Subject: PCI hotplug: clean up acpi_run_hpp() This patch cleans up acpi_run_hpp() and follows the style of acpi_run_hpx(): - remove unnecessary METHOD_NAME__HPP #define - use ACPI_ALLOCATE_BUFFER rather than evaluating _HPP twice - validate _HPP package length (defined as 4 by the spec) - avoid ref to undefined data if FW provides < 4 elements - remove temporary nui[] array Signed-off-by: Bjorn Helgaas Reviewed-by: Alex Chiang Reviewed-by: Kenji Kaneshige Acked-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 84 +++++++++++----------------------------- 1 file changed, 22 insertions(+), 62 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index ee24de1c5fa..a73028ec52e 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -41,7 +41,6 @@ #define warn(format, arg...) printk(KERN_WARNING "%s: " format , MY_NAME , ## arg) #define METHOD_NAME__SUN "_SUN" -#define METHOD_NAME__HPP "_HPP" #define METHOD_NAME_OSHP "OSHP" static int debug_acpi; @@ -215,80 +214,41 @@ acpi_run_hpx(acpi_handle handle, struct hotplug_params *hpx) static acpi_status acpi_run_hpp(acpi_handle handle, struct hotplug_params *hpp) { - acpi_status status; - u8 nui[4]; - struct acpi_buffer ret_buf = { 0, NULL}; - struct acpi_buffer string = { ACPI_ALLOCATE_BUFFER, NULL }; - union acpi_object *ext_obj, *package; - int i, len = 0; - - acpi_get_name(handle, ACPI_FULL_PATHNAME, &string); + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object *package, *fields; + int i; - /* Clear the return buffer with zeros */ memset(hpp, 0, sizeof(struct hotplug_params)); - /* get _hpp */ - status = acpi_evaluate_object(handle, METHOD_NAME__HPP, NULL, &ret_buf); - switch (status) { - case AE_BUFFER_OVERFLOW: - ret_buf.pointer = kmalloc (ret_buf.length, GFP_KERNEL); - if (!ret_buf.pointer) { - printk(KERN_ERR "%s:%s alloc for _HPP fail\n", - __func__, (char *)string.pointer); - kfree(string.pointer); - return AE_NO_MEMORY; - } - status = acpi_evaluate_object(handle, METHOD_NAME__HPP, - NULL, &ret_buf); - if (ACPI_SUCCESS(status)) - break; - default: - if (ACPI_FAILURE(status)) { - pr_debug("%s:%s _HPP fail=0x%x\n", __func__, - (char *)string.pointer, status); - kfree(string.pointer); - return status; - } - } + status = acpi_evaluate_object(handle, "_HPP", NULL, &buffer); + if (ACPI_FAILURE(status)) + return status; - ext_obj = (union acpi_object *) ret_buf.pointer; - if (ext_obj->type != ACPI_TYPE_PACKAGE) { - printk(KERN_ERR "%s:%s _HPP obj not a package\n", __func__, - (char *)string.pointer); + package = (union acpi_object *) buffer.pointer; + if (package->type != ACPI_TYPE_PACKAGE || + package->package.count != 4) { status = AE_ERROR; - goto free_and_return; + goto exit; } - len = ext_obj->package.count; - package = (union acpi_object *) ret_buf.pointer; - for ( i = 0; (i < len) || (i < 4); i++) { - ext_obj = (union acpi_object *) &package->package.elements[i]; - switch (ext_obj->type) { - case ACPI_TYPE_INTEGER: - nui[i] = (u8)ext_obj->integer.value; - break; - default: - printk(KERN_ERR "%s:%s _HPP obj type incorrect\n", - __func__, (char *)string.pointer); + fields = package->package.elements; + for (i = 0; i < 4; i++) { + if (fields[i].type != ACPI_TYPE_INTEGER) { status = AE_ERROR; - goto free_and_return; + goto exit; } } hpp->t0 = &hpp->type0_data; - hpp->t0->cache_line_size = nui[0]; - hpp->t0->latency_timer = nui[1]; - hpp->t0->enable_serr = nui[2]; - hpp->t0->enable_perr = nui[3]; - - pr_debug(" _HPP: cache_line_size=0x%x\n", hpp->t0->cache_line_size); - pr_debug(" _HPP: latency timer =0x%x\n", hpp->t0->latency_timer); - pr_debug(" _HPP: enable SERR =0x%x\n", hpp->t0->enable_serr); - pr_debug(" _HPP: enable PERR =0x%x\n", hpp->t0->enable_perr); + hpp->t0->revision = 1; + hpp->t0->cache_line_size = fields[0].integer.value; + hpp->t0->latency_timer = fields[1].integer.value; + hpp->t0->enable_serr = fields[2].integer.value; + hpp->t0->enable_perr = fields[3].integer.value; -free_and_return: - kfree(string.pointer); - kfree(ret_buf.pointer); +exit: + kfree(buffer.pointer); return status; } -- cgit v1.2.3