> > +struct ltc2664_state { > > + struct spi_device *spi; > > + struct regmap *regmap; > > + struct ltc2664_chan channels[LTC2672_MAX_CHANNEL]; > > + /* lock to protect against multiple access to the device and shared data > */ > > + struct mutex lock; > > + const struct ltc2664_chip_info *chip_info; > > + struct iio_chan_spec *iio_channels; > > + int vref; > > vref_mv > > Always nice to have the units since regulators use µV and IIO uses mV. > Otherwise we have to guess. > > > + u32 toggle_sel; > > + u32 global_toggle; > > Should this be bool? > > > + u32 rfsadj; > > rfsadj_ohms > > > +}; > > + > > +static const int ltc2664_span_helper[][2] = { > > + { 0, 5000 }, > > + { 0, 10000 }, > > + { -5000, 5000 }, > > + { -10000, 10000 }, > > + { -2500, 2500 }, > > +}; > > + > > +static const int ltc2672_span_helper[][2] = { > > + { 0, 3125 }, > > + { 0, 6250 }, > > + { 0, 12500 }, > > + { 0, 25000 }, > > + { 0, 50000 }, > > + { 0, 100000 }, > > + { 0, 200000 }, > > + { 0, 300000 }, > > +}; > > + > > +static int ltc2664_scale_get(const struct ltc2664_state *st, int c) > > +{ > > + const struct ltc2664_chan *chan = &st->channels[c]; > > + const int (*span_helper)[2] = st->chip_info->span_helper; > > + int span, fs; > > + > > + span = chan->span; > > + if (span < 0) > > + return span; > > + > > + fs = span_helper[span][1] - span_helper[span][0]; > > + > > + return (fs / 2500) * st->vref; > > Should we multiply first and then divide? 3125 isn't divisible by 2500 > so there may be unwanted rounding otherwise. Yes that would make sense, should perform the multiplication first, then the division. > > +} > > + > > +static int ltc2672_scale_get(const struct ltc2664_state *st, int c) > > +{ > > + const struct ltc2664_chan *chan = &st->channels[c]; > > + int span, fs; > > + > > + span = chan->span; > > + if (span < 0) > > + return span; > > + > > + fs = 1000 * st->vref / st->rfsadj; > > + > > + if (span == LTC2672_MAX_SPAN) > > + return 4800 * fs; > > + > > + return LTC2672_SCALE_MULTIPLIER(span) * fs; > > Are we losing accuracy by multiplying after dividing here as well? > > > +} > > + > > +static int ltc2664_offset_get(const struct ltc2664_state *st, int c) > > +{ > > + const struct ltc2664_chan *chan = &st->channels[c]; > > + int span; > > + > > + span = chan->span; > > + if (span < 0) > > + return span; > > + > > + if (st->chip_info->span_helper[span][0] < 0) > > + return -32768; > > + > > + return 0; > > +} > > + > > +static int ltc2672_offset_get(const struct ltc2664_state *st, int c) > > +{ > > + const struct ltc2664_chan *chan = &st->channels[c]; > > + int span; > > + > > + span = chan->span; > > + if (span < 0) > > + return span; > > + > > + if (st->chip_info->span_helper[span][1] < 0) > > Should this be span_helper[span][0]? [span][1] is always > 0. > > And for that matter, [span][0] is always 0 for ltc2672, so > maybe we don't need this check at all? > > > + return -32768; > > + > > + return st->chip_info->span_helper[span][1] / 250; > > Why is this one not return 0 like the other chip? > > Figure 24 and 25 in the datasheet don't show an offset in > the tranfer function. I think I misinterpret page 25 of the datasheet about the offset. We can make it return 0. > > +} > > + > > +static int ltc2664_dac_code_write(struct ltc2664_state *st, u32 chan, u32 > input, > > + u16 code) > > +{ > > + struct ltc2664_chan *c = &st->channels[chan]; > > + int ret, reg; > > + > > + guard(mutex)(&st->lock); > > + /* select the correct input register to write to */ > > + if (c->toggle_chan) { > > + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, > > + input << chan); > > + if (ret) > > + return ret; > > + } > > + /* > > + * If in toggle mode the dac should be updated by an > > + * external signal (or sw toggle) and not here. > > + */ > > + if (st->toggle_sel & BIT(chan)) > > + reg = LTC2664_CMD_WRITE_N(chan); > > + else > > + reg = LTC2664_CMD_WRITE_N_UPDATE_N(chan); > > + > > + ret = regmap_write(st->regmap, reg, code); > > + if (ret) > > + return ret; > > + > > + c->raw[input] = code; > > + > > + if (c->toggle_chan) { > > + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, > > + st->toggle_sel); > > + if (ret) > > + return ret; > > + } > > + > > + return ret; > > +} > > + > > +static int ltc2664_dac_code_read(struct ltc2664_state *st, u32 chan, u32 > input, > > + u32 *code) > > +{ > > + guard(mutex)(&st->lock); > > + *code = st->channels[chan].raw[input]; > > + > > + return 0; > > +} > > + > > +static const int ltc2664_raw_range[] = {0, 1, U16_MAX}; > > + > > +static int ltc2664_read_avail(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, > > + const int **vals, int *type, int *length, > > + long info) > > +{ > > + switch (info) { > > + case IIO_CHAN_INFO_RAW: > > + *vals = ltc2664_raw_range; > > + *type = IIO_VAL_INT; > > + > > + return IIO_AVAIL_RANGE; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static int ltc2664_read_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, int *val, > > + int *val2, long info) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + int ret; > > + > > + switch (info) { > > + case IIO_CHAN_INFO_RAW: > > + ret = ltc2664_dac_code_read(st, chan->channel, > > + LTC2664_INPUT_A, val); > > + if (ret) > > + return ret; > > + > > + return IIO_VAL_INT; > > + case IIO_CHAN_INFO_OFFSET: > > + *val = st->chip_info->offset_get(st, chan->channel); > > + > > + return IIO_VAL_INT; > > + case IIO_CHAN_INFO_SCALE: > > + *val = st->chip_info->scale_get(st, chan->channel); > > + > > + *val2 = 16; > > + return IIO_VAL_FRACTIONAL_LOG2; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static int ltc2664_write_raw(struct iio_dev *indio_dev, > > + struct iio_chan_spec const *chan, int val, > > + int val2, long info) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + > > + switch (info) { > > + case IIO_CHAN_INFO_RAW: > > + if (val > U16_MAX || val < 0) > > + return -EINVAL; > > + > > + return ltc2664_dac_code_write(st, chan->channel, > > + LTC2664_INPUT_A, val); > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static ssize_t ltc2664_reg_bool_get(struct iio_dev *indio_dev, > > + uintptr_t private, > > + const struct iio_chan_spec *chan, > > + char *buf) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + u32 val; > > + > > + guard(mutex)(&st->lock); > > + switch (private) { > > + case LTC2664_POWERDOWN: > > + val = st->channels[chan->channel].powerdown; > > + > > + return sysfs_emit(buf, "%u\n", val); > > + case LTC2664_POWERDOWN_MODE: > > + return sysfs_emit(buf, "42kohm_to_gnd\n");> + case > LTC2664_TOGGLE_EN: > > + val = !!(st->toggle_sel & BIT(chan->channel)); > > + > > + return sysfs_emit(buf, "%u\n", val); > > + case LTC2664_GLOBAL_TOGGLE: > > + val = st->global_toggle; > > + > > + return sysfs_emit(buf, "%u\n", val); > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static ssize_t ltc2664_reg_bool_set(struct iio_dev *indio_dev, > > + uintptr_t private, > > + const struct iio_chan_spec *chan, > > + const char *buf, size_t len) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + int ret; > > + bool en; > > + > > + ret = kstrtobool(buf, &en); > > + if (ret) > > + return ret; > > + > > + guard(mutex)(&st->lock); > > + switch (private) { > > + case LTC2664_POWERDOWN: > > + ret = regmap_write(st->regmap, > > + en ? > LTC2664_CMD_POWER_DOWN_N(chan->channel) : > > + LTC2664_CMD_UPDATE_N(chan->channel), > en); > > + if (ret) > > + return ret; > > + > > + st->channels[chan->channel].powerdown = en; > > + > > + return len; > > + case LTC2664_TOGGLE_EN: > > + if (en) > > + st->toggle_sel |= BIT(chan->channel); > > + else > > + st->toggle_sel &= ~BIT(chan->channel); > > + > > + ret = regmap_write(st->regmap, LTC2664_CMD_TOGGLE_SEL, > > + st->toggle_sel); > > + if (ret) > > + return ret; > > + > > + return len; > > + case LTC2664_GLOBAL_TOGGLE: > > + ret = regmap_write(st->regmap, > LTC2664_CMD_GLOBAL_TOGGLE, en); > > + if (ret) > > + return ret; > > + > > + st->global_toggle = en; > > + > > + return len; > > + default: > > + return -EINVAL; > > + } > > +} > > + > > +static ssize_t ltc2664_dac_input_read(struct iio_dev *indio_dev, > > + uintptr_t private, > > + const struct iio_chan_spec *chan, > > + char *buf) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + int ret; > > + u32 val; > > + > > + if (private == LTC2664_INPUT_B_AVAIL) > > + return sysfs_emit(buf, "[%u %u %u]\n", ltc2664_raw_range[0], > > + ltc2664_raw_range[1], > > + ltc2664_raw_range[2] / 4); > > + > > + ret = ltc2664_dac_code_read(st, chan->channel, private, &val); > > + if (ret) > > + return ret; > > + > > + return sysfs_emit(buf, "%u\n", val); > > +} > > + > > +static ssize_t ltc2664_dac_input_write(struct iio_dev *indio_dev, > > + uintptr_t private, > > + const struct iio_chan_spec *chan, > > + const char *buf, size_t len) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + int ret; > > + u16 val; > > + > > + if (private == LTC2664_INPUT_B_AVAIL) > > + return -EINVAL; > > + > > + ret = kstrtou16(buf, 10, &val); > > + if (ret) > > + return ret; > > + > > + ret = ltc2664_dac_code_write(st, chan->channel, private, val); > > + if (ret) > > + return ret; > > + > > + return len; > > +} > > + > > +static int ltc2664_reg_access(struct iio_dev *indio_dev, > > + unsigned int reg, > > + unsigned int writeval, > > + unsigned int *readval) > > +{ > > + struct ltc2664_state *st = iio_priv(indio_dev); > > + > > + if (readval) > > + return -EOPNOTSUPP; > > + > > + return regmap_write(st->regmap, reg, writeval); > > +} > > + > > +#define LTC2664_CHAN_EXT_INFO(_name, _what, _shared, _read, _write) { > \ > > + .name = _name, \ > > + .read = (_read), \ > > + .write = (_write), \ > > + .private = (_what), \ > > + .shared = (_shared), \ > > +} > > + > > +/* > > + * For toggle mode we only expose the symbol attr (sw_toggle) in case a > TGPx is > > + * not provided in dts. > > + */ > > +static const struct iio_chan_spec_ext_info ltc2664_toggle_sym_ext_info[] = { > > + LTC2664_CHAN_EXT_INFO("raw0", LTC2664_INPUT_A, IIO_SEPARATE, > > + ltc2664_dac_input_read, > ltc2664_dac_input_write), > > + LTC2664_CHAN_EXT_INFO("raw1", LTC2664_INPUT_B, IIO_SEPARATE, > > + ltc2664_dac_input_read, > ltc2664_dac_input_write), > > + LTC2664_CHAN_EXT_INFO("powerdown", LTC2664_POWERDOWN, > IIO_SEPARATE, > > + ltc2664_reg_bool_get, ltc2664_reg_bool_set), > > + LTC2664_CHAN_EXT_INFO("powerdown_mode", > LTC2664_POWERDOWN_MODE, > > + IIO_SEPARATE, ltc2664_reg_bool_get, NULL), > > + LTC2664_CHAN_EXT_INFO("symbol", LTC2664_GLOBAL_TOGGLE, > IIO_SEPARATE, > > + ltc2664_reg_bool_get, ltc2664_reg_bool_set), > > + LTC2664_CHAN_EXT_INFO("toggle_en", LTC2664_TOGGLE_EN, > > + IIO_SEPARATE, ltc2664_reg_bool_get, > > + ltc2664_reg_bool_set), > > + { } > > +}; > > + > > +static const struct iio_chan_spec_ext_info ltc2664_ext_info[] = { > > + LTC2664_CHAN_EXT_INFO("powerdown", LTC2664_POWERDOWN, > IIO_SEPARATE, > > + ltc2664_reg_bool_get, ltc2664_reg_bool_set), > > + LTC2664_CHAN_EXT_INFO("powerdown_mode", > LTC2664_POWERDOWN_MODE, > > + IIO_SEPARATE, ltc2664_reg_bool_get, NULL), > > + { } > > +}; > > + > > +#define LTC2664_CHANNEL(_chan) { \ > > + .indexed = 1, \ > > + .output = 1, \ > > + .channel = (_chan), \ > > + .info_mask_separate = BIT(IIO_CHAN_INFO_SCALE) | \ > > + BIT(IIO_CHAN_INFO_OFFSET) | BIT(IIO_CHAN_INFO_RAW), > \ > > + .info_mask_separate_available = BIT(IIO_CHAN_INFO_RAW), > \ > > + .ext_info = ltc2664_ext_info, \ > > +} > > + > > +static const struct iio_chan_spec ltc2664_channels[] = { > > + LTC2664_CHANNEL(0), > > + LTC2664_CHANNEL(1), > > + LTC2664_CHANNEL(2), > > + LTC2664_CHANNEL(3), > > +}; > > + > > +static const struct iio_chan_spec ltc2672_channels[] = { > > + LTC2664_CHANNEL(0), > > + LTC2664_CHANNEL(1), > > + LTC2664_CHANNEL(2), > > + LTC2664_CHANNEL(3), > > + LTC2664_CHANNEL(4), > > +}; > > Do we really need these since they are only used as a template anyway? > We could just have a single template for one channel and copy it as > manay times as needed. Yes, from what I can see we need separate channel specs for both devices since they have a differing number of channels. As for your suggestion about having a single template for one channel and copying it as many times as needed, I'm not entirely sure how to implement it in this context. Could you provide something like a code snippet to illustrate this? > > + > > +static const struct ltc2664_chip_info ltc2664_chip = { > > + .id = LTC2664, > > + .name = "ltc2664", > > + .scale_get = ltc2664_scale_get, > > + .offset_get = ltc2664_offset_get, > > + .measurement_type = IIO_VOLTAGE, > > + .num_channels = ARRAY_SIZE(ltc2664_channels), > > + .iio_chan = ltc2664_channels, > > + .span_helper = ltc2664_span_helper, > > + .num_span = ARRAY_SIZE(ltc2664_span_helper), > > + .internal_vref = 2500, > > + .manual_span_support = true, > > + .rfsadj_support = false, > > +}; > > +