linux-iio.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH] iio: adc: Add TI ADS1015 ADC driver support
@ 2016-01-14 16:25 Daniel Baluta
  2016-01-14 16:51 ` Michael Welling
                   ` (2 more replies)
  0 siblings, 3 replies; 9+ messages in thread
From: Daniel Baluta @ 2016-01-14 16:25 UTC (permalink / raw)
  To: jic23
  Cc: knaack.h, lars, pmeerw, linux-kernel, linux-iio, daniel.baluta,
	lucas.demarchi

The driver has sysfs readings with runtime PM support for power saving.
It also offers buffer support that can be used together with IIO software
triggers.

Datasheet can be found here:
	http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf

Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
---
 drivers/iio/adc/Kconfig      |  14 ++
 drivers/iio/adc/Makefile     |   1 +
 drivers/iio/adc/ti-ads1015.c | 452 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 467 insertions(+)
 create mode 100644 drivers/iio/adc/ti-ads1015.c

diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 605ff42..fc80ecd 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -360,6 +360,20 @@ config TI_ADC128S052
 	  This driver can also be built as a module. If so, the module will be
 	  called ti-adc128s052.
 
+config TI_ADS1015
+	tristate "Texas Instruments ADS1015 ADC"
+	depends on I2C
+	select REGMAP_I2C
+	select IIO_BUFFER
+	select IIO_TRIGGERED_BUFFER
+	help
+	  If you say yes here you get support for Texas Instruments ADS1015
+	  ADC chip.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called ti-ads1015.
+
+
 config TI_ADS8688
 	tristate "Texas Instruments ADS8688"
 	depends on SPI && OF
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index 6435780..ce935de 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -35,6 +35,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
 obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
 obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
 obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
+obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
 obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
 obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
 obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
