Patchwork [U-Boot,V2,2/6] spi: omap3: add support for more word lengths

login
register
mail settings
Submitter Nikita Kiryanov
Date Oct. 16, 2013, 2:23 p.m.
Message ID <1381933409-8127-3-git-send-email-nikita@compulab.co.il>
Download mbox | patch
Permalink /patch/283960/
State Accepted
Delegated to: Anatolij Gustschin
Headers show

Comments

Nikita Kiryanov - Oct. 16, 2013, 2:23 p.m.
Current implementation only supports 8 bit word lengths, even though
omap3 can handle anything between 4 and 32.

Update the spi interface to support changing the SPI word length,
and implement it in omap3_spi driver to support the full range of
possible word lengths.
This implementation is backwards compatible by defaulting to the old
behavior of 8 bit word lengths.
Also, it required a change to the omap3_spi non static I/O functions,
but since they are not used anywhere else, no collateral changes are required.

Cc: Tom Rini <trini@ti.com>
Cc: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
Cc: Igor Grinberg <grinberg@compulab.co.il>
Signed-off-by: Nikita Kiryanov <nikita@compulab.co.il>
---
Changes in V2:
	- Removed semicolon from #define SPI_DEFAULT_WORDLEN (Gerhard Sittig,
	Igor Grinberg)
	- Moved wordlen to generic spi_slave struct, and added generic
	implementation for spi_set_wordlen which only updates the struct without
	touching the hardware (Igor Grinberg)
	- Update wordlen in hardware just before doing SPI transactions, not
	when changing wordlen (Igor Grinberg)
	- OMAP3 specific check of wordlen value from old implementation of
	spi_set_wordlen moved to xfer. It refines the more general check done
	in the new spi_set_wordlen.
	- Fixed comment style in spi.h following a rebase on top of latest
	U-Boot

 drivers/spi/omap3_spi.c | 69 +++++++++++++++++++++++++++++++++++--------------
 drivers/spi/omap3_spi.h |  8 +++---
 drivers/spi/spi.c       | 13 ++++++++++
 include/spi.h           | 16 ++++++++++++
 4 files changed, 82 insertions(+), 24 deletions(-)
Anatolij Gustschin - Nov. 12, 2013, 9:18 a.m.
On Wed, 16 Oct 2013 17:23:25 +0300
Nikita Kiryanov <nikita@compulab.co.il> wrote:

> Current implementation only supports 8 bit word lengths, even though
> omap3 can handle anything between 4 and 32.
> 
> Update the spi interface to support changing the SPI word length,
> and implement it in omap3_spi driver to support the full range of
> possible word lengths.
> This implementation is backwards compatible by defaulting to the old
> behavior of 8 bit word lengths.
> Also, it required a change to the omap3_spi non static I/O functions,
> but since they are not used anywhere else, no collateral changes are required.
> 
> Cc: Tom Rini <trini@ti.com>
> Cc: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
> Cc: Igor Grinberg <grinberg@compulab.co.il>
> Signed-off-by: Nikita Kiryanov <nikita@compulab.co.il>
> ---
> Changes in V2:
> 	- Removed semicolon from #define SPI_DEFAULT_WORDLEN (Gerhard Sittig,
> 	Igor Grinberg)
> 	- Moved wordlen to generic spi_slave struct, and added generic
> 	implementation for spi_set_wordlen which only updates the struct without
> 	touching the hardware (Igor Grinberg)
> 	- Update wordlen in hardware just before doing SPI transactions, not
> 	when changing wordlen (Igor Grinberg)
> 	- OMAP3 specific check of wordlen value from old implementation of
> 	spi_set_wordlen moved to xfer. It refines the more general check done
> 	in the new spi_set_wordlen.
> 	- Fixed comment style in spi.h following a rebase on top of latest
> 	U-Boot
> 
>  drivers/spi/omap3_spi.c | 69 +++++++++++++++++++++++++++++++++++--------------
>  drivers/spi/omap3_spi.h |  8 +++---
>  drivers/spi/spi.c       | 13 ++++++++++
>  include/spi.h           | 16 ++++++++++++
>  4 files changed, 82 insertions(+), 24 deletions(-)

applied to u-boot-video/master, thanks!

Anatolij

Patch

diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 116276c..a3ad056 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -20,7 +20,6 @@ 
 #include <asm/io.h>
 #include "omap3_spi.h"
 
-#define WORD_LEN	8
 #define SPI_WAIT_TIMEOUT 3000000
 
 static void spi_reset(struct omap3_spi_slave *ds)
@@ -185,7 +184,7 @@  int spi_claim_bus(struct spi_slave *slave)
 
 	/* wordlength */
 	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
