2016-03-31 17:31:53

by Crestez Dan Leonard

[permalink] [raw]
Subject: [PATCH 0/2] Add support for adc101c* and adc121c*

This adds support for adc101c* and adc121c* using the ti-adc081c driver.

I send just the first patch earlier but got no response. The first patch now no
longer sets .data in of_device_id because that was not actually used.

The second patch adds buffer support based on external trigger.

Crestez Dan Leonard (2):
ti-adc081c: Add support for adc101c* and adc121c*
ti-adc081c: Initial triggered buffer support

drivers/iio/adc/Kconfig | 6 +--
drivers/iio/adc/ti-adc081c.c | 116 ++++++++++++++++++++++++++++++++++++++-----
2 files changed, 106 insertions(+), 16 deletions(-)

--
2.8.0.rc3


2016-03-31 17:21:19

by Crestez Dan Leonard

[permalink] [raw]
Subject: [PATCH 2/2] ti-adc081c: Initial triggered buffer support

Using this requires software triggers like CONFIG_IIO_HRTIMER_TRIGGER.

The device can be configured to do internal periodic sampling but does
not appear to offer some sort of interrupt on data ready. It only offers
interrupts on values out of a specific range.

Signed-off-by: Crestez Dan Leonard <[email protected]>
---
drivers/iio/adc/ti-adc081c.c | 99 +++++++++++++++++++++++++++++++++++++-------
1 file changed, 83 insertions(+), 16 deletions(-)

diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index 9b2f26f..040e2aa 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -24,6 +24,9 @@
#include <linux/of.h>

#include <linux/iio/iio.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/trigger_consumer.h>
+#include <linux/iio/triggered_buffer.h>
#include <linux/regulator/consumer.h>

struct adc081c {
@@ -69,27 +72,83 @@ static int adc081c_read_raw(struct iio_dev *iio,
return -EINVAL;
}

-static const struct iio_chan_spec adc081c_channel = {
- .type = IIO_VOLTAGE,
- .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
- .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
-};
+static irqreturn_t adc081c_trigger_handler(int irq, void *p)
+{
+ struct iio_poll_func *pf = p;
+ struct iio_dev *indio_dev = pf->indio_dev;
+ struct adc081c *data = iio_priv(indio_dev);
+ s64 ts;
+ u16 buf[8];
+ int ret;
+
+ /* Otherwise iio_push_to_buffers will corrupt the stack. */
+ if (indio_dev->scan_bytes > sizeof(buf)) {
+ dev_crit_once(&indio_dev->dev, "Bad iio_scan_bytes=%d > %d\n",
+ indio_dev->scan_bytes, (int)sizeof(buf));
+ goto out;
+ }
+
+ ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
+ ts = iio_get_time_ns();
+ if (ret < 0)
+ goto out;
+ buf[0] = ret;
+ iio_push_to_buffers_with_timestamp(indio_dev, buf, ts);
+out:
+ iio_trigger_notify_done(indio_dev->trig);
+ return IRQ_HANDLED;
+}

static const struct iio_info adc081c_info = {
.read_raw = adc081c_read_raw,
.driver_module = THIS_MODULE,
};

+struct adcxx1c_model {
+ int bits;
+ const struct iio_chan_spec* channels;
+};
+
+#define ADCxx1C_CHAN(_bits) { \
+ .type = IIO_VOLTAGE, \
+ .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
+ .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
+ .scan_type = { \
+ .sign = 'u', \
+ .realbits = (_bits), \
+ .storagebits = 16, \
+ .shift = 12 - (_bits), \
+ .endianness = IIO_CPU, \
+ }, \
+}
+
+#define DEFINE_ADCxx1C_MODEL(_name, _bits) \
+ static const struct iio_chan_spec _name ## _channels[] = { \
+ ADCxx1C_CHAN((_bits)), \
+ IIO_CHAN_SOFT_TIMESTAMP(1), \
+ }; \
+ static const struct adcxx1c_model _name ## _model = { \
+ .bits = (_bits), \
+ .channels = _name ## _channels, \
+ }
+
+DEFINE_ADCxx1C_MODEL(adc081c, 8);
+DEFINE_ADCxx1C_MODEL(adc101c, 10);
+DEFINE_ADCxx1C_MODEL(adc121c, 12);
+
+struct adcxx1c_info {
+ int bits;
+ const struct adc081c_channels* channels;
+};
+
static int adc081c_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct iio_dev *iio;
struct adc081c *adc;
+ struct adcxx1c_model *model = (struct adcxx1c_model*)id->driver_data;
int err;

- if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
- return -EINVAL;
-
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;

@@ -99,7 +158,7 @@ static int adc081c_probe(struct i2c_client *client,

adc = iio_priv(iio);
adc->i2c = client;
- adc->bits = id->driver_data;
+ adc->bits = model->bits;

adc->ref = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(adc->ref))
@@ -114,18 +173,26 @@ static int adc081c_probe(struct i2c_client *client,
iio->modes = INDIO_DIRECT_MODE;
iio->info = &adc081c_info;

- iio->channels = &adc081c_channel;
- iio->num_channels = 1;
+ iio->channels = model->channels;
+ iio->num_channels = 2;
+
+ err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL);
+ if (err < 0) {
+ dev_err(&client->dev, "iio triggered buffer setup failed\n");
+ goto err_regulator_disable;
+ }

err = iio_device_register(iio);
if (err < 0)
- goto regulator_disable;
+ goto err_buffer_cleanup;

i2c_set_clientdata(client, iio);

return 0;

