aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hwmon/lm63.c
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2012-03-23 05:02:19 -0400
committerJean Delvare <khali@endymion.delvare>2012-03-23 05:02:19 -0400
commit817c6cc546a4ebea8016766f0f26e7d53118c6b6 (patch)
tree2a01f9d98feccc4fed2c05c2f13f25375dfecdf2 /drivers/hwmon/lm63.c
parentdac27dce318401fee028b19cdd4c52fe163f53f1 (diff)
hwmon: (lm63) Make fan speed control strategy changeable
Let the user switch between automatic and manual fan speed control. Before switching to automatic fan speed control, we always check that the lookup table looks sane. Signed-off-by: Jean Delvare <khali@linux-fr.org> Acked-by: Guenter Roeck <guenter.roeck@ericsson.com>
Diffstat (limited to 'drivers/hwmon/lm63.c')
-rw-r--r--drivers/hwmon/lm63.c105
1 files changed, 89 insertions, 16 deletions
diff --git a/drivers/hwmon/lm63.c b/drivers/hwmon/lm63.c
index b90bdc908e5e..910599ac31ca 100644
--- a/drivers/hwmon/lm63.c
+++ b/drivers/hwmon/lm63.c
@@ -205,12 +205,36 @@ static inline int lut_temp_from_reg(struct lm63_data *data, int nr)
205 return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000); 205 return data->temp8[nr] * (data->lut_temp_highres ? 500 : 1000);
206} 206}
207 207
208/*
209 * Update the lookup table register cache.
210 * client->update_lock must be held when calling this function.
211 */
212static void lm63_update_lut(struct i2c_client *client)
213{
214 struct lm63_data *data = i2c_get_clientdata(client);
215 int i;
216
217 if (time_after(jiffies, data->lut_last_updated + 5 * HZ) ||
218 !data->lut_valid) {
219 for (i = 0; i < data->lut_size; i++) {
220 data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
221 LM63_REG_LUT_PWM(i));
222 data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
223 LM63_REG_LUT_TEMP(i));
224 }
225 data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
226 LM63_REG_LUT_TEMP_HYST);
227
228 data->lut_last_updated = jiffies;
229 data->lut_valid = 1;
230 }
231}
232
208static struct lm63_data *lm63_update_device(struct device *dev) 233static struct lm63_data *lm63_update_device(struct device *dev)
209{ 234{
210 struct i2c_client *client = to_i2c_client(dev); 235 struct i2c_client *client = to_i2c_client(dev);
211 struct lm63_data *data = i2c_get_clientdata(client); 236 struct lm63_data *data = i2c_get_clientdata(client);
212 unsigned long next_update; 237 unsigned long next_update;
213 int i;
214 238
215 mutex_lock(&data->update_lock); 239 mutex_lock(&data->update_lock);
216 240
@@ -278,20 +302,7 @@ static struct lm63_data *lm63_update_device(struct device *dev)
278 data->valid = 1; 302 data->valid = 1;
279 } 303 }
280 304
281 if (time_after(jiffies, data->lut_last_updated + 5 * HZ) || 305 lm63_update_lut(client);
282 !data->lut_valid) {
283 for (i = 0; i < data->lut_size; i++) {
284 data->pwm1[1 + i] = i2c_smbus_read_byte_data(client,
285 LM63_REG_LUT_PWM(i));
286 data->temp8[3 + i] = i2c_smbus_read_byte_data(client,
287 LM63_REG_LUT_TEMP(i));
288 }
289 data->lut_temp_hyst = i2c_smbus_read_byte_data(client,
290 LM63_REG_LUT_TEMP_HYST);
291
292 data->lut_last_updated = jiffies;
293 data->lut_valid = 1;
294 }
295 306
296 mutex_unlock(&data->update_lock); 307 mutex_unlock(&data->update_lock);
297 308
@@ -299,6 +310,32 @@ static struct lm63_data *lm63_update_device(struct device *dev)
299} 310}
300 311
301/* 312/*
313 * Trip points in the lookup table should be in ascending order for both
314 * temperatures and PWM output values.
315 */
316static int lm63_lut_looks_bad(struct i2c_client *client)
317{
318 struct lm63_data *data = i2c_get_clientdata(client);
319 int i;
320
321 mutex_lock(&data->update_lock);
322 lm63_update_lut(client);
323
324 for (i = 1; i < data->lut_size; i++) {
325 if (data->pwm1[1 + i - 1] > data->pwm1[1 + i]
326 || data->temp8[3 + i - 1] > data->temp8[3 + i]) {
327 dev_warn(&client->dev,
328 "Lookup table doesn't look sane (check entries %d and %d)\n",
329 i, i + 1);
330 break;
331 }
332 }
333 mutex_unlock(&data->update_lock);
334
335 return i == data->lut_size ? 0 : 1;
336}
337
338/*
302 * Sysfs callback functions and files 339 * Sysfs callback functions and files
303 */ 340 */
304 341
@@ -381,6 +418,41 @@ static ssize_t show_pwm1_enable(struct device *dev,
381 return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2); 418 return sprintf(buf, "%d\n", data->config_fan & 0x20 ? 1 : 2);
382} 419}
383 420
421static ssize_t set_pwm1_enable(struct device *dev,
422 struct device_attribute *dummy,
423 const char *buf, size_t count)
424{
425 struct i2c_client *client = to_i2c_client(dev);
426 struct lm63_data *data = i2c_get_clientdata(client);
427 unsigned long val;
428 int err;
429
430 err = kstrtoul(buf, 10, &val);
431 if (err)
432 return err;
433 if (val < 1 || val > 2)
434 return -EINVAL;
435
436 /*
437 * Only let the user switch to automatic mode if the lookup table
438 * looks sane.
439 */
440 if (val == 2 && lm63_lut_looks_bad(client))
441 return -EPERM;
442
443 mutex_lock(&data->update_lock);
444 data->config_fan = i2c_smbus_read_byte_data(client,
445 LM63_REG_CONFIG_FAN);
446 if (val == 1)
447 data->config_fan |= 0x20;
448 else
449 data->config_fan &= ~0x20;
450 i2c_smbus_write_byte_data(client, LM63_REG_CONFIG_FAN,
451 data->config_fan);
452 mutex_unlock(&data->update_lock);
453 return count;
454}
455
384/* 456/*
385 * There are 8bit registers for both local(temp1) and remote(temp2) sensor. 457 * There are 8bit registers for both local(temp1) and remote(temp2) sensor.
386 * For remote sensor registers temp2_offset has to be considered, 458 * For remote sensor registers temp2_offset has to be considered,
@@ -669,7 +741,8 @@ static SENSOR_DEVICE_ATTR(fan1_min, S_IWUSR | S_IRUGO, show_fan,
669 set_fan, 1); 741 set_fan, 1);
670 742
671static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0); 743static SENSOR_DEVICE_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm1, set_pwm1, 0);
672static DEVICE_ATTR(pwm1_enable, S_IRUGO, show_pwm1_enable, NULL); 744static DEVICE_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
745 show_pwm1_enable, set_pwm1_enable);
673static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1); 746static SENSOR_DEVICE_ATTR(pwm1_auto_point1_pwm, S_IRUGO, show_pwm1, NULL, 1);
674static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO, 747static SENSOR_DEVICE_ATTR(pwm1_auto_point1_temp, S_IRUGO,
675 show_lut_temp, NULL, 3); 748 show_lut_temp, NULL, 3);