hwmon: (w83791d) add support for thermal cruise mode

Add support to set target temperature and tolerance for thermal
cruise mode.

Signed-off-by: Marc Hulsman <m.hulsman@tudelft.nl>
Acked-by: Hans de Goede <j.w.r.degoede@hhs.nl>
Signed-off-by: Jean Delvare <khali@linux-fr.org>
This commit is contained in:
Marc Hulsman 2008-10-17 17:51:17 +02:00 committed by Jean Delvare
parent b5938f8c4a
commit a5a4598cd2
2 changed files with 162 additions and 5 deletions

View file

@ -77,6 +77,9 @@ readings can be divided by a programmable divider (1, 2, 4, 8, 16,
Each fan controlled is controlled by PWM. The PWM duty cycle can be read and Each fan controlled is controlled by PWM. The PWM duty cycle can be read and
set for each fan separately. Valid values range from 0 (stop) to 255 (full). set for each fan separately. Valid values range from 0 (stop) to 255 (full).
PWM 1-3 support Thermal Cruise mode, in which the PWMs are automatically
regulated to keep respectively temp 1-3 at a certain target temperature.
See below for the description of the sysfs-interface.
The w83791d has a global bit used to enable beeping from the speaker when an The w83791d has a global bit used to enable beeping from the speaker when an
alarm is triggered as well as a bitmask to enable or disable the beep for alarm is triggered as well as a bitmask to enable or disable the beep for
@ -116,9 +119,19 @@ chip-specific options are documented here.
pwm[1-3]_enable - this file controls mode of fan/temperature control for pwm[1-3]_enable - this file controls mode of fan/temperature control for
fan 1-3. Fan/PWM 4-5 only support manual mode. fan 1-3. Fan/PWM 4-5 only support manual mode.
* 1 Manual mode * 1 Manual mode
* 2 Thermal Cruise mode (no further support) * 2 Thermal Cruise mode
* 3 Fan Speed Cruise mode (no further support) * 3 Fan Speed Cruise mode (no further support)
temp[1-3]_target - defines the target temperature for Thermal Cruise mode.
Unit: millidegree Celsius
RW
temp[1-3]_tolerance - temperature tolerance for Thermal Cruise mode.
Specifies an interval around the target temperature
in which the fan speed is not changed.
Unit: millidegree Celsius
RW
Alarms bitmap vs. beep_mask bitmask Alarms bitmap vs. beep_mask bitmask
------------------------------------ ------------------------------------
For legacy code using the alarms and beep_mask files: For legacy code using the alarms and beep_mask files:
@ -146,7 +159,3 @@ tart2 : alarms: 0x020000 beep_mask: 0x080000 <== mismatch
tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch
case_open : alarms: 0x001000 beep_mask: 0x001000 case_open : alarms: 0x001000 beep_mask: 0x001000
global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable)
W83791D TODO:
---------------
Provide a patch for Thermal Cruise registers.

View file

