:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / bus / acpi / ospm / busmgr / bmpower.c
1 /****************************************************************************
2  *
3  * Module Name: bmpower.c - Driver for ACPI Power Resource 'devices'
4  *   $Revision$
5  *
6  ****************************************************************************/
7
8 /*
9  *  Copyright (C) 2000, 2001 Andrew Grover
10  *
11  *  This program is free software; you can redistribute it and/or modify
12  *  it under the terms of the GNU General Public License as published by
13  *  the Free Software Foundation; either version 2 of the License, or
14  *  (at your option) any later version.
15  *
16  *  This program is distributed in the hope that it will be useful,
17  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
18  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  *  GNU General Public License for more details.
20  *
21  *  You should have received a copy of the GNU General Public License
22  *  along with this program; if not, write to the Free Software
23  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
24  */
25
26 /*
27  * TODO:
28  * -----
29  *      1. Sequencing of power resource list transitions.
30  *      2. Global serialization of power resource transtions (see ACPI 
31  *         spec section 7.1.2/7.1.3).
32  *      3. Better error handling.
33  */
34
35
36 #include <acpi.h>
37 #include "bm.h"
38 #include "bmpower.h"
39
40
41 #define _COMPONENT              ACPI_POWER_CONTROL
42         MODULE_NAME             ("bmpower")
43
44
45 /****************************************************************************
46  *                             Function Prototypes
47  ****************************************************************************/
48
49 ACPI_STATUS
50 bm_pr_notify (
51         BM_NOTIFY               notify_type,
52         BM_HANDLE               device_handle,
53         void                    **context);
54         
55 ACPI_STATUS
56 bm_pr_request (
57         BM_REQUEST              *request,
58         void                    *context);
59
60         
61 /****************************************************************************
62  *                             Internal Functions
63  ****************************************************************************/
64
65 /****************************************************************************
66  *
67  * FUNCTION:    bm_pr_print
68  *
69  * PARAMETERS:  <TBD>
70  *
71  * RETURN:      <TBD>
72  *
73  * DESCRIPTION: <TBD>
74  *
75  ****************************************************************************/
76
77 ACPI_STATUS
78 bm_pr_print (
79         BM_POWER_RESOURCE       *pr)
80 {
81         ACPI_BUFFER             buffer;
82
83         if (!pr) {
84                 return(AE_BAD_PARAMETER);
85         }
86
87         buffer.length = 256;
88         buffer.pointer = acpi_os_callocate(buffer.length);
89         if (!buffer.pointer) {
90                 return(AE_NO_MEMORY);
91         }
92
93         acpi_get_name(pr->acpi_handle, ACPI_FULL_PATHNAME, &buffer);
94
95         acpi_os_printf("Power Resource: found\n");
96
97         DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
98         DEBUG_PRINT(ACPI_INFO, ("PowerResource[0x%02X]|[0x%08X] %s\n", pr->device_handle, pr->acpi_handle, buffer.pointer));
99         DEBUG_PRINT(ACPI_INFO, ("  system_level[S%d] resource_order[%d]\n", pr->system_level, pr->resource_order));
100         DEBUG_PRINT(ACPI_INFO, ("  state[D%d] reference_count[%d]\n", pr->state, pr->reference_count));
101         DEBUG_PRINT(ACPI_INFO, ("+------------------------------------------------------------\n"));
102
103         acpi_os_free(buffer.pointer);
104
105         return(AE_OK);
106 }
107
108
109 /****************************************************************************
110  *
111  * FUNCTION:    bm_pr_get_state
112  *
113  * PARAMETERS:  <TBD>
114  *
115  * RETURN:      <TBD>
116  *
117  * DESCRIPTION: <TBD>
118  *
119  ****************************************************************************/
120
121 ACPI_STATUS
122 bm_pr_get_state (
123         BM_POWER_RESOURCE       *pr)
124 {
125         ACPI_STATUS             status = AE_OK;
126         BM_DEVICE_STATUS        device_status = BM_STATUS_UNKNOWN;
127
128         FUNCTION_TRACE("bm_pr_get_state");
129
130         if (!pr) {
131                 return_ACPI_STATUS(AE_BAD_PARAMETER);
132         }
133
134         pr->state = ACPI_STATE_UNKNOWN;
135
136         /* 
137          * Evaluate _STA:
138          * --------------
139          * Evalute _STA to determine whether the power resource is ON or OFF.
140          * Note that if the power resource isn't present we'll get AE_OK but
141          * an unknown status.
142          */
143         status = bm_get_device_status(pr->device_handle, &device_status);
144         if (ACPI_FAILURE(status)) {
145                 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
146                 return_ACPI_STATUS(status);
147         }
148         if (device_status == BM_STATUS_UNKNOWN) {
149                 DEBUG_PRINT(ACPI_ERROR, ("Error reading status for power resource [0x%02x].\n", pr->device_handle));
150                 return_ACPI_STATUS(AE_NOT_EXIST);
151         }
152
153         /*
154          * Mask off all bits but the first as some systems return non-standard 
155          * values (e.g. 0x51).
156          */
157         switch (device_status & 0x01) {
158         case 0:
159                 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is OFF.\n", pr->device_handle));
160                 pr->state = ACPI_STATE_D3;
161                 break;
162         case 1:
163                 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02x] is ON.\n", pr->device_handle));
164                 pr->state = ACPI_STATE_D0;
165                 break;
166         }
167
168         return_ACPI_STATUS(status);
169 }
170
171
172 /****************************************************************************
173  *
174  * FUNCTION:    bm_pr_set_state
175  *
176  * PARAMETERS:  <TBD>
177  *
178  * RETURN:      <TBD>
179  *
180  * DESCRIPTION: <TBD>
181  *
182  ****************************************************************************/
183
184 ACPI_STATUS
185 bm_pr_set_state (
186         BM_POWER_RESOURCE       *pr,
187         BM_POWER_STATE          target_state)
188 {
189         ACPI_STATUS             status = AE_OK;
190
191         FUNCTION_TRACE("bm_pr_set_state");
192
193         if (!pr) {
194                 return_ACPI_STATUS(AE_BAD_PARAMETER);
195         }
196
197         status = bm_pr_get_state(pr);
198         if (ACPI_FAILURE(status)) {
199                 return_ACPI_STATUS(status);
200         }
201
202         if (target_state == pr->state) {
203                 DEBUG_PRINT(ACPI_INFO, ("Power resource [0x%02X] already at target power state [D%d].\n", pr->device_handle, pr->state));
204                 return_ACPI_STATUS(AE_OK);
205         }
206
207         switch (target_state) {
208
209         case ACPI_STATE_D0:
210                 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] ON.\n", pr->device_handle));
211                 status = bm_evaluate_object(pr->acpi_handle, "_ON", NULL, NULL);
212                 break;
213
214         case ACPI_STATE_D3:
215                 DEBUG_PRINT(ACPI_INFO, ("Turning power resource [0x%02X] OFF.\n", pr->device_handle));
216                 status = bm_evaluate_object(pr->acpi_handle, "_OFF", NULL, NULL);
217                 break;
218
219         default:
220                 status = AE_BAD_PARAMETER;
221                 break;
222         }
223
224         status = bm_pr_get_state(pr);
225         if (ACPI_FAILURE(status)) {
226                 return_ACPI_STATUS(status);
227         }
228
229         return_ACPI_STATUS(status);
230 }
231
232
233 /****************************************************************************
234  *
235  * FUNCTION:    bm_pr_list_get_state
236  *
237  * PARAMETERS:  <TBD>
238  *
239  * RETURN:      <TBD>
240  *
241  * DESCRIPTION: <TBD>
242  *
243  ****************************************************************************/
244
245 ACPI_STATUS
246 bm_pr_list_get_state (
247         BM_HANDLE_LIST          *pr_list,
248         BM_POWER_STATE          *power_state)
249 {
250         ACPI_STATUS             status = AE_OK;
251         BM_POWER_RESOURCE       *pr = NULL;
252         u32                     i = 0;
253
254         FUNCTION_TRACE("bm_pr_list_get_state");
255
256         if (!pr_list || !power_state) {
257                 return_ACPI_STATUS(AE_BAD_PARAMETER);
258         }
259
260         if (pr_list->count < 1) {
261                 pr->state = ACPI_STATE_UNKNOWN;
262                 return_ACPI_STATUS(AE_ERROR);
263         }
264
265         (*power_state) = ACPI_STATE_D0;
266
267         /*
268          * Calculate Current power_state:
269          * -----------------------------
270          * The current state of a list of power resources is ON if all
271          * power resources are currently in the ON state.  In other words,
272          * if any power resource in the list is OFF then the collection 
273          * isn't fully ON.
274          */
275         for (i = 0; i < pr_list->count; i++) {
276
277                 status = bm_get_device_context(pr_list->handles[i],
278                         (BM_DRIVER_CONTEXT*)(&pr));
279                 if (ACPI_FAILURE(status)) {
280                         DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", pr_list->handles[i]));
281                         (*power_state) = ACPI_STATE_UNKNOWN;
282                         break;
283                 }
284
285                 status = bm_pr_get_state(pr);
286                 if (ACPI_FAILURE(status)) {
287                         (*power_state) = ACPI_STATE_UNKNOWN;
288                         break;
289                 }
290
291                 if (pr->state != ACPI_STATE_D0) {
292                         (*power_state) = pr->state;
293                         break;
294                 }
295         }
296
297         return_ACPI_STATUS(status);
298 }
299
300
301 /****************************************************************************
302  *
303  * FUNCTION:    bm_pr_list_transition
304  *
305  * PARAMETERS:  <TBD>
306  *
307  * RETURN:      <TBD>
308  *
309  * DESCRIPTION: <TBD>
310  *
311  ****************************************************************************/
312
313 ACPI_STATUS
314 bm_pr_list_transition (
315         BM_HANDLE_LIST          *current_list,
316         BM_HANDLE_LIST          *target_list)
317 {
318         ACPI_STATUS             status = AE_OK;
319         BM_POWER_RESOURCE       *pr = NULL;
320         u32                     i = 0;
321
322         FUNCTION_TRACE("bm_pr_list_transition");
323
324         if (!current_list || !target_list) {
325                 return_ACPI_STATUS(AE_BAD_PARAMETER);
326         }
327
328         /*
329          * Reference Target:
330          * -----------------
331          * Reference all resources for the target power state first (so 
332          * the device doesn't get turned off while transitioning).  Power 
333          * resources that aren't on (new reference count of 1) are turned on.
334          */
335         for (i = 0; i < target_list->count; i++) {
336
337                 status = bm_get_device_context(target_list->handles[i], 
338                         (BM_DRIVER_CONTEXT*)(&pr));
339                 if (ACPI_FAILURE(status)) {
340                         DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
341                         continue;
342                 }
343
344                 if (++pr->reference_count == 1) {
345                         /* TODO: Need ordering based upon resource_order */
346                         status = bm_pr_set_state(pr, ACPI_STATE_D0);
347                         if (ACPI_FAILURE(status)) {
348                                 /* TODO: How do we handle this? */
349                                 DEBUG_PRINT(ACPI_WARN, ("Unable to change power state for power resource [0x%02X].\n", target_list->handles[i]));
350                         }
351                 }
352         }
353
354         /*
355          * Dereference Current:
356          * --------------------
357          * Dereference all resources for the current power state.  Power
358          * resources no longer referenced (new reference count of 0) are 
359          * turned off.
360          */
361         for (i = 0; i < current_list->count; i++) {
362
363                 status = bm_get_device_context(current_list->handles[i], 
364                         (BM_DRIVER_CONTEXT*)(&pr));
365                 if (ACPI_FAILURE(status)) {
366                         DEBUG_PRINT(ACPI_WARN, ("Invalid reference to power resource [0x%02X].\n", target_list->handles[i]));
367                         continue;
368                 }
369
370                 if (--pr->reference_count == 0) {
371                         /* TODO: Need ordering based upon resource_order */
372                         status = bm_pr_set_state(pr, ACPI_STATE_D3);
373                         if (ACPI_FAILURE(status)) {
374                                 /* TODO: How do we handle this? */
375                                 DEBUG_PRINT(ACPI_ERROR, ("Unable to change power state for power resource [0x%02X].\n", current_list->handles[i]));
376                         }
377                 }
378         }
379
380         return_ACPI_STATUS(status);
381 }
382
383
384 /****************************************************************************
385  *
386  * FUNCTION:    bm_pr_add_device
387  *
388  * PARAMETERS:  <TBD>
389  *
390  * RETURN:      <TBD>
391  *
392  * DESCRIPTION: <TBD>
393  *
394  ****************************************************************************/
395
396 ACPI_STATUS
397 bm_pr_add_device (
398         BM_HANDLE               device_handle,
399         void                    **context)
400 {
401         ACPI_STATUS             status = AE_OK;
402         BM_POWER_RESOURCE       *pr = NULL;
403         BM_DEVICE               *device = NULL;
404         ACPI_BUFFER             buffer;
405         ACPI_OBJECT             acpi_object;
406
407         FUNCTION_TRACE("bm_pr_add_device");
408
409         DEBUG_PRINT(ACPI_INFO, ("Adding power resource [0x%02X].\n", device_handle));
410
411         if (!context || *context) {
412                 return_ACPI_STATUS(AE_BAD_PARAMETER);
413         }
414
415         buffer.length = sizeof(ACPI_OBJECT);
416         buffer.pointer = &acpi_object;
417
418         /*
419          * Get information on this device.
420          */
421         status = bm_get_device_info(device_handle, &device);
422         if (ACPI_FAILURE(status)) {
423                 return_ACPI_STATUS(status);
424         }
425
426         /*
427          * Allocate a new BM_POWER_RESOURCE structure.
428          */
429         pr = acpi_os_callocate(sizeof(BM_POWER_RESOURCE));
430         if (!pr) {
431                 return_ACPI_STATUS(AE_NO_MEMORY);
432         }
433
434         pr->device_handle = device->handle;
435         pr->acpi_handle = device->acpi_handle;
436
437         /* 
438          * Get information on this power resource.
439          */
440         status = acpi_evaluate_object(pr->acpi_handle, NULL, NULL, &buffer);
441         if (ACPI_FAILURE(status)) {
442                 goto end;
443         }
444
445         pr->system_level = acpi_object.power_resource.system_level;
446         pr->resource_order = acpi_object.power_resource.resource_order;
447         pr->state = ACPI_STATE_UNKNOWN;
448         pr->reference_count = 0;
449
450         /*
451          * Get the power resource's current state (ON|OFF).
452          */
453         status = bm_pr_get_state(pr);
454
455 end:
456         if (ACPI_FAILURE(status)) {
457                 acpi_os_free(pr);
458         }
459         else {
460                 *context = pr;
461                 bm_pr_print(pr);
462         }
463
464         return_ACPI_STATUS(status);
465 }
466
467
468 /****************************************************************************
469  *
470  * FUNCTION:    bm_pr_remove_device
471  *
472  * PARAMETERS:  <TBD>
473  *
474  * RETURN:      <TBD>
475  *
476  * DESCRIPTION: <TBD>
477  *
478  ****************************************************************************/
479
480 ACPI_STATUS
481 bm_pr_remove_device (
482         void                    **context)
483 {
484         ACPI_STATUS             status = AE_OK;
485         BM_POWER_RESOURCE       *pr = NULL;
486
487         FUNCTION_TRACE("bm_pr_remove_device");
488
489         if (!context || !*context) {
490                 return_ACPI_STATUS(AE_BAD_PARAMETER);
491         }
492
493         pr = (BM_POWER_RESOURCE*)*context;
494
495         DEBUG_PRINT(ACPI_INFO, ("Removing power resource [0x%02X].\n", pr->device_handle));
496
497         acpi_os_free(pr);
498
499         return_ACPI_STATUS(status);
500 }
501
502
503 /****************************************************************************
504  *                             External Functions
505  ****************************************************************************/
506
507 /****************************************************************************
508  *
509  * FUNCTION:    bm_pr_initialize
510  *
511  * PARAMETERS:  <none>
512  *
513  * RETURN:
514  *
515  * DESCRIPTION: <TBD>
516  *
517  ****************************************************************************/
518
519 ACPI_STATUS
520 bm_pr_initialize (void)
521 {
522         ACPI_STATUS             status = AE_OK;
523         BM_DEVICE_ID            criteria;
524         BM_DRIVER               driver;
525
526         FUNCTION_TRACE("bm_pr_initialize");
527
528         MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
529         MEMSET(&driver, 0, sizeof(BM_DRIVER));
530
531         criteria.type = BM_TYPE_POWER_RESOURCE;
532
533         driver.notify = &bm_pr_notify;
534         driver.request = &bm_pr_request;
535
536         status = bm_register_driver(&criteria, &driver);
537
538         return_ACPI_STATUS(status);
539 }
540
541
542 /****************************************************************************
543  *
544  * FUNCTION:    bm_pr_terminate
545  *
546  * PARAMETERS:  <TBD>
547  *
548  * RETURN:      <TBD>
549  *
550  * DESCRIPTION: <TBD>
551  *
552  ****************************************************************************/
553
554 ACPI_STATUS
555 bm_pr_terminate (void)
556 {
557         ACPI_STATUS             status = AE_OK;
558         BM_DEVICE_ID            criteria;
559         BM_DRIVER               driver;
560
561         FUNCTION_TRACE("bm_pr_terminate");
562
563         MEMSET(&criteria, 0, sizeof(BM_DEVICE_ID));
564         MEMSET(&driver, 0, sizeof(BM_DRIVER));
565
566         criteria.type = BM_TYPE_POWER_RESOURCE;
567
568         driver.notify = &bm_pr_notify;
569         driver.request = &bm_pr_request;
570
571         status = bm_unregister_driver(&criteria, &driver);
572
573         return_ACPI_STATUS(status);
574 }
575
576
577 /****************************************************************************
578  *
579  * FUNCTION:    bm_pr_notify
580  *
581  * PARAMETERS:  <TBD>
582  *
583  * RETURN:      <TBD>
584  *
585  * DESCRIPTION: <TBD>
586  *
587  ****************************************************************************/
588
589 ACPI_STATUS
590 bm_pr_notify (
591         BM_NOTIFY               notify_type,
592         BM_HANDLE               device_handle,
593         void                    **context)
594 {
595         ACPI_STATUS             status = AE_OK;
596
597         FUNCTION_TRACE("bm_pr_notify");
598
599         switch (notify_type) {
600
601         case BM_NOTIFY_DEVICE_ADDED:
602                 status = bm_pr_add_device(device_handle, context);
603                 break;
604
605         case BM_NOTIFY_DEVICE_REMOVED:
606                 status = bm_pr_remove_device(context);
607                 break;
608
609         default:
610                 status = AE_SUPPORT;
611                 break;
612         }
613
614         return_ACPI_STATUS(status);
615 }
616
617
618 /****************************************************************************
619  *
620  * FUNCTION:    bm_pr_request
621  *
622  * PARAMETERS:  <TBD>
623  *
624  * RETURN:      <TBD>
625  *
626  * DESCRIPTION: <TBD>
627  *
628  ****************************************************************************/
629
630 ACPI_STATUS
631 bm_pr_request (
632         BM_REQUEST              *request,
633         void                    *context)
634 {
635         ACPI_STATUS             status = AE_OK;
636         BM_POWER_RESOURCE       *pr = NULL;
637
638         FUNCTION_TRACE("bm_pr_request");
639
640         /*
641          * Must have a valid request structure and context.
642          */
643         if (!request || !context) {
644                 return_ACPI_STATUS(AE_BAD_PARAMETER);
645         }
646
647         /*
648          * context contains information specific to this power resource.
649          */
650         pr = (BM_POWER_RESOURCE*)context;
651
652         /*
653          * Handle request:
654          * ---------------
655          */
656         switch (request->command) {
657
658         default:
659                 status = AE_SUPPORT;
660                 break;
661         }
662
663         request->status = status;
664
665         return_ACPI_STATUS(status);
666 }
667
668
669