devicetree.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jonathan Cameron <jic23@kernel.org>
To: Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org>
Cc: nuno.sa@analog.com, linux-iio@vger.kernel.org,
	devicetree@vger.kernel.org,
	Dragos Bogdan <dragos.bogdan@analog.com>,
	Lars-Peter Clausen <lars@metafoo.de>,
	Michael Hennerich <Michael.Hennerich@analog.com>,
	Rob Herring <robh@kernel.org>,
	Krzysztof Kozlowski <krzysztof.kozlowski+dt@linaro.org>,
	Conor Dooley <conor+dt@kernel.org>,
	Olivier Moysan <olivier.moysan@foss.st.com>
Subject: Re: [PATCH 10/10] iio: dac: support the ad9739a RF DAC
Date: Thu, 28 Mar 2024 15:51:26 +0000	[thread overview]
Message-ID: <20240328155126.2575d754@jic23-huawei> (raw)
In-Reply-To: <20240328-iio-backend-axi-dac-v1-10-afc808b3fde3@analog.com>

On Thu, 28 Mar 2024 14:22:34 +0100
Nuno Sa via B4 Relay <devnull+nuno.sa.analog.com@kernel.org> wrote:

> From: Nuno Sa <nuno.sa@analog.com>
> 
> The AD9739A is a 14-bit, 2.5 GSPS high performance RF DACs that are capable
> of synthesizing wideband signals from dc up to 3 GHz.
DC perhaps

> 
> A dual-port, source synchronous, LVDS interface simplifies the digital
> interface with existing FGPA/ASIC technology. On-chip controllers are used
> to manage external and internal clock domain variations over temperature to
> ensure reliable data transfer from the host to the DAC core.
> 
> Co-developed-by: Dragos Bogdan <dragos.bogdan@analog.com>
> Signed-off-by: Dragos Bogdan <dragos.bogdan@analog.com>
> Signed-off-by: Nuno Sa <nuno.sa@analog.com>
Hi Nuno,

A few questions / comments inline but on the whole looking good to me.

Jonathan

> ---
>  Documentation/ABI/testing/sysfs-bus-iio-ad9739a |  17 +
>  MAINTAINERS                                     |   1 +
>  drivers/iio/dac/Kconfig                         |  16 +
>  drivers/iio/dac/Makefile                        |   1 +
>  drivers/iio/dac/ad9739a.c                       | 445 ++++++++++++++++++++++++
>  5 files changed, 480 insertions(+)
> 
> diff --git a/Documentation/ABI/testing/sysfs-bus-iio-ad9739a b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> new file mode 100644
> index 000000000000..8a8a5cd10386
> --- /dev/null
> +++ b/Documentation/ABI/testing/sysfs-bus-iio-ad9739a
> @@ -0,0 +1,17 @@
> +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode
> +KernelVersion:	6.9
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Dac operating mode. One of the following modes can be selected:

DAC operating mode. ...

> +         * normal: This is DAC normal mode.
> +         * mixed-mode: In this mode the output is effectively chopped at the

Spaces and tabs mixed...

> +                       DAC sample rate. This has the effect of reducing the
> +                       power of the fundamental signal while increasing the
> +                       power of the images centered around the DAC sample rate,
> +                       thus improving the output power of these images.

Any idea why it is called mixed mode?  Name doesn't suggest to me what the Docs say
this does.

> +
> +What:		/sys/bus/iio/devices/iio:deviceX/out_voltageY_operating_mode_available
> +KernelVersion:	6.9
> +Contact:	linux-iio@vger.kernel.org
> +Description:
> +		Available operating modes.

>  M:	Antoniu Miclaus <antoniu.miclaus@analog.com>
> diff --git a/drivers/iio/dac/Kconfig b/drivers/iio/dac/Kconfig
> index 7c0a8caa9a34..ee0d9798d8b4 100644
> --- a/drivers/iio/dac/Kconfig
> +++ b/drivers/iio/dac/Kconfig
> @@ -131,6 +131,22 @@ config AD5624R_SPI
>  	  Say yes here to build support for Analog Devices AD5624R, AD5644R and
>  	  AD5664R converters (DAC). This driver uses the common SPI interface.
>  
> +config AD9739A
> +	tristate "Analog Devices AD9739A RF DAC spi driver"
> +	depends on SPI
> +	select REGMAP_SPI
> +	select IIO_BACKEND
> +	help
> +	  Say yes here to build support for Analog Devices AD9739A Digital-to
> +	  Analog Converter.
> +
> +	  The driver requires the assistance of the AXI DAC IP core to operate,

Maybe a depends on || COMPILE_TEST to increase build coverage (compared to
a hard depends on)

