|
16 | 16 | #include <acpi/acpi_bus.h>
|
17 | 17 |
|
18 | 18 | #include <linux/pci-acpi.h>
|
| 19 | +#include <linux/pm_runtime.h> |
19 | 20 | #include "pci.h"
|
20 | 21 |
|
| 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 | + |
21 | 157 | /*
|
22 | 158 | * _SxD returns the D-state with the highest power
|
23 | 159 | * (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)
|
131 | 267 | return 0;
|
132 | 268 | }
|
133 | 269 |
|
| 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 | + |
134 | 344 | static struct pci_platform_pm_ops acpi_pci_platform_pm = {
|
135 | 345 | .is_manageable = acpi_pci_power_manageable,
|
136 | 346 | .set_state = acpi_pci_set_power_state,
|
137 | 347 | .choose_state = acpi_pci_choose_state,
|
138 | 348 | .can_wakeup = acpi_pci_can_wakeup,
|
139 | 349 | .sleep_wake = acpi_pci_sleep_wake,
|
| 350 | + .run_wake = acpi_pci_run_wake, |
140 | 351 | };
|
141 | 352 |
|
142 | 353 | /* ACPI bus type */
|
|
0 commit comments