1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | * drivers/acpi/power.c - ACPI Power Resources management. |
4 | * |
5 | * Copyright (C) 2001 - 2015 Intel Corp. |
6 | * Author: Andy Grover <andrew.grover@intel.com> |
7 | * Author: Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> |
8 | * Author: Rafael J. Wysocki <rafael.j.wysocki@intel.com> |
9 | */ |
10 | |
11 | /* |
12 | * ACPI power-managed devices may be controlled in two ways: |
13 | * 1. via "Device Specific (D-State) Control" |
14 | * 2. via "Power Resource Control". |
15 | * The code below deals with ACPI Power Resources control. |
16 | * |
17 | * An ACPI "power resource object" represents a software controllable power |
18 | * plane, clock plane, or other resource depended on by a device. |
19 | * |
20 | * A device may rely on multiple power resources, and a power resource |
21 | * may be shared by multiple devices. |
22 | */ |
23 | |
24 | #define pr_fmt(fmt) "ACPI: PM: " fmt |
25 | |
26 | #include <linux/dmi.h> |
27 | #include <linux/kernel.h> |
28 | #include <linux/module.h> |
29 | #include <linux/init.h> |
30 | #include <linux/types.h> |
31 | #include <linux/slab.h> |
32 | #include <linux/pm_runtime.h> |
33 | #include <linux/sysfs.h> |
34 | #include <linux/acpi.h> |
35 | #include "sleep.h" |
36 | #include "internal.h" |
37 | |
38 | #define ACPI_POWER_CLASS "power_resource" |
39 | #define ACPI_POWER_DEVICE_NAME "Power Resource" |
40 | #define ACPI_POWER_RESOURCE_STATE_OFF 0x00 |
41 | #define ACPI_POWER_RESOURCE_STATE_ON 0x01 |
42 | #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF |
43 | |
44 | struct acpi_power_dependent_device { |
45 | struct device *dev; |
46 | struct list_head node; |
47 | }; |
48 | |
49 | struct acpi_power_resource { |
50 | struct acpi_device device; |
51 | struct list_head list_node; |
52 | u32 system_level; |
53 | u32 order; |
54 | unsigned int ref_count; |
55 | u8 state; |
56 | struct mutex resource_lock; |
57 | struct list_head dependents; |
58 | }; |
59 | |
60 | struct acpi_power_resource_entry { |
61 | struct list_head node; |
62 | struct acpi_power_resource *resource; |
63 | }; |
64 | |
65 | static LIST_HEAD(acpi_power_resource_list); |
66 | static DEFINE_MUTEX(power_resource_list_lock); |
67 | |
68 | /* -------------------------------------------------------------------------- |
69 | Power Resource Management |
70 | -------------------------------------------------------------------------- */ |
71 | |
72 | static inline const char *resource_dev_name(struct acpi_power_resource *pr) |
73 | { |
74 | return dev_name(dev: &pr->device.dev); |
75 | } |
76 | |
77 | static inline |
78 | struct acpi_power_resource *to_power_resource(struct acpi_device *device) |
79 | { |
80 | return container_of(device, struct acpi_power_resource, device); |
81 | } |
82 | |
83 | static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle) |
84 | { |
85 | struct acpi_device *device = acpi_fetch_acpi_dev(handle); |
86 | |
87 | if (!device) |
88 | return NULL; |
89 | |
90 | return to_power_resource(device); |
91 | } |
92 | |
93 | static int acpi_power_resources_list_add(acpi_handle handle, |
94 | struct list_head *list) |
95 | { |
96 | struct acpi_power_resource *resource = acpi_power_get_context(handle); |
97 | struct acpi_power_resource_entry *entry; |
98 | |
99 | if (!resource || !list) |
100 | return -EINVAL; |
101 | |
102 | entry = kzalloc(size: sizeof(*entry), GFP_KERNEL); |
103 | if (!entry) |
104 | return -ENOMEM; |
105 | |
106 | entry->resource = resource; |
107 | if (!list_empty(head: list)) { |
108 | struct acpi_power_resource_entry *e; |
109 | |
110 | list_for_each_entry(e, list, node) |
111 | if (e->resource->order > resource->order) { |
112 | list_add_tail(new: &entry->node, head: &e->node); |
113 | return 0; |
114 | } |
115 | } |
116 | list_add_tail(new: &entry->node, head: list); |
117 | return 0; |
118 | } |
119 | |
120 | void acpi_power_resources_list_free(struct list_head *list) |
121 | { |
122 | struct acpi_power_resource_entry *entry, *e; |
123 | |
124 | list_for_each_entry_safe(entry, e, list, node) { |
125 | list_del(entry: &entry->node); |
126 | kfree(objp: entry); |
127 | } |
128 | } |
129 | |
130 | static bool acpi_power_resource_is_dup(union acpi_object *package, |
131 | unsigned int start, unsigned int i) |
132 | { |
133 | acpi_handle rhandle, dup; |
134 | unsigned int j; |
135 | |
136 | /* The caller is expected to check the package element types */ |
137 | rhandle = package->package.elements[i].reference.handle; |
138 | for (j = start; j < i; j++) { |
139 | dup = package->package.elements[j].reference.handle; |
140 | if (dup == rhandle) |
141 | return true; |
142 | } |
143 | |
144 | return false; |
145 | } |
146 | |
147 | int (union acpi_object *package, unsigned int start, |
148 | struct list_head *list) |
149 | { |
150 | unsigned int i; |
151 | int err = 0; |
152 | |
153 | for (i = start; i < package->package.count; i++) { |
154 | union acpi_object *element = &package->package.elements[i]; |
155 | struct acpi_device *rdev; |
156 | acpi_handle rhandle; |
157 | |
158 | if (element->type != ACPI_TYPE_LOCAL_REFERENCE) { |
159 | err = -ENODATA; |
160 | break; |
161 | } |
162 | rhandle = element->reference.handle; |
163 | if (!rhandle) { |
164 | err = -ENODEV; |
165 | break; |
166 | } |
167 | |
168 | /* Some ACPI tables contain duplicate power resource references */ |
169 | if (acpi_power_resource_is_dup(package, start, i)) |
170 | continue; |
171 | |
172 | rdev = acpi_add_power_resource(handle: rhandle); |
173 | if (!rdev) { |
174 | err = -ENODEV; |
175 | break; |
176 | } |
177 | err = acpi_power_resources_list_add(handle: rhandle, list); |
178 | if (err) |
179 | break; |
180 | } |
181 | if (err) |
182 | acpi_power_resources_list_free(list); |
183 | |
184 | return err; |
185 | } |
186 | |
187 | static int __get_state(acpi_handle handle, u8 *state) |
188 | { |
189 | acpi_status status = AE_OK; |
190 | unsigned long long sta = 0; |
191 | u8 cur_state; |
192 | |
193 | status = acpi_evaluate_integer(handle, pathname: "_STA" , NULL, data: &sta); |
194 | if (ACPI_FAILURE(status)) |
195 | return -ENODEV; |
196 | |
197 | cur_state = sta & ACPI_POWER_RESOURCE_STATE_ON; |
198 | |
199 | acpi_handle_debug(handle, "Power resource is %s\n" , |
200 | cur_state ? "on" : "off" ); |
201 | |
202 | *state = cur_state; |
203 | return 0; |
204 | } |
205 | |
206 | static int acpi_power_get_state(struct acpi_power_resource *resource, u8 *state) |
207 | { |
208 | if (resource->state == ACPI_POWER_RESOURCE_STATE_UNKNOWN) { |
209 | int ret; |
210 | |
211 | ret = __get_state(handle: resource->device.handle, state: &resource->state); |
212 | if (ret) |
213 | return ret; |
214 | } |
215 | |
216 | *state = resource->state; |
217 | return 0; |
218 | } |
219 | |
220 | static int acpi_power_get_list_state(struct list_head *list, u8 *state) |
221 | { |
222 | struct acpi_power_resource_entry *entry; |
223 | u8 cur_state = ACPI_POWER_RESOURCE_STATE_OFF; |
224 | |
225 | if (!list || !state) |
226 | return -EINVAL; |
227 | |
228 | /* The state of the list is 'on' IFF all resources are 'on'. */ |
229 | list_for_each_entry(entry, list, node) { |
230 | struct acpi_power_resource *resource = entry->resource; |
231 | int result; |
232 | |
233 | mutex_lock(&resource->resource_lock); |
234 | result = acpi_power_get_state(resource, state: &cur_state); |
235 | mutex_unlock(lock: &resource->resource_lock); |
236 | if (result) |
237 | return result; |
238 | |
239 | if (cur_state != ACPI_POWER_RESOURCE_STATE_ON) |
240 | break; |
241 | } |
242 | |
243 | pr_debug("Power resource list is %s\n" , cur_state ? "on" : "off" ); |
244 | |
245 | *state = cur_state; |
246 | return 0; |
247 | } |
248 | |
249 | static int |
250 | acpi_power_resource_add_dependent(struct acpi_power_resource *resource, |
251 | struct device *dev) |
252 | { |
253 | struct acpi_power_dependent_device *dep; |
254 | int ret = 0; |
255 | |
256 | mutex_lock(&resource->resource_lock); |
257 | list_for_each_entry(dep, &resource->dependents, node) { |
258 | /* Only add it once */ |
259 | if (dep->dev == dev) |
260 | goto unlock; |
261 | } |
262 | |
263 | dep = kzalloc(size: sizeof(*dep), GFP_KERNEL); |
264 | if (!dep) { |
265 | ret = -ENOMEM; |
266 | goto unlock; |
267 | } |
268 | |
269 | dep->dev = dev; |
270 | list_add_tail(new: &dep->node, head: &resource->dependents); |
271 | dev_dbg(dev, "added power dependency to [%s]\n" , |
272 | resource_dev_name(resource)); |
273 | |
274 | unlock: |
275 | mutex_unlock(lock: &resource->resource_lock); |
276 | return ret; |
277 | } |
278 | |
279 | static void |
280 | acpi_power_resource_remove_dependent(struct acpi_power_resource *resource, |
281 | struct device *dev) |
282 | { |
283 | struct acpi_power_dependent_device *dep; |
284 | |
285 | mutex_lock(&resource->resource_lock); |
286 | list_for_each_entry(dep, &resource->dependents, node) { |
287 | if (dep->dev == dev) { |
288 | list_del(entry: &dep->node); |
289 | kfree(objp: dep); |
290 | dev_dbg(dev, "removed power dependency to [%s]\n" , |
291 | resource_dev_name(resource)); |
292 | break; |
293 | } |
294 | } |
295 | mutex_unlock(lock: &resource->resource_lock); |
296 | } |
297 | |
298 | /** |
299 | * acpi_device_power_add_dependent - Add dependent device of this ACPI device |
300 | * @adev: ACPI device pointer |
301 | * @dev: Dependent device |
302 | * |
303 | * If @adev has non-empty _PR0 the @dev is added as dependent device to all |
304 | * power resources returned by it. This means that whenever these power |
305 | * resources are turned _ON the dependent devices get runtime resumed. This |
306 | * is needed for devices such as PCI to allow its driver to re-initialize |
307 | * it after it went to D0uninitialized. |
308 | * |
309 | * If @adev does not have _PR0 this does nothing. |
310 | * |
311 | * Returns %0 in case of success and negative errno otherwise. |
312 | */ |
313 | int acpi_device_power_add_dependent(struct acpi_device *adev, |
314 | struct device *dev) |
315 | { |
316 | struct acpi_power_resource_entry *entry; |
317 | struct list_head *resources; |
318 | int ret; |
319 | |
320 | if (!adev->flags.power_manageable) |
321 | return 0; |
322 | |
323 | resources = &adev->power.states[ACPI_STATE_D0].resources; |
324 | list_for_each_entry(entry, resources, node) { |
325 | ret = acpi_power_resource_add_dependent(resource: entry->resource, dev); |
326 | if (ret) |
327 | goto err; |
328 | } |
329 | |
330 | return 0; |
331 | |
332 | err: |
333 | list_for_each_entry(entry, resources, node) |
334 | acpi_power_resource_remove_dependent(resource: entry->resource, dev); |
335 | |
336 | return ret; |
337 | } |
338 | |
339 | /** |
340 | * acpi_device_power_remove_dependent - Remove dependent device |
341 | * @adev: ACPI device pointer |
342 | * @dev: Dependent device |
343 | * |
344 | * Does the opposite of acpi_device_power_add_dependent() and removes the |
345 | * dependent device if it is found. Can be called to @adev that does not |
346 | * have _PR0 as well. |
347 | */ |
348 | void acpi_device_power_remove_dependent(struct acpi_device *adev, |
349 | struct device *dev) |
350 | { |
351 | struct acpi_power_resource_entry *entry; |
352 | struct list_head *resources; |
353 | |
354 | if (!adev->flags.power_manageable) |
355 | return; |
356 | |
357 | resources = &adev->power.states[ACPI_STATE_D0].resources; |
358 | list_for_each_entry_reverse(entry, resources, node) |
359 | acpi_power_resource_remove_dependent(resource: entry->resource, dev); |
360 | } |
361 | |
362 | static int __acpi_power_on(struct acpi_power_resource *resource) |
363 | { |
364 | acpi_handle handle = resource->device.handle; |
365 | struct acpi_power_dependent_device *dep; |
366 | acpi_status status = AE_OK; |
367 | |
368 | status = acpi_evaluate_object(object: handle, pathname: "_ON" , NULL, NULL); |
369 | if (ACPI_FAILURE(status)) { |
370 | resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; |
371 | return -ENODEV; |
372 | } |
373 | |
374 | resource->state = ACPI_POWER_RESOURCE_STATE_ON; |
375 | |
376 | acpi_handle_debug(handle, "Power resource turned on\n" ); |
377 | |
378 | /* |
379 | * If there are other dependents on this power resource we need to |
380 | * resume them now so that their drivers can re-initialize the |
381 | * hardware properly after it went back to D0. |
382 | */ |
383 | if (list_empty(head: &resource->dependents) || |
384 | list_is_singular(head: &resource->dependents)) |
385 | return 0; |
386 | |
387 | list_for_each_entry(dep, &resource->dependents, node) { |
388 | dev_dbg(dep->dev, "runtime resuming because [%s] turned on\n" , |
389 | resource_dev_name(resource)); |
390 | pm_request_resume(dev: dep->dev); |
391 | } |
392 | |
393 | return 0; |
394 | } |
395 | |
396 | static int acpi_power_on_unlocked(struct acpi_power_resource *resource) |
397 | { |
398 | int result = 0; |
399 | |
400 | if (resource->ref_count++) { |
401 | acpi_handle_debug(resource->device.handle, |
402 | "Power resource already on\n" ); |
403 | } else { |
404 | result = __acpi_power_on(resource); |
405 | if (result) |
406 | resource->ref_count--; |
407 | } |
408 | return result; |
409 | } |
410 | |
411 | static int acpi_power_on(struct acpi_power_resource *resource) |
412 | { |
413 | int result; |
414 | |
415 | mutex_lock(&resource->resource_lock); |
416 | result = acpi_power_on_unlocked(resource); |
417 | mutex_unlock(lock: &resource->resource_lock); |
418 | return result; |
419 | } |
420 | |
421 | static int __acpi_power_off(struct acpi_power_resource *resource) |
422 | { |
423 | acpi_handle handle = resource->device.handle; |
424 | acpi_status status; |
425 | |
426 | status = acpi_evaluate_object(object: handle, pathname: "_OFF" , NULL, NULL); |
427 | if (ACPI_FAILURE(status)) { |
428 | resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; |
429 | return -ENODEV; |
430 | } |
431 | |
432 | resource->state = ACPI_POWER_RESOURCE_STATE_OFF; |
433 | |
434 | acpi_handle_debug(handle, "Power resource turned off\n" ); |
435 | |
436 | return 0; |
437 | } |
438 | |
439 | static int acpi_power_off_unlocked(struct acpi_power_resource *resource) |
440 | { |
441 | int result = 0; |
442 | |
443 | if (!resource->ref_count) { |
444 | acpi_handle_debug(resource->device.handle, |
445 | "Power resource already off\n" ); |
446 | return 0; |
447 | } |
448 | |
449 | if (--resource->ref_count) { |
450 | acpi_handle_debug(resource->device.handle, |
451 | "Power resource still in use\n" ); |
452 | } else { |
453 | result = __acpi_power_off(resource); |
454 | if (result) |
455 | resource->ref_count++; |
456 | } |
457 | return result; |
458 | } |
459 | |
460 | static int acpi_power_off(struct acpi_power_resource *resource) |
461 | { |
462 | int result; |
463 | |
464 | mutex_lock(&resource->resource_lock); |
465 | result = acpi_power_off_unlocked(resource); |
466 | mutex_unlock(lock: &resource->resource_lock); |
467 | return result; |
468 | } |
469 | |
470 | static int acpi_power_off_list(struct list_head *list) |
471 | { |
472 | struct acpi_power_resource_entry *entry; |
473 | int result = 0; |
474 | |
475 | list_for_each_entry_reverse(entry, list, node) { |
476 | result = acpi_power_off(resource: entry->resource); |
477 | if (result) |
478 | goto err; |
479 | } |
480 | return 0; |
481 | |
482 | err: |
483 | list_for_each_entry_continue(entry, list, node) |
484 | acpi_power_on(resource: entry->resource); |
485 | |
486 | return result; |
487 | } |
488 | |
489 | static int acpi_power_on_list(struct list_head *list) |
490 | { |
491 | struct acpi_power_resource_entry *entry; |
492 | int result = 0; |
493 | |
494 | list_for_each_entry(entry, list, node) { |
495 | result = acpi_power_on(resource: entry->resource); |
496 | if (result) |
497 | goto err; |
498 | } |
499 | return 0; |
500 | |
501 | err: |
502 | list_for_each_entry_continue_reverse(entry, list, node) |
503 | acpi_power_off(resource: entry->resource); |
504 | |
505 | return result; |
506 | } |
507 | |
508 | static struct attribute *attrs[] = { |
509 | NULL, |
510 | }; |
511 | |
512 | static const struct attribute_group attr_groups[] = { |
513 | [ACPI_STATE_D0] = { |
514 | .name = "power_resources_D0" , |
515 | .attrs = attrs, |
516 | }, |
517 | [ACPI_STATE_D1] = { |
518 | .name = "power_resources_D1" , |
519 | .attrs = attrs, |
520 | }, |
521 | [ACPI_STATE_D2] = { |
522 | .name = "power_resources_D2" , |
523 | .attrs = attrs, |
524 | }, |
525 | [ACPI_STATE_D3_HOT] = { |
526 | .name = "power_resources_D3hot" , |
527 | .attrs = attrs, |
528 | }, |
529 | }; |
530 | |
531 | static const struct attribute_group wakeup_attr_group = { |
532 | .name = "power_resources_wakeup" , |
533 | .attrs = attrs, |
534 | }; |
535 | |
536 | static void acpi_power_hide_list(struct acpi_device *adev, |
537 | struct list_head *resources, |
538 | const struct attribute_group *attr_group) |
539 | { |
540 | struct acpi_power_resource_entry *entry; |
541 | |
542 | if (list_empty(head: resources)) |
543 | return; |
544 | |
545 | list_for_each_entry_reverse(entry, resources, node) { |
546 | struct acpi_device *res_dev = &entry->resource->device; |
547 | |
548 | sysfs_remove_link_from_group(kobj: &adev->dev.kobj, |
549 | group_name: attr_group->name, |
550 | link_name: dev_name(dev: &res_dev->dev)); |
551 | } |
552 | sysfs_remove_group(kobj: &adev->dev.kobj, grp: attr_group); |
553 | } |
554 | |
555 | static void acpi_power_expose_list(struct acpi_device *adev, |
556 | struct list_head *resources, |
557 | const struct attribute_group *attr_group) |
558 | { |
559 | struct acpi_power_resource_entry *entry; |
560 | int ret; |
561 | |
562 | if (list_empty(head: resources)) |
563 | return; |
564 | |
565 | ret = sysfs_create_group(kobj: &adev->dev.kobj, grp: attr_group); |
566 | if (ret) |
567 | return; |
568 | |
569 | list_for_each_entry(entry, resources, node) { |
570 | struct acpi_device *res_dev = &entry->resource->device; |
571 | |
572 | ret = sysfs_add_link_to_group(kobj: &adev->dev.kobj, |
573 | group_name: attr_group->name, |
574 | target: &res_dev->dev.kobj, |
575 | link_name: dev_name(dev: &res_dev->dev)); |
576 | if (ret) { |
577 | acpi_power_hide_list(adev, resources, attr_group); |
578 | break; |
579 | } |
580 | } |
581 | } |
582 | |
583 | static void acpi_power_expose_hide(struct acpi_device *adev, |
584 | struct list_head *resources, |
585 | const struct attribute_group *attr_group, |
586 | bool expose) |
587 | { |
588 | if (expose) |
589 | acpi_power_expose_list(adev, resources, attr_group); |
590 | else |
591 | acpi_power_hide_list(adev, resources, attr_group); |
592 | } |
593 | |
594 | void acpi_power_add_remove_device(struct acpi_device *adev, bool add) |
595 | { |
596 | int state; |
597 | |
598 | if (adev->wakeup.flags.valid) |
599 | acpi_power_expose_hide(adev, resources: &adev->wakeup.resources, |
600 | attr_group: &wakeup_attr_group, expose: add); |
601 | |
602 | if (!adev->power.flags.power_resources) |
603 | return; |
604 | |
605 | for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++) |
606 | acpi_power_expose_hide(adev, |
607 | resources: &adev->power.states[state].resources, |
608 | attr_group: &attr_groups[state], expose: add); |
609 | } |
610 | |
611 | int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p) |
612 | { |
613 | struct acpi_power_resource_entry *entry; |
614 | int system_level = 5; |
615 | |
616 | list_for_each_entry(entry, list, node) { |
617 | struct acpi_power_resource *resource = entry->resource; |
618 | u8 state; |
619 | |
620 | mutex_lock(&resource->resource_lock); |
621 | |
622 | /* |
623 | * Make sure that the power resource state and its reference |
624 | * counter value are consistent with each other. |
625 | */ |
626 | if (!resource->ref_count && |
627 | !acpi_power_get_state(resource, state: &state) && |
628 | state == ACPI_POWER_RESOURCE_STATE_ON) |
629 | __acpi_power_off(resource); |
630 | |
631 | if (system_level > resource->system_level) |
632 | system_level = resource->system_level; |
633 | |
634 | mutex_unlock(lock: &resource->resource_lock); |
635 | } |
636 | *system_level_p = system_level; |
637 | return 0; |
638 | } |
639 | |
640 | /* -------------------------------------------------------------------------- |
641 | Device Power Management |
642 | -------------------------------------------------------------------------- */ |
643 | |
644 | /** |
645 | * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in |
646 | * ACPI 3.0) _PSW (Power State Wake) |
647 | * @dev: Device to handle. |
648 | * @enable: 0 - disable, 1 - enable the wake capabilities of the device. |
649 | * @sleep_state: Target sleep state of the system. |
650 | * @dev_state: Target power state of the device. |
651 | * |
652 | * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power |
653 | * State Wake) for the device, if present. On failure reset the device's |
654 | * wakeup.flags.valid flag. |
655 | * |
656 | * RETURN VALUE: |
657 | * 0 if either _DSW or _PSW has been successfully executed |
658 | * 0 if neither _DSW nor _PSW has been found |
659 | * -ENODEV if the execution of either _DSW or _PSW has failed |
660 | */ |
661 | int acpi_device_sleep_wake(struct acpi_device *dev, |
662 | int enable, int sleep_state, int dev_state) |
663 | { |
664 | union acpi_object in_arg[3]; |
665 | struct acpi_object_list arg_list = { 3, in_arg }; |
666 | acpi_status status = AE_OK; |
667 | |
668 | /* |
669 | * Try to execute _DSW first. |
670 | * |
671 | * Three arguments are needed for the _DSW object: |
672 | * Argument 0: enable/disable the wake capabilities |
673 | * Argument 1: target system state |
674 | * Argument 2: target device state |
675 | * When _DSW object is called to disable the wake capabilities, maybe |
676 | * the first argument is filled. The values of the other two arguments |
677 | * are meaningless. |
678 | */ |
679 | in_arg[0].type = ACPI_TYPE_INTEGER; |
680 | in_arg[0].integer.value = enable; |
681 | in_arg[1].type = ACPI_TYPE_INTEGER; |
682 | in_arg[1].integer.value = sleep_state; |
683 | in_arg[2].type = ACPI_TYPE_INTEGER; |
684 | in_arg[2].integer.value = dev_state; |
685 | status = acpi_evaluate_object(object: dev->handle, pathname: "_DSW" , parameter_objects: &arg_list, NULL); |
686 | if (ACPI_SUCCESS(status)) { |
687 | return 0; |
688 | } else if (status != AE_NOT_FOUND) { |
689 | acpi_handle_info(dev->handle, "_DSW execution failed\n" ); |
690 | dev->wakeup.flags.valid = 0; |
691 | return -ENODEV; |
692 | } |
693 | |
694 | /* Execute _PSW */ |
695 | status = acpi_execute_simple_method(handle: dev->handle, method: "_PSW" , arg: enable); |
696 | if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { |
697 | acpi_handle_info(dev->handle, "_PSW execution failed\n" ); |
698 | dev->wakeup.flags.valid = 0; |
699 | return -ENODEV; |
700 | } |
701 | |
702 | return 0; |
703 | } |
704 | |
705 | /* |
706 | * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): |
707 | * 1. Power on the power resources required for the wakeup device |
708 | * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power |
709 | * State Wake) for the device, if present |
710 | */ |
711 | int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state) |
712 | { |
713 | int err = 0; |
714 | |
715 | if (!dev || !dev->wakeup.flags.valid) |
716 | return -EINVAL; |
717 | |
718 | mutex_lock(&acpi_device_lock); |
719 | |
720 | dev_dbg(&dev->dev, "Enabling wakeup power (count %d)\n" , |
721 | dev->wakeup.prepare_count); |
722 | |
723 | if (dev->wakeup.prepare_count++) |
724 | goto out; |
725 | |
726 | err = acpi_power_on_list(list: &dev->wakeup.resources); |
727 | if (err) { |
728 | dev_err(&dev->dev, "Cannot turn on wakeup power resources\n" ); |
729 | dev->wakeup.flags.valid = 0; |
730 | goto out; |
731 | } |
732 | |
733 | /* |
734 | * Passing 3 as the third argument below means the device may be |
735 | * put into arbitrary power state afterward. |
736 | */ |
737 | err = acpi_device_sleep_wake(dev, enable: 1, sleep_state, dev_state: 3); |
738 | if (err) { |
739 | acpi_power_off_list(list: &dev->wakeup.resources); |
740 | dev->wakeup.prepare_count = 0; |
741 | goto out; |
742 | } |
743 | |
744 | dev_dbg(&dev->dev, "Wakeup power enabled\n" ); |
745 | |
746 | out: |
747 | mutex_unlock(lock: &acpi_device_lock); |
748 | return err; |
749 | } |
750 | |
751 | /* |
752 | * Shutdown a wakeup device, counterpart of above method |
753 | * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power |
754 | * State Wake) for the device, if present |
755 | * 2. Shutdown down the power resources |
756 | */ |
757 | int acpi_disable_wakeup_device_power(struct acpi_device *dev) |
758 | { |
759 | struct acpi_power_resource_entry *entry; |
760 | int err = 0; |
761 | |
762 | if (!dev || !dev->wakeup.flags.valid) |
763 | return -EINVAL; |
764 | |
765 | mutex_lock(&acpi_device_lock); |
766 | |
767 | dev_dbg(&dev->dev, "Disabling wakeup power (count %d)\n" , |
768 | dev->wakeup.prepare_count); |
769 | |
770 | /* Do nothing if wakeup power has not been enabled for this device. */ |
771 | if (dev->wakeup.prepare_count <= 0) |
772 | goto out; |
773 | |
774 | if (--dev->wakeup.prepare_count > 0) |
775 | goto out; |
776 | |
777 | err = acpi_device_sleep_wake(dev, enable: 0, sleep_state: 0, dev_state: 0); |
778 | if (err) |
779 | goto out; |
780 | |
781 | /* |
782 | * All of the power resources in the list need to be turned off even if |
783 | * there are errors. |
784 | */ |
785 | list_for_each_entry(entry, &dev->wakeup.resources, node) { |
786 | int ret; |
787 | |
788 | ret = acpi_power_off(resource: entry->resource); |
789 | if (ret && !err) |
790 | err = ret; |
791 | } |
792 | if (err) { |
793 | dev_err(&dev->dev, "Cannot turn off wakeup power resources\n" ); |
794 | dev->wakeup.flags.valid = 0; |
795 | goto out; |
796 | } |
797 | |
798 | dev_dbg(&dev->dev, "Wakeup power disabled\n" ); |
799 | |
800 | out: |
801 | mutex_unlock(lock: &acpi_device_lock); |
802 | return err; |
803 | } |
804 | |
805 | int acpi_power_get_inferred_state(struct acpi_device *device, int *state) |
806 | { |
807 | u8 list_state = ACPI_POWER_RESOURCE_STATE_OFF; |
808 | int result = 0; |
809 | int i = 0; |
810 | |
811 | if (!device || !state) |
812 | return -EINVAL; |
813 | |
814 | /* |
815 | * We know a device's inferred power state when all the resources |
816 | * required for a given D-state are 'on'. |
817 | */ |
818 | for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { |
819 | struct list_head *list = &device->power.states[i].resources; |
820 | |
821 | if (list_empty(head: list)) |
822 | continue; |
823 | |
824 | result = acpi_power_get_list_state(list, state: &list_state); |
825 | if (result) |
826 | return result; |
827 | |
828 | if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { |
829 | *state = i; |
830 | return 0; |
831 | } |
832 | } |
833 | |
834 | *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ? |
835 | ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT; |
836 | return 0; |
837 | } |
838 | |
839 | int acpi_power_on_resources(struct acpi_device *device, int state) |
840 | { |
841 | if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT) |
842 | return -EINVAL; |
843 | |
844 | return acpi_power_on_list(list: &device->power.states[state].resources); |
845 | } |
846 | |
847 | int acpi_power_transition(struct acpi_device *device, int state) |
848 | { |
849 | int result = 0; |
850 | |
851 | if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) |
852 | return -EINVAL; |
853 | |
854 | if (device->power.state == state || !device->flags.power_manageable) |
855 | return 0; |
856 | |
857 | if ((device->power.state < ACPI_STATE_D0) |
858 | || (device->power.state > ACPI_STATE_D3_COLD)) |
859 | return -ENODEV; |
860 | |
861 | /* |
862 | * First we reference all power resources required in the target list |
863 | * (e.g. so the device doesn't lose power while transitioning). Then, |
864 | * we dereference all power resources used in the current list. |
865 | */ |
866 | if (state < ACPI_STATE_D3_COLD) |
867 | result = acpi_power_on_list( |
868 | list: &device->power.states[state].resources); |
869 | |
870 | if (!result && device->power.state < ACPI_STATE_D3_COLD) |
871 | acpi_power_off_list( |
872 | list: &device->power.states[device->power.state].resources); |
873 | |
874 | /* We shouldn't change the state unless the above operations succeed. */ |
875 | device->power.state = result ? ACPI_STATE_UNKNOWN : state; |
876 | |
877 | return result; |
878 | } |
879 | |
880 | static void acpi_release_power_resource(struct device *dev) |
881 | { |
882 | struct acpi_device *device = to_acpi_device(dev); |
883 | struct acpi_power_resource *resource; |
884 | |
885 | resource = container_of(device, struct acpi_power_resource, device); |
886 | |
887 | mutex_lock(&power_resource_list_lock); |
888 | list_del(entry: &resource->list_node); |
889 | mutex_unlock(lock: &power_resource_list_lock); |
890 | |
891 | acpi_free_pnp_ids(pnp: &device->pnp); |
892 | kfree(objp: resource); |
893 | } |
894 | |
895 | static ssize_t resource_in_use_show(struct device *dev, |
896 | struct device_attribute *attr, |
897 | char *buf) |
898 | { |
899 | struct acpi_power_resource *resource; |
900 | |
901 | resource = to_power_resource(to_acpi_device(dev)); |
902 | return sprintf(buf, fmt: "%u\n" , !!resource->ref_count); |
903 | } |
904 | static DEVICE_ATTR_RO(resource_in_use); |
905 | |
906 | static void acpi_power_sysfs_remove(struct acpi_device *device) |
907 | { |
908 | device_remove_file(dev: &device->dev, attr: &dev_attr_resource_in_use); |
909 | } |
910 | |
911 | static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource) |
912 | { |
913 | mutex_lock(&power_resource_list_lock); |
914 | |
915 | if (!list_empty(head: &acpi_power_resource_list)) { |
916 | struct acpi_power_resource *r; |
917 | |
918 | list_for_each_entry(r, &acpi_power_resource_list, list_node) |
919 | if (r->order > resource->order) { |
920 | list_add_tail(new: &resource->list_node, head: &r->list_node); |
921 | goto out; |
922 | } |
923 | } |
924 | list_add_tail(new: &resource->list_node, head: &acpi_power_resource_list); |
925 | |
926 | out: |
927 | mutex_unlock(lock: &power_resource_list_lock); |
928 | } |
929 | |
930 | struct acpi_device *acpi_add_power_resource(acpi_handle handle) |
931 | { |
932 | struct acpi_device *device = acpi_fetch_acpi_dev(handle); |
933 | struct acpi_power_resource *resource; |
934 | union acpi_object acpi_object; |
935 | struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; |
936 | acpi_status status; |
937 | u8 state_dummy; |
938 | int result; |
939 | |
940 | if (device) |
941 | return device; |
942 | |
943 | resource = kzalloc(size: sizeof(*resource), GFP_KERNEL); |
944 | if (!resource) |
945 | return NULL; |
946 | |
947 | device = &resource->device; |
948 | acpi_init_device_object(device, handle, type: ACPI_BUS_TYPE_POWER, |
949 | release: acpi_release_power_resource); |
950 | mutex_init(&resource->resource_lock); |
951 | INIT_LIST_HEAD(list: &resource->list_node); |
952 | INIT_LIST_HEAD(list: &resource->dependents); |
953 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); |
954 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); |
955 | device->power.state = ACPI_STATE_UNKNOWN; |
956 | device->flags.match_driver = true; |
957 | |
958 | /* Evaluate the object to get the system level and resource order. */ |
959 | status = acpi_evaluate_object(object: handle, NULL, NULL, return_object_buffer: &buffer); |
960 | if (ACPI_FAILURE(status)) |
961 | goto err; |
962 | |
963 | resource->system_level = acpi_object.power_resource.system_level; |
964 | resource->order = acpi_object.power_resource.resource_order; |
965 | resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; |
966 | |
967 | /* Get the initial state or just flip it on if that fails. */ |
968 | if (acpi_power_get_state(resource, state: &state_dummy)) |
969 | __acpi_power_on(resource); |
970 | |
971 | acpi_handle_info(handle, "New power resource\n" ); |
972 | |
973 | result = acpi_tie_acpi_dev(adev: device); |
974 | if (result) |
975 | goto err; |
976 | |
977 | result = acpi_device_add(device); |
978 | if (result) |
979 | goto err; |
980 | |
981 | if (!device_create_file(device: &device->dev, entry: &dev_attr_resource_in_use)) |
982 | device->remove = acpi_power_sysfs_remove; |
983 | |
984 | acpi_power_add_resource_to_list(resource); |
985 | acpi_device_add_finalize(device); |
986 | return device; |
987 | |
988 | err: |
989 | acpi_release_power_resource(dev: &device->dev); |
990 | return NULL; |
991 | } |
992 | |
993 | #ifdef CONFIG_ACPI_SLEEP |
994 | void acpi_resume_power_resources(void) |
995 | { |
996 | struct acpi_power_resource *resource; |
997 | |
998 | mutex_lock(&power_resource_list_lock); |
999 | |
1000 | list_for_each_entry(resource, &acpi_power_resource_list, list_node) { |
1001 | int result; |
1002 | u8 state; |
1003 | |
1004 | mutex_lock(&resource->resource_lock); |
1005 | |
1006 | resource->state = ACPI_POWER_RESOURCE_STATE_UNKNOWN; |
1007 | result = acpi_power_get_state(resource, state: &state); |
1008 | if (result) { |
1009 | mutex_unlock(lock: &resource->resource_lock); |
1010 | continue; |
1011 | } |
1012 | |
1013 | if (state == ACPI_POWER_RESOURCE_STATE_OFF |
1014 | && resource->ref_count) { |
1015 | acpi_handle_debug(resource->device.handle, "Turning ON\n" ); |
1016 | __acpi_power_on(resource); |
1017 | } |
1018 | |
1019 | mutex_unlock(lock: &resource->resource_lock); |
1020 | } |
1021 | |
1022 | mutex_unlock(lock: &power_resource_list_lock); |
1023 | } |
1024 | #endif |
1025 | |
1026 | static const struct dmi_system_id dmi_leave_unused_power_resources_on[] = { |
1027 | { |
1028 | /* |
1029 | * The Toshiba Click Mini has a CPR3 power-resource which must |
1030 | * be on for the touchscreen to work, but which is not in any |
1031 | * _PR? lists. The other 2 affected power-resources are no-ops. |
1032 | */ |
1033 | .matches = { |
1034 | DMI_MATCH(DMI_SYS_VENDOR, "TOSHIBA" ), |
1035 | DMI_MATCH(DMI_PRODUCT_NAME, "SATELLITE Click Mini L9W-B" ), |
1036 | }, |
1037 | }, |
1038 | {} |
1039 | }; |
1040 | |
1041 | /** |
1042 | * acpi_turn_off_unused_power_resources - Turn off power resources not in use. |
1043 | */ |
1044 | void acpi_turn_off_unused_power_resources(void) |
1045 | { |
1046 | struct acpi_power_resource *resource; |
1047 | |
1048 | if (dmi_check_system(list: dmi_leave_unused_power_resources_on)) |
1049 | return; |
1050 | |
1051 | mutex_lock(&power_resource_list_lock); |
1052 | |
1053 | list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) { |
1054 | mutex_lock(&resource->resource_lock); |
1055 | |
1056 | if (!resource->ref_count && |
1057 | resource->state == ACPI_POWER_RESOURCE_STATE_ON) { |
1058 | acpi_handle_debug(resource->device.handle, "Turning OFF\n" ); |
1059 | __acpi_power_off(resource); |
1060 | } |
1061 | |
1062 | mutex_unlock(lock: &resource->resource_lock); |
1063 | } |
1064 | |
1065 | mutex_unlock(lock: &power_resource_list_lock); |
1066 | } |
1067 | |