> +	  since SPI is used for configuration only, while data has to be
> +	  streamed into memory via DMA.
> +
> +	  To compile this driver as a module, choose M here: the module will be
> +	  called ad9739a.
> +


> diff --git a/drivers/iio/dac/ad9739a.c b/drivers/iio/dac/ad9739a.c
> new file mode 100644
> index 000000000000..46431fa345a5
> --- /dev/null
> +++ b/drivers/iio/dac/ad9739a.c

> +
> +enum {
> +	AD9739A_NORMAL_MODE,
> +	AD9739A_MIXED_MODE = 2,

Push these next to the relevant registers and more conventional defines.
Not seeing why the enum helps much here.

> +};
> +
> +static int ad9739a_oper_mode_get(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +	u32 mode;
> +	int ret;
> +
> +	ret = regmap_read(st->regmap, AD9739A_REG_DEC_CNT, &mode);
> +	if (ret)
> +		return ret;
> +
> +	mode = FIELD_GET(AD9739A_DAC_DEC, mode);
> +	/* sanity check we get valid values from the HW */
> +	if (mode != AD9739A_NORMAL_MODE && mode != AD9739A_MIXED_MODE)
> +		return -EIO;
> +	if (!mode)
> +		return AD9739A_NORMAL_MODE;
> +
> +	return AD9739A_MIXED_MODE - 1;

As below. I'd like to see a mapping function, or lookup table or similar
rather than handling this conversion in code.

> +}
> +
> +static int ad9739a_oper_mode_set(struct iio_dev *indio_dev,
> +				 const struct iio_chan_spec *chan, u32 mode)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +
> +	if (mode == AD9739A_MIXED_MODE - 1)
> +		mode = AD9739A_MIXED_MODE;

Why?  Feels like a comment is needed. Or a more obvious conversion function.

> +
> +	return regmap_update_bits(st->regmap, AD9739A_REG_DEC_CNT,
> +				  AD9739A_DAC_DEC, mode);
> +}
> +
> +static int ad9739a_read_raw(struct iio_dev *indio_dev,
> +			    struct iio_chan_spec const *chan,
> +			    int *val, int *val2, long mask)
> +{
> +	struct ad9739a_state *st = iio_priv(indio_dev);
> +
> +	switch (mask) {
> +	case IIO_CHAN_INFO_SAMP_FREQ:
> +		*val = st->sample_rate;
> +		*val2 = 0;
> +		return IIO_VAL_INT_64;

Big numbers :)

> +	default:
> +		return -EINVAL;
> +	}
> +}
> +


