Linux IIO development
 help / color / mirror / Atom feed
From: David Lechner <dlechner@baylibre.com>
To: Kurt Borja <kuurtb@gmail.com>,
	Jonathan Cameron <jic23@kernel.org>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzk+dt@kernel.org>,
	Conor Dooley <conor+dt@kernel.org>
Cc: "Nuno Sá" <nuno.sa@analog.com>,
	"Andy Shevchenko" <andy@kernel.org>,
	linux-iio@vger.kernel.org, devicetree@vger.kernel.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v4 03/10] iio: adc: add the ti-ads1262 driver
Date: Mon, 31 Aug 2026 15:22:57 -0500	[thread overview]
Message-ID: <bc00d888-9944-4a22-88e3-8f4d7384d037@baylibre.com> (raw)
In-Reply-To: <20260828-ads126x-v4-3-1dc27e9c0260@gmail.com>

On 8/28/26 1:38 AM, Kurt Borja wrote:
> Add the ti-ads1262 driver with initial support for the primary ADC
> (ADC1). The ADS1263 auxiliary ADC (ADC2) is handled by a separate driver
> and interoperability considerations were taken into account.
> 

...

> +#define ADS1262_FW_CHANNEL_COUNT		16
> +#define ADS1262_MON_CHANNEL_COUNT		4
> +#define ADS1262_REGMAP_WRITE_SZ			8
> +#define ADS1262_MONITOR_ADDR_OFFSET		100

Where does this offset come from? I would make the address the value that
gets written to MUXP/MUXN. But it looks like we are using the same value
for the .channel, so setting .address to that would be redundant.

> +
> +#define ADS1262_ADC1_RESOLUTION			32
> +
> +struct ads1262 {
> +	struct spi_device *spi;
> +	struct regmap *regmap;
> +	struct gpio_desc *start_gpiod;
> +	/* protects concurrent SPI transfers */
> +	struct mutex xfer_lock;
> +	/* protects channel state */
> +	struct mutex chan_lock;
> +	struct completion drdy;
> +	unsigned long clk_rate;
> +	u8 dev_id;
> +};
> +
> +static const char * const ads1262_device_id_to_name[] = {
> +	[ADS1262_DEV_ID] = "ads1262",
> +	[ADS1263_DEV_ID] = "ads1263",
> +};
> +
> +static const struct iio_chan_spec ads1262_monitor_chan_specs[] = {
> +	{
> +		.type = IIO_TEMP,
> +		.channel = ADS1262_INPMUX_TEMP,
> +		.channel2 = ADS1262_INPMUX_TEMP,

Since these are the same, I would just not set .channel2 and later say
MUXN = spec->differential ? spec->channel2 : spec->channel. Same applies
to others below.

> +		.address = ADS1262_MONITOR_ADDR_OFFSET + 0,
> +		.scan_type = {
> +			.format = IIO_SCAN_FORMAT_SIGNED_INT,
> +			.realbits = ADS1262_ADC1_RESOLUTION,
> +			.storagebits = 32,
> +			.endianness = IIO_BE,
> +		},
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),

Where is SCALE and OFFSET?

> +	},
> +	{
> +		.type = IIO_VOLTAGE,
> +		.channel = ADS1262_INPMUX_AVDD,
> +		.channel2 = ADS1262_INPMUX_AVDD,
> +		.indexed = 1,
> +		.address = ADS1262_MONITOR_ADDR_OFFSET + 1,
> +		.scan_type = {
> +			.format = IIO_SCAN_FORMAT_SIGNED_INT,
> +			.realbits = ADS1262_ADC1_RESOLUTION,
> +			.storagebits = 32,
> +			.endianness = IIO_BE,
> +		},
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +	},
> +	{
> +		.type = IIO_VOLTAGE,
> +		.channel = ADS1262_INPMUX_DVDD,
> +		.channel2 = ADS1262_INPMUX_DVDD,
> +		.indexed = 1,
> +		.address = ADS1262_MONITOR_ADDR_OFFSET + 2,
> +		.scan_type = {
> +			.format = IIO_SCAN_FORMAT_SIGNED_INT,
> +			.realbits = ADS1262_ADC1_RESOLUTION,
> +			.storagebits = 32,
> +			.endianness = IIO_BE,
> +		},
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +	},
> +	{
> +		.type = IIO_VOLTAGE,
> +		.channel = ADS1262_INPMUX_TDAC,
> +		.channel2 = ADS1262_INPMUX_TDAC,

Hmm... a differential where channel == channel2 usually means a shorted
input. TDACP and TDACN can be controlled indepedantly, so really are two
separate channels.

> +		.indexed = 1,
> +		.differential = 1,
> +		.address = ADS1262_MONITOR_ADDR_OFFSET + 3,
> +		.scan_type = {
> +			.format = IIO_SCAN_FORMAT_SIGNED_INT,
> +			.realbits = ADS1262_ADC1_RESOLUTION,
> +			.storagebits = 32,
> +			.endianness = IIO_BE,
> +		},
> +		.info_mask_separate = BIT(IIO_CHAN_INFO_RAW),
> +	},
> +};
> +

