[PATCH] spi-imx: imx6q add single burst transfer support

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The patch add support for single burst transfer where chipselect will
hold active until transfer completes with a limit to 2^7 words transferred.
The single-burst-mode need set the burstlength in ECSPI_CONREG.BURST_LENGTH
and clear the ecspi channel related ss_ctl flag in ECSPI_CONFIGREG.SS_CTL.

The single-burst-mode is disabled by default. The activation from spidev
is implemented by set bit0 of the xfer.speed_hz, which don't break anything
in the mx51_ecspi_clkdiv() function.

xfer[0].speed_hz = 2000000 | 0x1; /* enable single burst mode with 1hz */

There is a bug in the SDMA firmware (referenced as TKT238285 or ERR008517)
which breaks the transmission with a odd numbers of bytes.
Its turns out, that its safe to use with slaves in half duplex mode where
always an even number of bytes is required.

function added:
spi_imx_buf_tx_sb()  - singleburst transmit
spi_imx_buf_rx_sb()  - singleburst receive

stucts modified:
spi_imx_config - add len ; add single_burst_mode:1
spi_imx_data   - add rxcount; add bits_per_word

Signed-off-by: Chris Ruehl <chris.ruehl@xxxxxxxxxxxx>
---
 drivers/spi/spi-imx.c | 233 +++++++++++++++++++++++++++++++++++++++++++++++++-
 1 file changed, 231 insertions(+), 2 deletions(-)

diff --git a/drivers/spi/spi-imx.c b/drivers/spi/spi-imx.c
index 50769078..15708eb 100644
--- a/drivers/spi/spi-imx.c
+++ b/drivers/spi/spi-imx.c
@@ -60,6 +60,8 @@ struct spi_imx_config {
 	unsigned int speed_hz;
 	unsigned int bpw;
 	unsigned int mode;
+	unsigned int len;
+	unsigned int single_burst_mode:1;
 	u8 cs;
 };
 
