From 9eff02e2042f96fb2aedd02e032eca1c5333d767 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Fri, 24 Oct 2008 10:32:33 -0700 Subject: [PATCH 01/98] PCI: check mmap range of /proc/bus/pci files too /proc/bus/pci allows you to mmap resource ranges too, so we should probably be checking to make sure the mapping is somewhat valid. Uses the same code as the recent sysfs mmap range checking patch from Linus. Acked-by: David Miller Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 2 +- drivers/pci/pci.h | 4 ++++ drivers/pci/proc.c | 11 ++++++++++- 3 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index c88485860a0a..388440e0d222 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -569,7 +569,7 @@ void pci_remove_legacy_files(struct pci_bus *b) #ifdef HAVE_PCI_MMAP -static int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) +int pci_mmap_fits(struct pci_dev *pdev, int resno, struct vm_area_struct *vma) { unsigned long nr, start, size; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9de87e9f98f5..d3e65e29df51 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -10,6 +10,10 @@ extern int pci_uevent(struct device *dev, struct kobj_uevent_env *env); extern int pci_create_sysfs_dev_files(struct pci_dev *pdev); extern void pci_remove_sysfs_dev_files(struct pci_dev *pdev); extern void pci_cleanup_rom(struct pci_dev *dev); +#ifdef HAVE_PCI_MMAP +extern int pci_mmap_fits(struct pci_dev *pdev, int resno, + struct vm_area_struct *vma); +#endif /** * Firmware PM callbacks diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index e1098c302c45..7fb086d39617 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -252,11 +252,20 @@ static int proc_bus_pci_mmap(struct file *file, struct vm_area_struct *vma) const struct proc_dir_entry *dp = PDE(inode); struct pci_dev *dev = dp->data; struct pci_filp_private *fpriv = file->private_data; - int ret; + int i, ret; if (!capable(CAP_SYS_RAWIO)) return -EPERM; + /* Make sure the caller is mapping a real resource for this device */ + for (i = 0; i < PCI_ROM_RESOURCE; i++) { + if (pci_mmap_fits(dev, i, vma)) + break; + } + + if (i >= PCI_ROM_RESOURCE) + return -ENODEV; + ret = pci_mmap_page_range(dev, vma, fpriv->mmap_state, fpriv->write_combine); From c7b4fee3808a061ee0e704ba596ace56bf65a83d Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Fri, 24 Oct 2008 14:26:35 +0900 Subject: [PATCH 02/98] PCI hotplug: pciehp: remove unnecessary wait after turning power off The pciehp driver waits for 1000 msec after turning power off to make sure the power has been completely removed. But this 1000 msec wait is not needed if a slot doesn't implement power control because software cannot control the power. Power will be automatically removed at adapter removal time on such a slot Tested-by: "Phil Endecott" Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_ctrl.c | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index fead63c6b49e..ff4034502d24 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -178,15 +178,14 @@ static void set_slot_off(struct controller *ctrl, struct slot * pslot) "Issue of Slot Power Off command failed\n"); return; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) pslot->hpc_ops->green_led_off(pslot); @@ -286,15 +285,14 @@ static int remove_board(struct slot *p_slot) "Issue of Slot Disable command failed\n"); return retval; } + /* + * After turning power off, we must wait for at least 1 second + * before taking any action that relies on power having been + * removed from the slot/adapter. + */ + msleep(1000); } - /* - * After turning power off, we must wait for at least 1 second - * before taking any action that relies on power having been - * removed from the slot/adapter. - */ - msleep(1000); - if (PWR_LED(ctrl)) /* turn off Green LED */ p_slot->hpc_ops->green_led_off(p_slot); From bfb0f330a6c833fd12c35b907434256b4211a1dc Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Mon, 27 Oct 2008 17:50:21 -0700 Subject: [PATCH 03/98] PCI: fixup whitespace in quirks.c Had a space before tab in do_fixups, prototype wasn't wrapped properly either. Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index ce0985615133..e915a17b36c3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2074,11 +2074,12 @@ DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x4375, #endif /* CONFIG_PCI_MSI */ -static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, struct pci_fixup *end) +static void pci_do_fixups(struct pci_dev *dev, struct pci_fixup *f, + struct pci_fixup *end) { while (f < end) { if ((f->vendor == dev->vendor || f->vendor == (u16) PCI_ANY_ID) && - (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { + (f->device == dev->device || f->device == (u16) PCI_ANY_ID)) { dev_dbg(&dev->dev, "calling %pF\n", f->hook); f->hook(dev); } From 1a9271331ab663f3c7cda78d86b884f2ea86d4d7 Mon Sep 17 00:00:00 2001 From: Kay Sievers Date: Thu, 30 Oct 2008 02:17:49 +0100 Subject: [PATCH 04/98] PCI: struct device - replace bus_id with dev_name(), dev_set_name() This patch is part of a larger patch series which will remove the "char bus_id[20]" name string from struct device. The device name is managed in the kobject anyway, and without any size limitation, and just needlessly copied into "struct device". To set and read the device name dev_name(dev) and dev_set_name(dev) must be used. If your code uses static kobjects, which it shouldn't do, "const char *init_name" can be used to statically provide the name the registered device should have. At registration time, the init_name field is cleared, to enforce the use of dev_name(dev) to access the device name at a later time. We need to get rid of all occurrences of bus_id in the entire tree to be able to enable the new interface. Please apply this patch, and possibly convert any remaining remaining occurrences of bus_id. Acked-by: Greg Kroah-Hartman Signed-Off-By: Kay Sievers Signed-off-by: Jesse Barnes --- arch/x86/kernel/pci-dma.c | 2 +- drivers/pci/hotplug/acpiphp_ibm.c | 2 +- drivers/pci/irq.c | 2 +- drivers/pci/pci-acpi.c | 2 +- drivers/pci/pcie/portdrv_core.c | 2 +- drivers/pci/probe.c | 6 +++--- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/arch/x86/kernel/pci-dma.c b/arch/x86/kernel/pci-dma.c index 19a1044a0cd9..b25428533141 100644 --- a/arch/x86/kernel/pci-dma.c +++ b/arch/x86/kernel/pci-dma.c @@ -38,7 +38,7 @@ EXPORT_SYMBOL(bad_dma_address); be probably a smaller DMA mask, but this is bug-to-bug compatible to older i386. */ struct device x86_dma_fallback_dev = { - .bus_id = "fallback device", + .init_name = "fallback device", .coherent_dma_mask = DMA_32BIT_MASK, .dma_mask = &x86_dma_fallback_dev.coherent_dma_mask, }; diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c index 881fdd2b7313..5befa7e379b7 100644 --- a/drivers/pci/hotplug/acpiphp_ibm.c +++ b/drivers/pci/hotplug/acpiphp_ibm.c @@ -271,7 +271,7 @@ static void ibm_handle_events(acpi_handle handle, u32 event, void *context) dbg("%s: generationg bus event\n", __func__); acpi_bus_generate_proc_event(note->device, note->event, detail); acpi_bus_generate_netlink_event(note->device->pnp.device_class, - note->device->dev.bus_id, + dev_name(¬e->device->dev), note->event, detail); } else note->event = event; diff --git a/drivers/pci/irq.c b/drivers/pci/irq.c index 6441dfa969a3..de01174aff06 100644 --- a/drivers/pci/irq.c +++ b/drivers/pci/irq.c @@ -15,7 +15,7 @@ static void pci_note_irq_problem(struct pci_dev *pdev, const char *reason) dev_printk(KERN_ERR, &pdev->dev, "Potentially misrouted IRQ (Bridge %s %04x:%04x)\n", - parent->dev.bus_id, parent->vendor, parent->device); + dev_name(&parent->dev), parent->vendor, parent->device); dev_printk(KERN_ERR, &pdev->dev, "%s\n", reason); dev_printk(KERN_ERR, &pdev->dev, "Please report to linux-kernel@vger.kernel.org\n"); WARN_ON(1); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index ae5ec76dca77..2ed3f10d0860 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -375,7 +375,7 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) * The string should be the same as root bridge's name * Please look at 'pci_scan_bus_parented' */ - num = sscanf(dev->bus_id, "pci%04x:%02x", &seg, &bus); + num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); if (num != 2) return -ENODEV; *handle = acpi_get_pci_rootbridge_handle(seg, bus); diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 2e091e014829..75f501ab6468 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -224,7 +224,7 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, device->driver = NULL; device->driver_data = NULL; device->release = release_pcie_device; /* callback to free pcie dev */ - snprintf(device->bus_id, sizeof(device->bus_id), "%s:pcie%02x", + dev_set_name(device, "%s:pcie%02x", pci_name(parent), get_descriptor_id(port_type, service_type)); device->parent = &parent->dev; } diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5b3f5937ecf5..eb2b985beb48 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -412,7 +412,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, * registered later in pci_bus_add_devices() */ child->dev.class = &pcibus_class; - sprintf(child->dev.bus_id, "%04x:%02x", pci_domain_nr(child), busnr); + dev_set_name(&child->dev, "%04x:%02x", pci_domain_nr(child), busnr); /* * Set up the primary, secondary and subordinate @@ -1130,7 +1130,7 @@ struct pci_bus * pci_create_bus(struct device *parent, memset(dev, 0, sizeof(*dev)); dev->parent = parent; dev->release = pci_release_bus_bridge_dev; - sprintf(dev->bus_id, "pci%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(dev, "pci%04x:%02x", pci_domain_nr(b), bus); error = device_register(dev); if (error) goto dev_reg_err; @@ -1141,7 +1141,7 @@ struct pci_bus * pci_create_bus(struct device *parent, b->dev.class = &pcibus_class; b->dev.parent = b->bridge; - sprintf(b->dev.bus_id, "%04x:%02x", pci_domain_nr(b), bus); + dev_set_name(&b->dev, "%04x:%02x", pci_domain_nr(b), bus); error = device_register(&b->dev); if (error) goto class_dev_reg_err; From d91cdc745524a1b1ff537712a62803b8413c12d6 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 11 Nov 2008 17:17:47 +0800 Subject: [PATCH 05/98] PCI: Refactor pci_reset_function() Separate out function level reset so that pci_reset_function can be more easily extended. Signed-off-by: Sheng Yang Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 66 ++++++++++++++++++++++++++++------------------- 1 file changed, 40 insertions(+), 26 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 061d1ee0046a..62978f644a92 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1751,24 +1751,7 @@ int pci_set_dma_seg_boundary(struct pci_dev *dev, unsigned long mask) EXPORT_SYMBOL(pci_set_dma_seg_boundary); #endif -/** - * pci_execute_reset_function() - Reset a PCI device function - * @dev: Device function 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. - * - * The device function is presumed to be unused when this function is called. - * Resetting the device will make the contents of PCI configuration space - * random, so any caller of this must be prepared to reinitialise the - * device including MSI, bus mastering, BARs, decoding IO and memory spaces, - * etc. - * - * Returns 0 if the device function was successfully reset or -ENOTTY if the - * device doesn't support resetting a single function. - */ -int pci_execute_reset_function(struct pci_dev *dev) +static int __pcie_flr(struct pci_dev *dev, int probe) { u16 status; u32 cap; @@ -1780,6 +1763,9 @@ int pci_execute_reset_function(struct pci_dev *dev) if (!(cap & PCI_EXP_DEVCAP_FLR)) return -ENOTTY; + if (probe) + return 0; + pci_block_user_cfg_access(dev); /* Wait for Transaction Pending bit clean */ @@ -1802,6 +1788,39 @@ int pci_execute_reset_function(struct pci_dev *dev) pci_unblock_user_cfg_access(dev); return 0; } + +static int __pci_reset_function(struct pci_dev *pdev, int probe) +{ + int res; + + res = __pcie_flr(pdev, probe); + if (res != -ENOTTY) + return res; + + return res; +} + +/** + * pci_execute_reset_function() - Reset a PCI device function + * @dev: Device function 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. + * + * The device function is presumed to be unused when this function is called. + * Resetting the device will make the contents of PCI configuration space + * random, so any caller of this must be prepared to reinitialise the + * device including MSI, bus mastering, BARs, decoding IO and memory spaces, + * etc. + * + * Returns 0 if the device function was successfully reset or -ENOTTY if the + * device doesn't support resetting a single function. + */ +int pci_execute_reset_function(struct pci_dev *dev) +{ + return __pci_reset_function(dev, 0); +} EXPORT_SYMBOL_GPL(pci_execute_reset_function); /** @@ -1822,15 +1841,10 @@ EXPORT_SYMBOL_GPL(pci_execute_reset_function); */ int pci_reset_function(struct pci_dev *dev) { - u32 cap; - int exppos = pci_find_capability(dev, PCI_CAP_ID_EXP); - int r; + int r = __pci_reset_function(dev, 1); - if (!exppos) - return -ENOTTY; - pci_read_config_dword(dev, exppos + PCI_EXP_DEVCAP, &cap); - if (!(cap & PCI_EXP_DEVCAP_FLR)) - return -ENOTTY; + if (r < 0) + return r; if (!dev->msi_enabled && !dev->msix_enabled && dev->irq != 0) disable_irq(dev->irq); From f7b7baae6b30ff04124259ff8d7c0c0d281320e6 Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 11 Nov 2008 17:17:46 +0800 Subject: [PATCH 06/98] PCI: add PCI Advanced Feature Capability defines PCI Advanced Features Capability is introduced by "Conventional PCI Advanced Caps ECN" (can be downloaded in pcisig.com). Add defines for the various AF capabilities, including function level reset (FLR). Reviewed-by: Matthew Wilcox Signed-off-by: Sheng Yang Signed-off-by: Jesse Barnes --- include/linux/pci_regs.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index e5effd47ed74..7766488470e4 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -210,6 +210,7 @@ #define PCI_CAP_ID_AGP3 0x0E /* AGP Target PCI-PCI bridge */ #define PCI_CAP_ID_EXP 0x10 /* PCI Express */ #define PCI_CAP_ID_MSIX 0x11 /* MSI-X */ +#define PCI_CAP_ID_AF 0x13 /* PCI Advanced Features */ #define PCI_CAP_LIST_NEXT 1 /* Next capability in the list */ #define PCI_CAP_FLAGS 2 /* Capability defined flags (16 bits) */ #define PCI_CAP_SIZEOF 4 @@ -316,6 +317,17 @@ #define PCI_CHSWP_EXT 0x40 /* ENUM# status - extraction */ #define PCI_CHSWP_INS 0x80 /* ENUM# status - insertion */ +/* PCI Advanced Feature registers */ + +#define PCI_AF_LENGTH 2 +#define PCI_AF_CAP 3 +#define PCI_AF_CAP_TP 0x01 +#define PCI_AF_CAP_FLR 0x02 +#define PCI_AF_CTRL 4 +#define PCI_AF_CTRL_FLR 0x01 +#define PCI_AF_STATUS 5 +#define PCI_AF_STATUS_TP 0x01 + /* PCI-X registers */ #define PCI_X_CMD 2 /* Modes & Features */ From 1ca887970a3971a22e4875b7c6ad5ae3ce49f61a Mon Sep 17 00:00:00 2001 From: Sheng Yang Date: Tue, 11 Nov 2008 17:17:48 +0800 Subject: [PATCH 07/98] PCI: Extend pci_reset_function() to support PCI Advanced Features Some PCI devices implement PCI Advanced Features, which means they support Function Level Reset(FLR). Implement support for that in pci_reset_function. Signed-off-by: Sheng Yang Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 62978f644a92..3c2fa2fdc9cd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1789,6 +1789,43 @@ static int __pcie_flr(struct pci_dev *dev, int probe) return 0; } +static int __pci_af_flr(struct pci_dev *dev, int probe) +{ + int cappos = pci_find_capability(dev, PCI_CAP_ID_AF); + u8 status; + u8 cap; + + if (!cappos) + return -ENOTTY; + pci_read_config_byte(dev, cappos + PCI_AF_CAP, &cap); + if (!(cap & PCI_AF_CAP_TP) || !(cap & PCI_AF_CAP_FLR)) + return -ENOTTY; + + if (probe) + return 0; + + pci_block_user_cfg_access(dev); + + /* Wait for Transaction Pending bit clean */ + msleep(100); + pci_read_config_byte(dev, cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) { + dev_info(&dev->dev, "Busy after 100ms while trying to" + " reset; sleeping for 1 second\n"); + ssleep(1); + pci_read_config_byte(dev, + cappos + PCI_AF_STATUS, &status); + if (status & PCI_AF_STATUS_TP) + dev_info(&dev->dev, "Still busy after 1s; " + "proceeding with reset anyway\n"); + } + pci_write_config_byte(dev, cappos + PCI_AF_CTRL, PCI_AF_CTRL_FLR); + mdelay(100); + + pci_unblock_user_cfg_access(dev); + return 0; +} + static int __pci_reset_function(struct pci_dev *pdev, int probe) { int res; @@ -1797,6 +1834,10 @@ static int __pci_reset_function(struct pci_dev *pdev, int probe) if (res != -ENOTTY) return res; + res = __pci_af_flr(pdev, probe); + if (res != -ENOTTY) + return res; + return res; } From d3a54014e2a94bd37b7dee5e76e03f7bc4fab49a Mon Sep 17 00:00:00 2001 From: Benjamin Herrenschmidt Date: Wed, 12 Nov 2008 14:38:53 +1100 Subject: [PATCH 08/98] PCI: Add legacy_io/mem to all busses Currently, only PHBs get the legacy_* files, which makes it tricky for userland to get access to the legacy space. This commit exposes them in every bus, since even child buses may forward legacy cycles if configured properly. Signed-off-by: Benjamin Herrenschmidt Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 999cc4088b59..3e1c135b174a 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -158,6 +158,10 @@ void pci_bus_add_devices(struct pci_bus *bus) dev_err(&dev->dev, "Error creating cpulistaffinity" " file, continuing...\n"); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(child_bus); + } } } From 8b62091e20215730be1b94b7cd135a78a3e692ca Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:30:40 -0700 Subject: [PATCH 09/98] ACPI/PCI: include missing acpi.h file in pci-acpi.h. The pci-acpi.h file will not compile without including linux/acpi.h. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- include/linux/pci-acpi.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 8837928fbf33..a9e4c34e9389 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -8,6 +8,8 @@ #ifndef _PCI_ACPI_H_ #define _PCI_ACPI_H_ +#include + #define OSC_QUERY_TYPE 0 #define OSC_SUPPORT_TYPE 1 #define OSC_CONTROL_TYPE 2 From 990a7ac5645883a833a11b900bb6f25b65dea65b Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:30:45 -0700 Subject: [PATCH 10/98] ACPI/PCI: call _OSC support during root bridge discovery Add pci_acpi_osc_support() and call it when a PCI bridge is added. This allows us to avoid having every individual PCI root bridge driver call _OSC support for every root bridge in their probe functions, a significant savings in boot time. Signed-off-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/acpi/pci_root.c | 9 +++++++++ drivers/pci/pci-acpi.c | 24 +++++++++++++++++++----- include/linux/pci-acpi.h | 1 + 3 files changed, 29 insertions(+), 5 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 642554b1b60c..de4d57114fe4 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -31,6 +31,7 @@ #include #include #include +#include #include #include #include @@ -193,6 +194,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) unsigned long long value = 0; acpi_handle handle = NULL; struct acpi_device *child; + u32 flags; if (!device) @@ -210,6 +212,13 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) device->ops.bind = acpi_pci_bind; + /* + * All supported architectures that use ACPI have support for + * PCI domains, so we indicate this in _OSC support capabilities. + */ + flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + pci_acpi_osc_support(device->handle, flags); + /* * Segment * ------- diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 2ed3f10d0860..8a1f02c3c915 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -143,28 +143,42 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, return status; } -static acpi_status acpi_query_osc(acpi_handle handle, - u32 level, void *context, void **retval) +/* + * pci_acpi_osc_support: Invoke _OSC indicating support for the given feature + * @flags: Bitmask of flags to support + * + * See the ACPI spec for the definition of the flags + */ +int pci_acpi_osc_support(acpi_handle handle, u32 flags) { + u32 dummy; acpi_status status; - struct acpi_osc_data *osc_data; - u32 flags = (unsigned long)context, dummy; acpi_handle tmp; + struct acpi_osc_data *osc_data; + int rc = 0; status = acpi_get_handle(handle, "_OSC", &tmp); if (ACPI_FAILURE(status)) - return AE_OK; + return -ENOTTY; mutex_lock(&pci_acpi_lock); osc_data = acpi_get_osc_data(handle); if (!osc_data) { printk(KERN_ERR "acpi osc data array is full\n"); + rc = -ENOMEM; goto out; } __acpi_query_osc(flags, osc_data, &dummy); out: mutex_unlock(&pci_acpi_lock); + return rc; +} + +static acpi_status acpi_query_osc(acpi_handle handle, u32 level, + void *context, void **retval) +{ + pci_acpi_osc_support(handle, (unsigned long)context); return AE_OK; } diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index a9e4c34e9389..424f06f84cab 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -51,6 +51,7 @@ #ifdef CONFIG_ACPI extern acpi_status pci_osc_control_set(acpi_handle handle, u32 flags); extern acpi_status __pci_osc_support_set(u32 flags, const char *hid); +int pci_acpi_osc_support(acpi_handle handle, u32 flags); static inline acpi_status pci_osc_support_set(u32 flags) { return __pci_osc_support_set(flags, PCI_ROOT_HID_STRING); From 0ef5f8f6159e44b4faa997be08d1a3bcbf44ad08 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:30:50 -0700 Subject: [PATCH 11/98] ACPI/PCI: PCI extended config _OSC support called when root bridge added The _OSC capability OSC_EXT_PCI_CONFIG_SUPPORT is set when the root bridge is added with pci_acpi_osc_support() if we can access PCI extended config space. This adds the function pci_ext_cfg_avail which returns true if we can access PCI extended config space (offset greater than 0xff). It currently only returns false if arch=x86 and raw_pci_ext_ops is not set (which might happen if pci=nommcfg is set on the kernel command-line). Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- arch/x86/pci/common.c | 8 ++++++++ drivers/acpi/pci_root.c | 10 ++++++++-- drivers/pci/pci.c | 13 +++++++++++++ include/linux/pci.h | 2 ++ 4 files changed, 31 insertions(+), 2 deletions(-) diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 62ddb73e09ed..9ab8509f7b15 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -562,6 +562,14 @@ void pcibios_disable_device (struct pci_dev *dev) pcibios_disable_irq(dev); } +int pci_ext_cfg_avail(struct pci_dev *dev) +{ + if (raw_pci_ext_ops) + return 1; + else + return 0; +} + struct pci_bus * __devinit pci_scan_bus_on_node(int busno, struct pci_ops *ops, int node) { struct pci_bus *bus = NULL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index de4d57114fe4..96e68e841539 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -194,7 +194,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) unsigned long long value = 0; acpi_handle handle = NULL; struct acpi_device *child; - u32 flags; + u32 flags, base_flags; if (!device) @@ -216,7 +216,7 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) * All supported architectures that use ACPI have support for * PCI domains, so we indicate this in _OSC support capabilities. */ - flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; + flags = base_flags = OSC_PCI_SEGMENT_GROUPS_SUPPORT; pci_acpi_osc_support(device->handle, flags); /* @@ -344,6 +344,12 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) list_for_each_entry(child, &device->children, node) acpi_pci_bridge_scan(child); + /* Indicate support for various _OSC capabilities. */ + if (pci_ext_cfg_avail(root->bus->self)) + flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + if (flags != base_flags) + pci_acpi_osc_support(device->handle, flags); + end: if (result) { if (!list_empty(&root->node)) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3c2fa2fdc9cd..48fa860276d4 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2084,6 +2084,19 @@ static void __devinit pci_no_domains(void) #endif } +/** + * pci_ext_cfg_enabled - can we access extended PCI config space? + * @dev: The PCI device of the root bridge. + * + * Returns 1 if we can access PCI extended config space (offsets + * greater than 0xff). This is the default implementation. Architecture + * implementations can override this. + */ +int __attribute__ ((weak)) pci_ext_cfg_avail(struct pci_dev *dev) +{ + return 1; +} + static int __devinit pci_init(void) { struct pci_dev *dev = NULL; diff --git a/include/linux/pci.h b/include/linux/pci.h index 4bb156ba854a..6fd47654ca4e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1140,6 +1140,8 @@ static inline void pci_mmcfg_early_init(void) { } static inline void pci_mmcfg_late_init(void) { } #endif +int pci_ext_cfg_avail(struct pci_dev *dev); + #ifdef CONFIG_HAS_IOMEM static inline void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) { From 3e1b16002af29758b6bc9c38939d43838d9335bc Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:30:55 -0700 Subject: [PATCH 12/98] ACPI/PCI: PCIe ASPM _OSC support capabilities called when root bridge added The _OSC capabilities OSC_ACTIVE_STATE_PWR_SUPPORT and OSC_CLOCK_PWR_CAPABILITY_SUPPORT are set when the root bridge is added with pci_acpi_osc_support(), so we no longer need to do it in the ASPM driver. Also add the function pcie_aspm_enabled, which returns true if pcie_aspm=off is not on the kernel command-line. Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/acpi/pci_root.c | 3 +++ drivers/pci/pcie/aspm.c | 27 +++++++++------------------ include/linux/pci.h | 9 +++++++++ 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 96e68e841539..9fe026b1c9d0 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -347,6 +347,9 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) /* Indicate support for various _OSC capabilities. */ if (pci_ext_cfg_avail(root->bus->self)) flags |= OSC_EXT_PCI_CONFIG_SUPPORT; + if (pcie_aspm_enabled()) + flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | + OSC_CLOCK_PWR_CAPABILITY_SUPPORT; if (flags != base_flags) pci_acpi_osc_support(device->handle, flags); diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 9aad608bcf3f..e361c7dc726f 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -857,24 +857,15 @@ void pcie_no_aspm(void) aspm_disabled = 1; } -#ifdef CONFIG_ACPI -#include -#include -static void pcie_aspm_platform_init(void) +/** + * pcie_aspm_enabled - is PCIe ASPM enabled? + * + * Returns true if ASPM has not been disabled by the command-line option + * pcie_aspm=off. + **/ +int pcie_aspm_enabled(void) { - pcie_osc_support_set(OSC_ACTIVE_STATE_PWR_SUPPORT| - OSC_CLOCK_PWR_CAPABILITY_SUPPORT); + return !aspm_disabled; } -#else -static inline void pcie_aspm_platform_init(void) { } -#endif +EXPORT_SYMBOL(pcie_aspm_enabled); -static int __init pcie_aspm_init(void) -{ - if (aspm_disabled) - return 0; - pcie_aspm_platform_init(); - return 0; -} - -fs_initcall(pcie_aspm_init); diff --git a/include/linux/pci.h b/include/linux/pci.h index 6fd47654ca4e..eae97a2bf603 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -791,6 +791,15 @@ extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); extern void pci_restore_msi_state(struct pci_dev *dev); #endif +#ifndef CONFIG_PCIEASPM +static inline int pcie_aspm_enabled(void) +{ + return 0; +} +#else +extern int pcie_aspm_enabled(void); +#endif + #ifdef CONFIG_HT_IRQ /* The functions a driver should call */ int ht_create_irq(struct pci_dev *dev, int idx); From eb9188bdb9d65aeead2382ec3dd656a17ec8936d Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:31:00 -0700 Subject: [PATCH 13/98] ACPI/PCI: PCIe AER _OSC support capabilities called when root bridge added The _OSC capability OSC_EXT_PCI_CONFIG_SUPPORT is set when the root bridge is added with pci_acpi_osc_support(), so we no longer need to do it in the PCIe AER driver. Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_acpi.c | 1 - 1 file changed, 1 deletion(-) diff --git a/drivers/pci/pcie/aer/aerdrv_acpi.c b/drivers/pci/pcie/aer/aerdrv_acpi.c index 6dd7b13e9808..ebce26c37049 100644 --- a/drivers/pci/pcie/aer/aerdrv_acpi.c +++ b/drivers/pci/pcie/aer/aerdrv_acpi.c @@ -38,7 +38,6 @@ int aer_osc_setup(struct pcie_device *pciedev) handle = acpi_find_root_bridge_handle(pdev); if (handle) { - pcie_osc_support_set(OSC_EXT_PCI_CONFIG_SUPPORT); status = pci_osc_control_set(handle, OSC_PCI_EXPRESS_AER_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); From 07ae95f988a34465bdcb384bfa73c03424fe2312 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:31:05 -0700 Subject: [PATCH 14/98] ACPI/PCI: PCI MSI _OSC support capabilities called when root bridge added The _OSC capability OSC_MSI_SUPPORT is set when the root bridge is added with pci_acpi_osc_support(), so we no longer need to do it in the PCI MSI driver. Also adds the function pci_msi_enabled, which returns true if pci=nomsi is not on the kernel command-line. Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/acpi/pci_root.c | 2 ++ drivers/pci/msi.c | 33 ++++++++++++--------------------- drivers/pci/pci.c | 2 -- drivers/pci/pci.h | 2 -- include/linux/pci.h | 5 +++++ 5 files changed, 19 insertions(+), 25 deletions(-) diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 9fe026b1c9d0..5b38a026d122 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -350,6 +350,8 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device) if (pcie_aspm_enabled()) flags |= OSC_ACTIVE_STATE_PWR_SUPPORT | OSC_CLOCK_PWR_CAPABILITY_SUPPORT; + if (pci_msi_enabled()) + flags |= OSC_MSI_SUPPORT; if (flags != base_flags) pci_acpi_osc_support(device->handle, flags); diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 11a51f8ed3b3..b4a90badd0a6 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -776,28 +776,19 @@ void pci_no_msi(void) pci_msi_enable = 0; } +/** + * pci_msi_enabled - is MSI enabled? + * + * Returns true if MSI has not been disabled by the command-line option + * pci=nomsi. + **/ +int pci_msi_enabled(void) +{ + return pci_msi_enable; +} +EXPORT_SYMBOL(pci_msi_enabled); + void pci_msi_init_pci_dev(struct pci_dev *dev) { INIT_LIST_HEAD(&dev->msi_list); } - -#ifdef CONFIG_ACPI -#include -#include -static void __devinit msi_acpi_init(void) -{ - if (acpi_pci_disabled) - return; - pci_osc_support_set(OSC_MSI_SUPPORT); - pcie_osc_support_set(OSC_MSI_SUPPORT); -} -#else -static inline void msi_acpi_init(void) { } -#endif /* CONFIG_ACPI */ - -void __devinit msi_init(void) -{ - if (!pci_msi_enable) - return; - msi_acpi_init(); -} diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 48fa860276d4..2cfa41e367a7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2105,8 +2105,6 @@ static int __devinit pci_init(void) pci_fixup_device(pci_fixup_final, dev); } - msi_init(); - return 0; } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d3e65e29df51..9162e242b99e 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -102,11 +102,9 @@ extern unsigned int pci_pm_d3_delay; #ifdef CONFIG_PCI_MSI void pci_no_msi(void); extern void pci_msi_init_pci_dev(struct pci_dev *dev); -extern void __devinit msi_init(void); #else static inline void pci_no_msi(void) { } static inline void pci_msi_init_pci_dev(struct pci_dev *dev) { } -static inline void msi_init(void) { } #endif #ifdef CONFIG_PCIEAER diff --git a/include/linux/pci.h b/include/linux/pci.h index eae97a2bf603..59a3dc2059d3 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -779,6 +779,10 @@ static inline void msi_remove_pci_irq_vectors(struct pci_dev *dev) static inline void pci_restore_msi_state(struct pci_dev *dev) { } +static inline int pci_msi_enabled(void) +{ + return 0; +} #else extern int pci_enable_msi(struct pci_dev *dev); extern void pci_msi_shutdown(struct pci_dev *dev); @@ -789,6 +793,7 @@ extern void pci_msix_shutdown(struct pci_dev *dev); extern void pci_disable_msix(struct pci_dev *dev); extern void msi_remove_pci_irq_vectors(struct pci_dev *dev); extern void pci_restore_msi_state(struct pci_dev *dev); +extern int pci_msi_enabled(void); #endif #ifndef CONFIG_PCIEASPM From 23616941914917cf25b94789856b5326b68d8ee8 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 10 Nov 2008 15:31:10 -0700 Subject: [PATCH 15/98] ACPI/PCI: remove obsolete _OSC capability support functions The acpi_query_osc, __pci_osc_support_set, pci_osc_support_set, and pcie_osc_support_set functions have been obsoleted in favor of setting these capabilities during root bridge discovery with pci_acpi_osc_support. There are no longer any callers of these functions, so remove them. Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 25 ------------------------- include/linux/pci-acpi.h | 11 ----------- 2 files changed, 36 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 8a1f02c3c915..8f923ee5177f 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -175,31 +175,6 @@ out: return rc; } -static acpi_status acpi_query_osc(acpi_handle handle, u32 level, - void *context, void **retval) -{ - pci_acpi_osc_support(handle, (unsigned long)context); - return AE_OK; -} - -/** - * __pci_osc_support_set - register OS support to Firmware - * @flags: OS support bits - * @hid: hardware ID - * - * Update OS support fields and doing a _OSC Query to obtain an update - * from Firmware on supported control bits. - **/ -acpi_status __pci_osc_support_set(u32 flags, const char *hid) -{ - if (!(flags & OSC_SUPPORT_MASKS)) - return AE_TYPE; - - acpi_get_devices(hid, acpi_query_osc, - (void *)(unsigned long)flags, NULL); - return AE_OK; -} - /** * pci_osc_control_set - commit requested control to Firmware * @handle: acpi_handle for the target ACPI object diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 424f06f84cab..871e096e0fbc 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -50,16 +50,7 @@ #ifdef CONFIG_ACPI extern acpi_status pci_osc_control_set(acpi_handle handle, u32 flags); -extern acpi_status __pci_osc_support_set(u32 flags, const char *hid); int pci_acpi_osc_support(acpi_handle handle, u32 flags); -static inline acpi_status pci_osc_support_set(u32 flags) -{ - return __pci_osc_support_set(flags, PCI_ROOT_HID_STRING); -} -static inline acpi_status pcie_osc_support_set(u32 flags) -{ - return __pci_osc_support_set(flags, PCI_EXPRESS_ROOT_HID_STRING); -} static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { /* Find root host bridge */ @@ -76,8 +67,6 @@ typedef u32 acpi_status; #endif static inline acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) {return AE_ERROR;} -static inline acpi_status pci_osc_support_set(u32 flags) {return AE_ERROR;} -static inline acpi_status pcie_osc_support_set(u32 flags) {return AE_ERROR;} static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) { return NULL; } #endif From e8de1481fd7126ee9e93d6889da6f00c05e1e019 Mon Sep 17 00:00:00 2001 From: Arjan van de Ven Date: Wed, 22 Oct 2008 19:55:31 -0700 Subject: [PATCH 16/98] resource: allow MMIO exclusivity for device drivers Device drivers that use pci_request_regions() (and similar APIs) have a reasonable expectation that they are the only ones accessing their device. As part of the e1000e hunt, we were afraid that some userland (X or some bootsplash stuff) was mapping the MMIO region that the driver thought it had exclusively via /dev/mem or via various sysfs resource mappings. This patch adds the option for device drivers to cause their reserved regions to the "banned from /dev/mem use" list, so now both kernel memory and device-exclusive MMIO regions are banned. NOTE: This is only active when CONFIG_STRICT_DEVMEM is set. In addition to the config option, a kernel parameter iomem=relaxed is provided for the cases where developers want to diagnose, in the field, drivers issues from userspace. Reviewed-by: Matthew Wilcox Signed-off-by: Arjan van de Ven Signed-off-by: Jesse Barnes --- Documentation/kernel-parameters.txt | 4 ++ arch/x86/mm/init_32.c | 2 + arch/x86/mm/init_64.c | 2 + drivers/net/e1000e/netdev.c | 2 +- drivers/pci/pci-sysfs.c | 3 + drivers/pci/pci.c | 107 ++++++++++++++++++++++++---- include/linux/ioport.h | 11 ++- include/linux/pci.h | 3 + kernel/resource.c | 61 +++++++++++++++- 9 files changed, 176 insertions(+), 19 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index 0b3f6711d2f1..0072fabb1dd1 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -918,6 +918,10 @@ and is between 256 and 4096 characters. It is defined in the file inttest= [IA64] + iomem= Disable strict checking of access to MMIO memory + strict regions from userspace. + relaxed + iommu= [x86] off force diff --git a/arch/x86/mm/init_32.c b/arch/x86/mm/init_32.c index 544d724caeee..88f1b10de3be 100644 --- a/arch/x86/mm/init_32.c +++ b/arch/x86/mm/init_32.c @@ -328,6 +328,8 @@ int devmem_is_allowed(unsigned long pagenr) { if (pagenr <= 256) return 1; + if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) + return 0; if (!page_is_ram(pagenr)) return 1; return 0; diff --git a/arch/x86/mm/init_64.c b/arch/x86/mm/init_64.c index 54c437e96541..23f68e77ad1f 100644 --- a/arch/x86/mm/init_64.c +++ b/arch/x86/mm/init_64.c @@ -888,6 +888,8 @@ int devmem_is_allowed(unsigned long pagenr) { if (pagenr <= 256) return 1; + if (iomem_is_exclusive(pagenr << PAGE_SHIFT)) + return 0; if (!page_is_ram(pagenr)) return 1; return 0; diff --git a/drivers/net/e1000e/netdev.c b/drivers/net/e1000e/netdev.c index d4639facd1bd..91817d0afcaf 100644 --- a/drivers/net/e1000e/netdev.c +++ b/drivers/net/e1000e/netdev.c @@ -4807,7 +4807,7 @@ static int __devinit e1000_probe(struct pci_dev *pdev, } } - err = pci_request_selected_regions(pdev, + err = pci_request_selected_regions_exclusive(pdev, pci_select_bars(pdev, IORESOURCE_MEM), e1000e_driver_name); if (err) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 388440e0d222..d5cdccf27a69 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -620,6 +620,9 @@ pci_mmap_resource(struct kobject *kobj, struct bin_attribute *attr, vma->vm_pgoff += start >> PAGE_SHIFT; mmap_type = res->flags & IORESOURCE_MEM ? pci_mmap_mem : pci_mmap_io; + if (res->flags & IORESOURCE_MEM && iomem_is_exclusive(start)) + return -EINVAL; + return pci_mmap_page_range(pdev, vma, mmap_type, write_combine); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 2cfa41e367a7..47663dc0daf7 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1395,7 +1395,8 @@ void pci_release_region(struct pci_dev *pdev, int bar) * Returns 0 on success, or %EBUSY on error. A warning * message is also printed on failure. */ -int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) +static int __pci_request_region(struct pci_dev *pdev, int bar, const char *res_name, + int exclusive) { struct pci_devres *dr; @@ -1408,8 +1409,9 @@ int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) goto err_out; } else if (pci_resource_flags(pdev, bar) & IORESOURCE_MEM) { - if (!request_mem_region(pci_resource_start(pdev, bar), - pci_resource_len(pdev, bar), res_name)) + if (!__request_mem_region(pci_resource_start(pdev, bar), + pci_resource_len(pdev, bar), res_name, + exclusive)) goto err_out; } @@ -1427,6 +1429,47 @@ err_out: return -EBUSY; } +/** + * pci_request_region - Reserved PCI I/O and memory resource + * @pdev: PCI device whose resources are to be reserved + * @bar: BAR to be reserved + * @res_name: Name to be associated with resource. + * + * Mark the PCI region associated with PCI device @pdev BR @bar as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int pci_request_region(struct pci_dev *pdev, int bar, const char *res_name) +{ + return __pci_request_region(pdev, bar, res_name, 0); +} + +/** + * pci_request_region_exclusive - Reserved PCI I/O and memory resource + * @pdev: PCI device whose resources are to be reserved + * @bar: BAR to be reserved + * @res_name: Name to be associated with resource. + * + * Mark the PCI region associated with PCI device @pdev BR @bar as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + * + * The key difference that _exclusive makes it that userspace is + * explicitly not allowed to map the resource via /dev/mem or + * sysfs. + */ +int pci_request_region_exclusive(struct pci_dev *pdev, int bar, const char *res_name) +{ + return __pci_request_region(pdev, bar, res_name, IORESOURCE_EXCLUSIVE); +} /** * pci_release_selected_regions - Release selected PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved @@ -1444,20 +1487,14 @@ void pci_release_selected_regions(struct pci_dev *pdev, int bars) pci_release_region(pdev, i); } -/** - * pci_request_selected_regions - Reserve selected PCI I/O and memory resources - * @pdev: PCI device whose resources are to be reserved - * @bars: Bitmask of BARs to be requested - * @res_name: Name to be associated with resource - */ -int pci_request_selected_regions(struct pci_dev *pdev, int bars, - const char *res_name) +int __pci_request_selected_regions(struct pci_dev *pdev, int bars, + const char *res_name, int excl) { int i; for (i = 0; i < 6; i++) if (bars & (1 << i)) - if(pci_request_region(pdev, i, res_name)) + if (__pci_request_region(pdev, i, res_name, excl)) goto err_out; return 0; @@ -1469,6 +1506,26 @@ err_out: return -EBUSY; } + +/** + * pci_request_selected_regions - Reserve selected PCI I/O and memory resources + * @pdev: PCI device whose resources are to be reserved + * @bars: Bitmask of BARs to be requested + * @res_name: Name to be associated with resource + */ +int pci_request_selected_regions(struct pci_dev *pdev, int bars, + const char *res_name) +{ + return __pci_request_selected_regions(pdev, bars, res_name, 0); +} + +int pci_request_selected_regions_exclusive(struct pci_dev *pdev, + int bars, const char *res_name) +{ + return __pci_request_selected_regions(pdev, bars, res_name, + IORESOURCE_EXCLUSIVE); +} + /** * pci_release_regions - Release reserved PCI I/O and memory resources * @pdev: PCI device whose resources were previously reserved by pci_request_regions @@ -1501,6 +1558,29 @@ int pci_request_regions(struct pci_dev *pdev, const char *res_name) return pci_request_selected_regions(pdev, ((1 << 6) - 1), res_name); } +/** + * pci_request_regions_exclusive - Reserved PCI I/O and memory resources + * @pdev: PCI device whose resources are to be reserved + * @res_name: Name to be associated with resource. + * + * Mark all PCI regions associated with PCI device @pdev as + * being reserved by owner @res_name. Do not access any + * address inside the PCI regions unless this call returns + * successfully. + * + * pci_request_regions_exclusive() will mark the region so that + * /dev/mem and the sysfs MMIO access will not be allowed. + * + * Returns 0 on success, or %EBUSY on error. A warning + * message is also printed on failure. + */ +int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) +{ + return pci_request_selected_regions_exclusive(pdev, + ((1 << 6) - 1), res_name); +} + + /** * pci_set_master - enables bus-mastering for device dev * @dev: the PCI device to enable @@ -2149,10 +2229,13 @@ EXPORT_SYMBOL(pci_find_capability); EXPORT_SYMBOL(pci_bus_find_capability); EXPORT_SYMBOL(pci_release_regions); EXPORT_SYMBOL(pci_request_regions); +EXPORT_SYMBOL(pci_request_regions_exclusive); EXPORT_SYMBOL(pci_release_region); EXPORT_SYMBOL(pci_request_region); +EXPORT_SYMBOL(pci_request_region_exclusive); EXPORT_SYMBOL(pci_release_selected_regions); EXPORT_SYMBOL(pci_request_selected_regions); +EXPORT_SYMBOL(pci_request_selected_regions_exclusive); EXPORT_SYMBOL(pci_set_master); EXPORT_SYMBOL(pci_set_mwi); EXPORT_SYMBOL(pci_try_set_mwi); diff --git a/include/linux/ioport.h b/include/linux/ioport.h index 041e95aac2bf..f6bb2ca8e3ba 100644 --- a/include/linux/ioport.h +++ b/include/linux/ioport.h @@ -49,6 +49,7 @@ struct resource_list { #define IORESOURCE_SIZEALIGN 0x00020000 /* size indicates alignment */ #define IORESOURCE_STARTALIGN 0x00040000 /* start field is alignment */ +#define IORESOURCE_EXCLUSIVE 0x08000000 /* Userland may not map this resource */ #define IORESOURCE_DISABLED 0x10000000 #define IORESOURCE_UNSET 0x20000000 #define IORESOURCE_AUTO 0x40000000 @@ -133,13 +134,16 @@ static inline unsigned long resource_type(struct resource *res) } /* Convenience shorthand with allocation */ -#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name)) -#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name)) +#define request_region(start,n,name) __request_region(&ioport_resource, (start), (n), (name), 0) +#define __request_mem_region(start,n,name, excl) __request_region(&iomem_resource, (start), (n), (name), excl) +#define request_mem_region(start,n,name) __request_region(&iomem_resource, (start), (n), (name), 0) +#define request_mem_region_exclusive(start,n,name) \ + __request_region(&iomem_resource, (start), (n), (name), IORESOURCE_EXCLUSIVE) #define rename_region(region, newname) do { (region)->name = (newname); } while (0) extern struct resource * __request_region(struct resource *, resource_size_t start, - resource_size_t n, const char *name); + resource_size_t n, const char *name, int relaxed); /* Compatibility cruft */ #define release_region(start,n) __release_region(&ioport_resource, (start), (n)) @@ -175,6 +179,7 @@ extern struct resource * __devm_request_region(struct device *dev, extern void __devm_release_region(struct device *dev, struct resource *parent, resource_size_t start, resource_size_t n); extern int iomem_map_sanity_check(resource_size_t addr, unsigned long size); +extern int iomem_is_exclusive(u64 addr); #endif /* __ASSEMBLY__ */ #endif /* _LINUX_IOPORT_H */ diff --git a/include/linux/pci.h b/include/linux/pci.h index 59a3dc2059d3..bfcb39ca8879 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -686,10 +686,13 @@ void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *), int (*)(struct pci_dev *, u8, u8)); #define HAVE_PCI_REQ_REGIONS 2 int __must_check pci_request_regions(struct pci_dev *, const char *); +int __must_check pci_request_regions_exclusive(struct pci_dev *, const char *); void pci_release_regions(struct pci_dev *); int __must_check pci_request_region(struct pci_dev *, int, const char *); +int __must_check pci_request_region_exclusive(struct pci_dev *, int, const char *); void pci_release_region(struct pci_dev *, int); int pci_request_selected_regions(struct pci_dev *, int, const char *); +int pci_request_selected_regions_exclusive(struct pci_dev *, int, const char *); void pci_release_selected_regions(struct pci_dev *, int); /* drivers/pci/bus.c */ diff --git a/kernel/resource.c b/kernel/resource.c index e633106b12f6..ca6a1536b205 100644 --- a/kernel/resource.c +++ b/kernel/resource.c @@ -623,7 +623,7 @@ resource_size_t resource_alignment(struct resource *res) */ struct resource * __request_region(struct resource *parent, resource_size_t start, resource_size_t n, - const char *name) + const char *name, int flags) { struct resource *res = kzalloc(sizeof(*res), GFP_KERNEL); @@ -634,6 +634,7 @@ struct resource * __request_region(struct resource *parent, res->start = start; res->end = start + n - 1; res->flags = IORESOURCE_BUSY; + res->flags |= flags; write_lock(&resource_lock); @@ -679,7 +680,7 @@ int __check_region(struct resource *parent, resource_size_t start, { struct resource * res; - res = __request_region(parent, start, n, "check-region"); + res = __request_region(parent, start, n, "check-region", 0); if (!res) return -EBUSY; @@ -776,7 +777,7 @@ struct resource * __devm_request_region(struct device *dev, dr->start = start; dr->n = n; - res = __request_region(parent, start, n, name); + res = __request_region(parent, start, n, name, 0); if (res) devres_add(dev, dr); else @@ -876,3 +877,57 @@ int iomem_map_sanity_check(resource_size_t addr, unsigned long size) return err; } + +#ifdef CONFIG_STRICT_DEVMEM +static int strict_iomem_checks = 1; +#else +static int strict_iomem_checks; +#endif + +/* + * check if an address is reserved in the iomem resource tree + * returns 1 if reserved, 0 if not reserved. + */ +int iomem_is_exclusive(u64 addr) +{ + struct resource *p = &iomem_resource; + int err = 0; + loff_t l; + int size = PAGE_SIZE; + + if (!strict_iomem_checks) + return 0; + + addr = addr & PAGE_MASK; + + read_lock(&resource_lock); + for (p = p->child; p ; p = r_next(NULL, p, &l)) { + /* + * We can probably skip the resources without + * IORESOURCE_IO attribute? + */ + if (p->start >= addr + size) + break; + if (p->end < addr) + continue; + if (p->flags & IORESOURCE_BUSY && + p->flags & IORESOURCE_EXCLUSIVE) { + err = 1; + break; + } + } + read_unlock(&resource_lock); + + return err; +} + +static int __init strict_iomem(char *str) +{ + if (strstr(str, "relaxed")) + strict_iomem_checks = 0; + if (strstr(str, "strict")) + strict_iomem_checks = 1; + return 1; +} + +__setup("iomem=", strict_iomem); From 4ba7d0f0eb68cf7731ead4ca20e540d0266cfa8e Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 23 Nov 2008 09:08:28 +0100 Subject: [PATCH 17/98] drivers/pci/hotplug: Add missing pci_dev_get pci_get_slot does a pci_dev_get, so pci_dev_put needs to be called in an error case. An alterative would be to move the test_and_set_bit before the call to pci_get_slot. The problem was fixed using the following semantic patch. (http://www.emn.fr/x-info/coccinelle/) // @@ local idexpression *n; statement S1,S2; expression E,E1; expression *ptr != NULL; type T,T1; @@ ( if (!(n = pci_get_slot(...))) S1 | n = pci_get_slot(...) ) <... when != pci_dev_put(n) when != if (...) { <+... pci_dev_put(n) ...+> } when != true !n || ... when != n = (T)E when != E = n if (!n || ...) S2 ...> ( return \(0\|<+...n...+>\|ptr\); | + pci_dev_put(n); return ...; | pci_dev_put(n); | n = (T1)E1 | E1 = n ) // Signed-off-by: Julia Lawall Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/fakephp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pci/hotplug/fakephp.c b/drivers/pci/hotplug/fakephp.c index 3a2637a00934..b0e7de9e536d 100644 --- a/drivers/pci/hotplug/fakephp.c +++ b/drivers/pci/hotplug/fakephp.c @@ -324,6 +324,7 @@ static int disable_slot(struct hotplug_slot *slot) if (test_and_set_bit(0, &dslot->removed)) { dbg("Slot already scheduled for removal\n"); + pci_dev_put(dev); return -ENODEV; } From 753e3aca735dc446f66d501b625122661738b57d Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Thu, 20 Nov 2008 15:22:32 +0900 Subject: [PATCH 18/98] PCI: revert additional _OSC evaluation Reverts adf411b819adc9fa96e9b3e638c7480d5e71d270. The commit adf411b819adc9fa96e9b3e638c7480d5e71d270 was based on the improper assumption that queried result was not updated when _OSC support field was changed. But, in fact, queried result is updated whenever _OSC support field was changed through __acpi_query_osc(). As a result, the commit adf411b819adc9fa96e9b3e638c7480d5e71d270 only introduced unnecessary additional _OSC evaluation... Tested-by: Andrew Patterson Reviewed-by: Andrew Patterson Signed-off-by: Kenji Kaneshige Signed-off-by: Taku Izumi Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 8f923ee5177f..c580a525d9ce 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -24,13 +24,15 @@ struct acpi_osc_data { acpi_handle handle; u32 support_set; u32 control_set; + int is_queried; + u32 query_result; struct list_head sibiling; }; static LIST_HEAD(acpi_osc_data_list); struct acpi_osc_args { u32 capbuf[3]; - u32 ctrl_result; + u32 query_result; }; static DEFINE_MUTEX(pci_acpi_lock); @@ -112,8 +114,9 @@ static acpi_status acpi_run_osc(acpi_handle handle, goto out_kfree; } out_success: - osc_args->ctrl_result = - *((u32 *)(out_obj->buffer.pointer + 8)); + if (flags & OSC_QUERY_ENABLE) + osc_args->query_result = + *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; out_kfree: @@ -121,8 +124,7 @@ out_kfree: return status; } -static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, - u32 *result) +static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) { acpi_status status; u32 support_set; @@ -137,7 +139,8 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, status = acpi_run_osc(osc_data->handle, &osc_args); if (ACPI_SUCCESS(status)) { osc_data->support_set = support_set; - *result = osc_args.ctrl_result; + osc_data->query_result = osc_args.query_result; + osc_data->is_queried = 1; } return status; @@ -151,7 +154,6 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data, */ int pci_acpi_osc_support(acpi_handle handle, u32 flags) { - u32 dummy; acpi_status status; acpi_handle tmp; struct acpi_osc_data *osc_data; @@ -169,7 +171,7 @@ int pci_acpi_osc_support(acpi_handle handle, u32 flags) goto out; } - __acpi_query_osc(flags, osc_data, &dummy); + __acpi_query_osc(flags, osc_data); out: mutex_unlock(&pci_acpi_lock); return rc; @@ -185,7 +187,7 @@ out: acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) { acpi_status status; - u32 ctrlset, control_set, result; + u32 ctrlset, control_set; acpi_handle tmp; struct acpi_osc_data *osc_data; struct acpi_osc_args osc_args; @@ -208,11 +210,13 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) goto out; } - status = __acpi_query_osc(osc_data->support_set, osc_data, &result); - if (ACPI_FAILURE(status)) - goto out; + if (!osc_data->is_queried) { + status = __acpi_query_osc(osc_data->support_set, osc_data); + if (ACPI_FAILURE(status)) + goto out; + } - if ((result & ctrlset) != ctrlset) { + if ((osc_data->query_result & ctrlset) != ctrlset) { status = AE_SUPPORT; goto out; } From e0fa3b43df0b57967857b2c83bad9a5193fbddf5 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Thu, 20 Nov 2008 15:22:37 +0900 Subject: [PATCH 19/98] PCI/ACPI: Remove unnecessary _OSC evaluation for control request If a control had already been granted, we don't need to re-evaluate _OSC for it because firmware may not reject control of any feature it has previously granted control to. Reviewed-by: Andrew Patterson Tested-by: Andrew Patterson Signed-off-by: Kenji Kaneshige Signed-off-by: Taku Izumi Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index c580a525d9ce..736bb248008e 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -210,6 +210,10 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) goto out; } + /* No need to evaluate _OSC if the control was already granted. */ + if ((osc_data->control_set & ctrlset) == ctrlset) + goto out; + if (!osc_data->is_queried) { status = __acpi_query_osc(osc_data->support_set, osc_data); if (ACPI_FAILURE(status)) From 86d8698027fd30cc067d2aeeb1e3603d43c83df0 Mon Sep 17 00:00:00 2001 From: Taku Izumi Date: Thu, 20 Nov 2008 15:22:39 +0900 Subject: [PATCH 20/98] pci-acpi: Cleanup _OSC evaluation code Cleanup _OSC evaluation code. Some whitespace changes and a few other minor cleanups. Reviewed-by: Andrew Patterson Tested-by: Andrew Patterson Signed-off-by: Kenji Kaneshige Signed-off-by: Taku Izumi Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 736bb248008e..3582512e7226 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -24,15 +24,14 @@ struct acpi_osc_data { acpi_handle handle; u32 support_set; u32 control_set; + u32 control_query; int is_queried; - u32 query_result; struct list_head sibiling; }; static LIST_HEAD(acpi_osc_data_list); struct acpi_osc_args { u32 capbuf[3]; - u32 query_result; }; static DEFINE_MUTEX(pci_acpi_lock); @@ -58,7 +57,7 @@ static u8 OSC_UUID[16] = {0x5B, 0x4D, 0xDB, 0x33, 0xF7, 0x1F, 0x1C, 0x40, 0x96, 0x57, 0x74, 0x41, 0xC0, 0x3D, 0xD7, 0x66}; static acpi_status acpi_run_osc(acpi_handle handle, - struct acpi_osc_args *osc_args) + struct acpi_osc_args *osc_args, u32 *retval) { acpi_status status; struct acpi_object_list input; @@ -114,9 +113,7 @@ static acpi_status acpi_run_osc(acpi_handle handle, goto out_kfree; } out_success: - if (flags & OSC_QUERY_ENABLE) - osc_args->query_result = - *((u32 *)(out_obj->buffer.pointer + 8)); + *retval = *((u32 *)(out_obj->buffer.pointer + 8)); status = AE_OK; out_kfree: @@ -127,7 +124,7 @@ out_kfree: static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) { acpi_status status; - u32 support_set; + u32 support_set, result; struct acpi_osc_args osc_args; /* do _OSC query for all possible controls */ @@ -136,10 +133,10 @@ static acpi_status __acpi_query_osc(u32 flags, struct acpi_osc_data *osc_data) osc_args.capbuf[OSC_SUPPORT_TYPE] = support_set; osc_args.capbuf[OSC_CONTROL_TYPE] = OSC_CONTROL_MASKS; - status = acpi_run_osc(osc_data->handle, &osc_args); + status = acpi_run_osc(osc_data->handle, &osc_args, &result); if (ACPI_SUCCESS(status)) { osc_data->support_set = support_set; - osc_data->query_result = osc_args.query_result; + osc_data->control_query = result; osc_data->is_queried = 1; } @@ -187,7 +184,7 @@ out: acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) { acpi_status status; - u32 ctrlset, control_set; + u32 control_req, control_set, result; acpi_handle tmp; struct acpi_osc_data *osc_data; struct acpi_osc_args osc_args; @@ -204,14 +201,14 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) goto out; } - ctrlset = (flags & OSC_CONTROL_MASKS); - if (!ctrlset) { + control_req = (flags & OSC_CONTROL_MASKS); + if (!control_req) { status = AE_TYPE; goto out; } /* No need to evaluate _OSC if the control was already granted. */ - if ((osc_data->control_set & ctrlset) == ctrlset) + if ((osc_data->control_set & control_req) == control_req) goto out; if (!osc_data->is_queried) { @@ -220,18 +217,18 @@ acpi_status pci_osc_control_set(acpi_handle handle, u32 flags) goto out; } - if ((osc_data->query_result & ctrlset) != ctrlset) { + if ((osc_data->control_query & control_req) != control_req) { status = AE_SUPPORT; goto out; } - control_set = osc_data->control_set | ctrlset; + control_set = osc_data->control_set | control_req; osc_args.capbuf[OSC_QUERY_TYPE] = 0; osc_args.capbuf[OSC_SUPPORT_TYPE] = osc_data->support_set; osc_args.capbuf[OSC_CONTROL_TYPE] = control_set; - status = acpi_run_osc(handle, &osc_args); + status = acpi_run_osc(handle, &osc_args, &result); if (ACPI_SUCCESS(status)) - osc_data->control_set = control_set; + osc_data->control_set = result; out: mutex_unlock(&pci_acpi_lock); return status; From 56ee325e25a0f76fc3267872867b3d70af179aad Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Tue, 25 Nov 2008 21:48:14 +0000 Subject: [PATCH 21/98] PCI/ACPI: acpiphp: Identify more removable slots According to section 6.3.6 of the ACPI spec, the presence of an _RMV method that evaluates to 1 is sufficient to indicate that a slot is removable without needing an eject method. This patch refactors the ejectable slot detection code a little in order to flag these slots as ejectable and register them. Acpihp then binds to the expresscard slot on my HP test machine. Acked-by: Kristen Carlson Accardi Signed-off-by: Matthew Garrett Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 32 ++++++++++++++++-------------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 3affc6472e65..1b19b7ec3e8c 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -74,7 +74,7 @@ static void handle_hotplug_event_func(acpi_handle handle, u32 type, void *contex * Ejectable slot should satisfy at least these conditions: * * 1. has _ADR method - * 2. has _EJ0 method + * 2. has _EJ0 method or _RMV method * * optionally * @@ -87,18 +87,25 @@ static int is_ejectable(acpi_handle handle) { acpi_status status; acpi_handle tmp; + unsigned long long removable; status = acpi_get_handle(handle, "_ADR", &tmp); - if (ACPI_FAILURE(status)) { + if (ACPI_FAILURE(status)) return 0; - } status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_FAILURE(status)) { - return 0; + if (ACPI_SUCCESS(status)) + return 1; + + status = acpi_get_handle(handle, "_RMV", &tmp); + if (ACPI_SUCCESS(status)) { + status = acpi_evaluate_integer(handle, "_RMV", NULL, + &removable); + if (ACPI_SUCCESS(status) && removable) + return 1; } - return 1; + return 0; } @@ -185,16 +192,10 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) unsigned long long adr, sun; int device, function, retval; - status = acpi_evaluate_integer(handle, "_ADR", NULL, &adr); - - if (ACPI_FAILURE(status)) - return AE_OK; - - status = acpi_get_handle(handle, "_EJ0", &tmp); - - if (ACPI_FAILURE(status) && !(is_dock_device(handle))) + if (!is_ejectable(handle) && !is_dock_device(handle)) return AE_OK; + acpi_evaluate_integer(handle, "_ADR", NULL, &adr); device = (adr >> 16) & 0xffff; function = adr & 0xffff; @@ -205,7 +206,8 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) INIT_LIST_HEAD(&newfunc->sibling); newfunc->handle = handle; newfunc->function = function; - if (ACPI_SUCCESS(status)) + + if (ACPI_SUCCESS(acpi_get_handle(handle, "_EJ0", &tmp))) newfunc->flags = FUNC_HAS_EJ0; if (ACPI_SUCCESS(acpi_get_handle(handle, "_STA", &tmp))) From 2debb4d2019fa05a0896f1591dea0e0dc21bc046 Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Tue, 25 Nov 2008 19:36:10 -0800 Subject: [PATCH 22/98] PCI: allow pci driver to support only dynids commit b41d6cf38e27 (PCI: Check dynids driver_data value for validity) requires all drivers to include an id table to try and match driver_data. Before validating driver_data check driver has an id table. Acked-by: Jean Delvare Cc: Milton Miller Signed-off-by: Chris Wright Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 99d867bcf22a..888191a3b0d1 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -48,7 +48,7 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) subdevice=PCI_ANY_ID, class=0, class_mask=0; unsigned long driver_data=0; int fields=0; - int retval; + int retval=0; fields = sscanf(buf, "%x %x %x %x %x %x %lx", &vendor, &device, &subvendor, &subdevice, @@ -58,16 +58,18 @@ store_new_id(struct device_driver *driver, const char *buf, size_t count) /* Only accept driver_data values that match an existing id_table entry */ - retval = -EINVAL; - while (ids->vendor || ids->subvendor || ids->class_mask) { - if (driver_data == ids->driver_data) { - retval = 0; - break; + if (ids) { + retval = -EINVAL; + while (ids->vendor || ids->subvendor || ids->class_mask) { + if (driver_data == ids->driver_data) { + retval = 0; + break; + } + ids++; } - ids++; + if (retval) /* No match */ + return retval; } - if (retval) /* No match */ - return retval; dynid = kzalloc(sizeof(*dynid), GFP_KERNEL); if (!dynid) From c70e0d9dfef3d826c8ae4f7544acc53887cb161d Mon Sep 17 00:00:00 2001 From: Chris Wright Date: Tue, 25 Nov 2008 21:17:13 -0800 Subject: [PATCH 23/98] PCI: pci-stub module to reserve pci device When doing device assignment with KVM there's currently nothing to protect the device from having a driver in the host as well as the guest. This trivial module just binds the pci device on the host to a stub driver so that a real host driver can't bind to the device. It has no pci id table, it supports only dynamic ids. # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind # ls -l /sys/bus/pci/devices/0000:00:19.0/driver lrwxrwxrwx 1 root root 0 2008-11-25 19:10 /sys/bus/pci/devices/0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub Cc: "Kay, Allen M" Cc: "Nakajima, Jun" Signed-off-by: Chris Wright Acked-by: Greg Kroah-Hartman Signed-off-by: Jesse Barnes --- drivers/pci/Kconfig | 9 ++++++++ drivers/pci/Makefile | 2 ++ drivers/pci/pci-stub.c | 47 ++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 drivers/pci/pci-stub.c diff --git a/drivers/pci/Kconfig b/drivers/pci/Kconfig index e1ca42591ac4..2a4501dd2515 100644 --- a/drivers/pci/Kconfig +++ b/drivers/pci/Kconfig @@ -42,6 +42,15 @@ config PCI_DEBUG When in doubt, say N. +config PCI_STUB + tristate "PCI Stub driver" + depends on PCI + help + Say Y or M here if you want be able to reserve a PCI device + when it is going to be assigned to a guest operating system. + + When in doubt, say N. + config HT_IRQ bool "Interrupts on hypertransport devices" default y diff --git a/drivers/pci/Makefile b/drivers/pci/Makefile index af3bfe22847b..3d07ce24f6a8 100644 --- a/drivers/pci/Makefile +++ b/drivers/pci/Makefile @@ -53,6 +53,8 @@ obj-$(CONFIG_HOTPLUG) += setup-bus.o obj-$(CONFIG_PCI_SYSCALL) += syscall.o +obj-$(CONFIG_PCI_STUB) += pci-stub.o + ifeq ($(CONFIG_PCI_DEBUG),y) EXTRA_CFLAGS += -DDEBUG endif diff --git a/drivers/pci/pci-stub.c b/drivers/pci/pci-stub.c new file mode 100644 index 000000000000..74fbec0bf6cb --- /dev/null +++ b/drivers/pci/pci-stub.c @@ -0,0 +1,47 @@ +/* pci-stub - simple stub driver to reserve a pci device + * + * Copyright (C) 2008 Red Hat, Inc. + * Author: + * Chris Wright + * + * This work is licensed under the terms of the GNU GPL, version 2. + * + * Usage is simple, allocate a new id to the stub driver and bind the + * device to it. For example: + * + * # echo "8086 10f5" > /sys/bus/pci/drivers/pci-stub/new_id + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/e1000e/unbind + * # echo -n 0000:00:19.0 > /sys/bus/pci/drivers/pci-stub/bind + * # ls -l /sys/bus/pci/devices/0000:00:19.0/driver + * .../0000:00:19.0/driver -> ../../../bus/pci/drivers/pci-stub + */ + +#include +#include + +static int pci_stub_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + return 0; +} + +static struct pci_driver stub_driver = { + .name = "pci-stub", + .id_table = NULL, /* only dynamic id's */ + .probe = pci_stub_probe, +}; + +static int __init pci_stub_init(void) +{ + return pci_register_driver(&stub_driver); +} + +static void __exit pci_stub_exit(void) +{ + pci_unregister_driver(&stub_driver); +} + +module_init(pci_stub_init); +module_exit(pci_stub_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Chris Wright "); From 90d25f246ddefbb743764f8d45ae97e545a6ee86 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 6 Dec 2008 15:07:59 +0100 Subject: [PATCH 24/98] PCI: Suspend and resume PCI Express ports with interrupts disabled I don't see why the suspend and resume of PCI Express ports should be handled with interrupts enabled and it may even lead to problems in some situations. For this reason, move the suspending and resuming of PCI Express ports into ->suspend_late() and ->resume_early() callbacks executed with interrupts disabled. This patch addresses the regression from 2.6.26 tracked as http://bugzilla.kernel.org/show_bug.cgi?id=12121 . Acked-by: Linus Torvalds Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 584422da8d8b..0549fe2bdac9 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -50,7 +50,7 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) } #ifdef CONFIG_PM -static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) +static int pcie_portdrv_suspend_late(struct pci_dev *dev, pm_message_t state) { int ret = pcie_port_device_suspend(dev, state); @@ -59,14 +59,14 @@ static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) return ret; } -static int pcie_portdrv_resume(struct pci_dev *dev) +static int pcie_portdrv_resume_early(struct pci_dev *dev) { pcie_portdrv_restore_config(dev); return pcie_port_device_resume(dev); } #else -#define pcie_portdrv_suspend NULL -#define pcie_portdrv_resume NULL +#define pcie_portdrv_suspend_late NULL +#define pcie_portdrv_resume_early NULL #endif /* @@ -282,8 +282,8 @@ static struct pci_driver pcie_portdriver = { .probe = pcie_portdrv_probe, .remove = pcie_portdrv_remove, - .suspend = pcie_portdrv_suspend, - .resume = pcie_portdrv_resume, + .suspend_late = pcie_portdrv_suspend_late, + .resume_early = pcie_portdrv_resume_early, .err_handler = &pcie_portdrv_err_handler, }; From 894886e5d3de0bde2eded8a39bf7e76023fbd791 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Sat, 6 Dec 2008 10:10:10 -0800 Subject: [PATCH 25/98] PCI: extend on the ICH motherboard IO decode quirk list This adds more LPC controller IO range decode quirks for the Intel ICH family of chipsets. They differ a bit between the older ICH6 chipset and the more modern layout of the ICH7-ICH10 chipsets. This patch just prints out the IO decode information found by the quirks, but eventually we may want to add them to the resource tree, in order to know to avoid allocating things over them. That's especially true if it turns out that any firmware ends up putting the magic motherboard resources in an address range that we use for dynamic allocations (ie above PCIBIOS_MIN_IO, which is 0x1000 on x86). Signed-off-by: Linus Torvalds Cc: Rafael J. Wysocki Cc: Ivan Kokshaysky Cc: Greg KH Cc: Frans Pop Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 105 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 90 insertions(+), 15 deletions(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index e915a17b36c3..d3a9e0f38682 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -449,7 +449,7 @@ DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_12, DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, quirk_ich4_lpc_acpi); DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1, quirk_ich4_lpc_acpi); -static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev) +static void __devinit ich6_lpc_acpi_gpio(struct pci_dev *dev) { u32 region; @@ -459,20 +459,95 @@ static void __devinit quirk_ich6_lpc_acpi(struct pci_dev *dev) pci_read_config_dword(dev, 0x48, ®ion); quirk_io_region(dev, region, 64, PCI_BRIDGE_RESOURCES+1, "ICH6 GPIO"); } -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich6_lpc_acpi); -DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich6_lpc_acpi); + +static void __devinit ich6_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name, int dynsize) +{ + u32 val; + u32 size, base; + + pci_read_config_dword(dev, reg, &val); + + /* Enabled? */ + if (!(val & 1)) + return; + base = val & 0xfffc; + if (dynsize) { + /* + * This is not correct. It is 16, 32 or 64 bytes depending on + * register D31:F0:ADh bits 5:4. + * + * But this gets us at least _part_ of it. + */ + size = 16; + } else { + size = 128; + } + base &= ~(size-1); + + /* Just print it out for now. We should reserve it after more debugging */ + dev_info(&dev->dev, "%s PIO at %04x-%04x\n", name, base, base+size-1); +} + +static void __devinit quirk_ich6_lpc(struct pci_dev *dev) +{ + /* Shared ACPI/GPIO decode with all ICH6+ */ + ich6_lpc_acpi_gpio(dev); + + /* ICH6-specific generic IO decode */ + ich6_lpc_generic_decode(dev, 0x84, "LPC Generic IO decode 1", 0); + ich6_lpc_generic_decode(dev, 0x88, "LPC Generic IO decode 2", 1); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_0, quirk_ich6_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH6_1, quirk_ich6_lpc); + +static void __devinit ich7_lpc_generic_decode(struct pci_dev *dev, unsigned reg, const char *name) +{ + u32 val; + u32 mask, base; + + pci_read_config_dword(dev, reg, &val); + + /* Enabled? */ + if (!(val & 1)) + return; + + /* + * IO base in bits 15:2, mask in bits 23:18, both + * are dword-based + */ + base = val & 0xfffc; + mask = (val >> 16) & 0xfc; + mask |= 3; + + /* Just print it out for now. We should reserve it after more debugging */ + dev_info(&dev->dev, "%s PIO at %04x (mask %04x)\n", name, base, mask); +} + +/* ICH7-10 has the same common LPC generic IO decode registers */ +static void __devinit quirk_ich7_lpc(struct pci_dev *dev) +{ + /* We share the common ACPI/DPIO decode with ICH6 */ + ich6_lpc_acpi_gpio(dev); + + /* And have 4 ICH7+ generic decodes */ + ich7_lpc_generic_decode(dev, 0x84, "ICH7 LPC Generic IO decode 1"); + ich7_lpc_generic_decode(dev, 0x88, "ICH7 LPC Generic IO decode 2"); + ich7_lpc_generic_decode(dev, 0x8c, "ICH7 LPC Generic IO decode 3"); + ich7_lpc_generic_decode(dev, 0x90, "ICH7 LPC Generic IO decode 4"); +} +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_0, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_1, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH7_31, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_0, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_2, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_3, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_1, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH8_4, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_2, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_4, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_7, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH9_8, quirk_ich7_lpc); +DECLARE_PCI_FIXUP_HEADER(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ICH10_1, quirk_ich7_lpc); /* * VIA ACPI: One IO region pointed to by longword at From 63f4898ace2788a89ed685672aab092e1c3e50e6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 7 Dec 2008 22:02:58 +0100 Subject: [PATCH 26/98] PCI: handle PCI state saving with interrupts disabled Since interrupts will soon be disabled at PCI resume time, we need to pre-allocate memory to save/restore PCI config space (or use GFP_ATOMIC, but this is safer). Reported-by: Linus Torvalds Signed-off-by: "Rafael J. Wysocki" Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 73 +++++++++++++++++++++++++++++++-------------- drivers/pci/pci.h | 1 + drivers/pci/probe.c | 3 ++ 3 files changed, 55 insertions(+), 22 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 47663dc0daf7..3222f9022707 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -640,19 +640,14 @@ static int pci_save_pcie_state(struct pci_dev *dev) int pos, i = 0; struct pci_cap_saved_state *save_state; u16 *cap; - int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_EXP); if (pos <= 0) return 0; save_state = pci_find_saved_cap(dev, PCI_CAP_ID_EXP); - if (!save_state) - save_state = kzalloc(sizeof(*save_state) + sizeof(u16) * 4, GFP_KERNEL); - else - found = 1; if (!save_state) { - dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); + dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); return -ENOMEM; } cap = (u16 *)&save_state->data[0]; @@ -661,9 +656,7 @@ static int pci_save_pcie_state(struct pci_dev *dev) pci_read_config_word(dev, pos + PCI_EXP_LNKCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_SLTCTL, &cap[i++]); pci_read_config_word(dev, pos + PCI_EXP_RTCTL, &cap[i++]); - save_state->cap_nr = PCI_CAP_ID_EXP; - if (!found) - pci_add_saved_cap(dev, save_state); + return 0; } @@ -688,30 +681,21 @@ static void pci_restore_pcie_state(struct pci_dev *dev) static int pci_save_pcix_state(struct pci_dev *dev) { - int pos, i = 0; + int pos; struct pci_cap_saved_state *save_state; - u16 *cap; - int found = 0; pos = pci_find_capability(dev, PCI_CAP_ID_PCIX); if (pos <= 0) return 0; save_state = pci_find_saved_cap(dev, PCI_CAP_ID_PCIX); - if (!save_state) - save_state = kzalloc(sizeof(*save_state) + sizeof(u16), GFP_KERNEL); - else - found = 1; if (!save_state) { - dev_err(&dev->dev, "out of memory in pci_save_pcie_state\n"); + dev_err(&dev->dev, "buffer not found in %s\n", __FUNCTION__); return -ENOMEM; } - cap = (u16 *)&save_state->data[0]; - pci_read_config_word(dev, pos + PCI_X_CMD, &cap[i++]); - save_state->cap_nr = PCI_CAP_ID_PCIX; - if (!found) - pci_add_saved_cap(dev, save_state); + pci_read_config_word(dev, pos + PCI_X_CMD, (u16 *)save_state->data); + return 0; } @@ -1300,6 +1284,51 @@ void pci_pm_init(struct pci_dev *dev) } } +/** + * pci_add_save_buffer - allocate buffer for saving given capability registers + * @dev: the PCI device + * @cap: the capability to allocate the buffer for + * @size: requested size of the buffer + */ +static int pci_add_cap_save_buffer( + struct pci_dev *dev, char cap, unsigned int size) +{ + int pos; + struct pci_cap_saved_state *save_state; + + pos = pci_find_capability(dev, cap); + if (pos <= 0) + return 0; + + save_state = kzalloc(sizeof(*save_state) + size, GFP_KERNEL); + if (!save_state) + return -ENOMEM; + + save_state->cap_nr = cap; + pci_add_saved_cap(dev, save_state); + + return 0; +} + +/** + * pci_allocate_cap_save_buffers - allocate buffers for saving capabilities + * @dev: the PCI device + */ +void pci_allocate_cap_save_buffers(struct pci_dev *dev) +{ + int error; + + error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_EXP, 4 * sizeof(u16)); + if (error) + dev_err(&dev->dev, + "unable to preallocate PCI Express save buffer\n"); + + error = pci_add_cap_save_buffer(dev, PCI_CAP_ID_PCIX, sizeof(u16)); + if (error) + dev_err(&dev->dev, + "unable to preallocate PCI-X save buffer\n"); +} + /** * pci_enable_ari - enable ARI forwarding if hardware support it * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 9162e242b99e..7242b511a93f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -45,6 +45,7 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); extern void pci_pm_init(struct pci_dev *dev); +extern void pci_allocate_cap_save_buffers(struct pci_dev *dev); extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); extern int pci_user_read_config_word(struct pci_dev *dev, int where, u16 *val); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index eb2b985beb48..5dcf2b65e3f9 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -958,6 +958,9 @@ static void pci_init_capabilities(struct pci_dev *dev) /* MSI/MSI-X list */ pci_msi_init_pci_dev(dev); + /* Buffers for saving PCIe and PCI-X capabilities */ + pci_allocate_cap_save_buffers(dev); + /* Power Management */ pci_pm_init(dev); From a367f74cb6f9c49850a4ee86e45fd3a8e83065e4 Mon Sep 17 00:00:00 2001 From: Hidetoshi Seto Date: Fri, 5 Dec 2008 14:05:23 +0900 Subject: [PATCH 27/98] PCI hotplug: aerdrv: fix a typo in error message "TLP" is an acronym for "Transaction Layer Packet." Signed-off-by: Hidetoshi Seto Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aer/aerdrv_errprint.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pcie/aer/aerdrv_errprint.c b/drivers/pci/pcie/aer/aerdrv_errprint.c index 3933d4f30e8c..0fc29ae80df8 100644 --- a/drivers/pci/pcie/aer/aerdrv_errprint.c +++ b/drivers/pci/pcie/aer/aerdrv_errprint.c @@ -233,7 +233,7 @@ 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("%sTLB Header:\n", loglevel); + printk("%sTLP Header:\n", loglevel); printk("%s%02x%02x%02x%02x %02x%02x%02x%02x" " %02x%02x%02x%02x %02x%02x%02x%02x\n", loglevel, From 104bafcfab7ce3031399e60069949f10acecc022 Mon Sep 17 00:00:00 2001 From: Ingo Molnar Date: Fri, 12 Dec 2008 06:49:40 +0100 Subject: [PATCH 28/98] PCI: Don't carp about BAR allocation failures in quiet boot These are easy to trigger (more or less harmlessly) with multiple video cards, since the ROM BAR will typically not be given any space by the BIOS bridge setup. No reason to punish quiet boot for this. Signed-off-by: Adam Jackson Signed-off-by: Jesse Barnes --- arch/x86/pci/i386.c | 4 ++-- drivers/pci/setup-res.c | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/pci/i386.c b/arch/x86/pci/i386.c index e51bf2cda4b0..f884740da318 100644 --- a/arch/x86/pci/i386.c +++ b/arch/x86/pci/i386.c @@ -129,7 +129,7 @@ static void __init pcibios_allocate_bus_resources(struct list_head *bus_list) pr = pci_find_parent_resource(dev, r); if (!r->start || !pr || request_resource(pr, r) < 0) { - dev_err(&dev->dev, "BAR %d: can't allocate resource\n", idx); + dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx); /* * Something is wrong with the region. * Invalidate the resource to prevent @@ -170,7 +170,7 @@ static void __init pcibios_allocate_resources(int pass) r->flags, disabled, pass); pr = pci_find_parent_resource(dev, r); if (!pr || request_resource(pr, r) < 0) { - dev_err(&dev->dev, "BAR %d: can't allocate resource\n", idx); + dev_info(&dev->dev, "BAR %d: can't allocate resource\n", idx); /* We'll assign a new address later */ r->end -= r->start; r->start = 0; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 2dbd96cce2d8..4e375632499a 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -134,7 +134,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) align = resource_alignment(res); if (!align) { - dev_err(&dev->dev, "BAR %d: can't allocate resource (bogus " + dev_info(&dev->dev, "BAR %d: can't allocate resource (bogus " "alignment) %pR flags %#lx\n", resno, res, res->flags); return -EINVAL; @@ -157,7 +157,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) } if (ret) { - dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", + dev_info(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); } else { res->flags &= ~IORESOURCE_STARTALIGN; From 2a9d35219c593bdf46ec21f2b75a6370af7af1b0 Mon Sep 17 00:00:00 2001 From: Alex Chiang Date: Thu, 11 Dec 2008 11:17:55 -0700 Subject: [PATCH 29/98] PCI hotplug: acpiphp whitespace cleanup Clean up whitespace. Setting 'let c_space_errors=1' in .vimrc shows all sorts of ugliness. ;) Signed-off-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp.h | 2 +- drivers/pci/hotplug/acpiphp_glue.c | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/pci/hotplug/acpiphp.h b/drivers/pci/hotplug/acpiphp.h index 9bcb6cbd5aa9..4fc168b70095 100644 --- a/drivers/pci/hotplug/acpiphp.h +++ b/drivers/pci/hotplug/acpiphp.h @@ -44,7 +44,7 @@ do { \ if (acpiphp_debug) \ printk(KERN_DEBUG "%s: " format, \ - MY_NAME , ## arg); \ + MY_NAME , ## arg); \ } while (0) #define err(format, arg...) printk(KERN_ERR "%s: " format, MY_NAME , ## arg) #define info(format, arg...) printk(KERN_INFO "%s: " format, MY_NAME , ## arg) diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 1b19b7ec3e8c..7a5760426897 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -167,9 +167,9 @@ static int post_dock_fixups(struct notifier_block *nb, unsigned long val, if (((buses >> 8) & 0xff) != bus->secondary) { buses = (buses & 0xff000000) - | ((unsigned int)(bus->primary) << 0) - | ((unsigned int)(bus->secondary) << 8) - | ((unsigned int)(bus->subordinate) << 16); + | ((unsigned int)(bus->primary) << 0) + | ((unsigned int)(bus->secondary) << 8) + | ((unsigned int)(bus->subordinate) << 16); pci_write_config_dword(bus->self, PCI_PRIMARY_BUS, buses); } return NOTIFY_OK; From 29935282f24a6ce2f0a65c3d77fb7f695461f583 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Dec 2008 13:02:18 -0700 Subject: [PATCH 30/98] PCI: make PCI bus resource messages more meaningful Change PCI bus resource messages so they have a bit more context and look like the rest of PCI, e.g., - bus: 00 index 0 io port: [0x00-0xffff] - bus: 00 index 1 mmio: [0x000000-0xffffffff] + pci 0000:00: bus resource 0 io : [0x00-0xffff] + pci 0000:00: bus resource 1 mem: [0x000000-0xffffffff] This also changes them from KERN_INFO to KERN_DEBUG. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index ea979f2bc6db..09e2c3cd2bef 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -536,9 +536,9 @@ static void pci_bus_dump_res(struct pci_bus *bus) if (!res) continue; - printk(KERN_INFO "bus: %02x index %x %s: %pR\n", - bus->number, i, - (res->flags & IORESOURCE_IO) ? "io port" : "mmio", res); + printk(KERN_DEBUG "pci %04x:%02x: bus resource %d %s %pR\n", + pci_domain_nr(bus), bus->number, i, + (res->flags & IORESOURCE_IO) ? "io: " : "mem:", res); } } From 0663a36284586ac9a9781be8aa7e8ca9fff16d06 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Dec 2008 13:12:00 -0700 Subject: [PATCH 31/98] x86/PCI: make PCI bus locality messages more meaningful Change PCI bus locality messages so they have a bit more context and look like the rest of PCI, e.g., - bus 01 -> node 0 - bus 04 -> node 0 + pci 0000:01: bus on NUMA node 0 + pci 0000:04: bus on NUMA node 0 Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/acpi.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 9e5752fe4d15..6c7683709c44 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -210,11 +210,12 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do if (bus && node != -1) { #ifdef CONFIG_ACPI_NUMA if (pxm >= 0) - printk(KERN_DEBUG "bus %02x -> pxm %d -> node %d\n", - busnum, pxm, node); + printk(KERN_DEBUG + "pci %04x:%02x: bus on NUMA node %d (pxm %d)\n", + domain, busnum, node, pxm); #else - printk(KERN_DEBUG "bus %02x -> node %d\n", - busnum, node); + printk(KERN_DEBUG "pci %04x:%02x: bus on NUMA node %d\n", + domain, busnum, node); #endif } From 0e331bf1c44401a5b6d7b778dd1f254724132dff Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Wed, 10 Dec 2008 13:00:21 -0700 Subject: [PATCH 32/98] PCI: pcie port driver: remove extra printks These printks don't contain enough information to be useful. I think it would be more useful to have a message when a service driver binds to a root port. That could contain the service type, the interrupt mode and IRQ, etc. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 4 ---- 1 file changed, 4 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 75f501ab6468..6a9f83ccaff4 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -103,7 +103,6 @@ static int pcie_port_resume_service(struct device *dev) */ static void release_pcie_device(struct device *dev) { - dev_printk(KERN_DEBUG, dev, "free port service\n"); kfree(to_pcie_device(dev)); } @@ -150,7 +149,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (pos) { struct msix_entry msix_entries[PCIE_PORT_DEVICE_MAXSERVICES] = {{0, 0}, {0, 1}, {0, 2}, {0, 3}}; - dev_info(&dev->dev, "found MSI-X capability\n"); status = pci_enable_msix(dev, msix_entries, nvec); if (!status) { int j = 0; @@ -165,7 +163,6 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) if (status) { pos = pci_find_capability(dev, PCI_CAP_ID_MSI); if (pos) { - dev_info(&dev->dev, "found MSI capability\n"); status = pci_enable_msi(dev); if (!status) { interrupt_mode = PCIE_PORT_MSI_MODE; @@ -239,7 +236,6 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, return NULL; pcie_device_init(parent, device, port_type, service_type, irq,irq_mode); - dev_printk(KERN_DEBUG, &device->device, "allocate port service\n"); return device; } From 23a36002742bca87510201770a7d931c4aa63e97 Mon Sep 17 00:00:00 2001 From: Jacob Pan Date: Mon, 8 Dec 2008 09:44:16 -0800 Subject: [PATCH 33/98] PCI: avoid early PCI mmconfig init if pci=noearly is given in cmdline Early type 1 accesses can cause problems on some platforms, and pci=noearly is supposed to prevent them from occurring. However, early mcfg probing code uses type 1 and isn't protected by a check for noearly. This patch fixes that problem. Signed-off-by: Jacob Pan Signed-off-by: Jesse Barnes --- arch/x86/pci/init.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/arch/x86/pci/init.c b/arch/x86/pci/init.c index bec3b048e72b..25a1f8efed4a 100644 --- a/arch/x86/pci/init.c +++ b/arch/x86/pci/init.c @@ -12,7 +12,8 @@ static __init int pci_arch_init(void) type = pci_direct_probe(); #endif - pci_mmcfg_early_init(); + if (!(pci_probe & PCI_PROBE_NOEARLY)) + pci_mmcfg_early_init(); #ifdef CONFIG_PCI_OLPC if (!pci_olpc_init()) From 98d3333a13029ab07ca1d1bfb9bfa138ea76c3c0 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:11:41 -0700 Subject: [PATCH 34/98] PCI hotplug: cpqphp: use config space PCI interrupt pin encoding This patch changes cpqphp to use interrupt pin values just as they come from PCI config space, i.e., 1=INTA, ..., 4=INTD. pcibios_set_irq_routing() takes pin arguments in the range 0=INTA, ..., 3=INTD, so we'll adjust the pin just before calling it. Signed-off-by: Bjorn Helgaas Acked-by: Alex Chiang Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/cpqphp_ctrl.c | 4 ++-- drivers/pci/hotplug/cpqphp_pci.c | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index a60a25290995..d94722149e02 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -2604,7 +2604,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func for (cloop = 0; cloop < 4; cloop++) { if (irqs.valid_INT & (0x01 << cloop)) { rc = cpqhp_set_irq(func->bus, func->device, - 0x0A + cloop, irqs.interrupt[cloop]); + cloop + 1, irqs.interrupt[cloop]); if (rc) goto free_and_out; } @@ -2945,7 +2945,7 @@ static int configure_new_function(struct controller *ctrl, struct pci_func *func } if (!behind_bridge) { - rc = cpqhp_set_irq(func->bus, func->device, temp_byte + 0x09, IRQ); + rc = cpqhp_set_irq(func->bus, func->device, temp_byte, IRQ); if (rc) return 1; } else { diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index df146be9d2e9..6c0ed0fcb8ee 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -171,7 +171,7 @@ int cpqhp_set_irq (u8 bus_num, u8 dev_num, u8 int_pin, u8 irq_num) fakebus->number = bus_num; dbg("%s: dev %d, bus %d, pin %d, num %d\n", __func__, dev_num, bus_num, int_pin, irq_num); - rc = pcibios_set_irq_routing(fakedev, int_pin - 0x0a, irq_num); + rc = pcibios_set_irq_routing(fakedev, int_pin - 1, irq_num); kfree(fakedev); kfree(fakebus); dbg("%s: rc %d\n", __func__, rc); From 878f2e50fd1cfea575cdca5bf019c2175dc64131 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:11:46 -0700 Subject: [PATCH 35/98] PCI: use config space encoding in pci_get_interrupt_pin() This patch makes pci_get_interrupt_pin() return values encoded the same way as the "Interrupt Pin" value in PCI config space, i.e., 1=INTA, ..., 4=INTD. pirq_bios_set() is the only in-tree caller of pci_get_interrupt_pin() and pci_get_interrupt_pin() is not exported. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: hpa@zytor.com Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 2 +- drivers/pci/pci.c | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 373b9afe6d44..399a172f047d 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -533,7 +533,7 @@ static int pirq_bios_set(struct pci_dev *router, struct pci_dev *dev, int pirq, { struct pci_dev *bridge; int pin = pci_get_interrupt_pin(dev, &bridge); - return pcibios_set_irq_routing(bridge, pin, irq); + return pcibios_set_irq_routing(bridge, pin - 1, irq); } #endif diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 3222f9022707..bd52ca4c2893 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1374,9 +1374,9 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) pin = dev->pin; if (!pin) return -1; - pin--; + while (dev->bus->self) { - pin = (pin + PCI_SLOT(dev->devfn)) % 4; + pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; dev = dev->bus->self; } *bridge = dev; From f672c392b9c61bcdfb1247561d404b2c3ed4b0b3 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:11:51 -0700 Subject: [PATCH 36/98] x86/PCI: use config space encoding for interrupt pins Keep "pin" encoded as it is in the "Interrupt Pin" value in PCI config space, i.e., 0=device doesn't use interrupts, 1=INTA, ..., 4=INTD. This makes the bridge INTx swizzle match other architectures. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: hpa@zytor.com Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index 399a172f047d..cc9d5e254060 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -887,7 +887,6 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) dev_dbg(&dev->dev, "no interrupt pin\n"); return 0; } - pin = pin - 1; /* Find IRQ routing entry */ @@ -897,17 +896,17 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) info = pirq_get_info(dev); if (!info) { dev_dbg(&dev->dev, "PCI INT %c not found in routing table\n", - 'A' + pin); + 'A' + pin - 1); return 0; } - pirq = info->irq[pin].link; - mask = info->irq[pin].bitmap; + pirq = info->irq[pin - 1].link; + mask = info->irq[pin - 1].bitmap; if (!pirq) { - dev_dbg(&dev->dev, "PCI INT %c not routed\n", 'A' + pin); + dev_dbg(&dev->dev, "PCI INT %c not routed\n", 'A' + pin - 1); return 0; } dev_dbg(&dev->dev, "PCI INT %c -> PIRQ %02x, mask %04x, excl %04x", - 'A' + pin, pirq, mask, pirq_table->exclusive_irqs); + 'A' + pin - 1, pirq, mask, pirq_table->exclusive_irqs); mask &= pcibios_irq_mask; /* Work around broken HP Pavilion Notebooks which assign USB to @@ -949,7 +948,7 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) newirq = i; } } - dev_dbg(&dev->dev, "PCI INT %c -> newirq %d", 'A' + pin, newirq); + dev_dbg(&dev->dev, "PCI INT %c -> newirq %d", 'A' + pin - 1, newirq); /* Check if it is hardcoded */ if ((pirq & 0xf0) == 0xf0) { @@ -977,18 +976,18 @@ static int pcibios_lookup_irq(struct pci_dev *dev, int assign) return 0; } } - dev_info(&dev->dev, "%s PCI INT %c -> IRQ %d\n", msg, 'A' + pin, irq); + dev_info(&dev->dev, "%s PCI INT %c -> IRQ %d\n", msg, 'A' + pin - 1, irq); /* Update IRQ for all devices with the same pirq value */ while ((dev2 = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev2)) != NULL) { pci_read_config_byte(dev2, PCI_INTERRUPT_PIN, &pin); if (!pin) continue; - pin--; + info = pirq_get_info(dev2); if (!info) continue; - if (info->irq[pin].link == pirq) { + if (info->irq[pin - 1].link == pirq) { /* * We refuse to override the dev->irq * information. Give a warning! @@ -1055,9 +1054,8 @@ static void __init pcibios_fixup_irqs(void) /* * interrupt pins are numbered starting from 1 */ - pin--; irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, - PCI_SLOT(dev->devfn), pin); + PCI_SLOT(dev->devfn), pin - 1); /* * Busses behind bridges are typically not listed in the * MP-table. In this case we have to look up the IRQ @@ -1070,22 +1068,22 @@ static void __init pcibios_fixup_irqs(void) struct pci_dev *bridge = dev->bus->self; int bus; - pin = (pin + PCI_SLOT(dev->devfn)) % 4; + pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; bus = bridge->bus->number; irq = IO_APIC_get_PCI_irq_vector(bus, - PCI_SLOT(bridge->devfn), pin); + PCI_SLOT(bridge->devfn), pin - 1); if (irq >= 0) dev_warn(&dev->dev, "using bridge %s INT %c to " "get IRQ %d\n", pci_name(bridge), - 'A' + pin, irq); + 'A' + pin - 1, irq); } if (irq >= 0) { dev_info(&dev->dev, "PCI->APIC IRQ transform: INT %c " "-> IRQ %d\n", - 'A' + pin, irq); + 'A' + pin - 1, irq); dev->irq = irq; } } @@ -1220,12 +1218,10 @@ static int pirq_enable_irq(struct pci_dev *dev) if (pin && !pcibios_lookup_irq(dev, 1) && !dev->irq) { char *msg = ""; - pin--; /* interrupt pins are numbered starting from 1 */ - if (io_apic_assign_pci_irqs) { int irq; - irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin); + irq = IO_APIC_get_PCI_irq_vector(dev->bus->number, PCI_SLOT(dev->devfn), pin - 1); /* * Busses behind bridges are typically not listed in the MP-table. * In this case we have to look up the IRQ based on the parent bus, @@ -1236,20 +1232,20 @@ static int pirq_enable_irq(struct pci_dev *dev) while (irq < 0 && dev->bus->parent) { /* go back to the bridge */ struct pci_dev *bridge = dev->bus->self; - pin = (pin + PCI_SLOT(dev->devfn)) % 4; + pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, - PCI_SLOT(bridge->devfn), pin); + PCI_SLOT(bridge->devfn), pin - 1); if (irq >= 0) dev_warn(&dev->dev, "using bridge %s " "INT %c to get IRQ %d\n", - pci_name(bridge), 'A' + pin, + pci_name(bridge), 'A' + pin - 1, irq); dev = bridge; } dev = temp_dev; if (irq >= 0) { dev_info(&dev->dev, "PCI->APIC IRQ transform: " - "INT %c -> IRQ %d\n", 'A' + pin, irq); + "INT %c -> IRQ %d\n", 'A' + pin - 1, irq); dev->irq = irq; return 0; } else @@ -1268,7 +1264,7 @@ static int pirq_enable_irq(struct pci_dev *dev) return 0; dev_warn(&dev->dev, "can't find IRQ for PCI INT %c%s\n", - 'A' + pin, msg); + 'A' + pin - 1, msg); } return 0; } From 12b955ff63db0b75cfc2d4939696c57b31891ec6 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:11:57 -0700 Subject: [PATCH 37/98] x86/PCI: minor logic simplications Test "pin" immediately to simplify the subsequent code. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: hpa@zytor.com Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index cc9d5e254060..e1d605bfeb47 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -1041,6 +1041,9 @@ static void __init pcibios_fixup_irqs(void) dev = NULL; while ((dev = pci_get_device(PCI_ANY_ID, PCI_ANY_ID, dev)) != NULL) { pci_read_config_byte(dev, PCI_INTERRUPT_PIN, &pin); + if (!pin) + continue; + #ifdef CONFIG_X86_IO_APIC /* * Recalculate IRQ numbers if we use the I/O APIC. @@ -1048,9 +1051,6 @@ static void __init pcibios_fixup_irqs(void) if (io_apic_assign_pci_irqs) { int irq; - if (!pin) - continue; - /* * interrupt pins are numbered starting from 1 */ @@ -1091,7 +1091,7 @@ static void __init pcibios_fixup_irqs(void) /* * Still no IRQ? Try to lookup one... */ - if (pin && !dev->irq) + if (!dev->irq) pcibios_lookup_irq(dev, 0); } } From 57c2cf71c12318b72ebaa5720d210476b6bac4d4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 11 Dec 2008 11:24:23 -0700 Subject: [PATCH 38/98] PCI: add pci_swizzle_interrupt_pin() This patch adds pci_swizzle_interrupt_pin(), which implements the INTx swizzling algorithm specified in Table 9-1 of the "PCI-to-PCI Bridge Architecture Specification," revision 1.2. There are many architecture-specific implementations of this swizzle that can be replaced by this common one. Reviewed-by: David Howells Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 16 +++++++++++++++- include/linux/pci.h | 1 + 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index bd52ca4c2893..d4d71fae6233 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1366,6 +1366,20 @@ void pci_enable_ari(struct pci_dev *dev) bridge->ari_enabled = 1; } +/** + * pci_swizzle_interrupt_pin - swizzle INTx for device behind bridge + * @dev: the PCI device + * @pin: the INTx pin (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * + * Perform INTx swizzling for a device behind one level of bridge. This is + * required by section 9.1 of the PCI-to-PCI bridge specification for devices + * behind bridges on add-in cards. + */ +u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin) +{ + return (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; +} + int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) { @@ -1376,7 +1390,7 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) return -1; while (dev->bus->self) { - pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; + pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } *bridge = dev; diff --git a/include/linux/pci.h b/include/linux/pci.h index bfcb39ca8879..58357d14f94c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -532,6 +532,7 @@ int __must_check pci_bus_add_device(struct pci_dev *dev); void pci_read_bridge_bases(struct pci_bus *child); struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res); +u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin); int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); From f14d766a1a438fdc69d04b876965d6b26b071f14 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:12 -0700 Subject: [PATCH 39/98] PCI: arm: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Cc: Russell King Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/arm/kernel/bios32.c | 6 +++--- arch/arm/mach-integrator/pci.c | 11 ++--------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 17a59b6e521f..5c5a0bc06709 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -491,17 +491,17 @@ EXPORT_SYMBOL(pcibios_bus_to_resource); */ u8 __devinit pci_std_swizzle(struct pci_dev *dev, u8 *pinp) { - int pin = *pinp - 1; + int pin = *pinp; while (dev->bus->self) { - pin = (pin + PCI_SLOT(dev->devfn)) & 3; + pin = pci_swizzle_interrupt_pin(dev, pin); /* * move up the chain of bridges, * swizzling as we go. */ dev = dev->bus->self; } - *pinp = pin + 1; + *pinp = pin; return PCI_SLOT(dev->devfn); } diff --git a/arch/arm/mach-integrator/pci.c b/arch/arm/mach-integrator/pci.c index af7d3ff013ec..2fdb95433f0a 100644 --- a/arch/arm/mach-integrator/pci.c +++ b/arch/arm/mach-integrator/pci.c @@ -63,13 +63,7 @@ * * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A. * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 - * - * The following code swizzles for exactly one bridge. */ -static inline int bridge_swizzle(int pin, unsigned int slot) -{ - return (pin + slot) & 3; -} /* * This routine handles multiple bridges. @@ -81,15 +75,14 @@ static u8 __init integrator_swizzle(struct pci_dev *dev, u8 *pinp) if (pin == 0) pin = 1; - pin -= 1; while (dev->bus->self) { - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* * move up the chain of bridges, swizzling as we go. */ dev = dev->bus->self; } - *pinp = pin + 1; + *pinp = pin; return PCI_SLOT(dev->devfn); } From 6aa6e4981741013e4a8c7c3ee7b90c24e89fae24 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:32 -0700 Subject: [PATCH 40/98] PCI: sh: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Acked-by: Paul Mundt Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/sh/drivers/pci/ops-cayman.c | 9 ++------- arch/sh/drivers/pci/pci.c | 7 +------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/arch/sh/drivers/pci/ops-cayman.c b/arch/sh/drivers/pci/ops-cayman.c index 5ccf9ea3a9de..38ef76207af6 100644 --- a/arch/sh/drivers/pci/ops-cayman.c +++ b/arch/sh/drivers/pci/ops-cayman.c @@ -5,11 +5,6 @@ #include #include "pci-sh5.h" -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin - 1) + slot) % 4) + 1; -} - int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) { int result = -1; @@ -42,7 +37,7 @@ int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) while (dev->bus->number > 0) { slot = path[i].slot = PCI_SLOT(dev->devfn); - pin = path[i].pin = bridge_swizzle(pin, slot); + pin = path[i].pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; i++; if (i > 3) panic("PCI path to root bus too long!\n"); @@ -56,7 +51,7 @@ int __init pcibios_map_platform_irq(struct pci_dev *dev, u8 slot, u8 pin) if ((slot < 3) || (i == 0)) { /* Bus 0 (incl. PCI-PCI bridge itself) : perform the final swizzle now. */ - result = IRQ_INTA + bridge_swizzle(pin, slot) - 1; + result = IRQ_INTA + pci_swizzle_interrupt_pin(dev, pin) - 1; } else { i--; slot = path[i].slot; diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index d3839e609aac..aea3b6b3033a 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -21,17 +21,12 @@ #include #include -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin - 1) + slot) % 4) + 1; -} - static u8 __init simple_swizzle(struct pci_dev *dev, u8 *pinp) { u8 pin = *pinp; while (dev->bus->parent) { - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; } From 3f9455d488ca97f68a1c99c7473c26030261b713 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:27 -0700 Subject: [PATCH 41/98] PCI: powerpc: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Acked-by: Benjamin Herrenschmidt Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/powerpc/kernel/prom_parse.c | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/arch/powerpc/kernel/prom_parse.c b/arch/powerpc/kernel/prom_parse.c index 8c1335566089..8f0856f312da 100644 --- a/arch/powerpc/kernel/prom_parse.c +++ b/arch/powerpc/kernel/prom_parse.c @@ -232,11 +232,6 @@ int of_pci_address_to_resource(struct device_node *dev, int bar, } EXPORT_SYMBOL_GPL(of_pci_address_to_resource); -static u8 of_irq_pci_swizzle(u8 slot, u8 pin) -{ - return (((pin - 1) + slot) % 4) + 1; -} - int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) { struct device_node *dn, *ppnode; @@ -306,7 +301,7 @@ int of_irq_map_pci(struct pci_dev *pdev, struct of_irq *out_irq) /* We can only get here if we hit a P2P bridge with no node, * let's do standard swizzling and try again */ - lspec = of_irq_pci_swizzle(PCI_SLOT(pdev->devfn), lspec); + lspec = pci_swizzle_interrupt_pin(pdev, lspec); pdev = ppdev; } From 1be9baa09340bbe3329aab3bd0d41076f59c8f22 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:07 -0700 Subject: [PATCH 42/98] PCI: alpha: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Cc: Ivan Kokshaysky Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/alpha/kernel/pci.c | 2 +- arch/alpha/kernel/pci_impl.h | 9 ++------- arch/alpha/kernel/sys_dp264.c | 2 +- arch/alpha/kernel/sys_eiger.c | 2 +- arch/alpha/kernel/sys_miata.c | 2 +- arch/alpha/kernel/sys_noritake.c | 2 +- arch/alpha/kernel/sys_ruffian.c | 2 +- arch/alpha/kernel/sys_sable.c | 2 +- 8 files changed, 9 insertions(+), 14 deletions(-) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index ff8cb638472e..b03d9f420425 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -328,7 +328,7 @@ common_swizzle(struct pci_dev *dev, u8 *pinp) u8 pin = *pinp; while (dev->bus->parent) { - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; } diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h index f8b74995a002..7336651592a3 100644 --- a/arch/alpha/kernel/pci_impl.h +++ b/arch/alpha/kernel/pci_impl.h @@ -106,16 +106,11 @@ struct pci_iommu_arena; * Where A = pin 1, B = pin 2 and so on and pin=0 = default = A. * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 * - * The following code swizzles for exactly one bridge. The routine + * pci_swizzle_interrupt_pin() swizzles for exactly one bridge. The routine * common_swizzle below handles multiple bridges. But there are a - * couple boards that do strange things, so we define this here. + * couple boards that do strange things. */ -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin-1) + slot) % 4) + 1; -} - /* The following macro is used to implement the table-based irq mapping function for all single-bus Alphas. */ diff --git a/arch/alpha/kernel/sys_dp264.c b/arch/alpha/kernel/sys_dp264.c index ab44c164d9d4..9c9d1fd4155f 100644 --- a/arch/alpha/kernel/sys_dp264.c +++ b/arch/alpha/kernel/sys_dp264.c @@ -481,7 +481,7 @@ monet_swizzle(struct pci_dev *dev, u8 *pinp) slot = PCI_SLOT(dev->devfn); break; } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; diff --git a/arch/alpha/kernel/sys_eiger.c b/arch/alpha/kernel/sys_eiger.c index 7ef3b6fb3700..baf60f36cbd7 100644 --- a/arch/alpha/kernel/sys_eiger.c +++ b/arch/alpha/kernel/sys_eiger.c @@ -204,7 +204,7 @@ eiger_swizzle(struct pci_dev *dev, u8 *pinp) break; } /* Must be a card-based bridge. */ - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; diff --git a/arch/alpha/kernel/sys_miata.c b/arch/alpha/kernel/sys_miata.c index 910b43cd63e8..61ccd95579ec 100644 --- a/arch/alpha/kernel/sys_miata.c +++ b/arch/alpha/kernel/sys_miata.c @@ -219,7 +219,7 @@ miata_swizzle(struct pci_dev *dev, u8 *pinp) slot = PCI_SLOT(dev->devfn) + 9; break; } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; diff --git a/arch/alpha/kernel/sys_noritake.c b/arch/alpha/kernel/sys_noritake.c index eb2a1d63f484..538876b62449 100644 --- a/arch/alpha/kernel/sys_noritake.c +++ b/arch/alpha/kernel/sys_noritake.c @@ -257,7 +257,7 @@ noritake_swizzle(struct pci_dev *dev, u8 *pinp) slot = PCI_SLOT(dev->devfn) + 15; break; } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; diff --git a/arch/alpha/kernel/sys_ruffian.c b/arch/alpha/kernel/sys_ruffian.c index 5b99cf3cd69c..f15a329b6011 100644 --- a/arch/alpha/kernel/sys_ruffian.c +++ b/arch/alpha/kernel/sys_ruffian.c @@ -160,7 +160,7 @@ ruffian_swizzle(struct pci_dev *dev, u8 *pinp) slot = PCI_SLOT(dev->devfn) + 10; break; } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; diff --git a/arch/alpha/kernel/sys_sable.c b/arch/alpha/kernel/sys_sable.c index a4555f497639..d232e42be018 100644 --- a/arch/alpha/kernel/sys_sable.c +++ b/arch/alpha/kernel/sys_sable.c @@ -425,7 +425,7 @@ lynx_swizzle(struct pci_dev *dev, u8 *pinp) slot = PCI_SLOT(dev->devfn) + 11; break; } - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)) ; + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; From b1c86792a0f3cf24a12c1ac7d452d665d90284b1 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:37 -0700 Subject: [PATCH 43/98] PCI: x86: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Cc: Thomas Gleixner Cc: Ingo Molnar Cc: hpa@zytor.com Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/irq.c | 4 ++-- arch/x86/pci/visws.c | 7 +------ 2 files changed, 3 insertions(+), 8 deletions(-) diff --git a/arch/x86/pci/irq.c b/arch/x86/pci/irq.c index e1d605bfeb47..4064345cf144 100644 --- a/arch/x86/pci/irq.c +++ b/arch/x86/pci/irq.c @@ -1068,7 +1068,7 @@ static void __init pcibios_fixup_irqs(void) struct pci_dev *bridge = dev->bus->self; int bus; - pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; + pin = pci_swizzle_interrupt_pin(dev, pin); bus = bridge->bus->number; irq = IO_APIC_get_PCI_irq_vector(bus, PCI_SLOT(bridge->devfn), pin - 1); @@ -1232,7 +1232,7 @@ static int pirq_enable_irq(struct pci_dev *dev) while (irq < 0 && dev->bus->parent) { /* go back to the bridge */ struct pci_dev *bridge = dev->bus->self; - pin = (((pin - 1) + PCI_SLOT(dev->devfn)) % 4) + 1; + pin = pci_swizzle_interrupt_pin(dev, pin); irq = IO_APIC_get_PCI_irq_vector(bridge->bus->number, PCI_SLOT(bridge->devfn), pin - 1); if (irq >= 0) diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c index 16d0c0eb0d19..2c54e7e03f53 100644 --- a/arch/x86/pci/visws.c +++ b/arch/x86/pci/visws.c @@ -24,17 +24,12 @@ static void pci_visws_disable_irq(struct pci_dev *dev) { } unsigned int pci_bus0, pci_bus1; -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin - 1) + slot) % 4) + 1; -} - static u8 __init visws_swizzle(struct pci_dev *dev, u8 *pinp) { u8 pin = *pinp; while (dev->bus->self) { /* Move up the chain of bridges. */ - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); dev = dev->bus->self; } *pinp = pin; From 30d546dac9420fc238109aec2c6596e044cc6d4c Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:17 -0700 Subject: [PATCH 44/98] PCI: mips: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Acked-by: Ralf Baechle Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/mips/pci/pci-ip27.c | 6 ------ arch/mips/pci/pci.c | 9 +-------- 2 files changed, 1 insertion(+), 14 deletions(-) diff --git a/arch/mips/pci/pci-ip27.c b/arch/mips/pci/pci-ip27.c index f97ab1461012..dda6f2058665 100644 --- a/arch/mips/pci/pci-ip27.c +++ b/arch/mips/pci/pci-ip27.c @@ -146,12 +146,6 @@ int __devinit pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) return 0; } -/* Most MIPS systems have straight-forward swizzling needs. */ -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin - 1) + slot) % 4) + 1; -} - static inline struct pci_dev *bridge_root_dev(struct pci_dev *dev) { while (dev->bus->parent) { diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index 62cae740e250..fa63dc25125d 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -149,19 +149,12 @@ out: "Skipping PCI bus scan due to resource conflict\n"); } -/* Most MIPS systems have straight-forward swizzling needs. */ - -static inline u8 bridge_swizzle(u8 pin, u8 slot) -{ - return (((pin - 1) + slot) % 4) + 1; -} - static u8 __init common_swizzle(struct pci_dev *dev, u8 *pinp) { u8 pin = *pinp; while (dev->bus->parent) { - pin = bridge_swizzle(pin, PCI_SLOT(dev->devfn)); + pin = pci_swizzle_interrupt_pin(dev, pin); /* Move up the chain of bridges. */ dev = dev->bus->self; } From f0e88af855cbc5012f2e796f42686969b82d79d4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 9 Dec 2008 16:12:22 -0700 Subject: [PATCH 45/98] PCI: parisc: use generic pci_swizzle_interrupt_pin() Use the generic pci_swizzle_interrupt_pin() instead of arch-specific code. Acked-by: Kyle McMartin Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/parisc/dino.c | 4 ++-- drivers/parisc/iosapic.c | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/drivers/parisc/dino.c b/drivers/parisc/dino.c index 3bc54b30c3a1..77cc8bfef8c9 100644 --- a/drivers/parisc/dino.c +++ b/drivers/parisc/dino.c @@ -547,7 +547,7 @@ dino_card_fixup(struct pci_dev *dev) ** The additional "-1" adjusts for skewing the IRQ<->slot. */ dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); - dev->irq = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ; + dev->irq = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; /* Shouldn't really need to do this but it's in case someone tries ** to bypass PCI services and look at the card themselves. @@ -672,7 +672,7 @@ dino_fixup_bus(struct pci_bus *bus) dino_cfg_read(dev->bus, dev->devfn, PCI_INTERRUPT_PIN, 1, &irq_pin); - irq_pin = (irq_pin + PCI_SLOT(dev->devfn) - 1) % 4 ; + irq_pin = pci_swizzle_interrupt_pin(dev, irq_pin) - 1; printk(KERN_WARNING "Device %s has undefined IRQ, " "setting to %d\n", pci_name(dev), irq_pin); dino_cfg_write(dev->bus, dev->devfn, diff --git a/drivers/parisc/iosapic.c b/drivers/parisc/iosapic.c index 9dedbbd218c3..0797659ee016 100644 --- a/drivers/parisc/iosapic.c +++ b/drivers/parisc/iosapic.c @@ -519,8 +519,7 @@ iosapic_xlate_pin(struct iosapic_info *isi, struct pci_dev *pcidev) ** ** Advantage is it's really easy to implement. */ - intr_pin = ((intr_pin-1)+PCI_SLOT(pcidev->devfn)) % 4; - intr_pin++; /* convert back to INTA-D (1-4) */ + intr_pin = pci_swizzle_interrupt_pin(pcidev, intr_pin); #endif /* PCI_BRIDGE_FUNCS */ /* From bebd590ca27e80ffe3129ab4f0a3124f0a340f43 Mon Sep 17 00:00:00 2001 From: Alan Stern Date: Tue, 16 Dec 2008 14:06:58 -0500 Subject: [PATCH 46/98] PCI: fix incorrect error return in pci_enable_wake This patch (as1186) fixes a minor mistake in pci_enable_wake(). When the routine is asked to disable remote wakeup, it should not return an error merely because the device is not allowed to do wakeups! Signed-off-by: Alan Stern Acked-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index d4d71fae6233..1fb7cff4cdae 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1091,7 +1091,7 @@ int pci_enable_wake(struct pci_dev *dev, pci_power_t state, int enable) int error = 0; bool pme_done = false; - if (!device_may_wakeup(&dev->dev)) + if (enable && !device_may_wakeup(&dev->dev)) return -EINVAL; /* From 1684f5ddd4c0c754f52c78eaa2c5c69ad09fb18c Mon Sep 17 00:00:00 2001 From: Andrew Morton Date: Mon, 1 Dec 2008 14:30:30 -0800 Subject: [PATCH 47/98] PCI: uninline pci_ioremap_bar() It's too large to be inlined. Acked-by: Arjan van de Ven Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 16 ++++++++++++++++ include/linux/pci.h | 15 +-------------- 2 files changed, 17 insertions(+), 14 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1fb7cff4cdae..9354dd63f035 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -56,6 +56,22 @@ unsigned char pci_bus_max_busnr(struct pci_bus* bus) } EXPORT_SYMBOL_GPL(pci_bus_max_busnr); +#ifdef CONFIG_HAS_IOMEM +void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) +{ + /* + * Make sure the BAR is actually a memory resource, not an IO resource + */ + if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { + WARN_ON(1); + return NULL; + } + return ioremap_nocache(pci_resource_start(pdev, bar), + pci_resource_len(pdev, bar)); +} +EXPORT_SYMBOL_GPL(pci_ioremap_bar); +#endif + #if 0 /** * pci_max_busnr - returns maximum PCI bus number diff --git a/include/linux/pci.h b/include/linux/pci.h index 58357d14f94c..0d8bc920c2e5 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1160,20 +1160,7 @@ static inline void pci_mmcfg_late_init(void) { } int pci_ext_cfg_avail(struct pci_dev *dev); -#ifdef CONFIG_HAS_IOMEM -static inline void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar) -{ - /* - * Make sure the BAR is actually a memory resource, not an IO resource - */ - if (!(pci_resource_flags(pdev, bar) & IORESOURCE_MEM)) { - WARN_ON(1); - return NULL; - } - return ioremap_nocache(pci_resource_start(pdev, bar), - pci_resource_len(pdev, bar)); -} -#endif +void __iomem *pci_ioremap_bar(struct pci_dev *pdev, int bar); #endif /* __KERNEL__ */ #endif /* LINUX_PCI_H */ From 92425a405ea482209b43093a5e35be7de02acf18 Mon Sep 17 00:00:00 2001 From: Trent Piepho Date: Sun, 30 Nov 2008 17:10:12 -0800 Subject: [PATCH 48/98] PCI: Make settable sysfs attributes more consistent PCI devices have three settable boolean attributes, enable, broken_parity_status, and msi_bus. The store functions for these would silently interpret "0x01" as false, "1llogical" as true, and "true" would be (silently!) ignored and do nothing. This is inconsistent with typical sysfs handling of settable attributes, and just plain doesn't make much sense. So, use strict_strtoul(), which was created for this purpose. The store functions will treat a value of 0 as false, non-zero as true, and return -EINVAL for a parse failure. Additionally, is_enabled_store() and msi_bus_store() return -EPERM if CAP_SYS_ADMIN is lacking, rather than silently doing nothing. This is more typical behavior for sysfs attributes that need a capability. And msi_bus_store() will only print the "forced subordinate bus ..." warning if the MSI flag was actually forced to a different value. Signed-off-by: Trent Piepho Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 48 ++++++++++++++++++++++++----------------- 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d5cdccf27a69..d2f1354fd189 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -58,13 +58,14 @@ static ssize_t broken_parity_status_store(struct device *dev, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - ssize_t consumed = -EINVAL; + unsigned long val; - if ((count > 0) && (*buf == '0' || *buf == '1')) { - pdev->broken_parity_status = *buf == '1' ? 1 : 0; - consumed = count; - } - return consumed; + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; + + pdev->broken_parity_status = !!val; + + return count; } static ssize_t local_cpus_show(struct device *dev, @@ -133,19 +134,23 @@ static ssize_t is_enabled_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - ssize_t result = -EINVAL; struct pci_dev *pdev = to_pci_dev(dev); + unsigned long val; + ssize_t result = strict_strtoul(buf, 0, &val); + + if (result < 0) + return result; /* this can crash the machine when done on the "wrong" device */ if (!capable(CAP_SYS_ADMIN)) - return count; + return -EPERM; - if (*buf == '0') { + if (!val) { if (atomic_read(&pdev->enable_cnt) != 0) pci_disable_device(pdev); else result = -EIO; - } else if (*buf == '1') + } else result = pci_enable_device(pdev); return result < 0 ? result : count; @@ -185,25 +190,28 @@ msi_bus_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; + + if (strict_strtoul(buf, 0, &val) < 0) + return -EINVAL; /* bad things may happen if the no_msi flag is changed * while some drivers are loaded */ if (!capable(CAP_SYS_ADMIN)) - return count; + return -EPERM; + /* Maybe pci devices without subordinate busses shouldn't even have this + * attribute in the first place? */ if (!pdev->subordinate) return count; - if (*buf == '0') { - pdev->subordinate->bus_flags |= PCI_BUS_FLAGS_NO_MSI; - dev_warn(&pdev->dev, "forced subordinate bus to not support MSI," - " bad things could happen.\n"); - } + /* Is the flag going to change, or keep the value it already had? */ + if (!(pdev->subordinate->bus_flags & PCI_BUS_FLAGS_NO_MSI) ^ + !!val) { + pdev->subordinate->bus_flags ^= PCI_BUS_FLAGS_NO_MSI; - if (*buf == '1') { - pdev->subordinate->bus_flags &= ~PCI_BUS_FLAGS_NO_MSI; - dev_warn(&pdev->dev, "forced subordinate bus to support MSI," - " bad things could happen.\n"); + dev_warn(&pdev->dev, "forced subordinate bus to%s support MSI," + " bad things could happen\n", val ? "" : " not"); } return count; From 999da9fd489cd9774a89122940190376e19b21ce Mon Sep 17 00:00:00 2001 From: Adam Jackson Date: Mon, 1 Dec 2008 14:30:29 -0800 Subject: [PATCH 49/98] PCI quirks: piix3: warn softer about enabling passive release All the other quirks are dev_info() not dev_err(), this one isn't special. This makes 'quiet' boot in qemu really quiet. Signed-off-by: Adam Jackson Cc: Bartlomiej Zolnierkiewicz Cc: Jeff Garzik Cc: Tejun Heo Signed-off-by: Andrew Morton Signed-off-by: Jesse Barnes --- drivers/pci/quirks.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index d3a9e0f38682..baad093aafe3 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -56,7 +56,7 @@ static void quirk_passive_release(struct pci_dev *dev) while ((d = pci_get_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371SB_0, d))) { pci_read_config_byte(d, 0x82, &dlc); if (!(dlc & 1<<1)) { - dev_err(&d->dev, "PIIX3: Enabling Passive Release\n"); + dev_info(&d->dev, "PIIX3: Enabling Passive Release\n"); dlc |= 1<<1; pci_write_config_byte(d, 0x82, dlc); } From 6a49d8120021897e139641062236215aac5d220e Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:38:21 +0800 Subject: [PATCH 50/98] PCI: enhance pci_ari_enabled() Change parameter of pci_ari_enabled() from 'pci_dev' to 'pci_bus'. ARI forwarding on the bridge mostly concerns the subordinate devices rather than the bridge itself. So this change will make the function easier to use. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 7242b511a93f..392388468f6f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -165,13 +165,13 @@ struct pci_slot_attribute { extern void pci_enable_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status - * @dev: the PCI device + * @bus: the PCI bus * * Returns 1 if ARI forwarding is enabled, or 0 if not enabled; */ -static inline int pci_ari_enabled(struct pci_dev *dev) +static inline int pci_ari_enabled(struct pci_bus *bus) { - return dev->ari_enabled; + return bus->self && bus->self->ari_enabled; } #endif /* DRIVERS_PCI_H */ From 14add80b5120966fe0659d61815b9e9b4b68fdc5 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:38:52 +0800 Subject: [PATCH 51/98] PCI: remove unnecessary arg of pci_update_resource() This cleanup removes unnecessary argument 'struct resource *res' in pci_update_resource(), so it takes same arguments as other companion functions (pci_assign_resource(), etc.). Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 4 ++-- drivers/pci/setup-res.c | 7 ++++--- include/linux/pci.h | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9354dd63f035..c3ef2e78fc58 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -393,8 +393,8 @@ pci_restore_bars(struct pci_dev *dev) return; } - for (i = 0; i < numres; i ++) - pci_update_resource(dev, &dev->resource[i], i); + for (i = 0; i < numres; i++) + pci_update_resource(dev, i); } static struct pci_platform_pm_ops *pci_platform_pm; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 4e375632499a..3c5203ff53c7 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -26,11 +26,12 @@ #include "pci.h" -void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno) +void pci_update_resource(struct pci_dev *dev, int resno) { struct pci_bus_region region; u32 new, check, mask; int reg; + struct resource *res = dev->resource + resno; /* * Ignore resources for unimplemented BARs and unused resource slots @@ -162,7 +163,7 @@ int pci_assign_resource(struct pci_dev *dev, int resno) } else { res->flags &= ~IORESOURCE_STARTALIGN; if (resno < PCI_BRIDGE_RESOURCES) - pci_update_resource(dev, res, resno); + pci_update_resource(dev, resno); } return ret; @@ -197,7 +198,7 @@ int pci_assign_resource_fixed(struct pci_dev *dev, int resno) dev_err(&dev->dev, "BAR %d: can't allocate %s resource %pR\n", resno, res->flags & IORESOURCE_IO ? "I/O" : "mem", res); } else if (resno < PCI_BRIDGE_RESOURCES) { - pci_update_resource(dev, res, resno); + pci_update_resource(dev, resno); } return ret; diff --git a/include/linux/pci.h b/include/linux/pci.h index 0d8bc920c2e5..c5e02f324e13 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -648,7 +648,7 @@ int pcie_get_readrq(struct pci_dev *dev); int pcie_set_readrq(struct pci_dev *dev, int rq); int pci_reset_function(struct pci_dev *dev); int pci_execute_reset_function(struct pci_dev *dev); -void pci_update_resource(struct pci_dev *dev, struct resource *res, int resno); +void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int pci_select_bars(struct pci_dev *dev, unsigned long flags); From fde09c6d8f92de0c9f75698a75f0989f2234c517 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:39:32 +0800 Subject: [PATCH 52/98] PCI: define PCI resource names in an 'enum' This patch moves all definitions of the PCI resource names to an 'enum', and also replaces some hard-coded resource variables with symbol names. This change eases introduction of device specific resources. Reviewed-by: Bjorn Helgaas Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci-sysfs.c | 4 +++- drivers/pci/probe.c | 2 +- drivers/pci/proc.c | 7 ++++--- include/linux/pci.h | 37 ++++++++++++++++++++++++------------- 4 files changed, 32 insertions(+), 18 deletions(-) diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index d2f1354fd189..ea54cedcdfc6 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -102,11 +102,13 @@ resource_show(struct device * dev, struct device_attribute *attr, char * buf) struct pci_dev * pci_dev = to_pci_dev(dev); char * str = buf; int i; - int max = 7; + int max; resource_size_t start, end; if (pci_dev->subordinate) max = DEVICE_COUNT_RESOURCE; + else + max = PCI_BRIDGE_RESOURCES; for (i = 0; i < max; i++) { struct resource *res = &pci_dev->resource[i]; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5dcf2b65e3f9..e1cf5d50ed4d 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -423,7 +423,7 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->subordinate = 0xff; /* Set up default resource pointers and names.. */ - for (i = 0; i < 4; i++) { + for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; child->resource[i]->name = child->name; } diff --git a/drivers/pci/proc.c b/drivers/pci/proc.c index 7fb086d39617..593bb844b8db 100644 --- a/drivers/pci/proc.c +++ b/drivers/pci/proc.c @@ -361,15 +361,16 @@ static int show_device(struct seq_file *m, void *v) dev->vendor, dev->device, dev->irq); - /* Here should be 7 and not PCI_NUM_RESOURCES as we need to preserve compatibility */ - for (i=0; i<7; i++) { + + /* only print standard and ROM resources to preserve compatibility */ + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { resource_size_t start, end; pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, "\t%16llx", (unsigned long long)(start | (dev->resource[i].flags & PCI_REGION_FLAG_MASK))); } - for (i=0; i<7; i++) { + for (i = 0; i <= PCI_ROM_RESOURCE; i++) { resource_size_t start, end; pci_resource_to_user(dev, i, &dev->resource[i], &start, &end); seq_printf(m, "\t%16llx", diff --git a/include/linux/pci.h b/include/linux/pci.h index c5e02f324e13..da1c22bab40e 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -82,7 +82,30 @@ enum pci_mmap_state { #define PCI_DMA_FROMDEVICE 2 #define PCI_DMA_NONE 3 -#define DEVICE_COUNT_RESOURCE 12 +/* + * For PCI devices, the region numbers are assigned this way: + */ +enum { + /* #0-5: standard PCI resources */ + PCI_STD_RESOURCES, + PCI_STD_RESOURCE_END = 5, + + /* #6: expansion ROM resource */ + PCI_ROM_RESOURCE, + + /* resources assigned to buses behind the bridge */ +#define PCI_BRIDGE_RESOURCE_NUM 4 + + PCI_BRIDGE_RESOURCES, + PCI_BRIDGE_RESOURCE_END = PCI_BRIDGE_RESOURCES + + PCI_BRIDGE_RESOURCE_NUM - 1, + + /* total resources associated with a PCI device */ + PCI_NUM_RESOURCES, + + /* preserve this for compatibility */ + DEVICE_COUNT_RESOURCE +}; typedef int __bitwise pci_power_t; @@ -274,18 +297,6 @@ static inline void pci_add_saved_cap(struct pci_dev *pci_dev, hlist_add_head(&new_cap->next, &pci_dev->saved_cap_space); } -/* - * For PCI devices, the region numbers are assigned this way: - * - * 0-5 standard PCI regions - * 6 expansion ROM - * 7-10 bridges: address space assigned to buses behind the bridge - */ - -#define PCI_ROM_RESOURCE 6 -#define PCI_BRIDGE_RESOURCES 7 -#define PCI_NUM_RESOURCES 11 - #ifndef PCI_BUS_NUM_RESOURCES #define PCI_BUS_NUM_RESOURCES 16 #endif From bc5f5a8277cb353161454b6704b3186ebcf3a2a3 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:40:00 +0800 Subject: [PATCH 53/98] PCI: remove unnecessary condition check in pci_restore_bars() Remove the unnecessary number of resources condition checks because the pci_update_resource() will check availability of the resources. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 19 ++----------------- 1 file changed, 2 insertions(+), 17 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c3ef2e78fc58..deeab19d7d10 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -376,24 +376,9 @@ pci_find_parent_resource(const struct pci_dev *dev, struct resource *res) static void pci_restore_bars(struct pci_dev *dev) { - int i, numres; + int i; - switch (dev->hdr_type) { - case PCI_HEADER_TYPE_NORMAL: - numres = 6; - break; - case PCI_HEADER_TYPE_BRIDGE: - numres = 2; - break; - case PCI_HEADER_TYPE_CARDBUS: - numres = 1; - break; - default: - /* Should never get here, but just in case... */ - return; - } - - for (i = 0; i < numres; i++) + for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) pci_update_resource(dev, i); } From 0b400c7ed4d027e02f6231afa39852a2d48e6f25 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:40:40 +0800 Subject: [PATCH 54/98] PCI: export __pci_read_base() Export __pci_read_base() so it can be used by whole PCI subsystem. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.h | 9 +++++++++ drivers/pci/probe.c | 20 +++++++++----------- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 392388468f6f..d881fde8bb82 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -162,6 +162,15 @@ struct pci_slot_attribute { }; #define to_pci_slot_attr(s) container_of(s, struct pci_slot_attribute, attr) +enum pci_bar_type { + pci_bar_unknown, /* Standard PCI BAR probe */ + pci_bar_io, /* An io port BAR */ + pci_bar_mem32, /* A 32-bit memory BAR */ + pci_bar_mem64, /* A 64-bit memory BAR */ +}; + +extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, + struct resource *res, unsigned int reg); extern void pci_enable_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index e1cf5d50ed4d..5372d3699e08 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -135,13 +135,6 @@ static u64 pci_size(u64 base, u64 maxbase, u64 mask) return size; } -enum pci_bar_type { - pci_bar_unknown, /* Standard PCI BAR probe */ - pci_bar_io, /* An io port BAR */ - pci_bar_mem32, /* A 32-bit memory BAR */ - pci_bar_mem64, /* A 64-bit memory BAR */ -}; - static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) { if ((bar & PCI_BASE_ADDRESS_SPACE) == PCI_BASE_ADDRESS_SPACE_IO) { @@ -156,11 +149,16 @@ static inline enum pci_bar_type decode_bar(struct resource *res, u32 bar) return pci_bar_mem32; } -/* - * If the type is not unknown, we assume that the lowest bit is 'enable'. - * Returns 1 if the BAR was 64-bit and 0 if it was 32-bit. +/** + * pci_read_base - read a PCI BAR + * @dev: the PCI device + * @type: type of the BAR + * @res: resource buffer to be filled in + * @pos: BAR position in the config space + * + * Returns 1 if the BAR is 64-bit, or 0 if 32-bit. */ -static int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, +int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int pos) { u32 l, sz, mask; From 3789fa8a2e534523c896a32a9f27f78d52ad7d82 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:41:07 +0800 Subject: [PATCH 55/98] PCI: allow pci_alloc_child_bus() to handle a NULL bridge Allow pci_alloc_child_bus() to allocate buses without bridge devices. Some SR-IOV devices can occupy more than one bus number, but there is no explicit bridges because that have internal routing mechanism. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/probe.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 5372d3699e08..2ee00962734b 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -398,12 +398,10 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, if (!child) return NULL; - child->self = bridge; child->parent = parent; child->ops = parent->ops; child->sysdata = parent->sysdata; child->bus_flags = parent->bus_flags; - child->bridge = get_device(&bridge->dev); /* initialize some portions of the bus device, but don't register it * now as the parent is not properly set up yet. This device will get @@ -420,6 +418,12 @@ static struct pci_bus *pci_alloc_child_bus(struct pci_bus *parent, child->primary = parent->secondary; child->subordinate = 0xff; + if (!bridge) + return child; + + child->self = bridge; + child->bridge = get_device(&bridge->dev); + /* Set up default resource pointers and names.. */ for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) { child->resource[i] = &bridge->resource[PCI_BRIDGE_RESOURCES+i]; From 613e7ed6f72b1a115f7ece8ce1b66cf095de1348 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:41:27 +0800 Subject: [PATCH 56/98] PCI: add a new function to map BAR offsets Add a function to map a given resource number to a corresponding register so drivers can get the offset and type of device specific BARs. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 22 ++++++++++++++++++++++ drivers/pci/pci.h | 2 ++ drivers/pci/setup-res.c | 13 +++++-------- 3 files changed, 29 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index deeab19d7d10..7e9c0f3936dd 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -2201,6 +2201,28 @@ int pci_select_bars(struct pci_dev *dev, unsigned long flags) return bars; } +/** + * pci_resource_bar - get position of the BAR associated with a resource + * @dev: the PCI device + * @resno: the resource number + * @type: the BAR type to be filled in + * + * Returns BAR position in config space, or 0 if the BAR is invalid. + */ +int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type) +{ + if (resno < PCI_ROM_RESOURCE) { + *type = pci_bar_unknown; + return PCI_BASE_ADDRESS_0 + 4 * resno; + } else if (resno == PCI_ROM_RESOURCE) { + *type = pci_bar_mem32; + return dev->rom_base_reg; + } + + dev_err(&dev->dev, "BAR: invalid resource #%d\n", resno); + return 0; +} + static void __devinit pci_no_domains(void) { #ifdef CONFIG_PCI_DOMAINS diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d881fde8bb82..c4f4a1e6ea28 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -171,6 +171,8 @@ enum pci_bar_type { extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); +extern int pci_resource_bar(struct pci_dev *dev, int resno, + enum pci_bar_type *type); extern void pci_enable_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 3c5203ff53c7..32e8d88a4619 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -31,6 +31,7 @@ void pci_update_resource(struct pci_dev *dev, int resno) struct pci_bus_region region; u32 new, check, mask; int reg; + enum pci_bar_type type; struct resource *res = dev->resource + resno; /* @@ -62,17 +63,13 @@ void pci_update_resource(struct pci_dev *dev, int resno) else mask = (u32)PCI_BASE_ADDRESS_MEM_MASK; - if (resno < 6) { - reg = PCI_BASE_ADDRESS_0 + 4 * resno; - } else if (resno == PCI_ROM_RESOURCE) { + reg = pci_resource_bar(dev, resno, &type); + if (!reg) + return; + if (type != pci_bar_unknown) { if (!(res->flags & IORESOURCE_ROM_ENABLE)) return; new |= PCI_ROM_ADDRESS_ENABLE; - reg = dev->rom_base_reg; - } else { - /* Hmm, non-standard resource. */ - - return; /* kill uninitialised var warning */ } pci_write_config_dword(dev, reg, new); From 3fa16fdb48e0d83c2acf46e357548c89891df58b Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:41:45 +0800 Subject: [PATCH 57/98] PCI: cleanup pci_bus_add_devices() Cleanup pci_bus_add_devices() by negating the conditional and continuing, rather than having a single conditional take up the whole body. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 53 ++++++++++++++++++++++++----------------------- 1 file changed, 27 insertions(+), 26 deletions(-) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 3e1c135b174a..1b6de1b565aa 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -71,7 +71,7 @@ pci_bus_alloc_resource(struct pci_bus *bus, struct resource *res, } /** - * add a single device + * pci_bus_add_device - add a single device * @dev: device to add * * This adds a single pci device to the global @@ -105,7 +105,7 @@ int pci_bus_add_device(struct pci_dev *dev) void pci_bus_add_devices(struct pci_bus *bus) { struct pci_dev *dev; - struct pci_bus *child_bus; + struct pci_bus *child; int retval; list_for_each_entry(dev, &bus->devices, bus_list) { @@ -120,39 +120,40 @@ void pci_bus_add_devices(struct pci_bus *bus) list_for_each_entry(dev, &bus->devices, bus_list) { BUG_ON(!dev->is_added); + child = dev->subordinate; /* * If there is an unattached subordinate bus, attach * it and then scan for unattached PCI devices. */ - if (dev->subordinate) { - if (list_empty(&dev->subordinate->node)) { - down_write(&pci_bus_sem); - list_add_tail(&dev->subordinate->node, - &dev->bus->children); - up_write(&pci_bus_sem); - } - pci_bus_add_devices(dev->subordinate); + if (!child) + continue; + if (list_empty(&child->node)) { + down_write(&pci_bus_sem); + list_add_tail(&child->node, &dev->bus->children); + up_write(&pci_bus_sem); + } + pci_bus_add_devices(child); - /* register the bus with sysfs as the parent is now - * properly registered. */ - child_bus = dev->subordinate; - if (child_bus->is_added) - continue; - child_bus->dev.parent = child_bus->bridge; - retval = device_register(&child_bus->dev); - if (retval) - dev_err(&dev->dev, "Error registering pci_bus," - " continuing...\n"); - else { - child_bus->is_added = 1; - retval = device_create_file(&child_bus->dev, - &dev_attr_cpuaffinity); - } + /* + * register the bus with sysfs as the parent is now + * properly registered. + */ + if (child->is_added) + continue; + child->dev.parent = child->bridge; + retval = device_register(&child->dev); + if (retval) + dev_err(&dev->dev, "Error registering pci_bus," + " continuing...\n"); + else { + child->is_added = 1; + retval = device_create_file(&child->dev, + &dev_attr_cpuaffinity); if (retval) dev_err(&dev->dev, "Error creating cpuaffinity" " file, continuing...\n"); - retval = device_create_file(&child_bus->dev, + retval = device_create_file(&child->dev, &dev_attr_cpulistaffinity); if (retval) dev_err(&dev->dev, From 876e501ab25dcd683574a5d3d56d8fe450083ed6 Mon Sep 17 00:00:00 2001 From: Yu Zhao Date: Sat, 22 Nov 2008 02:42:35 +0800 Subject: [PATCH 58/98] PCI: factor pci_bus_add_child() from pci_bus_add_devices() This patch splits a new function, pci_bus_add_child(), from pci_bus_add_devices(). The new function can be used to register PCI buses to the device core. Signed-off-by: Yu Zhao Signed-off-by: Jesse Barnes --- drivers/pci/bus.c | 56 ++++++++++++++++++++++++++++------------------- drivers/pci/pci.h | 1 + 2 files changed, 34 insertions(+), 23 deletions(-) diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 1b6de1b565aa..52b54f053be0 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -90,6 +90,37 @@ int pci_bus_add_device(struct pci_dev *dev) return 0; } +/** + * pci_bus_add_child - add a child bus + * @bus: bus to add + * + * This adds sysfs entries for a single bus + */ +int pci_bus_add_child(struct pci_bus *bus) +{ + int retval; + + if (bus->bridge) + bus->dev.parent = bus->bridge; + + retval = device_register(&bus->dev); + if (retval) + return retval; + + bus->is_added = 1; + + retval = device_create_file(&bus->dev, &dev_attr_cpuaffinity); + if (retval) + return retval; + + retval = device_create_file(&bus->dev, &dev_attr_cpulistaffinity); + + /* Create legacy_io and legacy_mem files for this bus */ + pci_create_legacy_files(bus); + + return retval; +} + /** * pci_bus_add_devices - insert newly discovered PCI devices * @bus: bus to check for new devices @@ -140,30 +171,9 @@ void pci_bus_add_devices(struct pci_bus *bus) */ if (child->is_added) continue; - child->dev.parent = child->bridge; - retval = device_register(&child->dev); + retval = pci_bus_add_child(child); if (retval) - dev_err(&dev->dev, "Error registering pci_bus," - " continuing...\n"); - else { - child->is_added = 1; - retval = device_create_file(&child->dev, - &dev_attr_cpuaffinity); - if (retval) - dev_err(&dev->dev, "Error creating cpuaffinity" - " file, continuing...\n"); - - retval = device_create_file(&child->dev, - &dev_attr_cpulistaffinity); - if (retval) - dev_err(&dev->dev, - "Error creating cpulistaffinity" - " file, continuing...\n"); - - /* Create legacy_io and legacy_mem files for this bus */ - pci_create_legacy_files(child_bus); - - } + dev_err(&dev->dev, "Error adding bus, continuing\n"); } } diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index c4f4a1e6ea28..d1e92d83aa06 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -173,6 +173,7 @@ extern int __pci_read_base(struct pci_dev *dev, enum pci_bar_type type, struct resource *res, unsigned int reg); extern int pci_resource_bar(struct pci_dev *dev, int resno, enum pci_bar_type *type); +extern int pci_bus_add_child(struct pci_bus *bus); extern void pci_enable_ari(struct pci_dev *dev); /** * pci_ari_enabled - query ARI forwarding status From eb9c39d031bbcfd4005bd7e0337c3fd3909c1bf7 Mon Sep 17 00:00:00 2001 From: Jesse Barnes Date: Wed, 17 Dec 2008 12:10:05 -0800 Subject: [PATCH 59/98] PCI: set device wakeup capable flag if platform support is present When PCI devices are initialized, we check whether they support PCI PM caps and set the device can_wakeup flag if so. However, some devices may have platform provided wakeup events rather than PCI PME signals, so we need to set can_wakeup in that case too. Doing so should allow wakeups from many more devices, especially on cost constrained systems. Reported-by: Alan Stern Tested-by: Joseph Chan Acked-by: "Rafael J. Wysocki" Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 20 ++++++++++++++++++++ drivers/pci/pci.h | 1 + drivers/pci/probe.c | 1 + 3 files changed, 22 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 7e9c0f3936dd..1b807330e500 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1285,6 +1285,26 @@ void pci_pm_init(struct pci_dev *dev) } } +/** + * platform_pci_wakeup_init - init platform wakeup if present + * @dev: PCI device + * + * Some devices don't have PCI PM caps but can still generate wakeup + * events through platform methods (like ACPI events). If @dev supports + * platform wakeup events, set the device flag to indicate as much. This + * may be redundant if the device also supports PCI PM caps, but double + * initialization should be safe in that case. + */ +void platform_pci_wakeup_init(struct pci_dev *dev) +{ + if (!platform_pci_can_wakeup(dev)) + return; + + device_set_wakeup_capable(&dev->dev, true); + device_set_wakeup_enable(&dev->dev, false); + platform_pci_sleep_wake(dev, false); +} + /** * pci_add_save_buffer - allocate buffer for saving given capability registers * @dev: the PCI device diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d1e92d83aa06..65deed8bfc06 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -45,6 +45,7 @@ struct pci_platform_pm_ops { extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); 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); extern int pci_user_read_config_byte(struct pci_dev *dev, int where, u8 *val); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2ee00962734b..303644614eea 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -965,6 +965,7 @@ static void pci_init_capabilities(struct pci_dev *dev) /* Power Management */ pci_pm_init(dev); + platform_pci_wakeup_init(dev); /* Vital Product Data */ pci_vpd_pci22_init(dev); From f06fc0b6f8a6846e0ad48aee7b0f282b4fb5dcdc Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 27 Dec 2008 16:30:52 +0100 Subject: [PATCH 60/98] PCI PM: Fix pci_update_current_state Currently, PCI devices without the PM capability that are power manageable by the platform (eg. ACPI) are not handled correctly by pci_set_power_state(), because their current_state field is not updated to reflect the new power state of the device. Fix this by making pci_update_current_state() accept additional argument representing the power state of the device as set by the platform. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 1b807330e500..9d2aa6366fd0 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -525,14 +525,17 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) * pci_update_current_state - Read PCI power state of given device from its * PCI PM registers and cache it * @dev: PCI device to handle. + * @state: State to cache in case the device doesn't have the PM capability */ -static void pci_update_current_state(struct pci_dev *dev) +static void pci_update_current_state(struct pci_dev *dev, pci_power_t state) { if (dev->pm_cap) { u16 pmcsr; pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); dev->current_state = (pmcsr & PCI_PM_CTRL_STATE_MASK); + } else { + dev->current_state = state; } } @@ -575,7 +578,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) */ int ret = platform_pci_set_power_state(dev, PCI_D0); if (!ret) - pci_update_current_state(dev); + pci_update_current_state(dev, PCI_D0); } /* This device is quirked not to be put into D3, so don't put it in D3 */ @@ -588,7 +591,7 @@ int pci_set_power_state(struct pci_dev *dev, pci_power_t state) /* Allow the platform to finalize the transition */ int ret = platform_pci_set_power_state(dev, state); if (!ret) { - pci_update_current_state(dev); + pci_update_current_state(dev, state); error = 0; } } From a79d682f789730dfabaebbb507c87a90c0671a62 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sat, 27 Dec 2008 16:28:58 +0100 Subject: [PATCH 61/98] PCI PM: Split PCI Express port suspend-resume Suspend-resume of PCI Express ports has recently been moved into _suspend_late() and _resume_early() callbacks, but some functions executed from there should not be called with interrupts disabled, eg. pci_enable_device(). For this reason, split the suspend-resume of PCI Express ports into parts to be executed with interrupts disabled and with interrupts enabled. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_pci.c | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/drivers/pci/pcie/portdrv_pci.c b/drivers/pci/pcie/portdrv_pci.c index 0549fe2bdac9..99a914a027f8 100644 --- a/drivers/pci/pcie/portdrv_pci.c +++ b/drivers/pci/pcie/portdrv_pci.c @@ -41,7 +41,6 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) { int retval; - pci_restore_state(dev); retval = pci_enable_device(dev); if (retval) return retval; @@ -50,23 +49,32 @@ static int pcie_portdrv_restore_config(struct pci_dev *dev) } #ifdef CONFIG_PM +static int pcie_portdrv_suspend(struct pci_dev *dev, pm_message_t state) +{ + return pcie_port_device_suspend(dev, state); + +} + static int pcie_portdrv_suspend_late(struct pci_dev *dev, pm_message_t state) { - int ret = pcie_port_device_suspend(dev, state); - - if (!ret) - ret = pcie_portdrv_save_config(dev); - return ret; + return pci_save_state(dev); } static int pcie_portdrv_resume_early(struct pci_dev *dev) +{ + return pci_restore_state(dev); +} + +static int pcie_portdrv_resume(struct pci_dev *dev) { pcie_portdrv_restore_config(dev); return pcie_port_device_resume(dev); } #else +#define pcie_portdrv_suspend NULL #define pcie_portdrv_suspend_late NULL #define pcie_portdrv_resume_early NULL +#define pcie_portdrv_resume NULL #endif /* @@ -221,6 +229,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) { + pci_restore_state(dev); pcie_portdrv_restore_config(dev); pci_enable_pcie_error_reporting(dev); } @@ -282,8 +291,10 @@ static struct pci_driver pcie_portdriver = { .probe = pcie_portdrv_probe, .remove = pcie_portdrv_remove, + .suspend = pcie_portdrv_suspend, .suspend_late = pcie_portdrv_suspend_late, .resume_early = pcie_portdrv_resume_early, + .resume = pcie_portdrv_resume, .err_handler = &pcie_portdrv_err_handler, }; From 873392ca514f87eae39f53b6944caf85b1a047cb Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 31 Dec 2008 23:54:56 +1030 Subject: [PATCH 62/98] PCI: work_on_cpu: use in drivers/pci/pci-driver.c This uses work_on_cpu(), rather than altering the cpumask of the thread which we happen to be. Note the cleanups: 1) I've removed the CONFIG_NUMA test, since dev_to_node() returns -1 for !CONFIG_NUMA anyway and the compiler will eliminate it. 2) No need to reset mempolicy to default (a bad idea anyway) since work_on_cpu is run from a workqueue. Signed-off-by: Rusty Russell Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 52 ++++++++++++++++++++++++---------------- 1 file changed, 32 insertions(+), 20 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 888191a3b0d1..c3f76be832d4 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -16,6 +16,7 @@ #include #include #include +#include #include "pci.h" /* @@ -185,32 +186,43 @@ static const struct pci_device_id *pci_match_device(struct pci_driver *drv, return pci_match_id(drv->id_table, dev); } +struct drv_dev_and_id { + struct pci_driver *drv; + struct pci_dev *dev; + const struct pci_device_id *id; +}; + +static long local_pci_probe(void *_ddi) +{ + struct drv_dev_and_id *ddi = _ddi; + + return ddi->drv->probe(ddi->dev, ddi->id); +} + static int pci_call_probe(struct pci_driver *drv, struct pci_dev *dev, const struct pci_device_id *id) { - int error; -#ifdef CONFIG_NUMA - /* Execute driver initialization on node where the - device's bus is attached to. This way the driver likely - allocates its local memory on the right node without - any need to change it. */ - struct mempolicy *oldpol; - cpumask_t oldmask = current->cpus_allowed; - int node = dev_to_node(&dev->dev); + int error, node; + struct drv_dev_and_id ddi = { drv, dev, id }; + /* Execute driver initialization on node where the device's + bus is attached to. This way the driver likely allocates + its local memory on the right node without any need to + change it. */ + node = dev_to_node(&dev->dev); if (node >= 0) { + int cpu; node_to_cpumask_ptr(nodecpumask, node); - set_cpus_allowed_ptr(current, nodecpumask); - } - /* And set default memory allocation policy */ - oldpol = current->mempolicy; - current->mempolicy = NULL; /* fall back to system default policy */ -#endif - error = drv->probe(dev, id); -#ifdef CONFIG_NUMA - set_cpus_allowed_ptr(current, &oldmask); - current->mempolicy = oldpol; -#endif + + get_online_cpus(); + cpu = cpumask_any_and(nodecpumask, cpu_online_mask); + if (cpu < nr_cpu_ids) + error = work_on_cpu(cpu, local_pci_probe, &ddi); + else + error = local_pci_probe(&ddi); + put_online_cpus(); + } else + error = local_pci_probe(&ddi); return error; } From c9ffa5a586a97da4d552f89b8f39eea79a63a612 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 17 Dec 2008 12:07:38 +0900 Subject: [PATCH 63/98] PCI: pciehp: add ACPI based slot detection There is a problem that some non hot-pluggable PCIe slots are detected as hot-pluggable by pciehp on some platforms. The immediate cause of this problem is that hot-plug capable bit in the Slot Capabilities register is set even for non hot-pluggable slots on those platforms. It seems a BIOS/hardware problem, but we need workaround about that. Some of those platforms define hot-pluggable PCIe slots on ACPI namespace properly, while hot-plug capable bit in the Slot Capabilities register is set improperly. So using ACPI namespace information in pciehp to detect PCIe hot-pluggable slots would be a workaround. This patch adds 'pciehp_detect_mode' module option. When 'acpi' is specified, pciehp uses ACPI namespace information to detect PCIe hot-pluggable slots. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/Makefile | 3 + drivers/pci/hotplug/pciehp.h | 15 +++- drivers/pci/hotplug/pciehp_acpi.c | 114 ++++++++++++++++++++++++++++++ drivers/pci/hotplug/pciehp_core.c | 1 + 4 files changed, 132 insertions(+), 1 deletion(-) create mode 100644 drivers/pci/hotplug/pciehp_acpi.c diff --git a/drivers/pci/hotplug/Makefile b/drivers/pci/hotplug/Makefile index 9bdbe1a6688f..e31fb91652ce 100644 --- a/drivers/pci/hotplug/Makefile +++ b/drivers/pci/hotplug/Makefile @@ -55,6 +55,9 @@ pciehp-objs := pciehp_core.o \ pciehp_ctrl.o \ pciehp_pci.o \ pciehp_hpc.o +ifdef CONFIG_ACPI +pciehp-objs += pciehp_acpi.o +endif shpchp-objs := shpchp_core.o \ shpchp_ctrl.o \ diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index b2801a7ee37f..27fd18f019f8 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -220,11 +220,23 @@ struct hpc_ops { #include #include +extern void __init pciehp_acpi_slot_detection_init(void); +extern int pciehp_acpi_slot_detection_check(struct pci_dev *dev); + +static inline void pciehp_firmware_init(void) +{ + pciehp_acpi_slot_detection_init(); +} + static inline int pciehp_get_hp_hw_control_from_firmware(struct pci_dev *dev) { + int retval; u32 flags = (OSC_PCI_EXPRESS_NATIVE_HP_CONTROL | OSC_PCI_EXPRESS_CAP_STRUCTURE_CONTROL); - return acpi_get_hp_hw_control_from_firmware(dev, flags); + retval = acpi_get_hp_hw_control_from_firmware(dev, flags); + if (retval) + return retval; + return pciehp_acpi_slot_detection_check(dev); } static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, @@ -235,6 +247,7 @@ static inline int pciehp_get_hp_params_from_firmware(struct pci_dev *dev, return 0; } #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 */ diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c new file mode 100644 index 000000000000..0cd49b728045 --- /dev/null +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -0,0 +1,114 @@ +/* + * ACPI related functions for PCI Express Hot Plug driver. + * + * Copyright (C) 2008 Kenji Kaneshige + * Copyright (C) 2008 Fujitsu Limited. + * + * 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 "pciehp.h" + +#define PCIEHP_DETECT_PCIE (0) +#define PCIEHP_DETECT_ACPI (1) +#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE + +static int slot_detection_mode; +static char *pciehp_detect_mode; +module_param(pciehp_detect_mode, charp, 0444); +MODULE_PARM_DESC(pciehp_detect_mode, + "Slot detection mode: pcie, acpi\n" + " pcie - Use PCIe based slot detection (default)\n" + " acpi - Use ACPI for slot detection\n"); + +static int is_ejectable(acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + unsigned long long removable; + status = acpi_get_handle(handle, "_ADR", &tmp); + if (ACPI_FAILURE(status)) + return 0; + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_SUCCESS(status)) + return 1; + status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); + if (ACPI_SUCCESS(status) && removable) + return 1; + return 0; +} + +static acpi_status +check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *found = (int *)context; + if (is_ejectable(handle)) { + *found = 1; + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +static int pciehp_detect_acpi_slot(struct pci_bus *pbus) +{ + acpi_handle handle; + struct pci_dev *pdev = pbus->self; + int found = 0; + + if (!pdev){ + int seg = pci_domain_nr(pbus), busnr = pbus->number; + handle = acpi_get_pci_rootbridge_handle(seg, busnr); + } else + handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); + + if (!handle) + return 0; + + acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, + check_hotplug, (void *)&found, NULL); + return found; +} + +int pciehp_acpi_slot_detection_check(struct pci_dev *dev) +{ + if (slot_detection_mode != PCIEHP_DETECT_ACPI) + return 0; + if (pciehp_detect_acpi_slot(dev->subordinate)) + return 0; + return -ENODEV; +} + +static int __init parse_detect_mode(void) +{ + if (!pciehp_detect_mode) + return PCIEHP_DETECT_DEFAULT; + if (!strcmp(pciehp_detect_mode, "pcie")) + return PCIEHP_DETECT_PCIE; + if (!strcmp(pciehp_detect_mode, "acpi")) + return PCIEHP_DETECT_ACPI; + warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", + pciehp_detect_mode); + return PCIEHP_DETECT_DEFAULT; +} + +void __init pciehp_acpi_slot_detection_init(void) +{ + slot_detection_mode = parse_detect_mode(); +} diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 39cf248d24e3..5482d4ed8256 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -522,6 +522,7 @@ static int __init pcied_init(void) { int retval = 0; + pciehp_firmware_init(); retval = pcie_port_service_register(&hpdriver_portdrv); dbg("pcie_port_service_register = %d\n", retval); info(DRIVER_DESC " version: " DRIVER_VERSION "\n"); From e046cbd6c05ee859244245d7beeac395cd0057b3 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 17 Dec 2008 12:08:15 +0900 Subject: [PATCH 64/98] PCI: pciehp: add auto option to pciehp_detect_mode ACPI based hot-pluggable PCIe slot detection logic was added to prevent the problem non hot-pluggable PCIe slot was detected as hot-pluggable. The slot detection logic can be selected through 'pciehp_detect_mode', but it would be better if it is selected automatically. This patch adds 'auto' option for 'pciehp_detect_mode'. When it is specified, pciehp judges which 'acpi' or 'pcie' should be used. It seems that the physical slot number is duplicated among some slots on most of the platforms with the above-mentioned problem. So 'auto' mode uses this information to judge which 'acpi' or 'pcie' should be used. That is, if duplicated physical slot numbers are detected, 'acpi' mode is used. This method is not perfect, but it's realistic. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_acpi.c | 80 +++++++++++++++++++++++++++++-- 1 file changed, 76 insertions(+), 4 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 0cd49b728045..88a5c57f2e5b 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -28,15 +28,18 @@ #define PCIEHP_DETECT_PCIE (0) #define PCIEHP_DETECT_ACPI (1) -#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_PCIE +#define PCIEHP_DETECT_AUTO (2) +#define PCIEHP_DETECT_DEFAULT PCIEHP_DETECT_AUTO static int slot_detection_mode; static char *pciehp_detect_mode; module_param(pciehp_detect_mode, charp, 0444); MODULE_PARM_DESC(pciehp_detect_mode, - "Slot detection mode: pcie, acpi\n" - " pcie - Use PCIe based slot detection (default)\n" - " acpi - Use ACPI for slot detection\n"); + "Slot detection mode: pcie, acpi, auto\n" + " pcie - Use PCIe based slot detection\n" + " acpi - Use ACPI for slot detection\n" + " auto(default) - Auto select mode. Use acpi option if duplicate\n" + " slot ids are found. Otherwise, use pcie option\n"); static int is_ejectable(acpi_handle handle) { @@ -103,12 +106,81 @@ static int __init parse_detect_mode(void) return PCIEHP_DETECT_PCIE; if (!strcmp(pciehp_detect_mode, "acpi")) return PCIEHP_DETECT_ACPI; + if (!strcmp(pciehp_detect_mode, "auto")) + return PCIEHP_DETECT_AUTO; warn("bad specifier '%s' for pciehp_detect_mode. Use default\n", pciehp_detect_mode); return PCIEHP_DETECT_DEFAULT; } +static struct pcie_port_service_id __initdata port_pci_ids[] = { + { + .vendor = PCI_ANY_ID, + .device = PCI_ANY_ID, + .port_type = PCIE_ANY_PORT, + .service_type = PCIE_PORT_SERVICE_HP, + .driver_data = 0, + }, { /* end: all zeroes */ } +}; + +static int __initdata dup_slot_id; +static int __initdata acpi_slot_detected; +static struct list_head __initdata dummy_slots = LIST_HEAD_INIT(dummy_slots); + +/* Dummy driver for dumplicate name detection */ +static int __init dummy_probe(struct pcie_device *dev, + const struct pcie_port_service_id *id) +{ + int pos; + u32 slot_cap; + struct slot *slot, *tmp; + struct pci_dev *pdev = dev->port; + if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) + return -ENOMEM; + /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ + if (pciehp_get_hp_hw_control_from_firmware(pdev)) + return -ENODEV; + if (!(pos = pci_find_capability(pdev, PCI_CAP_ID_EXP))) + return -ENODEV; + pci_read_config_dword(pdev, pos + PCI_EXP_SLTCAP, &slot_cap); + slot->number = slot_cap >> 19; + list_for_each_entry(tmp, &dummy_slots, slot_list) { + if (tmp->number == slot->number) + dup_slot_id++; + } + list_add_tail(&slot->slot_list, &dummy_slots); + if (!acpi_slot_detected && pciehp_detect_acpi_slot(pdev->subordinate)) + acpi_slot_detected = 1; + return -ENODEV; /* dummy driver always returns error */ +} + +static struct pcie_port_service_driver __initdata dummy_driver = { + .name = "pciehp_dummy", + .id_table = port_pci_ids, + .probe = dummy_probe, +}; + +static int __init select_detection_mode(void) +{ + struct slot *slot, *tmp; + pcie_port_service_register(&dummy_driver); + pcie_port_service_unregister(&dummy_driver); + list_for_each_entry_safe(slot, tmp, &dummy_slots, slot_list) { + list_del(&slot->slot_list); + kfree(slot); + } + if (acpi_slot_detected && dup_slot_id) + return PCIEHP_DETECT_ACPI; + return PCIEHP_DETECT_PCIE; +} + void __init pciehp_acpi_slot_detection_init(void) { slot_detection_mode = parse_detect_mode(); + if (slot_detection_mode != PCIEHP_DETECT_AUTO) + goto out; + slot_detection_mode = select_detection_mode(); +out: + if (slot_detection_mode == PCIEHP_DETECT_ACPI) + info("Using ACPI for slot detection.\n"); } From e8c331e963c58b83db24b7d0e39e8c07f687dbc6 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Wed, 17 Dec 2008 12:09:12 +0900 Subject: [PATCH 65/98] PCI hotplug: introduce functions for ACPI slot detection Some ACPI related PCI hotplug code can be shared among PCI hotplug drivers. This patch introduces the following functions in drivers/pci/hotplug/acpi_pcihp.c to share the code, and changes acpiphp and pciehp to use them. - int acpi_pci_detect_ejectable(struct pci_bus *pbus) This checks if the specified PCI bus has ejectable slots. - int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) This checks if the specified handle is ejectable ACPI PCI slot. The 'pbus' parameter is needed to check if 'handle' is PCI related ACPI object. This patch also introduces the following inline function in include/linux/pci-acpi.h, which is useful to get ACPI handle of the PCI bridge from struct pci_bus of the bridge's secondary bus. - static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) This returns ACPI handle of the PCI bridge which generates PCI bus specified by 'pbus'. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpi_pcihp.c | 69 +++++++++++++++++++ drivers/pci/hotplug/acpiphp_glue.c | 107 ++++------------------------- drivers/pci/hotplug/pciehp_acpi.c | 55 ++------------- include/linux/pci-acpi.h | 9 +++ include/linux/pci_hotplug.h | 2 + 5 files changed, 100 insertions(+), 142 deletions(-) diff --git a/drivers/pci/hotplug/acpi_pcihp.c b/drivers/pci/hotplug/acpi_pcihp.c index e17ef54f0efc..c62ab8d240aa 100644 --- a/drivers/pci/hotplug/acpi_pcihp.c +++ b/drivers/pci/hotplug/acpi_pcihp.c @@ -501,5 +501,74 @@ int acpi_root_bridge(acpi_handle handle) } EXPORT_SYMBOL_GPL(acpi_root_bridge); + +static int is_ejectable(acpi_handle handle) +{ + acpi_status status; + acpi_handle tmp; + unsigned long long removable; + status = acpi_get_handle(handle, "_ADR", &tmp); + if (ACPI_FAILURE(status)) + return 0; + status = acpi_get_handle(handle, "_EJ0", &tmp); + if (ACPI_SUCCESS(status)) + return 1; + status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); + if (ACPI_SUCCESS(status) && removable) + return 1; + return 0; +} + +/** + * acpi_pcihp_check_ejectable - check if handle is ejectable ACPI PCI slot + * @pbus: the PCI bus of the PCI slot corresponding to 'handle' + * @handle: ACPI handle to check + * + * Return 1 if handle is ejectable PCI slot, 0 otherwise. + */ +int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle) +{ + acpi_handle bridge_handle, parent_handle; + + if (!(bridge_handle = acpi_pci_get_bridge_handle(pbus))) + return 0; + if ((ACPI_FAILURE(acpi_get_parent(handle, &parent_handle)))) + return 0; + if (bridge_handle != parent_handle) + return 0; + return is_ejectable(handle); +} +EXPORT_SYMBOL_GPL(acpi_pci_check_ejectable); + +static acpi_status +check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) +{ + int *found = (int *)context; + if (is_ejectable(handle)) { + *found = 1; + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +/** + * acpi_pci_detect_ejectable - check if the PCI bus has ejectable slots + * @pbus - 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) +{ + 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, + check_hotplug, (void *)&found, NULL); + return found; +} +EXPORT_SYMBOL_GPL(acpi_pci_detect_ejectable); + module_param(debug_acpi, bool, 0644); MODULE_PARM_DESC(debug_acpi, "Debugging mode for ACPI enabled or not"); diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index 7a5760426897..f09b1010d477 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -46,6 +46,7 @@ #include #include #include +#include #include #include "../pci.h" @@ -62,68 +63,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); - -/* - * initialization & terminatation routines - */ - -/** - * is_ejectable - determine if a slot is ejectable - * @handle: handle to acpi namespace - * - * Ejectable slot should satisfy at least these conditions: - * - * 1. has _ADR method - * 2. has _EJ0 method or _RMV method - * - * optionally - * - * 1. has _STA method - * 2. has _PS0 method - * 3. has _PS3 method - * 4. .. - */ -static int is_ejectable(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - unsigned long long removable; - - status = acpi_get_handle(handle, "_ADR", &tmp); - if (ACPI_FAILURE(status)) - return 0; - - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_SUCCESS(status)) - return 1; - - status = acpi_get_handle(handle, "_RMV", &tmp); - if (ACPI_SUCCESS(status)) { - status = acpi_evaluate_integer(handle, "_RMV", NULL, - &removable); - if (ACPI_SUCCESS(status) && removable) - return 1; - } - - return 0; -} - - -/* callback routine to check for the existence of ejectable slots */ -static acpi_status -is_ejectable_slot(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *count = (int *)context; - - if (is_ejectable(handle)) { - (*count)++; - /* only one ejectable slot is enough */ - return AE_CTRL_TERMINATE; - } else { - return AE_OK; - } -} - /* 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) @@ -138,9 +77,6 @@ is_pci_dock_device(acpi_handle handle, u32 lvl, void *context, void **rv) } } - - - /* * the _DCK method can do funny things... and sometimes not * hah-hah funny. @@ -191,8 +127,9 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) acpi_status status = AE_OK; unsigned long long adr, sun; int device, function, retval; + struct pci_bus *pbus = bridge->pci_bus; - if (!is_ejectable(handle) && !is_dock_device(handle)) + if (!acpi_pci_check_ejectable(pbus, handle) && !is_dock_device(handle)) return AE_OK; acpi_evaluate_integer(handle, "_ADR", NULL, &adr); @@ -258,8 +195,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) bridge->nr_slots++; dbg("found ACPI PCI Hotplug slot %llu at PCI %04x:%02x:%02x\n", - slot->sun, pci_domain_nr(bridge->pci_bus), - bridge->pci_bus->number, slot->device); + slot->sun, pci_domain_nr(pbus), pbus->number, device); retval = acpiphp_register_hotplug_slot(slot); if (retval) { if (retval == -EBUSY) @@ -276,8 +212,7 @@ register_slot(acpi_handle handle, u32 lvl, void *context, void **rv) list_add_tail(&newfunc->sibling, &slot->funcs); /* associate corresponding pci_dev */ - newfunc->pci_dev = pci_get_slot(bridge->pci_bus, - PCI_DEVFN(device, function)); + newfunc->pci_dev = pci_get_slot(pbus, PCI_DEVFN(device, function)); if (newfunc->pci_dev) { slot->flags |= (SLOT_ENABLED | SLOT_POWEREDON); } @@ -326,27 +261,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(acpi_handle *bridge_handle) +static int detect_ejectable_slots(struct pci_bus *pbus) { - acpi_status status; - int count; - - count = 0; - - /* only check slots defined directly below bridge object */ - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, - is_ejectable_slot, (void *)&count, NULL); - - /* - * we also need to add this bridge if there is a dock bridge or - * other pci device on a dock station (removable) - */ - if (!count) - status = acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, - (u32)1, is_pci_dock_device, (void *)&count, - NULL); - - return count; + int found = acpi_pci_detect_ejectable(pbus); + if (!found) { + acpi_handle bridge_handle = acpi_pci_get_bridge_handle(pbus); + acpi_walk_namespace(ACPI_TYPE_DEVICE, bridge_handle, (u32)1, + is_pci_dock_device, (void *)&found, NULL); + } + return found; } @@ -556,7 +479,7 @@ find_p2p_bridge(acpi_handle handle, u32 lvl, void *context, void **rv) goto out; /* check if this bridge has ejectable slots */ - if ((detect_ejectable_slots(handle) > 0)) { + if ((detect_ejectable_slots(dev->subordinate) > 0)) { dbg("found PCI-to-PCI bridge at PCI %s\n", pci_name(dev)); add_p2p_bridge(handle, dev); } @@ -617,7 +540,7 @@ static int add_bridge(acpi_handle handle) } /* check if this bridge has ejectable slots */ - if (detect_ejectable_slots(handle) > 0) { + if (detect_ejectable_slots(pci_bus) > 0) { dbg("found PCI host-bus bridge with hot-pluggable slots\n"); add_host_bridge(handle, pci_bus); } diff --git a/drivers/pci/hotplug/pciehp_acpi.c b/drivers/pci/hotplug/pciehp_acpi.c index 88a5c57f2e5b..438d795f9fe3 100644 --- a/drivers/pci/hotplug/pciehp_acpi.c +++ b/drivers/pci/hotplug/pciehp_acpi.c @@ -24,6 +24,8 @@ */ #include +#include +#include #include "pciehp.h" #define PCIEHP_DETECT_PCIE (0) @@ -41,59 +43,11 @@ MODULE_PARM_DESC(pciehp_detect_mode, " auto(default) - Auto select mode. Use acpi option if duplicate\n" " slot ids are found. Otherwise, use pcie option\n"); -static int is_ejectable(acpi_handle handle) -{ - acpi_status status; - acpi_handle tmp; - unsigned long long removable; - status = acpi_get_handle(handle, "_ADR", &tmp); - if (ACPI_FAILURE(status)) - return 0; - status = acpi_get_handle(handle, "_EJ0", &tmp); - if (ACPI_SUCCESS(status)) - return 1; - status = acpi_evaluate_integer(handle, "_RMV", NULL, &removable); - if (ACPI_SUCCESS(status) && removable) - return 1; - return 0; -} - -static acpi_status -check_hotplug(acpi_handle handle, u32 lvl, void *context, void **rv) -{ - int *found = (int *)context; - if (is_ejectable(handle)) { - *found = 1; - return AE_CTRL_TERMINATE; - } - return AE_OK; -} - -static int pciehp_detect_acpi_slot(struct pci_bus *pbus) -{ - acpi_handle handle; - struct pci_dev *pdev = pbus->self; - int found = 0; - - if (!pdev){ - int seg = pci_domain_nr(pbus), busnr = pbus->number; - handle = acpi_get_pci_rootbridge_handle(seg, busnr); - } else - handle = DEVICE_ACPI_HANDLE(&(pdev->dev)); - - if (!handle) - return 0; - - acpi_walk_namespace(ACPI_TYPE_DEVICE, handle, (u32)1, - check_hotplug, (void *)&found, NULL); - return found; -} - int pciehp_acpi_slot_detection_check(struct pci_dev *dev) { if (slot_detection_mode != PCIEHP_DETECT_ACPI) return 0; - if (pciehp_detect_acpi_slot(dev->subordinate)) + if (acpi_pci_detect_ejectable(dev->subordinate)) return 0; return -ENODEV; } @@ -135,6 +89,7 @@ static int __init dummy_probe(struct pcie_device *dev, u32 slot_cap; struct slot *slot, *tmp; struct pci_dev *pdev = dev->port; + struct pci_bus *pbus = pdev->subordinate; if (!(slot = kzalloc(sizeof(*slot), GFP_KERNEL))) return -ENOMEM; /* Note: pciehp_detect_mode != PCIEHP_DETECT_ACPI here */ @@ -149,7 +104,7 @@ static int __init dummy_probe(struct pcie_device *dev, dup_slot_id++; } list_add_tail(&slot->slot_list, &dummy_slots); - if (!acpi_slot_detected && pciehp_detect_acpi_slot(pdev->subordinate)) + if (!acpi_slot_detected && acpi_pci_detect_ejectable(pbus)) acpi_slot_detected = 1; return -ENODEV; /* dummy driver always returns error */ } diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h index 871e096e0fbc..042c166f65d5 100644 --- a/include/linux/pci-acpi.h +++ b/include/linux/pci-acpi.h @@ -60,6 +60,15 @@ static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev) return acpi_get_pci_rootbridge_handle(pci_domain_nr(pdev->bus), pdev->bus->number); } + +static inline acpi_handle acpi_pci_get_bridge_handle(struct pci_bus *pbus) +{ + int seg = pci_domain_nr(pbus), busnr = pbus->number; + struct pci_dev *bridge = pbus->self; + if (bridge) + return DEVICE_ACPI_HANDLE(&(bridge->dev)); + return acpi_get_pci_rootbridge_handle(seg, busnr); +} #else #if !defined(AE_ERROR) typedef u32 acpi_status; diff --git a/include/linux/pci_hotplug.h b/include/linux/pci_hotplug.h index a00bd1a0f156..f7cc204fab07 100644 --- a/include/linux/pci_hotplug.h +++ b/include/linux/pci_hotplug.h @@ -228,6 +228,8 @@ extern acpi_status acpi_get_hp_params_from_firmware(struct pci_bus *bus, struct hotplug_params *hpp); int acpi_get_hp_hw_control_from_firmware(struct pci_dev *dev, u32 flags); int acpi_root_bridge(acpi_handle handle); +int acpi_pci_check_ejectable(struct pci_bus *pbus, acpi_handle handle); +int acpi_pci_detect_ejectable(struct pci_bus *pbus); #endif #endif From 68feac87de15edfc2c700d2d81b814288c93d003 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:36:55 -0700 Subject: [PATCH 66/98] PCI: add pci_common_swizzle() for INTx swizzling This patch adds pci_common_swizzle(), which swizzles INTx values all the way up to a root bridge. This common implementation can replace several architecture-specific ones. This should someday be combined with pci_get_interrupt_pin(), but I left it separate for now to make reviewing easier. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 20 ++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 21 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 9d2aa6366fd0..c824dc8d617c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1421,6 +1421,26 @@ pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge) return pin; } +/** + * pci_common_swizzle - swizzle INTx all the way to root bridge + * @dev: the PCI device + * @pinp: pointer to the INTx pin value (1=INTA, 2=INTB, 3=INTD, 4=INTD) + * + * Perform INTx swizzling for a device. This traverses through all PCI-to-PCI + * bridges all the way up to a PCI root bus. + */ +u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp) +{ + u8 pin = *pinp; + + while (dev->bus->self) { + pin = pci_swizzle_interrupt_pin(dev, pin); + dev = dev->bus->self; + } + *pinp = pin; + return PCI_SLOT(dev->devfn); +} + /** * pci_release_region - Release a PCI bar * @pdev: PCI device whose resources were previously reserved by pci_request_region diff --git a/include/linux/pci.h b/include/linux/pci.h index da1c22bab40e..170f9ae2d8a0 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -545,6 +545,7 @@ struct resource *pci_find_parent_resource(const struct pci_dev *dev, struct resource *res); u8 pci_swizzle_interrupt_pin(struct pci_dev *dev, u8 pin); int pci_get_interrupt_pin(struct pci_dev *dev, struct pci_dev **bridge); +u8 pci_common_swizzle(struct pci_dev *dev, u8 *pinp); extern struct pci_dev *pci_dev_get(struct pci_dev *dev); extern void pci_dev_put(struct pci_dev *dev); extern void pci_remove_bus(struct pci_bus *b); From 3e08601f7f9b497abcb383491058bfb9e1dc6ec9 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:37:00 -0700 Subject: [PATCH 67/98] PCI: alpha: use generic INTx swizzle from PCI core Use the generic pci_common_swizzle() instead of arch-specific code. Note that pci_common_swizzle() loops based on dev->bus->self, not dev->bus->parent as the alpha common_swizzle() did. I think they are equivalent for this purpose. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/alpha/kernel/pci.c | 18 ------------------ arch/alpha/kernel/pci_impl.h | 4 ++-- 2 files changed, 2 insertions(+), 20 deletions(-) diff --git a/arch/alpha/kernel/pci.c b/arch/alpha/kernel/pci.c index b03d9f420425..a3b938811400 100644 --- a/arch/alpha/kernel/pci.c +++ b/arch/alpha/kernel/pci.c @@ -320,24 +320,6 @@ pcibios_update_irq(struct pci_dev *dev, int irq) pci_write_config_byte(dev, PCI_INTERRUPT_LINE, irq); } -/* Most Alphas have straight-forward swizzling needs. */ - -u8 __init -common_swizzle(struct pci_dev *dev, u8 *pinp) -{ - u8 pin = *pinp; - - while (dev->bus->parent) { - pin = pci_swizzle_interrupt_pin(dev, pin); - /* Move up the chain of bridges. */ - dev = dev->bus->self; - } - *pinp = pin; - - /* The slot is the slot of the last bridge. */ - return PCI_SLOT(dev->devfn); -} - void pcibios_resource_to_bus(struct pci_dev *dev, struct pci_bus_region *region, struct resource *res) diff --git a/arch/alpha/kernel/pci_impl.h b/arch/alpha/kernel/pci_impl.h index 7336651592a3..00edd04b585e 100644 --- a/arch/alpha/kernel/pci_impl.h +++ b/arch/alpha/kernel/pci_impl.h @@ -107,7 +107,7 @@ struct pci_iommu_arena; * Thus, each swizzle is ((pin-1) + (device#-4)) % 4 * * pci_swizzle_interrupt_pin() swizzles for exactly one bridge. The routine - * common_swizzle below handles multiple bridges. But there are a + * pci_common_swizzle() handles multiple bridges. But there are a * couple boards that do strange things. */ @@ -179,7 +179,7 @@ extern int pci_probe_only; extern unsigned long alpha_agpgart_size; extern void common_init_pci(void); -extern u8 common_swizzle(struct pci_dev *, u8 *); +#define common_swizzle pci_common_swizzle extern struct pci_controller *alloc_pci_controller(void); extern struct resource *alloc_resource(void); From 06df69932add8108f599ef26fcdf36c4f8125e50 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:37:05 -0700 Subject: [PATCH 68/98] PCI: arm: use generic INTx swizzle from PCI core Use the generic pci_common_swizzle() instead of arch-specific code. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/arm/include/asm/mach/pci.h | 2 +- arch/arm/kernel/bios32.c | 27 --------------------------- 2 files changed, 1 insertion(+), 28 deletions(-) diff --git a/arch/arm/include/asm/mach/pci.h b/arch/arm/include/asm/mach/pci.h index 32da1ae17e06..a38bdc7afa34 100644 --- a/arch/arm/include/asm/mach/pci.h +++ b/arch/arm/include/asm/mach/pci.h @@ -42,7 +42,7 @@ struct pci_sys_data { /* * This is the standard PCI-PCI bridge swizzling algorithm. */ -u8 pci_std_swizzle(struct pci_dev *dev, u8 *pinp); +#define pci_std_swizzle pci_common_swizzle /* * Call this with your hw_pci struct to initialise the PCI system. diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index 5c5a0bc06709..809681900ec8 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -479,33 +479,6 @@ EXPORT_SYMBOL(pcibios_resource_to_bus); EXPORT_SYMBOL(pcibios_bus_to_resource); #endif -/* - * This is the standard PCI-PCI bridge swizzling algorithm: - * - * Dev: 0 1 2 3 - * A A B C D - * B B C D A - * C C D A B - * D D A B C - * ^^^^^^^^^^ irq pin on bridge - */ -u8 __devinit pci_std_swizzle(struct pci_dev *dev, u8 *pinp) -{ - int pin = *pinp; - - while (dev->bus->self) { - pin = pci_swizzle_interrupt_pin(dev, pin); - /* - * move up the chain of bridges, - * swizzling as we go. - */ - dev = dev->bus->self; - } - *pinp = pin; - - return PCI_SLOT(dev->devfn); -} - /* * Swizzle the device pin each time we cross a bridge. * This might update pin and returns the slot number. From 67eed58060ca2049fd59d35f426b6c7dbd51b20b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:37:10 -0700 Subject: [PATCH 69/98] PCI: mips: use generic INTx swizzle from PCI core Use the generic pci_common_swizzle() instead of arch-specific code. Note that pci_common_swizzle() loops based on dev->bus->self, not dev->bus->parent as the mips common_swizzle() did. I think they are equivalent for this purpose. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/mips/pci/pci.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/arch/mips/pci/pci.c b/arch/mips/pci/pci.c index fa63dc25125d..b0eb9e75c682 100644 --- a/arch/mips/pci/pci.c +++ b/arch/mips/pci/pci.c @@ -149,21 +149,6 @@ out: "Skipping PCI bus scan due to resource conflict\n"); } -static u8 __init common_swizzle(struct pci_dev *dev, u8 *pinp) -{ - u8 pin = *pinp; - - while (dev->bus->parent) { - pin = pci_swizzle_interrupt_pin(dev, pin); - /* Move up the chain of bridges. */ - dev = dev->bus->self; - } - *pinp = pin; - - /* The slot is the slot of the last bridge. */ - return PCI_SLOT(dev->devfn); -} - static int __init pcibios_init(void) { struct pci_controller *hose; @@ -172,7 +157,7 @@ static int __init pcibios_init(void) for (hose = hose_head; hose; hose = hose->next) pcibios_scanbus(hose); - pci_fixup_irqs(common_swizzle, pcibios_map_irq); + pci_fixup_irqs(pci_common_swizzle, pcibios_map_irq); pci_initialized = 1; From e55823492dde22f841ec7deeb4d61f195ecb7ed4 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:37:15 -0700 Subject: [PATCH 70/98] PCI: sh: use generic INTx swizzle from PCI core Use the generic pci_common_swizzle() instead of arch-specific code. Note that pci_common_swizzle() loops based on dev->bus->self, not dev->bus->parent as the sh simple_swizzle() did. I think they are equivalent for this purpose. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/sh/drivers/pci/pci.c | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/arch/sh/drivers/pci/pci.c b/arch/sh/drivers/pci/pci.c index aea3b6b3033a..e36c7b870861 100644 --- a/arch/sh/drivers/pci/pci.c +++ b/arch/sh/drivers/pci/pci.c @@ -21,21 +21,6 @@ #include #include -static u8 __init simple_swizzle(struct pci_dev *dev, u8 *pinp) -{ - u8 pin = *pinp; - - while (dev->bus->parent) { - pin = pci_swizzle_interrupt_pin(dev, pin); - /* Move up the chain of bridges. */ - dev = dev->bus->self; - } - *pinp = pin; - - /* The slot is the slot of the last bridge. */ - return PCI_SLOT(dev->devfn); -} - static int __init pcibios_init(void) { struct pci_channel *p; @@ -56,7 +41,7 @@ static int __init pcibios_init(void) busno = bus->subordinate + 1; } - pci_fixup_irqs(simple_swizzle, pcibios_map_platform_irq); + pci_fixup_irqs(pci_common_swizzle, pcibios_map_platform_irq); return 0; } From 904d6a303361a85bfa4c8181ef62a24edb8da0a8 Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Tue, 16 Dec 2008 21:37:20 -0700 Subject: [PATCH 71/98] PCI: x86/visws: use generic INTx swizzle from PCI core Use the generic pci_common_swizzle() instead of arch-specific code. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/visws.c | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/arch/x86/pci/visws.c b/arch/x86/pci/visws.c index 2c54e7e03f53..bcead7a46871 100644 --- a/arch/x86/pci/visws.c +++ b/arch/x86/pci/visws.c @@ -24,19 +24,6 @@ static void pci_visws_disable_irq(struct pci_dev *dev) { } unsigned int pci_bus0, pci_bus1; -static u8 __init visws_swizzle(struct pci_dev *dev, u8 *pinp) -{ - u8 pin = *pinp; - - while (dev->bus->self) { /* Move up the chain of bridges. */ - pin = pci_swizzle_interrupt_pin(dev, pin); - dev = dev->bus->self; - } - *pinp = pin; - - return PCI_SLOT(dev->devfn); -} - static int __init visws_map_irq(struct pci_dev *dev, u8 slot, u8 pin) { int irq, bus = dev->bus->number; @@ -101,7 +88,7 @@ int __init pci_visws_init(void) raw_pci_ops = &pci_direct_conf1; pci_scan_bus_with_sysdata(pci_bus0); pci_scan_bus_with_sysdata(pci_bus1); - pci_fixup_irqs(visws_swizzle, visws_map_irq); + pci_fixup_irqs(pci_common_swizzle, visws_map_irq); pcibios_resource_survey(); return 0; } From 1120f8b8169fb2cb51219d326892d963e762edb6 Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 18 Dec 2008 09:17:16 -0800 Subject: [PATCH 72/98] PCI: handle long delays in VPD access Accessing the VPD area can take a long time. The existing VPD access code fails consistently on my hardware. There are comments in the SysKonnect vendor driver that it can take up to 13ms per word. Change the access routines to: * use a mutex rather than spinning with IRQ's disabled and lock held * have a much longer timeout * call cond_resched while spinning Signed-off-by: Stephen Hemminger Reviewed-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 55 ++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 39bb96b413ef..98ddba94b5b9 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -133,39 +133,46 @@ PCI_USER_WRITE_CONFIG(dword, u32) struct pci_vpd_pci22 { struct pci_vpd base; - spinlock_t lock; /* controls access to hardware and the flags */ - u8 cap; + struct mutex lock; + u16 flag; bool busy; - bool flag; /* value of F bit to wait for */ + u8 cap; }; -/* Wait for last operation to complete */ +/* + * Wait for last operation to complete. + * This code has to spin since there is no other notification from the PCI + * hardware. Since the VPD is often implemented by serial attachment to an + * EEPROM, it may take many milliseconds to complete. + */ static int pci_vpd_pci22_wait(struct pci_dev *dev) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u16 flag, status; - int wait; + unsigned long timeout = jiffies + HZ/20 + 2; + u16 status; int ret; if (!vpd->busy) return 0; - flag = vpd->flag ? PCI_VPD_ADDR_F : 0; - wait = vpd->flag ? 10 : 1000; /* read: 100 us; write: 10 ms */ for (;;) { - ret = pci_user_read_config_word(dev, - vpd->cap + PCI_VPD_ADDR, + ret = pci_user_read_config_word(dev, vpd->cap + PCI_VPD_ADDR, &status); - if (ret < 0) + if (ret) return ret; - if ((status & PCI_VPD_ADDR_F) == flag) { + + if ((status & PCI_VPD_ADDR_F) == vpd->flag) { vpd->busy = false; return 0; } - if (wait-- == 0) + + if (time_after(jiffies, timeout)) return -ETIMEDOUT; - udelay(10); + if (fatal_signal_pending(current)) + return -EINTR; + if (!cond_resched()) + udelay(10); } } @@ -175,7 +182,7 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); u32 val; - int ret; + int ret = 0; int begin, end, i; if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) @@ -183,7 +190,9 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, if (size == 0) return 0; - spin_lock_irq(&vpd->lock); + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; + ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; @@ -191,15 +200,16 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, pos & ~3); if (ret < 0) goto out; + vpd->busy = true; - vpd->flag = 1; + vpd->flag = PCI_VPD_ADDR_F; ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); out: - spin_unlock_irq(&vpd->lock); + mutex_unlock(&vpd->lock); if (ret < 0) return ret; @@ -220,7 +230,7 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); u32 val; - int ret; + int ret = 0; if (pos < 0 || pos > vpd->base.len || pos & 3 || size > vpd->base.len - pos || size < 4) @@ -231,7 +241,8 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, val |= ((u8) *buf++) << 16; val |= ((u32)(u8) *buf++) << 24; - spin_lock_irq(&vpd->lock); + if (mutex_lock_killable(&vpd->lock)) + return -EINTR; ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; @@ -247,7 +258,7 @@ static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, vpd->flag = 0; ret = pci_vpd_pci22_wait(dev); out: - spin_unlock_irq(&vpd->lock); + mutex_unlock(&vpd->lock); if (ret < 0) return ret; @@ -279,7 +290,7 @@ int pci_vpd_pci22_init(struct pci_dev *dev) vpd->base.len = PCI_VPD_PCI22_SIZE; vpd->base.ops = &pci_vpd_pci22_ops; - spin_lock_init(&vpd->lock); + mutex_init(&vpd->lock); vpd->cap = cap; vpd->busy = false; dev->vpd = &vpd->base; From 287d19ce2e67c15e79a187b3bdcbbea1a0a51a7d Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 18 Dec 2008 09:17:16 -0800 Subject: [PATCH 73/98] PCI: revise VPD access interface Change PCI VPD API which was only used by sysfs to something usable in drivers. * move iteration over multiple words to the low level * use conventional types for arguments * add exportable wrapper Signed-off-by: Stephen Hemminger Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 156 +++++++++++++++++++++++++--------------- drivers/pci/pci-sysfs.c | 58 +++++---------- drivers/pci/pci.h | 6 +- include/linux/pci.h | 4 ++ 4 files changed, 124 insertions(+), 100 deletions(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 98ddba94b5b9..86ec4ad44bcd 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -66,6 +66,39 @@ EXPORT_SYMBOL(pci_bus_write_config_byte); EXPORT_SYMBOL(pci_bus_write_config_word); EXPORT_SYMBOL(pci_bus_write_config_dword); + +/** + * pci_read_vpd - Read one entry from Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @buf: pointer to where to store result + * + */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->read(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_read_vpd); + +/** + * pci_write_vpd - Write entry to Vital Product Data + * @dev: pci device struct + * @pos: offset in vpd space + * @count: number of bytes to read + * @val: value to write + * + */ +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf) +{ + if (!dev->vpd || !dev->vpd->ops) + return -ENODEV; + return dev->vpd->ops->write(dev, pos, count, buf); +} +EXPORT_SYMBOL(pci_write_vpd); + /* * The following routines are to prevent the user from accessing PCI config * space when it's unsafe to do so. Some devices require this during BIST and @@ -176,19 +209,17 @@ static int pci_vpd_pci22_wait(struct pci_dev *dev) } } -static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, - char *buf) +static ssize_t pci_vpd_pci22_read(struct pci_dev *dev, loff_t pos, size_t count, + void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u32 val; - int ret = 0; - int begin, end, i; + int ret; + loff_t end = pos + count; + u8 *buf = arg; - if (pos < 0 || pos > vpd->base.len || size > vpd->base.len - pos) + if (pos < 0 || pos > vpd->base.len || end > vpd->base.len) return -EINVAL; - if (size == 0) - return 0; if (mutex_lock_killable(&vpd->lock)) return -EINTR; @@ -196,73 +227,84 @@ static int pci_vpd_pci22_read(struct pci_dev *dev, int pos, int size, ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos & ~3); - if (ret < 0) - goto out; - vpd->busy = true; - vpd->flag = PCI_VPD_ADDR_F; - ret = pci_vpd_pci22_wait(dev); - if (ret < 0) - goto out; - ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, - &val); + while (pos < end) { + u32 val; + unsigned int i, skip; + + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos & ~3); + if (ret < 0) + break; + vpd->busy = true; + vpd->flag = PCI_VPD_ADDR_F; + ret = pci_vpd_pci22_wait(dev); + if (ret < 0) + break; + + ret = pci_user_read_config_dword(dev, vpd->cap + PCI_VPD_DATA, &val); + if (ret < 0) + break; + + skip = pos & 3; + for (i = 0; i < sizeof(u32); i++) { + if (i >= skip) { + *buf++ = val; + if (++pos == end) + break; + } + val >>= 8; + } + } out: mutex_unlock(&vpd->lock); - if (ret < 0) - return ret; - - /* Convert to bytes */ - begin = pos & 3; - end = min(4, begin + size); - for (i = 0; i < end; ++i) { - if (i >= begin) - *buf++ = val; - val >>= 8; - } - return end - begin; + return ret ? ret : count; } -static int pci_vpd_pci22_write(struct pci_dev *dev, int pos, int size, - const char *buf) +static ssize_t pci_vpd_pci22_write(struct pci_dev *dev, loff_t pos, size_t count, + const void *arg) { struct pci_vpd_pci22 *vpd = container_of(dev->vpd, struct pci_vpd_pci22, base); - u32 val; + const u8 *buf = arg; + loff_t end = pos + count; int ret = 0; - if (pos < 0 || pos > vpd->base.len || pos & 3 || - size > vpd->base.len - pos || size < 4) + if (pos < 0 || (pos & 3) || (count & 3) || end > vpd->base.len) return -EINVAL; - val = (u8) *buf++; - val |= ((u8) *buf++) << 8; - val |= ((u8) *buf++) << 16; - val |= ((u32)(u8) *buf++) << 24; - if (mutex_lock_killable(&vpd->lock)) return -EINTR; + ret = pci_vpd_pci22_wait(dev); if (ret < 0) goto out; - ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, - val); - if (ret < 0) - goto out; - ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, - pos | PCI_VPD_ADDR_F); - if (ret < 0) - goto out; - vpd->busy = true; - vpd->flag = 0; - ret = pci_vpd_pci22_wait(dev); + + while (pos < end) { + u32 val; + + val = *buf++; + val |= *buf++ << 8; + val |= *buf++ << 16; + val |= *buf++ << 24; + + ret = pci_user_write_config_dword(dev, vpd->cap + PCI_VPD_DATA, val); + if (ret < 0) + break; + ret = pci_user_write_config_word(dev, vpd->cap + PCI_VPD_ADDR, + pos | PCI_VPD_ADDR_F); + if (ret < 0) + break; + + vpd->busy = true; + vpd->flag = 0; + ret = pci_vpd_pci22_wait(dev); + + pos += sizeof(u32); + } out: mutex_unlock(&vpd->lock); - if (ret < 0) - return ret; - - return 4; + return ret ? ret : count; } static void pci_vpd_pci22_release(struct pci_dev *dev) @@ -270,7 +312,7 @@ static void pci_vpd_pci22_release(struct pci_dev *dev) kfree(container_of(dev->vpd, struct pci_vpd_pci22, base)); } -static struct pci_vpd_ops pci_vpd_pci22_ops = { +static const struct pci_vpd_ops pci_vpd_pci22_ops = { .read = pci_vpd_pci22_read, .write = pci_vpd_pci22_write, .release = pci_vpd_pci22_release, diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index ea54cedcdfc6..c23619fb6c4b 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -371,55 +371,33 @@ pci_write_config(struct kobject *kobj, struct bin_attribute *bin_attr, } static ssize_t -pci_read_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, - char *buf, loff_t off, size_t count) -{ - struct pci_dev *dev = - to_pci_dev(container_of(kobj, struct device, kobj)); - int end; - int ret; - - if (off > bin_attr->size) - count = 0; - else if (count > bin_attr->size - off) - count = bin_attr->size - off; - end = off + count; - - while (off < end) { - ret = dev->vpd->ops->read(dev, off, end - off, buf); - if (ret < 0) - return ret; - buf += ret; - off += ret; - } - - return count; -} - -static ssize_t -pci_write_vpd(struct kobject *kobj, struct bin_attribute *bin_attr, +read_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) { struct pci_dev *dev = to_pci_dev(container_of(kobj, struct device, kobj)); - int end; - int ret; if (off > bin_attr->size) count = 0; else if (count > bin_attr->size - off) count = bin_attr->size - off; - end = off + count; - while (off < end) { - ret = dev->vpd->ops->write(dev, off, end - off, buf); - if (ret < 0) - return ret; - buf += ret; - off += ret; - } + return pci_read_vpd(dev, off, count, buf); +} - return count; +static ssize_t +write_vpd_attr(struct kobject *kobj, struct bin_attribute *bin_attr, + char *buf, loff_t off, size_t count) +{ + struct pci_dev *dev = + to_pci_dev(container_of(kobj, struct device, kobj)); + + if (off > bin_attr->size) + count = 0; + else if (count > bin_attr->size - off) + count = bin_attr->size - off; + + return pci_write_vpd(dev, off, count, buf); } #ifdef HAVE_PCI_LEGACY @@ -845,8 +823,8 @@ static int pci_create_capabilities_sysfs(struct pci_dev *dev) attr->size = dev->vpd->len; attr->attr.name = "vpd"; attr->attr.mode = S_IRUSR | S_IWUSR; - attr->read = pci_read_vpd; - attr->write = pci_write_vpd; + attr->read = read_vpd_attr; + attr->write = write_vpd_attr; retval = sysfs_create_bin_file(&dev->dev.kobj, attr); if (retval) { kfree(dev->vpd->attr); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 65deed8bfc06..211fd418f48f 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -56,14 +56,14 @@ extern int pci_user_write_config_word(struct pci_dev *dev, int where, u16 val); extern int pci_user_write_config_dword(struct pci_dev *dev, int where, u32 val); struct pci_vpd_ops { - int (*read)(struct pci_dev *dev, int pos, int size, char *buf); - int (*write)(struct pci_dev *dev, int pos, int size, const char *buf); + ssize_t (*read)(struct pci_dev *dev, loff_t pos, size_t count, void *buf); + ssize_t (*write)(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); void (*release)(struct pci_dev *dev); }; struct pci_vpd { unsigned int len; - struct pci_vpd_ops *ops; + const struct pci_vpd_ops *ops; struct bin_attribute *attr; /* descriptor for sysfs VPD entry */ }; diff --git a/include/linux/pci.h b/include/linux/pci.h index 170f9ae2d8a0..76079e106895 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -687,6 +687,10 @@ int pci_back_from_sleep(struct pci_dev *dev); /* Functions for PCI Hotplug drivers to use */ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); +/* Vital product data routines */ +ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); +ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); + /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ void pci_bus_assign_resources(struct pci_bus *bus); void pci_bus_size_bridges(struct pci_bus *bus); From db5679437a2b938c9127480a3923633721583a4f Mon Sep 17 00:00:00 2001 From: Stephen Hemminger Date: Thu, 18 Dec 2008 09:17:16 -0800 Subject: [PATCH 74/98] PCI: add interface to set visible size of VPD The VPD on all devices may not be 32K. Unfortunately, there is no generic way to find the size, so this adds a simple API hook to reset it. Signed-off-by: Stephen Hemminger Signed-off-by: Jesse Barnes --- drivers/pci/access.c | 23 +++++++++++++++++++++++ include/linux/pci.h | 1 + 2 files changed, 24 insertions(+) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index 86ec4ad44bcd..381444794778 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -339,6 +339,29 @@ int pci_vpd_pci22_init(struct pci_dev *dev) return 0; } +/** + * pci_vpd_truncate - Set available Vital Product Data size + * @dev: pci device struct + * @size: available memory in bytes + * + * Adjust size of available VPD area. + */ +int pci_vpd_truncate(struct pci_dev *dev, size_t size) +{ + if (!dev->vpd) + return -EINVAL; + + /* limited by the access method */ + if (size > dev->vpd->len) + return -EINVAL; + + dev->vpd->len = size; + dev->vpd->attr->size = size; + + return 0; +} +EXPORT_SYMBOL(pci_vpd_truncate); + /** * pci_block_user_cfg_access - Block userspace PCI config reads/writes * @dev: pci device struct diff --git a/include/linux/pci.h b/include/linux/pci.h index 76079e106895..7cbecef19bb6 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -690,6 +690,7 @@ int pci_bus_find_capability(struct pci_bus *bus, unsigned int devfn, int cap); /* Vital product data routines */ ssize_t pci_read_vpd(struct pci_dev *dev, loff_t pos, size_t count, void *buf); ssize_t pci_write_vpd(struct pci_dev *dev, loff_t pos, size_t count, const void *buf); +int pci_vpd_truncate(struct pci_dev *dev, size_t size); /* Helper functions for low-level code (drivers/pci/setup-[bus,res].c) */ void pci_bus_assign_resources(struct pci_bus *bus); From a19f5df7d9696b9e53ba7c865816597057d5f76e Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 18 Dec 2008 16:34:19 -0700 Subject: [PATCH 75/98] PCI: use dev_printk for PCI bus resource mssages Since pci_bus has a struct device, use dev_printk directly instead of faking it by hand. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- drivers/pci/setup-bus.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 09e2c3cd2bef..704608945780 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -536,9 +536,8 @@ static void pci_bus_dump_res(struct pci_bus *bus) if (!res) continue; - printk(KERN_DEBUG "pci %04x:%02x: bus resource %d %s %pR\n", - pci_domain_nr(bus), bus->number, i, - (res->flags & IORESOURCE_IO) ? "io: " : "mem:", res); + dev_printk(KERN_DEBUG, &bus->dev, "resource %d %s %pR\n", i, + (res->flags & IORESOURCE_IO) ? "io: " : "mem:", res); } } From 2b8c2efe44ed897fc958131d70addc89876d806b Mon Sep 17 00:00:00 2001 From: Bjorn Helgaas Date: Thu, 18 Dec 2008 16:34:51 -0700 Subject: [PATCH 76/98] x86/PCI: use dev_printk for PCI bus locality messages Since pci_bus has a struct device, use dev_printk directly instead of faking it by hand. Signed-off-by: Bjorn Helgaas Signed-off-by: Jesse Barnes --- arch/x86/pci/acpi.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 6c7683709c44..c0ecf250fe51 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -210,12 +210,10 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_device *device, int do if (bus && node != -1) { #ifdef CONFIG_ACPI_NUMA if (pxm >= 0) - printk(KERN_DEBUG - "pci %04x:%02x: bus on NUMA node %d (pxm %d)\n", - domain, busnum, node, pxm); + dev_printk(KERN_DEBUG, &bus->dev, + "on NUMA node %d (pxm %d)\n", node, pxm); #else - printk(KERN_DEBUG "pci %04x:%02x: bus on NUMA node %d\n", - domain, busnum, node); + dev_printk(KERN_DEBUG, &bus->dev, "on NUMA node %d\n", node); #endif } From 46bbdfa44cfc0d352148a0dc33ba9f6db02ccdf0 Mon Sep 17 00:00:00 2001 From: Shaohua Li Date: Fri, 19 Dec 2008 09:27:42 +0800 Subject: [PATCH 77/98] PCI: keep ASPM link state consistent throughout PCIe hierarchy In a PCIe hierarchy with a switch present, if the link state of an endpoint device is changed, we must check the whole hierarchy from the endpoint device to root port, and for each link in the hierarchy, the new link state should be configured. Previously, the implementation checked the state but forgot to configure the links between root port to switch. Fixes Novell bz #448987. Signed-off-by: Shaohua Li Tested-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 129 +++++++++++++++++++++++++++++++++------- 1 file changed, 108 insertions(+), 21 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index e361c7dc726f..4d8e2c7b2ad1 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -33,6 +33,11 @@ struct endpoint_state { struct pcie_link_state { struct list_head sibiling; struct pci_dev *pdev; + bool downstream_has_switch; + + struct pcie_link_state *parent; + struct list_head children; + struct list_head link; /* ASPM state */ unsigned int support_state; @@ -125,7 +130,7 @@ static void pcie_set_clock_pm(struct pci_dev *pdev, int enable) link_state->clk_pm_enabled = !!enable; } -static void pcie_check_clock_pm(struct pci_dev *pdev) +static void pcie_check_clock_pm(struct pci_dev *pdev, int blacklist) { int pos; u32 reg32; @@ -149,10 +154,26 @@ static void pcie_check_clock_pm(struct pci_dev *pdev) if (!(reg16 & PCI_EXP_LNKCTL_CLKREQ_EN)) enabled = 0; } - link_state->clk_pm_capable = capable; link_state->clk_pm_enabled = enabled; link_state->bios_clk_state = enabled; - pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); + if (!blacklist) { + link_state->clk_pm_capable = capable; + pcie_set_clock_pm(pdev, policy_to_clkpm_state(pdev)); + } else { + link_state->clk_pm_capable = 0; + pcie_set_clock_pm(pdev, 0); + } +} + +static bool pcie_aspm_downstream_has_switch(struct pci_dev *pdev) +{ + struct pci_dev *child_dev; + + list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { + if (child_dev->pcie_type == PCI_EXP_TYPE_UPSTREAM) + return true; + } + return false; } /* @@ -419,9 +440,9 @@ static unsigned int pcie_aspm_check_state(struct pci_dev *pdev, { struct pci_dev *child_dev; - /* If no child, disable the link */ + /* If no child, ignore the link */ if (list_empty(&pdev->subordinate->devices)) - return 0; + return state; list_for_each_entry(child_dev, &pdev->subordinate->devices, bus_list) { if (child_dev->pcie_type == PCI_EXP_TYPE_PCI_BRIDGE) { /* @@ -462,6 +483,9 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state) int valid = 1; struct pcie_link_state *link_state = pdev->link_state; + /* If no child, disable the link */ + if (list_empty(&pdev->subordinate->devices)) + state = 0; /* * if the downstream component has pci bridge function, don't do ASPM * now @@ -493,20 +517,52 @@ static void __pcie_aspm_config_link(struct pci_dev *pdev, unsigned int state) link_state->enabled_state = state; } +static struct pcie_link_state *get_root_port_link(struct pcie_link_state *link) +{ + struct pcie_link_state *root_port_link = link; + while (root_port_link->parent) + root_port_link = root_port_link->parent; + return root_port_link; +} + +/* check the whole hierarchy, and configure each link in the hierarchy */ static void __pcie_aspm_configure_link_state(struct pci_dev *pdev, unsigned int state) { struct pcie_link_state *link_state = pdev->link_state; + struct pcie_link_state *root_port_link = get_root_port_link(link_state); + struct pcie_link_state *leaf; - if (link_state->support_state == 0) - return; state &= PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; - /* state 0 means disabling aspm */ - state = pcie_aspm_check_state(pdev, state); + /* check all links who have specific root port link */ + list_for_each_entry(leaf, &link_list, sibiling) { + if (!list_empty(&leaf->children) || + get_root_port_link(leaf) != root_port_link) + continue; + state = pcie_aspm_check_state(leaf->pdev, state); + } + /* check root port link too in case it hasn't children */ + state = pcie_aspm_check_state(root_port_link->pdev, state); + if (link_state->enabled_state == state) return; - __pcie_aspm_config_link(pdev, state); + + /* + * 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, sibiling) { + if (get_root_port_link(leaf) == root_port_link) + __pcie_aspm_config_link(leaf->pdev, state); + } + } else { + list_for_each_entry_reverse(leaf, &link_list, sibiling) { + if (get_root_port_link(leaf) == root_port_link) + __pcie_aspm_config_link(leaf->pdev, state); + } + } } /* @@ -570,6 +626,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) unsigned int state; struct pcie_link_state *link_state; int error = 0; + int blacklist; if (aspm_disabled || !pdev->is_pcie || pdev->link_state) return; @@ -580,29 +637,58 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) if (list_empty(&pdev->subordinate->devices)) goto out; - if (pcie_aspm_sanity_check(pdev)) - goto out; + blacklist = !!pcie_aspm_sanity_check(pdev); mutex_lock(&aspm_lock); link_state = kzalloc(sizeof(*link_state), GFP_KERNEL); if (!link_state) goto unlock_out; + + link_state->downstream_has_switch = pcie_aspm_downstream_has_switch(pdev); + INIT_LIST_HEAD(&link_state->children); + INIT_LIST_HEAD(&link_state->link); + if (pdev->bus->self) {/* this is a switch */ + struct pcie_link_state *parent_link_state; + + parent_link_state = pdev->bus->parent->self->link_state; + if (!parent_link_state) { + kfree(link_state); + goto unlock_out; + } + list_add(&link_state->link, &parent_link_state->children); + link_state->parent = parent_link_state; + } + pdev->link_state = link_state; - pcie_aspm_configure_common_clock(pdev); - - pcie_aspm_cap_init(pdev); - - /* config link state to avoid BIOS error */ - state = pcie_aspm_check_state(pdev, policy_to_aspm_state(pdev)); - __pcie_aspm_config_link(pdev, state); - - pcie_check_clock_pm(pdev); + if (!blacklist) { + pcie_aspm_configure_common_clock(pdev); + pcie_aspm_cap_init(pdev); + } else { + link_state->enabled_state = PCIE_LINK_STATE_L0S|PCIE_LINK_STATE_L1; + link_state->bios_aspm_state = 0; + /* Set support state to 0, so we will disable ASPM later */ + link_state->support_state = 0; + } link_state->pdev = pdev; list_add(&link_state->sibiling, &link_list); + if (link_state->downstream_has_switch) { + /* + * 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 + **/ + state = pcie_aspm_check_state(pdev, link_state->bios_aspm_state); + __pcie_aspm_config_link(pdev, state); + } else + __pcie_aspm_configure_link_state(pdev, + policy_to_aspm_state(pdev)); + + pcie_check_clock_pm(pdev, blacklist); + unlock_out: if (error) free_link_state(pdev); @@ -635,6 +721,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) /* All functions are removed, so just disable ASPM for the link */ __pcie_aspm_config_one_dev(parent, 0); list_del(&link_state->sibiling); + list_del(&link_state->link); /* Clock PM is for endpoint device */ free_link_state(parent); From 67f6533802fd2cc6f5b3c6355ef72bcf636d7fda Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Fri, 19 Dec 2008 15:18:10 +0900 Subject: [PATCH 78/98] PCI: pciehp: ignore undefined bit in link status register Bit 10 in Link Status register used to be defined as Training Error in the PCI Express 1.0a specification. But it was removed by Training Error ECN and is no longer defined. So pciehp must ignore the value read from it. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_hpc.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index b643ca13e4f1..22b88cb17a07 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -419,8 +419,7 @@ static int hpc_check_lnk_status(struct controller *ctrl) } ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); - if ( (lnk_status & LNK_TRN) || (lnk_status & LNK_TRN_ERR) || - !(lnk_status & NEG_LINK_WD)) { + if ((lnk_status & LNK_TRN) || !(lnk_status & NEG_LINK_WD)) { ctrl_err(ctrl, "Link Training Error occurs \n"); retval = -1; return retval; From 322162a71bd9fc4edb1b11236e7bc8aa27ccac22 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Fri, 19 Dec 2008 15:19:02 +0900 Subject: [PATCH 79/98] PCI: pciehp: cleanup register and field definitions Clean up register definitions related to PCI Express Hot plug. - Add register definitions into include/linux/pci_regs.h, and use them instead of pciehp's locally definied register definitions. - Remove pciehp's locally defined register definitions - Remove unused register definitions in pciehp. - Some minor cleanups. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_hpc.c | 328 ++++++++++--------------------- include/linux/pci_regs.h | 64 +++++- 2 files changed, 156 insertions(+), 236 deletions(-) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 22b88cb17a07..71a8012886b0 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -42,42 +42,6 @@ static atomic_t pciehp_num_controllers = ATOMIC_INIT(0); -struct ctrl_reg { - u8 cap_id; - u8 nxt_ptr; - u16 cap_reg; - u32 dev_cap; - u16 dev_ctrl; - u16 dev_status; - u32 lnk_cap; - u16 lnk_ctrl; - u16 lnk_status; - u32 slot_cap; - u16 slot_ctrl; - u16 slot_status; - u16 root_ctrl; - u16 rsvp; - u32 root_status; -} __attribute__ ((packed)); - -/* offsets to the controller registers based on the above structure layout */ -enum ctrl_offsets { - PCIECAPID = offsetof(struct ctrl_reg, cap_id), - NXTCAPPTR = offsetof(struct ctrl_reg, nxt_ptr), - CAPREG = offsetof(struct ctrl_reg, cap_reg), - DEVCAP = offsetof(struct ctrl_reg, dev_cap), - DEVCTRL = offsetof(struct ctrl_reg, dev_ctrl), - DEVSTATUS = offsetof(struct ctrl_reg, dev_status), - LNKCAP = offsetof(struct ctrl_reg, lnk_cap), - LNKCTRL = offsetof(struct ctrl_reg, lnk_ctrl), - LNKSTATUS = offsetof(struct ctrl_reg, lnk_status), - SLOTCAP = offsetof(struct ctrl_reg, slot_cap), - SLOTCTRL = offsetof(struct ctrl_reg, slot_ctrl), - SLOTSTATUS = offsetof(struct ctrl_reg, slot_status), - ROOTCTRL = offsetof(struct ctrl_reg, root_ctrl), - ROOTSTATUS = offsetof(struct ctrl_reg, root_status), -}; - static inline int pciehp_readw(struct controller *ctrl, int reg, u16 *value) { struct pci_dev *dev = ctrl->pci_dev; @@ -102,95 +66,9 @@ static inline int pciehp_writel(struct controller *ctrl, int reg, u32 value) return pci_write_config_dword(dev, ctrl->cap_base + reg, value); } -/* Field definitions in PCI Express Capabilities Register */ -#define CAP_VER 0x000F -#define DEV_PORT_TYPE 0x00F0 -#define SLOT_IMPL 0x0100 -#define MSG_NUM 0x3E00 - -/* Device or Port Type */ -#define NAT_ENDPT 0x00 -#define LEG_ENDPT 0x01 -#define ROOT_PORT 0x04 -#define UP_STREAM 0x05 -#define DN_STREAM 0x06 -#define PCIE_PCI_BRDG 0x07 -#define PCI_PCIE_BRDG 0x10 - -/* Field definitions in Device Capabilities Register */ -#define DATTN_BUTTN_PRSN 0x1000 -#define DATTN_LED_PRSN 0x2000 -#define DPWR_LED_PRSN 0x4000 - -/* Field definitions in Link Capabilities Register */ -#define MAX_LNK_SPEED 0x000F -#define MAX_LNK_WIDTH 0x03F0 -#define LINK_ACTIVE_REPORTING 0x00100000 - -/* Link Width Encoding */ -#define LNK_X1 0x01 -#define LNK_X2 0x02 -#define LNK_X4 0x04 -#define LNK_X8 0x08 -#define LNK_X12 0x0C -#define LNK_X16 0x10 -#define LNK_X32 0x20 - -/*Field definitions of Link Status Register */ -#define LNK_SPEED 0x000F -#define NEG_LINK_WD 0x03F0 -#define LNK_TRN_ERR 0x0400 -#define LNK_TRN 0x0800 -#define SLOT_CLK_CONF 0x1000 -#define LINK_ACTIVE 0x2000 - -/* Field definitions in Slot Capabilities Register */ -#define ATTN_BUTTN_PRSN 0x00000001 -#define PWR_CTRL_PRSN 0x00000002 -#define MRL_SENS_PRSN 0x00000004 -#define ATTN_LED_PRSN 0x00000008 -#define PWR_LED_PRSN 0x00000010 -#define HP_SUPR_RM_SUP 0x00000020 -#define HP_CAP 0x00000040 -#define SLOT_PWR_VALUE 0x000003F8 -#define SLOT_PWR_LIMIT 0x00000C00 -#define PSN 0xFFF80000 /* PSN: Physical Slot Number */ - -/* Field definitions in Slot Control Register */ -#define ATTN_BUTTN_ENABLE 0x0001 -#define PWR_FAULT_DETECT_ENABLE 0x0002 -#define MRL_DETECT_ENABLE 0x0004 -#define PRSN_DETECT_ENABLE 0x0008 -#define CMD_CMPL_INTR_ENABLE 0x0010 -#define HP_INTR_ENABLE 0x0020 -#define ATTN_LED_CTRL 0x00C0 -#define PWR_LED_CTRL 0x0300 -#define PWR_CTRL 0x0400 -#define EMI_CTRL 0x0800 - -/* Attention indicator and Power indicator states */ -#define LED_ON 0x01 -#define LED_BLINK 0x10 -#define LED_OFF 0x11 - /* Power Control Command */ #define POWER_ON 0 -#define POWER_OFF 0x0400 - -/* EMI Status defines */ -#define EMI_DISENGAGED 0 -#define EMI_ENGAGED 1 - -/* Field definitions in Slot Status Register */ -#define ATTN_BUTTN_PRESSED 0x0001 -#define PWR_FAULT_DETECTED 0x0002 -#define MRL_SENS_CHANGED 0x0004 -#define PRSN_DETECT_CHANGED 0x0008 -#define CMD_COMPLETED 0x0010 -#define MRL_STATE 0x0020 -#define PRSN_STATE 0x0040 -#define EMI_STATE 0x0080 -#define EMI_STATUS_BIT 7 +#define POWER_OFF PCI_EXP_SLTCTL_PCC static irqreturn_t pcie_isr(int irq, void *dev_id); static void start_int_poll_timer(struct controller *ctrl, int sec); @@ -253,22 +131,20 @@ static inline void pciehp_free_irq(struct controller *ctrl) static int pcie_poll_cmd(struct controller *ctrl) { u16 slot_status; - int timeout = 1000; + int err, timeout = 1000; - if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) { - if (slot_status & CMD_COMPLETED) { - pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); - return 1; - } + err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + return 1; } while (timeout > 0) { msleep(10); timeout -= 10; - if (!pciehp_readw(ctrl, SLOTSTATUS, &slot_status)) { - if (slot_status & CMD_COMPLETED) { - pciehp_writew(ctrl, SLOTSTATUS, CMD_COMPLETED); - return 1; - } + err = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); + if (!err && (slot_status & PCI_EXP_SLTSTA_CC)) { + pciehp_writew(ctrl, PCI_EXP_SLTSTA, PCI_EXP_SLTSTA_CC); + return 1; } } return 0; /* timeout */ @@ -302,14 +178,14 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) mutex_lock(&ctrl->ctrl_lock); - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); goto out; } - if (slot_status & CMD_COMPLETED) { + if (slot_status & PCI_EXP_SLTSTA_CC) { if (!ctrl->no_cmd_complete) { /* * After 1 sec and CMD_COMPLETED still not set, just @@ -332,7 +208,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) } } - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); goto out; @@ -342,7 +218,7 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) slot_ctrl |= (cmd & mask); ctrl->cmd_busy = 1; smp_mb(); - retval = pciehp_writew(ctrl, SLOTCTRL, slot_ctrl); + retval = pciehp_writew(ctrl, PCI_EXP_SLTCTL, slot_ctrl); if (retval) ctrl_err(ctrl, "Cannot write to SLOTCTRL register\n"); @@ -356,8 +232,8 @@ static int pcie_write_cmd(struct controller *ctrl, u16 cmd, u16 mask) * completed interrupt is not enabled, we need to poll * command completed event. */ - if (!(slot_ctrl & HP_INTR_ENABLE) || - !(slot_ctrl & CMD_CMPL_INTR_ENABLE)) + if (!(slot_ctrl & PCI_EXP_SLTCTL_HPIE) || + !(slot_ctrl & PCI_EXP_SLTCTL_CCIE)) poll = 1; pcie_wait_cmd(ctrl, poll); } @@ -370,9 +246,9 @@ static inline int check_link_active(struct controller *ctrl) { u16 link_status; - if (pciehp_readw(ctrl, LNKSTATUS, &link_status)) + if (pciehp_readw(ctrl, PCI_EXP_LNKSTA, &link_status)) return 0; - return !!(link_status & LINK_ACTIVE); + return !!(link_status & PCI_EXP_LNKSTA_DLLLA); } static void pcie_wait_link_active(struct controller *ctrl) @@ -412,14 +288,15 @@ static int hpc_check_lnk_status(struct controller *ctrl) } else msleep(1000); - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "Cannot read LNKSTATUS register\n"); return retval; } ctrl_dbg(ctrl, "%s: lnk_status = %x\n", __func__, lnk_status); - if ((lnk_status & LNK_TRN) || !(lnk_status & NEG_LINK_WD)) { + if ((lnk_status & PCI_EXP_LNKSTA_LT) || + !(lnk_status & PCI_EXP_LNKSTA_NLW)) { ctrl_err(ctrl, "Link Training Error occurs \n"); retval = -1; return retval; @@ -435,16 +312,16 @@ static int hpc_get_attention_status(struct slot *slot, u8 *status) u8 atten_led_state; int retval = 0; - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x, value read %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl); - atten_led_state = (slot_ctrl & ATTN_LED_CTRL) >> 6; + atten_led_state = (slot_ctrl & PCI_EXP_SLTCTL_AIC) >> 6; switch (atten_led_state) { case 0: @@ -474,15 +351,15 @@ static int hpc_get_power_status(struct slot *slot, u8 *status) u8 pwr_state; int retval = 0; - retval = pciehp_readw(ctrl, SLOTCTRL, &slot_ctrl); + retval = pciehp_readw(ctrl, PCI_EXP_SLTCTL, &slot_ctrl); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTCTRL register\n", __func__); return retval; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x value read %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_ctrl); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_ctrl); - pwr_state = (slot_ctrl & PWR_CTRL) >> 10; + pwr_state = (slot_ctrl & PCI_EXP_SLTCTL_PCC) >> 10; switch (pwr_state) { case 0: @@ -503,17 +380,15 @@ static int hpc_get_latch_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - - *status = (((slot_status & MRL_STATE) >> 5) == 0) ? 0 : 1; - + *status = !!(slot_status & PCI_EXP_SLTSTA_MRLSS); return 0; } @@ -521,18 +396,15 @@ static int hpc_get_adapter_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - u8 card_state; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - card_state = (u8)((slot_status & PRSN_STATE) >> 6); - *status = (card_state == 1) ? 1 : 0; - + *status = !!(slot_status & PCI_EXP_SLTSTA_PDS); return 0; } @@ -540,32 +412,28 @@ static int hpc_query_power_fault(struct slot *slot) { struct controller *ctrl = slot->ctrl; u16 slot_status; - u8 pwr_fault; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "Cannot check for power fault\n"); return retval; } - pwr_fault = (u8)((slot_status & PWR_FAULT_DETECTED) >> 1); - - return pwr_fault; + return !!(slot_status & PCI_EXP_SLTSTA_PFD); } static int hpc_get_emi_status(struct slot *slot, u8 *status) { struct controller *ctrl = slot->ctrl; u16 slot_status; - int retval = 0; + int retval; - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "Cannot check EMI status\n"); return retval; } - *status = (slot_status & EMI_STATE) >> EMI_STATUS_BIT; - + *status = !!(slot_status & PCI_EXP_SLTSTA_EIS); return retval; } @@ -575,8 +443,8 @@ static int hpc_toggle_emi(struct slot *slot) u16 cmd_mask; int rc; - slot_cmd = EMI_CTRL; - cmd_mask = EMI_CTRL; + slot_cmd = PCI_EXP_SLTCTL_EIC; + cmd_mask = PCI_EXP_SLTCTL_EIC; rc = pcie_write_cmd(slot->ctrl, slot_cmd, cmd_mask); slot->last_emi_toggle = get_seconds(); @@ -590,7 +458,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) u16 cmd_mask; int rc; - cmd_mask = ATTN_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_AIC; switch (value) { case 0 : /* turn off */ slot_cmd = 0x00C0; @@ -606,7 +474,7 @@ static int hpc_set_attention_status(struct slot *slot, u8 value) } rc = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); return rc; } @@ -618,10 +486,10 @@ static void hpc_set_green_led_on(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0100; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static void hpc_set_green_led_off(struct slot *slot) @@ -631,10 +499,10 @@ static void hpc_set_green_led_off(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0300; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static void hpc_set_green_led_blink(struct slot *slot) @@ -644,10 +512,10 @@ static void hpc_set_green_led_blink(struct slot *slot) u16 cmd_mask; slot_cmd = 0x0200; - cmd_mask = PWR_LED_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PIC; pcie_write_cmd(ctrl, slot_cmd, cmd_mask); ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); } static int hpc_power_on_slot(struct slot * slot) @@ -661,15 +529,15 @@ static int hpc_power_on_slot(struct slot * slot) ctrl_dbg(ctrl, "%s: slot->hp_slot %x\n", __func__, slot->hp_slot); /* Clear sticky power-fault bit from previous power failures */ - retval = pciehp_readw(ctrl, SLOTSTATUS, &slot_status); + retval = pciehp_readw(ctrl, PCI_EXP_SLTSTA, &slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS register\n", __func__); return retval; } - slot_status &= PWR_FAULT_DETECTED; + slot_status &= PCI_EXP_SLTSTA_PFD; if (slot_status) { - retval = pciehp_writew(ctrl, SLOTSTATUS, slot_status); + retval = pciehp_writew(ctrl, PCI_EXP_SLTSTA, slot_status); if (retval) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS register\n", @@ -679,13 +547,13 @@ static int hpc_power_on_slot(struct slot * slot) } slot_cmd = POWER_ON; - cmd_mask = PWR_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PCC; /* Enable detection that we turned off at slot power-off time */ if (!pciehp_poll_mode) { - slot_cmd |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); - cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); + slot_cmd |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); + cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); @@ -695,7 +563,7 @@ static int hpc_power_on_slot(struct slot * slot) return -1; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); return retval; } @@ -752,7 +620,7 @@ static int hpc_power_off_slot(struct slot * slot) changed = pcie_mask_bad_dllp(ctrl); slot_cmd = POWER_OFF; - cmd_mask = PWR_CTRL; + cmd_mask = PCI_EXP_SLTCTL_PCC; /* * If we get MRL or presence detect interrupts now, the isr * will notice the sticky power-fault bit too and issue power @@ -761,10 +629,10 @@ static int hpc_power_off_slot(struct slot * slot) * till the slot is powered on again. */ if (!pciehp_poll_mode) { - slot_cmd &= ~(PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); - cmd_mask |= (PWR_FAULT_DETECT_ENABLE | MRL_DETECT_ENABLE | - PRSN_DETECT_ENABLE); + slot_cmd &= ~(PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); + cmd_mask |= (PCI_EXP_SLTCTL_PFDE | PCI_EXP_SLTCTL_MRLSCE | + PCI_EXP_SLTCTL_PDCE); } retval = pcie_write_cmd(ctrl, slot_cmd, cmd_mask); @@ -774,7 +642,7 @@ static int hpc_power_off_slot(struct slot * slot) goto out; } ctrl_dbg(ctrl, "%s: SLOTCTRL %x write cmd %x\n", - __func__, ctrl->cap_base + SLOTCTRL, slot_cmd); + __func__, ctrl->cap_base + PCI_EXP_SLTCTL, slot_cmd); out: if (changed) pcie_unmask_bad_dllp(ctrl); @@ -795,19 +663,19 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) */ intr_loc = 0; do { - if (pciehp_readw(ctrl, SLOTSTATUS, &detected)) { + if (pciehp_readw(ctrl, PCI_EXP_SLTSTA, &detected)) { ctrl_err(ctrl, "%s: Cannot read SLOTSTATUS\n", __func__); return IRQ_NONE; } - detected &= (ATTN_BUTTN_PRESSED | PWR_FAULT_DETECTED | - MRL_SENS_CHANGED | PRSN_DETECT_CHANGED | - CMD_COMPLETED); + detected &= (PCI_EXP_SLTSTA_ABP | PCI_EXP_SLTSTA_PFD | + PCI_EXP_SLTSTA_MRLSC | PCI_EXP_SLTSTA_PDC | + PCI_EXP_SLTSTA_CC); intr_loc |= detected; if (!intr_loc) return IRQ_NONE; - if (detected && pciehp_writew(ctrl, SLOTSTATUS, detected)) { + if (detected && pciehp_writew(ctrl, PCI_EXP_SLTSTA, detected)) { ctrl_err(ctrl, "%s: Cannot write to SLOTSTATUS\n", __func__); return IRQ_NONE; @@ -817,31 +685,31 @@ static irqreturn_t pcie_isr(int irq, void *dev_id) ctrl_dbg(ctrl, "%s: intr_loc %x\n", __func__, intr_loc); /* Check Command Complete Interrupt Pending */ - if (intr_loc & CMD_COMPLETED) { + if (intr_loc & PCI_EXP_SLTSTA_CC) { ctrl->cmd_busy = 0; smp_mb(); wake_up(&ctrl->queue); } - if (!(intr_loc & ~CMD_COMPLETED)) + if (!(intr_loc & ~PCI_EXP_SLTSTA_CC)) return IRQ_HANDLED; p_slot = pciehp_find_slot(ctrl, ctrl->slot_device_offset); /* Check MRL Sensor Changed */ - if (intr_loc & MRL_SENS_CHANGED) + if (intr_loc & PCI_EXP_SLTSTA_MRLSC) pciehp_handle_switch_change(p_slot); /* Check Attention Button Pressed */ - if (intr_loc & ATTN_BUTTN_PRESSED) + if (intr_loc & PCI_EXP_SLTSTA_ABP) pciehp_handle_attention_button(p_slot); /* Check Presence Detect Changed */ - if (intr_loc & PRSN_DETECT_CHANGED) + if (intr_loc & PCI_EXP_SLTSTA_PDC) pciehp_handle_presence_change(p_slot); /* Check Power Fault Detected */ - if (intr_loc & PWR_FAULT_DETECTED) + if (intr_loc & PCI_EXP_SLTSTA_PFD) pciehp_handle_power_fault(p_slot); return IRQ_HANDLED; @@ -854,7 +722,7 @@ static int hpc_get_max_lnk_speed(struct slot *slot, enum pci_bus_speed *value) u32 lnk_cap; int retval = 0; - retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); + retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); return retval; @@ -883,13 +751,13 @@ static int hpc_get_max_lnk_width(struct slot *slot, u32 lnk_cap; int retval = 0; - retval = pciehp_readl(ctrl, LNKCAP, &lnk_cap); + retval = pciehp_readl(ctrl, PCI_EXP_LNKCAP, &lnk_cap); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); return retval; } - switch ((lnk_cap & 0x03F0) >> 4){ + switch ((lnk_cap & PCI_EXP_LNKSTA_NLW) >> 4){ case 0: lnk_wdth = PCIE_LNK_WIDTH_RESRV; break; @@ -932,14 +800,14 @@ static int hpc_get_cur_lnk_speed(struct slot *slot, enum pci_bus_speed *value) int retval = 0; u16 lnk_status; - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", __func__); return retval; } - switch (lnk_status & 0x0F) { + switch (lnk_status & PCI_EXP_LNKSTA_CLS) { case 1: lnk_speed = PCIE_2PT5GB; break; @@ -962,14 +830,14 @@ static int hpc_get_cur_lnk_width(struct slot *slot, int retval = 0; u16 lnk_status; - retval = pciehp_readw(ctrl, LNKSTATUS, &lnk_status); + retval = pciehp_readw(ctrl, PCI_EXP_LNKSTA, &lnk_status); if (retval) { ctrl_err(ctrl, "%s: Cannot read LNKSTATUS register\n", __func__); return retval; } - switch ((lnk_status & 0x03F0) >> 4){ + switch ((lnk_status & PCI_EXP_LNKSTA_NLW) >> 4){ case 0: lnk_wdth = PCIE_LNK_WIDTH_RESRV; break; @@ -1035,18 +903,19 @@ int pcie_enable_notification(struct controller *ctrl) { u16 cmd, mask; - cmd = PRSN_DETECT_ENABLE; + cmd = PCI_EXP_SLTCTL_PDCE; if (ATTN_BUTTN(ctrl)) - cmd |= ATTN_BUTTN_ENABLE; + cmd |= PCI_EXP_SLTCTL_ABPE; if (POWER_CTRL(ctrl)) - cmd |= PWR_FAULT_DETECT_ENABLE; + cmd |= PCI_EXP_SLTCTL_PFDE; if (MRL_SENS(ctrl)) - cmd |= MRL_DETECT_ENABLE; + cmd |= PCI_EXP_SLTCTL_MRLSCE; if (!pciehp_poll_mode) - cmd |= HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + cmd |= PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE; - mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | - PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); if (pcie_write_cmd(ctrl, cmd, mask)) { ctrl_err(ctrl, "Cannot enable software notification\n"); @@ -1058,8 +927,9 @@ int pcie_enable_notification(struct controller *ctrl) static void pcie_disable_notification(struct controller *ctrl) { u16 mask; - mask = PRSN_DETECT_ENABLE | ATTN_BUTTN_ENABLE | MRL_DETECT_ENABLE | - PWR_FAULT_DETECT_ENABLE | HP_INTR_ENABLE | CMD_CMPL_INTR_ENABLE; + mask = (PCI_EXP_SLTCTL_PDCE | PCI_EXP_SLTCTL_ABPE | + PCI_EXP_SLTCTL_MRLSCE | PCI_EXP_SLTCTL_PFDE | + PCI_EXP_SLTCTL_HPIE | PCI_EXP_SLTCTL_CCIE); if (pcie_write_cmd(ctrl, 0, mask)) ctrl_warn(ctrl, "Cannot disable software notification\n"); } @@ -1156,9 +1026,9 @@ static inline void dbg_ctrl(struct controller *ctrl) EMI(ctrl) ? "yes" : "no"); ctrl_info(ctrl, " Command Completed : %3s\n", NO_CMD_CMPL(ctrl) ? "no" : "yes"); - pciehp_readw(ctrl, SLOTSTATUS, ®16); + pciehp_readw(ctrl, PCI_EXP_SLTSTA, ®16); ctrl_info(ctrl, "Slot Status : 0x%04x\n", reg16); - pciehp_readw(ctrl, SLOTCTRL, ®16); + pciehp_readw(ctrl, PCI_EXP_SLTCTL, ®16); ctrl_info(ctrl, "Slot Control : 0x%04x\n", reg16); } @@ -1182,7 +1052,7 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl_err(ctrl, "Cannot find PCI Express capability\n"); goto abort_ctrl; } - if (pciehp_readl(ctrl, SLOTCAP, &slot_cap)) { + if (pciehp_readl(ctrl, PCI_EXP_SLTCAP, &slot_cap)) { ctrl_err(ctrl, "Cannot read SLOTCAP register\n"); goto abort_ctrl; } @@ -1207,17 +1077,17 @@ struct controller *pcie_init(struct pcie_device *dev) ctrl->no_cmd_complete = 1; /* Check if Data Link Layer Link Active Reporting is implemented */ - if (pciehp_readl(ctrl, LNKCAP, &link_cap)) { + if (pciehp_readl(ctrl, PCI_EXP_LNKCAP, &link_cap)) { ctrl_err(ctrl, "%s: Cannot read LNKCAP register\n", __func__); goto abort_ctrl; } - if (link_cap & LINK_ACTIVE_REPORTING) { + if (link_cap & PCI_EXP_LNKCAP_DLLLARC) { ctrl_dbg(ctrl, "Link Active Reporting supported\n"); ctrl->link_active_reporting = 1; } /* Clear all remaining event bits in Slot Status register */ - if (pciehp_writew(ctrl, SLOTSTATUS, 0x1f)) + if (pciehp_writew(ctrl, PCI_EXP_SLTSTA, 0x1f)) goto abort_ctrl; /* Disable sotfware notification */ diff --git a/include/linux/pci_regs.h b/include/linux/pci_regs.h index 7766488470e4..027815b4635e 100644 --- a/include/linux/pci_regs.h +++ b/include/linux/pci_regs.h @@ -411,20 +411,70 @@ #define PCI_EXP_DEVSTA_AUXPD 0x10 /* AUX Power Detected */ #define PCI_EXP_DEVSTA_TRPND 0x20 /* Transactions Pending */ #define PCI_EXP_LNKCAP 12 /* Link Capabilities */ -#define PCI_EXP_LNKCAP_ASPMS 0xc00 /* ASPM Support */ -#define PCI_EXP_LNKCAP_L0SEL 0x7000 /* L0s Exit Latency */ -#define PCI_EXP_LNKCAP_L1EL 0x38000 /* L1 Exit Latency */ -#define PCI_EXP_LNKCAP_CLKPM 0x40000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_SLS 0x0000000f /* Supported Link Speeds */ +#define PCI_EXP_LNKCAP_MLW 0x000003f0 /* Maximum Link Width */ +#define PCI_EXP_LNKCAP_ASPMS 0x00000c00 /* ASPM Support */ +#define PCI_EXP_LNKCAP_L0SEL 0x00007000 /* L0s Exit Latency */ +#define PCI_EXP_LNKCAP_L1EL 0x00038000 /* L1 Exit Latency */ +#define PCI_EXP_LNKCAP_CLKPM 0x00040000 /* L1 Clock Power Management */ +#define PCI_EXP_LNKCAP_SDERC 0x00080000 /* Suprise Down Error Reporting Capable */ +#define PCI_EXP_LNKCAP_DLLLARC 0x00100000 /* Data Link Layer Link Active Reporting Capable */ +#define PCI_EXP_LNKCAP_LBNC 0x00200000 /* Link Bandwidth Notification Capability */ +#define PCI_EXP_LNKCAP_PN 0xff000000 /* Port Number */ #define PCI_EXP_LNKCTL 16 /* Link Control */ -#define PCI_EXP_LNKCTL_RL 0x20 /* Retrain Link */ -#define PCI_EXP_LNKCTL_CCC 0x40 /* Common Clock COnfiguration */ +#define PCI_EXP_LNKCTL_ASPMC 0x0003 /* ASPM Control */ +#define PCI_EXP_LNKCTL_RCB 0x0008 /* Read Completion Boundary */ +#define PCI_EXP_LNKCTL_LD 0x0010 /* Link Disable */ +#define PCI_EXP_LNKCTL_RL 0x0020 /* Retrain Link */ +#define PCI_EXP_LNKCTL_CCC 0x0040 /* Common Clock Configuration */ +#define PCI_EXP_LNKCTL_ES 0x0080 /* Extended Synch */ #define PCI_EXP_LNKCTL_CLKREQ_EN 0x100 /* Enable clkreq */ +#define PCI_EXP_LNKCTL_HAWD 0x0200 /* Hardware Autonomous Width Disable */ +#define PCI_EXP_LNKCTL_LBMIE 0x0400 /* Link Bandwidth Management Interrupt Enable */ +#define PCI_EXP_LNKCTL_LABIE 0x0800 /* Lnk Autonomous Bandwidth Interrupt Enable */ #define PCI_EXP_LNKSTA 18 /* Link Status */ -#define PCI_EXP_LNKSTA_LT 0x800 /* Link Training */ +#define PCI_EXP_LNKSTA_CLS 0x000f /* Current Link Speed */ +#define PCI_EXP_LNKSTA_NLW 0x03f0 /* Nogotiated Link Width */ +#define PCI_EXP_LNKSTA_LT 0x0800 /* Link Training */ #define PCI_EXP_LNKSTA_SLC 0x1000 /* Slot Clock Configuration */ +#define PCI_EXP_LNKSTA_DLLLA 0x2000 /* Data Link Layer Link Active */ +#define PCI_EXP_LNKSTA_LBMS 0x4000 /* Link Bandwidth Management Status */ +#define PCI_EXP_LNKSTA_LABS 0x8000 /* Link Autonomous Bandwidth Status */ #define PCI_EXP_SLTCAP 20 /* Slot Capabilities */ +#define PCI_EXP_SLTCAP_ABP 0x00000001 /* Attention Button Present */ +#define PCI_EXP_SLTCAP_PCP 0x00000002 /* Power Controller Present */ +#define PCI_EXP_SLTCAP_MRLSP 0x00000004 /* MRL Sensor Present */ +#define PCI_EXP_SLTCAP_AIP 0x00000008 /* Attention Indicator Present */ +#define PCI_EXP_SLTCAP_PIP 0x00000010 /* Power Indicator Present */ +#define PCI_EXP_SLTCAP_HPS 0x00000020 /* Hot-Plug Surprise */ +#define PCI_EXP_SLTCAP_HPC 0x00000040 /* Hot-Plug Capable */ +#define PCI_EXP_SLTCAP_SPLV 0x00007f80 /* Slot Power Limit Value */ +#define PCI_EXP_SLTCAP_SPLS 0x00018000 /* Slot Power Limit Scale */ +#define PCI_EXP_SLTCAP_EIP 0x00020000 /* Electromechanical Interlock Present */ +#define PCI_EXP_SLTCAP_NCCS 0x00040000 /* No Command Completed Support */ +#define PCI_EXP_SLTCAP_PSN 0xfff80000 /* Physical Slot Number */ #define PCI_EXP_SLTCTL 24 /* Slot Control */ +#define PCI_EXP_SLTCTL_ABPE 0x0001 /* Attention Button Pressed Enable */ +#define PCI_EXP_SLTCTL_PFDE 0x0002 /* Power Fault Detected Enable */ +#define PCI_EXP_SLTCTL_MRLSCE 0x0004 /* MRL Sensor Changed Enable */ +#define PCI_EXP_SLTCTL_PDCE 0x0008 /* Presence Detect Changed Enable */ +#define PCI_EXP_SLTCTL_CCIE 0x0010 /* Command Completed Interrupt Enable */ +#define PCI_EXP_SLTCTL_HPIE 0x0020 /* Hot-Plug Interrupt Enable */ +#define PCI_EXP_SLTCTL_AIC 0x00c0 /* Attention Indicator Control */ +#define PCI_EXP_SLTCTL_PIC 0x0300 /* Power Indicator Control */ +#define PCI_EXP_SLTCTL_PCC 0x0400 /* Power Controller Control */ +#define PCI_EXP_SLTCTL_EIC 0x0800 /* Electromechanical Interlock Control */ +#define PCI_EXP_SLTCTL_DLLSCE 0x1000 /* Data Link Layer State Changed Enable */ #define PCI_EXP_SLTSTA 26 /* Slot Status */ +#define PCI_EXP_SLTSTA_ABP 0x0001 /* Attention Button Pressed */ +#define PCI_EXP_SLTSTA_PFD 0x0002 /* Power Fault Detected */ +#define PCI_EXP_SLTSTA_MRLSC 0x0004 /* MRL Sensor Changed */ +#define PCI_EXP_SLTSTA_PDC 0x0008 /* Presence Detect Changed */ +#define PCI_EXP_SLTSTA_CC 0x0010 /* Command Completed */ +#define PCI_EXP_SLTSTA_MRLSS 0x0020 /* MRL Sensor State */ +#define PCI_EXP_SLTSTA_PDS 0x0040 /* Presence Detect State */ +#define PCI_EXP_SLTSTA_EIS 0x0080 /* Electromechanical Interlock Status */ +#define PCI_EXP_SLTSTA_DLLSC 0x0100 /* Data Link Layer State Changed */ #define PCI_EXP_RTCTL 28 /* Root Control */ #define PCI_EXP_RTCTL_SECEE 0x01 /* System Error on Correctable Error */ #define PCI_EXP_RTCTL_SENFEE 0x02 /* System Error on Non-Fatal Error */ From b8d9cb2a2226118fd71f657c80b06b670a653022 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sun, 21 Dec 2008 16:39:37 +0100 Subject: [PATCH 80/98] PCI hotplug: remove redundant test in cpq hotplug func is checked not to be NULL a few lines before. A simplified version of the semantic patch that makes this change is as follows: (http://www.emn.fr/x-info/coccinelle/) // @r exists@ local idexpression x; expression E; position p1,p2; @@ if (x@p1 == NULL || ...) { ... when forall return ...; } ... when != \(x=E\|x--\|x++\|--x\|++x\|x-=E\|x+=E\|x|=E\|x&=E\|&x\) ( x@p2 == NULL | x@p2 != NULL ) // another path to the test that is not through p1? @s exists@ local idexpression r.x; position r.p1,r.p2; @@ ... when != x@p1 ( x@p2 == NULL | x@p2 != NULL ) @fix depends on !s@ position r.p1,r.p2; expression x,E; statement S1,S2; @@ ( - if ((x@p2 != NULL) || ...) S1 | - if ((x@p2 == NULL) && ...) S1 | - BUG_ON(x@p2 == NULL); ) // Signed-off-by: Julia Lawall Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/cpqphp_ctrl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/pci/hotplug/cpqphp_ctrl.c b/drivers/pci/hotplug/cpqphp_ctrl.c index d94722149e02..cc227a8c4b11 100644 --- a/drivers/pci/hotplug/cpqphp_ctrl.c +++ b/drivers/pci/hotplug/cpqphp_ctrl.c @@ -1954,7 +1954,7 @@ void cpqhp_pushbutton_thread(unsigned long slot) return ; } - if (func != NULL && ctrl != NULL) { + if (ctrl != NULL) { if (cpqhp_process_SI(ctrl, func) != 0) { amber_LED_on(ctrl, hp_slot); green_LED_off(ctrl, hp_slot); From 6a479079c07211bf348ac8a79754f26bea258f26 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 23 Dec 2008 03:08:29 +0000 Subject: [PATCH 81/98] PCI: Add pci_clear_master() as opposite of pci_set_master() During an online device reset it may be useful to disable bus-mastering. pci_disable_device() does that, and far more besides, so is not suitable for an online reset. Add pci_clear_master() which does just this. Signed-off-by: Ben Hutchings Reviewed-by: Matthew Wilcox Signed-off-by: Jesse Barnes --- Documentation/PCI/pci.txt | 3 ++- drivers/pci/pci.c | 39 ++++++++++++++++++++++++++++----------- include/linux/pci.h | 1 + 3 files changed, 31 insertions(+), 12 deletions(-) diff --git a/Documentation/PCI/pci.txt b/Documentation/PCI/pci.txt index fd4907a2968c..7f6de6ea5b47 100644 --- a/Documentation/PCI/pci.txt +++ b/Documentation/PCI/pci.txt @@ -294,7 +294,8 @@ NOTE: pci_enable_device() can fail! Check the return value. pci_set_master() will enable DMA by setting the bus master bit in the PCI_COMMAND register. It also fixes the latency timer value if -it's set to something bogus by the BIOS. +it's set to something bogus by the BIOS. pci_clear_master() will +disable DMA by clearing the bus master bit. If the PCI device can use the PCI Memory-Write-Invalidate transaction, call pci_set_mwi(). This enables the PCI_COMMAND bit for Mem-Wr-Inval diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index c824dc8d617c..f3fd55df67db 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1667,6 +1667,22 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) ((1 << 6) - 1), res_name); } +static void __pci_set_master(struct pci_dev *dev, bool enable) +{ + u16 old_cmd, cmd; + + pci_read_config_word(dev, PCI_COMMAND, &old_cmd); + if (enable) + cmd = old_cmd | PCI_COMMAND_MASTER; + else + cmd = old_cmd & ~PCI_COMMAND_MASTER; + if (cmd != old_cmd) { + dev_dbg(&dev->dev, "%s bus mastering\n", + enable ? "enabling" : "disabling"); + pci_write_config_word(dev, PCI_COMMAND, cmd); + } + dev->is_busmaster = enable; +} /** * pci_set_master - enables bus-mastering for device dev @@ -1675,21 +1691,21 @@ int pci_request_regions_exclusive(struct pci_dev *pdev, const char *res_name) * Enables bus-mastering on the device and calls pcibios_set_master() * to do the needed arch specific settings. */ -void -pci_set_master(struct pci_dev *dev) +void pci_set_master(struct pci_dev *dev) { - u16 cmd; - - pci_read_config_word(dev, PCI_COMMAND, &cmd); - if (! (cmd & PCI_COMMAND_MASTER)) { - dev_dbg(&dev->dev, "enabling bus mastering\n"); - cmd |= PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, cmd); - } - dev->is_busmaster = 1; + __pci_set_master(dev, true); pcibios_set_master(dev); } +/** + * pci_clear_master - disables bus-mastering for device dev + * @dev: the PCI device to disable + */ +void pci_clear_master(struct pci_dev *dev) +{ + __pci_set_master(dev, false); +} + #ifdef PCI_DISABLE_MWI int pci_set_mwi(struct pci_dev *dev) { @@ -2346,6 +2362,7 @@ EXPORT_SYMBOL(pci_release_selected_regions); EXPORT_SYMBOL(pci_request_selected_regions); EXPORT_SYMBOL(pci_request_selected_regions_exclusive); EXPORT_SYMBOL(pci_set_master); +EXPORT_SYMBOL(pci_clear_master); EXPORT_SYMBOL(pci_set_mwi); EXPORT_SYMBOL(pci_try_set_mwi); EXPORT_SYMBOL(pci_clear_mwi); diff --git a/include/linux/pci.h b/include/linux/pci.h index 7cbecef19bb6..0f6d2bb1df9c 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -642,6 +642,7 @@ static inline int pci_is_managed(struct pci_dev *pdev) void pci_disable_device(struct pci_dev *dev); void pci_set_master(struct pci_dev *dev); +void pci_clear_master(struct pci_dev *dev); int pci_set_pcie_reset_state(struct pci_dev *dev, enum pcie_reset_state state); #define HAVE_PCI_SET_MWI int __must_check pci_set_mwi(struct pci_dev *dev); From ef1bba28bfe68ef3c0488feeaabd3e8bc523130c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Tue, 23 Dec 2008 03:09:53 +0000 Subject: [PATCH 82/98] net: sfc: Use pci_clear_master() to disable bus mastering pci_disable_device() disables many features, like MSI-X, which we never reenable in efx_reset(). Further, calls to pci_enable_device() and pci_disable_device() must be matched since the nesting count was introduced, so switch to using pci_clear_master() instead. Signed-off-by: Ben Hutchings Signed-off-by: Jesse Barnes --- drivers/net/sfc/falcon.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/net/sfc/falcon.c b/drivers/net/sfc/falcon.c index 6884dc8c1f82..5b9f2d9cc4ed 100644 --- a/drivers/net/sfc/falcon.c +++ b/drivers/net/sfc/falcon.c @@ -1403,9 +1403,9 @@ static irqreturn_t falcon_fatal_interrupt(struct efx_nic *efx) } /* Disable both devices */ - pci_disable_device(efx->pci_dev); + pci_clear_master(efx->pci_dev); if (FALCON_IS_DUAL_FUNC(efx)) - pci_disable_device(nic_data->pci_dev2); + pci_clear_master(nic_data->pci_dev2); falcon_disable_interrupts(efx); if (++n_int_errors < FALCON_MAX_INT_ERRORS) { From 16cf0ebc35dd63f72628ba1246132a6fd17bced2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 5 Jan 2009 14:50:27 +0100 Subject: [PATCH 83/98] x86/PCI: Do not use interrupt links for devices using MSI-X pcibios_enable_device() and pcibios_disable_device() don't handle IRQs for devices that have MSI enabled and it should treat the devices with MSI-X enabled in the same way. Signed-off-by: Rafael J. Wysocki Acked-by: Ingo Molnar Signed-off-by: Jesse Barnes --- arch/x86/pci/common.c | 4 ++-- include/linux/pci.h | 9 +++++++++ 2 files changed, 11 insertions(+), 2 deletions(-) diff --git a/arch/x86/pci/common.c b/arch/x86/pci/common.c index 9ab8509f7b15..82d22fc601ae 100644 --- a/arch/x86/pci/common.c +++ b/arch/x86/pci/common.c @@ -551,14 +551,14 @@ int pcibios_enable_device(struct pci_dev *dev, int mask) if ((err = pci_enable_resources(dev, mask)) < 0) return err; - if (!dev->msi_enabled) + if (!pci_dev_msi_enabled(dev)) return pcibios_enable_irq(dev); return 0; } void pcibios_disable_device (struct pci_dev *dev) { - if (!dev->msi_enabled && pcibios_disable_irq) + if (!pci_dev_msi_enabled(dev) && pcibios_disable_irq) pcibios_disable_irq(dev); } diff --git a/include/linux/pci.h b/include/linux/pci.h index 0f6d2bb1df9c..80f8b8b65fde 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -336,6 +336,15 @@ struct pci_bus { #define pci_bus_b(n) list_entry(n, struct pci_bus, node) #define to_pci_bus(n) container_of(n, struct pci_bus, dev) +#ifdef CONFIG_PCI_MSI +static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) +{ + return pci_dev->msi_enabled || pci_dev->msix_enabled; +} +#else +static inline bool pci_dev_msi_enabled(struct pci_dev *pci_dev) { return false; } +#endif + /* * Error values that may be returned by PCI functions. */ From facf6d1627a33badbbc154524c4a2c73c51bdc99 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 1 Jan 2009 19:48:55 +0100 Subject: [PATCH 84/98] PCI: PCIe portdrv: Add kerneldoc comments to some core functions Add kerneldoc comments to some functions in drivers/pci/pcie/portdrv_core.c, since the code in there is not easy to follow without any additional description. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 77 ++++++++++++++++++++++++++++++--- 1 file changed, 70 insertions(+), 7 deletions(-) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6a9f83ccaff4..8f09b353d2af 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -94,12 +94,12 @@ static int pcie_port_resume_service(struct device *dev) return 0; } -/* - * release_pcie_device - * - * Being invoked automatically when device is being removed - * in response to device_unregister(dev) call. - * Release all resources being claimed. +/** + * release_pcie_device - free PCI Express port service device structure + * @dev: Port service device to release + * + * Invoked automatically when device is being removed in response to + * device_unregister(dev). Release all resources being claimed. */ static void release_pcie_device(struct device *dev) { @@ -127,7 +127,16 @@ static int is_msi_quirked(struct pci_dev *dev) } return quirk; } - + +/** + * assign_interrupt_mode - choose interrupt mode for PCI Express port services + * (INTx, MSI-X, MSI) and set up vectors + * @dev: PCI Express port to handle + * @vectors: Array of interrupt vectors to populate + * @mask: Bitmask of port capabilities returned by get_port_device_capability() + * + * Return value: Interrupt mode associated with the port + */ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) { int i, pos, nvec, status = -EINVAL; @@ -174,6 +183,16 @@ static int assign_interrupt_mode(struct pci_dev *dev, int *vectors, int mask) return interrupt_mode; } +/** + * get_port_device_capability - discover capabilities of a PCI Express port + * @dev: PCI Express port to examine + * + * The capabilities are read from the port's PCI Express configuration registers + * as described in PCI Express Base Specification 1.0a sections 7.8.2, 7.8.9 and + * 7.9 - 7.11. + * + * Return value: Bitmask of discovered port capabilities + */ static int get_port_device_capability(struct pci_dev *dev) { int services = 0, pos; @@ -201,6 +220,15 @@ static int get_port_device_capability(struct pci_dev *dev) return services; } +/** + * pcie_device_init - initialize PCI Express port service device + * @dev: Port service device to initialize + * @parent: PCI Express port to associate the service device with + * @port_type: Type of the port + * @service_type: Type of service to associate with the service device + * @irq: Interrupt vector to associate with the service device + * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) + */ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, int port_type, int service_type, int irq, int irq_mode) { @@ -226,6 +254,14 @@ static void pcie_device_init(struct pci_dev *parent, struct pcie_device *dev, device->parent = &parent->dev; } +/** + * alloc_pcie_device - allocate PCI Express port service device structure + * @parent: PCI Express port to associate the service device with + * @port_type: Type of the port + * @service_type: Type of service to associate with the service device + * @irq: Interrupt vector to associate with the service device + * @irq_mode: Interrupt mode of the service (INTx, MSI-X, MSI) + */ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, int port_type, int service_type, int irq, int irq_mode) { @@ -239,6 +275,10 @@ static struct pcie_device* alloc_pcie_device(struct pci_dev *parent, return device; } +/** + * pcie_port_device_probe - check if device is a PCI Express port + * @dev: Device to check + */ int pcie_port_device_probe(struct pci_dev *dev) { int pos, type; @@ -256,6 +296,13 @@ int pcie_port_device_probe(struct pci_dev *dev) return -ENODEV; } +/** + * pcie_port_device_register - register PCI Express port + * @dev: PCI Express port to register + * + * Allocate the port extension structure and register services associated with + * the port. + */ int pcie_port_device_register(struct pci_dev *dev) { struct pcie_port_device_ext *p_ext; @@ -319,6 +366,11 @@ static int suspend_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_suspend - suspend port services associated with a PCIe port + * @dev: PCI Express port to handle + * @state: Representation of system power management transition in progress + */ int pcie_port_device_suspend(struct pci_dev *dev, pm_message_t state) { return device_for_each_child(&dev->dev, &state, suspend_iter); @@ -337,6 +389,10 @@ static int resume_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_suspend - resume port services associated with a PCIe port + * @dev: PCI Express port to handle + */ int pcie_port_device_resume(struct pci_dev *dev) { return device_for_each_child(&dev->dev, NULL, resume_iter); @@ -359,6 +415,13 @@ static int remove_iter(struct device *dev, void *data) return 0; } +/** + * pcie_port_device_remove - unregister PCI Express port service devices + * @dev: PCI Express port the service devices to unregister are associated with + * + * Remove PCI Express port service devices associated with given port and + * disable MSI-X or MSI for the port. + */ void pcie_port_device_remove(struct pci_dev *dev) { struct device *device; From e7ae88486670f3904f187d0fff9dcf469bcdd8ba Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 1 Jan 2009 19:51:15 +0100 Subject: [PATCH 85/98] PCI: PCIe portdrv: Fix suspend and resume of PCI Express port services There is a problem with the suspend and resume of PCI Express port service devices that the ->suspend() and ->resume() routines of each service device are called twice in each suspend-resume cycle, which is obviously wrong. The scenario is that first, the PCI Express port driver calls suspend and resume routines of each port service driver from its pcie_portdrv_suspend() and pcie_portdrv_resume() callbacks, respectively (which is correct), and second, the pcie_port_bus_type driver calls them from its ->suspend() and ->resume() callbacks (which is not correct, because it doesn't happen at the right time). The solution is to remove the ->suspend() and ->resume() callbacks from pcie_port_bus_type and the associated functions. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_bus.c | 34 --------------------------------- drivers/pci/pcie/portdrv_core.c | 33 -------------------------------- 2 files changed, 67 deletions(-) diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index 359fe5568df1..3a03db4323ad 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -16,14 +16,10 @@ #include "portdrv.h" static int pcie_port_bus_match(struct device *dev, struct device_driver *drv); -static int pcie_port_bus_suspend(struct device *dev, pm_message_t state); -static int pcie_port_bus_resume(struct device *dev); struct bus_type pcie_port_bus_type = { .name = "pci_express", .match = pcie_port_bus_match, - .suspend = pcie_port_bus_suspend, - .resume = pcie_port_bus_resume, }; EXPORT_SYMBOL_GPL(pcie_port_bus_type); @@ -48,33 +44,3 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) return 1; } - -static int pcie_port_bus_suspend(struct device *dev, pm_message_t state) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->suspend) - driver->suspend(pciedev, state); - return 0; -} - -static int pcie_port_bus_resume(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->resume) - driver->resume(pciedev); - return 0; -} diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 8f09b353d2af..6ca5a40cc61c 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -63,37 +63,6 @@ static int pcie_port_remove_service(struct device *dev) static void pcie_port_shutdown_service(struct device *dev) {} -static int pcie_port_suspend_service(struct device *dev, pm_message_t state) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->suspend) - driver->suspend(pciedev, state); - return 0; -} - -static int pcie_port_resume_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - - if (driver && driver->resume) - driver->resume(pciedev); - return 0; -} - /** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release @@ -462,8 +431,6 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) new->driver.probe = pcie_port_probe_service; new->driver.remove = pcie_port_remove_service; new->driver.shutdown = pcie_port_shutdown_service; - new->driver.suspend = pcie_port_suspend_service; - new->driver.resume = pcie_port_resume_service; return driver_register(&new->driver); } From fa6c993736cb8cc18af86b8d17b608efa7882ab5 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 1 Jan 2009 19:52:12 +0100 Subject: [PATCH 86/98] PCI: PCIe portdrv: Rearrange code so that related things are together Rearrange code in drivers/pci/pcie/portdrv_bus.c and drivers/pci/pcie/portdrv_core.c so that related functions and data structures are closer together. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_bus.c | 10 ++++ drivers/pci/pcie/portdrv_core.c | 90 +++++++++++++++------------------ 2 files changed, 50 insertions(+), 50 deletions(-) diff --git a/drivers/pci/pcie/portdrv_bus.c b/drivers/pci/pcie/portdrv_bus.c index 3a03db4323ad..eec89b767f9f 100644 --- a/drivers/pci/pcie/portdrv_bus.c +++ b/drivers/pci/pcie/portdrv_bus.c @@ -44,3 +44,13 @@ static int pcie_port_bus_match(struct device *dev, struct device_driver *drv) return 1; } + +int pcie_port_bus_register(void) +{ + return bus_register(&pcie_port_bus_type); +} + +void pcie_port_bus_unregister(void) +{ + bus_unregister(&pcie_port_bus_type); +} diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index 6ca5a40cc61c..a0d51e3f34f4 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -19,50 +19,6 @@ extern int pcie_mch_quirk; /* MSI-quirk Indicator */ -static int pcie_port_probe_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - int status; - - if (!dev || !dev->driver) - return -ENODEV; - - driver = to_service_driver(dev->driver); - if (!driver || !driver->probe) - return -ENODEV; - - pciedev = to_pcie_device(dev); - status = driver->probe(pciedev, driver->id_table); - if (!status) { - dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", - driver->name); - get_device(dev); - } - return status; -} - -static int pcie_port_remove_service(struct device *dev) -{ - struct pcie_device *pciedev; - struct pcie_port_service_driver *driver; - - if (!dev || !dev->driver) - return 0; - - pciedev = to_pcie_device(dev); - driver = to_service_driver(dev->driver); - if (driver && driver->remove) { - dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n", - driver->name); - driver->remove(pciedev); - put_device(dev); - } - return 0; -} - -static void pcie_port_shutdown_service(struct device *dev) {} - /** * release_pcie_device - free PCI Express port service device structure * @dev: Port service device to release @@ -414,16 +370,50 @@ void pcie_port_device_remove(struct pci_dev *dev) pci_disable_msi(dev); } -int pcie_port_bus_register(void) +static int pcie_port_probe_service(struct device *dev) { - return bus_register(&pcie_port_bus_type); + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + int status; + + if (!dev || !dev->driver) + return -ENODEV; + + driver = to_service_driver(dev->driver); + if (!driver || !driver->probe) + return -ENODEV; + + pciedev = to_pcie_device(dev); + status = driver->probe(pciedev, driver->id_table); + if (!status) { + dev_printk(KERN_DEBUG, dev, "service driver %s loaded\n", + driver->name); + get_device(dev); + } + return status; } -void pcie_port_bus_unregister(void) +static int pcie_port_remove_service(struct device *dev) { - bus_unregister(&pcie_port_bus_type); + struct pcie_device *pciedev; + struct pcie_port_service_driver *driver; + + if (!dev || !dev->driver) + return 0; + + pciedev = to_pcie_device(dev); + driver = to_service_driver(dev->driver); + if (driver && driver->remove) { + dev_printk(KERN_DEBUG, dev, "unloading service driver %s\n", + driver->name); + driver->remove(pciedev); + put_device(dev); + } + return 0; } +static void pcie_port_shutdown_service(struct device *dev) {} + int pcie_port_service_register(struct pcie_port_service_driver *new) { new->driver.name = (char *)new->name; @@ -435,9 +425,9 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) return driver_register(&new->driver); } -void pcie_port_service_unregister(struct pcie_port_service_driver *new) +void pcie_port_service_unregister(struct pcie_port_service_driver *drv) { - driver_unregister(&new->driver); + driver_unregister(&drv->driver); } EXPORT_SYMBOL(pcie_port_service_register); From d9347371c538544a7309d5b6475ec011d98d40e6 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 1 Jan 2009 19:53:32 +0100 Subject: [PATCH 87/98] PCI: PCIe portdrv: Add kerneldoc comments to remining core funtions Add kerneldoc comments to the reamining functions in drivers/pci/pcie/portdrv_core.c . Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pcie/portdrv_core.c | 34 +++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/drivers/pci/pcie/portdrv_core.c b/drivers/pci/pcie/portdrv_core.c index a0d51e3f34f4..8b3f8c18032f 100644 --- a/drivers/pci/pcie/portdrv_core.c +++ b/drivers/pci/pcie/portdrv_core.c @@ -370,6 +370,14 @@ void pcie_port_device_remove(struct pci_dev *dev) pci_disable_msi(dev); } +/** + * pcie_port_probe_service - probe driver for given PCI Express port service + * @dev: PCI Express port service device to probe against + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * whenever match is found between the driver and a port service device. + */ static int pcie_port_probe_service(struct device *dev) { struct pcie_device *pciedev; @@ -393,6 +401,15 @@ static int pcie_port_probe_service(struct device *dev) return status; } +/** + * pcie_port_remove_service - detach driver from given PCI Express port service + * @dev: PCI Express port service device to handle + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * when device_unregister() is called for the port service device associated + * with the driver. + */ static int pcie_port_remove_service(struct device *dev) { struct pcie_device *pciedev; @@ -412,8 +429,21 @@ static int pcie_port_remove_service(struct device *dev) return 0; } +/** + * pcie_port_shutdown_service - shut down given PCI Express port service + * @dev: PCI Express port service device to handle + * + * If PCI Express port service driver is registered with + * pcie_port_service_register(), this function will be called by the driver core + * when device_shutdown() is called for the port service device associated + * with the driver. + */ static void pcie_port_shutdown_service(struct device *dev) {} +/** + * pcie_port_service_register - register PCI Express port service driver + * @new: PCI Express port service driver to register + */ int pcie_port_service_register(struct pcie_port_service_driver *new) { new->driver.name = (char *)new->name; @@ -425,6 +455,10 @@ int pcie_port_service_register(struct pcie_port_service_driver *new) return driver_register(&new->driver); } +/** + * pcie_port_service_unregister - unregister PCI Express port service driver + * @drv: PCI Express port service driver to unregister + */ void pcie_port_service_unregister(struct pcie_port_service_driver *drv) { driver_unregister(&drv->driver); From 987a4c783a8bbf3baf554e6b8ff588b26e06e020 Mon Sep 17 00:00:00 2001 From: Andrew Patterson Date: Mon, 5 Jan 2009 16:21:04 -0700 Subject: [PATCH 88/98] PCI: Use msleep instead of cpu_relax during ASPM link retraining The cpu_relax() function can be a noop on certain architectures like IA-64 when CPU threads are disabled, so use msleep instead during link retraining busy/wait loop. Introduce define LINK_RETRAIN_TIMEOUT instead of hard-coding timeout in pcie_aspm_configure_common_clock. Use time_after() to avoid jiffy wraparound when checking for expired timeout. After timeout expires, recheck link status register link training bit instead of checking for expired timeout to avoid possible false positive. Note that Matthew Wilcox came up with the first rough version of this patch. Reviewed-by: Matthew Wilcox Signed-off-by: Andrew Patterson Signed-off-by: Jesse Barnes --- drivers/pci/pcie/aspm.c | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 4d8e2c7b2ad1..586b6f75910d 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "../pci.h" @@ -75,6 +76,8 @@ static const char *policy_str[] = { [POLICY_POWERSAVE] = "powersave" }; +#define LINK_RETRAIN_TIMEOUT HZ + static int policy_to_aspm_state(struct pci_dev *pdev) { struct pcie_link_state *link_state = pdev->link_state; @@ -238,16 +241,18 @@ static void pcie_aspm_configure_common_clock(struct pci_dev *pdev) pci_write_config_word(pdev, pos + PCI_EXP_LNKCTL, reg16); /* Wait for link training end */ - /* break out after waiting for 1 second */ + /* break out after waiting for timeout */ start_jiffies = jiffies; - while ((jiffies - start_jiffies) < HZ) { + for (;;) { pci_read_config_word(pdev, pos + PCI_EXP_LNKSTA, ®16); if (!(reg16 & PCI_EXP_LNKSTA_LT)) break; - cpu_relax(); + if (time_after(jiffies, start_jiffies + LINK_RETRAIN_TIMEOUT)) + break; + msleep(1); } /* training failed -> recover */ - if ((jiffies - start_jiffies) >= HZ) { + if (reg16 & PCI_EXP_LNKSTA_LT) { dev_printk (KERN_ERR, &pdev->dev, "ASPM: Could not configure" " common clock\n"); i = 0; From c9b9972b3c88272be02d971346285d1c67fbb95f Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:02:36 +0100 Subject: [PATCH 89/98] PCI PM: Fix poweroff and restore callbacks pci_fixup_device() is called too early in pci_pm_poweroff() and too late in pci_pm_restore(). Moreover, pci_pm_restore_noirq() calls pci_fixup_device() twice and in a wrong way. Fix that. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index c3f76be832d4..23bdf64411e5 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -611,8 +611,6 @@ static int pci_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_suspend, pci_dev); - if (drv && drv->pm) { if (drv->pm->poweroff) { error = drv->pm->poweroff(dev); @@ -622,6 +620,8 @@ static int pci_pm_poweroff(struct device *dev) error = pci_legacy_suspend(dev, PMSG_HIBERNATE); } + pci_fixup_device(pci_fixup_suspend, pci_dev); + return error; } @@ -648,6 +648,8 @@ static int pci_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + pci_fixup_device(pci_fixup_resume, pci_dev); + if (drv && drv->pm) { if (drv->pm->restore) error = drv->pm->restore(dev); @@ -656,7 +658,6 @@ static int pci_pm_restore(struct device *dev) } else { error = pci_default_pm_resume_late(pci_dev); } - pci_fixup_device(pci_fixup_resume, pci_dev); return error; } @@ -667,7 +668,7 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume, pci_dev); + pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv && drv->pm) { if (drv->pm->restore_noirq) @@ -677,7 +678,6 @@ static int pci_pm_restore_noirq(struct device *dev) } else { pci_default_pm_resume_early(pci_dev); } - pci_fixup_device(pci_fixup_resume_early, pci_dev); return error; } From fa58d305d9925b01830e535896a7227a868a9e15 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:03:42 +0100 Subject: [PATCH 90/98] PCI PM: Add suspend counterpart of pci_reenable_device PCI devices without drivers are not disabled during suspend and hibernation, but they are enabled during resume, with the help of pci_reenable_device(), so there is an unbalanced execution of pcibios_enable_device() in the resume code path. To correct this introduce function pci_disable_enabled_device() that will disable the argument device, if it is enabled when the function is being run, without updating the device's pci_dev structure and use it in the suspend code path to balance the pci_reenable_device() executed during resume. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 35 ++++++++++++++++++++++++++++++----- drivers/pci/pci.c | 36 ++++++++++++++++++++++++++++-------- drivers/pci/pci.h | 1 + 3 files changed, 59 insertions(+), 13 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 23bdf64411e5..57cb0015a470 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -324,9 +324,19 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) /* * Default "suspend" method for devices that have no driver provided suspend, - * or not even a driver at all. + * or not even a driver at all (first part). */ -static void pci_default_pm_suspend(struct pci_dev *pci_dev) +static void pci_default_pm_suspend_early(struct pci_dev *pci_dev) +{ + /* If device is enabled at this point, disable it */ + pci_disable_enabled_device(pci_dev); +} + +/* + * Default "suspend" method for devices that have no driver provided suspend, + * or not even a driver at all (second part). + */ +static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) { pci_save_state(pci_dev); /* @@ -377,7 +387,11 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { - pci_default_pm_suspend(pci_dev); + /* + * For compatibility with existing code with legacy PM support + * don't call pci_default_pm_suspend_early() here. + */ + pci_default_pm_suspend_late(pci_dev); } return i; } @@ -455,7 +469,10 @@ static int pci_pm_suspend(struct device *dev) } } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend(dev, PMSG_SUSPEND); + } else { + pci_default_pm_suspend_early(pci_dev); } + pci_fixup_device(pci_fixup_suspend, pci_dev); return error; @@ -475,7 +492,7 @@ static int pci_pm_suspend_noirq(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); } else { - pci_default_pm_suspend(pci_dev); + pci_default_pm_suspend_late(pci_dev); } return error; @@ -546,6 +563,8 @@ static int pci_pm_freeze(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend(dev, PMSG_FREEZE); pci_fixup_device(pci_fixup_suspend, pci_dev); + } else { + pci_default_pm_suspend_early(pci_dev); } return error; @@ -565,7 +584,7 @@ static int pci_pm_freeze_noirq(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend_late(dev, PMSG_FREEZE); } else { - pci_default_pm_suspend(pci_dev); + pci_default_pm_suspend_late(pci_dev); } return error; @@ -583,6 +602,8 @@ static int pci_pm_thaw(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { pci_fixup_device(pci_fixup_resume, pci_dev); error = pci_legacy_resume(dev); + } else { + pci_default_pm_resume_late(pci_dev); } return error; @@ -600,6 +621,8 @@ static int pci_pm_thaw_noirq(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); error = pci_legacy_resume_early(dev); + } else { + pci_default_pm_resume_early(pci_dev); } return error; @@ -618,6 +641,8 @@ static int pci_pm_poweroff(struct device *dev) } } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + } else { + pci_default_pm_suspend_early(pci_dev); } pci_fixup_device(pci_fixup_suspend, pci_dev); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index f3fd55df67db..6e309c8b47df 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -970,6 +970,32 @@ void pcim_pin_device(struct pci_dev *pdev) */ void __attribute__ ((weak)) pcibios_disable_device (struct pci_dev *dev) {} +static void do_pci_disable_device(struct pci_dev *dev) +{ + u16 pci_command; + + pci_read_config_word(dev, PCI_COMMAND, &pci_command); + if (pci_command & PCI_COMMAND_MASTER) { + pci_command &= ~PCI_COMMAND_MASTER; + pci_write_config_word(dev, PCI_COMMAND, pci_command); + } + + pcibios_disable_device(dev); +} + +/** + * pci_disable_enabled_device - Disable device without updating enable_cnt + * @dev: PCI device to disable + * + * NOTE: This function is a backend of PCI power management routines and is + * not supposed to be called drivers. + */ +void pci_disable_enabled_device(struct pci_dev *dev) +{ + if (atomic_read(&dev->enable_cnt)) + do_pci_disable_device(dev); +} + /** * pci_disable_device - Disable PCI device after use * @dev: PCI device to be disabled @@ -984,7 +1010,6 @@ void pci_disable_device(struct pci_dev *dev) { struct pci_devres *dr; - u16 pci_command; dr = find_pci_dr(dev); if (dr) @@ -993,14 +1018,9 @@ pci_disable_device(struct pci_dev *dev) if (atomic_sub_return(1, &dev->enable_cnt) != 0) return; - pci_read_config_word(dev, PCI_COMMAND, &pci_command); - if (pci_command & PCI_COMMAND_MASTER) { - pci_command &= ~PCI_COMMAND_MASTER; - pci_write_config_word(dev, PCI_COMMAND, pci_command); - } - dev->is_busmaster = 0; + do_pci_disable_device(dev); - pcibios_disable_device(dev); + dev->is_busmaster = 0; } /** diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 211fd418f48f..881dc15f8efd 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -44,6 +44,7 @@ struct pci_platform_pm_ops { }; extern int pci_set_platform_pm(struct pci_platform_pm_ops *ops); +extern void pci_disable_enabled_device(struct pci_dev *dev); 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); From 571ff7584bb9e05fca0eb79752ae55a46faf3a98 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:05:05 +0100 Subject: [PATCH 91/98] PCI PM: Power-manage devices without drivers during suspend-resume PCI devices without drivers can be put into low power states during suspend with the help of pci_prepare_to_sleep() and prevented from generating wake-up events during resume with the help of pci_enable_wake(). However, it's better not to put bridges into low power states during suspend, because that might result in entire bus segments being powered off. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 33 +++++++++++++++++++++++++++++---- 1 file changed, 29 insertions(+), 4 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 57cb0015a470..2617ebb34e14 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -435,6 +435,31 @@ static int pci_legacy_resume_early(struct device *dev) return error; } +/* Auxiliary functions used by the new power management framework */ + +static bool pci_is_bridge(struct pci_dev *pci_dev) +{ + return !!(pci_dev->subordinate); +} + +static int pci_pm_default_resume(struct pci_dev *pci_dev) +{ + if (!pci_is_bridge(pci_dev)) + pci_enable_wake(pci_dev, PCI_D0, false); + + return pci_default_pm_resume_late(pci_dev); +} + +static void pci_pm_default_suspend(struct pci_dev *pci_dev) +{ + pci_default_pm_suspend_early(pci_dev); + + if (!pci_is_bridge(pci_dev)) + pci_prepare_to_sleep(pci_dev); +} + +/* New power management framework */ + static int pci_pm_prepare(struct device *dev) { struct device_driver *drv = dev->driver; @@ -470,7 +495,7 @@ static int pci_pm_suspend(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend(dev, PMSG_SUSPEND); } else { - pci_default_pm_suspend_early(pci_dev); + pci_pm_default_suspend(pci_dev); } pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -512,7 +537,7 @@ static int pci_pm_resume(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_resume(dev); } else { - error = pci_default_pm_resume_late(pci_dev); + error = pci_pm_default_resume(pci_dev); } return error; @@ -642,7 +667,7 @@ static int pci_pm_poweroff(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_suspend(dev, PMSG_HIBERNATE); } else { - pci_default_pm_suspend_early(pci_dev); + pci_pm_default_suspend(pci_dev); } pci_fixup_device(pci_fixup_suspend, pci_dev); @@ -681,7 +706,7 @@ static int pci_pm_restore(struct device *dev) } else if (pci_has_legacy_pm_support(pci_dev)) { error = pci_legacy_resume(dev); } else { - error = pci_default_pm_resume_late(pci_dev); + error = pci_pm_default_resume(pci_dev); } return error; From 07e836e8d1f3688311d97fe1bf46980b0f9ae9c1 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:06:10 +0100 Subject: [PATCH 92/98] PCI PM: Move pci_has_legacy_pm_support Move pci_has_legacy_pm_support() closer to the functions that call it. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 2617ebb34e14..bfaa77d88537 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -314,14 +314,6 @@ static void pci_device_shutdown(struct device *dev) #ifdef CONFIG_PM_SLEEP -static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) -{ - struct pci_driver *drv = pci_dev->driver; - - return drv && (drv->suspend || drv->suspend_late || drv->resume - || drv->resume_early); -} - /* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (first part). @@ -458,6 +450,14 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev) pci_prepare_to_sleep(pci_dev); } +static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) +{ + struct pci_driver *drv = pci_dev->driver; + + return drv && (drv->suspend || drv->suspend_late || drv->resume + || drv->resume_early); +} + /* New power management framework */ static int pci_pm_prepare(struct device *dev) From 734104292ff77dc71fe626b4ebd91b314547ca1b Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:07:15 +0100 Subject: [PATCH 93/98] PCI PM: Avoid touching devices behind bridges in unknown state It generally is better to avoid accessing devices behind bridges that may not be in the D0 power state, because in that case the bridges' secondary buses may not be accessible. For this reason, during the early phase of resume (ie. with interrupts disabled), before restoring the standard config registers of a device, check the power state of the bridge the device is behind and postpone the restoration of the device's config space, as well as any other operations that would involve accessing the device, if that state is not D0. In such cases the restoration of the device's config space will be retried during the "normal" phase of resume (ie. with interrupts enabled), so that the bridge can be put into D0 before that happens. Also, save standard configuration registers of PCI devices during the "normal" phase of suspend (ie. with interrupts enabled), so that the bridges the devices are behind can be put into low power states (we don't put bridges into low power states at the moment, but we may want to do it in the future and it seems reasonable to design for that). Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 109 ++++++++++++++++++++++++++------------- drivers/pci/pci.c | 2 +- drivers/pci/pci.h | 1 + 3 files changed, 74 insertions(+), 38 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index bfaa77d88537..750ee79c178f 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -314,23 +314,12 @@ static void pci_device_shutdown(struct device *dev) #ifdef CONFIG_PM_SLEEP -/* - * Default "suspend" method for devices that have no driver provided suspend, - * or not even a driver at all (first part). - */ -static void pci_default_pm_suspend_early(struct pci_dev *pci_dev) -{ - /* If device is enabled at this point, disable it */ - pci_disable_enabled_device(pci_dev); -} - /* * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part). */ static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) { - pci_save_state(pci_dev); /* * mark its power state as "unknown", since we don't know if * e.g. the BIOS will change its device state when we suspend. @@ -339,16 +328,6 @@ static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) pci_dev->current_state = PCI_UNKNOWN; } -/* - * Default "resume" method for devices that have no driver provided resume, - * or not even a driver at all (first part). - */ -static void pci_default_pm_resume_early(struct pci_dev *pci_dev) -{ - /* restore the PCI config space */ - pci_restore_state(pci_dev); -} - /* * Default "resume" method for devices that have no driver provided resume, * or not even a driver at all (second part). @@ -379,9 +358,10 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) i = drv->suspend(pci_dev, state); suspend_report_result(drv->suspend, i); } else { + pci_save_state(pci_dev); /* - * For compatibility with existing code with legacy PM support - * don't call pci_default_pm_suspend_early() here. + * This is for compatibility with existing code with legacy PM + * support. */ pci_default_pm_suspend_late(pci_dev); } @@ -410,7 +390,8 @@ static int pci_legacy_resume(struct device *dev) if (drv && drv->resume) { error = drv->resume(pci_dev); } else { - pci_default_pm_resume_early(pci_dev); + /* restore the PCI config space */ + pci_restore_state(pci_dev); error = pci_default_pm_resume_late(pci_dev); } return error; @@ -429,22 +410,72 @@ static int pci_legacy_resume_early(struct device *dev) /* Auxiliary functions used by the new power management framework */ +static int pci_restore_standard_config(struct pci_dev *pci_dev) +{ + struct pci_dev *parent = pci_dev->bus->self; + int error = 0; + + /* Check if the device's bus is operational */ + if (!parent || parent->current_state == PCI_D0) { + pci_restore_state(pci_dev); + pci_update_current_state(pci_dev, PCI_D0); + } else { + dev_warn(&pci_dev->dev, "unable to restore config, " + "bridge %s in low power state D%d\n", pci_name(parent), + parent->current_state); + pci_dev->current_state = PCI_UNKNOWN; + error = -EAGAIN; + } + + return error; +} + static bool pci_is_bridge(struct pci_dev *pci_dev) { return !!(pci_dev->subordinate); } +static void pci_pm_default_resume_noirq(struct pci_dev *pci_dev) +{ + if (pci_restore_standard_config(pci_dev)) + pci_fixup_device(pci_fixup_resume_early, pci_dev); +} + static int pci_pm_default_resume(struct pci_dev *pci_dev) { + /* + * pci_restore_standard_config() should have been called once already, + * but it would have failed if the device's parent bridge had not been + * in power state D0 at that time. Check it and try again if necessary. + */ + if (pci_dev->current_state == PCI_UNKNOWN) { + int error = pci_restore_standard_config(pci_dev); + if (error) + return error; + } + + pci_fixup_device(pci_fixup_resume, pci_dev); + if (!pci_is_bridge(pci_dev)) pci_enable_wake(pci_dev, PCI_D0, false); return pci_default_pm_resume_late(pci_dev); } +static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) +{ + /* If device is enabled at this point, disable it */ + pci_disable_enabled_device(pci_dev); + /* + * Save state with interrupts enabled, because in principle the bus the + * device is on may be put into a low power state after this code runs. + */ + pci_save_state(pci_dev); +} + static void pci_pm_default_suspend(struct pci_dev *pci_dev) { - pci_default_pm_suspend_early(pci_dev); + pci_pm_default_suspend_generic(pci_dev); if (!pci_is_bridge(pci_dev)) pci_prepare_to_sleep(pci_dev); @@ -529,12 +560,13 @@ static int pci_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume, pci_dev); - if (drv && drv->pm) { + pci_fixup_device(pci_fixup_resume, pci_dev); + if (drv->pm->resume) error = drv->pm->resume(dev); } else if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume, pci_dev); error = pci_legacy_resume(dev); } else { error = pci_pm_default_resume(pci_dev); @@ -549,15 +581,16 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); - if (drv && drv->pm) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + if (drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); } else if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); error = pci_legacy_resume_early(dev); } else { - pci_default_pm_resume_early(pci_dev); + pci_pm_default_resume_noirq(pci_dev); } return error; @@ -589,7 +622,7 @@ static int pci_pm_freeze(struct device *dev) error = pci_legacy_suspend(dev, PMSG_FREEZE); pci_fixup_device(pci_fixup_suspend, pci_dev); } else { - pci_default_pm_suspend_early(pci_dev); + pci_pm_default_suspend_generic(pci_dev); } return error; @@ -647,7 +680,7 @@ static int pci_pm_thaw_noirq(struct device *dev) pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); error = pci_legacy_resume_early(dev); } else { - pci_default_pm_resume_early(pci_dev); + pci_update_current_state(pci_dev, PCI_D0); } return error; @@ -698,12 +731,13 @@ static int pci_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume, pci_dev); - if (drv && drv->pm) { + pci_fixup_device(pci_fixup_resume, pci_dev); + if (drv->pm->restore) error = drv->pm->restore(dev); } else if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume, pci_dev); error = pci_legacy_resume(dev); } else { error = pci_pm_default_resume(pci_dev); @@ -718,15 +752,16 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - pci_fixup_device(pci_fixup_resume_early, pci_dev); - if (drv && drv->pm) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + if (drv->pm->restore_noirq) error = drv->pm->restore_noirq(dev); } else if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); error = pci_legacy_resume_early(dev); } else { - pci_default_pm_resume_early(pci_dev); + pci_pm_default_resume_noirq(pci_dev); } return error; diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 6e309c8b47df..e491fdedf705 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -527,7 +527,7 @@ pci_raw_set_power_state(struct pci_dev *dev, pci_power_t state) * @dev: PCI device to handle. * @state: State to cache in case the device doesn't have the PM capability */ -static void pci_update_current_state(struct pci_dev *dev, pci_power_t state) +void pci_update_current_state(struct pci_dev *dev, pci_power_t state) { if (dev->pm_cap) { u16 pmcsr; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 881dc15f8efd..1351bb4addde 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -44,6 +44,7 @@ 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 void pci_pm_init(struct pci_dev *dev); extern void platform_pci_wakeup_init(struct pci_dev *dev); From bb8089454391ac5577215aec1f1991adcd4b4cbf Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 14:15:17 +0100 Subject: [PATCH 94/98] PCI PM: Rearrange code in pci-driver.c Rename two functions and rearrange code in drivers/pci/pci-driver.c so that it's easier to follow. In particular, separate invocations of the legacy callbacks from the rest of the new callbacks' code. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 116 +++++++++++++++++++++++++-------------- 1 file changed, 75 insertions(+), 41 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 750ee79c178f..2e76945a1cd8 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -318,7 +318,7 @@ static void pci_device_shutdown(struct device *dev) * Default "suspend" method for devices that have no driver provided suspend, * or not even a driver at all (second part). */ -static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) +static void pci_pm_set_unknown_state(struct pci_dev *pci_dev) { /* * mark its power state as "unknown", since we don't know if @@ -332,7 +332,7 @@ static void pci_default_pm_suspend_late(struct pci_dev *pci_dev) * Default "resume" method for devices that have no driver provided resume, * or not even a driver at all (second part). */ -static int pci_default_pm_resume_late(struct pci_dev *pci_dev) +static int pci_pm_reenable_device(struct pci_dev *pci_dev) { int retval; @@ -363,7 +363,7 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) * This is for compatibility with existing code with legacy PM * support. */ - pci_default_pm_suspend_late(pci_dev); + pci_pm_set_unknown_state(pci_dev); } return i; } @@ -392,7 +392,7 @@ static int pci_legacy_resume(struct device *dev) } else { /* restore the PCI config space */ pci_restore_state(pci_dev); - error = pci_default_pm_resume_late(pci_dev); + error = pci_pm_reenable_device(pci_dev); } return error; } @@ -459,7 +459,7 @@ static int pci_pm_default_resume(struct pci_dev *pci_dev) if (!pci_is_bridge(pci_dev)) pci_enable_wake(pci_dev, PCI_D0, false); - return pci_default_pm_resume_late(pci_dev); + return pci_pm_reenable_device(pci_dev); } static void pci_pm_default_suspend_generic(struct pci_dev *pci_dev) @@ -484,9 +484,17 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev) static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) { struct pci_driver *drv = pci_dev->driver; - - return drv && (drv->suspend || drv->suspend_late || drv->resume + bool ret = drv && (drv->suspend || drv->suspend_late || drv->resume || drv->resume_early); + + /* + * Legacy PM support is used by default, so warn if the new framework is + * supported as well. Drivers are supposed to support either the + * former, or the latter, but not both at the same time. + */ + WARN_ON(ret && drv->driver.pm); + + return ret; } /* New power management framework */ @@ -518,17 +526,21 @@ static int pci_pm_suspend(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + error = pci_legacy_suspend(dev, PMSG_SUSPEND); + goto Exit; + } + if (drv && drv->pm) { if (drv->pm->suspend) { error = drv->pm->suspend(dev); suspend_report_result(drv->pm->suspend, error); } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_SUSPEND); } else { pci_pm_default_suspend(pci_dev); } + Exit: pci_fixup_device(pci_fixup_suspend, pci_dev); return error; @@ -540,15 +552,16 @@ static int pci_pm_suspend_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend_late(dev, PMSG_SUSPEND); + if (drv && drv->pm) { if (drv->pm->suspend_noirq) { error = drv->pm->suspend_noirq(dev); suspend_report_result(drv->pm->suspend_noirq, error); } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend_late(dev, PMSG_SUSPEND); } else { - pci_default_pm_suspend_late(pci_dev); + pci_pm_set_unknown_state(pci_dev); } return error; @@ -560,14 +573,16 @@ static int pci_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume, pci_dev); + return pci_legacy_resume(dev); + } + if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume, pci_dev); if (drv->pm->resume) error = drv->pm->resume(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); - error = pci_legacy_resume(dev); } else { error = pci_pm_default_resume(pci_dev); } @@ -581,14 +596,16 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + return pci_legacy_resume_early(dev); + } + if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv->pm->resume_noirq) error = drv->pm->resume_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); - error = pci_legacy_resume_early(dev); } else { pci_pm_default_resume_noirq(pci_dev); } @@ -613,14 +630,17 @@ static int pci_pm_freeze(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + error = pci_legacy_suspend(dev, PMSG_FREEZE); + pci_fixup_device(pci_fixup_suspend, pci_dev); + return error; + } + if (drv && drv->pm) { if (drv->pm->freeze) { error = drv->pm->freeze(dev); suspend_report_result(drv->pm->freeze, error); } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_FREEZE); - pci_fixup_device(pci_fixup_suspend, pci_dev); } else { pci_pm_default_suspend_generic(pci_dev); } @@ -634,15 +654,16 @@ static int pci_pm_freeze_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend_late(dev, PMSG_FREEZE); + if (drv && drv->pm) { if (drv->pm->freeze_noirq) { error = drv->pm->freeze_noirq(dev); suspend_report_result(drv->pm->freeze_noirq, error); } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend_late(dev, PMSG_FREEZE); } else { - pci_default_pm_suspend_late(pci_dev); + pci_pm_set_unknown_state(pci_dev); } return error; @@ -654,14 +675,16 @@ static int pci_pm_thaw(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume, pci_dev); + return pci_legacy_resume(dev); + } + if (drv && drv->pm) { if (drv->pm->thaw) error = drv->pm->thaw(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); - error = pci_legacy_resume(dev); } else { - pci_default_pm_resume_late(pci_dev); + pci_pm_reenable_device(pci_dev); } return error; @@ -673,12 +696,14 @@ static int pci_pm_thaw_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); + return pci_legacy_resume_early(dev); + } + if (drv && drv->pm) { if (drv->pm->thaw_noirq) error = drv->pm->thaw_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); - error = pci_legacy_resume_early(dev); } else { pci_update_current_state(pci_dev, PCI_D0); } @@ -692,17 +717,21 @@ static int pci_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + error = pci_legacy_suspend(dev, PMSG_HIBERNATE); + goto Exit; + } + if (drv && drv->pm) { if (drv->pm->poweroff) { error = drv->pm->poweroff(dev); suspend_report_result(drv->pm->poweroff, error); } - } else if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_HIBERNATE); } else { pci_pm_default_suspend(pci_dev); } + Exit: pci_fixup_device(pci_fixup_suspend, pci_dev); return error; @@ -713,13 +742,14 @@ static int pci_pm_poweroff_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(to_pci_dev(dev))) + return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); + if (drv && drv->pm) { if (drv->pm->poweroff_noirq) { error = drv->pm->poweroff_noirq(dev); suspend_report_result(drv->pm->poweroff_noirq, error); } - } else if (pci_has_legacy_pm_support(to_pci_dev(dev))) { - error = pci_legacy_suspend_late(dev, PMSG_HIBERNATE); } return error; @@ -731,14 +761,16 @@ static int pci_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume, pci_dev); + return pci_legacy_resume(dev); + } + if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume, pci_dev); if (drv->pm->restore) error = drv->pm->restore(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); - error = pci_legacy_resume(dev); } else { error = pci_pm_default_resume(pci_dev); } @@ -752,14 +784,16 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; + if (pci_has_legacy_pm_support(pci_dev)) { + pci_fixup_device(pci_fixup_resume_early, pci_dev); + return pci_legacy_resume_early(dev); + } + if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume_early, pci_dev); if (drv->pm->restore_noirq) error = drv->pm->restore_noirq(dev); - } else if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); - error = pci_legacy_resume_early(dev); } else { pci_pm_default_resume_noirq(pci_dev); } From ad8cfa1defee14a5181d9b63e666318c51cfaeed Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:09:37 +0100 Subject: [PATCH 95/98] PCI PM: Call pci_fixup_device from legacy routines The size of drivers/pci/pci-driver.c can be reduced quite a bit if pci_fixup_device() is called from the legacy PM callbacks, so make it happen. No functional changes should result from this. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 52 +++++++++++++++------------------------- 1 file changed, 19 insertions(+), 33 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 2e76945a1cd8..02bf4d4125e4 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -365,6 +365,9 @@ static int pci_legacy_suspend(struct device *dev, pm_message_t state) */ pci_pm_set_unknown_state(pci_dev); } + + pci_fixup_device(pci_fixup_suspend, pci_dev); + return i; } @@ -387,6 +390,8 @@ static int pci_legacy_resume(struct device *dev) struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; + pci_fixup_device(pci_fixup_resume, pci_dev); + if (drv && drv->resume) { error = drv->resume(pci_dev); } else { @@ -403,6 +408,8 @@ static int pci_legacy_resume_early(struct device *dev) struct pci_dev * pci_dev = to_pci_dev(dev); struct pci_driver * drv = pci_dev->driver; + pci_fixup_device(pci_fixup_resume_early, pci_dev); + if (drv && drv->resume_early) error = drv->resume_early(pci_dev); return error; @@ -526,10 +533,8 @@ static int pci_pm_suspend(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_SUSPEND); - goto Exit; - } + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_SUSPEND); if (drv && drv->pm) { if (drv->pm->suspend) { @@ -540,7 +545,6 @@ static int pci_pm_suspend(struct device *dev) pci_pm_default_suspend(pci_dev); } - Exit: pci_fixup_device(pci_fixup_suspend, pci_dev); return error; @@ -573,10 +577,8 @@ static int pci_pm_resume(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - } if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume, pci_dev); @@ -596,10 +598,8 @@ static int pci_pm_resume_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - } if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume_early, pci_dev); @@ -630,11 +630,8 @@ static int pci_pm_freeze(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_FREEZE); - pci_fixup_device(pci_fixup_suspend, pci_dev); - return error; - } + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_FREEZE); if (drv && drv->pm) { if (drv->pm->freeze) { @@ -675,10 +672,8 @@ static int pci_pm_thaw(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - } if (drv && drv->pm) { if (drv->pm->thaw) @@ -696,10 +691,8 @@ static int pci_pm_thaw_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, to_pci_dev(dev)); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - } if (drv && drv->pm) { if (drv->pm->thaw_noirq) @@ -717,10 +710,8 @@ static int pci_pm_poweroff(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - error = pci_legacy_suspend(dev, PMSG_HIBERNATE); - goto Exit; - } + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_suspend(dev, PMSG_HIBERNATE); if (drv && drv->pm) { if (drv->pm->poweroff) { @@ -731,7 +722,6 @@ static int pci_pm_poweroff(struct device *dev) pci_pm_default_suspend(pci_dev); } - Exit: pci_fixup_device(pci_fixup_suspend, pci_dev); return error; @@ -761,10 +751,8 @@ static int pci_pm_restore(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - } if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume, pci_dev); @@ -784,10 +772,8 @@ static int pci_pm_restore_noirq(struct device *dev) struct device_driver *drv = dev->driver; int error = 0; - if (pci_has_legacy_pm_support(pci_dev)) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - } if (drv && drv->pm) { pci_fixup_device(pci_fixup_resume_early, pci_dev); From 98e6e286d7b01deb7453b717aa38ebb69d6cefc0 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:10:35 +0100 Subject: [PATCH 96/98] PCI PM: Register power state of devices during initialization Use the observation that the power state of a PCI device can be loaded into its pci_dev structure as soon as pci_pm_init() is run for it and make that happen. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index e491fdedf705..c12f6c790698 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -1260,14 +1260,15 @@ void pci_pm_init(struct pci_dev *dev) /* find PCI PM capability in list */ pm = pci_find_capability(dev, PCI_CAP_ID_PM); if (!pm) - return; + goto Exit; + /* Check device's ability to generate PME# */ pci_read_config_word(dev, pm + PCI_PM_PMC, &pmc); if ((pmc & PCI_PM_CAP_VER_MASK) > 3) { dev_err(&dev->dev, "unsupported PM cap regs version (%u)\n", pmc & PCI_PM_CAP_VER_MASK); - return; + goto Exit; } dev->pm_cap = pm; @@ -1306,6 +1307,9 @@ void pci_pm_init(struct pci_dev *dev) } else { dev->pme_support = 0; } + + Exit: + pci_update_current_state(dev, PCI_D0); } /** From d67e37d7933ba3b28a63ff38c957e433aaca5dc4 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:11:28 +0100 Subject: [PATCH 97/98] PCI PM: Run default PM callbacks for all devices using new framework It should be quite clear that it generally makes sense to execute the default PM callbacks (ie. the callbacks used for handling suspend, hibernation and resume of PCI devices without drivers) for all devices. Of course, the drivers that provide legacy PCI PM support (ie. the ->suspend, ->suspend_late, ->resume_early or ->resume hooks in the pci_driver structure), carry out these operations too, so we can't do it for devices with such drivers. Still, we can make the default PM callbacks run for devices with drivers using the new framework (ie. implement the pm object), since there are no such drivers at the moment. This also simplifies the code and makes it smaller. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 135 +++++++++++++++------------------------ 1 file changed, 53 insertions(+), 82 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 02bf4d4125e4..b7e67c2f40b9 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -486,6 +486,8 @@ static void pci_pm_default_suspend(struct pci_dev *pci_dev) if (!pci_is_bridge(pci_dev)) pci_prepare_to_sleep(pci_dev); + + pci_fixup_device(pci_fixup_suspend, pci_dev); } static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) @@ -536,16 +538,13 @@ static int pci_pm_suspend(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_SUSPEND); - if (drv && drv->pm) { - if (drv->pm->suspend) { - error = drv->pm->suspend(dev); - suspend_report_result(drv->pm->suspend, error); - } - } else { - pci_pm_default_suspend(pci_dev); + if (drv && drv->pm && drv->pm->suspend) { + error = drv->pm->suspend(dev); + suspend_report_result(drv->pm->suspend, error); } - pci_fixup_device(pci_fixup_suspend, pci_dev); + if (!error) + pci_pm_default_suspend(pci_dev); return error; } @@ -559,15 +558,14 @@ static int pci_pm_suspend_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_SUSPEND); - if (drv && drv->pm) { - if (drv->pm->suspend_noirq) { - error = drv->pm->suspend_noirq(dev); - suspend_report_result(drv->pm->suspend_noirq, error); - } - } else { - pci_pm_set_unknown_state(pci_dev); + if (drv && drv->pm && drv->pm->suspend_noirq) { + error = drv->pm->suspend_noirq(dev); + suspend_report_result(drv->pm->suspend_noirq, error); } + if (!error) + pci_pm_set_unknown_state(pci_dev); + return error; } @@ -580,14 +578,10 @@ static int pci_pm_resume(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - if (drv && drv->pm) { - pci_fixup_device(pci_fixup_resume, pci_dev); + error = pci_pm_default_resume(pci_dev); - if (drv->pm->resume) - error = drv->pm->resume(dev); - } else { - error = pci_pm_default_resume(pci_dev); - } + if (!error && drv && drv->pm && drv->pm->resume) + error = drv->pm->resume(dev); return error; } @@ -601,14 +595,10 @@ static int pci_pm_resume_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - if (drv && drv->pm) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_pm_default_resume_noirq(pci_dev); - if (drv->pm->resume_noirq) - error = drv->pm->resume_noirq(dev); - } else { - pci_pm_default_resume_noirq(pci_dev); - } + if (drv && drv->pm && drv->pm->resume_noirq) + error = drv->pm->resume_noirq(dev); return error; } @@ -633,15 +623,14 @@ static int pci_pm_freeze(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_FREEZE); - if (drv && drv->pm) { - if (drv->pm->freeze) { - error = drv->pm->freeze(dev); - suspend_report_result(drv->pm->freeze, error); - } - } else { - pci_pm_default_suspend_generic(pci_dev); + if (drv && drv->pm && drv->pm->freeze) { + error = drv->pm->freeze(dev); + suspend_report_result(drv->pm->freeze, error); } + if (!error) + pci_pm_default_suspend_generic(pci_dev); + return error; } @@ -654,15 +643,14 @@ static int pci_pm_freeze_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend_late(dev, PMSG_FREEZE); - if (drv && drv->pm) { - if (drv->pm->freeze_noirq) { - error = drv->pm->freeze_noirq(dev); - suspend_report_result(drv->pm->freeze_noirq, error); - } - } else { - pci_pm_set_unknown_state(pci_dev); + if (drv && drv->pm && drv->pm->freeze_noirq) { + error = drv->pm->freeze_noirq(dev); + suspend_report_result(drv->pm->freeze_noirq, error); } + if (!error) + pci_pm_set_unknown_state(pci_dev); + return error; } @@ -675,12 +663,10 @@ static int pci_pm_thaw(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - if (drv && drv->pm) { - if (drv->pm->thaw) - error = drv->pm->thaw(dev); - } else { - pci_pm_reenable_device(pci_dev); - } + pci_pm_reenable_device(pci_dev); + + if (drv && drv->pm && drv->pm->thaw) + error = drv->pm->thaw(dev); return error; } @@ -694,12 +680,10 @@ static int pci_pm_thaw_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - if (drv && drv->pm) { - if (drv->pm->thaw_noirq) - error = drv->pm->thaw_noirq(dev); - } else { - pci_update_current_state(pci_dev, PCI_D0); - } + pci_update_current_state(pci_dev, PCI_D0); + + if (drv && drv->pm && drv->pm->thaw_noirq) + error = drv->pm->thaw_noirq(dev); return error; } @@ -713,16 +697,13 @@ static int pci_pm_poweroff(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_suspend(dev, PMSG_HIBERNATE); - if (drv && drv->pm) { - if (drv->pm->poweroff) { - error = drv->pm->poweroff(dev); - suspend_report_result(drv->pm->poweroff, error); - } - } else { - pci_pm_default_suspend(pci_dev); + if (drv && drv->pm && drv->pm->poweroff) { + error = drv->pm->poweroff(dev); + suspend_report_result(drv->pm->poweroff, error); } - pci_fixup_device(pci_fixup_suspend, pci_dev); + if (!error) + pci_pm_default_suspend(pci_dev); return error; } @@ -735,11 +716,9 @@ static int pci_pm_poweroff_noirq(struct device *dev) if (pci_has_legacy_pm_support(to_pci_dev(dev))) return pci_legacy_suspend_late(dev, PMSG_HIBERNATE); - if (drv && drv->pm) { - if (drv->pm->poweroff_noirq) { - error = drv->pm->poweroff_noirq(dev); - suspend_report_result(drv->pm->poweroff_noirq, error); - } + if (drv && drv->pm && drv->pm->poweroff_noirq) { + error = drv->pm->poweroff_noirq(dev); + suspend_report_result(drv->pm->poweroff_noirq, error); } return error; @@ -754,14 +733,10 @@ static int pci_pm_restore(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume(dev); - if (drv && drv->pm) { - pci_fixup_device(pci_fixup_resume, pci_dev); + error = pci_pm_default_resume(pci_dev); - if (drv->pm->restore) - error = drv->pm->restore(dev); - } else { - error = pci_pm_default_resume(pci_dev); - } + if (!error && drv && drv->pm && drv->pm->restore) + error = drv->pm->restore(dev); return error; } @@ -775,14 +750,10 @@ static int pci_pm_restore_noirq(struct device *dev) if (pci_has_legacy_pm_support(pci_dev)) return pci_legacy_resume_early(dev); - if (drv && drv->pm) { - pci_fixup_device(pci_fixup_resume_early, pci_dev); + pci_pm_default_resume_noirq(pci_dev); - if (drv->pm->restore_noirq) - error = drv->pm->restore_noirq(dev); - } else { - pci_pm_default_resume_noirq(pci_dev); - } + if (drv && drv->pm && drv->pm->restore_noirq) + error = drv->pm->restore_noirq(dev); return error; } From f6dc1e5e3d4b523e1616b43beddb04e4fb1d376a Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 7 Jan 2009 13:12:22 +0100 Subject: [PATCH 98/98] PCI PM: Put PM callbacks in the order of execution Put PM callbacks in drivers/pci/pci-driver.c in the order in which they are executed which makes it much easier to follow the code. No functional changes should result from this. Signed-off-by: Rafael J. Wysocki Acked-by: Pavel Machek Signed-off-by: Jesse Barnes --- drivers/pci/pci-driver.c | 128 +++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index b7e67c2f40b9..c697f2680856 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -384,6 +384,19 @@ static int pci_legacy_suspend_late(struct device *dev, pm_message_t state) return i; } +static int pci_legacy_resume_early(struct device *dev) +{ + int error = 0; + struct pci_dev * pci_dev = to_pci_dev(dev); + struct pci_driver * drv = pci_dev->driver; + + pci_fixup_device(pci_fixup_resume_early, pci_dev); + + if (drv && drv->resume_early) + error = drv->resume_early(pci_dev); + return error; +} + static int pci_legacy_resume(struct device *dev) { int error; @@ -402,19 +415,6 @@ static int pci_legacy_resume(struct device *dev) return error; } -static int pci_legacy_resume_early(struct device *dev) -{ - int error = 0; - struct pci_dev * pci_dev = to_pci_dev(dev); - struct pci_driver * drv = pci_dev->driver; - - pci_fixup_device(pci_fixup_resume_early, pci_dev); - - if (drv && drv->resume_early) - error = drv->resume_early(pci_dev); - return error; -} - /* Auxiliary functions used by the new power management framework */ static int pci_restore_standard_config(struct pci_dev *pci_dev) @@ -569,23 +569,6 @@ static int pci_pm_suspend_noirq(struct device *dev) return error; } -static int pci_pm_resume(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; - int error = 0; - - if (pci_has_legacy_pm_support(pci_dev)) - return pci_legacy_resume(dev); - - error = pci_pm_default_resume(pci_dev); - - if (!error && drv && drv->pm && drv->pm->resume) - error = drv->pm->resume(dev); - - return error; -} - static int pci_pm_resume_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -603,6 +586,23 @@ static int pci_pm_resume_noirq(struct device *dev) return error; } +static int pci_pm_resume(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); + + error = pci_pm_default_resume(pci_dev); + + if (!error && drv && drv->pm && drv->pm->resume) + error = drv->pm->resume(dev); + + return error; +} + #else /* !CONFIG_SUSPEND */ #define pci_pm_suspend NULL @@ -654,23 +654,6 @@ static int pci_pm_freeze_noirq(struct device *dev) return error; } -static int pci_pm_thaw(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; - int error = 0; - - if (pci_has_legacy_pm_support(pci_dev)) - return pci_legacy_resume(dev); - - pci_pm_reenable_device(pci_dev); - - if (drv && drv->pm && drv->pm->thaw) - error = drv->pm->thaw(dev); - - return error; -} - static int pci_pm_thaw_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -688,6 +671,23 @@ static int pci_pm_thaw_noirq(struct device *dev) return error; } +static int pci_pm_thaw(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); + + pci_pm_reenable_device(pci_dev); + + if (drv && drv->pm && drv->pm->thaw) + error = drv->pm->thaw(dev); + + return error; +} + static int pci_pm_poweroff(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -724,23 +724,6 @@ static int pci_pm_poweroff_noirq(struct device *dev) return error; } -static int pci_pm_restore(struct device *dev) -{ - struct pci_dev *pci_dev = to_pci_dev(dev); - struct device_driver *drv = dev->driver; - int error = 0; - - if (pci_has_legacy_pm_support(pci_dev)) - return pci_legacy_resume(dev); - - error = pci_pm_default_resume(pci_dev); - - if (!error && drv && drv->pm && drv->pm->restore) - error = drv->pm->restore(dev); - - return error; -} - static int pci_pm_restore_noirq(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -758,6 +741,23 @@ static int pci_pm_restore_noirq(struct device *dev) return error; } +static int pci_pm_restore(struct device *dev) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + struct device_driver *drv = dev->driver; + int error = 0; + + if (pci_has_legacy_pm_support(pci_dev)) + return pci_legacy_resume(dev); + + error = pci_pm_default_resume(pci_dev); + + if (!error && drv && drv->pm && drv->pm->restore) + error = drv->pm->restore(dev); + + return error; +} + #else /* !CONFIG_HIBERNATION */ #define pci_pm_freeze NULL