new file mode 100644
index 0000000..5016221
--- /dev/null
+++ b/drivers/iio/adc/ti-ads1015.c
@@ -0,0 +1,452 @@
+/*
+ * ADS1015 - Texas Instruments Analog-to-Digital Converter
+ *
+ * Copyright (c) 2016, Intel Corporation.
+ *
+ * This file is subject to the terms and conditions of version 2 of
+ * the GNU General Public License.  See the file COPYING in the main
+ * directory of this archive for more details.
+ *
+ * IIO driver for ADS1015 ADC 7-bit I2C slave address:
+ *	* 0x48 - ADDR connectd to Ground
+ *	* 0x49 - ADDR connected to Vdd
+ *	* 0x4A - ADDR connected to SDA
+ *	* 0x4B - ADDR connected to SCL
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+#include <linux/iio/types.h>
+
+#include <linux/iio/iio.h>
+#include <linux/iio/sysfs.h>
+#include <linux/iio/buffer.h>
+#include <linux/iio/triggered_buffer.h>
+#include <linux/iio/trigger_consumer.h>
+
+#define ADS1015_DRV_NAME "ads1015"
+
+#define ADS1015_CONV_REG	0x00
+#define ADS1015_CONFIG_REG	0x01
+
+#define ADS1015_CONFIG_DR	GENMASK(7, 5)
+#define ADS1015_CONFIG_MODE	BIT(8)
+#define ADS1015_CONFIG_SCALE	GENMASK(11, 9)
+#define ADS1015_CONFIG_MUX	GENMASK(14, 12)
+
+#define ADS1015_SLEEP_DELAY_MS	2000
+
+enum ads1015_channels {
+	ADS1015_AIN0_AIN1 = 0,
+	ADS1015_AIN0_AIN3,
+	ADS1015_AIN1_AIN3,
+	ADS1015_AIN2_AIN3,
+	ADS1015_AIN0,
+	ADS1015_AIN1,
+	ADS1015_AIN2,
+	ADS1015_AIN3,
+};
+
+static const unsigned int ads1015_data_rate[] = {
+	128, 250, 490, 920, 1600, 2400, 3300, 3300
+};
+
+static const struct {
+	int scale;
+	int uscale;
+} ads1015_scale[] = {
+	{3, 0},
+	{2, 0},
+	{1, 0},
+	{0, 500000},
+	{0, 250000},
+	{0, 125000},
+	{0, 125000},
+	{0, 125000},
+};
+
+#define ADS1015_V_CHAN(_chan, _addr) {				\
+	.type = IIO_VOLTAGE,					\
+	.indexed = 1,						\
+	.address = _addr,					\
+	.channel = _chan,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = _addr,					\
+	.scan_type = {						\
+		.sign = 's',					\
+		.realbits = 12,					\
+		.storagebits = 16,				\
+		.shift = 4,					\
+		.endianness = IIO_CPU,				\
+	},							\
+}
+
+#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) {		\
+	.type = IIO_VOLTAGE,					\
+	.differential = 1,					\
+	.indexed = 1,						\
+	.address = _addr,					\
+	.channel = _chan,					\
+	.channel2 = _chan2,					\
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
+	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
+				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
+	.scan_index = _addr,					\
+	.scan_type = {						\
+		.sign = 's',					\
+		.realbits = 12,					\
+		.storagebits = 16,				\
+		.shift = 4,					\
+		.endianness = IIO_CPU,				\
+	},							\
+}
+
+struct ads1015_data {
+	struct i2c_client *client;
+	struct regmap *regmap;
+	s16 buffer[8];
+	int64_t timestamp;
+};
+
+static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
+{
+	return (reg == ADS1015_CONFIG_REG);
+}
+
+static const struct regmap_config ads1015_regmap_config = {
+	.reg_bits = 8,
+	.val_bits = 16,
+	.max_register = ADS1015_CONFIG_REG,
+	.writeable_reg = ads1015_is_writeable_reg,
+};
+
+static const struct iio_chan_spec ads1015_channels[] = {
+	ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1),
+	ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3),
+	ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3),
+	ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3),
+	ADS1015_V_CHAN(0, ADS1015_AIN0),
+	ADS1015_V_CHAN(1, ADS1015_AIN1),
+	ADS1015_V_CHAN(2, ADS1015_AIN2),
+	ADS1015_V_CHAN(3, ADS1015_AIN3),
+};
+
+static int ads1015_set_power_state(struct ads1015_data *data, bool on)
+{
+	int ret;
+
+	if (on) {
+		ret = pm_runtime_get_sync(&data->client->dev);
+		if (ret < 0)
+			pm_runtime_put_noidle(&data->client->dev);
+	} else {
+		pm_runtime_mark_last_busy(&data->client->dev);
+		ret = pm_runtime_put_autosuspend(&data->client->dev);
+	}
+
+	return ret;
+}
+
+static
+int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
+{
+	int ret;
+
+	ret = regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				 ADS1015_CONFIG_MUX, chan << 12);
+	if (ret < 0)
+		return ret;
+
+	ret = regmap_read(data->regmap, ADS1015_CONV_REG, val);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static irqreturn_t ads1015_trigger_handler(int irq, void *p)
+{
+	struct iio_poll_func *pf = p;
+	struct iio_dev *indio_dev = pf->indio_dev;
+	struct ads1015_data *data = iio_priv(indio_dev);
+	s16 res;
+	int chan, ret;
+
+	chan = find_first_bit(indio_dev->active_scan_mask,
+			      indio_dev->masklength);
+
+	ret = ads1015_get_adc_result(data, chan, (int *)&res);
+	if (ret < 0)
+		goto err;
+
+	data->buffer[0] = res;
+
+	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
+					   data->timestamp);
+err:
+	iio_trigger_notify_done(indio_dev->trig);
+	return IRQ_HANDLED;
+}
+
+static int ads1015_set_scale(struct ads1015_data *data, int scale,
+				 int uscale)
+{
+	int i, rindex = -1;
+
+	for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
+		if (ads1015_scale[i].scale == scale &&
+			ads1015_scale[i].uscale == uscale) {
+			rindex = i;
+			break;
+		}
+	if (rindex < 0)
+		return -EINVAL;
+
+	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				  ADS1015_CONFIG_SCALE, rindex << 9);
+}
+
+static int ads1015_set_data_rate(struct ads1015_data *data, int rate)
+{
+	int i, rindex = -1;
+
+	for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
+		if (ads1015_data_rate[i] == rate) {
+			rindex = i;
+			break;
+		}
+	if (rindex < 0)
+		return -EINVAL;
+
+	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				  ADS1015_CONFIG_DR, rindex << 5);
+}
+
+static int ads1015_read_raw(struct iio_dev *indio_dev,
+			    struct iio_chan_spec const *chan, int *val,
+			    int *val2, long mask)
+{
+	int ret, idx;
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_RAW:
+		ret = ads1015_set_power_state(data, true);
+		if (ret < 0)
+			return ret;
+		ret = ads1015_get_adc_result(data, chan->address, val);
+		if (ret < 0) {
+			ads1015_set_power_state(data, false);
+			return ret;
+		}
+
+		/* 12 bit res, D0 is bit 4 in conversion register */
+		*val = sign_extend32(*val >> 4, 11);
+
+		ret = ads1015_set_power_state(data, false);
+		if (ret < 0)
+			return ret;
+
+		return IIO_VAL_INT;
+	case IIO_CHAN_INFO_SCALE:
+		ret = regmap_read(data->regmap, ADS1015_CONFIG_REG, val);
+		if (ret < 0)
+			return ret;
+		idx = (*val >> 9) & 0x0007;
+		*val = ads1015_scale[idx].scale;
+		*val2 = ads1015_scale[idx].uscale;
+
+		return IIO_VAL_INT_PLUS_MICRO;
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		ret = regmap_read(data->regmap, ADS1015_CONFIG_REG, val);
+		if (ret < 0)
+			return ret;
+
+		idx = (*val >> 5) & 0x0007;
+		*val = ads1015_data_rate[idx];
+
+		return IIO_VAL_INT;
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int ads1015_write_raw(struct iio_dev *indio_dev,
+			     struct iio_chan_spec const *chan, int val,
+			     int val2, long mask)
+{
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		return ads1015_set_scale(data, val, val2);
+	case IIO_CHAN_INFO_SAMP_FREQ:
+		return ads1015_set_data_rate(data, val);
+	default:
+		break;
+	}
+
+	return -EINVAL;
+}
+
+static int ads1015_buffer_preenable(struct iio_dev *indio_dev)
+{
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	return ads1015_set_power_state(data, true);
+}
+
+static int ads1015_buffer_postdisable(struct iio_dev *indio_dev)
+{
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	return ads1015_set_power_state(data, false);
+}
+static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
+	.preenable	= ads1015_buffer_preenable,
+	.postenable	= iio_triggered_buffer_postenable,
+	.postdisable	= ads1015_buffer_postdisable,
+	.predisable	= iio_triggered_buffer_predisable,
+	.validate_scan_mask = &iio_validate_scan_mask_onehot,
+};
+
+static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
+static IIO_CONST_ATTR(sampling_frequency_available,
+		      "128 250 490 920 1600 2400 3300");
+
+static struct attribute *ads1015_attributes[] = {
+	&iio_const_attr_scale_available.dev_attr.attr,
+	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
+	NULL,
+};
+
+static const struct attribute_group ads1015_attribute_group = {
+	.attrs = ads1015_attributes,
+};
+
+static const struct iio_info ads1015_info = {
+	.driver_module	= THIS_MODULE,
+	.read_raw	= ads1015_read_raw,
+	.write_raw	= ads1015_write_raw,
+	.attrs		= &ads1015_attribute_group,
+};
+
+static int ads1015_probe(struct i2c_client *client,
+			 const struct i2c_device_id *id)
+{
+	struct iio_dev *indio_dev;
+	struct ads1015_data *data;
+	int ret;
+
+	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
+	if (!indio_dev)
+		return -ENOMEM;
+
+	data = iio_priv(indio_dev);
+	i2c_set_clientdata(client, indio_dev);
+	data->client = client;
+
+	indio_dev->dev.parent = &client->dev;
+	indio_dev->info = &ads1015_info;
+	indio_dev->name = ADS1015_DRV_NAME;
+	indio_dev->channels = ads1015_channels;
+	indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
+	indio_dev->modes = INDIO_DIRECT_MODE;
+
+	data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config);
+	if (IS_ERR(data->regmap)) {
+		dev_err(&client->dev, "Failed to allocate register map\n");
+		return PTR_ERR(data->regmap);
+	}
+
+	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
+					 ads1015_trigger_handler,
+					 &ads1015_buffer_setup_ops);
+	if (ret < 0) {
+		dev_err(&client->dev, "iio triggered buffer setup failed\n");
+		return ret;
+	}
+
+	ret = pm_runtime_set_active(&client->dev);
+	if (ret) {
+		iio_triggered_buffer_cleanup(indio_dev);
+		return ret;
+	}
+
+	pm_runtime_enable(&client->dev);
+
+	pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
+	pm_runtime_use_autosuspend(&client->dev);
+
+	return devm_iio_device_register(&client->dev, indio_dev);
+}
+
+static int ads1015_remove(struct i2c_client *client)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(client);
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	pm_runtime_disable(&client->dev);
+	pm_runtime_set_suspended(&client->dev);
+	pm_runtime_put_noidle(&client->dev);
+
+	iio_triggered_buffer_cleanup(indio_dev);
+
+	/* power down single shot mode */
+	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				  ADS1015_CONFIG_MODE, 0x01 << 8);
+}
+
+#ifdef CONFIG_PM
+static int ads1015_runtime_suspend(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				  ADS1015_CONFIG_MODE, 0x01 << 8);
+}
+
+static int ads1015_runtime_resume(struct device *dev)
+{
+	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
+	struct ads1015_data *data = iio_priv(indio_dev);
+
+	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
+				  ADS1015_CONFIG_MODE, 0x00 << 8);
+}
+#endif
+
+static const struct dev_pm_ops ads1015_pm_ops = {
+	SET_RUNTIME_PM_OPS(ads1015_runtime_suspend,
+			   ads1015_runtime_resume, NULL)
+};
+
+static const struct i2c_device_id ads1015_id[] = {
+	{"ads1015", 0},
+	{}
+};
+MODULE_DEVICE_TABLE(i2c, ads1015_id);
+
+static struct i2c_driver ads1015_driver = {
+	.driver = {
+		.name = ADS1015_DRV_NAME,
+		.pm = &ads1015_pm_ops,
+	},
+	.probe		= ads1015_probe,
+	.remove		= ads1015_remove,
+	.id_table	= ads1015_id,
+};
+
+module_i2c_driver(ads1015_driver);
+
+MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
+MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver");
+MODULE_LICENSE("GPL v2");
-- 
1.9.1


