From 93e75faba3982767d425323aec5726282d3ad7a2 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 5 Aug 2010 22:23:16 +0200 Subject: PCI: Adjust confusing if indentation in pcie_get_readrq Indent the branch of an if. The semantic match that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @r disable braces4@ position p1,p2; statement S1,S2; @@ ( if (...) { ... } | if (...) S1@p1 S2@p2 ) @script:python@ p1 << r.p1; p2 << r.p2; @@ if (p1[0].column == p2[0].column): cocci.print_main("branch",p1) cocci.print_secs("after",p2) // Signed-off-by: Julia Lawall Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7fa3cbd742c5..cc232c016ef9 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2689,7 +2689,7 @@ int pcie_get_readrq(struct pci_dev *dev) ret = pci_read_config_word(dev, cap + PCI_EXP_DEVCTL, &ctl); if (!ret) - ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12); + ret = 128 << ((ctl & PCI_EXP_DEVCTL_READRQ) >> 12); return ret; } -- cgit v1.2.3 From 991f739544a0923b70fb69b115edb880ff9fcc4a Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Sun, 4 Jul 2010 00:02:28 +0200 Subject: PCI: kill BKL in /proc/pci All operations in the pci procfs ioctl functions are atomic, so no lock is needed here. Also add a compat_ioctl method, since all the commands are compatible in 32 bit mode. Signed-off-by: Arnd Bergmann Cc: Jesse Barnes Cc: Tejun Heo Cc: linux-pci@vger.kernel.org Signed-off-by: Jesse Barnes --- drivers/pci/proc.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 01f0306525a5..297b72c880a1 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -212,8 +212,6 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, #endif /* HAVE_PCI_MMAP */ int ret = 0; - lock_kernel(); - switch (cmd) { case PCIIOC_CONTROLLER: ret = pci_domain_nr(dev->bus); @@ -242,7 +240,6 @@ static long proc_bus_pci_ioctl(struct file *file, unsigned int cmd, break; }; - unlock_kernel(); return ret; } @@ -306,6 +303,7 @@ static const struct file_operations proc_bus_pci_operations = { .read = proc_bus_pci_read, .write = proc_bus_pci_write, .unlocked_ioctl = proc_bus_pci_ioctl, + .compat_ioctl = proc_bus_pci_ioctl, #ifdef HAVE_PCI_MMAP .open = proc_bus_pci_open, .release = proc_bus_pci_release, -- cgit v1.2.3 From 50c1126ee1990920705a067a6f3f9bb892369b08 Mon Sep 17 00:00:00 2001 From: Bill Pemberton Date: Tue, 3 Aug 2010 15:18:43 -0400 Subject: PCI: aerdrv: fix uninitialized variable warning quiet the warning about use of uninitialized e_src in aer_isr() e_src is initialized by get_e_source() Signed-off-by: Bill Pemberton Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv_core.c b/drivers/pci/pcie/aer/aerdrv_core.c index 29e268fadf14..43421fbe080a 100644 --- a/drivers/pci/pcie/aer/aerdrv_core.c +++ b/drivers/pci/pcie/aer/aerdrv_core.c @@ -754,7 +754,7 @@ void aer_isr(struct work_struct *work) { struct aer_rpc *rpc = container_of(work, struct aer_rpc, dpc_handler); struct pcie_device *p_device = rpc->rpd; - struct aer_err_source e_src; + struct aer_err_source uninitialized_var(e_src); mutex_lock(&rpc->rpc_mutex); while (get_e_source(rpc, &e_src)) -- cgit v1.2.3 From 5a37f1381f1d8625fa458360c9b5d17f0c5f1dea Mon Sep 17 00:00:00 2001 From: Thomas Gleixner Date: Tue, 7 Sep 2010 14:32:38 +0000 Subject: PCI hotplug: ibmphp-hpc: semaphore cleanup Get rid of init_MUTEX[_LOCKED]() and use sema_init() instead. Signed-off-by: Thomas Gleixner Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/ibmphp_hpc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c index 1aaf3f32d3cd..f59ed30512b5 100644 --- a/drivers/pci/hotplug/ibmphp_hpc.c +++ b/drivers/pci/hotplug/ibmphp_hpc.c @@ -133,8 +133,8 @@ void __init ibmphp_hpc_initvars (void) debug ("%s - Entry\n", __func__); mutex_init(&sem_hpcaccess); - init_MUTEX (&semOperations); - init_MUTEX_LOCKED (&sem_exit); + sema_init(&semOperations, 1); + sema_init(&sem_exit, 0); to_debug = 0; debug ("%s - Exit\n", __func__); -- cgit v1.2.3 From b22c3d82757109fa107ce17ba9484d45273eed05 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 20 Sep 2010 18:50:00 +0200 Subject: PCI/PCIe/AER: Disable native AER service if BIOS has precedence There is a design issue related to PCIe AER and _OSC that the BIOS may be asked to grant control of the AER service even if some Hardware Error Source Table (HEST) entries contain information meaning that the BIOS really should control it. Namely, pcie_port_acpi_setup() calls pcie_aer_get_firmware_first() that determines whether or not the AER service should be controlled by the BIOS on the basis of the HEST information for the given PCIe port. The BIOS is asked to grant control of the AER service for a PCIe Root Complex if pcie_aer_get_firmware_first() returns 'false' for at least one root port in that complex, even if all of the other root ports' HEST entries have the FIRMWARE_FIRST flag set (and none of them has the GLOBAL flag set). However, if the AER service is controlled by the kernel, that may interfere with the BIOS' handling of the error sources having the FIRMWARE_FIRST flag. Moreover, there may be PCIe endpoints that have the FIRMWARE_FIRST flag set in HEST and are attached to the root ports in question, in which case it also may be unsafe to ask the BIOS for control of the AER service. For this reason, introduce a function checking if there's at least one PCIe-related HEST entry with the FIRMWARE_FIRST flag set and disable the native AER service altogether if this function returns 'true'. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv.c | 2 +- drivers/pci/pcie/aer/aerdrv.h | 3 +++ drivers/pci/pcie/aer/aerdrv_acpi.c | 34 ++++++++++++++++++++++++++++++++++ drivers/pci/pcie/portdrv_acpi.c | 2 +- 4 files changed, 39 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pcie/aer/aerdrv.c b/drivers/pci/pcie/aer/aerdrv.c index f409948e1a9b..2b2b6508efde 100644 --- a/drivers/pci/pcie/aer/aerdrv.c +++ b/drivers/pci/pcie/aer/aerdrv.c @@ -416,7 +416,7 @@ static void aer_error_resume(struct pci_dev *dev) */ static int __init aer_service_init(void) { - if (!pci_aer_available()) + if (!pci_aer_available() || aer_acpi_firmware_first()) return -ENXIO; return pcie_port_service_register(&aerdriver); } diff --git a/drivers/pci/pcie/aer/aerdrv.h b/drivers/pci/pcie/aer/aerdrv.h index 80c11d131499..9656e3060412 100644 --- a/drivers/pci/pcie/aer/aerdrv.h +++ b/drivers/pci/pcie/aer/aerdrv.h @@ -132,6 +132,7 @@ static inline int aer_osc_setup(struct pcie_device *pciedev) #ifdef CONFIG_ACPI_APEI extern int pcie_aer_get_firmware_first(struct pci_dev *pci_dev); +extern bool aer_acpi_firmware_first(void); #else static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) { @@ -139,6 +140,8 @@ static inline int pcie_aer_get_firmware_first(struct pci_dev *pci_dev) return pci_dev->__aer_firmware_first; return 0; } + +static inline bool aer_acpi_firmware_first(void) { return false; } #endif static inline void pcie_aer_force_firmware_first(struct pci_dev *pci_dev, diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 2bb9b8972211..275bf158ffa7 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -93,4 +93,38 @@ int pcie_aer_get_firmware_first(struct pci_dev *dev) aer_set_firmware_first(dev); return dev->__aer_firmware_first; } + +static bool aer_firmware_first; + +static int aer_hest_parse_aff(struct acpi_hest_header *hest_hdr, void *data) +{ + struct acpi_hest_aer_common *p; + + if (aer_firmware_first) + return 0; + + switch (hest_hdr->type) { + case ACPI_HEST_TYPE_AER_ROOT_PORT: + case ACPI_HEST_TYPE_AER_ENDPOINT: + case ACPI_HEST_TYPE_AER_BRIDGE: + p = (struct acpi_hest_aer_common *)(hest_hdr + 1); + aer_firmware_first = !!(p->flags & ACPI_HEST_FIRMWARE_FIRST); + default: + return 0; + } +} + +/** + * aer_acpi_firmware_first - Check if APEI should control AER. + */ +bool aer_acpi_firmware_first(void) +{ + static bool parsed = false; + + if (!parsed) { + apei_hest_parse(aer_hest_parse_aff, NULL); + parsed = true; + } + return aer_firmware_first; +} #endif diff --git a/drivers/pci/pcie/portdrv_acpi.c b/drivers/pci/pcie/portdrv_acpi.c index b7c4cb1ccb23..5982b6a63b89 100644 --- a/drivers/pci/pcie/portdrv_acpi.c +++ b/drivers/pci/pcie/portdrv_acpi.c @@ -49,7 +49,7 @@ int pcie_port_acpi_setup(struct pci_dev *port, int *srv_mask) | OSC_PCI_EXPRESS_PME_CONTROL; if (pci_aer_available()) { - if (pcie_aer_get_firmware_first(port)) + if (aer_acpi_firmware_first()) dev_dbg(&port->dev, "PCIe errors handled by BIOS.\n"); else flags |= OSC_PCI_EXPRESS_AER_CONTROL; -- cgit v1.2.3 From 66db60eaf158aa953651d03e43e931e757e87262 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Tue, 21 Sep 2010 13:54:39 -0400 Subject: PCI: add quirk for non-symmetric-mode irq routing to versions 0 and 4 of the MCP55 northbridge A long time ago I worked on a RHEL5 bug in which kdump hung during boot on a set of systems. The systems hung because they never received timer interrupts during calibrate_delay. These systems also all had Opteron processors on a hypertransport bus, bridged to a pci bus via an Nvidia MCP55 northbridge chip. After much wrangling I managed to learn from Nvidia that they have an undocumented register in some versions of that chip which control how legacy interrupts are send to the cpu complex when the ioapic isn't active. Nvidia defaults this register to only send legacy interrupts to the BSP, so if kdump happens to boot on an AP, we never get timer interrupts and boom. I had initially used this quirk as a workaround, with my intent being to move apic initalization to an earlier point in the boot process, so the setting of the register would be irrelevant. Given the work involved in doing that however, the fragile nature of the apic initalization code, and the fact that, over the 2 years since we found this bug, the MCP55 is the only chip which seems to have this issue, I've figure at this point its likely safer to just carry the quirk around. By setting the referenced bits in this hidden register, interrupts will be broadcast to all cpus when the ioapic isn't active on the above described systems. Acked-by: Simon Horman Acked-by: Vivek Goyal Signed-off-by: Neil Horman Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 31 +++++++++++++++++++++++++++++++ include/linux/pci_ids.h | 2 ++ 2 files changed, 33 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 857ae01734a6..034430690a5b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2296,6 +2296,37 @@ DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, PCI_DEVICE_ID_NVIDIA_NVENET_15, nvenet_msi_disable); +/* + * Some versions of the MCP55 bridge from nvidia have a legacy irq routing + * config register. This register controls the routing of legacy interrupts + * from devices that route through the MCP55. If this register is misprogramed + * interrupts are only sent to the bsp, unlike conventional systems where the + * irq is broadxast to all online cpus. Not having this register set + * properly prevents kdump from booting up properly, so lets make sure that + * we have it set correctly. + * Note this is an undocumented register. + */ +static void __devinit nvbridge_check_legacy_irq_routing(struct pci_dev *dev) +{ + u32 cfg; + + pci_read_config_dword(dev, 0x74, &cfg); + + if (cfg & ((1 << 2) | (1 << 15))) { + printk(KERN_INFO "Rewriting irq routing register on MCP55\n"); + cfg &= ~((1 << 2) | (1 << 15)); + pci_write_config_dword(dev, 0x74, cfg); + } +} + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0, + nvbridge_check_legacy_irq_routing); + +DECLARE_PCI_FIXUP_EARLY(PCI_VENDOR_ID_NVIDIA, + PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4, + nvbridge_check_legacy_irq_routing); + static int __devinit ht_check_msi_mapping(struct pci_dev *dev) { int pos, ttl = 48; diff --git a/include/linux/pci_ids.h b/include/linux/pci_ids.h index 570fddeb0388..dc2827723c1e 100644 --- a/include/linux/pci_ids.h +++ b/include/linux/pci_ids.h @@ -1249,6 +1249,8 @@ #define PCI_DEVICE_ID_NVIDIA_GEFORCE_FX_GO5700_2 0x0348 #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_GO1000 0x034C #define PCI_DEVICE_ID_NVIDIA_QUADRO_FX_1100 0x034E +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V0 0x0360 +#define PCI_DEVICE_ID_NVIDIA_MCP55_BRIDGE_V4 0x0364 #define PCI_DEVICE_ID_NVIDIA_NVENET_15 0x0373 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SATA 0x03E7 #define PCI_DEVICE_ID_NVIDIA_NFORCE_MCP61_SMBUS 0x03EB -- cgit v1.2.3 From db5004195481fcb500c929bd3a0e1c0c48eec527 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Wed, 13 Oct 2010 15:00:23 +0900 Subject: PCI: add PCI_MSIX_TABLE/PBA defines These are already defined in pcilib's pci/header.h but not in kernel's linux/pci_regs.h. Copy them to avoid using magic numbers. Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/msi.h | 4 ++-- include/linux/pci_regs.h | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.h b/drivers/pci/msi.h index de27c1cb5a2b..feff3bee6fe5 100644 --- a/drivers/pci/msi.h +++ b/drivers/pci/msi.h @@ -22,8 +22,8 @@ #define is_64bit_address(control) (!!(control & PCI_MSI_FLAGS_64BIT)) #define is_mask_bit_support(control) (!!(control & PCI_MSI_FLAGS_MASKBIT)) -#define msix_table_offset_reg(base) (base + 0x04) -#define msix_pba_offset_reg(base) (base + 0x08) +#define msix_table_offset_reg(base) (base + PCI_MSIX_TABLE) +#define msix_pba_offset_reg(base) (base + PCI_MSIX_PBA) #define msix_table_size(control) ((control & PCI_MSIX_FLAGS_QSIZE)+1) #define multi_msix_capable(control) msix_table_size((control)) diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 455b9ccdfca7..af83076c31a6 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -300,12 +300,14 @@ #define PCI_MSI_DATA_64 12 /* 16 bits of data for 64-bit devices */ #define PCI_MSI_MASK_64 16 /* Mask bits register for 64-bit devices */ -/* MSI-X registers (these are at offset PCI_MSIX_FLAGS) */ +/* MSI-X registers */ #define PCI_MSIX_FLAGS 2 #define PCI_MSIX_FLAGS_QSIZE 0x7FF #define PCI_MSIX_FLAGS_ENABLE (1 << 15) #define PCI_MSIX_FLAGS_MASKALL (1 << 14) -#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) +#define PCI_MSIX_TABLE 4 +#define PCI_MSIX_PBA 8 +#define PCI_MSIX_FLAGS_BIRMASK (7 << 0) /* CompactPCI Hotswap Register */ -- cgit v1.2.3 From 350a55e9ff6005032407d3234af800f413b03af5 Mon Sep 17 00:00:00 2001 From: matt mooney Date: Fri, 24 Sep 2010 12:17:26 -0700 Subject: PCI: use new ccflags variable in Makefile Replace EXTRA_CFLAGS with ccflags-y. Signed-off-by: matt mooney Signed-off-by: Jesse Barnes --- drivers/pci/Makefile | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index dc1aa0922868..dcd7ace9221e 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -65,6 +65,4 @@ obj-$(CONFIG_PCI_SYSCALL) += syscall.o obj-$(CONFIG_PCI_STUB) += pci-stub.o -ifeq ($(CONFIG_PCI_DEBUG),y) -EXTRA_CFLAGS += -DDEBUG -endif +ccflags-$(CONFIG_PCI_DEBUG) := -DDEBUG -- cgit v1.2.3 From 2c6413aee215a43b1f95e218067abcde50ccbc5e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 29 Sep 2010 12:23:21 -0600 Subject: PCI: log vendor/device ID always Previously we had to have CONFIG_PCI_DEBUG=y or CONFIG_DYNAMIC_DEBUG=y to turn on this printk, but I think the IDs are valuable enough that it's worth putting them in the log always. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 12625d90f8b5..c84900da3c59 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -961,8 +961,8 @@ int pci_setup_device(struct pci_dev *dev) dev->class = class; class >>= 8; - dev_dbg(&dev->dev, "found [%04x:%04x] class %06x header type %02x\n", - dev->vendor, dev->device, class, dev->hdr_type); + dev_printk(KERN_DEBUG, &dev->dev, "[%04x:%04x] type %d class %#08x\n", + dev->vendor, dev->device, dev->hdr_type, class); /* need to have dev->class ready */ dev->cfg_size = pci_cfg_space_size(dev); -- cgit v1.2.3 From 1bcd495be9ed3194f618e8af0446459dc52a1423 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 29 Sep 2010 12:23:54 -0600 Subject: PCI: fix message typo I missed the closing parenthesis on "(PCI address ...)". Acked-by: Arnd Bergmann Reported-by: Peter Maydell Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/setup-res.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 2aaa13150de3..bc0e6eea0fff 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -85,7 +85,7 @@ void pci_update_resource(struct pci_dev *dev, int resno) } } res->flags &= ~IORESOURCE_UNSET; - dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx]\n", + dev_info(&dev->dev, "BAR %d: set to %pR (PCI address [%#llx-%#llx])\n", resno, res, (unsigned long long)region.start, (unsigned long long)region.end); } -- cgit v1.2.3 From bf4d29086972ceaeaf72544d8f64933c2cfdc992 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 4 Oct 2010 14:22:26 -0400 Subject: PCI: Export some PCI PM functionality It's helpful to have some extra PCI power management functions available to platform code, so move the declarations to an exported header. Acked-by: Rafael J. Wysocki Signed-off-by: Matthew Garrett Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 3 --- include/linux/pci.h | 3 +++ 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 6beb11b617a9..f5c7c382765f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -63,11 +63,8 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_update_current_state(struct pci_dev *dev, pci_power_t state); extern void pci_disable_enabled_device(struct pci_dev *dev); -extern bool pci_check_pme_status(struct pci_dev *dev); extern int pci_finish_runtime_suspend(struct pci_dev *dev); -extern void pci_wakeup_event(struct pci_dev *dev); extern int __pci_pme_wakeup(struct pci_dev *dev, void *ign); -extern void pci_pme_wakeup_bus(struct pci_bus *bus); extern void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); diff --git a/include/linux/pci.h b/include/linux/pci.h index 30faf4f3db0b..7454408c41b6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -819,6 +819,9 @@ pci_power_t pci_target_state(struct pci_dev *dev); int pci_prepare_to_sleep(struct pci_dev *dev); int pci_back_from_sleep(struct pci_dev *dev); bool pci_dev_run_wake(struct pci_dev *dev); +bool pci_check_pme_status(struct pci_dev *dev); +void pci_wakeup_event(struct pci_dev *dev); +void pci_pme_wakeup_bus(struct pci_bus *bus); static inline int pci_enable_wake(struct pci_dev *dev, pci_power_t state, bool enable) -- cgit v1.2.3 From df17e62e5bff60aeefd0e81165c62f9e46f33217 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Mon, 4 Oct 2010 14:22:29 -0400 Subject: PCI: Add support for polling PME state on suspended legacy PCI devices Not all hardware vendors hook up the PME line for legacy PCI devices, meaning that wakeup events get lost. The only way around this is to poll the devices to see if their state has changed, so add support for doing that on legacy PCI devices that aren't part of the core chipset. Acked-by: Rafael J. Wysocki Signed-off-by: Matthew Garrett Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 77 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 77 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cc232c016ef9..e98c8104297b 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -38,6 +38,19 @@ EXPORT_SYMBOL(pci_pci_problems); unsigned int pci_pm_d3_delay; +static void pci_pme_list_scan(struct work_struct *work); + +static LIST_HEAD(pci_pme_list); +static DEFINE_MUTEX(pci_pme_list_mutex); +static DECLARE_DELAYED_WORK(pci_pme_work, pci_pme_list_scan); + +struct pci_pme_device { + struct list_head list; + struct pci_dev *dev; +}; + +#define PME_TIMEOUT 1000 /* How long between PME checks */ + static void pci_dev_d3_sleep(struct pci_dev *dev) { unsigned int delay = dev->d3_delay; @@ -1331,6 +1344,32 @@ bool pci_pme_capable(struct pci_dev *dev, pci_power_t state) return !!(dev->pme_support & (1 << state)); } +static void pci_pme_list_scan(struct work_struct *work) +{ + struct pci_pme_device *pme_dev; + + mutex_lock(&pci_pme_list_mutex); + if (!list_empty(&pci_pme_list)) { + list_for_each_entry(pme_dev, &pci_pme_list, list) + pci_pme_wakeup(pme_dev->dev, NULL); + schedule_delayed_work(&pci_pme_work, msecs_to_jiffies(PME_TIMEOUT)); + } + mutex_unlock(&pci_pme_list_mutex); +} + +/** + * pci_external_pme - is a device an external PCI PME source? + * @dev: PCI device to check + * + */ + +static bool pci_external_pme(struct pci_dev *dev) +{ + if (pci_is_pcie(dev) || dev->bus->number == 0) + return false; + return true; +} + /** * pci_pme_active - enable or disable PCI device's PME# function * @dev: PCI device to handle. @@ -1354,6 +1393,44 @@ void pci_pme_active(struct pci_dev *dev, bool enable) pci_write_config_word(dev, dev->pm_cap + PCI_PM_CTRL, pmcsr); + /* PCI (as opposed to PCIe) PME requires that the device have + its PME# line hooked up correctly. Not all hardware vendors + do this, so the PME never gets delivered and the device + remains asleep. The easiest way around this is to + periodically walk the list of suspended devices and check + whether any have their PME flag set. The assumption is that + we'll wake up often enough anyway that this won't be a huge + hit, and the power savings from the devices will still be a + win. */ + + if (pci_external_pme(dev)) { + struct pci_pme_device *pme_dev; + if (enable) { + pme_dev = kmalloc(sizeof(struct pci_pme_device), + GFP_KERNEL); + if (!pme_dev) + goto out; + pme_dev->dev = dev; + mutex_lock(&pci_pme_list_mutex); + list_add(&pme_dev->list, &pci_pme_list); + if (list_is_singular(&pci_pme_list)) + schedule_delayed_work(&pci_pme_work, + msecs_to_jiffies(PME_TIMEOUT)); + mutex_unlock(&pci_pme_list_mutex); + } else { + mutex_lock(&pci_pme_list_mutex); + list_for_each_entry(pme_dev, &pci_pme_list, list) { + if (pme_dev->dev == dev) { + list_del(&pme_dev->list); + kfree(pme_dev); + break; + } + } + mutex_unlock(&pci_pme_list_mutex); + } + } + +out: dev_printk(KERN_DEBUG, &dev->dev, "PME# %s\n", enable ? "enabled" : "disabled"); } -- cgit v1.2.3 From b126b4703afa4010b161784a43650337676dd03b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 26 Oct 2010 15:41:39 -0600 Subject: PCI: allocate bus resources from the top down Allocate space from the highest-address PCI bus resource first, then work downward. Previously, we looked for space in PCI host bridge windows in the order we discovered the windows. For example, given the following windows (discovered via an ACPI _CRS method): pci_root PNP0A03:00: host bridge window [mem 0x000a0000-0x000bffff] pci_root PNP0A03:00: host bridge window [mem 0x000c0000-0x000effff] pci_root PNP0A03:00: host bridge window [mem 0x000f0000-0x000fffff] pci_root PNP0A03:00: host bridge window [mem 0xbff00000-0xf7ffffff] pci_root PNP0A03:00: host bridge window [mem 0xff980000-0xff980fff] pci_root PNP0A03:00: host bridge window [mem 0xff97c000-0xff97ffff] pci_root PNP0A03:00: host bridge window [mem 0xfed20000-0xfed9ffff] we attempted to allocate from [mem 0x000a0000-0x000bffff] first, then [mem 0x000c0000-0x000effff], and so on. With this patch, we allocate from [mem 0xff980000-0xff980fff] first, then [mem 0xff97c000-0xff97ffff], [mem 0xfed20000-0xfed9ffff], etc. Allocating top-down follows Windows practice, so we're less likely to trip over BIOS defects in the _CRS description. On the machine above (a Dell T3500), the [mem 0xbff00000-0xbfffffff] region doesn't actually work and is likely a BIOS defect. The symptom is that we move the AHCI controller to 0xbff00000, which leads to "Boot has failed, sleeping forever," a BUG in ahci_stop_engine(), or some other boot failure. Reference: https://bugzilla.kernel.org/show_bug.cgi?id=16228#c43 Reference: https://bugzilla.redhat.com/show_bug.cgi?id=620313 Reference: https://bugzilla.redhat.com/show_bug.cgi?id=629933 Reported-by: Brian Bloniarz Reported-and-tested-by: Stefan Becker Reported-by: Denys Vlasenko Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 53 ++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 7f0af0e9b826..172bf26e0680 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -64,6 +64,49 @@ void pci_bus_remove_resources(struct pci_bus *bus) } } +/* + * Find the highest-address bus resource below the cursor "res". If the + * cursor is NULL, return the highest resource. + */ +static struct resource *pci_bus_find_resource_prev(struct pci_bus *bus, + unsigned int type, + struct resource *res) +{ + struct resource *r, *prev = NULL; + int i; + + pci_bus_for_each_resource(bus, r, i) { + if (!r) + continue; + + if ((r->flags & IORESOURCE_TYPE_BITS) != type) + continue; + + /* If this resource is at or past the cursor, skip it */ + if (res) { + if (r == res) + continue; + if (r->end > res->end) + continue; + if (r->end == res->end && r->start > res->start) + continue; + } + + if (!prev) + prev = r; + + /* + * A small resource is higher than a large one that ends at + * the same address. + */ + if (r->end > prev->end || + (r->end == prev->end && r->start > prev->start)) + prev = r; + } + + return prev; +} + /** * pci_bus_alloc_resource - allocate a resource from a parent bus * @bus: PCI bus @@ -89,9 +132,10 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, resource_size_t), void *alignf_data) { - int i, ret = -ENOMEM; + int ret = -ENOMEM; struct resource *r; resource_size_t max = -1; + unsigned int type = res->flags & IORESOURCE_TYPE_BITS; type_mask |= IORESOURCE_IO | IORESOURCE_MEM; @@ -99,10 +143,9 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, if (!(res->flags & IORESOURCE_MEM_64)) max = PCIBIOS_MAX_MEM_32; - pci_bus_for_each_resource(bus, r, i) { - if (!r) - continue; - + /* Look for space at highest addresses first */ + r = pci_bus_find_resource_prev(bus, type, NULL); + for ( ; r; r = pci_bus_find_resource_prev(bus, type, r)) { /* type_mask must match */ if ((res->flags ^ r->flags) & type_mask) continue; -- cgit v1.2.3