[lm-sensors] [PATCH] Fix the set pwm value will change fan mode bug in w83792d driver
Jean Delvare
khali at linux-fr.org
Sun May 28 17:03:34 CEST 2006
Hi Yuan,
> W83792D use pwm register low 4 bits to store fan PWM/DC
> value, bit 7 is used to store fan PWM/DC mode. The store_pwm
> function use a wrong limit 0-255, so it may change the fan
> mode when set value is large than 127.
Good catch, thanks for working on this (and sorry for the late answer.)
> This fix the problem. Change the "index" value of
> pwm*_mode and pwm* SENSOR_ATTR to simplify code.
That's not totally correct, unfortunately.
> --- linux-2.6.17-rc2.orig/drivers/hwmon/w83792d.c 2006-04-19 18:04:04.000000000 +0800
> +++ linux-2.6.17-rc2/drivers/hwmon/w83792d.c 2006-04-19 18:07:05.000000000 +0800
> @@ -250,8 +250,6 @@ FAN_TO_REG(long rpm, int div)
> : (val)) / 1000, 0, 0xff))
> #define TEMP_ADD_TO_REG_LOW(val) ((val%1000) ? 0x80 : 0x00)
>
> -#define PWM_FROM_REG(val) (val)
> -#define PWM_TO_REG(val) (SENSORS_LIMIT((val),0,255))
> #define DIV_FROM_REG(val) (1 << (val))
>
> static inline u8
> @@ -288,10 +286,8 @@ struct w83792d_data {
> u8 temp1[3]; /* current, over, thyst */
> u8 temp_add[2][6]; /* Register value */
> u8 fan_div[7]; /* Register encoding, shifted right */
> - u8 pwm[7]; /* We only consider the first 3 set of pwm,
> - although 792 chip has 7 set of pwm. */
> + u8 pwm[7]; /* Register value */
The comment still seems valid to me, pwm4-7 aren't handled by the
driver, right?
> u8 pwmenable[3];
> - u8 pwm_mode[7]; /* indicates PWM or DC mode: 1->PWM; 0->DC */
> u32 alarms; /* realtime status register encoding,combined */
> u8 chassis; /* Chassis status */
> u8 chassis_clear; /* CLR_CHS, clear chassis intrusion detection */
> @@ -627,7 +623,8 @@ show_pwm(struct device *dev, struct devi
> struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> int nr = sensor_attr->index;
> struct w83792d_data *data = w83792d_update_device(dev);
> - return sprintf(buf, "%ld\n", (long) PWM_FROM_REG(data->pwm[nr-1]));
> +
> + return sprintf(buf, "%d\n", data->pwm[nr] & 0x0f);
> }
No, Documentation/hwmon/sysfs-interface says that the PWM values must
range from 0 (fan stopped) to 255 (fan at full speed). Here you range
from 0 to 15 only. You must scale the result so that it fits the
standard range.
>
> static ssize_t
> @@ -659,14 +656,16 @@ store_pwm(struct device *dev, struct dev
> const char *buf, size_t count)
> {
> struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> - int nr = sensor_attr->index - 1;
> + int nr = sensor_attr->index;
> struct i2c_client *client = to_i2c_client(dev);
> struct w83792d_data *data = i2c_get_clientdata(client);
> - u32 val;
> + u8 val = data->pwm[nr] & 0xf0;
>
> - val = simple_strtoul(buf, NULL, 10);
> - data->pwm[nr] = PWM_TO_REG(val);
> - w83792d_write_value(client, W83792D_REG_PWM[nr], data->pwm[nr]);
> + val |= SENSORS_LIMIT(simple_strtoul(buf, NULL, 10), 0, 0x0f);
> + if (val != data->pwm[nr]) {
> + data->pwm[nr] = val;
> + w83792d_write_value(client, W83792D_REG_PWM[nr], val);
> + }
>
> return count;
> }
The write shouldn't be conditional. You base your condition on a cached
value, which can be very old if the user didn't read any register
lately. And you should be holding the lock here, else data->pwm[nr] may
change in your back.
Please also keep in mind that the allowed input range IS 0 to 255, as
per interface specification. It is the driver's job to scale it to what
the chip expects.
> @@ -707,9 +706,9 @@ store_pwmenable(struct device *dev, stru
> }
>
> static struct sensor_device_attribute sda_pwm[] = {
> - SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
> - SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
> - SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 3),
> + SENSOR_ATTR(pwm1, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 0),
> + SENSOR_ATTR(pwm2, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 1),
> + SENSOR_ATTR(pwm3, S_IWUSR | S_IRUGO, show_pwm, store_pwm, 2),
> };
> static struct sensor_device_attribute sda_pwm_enable[] = {
> SENSOR_ATTR(pwm1_enable, S_IWUSR | S_IRUGO,
> @@ -728,7 +727,7 @@ show_pwm_mode(struct device *dev, struct
> struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> int nr = sensor_attr->index;
> struct w83792d_data *data = w83792d_update_device(dev);
> - return sprintf(buf, "%d\n", data->pwm_mode[nr-1]);
> + return sprintf(buf, "%d\n", data->pwm[nr] >> 7);
> }
>
> static ssize_t
> @@ -736,29 +735,28 @@ store_pwm_mode(struct device *dev, struc
> const char *buf, size_t count)
> {
> struct sensor_device_attribute *sensor_attr = to_sensor_dev_attr(attr);
> - int nr = sensor_attr->index - 1;
> + int nr = sensor_attr->index;
> struct i2c_client *client = to_i2c_client(dev);
> struct w83792d_data *data = i2c_get_clientdata(client);
> - u32 val;
> - u8 pwm_mode_mask = 0;
> + u8 val = data->pwm[nr] & 0x7f;
>
> - val = simple_strtoul(buf, NULL, 10);
> - data->pwm_mode[nr] = SENSORS_LIMIT(val, 0, 1);
> - pwm_mode_mask = w83792d_read_value(client,
> - W83792D_REG_PWM[nr]) & 0x7f;
> - w83792d_write_value(client, W83792D_REG_PWM[nr],
> - ((data->pwm_mode[nr]) << 7) | pwm_mode_mask);
> + val |= SENSORS_LIMIT(simple_strtol(buf, NULL, 10), 0, 1) ? 0x80 : 0;
> +
> + if (val != data->pwm[nr]) {
> + data->pwm[nr] = val;
> + w83792d_write_value(client, W83792D_REG_PWM[nr], val);
> + }
>
> return count;
> }
Same here, write should be unconditional, and you should be holding the
lock. Also, you don't want to use SENSORS_LIMIT here. Instead, reject
invalid values (return -EINVAL).
>
> static struct sensor_device_attribute sda_pwm_mode[] = {
> SENSOR_ATTR(pwm1_mode, S_IWUSR | S_IRUGO,
> - show_pwm_mode, store_pwm_mode, 1),
> + show_pwm_mode, store_pwm_mode, 0),
> SENSOR_ATTR(pwm2_mode, S_IWUSR | S_IRUGO,
> - show_pwm_mode, store_pwm_mode, 2),
> + show_pwm_mode, store_pwm_mode, 1),
> SENSOR_ATTR(pwm3_mode, S_IWUSR | S_IRUGO,
> - show_pwm_mode, store_pwm_mode, 3),
> + show_pwm_mode, store_pwm_mode, 2),
> };
>
>
> @@ -1373,7 +1371,7 @@ static struct w83792d_data *w83792d_upda
> struct i2c_client *client = to_i2c_client(dev);
> struct w83792d_data *data = i2c_get_clientdata(client);
> int i, j;
> - u8 reg_array_tmp[4], pwm_array_tmp[7], reg_tmp;
> + u8 reg_array_tmp[4], reg_tmp;
>
> mutex_lock(&data->update_lock);
>
> @@ -1402,10 +1400,8 @@ static struct w83792d_data *w83792d_upda
> data->fan_min[i] = w83792d_read_value(client,
> W83792D_REG_FAN_MIN[i]);
> /* Update the PWM/DC Value and PWM/DC flag */
> - pwm_array_tmp[i] = w83792d_read_value(client,
> + data->pwm[i] = w83792d_read_value(client,
> W83792D_REG_PWM[i]);
> - data->pwm[i] = pwm_array_tmp[i] & 0x0f;
> - data->pwm_mode[i] = pwm_array_tmp[i] >> 7;
> }
>
> reg_tmp = w83792d_read_value(client, W83792D_REG_FAN_CFG);
> @@ -1513,7 +1509,6 @@ static void w83792d_print_debug(struct w
> dev_dbg(dev, "fan[%d] is: 0x%x\n", i, data->fan[i]);
> dev_dbg(dev, "fan[%d] min is: 0x%x\n", i, data->fan_min[i]);
> dev_dbg(dev, "pwm[%d] is: 0x%x\n", i, data->pwm[i]);
> - dev_dbg(dev, "pwm_mode[%d] is: 0x%x\n", i, data->pwm_mode[i]);
> }
> dev_dbg(dev, "3 set of Temperatures: =====>\n");
> for (i=0; i<3; i++) {
I'm fine with all the other changes. Care to update this patch, test it
and submit it again?
Thanks,
--
Jean Delvare
More information about the lm-sensors
mailing list