All of lore.kernel.org
 help / color / mirror / Atom feed
From: Andy Shevchenko <andriy.shevchenko@intel.com>
To: Kurt Borja <kuurtb@gmail.com>
Cc: "Jonathan Cameron" <jic23@kernel.org>,
	"Rob Herring" <robh@kernel.org>,
	"Krzysztof Kozlowski" <krzk+dt@kernel.org>,
	"Conor Dooley" <conor+dt@kernel.org>,
	"Tobias Sperling" <tobias.sperling@softing.com>,
	"David Lechner" <dlechner@baylibre.com>,
	"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,
	"Jonathan Cameron" <Jonathan.Cameron@huawei.com>
Subject: Re: [PATCH v3 2/2] iio: adc: Add ti-ads1018 driver
Date: Sat, 29 Nov 2025 16:21:40 +0200	[thread overview]
Message-ID: <aSsBdJZDWcadxEHC@smile.fi.intel.com> (raw)
In-Reply-To: <20251128-ads1x18-v3-2-a6ebab815b2d@gmail.com>

On Fri, Nov 28, 2025 at 10:47:13PM -0500, Kurt Borja wrote:
> Add ti-ads1018 driver for Texas Instruments ADS1018 and ADS1118 SPI
> analog-to-digital converters.
> 
> These chips' MOSI pin is shared with a data-ready interrupt. Defining
> this interrupt in devicetree is optional, therefore we only create an
> IIO trigger if one is found.
> 
> Handling this interrupt requires some considerations. When enabling the
> trigger the CS line is tied low (active), thus we need to hold
> spi_bus_lock() too, to avoid state corruption. This is done inside the
> set_trigger_state() callback, to let users use other triggers without
> wasting a bus lock.

Thank you for the update, my comments below.

...

> +#include <linux/array_size.h>
> +#include <linux/bitfield.h>
> +#include <linux/bitmap.h>

> +#include <linux/byteorder/generic.h>

Must be asm/byteorder.h after the linux/* generic group...

> +#include <linux/dev_printk.h>
> +#include <linux/gpio/consumer.h>
> +#include <linux/interrupt.h>
> +#include <linux/math.h>
> +#include <linux/mod_devicetable.h>
> +#include <linux/module.h>
> +#include <linux/spi/spi.h>
> +#include <linux/types.h>
> +#include <linux/units.h>
> +

...here

#include <asm/byteorder.h>
+ blank line.

...

> +static void ads1018_set_data_rate_mode(struct ads1018 *ads1018, unsigned int address,
> +				       u8 val)

It's a bit too long.

static void ads1018_set_data_rate_mode(struct ads1018 *ads1018,
				       unsigned int address, u8 val)

...

> +static void ads1018_set_pga_mode(struct ads1018 *ads1018, unsigned int address,
> +				 u8 val)

This is not so, but I would resplit logically, either

static void ads1018_set_pga_mode(struct ads1018 *ads1018, unsigned int address, u8 val)

(however it's significantly longer than 80 limit)

OR

static void ads1018_set_pga_mode(struct ads1018 *ads1018,
				 unsigned int address, u8 val)

(better and consistent with the above).

...

> +/**
> + * ads1018_calc_delay - Calculates an appropriate delay for a single-shot
> + *			reading

Having this on a single line is fine I suppose.

> + * @ad1018: Device data
> + *
> + * Calculates an appropriate delay for a single shot reading, assuming the
> + * device's maximum data-rate is used.
> + *
> + * Context: Expects iio_device_claim_direct() is held.
> + *
> + * Return: Delay in microseconds.

Does 0 have any special meaning?

> + */

...

> +	/* We subtract 10% data-rate error */
> +	hz -= DIV_ROUND_UP(hz, 10);

Hmm... For delays I expect to see adding 10% to have a good margin.

...

> + * Context: Expects spi_bus_lock() is held.

Do we have a lockdep assert for this?

...

> +static int ads1018_read_unlocked(struct ads1018 *ads1018, __be16 *cnv, bool hold_cs)

Hmm... Don't we want to return value in CPU order? I don't know the answer
here, and IIRC IIO triggers might be actually good with endianess conversion
done, if required, in user space.

...

> + * Context: Expects iio_device_claim_direct() is held.

Jonathan et al., do we have lockdep assert available for this?
I really prefer to see the code for it, while comment is good,
it is not good enough.

...

