On 11/11/19 12:55 AM, Jonathan Cameron wrote: > On Fri, 8 Nov 2019 21:09:45 +0800 > Song Qiang <songqiang1304521@xxxxxxxxx> wrote: > >> The AD5940 is a high precision, low power analog front end (AFE) >> designed for portable applications that require high precision, >> electrochemical-based measurement techniques, such as amper- >> ometric, voltammetric, or impedance measurements. >> >> Datasheet: >> Link: https://www.analog.com/media/en/technical-documentation/data-sheets/AD5940.pdf >> >> Signed-off-by: Song Qiang <songqiang1304521@xxxxxxxxx> > > Nice little driver on the whole. I'm guessing this is very much the 'first of > many' patches to support this complex part. Makes sense and keeps it manageable > from a review point of view. > > A few comments inline. > > Thanks, > > Jonathan > >> --- >> .../ABI/testing/sysfs-bus-iio-adc-ad5940 | 7 + >> drivers/iio/adc/Kconfig | 10 + >> drivers/iio/adc/Makefile | 1 + >> drivers/iio/adc/ad5940.c | 679 ++++++++++++++++++ >> 4 files changed, 697 insertions(+) >> create mode 100644 Documentation/ABI/testing/sysfs-bus-iio-adc-ad5940 >> create mode 100644 drivers/iio/adc/ad5940.c >> ... >> +static int ad5940_write_reg_mask(struct ad5940_state *st, u16 addr, >> + u32 mask, u32 data) >> +{ >> + u32 temp; >> + int ret; >> + >> + ret = ad5940_read_reg(st, addr, &temp); >> + if (ret < 0) >> + return ret; >> + >> + temp &= ~mask; >> + temp |= data; >> + >> + return ad5940_write_reg(st, addr, temp); >> +} >> + >> +static ssize_t ad5940_read_info(struct iio_dev *indio_dev, >> + uintptr_t private, >> + const struct iio_chan_spec *chan, >> + char *buf) >> +{ >> + struct ad5940_state *st = iio_priv(indio_dev); >> + >> + switch ((u32)private) { >> + case AD5940_CHANNEL_NAME: > > What is the logic here in this magic define? > Do you mean this is not necessary? In this driver, 'ad5940_read_info' is only used in name reading, but I was thinking maybe there will be some other stuff to be reading from this in the future. >> + return sprintf(buf, "%s\n", >> + st->channel_config[chan->address].channel_name); >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static const struct iio_chan_spec_ext_info ad4590_ext_info[] = { >> + { >> + .name = "name", > > This is defining new ABI. I'm not necessarily against doing so but > it needs a separate documentation patch so that it's easy to discuss. > Documentation/ABI/testing/sysfs-bus-iio > > We might want to think of a similar naming to the label we recently > added for the device itself. > I'll look into this and see if I can use 'label' instead. >> + .read = ad5940_read_info, >> + .private = AD5940_CHANNEL_NAME, >> + .shared = IIO_SEPARATE, >> + }, >> + { }, >> +}; >> + >> +static const struct iio_chan_spec ad5940_channel_template = { >> + .type = IIO_VOLTAGE, >> + .differential = 1, >> + .indexed = 1, >> + .ext_info = ad4590_ext_info, >> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), >> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), >> +}; >> + >> +static int ad5940_clear_ready(struct ad5940_state *st) >> +{ >> + int ret; >> + >> + ret = ad5940_write_reg_mask(st, AD5940_REG_AFECON, >> + AD5940_AFECON_ADCCONV_MSK, >> + AD5940_AFECON_ADCCONV_DIS); >> + if (ret < 0) >> + return ret; >> + >> + return ad5940_write_reg(st, AD5940_REG_INTCLR, AD5940_INTC_ADC); >> +} >> + >> +static irqreturn_t ad5940_irq_handler(int irq, void *private) >> +{ >> + struct ad5940_state *st = private; >> + int ret; >> + >> + ret = ad5940_clear_ready(st); >> + if (ret < 0) >> + return ret; >> + >> + complete(&st->complete); >> + >> + return IRQ_HANDLED; >> +} >> + >> +static int ad5940_scan_direct(struct ad5940_state *st, u32 mux, int *val) >> +{ >> + int ret; >> + u32 result; >> + >> + mutex_lock(&st->lock); >> + ret = ad5940_write_reg_mask(st, AD5940_REG_ADCCON, >> + AD5940_ADCCON_MUX_MSK, mux); >> + if (ret < 0) >> + goto unlock_return; >> + >> + reinit_completion(&st->complete); >> + ret = ad5940_write_reg_mask(st, AD5940_REG_AFECON, >> + AD5940_AFECON_ADCCONV_MSK, >> + AD5940_AFECON_ADCCONV_EN); >> + if (ret < 0) >> + goto unlock_return; >> + >> + ret = wait_for_completion_timeout(&st->complete, >> + msecs_to_jiffies(1000)); >> + if (!ret) { >> + ad5940_clear_ready(st); >> + ret = -ETIMEDOUT; >> + goto unlock_return; >> + } >> + >> + ret = ad5940_read_reg(st, AD5940_REG_ADCDAT, &result); >> + mutex_unlock(&st->lock); >> + if (ret < 0) >> + return ret; > > As you already have the unlock_return below on this occasion I would > move the if (ret) inside the lock so all similar errors take a simpler > exit path. If this was the only case and you didn't have the handling > I would agree with how you have done it. > >> + *val = result & 0xffff; >> + >> + return 0; >> + >> +unlock_return: >> + mutex_unlock(&st->lock); >> + return ret; >> +} >> + >> +static int ad5940_read_raw(struct iio_dev *indio_dev, >> + const struct iio_chan_spec *chan, int *val, int *val2, long info) >> +{ >> + struct ad5940_state *st = iio_priv(indio_dev); >> + int ret; >> + >> + switch (info) { >> + case IIO_CHAN_INFO_RAW: >> + ret = ad5940_scan_direct(st, >> + st->channel_config[chan->address].ain, >> + val); >> + if (ret < 0) >> + return ret; >> + else >> + return IIO_VAL_INT; > return IIO_VAL_INT; > > Cleaner to only have the error path indented. > if / else kind of implies some 'equality' of the two > options. > >> + case IIO_CHAN_INFO_SCALE: >> + *val = st->vref_mv; >> + *val2 = 16; >> + return IIO_VAL_FRACTIONAL_LOG2; >> + default: >> + return -EINVAL; >> + } >> +} >> + >> +static const struct iio_info ad5940_info = { >> + .read_raw = &ad5940_read_raw, >> +}; >> + >> +int cmp_u8(const void *a, const void *b) >> +{ >> + return (*(u8 *)a - *(u8 *)b); >> +} >> + >> +static int ad5940_check_channel_indexes(struct device *dev, u32 *ain) >> +{ >> + const u8 channel_p[] = { >> + 0, 1, 2, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 18, 19, >> + 20, 22, 23, 24, 25, 26, 31, 33, 35, 36 >> + }; >> + const u8 channel_n[] = { >> + 0, 1, 2, 4, 5, 6, 7, 10, 11, 12, 14, 16, 17, 20 >> + }; >> + u8 *index; >> + >> + index = (u8 *) bsearch(&ain[0], channel_p, ARRAY_SIZE(channel_p), >> + sizeof(u8), cmp_u8); >> + if (!index) { >> + dev_err(dev, "Positive input index not found.\n"); >> + return -EINVAL; >> + } >> + >> + index = (u8 *) bsearch(&ain[1], channel_n, ARRAY_SIZE(channel_n), >> + sizeof(u8), cmp_u8); >> + if (!index) { >> + dev_err(dev, "negtive input index not found.\n"); >> + return -EINVAL; >> + } >> + >> + return 0; >> +} >> + >> +static int ad5940_of_parse_channel_config(struct iio_dev *indio_dev, >> + struct device_node *np) >> +{ >> + struct ad5940_state *st = iio_priv(indio_dev); >> + struct iio_chan_spec *chan; >> + struct device_node *child; >> + u32 channel, ain[2]; >> + int ret; >> + >> + st->num_channels = of_get_available_child_count(np); >> + if (!st->num_channels) { >> + dev_err(indio_dev->dev.parent, "no channel children\n"); >> + return -ENODEV; >> + } >> + >> + chan = devm_kcalloc(indio_dev->dev.parent, st->num_channels, >> + sizeof(*chan), GFP_KERNEL); >> + if (!chan) >> + return -ENOMEM; >> + >> + st->channel_config = devm_kcalloc(indio_dev->dev.parent, >> + st->num_channels, >> + sizeof(*st->channel_config), >> + GFP_KERNEL); >> + if (!st->channel_config) >> + return -ENOMEM; >> + >> + indio_dev->channels = chan; >> + indio_dev->num_channels = st->num_channels; >> + >> + for_each_available_child_of_node(np, child) { >> + ret = of_property_read_u32(child, "reg", &channel); >> + if (ret) >> + goto err; >> + >> + ret = of_property_read_u32_array(child, "diff-channels", >> + ain, 2); >> + if (ret) >> + goto err; >> + >> + ret = of_property_read_string(child, "channel-name", >> + &st->channel_config[channel].channel_name); >> + if (ret) >> + st->channel_config[channel].channel_name = "none-name"; > > You have this as required I think in the dt properties. If that is the case then > enforce it and refuse to load the driver if not supplied. Otherwise change > the dt docs to make it optional (which is probably better) > I prefer to have name required because a channel here is a combination of some input and some output. Without name, we will have to look into dt to see which input and which output is used in this channel. >> + >> + ret = ad5940_check_channel_indexes(indio_dev->dev.parent, ain); >> + if (ret) { >> + dev_err(indio_dev->dev.parent, >> + "some input channel index does not exist: %d, %d, %d", >> + channel, ain[0], ain[1]); >> + goto err; >> + } >> + >> + st->channel_config[channel].ain = AD5940_CHANNEL_AINP(ain[0]) | >> + AD5940_CHANNEL_AINN(ain[1]); >> + >> + *chan = ad5940_channel_template; >> + chan->address = channel; >> + chan->scan_index = channel; >> + chan->channel = ain[0]; >> + chan->channel2 = ain[1]; >> + >> + chan++; >> + } >> + >> + return 0; >> +err: >> + of_node_put(child); >> + >> + return ret; >> +} >> + >> +static int ad5940_config_polarity(struct ad5940_state *st, u32 polarity) >> +{ >> + u32 val; >> + >> + if (polarity == IRQF_TRIGGER_RISING) >> + val = AD5940_INTCPOL_POS; >> + else >> + val = AD5940_INTCPOL_NEG; >> + >> + return ad5940_write_reg_mask(st, AD5940_REG_INTCPOL, >> + AD5940_INTCPOL_MSK, val); >> +} >> + >> +static int ad5940_config_int_io(struct ad5940_state *st, u8 int_io) >> +{ >> + int ret = 0; >> + >> + if (int_io == 3) > > Switch statement preferred for matches like this. > >> + ret = ad5940_write_reg_mask(st, AD5940_REG_GP0CON, >> + AD5940_GP0CON_3_MSK, >> + AD5940_GP0CON_3_INT); >> + else if (int_io == 6) >> + ret = ad5940_write_reg_mask(st, AD5940_REG_GP0CON, >> + AD5940_GP0CON_6_MSK, >> + AD5940_GP0CON_6_INT); >> + if (ret < 0) >> + return ret; >> + >> + return ad5940_write_reg(st, AD5940_REG_GP0OEN, BIT(int_io)); >> +} >> + >> +static const u32 ad5940_powerup_setting[][2] = { > > Hmm. This is not good practice when we have docs for the values. > If we can provide a breakdown into what is being set that would be > great. I can't find docs immediately for some of these however... > We have docs for some of them, but not for all. I got some of the init register values from Analog's example code, which didn't explain much. I'll add comment to these values as much as I can. yours, Song Qiang >> + { 0x0908, 0x02c9 }, >> + { 0x0c08, 0x206c }, >> + { 0x21f0, 0x0010 }, > > This one is is simply saying only 1 repeat conversion for example. > Add some defines and you can make that clear. > >> + { 0x0410, 0x02c9 }, >> + { 0x0a28, 0x0009 }, >> + { 0x238c, 0x0104 }, >> + { 0x0a04, 0x4859 }, >> + { 0x0a04, 0xf27b }, >> + { 0x0a00, 0x8009 }, >> + { 0x22f0, 0x0000 }, >> + { 0x2230, 0xde87a5af }, >> + { 0x2250, 0x103f }, >> + { 0x22b0, 0x203c }, >> + { 0x2230, 0xde87a5a0 }, >> +}; >> + ...