All of lore.kernel.org
 help / color / mirror / Atom feed
* [RCF 0/3] hwmon: add driver for TI ADS1118
@ 2016-07-16  0:18 ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon
  Cc: Joshua Clayton

This is a new driver for the TI ADS1118 and ADS1018.
It works, and is somewhat based on the work that went into
ads1015, except for the sysfs code, which I hope is more
correct and up to date.

The main reason this is an RFC, rather than a submission
is that I pulled several new dts properies out of the
air, and I would like any thoughts on these in particular.
The code is tested only against ads1118.

If anyone has an ads1018, I'd love to get testing from you
as well.

Joshua Clayton (3):
  hwmon: Add ads1118 driver
  hwmon: Document bindings for ads1118 adc driver
  ARM: imx6q-evi: ads ads1118 support

 .../devicetree/bindings/hwmon/ads1118.txt          |  81 ++++
 arch/arm/boot/dts/imx6q-evi.dts                    |  90 ++++
 drivers/hwmon/Kconfig                              |  11 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/ads1118.c                            | 516 +++++++++++++++++++++
 5 files changed, 699 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
 create mode 100644 drivers/hwmon/ads1118.c

-- 
2.7.4

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

* [RCF 0/3] hwmon: add driver for TI ADS1118
@ 2016-07-16  0:18 ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: linux-arm-kernel

This is a new driver for the TI ADS1118 and ADS1018.
It works, and is somewhat based on the work that went into
ads1015, except for the sysfs code, which I hope is more
correct and up to date.

The main reason this is an RFC, rather than a submission
is that I pulled several new dts properies out of the
air, and I would like any thoughts on these in particular.
The code is tested only against ads1118.

If anyone has an ads1018, I'd love to get testing from you
as well.

Joshua Clayton (3):
  hwmon: Add ads1118 driver
  hwmon: Document bindings for ads1118 adc driver
  ARM: imx6q-evi: ads ads1118 support

 .../devicetree/bindings/hwmon/ads1118.txt          |  81 ++++
 arch/arm/boot/dts/imx6q-evi.dts                    |  90 ++++
 drivers/hwmon/Kconfig                              |  11 +
 drivers/hwmon/Makefile                             |   1 +
 drivers/hwmon/ads1118.c                            | 516 +++++++++++++++++++++
 5 files changed, 699 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
 create mode 100644 drivers/hwmon/ads1118.c

-- 
2.7.4

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

* [RCF 1/3] hwmon: Add ads1118 driver
  2016-07-16  0:18 ` Joshua Clayton
@ 2016-07-16  0:18   ` Joshua Clayton
  -1 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon
  Cc: Joshua Clayton

Add new driver for Texas Instruments ADS1118 and and ADS1018.
This driver works with ADS1018, because of code borrowed
from asd1015, which is similar, but I can only test ADS1118

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/hwmon/Kconfig   |  11 ++
 drivers/hwmon/Makefile  |   1 +
 drivers/hwmon/ads1118.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 528 insertions(+)
 create mode 100644 drivers/hwmon/ads1118.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index ff94007..cadde38 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1459,6 +1459,17 @@ config SENSORS_ADS1015
 	  This driver can also be built as a module.  If so, the module
 	  will be called ads1015.
 
+config SENSORS_ADS1118
+	tristate "Texas Instruments ADS1018/ADS1118"
+	depends on SPI
+	help
+	  If you say yes here you get support for Texas Instruments
+	  ADS1118 16-bit 4-input ADC device and temperature sensor,
+	  and the 12-bit ADS1018.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ads1118.
+
 config SENSORS_ADS7828
 	tristate "Texas Instruments ADS7828 and compatibles"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2ef5b7c..a3b4b2e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
 obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
+obj-$(CONFIG_SENSORS_ADS1118)	+= ads1118.o
 obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
 obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
 obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
