All of lore.kernel.org
 help / color / mirror / Atom feed
From: khali@linux-fr.org (Jean Delvare)
To: lm-sensors@vger.kernel.org
Subject: [lm-sensors] [PATCH] Fix the set pwm value will change fan mode
Date: Sun, 28 May 2006 15:03:34 +0000	[thread overview]
Message-ID: <20060528170334.0ebe83a1.khali@linux-fr.org> (raw)
In-Reply-To: <444610F0.1070809@winbond.com.tw>

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


  parent reply	other threads:[~2006-05-28 15:03 UTC|newest]

Thread overview: 8+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2006-04-19 10:29 [lm-sensors] [PATCH] Fix the set pwm value will change fan mode bug Yuan Mu
2006-05-27 19:28 ` [lm-sensors] [PATCH] Fix the set pwm value will change fan mode Rudolf Marek
2006-05-28 15:03 ` Jean Delvare [this message]
2006-05-28 15:55 ` Rudolf Marek
2006-05-30  2:12 ` Yuan Mu
2006-05-30  7:25 ` Jean Delvare
2006-05-30  7:34 ` Yuan Mu
2006-05-30 12:34 ` Jean Delvare

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20060528170334.0ebe83a1.khali@linux-fr.org \
    --to=khali@linux-fr.org \
    --cc=lm-sensors@vger.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.