From b50cac55bf859d5b2fdcc1803a553a251b703456 Mon Sep 17 00:00:00 2001 From: Neil Horman Date: Thu, 6 Oct 2011 14:08:18 -0400 Subject: PCI/sysfs: add per pci device msi[x] irq listing (v5) This patch adds a per-pci-device subdirectory in sysfs called: /sys/bus/pci/devices//msi_irqs This sub-directory exports the set of msi vectors allocated by a given pci device, by creating a numbered sub-directory for each vector beneath msi_irqs. For each vector various attributes can be exported. Currently the only attribute is called mode, which tracks the operational mode of that vector (msi vs. msix) Acked-by: Greg Kroah-Hartman Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 111 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 111 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index 0e6d04d7ba4..e6b6b9c6702 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -323,6 +323,8 @@ static void free_msi_irqs(struct pci_dev *dev) if (list_is_last(&entry->list, &dev->msi_list)) iounmap(entry->mask_base); } + kobject_del(&entry->kobj); + kobject_put(&entry->kobj); list_del(&entry->list); kfree(entry); } @@ -403,6 +405,98 @@ void pci_restore_msi_state(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_restore_msi_state); + +#define to_msi_attr(obj) container_of(obj, struct msi_attribute, attr) +#define to_msi_desc(obj) container_of(obj, struct msi_desc, kobj) + +struct msi_attribute { + struct attribute attr; + ssize_t (*show)(struct msi_desc *entry, struct msi_attribute *attr, + char *buf); + ssize_t (*store)(struct msi_desc *entry, struct msi_attribute *attr, + const char *buf, size_t count); +}; + +static ssize_t show_msi_mode(struct msi_desc *entry, struct msi_attribute *atr, + char *buf) +{ + return sprintf(buf, "%s\n", entry->msi_attrib.is_msix ? "msix" : "msi"); +} + +static ssize_t msi_irq_attr_show(struct kobject *kobj, + struct attribute *attr, char *buf) +{ + struct msi_attribute *attribute = to_msi_attr(attr); + struct msi_desc *entry = to_msi_desc(kobj); + + if (!attribute->show) + return -EIO; + + return attribute->show(entry, attribute, buf); +} + +static const struct sysfs_ops msi_irq_sysfs_ops = { + .show = msi_irq_attr_show, +}; + +static struct msi_attribute mode_attribute = + __ATTR(mode, S_IRUGO, show_msi_mode, NULL); + + +struct attribute *msi_irq_default_attrs[] = { + &mode_attribute.attr, + NULL +}; + +void msi_kobj_release(struct kobject *kobj) +{ + struct msi_desc *entry = to_msi_desc(kobj); + + pci_dev_put(entry->dev); +} + +static struct kobj_type msi_irq_ktype = { + .release = msi_kobj_release, + .sysfs_ops = &msi_irq_sysfs_ops, + .default_attrs = msi_irq_default_attrs, +}; + +static int populate_msi_sysfs(struct pci_dev *pdev) +{ + struct msi_desc *entry; + struct kobject *kobj; + int ret; + int count = 0; + + pdev->msi_kset = kset_create_and_add("msi_irqs", NULL, &pdev->dev.kobj); + if (!pdev->msi_kset) + return -ENOMEM; + + list_for_each_entry(entry, &pdev->msi_list, list) { + kobj = &entry->kobj; + kobj->kset = pdev->msi_kset; + pci_dev_get(pdev); + ret = kobject_init_and_add(kobj, &msi_irq_ktype, NULL, + "%u", entry->irq); + if (ret) + goto out_unroll; + + count++; + } + + return 0; + +out_unroll: + list_for_each_entry(entry, &pdev->msi_list, list) { + if (!count) + break; + kobject_del(&entry->kobj); + kobject_put(&entry->kobj); + count--; + } + return ret; +} + /** * msi_capability_init - configure device's MSI capability structure * @dev: pointer to the pci_dev data structure of MSI device function @@ -454,6 +548,13 @@ static int msi_capability_init(struct pci_dev *dev, int nvec) return ret; } + ret = populate_msi_sysfs(dev); + if (ret) { + msi_mask_irq(entry, mask, ~mask); + free_msi_irqs(dev); + return ret; + } + /* Set MSI enabled bits */ pci_intx_for_msi(dev, 0); msi_set_enable(dev, pos, 1); @@ -574,6 +675,12 @@ static int msix_capability_init(struct pci_dev *dev, msix_program_entries(dev, entries); + ret = populate_msi_sysfs(dev); + if (ret) { + ret = 0; + goto error; + } + /* Set MSI-X enabled bits and unmask the function */ pci_intx_for_msi(dev, 0); dev->msix_enabled = 1; @@ -732,6 +839,8 @@ void pci_disable_msi(struct pci_dev *dev) pci_msi_shutdown(dev); free_msi_irqs(dev); + kset_unregister(dev->msi_kset); + dev->msi_kset = NULL; } EXPORT_SYMBOL(pci_disable_msi); @@ -830,6 +939,8 @@ void pci_disable_msix(struct pci_dev *dev) pci_msix_shutdown(dev); free_msi_irqs(dev); + kset_unregister(dev->msi_kset); + dev->msi_kset = NULL; } EXPORT_SYMBOL(pci_disable_msix); -- cgit v1.2.3 From 69166fbf02c7a21745013f2de037bf7af26e4279 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Wed, 2 Nov 2011 14:07:15 -0600 Subject: PCI: Fix PRI and PASID consistency These are extended capabilities, rename and move to proper group for consistency. Signed-off-by: Alex Williamson Signed-off-by: Jesse Barnes --- drivers/pci/ats.c | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 7ec56fb0bd7..831e1920386 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -174,7 +174,7 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) u32 max_requests; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -205,7 +205,7 @@ void pci_disable_pri(struct pci_dev *pdev) u16 control; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return; @@ -226,7 +226,7 @@ bool pci_pri_enabled(struct pci_dev *pdev) u16 control; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return false; @@ -248,7 +248,7 @@ int pci_reset_pri(struct pci_dev *pdev) u16 control; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -281,7 +281,7 @@ bool pci_pri_stopped(struct pci_dev *pdev) u16 control, status; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return true; @@ -310,7 +310,7 @@ int pci_pri_status(struct pci_dev *pdev) u16 status, control; int pos; - pos = pci_find_ext_capability(pdev, PCI_PRI_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PRI); if (!pos) return -EINVAL; @@ -341,7 +341,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) u16 control, supported; int pos; - pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return -EINVAL; @@ -375,7 +375,7 @@ void pci_disable_pasid(struct pci_dev *pdev) u16 control = 0; int pos; - pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return; @@ -399,7 +399,7 @@ int pci_pasid_features(struct pci_dev *pdev) u16 supported; int pos; - pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return -EINVAL; @@ -425,7 +425,7 @@ int pci_max_pasids(struct pci_dev *pdev) u16 supported; int pos; - pos = pci_find_ext_capability(pdev, PCI_PASID_CAP); + pos = pci_find_ext_capability(pdev, PCI_EXT_CAP_ID_PASID); if (!pos) return -EINVAL; -- cgit v1.2.3 From 3c076351c4027a56d5005a39a0b518a4ba393ce2 Mon Sep 17 00:00:00 2001 From: Matthew Garrett Date: Thu, 10 Nov 2011 16:38:33 -0500 Subject: PCI: Rework ASPM disable code Right now we forcibly clear ASPM state on all devices if the BIOS indicates that the feature isn't supported. Based on the Microsoft presentation "PCI Express In Depth for Windows Vista and Beyond", I'm starting to think that this may be an error. The implication is that unless the platform grants full control via _OSC, Windows will not touch any PCIe features - including ASPM. In that case clearing ASPM state would be an error unless the platform has granted us that control. This patch reworks the ASPM disabling code such that the actual clearing of state is triggered by a successful handoff of PCIe control to the OS. The general ASPM code undergoes some changes in order to ensure that the ability to clear the bits isn't overridden by ASPM having already been disabled. Further, this theoretically now allows for situations where only a subset of PCIe roots hand over control, leaving the others in the BIOS state. It's difficult to know for sure that this is the right thing to do - there's zero public documentation on the interaction between all of these components. But enough vendors enable ASPM on platforms and then set this bit that it seems likely that they're expecting the OS to leave them alone. Measured to save around 5W on an idle Thinkpad X220. Signed-off-by: Matthew Garrett Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 1 - drivers/pci/pcie/aspm.c | 58 +++++++++++++++++++++++++++++++------------------ 2 files changed, 37 insertions(+), 22 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 4ecb6408b0d..c8e75851a31 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -395,7 +395,6 @@ static int __init acpi_pci_init(void) if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) { printk(KERN_INFO"ACPI FADT declares the system doesn't support PCIe ASPM, so disable it\n"); - pcie_clear_aspm(); pcie_no_aspm(); } diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index cbfbab18be9..1cfbf228fbb 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -68,7 +68,7 @@ struct pcie_link_state { struct aspm_latency acceptable[8]; }; -static int aspm_disabled, aspm_force, aspm_clear_state; +static int aspm_disabled, aspm_force; static bool aspm_support_enabled = true; static DEFINE_MUTEX(aspm_lock); static LIST_HEAD(link_list); @@ -500,9 +500,6 @@ static int pcie_aspm_sanity_check(struct pci_dev *pdev) int pos; u32 reg32; - if (aspm_clear_state) - return -EINVAL; - /* * Some functions in a slot might not all be PCIe functions, * very strange. Disable ASPM for the whole slot @@ -574,9 +571,6 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) pdev->pcie_type != PCI_EXP_TYPE_DOWNSTREAM) return; - if (aspm_disabled && !aspm_clear_state) - return; - /* VIA has a strange chipset, root port is under a bridge */ if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT && pdev->bus->self) @@ -608,7 +602,7 @@ void pcie_aspm_init_link_state(struct pci_dev *pdev) * the BIOS's expectation, we'll do so once pci_enable_device() is * called. */ - if (aspm_policy != POLICY_POWERSAVE || aspm_clear_state) { + if (aspm_policy != POLICY_POWERSAVE) { pcie_config_aspm_path(link); pcie_set_clkpm(link, policy_to_clkpm_state(link)); } @@ -649,8 +643,7 @@ void pcie_aspm_exit_link_state(struct pci_dev *pdev) struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link, *root, *parent_link; - if ((aspm_disabled && !aspm_clear_state) || !pci_is_pcie(pdev) || - !parent || !parent->link_state) + if (!pci_is_pcie(pdev) || !parent || !parent->link_state) return; if ((parent->pcie_type != PCI_EXP_TYPE_ROOT_PORT) && (parent->pcie_type != PCI_EXP_TYPE_DOWNSTREAM)) @@ -734,13 +727,18 @@ void pcie_aspm_powersave_config_link(struct pci_dev *pdev) * pci_disable_link_state - disable pci device's link state, so the link will * never enter specific states */ -static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) +static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem, + bool force) { struct pci_dev *parent = pdev->bus->self; struct pcie_link_state *link; - if (aspm_disabled || !pci_is_pcie(pdev)) + if (aspm_disabled && !force) + return; + + if (!pci_is_pcie(pdev)) return; + if (pdev->pcie_type == PCI_EXP_TYPE_ROOT_PORT || pdev->pcie_type == PCI_EXP_TYPE_DOWNSTREAM) parent = pdev; @@ -768,16 +766,31 @@ static void __pci_disable_link_state(struct pci_dev *pdev, int state, bool sem) void pci_disable_link_state_locked(struct pci_dev *pdev, int state) { - __pci_disable_link_state(pdev, state, false); + __pci_disable_link_state(pdev, state, false, false); } EXPORT_SYMBOL(pci_disable_link_state_locked); void pci_disable_link_state(struct pci_dev *pdev, int state) { - __pci_disable_link_state(pdev, state, true); + __pci_disable_link_state(pdev, state, true, false); } EXPORT_SYMBOL(pci_disable_link_state); +void pcie_clear_aspm(struct pci_bus *bus) +{ + struct pci_dev *child; + + /* + * Clear any ASPM setup that the firmware has carried out on this bus + */ + list_for_each_entry(child, &bus->devices, bus_list) { + __pci_disable_link_state(child, PCIE_LINK_STATE_L0S | + PCIE_LINK_STATE_L1 | + PCIE_LINK_STATE_CLKPM, + false, true); + } +} + static int pcie_aspm_set_policy(const char *val, struct kernel_param *kp) { int i; @@ -935,6 +948,7 @@ void pcie_aspm_remove_sysfs_dev_files(struct pci_dev *pdev) static int __init pcie_aspm_disable(char *str) { if (!strcmp(str, "off")) { + aspm_policy = POLICY_DEFAULT; aspm_disabled = 1; aspm_support_enabled = false; printk(KERN_INFO "PCIe ASPM is disabled\n"); @@ -947,16 +961,18 @@ static int __init pcie_aspm_disable(char *str) __setup("pcie_aspm=", pcie_aspm_disable); -void pcie_clear_aspm(void) -{ - if (!aspm_force) - aspm_clear_state = 1; -} - void pcie_no_aspm(void) { - if (!aspm_force) + /* + * Disabling ASPM is intended to prevent the kernel from modifying + * existing hardware state, not to clear existing state. To that end: + * (a) set policy to POLICY_DEFAULT in order to avoid changing state + * (b) prevent userspace from changing policy + */ + if (!aspm_force) { + aspm_policy = POLICY_DEFAULT; aspm_disabled = 1; + } } /** -- cgit v1.2.3 From 027e8d52abdd44bc00e405af83cd2fbfb96c0824 Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 7 Nov 2011 20:55:46 +0900 Subject: PCI: pciehp: Fix wrong workqueue cleanup Fix improper workqueue cleanup. In the current pciehp, pcied_cleanup() calls destroy_workqueue() before calling pcie_port_service_unregister(). This causes kernel oops because flush_workqueue() is called in the pcie_port_service_unregister() code path after the workqueue was destroyed. So pcied_cleanup() must call pcie_port_service_unregister() first before calling destroy_workqueue(). Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp_core.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index 7ac8358df8f..a13ad1308ad 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -366,9 +366,9 @@ static int __init pcied_init(void) static void __exit pcied_cleanup(void) { dbg("unload_pciehpd()\n"); + pcie_port_service_unregister(&hpdriver_portdrv); destroy_workqueue(pciehp_ordered_wq); destroy_workqueue(pciehp_wq); - pcie_port_service_unregister(&hpdriver_portdrv); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } -- cgit v1.2.3 From 486b10b9f43500741cd63a878d0ef23cd87fc66d Mon Sep 17 00:00:00 2001 From: Kenji Kaneshige Date: Mon, 7 Nov 2011 20:56:50 +0900 Subject: PCI: pciehp: Handle push button event asynchronously Use non-ordered workqueue for attention button events. Attention button events on each slot can be handled asynchronously. So we should use non-ordered workqueue. This patch also removes ordered workqueue in pciehp as a result. Signed-off-by: Kenji Kaneshige Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/pciehp.h | 1 - drivers/pci/hotplug/pciehp_core.c | 9 --------- drivers/pci/hotplug/pciehp_ctrl.c | 4 ++-- drivers/pci/hotplug/pciehp_hpc.c | 1 - 4 files changed, 2 insertions(+), 13 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/pciehp.h b/drivers/pci/hotplug/pciehp.h index 838f571027b..9a33fdde2d1 100644 --- a/drivers/pci/hotplug/pciehp.h +++ b/drivers/pci/hotplug/pciehp.h @@ -45,7 +45,6 @@ extern int pciehp_poll_time; extern int pciehp_debug; extern int pciehp_force; extern struct workqueue_struct *pciehp_wq; -extern struct workqueue_struct *pciehp_ordered_wq; #define dbg(format, arg...) \ do { \ diff --git a/drivers/pci/hotplug/pciehp_core.c b/drivers/pci/hotplug/pciehp_core.c index a13ad1308ad..b8c99d35ac9 100644 --- a/drivers/pci/hotplug/pciehp_core.c +++ b/drivers/pci/hotplug/pciehp_core.c @@ -43,7 +43,6 @@ int pciehp_poll_mode; int pciehp_poll_time; int pciehp_force; struct workqueue_struct *pciehp_wq; -struct workqueue_struct *pciehp_ordered_wq; #define DRIVER_VERSION "0.4" #define DRIVER_AUTHOR "Dan Zink , Greg Kroah-Hartman , Dely Sy " @@ -345,18 +344,11 @@ static int __init pcied_init(void) if (!pciehp_wq) return -ENOMEM; - pciehp_ordered_wq = alloc_ordered_workqueue("pciehp_ordered", 0); - if (!pciehp_ordered_wq) { - destroy_workqueue(pciehp_wq); - return -ENOMEM; - } - 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"); if (retval) { - destroy_workqueue(pciehp_ordered_wq); destroy_workqueue(pciehp_wq); dbg("Failure to register service\n"); } @@ -367,7 +359,6 @@ static void __exit pcied_cleanup(void) { dbg("unload_pciehpd()\n"); pcie_port_service_unregister(&hpdriver_portdrv); - destroy_workqueue(pciehp_ordered_wq); destroy_workqueue(pciehp_wq); info(DRIVER_DESC " version: " DRIVER_VERSION " unloaded\n"); } diff --git a/drivers/pci/hotplug/pciehp_ctrl.c b/drivers/pci/hotplug/pciehp_ctrl.c index 085dbb5fc16..27f44295a65 100644 --- a/drivers/pci/hotplug/pciehp_ctrl.c +++ b/drivers/pci/hotplug/pciehp_ctrl.c @@ -344,7 +344,7 @@ void pciehp_queue_pushbutton_work(struct work_struct *work) kfree(info); goto out; } - queue_work(pciehp_ordered_wq, &info->work); + queue_work(pciehp_wq, &info->work); out: mutex_unlock(&p_slot->lock); } @@ -439,7 +439,7 @@ static void handle_surprise_event(struct slot *p_slot) else p_slot->state = POWERON_STATE; - queue_work(pciehp_ordered_wq, &info->work); + queue_work(pciehp_wq, &info->work); } static void interrupt_event_handler(struct work_struct *work) diff --git a/drivers/pci/hotplug/pciehp_hpc.c b/drivers/pci/hotplug/pciehp_hpc.c index 7b1414810ae..bcdbb164362 100644 --- a/drivers/pci/hotplug/pciehp_hpc.c +++ b/drivers/pci/hotplug/pciehp_hpc.c @@ -806,7 +806,6 @@ static void pcie_cleanup_slot(struct controller *ctrl) struct slot *slot = ctrl->slot; cancel_delayed_work(&slot->work); flush_workqueue(pciehp_wq); - flush_workqueue(pciehp_ordered_wq); kfree(slot); } -- cgit v1.2.3 From d90116ea38f7768dac0349f01ffbc2663d63b7e9 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 6 Nov 2011 23:11:28 +0100 Subject: PCI/ACPI: Make acpiphp ignore root bridges using SHPC native hotplug If the kernel has requested control of the SHPC native hotplug feature for a given root bridge, the acpiphp driver should not try to handle that root bridge and it should leave it to shpchp. Failing to do so causes problems to happen if shpchp is loaded and unloaded before loading acpiphp (ACPI-based hotplug won't work in that case anyway). To address this issue make find_root_bridges() ignore PCI root bridges with SHPC native hotplug enabled and make add_bridge() return error code if SHPC native hotplug is enabled for the given root bridge. This causes acpiphp to refuse to load if SHPC native hotplug is enabled for all root bridges and to refuse binding to the root bridges with SHPC native hotplug enabled. Reviewed-by: Kenji Kaneshige Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/hotplug/acpiphp_glue.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c index fce1c54a0c8..ba43c037de8 100644 --- a/drivers/pci/hotplug/acpiphp_glue.c +++ b/drivers/pci/hotplug/acpiphp_glue.c @@ -467,7 +467,7 @@ static int add_bridge(acpi_handle handle) * granted by the BIOS for it. */ root = acpi_pci_find_root(handle); - if (root && (root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL)) + if (root && (root->osc_control_set & OSC_PCI_NATIVE_HOTPLUG)) return -ENODEV; /* if the bridge doesn't have _STA, we assume it is always there */ @@ -1395,7 +1395,7 @@ find_root_bridges(acpi_handle handle, u32 lvl, void *context, void **rv) if (!root) return AE_OK; - if (root->osc_control_set & OSC_PCI_EXPRESS_NATIVE_HP_CONTROL) + if (root->osc_control_set & OSC_PCI_NATIVE_HOTPLUG) return AE_OK; (*count)++; -- cgit v1.2.3 From a424948dde8421089826e2f782d0efe9e565707e Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 6 Nov 2011 22:21:46 +0100 Subject: PCI/ACPI/PM: Avoid resuming devices that don't signal PME Modify pci_acpi_wake_dev() to avoid resuming PME-capable devices whose PME Status bits are not set, which may happen currently if several devices are associated with the same wakeup GPE and all of them are notified whenever at least one of them signals PME. Signed-off-by: Rafael J. Wysocki Signed-off-by: Jesse Barnes --- drivers/pci/pci-acpi.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index c8e75851a31..060fd22a110 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -45,16 +45,20 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context) { struct pci_dev *pci_dev = context; - if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) { + if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev) + return; + + if (!pci_dev->pm_cap || !pci_dev->pme_support + || pci_check_pme_status(pci_dev)) { if (pci_dev->pme_poll) pci_dev->pme_poll = false; pci_wakeup_event(pci_dev); - pci_check_pme_status(pci_dev); pm_runtime_resume(&pci_dev->dev); - if (pci_dev->subordinate) - pci_pme_wakeup_bus(pci_dev->subordinate); } + + if (pci_dev->subordinate) + pci_pme_wakeup_bus(pci_dev->subordinate); } /** -- cgit v1.2.3 From a776c491ca5e38c26d9f66923ff574d041e747f4 Mon Sep 17 00:00:00 2001 From: "Eric W. Biederman" Date: Mon, 17 Oct 2011 11:46:06 -0700 Subject: PCI: msi: Disable msi interrupts when we initialize a pci device I traced a nasty kexec on panic boot failure to the fact that we had screaming msi interrupts and we were not disabling the msi messages at kernel startup. The booting kernel had not enabled those interupts so was not prepared to handle them. I can see no reason why we would ever want to leave the msi interrupts enabled at boot if something else has enabled those interrupts. The pci spec specifies that msi interrupts should be off by default. Drivers are expected to enable the msi interrupts if they want to use them. Our interrupt handling code reprograms the interrupt handlers at boot and will not be be able to do anything useful with an unexpected interrupt. This patch applies cleanly all of the way back to 2.6.32 where I noticed the problem. Cc: stable@kernel.org Signed-off-by: Eric W. Biederman Signed-off-by: Jesse Barnes --- drivers/pci/msi.c | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'drivers/pci') diff --git a/drivers/pci/msi.c b/drivers/pci/msi.c index e6b6b9c6702..337e16ab4a9 100644 --- a/drivers/pci/msi.c +++ b/drivers/pci/msi.c @@ -981,5 +981,15 @@ EXPORT_SYMBOL(pci_msi_enabled); void pci_msi_init_pci_dev(struct pci_dev *dev) { + int pos; INIT_LIST_HEAD(&dev->msi_list); + + /* Disable the msi hardware to avoid screaming interrupts + * during boot. This is the power on reset default so + * usually this should be a noop. + */ + pos = pci_find_capability(dev, PCI_CAP_ID_MSI); + if (pos) + msi_set_enable(dev, pos, 0); + msix_set_enable(dev, 0); } -- cgit v1.2.3 From 60fe823837d10673500ff685c01eb2f896fe5849 Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 11 Nov 2011 10:06:56 -0700 Subject: PCI: Enable is not exposed as a PASID capability The PASID ECN indicates bit 0 is reserved in the capability register. Switch pci_enable_pasid() to error if PASID is already enabled and don't expose enable as a feature in pci_pasid_features(). Reviewed-by: Joerg Roedel Tested-by: Joerg Roedel Signed-off-by: Alex Williamson Signed-off-by: Jesse Barnes --- drivers/pci/ats.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 831e1920386..8e95a123d37 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -348,7 +348,7 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) pci_read_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, &control); pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); - if (!(supported & PCI_PASID_ENABLE)) + if (control & PCI_PASID_ENABLE) return -EINVAL; supported &= PCI_PASID_EXEC | PCI_PASID_PRIV; @@ -390,7 +390,6 @@ EXPORT_SYMBOL_GPL(pci_disable_pasid); * Returns a negative value when no PASI capability is present. * Otherwise is returns a bitmask with supported features. Current * features reported are: - * PCI_PASID_ENABLE - PASID capability can be enabled * PCI_PASID_EXEC - Execute permission supported * PCI_PASID_PRIV - Priviledged mode supported */ @@ -405,7 +404,7 @@ int pci_pasid_features(struct pci_dev *pdev) pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); - supported &= PCI_PASID_ENABLE | PCI_PASID_EXEC | PCI_PASID_PRIV; + supported &= PCI_PASID_EXEC | PCI_PASID_PRIV; return supported; } -- cgit v1.2.3 From 91f57d5e1be3db1e079c8696f1eab214f1c7922d Mon Sep 17 00:00:00 2001 From: Alex Williamson Date: Fri, 11 Nov 2011 10:07:36 -0700 Subject: PCI: More PRI/PASID cleanup More consistency cleanups. Drop the _OFF, separate and indent CTRL/CAP/STATUS bit definitions. This helped find the previous mis-use of bit 0 in the PASID capability register. Reviewed-by: Joerg Roedel Tested-by: Joerg Roedel Signed-off-by: Alex Williamson Signed-off-by: Jesse Barnes --- drivers/pci/ats.c | 69 ++++++++++++++++++++++++++++--------------------------- 1 file changed, 35 insertions(+), 34 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/ats.c b/drivers/pci/ats.c index 8e95a123d37..2df49af6cc9 100644 --- a/drivers/pci/ats.c +++ b/drivers/pci/ats.c @@ -178,17 +178,18 @@ int pci_enable_pri(struct pci_dev *pdev, u32 reqs) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); - pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); - if ((control & PCI_PRI_ENABLE) || !(status & PCI_PRI_STATUS_STOPPED)) + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); + pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); + if ((control & PCI_PRI_CTRL_ENABLE) || + !(status & PCI_PRI_STATUS_STOPPED)) return -EBUSY; - pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ_OFF, &max_requests); + pci_read_config_dword(pdev, pos + PCI_PRI_MAX_REQ, &max_requests); reqs = min(max_requests, reqs); - pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ_OFF, reqs); + pci_write_config_dword(pdev, pos + PCI_PRI_ALLOC_REQ, reqs); - control |= PCI_PRI_ENABLE; - pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); + control |= PCI_PRI_CTRL_ENABLE; + pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); return 0; } @@ -209,9 +210,9 @@ void pci_disable_pri(struct pci_dev *pdev) if (!pos) return; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); - control &= ~PCI_PRI_ENABLE; - pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); + control &= ~PCI_PRI_CTRL_ENABLE; + pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); } EXPORT_SYMBOL_GPL(pci_disable_pri); @@ -230,9 +231,9 @@ bool pci_pri_enabled(struct pci_dev *pdev) if (!pos) return false; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); - return (control & PCI_PRI_ENABLE) ? true : false; + return (control & PCI_PRI_CTRL_ENABLE) ? true : false; } EXPORT_SYMBOL_GPL(pci_pri_enabled); @@ -252,13 +253,13 @@ int pci_reset_pri(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); - if (control & PCI_PRI_ENABLE) + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); + if (control & PCI_PRI_CTRL_ENABLE) return -EBUSY; - control |= PCI_PRI_RESET; + control |= PCI_PRI_CTRL_RESET; - pci_write_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, control); + pci_write_config_word(pdev, pos + PCI_PRI_CTRL, control); return 0; } @@ -285,10 +286,10 @@ bool pci_pri_stopped(struct pci_dev *pdev) if (!pos) return true; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); - pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); + pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); - if (control & PCI_PRI_ENABLE) + if (control & PCI_PRI_CTRL_ENABLE) return false; return (status & PCI_PRI_STATUS_STOPPED) ? true : false; @@ -314,11 +315,11 @@ int pci_pri_status(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PRI_CONTROL_OFF, &control); - pci_read_config_word(pdev, pos + PCI_PRI_STATUS_OFF, &status); + pci_read_config_word(pdev, pos + PCI_PRI_CTRL, &control); + pci_read_config_word(pdev, pos + PCI_PRI_STATUS, &status); /* Stopped bit is undefined when enable == 1, so clear it */ - if (control & PCI_PRI_ENABLE) + if (control & PCI_PRI_CTRL_ENABLE) status &= ~PCI_PRI_STATUS_STOPPED; return status; @@ -345,21 +346,21 @@ int pci_enable_pasid(struct pci_dev *pdev, int features) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, &control); - pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + pci_read_config_word(pdev, pos + PCI_PASID_CTRL, &control); + pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported); - if (control & PCI_PASID_ENABLE) + if (control & PCI_PASID_CTRL_ENABLE) return -EINVAL; - supported &= PCI_PASID_EXEC | PCI_PASID_PRIV; + supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; /* User wants to enable anything unsupported? */ if ((supported & features) != features) return -EINVAL; - control = PCI_PASID_ENABLE | features; + control = PCI_PASID_CTRL_ENABLE | features; - pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control); + pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); return 0; } @@ -379,7 +380,7 @@ void pci_disable_pasid(struct pci_dev *pdev) if (!pos) return; - pci_write_config_word(pdev, pos + PCI_PASID_CONTROL_OFF, control); + pci_write_config_word(pdev, pos + PCI_PASID_CTRL, control); } EXPORT_SYMBOL_GPL(pci_disable_pasid); @@ -390,8 +391,8 @@ EXPORT_SYMBOL_GPL(pci_disable_pasid); * Returns a negative value when no PASI capability is present. * Otherwise is returns a bitmask with supported features. Current * features reported are: - * PCI_PASID_EXEC - Execute permission supported - * PCI_PASID_PRIV - Priviledged mode supported + * PCI_PASID_CAP_EXEC - Execute permission supported + * PCI_PASID_CAP_PRIV - Priviledged mode supported */ int pci_pasid_features(struct pci_dev *pdev) { @@ -402,9 +403,9 @@ int pci_pasid_features(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported); - supported &= PCI_PASID_EXEC | PCI_PASID_PRIV; + supported &= PCI_PASID_CAP_EXEC | PCI_PASID_CAP_PRIV; return supported; } @@ -428,7 +429,7 @@ int pci_max_pasids(struct pci_dev *pdev) if (!pos) return -EINVAL; - pci_read_config_word(pdev, pos + PCI_PASID_CAP_OFF, &supported); + pci_read_config_word(pdev, pos + PCI_PASID_CAP, &supported); supported = (supported & PASID_NUMBER_MASK) >> PASID_NUMBER_SHIFT; -- cgit v1.2.3