Commit 0b224527323669c66e0a37ae05b04034bfcdce14

Authored by Rafael J. Wysocki
1 parent 722c929f32

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>

Showing 4 changed files with 118 additions and 78 deletions Side-by-side Diff

drivers/acpi/internal.h
... ... @@ -50,6 +50,8 @@
50 50 Power Resource
51 51 -------------------------------------------------------------------------- */
52 52 int acpi_power_init(void);
  53 +void acpi_power_resources_list_add(acpi_handle handle, struct list_head *list);
  54 +void acpi_power_resources_list_free(struct list_head *list);
53 55 void acpi_add_power_resource(acpi_handle handle);
54 56 void acpi_power_add_remove_device(struct acpi_device *adev, bool add);
55 57 int acpi_device_sleep_wake(struct acpi_device *dev,
drivers/acpi/power.c
... ... @@ -75,6 +75,11 @@
75 75 struct mutex resource_lock;
76 76 };
77 77  
  78 +struct acpi_power_resource_entry {
  79 + struct list_head node;
  80 + struct acpi_power_resource *resource;
  81 +};
  82 +
78 83 static LIST_HEAD(acpi_power_resource_list);
79 84 static DEFINE_MUTEX(power_resource_list_lock);
80 85  
... ... @@ -92,6 +97,41 @@
92 97 return container_of(device, struct acpi_power_resource, device);
93 98 }
94 99  
  100 +void 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 +
  125 +void 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 +
95 135 static int acpi_power_get_state(acpi_handle handle, int *state)
96 136 {
97 137 acpi_status status = AE_OK;
98 138  
99 139  
100 140  
101 141  
102 142  
103 143  
104 144  
... ... @@ -119,31 +159,23 @@
119 159 return 0;
120 160 }
121 161  
122   -static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state)
  162 +static int acpi_power_get_list_state(struct list_head *list, int *state)
123 163 {
  164 + struct acpi_power_resource_entry *entry;
124 165 int cur_state;
125   - int i = 0;
126 166  
127 167 if (!list || !state)
128 168 return -EINVAL;
129 169  
130 170 /* The state of the list is 'on' IFF all resources are 'on'. */
131   -
132   - for (i = 0; i < list->count; i++) {
133   - struct acpi_power_resource *resource;
134   - acpi_handle handle = list->handles[i];
  171 + list_for_each_entry(entry, list, node) {
  172 + struct acpi_power_resource *resource = entry->resource;
  173 + acpi_handle handle = resource->device.handle;
135 174 int result;
136 175  
137   - resource = acpi_power_get_context(handle);
138   - if (!resource)
139   - return -ENODEV;
140   -
141 176 mutex_lock(&resource->resource_lock);
142   -
143 177 result = acpi_power_get_state(handle, &cur_state);
144   -
145 178 mutex_unlock(&resource->resource_lock);
146   -
147 179 if (result)
148 180 return result;
149 181  
... ... @@ -155,7 +187,6 @@
155 187 cur_state ? "on" : "off"));
156 188  
157 189 *state = cur_state;
158   -
159 190 return 0;
160 191 }
161 192  
162 193  
163 194  
... ... @@ -199,15 +230,10 @@
199 230 return 0;
200 231 }
201 232  
202   -static int acpi_power_on(acpi_handle handle)
  233 +static int acpi_power_on(struct acpi_power_resource *resource)
