Blame view
drivers/acpi/power.c
18.3 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
/* * acpi_power.c - ACPI Bus Power Management ($Revision: 39 $) * * Copyright (C) 2001, 2002 Andy Grover <andrew.grover@intel.com> * Copyright (C) 2001, 2002 Paul Diefenbaugh <paul.s.diefenbaugh@intel.com> * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ /* * ACPI power-managed devices may be controlled in two ways: * 1. via "Device Specific (D-State) Control" * 2. via "Power Resource Control". * This module is used to manage devices relying on Power Resource Control. * * An ACPI "power resource object" describes a software controllable power * plane, clock plane, or other resource used by a power managed device. * A device may rely on multiple power resources, and a power resource * may be shared by multiple devices. */ #include <linux/kernel.h> #include <linux/module.h> #include <linux/init.h> #include <linux/types.h> #include <linux/proc_fs.h> #include <linux/seq_file.h> #include <acpi/acpi_bus.h> #include <acpi/acpi_drivers.h> |
1da177e4c Linux-2.6.12-rc2 |
46 |
#define _COMPONENT ACPI_POWER_COMPONENT |
f52fd66d2 ACPI: clean up AC... |
47 |
ACPI_MODULE_NAME("power"); |
1da177e4c Linux-2.6.12-rc2 |
48 49 |
#define ACPI_POWER_COMPONENT 0x00800000 #define ACPI_POWER_CLASS "power_resource" |
1da177e4c Linux-2.6.12-rc2 |
50 51 52 53 54 55 |
#define ACPI_POWER_DEVICE_NAME "Power Resource" #define ACPI_POWER_FILE_INFO "info" #define ACPI_POWER_FILE_STATUS "state" #define ACPI_POWER_RESOURCE_STATE_OFF 0x00 #define ACPI_POWER_RESOURCE_STATE_ON 0x01 #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF |
4be44fcd3 [ACPI] Lindent al... |
56 57 |
static int acpi_power_add(struct acpi_device *device); static int acpi_power_remove(struct acpi_device *device, int type); |
e8363f332 ACPI: update acpi... |
58 |
static int acpi_power_resume(struct acpi_device *device); |
1da177e4c Linux-2.6.12-rc2 |
59 60 61 |
static int acpi_power_open_fs(struct inode *inode, struct file *file); static struct acpi_driver acpi_power_driver = { |
c2b6705b7 ACPI: fix acpi_dr... |
62 |
.name = "power", |
4be44fcd3 [ACPI] Lindent al... |
63 64 65 66 67 |
.class = ACPI_POWER_CLASS, .ids = ACPI_POWER_HID, .ops = { .add = acpi_power_add, .remove = acpi_power_remove, |
0a6139027 ACPI: Thermal iss... |
68 |
.resume = acpi_power_resume, |
4be44fcd3 [ACPI] Lindent al... |
69 |
}, |
1da177e4c Linux-2.6.12-rc2 |
70 |
}; |
0a6139027 ACPI: Thermal iss... |
71 72 73 74 |
struct acpi_power_reference { struct list_head node; struct acpi_device *device; }; |
4be44fcd3 [ACPI] Lindent al... |
75 |
struct acpi_power_resource { |
415985728 ACPI: power: add ... |
76 |
struct acpi_device * device; |
4be44fcd3 [ACPI] Lindent al... |
77 78 79 80 |
acpi_bus_id name; u32 system_level; u32 order; int state; |
0a6139027 ACPI: Thermal iss... |
81 82 |
struct mutex resource_lock; struct list_head reference; |
1da177e4c Linux-2.6.12-rc2 |
83 |
}; |
4be44fcd3 [ACPI] Lindent al... |
84 |
static struct list_head acpi_power_resource_list; |
1da177e4c Linux-2.6.12-rc2 |
85 |
|
d75080328 ACPI: add 'const'... |
86 |
static const struct file_operations acpi_power_fops = { |
4be44fcd3 [ACPI] Lindent al... |
87 88 89 90 |
.open = acpi_power_open_fs, .read = seq_read, .llseek = seq_lseek, .release = single_release, |
1da177e4c Linux-2.6.12-rc2 |
91 92 93 94 95 96 97 |
}; /* -------------------------------------------------------------------------- Power Resource Management -------------------------------------------------------------------------- */ static int |
4be44fcd3 [ACPI] Lindent al... |
98 99 |
acpi_power_get_context(acpi_handle handle, struct acpi_power_resource **resource) |
1da177e4c Linux-2.6.12-rc2 |
100 |
{ |
4be44fcd3 [ACPI] Lindent al... |
101 102 |
int result = 0; struct acpi_device *device = NULL; |
1da177e4c Linux-2.6.12-rc2 |
103 |
|
1da177e4c Linux-2.6.12-rc2 |
104 105 |
if (!resource) |
d550d98d3 ACPI: delete trac... |
106 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
107 108 109 |
result = acpi_bus_get_device(handle, &device); if (result) { |
cece92969 ACPI: un-export A... |
110 111 |
printk(KERN_WARNING PREFIX "Getting context [%p] ", handle); |
d550d98d3 ACPI: delete trac... |
112 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
113 |
} |
50dd09697 ACPI: Remove unne... |
114 |
*resource = acpi_driver_data(device); |
1da177e4c Linux-2.6.12-rc2 |
115 |
if (!resource) |
d550d98d3 ACPI: delete trac... |
116 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
117 |
|
d550d98d3 ACPI: delete trac... |
118 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
119 |
} |
4be44fcd3 [ACPI] Lindent al... |
120 |
static int acpi_power_get_state(struct acpi_power_resource *resource) |
1da177e4c Linux-2.6.12-rc2 |
121 |
{ |
4be44fcd3 [ACPI] Lindent al... |
122 123 |
acpi_status status = AE_OK; unsigned long sta = 0; |
1da177e4c Linux-2.6.12-rc2 |
124 |
|
1da177e4c Linux-2.6.12-rc2 |
125 126 |
if (!resource) |
d550d98d3 ACPI: delete trac... |
127 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
128 |
|
5fbc19efd ACPI: power: Use ... |
129 |
status = acpi_evaluate_integer(resource->device->handle, "_STA", NULL, &sta); |
1da177e4c Linux-2.6.12-rc2 |
130 |
if (ACPI_FAILURE(status)) |
d550d98d3 ACPI: delete trac... |
131 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
132 133 134 135 136 137 138 139 |
if (sta & 0x01) resource->state = ACPI_POWER_RESOURCE_STATE_ON; else resource->state = ACPI_POWER_RESOURCE_STATE_OFF; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] is %s ", |
4be44fcd3 [ACPI] Lindent al... |
140 |
resource->name, resource->state ? "on" : "off")); |
1da177e4c Linux-2.6.12-rc2 |
141 |
|
d550d98d3 ACPI: delete trac... |
142 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
143 |
} |
4be44fcd3 [ACPI] Lindent al... |
144 |
static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) |
1da177e4c Linux-2.6.12-rc2 |
145 |
{ |
4be44fcd3 [ACPI] Lindent al... |
146 |
int result = 0; |
1da177e4c Linux-2.6.12-rc2 |
147 |
struct acpi_power_resource *resource = NULL; |
4be44fcd3 [ACPI] Lindent al... |
148 |
u32 i = 0; |
1da177e4c Linux-2.6.12-rc2 |
149 |
|
1da177e4c Linux-2.6.12-rc2 |
150 151 |
if (!list || !state) |
d550d98d3 ACPI: delete trac... |
152 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
153 154 |
/* The state of the list is 'on' IFF all resources are 'on'. */ |
4be44fcd3 [ACPI] Lindent al... |
155 |
for (i = 0; i < list->count; i++) { |
1da177e4c Linux-2.6.12-rc2 |
156 157 |
result = acpi_power_get_context(list->handles[i], &resource); if (result) |
d550d98d3 ACPI: delete trac... |
158 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
159 160 |
result = acpi_power_get_state(resource); if (result) |
d550d98d3 ACPI: delete trac... |
161 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
162 163 164 165 166 167 168 169 170 |
*state = resource->state; if (*state != ACPI_POWER_RESOURCE_STATE_ON) break; } ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource list is %s ", |
4be44fcd3 [ACPI] Lindent al... |
171 |
*state ? "on" : "off")); |
1da177e4c Linux-2.6.12-rc2 |
172 |
|
d550d98d3 ACPI: delete trac... |
173 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
174 |
} |
0a6139027 ACPI: Thermal iss... |
175 |
static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
176 |
{ |
4be44fcd3 [ACPI] Lindent al... |
177 |
int result = 0; |
0a6139027 ACPI: Thermal iss... |
178 |
int found = 0; |
4be44fcd3 [ACPI] Lindent al... |
179 |
acpi_status status = AE_OK; |
1da177e4c Linux-2.6.12-rc2 |
180 |
struct acpi_power_resource *resource = NULL; |
0a6139027 ACPI: Thermal iss... |
181 182 |
struct list_head *node, *next; struct acpi_power_reference *ref; |
1da177e4c Linux-2.6.12-rc2 |
183 |
|
1da177e4c Linux-2.6.12-rc2 |
184 185 186 |
result = acpi_power_get_context(handle, &resource); if (result) |
d550d98d3 ACPI: delete trac... |
187 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
188 |
|
0a6139027 ACPI: Thermal iss... |
189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 |
mutex_lock(&resource->resource_lock); list_for_each_safe(node, next, &resource->reference) { ref = container_of(node, struct acpi_power_reference, node); if (dev->handle == ref->device->handle) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s] ", dev->pnp.bus_id, resource->name)); found = 1; break; } } if (!found) { ref = kmalloc(sizeof (struct acpi_power_reference), irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); if (!ref) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed ")); mutex_unlock(&resource->resource_lock); return -ENOMEM; } list_add_tail(&ref->node, &resource->reference); ref->device = dev; ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references ", dev->pnp.bus_id, resource->name)); } mutex_unlock(&resource->resource_lock); |
1da177e4c Linux-2.6.12-rc2 |
217 |
|
0a6139027 ACPI: Thermal iss... |
218 |
if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) { |
1da177e4c Linux-2.6.12-rc2 |
219 220 |
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on ", |
4be44fcd3 [ACPI] Lindent al... |
221 |
resource->name)); |
d550d98d3 ACPI: delete trac... |
222 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
223 |
} |
5fbc19efd ACPI: power: Use ... |
224 |
status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); |
1da177e4c Linux-2.6.12-rc2 |
225 |
if (ACPI_FAILURE(status)) |
d550d98d3 ACPI: delete trac... |
226 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
227 228 229 |
result = acpi_power_get_state(resource); if (result) |
d550d98d3 ACPI: delete trac... |
230 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
231 |
if (resource->state != ACPI_POWER_RESOURCE_STATE_ON) |
d550d98d3 ACPI: delete trac... |
232 |
return -ENOEXEC; |
1da177e4c Linux-2.6.12-rc2 |
233 234 |
/* Update the power resource's _device_ power state */ |
415985728 ACPI: power: add ... |
235 |
resource->device->power.state = ACPI_STATE_D0; |
1da177e4c Linux-2.6.12-rc2 |
236 237 238 |
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on ", |
4be44fcd3 [ACPI] Lindent al... |
239 |
resource->name)); |
d550d98d3 ACPI: delete trac... |
240 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
241 |
} |
0a6139027 ACPI: Thermal iss... |
242 |
static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
243 |
{ |
4be44fcd3 [ACPI] Lindent al... |
244 245 |
int result = 0; acpi_status status = AE_OK; |
1da177e4c Linux-2.6.12-rc2 |
246 |
struct acpi_power_resource *resource = NULL; |
0a6139027 ACPI: Thermal iss... |
247 248 |
struct list_head *node, *next; struct acpi_power_reference *ref; |
1da177e4c Linux-2.6.12-rc2 |
249 |
|
1da177e4c Linux-2.6.12-rc2 |
250 251 |
result = acpi_power_get_context(handle, &resource); if (result) |
d550d98d3 ACPI: delete trac... |
252 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
253 |
|
0a6139027 ACPI: Thermal iss... |
254 255 256 257 258 259 260 261 262 263 264 265 |
mutex_lock(&resource->resource_lock); list_for_each_safe(node, next, &resource->reference) { ref = container_of(node, struct acpi_power_reference, node); if (dev->handle == ref->device->handle) { list_del(&ref->node); kfree(ref); ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references ", dev->pnp.bus_id, resource->name)); break; } } |
1da177e4c Linux-2.6.12-rc2 |
266 |
|
0a6139027 ACPI: Thermal iss... |
267 268 269 270 271 |
if (!list_empty(&resource->reference)) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use ", resource->name)); mutex_unlock(&resource->resource_lock); |
d550d98d3 ACPI: delete trac... |
272 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
273 |
} |
0a6139027 ACPI: Thermal iss... |
274 |
mutex_unlock(&resource->resource_lock); |
1da177e4c Linux-2.6.12-rc2 |
275 276 277 278 |
if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off ", |
0a6139027 ACPI: Thermal iss... |
279 |
resource->name)); |
d550d98d3 ACPI: delete trac... |
280 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
281 |
} |
5fbc19efd ACPI: power: Use ... |
282 |
status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); |
1da177e4c Linux-2.6.12-rc2 |
283 |
if (ACPI_FAILURE(status)) |
d550d98d3 ACPI: delete trac... |
284 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
285 286 287 |
result = acpi_power_get_state(resource); if (result) |
d550d98d3 ACPI: delete trac... |
288 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
289 |
if (resource->state != ACPI_POWER_RESOURCE_STATE_OFF) |
d550d98d3 ACPI: delete trac... |
290 |
return -ENOEXEC; |
1da177e4c Linux-2.6.12-rc2 |
291 292 |
/* Update the power resource's _device_ power state */ |
786f18c66 ACPI: fix potenti... |
293 |
resource->device->power.state = ACPI_STATE_D3; |
1da177e4c Linux-2.6.12-rc2 |
294 295 296 |
ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off ", |
4be44fcd3 [ACPI] Lindent al... |
297 |
resource->name)); |
1da177e4c Linux-2.6.12-rc2 |
298 |
|
d550d98d3 ACPI: delete trac... |
299 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
300 301 302 303 304 305 306 |
} /* * Prepare a wakeup device, two steps (Ref ACPI 2.0:P229): * 1. Power on the power resources required for the wakeup device * 2. Enable _PSW (power state wake) for the device if present */ |
4be44fcd3 [ACPI] Lindent al... |
307 |
int acpi_enable_wakeup_device_power(struct acpi_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
308 |
{ |
4be44fcd3 [ACPI] Lindent al... |
309 310 311 312 313 |
union acpi_object arg = { ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg }; acpi_status status = AE_OK; int i; int ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
314 |
|
1da177e4c Linux-2.6.12-rc2 |
315 |
if (!dev || !dev->wakeup.flags.valid) |
d550d98d3 ACPI: delete trac... |
316 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
317 318 319 320 |
arg.integer.value = 1; /* Open power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { |
0a6139027 ACPI: Thermal iss... |
321 |
ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); |
1da177e4c Linux-2.6.12-rc2 |
322 |
if (ret) { |
6468463ab ACPI: un-export A... |
323 324 |
printk(KERN_ERR PREFIX "Transition power state "); |
1da177e4c Linux-2.6.12-rc2 |
325 |
dev->wakeup.flags.valid = 0; |
d550d98d3 ACPI: delete trac... |
326 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
327 328 329 330 331 332 |
} } /* Execute PSW */ status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { |
6468463ab ACPI: un-export A... |
333 334 |
printk(KERN_ERR PREFIX "Evaluate _PSW "); |
1da177e4c Linux-2.6.12-rc2 |
335 336 337 |
dev->wakeup.flags.valid = 0; ret = -1; } |
d550d98d3 ACPI: delete trac... |
338 |
return ret; |
1da177e4c Linux-2.6.12-rc2 |
339 340 341 342 343 344 345 |
} /* * Shutdown a wakeup device, counterpart of above method * 1. Disable _PSW (power state wake) * 2. Shutdown down the power resources */ |
4be44fcd3 [ACPI] Lindent al... |
346 |
int acpi_disable_wakeup_device_power(struct acpi_device *dev) |
1da177e4c Linux-2.6.12-rc2 |
347 |
{ |
4be44fcd3 [ACPI] Lindent al... |
348 349 350 351 352 |
union acpi_object arg = { ACPI_TYPE_INTEGER }; struct acpi_object_list arg_list = { 1, &arg }; acpi_status status = AE_OK; int i; int ret = 0; |
1da177e4c Linux-2.6.12-rc2 |
353 |
|
1da177e4c Linux-2.6.12-rc2 |
354 355 |
if (!dev || !dev->wakeup.flags.valid) |
d550d98d3 ACPI: delete trac... |
356 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
357 |
|
4be44fcd3 [ACPI] Lindent al... |
358 |
arg.integer.value = 0; |
1da177e4c Linux-2.6.12-rc2 |
359 360 361 |
/* Execute PSW */ status = acpi_evaluate_object(dev->handle, "_PSW", &arg_list, NULL); if (ACPI_FAILURE(status) && (status != AE_NOT_FOUND)) { |
6468463ab ACPI: un-export A... |
362 363 |
printk(KERN_ERR PREFIX "Evaluate _PSW "); |
1da177e4c Linux-2.6.12-rc2 |
364 |
dev->wakeup.flags.valid = 0; |
d550d98d3 ACPI: delete trac... |
365 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
366 367 368 369 |
} /* Close power resource */ for (i = 0; i < dev->wakeup.resources.count; i++) { |
0a6139027 ACPI: Thermal iss... |
370 |
ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev); |
1da177e4c Linux-2.6.12-rc2 |
371 |
if (ret) { |
6468463ab ACPI: un-export A... |
372 373 |
printk(KERN_ERR PREFIX "Transition power state "); |
1da177e4c Linux-2.6.12-rc2 |
374 |
dev->wakeup.flags.valid = 0; |
d550d98d3 ACPI: delete trac... |
375 |
return -1; |
1da177e4c Linux-2.6.12-rc2 |
376 377 |
} } |
d550d98d3 ACPI: delete trac... |
378 |
return ret; |
1da177e4c Linux-2.6.12-rc2 |
379 380 381 382 383 |
} /* -------------------------------------------------------------------------- Device Power Management -------------------------------------------------------------------------- */ |
4be44fcd3 [ACPI] Lindent al... |
384 |
int acpi_power_get_inferred_state(struct acpi_device *device) |
1da177e4c Linux-2.6.12-rc2 |
385 |
{ |
4be44fcd3 [ACPI] Lindent al... |
386 387 388 389 |
int result = 0; struct acpi_handle_list *list = NULL; int list_state = 0; int i = 0; |
1da177e4c Linux-2.6.12-rc2 |
390 |
|
1da177e4c Linux-2.6.12-rc2 |
391 392 |
if (!device) |
d550d98d3 ACPI: delete trac... |
393 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
394 395 396 397 398 399 400 |
device->power.state = ACPI_STATE_UNKNOWN; /* * We know a device's inferred power state when all the resources * required for a given D-state are 'on'. */ |
4be44fcd3 [ACPI] Lindent al... |
401 |
for (i = ACPI_STATE_D0; i < ACPI_STATE_D3; i++) { |
1da177e4c Linux-2.6.12-rc2 |
402 403 404 405 406 407 |
list = &device->power.states[i].resources; if (list->count < 1) continue; result = acpi_power_get_list_state(list, &list_state); if (result) |
d550d98d3 ACPI: delete trac... |
408 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
409 410 411 |
if (list_state == ACPI_POWER_RESOURCE_STATE_ON) { device->power.state = i; |
d550d98d3 ACPI: delete trac... |
412 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
413 414 415 416 |
} } device->power.state = ACPI_STATE_D3; |
d550d98d3 ACPI: delete trac... |
417 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
418 |
} |
4be44fcd3 [ACPI] Lindent al... |
419 |
int acpi_power_transition(struct acpi_device *device, int state) |
1da177e4c Linux-2.6.12-rc2 |
420 |
{ |
4be44fcd3 [ACPI] Lindent al... |
421 422 423 424 |
int result = 0; struct acpi_handle_list *cl = NULL; /* Current Resources */ struct acpi_handle_list *tl = NULL; /* Target Resources */ int i = 0; |
1da177e4c Linux-2.6.12-rc2 |
425 |
|
1da177e4c Linux-2.6.12-rc2 |
426 427 |
if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3)) |
d550d98d3 ACPI: delete trac... |
428 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
429 |
|
4be44fcd3 [ACPI] Lindent al... |
430 431 |
if ((device->power.state < ACPI_STATE_D0) || (device->power.state > ACPI_STATE_D3)) |
d550d98d3 ACPI: delete trac... |
432 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
433 434 435 |
cl = &device->power.states[device->power.state].resources; tl = &device->power.states[state].resources; |
1da177e4c Linux-2.6.12-rc2 |
436 437 438 439 440 441 442 443 444 445 446 |
if (!cl->count && !tl->count) { result = -ENODEV; goto end; } /* TBD: Resources must be ordered. */ /* * First we reference all power resources required in the target list * (e.g. so the device doesn't lose power while transitioning). */ |
4be44fcd3 [ACPI] Lindent al... |
447 |
for (i = 0; i < tl->count; i++) { |
0a6139027 ACPI: Thermal iss... |
448 |
result = acpi_power_on(tl->handles[i], device); |
1da177e4c Linux-2.6.12-rc2 |
449 450 451 |
if (result) goto end; } |
b1028c545 ACPI: fix fan aft... |
452 453 454 |
if (device->power.state == state) { goto end; } |
1da177e4c Linux-2.6.12-rc2 |
455 456 457 |
/* * Then we dereference all power resources used in the current list. */ |
4be44fcd3 [ACPI] Lindent al... |
458 |
for (i = 0; i < cl->count; i++) { |
0a6139027 ACPI: Thermal iss... |
459 |
result = acpi_power_off_device(cl->handles[i], device); |
1da177e4c Linux-2.6.12-rc2 |
460 461 462 |
if (result) goto end; } |
729257604 ACPI: fix S3 fan ... |
463 464 465 |
end: if (result) { device->power.state = ACPI_STATE_UNKNOWN; |
cece92969 ACPI: un-export A... |
466 467 468 |
printk(KERN_WARNING PREFIX "Transitioning device [%s] to D%d ", device->pnp.bus_id, state); |
729257604 ACPI: fix S3 fan ... |
469 470 471 472 |
} else { /* We shouldn't change the state till all above operations succeed */ device->power.state = state; } |
1da177e4c Linux-2.6.12-rc2 |
473 |
|
d550d98d3 ACPI: delete trac... |
474 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
475 |
} |
1da177e4c Linux-2.6.12-rc2 |
476 477 478 |
/* -------------------------------------------------------------------------- FS Interface (/proc) -------------------------------------------------------------------------- */ |
4be44fcd3 [ACPI] Lindent al... |
479 |
static struct proc_dir_entry *acpi_power_dir; |
1da177e4c Linux-2.6.12-rc2 |
480 481 482 |
static int acpi_power_seq_show(struct seq_file *seq, void *offset) { |
0a6139027 ACPI: Thermal iss... |
483 484 |
int count = 0; int result = 0; |
1da177e4c Linux-2.6.12-rc2 |
485 |
struct acpi_power_resource *resource = NULL; |
0a6139027 ACPI: Thermal iss... |
486 487 |
struct list_head *node, *next; struct acpi_power_reference *ref; |
1da177e4c Linux-2.6.12-rc2 |
488 |
|
1da177e4c Linux-2.6.12-rc2 |
489 |
|
50dd09697 ACPI: Remove unne... |
490 |
resource = seq->private; |
1da177e4c Linux-2.6.12-rc2 |
491 492 493 |
if (!resource) goto end; |
0a6139027 ACPI: Thermal iss... |
494 495 496 |
result = acpi_power_get_state(resource); if (result) goto end; |
1da177e4c Linux-2.6.12-rc2 |
497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 |
seq_puts(seq, "state: "); switch (resource->state) { case ACPI_POWER_RESOURCE_STATE_ON: seq_puts(seq, "on "); break; case ACPI_POWER_RESOURCE_STATE_OFF: seq_puts(seq, "off "); break; default: seq_puts(seq, "unknown "); break; } |
0a6139027 ACPI: Thermal iss... |
512 513 514 515 516 517 |
mutex_lock(&resource->resource_lock); list_for_each_safe(node, next, &resource->reference) { ref = container_of(node, struct acpi_power_reference, node); count++; } mutex_unlock(&resource->resource_lock); |
1da177e4c Linux-2.6.12-rc2 |
518 519 |
seq_printf(seq, "system level: S%d " |
4be44fcd3 [ACPI] Lindent al... |
520 521 522 523 524 |
"order: %d " "reference count: %d ", resource->system_level, |
0a6139027 ACPI: Thermal iss... |
525 |
resource->order, count); |
1da177e4c Linux-2.6.12-rc2 |
526 |
|
4be44fcd3 [ACPI] Lindent al... |
527 |
end: |
d550d98d3 ACPI: delete trac... |
528 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
529 530 531 532 533 534 |
} static int acpi_power_open_fs(struct inode *inode, struct file *file) { return single_open(file, acpi_power_seq_show, PDE(inode)->data); } |
4be44fcd3 [ACPI] Lindent al... |
535 |
static int acpi_power_add_fs(struct acpi_device *device) |
1da177e4c Linux-2.6.12-rc2 |
536 |
{ |
4be44fcd3 [ACPI] Lindent al... |
537 |
struct proc_dir_entry *entry = NULL; |
1da177e4c Linux-2.6.12-rc2 |
538 |
|
1da177e4c Linux-2.6.12-rc2 |
539 540 |
if (!device) |
d550d98d3 ACPI: delete trac... |
541 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
542 543 544 |
if (!acpi_device_dir(device)) { acpi_device_dir(device) = proc_mkdir(acpi_device_bid(device), |
4be44fcd3 [ACPI] Lindent al... |
545 |
acpi_power_dir); |
1da177e4c Linux-2.6.12-rc2 |
546 |
if (!acpi_device_dir(device)) |
d550d98d3 ACPI: delete trac... |
547 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
548 549 550 551 |
} /* 'status' [R] */ entry = create_proc_entry(ACPI_POWER_FILE_STATUS, |
4be44fcd3 [ACPI] Lindent al... |
552 |
S_IRUGO, acpi_device_dir(device)); |
1da177e4c Linux-2.6.12-rc2 |
553 |
if (!entry) |
d550d98d3 ACPI: delete trac... |
554 |
return -EIO; |
1da177e4c Linux-2.6.12-rc2 |
555 556 557 558 |
else { entry->proc_fops = &acpi_power_fops; entry->data = acpi_driver_data(device); } |
d550d98d3 ACPI: delete trac... |
559 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
560 |
} |
4be44fcd3 [ACPI] Lindent al... |
561 |
static int acpi_power_remove_fs(struct acpi_device *device) |
1da177e4c Linux-2.6.12-rc2 |
562 |
{ |
1da177e4c Linux-2.6.12-rc2 |
563 564 565 566 567 568 569 |
if (acpi_device_dir(device)) { remove_proc_entry(ACPI_POWER_FILE_STATUS, acpi_device_dir(device)); remove_proc_entry(acpi_device_bid(device), acpi_power_dir); acpi_device_dir(device) = NULL; } |
d550d98d3 ACPI: delete trac... |
570 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
571 |
} |
1da177e4c Linux-2.6.12-rc2 |
572 573 574 |
/* -------------------------------------------------------------------------- Driver Interface -------------------------------------------------------------------------- */ |
4be44fcd3 [ACPI] Lindent al... |
575 |
static int acpi_power_add(struct acpi_device *device) |
1da177e4c Linux-2.6.12-rc2 |
576 |
{ |
4be44fcd3 [ACPI] Lindent al... |
577 578 |
int result = 0; acpi_status status = AE_OK; |
1da177e4c Linux-2.6.12-rc2 |
579 |
struct acpi_power_resource *resource = NULL; |
4be44fcd3 [ACPI] Lindent al... |
580 581 |
union acpi_object acpi_object; struct acpi_buffer buffer = { sizeof(acpi_object), &acpi_object }; |
1da177e4c Linux-2.6.12-rc2 |
582 |
|
1da177e4c Linux-2.6.12-rc2 |
583 584 |
if (!device) |
d550d98d3 ACPI: delete trac... |
585 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
586 |
|
36bcbec7c ACPI: replace kma... |
587 |
resource = kzalloc(sizeof(struct acpi_power_resource), GFP_KERNEL); |
1da177e4c Linux-2.6.12-rc2 |
588 |
if (!resource) |
d550d98d3 ACPI: delete trac... |
589 |
return -ENOMEM; |
1da177e4c Linux-2.6.12-rc2 |
590 |
|
415985728 ACPI: power: add ... |
591 |
resource->device = device; |
0a6139027 ACPI: Thermal iss... |
592 593 |
mutex_init(&resource->resource_lock); INIT_LIST_HEAD(&resource->reference); |
1da177e4c Linux-2.6.12-rc2 |
594 595 596 597 598 599 |
strcpy(resource->name, device->pnp.bus_id); strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); strcpy(acpi_device_class(device), ACPI_POWER_CLASS); acpi_driver_data(device) = resource; /* Evalute the object to get the system level and resource order. */ |
5fbc19efd ACPI: power: Use ... |
600 |
status = acpi_evaluate_object(device->handle, NULL, NULL, &buffer); |
1da177e4c Linux-2.6.12-rc2 |
601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 |
if (ACPI_FAILURE(status)) { result = -ENODEV; goto end; } resource->system_level = acpi_object.power_resource.system_level; resource->order = acpi_object.power_resource.resource_order; result = acpi_power_get_state(resource); if (result) goto end; switch (resource->state) { case ACPI_POWER_RESOURCE_STATE_ON: device->power.state = ACPI_STATE_D0; break; case ACPI_POWER_RESOURCE_STATE_OFF: device->power.state = ACPI_STATE_D3; break; default: device->power.state = ACPI_STATE_UNKNOWN; break; } result = acpi_power_add_fs(device); if (result) goto end; |
4be44fcd3 [ACPI] Lindent al... |
627 |
|
1da177e4c Linux-2.6.12-rc2 |
628 629 |
printk(KERN_INFO PREFIX "%s [%s] (%s) ", acpi_device_name(device), |
4be44fcd3 [ACPI] Lindent al... |
630 |
acpi_device_bid(device), resource->state ? "on" : "off"); |
1da177e4c Linux-2.6.12-rc2 |
631 |
|
4be44fcd3 [ACPI] Lindent al... |
632 |
end: |
1da177e4c Linux-2.6.12-rc2 |
633 634 |
if (result) kfree(resource); |
4be44fcd3 [ACPI] Lindent al... |
635 |
|
d550d98d3 ACPI: delete trac... |
636 |
return result; |
1da177e4c Linux-2.6.12-rc2 |
637 |
} |
4be44fcd3 [ACPI] Lindent al... |
638 |
static int acpi_power_remove(struct acpi_device *device, int type) |
1da177e4c Linux-2.6.12-rc2 |
639 640 |
{ struct acpi_power_resource *resource = NULL; |
0a6139027 ACPI: Thermal iss... |
641 |
struct list_head *node, *next; |
1da177e4c Linux-2.6.12-rc2 |
642 |
|
1da177e4c Linux-2.6.12-rc2 |
643 644 |
if (!device || !acpi_driver_data(device)) |
d550d98d3 ACPI: delete trac... |
645 |
return -EINVAL; |
1da177e4c Linux-2.6.12-rc2 |
646 |
|
50dd09697 ACPI: Remove unne... |
647 |
resource = acpi_driver_data(device); |
1da177e4c Linux-2.6.12-rc2 |
648 649 |
acpi_power_remove_fs(device); |
0a6139027 ACPI: Thermal iss... |
650 651 652 653 654 655 656 |
mutex_lock(&resource->resource_lock); list_for_each_safe(node, next, &resource->reference) { struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); list_del(&ref->node); kfree(ref); } mutex_unlock(&resource->resource_lock); |
1da177e4c Linux-2.6.12-rc2 |
657 |
kfree(resource); |
d550d98d3 ACPI: delete trac... |
658 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
659 |
} |
e8363f332 ACPI: update acpi... |
660 |
static int acpi_power_resume(struct acpi_device *device) |
0a6139027 ACPI: Thermal iss... |
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 |
{ int result = 0; struct acpi_power_resource *resource = NULL; struct acpi_power_reference *ref; if (!device || !acpi_driver_data(device)) return -EINVAL; resource = (struct acpi_power_resource *)acpi_driver_data(device); result = acpi_power_get_state(resource); if (result) return result; mutex_lock(&resource->resource_lock); |
0a6139027 ACPI: Thermal iss... |
676 677 678 679 680 681 682 683 684 685 686 |
if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) && !list_empty(&resource->reference)) { ref = container_of(resource->reference.next, struct acpi_power_reference, node); mutex_unlock(&resource->resource_lock); result = acpi_power_on(device->handle, ref->device); return result; } mutex_unlock(&resource->resource_lock); return 0; } |
4be44fcd3 [ACPI] Lindent al... |
687 |
static int __init acpi_power_init(void) |
1da177e4c Linux-2.6.12-rc2 |
688 |
{ |
4be44fcd3 [ACPI] Lindent al... |
689 |
int result = 0; |
1da177e4c Linux-2.6.12-rc2 |
690 |
|
1da177e4c Linux-2.6.12-rc2 |
691 692 |
if (acpi_disabled) |
d550d98d3 ACPI: delete trac... |
693 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
694 695 696 697 698 |
INIT_LIST_HEAD(&acpi_power_resource_list); acpi_power_dir = proc_mkdir(ACPI_POWER_CLASS, acpi_root_dir); if (!acpi_power_dir) |
d550d98d3 ACPI: delete trac... |
699 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
700 701 702 703 |
result = acpi_bus_register_driver(&acpi_power_driver); if (result < 0) { remove_proc_entry(ACPI_POWER_CLASS, acpi_root_dir); |
d550d98d3 ACPI: delete trac... |
704 |
return -ENODEV; |
1da177e4c Linux-2.6.12-rc2 |
705 |
} |
d550d98d3 ACPI: delete trac... |
706 |
return 0; |
1da177e4c Linux-2.6.12-rc2 |
707 708 709 |
} subsys_initcall(acpi_power_init); |