> +
> +/*
> + * Recommended values (as per datasheet) for the dac clk common mode voltage
> + * and Mu controller. Look at table 29.
> + */
> +static const struct reg_sequence ad9739a_clk_mu_ctrl[] = {
> +	/* DAC clk common mode voltage */
> +	{AD9739A_REG_CROSS_CNT1, 0x0f},
	{ AD9739A_REG_CROSS_CNT1, 0x0f },
etc is more readable in my opinion so is always my preference in IIO.

> +	{AD9739A_REG_CROSS_CNT2, 0x0f},
> +	/* Mu controller configuration */
> +	{AD9739A_REG_PHS_DET, 0x30},
> +	{AD9739A_REG_MU_DUTY, 0x80},
> +	{AD9739A_REG_MU_CNT2, 0x44},
> +	{AD9739A_REG_MU_CNT3, 0x6c},
> +};
> +
> +static int ad9739a_init(struct device *dev, const struct ad9739a_state *st)
> +{
> +	unsigned int i = 0, lock, fsc;
> +	u32 fsc_raw;
> +	int ret;
> +
> +	ret = regmap_multi_reg_write(st->regmap, ad9739a_clk_mu_ctrl,
> +				     ARRAY_SIZE(ad9739a_clk_mu_ctrl));
> +	if (ret)
> +		return ret;
> +
> +	/*
> +	 * Try to get the MU lock. Repeat the below steps AD9739A_LOCK_N_TRIES
> +	 * (as specified by the datasheet) until we get the lock.
> +	 */
> +	do {
> +		ret = regmap_write(st->regmap, AD9739A_REG_MU_CNT4,
> +				   AD9739A_MU_CNT4_DEFAULT);
> +		if (ret)
> +			return ret;
> +
> +		/* Enable the Mu controller search and track mode. */

MU for consistency

> +		ret = regmap_set_bits(st->regmap, AD9739A_REG_MU_CNT1,
> +				      AD9739A_MU_EN_MASK);
> +		if (ret)
> +			return ret;
> +
> +		/* Ensure the DLL loop is locked */
> +		ret = regmap_read_poll_timeout(st->regmap, AD9739A_REG_MU_STAT1,
> +					       lock, lock & AD9739A_MU_LOCK_MASK,
> +					       0, 1000);
		if (ret < 0 && ret != -ETIMEOUT)
			return ret;

i.e. deal with error codes that don't meant it timed out.

> +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> +
> +	if (i == AD9739A_LOCK_N_TRIES)
> +		return dev_err_probe(dev, ret, "Mu lock timeout\n");
> +
> +	/* Receiver tracking and lock. Same deal as the Mu controller */

MU or Mu.  Either fine but be consistent in comments. I have no idea what this is
so can't say which is better.

> +	i = 0;
> +	do {
> +		ret = regmap_update_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT4,
> +					 AD9739A_FINE_DEL_SKW_MASK,
> +					 FIELD_PREP(AD9739A_FINE_DEL_SKW_MASK, 2));
> +		if (ret)
> +			return ret;
> +
> +		/* Disable the receiver and the loop. */
> +		ret = regmap_write(st->regmap, AD9739A_REG_LVDS_REC_CNT1, 0);
> +		if (ret)
> +			return ret;
> +
> +		/*
> +		 * Re-enable the loop so it falls out of lock and begins the
> +		 * search/track routine again.
> +		 */
> +		ret = regmap_set_bits(st->regmap, AD9739A_REG_LVDS_REC_CNT1,
> +				      AD9739A_RCVR_LOOP_EN_MASK);
> +		if (ret)
> +			return ret;
> +
> +		/* Ensure the DLL loop is locked */
> +		ret = regmap_read_poll_timeout(st->regmap,
> +					       AD9739A_REG_LVDS_REC_STAT9, lock,
> +					       lock == AD9739A_RCVR_TRACK_AND_LOCK,
> +					       0, 1000);

As above, consider other error codes than -ETIMEOUT;

> +	} while (ret && ++i < AD9739A_LOCK_N_TRIES);
> +
> +	if (i == AD9739A_LOCK_N_TRIES)
> +		return dev_err_probe(dev, ret, "Receiver lock timeout\n");
> +
> +	ret = device_property_read_u32(dev, "adi,full-scale-microamp", &fsc);
> +	if (ret && ret == -EINVAL)
> +		return 0;
> +	if (ret)
> +		return ret;
> +	if (!in_range(fsc, AD9739A_FSC_MIN, AD9739A_FSC_RANGE))
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid full scale current(%u) [%u %u]\n",
> +				     fsc, AD9739A_FSC_MIN, AD9739A_FSC_MAX);
> +	/*
> +	 * IOUTFS is given by
> +	 *	Ioutfs = 0.0226 * FSC + 8.58
> +	 * and is given in mA. Hence we'll have to multiply by 10 * MILLI in
> +	 * order to get rid of the fractional.
> +	 */
> +	fsc_raw = DIV_ROUND_CLOSEST(fsc * 10 - 85800, 226);
> +
> +	ret = regmap_write(st->regmap, AD9739A_REG_FSC_1, fsc_raw & 0xff);
> +	if (ret)
> +		return ret;
> +
> +	return regmap_update_bits(st->regmap, AD9739A_REG_FSC_1,
> +				  AD9739A_FSC_MSB, fsc_raw >> 8);
> +}



