spi: exynos: Support word transfers
authorRajeshwari Shinde <rajeshwari.s@samsung.com>
Tue, 8 Oct 2013 10:50:06 +0000 (16:20 +0530)
committerJagannadha Sutradharudu Teki <jaganna@xilinx.com>
Tue, 8 Oct 2013 12:48:12 +0000 (18:18 +0530)
Since SPI register access is so expensive, it is worth transferring data
a word at a time if we can. This complicates the driver unfortunately.

Use the byte-swapping feature to avoid having to convert to/from big
endian in software.

This change increases speed from about 2MB/s to about 4.5MB/s.

Signed-off-by: Simon Glass <sjg@chromium.org>
Signed-off-by: Rajeshwari S Shinde <rajeshwari.s@samsung.com>
Reviewed-by: Jagannadha Sutradharudu Teki <jagannadh.teki@gmail.com>
arch/arm/include/asm/arch-exynos/spi.h
drivers/spi/exynos_spi.c

index fb23aa69c2a56c3fa66464d70d0da1735de785b3..147c1a7304368932d182ef5f5de288866b8b9636 100644 (file)
@@ -22,7 +22,7 @@ struct exynos_spi {
        unsigned int            rx_data;        /* 0x1c */
        unsigned int            pkt_cnt;        /* 0x20 */
        unsigned char           reserved2[4];
-       unsigned char           reserved3[4];
+       unsigned int            swap_cfg;       /* 0x28 */
        unsigned int            fb_clk;         /* 0x2c */
        unsigned char           padding[0xffd0];
 };
@@ -62,5 +62,14 @@ struct exynos_spi {
 /* Packet Count */
 #define SPI_PACKET_CNT_EN      (1 << 16)
 
+/* Swap config */
+#define SPI_TX_SWAP_EN         (1 << 0)
+#define SPI_TX_BYTE_SWAP       (1 << 2)
+#define SPI_TX_HWORD_SWAP      (1 << 3)
+#define SPI_TX_BYTE_SWAP       (1 << 2)
+#define SPI_RX_SWAP_EN         (1 << 4)
+#define SPI_RX_BYTE_SWAP       (1 << 6)
+#define SPI_RX_HWORD_SWAP      (1 << 7)
+
 #endif /* __ASSEMBLY__ */
 #endif
index 7407d6cc1287b05e535befbc8ded08a6cc2aa857..699c57eb6d99209a2180dd171224fa7be3f8d0fa 100644 (file)
@@ -204,12 +204,29 @@ static void spi_get_fifo_levels(struct exynos_spi *regs,
  *
  * @param regs SPI peripheral registers
  * @param count        Number of bytes to transfer
+ * @param step Number of bytes to transfer in each packet (1 or 4)
  */
-static void spi_request_bytes(struct exynos_spi *regs, int count)
+static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
 {
+       /* For word address we need to swap bytes */
+       if (step == 4) {
+               setbits_le32(&regs->mode_cfg,
+                            SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
+               count /= 4;
+               setbits_le32(&regs->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
+                       SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
+                       SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
+       } else {
+               /* Select byte access and clear the swap configuration */
+               clrbits_le32(&regs->mode_cfg,
+                            SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
+               writel(0, &regs->swap_cfg);
+       }
+
        assert(count && count < (1 << 16));
        setbits_le32(&regs->ch_cfg, SPI_CH_RST);
        clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
+
        writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
 }
 
@@ -224,17 +241,27 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
        int toread;
        unsigned start = get_timer(0);
        int stopping;
+       int step;
 
        out_bytes = in_bytes = todo;
 
        stopping = spi_slave->skip_preamble && (flags & SPI_XFER_END) &&
                                        !(spi_slave->mode & SPI_SLAVE);
 
+       /*
+        * Try to transfer words if we can. This helps read performance at
+        * SPI clock speeds above about 20MHz.
+        */
+       step = 1;
+       if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
+           !spi_slave->skip_preamble)
+               step = 4;
+
        /*
         * If there's something to send, do a software reset and set a
         * transaction size.
         */
-       spi_request_bytes(regs, todo);
+       spi_request_bytes(regs, todo, step);
 
        /*
         * Bytes are transmitted/received in pairs. Wait to receive all the
@@ -247,14 +274,26 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
 
                /* Keep the fifos full/empty. */
                spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
+
+               /*
+                * Don't completely fill the txfifo, since we don't want our
+                * rxfifo to overflow, and it may already contain data.
+                */
                while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
-                       temp = txp ? *txp++ : 0xff;
+                       if (!txp)
+                               temp = -1;
+                       else if (step == 4)
+                               temp = *(uint32_t *)txp;
+                       else
+                               temp = *txp;
                        writel(temp, &regs->tx_data);
-                       out_bytes--;
-                       tx_lvl++;
+                       out_bytes -= step;
+                       if (txp)
+                               txp += step;
+                       tx_lvl += step;
                }
-               if (rx_lvl > 0) {
-                       while (rx_lvl > 0) {
+               if (rx_lvl >= step) {
+                       while (rx_lvl >= step) {
                                temp = readl(&regs->rx_data);
                                if (spi_slave->skip_preamble) {
                                        if (temp == SPI_PREAMBLE_END_BYTE) {
@@ -262,12 +301,15 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
                                                stopping = 0;
                                        }
                                } else {
-                                       if (rxp || stopping)
-                                               *rxp++ = temp;
-                                       in_bytes--;
+                                       if (rxp || stopping) {
+                                               *rxp = temp;
+                                               rxp += step;
+                                       }
+                                       in_bytes -= step;
                                }
-                               toread--;
-                               rx_lvl--;
+                               toread -= step;
+                               rx_lvl -= step;
+                       }
                } else if (!toread) {
                        /*
                         * We have run out of input data, but haven't read
@@ -279,7 +321,7 @@ static int spi_rx_tx(struct exynos_spi_slave *spi_slave, int todo,
                        out_bytes = in_bytes;
                        toread = in_bytes;
                        txp = NULL;
-                       spi_request_bytes(regs, toread);
+                       spi_request_bytes(regs, toread, step);
                }
                if (spi_slave->skip_preamble && get_timer(start) > 100) {
                        printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
@@ -323,10 +365,14 @@ int spi_xfer(struct spi_slave *slave, unsigned int bitlen, const void *dout,
        if ((flags & SPI_XFER_BEGIN))
                spi_cs_activate(slave);
 
-       /* Exynos SPI limits each transfer to 65535 bytes */
+       /*
+        * Exynos SPI limits each transfer to 65535 transfers. To keep
+        * things simple, allow a maximum of 65532 bytes. We could allow
+        * more in word mode, but the performance difference is small.
+        */
        bytelen =  bitlen / 8;
        for (upto = 0; !ret && upto < bytelen; upto += todo) {
-               todo = min(bytelen - upto, (1 << 16) - 1);
+               todo = min(bytelen - upto, (1 << 16) - 4);
                ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
                if (ret)
                        break;