blob: 9b608b55d2b29545ccd32965bbdc5636af49252b [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Linus Torvalds1da177e2005-04-16 15:20:36 -07002/*
Rafael J. Wysockiaa57aca2015-07-16 02:01:28 +02003 * drivers/acpi/power.c - ACPI Power Resources management.
Linus Torvalds1da177e2005-04-16 15:20:36 -07004 *
Rafael J. Wysockiaa57aca2015-07-16 02:01:28 +02005 * 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>
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
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".
Rafael J. Wysockiaa57aca2015-07-16 02:01:28 +020015 * The code below deals with ACPI Power Resources control.
Maximilian Luzc6237b22020-11-05 03:06:00 +010016 *
Rafael J. Wysockiaa57aca2015-07-16 02:01:28 +020017 * An ACPI "power resource object" represents a software controllable power
18 * plane, clock plane, or other resource depended on by a device.
19 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070020 * A device may rely on multiple power resources, and a power resource
21 * may be shared by multiple devices.
22 */
23
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +010024#define pr_fmt(fmt) "ACPI: PM: " fmt
25
Linus Torvalds1da177e2005-04-16 15:20:36 -070026#include <linux/kernel.h>
27#include <linux/module.h>
28#include <linux/init.h>
29#include <linux/types.h>
Tejun Heo5a0e3ad2010-03-24 17:04:11 +090030#include <linux/slab.h>
Lin Ming0090def2012-03-29 14:09:39 +080031#include <linux/pm_runtime.h>
Rafael J. Wysocki18a38702013-01-25 21:51:32 +010032#include <linux/sysfs.h>
Lv Zheng8b484632013-12-03 08:49:16 +080033#include <linux/acpi.h>
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +020034#include "sleep.h"
Lin Ming0090def2012-03-29 14:09:39 +080035#include "internal.h"
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +020036
Linus Torvalds1da177e2005-04-16 15:20:36 -070037#define ACPI_POWER_CLASS "power_resource"
Linus Torvalds1da177e2005-04-16 15:20:36 -070038#define ACPI_POWER_DEVICE_NAME "Power Resource"
Linus Torvalds1da177e2005-04-16 15:20:36 -070039#define ACPI_POWER_RESOURCE_STATE_OFF 0x00
40#define ACPI_POWER_RESOURCE_STATE_ON 0x01
41#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
Zhao Yakuif5adfaa2008-08-11 14:57:50 +080042
Mika Westerberg45337712019-06-25 13:29:41 +030043struct acpi_power_dependent_device {
44 struct device *dev;
45 struct list_head node;
46};
47
Len Brown4be44fc2005-08-05 00:44:28 -040048struct acpi_power_resource {
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +010049 struct acpi_device device;
Rafael J. Wysocki781d7372013-01-17 14:11:06 +010050 struct list_head list_node;
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +010051 char *name;
Len Brown4be44fc2005-08-05 00:44:28 -040052 u32 system_level;
53 u32 order;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +020054 unsigned int ref_count;
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +010055 bool wakeup_enabled;
Konstantin Karasyov0a613902007-02-16 01:47:06 -050056 struct mutex resource_lock;
Mika Westerberg45337712019-06-25 13:29:41 +030057 struct list_head dependents;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058};
59
Rafael J. Wysocki0b224522013-01-17 14:11:06 +010060struct acpi_power_resource_entry {
61 struct list_head node;
62 struct acpi_power_resource *resource;
63};
64
Rafael J. Wysocki781d7372013-01-17 14:11:06 +010065static LIST_HEAD(acpi_power_resource_list);
66static DEFINE_MUTEX(power_resource_list_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -070067
Linus Torvalds1da177e2005-04-16 15:20:36 -070068/* --------------------------------------------------------------------------
69 Power Resource Management
70 -------------------------------------------------------------------------- */
71
Rafael J. Wysockib1c0f992013-01-24 12:50:09 +010072static inline
73struct acpi_power_resource *to_power_resource(struct acpi_device *device)
74{
75 return container_of(device, struct acpi_power_resource, device);
76}
77
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +010078static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle)
Linus Torvalds1da177e2005-04-16 15:20:36 -070079{
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +010080 struct acpi_device *device;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +010082 if (acpi_bus_get_device(handle, &device))
83 return NULL;
Linus Torvalds1da177e2005-04-16 15:20:36 -070084
Rafael J. Wysockib1c0f992013-01-24 12:50:09 +010085 return to_power_resource(device);
Linus Torvalds1da177e2005-04-16 15:20:36 -070086}
87
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +010088static int acpi_power_resources_list_add(acpi_handle handle,
89 struct list_head *list)
Rafael J. Wysocki0b224522013-01-17 14:11:06 +010090{
91 struct acpi_power_resource *resource = acpi_power_get_context(handle);
92 struct acpi_power_resource_entry *entry;
93
94 if (!resource || !list)
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +010095 return -EINVAL;
Rafael J. Wysocki0b224522013-01-17 14:11:06 +010096
97 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
98 if (!entry)
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +010099 return -ENOMEM;
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100100
101 entry->resource = resource;
102 if (!list_empty(list)) {
103 struct acpi_power_resource_entry *e;
104
105 list_for_each_entry(e, list, node)
106 if (e->resource->order > resource->order) {
107 list_add_tail(&entry->node, &e->node);
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100108 return 0;
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100109 }
110 }
111 list_add_tail(&entry->node, list);
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100112 return 0;
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100113}
114
115void acpi_power_resources_list_free(struct list_head *list)
116{
117 struct acpi_power_resource_entry *entry, *e;
118
119 list_for_each_entry_safe(entry, e, list, node) {
120 list_del(&entry->node);
121 kfree(entry);
122 }
123}
124
Hans de Goede7d7b4672018-12-30 18:25:00 +0100125static bool acpi_power_resource_is_dup(union acpi_object *package,
126 unsigned int start, unsigned int i)
127{
128 acpi_handle rhandle, dup;
129 unsigned int j;
130
131 /* The caller is expected to check the package element types */
132 rhandle = package->package.elements[i].reference.handle;
133 for (j = start; j < i; j++) {
134 dup = package->package.elements[j].reference.handle;
135 if (dup == rhandle)
136 return true;
137 }
138
139 return false;
140}
141
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100142int acpi_extract_power_resources(union acpi_object *package, unsigned int start,
143 struct list_head *list)
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100144{
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100145 unsigned int i;
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100146 int err = 0;
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100147
148 for (i = start; i < package->package.count; i++) {
149 union acpi_object *element = &package->package.elements[i];
150 acpi_handle rhandle;
151
152 if (element->type != ACPI_TYPE_LOCAL_REFERENCE) {
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100153 err = -ENODATA;
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100154 break;
155 }
156 rhandle = element->reference.handle;
157 if (!rhandle) {
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100158 err = -ENODEV;
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100159 break;
160 }
Hans de Goede7d7b4672018-12-30 18:25:00 +0100161
162 /* Some ACPI tables contain duplicate power resource references */
163 if (acpi_power_resource_is_dup(package, start, i))
164 continue;
165
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100166 err = acpi_add_power_resource(rhandle);
167 if (err)
168 break;
169
170 err = acpi_power_resources_list_add(rhandle, list);
171 if (err)
172 break;
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100173 }
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100174 if (err)
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100175 acpi_power_resources_list_free(list);
176
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100177 return err;
Rafael J. Wysockief85bdb2013-01-17 14:11:07 +0100178}
179
Zhao Yakuia51e1452008-08-11 14:55:05 +0800180static int acpi_power_get_state(acpi_handle handle, int *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700181{
Len Brown4be44fc2005-08-05 00:44:28 -0400182 acpi_status status = AE_OK;
Matthew Wilcox27663c52008-10-10 02:22:59 -0400183 unsigned long long sta = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184
Zhao Yakuia51e1452008-08-11 14:55:05 +0800185 if (!handle || !state)
Patrick Mocheld550d982006-06-27 00:41:40 -0400186 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700187
Zhao Yakuia51e1452008-08-11 14:55:05 +0800188 status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700189 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400190 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191
Alexey Starikovskiyc35923b2007-10-22 14:19:09 +0400192 *state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
193 ACPI_POWER_RESOURCE_STATE_OFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700194
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100195 acpi_handle_debug(handle, "Power resource is %s\n",
196 *state ? "on" : "off");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197
Patrick Mocheld550d982006-06-27 00:41:40 -0400198 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199}
200
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100201static int acpi_power_get_list_state(struct list_head *list, int *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202{
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100203 struct acpi_power_resource_entry *entry;
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100204 int cur_state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700205
206 if (!list || !state)
Patrick Mocheld550d982006-06-27 00:41:40 -0400207 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700208
209 /* The state of the list is 'on' IFF all resources are 'on'. */
Arnd Bergmannfe8c4702017-04-19 19:47:04 +0200210 cur_state = 0;
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100211 list_for_each_entry(entry, list, node) {
212 struct acpi_power_resource *resource = entry->resource;
213 acpi_handle handle = resource->device.handle;
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100214 int result;
215
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100216 mutex_lock(&resource->resource_lock);
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100217 result = acpi_power_get_state(handle, &cur_state);
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100218 mutex_unlock(&resource->resource_lock);
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100219 if (result)
220 return result;
221
222 if (cur_state != ACPI_POWER_RESOURCE_STATE_ON)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700223 break;
224 }
225
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100226 pr_debug("Power resource list is %s\n", cur_state ? "on" : "off");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100228 *state = cur_state;
Rafael J. Wysockid0515d92011-01-06 23:38:57 +0100229 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700230}
231
Mika Westerberg45337712019-06-25 13:29:41 +0300232static int
233acpi_power_resource_add_dependent(struct acpi_power_resource *resource,
234 struct device *dev)
235{
236 struct acpi_power_dependent_device *dep;
237 int ret = 0;
238
239 mutex_lock(&resource->resource_lock);
240 list_for_each_entry(dep, &resource->dependents, node) {
241 /* Only add it once */
242 if (dep->dev == dev)
243 goto unlock;
244 }
245
246 dep = kzalloc(sizeof(*dep), GFP_KERNEL);
247 if (!dep) {
248 ret = -ENOMEM;
249 goto unlock;
250 }
251
252 dep->dev = dev;
253 list_add_tail(&dep->node, &resource->dependents);
254 dev_dbg(dev, "added power dependency to [%s]\n", resource->name);
255
256unlock:
257 mutex_unlock(&resource->resource_lock);
258 return ret;
259}
260
261static void
262acpi_power_resource_remove_dependent(struct acpi_power_resource *resource,
263 struct device *dev)
264{
265 struct acpi_power_dependent_device *dep;
266
267 mutex_lock(&resource->resource_lock);
268 list_for_each_entry(dep, &resource->dependents, node) {
269 if (dep->dev == dev) {
270 list_del(&dep->node);
271 kfree(dep);
272 dev_dbg(dev, "removed power dependency to [%s]\n",
273 resource->name);
274 break;
275 }
276 }
277 mutex_unlock(&resource->resource_lock);
278}
279
280/**
281 * acpi_device_power_add_dependent - Add dependent device of this ACPI device
282 * @adev: ACPI device pointer
283 * @dev: Dependent device
284 *
285 * If @adev has non-empty _PR0 the @dev is added as dependent device to all
286 * power resources returned by it. This means that whenever these power
287 * resources are turned _ON the dependent devices get runtime resumed. This
288 * is needed for devices such as PCI to allow its driver to re-initialize
289 * it after it went to D0uninitialized.
290 *
291 * If @adev does not have _PR0 this does nothing.
292 *
293 * Returns %0 in case of success and negative errno otherwise.
294 */
295int acpi_device_power_add_dependent(struct acpi_device *adev,
296 struct device *dev)
297{
298 struct acpi_power_resource_entry *entry;
299 struct list_head *resources;
300 int ret;
301
302 if (!adev->flags.power_manageable)
303 return 0;
304
305 resources = &adev->power.states[ACPI_STATE_D0].resources;
306 list_for_each_entry(entry, resources, node) {
307 ret = acpi_power_resource_add_dependent(entry->resource, dev);
308 if (ret)
309 goto err;
310 }
311
312 return 0;
313
314err:
315 list_for_each_entry(entry, resources, node)
316 acpi_power_resource_remove_dependent(entry->resource, dev);
317
318 return ret;
319}
320
321/**
322 * acpi_device_power_remove_dependent - Remove dependent device
323 * @adev: ACPI device pointer
324 * @dev: Dependent device
325 *
326 * Does the opposite of acpi_device_power_add_dependent() and removes the
327 * dependent device if it is found. Can be called to @adev that does not
328 * have _PR0 as well.
329 */
330void acpi_device_power_remove_dependent(struct acpi_device *adev,
331 struct device *dev)
332{
333 struct acpi_power_resource_entry *entry;
334 struct list_head *resources;
335
336 if (!adev->flags.power_manageable)
337 return;
338
339 resources = &adev->power.states[ACPI_STATE_D0].resources;
340 list_for_each_entry_reverse(entry, resources, node)
341 acpi_power_resource_remove_dependent(entry->resource, dev);
342}
343
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200344static int __acpi_power_on(struct acpi_power_resource *resource)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700345{
Mika Westerberg45337712019-06-25 13:29:41 +0300346 struct acpi_power_dependent_device *dep;
Len Brown4be44fc2005-08-05 00:44:28 -0400347 acpi_status status = AE_OK;
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500348
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100349 status = acpi_evaluate_object(resource->device.handle, "_ON", NULL, NULL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700350 if (ACPI_FAILURE(status))
Patrick Mocheld550d982006-06-27 00:41:40 -0400351 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700352
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100353 pr_debug("Power resource [%s] turned on\n", resource->name);
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200354
Mika Westerberg45337712019-06-25 13:29:41 +0300355 /*
356 * If there are other dependents on this power resource we need to
357 * resume them now so that their drivers can re-initialize the
358 * hardware properly after it went back to D0.
359 */
360 if (list_empty(&resource->dependents) ||
361 list_is_singular(&resource->dependents))
362 return 0;
363
364 list_for_each_entry(dep, &resource->dependents, node) {
365 dev_dbg(dep->dev, "runtime resuming because [%s] turned on\n",
366 resource->name);
367 pm_request_resume(dep->dev);
368 }
369
Patrick Mocheld550d982006-06-27 00:41:40 -0400370 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700371}
372
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100373static int acpi_power_on_unlocked(struct acpi_power_resource *resource)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700374{
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100375 int result = 0;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200376
377 if (resource->ref_count++) {
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100378 pr_debug("Power resource [%s] already on\n", resource->name);
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200379 } else {
380 result = __acpi_power_on(resource);
Rafael J. Wysocki41863fc2013-10-16 23:05:42 +0200381 if (result)
Rafael J. Wysocki12b3b5a2010-11-25 00:03:32 +0100382 resource->ref_count--;
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500383 }
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100384 return result;
385}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700386
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100387static int acpi_power_on(struct acpi_power_resource *resource)
388{
389 int result;
390
391 mutex_lock(&resource->resource_lock);
392 result = acpi_power_on_unlocked(resource);
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500393 mutex_unlock(&resource->resource_lock);
Rafael J. Wysocki12b3b5a2010-11-25 00:03:32 +0100394 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395}
396
Rafael J. Wysocki660b1112013-01-25 21:51:57 +0100397static int __acpi_power_off(struct acpi_power_resource *resource)
398{
399 acpi_status status;
400
401 status = acpi_evaluate_object(resource->device.handle, "_OFF",
402 NULL, NULL);
403 if (ACPI_FAILURE(status))
404 return -ENODEV;
405
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100406 pr_debug("Power resource [%s] turned off\n", resource->name);
407
Rafael J. Wysocki660b1112013-01-25 21:51:57 +0100408 return 0;
409}
410
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100411static int acpi_power_off_unlocked(struct acpi_power_resource *resource)
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200412{
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100413 int result = 0;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200414
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200415 if (!resource->ref_count) {
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100416 pr_debug("Power resource [%s] already off\n", resource->name);
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100417 return 0;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200418 }
419
420 if (--resource->ref_count) {
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100421 pr_debug("Power resource [%s] still in use\n", resource->name);
Rafael J. Wysocki660b1112013-01-25 21:51:57 +0100422 } else {
423 result = __acpi_power_off(resource);
424 if (result)
425 resource->ref_count++;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200426 }
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100427 return result;
428}
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200429
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100430static int acpi_power_off(struct acpi_power_resource *resource)
431{
432 int result;
433
434 mutex_lock(&resource->resource_lock);
435 result = acpi_power_off_unlocked(resource);
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200436 mutex_unlock(&resource->resource_lock);
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200437 return result;
438}
439
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100440static int acpi_power_off_list(struct list_head *list)
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100441{
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100442 struct acpi_power_resource_entry *entry;
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100443 int result = 0;
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100444
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100445 list_for_each_entry_reverse(entry, list, node) {
446 result = acpi_power_off(entry->resource);
447 if (result)
448 goto err;
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100449 }
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100450 return 0;
451
452 err:
453 list_for_each_entry_continue(entry, list, node)
454 acpi_power_on(entry->resource);
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100455
456 return result;
457}
458
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100459static int acpi_power_on_list(struct list_head *list)
460{
461 struct acpi_power_resource_entry *entry;
462 int result = 0;
463
464 list_for_each_entry(entry, list, node) {
465 result = acpi_power_on(entry->resource);
466 if (result)
467 goto err;
468 }
469 return 0;
470
471 err:
472 list_for_each_entry_continue_reverse(entry, list, node)
473 acpi_power_off(entry->resource);
474
475 return result;
476}
477
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100478static struct attribute *attrs[] = {
479 NULL,
480};
481
Arvind Yadav26408b22017-06-30 17:39:05 +0530482static const struct attribute_group attr_groups[] = {
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100483 [ACPI_STATE_D0] = {
484 .name = "power_resources_D0",
485 .attrs = attrs,
486 },
487 [ACPI_STATE_D1] = {
488 .name = "power_resources_D1",
489 .attrs = attrs,
490 },
491 [ACPI_STATE_D2] = {
492 .name = "power_resources_D2",
493 .attrs = attrs,
494 },
495 [ACPI_STATE_D3_HOT] = {
496 .name = "power_resources_D3hot",
497 .attrs = attrs,
498 },
499};
500
Arvind Yadav26408b22017-06-30 17:39:05 +0530501static const struct attribute_group wakeup_attr_group = {
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200502 .name = "power_resources_wakeup",
503 .attrs = attrs,
504};
505
506static void acpi_power_hide_list(struct acpi_device *adev,
507 struct list_head *resources,
Arvind Yadav26408b22017-06-30 17:39:05 +0530508 const struct attribute_group *attr_group)
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100509{
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100510 struct acpi_power_resource_entry *entry;
511
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200512 if (list_empty(resources))
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100513 return;
514
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200515 list_for_each_entry_reverse(entry, resources, node) {
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100516 struct acpi_device *res_dev = &entry->resource->device;
517
518 sysfs_remove_link_from_group(&adev->dev.kobj,
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200519 attr_group->name,
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100520 dev_name(&res_dev->dev));
521 }
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200522 sysfs_remove_group(&adev->dev.kobj, attr_group);
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100523}
524
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200525static void acpi_power_expose_list(struct acpi_device *adev,
526 struct list_head *resources,
Arvind Yadav26408b22017-06-30 17:39:05 +0530527 const struct attribute_group *attr_group)
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100528{
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100529 struct acpi_power_resource_entry *entry;
530 int ret;
531
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200532 if (list_empty(resources))
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100533 return;
534
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200535 ret = sysfs_create_group(&adev->dev.kobj, attr_group);
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100536 if (ret)
537 return;
538
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200539 list_for_each_entry(entry, resources, node) {
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100540 struct acpi_device *res_dev = &entry->resource->device;
541
542 ret = sysfs_add_link_to_group(&adev->dev.kobj,
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200543 attr_group->name,
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100544 &res_dev->dev.kobj,
545 dev_name(&res_dev->dev));
546 if (ret) {
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200547 acpi_power_hide_list(adev, resources, attr_group);
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100548 break;
549 }
550 }
551}
552
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200553static void acpi_power_expose_hide(struct acpi_device *adev,
554 struct list_head *resources,
Arvind Yadav26408b22017-06-30 17:39:05 +0530555 const struct attribute_group *attr_group,
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200556 bool expose)
557{
558 if (expose)
559 acpi_power_expose_list(adev, resources, attr_group);
560 else
561 acpi_power_hide_list(adev, resources, attr_group);
562}
563
Rafael J. Wysockibc9b6402013-01-17 14:11:05 +0100564void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
565{
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100566 int state;
Rafael J. Wysockibc9b6402013-01-17 14:11:05 +0100567
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200568 if (adev->wakeup.flags.valid)
569 acpi_power_expose_hide(adev, &adev->wakeup.resources,
570 &wakeup_attr_group, add);
571
Rafael J. Wysocki18a38702013-01-25 21:51:32 +0100572 if (!adev->power.flags.power_resources)
573 return;
Rafael J. Wysockibc9b6402013-01-17 14:11:05 +0100574
Rafael J. Wysocki41a2a462013-04-11 22:41:48 +0200575 for (state = ACPI_STATE_D0; state <= ACPI_STATE_D3_HOT; state++)
576 acpi_power_expose_hide(adev,
577 &adev->power.states[state].resources,
578 &attr_groups[state], add);
Lin Ming0090def2012-03-29 14:09:39 +0800579}
Rafael J. Wysockibc9b6402013-01-17 14:11:05 +0100580
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100581int acpi_power_wakeup_list_init(struct list_head *list, int *system_level_p)
Rafael J. Wysocki0596a522013-01-17 14:11:07 +0100582{
583 struct acpi_power_resource_entry *entry;
584 int system_level = 5;
585
586 list_for_each_entry(entry, list, node) {
587 struct acpi_power_resource *resource = entry->resource;
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100588 acpi_handle handle = resource->device.handle;
589 int result;
590 int state;
Rafael J. Wysocki0596a522013-01-17 14:11:07 +0100591
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100592 mutex_lock(&resource->resource_lock);
593
594 result = acpi_power_get_state(handle, &state);
595 if (result) {
596 mutex_unlock(&resource->resource_lock);
597 return result;
598 }
599 if (state == ACPI_POWER_RESOURCE_STATE_ON) {
600 resource->ref_count++;
601 resource->wakeup_enabled = true;
602 }
Rafael J. Wysocki0596a522013-01-17 14:11:07 +0100603 if (system_level > resource->system_level)
604 system_level = resource->system_level;
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100605
606 mutex_unlock(&resource->resource_lock);
Rafael J. Wysocki0596a522013-01-17 14:11:07 +0100607 }
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100608 *system_level_p = system_level;
609 return 0;
Rafael J. Wysocki0596a522013-01-17 14:11:07 +0100610}
611
Rafael J. Wysockibc9b6402013-01-17 14:11:05 +0100612/* --------------------------------------------------------------------------
613 Device Power Management
614 -------------------------------------------------------------------------- */
Lin Ming0090def2012-03-29 14:09:39 +0800615
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200616/**
617 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
618 * ACPI 3.0) _PSW (Power State Wake)
619 * @dev: Device to handle.
620 * @enable: 0 - disable, 1 - enable the wake capabilities of the device.
621 * @sleep_state: Target sleep state of the system.
622 * @dev_state: Target power state of the device.
623 *
624 * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
625 * State Wake) for the device, if present. On failure reset the device's
626 * wakeup.flags.valid flag.
627 *
628 * RETURN VALUE:
629 * 0 if either _DSW or _PSW has been successfully executed
630 * 0 if neither _DSW nor _PSW has been found
631 * -ENODEV if the execution of either _DSW or _PSW has failed
632 */
633int acpi_device_sleep_wake(struct acpi_device *dev,
Maximilian Luzc6237b22020-11-05 03:06:00 +0100634 int enable, int sleep_state, int dev_state)
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200635{
636 union acpi_object in_arg[3];
637 struct acpi_object_list arg_list = { 3, in_arg };
638 acpi_status status = AE_OK;
639
640 /*
641 * Try to execute _DSW first.
642 *
Bjorn Helgaas603fadf2019-03-25 13:34:00 -0500643 * Three arguments are needed for the _DSW object:
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200644 * Argument 0: enable/disable the wake capabilities
645 * Argument 1: target system state
646 * Argument 2: target device state
647 * When _DSW object is called to disable the wake capabilities, maybe
Bjorn Helgaas603fadf2019-03-25 13:34:00 -0500648 * the first argument is filled. The values of the other two arguments
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200649 * are meaningless.
650 */
651 in_arg[0].type = ACPI_TYPE_INTEGER;
652 in_arg[0].integer.value = enable;
653 in_arg[1].type = ACPI_TYPE_INTEGER;
654 in_arg[1].integer.value = sleep_state;
655 in_arg[2].type = ACPI_TYPE_INTEGER;
656 in_arg[2].integer.value = dev_state;
657 status = acpi_evaluate_object(dev->handle, "_DSW", &arg_list, NULL);
658 if (ACPI_SUCCESS(status)) {
659 return 0;
660 } else if (status != AE_NOT_FOUND) {
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100661 acpi_handle_info(dev->handle, "_DSW execution failed\n");
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200662 dev->wakeup.flags.valid = 0;
663 return -ENODEV;
664 }
665
666 /* Execute _PSW */
Jiang Liu0db98202013-06-29 00:24:39 +0800667 status = acpi_execute_simple_method(dev->handle, "_PSW", enable);
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200668 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100669 acpi_handle_info(dev->handle, "_PSW execution failed\n");
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200670 dev->wakeup.flags.valid = 0;
671 return -ENODEV;
672 }
673
674 return 0;
675}
676
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677/*
678 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
Maximilian Luzc6237b22020-11-05 03:06:00 +0100679 * 1. Power on the power resources required for the wakeup device
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200680 * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
681 * State Wake) for the device, if present
Linus Torvalds1da177e2005-04-16 15:20:36 -0700682 */
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200683int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684{
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100685 struct acpi_power_resource_entry *entry;
Rafael J. Wysocki993cbe52013-01-17 14:11:06 +0100686 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700687
Linus Torvalds1da177e2005-04-16 15:20:36 -0700688 if (!dev || !dev->wakeup.flags.valid)
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200689 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700690
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200691 mutex_lock(&acpi_device_lock);
692
693 if (dev->wakeup.prepare_count++)
694 goto out;
Rafael J. Wysocki0af4b8c2008-07-07 03:34:11 +0200695
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100696 list_for_each_entry(entry, &dev->wakeup.resources, node) {
697 struct acpi_power_resource *resource = entry->resource;
698
699 mutex_lock(&resource->resource_lock);
700
701 if (!resource->wakeup_enabled) {
702 err = acpi_power_on_unlocked(resource);
703 if (!err)
704 resource->wakeup_enabled = true;
705 }
706
707 mutex_unlock(&resource->resource_lock);
708
709 if (err) {
710 dev_err(&dev->dev,
711 "Cannot turn wakeup power resources on\n");
712 dev->wakeup.flags.valid = 0;
713 goto out;
714 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 }
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100716 /*
717 * Passing 3 as the third argument below means the device may be
718 * put into arbitrary power state afterward.
719 */
720 err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200721 if (err)
722 dev->wakeup.prepare_count = 0;
723
724 out:
725 mutex_unlock(&acpi_device_lock);
Rafael J. Wysocki0af4b8c2008-07-07 03:34:11 +0200726 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727}
728
729/*
730 * Shutdown a wakeup device, counterpart of above method
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200731 * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
732 * State Wake) for the device, if present
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733 * 2. Shutdown down the power resources
734 */
Len Brown4be44fc2005-08-05 00:44:28 -0400735int acpi_disable_wakeup_device_power(struct acpi_device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736{
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100737 struct acpi_power_resource_entry *entry;
Rafael J. Wysocki993cbe52013-01-17 14:11:06 +0100738 int err = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700739
740 if (!dev || !dev->wakeup.flags.valid)
Rafael J. Wysocki77e76602008-07-07 03:33:34 +0200741 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200743 mutex_lock(&acpi_device_lock);
744
745 if (--dev->wakeup.prepare_count > 0)
746 goto out;
747
Rafael J. Wysocki0af4b8c2008-07-07 03:34:11 +0200748 /*
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200749 * Executing the code below even if prepare_count is already zero when
750 * the function is called may be useful, for example for initialisation.
Rafael J. Wysocki0af4b8c2008-07-07 03:34:11 +0200751 */
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200752 if (dev->wakeup.prepare_count < 0)
753 dev->wakeup.prepare_count = 0;
Rafael J. Wysocki0af4b8c2008-07-07 03:34:11 +0200754
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200755 err = acpi_device_sleep_wake(dev, 0, 0, 0);
756 if (err)
757 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758
Rafael J. Wysockib5d667e2013-02-23 23:15:21 +0100759 list_for_each_entry(entry, &dev->wakeup.resources, node) {
760 struct acpi_power_resource *resource = entry->resource;
761
762 mutex_lock(&resource->resource_lock);
763
764 if (resource->wakeup_enabled) {
765 err = acpi_power_off_unlocked(resource);
766 if (!err)
767 resource->wakeup_enabled = false;
768 }
769
770 mutex_unlock(&resource->resource_lock);
771
772 if (err) {
773 dev_err(&dev->dev,
774 "Cannot turn wakeup power resources off\n");
775 dev->wakeup.flags.valid = 0;
776 break;
777 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700778 }
779
Rafael J. Wysocki9b83ccd2009-09-08 23:15:31 +0200780 out:
781 mutex_unlock(&acpi_device_lock);
782 return err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700783}
784
Rafael J. Wysocki32a00d22010-11-25 00:05:17 +0100785int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700786{
Len Brown4be44fc2005-08-05 00:44:28 -0400787 int result = 0;
Len Brown4be44fc2005-08-05 00:44:28 -0400788 int list_state = 0;
789 int i = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
Rafael J. Wysocki32a00d22010-11-25 00:05:17 +0100791 if (!device || !state)
Patrick Mocheld550d982006-06-27 00:41:40 -0400792 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 /*
795 * We know a device's inferred power state when all the resources
796 * required for a given D-state are 'on'.
797 */
Rafael J. Wysocki38c92ff2012-05-20 13:58:00 +0200798 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100799 struct list_head *list = &device->power.states[i].resources;
800
801 if (list_empty(list))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700802 continue;
803
804 result = acpi_power_get_list_state(list, &list_state);
805 if (result)
Patrick Mocheld550d982006-06-27 00:41:40 -0400806 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807
808 if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
Rafael J. Wysocki32a00d22010-11-25 00:05:17 +0100809 *state = i;
Patrick Mocheld550d982006-06-27 00:41:40 -0400810 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 }
812 }
813
Rafael J. Wysocki20dacb72015-05-16 01:55:35 +0200814 *state = device->power.states[ACPI_STATE_D3_COLD].flags.valid ?
815 ACPI_STATE_D3_COLD : ACPI_STATE_D3_HOT;
Patrick Mocheld550d982006-06-27 00:41:40 -0400816 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
Rafael J. Wysocki30d3df412010-11-25 00:06:55 +0100819int acpi_power_on_resources(struct acpi_device *device, int state)
820{
Rafael J. Wysocki87e753b2013-01-22 12:56:16 +0100821 if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_HOT)
Rafael J. Wysocki30d3df412010-11-25 00:06:55 +0100822 return -EINVAL;
823
824 return acpi_power_on_list(&device->power.states[state].resources);
825}
826
Len Brown4be44fc2005-08-05 00:44:28 -0400827int acpi_power_transition(struct acpi_device *device, int state)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700828{
Rafael J. Wysocki5c7dd712012-05-18 00:39:35 +0200829 int result = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700830
Zhang Rui3ebc81b2012-03-29 14:09:38 +0800831 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
Patrick Mocheld550d982006-06-27 00:41:40 -0400832 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700833
Rafael J. Wysocki0b224522013-01-17 14:11:06 +0100834 if (device->power.state == state || !device->flags.power_manageable)
Rafael J. Wysocki212967c2010-11-25 00:02:36 +0100835 return 0;
836
Len Brown4be44fc2005-08-05 00:44:28 -0400837 if ((device->power.state < ACPI_STATE_D0)
Zhang Rui3ebc81b2012-03-29 14:09:38 +0800838 || (device->power.state > ACPI_STATE_D3_COLD))
Patrick Mocheld550d982006-06-27 00:41:40 -0400839 return -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700840
Linus Torvalds1da177e2005-04-16 15:20:36 -0700841 /*
842 * First we reference all power resources required in the target list
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100843 * (e.g. so the device doesn't lose power while transitioning). Then,
844 * we dereference all power resources used in the current list.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700845 */
Rafael J. Wysocki5c7dd712012-05-18 00:39:35 +0200846 if (state < ACPI_STATE_D3_COLD)
847 result = acpi_power_on_list(
848 &device->power.states[state].resources);
849
850 if (!result && device->power.state < ACPI_STATE_D3_COLD)
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100851 acpi_power_off_list(
852 &device->power.states[device->power.state].resources);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700853
Rafael J. Wysockid2ef5552010-11-25 00:06:09 +0100854 /* We shouldn't change the state unless the above operations succeed. */
855 device->power.state = result ? ACPI_STATE_UNKNOWN : state;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856
Patrick Mocheld550d982006-06-27 00:41:40 -0400857 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700858}
859
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100860static void acpi_release_power_resource(struct device *dev)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861{
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100862 struct acpi_device *device = to_acpi_device(dev);
863 struct acpi_power_resource *resource;
864
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100865 resource = container_of(device, struct acpi_power_resource, device);
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100866
867 mutex_lock(&power_resource_list_lock);
868 list_del(&resource->list_node);
869 mutex_unlock(&power_resource_list_lock);
870
Toshi Kanic0af4172013-03-04 21:30:42 +0000871 acpi_free_pnp_ids(&device->pnp);
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100872 kfree(resource);
873}
874
Dwaipayan Ray0f39ee82020-12-17 18:15:36 +0530875static ssize_t resource_in_use_show(struct device *dev,
876 struct device_attribute *attr,
877 char *buf)
878{
Rafael J. Wysockib1c0f992013-01-24 12:50:09 +0100879 struct acpi_power_resource *resource;
880
881 resource = to_power_resource(to_acpi_device(dev));
882 return sprintf(buf, "%u\n", !!resource->ref_count);
883}
Dwaipayan Ray0f39ee82020-12-17 18:15:36 +0530884static DEVICE_ATTR_RO(resource_in_use);
Rafael J. Wysockib1c0f992013-01-24 12:50:09 +0100885
886static void acpi_power_sysfs_remove(struct acpi_device *device)
887{
888 device_remove_file(&device->dev, &dev_attr_resource_in_use);
889}
890
Rafael J. Wysockid5eefa82015-05-21 04:19:49 +0200891static void acpi_power_add_resource_to_list(struct acpi_power_resource *resource)
892{
893 mutex_lock(&power_resource_list_lock);
894
895 if (!list_empty(&acpi_power_resource_list)) {
896 struct acpi_power_resource *r;
897
898 list_for_each_entry(r, &acpi_power_resource_list, list_node)
899 if (r->order > resource->order) {
900 list_add_tail(&resource->list_node, &r->list_node);
901 goto out;
902 }
903 }
904 list_add_tail(&resource->list_node, &acpi_power_resource_list);
905
906 out:
907 mutex_unlock(&power_resource_list_lock);
908}
909
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100910int acpi_add_power_resource(acpi_handle handle)
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100911{
912 struct acpi_power_resource *resource;
913 struct acpi_device *device = NULL;
Len Brown4be44fc2005-08-05 00:44:28 -0400914 union acpi_object acpi_object;
915 struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100916 acpi_status status;
917 int state, result = -ENODEV;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700918
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100919 acpi_bus_get_device(handle, &device);
920 if (device)
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100921 return 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700922
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100923 resource = kzalloc(sizeof(*resource), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700924 if (!resource)
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100925 return -ENOMEM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700926
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100927 device = &resource->device;
928 acpi_init_device_object(device, handle, ACPI_BUS_TYPE_POWER,
Hans de Goede6091b262020-11-21 21:30:35 +0100929 ACPI_STA_DEFAULT, NULL);
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500930 mutex_init(&resource->resource_lock);
Rafael J. Wysocki6ee22e9d2013-06-19 00:44:45 +0200931 INIT_LIST_HEAD(&resource->list_node);
Mika Westerberg45337712019-06-25 13:29:41 +0300932 INIT_LIST_HEAD(&resource->dependents);
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100933 resource->name = device->pnp.bus_id;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700934 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
935 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
Rafael J. Wysocki722c9292013-01-17 14:11:06 +0100936 device->power.state = ACPI_STATE_UNKNOWN;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700937
938 /* Evalute the object to get the system level and resource order. */
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100939 status = acpi_evaluate_object(handle, NULL, NULL, &buffer);
940 if (ACPI_FAILURE(status))
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100941 goto err;
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100942
Linus Torvalds1da177e2005-04-16 15:20:36 -0700943 resource->system_level = acpi_object.power_resource.system_level;
944 resource->order = acpi_object.power_resource.resource_order;
945
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100946 result = acpi_power_get_state(handle, &state);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700947 if (result)
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100948 goto err;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700949
Rafael J. Wysocki56ce8332021-01-20 19:57:03 +0100950 pr_info("%s [%s] (%s)\n", acpi_device_name(device),
951 acpi_device_bid(device), state ? "on" : "off");
Len Brown4be44fc2005-08-05 00:44:28 -0400952
Rafael J. Wysocki82c7d5e2013-01-17 14:11:05 +0100953 device->flags.match_driver = true;
Rafael J. Wysockicf860be2013-01-24 12:49:49 +0100954 result = acpi_device_add(device, acpi_release_power_resource);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700955 if (result)
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100956 goto err;
Len Brown4be44fc2005-08-05 00:44:28 -0400957
Rafael J. Wysockib1c0f992013-01-24 12:50:09 +0100958 if (!device_create_file(&device->dev, &dev_attr_resource_in_use))
959 device->remove = acpi_power_sysfs_remove;
960
Rafael J. Wysockid5eefa82015-05-21 04:19:49 +0200961 acpi_power_add_resource_to_list(resource);
Rafael J. Wysockicf860be2013-01-24 12:49:49 +0100962 acpi_device_add_finalize(device);
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100963 return 0;
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100964
965 err:
966 acpi_release_power_resource(&device->dev);
Rafael J. Wysockie88c9c62013-01-17 14:11:07 +0100967 return result;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700968}
969
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100970#ifdef CONFIG_ACPI_SLEEP
971void acpi_resume_power_resources(void)
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500972{
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200973 struct acpi_power_resource *resource;
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500974
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100975 mutex_lock(&power_resource_list_lock);
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500976
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100977 list_for_each_entry(resource, &acpi_power_resource_list, list_node) {
978 int result, state;
Rafael J. Wysocki3e384ee2010-10-22 02:35:54 +0200979
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100980 mutex_lock(&resource->resource_lock);
Konstantin Karasyov0a613902007-02-16 01:47:06 -0500981
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100982 result = acpi_power_get_state(resource->device.handle, &state);
Lan Tianyud7d49012013-10-15 19:48:11 +0800983 if (result) {
984 mutex_unlock(&resource->resource_lock);
Rafael J. Wysocki660b1112013-01-25 21:51:57 +0100985 continue;
Lan Tianyud7d49012013-10-15 19:48:11 +0800986 }
Rafael J. Wysocki660b1112013-01-25 21:51:57 +0100987
988 if (state == ACPI_POWER_RESOURCE_STATE_OFF
Rafael J. Wysocki781d7372013-01-17 14:11:06 +0100989 && resource->ref_count) {
990 dev_info(&resource->device.dev, "Turning ON\n");
991 __acpi_power_on(resource);
Rafael J. Wysockid5eefa82015-05-21 04:19:49 +0200992 }
993
994 mutex_unlock(&resource->resource_lock);
995 }
Hans de Goede8ece1d82017-04-30 22:54:16 +0200996
997 mutex_unlock(&power_resource_list_lock);
998}
999
1000void acpi_turn_off_unused_power_resources(void)
1001{
1002 struct acpi_power_resource *resource;
1003
1004 mutex_lock(&power_resource_list_lock);
1005
Rafael J. Wysockid5eefa82015-05-21 04:19:49 +02001006 list_for_each_entry_reverse(resource, &acpi_power_resource_list, list_node) {
1007 int result, state;
1008
1009 mutex_lock(&resource->resource_lock);
1010
1011 result = acpi_power_get_state(resource->device.handle, &state);
1012 if (result) {
1013 mutex_unlock(&resource->resource_lock);
1014 continue;
1015 }
1016
1017 if (state == ACPI_POWER_RESOURCE_STATE_ON
Rafael J. Wysocki660b1112013-01-25 21:51:57 +01001018 && !resource->ref_count) {
1019 dev_info(&resource->device.dev, "Turning OFF\n");
1020 __acpi_power_off(resource);
Rafael J. Wysocki781d7372013-01-17 14:11:06 +01001021 }
Konstantin Karasyov0a613902007-02-16 01:47:06 -05001022
Rafael J. Wysocki781d7372013-01-17 14:11:06 +01001023 mutex_unlock(&resource->resource_lock);
1024 }
Konstantin Karasyov0a613902007-02-16 01:47:06 -05001025
Rafael J. Wysocki781d7372013-01-17 14:11:06 +01001026 mutex_unlock(&power_resource_list_lock);
Konstantin Karasyov0a613902007-02-16 01:47:06 -05001027}
Rafael J. Wysocki90692402012-08-09 23:00:02 +02001028#endif