Linux-IIO Archive on lore.kernel.org
 help / color / Atom feed
* [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header
@ 2019-07-18  6:27 Beniamin Bia
  2019-07-18  6:27 ` [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file Beniamin Bia
                   ` (4 more replies)
  0 siblings, 5 replies; 14+ messages in thread
From: Beniamin Bia @ 2019-07-18  6:27 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, biabeniamin, Beniamin Bia

The common channel definition which are going to be used by both core
file but also spi file, were moved in header file. Some devices have
different channel definitions when are used in software mode, feature
available only with spi, and those definitions will be added in spi file.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v2:
-nothing changed

 drivers/iio/adc/ad7606.c | 23 -----------------------
 drivers/iio/adc/ad7606.h | 23 +++++++++++++++++++++++
 2 files changed, 23 insertions(+), 23 deletions(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index aba0fd123a51..a49dc106a21c 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -308,29 +308,6 @@ static const struct attribute_group ad7606_attribute_group_range = {
 	.attrs = ad7606_attributes_range,
 };
 
-#define AD760X_CHANNEL(num, mask) {				\
-		.type = IIO_VOLTAGE,				\
-		.indexed = 1,					\
-		.channel = num,					\
-		.address = num,					\
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
-		.info_mask_shared_by_all = mask,		\
-		.scan_index = num,				\
-		.scan_type = {					\
-			.sign = 's',				\
-			.realbits = 16,				\
-			.storagebits = 16,			\
-			.endianness = IIO_CPU,			\
-		},						\
-}
-
-#define AD7605_CHANNEL(num)	\
-	AD760X_CHANNEL(num, 0)
-
-#define AD7606_CHANNEL(num)	\
-	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
-
 static const struct iio_chan_spec ad7605_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(4),
 	AD7605_CHANNEL(0),
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
index d8a509c2c428..d547e88f4c9d 100644
--- a/drivers/iio/adc/ad7606.h
+++ b/drivers/iio/adc/ad7606.h
@@ -8,6 +8,29 @@
 #ifndef IIO_ADC_AD7606_H_
 #define IIO_ADC_AD7606_H_
 
+#define AD760X_CHANNEL(num, mask) {				\
+		.type = IIO_VOLTAGE,				\
+		.indexed = 1,					\
+		.channel = num,					\
+		.address = num,					\
+		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
+		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
+		.info_mask_shared_by_all = mask,		\
+		.scan_index = num,				\
+		.scan_type = {					\
+			.sign = 's',				\
+			.realbits = 16,				\
+			.storagebits = 16,			\
+			.endianness = IIO_CPU,			\
+		},						\
+}
+
+#define AD7605_CHANNEL(num)	\
+	AD760X_CHANNEL(num, 0)
+
+#define AD7606_CHANNEL(num)	\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
 /**
  * struct ad7606_chip_info - chip specific information
  * @channels:		channel specification
-- 
2.17.1


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

* [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file
  2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
@ 2019-07-18  6:27 ` Beniamin Bia
  2019-07-28  7:57   ` Jonathan Cameron
  2019-07-18  6:27 ` [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset Beniamin Bia
                   ` (3 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-07-18  6:27 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, biabeniamin, Beniamin Bia

Because software mode and register access are only available in spi, they
were moved in spi file and are accessbile via bops structure.
The write_os/scale will be overwritten by sw_mode_config function.
This patch was made in order to support devices in software mode without
making the driver dependent to spi and increase the abstraction of the
core.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v2:
-nothing changed

 drivers/iio/adc/ad7606.c | 20 ++------------------
 drivers/iio/adc/ad7606.h | 12 +++---------
 2 files changed, 5 insertions(+), 27 deletions(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index a49dc106a21c..9eec3db01a17 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -597,7 +597,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 	st->write_scale = ad7606_write_scale_hw;
 	st->write_os = ad7606_write_os_hw;
 
-	if (st->chip_info->sw_mode_config)
+	if (st->bops->sw_mode_config)
 		st->sw_mode_en = device_property_present(st->dev,
 							 "adi,sw-mode");
 
@@ -606,23 +606,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 		memset32(st->range, 2, ARRAY_SIZE(st->range));
 		indio_dev->info = &ad7606_info_os_and_range;
 
-		/*
-		 * In software mode, the range gpio has no longer its function.
-		 * Instead, the scale can be configured individually for each
-		 * channel from the range registers.
-		 */
-		if (st->chip_info->write_scale_sw)
-			st->write_scale = st->chip_info->write_scale_sw;
-
-		/*
-		 * In software mode, the oversampling is no longer configured
-		 * with GPIO pins. Instead, the oversampling can be configured
-		 * in configuratiion register.
-		 */
-		if (st->chip_info->write_os_sw)
-			st->write_os = st->chip_info->write_os_sw;
-
-		ret = st->chip_info->sw_mode_config(indio_dev);
+		ret = st->bops->sw_mode_config(indio_dev);
 		if (ret < 0)
 			return ret;
 	}
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
index d547e88f4c9d..a6aac33aa33c 100644
--- a/drivers/iio/adc/ad7606.h
+++ b/drivers/iio/adc/ad7606.h
@@ -39,12 +39,6 @@
  *			oversampling ratios.
  * @oversampling_num	number of elements stored in oversampling_avail array
  * @os_req_reset	some devices require a reset to update oversampling
- * @write_scale_sw	pointer to the function which writes the scale via spi
-			in software mode
- * @write_os_sw		pointer to the function which writes the os via spi
-			in software mode
- * @sw_mode_config:	pointer to a function which configured the device
- *			for software mode
  */
 struct ad7606_chip_info {
 	const struct iio_chan_spec	*channels;
@@ -52,9 +46,6 @@ struct ad7606_chip_info {
 	const unsigned int		*oversampling_avail;
 	unsigned int			oversampling_num;
 	bool				os_req_reset;
-	int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val);
-	int (*write_os_sw)(struct iio_dev *indio_dev, int val);
-	int (*sw_mode_config)(struct iio_dev *indio_dev);
 };
 
 /**
@@ -124,10 +115,13 @@ struct ad7606_state {
 /**
  * struct ad7606_bus_ops - driver bus operations
  * @read_block		function pointer for reading blocks of data
+ * @sw_mode_config:	pointer to a function which configured the device
+ *			for software mode
  */
 struct ad7606_bus_ops {
 	/* more methods added in future? */
 	int (*read_block)(struct device *dev, int num, void *data);
+	int (*sw_mode_config)(struct iio_dev *indio_dev);
 };
 
 int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
-- 
2.17.1


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

