.. | .. |
---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
---|
1 | 2 | /* |
---|
2 | 3 | * drivers/acpi/power.c - ACPI Power Resources management. |
---|
3 | 4 | * |
---|
.. | .. |
---|
5 | 6 | * Author: Andy Grover <andrew.grover@intel.com> |
---|
6 | 7 | * Author: Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
---|
7 | 8 | * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
---|
8 | | - * |
---|
9 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
10 | | - * |
---|
11 | | - * This program is free software; you can redistribute it and/or modify |
---|
12 | | - * it under the terms of the GNU General Public License as published by |
---|
13 | | - * the Free Software Foundation; either version 2 of the License, or (at |
---|
14 | | - * your option) any later version. |
---|
15 | | - * |
---|
16 | | - * This program is distributed in the hope that it will be useful, but |
---|
17 | | - * WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
18 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
---|
19 | | - * General Public License for more details. |
---|
20 | | - * |
---|
21 | | - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
---|
22 | 9 | */ |
---|
23 | 10 | |
---|
24 | 11 | /* |
---|
.. | .. |
---|
26 | 13 | * 1. via "Device Specific (D-State) Control" |
---|
27 | 14 | * 2. via "Power Resource Control". |
---|
28 | 15 | * The code below deals with ACPI Power Resources control. |
---|
29 | | - * |
---|
| 16 | + * |
---|
30 | 17 | * An ACPI "power resource object" represents a software controllable power |
---|
31 | 18 | * plane, clock plane, or other resource depended on by a device. |
---|
32 | 19 | * |
---|
.. | .. |
---|
49 | 36 | ACPI_MODULE_NAME("power"); |
---|
50 | 37 | #define ACPI_POWER_CLASS "power_resource" |
---|
51 | 38 | #define ACPI_POWER_DEVICE_NAME "Power Resource" |
---|
52 | | -#define ACPI_POWER_FILE_INFO "info" |
---|
53 | | -#define ACPI_POWER_FILE_STATUS "state" |
---|
54 | 39 | #define ACPI_POWER_RESOURCE_STATE_OFF 0x00 |
---|
55 | 40 | #define ACPI_POWER_RESOURCE_STATE_ON 0x01 |
---|
56 | 41 | #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF |
---|
| 42 | + |
---|
| 43 | +struct acpi_power_dependent_device { |
---|
| 44 | + struct device *dev; |
---|
| 45 | + struct list_head node; |
---|
| 46 | +}; |
---|
57 | 47 | |
---|
58 | 48 | struct acpi_power_resource { |
---|
59 | 49 | struct acpi_device device; |
---|
.. | .. |
---|
64 | 54 | unsigned int ref_count; |
---|
65 | 55 | bool wakeup_enabled; |
---|
66 | 56 | struct mutex resource_lock; |
---|
| 57 | + struct list_head dependents; |
---|
67 | 58 | }; |
---|
68 | 59 | |
---|
69 | 60 | struct acpi_power_resource_entry { |
---|
.. | .. |
---|
245 | 236 | return 0; |
---|
246 | 237 | } |
---|
247 | 238 | |
---|
| 239 | +static int |
---|
| 240 | +acpi_power_resource_add_dependent(struct acpi_power_resource *resource, |
---|
| 241 | + struct device *dev) |
---|
| 242 | +{ |
---|
| 243 | + struct acpi_power_dependent_device *dep; |
---|
| 244 | + int ret = 0; |
---|
| 245 | + |
---|
| 246 | + mutex_lock(&resource->resource_lock); |
---|
| 247 | + list_for_each_entry(dep, &resource->dependents, node) { |
---|
| 248 | + /* Only add it once */ |
---|
| 249 | + if (dep->dev == dev) |
---|
| 250 | + goto unlock; |
---|
| 251 | + } |
---|
| 252 | + |
---|
| 253 | + dep = kzalloc(sizeof(*dep), GFP_KERNEL); |
---|
| 254 | + if (!dep) { |
---|
| 255 | + ret = -ENOMEM; |
---|
| 256 | + goto unlock; |
---|
| 257 | + } |
---|
| 258 | + |
---|
| 259 | + dep->dev = dev; |
---|
| 260 | + list_add_tail(&dep->node, &resource->dependents); |
---|
| 261 | + dev_dbg(dev, "added power dependency to [%s]\n", resource->name); |
---|
| 262 | + |
---|
| 263 | +unlock: |
---|
| 264 | + mutex_unlock(&resource->resource_lock); |
---|
| 265 | + return ret; |
---|
| 266 | +} |
---|
| 267 | + |
---|
| 268 | +static void |
---|
| 269 | +acpi_power_resource_remove_dependent(struct acpi_power_resource *resource, |
---|
| 270 | + struct device *dev) |
---|
| 271 | +{ |
---|
| 272 | + struct acpi_power_dependent_device *dep; |
---|
| 273 | + |
---|
| 274 | + mutex_lock(&resource->resource_lock); |
---|
| 275 | + list_for_each_entry(dep, &resource->dependents, node) { |
---|
| 276 | + if (dep->dev == dev) { |
---|
| 277 | + list_del(&dep->node); |
---|
| 278 | + kfree(dep); |
---|
| 279 | + dev_dbg(dev, "removed power dependency to [%s]\n", |
---|
| 280 | + resource->name); |
---|
| 281 | + break; |
---|
| 282 | + } |
---|
| 283 | + } |
---|
| 284 | + mutex_unlock(&resource->resource_lock); |
---|
| 285 | +} |
---|
| 286 | + |
---|
| 287 | +/** |
---|
| 288 | + * acpi_device_power_add_dependent - Add dependent device of this ACPI device |
---|
| 289 | + * @adev: ACPI device pointer |
---|
| 290 | + * @dev: Dependent device |
---|
| 291 | + * |
---|
| 292 | + * If @adev has non-empty _PR0 the @dev is added as dependent device to all |
---|
| 293 | + * power resources returned by it. This means that whenever these power |
---|
| 294 | + * resources are turned _ON the dependent devices get runtime resumed. This |
---|
| 295 | + * is needed for devices such as PCI to allow its driver to re-initialize |
---|
| 296 | + * it after it went to D0uninitialized. |
---|
| 297 | + * |
---|
| 298 | + * If @adev does not have _PR0 this does nothing. |
---|
| 299 | + * |
---|
| 300 | + * Returns %0 in case of success and negative errno otherwise. |
---|
| 301 | + */ |
---|
| 302 | +int acpi_device_power_add_dependent(struct acpi_device *adev, |
---|
| 303 | + struct device *dev) |
---|
| 304 | +{ |
---|
| 305 | + struct acpi_power_resource_entry *entry; |
---|
| 306 | + struct list_head *resources; |
---|
| 307 | + int ret; |
---|
| 308 | + |
---|
| 309 | + if (!adev->flags.power_manageable) |
---|
| 310 | + return 0; |
---|
| 311 | + |
---|
| 312 | + resources = &adev->power.states[ACPI_STATE_D0].resources; |
---|
| 313 | + list_for_each_entry(entry, resources, node) { |
---|
| 314 | + ret = acpi_power_resource_add_dependent(entry->resource, dev); |
---|
| 315 | + if (ret) |
---|
| 316 | + goto err; |
---|
| 317 | + } |
---|
| 318 | + |
---|
| 319 | + return 0; |
---|
| 320 | + |
---|
| 321 | +err: |
---|
| 322 | + list_for_each_entry(entry, resources, node) |
---|
| 323 | + acpi_power_resource_remove_dependent(entry->resource, dev); |
---|
| 324 | + |
---|
| 325 | + return ret; |
---|
| 326 | +} |
---|
| 327 | + |
---|
| 328 | +/** |
---|
| 329 | + * acpi_device_power_remove_dependent - Remove dependent device |
---|
| 330 | + * @adev: ACPI device pointer |
---|
| 331 | + * @dev: Dependent device |
---|
| 332 | + * |
---|
| 333 | + * Does the opposite of acpi_device_power_add_dependent() and removes the |
---|
| 334 | + * dependent device if it is found. Can be called to @adev that does not |
---|
| 335 | + * have _PR0 as well. |
---|
| 336 | + */ |
---|
| 337 | +void acpi_device_power_remove_dependent(struct acpi_device *adev, |
---|
| 338 | + struct device *dev) |
---|
| 339 | +{ |
---|
| 340 | + struct acpi_power_resource_entry *entry; |
---|
| 341 | + struct list_head *resources; |
---|
| 342 | + |
---|
| 343 | + if (!adev->flags.power_manageable) |
---|
| 344 | + return; |
---|
| 345 | + |
---|
| 346 | + resources = &adev->power.states[ACPI_STATE_D0].resources; |
---|
| 347 | + list_for_each_entry_reverse(entry, resources, node) |
---|
| 348 | + acpi_power_resource_remove_dependent(entry->resource, dev); |
---|
| 349 | +} |
---|
| 350 | + |
---|
248 | 351 | static int __acpi_power_on(struct acpi_power_resource *resource) |
---|
249 | 352 | { |
---|
| 353 | + struct acpi_power_dependent_device *dep; |
---|
250 | 354 | acpi_status status = AE_OK; |
---|
251 | 355 | |
---|
252 | 356 | status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL); |
---|
.. | .. |
---|
255 | 359 | |
---|
256 | 360 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n", |
---|
257 | 361 | resource->name)); |
---|
| 362 | + |
---|
| 363 | + /* |
---|
| 364 | + * If there are other dependents on this power resource we need to |
---|
| 365 | + * resume them now so that their drivers can re-initialize the |
---|
| 366 | + * hardware properly after it went back to D0. |
---|
| 367 | + */ |
---|
| 368 | + if (list_empty(&resource->dependents) || |
---|
| 369 | + list_is_singular(&resource->dependents)) |
---|
| 370 | + return 0; |
---|
| 371 | + |
---|
| 372 | + list_for_each_entry(dep, &resource->dependents, node) { |
---|
| 373 | + dev_dbg(dep->dev, "runtime resuming because [%s] turned on\n", |
---|
| 374 | + resource->name); |
---|
| 375 | + pm_request_resume(dep->dev); |
---|
| 376 | + } |
---|
258 | 377 | |
---|
259 | 378 | return 0; |
---|
260 | 379 | } |
---|
.. | .. |
---|
526 | 645 | * -ENODEV if the execution of either _DSW or _PSW has failed |
---|
527 | 646 | */ |
---|
528 | 647 | int acpi_device_sleep_wake(struct acpi_device *dev, |
---|
529 | | - int enable, int sleep_state, int dev_state) |
---|
| 648 | + int enable, int sleep_state, int dev_state) |
---|
530 | 649 | { |
---|
531 | 650 | union acpi_object in_arg[3]; |
---|
532 | 651 | struct acpi_object_list arg_list = { 3, in_arg }; |
---|
.. | .. |
---|
535 | 654 | /* |
---|
536 | 655 | * Try to execute _DSW first. |
---|
537 | 656 | * |
---|
538 | | - * Three agruments are needed for the _DSW object: |
---|
| 657 | + * Three arguments are needed for the _DSW object: |
---|
539 | 658 | * Argument 0: enable/disable the wake capabilities |
---|
540 | 659 | * Argument 1: target system state |
---|
541 | 660 | * Argument 2: target device state |
---|
542 | 661 | * When _DSW object is called to disable the wake capabilities, maybe |
---|
543 | | - * the first argument is filled. The values of the other two agruments |
---|
| 662 | + * the first argument is filled. The values of the other two arguments |
---|
544 | 663 | * are meaningless. |
---|
545 | 664 | */ |
---|
546 | 665 | in_arg[0].type = ACPI_TYPE_INTEGER; |
---|
.. | .. |
---|
571 | 690 | |
---|
572 | 691 | /* |
---|
573 | 692 | * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): |
---|
574 | | - * 1. Power on the power resources required for the wakeup device |
---|
| 693 | + * 1. Power on the power resources required for the wakeup device |
---|
575 | 694 | * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power |
---|
576 | 695 | * State Wake) for the device, if present |
---|
577 | 696 | */ |
---|
.. | .. |
---|
767 | 886 | kfree(resource); |
---|
768 | 887 | } |
---|
769 | 888 | |
---|
770 | | -static ssize_t acpi_power_in_use_show(struct device *dev, |
---|
771 | | - struct device_attribute *attr, |
---|
772 | | - char *buf) { |
---|
| 889 | +static ssize_t resource_in_use_show(struct device *dev, |
---|
| 890 | + struct device_attribute *attr, |
---|
| 891 | + char *buf) |
---|
| 892 | +{ |
---|
773 | 893 | struct acpi_power_resource *resource; |
---|
774 | 894 | |
---|
775 | 895 | resource = to_power_resource(to_acpi_device(dev)); |
---|
776 | 896 | return sprintf(buf, "%u\n", !!resource->ref_count); |
---|
777 | 897 | } |
---|
778 | | -static DEVICE_ATTR(resource_in_use, 0444, acpi_power_in_use_show, NULL); |
---|
| 898 | +static DEVICE_ATTR_RO(resource_in_use); |
---|
779 | 899 | |
---|
780 | 900 | static void acpi_power_sysfs_remove(struct acpi_device *device) |
---|
781 | 901 | { |
---|
.. | .. |
---|
823 | 943 | ACPI_STA_DEFAULT); |
---|
824 | 944 | mutex_init(&resource->resource_lock); |
---|
825 | 945 | INIT_LIST_HEAD(&resource->list_node); |
---|
| 946 | + INIT_LIST_HEAD(&resource->dependents); |
---|
826 | 947 | resource->name = device->pnp.bus_id; |
---|
827 | 948 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); |
---|
828 | 949 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); |
---|