diff mbox

[U-Boot,1/3] spi: omap3: add support for more word lengths

Message ID 1381329965-7775-2-git-send-email-nikita@compulab.co.il
State Changes Requested
Delegated to: Tom Rini
Headers show

Commit Message

Nikita Kiryanov Oct. 9, 2013, 2:46 p.m. UTC
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: Igor Grinberg <grinberg@compulab.co.il>
Cc: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
Signed-off-by: Nikita Kiryanov <nikita@compulab.co.il>
---
 drivers/spi/omap3_spi.c | 78 ++++++++++++++++++++++++++++++++++++++-----------
 drivers/spi/omap3_spi.h | 11 ++++---
 include/spi.h           | 13 +++++++++
 3 files changed, 81 insertions(+), 21 deletions(-)

Comments

Gerhard Sittig Oct. 10, 2013, 3:26 p.m. UTC | #1
On Wed, Oct 09, 2013 at 17:46 +0300, Nikita Kiryanov wrote:
> 
> --- a/drivers/spi/omap3_spi.c
> +++ b/drivers/spi/omap3_spi.c
> @@ -20,7 +20,7 @@
>  #include <asm/io.h>
>  #include "omap3_spi.h"
>  
> -#define WORD_LEN	8
> +#define SPI_DEFAULT_WORDLEN 8;
>  #define SPI_WAIT_TIMEOUT 3000000;
>  
>  static void spi_reset(struct omap3_spi_slave *ds)

This is strictly speaking unrelated to your change, but might be
worth getting fixed in a separate submission.  I haven't read
your patch in the meaning of a review, just happened to spot this
nit before skipping to the next message ...

The semicolon in a #define for a constant is certainly wrong.
This applies to both your new line as well as the context which
already was in place.  The code may appear to work, but for
sanity and to avoid future problems the defines should get fixed.
You might add the separate cleanup patch in front of the new
feature should feedback make you send another version of your
series, or send a fix individually (and check for other
occurances for bonus points).


virtually yours
Gerhard Sittig
Igor Grinberg Oct. 14, 2013, 2:18 p.m. UTC | #2
Hi Nikita,

On 10/09/13 16:46, Nikita Kiryanov 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: Igor Grinberg <grinberg@compulab.co.il>
> Cc: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
> Signed-off-by: Nikita Kiryanov <nikita@compulab.co.il>
> ---
>  drivers/spi/omap3_spi.c | 78 ++++++++++++++++++++++++++++++++++++++-----------
>  drivers/spi/omap3_spi.h | 11 ++++---
>  include/spi.h           | 13 +++++++++
>  3 files changed, 81 insertions(+), 21 deletions(-)
> 
> diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
> index 6bac245..add98e1 100644
> --- a/drivers/spi/omap3_spi.c
> +++ b/drivers/spi/omap3_spi.c
> @@ -20,7 +20,7 @@
>  #include <asm/io.h>
>  #include "omap3_spi.h"
>  
> -#define WORD_LEN	8
> +#define SPI_DEFAULT_WORDLEN 8;

As already pointed out, the semicolon should not be there...

>  #define SPI_WAIT_TIMEOUT 3000000;

While at this, can you please, send a separate patch
to fix the above semicolon?

>  
>  static void spi_reset(struct omap3_spi_slave *ds)
> @@ -128,10 +128,32 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
>  	ds->regs = regs;
>  	ds->freq = max_hz;
>  	ds->mode = mode;
> +	ds->wordlen = SPI_DEFAULT_WORDLEN;

While we know, OMAP SPI controller supports this,
I think many other controllers can also support this feature
and thus their drivers can benefit from this option.

Wouldn't it be better to add the wordlen field to the generic slave
structure instead of the OMAP specific structure?

>  
>  	return &ds->slave;
>  }
>  
> +int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
> +{
> +	struct omap3_spi_slave *ds;
> +	int chconf;
> +
> +	if (wordlen < 4 || wordlen > 32) {
> +		printf("SPI error: unsupported wordlen %d\n", wordlen);
> +		return -1;
> +	}
> +
> +	ds = to_omap3_spi(slave);
> +	ds->wordlen = wordlen;
> +
> +	chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
> +	chconf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
> +	chconf |= (wordlen - 1) << 7;
> +	omap3_spi_write_chconf(ds, chconf);

I would recommend to update the controller just before the transaction
takes place, e.g. in spi_xfer() function, so the spi_set_wordlen() function
will only update the slave structure and will not touch the hardware.
Thus spi_set_wordlen() can be placed in the generic SPI code.

> +
> +	return 0;
> +}
> +
>  void spi_free_slave(struct spi_slave *slave)
>  {
>  	struct omap3_spi_slave *ds = to_omap3_spi(slave);
> @@ -185,7 +207,7 @@ int spi_claim_bus(struct spi_slave *slave)
>  
>  	/* wordlength */
>  	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
> -	conf |= (WORD_LEN - 1) << 7;
> +	conf |= (ds->wordlen - 1) << 7;
>  
>  	/* set chipselect polarity; manage with FORCE */
>  	if (!(ds->mode & SPI_CS_HIGH))
> @@ -223,7 +245,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);
> @@ -250,7 +272,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->wordlen > 16)
> +			writel(((u32 *)txp)[i], tx);
> +		else if (ds->wordlen > 8)
> +			writel(((u16 *)txp)[i], tx);
> +		else
> +			writel(((u8 *)txp)[i], tx);

