acpi: make source files look in <acpi/...> for headers. (other top-level files)
[linux-flexiantxendom0-3.2.10.git] / drivers / acpi / power.c
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 #include <linux/kernel.h>
27 #include <linux/module.h>
28 #include <linux/init.h>
29 #include <linux/types.h>
30 #include <linux/proc_fs.h>
31 #include <linux/seq_file.h>
32 #include <acpi/acpi_bus.h>
33 #include <acpi/acpi_drivers.h>
34
35
36 #define _COMPONENT              ACPI_POWER_COMPONENT
37 ACPI_MODULE_NAME                ("acpi_power")
38
39 #define ACPI_POWER_COMPONENT            0x00800000
40 #define ACPI_POWER_CLASS                "power_resource"
41 #define ACPI_POWER_DRIVER_NAME          "ACPI Power Resource Driver"
42 #define ACPI_POWER_DEVICE_NAME          "Power Resource"
43 #define ACPI_POWER_FILE_INFO            "info"
44 #define ACPI_POWER_FILE_STATUS          "state"
45 #define ACPI_POWER_RESOURCE_STATE_OFF   0x00
46 #define ACPI_POWER_RESOURCE_STATE_ON    0x01
47 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF
48
49 int acpi_power_add (struct acpi_device *device);
50 int acpi_power_remove (struct acpi_device *device, int type);
51 static int acpi_power_open_fs(struct inode *inode, struct file *file);
52
53 static struct acpi_driver acpi_power_driver = {
54         .name =         ACPI_POWER_DRIVER_NAME,
55         .class =        ACPI_POWER_CLASS,
56         .ids =          ACPI_POWER_HID,
57         .ops =          {
58                                 .add =          acpi_power_add,
59                                 .remove =       acpi_power_remove,
60                         },
61 };
62
63 struct acpi_power_resource
64 {
65         acpi_handle             handle;
66         acpi_bus_id             name;
67         u32                     system_level;
68         u32                     order;
69         int                     state;
70         int                     references;
71 };
72
73 static struct list_head         acpi_power_resource_list;
74
75 static struct file_operations acpi_power_fops = {
76         .open           = acpi_power_open_fs,
77         .read           = seq_read,
78         .llseek         = seq_lseek,
79         .release        = single_release,
80 };
81
82 /* --------------------------------------------------------------------------
83                              Power Resource Management
84    -------------------------------------------------------------------------- */
85
86 static int
87 acpi_power_get_context (
88         acpi_handle             handle,
89         struct acpi_power_resource **resource)
90 {
91         int                     result = 0;
92         struct acpi_device      *device = NULL;
93
94         ACPI_FUNCTION_TRACE("acpi_power_get_context");
95
96         if (!resource)
97                 return_VALUE(-ENODEV);
98
99         result = acpi_bus_get_device(handle, &device);
100         if (result) {
101                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, "Error getting context [%p]\n",
102                         handle));
103                 return_VALUE(result);
104         }
105
106         *resource = (struct acpi_power_resource *) acpi_driver_data(device);
107         if (!resource)
108                 return_VALUE(-ENODEV);
109
110         return_VALUE(0);
111 }
112
113
114 static int
115 acpi_power_get_state (
116         struct acpi_power_resource *resource)
117 {
118         acpi_status             status = AE_OK;
119         unsigned long           sta = 0;
120
121         ACPI_FUNCTION_TRACE("acpi_power_get_state");
122
123         if (!resource)
124                 return_VALUE(-EINVAL);
125
126         status = acpi_evaluate_integer(resource->handle, "_STA", NULL, &sta);
127         if (ACPI_FAILURE(status))
128                 return_VALUE(-ENODEV);
129
130         if (sta & 0x01)
131                 resource->state = ACPI_POWER_RESOURCE_STATE_ON;
132         else
133                 resource->state = ACPI_POWER_RESOURCE_STATE_OFF;
134
135         ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s\n",
136                 resource->name, resource->state?"on":"off"));
137
138         return_VALUE(0);
139 }
140
141
142 static int
143 acpi_power_get_list_state (
144         struct acpi_handle_list *list,
145         int                     *state)
146 {
147         int                     result = 0;
148         struct acpi_power_resource *resource = NULL;
149         u32                     i = 0;
150
151         ACPI_FUNCTION_TRACE("acpi_power_get_list_state");
152
153         if (!list || !state)
154                 return_VALUE(-EINVAL);
155
156         /* The state of the list is 'on' IFF all resources are 'on'. */
157
158         for (i=0; i<list->count; i++) {
159                 result = acpi_power_get_context(list->handles[i], &resource);
160                 if (result)
161                         return_VALUE(result);
162                 result = acpi_power_get_state(resource);
163                 if (result)
164                         return_VALUE(result);
165
166                 *state = resource->state;
167
168                 if (*state != ACPI_POWER_RESOURCE_STATE_ON)
169                         break;
170         }
171
172         ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s\n",
173                 *state?"on":"off"));
174
175         return_VALUE(result);
176 }
177
178
179 static int
180 acpi_power_on (
181         acpi_handle             handle)
182 {
183         int                     result = 0;
184         acpi_status             status = AE_OK;
185         struct acpi_device      *device = NULL;
186         struct acpi_power_resource *resource = NULL;
187
188         ACPI_FUNCTION_TRACE("acpi_power_on");
189
190         result = acpi_power_get_context(handle, &resource);
191         if (result)
192                 return_VALUE(result);
193
194         resource->references++;
195
196         if ((resource->references > 1) 
197                 || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) {
198                 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n",
199                         resource->name));
200                 return_VALUE(0);
201         }
202
203         status = acpi_evaluate_object(resource->handle, "_ON", NULL, NULL);
204         if (ACPI_FAILURE(status))
205                 return_VALUE(-ENODEV);
206
207         result = acpi_power_get_state(resource);
208         if (result)
209                 return_VALUE(result);
210         if (resource->state != ACPI_POWER_RESOURCE_STATE_ON)
211                 return_VALUE(-ENOEXEC);
212
213         /* Update the power resource's _device_ power state */
214         result = acpi_bus_get_device(resource->handle, &device);
215         if (result)
216                 return_VALUE(result);
217         device->power.state = ACPI_STATE_D0;
218
219         ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
220                 resource->name));
221
222         return_VALUE(0);
223 }
224
225
226 static int
227 acpi_power_off_device (
228         acpi_handle             handle)
229 {
230         int                     result = 0;
231         acpi_status             status = AE_OK;
232         struct acpi_device      *device = NULL;
233         struct acpi_power_resource *resource = NULL;
234
235         ACPI_FUNCTION_TRACE("acpi_power_off_device");
236
237         result = acpi_power_get_context(handle, &resource);
238         if (result)
239                 return_VALUE(result);
240
241         if (resource->references)
242                 resource->references--;
243
244         if (resource->references) {
245                 ACPI_DEBUG_PRINT((ACPI_DB_INFO, 
246                         "Resource [%s] is still in use, dereferencing\n",
247                         device->pnp.bus_id));
248                 return_VALUE(0);
249         }
250
251         if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) {
252                 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n",
253                         device->pnp.bus_id));
254                 return_VALUE(0);
255         }
256
257         status = acpi_evaluate_object(resource->handle, "_OFF", NULL, NULL);
258         if (ACPI_FAILURE(status))
259                 return_VALUE(-ENODEV);
260
261         result = acpi_power_get_state(resource);
262         if (result)
263                 return_VALUE(result);
264         if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF)
265                 return_VALUE(-ENOEXEC);
266
267         /* Update the power resource's _device_ power state */
268         result = acpi_bus_get_device(resource->handle, &device);
269         if (result)
270                 return_VALUE(result);
271         device->power.state = ACPI_STATE_D3;
272
273         ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n",
274                 resource->name));
275
276         return_VALUE(0);
277 }
278
279
280 /* --------------------------------------------------------------------------
281                              Device Power Management
282    -------------------------------------------------------------------------- */
283
284 int
285 acpi_power_get_inferred_state (
286         struct acpi_device      *device)
287 {
288         int                     result = 0;
289         struct acpi_handle_list *list = NULL;
290         int                     list_state = 0;
291         int                     i = 0;
292
293         ACPI_FUNCTION_TRACE("acpi_power_get_inferred_state");
294
295         if (!device)
296                 return_VALUE(-EINVAL);
297
298         device->power.state = ACPI_STATE_UNKNOWN;
299
300         /*
301          * We know a device's inferred power state when all the resources
302          * required for a given D-state are 'on'.
303          */
304         for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
305                 list = &device->power.states[i].resources;
306                 if (list->count < 1)
307                         continue;
308
309                 result = acpi_power_get_list_state(list, &list_state);
310                 if (result)
311                         return_VALUE(result);
312
313                 if (list_state == ACPI_POWER_RESOURCE_STATE_ON) {
314                         device->power.state = i;
315                         return_VALUE(0);
316                 }
317         }
318
319         device->power.state = ACPI_STATE_D3;
320
321         return_VALUE(0);
322 }
323
324
325 int
326 acpi_power_transition (
327         struct acpi_device      *device,
328         int                     state)
329 {
330         int                     result = 0;
331         struct acpi_handle_list *cl = NULL;     /* Current Resources */
332         struct acpi_handle_list *tl = NULL;     /* Target Resources */
333         int                     i = 0;
334
335         ACPI_FUNCTION_TRACE("acpi_power_transition");
336
337         if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3))
338                 return_VALUE(-EINVAL);
339
340         cl = &device->power.states[device->power.state].resources;
341         tl = &device->power.states[state].resources;
342
343         device->power.state = ACPI_STATE_UNKNOWN;
344
345         if (!cl->count && !tl->count) {
346                 result = -ENODEV;
347                 goto end;
348         }
349
350         /* TBD: Resources must be ordered. */
351
352         /*
353          * First we reference all power resources required in the target list
354          * (e.g. so the device doesn't loose power while transitioning).
355          */
356         for (i=0; i<tl->count; i++) {
357                 result = acpi_power_on(tl->handles[i]);
358                 if (result)
359                         goto end;
360         }
361
362         device->power.state = state;
363
364         /*
365          * Then we dereference all power resources used in the current list.
366          */
367         for (i=0; i<cl->count; i++) {
368                 result = acpi_power_off_device(cl->handles[i]);
369                 if (result)
370                         goto end;
371         }
372
373 end:
374         if (result)
375                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 
376                         "Error transitioning device [%s] to D%d\n",
377                         device->pnp.bus_id, state));
378
379         return_VALUE(result);
380 }
381
382
383 /* --------------------------------------------------------------------------
384                               FS Interface (/proc)
385    -------------------------------------------------------------------------- */
386
387 struct proc_dir_entry           *acpi_power_dir = NULL;
388
389 static int acpi_power_seq_show(struct seq_file *seq, void *offset)
390 {
391         struct acpi_power_resource *resource = NULL;
392
393         ACPI_FUNCTION_TRACE("acpi_power_seq_show");
394
395         resource = (struct acpi_power_resource *)seq->private;
396
397         if (!resource)
398                 goto end;
399
400         seq_puts(seq, "state:                   ");
401         switch (resource->state) {
402         case ACPI_POWER_RESOURCE_STATE_ON:
403                 seq_puts(seq, "on\n");
404                 break;
405         case ACPI_POWER_RESOURCE_STATE_OFF:
406                 seq_puts(seq, "off\n");
407                 break;
408         default:
409                 seq_puts(seq, "unknown\n");
410                 break;
411         }
412
413         seq_printf(seq, "system level:            S%d\n"
414                         "order:                   %d\n"
415                         "reference count:         %d\n",
416                         resource->system_level,
417                         resource->order,
418                         resource->references);
419
420 end:
421         return 0;
422 }
423
424 static int acpi_power_open_fs(struct inode *inode, struct file *file)
425 {
426         return single_open(file, acpi_power_seq_show, PDE(inode)->data);
427 }
428
429 static int
430 acpi_power_add_fs (
431         struct acpi_device      *device)
432 {
433         struct proc_dir_entry   *entry = NULL;
434
435         ACPI_FUNCTION_TRACE("acpi_power_add_fs");
436
437         if (!device)
438                 return_VALUE(-EINVAL);
439
440         if (!acpi_device_dir(device)) {
441                 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
442                         acpi_power_dir);
443                 if (!acpi_device_dir(device))
444                         return_VALUE(-ENODEV);
445         }
446
447         /* 'status' [R] */
448         entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
449                 S_IRUGO, acpi_device_dir(device));
450         if (!entry)
451                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
452                         "Unable to create '%s' fs entry\n",
453                         ACPI_POWER_FILE_STATUS));
454         else {
455                 entry->proc_fops = &acpi_power_fops;
456                 entry->data = acpi_driver_data(device);
457         }
458
459         return_VALUE(0);
460 }
461
462
463 static int
464 acpi_power_remove_fs (
465         struct acpi_device      *device)
466 {
467         ACPI_FUNCTION_TRACE("acpi_power_remove_fs");
468
469         if (acpi_device_dir(device)) {
470                 remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
471                 acpi_device_dir(device) = NULL;
472         }
473
474         return_VALUE(0);
475 }
476
477
478 /* --------------------------------------------------------------------------
479                                 Driver Interface
480    -------------------------------------------------------------------------- */
481
482 int
483 acpi_power_add (
484         struct acpi_device      *device)
485 {
486         int                     result = 0;
487         acpi_status             status = AE_OK;
488         struct acpi_power_resource *resource = NULL;
489         union acpi_object       acpi_object;
490         struct acpi_buffer      buffer = {sizeof(acpi_object), &acpi_object};
491
492         ACPI_FUNCTION_TRACE("acpi_power_add");
493
494         if (!device)
495                 return_VALUE(-EINVAL);
496
497         resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
498         if (!resource)
499                 return_VALUE(-ENOMEM);
500         memset(resource, 0, sizeof(struct acpi_power_resource));
501
502         resource->handle = device->handle;
503         sprintf(resource->name, "%s", device->pnp.bus_id);
504         sprintf(acpi_device_name(device), "%s", ACPI_POWER_DEVICE_NAME);
505         sprintf(acpi_device_class(device), "%s", ACPI_POWER_CLASS);
506         acpi_driver_data(device) = resource;
507
508         /* Evalute the object to get the system level and resource order. */
509         status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer);
510         if (ACPI_FAILURE(status)) {
511                 result = -ENODEV;
512                 goto end;
513         }
514         resource->system_level = acpi_object.power_resource.system_level;
515         resource->order = acpi_object.power_resource.resource_order;
516
517         result = acpi_power_get_state(resource);
518         if (result)
519                 goto end;
520
521         switch (resource->state) {
522         case ACPI_POWER_RESOURCE_STATE_ON:
523                 device->power.state = ACPI_STATE_D0;
524                 break;
525         case ACPI_POWER_RESOURCE_STATE_OFF:
526                 device->power.state = ACPI_STATE_D3;
527                 break;
528         default:
529                 device->power.state = ACPI_STATE_UNKNOWN;
530                 break;
531         }
532
533         result = acpi_power_add_fs(device);
534         if (result)
535                 goto end;
536         
537         printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
538                 acpi_device_bid(device), resource->state?"on":"off");
539
540 end:
541         if (result)
542                 kfree(resource);
543         
544         return_VALUE(result);
545 }
546
547
548 int
549 acpi_power_remove (
550         struct acpi_device      *device,
551         int                     type)
552 {
553         struct acpi_power_resource *resource = NULL;
554
555         ACPI_FUNCTION_TRACE("acpi_power_remove");
556
557         if (!device || !acpi_driver_data(device))
558                 return_VALUE(-EINVAL);
559
560         resource = (struct acpi_power_resource *) acpi_driver_data(device);
561
562         acpi_power_remove_fs(device);
563
564         kfree(resource);
565
566         return_VALUE(0);
567 }
568
569
570 static int __init acpi_power_init (void)
571 {
572         int                     result = 0;
573
574         ACPI_FUNCTION_TRACE("acpi_power_init");
575
576         if (acpi_disabled)
577                 return_VALUE(0);
578
579         INIT_LIST_HEAD(&acpi_power_resource_list);
580
581         acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
582         if (!acpi_power_dir)
583                 return_VALUE(-ENODEV);
584
585         result = acpi_bus_register_driver(&acpi_power_driver);
586         if (result < 0) {
587                 remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
588                 return_VALUE(-ENODEV);
589         }
590
591         return_VALUE(0);
592 }
593
594 subsys_initcall(acpi_power_init);
595