From bac2a909a096c9110525c18cbb8ce73c660d5f71 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Wed, 21 Jan 2015 02:17:42 +0100 Subject: PCI / PM: Avoid resuming PCI devices during system suspend Commit f25c0ae2b4c4 (ACPI / PM: Avoid resuming devices in ACPI PM domain during system suspend) modified the ACPI PM domain's system suspend callbacks to allow devices attached to it to be left in the runtime-suspended state during system suspend so as to optimize the suspend process. This was based on the general mechanism introduced by commit aae4518b3124 (PM / sleep: Mechanism to avoid resuming runtime-suspended devices unnecessarily). Extend that approach to PCI devices by modifying the PCI bus type's ->prepare callback to return 1 for devices that are runtime-suspended when it is being executed and that are in a suitable power state and need not be resumed going forward. Signed-off-by: Rafael J. Wysocki Acked-by: Bjorn Helgaas --- drivers/pci/pci-acpi.c | 17 +++++++++++++++++ drivers/pci/pci-driver.c | 11 ++++++----- drivers/pci/pci.c | 26 ++++++++++++++++++++++++++ drivers/pci/pci.h | 6 ++++++ 4 files changed, 55 insertions(+), 5 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index 3542150fc8a3..489063987325 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -501,12 +501,29 @@ static int acpi_pci_run_wake(struct pci_dev *dev, bool enable) return 0; } +static bool acpi_pci_need_resume(struct pci_dev *dev) +{ + struct acpi_device *adev = ACPI_COMPANION(&dev->dev); + + if (!adev || !acpi_device_power_manageable(adev)) + return false; + + if (device_may_wakeup(&dev->dev) != !!adev->wakeup.prepare_count) + return true; + + if (acpi_target_system_state() == ACPI_STATE_S0) + return false; + + return !!adev->power.flags.dsw_present; +} + 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, .sleep_wake = acpi_pci_sleep_wake, .run_wake = acpi_pci_run_wake, + .need_resume = acpi_pci_need_resume, }; void acpi_pci_add_bus(struct pci_bus *bus) diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 887e6bd95af7..741023e94008 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -653,7 +653,6 @@ static bool pci_has_legacy_pm_support(struct pci_dev *pci_dev) static int pci_pm_prepare(struct device *dev) { struct device_driver *drv = dev->driver; - int error = 0; /* * Devices having power.ignore_children set may still be necessary for @@ -662,10 +661,12 @@ static int pci_pm_prepare(struct device *dev) if (dev->power.ignore_children) pm_runtime_resume(dev); - if (drv && drv->pm && drv->pm->prepare) - error = drv->pm->prepare(dev); - - return error; + if (drv && drv->pm && drv->pm->prepare) { + int error = drv->pm->prepare(dev); + if (error) + return error; + } + return pci_dev_keep_suspended(to_pci_dev(dev)); } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index cab05f31223f..7a671abceccc 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -521,6 +521,11 @@ static inline int platform_pci_run_wake(struct pci_dev *dev, bool enable) pci_platform_pm->run_wake(dev, enable) : -ENODEV; } +static inline bool platform_pci_need_resume(struct pci_dev *dev) +{ + return pci_platform_pm ? pci_platform_pm->need_resume(dev) : false; +} + /** * pci_raw_set_power_state - Use PCI PM registers to set the power state of * given PCI device @@ -1999,6 +2004,27 @@ bool pci_dev_run_wake(struct pci_dev *dev) } EXPORT_SYMBOL_GPL(pci_dev_run_wake); +/** + * pci_dev_keep_suspended - Check if the device can stay in the suspended state. + * @pci_dev: Device to check. + * + * Return 'true' if the device is runtime-suspended, it doesn't have to be + * reconfigured due to wakeup settings difference between system and runtime + * suspend and the current power state of it is suitable for the upcoming + * (system) transition. + */ +bool pci_dev_keep_suspended(struct pci_dev *pci_dev) +{ + struct device *dev = &pci_dev->dev; + + if (!pm_runtime_suspended(dev) + || (device_can_wakeup(dev) && !device_may_wakeup(dev)) + || platform_pci_need_resume(pci_dev)) + return false; + + return pci_target_state(pci_dev) == pci_dev->current_state; +} + void pci_config_pm_runtime_get(struct pci_dev *pdev) { struct device *dev = &pdev->dev; diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 8aff29a804ff..febb3db9f742 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -50,6 +50,10 @@ int pci_probe_reset_function(struct pci_dev *dev); * for given device (the device's wake-up capability has to be * enabled by @sleep_wake for this feature to work) * + * @need_resume: returns 'true' if the given device (which is currently + * suspended) needs to be resumed to be configured for system + * wakeup. + * * If given platform is generally capable of power managing PCI devices, all of * these callbacks are mandatory. */ @@ -59,6 +63,7 @@ struct pci_platform_pm_ops { pci_power_t (*choose_state)(struct pci_dev *dev); int (*sleep_wake)(struct pci_dev *dev, bool enable); int (*run_wake)(struct pci_dev *dev, bool enable); + bool (*need_resume)(struct pci_dev *dev); }; int pci_set_platform_pm(struct pci_platform_pm_ops *ops); @@ -67,6 +72,7 @@ void pci_power_up(struct pci_dev *dev); void pci_disable_enabled_device(struct pci_dev *dev); int pci_finish_runtime_suspend(struct pci_dev *dev); int __pci_pme_wakeup(struct pci_dev *dev, void *ign); +bool pci_dev_keep_suspended(struct pci_dev *dev); void pci_config_pm_runtime_get(struct pci_dev *dev); void pci_config_pm_runtime_put(struct pci_dev *dev); void pci_pm_init(struct pci_dev *dev); -- cgit v1.2.3 From e044d8f92f79db167bb7f9dfd0f317e3238d82d1 Mon Sep 17 00:00:00 2001 From: Lv Zheng Date: Mon, 26 Jan 2015 16:58:48 +0800 Subject: ACPI: Introduce acpi_unload_parent_table() usages in Linux kernel ACPICA has implemented acpi_unload_parent_table() which can exactly replace the acpi_get_id()/acpi_unload_table_id() implemented in Linux kernel. The acpi_unload_parent_table() has been unit tested in ACPICA simulation environment. This patch can also help to reduce the source code differences between Linux and ACPICA. Signed-off-by: Lv Zheng Acked-by: Bjorn Helgaas Tested-by: Octavian Purdila Signed-off-by: Rafael J. Wysocki --- drivers/acpi/acpica/nsxfobj.c | 44 ------------------------------------- drivers/acpi/acpica/tbxface.c | 46 --------------------------------------- drivers/pci/hotplug/sgi_hotplug.c | 13 ++++++----- include/acpi/acpixf.h | 6 ----- 4 files changed, 7 insertions(+), 102 deletions(-) (limited to 'drivers/pci') diff --git a/drivers/acpi/acpica/nsxfobj.c b/drivers/acpi/acpica/nsxfobj.c index dae9401be7a2..51a83298c18d 100644 --- a/drivers/acpi/acpica/nsxfobj.c +++ b/drivers/acpi/acpica/nsxfobj.c @@ -51,50 +51,6 @@ #define _COMPONENT ACPI_NAMESPACE ACPI_MODULE_NAME("nsxfobj") -/******************************************************************************* - * - * FUNCTION: acpi_get_id - * - * PARAMETERS: Handle - Handle of object whose id is desired - * ret_id - Where the id will be placed - * - * RETURN: Status - * - * DESCRIPTION: This routine returns the owner id associated with a handle - * - ******************************************************************************/ -acpi_status acpi_get_id(acpi_handle handle, acpi_owner_id * ret_id) -{ - struct acpi_namespace_node *node; - acpi_status status; - - /* Parameter Validation */ - - if (!ret_id) { - return (AE_BAD_PARAMETER); - } - - status = acpi_ut_acquire_mutex(ACPI_MTX_NAMESPACE); - if (ACPI_FAILURE(status)) { - return (status); - } - - /* Convert and validate the handle */ - - node = acpi_ns_validate_handle(handle); - if (!node) { - (void)acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return (AE_BAD_PARAMETER); - } - - *ret_id = node->owner_id; - - status = acpi_ut_release_mutex(ACPI_MTX_NAMESPACE); - return (status); -} - -ACPI_EXPORT_SYMBOL(acpi_get_id) - /******************************************************************************* * * FUNCTION: acpi_get_type diff --git a/drivers/acpi/acpica/tbxface.c b/drivers/acpi/acpica/tbxface.c index 9520ae19bb37..0f9dd80b1cdd 100644 --- a/drivers/acpi/acpica/tbxface.c +++ b/drivers/acpi/acpica/tbxface.c @@ -263,52 +263,6 @@ acpi_get_table_header(char *signature, ACPI_EXPORT_SYMBOL(acpi_get_table_header) -/******************************************************************************* - * - * FUNCTION: acpi_unload_table_id - * - * PARAMETERS: id - Owner ID of the table to be removed. - * - * RETURN: Status - * - * DESCRIPTION: This routine is used to force the unload of a table (by id) - * - ******************************************************************************/ -acpi_status acpi_unload_table_id(acpi_owner_id id) -{ - int i; - acpi_status status = AE_NOT_EXIST; - - ACPI_FUNCTION_TRACE(acpi_unload_table_id); - - status = acpi_ut_acquire_mutex(ACPI_MTX_INTERPRETER); - if (ACPI_FAILURE(status)) { - return_ACPI_STATUS(status); - } - - /* Find table in the global table list */ - for (i = 0; i < acpi_gbl_root_table_list.current_table_count; ++i) { - if (id != acpi_gbl_root_table_list.tables[i].owner_id) { - continue; - } - /* - * Delete all namespace objects owned by this table. Note that these - * objects can appear anywhere in the namespace by virtue of the AML - * "Scope" operator. Thus, we need to track ownership by an ID, not - * simply a position within the hierarchy - */ - acpi_tb_delete_namespace_by_owner(i); - status = acpi_tb_release_owner_id(i); - acpi_tb_set_table_loaded_flag(i, FALSE); - break; - } - - (void)acpi_ut_release_mutex(ACPI_MTX_INTERPRETER); - return_ACPI_STATUS(status); -} - -ACPI_EXPORT_SYMBOL(acpi_unload_table_id) - /******************************************************************************* * * FUNCTION: acpi_get_table_with_size diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c index bada20999870..c32fb786d48e 100644 --- a/drivers/pci/hotplug/sgi_hotplug.c +++ b/drivers/pci/hotplug/sgi_hotplug.c @@ -475,7 +475,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) struct slot *slot = bss_hotplug_slot->private; struct pci_dev *dev, *temp; int rc; - acpi_owner_id ssdt_id = 0; + acpi_handle ssdt_hdl = NULL; /* Acquire update access to the bus */ mutex_lock(&sn_hotplug_mutex); @@ -522,7 +522,7 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) if (ACPI_SUCCESS(ret) && (adr>>16) == (slot->device_num + 1)) { /* retain the owner id */ - acpi_get_id(chandle, &ssdt_id); + ssdt_hdl = chandle; ret = acpi_bus_get_device(chandle, &device); @@ -547,12 +547,13 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot) pci_unlock_rescan_remove(); /* Remove the SSDT for the slot from the ACPI namespace */ - if (SN_ACPI_BASE_SUPPORT() && ssdt_id) { + if (SN_ACPI_BASE_SUPPORT() && ssdt_hdl) { acpi_status ret; - ret = acpi_unload_table_id(ssdt_id); + ret = acpi_unload_parent_table(ssdt_hdl); if (ACPI_FAILURE(ret)) { - printk(KERN_ERR "%s: acpi_unload_table_id failed (0x%x) for id %d\n", - __func__, ret, ssdt_id); + acpi_handle_err(ssdt_hdl, + "%s: acpi_unload_parent_table failed (0x%x)\n", + __func__, ret); /* try to continue on */ } } diff --git a/include/acpi/acpixf.h b/include/acpi/acpixf.h index 5ba78464c1b1..5dd21bc18ce6 100644 --- a/include/acpi/acpixf.h +++ b/include/acpi/acpixf.h @@ -890,12 +890,6 @@ ACPI_APP_DEPENDENT_RETURN_VOID(ACPI_PRINTF_LIKE(1) */ ACPI_GLOBAL(u8, acpi_gbl_permanent_mmap); -ACPI_EXTERNAL_RETURN_STATUS(acpi_status - acpi_get_id(acpi_handle object, - acpi_owner_id * out_type)) - -ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_unload_table_id(acpi_owner_id id)) - ACPI_EXTERNAL_RETURN_STATUS(acpi_status acpi_get_table_with_size(acpi_string signature, u32 instance, -- cgit v1.2.3 From 14d76b68f2819a1d0b50236a7e9e9f2ea69869d9 Mon Sep 17 00:00:00 2001 From: Jiang Liu Date: Thu, 5 Feb 2015 13:44:44 +0800 Subject: PCI: Use common resource list management code instead of private implementation Use common resource list management data structure and interfaces instead of private implementation. Signed-off-by: Jiang Liu Acked-by: Will Deacon Acked-by: Bjorn Helgaas Signed-off-by: Rafael J. Wysocki --- arch/arm/kernel/bios32.c | 5 ++--- arch/x86/pci/bus_numa.c | 4 ++-- drivers/pci/bus.c | 18 ++++++------------ drivers/pci/host-bridge.c | 8 ++++---- drivers/pci/host/pci-host-generic.c | 4 ++-- drivers/pci/host/pci-xgene.c | 4 ++-- drivers/pci/host/pcie-xilinx.c | 4 ++-- drivers/pci/probe.c | 10 +++++----- include/linux/pci.h | 9 ++------- 9 files changed, 27 insertions(+), 39 deletions(-) (limited to 'drivers/pci') diff --git a/arch/arm/kernel/bios32.c b/arch/arm/kernel/bios32.c index a4effd6d8f2f..016991792b0b 100644 --- a/arch/arm/kernel/bios32.c +++ b/arch/arm/kernel/bios32.c @@ -422,17 +422,16 @@ static int pcibios_map_irq(const struct pci_dev *dev, u8 slot, u8 pin) static int pcibios_init_resources(int busnr, struct pci_sys_data *sys) { int ret; - struct pci_host_bridge_window *window; + struct resource_entry *window; if (list_empty(&sys->resources)) { pci_add_resource_offset(&sys->resources, &iomem_resource, sys->mem_offset); } - list_for_each_entry(window, &sys->resources, list) { + resource_list_for_each_entry(window, &sys->resources) if (resource_type(window->res) == IORESOURCE_IO) return 0; - } sys->io_res.start = (busnr * SZ_64K) ? : pcibios_min_io; sys->io_res.end = (busnr + 1) * SZ_64K - 1; diff --git a/arch/x86/pci/bus_numa.c b/arch/x86/pci/bus_numa.c index f3a2cfc14125..7bcf06a7cd12 100644 --- a/arch/x86/pci/bus_numa.c +++ b/arch/x86/pci/bus_numa.c @@ -31,7 +31,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) { struct pci_root_info *info = x86_find_pci_root_info(bus); struct pci_root_res *root_res; - struct pci_host_bridge_window *window; + struct resource_entry *window; bool found = false; if (!info) @@ -41,7 +41,7 @@ void x86_pci_root_bus_resources(int bus, struct list_head *resources) bus); /* already added by acpi ? */ - list_for_each_entry(window, resources, list) + resource_list_for_each_entry(window, resources) if (window->res->flags & IORESOURCE_BUS) { found = true; break; diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index 8fb16188cd82..90fa3a78fb7c 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -20,17 +20,16 @@ void pci_add_resource_offset(struct list_head *resources, struct resource *res, resource_size_t offset) { - struct pci_host_bridge_window *window; + struct resource_entry *entry; - window = kzalloc(sizeof(struct pci_host_bridge_window), GFP_KERNEL); - if (!window) { + entry = resource_list_create_entry(res, 0); + if (!entry) { printk(KERN_ERR "PCI: can't add host bridge window %pR\n", res); return; } - window->res = res; - window->offset = offset; - list_add_tail(&window->list, resources); + entry->offset = offset; + resource_list_add_tail(entry, resources); } EXPORT_SYMBOL(pci_add_resource_offset); @@ -42,12 +41,7 @@ EXPORT_SYMBOL(pci_add_resource); void pci_free_resource_list(struct list_head *resources) { - struct pci_host_bridge_window *window, *tmp; - - list_for_each_entry_safe(window, tmp, resources, list) { - list_del(&window->list); - kfree(window); - } + resource_list_free(resources); } EXPORT_SYMBOL(pci_free_resource_list); diff --git a/drivers/pci/host-bridge.c b/drivers/pci/host-bridge.c index 0e5f3c95af5b..39b2dbe585aa 100644 --- a/drivers/pci/host-bridge.c +++ b/drivers/pci/host-bridge.c @@ -35,10 +35,10 @@ void pcibios_resource_to_bus(struct pci_bus *bus, struct pci_bus_region *region, struct resource *res) { struct pci_host_bridge *bridge = find_pci_host_bridge(bus); - struct pci_host_bridge_window *window; + struct resource_entry *window; resource_size_t offset = 0; - list_for_each_entry(window, &bridge->windows, list) { + resource_list_for_each_entry(window, &bridge->windows) { if (resource_contains(window->res, res)) { offset = window->offset; break; @@ -60,10 +60,10 @@ void pcibios_bus_to_resource(struct pci_bus *bus, struct resource *res, struct pci_bus_region *region) { struct pci_host_bridge *bridge = find_pci_host_bridge(bus); - struct pci_host_bridge_window *window; + struct resource_entry *window; resource_size_t offset = 0; - list_for_each_entry(window, &bridge->windows, list) { + resource_list_for_each_entry(window, &bridge->windows) { struct pci_bus_region bus_region; if (resource_type(res) != resource_type(window->res)) diff --git a/drivers/pci/host/pci-host-generic.c b/drivers/pci/host/pci-host-generic.c index 6eb1aa75bd37..aee3c620ecf9 100644 --- a/drivers/pci/host/pci-host-generic.c +++ b/drivers/pci/host/pci-host-generic.c @@ -149,14 +149,14 @@ static int gen_pci_parse_request_of_pci_ranges(struct gen_pci *pci) struct device *dev = pci->host.dev.parent; struct device_node *np = dev->of_node; resource_size_t iobase; - struct pci_host_bridge_window *win; + struct resource_entry *win; err = of_pci_get_host_bridge_resources(np, 0, 0xff, &pci->resources, &iobase); if (err) return err; - list_for_each_entry(win, &pci->resources, list) { + resource_list_for_each_entry(win, &pci->resources) { struct resource *parent, *res = win->res; switch (resource_type(res)) { diff --git a/drivers/pci/host/pci-xgene.c b/drivers/pci/host/pci-xgene.c index b1d0596457c5..a704257bab7f 100644 --- a/drivers/pci/host/pci-xgene.c +++ b/drivers/pci/host/pci-xgene.c @@ -401,11 +401,11 @@ static int xgene_pcie_map_ranges(struct xgene_pcie_port *port, struct list_head *res, resource_size_t io_base) { - struct pci_host_bridge_window *window; + struct resource_entry *window; struct device *dev = port->dev; int ret; - list_for_each_entry(window, res, list) { + resource_list_for_each_entry(window, res) { struct resource *res = window->res; u64 restype = resource_type(res); diff --git a/drivers/pci/host/pcie-xilinx.c b/drivers/pci/host/pcie-xilinx.c index ef3ebaf9a738..601261df7663 100644 --- a/drivers/pci/host/pcie-xilinx.c +++ b/drivers/pci/host/pcie-xilinx.c @@ -737,7 +737,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) resource_size_t offset; struct of_pci_range_parser parser; struct of_pci_range range; - struct pci_host_bridge_window *win; + struct resource_entry *win; int err = 0, mem_resno = 0; /* Get the ranges */ @@ -807,7 +807,7 @@ static int xilinx_pcie_parse_and_add_res(struct xilinx_pcie_port *port) free_resources: release_child_resources(&iomem_resource); - list_for_each_entry(win, &port->resources, list) + resource_list_for_each_entry(win, &port->resources) devm_kfree(dev, win->res); pci_free_resource_list(&port->resources); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 23212f8ae09b..8d2f400e96cb 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -1895,7 +1895,7 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, int error; struct pci_host_bridge *bridge; struct pci_bus *b, *b2; - struct pci_host_bridge_window *window, *n; + struct resource_entry *window, *n; struct resource *res; resource_size_t offset; char bus_addr[64]; @@ -1959,8 +1959,8 @@ struct pci_bus *pci_create_root_bus(struct device *parent, int bus, printk(KERN_INFO "PCI host bridge to bus %s\n", dev_name(&b->dev)); /* Add initial resources to the bus */ - list_for_each_entry_safe(window, n, resources, list) { - list_move_tail(&window->list, &bridge->windows); + resource_list_for_each_entry_safe(window, n, resources) { + list_move_tail(&window->node, &bridge->windows); res = window->res; offset = window->offset; if (res->flags & IORESOURCE_BUS) @@ -2060,12 +2060,12 @@ void pci_bus_release_busn_res(struct pci_bus *b) struct pci_bus *pci_scan_root_bus(struct device *parent, int bus, struct pci_ops *ops, void *sysdata, struct list_head *resources) { - struct pci_host_bridge_window *window; + struct resource_entry *window; bool found = false; struct pci_bus *b; int max; - list_for_each_entry(window, resources, list) + resource_list_for_each_entry(window, resources) if (window->res->flags & IORESOURCE_BUS) { found = true; break; diff --git a/include/linux/pci.h b/include/linux/pci.h index 9603094ed59b..faa60fa26314 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -29,6 +29,7 @@ #include #include #include +#include #include #include @@ -397,16 +398,10 @@ static inline int pci_channel_offline(struct pci_dev *pdev) return (pdev->error_state != pci_channel_io_normal); } -struct pci_host_bridge_window { - struct list_head list; - struct resource *res; /* host bridge aperture (CPU address) */ - resource_size_t offset; /* bus address + offset = CPU address */ -}; - struct pci_host_bridge { struct device dev; struct pci_bus *bus; /* root bus */ - struct list_head windows; /* pci_host_bridge_windows */ + struct list_head windows; /* resource_entry */ void (*release_fn)(struct pci_host_bridge *); void *release_data; }; -- cgit v1.2.3