^ permalink raw reply related	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-14 16:25 [PATCH] iio: adc: Add TI ADS1015 ADC driver support Daniel Baluta
@ 2016-01-14 16:51 ` Michael Welling
  2016-01-14 17:04   ` Lars-Peter Clausen
  2016-01-14 17:34   ` Guenter Roeck
  2016-01-16 12:07 ` Jonathan Cameron
  2016-01-26 22:28 ` Lucas De Marchi
  2 siblings, 2 replies; 9+ messages in thread
From: Michael Welling @ 2016-01-14 16:51 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: jic23, knaack.h, lars, pmeerw, linux-kernel, linux-iio,
	lucas.demarchi, eibach, linux

On Thu, Jan 14, 2016 at 06:25:08PM +0200, Daniel Baluta wrote:
> The driver has sysfs readings with runtime PM support for power saving.
> It also offers buffer support that can be used together with IIO software
> triggers.
>

It should be noted that the hwmon driver subsystem has support for this device.

http://lxr.free-electrons.com/source/drivers/hwmon/ads1015.c

The driver could likely be removed if this is accepted into iio.
The hwmon subsystem has an iio wrapper driver.

http://lxr.free-electrons.com/source/drivers/hwmon/iio_hwmon.c

The only hurdle is adding support for the ads1115 before removing the other driver.


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-14 16:51 ` Michael Welling
@ 2016-01-14 17:04   ` Lars-Peter Clausen
  2016-01-14 17:34   ` Guenter Roeck
  1 sibling, 0 replies; 9+ messages in thread
From: Lars-Peter Clausen @ 2016-01-14 17:04 UTC (permalink / raw)
  To: Michael Welling, Daniel Baluta
  Cc: jic23, knaack.h, pmeerw, linux-kernel, linux-iio, lucas.demarchi,
	eibach, linux

On 01/14/2016 05:51 PM, Michael Welling wrote:
> On Thu, Jan 14, 2016 at 06:25:08PM +0200, Daniel Baluta wrote:
>> The driver has sysfs readings with runtime PM support for power saving.
>> It also offers buffer support that can be used together with IIO software
>> triggers.
>>
> 
> It should be noted that the hwmon driver subsystem has support for this device.
> 
> http://lxr.free-electrons.com/source/drivers/hwmon/ads1015.c
> 
> The driver could likely be removed if this is accepted into iio.
> The hwmon subsystem has an iio wrapper driver.

If we do this we should instantiate the hwmon bridge for such drivers by
default, so that the userspace ABI is kept intact.

And of course we'd also need backwards compatibility for those horrible
devicetree bindings.

- Lars

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-14 16:51 ` Michael Welling
  2016-01-14 17:04   ` Lars-Peter Clausen
