aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--drivers/acpi/power.c167
1 files changed, 70 insertions, 97 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 844c155aeb0f..67dedeed144c 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -80,18 +80,13 @@ static struct acpi_driver acpi_power_driver = {
80 }, 80 },
81}; 81};
82 82
83struct acpi_power_reference {
84 struct list_head node;
85 struct acpi_device *device;
86};
87
88struct acpi_power_resource { 83struct acpi_power_resource {
89 struct acpi_device * device; 84 struct acpi_device * device;
90 acpi_bus_id name; 85 acpi_bus_id name;
91 u32 system_level; 86 u32 system_level;
92 u32 order; 87 u32 order;
88 unsigned int ref_count;
93 struct mutex resource_lock; 89 struct mutex resource_lock;
94 struct list_head reference;
95}; 90};
96 91
97static struct list_head acpi_power_resource_list; 92static struct list_head acpi_power_resource_list;
@@ -184,101 +179,89 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
184 return result; 179 return result;
185} 180}
186 181
187static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) 182static int __acpi_power_on(struct acpi_power_resource *resource)
188{ 183{
189 int result = 0;
190 int found = 0;
191 acpi_status status = AE_OK; 184 acpi_status status = AE_OK;
192 struct acpi_power_resource *resource = NULL;
193 struct list_head *node, *next;
194 struct acpi_power_reference *ref;
195 185
186 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL);
187 if (ACPI_FAILURE(status))
188 return -ENODEV;
189
190 /* Update the power resource's _device_ power state */
191 resource->device->power.state = ACPI_STATE_D0;
192
193 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Power resource [%s] turned on\n",
194 resource->name));
195
196 return 0;
197}
198
199static int acpi_power_on(acpi_handle handle)
200{
201 int result = 0;
202 struct acpi_power_resource *resource = NULL;
196 203
197 result = acpi_power_get_context(handle, &resource); 204 result = acpi_power_get_context(handle, &resource);
198 if (result) 205 if (result)
199 return result; 206 return result;
200 207
201 mutex_lock(&resource->resource_lock); 208 mutex_lock(&resource->resource_lock);
202 list_for_each_safe(node, next, &resource->reference) {
203 ref = container_of(node, struct acpi_power_reference, node);
204 if (dev->handle == ref->device->handle) {
205 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n",
206 dev->pnp.bus_id, resource->name));
207 found = 1;
208 break;
209 }
210 }
211 209
212 if (!found) { 210 if (resource->ref_count++) {
213 ref = kmalloc(sizeof (struct acpi_power_reference), 211 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
214 irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); 212 "Power resource [%s] already on",
215 if (!ref) { 213 resource->name));
216 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); 214 } else {
217 mutex_unlock(&resource->resource_lock); 215 result = __acpi_power_on(resource);
218 return -ENOMEM;
219 }
220 list_add_tail(&ref->node, &resource->reference);
221 ref->device = dev;
222 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n",
223 dev->pnp.bus_id, resource->name));
224 } 216 }
225 mutex_unlock(&resource->resource_lock);
226 217
227 status = acpi_evaluate_object(resource->device->handle, "_ON", NULL, NULL); 218 mutex_unlock(&resource->resource_lock);
228 if (ACPI_FAILURE(status))
229 return -ENODEV;
230
231 /* Update the power resource's _device_ power state */
232 resource->device->power.state = ACPI_STATE_D0;
233 219
234 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n",
235 resource->name));
236 return 0; 220 return 0;
237} 221}
238 222
239static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) 223static int acpi_power_off_device(acpi_handle handle)
240{ 224{
241 int result = 0; 225 int result = 0;
242 acpi_status status = AE_OK; 226 acpi_status status = AE_OK;
243 struct acpi_power_resource *resource = NULL; 227 struct acpi_power_resource *resource = NULL;
244 struct list_head *node, *next;
245 struct acpi_power_reference *ref;
246 228
247 result = acpi_power_get_context(handle, &resource); 229 result = acpi_power_get_context(handle, &resource);
248 if (result) 230 if (result)
249 return result; 231 return result;
250 232
251 mutex_lock(&resource->resource_lock); 233 mutex_lock(&resource->resource_lock);
252 list_for_each_safe(node, next, &resource->reference) { 234
253 ref = container_of(node, struct acpi_power_reference, node); 235 if (!resource->ref_count) {
254 if (dev->handle == ref->device->handle) { 236 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
255 list_del(&ref->node); 237 "Power resource [%s] already off",
256 kfree(ref); 238 resource->name));
257 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", 239 goto unlock;
258 dev->pnp.bus_id, resource->name));
259 break;
260 }
261 } 240 }
262 241
263 if (!list_empty(&resource->reference)) { 242 if (--resource->ref_count) {
264 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", 243 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
265 resource->name)); 244 "Power resource [%s] still in use\n",
266 mutex_unlock(&resource->resource_lock); 245 resource->name));
267 return 0; 246 goto unlock;
268 } 247 }
269 mutex_unlock(&resource->resource_lock);
270 248
271 status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL); 249 status = acpi_evaluate_object(resource->device->handle, "_OFF", NULL, NULL);
272 if (ACPI_FAILURE(status)) 250 if (ACPI_FAILURE(status)) {
273 return -ENODEV; 251 result = -ENODEV;
252 } else {
253 /* Update the power resource's _device_ power state */
254 resource->device->power.state = ACPI_STATE_D3;
274 255
275 /* Update the power resource's _device_ power state */ 256 ACPI_DEBUG_PRINT((ACPI_DB_INFO,
276 resource->device->power.state = ACPI_STATE_D3; 257 "Power resource [%s] turned off\n",
258 resource->name));
259 }
277 260
278 ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned off\n", 261 unlock:
279 resource->name)); 262 mutex_unlock(&resource->resource_lock);
280 263
281 return 0; 264 return result;
282} 265}
283 266
284/** 267/**
@@ -364,7 +347,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev, int sleep_state)
364 347
365 /* Open power resource */ 348 /* Open power resource */
366 for (i = 0; i < dev->wakeup.resources.count; i++) { 349 for (i = 0; i < dev->wakeup.resources.count; i++) {
367 int ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); 350 int ret = acpi_power_on(dev->wakeup.resources.handles[i]);
368 if (ret) { 351 if (ret) {
369 printk(KERN_ERR PREFIX "Transition power state\n"); 352 printk(KERN_ERR PREFIX "Transition power state\n");
370 dev->wakeup.flags.valid = 0; 353 dev->wakeup.flags.valid = 0;
@@ -420,7 +403,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
420 /* Close power resource */ 403 /* Close power resource */
421 for (i = 0; i < dev->wakeup.resources.count; i++) { 404 for (i = 0; i < dev->wakeup.resources.count; i++) {
422 int ret = acpi_power_off_device( 405 int ret = acpi_power_off_device(
423 dev->wakeup.resources.handles[i], dev); 406 dev->wakeup.resources.handles[i]);
424 if (ret) { 407 if (ret) {
425 printk(KERN_ERR PREFIX "Transition power state\n"); 408 printk(KERN_ERR PREFIX "Transition power state\n");
426 dev->wakeup.flags.valid = 0; 409 dev->wakeup.flags.valid = 0;
@@ -500,7 +483,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
500 * (e.g. so the device doesn't lose power while transitioning). 483 * (e.g. so the device doesn't lose power while transitioning).
501 */ 484 */
502 for (i = 0; i < tl->count; i++) { 485 for (i = 0; i < tl->count; i++) {
503 result = acpi_power_on(tl->handles[i], device); 486 result = acpi_power_on(tl->handles[i]);
504 if (result) 487 if (result)
505 goto end; 488 goto end;
506 } 489 }
@@ -513,7 +496,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
513 * Then we dereference all power resources used in the current list. 496 * Then we dereference all power resources used in the current list.
514 */ 497 */
515 for (i = 0; i < cl->count; i++) { 498 for (i = 0; i < cl->count; i++) {
516 result = acpi_power_off_device(cl->handles[i], device); 499 result = acpi_power_off_device(cl->handles[i]);
517 if (result) 500 if (result)
518 goto end; 501 goto end;
519 } 502 }
@@ -551,7 +534,6 @@ static int acpi_power_add(struct acpi_device *device)
551 534
552 resource->device = device; 535 resource->device = device;
553 mutex_init(&resource->resource_lock); 536 mutex_init(&resource->resource_lock);
554 INIT_LIST_HEAD(&resource->reference);
555 strcpy(resource->name, device->pnp.bus_id); 537 strcpy(resource->name, device->pnp.bus_id);
556 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); 538 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
557 strcpy(acpi_device_class(device), ACPI_POWER_CLASS); 539 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);
@@ -594,22 +576,14 @@ static int acpi_power_add(struct acpi_device *device)
594 576
595static int acpi_power_remove(struct acpi_device *device, int type) 577static int acpi_power_remove(struct acpi_device *device, int type)
596{ 578{
597 struct acpi_power_resource *resource = NULL; 579 struct acpi_power_resource *resource;
598 struct list_head *node, *next;
599 580
600 581 if (!device)
601 if (!device || !acpi_driver_data(device))
602 return -EINVAL; 582 return -EINVAL;
603 583
604 resource = acpi_driver_data(device); 584 resource = acpi_driver_data(device);
605 585 if (!resource)
606 mutex_lock(&resource->resource_lock); 586 return -EINVAL;
607 list_for_each_safe(node, next, &resource->reference) {
608 struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node);
609 list_del(&ref->node);
610 kfree(ref);
611 }
612 mutex_unlock(&resource->resource_lock);
613 587
614 kfree(resource); 588 kfree(resource);
615 589
@@ -619,29 +593,28 @@ static int acpi_power_remove(struct acpi_device *device, int type)
619static int acpi_power_resume(struct acpi_device *device) 593static int acpi_power_resume(struct acpi_device *device)
620{ 594{
621 int result = 0, state; 595 int result = 0, state;
622 struct acpi_power_resource *resource = NULL; 596 struct acpi_power_resource *resource;
623 struct acpi_power_reference *ref;
624 597
625 if (!device || !acpi_driver_data(device)) 598 if (!device)
626 return -EINVAL; 599 return -EINVAL;
627 600
628 resource = acpi_driver_data(device); 601 resource = acpi_driver_data(device);
602 if (!resource)
603 return -EINVAL;
604
605 mutex_lock(&resource->resource_lock);
629 606
630 result = acpi_power_get_state(device->handle, &state); 607 result = acpi_power_get_state(device->handle, &state);
631 if (result) 608 if (result)
632 return result; 609 goto unlock;
633 610
634 mutex_lock(&resource->resource_lock); 611 if (state == ACPI_POWER_RESOURCE_STATE_OFF && resource->ref_count)
635 if (state == ACPI_POWER_RESOURCE_STATE_OFF && 612 result = __acpi_power_on(resource);
636 !list_empty(&resource->reference)) {
637 ref = container_of(resource->reference.next, struct acpi_power_reference, node);
638 mutex_unlock(&resource->resource_lock);
639 result = acpi_power_on(device->handle, ref->device);
640 return result;
641 }
642 613
614 unlock:
643 mutex_unlock(&resource->resource_lock); 615 mutex_unlock(&resource->resource_lock);
644 return 0; 616
617 return result;
645} 618}
646 619
647int __init acpi_power_init(void) 620int __init acpi_power_init(void)