Commit d216f6809eb690b9a888c286cde68cda4d0c4cfa

Authored by Jean Delvare
Committed by Jean Delvare
1 parent d93ab78070

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>

Showing 2 changed files with 136 additions and 15 deletions Side-by-side Diff

Documentation/hwmon/lm63
... ... @@ -66,7 +66,8 @@
66 66  
67 67 The lm63 driver will not update its values more frequently than configured with
68 68 the update_interval sysfs attribute; reading them more often will do no harm,
69   -but will return 'old' values.
  69 +but will return 'old' values. Values in the automatic fan control lookup table
  70 +(attributes pwm1_auto_*) have their own independent lifetime of 5 seconds.
70 71  
71 72 The LM64 is effectively an LM63 with GPIO lines. The driver does not
72 73 support these GPIO lines at present.
drivers/hwmon/lm63.c
... ... @@ -75,6 +75,9 @@
75 75  
76 76 #define LM63_REG_PWM_VALUE 0x4C
77 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 82 #define LM63_REG_LOCAL_TEMP 0x00
80 83 #define LM63_REG_LOCAL_HIGH 0x05
81 84  
... ... @@ -192,7 +195,9 @@
192 195 struct device *hwmon_dev;
193 196 struct mutex update_lock;
194 197 char valid; /* zero until following fields are valid */
  198 + char lut_valid; /* zero until lut fields are valid */
195 199 unsigned long last_updated; /* in jiffies */
  200 + unsigned long lut_last_updated; /* in jiffies */
196 201 enum chips kind;
197 202 int temp2_offset;
198 203  
199 204  
200 205  
201 206  
... ... @@ -204,18 +209,22 @@
204 209 u16 fan[2]; /* 0: input
205 210 1: low limit */
206 211 u8 pwm1_freq;
207   - u8 pwm1_value;
208   - s8 temp8[3]; /* 0: local input
  212 + u8 pwm1[9]; /* 0: current output
  213 + 1-8: lookup table */
  214 + s8 temp8[11]; /* 0: local input
209 215 1: local high limit
210   - 2: remote critical limit */
  216 + 2: remote critical limit
  217 + 3-10: lookup table */
211 218 s16 temp11[4]; /* 0: remote input
212 219 1: remote low limit
213 220 2: remote high limit
214 221 3: remote offset */
215 222 u16 temp11u; /* remote input (unsigned) */
216 223 u8 temp2_crit_hyst;
  224 + u8 lut_temp_hyst;
217 225 u8 alarms;
218 226 bool pwm_highres;
  227 + bool lut_temp_highres;
219 228 bool remote_unsigned; /* true if unsigned remote upper limits */
220 229 bool trutherm;
221 230 };
... ... @@ -227,6 +236,11 @@
227 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 245 * Sysfs callback functions and files
232 246 */
233 247  
234 248  
235 249  
236 250  
... ... @@ -261,17 +275,19 @@
261 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 279 char *buf)
266 280 {
  281 + struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
267 282 struct lm63_data *data = lm63_update_device(dev);
  283 + int nr = attr->index;
268 284 int pwm;
269 285  
270 286 if (data->pwm_highres)
271   - pwm = data->pwm1_value;
  287 + pwm = data->pwm1[nr];
272 288 else
273   - pwm = data->pwm1_value >= 2 * data->pwm1_freq ?
274   - 255 : (data->pwm1_value * 255 + data->pwm1_freq) /
  289 + pwm = data->pwm1[nr] >= 2 * data->pwm1_freq ?
  290 + 255 : (data->pwm1[nr] * 255 + data->pwm1_freq) /
275 291 (2 * data->pwm1_freq);
276 292  
277 293 return sprintf(buf, "%d\n", pwm);
... ... @@ -294,9 +310,9 @@
294 310  
295 311 val = SENSORS_LIMIT(val, 0, 255);
296 312 mutex_lock(&data->update_lock);
297   - data->pwm1_value = data->pwm_highres ? val :
298   - (val * data->pwm1_freq * 2 + 127) / 255;
299   - i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1_value);
  313 + data->pwm1[0] = data->pwm_highres ? val :
  314 + (val * data->pwm1_freq * 2 + 127) / 255;
  315 + i2c_smbus_write_byte_data(client, LM63_REG_PWM_VALUE, data->pwm1[0]);
300 316 mutex_unlock(&data->update_lock);
301 317 return count;
302 318 }
... ... @@ -333,6 +349,16 @@
333 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 362 static ssize_t set_temp8(struct device *dev, struct device_attribute *devattr,
337 363 const char *buf, size_t count)
338 364 {
... ... @@ -440,6 +466,17 @@
440 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 481 * And now the other way around, user-space provides an absolute
445 482 * hysteresis value and we have to store a relative one
446 483  
... ... @@ -574,8 +611,48 @@
574 611 static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
575 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 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 657 static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_local_temp8, NULL, 0);
581 658 static SENSOR_DEVICE_ATTR(temp1_max, S_IWUSR | S_IRUGO, show_local_temp8,
582 659  
... ... @@ -609,8 +686,33 @@
609 686 set_update_interval);
610 687  
611 688 static struct attribute *lm63_attributes[] = {
612   - &dev_attr_pwm1.attr,
  689 + &sensor_dev_attr_pwm1.dev_attr.attr,
613 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 716 &sensor_dev_attr_temp1_input.dev_attr.attr,
615 717 &sensor_dev_attr_temp2_input.dev_attr.attr,
616 718 &sensor_dev_attr_temp2_min.dev_attr.attr,
... ... @@ -834,6 +936,8 @@
834 936 u8 config_enhanced
835 937 = i2c_smbus_read_byte_data(client,
836 938 LM96163_REG_CONFIG_ENHANCED);
  939 + if (config_enhanced & 0x20)
  940 + data->lut_temp_highres = true;
837 941 if ((config_enhanced & 0x10)
838 942 && !(data->config_fan & 0x08) && data->pwm1_freq == 8)
839 943 data->pwm_highres = true;
... ... @@ -872,6 +976,7 @@
872 976 struct i2c_client *client = to_i2c_client(dev);
873 977 struct lm63_data *data = i2c_get_clientdata(client);
874 978 unsigned long next_update;
  979 + int i;
875 980  
876 981 mutex_lock(&data->update_lock);
877 982  
... ... @@ -895,8 +1000,8 @@
895 1000 LM63_REG_PWM_FREQ);
896 1001 if (data->pwm1_freq == 0)
897 1002 data->pwm1_freq = 1;
898   - data->pwm1_value = i2c_smbus_read_byte_data(client,
899   - LM63_REG_PWM_VALUE);
  1003 + data->pwm1[0] = i2c_smbus_read_byte_data(client,
  1004 + LM63_REG_PWM_VALUE);
900 1005  
901 1006 data->temp8[0] = i2c_smbus_read_byte_data(client,
902 1007 LM63_REG_LOCAL_TEMP);
... ... @@ -937,6 +1042,21 @@
937 1042  
938 1043 data->last_updated = jiffies;
939 1044 data->valid = 1;
  1045 + }
  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;
940 1060 }
941 1061  
942 1062 mutex_unlock(&data->update_lock);