[U-Boot] [PATCH 3/9 v4] 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 ---
Changes since v1: long lines split, mx31_gpio_* calls now also defined if CONFIG_MX31_GPIO is not defined (see patch 2/9), '!!' removed. Changes since v2: added braces in a multiline if, switched to ARRAY_SIZE(). Changes since v3: moved GPIO chipselect initialisation into a separate function, as suggested by Wolfgang Denk
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 3135817..daad1bb 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,15 +152,54 @@ 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 >= ARRAY_SIZE(spi_bases)) + return NULL; + + mxcs = malloc(sizeof(struct mxc_spi_slave)); + if (!mxcs) + return NULL;
- if (bus >= sizeof(spi_bases) / sizeof(spi_bases[0]) || - cs > 3) + ret = decode_cs(mxcs, cs); + if (ret < 0) { + free(mxcs); return NULL; + } + + cs = ret;
ctrl_reg = MXC_CSPICTRL_CHIPSELECT(cs) | MXC_CSPICTRL_BITCOUNT(31) | @@ -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];

On 09:26 Fri 13 Feb , Guennadi Liakhovetski wrote:
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
Changes since v1: long lines split, mx31_gpio_* calls now also defined if CONFIG_MX31_GPIO is not defined (see patch 2/9), '!!' removed. Changes since v2: added braces in a multiline if, switched to ARRAY_SIZE(). Changes since v3: moved GPIO chipselect initialisation into a separate function, as suggested by Wolfgang Denk
Evenif I do not like to only allow to use cpu GPIO as cs
With the current SPI implementation
Ack-by : Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com
Best Regards, J.

Dear Jean-Christophe PLAGNIOL-VILLARD,
In message 20090214083112.GC13612@game.jcrosoft.org you wrote:
Ack-by : Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com
Why do you ACK this? Are you waiting for someone else to apply this?
It's you who is the ARM custodian, right?
Best regards,
Wolfgang Denk

On Mon, 16 Feb 2009, Wolfgang Denk wrote:
Dear Jean-Christophe PLAGNIOL-VILLARD,
In message 20090214083112.GC13612@game.jcrosoft.org you wrote:
Ack-by : Jean-Christophe PLAGNIOL-VILLARD plagnioj@jcrosoft.com
Why do you ACK this? Are you waiting for someone else to apply this?
It's better to pull this whole series via one tree due to inter-dependencies, and as most diffstat is under drivers/video, we decided to pull it via the video tree, patches that are not directly video-related have to be acked by respective custodians.
It's you who is the ARM custodian, right?
Thanks Guennadi --- Guennadi Liakhovetski, Ph.D.
DENX Software Engineering GmbH, MD: Wolfgang Denk & Detlev Zundel HRB 165235 Munich, Office: Kirchenstr.5, D-82194 Groebenzell, Germany Phone: +49-8142-66989-0 Fax: +49-8142-66989-80 Email: office@denx.de

On Friday 13 February 2009 09:26:40 am Guennadi Liakhovetski wrote:
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
Changes since v1: long lines split, mx31_gpio_* calls now also defined if CONFIG_MX31_GPIO is not defined (see patch 2/9), '!!' removed. Changes since v2: added braces in a multiline if, switched to ARRAY_SIZE(). Changes since v3: moved GPIO chipselect initialisation into a separate function, as suggested by Wolfgang Denk
drivers/spi/mxc_spi.c | 64 ++++++++++++++++++++++++++++++++++++++++++------ 1 files changed, 56 insertions(+), 8 deletions(-)
Applied to u-boot-video/master.
Thanks, Anatolij
participants (4)
-
Anatolij Gustschin
-
Guennadi Liakhovetski
-
Jean-Christophe PLAGNIOL-VILLARD
-
Wolfgang Denk