-	conf |= (WORD_LEN - 1) << 7;
+	conf |= (ds->slave.wordlen - 1) << 7;
 
 	/* set chipselect polarity; manage with FORCE */
 	if (!(ds->mode & SPI_CS_HIGH))
@@ -223,7 +222,7 @@  void spi_release_bus(struct spi_slave *slave)
 	spi_reset(ds);
 }
 
-int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
+int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp,
 		    unsigned long flags)
 {
 	struct omap3_spi_slave *ds = to_omap3_spi(slave);
@@ -234,7 +233,8 @@  int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
 	/* Enable the channel */
 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 
-	chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
+	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
+	chconf |= (ds->slave.wordlen - 1) << 7;
 	chconf |= OMAP3_MCSPI_CHCONF_TRM_TX_ONLY;
 	chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 	omap3_spi_write_chconf(ds,chconf);
@@ -250,7 +250,13 @@  int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
 			}
 		}
 		/* Write the data */
-		writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
+		unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx;
+		if (ds->slave.wordlen > 16)
+			writel(((u32 *)txp)[i], tx);
+		else if (ds->slave.wordlen > 8)
+			writel(((u16 *)txp)[i], tx);
+		else
+			writel(((u8 *)txp)[i], tx);
 	}
 
 	/* wait to finish of transfer */
@@ -268,7 +274,7 @@  int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
 	return 0;
 }
 
-int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
+int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp,
 		   unsigned long flags)
 {
 	struct omap3_spi_slave *ds = to_omap3_spi(slave);
@@ -279,7 +285,8 @@  int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
 	/* Enable the channel */
 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 
-	chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
+	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
+	chconf |= (ds->slave.wordlen - 1) << 7;
 	chconf |= OMAP3_MCSPI_CHCONF_TRM_RX_ONLY;
 	chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 	omap3_spi_write_chconf(ds,chconf);
@@ -302,7 +309,13 @@  int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
 			omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
 
 		/* Read the data */
-		rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
+		unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx;
+		if (ds->slave.wordlen > 16)
+			((u32 *)rxp)[i] = readl(rx);
+		else if (ds->slave.wordlen > 8)
+			((u16 *)rxp)[i] = (u16)readl(rx);
+		else
+			((u8 *)rxp)[i] = (u8)readl(rx);
 	}
 
 	if (flags & SPI_XFER_END) {
@@ -314,8 +327,8 @@  int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
 }
 
 /*McSPI Transmit Receive Mode*/
-int omap3_spi_txrx(struct spi_slave *slave,
-		unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags)
+int omap3_spi_txrx(struct spi_slave *slave, unsigned int len,
+		   const void *txp, void *rxp, unsigned long flags)
 {
 	struct omap3_spi_slave *ds = to_omap3_spi(slave);
 	int timeout = SPI_WAIT_TIMEOUT;
@@ -327,7 +340,8 @@  int omap3_spi_txrx(struct spi_slave *slave,
 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_EN);
 
 	/*set TRANSMIT-RECEIVE Mode*/
-	chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK;
+	chconf &= ~(OMAP3_MCSPI_CHCONF_TRM_MASK | OMAP3_MCSPI_CHCONF_WL_MASK);
+	chconf |= (ds->slave.wordlen - 1) << 7;
 	chconf |= OMAP3_MCSPI_CHCONF_FORCE;
 	omap3_spi_write_chconf(ds,chconf);
 
@@ -344,7 +358,13 @@  int omap3_spi_txrx(struct spi_slave *slave,
 			}
 		}
 		/* Write the data */
-		writel(txp[i], &ds->regs->channel[ds->slave.cs].tx);
+		unsigned int *tx = &ds->regs->channel[ds->slave.cs].tx;
+		if (ds->slave.wordlen > 16)
+			writel(((u32 *)txp)[i], tx);
+		else if (ds->slave.wordlen > 8)
+			writel(((u16 *)txp)[i], tx);
+		else
+			writel(((u8 *)txp)[i], tx);
 
 		/*Read: wait for RX containing data (RXS == 1)*/
 		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
@@ -356,7 +376,13 @@  int omap3_spi_txrx(struct spi_slave *slave,
 			}
 		}
 		/* Read the data */
-		rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx);
+		unsigned int *rx = &ds->regs->channel[ds->slave.cs].rx;
+		if (ds->slave.wordlen > 16)
+			((u32 *)rxp)[i] = readl(rx);
+		else if (ds->slave.wordlen > 8)
+			((u16 *)rxp)[i] = (u16)readl(rx);
+		else
+			((u8 *)rxp)[i] = (u8)readl(rx);
 	}
 	/* Disable the channel */
 	omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
