diff options
Diffstat (limited to 'drivers/acpi/thermal.c')
-rw-r--r-- | drivers/acpi/thermal.c | 646 |
1 files changed, 492 insertions, 154 deletions
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 3a0af9a8cd27..8d4b79b4f933 100644 --- a/drivers/acpi/thermal.c +++ b/drivers/acpi/thermal.c | |||
@@ -43,7 +43,7 @@ | |||
43 | #include <linux/seq_file.h> | 43 | #include <linux/seq_file.h> |
44 | #include <linux/reboot.h> | 44 | #include <linux/reboot.h> |
45 | #include <asm/uaccess.h> | 45 | #include <asm/uaccess.h> |
46 | 46 | #include <linux/thermal.h> | |
47 | #include <acpi/acpi_bus.h> | 47 | #include <acpi/acpi_bus.h> |
48 | #include <acpi/acpi_drivers.h> | 48 | #include <acpi/acpi_drivers.h> |
49 | 49 | ||
@@ -65,9 +65,6 @@ | |||
65 | #define ACPI_THERMAL_MAX_ACTIVE 10 | 65 | #define ACPI_THERMAL_MAX_ACTIVE 10 |
66 | #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 | 66 | #define ACPI_THERMAL_MAX_LIMIT_STR_LEN 65 |
67 | 67 | ||
68 | #define KELVIN_TO_CELSIUS(t) (long)(((long)t-2732>=0) ? ((long)t-2732+5)/10 : ((long)t-2732-5)/10) | ||
69 | #define CELSIUS_TO_KELVIN(t) ((t+273)*10) | ||
70 | |||
71 | #define _COMPONENT ACPI_THERMAL_COMPONENT | 68 | #define _COMPONENT ACPI_THERMAL_COMPONENT |
72 | ACPI_MODULE_NAME("thermal"); | 69 | ACPI_MODULE_NAME("thermal"); |
73 | 70 | ||
@@ -195,6 +192,8 @@ struct acpi_thermal { | |||
195 | struct acpi_thermal_trips trips; | 192 | struct acpi_thermal_trips trips; |
196 | struct acpi_handle_list devices; | 193 | struct acpi_handle_list devices; |
197 | struct timer_list timer; | 194 | struct timer_list timer; |
195 | struct thermal_zone_device *thermal_zone; | ||
196 | int tz_enabled; | ||
198 | struct mutex lock; | 197 | struct mutex lock; |
199 | }; | 198 | }; |
200 | 199 | ||
@@ -321,173 +320,221 @@ static int acpi_thermal_set_cooling_mode(struct acpi_thermal *tz, int mode) | |||
321 | return 0; | 320 | return 0; |
322 | } | 321 | } |
323 | 322 | ||
324 | static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) | 323 | #define ACPI_TRIPS_CRITICAL 0x01 |
325 | { | 324 | #define ACPI_TRIPS_HOT 0x02 |
326 | acpi_status status = AE_OK; | 325 | #define ACPI_TRIPS_PASSIVE 0x04 |
327 | int i = 0; | 326 | #define ACPI_TRIPS_ACTIVE 0x08 |
327 | #define ACPI_TRIPS_DEVICES 0x10 | ||
328 | 328 | ||
329 | #define ACPI_TRIPS_REFRESH_THRESHOLDS (ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE) | ||
330 | #define ACPI_TRIPS_REFRESH_DEVICES ACPI_TRIPS_DEVICES | ||
329 | 331 | ||
330 | if (!tz) | 332 | #define ACPI_TRIPS_INIT (ACPI_TRIPS_CRITICAL | ACPI_TRIPS_HOT | \ |
331 | return -EINVAL; | 333 | ACPI_TRIPS_PASSIVE | ACPI_TRIPS_ACTIVE | \ |
334 | ACPI_TRIPS_DEVICES) | ||
332 | 335 | ||
333 | /* Critical Shutdown (required) */ | 336 | /* |
334 | 337 | * This exception is thrown out in two cases: | |
335 | status = acpi_evaluate_integer(tz->device->handle, "_CRT", NULL, | 338 | * 1.An invalid trip point becomes invalid or a valid trip point becomes invalid |
336 | &tz->trips.critical.temperature); | 339 | * when re-evaluating the AML code. |
337 | if (ACPI_FAILURE(status)) { | 340 | * 2.TODO: Devices listed in _PSL, _ALx, _TZD may change. |
338 | tz->trips.critical.flags.valid = 0; | 341 | * We need to re-bind the cooling devices of a thermal zone when this occurs. |
339 | ACPI_EXCEPTION((AE_INFO, status, "No critical threshold")); | 342 | */ |
340 | return -ENODEV; | 343 | #define ACPI_THERMAL_TRIPS_EXCEPTION(flags, str) \ |
341 | } else { | 344 | do { \ |
342 | tz->trips.critical.flags.valid = 1; | 345 | if (flags != ACPI_TRIPS_INIT) \ |
343 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 346 | ACPI_EXCEPTION((AE_INFO, AE_ERROR, \ |
344 | "Found critical threshold [%lu]\n", | 347 | "ACPI thermal trip point %s changed\n" \ |
345 | tz->trips.critical.temperature)); | 348 | "Please send acpidump to linux-acpi@vger.kernel.org\n", str)); \ |
346 | } | 349 | } while (0) |
350 | |||
351 | static int acpi_thermal_trips_update(struct acpi_thermal *tz, int flag) | ||
352 | { | ||
353 | acpi_status status = AE_OK; | ||
354 | struct acpi_handle_list devices; | ||
355 | int valid = 0; | ||
356 | int i; | ||
347 | 357 | ||
348 | if (tz->trips.critical.flags.valid == 1) { | 358 | /* Critical Shutdown (required) */ |
349 | if (crt == -1) { | 359 | if (flag & ACPI_TRIPS_CRITICAL) { |
360 | status = acpi_evaluate_integer(tz->device->handle, | ||
361 | "_CRT", NULL, &tz->trips.critical.temperature); | ||
362 | if (ACPI_FAILURE(status)) { | ||
350 | tz->trips.critical.flags.valid = 0; | 363 | tz->trips.critical.flags.valid = 0; |
351 | } else if (crt > 0) { | 364 | ACPI_EXCEPTION((AE_INFO, status, |
352 | unsigned long crt_k = CELSIUS_TO_KELVIN(crt); | 365 | "No critical threshold")); |
353 | 366 | return -ENODEV; | |
354 | /* | 367 | } else { |
355 | * Allow override to lower critical threshold | 368 | tz->trips.critical.flags.valid = 1; |
356 | */ | 369 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, |
357 | if (crt_k < tz->trips.critical.temperature) | 370 | "Found critical threshold [%lu]\n", |
358 | tz->trips.critical.temperature = crt_k; | 371 | tz->trips.critical.temperature)); |
372 | } | ||
373 | if (tz->trips.critical.flags.valid == 1) { | ||
374 | if (crt == -1) { | ||
375 | tz->trips.critical.flags.valid = 0; | ||
376 | } else if (crt > 0) { | ||
377 | unsigned long crt_k = CELSIUS_TO_KELVIN(crt); | ||
378 | /* | ||
379 | * Allow override to lower critical threshold | ||
380 | */ | ||
381 | if (crt_k < tz->trips.critical.temperature) | ||
382 | tz->trips.critical.temperature = crt_k; | ||
383 | } | ||
359 | } | 384 | } |
360 | } | 385 | } |
361 | 386 | ||
362 | /* Critical Sleep (optional) */ | 387 | /* Critical Sleep (optional) */ |
363 | 388 | if (flag & ACPI_TRIPS_HOT) { | |
364 | status = | ||
365 | acpi_evaluate_integer(tz->device->handle, "_HOT", NULL, | ||
366 | &tz->trips.hot.temperature); | ||
367 | if (ACPI_FAILURE(status)) { | ||
368 | tz->trips.hot.flags.valid = 0; | ||
369 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No hot threshold\n")); | ||
370 | } else { | ||
371 | tz->trips.hot.flags.valid = 1; | ||
372 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Found hot threshold [%lu]\n", | ||
373 | tz->trips.hot.temperature)); | ||
374 | } | ||
375 | |||
376 | /* Passive: Processors (optional) */ | ||
377 | |||
378 | if (psv == -1) { | ||
379 | status = AE_SUPPORT; | ||
380 | } else if (psv > 0) { | ||
381 | tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); | ||
382 | status = AE_OK; | ||
383 | } else { | ||
384 | status = acpi_evaluate_integer(tz->device->handle, | 389 | status = acpi_evaluate_integer(tz->device->handle, |
385 | "_PSV", NULL, &tz->trips.passive.temperature); | 390 | "_HOT", NULL, &tz->trips.hot.temperature); |
391 | if (ACPI_FAILURE(status)) { | ||
392 | tz->trips.hot.flags.valid = 0; | ||
393 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
394 | "No hot threshold\n")); | ||
395 | } else { | ||
396 | tz->trips.hot.flags.valid = 1; | ||
397 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
398 | "Found hot threshold [%lu]\n", | ||
399 | tz->trips.critical.temperature)); | ||
400 | } | ||
386 | } | 401 | } |
387 | 402 | ||
388 | if (ACPI_FAILURE(status)) { | 403 | /* Passive (optional) */ |
389 | tz->trips.passive.flags.valid = 0; | 404 | if (flag & ACPI_TRIPS_PASSIVE) { |
390 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No passive threshold\n")); | 405 | valid = tz->trips.passive.flags.valid; |
391 | } else { | 406 | if (psv == -1) { |
392 | tz->trips.passive.flags.valid = 1; | 407 | status = AE_SUPPORT; |
393 | 408 | } else if (psv > 0) { | |
394 | status = | 409 | tz->trips.passive.temperature = CELSIUS_TO_KELVIN(psv); |
395 | acpi_evaluate_integer(tz->device->handle, "_TC1", NULL, | 410 | status = AE_OK; |
396 | &tz->trips.passive.tc1); | 411 | } else { |
397 | if (ACPI_FAILURE(status)) | 412 | status = acpi_evaluate_integer(tz->device->handle, |
398 | tz->trips.passive.flags.valid = 0; | 413 | "_PSV", NULL, &tz->trips.passive.temperature); |
399 | 414 | } | |
400 | status = | ||
401 | acpi_evaluate_integer(tz->device->handle, "_TC2", NULL, | ||
402 | &tz->trips.passive.tc2); | ||
403 | if (ACPI_FAILURE(status)) | ||
404 | tz->trips.passive.flags.valid = 0; | ||
405 | 415 | ||
406 | status = | ||
407 | acpi_evaluate_integer(tz->device->handle, "_TSP", NULL, | ||
408 | &tz->trips.passive.tsp); | ||
409 | if (ACPI_FAILURE(status)) | 416 | if (ACPI_FAILURE(status)) |
410 | tz->trips.passive.flags.valid = 0; | 417 | tz->trips.passive.flags.valid = 0; |
411 | 418 | else { | |
412 | status = | 419 | tz->trips.passive.flags.valid = 1; |
413 | acpi_evaluate_reference(tz->device->handle, "_PSL", NULL, | 420 | if (flag == ACPI_TRIPS_INIT) { |
414 | &tz->trips.passive.devices); | 421 | status = acpi_evaluate_integer( |
422 | tz->device->handle, "_TC1", | ||
423 | NULL, &tz->trips.passive.tc1); | ||
424 | if (ACPI_FAILURE(status)) | ||
425 | tz->trips.passive.flags.valid = 0; | ||
426 | status = acpi_evaluate_integer( | ||
427 | tz->device->handle, "_TC2", | ||
428 | NULL, &tz->trips.passive.tc2); | ||
429 | if (ACPI_FAILURE(status)) | ||
430 | tz->trips.passive.flags.valid = 0; | ||
431 | status = acpi_evaluate_integer( | ||
432 | tz->device->handle, "_TSP", | ||
433 | NULL, &tz->trips.passive.tsp); | ||
434 | if (ACPI_FAILURE(status)) | ||
435 | tz->trips.passive.flags.valid = 0; | ||
436 | } | ||
437 | } | ||
438 | } | ||
439 | if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.passive.flags.valid) { | ||
440 | memset(&devices, 0, sizeof(struct acpi_handle_list)); | ||
441 | status = acpi_evaluate_reference(tz->device->handle, "_PSL", | ||
442 | NULL, &devices); | ||
415 | if (ACPI_FAILURE(status)) | 443 | if (ACPI_FAILURE(status)) |
416 | tz->trips.passive.flags.valid = 0; | 444 | tz->trips.passive.flags.valid = 0; |
417 | |||
418 | if (!tz->trips.passive.flags.valid) | ||
419 | printk(KERN_WARNING PREFIX "Invalid passive threshold\n"); | ||
420 | else | 445 | else |
421 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 446 | tz->trips.passive.flags.valid = 1; |
422 | "Found passive threshold [%lu]\n", | ||
423 | tz->trips.passive.temperature)); | ||
424 | } | ||
425 | 447 | ||
426 | /* Active: Fans, etc. (optional) */ | 448 | if (memcmp(&tz->trips.passive.devices, &devices, |
449 | sizeof(struct acpi_handle_list))) { | ||
450 | memcpy(&tz->trips.passive.devices, &devices, | ||
451 | sizeof(struct acpi_handle_list)); | ||
452 | ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); | ||
453 | } | ||
454 | } | ||
455 | if ((flag & ACPI_TRIPS_PASSIVE) || (flag & ACPI_TRIPS_DEVICES)) { | ||
456 | if (valid != tz->trips.passive.flags.valid) | ||
457 | ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); | ||
458 | } | ||
427 | 459 | ||
460 | /* Active (optional) */ | ||
428 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { | 461 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { |
429 | |||
430 | char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; | 462 | char name[5] = { '_', 'A', 'C', ('0' + i), '\0' }; |
463 | valid = tz->trips.active[i].flags.valid; | ||
431 | 464 | ||
432 | if (act == -1) | 465 | if (act == -1) |
433 | break; /* disable all active trip points */ | 466 | break; /* disable all active trip points */ |
434 | 467 | ||
435 | status = acpi_evaluate_integer(tz->device->handle, | 468 | if (flag & ACPI_TRIPS_ACTIVE) { |
436 | name, NULL, &tz->trips.active[i].temperature); | 469 | status = acpi_evaluate_integer(tz->device->handle, |
437 | 470 | name, NULL, &tz->trips.active[i].temperature); | |
438 | if (ACPI_FAILURE(status)) { | 471 | if (ACPI_FAILURE(status)) { |
439 | if (i == 0) /* no active trip points */ | 472 | tz->trips.active[i].flags.valid = 0; |
473 | if (i == 0) | ||
474 | break; | ||
475 | if (act <= 0) | ||
476 | break; | ||
477 | if (i == 1) | ||
478 | tz->trips.active[0].temperature = | ||
479 | CELSIUS_TO_KELVIN(act); | ||
480 | else | ||
481 | /* | ||
482 | * Don't allow override higher than | ||
483 | * the next higher trip point | ||
484 | */ | ||
485 | tz->trips.active[i - 1].temperature = | ||
486 | (tz->trips.active[i - 2].temperature < | ||
487 | CELSIUS_TO_KELVIN(act) ? | ||
488 | tz->trips.active[i - 2].temperature : | ||
489 | CELSIUS_TO_KELVIN(act)); | ||
440 | break; | 490 | break; |
441 | if (act <= 0) /* no override requested */ | 491 | } else |
442 | break; | 492 | tz->trips.active[i].flags.valid = 1; |
443 | if (i == 1) { /* 1 trip point */ | ||
444 | tz->trips.active[0].temperature = | ||
445 | CELSIUS_TO_KELVIN(act); | ||
446 | } else { /* multiple trips */ | ||
447 | /* | ||
448 | * Don't allow override higher than | ||
449 | * the next higher trip point | ||
450 | */ | ||
451 | tz->trips.active[i - 1].temperature = | ||
452 | (tz->trips.active[i - 2].temperature < | ||
453 | CELSIUS_TO_KELVIN(act) ? | ||
454 | tz->trips.active[i - 2].temperature : | ||
455 | CELSIUS_TO_KELVIN(act)); | ||
456 | } | ||
457 | break; | ||
458 | } | 493 | } |
459 | 494 | ||
460 | name[2] = 'L'; | 495 | name[2] = 'L'; |
461 | status = | 496 | if ((flag & ACPI_TRIPS_DEVICES) && tz->trips.active[i].flags.valid ) { |
462 | acpi_evaluate_reference(tz->device->handle, name, NULL, | 497 | memset(&devices, 0, sizeof(struct acpi_handle_list)); |
463 | &tz->trips.active[i].devices); | 498 | status = acpi_evaluate_reference(tz->device->handle, |
464 | if (ACPI_SUCCESS(status)) { | 499 | name, NULL, &devices); |
465 | tz->trips.active[i].flags.valid = 1; | 500 | if (ACPI_FAILURE(status)) |
466 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | 501 | tz->trips.active[i].flags.valid = 0; |
467 | "Found active threshold [%d]:[%lu]\n", | 502 | else |
468 | i, tz->trips.active[i].temperature)); | 503 | tz->trips.active[i].flags.valid = 1; |
469 | } else | 504 | |
470 | ACPI_EXCEPTION((AE_INFO, status, | 505 | if (memcmp(&tz->trips.active[i].devices, &devices, |
471 | "Invalid active threshold [%d]", i)); | 506 | sizeof(struct acpi_handle_list))) { |
507 | memcpy(&tz->trips.active[i].devices, &devices, | ||
508 | sizeof(struct acpi_handle_list)); | ||
509 | ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); | ||
510 | } | ||
511 | } | ||
512 | if ((flag & ACPI_TRIPS_ACTIVE) || (flag & ACPI_TRIPS_DEVICES)) | ||
513 | if (valid != tz->trips.active[i].flags.valid) | ||
514 | ACPI_THERMAL_TRIPS_EXCEPTION(flag, "state"); | ||
515 | |||
516 | if (!tz->trips.active[i].flags.valid) | ||
517 | break; | ||
518 | } | ||
519 | |||
520 | if (flag & ACPI_TRIPS_DEVICES) { | ||
521 | memset(&devices, 0, sizeof(struct acpi_handle_list)); | ||
522 | status = acpi_evaluate_reference(tz->device->handle, "_TZD", | ||
523 | NULL, &devices); | ||
524 | if (memcmp(&tz->devices, &devices, | ||
525 | sizeof(struct acpi_handle_list))) { | ||
526 | memcpy(&tz->devices, &devices, | ||
527 | sizeof(struct acpi_handle_list)); | ||
528 | ACPI_THERMAL_TRIPS_EXCEPTION(flag, "device"); | ||
529 | } | ||
472 | } | 530 | } |
473 | 531 | ||
474 | return 0; | 532 | return 0; |
475 | } | 533 | } |
476 | 534 | ||
477 | static int acpi_thermal_get_devices(struct acpi_thermal *tz) | 535 | static int acpi_thermal_get_trip_points(struct acpi_thermal *tz) |
478 | { | 536 | { |
479 | acpi_status status = AE_OK; | 537 | return acpi_thermal_trips_update(tz, ACPI_TRIPS_INIT); |
480 | |||
481 | |||
482 | if (!tz) | ||
483 | return -EINVAL; | ||
484 | |||
485 | status = | ||
486 | acpi_evaluate_reference(tz->device->handle, "_TZD", NULL, &tz->devices); | ||
487 | if (ACPI_FAILURE(status)) | ||
488 | return -ENODEV; | ||
489 | |||
490 | return 0; | ||
491 | } | 538 | } |
492 | 539 | ||
493 | static int acpi_thermal_critical(struct acpi_thermal *tz) | 540 | static int acpi_thermal_critical(struct acpi_thermal *tz) |
@@ -735,6 +782,9 @@ static void acpi_thermal_check(void *data) | |||
735 | if (result) | 782 | if (result) |
736 | goto unlock; | 783 | goto unlock; |
737 | 784 | ||
785 | if (!tz->tz_enabled) | ||
786 | goto unlock; | ||
787 | |||
738 | memset(&tz->state, 0, sizeof(tz->state)); | 788 | memset(&tz->state, 0, sizeof(tz->state)); |
739 | 789 | ||
740 | /* | 790 | /* |
@@ -828,6 +878,290 @@ static void acpi_thermal_check(void *data) | |||
828 | mutex_unlock(&tz->lock); | 878 | mutex_unlock(&tz->lock); |
829 | } | 879 | } |
830 | 880 | ||
881 | /* sys I/F for generic thermal sysfs support */ | ||
882 | static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf) | ||
883 | { | ||
884 | struct acpi_thermal *tz = thermal->devdata; | ||
885 | |||
886 | if (!tz) | ||
887 | return -EINVAL; | ||
888 | |||
889 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature)); | ||
890 | } | ||
891 | |||
892 | static const char enabled[] = "kernel"; | ||
893 | static const char disabled[] = "user"; | ||
894 | static int thermal_get_mode(struct thermal_zone_device *thermal, | ||
895 | char *buf) | ||
896 | { | ||
897 | struct acpi_thermal *tz = thermal->devdata; | ||
898 | |||
899 | if (!tz) | ||
900 | return -EINVAL; | ||
901 | |||
902 | return sprintf(buf, "%s\n", tz->tz_enabled ? | ||
903 | enabled : disabled); | ||
904 | } | ||
905 | |||
906 | static int thermal_set_mode(struct thermal_zone_device *thermal, | ||
907 | const char *buf) | ||
908 | { | ||
909 | struct acpi_thermal *tz = thermal->devdata; | ||
910 | int enable; | ||
911 | |||
912 | if (!tz) | ||
913 | return -EINVAL; | ||
914 | |||
915 | /* | ||
916 | * enable/disable thermal management from ACPI thermal driver | ||
917 | */ | ||
918 | if (!strncmp(buf, enabled, sizeof enabled - 1)) | ||
919 | enable = 1; | ||
920 | else if (!strncmp(buf, disabled, sizeof disabled - 1)) | ||
921 | enable = 0; | ||
922 | else | ||
923 | return -EINVAL; | ||
924 | |||
925 | if (enable != tz->tz_enabled) { | ||
926 | tz->tz_enabled = enable; | ||
927 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
928 | "%s ACPI thermal control\n", | ||
929 | tz->tz_enabled ? enabled : disabled)); | ||
930 | acpi_thermal_check(tz); | ||
931 | } | ||
932 | return 0; | ||
933 | } | ||
934 | |||
935 | static int thermal_get_trip_type(struct thermal_zone_device *thermal, | ||
936 | int trip, char *buf) | ||
937 | { | ||
938 | struct acpi_thermal *tz = thermal->devdata; | ||
939 | int i; | ||
940 | |||
941 | if (!tz || trip < 0) | ||
942 | return -EINVAL; | ||
943 | |||
944 | if (tz->trips.critical.flags.valid) { | ||
945 | if (!trip) | ||
946 | return sprintf(buf, "critical\n"); | ||
947 | trip--; | ||
948 | } | ||
949 | |||
950 | if (tz->trips.hot.flags.valid) { | ||
951 | if (!trip) | ||
952 | return sprintf(buf, "hot\n"); | ||
953 | trip--; | ||
954 | } | ||
955 | |||
956 | if (tz->trips.passive.flags.valid) { | ||
957 | if (!trip) | ||
958 | return sprintf(buf, "passive\n"); | ||
959 | trip--; | ||
960 | } | ||
961 | |||
962 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
963 | tz->trips.active[i].flags.valid; i++) { | ||
964 | if (!trip) | ||
965 | return sprintf(buf, "active%d\n", i); | ||
966 | trip--; | ||
967 | } | ||
968 | |||
969 | return -EINVAL; | ||
970 | } | ||
971 | |||
972 | static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | ||
973 | int trip, char *buf) | ||
974 | { | ||
975 | struct acpi_thermal *tz = thermal->devdata; | ||
976 | int i; | ||
977 | |||
978 | if (!tz || trip < 0) | ||
979 | return -EINVAL; | ||
980 | |||
981 | if (tz->trips.critical.flags.valid) { | ||
982 | if (!trip) | ||
983 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
984 | tz->trips.critical.temperature)); | ||
985 | trip--; | ||
986 | } | ||
987 | |||
988 | if (tz->trips.hot.flags.valid) { | ||
989 | if (!trip) | ||
990 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
991 | tz->trips.hot.temperature)); | ||
992 | trip--; | ||
993 | } | ||
994 | |||
995 | if (tz->trips.passive.flags.valid) { | ||
996 | if (!trip) | ||
997 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
998 | tz->trips.passive.temperature)); | ||
999 | trip--; | ||
1000 | } | ||
1001 | |||
1002 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
1003 | tz->trips.active[i].flags.valid; i++) { | ||
1004 | if (!trip) | ||
1005 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
1006 | tz->trips.active[i].temperature)); | ||
1007 | trip--; | ||
1008 | } | ||
1009 | |||
1010 | return -EINVAL; | ||
1011 | } | ||
1012 | |||
1013 | typedef int (*cb)(struct thermal_zone_device *, int, | ||
1014 | struct thermal_cooling_device *); | ||
1015 | static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, | ||
1016 | struct thermal_cooling_device *cdev, | ||
1017 | cb action) | ||
1018 | { | ||
1019 | struct acpi_device *device = cdev->devdata; | ||
1020 | struct acpi_thermal *tz = thermal->devdata; | ||
1021 | struct acpi_device *dev; | ||
1022 | acpi_status status; | ||
1023 | acpi_handle handle; | ||
1024 | int i; | ||
1025 | int j; | ||
1026 | int trip = -1; | ||
1027 | int result = 0; | ||
1028 | |||
1029 | if (tz->trips.critical.flags.valid) | ||
1030 | trip++; | ||
1031 | |||
1032 | if (tz->trips.hot.flags.valid) | ||
1033 | trip++; | ||
1034 | |||
1035 | if (tz->trips.passive.flags.valid) { | ||
1036 | trip++; | ||
1037 | for (i = 0; i < tz->trips.passive.devices.count; | ||
1038 | i++) { | ||
1039 | handle = tz->trips.passive.devices.handles[i]; | ||
1040 | status = acpi_bus_get_device(handle, &dev); | ||
1041 | if (ACPI_SUCCESS(status) && (dev == device)) { | ||
1042 | result = action(thermal, trip, cdev); | ||
1043 | if (result) | ||
1044 | goto failed; | ||
1045 | } | ||
1046 | } | ||
1047 | } | ||
1048 | |||
1049 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
1050 | if (!tz->trips.active[i].flags.valid) | ||
1051 | break; | ||
1052 | trip++; | ||
1053 | for (j = 0; | ||
1054 | j < tz->trips.active[i].devices.count; | ||
1055 | j++) { | ||
1056 | handle = tz->trips.active[i].devices.handles[j]; | ||
1057 | status = acpi_bus_get_device(handle, &dev); | ||
1058 | if (ACPI_SUCCESS(status) && (dev == device)) { | ||
1059 | result = action(thermal, trip, cdev); | ||
1060 | if (result) | ||
1061 | goto failed; | ||
1062 | } | ||
1063 | } | ||
1064 | } | ||
1065 | |||
1066 | for (i = 0; i < tz->devices.count; i++) { | ||
1067 | handle = tz->devices.handles[i]; | ||
1068 | status = acpi_bus_get_device(handle, &dev); | ||
1069 | if (ACPI_SUCCESS(status) && (dev == device)) { | ||
1070 | result = action(thermal, -1, cdev); | ||
1071 | if (result) | ||
1072 | goto failed; | ||
1073 | } | ||
1074 | } | ||
1075 | |||
1076 | failed: | ||
1077 | return result; | ||
1078 | } | ||
1079 | |||
1080 | static int | ||
1081 | acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, | ||
1082 | struct thermal_cooling_device *cdev) | ||
1083 | { | ||
1084 | return acpi_thermal_cooling_device_cb(thermal, cdev, | ||
1085 | thermal_zone_bind_cooling_device); | ||
1086 | } | ||
1087 | |||
1088 | static int | ||
1089 | acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, | ||
1090 | struct thermal_cooling_device *cdev) | ||
1091 | { | ||
1092 | return acpi_thermal_cooling_device_cb(thermal, cdev, | ||
1093 | thermal_zone_unbind_cooling_device); | ||
1094 | } | ||
1095 | |||
1096 | static struct thermal_zone_device_ops acpi_thermal_zone_ops = { | ||
1097 | .bind = acpi_thermal_bind_cooling_device, | ||
1098 | .unbind = acpi_thermal_unbind_cooling_device, | ||
1099 | .get_temp = thermal_get_temp, | ||
1100 | .get_mode = thermal_get_mode, | ||
1101 | .set_mode = thermal_set_mode, | ||
1102 | .get_trip_type = thermal_get_trip_type, | ||
1103 | .get_trip_temp = thermal_get_trip_temp, | ||
1104 | }; | ||
1105 | |||
1106 | static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) | ||
1107 | { | ||
1108 | int trips = 0; | ||
1109 | int result; | ||
1110 | acpi_status status; | ||
1111 | int i; | ||
1112 | |||
1113 | if (tz->trips.critical.flags.valid) | ||
1114 | trips++; | ||
1115 | |||
1116 | if (tz->trips.hot.flags.valid) | ||
1117 | trips++; | ||
1118 | |||
1119 | if (tz->trips.passive.flags.valid) | ||
1120 | trips++; | ||
1121 | |||
1122 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
1123 | tz->trips.active[i].flags.valid; i++, trips++); | ||
1124 | tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone", | ||
1125 | trips, tz, &acpi_thermal_zone_ops); | ||
1126 | if (!tz->thermal_zone) | ||
1127 | return -ENODEV; | ||
1128 | |||
1129 | result = sysfs_create_link(&tz->device->dev.kobj, | ||
1130 | &tz->thermal_zone->device.kobj, "thermal_zone"); | ||
1131 | if (result) | ||
1132 | return result; | ||
1133 | |||
1134 | result = sysfs_create_link(&tz->thermal_zone->device.kobj, | ||
1135 | &tz->device->dev.kobj, "device"); | ||
1136 | if (result) | ||
1137 | return result; | ||
1138 | |||
1139 | status = acpi_attach_data(tz->device->handle, | ||
1140 | acpi_bus_private_data_handler, | ||
1141 | tz->thermal_zone); | ||
1142 | if (ACPI_FAILURE(status)) { | ||
1143 | ACPI_DEBUG_PRINT((ACPI_DB_ERROR, | ||
1144 | "Error attaching device data\n")); | ||
1145 | return -ENODEV; | ||
1146 | } | ||
1147 | |||
1148 | tz->tz_enabled = 1; | ||
1149 | |||
1150 | printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n", | ||
1151 | tz->device->dev.bus_id, tz->thermal_zone->id); | ||
1152 | return 0; | ||
1153 | } | ||
1154 | |||
1155 | static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) | ||
1156 | { | ||
1157 | sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); | ||
1158 | sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); | ||
1159 | thermal_zone_device_unregister(tz->thermal_zone); | ||
1160 | tz->thermal_zone = NULL; | ||
1161 | acpi_detach_data(tz->device->handle, acpi_bus_private_data_handler); | ||
1162 | } | ||
1163 | |||
1164 | |||
831 | /* -------------------------------------------------------------------------- | 1165 | /* -------------------------------------------------------------------------- |
832 | FS Interface (/proc) | 1166 | FS Interface (/proc) |
833 | -------------------------------------------------------------------------- */ | 1167 | -------------------------------------------------------------------------- */ |
@@ -1184,15 +1518,15 @@ static void acpi_thermal_notify(acpi_handle handle, u32 event, void *data) | |||
1184 | acpi_thermal_check(tz); | 1518 | acpi_thermal_check(tz); |
1185 | break; | 1519 | break; |
1186 | case ACPI_THERMAL_NOTIFY_THRESHOLDS: | 1520 | case ACPI_THERMAL_NOTIFY_THRESHOLDS: |
1187 | acpi_thermal_get_trip_points(tz); | 1521 | acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_THRESHOLDS); |
1188 | acpi_thermal_check(tz); | 1522 | acpi_thermal_check(tz); |
1189 | acpi_bus_generate_proc_event(device, event, 0); | 1523 | acpi_bus_generate_proc_event(device, event, 0); |
1190 | acpi_bus_generate_netlink_event(device->pnp.device_class, | 1524 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
1191 | device->dev.bus_id, event, 0); | 1525 | device->dev.bus_id, event, 0); |
1192 | break; | 1526 | break; |
1193 | case ACPI_THERMAL_NOTIFY_DEVICES: | 1527 | case ACPI_THERMAL_NOTIFY_DEVICES: |
1194 | if (tz->flags.devices) | 1528 | acpi_thermal_trips_update(tz, ACPI_TRIPS_REFRESH_DEVICES); |
1195 | acpi_thermal_get_devices(tz); | 1529 | acpi_thermal_check(tz); |
1196 | acpi_bus_generate_proc_event(device, event, 0); | 1530 | acpi_bus_generate_proc_event(device, event, 0); |
1197 | acpi_bus_generate_netlink_event(device->pnp.device_class, | 1531 | acpi_bus_generate_netlink_event(device->pnp.device_class, |
1198 | device->dev.bus_id, event, 0); | 1532 | device->dev.bus_id, event, 0); |
@@ -1235,11 +1569,6 @@ static int acpi_thermal_get_info(struct acpi_thermal *tz) | |||
1235 | else | 1569 | else |
1236 | acpi_thermal_get_polling_frequency(tz); | 1570 | acpi_thermal_get_polling_frequency(tz); |
1237 | 1571 | ||
1238 | /* Get devices in this thermal zone [_TZD] (optional) */ | ||
1239 | result = acpi_thermal_get_devices(tz); | ||
1240 | if (!result) | ||
1241 | tz->flags.devices = 1; | ||
1242 | |||
1243 | return 0; | 1572 | return 0; |
1244 | } | 1573 | } |
1245 | 1574 | ||
@@ -1263,13 +1592,19 @@ static int acpi_thermal_add(struct acpi_device *device) | |||
1263 | strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); | 1592 | strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); |
1264 | acpi_driver_data(device) = tz; | 1593 | acpi_driver_data(device) = tz; |
1265 | mutex_init(&tz->lock); | 1594 | mutex_init(&tz->lock); |
1595 | |||
1596 | |||
1266 | result = acpi_thermal_get_info(tz); | 1597 | result = acpi_thermal_get_info(tz); |
1267 | if (result) | 1598 | if (result) |
1268 | goto end; | 1599 | goto free_memory; |
1600 | |||
1601 | result = acpi_thermal_register_thermal_zone(tz); | ||
1602 | if (result) | ||
1603 | goto free_memory; | ||
1269 | 1604 | ||
1270 | result = acpi_thermal_add_fs(device); | 1605 | result = acpi_thermal_add_fs(device); |
1271 | if (result) | 1606 | if (result) |
1272 | goto end; | 1607 | goto unregister_thermal_zone; |
1273 | 1608 | ||
1274 | init_timer(&tz->timer); | 1609 | init_timer(&tz->timer); |
1275 | 1610 | ||
@@ -1280,19 +1615,21 @@ static int acpi_thermal_add(struct acpi_device *device) | |||
1280 | acpi_thermal_notify, tz); | 1615 | acpi_thermal_notify, tz); |
1281 | if (ACPI_FAILURE(status)) { | 1616 | if (ACPI_FAILURE(status)) { |
1282 | result = -ENODEV; | 1617 | result = -ENODEV; |
1283 | goto end; | 1618 | goto remove_fs; |
1284 | } | 1619 | } |
1285 | 1620 | ||
1286 | printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", | 1621 | printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", |
1287 | acpi_device_name(device), acpi_device_bid(device), | 1622 | acpi_device_name(device), acpi_device_bid(device), |
1288 | KELVIN_TO_CELSIUS(tz->temperature)); | 1623 | KELVIN_TO_CELSIUS(tz->temperature)); |
1624 | goto end; | ||
1289 | 1625 | ||
1290 | end: | 1626 | remove_fs: |
1291 | if (result) { | 1627 | acpi_thermal_remove_fs(device); |
1292 | acpi_thermal_remove_fs(device); | 1628 | unregister_thermal_zone: |
1293 | kfree(tz); | 1629 | thermal_zone_device_unregister(tz->thermal_zone); |
1294 | } | 1630 | free_memory: |
1295 | 1631 | kfree(tz); | |
1632 | end: | ||
1296 | return result; | 1633 | return result; |
1297 | } | 1634 | } |
1298 | 1635 | ||
@@ -1332,6 +1669,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) | |||
1332 | } | 1669 | } |
1333 | 1670 | ||
1334 | acpi_thermal_remove_fs(device); | 1671 | acpi_thermal_remove_fs(device); |
1672 | acpi_thermal_unregister_thermal_zone(tz); | ||
1335 | mutex_destroy(&tz->lock); | 1673 | mutex_destroy(&tz->lock); |
1336 | kfree(tz); | 1674 | kfree(tz); |
1337 | return 0; | 1675 | return 0; |