aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-17 08:11:05 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-17 08:11:05 -0500
commitbc9b6407bd6df3ab7189e5622816bbc11ae9d2d8 (patch)
tree8f44ae6c9750b271eb423787e19db1288b53c42e /drivers/acpi/power.c
parent6a8dd80821c215bc49bf6b108e85c1738c82bf43 (diff)
ACPI / PM: Rework the handling of devices depending on power resources
Commit 0090def6 (ACPI: Add interface to register/unregister device to/from power resources) made it possible to indicate to the ACPI core that if the given device depends on any power resources, then it should be resumed as soon as all of the power resources required by it to transition to the D0 power state have been turned on. Unfortunately, however, this was a mistake, because all devices depending on power resources should be treated this way (i.e. they should be resumed when all power resources required by their D0 state have been turned on) and for the majority of those devices the ACPI core can figure out by itself which (physical) devices depend on what power resources. For this reason, replace the code added by commit 0090def6 with a new, much more straightforward, mechanism that will be used internally by the ACPI core and remove all references to that code from kernel subsystems using ACPI. For the cases when there are (physical) devices that should be resumed whenever a not directly related ACPI device node goes into D0 as a result of power resources configuration changes, like in the SATA case, add two new routines, acpi_dev_pm_add_dependent() and acpi_dev_pm_remove_dependent(), allowing subsystems to manage such dependencies. Convert the SATA subsystem to use the new functions accordingly. Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r--drivers/acpi/power.c219
1 files changed, 82 insertions, 137 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 6e7b9d523812..659386c4f0cb 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -83,31 +83,20 @@ static struct acpi_driver acpi_power_driver = {
83 .drv.pm = &acpi_power_pm, 83 .drv.pm = &acpi_power_pm,
84}; 84};
85 85
86/* 86struct acpi_power_dependent_device {
87 * A power managed device 87 struct list_head node;
88 * A device may rely on multiple power resources. 88 struct acpi_device *adev;
89 * */ 89 struct work_struct work;
90struct acpi_power_managed_device {
91 struct device *dev; /* The physical device */
92 acpi_handle *handle;
93};
94
95struct acpi_power_resource_device {
96 struct acpi_power_managed_device *device;
97 struct acpi_power_resource_device *next;
98}; 90};
99 91
100struct acpi_power_resource { 92struct acpi_power_resource {
101 struct acpi_device * device; 93 struct acpi_device *device;
94 struct list_head dependent;
102 acpi_bus_id name; 95 acpi_bus_id name;
103 u32 system_level; 96 u32 system_level;
104 u32 order; 97 u32 order;
105 unsigned int ref_count; 98 unsigned int ref_count;
106 struct mutex resource_lock; 99 struct mutex resource_lock;
107
108 /* List of devices relying on this power resource */
109 struct acpi_power_resource_device *devices;
110 struct mutex devices_lock;
111}; 100};
112 101
113static struct list_head acpi_power_resource_list; 102static struct list_head acpi_power_resource_list;
@@ -207,21 +196,30 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
207 return 0; 196 return 0;
208} 197}
209 198
210/* Resume the device when all power resources in _PR0 are on */ 199static void acpi_power_resume_dependent(struct work_struct *work)
211static void acpi_power_on_device(struct acpi_power_managed_device *device)
212{ 200{
213 struct acpi_device *acpi_dev; 201 struct acpi_power_dependent_device *dep;
214 acpi_handle handle = device->handle; 202 struct acpi_device_physical_node *pn;
203 struct acpi_device *adev;
215 int state; 204 int state;
216 205
217 if (acpi_bus_get_device(handle, &acpi_dev)) 206 dep = container_of(work, struct acpi_power_dependent_device, work);
207 adev = dep->adev;
208 if (acpi_power_get_inferred_state(adev, &state))
218 return; 209 return;
219 210
220 if(acpi_power_get_inferred_state(acpi_dev, &state)) 211 if (state > ACPI_STATE_D0)
221 return; 212 return;
222 213
223 if (state == ACPI_STATE_D0 && pm_runtime_suspended(device->dev)) 214 mutex_lock(&adev->physical_node_lock);
224 pm_request_resume(device->dev); 215
216 list_for_each_entry(pn, &adev->physical_node_list, node)
217 pm_request_resume(pn->dev);
218
219 list_for_each_entry(pn, &adev->power_dependent, node)
220 pm_request_resume(pn->dev);
221
222 mutex_unlock(&adev->physical_node_lock);
225} 223}
226 224
227static int __acpi_power_on(struct acpi_power_resource *resource) 225static int __acpi_power_on(struct acpi_power_resource *resource)
@@ -244,9 +242,7 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
244static int acpi_power_on(acpi_handle handle) 242static int acpi_power_on(acpi_handle handle)
245{ 243{
246 int result = 0; 244 int result = 0;
247 bool resume_device = false;
248 struct acpi_power_resource *resource = NULL; 245 struct acpi_power_resource *resource = NULL;
249 struct acpi_power_resource_device *device_list;
250 246
251 result = acpi_power_get_context(handle, &resource); 247 result = acpi_power_get_context(handle, &resource);
252 if (result) 248 if (result)
@@ -260,26 +256,17 @@ static int acpi_power_on(acpi_handle handle)
260 resource->name)); 256 resource->name));
261 } else { 257 } else {
262 result = __acpi_power_on(resource); 258 result = __acpi_power_on(resource);
263 if (result) 259 if (result) {
264 resource->ref_count--; 260 resource->ref_count--;
265 else 261 } else {
266 resume_device = true; 262 struct acpi_power_dependent_device *dep;
267 }
268 263
269 mutex_unlock(&resource->resource_lock); 264 list_for_each_entry(dep, &resource->dependent, node)
270 265 schedule_work(&dep->work);
271 if (!resume_device) 266 }
272 return result;
273
274 mutex_lock(&resource->devices_lock);
275
276 device_list = resource->devices;
277 while (device_list) {
278 acpi_power_on_device(device_list->device);
279 device_list = device_list->next;
280 } 267 }
281 268
282 mutex_unlock(&resource->devices_lock); 269 mutex_unlock(&resource->resource_lock);
283 270
284 return result; 271 return result;
285} 272}
@@ -357,119 +344,81 @@ static int acpi_power_on_list(struct acpi_handle_list *list)
357 return result; 344 return result;
358} 345}
359 346
360static void __acpi_power_resource_unregister_device(struct device *dev, 347static void acpi_power_add_dependent(acpi_handle rhandle,
361 acpi_handle res_handle) 348 struct acpi_device *adev)
362{ 349{
363 struct acpi_power_resource *resource = NULL; 350 struct acpi_power_dependent_device *dep;
364 struct acpi_power_resource_device *prev, *curr; 351 struct acpi_power_resource *resource;
365 352
366 if (acpi_power_get_context(res_handle, &resource)) 353 if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource))
367 return; 354 return;
368 355
369 mutex_lock(&resource->devices_lock); 356 mutex_lock(&resource->resource_lock);
370 prev = NULL;
371 curr = resource->devices;
372 while (curr) {
373 if (curr->device->dev == dev) {
374 if (!prev)
375 resource->devices = curr->next;
376 else
377 prev->next = curr->next;
378
379 kfree(curr);
380 break;
381 }
382
383 prev = curr;
384 curr = curr->next;
385 }
386 mutex_unlock(&resource->devices_lock);
387}
388
389/* Unlink dev from all power resources in _PR0 */
390void acpi_power_resource_unregister_device(struct device *dev, acpi_handle handle)
391{
392 struct acpi_device *acpi_dev;
393 struct acpi_handle_list *list;
394 int i;
395 357
396 if (!dev || !handle) 358 list_for_each_entry(dep, &resource->dependent, node)
397 return; 359 if (dep->adev == adev)
360 goto out;
398 361
399 if (acpi_bus_get_device(handle, &acpi_dev)) 362 dep = kzalloc(sizeof(*dep), GFP_KERNEL);
400 return; 363 if (!dep)
364 goto out;
401 365
402 list = &acpi_dev->power.states[ACPI_STATE_D0].resources; 366 dep->adev = adev;
367 INIT_WORK(&dep->work, acpi_power_resume_dependent);
368 list_add_tail(&dep->node, &resource->dependent);
403 369
404 for (i = 0; i < list->count; i++) 370 out:
405 __acpi_power_resource_unregister_device(dev, 371 mutex_unlock(&resource->resource_lock);
406 list->handles[i]);
407} 372}
408EXPORT_SYMBOL_GPL(acpi_power_resource_unregister_device);
409 373
410static int __acpi_power_resource_register_device( 374static void acpi_power_remove_dependent(acpi_handle rhandle,
411 struct acpi_power_managed_device *powered_device, acpi_handle handle) 375 struct acpi_device *adev)
412{ 376{
413 struct acpi_power_resource *resource = NULL; 377 struct acpi_power_dependent_device *dep;
414 struct acpi_power_resource_device *power_resource_device; 378 struct acpi_power_resource *resource;
415 int result; 379 struct work_struct *work = NULL;
416 380
417 result = acpi_power_get_context(handle, &resource); 381 if (!rhandle || !adev || acpi_power_get_context(rhandle, &resource))
418 if (result) 382 return;
419 return result;
420 383
421 power_resource_device = kzalloc( 384 mutex_lock(&resource->resource_lock);
422 sizeof(*power_resource_device), GFP_KERNEL);
423 if (!power_resource_device)
424 return -ENOMEM;
425 385
426 power_resource_device->device = powered_device; 386 list_for_each_entry(dep, &resource->dependent, node)
387 if (dep->adev == adev) {
388 list_del(&dep->node);
389 work = &dep->work;
390 break;
391 }
427 392
428 mutex_lock(&resource->devices_lock); 393 mutex_unlock(&resource->resource_lock);
429 power_resource_device->next = resource->devices;
430 resource->devices = power_resource_device;
431 mutex_unlock(&resource->devices_lock);
432 394
433 return 0; 395 if (work) {
396 cancel_work_sync(work);
397 kfree(dep);
398 }
434} 399}
435 400
436/* Link dev to all power resources in _PR0 */ 401void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
437int acpi_power_resource_register_device(struct device *dev, acpi_handle handle)
438{ 402{
439 struct acpi_device *acpi_dev; 403 if (adev->power.flags.power_resources) {
440 struct acpi_handle_list *list; 404 struct acpi_device_power_state *ps;
441 struct acpi_power_managed_device *powered_device; 405 int j;
442 int i, ret;
443 406
444 if (!dev || !handle) 407 ps = &adev->power.states[ACPI_STATE_D0];
445 return -ENODEV; 408 for (j = 0; j < ps->resources.count; j++) {
446 409 acpi_handle rhandle = ps->resources.handles[j];
447 ret = acpi_bus_get_device(handle, &acpi_dev);
448 if (ret || !acpi_dev->power.flags.power_resources)
449 return -ENODEV;
450 410
451 powered_device = kzalloc(sizeof(*powered_device), GFP_KERNEL); 411 if (add)
452 if (!powered_device) 412 acpi_power_add_dependent(rhandle, adev);
453 return -ENOMEM; 413 else
454 414 acpi_power_remove_dependent(rhandle, adev);
455 powered_device->dev = dev;
456 powered_device->handle = handle;
457
458 list = &acpi_dev->power.states[ACPI_STATE_D0].resources;
459
460 for (i = 0; i < list->count; i++) {
461 ret = __acpi_power_resource_register_device(powered_device,
462 list->handles[i]);
463
464 if (ret) {
465 acpi_power_resource_unregister_device(dev, handle);
466 break;
467 } 415 }
468 } 416 }
469
470 return ret;
471} 417}
472EXPORT_SYMBOL_GPL(acpi_power_resource_register_device); 418
419/* --------------------------------------------------------------------------
420 Device Power Management
421 -------------------------------------------------------------------------- */
473 422
474/** 423/**
475 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in 424 * acpi_device_sleep_wake - execute _DSW (Device Sleep Wake) or (deprecated in
@@ -623,10 +572,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
623 return err; 572 return err;
624} 573}
625 574
626/* --------------------------------------------------------------------------
627 Device Power Management
628 -------------------------------------------------------------------------- */
629
630int acpi_power_get_inferred_state(struct acpi_device *device, int *state) 575int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
631{ 576{
632 int result = 0; 577 int result = 0;
@@ -725,7 +670,7 @@ static int acpi_power_add(struct acpi_device *device)
725 670
726 resource->device = device; 671 resource->device = device;
727 mutex_init(&resource->resource_lock); 672 mutex_init(&resource->resource_lock);
728 mutex_init(&resource->devices_lock); 673 INIT_LIST_HEAD(&resource->dependent);
729 strcpy(resource->name, device->pnp.bus_id); 674 strcpy(resource->name, device->pnp.bus_id);
730 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); 675 strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME);
731 strcpy(acpi_device_class(device), ACPI_POWER_CLASS); 676 strcpy(acpi_device_class(device), ACPI_POWER_CLASS);