Can we always have u32 for the txp and use masks ans shifts instead of
the ugly castings?

>  	}
>  
>          /* wait to finish of transfer */
> @@ -268,7 +296,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);
> @@ -302,7 +330,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->wordlen > 16)
> +			((u32 *)rxp)[i] = readl(rx);
> +		else if (ds->wordlen > 8)
> +			((u16 *)rxp)[i] = (u16)readl(rx);
> +		else
> +			((u8 *)rxp)[i] = (u8)readl(rx);

same here...

>  	}
>  
>  	if (flags & SPI_XFER_END) {
> @@ -314,8 +348,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;
> @@ -344,7 +378,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->wordlen > 16)
> +			writel(((u32 *)txp)[i], tx);
> +		else if (ds->wordlen > 8)
> +			writel(((u16 *)txp)[i], tx);
> +		else
> +			writel(((u8 *)txp)[i], tx);

and here...

>  
>  		/*Read: wait for RX containing data (RXS == 1)*/
>  		while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) &
> @@ -356,7 +396,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->wordlen > 16)
> +			((u32 *)rxp)[i] = readl(rx);
> +		else if (ds->wordlen > 8)
> +			((u16 *)rxp)[i] = (u16)readl(rx);
> +		else
> +			((u8 *)rxp)[i] = (u8)readl(rx);

and here...

>  	}
>  	/* Disable the channel */
>          omap3_spi_set_enable(ds,OMAP3_MCSPI_CHCTRL_DIS);
> @@ -375,14 +421,12 @@ 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 (bitlen % ds->wordlen)
>  		return -1;
>  
> -	len = bitlen / 8;
> +	len = bitlen / ds->wordlen;
>  
>  	if (bitlen == 0) {	 /* only change CS */
>  		int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
> @@ -400,11 +444,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..aeddb53 100644
> --- a/drivers/spi/omap3_spi.h
> +++ b/drivers/spi/omap3_spi.h
> @@ -92,6 +92,7 @@ struct omap3_spi_slave {
>  	struct mcspi *regs;
>  	unsigned int freq;
>  	unsigned int mode;
> +	unsigned int wordlen;

See above.

>  };
>  
>  static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave)
> @@ -99,11 +100,13 @@ 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_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
> +
> +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);

u32 *?

>  
>  #endif /* _OMAP3_SPI_H_ */

[...]

> diff --git a/include/spi.h b/include/spi.h
> index c0dab57..ae318ff 100644
> --- a/include/spi.h
> +++ b/include/spi.h
> @@ -149,6 +149,19 @@ 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.
> + * This setting is persistent until changed.
> + *
> + *   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
>
Nikita Kiryanov Oct. 15, 2013, 11:09 a.m. UTC | #3
On 10/14/2013 05:18 PM, Igor Grinberg wrote:
>> diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
>> index 6bac245..add98e1 100644
>> --- a/drivers/spi/omap3_spi.c
>> +++ b/drivers/spi/omap3_spi.c
>> @@ -20,7 +20,7 @@
>>   #include <asm/io.h>
>>   #include "omap3_spi.h"
>>
>> -#define WORD_LEN	8
>> +#define SPI_DEFAULT_WORDLEN 8;
>
> As already pointed out, the semicolon should not be there...

Will fix

>
>>   #define SPI_WAIT_TIMEOUT 3000000;
>
> While at this, can you please, send a separate patch
> to fix the above semicolon?

Will do

>
>>
>>   static void spi_reset(struct omap3_spi_slave *ds)
>> @@ -128,10 +128,32 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
>>   	ds->regs = regs;
>>   	ds->freq = max_hz;
>>   	ds->mode = mode;
>> +	ds->wordlen = SPI_DEFAULT_WORDLEN;
>
> While we know, OMAP SPI controller supports this,
> I think many other controllers can also support this feature
> and thus their drivers can benefit from this option.
>
> Wouldn't it be better to add the wordlen field to the generic slave
> structure instead of the OMAP specific structure?

Makes sense. Will move wordlen to struct spi_slave.

>
>>
>>   	return &ds->slave;
>>   }
>>
>> +int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
>> +{
>> +	struct omap3_spi_slave *ds;
>> +	int chconf;
>> +
>> +	if (wordlen < 4 || wordlen > 32) {
>> +		printf("SPI error: unsupported wordlen %d\n", wordlen);
>> +		return -1;
>> +	}
>> +
>> +	ds = to_omap3_spi(slave);
>> +	ds->wordlen = wordlen;
>> +
>> +	chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
>> +	chconf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
>> +	chconf |= (wordlen - 1) << 7;
>> +	omap3_spi_write_chconf(ds, chconf);
>
> I would recommend to update the controller just before the transaction
> takes place, e.g. in spi_xfer() function, so the spi_set_wordlen() function
> will only update the slave structure and will not touch the hardware.
> Thus spi_set_wordlen() can be placed in the generic SPI code.

Yes that is better. Will do.

>>
>> -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);
>> @@ -250,7 +272,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->wordlen > 16)
>> +			writel(((u32 *)txp)[i], tx);
>> +		else if (ds->wordlen > 8)
>> +			writel(((u16 *)txp)[i], tx);
>> +		else
>> +			writel(((u8 *)txp)[i], tx);
>
> Can we always have u32 for the txp and use masks ans shifts instead of
> the ugly castings?