> +static int
> +ads1018_write_raw_unlocked(struct iio_dev *indio_dev, struct iio_chan_spec const *chan,
> +			   int val, int val2, long mask)
> +{
> +	struct ads1018 *ads1018 = iio_priv(indio_dev);
> +	const struct ads1018_chip_info *info = ads1018->chip_info;
> +	unsigned int i;
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SCALE:
> +		for (i = 0; i < info->num_pga_mode_to_gain; i++) {
> +			if (val == info->pga_mode_to_gain[i][0] &&
> +			    val2 == info->pga_mode_to_gain[i][1])
> +				break;
> +		}

> +

You can remove this blank line as the condition is tighten with the for-loop.
But up to you, I'm fine with either.

> +		if (i == info->num_pga_mode_to_gain)
> +			return -EINVAL;
> +
> +		ads1018_set_pga_mode(ads1018, chan->scan_index, i);
> +		return 0;
> +
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		for (i = 0; i < info->num_data_rate_mode_to_hz; i++) {
> +			if (val == info->data_rate_mode_to_hz[i])
> +				break;
> +		}

> +

Ditto.

> +		if (i == info->num_data_rate_mode_to_hz)
> +			return -EINVAL;
> +
> +		ads1018_set_data_rate_mode(ads1018, chan->scan_index, i);
> +		return 0;
> +
> +	default:
> +		return -EOPNOTSUPP;
> +	}
> +}

...

> +	if (iio_device_claim_buffer_mode(indio_dev))
> +		goto out_notify_done;
> +
> +	if (iio_trigger_using_own(indio_dev)) {
> +		disable_irq(ads1018->drdy_irq);
> +		ret = ads1018_read_unlocked(ads1018, &scan.conv, true);
> +		enable_irq(ads1018->drdy_irq);
> +	} else {
> +		ret = spi_read(ads1018->spi, ads1018->rx_buf, sizeof(ads1018->rx_buf));
> +		scan.conv = ads1018->rx_buf[0];
> +	}
> +
> +	iio_device_release_buffer_mode(indio_dev);
> +
> +	if (ret)
> +		goto out_notify_done;
> +
> +	iio_push_to_buffers_with_ts(indio_dev, &scan, sizeof(scan), pf->timestamp);
> +
> +out_notify_done:
> +	iio_trigger_notify_done(ads1018->indio_trig);

Jonathan et al., maybe we need an ACQUIRE() class for this? It will solve
the conditional scoped guard case, no?

...

> +static int ads1018_trigger_setup(struct iio_dev *indio_dev)
> +{
> +	struct ads1018 *ads1018 = iio_priv(indio_dev);
> +	struct spi_device *spi = ads1018->spi;
> +	struct device *dev = &spi->dev;
> +	const char *con_id = "drdy";
> +	int ret;
> +
> +	ads1018->drdy_gpiod = devm_gpiod_get_optional(dev, con_id, GPIOD_IN);
> +	if (IS_ERR(ads1018->drdy_gpiod))
> +		return dev_err_probe(dev, PTR_ERR(ads1018->drdy_gpiod),
> +				     "Failed to get %s GPIO.\n", con_id);
> +
> +	/* First try to get IRQ from SPI core, then from GPIO */
> +	if (spi->irq > 0)
> +		ads1018->drdy_irq = spi->irq;
> +	else if (ads1018->drdy_gpiod)
> +		ads1018->drdy_irq = gpiod_to_irq(ads1018->drdy_gpiod);
> +	if (ads1018->drdy_irq < 0)
> +		return dev_err_probe(dev, ads1018->drdy_irq,
> +				     "Failed to get IRQ from %s GPIO.\n", con_id);
> +
> +	/* An IRQ line is only an optional requirement for the IIO trigger */
> +	if (ads1018->drdy_irq == 0)
> +		return 0;
> +
> +	ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-drdy", indio_dev->name,
> +						     iio_device_id(indio_dev));

	ads1018->indio_trig = devm_iio_trigger_alloc(dev, "%s-dev%d-%s",
						     indio_dev->name,
						     iio_device_id(indio_dev),
						     con_id);

This also will be kept below 80 limit.

> +	if (!ads1018->indio_trig)
> +		return -ENOMEM;
> +
> +	iio_trigger_set_drvdata(ads1018->indio_trig, ads1018);
> +	ads1018->indio_trig->ops = &ads1018_trigger_ops;
> +
> +	ret = devm_iio_trigger_register(dev, ads1018->indio_trig);
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * The "data-ready" IRQ line is shared with the MOSI pin, thus we need
> +	 * to keep it disabled until we actually request data.
> +	 */
> +	return devm_request_irq(dev, ads1018->drdy_irq, ads1018_irq_handler,
> +				IRQF_NO_AUTOEN, ads1018->chip_info->name, ads1018);
> +}

