public inbox for linux-kernel@vger.kernel.org
 help / color / mirror / Atom feed
From: Jonathan Cameron <jic23@kernel.org>
To: Antoniu Miclaus <antoniu.miclaus@analog.com>
Cc: <conor+dt@kernel.org>, <dlechner@baylibre.com>,
	<linux-iio@vger.kernel.org>, <devicetree@vger.kernel.org>,
	<linux-kernel@vger.kernel.org>, <linux-pwm@vger.kernel.org>
Subject: Re: [PATCH v5 6/6] iio: adc: ad4851: add ad485x driver
Date: Sat, 2 Nov 2024 14:58:47 +0000	[thread overview]
Message-ID: <20241102145847.6e615d42@jic23-huawei> (raw)
In-Reply-To: <20241101112358.22996-7-antoniu.miclaus@analog.com>

On Fri, 1 Nov 2024 13:23:58 +0200
Antoniu Miclaus <antoniu.miclaus@analog.com> wrote:

> Add support for the AD485X a fully buffered, 8-channel simultaneous
> sampling, 16/20-bit, 1 MSPS data acquisition system (DAS) with
> differential, wide common-mode range inputs.
> 
> Signed-off-by: Antoniu Miclaus <antoniu.miclaus@analog.com>

Hi Antoniu.

Various things inline to add to David's much more thorough review.

Jonathan

