On Fri, 22 Mar 2019 10:24:00 +0100 Ibtsam Ul-Haq <ibtsam.haq.0x01@xxxxxxxxx> wrote: > Thanks for the review. I shall make these changes in v3. One comment below. > > On Sat, Mar 16, 2019 at 3:48 PM Jonathan Cameron <jic23@xxxxxxxxxx> wrote: > > > > On Wed, 13 Mar 2019 12:14:03 +0100 > > Ibtsam Ul-Haq <ibtsam.haq.0x01@xxxxxxxxx> wrote: > > > > > Basic driver for Texas Instruments TLA202x series ADCs. Input > > > channels are configurable from the device tree. Input scaling > > > is supported. Trigger buffer support is not yet implemented. > > > > > > Signed-off-by: Ibtsam Ul-Haq <ibtsam.haq.0x01@xxxxxxxxx> > > One quick process thing - and this varies a bit by subsystem > > so worth checking for each one what happened for previous drivers.. > > > > Please don't post new versions in replies to old threads. > > They become buried in most email clients and it's not always > > obvious whether different branches of the thread are talking > > about particular versions of the driver. > > > > Much better to just start a fresh thread and rely on the > > consistent naming to make it obvious that it's a new version > > of the same patch. > > > > Anyhow, on to the review. > > > > This needs a devicetree binding document. > > > > Various minor bits inline. > > > > Jonathan > > > --- > > > Changes in v2: > > > - changed commit message > > > - Added mutex to protect channel selection > > > - Removed redundant mutex around i2c transfers > > > - Removed PROCESSED channel output > > > - Added SCALE info > > > - Removed Continuous Mode since continuous read not supported > > > - Removed SAMP_FREQ info since continuous read not supported > > > --- > > > drivers/iio/adc/Kconfig | 11 + > > > drivers/iio/adc/Makefile | 1 + > > > drivers/iio/adc/ti-tla2024.c | 463 +++++++++++++++++++++++++++++++++++++++++++ > > > 3 files changed, 475 insertions(+) > > > create mode 100644 drivers/iio/adc/ti-tla2024.c > > > > > > diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig > > > index 5762565..8c214c8 100644 > > > --- a/drivers/iio/adc/Kconfig > > > +++ b/drivers/iio/adc/Kconfig > > > @@ -816,6 +816,17 @@ config TI_AM335X_ADC > > > To compile this driver as a module, choose M here: the module will be > > > called ti_am335x_adc. > > > > > > +config TI_TLA2024 > > > + tristate "Texas Instruments TLA2024/TLA2022/TLA2021 ADC driver" > > > + depends on OF > > > + depends on I2C > > > + help > > > + Say yes here to build support for Texas Instruments TLA2024, > > > + TLA2022 or TLA2021 I2C Analog to Digital Converters. > > > + > > > + To compile this driver as a module, choose M here: the > > > + module will be called ti-tla2024. > > > + > > > config TI_TLC4541 > > > tristate "Texas Instruments TLC4541 ADC driver" > > > depends on SPI > > > diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile > > > index 9874e05..819f35b 100644 > > > --- a/drivers/iio/adc/Makefile > > > +++ b/drivers/iio/adc/Makefile > > > @@ -75,6 +75,7 @@ obj-$(CONFIG_TI_ADS7950) += ti-ads7950.o > > > obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o > > > obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o > > > obj-$(CONFIG_TI_TLC4541) += ti-tlc4541.o > > > +obj-$(CONFIG_TI_TLA2024) += ti-tla2024.o > > > obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o > > > obj-$(CONFIG_TWL6030_GPADC) += twl6030-gpadc.o > > > obj-$(CONFIG_VF610_ADC) += vf610_adc.o > > > diff --git a/drivers/iio/adc/ti-tla2024.c b/drivers/iio/adc/ti-tla2024.c > > > new file mode 100644 > > > index 0000000..e902eb8 > > > --- /dev/null > > > +++ b/drivers/iio/adc/ti-tla2024.c > > > @@ -0,0 +1,463 @@ > > > +/* SPDX-License-Identifier: GPL-2.0 > > > + * > > > + * Texas Instruments TLA2021/TLA2022/TLA2024 12-bit ADC driver > > > + * > > > + * Copyright (C) 2019 Koninklijke Philips N.V. > > > + */ > > > + > > > +#include <linux/delay.h> > > > +#include <linux/i2c.h> > > > +#include <linux/iio/iio.h> > > > +#include <linux/module.h> > > > +#include <linux/of.h> > > > + > > > +#define TLA2024_DATA 0x00 > > > +#define TLA2024_DATA_RES_MASK GENMASK(15, 4) > > > +#define TLA2024_DATA_RES_SHIFT 4 > > > + > > > +#define TLA2024_CONF 0x01 > > > +#define TLA2024_CONF_OS_MASK BIT(15) > > > +#define TLA2024_CONF_OS_SHIFT 15 > > > +#define TLA2024_CONF_MUX_MASK GENMASK(14, 12) > > > +#define TLA2024_CONF_MUX_SHIFT 12 > > > +#define TLA2024_CONF_PGA_MASK GENMASK(11, 9) > > > +#define TLA2024_CONF_PGA_SHIFT 9 > > > +#define TLA2024_CONF_MODE_MASK BIT(8) > > > +#define TLA2024_CONF_MODE_SHIFT 8 > > > +#define TLA2024_CONF_DR_MASK GENMASK(7, 5) > > > +#define TLA2024_CONF_DR_SHIFT 5 > > > + > > > +#define TLA2024_CONV_RETRY 10 > > > + > > > +struct tla202x_model { > > > + unsigned int mux_available; > > > + unsigned int pga_available; > > > +}; > > > + > > > +struct tla2024 { > > > + struct i2c_client *i2c; > > > + struct tla202x_model *model; > > > + struct mutex lock; /* protect channel selection */ > > > + u8 used_mux_channels; > > > +}; > > > + > > > +struct tla2024_channel { > > > + int ainp; > > > + int ainn; > > > + const char *datasheet_name; > > > + bool differential; > > > +}; > > > + > > > +static const struct tla2024_channel tla2024_all_channels[] = { > > > + {0, 1, "AIN0-AIN1", 1}, > > > + {0, 3, "AIN0-AIN3", 1}, > > > + {1, 3, "AIN1-AIN3", 1}, > > > + {2, 3, "AIN2-AIN3", 1}, > > > + {0, -1, "AIN0-GND", 0}, > > > > Single ended, so normal convention would just be AIN0 > > > > > + {1, -1, "AIN1-GND", 0}, > > > + {2, -1, "AIN2-GND", 0}, > > > + {3, -1, "AIN3-GND", 0}, > > > +}; > > > + > > > +static int tla2024_find_chan_idx(int ainp_in, int ainn_in, u16 *idx) > > > +{ > > > + u16 i; > > > + > > > + for (i = 0; i < ARRAY_SIZE(tla2024_all_channels); i++) { > > > + if ((tla2024_all_channels[i].ainp == ainp_in) && > > > + (tla2024_all_channels[i].ainn == ainn_in)) { > > > + *idx = i; > > > + return 0; > > > + } > > > + } > > > + > > > + return -EINVAL; > > > +} > > > + > > > +#define TLA202x_MODEL(_mux, _pga) \ > > > + { \ > > > + .mux_available = (_mux), \ > > > + .pga_available = (_pga), \ > > > + } > > > + > > > +enum tla2024_model_id { > > > + TLA2024 = 0, > > > + TLA2022 = 1, > > > + TLA2021 = 2, > > > +}; > > > + > > > +static struct tla202x_model tla202x_models[] = { > > > + TLA202x_MODEL(1, 1), // TLA2024 > > [TLA2024] = TLA202x_MODEL(1, 1), > > > > Makes it clear which is which and avoids the ugly c++ comments. > > > > > + TLA202x_MODEL(0, 1), // TLA2022 > > > + TLA202x_MODEL(0, 0), // TLA2021 > > > +}; > > > + > > > +static const int tla2024_scale[] = { /* scale, int plus micro */ > > > + 3, 0, 2, 0, 1, 0, 0, 500000, 0, 250000, 0, 125000 }; > > > + > > > +static int tla2024_get(struct tla2024 *priv, u8 addr, u16 mask, > > > + u16 shift, u16 *val) > > > +{ > > > + int ret; > > > + u16 data; > > > + > > > + ret = i2c_smbus_read_word_swapped(priv->i2c, addr); > > > + if (ret < 0) > > > + return ret; > > > + > > > + data = ret; > > > + *val = (mask & data) >> shift; > > I'm going to guess that your shift is always just that needed > > to move the mask to 0? As such we have the really neat > > FIELD_GET macros to avoid the need to pass both shifts and > > masks around (it computes the shift from the mask). > > > > yes but my understanding was that FIELD_GET/FIELD_PREP > require the "mask" to be a compile-time constant, so I could not > use them in the somewhat generic get/set. Ah good point. oops. > > I could still use them if I just remove the get/set. Then I could use > i2c read/write and apply FIELD_GET/PREP on the output/input with > a context-specific static mask. Is this the preferred way? Sorry if I > am missing something completely... Certainly there isn't a lot of point in the get function as it's a very light weight wrapper. Set however, is less clear so just leave things as they are. Sorry for the confusing comments. Jonathan > > > > + > > > + return 0; > > > +} > > > + > > > +static int tla2024_set(struct tla2024 *priv, u8 addr, u16 mask, > > > + u16 shift, u16 val) > > > +{ > > > + int ret; > > > + u16 data; > > > + > > > + ret = i2c_smbus_read_word_swapped(priv->i2c, addr); > > > + if (ret < 0) > > > + return ret; > > > + > > > + data = ret; > > > + data &= ~mask; > > > + data |= mask & (val << shift); > > > + > > > + ret = i2c_smbus_write_word_swapped(priv->i2c, addr, data); > > > + > > > + return ret; > > > +} > > > + > > > +static int tla2024_read_avail(struct iio_dev *idev, > > > + struct iio_chan_spec const *chan, > > > + const int **vals, int *type, int *length, > > > + long mask) > > > +{ > > > + switch (mask) { > > > + case IIO_CHAN_INFO_SCALE: > > > + > > > + *length = ARRAY_SIZE(tla2024_scale); > > > + *vals = tla2024_scale; > > > + > > > + *type = IIO_VAL_INT_PLUS_MICRO; > > > + return IIO_AVAIL_LIST; > > > + } > > > + > > > + return -EINVAL; > > > +} > > > + > > > +static int tla2024_of_find_chan(struct tla2024 *priv, struct device_node *ch) > > > +{ > > > + u16 chan_idx = 0; > > > + u32 tmp_p, tmp_n; > > > + int ainp, ainn; > > > + int ret; > > > + > > > + ret = of_property_read_u32_index(ch, "single-channel", 0, &tmp_p); > > > > This property isn't currently in the dt docs (I think...) > > Hence the generic binding doc needs amending. > > > > > + if (ret) { > > > + ret = of_property_read_u32_index(ch, > > > + "diff-channels", 0, &tmp_p); > > > + if (ret) > > > + return ret; > > > + > > > + ret = of_property_read_u32_index(ch, > > > + "diff-channels", 1, &tmp_n); > > > + if (ret) > > > + return ret; > > > + > > > + ainp = (int)tmp_p; > > > + ainn = (int)tmp_n; > > > + } else { > > > + ainp = (int)tmp_p; > > > + ainn = -1; > > > > Given this value is 'magic' anyway just use MAX_UINT instead and avoid > > the need for negative handling? That gets rid of the need for tmp_p, tmp_n > > above. > > > > > + } > > > + > > > + ret = tla2024_find_chan_idx(ainp, ainn, &chan_idx); > > > + if (ret < 0) > > > + return ret; > > > + > > > + /* if model doesn"t have mux then only channel 0 is allowed */ > > > + if (!priv->model->mux_available && chan_idx) > > > + return -EINVAL; > > > + > > > + /* if already used */ > > > + if ((priv->used_mux_channels) & (1 << chan_idx)) > > > + return -EINVAL; > > > + > > > + return chan_idx; > > > +} > > > + > > > +static int tla2024_init_chan(struct iio_dev *idev, struct device_node *node, > > > + struct iio_chan_spec *chan) > > > +{ > > > + struct tla2024 *priv = iio_priv(idev); > > > + u16 chan_idx; > > > + int ret; > > > + > > > + ret = tla2024_of_find_chan(priv, node); > > > + if (ret < 0) > > > + return ret; > > > + > > > + chan_idx = ret; > > > + priv->used_mux_channels |= BIT(chan_idx); > > > + chan->type = IIO_VOLTAGE; > > > + chan->channel = tla2024_all_channels[chan_idx].ainp; > > > + chan->channel2 = tla2024_all_channels[chan_idx].ainn; > > > + chan->differential = tla2024_all_channels[chan_idx].differential; > > > + chan->extend_name = node->name; > > This is a bad idea. You've just created a device specific userspace ABI > > so all generic tools cannot be used. What is the intended purpose of > > doing this? > > > > > + chan->datasheet_name = tla2024_all_channels[chan_idx].datasheet_name; > > > + chan->indexed = 1; > > > + chan->info_mask_separate = BIT(IIO_CHAN_INFO_RAW); > > > + chan->info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE); > > > + chan->info_mask_shared_by_all_available = BIT(IIO_CHAN_INFO_SCALE); > > > + > > > + return 0; > > > +} > > > + > > > +static int tla2024_wait(struct tla2024 *priv) > > > +{ > > > + int ret; > > > + unsigned int retry = TLA2024_CONV_RETRY; > > > + u16 status; > > > + > > > + do { > > > + if (!--retry) > > > + return -EIO; > > > + ret = tla2024_get(priv, TLA2024_CONF, TLA2024_CONF_OS_MASK, > > > + TLA2024_CONF_OS_SHIFT, &status); > > > + if (ret < 0) > > > + return ret; > > > + if (!status) > > > + usleep_range(25, 1000); > > > + } while (!status); > > > + > > > + return ret; > > > +} > > > + > > > +static int tla2024_select_channel(struct tla2024 *priv, > > > + struct iio_chan_spec const *chan) > > > +{ > > > + int ret; > > > + int ainp = chan->channel; > > > + int ainn = chan->channel2; > > > + u16 chan_id = 0; > > > + > > > + ret = tla2024_find_chan_idx(ainp, ainn, &chan_id); > > > + if (ret < 0) > > > + return ret; > > > + > > > + return tla2024_set(priv, TLA2024_CONF, TLA2024_CONF_MUX_MASK, > > > + TLA2024_CONF_MUX_SHIFT, chan_id); > > > +} > > > + > > > +static int tla2024_convert(struct tla2024 *priv) > > > +{ > > > + int ret = 0; > > > + > > > + ret = tla2024_set(priv, TLA2024_CONF, TLA2024_CONF_OS_MASK, > > > + TLA2024_CONF_OS_SHIFT, 1); > > > + if (ret < 0) > > > + return ret; > > > + > > > + return tla2024_wait(priv); > > > +} > > > + > > > +static int tla2024_read_raw(struct iio_dev *idev, > > > + struct iio_chan_spec const *channel, int *val, > > > + int *val2, long mask) > > > +{ > > > + struct tla2024 *priv = iio_priv(idev); > > > + int ret; > > > + u16 data; > > > + s16 tmp; > > > + > > > + switch (mask) { > > > + case IIO_CHAN_INFO_RAW: > > > + mutex_lock(&priv->lock); > > > + ret = tla2024_select_channel(priv, channel); > > > + if (ret < 0) > > > + goto return_unlock; > > > + > > > + ret = tla2024_convert(priv); > > > + if (ret < 0) > > > + goto return_unlock; > > > + > > > + ret = tla2024_get(priv, TLA2024_DATA, TLA2024_DATA_RES_MASK, > > > + TLA2024_DATA_RES_SHIFT, &data); > > > + if (ret < 0) > > > + goto return_unlock; > > > + > > > + tmp = (s16)(data << TLA2024_DATA_RES_SHIFT); > > > + *val = tmp >> TLA2024_DATA_RES_SHIFT; > > > + ret = IIO_VAL_INT; > > > + > > The moment we get a deeply indented goto target like this it > > always suggests to me that we should consider pulling the code > > being protected by the lock out to a separate function, leaving > > the lock external so you can have a simple > > > > lock > > ret = functioncall > > unlock > > if (ret)... or just return ret; > > > > > +return_unlock: > > > + mutex_unlock(&priv->lock); > > > + return ret; > > > + > > > + case IIO_CHAN_INFO_SCALE: > > > + if (!(priv->model->pga_available)) { > > > + *val = 1; /* Scale always 1 mV when no PGA */ > > > + return IIO_VAL_INT; > > > + } > > > + ret = tla2024_get(priv, TLA2024_CONF, TLA2024_CONF_PGA_MASK, > > > + TLA2024_CONF_PGA_SHIFT, &data); > > > + if (ret < 0) > > > + return ret; > > > + > > > + /* gain for the 3bit pga values 6 and 7 is same as at 5 */ > > > + if (data >= (ARRAY_SIZE(tla2024_scale) >> 1)) > > > + data = (ARRAY_SIZE(tla2024_scale) >> 1) - 1; > > > + > > > + *val = tla2024_scale[data << 1]; > > > + *val2 = tla2024_scale[(data << 1) + 1]; > > > + return IIO_VAL_INT_PLUS_MICRO; > > > + > > > + default: > > > + return -EINVAL; > > > + } > > > +} > > > + > > > +static int tla2024_write_raw(struct iio_dev *idev, > > > + struct iio_chan_spec const *chan, > > > + int val, int val2, long mask) > > > +{ > > > + struct tla2024 *priv = iio_priv(idev); > > > + int i; > > > + > > > + switch (mask) { > > > + case IIO_CHAN_INFO_SCALE: > > > + if (!(priv->model->pga_available)) > > > + return -EINVAL; /* scale can't be changed if no pga */ > > > + > > > + for (i = 0; i < ARRAY_SIZE(tla2024_scale); i = i + 2) { > > > + if ((tla2024_scale[i] == val) && > > > + (tla2024_scale[i + 1] == val2)) > > > + break; > > > + } > > > + > > > + if (i == ARRAY_SIZE(tla2024_scale)) > > > + return -EINVAL; > > > + > > > > This need a lock I think... > > > > > + return tla2024_set(priv, TLA2024_CONF, TLA2024_CONF_PGA_MASK, > > > ++ TLA2024_CONF_PGA_SHIFT, i >> 1); > > > + > > > + default: > > > + break; > > > + } > > > + > > > + return -EINVAL; > > > +} > > > + > > > +static int tla2024_of_chan_init(struct iio_dev *idev) > > > +{ > > > + struct device_node *node = idev->dev.of_node; > > > + struct device_node *child; > > > + struct iio_chan_spec *channels; > > > + int ret, i, num_channels; > > > > > + > > > + num_channels = of_get_available_child_count(node); > > > + if (!num_channels) { > > > + dev_err(&idev->dev, "no channels configured\n"); > > > + return -ENODEV; > > > + } > > > + > > > + channels = devm_kcalloc(&idev->dev, num_channels, > > > + sizeof(struct iio_chan_spec), GFP_KERNEL); > > > + if (!channels) > > > + return -ENOMEM; > > > + > > > + i = 0; > > > + for_each_available_child_of_node(node, child) { > > > + ret = tla2024_init_chan(idev, child, &channels[i]); > > > + if (ret) { > > > + of_node_put(child); > > > + return ret; > > > + } > > > + i++; > > > + } > > > + > > > + idev->channels = channels; > > > + idev->num_channels = num_channels; > > > + > > > + return 0; > > > +} > > > + > > > +static const struct iio_info tla2024_info = { > > > + .read_raw = tla2024_read_raw, > > > + .write_raw = tla2024_write_raw, > > > + .read_avail = tla2024_read_avail, > > > +}; > > > + > > > +static int tla2024_probe(struct i2c_client *client, > > > + const struct i2c_device_id *id) > > > +{ > > > + struct iio_dev *iio; > > > + struct tla2024 *priv; > > > + struct tla202x_model *model; > > > + int ret; > > > + > > > + if (!i2c_check_functionality(client->adapter, > > > + I2C_FUNC_SMBUS_WORD_DATA)) > > > + return -EOPNOTSUPP; > > > + > > > + model = &tla202x_models[id->driver_data]; > > > > What is the point in this local variable? I would just > > assign this directly below. > > > > > + > > > + iio = devm_iio_device_alloc(&client->dev, sizeof(*priv)); > > > + if (!iio) > > > + return -ENOMEM; > > > + > > > + priv = iio_priv(iio); > > > + priv->i2c = client; > > > + priv->model = model; > > > + mutex_init(&priv->lock); > > > + > > > + iio->dev.parent = &client->dev; > > > + iio->dev.of_node = client->dev.of_node; > > > + iio->name = client->name; > > > + iio->modes = INDIO_DIRECT_MODE; > > > + iio->info = &tla2024_info; > > > + > > > + ret = tla2024_of_chan_init(iio); > > > + if (ret < 0) > > > + return ret; > > > + > > > + ret = tla2024_set(priv, TLA2024_CONF, TLA2024_CONF_MODE_MASK, > > > + TLA2024_CONF_MODE_SHIFT, 1); > > > + if (ret < 0) > > > + return ret; > > > + > > > + return devm_iio_device_register(&client->dev, iio); > > > +} > > > + > > > +static const struct i2c_device_id tla2024_id[] = { > > > + { "tla2024", TLA2024 }, > > > + { "tla2022", TLA2022 }, > > > + { "tla2021", TLA2021 }, > > > > Really minor point, but reverse numerical order is 'unusual'.. > > > > > + { } > > > +}; > > > +MODULE_DEVICE_TABLE(i2c, tla2024_id); > > > + > > > +static const struct of_device_id tla2024_of_match[] = { > > > + { .compatible = "ti,tla2024" }, > > > + { .compatible = "ti,tla2022" }, > > > + { .compatible = "ti,tla2021" }, > > > + { } > > > +}; > > > +MODULE_DEVICE_TABLE(of, tla2024_of_match); > > > + > > > +static struct i2c_driver tla2024_driver = { > > > + .driver = { > > > + .name = "tla2024", > > > + .of_match_table = of_match_ptr(tla2024_of_match), > > > + }, > > > + .probe = tla2024_probe, > > > + .id_table = tla2024_id, > > > +}; > > > +module_i2c_driver(tla2024_driver); > > > + > > > +MODULE_AUTHOR("Ibtsam Haq <ibtsam.haq@xxxxxxxxxxx>"); > > > +MODULE_DESCRIPTION("Texas Instruments TLA2021/TLA2022/TLA2024 ADC driver"); > > > +MODULE_LICENSE("GPL v2"); > >