Skip to content

Commit b67ea76

Browse files
rjwysockijbarnes993
authored andcommitted
PCI / ACPI / PM: Platform support for PCI PME wake-up
Although the majority of PCI devices can generate PMEs that in principle may be used to wake up devices suspended at run time, platform support is generally necessary to convert PMEs into wake-up events that can be delivered to the kernel. If ACPI is used for this purpose, PME signals generated by a PCI device will trigger the ACPI GPE associated with the device to generate an ACPI wake-up event that we can set up a handler for, provided that everything is configured correctly. Unfortunately, the subset of PCI devices that have GPEs associated with them is quite limited. The devices without dedicated GPEs have to rely on the GPEs associated with other devices (in the majority of cases their upstream bridges and, possibly, the root bridge) to generate ACPI wake-up events in response to PME signals from them. Add ACPI platform support for PCI PME wake-up: o Add a framework making is possible to use ACPI system notify handlers for run-time PM. o Add new PCI platform callback ->run_wake() to struct pci_platform_pm_ops allowing us to enable/disable the platform to generate wake-up events for given device. Implemet this callback for the ACPI platform. o Define ACPI wake-up handlers for PCI devices and PCI root buses and make the PCI-ACPI binding code register wake-up notifiers for all PCI devices present in the ACPI tables. o Add function pci_dev_run_wake() which can be used by PCI drivers to check if given device is capable of generating wake-up events at run time. Developed in cooperation with Matthew Garrett <mjg@redhat.com>. Signed-off-by: Rafael J. Wysocki <rjw@sisk.pl> Signed-off-by: Jesse Barnes <jbarnes@virtuousgeek.org>
1 parent 3f0be67 commit b67ea76

File tree

10 files changed

+319
-3
lines changed

10 files changed

+319
-3
lines changed

drivers/acpi/internal.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,6 @@ static inline int acpi_debug_init(void) { return 0; }
3636
int acpi_power_init(void);
3737
int acpi_device_sleep_wake(struct acpi_device *dev,
3838
int enable, int sleep_state, int dev_state);
39-
int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state);
40-
int acpi_disable_wakeup_device_power(struct acpi_device *dev);
4139
int acpi_power_get_inferred_state(struct acpi_device *device);
4240
int acpi_power_transition(struct acpi_device *device, int state);
4341
extern int acpi_power_nocheck;

drivers/acpi/pci_bind.c

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,9 @@
2626
#include <linux/kernel.h>
2727
#include <linux/types.h>
2828
#include <linux/pci.h>
29+
#include <linux/pci-acpi.h>
2930
#include <linux/acpi.h>
31+
#include <linux/pm_runtime.h>
3032
#include <acpi/acpi_bus.h>
3133
#include <acpi/acpi_drivers.h>
3234

@@ -38,7 +40,13 @@ static int acpi_pci_unbind(struct acpi_device *device)
3840
struct pci_dev *dev;
3941

4042
dev = acpi_get_pci_dev(device->handle);
41-
if (!dev || !dev->subordinate)
43+
if (!dev)
44+
goto out;
45+
46+
device_set_run_wake(&dev->dev, false);
47+
pci_acpi_remove_pm_notifier(device);
48+
49+
if (!dev->subordinate)
4250
goto out;
4351

4452
acpi_pci_irq_del_prt(dev->subordinate);
@@ -62,6 +70,10 @@ static int acpi_pci_bind(struct acpi_device *device)
6270
if (!dev)
6371
return 0;
6472