-regulator_disable:
+err_buffer_cleanup:
+ iio_triggered_buffer_cleanup(iio);
+err_regulator_disable:
regulator_disable(adc->ref);

return err;
@@ -143,9 +210,9 @@ static int adc081c_remove(struct i2c_client *client)
}

static const struct i2c_device_id adc081c_id[] = {
- { "adc081c", 8 },
- { "adc101c", 10 },
- { "adc121c", 12 },
+ { "adc081c", (long)&adc081c_model },
+ { "adc101c", (long)&adc101c_model },
+ { "adc121c", (long)&adc121c_model },
{ }
};
MODULE_DEVICE_TABLE(i2c, adc081c_id);
--
2.8.0.rc3

2016-03-31 17:21:18

by Crestez Dan Leonard

[permalink] [raw]
Subject: [PATCH 1/2] ti-adc081c: Add support for adc101c* and adc121c*

These chips has an almost identical interface but a deal with a
different number of value bits. Datasheet links for comparison:

* http://www.ti.com/lit/ds/symlink/adc081c021.pdf
* http://www.ti.com/lit/ds/symlink/adc101c021.pdf
* http://www.ti.com/lit/ds/symlink/adc121c021.pdf

Signed-off-by: Crestez Dan Leonard <[email protected]>
---
drivers/iio/adc/Kconfig | 6 +++---
drivers/iio/adc/ti-adc081c.c | 31 +++++++++++++++++++++++++++----
2 files changed, 30 insertions(+), 7 deletions(-)

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 9ddcd5d..a2d0db5 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -378,11 +378,11 @@ config ROCKCHIP_SARADC
module will be called rockchip_saradc.

config TI_ADC081C
- tristate "Texas Instruments ADC081C021/027"
+ tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
depends on I2C
help
- If you say yes here you get support for Texas Instruments ADC081C021
- and ADC081C027 ADC chips.
+ If you say yes here you get support for Texas Instruments ADC081C,
+ ADC101C and ADC121C ADC chips.

This driver can also be built as a module. If so, the module will be
called ti-adc081c.
diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
index ecbc121..9b2f26f 100644
--- a/drivers/iio/adc/ti-adc081c.c
+++ b/drivers/iio/adc/ti-adc081c.c
@@ -1,9 +1,21 @@
/*
+ * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
+ *
* Copyright (C) 2012 Avionic Design GmbH
+ * Copyright (C) 2016 Intel
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
+ *
+ * Datasheets:
+ * http://www.ti.com/lit/ds/symlink/adc081c021.pdf
+ * http://www.ti.com/lit/ds/symlink/adc101c021.pdf
+ * http://www.ti.com/lit/ds/symlink/adc121c021.pdf
+ *
+ * The devices have a very similar interface and differ mostly in the number of
+ * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
+ * bits of value registers are reserved.
*/

#include <linux/err.h>
@@ -17,6 +29,9 @@
struct adc081c {
struct i2c_client *i2c;
struct regulator *ref;
+
+ /* 8, 10 or 12 */
+ int bits;
};

#define REG_CONV_RES 0x00
@@ -34,7 +49,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
if (err < 0)
return err;

- *value = (err >> 4) & 0xff;
+ *value = (err & 0xFFF) >> (12 - adc->bits);
return IIO_VAL_INT;

case IIO_CHAN_INFO_SCALE:
@@ -43,7 +58,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
return err;

*value = err / 1000;
- *shift = 8;
+ *shift = adc->bits;

return IIO_VAL_FRACTIONAL_LOG2;