@ 2016-01-14 17:34   ` Guenter Roeck
  1 sibling, 0 replies; 9+ messages in thread
From: Guenter Roeck @ 2016-01-14 17:34 UTC (permalink / raw)
  To: Michael Welling, Daniel Baluta
  Cc: jic23, knaack.h, lars, pmeerw, linux-kernel, linux-iio,
	lucas.demarchi, eibach

On 01/14/2016 08:51 AM, Michael Welling wrote:
> On Thu, Jan 14, 2016 at 06:25:08PM +0200, Daniel Baluta wrote:
>> The driver has sysfs readings with runtime PM support for power saving.
>> It also offers buffer support that can be used together with IIO software
>> triggers.
>>
>
> It should be noted that the hwmon driver subsystem has support for this device.
>
> http://lxr.free-electrons.com/source/drivers/hwmon/ads1015.c
>
> The driver could likely be removed if this is accepted into iio.
> The hwmon subsystem has an iio wrapper driver.
>
> http://lxr.free-electrons.com/source/drivers/hwmon/iio_hwmon.c
>
> The only hurdle is adding support for the ads1115 before removing the other driver.
>
Yes, plus one would hope that there are not going to be a different set of
devicetree properties associated with the new driver, and that the new driver
will support (at least) the same properties.

You'll also have to make sure that only one of the two drivers can be selected.

Guenter

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-14 16:25 [PATCH] iio: adc: Add TI ADS1015 ADC driver support Daniel Baluta
  2016-01-14 16:51 ` Michael Welling
