> 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, > - ®, &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, > + ®, &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