xref: /reactos/drivers/bus/acpi/busmgr/power.c (revision c2c66aff)
1 /*
2  *  acpi_power.c - ACPI Bus Power Management ($Revision: 39 $)
3  *
4  *  Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5  *  Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
6  *
7  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
8  *
9  *  This program is free software; you can redistribute it and/or modify
10  *  it under the terms of the GNU General Public License as published by
11  *  the Free Software Foundation; either version 2 of the License, or (at
12  *  your option) any later version.
13  *
14  *  This program is distributed in the hope that it will be useful, but
15  *  WITHOUT ANY WARRANTY; without even the implied warranty of
16  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  *  General Public License for more details.
18  *
19  *  You should have received a copy of the GNU General Public License along
20  *  with this program; if not, write to the Free Software Foundation, Inc.,
21  *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
22  *
23  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
24  */
25 
26 /*
27  * ACPI power-managed devices may be controlled in two ways:
28  * 1. via "Device Specific (D-State) Control"
29  * 2. via "Power Resource Control".
30  * This module is used to manage devices relying on Power Resource Control.
31  *
32  * An ACPI "power resource object" describes a software controllable power
33  * plane, clock plane, or other resource used by a power managed device.
34  * A device may rely on multiple power resources, and a power resource
35  * may be shared by multiple devices.
36  */
37 
38 /*
39  * Modified for ReactOS and latest ACPICA
40  * Copyright (C)2009  Samuel Serapion
41  */
42 
43 #include <precomp.h>
44 
45 #define NDEBUG
46 #include <debug.h>
47 
48 #define _COMPONENT		ACPI_POWER_COMPONENT
49 ACPI_MODULE_NAME		("acpi_power")
50 
51 #define ACPI_POWER_RESOURCE_STATE_OFF	0x00
52 #define ACPI_POWER_RESOURCE_STATE_ON	0x01
53 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
54 
55 int acpi_power_nocheck;
56 
57 static int acpi_power_add (struct acpi_device *device);
58 static int acpi_power_remove (struct acpi_device *device, int type);
59 static int acpi_power_resume(struct acpi_device *device, int state);
60 
61 static struct acpi_driver acpi_power_driver = {
62 	{0,0},
63 	ACPI_POWER_DRIVER_NAME,
64 	ACPI_POWER_CLASS,
65 	0,
66 	0,
67 	ACPI_POWER_HID,
68 	{acpi_power_add, acpi_power_remove, NULL, NULL, acpi_power_resume}
69 };
70 
71 struct acpi_power_reference {
72 	struct list_head node;
73 	struct acpi_device *device;
74 };
75 
76 struct acpi_power_resource
77 {
78 	struct acpi_device * device;
79 	acpi_bus_id		name;
80 	UINT32			system_level;
81 	UINT32			order;
82 	//struct mutex resource_lock;
83 	struct list_head reference;
84 };
85 
86 static struct list_head		acpi_power_resource_list;
87 
88 
89 /* --------------------------------------------------------------------------
90                              Power Resource Management
91    -------------------------------------------------------------------------- */
92 
93 static int
acpi_power_get_context(ACPI_HANDLE handle,struct acpi_power_resource ** resource)94 acpi_power_get_context (
95 	ACPI_HANDLE		handle,
96 	struct acpi_power_resource **resource)
97 {
98 	int			result = 0;
99 	struct acpi_device	*device = NULL;
100 
101 	if (!resource)
102 		return_VALUE(-15);
103 
104 	result = acpi_bus_get_device(handle, &device);
105 	if (result) {
106 		ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n",
107 			handle));
108 		return_VALUE(result);
109 	}
110 
111 	*resource = (struct acpi_power_resource *) acpi_driver_data(device);
112 	if (!*resource)
113 		return_VALUE(-15);
114 
115 	return 0;
116 }
117 
118 
119 static int
acpi_power_get_state(ACPI_HANDLE handle,int * state)120 acpi_power_get_state (
121 	ACPI_HANDLE handle,
122 	int *state)
123 {
124 	ACPI_STATUS		status = AE_OK;
125 	unsigned long long	sta = 0;
126 	char node_name[5];
127 	ACPI_BUFFER buffer = { sizeof(node_name), node_name };
128 
129 
130 	if (!handle || !state)
131 		return_VALUE(-1);
132 
133 	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
134 	if (ACPI_FAILURE(status))
135 		return_VALUE(-15);
136 
137 	*state = (sta & 0x01)?ACPI_POWER_RESOURCE_STATE_ON:
138 			      ACPI_POWER_RESOURCE_STATE_OFF;
139 
140 	AcpiGetName(handle, ACPI_SINGLE_NAME, &buffer);
141 
142 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
143 		node_name, *state?"on":"off"));
144 
145 	return 0;
146 }
147 
148 
149 static int
acpi_power_get_list_state(struct acpi_handle_list * list,int * state)150 acpi_power_get_list_state (
151 	struct acpi_handle_list	*list,
152 	int			*state)
153 {
154 	int result = 0, state1;
155 	UINT32 i = 0;
156 
157 	if (!list || !state)
158 		return_VALUE(-1);
159 
160 	/* The state of the list is 'on' IFF all resources are 'on'. */
161 
162 	for (i=0; i<list->count; i++) {
163 		/*
164 		 * The state of the power resource can be obtained by
165 		 * using the ACPI handle. In such case it is unnecessary to
166 		 * get the Power resource first and then get its state again.
167 		 */
168 		result = acpi_power_get_state(list->handles[i], &state1);
169 		if (result)
170 			return result;
171 
172 		*state = state1;
173 
174 		if (*state != ACPI_POWER_RESOURCE_STATE_ON)
175 			break;
176 	}
177 
178 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
179 		*state?"on":"off"));
180 
181 	return result;
182 }
183 
184 
185 static int
acpi_power_on(ACPI_HANDLE handle,struct acpi_device * dev)186 acpi_power_on (
187 	ACPI_HANDLE handle, struct acpi_device *dev)
188 {
189 	int result = 0;
190 	int found = 0;
191 	ACPI_STATUS status = AE_OK;
192 	struct acpi_power_resource *resource = NULL;
193 	struct list_head *node, *next;
194 	struct acpi_power_reference *ref;
195 
196 	result = acpi_power_get_context(handle, &resource);
197 	if (result)
198 		return result;
199 
200 	//mutex_lock(&resource->resource_lock);
201 	list_for_each_safe(node, next, &resource->reference) {
202 		ref = container_of(node, struct acpi_power_reference, node);
203 		if (dev->handle == ref->device->handle) {
204 			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
205 				  dev->pnp.bus_id, resource->name));
206 			found = 1;
207 			break;
208 		}
209 	}
210 
211 	if (!found) {
212 		ref = ExAllocatePoolWithTag(NonPagedPool,sizeof (struct acpi_power_reference),'IPCA');
213 		if (!ref) {
214 			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
215 			//mutex_unlock(&resource->resource_lock);
216 			return -1;//-ENOMEM;
217 		}
218 		list_add_tail(&ref->node, &resource->reference);
219 		ref->device = dev;
220 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
221 			  dev->pnp.bus_id, resource->name));
222 	}
223 	//mutex_unlock(&resource->resource_lock);
224 
225 	status = AcpiEvaluateObject(resource->device->handle, "_ON", NULL, NULL);
226 	if (ACPI_FAILURE(status))
227 		return_VALUE(-15);
228 
229 	/* Update the power resource's _device_ power state */
230 	resource->device->power.state = ACPI_STATE_D0;
231 
232 	return 0;
233 }
234 
235 
236 static int
acpi_power_off_device(ACPI_HANDLE handle,struct acpi_device * dev)237 acpi_power_off_device (
238 	ACPI_HANDLE handle,
239 	struct acpi_device *dev)
240 {
241 	int result = 0;
242 	ACPI_STATUS status = AE_OK;
243 	struct acpi_power_resource *resource = NULL;
244 	struct list_head *node, *next;
245 	struct acpi_power_reference *ref;
246 
247 	result = acpi_power_get_context(handle, &resource);
248 	if (result)
249 		return result;
250 
251 	//mutex_lock(&resource->resource_lock);
252 	list_for_each_safe(node, next, &resource->reference) {
253 		ref = container_of(node, struct acpi_power_reference, node);
254 		if (dev->handle == ref->device->handle) {
255 			list_del(&ref->node);
256 			ExFreePool(ref);
257 			ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
258 			    dev->pnp.bus_id, resource->name));
259 			break;
260 		}
261 	}
262 
263 	if (!list_empty(&resource->reference)) {
264 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
265 		    resource->name));
266 		//mutex_unlock(&resource->resource_lock);
267 		return 0;
268 	}
269 	//mutex_unlock(&resource->resource_lock);
270 
271 	status = AcpiEvaluateObject(resource->device->handle, "_OFF", NULL, NULL);
272 	if (ACPI_FAILURE(status))
273 		return -1;
274 
275 	/* Update the power resource's _device_ power state */
276 	resource->device->power.state = ACPI_STATE_D3;
277 
278 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
279 			  resource->name));
280 
281 	return 0;
282 }
283 
284 /**
285  * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
286  *                          ACPI 3.0) _PSW (Power State Wake)
287  * @dev: Device to handle.
288  * @enable: 0 - disable, 1 - enable the wake capabilities of the device.
289  * @sleep_state: Target sleep state of the system.
290  * @dev_state: Target power state of the device.
291  *
292  * Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
293  * State Wake) for the device, if present.  On failure reset the device's
294  * wakeup.flags.valid flag.
295  *
296  * RETURN VALUE:
297  * 0 if either _DSW or _PSW has been successfully executed
298  * 0 if neither _DSW nor _PSW has been found
299  * -ENODEV if the execution of either _DSW or _PSW has failed
300  */
acpi_device_sleep_wake(struct acpi_device * dev,int enable,int sleep_state,int dev_state)301 int acpi_device_sleep_wake(struct acpi_device *dev,
302                            int enable, int sleep_state, int dev_state)
303 {
304 	union acpi_object in_arg[3];
305 	struct acpi_object_list arg_list = { 3, in_arg };
306 	ACPI_STATUS status = AE_OK;
307 
308 	/*
309 	 * Try to execute _DSW first.
310 	 *
311 	 * Three arguments are needed for the _DSW object:
312 	 * Argument 0: enable/disable the wake capabilities
313 	 * Argument 1: target system state
314 	 * Argument 2: target device state
315 	 * When _DSW object is called to disable the wake capabilities, maybe
316 	 * the first argument is filled. The values of the other two arguments
317 	 * are meaningless.
318 	 */
319 	in_arg[0].Type = ACPI_TYPE_INTEGER;
320 	in_arg[0].Integer.Value = enable;
321 	in_arg[1].Type = ACPI_TYPE_INTEGER;
322 	in_arg[1].Integer.Value = sleep_state;
323 	in_arg[2].Type = ACPI_TYPE_INTEGER;
324 	in_arg[2].Integer.Value = dev_state;
325 	status = AcpiEvaluateObject(dev->handle, "_DSW", &arg_list, NULL);
326 	if (ACPI_SUCCESS(status)) {
327 		return 0;
328 	} else if (status != AE_NOT_FOUND) {
329 		DPRINT1("_DSW execution failed\n");
330 		dev->wakeup.flags.valid = 0;
331 		return -1;
332 	}
333 
334 	/* Execute _PSW */
335 	arg_list.Count = 1;
336 	in_arg[0].Integer.Value = enable;
337 	status = AcpiEvaluateObject(dev->handle, "_PSW", &arg_list, NULL);
338 	if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
339 		DPRINT1("_PSW execution failed\n");
340 		dev->wakeup.flags.valid = 0;
341 		return -1;
342 	}
343 
344 	return 0;
345 }
346 
347 /*
348  * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
349  * 1. Power on the power resources required for the wakeup device
350  * 2. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
351  *    State Wake) for the device, if present
352  */
acpi_enable_wakeup_device_power(struct acpi_device * dev,int sleep_state)353 int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
354 {
355 	unsigned int i;
356 	int err = 0;
357 
358 	if (!dev || !dev->wakeup.flags.valid)
359 		return -1;
360 
361 	//mutex_lock(&acpi_device_lock);
362 
363 	if (dev->wakeup.prepare_count++)
364 		goto out;
365 
366 	/* Open power resource */
367 	for (i = 0; i < dev->wakeup.resources.count; i++) {
368 		int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
369 		if (ret) {
370 			DPRINT( "Transition power state\n");
371 			dev->wakeup.flags.valid = 0;
372 			err = -1;
373 			goto err_out;
374 		}
375 	}
376 
377 	/*
378 	 * Passing 3 as the third argument below means the device may be placed
379 	 * in arbitrary power state afterwards.
380 	 */
381 	err = acpi_device_sleep_wake(dev, 1, sleep_state, 3);
382 
383  err_out:
384 	if (err)
385 		dev->wakeup.prepare_count = 0;
386 
387  out:
388 	//mutex_unlock(&acpi_device_lock);
389 	return err;
390 }
391 
392 /*
393  * Shutdown a wakeup device, counterpart of above method
394  * 1. Execute _DSW (Device Sleep Wake) or (deprecated in ACPI 3.0) _PSW (Power
395  *    State Wake) for the device, if present
396  * 2. Shutdown down the power resources
397  */
acpi_disable_wakeup_device_power(struct acpi_device * dev)398 int acpi_disable_wakeup_device_power(struct acpi_device *dev)
399 {
400 	unsigned int i;
401 	int err = 0;
402 
403 	if (!dev || !dev->wakeup.flags.valid)
404 		return -1;
405 
406 	//mutex_lock(&acpi_device_lock);
407 
408 	if (--dev->wakeup.prepare_count > 0)
409 		goto out;
410 
411 	/*
412 	 * Executing the code below even if prepare_count is already zero when
413 	 * the function is called may be useful, for example for initialisation.
414 	 */
415 	if (dev->wakeup.prepare_count < 0)
416 		dev->wakeup.prepare_count = 0;
417 
418 	err = acpi_device_sleep_wake(dev, 0, 0, 0);
419 	if (err)
420 		goto out;
421 
422 	/* Close power resource */
423 	for (i = 0; i < dev->wakeup.resources.count; i++) {
424 		int ret = acpi_power_off_device(
425 				dev->wakeup.resources.handles[i], dev);
426 		if (ret) {
427 			DPRINT("Transition power state\n");
428 			dev->wakeup.flags.valid = 0;
429 			err = -1;
430 			goto out;
431 		}
432 	}
433 
434  out:
435 	//mutex_unlock(&acpi_device_lock);
436 	return err;
437 }
438 
439 /* --------------------------------------------------------------------------
440                              Device Power Management
441    -------------------------------------------------------------------------- */
442 
443 int
acpi_power_get_inferred_state(struct acpi_device * device)444 acpi_power_get_inferred_state (
445 	struct acpi_device	*device)
446 {
447 	int			result = 0;
448 	struct acpi_handle_list	*list = NULL;
449 	int			list_state = 0;
450 	int			i = 0;
451 
452 
453 	if (!device)
454 		return_VALUE(-1);
455 
456 	device->power.state = ACPI_STATE_UNKNOWN;
457 
458 	/*
459 	 * We know a device's inferred power state when all the resources
460 	 * required for a given D-state are 'on'.
461 	 */
462 	for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
463 		list = &device->power.states[i].resources;
464 		if (list->count < 1)
465 			continue;
466 
467 		result = acpi_power_get_list_state(list, &list_state);
468 		if (result)
469 			return_VALUE(result);
470 
471 		if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
472 			device->power.state = i;
473 			return_VALUE(0);
474 		}
475 	}
476 
477 	device->power.state = ACPI_STATE_D3;
478 
479 	return_VALUE(0);
480 }
481 
482 
483 int
acpi_power_transition(struct acpi_device * device,int state)484 acpi_power_transition (
485 	struct acpi_device	*device,
486 	int			state)
487 {
488 	int			result = 0;
489 	struct acpi_handle_list	*cl = NULL;	/* Current Resources */
490 	struct acpi_handle_list	*tl = NULL;	/* Target Resources */
491 	unsigned int	i = 0;
492 
493 	if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
494 		return_VALUE(-1);
495 
496 	if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3))
497 		return_VALUE(-15);
498 
499 	cl = &device->power.states[device->power.state].resources;
500 	tl = &device->power.states[state].resources;
501 
502 	/* TBD: Resources must be ordered. */
503 
504 	/*
505 	 * First we reference all power resources required in the target list
506 	 * (e.g. so the device doesn't lose power while transitioning).
507 	 */
508 	for (i = 0; i < tl->count; i++) {
509 		result = acpi_power_on(tl->handles[i], device);
510 		if (result)
511 			goto end;
512 	}
513 
514 	if (device->power.state == state) {
515 		goto end;
516 	}
517 
518 	/*
519 	 * Then we dereference all power resources used in the current list.
520 	 */
521 	for (i = 0; i < cl->count; i++) {
522 		result = acpi_power_off_device(cl->handles[i], device);
523 		if (result)
524 			goto end;
525 	}
526 
527      end:
528 	if (result)
529 		device->power.state = ACPI_STATE_UNKNOWN;
530 	else {
531 	/* We shouldn't change the state till all above operations succeed */
532 		device->power.state = state;
533 	}
534 
535 	return result;
536 }
537 
538 /* --------------------------------------------------------------------------
539                                 Driver Interface
540    -------------------------------------------------------------------------- */
541 
542 int
acpi_power_add(struct acpi_device * device)543 acpi_power_add (
544 	struct acpi_device	*device)
545 {
546 	int	 result = 0, state;
547 	ACPI_STATUS		status = AE_OK;
548 	struct acpi_power_resource *resource = NULL;
549 	union acpi_object	acpi_object;
550 	ACPI_BUFFER	buffer = {sizeof(ACPI_OBJECT), &acpi_object};
551 
552 
553 	if (!device)
554 		return_VALUE(-1);
555 
556 	resource = ExAllocatePoolWithTag(NonPagedPool,sizeof(struct acpi_power_resource),'IPCA');
557 	if (!resource)
558 		return_VALUE(-4);
559 
560 	resource->device = device;
561 	//mutex_init(&resource->resource_lock);
562 	INIT_LIST_HEAD(&resource->reference);
563 
564 	strcpy(resource->name, device->pnp.bus_id);
565 	strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
566 	strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
567 	device->driver_data = resource;
568 
569 	/* Evalute the object to get the system level and resource order. */
570 	status = AcpiEvaluateObject(device->handle, NULL, NULL, &buffer);
571 	if (ACPI_FAILURE(status)) {
572 		result = -15;
573 		goto end;
574 	}
575 	resource->system_level = acpi_object.PowerResource.SystemLevel;
576 	resource->order = acpi_object.PowerResource.ResourceOrder;
577 
578 	result = acpi_power_get_state(device->handle, &state);
579 	if (result)
580 		goto end;
581 
582 	switch (state) {
583 	case ACPI_POWER_RESOURCE_STATE_ON:
584 		device->power.state = ACPI_STATE_D0;
585 		break;
586 	case ACPI_POWER_RESOURCE_STATE_OFF:
587 		device->power.state = ACPI_STATE_D3;
588 		break;
589 	default:
590 		device->power.state = ACPI_STATE_UNKNOWN;
591 		break;
592 	}
593 
594 
595 	DPRINT("%s [%s] (%s)\n", acpi_device_name(device),
596 		acpi_device_bid(device), state?"on":"off");
597 
598 end:
599 	if (result)
600 		ExFreePool(resource);
601 
602 	return result;
603 }
604 
605 
606 int
acpi_power_remove(struct acpi_device * device,int type)607 acpi_power_remove (
608 	struct acpi_device	*device,
609 	int			type)
610 {
611 	struct acpi_power_resource *resource = NULL;
612 	struct list_head *node, *next;
613 
614 	if (!device || !acpi_driver_data(device))
615 		return_VALUE(-1);
616 
617 	resource = acpi_driver_data(device);
618 
619 	//mutex_lock(&resource->resource_lock);
620 	list_for_each_safe(node, next, &resource->reference) {
621 		struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
622 		list_del(&ref->node);
623 		ExFreePool(ref);
624 	}
625 	//mutex_unlock(&resource->resource_lock);
626 	ExFreePool(resource);
627 
628 	return_VALUE(0);
629 }
630 
acpi_power_resume(struct acpi_device * device,int state)631 static int acpi_power_resume(struct acpi_device *device, int state)
632 {
633 	int result = 0;
634 	struct acpi_power_resource *resource = NULL;
635 	struct acpi_power_reference *ref;
636 
637 	if (!device || !acpi_driver_data(device))
638 		return -1;
639 
640 	resource = acpi_driver_data(device);
641 
642 	result = acpi_power_get_state(device->handle, &state);
643 	if (result)
644 		return result;
645 
646 	//mutex_lock(&resource->resource_lock);
647 	if (state == ACPI_POWER_RESOURCE_STATE_OFF &&
648 	    !list_empty(&resource->reference)) {
649 		ref = container_of(resource->reference.next, struct acpi_power_reference, node);
650 		//mutex_unlock(&resource->resource_lock);
651 		result = acpi_power_on(device->handle, ref->device);
652 		return result;
653 	}
654 
655 	//mutex_unlock(&resource->resource_lock);
656 	return 0;
657 }
658 
659 int
acpi_power_init(void)660 acpi_power_init (void)
661 {
662 	int			result = 0;
663 
664 	DPRINT("acpi_power_init\n");
665 
666 	INIT_LIST_HEAD(&acpi_power_resource_list);
667 
668 
669 	result = acpi_bus_register_driver(&acpi_power_driver);
670 	if (result < 0) {
671 		return_VALUE(-15);
672 	}
673 
674 	return_VALUE(0);
675 }
676