ACPI: Thermal issues on HP nx6325
[linux-2.6-block.git] / drivers / acpi / power.c
CommitLineData
1da177e4
LT
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#include <linux/kernel.h>
39#include <linux/module.h>
40#include <linux/init.h>
41#include <linux/types.h>
42#include <linux/proc_fs.h>
43#include <linux/seq_file.h>
44#include <acpi/acpi_bus.h>
45#include <acpi/acpi_drivers.h>
46
1da177e4 47#define _COMPONENT ACPI_POWER_COMPONENT
4be44fcd 48ACPI_MODULE_NAME("acpi_power")
1da177e4
LT
49#define ACPI_POWER_COMPONENT 0x00800000
50#define ACPI_POWER_CLASS "power_resource"
51#define ACPI_POWER_DRIVER_NAME "ACPI Power Resource Driver"
52#define ACPI_POWER_DEVICE_NAME "Power Resource"
53#define ACPI_POWER_FILE_INFO "info"
54#define ACPI_POWER_FILE_STATUS "state"
55#define ACPI_POWER_RESOURCE_STATE_OFF 0x00
56#define ACPI_POWER_RESOURCE_STATE_ON 0x01
57#define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
4be44fcd
LB
58static int acpi_power_add(struct acpi_device *device);
59static int acpi_power_remove(struct acpi_device *device, int type);
0a613902 60static int acpi_power_resume(struct acpi_device *device, int state);
1da177e4
LT
61static int acpi_power_open_fs(struct inode *inode, struct file *file);
62
63static struct acpi_driver acpi_power_driver = {
4be44fcd
LB
64 .name = ACPI_POWER_DRIVER_NAME,
65 .class = ACPI_POWER_CLASS,
66 .ids = ACPI_POWER_HID,
67 .ops = {
68 .add = acpi_power_add,
69 .remove = acpi_power_remove,
0a613902 70 .resume = acpi_power_resume,
4be44fcd 71 },
1da177e4
LT
72};
73
0a613902
KK
74struct acpi_power_reference {
75 struct list_head node;
76 struct acpi_device *device;
77};
78
4be44fcd 79struct acpi_power_resource {
41598572 80 struct acpi_device * device;
4be44fcd
LB
81 acpi_bus_id name;
82 u32 system_level;
83 u32 order;
84 int state;
0a613902
KK
85 struct mutex resource_lock;
86 struct list_head reference;
1da177e4
LT
87};
88
4be44fcd 89static struct list_head acpi_power_resource_list;
1da177e4 90
d7508032 91static const struct file_operations acpi_power_fops = {
4be44fcd
LB
92 .open = acpi_power_open_fs,
93 .read = seq_read,
94 .llseek = seq_lseek,
95 .release = single_release,
1da177e4
LT
96};
97
98/* --------------------------------------------------------------------------
99 Power Resource Management
100 -------------------------------------------------------------------------- */
101
102static int
4be44fcd
LB
103acpi_power_get_context(acpi_handle handle,
104 struct acpi_power_resource **resource)
1da177e4 105{
4be44fcd
LB
106 int result = 0;
107 struct acpi_device *device = NULL;
1da177e4 108
1da177e4
LT
109
110 if (!resource)
d550d98d 111 return -ENODEV;
1da177e4
LT
112
113 result = acpi_bus_get_device(handle, &device);
114 if (result) {
cece9296 115 printk(KERN_WARNING PREFIX "Getting context [%p]\n", handle);
d550d98d 116 return result;
1da177e4
LT
117 }
118
50dd0969 119 *resource = acpi_driver_data(device);
1da177e4 120 if (!resource)
d550d98d 121 return -ENODEV;
1da177e4 122
d550d98d 123 return 0;
1da177e4
LT
124}
125
4be44fcd 126static int acpi_power_get_state(struct acpi_power_resource *resource)
1da177e4 127{
4be44fcd
LB
128 acpi_status status = AE_OK;
129 unsigned long sta = 0;
1da177e4 130
1da177e4
LT
131
132 if (!resource)
d550d98d 133 return -EINVAL;
1da177e4 134
5fbc19ef 135 status = acpi_evaluate_integer(resource->device->handle, "_STA", NULL, &sta);
1da177e4 136 if (ACPI_FAILURE(status))
d550d98d 137 return -ENODEV;
1da177e4
LT
138
139 if (sta & 0x01)
140 resource->state = ACPI_POWER_RESOURCE_STATE_ON;
141 else
142 resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
143
144 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
4be44fcd 145 resource->name, resource->state ? "on" : "off"));
1da177e4 146
d550d98d 147 return 0;
1da177e4
LT
148}
149
4be44fcd 150static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
1da177e4 151{
4be44fcd 152 int result = 0;
1da177e4 153 struct acpi_power_resource *resource = NULL;
4be44fcd 154 u32 i = 0;
1da177e4 155
1da177e4
LT
156
157 if (!list || !state)
d550d98d 158 return -EINVAL;
1da177e4
LT
159
160 /* The state of the list is 'on' IFF all resources are 'on'. */
161
4be44fcd 162 for (i = 0; i < list->count; i++) {
1da177e4
LT
163 result = acpi_power_get_context(list->handles[i], &resource);
164 if (result)
d550d98d 165 return result;
1da177e4
LT
166 result = acpi_power_get_state(resource);
167 if (result)
d550d98d 168 return result;
1da177e4
LT
169
170 *state = resource->state;
171
172 if (*state != ACPI_POWER_RESOURCE_STATE_ON)
173 break;
174 }
175
176 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
4be44fcd 177 *state ? "on" : "off"));
1da177e4 178
d550d98d 179 return result;
1da177e4
LT
180}
181
0a613902 182static int acpi_power_on(acpi_handle handle, struct acpi_device *dev)
1da177e4 183{
4be44fcd 184 int result = 0;
0a613902 185 int found = 0;
4be44fcd 186 acpi_status status = AE_OK;
1da177e4 187 struct acpi_power_resource *resource = NULL;
0a613902
KK
188 struct list_head *node, *next;
189 struct acpi_power_reference *ref;
1da177e4 190
1da177e4
LT
191
192 result = acpi_power_get_context(handle, &resource);
193 if (result)
d550d98d 194 return result;
1da177e4 195
0a613902
KK
196 mutex_lock(&resource->resource_lock);
197 list_for_each_safe(node, next, &resource->reference) {
198 ref = container_of(node, struct acpi_power_reference, node);
199 if (dev->handle == ref->device->handle) {
200 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
201 dev->pnp.bus_id, resource->name));
202 found = 1;
203 break;
204 }
205 }
206
207 if (!found) {
208 ref = kmalloc(sizeof (struct acpi_power_reference),
209 irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL);
210 if (!ref) {
211 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n"));
212 mutex_unlock(&resource->resource_lock);
213 return -ENOMEM;
214 }
215 list_add_tail(&ref->node, &resource->reference);
216 ref->device = dev;
217 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
218 dev->pnp.bus_id, resource->name));
219 }
220 mutex_unlock(&resource->resource_lock);
1da177e4 221
0a613902 222 if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) {
1da177e4 223 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
4be44fcd 224 resource->name));
d550d98d 225 return 0;
1da177e4
LT
226 }
227
5fbc19ef 228 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
1da177e4 229 if (ACPI_FAILURE(status))
d550d98d 230 return -ENODEV;
1da177e4
LT
231
232 result = acpi_power_get_state(resource);
233 if (result)
d550d98d 234 return result;
1da177e4 235 if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
d550d98d 236 return -ENOEXEC;
1da177e4
LT
237
238 /* Update the power resource's _device_ power state */
41598572 239 resource->device->power.state = ACPI_STATE_D0;
1da177e4
LT
240
241 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
4be44fcd 242 resource->name));
d550d98d 243 return 0;
1da177e4
LT
244}
245
0a613902 246static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev)
1da177e4 247{
4be44fcd
LB
248 int result = 0;
249 acpi_status status = AE_OK;
1da177e4 250 struct acpi_power_resource *resource = NULL;
0a613902
KK
251 struct list_head *node, *next;
252 struct acpi_power_reference *ref;
253
1da177e4 254
1da177e4
LT
255 result = acpi_power_get_context(handle, &resource);
256 if (result)
d550d98d 257 return result;
1da177e4 258
0a613902
KK
259 mutex_lock(&resource->resource_lock);
260 list_for_each_safe(node, next, &resource->reference) {
261 ref = container_of(node, struct acpi_power_reference, node);
262 if (dev->handle == ref->device->handle) {
263 list_del(&ref->node);
264 kfree(ref);
265 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n",
266 dev->pnp.bus_id, resource->name));
267 break;
268 }
269 }
1da177e4 270
0a613902
KK
271 if (!list_empty(&resource->reference)) {
272 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n",
273 resource->name));
274 mutex_unlock(&resource->resource_lock);
d550d98d 275 return 0;
1da177e4 276 }
0a613902 277 mutex_unlock(&resource->resource_lock);
1da177e4
LT
278
279 if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
280 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
0a613902 281 resource->name));
d550d98d 282 return 0;
1da177e4
LT
283 }
284
5fbc19ef 285 status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
1da177e4 286 if (ACPI_FAILURE(status))
d550d98d 287 return -ENODEV;
1da177e4
LT
288
289 result = acpi_power_get_state(resource);
290 if (result)
d550d98d 291 return result;
1da177e4 292 if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
d550d98d 293 return -ENOEXEC;
1da177e4
LT
294
295 /* Update the power resource's _device_ power state */
786f18c6 296 resource->device->power.state = ACPI_STATE_D3;
1da177e4
LT
297
298 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
4be44fcd 299 resource->name));
1da177e4 300
d550d98d 301 return 0;
1da177e4
LT
302}
303
304/*
305 * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229):
306 * 1. Power on the power resources required for the wakeup device
307 * 2. Enable _PSW (power state wake) for the device if present
308 */
4be44fcd 309int acpi_enable_wakeup_device_power(struct acpi_device *dev)
1da177e4 310{
4be44fcd
LB
311 union acpi_object arg = { ACPI_TYPE_INTEGER };
312 struct acpi_object_list arg_list = { 1, &arg };
313 acpi_status status = AE_OK;
314 int i;
315 int ret = 0;
1da177e4 316
1da177e4 317 if (!dev || !dev->wakeup.flags.valid)
d550d98d 318 return -1;
1da177e4
LT
319
320 arg.integer.value = 1;
321 /* Open power resource */
322 for (i = 0; i < dev->wakeup.resources.count; i++) {
0a613902 323 ret = acpi_power_on(dev->wakeup.resources.handles[i], dev);
1da177e4 324 if (ret) {
6468463a 325 printk(KERN_ERR PREFIX "Transition power state\n");
1da177e4 326 dev->wakeup.flags.valid = 0;
d550d98d 327 return -1;
1da177e4
LT
328 }
329 }
330
331 /* Execute PSW */
332 status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
333 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
6468463a 334 printk(KERN_ERR PREFIX "Evaluate _PSW\n");
1da177e4
LT
335 dev->wakeup.flags.valid = 0;
336 ret = -1;
337 }
338
d550d98d 339 return ret;
1da177e4
LT
340}
341
342/*
343 * Shutdown a wakeup device, counterpart of above method
344 * 1. Disable _PSW (power state wake)
345 * 2. Shutdown down the power resources
346 */
4be44fcd 347int acpi_disable_wakeup_device_power(struct acpi_device *dev)
1da177e4 348{
4be44fcd
LB
349 union acpi_object arg = { ACPI_TYPE_INTEGER };
350 struct acpi_object_list arg_list = { 1, &arg };
351 acpi_status status = AE_OK;
352 int i;
353 int ret = 0;
1da177e4 354
1da177e4
LT
355
356 if (!dev || !dev->wakeup.flags.valid)
d550d98d 357 return -1;
1da177e4 358
4be44fcd 359 arg.integer.value = 0;
1da177e4
LT
360 /* Execute PSW */
361 status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL);
362 if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) {
6468463a 363 printk(KERN_ERR PREFIX "Evaluate _PSW\n");
1da177e4 364 dev->wakeup.flags.valid = 0;
d550d98d 365 return -1;
1da177e4
LT
366 }
367
368 /* Close power resource */
369 for (i = 0; i < dev->wakeup.resources.count; i++) {
0a613902 370 ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev);
1da177e4 371 if (ret) {
6468463a 372 printk(KERN_ERR PREFIX "Transition power state\n");
1da177e4 373 dev->wakeup.flags.valid = 0;
d550d98d 374 return -1;
1da177e4
LT
375 }
376 }
377
d550d98d 378 return ret;
1da177e4
LT
379}
380
381/* --------------------------------------------------------------------------
382 Device Power Management
383 -------------------------------------------------------------------------- */
384
4be44fcd 385int acpi_power_get_inferred_state(struct acpi_device *device)
1da177e4 386{
4be44fcd
LB
387 int result = 0;
388 struct acpi_handle_list *list = NULL;
389 int list_state = 0;
390 int i = 0;
1da177e4 391
1da177e4
LT
392
393 if (!device)
d550d98d 394 return -EINVAL;
1da177e4
LT
395
396 device->power.state = ACPI_STATE_UNKNOWN;
397
398 /*
399 * We know a device's inferred power state when all the resources
400 * required for a given D-state are 'on'.
401 */
4be44fcd 402 for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) {
1da177e4
LT
403 list = &device->power.states[i].resources;
404 if (list->count < 1)
405 continue;
406
407 result = acpi_power_get_list_state(list, &list_state);
408 if (result)
d550d98d 409 return result;
1da177e4
LT
410
411 if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
412 device->power.state = i;
d550d98d 413 return 0;
1da177e4
LT
414 }
415 }
416
417 device->power.state = ACPI_STATE_D3;
418
d550d98d 419 return 0;
1da177e4
LT
420}
421
4be44fcd 422int acpi_power_transition(struct acpi_device *device, int state)
1da177e4 423{
4be44fcd
LB
424 int result = 0;
425 struct acpi_handle_list *cl = NULL; /* Current Resources */
426 struct acpi_handle_list *tl = NULL; /* Target Resources */
427 int i = 0;
1da177e4 428
1da177e4
LT
429
430 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
d550d98d 431 return -EINVAL;
1da177e4 432
4be44fcd
LB
433 if ((device->power.state < ACPI_STATE_D0)
434 || (device->power.state > ACPI_STATE_D3))
d550d98d 435 return -ENODEV;
1da177e4
LT
436
437 cl = &device->power.states[device->power.state].resources;
438 tl = &device->power.states[state].resources;
439
440 device->power.state = ACPI_STATE_UNKNOWN;
441
442 if (!cl->count && !tl->count) {
443 result = -ENODEV;
444 goto end;
445 }
446
447 /* TBD: Resources must be ordered. */
448
449 /*
450 * First we reference all power resources required in the target list
451 * (e.g. so the device doesn't lose power while transitioning).
452 */
4be44fcd 453 for (i = 0; i < tl->count; i++) {
0a613902 454 result = acpi_power_on(tl->handles[i], device);
1da177e4
LT
455 if (result)
456 goto end;
457 }
458
459 /*
460 * Then we dereference all power resources used in the current list.
461 */
4be44fcd 462 for (i = 0; i < cl->count; i++) {
0a613902 463 result = acpi_power_off_device(cl->handles[i], device);
1da177e4
LT
464 if (result)
465 goto end;
466 }
467
468 /* We shouldn't change the state till all above operations succeed */
469 device->power.state = state;
4be44fcd 470 end:
1da177e4 471 if (result)
cece9296
LB
472 printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d\n",
473 device->pnp.bus_id, state);
1da177e4 474
d550d98d 475 return result;
1da177e4
LT
476}
477
1da177e4
LT
478/* --------------------------------------------------------------------------
479 FS Interface (/proc)
480 -------------------------------------------------------------------------- */
481
4be44fcd 482static struct proc_dir_entry *acpi_power_dir;
1da177e4
LT
483
484static int acpi_power_seq_show(struct seq_file *seq, void *offset)
485{
0a613902
KK
486 int count = 0;
487 int result = 0;
1da177e4 488 struct acpi_power_resource *resource = NULL;
0a613902
KK
489 struct list_head *node, *next;
490 struct acpi_power_reference *ref;
1da177e4 491
1da177e4 492
50dd0969 493 resource = seq->private;
1da177e4
LT
494
495 if (!resource)
496 goto end;
497
0a613902
KK
498 result = acpi_power_get_state(resource);
499 if (result)
500 goto end;
501
1da177e4
LT
502 seq_puts(seq, "state: ");
503 switch (resource->state) {
504 case ACPI_POWER_RESOURCE_STATE_ON:
505 seq_puts(seq, "on\n");
506 break;
507 case ACPI_POWER_RESOURCE_STATE_OFF:
508 seq_puts(seq, "off\n");
509 break;
510 default:
511 seq_puts(seq, "unknown\n");
512 break;
513 }
514
0a613902
KK
515 mutex_lock(&resource->resource_lock);
516 list_for_each_safe(node, next, &resource->reference) {
517 ref = container_of(node, struct acpi_power_reference, node);
518 count++;
519 }
520 mutex_unlock(&resource->resource_lock);
521
1da177e4 522 seq_printf(seq, "system level: S%d\n"
4be44fcd
LB
523 "order: %d\n"
524 "reference count: %d\n",
525 resource->system_level,
0a613902 526 resource->order, count);
1da177e4 527
4be44fcd 528 end:
d550d98d 529 return 0;
1da177e4
LT
530}
531
532static int acpi_power_open_fs(struct inode *inode, struct file *file)
533{
534 return single_open(file, acpi_power_seq_show, PDE(inode)->data);
535}
536
4be44fcd 537static int acpi_power_add_fs(struct acpi_device *device)
1da177e4 538{
4be44fcd 539 struct proc_dir_entry *entry = NULL;
1da177e4 540
1da177e4
LT
541
542 if (!device)
d550d98d 543 return -EINVAL;
1da177e4
LT
544
545 if (!acpi_device_dir(device)) {
546 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
4be44fcd 547 acpi_power_dir);
1da177e4 548 if (!acpi_device_dir(device))
d550d98d 549 return -ENODEV;
1da177e4
LT
550 }
551
552 /* 'status' [R] */
553 entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
4be44fcd 554 S_IRUGO, acpi_device_dir(device));
1da177e4 555 if (!entry)
d550d98d 556 return -EIO;
1da177e4
LT
557 else {
558 entry->proc_fops = &acpi_power_fops;
559 entry->data = acpi_driver_data(device);
560 }
561
d550d98d 562 return 0;
1da177e4
LT
563}
564
4be44fcd 565static int acpi_power_remove_fs(struct acpi_device *device)
1da177e4 566{
1da177e4
LT
567
568 if (acpi_device_dir(device)) {
569 remove_proc_entry(ACPI_POWER_FILE_STATUS,
570 acpi_device_dir(device));
571 remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
572 acpi_device_dir(device) = NULL;
573 }
574
d550d98d 575 return 0;
1da177e4
LT
576}
577
1da177e4
LT
578/* --------------------------------------------------------------------------
579 Driver Interface
580 -------------------------------------------------------------------------- */
581
4be44fcd 582static int acpi_power_add(struct acpi_device *device)
1da177e4 583{
4be44fcd
LB
584 int result = 0;
585 acpi_status status = AE_OK;
1da177e4 586 struct acpi_power_resource *resource = NULL;
4be44fcd
LB
587 union acpi_object acpi_object;
588 struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object };
1da177e4 589
1da177e4
LT
590
591 if (!device)
d550d98d 592 return -EINVAL;
1da177e4 593
36bcbec7 594 resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
1da177e4 595 if (!resource)
d550d98d 596 return -ENOMEM;
1da177e4 597
41598572 598 resource->device = device;
0a613902
KK
599 mutex_init(&resource->resource_lock);
600 INIT_LIST_HEAD(&resource->reference);
1da177e4
LT
601 strcpy(resource->name, device->pnp.bus_id);
602 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
603 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
604 acpi_driver_data(device) = resource;
605
606 /* Evalute the object to get the system level and resource order. */
5fbc19ef 607 status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer);
1da177e4
LT
608 if (ACPI_FAILURE(status)) {
609 result = -ENODEV;
610 goto end;
611 }
612 resource->system_level = acpi_object.power_resource.system_level;
613 resource->order = acpi_object.power_resource.resource_order;
614
615 result = acpi_power_get_state(resource);
616 if (result)
617 goto end;
618
619 switch (resource->state) {
620 case ACPI_POWER_RESOURCE_STATE_ON:
621 device->power.state = ACPI_STATE_D0;
622 break;
623 case ACPI_POWER_RESOURCE_STATE_OFF:
624 device->power.state = ACPI_STATE_D3;
625 break;
626 default:
627 device->power.state = ACPI_STATE_UNKNOWN;
628 break;
629 }
630
631 result = acpi_power_add_fs(device);
632 if (result)
633 goto end;
4be44fcd 634
1da177e4 635 printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
4be44fcd 636 acpi_device_bid(device), resource->state ? "on" : "off");
1da177e4 637
4be44fcd 638 end:
1da177e4
LT
639 if (result)
640 kfree(resource);
4be44fcd 641
d550d98d 642 return result;
1da177e4
LT
643}
644
4be44fcd 645static int acpi_power_remove(struct acpi_device *device, int type)
1da177e4
LT
646{
647 struct acpi_power_resource *resource = NULL;
0a613902 648 struct list_head *node, *next;
1da177e4 649
1da177e4
LT
650
651 if (!device || !acpi_driver_data(device))
d550d98d 652 return -EINVAL;
1da177e4 653
50dd0969 654 resource = acpi_driver_data(device);
1da177e4
LT
655
656 acpi_power_remove_fs(device);
657
0a613902
KK
658 mutex_lock(&resource->resource_lock);
659 list_for_each_safe(node, next, &resource->reference) {
660 struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
661 list_del(&ref->node);
662 kfree(ref);
663 }
664 mutex_unlock(&resource->resource_lock);
665
1da177e4
LT
666 kfree(resource);
667
d550d98d 668 return 0;
1da177e4
LT
669}
670
0a613902
KK
671static int acpi_power_resume(struct acpi_device *device, int state)
672{
673 int result = 0;
674 struct acpi_power_resource *resource = NULL;
675 struct acpi_power_reference *ref;
676
677 if (!device || !acpi_driver_data(device))
678 return -EINVAL;
679
680 resource = (struct acpi_power_resource *)acpi_driver_data(device);
681
682 result = acpi_power_get_state(resource);
683 if (result)
684 return result;
685
686 mutex_lock(&resource->resource_lock);
687 if ((resource->state == ACPI_POWER_RESOURCE_STATE_ON) &&
688 list_empty(&resource->reference)) {
689 mutex_unlock(&resource->resource_lock);
690 result = acpi_power_off_device(device->handle, NULL);
691 return result;
692 }
693
694 if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) &&
695 !list_empty(&resource->reference)) {
696 ref = container_of(resource->reference.next, struct acpi_power_reference, node);
697 mutex_unlock(&resource->resource_lock);
698 result = acpi_power_on(device->handle, ref->device);
699 return result;
700 }
701
702 mutex_unlock(&resource->resource_lock);
703 return 0;
704}
705
4be44fcd 706static int __init acpi_power_init(void)
1da177e4 707{
4be44fcd 708 int result = 0;
1da177e4 709
1da177e4
LT
710
711 if (acpi_disabled)
d550d98d 712 return 0;
1da177e4
LT
713
714 INIT_LIST_HEAD(&acpi_power_resource_list);
715
716 acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
717 if (!acpi_power_dir)
d550d98d 718 return -ENODEV;
1da177e4
LT
719
720 result = acpi_bus_register_driver(&acpi_power_driver);
721 if (result < 0) {
722 remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
d550d98d 723 return -ENODEV;
1da177e4
LT
724 }
725
d550d98d 726 return 0;
1da177e4
LT
727}
728
729subsys_initcall(acpi_power_init);