@@ -72,6 +87,9 @@ static int adc081c_probe(struct i2c_client *client,
struct adc081c *adc;
int err;

+ if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
+ return -EINVAL;
+
if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
return -EOPNOTSUPP;

@@ -81,6 +99,7 @@ static int adc081c_probe(struct i2c_client *client,

adc = iio_priv(iio);
adc->i2c = client;
+ adc->bits = id->driver_data;

adc->ref = devm_regulator_get(&client->dev, "vref");
if (IS_ERR(adc->ref))
@@ -124,7 +143,9 @@ static int adc081c_remove(struct i2c_client *client)
}

static const struct i2c_device_id adc081c_id[] = {
- { "adc081c", 0 },
+ { "adc081c", 8 },
+ { "adc101c", 10 },
+ { "adc121c", 12 },
{ }
};
MODULE_DEVICE_TABLE(i2c, adc081c_id);
@@ -132,6 +153,8 @@ MODULE_DEVICE_TABLE(i2c, adc081c_id);
#ifdef CONFIG_OF
static const struct of_device_id adc081c_of_match[] = {
{ .compatible = "ti,adc081c" },
+ { .compatible = "ti,adc101c" },
+ { .compatible = "ti,adc121c" },
{ }
};
MODULE_DEVICE_TABLE(of, adc081c_of_match);
@@ -149,5 +172,5 @@ static struct i2c_driver adc081c_driver = {
module_i2c_driver(adc081c_driver);

MODULE_AUTHOR("Thierry Reding <[email protected]>");
-MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
+MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
MODULE_LICENSE("GPL v2");
--
2.8.0.rc3

2016-04-01 08:09:18

by Crt Mori

[permalink] [raw]
Subject: Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support

On 31 March 2016 at 19:20, Crestez Dan Leonard
<[email protected]> wrote:
> Using this requires software triggers like CONFIG_IIO_HRTIMER_TRIGGER.
Then we are missing DEPENDS in Kconfig...

>
> The device can be configured to do internal periodic sampling but does
> not appear to offer some sort of interrupt on data ready. It only offers
> interrupts on values out of a specific range.
>
> Signed-off-by: Crestez Dan Leonard <[email protected]>
> ---
> drivers/iio/adc/ti-adc081c.c | 99 +++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 83 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
> index 9b2f26f..040e2aa 100644
> --- a/drivers/iio/adc/ti-adc081c.c
> +++ b/drivers/iio/adc/ti-adc081c.c
> @@ -24,6 +24,9 @@
> #include <linux/of.h>
>
> #include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> #include <linux/regulator/consumer.h>
>
> struct adc081c {
> @@ -69,27 +72,83 @@ static int adc081c_read_raw(struct iio_dev *iio,
> return -EINVAL;
> }
>
> -static const struct iio_chan_spec adc081c_channel = {
> - .type = IIO_VOLTAGE,
> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> -};
> +static irqreturn_t adc081c_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct adc081c *data = iio_priv(indio_dev);
> + s64 ts;
> + u16 buf[8];
> + int ret;
> +
> + /* Otherwise iio_push_to_buffers will corrupt the stack. */
> + if (indio_dev->scan_bytes > sizeof(buf)) {
> + dev_crit_once(&indio_dev->dev, "Bad iio_scan_bytes=%d > %d\n",
> + indio_dev->scan_bytes, (int)sizeof(buf));
> + goto out;
> + }
> +
> + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
> + ts = iio_get_time_ns();
> + if (ret < 0)
> + goto out;
> + buf[0] = ret;
> + iio_push_to_buffers_with_timestamp(indio_dev, buf, ts);
> +out:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
>
> static const struct iio_info adc081c_info = {
> .read_raw = adc081c_read_raw,
> .driver_module = THIS_MODULE,
> };
>
> +struct adcxx1c_model {
> + int bits;
> + const struct iio_chan_spec* channels;
> +};
> +
> +#define ADCxx1C_CHAN(_bits) { \
> + .type = IIO_VOLTAGE, \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = (_bits), \
> + .storagebits = 16, \
> + .shift = 12 - (_bits), \
> + .endianness = IIO_CPU, \
> + }, \
> +}
> +
> +#define DEFINE_ADCxx1C_MODEL(_name, _bits) \
> + static const struct iio_chan_spec _name ## _channels[] = { \
> + ADCxx1C_CHAN((_bits)), \
> + IIO_CHAN_SOFT_TIMESTAMP(1), \
> + }; \
> + static const struct adcxx1c_model _name ## _model = { \
> + .bits = (_bits), \
> + .channels = _name ## _channels, \
> + }
> +
> +DEFINE_ADCxx1C_MODEL(adc081c, 8);
> +DEFINE_ADCxx1C_MODEL(adc101c, 10);
> +DEFINE_ADCxx1C_MODEL(adc121c, 12);
> +
> +struct adcxx1c_info {
> + int bits;
> + const struct adc081c_channels* channels;
> +};
> +
> static int adc081c_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> struct iio_dev *iio;
> struct adc081c *adc;
> + struct adcxx1c_model *model = (struct adcxx1c_model*)id->driver_data;
> int err;
>
> - if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
> - return -EINVAL;
> -
> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
> return -EOPNOTSUPP;
>
> @@ -99,7 +158,7 @@ static int adc081c_probe(struct i2c_client *client,
>
> adc = iio_priv(iio);
> adc->i2c = client;
> - adc->bits = id->driver_data;
> + adc->bits = model->bits;
>
> adc->ref = devm_regulator_get(&client->dev, "vref");
> if (IS_ERR(adc->ref))
> @@ -114,18 +173,26 @@ static int adc081c_probe(struct i2c_client *client,
> iio->modes = INDIO_DIRECT_MODE;
> iio->info = &adc081c_info;
>
> - iio->channels = &adc081c_channel;
> - iio->num_channels = 1;
> + iio->channels = model->channels;
> + iio->num_channels = 2;
> +
> + err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL);
> + if (err < 0) {
> + dev_err(&client->dev, "iio triggered buffer setup failed\n");
> + goto err_regulator_disable;
> + }
>
> err = iio_device_register(iio);
> if (err < 0)
> - goto regulator_disable;
> + goto err_buffer_cleanup;
>
> i2c_set_clientdata(client, iio);
>
> return 0;
>
> -regulator_disable:
> +err_buffer_cleanup:
> + iio_triggered_buffer_cleanup(iio);
> +err_regulator_disable:
> regulator_disable(adc->ref);
>
> return err;
> @@ -143,9 +210,9 @@ static int adc081c_remove(struct i2c_client *client)
> }
>
> static const struct i2c_device_id adc081c_id[] = {
> - { "adc081c", 8 },
> - { "adc101c", 10 },
> - { "adc121c", 12 },
> + { "adc081c", (long)&adc081c_model },
> + { "adc101c", (long)&adc101c_model },
> + { "adc121c", (long)&adc121c_model },
> { }
> };
> MODULE_DEVICE_TABLE(i2c, adc081c_id);
> --
> 2.8.0.rc3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html

2016-04-01 08:17:29

by Peter Meerwald-Stadler

[permalink] [raw]
Subject: Re: [PATCH 1/2] ti-adc081c: Add support for adc101c* and adc121c*


> These chips has an almost identical interface but a deal with a
> different number of value bits. Datasheet links for comparison:

comments below

