diff options
| author | Phil Endecott <kernel@chezphil.org> | 2007-06-29 03:19:14 -0400 |
|---|---|---|
| committer | Mark M. Hoffman <mhoffman@lightlink.com> | 2007-07-19 14:22:15 -0400 |
| commit | aba5073d3f4c928c89c483d85f8cff7cc9aa3312 (patch) | |
| tree | c966ca4de6e14d4edbb7232b3dcef3fb7e114b7f | |
| parent | 158ce07564b68d4215b9560213a089d6f7c5a4ea (diff) | |
hwmon/f71805f: Add temperature-tracking fan control mode
Add support for the "temperature mode" fan speed control. In this mode,
the user can define 3 temperature/speed trip points, and the chip will
set the speed automatically according to the temperature changes.
Signed-off-by: Phil Endecott <kernel@chezphil.org>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
Signed-off-by: Mark M. Hoffman <mhoffman@lightlink.com>
| -rw-r--r-- | Documentation/hwmon/f71805f | 35 | ||||
| -rw-r--r-- | drivers/hwmon/f71805f.c | 167 |
2 files changed, 188 insertions, 14 deletions
diff --git a/Documentation/hwmon/f71805f b/Documentation/hwmon/f71805f index bfd0f154959c..94e0d2cbd3d2 100644 --- a/Documentation/hwmon/f71805f +++ b/Documentation/hwmon/f71805f | |||
| @@ -5,11 +5,11 @@ Supported chips: | |||
| 5 | * Fintek F71805F/FG | 5 | * Fintek F71805F/FG |
| 6 | Prefix: 'f71805f' | 6 | Prefix: 'f71805f' |
| 7 | Addresses scanned: none, address read from Super I/O config space | 7 | Addresses scanned: none, address read from Super I/O config space |
| 8 | Datasheet: Provided by Fintek on request | 8 | Datasheet: Available from the Fintek website |
| 9 | * Fintek F71872F/FG | 9 | * Fintek F71872F/FG |
| 10 | Prefix: 'f71872f' | 10 | Prefix: 'f71872f' |
| 11 | Addresses scanned: none, address read from Super I/O config space | 11 | Addresses scanned: none, address read from Super I/O config space |
| 12 | Datasheet: Provided by Fintek on request | 12 | Datasheet: Available from the Fintek website |
| 13 | 13 | ||
| 14 | Author: Jean Delvare <khali@linux-fr.org> | 14 | Author: Jean Delvare <khali@linux-fr.org> |
| 15 | 15 | ||
| @@ -128,7 +128,9 @@ it. | |||
| 128 | When the PWM method is used, you can select the operating frequency, | 128 | When the PWM method is used, you can select the operating frequency, |
| 129 | from 187.5 kHz (default) to 31 Hz. The best frequency depends on the | 129 | from 187.5 kHz (default) to 31 Hz. The best frequency depends on the |
| 130 | fan model. As a rule of thumb, lower frequencies seem to give better | 130 | fan model. As a rule of thumb, lower frequencies seem to give better |
| 131 | control, but may generate annoying high-pitch noise. Fintek recommends | 131 | control, but may generate annoying high-pitch noise. So a frequency just |
| 132 | above the audible range, such as 25 kHz, may be a good choice; if this | ||
| 133 | doesn't give you good linear control, try reducing it. Fintek recommends | ||
| 132 | not going below 1 kHz, as the fan tachometers get confused by lower | 134 | not going below 1 kHz, as the fan tachometers get confused by lower |
| 133 | frequencies as well. | 135 | frequencies as well. |
| 134 | 136 | ||
| @@ -136,16 +138,23 @@ When the DC method is used, Fintek recommends not going below 5 V, which | |||
| 136 | corresponds to a pwm value of 106 for the driver. The driver doesn't | 138 | corresponds to a pwm value of 106 for the driver. The driver doesn't |
| 137 | enforce this limit though. | 139 | enforce this limit though. |
| 138 | 140 | ||
| 139 | Three different fan control modes are supported: | 141 | Three different fan control modes are supported; the mode number is written |
| 142 | to the pwm<n>_enable file. | ||
| 140 | 143 | ||
| 141 | * Manual mode | 144 | * 1: Manual mode |
| 142 | You ask for a specific PWM duty cycle or DC voltage. | 145 | You ask for a specific PWM duty cycle or DC voltage by writing to the |
| 146 | pwm<n> file. | ||
| 143 | 147 | ||
| 144 | * Fan speed mode | 148 | * 2: Temperature mode |
| 145 | You ask for a specific fan speed. This mode assumes that pwm1 | 149 | You define 3 temperature/fan speed trip points using the |
| 146 | corresponds to fan1, pwm2 to fan2 and pwm3 to fan3. | 150 | pwm<n>_auto_point<m>_temp and _fan files. These define a staircase |
| 151 | relationship between temperature and fan speed with two additional points | ||
| 152 | interpolated between the values that you define. When the temperature | ||
| 153 | is below auto_point1_temp the fan is switched off. | ||
| 147 | 154 | ||
| 148 | * Temperature mode | 155 | * 3: Fan speed mode |
| 149 | You define 3 temperature/fan speed trip points, and the fan speed is | 156 | You ask for a specific fan speed by writing to the fan<n>_target file. |
| 150 | adjusted depending on the measured temperature, using interpolation. | 157 | |
| 151 | This mode is not yet supported by the driver. | 158 | Both of the automatic modes require that pwm1 corresponds to fan1, pwm2 to |
| 159 | fan2 and pwm3 to fan3. Temperature mode also requires that temp1 corresponds | ||
| 160 | to pwm1 and fan1, etc. | ||
diff --git a/drivers/hwmon/f71805f.c b/drivers/hwmon/f71805f.c index 8fe4d70d8f51..6f60715f34f8 100644 --- a/drivers/hwmon/f71805f.c +++ b/drivers/hwmon/f71805f.c | |||
| @@ -127,6 +127,13 @@ superio_exit(int base) | |||
| 127 | #define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) | 127 | #define F71805F_REG_TEMP_HIGH(nr) (0x54 + 2 * (nr)) |
| 128 | #define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr)) | 128 | #define F71805F_REG_TEMP_HYST(nr) (0x55 + 2 * (nr)) |
| 129 | #define F71805F_REG_TEMP_MODE 0x01 | 129 | #define F71805F_REG_TEMP_MODE 0x01 |
| 130 | /* pwm/fan pwmnr from 0 to 2, auto point apnr from 0 to 2 */ | ||
| 131 | /* map Fintek numbers to our numbers as follows: 9->0, 5->1, 1->2 */ | ||
| 132 | #define F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr) \ | ||
| 133 | (0xA0 + 0x10 * (pwmnr) + (2 - (apnr))) | ||
| 134 | #define F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr) \ | ||
| 135 | (0xA4 + 0x10 * (pwmnr) + \ | ||
| 136 | 2 * (2 - (apnr))) | ||
| 130 | 137 | ||
| 131 | #define F71805F_REG_START 0x00 | 138 | #define F71805F_REG_START 0x00 |
| 132 | /* status nr from 0 to 2 */ | 139 | /* status nr from 0 to 2 */ |
| @@ -144,6 +151,11 @@ superio_exit(int base) | |||
| 144 | * Data structures and manipulation thereof | 151 | * Data structures and manipulation thereof |
| 145 | */ | 152 | */ |
| 146 | 153 | ||
| 154 | struct f71805f_auto_point { | ||
| 155 | u8 temp[3]; | ||
| 156 | u16 fan[3]; | ||
| 157 | }; | ||
| 158 | |||
| 147 | struct f71805f_data { | 159 | struct f71805f_data { |
| 148 | unsigned short addr; | 160 | unsigned short addr; |
| 149 | const char *name; | 161 | const char *name; |
| @@ -170,6 +182,7 @@ struct f71805f_data { | |||
| 170 | u8 temp_hyst[3]; | 182 | u8 temp_hyst[3]; |
| 171 | u8 temp_mode; | 183 | u8 temp_mode; |
| 172 | unsigned long alarms; | 184 | unsigned long alarms; |
| 185 | struct f71805f_auto_point auto_points[3]; | ||
| 173 | }; | 186 | }; |
| 174 | 187 | ||
| 175 | struct f71805f_sio_data { | 188 | struct f71805f_sio_data { |
| @@ -312,7 +325,7 @@ static void f71805f_write16(struct f71805f_data *data, u8 reg, u16 val) | |||
| 312 | static struct f71805f_data *f71805f_update_device(struct device *dev) | 325 | static struct f71805f_data *f71805f_update_device(struct device *dev) |
| 313 | { | 326 | { |
| 314 | struct f71805f_data *data = dev_get_drvdata(dev); | 327 | struct f71805f_data *data = dev_get_drvdata(dev); |
| 315 | int nr; | 328 | int nr, apnr; |
| 316 | 329 | ||
| 317 | mutex_lock(&data->update_lock); | 330 | mutex_lock(&data->update_lock); |
| 318 | 331 | ||
| @@ -342,6 +355,18 @@ static struct f71805f_data *f71805f_update_device(struct device *dev) | |||
| 342 | F71805F_REG_TEMP_HYST(nr)); | 355 | F71805F_REG_TEMP_HYST(nr)); |
| 343 | } | 356 | } |
| 344 | data->temp_mode = f71805f_read8(data, F71805F_REG_TEMP_MODE); | 357 | data->temp_mode = f71805f_read8(data, F71805F_REG_TEMP_MODE); |
| 358 | for (nr = 0; nr < 3; nr++) { | ||
| 359 | for (apnr = 0; apnr < 3; apnr++) { | ||
| 360 | data->auto_points[nr].temp[apnr] = | ||
| 361 | f71805f_read8(data, | ||
| 362 | F71805F_REG_PWM_AUTO_POINT_TEMP(nr, | ||
| 363 | apnr)); | ||
| 364 | data->auto_points[nr].fan[apnr] = | ||
| 365 | f71805f_read16(data, | ||
| 366 | F71805F_REG_PWM_AUTO_POINT_FAN(nr, | ||
| 367 | apnr)); | ||
| 368 | } | ||
| 369 | } | ||
| 345 | 370 | ||
| 346 | data->last_limits = jiffies; | 371 | data->last_limits = jiffies; |
| 347 | } | 372 | } |
| @@ -705,6 +730,70 @@ static ssize_t set_pwm_freq(struct device *dev, struct device_attribute | |||
| 705 | return count; | 730 | return count; |
| 706 | } | 731 | } |
| 707 | 732 | ||
| 733 | static ssize_t show_pwm_auto_point_temp(struct device *dev, | ||
| 734 | struct device_attribute *devattr, | ||
| 735 | char* buf) | ||
| 736 | { | ||
| 737 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
| 738 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | ||
| 739 | int pwmnr = attr->nr; | ||
| 740 | int apnr = attr->index; | ||
| 741 | |||
| 742 | return sprintf(buf, "%ld\n", | ||
| 743 | temp_from_reg(data->auto_points[pwmnr].temp[apnr])); | ||
| 744 | } | ||
| 745 | |||
| 746 | static ssize_t set_pwm_auto_point_temp(struct device *dev, | ||
| 747 | struct device_attribute *devattr, | ||
| 748 | const char* buf, size_t count) | ||
| 749 | { | ||
| 750 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
| 751 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | ||
| 752 | int pwmnr = attr->nr; | ||
| 753 | int apnr = attr->index; | ||
| 754 | unsigned long val = simple_strtol(buf, NULL, 10); | ||
| 755 | |||
| 756 | mutex_lock(&data->update_lock); | ||
| 757 | data->auto_points[pwmnr].temp[apnr] = temp_to_reg(val); | ||
| 758 | f71805f_write8(data, F71805F_REG_PWM_AUTO_POINT_TEMP(pwmnr, apnr), | ||
| 759 | data->auto_points[pwmnr].temp[apnr]); | ||
| 760 | mutex_unlock(&data->update_lock); | ||
| 761 | |||
| 762 | return count; | ||
| 763 | } | ||
| 764 | |||
| 765 | static ssize_t show_pwm_auto_point_fan(struct device *dev, | ||
| 766 | struct device_attribute *devattr, | ||
| 767 | char* buf) | ||
| 768 | { | ||
| 769 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
| 770 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | ||
| 771 | int pwmnr = attr->nr; | ||
| 772 | int apnr = attr->index; | ||
| 773 | |||
| 774 | return sprintf(buf, "%ld\n", | ||
| 775 | fan_from_reg(data->auto_points[pwmnr].fan[apnr])); | ||
| 776 | } | ||
| 777 | |||
| 778 | static ssize_t set_pwm_auto_point_fan(struct device *dev, | ||
| 779 | struct device_attribute *devattr, | ||
| 780 | const char* buf, size_t count) | ||
| 781 | { | ||
| 782 | struct f71805f_data *data = dev_get_drvdata(dev); | ||
| 783 | struct sensor_device_attribute_2 *attr = to_sensor_dev_attr_2(devattr); | ||
| 784 | int pwmnr = attr->nr; | ||
| 785 | int apnr = attr->index; | ||
| 786 | unsigned long val = simple_strtoul(buf, NULL, 10); | ||
| 787 | |||
| 788 | mutex_lock(&data->update_lock); | ||
| 789 | data->auto_points[pwmnr].fan[apnr] = fan_to_reg(val); | ||
| 790 | f71805f_write16(data, F71805F_REG_PWM_AUTO_POINT_FAN(pwmnr, apnr), | ||
| 791 | data->auto_points[pwmnr].fan[apnr]); | ||
| 792 | mutex_unlock(&data->update_lock); | ||
| 793 | |||
| 794 | return count; | ||
| 795 | } | ||
| 796 | |||
| 708 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, | 797 | static ssize_t show_temp(struct device *dev, struct device_attribute *devattr, |
| 709 | char *buf) | 798 | char *buf) |
| 710 | { | 799 | { |
| @@ -932,6 +1021,63 @@ static SENSOR_DEVICE_ATTR(pwm3_freq, S_IRUGO | S_IWUSR, | |||
| 932 | show_pwm_freq, set_pwm_freq, 2); | 1021 | show_pwm_freq, set_pwm_freq, 2); |
| 933 | static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2); | 1022 | static SENSOR_DEVICE_ATTR(pwm3_mode, S_IRUGO, show_pwm_mode, NULL, 2); |
| 934 | 1023 | ||
| 1024 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_temp, S_IRUGO | S_IWUSR, | ||
| 1025 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1026 | 0, 0); | ||
| 1027 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point1_fan, S_IRUGO | S_IWUSR, | ||
| 1028 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1029 | 0, 0); | ||
| 1030 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_temp, S_IRUGO | S_IWUSR, | ||
| 1031 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1032 | 0, 1); | ||
| 1033 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point2_fan, S_IRUGO | S_IWUSR, | ||
| 1034 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1035 | 0, 1); | ||
| 1036 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_temp, S_IRUGO | S_IWUSR, | ||
| 1037 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1038 | 0, 2); | ||
| 1039 | static SENSOR_DEVICE_ATTR_2(pwm1_auto_point3_fan, S_IRUGO | S_IWUSR, | ||
| 1040 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1041 | 0, 2); | ||
| 1042 | |||
| 1043 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_temp, S_IRUGO | S_IWUSR, | ||
| 1044 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1045 | 1, 0); | ||
| 1046 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point1_fan, S_IRUGO | S_IWUSR, | ||
| 1047 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1048 | 1, 0); | ||
| 1049 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_temp, S_IRUGO | S_IWUSR, | ||
| 1050 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1051 | 1, 1); | ||
| 1052 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point2_fan, S_IRUGO | S_IWUSR, | ||
| 1053 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1054 | 1, 1); | ||
| 1055 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_temp, S_IRUGO | S_IWUSR, | ||
| 1056 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1057 | 1, 2); | ||
| 1058 | static SENSOR_DEVICE_ATTR_2(pwm2_auto_point3_fan, S_IRUGO | S_IWUSR, | ||
| 1059 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1060 | 1, 2); | ||
| 1061 | |||
| 1062 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_temp, S_IRUGO | S_IWUSR, | ||
| 1063 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1064 | 2, 0); | ||
| 1065 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point1_fan, S_IRUGO | S_IWUSR, | ||
| 1066 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1067 | 2, 0); | ||
| 1068 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_temp, S_IRUGO | S_IWUSR, | ||
| 1069 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1070 | 2, 1); | ||
| 1071 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point2_fan, S_IRUGO | S_IWUSR, | ||
| 1072 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1073 | 2, 1); | ||
| 1074 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_temp, S_IRUGO | S_IWUSR, | ||
| 1075 | show_pwm_auto_point_temp, set_pwm_auto_point_temp, | ||
| 1076 | 2, 2); | ||
| 1077 | static SENSOR_DEVICE_ATTR_2(pwm3_auto_point3_fan, S_IRUGO | S_IWUSR, | ||
| 1078 | show_pwm_auto_point_fan, set_pwm_auto_point_fan, | ||
| 1079 | 2, 2); | ||
| 1080 | |||
| 935 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); | 1081 | static SENSOR_DEVICE_ATTR(in0_alarm, S_IRUGO, show_alarm, NULL, 0); |
| 936 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); | 1082 | static SENSOR_DEVICE_ATTR(in1_alarm, S_IRUGO, show_alarm, NULL, 1); |
| 937 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); | 1083 | static SENSOR_DEVICE_ATTR(in2_alarm, S_IRUGO, show_alarm, NULL, 2); |
| @@ -1014,6 +1160,25 @@ static struct attribute *f71805f_attributes[] = { | |||
| 1014 | &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, | 1160 | &sensor_dev_attr_temp3_max_hyst.dev_attr.attr, |
| 1015 | &sensor_dev_attr_temp3_type.dev_attr.attr, | 1161 | &sensor_dev_attr_temp3_type.dev_attr.attr, |
| 1016 | 1162 | ||
| 1163 | &sensor_dev_attr_pwm1_auto_point1_temp.dev_attr.attr, | ||
| 1164 | &sensor_dev_attr_pwm1_auto_point1_fan.dev_attr.attr, | ||
| 1165 | &sensor_dev_attr_pwm1_auto_point2_temp.dev_attr.attr, | ||
| 1166 | &sensor_dev_attr_pwm1_auto_point2_fan.dev_attr.attr, | ||
| 1167 | &sensor_dev_attr_pwm1_auto_point3_temp.dev_attr.attr, | ||
| 1168 | &sensor_dev_attr_pwm1_auto_point3_fan.dev_attr.attr, | ||
| 1169 | &sensor_dev_attr_pwm2_auto_point1_temp.dev_attr.attr, | ||
| 1170 | &sensor_dev_attr_pwm2_auto_point1_fan.dev_attr.attr, | ||
| 1171 | &sensor_dev_attr_pwm2_auto_point2_temp.dev_attr.attr, | ||
| 1172 | &sensor_dev_attr_pwm2_auto_point2_fan.dev_attr.attr, | ||
| 1173 | &sensor_dev_attr_pwm2_auto_point3_temp.dev_attr.attr, | ||
| 1174 | &sensor_dev_attr_pwm2_auto_point3_fan.dev_attr.attr, | ||
| 1175 | &sensor_dev_attr_pwm3_auto_point1_temp.dev_attr.attr, | ||
| 1176 | &sensor_dev_attr_pwm3_auto_point1_fan.dev_attr.attr, | ||
| 1177 | &sensor_dev_attr_pwm3_auto_point2_temp.dev_attr.attr, | ||
| 1178 | &sensor_dev_attr_pwm3_auto_point2_fan.dev_attr.attr, | ||
| 1179 | &sensor_dev_attr_pwm3_auto_point3_temp.dev_attr.attr, | ||
| 1180 | &sensor_dev_attr_pwm3_auto_point3_fan.dev_attr.attr, | ||
| 1181 | |||
| 1017 | &sensor_dev_attr_in0_alarm.dev_attr.attr, | 1182 | &sensor_dev_attr_in0_alarm.dev_attr.attr, |
| 1018 | &sensor_dev_attr_in1_alarm.dev_attr.attr, | 1183 | &sensor_dev_attr_in1_alarm.dev_attr.attr, |
| 1019 | &sensor_dev_attr_in2_alarm.dev_attr.attr, | 1184 | &sensor_dev_attr_in2_alarm.dev_attr.attr, |