@ -125,6 +125,17 @@ static const u8 W83791D_REG_PWM[NUMBER_OF_PWM] = {
0xA1, /* PWM 5 duty cycle register in DataSheet */ 0xA1, /* PWM 5 duty cycle register in DataSheet */
}; };
static const u8 W83791D_REG_TEMP_TARGET[3] = {
0x85, /* PWM 1 target temperature for temp 1 */
0x86, /* PWM 2 target temperature for temp 2 */
0x96, /* PWM 3 target temperature for temp 3 */
};
static const u8 W83791D_REG_TEMP_TOL[2] = {
0x87, /* PWM 1/2 temperature tolerance */
0x97, /* PWM 3 temperature tolerance */
};
static const u8 W83791D_REG_FAN_CFG[2] = { static const u8 W83791D_REG_FAN_CFG[2] = {
0x84, /* FAN 1/2 configuration */ 0x84, /* FAN 1/2 configuration */
0x95, /* FAN 3 configuration */ 0x95, /* FAN 3 configuration */
@ -234,6 +245,15 @@ static u8 fan_to_reg(long rpm, int div)
(val) < 0 ? ((val) - 250) / 500 * 128 : \ (val) < 0 ? ((val) - 250) / 500 * 128 : \
((val) + 250) / 500 * 128) ((val) + 250) / 500 * 128)
/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */
#define TARGET_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
(val) >= 127000 ? 127 : \
((val) + 500) / 1000)
/* for thermal cruise temp tolerance, 4-bits, LSB = 1 degree Celsius */
#define TOL_TEMP_TO_REG(val) ((val) < 0 ? 0 : \
(val) >= 15000 ? 15 : \
((val) + 500) / 1000)
#define BEEP_MASK_TO_REG(val) ((val) & 0xffffff) #define BEEP_MASK_TO_REG(val) ((val) & 0xffffff)
#define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff) #define BEEP_MASK_FROM_REG(val) ((val) & 0xffffff)
@ -290,6 +310,9 @@ struct w83791d_data {
u8 pwm_enable[3]; /* pwm enable status for fan 1-3 u8 pwm_enable[3]; /* pwm enable status for fan 1-3
(fan 4-5 only support manual mode) */ (fan 4-5 only support manual mode) */
u8 temp_target[3]; /* pwm 1-3 target temperature */
u8 temp_tolerance[3]; /* pwm 1-3 temperature tolerance */
/* Misc */ /* Misc */
u32 alarms; /* realtime status register encoding,combined */ u32 alarms; /* realtime status register encoding,combined */
u8 beep_enable; /* Global beep enable */ u8 beep_enable; /* Global beep enable */
@ -774,6 +797,110 @@ static struct sensor_device_attribute sda_pwmenable[] = {
show_pwmenable, store_pwmenable, 2), show_pwmenable, store_pwmenable, 2),
}; };
/* For Smart Fan I / Thermal Cruise */
static ssize_t show_temp_target(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct w83791d_data *data = w83791d_update_device(dev);
int nr = sensor_attr->index;
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_target[nr]));
}
static ssize_t store_temp_target(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
u8 target_mask;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
mutex_lock(&data->update_lock);
data->temp_target[nr] = TARGET_TEMP_TO_REG(val);
target_mask = w83791d_read(client,
W83791D_REG_TEMP_TARGET[nr]) & 0x80;
w83791d_write(client, W83791D_REG_TEMP_TARGET[nr],
data->temp_target[nr] | target_mask);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_temp_target[] = {
SENSOR_ATTR(temp1_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 0),
SENSOR_ATTR(temp2_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 1),
SENSOR_ATTR(temp3_target, S_IWUSR | S_IRUGO,
show_temp_target, store_temp_target, 2),
};
static ssize_t show_temp_tolerance(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct w83791d_data *data = w83791d_update_device(dev);
int nr = sensor_attr->index;
return sprintf(buf, "%d\n", TEMP1_FROM_REG(data->temp_tolerance[nr]));
}
static ssize_t store_temp_tolerance(struct device *dev,
struct device_attribute *attr, const char *buf, size_t count)
{
struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
struct i2c_client *client = to_i2c_client(dev);
struct w83791d_data *data = i2c_get_clientdata(client);
int nr = sensor_attr->index;
unsigned long val;
u8 target_mask;
u8 reg_idx = 0;
u8 val_shift = 0;
u8 keep_mask = 0;
if (strict_strtoul(buf, 10, &val))
return -EINVAL;
switch (nr) {
case 0:
reg_idx = 0;
val_shift = 0;
keep_mask = 0xf0;
break;
case 1:
reg_idx = 0;
val_shift = 4;
keep_mask = 0x0f;
break;
case 2:
reg_idx = 1;
val_shift = 0;
keep_mask = 0xf0;
break;
}
mutex_lock(&data->update_lock);
data->temp_tolerance[nr] = TOL_TEMP_TO_REG(val);
target_mask = w83791d_read(client,
W83791D_REG_TEMP_TOL[reg_idx]) & keep_mask;
w83791d_write(client, W83791D_REG_TEMP_TOL[reg_idx],
(data->temp_tolerance[nr] << val_shift) | target_mask);
mutex_unlock(&data->update_lock);
return count;
}
static struct sensor_device_attribute sda_temp_tolerance[] = {
SENSOR_ATTR(temp1_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 0),
SENSOR_ATTR(temp2_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 1),
SENSOR_ATTR(temp3_tolerance, S_IWUSR | S_IRUGO,
show_temp_tolerance, store_temp_tolerance, 2),
};
/* read/write the temperature1, includes measured value and limits */ /* read/write the temperature1, includes measured value and limits */
static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr,
char *buf) char *buf)
@ -1044,6 +1171,12 @@ static struct attribute *w83791d_attributes[] = {
&sda_pwmenable[0].dev_attr.attr, &sda_pwmenable[0].dev_attr.attr,
&sda_pwmenable[1].dev_attr.attr, &sda_pwmenable[1].dev_attr.attr,
&sda_pwmenable[2].dev_attr.attr, &sda_pwmenable[2].dev_attr.attr,
&sda_temp_target[0].dev_attr.attr,
&sda_temp_target[1].dev_attr.attr,
&sda_temp_target[2].dev_attr.attr,
&sda_temp_tolerance[0].dev_attr.attr,
&sda_temp_tolerance[1].dev_attr.attr,
&sda_temp_tolerance[2].dev_attr.attr,
NULL NULL
}; };
@ -1404,6 +1537,21 @@ static struct w83791d_data *w83791d_update_device(struct device *dev)
data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03; data->pwm_enable[1] = (reg_array_tmp[0] >> 4) & 0x03;
data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03; data->pwm_enable[2] = (reg_array_tmp[1] >> 2) & 0x03;
/* Update PWM target temperature */
for (i = 0; i < 3; i++) {
data->temp_target[i] = w83791d_read(client,
W83791D_REG_TEMP_TARGET[i]) & 0x7f;
}
/* Update PWM temperature tolerance */
for (i = 0; i < 2; i++) {
reg_array_tmp[i] = w83791d_read(client,
W83791D_REG_TEMP_TOL[i]);
}
data->temp_tolerance[0] = reg_array_tmp[0] & 0x0f;
data->temp_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f;
data->temp_tolerance[2] = reg_array_tmp[1] & 0x0f;
/* Update the first temperature sensor */ /* Update the first temperature sensor */
for (i = 0; i < 3; i++) { for (i = 0; i < 3; i++) {
data->temp1[i] = w83791d_read(client, data->temp1[i] = w83791d_read(client,