> * http://www.ti.com/lit/ds/symlink/adc081c021.pdf
> * http://www.ti.com/lit/ds/symlink/adc101c021.pdf
> * http://www.ti.com/lit/ds/symlink/adc121c021.pdf
>
> Signed-off-by: Crestez Dan Leonard <[email protected]>
> ---
> drivers/iio/adc/Kconfig | 6 +++---
> drivers/iio/adc/ti-adc081c.c | 31 +++++++++++++++++++++++++++----
> 2 files changed, 30 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 9ddcd5d..a2d0db5 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -378,11 +378,11 @@ config ROCKCHIP_SARADC
> module will be called rockchip_saradc.
>
> config TI_ADC081C
> - tristate "Texas Instruments ADC081C021/027"
> + tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
> depends on I2C
> help
> - If you say yes here you get support for Texas Instruments ADC081C021
> - and ADC081C027 ADC chips.
> + If you say yes here you get support for Texas Instruments ADC081C,
> + ADC101C and ADC121C ADC chips.
>
> This driver can also be built as a module. If so, the module will be
> called ti-adc081c.
> diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
> index ecbc121..9b2f26f 100644
> --- a/drivers/iio/adc/ti-adc081c.c
> +++ b/drivers/iio/adc/ti-adc081c.c
> @@ -1,9 +1,21 @@
> /*
> + * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
> + *
> * Copyright (C) 2012 Avionic Design GmbH
> + * Copyright (C) 2016 Intel
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 as
> * published by the Free Software Foundation.
> + *
> + * Datasheets:
> + * http://www.ti.com/lit/ds/symlink/adc081c021.pdf
> + * http://www.ti.com/lit/ds/symlink/adc101c021.pdf
> + * http://www.ti.com/lit/ds/symlink/adc121c021.pdf
> + *
> + * The devices have a very similar interface and differ mostly in the number of
> + * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
> + * bits of value registers are reserved.
> */
>
> #include <linux/err.h>
> @@ -17,6 +29,9 @@
> struct adc081c {
> struct i2c_client *i2c;
> struct regulator *ref;
> +
> + /* 8, 10 or 12 */
> + int bits;
> };
>
> #define REG_CONV_RES 0x00
> @@ -34,7 +49,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
> if (err < 0)
> return err;
>
> - *value = (err >> 4) & 0xff;
> + *value = (err & 0xFFF) >> (12 - adc->bits);
> return IIO_VAL_INT;
>
> case IIO_CHAN_INFO_SCALE:
> @@ -43,7 +58,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
> return err;
>
> *value = err / 1000;
> - *shift = 8;
> + *shift = adc->bits;
>
> return IIO_VAL_FRACTIONAL_LOG2;
>
> @@ -72,6 +87,9 @@ static int adc081c_probe(struct i2c_client *client,
> struct adc081c *adc;
> int err;
>
> + if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)

often the id/driverdata is used to point to a table of chip-specific
configuration data rather than using driver_data to store the data itself

the second approach does not scale when more configuration data needs to
be passed (just an observation)

