Re: [RFC v2] iio: ad5755: added support for switching between voltage and current output

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

 



> This patch adds support for switching modes from userspace.

comments below
 
> Signed-off-by: Sean Nyekjaer <sean.nyekjaer@xxxxxxxxx>
> ---
> Changes since v1:
> - Channels are exported as two independent channels
> - Changes between modes can be done by writing a new scale that fits the channel
> - If voltage mode is chosen, the readback from the current channel is -EBUSY
> 
>  drivers/iio/dac/ad5755.c | 385 ++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 297 insertions(+), 88 deletions(-)
> 
> diff --git a/drivers/iio/dac/ad5755.c b/drivers/iio/dac/ad5755.c
> index bfb350a..e614884 100644
> --- a/drivers/iio/dac/ad5755.c
> +++ b/drivers/iio/dac/ad5755.c
> @@ -18,7 +18,7 @@
>  #include <linux/iio/sysfs.h>
>  #include <linux/platform_data/ad5755.h>
>  
> -#define AD5755_NUM_CHANNELS 4
> +#define AD5755_NUM_CHANNELS 8	//4

please delete the leftover C++-style comment

>  
>  #define AD5755_ADDR(x)			((x) << 16)
>  
> @@ -91,6 +91,8 @@ struct ad5755_state {
>  	unsigned int			ctrl[AD5755_NUM_CHANNELS];
>  	struct iio_chan_spec		channels[AD5755_NUM_CHANNELS];
>  
> +	struct ad5755_platform_data *pdata;
> +
>  	/*
>  	 * DMA (thus cache coherency maintenance) requires the
>  	 * transfer buffers to live in their own cache lines.
> @@ -109,6 +111,153 @@ enum ad5755_type {
>  	ID_AD5737,
>  };
>  
> +struct ad5755_ranges {
> +	enum ad5755_mode range;
> +	unsigned int scale;
> +	int offset;
> +};
> +
> +static const struct ad5755_ranges ad5755_range_def[] = {
> +	{
> +		.range = AD5755_MODE_VOLTAGE_0V_5V,
> +		.scale = 76293945,
> +		.offset = 0,
> +	}, {
> +		.range = AD5755_MODE_VOLTAGE_0V_10V,
> +		.scale = 152587890,
> +		.offset = 0,
> +	}, {
> +		.range = AD5755_MODE_VOLTAGE_PLUSMINUS_5V,
> +		.scale = 152587890,
> +		.offset = -(1 << (16 /*REALBITS*/ - 1)),
> +	}, {
> +		.range = AD5755_MODE_VOLTAGE_PLUSMINUS_10V,
> +		.scale = 305175781,
> +		.offset = -(1 << (16 /*REALBITS*/ - 1)),
> +	}, {
> +		.range = AD5755_MODE_CURRENT_4mA_20mA,
> +		.scale = 244140,
> +		.offset = 16384,
> +	}, {
> +		.range = AD5755_MODE_CURRENT_0mA_20mA,
> +		.scale = 305175,
> +		.offset = 0,
> +	}, {
> +		.range = AD5755_MODE_CURRENT_0mA_24mA,
> +		.scale = 366210,
> +		.offset = 0,
> +	}
> +};
> +
> +static inline enum ad5755_mode ad5755_get_chan_mode(struct ad5755_state *st,

please drop 'inline' (here and below), let the compiler figure it out (or 
not)

> +						    const struct iio_chan_spec
> +						    *chan)
> +{
> +	return st->ctrl[chan->channel] & 7;

what does the magic 7 do?
it does NOT protect out-of-bound accesses to ad5755_range_def which has 
only 7 elements, not 8

> +}
> +
> +static inline int ad5755_get_chan_scale(struct ad5755_state *st,
> +					const struct iio_chan_spec *chan)
> +{
> +	return ad5755_range_def[ad5755_get_chan_mode(st, chan)].scale;
> +}
> +
> +static inline int ad5755_get_chan_offset(struct ad5755_state *st,
> +					 const struct iio_chan_spec *chan)
> +{
> +	return ad5755_range_def[ad5755_get_chan_mode(st, chan)].offset;
> +}
> +
> +static bool ad5755_is_voltage_mode(enum ad5755_mode mode)
> +{
> +	switch (mode) {
> +	case AD5755_MODE_VOLTAGE_0V_5V:
> +	case AD5755_MODE_VOLTAGE_0V_10V:
> +	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
> +	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
> +
> +static ssize_t ad5755_scales(char *buf, bool voltage_mode)
> +{
> +	int i, len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(ad5755_range_def); i++) {
> +		if (ad5755_is_voltage_mode(i) != voltage_mode)
> +			continue;
> +		len += sprintf(buf + len, "0.%09u ", ad5755_range_def[i].scale);
> +	}
> +	len += sprintf(buf + len, "\n");
> +
> +	return len;
> +}
> +
> +static ssize_t ad5755_offset(char *buf, bool voltage_mode)
> +{
> +	int i, len = 0;
> +
> +	for (i = 0; i < ARRAY_SIZE(ad5755_range_def); i++) {
> +		if (ad5755_is_voltage_mode(i) != voltage_mode)
> +			continue;
> +		len += sprintf(buf + len, "%d ", ad5755_range_def[i].offset);
> +	}
> +	len += sprintf(buf + len, "\n");
> +
> +	return len;
> +}
> +
> +static ssize_t ad5755_show_voltage_scales(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	return ad5755_scales(buf, true);
> +}
> +
> +static ssize_t ad5755_show_voltage_offset(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	return ad5755_offset(buf, true);
> +}
> +
> +static ssize_t ad5755_show_current_scales(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	return ad5755_scales(buf, false);
> +}
> +
> +static ssize_t ad5755_show_current_offset(struct device *dev,
> +					  struct device_attribute *attr,
> +					  char *buf)
> +{
> +	return ad5755_offset(buf, false);
> +}
> +
> +static IIO_DEVICE_ATTR(out_voltage_scale_available, S_IRUGO,
> +		       ad5755_show_voltage_scales, NULL, 0);
> +static IIO_DEVICE_ATTR(out_voltage_offset_available, S_IRUGO,
> +		       ad5755_show_voltage_offset, NULL, 0);
> +static IIO_DEVICE_ATTR(out_current_scale_available, S_IRUGO,
> +		       ad5755_show_current_scales, NULL, 0);
> +static IIO_DEVICE_ATTR(out_current_offset_available, S_IRUGO,
> +		       ad5755_show_current_offset, NULL, 0);
> +
> +static struct attribute *ad5755_attributes[] = {
> +	&iio_dev_attr_out_voltage_scale_available.dev_attr.attr,
> +	&iio_dev_attr_out_voltage_offset_available.dev_attr.attr,
> +	&iio_dev_attr_out_current_scale_available.dev_attr.attr,
> +	&iio_dev_attr_out_current_offset_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ad5755_attribute_group = {
> +	.attrs = ad5755_attributes,
> +};
> +
>  static int ad5755_write_unlocked(struct iio_dev *indio_dev,
>  	unsigned int reg, unsigned int val)
>  {
> @@ -226,31 +375,54 @@ out_unlock:
>  	return 0;
>  }
>  
> -static const int ad5755_min_max_table[][2] = {
> -	[AD5755_MODE_VOLTAGE_0V_5V] = { 0, 5000 },
> -	[AD5755_MODE_VOLTAGE_0V_10V] = { 0, 10000 },
> -	[AD5755_MODE_VOLTAGE_PLUSMINUS_5V] = { -5000, 5000 },
> -	[AD5755_MODE_VOLTAGE_PLUSMINUS_10V] = { -10000, 10000 },
> -	[AD5755_MODE_CURRENT_4mA_20mA] = { 4, 20 },
> -	[AD5755_MODE_CURRENT_0mA_20mA] = { 0, 20 },
> -	[AD5755_MODE_CURRENT_0mA_24mA] = { 0, 24 },
> -};
> +static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
> +{
> +	switch (mode) {
> +	case AD5755_MODE_VOLTAGE_0V_5V:
> +	case AD5755_MODE_VOLTAGE_0V_10V:
> +	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
> +	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
> +		return st->chip_info->has_voltage_out;
> +	case AD5755_MODE_CURRENT_4mA_20mA:
> +	case AD5755_MODE_CURRENT_0mA_20mA:
> +	case AD5755_MODE_CURRENT_0mA_24mA:
> +		return true;
> +	default:
> +		return false;
> +	}
> +}
>  
> -static void ad5755_get_min_max(struct ad5755_state *st,
> -	struct iio_chan_spec const *chan, int *min, int *max)
> +static int ad5755_clear_dac(struct iio_dev *indio_dev, int channel)
>  {
> -	enum ad5755_mode mode = st->ctrl[chan->channel] & 7;
> -	*min = ad5755_min_max_table[mode][0];
> -	*max = ad5755_min_max_table[mode][1];
> +	int i = channel;

what is variable i for?

> +	int ret;

just 
return ad5755_update_dac_ctrl();

> +
> +	ret = ad5755_update_dac_ctrl(indio_dev, i, 0, UINT_MAX);
> +	return ret;
> +

drop extra newline here

>  }
>  
> -static inline int ad5755_get_offset(struct ad5755_state *st,
> -	struct iio_chan_spec const *chan)
> +static int ad5755_setup_dac(struct iio_dev *indio_dev, int channel)
>  {
> -	int min, max;
> +	int i = channel;

why i?
just return...

> +	int ret;
> +	struct ad5755_state *st = iio_priv(indio_dev);
> +	unsigned int val;
> +	struct ad5755_platform_data *pdata = st->pdata;
> +
> +	if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
> +		return -EINVAL;
> +
> +	val = 0;
> +	if (!pdata->dac[i].ext_current_sense_resistor)
> +		val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
> +	if (pdata->dac[i].enable_voltage_overrange)
> +		val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
> +	val |= pdata->dac[i].mode;
> +
> +	ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
> +	return ret;
>  
> -	ad5755_get_min_max(st, chan, &min, &max);
> -	return (min * (1 << chan->scan_type.realbits)) / (max - min);
>  }
>  
>  static int ad5755_chan_reg_info(struct ad5755_state *st,
> @@ -289,22 +461,29 @@ static int ad5755_chan_reg_info(struct ad5755_state *st,
>  	return 0;
>  }
>  
> +static int ad5755_channel_report(int type, int mode)
> +{
> +	return (type == IIO_VOLTAGE) == ad5755_is_voltage_mode(mode);
> +

drop newline

> +}
> +
>  static int ad5755_read_raw(struct iio_dev *indio_dev,
>  	const struct iio_chan_spec *chan, int *val, int *val2, long info)
>  {
>  	struct ad5755_state *st = iio_priv(indio_dev);
>  	unsigned int reg, shift, offset;
> -	int min, max;
>  	int ret;
>  
> +	if(!ad5755_channel_report(chan->type,st->pdata->dac[chan->address].mode))
> +		return -EBUSY;
> +
>  	switch (info) {
>  	case IIO_CHAN_INFO_SCALE:
> -		ad5755_get_min_max(st, chan, &min, &max);
> -		*val = max - min;
> -		*val2 = chan->scan_type.realbits;
> -		return IIO_VAL_FRACTIONAL_LOG2;
> +		*val = 0;
> +		*val2 = ad5755_get_chan_scale(st, chan);
> +		return IIO_VAL_INT_PLUS_NANO;
>  	case IIO_CHAN_INFO_OFFSET:
> -		*val = ad5755_get_offset(st, chan);
> +		*val = ad5755_get_chan_offset(st, chan);
>  		return IIO_VAL_INT;
>  	default:
>  		ret = ad5755_chan_reg_info(st, chan, info, false,
> @@ -328,21 +507,82 @@ static int ad5755_write_raw(struct iio_dev *indio_dev,
>  	const struct iio_chan_spec *chan, int val, int val2, long info)
>  {
>  	struct ad5755_state *st = iio_priv(indio_dev);
> -	unsigned int shift, reg, offset;
> -	int ret;
> -
> -	ret = ad5755_chan_reg_info(st, chan, info, true,
> -					&reg, &shift, &offset);
> -	if (ret)
> -		return ret;
> +	unsigned int shift, reg;
> +	int offset;
> +	unsigned int scale;
> +	int ret, i;
> +
> +	/* Lets us select mode only when a new scale is applied */

spelling: Let us select...

> +	if(info != IIO_CHAN_INFO_SCALE) {

whitespace after if please

> +		if(!ad5755_channel_report(chan->type,st->pdata->dac[chan->address].mode))
> +		return -EBUSY;
> +	}
>  
> -	val <<= shift;
> -	val += offset;
> +	switch (info) {
> +	case IIO_CHAN_INFO_SCALE:
> +	case IIO_CHAN_INFO_OFFSET:
> +		if (!(bool) (st->pwr_down & (1 << chan->channel))) {

no cast necessary

> +			printk("POWER DOWN off - Power down before shifting modes\n");

use dev_err()

> +			return -EPERM;
> +		}
> +	}
>  
> -	if (val < 0 || val > 0xffff)
> +	switch (info) {
> +	case IIO_CHAN_INFO_SCALE:
> +		offset = ad5755_range_def[st->pdata->dac[chan->address].mode].offset;
> +		/* is new scale allowed */
> +		for (i = 0; i < ARRAY_SIZE(ad5755_range_def); i++) {
> +			if (val2 == ad5755_range_def[i].scale &&
> +			    offset == ad5755_range_def[i].offset &&
> +			    ad5755_channel_report(chan->type, i)) {
> +				st->pdata->dac[chan->address].mode = i;
> +				ad5755_clear_dac(indio_dev, chan->address);
> +				return ad5755_setup_dac(indio_dev, chan->address);
> +			}
> +		}
>  		return -EINVAL;
> +	case IIO_CHAN_INFO_OFFSET:
> +		scale = ad5755_range_def[st->pdata->dac[chan->address].mode].scale;
> +		/* is new offset allowed */
> +		for (i = 0; i < ARRAY_SIZE(ad5755_range_def); i++) {
> +			if (val == ad5755_range_def[i].offset &&
> +			    scale == ad5755_range_def[i].scale &&
> +			    ad5755_channel_report(chan->type, i)) {
> +				st->pdata->dac[chan->address].mode = i;
> +				ad5755_clear_dac(indio_dev, chan->address);
> +				return ad5755_setup_dac(indio_dev, chan->address);
> +			}
> +		}
> +		return -EINVAL;
> +	default:
> +		ret = ad5755_chan_reg_info(st, chan, info, true,
> +					   &reg, &shift, &offset);
> +		if (ret)
> +			return ret;
> +
> +		val <<= shift;
> +		val += offset;
> +
> +		if (val < 0 || val > 0xffff)
> +			return -EINVAL;
> +
> +		return ad5755_write(indio_dev, reg, val);
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int ad5755_write_raw_get_fmt(struct iio_dev *indio_dev,
> +				    struct iio_chan_spec const *chan, long mask)
> +{
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		return IIO_VAL_INT_PLUS_NANO;
> +	default:
> +		return IIO_VAL_INT;
> +	}
>  
> -	return ad5755_write(indio_dev, reg, val);
> +	return -EINVAL;
>  }
>  
>  static ssize_t ad5755_read_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
> @@ -371,6 +611,8 @@ static ssize_t ad5755_write_powerdown(struct iio_dev *indio_dev, uintptr_t priv,
>  static const struct iio_info ad5755_info = {
>  	.read_raw = ad5755_read_raw,
>  	.write_raw = ad5755_write_raw,
> +	.write_raw_get_fmt = &ad5755_write_raw_get_fmt,
> +	.attrs = &ad5755_attribute_group,
>  	.driver_module = THIS_MODULE,
>  };
>  
> @@ -424,27 +666,9 @@ static const struct ad5755_chip_info ad5755_chip_info_tbl[] = {
>  	},
>  };
>  
> -static bool ad5755_is_valid_mode(struct ad5755_state *st, enum ad5755_mode mode)
> -{
> -	switch (mode) {
> -	case AD5755_MODE_VOLTAGE_0V_5V:
> -	case AD5755_MODE_VOLTAGE_0V_10V:
> -	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
> -	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
> -		return st->chip_info->has_voltage_out;
> -	case AD5755_MODE_CURRENT_4mA_20mA:
> -	case AD5755_MODE_CURRENT_0mA_20mA:
> -	case AD5755_MODE_CURRENT_0mA_24mA:
> -		return true;
> -	default:
> -		return false;
> -	}
> -}
> -
>  static int ad5755_setup_pdata(struct iio_dev *indio_dev,
> -			      const struct ad5755_platform_data *pdata)
> +			      struct ad5755_platform_data *pdata)
>  {
> -	struct ad5755_state *st = iio_priv(indio_dev);
>  	unsigned int val;
>  	unsigned int i;
>  	int ret;
> @@ -479,17 +703,7 @@ static int ad5755_setup_pdata(struct iio_dev *indio_dev,
>  	}
>  
>  	for (i = 0; i < ARRAY_SIZE(pdata->dac); ++i) {
> -		if (!ad5755_is_valid_mode(st, pdata->dac[i].mode))
> -			return -EINVAL;
> -
> -		val = 0;
> -		if (!pdata->dac[i].ext_current_sense_resistor)
> -			val |= AD5755_DAC_INT_CURRENT_SENSE_RESISTOR;
> -		if (pdata->dac[i].enable_voltage_overrange)
> -			val |= AD5755_DAC_VOLTAGE_OVERRANGE_EN;
> -		val |= pdata->dac[i].mode;
> -
> -		ret = ad5755_update_dac_ctrl(indio_dev, i, val, 0);
> +		ret = ad5755_setup_dac(indio_dev, i);
>  		if (ret < 0)
>  			return ret;
>  	}
> @@ -497,21 +711,8 @@ static int ad5755_setup_pdata(struct iio_dev *indio_dev,
>  	return 0;
>  }
>  
> -static bool ad5755_is_voltage_mode(enum ad5755_mode mode)
> -{
> -	switch (mode) {
> -	case AD5755_MODE_VOLTAGE_0V_5V:
> -	case AD5755_MODE_VOLTAGE_0V_10V:
> -	case AD5755_MODE_VOLTAGE_PLUSMINUS_5V:
> -	case AD5755_MODE_VOLTAGE_PLUSMINUS_10V:
> -		return true;
> -	default:
> -		return false;
> -	}
> -}
> -
>  static int ad5755_init_channels(struct iio_dev *indio_dev,
> -				const struct ad5755_platform_data *pdata)
> +				struct ad5755_platform_data *pdata)
>  {
>  	struct ad5755_state *st = iio_priv(indio_dev);
>  	struct iio_chan_spec *channels = st->channels;
> @@ -519,12 +720,18 @@ static int ad5755_init_channels(struct iio_dev *indio_dev,
>  
>  	for (i = 0; i < AD5755_NUM_CHANNELS; ++i) {
>  		channels[i] = st->chip_info->channel_template;
> -		channels[i].channel = i;
> -		channels[i].address = i;
> -		if (pdata && ad5755_is_voltage_mode(pdata->dac[i].mode))
> -			channels[i].type = IIO_VOLTAGE;
> -		else
> +		if (i <= 3) {
> +			channels[i].channel = i;
> +			channels[i].address = i;
>  			channels[i].type = IIO_CURRENT;
> +		} else {
> +			channels[i].channel = i - 4;
> +			channels[i].address = i - 4;
> +			channels[i].type = IIO_VOLTAGE;
> +		}
> +		/*if(!st->chip_info->has_voltage_out)
> +			if(i>3)
> +				break;*/
>  	}
>  
>  	indio_dev->channels = channels;
> @@ -543,7 +750,7 @@ static int ad5755_init_channels(struct iio_dev *indio_dev,
>  		}, \
>  	}
>  
> -static const struct ad5755_platform_data ad5755_default_pdata = {
> +struct ad5755_platform_data ad5755_default_pdata = {
>  	.ext_dc_dc_compenstation_resistor = false,
>  	.dc_dc_phase = AD5755_DC_DC_PHASE_ALL_SAME_EDGE,
>  	.dc_dc_freq = AD5755_DC_DC_FREQ_410kHZ,
> @@ -559,7 +766,7 @@ static const struct ad5755_platform_data ad5755_default_pdata = {
>  static int ad5755_probe(struct spi_device *spi)
>  {
>  	enum ad5755_type type = spi_get_device_id(spi)->driver_data;
> -	const struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
> +	struct ad5755_platform_data *pdata = dev_get_platdata(&spi->dev);
>  	struct iio_dev *indio_dev;
>  	struct ad5755_state *st;
>  	int ret;
> @@ -586,6 +793,8 @@ static int ad5755_probe(struct spi_device *spi)
>  	if (!pdata)
>  		pdata = &ad5755_default_pdata;
>  
> +	st->pdata = pdata;
> +
>  	ret = ad5755_init_channels(indio_dev, pdata);
>  	if (ret)
>  		return ret;
> 

-- 

Peter Meerwald-Stadler
+43-664-2444418 (mobile)
--
To unsubscribe from this list: send the line "unsubscribe linux-iio" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Input]     [Linux Kernel]     [Linux SCSI]     [X.org]

  Powered by Linux