@ 2016-01-16 12:07 ` Jonathan Cameron
  2016-01-27 16:31   ` Daniel Baluta
  2016-01-26 22:28 ` Lucas De Marchi
  2 siblings, 1 reply; 9+ messages in thread
From: Jonathan Cameron @ 2016-01-16 12:07 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: knaack.h, lars, pmeerw, linux-kernel, linux-iio, lucas.demarchi

On 14/01/16 16:25, Daniel Baluta wrote:
> The driver has sysfs readings with runtime PM support for power saving.
> It also offers buffer support that can be used together with IIO software
> triggers.
> 
> Datasheet can be found here:
> 	http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf
> 
> Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
The hwmon compatibility stuff has been covered by others so I'll just assume
you'll sort that out :)

Various bits and bobs inline.

Jonathan
> ---
>  drivers/iio/adc/Kconfig      |  14 ++
>  drivers/iio/adc/Makefile     |   1 +
>  drivers/iio/adc/ti-ads1015.c | 452 +++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 467 insertions(+)
>  create mode 100644 drivers/iio/adc/ti-ads1015.c
> 
> diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
> index 605ff42..fc80ecd 100644
> --- a/drivers/iio/adc/Kconfig
> +++ b/drivers/iio/adc/Kconfig
> @@ -360,6 +360,20 @@ config TI_ADC128S052
>  	  This driver can also be built as a module. If so, the module will be
>  	  called ti-adc128s052.
>  
> +config TI_ADS1015
> +	tristate "Texas Instruments ADS1015 ADC"
> +	depends on I2C
> +	select REGMAP_I2C
> +	select IIO_BUFFER
> +	select IIO_TRIGGERED_BUFFER
> +	help
> +	  If you say yes here you get support for Texas Instruments ADS1015
> +	  ADC chip.
> +
> +	  This driver can also be built as a module. If so, the module will be
> +	  called ti-ads1015.
> +
> +
>  config TI_ADS8688
>  	tristate "Texas Instruments ADS8688"
>  	depends on SPI && OF
> diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
> index 6435780..ce935de 100644
> --- a/drivers/iio/adc/Makefile
> +++ b/drivers/iio/adc/Makefile
> @@ -35,6 +35,7 @@ obj-$(CONFIG_QCOM_SPMI_VADC) += qcom-spmi-vadc.o
>  obj-$(CONFIG_ROCKCHIP_SARADC) += rockchip_saradc.o
>  obj-$(CONFIG_TI_ADC081C) += ti-adc081c.o
>  obj-$(CONFIG_TI_ADC128S052) += ti-adc128s052.o
> +obj-$(CONFIG_TI_ADS1015) += ti-ads1015.o
>  obj-$(CONFIG_TI_ADS8688) += ti-ads8688.o
>  obj-$(CONFIG_TI_AM335X_ADC) += ti_am335x_adc.o
>  obj-$(CONFIG_TWL4030_MADC) += twl4030-madc.o
> diff --git a/drivers/iio/adc/ti-ads1015.c b/drivers/iio/adc/ti-ads1015.c
> new file mode 100644
> index 0000000..5016221
> --- /dev/null
> +++ b/drivers/iio/adc/ti-ads1015.c
> @@ -0,0 +1,452 @@
> +/*
> + * ADS1015 - Texas Instruments Analog-to-Digital Converter
> + *
> + * Copyright (c) 2016, Intel Corporation.
> + *
> + * This file is subject to the terms and conditions of version 2 of
> + * the GNU General Public License.  See the file COPYING in the main
> + * directory of this archive for more details.
> + *
> + * IIO driver for ADS1015 ADC 7-bit I2C slave address:
> + *	* 0x48 - ADDR connectd to Ground
> + *	* 0x49 - ADDR connected to Vdd
> + *	* 0x4A - ADDR connected to SDA
> + *	* 0x4B - ADDR connected to SCL
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/i2c.h>
> +#include <linux/regmap.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/iio/types.h>
> +
> +#include <linux/iio/iio.h>
> +#include <linux/iio/sysfs.h>
> +#include <linux/iio/buffer.h>
> +#include <linux/iio/triggered_buffer.h>
> +#include <linux/iio/trigger_consumer.h>
> +
> +#define ADS1015_DRV_NAME "ads1015"
> +
> +#define ADS1015_CONV_REG	0x00
> +#define ADS1015_CONFIG_REG	0x01
> +
> +#define ADS1015_CONFIG_DR	GENMASK(7, 5)
> +#define ADS1015_CONFIG_MODE	BIT(8)
> +#define ADS1015_CONFIG_SCALE	GENMASK(11, 9)
> +#define ADS1015_CONFIG_MUX	GENMASK(14, 12)
> +
> +#define ADS1015_SLEEP_DELAY_MS	2000
> +
> +enum ads1015_channels {
> +	ADS1015_AIN0_AIN1 = 0,
> +	ADS1015_AIN0_AIN3,
> +	ADS1015_AIN1_AIN3,
> +	ADS1015_AIN2_AIN3,
> +	ADS1015_AIN0,
> +	ADS1015_AIN1,
> +	ADS1015_AIN2,
> +	ADS1015_AIN3,
> +};
> +
> +static const unsigned int ads1015_data_rate[] = {
> +	128, 250, 490, 920, 1600, 2400, 3300, 3300
> +};
> +
> +static const struct {
> +	int scale;
> +	int uscale;
> +} ads1015_scale[] = {
> +	{3, 0},
> +	{2, 0},
> +	{1, 0},
> +	{0, 500000},
> +	{0, 250000},
> +	{0, 125000},
> +	{0, 125000},
> +	{0, 125000},
> +};
> +
> +#define ADS1015_V_CHAN(_chan, _addr) {				\
> +	.type = IIO_VOLTAGE,					\
> +	.indexed = 1,						\
> +	.address = _addr,					\
> +	.channel = _chan,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
> +				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> +	.scan_index = _addr,					\
> +	.scan_type = {						\
> +		.sign = 's',					\
> +		.realbits = 12,					\
> +		.storagebits = 16,				\
> +		.shift = 4,					\
> +		.endianness = IIO_CPU,				\
> +	},							\
> +}
> +
> +#define ADS1015_V_DIFF_CHAN(_chan, _chan2, _addr) {		\
> +	.type = IIO_VOLTAGE,					\
> +	.differential = 1,					\
> +	.indexed = 1,						\
> +	.address = _addr,					\
> +	.channel = _chan,					\
> +	.channel2 = _chan2,					\
> +	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),		\
> +	.info_mask_shared_by_all = BIT(IIO_CHAN_INFO_SCALE) |	\
> +				BIT(IIO_CHAN_INFO_SAMP_FREQ),	\
> +	.scan_index = _addr,					\
> +	.scan_type = {						\
> +		.sign = 's',					\
> +		.realbits = 12,					\
> +		.storagebits = 16,				\
> +		.shift = 4,					\
> +		.endianness = IIO_CPU,				\
> +	},							\
> +}
> +
> +struct ads1015_data {
I was a little suprised to see both the i2c_client and regmap in here...
Could use regmap_get_device for most if not all uses in here of the
client pointer..
> +	struct i2c_client *client;
> +	struct regmap *regmap;
You only use buffer[0] that I can see...
> +	s16 buffer[8];
> +	int64_t timestamp;
> +};
> +
> +static bool ads1015_is_writeable_reg(struct device *dev, unsigned int reg)
> +{
> +	return (reg == ADS1015_CONFIG_REG);
> +}
> +
> +static const struct regmap_config ads1015_regmap_config = {
> +	.reg_bits = 8,
> +	.val_bits = 16,
> +	.max_register = ADS1015_CONFIG_REG,
> +	.writeable_reg = ads1015_is_writeable_reg,
> +};
> +
> +static const struct iio_chan_spec ads1015_channels[] = {
> +	ADS1015_V_DIFF_CHAN(0, 1, ADS1015_AIN0_AIN1),
> +	ADS1015_V_DIFF_CHAN(0, 3, ADS1015_AIN0_AIN3),
> +	ADS1015_V_DIFF_CHAN(1, 3, ADS1015_AIN1_AIN3),
> +	ADS1015_V_DIFF_CHAN(2, 3, ADS1015_AIN2_AIN3),
> +	ADS1015_V_CHAN(0, ADS1015_AIN0),
> +	ADS1015_V_CHAN(1, ADS1015_AIN1),
> +	ADS1015_V_CHAN(2, ADS1015_AIN2),
> +	ADS1015_V_CHAN(3, ADS1015_AIN3),
> +};
> +
> +static int ads1015_set_power_state(struct ads1015_data *data, bool on)
> +{
> +	int ret;
> +
> +	if (on) {
> +		ret = pm_runtime_get_sync(&data->client->dev);
> +		if (ret < 0)
> +			pm_runtime_put_noidle(&data->client->dev);
> +	} else {
> +		pm_runtime_mark_last_busy(&data->client->dev);
> +		ret = pm_runtime_put_autosuspend(&data->client->dev);
> +	}
> +
> +	return ret;
> +}
> +
> +static
> +int ads1015_get_adc_result(struct ads1015_data *data, int chan, int *val)
> +{
> +	int ret;
> +
> +	ret = regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				 ADS1015_CONFIG_MUX, chan << 12);
> +	if (ret < 0)
> +		return ret;
> +
> +	ret = regmap_read(data->regmap, ADS1015_CONV_REG, val);
> +	if (ret < 0)
> +		return ret;
> +
> +	return 0;
> +}
> +
> +static irqreturn_t ads1015_trigger_handler(int irq, void *p)
> +{
> +	struct iio_poll_func *pf = p;
> +	struct iio_dev *indio_dev = pf->indio_dev;
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +	s16 res;
> +	int chan, ret;
> +
> +	chan = find_first_bit(indio_dev->active_scan_mask,
> +			      indio_dev->masklength);
hmm. This made me wonder if there was any milage in adding (or an existing)
find_only_bit.  Given the hardware support for many arches for ffs probably
not worthwhile
> +
> +	ret = ads1015_get_adc_result(data, chan, (int *)&res);
> +	if (ret < 0)
> +		goto err;
> +
> +	data->buffer[0] = res;
This made me wonder - what is the rest of buffer for?
> +
> +	iio_push_to_buffers_with_timestamp(indio_dev, data->buffer,
> +					   data->timestamp);
> +err:
> +	iio_trigger_notify_done(indio_dev->trig);
blank line here.
> +	return IRQ_HANDLED;
> +}
> +
> +static int ads1015_set_scale(struct ads1015_data *data, int scale,
> +				 int uscale)
> +{
> +	int i, rindex = -1;
> +
> +	for (i = 0; i < ARRAY_SIZE(ads1015_scale); i++)
> +		if (ads1015_scale[i].scale == scale &&
> +			ads1015_scale[i].uscale == uscale) {
> +			rindex = i;
> +			break;
> +		}
> +	if (rindex < 0)
> +		return -EINVAL;
> +
> +	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				  ADS1015_CONFIG_SCALE, rindex << 9);
> +}
> +
> +static int ads1015_set_data_rate(struct ads1015_data *data, int rate)
> +{
> +	int i, rindex = -1;
> +
> +	for (i = 0; i < ARRAY_SIZE(ads1015_data_rate); i++)
> +		if (ads1015_data_rate[i] == rate) {
> +			rindex = i;
> +			break;
> +		}
> +	if (rindex < 0)
> +		return -EINVAL;
> +
> +	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				  ADS1015_CONFIG_DR, rindex << 5);
> +}
> +
> +static int ads1015_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan, int *val,
> +			    int *val2, long mask)
> +{
> +	int ret, idx;
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_RAW:
Locking to prevent concurrent reads via sysfs or a mix of sysfs / buffered
reads?
> +		ret = ads1015_set_power_state(data, true);
> +		if (ret < 0)
> +			return ret;
> +		ret = ads1015_get_adc_result(data, chan->address, val);
> +		if (ret < 0) {
> +			ads1015_set_power_state(data, false);
> +			return ret;
> +		}
> +
> +		/* 12 bit res, D0 is bit 4 in conversion register */
> +		*val = sign_extend32(*val >> 4, 11);
> +
> +		ret = ads1015_set_power_state(data, false);
> +		if (ret < 0)
> +			return ret;
> +
> +		return IIO_VAL_INT;
> +	case IIO_CHAN_INFO_SCALE:
> +		ret = regmap_read(data->regmap, ADS1015_CONFIG_REG, val);
> +		if (ret < 0)
> +			return ret;
> +		idx = (*val >> 9) & 0x0007;
> +		*val = ads1015_scale[idx].scale;
> +		*val2 = ads1015_scale[idx].uscale;
> +
> +		return IIO_VAL_INT_PLUS_MICRO;
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		ret = regmap_read(data->regmap, ADS1015_CONFIG_REG, val);
> +		if (ret < 0)
> +			return ret;
> +
> +		idx = (*val >> 5) & 0x0007;

Feels a touch magic numberish to me. Either use the regmap field reading
stuff to do this cleanly (if a touch verbosely), or at very least add some
defines for the magic constants.

> +		*val = ads1015_data_rate[idx];
> +
> +		return IIO_VAL_INT;
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int ads1015_write_raw(struct iio_dev *indio_dev,
> +			     struct iio_chan_spec const *chan, int val,
> +			     int val2, long mask)
> +{
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		return ads1015_set_scale(data, val, val2);
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		return ads1015_set_data_rate(data, val);
> +	default:
> +		break;
> +	}
> +
> +	return -EINVAL;
> +}
> +
> +static int ads1015_buffer_preenable(struct iio_dev *indio_dev)
> +{
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	return ads1015_set_power_state(data, true);
> +}
> +
> +static int ads1015_buffer_postdisable(struct iio_dev *indio_dev)
> +{
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	return ads1015_set_power_state(data, false);
I'd roll these two lines into one as I don't personally think any
readability is added by the local variable (don't care that much though)
> +}
> +static const struct iio_buffer_setup_ops ads1015_buffer_setup_ops = {
> +	.preenable	= ads1015_buffer_preenable,
> +	.postenable	= iio_triggered_buffer_postenable,
> +	.postdisable	= ads1015_buffer_postdisable,
> +	.predisable	= iio_triggered_buffer_predisable,
real nitpick but I'd reorder this as it didn't scan right to me.
preenable, postenable, predisable, postdisable.  Then it's obvious
that you have replaced the central pair (which makes sense) rather
than looking like you have gotten it wrong and replaced a non
matched pair!

> +	.validate_scan_mask = &iio_validate_scan_mask_onehot,
> +};
> +
> +static IIO_CONST_ATTR(scale_available, "3 2 1 0.5 0.25 0.125");
> +static IIO_CONST_ATTR(sampling_frequency_available,
> +		      "128 250 490 920 1600 2400 3300");
> +
> +static struct attribute *ads1015_attributes[] = {
> +	&iio_const_attr_scale_available.dev_attr.attr,
> +	&iio_const_attr_sampling_frequency_available.dev_attr.attr,
> +	NULL,
> +};
> +
> +static const struct attribute_group ads1015_attribute_group = {
> +	.attrs = ads1015_attributes,
> +};
> +
> +static const struct iio_info ads1015_info = {
> +	.driver_module	= THIS_MODULE,
> +	.read_raw	= ads1015_read_raw,
> +	.write_raw	= ads1015_write_raw,
> +	.attrs		= &ads1015_attribute_group,
> +};
> +
> +static int ads1015_probe(struct i2c_client *client,
> +			 const struct i2c_device_id *id)
> +{
> +	struct iio_dev *indio_dev;
> +	struct ads1015_data *data;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&client->dev, sizeof(*data));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	data = iio_priv(indio_dev);
> +	i2c_set_clientdata(client, indio_dev);
> +	data->client = client;
> +
> +	indio_dev->dev.parent = &client->dev;
> +	indio_dev->info = &ads1015_info;
> +	indio_dev->name = ADS1015_DRV_NAME;
> +	indio_dev->channels = ads1015_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(ads1015_channels);
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +
> +	data->regmap = devm_regmap_init_i2c(client, &ads1015_regmap_config);
> +	if (IS_ERR(data->regmap)) {
> +		dev_err(&client->dev, "Failed to allocate register map\n");
> +		return PTR_ERR(data->regmap);
> +	}
> +
> +	ret = iio_triggered_buffer_setup(indio_dev, &iio_pollfunc_store_time,
> +					 ads1015_trigger_handler,
> +					 &ads1015_buffer_setup_ops);
> +	if (ret < 0) {
> +		dev_err(&client->dev, "iio triggered buffer setup failed\n");
> +		return ret;
> +	}
> +
> +	ret = pm_runtime_set_active(&client->dev);
> +	if (ret) {
> +		iio_triggered_buffer_cleanup(indio_dev);
> +		return ret;
> +	}
> +
> +	pm_runtime_enable(&client->dev);
> +
> +	pm_runtime_set_autosuspend_delay(&client->dev, ADS1015_SLEEP_DELAY_MS);
> +	pm_runtime_use_autosuspend(&client->dev);
> +
> +	return devm_iio_device_register(&client->dev, indio_dev);
> +}
> +
> +static int ads1015_remove(struct i2c_client *client)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(client);
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
Alarm bells immediately ringing... You still have the userspace
interface exposed at this point as the device unregister won't
happen until after this remove function has finished...

Don't use devm_ version of device register unless there is no
remove function.  Here you've removed the buffer when there is nothing
stopping it being started whilst this function is running.

> +	pm_runtime_disable(&client->dev);
> +	pm_runtime_set_suspended(&client->dev);
> +	pm_runtime_put_noidle(&client->dev);
> +
> +	iio_triggered_buffer_cleanup(indio_dev);
> +
> +	/* power down single shot mode */
> +	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				  ADS1015_CONFIG_MODE, 0x01 << 8);
> +}
> +
> +#ifdef CONFIG_PM
> +static int ads1015_runtime_suspend(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				  ADS1015_CONFIG_MODE, 0x01 << 8);
> +}
> +
> +static int ads1015_runtime_resume(struct device *dev)
> +{
> +	struct iio_dev *indio_dev = i2c_get_clientdata(to_i2c_client(dev));
> +	struct ads1015_data *data = iio_priv(indio_dev);
> +
> +	return regmap_update_bits(data->regmap, ADS1015_CONFIG_REG,
> +				  ADS1015_CONFIG_MODE, 0x00 << 8);
> +}
> +#endif
> +
> +static const struct dev_pm_ops ads1015_pm_ops = {
> +	SET_RUNTIME_PM_OPS(ads1015_runtime_suspend,
> +			   ads1015_runtime_resume, NULL)
> +};
> +
> +static const struct i2c_device_id ads1015_id[] = {
> +	{"ads1015", 0},
> +	{}
> +};
> +MODULE_DEVICE_TABLE(i2c, ads1015_id);
> +
> +static struct i2c_driver ads1015_driver = {
> +	.driver = {
> +		.name = ADS1015_DRV_NAME,
> +		.pm = &ads1015_pm_ops,
> +	},
> +	.probe		= ads1015_probe,
> +	.remove		= ads1015_remove,
> +	.id_table	= ads1015_id,
> +};
> +
> +module_i2c_driver(ads1015_driver);
> +
> +MODULE_AUTHOR("Daniel Baluta <daniel.baluta@intel.com>");
> +MODULE_DESCRIPTION("Texas Instruments ADS1015 ADC driver");
> +MODULE_LICENSE("GPL v2");
> 


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-14 16:25 [PATCH] iio: adc: Add TI ADS1015 ADC driver support Daniel Baluta
  2016-01-14 16:51 ` Michael Welling
  2016-01-16 12:07 ` Jonathan Cameron
@ 2016-01-26 22:28 ` Lucas De Marchi
  2016-01-27  9:49   ` Daniel Baluta
  2 siblings, 1 reply; 9+ messages in thread
From: Lucas De Marchi @ 2016-01-26 22:28 UTC (permalink / raw)
  To: Daniel Baluta
  Cc: jic23, knaack.h, lars, pmeerw, lkml, linux-iio, Lucas De Marchi

Hi,

On Thu, Jan 14, 2016 at 2:25 PM, Daniel Baluta <daniel.baluta@intel.com> wrote:
> The driver has sysfs readings with runtime PM support for power saving.
> It also offers buffer support that can be used together with IIO software
> triggers.
>
> Datasheet can be found here:
>         http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf
>
> Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
> ---

[ ... ]

> +static const struct i2c_device_id ads1015_id[] = {
> +       {"ads1015", 0},

shouldn't this be ti-ads1015 so not to clash with the hwmon driver?

> +       {}
> +};
> +MODULE_DEVICE_TABLE(i2c, ads1015_id);
> +
> +static struct i2c_driver ads1015_driver = {
> +       .driver = {
> +               .name = ADS1015_DRV_NAME,

same here, otherwise we will have an i2c:ads1015 alias


Lucas De Marchi

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-26 22:28 ` Lucas De Marchi
@ 2016-01-27  9:49   ` Daniel Baluta
  0 siblings, 0 replies; 9+ messages in thread
From: Daniel Baluta @ 2016-01-27  9:49 UTC (permalink / raw)
  To: Lucas De Marchi
  Cc: Daniel Baluta, Jonathan Cameron, Hartmut Knaack,
	Lars-Peter Clausen, Peter Meerwald-Stadler, lkml,
	linux-iio@vger.kernel.org, Lucas De Marchi

On Wed, Jan 27, 2016 at 12:28 AM, Lucas De Marchi
<lucas.de.marchi@gmail.com> wrote:
> Hi,
>
> On Thu, Jan 14, 2016 at 2:25 PM, Daniel Baluta <daniel.baluta@intel.com> wrote:
>> The driver has sysfs readings with runtime PM support for power saving.
>> It also offers buffer support that can be used together with IIO software
>> triggers.
>>
>> Datasheet can be found here:
>>         http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf
>>
>> Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
>> ---
>
> [ ... ]
>
>> +static const struct i2c_device_id ads1015_id[] = {
>> +       {"ads1015", 0},
>
> shouldn't this be ti-ads1015 so not to clash with the hwmon driver?

The plan is to remove hwmon driver and replace it with the a backward
compatbile IIO driver.

I am now working to address the comments from this thread and send a v2
asap.

>
>> +       {}
>> +};
>> +MODULE_DEVICE_TABLE(i2c, ads1015_id);
>> +
>> +static struct i2c_driver ads1015_driver = {
>> +       .driver = {
>> +               .name = ADS1015_DRV_NAME,
>
> same here, otherwise we will have an i2c:ads1015 alias

thanks,
Daniel.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-16 12:07 ` Jonathan Cameron
@ 2016-01-27 16:31   ` Daniel Baluta
  2016-01-27 17:23     ` Jonathan Cameron
  0 siblings, 1 reply; 9+ messages in thread
