[PATCH] More 3.4 compilation fixes
[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         if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3))
341                 return_VALUE(-ENODEV);
342
343         cl = &device->power.states[device->power.state].resources;
344         tl = &device->power.states[state].resources;
345
346         device->power.state = ACPI_STATE_UNKNOWN;
347
348         if (!cl->count && !tl->count) {
349                 result = -ENODEV;
350                 goto end;
351         }
352
353         /* TBD: Resources must be ordered. */
354
355         /*
356          * First we reference all power resources required in the target list
357          * (e.g. so the device doesn't lose power while transitioning).
358          */
359         for (i=0; i<tl->count; i++) {
360                 result = acpi_power_on(tl->handles[i]);
361                 if (result)
362                         goto end;
363         }
364
365         /*
366          * Then we dereference all power resources used in the current list.
367          */
368         for (i=0; i<cl->count; i++) {
369                 result = acpi_power_off_device(cl->handles[i]);
370                 if (result)
371                         goto end;
372         }
373
374         /* We shouldn't change the state till all above operations succeed */
375         device->power.state = state;
376 end:
377         if (result)
378                 ACPI_DEBUG_PRINT((ACPI_DB_WARN, 
379                         "Error transitioning device [%s] to D%d\n",
380                         device->pnp.bus_id, state));
381
382         return_VALUE(result);
383 }
384
385
386 /* --------------------------------------------------------------------------
387                               FS Interface (/proc)
388    -------------------------------------------------------------------------- */
389
390 struct proc_dir_entry           *acpi_power_dir;
391
392 static int acpi_power_seq_show(struct seq_file *seq, void *offset)
393 {
394         struct acpi_power_resource *resource = NULL;
395
396         ACPI_FUNCTION_TRACE("acpi_power_seq_show");
397
398         resource = (struct acpi_power_resource *)seq->private;
399
400         if (!resource)
401                 goto end;
402
403         seq_puts(seq, "state:                   ");
404         switch (resource->state) {
405         case ACPI_POWER_RESOURCE_STATE_ON:
406                 seq_puts(seq, "on\n");
407                 break;
408         case ACPI_POWER_RESOURCE_STATE_OFF:
409                 seq_puts(seq, "off\n");
410                 break;
411         default:
412                 seq_puts(seq, "unknown\n");
413                 break;
414         }
415
416         seq_printf(seq, "system level:            S%d\n"
417                         "order:                   %d\n"
418                         "reference count:         %d\n",
419                         resource->system_level,
420                         resource->order,
421                         resource->references);
422
423 end:
424         return 0;
425 }
426
427 static int acpi_power_open_fs(struct inode *inode, struct file *file)
428 {
429         return single_open(file, acpi_power_seq_show, PDE(inode)->data);
430 }
431
432 static int
433 acpi_power_add_fs (
434         struct acpi_device      *device)
435 {
436         struct proc_dir_entry   *entry = NULL;
437
438         ACPI_FUNCTION_TRACE("acpi_power_add_fs");
439
440         if (!device)
441                 return_VALUE(-EINVAL);
442
443         if (!acpi_device_dir(device)) {
444                 acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device),
445                         acpi_power_dir);
446                 if (!acpi_device_dir(device))
447                         return_VALUE(-ENODEV);
448         }
449
450         /* 'status' [R] */
451         entry = create_proc_entry(ACPI_POWER_FILE_STATUS,
452                 S_IRUGO, acpi_device_dir(device));
453         if (!entry)
454                 ACPI_DEBUG_PRINT((ACPI_DB_ERROR,
455                         "Unable to create '%s' fs entry\n",
456                         ACPI_POWER_FILE_STATUS));
457         else {
458                 entry->proc_fops = &acpi_power_fops;
459                 entry->data = acpi_driver_data(device);
460         }
461
462         return_VALUE(0);
463 }
464
465
466 static int
467 acpi_power_remove_fs (
468         struct acpi_device      *device)
469 {
470         ACPI_FUNCTION_TRACE("acpi_power_remove_fs");
471
472         if (acpi_device_dir(device)) {
473                 remove_proc_entry(acpi_device_bid(device), acpi_power_dir);
474                 acpi_device_dir(device) = NULL;
475         }
476
477         return_VALUE(0);
478 }
479
480
481 /* --------------------------------------------------------------------------
482                                 Driver Interface
483    -------------------------------------------------------------------------- */
484
485 int
486 acpi_power_add (
487         struct acpi_device      *device)
488 {
489         int                     result = 0;
490         acpi_status             status = AE_OK;
491         struct acpi_power_resource *resource = NULL;
492         union acpi_object       acpi_object;
493         struct acpi_buffer      buffer = {sizeof(acpi_object), &acpi_object};
494
495         ACPI_FUNCTION_TRACE("acpi_power_add");
496
497         if (!device)
498                 return_VALUE(-EINVAL);
499
500         resource = kmalloc(sizeof(struct acpi_power_resource), GFP_KERNEL);
501         if (!resource)
502                 return_VALUE(-ENOMEM);
503         memset(resource, 0, sizeof(struct acpi_power_resource));
504
505         resource->handle = device->handle;
506         strcpy(resource->name, device->pnp.bus_id);
507         strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
508         strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
509         acpi_driver_data(device) = resource;
510
511         /* Evalute the object to get the system level and resource order. */
512         status = acpi_evaluate_object(resource->handle, NULL, NULL, &buffer);
513         if (ACPI_FAILURE(status)) {
514                 result = -ENODEV;
515                 goto end;
516         }
517         resource->system_level = acpi_object.power_resource.system_level;
518         resource->order = acpi_object.power_resource.resource_order;
519
520         result = acpi_power_get_state(resource);
521         if (result)
522                 goto end;
523
524         switch (resource->state) {
525         case ACPI_POWER_RESOURCE_STATE_ON:
526                 device->power.state = ACPI_STATE_D0;
527                 break;
528         case ACPI_POWER_RESOURCE_STATE_OFF:
529                 device->power.state = ACPI_STATE_D3;
530                 break;
531         default:
532                 device->power.state = ACPI_STATE_UNKNOWN;
533                 break;
534         }
535
536         result = acpi_power_add_fs(device);
537         if (result)
538                 goto end;
539         
540         printk(KERN_INFO PREFIX "%s [%s] (%s)\n", acpi_device_name(device),
541                 acpi_device_bid(device), resource->state?"on":"off");
542
543 end:
544         if (result)
545                 kfree(resource);
546         
547         return_VALUE(result);
548 }
549
550
551 int
552 acpi_power_remove (
553         struct acpi_device      *device,
554         int                     type)
555 {
556         struct acpi_power_resource *resource = NULL;
557
558         ACPI_FUNCTION_TRACE("acpi_power_remove");
559
560         if (!device || !acpi_driver_data(device))
561                 return_VALUE(-EINVAL);
562
563         resource = (struct acpi_power_resource *) acpi_driver_data(device);
564
565         acpi_power_remove_fs(device);
566
567         kfree(resource);
568
569         return_VALUE(0);
570 }
571
572
573 static int __init acpi_power_init (void)
574 {
575         int                     result = 0;
576
577         ACPI_FUNCTION_TRACE("acpi_power_init");
578
579         if (acpi_disabled)
580                 return_VALUE(0);
581
582         INIT_LIST_HEAD(&acpi_power_resource_list);
583
584         acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir);
585         if (!acpi_power_dir)
586                 return_VALUE(-ENODEV);
587
588         result = acpi_bus_register_driver(&acpi_power_driver);
589         if (result < 0) {
590                 remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir);
591                 return_VALUE(-ENODEV);
592         }
593
594         return_VALUE(0);
595 }
596
597 subsys_initcall(acpi_power_init);
598