@@ -375,14 +401,17 @@  int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 {
 	struct omap3_spi_slave *ds = to_omap3_spi(slave);
 	unsigned int	len;
-	const u8	*txp = dout;
-	u8		*rxp = din;
 	int ret = -1;
 
-	if (bitlen % 8)
+	if (ds->slave.wordlen < 4 || ds->slave.wordlen > 32) {
+		printf("omap3_spi: invalid wordlen %d\n", ds->slave.wordlen);
+		return -1;
+	}
+
+	if (bitlen % ds->slave.wordlen)
 		return -1;
 
-	len = bitlen / 8;
+	len = bitlen / ds->slave.wordlen;
 
 	if (bitlen == 0) {	 /* only change CS */
 		int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
@@ -400,11 +429,11 @@  int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 		ret = 0;
 	} else {
 		if (dout != NULL && din != NULL)
-			ret = omap3_spi_txrx(slave, len, txp, rxp, flags);
+			ret = omap3_spi_txrx(slave, len, dout, din, flags);
 		else if (dout != NULL)
-			ret = omap3_spi_write(slave, len, txp, flags);
+			ret = omap3_spi_write(slave, len, dout, flags);
 		else if (din != NULL)
-			ret = omap3_spi_read(slave, len, rxp, flags);
+			ret = omap3_spi_read(slave, len, din, flags);
 	}
 	return ret;
 }
diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h
index 01537b6..ab7cd84 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -99,11 +99,11 @@  static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave)
 	return container_of(slave, struct omap3_spi_slave, slave);
 }
 
-int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, const u8 *txp,
-			u8 *rxp, unsigned long flags);
-int omap3_spi_write(struct spi_slave *slave, unsigned int len, const u8 *txp,
+int omap3_spi_txrx(struct spi_slave *slave, unsigned int len, const void *txp,
+			void *rxp, unsigned long flags);
+int omap3_spi_write(struct spi_slave *slave, unsigned int len, const void *txp,
 		    unsigned long flags);
-int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp,
+int omap3_spi_read(struct spi_slave *slave, unsigned int len, void *rxp,
 		   unsigned long flags);
 
 #endif /* _OMAP3_SPI_H_ */
diff --git a/drivers/spi/spi.c b/drivers/spi/spi.c
index ea39d1a..b76a26c 100644
--- a/drivers/spi/spi.c
+++ b/drivers/spi/spi.c
@@ -8,6 +8,18 @@ 
 #include <malloc.h>
 #include <spi.h>
 
+int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
+{
+	if (wordlen == 0 || wordlen > 32) {
+		printf("spi: invalid wordlen %d\n", wordlen);
+		return -1;
+	}
+
+	slave->wordlen = wordlen;
+
+	return 0;
+}
+
 void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
 			 unsigned int cs)
 {
@@ -20,6 +32,7 @@  void *spi_do_alloc_slave(int offset, int size, unsigned int bus,
 		slave = (struct spi_slave *)(ptr + offset);
 		slave->bus = bus;
 		slave->cs = cs;
+		slave->wordlen = SPI_DEFAULT_WORDLEN;
 	}
 
 	return ptr;
diff --git a/include/spi.h b/include/spi.h
index ad9248b..67da75c 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -33,6 +33,8 @@ 
 /* Header byte that marks the start of the message */
 #define SPI_PREAMBLE_END_BYTE	0xec
 
+#define SPI_DEFAULT_WORDLEN 8
+
 /**
  * struct spi_slave - Representation of a SPI slave
  *
@@ -40,6 +42,7 @@ 
  *
  * @bus:		ID of the bus that the slave is attached to.
  * @cs:			ID of the chip select connected to the slave.
+ * @wordlen:		Size of SPI word in number of bits
  * @max_write_size:	If non-zero, the maximum number of bytes which can
  *			be written at once, excluding command bytes.
  * @memory_map:		Address of read-only SPI flash access.
@@ -47,6 +50,7 @@ 
 struct spi_slave {
 	unsigned int bus;
 	unsigned int cs;
+	unsigned int wordlen;
 	unsigned int max_write_size;
 	void *memory_map;
 };
@@ -153,6 +157,18 @@  int spi_claim_bus(struct spi_slave *slave);
 void spi_release_bus(struct spi_slave *slave);
 
 /**
+ * Set the word length for SPI transactions
+ *
+ * Set the word length (number of bits per word) for SPI transactions.
+ *
+ * @slave:	The SPI slave
+ * @wordlen:	The number of bits in a word
+ *
+ * Returns: 0 on success, -1 on failure.
+ */
+int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
+
+/**
  * SPI transfer
  *
  * This writes "bitlen" bits out the SPI MOSI port and simultaneously clocks