* [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset
  2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
  2019-07-18  6:27 ` [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file Beniamin Bia
@ 2019-07-18  6:27 ` Beniamin Bia
  2019-07-28  7:57   ` Jonathan Cameron
  2019-07-18  6:27 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-07-18  6:27 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, biabeniamin, Beniamin Bia

According to datasheet, ad7616 require at least 15ms after a restart
to fully reconfigure and being able to receive new commands via spi.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v2:
-nothing changed

 drivers/iio/adc/ad7606.c | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index 9eec3db01a17..a6034cf7238a 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -594,6 +594,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 	if (ret)
 		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
 
+	/* AD7616 requires al least 15ms to reconfigure after a reset */
+	if (msleep_interruptible(15))
+		return -ERESTARTSYS;
+
 	st->write_scale = ad7606_write_scale_hw;
 	st->write_os = ad7606_write_os_hw;
 
-- 
2.17.1


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

* [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
  2019-07-18  6:27 ` [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file Beniamin Bia
  2019-07-18  6:27 ` [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset Beniamin Bia
@ 2019-07-18  6:27 ` Beniamin Bia
  2019-07-28  7:58   ` Jonathan Cameron
  2019-07-18  6:27 ` [PATCH 5/5] iio: adc: ad7606: Add debug " Beniamin Bia
  2019-07-28  7:57 ` [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Jonathan Cameron
  4 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-07-18  6:27 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, biabeniamin, Beniamin Bia

Support for ad7616 running in software was added. In order
to activate the software mode, HW_RNGSEL pins must be pulled low.
Oversampling and input ranges are now configured in corresponding
registers. Ad7616 has multiple scale options when it is configured
in software mode.
Also, in order to support multiple devices in software mode, the spi
calculation of registers address must be generic. Because
the length of address and bit which specifies the read/write operation is
different for every device, calculation of address was made generic.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
---
Changes in v2:
-aditional comments added to explain calculation of range register address
-range macros were simplified

 drivers/iio/adc/ad7606.c     |   9 ++
 drivers/iio/adc/ad7606.h     |  38 ++++++--
 drivers/iio/adc/ad7606_spi.c | 175 ++++++++++++++++++++++++++++++++++-
 3 files changed, 213 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index a6034cf7238a..be2330c8b277 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
 	152588, 305176
 };
 
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+	76293, 152588, 305176
+};
+
 static const unsigned int ad7606_oversampling_avail[7] = {
 	1, 2, 4, 8, 16, 32, 64,
 };
@@ -606,6 +611,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 							 "adi,sw-mode");
 
 	if (st->sw_mode_en) {
+		/* Scale of 0.076293 is only available in sw mode */
+		st->scale_avail = ad7616_sw_scale_avail;
+		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
 		/* After reset, in software mode, ±10 V is set by default */
 		memset32(st->range, 2, ARRAY_SIZE(st->range));
 		indio_dev->info = &ad7606_info_os_and_range;
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
index a6aac33aa33c..eeaaa8b905db 100644
--- a/drivers/iio/adc/ad7606.h
+++ b/drivers/iio/adc/ad7606.h
@@ -8,14 +8,14 @@
 #ifndef IIO_ADC_AD7606_H_
 #define IIO_ADC_AD7606_H_
 
-#define AD760X_CHANNEL(num, mask) {				\
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
 		.type = IIO_VOLTAGE,				\
 		.indexed = 1,					\
 		.channel = num,					\
 		.address = num,					\
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
-		.info_mask_shared_by_all = mask,		\
+		.info_mask_separate = mask_sep,			\
+		.info_mask_shared_by_type = mask_type,		\
+		.info_mask_shared_by_all = mask_all,		\
 		.scan_index = num,				\
 		.scan_type = {					\
 			.sign = 's',				\
@@ -25,11 +25,18 @@
 		},						\
 }
 
-#define AD7605_CHANNEL(num)	\
-	AD760X_CHANNEL(num, 0)
+#define AD7605_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE), 0)
 
-#define AD7606_CHANNEL(num)	\
-	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+#define AD7606_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE),		\
+		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+#define AD7616_CHANNEL(num)	\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
 
 /**
  * struct ad7606_chip_info - chip specific information
@@ -77,6 +84,7 @@ struct ad7606_chip_info {
  * @complete		completion to indicate end of conversion
  * @trig		The IIO trigger associated with the device.
  * @data		buffer for reading data from the device
+ * @d16			be16 buffer for reading data from the device
  */
 struct ad7606_state {
 	struct device			*dev;
@@ -110,6 +118,7 @@ struct ad7606_state {
 	 * 16 * 16-bit samples + 64-bit timestamp
 	 */
 	unsigned short			data[20] ____cacheline_aligned;
+	__be16				d16[2];
 };
 
 /**
@@ -117,11 +126,24 @@ struct ad7606_state {
  * @read_block		function pointer for reading blocks of data
  * @sw_mode_config:	pointer to a function which configured the device
  *			for software mode
+ * @reg_read	function pointer for reading spi register
+ * @reg_write	function pointer for writing spi register
+ * @write_mask	function pointer for write spi register with mask
+ * @rd_wr_cmd	pointer to the function which calculates the spi address
  */
 struct ad7606_bus_ops {
 	/* more methods added in future? */
 	int (*read_block)(struct device *dev, int num, void *data);
 	int (*sw_mode_config)(struct iio_dev *indio_dev);
+	int (*reg_read)(struct ad7606_state *st, unsigned int addr);
+	int (*reg_write)(struct ad7606_state *st,
+				unsigned int addr,
+				unsigned int val);
+	int (*write_mask)(struct ad7606_state *st,
+				 unsigned int addr,
+				 unsigned long mask,
+				 unsigned int val);
+	u16 (*rd_wr_cmd)(int addr, char isWriteOp);
 };
 
 int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index b7faef69a58f..98ed52b74507 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -15,6 +15,51 @@
 
 #define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */
 
+#define AD7616_CONFIGURATION_REGISTER	0x02
+#define AD7616_OS_MASK			GENMASK(4, 2)
+#define AD7616_BURST_MODE		BIT(6)
+#define AD7616_SEQEN_MODE		BIT(5)
+#define AD7616_RANGE_CH_A_ADDR_OFF	0x04
+#define AD7616_RANGE_CH_B_ADDR_OFF	0x06
+/*
+ * Range of channels from a group are stored in 2 registers.
+ * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
+ * For channels from second group(8-15) the order is the same, only with
+ * an offset of 2 for register address.
+ */
+#define AD7616_RANGE_CH_ADDR(ch)	((ch) >> 2)
+/* The range of the channel is stored on 2 bits*/
+#define AD7616_RANGE_CH_MSK(ch)		(0b11 << (((ch) & 0b11) * 2))
+#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << ((((ch) & 0b11)) * 2))
+static const struct iio_chan_spec ad7616_sw_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(16),
+	AD7616_CHANNEL(0),
+	AD7616_CHANNEL(1),
+	AD7616_CHANNEL(2),
+	AD7616_CHANNEL(3),
+	AD7616_CHANNEL(4),
+	AD7616_CHANNEL(5),
+	AD7616_CHANNEL(6),
+	AD7616_CHANNEL(7),
+	AD7616_CHANNEL(8),
+	AD7616_CHANNEL(9),
+	AD7616_CHANNEL(10),
+	AD7616_CHANNEL(11),
+	AD7616_CHANNEL(12),
+	AD7616_CHANNEL(13),
+	AD7616_CHANNEL(14),
+	AD7616_CHANNEL(15),
+};
+
+static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
+{
+	/*
+	 * The address of register consist of one w/r bit
+	 * 6 bits of address followed by one reserved bit.
+	 */
+	return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
+}
+
 static int ad7606_spi_read_block(struct device *dev,
 				 int count, void *buf)
 {
@@ -35,17 +80,145 @@ static int ad7606_spi_read_block(struct device *dev,
 	return 0;
 }
 
+static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->d16[0],
+			.len = 2,
+			.cs_change = 0,
+		}, {
+			.rx_buf = &st->d16[1],
+			.len = 2,
+		},
+	};
+	int ret;
+
+	st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
+
+	ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+	if (ret < 0)
+		return ret;
+
+	return be16_to_cpu(st->d16[1]);
+}
+
+static int ad7606_spi_reg_write(struct ad7606_state *st,
+				unsigned int addr,
+				unsigned int val)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+
+	st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
+				  (val & 0x1FF));
+
+	return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
+}
+
+static int ad7606_spi_write_mask(struct ad7606_state *st,
+				 unsigned int addr,
+				 unsigned long mask,
+				 unsigned int val)
+{
+	int readval;
+
+	readval = st->bops->reg_read(st, addr);
+	if (readval < 0)
+		return readval;
+
+	readval &= ~mask;
+	readval |= val;
+
+	return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+	unsigned int ch_addr, mode, ch_index;
+
+
+	/*
+	 * Ad7616 has 16 channels divided in group A and group B.
+	 * The range of channels from A are stored in registers with address 4
+	 * while channels from B are stored in register with address 6.
+	 * The last bit from channels determines if it is from group A or B
+	 * because the order of channels in iio is 0A, 0B, 1A, 1B...
+	 */
+	ch_index = ch >> 1;
+
+	ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
+
+	if ((ch & 0x1) == 0) /* channel A */
+		ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
+	else	/* channel B */
+		ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
+
+	/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
+	mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
+	return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
+				     mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
+				     AD7616_OS_MASK, val << 2);
+}
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	/*
+	 * Scale can be configured individually for each channel
+	 * in software mode.
+	 */
+	indio_dev->channels = ad7616_sw_channels;
+
+	st->write_scale = ad7616_write_scale_sw;
+	st->write_os = &ad7616_write_os_sw;
+
+	/* Activate Burst mode and SEQEN MODE */
+	return st->bops->write_mask(st,
+			      AD7616_CONFIGURATION_REGISTER,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
 static const struct ad7606_bus_ops ad7606_spi_bops = {
 	.read_block = ad7606_spi_read_block,
 };
 
+static const struct ad7606_bus_ops ad7616_spi_bops = {
+	.read_block = ad7606_spi_read_block,
+	.reg_read = ad7606_spi_reg_read,
+	.reg_write = ad7606_spi_reg_write,
+	.write_mask = ad7606_spi_write_mask,
+	.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+	.sw_mode_config = ad7616_sw_mode_config,
+};
+
 static int ad7606_spi_probe(struct spi_device *spi)
 {
 	const struct spi_device_id *id = spi_get_device_id(spi);
+	const struct ad7606_bus_ops *bops;
+
+	switch (id->driver_data) {
+	case ID_AD7616:
+		bops = &ad7616_spi_bops;
+		break;
+	default:
+		bops = &ad7606_spi_bops;
+		break;
+	}
 
 	return ad7606_probe(&spi->dev, spi->irq, NULL,
 			    id->name, id->driver_data,
-			    &ad7606_spi_bops);
+			    bops);
 }
 
 static const struct spi_device_id ad7606_id_table[] = {
-- 
2.17.1


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

* [PATCH 5/5] iio: adc: ad7606: Add debug mode for ad7616
  2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
                   ` (2 preceding siblings ...)
  2019-07-18  6:27 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
@ 2019-07-18  6:27 ` " Beniamin Bia
  2019-07-28  7:58   ` Jonathan Cameron
  2019-07-28  7:57 ` [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Jonathan Cameron
  4 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-07-18  6:27 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, biabeniamin, Beniamin Bia

Support for register access was added for devices which have software
mode.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Acked-by: Jonathan Cameron <jic23@kernel.org>
---
Changes in v2:
-nothing changed

 drivers/iio/adc/ad7606.c | 33 ++++++++++++++++++++++++++++++++-
 1 file changed, 32 insertions(+), 1 deletion(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index be2330c8b277..ed2d08437e5d 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -60,6 +60,29 @@ static int ad7606_reset(struct ad7606_state *st)
 	return -ENODEV;
 }
 
+static int ad7606_reg_access(struct iio_dev *indio_dev,
+			     unsigned int reg,
+			     unsigned int writeval,
+			     unsigned int *readval)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+	int ret;
+
+	mutex_lock(&st->lock);
+	if (readval) {
+		ret = st->bops->reg_read(st, reg);
+		if (ret < 0)
+			goto err_unlock;
+		*readval = ret;
+		ret = 0;
+	} else {
+		ret = st->bops->reg_write(st, reg, writeval);
+	}
+err_unlock:
+	mutex_unlock(&st->lock);
+	return ret;
+}
+
 static int ad7606_read_samples(struct ad7606_state *st)
 {
 	unsigned int num = st->chip_info->num_channels;
@@ -501,6 +524,14 @@ static const struct iio_info ad7606_info_os_and_range = {
 	.validate_trigger = &ad7606_validate_trigger,
 };
 
+static const struct iio_info ad7606_info_os_range_and_debug = {
+	.read_raw = &ad7606_read_raw,
+	.write_raw = &ad7606_write_raw,
+	.debugfs_reg_access = &ad7606_reg_access,
+	.attrs = &ad7606_attribute_group_os_and_range,
+	.validate_trigger = &ad7606_validate_trigger,
+};
+
 static const struct iio_info ad7606_info_os = {
 	.read_raw = &ad7606_read_raw,
 	.write_raw = &ad7606_write_raw,
@@ -617,7 +648,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 
 		/* After reset, in software mode, ±10 V is set by default */
 		memset32(st->range, 2, ARRAY_SIZE(st->range));
-		indio_dev->info = &ad7606_info_os_and_range;
+		indio_dev->info = &ad7606_info_os_range_and_debug;
 
 		ret = st->bops->sw_mode_config(indio_dev);
 		if (ret < 0)
-- 
2.17.1


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

* Re: [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header
  2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
                   ` (3 preceding siblings ...)
  2019-07-18  6:27 ` [PATCH 5/5] iio: adc: ad7606: Add debug " Beniamin Bia
@ 2019-07-28  7:57 ` Jonathan Cameron
  4 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-28  7:57 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio, biabeniamin

On Thu, 18 Jul 2019 09:27:30 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> The common channel definition which are going to be used by both core
> file but also spi file, were moved in header file. Some devices have
> different channel definitions when are used in software mode, feature
> available only with spi, and those definitions will be added in spi file.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
Applied to the togreg branch of iio.git and pushed out as testing for the
autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v2:
> -nothing changed
> 
>  drivers/iio/adc/ad7606.c | 23 -----------------------
>  drivers/iio/adc/ad7606.h | 23 +++++++++++++++++++++++
>  2 files changed, 23 insertions(+), 23 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index aba0fd123a51..a49dc106a21c 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -308,29 +308,6 @@ static const struct attribute_group ad7606_attribute_group_range = {
>  	.attrs = ad7606_attributes_range,
>  };
>  
> -#define AD760X_CHANNEL(num, mask) {				\
> -		.type = IIO_VOLTAGE,				\
> -		.indexed = 1,					\
> -		.channel = num,					\
> -		.address = num,					\
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
> -		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> -		.info_mask_shared_by_all = mask,		\
> -		.scan_index = num,				\
> -		.scan_type = {					\
> -			.sign = 's',				\
> -			.realbits = 16,				\
> -			.storagebits = 16,			\
> -			.endianness = IIO_CPU,			\
> -		},						\
> -}
> -
> -#define AD7605_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, 0)
> -
> -#define AD7606_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> -
>  static const struct iio_chan_spec ad7605_channels[] = {
>  	IIO_CHAN_SOFT_TIMESTAMP(4),
>  	AD7605_CHANNEL(0),
> diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
> index d8a509c2c428..d547e88f4c9d 100644
> --- a/drivers/iio/adc/ad7606.h
> +++ b/drivers/iio/adc/ad7606.h
> @@ -8,6 +8,29 @@
>  #ifndef IIO_ADC_AD7606_H_
>  #define IIO_ADC_AD7606_H_
>  
> +#define AD760X_CHANNEL(num, mask) {				\
> +		.type = IIO_VOLTAGE,				\
> +		.indexed = 1,					\
> +		.channel = num,					\
> +		.address = num,					\
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
> +		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> +		.info_mask_shared_by_all = mask,		\
> +		.scan_index = num,				\
> +		.scan_type = {					\
> +			.sign = 's',				\
> +			.realbits = 16,				\
> +			.storagebits = 16,			\
> +			.endianness = IIO_CPU,			\
> +		},						\
> +}
> +
> +#define AD7605_CHANNEL(num)	\
> +	AD760X_CHANNEL(num, 0)
> +
> +#define AD7606_CHANNEL(num)	\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +
>  /**
>   * struct ad7606_chip_info - chip specific information
>   * @channels:		channel specification


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

* Re: [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file
  2019-07-18  6:27 ` [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file Beniamin Bia
@ 2019-07-28  7:57   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-28  7:57 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio, biabeniamin

On Thu, 18 Jul 2019 09:27:31 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> Because software mode and register access are only available in spi, they
> were moved in spi file and are accessbile via bops structure.
> The write_os/scale will be overwritten by sw_mode_config function.
> This patch was made in order to support devices in software mode without
> making the driver dependent to spi and increase the abstraction of the
> core.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
Applied.

thanks,

Jonathan

> ---
> Changes in v2:
> -nothing changed
> 
>  drivers/iio/adc/ad7606.c | 20 ++------------------
>  drivers/iio/adc/ad7606.h | 12 +++---------
>  2 files changed, 5 insertions(+), 27 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index a49dc106a21c..9eec3db01a17 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -597,7 +597,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  	st->write_scale = ad7606_write_scale_hw;
>  	st->write_os = ad7606_write_os_hw;
>  
> -	if (st->chip_info->sw_mode_config)
> +	if (st->bops->sw_mode_config)
>  		st->sw_mode_en = device_property_present(st->dev,
>  							 "adi,sw-mode");
>  
> @@ -606,23 +606,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  		memset32(st->range, 2, ARRAY_SIZE(st->range));
>  		indio_dev->info = &ad7606_info_os_and_range;
>  
> -		/*
> -		 * In software mode, the range gpio has no longer its function.
> -		 * Instead, the scale can be configured individually for each
> -		 * channel from the range registers.
> -		 */
> -		if (st->chip_info->write_scale_sw)
> -			st->write_scale = st->chip_info->write_scale_sw;
> -
> -		/*
> -		 * In software mode, the oversampling is no longer configured
> -		 * with GPIO pins. Instead, the oversampling can be configured
> -		 * in configuratiion register.
> -		 */
> -		if (st->chip_info->write_os_sw)
> -			st->write_os = st->chip_info->write_os_sw;
> -
> -		ret = st->chip_info->sw_mode_config(indio_dev);
> +		ret = st->bops->sw_mode_config(indio_dev);
>  		if (ret < 0)
>  			return ret;
>  	}
> diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
> index d547e88f4c9d..a6aac33aa33c 100644
> --- a/drivers/iio/adc/ad7606.h
> +++ b/drivers/iio/adc/ad7606.h
> @@ -39,12 +39,6 @@
>   *			oversampling ratios.
>   * @oversampling_num	number of elements stored in oversampling_avail array
>   * @os_req_reset	some devices require a reset to update oversampling
> - * @write_scale_sw	pointer to the function which writes the scale via spi
> -			in software mode
> - * @write_os_sw		pointer to the function which writes the os via spi
> -			in software mode
> - * @sw_mode_config:	pointer to a function which configured the device
> - *			for software mode
>   */
>  struct ad7606_chip_info {
>  	const struct iio_chan_spec	*channels;
> @@ -52,9 +46,6 @@ struct ad7606_chip_info {
>  	const unsigned int		*oversampling_avail;
>  	unsigned int			oversampling_num;
>  	bool				os_req_reset;
> -	int (*write_scale_sw)(struct iio_dev *indio_dev, int ch, int val);
> -	int (*write_os_sw)(struct iio_dev *indio_dev, int val);
> -	int (*sw_mode_config)(struct iio_dev *indio_dev);
>  };
>  
>  /**
> @@ -124,10 +115,13 @@ struct ad7606_state {
>  /**
>   * struct ad7606_bus_ops - driver bus operations
>   * @read_block		function pointer for reading blocks of data
> + * @sw_mode_config:	pointer to a function which configured the device
> + *			for software mode
>   */
>  struct ad7606_bus_ops {
>  	/* more methods added in future? */
>  	int (*read_block)(struct device *dev, int num, void *data);
> +	int (*sw_mode_config)(struct iio_dev *indio_dev);
>  };
>  
>  int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,


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

* Re: [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset
  2019-07-18  6:27 ` [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset Beniamin Bia
@ 2019-07-28  7:57   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-28  7:57 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio, biabeniamin

On Thu, 18 Jul 2019 09:27:32 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> According to datasheet, ad7616 require at least 15ms after a restart
> to fully reconfigure and being able to receive new commands via spi.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>
Applied.

Thanks,

J

> ---
> Changes in v2:
> -nothing changed
> 
>  drivers/iio/adc/ad7606.c | 4 ++++
>  1 file changed, 4 insertions(+)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index 9eec3db01a17..a6034cf7238a 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -594,6 +594,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  	if (ret)
>  		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
>  
> +	/* AD7616 requires al least 15ms to reconfigure after a reset */
> +	if (msleep_interruptible(15))
> +		return -ERESTARTSYS;
> +
>  	st->write_scale = ad7606_write_scale_hw;
>  	st->write_os = ad7606_write_os_hw;
>  


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

* Re: [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-07-18  6:27 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
@ 2019-07-28  7:58   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-28  7:58 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio, biabeniamin

On Thu, 18 Jul 2019 09:27:33 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> Support for ad7616 running in software was added. In order
> to activate the software mode, HW_RNGSEL pins must be pulled low.
> Oversampling and input ranges are now configured in corresponding
> registers. Ad7616 has multiple scale options when it is configured
> in software mode.
> Also, in order to support multiple devices in software mode, the spi
> calculation of registers address must be generic. Because
> the length of address and bit which specifies the read/write operation is
> different for every device, calculation of address was made generic.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Applied.

Thanks,

J 

> ---
> Changes in v2:
> -aditional comments added to explain calculation of range register address
> -range macros were simplified
> 
>  drivers/iio/adc/ad7606.c     |   9 ++
>  drivers/iio/adc/ad7606.h     |  38 ++++++--
>  drivers/iio/adc/ad7606_spi.c | 175 ++++++++++++++++++++++++++++++++++-
>  3 files changed, 213 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index a6034cf7238a..be2330c8b277 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
>  	152588, 305176
>  };
>  
> +
> +static const unsigned int ad7616_sw_scale_avail[3] = {
> +	76293, 152588, 305176
> +};
> +
>  static const unsigned int ad7606_oversampling_avail[7] = {
>  	1, 2, 4, 8, 16, 32, 64,
>  };
> @@ -606,6 +611,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  							 "adi,sw-mode");
>  
>  	if (st->sw_mode_en) {
> +		/* Scale of 0.076293 is only available in sw mode */
> +		st->scale_avail = ad7616_sw_scale_avail;
> +		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
> +
>  		/* After reset, in software mode, ±10 V is set by default */
>  		memset32(st->range, 2, ARRAY_SIZE(st->range));
>  		indio_dev->info = &ad7606_info_os_and_range;
> diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
> index a6aac33aa33c..eeaaa8b905db 100644
> --- a/drivers/iio/adc/ad7606.h
> +++ b/drivers/iio/adc/ad7606.h
> @@ -8,14 +8,14 @@
>  #ifndef IIO_ADC_AD7606_H_
>  #define IIO_ADC_AD7606_H_
>  
> -#define AD760X_CHANNEL(num, mask) {				\
> +#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
>  		.type = IIO_VOLTAGE,				\
>  		.indexed = 1,					\
>  		.channel = num,					\
>  		.address = num,					\
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
> -		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> -		.info_mask_shared_by_all = mask,		\
> +		.info_mask_separate = mask_sep,			\
> +		.info_mask_shared_by_type = mask_type,		\
> +		.info_mask_shared_by_all = mask_all,		\
>  		.scan_index = num,				\
>  		.scan_type = {					\
>  			.sign = 's',				\
> @@ -25,11 +25,18 @@
>  		},						\
>  }
>  
> -#define AD7605_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, 0)
> +#define AD7605_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE), 0)
>  
> -#define AD7606_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +#define AD7606_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE),		\
> +		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +
> +#define AD7616_CHANNEL(num)	\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
> +		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
>  
>  /**
>   * struct ad7606_chip_info - chip specific information
> @@ -77,6 +84,7 @@ struct ad7606_chip_info {
>   * @complete		completion to indicate end of conversion
>   * @trig		The IIO trigger associated with the device.
>   * @data		buffer for reading data from the device
> + * @d16			be16 buffer for reading data from the device
>   */
>  struct ad7606_state {
>  	struct device			*dev;
> @@ -110,6 +118,7 @@ struct ad7606_state {
>  	 * 16 * 16-bit samples + 64-bit timestamp
>  	 */
>  	unsigned short			data[20] ____cacheline_aligned;
> +	__be16				d16[2];
>  };
>  
>  /**
> @@ -117,11 +126,24 @@ struct ad7606_state {
>   * @read_block		function pointer for reading blocks of data
>   * @sw_mode_config:	pointer to a function which configured the device
>   *			for software mode
> + * @reg_read	function pointer for reading spi register
> + * @reg_write	function pointer for writing spi register
> + * @write_mask	function pointer for write spi register with mask
> + * @rd_wr_cmd	pointer to the function which calculates the spi address
>   */
>  struct ad7606_bus_ops {
>  	/* more methods added in future? */
>  	int (*read_block)(struct device *dev, int num, void *data);
>  	int (*sw_mode_config)(struct iio_dev *indio_dev);
> +	int (*reg_read)(struct ad7606_state *st, unsigned int addr);
> +	int (*reg_write)(struct ad7606_state *st,
> +				unsigned int addr,
> +				unsigned int val);
> +	int (*write_mask)(struct ad7606_state *st,
> +				 unsigned int addr,
> +				 unsigned long mask,
> +				 unsigned int val);
> +	u16 (*rd_wr_cmd)(int addr, char isWriteOp);
>  };
>  
>  int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
> diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
> index b7faef69a58f..98ed52b74507 100644
> --- a/drivers/iio/adc/ad7606_spi.c
> +++ b/drivers/iio/adc/ad7606_spi.c
> @@ -15,6 +15,51 @@
>  
>  #define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */
>  
> +#define AD7616_CONFIGURATION_REGISTER	0x02
> +#define AD7616_OS_MASK			GENMASK(4, 2)
> +#define AD7616_BURST_MODE		BIT(6)
> +#define AD7616_SEQEN_MODE		BIT(5)
> +#define AD7616_RANGE_CH_A_ADDR_OFF	0x04
> +#define AD7616_RANGE_CH_B_ADDR_OFF	0x06
> +/*
> + * Range of channels from a group are stored in 2 registers.
> + * 0, 1, 2, 3 in a register followed by 4, 5, 6, 7 in second register.
> + * For channels from second group(8-15) the order is the same, only with
> + * an offset of 2 for register address.
> + */
> +#define AD7616_RANGE_CH_ADDR(ch)	((ch) >> 2)
> +/* The range of the channel is stored on 2 bits*/
> +#define AD7616_RANGE_CH_MSK(ch)		(0b11 << (((ch) & 0b11) * 2))
> +#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << ((((ch) & 0b11)) * 2))
> +static const struct iio_chan_spec ad7616_sw_channels[] = {
> +	IIO_CHAN_SOFT_TIMESTAMP(16),
> +	AD7616_CHANNEL(0),
> +	AD7616_CHANNEL(1),
> +	AD7616_CHANNEL(2),
> +	AD7616_CHANNEL(3),
> +	AD7616_CHANNEL(4),
> +	AD7616_CHANNEL(5),
> +	AD7616_CHANNEL(6),
> +	AD7616_CHANNEL(7),
> +	AD7616_CHANNEL(8),
> +	AD7616_CHANNEL(9),
> +	AD7616_CHANNEL(10),
> +	AD7616_CHANNEL(11),
> +	AD7616_CHANNEL(12),
> +	AD7616_CHANNEL(13),
> +	AD7616_CHANNEL(14),
> +	AD7616_CHANNEL(15),
> +};
> +
> +static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
> +{
> +	/*
> +	 * The address of register consist of one w/r bit
> +	 * 6 bits of address followed by one reserved bit.
> +	 */
> +	return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
> +}
> +
>  static int ad7606_spi_read_block(struct device *dev,
>  				 int count, void *buf)
>  {
> @@ -35,17 +80,145 @@ static int ad7606_spi_read_block(struct device *dev,
>  	return 0;
>  }
>  
> +static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
> +{
> +	struct spi_device *spi = to_spi_device(st->dev);
> +	struct spi_transfer t[] = {
> +		{
> +			.tx_buf = &st->d16[0],
> +			.len = 2,
> +			.cs_change = 0,
> +		}, {
> +			.rx_buf = &st->d16[1],
> +			.len = 2,
> +		},
> +	};
> +	int ret;
> +
> +	st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
> +
> +	ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
> +	if (ret < 0)
> +		return ret;
> +
> +	return be16_to_cpu(st->d16[1]);
> +}
> +
> +static int ad7606_spi_reg_write(struct ad7606_state *st,
> +				unsigned int addr,
> +				unsigned int val)
> +{
> +	struct spi_device *spi = to_spi_device(st->dev);
> +
> +	st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
> +				  (val & 0x1FF));
> +
> +	return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
> +}
> +
> +static int ad7606_spi_write_mask(struct ad7606_state *st,
> +				 unsigned int addr,
> +				 unsigned long mask,
> +				 unsigned int val)
> +{
> +	int readval;
> +
> +	readval = st->bops->reg_read(st, addr);
> +	if (readval < 0)
> +		return readval;
> +
> +	readval &= ~mask;
> +	readval |= val;
> +
> +	return st->bops->reg_write(st, addr, readval);
> +}
> +
> +static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +	unsigned int ch_addr, mode, ch_index;
> +
> +
> +	/*
> +	 * Ad7616 has 16 channels divided in group A and group B.
> +	 * The range of channels from A are stored in registers with address 4
> +	 * while channels from B are stored in register with address 6.
> +	 * The last bit from channels determines if it is from group A or B
> +	 * because the order of channels in iio is 0A, 0B, 1A, 1B...
> +	 */
> +	ch_index = ch >> 1;
> +
> +	ch_addr = AD7616_RANGE_CH_ADDR(ch_index);
> +
> +	if ((ch & 0x1) == 0) /* channel A */
> +		ch_addr += AD7616_RANGE_CH_A_ADDR_OFF;
> +	else	/* channel B */
> +		ch_addr += AD7616_RANGE_CH_B_ADDR_OFF;
> +
> +	/* 0b01 for 2.5v, 0b10 for 5v and 0b11 for 10v */
> +	mode = AD7616_RANGE_CH_MODE(ch_index, ((val + 1) & 0b11));
> +	return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch_index),
> +				     mode);
> +}
> +
> +static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
> +				     AD7616_OS_MASK, val << 2);
> +}
> +
> +static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	/*
> +	 * Scale can be configured individually for each channel
> +	 * in software mode.
> +	 */
> +	indio_dev->channels = ad7616_sw_channels;
> +
> +	st->write_scale = ad7616_write_scale_sw;
> +	st->write_os = &ad7616_write_os_sw;
> +
> +	/* Activate Burst mode and SEQEN MODE */
> +	return st->bops->write_mask(st,
> +			      AD7616_CONFIGURATION_REGISTER,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
> +}
> +
>  static const struct ad7606_bus_ops ad7606_spi_bops = {
>  	.read_block = ad7606_spi_read_block,
>  };
>  
> +static const struct ad7606_bus_ops ad7616_spi_bops = {
> +	.read_block = ad7606_spi_read_block,
> +	.reg_read = ad7606_spi_reg_read,
> +	.reg_write = ad7606_spi_reg_write,
> +	.write_mask = ad7606_spi_write_mask,
> +	.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
> +	.sw_mode_config = ad7616_sw_mode_config,
> +};
> +
>  static int ad7606_spi_probe(struct spi_device *spi)
>  {
>  	const struct spi_device_id *id = spi_get_device_id(spi);
> +	const struct ad7606_bus_ops *bops;
> +
> +	switch (id->driver_data) {
> +	case ID_AD7616:
> +		bops = &ad7616_spi_bops;
> +		break;
> +	default:
> +		bops = &ad7606_spi_bops;
> +		break;
> +	}
>  
>  	return ad7606_probe(&spi->dev, spi->irq, NULL,
>  			    id->name, id->driver_data,
> -			    &ad7606_spi_bops);
> +			    bops);
>  }
>  
>  static const struct spi_device_id ad7606_id_table[] = {


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

* Re: [PATCH 5/5] iio: adc: ad7606: Add debug mode for ad7616
  2019-07-18  6:27 ` [PATCH 5/5] iio: adc: ad7606: Add debug " Beniamin Bia
@ 2019-07-28  7:58   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-28  7:58 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio, biabeniamin

On Thu, 18 Jul 2019 09:27:34 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> Support for register access was added for devices which have software
> mode.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
> Acked-by: Jonathan Cameron <jic23@kernel.org>

Applied to the togreg branch of iio.git and pushed out as testing for
the autobuilders to play with it.

Thanks,

Jonathan

> ---
> Changes in v2:
> -nothing changed
> 
>  drivers/iio/adc/ad7606.c | 33 ++++++++++++++++++++++++++++++++-
>  1 file changed, 32 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index be2330c8b277..ed2d08437e5d 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -60,6 +60,29 @@ static int ad7606_reset(struct ad7606_state *st)
>  	return -ENODEV;
>  }
>  
> +static int ad7606_reg_access(struct iio_dev *indio_dev,
> +			     unsigned int reg,
> +			     unsigned int writeval,
> +			     unsigned int *readval)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	mutex_lock(&st->lock);
> +	if (readval) {
> +		ret = st->bops->reg_read(st, reg);
> +		if (ret < 0)
> +			goto err_unlock;
> +		*readval = ret;
> +		ret = 0;
> +	} else {
> +		ret = st->bops->reg_write(st, reg, writeval);
> +	}
> +err_unlock:
> +	mutex_unlock(&st->lock);
> +	return ret;
> +}
> +
>  static int ad7606_read_samples(struct ad7606_state *st)
>  {
>  	unsigned int num = st->chip_info->num_channels;
> @@ -501,6 +524,14 @@ static const struct iio_info ad7606_info_os_and_range = {
>  	.validate_trigger = &ad7606_validate_trigger,
>  };
>  
> +static const struct iio_info ad7606_info_os_range_and_debug = {
> +	.read_raw = &ad7606_read_raw,
> +	.write_raw = &ad7606_write_raw,
> +	.debugfs_reg_access = &ad7606_reg_access,
> +	.attrs = &ad7606_attribute_group_os_and_range,
> +	.validate_trigger = &ad7606_validate_trigger,
> +};
> +
>  static const struct iio_info ad7606_info_os = {
>  	.read_raw = &ad7606_read_raw,
>  	.write_raw = &ad7606_write_raw,
> @@ -617,7 +648,7 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  
>  		/* After reset, in software mode, ±10 V is set by default */
>  		memset32(st->range, 2, ARRAY_SIZE(st->range));
> -		indio_dev->info = &ad7606_info_os_and_range;
> +		indio_dev->info = &ad7606_info_os_range_and_debug;
>  
>  		ret = st->bops->sw_mode_config(indio_dev);
>  		if (ret < 0)


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

* Re: [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-07-03 14:36 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
@ 2019-07-14 15:54   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-07-14 15:54 UTC (permalink / raw)
  To: Beniamin Bia; +Cc: linux-iio

On Wed, 3 Jul 2019 17:36:47 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> Support for ad7616 running in software was added. In order
> to activate the software mode, HW_RNGSEL pins must be pulled low.
> Oversampling and input ranges are now configured in corresponding
> registers. Ad7616 has multiple scale options when it is configured
> in software mode.
> Also, in order to support multiple devices in software mode, the spi
> calculation of registers address must be generic. Because
> the length of address and bit which specifies the read/write operation is
> different for every device, calculation of address was made generic.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
A few minor comments inline that might improve readability a little.

Thanks,

Jonathan

> ---
>  drivers/iio/adc/ad7606.c     |   9 +++
>  drivers/iio/adc/ad7606.h     |  38 +++++++--
>  drivers/iio/adc/ad7606_spi.c | 148 ++++++++++++++++++++++++++++++++++-
>  3 files changed, 186 insertions(+), 9 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index a6034cf7238a..be2330c8b277 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
>  	152588, 305176
>  };
>  
> +
> +static const unsigned int ad7616_sw_scale_avail[3] = {
> +	76293, 152588, 305176
> +};
> +
>  static const unsigned int ad7606_oversampling_avail[7] = {
>  	1, 2, 4, 8, 16, 32, 64,
>  };
> @@ -606,6 +611,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  							 "adi,sw-mode");
>  
>  	if (st->sw_mode_en) {
> +		/* Scale of 0.076293 is only available in sw mode */
> +		st->scale_avail = ad7616_sw_scale_avail;
> +		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
> +
>  		/* After reset, in software mode, ±10 V is set by default */
>  		memset32(st->range, 2, ARRAY_SIZE(st->range));
>  		indio_dev->info = &ad7606_info_os_and_range;
> diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
> index a6aac33aa33c..eeaaa8b905db 100644
> --- a/drivers/iio/adc/ad7606.h
> +++ b/drivers/iio/adc/ad7606.h
> @@ -8,14 +8,14 @@
>  #ifndef IIO_ADC_AD7606_H_
>  #define IIO_ADC_AD7606_H_
>  
> -#define AD760X_CHANNEL(num, mask) {				\
> +#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
>  		.type = IIO_VOLTAGE,				\
>  		.indexed = 1,					\
>  		.channel = num,					\
>  		.address = num,					\
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
> -		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> -		.info_mask_shared_by_all = mask,		\
> +		.info_mask_separate = mask_sep,			\
> +		.info_mask_shared_by_type = mask_type,		\
> +		.info_mask_shared_by_all = mask_all,		\
>  		.scan_index = num,				\
>  		.scan_type = {					\
>  			.sign = 's',				\
> @@ -25,11 +25,18 @@
>  		},						\
>  }
>  
> -#define AD7605_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, 0)
> +#define AD7605_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE), 0)
>  
> -#define AD7606_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +#define AD7606_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE),		\
> +		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +
> +#define AD7616_CHANNEL(num)	\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
> +		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
>  
>  /**
>   * struct ad7606_chip_info - chip specific information
> @@ -77,6 +84,7 @@ struct ad7606_chip_info {
>   * @complete		completion to indicate end of conversion
>   * @trig		The IIO trigger associated with the device.
>   * @data		buffer for reading data from the device
> + * @d16			be16 buffer for reading data from the device
>   */
>  struct ad7606_state {
>  	struct device			*dev;
> @@ -110,6 +118,7 @@ struct ad7606_state {
>  	 * 16 * 16-bit samples + 64-bit timestamp
>  	 */
>  	unsigned short			data[20] ____cacheline_aligned;
> +	__be16				d16[2];
>  };
>  
>  /**
> @@ -117,11 +126,24 @@ struct ad7606_state {
>   * @read_block		function pointer for reading blocks of data
>   * @sw_mode_config:	pointer to a function which configured the device
>   *			for software mode
> + * @reg_read	function pointer for reading spi register
> + * @reg_write	function pointer for writing spi register
> + * @write_mask	function pointer for write spi register with mask
> + * @rd_wr_cmd	pointer to the function which calculates the spi address
>   */
>  struct ad7606_bus_ops {
>  	/* more methods added in future? */
>  	int (*read_block)(struct device *dev, int num, void *data);
>  	int (*sw_mode_config)(struct iio_dev *indio_dev);
> +	int (*reg_read)(struct ad7606_state *st, unsigned int addr);
> +	int (*reg_write)(struct ad7606_state *st,
> +				unsigned int addr,
> +				unsigned int val);
> +	int (*write_mask)(struct ad7606_state *st,
> +				 unsigned int addr,
> +				 unsigned long mask,
> +				 unsigned int val);
> +	u16 (*rd_wr_cmd)(int addr, char isWriteOp);
>  };
>  
>  int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
> diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
> index b7faef69a58f..d96d8d6d5fc5 100644
> --- a/drivers/iio/adc/ad7606_spi.c
> +++ b/drivers/iio/adc/ad7606_spi.c
> @@ -15,6 +15,40 @@
>  
>  #define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */
>  
> +#define AD7616_CONFIGURATION_REGISTER	0x02
> +#define AD7616_OS_MASK			GENMASK(4,  2)
Extra space between , and 2
> +#define AD7616_BURST_MODE		BIT(6)
> +#define AD7616_SEQEN_MODE		BIT(5)
> +#define AD7616_RANGE_CH_ADDR_OFF	0x04
> +#define AD7616_RANGE_CH_ADDR(ch)	((((ch) & 0x1) << 1) + ((ch) >> 3))
> +#define AD7616_RANGE_CH_MSK(ch)		(GENMASK(1, 0) << ((ch) & 0x6))
> +#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << (ch & GENMASK(2, 1)))
Hmm. These 3 are rather non obvious.

GENMASK(2, 1) is 0x6, yet you used the value for the second one and not the third?
ch in the last one should have brackets.

So this is complicated by the channel ordering as v0a, v0b, v1a, v1b.
I would add some comments to explain what is going on here.

> +
> +static const struct iio_chan_spec ad7616_sw_channels[] = {
> +	IIO_CHAN_SOFT_TIMESTAMP(16),
> +	AD7616_CHANNEL(0),
> +	AD7616_CHANNEL(1),
> +	AD7616_CHANNEL(2),
> +	AD7616_CHANNEL(3),
> +	AD7616_CHANNEL(4),
> +	AD7616_CHANNEL(5),
> +	AD7616_CHANNEL(6),
> +	AD7616_CHANNEL(7),
> +	AD7616_CHANNEL(8),
> +	AD7616_CHANNEL(9),
> +	AD7616_CHANNEL(10),
> +	AD7616_CHANNEL(11),
> +	AD7616_CHANNEL(12),
> +	AD7616_CHANNEL(13),
> +	AD7616_CHANNEL(14),
> +	AD7616_CHANNEL(15),
> +};
> +
> +static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
> +{
> +	return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
Hmm. Wrapping this up makes it harder to realise that this is only
7 bits, that we happen to shove left 1 bit (then always shift another
8 when using it).

I would make it take the value as well, and put all of the data
arrangement in one function.

> +}
> +
>  static int ad7606_spi_read_block(struct device *dev,
>  				 int count, void *buf)
>  {
> @@ -35,17 +69,129 @@ static int ad7606_spi_read_block(struct device *dev,
>  	return 0;
>  }
>  
> +static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
> +{
> +	struct spi_device *spi = to_spi_device(st->dev);
> +	struct spi_transfer t[] = {
> +		{
> +			.tx_buf = &st->d16[0],
> +			.len = 2,
> +			.cs_change = 0,
> +		}, {
> +			.rx_buf = &st->d16[1],
> +			.len = 2,
> +		},
> +	};
> +	int ret;
> +
> +	st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
> +
> +	ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
> +	if (ret < 0)
> +		return ret;
> +
> +	return be16_to_cpu(st->d16[1]);
> +}
> +
> +static int ad7606_spi_reg_write(struct ad7606_state *st,
> +				unsigned int addr,
> +				unsigned int val)
> +{
> +	struct spi_device *spi = to_spi_device(st->dev);
> +
> +	st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
> +				  (val & 0x1FF));
> +
> +	return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
> +}
> +
> +static int ad7606_spi_write_mask(struct ad7606_state *st,
> +				 unsigned int addr,
> +				 unsigned long mask,
> +				 unsigned int val)
> +{
> +	int readval;
> +
> +	readval = st->bops->reg_read(st, addr);
> +	if (readval < 0)
> +		return readval;
> +
> +	readval &= ~mask;
> +	readval |= val;
> +
> +	return st->bops->reg_write(st, addr, readval);
> +}
> +
> +static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +	unsigned int ch_addr, mode;
> +
> +	ch_addr = AD7616_RANGE_CH_ADDR_OFF + AD7616_RANGE_CH_ADDR(ch);
> +	mode = AD7616_RANGE_CH_MODE(ch, ((val + 1) & 0x3));
> +
> +	return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch),
> +				     mode);
> +}
> +
> +static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
> +				     AD7616_OS_MASK, val << 2);
> +}
> +
> +static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	/*
> +	 * Scale can be configured individually for each channel
> +	 * in software mode.
> +	 */
> +	indio_dev->channels = ad7616_sw_channels;
> +
> +	st->write_scale = ad7616_write_scale_sw;
> +	st->write_os = &ad7616_write_os_sw;
> +
> +	/* Activate Burst mode and SEQEN MODE */
> +	return st->bops->write_mask(st,
> +			      AD7616_CONFIGURATION_REGISTER,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
> +}
> +
>  static const struct ad7606_bus_ops ad7606_spi_bops = {
>  	.read_block = ad7606_spi_read_block,
>  };
>  
> +static const struct ad7606_bus_ops ad7616_spi_bops = {
> +	.read_block = ad7606_spi_read_block,
> +	.reg_read = ad7606_spi_reg_read,
> +	.reg_write = ad7606_spi_reg_write,
> +	.write_mask = ad7606_spi_write_mask,
> +	.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
> +	.sw_mode_config = ad7616_sw_mode_config,
> +};
> +
>  static int ad7606_spi_probe(struct spi_device *spi)
>  {
>  	const struct spi_device_id *id = spi_get_device_id(spi);
> +	const struct ad7606_bus_ops *bops;
> +
> +	switch (id->driver_data) {
> +	case ID_AD7616:
> +		bops = &ad7616_spi_bops;
> +		break;
> +	default:
> +		bops = &ad7606_spi_bops;
> +		break;
> +	}
>  
>  	return ad7606_probe(&spi->dev, spi->irq, NULL,
>  			    id->name, id->driver_data,
> -			    &ad7606_spi_bops);
> +			    bops);
>  }
>  
>  static const struct spi_device_id ad7606_id_table[] = {


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

* [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-07-03 14:36 Beniamin Bia
@ 2019-07-03 14:36 ` Beniamin Bia
  2019-07-14 15:54   ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-07-03 14:36 UTC (permalink / raw)
  To: jic23; +Cc: linux-iio, Beniamin Bia

Support for ad7616 running in software was added. In order
to activate the software mode, HW_RNGSEL pins must be pulled low.
Oversampling and input ranges are now configured in corresponding
registers. Ad7616 has multiple scale options when it is configured
in software mode.
Also, in order to support multiple devices in software mode, the spi
calculation of registers address must be generic. Because
the length of address and bit which specifies the read/write operation is
different for every device, calculation of address was made generic.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
---
 drivers/iio/adc/ad7606.c     |   9 +++
 drivers/iio/adc/ad7606.h     |  38 +++++++--
 drivers/iio/adc/ad7606_spi.c | 148 ++++++++++++++++++++++++++++++++++-
 3 files changed, 186 insertions(+), 9 deletions(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index a6034cf7238a..be2330c8b277 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -35,6 +35,11 @@ static const unsigned int ad7606_scale_avail[2] = {
 	152588, 305176
 };
 
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+	76293, 152588, 305176
+};
+
 static const unsigned int ad7606_oversampling_avail[7] = {
 	1, 2, 4, 8, 16, 32, 64,
 };
@@ -606,6 +611,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 							 "adi,sw-mode");
 
 	if (st->sw_mode_en) {
+		/* Scale of 0.076293 is only available in sw mode */
+		st->scale_avail = ad7616_sw_scale_avail;
+		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
 		/* After reset, in software mode, ±10 V is set by default */
 		memset32(st->range, 2, ARRAY_SIZE(st->range));
 		indio_dev->info = &ad7606_info_os_and_range;
diff --git a/drivers/iio/adc/ad7606.h b/drivers/iio/adc/ad7606.h
index a6aac33aa33c..eeaaa8b905db 100644
--- a/drivers/iio/adc/ad7606.h
+++ b/drivers/iio/adc/ad7606.h
@@ -8,14 +8,14 @@
 #ifndef IIO_ADC_AD7606_H_
 #define IIO_ADC_AD7606_H_
 
-#define AD760X_CHANNEL(num, mask) {				\
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
 		.type = IIO_VOLTAGE,				\
 		.indexed = 1,					\
 		.channel = num,					\
 		.address = num,					\
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
-		.info_mask_shared_by_all = mask,		\
+		.info_mask_separate = mask_sep,			\
+		.info_mask_shared_by_type = mask_type,		\
+		.info_mask_shared_by_all = mask_all,		\
 		.scan_index = num,				\
 		.scan_type = {					\
 			.sign = 's',				\
@@ -25,11 +25,18 @@
 		},						\
 }
 
-#define AD7605_CHANNEL(num)	\
-	AD760X_CHANNEL(num, 0)
+#define AD7605_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE), 0)
 
-#define AD7606_CHANNEL(num)	\
-	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+#define AD7606_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE),		\
+		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+
+#define AD7616_CHANNEL(num)	\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
 
 /**
  * struct ad7606_chip_info - chip specific information
@@ -77,6 +84,7 @@ struct ad7606_chip_info {
  * @complete		completion to indicate end of conversion
  * @trig		The IIO trigger associated with the device.
  * @data		buffer for reading data from the device
+ * @d16			be16 buffer for reading data from the device
  */
 struct ad7606_state {
 	struct device			*dev;
@@ -110,6 +118,7 @@ struct ad7606_state {
 	 * 16 * 16-bit samples + 64-bit timestamp
 	 */
 	unsigned short			data[20] ____cacheline_aligned;
+	__be16				d16[2];
 };
 
 /**
@@ -117,11 +126,24 @@ struct ad7606_state {
  * @read_block		function pointer for reading blocks of data
  * @sw_mode_config:	pointer to a function which configured the device
  *			for software mode
+ * @reg_read	function pointer for reading spi register
+ * @reg_write	function pointer for writing spi register
+ * @write_mask	function pointer for write spi register with mask
+ * @rd_wr_cmd	pointer to the function which calculates the spi address
  */
 struct ad7606_bus_ops {
 	/* more methods added in future? */
 	int (*read_block)(struct device *dev, int num, void *data);
 	int (*sw_mode_config)(struct iio_dev *indio_dev);
+	int (*reg_read)(struct ad7606_state *st, unsigned int addr);
+	int (*reg_write)(struct ad7606_state *st,
+				unsigned int addr,
+				unsigned int val);
+	int (*write_mask)(struct ad7606_state *st,
+				 unsigned int addr,
+				 unsigned long mask,
+				 unsigned int val);
+	u16 (*rd_wr_cmd)(int addr, char isWriteOp);
 };
 
 int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
diff --git a/drivers/iio/adc/ad7606_spi.c b/drivers/iio/adc/ad7606_spi.c
index b7faef69a58f..d96d8d6d5fc5 100644
--- a/drivers/iio/adc/ad7606_spi.c
+++ b/drivers/iio/adc/ad7606_spi.c
@@ -15,6 +15,40 @@
 
 #define MAX_SPI_FREQ_HZ		23500000	/* VDRIVE above 4.75 V */
 
+#define AD7616_CONFIGURATION_REGISTER	0x02
+#define AD7616_OS_MASK			GENMASK(4,  2)
+#define AD7616_BURST_MODE		BIT(6)
+#define AD7616_SEQEN_MODE		BIT(5)
+#define AD7616_RANGE_CH_ADDR_OFF	0x04
+#define AD7616_RANGE_CH_ADDR(ch)	((((ch) & 0x1) << 1) + ((ch) >> 3))
+#define AD7616_RANGE_CH_MSK(ch)		(GENMASK(1, 0) << ((ch) & 0x6))
+#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << (ch & GENMASK(2, 1)))
+
+static const struct iio_chan_spec ad7616_sw_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(16),
+	AD7616_CHANNEL(0),
+	AD7616_CHANNEL(1),
+	AD7616_CHANNEL(2),
+	AD7616_CHANNEL(3),
+	AD7616_CHANNEL(4),
+	AD7616_CHANNEL(5),
+	AD7616_CHANNEL(6),
+	AD7616_CHANNEL(7),
+	AD7616_CHANNEL(8),
+	AD7616_CHANNEL(9),
+	AD7616_CHANNEL(10),
+	AD7616_CHANNEL(11),
+	AD7616_CHANNEL(12),
+	AD7616_CHANNEL(13),
+	AD7616_CHANNEL(14),
+	AD7616_CHANNEL(15),
+};
+
+static u16 ad7616_spi_rd_wr_cmd(int addr, char isWriteOp)
+{
+	return ((addr & 0x7F) << 1) | ((isWriteOp & 0x1) << 7);
+}
+
 static int ad7606_spi_read_block(struct device *dev,
 				 int count, void *buf)
 {
@@ -35,17 +69,129 @@ static int ad7606_spi_read_block(struct device *dev,
 	return 0;
 }
 
+static int ad7606_spi_reg_read(struct ad7606_state *st, unsigned int addr)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+	struct spi_transfer t[] = {
+		{
+			.tx_buf = &st->d16[0],
+			.len = 2,
+			.cs_change = 0,
+		}, {
+			.rx_buf = &st->d16[1],
+			.len = 2,
+		},
+	};
+	int ret;
+
+	st->d16[0] = cpu_to_be16(st->bops->rd_wr_cmd(addr, 0) << 8);
+
+	ret = spi_sync_transfer(spi, t, ARRAY_SIZE(t));
+	if (ret < 0)
+		return ret;
+
+	return be16_to_cpu(st->d16[1]);
+}
+
+static int ad7606_spi_reg_write(struct ad7606_state *st,
+				unsigned int addr,
+				unsigned int val)
+{
+	struct spi_device *spi = to_spi_device(st->dev);
+
+	st->d16[0] = cpu_to_be16((st->bops->rd_wr_cmd(addr, 1) << 8) |
+				  (val & 0x1FF));
+
+	return spi_write(spi, &st->d16[0], sizeof(st->d16[0]));
+}
+
+static int ad7606_spi_write_mask(struct ad7606_state *st,
+				 unsigned int addr,
+				 unsigned long mask,
+				 unsigned int val)
+{
+	int readval;
+
+	readval = st->bops->reg_read(st, addr);
+	if (readval < 0)
+		return readval;
+
+	readval &= ~mask;
+	readval |= val;
+
+	return st->bops->reg_write(st, addr, readval);
+}
+
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+	unsigned int ch_addr, mode;
+
+	ch_addr = AD7616_RANGE_CH_ADDR_OFF + AD7616_RANGE_CH_ADDR(ch);
+	mode = AD7616_RANGE_CH_MODE(ch, ((val + 1) & 0x3));
+
+	return st->bops->write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch),
+				     mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	return st->bops->write_mask(st, AD7616_CONFIGURATION_REGISTER,
+				     AD7616_OS_MASK, val << 2);
+}
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	/*
+	 * Scale can be configured individually for each channel
+	 * in software mode.
+	 */
+	indio_dev->channels = ad7616_sw_channels;
+
+	st->write_scale = ad7616_write_scale_sw;
+	st->write_os = &ad7616_write_os_sw;
+
+	/* Activate Burst mode and SEQEN MODE */
+	return st->bops->write_mask(st,
+			      AD7616_CONFIGURATION_REGISTER,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
 static const struct ad7606_bus_ops ad7606_spi_bops = {
 	.read_block = ad7606_spi_read_block,
 };
 
+static const struct ad7606_bus_ops ad7616_spi_bops = {
+	.read_block = ad7606_spi_read_block,
+	.reg_read = ad7606_spi_reg_read,
+	.reg_write = ad7606_spi_reg_write,
+	.write_mask = ad7606_spi_write_mask,
+	.rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+	.sw_mode_config = ad7616_sw_mode_config,
+};
+
 static int ad7606_spi_probe(struct spi_device *spi)
 {
 	const struct spi_device_id *id = spi_get_device_id(spi);
+	const struct ad7606_bus_ops *bops;
+
+	switch (id->driver_data) {
+	case ID_AD7616:
+		bops = &ad7616_spi_bops;
+		break;
+	default:
+		bops = &ad7606_spi_bops;
+		break;
+	}
 
 	return ad7606_probe(&spi->dev, spi->irq, NULL,
 			    id->name, id->driver_data,
-			    &ad7606_spi_bops);
+			    bops);
 }
 
 static const struct spi_device_id ad7606_id_table[] = {
-- 
2.17.1


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

* Re: [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-05-16 14:32 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
@ 2019-05-18 10:20   ` Jonathan Cameron
  0 siblings, 0 replies; 14+ messages in thread
From: Jonathan Cameron @ 2019-05-18 10:20 UTC (permalink / raw)
  To: Beniamin Bia
  Cc: lars, Michael.Hennerich, knaack.h, pmeerw, gregkh, linux-iio,
	devel, linux-kernel, mark.rutland, robh+dt, devicetree,
	biabeniamin

On Thu, 16 May 2019 17:32:07 +0300
Beniamin Bia <beniamin.bia@analog.com> wrote:

> Support for ad7616 running in software was added. In order
> to activate the software mode, HW_RNGSEL pins must be pulled low.
> Oversampling and input ranges are now configured in corresponding
> registers. Ad7616 has multiple scale options when it is configured
> in software mode.
> 
> Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
Looks good subject to comments on the previous.

Thanks,

Jonathan

> ---
>  drivers/iio/adc/ad7606.c | 111 ++++++++++++++++++++++++++++++++++++---
>  1 file changed, 103 insertions(+), 8 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
> index 6df81117cacc..f77df3efe43f 100644
> --- a/drivers/iio/adc/ad7606.c
> +++ b/drivers/iio/adc/ad7606.c
> @@ -29,6 +29,20 @@
>  
>  #include "ad7606.h"
>  
> +#define AD7606_RANGE_CH_ADDR(ch)	(0x03 + ((ch) >> 1))
> +#define AD7606_OS_MODE			0x08
> +
> +#define AD7616_CONFIGURATION_REGISTER	0x02
> +#define AD7616_OS_MASK			GENMASK(4,  2)
> +#define AD7616_BURST_MODE		BIT(6)
> +#define AD7616_SEQEN_MODE		BIT(5)
> +#define AD7616_RANGE_CH_ADDR_OFF	0x04
> +#define AD7616_RANGE_CH_ADDR(ch)	((((ch) & 0x1) << 1) + ((ch) >> 3))
> +#define AD7616_RANGE_CH_MSK(ch)		(GENMASK(1, 0) << ((ch) & 0x6))
> +#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << (ch & GENMASK(2, 1)))
> +
> +static int ad7616_sw_mode_config(struct iio_dev *indio_dev);
> +
>  /*
>   * Scales are computed as 5000/32768 and 10000/32768 respectively,
>   * so that when applied to the raw values they provide mV values
> @@ -37,6 +51,11 @@ static const unsigned int ad7606_scale_avail[2] = {
>  	152588, 305176
>  };
>  
> +
> +static const unsigned int ad7616_sw_scale_avail[3] = {
> +	76293, 152588, 305176
> +};
> +
>  static const unsigned int ad7606_oversampling_avail[7] = {
>  	1, 2, 4, 8, 16, 32, 64,
>  };
> @@ -282,6 +301,26 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
>  	return 0;
>  }
>  
> +static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +	unsigned int ch_addr, mode;
> +
> +	ch_addr = AD7616_RANGE_CH_ADDR_OFF + AD7616_RANGE_CH_ADDR(ch);
> +	mode = AD7616_RANGE_CH_MODE(ch, ((val + 1) & 0x3));
> +
> +	return ad7606_spi_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch),
> +				     mode);
> +}
> +
> +static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	return ad7606_spi_write_mask(st, AD7616_CONFIGURATION_REGISTER,
> +				     AD7616_OS_MASK, val << 2);
> +}
> +
>  static int ad7606_write_raw(struct iio_dev *indio_dev,
>  			    struct iio_chan_spec const *chan,
>  			    int val,
> @@ -368,14 +407,14 @@ static const struct attribute_group ad7606_attribute_group_range = {
>  	.attrs = ad7606_attributes_range,
>  };
>  
> -#define AD760X_CHANNEL(num, mask) {				\
> +#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
>  		.type = IIO_VOLTAGE,				\
>  		.indexed = 1,					\
>  		.channel = num,					\
>  		.address = num,					\
> -		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
> -		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
> -		.info_mask_shared_by_all = mask,		\
> +		.info_mask_separate = mask_sep,			\
> +		.info_mask_shared_by_type = mask_type,		\
> +		.info_mask_shared_by_all = mask_all,		\
>  		.scan_index = num,				\
>  		.scan_type = {					\
>  			.sign = 's',				\
> @@ -385,11 +424,18 @@ static const struct attribute_group ad7606_attribute_group_range = {
>  		},						\
>  }
>  
> -#define AD7605_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, 0)
> +#define AD7605_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE), 0)
> +
> +#define AD7606_CHANNEL(num)				\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
> +		BIT(IIO_CHAN_INFO_SCALE),		\
> +		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
>  
> -#define AD7606_CHANNEL(num)	\
> -	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
> +#define AD7616_CHANNEL(num)	\
> +	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
> +		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
>  
>  static const struct iio_chan_spec ad7605_channels[] = {
>  	IIO_CHAN_SOFT_TIMESTAMP(4),
> @@ -441,6 +487,26 @@ static const struct iio_chan_spec ad7616_channels[] = {
>  	AD7606_CHANNEL(15),
>  };
>  
> +static const struct iio_chan_spec ad7616_sw_channels[] = {
> +	IIO_CHAN_SOFT_TIMESTAMP(16),
> +	AD7616_CHANNEL(0),
> +	AD7616_CHANNEL(1),
> +	AD7616_CHANNEL(2),
> +	AD7616_CHANNEL(3),
> +	AD7616_CHANNEL(4),
> +	AD7616_CHANNEL(5),
> +	AD7616_CHANNEL(6),
> +	AD7616_CHANNEL(7),
> +	AD7616_CHANNEL(8),
> +	AD7616_CHANNEL(9),
> +	AD7616_CHANNEL(10),
> +	AD7616_CHANNEL(11),
> +	AD7616_CHANNEL(12),
> +	AD7616_CHANNEL(13),
> +	AD7616_CHANNEL(14),
> +	AD7616_CHANNEL(15),
> +};
> +
>  static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
>  	/* More devices added in future */
>  	[ID_AD7605_4] = {
> @@ -468,9 +534,13 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
>  	[ID_AD7616] = {
>  		.channels = ad7616_channels,
>  		.num_channels = 17,
> +		.sw_mode_config = ad7616_sw_mode_config,
>  		.oversampling_avail = ad7616_oversampling_avail,
>  		.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
>  		.os_req_reset = true,
> +		.spi_rd_wr_cmd = ad7616_spi_rd_wr_cmd,
> +		.write_scale_sw = ad7616_write_scale_sw,
> +		.write_os_sw = ad7616_write_os_sw,
>  	},
>  };
>  
> @@ -604,6 +674,23 @@ static void ad7606_regulator_disable(void *data)
>  	regulator_disable(st->reg);
>  }
>  
> +static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
> +{
> +	struct ad7606_state *st = iio_priv(indio_dev);
> +
> +	/*
> +	 * Scale can be configured individually for each channel
> +	 * in software mode.
> +	 */
> +	indio_dev->channels = ad7616_sw_channels;
> +
> +	/* Activate Burst mode and SEQEN MODE */
> +	return ad7606_spi_write_mask(st,
> +			      AD7616_CONFIGURATION_REGISTER,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
> +			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
> +}
> +
>  int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  		 const char *name, unsigned int id,
>  		 const struct ad7606_bus_ops *bops)
> @@ -677,6 +764,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  	if (ret)
>  		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
>  
> +	/* AD7616 requires al least 15ms to reconfigure after a reset */
> +	if (msleep_interruptible(15))
> +		return -ERESTARTSYS;
> +
>  	st->write_scale = ad7606_write_scale_hw;
>  	st->write_os = ad7606_write_os_hw;
>  
> @@ -685,6 +776,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
>  							 "adi,sw-mode");
>  
>  	if (st->sw_mode_en) {
> +		/* Scale of 0.076293 is only available in sw mode */
> +		st->scale_avail = ad7616_sw_scale_avail;
> +		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
> +
>  		/* After reset, in software mode, ±10 V is set by default */
>  		memset32(st->range, 2, ARRAY_SIZE(st->range));
>  		indio_dev->info = &ad7606_info_os_and_range;


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

* [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616
  2019-05-16 14:32 [PATCH 1/5] iio: adc: ad7606: Move oversampling and scale options to chip info Beniamin Bia
@ 2019-05-16 14:32 ` Beniamin Bia
  2019-05-18 10:20   ` Jonathan Cameron
  0 siblings, 1 reply; 14+ messages in thread
From: Beniamin Bia @ 2019-05-16 14:32 UTC (permalink / raw)
  To: jic23
  Cc: lars, Michael.Hennerich, knaack.h, pmeerw, gregkh, linux-iio,
	devel, linux-kernel, mark.rutland, robh+dt, devicetree,
	biabeniamin, Beniamin Bia

Support for ad7616 running in software was added. In order
to activate the software mode, HW_RNGSEL pins must be pulled low.
Oversampling and input ranges are now configured in corresponding
registers. Ad7616 has multiple scale options when it is configured
in software mode.

Signed-off-by: Beniamin Bia <beniamin.bia@analog.com>
---
 drivers/iio/adc/ad7606.c | 111 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 103 insertions(+), 8 deletions(-)

diff --git a/drivers/iio/adc/ad7606.c b/drivers/iio/adc/ad7606.c
index 6df81117cacc..f77df3efe43f 100644
--- a/drivers/iio/adc/ad7606.c
+++ b/drivers/iio/adc/ad7606.c
@@ -29,6 +29,20 @@
 
 #include "ad7606.h"
 
+#define AD7606_RANGE_CH_ADDR(ch)	(0x03 + ((ch) >> 1))
+#define AD7606_OS_MODE			0x08
+
+#define AD7616_CONFIGURATION_REGISTER	0x02
+#define AD7616_OS_MASK			GENMASK(4,  2)
+#define AD7616_BURST_MODE		BIT(6)
+#define AD7616_SEQEN_MODE		BIT(5)
+#define AD7616_RANGE_CH_ADDR_OFF	0x04
+#define AD7616_RANGE_CH_ADDR(ch)	((((ch) & 0x1) << 1) + ((ch) >> 3))
+#define AD7616_RANGE_CH_MSK(ch)		(GENMASK(1, 0) << ((ch) & 0x6))
+#define AD7616_RANGE_CH_MODE(ch, mode)	((mode) << (ch & GENMASK(2, 1)))
+
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev);
+
 /*
  * Scales are computed as 5000/32768 and 10000/32768 respectively,
  * so that when applied to the raw values they provide mV values
@@ -37,6 +51,11 @@ static const unsigned int ad7606_scale_avail[2] = {
 	152588, 305176
 };
 
+
+static const unsigned int ad7616_sw_scale_avail[3] = {
+	76293, 152588, 305176
+};
+
 static const unsigned int ad7606_oversampling_avail[7] = {
 	1, 2, 4, 8, 16, 32, 64,
 };
@@ -282,6 +301,26 @@ static int ad7606_write_os_hw(struct iio_dev *indio_dev, int val)
 	return 0;
 }
 
+static int ad7616_write_scale_sw(struct iio_dev *indio_dev, int ch, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+	unsigned int ch_addr, mode;
+
+	ch_addr = AD7616_RANGE_CH_ADDR_OFF + AD7616_RANGE_CH_ADDR(ch);
+	mode = AD7616_RANGE_CH_MODE(ch, ((val + 1) & 0x3));
+
+	return ad7606_spi_write_mask(st, ch_addr, AD7616_RANGE_CH_MSK(ch),
+				     mode);
+}
+
+static int ad7616_write_os_sw(struct iio_dev *indio_dev, int val)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	return ad7606_spi_write_mask(st, AD7616_CONFIGURATION_REGISTER,
+				     AD7616_OS_MASK, val << 2);
+}
+
 static int ad7606_write_raw(struct iio_dev *indio_dev,
 			    struct iio_chan_spec const *chan,
 			    int val,
@@ -368,14 +407,14 @@ static const struct attribute_group ad7606_attribute_group_range = {
 	.attrs = ad7606_attributes_range,
 };
 
-#define AD760X_CHANNEL(num, mask) {				\
+#define AD760X_CHANNEL(num, mask_sep, mask_type, mask_all) {	\
 		.type = IIO_VOLTAGE,				\
 		.indexed = 1,					\
 		.channel = num,					\
 		.address = num,					\
-		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),	\
-		.info_mask_shared_by_type = BIT(IIO_CHAN_INFO_SCALE),\
-		.info_mask_shared_by_all = mask,		\
+		.info_mask_separate = mask_sep,			\
+		.info_mask_shared_by_type = mask_type,		\
+		.info_mask_shared_by_all = mask_all,		\
 		.scan_index = num,				\
 		.scan_type = {					\
 			.sign = 's',				\
@@ -385,11 +424,18 @@ static const struct attribute_group ad7606_attribute_group_range = {
 		},						\
 }
 
-#define AD7605_CHANNEL(num)	\
-	AD760X_CHANNEL(num, 0)
+#define AD7605_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE), 0)
+
+#define AD7606_CHANNEL(num)				\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW),	\
+		BIT(IIO_CHAN_INFO_SCALE),		\
+		BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
 
-#define AD7606_CHANNEL(num)	\
-	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
+#define AD7616_CHANNEL(num)	\
+	AD760X_CHANNEL(num, BIT(IIO_CHAN_INFO_RAW) | BIT(IIO_CHAN_INFO_SCALE),\
+		0, BIT(IIO_CHAN_INFO_OVERSAMPLING_RATIO))
 
 static const struct iio_chan_spec ad7605_channels[] = {
 	IIO_CHAN_SOFT_TIMESTAMP(4),
@@ -441,6 +487,26 @@ static const struct iio_chan_spec ad7616_channels[] = {
 	AD7606_CHANNEL(15),
 };
 
+static const struct iio_chan_spec ad7616_sw_channels[] = {
+	IIO_CHAN_SOFT_TIMESTAMP(16),
+	AD7616_CHANNEL(0),
+	AD7616_CHANNEL(1),
+	AD7616_CHANNEL(2),
+	AD7616_CHANNEL(3),
+	AD7616_CHANNEL(4),
+	AD7616_CHANNEL(5),
+	AD7616_CHANNEL(6),
+	AD7616_CHANNEL(7),
+	AD7616_CHANNEL(8),
+	AD7616_CHANNEL(9),
+	AD7616_CHANNEL(10),
+	AD7616_CHANNEL(11),
+	AD7616_CHANNEL(12),
+	AD7616_CHANNEL(13),
+	AD7616_CHANNEL(14),
+	AD7616_CHANNEL(15),
+};
+
 static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
 	/* More devices added in future */
 	[ID_AD7605_4] = {
@@ -468,9 +534,13 @@ static const struct ad7606_chip_info ad7606_chip_info_tbl[] = {
 	[ID_AD7616] = {
 		.channels = ad7616_channels,
 		.num_channels = 17,
+		.sw_mode_config = ad7616_sw_mode_config,
 		.oversampling_avail = ad7616_oversampling_avail,
 		.oversampling_num = ARRAY_SIZE(ad7616_oversampling_avail),
 		.os_req_reset = true,
+		.spi_rd_wr_cmd = ad7616_spi_rd_wr_cmd,
+		.write_scale_sw = ad7616_write_scale_sw,
+		.write_os_sw = ad7616_write_os_sw,
 	},
 };
 
@@ -604,6 +674,23 @@ static void ad7606_regulator_disable(void *data)
 	regulator_disable(st->reg);
 }
 
+static int ad7616_sw_mode_config(struct iio_dev *indio_dev)
+{
+	struct ad7606_state *st = iio_priv(indio_dev);
+
+	/*
+	 * Scale can be configured individually for each channel
+	 * in software mode.
+	 */
+	indio_dev->channels = ad7616_sw_channels;
+
+	/* Activate Burst mode and SEQEN MODE */
+	return ad7606_spi_write_mask(st,
+			      AD7616_CONFIGURATION_REGISTER,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE,
+			      AD7616_BURST_MODE | AD7616_SEQEN_MODE);
+}
+
 int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 		 const char *name, unsigned int id,
 		 const struct ad7606_bus_ops *bops)