203 234 {
204   - int result = 0;
205   - struct acpi_power_resource *resource;
  235 + int result = 0;;
206 236  
207   - resource = acpi_power_get_context(handle);
208   - if (!resource)
209   - return -ENODEV;
210   -
211 237 mutex_lock(&resource->resource_lock);
212 238  
213 239 if (resource->ref_count++) {
214 240  
215 241  
216 242  
... ... @@ -231,16 +257,11 @@
231 257 return result;
232 258 }
233 259  
234   -static int acpi_power_off(acpi_handle handle)
  260 +static int acpi_power_off(struct acpi_power_resource *resource)
235 261 {
236   - int result = 0;
237 262 acpi_status status = AE_OK;
238   - struct acpi_power_resource *resource;
  263 + int result = 0;
239 264  
240   - resource = acpi_power_get_context(handle);
241   - if (!resource)
242   - return -ENODEV;
243   -
244 265 mutex_lock(&resource->resource_lock);
245 266  
246 267 if (!resource->ref_count) {
247 268  
248 269  
249 270  
250 271  
251 272  
252 273  
253 274  
254 275  
255 276  
256 277  
257 278  
258 279  
... ... @@ -271,48 +292,49 @@
271 292 return result;
272 293 }
273 294  
274   -static void __acpi_power_off_list(struct acpi_handle_list *list, int num_res)
  295 +static 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--)
279   - acpi_power_off(list->handles[i]);
280   -}
  300 + list_for_each_entry_reverse(entry, list, node) {
  301 + result = acpi_power_off(entry->resource);
  302 + if (result)
  303 + goto err;
  304 + }
  305 + return 0;
281 306  
282   -static void acpi_power_off_list(struct acpi_handle_list *list)
283   -{
284   - __acpi_power_off_list(list, list->count);
  307 + err:
  308 + list_for_each_entry_continue(entry, list, node)
  309 + acpi_power_on(entry->resource);
  310 +
  311 + return result;
285 312 }
286 313  
287   -static int acpi_power_on_list(struct acpi_handle_list *list)
  314 +static int acpi_power_on_list(struct list_head *list)
