2 * acpi_power.c - ACPI Bus Power Management ($Revision: 39 $)
4 * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com>
5 * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com>
7 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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.
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.
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.
23 * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
26 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/types.h>
30 #include <linux/compatmac.h>
31 #include <linux/proc_fs.h>
32 #include <linux/seq_file.h>
34 #include "acpi_drivers.h"
37 #define _COMPONENT ACPI_POWER_COMPONENT
38 ACPI_MODULE_NAME ("acpi_power")
40 #define ACPI_POWER_COMPONENT 0x00800000
41 #define ACPI_POWER_CLASS "power_resource"
42 #define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver"
43 #define ACPI_POWER_DEVICE_NAME "Power Resource"
44 #define ACPI_POWER_FILE_INFO "info"
45 #define ACPI_POWER_FILE_STATUS "state"
46 #define ACPI_POWER_RESOURCE_STATE_OFF 0x00
47 #define ACPI_POWER_RESOURCE_STATE_ON 0x01
48 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
50 int acpi_power_add (struct acpi_device
*device
);
51 int acpi_power_remove (struct acpi_device
*device
, int type
);
52 static int acpi_power_open_fs(struct inode
*inode
, struct file
*file
);
54 static struct acpi_driver acpi_power_driver
= {
55 .name
= ACPI_POWER_DRIVER_NAME
,
56 .class = ACPI_POWER_CLASS
,
57 .ids
= ACPI_POWER_HID
,
59 .add
= acpi_power_add
,
60 .remove
= acpi_power_remove
,
64 struct acpi_power_resource
74 static struct list_head acpi_power_resource_list
;
76 static struct file_operations acpi_power_fops
= {
77 .open
= acpi_power_open_fs
,
80 .release
= single_release
,
83 /* --------------------------------------------------------------------------
84 Power Resource Management
85 -------------------------------------------------------------------------- */
88 acpi_power_get_context (
90 struct acpi_power_resource
**resource
)
93 struct acpi_device
*device
= NULL
;
95 ACPI_FUNCTION_TRACE("acpi_power_get_context");
98 return_VALUE(-ENODEV
);
100 result
= acpi_bus_get_device(handle
, &device
);
102 ACPI_DEBUG_PRINT((ACPI_DB_WARN
, "Error getting context [%p]\n",
104 return_VALUE(result
);
107 *resource
= (struct acpi_power_resource
*) acpi_driver_data(device
);
109 return_VALUE(-ENODEV
);
116 acpi_power_get_state (
117 struct acpi_power_resource
*resource
)
119 acpi_status status
= AE_OK
;
120 unsigned long sta
= 0;
122 ACPI_FUNCTION_TRACE("acpi_power_get_state");
125 return_VALUE(-EINVAL
);
127 status
= acpi_evaluate_integer(resource
->handle
, "_STA", NULL
, &sta
);
128 if (ACPI_FAILURE(status
))
129 return_VALUE(-ENODEV
);
132 resource
->state
= ACPI_POWER_RESOURCE_STATE_ON
;
134 resource
->state
= ACPI_POWER_RESOURCE_STATE_OFF
;
136 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource [%s] is %s\n",
137 resource
->name
, resource
->state
?"on":"off"));
144 acpi_power_get_list_state (
145 struct acpi_handle_list
*list
,
149 struct acpi_power_resource
*resource
= NULL
;
152 ACPI_FUNCTION_TRACE("acpi_power_get_list_state");
155 return_VALUE(-EINVAL
);
157 /* The state of the list is 'on' IFF all resources are 'on'. */
159 for (i
=0; i
<list
->count
; i
++) {
160 result
= acpi_power_get_context(list
->handles
[i
], &resource
);
162 return_VALUE(result
);
163 result
= acpi_power_get_state(resource
);
165 return_VALUE(result
);
167 *state
= resource
->state
;
169 if (*state
!= ACPI_POWER_RESOURCE_STATE_ON
)
173 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource list is %s\n",
176 return_VALUE(result
);
185 acpi_status status
= AE_OK
;
186 struct acpi_device
*device
= NULL
;
187 struct acpi_power_resource
*resource
= NULL
;
189 ACPI_FUNCTION_TRACE("acpi_power_on");
191 result
= acpi_power_get_context(handle
, &resource
);
193 return_VALUE(result
);
195 resource
->references
++;
197 if ((resource
->references
> 1)
198 || (resource
->state
== ACPI_POWER_RESOURCE_STATE_ON
)) {
199 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource [%s] already on\n",
204 status
= acpi_evaluate_object(resource
->handle
, "_ON", NULL
, NULL
);
205 if (ACPI_FAILURE(status
))
206 return_VALUE(-ENODEV
);
208 result
= acpi_power_get_state(resource
);
210 return_VALUE(result
);
211 if (resource
->state
!= ACPI_POWER_RESOURCE_STATE_ON
)
212 return_VALUE(-ENOEXEC
);
214 /* Update the power resource's _device_ power state */
215 result
= acpi_bus_get_device(resource
->handle
, &device
);
217 return_VALUE(result
);
218 device
->power
.state
= ACPI_STATE_D0
;
220 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource [%s] turned on\n",
228 acpi_power_off_device (
232 acpi_status status
= AE_OK
;
233 struct acpi_device
*device
= NULL
;
234 struct acpi_power_resource
*resource
= NULL
;
236 ACPI_FUNCTION_TRACE("acpi_power_off_device");
238 result
= acpi_power_get_context(handle
, &resource
);
240 return_VALUE(result
);
242 if (resource
->references
)
243 resource
->references
--;
245 if (resource
->references
) {
246 ACPI_DEBUG_PRINT((ACPI_DB_INFO
,
247 "Resource [%s] is still in use, dereferencing\n",
248 device
->pnp
.bus_id
));
252 if (resource
->state
== ACPI_POWER_RESOURCE_STATE_OFF
) {
253 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource [%s] already off\n",
254 device
->pnp
.bus_id
));
258 status
= acpi_evaluate_object(resource
->handle
, "_OFF", NULL
, NULL
);
259 if (ACPI_FAILURE(status
))
260 return_VALUE(-ENODEV
);
262 result
= acpi_power_get_state(resource
);
264 return_VALUE(result
);
265 if (resource
->state
!= ACPI_POWER_RESOURCE_STATE_OFF
)
266 return_VALUE(-ENOEXEC
);
268 /* Update the power resource's _device_ power state */
269 result
= acpi_bus_get_device(resource
->handle
, &device
);
271 return_VALUE(result
);
272 device
->power
.state
= ACPI_STATE_D3
;
274 ACPI_DEBUG_PRINT((ACPI_DB_INFO
, "Resource [%s] turned off\n",
281 /* --------------------------------------------------------------------------
282 Device Power Management
283 -------------------------------------------------------------------------- */
286 acpi_power_get_inferred_state (
287 struct acpi_device
*device
)
290 struct acpi_handle_list
*list
= NULL
;
294 ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state");
297 return_VALUE(-EINVAL
);
299 device
->power
.state
= ACPI_STATE_UNKNOWN
;
302 * We know a device's inferred power state when all the resources
303 * required for a given D-state are 'on'.
305 for (i
=ACPI_STATE_D0
; i
<ACPI_STATE_D3
; i
++) {
306 list
= &device
->power
.states
[i
].resources
;
310 result
= acpi_power_get_list_state(list
, &list_state
);
312 return_VALUE(result
);
314 if (list_state
== ACPI_POWER_RESOURCE_STATE_ON
) {
315 device
->power
.state
= i
;
320 device
->power
.state
= ACPI_STATE_D3
;
327 acpi_power_transition (
328 struct acpi_device
*device
,
332 struct acpi_handle_list
*cl
= NULL
; /* Current Resources */
333 struct acpi_handle_list
*tl
= NULL
; /* Target Resources */
336 ACPI_FUNCTION_TRACE("acpi_power_transition");
338 if (!device
|| (state
< ACPI_STATE_D0
) || (state
> ACPI_STATE_D3
))
339 return_VALUE(-EINVAL
);
341 cl
= &device
->power
.states
[device
->power
.state
].resources
;
342 tl
= &device
->power
.states
[state
].resources
;
344 device
->power
.state
= ACPI_STATE_UNKNOWN
;
346 if (!cl
->count
&& !tl
->count
) {
351 /* TBD: Resources must be ordered. */
354 * First we reference all power resources required in the target list
355 * (e.g. so the device doesn't loose power while transitioning).
357 for (i
=0; i
<tl
->count
; i
++) {
358 result
= acpi_power_on(tl
->handles
[i
]);
363 device
->power
.state
= state
;
366 * Then we dereference all power resources used in the current list.
368 for (i
=0; i
<cl
->count
; i
++) {
369 result
= acpi_power_off_device(cl
->handles
[i
]);
376 ACPI_DEBUG_PRINT((ACPI_DB_WARN
,
377 "Error transitioning device [%s] to D%d\n",
378 device
->pnp
.bus_id
, state
));
380 return_VALUE(result
);
384 /* --------------------------------------------------------------------------
386 -------------------------------------------------------------------------- */
388 struct proc_dir_entry
*acpi_power_dir
= NULL
;
390 static int acpi_power_seq_show(struct seq_file
*seq
, void *offset
)
392 struct acpi_power_resource
*resource
= NULL
;
394 ACPI_FUNCTION_TRACE("acpi_power_seq_show");
396 resource
= (struct acpi_power_resource
*)seq
->private;
401 seq_puts(seq
, "state: ");
402 switch (resource
->state
) {
403 case ACPI_POWER_RESOURCE_STATE_ON
:
404 seq_puts(seq
, "on\n");
406 case ACPI_POWER_RESOURCE_STATE_OFF
:
407 seq_puts(seq
, "off\n");
410 seq_puts(seq
, "unknown\n");
414 seq_printf(seq
, "system level: S%d\n"
416 "reference count: %d\n",
417 resource
->system_level
,
419 resource
->references
);
425 static int acpi_power_open_fs(struct inode
*inode
, struct file
*file
)
427 return single_open(file
, acpi_power_seq_show
, PDE(inode
)->data
);
432 struct acpi_device
*device
)
434 struct proc_dir_entry
*entry
= NULL
;
436 ACPI_FUNCTION_TRACE("acpi_power_add_fs");
439 return_VALUE(-EINVAL
);
441 if (!acpi_device_dir(device
)) {
442 acpi_device_dir(device
) = proc_mkdir(acpi_device_bid(device
),
444 if (!acpi_device_dir(device
))
445 return_VALUE(-ENODEV
);
449 entry
= create_proc_entry(ACPI_POWER_FILE_STATUS
,
450 S_IRUGO
, acpi_device_dir(device
));
452 ACPI_DEBUG_PRINT((ACPI_DB_ERROR
,
453 "Unable to create '%s' fs entry\n",
454 ACPI_POWER_FILE_STATUS
));
456 entry
->proc_fops
= &acpi_power_fops
;
457 entry
->data
= acpi_driver_data(device
);
465 acpi_power_remove_fs (
466 struct acpi_device
*device
)
468 ACPI_FUNCTION_TRACE("acpi_power_remove_fs");
470 if (acpi_device_dir(device
)) {
471 remove_proc_entry(acpi_device_bid(device
), acpi_power_dir
);
472 acpi_device_dir(device
) = NULL
;
479 /* --------------------------------------------------------------------------
481 -------------------------------------------------------------------------- */
485 struct acpi_device
*device
)
488 acpi_status status
= AE_OK
;
489 struct acpi_power_resource
*resource
= NULL
;
490 union acpi_object acpi_object
;
491 struct acpi_buffer buffer
= {sizeof(acpi_object
), &acpi_object
};
493 ACPI_FUNCTION_TRACE("acpi_power_add");
496 return_VALUE(-EINVAL
);
498 resource
= kmalloc(sizeof(struct acpi_power_resource
), GFP_KERNEL
);
500 return_VALUE(-ENOMEM
);
501 memset(resource
, 0, sizeof(struct acpi_power_resource
));
503 resource
->handle
= device
->handle
;
504 sprintf(resource
->name
, "%s", device
->pnp
.bus_id
);
505 sprintf(acpi_device_name(device
), "%s", ACPI_POWER_DEVICE_NAME
);
506 sprintf(acpi_device_class(device
), "%s", ACPI_POWER_CLASS
);
507 acpi_driver_data(device
) = resource
;
509 /* Evalute the object to get the system level and resource order. */
510 status
= acpi_evaluate_object(resource
->handle
, NULL
, NULL
, &buffer
);
511 if (ACPI_FAILURE(status
)) {
515 resource
->system_level
= acpi_object
.power_resource
.system_level
;
516 resource
->order
= acpi_object
.power_resource
.resource_order
;
518 result
= acpi_power_get_state(resource
);
522 switch (resource
->state
) {
523 case ACPI_POWER_RESOURCE_STATE_ON
:
524 device
->power
.state
= ACPI_STATE_D0
;
526 case ACPI_POWER_RESOURCE_STATE_OFF
:
527 device
->power
.state
= ACPI_STATE_D3
;
530 device
->power
.state
= ACPI_STATE_UNKNOWN
;
534 result
= acpi_power_add_fs(device
);
538 printk(KERN_INFO PREFIX
"%s [%s] (%s)\n", acpi_device_name(device
),
539 acpi_device_bid(device
), resource
->state
?"on":"off");
545 return_VALUE(result
);
551 struct acpi_device
*device
,
554 struct acpi_power_resource
*resource
= NULL
;
556 ACPI_FUNCTION_TRACE("acpi_power_remove");
558 if (!device
|| !acpi_driver_data(device
))
559 return_VALUE(-EINVAL
);
561 resource
= (struct acpi_power_resource
*) acpi_driver_data(device
);
563 acpi_power_remove_fs(device
);
571 static int __init
acpi_power_init (void)
575 ACPI_FUNCTION_TRACE("acpi_power_init");
580 INIT_LIST_HEAD(&acpi_power_resource_list
);
582 acpi_power_dir
= proc_mkdir(ACPI_POWER_CLASS
, acpi_root_dir
);
584 return_VALUE(-ENODEV
);
586 result
= acpi_bus_register_driver(&acpi_power_driver
);
588 remove_proc_entry(ACPI_POWER_CLASS
, acpi_root_dir
);
589 return_VALUE(-ENODEV
);
595 subsys_initcall(acpi_power_init
);