summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/device_pm.c80
-rw-r--r--drivers/acpi/pci_root.c2
-rw-r--r--drivers/pci/pci-acpi.c60
-rw-r--r--include/acpi/acpi_bus.h21
-rw-r--r--include/linux/pci-acpi.h13
5 files changed, 101 insertions, 75 deletions
diff --git a/drivers/acpi/device_pm.c b/drivers/acpi/device_pm.c
index 49a51277f81..366de0b0c39 100644
--- a/drivers/acpi/device_pm.c
+++ b/drivers/acpi/device_pm.c
@@ -367,29 +367,61 @@ EXPORT_SYMBOL(acpi_bus_power_manageable);
#ifdef CONFIG_PM
static DEFINE_MUTEX(acpi_pm_notifier_lock);
+static void acpi_pm_notify_handler(acpi_handle handle, u32 val, void *not_used)
+{
+ struct acpi_device *adev;
+
+ if (val != ACPI_NOTIFY_DEVICE_WAKE)
+ return;
+
+ adev = acpi_bus_get_acpi_device(handle);
+ if (!adev)
+ return;
+
+ mutex_lock(&acpi_pm_notifier_lock);
+
+ if (adev->wakeup.flags.notifier_present) {
+ __pm_wakeup_event(adev->wakeup.ws, 0);
+ if (adev->wakeup.context.work.func)
+ queue_pm_work(&adev->wakeup.context.work);
+ }
+
+ mutex_unlock(&acpi_pm_notifier_lock);
+
+ acpi_bus_put_acpi_device(adev);
+}
+
/**
- * acpi_add_pm_notifier - Register PM notifier for given ACPI device.
- * @adev: ACPI device to add the notifier for.
- * @context: Context information to pass to the notifier routine.
+ * acpi_add_pm_notifier - Register PM notify handler for given ACPI device.
+ * @adev: ACPI device to add the notify handler for.
+ * @dev: Device to generate a wakeup event for while handling the notification.
+ * @work_func: Work function to execute when handling the notification.
*
* NOTE: @adev need not be a run-wake or wakeup device to be a valid source of
* PM wakeup events. For example, wakeup events may be generated for bridges
* if one of the devices below the bridge is signaling wakeup, even if the
* bridge itself doesn't have a wakeup GPE associated with it.
*/
-acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler, void *context)
+acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
+ void (*work_func)(struct work_struct *work))
{
acpi_status status = AE_ALREADY_EXISTS;
+ if (!dev && !work_func)
+ return AE_BAD_PARAMETER;
+
mutex_lock(&acpi_pm_notifier_lock);
if (adev->wakeup.flags.notifier_present)
goto out;
- status = acpi_install_notify_handler(adev->handle,
- ACPI_SYSTEM_NOTIFY,
- handler, context);
+ adev->wakeup.ws = wakeup_source_register(dev_name(&adev->dev));
+ adev->wakeup.context.dev = dev;
+ if (work_func)
+ INIT_WORK(&adev->wakeup.context.work, work_func);
+
+ status = acpi_install_notify_handler(adev->handle, ACPI_SYSTEM_NOTIFY,
+ acpi_pm_notify_handler, NULL);
if (ACPI_FAILURE(status))
goto out;
@@ -404,8 +436,7 @@ acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
* acpi_remove_pm_notifier - Unregister PM notifier from given ACPI device.
* @adev: ACPI device to remove the notifier from.
*/
-acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler)
+acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
{
acpi_status status = AE_BAD_PARAMETER;
@@ -416,10 +447,17 @@ acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
status = acpi_remove_notify_handler(adev->handle,
ACPI_SYSTEM_NOTIFY,
- handler);
+ acpi_pm_notify_handler);
if (ACPI_FAILURE(status))
goto out;
+ if (adev->wakeup.context.work.func) {
+ cancel_work_sync(&adev->wakeup.context.work);
+ adev->wakeup.context.work.func = NULL;
+ }
+ adev->wakeup.context.dev = NULL;
+ wakeup_source_unregister(adev->wakeup.ws);
+
adev->wakeup.flags.notifier_present = false;
out:
@@ -602,16 +640,15 @@ EXPORT_SYMBOL(acpi_pm_device_sleep_state);
#ifdef CONFIG_PM_RUNTIME
/**
- * acpi_wakeup_device - Wakeup notification handler for ACPI devices.
- * @handle: ACPI handle of the device the notification is for.
- * @event: Type of the signaled event.
- * @context: Device corresponding to @handle.
+ * acpi_pm_notify_work_func - ACPI devices wakeup notification work function.
+ * @work: Work item to handle.
*/
-static void acpi_wakeup_device(acpi_handle handle, u32 event, void *context)
+static void acpi_pm_notify_work_func(struct work_struct *work)
{
- struct device *dev = context;
+ struct device *dev;
- if (event == ACPI_NOTIFY_DEVICE_WAKE && dev) {
+ dev = container_of(work, struct acpi_device_wakeup_context, work)->dev;
+ if (dev) {
pm_wakeup_event(dev, 0);
pm_runtime_resume(dev);
}
@@ -677,8 +714,7 @@ int acpi_pm_device_run_wake(struct device *phys_dev, bool enable)
}
EXPORT_SYMBOL(acpi_pm_device_run_wake);
#else
-static inline void acpi_wakeup_device(acpi_handle handle, u32 event,
- void *context) {}
+static inline void acpi_pm_notify_work_func(struct work_struct *work) {}
#endif /* CONFIG_PM_RUNTIME */
#ifdef CONFIG_PM_SLEEP
@@ -1048,7 +1084,7 @@ int acpi_dev_pm_attach(struct device *dev, bool power_on)
if (dev->pm_domain)
return -EEXIST;
- acpi_add_pm_notifier(adev, acpi_wakeup_device, dev);
+ acpi_add_pm_notifier(adev, dev, acpi_pm_notify_work_func);
dev->pm_domain = &acpi_general_pm_domain;
if (power_on) {
acpi_dev_pm_full_power(adev);
@@ -1076,7 +1112,7 @@ void acpi_dev_pm_detach(struct device *dev, bool power_off)
if (adev && dev->pm_domain == &acpi_general_pm_domain) {
dev->pm_domain = NULL;
- acpi_remove_pm_notifier(adev, acpi_wakeup_device);
+ acpi_remove_pm_notifier(adev);
if (power_off) {
/*
* If the device's PM QoS resume latency limit or flags
diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c
index d388f13d48b..e6ae603ed1a 100644
--- a/drivers/acpi/pci_root.c
+++ b/drivers/acpi/pci_root.c
@@ -593,7 +593,7 @@ static int acpi_pci_root_add(struct acpi_device *device,
if (no_aspm)
pcie_no_aspm();
- pci_acpi_add_bus_pm_notifier(device, root->bus);
+ pci_acpi_add_bus_pm_notifier(device);
if (device->wakeup.flags.run_wake)
device_set_run_wake(root->bus->bridge, true);
diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c
index ca4927ba843..7b8b2298840 100644
--- a/drivers/pci/pci-acpi.c
+++ b/drivers/pci/pci-acpi.c
@@ -18,31 +18,31 @@
#include "pci.h"
/**
- * pci_acpi_wake_bus - Wake-up notification handler for root buses.
- * @handle: ACPI handle of a device the notification is for.
- * @event: Type of the signaled event.
- * @context: PCI root bus to wake up devices on.
+ * pci_acpi_wake_bus - Root bus wakeup notification fork function.
+ * @work: Work item to handle.
*/
-static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context)
+static void pci_acpi_wake_bus(struct work_struct *work)
{
- struct pci_bus *pci_bus = context;
+ struct acpi_device *adev;
+ struct acpi_pci_root *root;
- if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus)
- pci_pme_wakeup_bus(pci_bus);
+ adev = container_of(work, struct acpi_device, wakeup.context.work);
+ root = acpi_driver_data(adev);
+ pci_pme_wakeup_bus(root->bus);
}
/**
- * pci_acpi_wake_dev - Wake-up notification handler for PCI devices.
+ * pci_acpi_wake_dev - PCI device wakeup notification work function.
* @handle: ACPI handle of a device the notification is for.
- * @event: Type of the signaled event.
- * @context: PCI device object to wake up.
+ * @work: Work item to handle.
*/
-static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
+static void pci_acpi_wake_dev(struct work_struct *work)
{
- struct pci_dev *pci_dev = context;
+ struct acpi_device_wakeup_context *context;
+ struct pci_dev *pci_dev;
- if (event != ACPI_NOTIFY_DEVICE_WAKE || !pci_dev)
- return;
+ context = container_of(work, struct acpi_device_wakeup_context, work);
+ pci_dev = to_pci_dev(context->dev);
if (pci_dev->pme_poll)
pci_dev->pme_poll = false;
@@ -65,23 +65,12 @@ static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
}
/**
- * pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus.
- * @dev: ACPI device to add the notifier for.
- * @pci_bus: PCI bus to walk checking for PME status if an event is signaled.
+ * pci_acpi_add_bus_pm_notifier - Register PM notifier for root PCI bus.
+ * @dev: PCI root bridge ACPI device.
*/
-acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
- struct pci_bus *pci_bus)
+acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev)
{
- return acpi_add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus);
-}
-
-/**
- * pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier.
- * @dev: ACPI device to remove the notifier from.
- */
-acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
-{
- return acpi_remove_pm_notifier(dev, pci_acpi_wake_bus);
+ return acpi_add_pm_notifier(dev, NULL, pci_acpi_wake_bus);
}
/**
@@ -92,16 +81,7 @@ acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
struct pci_dev *pci_dev)
{
- return acpi_add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev);
-}
-
-/**
- * pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier.
- * @dev: ACPI device to remove the notifier from.
- */
-acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
-{
- return acpi_remove_pm_notifier(dev, pci_acpi_wake_dev);
+ return acpi_add_pm_notifier(dev, &pci_dev->dev, pci_acpi_wake_dev);
}
phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle)
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index b5714580801..99780d46abb 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -315,12 +315,19 @@ struct acpi_device_wakeup_flags {
u8 notifier_present:1; /* Wake-up notify handler has been installed */
};
+struct acpi_device_wakeup_context {
+ struct work_struct work;
+ struct device *dev;
+};
+
struct acpi_device_wakeup {
acpi_handle gpe_device;
u64 gpe_number;
u64 sleep_state;
struct list_head resources;
struct acpi_device_wakeup_flags flags;
+ struct acpi_device_wakeup_context context;
+ struct wakeup_source *ws;
int prepare_count;
};
@@ -510,20 +517,18 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int state);
int acpi_disable_wakeup_device_power(struct acpi_device *dev);
#ifdef CONFIG_PM
-acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler, void *context);
-acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler);
+acpi_status acpi_add_pm_notifier(struct acpi_device *adev, struct device *dev,
+ void (*work_func)(struct work_struct *work));
+acpi_status acpi_remove_pm_notifier(struct acpi_device *adev);
int acpi_pm_device_sleep_state(struct device *, int *, int);
#else
static inline acpi_status acpi_add_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler,
- void *context)
+ struct device *dev,
+ void (*work_func)(struct work_struct *work))
{
return AE_SUPPORT;
}
-static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev,
- acpi_notify_handler handler)
+static inline acpi_status acpi_remove_pm_notifier(struct acpi_device *adev)
{
return AE_SUPPORT;
}
diff --git a/include/linux/pci-acpi.h b/include/linux/pci-acpi.h
index 637a608ded0..64dacb7288a 100644
--- a/include/linux/pci-acpi.h
+++ b/include/linux/pci-acpi.h
@@ -11,12 +11,17 @@
#include <linux/acpi.h>
#ifdef CONFIG_ACPI
-extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
- struct pci_bus *pci_bus);
-extern acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev);
+extern acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev);
+static inline acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
+{
+ return acpi_remove_pm_notifier(dev);
+}
extern acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
struct pci_dev *pci_dev);
-extern acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev);
+static inline acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
+{
+ return acpi_remove_pm_notifier(dev);
+}
extern phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle);
static inline acpi_handle acpi_find_root_bridge_handle(struct pci_dev *pdev)