diff options
author | Jean Delvare <khali@linux-fr.org> | 2012-01-16 16:51:47 -0500 |
---|---|---|
committer | Jean Delvare <khali@endymion.delvare> | 2012-01-16 16:51:47 -0500 |
commit | d216f6809eb690b9a888c286cde68cda4d0c4cfa (patch) | |
tree | 46a83526ded3df6713a0d41514adb1ad465b969f /drivers/hwmon | |
parent | d93ab7807063ade8ad4b3ba55347e333dbde4d52 (diff) |
hwmon: (lm63) Expose automatic fan speed control lookup table
The LM63 and compatible devices have a lookup table to control the fan
speed automatically. Expose it in sysfs. Values are cached for 5
seconds, independently of the other register values to avoid slowing
down "sensors". We might make the table values writable in the future.
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Tested-by: Guenter Roeck <guenter.roeck@ericsson.com>
Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon')
-rw-r--r-- | drivers/hwmon/lm63.c | 148 |
1 files changed, 134 insertions, 14 deletions
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c index a5e4ba82af17..1c06a333ba20 100644 --- a/drivers/hwmon/lm63.c +++ b/drivers/hwmon/lm63.c | |||
@@ -75,6 +75,9 @@ static const unsigned short normal_i2c[] = { 0x18, 0x4c, 0x4e, I2C_CLIENT_END }; | |||
75 | 75 | ||
76 | #define LM63_REG_PWM_VALUE 0x4C | 76 | #define LM63_REG_PWM_VALUE 0x4C |
77 | #define LM63_REG_PWM_FREQ 0x4D | 77 | #define LM63_REG_PWM_FREQ 0x4D |
78 | #define LM63_REG_LUT_TEMP_HYST 0x4F | ||
79 | #define LM63_REG_LUT_TEMP(nr) (0x50 + 2 * (nr)) | ||
80 | #define LM63_REG_LUT_PWM(nr) (0x51 + 2 * (nr)) | ||
78 | 81 | ||
79 | #define LM63_REG_LOCAL_TEMP 0x00 | 82 | #define LM63_REG_LOCAL_TEMP 0x00 |
80 | #define LM63_REG_LOCAL_HIGH 0x05 | 83 | #define LM63_REG_LOCAL_HIGH 0x05 |
@@ -192,7 +195,9 @@ struct lm63_data { | |||
192 | struct device *hwmon_dev; | 195 | struct device *hwmon_dev; |
193 | struct mutex update_lock; | 196 | struct mutex update_lock; |
194 | char valid; /* zero until following fields are valid */ | 197 | char valid; /* zero until following fields are valid */ |
198 | char lut_valid; /* zero until lut fields are valid */ | ||
195 | unsigned long last_updated; /* in jiffies */ | 199 | unsigned long last_updated; /* in jiffies */ |
200 | unsigned long lut_last_updated; /* in jiffies */ | ||
196 | enum chips kind; | 201 | enum chips kind; |
197 | int temp2_offset; | 202 | int temp2_offset; |
198 | 203 | ||
@@ -204,18 +209,22 @@ struct lm63_data { | |||
204 | u16 fan[2]; /* 0: input | 209 | u16 fan[2]; /* 0: input |
205 | 1: low limit */ | 210 | 1: low limit */ |
206 | u8 pwm1_freq; | 211 | u8 pwm1_freq; |
207 | u8 pwm1_value; | 212 | u8 pwm1[9]; /* 0: current output |
208 | s8 temp8[3]; /* 0: local input | 213 | 1-8: lookup table */ |
214 | s8 temp8[11]; /* 0: local input | ||
209 | 1: local high limit | 215 | 1: local high limit |
210 | 2: remote critical limit */ | 216 | 2: remote critical limit |
217 | 3-10: lookup table */ | ||
211 | s16 temp11[4]; /* 0: remote input | 218 | s16 temp11[4]; /* 0: remote input |
212 | 1: remote low limit | 219 | 1: remote low limit |
213 | 2: remote high limit | 220 | 2: remote high limit |
214 | 3: remote offset */ | 221 | 3: remote offset */ |
215 | u16 temp11u; /* remote input (unsigned) */ | 222 | u16 temp11u; /* remote input (unsigned) */ |
216 | u8 temp2_crit_hyst; | 223 | u8 temp2_crit_hyst; |
224 | u8 lut_temp_hyst; | ||
217 | u8 alarms; | 225 | u8 alarms; |
218 | bool pwm_highres; | 226 | bool pwm_highres; |
227 | bool lut_temp_highres; | ||
219 | bool remote_unsigned; /* true if unsigned remote upper limits */ | 228 | bool remote_unsigned; /* true if unsigned remote upper limits */ |
220 | bool trutherm; | 229 | bool trutherm; |
221 | }; | 230 | }; |
@@ -227,6 +236,11 @@ static inline int temp8_from_reg(struct lm63_data *data, int nr) | |||
227 | return TEMP8_FROM_REG(data->temp8[nr]); | 236 | return TEMP8_FROM_REG(data->temp8[nr]); |
228 | } | 237 | } |
229 | 238 | ||
239 | static inline int lut_temp_from_reg(struct lm63_data *data, int nr) | ||
240 | { | ||
241 | return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); | ||
242 | } | ||
243 | |||
230 | /* | 244 | /* |
231 | * Sysfs callback functions and files | 245 | * Sysfs callback functions and files |
232 | */ | 246 | */ |
@@ -261,17 +275,19 @@ static ssize_t set_fan(struct device *dev, struct device_attribute *dummy, | |||
261 | return count; | 275 | return count; |
262 | } | 276 | } |
263 | 277 | ||
264 | static ssize_t show_pwm1(struct device *dev, struct device_attribute *dummy, | 278 | static ssize_t show_pwm1(struct device *dev, struct device_attribute *devattr, |
265 | char *buf) | 279 | char *buf) |
266 | { | 280 | { |
281 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
267 | struct lm63_data *data = lm63_update_device(dev); | 282 | struct lm63_data *data = lm63_update_device(dev); |
283 | int nr = attr->index; | ||
268 | int pwm; | 284 | int pwm; |
269 | 285 | ||
270 | if (data->pwm_highres) | 286 | if (data->pwm_highres) |
271 | pwm = data->pwm1_value; | 287 | pwm = data->pwm1[nr]; |
272 | else | 288 | else |
273 | pwm = data->pwm1_value >= 2 * data->pwm1_freq ? | 289 | pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ? |
274 | 255 : (data->pwm1_value * 255 + data->pwm1_freq) / | 290 | 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) / |
275 | (2 * data->pwm1_freq); | 291 | (2 * data->pwm1_freq); |
276 | 292 | ||
277 | return sprintf(buf, "%d\n", pwm); | 293 | return sprintf(buf, "%d\n", pwm); |
@@ -294,9 +310,9 @@ static ssize_t set_pwm1(struct device *dev, struct device_attribute *dummy, | |||
294 | 310 | ||
295 | val = SENSORS_LIMIT(val, 0, 255); | 311 | val = SENSORS_LIMIT(val, 0, 255); |
296 | mutex_lock(&data->update_lock); | 312 | mutex_lock(&data->update_lock); |
297 | data->pwm1_value = data->pwm_highres ? val : | 313 | data->pwm1[0] = data->pwm_highres ? val : |
298 | (val * data->pwm1_freq * 2 + 127) / 255; | 314 | (val * data->pwm1_freq * 2 + 127) / 255; |
299 | i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value); | 315 | i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]); |
300 | mutex_unlock(&data->update_lock); | 316 | mutex_unlock(&data->update_lock); |
301 | return count; | 317 | return count; |
302 | } | 318 | } |
@@ -333,6 +349,16 @@ static ssize_t show_remote_temp8(struct device *dev, | |||
333 | + data->temp2_offset); | 349 | + data->temp2_offset); |
334 | } | 350 | } |
335 | 351 | ||
352 | static ssize_t show_lut_temp(struct device *dev, | ||
353 | struct device_attribute *devattr, | ||
354 | char *buf) | ||
355 | { | ||
356 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
357 | struct lm63_data *data = lm63_update_device(dev); | ||
358 | return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) | ||
359 | + data->temp2_offset); | ||
360 | } | ||
361 | |||
336 | static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, | 362 | static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr, |
337 | const char *buf, size_t count) | 363 | const char *buf, size_t count) |
338 | { | 364 | { |
@@ -440,6 +466,17 @@ static ssize_t show_temp2_crit_hyst(struct device *dev, | |||
440 | - TEMP8_FROM_REG(data->temp2_crit_hyst)); | 466 | - TEMP8_FROM_REG(data->temp2_crit_hyst)); |
441 | } | 467 | } |
442 | 468 | ||
469 | static ssize_t show_lut_temp_hyst(struct device *dev, | ||
470 | struct device_attribute *devattr, char *buf) | ||
471 | { | ||
472 | struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr); | ||
473 | struct lm63_data *data = lm63_update_device(dev); | ||
474 | |||
475 | return sprintf(buf, "%d\n", lut_temp_from_reg(data, attr->index) | ||
476 | + data->temp2_offset | ||
477 | - TEMP8_FROM_REG(data->lut_temp_hyst)); | ||
478 | } | ||
479 | |||
443 | /* | 480 | /* |
444 | * And now the other way around, user-space provides an absolute | 481 | * And now the other way around, user-space provides an absolute |
445 | * hysteresis value and we have to store a relative one | 482 | * hysteresis value and we have to store a relative one |
@@ -574,8 +611,48 @@ static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, show_fan, NULL, 0); | |||
574 | static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, | 611 | static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan, |
575 | set_fan, 1); | 612 | set_fan, 1); |
576 | 613 | ||
577 | static DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1); | 614 | static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); |
578 | static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); | 615 | static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); |
616 | static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); | ||
617 | static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, | ||
618 | show_lut_temp, NULL, 3); | ||
619 | static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp_hyst, S_IRUGO, | ||
620 | show_lut_temp_hyst, NULL, 3); | ||
621 | static SENSOR_DEVICE_ATTR(pwm1_auto_point2_pwm, S_IRUGO, show_pwm1, NULL, 2); | ||
622 | static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp, S_IRUGO, | ||
623 | show_lut_temp, NULL, 4); | ||
624 | static SENSOR_DEVICE_ATTR(pwm1_auto_point2_temp_hyst, S_IRUGO, | ||
625 | show_lut_temp_hyst, NULL, 4); | ||
626 | static SENSOR_DEVICE_ATTR(pwm1_auto_point3_pwm, S_IRUGO, show_pwm1, NULL, 3); | ||
627 | static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp, S_IRUGO, | ||
628 | show_lut_temp, NULL, 5); | ||
629 | static SENSOR_DEVICE_ATTR(pwm1_auto_point3_temp_hyst, S_IRUGO, | ||
630 | show_lut_temp_hyst, NULL, 5); | ||
631 | static SENSOR_DEVICE_ATTR(pwm1_auto_point4_pwm, S_IRUGO, show_pwm1, NULL, 4); | ||
632 | static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp, S_IRUGO, | ||
633 | show_lut_temp, NULL, 6); | ||
634 | static SENSOR_DEVICE_ATTR(pwm1_auto_point4_temp_hyst, S_IRUGO, | ||
635 | show_lut_temp_hyst, NULL, 6); | ||
636 | static SENSOR_DEVICE_ATTR(pwm1_auto_point5_pwm, S_IRUGO, show_pwm1, NULL, 5); | ||
637 | static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp, S_IRUGO, | ||
638 | show_lut_temp, NULL, 7); | ||
639 | static SENSOR_DEVICE_ATTR(pwm1_auto_point5_temp_hyst, S_IRUGO, | ||
640 | show_lut_temp_hyst, NULL, 7); | ||
641 | static SENSOR_DEVICE_ATTR(pwm1_auto_point6_pwm, S_IRUGO, show_pwm1, NULL, 6); | ||
642 | static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp, S_IRUGO, | ||
643 | show_lut_temp, NULL, 8); | ||
644 | static SENSOR_DEVICE_ATTR(pwm1_auto_point6_temp_hyst, S_IRUGO, | ||
645 | show_lut_temp_hyst, NULL, 8); | ||
646 | static SENSOR_DEVICE_ATTR(pwm1_auto_point7_pwm, S_IRUGO, show_pwm1, NULL, 7); | ||
647 | static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp, S_IRUGO, | ||
648 | show_lut_temp, NULL, 9); | ||
649 | static SENSOR_DEVICE_ATTR(pwm1_auto_point7_temp_hyst, S_IRUGO, | ||
650 | show_lut_temp_hyst, NULL, 9); | ||
651 | static SENSOR_DEVICE_ATTR(pwm1_auto_point8_pwm, S_IRUGO, show_pwm1, NULL, 8); | ||
652 | static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp, S_IRUGO, | ||
653 | show_lut_temp, NULL, 10); | ||
654 | static SENSOR_DEVICE_ATTR(pwm1_auto_point8_temp_hyst, S_IRUGO, | ||
655 | show_lut_temp_hyst, NULL, 10); | ||
579 | 656 | ||
580 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0); | 657 | static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0); |
581 | static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8, | 658 | static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8, |
@@ -609,8 +686,33 @@ static DEVICE_ATTR(update_interval, S_IRUGO | S_IWUSR, show_update_interval, | |||
609 | set_update_interval); | 686 | set_update_interval); |
610 | 687 | ||
611 | static struct attribute *lm63_attributes[] = { | 688 | static struct attribute *lm63_attributes[] = { |
612 | &dev_attr_pwm1.attr, | 689 | &sensor_dev_attr_pwm1.dev_attr.attr, |
613 | &dev_attr_pwm1_enable.attr, | 690 | &dev_attr_pwm1_enable.attr, |
691 | &sensor_dev_attr_pwm1_auto_point1_pwm.dev_attr.attr, | ||
692 | &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, | ||
693 | &sensor_dev_attr_pwm1_auto_point1_temp_hyst.dev_attr.attr, | ||
694 | &sensor_dev_attr_pwm1_auto_point2_pwm.dev_attr.attr, | ||
695 | &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, | ||
696 | &sensor_dev_attr_pwm1_auto_point2_temp_hyst.dev_attr.attr, | ||
697 | &sensor_dev_attr_pwm1_auto_point3_pwm.dev_attr.attr, | ||
698 | &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, | ||
699 | &sensor_dev_attr_pwm1_auto_point3_temp_hyst.dev_attr.attr, | ||
700 | &sensor_dev_attr_pwm1_auto_point4_pwm.dev_attr.attr, | ||
701 | &sensor_dev_attr_pwm1_auto_point4_temp.dev_attr.attr, | ||
702 | &sensor_dev_attr_pwm1_auto_point4_temp_hyst.dev_attr.attr, | ||
703 | &sensor_dev_attr_pwm1_auto_point5_pwm.dev_attr.attr, | ||
704 | &sensor_dev_attr_pwm1_auto_point5_temp.dev_attr.attr, | ||
705 | &sensor_dev_attr_pwm1_auto_point5_temp_hyst.dev_attr.attr, | ||
706 | &sensor_dev_attr_pwm1_auto_point6_pwm.dev_attr.attr, | ||
707 | &sensor_dev_attr_pwm1_auto_point6_temp.dev_attr.attr, | ||
708 | &sensor_dev_attr_pwm1_auto_point6_temp_hyst.dev_attr.attr, | ||
709 | &sensor_dev_attr_pwm1_auto_point7_pwm.dev_attr.attr, | ||
710 | &sensor_dev_attr_pwm1_auto_point7_temp.dev_attr.attr, | ||
711 | &sensor_dev_attr_pwm1_auto_point7_temp_hyst.dev_attr.attr, | ||
712 | &sensor_dev_attr_pwm1_auto_point8_pwm.dev_attr.attr, | ||
713 | &sensor_dev_attr_pwm1_auto_point8_temp.dev_attr.attr, | ||
714 | &sensor_dev_attr_pwm1_auto_point8_temp_hyst.dev_attr.attr, | ||
715 | |||
614 | &sensor_dev_attr_temp1_input.dev_attr.attr, | 716 | &sensor_dev_attr_temp1_input.dev_attr.attr, |
615 | &sensor_dev_attr_temp2_input.dev_attr.attr, | 717 | &sensor_dev_attr_temp2_input.dev_attr.attr, |
616 | &sensor_dev_attr_temp2_min.dev_attr.attr, | 718 | &sensor_dev_attr_temp2_min.dev_attr.attr, |
@@ -834,6 +936,8 @@ static void lm63_init_client(struct i2c_client *client) | |||
834 | u8 config_enhanced | 936 | u8 config_enhanced |
835 | = i2c_smbus_read_byte_data(client, | 937 | = i2c_smbus_read_byte_data(client, |
836 | LM96163_REG_CONFIG_ENHANCED); | 938 | LM96163_REG_CONFIG_ENHANCED); |
939 | if (config_enhanced & 0x20) | ||
940 | data->lut_temp_highres = true; | ||
837 | if ((config_enhanced & 0x10) | 941 | if ((config_enhanced & 0x10) |
838 | && !(data->config_fan & 0x08) && data->pwm1_freq == 8) | 942 | && !(data->config_fan & 0x08) && data->pwm1_freq == 8) |
839 | data->pwm_highres = true; | 943 | data->pwm_highres = true; |
@@ -872,6 +976,7 @@ static struct lm63_data *lm63_update_device(struct device *dev) | |||
872 | struct i2c_client *client = to_i2c_client(dev); | 976 | struct i2c_client *client = to_i2c_client(dev); |
873 | struct lm63_data *data = i2c_get_clientdata(client); | 977 | struct lm63_data *data = i2c_get_clientdata(client); |
874 | unsigned long next_update; | 978 | unsigned long next_update; |
979 | int i; | ||
875 | 980 | ||
876 | mutex_lock(&data->update_lock); | 981 | mutex_lock(&data->update_lock); |
877 | 982 | ||
@@ -895,8 +1000,8 @@ static struct lm63_data *lm63_update_device(struct device *dev) | |||
895 | LM63_REG_PWM_FREQ); | 1000 | LM63_REG_PWM_FREQ); |
896 | if (data->pwm1_freq == 0) | 1001 | if (data->pwm1_freq == 0) |
897 | data->pwm1_freq = 1; | 1002 | data->pwm1_freq = 1; |
898 | data->pwm1_value = i2c_smbus_read_byte_data(client, | 1003 | data->pwm1[0] = i2c_smbus_read_byte_data(client, |
899 | LM63_REG_PWM_VALUE); | 1004 | LM63_REG_PWM_VALUE); |
900 | 1005 | ||
901 | data->temp8[0] = i2c_smbus_read_byte_data(client, | 1006 | data->temp8[0] = i2c_smbus_read_byte_data(client, |
902 | LM63_REG_LOCAL_TEMP); | 1007 | LM63_REG_LOCAL_TEMP); |
@@ -939,6 +1044,21 @@ static struct lm63_data *lm63_update_device(struct device *dev) | |||
939 | data->valid = 1; | 1044 | data->valid = 1; |
940 | } | 1045 | } |
941 | 1046 | ||
1047 | if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || | ||
1048 | !data->lut_valid) { | ||
1049 | for (i = 0; i < 8; i++) { | ||
1050 | data->pwm1[1 + i] = i2c_smbus_read_byte_data(client, | ||
1051 | LM63_REG_LUT_PWM(i)); | ||
1052 | data->temp8[3 + i] = i2c_smbus_read_byte_data(client, | ||
1053 | LM63_REG_LUT_TEMP(i)); | ||
1054 | } | ||
1055 | data->lut_temp_hyst = i2c_smbus_read_byte_data(client, | ||
1056 | LM63_REG_LUT_TEMP_HYST); | ||
1057 | |||
1058 | data->lut_last_updated = jiffies; | ||
1059 | data->lut_valid = 1; | ||
1060 | } | ||
1061 | |||
942 | mutex_unlock(&data->update_lock); | 1062 | mutex_unlock(&data->update_lock); |
943 | 1063 | ||
944 | return data; | 1064 | return data; |