:pserver:cvsanon@mok.lvcm.com:/CVS/ReactOS reactos
[reactos.git] / drivers / bus / acpi / ospm / busmgr / bmpm.c
1 /*****************************************************************************
2  *
3  * Module Name: bmpm.c
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 #include <acpi.h>
28 #include "bm.h"
29 #include "bmpower.h"
30
31
32 #define _COMPONENT              ACPI_POWER_CONTROL
33         MODULE_NAME             ("bmpm")
34
35
36 /****************************************************************************
37  *                             Internal Functions
38  ****************************************************************************/
39
40 /****************************************************************************
41  *
42  * FUNCTION:    bm_get_inferred_power_state
43  *
44  * PARAMETERS:  <TBD>
45  *
46  * RETURN:      <TBD>
47  *
48  * DESCRIPTION: <TBD>
49  *
50  ****************************************************************************/
51
52 ACPI_STATUS
53 bm_get_inferred_power_state (
54         BM_DEVICE               *device)
55 {
56         ACPI_STATUS             status = AE_OK;
57         BM_HANDLE_LIST          pr_list;
58         BM_POWER_STATE          list_state = ACPI_STATE_UNKNOWN;
59         char                    object_name[5] = {'_','P','R','0','\0'};
60         u32                     i = 0;
61
62         FUNCTION_TRACE("bm_get_inferred_power_state");
63
64         if (!device) {
65                 return_ACPI_STATUS(AE_BAD_PARAMETER);
66         }
67
68         MEMSET(&pr_list, 0, sizeof(BM_HANDLE_LIST));
69
70         device->power.state = ACPI_STATE_D3;
71
72         /*
73          * Calculate Power State:
74          * ----------------------
75          * Try to infer the devices's power state by checking the state of 
76          * the devices's power resources.  We start by evaluating _PR0 
77          * (resource requirements at D0) and work through _PR1 and _PR2.  
78          * We know the current devices power state when all resources (for 
79          * a give Dx state) are ON.  If no power resources are on then the 
80          * device is assumed to be off (D3).
81          */
82         for (i=ACPI_STATE_D0; i<ACPI_STATE_D3; i++) {
83
84                 status = bm_evaluate_reference_list(device->acpi_handle, 
85                         object_name, &pr_list);
86
87                 if (ACPI_SUCCESS(status)) {
88
89                         status = bm_pr_list_get_state(&pr_list, 
90                                 &list_state);
91
92                         if (ACPI_SUCCESS(status)) {
93
94                                 if (list_state == ACPI_STATE_D0) {
95                                         device->power.state = i;
96                                         break;
97                                 }
98                         }
99                 }
100         }
101
102         return_ACPI_STATUS(AE_OK);
103 }
104
105
106 /****************************************************************************
107  *                             External Functions
108  ****************************************************************************/
109
110 /****************************************************************************
111  *
112  * FUNCTION:    bm_get_power_state
113  *
114  * PARAMETERS:  <TBD>
115  *
116  * RETURN:      <TBD>
117  *
118  * DESCRIPTION: <TBD>
119  *
120  ****************************************************************************/
121
122 ACPI_STATUS
123 bm_get_power_state (
124         BM_NODE                 *node)
125 {
126         ACPI_STATUS             status = AE_OK;
127         BM_DEVICE               *device = NULL;
128
129         FUNCTION_TRACE("bm_get_power_state");
130
131         if (!node) {
132                 return_ACPI_STATUS(AE_BAD_PARAMETER);
133         }
134
135         device = &(node->device);
136
137         device->power.state = ACPI_STATE_UNKNOWN;
138
139         if (device->flags & BM_FLAGS_POWER_STATE) {
140                 status = bm_evaluate_simple_integer(device->acpi_handle, 
141                         "_PSC", &(device->power.state));
142         }
143         else {
144                 status = bm_get_inferred_power_state(device);
145         }
146
147         if (ACPI_SUCCESS(status)) {
148                 DEBUG_PRINT(ACPI_INFO, ("Device [0x%02x] is at power state [D%d].\n", device->handle, device->power.state));
149         }
150         else {
151                 DEBUG_PRINT(ACPI_INFO, ("Error getting power state for device [0x%02x]\n", device->handle));
152         }
153
154         return_ACPI_STATUS(status);
155 }
156
157
158 /****************************************************************************
159  *
160  * FUNCTION:    bm_set_power_state
161  *
162  * PARAMETERS:  <TBD>
163  *
164  * RETURN:      <TBD>
165  *
166  * DESCRIPTION: <TBD>
167  *
168  ****************************************************************************/
169
170 ACPI_STATUS
171 bm_set_power_state (
172         BM_NODE                 *node,
173         BM_POWER_STATE          state)
174 {
175         ACPI_STATUS             status = AE_OK;
176         BM_DEVICE               *device = NULL;
177         BM_DEVICE               *parent_device = NULL;
178         BM_HANDLE_LIST          current_list;
179         BM_HANDLE_LIST          target_list;
180         char                    object_name[5] = {'_','P','R','0','\0'};
181
182         FUNCTION_TRACE("bm_set_power_state");
183
184         if (!node || !node->parent || (state > ACPI_STATE_D3)) {
185                 return_ACPI_STATUS(AE_BAD_PARAMETER);
186         }
187
188         MEMSET(&current_list, 0, sizeof(BM_HANDLE_LIST));
189         MEMSET(&target_list, 0, sizeof(BM_HANDLE_LIST));
190
191         device = &(node->device);
192         parent_device = &(node->parent->device);
193
194         /*
195          * Check Parent's Power State:
196          * ---------------------------
197          * Can't be in a higher power state (lower Dx value) than parent.
198          */
199         if (state < parent_device->power.state) {
200                 DEBUG_PRINT(ACPI_WARN, ("Cannot set device [0x%02x] to a higher-powered state than parent_device.\n", device->handle));
201                 return_ACPI_STATUS(AE_ERROR);
202         }
203
204         /*
205          * Get Resources:
206          * --------------
207          * Get the power resources associated with the device's current 
208          * and target power states.
209          */
210         if (device->power.state != ACPI_STATE_UNKNOWN) {
211                 object_name[3] = '0' + device->power.state;
212                 bm_evaluate_reference_list(device->acpi_handle, 
213                         object_name, &current_list);
214         }
215
216         object_name[3] = '0' + state;
217         bm_evaluate_reference_list(device->acpi_handle, object_name, 
218                 &target_list);
219
220         /*
221          * Transition Resources:
222          * ---------------------
223          * Transition all power resources referenced by this device to 
224          * the correct power state (taking into consideration sequencing 
225          * and dependencies to other devices).
226          */
227         if (current_list.count || target_list.count) {
228                 status = bm_pr_list_transition(&current_list, &target_list);
229         }
230         if (ACPI_FAILURE(status)) {
231                 return_ACPI_STATUS(status);
232         }
233
234         /*
235          * Execute _PSx:
236          * -------------
237          * Execute the _PSx method corresponding to the target Dx state, 
238          * if it exists.
239          */
240         object_name[2] = 'S';
241         object_name[3] = '0' + state;
242         bm_evaluate_object(device->acpi_handle, object_name, NULL, NULL);
243
244         if (ACPI_SUCCESS(status)) {
245                 DEBUG_PRINT(ACPI_INFO, ("Device [0x%02x] is now at [D%d].\n", device->handle, state));
246                 device->power.state = state;
247         }
248
249         return_ACPI_STATUS(status);
250 }
251
252
253 /****************************************************************************
254  *
255  * FUNCTION:    bm_get_pm_capabilities
256  *
257  * PARAMETERS:  <TBD>
258  *
259  * RETURN:      <TBD>
260  *
261  * DESCRIPTION: <TBD>
262  *
263  ****************************************************************************/
264
265 ACPI_STATUS
266 bm_get_pm_capabilities (
267         BM_NODE                 *node)
268 {
269         ACPI_STATUS             status = AE_OK;
270         BM_DEVICE               *device = NULL;
271         BM_DEVICE               *parent_device = NULL;
272         ACPI_HANDLE             acpi_handle = NULL;
273         BM_POWER_STATE          dx_supported = ACPI_STATE_UNKNOWN;
274         char                    object_name[5] = {'_','S','0','D','\0'};
275         u32                     i = 0;
276
277         FUNCTION_TRACE("bm_get_pm_capabilities");
278
279         if (!node || !node->parent) {
280                 return_ACPI_STATUS(AE_BAD_PARAMETER);
281         }
282
283         device = &(node->device);
284         parent_device = &(node->parent->device);
285
286         /*
287          * Power Management Flags:
288          * -----------------------
289          */
290         if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PSC", 
291                 &acpi_handle))) {
292                 device->power.flags |= BM_FLAGS_POWER_STATE;
293         }
294
295         if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_IRC", 
296                 &acpi_handle))) {
297                 device->power.flags |= BM_FLAGS_INRUSH_CURRENT;
298         }
299
300         if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PRW", 
301                 &acpi_handle))) {
302                 device->power.flags |= BM_FLAGS_WAKE_CAPABLE;
303         }
304
305         /*
306          * Device Power State:
307          * -------------------
308          * Note that we can't get the device's power state until we've
309          * initialized all power resources, so for now we just set to
310          * unknown.
311          */
312         device->power.state = ACPI_STATE_UNKNOWN;
313
314         /*
315          * Dx Supported in S0:
316          * -------------------
317          * Figure out which Dx states are supported by this device for the
318          * S0 (working) state.  Note that D0 and D3 are required (assumed).
319          */
320         device->power.dx_supported[ACPI_STATE_S0] = BM_FLAGS_D0_SUPPORT | 
321                 BM_FLAGS_D3_SUPPORT;
322
323         if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR1", 
324                 &acpi_handle))) || 
325                 (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS1", 
326                 &acpi_handle)))) {
327                 device->power.dx_supported[ACPI_STATE_S0] |= 
328                         BM_FLAGS_D1_SUPPORT;
329         }
330
331         if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR2", 
332                 &acpi_handle))) || 
333                 (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS2", 
334                 &acpi_handle)))) {
335                 device->power.dx_supported[ACPI_STATE_S0] |= 
336                         BM_FLAGS_D2_SUPPORT;
337         }
338
339         /*
340          * Dx Supported in S1-S5:
341          * ----------------------
342          * Figure out which Dx states are supported by this device for
343          * all other Sx states.
344          */
345         for (i = ACPI_STATE_S1; i <= ACPI_STATE_S5; i++) {
346
347                 /*
348                  * D3 support is assumed (off is always possible!).
349                  */
350                 device->power.dx_supported[i] = BM_FLAGS_D3_SUPPORT;
351
352                 /*
353                  * Evalute _SxD:
354                  * -------------
355                  * Which returns the highest (power) Dx state supported in 
356                  * this system (Sx) state.  We convert this value to a bit 
357                  * mask of supported states (conceptually simpler).
358                  */
359                 status = bm_evaluate_simple_integer(device->acpi_handle, 
360                         object_name, &dx_supported);
361                 if (ACPI_SUCCESS(status)) {
362                         switch (dx_supported) {
363                         case 0:
364                                 device->power.dx_supported[i] |= 
365                                         BM_FLAGS_D0_SUPPORT;
366                                 /* fall through */
367                         case 1:
368                                 device->power.dx_supported[i] |= 
369                                         BM_FLAGS_D1_SUPPORT;
370                                 /* fall through */
371                         case 2:
372                                 device->power.dx_supported[i] |= 
373                                         BM_FLAGS_D2_SUPPORT;
374                                 /* fall through */
375                         case 3:
376                                 device->power.dx_supported[i] |= 
377                                         BM_FLAGS_D3_SUPPORT;
378                                 break;
379                         }
380
381                         /*
382                          * Validate:
383                          * ---------
384                          * Mask of any states that _Sx_d falsely advertises 
385                          * (e.g.claims D1 support but neither _PR2 or _PS2 
386                          * exist).  In other words, S1-S5 can't offer a Dx 
387                          * state that isn't supported by S0.
388                          */
389                         device->power.dx_supported[i] &= 
390                                 device->power.dx_supported[ACPI_STATE_S0];
391                 }
392
393                 object_name[2]++;
394         }
395
396         return_ACPI_STATUS(status);
397 }