288 315 {
  316 + struct acpi_power_resource_entry *entry;
289 317 int result = 0;
290   - int i;
291 318  
292   - for (i = 0; i < list->count; i++) {
293   - result = acpi_power_on(list->handles[i]);
294   - if (result) {
295   - __acpi_power_off_list(list, i);
296   - break;
297   - }
  319 + list_for_each_entry(entry, list, node) {
  320 + result = acpi_power_on(entry->resource);
  321 + if (result)
  322 + goto err;
298 323 }
  324 + return 0;
299 325  
  326 + err:
  327 + list_for_each_entry_continue_reverse(entry, list, node)
  328 + acpi_power_off(entry->resource);
  329 +
300 330 return result;
301 331 }
302 332  
303   -static void acpi_power_add_dependent(acpi_handle rhandle,
  333 +static void acpi_power_add_dependent(struct acpi_power_resource *resource,
304 334 struct acpi_device *adev)
305 335 {
306 336 struct acpi_power_dependent_device *dep;
307   - struct acpi_power_resource *resource;
308 337  
309   - if (!rhandle || !adev)
310   - return;
311   -
312   - resource = acpi_power_get_context(rhandle);
313   - if (!resource)
314   - return;
315   -
316 338 mutex_lock(&resource->resource_lock);
317 339  
318 340 list_for_each_entry(dep, &resource->dependent, node)
319 341  
320 342  
... ... @@ -331,20 +353,12 @@
331 353 mutex_unlock(&resource->resource_lock);
332 354 }
333 355  
334   -static void acpi_power_remove_dependent(acpi_handle rhandle,
  356 +static void acpi_power_remove_dependent(struct acpi_power_resource *resource,
335 357 struct acpi_device *adev)
336 358 {
337 359 struct acpi_power_dependent_device *dep;
338   - struct acpi_power_resource *resource;
339 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 362 mutex_lock(&resource->resource_lock);
349 363  
350 364 list_for_each_entry(dep, &resource->dependent, node)
351 365  
352 366  
353 367  
... ... @@ -366,16 +380,16 @@
366 380 {
367 381 if (adev->power.flags.power_resources) {
368 382 struct acpi_device_power_state *ps;
369   - int j;
  383 + struct acpi_power_resource_entry *entry;
370 384  
371 385 ps = &adev->power.states[ACPI_STATE_D0];
372   - for (j = 0; j < ps->resources.count; j++) {
373   - acpi_handle rhandle = ps->resources.handles[j];
  386 + list_for_each_entry(entry, &ps->resources, node) {
  387 + struct acpi_power_resource *resource = entry->resource;
374 388  
375 389 if (add)
376   - acpi_power_add_dependent(rhandle, adev);
  390 + acpi_power_add_dependent(resource, adev);
377 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 @@
539 553 int acpi_power_get_inferred_state(struct acpi_device *device, int *state)
540 554 {
541 555 int result = 0;
542   - struct acpi_handle_list *list = NULL;
543 556 int list_state = 0;
544 557 int i = 0;
545 558  
... ... @@ -551,8 +564,9 @@
551 564 * required for a given D-state are 'on'.
552 565 */
553 566 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
554   - list = &device->power.states[i].resources;
555   - if (list->count < 1)
  567 + struct list_head *list = &device->power.states[i].resources;
  568 +
  569 + if (list_empty(list))
556 570 continue;
557 571  
558 572 result = acpi_power_get_list_state(list, &list_state);
559 573  
... ... @@ -571,9 +585,12 @@
571 585  
572 586 int 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 589 return -EINVAL;
576 590  
  591 + if (state == ACPI_STATE_D3_COLD)
  592 + return 0;
  593 +
577 594 return acpi_power_on_list(&device->power.states[state].resources);
578 595 }
579 596  
... ... @@ -584,7 +601,7 @@
584 601 if (!device || (state < ACPI_STATE_D0) || (state > ACPI_STATE_D3_COLD))
585 602 return -EINVAL;
586 603  
587   - if (device->power.state == state)
  604 + if (device->power.state == state || !device->flags.power_manageable)
588 605 return 0;
589 606  
590 607 if ((device->power.state < ACPI_STATE_D0)
... ... @@ -475,11 +475,25 @@
475 475 kfree(device->pnp.unique_id);
476 476 }
477 477  
  478 +static 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 +
478 491 static void acpi_device_release(struct device *dev)
479 492 {
480 493 struct acpi_device *acpi_dev = to_acpi_device(dev);
481 494  
482 495 acpi_free_ids(acpi_dev);
  496 + acpi_free_power_resources_lists(acpi_dev);
483 497 kfree(acpi_dev);
484 498 }
485 499  
486 500  
487 501  
488 502  
489 503  
... ... @@ -1055,17 +1069,22 @@
1055 1069 for (i = ACPI_STATE_D0; i <= ACPI_STATE_D3_HOT; i++) {
1056 1070 struct acpi_device_power_state *ps = &device->power.states[i];
1057 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 1075 /* Evaluate "_PRx" to se if power resources are referenced */
1060 1076 acpi_evaluate_reference(device->handle, object_name, NULL,
1061   - &ps->resources);
1062   - if (ps->resources.count) {
  1077 + &resources);
  1078 + if (resources.count) {
1063 1079 int j;
1064 1080  
1065 1081 device->power.flags.power_resources = 1;
1066   - for (j = 0; j < ps->resources.count; j++) {
1067   - acpi_handle rhandle = ps->resources.handles[j];
  1082 + for (j = 0; j < resources.count; j++) {
  1083 + acpi_handle rhandle = resources.handles[j];
  1084 +
1068 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 @@
1079 1098 * State is valid if there are means to put the device into it.
1080 1099 * D3hot is only valid if _PR3 present.
1081 1100 */
1082   - if (ps->resources.count ||
  1101 + if (resources.count ||
1083 1102 (ps->flags.explicit_set && i < ACPI_STATE_D3_HOT)) {
1084 1103 ps->flags.valid = 1;
1085 1104 ps->flags.os_accessible = 1;
... ... @@ -1088,6 +1107,8 @@
1088 1107 ps->power = -1; /* Unknown - driver assigned */
1089 1108 ps->latency = -1; /* Unknown - driver assigned */
1090 1109 }
  1110 +
  1111 + INIT_LIST_HEAD(&device->power.states[ACPI_STATE_D3_COLD].resources);
1091 1112  
1092 1113 /* Set defaults for D0 and D3 states (always valid) */
1093 1114 device->power.states[ACPI_STATE_D0].flags.valid = 1;
include/acpi/acpi_bus.h
... ... @@ -199,7 +199,7 @@
199 199 } flags;
200 200 int power; /* % Power (compared to D0) */
201 201 int latency; /* Dx->D0 time (microseconds) */
202   - struct acpi_handle_list resources; /* Power resources referenced */
  202 + struct list_head resources; /* Power resources referenced */
203 203 };
204 204  
205 205 struct acpi_device_power {