[PATCH 09/15] iio: adc: ad4030: Support multiple data lanes per channel

Marcelo Schmitt posted 15 patches 1 month ago
There is a newer version of this series
[PATCH 09/15] iio: adc: ad4030: Support multiple data lanes per channel
Posted by Marcelo Schmitt 1 month ago
AD4030 and similar chips can output ADC sample data through 1, 2, or 4
lines per channel. The number of SPI lines the device uses to output data
is specified in firmware. Parse SPI read bus width setting from firmware
and configure the device to use that amount of lines to output data.

Co-developed-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
---
 drivers/iio/adc/ad4030.c | 33 ++++++++++++++++++++++++++++++---
 1 file changed, 30 insertions(+), 3 deletions(-)

diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
index 68f76432dbfd..e6c1c9be1632 100644
--- a/drivers/iio/adc/ad4030.c
+++ b/drivers/iio/adc/ad4030.c
@@ -20,6 +20,7 @@
 #include <linux/iio/iio.h>
 #include <linux/iio/trigger_consumer.h>
 #include <linux/iio/triggered_buffer.h>
+#include <linux/log2.h>
 #include <linux/pwm.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@@ -258,6 +259,10 @@ struct ad4030_state {
 #define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type)			\
 	__AD4030_CHAN_DIFF(_idx, _scan_type, 1)
 
+static const int ad4030_rx_bus_width[] = {
+	1, 2, 4, 8,
+};
+
 static const int ad4030_average_modes[] = {
 	1, 2, 4, 8, 16, 32, 64, 128,
 	256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
@@ -1197,7 +1202,7 @@ static void ad4030_prepare_offload_msg(struct ad4030_state *st)
 		 */
 		offload_bpw = data_width * st->chip->num_voltage_inputs;
 	else
-		offload_bpw  = data_width;
+		offload_bpw  = data_width / (1 << st->lane_mode);
 
 	st->offload_xfer.speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED;
 	st->offload_xfer.bits_per_word = offload_bpw;
@@ -1208,6 +1213,10 @@ static void ad4030_prepare_offload_msg(struct ad4030_state *st)
 
 static int ad4030_config(struct ad4030_state *st)
 {
+	struct device *dev = &st->spi->dev;
+	const char *propname;
+	u32 rx_bus_width;
+	unsigned int i;
 	int ret;
 	u8 reg_modes;
 
@@ -1215,10 +1224,28 @@ static int ad4030_config(struct ad4030_state *st)
 	st->offset_avail[1] = 1;
 	st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1;
 
-	if (st->chip->num_voltage_inputs > 1)
+	/* Optional property specifying the number of lanes to read ADC data */
+	propname = "spi-rx-bus-width";
+	rx_bus_width = ad4030_rx_bus_width[0]; /* Default to 1 rx lane. */
+	device_property_read_u32(dev, propname, &rx_bus_width);
+	/* Check the rx bus width is valid */
+	for (i = 0; i < ARRAY_SIZE(ad4030_rx_bus_width); i++)
+		if (ad4030_rx_bus_width[i] == rx_bus_width)
+			break;
+
+	if (i >= ARRAY_SIZE(ad4030_rx_bus_width))
+		return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n",
+				     propname, rx_bus_width);
+
+	rx_bus_width = ad4030_rx_bus_width[i];
+
+	if (rx_bus_width == 8 && st->chip->num_voltage_inputs == 1)
+		return dev_err_probe(dev, -EINVAL, "1 channel with 8 lanes?\n");
+
+	if (rx_bus_width == 1 && st->chip->num_voltage_inputs > 1)
 		st->lane_mode = AD4030_LANE_MD_INTERLEAVED;
 	else
-		st->lane_mode = AD4030_LANE_MD_1_PER_CH;
+		st->lane_mode = ilog2(rx_bus_width / st->chip->num_voltage_inputs);
 
 	reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, st->lane_mode);
 
