aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorJean Delvare <khali@linux-fr.org>2006-12-12 12:18:26 -0500
committerJean Delvare <khali@arrakis.delvare>2006-12-12 12:18:26 -0500
commit95e353127dfcd86df56a742a96ccf56369929b95 (patch)
treef358198098a47a94f0b6727b6d405e9be8a77489 /drivers
parent6b14a546a5a7fde46d20d3c14a4e91a24a3f1be0 (diff)
hwmon/f71805f: Add manual fan speed control
pwmN files are writable only in manual fan speed control mode. In automatic fan speed control modes, they are read-only and report the duty cycle chosen by the chip. Signed-off-by: Jean Delvare <khali@linux-fr.org>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/hwmon/f71805f.c145
1 files changed, 144 insertions, 1 deletions
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c
index dfca856854c3..6e6542036c6a 100644
--- a/drivers/hwmon/f71805f.c
+++ b/drivers/hwmon/f71805f.c
@@ -112,6 +112,7 @@ superio_exit(int base)
112#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr)) 112#define F71805F_REG_FAN(nr) (0x20 + 2 * (nr))
113#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr)) 113#define F71805F_REG_FAN_LOW(nr) (0x28 + 2 * (nr))
114#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr)) 114#define F71805F_REG_FAN_CTRL(nr) (0x60 + 16 * (nr))
115#define F71805F_REG_PWM_DUTY(nr) (0x6B + 16 * (nr))
115/* temp nr from 0 to 2 (8-bit values) */ 116/* temp nr from 0 to 2 (8-bit values) */
116#define F71805F_REG_TEMP(nr) (0x1B + (nr)) 117#define F71805F_REG_TEMP(nr) (0x1B + (nr))
117#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) 118#define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr))
@@ -124,6 +125,10 @@ superio_exit(int base)
124 125
125/* individual register bits */ 126/* individual register bits */
126#define FAN_CTRL_SKIP 0x80 127#define FAN_CTRL_SKIP 0x80
128#define FAN_CTRL_MODE_MASK 0x03
129#define FAN_CTRL_MODE_SPEED 0x00
130#define FAN_CTRL_MODE_TEMPERATURE 0x01
131#define FAN_CTRL_MODE_MANUAL 0x02
127 132
128/* 133/*
129 * Data structures and manipulation thereof 134 * Data structures and manipulation thereof
@@ -147,6 +152,7 @@ struct f71805f_data {
147 u16 fan[3]; 152 u16 fan[3];
148 u16 fan_low[3]; 153 u16 fan_low[3];
149 u8 fan_ctrl[3]; 154 u8 fan_ctrl[3];
155 u8 pwm[3];
150 u8 temp[3]; 156 u8 temp[3];
151 u8 temp_high[3]; 157 u8 temp_high[3];
152 u8 temp_hyst[3]; 158 u8 temp_hyst[3];
@@ -312,6 +318,10 @@ static struct f71805f_data *f71805f_update_device(struct device *dev)
312 continue; 318 continue;
313 data->fan[nr] = f71805f_read16(data, 319 data->fan[nr] = f71805f_read16(data,
314 F71805F_REG_FAN(nr)); 320 F71805F_REG_FAN(nr));
321 data->fan_ctrl[nr] = f71805f_read8(data,
322 F71805F_REG_FAN_CTRL(nr));
323 data->pwm[nr] = f71805f_read8(data,
324 F71805F_REG_PWM_DUTY(nr));
315 } 325 }
316 for (nr = 0; nr < 3; nr++) { 326 for (nr = 0; nr < 3; nr++) {
317 data->temp[nr] = f71805f_read8(data, 327 data->temp[nr] = f71805f_read8(data,
@@ -484,6 +494,104 @@ static ssize_t set_fan_min(struct device *dev, struct device_attribute
484 return count; 494 return count;
485} 495}
486 496
497static ssize_t show_pwm(struct device *dev, struct device_attribute *devattr,
498 char *buf)
499{
500 struct f71805f_data *data = f71805f_update_device(dev);
501 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
502 int nr = attr->index;
503
504 return sprintf(buf, "%d\n", (int)data->pwm[nr]);
505}
506
507static ssize_t show_pwm_enable(struct device *dev, struct device_attribute
508 *devattr, char *buf)
509{
510 struct f71805f_data *data = f71805f_update_device(dev);
511 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
512 int nr = attr->index;
513 int mode;
514
515 switch (data->fan_ctrl[nr] & FAN_CTRL_MODE_MASK) {
516 case FAN_CTRL_MODE_SPEED:
517 mode = 3;
518 break;
519 case FAN_CTRL_MODE_TEMPERATURE:
520 mode = 2;
521 break;
522 default: /* MANUAL */
523 mode = 1;
524 }
525
526 return sprintf(buf, "%d\n", mode);
527}
528
529static ssize_t set_pwm(struct device *dev, struct device_attribute *devattr,
530 const char *buf, size_t count)
531{
532 struct f71805f_data *data = dev_get_drvdata(dev);
533 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
534 int nr = attr->index;
535 unsigned long val = simple_strtoul(buf, NULL, 10);
536
537 if (val > 255)
538 return -EINVAL;
539
540 mutex_lock(&data->update_lock);
541 data->pwm[nr] = val;
542 f71805f_write8(data, F71805F_REG_PWM_DUTY(nr), data->pwm[nr]);
543 mutex_unlock(&data->update_lock);
544
545 return count;
546}
547
548static struct attribute *f71805f_attr_pwm[];
549
550static ssize_t set_pwm_enable(struct device *dev, struct device_attribute
551 *devattr, const char *buf, size_t count)
552{
553 struct f71805f_data *data = dev_get_drvdata(dev);
554 struct sensor_device_attribute *attr = to_sensor_dev_attr(devattr);
555 int nr = attr->index;
556 unsigned long val = simple_strtoul(buf, NULL, 10);
557 u8 reg;
558
559 if (val < 1 || val > 3)
560 return -EINVAL;
561
562 if (val > 1) { /* Automatic mode, user can't set PWM value */
563 if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
564 S_IRUGO))
565 dev_dbg(dev, "chmod -w pwm%d failed\n", nr + 1);
566 }
567
568 mutex_lock(&data->update_lock);
569 reg = f71805f_read8(data, F71805F_REG_FAN_CTRL(nr))
570 & ~FAN_CTRL_MODE_MASK;
571 switch (val) {
572 case 1:
573 reg |= FAN_CTRL_MODE_MANUAL;
574 break;
575 case 2:
576 reg |= FAN_CTRL_MODE_TEMPERATURE;
577 break;
578 case 3:
579 reg |= FAN_CTRL_MODE_SPEED;
580 break;
581 }
582 data->fan_ctrl[nr] = reg;
583 f71805f_write8(data, F71805F_REG_FAN_CTRL(nr), reg);
584 mutex_unlock(&data->update_lock);
585
586 if (val == 1) { /* Manual mode, user can set PWM value */
587 if (sysfs_chmod_file(&dev->kobj, f71805f_attr_pwm[nr],
588 S_IRUGO | S_IWUSR))
589 dev_dbg(dev, "chmod +w pwm%d failed\n", nr + 1);
590 }
591
592 return count;
593}
594
487static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, 595static ssize_t show_temp(struct device *dev, struct device_attribute *devattr,
488 char *buf) 596 char *buf)
489{ 597{
@@ -672,6 +780,18 @@ static SENSOR_DEVICE_ATTR(temp3_max_hyst, S_IRUGO | S_IWUSR,
672 show_temp_hyst, set_temp_hyst, 2); 780 show_temp_hyst, set_temp_hyst, 2);
673static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); 781static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2);
674 782
783/* pwm (value) files are created read-only, write permission is
784 then added or removed dynamically as needed */
785static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0);
786static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR,
787 show_pwm_enable, set_pwm_enable, 0);
788static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO, show_pwm, set_pwm, 1);
789static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR,
790 show_pwm_enable, set_pwm_enable, 1);
791static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO, show_pwm, set_pwm, 2);
792static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR,
793 show_pwm_enable, set_pwm_enable, 2);
794
675static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); 795static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0);
676static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); 796static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1);
677static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); 797static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2);
@@ -759,21 +879,27 @@ static const struct attribute_group f71805f_group = {
759 .attrs = f71805f_attributes, 879 .attrs = f71805f_attributes,
760}; 880};
761 881
762static struct attribute *f71805f_attributes_fan[3][4] = { 882static struct attribute *f71805f_attributes_fan[3][6] = {
763 { 883 {
764 &sensor_dev_attr_fan1_input.dev_attr.attr, 884 &sensor_dev_attr_fan1_input.dev_attr.attr,
765 &sensor_dev_attr_fan1_min.dev_attr.attr, 885 &sensor_dev_attr_fan1_min.dev_attr.attr,
766 &sensor_dev_attr_fan1_alarm.dev_attr.attr, 886 &sensor_dev_attr_fan1_alarm.dev_attr.attr,
887 &sensor_dev_attr_pwm1.dev_attr.attr,
888 &sensor_dev_attr_pwm1_enable.dev_attr.attr,
767 NULL 889 NULL
768 }, { 890 }, {
769 &sensor_dev_attr_fan2_input.dev_attr.attr, 891 &sensor_dev_attr_fan2_input.dev_attr.attr,
770 &sensor_dev_attr_fan2_min.dev_attr.attr, 892 &sensor_dev_attr_fan2_min.dev_attr.attr,
771 &sensor_dev_attr_fan2_alarm.dev_attr.attr, 893 &sensor_dev_attr_fan2_alarm.dev_attr.attr,
894 &sensor_dev_attr_pwm2.dev_attr.attr,
895 &sensor_dev_attr_pwm2_enable.dev_attr.attr,
772 NULL 896 NULL
773 }, { 897 }, {
774 &sensor_dev_attr_fan3_input.dev_attr.attr, 898 &sensor_dev_attr_fan3_input.dev_attr.attr,
775 &sensor_dev_attr_fan3_min.dev_attr.attr, 899 &sensor_dev_attr_fan3_min.dev_attr.attr,
776 &sensor_dev_attr_fan3_alarm.dev_attr.attr, 900 &sensor_dev_attr_fan3_alarm.dev_attr.attr,
901 &sensor_dev_attr_pwm3.dev_attr.attr,
902 &sensor_dev_attr_pwm3_enable.dev_attr.attr,
777 NULL 903 NULL
778 } 904 }
779}; 905};
@@ -784,6 +910,13 @@ static const struct attribute_group f71805f_group_fan[3] = {
784 { .attrs = f71805f_attributes_fan[2] }, 910 { .attrs = f71805f_attributes_fan[2] },
785}; 911};
786 912
913/* We also need an indexed access to pwmN files to toggle writability */
914static struct attribute *f71805f_attr_pwm[] = {
915 &sensor_dev_attr_pwm1.dev_attr.attr,
916 &sensor_dev_attr_pwm2.dev_attr.attr,
917 &sensor_dev_attr_pwm3.dev_attr.attr,
918};
919
787/* 920/*
788 * Device registration and initialization 921 * Device registration and initialization
789 */ 922 */
@@ -840,6 +973,16 @@ static int __devinit f71805f_probe(struct platform_device *pdev)
840 if ((err = sysfs_create_group(&pdev->dev.kobj, 973 if ((err = sysfs_create_group(&pdev->dev.kobj,
841 &f71805f_group_fan[i]))) 974 &f71805f_group_fan[i])))
842 goto exit_remove_files; 975 goto exit_remove_files;
976 /* If PWM is in manual mode, add write permission */
977 if (data->fan_ctrl[i] & FAN_CTRL_MODE_MANUAL) {
978 if ((err = sysfs_chmod_file(&pdev->dev.kobj,
979 f71805f_attr_pwm[i],
980 S_IRUGO | S_IWUSR))) {
981 dev_err(&pdev->dev, "chmod +w pwm%d failed\n",
982 i + 1);
983 goto exit_remove_files;
984 }
985 }
843 } 986 }
844 987
845 data->class_dev = hwmon_device_register(&pdev->dev); 988 data->class_dev = hwmon_device_register(&pdev->dev);