> + return -EINVAL;
> +
> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
> return -EOPNOTSUPP;
>
> @@ -81,6 +99,7 @@ static int adc081c_probe(struct i2c_client *client,
>
> adc = iio_priv(iio);
> adc->i2c = client;
> + adc->bits = id->driver_data;
>
> adc->ref = devm_regulator_get(&client->dev, "vref");
> if (IS_ERR(adc->ref))
> @@ -124,7 +143,9 @@ static int adc081c_remove(struct i2c_client *client)
> }
>
> static const struct i2c_device_id adc081c_id[] = {
> - { "adc081c", 0 },
> + { "adc081c", 8 },
> + { "adc101c", 10 },
> + { "adc121c", 12 },
> { }
> };
> MODULE_DEVICE_TABLE(i2c, adc081c_id);
> @@ -132,6 +153,8 @@ MODULE_DEVICE_TABLE(i2c, adc081c_id);
> #ifdef CONFIG_OF
> static const struct of_device_id adc081c_of_match[] = {
> { .compatible = "ti,adc081c" },
> + { .compatible = "ti,adc101c" },
> + { .compatible = "ti,adc121c" },
> { }
> };
> MODULE_DEVICE_TABLE(of, adc081c_of_match);
> @@ -149,5 +172,5 @@ static struct i2c_driver adc081c_driver = {
> module_i2c_driver(adc081c_driver);
>
> MODULE_AUTHOR("Thierry Reding <[email protected]>");
> -MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
> +MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
> MODULE_LICENSE("GPL v2");
>

--

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

2016-04-01 08:34:35

by Peter Meerwald-Stadler

[permalink] [raw]
Subject: Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support


> Using this requires software triggers like CONFIG_IIO_HRTIMER_TRIGGER.
>
> The device can be configured to do internal periodic sampling but does
> not appear to offer some sort of interrupt on data ready. It only offers
> interrupts on values out of a specific range.
>
> Signed-off-by: Crestez Dan Leonard <[email protected]>
> ---
> drivers/iio/adc/ti-adc081c.c | 99 +++++++++++++++++++++++++++++++++++++-------
> 1 file changed, 83 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
> index 9b2f26f..040e2aa 100644
> --- a/drivers/iio/adc/ti-adc081c.c
> +++ b/drivers/iio/adc/ti-adc081c.c
> @@ -24,6 +24,9 @@
> #include <linux/of.h>
>
> #include <linux/iio/iio.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +#include <linux/iio/triggered_buffer.h>
> #include <linux/regulator/consumer.h>
>
> struct adc081c {
> @@ -69,27 +72,83 @@ static int adc081c_read_raw(struct iio_dev *iio,
> return -EINVAL;
> }
>
> -static const struct iio_chan_spec adc081c_channel = {
> - .type = IIO_VOLTAGE,
> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> -};

the patch would look cleaner/shorter if adc081c_channel won't get moved
around

> +static irqreturn_t adc081c_trigger_handler(int irq, void *p)
> +{
> + struct iio_poll_func *pf = p;
> + struct iio_dev *indio_dev = pf->indio_dev;
> + struct adc081c *data = iio_priv(indio_dev);
> + s64 ts;
> + u16 buf[8];

comment: 2 bytes data + 6 bytes padding + 8 bytes timestamp

> + int ret;
> +
> + /* Otherwise iio_push_to_buffers will corrupt the stack. */
> + if (indio_dev->scan_bytes > sizeof(buf)) {
> + dev_crit_once(&indio_dev->dev, "Bad iio_scan_bytes=%d > %d\n",
> + indio_dev->scan_bytes, (int)sizeof(buf));

rather than casting sizeof(buf), use the correct printf length modifier,
i.e. %z

not sure if this check is needed

> + goto out;
> + }
> +
> + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);

REG_CONV_RES should be called ADC081C_REG_CONV_RES, but that's a separate
issue

> + ts = iio_get_time_ns();

why is the timestamp taken here?, seems strange
often this is done together with iio_push_to_buffers_with_timestamp()

> + if (ret < 0)
> + goto out;
> + buf[0] = ret;
> + iio_push_to_buffers_with_timestamp(indio_dev, buf, ts);
> +out:
> + iio_trigger_notify_done(indio_dev->trig);
> + return IRQ_HANDLED;
> +}
>
> static const struct iio_info adc081c_info = {
> .read_raw = adc081c_read_raw,
> .driver_module = THIS_MODULE,
> };
>
> +struct adcxx1c_model {
> + int bits;
> + const struct iio_chan_spec* channels;
> +};
> +
> +#define ADCxx1C_CHAN(_bits) { \
> + .type = IIO_VOLTAGE, \
> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
> + .scan_type = { \
> + .sign = 'u', \
> + .realbits = (_bits), \
> + .storagebits = 16, \
> + .shift = 12 - (_bits), \
> + .endianness = IIO_CPU, \
> + }, \
> +}
> +
> +#define DEFINE_ADCxx1C_MODEL(_name, _bits) \
> + static const struct iio_chan_spec _name ## _channels[] = { \
> + ADCxx1C_CHAN((_bits)), \
> + IIO_CHAN_SOFT_TIMESTAMP(1), \
> + }; \
> + static const struct adcxx1c_model _name ## _model = { \
> + .bits = (_bits), \
> + .channels = _name ## _channels, \
> + }
> +
> +DEFINE_ADCxx1C_MODEL(adc081c, 8);
> +DEFINE_ADCxx1C_MODEL(adc101c, 10);
> +DEFINE_ADCxx1C_MODEL(adc121c, 12);
> +
> +struct adcxx1c_info {
> + int bits;
> + const struct adc081c_channels* channels;
> +};
> +
> static int adc081c_probe(struct i2c_client *client,
> const struct i2c_device_id *id)
> {
> struct iio_dev *iio;
> struct adc081c *adc;
> + struct adcxx1c_model *model = (struct adcxx1c_model*)id->driver_data;
> int err;
>
> - if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
> - return -EINVAL;
> -
> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
> return -EOPNOTSUPP;
>
> @@ -99,7 +158,7 @@ static int adc081c_probe(struct i2c_client *client,
>
> adc = iio_priv(iio);
> adc->i2c = client;
> - adc->bits = id->driver_data;
> + adc->bits = model->bits;
>
> adc->ref = devm_regulator_get(&client->dev, "vref");
> if (IS_ERR(adc->ref))
> @@ -114,18 +173,26 @@ static int adc081c_probe(struct i2c_client *client,
> iio->modes = INDIO_DIRECT_MODE;
> iio->info = &adc081c_info;
>
> - iio->channels = &adc081c_channel;
> - iio->num_channels = 1;
> + iio->channels = model->channels;
> + iio->num_channels = 2;

the number of channels could go into the adcxx1c_info struct

> +
> + err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL);
> + if (err < 0) {
> + dev_err(&client->dev, "iio triggered buffer setup failed\n");
> + goto err_regulator_disable;
> + }
>
> err = iio_device_register(iio);
> if (err < 0)
> - goto regulator_disable;
> + goto err_buffer_cleanup;
>
> i2c_set_clientdata(client, iio);
>
> return 0;
>
> -regulator_disable:
> +err_buffer_cleanup:
> + iio_triggered_buffer_cleanup(iio);
> +err_regulator_disable:
> regulator_disable(adc->ref);
>
> return err;
> @@ -143,9 +210,9 @@ static int adc081c_remove(struct i2c_client *client)
> }

iio_triggered_buffer_cleanup() in _remove()?


> static const struct i2c_device_id adc081c_id[] = {
> - { "adc081c", 8 },
> - { "adc101c", 10 },
> - { "adc121c", 12 },
> + { "adc081c", (long)&adc081c_model },

often an enum is used instead of a pointer

> + { "adc101c", (long)&adc101c_model },
> + { "adc121c", (long)&adc121c_model },
> { }
> };
> MODULE_DEVICE_TABLE(i2c, adc081c_id);
>

--

Peter Meerwald-Stadler
+43-664-2444418 (mobile)

2016-04-01 09:23:26

by Lars-Peter Clausen

[permalink] [raw]
Subject: Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support

On 04/01/2016 10:08 AM, Crt Mori wrote:
> On 31 March 2016 at 19:20, Crestez Dan Leonard
> <[email protected]> wrote:
>> Using this requires software triggers like CONFIG_IIO_HRTIMER_TRIGGER.
> Then we are missing DEPENDS in Kconfig...

The device could be used with any generic trigger. The device driver
shouldn't make the choice here which one it requires and which one not.

- Lars

2016-04-01 11:14:12

by Crestez Dan Leonard

[permalink] [raw]
Subject: Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support

On 04/01/2016 11:34 AM, Peter Meerwald-Stadler wrote:
>> -static const struct iio_chan_spec adc081c_channel = {
>> - .type = IIO_VOLTAGE,
>> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
>> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>> -};
>
> the patch would look cleaner/shorter if adc081c_channel won't get moved
> around

It was not moved around, it is now defined by a macro. Buffer support
requires defining scan_type which contains a different number of bits.

The macros are now after iio_info adc081c_info instead of before, I can
move that around. I also noticed that I declared both a struct
adcxx1c_model and an unused struct adcxx1c_info. I will remove that.

The first patch in the series doesn't use any per-model data and just
stores the number of bits in driver_data. I can change the series to
introduce adcxx1c_model in the first patch.

>> +static irqreturn_t adc081c_trigger_handler(int irq, void *p)
>> +{
>> + struct iio_poll_func *pf = p;
>> + struct iio_dev *indio_dev = pf->indio_dev;
>> + struct adc081c *data = iio_priv(indio_dev);
>> + s64 ts;
>> + u16 buf[8];
>
> comment: 2 bytes data + 6 bytes padding + 8 bytes timestamp
>
>> + int ret;
>> +
>> + /* Otherwise iio_push_to_buffers will corrupt the stack. */
>> + if (indio_dev->scan_bytes > sizeof(buf)) {
>> + dev_crit_once(&indio_dev->dev, "Bad iio_scan_bytes=%d > %d\n",
>> + indio_dev->scan_bytes, (int)sizeof(buf));
>
> rather than casting sizeof(buf), use the correct printf length modifier,
> i.e. %z
>
> not sure if this check is needed

I guess it's not needed. My first version defined the buffer incorrectly
and caused a messy crash. Calculating manual buffer alignments seems
very fragile.

It seems that C99 variable-length-arrays work fine, something like:
u16 buf[indio_dev->scan_bytes / 2];

Would that be acceptable? It compiles without warnings and there some
other places in the kernel where VLAs are used.

>> + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
>
> REG_CONV_RES should be called ADC081C_REG_CONV_RES, but that's a separate
> issue

Yes, but that would be an entirely unrelated renaming.

>
>> + ts = iio_get_time_ns();
>
> why is the timestamp taken here?, seems strange
> often this is done together with iio_push_to_buffers_with_timestamp()

I wanted to keep it as close to the read as possible. In this case it
doesn't matter.

>> - iio->channels = &adc081c_channel;
>> - iio->num_channels = 1;
>> + iio->channels = model->channels;
>> + iio->num_channels = 2;
>
> the number of channels could go into the adcxx1c_info struct

But it's a constant, it does not vary between devices. I could make an
ADC081C_NUM_CHANNELS define.

--
Regards,
Leonard

2016-04-03 09:16:40

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 1/2] ti-adc081c: Add support for adc101c* and adc121c*

On 31/03/16 18:20, Crestez Dan Leonard wrote:
> These chips has an almost identical interface but a deal with a
> different number of value bits. Datasheet links for comparison:
>
> * http://www.ti.com/lit/ds/symlink/adc081c021.pdf
> * http://www.ti.com/lit/ds/symlink/adc101c021.pdf
> * http://www.ti.com/lit/ds/symlink/adc121c021.pdf
>
> Signed-off-by: Crestez Dan Leonard <[email protected]>
Just one query inline.

J
> ---
> drivers/iio/adc/Kconfig | 6 +++---
> drivers/iio/adc/ti-adc081c.c | 31 +++++++++++++++++++++++++++----
> 2 files changed, 30 insertions(+), 7 deletions(-)
>
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 9ddcd5d..a2d0db5 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -378,11 +378,11 @@ config ROCKCHIP_SARADC
> module will be called rockchip_saradc.
>
> config TI_ADC081C
> - tristate "Texas Instruments ADC081C021/027"
> + tristate "Texas Instruments ADC081C/ADC101C/ADC121C family"
> depends on I2C
> help
> - If you say yes here you get support for Texas Instruments ADC081C021
> - and ADC081C027 ADC chips.
> + If you say yes here you get support for Texas Instruments ADC081C,
> + ADC101C and ADC121C ADC chips.
>
> This driver can also be built as a module. If so, the module will be
> called ti-adc081c.
> diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
> index ecbc121..9b2f26f 100644
> --- a/drivers/iio/adc/ti-adc081c.c
> +++ b/drivers/iio/adc/ti-adc081c.c
> @@ -1,9 +1,21 @@
> /*
> + * TI ADC081C/ADC101C/ADC121C 8/10/12-bit ADC driver
> + *
> * Copyright (C) 2012 Avionic Design GmbH
> + * Copyright (C) 2016 Intel
> *
> * This program is free software; you can redistribute it and/or modify
> * it under the terms of the GNU General Public License version 2 as
> * published by the Free Software Foundation.
> + *
> + * Datasheets:
> + * http://www.ti.com/lit/ds/symlink/adc081c021.pdf
> + * http://www.ti.com/lit/ds/symlink/adc101c021.pdf
> + * http://www.ti.com/lit/ds/symlink/adc121c021.pdf
> + *
> + * The devices have a very similar interface and differ mostly in the number of
> + * bits handled. For the 8-bit and 10-bit models the least-significant 4 or 2
> + * bits of value registers are reserved.
> */
>
> #include <linux/err.h>
> @@ -17,6 +29,9 @@
> struct adc081c {
> struct i2c_client *i2c;
> struct regulator *ref;
> +
> + /* 8, 10 or 12 */
> + int bits;
> };
>
> #define REG_CONV_RES 0x00
> @@ -34,7 +49,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
> if (err < 0)
> return err;
>
> - *value = (err >> 4) & 0xff;
> + *value = (err & 0xFFF) >> (12 - adc->bits);
> return IIO_VAL_INT;
>
> case IIO_CHAN_INFO_SCALE:
> @@ -43,7 +58,7 @@ static int adc081c_read_raw(struct iio_dev *iio,
> return err;
>
> *value = err / 1000;
> - *shift = 8;
> + *shift = adc->bits;
>
> return IIO_VAL_FRACTIONAL_LOG2;
>
> @@ -72,6 +87,9 @@ static int adc081c_probe(struct i2c_client *client,
> struct adc081c *adc;
> int err;
>
> + if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
> + return -EINVAL;
How could this condition occur? The driver won't be probed if we don't have
a match on one of the items in the id table and they all obey this rull?

On Peter's comment, it would be future proofing to use an index into a
information structure array, but I'd prefer that to be added when needed and
am reasonably happy with how you have it here.
> +
> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
> return -EOPNOTSUPP;
>
> @@ -81,6 +99,7 @@ static int adc081c_probe(struct i2c_client *client,
>
> adc = iio_priv(iio);
> adc->i2c = client;
> + adc->bits = id->driver_data;
>
> adc->ref = devm_regulator_get(&client->dev, "vref");
> if (IS_ERR(adc->ref))
> @@ -124,7 +143,9 @@ static int adc081c_remove(struct i2c_client *client)
> }
>
> static const struct i2c_device_id adc081c_id[] = {
> - { "adc081c", 0 },
> + { "adc081c", 8 },
> + { "adc101c", 10 },
> + { "adc121c", 12 },
> { }
> };
> MODULE_DEVICE_TABLE(i2c, adc081c_id);
> @@ -132,6 +153,8 @@ MODULE_DEVICE_TABLE(i2c, adc081c_id);
> #ifdef CONFIG_OF
> static const struct of_device_id adc081c_of_match[] = {
> { .compatible = "ti,adc081c" },
> + { .compatible = "ti,adc101c" },
> + { .compatible = "ti,adc121c" },
> { }
> };
> MODULE_DEVICE_TABLE(of, adc081c_of_match);
> @@ -149,5 +172,5 @@ static struct i2c_driver adc081c_driver = {
> module_i2c_driver(adc081c_driver);
>
> MODULE_AUTHOR("Thierry Reding <[email protected]>");
> -MODULE_DESCRIPTION("Texas Instruments ADC081C021/027 driver");
> +MODULE_DESCRIPTION("Texas Instruments ADC081C/ADC101C/ADC121C driver");
> MODULE_LICENSE("GPL v2");
>

2016-04-03 09:25:08

by Jonathan Cameron

[permalink] [raw]
Subject: Re: [PATCH 2/2] ti-adc081c: Initial triggered buffer support

On 01/04/16 09:34, Peter Meerwald-Stadler wrote:
>
>> Using this requires software triggers like CONFIG_IIO_HRTIMER_TRIGGER.
>>
>> The device can be configured to do internal periodic sampling but does
>> not appear to offer some sort of interrupt on data ready. It only offers
>> interrupts on values out of a specific range.
This isn't uncommon on chips that have some 'alarm' type capability. max1363
would be another example.
>>
>> Signed-off-by: Crestez Dan Leonard <[email protected]>
Mostly good, but a few little points inline to add to Peter's.

Jonathan
>> ---
>> drivers/iio/adc/ti-adc081c.c | 99 +++++++++++++++++++++++++++++++++++++-------
>> 1 file changed, 83 insertions(+), 16 deletions(-)
>>
>> diff --git a/drivers/iio/adc/ti-adc081c.c b/drivers/iio/adc/ti-adc081c.c
>> index 9b2f26f..040e2aa 100644
>> --- a/drivers/iio/adc/ti-adc081c.c
>> +++ b/drivers/iio/adc/ti-adc081c.c
>> @@ -24,6 +24,9 @@
>> #include <linux/of.h>
>>
>> #include <linux/iio/iio.h>
>> +#include <linux/iio/buffer.h>
>> +#include <linux/iio/trigger_consumer.h>
>> +#include <linux/iio/triggered_buffer.h>
>> #include <linux/regulator/consumer.h>
>>
>> struct adc081c {
>> @@ -69,27 +72,83 @@ static int adc081c_read_raw(struct iio_dev *iio,
>> return -EINVAL;
>> }
>>
>> -static const struct iio_chan_spec adc081c_channel = {
>> - .type = IIO_VOLTAGE,
>> - .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),
>> - .info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
>> -};
>
> the patch would look cleaner/shorter if adc081c_channel won't get moved
> around
>
>> +static irqreturn_t adc081c_trigger_handler(int irq, void *p)
>> +{
>> + struct iio_poll_func *pf = p;
>> + struct iio_dev *indio_dev = pf->indio_dev;
>> + struct adc081c *data = iio_priv(indio_dev);
>> + s64 ts;
>> + u16 buf[8];
>
> comment: 2 bytes data + 6 bytes padding + 8 bytes timestamp
>
>> + int ret;
>> +
>> + /* Otherwise iio_push_to_buffers will corrupt the stack. */
>> + if (indio_dev->scan_bytes > sizeof(buf)) {
>> + dev_crit_once(&indio_dev->dev, "Bad iio_scan_bytes=%d > %d\n",
>> + indio_dev->scan_bytes, (int)sizeof(buf));
>
> rather than casting sizeof(buf), use the correct printf length modifier,
> i.e. %z
>
> not sure if this check is needed
>
>> + goto out;
>> + }
>> +
>> + ret = i2c_smbus_read_word_swapped(data->i2c, REG_CONV_RES);
>
> REG_CONV_RES should be called ADC081C_REG_CONV_RES, but that's a separate
> issue
>
>> + ts = iio_get_time_ns();
>
> why is the timestamp taken here?, seems strange
> often this is done together with iio_push_to_buffers_with_timestamp()

Depends on where the trigger is coming from - here this is the correct option
as it is the best guess on when the reading was actually taken, however
the local variable is pointless, just roll this into the place it is
used below.
>
>> + if (ret < 0)
>> + goto out;
>> + buf[0] = ret;
>> + iio_push_to_buffers_with_timestamp(indio_dev, buf, ts);
>> +out:
>> + iio_trigger_notify_done(indio_dev->trig);
>> + return IRQ_HANDLED;
>> +}
>>
>> static const struct iio_info adc081c_info = {
>> .read_raw = adc081c_read_raw,
>> .driver_module = THIS_MODULE,
>> };
>>
>> +struct adcxx1c_model {
>> + int bits;
>> + const struct iio_chan_spec* channels;
>> +};
>> +
>> +#define ADCxx1C_CHAN(_bits) { \
>> + .type = IIO_VOLTAGE, \
>> + .info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE), \
>> + .info_mask_separate = BIT(IIO_CHAN_INFO_RAW), \
>> + .scan_type = { \
>> + .sign = 'u', \
>> + .realbits = (_bits), \
>> + .storagebits = 16, \
>> + .shift = 12 - (_bits), \
>> + .endianness = IIO_CPU, \
>> + }, \
>> +}
>> +
>> +#define DEFINE_ADCxx1C_MODEL(_name, _bits) \
>> + static const struct iio_chan_spec _name ## _channels[] = { \
>> + ADCxx1C_CHAN((_bits)), \
>> + IIO_CHAN_SOFT_TIMESTAMP(1), \
>> + }; \
>> + static const struct adcxx1c_model _name ## _model = { \
>> + .bits = (_bits), \
Worth replicating bits? I would imagine that everywhere it is needed
the channel pointer is also available, complete with this information.
>> + .channels = _name ## _channels, \
>> + }
>> +
>> +DEFINE_ADCxx1C_MODEL(adc081c, 8);
>> +DEFINE_ADCxx1C_MODEL(adc101c, 10);
>> +DEFINE_ADCxx1C_MODEL(adc121c, 12);
>> +
>> +struct adcxx1c_info {
>> + int bits;
>> + const struct adc081c_channels* channels;
>> +};
>> +
>> static int adc081c_probe(struct i2c_client *client,
>> const struct i2c_device_id *id)
>> {
>> struct iio_dev *iio;
>> struct adc081c *adc;
>> + struct adcxx1c_model *model = (struct adcxx1c_model*)id->driver_data;
>> int err;
>>
>> - if (id->driver_data != 8 && id->driver_data != 10 && id->driver_data != 12)
>> - return -EINVAL;
>> -
Interesting that you have dropped this check entirely (rather than making
it work with the new structures). Good, but drop it from the previous patch.
>> if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_WORD_DATA))
>> return -EOPNOTSUPP;
>>
>> @@ -99,7 +158,7 @@ static int adc081c_probe(struct i2c_client *client,
>>
>> adc = iio_priv(iio);
>> adc->i2c = client;
>> - adc->bits = id->driver_data;
>> + adc->bits = model->bits;
>>
>> adc->ref = devm_regulator_get(&client->dev, "vref");
>> if (IS_ERR(adc->ref))
>> @@ -114,18 +173,26 @@ static int adc081c_probe(struct i2c_client *client,
>> iio->modes = INDIO_DIRECT_MODE;
>> iio->info = &adc081c_info;
>>
>> - iio->channels = &adc081c_channel;
>> - iio->num_channels = 1;
>> + iio->channels = model->channels;
>> + iio->num_channels = 2;
>
> the number of channels could go into the adcxx1c_info struct
>
>> +
>> + err = iio_triggered_buffer_setup(iio, NULL, adc081c_trigger_handler, NULL);
>> + if (err < 0) {
>> + dev_err(&client->dev, "iio triggered buffer setup failed\n");
>> + goto err_regulator_disable;
>> + }
>>
>> err = iio_device_register(iio);
>> if (err < 0)
>> - goto regulator_disable;
>> + goto err_buffer_cleanup;
>>
>> i2c_set_clientdata(client, iio);
>>
>> return 0;
>>
>> -regulator_disable:
>> +err_buffer_cleanup:
>> + iio_triggered_buffer_cleanup(iio);
>> +err_regulator_disable:
>> regulator_disable(adc->ref);
>>
>> return err;
>> @@ -143,9 +210,9 @@ static int adc081c_remove(struct i2c_client *client)
>> }
>
> iio_triggered_buffer_cleanup() in _remove()?
>
>
>> static const struct i2c_device_id adc081c_id[] = {
>> - { "adc081c", 8 },
>> - { "adc101c", 10 },
>> - { "adc121c", 12 },
>> + { "adc081c", (long)&adc081c_model },
>
> often an enum is used instead of a pointer
Tends to end up slightly cleaner.
>
>> + { "adc101c", (long)&adc101c_model },
>> + { "adc121c", (long)&adc121c_model },
>> { }
>> };
>> MODULE_DEVICE_TABLE(i2c, adc081c_id);
>>
>