From: Daniel Baluta @ 2016-01-27 16:31 UTC (permalink / raw)
  To: Jonathan Cameron
  Cc: Daniel Baluta, Hartmut Knaack, Lars-Peter Clausen,
	Peter Meerwald-Stadler, Linux Kernel Mailing List,
	linux-iio@vger.kernel.org, Lucas De Marchi

On Sat, Jan 16, 2016 at 2:07 PM, Jonathan Cameron <jic23@kernel.org> wrote:
> On 14/01/16 16:25, Daniel Baluta wrote:
>> The driver has sysfs readings with runtime PM support for power saving.
>> It also offers buffer support that can be used together with IIO software
>> triggers.
>>
>> Datasheet can be found here:
>>       http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf
>>
>> Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>

Thanks for the feedback! Few questions below.


> The hwmon compatibility stuff has been covered by others so I'll just assume
> you'll sort that out :)
>
> Various bits and bobs inline.

Do you think it is worth to have both of the drivers in parallel for a
period until
we figure out that everything is correct? The IIO config symbol can be enabled
only hwmon symbol is not enabled?

<snip>

>> +struct ads1015_data {
> I was a little suprised to see both the i2c_client and regmap in here...
> Could use regmap_get_device for most if not all uses in here of the
> client pointer..

That's a good point. Anyhow, there is no single instance of regmap_get_device
in the IIO tree. I counted at least 4 drivers where we could use it.
Do you think
is there a great benefit from using it?

This will be a nice change for Linux kernel outreachy applicants :).
I'm sure they will
love to work on it.