...

> +static int ads1262_channel_read(struct iio_dev *indio_dev,
> +				const struct iio_chan_spec *spec, __be32 *val)
> +{
> +	struct ads1262 *st = iio_priv(indio_dev);
> +	int ret;
> +
> +	IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim);
> +	if (IIO_DEV_ACQUIRE_FAILED(claim))
> +		return -EBUSY;
> +
> +	ret = ads1262_set_runmode(st, ADS1262_RUNMODE_PULSE);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads1262_channel_enable(st, spec);
> +	if (ret)
> +		return ret;
> +
> +	reinit_completion(&st->drdy);
> +
> +	ret = ads1262_dev_start_one(st);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads1262_wait_for_conversion(st);
> +	if (ret)

Since wait is interruptable, do we need to do something to stop the
conversion here?

> +		return ret;
> +
> +	return ads1262_dev_read_by_cmd(st, ADS1262_OPCODE_RDATA1, val);
> +}
> +

...

> +static int ads1262_fwnode_xlate(struct iio_dev *indio_dev,
> +				const struct fwnode_reference_args *iiospec)
> +{
> +	/* REVISIT: the auxiliary ADC (ADC2) is currently not supported */
> +	if (iiospec->nargs > 1 && iiospec->args[1])
> +		return -EINVAL;
> +
> +	if (!iiospec->nargs)
> +		return 0;
> +
> +	for (unsigned int i = 0; i < indio_dev->num_channels; i++) {

Won't this include the timestamp channel?

> +		if (indio_dev->channels[i].address == iiospec->args[0])

I don't think .address is the right thing to use here (it is coming from
reg in the devcietree). I would expect channel. Otherwise consumers in the
devicetree have to be away of how channels were assigned rather than picking
the datasheet channel number.

And the devicetree bindings should mention the monitor channel numbers (11 - 14).

> +			return i;
> +	}
> +
> +	return -EINVAL;
> +}
> +

...