Working with masks and shifts will basically do the same thing that the
casts do, but in a more complicated way. The best way to get rid of the
casts will be to store SPI words in u32 arrays to begin with,
regardless of the size of SPI word. Unfortunately, this is actually a
difficult change:

If we change omap3_spi implementation to work with u32 arrays we also
have to change cmd_spi.c, which stores data in byte arrays, and then
we will be forced to change all the other U-Boot SPI implementations.

Due to the scope of this change I think it should be done in another
patch series.
diff mbox

Patch

diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index 6bac245..add98e1 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -20,7 +20,7 @@ 
 #include <asm/io.h>
 #include "omap3_spi.h"
 
-#define WORD_LEN	8
+#define SPI_DEFAULT_WORDLEN 8;
 #define SPI_WAIT_TIMEOUT 3000000;
 
 static void spi_reset(struct omap3_spi_slave *ds)
@@ -128,10 +128,32 @@  struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,
 	ds->regs = regs;
 	ds->freq = max_hz;
 	ds->mode = mode;
+	ds->wordlen = SPI_DEFAULT_WORDLEN;
 
 	return &ds->slave;
 }
 
+int spi_set_wordlen(struct spi_slave *slave, unsigned int wordlen)
+{
+	struct omap3_spi_slave *ds;
+	int chconf;
+
+	if (wordlen < 4 || wordlen > 32) {
+		printf("SPI error: unsupported wordlen %d\n", wordlen);
+		return -1;
+	}
+
+	ds = to_omap3_spi(slave);
+	ds->wordlen = wordlen;
+
+	chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
+	chconf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
+	chconf |= (wordlen - 1) << 7;
+	omap3_spi_write_chconf(ds, chconf);
+
+	return 0;
+}
+
 void spi_free_slave(struct spi_slave *slave)
 {
 	struct omap3_spi_slave *ds = to_omap3_spi(slave);
@@ -185,7 +207,7 @@  int spi_claim_bus(struct spi_slave *slave)
 
 	/* wordlength */
 	conf &= ~OMAP3_MCSPI_CHCONF_WL_MASK;
-	conf |= (WORD_LEN - 1) << 7;
+	conf |= (ds->wordlen - 1) << 7;
 
 	/* set chipselect polarity; manage with FORCE */
 	if (!(ds->mode & SPI_CS_HIGH))
@@ -223,7 +245,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);
@@ -250,7 +272,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->wordlen > 16)
+			writel(((u32 *)txp)[i], tx);
+		else if (ds->wordlen > 8)
+			writel(((u16 *)txp)[i], tx);
+		else
+			writel(((u8 *)txp)[i], tx);
 	}
 
         /* wait to finish of transfer */
@@ -268,7 +296,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);
@@ -302,7 +330,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->wordlen > 16)
+			((u32 *)rxp)[i] = readl(rx);
+		else if (ds->wordlen > 8)
+			((u16 *)rxp)[i] = (u16)readl(rx);
+		else
+			((u8 *)rxp)[i] = (u8)readl(rx);
 	}
 
 	if (flags & SPI_XFER_END) {
@@ -314,8 +348,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;
@@ -344,7 +378,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->wordlen > 16)
+			writel(((u32 *)txp)[i], tx);
+		else if (ds->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 +396,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->wordlen > 16)
+			((u32 *)rxp)[i] = readl(rx);
+		else if (ds->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 +421,12 @@  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 (bitlen % ds->wordlen)
 		return -1;
 
-	len = bitlen / 8;
+	len = bitlen / ds->wordlen;
 
 	if (bitlen == 0) {	 /* only change CS */
 		int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf);
@@ -400,11 +444,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..aeddb53 100644
--- a/drivers/spi/omap3_spi.h
+++ b/drivers/spi/omap3_spi.h
@@ -92,6 +92,7 @@  struct omap3_spi_slave {
 	struct mcspi *regs;
 	unsigned int freq;
 	unsigned int mode;
+	unsigned int wordlen;
 };
 
 static inline struct omap3_spi_slave *to_omap3_spi(struct spi_slave *slave)
@@ -99,11 +100,13 @@  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_set_wordlen(struct spi_slave *slave, unsigned int wordlen);
+
+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/include/spi.h b/include/spi.h
index c0dab57..ae318ff 100644
--- a/include/spi.h
+++ b/include/spi.h
@@ -149,6 +149,19 @@  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.
+ * This setting is persistent until changed.
+ *
+ *   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