@@ -99,11 +101,13 @@ struct spi_imx_data {
 	unsigned int bytes_per_word;
 
 	unsigned int count;
+	unsigned int rxcount;
 	void (*tx)(struct spi_imx_data *);
 	void (*rx)(struct spi_imx_data *);
 	void *rx_buf;
 	const void *tx_buf;
 	unsigned int txfifo; /* number of words pushed in tx FIFO */
+	u8 bits_per_word;
 
 	/* DMA */
 	bool usedma;
@@ -168,6 +172,188 @@ MXC_SPI_BUF_TX(u16)
 MXC_SPI_BUF_RX(u32)
 MXC_SPI_BUF_TX(u32)
 
+static void spi_imx_buf_rx_sb(struct spi_imx_data *spi_imx)
+{
+	unsigned int val = readl(spi_imx->base + MXC_CSPIRXDATA);
+
+	dev_dbg(spi_imx->dev, "%s: rxcount: %u val:0x%08x\n",
+		__func__, spi_imx->rxcount, val);
+
+	if (spi_imx->rxcount >= 4) {
+		if (spi_imx->rx_buf) {
+			if (spi_imx->bits_per_word == 32) {
+				spi_imx_buf_rx_u32(spi_imx);
+			} else if (spi_imx->bits_per_word == 16) {
+				*(u16 *)spi_imx->rx_buf = (val>>16);
+				spi_imx->rx_buf += sizeof(u16);
+				*(u16 *)spi_imx->rx_buf = val;
+				spi_imx->rx_buf += sizeof(u16);
+			} else {
+				*(u8 *)spi_imx->rx_buf = (val>>24);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = (val>>16);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = (val>>8);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val;
+				spi_imx->rx_buf += sizeof(u8);
+			}
+		}
+		spi_imx->rxcount -= 4;
+	} else if (spi_imx->rxcount == 3) {
+		if (spi_imx->rx_buf) {
+			if (spi_imx->bits_per_word == 32) {
+				*(u8 *)spi_imx->rx_buf = val>>24;
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val>>16;
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val>>8;
+				spi_imx->rx_buf += sizeof(u8);
+			} else if (spi_imx->bits_per_word == 16) {
+				*(u16 *)spi_imx->rx_buf = (val>>16);
+				spi_imx->rx_buf += sizeof(u16);
+				*(u8 *)spi_imx->rx_buf = val>>8;
+				spi_imx->rx_buf += sizeof(u8);
+			} else {
+				*(u8 *)spi_imx->rx_buf = (val>>16);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = (val>>8);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val;
+				spi_imx->rx_buf += sizeof(u8);
+			}
+		}
+		spi_imx->rxcount -= 3;
+	} else if (spi_imx->rxcount == 2) {
+		if (spi_imx->rx_buf) {
+			if (spi_imx->bits_per_word == 32) {
+				*(u8 *)spi_imx->rx_buf = val>>24;
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val>>16;
+				spi_imx->rx_buf += sizeof(u8);
+			} else if (spi_imx->bits_per_word == 16) {
+				spi_imx_buf_rx_u16(spi_imx);
+			} else {
+				*(u8 *)spi_imx->rx_buf = (val>>8);
+				spi_imx->rx_buf += sizeof(u8);
+				*(u8 *)spi_imx->rx_buf = val;
+				spi_imx->rx_buf += sizeof(u8);
+			}
+		}
+		spi_imx->rxcount -= 2;
+	} else if (spi_imx->rxcount == 1) {
+		if (spi_imx->rx_buf) {
+			if (spi_imx->bits_per_word == 32)
+				*(u8 *)spi_imx->rx_buf = val>>24;
+			else if (spi_imx->bits_per_word == 16)
+				*(u8 *)spi_imx->rx_buf = val>>8;
+			else
+				spi_imx_buf_rx_u8(spi_imx);
+		}
+		spi_imx->rxcount -= 1;
+	}
+}
+
+static void spi_imx_buf_tx_sb(struct spi_imx_data *spi_imx)
+{
+	unsigned int val = 0;
+
+	dev_dbg(spi_imx->dev, "%s: txcount: %u ptr:0x%08x\n",
+		__func__, spi_imx->count, *(u32 *)spi_imx->tx_buf);
+
+	if (spi_imx->count >= 4) {
+		if (spi_imx->bits_per_word == 32) {
+			spi_imx_buf_tx_u32(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word == 16) {
+					val = *(u16 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u16);
+					val |= *(u16 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u16);
+				} else {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 4;
+		}
+	} else if (spi_imx->count == 3) {
+		if (spi_imx->tx_buf) {
+			if (spi_imx->bits_per_word == 32) {
+				val = *(u8 *)spi_imx->tx_buf<<24;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<8;
+				spi_imx->tx_buf += sizeof(u8);
+
+			} else if (spi_imx->bits_per_word == 16) {
+				val = *(u8 *)spi_imx->tx_buf<<24;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf;
+				spi_imx->tx_buf += sizeof(u8);
+
+			} else {
+				val = *(u8 *)spi_imx->tx_buf<<16;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf<<8;
+				spi_imx->tx_buf += sizeof(u8);
+				val |= *(u8 *)spi_imx->tx_buf;
+				spi_imx->tx_buf += sizeof(u8);
+			}
+			writel(val, spi_imx->base + MXC_CSPITXDATA);
+		}
+		spi_imx->count -= 3;
+	} else if (spi_imx->count == 2) {
+		if (spi_imx->bits_per_word == 16) {
+			spi_imx_buf_tx_u16(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word == 32) {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf<<16;
+					spi_imx->tx_buf += sizeof(u8);
+				} else {
+					val = *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+					val |= *(u8 *)spi_imx->tx_buf;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 2;
+		}
+	} else if (spi_imx->count == 1) {
+		if (spi_imx->bits_per_word == 8) {
+			spi_imx_buf_tx_u8(spi_imx);
+		} else {
+			if (spi_imx->tx_buf) {
+				if (spi_imx->bits_per_word == 32) {
+					val = *(u8 *)spi_imx->tx_buf<<24;
+					spi_imx->tx_buf += sizeof(u8);
+				} else if (spi_imx->bits_per_word == 16) {
+					val = *(u8 *)spi_imx->tx_buf<<8;
+					spi_imx->tx_buf += sizeof(u8);
+				}
+				writel(val, spi_imx->base + MXC_CSPITXDATA);
+			}
+			spi_imx->count -= 1;
+		}
+	}
+}
+
+
 /* First entry is reserved, second entry is valid only if SDHC_SPIEN is set
  * (which is currently not the case in this driver)
  */
@@ -357,9 +543,49 @@ static int __maybe_unused mx51_ecspi_config(struct spi_imx_data *spi_imx,
 	/* set chip select to use */
 	ctrl |= MX51_ECSPI_CTRL_CS(config->cs);
 
-	ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+	/* set single else multible burst parameters */
+	if (config->single_burst_mode > 0) {
+		reg = 0;
+		spi_imx->rx = spi_imx_buf_rx_sb;
+		spi_imx->tx = spi_imx_buf_tx_sb;
+		spi_imx->rxcount = config->len;
+		spi_imx->bits_per_word = config->bpw;
+
+		/*
+		 * calculate the Burst Length,
+		 * refer to 21.7.3 Control Register (ECSPIx_CONREG)
+		 * for details.
+		 */
+		switch (config->len%4) {
+		case 1:
+			ctrl |= 7 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-1) / 4;
+			break;
+		case 2:
+			ctrl |= 15 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-2) / 4;
+			break;
+		case 3:
+			ctrl |= 23 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-3) / 4;
+			break;
+		case 0:
+			ctrl |= 31 << MX51_ECSPI_CTRL_BL_OFFSET;
+			reg = (config->len-4) / 4;
+			break;
+		}
+
+		if (reg > 0)
+			ctrl |= reg << (MX51_ECSPI_CTRL_BL_OFFSET + 5);
 
-	cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
+		cfg &= ~(MX51_ECSPI_CONFIG_SBBCTRL(config->cs));
+
+		dev_dbg(spi_imx->dev, "Single Burst reg:0x%08x cfg:0x%08x ctrl:0x%08x\n",
+			reg, cfg, ctrl);
+	} else {
+		ctrl |= (config->bpw - 1) << MX51_ECSPI_CTRL_BL_OFFSET;
+		cfg |= MX51_ECSPI_CONFIG_SBBCTRL(config->cs);
+	}
 
 	if (config->mode & SPI_CPHA)
 		cfg |= MX51_ECSPI_CONFIG_SCLKPHA(config->cs);
@@ -861,6 +1087,9 @@ static int spi_imx_setupxfer(struct spi_device *spi,
 	config.speed_hz  = t ? t->speed_hz : spi->max_speed_hz;
 	config.mode = spi->mode;
 	config.cs = spi->chip_select;
+	config.len = t->len;
+	/* using bit0 of hz to enable the single burst mode */
+	config.single_burst_mode = (config.speed_hz&0x1) > 0 ? true : false;
 
 	if (!config.speed_hz)
 		config.speed_hz = spi->max_speed_hz;
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe linux-spi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Linux Kernel]     [Linux ARM (vger)]     [Linux ARM MSM]     [Linux Omap]     [Linux Arm]     [Linux Tegra]     [Fedora ARM]     [Linux for Samsung SOC]     [eCos]     [Linux Fastboot]     [Gcc Help]     [Git]     [DCCP]     [IETF Announce]     [Security]     [Linux MIPS]     [Yosemite Campsites]

  Powered by Linux