diff options
author | Guenter Roeck <linux@roeck-us.net> | 2013-12-14 12:30:21 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2013-12-18 19:47:05 -0500 |
commit | e7f5f275c62a820fdddfced5882996143d0d7e72 (patch) | |
tree | e1ab9f88354f551fce73aa360114a6bdfdd514a1 /drivers/char/i8k.c | |
parent | e551b15250bfb25ed9a136496e8bfde921194ea1 (diff) |
i8k: Implement hwmon based fan speed control
Fan speed can be set to off, slow, and fast, which can be exported
to userspace through the hwmon ABI as pwm1 / pwm2.
Cc: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Guenter Roeck <linux@roeck-us.net>
Acked-by: Arnd Bergmann <arnd@arndb.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/char/i8k.c')
-rw-r--r-- | drivers/char/i8k.c | 49 |
1 files changed, 44 insertions, 5 deletions
diff --git a/drivers/char/i8k.c b/drivers/char/i8k.c index 216b6900bdc8..e210f858d3cb 100644 --- a/drivers/char/i8k.c +++ b/drivers/char/i8k.c | |||
@@ -509,6 +509,39 @@ static ssize_t i8k_hwmon_show_fan(struct device *dev, | |||
509 | return sprintf(buf, "%d\n", fan_speed); | 509 | return sprintf(buf, "%d\n", fan_speed); |
510 | } | 510 | } |
511 | 511 | ||
512 | static ssize_t i8k_hwmon_show_pwm(struct device *dev, | ||
513 | struct device_attribute *devattr, | ||
514 | char *buf) | ||
515 | { | ||
516 | int index = to_sensor_dev_attr(devattr)->index; | ||
517 | int status; | ||
518 | |||
519 | status = i8k_get_fan_status(index); | ||
520 | if (status < 0) | ||
521 | return -EIO; | ||
522 | return sprintf(buf, "%d\n", clamp_val(status * 128, 0, 255)); | ||
523 | } | ||
524 | |||
525 | static ssize_t i8k_hwmon_set_pwm(struct device *dev, | ||
526 | struct device_attribute *attr, | ||
527 | const char *buf, size_t count) | ||
528 | { | ||
529 | int index = to_sensor_dev_attr(attr)->index; | ||
530 | unsigned long val; | ||
531 | int err; | ||
532 | |||
533 | err = kstrtoul(buf, 10, &val); | ||
534 | if (err) | ||
535 | return err; | ||
536 | val = clamp_val(DIV_ROUND_CLOSEST(val, 128), 0, 2); | ||
537 | |||
538 | mutex_lock(&i8k_mutex); | ||
539 | err = i8k_set_fan(index, val); | ||
540 | mutex_unlock(&i8k_mutex); | ||
541 | |||
542 | return err < 0 ? -EIO : count; | ||
543 | } | ||
544 | |||
512 | static ssize_t i8k_hwmon_show_label(struct device *dev, | 545 | static ssize_t i8k_hwmon_show_label(struct device *dev, |
513 | struct device_attribute *devattr, | 546 | struct device_attribute *devattr, |
514 | char *buf) | 547 | char *buf) |
@@ -529,8 +562,12 @@ static SENSOR_DEVICE_ATTR(temp3_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 2); | |||
529 | static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3); | 562 | static SENSOR_DEVICE_ATTR(temp4_input, S_IRUGO, i8k_hwmon_show_temp, NULL, 3); |
530 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, | 563 | static SENSOR_DEVICE_ATTR(fan1_input, S_IRUGO, i8k_hwmon_show_fan, NULL, |
531 | I8K_FAN_LEFT); | 564 | I8K_FAN_LEFT); |
565 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, | ||
566 | i8k_hwmon_set_pwm, I8K_FAN_LEFT); | ||
532 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, | 567 | static SENSOR_DEVICE_ATTR(fan2_input, S_IRUGO, i8k_hwmon_show_fan, NULL, |
533 | I8K_FAN_RIGHT); | 568 | I8K_FAN_RIGHT); |
569 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO | S_IWUSR, i8k_hwmon_show_pwm, | ||
570 | i8k_hwmon_set_pwm, I8K_FAN_RIGHT); | ||
534 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0); | 571 | static SENSOR_DEVICE_ATTR(temp1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 0); |
535 | static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); | 572 | static SENSOR_DEVICE_ATTR(fan1_label, S_IRUGO, i8k_hwmon_show_label, NULL, 1); |
536 | static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); | 573 | static SENSOR_DEVICE_ATTR(fan2_label, S_IRUGO, i8k_hwmon_show_label, NULL, 2); |
@@ -542,9 +579,11 @@ static struct attribute *i8k_attrs[] = { | |||
542 | &sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */ | 579 | &sensor_dev_attr_temp3_input.dev_attr.attr, /* 3 */ |
543 | &sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */ | 580 | &sensor_dev_attr_temp4_input.dev_attr.attr, /* 4 */ |
544 | &sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */ | 581 | &sensor_dev_attr_fan1_input.dev_attr.attr, /* 5 */ |
545 | &sensor_dev_attr_fan1_label.dev_attr.attr, /* 6 */ | 582 | &sensor_dev_attr_pwm1.dev_attr.attr, /* 6 */ |
546 | &sensor_dev_attr_fan2_input.dev_attr.attr, /* 7 */ | 583 | &sensor_dev_attr_fan1_label.dev_attr.attr, /* 7 */ |
547 | &sensor_dev_attr_fan2_label.dev_attr.attr, /* 8 */ | 584 | &sensor_dev_attr_fan2_input.dev_attr.attr, /* 8 */ |
585 | &sensor_dev_attr_pwm2.dev_attr.attr, /* 9 */ | ||
586 | &sensor_dev_attr_fan2_label.dev_attr.attr, /* 10 */ | ||
548 | NULL | 587 | NULL |
549 | }; | 588 | }; |
550 | 589 | ||
@@ -560,10 +599,10 @@ static umode_t i8k_is_visible(struct kobject *kobj, struct attribute *attr, | |||
560 | return 0; | 599 | return 0; |
561 | if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) | 600 | if (index == 4 && !(i8k_hwmon_flags & I8K_HWMON_HAVE_TEMP4)) |
562 | return 0; | 601 | return 0; |
563 | if ((index == 5 || index == 6) && | 602 | if (index >= 5 && index <= 7 && |
564 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) | 603 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN1)) |
565 | return 0; | 604 | return 0; |
566 | if ((index == 7 || index == 8) && | 605 | if (index >= 8 && index <= 10 && |
567 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) | 606 | !(i8k_hwmon_flags & I8K_HWMON_HAVE_FAN2)) |
568 | return 0; | 607 | return 0; |
569 | 608 | ||