DaVinci: Improve DaVinci SPI speed.
authorDelio Brignoli <dbrignoli@audioscience.com>
Mon, 7 Jun 2010 21:16:13 +0000 (17:16 -0400)
committerSandeep Paulraj <s-paulraj@ti.com>
Mon, 7 Jun 2010 21:21:25 +0000 (17:21 -0400)
I have updated this patch based on the comments [1] by Wolfgang Denk and
removed unused variables.
[1][http://lists.denx.de/pipermail/u-boot/2010-May/071728.html]

Reduce the number of reads per byte transferred on the BUF register from 2 to 1 and
take advantage of the TX buffer in the SPI module. On LogicPD OMAP-L138 EVM,
SPI read throughput goes up from ~0.8Mbyte/s to ~1.3Mbyte/s. Tested with a 2Mbyte image file.
Remove unused variables in the spi_xfer() function.

Signed-off-by: Delio Brignoli <dbrignoli@audioscience.com>
Tested-by: Ben Gardiner <bengardiner@nanometrics.ca>
Signed-off-by: Sandeep Paulraj <s-paulraj@ti.com>
drivers/spi/davinci_spi.c

index 60ba007aaabf2c83f902ec6e244337a0c98f0fde..08f837b66f61ad753aba9816df8cabe1b6838ac9 100644 (file)
@@ -113,7 +113,8 @@ int spi_claim_bus(struct spi_slave *slave)
        writel(0, &ds->regs->lvl);
 
        /* enable SPI */
-       writel((readl(&ds->regs->gcr1) | SPIGCR1_SPIENA_MASK), &ds->regs->gcr1);
+       writel((readl(&ds->regs->gcr1) |
+               SPIGCR1_SPIENA_MASK), &ds->regs->gcr1);
 
        return 0;
 }
@@ -131,12 +132,10 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
 {
        struct davinci_spi_slave *ds = to_davinci_spi(slave);
        unsigned int    len, data1_reg_val = readl(&ds->regs->dat1);
-       int             ret, i;
+       unsigned int    i_cnt = 0, o_cnt = 0, buf_reg_val;
        const u8        *txp = dout; /* dout can be NULL for read operation */
        u8              *rxp = din;  /* din can be NULL for write operation */
 
-       ret = 0;
-
        if (bitlen == 0)
                /* Finish any previously submitted transfers */
                goto out;
@@ -159,41 +158,51 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen,
        readl(&ds->regs->buf);
 
        /* keep writing and reading 1 byte until done */
-       for (i = 0; i < len; i++) {
-               /* wait till TXFULL is asserted */
-               while (readl(&ds->regs->buf) & SPIBUF_TXFULL_MASK);
-
-               /* write the data */
-               data1_reg_val &= ~0xFFFF;
-               if (txp) {
-                       data1_reg_val |= *txp;
-                       txp++;
+       while ((i_cnt < len) || (o_cnt < len)) {
+               /* read RX buffer and flags */
+               buf_reg_val = readl(&ds->regs->buf);
+
+               /* if data is available */
+               if ((i_cnt < len) &&
+                       (buf_reg_val & SPIBUF_RXEMPTY_MASK) == 0) {
+                       /*
+                        * If there is no read buffer simply
+                        * ignore the read character
+                        */
+                       if (rxp)
+                               *rxp++ = buf_reg_val & 0xFF;
+                       /* increment read words count */
+                       i_cnt++;
                }
 
                /*
-                * Write to DAT1 is required to keep the serial transfer going.
-                * We just terminate when we reach the end.
+                * if the tx buffer is empty and there
+                * is still data to transmit
                 */
-               if ((i == (len - 1)) && (flags & SPI_XFER_END)) {
-                       /* clear CS hold */
-                       writel(data1_reg_val &
-                               ~(1 << SPIDAT1_CSHOLD_SHIFT), &ds->regs->dat1);
-               } else {
-                       /* enable CS hold */
-                       data1_reg_val |= ((1 << SPIDAT1_CSHOLD_SHIFT) |
+               if ((o_cnt < len) &&
+                       ((buf_reg_val & SPIBUF_TXFULL_MASK) == 0)) {
+                       /* write the data */
+                       data1_reg_val &= ~0xFFFF;
+                       if (txp)
+                               data1_reg_val |= *txp++;
+                       /*
+                        * Write to DAT1 is required to keep
+                        * the serial transfer going.
+                        * We just terminate when we reach the end.
+                        */
+                       if ((o_cnt == (len - 1)) && (flags & SPI_XFER_END)) {
+                               /* clear CS hold */
+                               writel(data1_reg_val &
+                                               ~(1 << SPIDAT1_CSHOLD_SHIFT),
+                                               &ds->regs->dat1);
+                       } else {
+                               /* enable CS hold and write TX register */
+                               data1_reg_val |= ((1 << SPIDAT1_CSHOLD_SHIFT) |
                                        (slave->cs << SPIDAT1_CSNR_SHIFT));
-                       writel(data1_reg_val, &ds->regs->dat1);
-               }
-
-               /* read the data - wait for data availability */
-               while (readl(&ds->regs->buf) & SPIBUF_RXEMPTY_MASK);
-
-               if (rxp) {
-                       *rxp = readl(&ds->regs->buf) & 0xFF;
-                       rxp++;
-               } else {
-                       /* simply drop the read character */
-                       readl(&ds->regs->buf);
+                               writel(data1_reg_val, &ds->regs->dat1);
+                       }
+                       /* increment written words count */
+                       o_cnt++;
                }
        }
        return 0;