> +static int ads1262_spi_probe(struct spi_device *spi)
> +{
> +	struct device *dev = &spi->dev;
> +	struct iio_dev *indio_dev;
> +	struct ads1262 *st;
> +	unsigned long rate;
> +	struct clk *clk;
> +	int irq;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->info = &ads1262_iio_info;
> +
> +	st = iio_priv(indio_dev);
> +	st->spi = spi;
> +	init_completion(&st->drdy);
> +
> +	ret = devm_mutex_init(dev, &st->chan_lock);
> +	if (ret)
> +		return ret;
> +	ret = devm_mutex_init(dev, &st->xfer_lock);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads1262_parse_channels(indio_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = ads1262_supply_setup(st);
> +	if (ret)
> +		return ret;
> +
> +	clk = devm_clk_get_optional_enabled(dev, NULL);
> +	if (IS_ERR(clk))
> +		return dev_err_probe(dev, PTR_ERR(clk), "failed to get external clock\n");
> +
> +	rate = clk_get_rate(clk);
> +	if (clk && !rate)
> +		return dev_err_probe(dev, -EINVAL, "failed to get clock rate\n");
> +	st->clk_rate = rate ? rate : ADS1262_NOMINAL_CLK_RATE;
> +
> +	st->start_gpiod = devm_gpiod_get_optional(dev, "start", GPIOD_OUT_LOW);
> +	if (IS_ERR(st->start_gpiod))
> +		return dev_err_probe(dev, PTR_ERR(st->start_gpiod),
> +				     "failed to get start GPIO\n");
> +
> +	st->regmap = devm_regmap_init(dev, &ads1262_regmap_bus, st,
> +				      &ads1262_regmap_config);
> +	if (IS_ERR(st->regmap))
> +		return PTR_ERR(st->regmap);
> +
> +	ret = ads1262_dev_configure(st);
> +	if (ret)
> +		return dev_err_probe(dev, ret, "failed to configure device\n");
> +
> +	indio_dev->name = ads1262_device_id_to_name[st->dev_id];

Not so sure about this. Almost always, this is coming from the compatible
match data. So unless we plan on trusting the device ID returned by the
chip over the devicetree when we add more to the device id tables and looking
up per-chip behavior from there instead of the compatible, I would go with
the traditional approach. That way the name userpace sees match the driver
behavior that goes with the other chip-specific match data that is likely
to be added in the future.

> +
> +	/*
> +	 * REVISIT: This chip has software polling capabilities, which could be
> +	 * used to stop depending on the DRDY signal.
> +	 *
> +	 * Additionally, the MISO pin also can be used as a DRDY IRQ, in which
> +	 * case the interrupt would be named 'doutdrdy', but requires extra
> +	 * timing and synchronization considerations to be reliable.
> +	 */
> +	irq = fwnode_irq_get_byname(dev_fwnode(dev), "drdy");
> +	if (irq < 0)
> +		return dev_err_probe(dev, irq,
> +				     "the 'drdy' IRQ is currently required for operation\n");
> +
> +	ret = devm_request_irq(dev, irq, ads1262_irq_handler, IRQF_NO_THREAD,
> +			       indio_dev->name, st);
> +	if (ret)
> +		return ret;
> +
> +	return devm_iio_device_register(dev, indio_dev);
> +}
> +

  parent reply	other threads:[~2026-08-31 20:23 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-08-28  6:38 [PATCH v4 00/10] iio: adc: Add TI ADS126X ADC family support Kurt Borja
2026-08-28  6:38 ` [PATCH v4 01/10] dt-bindings: adc: add excitation-current-chopping property Kurt Borja
2026-08-28 16:33   ` Conor Dooley
2026-08-28  6:38 ` [PATCH v4 02/10] dt-bindings: iio: adc: support the TI ADS126x ADC family Kurt Borja
2026-08-28 16:39   ` Conor Dooley
2026-08-30  1:57     ` Jonathan Cameron
2026-08-31 16:24       ` Conor Dooley
2026-08-30  1:53   ` Jonathan Cameron
2026-08-31 19:44   ` David Lechner
2026-08-28  6:38 ` [PATCH v4 03/10] iio: adc: add the ti-ads1262 driver Kurt Borja
2026-08-28  8:09   ` Andy Shevchenko
2026-08-31 20:22   ` David Lechner [this message]
2026-08-28  6:38 ` [PATCH v4 04/10] iio: adc: ti-ads1262: support per-channel sampling frequency Kurt Borja
2026-08-30  1:36   ` Jonathan Cameron
2026-08-30  2:22   ` Jonathan Cameron
2026-08-28  6:38 ` [PATCH v4 05/10] iio: adc: ti-ads1262: support per-channel reference and gain Kurt Borja
2026-08-28  6:38 ` [PATCH v4 06/10] iio: adc: ti-ads1262: support input chopping Kurt Borja
2026-08-28  6:38 ` [PATCH v4 07/10] iio: adc: ti-ads1262: support excitation currents Kurt Borja
2026-08-28  6:38 ` [PATCH v4 08/10] iio: adc: ti-ads1262: support triggered buffer sampling Kurt Borja
2026-08-28  6:38 ` [PATCH v4 09/10] iio: adc: ti-ads1262: support REFOUT and VBIAS regulators Kurt Borja
2026-08-28  6:38 ` [PATCH v4 10/10] iio: adc: ti-ads1262: support common mode supplies Kurt Borja

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=bc00d888-9944-4a22-88e3-8f4d7384d037@baylibre.com \
    --to=dlechner@baylibre.com \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=jic23@kernel.org \
    --cc=krzk+dt@kernel.org \
    --cc=kuurtb@gmail.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=robh@kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox