Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
From: Frank Li <Frank.li@nxp.com>
To: Jonas Rebmann <jre@pengutronix.de>
Cc: Mark Brown <broonie@kernel.org>, Shawn Guo <shawnguo@kernel.org>,
	Sascha Hauer <s.hauer@pengutronix.de>,
	Fabio Estevam <festevam@gmail.com>,
	kernel@pengutronix.de, linux-spi@vger.kernel.org,
	imx@lists.linux.dev, linux-arm-kernel@lists.infradead.org,
	linux-kernel@vger.kernel.org
Subject: Re: [PATCH v2 2/2] spi: imx: support word delay
Date: Wed, 13 Nov 2024 10:35:09 -0500	[thread overview]
Message-ID: <ZzTHLec7Ua0a+VxG@lizhi-Precision-Tower-5810> (raw)
In-Reply-To: <20241113-imx-spi-word-delay-v2-2-2b65b737bf29@pengutronix.de>

On Wed, Nov 13, 2024 at 01:18:32PM +0100, Jonas Rebmann wrote:
> Implement support for the word delay feature of i.MX51 (and onwards) via
> the ECSPI interface.
>
> Convert the requested delay to SPI cycles and account for an extra
> inter-word delay inserted by the controller in addition to the requested
> number of cycles, which was observed when testing this patch.
>
> Disable dynamic burst when word delay is set. As the configurable delay
> period in the controller is inserted after bursts, the burst length must
> equal the word length.
>
> Account for word delay in the transfer time estimation for
> polling_limit_us.
>
> Signed-off-by: Jonas Rebmann <jre@pengutronix.de>

Reviewed-by: Frank Li <Frank.Li@nxp.com>

> ---
>  drivers/spi/spi-imx.c | 95 +++++++++++++++++++++++++++++++++++++++++++++------
>  1 file changed, 85 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
> index 65a8303b80b1191cd2c19d61f88836e7fd3c7ae9..29b83659b8036d1cffe076012ad5cb229509abd8 100644
> --- a/drivers/spi/spi-imx.c
> +++ b/drivers/spi/spi-imx.c
> @@ -3,6 +3,7 @@
>  // Copyright (C) 2008 Juergen Beisert
>
>  #include <linux/bits.h>
> +#include <linux/bitfield.h>
>  #include <linux/clk.h>
>  #include <linux/completion.h>
>  #include <linux/delay.h>
> @@ -13,7 +14,10 @@
>  #include <linux/io.h>
>  #include <linux/irq.h>
>  #include <linux/kernel.h>
> +#include <linux/math.h>
> +#include <linux/math64.h>
>  #include <linux/module.h>
> +#include <linux/overflow.h>
>  #include <linux/pinctrl/consumer.h>
>  #include <linux/platform_device.h>
>  #include <linux/pm_runtime.h>
> @@ -302,6 +306,18 @@ static bool spi_imx_can_dma(struct spi_controller *controller, struct spi_device
>  #define MX51_ECSPI_STAT		0x18
>  #define MX51_ECSPI_STAT_RR		(1 <<  3)
>
> +#define MX51_ECSPI_PERIOD	0x1c
> +#define MX51_ECSPI_PERIOD_MASK		0x7fff
> +/*
> + * As measured on the i.MX6, the SPI host controller inserts a 4 SPI-Clock
> + * (SCLK) delay after each burst if the PERIOD reg is 0x0. This value will be
> + * called MX51_ECSPI_PERIOD_MIN_DELAY_SCK.
> + *
> + * If the PERIOD register is != 0, the controller inserts a delay of
> + * MX51_ECSPI_PERIOD_MIN_DELAY_SCK + register value + 1 SCLK after each burst.
> + */
> +#define MX51_ECSPI_PERIOD_MIN_DELAY_SCK 4
> +
>  #define MX51_ECSPI_TESTREG	0x20
>  #define MX51_ECSPI_TESTREG_LBC	BIT(31)
>
> @@ -653,6 +669,7 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
>  				       struct spi_device *spi, struct spi_transfer *t)
>  {
>  	u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
> +	u64 word_delay_sck;
>  	u32 clk;
>
>  	/* Clear BL field and set the right value */
> @@ -684,6 +701,49 @@ static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
>
>  	writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
>
> +	/* calculate word delay in SPI Clock (SCLK) cycles */
> +	if (t->word_delay.value == 0) {
> +		word_delay_sck = 0;
> +	} else if (t->word_delay.unit == SPI_DELAY_UNIT_SCK) {
> +		word_delay_sck = t->word_delay.value;
> +
> +		if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK)
> +			word_delay_sck = 0;
> +		else if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1)
> +			word_delay_sck = 1;
> +		else
> +			word_delay_sck -= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1;
> +	} else {
> +		int word_delay_ns;
> +
> +		word_delay_ns = spi_delay_to_ns(&t->word_delay, t);
> +		if (word_delay_ns < 0)
> +			return word_delay_ns;
> +
> +		if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
> +						     MX51_ECSPI_PERIOD_MIN_DELAY_SCK,
> +						     spi_imx->spi_bus_clk)) {
> +			word_delay_sck = 0;
> +		} else if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
> +							    MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
> +							    spi_imx->spi_bus_clk)) {
> +			word_delay_sck = 1;
> +		} else {
> +			word_delay_ns -= mul_u64_u32_div(NSEC_PER_SEC,
> +							 MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
> +							 spi_imx->spi_bus_clk);
> +
> +			word_delay_sck = DIV_U64_ROUND_UP((u64)word_delay_ns * spi_imx->spi_bus_clk,
> +							  NSEC_PER_SEC);
> +		}
> +	}
> +
> +	if (!FIELD_FIT(MX51_ECSPI_PERIOD_MASK, word_delay_sck))
> +		return -EINVAL;
> +
> +	writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
> +	       spi_imx->base + MX51_ECSPI_PERIOD);
> +
>  	return 0;
>  }
>
> @@ -1264,11 +1324,13 @@ static int spi_imx_setupxfer(struct spi_device *spi,
>
>  	/*
>  	 * Initialize the functions for transfer. To transfer non byte-aligned
> -	 * words, we have to use multiple word-size bursts, we can't use
> -	 * dynamic_burst in that case.
> +	 * words, we have to use multiple word-size bursts. To insert word
> +	 * delay, the burst size has to equal the word size. We can't use
> +	 * dynamic_burst in these cases.
>  	 */
>  	if (spi_imx->devtype_data->dynamic_burst && !spi_imx->target_mode &&
>  	    !(spi->mode & SPI_CS_WORD) &&
> +	    !(t->word_delay.value) &&
>  	    (spi_imx->bits_per_word == 8 ||
>  	    spi_imx->bits_per_word == 16 ||
>  	    spi_imx->bits_per_word == 32)) {
> @@ -1611,12 +1673,30 @@ static int spi_imx_pio_transfer_target(struct spi_device *spi,
>  	return ret;
>  }
>
> +static unsigned int spi_imx_transfer_estimate_time_us(struct spi_transfer *transfer)
> +{
> +	u64 result;
> +
> +	result = DIV_U64_ROUND_CLOSEST((u64)USEC_PER_SEC * transfer->len * BITS_PER_BYTE,
> +				       transfer->effective_speed_hz);
> +	if (transfer->word_delay.value) {
> +		unsigned int word_delay_us;
> +		unsigned int words;
> +
> +		words = DIV_ROUND_UP(transfer->len * BITS_PER_BYTE, transfer->bits_per_word);
> +		word_delay_us = DIV_ROUND_CLOSEST(spi_delay_to_ns(&transfer->word_delay, transfer),
> +						  NSEC_PER_USEC);
> +		result += words * word_delay_us;
> +	}
> +
> +	return min(result, U32_MAX);
> +}
> +
>  static int spi_imx_transfer_one(struct spi_controller *controller,
>  				struct spi_device *spi,
>  				struct spi_transfer *transfer)
>  {
>  	struct spi_imx_data *spi_imx = spi_controller_get_devdata(spi->controller);
> -	unsigned long hz_per_byte, byte_limit;
>
>  	spi_imx_setupxfer(spi, transfer);
>  	transfer->effective_speed_hz = spi_imx->spi_bus_clk;
> @@ -1635,15 +1715,10 @@ static int spi_imx_transfer_one(struct spi_controller *controller,
>  	 */
>  	if (spi_imx->usedma)
>  		return spi_imx_dma_transfer(spi_imx, transfer);
> -	/*
> -	 * Calculate the estimated time in us the transfer runs. Find
> -	 * the number of Hz per byte per polling limit.
> -	 */
> -	hz_per_byte = polling_limit_us ? ((8 + 4) * USEC_PER_SEC) / polling_limit_us : 0;
> -	byte_limit = hz_per_byte ? transfer->effective_speed_hz / hz_per_byte : 1;
>
>  	/* run in polling mode for short transfers */
> -	if (transfer->len < byte_limit)
> +	if (transfer->len == 1 || (polling_limit_us &&
> +				   spi_imx_transfer_estimate_time_us(transfer) < polling_limit_us))
>  		return spi_imx_poll_transfer(spi, transfer);
>
>  	return spi_imx_pio_transfer(spi, transfer);
>
> --
> 2.39.5
>


  reply	other threads:[~2024-11-13 15:52 UTC|newest]

Thread overview: 7+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2024-11-13 12:18 [PATCH v2 0/2] spi: imx: support word delay in ecspi Jonas Rebmann
2024-11-13 12:18 ` [PATCH v2 1/2] spi: imx: pass struct spi_transfer to prepare_transfer() Jonas Rebmann
2024-11-13 15:27   ` Frank Li
2024-11-13 12:18 ` [PATCH v2 2/2] spi: imx: support word delay Jonas Rebmann
2024-11-13 15:35   ` Frank Li [this message]
2024-11-15 21:53   ` Kees Bakker
2024-11-14 13:01 ` [PATCH v2 0/2] spi: imx: support word delay in ecspi Mark Brown

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=ZzTHLec7Ua0a+VxG@lizhi-Precision-Tower-5810 \
    --to=frank.li@nxp.com \
    --cc=broonie@kernel.org \
    --cc=festevam@gmail.com \
    --cc=imx@lists.linux.dev \
    --cc=jre@pengutronix.de \
    --cc=kernel@pengutronix.de \
    --cc=linux-arm-kernel@lists.infradead.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-spi@vger.kernel.org \
    --cc=s.hauer@pengutronix.de \
    --cc=shawnguo@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