diff options
author | Konstantin Karasyov <konstantin.a.karasyov@intel.com> | 2007-02-16 01:47:06 -0500 |
---|---|---|
committer | Len Brown <len.brown@intel.com> | 2007-02-16 01:47:06 -0500 |
commit | 0a6139027f3986162233adc17285151e78b39cac (patch) | |
tree | a8df80c919bc5f05c51b91a8708436fc2c9933df /drivers/acpi/power.c | |
parent | 724339d76d9407cd1a8ad32a9c1fdf64840cc51b (diff) |
ACPI: Thermal issues on HP nx6325
The previous reference counting scheme to enable power resources
got confused when multiple devices were present that might
repeatedly enable or disable the resource and throw off the count.
The new code simply lists the referencing devices which
are requesting the resource to be enabled. When there are none,
then it is off.
Signed-off-by: Konstantin Karasyov <konstantin.a.karasyov@intel.com>
Signed-off-by: Len Brown <len.brown@intel.com>
Diffstat (limited to 'drivers/acpi/power.c')
-rw-r--r-- | drivers/acpi/power.c | 147 |
1 files changed, 126 insertions, 21 deletions
diff --git a/drivers/acpi/power.c b/drivers/acpi/power.c index 0ba7dfbbb2ee..784cbdb1084b 100644 --- a/drivers/acpi/power.c +++ b/drivers/acpi/power.c | |||
@@ -57,6 +57,7 @@ ACPI_MODULE_NAME("acpi_power") | |||
57 | #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF | 57 | #define ACPI_POWER_RESOURCE_STATE_UNKNOWN 0xFF |
58 | static int acpi_power_add(struct acpi_device *device); | 58 | static int acpi_power_add(struct acpi_device *device); |
59 | static int acpi_power_remove(struct acpi_device *device, int type); | 59 | static int acpi_power_remove(struct acpi_device *device, int type); |
60 | static int acpi_power_resume(struct acpi_device *device, int state); | ||
60 | static int acpi_power_open_fs(struct inode *inode, struct file *file); | 61 | static int acpi_power_open_fs(struct inode *inode, struct file *file); |
61 | 62 | ||
62 | static struct acpi_driver acpi_power_driver = { | 63 | static struct acpi_driver acpi_power_driver = { |
@@ -66,16 +67,23 @@ static struct acpi_driver acpi_power_driver = { | |||
66 | .ops = { | 67 | .ops = { |
67 | .add = acpi_power_add, | 68 | .add = acpi_power_add, |
68 | .remove = acpi_power_remove, | 69 | .remove = acpi_power_remove, |
70 | .resume = acpi_power_resume, | ||
69 | }, | 71 | }, |
70 | }; | 72 | }; |
71 | 73 | ||
74 | struct acpi_power_reference { | ||
75 | struct list_head node; | ||
76 | struct acpi_device *device; | ||
77 | }; | ||
78 | |||
72 | struct acpi_power_resource { | 79 | struct acpi_power_resource { |
73 | struct acpi_device * device; | 80 | struct acpi_device * device; |
74 | acpi_bus_id name; | 81 | acpi_bus_id name; |
75 | u32 system_level; | 82 | u32 system_level; |
76 | u32 order; | 83 | u32 order; |
77 | int state; | 84 | int state; |
78 | int references; | 85 | struct mutex resource_lock; |
86 | struct list_head reference; | ||
79 | }; | 87 | }; |
80 | 88 | ||
81 | static struct list_head acpi_power_resource_list; | 89 | static struct list_head acpi_power_resource_list; |
@@ -171,22 +179,47 @@ static int acpi_power_get_list_state(struct acpi_handle_list *list, int *state) | |||
171 | return result; | 179 | return result; |
172 | } | 180 | } |
173 | 181 | ||
174 | static int acpi_power_on(acpi_handle handle) | 182 | static int acpi_power_on(acpi_handle handle, struct acpi_device *dev) |
175 | { | 183 | { |
176 | int result = 0; | 184 | int result = 0; |
185 | int found = 0; | ||
177 | acpi_status status = AE_OK; | 186 | acpi_status status = AE_OK; |
178 | struct acpi_device *device = NULL; | ||
179 | struct acpi_power_resource *resource = NULL; | 187 | struct acpi_power_resource *resource = NULL; |
188 | struct list_head *node, *next; | ||
189 | struct acpi_power_reference *ref; | ||
180 | 190 | ||
181 | 191 | ||
182 | result = acpi_power_get_context(handle, &resource); | 192 | result = acpi_power_get_context(handle, &resource); |
183 | if (result) | 193 | if (result) |
184 | return result; | 194 | return result; |
185 | 195 | ||
186 | resource->references++; | 196 | mutex_lock(&resource->resource_lock); |
197 | list_for_each_safe(node, next, &resource->reference) { | ||
198 | ref = container_of(node, struct acpi_power_reference, node); | ||
199 | if (dev->handle == ref->device->handle) { | ||
200 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] already referenced by resource [%s]\n", | ||
201 | dev->pnp.bus_id, resource->name)); | ||
202 | found = 1; | ||
203 | break; | ||
204 | } | ||
205 | } | ||
206 | |||
207 | if (!found) { | ||
208 | ref = kmalloc(sizeof (struct acpi_power_reference), | ||
209 | irqs_disabled() ? GFP_ATOMIC : GFP_KERNEL); | ||
210 | if (!ref) { | ||
211 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "kmalloc() failed\n")); | ||
212 | mutex_unlock(&resource->resource_lock); | ||
213 | return -ENOMEM; | ||
214 | } | ||
215 | list_add_tail(&ref->node, &resource->reference); | ||
216 | ref->device = dev; | ||
217 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] added to resource [%s] references\n", | ||
218 | dev->pnp.bus_id, resource->name)); | ||
219 | } | ||
220 | mutex_unlock(&resource->resource_lock); | ||
187 | 221 | ||
188 | if ((resource->references > 1) | 222 | if (resource->state == ACPI_POWER_RESOURCE_STATE_ON) { |
189 | || (resource->state == ACPI_POWER_RESOURCE_STATE_ON)) { | ||
190 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", | 223 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already on\n", |
191 | resource->name)); | 224 | resource->name)); |
192 | return 0; | 225 | return 0; |
@@ -203,38 +236,49 @@ static int acpi_power_on(acpi_handle handle) | |||
203 | return -ENOEXEC; | 236 | return -ENOEXEC; |
204 | 237 | ||
205 | /* Update the power resource's _device_ power state */ | 238 | /* Update the power resource's _device_ power state */ |
206 | device = resource->device; | ||
207 | resource->device->power.state = ACPI_STATE_D0; | 239 | resource->device->power.state = ACPI_STATE_D0; |
208 | 240 | ||
209 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", | 241 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] turned on\n", |
210 | resource->name)); | 242 | resource->name)); |
211 | |||
212 | return 0; | 243 | return 0; |
213 | } | 244 | } |
214 | 245 | ||
215 | static int acpi_power_off_device(acpi_handle handle) | 246 | static int acpi_power_off_device(acpi_handle handle, struct acpi_device *dev) |
216 | { | 247 | { |
217 | int result = 0; | 248 | int result = 0; |
218 | acpi_status status = AE_OK; | 249 | acpi_status status = AE_OK; |
219 | struct acpi_power_resource *resource = NULL; | 250 | struct acpi_power_resource *resource = NULL; |
251 | struct list_head *node, *next; | ||
252 | struct acpi_power_reference *ref; | ||
253 | |||
220 | 254 | ||
221 | result = acpi_power_get_context(handle, &resource); | 255 | result = acpi_power_get_context(handle, &resource); |
222 | if (result) | 256 | if (result) |
223 | return result; | 257 | return result; |
224 | 258 | ||
225 | if (resource->references) | 259 | mutex_lock(&resource->resource_lock); |
226 | resource->references--; | 260 | list_for_each_safe(node, next, &resource->reference) { |
261 | ref = container_of(node, struct acpi_power_reference, node); | ||
262 | if (dev->handle == ref->device->handle) { | ||
263 | list_del(&ref->node); | ||
264 | kfree(ref); | ||
265 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device [%s] removed from resource [%s] references\n", | ||
266 | dev->pnp.bus_id, resource->name)); | ||
267 | break; | ||
268 | } | ||
269 | } | ||
227 | 270 | ||
228 | if (resource->references) { | 271 | if (!list_empty(&resource->reference)) { |
229 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 272 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Cannot turn resource [%s] off - resource is in use\n", |
230 | "Resource [%s] is still in use, dereferencing\n", | 273 | resource->name)); |
231 | resource->device->pnp.bus_id)); | 274 | mutex_unlock(&resource->resource_lock); |
232 | return 0; | 275 | return 0; |
233 | } | 276 | } |
277 | mutex_unlock(&resource->resource_lock); | ||
234 | 278 | ||
235 | if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { | 279 | if (resource->state == ACPI_POWER_RESOURCE_STATE_OFF) { |
236 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", | 280 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Resource [%s] already off\n", |
237 | resource->device->pnp.bus_id)); | 281 | resource->name)); |
238 | return 0; | 282 | return 0; |
239 | } | 283 | } |
240 | 284 | ||
@@ -276,7 +320,7 @@ int acpi_enable_wakeup_device_power(struct acpi_device *dev) | |||
276 | arg.integer.value = 1; | 320 | arg.integer.value = 1; |
277 | /* Open power resource */ | 321 | /* Open power resource */ |
278 | for (i = 0; i < dev->wakeup.resources.count; i++) { | 322 | for (i = 0; i < dev->wakeup.resources.count; i++) { |
279 | ret = acpi_power_on(dev->wakeup.resources.handles[i]); | 323 | ret = acpi_power_on(dev->wakeup.resources.handles[i], dev); |
280 | if (ret) { | 324 | if (ret) { |
281 | printk(KERN_ERR PREFIX "Transition power state\n"); | 325 | printk(KERN_ERR PREFIX "Transition power state\n"); |
282 | dev->wakeup.flags.valid = 0; | 326 | dev->wakeup.flags.valid = 0; |
@@ -323,7 +367,7 @@ int acpi_disable_wakeup_device_power(struct acpi_device *dev) | |||
323 | 367 | ||
324 | /* Close power resource */ | 368 | /* Close power resource */ |
325 | for (i = 0; i < dev->wakeup.resources.count; i++) { | 369 | for (i = 0; i < dev->wakeup.resources.count; i++) { |
326 | ret = acpi_power_off_device(dev->wakeup.resources.handles[i]); | 370 | ret = acpi_power_off_device(dev->wakeup.resources.handles[i], dev); |
327 | if (ret) { | 371 | if (ret) { |
328 | printk(KERN_ERR PREFIX "Transition power state\n"); | 372 | printk(KERN_ERR PREFIX "Transition power state\n"); |
329 | dev->wakeup.flags.valid = 0; | 373 | dev->wakeup.flags.valid = 0; |
@@ -407,7 +451,7 @@ int acpi_power_transition(struct acpi_device *device, int state) | |||
407 | * (e.g. so the device doesn't lose power while transitioning). | 451 | * (e.g. so the device doesn't lose power while transitioning). |
408 | */ | 452 | */ |
409 | for (i = 0; i < tl->count; i++) { | 453 | for (i = 0; i < tl->count; i++) { |
410 | result = acpi_power_on(tl->handles[i]); | 454 | result = acpi_power_on(tl->handles[i], device); |
411 | if (result) | 455 | if (result) |
412 | goto end; | 456 | goto end; |
413 | } | 457 | } |
@@ -416,7 +460,7 @@ int acpi_power_transition(struct acpi_device *device, int state) | |||
416 | * Then we dereference all power resources used in the current list. | 460 | * Then we dereference all power resources used in the current list. |
417 | */ | 461 | */ |
418 | for (i = 0; i < cl->count; i++) { | 462 | for (i = 0; i < cl->count; i++) { |
419 | result = acpi_power_off_device(cl->handles[i]); | 463 | result = acpi_power_off_device(cl->handles[i], device); |
420 | if (result) | 464 | if (result) |
421 | goto end; | 465 | goto end; |
422 | } | 466 | } |
@@ -439,7 +483,11 @@ static struct proc_dir_entry *acpi_power_dir; | |||
439 | 483 | ||
440 | static int acpi_power_seq_show(struct seq_file *seq, void *offset) | 484 | static int acpi_power_seq_show(struct seq_file *seq, void *offset) |
441 | { | 485 | { |
486 | int count = 0; | ||
487 | int result = 0; | ||
442 | struct acpi_power_resource *resource = NULL; | 488 | struct acpi_power_resource *resource = NULL; |
489 | struct list_head *node, *next; | ||
490 | struct acpi_power_reference *ref; | ||
443 | 491 | ||
444 | 492 | ||
445 | resource = seq->private; | 493 | resource = seq->private; |
@@ -447,6 +495,10 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) | |||
447 | if (!resource) | 495 | if (!resource) |
448 | goto end; | 496 | goto end; |
449 | 497 | ||
498 | result = acpi_power_get_state(resource); | ||
499 | if (result) | ||
500 | goto end; | ||
501 | |||
450 | seq_puts(seq, "state: "); | 502 | seq_puts(seq, "state: "); |
451 | switch (resource->state) { | 503 | switch (resource->state) { |
452 | case ACPI_POWER_RESOURCE_STATE_ON: | 504 | case ACPI_POWER_RESOURCE_STATE_ON: |
@@ -460,11 +512,18 @@ static int acpi_power_seq_show(struct seq_file *seq, void *offset) | |||
460 | break; | 512 | break; |
461 | } | 513 | } |
462 | 514 | ||
515 | mutex_lock(&resource->resource_lock); | ||
516 | list_for_each_safe(node, next, &resource->reference) { | ||
517 | ref = container_of(node, struct acpi_power_reference, node); | ||
518 | count++; | ||
519 | } | ||
520 | mutex_unlock(&resource->resource_lock); | ||
521 | |||
463 | seq_printf(seq, "system level: S%d\n" | 522 | seq_printf(seq, "system level: S%d\n" |
464 | "order: %d\n" | 523 | "order: %d\n" |
465 | "reference count: %d\n", | 524 | "reference count: %d\n", |
466 | resource->system_level, | 525 | resource->system_level, |
467 | resource->order, resource->references); | 526 | resource->order, count); |
468 | 527 | ||
469 | end: | 528 | end: |
470 | return 0; | 529 | return 0; |
@@ -537,6 +596,8 @@ static int acpi_power_add(struct acpi_device *device) | |||
537 | return -ENOMEM; | 596 | return -ENOMEM; |
538 | 597 | ||
539 | resource->device = device; | 598 | resource->device = device; |
599 | mutex_init(&resource->resource_lock); | ||
600 | INIT_LIST_HEAD(&resource->reference); | ||
540 | strcpy(resource->name, device->pnp.bus_id); | 601 | strcpy(resource->name, device->pnp.bus_id); |
541 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); | 602 | strcpy(acpi_device_name(device), ACPI_POWER_DEVICE_NAME); |
542 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); | 603 | strcpy(acpi_device_class(device), ACPI_POWER_CLASS); |
@@ -584,6 +645,7 @@ static int acpi_power_add(struct acpi_device *device) | |||
584 | static int acpi_power_remove(struct acpi_device *device, int type) | 645 | static int acpi_power_remove(struct acpi_device *device, int type) |
585 | { | 646 | { |
586 | struct acpi_power_resource *resource = NULL; | 647 | struct acpi_power_resource *resource = NULL; |
648 | struct list_head *node, *next; | ||
587 | 649 | ||
588 | 650 | ||
589 | if (!device || !acpi_driver_data(device)) | 651 | if (!device || !acpi_driver_data(device)) |
@@ -593,11 +655,54 @@ static int acpi_power_remove(struct acpi_device *device, int type) | |||
593 | 655 | ||
594 | acpi_power_remove_fs(device); | 656 | acpi_power_remove_fs(device); |
595 | 657 | ||
658 | mutex_lock(&resource->resource_lock); | ||
659 | list_for_each_safe(node, next, &resource->reference) { | ||
660 | struct acpi_power_reference *ref = container_of(node, struct acpi_power_reference, node); | ||
661 | list_del(&ref->node); | ||
662 | kfree(ref); | ||
663 | } | ||
664 | mutex_unlock(&resource->resource_lock); | ||
665 | |||
596 | kfree(resource); | 666 | kfree(resource); |
597 | 667 | ||
598 | return 0; | 668 | return 0; |
599 | } | 669 | } |
600 | 670 | ||
671 | static int acpi_power_resume(struct acpi_device *device, int state) | ||
672 | { | ||
673 | int result = 0; | ||
674 | struct acpi_power_resource *resource = NULL; | ||
675 | struct acpi_power_reference *ref; | ||
676 | |||
677 | if (!device || !acpi_driver_data(device)) | ||
678 | return -EINVAL; | ||
679 | |||
680 | resource = (struct acpi_power_resource *)acpi_driver_data(device); | ||
681 | |||
682 | result = acpi_power_get_state(resource); | ||
683 | if (result) | ||
684 | return result; | ||
685 | |||
686 | mutex_lock(&resource->resource_lock); | ||
687 | if ((resource->state == ACPI_POWER_RESOURCE_STATE_ON) && | ||
688 | list_empty(&resource->reference)) { | ||
689 | mutex_unlock(&resource->resource_lock); | ||
690 | result = acpi_power_off_device(device->handle, NULL); | ||
691 | return result; | ||
692 | } | ||
693 | |||
694 | if ((resource->state == ACPI_POWER_RESOURCE_STATE_OFF) && | ||
695 | !list_empty(&resource->reference)) { | ||
696 | ref = container_of(resource->reference.next, struct acpi_power_reference, node); | ||
697 | mutex_unlock(&resource->resource_lock); | ||
698 | result = acpi_power_on(device->handle, ref->device); | ||
699 | return result; | ||
700 | } | ||
701 | |||
702 | mutex_unlock(&resource->resource_lock); | ||
703 | return 0; | ||
704 | } | ||
705 | |||
601 | static int __init acpi_power_init(void) | 706 | static int __init acpi_power_init(void) |
602 | { | 707 | { |
603 | int result = 0; | 708 | int result = 0; |