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 <mondi@cs.unibo.it> <j.mondi@voltaelectronics.com>
diff --git a/drivers/spi/omap3_spi.c b/drivers/spi/omap3_spi.c
index af12c0e..9346c0b 100644
--- a/drivers/spi/omap3_spi.c
+++ b/drivers/spi/omap3_spi.c
@@ -297,6 +297,65 @@
 	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 @@
 		}
 		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;