Commit c4a796329d00ce46de6b5afeb1fdabec82830677

Authored by Rajeshwari Shinde
Committed by Jagannadha Sutradharudu Teki
1 parent 120af1572a

spi: exynos: Support word transfers

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>

Showing 2 changed files with 71 additions and 16 deletions Side-by-side Diff

arch/arm/include/asm/arch-exynos/spi.h
... ... @@ -22,7 +22,7 @@
22 22 unsigned int rx_data; /* 0x1c */
23 23 unsigned int pkt_cnt; /* 0x20 */
24 24 unsigned char reserved2[4];
25   - unsigned char reserved3[4];
  25 + unsigned int swap_cfg; /* 0x28 */
26 26 unsigned int fb_clk; /* 0x2c */
27 27 unsigned char padding[0xffd0];
28 28 };
... ... @@ -61,6 +61,15 @@
61 61  
62 62 /* Packet Count */
63 63 #define SPI_PACKET_CNT_EN (1 << 16)
  64 +
  65 +/* Swap config */
  66 +#define SPI_TX_SWAP_EN (1 << 0)
  67 +#define SPI_TX_BYTE_SWAP (1 << 2)
  68 +#define SPI_TX_HWORD_SWAP (1 << 3)
  69 +#define SPI_TX_BYTE_SWAP (1 << 2)
  70 +#define SPI_RX_SWAP_EN (1 << 4)
  71 +#define SPI_RX_BYTE_SWAP (1 << 6)
  72 +#define SPI_RX_HWORD_SWAP (1 << 7)
64 73  
65 74 #endif /* __ASSEMBLY__ */
66 75 #endif
drivers/spi/exynos_spi.c
... ... @@ -204,12 +204,29 @@
204 204 *
205 205 * @param regs SPI peripheral registers
206 206 * @param count Number of bytes to transfer
  207 + * @param step Number of bytes to transfer in each packet (1 or 4)
207 208 */
208   -static void spi_request_bytes(struct exynos_spi *regs, int count)
  209 +static void spi_request_bytes(struct exynos_spi *regs, int count, int step)
209 210 {
  211 + /* For word address we need to swap bytes */
  212 + if (step == 4) {
  213 + setbits_le32(&regs->mode_cfg,
  214 + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
  215 + count /= 4;
  216 + setbits_le32(&regs->swap_cfg, SPI_TX_SWAP_EN | SPI_RX_SWAP_EN |
  217 + SPI_TX_BYTE_SWAP | SPI_RX_BYTE_SWAP |
  218 + SPI_TX_HWORD_SWAP | SPI_RX_HWORD_SWAP);
  219 + } else {
  220 + /* Select byte access and clear the swap configuration */
  221 + clrbits_le32(&regs->mode_cfg,
  222 + SPI_MODE_CH_WIDTH_WORD | SPI_MODE_BUS_WIDTH_WORD);
  223 + writel(0, &regs->swap_cfg);
  224 + }
  225 +
210 226 assert(count && count < (1 << 16));
211 227 setbits_le32(&regs->ch_cfg, SPI_CH_RST);
212 228 clrbits_le32(&regs->ch_cfg, SPI_CH_RST);
  229 +
213 230 writel(count | SPI_PACKET_CNT_EN, &regs->pkt_cnt);
214 231 }
215 232  
... ... @@ -224,6 +241,7 @@
224 241 int toread;
225 242 unsigned start = get_timer(0);
226 243 int stopping;
  244 + int step;
227 245  
228 246 out_bytes = in_bytes = todo;
229 247  
230 248  
... ... @@ -231,10 +249,19 @@
231 249 !(spi_slave->mode & SPI_SLAVE);
232 250  
233 251 /*
  252 + * Try to transfer words if we can. This helps read performance at
  253 + * SPI clock speeds above about 20MHz.
  254 + */
  255 + step = 1;
  256 + if (!((todo | (uintptr_t)rxp | (uintptr_t)txp) & 3) &&
  257 + !spi_slave->skip_preamble)
  258 + step = 4;
  259 +
  260 + /*
234 261 * If there's something to send, do a software reset and set a
235 262 * transaction size.
236 263 */
237   - spi_request_bytes(regs, todo);
  264 + spi_request_bytes(regs, todo, step);
238 265  
239 266 /*
240 267 * Bytes are transmitted/received in pairs. Wait to receive all the
241 268  
242 269  
243 270  
... ... @@ -247,14 +274,26 @@
247 274  
248 275 /* Keep the fifos full/empty. */
249 276 spi_get_fifo_levels(regs, &rx_lvl, &tx_lvl);
  277 +
  278 + /*
  279 + * Don't completely fill the txfifo, since we don't want our
  280 + * rxfifo to overflow, and it may already contain data.
  281 + */
250 282 while (tx_lvl < spi_slave->fifo_size/2 && out_bytes) {
251   - temp = txp ? *txp++ : 0xff;
  283 + if (!txp)
  284 + temp = -1;
  285 + else if (step == 4)
  286 + temp = *(uint32_t *)txp;
  287 + else
  288 + temp = *txp;
252 289 writel(temp, &regs->tx_data);
253   - out_bytes--;
254   - tx_lvl++;
  290 + out_bytes -= step;
  291 + if (txp)
  292 + txp += step;
  293 + tx_lvl += step;
255 294 }
256   - if (rx_lvl > 0) {
257   - while (rx_lvl > 0) {
  295 + if (rx_lvl >= step) {
  296 + while (rx_lvl >= step) {
258 297 temp = readl(&regs->rx_data);
259 298 if (spi_slave->skip_preamble) {
260 299 if (temp == SPI_PREAMBLE_END_BYTE) {
261 300  
... ... @@ -262,12 +301,15 @@
262 301 stopping = 0;
263 302 }
264 303 } else {
265   - if (rxp || stopping)
266   - *rxp++ = temp;
267   - in_bytes--;
  304 + if (rxp || stopping) {
  305 + *rxp = temp;
  306 + rxp += step;
  307 + }
  308 + in_bytes -= step;
268 309 }
269   - toread--;
270   - rx_lvl--;
  310 + toread -= step;
  311 + rx_lvl -= step;
  312 + }
271 313 } else if (!toread) {
272 314 /*
273 315 * We have run out of input data, but haven't read
... ... @@ -279,7 +321,7 @@
279 321 out_bytes = in_bytes;
280 322 toread = in_bytes;
281 323 txp = NULL;
282   - spi_request_bytes(regs, toread);
  324 + spi_request_bytes(regs, toread, step);
283 325 }
284 326 if (spi_slave->skip_preamble && get_timer(start) > 100) {
285 327 printf("SPI timeout: in_bytes=%d, out_bytes=%d, ",
286 328  
... ... @@ -323,10 +365,14 @@
323 365 if ((flags & SPI_XFER_BEGIN))
324 366 spi_cs_activate(slave);
325 367  
326   - /* Exynos SPI limits each transfer to 65535 bytes */
  368 + /*
  369 + * Exynos SPI limits each transfer to 65535 transfers. To keep
  370 + * things simple, allow a maximum of 65532 bytes. We could allow
  371 + * more in word mode, but the performance difference is small.
  372 + */
327 373 bytelen = bitlen / 8;
328 374 for (upto = 0; !ret && upto < bytelen; upto += todo) {
329   - todo = min(bytelen - upto, (1 << 16) - 1);
  375 + todo = min(bytelen - upto, (1 << 16) - 4);
330 376 ret = spi_rx_tx(spi_slave, todo, &din, &dout, flags);
331 377 if (ret)
332 378 break;