@@ -677,6 +764,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 	if (ret)
 		dev_warn(st->dev, "failed to RESET: no RESET GPIO specified\n");
 
+	/* AD7616 requires al least 15ms to reconfigure after a reset */
+	if (msleep_interruptible(15))
+		return -ERESTARTSYS;
+
 	st->write_scale = ad7606_write_scale_hw;
 	st->write_os = ad7606_write_os_hw;
 
@@ -685,6 +776,10 @@ int ad7606_probe(struct device *dev, int irq, void __iomem *base_address,
 							 "adi,sw-mode");
 
 	if (st->sw_mode_en) {
+		/* Scale of 0.076293 is only available in sw mode */
+		st->scale_avail = ad7616_sw_scale_avail;
+		st->num_scales = ARRAY_SIZE(ad7616_sw_scale_avail);
+
 		/* After reset, in software mode, ±10 V is set by default */
 		memset32(st->range, 2, ARRAY_SIZE(st->range));
 		indio_dev->info = &ad7606_info_os_and_range;
-- 
2.17.1


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

end of thread, back to index

Thread overview: 14+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2019-07-18  6:27 [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Beniamin Bia
2019-07-18  6:27 ` [PATCH 2/5] iio: adc: ad7606: Move spi dependent features to spi file Beniamin Bia
2019-07-28  7:57   ` Jonathan Cameron
2019-07-18  6:27 ` [PATCH 3/5] iio: adc: ad7606: Allow reconfigration after reset Beniamin Bia
2019-07-28  7:57   ` Jonathan Cameron
2019-07-18  6:27 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
2019-07-28  7:58   ` Jonathan Cameron
2019-07-18  6:27 ` [PATCH 5/5] iio: adc: ad7606: Add debug " Beniamin Bia
2019-07-28  7:58   ` Jonathan Cameron
2019-07-28  7:57 ` [PATCH 1/5] iio: adc: ad7606: Move common channel definition to header Jonathan Cameron
  -- strict thread matches above, loose matches on Subject: below --
2019-07-03 14:36 Beniamin Bia
2019-07-03 14:36 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
2019-07-14 15:54   ` Jonathan Cameron
2019-05-16 14:32 [PATCH 1/5] iio: adc: ad7606: Move oversampling and scale options to chip info Beniamin Bia
2019-05-16 14:32 ` [PATCH 4/5] iio: adc: ad7606: Add support for software mode for ad7616 Beniamin Bia
2019-05-18 10:20   ` Jonathan Cameron

Linux-IIO Archive on lore.kernel.org

Archives are clonable:
	git clone --mirror https://lore.kernel.org/linux-iio/0 linux-iio/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 linux-iio linux-iio/ https://lore.kernel.org/linux-iio \
		linux-iio@vger.kernel.org linux-iio@archiver.kernel.org
	public-inbox-index linux-iio


Newsgroup available over NNTP:
	nntp://nntp.lore.kernel.org/org.kernel.vger.linux-iio


AGPL code for this site: git clone https://public-inbox.org/ public-inbox