aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi
diff options
context:
space:
mode:
authorRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-17 08:11:06 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2013-01-17 08:11:06 -0500
commit0b224527323669c66e0a37ae05b04034bfcdce14 (patch)
treeb7332e4f7492220d651374a5de56ebd03cbb22e5 /drivers/acpi
parent722c929f32616943d2b67332068f09c08e81eec8 (diff)
ACPI / PM: Take order attribute of power resources into account
ACPI power resources have an order attribute that should be taken into account when turning them on and off, but it is not used now. Modify the power resources management code to preserve the spec-compliant ordering of power resources that power states of devices depend on (analogous changes will be done separately for power resources used for wakeup). Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
Diffstat (limited to 'drivers/acpi')
-rw-r--r--drivers/acpi/internal.h2
-rw-r--r--drivers/acpi/power.c161
-rw-r--r--drivers/acpi/scan.c31
3 files changed, 117 insertions, 77 deletions
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index ce6cb24de8c7..e28068a765a9 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -50,6 +50,8 @@ void acpi_free_ids(struct acpi_device *device);
50 Power Resource 50 Power Resource
51 -------------------------------------------------------------------------- */ 51 -------------------------------------------------------------------------- */
52int acpi_power_init(void); 52int acpi_power_init(void);
53void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list);
54void acpi_power_resources_list_free(struct list_head *list);
53void acpi_add_power_resource(acpi_handle handle); 55void acpi_add_power_resource(acpi_handle handle);
54void acpi_power_add_remove_device(struct acpi_device *adev, bool add); 56void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
55int acpi_device_sleep_wake(struct acpi_device *dev, 57int acpi_device_sleep_wake(struct acpi_device *dev,
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c
index 06ad05288af8..22a3d00d0359 100644
--- a/drivers/acpi/power.c
+++ b/drivers/acpi/power.c
@@ -75,6 +75,11 @@ struct acpi_power_resource {
75 struct mutex resource_lock; 75 struct mutex resource_lock;
76}; 76};
77 77
78struct acpi_power_resource_entry {
79 struct list_head node;
80 struct acpi_power_resource *resource;
81};
82
78static LIST_HEAD(acpi_power_resource_list); 83static LIST_HEAD(acpi_power_resource_list);
79static DEFINE_MUTEX(power_resource_list_lock); 84static DEFINE_MUTEX(power_resource_list_lock);
80 85
@@ -92,6 +97,41 @@ static struct acpi_power_resource *acpi_power_get_context(acpi_handle handle)
92 return container_of(device, struct acpi_power_resource, device); 97 return container_of(device, struct acpi_power_resource, device);
93} 98}
94 99
100void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list)
101{
102 struct acpi_power_resource *resource = acpi_power_get_context(handle);
103 struct acpi_power_resource_entry *entry;
104
105 if (!resource || !list)
106 return;
107
108 entry = kzalloc(sizeof(*entry), GFP_KERNEL);
109 if (!entry)
110 return;
111
112 entry->resource = resource;
113 if (!list_empty(list)) {
114 struct acpi_power_resource_entry *e;
115
116 list_for_each_entry(e, list, node)
117 if (e->resource->order > resource->order) {
118 list_add_tail(&entry->node, &e->node);
119 return;
120 }
121 }
122 list_add_tail(&entry->node, list);
123}
124
125void acpi_power_resources_list_free(struct list_head *list)
126{
127 struct acpi_power_resource_entry *entry, *e;
128
129 list_for_each_entry_safe(entry, e, list, node) {
130 list_del(&entry->node);
131 kfree(entry);
132 }
133}
134
95static int acpi_power_get_state(acpi_handle handle, int *state) 135static int acpi_power_get_state(acpi_handle handle, int *state)
96{ 136{
97 acpi_status status = AE_OK; 137 acpi_status status = AE_OK;
@@ -119,31 +159,23 @@ static int acpi_power_get_state(acpi_handle handle, int *state)
119 return 0; 159 return 0;
120} 160}
121 161
122static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) 162static int acpi_power_get_list_state(struct list_head *list, int *state)
123{ 163{
164 struct acpi_power_resource_entry *entry;
124 int cur_state; 165 int cur_state;
125 int i = 0;
126 166
127 if (!list || !state) 167 if (!list || !state)
128 return -EINVAL; 168 return -EINVAL;
129 169
130 /* The state of the list is 'on' IFF all resources are 'on'. */ 170 /* The state of the list is 'on' IFF all resources are 'on'. */
131 171 list_for_each_entry(entry, list, node) {
132 for (i = 0; i < list->count; i++) { 172 struct acpi_power_resource *resource = entry->resource;
133 struct acpi_power_resource *resource; 173 acpi_handle handle = resource->device.handle;
134 acpi_handle handle = list->handles[i];
135 int result; 174 int result;
136 175
137 resource = acpi_power_get_context(handle);
138 if (!resource)
139 return -ENODEV;
140
141 mutex_lock(&resource->resource_lock); 176 mutex_lock(&resource->resource_lock);
142
143 result = acpi_power_get_state(handle, &cur_state); 177 result = acpi_power_get_state(handle, &cur_state);
144
145 mutex_unlock(&resource->resource_lock); 178 mutex_unlock(&resource->resource_lock);
146
147 if (result) 179 if (result)
148 return result; 180 return result;
149 181
@@ -155,7 +187,6 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
155 cur_state ? "on" : "off")); 187 cur_state ? "on" : "off"));
156 188
157 *state = cur_state; 189 *state = cur_state;
158
159 return 0; 190 return 0;
160} 191}
161 192
@@ -199,14 +230,9 @@ static int __acpi_power_on(struct acpi_power_resource *resource)
199 return 0; 230 return 0;
200} 231}
201 232
202static int acpi_power_on(acpi_handle handle) 233static int acpi_power_on(struct acpi_power_resource *resource)
203{ 234{
204 int result = 0; 235 int result = 0;;
205 struct acpi_power_resource *resource;
206
207 resource = acpi_power_get_context(handle);
208 if (!resource)
209 return -ENODEV;
210 236
211 mutex_lock(&resource->resource_lock); 237 mutex_lock(&resource->resource_lock);
212 238
@@ -231,15 +257,10 @@ static int acpi_power_on(acpi_handle handle)
231 return result; 257 return result;
232} 258}
233 259
234static int acpi_power_off(acpi_handle handle) 260static int acpi_power_off(struct acpi_power_resource *resource)
235{ 261{
236 int result = 0;
237 acpi_status status = AE_OK; 262 acpi_status status = AE_OK;
238 struct acpi_power_resource *resource; 263 int result = 0;
239
240 resource = acpi_power_get_context(handle);
241 if (!resource)
242 return -ENODEV;
243 264
244 mutex_lock(&resource->resource_lock); 265 mutex_lock(&resource->resource_lock);
245 266
@@ -271,47 +292,48 @@ static int acpi_power_off(acpi_handle handle)
271 return result; 292 return result;
272} 293}
273 294
274static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res) 295static int acpi_power_off_list(struct list_head *list)
275{ 296{
276 int i; 297 struct acpi_power_resource_entry *entry;
298 int result = 0;
277 299
278 for (i = num_res - 1; i >= 0 ; i--) 300 list_for_each_entry_reverse(entry, list, node) {
279 acpi_power_off(list->handles[i]); 301 result = acpi_power_off(entry->resource);
280} 302 if (result)
303 goto err;
304 }
305 return 0;
281 306
282static void acpi_power_off_list(struct acpi_handle_list *list) 307 err:
283{ 308 list_for_each_entry_continue(entry, list, node)
284 __acpi_power_off_list(list, list->count); 309 acpi_power_on(entry->resource);
310
311 return result;
285} 312}
286 313
287static int acpi_power_on_list(struct acpi_handle_list *list) 314static int acpi_power_on_list(struct list_head *list)
288{ 315{
316 struct acpi_power_resource_entry *entry;
289 int result = 0; 317 int result = 0;
290 int i;
291 318
292 for (i = 0; i < list->count; i++) { 319 list_for_each_entry(entry, list, node) {
293 result = acpi_power_on(list->handles[i]); 320 result = acpi_power_on(entry->resource);
294 if (result) { 321 if (result)
295 __acpi_power_off_list(list, i); 322 goto err;
296 break;
297 }
298 } 323 }
324 return 0;
325
326 err:
327 list_for_each_entry_continue_reverse(entry, list, node)
328 acpi_power_off(entry->resource);
299 329
300 return result; 330 return result;
301} 331}
302 332
303static void acpi_power_add_dependent(acpi_handle rhandle, 333static void acpi_power_add_dependent(struct acpi_power_resource *resource,
304 struct acpi_device *adev) 334 struct acpi_device *adev)
305{ 335{
306 struct acpi_power_dependent_device *dep; 336 struct acpi_power_dependent_device *dep;
307 struct acpi_power_resource *resource;
308
309 if (!rhandle || !adev)
310 return;
311
312 resource = acpi_power_get_context(rhandle);
313 if (!resource)
314 return;
315 337
316 mutex_lock(&resource->resource_lock); 338 mutex_lock(&resource->resource_lock);
317 339
@@ -331,20 +353,12 @@ static void acpi_power_add_dependent(acpi_handle rhandle,
331 mutex_unlock(&resource->resource_lock); 353 mutex_unlock(&resource->resource_lock);
332} 354}
333 355
334static void acpi_power_remove_dependent(acpi_handle rhandle, 356static void acpi_power_remove_dependent(struct acpi_power_resource *resource,
335 struct acpi_device *adev) 357 struct acpi_device *adev)
336{ 358{
337 struct acpi_power_dependent_device *dep; 359 struct acpi_power_dependent_device *dep;
338 struct acpi_power_resource *resource;
339 struct work_struct *work = NULL; 360 struct work_struct *work = NULL;
340 361
341 if (!rhandle || !adev)
342 return;
343
344 resource = acpi_power_get_context(rhandle);
345 if (!resource)
346 return;
347
348 mutex_lock(&resource->resource_lock); 362 mutex_lock(&resource->resource_lock);
349 363
350 list_for_each_entry(dep, &resource->dependent, node) 364 list_for_each_entry(dep, &resource->dependent, node)
@@ -366,16 +380,16 @@ void acpi_power_add_remove_device(struct acpi_device *adev, bool add)
366{ 380{
367 if (adev->power.flags.power_resources) { 381 if (adev->power.flags.power_resources) {
368 struct acpi_device_power_state *ps; 382 struct acpi_device_power_state *ps;
369 int j; 383 struct acpi_power_resource_entry *entry;
370 384
371 ps = &adev->power.states[ACPI_STATE_D0]; 385 ps = &adev->power.states[ACPI_STATE_D0];
372 for (j = 0; j < ps->resources.count; j++) { 386 list_for_each_entry(entry, &ps->resources, node) {
373 acpi_handle rhandle = ps->resources.handles[j]; 387 struct acpi_power_resource *resource = entry->resource;
374 388
375 if (add) 389 if (add)
376 acpi_power_add_dependent(rhandle, adev); 390 acpi_power_add_dependent(resource, adev);
377 else 391 else
378 acpi_power_remove_dependent(rhandle, adev); 392 acpi_power_remove_dependent(resource, adev);
379 } 393 }
380 } 394 }
381} 395}
@@ -539,7 +553,6 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev)
539int acpi_power_get_inferred_state(struct acpi_device *device, int *state) 553int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
540{ 554{
541 int result = 0; 555 int result = 0;
542 struct acpi_handle_list *list = NULL;
543 int list_state = 0; 556 int list_state = 0;
544 int i = 0; 557 int i = 0;
545 558
@@ -551,8 +564,9 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
551 * required for a given D-state are 'on'. 564 * required for a given D-state are 'on'.
552 */ 565 */
553 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { 566 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
554 list = &device->power.states[i].resources; 567 struct list_head *list = &device->power.states[i].resources;
555 if (list->count < 1) 568
569 if (list_empty(list))
556 continue; 570 continue;
557 571
558 result = acpi_power_get_list_state(list, &list_state); 572 result = acpi_power_get_list_state(list, &list_state);
@@ -571,9 +585,12 @@ int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
571 585
572int acpi_power_on_resources(struct acpi_device *device, int state) 586int acpi_power_on_resources(struct acpi_device *device, int state)
573{ 587{
574 if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3) 588 if (!device || state < ACPI_STATE_D0 || state > ACPI_STATE_D3_COLD)
575 return -EINVAL; 589 return -EINVAL;
576 590
591 if (state == ACPI_STATE_D3_COLD)
592 return 0;
593
577 return acpi_power_on_list(&device->power.states[state].resources); 594 return acpi_power_on_list(&device->power.states[state].resources);
578} 595}
579 596
@@ -584,7 +601,7 @@ int acpi_power_transition(struct acpi_device *device, int state)
584 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD)) 601 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
585 return -EINVAL; 602 return -EINVAL;
586 603
587 if (device->power.state == state) 604 if (device->power.state == state || !device->flags.power_manageable)
588 return 0; 605 return 0;
589 606
590 if ((device->power.state < ACPI_STATE_D0) 607 if ((device->power.state < ACPI_STATE_D0)
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index c7ea9c2649a4..d557868c0081 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -475,11 +475,25 @@ void acpi_free_ids(struct acpi_device *device)
475 kfree(device->pnp.unique_id); 475 kfree(device->pnp.unique_id);
476} 476}
477 477
478static void acpi_free_power_resources_lists(struct acpi_device *device)
479{
480 int i;
481
482 if (!device->flags.power_manageable)
483 return;
484
485 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
486 struct acpi_device_power_state *ps = &device->power.states[i];
487 acpi_power_resources_list_free(&ps->resources);
488 }
489}
490
478static void acpi_device_release(struct device *dev) 491static void acpi_device_release(struct device *dev)
479{ 492{
480 struct acpi_device *acpi_dev = to_acpi_device(dev); 493 struct acpi_device *acpi_dev = to_acpi_device(dev);
481 494
482 acpi_free_ids(acpi_dev); 495 acpi_free_ids(acpi_dev);
496 acpi_free_power_resources_lists(acpi_dev);
483 kfree(acpi_dev); 497 kfree(acpi_dev);
484} 498}
485 499
@@ -1055,17 +1069,22 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
1055 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) { 1069 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
1056 struct acpi_device_power_state *ps = &device->power.states[i]; 1070 struct acpi_device_power_state *ps = &device->power.states[i];
1057 char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' }; 1071 char object_name[5] = { '_', 'P', 'R', '0' + i, '\0' };
1072 struct acpi_handle_list resources;
1058 1073
1074 INIT_LIST_HEAD(&ps->resources);
1059 /* Evaluate "_PRx" to se if power resources are referenced */ 1075 /* Evaluate "_PRx" to se if power resources are referenced */
1060 acpi_evaluate_reference(device->handle, object_name, NULL, 1076 acpi_evaluate_reference(device->handle, object_name, NULL,
1061 &ps->resources); 1077 &resources);
1062 if (ps->resources.count) { 1078 if (resources.count) {
1063 int j; 1079 int j;
1064 1080
1065 device->power.flags.power_resources = 1; 1081 device->power.flags.power_resources = 1;
1066 for (j = 0; j < ps->resources.count; j++) { 1082 for (j = 0; j < resources.count; j++) {
1067 acpi_handle rhandle = ps->resources.handles[j]; 1083 acpi_handle rhandle = resources.handles[j];
1084
1068 acpi_add_power_resource(rhandle); 1085 acpi_add_power_resource(rhandle);
1086 acpi_power_resources_list_add(rhandle,
1087 &ps->resources);
1069 } 1088 }
1070 } 1089 }
1071 1090
@@ -1079,7 +1098,7 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
1079 * State is valid if there are means to put the device into it. 1098 * State is valid if there are means to put the device into it.
1080 * D3hot is only valid if _PR3 present. 1099 * D3hot is only valid if _PR3 present.
1081 */ 1100 */
1082 if (ps->resources.count || 1101 if (resources.count ||
1083 (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) { 1102 (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) {
1084 ps->flags.valid = 1; 1103 ps->flags.valid = 1;
1085 ps->flags.os_accessible = 1; 1104 ps->flags.os_accessible = 1;
@@ -1089,6 +1108,8 @@ static void acpi_bus_get_power_flags(struct acpi_device *device)
1089 ps->latency = -1; /* Unknown - driver assigned */ 1108 ps->latency = -1; /* Unknown - driver assigned */
1090 } 1109 }
1091 1110
1111 INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
1112
1092 /* Set defaults for D0 and D3 states (always valid) */ 1113 /* Set defaults for D0 and D3 states (always valid) */
1093 device->power.states[ACPI_STATE_D0].flags.valid = 1; 1114 device->power.states[ACPI_STATE_D0].flags.valid = 1;
1094 device->power.states[ACPI_STATE_D0].power = 100; 1115 device->power.states[ACPI_STATE_D0].power = 100;