From d2e5f0c16ad60a7208fd371233e63b73c990ece2 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 23 Dec 2012 00:02:44 +0100 Subject: ACPI / PCI: Rework the setup and cleanup of device wakeup Currently, the ACPI wakeup capability of PCI devices is set up in two different places, partially in acpi_pci_bind() where runtime wakeup is initialized and partially in platform_pci_wakeup_init(), where system wakeup is initialized. The cleanup is only done in acpi_pci_unbind() and it only covers runtime wakeup. Use the new .setup() and .cleanup() callbacks in struct acpi_bus_type to consolidate that code and do the setup and the cleanup each in one place. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/pci/pci-acpi.c | 30 +++++++++++++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 1af4008182fd..b98106c110e7 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -283,7 +283,6 @@ static struct pci_platform_pm_ops acpi_pci_platform_pm = { .is_manageable = acpi_pci_power_manageable, .set_state = acpi_pci_set_power_state, .choose_state = acpi_pci_choose_state, - .can_wakeup = acpi_pci_can_wakeup, .sleep_wake = acpi_pci_sleep_wake, .run_wake = acpi_pci_run_wake, }; @@ -321,10 +320,39 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) return 0; } +static void acpi_pci_wakeup_setup(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + struct pci_dev *pci_dev = to_pci_dev(dev); + + if (!adev || !adev->wakeup.flags.valid) + return; + + device_set_wakeup_capable(dev, true); + acpi_pci_sleep_wake(pci_dev, false); + + pci_acpi_add_pm_notifier(adev, pci_dev); + if (adev->wakeup.flags.run_wake) + device_set_run_wake(dev, true); +} + +static void acpi_pci_wakeup_cleanup(struct device *dev) +{ + struct acpi_device *adev = acpi_dev_pm_get_node(dev); + + if (adev && adev->wakeup.flags.valid) { + device_set_wakeup_capable(dev, false); + device_set_run_wake(dev, false); + pci_acpi_remove_pm_notifier(adev); + } +} + static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, .find_device = acpi_pci_find_device, .find_bridge = acpi_pci_find_root_bridge, + .setup = acpi_pci_wakeup_setup, + .cleanup = acpi_pci_wakeup_cleanup, }; static int __init acpi_pci_init(void) -- cgit v1.2.3 From 38a9a67a281eeebcd7cccf87f0e371f58ae625e3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 23 Dec 2012 00:02:54 +0100 Subject: ACPI / PCI: Move the _PRT setup and cleanup code to pci-acpi.c Move the code related to _PRT setup and removal and to power resources from acpi_pci_bind() and acpi_pci_unbind() to the .setup() and .cleanup() callbacks in acpi_pci_bus and remove acpi_pci_bind() and acpi_pci_unbind() that have no purpose any more. Accordingly, remove the code related to device .bind() and .unbind() operations from the ACPI PCI root bridge driver. Signed-off-by: Rafael J. Wysocki Acked-by: Yinghai Lu Acked-by: Toshi Kani --- drivers/acpi/Makefile | 2 +- drivers/acpi/pci_bind.c | 117 ------------------------------------------------ drivers/acpi/pci_root.c | 31 ------------- drivers/pci/pci-acpi.c | 44 ++++++++++++++---- 4 files changed, 37 insertions(+), 157 deletions(-) delete mode 100644 drivers/acpi/pci_bind.c (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index 2a4502becd13..4ee2e753306a 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -37,7 +37,7 @@ acpi-y += resource.o acpi-y += processor_core.o acpi-y += ec.o acpi-$(CONFIG_ACPI_DOCK) += dock.o -acpi-y += pci_root.o pci_link.o pci_irq.o pci_bind.o +acpi-y += pci_root.o pci_link.o pci_irq.o acpi-y += acpi_platform.o acpi-y += power.o acpi-y += event.o diff --git a/drivers/acpi/pci_bind.c b/drivers/acpi/pci_bind.c deleted file mode 100644 index bbddcc9c894f..000000000000 --- a/drivers/acpi/pci_bind.c +++ /dev/null @@ -1,117 +0,0 @@ -/* - * pci_bind.c - ACPI PCI Device Binding ($Revision: 2 $) - * - * Copyright (C) 2001, 2002 Andy Grover - * Copyright (C) 2001, 2002 Paul Diefenbaugh - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * 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. 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., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. - * - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - */ - -#include -#include -#include -#include -#include -#include -#include -#include - -#define _COMPONENT ACPI_PCI_COMPONENT -ACPI_MODULE_NAME("pci_bind"); - -static int acpi_pci_unbind(struct acpi_device *device) -{ - struct pci_dev *dev; - - dev = acpi_get_pci_dev(device->handle); - if (!dev) - goto out; - - acpi_power_resource_unregister_device(&dev->dev, device->handle); - - if (!dev->subordinate) - goto out; - - acpi_pci_irq_del_prt(pci_domain_nr(dev->bus), dev->subordinate->number); - - device->ops.bind = NULL; - device->ops.unbind = NULL; - -out: - pci_dev_put(dev); - return 0; -} - -static int acpi_pci_bind(struct acpi_device *device) -{ - acpi_status status; - acpi_handle handle; - unsigned char bus; - struct pci_dev *dev; - - dev = acpi_get_pci_dev(device->handle); - if (!dev) - return 0; - - acpi_power_resource_register_device(&dev->dev, device->handle); - - /* - * Install the 'bind' function to facilitate callbacks for - * children of the P2P bridge. - */ - if (dev->subordinate) { - ACPI_DEBUG_PRINT((ACPI_DB_INFO, - "Device %04x:%02x:%02x.%d is a PCI bridge\n", - pci_domain_nr(dev->bus), dev->bus->number, - PCI_SLOT(dev->devfn), PCI_FUNC(dev->devfn))); - device->ops.bind = acpi_pci_bind; - device->ops.unbind = acpi_pci_unbind; - } - - /* - * Evaluate and parse _PRT, if exists. This code allows parsing of - * _PRT objects within the scope of non-bridge devices. Note that - * _PRTs within the scope of a PCI bridge assume the bridge's - * subordinate bus number. - * - * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? - */ - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_FAILURE(status)) - goto out; - - if (dev->subordinate) - bus = dev->subordinate->number; - else - bus = dev->bus->number; - - acpi_pci_irq_add_prt(device->handle, pci_domain_nr(dev->bus), bus); - -out: - pci_dev_put(dev); - return 0; -} - -int acpi_pci_bind_root(struct acpi_device *device) -{ - device->ops.bind = acpi_pci_bind; - device->ops.unbind = acpi_pci_unbind; - - return 0; -} diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 0e593a203ff8..22a8458b4ec9 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -186,21 +186,6 @@ static acpi_status try_get_root_bridge_busnr(acpi_handle handle, return AE_OK; } -static void acpi_pci_bridge_scan(struct acpi_device *device) -{ - int status; - struct acpi_device *child = NULL; - - if (device->flags.bus_address) - if (device->parent && device->parent->ops.bind) { - status = device->parent->ops.bind(device); - if (!status) { - list_for_each_entry(child, &device->children, node) - acpi_pci_bridge_scan(child); - } - } -} - static u8 pci_osc_uuid_str[] = "33DB4D5B-1FF7-401C-9657-7441C03DD766"; static acpi_status acpi_pci_run_osc(acpi_handle handle, @@ -450,7 +435,6 @@ static int acpi_pci_root_add(struct acpi_device *device) int result; struct acpi_pci_root *root; acpi_handle handle; - struct acpi_device *child; struct acpi_pci_driver *driver; u32 flags, base_flags; bool is_osc_granted = false; @@ -602,21 +586,6 @@ static int acpi_pci_root_add(struct acpi_device *device) goto out_del_root; } - /* - * Attach ACPI-PCI Context - * ----------------------- - * Thus binding the ACPI and PCI devices. - */ - result = acpi_pci_bind_root(device); - if (result) - goto out_del_root; - - /* - * Scan and bind all _ADR-Based Devices - */ - list_for_each_entry(child, &device->children, node) - acpi_pci_bridge_scan(child); - /* ASPM setting */ if (is_osc_granted) { if (acpi_gbl_FADT.boot_flags & ACPI_FADT_NO_ASPM) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index b98106c110e7..42736e213f25 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -320,12 +320,33 @@ static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) return 0; } -static void acpi_pci_wakeup_setup(struct device *dev) +static void pci_acpi_setup(struct device *dev) { - struct acpi_device *adev = acpi_dev_pm_get_node(dev); struct pci_dev *pci_dev = to_pci_dev(dev); + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_device *adev; + acpi_status status; + acpi_handle dummy; - if (!adev || !adev->wakeup.flags.valid) + /* + * Evaluate and parse _PRT, if exists. This code allows parsing of + * _PRT objects within the scope of non-bridge devices. Note that + * _PRTs within the scope of a PCI bridge assume the bridge's + * subordinate bus number. + * + * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? + */ + status = acpi_get_handle(handle, METHOD_NAME__PRT, &dummy); + if (ACPI_SUCCESS(status)) { + unsigned char bus; + + bus = pci_dev->subordinate ? + pci_dev->subordinate->number : pci_dev->bus->number; + acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); + } + + acpi_power_resource_register_device(dev, handle); + if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) return; device_set_wakeup_capable(dev, true); @@ -336,23 +357,30 @@ static void acpi_pci_wakeup_setup(struct device *dev) device_set_run_wake(dev, true); } -static void acpi_pci_wakeup_cleanup(struct device *dev) +static void pci_acpi_cleanup(struct device *dev) { - struct acpi_device *adev = acpi_dev_pm_get_node(dev); + struct pci_dev *pci_dev = to_pci_dev(dev); + acpi_handle handle = ACPI_HANDLE(dev); + struct acpi_device *adev; - if (adev && adev->wakeup.flags.valid) { + if (!acpi_bus_get_device(handle, &adev) && adev->wakeup.flags.valid) { device_set_wakeup_capable(dev, false); device_set_run_wake(dev, false); pci_acpi_remove_pm_notifier(adev); } + acpi_power_resource_unregister_device(dev, handle); + + if (pci_dev->subordinate) + acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), + pci_dev->subordinate->number); } static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, .find_device = acpi_pci_find_device, .find_bridge = acpi_pci_find_root_bridge, - .setup = acpi_pci_wakeup_setup, - .cleanup = acpi_pci_wakeup_cleanup, + .setup = pci_acpi_setup, + .cleanup = pci_acpi_cleanup, }; static int __init acpi_pci_init(void) -- cgit v1.2.3 From 6c0cc950ae670403a362bdcbf3cde0df33744928 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 9 Jan 2013 22:33:37 +0100 Subject: ACPI / PCI: Set root bridge ACPI handle in advance The ACPI handles of PCI root bridges need to be known to acpi_bind_one(), so that it can create the appropriate "firmware_node" and "physical_node" files for them, but currently the way it gets to know those handles is not exactly straightforward (to put it lightly). This is how it works, roughly: 1. acpi_bus_scan() finds the handle of a PCI root bridge, creates a struct acpi_device object for it and passes that object to acpi_pci_root_add(). 2. acpi_pci_root_add() creates a struct acpi_pci_root object, populates its "device" field with its argument's address (device->handle is the ACPI handle found in step 1). 3. The struct acpi_pci_root object created in step 2 is passed to pci_acpi_scan_root() and used to get resources that are passed to pci_create_root_bus(). 4. pci_create_root_bus() creates a struct pci_host_bridge object and passes its "dev" member to device_register(). 5. platform_notify(), which for systems with ACPI is set to acpi_platform_notify(), is called. So far, so good. Now it starts to be "interesting". 6. acpi_find_bridge_device() is used to find the ACPI handle of the given device (which is the PCI root bridge) and executes acpi_pci_find_root_bridge(), among other things, for the given device object. 7. acpi_pci_find_root_bridge() uses the name (sic!) of the given device object to extract the segment and bus numbers of the PCI root bridge and passes them to acpi_get_pci_rootbridge_handle(). 8. acpi_get_pci_rootbridge_handle() browses the list of ACPI PCI root bridges and finds the one that matches the given segment and bus numbers. Its handle is then used to initialize the ACPI handle of the PCI root bridge's device object by acpi_bind_one(). However, this is *exactly* the ACPI handle we started with in step 1. Needless to say, this is quite embarassing, but it may be avoided thanks to commit f3fd0c8 (ACPI: Allow ACPI handles of devices to be initialized in advance), which makes it possible to initialize the ACPI handle of a device before passing it to device_register(). Accordingly, add a new __weak routine, pcibios_root_bridge_prepare(), defaulting to an empty implementation that can be replaced by the interested architecutres (x86 and ia64 at the moment) with functions that will set the root bridge's ACPI handle before its dev member is passed to device_register(). Make both x86 and ia64 provide such implementations of pcibios_root_bridge_prepare() and remove acpi_pci_find_root_bridge() and acpi_get_pci_rootbridge_handle() that aren't necessary any more. Included is a fix for breakage on systems with non-ACPI PCI host bridges from Bjorn Helgaas. Signed-off-by: Rafael J. Wysocki Signed-off-by: Bjorn Helgaas --- arch/ia64/pci/pci.c | 8 ++++++++ arch/x86/include/asm/pci.h | 3 +++ arch/x86/pci/acpi.c | 9 +++++++++ drivers/acpi/pci_root.c | 18 ------------------ drivers/pci/pci-acpi.c | 19 ------------------- drivers/pci/probe.c | 16 ++++++++++++++++ include/acpi/acpi_bus.h | 1 - include/linux/pci.h | 2 ++ 8 files changed, 38 insertions(+), 38 deletions(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/arch/ia64/pci/pci.c b/arch/ia64/pci/pci.c index 5faa66c5c2a8..00e59c7ad3c0 100644 --- a/arch/ia64/pci/pci.c +++ b/arch/ia64/pci/pci.c @@ -396,6 +396,14 @@ out1: return NULL; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_controller *controller = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, controller->acpi_handle); + return 0; +} + static int __devinit is_valid_resource(struct pci_dev *dev, int idx) { unsigned int i, type_mask = IORESOURCE_IO | IORESOURCE_MEM; diff --git a/arch/x86/include/asm/pci.h b/arch/x86/include/asm/pci.h index dba7805176bf..9f437e97e9e8 100644 --- a/arch/x86/include/asm/pci.h +++ b/arch/x86/include/asm/pci.h @@ -14,6 +14,9 @@ struct pci_sysdata { int domain; /* PCI domain */ int node; /* NUMA node */ +#ifdef CONFIG_ACPI + void *acpi; /* ACPI-specific data */ +#endif #ifdef CONFIG_X86_64 void *iommu; /* IOMMU private data */ #endif diff --git a/arch/x86/pci/acpi.c b/arch/x86/pci/acpi.c index 0c01261fe5a8..3d49094ed3e8 100644 --- a/arch/x86/pci/acpi.c +++ b/arch/x86/pci/acpi.c @@ -522,6 +522,7 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) sd = &info->sd; sd->domain = domain; sd->node = node; + sd->acpi = device->handle; /* * Maybe the desired pci bus has been already scanned. In such case * it is unnecessary to scan the pci bus with the given domain,busnum. @@ -593,6 +594,14 @@ struct pci_bus * __devinit pci_acpi_scan_root(struct acpi_pci_root *root) return bus; } +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + struct pci_sysdata *sd = bridge->bus->sysdata; + + ACPI_HANDLE_SET(&bridge->dev, sd->acpi); + return 0; +} + int __init pci_acpi_init(void) { struct pci_dev *dev = NULL; diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index 471b2dcb1c67..bf5108ad4d63 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -107,24 +107,6 @@ void acpi_pci_unregister_driver(struct acpi_pci_driver *driver) } EXPORT_SYMBOL(acpi_pci_unregister_driver); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int seg, unsigned int bus) -{ - struct acpi_pci_root *root; - acpi_handle handle = NULL; - - mutex_lock(&acpi_pci_root_lock); - list_for_each_entry(root, &acpi_pci_roots, node) - if ((root->segment == (u16) seg) && - (root->secondary.start == (u16) bus)) { - handle = root->device->handle; - break; - } - mutex_unlock(&acpi_pci_root_lock); - return handle; -} - -EXPORT_SYMBOL_GPL(acpi_get_pci_rootbridge_handle); - /** * acpi_is_root_bridge - determine whether an ACPI CA node is a PCI root bridge * @handle - the ACPI CA node in question. diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..1c2587c40299 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -302,24 +302,6 @@ static int acpi_pci_find_device(struct device *dev, acpi_handle *handle) return 0; } -static int acpi_pci_find_root_bridge(struct device *dev, acpi_handle *handle) -{ - int num; - unsigned int seg, bus; - - /* - * The string should be the same as root bridge's name - * Please look at 'pci_scan_bus_parented' - */ - num = sscanf(dev_name(dev), "pci%04x:%02x", &seg, &bus); - if (num != 2) - return -ENODEV; - *handle = acpi_get_pci_rootbridge_handle(seg, bus); - if (!*handle) - return -ENODEV; - return 0; -} - static void pci_acpi_setup(struct device *dev) { struct pci_dev *pci_dev = to_pci_dev(dev); @@ -378,7 +360,6 @@ static void pci_acpi_cleanup(struct device *dev) static struct acpi_bus_type acpi_pci_bus = { .bus = &pci_bus_type, .find_device = acpi_pci_find_device, - .find_bridge = acpi_pci_find_root_bridge, .setup = pci_acpi_setup, .cleanup = pci_acpi_cleanup, }; diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 2dcd22d9c816..bbe4be7fc685 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1632,6 +1632,18 @@ unsigned int pci_scan_child_bus(struct pci_bus *bus) return max; } +/** + * pcibios_root_bridge_prepare - Platform-specific host bridge setup. + * @bridge: Host bridge to set up. + * + * Default empty implementation. Replace with an architecture-specific setup + * routine, if necessary. + */ +int __weak pcibios_root_bridge_prepare(struct pci_host_bridge *bridge) +{ + return 0; +} + struct pci_bus *pci_create_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { @@ -1665,6 +1677,10 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, bridge->dev.parent = parent; bridge->dev.release = pci_release_bus_bridge_dev; dev_set_name(&bridge->dev, "pci%04x:%02x", pci_domain_nr(b), bus); + error = pcibios_root_bridge_prepare(bridge); + if (error) + goto bridge_dev_reg_err; + error = device_register(&bridge->dev); if (error) goto bridge_dev_reg_err; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index a9e1421cd007..796ccc3247df 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -402,7 +402,6 @@ struct acpi_pci_root { /* helper */ acpi_handle acpi_get_child(acpi_handle, u64); int acpi_is_root_bridge(acpi_handle); -acpi_handle acpi_get_pci_rootbridge_handle(unsigned int, unsigned int); struct acpi_pci_root *acpi_pci_find_root(acpi_handle handle); #define DEVICE_ACPI_HANDLE(dev) ((acpi_handle)ACPI_HANDLE(dev)) diff --git a/include/linux/pci.h b/include/linux/pci.h index 907b455ab603..6860f4dec997 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -378,6 +378,8 @@ void pci_set_host_bridge_release(struct pci_host_bridge *bridge, void (*release_fn)(struct pci_host_bridge *), void *release_data); +int pcibios_root_bridge_prepare(struct pci_host_bridge *bridge); + /* * The first PCI_BRIDGE_RESOURCE_NUM PCI bus resources (those that correspond * to P2P or CardBus bridge windows) go in a table. Additional ones (for -- cgit v1.2.3 From bc9b6407bd6df3ab7189e5622816bbc11ae9d2d8 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Thu, 17 Jan 2013 14:11:05 +0100 Subject: ACPI / PM: Rework the handling of devices depending on power resources Commit 0090def6 (ACPI: Add interface to register/unregister device to/from power resources) made it possible to indicate to the ACPI core that if the given device depends on any power resources, then it should be resumed as soon as all of the power resources required by it to transition to the D0 power state have been turned on. Unfortunately, however, this was a mistake, because all devices depending on power resources should be treated this way (i.e. they should be resumed when all power resources required by their D0 state have been turned on) and for the majority of those devices the ACPI core can figure out by itself which (physical) devices depend on what power resources. For this reason, replace the code added by commit 0090def6 with a new, much more straightforward, mechanism that will be used internally by the ACPI core and remove all references to that code from kernel subsystems using ACPI. For the cases when there are (physical) devices that should be resumed whenever a not directly related ACPI device node goes into D0 as a result of power resources configuration changes, like in the SATA case, add two new routines, acpi_dev_pm_add_dependent() and acpi_dev_pm_remove_dependent(), allowing subsystems to manage such dependencies. Convert the SATA subsystem to use the new functions accordingly. Signed-off-by: Rafael J. Wysocki --- drivers/acpi/device_pm.c | 56 ++++++++++++ drivers/acpi/internal.h | 1 + drivers/acpi/power.c | 219 +++++++++++++++++----------------------------- drivers/acpi/scan.c | 8 ++ drivers/ata/libata-acpi.c | 18 +--- drivers/pci/pci-acpi.c | 2 - include/acpi/acpi_bus.h | 9 +- 7 files changed, 158 insertions(+), 155 deletions(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c index 995019063f64..8be4b29e38aa 100644 --- a/drivers/acpi/device_pm.c +++ b/drivers/acpi/device_pm.c @@ -665,3 +665,59 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off) } } EXPORT_SYMBOL_GPL(acpi_dev_pm_detach); + +/** + * acpi_dev_pm_add_dependent - Add physical device depending for PM. + * @handle: Handle of ACPI device node. + * @depdev: Device depending on that node for PM. + */ +void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev) +{ + struct acpi_device_physical_node *dep; + struct acpi_device *adev; + + if (!depdev || acpi_bus_get_device(handle, &adev)) + return; + + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(dep, &adev->power_dependent, node) + if (dep->dev == depdev) + goto out; + + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (dep) { + dep->dev = depdev; + list_add_tail(&dep->node, &adev->power_dependent); + } + + out: + mutex_unlock(&adev->physical_node_lock); +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_add_dependent); + +/** + * acpi_dev_pm_remove_dependent - Remove physical device depending for PM. + * @handle: Handle of ACPI device node. + * @depdev: Device depending on that node for PM. + */ +void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev) +{ + struct acpi_device_physical_node *dep; + struct acpi_device *adev; + + if (!depdev || acpi_bus_get_device(handle, &adev)) + return; + + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(dep, &adev->power_dependent, node) + if (dep->dev == depdev) { + list_del(&dep->node); + kfree(dep); + break; + } + + mutex_unlock(&adev->physical_node_lock); +} +EXPORT_SYMBOL_GPL(acpi_dev_pm_remove_dependent); diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index e050254ae143..b79b4258bd6b 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -38,6 +38,7 @@ static inline void acpi_debugfs_init(void) { return; } Power Resource -------------------------------------------------------------------------- */ int acpi_power_init(void); +void acpi_power_add_remove_device(struct acpi_device *adev, bool add); int acpi_device_sleep_wake(struct acpi_device *dev, int enable, int sleep_state, int dev_state); int acpi_power_get_inferred_state(struct acpi_device *device, int *state); diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 6e7b9d523812..659386c4f0cb 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c @@ -83,31 +83,20 @@ static struct acpi_driver acpi_power_driver = { .drv.pm = &acpi_power_pm, }; -/* - * A power managed device - * A device may rely on multiple power resources. - * */ -struct acpi_power_managed_device { - struct device *dev; /* The physical device */ - acpi_handle *handle; -}; - -struct acpi_power_resource_device { - struct acpi_power_managed_device *device; - struct acpi_power_resource_device *next; +struct acpi_power_dependent_device { + struct list_head node; + struct acpi_device *adev; + struct work_struct work; }; struct acpi_power_resource { - struct acpi_device * device; + struct acpi_device *device; + struct list_head dependent; acpi_bus_id name; u32 system_level; u32 order; unsigned int ref_count; struct mutex resource_lock; - - /* List of devices relying on this power resource */ - struct acpi_power_resource_device *devices; - struct mutex devices_lock; }; static struct list_head acpi_power_resource_list; @@ -207,21 +196,30 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) return 0; } -/* Resume the device when all power resources in _PR0 are on */ -static void acpi_power_on_device(struct acpi_power_managed_device *device) +static void acpi_power_resume_dependent(struct work_struct *work) { - struct acpi_device *acpi_dev; - acpi_handle handle = device->handle; + struct acpi_power_dependent_device *dep; + struct acpi_device_physical_node *pn; + struct acpi_device *adev; int state; - if (acpi_bus_get_device(handle, &acpi_dev)) + dep = container_of(work, struct acpi_power_dependent_device, work); + adev = dep->adev; + if (acpi_power_get_inferred_state(adev, &state)) return; - if(acpi_power_get_inferred_state(acpi_dev, &state)) + if (state > ACPI_STATE_D0) return; - if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev)) - pm_request_resume(device->dev); + mutex_lock(&adev->physical_node_lock); + + list_for_each_entry(pn, &adev->physical_node_list, node) + pm_request_resume(pn->dev); + + list_for_each_entry(pn, &adev->power_dependent, node) + pm_request_resume(pn->dev); + + mutex_unlock(&adev->physical_node_lock); } static int __acpi_power_on(struct acpi_power_resource *resource) @@ -244,9 +242,7 @@ static int __acpi_power_on(struct acpi_power_resource *resource) static int acpi_power_on(acpi_handle handle) { int result = 0; - bool resume_device = false; struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *device_list; result = acpi_power_get_context(handle, &resource); if (result) @@ -260,26 +256,17 @@ static int acpi_power_on(acpi_handle handle) resource->name)); } else { result = __acpi_power_on(resource); - if (result) + if (result) { resource->ref_count--; - else - resume_device = true; - } + } else { + struct acpi_power_dependent_device *dep; - mutex_unlock(&resource->resource_lock); - - if (!resume_device) - return result; - - mutex_lock(&resource->devices_lock); - - device_list = resource->devices; - while (device_list) { - acpi_power_on_device(device_list->device); - device_list = device_list->next; + list_for_each_entry(dep, &resource->dependent, node) + schedule_work(&dep->work); + } } - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); return result; } @@ -357,119 +344,81 @@ static int acpi_power_on_list(struct acpi_handle_list *list) return result; } -static void __acpi_power_resource_unregister_device(struct device *dev, - acpi_handle res_handle) +static void acpi_power_add_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *prev, *curr; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; - if (acpi_power_get_context(res_handle, &resource)) + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) return; - mutex_lock(&resource->devices_lock); - prev = NULL; - curr = resource->devices; - while (curr) { - if (curr->device->dev == dev) { - if (!prev) - resource->devices = curr->next; - else - prev->next = curr->next; - - kfree(curr); - break; - } - - prev = curr; - curr = curr->next; - } - mutex_unlock(&resource->devices_lock); -} - -/* Unlink dev from all power resources in _PR0 */ -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle) -{ - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - int i; + mutex_lock(&resource->resource_lock); - if (!dev || !handle) - return; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) + goto out; - if (acpi_bus_get_device(handle, &acpi_dev)) - return; + dep = kzalloc(sizeof(*dep), GFP_KERNEL); + if (!dep) + goto out; - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; + dep->adev = adev; + INIT_WORK(&dep->work, acpi_power_resume_dependent); + list_add_tail(&dep->node, &resource->dependent); - for (i = 0; i < list->count; i++) - __acpi_power_resource_unregister_device(dev, - list->handles[i]); + out: + mutex_unlock(&resource->resource_lock); } -EXPORT_SYMBOL_GPL(acpi_power_resource_unregister_device); -static int __acpi_power_resource_register_device( - struct acpi_power_managed_device *powered_device, acpi_handle handle) +static void acpi_power_remove_dependent(acpi_handle rhandle, + struct acpi_device *adev) { - struct acpi_power_resource *resource = NULL; - struct acpi_power_resource_device *power_resource_device; - int result; + struct acpi_power_dependent_device *dep; + struct acpi_power_resource *resource; + struct work_struct *work = NULL; - result = acpi_power_get_context(handle, &resource); - if (result) - return result; + if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource)) + return; - power_resource_device = kzalloc( - sizeof(*power_resource_device), GFP_KERNEL); - if (!power_resource_device) - return -ENOMEM; + mutex_lock(&resource->resource_lock); - power_resource_device->device = powered_device; + list_for_each_entry(dep, &resource->dependent, node) + if (dep->adev == adev) { + list_del(&dep->node); + work = &dep->work; + break; + } - mutex_lock(&resource->devices_lock); - power_resource_device->next = resource->devices; - resource->devices = power_resource_device; - mutex_unlock(&resource->devices_lock); + mutex_unlock(&resource->resource_lock); - return 0; + if (work) { + cancel_work_sync(work); + kfree(dep); + } } -/* Link dev to all power resources in _PR0 */ -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle) +void acpi_power_add_remove_device(struct acpi_device *adev, bool add) { - struct acpi_device *acpi_dev; - struct acpi_handle_list *list; - struct acpi_power_managed_device *powered_device; - int i, ret; + if (adev->power.flags.power_resources) { + struct acpi_device_power_state *ps; + int j; - if (!dev || !handle) - return -ENODEV; - - ret = acpi_bus_get_device(handle, &acpi_dev); - if (ret || !acpi_dev->power.flags.power_resources) - return -ENODEV; + ps = &adev->power.states[ACPI_STATE_D0]; + for (j = 0; j < ps->resources.count; j++) { + acpi_handle rhandle = ps->resources.handles[j]; - powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL); - if (!powered_device) - return -ENOMEM; - - powered_device->dev = dev; - powered_device->handle = handle; - - list = &acpi_dev->power.states[ACPI_STATE_D0].resources; - - for (i = 0; i < list->count; i++) { - ret = __acpi_power_resource_register_device(powered_device, - list->handles[i]); - - if (ret) { - acpi_power_resource_unregister_device(dev, handle); - break; + if (add) + acpi_power_add_dependent(rhandle, adev); + else + acpi_power_remove_dependent(rhandle, adev); } } - - return ret; } -EXPORT_SYMBOL_GPL(acpi_power_resource_register_device); + +/* -------------------------------------------------------------------------- + Device Power Management + -------------------------------------------------------------------------- */ /** * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in @@ -623,10 +572,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) return err; } -/* -------------------------------------------------------------------------- - Device Power Management - -------------------------------------------------------------------------- */ - int acpi_power_get_inferred_state(struct acpi_device *device, int *state) { int result = 0; @@ -725,7 +670,7 @@ static int acpi_power_add(struct acpi_device *device) resource->device = device; mutex_init(&resource->resource_lock); - mutex_init(&resource->devices_lock); + INIT_LIST_HEAD(&resource->dependent); strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 7d164a966b0d..c6d60910e8a8 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -633,6 +633,7 @@ static int acpi_device_register(struct acpi_device *device) INIT_LIST_HEAD(&device->wakeup_list); INIT_LIST_HEAD(&device->physical_node_list); mutex_init(&device->physical_node_lock); + INIT_LIST_HEAD(&device->power_dependent); new_bus_id = kzalloc(sizeof(struct acpi_device_bus_id), GFP_KERNEL); if (!new_bus_id) { @@ -706,8 +707,14 @@ static void acpi_device_unregister(struct acpi_device *device) acpi_detach_data(device->handle, acpi_bus_data_handler); + acpi_power_add_remove_device(device, false); acpi_device_remove_files(device); device_unregister(&device->dev); + /* + * Drop the reference counts of all power resources the device depends + * on and turn off the ones that have no more references. + */ + acpi_power_transition(device, ACPI_STATE_D3_COLD); } /* -------------------------------------------------------------------------- @@ -1441,6 +1448,7 @@ static int acpi_add_single_object(struct acpi_device **child, end: if (!result) { + acpi_power_add_remove_device(device, true); acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Adding %s [%s] parent %s\n", dev_name(&device->dev), diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index ef01ac07502e..6fc67f7efb22 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1029,30 +1029,20 @@ static void ata_acpi_register_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; acpi_handle handle; - struct device *device; handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_register_device(device, handle); + if (handle) + acpi_dev_pm_remove_dependent(handle, &sdev->sdev_gendev); } static void ata_acpi_unregister_power_resource(struct ata_device *dev) { struct scsi_device *sdev = dev->sdev; acpi_handle handle; - struct device *device; handle = ata_dev_acpi_handle(dev); - if (!handle) - return; - - device = &sdev->sdev_gendev; - - acpi_power_resource_unregister_device(device, handle); + if (handle) + acpi_dev_pm_remove_dependent(handle, &sdev->sdev_gendev); } void ata_acpi_bind(struct ata_device *dev) diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..e407c61559ca 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -345,7 +345,6 @@ static void pci_acpi_setup(struct device *dev) acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); } - acpi_power_resource_register_device(dev, handle); if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) return; @@ -368,7 +367,6 @@ static void pci_acpi_cleanup(struct device *dev) device_set_run_wake(dev, false); pci_acpi_remove_pm_notifier(adev); } - acpi_power_resource_unregister_device(dev, handle); if (pci_dev->subordinate) acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index 567851b4f043..29a1badfca55 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -279,6 +279,7 @@ struct acpi_device { struct list_head physical_node_list; struct mutex physical_node_lock; DECLARE_BITMAP(physical_node_id_bitmap, ACPI_MAX_PHYSICAL_NODE); + struct list_head power_dependent; }; static inline void *acpi_driver_data(struct acpi_device *d) @@ -334,8 +335,6 @@ int acpi_device_set_power(struct acpi_device *device, int state); int acpi_bus_update_power(acpi_handle handle, int *state_p); bool acpi_bus_power_manageable(acpi_handle handle); bool acpi_bus_can_wakeup(acpi_handle handle); -int acpi_power_resource_register_device(struct device *dev, acpi_handle handle); -void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle); #ifdef CONFIG_ACPI_PROC_EVENT int acpi_bus_generate_proc_event(struct acpi_device *device, u8 type, int data); int acpi_bus_generate_proc_event4(const char *class, const char *bid, u8 type, int data); @@ -414,6 +413,8 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev, int acpi_device_power_state(struct device *dev, struct acpi_device *adev, u32 target_state, int d_max_in, int *d_min_p); int acpi_pm_device_sleep_state(struct device *, int *, int); +void acpi_dev_pm_add_dependent(acpi_handle handle, struct device *depdev); +void acpi_dev_pm_remove_dependent(acpi_handle handle, struct device *depdev); #else static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev, acpi_notify_handler handler, @@ -443,6 +444,10 @@ static inline int acpi_pm_device_sleep_state(struct device *d, int *p, int m) { return __acpi_device_power_state(m, p); } +static inline void acpi_dev_pm_add_dependent(acpi_handle handle, + struct device *depdev) {} +static inline void acpi_dev_pm_remove_dependent(acpi_handle handle, + struct device *depdev) {} #endif #ifdef CONFIG_PM_RUNTIME -- cgit v1.2.3 From 181380b702eee1a9aca51354d7b87c7b08541fcf Mon Sep 17 00:00:00 2001 From: Yinghai Lu Date: Sat, 16 Feb 2013 11:58:34 -0700 Subject: PCI/ACPI: Don't cache _PRT, and don't associate them with bus numbers Previously, we cached _PRT (PCI routing table, ACPI 5.0 sec 6.2.12) contents and associated each _PRT entry with a PCI bus number. The bus number association means dependencies on PCI device enumeration and bus number assignment, as well as on the PCI/ACPI binding process. After 4f535093cf ("PCI: Put pci_dev in device tree as early as possible"), these dependencies caused the IRQ issues reported by Peter: pci 0000:00:1e.0: PCI bridge to [bus 09] (subtractive decode) pci 0000:00:1e.0: can't derive routing for PCI INT A snd_ctxfi 0000:09:02.0: PCI INT A: no GSI - using ISA IRQ 5 irq 18: nobody cared (try booting with the "irqpoll" option) This patch removes _PRT caching. Instead, we evaluate _PRT as needed in the pci_enable_device() path. This also removes the dependency on PCI bus numbers: we can simply look at the _PRT associated with each bridge as we walk upstream toward the root. [bhelgaas: changelog] Reference: https://bugzilla.kernel.org/show_bug.cgi?id=53561 Reported-and-tested-by: Peter Hurley Suggested-by: Bjorn Helgaas Signed-off-by: Yinghai Lu Signed-off-by: Bjorn Helgaas --- drivers/acpi/pci_irq.c | 102 +++++++++++++++----------------------------- drivers/acpi/pci_root.c | 18 -------- drivers/pci/pci-acpi.c | 24 ----------- include/acpi/acpi_drivers.h | 5 --- 4 files changed, 34 insertions(+), 115 deletions(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/acpi/pci_irq.c b/drivers/acpi/pci_irq.c index 68a921d03247..41c5e1b799ef 100644 --- a/drivers/acpi/pci_irq.c +++ b/drivers/acpi/pci_irq.c @@ -53,9 +53,6 @@ struct acpi_prt_entry { u32 index; /* GSI, or link _CRS index */ }; -static LIST_HEAD(acpi_prt_list); -static DEFINE_SPINLOCK(acpi_prt_lock); - static inline char pin_name(int pin) { return 'A' + pin - 1; @@ -65,28 +62,6 @@ static inline char pin_name(int pin) PCI IRQ Routing Table (PRT) Support -------------------------------------------------------------------------- */ -static struct acpi_prt_entry *acpi_pci_irq_find_prt_entry(struct pci_dev *dev, - int pin) -{ - struct acpi_prt_entry *entry; - int segment = pci_domain_nr(dev->bus); - int bus = dev->bus->number; - int device = PCI_SLOT(dev->devfn); - - spin_lock(&acpi_prt_lock); - list_for_each_entry(entry, &acpi_prt_list, list) { - if ((segment == entry->id.segment) - && (bus == entry->id.bus) - && (device == entry->id.device) - && (pin == entry->pin)) { - spin_unlock(&acpi_prt_lock); - return entry; - } - } - spin_unlock(&acpi_prt_lock); - return NULL; -} - /* http://bugzilla.kernel.org/show_bug.cgi?id=4773 */ static const struct dmi_system_id medion_md9580[] = { { @@ -184,11 +159,19 @@ static void do_prt_fixups(struct acpi_prt_entry *entry, } } -static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, - struct acpi_pci_routing_table *prt) +static int acpi_pci_irq_check_entry(acpi_handle handle, struct pci_dev *dev, + int pin, struct acpi_pci_routing_table *prt, + struct acpi_prt_entry **entry_ptr) { + int segment = pci_domain_nr(dev->bus); + int bus = dev->bus->number; + int device = PCI_SLOT(dev->devfn); struct acpi_prt_entry *entry; + if (((prt->address >> 16) & 0xffff) != device || + prt->pin + 1 != pin) + return -ENODEV; + entry = kzalloc(sizeof(struct acpi_prt_entry), GFP_KERNEL); if (!entry) return -ENOMEM; @@ -237,43 +220,37 @@ static int acpi_pci_irq_add_entry(acpi_handle handle, int segment, int bus, entry->id.device, pin_name(entry->pin), prt->source, entry->index)); - spin_lock(&acpi_prt_lock); - list_add_tail(&entry->list, &acpi_prt_list); - spin_unlock(&acpi_prt_lock); + *entry_ptr = entry; return 0; } -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) +static int acpi_pci_irq_find_prt_entry(struct pci_dev *dev, + int pin, struct acpi_prt_entry **entry_ptr) { acpi_status status; struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; struct acpi_pci_routing_table *entry; + acpi_handle handle = NULL; - /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ - status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); - if (ACPI_FAILURE(status)) - return -ENODEV; - - printk(KERN_DEBUG "ACPI: PCI Interrupt Routing Table [%s._PRT]\n", - (char *) buffer.pointer); - - kfree(buffer.pointer); + if (dev->bus->bridge) + handle = ACPI_HANDLE(dev->bus->bridge); - buffer.length = ACPI_ALLOCATE_BUFFER; - buffer.pointer = NULL; + if (!handle) + return -ENODEV; + /* 'handle' is the _PRT's parent (root bridge or PCI-PCI bridge) */ status = acpi_get_irq_routing_table(handle, &buffer); if (ACPI_FAILURE(status)) { - ACPI_EXCEPTION((AE_INFO, status, "Evaluating _PRT [%s]", - acpi_format_exception(status))); kfree(buffer.pointer); return -ENODEV; } entry = buffer.pointer; while (entry && (entry->length > 0)) { - acpi_pci_irq_add_entry(handle, segment, bus, entry); + if (!acpi_pci_irq_check_entry(handle, dev, pin, + entry, entry_ptr)) + break; entry = (struct acpi_pci_routing_table *) ((unsigned long)entry + entry->length); } @@ -282,23 +259,6 @@ int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus) return 0; } -void acpi_pci_irq_del_prt(int segment, int bus) -{ - struct acpi_prt_entry *entry, *tmp; - - printk(KERN_DEBUG - "ACPI: Delete PCI Interrupt Routing Table for %04x:%02x\n", - segment, bus); - spin_lock(&acpi_prt_lock); - list_for_each_entry_safe(entry, tmp, &acpi_prt_list, list) { - if (segment == entry->id.segment && bus == entry->id.bus) { - list_del(&entry->list); - kfree(entry); - } - } - spin_unlock(&acpi_prt_lock); -} - /* -------------------------------------------------------------------------- PCI Interrupt Routing Support -------------------------------------------------------------------------- */ @@ -359,12 +319,13 @@ static int acpi_reroute_boot_interrupt(struct pci_dev *dev, static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) { - struct acpi_prt_entry *entry; + struct acpi_prt_entry *entry = NULL; struct pci_dev *bridge; u8 bridge_pin, orig_pin = pin; + int ret; - entry = acpi_pci_irq_find_prt_entry(dev, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(dev, pin, &entry); + if (!ret && entry) { #ifdef CONFIG_X86_IO_APIC acpi_reroute_boot_interrupt(dev, entry); #endif /* CONFIG_X86_IO_APIC */ @@ -373,7 +334,7 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) return entry; } - /* + /* * Attempt to derive an IRQ for this device from a parent bridge's * PCI interrupt routing entry (eg. yenta bridge and add-in card bridge). */ @@ -393,8 +354,8 @@ static struct acpi_prt_entry *acpi_pci_irq_lookup(struct pci_dev *dev, int pin) pin = bridge_pin; } - entry = acpi_pci_irq_find_prt_entry(bridge, pin); - if (entry) { + ret = acpi_pci_irq_find_prt_entry(bridge, pin, &entry); + if (!ret && entry) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Derived GSI for %s INT %c from %s\n", pci_name(dev), pin_name(orig_pin), @@ -470,6 +431,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) dev_warn(&dev->dev, "PCI INT %c: no GSI\n", pin_name(pin)); } + return 0; } @@ -477,6 +439,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) if (rc < 0) { dev_warn(&dev->dev, "PCI INT %c: failed to register GSI\n", pin_name(pin)); + kfree(entry); return rc; } dev->irq = rc; @@ -491,6 +454,7 @@ int acpi_pci_irq_enable(struct pci_dev *dev) (triggering == ACPI_LEVEL_SENSITIVE) ? "level" : "edge", (polarity == ACPI_ACTIVE_LOW) ? "low" : "high", dev->irq); + kfree(entry); return 0; } @@ -513,6 +477,8 @@ void acpi_pci_irq_disable(struct pci_dev *dev) else gsi = entry->index; + kfree(entry); + /* * TBD: It might be worth clearing dev->irq by magic constant * (e.g. PCI_UNDEFINED_IRQ). diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index fd59f57d3829..8545b1d22811 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -434,7 +434,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_status status; int result; struct acpi_pci_root *root; - acpi_handle handle; struct acpi_pci_driver *driver; u32 flags, base_flags; bool is_osc_granted = false; @@ -489,16 +488,6 @@ static int acpi_pci_root_add(struct acpi_device *device) acpi_device_name(device), acpi_device_bid(device), root->segment, &root->secondary); - /* - * PCI Routing Table - * ----------------- - * Evaluate and parse _PRT, if exists. - */ - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - result = acpi_pci_irq_add_prt(device->handle, root->segment, - root->secondary.start); - root->mcfg_addr = acpi_pci_root_get_mcfg_addr(device->handle); /* @@ -623,7 +612,6 @@ out_del_root: list_del(&root->node); mutex_unlock(&acpi_pci_root_lock); - acpi_pci_irq_del_prt(root->segment, root->secondary.start); end: kfree(root); return result; @@ -631,8 +619,6 @@ end: static int acpi_pci_root_remove(struct acpi_device *device, int type) { - acpi_status status; - acpi_handle handle; struct acpi_pci_root *root = acpi_driver_data(device); struct acpi_pci_driver *driver; @@ -647,10 +633,6 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type) device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device); - status = acpi_get_handle(device->handle, METHOD_NAME__PRT, &handle); - if (ACPI_SUCCESS(status)) - acpi_pci_irq_del_prt(root->segment, root->secondary.start); - pci_remove_root_bus(root->bus); mutex_lock(&acpi_pci_root_lock); diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 42736e213f25..9ba9df5f6161 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -325,25 +325,6 @@ static void pci_acpi_setup(struct device *dev) struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; - acpi_status status; - acpi_handle dummy; - - /* - * Evaluate and parse _PRT, if exists. This code allows parsing of - * _PRT objects within the scope of non-bridge devices. Note that - * _PRTs within the scope of a PCI bridge assume the bridge's - * subordinate bus number. - * - * TBD: Can _PRTs exist within the scope of non-bridge PCI devices? - */ - status = acpi_get_handle(handle, METHOD_NAME__PRT, &dummy); - if (ACPI_SUCCESS(status)) { - unsigned char bus; - - bus = pci_dev->subordinate ? - pci_dev->subordinate->number : pci_dev->bus->number; - acpi_pci_irq_add_prt(handle, pci_domain_nr(pci_dev->bus), bus); - } acpi_power_resource_register_device(dev, handle); if (acpi_bus_get_device(handle, &adev) || !adev->wakeup.flags.valid) @@ -359,7 +340,6 @@ static void pci_acpi_setup(struct device *dev) static void pci_acpi_cleanup(struct device *dev) { - struct pci_dev *pci_dev = to_pci_dev(dev); acpi_handle handle = ACPI_HANDLE(dev); struct acpi_device *adev; @@ -369,10 +349,6 @@ static void pci_acpi_cleanup(struct device *dev) pci_acpi_remove_pm_notifier(adev); } acpi_power_resource_unregister_device(dev, handle); - - if (pci_dev->subordinate) - acpi_pci_irq_del_prt(pci_domain_nr(pci_dev->bus), - pci_dev->subordinate->number); } static struct acpi_bus_type acpi_pci_bus = { diff --git a/include/acpi/acpi_drivers.h b/include/acpi/acpi_drivers.h index 8b1d7a6a9695..627749af0ba7 100644 --- a/include/acpi/acpi_drivers.h +++ b/include/acpi/acpi_drivers.h @@ -90,11 +90,6 @@ int acpi_pci_link_allocate_irq(acpi_handle handle, int index, int *triggering, int *polarity, char **name); int acpi_pci_link_free_irq(acpi_handle handle); -/* ACPI PCI Interrupt Routing (pci_irq.c) */ - -int acpi_pci_irq_add_prt(acpi_handle handle, int segment, int bus); -void acpi_pci_irq_del_prt(int segment, int bus); - /* ACPI PCI Device Binding (pci_bind.c) */ struct pci_bus; -- cgit v1.2.3 From 53540098b23c3884b4a0b4f220b9d977bc496af3 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Sun, 3 Mar 2013 22:35:20 +0100 Subject: ACPI / glue: Add .match() callback to struct acpi_bus_type USB uses the .find_bridge() callback from struct acpi_bus_type incorrectly, because as a result of the way it is used by USB every device in the system that doesn't have a bus type or parent is passed to usb_acpi_find_device() for inspection. What USB actually needs, though, is to call usb_acpi_find_device() for USB ports that don't have a bus type defined, but have usb_port_device_type as their device type, as well as for USB devices. To fix that replace the struct bus_type pointer in struct acpi_bus_type used for matching devices to specific subsystems with a .match() callback to be used for this purpose and update the users of struct acpi_bus_type, including USB, accordingly. Define the .match() callback routine for USB, usb_acpi_bus_match(), in such a way that it will cover both USB devices and USB ports and remove the now redundant .find_bridge() callback pointer from usb_acpi_bus. Signed-off-by: Rafael J. Wysocki Acked-by: Greg Kroah-Hartman Acked-by: Yinghai Lu Acked-by: Jeff Garzik --- drivers/acpi/glue.c | 39 +++++++++++++-------------------------- drivers/ata/libata-acpi.c | 1 + drivers/pci/pci-acpi.c | 8 +++++++- drivers/pnp/pnpacpi/core.c | 8 +++++++- drivers/scsi/scsi_lib.c | 7 ++++++- drivers/usb/core/usb-acpi.c | 9 +++++++-- include/acpi/acpi_bus.h | 3 ++- 7 files changed, 43 insertions(+), 32 deletions(-) (limited to 'drivers/pci/pci-acpi.c') diff --git a/drivers/acpi/glue.c b/drivers/acpi/glue.c index ef6f155469b5..b94d14721af3 100644 --- a/drivers/acpi/glue.c +++ b/drivers/acpi/glue.c @@ -36,12 +36,11 @@ int register_acpi_bus_type(struct acpi_bus_type *type) { if (acpi_disabled) return -ENODEV; - if (type && type->bus && type->find_device) { + if (type && type->match && type->find_device) { down_write(&bus_type_sem); list_add_tail(&type->list, &bus_type_list); up_write(&bus_type_sem); - printk(KERN_INFO PREFIX "bus type %s registered\n", - type->bus->name); + printk(KERN_INFO PREFIX "bus type %s registered\n", type->name); return 0; } return -ENODEV; @@ -56,24 +55,21 @@ int unregister_acpi_bus_type(struct acpi_bus_type *type) down_write(&bus_type_sem); list_del_init(&type->list); up_write(&bus_type_sem); - printk(KERN_INFO PREFIX "ACPI bus type %s unregistered\n", - type->bus->name); + printk(KERN_INFO PREFIX "bus type %s unregistered\n", + type->name); return 0; } return -ENODEV; } EXPORT_SYMBOL_GPL(unregister_acpi_bus_type); -static struct acpi_bus_type *acpi_get_bus_type(struct bus_type *type) +static struct acpi_bus_type *acpi_get_bus_type(struct device *dev) { struct acpi_bus_type *tmp, *ret = NULL; - if (!type) - return NULL; - down_read(&bus_type_sem); list_for_each_entry(tmp, &bus_type_list, list) { - if (tmp->bus == type) { + if (tmp->match(dev)) { ret = tmp; break; } @@ -261,26 +257,17 @@ err: static int acpi_platform_notify(struct device *dev) { - struct acpi_bus_type *type; + struct acpi_bus_type *type = acpi_get_bus_type(dev); acpi_handle handle; int ret; ret = acpi_bind_one(dev, NULL); - if (ret && (!dev->bus || !dev->parent)) { - /* bridge devices genernally haven't bus or parent */ - ret = acpi_find_bridge_device(dev, &handle); - if (!ret) { - ret = acpi_bind_one(dev, handle); - if (ret) - goto out; - } - } - - type = acpi_get_bus_type(dev->bus); if (ret) { - if (!type || !type->find_device) { - DBG("No ACPI bus support for %s\n", dev_name(dev)); - ret = -EINVAL; + if (!type) { + ret = acpi_find_bridge_device(dev, &handle); + if (!ret) + ret = acpi_bind_one(dev, handle); + goto out; } @@ -316,7 +303,7 @@ static int acpi_platform_notify_remove(struct device *dev) { struct acpi_bus_type *type; - type = acpi_get_bus_type(dev->bus); + type = acpi_get_bus_type(dev); if (type && type->cleanup) type->cleanup(dev); diff --git a/drivers/ata/libata-acpi.c b/drivers/ata/libata-acpi.c index 0ea1018280bd..c832a5ca09ad 100644 --- a/drivers/ata/libata-acpi.c +++ b/drivers/ata/libata-acpi.c @@ -1150,6 +1150,7 @@ static int ata_acpi_find_dummy(struct device *dev, acpi_handle *handle) } static struct acpi_bus_type ata_acpi_bus = { + .name = "ATA", .find_bridge = ata_acpi_find_dummy, .find_device = ata_acpi_find_device, }; diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 39c937f9b426..dee5dddaa292 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -331,8 +331,14 @@ static void pci_acpi_cleanup(struct device *dev) } } +static bool pci_acpi_bus_match(struct device *dev) +{ + return dev->bus == &pci_bus_type; +} + static struct acpi_bus_type acpi_pci_bus = { - .bus = &pci_bus_type, + .name = "PCI", + .match = pci_acpi_bus_match, .find_device = acpi_pci_find_device, .setup = pci_acpi_setup, .cleanup = pci_acpi_cleanup, diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c index 8813fc03aa09..55cd459a3908 100644 --- a/drivers/pnp/pnpacpi/core.c +++ b/drivers/pnp/pnpacpi/core.c @@ -353,8 +353,14 @@ static int __init acpi_pnp_find_device(struct device *dev, acpi_handle * handle) /* complete initialization of a PNPACPI device includes having * pnpdev->dev.archdata.acpi_handle point to its ACPI sibling. */ +static bool acpi_pnp_bus_match(struct device *dev) +{ + return dev->bus == &pnp_bus_type; +} + static struct acpi_bus_type __initdata acpi_pnp_bus = { - .bus = &pnp_bus_type, + .name = "PNP", + .match = acpi_pnp_bus_match, .find_device = acpi_pnp_find_device, }; diff --git a/drivers/scsi/scsi_lib.c b/drivers/scsi/scsi_lib.c index 765398c063c7..c31187d79343 100644 --- a/drivers/scsi/scsi_lib.c +++ b/drivers/scsi/scsi_lib.c @@ -71,9 +71,14 @@ struct kmem_cache *scsi_sdb_cache; #ifdef CONFIG_ACPI #include +static bool acpi_scsi_bus_match(struct device *dev) +{ + return dev->bus == &scsi_bus_type; +} + int scsi_register_acpi_bus_type(struct acpi_bus_type *bus) { - bus->bus = &scsi_bus_type; + bus->match = acpi_scsi_bus_match; return register_acpi_bus_type(bus); } EXPORT_SYMBOL_GPL(scsi_register_acpi_bus_type); diff --git a/drivers/usb/core/usb-acpi.c b/drivers/usb/core/usb-acpi.c index cef4252bb31a..b6f4bad3f756 100644 --- a/drivers/usb/core/usb-acpi.c +++ b/drivers/usb/core/usb-acpi.c @@ -210,9 +210,14 @@ static int usb_acpi_find_device(struct device *dev, acpi_handle *handle) return 0; } +static bool usb_acpi_bus_match(struct device *dev) +{ + return is_usb_device(dev) || is_usb_port(dev); +} + static struct acpi_bus_type usb_acpi_bus = { - .bus = &usb_bus_type, - .find_bridge = usb_acpi_find_device, + .name = "USB", + .match = usb_acpi_bus_match, .find_device = usb_acpi_find_device, }; diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h index e65278f560c4..c751d7de3a5f 100644 --- a/include/acpi/acpi_bus.h +++ b/include/acpi/acpi_bus.h @@ -437,7 +437,8 @@ void acpi_remove_dir(struct acpi_device *); */ struct acpi_bus_type { struct list_head list; - struct bus_type *bus; + const char *name; + bool (*match)(struct device *dev); /* For general devices under the bus */ int (*find_device) (struct device *, acpi_handle *); /* For bridges, such as PCI root bridge, IDE controller */ -- cgit v1.2.3