diff --git a/drivers/hwmon/ads1118.c b/drivers/hwmon/ads1118.c
new file mode 100644
index 0000000..65ee337
--- /dev/null
+++ b/drivers/hwmon/ads1118.c
@@ -0,0 +1,516 @@
+/*
+ * ads1118.c - hwmon driver for Texas Instruments ADS1118 16-bit 4-input ADC
+ * / temperature sensor, and Texas Instruments ADS1018, a faster, 12-bit
+ * chip  of the same family.
+ *
+ * Author: Joshua Clayton
+ *
+ * Loosely based on ads1015.c by Dirk Eibach and others
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+
+struct ads_table {
+	unsigned int rates[8];
+	unsigned int divisor;
+};
+
+struct ads_channel {
+	unsigned int delay_ms;
+	int pga;
+	u16 cfg;
+	bool enabled;
+};
+
+/* PGA fullscale voltages in microvolts */
+static const unsigned int fullscale_table[8] = {
+	6144000, 4096000, 2048000, 1024000, 512000, 256000, 256000, 256000 };
+
+static const struct ads_table ads1018_table = {
+	.rates = {128, 250, 490, 920, 1600, 2400, 3300, 3300},
+	.divisor = 0x7ff0,
+};
+
+static const struct ads_table ads1118_table = {
+	.rates = {8, 16, 32, 64, 128, 250, 475, 860},
+	.divisor = 0x7fff,
+};
+
+#define ADS1118_NUM_CHANS 5
+#define ADS1118_TEMP_CHAN 4
+
+struct ads1118 {
+	struct device *hwmon_dev;
+	struct device *dev;
+	struct mutex update_lock; /* mutex protect updates */
+	struct ads_channel channel_data[ADS1118_NUM_CHANS];
+	const struct ads_table *ref;
+};
+
+/*
+ * NOTE: the bit offsets in the datasheet are 16 bit big
+ * endian. I've swapped upper and lower bytes in the defines
+ * so no twiddling is needed when sending the cfg to the device.
+ */
+#define ADS1118_MODE	0	/* single shot mode */
+#define ADS1118_PGA	1	/* programmmed gain */
+#define ADS1118_MUX	4	/* input channel */
+#define ADS1118_SS	7	/* start a conversion */
+#define ADS1118_NOP	8	/* validation pattern */
+#define ADS1118_PULL_UP	11	/* pullup resistor on MISO */
+#define ADS1118_TS_MODE	12	/* temperature sensor mode */
+#define ADS1118_DR	13	/* data rate table index */
+
+#define ADS1118_ADC_CFG (BIT(ADS1118_MODE) | BIT(ADS1118_SS) | \
+		(0x3 << ADS1118_NOP) | BIT(ADS1118_PULL_UP))
+#define ADS1118_TEMP_CFG (ADS1118_ADC_CFG | BIT(ADS1118_TS_MODE))
+
+/* MUX values for AINN (second input or ground) */
+#define ADS1118_MUX_AINN1 0
+#define ADS1118_MUX_AINN3 1
+#define ADS1118_MUX_AINN_GND 4
+
+#define ADS1118_DEFAULT_PGA 0
+#define ADS1118_DEFAULT_DR 7
+
+static inline void ads1118_set_cfg(u16 *cfg, u16 value, int offset)
+{
+	*cfg &= ~(0x07 << offset);
+	*cfg |= ((value & 0x07) << offset);
+}
+
+static int ads1118_channel_set_pga(struct ads_channel *chan, u32 fullscale)
+{
+	int i;
+
+	for (i = 7; i >= 0; --i)
+		if (fullscale_table[i] == fullscale)
+			break;
+
+	if (i < 0)
+		return -EINVAL;
+
+	chan->pga = fullscale / 1000;
+	ads1118_set_cfg(&chan->cfg, i, ADS1118_PGA);
+
+	return 0;
+}
+
+static int ads1118_chan_set_mux(struct ads_channel *chan, u16 in1, u16 in2)
+{
+	switch (in1) {
+	case 0:
+		if (in2 == ADS1118_MUX_AINN1)
+			break;
+	case 1:
+	case 2:
+		if (in2 == ADS1118_MUX_AINN3)
+			break;
+	case 3:
+		if (in2 == ADS1118_MUX_AINN_GND)
+			break;
+	default:
+		return -EINVAL;
+	}
+
+	ads1118_set_cfg(&chan->cfg, in1 + in2, ADS1118_MUX);
+
+	return 0;
+}
+
+static int ads1118_chan_set_rate(struct ads1118 *ads,
+				 struct ads_channel *chan, u32 rate)
+{
+	int i;
+
+	for (i = 7; i >= 0; --i)
+		if (ads->ref->rates[i] == rate)
+			break;
+
+	if (i < 0)
+		return -EINVAL;
+
+	chan->delay_ms = DIV_ROUND_UP(1000, rate);
+	ads1118_set_cfg(&chan->cfg, i, ADS1118_DR);
+
+	return 0;
+}
+
+static int ads1118_read_adc(struct ads1118 *ads, struct ads_channel *chan,
+			    s16 *value)
+{
+	int ret;
+	u16 buf;
+	struct spi_device *spi = to_spi_device(ads->dev);
+
+	mutex_lock(&ads->update_lock);
+
+	ret = spi_write(spi, &chan->cfg, sizeof(chan->cfg));
+	if (ret < 0)
+		goto err_unlock;
+
+	/* wait until conversion finished */
+	msleep(chan->delay_ms);
+
+	ret = spi_read(spi, &buf, sizeof(buf));
+	if (ret)
+		dev_info(&spi->dev, "error reading: %d\n", ret);
+
+	*value = (s16)be16_to_cpu(buf);
+
+err_unlock:
+	mutex_unlock(&ads->update_lock);
+	return ret;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+	struct ads_channel *chan = &ads->channel_data[attr->index];
+	s16 read_value;
+	int microvolts;
+	int ret;
+
+	ret = ads1118_read_adc(ads, chan, &read_value);
+	if (ret < 0)
+		return ret;
+
+	microvolts = DIV_ROUND_CLOSEST(read_value * chan->pga,
+				       ads->ref->divisor);
+
+	return sprintf(buf, "%d\n", microvolts);
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *da,
+			 char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+	struct ads_channel *chan = &ads->channel_data[attr->index];
+	s16 read_value;
+	int ret;
+
+	ret = ads1118_read_adc(ads, chan, &read_value);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The ads1118 datasheet indicates 32nds of degree steps, but
+	 * 14 bits left justified means a divisor of 128.
+	 */
+	return sprintf(buf, "%d\n", (((int)read_value) * 1000) >> 7);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
+		ADS1118_TEMP_CHAN);
+
+static struct attribute *ads1118_attributes[] = {
+	&sensor_dev_attr_in0_input.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_in2_input.dev_attr.attr,
+	&sensor_dev_attr_in3_input.dev_attr.attr,
+
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	NULL
+};
+
+static umode_t ads1118_attrs_visible(struct kobject *kobj,
+				     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+
+	if (ads->channel_data[n].enabled)
+		return attr->mode;
+
+	return 0;
+}
+
+static const struct attribute_group ads1118_attr_group = {
+	.attrs = ads1118_attributes,
+	.is_visible = ads1118_attrs_visible,
+};
+
+static const struct attribute_group *groups[] = {
+	&ads1118_attr_group,
+	NULL
+};
+
+#ifdef CONFIG_OF
+static int ads1118_of_get_chan(struct device *dev, struct device_node *node)
+{
+	u32 chan_i;
+
+	if (!of_device_is_available(node))
+		return -EINVAL;
+
+	if (of_property_read_u32(node, "reg", &chan_i)) {
+		dev_err(dev, "reg value missing in %s\n", node->full_name);
+		return -EINVAL;
+	}
+
+	if (chan_i >= ADS1118_TEMP_CHAN) {
+		dev_err(dev, "reg value %d out of range in %s\n",
+				chan_i, node->full_name);
+		return -EINVAL;
+	}
+
+	return (int)chan_i;
+}
+
+static int ads1118_of_get_chan_pga(struct device *dev,
+				   struct device_node *node,
+				   struct ads_channel *chan)
+{
+	int ret;
+	u32 fullscale;
+
+	ret = of_property_read_u32(node, "ti,fullscale", &fullscale);
+	if (ret == -EINVAL) {
+		fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
+	} else if (ret) {
+		dev_err(dev, "bad ti,fullscale on %s: should be u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	ret = ads1118_channel_set_pga(chan, fullscale);
+	if (ret)
+		dev_err(dev, "bad ti,fullscale on %s: invalid value\n",
+			node->full_name);
+
+	return ret;
+}
+
+static int ads1118_of_get_chan_datarate(struct ads1118 *ads,
+					 struct device_node *node,
+					 struct ads_channel *chan)
+{
+	int ret;
+	u32 rate;
+
+	ret = of_property_read_u32(node, "ti,datarate", &rate);
+	if (ret == -EINVAL) {
+		rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+	} else if (ret) {
+		dev_err(ads->dev, "bad ti,datarate on %s: should be u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	ret = ads1118_chan_set_rate(ads, chan, rate);
+	if (ret)
+		dev_err(ads->dev, "bad ti,datarate on %s: invalid value\n",
+			node->full_name);
+
+	return ret;
+}
+
+static int ads1118_of_get_chan_mux(struct device *dev,
+				    struct device_node *node,
+				    struct ads_channel *chan,
+				    int chan_i)
+{
+	int ret;
+	u32 de;
+	u16 in2;
+
+	ret = of_property_read_u32(node, "ti,differential-endpoint", &de);
+	if (ret == -EINVAL) {
+		in2 = ADS1118_MUX_AINN_GND;
+		goto set_mux;
+	} else if (ret) {
+		dev_err(dev, "bad ti,differential-endpoint on %s: should be a u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	switch (de) {
+	case 1:
+		in2 = ADS1118_MUX_AINN1;
+		break;
+	case 3:
+		in2 = ADS1118_MUX_AINN3;
+		break;
+	default:
+		dev_err(dev, "bad ti,differential-endpoint %d on %s\n",
+			de, node->full_name);
+		return -EINVAL;
+	}
+
+set_mux:
+	ret = ads1118_chan_set_mux(chan, (u16)chan_i, in2);
+	if (ret)
+		dev_err(dev, "bad ti,differential-endpoint pair %d, %d on %s\n",
+			chan_i, de, node->full_name);
+
+	return ret;
+}
+
+static void ads1118_of_cfg_chan(struct ads1118 *ads, struct device_node *node)
+{
+	int ret;
+	int chan_i;
+	struct ads_channel *chan;
+
+	chan_i = ads1118_of_get_chan(ads->dev, node);
+	if (chan_i < 0)
+		return;
+
+	chan = &ads->channel_data[chan_i];
+	chan->cfg = ADS1118_ADC_CFG;
+	ret = ads1118_of_get_chan_pga(ads->dev, node, chan);
+	if (ret)
+		return;
+
+	ret = ads1118_of_get_chan_datarate(ads, node, chan);
+	if (ret)
+		return;
+
+	ret = ads1118_of_get_chan_mux(ads->dev, node, chan, chan_i);
+	if (ret)
+		return;
+
+	chan->enabled = true;
+}
+
+static void ads1118_of_get_pullup(struct ads1118 *ads)
+{
+	int i;
+
+	if (of_find_property(ads->dev->of_node, "ti,pullup-disable", NULL))
+		for (i = 0; i < ADS1118_NUM_CHANS; ++i)
+			ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
+}
+#endif
+
+static void ads1118_temp_chan_enable(struct ads1118 *ads)
+{
+	struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
+	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+
+	chan->cfg = ADS1118_TEMP_CFG;
+	ads1118_chan_set_rate(ads, chan, rate);
+	chan->enabled = true;
+}
+
+static int ads1118_get_cfg(struct ads1118 *ads)
+{
+	struct device_node *node;
+
+#ifndef CONFIG_OF
+	return -EINVAL;
+#else
+	if (!ads->dev->of_node
+	    || !of_get_next_child(ads->dev->of_node, NULL))
+		return -EINVAL;
+
+	if (of_find_property(ads->dev->of_node, "ti,tempsensor", NULL))
+		ads1118_temp_chan_enable(ads);
+
+	ads1118_of_get_pullup(ads);
+
+	for_each_child_of_node(ads->dev->of_node, node) {
+		ads1118_of_cfg_chan(ads, node);
+	}
+
+	return 0;
+#endif
+}
+
+static void ads1118_enable_all(struct ads1118 *ads)
+{
+	unsigned int i;
+	unsigned int fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
+	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+
+	ads1118_temp_chan_enable(ads);
+
+	for (i = 0; i < ADS1118_TEMP_CHAN; ++i) {
+		struct ads_channel *chan = &ads->channel_data[i];
+
+		chan->cfg = ADS1118_ADC_CFG;
+		ads1118_channel_set_pga(chan, fullscale);
+		ads1118_chan_set_rate(ads, chan, rate);
+		ads1118_chan_set_mux(chan, (u16)i, ADS1118_MUX_AINN_GND);
+		chan->enabled = true;
+	}
+}
+
+static const struct of_device_id ads_1x18_ids[] = {
+	{ .compatible = "ti,ads1018", .data = &ads1018_table, },
+	{ .compatible = "ti,ads1118", .data = &ads1118_table, },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ads_1x18_ids);
+
+static int ads1118_probe(struct spi_device *spi)
+{
+	const struct of_device_id *of_id;
+	struct ads1118 *ads;
+	int err;
+
+	ads = devm_kzalloc(&spi->dev, sizeof(struct ads1118),
+			    GFP_KERNEL);
+	if (!ads)
+		return -ENOMEM;
+
+	of_id = of_match_device(ads_1x18_ids, &spi->dev);
+	if (!of_id)
+		return -EINVAL;
+
+	ads->ref = of_id->data;
+	ads->dev = &spi->dev;
+	mutex_init(&ads->update_lock);
+	err = ads1118_get_cfg(ads);
+	if (err)
+		ads1118_enable_all(ads);
+
+	ads->hwmon_dev =
+		devm_hwmon_device_register_with_groups(ads->dev, "ads11118",
+						       ads, groups);
+	if (IS_ERR(ads->hwmon_dev)) {
+		err = PTR_ERR(ads->hwmon_dev);
+		dev_err(ads->dev, "error initializing hwmon: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static struct spi_driver ads1118_driver = {
+	.driver = {
+		.name = "ads1118",
+		.of_match_table = ads_1x18_ids,
+	},
+	.probe = ads1118_probe,
+};
+
+module_spi_driver(ads1118_driver);
+
+MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
+MODULE_DESCRIPTION("ADS1118 driver");
+MODULE_LICENSE("GPL");
-- 
2.7.4


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

* [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-16  0:18   ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: linux-arm-kernel

Add new driver for Texas Instruments ADS1118 and and ADS1018.
This driver works with ADS1018, because of code borrowed
from asd1015, which is similar, but I can only test ADS1118

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 drivers/hwmon/Kconfig   |  11 ++
 drivers/hwmon/Makefile  |   1 +
 drivers/hwmon/ads1118.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 528 insertions(+)
 create mode 100644 drivers/hwmon/ads1118.c

diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index ff94007..cadde38 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -1459,6 +1459,17 @@ config SENSORS_ADS1015
 	  This driver can also be built as a module.  If so, the module
 	  will be called ads1015.
 
+config SENSORS_ADS1118
+	tristate "Texas Instruments ADS1018/ADS1118"
+	depends on SPI
+	help
+	  If you say yes here you get support for Texas Instruments
+	  ADS1118 16-bit 4-input ADC device and temperature sensor,
+	  and the 12-bit ADS1018.
+
+	  This driver can also be built as a module.  If so, the module
+	  will be called ads1118.
+
 config SENSORS_ADS7828
 	tristate "Texas Instruments ADS7828 and compatibles"
 	depends on I2C
diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
index 2ef5b7c..a3b4b2e 100644
--- a/drivers/hwmon/Makefile
+++ b/drivers/hwmon/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
 obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
 obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
 obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
+obj-$(CONFIG_SENSORS_ADS1118)	+= ads1118.o
 obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
 obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
 obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
diff --git a/drivers/hwmon/ads1118.c b/drivers/hwmon/ads1118.c
new file mode 100644
index 0000000..65ee337
--- /dev/null
+++ b/drivers/hwmon/ads1118.c
@@ -0,0 +1,516 @@
+/*
+ * ads1118.c - hwmon driver for Texas Instruments ADS1118 16-bit 4-input ADC
+ * / temperature sensor, and Texas Instruments ADS1018, a faster, 12-bit
+ * chip  of the same family.
+ *
+ * Author: Joshua Clayton
+ *
+ * Loosely based on ads1015.c by Dirk Eibach and others
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/hwmon.h>
+#include <linux/hwmon-sysfs.h>
+#include <linux/err.h>
+#include <linux/mutex.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/spi/spi.h>
+
+struct ads_table {
+	unsigned int rates[8];
+	unsigned int divisor;
+};
+
+struct ads_channel {
+	unsigned int delay_ms;
+	int pga;
+	u16 cfg;
+	bool enabled;
+};
+
+/* PGA fullscale voltages in microvolts */
+static const unsigned int fullscale_table[8] = {
+	6144000, 4096000, 2048000, 1024000, 512000, 256000, 256000, 256000 };
+
+static const struct ads_table ads1018_table = {
+	.rates = {128, 250, 490, 920, 1600, 2400, 3300, 3300},
+	.divisor = 0x7ff0,
+};
+
+static const struct ads_table ads1118_table = {
+	.rates = {8, 16, 32, 64, 128, 250, 475, 860},
+	.divisor = 0x7fff,
+};
+
+#define ADS1118_NUM_CHANS 5
+#define ADS1118_TEMP_CHAN 4
+
+struct ads1118 {
+	struct device *hwmon_dev;
+	struct device *dev;
+	struct mutex update_lock; /* mutex protect updates */
+	struct ads_channel channel_data[ADS1118_NUM_CHANS];
+	const struct ads_table *ref;
+};
+
+/*
+ * NOTE: the bit offsets in the datasheet are 16 bit big
+ * endian. I've swapped upper and lower bytes in the defines
+ * so no twiddling is needed when sending the cfg to the device.
+ */
+#define ADS1118_MODE	0	/* single shot mode */
+#define ADS1118_PGA	1	/* programmmed gain */
+#define ADS1118_MUX	4	/* input channel */
+#define ADS1118_SS	7	/* start a conversion */
+#define ADS1118_NOP	8	/* validation pattern */
+#define ADS1118_PULL_UP	11	/* pullup resistor on MISO */
+#define ADS1118_TS_MODE	12	/* temperature sensor mode */
+#define ADS1118_DR	13	/* data rate table index */
+
+#define ADS1118_ADC_CFG (BIT(ADS1118_MODE) | BIT(ADS1118_SS) | \
+		(0x3 << ADS1118_NOP) | BIT(ADS1118_PULL_UP))
+#define ADS1118_TEMP_CFG (ADS1118_ADC_CFG | BIT(ADS1118_TS_MODE))
+
+/* MUX values for AINN (second input or ground) */
+#define ADS1118_MUX_AINN1 0
+#define ADS1118_MUX_AINN3 1
+#define ADS1118_MUX_AINN_GND 4
+
+#define ADS1118_DEFAULT_PGA 0
+#define ADS1118_DEFAULT_DR 7
+
+static inline void ads1118_set_cfg(u16 *cfg, u16 value, int offset)
+{
+	*cfg &= ~(0x07 << offset);
+	*cfg |= ((value & 0x07) << offset);
+}
+
+static int ads1118_channel_set_pga(struct ads_channel *chan, u32 fullscale)
+{
+	int i;
+
+	for (i = 7; i >= 0; --i)
+		if (fullscale_table[i] == fullscale)
+			break;
+
+	if (i < 0)
+		return -EINVAL;
+
+	chan->pga = fullscale / 1000;
+	ads1118_set_cfg(&chan->cfg, i, ADS1118_PGA);
+
+	return 0;
+}
+
+static int ads1118_chan_set_mux(struct ads_channel *chan, u16 in1, u16 in2)
+{
+	switch (in1) {
+	case 0:
+		if (in2 == ADS1118_MUX_AINN1)
+			break;
+	case 1:
+	case 2:
+		if (in2 == ADS1118_MUX_AINN3)
+			break;
+	case 3:
+		if (in2 == ADS1118_MUX_AINN_GND)
+			break;
+	default:
+		return -EINVAL;
+	}
+
+	ads1118_set_cfg(&chan->cfg, in1 + in2, ADS1118_MUX);
+
+	return 0;
+}
+
+static int ads1118_chan_set_rate(struct ads1118 *ads,
+				 struct ads_channel *chan, u32 rate)
+{
+	int i;
+
+	for (i = 7; i >= 0; --i)
+		if (ads->ref->rates[i] == rate)
+			break;
+
+	if (i < 0)
+		return -EINVAL;
+
+	chan->delay_ms = DIV_ROUND_UP(1000, rate);
+	ads1118_set_cfg(&chan->cfg, i, ADS1118_DR);
+
+	return 0;
+}
+
+static int ads1118_read_adc(struct ads1118 *ads, struct ads_channel *chan,
+			    s16 *value)
+{
+	int ret;
+	u16 buf;
+	struct spi_device *spi = to_spi_device(ads->dev);
+
+	mutex_lock(&ads->update_lock);
+
+	ret = spi_write(spi, &chan->cfg, sizeof(chan->cfg));
+	if (ret < 0)
+		goto err_unlock;
+
+	/* wait until conversion finished */
+	msleep(chan->delay_ms);
+
+	ret = spi_read(spi, &buf, sizeof(buf));
+	if (ret)
+		dev_info(&spi->dev, "error reading: %d\n", ret);
+
+	*value = (s16)be16_to_cpu(buf);
+
+err_unlock:
+	mutex_unlock(&ads->update_lock);
+	return ret;
+}
+
+static ssize_t show_in(struct device *dev, struct device_attribute *da,
+	char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+	struct ads_channel *chan = &ads->channel_data[attr->index];
+	s16 read_value;
+	int microvolts;
+	int ret;
+
+	ret = ads1118_read_adc(ads, chan, &read_value);
+	if (ret < 0)
+		return ret;
+
+	microvolts = DIV_ROUND_CLOSEST(read_value * chan->pga,
+				       ads->ref->divisor);
+
+	return sprintf(buf, "%d\n", microvolts);
+}
+
+static ssize_t show_temp(struct device *dev, struct device_attribute *da,
+			 char *buf)
+{
+	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+	struct ads_channel *chan = &ads->channel_data[attr->index];
+	s16 read_value;
+	int ret;
+
+	ret = ads1118_read_adc(ads, chan, &read_value);
+	if (ret < 0)
+		return ret;
+
+	/*
+	 * The ads1118 datasheet indicates 32nds of degree steps, but
+	 * 14 bits left justified means a divisor of 128.
+	 */
+	return sprintf(buf, "%d\n", (((int)read_value) * 1000) >> 7);
+}
+
+static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
+static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
+static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
+static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
+static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
+		ADS1118_TEMP_CHAN);
+
+static struct attribute *ads1118_attributes[] = {
+	&sensor_dev_attr_in0_input.dev_attr.attr,
+	&sensor_dev_attr_in1_input.dev_attr.attr,
+	&sensor_dev_attr_in2_input.dev_attr.attr,
+	&sensor_dev_attr_in3_input.dev_attr.attr,
+
+	&sensor_dev_attr_temp1_input.dev_attr.attr,
+	NULL
+};
+
+static umode_t ads1118_attrs_visible(struct kobject *kobj,
+				     struct attribute *attr, int n)
+{
+	struct device *dev = container_of(kobj, struct device, kobj);
+	struct ads1118 *ads = dev_get_drvdata(dev);
+
+	if (ads->channel_data[n].enabled)
+		return attr->mode;
+
+	return 0;
+}
+
+static const struct attribute_group ads1118_attr_group = {
+	.attrs = ads1118_attributes,
+	.is_visible = ads1118_attrs_visible,
+};
+
+static const struct attribute_group *groups[] = {
+	&ads1118_attr_group,
+	NULL
+};
+
+#ifdef CONFIG_OF
+static int ads1118_of_get_chan(struct device *dev, struct device_node *node)
+{
+	u32 chan_i;
+
+	if (!of_device_is_available(node))
+		return -EINVAL;
+
+	if (of_property_read_u32(node, "reg", &chan_i)) {
+		dev_err(dev, "reg value missing in %s\n", node->full_name);
+		return -EINVAL;
+	}
+
+	if (chan_i >= ADS1118_TEMP_CHAN) {
+		dev_err(dev, "reg value %d out of range in %s\n",
+				chan_i, node->full_name);
+		return -EINVAL;
+	}
+
+	return (int)chan_i;
+}
+
+static int ads1118_of_get_chan_pga(struct device *dev,
+				   struct device_node *node,
+				   struct ads_channel *chan)
+{
+	int ret;
+	u32 fullscale;
+
+	ret = of_property_read_u32(node, "ti,fullscale", &fullscale);
+	if (ret == -EINVAL) {
+		fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
+	} else if (ret) {
+		dev_err(dev, "bad ti,fullscale on %s: should be u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	ret = ads1118_channel_set_pga(chan, fullscale);
+	if (ret)
+		dev_err(dev, "bad ti,fullscale on %s: invalid value\n",
+			node->full_name);
+
+	return ret;
+}
+
+static int ads1118_of_get_chan_datarate(struct ads1118 *ads,
+					 struct device_node *node,
+					 struct ads_channel *chan)
+{
+	int ret;
+	u32 rate;
+
+	ret = of_property_read_u32(node, "ti,datarate", &rate);
+	if (ret == -EINVAL) {
+		rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+	} else if (ret) {
+		dev_err(ads->dev, "bad ti,datarate on %s: should be u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	ret = ads1118_chan_set_rate(ads, chan, rate);
+	if (ret)
+		dev_err(ads->dev, "bad ti,datarate on %s: invalid value\n",
+			node->full_name);
+
+	return ret;
+}
+
+static int ads1118_of_get_chan_mux(struct device *dev,
+				    struct device_node *node,
+				    struct ads_channel *chan,
+				    int chan_i)
+{
+	int ret;
+	u32 de;
+	u16 in2;
+
+	ret = of_property_read_u32(node, "ti,differential-endpoint", &de);
+	if (ret == -EINVAL) {
+		in2 = ADS1118_MUX_AINN_GND;
+		goto set_mux;
+	} else if (ret) {
+		dev_err(dev, "bad ti,differential-endpoint on %s: should be a u32\n",
+			node->full_name);
+		return ret;
+	}
+
+	switch (de) {
+	case 1:
+		in2 = ADS1118_MUX_AINN1;
+		break;
+	case 3:
+		in2 = ADS1118_MUX_AINN3;
+		break;
+	default:
+		dev_err(dev, "bad ti,differential-endpoint %d on %s\n",
+			de, node->full_name);
+		return -EINVAL;
+	}
+
+set_mux:
+	ret = ads1118_chan_set_mux(chan, (u16)chan_i, in2);
+	if (ret)
+		dev_err(dev, "bad ti,differential-endpoint pair %d, %d on %s\n",
+			chan_i, de, node->full_name);
+
+	return ret;
+}
+
+static void ads1118_of_cfg_chan(struct ads1118 *ads, struct device_node *node)
+{
+	int ret;
+	int chan_i;
+	struct ads_channel *chan;
+
+	chan_i = ads1118_of_get_chan(ads->dev, node);
+	if (chan_i < 0)
+		return;
+
+	chan = &ads->channel_data[chan_i];
+	chan->cfg = ADS1118_ADC_CFG;
+	ret = ads1118_of_get_chan_pga(ads->dev, node, chan);
+	if (ret)
+		return;
+
+	ret = ads1118_of_get_chan_datarate(ads, node, chan);
+	if (ret)
+		return;
+
+	ret = ads1118_of_get_chan_mux(ads->dev, node, chan, chan_i);
+	if (ret)
+		return;
+
+	chan->enabled = true;
+}
+
+static void ads1118_of_get_pullup(struct ads1118 *ads)
+{
+	int i;
+
+	if (of_find_property(ads->dev->of_node, "ti,pullup-disable", NULL))
+		for (i = 0; i < ADS1118_NUM_CHANS; ++i)
+			ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
+}
+#endif
+
+static void ads1118_temp_chan_enable(struct ads1118 *ads)
+{
+	struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
+	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+
+	chan->cfg = ADS1118_TEMP_CFG;
+	ads1118_chan_set_rate(ads, chan, rate);
+	chan->enabled = true;
+}
+
+static int ads1118_get_cfg(struct ads1118 *ads)
+{
+	struct device_node *node;
+
+#ifndef CONFIG_OF
+	return -EINVAL;
+#else
+	if (!ads->dev->of_node
+	    || !of_get_next_child(ads->dev->of_node, NULL))
+		return -EINVAL;
+
+	if (of_find_property(ads->dev->of_node, "ti,tempsensor", NULL))
+		ads1118_temp_chan_enable(ads);
+
+	ads1118_of_get_pullup(ads);
+
+	for_each_child_of_node(ads->dev->of_node, node) {
+		ads1118_of_cfg_chan(ads, node);
+	}
+
+	return 0;
+#endif
+}
+
+static void ads1118_enable_all(struct ads1118 *ads)
+{
+	unsigned int i;
+	unsigned int fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
+	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
+
+	ads1118_temp_chan_enable(ads);
+
+	for (i = 0; i < ADS1118_TEMP_CHAN; ++i) {
+		struct ads_channel *chan = &ads->channel_data[i];
+
+		chan->cfg = ADS1118_ADC_CFG;
+		ads1118_channel_set_pga(chan, fullscale);
+		ads1118_chan_set_rate(ads, chan, rate);
+		ads1118_chan_set_mux(chan, (u16)i, ADS1118_MUX_AINN_GND);
+		chan->enabled = true;
+	}
+}
+
+static const struct of_device_id ads_1x18_ids[] = {
+	{ .compatible = "ti,ads1018", .data = &ads1018_table, },
+	{ .compatible = "ti,ads1118", .data = &ads1118_table, },
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ads_1x18_ids);
+
+static int ads1118_probe(struct spi_device *spi)
+{
+	const struct of_device_id *of_id;
+	struct ads1118 *ads;
+	int err;
+
+	ads = devm_kzalloc(&spi->dev, sizeof(struct ads1118),
+			    GFP_KERNEL);
+	if (!ads)
+		return -ENOMEM;
+
+	of_id = of_match_device(ads_1x18_ids, &spi->dev);
+	if (!of_id)
+		return -EINVAL;
+
+	ads->ref = of_id->data;
+	ads->dev = &spi->dev;
+	mutex_init(&ads->update_lock);
+	err = ads1118_get_cfg(ads);
+	if (err)
+		ads1118_enable_all(ads);
+
+	ads->hwmon_dev =
+		devm_hwmon_device_register_with_groups(ads->dev, "ads11118",
+						       ads, groups);
+	if (IS_ERR(ads->hwmon_dev)) {
+		err = PTR_ERR(ads->hwmon_dev);
+		dev_err(ads->dev, "error initializing hwmon: %d\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static struct spi_driver ads1118_driver = {
+	.driver = {
+		.name = "ads1118",
+		.of_match_table = ads_1x18_ids,
+	},
+	.probe = ads1118_probe,
+};
+
+module_spi_driver(ads1118_driver);
+
+MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
+MODULE_DESCRIPTION("ADS1118 driver");
+MODULE_LICENSE("GPL");
-- 
2.7.4

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

* [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
  2016-07-16  0:18 ` Joshua Clayton
@ 2016-07-16  0:18   ` Joshua Clayton
  -1 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon
  Cc: Joshua Clayton

ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt

diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
new file mode 100644
index 0000000..41c66dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
@@ -0,0 +1,81 @@
+ADS1118 (SPI)
+
+The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
+temperature sensor.
+The ADS1018 is a 12-bit version of the chip.
+
+The inputs can be single ended or differential pair combinations.
+
+Each channel, 0 - 4 may be configured as a single ended adc input.
+Channel 0 may be configured differentially with channel 1.
+Channels 0, 1, or 2 may be configured differentially with channel 3.
+
+To set up a differential pair, the lower channel should be have
+the ti,differential-endpoint property set to the higher channel in the pair.
+
+Each channel can be configured individually:
+ - fullscale voltage range values are +/- microvolts
+    6144000, 4096000, 2048000, 1024000, 512000, or 256000
+
+ - data_rate in samples per second
+    - for the ads1018:
+      128, 250, 490, 920, 1600, 2400, or 3300
+    - for the ads1118:
+      8, 16, 32, 64, 128, 250, or 860
+
+The sensor contains an internal pullup register to keep the data output
+high when not in use. This may be disabled, but is enabled by default.
+
+1) The /ads1118 node
+
+  Required properties:
+
+   - compatible : must be "ti,ads1018" or "ti,ads1118"
+   - spi-cpha : spi phase set to 1 (spi polarity is 0)
+   - reg : the spi chipselect address
+   - #address-cells : must be <1>
+   - #size-cells : must be <0>
+
+  Optional properties:
+
+   - ti,tempsensor: enable the temperature sensor
+   - ti,pullup-disable: disable the internal pullup resistor
+
+  The node contains child nodes for each channel that the platform uses.
+
+2) channel nodes
+
+  Required properties:
+
+   - reg : the channel number (0, 1, 2, or 3)
+
+  Optional properties:
+
+   - ti,differential-endpoint: differential second endpoint.
+   - ti,fullscale: fullscale range in +/- microvolts
+   - ti,datarate : the converter data rate in samples / second
+
+  Example ADS1118 node:
+
+	ads1118@0 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <2450000>;
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		voltage0@0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+			ti,differential-endpoint = <1>;
+		};
+		voltage1@3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <475>;
+		};
+	};
+
+For the above example, The temperature sensor is enabled as well as
+channels 0 and 1 in differential mode, and channel 3 single ended.
-- 
2.7.4


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

* [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
@ 2016-07-16  0:18   ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: linux-arm-kernel

ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
 1 file changed, 81 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt

diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
new file mode 100644
index 0000000..41c66dc
--- /dev/null
+++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
@@ -0,0 +1,81 @@
+ADS1118 (SPI)
+
+The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
+temperature sensor.
+The ADS1018 is a 12-bit version of the chip.
+
+The inputs can be single ended or differential pair combinations.
+
+Each channel, 0 - 4 may be configured as a single ended adc input.
+Channel 0 may be configured differentially with channel 1.
+Channels 0, 1, or 2 may be configured differentially with channel 3.
+
+To set up a differential pair, the lower channel should be have
+the ti,differential-endpoint property set to the higher channel in the pair.
+
+Each channel can be configured individually:
+ - fullscale voltage range values are +/- microvolts
+    6144000, 4096000, 2048000, 1024000, 512000, or 256000
+
+ - data_rate in samples per second
+    - for the ads1018:
+      128, 250, 490, 920, 1600, 2400, or 3300
+    - for the ads1118:
+      8, 16, 32, 64, 128, 250, or 860
+
+The sensor contains an internal pullup register to keep the data output
+high when not in use. This may be disabled, but is enabled by default.
+
+1) The /ads1118 node
+
+  Required properties:
+
+   - compatible : must be "ti,ads1018" or "ti,ads1118"
+   - spi-cpha : spi phase set to 1 (spi polarity is 0)
+   - reg : the spi chipselect address
+   - #address-cells : must be <1>
+   - #size-cells : must be <0>
+
+  Optional properties:
+
+   - ti,tempsensor: enable the temperature sensor
+   - ti,pullup-disable: disable the internal pullup resistor
+
+  The node contains child nodes for each channel that the platform uses.
+
+2) channel nodes
+
+  Required properties:
+
+   - reg : the channel number (0, 1, 2, or 3)
+
+  Optional properties:
+
+   - ti,differential-endpoint: differential second endpoint.
+   - ti,fullscale: fullscale range in +/- microvolts
+   - ti,datarate : the converter data rate in samples / second
+
+  Example ADS1118 node:
+
+	ads1118 at 0 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <2450000>;
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		voltage0 at 0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+			ti,differential-endpoint = <1>;
+		};
+		voltage1 at 3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <475>;
+		};
+	};
+
+For the above example, The temperature sensor is enabled as well as
+channels 0 and 1 in differential mode, and channel 3 single ended.
-- 
2.7.4

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

* [RCF 3/3] ARM: imx6q-evi: ads ads1118 support
  2016-07-16  0:18 ` Joshua Clayton
@ 2016-07-16  0:18   ` Joshua Clayton
  -1 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon
  Cc: Joshua Clayton

add support to imx6q-evi for the 3 ADS1118 temperature/adc units,
 now that is has has a kernel driver

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 arch/arm/boot/dts/imx6q-evi.dts | 90 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 4fa5601..7e175bf 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -105,6 +105,66 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_ecspi3 &pinctrl_ecspi3cs>;
 	status = "okay";
+
+	temp1: adc@0 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		cpu-v@0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		cpld@1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		vocm@2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		fpga@3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
+
+	adc2: adc@1 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		signal@0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		signal@1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		probedrive@2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		probedrive@3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
 };
 
 &ecspi5 {
@@ -126,6 +186,36 @@
 		reg = <1>;
 	};
 
+	temp0: temp@2 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		analog-x@0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		analog-y@1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		main-12v@2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		battbox@3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
+
 	pb_rtc: rtc@3 {
 		compatible = "nxp,rtc-pcf2123";
 		spi-max-frequency = <2450000>;
-- 
2.7.4


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

* [RCF 3/3] ARM: imx6q-evi: ads ads1118 support
@ 2016-07-16  0:18   ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-16  0:18 UTC (permalink / raw)
  To: linux-arm-kernel

add support to imx6q-evi for the 3 ADS1118 temperature/adc units,
 now that is has has a kernel driver

Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
---
 arch/arm/boot/dts/imx6q-evi.dts | 90 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 90 insertions(+)

diff --git a/arch/arm/boot/dts/imx6q-evi.dts b/arch/arm/boot/dts/imx6q-evi.dts
index 4fa5601..7e175bf 100644
--- a/arch/arm/boot/dts/imx6q-evi.dts
+++ b/arch/arm/boot/dts/imx6q-evi.dts
@@ -105,6 +105,66 @@
 	pinctrl-names = "default";
 	pinctrl-0 = <&pinctrl_ecspi3 &pinctrl_ecspi3cs>;
 	status = "okay";
+
+	temp1: adc at 0 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <0>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		cpu-v at 0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		cpld at 1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		vocm at 2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		fpga at 3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
+
+	adc2: adc at 1 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <1>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		signal at 0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		signal at 1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		probedrive at 2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		probedrive at 3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
 };
 
 &ecspi5 {
@@ -126,6 +186,36 @@
 		reg = <1>;
 	};
 
+	temp0: temp at 2 {
+		compatible = "ti,ads1118";
+		spi-max-frequency = <500000>;
+		spi-cpha;
+		reg = <2>;
+		#address-cells = <1>;
+		#size-cells = <0>;
+		ti,tempsensor;
+		analog-x at 0 {
+			reg = <0>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		analog-y at 1 {
+			reg = <1>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		main-12v at 2 {
+			reg = <2>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+		battbox at 3 {
+			reg = <3>;
+			ti,fullscale = <6144000>;
+			ti,datarate = <860>;
+		};
+	};
+
 	pb_rtc: rtc at 3 {
 		compatible = "nxp,rtc-pcf2123";
 		spi-max-frequency = <2450000>;
-- 
2.7.4

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

* Re: [RCF 1/3] hwmon: Add ads1118 driver
  2016-07-16  0:18   ` Joshua Clayton
@ 2016-07-16  1:40     ` Guenter Roeck
  -1 siblings, 0 replies; 25+ messages in thread
From: Guenter Roeck @ 2016-07-16  1:40 UTC (permalink / raw)
  To: Joshua Clayton, Rob Herring, Mark Rutland, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Russell King, Jean Delvare,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon,
	Jonathan Cameron, linux-iio

On 07/15/2016 05:18 PM, Joshua Clayton wrote:
> Add new driver for Texas Instruments ADS1118 and and ADS1018.
> This driver works with ADS1018, because of code borrowed
> from asd1015, which is similar, but I can only test ADS1118
>

Browsing through the datasheet, I think this should probably be implemented
as iio driver (and iio already has a driver for ads1015).

Jonathan, what do you think ?

Thanks,
Guenter

> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>   drivers/hwmon/Kconfig   |  11 ++
>   drivers/hwmon/Makefile  |   1 +
>   drivers/hwmon/ads1118.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 528 insertions(+)
>   create mode 100644 drivers/hwmon/ads1118.c
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index ff94007..cadde38 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1459,6 +1459,17 @@ config SENSORS_ADS1015
>   	  This driver can also be built as a module.  If so, the module
>   	  will be called ads1015.
>
> +config SENSORS_ADS1118
> +	tristate "Texas Instruments ADS1018/ADS1118"
> +	depends on SPI
> +	help
> +	  If you say yes here you get support for Texas Instruments
> +	  ADS1118 16-bit 4-input ADC device and temperature sensor,
> +	  and the 12-bit ADS1018.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ads1118.
> +
>   config SENSORS_ADS7828
>   	tristate "Texas Instruments ADS7828 and compatibles"
>   	depends on I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 2ef5b7c..a3b4b2e 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
>   obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
>   obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
>   obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
> +obj-$(CONFIG_SENSORS_ADS1118)	+= ads1118.o
>   obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
>   obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
>   obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
> diff --git a/drivers/hwmon/ads1118.c b/drivers/hwmon/ads1118.c
> new file mode 100644
> index 0000000..65ee337
> --- /dev/null
> +++ b/drivers/hwmon/ads1118.c
> @@ -0,0 +1,516 @@
> +/*
> + * ads1118.c - hwmon driver for Texas Instruments ADS1118 16-bit 4-input ADC
> + * / temperature sensor, and Texas Instruments ADS1018, a faster, 12-bit
> + * chip  of the same family.
> + *
> + * Author: Joshua Clayton
> + *
> + * Loosely based on ads1015.c by Dirk Eibach and others
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/spi/spi.h>
> +
> +struct ads_table {
> +	unsigned int rates[8];
> +	unsigned int divisor;
> +};
> +
> +struct ads_channel {
> +	unsigned int delay_ms;
> +	int pga;
> +	u16 cfg;
> +	bool enabled;
> +};
> +
> +/* PGA fullscale voltages in microvolts */
> +static const unsigned int fullscale_table[8] = {
> +	6144000, 4096000, 2048000, 1024000, 512000, 256000, 256000, 256000 };
> +
> +static const struct ads_table ads1018_table = {
> +	.rates = {128, 250, 490, 920, 1600, 2400, 3300, 3300},
> +	.divisor = 0x7ff0,
> +};
> +
> +static const struct ads_table ads1118_table = {
> +	.rates = {8, 16, 32, 64, 128, 250, 475, 860},
> +	.divisor = 0x7fff,
> +};
> +
> +#define ADS1118_NUM_CHANS 5
> +#define ADS1118_TEMP_CHAN 4
> +
> +struct ads1118 {
> +	struct device *hwmon_dev;
> +	struct device *dev;
> +	struct mutex update_lock; /* mutex protect updates */
> +	struct ads_channel channel_data[ADS1118_NUM_CHANS];
> +	const struct ads_table *ref;
> +};
> +
> +/*
> + * NOTE: the bit offsets in the datasheet are 16 bit big
> + * endian. I've swapped upper and lower bytes in the defines
> + * so no twiddling is needed when sending the cfg to the device.
> + */
> +#define ADS1118_MODE	0	/* single shot mode */
> +#define ADS1118_PGA	1	/* programmmed gain */
> +#define ADS1118_MUX	4	/* input channel */
> +#define ADS1118_SS	7	/* start a conversion */
> +#define ADS1118_NOP	8	/* validation pattern */
> +#define ADS1118_PULL_UP	11	/* pullup resistor on MISO */
> +#define ADS1118_TS_MODE	12	/* temperature sensor mode */
> +#define ADS1118_DR	13	/* data rate table index */
> +
> +#define ADS1118_ADC_CFG (BIT(ADS1118_MODE) | BIT(ADS1118_SS) | \
> +		(0x3 << ADS1118_NOP) | BIT(ADS1118_PULL_UP))
> +#define ADS1118_TEMP_CFG (ADS1118_ADC_CFG | BIT(ADS1118_TS_MODE))
> +
> +/* MUX values for AINN (second input or ground) */
> +#define ADS1118_MUX_AINN1 0
> +#define ADS1118_MUX_AINN3 1
> +#define ADS1118_MUX_AINN_GND 4
> +
> +#define ADS1118_DEFAULT_PGA 0
> +#define ADS1118_DEFAULT_DR 7
> +
> +static inline void ads1118_set_cfg(u16 *cfg, u16 value, int offset)
> +{
> +	*cfg &= ~(0x07 << offset);
> +	*cfg |= ((value & 0x07) << offset);
> +}
> +
> +static int ads1118_channel_set_pga(struct ads_channel *chan, u32 fullscale)
> +{
> +	int i;
> +
> +	for (i = 7; i >= 0; --i)
> +		if (fullscale_table[i] == fullscale)
> +			break;
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	chan->pga = fullscale / 1000;
> +	ads1118_set_cfg(&chan->cfg, i, ADS1118_PGA);
> +
> +	return 0;
> +}
> +
> +static int ads1118_chan_set_mux(struct ads_channel *chan, u16 in1, u16 in2)
> +{
> +	switch (in1) {
> +	case 0:
> +		if (in2 == ADS1118_MUX_AINN1)
> +			break;
> +	case 1:
> +	case 2:
> +		if (in2 == ADS1118_MUX_AINN3)
> +			break;
> +	case 3:
> +		if (in2 == ADS1118_MUX_AINN_GND)
> +			break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ads1118_set_cfg(&chan->cfg, in1 + in2, ADS1118_MUX);
> +
> +	return 0;
> +}
> +
> +static int ads1118_chan_set_rate(struct ads1118 *ads,
> +				 struct ads_channel *chan, u32 rate)
> +{
> +	int i;
> +
> +	for (i = 7; i >= 0; --i)
> +		if (ads->ref->rates[i] == rate)
> +			break;
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	chan->delay_ms = DIV_ROUND_UP(1000, rate);
> +	ads1118_set_cfg(&chan->cfg, i, ADS1118_DR);
> +
> +	return 0;
> +}
> +
> +static int ads1118_read_adc(struct ads1118 *ads, struct ads_channel *chan,
> +			    s16 *value)
> +{
> +	int ret;
> +	u16 buf;
> +	struct spi_device *spi = to_spi_device(ads->dev);
> +
> +	mutex_lock(&ads->update_lock);
> +
> +	ret = spi_write(spi, &chan->cfg, sizeof(chan->cfg));
> +	if (ret < 0)
> +		goto err_unlock;
> +
> +	/* wait until conversion finished */
> +	msleep(chan->delay_ms);
> +
> +	ret = spi_read(spi, &buf, sizeof(buf));
> +	if (ret)
> +		dev_info(&spi->dev, "error reading: %d\n", ret);
> +
> +	*value = (s16)be16_to_cpu(buf);
> +
> +err_unlock:
> +	mutex_unlock(&ads->update_lock);
> +	return ret;
> +}
> +
> +static ssize_t show_in(struct device *dev, struct device_attribute *da,
> +	char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +	struct ads_channel *chan = &ads->channel_data[attr->index];
> +	s16 read_value;
> +	int microvolts;
> +	int ret;
> +
> +	ret = ads1118_read_adc(ads, chan, &read_value);
> +	if (ret < 0)
> +		return ret;
> +
> +	microvolts = DIV_ROUND_CLOSEST(read_value * chan->pga,
> +				       ads->ref->divisor);
> +
> +	return sprintf(buf, "%d\n", microvolts);
> +}
> +
> +static ssize_t show_temp(struct device *dev, struct device_attribute *da,
> +			 char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +	struct ads_channel *chan = &ads->channel_data[attr->index];
> +	s16 read_value;
> +	int ret;
> +
> +	ret = ads1118_read_adc(ads, chan, &read_value);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * The ads1118 datasheet indicates 32nds of degree steps, but
> +	 * 14 bits left justified means a divisor of 128.
> +	 */
> +	return sprintf(buf, "%d\n", (((int)read_value) * 1000) >> 7);
> +}
> +
> +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
> +		ADS1118_TEMP_CHAN);
> +
> +static struct attribute *ads1118_attributes[] = {
> +	&sensor_dev_attr_in0_input.dev_attr.attr,
> +	&sensor_dev_attr_in1_input.dev_attr.attr,
> +	&sensor_dev_attr_in2_input.dev_attr.attr,
> +	&sensor_dev_attr_in3_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +	NULL
> +};
> +
> +static umode_t ads1118_attrs_visible(struct kobject *kobj,
> +				     struct attribute *attr, int n)
> +{
> +	struct device *dev = container_of(kobj, struct device, kobj);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +
> +	if (ads->channel_data[n].enabled)
> +		return attr->mode;
> +
> +	return 0;
> +}
> +
> +static const struct attribute_group ads1118_attr_group = {
> +	.attrs = ads1118_attributes,
> +	.is_visible = ads1118_attrs_visible,
> +};
> +
> +static const struct attribute_group *groups[] = {
> +	&ads1118_attr_group,
> +	NULL
> +};
> +
> +#ifdef CONFIG_OF
> +static int ads1118_of_get_chan(struct device *dev, struct device_node *node)
> +{
> +	u32 chan_i;
> +
> +	if (!of_device_is_available(node))
> +		return -EINVAL;
> +
> +	if (of_property_read_u32(node, "reg", &chan_i)) {
> +		dev_err(dev, "reg value missing in %s\n", node->full_name);
> +		return -EINVAL;
> +	}
> +
> +	if (chan_i >= ADS1118_TEMP_CHAN) {
> +		dev_err(dev, "reg value %d out of range in %s\n",
> +				chan_i, node->full_name);
> +		return -EINVAL;
> +	}
> +
> +	return (int)chan_i;
> +}
> +
> +static int ads1118_of_get_chan_pga(struct device *dev,
> +				   struct device_node *node,
> +				   struct ads_channel *chan)
> +{
> +	int ret;
> +	u32 fullscale;
> +
> +	ret = of_property_read_u32(node, "ti,fullscale", &fullscale);
> +	if (ret == -EINVAL) {
> +		fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
> +	} else if (ret) {
> +		dev_err(dev, "bad ti,fullscale on %s: should be u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	ret = ads1118_channel_set_pga(chan, fullscale);
> +	if (ret)
> +		dev_err(dev, "bad ti,fullscale on %s: invalid value\n",
> +			node->full_name);
> +
> +	return ret;
> +}
> +
> +static int ads1118_of_get_chan_datarate(struct ads1118 *ads,
> +					 struct device_node *node,
> +					 struct ads_channel *chan)
> +{
> +	int ret;
> +	u32 rate;
> +
> +	ret = of_property_read_u32(node, "ti,datarate", &rate);
> +	if (ret == -EINVAL) {
> +		rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +	} else if (ret) {
> +		dev_err(ads->dev, "bad ti,datarate on %s: should be u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	ret = ads1118_chan_set_rate(ads, chan, rate);
> +	if (ret)
> +		dev_err(ads->dev, "bad ti,datarate on %s: invalid value\n",
> +			node->full_name);
> +
> +	return ret;
> +}
> +
> +static int ads1118_of_get_chan_mux(struct device *dev,
> +				    struct device_node *node,
> +				    struct ads_channel *chan,
> +				    int chan_i)
> +{
> +	int ret;
> +	u32 de;
> +	u16 in2;
> +
> +	ret = of_property_read_u32(node, "ti,differential-endpoint", &de);
> +	if (ret == -EINVAL) {
> +		in2 = ADS1118_MUX_AINN_GND;
> +		goto set_mux;
> +	} else if (ret) {
> +		dev_err(dev, "bad ti,differential-endpoint on %s: should be a u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	switch (de) {
> +	case 1:
> +		in2 = ADS1118_MUX_AINN1;
> +		break;
> +	case 3:
> +		in2 = ADS1118_MUX_AINN3;
> +		break;
> +	default:
> +		dev_err(dev, "bad ti,differential-endpoint %d on %s\n",
> +			de, node->full_name);
> +		return -EINVAL;
> +	}
> +
> +set_mux:
> +	ret = ads1118_chan_set_mux(chan, (u16)chan_i, in2);
> +	if (ret)
> +		dev_err(dev, "bad ti,differential-endpoint pair %d, %d on %s\n",
> +			chan_i, de, node->full_name);
> +
> +	return ret;
> +}
> +
> +static void ads1118_of_cfg_chan(struct ads1118 *ads, struct device_node *node)
> +{
> +	int ret;
> +	int chan_i;
> +	struct ads_channel *chan;
> +
> +	chan_i = ads1118_of_get_chan(ads->dev, node);
> +	if (chan_i < 0)
> +		return;
> +
> +	chan = &ads->channel_data[chan_i];
> +	chan->cfg = ADS1118_ADC_CFG;
> +	ret = ads1118_of_get_chan_pga(ads->dev, node, chan);
> +	if (ret)
> +		return;
> +
> +	ret = ads1118_of_get_chan_datarate(ads, node, chan);
> +	if (ret)
> +		return;
> +
> +	ret = ads1118_of_get_chan_mux(ads->dev, node, chan, chan_i);
> +	if (ret)
> +		return;
> +
> +	chan->enabled = true;
> +}
> +
> +static void ads1118_of_get_pullup(struct ads1118 *ads)
> +{
> +	int i;
> +
> +	if (of_find_property(ads->dev->of_node, "ti,pullup-disable", NULL))
> +		for (i = 0; i < ADS1118_NUM_CHANS; ++i)
> +			ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
> +}
> +#endif
> +
> +static void ads1118_temp_chan_enable(struct ads1118 *ads)
> +{
> +	struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
> +	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +
> +	chan->cfg = ADS1118_TEMP_CFG;
> +	ads1118_chan_set_rate(ads, chan, rate);
> +	chan->enabled = true;
> +}
> +
> +static int ads1118_get_cfg(struct ads1118 *ads)
> +{
> +	struct device_node *node;
> +
> +#ifndef CONFIG_OF
> +	return -EINVAL;
> +#else
> +	if (!ads->dev->of_node
> +	    || !of_get_next_child(ads->dev->of_node, NULL))
> +		return -EINVAL;
> +
> +	if (of_find_property(ads->dev->of_node, "ti,tempsensor", NULL))
> +		ads1118_temp_chan_enable(ads);
> +
> +	ads1118_of_get_pullup(ads);
> +
> +	for_each_child_of_node(ads->dev->of_node, node) {
> +		ads1118_of_cfg_chan(ads, node);
> +	}
> +
> +	return 0;
> +#endif
> +}
> +
> +static void ads1118_enable_all(struct ads1118 *ads)
> +{
> +	unsigned int i;
> +	unsigned int fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
> +	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +
> +	ads1118_temp_chan_enable(ads);
> +
> +	for (i = 0; i < ADS1118_TEMP_CHAN; ++i) {
> +		struct ads_channel *chan = &ads->channel_data[i];
> +
> +		chan->cfg = ADS1118_ADC_CFG;
> +		ads1118_channel_set_pga(chan, fullscale);
> +		ads1118_chan_set_rate(ads, chan, rate);
> +		ads1118_chan_set_mux(chan, (u16)i, ADS1118_MUX_AINN_GND);
> +		chan->enabled = true;
> +	}
> +}
> +
> +static const struct of_device_id ads_1x18_ids[] = {
> +	{ .compatible = "ti,ads1018", .data = &ads1018_table, },
> +	{ .compatible = "ti,ads1118", .data = &ads1118_table, },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, ads_1x18_ids);
> +
> +static int ads1118_probe(struct spi_device *spi)
> +{
> +	const struct of_device_id *of_id;
> +	struct ads1118 *ads;
> +	int err;
> +
> +	ads = devm_kzalloc(&spi->dev, sizeof(struct ads1118),
> +			    GFP_KERNEL);
> +	if (!ads)
> +		return -ENOMEM;
> +
> +	of_id = of_match_device(ads_1x18_ids, &spi->dev);
> +	if (!of_id)
> +		return -EINVAL;
> +
> +	ads->ref = of_id->data;
> +	ads->dev = &spi->dev;
> +	mutex_init(&ads->update_lock);
> +	err = ads1118_get_cfg(ads);
> +	if (err)
> +		ads1118_enable_all(ads);
> +
> +	ads->hwmon_dev =
> +		devm_hwmon_device_register_with_groups(ads->dev, "ads11118",
> +						       ads, groups);
> +	if (IS_ERR(ads->hwmon_dev)) {
> +		err = PTR_ERR(ads->hwmon_dev);
> +		dev_err(ads->dev, "error initializing hwmon: %d\n", err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct spi_driver ads1118_driver = {
> +	.driver = {
> +		.name = "ads1118",
> +		.of_match_table = ads_1x18_ids,
> +	},
> +	.probe = ads1118_probe,
> +};
> +
> +module_spi_driver(ads1118_driver);
> +
> +MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
> +MODULE_DESCRIPTION("ADS1118 driver");
> +MODULE_LICENSE("GPL");
>


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

* [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-16  1:40     ` Guenter Roeck
  0 siblings, 0 replies; 25+ messages in thread
From: Guenter Roeck @ 2016-07-16  1:40 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/15/2016 05:18 PM, Joshua Clayton wrote:
> Add new driver for Texas Instruments ADS1118 and and ADS1018.
> This driver works with ADS1018, because of code borrowed
> from asd1015, which is similar, but I can only test ADS1118
>

Browsing through the datasheet, I think this should probably be implemented
as iio driver (and iio already has a driver for ads1015).

Jonathan, what do you think ?

Thanks,
Guenter

> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>   drivers/hwmon/Kconfig   |  11 ++
>   drivers/hwmon/Makefile  |   1 +
>   drivers/hwmon/ads1118.c | 516 ++++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 528 insertions(+)
>   create mode 100644 drivers/hwmon/ads1118.c
>
> diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
> index ff94007..cadde38 100644
> --- a/drivers/hwmon/Kconfig
> +++ b/drivers/hwmon/Kconfig
> @@ -1459,6 +1459,17 @@ config SENSORS_ADS1015
>   	  This driver can also be built as a module.  If so, the module
>   	  will be called ads1015.
>
> +config SENSORS_ADS1118
> +	tristate "Texas Instruments ADS1018/ADS1118"
> +	depends on SPI
> +	help
> +	  If you say yes here you get support for Texas Instruments
> +	  ADS1118 16-bit 4-input ADC device and temperature sensor,
> +	  and the 12-bit ADS1018.
> +
> +	  This driver can also be built as a module.  If so, the module
> +	  will be called ads1118.
> +
>   config SENSORS_ADS7828
>   	tristate "Texas Instruments ADS7828 and compatibles"
>   	depends on I2C
> diff --git a/drivers/hwmon/Makefile b/drivers/hwmon/Makefile
> index 2ef5b7c..a3b4b2e 100644
> --- a/drivers/hwmon/Makefile
> +++ b/drivers/hwmon/Makefile
> @@ -34,6 +34,7 @@ obj-$(CONFIG_SENSORS_ADM1029)	+= adm1029.o
>   obj-$(CONFIG_SENSORS_ADM1031)	+= adm1031.o
>   obj-$(CONFIG_SENSORS_ADM9240)	+= adm9240.o
>   obj-$(CONFIG_SENSORS_ADS1015)	+= ads1015.o
> +obj-$(CONFIG_SENSORS_ADS1118)	+= ads1118.o
>   obj-$(CONFIG_SENSORS_ADS7828)	+= ads7828.o
>   obj-$(CONFIG_SENSORS_ADS7871)	+= ads7871.o
>   obj-$(CONFIG_SENSORS_ADT7X10)	+= adt7x10.o
> diff --git a/drivers/hwmon/ads1118.c b/drivers/hwmon/ads1118.c
> new file mode 100644
> index 0000000..65ee337
> --- /dev/null
> +++ b/drivers/hwmon/ads1118.c
> @@ -0,0 +1,516 @@
> +/*
> + * ads1118.c - hwmon driver for Texas Instruments ADS1118 16-bit 4-input ADC
> + * / temperature sensor, and Texas Instruments ADS1018, a faster, 12-bit
> + * chip  of the same family.
> + *
> + * Author: Joshua Clayton
> + *
> + * Loosely based on ads1015.c by Dirk Eibach and others
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation; either version 2 of the License, or
> + * (at your option) any later version.
> + *
> + */
> +
> +#include <linux/module.h>
> +#include <linux/init.h>
> +#include <linux/slab.h>
> +#include <linux/delay.h>
> +#include <linux/hwmon.h>
> +#include <linux/hwmon-sysfs.h>
> +#include <linux/err.h>
> +#include <linux/mutex.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/spi/spi.h>
> +
> +struct ads_table {
> +	unsigned int rates[8];
> +	unsigned int divisor;
> +};
> +
> +struct ads_channel {
> +	unsigned int delay_ms;
> +	int pga;
> +	u16 cfg;
> +	bool enabled;
> +};
> +
> +/* PGA fullscale voltages in microvolts */
> +static const unsigned int fullscale_table[8] = {
> +	6144000, 4096000, 2048000, 1024000, 512000, 256000, 256000, 256000 };
> +
> +static const struct ads_table ads1018_table = {
> +	.rates = {128, 250, 490, 920, 1600, 2400, 3300, 3300},
> +	.divisor = 0x7ff0,
> +};
> +
> +static const struct ads_table ads1118_table = {
> +	.rates = {8, 16, 32, 64, 128, 250, 475, 860},
> +	.divisor = 0x7fff,
> +};
> +
> +#define ADS1118_NUM_CHANS 5
> +#define ADS1118_TEMP_CHAN 4
> +
> +struct ads1118 {
> +	struct device *hwmon_dev;
> +	struct device *dev;
> +	struct mutex update_lock; /* mutex protect updates */
> +	struct ads_channel channel_data[ADS1118_NUM_CHANS];
> +	const struct ads_table *ref;
> +};
> +
> +/*
> + * NOTE: the bit offsets in the datasheet are 16 bit big
> + * endian. I've swapped upper and lower bytes in the defines
> + * so no twiddling is needed when sending the cfg to the device.
> + */
> +#define ADS1118_MODE	0	/* single shot mode */
> +#define ADS1118_PGA	1	/* programmmed gain */
> +#define ADS1118_MUX	4	/* input channel */
> +#define ADS1118_SS	7	/* start a conversion */
> +#define ADS1118_NOP	8	/* validation pattern */
> +#define ADS1118_PULL_UP	11	/* pullup resistor on MISO */
> +#define ADS1118_TS_MODE	12	/* temperature sensor mode */
> +#define ADS1118_DR	13	/* data rate table index */
> +
> +#define ADS1118_ADC_CFG (BIT(ADS1118_MODE) | BIT(ADS1118_SS) | \
> +		(0x3 << ADS1118_NOP) | BIT(ADS1118_PULL_UP))
> +#define ADS1118_TEMP_CFG (ADS1118_ADC_CFG | BIT(ADS1118_TS_MODE))
> +
> +/* MUX values for AINN (second input or ground) */
> +#define ADS1118_MUX_AINN1 0
> +#define ADS1118_MUX_AINN3 1
> +#define ADS1118_MUX_AINN_GND 4
> +
> +#define ADS1118_DEFAULT_PGA 0
> +#define ADS1118_DEFAULT_DR 7
> +
> +static inline void ads1118_set_cfg(u16 *cfg, u16 value, int offset)
> +{
> +	*cfg &= ~(0x07 << offset);
> +	*cfg |= ((value & 0x07) << offset);
> +}
> +
> +static int ads1118_channel_set_pga(struct ads_channel *chan, u32 fullscale)
> +{
> +	int i;
> +
> +	for (i = 7; i >= 0; --i)
> +		if (fullscale_table[i] == fullscale)
> +			break;
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	chan->pga = fullscale / 1000;
> +	ads1118_set_cfg(&chan->cfg, i, ADS1118_PGA);
> +
> +	return 0;
> +}
> +
> +static int ads1118_chan_set_mux(struct ads_channel *chan, u16 in1, u16 in2)
> +{
> +	switch (in1) {
> +	case 0:
> +		if (in2 == ADS1118_MUX_AINN1)
> +			break;
> +	case 1:
> +	case 2:
> +		if (in2 == ADS1118_MUX_AINN3)
> +			break;
> +	case 3:
> +		if (in2 == ADS1118_MUX_AINN_GND)
> +			break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	ads1118_set_cfg(&chan->cfg, in1 + in2, ADS1118_MUX);
> +
> +	return 0;
> +}
> +
> +static int ads1118_chan_set_rate(struct ads1118 *ads,
> +				 struct ads_channel *chan, u32 rate)
> +{
> +	int i;
> +
> +	for (i = 7; i >= 0; --i)
> +		if (ads->ref->rates[i] == rate)
> +			break;
> +
> +	if (i < 0)
> +		return -EINVAL;
> +
> +	chan->delay_ms = DIV_ROUND_UP(1000, rate);
> +	ads1118_set_cfg(&chan->cfg, i, ADS1118_DR);
> +
> +	return 0;
> +}
> +
> +static int ads1118_read_adc(struct ads1118 *ads, struct ads_channel *chan,
> +			    s16 *value)
> +{
> +	int ret;
> +	u16 buf;
> +	struct spi_device *spi = to_spi_device(ads->dev);
> +
> +	mutex_lock(&ads->update_lock);
> +
> +	ret = spi_write(spi, &chan->cfg, sizeof(chan->cfg));
> +	if (ret < 0)
> +		goto err_unlock;
> +
> +	/* wait until conversion finished */
> +	msleep(chan->delay_ms);
> +
> +	ret = spi_read(spi, &buf, sizeof(buf));
> +	if (ret)
> +		dev_info(&spi->dev, "error reading: %d\n", ret);
> +
> +	*value = (s16)be16_to_cpu(buf);
> +
> +err_unlock:
> +	mutex_unlock(&ads->update_lock);
> +	return ret;
> +}
> +
> +static ssize_t show_in(struct device *dev, struct device_attribute *da,
> +	char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +	struct ads_channel *chan = &ads->channel_data[attr->index];
> +	s16 read_value;
> +	int microvolts;
> +	int ret;
> +
> +	ret = ads1118_read_adc(ads, chan, &read_value);
> +	if (ret < 0)
> +		return ret;
> +
> +	microvolts = DIV_ROUND_CLOSEST(read_value * chan->pga,
> +				       ads->ref->divisor);
> +
> +	return sprintf(buf, "%d\n", microvolts);
> +}
> +
> +static ssize_t show_temp(struct device *dev, struct device_attribute *da,
> +			 char *buf)
> +{
> +	struct sensor_device_attribute *attr = to_sensor_dev_attr(da);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +	struct ads_channel *chan = &ads->channel_data[attr->index];
> +	s16 read_value;
> +	int ret;
> +
> +	ret = ads1118_read_adc(ads, chan, &read_value);
> +	if (ret < 0)
> +		return ret;
> +
> +	/*
> +	 * The ads1118 datasheet indicates 32nds of degree steps, but
> +	 * 14 bits left justified means a divisor of 128.
> +	 */
> +	return sprintf(buf, "%d\n", (((int)read_value) * 1000) >> 7);
> +}
> +
> +static SENSOR_DEVICE_ATTR(in0_input, S_IRUGO, show_in, NULL, 0);
> +static SENSOR_DEVICE_ATTR(in1_input, S_IRUGO, show_in, NULL, 1);
> +static SENSOR_DEVICE_ATTR(in2_input, S_IRUGO, show_in, NULL, 2);
> +static SENSOR_DEVICE_ATTR(in3_input, S_IRUGO, show_in, NULL, 3);
> +static SENSOR_DEVICE_ATTR(temp1_input, S_IRUGO, show_temp, NULL,
> +		ADS1118_TEMP_CHAN);
> +
> +static struct attribute *ads1118_attributes[] = {
> +	&sensor_dev_attr_in0_input.dev_attr.attr,
> +	&sensor_dev_attr_in1_input.dev_attr.attr,
> +	&sensor_dev_attr_in2_input.dev_attr.attr,
> +	&sensor_dev_attr_in3_input.dev_attr.attr,
> +
> +	&sensor_dev_attr_temp1_input.dev_attr.attr,
> +	NULL
> +};
> +
> +static umode_t ads1118_attrs_visible(struct kobject *kobj,
> +				     struct attribute *attr, int n)
> +{
> +	struct device *dev = container_of(kobj, struct device, kobj);
> +	struct ads1118 *ads = dev_get_drvdata(dev);
> +
> +	if (ads->channel_data[n].enabled)
> +		return attr->mode;
> +
> +	return 0;
> +}
> +
> +static const struct attribute_group ads1118_attr_group = {
> +	.attrs = ads1118_attributes,
> +	.is_visible = ads1118_attrs_visible,
> +};
> +
> +static const struct attribute_group *groups[] = {
> +	&ads1118_attr_group,
> +	NULL
> +};
> +
> +#ifdef CONFIG_OF
> +static int ads1118_of_get_chan(struct device *dev, struct device_node *node)
> +{
> +	u32 chan_i;
> +
> +	if (!of_device_is_available(node))
> +		return -EINVAL;
> +
> +	if (of_property_read_u32(node, "reg", &chan_i)) {
> +		dev_err(dev, "reg value missing in %s\n", node->full_name);
> +		return -EINVAL;
> +	}
> +
> +	if (chan_i >= ADS1118_TEMP_CHAN) {
> +		dev_err(dev, "reg value %d out of range in %s\n",
> +				chan_i, node->full_name);
> +		return -EINVAL;
> +	}
> +
> +	return (int)chan_i;
> +}
> +
> +static int ads1118_of_get_chan_pga(struct device *dev,
> +				   struct device_node *node,
> +				   struct ads_channel *chan)
> +{
> +	int ret;
> +	u32 fullscale;
> +
> +	ret = of_property_read_u32(node, "ti,fullscale", &fullscale);
> +	if (ret == -EINVAL) {
> +		fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
> +	} else if (ret) {
> +		dev_err(dev, "bad ti,fullscale on %s: should be u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	ret = ads1118_channel_set_pga(chan, fullscale);
> +	if (ret)
> +		dev_err(dev, "bad ti,fullscale on %s: invalid value\n",
> +			node->full_name);
> +
> +	return ret;
> +}
> +
> +static int ads1118_of_get_chan_datarate(struct ads1118 *ads,
> +					 struct device_node *node,
> +					 struct ads_channel *chan)
> +{
> +	int ret;
> +	u32 rate;
> +
> +	ret = of_property_read_u32(node, "ti,datarate", &rate);
> +	if (ret == -EINVAL) {
> +		rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +	} else if (ret) {
> +		dev_err(ads->dev, "bad ti,datarate on %s: should be u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	ret = ads1118_chan_set_rate(ads, chan, rate);
> +	if (ret)
> +		dev_err(ads->dev, "bad ti,datarate on %s: invalid value\n",
> +			node->full_name);
> +
> +	return ret;
> +}
> +
> +static int ads1118_of_get_chan_mux(struct device *dev,
> +				    struct device_node *node,
> +				    struct ads_channel *chan,
> +				    int chan_i)
> +{
> +	int ret;
> +	u32 de;
> +	u16 in2;
> +
> +	ret = of_property_read_u32(node, "ti,differential-endpoint", &de);
> +	if (ret == -EINVAL) {
> +		in2 = ADS1118_MUX_AINN_GND;
> +		goto set_mux;
> +	} else if (ret) {
> +		dev_err(dev, "bad ti,differential-endpoint on %s: should be a u32\n",
> +			node->full_name);
> +		return ret;
> +	}
> +
> +	switch (de) {
> +	case 1:
> +		in2 = ADS1118_MUX_AINN1;
> +		break;
> +	case 3:
> +		in2 = ADS1118_MUX_AINN3;
> +		break;
> +	default:
> +		dev_err(dev, "bad ti,differential-endpoint %d on %s\n",
> +			de, node->full_name);
> +		return -EINVAL;
> +	}
> +
> +set_mux:
> +	ret = ads1118_chan_set_mux(chan, (u16)chan_i, in2);
> +	if (ret)
> +		dev_err(dev, "bad ti,differential-endpoint pair %d, %d on %s\n",
> +			chan_i, de, node->full_name);
> +
> +	return ret;
> +}
> +
> +static void ads1118_of_cfg_chan(struct ads1118 *ads, struct device_node *node)
> +{
> +	int ret;
> +	int chan_i;
> +	struct ads_channel *chan;
> +
> +	chan_i = ads1118_of_get_chan(ads->dev, node);
> +	if (chan_i < 0)
> +		return;
> +
> +	chan = &ads->channel_data[chan_i];
> +	chan->cfg = ADS1118_ADC_CFG;
> +	ret = ads1118_of_get_chan_pga(ads->dev, node, chan);
> +	if (ret)
> +		return;
> +
> +	ret = ads1118_of_get_chan_datarate(ads, node, chan);
> +	if (ret)
> +		return;
> +
> +	ret = ads1118_of_get_chan_mux(ads->dev, node, chan, chan_i);
> +	if (ret)
> +		return;
> +
> +	chan->enabled = true;
> +}
> +
> +static void ads1118_of_get_pullup(struct ads1118 *ads)
> +{
> +	int i;
> +
> +	if (of_find_property(ads->dev->of_node, "ti,pullup-disable", NULL))
> +		for (i = 0; i < ADS1118_NUM_CHANS; ++i)
> +			ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
> +}
> +#endif
> +
> +static void ads1118_temp_chan_enable(struct ads1118 *ads)
> +{
> +	struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
> +	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +
> +	chan->cfg = ADS1118_TEMP_CFG;
> +	ads1118_chan_set_rate(ads, chan, rate);
> +	chan->enabled = true;
> +}
> +
> +static int ads1118_get_cfg(struct ads1118 *ads)
> +{
> +	struct device_node *node;
> +
> +#ifndef CONFIG_OF
> +	return -EINVAL;
> +#else
> +	if (!ads->dev->of_node
> +	    || !of_get_next_child(ads->dev->of_node, NULL))
> +		return -EINVAL;
> +
> +	if (of_find_property(ads->dev->of_node, "ti,tempsensor", NULL))
> +		ads1118_temp_chan_enable(ads);
> +
> +	ads1118_of_get_pullup(ads);
> +
> +	for_each_child_of_node(ads->dev->of_node, node) {
> +		ads1118_of_cfg_chan(ads, node);
> +	}
> +
> +	return 0;
> +#endif
> +}
> +
> +static void ads1118_enable_all(struct ads1118 *ads)
> +{
> +	unsigned int i;
> +	unsigned int fullscale = fullscale_table[ADS1118_DEFAULT_PGA];
> +	unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
> +
> +	ads1118_temp_chan_enable(ads);
> +
> +	for (i = 0; i < ADS1118_TEMP_CHAN; ++i) {
> +		struct ads_channel *chan = &ads->channel_data[i];
> +
> +		chan->cfg = ADS1118_ADC_CFG;
> +		ads1118_channel_set_pga(chan, fullscale);
> +		ads1118_chan_set_rate(ads, chan, rate);
> +		ads1118_chan_set_mux(chan, (u16)i, ADS1118_MUX_AINN_GND);
> +		chan->enabled = true;
> +	}
> +}
> +
> +static const struct of_device_id ads_1x18_ids[] = {
> +	{ .compatible = "ti,ads1018", .data = &ads1018_table, },
> +	{ .compatible = "ti,ads1118", .data = &ads1118_table, },
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, ads_1x18_ids);
> +
> +static int ads1118_probe(struct spi_device *spi)
> +{
> +	const struct of_device_id *of_id;
> +	struct ads1118 *ads;
> +	int err;
> +
> +	ads = devm_kzalloc(&spi->dev, sizeof(struct ads1118),
> +			    GFP_KERNEL);
> +	if (!ads)
> +		return -ENOMEM;
> +
> +	of_id = of_match_device(ads_1x18_ids, &spi->dev);
> +	if (!of_id)
> +		return -EINVAL;
> +
> +	ads->ref = of_id->data;
> +	ads->dev = &spi->dev;
> +	mutex_init(&ads->update_lock);
> +	err = ads1118_get_cfg(ads);
> +	if (err)
> +		ads1118_enable_all(ads);
> +
> +	ads->hwmon_dev =
> +		devm_hwmon_device_register_with_groups(ads->dev, "ads11118",
> +						       ads, groups);
> +	if (IS_ERR(ads->hwmon_dev)) {
> +		err = PTR_ERR(ads->hwmon_dev);
> +		dev_err(ads->dev, "error initializing hwmon: %d\n", err);
> +		return err;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct spi_driver ads1118_driver = {
> +	.driver = {
> +		.name = "ads1118",
> +		.of_match_table = ads_1x18_ids,
> +	},
> +	.probe = ads1118_probe,
> +};
> +
> +module_spi_driver(ads1118_driver);
> +
> +MODULE_AUTHOR("Joshua Clayton <stillcompiling@gmail.com>");
> +MODULE_DESCRIPTION("ADS1118 driver");
> +MODULE_LICENSE("GPL");
>

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

* Re: [RCF 1/3] hwmon: Add ads1118 driver
  2016-07-16  0:18   ` Joshua Clayton
  (?)
@ 2016-07-16 17:22     ` kbuild test robot
  -1 siblings, 0 replies; 25+ messages in thread
From: kbuild test robot @ 2016-07-16 17:22 UTC (permalink / raw)
  To: Joshua Clayton
  Cc: kbuild-all, Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon,
	Joshua Clayton

[-- Attachment #1: Type: text/plain, Size: 1800 bytes --]

Hi,

[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.7-rc7 next-20160715]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Joshua-Clayton/hwmon-add-driver-for-TI-ADS1118/20160716-082056
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: x86_64-randconfig-x001-201629 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   drivers/hwmon/ads1118.c: In function 'ads1118_get_cfg':
>> drivers/hwmon/ads1118.c:421:22: warning: unused variable 'node' [-Wunused-variable]
     struct device_node *node;
                         ^~~~

vim +/node +421 drivers/hwmon/ads1118.c

   405				ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
   406	}
   407	#endif
   408	
   409	static void ads1118_temp_chan_enable(struct ads1118 *ads)
   410	{
   411		struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
   412		unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
   413	
   414		chan->cfg = ADS1118_TEMP_CFG;
   415		ads1118_chan_set_rate(ads, chan, rate);
   416		chan->enabled = true;
   417	}
   418	
   419	static int ads1118_get_cfg(struct ads1118 *ads)
   420	{
 > 421		struct device_node *node;
   422	
   423	#ifndef CONFIG_OF
   424		return -EINVAL;
   425	#else
   426		if (!ads->dev->of_node
   427		    || !of_get_next_child(ads->dev->of_node, NULL))
   428			return -EINVAL;
   429	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 20855 bytes --]

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

* Re: [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-16 17:22     ` kbuild test robot
  0 siblings, 0 replies; 25+ messages in thread
From: kbuild test robot @ 2016-07-16 17:22 UTC (permalink / raw)
  Cc: kbuild-all, Rob Herring, Mark Rutland, Shawn Guo, Sascha Hauer,
	Fabio Estevam, Russell King, Jean Delvare, Guenter Roeck,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon,
	Joshua Clayton

[-- Attachment #1: Type: text/plain, Size: 1800 bytes --]

Hi,

[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.7-rc7 next-20160715]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Joshua-Clayton/hwmon-add-driver-for-TI-ADS1118/20160716-082056
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: x86_64-randconfig-x001-201629 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   drivers/hwmon/ads1118.c: In function 'ads1118_get_cfg':
>> drivers/hwmon/ads1118.c:421:22: warning: unused variable 'node' [-Wunused-variable]
     struct device_node *node;
                         ^~~~

vim +/node +421 drivers/hwmon/ads1118.c

   405				ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
   406	}
   407	#endif
   408	
   409	static void ads1118_temp_chan_enable(struct ads1118 *ads)
   410	{
   411		struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
   412		unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
   413	
   414		chan->cfg = ADS1118_TEMP_CFG;
   415		ads1118_chan_set_rate(ads, chan, rate);
   416		chan->enabled = true;
   417	}
   418	
   419	static int ads1118_get_cfg(struct ads1118 *ads)
   420	{
 > 421		struct device_node *node;
   422	
   423	#ifndef CONFIG_OF
   424		return -EINVAL;
   425	#else
   426		if (!ads->dev->of_node
   427		    || !of_get_next_child(ads->dev->of_node, NULL))
   428			return -EINVAL;
   429	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

[-- Attachment #2: .config.gz --]
[-- Type: application/octet-stream, Size: 20855 bytes --]

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

* [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-16 17:22     ` kbuild test robot
  0 siblings, 0 replies; 25+ messages in thread
From: kbuild test robot @ 2016-07-16 17:22 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

[auto build test WARNING on hwmon/hwmon-next]
[also build test WARNING on v4.7-rc7 next-20160715]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Joshua-Clayton/hwmon-add-driver-for-TI-ADS1118/20160716-082056
base:   https://git.kernel.org/pub/scm/linux/kernel/git/groeck/linux-staging.git hwmon-next
config: x86_64-randconfig-x001-201629 (attached as .config)
compiler: gcc-6 (Debian 6.1.1-1) 6.1.1 20160430
reproduce:
        # save the attached .config to linux build tree
        make ARCH=x86_64 

All warnings (new ones prefixed by >>):

   drivers/hwmon/ads1118.c: In function 'ads1118_get_cfg':
>> drivers/hwmon/ads1118.c:421:22: warning: unused variable 'node' [-Wunused-variable]
     struct device_node *node;
                         ^~~~

vim +/node +421 drivers/hwmon/ads1118.c

   405				ads->channel_data[i].cfg &= ~(BIT(ADS1118_PULL_UP));
   406	}
   407	#endif
   408	
   409	static void ads1118_temp_chan_enable(struct ads1118 *ads)
   410	{
   411		struct ads_channel *chan = &ads->channel_data[ADS1118_TEMP_CHAN];
   412		unsigned int rate = ads->ref->rates[ADS1118_DEFAULT_DR];
   413	
   414		chan->cfg = ADS1118_TEMP_CFG;
   415		ads1118_chan_set_rate(ads, chan, rate);
   416		chan->enabled = true;
   417	}
   418	
   419	static int ads1118_get_cfg(struct ads1118 *ads)
   420	{
 > 421		struct device_node *node;
   422	
   423	#ifndef CONFIG_OF
   424		return -EINVAL;
   425	#else
   426		if (!ads->dev->of_node
   427		    || !of_get_next_child(ads->dev->of_node, NULL))
   428			return -EINVAL;
   429	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/octet-stream
Size: 20855 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160717/1c2cbf0f/attachment-0001.obj>

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

* Re: [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
  2016-07-16  0:18   ` Joshua Clayton
@ 2016-07-17 20:34     ` Rob Herring
  -1 siblings, 0 replies; 25+ messages in thread
From: Rob Herring @ 2016-07-17 20:34 UTC (permalink / raw)
  To: Joshua Clayton
  Cc: Mark Rutland, Shawn Guo, Sascha Hauer, Fabio Estevam,
	Russell King, Jean Delvare, Guenter Roeck, devicetree,
	linux-kernel, linux-arm-kernel, linux-hwmon

On Fri, Jul 15, 2016 at 05:18:22PM -0700, Joshua Clayton wrote:
> ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge
> 
> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>  .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
>  1 file changed, 81 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> new file mode 100644
> index 0000000..41c66dc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> @@ -0,0 +1,81 @@
> +ADS1118 (SPI)
> +
> +The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
> +temperature sensor.
> +The ADS1018 is a 12-bit version of the chip.
> +
> +The inputs can be single ended or differential pair combinations.
> +
> +Each channel, 0 - 4 may be configured as a single ended adc input.
> +Channel 0 may be configured differentially with channel 1.
> +Channels 0, 1, or 2 may be configured differentially with channel 3.
> +
> +To set up a differential pair, the lower channel should be have
> +the ti,differential-endpoint property set to the higher channel in the pair.
> +
> +Each channel can be configured individually:
> + - fullscale voltage range values are +/- microvolts
> +    6144000, 4096000, 2048000, 1024000, 512000, or 256000
> +
> + - data_rate in samples per second
> +    - for the ads1018:
> +      128, 250, 490, 920, 1600, 2400, or 3300
> +    - for the ads1118:
> +      8, 16, 32, 64, 128, 250, or 860

Would be better if these are documented with the property itself.

> +
> +The sensor contains an internal pullup register to keep the data output
> +high when not in use. This may be disabled, but is enabled by default.
> +
> +1) The /ads1118 node
> +
> +  Required properties:
> +
> +   - compatible : must be "ti,ads1018" or "ti,ads1118"
> +   - spi-cpha : spi phase set to 1 (spi polarity is 0)
> +   - reg : the spi chipselect address
> +   - #address-cells : must be <1>
> +   - #size-cells : must be <0>
> +
> +  Optional properties:
> +
> +   - ti,tempsensor: enable the temperature sensor
> +   - ti,pullup-disable: disable the internal pullup resistor

Explicitly state these are boolean.

> +
> +  The node contains child nodes for each channel that the platform uses.
> +
> +2) channel nodes
> +
> +  Required properties:
> +
> +   - reg : the channel number (0, 1, 2, or 3)
> +
> +  Optional properties:
> +
> +   - ti,differential-endpoint: differential second endpoint.

Type?

> +   - ti,fullscale: fullscale range in +/- microvolts

Add standard unit suffix.

> +   - ti,datarate : the converter data rate in samples / second

Perhaps "-hz" here?

> +
> +  Example ADS1118 node:
> +
> +	ads1118@0 {
> +		compatible = "ti,ads1118";
> +		spi-max-frequency = <2450000>;
> +		reg = <0>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		ti,tempsensor;
> +		voltage0@0 {

Perhaps channel@0. Either way, drop the first 0.

> +			reg = <0>;
> +			ti,fullscale = <6144000>;
> +			ti,datarate = <860>;
> +			ti,differential-endpoint = <1>;
> +		};
> +		voltage1@3 {
> +			reg = <3>;
> +			ti,fullscale = <6144000>;
> +			ti,datarate = <475>;
> +		};
> +	};
> +
> +For the above example, The temperature sensor is enabled as well as
> +channels 0 and 1 in differential mode, and channel 3 single ended.
> -- 
> 2.7.4
> 

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

* [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
@ 2016-07-17 20:34     ` Rob Herring
  0 siblings, 0 replies; 25+ messages in thread
From: Rob Herring @ 2016-07-17 20:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Jul 15, 2016 at 05:18:22PM -0700, Joshua Clayton wrote:
> ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge
> 
> Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> ---
>  .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
>  1 file changed, 81 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
> 
> diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> new file mode 100644
> index 0000000..41c66dc
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> @@ -0,0 +1,81 @@
> +ADS1118 (SPI)
> +
> +The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
> +temperature sensor.
> +The ADS1018 is a 12-bit version of the chip.
> +
> +The inputs can be single ended or differential pair combinations.
> +
> +Each channel, 0 - 4 may be configured as a single ended adc input.
> +Channel 0 may be configured differentially with channel 1.
> +Channels 0, 1, or 2 may be configured differentially with channel 3.
> +
> +To set up a differential pair, the lower channel should be have
> +the ti,differential-endpoint property set to the higher channel in the pair.
> +
> +Each channel can be configured individually:
> + - fullscale voltage range values are +/- microvolts
> +    6144000, 4096000, 2048000, 1024000, 512000, or 256000
> +
> + - data_rate in samples per second
> +    - for the ads1018:
> +      128, 250, 490, 920, 1600, 2400, or 3300
> +    - for the ads1118:
> +      8, 16, 32, 64, 128, 250, or 860

Would be better if these are documented with the property itself.

> +
> +The sensor contains an internal pullup register to keep the data output
> +high when not in use. This may be disabled, but is enabled by default.
> +
> +1) The /ads1118 node
> +
> +  Required properties:
> +
> +   - compatible : must be "ti,ads1018" or "ti,ads1118"
> +   - spi-cpha : spi phase set to 1 (spi polarity is 0)
> +   - reg : the spi chipselect address
> +   - #address-cells : must be <1>
> +   - #size-cells : must be <0>
> +
> +  Optional properties:
> +
> +   - ti,tempsensor: enable the temperature sensor
> +   - ti,pullup-disable: disable the internal pullup resistor

Explicitly state these are boolean.

> +
> +  The node contains child nodes for each channel that the platform uses.
> +
> +2) channel nodes
> +
> +  Required properties:
> +
> +   - reg : the channel number (0, 1, 2, or 3)
> +
> +  Optional properties:
> +
> +   - ti,differential-endpoint: differential second endpoint.

Type?

> +   - ti,fullscale: fullscale range in +/- microvolts

Add standard unit suffix.

> +   - ti,datarate : the converter data rate in samples / second

Perhaps "-hz" here?

> +
> +  Example ADS1118 node:
> +
> +	ads1118 at 0 {
> +		compatible = "ti,ads1118";
> +		spi-max-frequency = <2450000>;
> +		reg = <0>;
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +		ti,tempsensor;
> +		voltage0 at 0 {

Perhaps channel at 0. Either way, drop the first 0.

> +			reg = <0>;
> +			ti,fullscale = <6144000>;
> +			ti,datarate = <860>;
> +			ti,differential-endpoint = <1>;
> +		};
> +		voltage1 at 3 {
> +			reg = <3>;
> +			ti,fullscale = <6144000>;
> +			ti,datarate = <475>;
> +		};
> +	};
> +
> +For the above example, The temperature sensor is enabled as well as
> +channels 0 and 1 in differential mode, and channel 3 single ended.
> -- 
> 2.7.4
> 

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

* Re: [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
  2016-07-17 20:34     ` Rob Herring
@ 2016-07-18 12:51       ` Joshua Clayton
  -1 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-18 12:51 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mark Rutland, Shawn Guo, Sascha Hauer, Fabio Estevam,
	Russell King, Jean Delvare, Guenter Roeck, devicetree,
	linux-kernel, linux-arm-kernel, linux-hwmon

On Sunday, July 17, 2016 03:34:25 PM Rob Herring wrote:
> On Fri, Jul 15, 2016 at 05:18:22PM -0700, Joshua Clayton wrote:
> > ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge
> > 
> > Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> > ---
> >  .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
> >  1 file changed, 81 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> > new file mode 100644
> > index 0000000..41c66dc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> > @@ -0,0 +1,81 @@
> > +ADS1118 (SPI)
> > +
> > +The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
> > +temperature sensor.
> > +The ADS1018 is a 12-bit version of the chip.
> > +
> > +The inputs can be single ended or differential pair combinations.
> > +
> > +Each channel, 0 - 4 may be configured as a single ended adc input.
> > +Channel 0 may be configured differentially with channel 1.
> > +Channels 0, 1, or 2 may be configured differentially with channel 3.
> > +
> > +To set up a differential pair, the lower channel should be have
> > +the ti,differential-endpoint property set to the higher channel in the pair.
> > +
> > +Each channel can be configured individually:
> > + - fullscale voltage range values are +/- microvolts
> > +    6144000, 4096000, 2048000, 1024000, 512000, or 256000
> > +
> > + - data_rate in samples per second
> > +    - for the ads1018:
> > +      128, 250, 490, 920, 1600, 2400, or 3300
> > +    - for the ads1118:
> > +      8, 16, 32, 64, 128, 250, or 860
> 
> Would be better if these are documented with the property itself.
> 
> > +
> > +The sensor contains an internal pullup register to keep the data output
> > +high when not in use. This may be disabled, but is enabled by default.
> > +
> > +1) The /ads1118 node
> > +
> > +  Required properties:
> > +
> > +   - compatible : must be "ti,ads1018" or "ti,ads1118"
> > +   - spi-cpha : spi phase set to 1 (spi polarity is 0)
> > +   - reg : the spi chipselect address
> > +   - #address-cells : must be <1>
> > +   - #size-cells : must be <0>
> > +
> > +  Optional properties:
> > +
> > +   - ti,tempsensor: enable the temperature sensor
> > +   - ti,pullup-disable: disable the internal pullup resistor
> 
> Explicitly state these are boolean.
> 
> > +
> > +  The node contains child nodes for each channel that the platform uses.
> > +
> > +2) channel nodes
> > +
> > +  Required properties:
> > +
> > +   - reg : the channel number (0, 1, 2, or 3)
> > +
> > +  Optional properties:
> > +
> > +   - ti,differential-endpoint: differential second endpoint.
> 
> Type?
Hmm. the type is numeric input "channel number". 1, 3 or "none" are
the only valid choices
> 
> > +   - ti,fullscale: fullscale range in +/- microvolts
> 
> Add standard unit suffix.
Will do
> 
> > +   - ti,datarate : the converter data rate in samples / second
> 
> Perhaps "-hz" here?
OK
> 
> > +
> > +  Example ADS1118 node:
> > +
> > +	ads1118@0 {
> > +		compatible = "ti,ads1118";
> > +		spi-max-frequency = <2450000>;
> > +		reg = <0>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		ti,tempsensor;
> > +		voltage0@0 {
> 
> Perhaps channel@0. Either way, drop the first 0.
Ah, thanks. I'll go with channel@0
> 
> > +			reg = <0>;
> > +			ti,fullscale = <6144000>;
> > +			ti,datarate = <860>;
> > +			ti,differential-endpoint = <1>;
> > +		};
> > +		voltage1@3 {
> > +			reg = <3>;
> > +			ti,fullscale = <6144000>;
> > +			ti,datarate = <475>;
> > +		};
> > +	};
> > +
> > +For the above example, The temperature sensor is enabled as well as
> > +channels 0 and 1 in differential mode, and channel 3 single ended.
> 

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

* [RCF 2/3] hwmon: Document bindings for ads1118 adc driver
@ 2016-07-18 12:51       ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-18 12:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday, July 17, 2016 03:34:25 PM Rob Herring wrote:
> On Fri, Jul 15, 2016 at 05:18:22PM -0700, Joshua Clayton wrote:
> > ads1118 is a 4 input 16 bit adc with a buit-in temperature gauge
> > 
> > Signed-off-by: Joshua Clayton <stillcompiling@gmail.com>
> > ---
> >  .../devicetree/bindings/hwmon/ads1118.txt          | 81 ++++++++++++++++++++++
> >  1 file changed, 81 insertions(+)
> >  create mode 100644 Documentation/devicetree/bindings/hwmon/ads1118.txt
> > 
> > diff --git a/Documentation/devicetree/bindings/hwmon/ads1118.txt b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> > new file mode 100644
> > index 0000000..41c66dc
> > --- /dev/null
> > +++ b/Documentation/devicetree/bindings/hwmon/ads1118.txt
> > @@ -0,0 +1,81 @@
> > +ADS1118 (SPI)
> > +
> > +The ADS1118 is 16-bit analog to digital converter with 4 inputs and a
> > +temperature sensor.
> > +The ADS1018 is a 12-bit version of the chip.
> > +
> > +The inputs can be single ended or differential pair combinations.
> > +
> > +Each channel, 0 - 4 may be configured as a single ended adc input.
> > +Channel 0 may be configured differentially with channel 1.
> > +Channels 0, 1, or 2 may be configured differentially with channel 3.
> > +
> > +To set up a differential pair, the lower channel should be have
> > +the ti,differential-endpoint property set to the higher channel in the pair.
> > +
> > +Each channel can be configured individually:
> > + - fullscale voltage range values are +/- microvolts
> > +    6144000, 4096000, 2048000, 1024000, 512000, or 256000
> > +
> > + - data_rate in samples per second
> > +    - for the ads1018:
> > +      128, 250, 490, 920, 1600, 2400, or 3300
> > +    - for the ads1118:
> > +      8, 16, 32, 64, 128, 250, or 860
> 
> Would be better if these are documented with the property itself.
> 
> > +
> > +The sensor contains an internal pullup register to keep the data output
> > +high when not in use. This may be disabled, but is enabled by default.
> > +
> > +1) The /ads1118 node
> > +
> > +  Required properties:
> > +
> > +   - compatible : must be "ti,ads1018" or "ti,ads1118"
> > +   - spi-cpha : spi phase set to 1 (spi polarity is 0)
> > +   - reg : the spi chipselect address
> > +   - #address-cells : must be <1>
> > +   - #size-cells : must be <0>
> > +
> > +  Optional properties:
> > +
> > +   - ti,tempsensor: enable the temperature sensor
> > +   - ti,pullup-disable: disable the internal pullup resistor
> 
> Explicitly state these are boolean.
> 
> > +
> > +  The node contains child nodes for each channel that the platform uses.
> > +
> > +2) channel nodes
> > +
> > +  Required properties:
> > +
> > +   - reg : the channel number (0, 1, 2, or 3)
> > +
> > +  Optional properties:
> > +
> > +   - ti,differential-endpoint: differential second endpoint.
> 
> Type?
Hmm. the type is numeric input "channel number". 1, 3 or "none" are
the only valid choices
> 
> > +   - ti,fullscale: fullscale range in +/- microvolts
> 
> Add standard unit suffix.
Will do
> 
> > +   - ti,datarate : the converter data rate in samples / second
> 
> Perhaps "-hz" here?
OK
> 
> > +
> > +  Example ADS1118 node:
> > +
> > +	ads1118 at 0 {
> > +		compatible = "ti,ads1118";
> > +		spi-max-frequency = <2450000>;
> > +		reg = <0>;
> > +		#address-cells = <1>;
> > +		#size-cells = <0>;
> > +		ti,tempsensor;
> > +		voltage0 at 0 {
> 
> Perhaps channel at 0. Either way, drop the first 0.
Ah, thanks. I'll go with channel at 0
> 
> > +			reg = <0>;
> > +			ti,fullscale = <6144000>;
> > +			ti,datarate = <860>;
> > +			ti,differential-endpoint = <1>;
> > +		};
> > +		voltage1 at 3 {
> > +			reg = <3>;
> > +			ti,fullscale = <6144000>;
> > +			ti,datarate = <475>;
> > +		};
> > +	};
> > +
> > +For the above example, The temperature sensor is enabled as well as
> > +channels 0 and 1 in differential mode, and channel 3 single ended.
> 

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

* Re: [RCF 1/3] hwmon: Add ads1118 driver
  2016-07-16  1:40     ` Guenter Roeck
@ 2016-07-22 14:39       ` Joshua Clayton
  -1 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-22 14:39 UTC (permalink / raw)
  To: Guenter Roeck, Rob Herring, Mark Rutland, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Russell King, Jean Delvare,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon,
	Jonathan Cameron, linux-iio

Greetings Guenter,

Thank you for reviewing my submission.

On 07/15/2016 06:40 PM, Guenter Roeck wrote:
> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>> This driver works with ADS1018, because of code borrowed
>> from asd1015, which is similar, but I can only test ADS1118
>>
>
> Browsing through the datasheet, I think this should probably be implemented
> as iio driver (and iio already has a driver for ads1015).
>
> Jonathan, what do you think ?
>
> Thanks,
> Guenter
>
No response from Jonathan as yet, but I am willing to rework the driver when I
have some time. It might be weeks before I can start, though.

I am not very familiar with the iio subsystem, (actually had never heard of it before
receiving your response). Learning it might be beneficial, as it looks like a good fit
for an in house driver, which I'd like to eventually upstream.

If an hwmon driver is adequate, I have new patches that address kbuild and endianness
issues in the first version.


~Joshua Clayton

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

* [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-22 14:39       ` Joshua Clayton
  0 siblings, 0 replies; 25+ messages in thread
From: Joshua Clayton @ 2016-07-22 14:39 UTC (permalink / raw)
  To: linux-arm-kernel

Greetings Guenter,

Thank you for reviewing my submission.

On 07/15/2016 06:40 PM, Guenter Roeck wrote:
> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>> This driver works with ADS1018, because of code borrowed
>> from asd1015, which is similar, but I can only test ADS1118
>>
>
> Browsing through the datasheet, I think this should probably be implemented
> as iio driver (and iio already has a driver for ads1015).
>
> Jonathan, what do you think ?
>
> Thanks,
> Guenter
>
No response from Jonathan as yet, but I am willing to rework the driver when I
have some time. It might be weeks before I can start, though.

I am not very familiar with the iio subsystem, (actually had never heard of it before
receiving your response). Learning it might be beneficial, as it looks like a good fit
for an in house driver, which I'd like to eventually upstream.

If an hwmon driver is adequate, I have new patches that address kbuild and endianness
issues in the first version.


~Joshua Clayton

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

* ADS1118: hwmon or iio ? [was: Re: [RCF 1/3] hwmon: Add ads1118 driver]
  2016-07-22 14:39       ` Joshua Clayton
@ 2016-07-23  1:20         ` Guenter Roeck
  -1 siblings, 0 replies; 25+ messages in thread
From: Guenter Roeck @ 2016-07-23  1:20 UTC (permalink / raw)
  To: Joshua Clayton, Rob Herring, Mark Rutland, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Russell King, Jean Delvare,
	devicetree, linux-kernel, linux-arm-kernel, linux-hwmon,
	Jonathan Cameron, linux-iio

On 07/22/2016 07:39 AM, Joshua Clayton wrote:
> Greetings Guenter,
>
> Thank you for reviewing my submission.
>
> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>> This driver works with ADS1018, because of code borrowed
>>> from asd1015, which is similar, but I can only test ADS1118
>>>
>>
>> Browsing through the datasheet, I think this should probably be implemented
>> as iio driver (and iio already has a driver for ads1015).
>>
>> Jonathan, what do you think ?
>>
>> Thanks,
>> Guenter
>>
> No response from Jonathan as yet, but I am willing to rework the driver when I
> have some time. It might be weeks before I can start, though.
>

Changing the subject - maybe it helps to get Jonathan's attention.

Guenter


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

* ADS1118: hwmon or iio ? [was: Re: [RCF 1/3] hwmon: Add ads1118 driver]
@ 2016-07-23  1:20         ` Guenter Roeck
  0 siblings, 0 replies; 25+ messages in thread
From: Guenter Roeck @ 2016-07-23  1:20 UTC (permalink / raw)
  To: linux-arm-kernel

On 07/22/2016 07:39 AM, Joshua Clayton wrote:
> Greetings Guenter,
>
> Thank you for reviewing my submission.
>
> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>> This driver works with ADS1018, because of code borrowed
>>> from asd1015, which is similar, but I can only test ADS1118
>>>
>>
>> Browsing through the datasheet, I think this should probably be implemented
>> as iio driver (and iio already has a driver for ads1015).
>>
>> Jonathan, what do you think ?
>>
>> Thanks,
>> Guenter
>>
> No response from Jonathan as yet, but I am willing to rework the driver when I
> have some time. It might be weeks before I can start, though.
>

Changing the subject - maybe it helps to get Jonathan's attention.

Guenter

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

* Re: ADS1118: hwmon or iio ? [was: Re: [RCF 1/3] hwmon: Add ads1118 driver]
  2016-07-23  1:20         ` Guenter Roeck
@ 2016-07-23  5:43           ` Jonathan Cameron
  -1 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-23  5:43 UTC (permalink / raw)
  To: Guenter Roeck, Joshua Clayton, Rob Herring, Mark Rutland,
	Shawn Guo, Sascha Hauer, Fabio Estevam, Russell King,
	Jean Delvare, devicetree, linux-kernel, linux-arm-kernel,
	linux-hwmon, linux-iio

On 23/07/16 03:20, Guenter Roeck wrote:
> On 07/22/2016 07:39 AM, Joshua Clayton wrote:
>> Greetings Guenter,
>>
>> Thank you for reviewing my submission.
>>
>> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>>> This driver works with ADS1018, because of code borrowed
>>>> from asd1015, which is similar, but I can only test ADS1118
>>>>
>>>
>>> Browsing through the datasheet, I think this should probably be implemented
>>> as iio driver (and iio already has a driver for ads1015).
>>>
>>> Jonathan, what do you think ?
>>>
>>> Thanks,
>>> Guenter
>>>
>> No response from Jonathan as yet, but I am willing to rework the driver when I
>> have some time. It might be weeks before I can start, though.
>>
> 
> Changing the subject - maybe it helps to get Jonathan's attention.
Sorry bit of silly travelling for day job over the last few days has
left me rather behind.

Just had my first experience of missing the last train home from the airport
and having to sleep in a 'pod' hotel waiting for the first on in the morning.

'interesting' and I hope I never have to repeat it.

Anyhow, looking fairly quickly through the datasheet I agree with Guenter.
This is a reasonably standard looking ADC with a decent analog front
end.  Whilst they state thermal monitoring in the 'applications' and there
are a few design choices in there (such as really small over voltage
protection) that make sense for thermocouples, there is nothing that
specific.  Also as Guenter has pointed out above we already have some
thermocouple focused chips in IIO.

Sampling is quick enough (up to 860 sps) that you'll probably want
to use the 'buffer' support in IIO rather than just a simple
polled (hwmon like) driver - but that can always come in a follow
up patch.  There is a nice datardy output that can be used to drive
a general purpose trigger (used to sample other devices in parallel
with this one if you want to).

Anyhow, all in, looks like a good general purpose ADC to me.
So would be delighted to have a driver for it in IIO.

You could combine it with the existing ads1015 driver, but
there are probably enough differences to make that fiddly
even using regmap to deal with the different bus (i2c -> spi),
so I think it doesn't really make sense (feel free to tell me
otherwise as I've only glanced at the datasheet!)

Thanks,

Jonathan

> 
> Guenter
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* ADS1118: hwmon or iio ? [was: Re: [RCF 1/3] hwmon: Add ads1118 driver]
@ 2016-07-23  5:43           ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-23  5:43 UTC (permalink / raw)
  To: linux-arm-kernel

On 23/07/16 03:20, Guenter Roeck wrote:
> On 07/22/2016 07:39 AM, Joshua Clayton wrote:
>> Greetings Guenter,
>>
>> Thank you for reviewing my submission.
>>
>> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>>> This driver works with ADS1018, because of code borrowed
>>>> from asd1015, which is similar, but I can only test ADS1118
>>>>
>>>
>>> Browsing through the datasheet, I think this should probably be implemented
>>> as iio driver (and iio already has a driver for ads1015).
>>>
>>> Jonathan, what do you think ?
>>>
>>> Thanks,
>>> Guenter
>>>
>> No response from Jonathan as yet, but I am willing to rework the driver when I
>> have some time. It might be weeks before I can start, though.
>>
> 
> Changing the subject - maybe it helps to get Jonathan's attention.
Sorry bit of silly travelling for day job over the last few days has
left me rather behind.

Just had my first experience of missing the last train home from the airport
and having to sleep in a 'pod' hotel waiting for the first on in the morning.

'interesting' and I hope I never have to repeat it.

Anyhow, looking fairly quickly through the datasheet I agree with Guenter.
This is a reasonably standard looking ADC with a decent analog front
end.  Whilst they state thermal monitoring in the 'applications' and there
are a few design choices in there (such as really small over voltage
protection) that make sense for thermocouples, there is nothing that
specific.  Also as Guenter has pointed out above we already have some
thermocouple focused chips in IIO.

Sampling is quick enough (up to 860 sps) that you'll probably want
to use the 'buffer' support in IIO rather than just a simple
polled (hwmon like) driver - but that can always come in a follow
up patch.  There is a nice datardy output that can be used to drive
a general purpose trigger (used to sample other devices in parallel
with this one if you want to).

Anyhow, all in, looks like a good general purpose ADC to me.
So would be delighted to have a driver for it in IIO.

You could combine it with the existing ads1015 driver, but
there are probably enough differences to make that fiddly
even using regmap to deal with the different bus (i2c -> spi),
so I think it doesn't really make sense (feel free to tell me
otherwise as I've only glanced at the datasheet!)

Thanks,

Jonathan

> 
> Guenter
> 
> -- 
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* Re: [RCF 1/3] hwmon: Add ads1118 driver
  2016-07-22 14:39       ` Joshua Clayton
@ 2016-07-23  6:02         ` Jonathan Cameron
  -1 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-23  6:02 UTC (permalink / raw)
  To: Joshua Clayton, Guenter Roeck, Rob Herring, Mark Rutland,
	Shawn Guo, Sascha Hauer, Fabio Estevam, Russell King,
	Jean Delvare, devicetree, linux-kernel, linux-arm-kernel,
	linux-hwmon, linux-iio

On 22/07/16 16:39, Joshua Clayton wrote:
> Greetings Guenter,
> 
> Thank you for reviewing my submission.
> 
> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>> This driver works with ADS1018, because of code borrowed
>>> from asd1015, which is similar, but I can only test ADS1118
>>>
>>
>> Browsing through the datasheet, I think this should probably be implemented
>> as iio driver (and iio already has a driver for ads1015).
>>
>> Jonathan, what do you think ?
>>
>> Thanks,
>> Guenter
>>
> No response from Jonathan as yet, but I am willing to rework the driver when I
> have some time. It might be weeks before I can start, though.
Sorry, made week and I thought I'd have time to get reviews done in evenings
so didn't look at anything last weekend.   Catching up on train home this morning.
> 
> I am not very familiar with the iio subsystem, (actually had never heard of it before
> receiving your response). Learning it might be beneficial, as it looks like a good fit
> for an in house driver, which I'd like to eventually upstream.
Cool
> 
> If an hwmon driver is adequate, I have new patches that address kbuild and endianness
> issues in the first version.
I'd flip it over to IIO.  If you keep to just the simple bits it'll end up pretty
simple to do.
> 
> 
> ~Joshua Clayton
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 


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

* [RCF 1/3] hwmon: Add ads1118 driver
@ 2016-07-23  6:02         ` Jonathan Cameron
  0 siblings, 0 replies; 25+ messages in thread
From: Jonathan Cameron @ 2016-07-23  6:02 UTC (permalink / raw)
  To: linux-arm-kernel

On 22/07/16 16:39, Joshua Clayton wrote:
> Greetings Guenter,
> 
> Thank you for reviewing my submission.
> 
> On 07/15/2016 06:40 PM, Guenter Roeck wrote:
>> On 07/15/2016 05:18 PM, Joshua Clayton wrote:
>>> Add new driver for Texas Instruments ADS1118 and and ADS1018.
>>> This driver works with ADS1018, because of code borrowed
>>> from asd1015, which is similar, but I can only test ADS1118
>>>
>>
>> Browsing through the datasheet, I think this should probably be implemented
>> as iio driver (and iio already has a driver for ads1015).
>>
>> Jonathan, what do you think ?
>>
>> Thanks,
>> Guenter
>>
> No response from Jonathan as yet, but I am willing to rework the driver when I
> have some time. It might be weeks before I can start, though.
Sorry, made week and I thought I'd have time to get reviews done in evenings
so didn't look at anything last weekend.   Catching up on train home this morning.
> 
> I am not very familiar with the iio subsystem, (actually had never heard of it before
> receiving your response). Learning it might be beneficial, as it looks like a good fit
> for an in house driver, which I'd like to eventually upstream.
Cool
> 
> If an hwmon driver is adequate, I have new patches that address kbuild and endianness
> issues in the first version.
I'd flip it over to IIO.  If you keep to just the simple bits it'll end up pretty
simple to do.
> 
> 
> ~Joshua Clayton
> --
> To unsubscribe from this list: send the line "unsubscribe linux-iio" in
> the body of a message to majordomo at vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> 

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

end of thread, other threads:[~2016-07-23 10:55 UTC | newest]

Thread overview: 25+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-07-16  0:18 [RCF 0/3] hwmon: add driver for TI ADS1118 Joshua Clayton
2016-07-16  0:18 ` Joshua Clayton
2016-07-16  0:18 ` [RCF 1/3] hwmon: Add ads1118 driver Joshua Clayton
2016-07-16  0:18   ` Joshua Clayton
2016-07-16  1:40   ` Guenter Roeck
2016-07-16  1:40     ` Guenter Roeck
2016-07-22 14:39     ` Joshua Clayton
2016-07-22 14:39       ` Joshua Clayton
2016-07-23  1:20       ` ADS1118: hwmon or iio ? [was: Re: [RCF 1/3] hwmon: Add ads1118 driver] Guenter Roeck
2016-07-23  1:20         ` Guenter Roeck
2016-07-23  5:43         ` Jonathan Cameron
2016-07-23  5:43           ` Jonathan Cameron
2016-07-23  6:02       ` [RCF 1/3] hwmon: Add ads1118 driver Jonathan Cameron
2016-07-23  6:02         ` Jonathan Cameron
2016-07-16 17:22   ` kbuild test robot
2016-07-16 17:22     ` kbuild test robot
2016-07-16 17:22     ` kbuild test robot
2016-07-16  0:18 ` [RCF 2/3] hwmon: Document bindings for ads1118 adc driver Joshua Clayton
2016-07-16  0:18   ` Joshua Clayton
2016-07-17 20:34   ` Rob Herring
2016-07-17 20:34     ` Rob Herring
2016-07-18 12:51     ` Joshua Clayton
2016-07-18 12:51       ` Joshua Clayton
2016-07-16  0:18 ` [RCF 3/3] ARM: imx6q-evi: ads ads1118 support Joshua Clayton
2016-07-16  0:18   ` Joshua Clayton

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.