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