Re: [PATCH 3/4] hwmon: (ina2xx) Use structure array to distinguish chip types

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Guenter,

On Mon, 10 Sep 2012 21:09:25 -0700, Guenter Roeck wrote:
> Replace per-device initialization and per-device calculation code with
> per-device configuration data, which is then used to configure the chip and
> perform calculations based on that data.
> 
> Also use i2c_smbus_write_read_swapped i2c_smbus_write_word_swapped to read and
> write word swapped I2C data.
> 
> This patch reduces code size by more than 400 bytes on x86_64.
> 
> Cc: Lothar Felten <l-felten@xxxxxx>
> Signed-off-by: Guenter Roeck <linux@xxxxxxxxxxxx>
> ---
>  drivers/hwmon/ina2xx.c |  168 ++++++++++++++++--------------------------------
>  1 file changed, 55 insertions(+), 113 deletions(-)
> 
> diff --git a/drivers/hwmon/ina2xx.c b/drivers/hwmon/ina2xx.c
> index 7f3f4a3..eb42e8b 100644
> --- a/drivers/hwmon/ina2xx.c
> +++ b/drivers/hwmon/ina2xx.c
> @@ -57,8 +57,19 @@
>  
>  enum ina2xx_ids { ina219, ina226 };
>  
> +struct ina2xx_config {
> +	u16 config;

config_default would be a better name IMHO.

> +	int calibration_factor;
> +	int registers;
> +	int shunt_div;
> +	int bus_voltage_shift;
> +	int bus_voltage_lsb;	/* uV */
> +	int power_lsb;		/* uW */
> +};
> +
>  struct ina2xx_data {
>  	struct device *hwmon_dev;
> +	struct ina2xx_config *config;

This is data shared between clients, so it should be a const pointer.

>  
>  	struct mutex update_lock;
>  	bool valid;
> @@ -69,21 +80,26 @@ struct ina2xx_data {
>  	u16 regs[INA2XX_MAX_REGISTERS];
>  };
>  
> -int ina2xx_read_word(struct i2c_client *client, int reg)
> -{
> -	int val = i2c_smbus_read_word_data(client, reg);
> -	if (unlikely(val < 0)) {
> -		dev_dbg(&client->dev,
> -			"Failed to read register: %d\n", reg);
> -		return val;
> -	}
> -	return be16_to_cpu(val);
> -}
> -
> -void ina2xx_write_word(struct i2c_client *client, int reg, int data)
> -{
> -	i2c_smbus_write_word_data(client, reg, cpu_to_be16(data));
> -}

This change doesn't belong to that patch.

Also note that these calls to be16_to_cpu() were suspicious.
i2c_smbus_read_word_swapped() uses swab16(), not *16_to_cpu(). The need
to swap depends on the slave I2C device, _not_ the host endianness. So
this was probably a bug in the driver originally, and calling
i2c_smbus_read_word_swapped() will fix it. This certainly deserves a
separate patch, as it may have to be backported.

> +static struct ina2xx_config ina2xx_config[] = {
> +	[ina219] = {
> +		.config = INA219_CONFIG_DEFAULT,
> +		.calibration_factor = 40960000,
> +		.registers = INA219_REGISTERS,
> +		.shunt_div = 100,
> +		.bus_voltage_shift = 3,
> +		.bus_voltage_lsb = 4000,
> +		.power_lsb = 20000,
> +	},
> +	[ina226] = {
> +		.config = INA226_CONFIG_DEFAULT,
> +		.calibration_factor = 5120000,
> +		.registers = INA226_REGISTERS,
> +		.shunt_div = 400,
> +		.bus_voltage_shift = 0,
> +		.bus_voltage_lsb = 1250,
> +		.power_lsb = 25000,
> +	},
> +};

Nobody should change these, so make them const.

>  
>  static struct ina2xx_data *ina2xx_update_device(struct device *dev)
>  {
> @@ -102,7 +118,7 @@ static struct ina2xx_data *ina2xx_update_device(struct device *dev)
>  
>  		/* Read all registers */
>  		for (i = 0; i < data->registers; i++) {
> -			int rv = ina2xx_read_word(client, i);
> +			int rv = i2c_smbus_read_word_swapped(client, i);
>  			if (rv < 0) {
>  				ret = ERR_PTR(rv);
>  				goto abort;
> @@ -117,73 +133,25 @@ abort:
>  	return ret;
>  }
>  
> -static int ina219_get_value(struct ina2xx_data *data, u8 reg)
> -{
> -	/*
> -	 * calculate exact value for the given register
> -	 * we assume default power-on reset settings:
> -	 * bus voltage range 32V
> -	 * gain = /8
> -	 * adc 1 & 2 -> conversion time 532uS
> -	 * mode is continuous shunt and bus
> -	 * calibration value is INA219_CALIBRATION_VALUE
> -	 */
> -	int val = data->regs[reg];
> -
> -	switch (reg) {
> -	case INA2XX_SHUNT_VOLTAGE:
> -		/* LSB=10uV. Convert to mV. */
> -		val = DIV_ROUND_CLOSEST(val, 100);
> -		break;
> -	case INA2XX_BUS_VOLTAGE:
> -		/* LSB=4mV. Register is not right aligned, convert to mV. */
> -		val = (val >> 3) * 4;
> -		break;
> -	case INA2XX_POWER:
> -		/* LSB=20mW. Convert to uW */
> -		val = val * 20 * 1000;
> -		break;
> -	case INA2XX_CURRENT:
> -		/* LSB=1mA (selected). Is in mA */
> -		break;
> -	default:
> -		/* programmer goofed */
> -		WARN_ON_ONCE(1);
> -		val = 0;
> -		break;
> -	}
> -
> -	return val;
> -}
> -
> -static int ina226_get_value(struct ina2xx_data *data, u8 reg)
> +static int ina2xx_get_value(struct ina2xx_data *data, u8 reg)
>  {
> -	/*
> -	 * calculate exact value for the given register
> -	 * we assume default power-on reset settings:
> -	 * bus voltage range 32V
> -	 * gain = /8
> -	 * adc 1 & 2 -> conversion time 532uS
> -	 * mode is continuous shunt and bus
> -	 * calibration value is INA226_CALIBRATION_VALUE
> -	 */
> -	int val = data->regs[reg];
> +	int val;
>  
>  	switch (reg) {
>  	case INA2XX_SHUNT_VOLTAGE:
> -		/* LSB=2.5uV. Convert to mV. */
> -		val = DIV_ROUND_CLOSEST(val, 400);
> +		val = DIV_ROUND_CLOSEST(data->regs[reg],
> +					data->config->shunt_div);
>  		break;
>  	case INA2XX_BUS_VOLTAGE:
> -		/* LSB=1.25mV. Convert to mV. */
> -		val = val + DIV_ROUND_CLOSEST(val, 4);
> +		val = (data->regs[reg] >> data->config->bus_voltage_shift)
> +		  * data->config->bus_voltage_lsb / 1000;

You're dropping DIV_ROUND_CLOSEST(), don't you think this could result
in improper rounding for the INA226? For example for val = 3, old code
returned 4, new code returns 3.

>  		break;
>  	case INA2XX_POWER:
> -		/* LSB=25mW. Convert to uW */
> -		val = val * 25 * 1000;
> +		val = data->regs[reg] * data->config->power_lsb;
>  		break;
>  	case INA2XX_CURRENT:
>  		/* LSB=1mA (selected). Is in mA */
> +		val = data->regs[reg];
>  		break;
>  	default:
>  		/* programmer goofed */
> @@ -200,23 +168,12 @@ static ssize_t ina2xx_show_value(struct device *dev,
>  {
>  	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
>  	struct ina2xx_data *data = ina2xx_update_device(dev);
> -	int value = 0;
>  
>  	if (IS_ERR(data))
>  		return PTR_ERR(data);
>  
> -	switch (data->kind) {
> -	case ina219:
> -		value = ina219_get_value(data, attr->index);
> -		break;
> -	case ina226:
> -		value = ina226_get_value(data, attr->index);
> -		break;
> -	default:
> -		WARN_ON_ONCE(1);
> -		break;
> -	}
> -	return snprintf(buf, PAGE_SIZE, "%d\n", value);
> +	return snprintf(buf, PAGE_SIZE, "%d\n",
> +			ina2xx_get_value(data, attr->index));
>  }
>  
>  /* shunt voltage */
> @@ -254,7 +211,7 @@ static int ina2xx_probe(struct i2c_client *client,
>  	struct i2c_adapter *adapter = client->adapter;
>  	struct ina2xx_data *data;
>  	struct ina2xx_platform_data *pdata;
> -	int ret = 0;
> +	int ret;
>  	long shunt = 10000; /* default shunt value 10mOhms */
>  
>  	if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_WORD_DATA))
> @@ -275,34 +232,16 @@ static int ina2xx_probe(struct i2c_client *client,
>  
>  	/* set the device type */
>  	data->kind = id->driver_data;
> +	data->config = &ina2xx_config[data->kind];
>  
> -	switch (data->kind) {
> -	case ina219:
> -		/* device configuration */
> -		ina2xx_write_word(client, INA2XX_CONFIG, INA219_CONFIG_DEFAULT);
> -
> -		/* set current LSB to 1mA, shunt is in uOhms */
> -		/* (equation 13 in datasheet) */
> -		ina2xx_write_word(client, INA2XX_CALIBRATION, 40960000 / shunt);
> -		dev_info(&client->dev,
> -			 "power monitor INA219 (Rshunt = %li uOhm)\n", shunt);
> -		data->registers = INA219_REGISTERS;
> -		break;
> -	case ina226:
> -		/* device configuration */
> -		ina2xx_write_word(client, INA2XX_CONFIG, INA226_CONFIG_DEFAULT);
> -
> -		/* set current LSB to 1mA, shunt is in uOhms */
> -		/* (equation 1 in datasheet)*/
> -		ina2xx_write_word(client, INA2XX_CALIBRATION, 5120000 / shunt);
> -		dev_info(&client->dev,
> -			 "power monitor INA226 (Rshunt = %li uOhm)\n", shunt);
> -		data->registers = INA226_REGISTERS;
> -		break;
> -	default:
> -		/* unknown device id */
> -		return -ENODEV;
> -	}
> +	/* device configuration */
> +	i2c_smbus_write_word_swapped(client, INA2XX_CONFIG,
> +				     data->config->config);
> +	/* set current LSB to 1mA, shunt is in uOhms */
> +	/* (equation 13 in datasheet) */
> +	i2c_smbus_write_word_swapped(client, INA2XX_CALIBRATION,
> +				     data->config->calibration_factor / shunt);
> +	data->registers = data->config->registers;

Maybe we can get rid of data->registers now?

>  
>  	i2c_set_clientdata(client, data);
>  	mutex_init(&data->update_lock);
> @@ -317,6 +256,9 @@ static int ina2xx_probe(struct i2c_client *client,
>  		goto out_err_hwmon;
>  	}
>  
> +	dev_info(&client->dev, "power monitor %s (Rshunt = %li uOhm)\n",
> +		 id->name, shunt);
> +
>  	return 0;
>  
>  out_err_hwmon:


-- 
Jean Delvare

_______________________________________________
lm-sensors mailing list
lm-sensors@xxxxxxxxxxxxxx
http://lists.lm-sensors.org/mailman/listinfo/lm-sensors


[Index of Archives]     [Linux Kernel]     [Linux Hardware Monitoring]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux