diff mbox series

[2/2] spi: imx: support word delay

Message ID 20241107-imx-spi-word-delay-v1-2-2a969214d796@pengutronix.de
State New
Headers show
Series spi: imx: support word delay in ecspi | expand

Commit Message

Jonas Rebmann Nov. 7, 2024, 3:35 p.m. UTC
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>
---
 drivers/spi/spi-imx.c | 93 +++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 83 insertions(+), 10 deletions(-)

Comments

kernel test robot Nov. 7, 2024, 8:01 p.m. UTC | #1
Hi Jonas,

kernel test robot noticed the following build errors:

[auto build test ERROR on 9852d85ec9d492ebef56dc5f229416c925758edc]

url:    https://github.com/intel-lab-lkp/linux/commits/Jonas-Rebmann/spi-imx-pass-struct-spi_transfer-to-prepare_transfer/20241107-233756
base:   9852d85ec9d492ebef56dc5f229416c925758edc
patch link:    https://lore.kernel.org/r/20241107-imx-spi-word-delay-v1-2-2a969214d796%40pengutronix.de
patch subject: [PATCH 2/2] spi: imx: support word delay
config: arc-randconfig-002-20241108 (https://download.01.org/0day-ci/archive/20241108/202411080315.8JXzpAnT-lkp@intel.com/config)
compiler: arc-elf-gcc (GCC) 13.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20241108/202411080315.8JXzpAnT-lkp@intel.com/reproduce)

If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202411080315.8JXzpAnT-lkp@intel.com/

All errors (new ones prefixed by >>):

   drivers/spi/spi-imx.c: In function 'mx51_ecspi_prepare_transfer':
>> drivers/spi/spi-imx.c:739:14: error: implicit declaration of function 'FIELD_FIT' [-Werror=implicit-function-declaration]
     739 |         if (!FIELD_FIT(MX51_ECSPI_PERIOD_MASK, word_delay_sck))
         |              ^~~~~~~~~
   In file included from include/linux/byteorder/little_endian.h:5,
                    from arch/arc/include/uapi/asm/byteorder.h:16,
                    from include/asm-generic/bitops/le.h:6,
                    from arch/arc/include/asm/bitops.h:192,
                    from include/linux/bitops.h:68,
                    from include/linux/kernel.h:23,
                    from include/linux/clk.h:13,
                    from drivers/spi/spi-imx.c:6:
>> drivers/spi/spi-imx.c:742:16: error: implicit declaration of function 'FIELD_PREP' [-Werror=implicit-function-declaration]
     742 |         writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
         |                ^~~~~~~~~~
   include/uapi/linux/byteorder/little_endian.h:34:51: note: in definition of macro '__cpu_to_le32'
      34 | #define __cpu_to_le32(x) ((__force __le32)(__u32)(x))
         |                                                   ^
   arch/arc/include/asm/io.h:208:47: note: in expansion of macro 'writel_relaxed'
     208 | #define writel(v,c)             ({ __iowmb(); writel_relaxed(v,c); })
         |                                               ^~~~~~~~~~~~~~
   drivers/spi/spi-imx.c:742:9: note: in expansion of macro 'writel'
     742 |         writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
         |         ^~~~~~
   cc1: some warnings being treated as errors


vim +/FIELD_FIT +739 drivers/spi/spi-imx.c

   665	
   666	static int mx51_ecspi_prepare_transfer(struct spi_imx_data *spi_imx,
   667					       struct spi_device *spi, struct spi_transfer *t)
   668	{
   669		u32 ctrl = readl(spi_imx->base + MX51_ECSPI_CTRL);
   670		u64 word_delay_sck;
   671		u32 clk;
   672	
   673		/* Clear BL field and set the right value */
   674		ctrl &= ~MX51_ECSPI_CTRL_BL_MASK;
   675		if (spi_imx->target_mode && is_imx53_ecspi(spi_imx))
   676			ctrl |= (spi_imx->target_burst * 8 - 1)
   677				<< MX51_ECSPI_CTRL_BL_OFFSET;
   678		else {
   679			ctrl |= (spi_imx->bits_per_word - 1)
   680				<< MX51_ECSPI_CTRL_BL_OFFSET;
   681		}
   682	
   683		/* set clock speed */
   684		ctrl &= ~(0xf << MX51_ECSPI_CTRL_POSTDIV_OFFSET |
   685			  0xf << MX51_ECSPI_CTRL_PREDIV_OFFSET);
   686		ctrl |= mx51_ecspi_clkdiv(spi_imx, spi_imx->spi_bus_clk, &clk);
   687		spi_imx->spi_bus_clk = clk;
   688	
   689		mx51_configure_cpha(spi_imx, spi);
   690	
   691		/*
   692		 * ERR009165: work in XHC mode instead of SMC as PIO on the chips
   693		 * before i.mx6ul.
   694		 */
   695		if (spi_imx->usedma && spi_imx->devtype_data->tx_glitch_fixed)
   696			ctrl |= MX51_ECSPI_CTRL_SMC;
   697		else
   698			ctrl &= ~MX51_ECSPI_CTRL_SMC;
   699	
   700		writel(ctrl, spi_imx->base + MX51_ECSPI_CTRL);
   701	
   702		/* calculate word delay in SPI Clock (SCLK) cycles */
   703		if (t->word_delay.value == 0) {
   704			word_delay_sck = 0;
   705		} else if (t->word_delay.unit == SPI_DELAY_UNIT_SCK) {
   706			word_delay_sck = t->word_delay.value;
   707	
   708			if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK)
   709				word_delay_sck = 0;
   710			else if (word_delay_sck <= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1)
   711				word_delay_sck = 1;
   712			else
   713				word_delay_sck -= MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1;
   714		} else {
   715			int word_delay_ns;
   716	
   717			word_delay_ns = spi_delay_to_ns(&t->word_delay, t);
   718			if (word_delay_ns < 0)
   719				return word_delay_ns;
   720	
   721			if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
   722							     MX51_ECSPI_PERIOD_MIN_DELAY_SCK,
   723							     spi_imx->spi_bus_clk)) {
   724				word_delay_sck = 0;
   725			} else if (word_delay_ns <= mul_u64_u32_div(NSEC_PER_SEC,
   726								    MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
   727								    spi_imx->spi_bus_clk)) {
   728				word_delay_sck = 1;
   729			} else {
   730				word_delay_ns -= mul_u64_u32_div(NSEC_PER_SEC,
   731								 MX51_ECSPI_PERIOD_MIN_DELAY_SCK + 1,
   732								 spi_imx->spi_bus_clk);
   733	
   734				word_delay_sck = DIV_U64_ROUND_UP((u64)word_delay_ns * spi_imx->spi_bus_clk,
   735								  NSEC_PER_SEC);
   736			}
   737		}
   738	
 > 739		if (!FIELD_FIT(MX51_ECSPI_PERIOD_MASK, word_delay_sck))
   740			return -EINVAL;
   741	
 > 742		writel(FIELD_PREP(MX51_ECSPI_PERIOD_MASK, word_delay_sck),
   743		       spi_imx->base + MX51_ECSPI_PERIOD);
   744	
   745		return 0;
   746	}
   747
diff mbox series

Patch

diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 65a8303b80b1191cd2c19d61f88836e7fd3c7ae9..c50c256e6e695659c23d5292949a98bda0a2cd1a 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -13,7 +13,9 @@ 
 #include <linux/io.h>
 #include <linux/irq.h>
 #include <linux/kernel.h>
+#include <linux/math.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 +304,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 +667,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 +699,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 +1322,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 +1671,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 +1713,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);