diff options
Diffstat (limited to 'drivers/hwmon/it87.c')
-rw-r--r-- | drivers/hwmon/it87.c | 182 |
1 files changed, 180 insertions, 2 deletions
diff --git a/drivers/hwmon/it87.c b/drivers/hwmon/it87.c index 8282282eb4ca..bbb0c7443b9b 100644 --- a/drivers/hwmon/it87.c +++ b/drivers/hwmon/it87.c | |||
@@ -192,6 +192,9 @@ static const u8 IT87_REG_FANX_MIN[] = { 0x1b, 0x1c, 0x1d, 0x85, 0x87 }; | |||
192 | 192 | ||
193 | #define IT87_REG_CHIPID 0x58 | 193 | #define IT87_REG_CHIPID 0x58 |
194 | 194 | ||
195 | #define IT87_REG_AUTO_TEMP(nr, i) (0x60 + (nr) * 8 + (i)) | ||
196 | #define IT87_REG_AUTO_PWM(nr, i) (0x65 + (nr) * 8 + (i)) | ||
197 | |||
195 | #define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) | 198 | #define IN_TO_REG(val) (SENSORS_LIMIT((((val) + 8)/16),0,255)) |
196 | #define IN_FROM_REG(val) ((val) * 16) | 199 | #define IN_FROM_REG(val) ((val) * 16) |
197 | 200 | ||
@@ -293,6 +296,10 @@ struct it87_data { | |||
293 | u8 pwm_ctrl[3]; /* Register value */ | 296 | u8 pwm_ctrl[3]; /* Register value */ |
294 | u8 pwm_duty[3]; /* Manual PWM value set by user (bit 6-0) */ | 297 | u8 pwm_duty[3]; /* Manual PWM value set by user (bit 6-0) */ |
295 | u8 pwm_temp_map[3]; /* PWM to temp. chan. mapping (bits 1-0) */ | 298 | u8 pwm_temp_map[3]; /* PWM to temp. chan. mapping (bits 1-0) */ |
299 | |||
300 | /* Automatic fan speed control registers */ | ||
301 | u8 auto_pwm[3][4]; /* [nr][3] is hard-coded */ | ||
302 | s8 auto_temp[3][5]; /* [nr][0] is point1_temp_hyst */ | ||
296 | }; | 303 | }; |
297 | 304 | ||
298 | static inline int has_16bit_fans(const struct it87_data *data) | 305 | static inline int has_16bit_fans(const struct it87_data *data) |
@@ -307,6 +314,15 @@ static inline int has_16bit_fans(const struct it87_data *data) | |||
307 | || data->type == it8720; | 314 | || data->type == it8720; |
308 | } | 315 | } |
309 | 316 | ||
317 | static inline int has_old_autopwm(const struct it87_data *data) | ||
318 | { | ||
319 | /* The old automatic fan speed control interface is implemented | ||
320 | by IT8705F chips up to revision F and IT8712F chips up to | ||
321 | revision G. */ | ||
322 | return (data->type == it87 && data->revision < 0x03) | ||
323 | || (data->type == it8712 && data->revision < 0x08); | ||
324 | } | ||
325 | |||
310 | static int it87_probe(struct platform_device *pdev); | 326 | static int it87_probe(struct platform_device *pdev); |
311 | static int __devexit it87_remove(struct platform_device *pdev); | 327 | static int __devexit it87_remove(struct platform_device *pdev); |
312 | 328 | ||
@@ -813,6 +829,13 @@ static ssize_t set_pwm_temp_map(struct device *dev, | |||
813 | long val; | 829 | long val; |
814 | u8 reg; | 830 | u8 reg; |
815 | 831 | ||
832 | /* This check can go away if we ever support automatic fan speed | ||
833 | control on newer chips. */ | ||
834 | if (!has_old_autopwm(data)) { | ||
835 | dev_notice(dev, "Mapping change disabled for safety reasons\n"); | ||
836 | return -EINVAL; | ||
837 | } | ||
838 | |||
816 | if (strict_strtol(buf, 10, &val) < 0) | 839 | if (strict_strtol(buf, 10, &val) < 0) |
817 | return -EINVAL; | 840 | return -EINVAL; |
818 | 841 | ||
@@ -842,6 +865,72 @@ static ssize_t set_pwm_temp_map(struct device *dev, | |||
842 | return count; | 865 | return count; |
843 | } | 866 | } |
844 | 867 | ||
868 | static ssize_t show_auto_pwm(struct device *dev, | ||
869 | struct device_attribute *attr, char *buf) | ||
870 | { | ||
871 | struct it87_data *data = it87_update_device(dev); | ||
872 | struct sensor_device_attribute_2 *sensor_attr = | ||
873 | to_sensor_dev_attr_2(attr); | ||
874 | int nr = sensor_attr->nr; | ||
875 | int point = sensor_attr->index; | ||
876 | |||
877 | return sprintf(buf, "%d\n", PWM_FROM_REG(data->auto_pwm[nr][point])); | ||
878 | } | ||
879 | |||
880 | static ssize_t set_auto_pwm(struct device *dev, | ||
881 | struct device_attribute *attr, const char *buf, size_t count) | ||
882 | { | ||
883 | struct it87_data *data = dev_get_drvdata(dev); | ||
884 | struct sensor_device_attribute_2 *sensor_attr = | ||
885 | to_sensor_dev_attr_2(attr); | ||
886 | int nr = sensor_attr->nr; | ||
887 | int point = sensor_attr->index; | ||
888 | long val; | ||
889 | |||
890 | if (strict_strtol(buf, 10, &val) < 0 || val < 0 || val > 255) | ||
891 | return -EINVAL; | ||
892 | |||
893 | mutex_lock(&data->update_lock); | ||
894 | data->auto_pwm[nr][point] = PWM_TO_REG(val); | ||
895 | it87_write_value(data, IT87_REG_AUTO_PWM(nr, point), | ||
896 | data->auto_pwm[nr][point]); | ||
897 | mutex_unlock(&data->update_lock); | ||
898 | return count; | ||
899 | } | ||
900 | |||
901 | static ssize_t show_auto_temp(struct device *dev, | ||
902 | struct device_attribute *attr, char *buf) | ||
903 | { | ||
904 | struct it87_data *data = it87_update_device(dev); | ||
905 | struct sensor_device_attribute_2 *sensor_attr = | ||
906 | to_sensor_dev_attr_2(attr); | ||
907 | int nr = sensor_attr->nr; | ||
908 | int point = sensor_attr->index; | ||
909 | |||
910 | return sprintf(buf, "%d\n", TEMP_FROM_REG(data->auto_temp[nr][point])); | ||
911 | } | ||
912 | |||
913 | static ssize_t set_auto_temp(struct device *dev, | ||
914 | struct device_attribute *attr, const char *buf, size_t count) | ||
915 | { | ||
916 | struct it87_data *data = dev_get_drvdata(dev); | ||
917 | struct sensor_device_attribute_2 *sensor_attr = | ||
918 | to_sensor_dev_attr_2(attr); | ||
919 | int nr = sensor_attr->nr; | ||
920 | int point = sensor_attr->index; | ||
921 | long val; | ||
922 | |||
923 | if (strict_strtol(buf, 10, &val) < 0 || val < -128000 || val > 127000) | ||
924 | return -EINVAL; | ||
925 | |||
926 | mutex_lock(&data->update_lock); | ||
927 | data->auto_temp[nr][point] = TEMP_TO_REG(val); | ||
928 | it87_write_value(data, IT87_REG_AUTO_TEMP(nr, point), | ||
929 | data->auto_temp[nr][point]); | ||
930 | mutex_unlock(&data->update_lock); | ||
931 | return count; | ||
932 | } | ||
933 | |||
845 | #define show_fan_offset(offset) \ | 934 | #define show_fan_offset(offset) \ |
846 | static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ | 935 | static SENSOR_DEVICE_ATTR(fan##offset##_input, S_IRUGO, \ |
847 | show_fan, NULL, offset - 1); \ | 936 | show_fan, NULL, offset - 1); \ |
@@ -863,8 +952,34 @@ static DEVICE_ATTR(pwm##offset##_freq, \ | |||
863 | (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ | 952 | (offset == 1 ? S_IRUGO | S_IWUSR : S_IRUGO), \ |
864 | show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); \ | 953 | show_pwm_freq, (offset == 1 ? set_pwm_freq : NULL)); \ |
865 | static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels_temp, \ | 954 | static SENSOR_DEVICE_ATTR(pwm##offset##_auto_channels_temp, \ |
866 | S_IRUGO, show_pwm_temp_map, set_pwm_temp_map, \ | 955 | S_IRUGO | S_IWUSR, show_pwm_temp_map, set_pwm_temp_map, \ |
867 | offset - 1); | 956 | offset - 1); \ |
957 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_pwm, \ | ||
958 | S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ | ||
959 | offset - 1, 0); \ | ||
960 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_pwm, \ | ||
961 | S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ | ||
962 | offset - 1, 1); \ | ||
963 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_pwm, \ | ||
964 | S_IRUGO | S_IWUSR, show_auto_pwm, set_auto_pwm, \ | ||
965 | offset - 1, 2); \ | ||
966 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_pwm, \ | ||
967 | S_IRUGO, show_auto_pwm, NULL, offset - 1, 3); \ | ||
968 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp, \ | ||
969 | S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ | ||
970 | offset - 1, 1); \ | ||
971 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point1_temp_hyst, \ | ||
972 | S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ | ||
973 | offset - 1, 0); \ | ||
974 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point2_temp, \ | ||
975 | S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ | ||
976 | offset - 1, 2); \ | ||
977 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point3_temp, \ | ||
978 | S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ | ||
979 | offset - 1, 3); \ | ||
980 | static SENSOR_DEVICE_ATTR_2(pwm##offset##_auto_point4_temp, \ | ||
981 | S_IRUGO | S_IWUSR, show_auto_temp, set_auto_temp, \ | ||
982 | offset - 1, 4); | ||
868 | 983 | ||
869 | show_pwm_offset(1); | 984 | show_pwm_offset(1); |
870 | show_pwm_offset(2); | 985 | show_pwm_offset(2); |
@@ -1219,6 +1334,47 @@ static const struct attribute_group it87_group_pwm[3] = { | |||
1219 | { .attrs = it87_attributes_pwm[2] }, | 1334 | { .attrs = it87_attributes_pwm[2] }, |
1220 | }; | 1335 | }; |
1221 | 1336 | ||
1337 | static struct attribute *it87_attributes_autopwm[3][9+1] = { { | ||
1338 | &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, | ||
1339 | &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, | ||
1340 | &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, | ||
1341 | &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, | ||
1342 | &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, | ||
1343 | &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, | ||
1344 | &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, | ||
1345 | &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, | ||
1346 | &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, | ||
1347 | NULL | ||
1348 | }, { | ||
1349 | &sensor_dev_attr_pwm2_auto_point1_pwm.dev_attr.attr, | ||
1350 | &sensor_dev_attr_pwm2_auto_point2_pwm.dev_attr.attr, | ||
1351 | &sensor_dev_attr_pwm2_auto_point3_pwm.dev_attr.attr, | ||
1352 | &sensor_dev_attr_pwm2_auto_point4_pwm.dev_attr.attr, | ||
1353 | &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, | ||
1354 | &sensor_dev_attr_pwm2_auto_point1_temp_hyst.dev_attr.attr, | ||
1355 | &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, | ||
1356 | &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, | ||
1357 | &sensor_dev_attr_pwm2_auto_point4_temp.dev_attr.attr, | ||
1358 | NULL | ||
1359 | }, { | ||
1360 | &sensor_dev_attr_pwm3_auto_point1_pwm.dev_attr.attr, | ||
1361 | &sensor_dev_attr_pwm3_auto_point2_pwm.dev_attr.attr, | ||
1362 | &sensor_dev_attr_pwm3_auto_point3_pwm.dev_attr.attr, | ||
1363 | &sensor_dev_attr_pwm3_auto_point4_pwm.dev_attr.attr, | ||
1364 | &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, | ||
1365 | &sensor_dev_attr_pwm3_auto_point1_temp_hyst.dev_attr.attr, | ||
1366 | &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, | ||
1367 | &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, | ||
1368 | &sensor_dev_attr_pwm3_auto_point4_temp.dev_attr.attr, | ||
1369 | NULL | ||
1370 | } }; | ||
1371 | |||
1372 | static const struct attribute_group it87_group_autopwm[3] = { | ||
1373 | { .attrs = it87_attributes_autopwm[0] }, | ||
1374 | { .attrs = it87_attributes_autopwm[1] }, | ||
1375 | { .attrs = it87_attributes_autopwm[2] }, | ||
1376 | }; | ||
1377 | |||
1222 | static struct attribute *it87_attributes_fan_beep[] = { | 1378 | static struct attribute *it87_attributes_fan_beep[] = { |
1223 | &sensor_dev_attr_fan1_beep.dev_attr.attr, | 1379 | &sensor_dev_attr_fan1_beep.dev_attr.attr, |
1224 | &sensor_dev_attr_fan2_beep.dev_attr.attr, | 1380 | &sensor_dev_attr_fan2_beep.dev_attr.attr, |
@@ -1382,6 +1538,9 @@ static void it87_remove_files(struct device *dev) | |||
1382 | if (sio_data->skip_pwm & (1 << 0)) | 1538 | if (sio_data->skip_pwm & (1 << 0)) |
1383 | continue; | 1539 | continue; |
1384 | sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); | 1540 | sysfs_remove_group(&dev->kobj, &it87_group_pwm[i]); |
1541 | if (has_old_autopwm(data)) | ||
1542 | sysfs_remove_group(&dev->kobj, | ||
1543 | &it87_group_autopwm[i]); | ||
1385 | } | 1544 | } |
1386 | if (!sio_data->skip_vid) | 1545 | if (!sio_data->skip_vid) |
1387 | sysfs_remove_group(&dev->kobj, &it87_group_vid); | 1546 | sysfs_remove_group(&dev->kobj, &it87_group_vid); |
@@ -1491,6 +1650,13 @@ static int __devinit it87_probe(struct platform_device *pdev) | |||
1491 | &it87_group_pwm[i]); | 1650 | &it87_group_pwm[i]); |
1492 | if (err) | 1651 | if (err) |
1493 | goto ERROR4; | 1652 | goto ERROR4; |
1653 | |||
1654 | if (!has_old_autopwm(data)) | ||
1655 | continue; | ||
1656 | err = sysfs_create_group(&dev->kobj, | ||
1657 | &it87_group_autopwm[i]); | ||
1658 | if (err) | ||
1659 | goto ERROR4; | ||
1494 | } | 1660 | } |
1495 | } | 1661 | } |
1496 | 1662 | ||
@@ -1624,6 +1790,7 @@ static void __devinit it87_init_device(struct platform_device *pdev) | |||
1624 | for (i = 0; i < 3; i++) { | 1790 | for (i = 0; i < 3; i++) { |
1625 | data->pwm_temp_map[i] = i; | 1791 | data->pwm_temp_map[i] = i; |
1626 | data->pwm_duty[i] = 0x7f; /* Full speed */ | 1792 | data->pwm_duty[i] = 0x7f; /* Full speed */ |
1793 | data->auto_pwm[i][3] = 0x7f; /* Full speed, hard-coded */ | ||
1627 | } | 1794 | } |
1628 | 1795 | ||
1629 | /* Some chips seem to have default value 0xff for all limit | 1796 | /* Some chips seem to have default value 0xff for all limit |
@@ -1703,6 +1870,17 @@ static void it87_update_pwm_ctrl(struct it87_data *data, int nr) | |||
1703 | data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; | 1870 | data->pwm_temp_map[nr] = data->pwm_ctrl[nr] & 0x03; |
1704 | else /* Manual mode */ | 1871 | else /* Manual mode */ |
1705 | data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; | 1872 | data->pwm_duty[nr] = data->pwm_ctrl[nr] & 0x7f; |
1873 | |||
1874 | if (has_old_autopwm(data)) { | ||
1875 | int i; | ||
1876 | |||
1877 | for (i = 0; i < 5 ; i++) | ||
1878 | data->auto_temp[nr][i] = it87_read_value(data, | ||
1879 | IT87_REG_AUTO_TEMP(nr, i)); | ||
1880 | for (i = 0; i < 3 ; i++) | ||
1881 | data->auto_pwm[nr][i] = it87_read_value(data, | ||
1882 | IT87_REG_AUTO_PWM(nr, i)); | ||
1883 | } | ||
1706 | } | 1884 | } |
1707 | 1885 | ||
1708 | static struct it87_data *it87_update_device(struct device *dev) | 1886 | static struct it87_data *it87_update_device(struct device *dev) |