73+
pci_acpi_add_pm_notifier(device, dev);
74+
if (device->wakeup.flags.run_wake)
75+
device_set_run_wake(&dev->dev, true);
76+
6577
/*
6678
* Install the 'bind' function to facilitate callbacks for
6779
* children of the P2P bridge.

drivers/acpi/pci_root.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
#include <linux/proc_fs.h>
3131
#include <linux/spinlock.h>
3232
#include <linux/pm.h>
33+
#include <linux/pm_runtime.h>
3334
#include <linux/pci.h>
3435
#include <linux/pci-acpi.h>
3536
#include <linux/acpi.h>
@@ -528,6 +529,10 @@ static int __devinit acpi_pci_root_add(struct acpi_device *device)
528529
if (flags != base_flags)
529530
acpi_pci_osc_support(root, flags);
530531

532+
pci_acpi_add_bus_pm_notifier(device, root->bus);
533+
if (device->wakeup.flags.run_wake)
534+
device_set_run_wake(root->bus->bridge, true);
535+
531536
return 0;
532537

533538
end:
@@ -549,6 +554,9 @@ static int acpi_pci_root_remove(struct acpi_device *device, int type)
549554
{
550555
struct acpi_pci_root *root = acpi_driver_data(device);
551556

557+
device_set_run_wake(root->bus->bridge, false);
558+
pci_acpi_remove_bus_pm_notifier(device);
559+
552560
kfree(root);
553561
return 0;
554562
}

drivers/acpi/scan.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -753,6 +753,7 @@ static void acpi_bus_set_run_wake_flags(struct acpi_device *device)
753753
acpi_event_status event_status;
754754

755755
device->wakeup.run_wake_count = 0;
756+
device->wakeup.flags.notifier_present = 0;
756757

757758
/* Power button, Lid switch always enable wakeup */
758759
if (!acpi_match_device_ids(device, button_device_ids)) {

drivers/pci/pci-acpi.c

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,8 +16,144 @@
1616
#include <acpi/acpi_bus.h>
1717

1818
#include <linux/pci-acpi.h>
19+
#include <linux/pm_runtime.h>
1920
#include "pci.h"
2021

22+
static DEFINE_MUTEX(pci_acpi_pm_notify_mtx);
23+
24+
/**
25+
* pci_acpi_wake_bus - Wake-up notification handler for root buses.
26+
* @handle: ACPI handle of a device the notification is for.
27+
* @event: Type of the signaled event.
28+
* @context: PCI root bus to wake up devices on.
29+
*/
30+
static void pci_acpi_wake_bus(acpi_handle handle, u32 event, void *context)
31+
{
32+
struct pci_bus *pci_bus = context;
33+
34+
if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_bus)
35+
pci_pme_wakeup_bus(pci_bus);
36+
}
37+
38+
/**
39+
* pci_acpi_wake_dev - Wake-up notification handler for PCI devices.
40+
* @handle: ACPI handle of a device the notification is for.
41+
* @event: Type of the signaled event.
42+
* @context: PCI device object to wake up.
43+
*/
44+
static void pci_acpi_wake_dev(acpi_handle handle, u32 event, void *context)
45+
{
46+
struct pci_dev *pci_dev = context;
47+
48+
if (event == ACPI_NOTIFY_DEVICE_WAKE && pci_dev) {
49+
pci_check_pme_status(pci_dev);
50+
pm_runtime_resume(&pci_dev->dev);
51+
if (pci_dev->subordinate)
52+
pci_pme_wakeup_bus(pci_dev->subordinate);
53+
}
54+
}
55+
56+
/**
57+
* add_pm_notifier - Register PM notifier for given ACPI device.
58+
* @dev: ACPI device to add the notifier for.
59+
* @context: PCI device or bus to check for PME status if an event is signaled.
60+
*
61+
* NOTE: @dev need not be a run-wake or wake-up device to be a valid source of
62+
* PM wake-up events. For example, wake-up events may be generated for bridges
63+
* if one of the devices below the bridge is signaling PME, even if the bridge
64+
* itself doesn't have a wake-up GPE associated with it.
65+
*/
66+
static acpi_status add_pm_notifier(struct acpi_device *dev,
67+
acpi_notify_handler handler,
68+
void *context)
69+
{
70+
acpi_status status = AE_ALREADY_EXISTS;
71+
72+
mutex_lock(&pci_acpi_pm_notify_mtx);
73+
74+
if (dev->wakeup.flags.notifier_present)
75+
goto out;
76+
77+
status = acpi_install_notify_handler(dev->handle,
78+
ACPI_SYSTEM_NOTIFY,
79+
handler, context);
80+
if (ACPI_FAILURE(status))
81+
goto out;
82+
83+
dev->wakeup.flags.notifier_present = true;
84+
85+
out:
86+
mutex_unlock(&pci_acpi_pm_notify_mtx);
87+
return status;
88+
}
89+
90+
/**
91+
* remove_pm_notifier - Unregister PM notifier from given ACPI device.
92+
* @dev: ACPI device to remove the notifier from.
93+
*/
94+
static acpi_status remove_pm_notifier(struct acpi_device *dev,
95+
acpi_notify_handler handler)
96+
{
97+
acpi_status status = AE_BAD_PARAMETER;
98+
99+
mutex_lock(&pci_acpi_pm_notify_mtx);
100+
101+
if (!dev->wakeup.flags.notifier_present)
102+
goto out;
103+
104+
status = acpi_remove_notify_handler(dev->handle,
105+
ACPI_SYSTEM_NOTIFY,
106+
handler);
107+
if (ACPI_FAILURE(status))
108+
goto out;
109+
110+
dev->wakeup.flags.notifier_present = false;
111+
112+
out:
113+
mutex_unlock(&pci_acpi_pm_notify_mtx);
114+
return status;
115+
}
116+
117+
/**
118+
* pci_acpi_add_bus_pm_notifier - Register PM notifier for given PCI bus.
119+
* @dev: ACPI device to add the notifier for.
120+
* @pci_bus: PCI bus to walk checking for PME status if an event is signaled.
121+
*/
122+
acpi_status pci_acpi_add_bus_pm_notifier(struct acpi_device *dev,
123+
struct pci_bus *pci_bus)
124+
{
125+
return add_pm_notifier(dev, pci_acpi_wake_bus, pci_bus);
126+
}
127+
128+
/**
129+
* pci_acpi_remove_bus_pm_notifier - Unregister PCI bus PM notifier.
130+
* @dev: ACPI device to remove the notifier from.
131+
*/
132+
acpi_status pci_acpi_remove_bus_pm_notifier(struct acpi_device *dev)
133+
{
134+
return remove_pm_notifier(dev, pci_acpi_wake_bus);
135+
}
136+
137+
/**
138+
* pci_acpi_add_pm_notifier - Register PM notifier for given PCI device.
139+
* @dev: ACPI device to add the notifier for.
140+
* @pci_dev: PCI device to check for the PME status if an event is signaled.
141+
*/
142+
acpi_status pci_acpi_add_pm_notifier(struct acpi_device *dev,
143+
struct pci_dev *pci_dev)
144+
{
145+
return add_pm_notifier(dev, pci_acpi_wake_dev, pci_dev);
146+
}
147+
148+
/**
149+
* pci_acpi_remove_pm_notifier - Unregister PCI device PM notifier.
150+
* @dev: ACPI device to remove the notifier from.
151+
*/
152+
acpi_status pci_acpi_remove_pm_notifier(struct acpi_device *dev)
153+
{
154+
return remove_pm_notifier(dev, pci_acpi_wake_dev);
155+
}
156+
21157
/*
22158
* _SxD returns the D-state with the highest power
23159
* (lowest D-state number) supported in the S-state "x".
@@ -131,12 +267,87 @@ static int acpi_pci_sleep_wake(struct pci_dev *dev, bool enable)
131267
return 0;
132268
}
133269

270+
/**
271+
* acpi_dev_run_wake - Enable/disable wake-up for given device.
272+
* @phys_dev: Device to enable/disable the platform to wake-up the system for.
273+
* @enable: Whether enable or disable the wake-up functionality.
274+
*
275+
* Find the ACPI device object corresponding to @pci_dev and try to
276+
* enable/disable the GPE associated with it.
277+
*/
278+
static int acpi_dev_run_wake(struct device *phys_dev, bool enable)
279+
{
280+
struct acpi_device *dev;
281+
acpi_handle handle;
282+
int error = -ENODEV;
283+
284+
if (!device_run_wake(phys_dev))
285+
return -EINVAL;
286+
287+
handle = DEVICE_ACPI_HANDLE(phys_dev);
288+
if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &dev))) {
289+
dev_dbg(phys_dev, "ACPI handle has no context in %s!\n",
290+
__func__);
291+
return -ENODEV;
292+
}
293+
294+
if (enable) {
295+
if (!dev->wakeup.run_wake_count++) {
296+
acpi_enable_wakeup_device_power(dev, ACPI_STATE_S0);
297+
acpi_enable_gpe(dev->wakeup.gpe_device,
298+
dev->wakeup.gpe_number,
299+
ACPI_GPE_TYPE_RUNTIME);
300+
}
301+
} else if (dev->wakeup.run_wake_count > 0) {
302+
if (!--dev->wakeup.run_wake_count) {
303+
acpi_disable_gpe(dev->wakeup.gpe_device,
304+
dev->wakeup.gpe_number,
305+
ACPI_GPE_TYPE_RUNTIME);
306+
acpi_disable_wakeup_device_power(dev);
307+
}
308+
} else {
309+
error = -EALREADY;
310+
}
311+
312+
return error;
313+
}
314+
315+
static void acpi_pci_propagate_run_wake(struct pci_bus *bus, bool enable)
316+
{
317+
while (bus->parent) {
318+
struct pci_dev *bridge = bus->self;
319+
320+
if (bridge->pme_interrupt)
321+
return;
322+
if (!acpi_dev_run_wake(&bridge->dev, enable))
323+
return;
324+
bus = bus->parent;
325+
}
326+
327+
/* We have reached the root bus. */
328+
if (bus->bridge)
329+
acpi_dev_run_wake(bus->bridge, enable);
330+
}
331+
332+
static int acpi_pci_run_wake(struct pci_dev *dev, bool enable)
333+
{
334+
if (dev->pme_interrupt)
335+
return 0;
336+
337+
if (!acpi_dev_run_wake(&dev->dev, enable))
338+
return 0;
339+
340+
acpi_pci_propagate_run_wake(dev->bus, enable);
341+
return 0;
342+
}
343+
134344
static struct pci_platform_pm_ops acpi_pci_platform_pm = {
135345
.is_manageable = acpi_pci_power_manageable,
136346
.set_state = acpi_pci_set_power_state,
137347
.choose_state = acpi_pci_choose_state,
138348
.can_wakeup = acpi_pci_can_wakeup,
139349
.sleep_wake = acpi_pci_sleep_wake,
350+
.run_wake = acpi_pci_run_wake,
140351
};
141352

142353
/* ACPI bus type */

0 commit comments

Comments
 (0)