>> +     struct i2c_client *client;
>> +     struct regmap *regmap;
> You only use buffer[0] that I can see...

The rest should be for the timestamp and I will fix it :).

<snip>

thanks,
Daniel.

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH] iio: adc: Add TI ADS1015 ADC driver support
  2016-01-27 16:31   ` Daniel Baluta
@ 2016-01-27 17:23     ` Jonathan Cameron
  0 siblings, 0 replies; 9+ messages in thread
From: Jonathan Cameron @ 2016-01-27 17:23 UTC (permalink / raw)
  To: Daniel Baluta, Jonathan Cameron
  Cc: Hartmut Knaack, Lars-Peter Clausen, Peter Meerwald-Stadler,
	Linux Kernel Mailing List, linux-iio@vger.kernel.org,
	Lucas De Marchi



On 27 January 2016 16:31:42 GMT+00:00, Daniel Baluta <daniel.baluta@intel.com> wrote:
>On Sat, Jan 16, 2016 at 2:07 PM, Jonathan Cameron <jic23@kernel.org>
>wrote:
>> On 14/01/16 16:25, Daniel Baluta wrote:
>>> The driver has sysfs readings with runtime PM support for power
>saving.
>>> It also offers buffer support that can be used together with IIO
>software
>>> triggers.
>>>
>>> Datasheet can be found here:
>>>       http://www.ti.com.cn/cn/lit/ds/symlink/ads1015.pdf
>>>
>>> Signed-off-by: Daniel Baluta <daniel.baluta@intel.com>
>
>Thanks for the feedback! Few questions below.
>
>
>> The hwmon compatibility stuff has been covered by others so I'll just
>assume
>> you'll sort that out :)
>>
>> Various bits and bobs inline.
>
>Do you think it is worth to have both of the drivers in parallel for a
>period until
>we figure out that everything is correct? The IIO config symbol can be
>enabled
>only hwmon symbol is not enabled?
>
Yes. (That was easy)
><snip>
>
>>> +struct ads1015_data {
>> I was a little suprised to see both the i2c_client and regmap in
>here...
>> Could use regmap_get_device for most if not all uses in here of the
>> client pointer..
>
>That's a good point. Anyhow, there is no single instance of
>regmap_get_device
>in the IIO tree. I counted at least 4 drivers where we could use it.
>Do you think
>is there a great benefit from using it?
I only came across it here by thinking surely there was a better way!

>
>This will be a nice change for Linux kernel outreachy applicants :).
>I'm sure they will
>love to work on it.
Cool.
>
>>> +     struct i2c_client *client;
>>> +     struct regmap *regmap;
>> You only use buffer[0] that I can see...
>
>The rest should be for the timestamp and I will fix it :).
>
><snip>
>
>thanks,
>Daniel.

-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2016-01-27 17:23 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-01-14 16:25 [PATCH] iio: adc: Add TI ADS1015 ADC driver support Daniel Baluta
2016-01-14 16:51 ` Michael Welling
2016-01-14 17:04   ` Lars-Peter Clausen
2016-01-14 17:34   ` Guenter Roeck
2016-01-16 12:07 ` Jonathan Cameron
2016-01-27 16:31   ` Daniel Baluta
2016-01-27 17:23     ` Jonathan Cameron
2016-01-26 22:28 ` Lucas De Marchi
2016-01-27  9:49   ` Daniel Baluta

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).