-- 
2.39.2
Re: [PATCH 09/15] iio: adc: ad4030: Support multiple data lanes per channel
Posted by David Lechner 1 month ago
On 8/29/25 7:43 PM, Marcelo Schmitt wrote:
> AD4030 and similar chips can output ADC sample data through 1, 2, or 4
> lines per channel. The number of SPI lines the device uses to output data
> is specified in firmware. Parse SPI read bus width setting from firmware
> and configure the device to use that amount of lines to output data.
> 
> Co-developed-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> Signed-off-by: Sergiu Cuciurean <sergiu.cuciurean@analog.com>
> Signed-off-by: Marcelo Schmitt <marcelo.schmitt@analog.com>
> ---
>  drivers/iio/adc/ad4030.c | 33 ++++++++++++++++++++++++++++++---
>  1 file changed, 30 insertions(+), 3 deletions(-)
> 
> diff --git a/drivers/iio/adc/ad4030.c b/drivers/iio/adc/ad4030.c
> index 68f76432dbfd..e6c1c9be1632 100644
> --- a/drivers/iio/adc/ad4030.c
> +++ b/drivers/iio/adc/ad4030.c
> @@ -20,6 +20,7 @@
>  #include <linux/iio/iio.h>
>  #include <linux/iio/trigger_consumer.h>
>  #include <linux/iio/triggered_buffer.h>
> +#include <linux/log2.h>
>  #include <linux/pwm.h>
>  #include <linux/regmap.h>
>  #include <linux/regulator/consumer.h>
> @@ -258,6 +259,10 @@ struct ad4030_state {
>  #define AD4030_OFFLOAD_CHAN_DIFF(_idx, _scan_type)			\
>  	__AD4030_CHAN_DIFF(_idx, _scan_type, 1)
>  
> +static const int ad4030_rx_bus_width[] = {
> +	1, 2, 4, 8,
> +};
> +
>  static const int ad4030_average_modes[] = {
>  	1, 2, 4, 8, 16, 32, 64, 128,
>  	256, 512, 1024, 2048, 4096, 8192, 16384, 32768,
> @@ -1197,7 +1202,7 @@ static void ad4030_prepare_offload_msg(struct ad4030_state *st)
>  		 */
>  		offload_bpw = data_width * st->chip->num_voltage_inputs;
>  	else
> -		offload_bpw  = data_width;
> +		offload_bpw  = data_width / (1 << st->lane_mode);

To make proper use of 2 or 4 lines for a single channel, we should be
using the SPI APIs correctly and set rx_nbits in struct spi_transfer
instead of providing an inaccurate bits per word.

>  
>  	st->offload_xfer.speed_hz = AD4030_SPI_MAX_REG_XFER_SPEED;
>  	st->offload_xfer.bits_per_word = offload_bpw;
> @@ -1208,6 +1213,10 @@ static void ad4030_prepare_offload_msg(struct ad4030_state *st)
>  
>  static int ad4030_config(struct ad4030_state *st)
>  {
> +	struct device *dev = &st->spi->dev;
> +	const char *propname;
> +	u32 rx_bus_width;
> +	unsigned int i;
>  	int ret;
>  	u8 reg_modes;
>  
> @@ -1215,10 +1224,28 @@ static int ad4030_config(struct ad4030_state *st)
>  	st->offset_avail[1] = 1;
>  	st->offset_avail[2] = BIT(st->chip->precision_bits - 1) - 1;
>  
> -	if (st->chip->num_voltage_inputs > 1)
> +	/* Optional property specifying the number of lanes to read ADC data */
> +	propname = "spi-rx-bus-width";

This property is already handled by the core SPI code and will set
spi->mode flags SPI_RX_DUAL, SPI_RX_QUAD or SPI_RX_OCTAL. So we don't
need to read the property again.

> +	rx_bus_width = ad4030_rx_bus_width[0]; /* Default to 1 rx lane. */
> +	device_property_read_u32(dev, propname, &rx_bus_width);
> +	/* Check the rx bus width is valid */
> +	for (i = 0; i < ARRAY_SIZE(ad4030_rx_bus_width); i++)
> +		if (ad4030_rx_bus_width[i] == rx_bus_width)
> +			break;
> +
> +	if (i >= ARRAY_SIZE(ad4030_rx_bus_width))
> +		return dev_err_probe(dev, -EINVAL, "Invalid %s: %u\n",
> +				     propname, rx_bus_width);
> +
> +	rx_bus_width = ad4030_rx_bus_width[i];
> +
> +	if (rx_bus_width == 8 && st->chip->num_voltage_inputs == 1)
> +		return dev_err_probe(dev, -EINVAL, "1 channel with 8 lanes?\n");

As mentioned in the dt-bindings patch review, we really should consider
the 2 channel case separate as 2 SPI buses rather than 8 lines on a
single bus. 

Only using "spi-rx-bus-width" also has the shortcoming that if we specify
4, we don't know if is it 4 lines on 1 channel or is it 2 lines each on 2
channels? There is no way to tell from just that information.

> +
> +	if (rx_bus_width == 1 && st->chip->num_voltage_inputs > 1)
>  		st->lane_mode = AD4030_LANE_MD_INTERLEAVED;
>  	else
> -		st->lane_mode = AD4030_LANE_MD_1_PER_CH;
> +		st->lane_mode = ilog2(rx_bus_width / st->chip->num_voltage_inputs);
>  
>  	reg_modes = FIELD_PREP(AD4030_REG_MODES_MASK_LANE_MODE, st->lane_mode);
>
Re: [PATCH 09/15] iio: adc: ad4030: Support multiple data lanes per channel
Posted by Andy Shevchenko 1 month ago
On Sat, Aug 30, 2025 at 3:43 AM Marcelo Schmitt
<marcelo.schmitt@analog.com> wrote:
>
> AD4030 and similar chips can output ADC sample data through 1, 2, or 4
> lines per channel. The number of SPI lines the device uses to output data
> is specified in firmware. Parse SPI read bus width setting from firmware
> and configure the device to use that amount of lines to output data.

...

> -               offload_bpw  = data_width;
> +               offload_bpw  = data_width / (1 << st->lane_mode);

This is interesting. What's the difference you see to the use of the
right shift?
Also note, in case of lane_mode == 31 (yeah, I understand that here
it's not the case) this is UB in accordance with C standard.

-- 
With Best Regards,
Andy Shevchenko