...

> +static int ads1018_spi_probe(struct spi_device *spi)
> +{
> +	const struct ads1018_chip_info *info = spi_get_device_match_data(spi);

	struct device *dev = &spi->dev;

> +	struct iio_dev *indio_dev;
> +	struct ads1018 *ads1018;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*ads1018));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	ads1018 = iio_priv(indio_dev);
> +	ads1018->spi = spi;
> +	ads1018->chip_info = info;
> +	spi_set_drvdata(spi, ads1018);
> +
> +	indio_dev->modes = INDIO_DIRECT_MODE;
> +	indio_dev->name = info->name;
> +	indio_dev->info = &ads1018_iio_info;
> +	indio_dev->channels = info->channels;
> +	indio_dev->num_channels = info->num_channels;
> +
> +	for (unsigned int i = 0; i < ADS1018_CHANNELS_MAX; i++) {
> +		ads1018->chan_data[i].data_rate_mode = ADS1018_DRATE_DEFAULT;
> +		ads1018->chan_data[i].pga_mode = ADS1018_PGA_DEFAULT;
> +	}
> +
> +	ads1018->xfer.rx_buf = ads1018->rx_buf;
> +	ads1018->xfer.len = sizeof(ads1018->rx_buf);
> +	spi_message_init_with_transfers(&ads1018->msg_read, &ads1018->xfer, 1);
> +
> +	ret = ads1018_trigger_setup(indio_dev);
> +	if (ret)
> +		return ret;

> +	ret = devm_iio_triggered_buffer_setup(&spi->dev, indio_dev, iio_pollfunc_store_time,
> +					      ads1018_trigger_handler, &ads1018_buffer_ops);

Too long. With the above done

	ret = devm_iio_triggered_buffer_setup(dev, indio_dev,
					      iio_pollfunc_store_time,
					      ads1018_trigger_handler,
					      &ads1018_buffer_ops);

> +	if (ret)
> +		return ret;
> +
> +	return devm_iio_device_register(&spi->dev, indio_dev);
> +}

...

> +/**
> + * ADS1018_FSR_TO_SCALE - Converts FSR into scale
> + * @_fsr: Full-scale range in millivolts
> + * @_res: ADC resolution

Add here something like this:

*
* The macro is crafted to avoid potential overflows on 32-bit machines.
* This imposes restrictions to the possible values for @_fsr (less
* than 274878), and @_res (great or equal to 6 bits).
*

> + * Return: Scale in IIO_VAL_INT_PLUS_NANO format
> + */
> +#define ADS1018_FSR_TO_SCALE(_fsr, _res) \
> +	{ 0, ((_fsr) * (MICRO >> 6)) / BIT((_res) - 6) }

-- 
With Best Regards,
Andy Shevchenko



  reply	other threads:[~2025-11-29 14:21 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-11-29  3:47 [PATCH v3 0/2] iio: Add support for TI ADS1X18 ADCs Kurt Borja
2025-11-29  3:47 ` [PATCH v3 1/2] dt-bindings: iio: adc: Add TI ADS1018/ADS1118 Kurt Borja
2025-11-29  9:25   ` Krzysztof Kozlowski
2025-11-30  3:32     ` Kurt Borja
2025-11-29  3:47 ` [PATCH v3 2/2] iio: adc: Add ti-ads1018 driver Kurt Borja
2025-11-29 14:21   ` Andy Shevchenko [this message]
2025-11-29 14:23     ` Andy Shevchenko
2025-11-30  3:31     ` Kurt Borja
2025-11-30 16:45       ` Andy Shevchenko
2025-12-01 16:07       ` David Lechner
2025-12-01 19:47         ` Kurt Borja
2025-12-01 21:53           ` David Lechner
2025-12-02 14:46             ` Kurt Borja
2025-12-06 19:27               ` Jonathan Cameron
2025-12-07 16:01                 ` Kurt Borja
2025-11-29 17:08   ` kernel test robot
2025-12-01 23:09   ` David Lechner
2025-12-02 14:39     ` Kurt Borja
2025-12-02 14:59       ` Andy Shevchenko
2025-12-02 17:49         ` Kurt Borja
2025-12-02 18:04           ` Andy Shevchenko
2025-12-02 16:52     ` 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=aSsBdJZDWcadxEHC@smile.fi.intel.com \
    --to=andriy.shevchenko@intel.com \
    --cc=Jonathan.Cameron@huawei.com \
    --cc=andy@kernel.org \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --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 \
    --cc=tobias.sperling@softing.com \
    /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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.