diff options
author | Marco Chiappero <marco@absence.it> | 2012-05-19 09:35:51 -0400 |
---|---|---|
committer | Matthew Garrett <mjg@redhat.com> | 2012-05-31 14:29:35 -0400 |
commit | 49f000adcad2d47f22ae97eb78fb2ef8081cd08f (patch) | |
tree | 9cef253a9ae99f02229aef419c3cb4745582185f /drivers/platform | |
parent | 967145a030a86cba29fe090acd2b55b56568359e (diff) |
sony-laptop: add thermal profiles support
[malattia@linux.it: support string based profiles names]
Signed-off-by: Marco Chiappero <marco@absence.it>
Signed-off-by: Mattia Dongili <malattia@linux.it>
Signed-off-by: Matthew Garrett <mjg@redhat.com>
Diffstat (limited to 'drivers/platform')
-rw-r--r-- | drivers/platform/x86/sony-laptop.c | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/drivers/platform/x86/sony-laptop.c b/drivers/platform/x86/sony-laptop.c index 0ac186e9abdc..c3f54ad8125c 100644 --- a/drivers/platform/x86/sony-laptop.c +++ b/drivers/platform/x86/sony-laptop.c | |||
@@ -148,6 +148,10 @@ static int sony_nc_battery_care_setup(struct platform_device *pd, | |||
148 | unsigned int handle); | 148 | unsigned int handle); |
149 | static void sony_nc_battery_care_cleanup(struct platform_device *pd); | 149 | static void sony_nc_battery_care_cleanup(struct platform_device *pd); |
150 | 150 | ||
151 | static int sony_nc_thermal_setup(struct platform_device *pd); | ||
152 | static void sony_nc_thermal_cleanup(struct platform_device *pd); | ||
153 | static void sony_nc_thermal_resume(void); | ||
154 | |||
151 | enum sony_nc_rfkill { | 155 | enum sony_nc_rfkill { |
152 | SONY_WIFI, | 156 | SONY_WIFI, |
153 | SONY_BLUETOOTH, | 157 | SONY_BLUETOOTH, |
@@ -1282,6 +1286,12 @@ static void sony_nc_function_setup(struct acpi_device *device, | |||
1282 | pr_err("couldn't set up battery care function (%d)\n", | 1286 | pr_err("couldn't set up battery care function (%d)\n", |
1283 | result); | 1287 | result); |
1284 | break; | 1288 | break; |
1289 | case 0x0122: | ||
1290 | result = sony_nc_thermal_setup(pf_device); | ||
1291 | if (result) | ||
1292 | pr_err("couldn't set up thermal profile function (%d)\n", | ||
1293 | result); | ||
1294 | break; | ||
1285 | case 0x0124: | 1295 | case 0x0124: |
1286 | case 0x0135: | 1296 | case 0x0135: |
1287 | sony_nc_rfkill_setup(device); | 1297 | sony_nc_rfkill_setup(device); |
@@ -1323,6 +1333,9 @@ static void sony_nc_function_cleanup(struct platform_device *pd) | |||
1323 | case 0x013f: | 1333 | case 0x013f: |
1324 | sony_nc_battery_care_cleanup(pd); | 1334 | sony_nc_battery_care_cleanup(pd); |
1325 | break; | 1335 | break; |
1336 | case 0x0122: | ||
1337 | sony_nc_thermal_cleanup(pd); | ||
1338 | break; | ||
1326 | case 0x0124: | 1339 | case 0x0124: |
1327 | case 0x0135: | 1340 | case 0x0135: |
1328 | sony_nc_rfkill_cleanup(); | 1341 | sony_nc_rfkill_cleanup(); |
@@ -1362,6 +1375,9 @@ static void sony_nc_function_resume(void) | |||
1362 | /* re-enable hotkeys */ | 1375 | /* re-enable hotkeys */ |
1363 | sony_call_snc_handle(handle, 0x100, &result); | 1376 | sony_call_snc_handle(handle, 0x100, &result); |
1364 | break; | 1377 | break; |
1378 | case 0x0122: | ||
1379 | sony_nc_thermal_resume(); | ||
1380 | break; | ||
1365 | case 0x0124: | 1381 | case 0x0124: |
1366 | case 0x0135: | 1382 | case 0x0135: |
1367 | sony_nc_rfkill_update(); | 1383 | sony_nc_rfkill_update(); |
@@ -1923,6 +1939,173 @@ static void sony_nc_battery_care_cleanup(struct platform_device *pd) | |||
1923 | } | 1939 | } |
1924 | } | 1940 | } |
1925 | 1941 | ||
1942 | struct snc_thermal_ctrl { | ||
1943 | unsigned int mode; | ||
1944 | unsigned int profiles; | ||
1945 | struct device_attribute mode_attr; | ||
1946 | struct device_attribute profiles_attr; | ||
1947 | }; | ||
1948 | static struct snc_thermal_ctrl *th_handle; | ||
1949 | |||
1950 | #define THM_PROFILE_MAX 3 | ||
1951 | static const char * const snc_thermal_profiles[] = { | ||
1952 | "balanced", | ||
1953 | "silent", | ||
1954 | "performance" | ||
1955 | }; | ||
1956 | |||
1957 | static int sony_nc_thermal_mode_set(unsigned short mode) | ||
1958 | { | ||
1959 | unsigned int result; | ||
1960 | |||
1961 | /* the thermal profile seems to be a two bit bitmask: | ||
1962 | * lsb -> silent | ||
1963 | * msb -> performance | ||
1964 | * no bit set is the normal operation and is always valid | ||
1965 | * Some vaio models only have "balanced" and "performance" | ||
1966 | */ | ||
1967 | if ((mode && !(th_handle->profiles & mode)) || mode >= THM_PROFILE_MAX) | ||
1968 | return -EINVAL; | ||
1969 | |||
1970 | if (sony_call_snc_handle(0x0122, mode << 0x10 | 0x0200, &result)) | ||
1971 | return -EIO; | ||
1972 | |||
1973 | th_handle->mode = mode; | ||
1974 | |||
1975 | return 0; | ||
1976 | } | ||
1977 | |||
1978 | static int sony_nc_thermal_mode_get(void) | ||
1979 | { | ||
1980 | unsigned int result; | ||
1981 | |||
1982 | if (sony_call_snc_handle(0x0122, 0x0100, &result)) | ||
1983 | return -EIO; | ||
1984 | |||
1985 | return result & 0xff; | ||
1986 | } | ||
1987 | |||
1988 | static ssize_t sony_nc_thermal_profiles_show(struct device *dev, | ||
1989 | struct device_attribute *attr, char *buffer) | ||
1990 | { | ||
1991 | short cnt; | ||
1992 | size_t idx = 0; | ||
1993 | |||
1994 | for (cnt = 0; cnt < THM_PROFILE_MAX; cnt++) { | ||
1995 | if (!cnt || (th_handle->profiles & cnt)) | ||
1996 | idx += snprintf(buffer + idx, PAGE_SIZE - idx, "%s ", | ||
1997 | snc_thermal_profiles[cnt]); | ||
1998 | } | ||
1999 | idx += snprintf(buffer + idx, PAGE_SIZE - idx, "\n"); | ||
2000 | |||
2001 | return idx; | ||
2002 | } | ||
2003 | |||
2004 | static ssize_t sony_nc_thermal_mode_store(struct device *dev, | ||
2005 | struct device_attribute *attr, | ||
2006 | const char *buffer, size_t count) | ||
2007 | { | ||
2008 | unsigned short cmd; | ||
2009 | size_t len = count; | ||
2010 | |||
2011 | if (count == 0) | ||
2012 | return -EINVAL; | ||
2013 | |||
2014 | /* skip the newline if present */ | ||
2015 | if (buffer[len - 1] == '\n') | ||
2016 | len--; | ||
2017 | |||
2018 | for (cmd = 0; cmd < THM_PROFILE_MAX; cmd++) | ||
2019 | if (strncmp(buffer, snc_thermal_profiles[cmd], len) == 0) | ||
2020 | break; | ||
2021 | |||
2022 | if (sony_nc_thermal_mode_set(cmd)) | ||
2023 | return -EIO; | ||
2024 | |||
2025 | return count; | ||
2026 | } | ||
2027 | |||
2028 | static ssize_t sony_nc_thermal_mode_show(struct device *dev, | ||
2029 | struct device_attribute *attr, char *buffer) | ||
2030 | { | ||
2031 | ssize_t count = 0; | ||
2032 | unsigned int mode = sony_nc_thermal_mode_get(); | ||
2033 | |||
2034 | if (mode < 0) | ||
2035 | return mode; | ||
2036 | |||
2037 | count = snprintf(buffer, PAGE_SIZE, "%s\n", snc_thermal_profiles[mode]); | ||
2038 | |||
2039 | return count; | ||
2040 | } | ||
2041 | |||
2042 | static int sony_nc_thermal_setup(struct platform_device *pd) | ||
2043 | { | ||
2044 | int ret = 0; | ||
2045 | th_handle = kzalloc(sizeof(struct snc_thermal_ctrl), GFP_KERNEL); | ||
2046 | if (!th_handle) | ||
2047 | return -ENOMEM; | ||
2048 | |||
2049 | ret = sony_call_snc_handle(0x0122, 0x0000, &th_handle->profiles); | ||
2050 | if (ret) { | ||
2051 | pr_warn("couldn't to read the thermal profiles\n"); | ||
2052 | goto outkzalloc; | ||
2053 | } | ||
2054 | |||
2055 | ret = sony_nc_thermal_mode_get(); | ||
2056 | if (ret < 0) { | ||
2057 | pr_warn("couldn't to read the current thermal profile"); | ||
2058 | goto outkzalloc; | ||
2059 | } | ||
2060 | th_handle->mode = ret; | ||
2061 | |||
2062 | sysfs_attr_init(&th_handle->profiles_attr.attr); | ||
2063 | th_handle->profiles_attr.attr.name = "thermal_profiles"; | ||
2064 | th_handle->profiles_attr.attr.mode = S_IRUGO; | ||
2065 | th_handle->profiles_attr.show = sony_nc_thermal_profiles_show; | ||
2066 | |||
2067 | sysfs_attr_init(&th_handle->mode_attr.attr); | ||
2068 | th_handle->mode_attr.attr.name = "thermal_control"; | ||
2069 | th_handle->mode_attr.attr.mode = S_IRUGO | S_IWUSR; | ||
2070 | th_handle->mode_attr.show = sony_nc_thermal_mode_show; | ||
2071 | th_handle->mode_attr.store = sony_nc_thermal_mode_store; | ||
2072 | |||
2073 | ret = device_create_file(&pd->dev, &th_handle->profiles_attr); | ||
2074 | if (ret) | ||
2075 | goto outkzalloc; | ||
2076 | |||
2077 | ret = device_create_file(&pd->dev, &th_handle->mode_attr); | ||
2078 | if (ret) | ||
2079 | goto outprofiles; | ||
2080 | |||
2081 | return 0; | ||
2082 | |||
2083 | outprofiles: | ||
2084 | device_remove_file(&pd->dev, &th_handle->profiles_attr); | ||
2085 | outkzalloc: | ||
2086 | kfree(th_handle); | ||
2087 | th_handle = NULL; | ||
2088 | return ret; | ||
2089 | } | ||
2090 | |||
2091 | static void sony_nc_thermal_cleanup(struct platform_device *pd) | ||
2092 | { | ||
2093 | if (th_handle) { | ||
2094 | device_remove_file(&pd->dev, &th_handle->profiles_attr); | ||
2095 | device_remove_file(&pd->dev, &th_handle->mode_attr); | ||
2096 | kfree(th_handle); | ||
2097 | th_handle = NULL; | ||
2098 | } | ||
2099 | } | ||
2100 | |||
2101 | static void sony_nc_thermal_resume(void) | ||
2102 | { | ||
2103 | unsigned int status = sony_nc_thermal_mode_get(); | ||
2104 | |||
2105 | if (status != th_handle->mode) | ||
2106 | sony_nc_thermal_mode_set(th_handle->mode); | ||
2107 | } | ||
2108 | |||
1926 | static void sony_nc_backlight_ng_read_limits(int handle, | 2109 | static void sony_nc_backlight_ng_read_limits(int handle, |
1927 | struct sony_backlight_props *props) | 2110 | struct sony_backlight_props *props) |
1928 | { | 2111 | { |