diff options
-rw-r--r-- | drivers/acpi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/acpi/thermal.c | 301 |
2 files changed, 292 insertions, 10 deletions
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index ccf6ea95f68c..558372957fd3 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig | |||
@@ -186,6 +186,7 @@ config ACPI_HOTPLUG_CPU | |||
186 | config ACPI_THERMAL | 186 | config ACPI_THERMAL |
187 | tristate "Thermal Zone" | 187 | tristate "Thermal Zone" |
188 | depends on ACPI_PROCESSOR | 188 | depends on ACPI_PROCESSOR |
189 | select THERMAL | ||
189 | default y | 190 | default y |
190 | help | 191 | help |
191 | This driver adds support for ACPI thermal zones. Most mobile and | 192 | This driver adds support for ACPI thermal zones. Most mobile and |
diff --git a/drivers/acpi/thermal.c b/drivers/acpi/thermal.c index 5f79b4451212..c6cfce4c0122 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 | ||
@@ -195,6 +195,8 @@ struct acpi_thermal { | |||
195 | struct acpi_thermal_trips trips; | 195 | struct acpi_thermal_trips trips; |
196 | struct acpi_handle_list devices; | 196 | struct acpi_handle_list devices; |
197 | struct timer_list timer; | 197 | struct timer_list timer; |
198 | struct thermal_zone_device *thermal_zone; | ||
199 | int tz_enabled; | ||
198 | struct mutex lock; | 200 | struct mutex lock; |
199 | }; | 201 | }; |
200 | 202 | ||
@@ -732,6 +734,9 @@ static void acpi_thermal_check(void *data) | |||
732 | if (result) | 734 | if (result) |
733 | goto unlock; | 735 | goto unlock; |
734 | 736 | ||
737 | if (!tz->tz_enabled) | ||
738 | goto unlock; | ||
739 | |||
735 | memset(&tz->state, 0, sizeof(tz->state)); | 740 | memset(&tz->state, 0, sizeof(tz->state)); |
736 | 741 | ||
737 | /* | 742 | /* |
@@ -825,6 +830,273 @@ static void acpi_thermal_check(void *data) | |||
825 | mutex_unlock(&tz->lock); | 830 | mutex_unlock(&tz->lock); |
826 | } | 831 | } |
827 | 832 | ||
833 | /* sys I/F for generic thermal sysfs support */ | ||
834 | static int thermal_get_temp(struct thermal_zone_device *thermal, char *buf) | ||
835 | { | ||
836 | struct acpi_thermal *tz = thermal->devdata; | ||
837 | |||
838 | if (!tz) | ||
839 | return -EINVAL; | ||
840 | |||
841 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS(tz->temperature)); | ||
842 | } | ||
843 | |||
844 | static const char enabled[] = "kernel"; | ||
845 | static const char disabled[] = "user"; | ||
846 | static int thermal_get_mode(struct thermal_zone_device *thermal, | ||
847 | char *buf) | ||
848 | { | ||
849 | struct acpi_thermal *tz = thermal->devdata; | ||
850 | |||
851 | if (!tz) | ||
852 | return -EINVAL; | ||
853 | |||
854 | return sprintf(buf, "%s\n", tz->tz_enabled ? | ||
855 | enabled : disabled); | ||
856 | } | ||
857 | |||
858 | static int thermal_set_mode(struct thermal_zone_device *thermal, | ||
859 | const char *buf) | ||
860 | { | ||
861 | struct acpi_thermal *tz = thermal->devdata; | ||
862 | int enable; | ||
863 | |||
864 | if (!tz) | ||
865 | return -EINVAL; | ||
866 | |||
867 | /* | ||
868 | * enable/disable thermal management from ACPI thermal driver | ||
869 | */ | ||
870 | if (!strncmp(buf, enabled, sizeof enabled - 1)) | ||
871 | enable = 1; | ||
872 | else if (!strncmp(buf, disabled, sizeof disabled - 1)) | ||
873 | enable = 0; | ||
874 | else | ||
875 | return -EINVAL; | ||
876 | |||
877 | if (enable != tz->tz_enabled) { | ||
878 | tz->tz_enabled = enable; | ||
879 | ACPI_DEBUG_PRINT((ACPI_DB_INFO, | ||
880 | "%s ACPI thermal control\n", | ||
881 | tz->tz_enabled ? enabled : disabled)); | ||
882 | acpi_thermal_check(tz); | ||
883 | } | ||
884 | return 0; | ||
885 | } | ||
886 | |||
887 | static int thermal_get_trip_type(struct thermal_zone_device *thermal, | ||
888 | int trip, char *buf) | ||
889 | { | ||
890 | struct acpi_thermal *tz = thermal->devdata; | ||
891 | int i; | ||
892 | |||
893 | if (!tz || trip < 0) | ||
894 | return -EINVAL; | ||
895 | |||
896 | if (tz->trips.critical.flags.valid) { | ||
897 | if (!trip) | ||
898 | return sprintf(buf, "critical\n"); | ||
899 | trip--; | ||
900 | } | ||
901 | |||
902 | if (tz->trips.hot.flags.valid) { | ||
903 | if (!trip) | ||
904 | return sprintf(buf, "hot\n"); | ||
905 | trip--; | ||
906 | } | ||
907 | |||
908 | if (tz->trips.passive.flags.valid) { | ||
909 | if (!trip) | ||
910 | return sprintf(buf, "passive\n"); | ||
911 | trip--; | ||
912 | } | ||
913 | |||
914 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
915 | tz->trips.active[i].flags.valid; i++) { | ||
916 | if (!trip) | ||
917 | return sprintf(buf, "active%d\n", i); | ||
918 | trip--; | ||
919 | } | ||
920 | |||
921 | return -EINVAL; | ||
922 | } | ||
923 | |||
924 | static int thermal_get_trip_temp(struct thermal_zone_device *thermal, | ||
925 | int trip, char *buf) | ||
926 | { | ||
927 | struct acpi_thermal *tz = thermal->devdata; | ||
928 | int i; | ||
929 | |||
930 | if (!tz || trip < 0) | ||
931 | return -EINVAL; | ||
932 | |||
933 | if (tz->trips.critical.flags.valid) { | ||
934 | if (!trip) | ||
935 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
936 | tz->trips.critical.temperature)); | ||
937 | trip--; | ||
938 | } | ||
939 | |||
940 | if (tz->trips.hot.flags.valid) { | ||
941 | if (!trip) | ||
942 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
943 | tz->trips.hot.temperature)); | ||
944 | trip--; | ||
945 | } | ||
946 | |||
947 | if (tz->trips.passive.flags.valid) { | ||
948 | if (!trip) | ||
949 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
950 | tz->trips.passive.temperature)); | ||
951 | trip--; | ||
952 | } | ||
953 | |||
954 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
955 | tz->trips.active[i].flags.valid; i++) { | ||
956 | if (!trip) | ||
957 | return sprintf(buf, "%ld\n", KELVIN_TO_CELSIUS( | ||
958 | tz->trips.active[i].temperature)); | ||
959 | trip--; | ||
960 | } | ||
961 | |||
962 | return -EINVAL; | ||
963 | } | ||
964 | |||
965 | typedef int (*cb)(struct thermal_zone_device *, int, | ||
966 | struct thermal_cooling_device *); | ||
967 | static int acpi_thermal_cooling_device_cb(struct thermal_zone_device *thermal, | ||
968 | struct thermal_cooling_device *cdev, | ||
969 | cb action) | ||
970 | { | ||
971 | struct acpi_device *device = cdev->devdata; | ||
972 | struct acpi_thermal *tz = thermal->devdata; | ||
973 | acpi_handle handle = device->handle; | ||
974 | int i; | ||
975 | int j; | ||
976 | int trip = -1; | ||
977 | int result = 0; | ||
978 | |||
979 | if (tz->trips.critical.flags.valid) | ||
980 | trip++; | ||
981 | |||
982 | if (tz->trips.hot.flags.valid) | ||
983 | trip++; | ||
984 | |||
985 | if (tz->trips.passive.flags.valid) { | ||
986 | trip++; | ||
987 | for (i = 0; i < tz->trips.passive.devices.count; | ||
988 | i++) { | ||
989 | if (tz->trips.passive.devices.handles[i] != | ||
990 | handle) | ||
991 | continue; | ||
992 | result = action(thermal, trip, cdev); | ||
993 | if (result) | ||
994 | goto failed; | ||
995 | } | ||
996 | } | ||
997 | |||
998 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE; i++) { | ||
999 | if (!tz->trips.active[i].flags.valid) | ||
1000 | break; | ||
1001 | trip++; | ||
1002 | for (j = 0; | ||
1003 | j < tz->trips.active[i].devices.count; | ||
1004 | j++) { | ||
1005 | if (tz->trips.active[i].devices. | ||
1006 | handles[j] != handle) | ||
1007 | continue; | ||
1008 | result = action(thermal, trip, cdev); | ||
1009 | if (result) | ||
1010 | goto failed; | ||
1011 | } | ||
1012 | } | ||
1013 | |||
1014 | for (i = 0; i < tz->devices.count; i++) { | ||
1015 | if (tz->devices.handles[i] != handle) | ||
1016 | continue; | ||
1017 | result = action(thermal, -1, cdev); | ||
1018 | if (result) | ||
1019 | goto failed; | ||
1020 | } | ||
1021 | |||
1022 | failed: | ||
1023 | return result; | ||
1024 | } | ||
1025 | |||
1026 | static int | ||
1027 | acpi_thermal_bind_cooling_device(struct thermal_zone_device *thermal, | ||
1028 | struct thermal_cooling_device *cdev) | ||
1029 | { | ||
1030 | return acpi_thermal_cooling_device_cb(thermal, cdev, | ||
1031 | thermal_zone_bind_cooling_device); | ||
1032 | } | ||
1033 | |||
1034 | static int | ||
1035 | acpi_thermal_unbind_cooling_device(struct thermal_zone_device *thermal, | ||
1036 | struct thermal_cooling_device *cdev) | ||
1037 | { | ||
1038 | return acpi_thermal_cooling_device_cb(thermal, cdev, | ||
1039 | thermal_zone_unbind_cooling_device); | ||
1040 | } | ||
1041 | |||
1042 | static struct thermal_zone_device_ops acpi_thermal_zone_ops = { | ||
1043 | .bind = acpi_thermal_bind_cooling_device, | ||
1044 | .unbind = acpi_thermal_unbind_cooling_device, | ||
1045 | .get_temp = thermal_get_temp, | ||
1046 | .get_mode = thermal_get_mode, | ||
1047 | .set_mode = thermal_set_mode, | ||
1048 | .get_trip_type = thermal_get_trip_type, | ||
1049 | .get_trip_temp = thermal_get_trip_temp, | ||
1050 | }; | ||
1051 | |||
1052 | static int acpi_thermal_register_thermal_zone(struct acpi_thermal *tz) | ||
1053 | { | ||
1054 | int trips = 0; | ||
1055 | int result; | ||
1056 | int i; | ||
1057 | |||
1058 | if (tz->trips.critical.flags.valid) | ||
1059 | trips++; | ||
1060 | |||
1061 | if (tz->trips.hot.flags.valid) | ||
1062 | trips++; | ||
1063 | |||
1064 | if (tz->trips.passive.flags.valid) | ||
1065 | trips++; | ||
1066 | |||
1067 | for (i = 0; i < ACPI_THERMAL_MAX_ACTIVE && | ||
1068 | tz->trips.active[i].flags.valid; i++, trips++); | ||
1069 | tz->thermal_zone = thermal_zone_device_register("ACPI thermal zone", | ||
1070 | trips, tz, &acpi_thermal_zone_ops); | ||
1071 | if (!tz->thermal_zone) | ||
1072 | return -ENODEV; | ||
1073 | |||
1074 | result = sysfs_create_link(&tz->device->dev.kobj, | ||
1075 | &tz->thermal_zone->device.kobj, "thermal_zone"); | ||
1076 | if (result) | ||
1077 | return result; | ||
1078 | |||
1079 | result = sysfs_create_link(&tz->thermal_zone->device.kobj, | ||
1080 | &tz->device->dev.kobj, "device"); | ||
1081 | if (result) | ||
1082 | return result; | ||
1083 | |||
1084 | tz->tz_enabled = 1; | ||
1085 | |||
1086 | printk(KERN_INFO PREFIX "%s is registered as thermal_zone%d\n", | ||
1087 | tz->device->dev.bus_id, tz->thermal_zone->id); | ||
1088 | return 0; | ||
1089 | } | ||
1090 | |||
1091 | static void acpi_thermal_unregister_thermal_zone(struct acpi_thermal *tz) | ||
1092 | { | ||
1093 | sysfs_remove_link(&tz->device->dev.kobj, "thermal_zone"); | ||
1094 | sysfs_remove_link(&tz->thermal_zone->device.kobj, "device"); | ||
1095 | thermal_zone_device_unregister(tz->thermal_zone); | ||
1096 | tz->thermal_zone = NULL; | ||
1097 | } | ||
1098 | |||
1099 | |||
828 | /* -------------------------------------------------------------------------- | 1100 | /* -------------------------------------------------------------------------- |
829 | FS Interface (/proc) | 1101 | FS Interface (/proc) |
830 | -------------------------------------------------------------------------- */ | 1102 | -------------------------------------------------------------------------- */ |
@@ -1260,13 +1532,19 @@ static int acpi_thermal_add(struct acpi_device *device) | |||
1260 | strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); | 1532 | strcpy(acpi_device_class(device), ACPI_THERMAL_CLASS); |
1261 | acpi_driver_data(device) = tz; | 1533 | acpi_driver_data(device) = tz; |
1262 | mutex_init(&tz->lock); | 1534 | mutex_init(&tz->lock); |
1535 | |||
1536 | |||
1263 | result = acpi_thermal_get_info(tz); | 1537 | result = acpi_thermal_get_info(tz); |
1264 | if (result) | 1538 | if (result) |
1265 | goto end; | 1539 | goto free_memory; |
1540 | |||
1541 | result = acpi_thermal_register_thermal_zone(tz); | ||
1542 | if (result) | ||
1543 | goto free_memory; | ||
1266 | 1544 | ||
1267 | result = acpi_thermal_add_fs(device); | 1545 | result = acpi_thermal_add_fs(device); |
1268 | if (result) | 1546 | if (result) |
1269 | goto end; | 1547 | goto unregister_thermal_zone; |
1270 | 1548 | ||
1271 | init_timer(&tz->timer); | 1549 | init_timer(&tz->timer); |
1272 | 1550 | ||
@@ -1277,19 +1555,21 @@ static int acpi_thermal_add(struct acpi_device *device) | |||
1277 | acpi_thermal_notify, tz); | 1555 | acpi_thermal_notify, tz); |
1278 | if (ACPI_FAILURE(status)) { | 1556 | if (ACPI_FAILURE(status)) { |
1279 | result = -ENODEV; | 1557 | result = -ENODEV; |
1280 | goto end; | 1558 | goto remove_fs; |
1281 | } | 1559 | } |
1282 | 1560 | ||
1283 | printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", | 1561 | printk(KERN_INFO PREFIX "%s [%s] (%ld C)\n", |
1284 | acpi_device_name(device), acpi_device_bid(device), | 1562 | acpi_device_name(device), acpi_device_bid(device), |
1285 | KELVIN_TO_CELSIUS(tz->temperature)); | 1563 | KELVIN_TO_CELSIUS(tz->temperature)); |
1564 | goto end; | ||
1286 | 1565 | ||
1287 | end: | 1566 | remove_fs: |
1288 | if (result) { | 1567 | acpi_thermal_remove_fs(device); |
1289 | acpi_thermal_remove_fs(device); | 1568 | unregister_thermal_zone: |
1290 | kfree(tz); | 1569 | thermal_zone_device_unregister(tz->thermal_zone); |
1291 | } | 1570 | free_memory: |
1292 | 1571 | kfree(tz); | |
1572 | end: | ||
1293 | return result; | 1573 | return result; |
1294 | } | 1574 | } |
1295 | 1575 | ||
@@ -1329,6 +1609,7 @@ static int acpi_thermal_remove(struct acpi_device *device, int type) | |||
1329 | } | 1609 | } |
1330 | 1610 | ||
1331 | acpi_thermal_remove_fs(device); | 1611 | acpi_thermal_remove_fs(device); |
1612 | acpi_thermal_unregister_thermal_zone(tz); | ||
1332 | mutex_destroy(&tz->lock); | 1613 | mutex_destroy(&tz->lock); |
1333 | kfree(tz); | 1614 | kfree(tz); |
1334 | return 0; | 1615 | return 0; |