From 08b5ab073d4eb389ffd622f71094cf1c70253c00 Mon Sep 17 00:00:00 2001 From: jacopo mondi Date: Wed, 2 Mar 2011 05:13:22 +0000 Subject: omap3_spi: receive transmit mode Implementation of receive-transmit mode for omap3 MCSPI. Introduces full duplex communication, needed by some spi devices (such as enc28j60). Signed-off-by: jacopo mondi --- drivers/spi/omap3_spi.c | 66 ++++++++++++++++++++++++++++++++++++++++++++++--- drivers/spi/omap3_spi.h | 2 ++ 2 files changed, 65 insertions(+), 3 deletions(-) (limited to 'drivers/spi') diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c index af12c0e590..9346c0b5b4 100644 --- a/drivers/spi/omap3_spi.c +++ b/drivers/spi/omap3_spi.c @@ -297,6 +297,65 @@ int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, return 0; } +/*McSPI Transmit Receive Mode*/ +int omap3_spi_txrx(struct spi_slave *slave, + unsigned int len, const u8 *txp, u8 *rxp, unsigned long flags) +{ + struct omap3_spi_slave *ds = to_omap3_spi(slave); + int timeout = SPI_WAIT_TIMEOUT; + int chconf = readl(&ds->regs->channel[ds->slave.cs].chconf); + int irqstatus = readl(&ds->regs->irqstatus); + int i=0; + + /*Enable SPI channel*/ + if (flags & SPI_XFER_BEGIN) + writel(OMAP3_MCSPI_CHCTRL_EN, + &ds->regs->channel[ds->slave.cs].chctrl); + + /*set TRANSMIT-RECEIVE Mode*/ + chconf &= ~OMAP3_MCSPI_CHCONF_TRM_MASK; + chconf |= OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + /*Shift in and out 1 byte at time*/ + for (i=0; i < len; i++){ + /* Write: wait for TX empty (TXS == 1)*/ + irqstatus |= (1<< (4*(ds->slave.bus))); + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_TXS)) { + if (--timeout <= 0) { + printf("SPI TXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Write the data */ + writel(txp[i], &ds->regs->channel[ds->slave.cs].tx); + + /*Read: wait for RX containing data (RXS == 1)*/ + while (!(readl(&ds->regs->channel[ds->slave.cs].chstat) & + OMAP3_MCSPI_CHSTAT_RXS)) { + if (--timeout <= 0) { + printf("SPI RXS timed out, status=0x%08x\n", + readl(&ds->regs->channel[ds->slave.cs].chstat)); + return -1; + } + } + /* Read the data */ + rxp[i] = readl(&ds->regs->channel[ds->slave.cs].rx); + } + + /*if transfer must be terminated disable the channel*/ + if (flags & SPI_XFER_END) { + chconf &= ~OMAP3_MCSPI_CHCONF_FORCE; + writel(chconf, &ds->regs->channel[ds->slave.cs].chconf); + + writel(0, &ds->regs->channel[ds->slave.cs].chctrl); + } + + return 0; +} + int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout, void *din, unsigned long flags) { @@ -329,10 +388,11 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, } ret = 0; } else { - if (dout != NULL) + if (dout != NULL && din != NULL) + ret = omap3_spi_txrx(slave, len, txp, rxp, flags); + else if (dout != NULL) ret = omap3_spi_write(slave, len, txp, flags); - - if (din != NULL) + else if (din != NULL) ret = omap3_spi_read(slave, len, rxp, flags); } return ret; diff --git a/drivers/spi/omap3_spi.h b/drivers/spi/omap3_spi.h index b8e3a4c44c..0ac801cb25 100644 --- a/drivers/spi/omap3_spi.h +++ b/drivers/spi/omap3_spi.h @@ -109,6 +109,8 @@ 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, unsigned long flags); int omap3_spi_read(struct spi_slave *slave, unsigned int len, u8 *rxp, -- cgit v1.2.1