File: /usr/src/linux/drivers/acpi/ospm/busmgr/bmpm.c
1 /*****************************************************************************
2 *
3 * Module Name: bmpm.c
4 * $Revision: 14 $
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_BUS
33 MODULE_NAME ("bmpm")
34
35
36 /****************************************************************************
37 * Internal Functions
38 ****************************************************************************/
39
40 /****************************************************************************
41 *
42 * FUNCTION: bm_get_inferred_power_state
43 *
44 * PARAMETERS:
45 *
46 * RETURN:
47 *
48 * DESCRIPTION:
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 object_name[3] = '0' + i;
85
86 status = bm_evaluate_reference_list(device->acpi_handle,
87 object_name, &pr_list);
88
89 if (ACPI_SUCCESS(status)) {
90
91 status = bm_pr_list_get_state(&pr_list, &list_state);
92
93 if (ACPI_SUCCESS(status)) {
94
95 if (list_state == ACPI_STATE_D0) {
96 device->power.state = i;
97 break;
98 }
99 }
100 }
101 }
102
103 return_ACPI_STATUS(AE_OK);
104 }
105
106
107 /****************************************************************************
108 * External Functions
109 ****************************************************************************/
110
111 /****************************************************************************
112 *
113 * FUNCTION: bm_get_power_state
114 *
115 * PARAMETERS:
116 *
117 * RETURN:
118 *
119 * DESCRIPTION:
120 *
121 ****************************************************************************/
122
123 acpi_status
124 bm_get_power_state (
125 BM_NODE *node)
126 {
127 acpi_status status = AE_OK;
128 BM_DEVICE *device = NULL;
129
130 FUNCTION_TRACE("bm_get_power_state");
131
132 if (!node || !node->parent) {
133 return_ACPI_STATUS(AE_BAD_PARAMETER);
134 }
135
136 device = &(node->device);
137
138 device->power.state = ACPI_STATE_UNKNOWN;
139
140 /*
141 * Power Control?
142 * --------------
143 * If this device isn't directly power manageable (e.g. doesn't
144 * include _PR0/_PS0) then there's nothing to do (state is static).
145 */
146 if (!BM_IS_POWER_CONTROL(device)) {
147 return_ACPI_STATUS(AE_OK);
148 }
149
150 /*
151 * Parent Present?
152 * ---------------
153 * Make sure the parent is present before mucking with the child.
154 */
155 if (!BM_NODE_PRESENT(node->parent)) {
156 return_ACPI_STATUS(AE_NOT_EXIST);
157 }
158
159 /*
160 * Get Power State:
161 * ----------------
162 * Either directly (via _PSC) or inferred (via power resource
163 * dependencies).
164 */
165 if (BM_IS_POWER_STATE(device)) {
166 status = bm_evaluate_simple_integer(device->acpi_handle,
167 "_PSC", &(device->power.state));
168 }
169 else {
170 status = bm_get_inferred_power_state(device);
171 }
172
173 if (ACPI_SUCCESS(status)) {
174 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device [%02x] is at power state [D%d].\n", device->handle, device->power.state));
175 }
176 else {
177 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Error getting power state for device [%02x]\n", device->handle));
178 }
179
180 return_ACPI_STATUS(status);
181 }
182
183
184 /****************************************************************************
185 *
186 * FUNCTION: bm_set_power_state
187 *
188 * PARAMETERS:
189 *
190 * RETURN:
191 *
192 * DESCRIPTION:
193 *
194 ****************************************************************************/
195
196 acpi_status
197 bm_set_power_state (
198 BM_NODE *node,
199 BM_POWER_STATE state)
200 {
201 acpi_status status = AE_OK;
202 BM_DEVICE *device = NULL;
203 BM_DEVICE *parent_device = NULL;
204 BM_HANDLE_LIST current_list;
205 BM_HANDLE_LIST target_list;
206 char object_name[5] = {'_','P','R','0','\0'};
207
208 FUNCTION_TRACE("bm_set_power_state");
209
210 if (!node || !node->parent || (state > ACPI_STATE_D3)) {
211 return_ACPI_STATUS(AE_BAD_PARAMETER);
212 }
213
214 MEMSET(¤t_list, 0, sizeof(BM_HANDLE_LIST));
215 MEMSET(&target_list, 0, sizeof(BM_HANDLE_LIST));
216
217 device = &(node->device);
218 parent_device = &(node->parent->device);
219
220 /*
221 * Power Control?
222 * --------------
223 * If this device isn't directly power manageable (e.g. doesn't
224 * include _PR0/_PS0) then return an error (can't set state).
225 */
226 if (!BM_IS_POWER_CONTROL(device)) {
227 return_ACPI_STATUS(AE_ERROR);
228 }
229
230 /*
231 * Parent Present?
232 * ---------------
233 * Make sure the parent is present before mucking with the child.
234 */
235 if (!BM_NODE_PRESENT(node->parent)) {
236 return_ACPI_STATUS(AE_NOT_EXIST);
237 }
238
239 /*
240 * Check Parent's Power State:
241 * ---------------------------
242 * Can't be in a higher power state (lower Dx value) than parent.
243 */
244 if (state < parent_device->power.state) {
245 ACPI_DEBUG_PRINT ((ACPI_DB_WARN, "Cannot set device [%02x] to a higher-powered state than parent_device.\n", device->handle));
246 return_ACPI_STATUS(AE_ERROR);
247 }
248
249 /*
250 * Get Resources:
251 * --------------
252 * Get the power resources associated with the device's current
253 * and target power states.
254 */
255 if (device->power.state != ACPI_STATE_UNKNOWN) {
256 object_name[3] = '0' + device->power.state;
257 bm_evaluate_reference_list(device->acpi_handle,
258 object_name, ¤t_list);
259 }
260
261 object_name[3] = '0' + state;
262 bm_evaluate_reference_list(device->acpi_handle, object_name,
263 &target_list);
264
265 /*
266 * Transition Resources:
267 * ---------------------
268 * Transition all power resources referenced by this device to
269 * the correct power state (taking into consideration sequencing
270 * and dependencies to other devices).
271 */
272 if (current_list.count || target_list.count) {
273 status = bm_pr_list_transition(¤t_list, &target_list);
274 }
275 if (ACPI_FAILURE(status)) {
276 return_ACPI_STATUS(status);
277 }
278
279 /*
280 * Execute _PSx:
281 * -------------
282 * Execute the _PSx method corresponding to the target Dx state,
283 * if it exists.
284 */
285 object_name[2] = 'S';
286 object_name[3] = '0' + state;
287 bm_evaluate_object(device->acpi_handle, object_name, NULL, NULL);
288
289 if (ACPI_SUCCESS(status)) {
290 ACPI_DEBUG_PRINT ((ACPI_DB_INFO, "Device [%02x] is now at [D%d].\n", device->handle, state));
291 device->power.state = state;
292 }
293
294 return_ACPI_STATUS(status);
295 }
296
297
298 /****************************************************************************
299 *
300 * FUNCTION: bm_get_pm_capabilities
301 *
302 * PARAMETERS:
303 *
304 * RETURN:
305 *
306 * DESCRIPTION:
307 *
308 ****************************************************************************/
309
310 acpi_status
311 bm_get_pm_capabilities (
312 BM_NODE *node)
313 {
314 acpi_status status = AE_OK;
315 BM_DEVICE *device = NULL;
316 BM_DEVICE *parent_device = NULL;
317 acpi_handle acpi_handle = NULL;
318 BM_POWER_STATE dx_supported = ACPI_STATE_UNKNOWN;
319 char object_name[5] = {'_','S','0','D','\0'};
320 u32 i = 0;
321
322 FUNCTION_TRACE("bm_get_pm_capabilities");
323
324 if (!node || !node->parent) {
325 return_ACPI_STATUS(AE_BAD_PARAMETER);
326 }
327
328 device = &(node->device);
329 parent_device = &(node->parent->device);
330
331 /*
332 * Power Management Flags:
333 * -----------------------
334 */
335 if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PSC",
336 &acpi_handle))) {
337 device->power.flags |= BM_FLAGS_POWER_STATE;
338 }
339
340 if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_IRC",
341 &acpi_handle))) {
342 device->power.flags |= BM_FLAGS_INRUSH_CURRENT;
343 }
344
345 if (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PRW",
346 &acpi_handle))) {
347 device->power.flags |= BM_FLAGS_WAKE_CAPABLE;
348 }
349
350 /*
351 * Device Power State:
352 * -------------------
353 * Note that we can't get the device's power state until we've
354 * initialized all power resources, so for now we just set to
355 * unknown.
356 */
357 device->power.state = ACPI_STATE_UNKNOWN;
358
359 /*
360 * Dx Supported in S0:
361 * -------------------
362 * Figure out which Dx states are supported by this device for the
363 * S0 (working) state. Note that D0 and D3 are required (assumed).
364 */
365 device->power.dx_supported[ACPI_STATE_S0] = BM_FLAGS_D0_SUPPORT |
366 BM_FLAGS_D3_SUPPORT;
367
368 if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR1",
369 &acpi_handle))) ||
370 (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS1",
371 &acpi_handle)))) {
372 device->power.dx_supported[ACPI_STATE_S0] |=
373 BM_FLAGS_D1_SUPPORT;
374 }
375
376 if ((ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PR2",
377 &acpi_handle))) ||
378 (ACPI_SUCCESS(acpi_get_handle(device->acpi_handle, "_PS2",
379 &acpi_handle)))) {
380 device->power.dx_supported[ACPI_STATE_S0] |=
381 BM_FLAGS_D2_SUPPORT;
382 }
383
384 /*
385 * Dx Supported in S1-S5:
386 * ----------------------
387 * Figure out which Dx states are supported by this device for
388 * all other Sx states.
389 */
390 for (i = ACPI_STATE_S1; i <= ACPI_STATE_S5; i++) {
391
392 /*
393 * D3 support is assumed (off is always possible!).
394 */
395 device->power.dx_supported[i] = BM_FLAGS_D3_SUPPORT;
396
397 /*
398 * Evalute _Sx_d:
399 * -------------
400 * Which returns the highest (power) Dx state supported in
401 * this system (Sx) state. We convert this value to a bit
402 * mask of supported states (conceptually simpler).
403 */
404 status = bm_evaluate_simple_integer(device->acpi_handle,
405 object_name, &dx_supported);
406 if (ACPI_SUCCESS(status)) {
407 switch (dx_supported) {
408 case 0:
409 device->power.dx_supported[i] |=
410 BM_FLAGS_D0_SUPPORT;
411 /* fall through */
412 case 1:
413 device->power.dx_supported[i] |=
414 BM_FLAGS_D1_SUPPORT;
415 /* fall through */
416 case 2:
417 device->power.dx_supported[i] |=
418 BM_FLAGS_D2_SUPPORT;
419 /* fall through */
420 case 3:
421 device->power.dx_supported[i] |=
422 BM_FLAGS_D3_SUPPORT;
423 break;
424 }
425
426 /*
427 * Validate:
428 * ---------
429 * Mask of any states that _Sx_d falsely advertises
430 * (e.g.claims D1 support but neither _PR2 or _PS2
431 * exist). In other words, S1-S5 can't offer a Dx
432 * state that isn't supported by S0.
433 */
434 device->power.dx_supported[i] &=
435 device->power.dx_supported[ACPI_STATE_S0];
436 }
437
438 object_name[2]++;
439 }
440
441 return_ACPI_STATUS(AE_OK);
442 }
443