diff options
| author | Guennadi Liakhovetski <lg@denx.de> | 2009-02-13 09:26:40 +0100 | 
|---|---|---|
| committer | Anatolij Gustschin <agust@denx.de> | 2009-02-24 09:48:33 +0100 | 
| commit | fc7a93c84f3f134484811a0d9ad751fbc1a7da6d (patch) | |
| tree | be6da52e89b10fa6edc404094e79aef92c6a822e /drivers | |
| parent | b30de3cccf8867566cd314e7c7033904afa5dc9d (diff) | |
| download | olio-uboot-2014.01-fc7a93c84f3f134484811a0d9ad751fbc1a7da6d.tar.xz olio-uboot-2014.01-fc7a93c84f3f134484811a0d9ad751fbc1a7da6d.zip | |
i.MX31: support GPIO as a chip-select in the mxc_spi driver
Some SPI devices have special requirements on chip-select handling.
With this patch we can use a GPIO as a chip-select and strictly follow
the SPI_XFER_BEGIN and SPI_XFER_END flags.
Signed-off-by: Guennadi Liakhovetski <lg@denx.de>
Acked-by: Jean-Christophe PLAGNIOL-VILLARD <plagnioj@jcrosoft.com>
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/spi/mxc_spi.c | 64 | 
1 files changed, 56 insertions, 8 deletions
| diff --git a/drivers/spi/mxc_spi.c b/drivers/spi/mxc_spi.c index 0ac4e908e..fad98403a 100644 --- a/drivers/spi/mxc_spi.c +++ b/drivers/spi/mxc_spi.c @@ -21,6 +21,7 @@  #include <common.h>  #include <malloc.h>  #include <spi.h> +#include <asm/errno.h>  #include <asm/io.h>  #ifdef CONFIG_MX27 @@ -32,6 +33,8 @@  #else +#include <asm/arch/mx31.h> +  #define MXC_CSPIRXDATA		0x00  #define MXC_CSPITXDATA		0x04  #define MXC_CSPICTRL		0x08 @@ -68,6 +71,7 @@ struct mxc_spi_slave {  	struct spi_slave slave;  	unsigned long	base;  	u32		ctrl_reg; +	int		gpio;  };  static inline struct mxc_spi_slave *to_mxc_spi_slave(struct spi_slave *slave) @@ -85,7 +89,8 @@ static inline void reg_write(unsigned long addr, u32 val)  	*(volatile unsigned long*)addr = val;  } -static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen) +static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen, +			   unsigned long flags)  {  	struct mxc_spi_slave *mxcs = to_mxc_spi_slave(slave);  	unsigned int cfg_reg = reg_read(mxcs->base + MXC_CSPICTRL); @@ -96,6 +101,9 @@ static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen)  	if (cfg_reg != mxcs->ctrl_reg)  		reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg); +	if (mxcs->gpio > 0 && (flags & SPI_XFER_BEGIN)) +		mx31_gpio_set(mxcs->gpio, mxcs->ctrl_reg & MXC_CSPICTRL_SSPOL); +  	reg_write(mxcs->base + MXC_CSPITXDATA, data);  	reg_write(mxcs->base + MXC_CSPICTRL, mxcs->ctrl_reg | MXC_CSPICTRL_XCH); @@ -103,6 +111,11 @@ static u32 spi_xchg_single(struct spi_slave *slave, u32 data, int bitlen)  	while (reg_read(mxcs->base + MXC_CSPICTRL) & MXC_CSPICTRL_XCH)  		; +	if (mxcs->gpio > 0 && (flags & SPI_XFER_END)) { +		mx31_gpio_set(mxcs->gpio, +			      !(mxcs->ctrl_reg & MXC_CSPICTRL_SSPOL)); +	} +  	return reg_read(mxcs->base + MXC_CSPIRXDATA);  } @@ -121,7 +134,7 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,  	for (i = 0, in_l = (u32 *)din, out_l = (u32 *)dout;  	     i < n_blks;  	     i++, in_l++, out_l++, bitlen -= 32) { -		u32 data = spi_xchg_single(slave, *out_l, bitlen); +		u32 data = spi_xchg_single(slave, *out_l, bitlen, flags);  		/* Check if we're only transfering 8 or 16 bits */  		if (!i) { @@ -139,16 +152,55 @@ void spi_init(void)  {  } +static int decode_cs(struct mxc_spi_slave *mxcs, unsigned int cs) +{ +	int ret; + +	/* +	 * Some SPI devices require active chip-select over multiple +	 * transactions, we achieve this using a GPIO. Still, the SPI +	 * controller has to be configured to use one of its own chipselects. +	 * To use this feature you have to call spi_setup_slave() with +	 * cs = internal_cs | (gpio << 8), and you have to use some unused +	 * on this SPI controller cs between 0 and 3. +	 */ +	if (cs > 3) { +		mxcs->gpio = cs >> 8; +		cs &= 3; +		ret = mx31_gpio_direction(mxcs->gpio, MX31_GPIO_DIRECTION_OUT); +		if (ret) { +			printf("mxc_spi: cannot setup gpio %d\n", mxcs->gpio); +			return -EINVAL; +		} +	} else { +		mxcs->gpio = -1; +	} + +	return cs; +} +  struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  			unsigned int max_hz, unsigned int mode)  {  	unsigned int ctrl_reg;  	struct mxc_spi_slave *mxcs; +	int ret; -	if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) || -	    cs > 3) +	if (bus >= ARRAY_SIZE(spi_bases))  		return NULL; +	mxcs = malloc(sizeof(struct mxc_spi_slave)); +	if (!mxcs) +		return NULL; + +	ret = decode_cs(mxcs, cs); +	if (ret < 0) { +		free(mxcs); +		return NULL; +	} + +	cs = ret; +  	ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) |  		MXC_CSPICTRL_BITCOUNT(31) |  		MXC_CSPICTRL_DATARATE(7) | /* FIXME: calculate data rate */ @@ -162,10 +214,6 @@ struct spi_slave *spi_setup_slave(unsigned int bus, unsigned int cs,  	if (mode & SPI_CS_HIGH)  		ctrl_reg |= MXC_CSPICTRL_SSPOL; -	mxcs = malloc(sizeof(struct mxc_spi_slave)); -	if (!mxcs) -		return NULL; -  	mxcs->slave.bus = bus;  	mxcs->slave.cs = cs;  	mxcs->base = spi_bases[bus]; |