> +enum {
> +	AD4851_SCAN_TYPE_NORMAL,
> +	AD4851_SCAN_TYPE_RESOLUTION_BOOST,
> +};
> +
> +struct ad4851_state {
> +	struct spi_device *spi;
> +	struct pwm_device *cnv;
> +	struct iio_backend *back;
> +	/*
> +	 * Synchronize access to members the of driver state, and ensure
> +	 * atomicity of consecutive regmap operations.
> +	 */
> +	struct mutex lock;
> +	struct regmap *regmap;
> +	struct regulator *vrefbuf;
> +	struct regulator *vrefio;
> +	const struct ad4851_chip_info *info;
> +	struct gpio_desc *pd_gpio;
> +	bool resolution_boost_enabled;
> +	unsigned long sampling_freq;
> +	unsigned int (*scales)[2];

As described below consider clearer type definition by using
a structure.

> +	int *offsets;
> +};
>
> +static int ad4851_setup(struct ad4851_state *st)
> +{
> +	unsigned int product_id;
> +	int ret;
> +
> +	if (st->pd_gpio) {

Not obvious why you are pulsing twice. Add a comment.

> +		gpiod_set_value(st->pd_gpio, GPIOD_OUT_HIGH);
> +		fsleep(1);
> +		gpiod_set_value(st->pd_gpio, GPIOD_OUT_LOW);
> +		fsleep(1);
> +		gpiod_set_value(st->pd_gpio, GPIOD_OUT_HIGH);
> +		fsleep(1);
> +		gpiod_set_value(st->pd_gpio, GPIOD_OUT_LOW);
> +		fsleep(1000);
> +	}
...

> +static int ad4851_calibrate(struct ad4851_state *st)
> +{
> +	unsigned int opt_delay, lane_num, delay, i, s, c;
> +	enum iio_backend_interface_type interface_type;
> +	DECLARE_BITMAP(pn_status, AD4851_MAX_LANES * AD4851_MAX_IODELAY);
> +	bool status;
> +	int ret;
> +
> +	ret = iio_backend_interface_type_get(st->back, &interface_type);
> +	if (ret)
> +		return ret;
> +
> +	switch (interface_type) {
> +	case IIO_BACKEND_INTERFACE_SERIAL_CMOS:
> +		lane_num = st->info->num_channels;

num_lanes is probably a better name as lane_num sounds like the number
of a particular lane rather than now many there are.

> +		break;
> +	case IIO_BACKEND_INTERFACE_SERIAL_LVDS:
> +		lane_num = 1;
> +		break;
> +	default:
> +		return -EINVAL;
> +	}
> +
> +	if (st->info->resolution == 16) {
> +		ret = iio_backend_data_size_set(st->back, 24);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_write(st->regmap, AD4851_REG_PACKET,
> +				   AD4851_TEST_PAT | AD4857_PACKET_SIZE_24);
> +		if (ret)
> +			return ret;
> +	} else {
> +		ret = iio_backend_data_size_set(st->back, 32);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_write(st->regmap, AD4851_REG_PACKET,
> +				   AD4851_TEST_PAT | AD4858_PACKET_SIZE_32);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < st->info->num_channels; i++) {
> +		ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_0(i),
> +				   AD4851_TESTPAT_0_DEFAULT);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_1(i),
> +				   AD4851_TESTPAT_1_DEFAULT);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_2(i),
> +				   AD4851_TESTPAT_2_DEFAULT);
> +		if (ret)
> +			return ret;
> +
> +		ret = regmap_write(st->regmap, AD4851_REG_TESTPAT_3(i),
> +				   AD4851_TESTPAT_3_DEFAULT(i));
> +		if (ret)
> +			return ret;
> +
> +		ret = iio_backend_chan_enable(st->back, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < lane_num; i++) {
> +		for (delay = 0; delay < AD4851_MAX_IODELAY; delay++) {
> +			ret = iio_backend_iodelay_set(st->back, i, delay);
> +			if (ret)
> +				return ret;

Blank line here is more consistent with surounding code (which looks better
than this block does).

> +			ret = iio_backend_chan_status(st->back, i, &status);
> +			if (ret)
> +				return ret;
> +
> +			if (status)
> +				set_bit(i * AD4851_MAX_IODELAY + delay, pn_status);
> +			else
> +				clear_bit(i * AD4851_MAX_IODELAY + delay, pn_status);
> +		}
> +	}
> +
> +	for (i = 0; i < lane_num; i++) {
> +		status = test_bit(i * AD4851_MAX_IODELAY, pn_status);
> +		c = ad4851_find_opt(&status, AD4851_MAX_IODELAY, &s);
> +		if (c < 0)
> +			return c;
> +
> +		opt_delay = s + c / 2;
> +		ret = iio_backend_iodelay_set(st->back, i, opt_delay);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	for (i = 0; i < st->info->num_channels; i++) {
> +		ret = iio_backend_chan_disable(st->back, i);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	ret = iio_backend_data_size_set(st->back, 20);
> +	if (ret)
> +		return ret;
> +
> +	return regmap_write(st->regmap, AD4851_REG_PACKET, 0);
> +}

> +
> +static int ad4851_set_calibscale(struct ad4851_state *st, int ch, int val,
> +				 int val2)
> +{
> +	u64 gain;
> +	u8 buf[0];

As per my very brief review the other day, 0 length is a problem.

> +	int ret;
> +
> +	if (val < 0 || val2 < 0)
> +		return -EINVAL;
> +
> +	gain = val * MICRO + val2;
> +	gain = DIV_U64_ROUND_CLOSEST(gain * 32768, MICRO);
> +
> +	put_unaligned_be16(gain, buf);
> +
> +	guard(mutex)(&st->lock);
> +
> +	ret = regmap_write(st->regmap, AD4851_REG_CHX_GAIN_MSB(ch),
> +			   buf[0]);

Don't wrap so early. It fits under 80 chars on oneline.

> +	if (ret)
> +		return ret;
> +
> +	return regmap_write(st->regmap, AD4851_REG_CHX_GAIN_LSB(ch),
> +			    buf[1]);
> +}

> +
> +static const unsigned int ad4851_scale_table[][2] = {
I'd suggest a structure for each entry. Then you can do
ad4851_scale_table[i].reg_val etc
rather than opaque indexing to 0 or 1.

> +	{ 2500, 0x0 },
> +	{ 5000, 0x1 },
> +	{ 5000, 0x2 },
> +	{ 10000, 0x3 },
> +	{ 6250, 0x04 },
> +	{ 12500, 0x5 },
> +	{ 10000, 0x6 },
> +	{ 20000, 0x7 },
> +	{ 12500, 0x8 },
> +	{ 25000, 0x9 },
> +	{ 20000, 0xA },
> +	{ 40000, 0xB },
> +	{ 25000, 0xC },
> +	{ 50000, 0xD },
> +	{ 40000, 0xE },
> +	{ 80000, 0xF },
> +};
...

> +
> +static int ad4851_get_scale(struct ad4851_state *st,
> +			    const struct iio_chan_spec *chan, int *val,
> +			    int *val2)
> +{
> +	int i, softspan_val;
> +	int ret;
> +
> +	ret = regmap_read(st->regmap, AD4851_REG_CHX_SOFTSPAN(chan->channel),
> +			  &softspan_val);
> +	if (ret)
> +		return ret;
> +
> +	for (i = 0; i < ARRAY_SIZE(ad4851_scale_table); i++) {
> +		if (softspan_val == ad4851_scale_table[i][1])

See above - I'd use a structure for the scale_table entries
so this and the [0] below can be named structure elements.

> +			break;
> +	}
> +
> +	if (i == ARRAY_SIZE(ad4851_scale_table))
> +		return -EIO;
> +
> +	__ad4851_get_scale(st, ad4851_scale_table[i][0], val, val2);
> +
> +	return IIO_VAL_INT_PLUS_MICRO;
> +}


> +
> +static int ad4851_probe(struct spi_device *spi)
> +{
> +	struct iio_dev *indio_dev;

struct device *dev = *spi->dev;
will shorten a lot of code in here so probably worth doing.

> +	struct ad4851_state *st;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(&spi->dev, sizeof(*st));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	st = iio_priv(indio_dev);
> +	st->spi = spi;
> +
> +	ret = devm_mutex_init(&spi->dev, &st->lock);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_regulator_bulk_get_enable(&spi->dev,
> +					     ARRAY_SIZE(ad4851_power_supplies),
> +					     ad4851_power_supplies);
> +	if (ret)
> +		return dev_err_probe(&spi->dev, ret,
> +				     "failed to get and enable supplies\n");
> +
> +	ret = devm_regulator_get_enable_optional(&spi->dev, "vddh");
> +	if (ret < 0 && ret != -ENODEV)
> +		return dev_err_probe(&spi->dev, ret, "failed to get vddh voltage\n");
You aren't trying to get the voltage, just turn it on.  So update the commit
message to "failed to enable vddh\n"

> +
> +	ret = devm_regulator_get_enable_optional(&spi->dev, "vddl");
> +	if (ret < 0 && ret != -ENODEV)
> +		return dev_err_probe(&spi->dev, ret, "failed to get vddl voltage\n");
Same here.
> +
> +	st->vrefbuf = devm_regulator_get_optional(&spi->dev, "vrefbuf");
> +	if (IS_ERR(st->vrefbuf)) {
> +		if (PTR_ERR(st->vrefbuf) != -ENODEV)
> +			return dev_err_probe(&spi->dev, PTR_ERR(st->vrefbuf),
> +					     "Failed to get vrefbuf regulator\n");
> +	}
> +
> +	st->vrefio = devm_regulator_get_optional(&spi->dev, "vrefio");
> +	if (IS_ERR(st->vrefio)) {
> +		if (PTR_ERR(st->vrefio) != -ENODEV)
> +			return dev_err_probe(&spi->dev, PTR_ERR(st->vrefio),
> +					     "Failed to get vrefbuf regulator\n");

wrong regulator in the message.

> +	}
> +
> +	st->pd_gpio = devm_gpiod_get_optional(&spi->dev, "pd", GPIOD_OUT_LOW);
> +	if (IS_ERR(st->pd_gpio))
> +		return dev_err_probe(&spi->dev, PTR_ERR(st->pd_gpio),
> +				     "Error on requesting pd GPIO\n");
> +
> +	st->cnv = devm_pwm_get(&spi->dev, NULL);
> +	if (IS_ERR(st->cnv))
> +		return dev_err_probe(&spi->dev, PTR_ERR(st->cnv),
> +				     "Error on requesting pwm\n");
> +
> +	st->info = spi_get_device_match_data(spi);
> +	if (!st->info)
> +		return -ENODEV;
> +
> +	st->regmap = devm_regmap_init_spi(spi, &regmap_config);
> +	if (IS_ERR(st->regmap))
> +		return PTR_ERR(st->regmap);
> +
> +	ret = ad4851_scale_fill(st);
> +	if (ret)
> +		return ret;
> +
> +	ret = ad4851_setup(st);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->name = st->info->name;
> +	indio_dev->channels = st->info->channels;
> +	indio_dev->num_channels = st->info->num_channels;
> +	indio_dev->info = &ad4851_iio_info;

Whilst it's not actively used, current best practice is to set
indio_dev->modes = INDIO_DIRECT_MODE;

> +
> +	st->back = devm_iio_backend_get(&spi->dev, NULL);
> +	if (IS_ERR(st->back))
> +		return PTR_ERR(st->back);
> +
> +	ret = devm_iio_backend_request_buffer(&spi->dev, st->back, indio_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_iio_backend_enable(&spi->dev, st->back);
> +	if (ret)
> +		return ret;
> +
> +	ret = ad4851_calibrate(st);
> +	if (ret)
> +		return ret;
> +
> +	return devm_iio_device_register(&spi->dev, indio_dev);
> +}

  parent reply	other threads:[~2024-11-02 14:58 UTC|newest]

Thread overview: 22+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-01 11:23 [PATCH 0/7] *** Add support for AD485x DAS Family *** Antoniu Miclaus
2024-11-01 11:23 ` [PATCH v5 1/6] iio: backend: add API for interface get Antoniu Miclaus
2024-11-01 19:17   ` David Lechner
2024-11-01 11:23 ` [PATCH v5 2/6] iio: backend: add support for data size set Antoniu Miclaus
2024-11-01 19:23   ` David Lechner
2024-11-01 11:23 ` [PATCH v5 3/6] iio: adc: adi-axi-adc: add interface type Antoniu Miclaus
2024-11-01 19:26   ` David Lechner
2024-11-01 11:23 ` [PATCH v5 4/6] iio: adc: adi-axi-adc: set data format Antoniu Miclaus
2024-11-01 19:52   ` David Lechner
2024-11-01 21:00     ` David Lechner
2024-11-01 11:23 ` [PATCH v5 5/6] dt-bindings: iio: adc: add ad4851 Antoniu Miclaus
2024-11-01 11:23 ` [PATCH v5 6/6] iio: adc: ad4851: add ad485x driver Antoniu Miclaus
2024-11-01 15:21   ` Jonathan Cameron
2024-11-01 19:14   ` David Lechner
2024-11-07 10:51     ` Miclaus, Antoniu
2024-11-07 16:13       ` David Lechner
2024-11-07 16:47         ` David Lechner
2024-11-09 15:39           ` Jonathan Cameron
2024-11-11 16:03             ` David Lechner
2024-11-08 12:50         ` Miclaus, Antoniu
2024-11-02 14:58   ` Jonathan Cameron [this message]
2024-11-01 15:17 ` [PATCH 0/7] *** Add support for AD485x DAS Family *** Jonathan Cameron

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=20241102145847.6e615d42@jic23-huawei \
    --to=jic23@kernel.org \
    --cc=antoniu.miclaus@analog.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=dlechner@baylibre.com \
    --cc=linux-iio@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pwm@vger.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