> +
> +static int ad9739a_probe(struct spi_device *spi)
> +{
> +	struct device *dev = &spi->dev;
> +	struct iio_dev *indio_dev;
> +	struct ad9739a_state *st;
> +	unsigned int id;
> +	struct clk *clk;
> +	int ret;
> +
> +	indio_dev = devm_iio_device_alloc(dev, sizeof(*st));
> +	if (!indio_dev)
> +		return -ENOMEM;
> +
> +	st = iio_priv(indio_dev);
> +
> +	clk = devm_clk_get_enabled(dev, NULL);
> +	if (IS_ERR(clk))
> +		return dev_err_probe(dev, PTR_ERR(clk), "Could not get clkin\n");
> +
> +	st->sample_rate = clk_get_rate(clk);
> +	if (!in_range(st->sample_rate, AD9739A_MIN_DAC_CLK,
> +		      AD9739A_DAC_CLK_RANGE))
> +		return dev_err_probe(dev, -EINVAL,
> +				     "Invalid dac clk range(%lu) [%lu %lu]\n",
> +				     st->sample_rate, AD9739A_MIN_DAC_CLK,
> +				     AD9739A_MAX_DAC_CLK);
> +
> +	st->regmap = devm_regmap_init_spi(spi, &ad9739a_regmap_config);
> +	if (IS_ERR(st->regmap))
> +		return PTR_ERR(st->regmap);
> +
> +	ret = regmap_read(st->regmap, AD9739A_REG_ID, &id);
> +	if (ret)
> +		return ret;
> +
> +	if (id != AD9739A_ID)
> +		return dev_err_probe(dev, -ENODEV, "Unrecognized CHIP_ID 0x%X",
> +				     id);
Do we have to give up here?  Could it be a compatible future part?
If so we should fallback on what firmware told us it was + perhaps a
dev_info() to say we don't recognise the ID register value.


> +
> +	ret = ad9739a_reset(dev, st);
> +	if (ret)
> +		return ret;
> +
> +	ret = ad9739a_init(dev, st);
> +	if (ret)
> +		return ret;
> +
> +	st->back = devm_iio_backend_get(dev, NULL);
> +	if (IS_ERR(st->back))
> +		return PTR_ERR(st->back);
> +
> +	ret = devm_iio_backend_request_buffer(dev, st->back, indio_dev);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_backend_extend_chan_spec(indio_dev, st->back,
> +					   &ad9739a_channels[0]);
> +	if (ret)
> +		return ret;
> +
> +	ret = iio_backend_set_sampling_freq(st->back, 0, st->sample_rate);
> +	if (ret)
> +		return ret;
> +
> +	ret = devm_iio_backend_enable(dev, st->back);
> +	if (ret)
> +		return ret;
> +
> +	indio_dev->name = "ad9739a";
> +	indio_dev->info = &ad9739a_info;
> +	indio_dev->channels = ad9739a_channels;
> +	indio_dev->num_channels = ARRAY_SIZE(ad9739a_channels);
> +	indio_dev->setup_ops = &ad9739a_buffer_setup_ops;
> +
> +	return devm_iio_device_register(&spi->dev, indio_dev);
> +}


  reply	other threads:[~2024-03-28 15:51 UTC|newest]

Thread overview: 32+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-03-28 13:22 [PATCH 00/10] iio: dac: support IIO backends on the output direction Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 01/10] iio: buffer: add helper for setting direction Nuno Sa via B4 Relay
2024-03-28 14:36   ` Jonathan Cameron
2024-03-28 15:18     ` Nuno Sá
2024-03-28 15:54       ` Jonathan Cameron
2024-03-28 13:22 ` [PATCH 02/10] iio: buffer-dma: Rename iio_dma_buffer_data_available() Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 03/10] iio: buffer-dma: Enable buffer write support Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 04/10] iio: buffer-dmaengine: Support specifying buffer direction Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 05/10] iio: buffer-dmaengine: Enable write support Nuno Sa via B4 Relay
2024-03-28 13:22 ` [PATCH 06/10] dt-bindings: iio: dac: add bindings doc for AXI DAC driver Nuno Sa via B4 Relay
2024-03-29 18:46   ` David Lechner
2024-04-02  7:51     ` Nuno Sá
2024-04-01 13:59   ` Rob Herring
2024-04-04 10:03     ` Nuno Sá
2024-03-28 13:22 ` [PATCH 07/10] dt-bindings: iio: dac: add bindings doc for AD9739A Nuno Sa via B4 Relay
2024-03-29 19:06   ` David Lechner
2024-03-30 18:27     ` Krzysztof Kozlowski
2024-04-02  7:49       ` Nuno Sá
2024-04-02  7:50     ` Nuno Sá
2024-04-01 14:02   ` Rob Herring
2024-03-28 13:22 ` [PATCH 08/10] iio: backend: add new functionality Nuno Sa via B4 Relay
2024-03-28 15:16   ` Jonathan Cameron
2024-03-28 15:42     ` Nuno Sá
2024-03-28 15:59       ` Jonathan Cameron
2024-03-28 16:54         ` Nuno Sá
2024-03-28 13:22 ` [PATCH 09/10] iio: dac: add support for AXI DAC IP core Nuno Sa via B4 Relay
2024-03-28 15:35   ` Jonathan Cameron
2024-03-28 16:43     ` Nuno Sá
2024-03-28 13:22 ` [PATCH 10/10] iio: dac: support the ad9739a RF DAC Nuno Sa via B4 Relay
2024-03-28 15:51   ` Jonathan Cameron [this message]
2024-03-28 16:37     ` Nuno Sá
2024-03-28 16:52       ` 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=20240328155126.2575d754@jic23-huawei \
    --to=jic23@kernel.org \
    --cc=Michael.Hennerich@analog.com \
    --cc=conor+dt@kernel.org \
    --cc=devicetree@vger.kernel.org \
    --cc=devnull+nuno.sa.analog.com@kernel.org \
    --cc=dragos.bogdan@analog.com \
    --cc=krzysztof.kozlowski+dt@linaro.org \
    --cc=lars@metafoo.de \
    --cc=linux-iio@vger.kernel.org \
    --cc=nuno.sa@analog.com \
    --cc=olivier.moysan@foss.st.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;
as well as URLs for NNTP newsgroup(s).