aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/acpi/power.c
diff options
context:
space:
mode:
authorKonstantin Karasyov <konstantin.a.karasyov@intel.com>2007-02-16 01:47:06 -0500
committerLen Brown <len.brown@intel.com>2007-02-16 01:47:06 -0500
commit0a6139027f3986162233adc17285151e78b39cac (patch)
treea8df80c919bc5f05c51b91a8708436fc2c9933df /drivers/acpi/power.c
parent724339d76d9407cd1a8ad32a9c1fdf64840cc51b (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.c147
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
58static int acpi_power_add(struct acpi_device *device); 58static int acpi_power_add(struct acpi_device *device);
59static int acpi_power_remove(struct acpi_device *device, int type); 59static int acpi_power_remove(struct acpi_device *device, int type);
60static int acpi_power_resume(struct acpi_device *device, int state);
60static int acpi_power_open_fs(struct inode *inode, struct file *file); 61static int acpi_power_open_fs(struct inode *inode, struct file *file);
61 62
62static struct acpi_driver acpi_power_driver = { 63static 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
74struct acpi_power_reference {
75 struct list_head node;
76 struct acpi_device *device;
77};
78
72struct acpi_power_resource { 79struct 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
81static struct list_head acpi_power_resource_list; 89static 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
174static int acpi_power_on(acpi_handle handle) 182static 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
215static int acpi_power_off_device(acpi_handle handle) 246static 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
440static int acpi_power_seq_show(struct seq_file *seq, void *offset) 484static 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)
584static int acpi_power_remove(struct acpi_device *device, int type) 645static 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
671static 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
601static int __init acpi_power_init(void) 706static int __init acpi_power_init(void)
602{ 707{
603 int result = 0; 708 int result = 0;