diff options
author | Jean Delvare <khali@linux-fr.org> | 2006-12-12 12:18:26 -0500 |
---|---|---|
committer | Jean Delvare <khali@arrakis.delvare> | 2006-12-12 12:18:26 -0500 |
commit | 95e353127dfcd86df56a742a96ccf56369929b95 (patch) | |
tree | f358198098a47a94f0b6727b6d405e9be8a77489 /drivers | |
parent | 6b14a546a5a7fde46d20d3c14a4e91a24a3f1be0 (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.c | 145 |
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 | ||
497 | static 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 | |||
507 | static 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 | |||
529 | static 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 | |||
548 | static struct attribute *f71805f_attr_pwm[]; | ||
549 | |||
550 | static 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 | |||
487 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, | 595 | static 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); |
673 | static SENSOR_DEVICE_ATTR(temp3_type, S_IRUGO, show_temp_type, NULL, 2); | 781 | static 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 */ | ||
785 | static SENSOR_DEVICE_ATTR(pwm1, S_IRUGO, show_pwm, set_pwm, 0); | ||
786 | static SENSOR_DEVICE_ATTR(pwm1_enable, S_IRUGO | S_IWUSR, | ||
787 | show_pwm_enable, set_pwm_enable, 0); | ||
788 | static SENSOR_DEVICE_ATTR(pwm2, S_IRUGO, show_pwm, set_pwm, 1); | ||
789 | static SENSOR_DEVICE_ATTR(pwm2_enable, S_IRUGO | S_IWUSR, | ||
790 | show_pwm_enable, set_pwm_enable, 1); | ||
791 | static SENSOR_DEVICE_ATTR(pwm3, S_IRUGO, show_pwm, set_pwm, 2); | ||
792 | static SENSOR_DEVICE_ATTR(pwm3_enable, S_IRUGO | S_IWUSR, | ||
793 | show_pwm_enable, set_pwm_enable, 2); | ||
794 | |||
675 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); | 795 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); |
676 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); | 796 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); |
677 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); | 797 | static 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 | ||
762 | static struct attribute *f71805f_attributes_fan[3][4] = { | 882 | static 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 */ | ||
914 | static 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); |