On Wednesday 06 August 2008 10:08:54 Hans de Goede wrote: > Marc Hulsman wrote: > > Last patch in the series. Adds support for automatic PWM control using > > smart fan 1 thermal cruise mode. Fan speed cruise mode shares registers > > with thermal cruise mode which makes implementing it a bit more > > complicated. As it does not add much over manual PWM control I did not > > implement it (for now?). > > > > Marc > > PS: I will be away from my mail during the next two weeks. > > PS2: When is it possible to remove the 'EXPERIMENTAL' status of the > > driver? > > > > --- > > Adds support to set target temperature and tolerance for thermal cruise > > mode. > > > > Signed-off-by: Marc Hulsman <m.hulsman at tudelft.nl> > > Looks good: > Acked-by: Hans de Goede <j.w.r.degoede at hhs.nl> > > (This ofcourse cannot be send upstream until patch 3/4 is fixed) > Refreshed patch which applies clean to fixed patch 3/4. Thanks for the fast review btw. Marc --- Adds support to set target temperature and tolerance for thermal cruise mode. Signed-off-by: Marc Hulsman <m.hulsman at tudelft.nl> --- Documentation/hwmon/w83791d | 19 ++++- drivers/hwmon/w83791d.c | 150 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 164 insertions(+), 5 deletions(-) --- Index: linux-2.6.27-rc1/drivers/hwmon/w83791d.c =================================================================== --- linux-2.6.27-rc1.orig/drivers/hwmon/w83791d.c +++ linux-2.6.27-rc1/drivers/hwmon/w83791d.c @@ -125,6 +125,17 @@ static const u8 W83791D_REG_PWM[NUMBER_O 0xA1, /* PWM 5 duty cycle register in DataSheet */ }; +static const u8 W83791D_REG_PWM_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_PWM_TOL[2] = { + 0x87, /* PWM 1/2 temperature tolerance */ + 0x97, /* PWM 3 temperature tolerance */ +}; + static const u8 W83791D_REG_FAN_CFG[2] = { 0x84, /* FAN 1/2 configuration */ 0x95, /* FAN 3 configuration */ @@ -234,6 +245,17 @@ static u8 fan_to_reg(long rpm, int div) (val) < 0 ? ((val) - 250) / 500 * 128 : \ ((val) + 250) / 500 * 128) +/* for thermal cruise target temp, 7-bits, LSB = 1 degree Celsius */ +#define TARGET_TEMP_FROM_REG(val) ((val) * 1000) +#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_FROM_REG(val) ((val) * 1000) +#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_FROM_REG(val) ((val) & 0xffffff) @@ -290,6 +312,9 @@ struct w83791d_data { u8 pwm_enable[3]; /* pwm enable status for fan 1-3 (fan 4-5 only support manual mode) */ + u8 pwm_target[3]; /* pwm 1-3 target temperature */ + u8 pwm_tolerance[3]; /* pwm 1-3 temperature tolerance */ + /* Misc */ u32 alarms; /* realtime status register encoding,combined */ u8 beep_enable; /* Global beep enable */ @@ -774,6 +799,110 @@ static struct sensor_device_attribute sd show_pwmenable, store_pwmenable, 2), }; +/* For Smart Fan I / Thermal Cruise */ +static ssize_t show_pwm_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", TARGET_TEMP_FROM_REG(data->pwm_target[nr])); +} + +static ssize_t store_pwm_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->pwm_target[nr] = TARGET_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_PWM_TARGET[nr]) & 0x80; + w83791d_write(client, W83791D_REG_PWM_TARGET[nr], + data->pwm_target[nr] | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm_target[] = { + SENSOR_ATTR(pwm1_target, S_IWUSR | S_IRUGO, + show_pwm_target, store_pwm_target, 0), + SENSOR_ATTR(pwm2_target, S_IWUSR | S_IRUGO, + show_pwm_target, store_pwm_target, 1), + SENSOR_ATTR(pwm3_target, S_IWUSR | S_IRUGO, + show_pwm_target, store_pwm_target, 2), +}; + +static ssize_t show_pwm_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", TOL_TEMP_FROM_REG(data->pwm_tolerance[nr])); +} + +static ssize_t store_pwm_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->pwm_tolerance[nr] = TOL_TEMP_TO_REG(val); + target_mask = w83791d_read(client, + W83791D_REG_PWM_TOL[reg_idx]) & keep_mask; + w83791d_write(client, W83791D_REG_PWM_TOL[reg_idx], + (data->pwm_tolerance[nr] << val_shift) | target_mask); + mutex_unlock(&data->update_lock); + return count; +} + +static struct sensor_device_attribute sda_pwm_tolerance[] = { + SENSOR_ATTR(pwm1_tolerance, S_IWUSR | S_IRUGO, + show_pwm_tolerance, store_pwm_tolerance, 0), + SENSOR_ATTR(pwm2_tolerance, S_IWUSR | S_IRUGO, + show_pwm_tolerance, store_pwm_tolerance, 1), + SENSOR_ATTR(pwm3_tolerance, S_IWUSR | S_IRUGO, + show_pwm_tolerance, store_pwm_tolerance, 2), +}; + /* read/write the temperature1, includes measured value and limits */ static ssize_t show_temp1(struct device *dev, struct device_attribute *devattr, char *buf) @@ -1044,6 +1173,12 @@ static struct attribute *w83791d_attribu &sda_pwmenable[0].dev_attr.attr, &sda_pwmenable[1].dev_attr.attr, &sda_pwmenable[2].dev_attr.attr, + &sda_pwm_target[0].dev_attr.attr, + &sda_pwm_target[1].dev_attr.attr, + &sda_pwm_target[2].dev_attr.attr, + &sda_pwm_tolerance[0].dev_attr.attr, + &sda_pwm_tolerance[1].dev_attr.attr, + &sda_pwm_tolerance[2].dev_attr.attr, NULL }; @@ -1403,6 +1538,21 @@ static struct w83791d_data *w83791d_upda data->pwm_enable[1] = ((reg_array_tmp[0] >> 4) & 0x03); data->pwm_enable[2] = ((reg_array_tmp[1] >> 2) & 0x03); + /* Update PWM target temperature */ + for (i = 0; i < 2; i++) { + data->pwm_target[i] = w83791d_read(client, + W83791D_REG_PWM_TARGET[i]) & 0x7f; + } + + /* Update PWM temperature tolerance */ + for (i = 0; i < 1; i++) { + reg_array_tmp[i] = w83791d_read(client, + W83791D_REG_PWM_TOL[i]); + } + data->pwm_tolerance[0] = reg_array_tmp[0] & 0x0f; + data->pwm_tolerance[1] = (reg_array_tmp[0] >> 4) & 0x0f; + data->pwm_tolerance[2] = reg_array_tmp[1] & 0x0f; + /* Update the first temperature sensor */ for (i = 0; i < 3; i++) { data->temp1[i] = w83791d_read(client, Index: linux-2.6.27-rc1/Documentation/hwmon/w83791d =================================================================== --- linux-2.6.27-rc1.orig/Documentation/hwmon/w83791d +++ linux-2.6.27-rc1/Documentation/hwmon/w83791d @@ -77,6 +77,9 @@ readings can be divided by a programmabl 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). +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 alarm is triggered as well as a bitmask to enable or disable the beep for @@ -116,9 +119,19 @@ chip-specific options are documented her pwm[1-3]_enable - this file controls mode of fan/temperature control for fan 1-3. Fan/PWM 4-5 only support manual mode. * 1 Manual mode - * 2 Thermal Cruise mode (no further support) + * 2 Thermal Cruise mode * 3 Fan Speed Cruise mode (no further support) +pwm[1-3]_target - defines the target temperature for Thermal Cruise mode. + Unit: millidegree Celsius + RW + +pwm[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 ------------------------------------ For legacy code using the alarms and beep_mask files: @@ -146,7 +159,3 @@ tart2 : alarms: 0x020000 beep_ma tart3 : alarms: 0x040000 beep_mask: 0x100000 <== mismatch case_open : alarms: 0x001000 beep_mask: 0x001000 global_enable: alarms: -------- beep_mask: 0x800000 (modified via beep_enable) - -W83791D TODO: ---------------- -Provide a patch for Thermal Cruise registers. -------------- next part -------------- A non-text attachment was scrubbed... Name: w83791d_thermal_cruise.patch Type: text/x-diff Size: 8904 bytes Desc: not available Url : http://lists.lm-sensors.org/pipermail/lm-sensors/attachments/20080806/1a127a6a/attachment.bin