Commit 4743a0f88c4000dfa3c422ecc4d750d3a3410550
Committed by
Grant Likely
1 parent
8b66c13474
Exists in
master
and in
4 other branches
spi/omap2_mcspi: add turbo mode support
Turbo mode allows to read data to shift register when rx-buffer is full thus improving the perfomance. This feature is available for RX-only mode. In PIO turbo mode when the penultimate word is available in RX-buffer the controller should be disabled before reading data to prevent the next transaction triggering. The controller itself handles the last word to be correctly loaded to shift-register and then transferred to RX-buffer. The turbo mode is enabled by setting turbo_mode parameter to 1. This parameter is a part of omap2_mcspi_device_config structure which is passed through the spi_device controller_data pointer. Signed-off-by: Roman Tereshonkov <roman.tereshonkov@nokia.com> Signed-off-by: Grant Likely <grant.likely@secretlab.ca>
Showing 1 changed file with 111 additions and 21 deletions Side-by-side Diff
drivers/spi/omap2_mcspi.c
... | ... | @@ -38,8 +38,8 @@ |
38 | 38 | |
39 | 39 | #include <plat/dma.h> |
40 | 40 | #include <plat/clock.h> |
41 | +#include <plat/mcspi.h> | |
41 | 42 | |
42 | - | |
43 | 43 | #define OMAP2_MCSPI_MAX_FREQ 48000000 |
44 | 44 | |
45 | 45 | /* OMAP2 has 3 SPI controllers, while OMAP3 has 4 */ |
... | ... | @@ -229,6 +229,8 @@ |
229 | 229 | |
230 | 230 | l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; |
231 | 231 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); |
232 | + /* Flash post-writes */ | |
233 | + mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHCTRL0); | |
232 | 234 | } |
233 | 235 | |
234 | 236 | static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) |
235 | 237 | |
... | ... | @@ -303,11 +305,14 @@ |
303 | 305 | unsigned int count, c; |
304 | 306 | unsigned long base, tx_reg, rx_reg; |
305 | 307 | int word_len, data_type, element_count; |
308 | + int elements; | |
309 | + u32 l; | |
306 | 310 | u8 * rx; |
307 | 311 | const u8 * tx; |
308 | 312 | |
309 | 313 | mcspi = spi_master_get_devdata(spi->master); |
310 | 314 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; |
315 | + l = mcspi_cached_chconf0(spi); | |
311 | 316 | |
312 | 317 | count = xfer->len; |
313 | 318 | c = count; |
314 | 319 | |
... | ... | @@ -346,8 +351,12 @@ |
346 | 351 | } |
347 | 352 | |
348 | 353 | if (rx != NULL) { |
354 | + elements = element_count - 1; | |
355 | + if (l & OMAP2_MCSPI_CHCONF_TURBO) | |
356 | + elements--; | |
357 | + | |
349 | 358 | omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, |
350 | - data_type, element_count - 1, 1, | |
359 | + data_type, elements, 1, | |
351 | 360 | OMAP_DMA_SYNC_ELEMENT, |
352 | 361 | mcspi_dma->dma_rx_sync_dev, 1); |
353 | 362 | |
354 | 363 | |
355 | 364 | |
356 | 365 | |
... | ... | @@ -379,17 +388,42 @@ |
379 | 388 | wait_for_completion(&mcspi_dma->dma_rx_completion); |
380 | 389 | dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); |
381 | 390 | omap2_mcspi_set_enable(spi, 0); |
391 | + | |
392 | + if (l & OMAP2_MCSPI_CHCONF_TURBO) { | |
393 | + | |
394 | + if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) | |
395 | + & OMAP2_MCSPI_CHSTAT_RXS)) { | |
396 | + u32 w; | |
397 | + | |
398 | + w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); | |
399 | + if (word_len <= 8) | |
400 | + ((u8 *)xfer->rx_buf)[elements++] = w; | |
401 | + else if (word_len <= 16) | |
402 | + ((u16 *)xfer->rx_buf)[elements++] = w; | |
403 | + else /* word_len <= 32 */ | |
404 | + ((u32 *)xfer->rx_buf)[elements++] = w; | |
405 | + } else { | |
406 | + dev_err(&spi->dev, | |
407 | + "DMA RX penultimate word empty"); | |
408 | + count -= (word_len <= 8) ? 2 : | |
409 | + (word_len <= 16) ? 4 : | |
410 | + /* word_len <= 32 */ 8; | |
411 | + omap2_mcspi_set_enable(spi, 1); | |
412 | + return count; | |
413 | + } | |
414 | + } | |
415 | + | |
382 | 416 | if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) |
383 | 417 | & OMAP2_MCSPI_CHSTAT_RXS)) { |
384 | 418 | u32 w; |
385 | 419 | |
386 | 420 | w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); |
387 | 421 | if (word_len <= 8) |
388 | - ((u8 *)xfer->rx_buf)[element_count - 1] = w; | |
422 | + ((u8 *)xfer->rx_buf)[elements] = w; | |
389 | 423 | else if (word_len <= 16) |
390 | - ((u16 *)xfer->rx_buf)[element_count - 1] = w; | |
424 | + ((u16 *)xfer->rx_buf)[elements] = w; | |
391 | 425 | else /* word_len <= 32 */ |
392 | - ((u32 *)xfer->rx_buf)[element_count - 1] = w; | |
426 | + ((u32 *)xfer->rx_buf)[elements] = w; | |
393 | 427 | } else { |
394 | 428 | dev_err(&spi->dev, "DMA RX last word empty"); |
395 | 429 | count -= (word_len <= 8) ? 1 : |
... | ... | @@ -433,7 +467,6 @@ |
433 | 467 | word_len = cs->word_len; |
434 | 468 | |
435 | 469 | l = mcspi_cached_chconf0(spi); |
436 | - l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; | |
437 | 470 | |
438 | 471 | /* We store the pre-calculated register addresses on stack to speed |
439 | 472 | * up the transfer loop. */ |
... | ... | @@ -468,11 +501,26 @@ |
468 | 501 | dev_err(&spi->dev, "RXS timed out\n"); |
469 | 502 | goto out; |
470 | 503 | } |
471 | - /* prevent last RX_ONLY read from triggering | |
472 | - * more word i/o: switch to rx+tx | |
473 | - */ | |
474 | - if (c == 0 && tx == NULL) | |
475 | - mcspi_write_chconf0(spi, l); | |
504 | + | |
505 | + if (c == 1 && tx == NULL && | |
506 | + (l & OMAP2_MCSPI_CHCONF_TURBO)) { | |
507 | + omap2_mcspi_set_enable(spi, 0); | |
508 | + *rx++ = __raw_readl(rx_reg); | |
509 | +#ifdef VERBOSE | |
510 | + dev_dbg(&spi->dev, "read-%d %02x\n", | |
511 | + word_len, *(rx - 1)); | |
512 | +#endif | |
513 | + if (mcspi_wait_for_reg_bit(chstat_reg, | |
514 | + OMAP2_MCSPI_CHSTAT_RXS) < 0) { | |
515 | + dev_err(&spi->dev, | |
516 | + "RXS timed out\n"); | |
517 | + goto out; | |
518 | + } | |
519 | + c = 0; | |
520 | + } else if (c == 0 && tx == NULL) { | |
521 | + omap2_mcspi_set_enable(spi, 0); | |
522 | + } | |
523 | + | |
476 | 524 | *rx++ = __raw_readl(rx_reg); |
477 | 525 | #ifdef VERBOSE |
478 | 526 | dev_dbg(&spi->dev, "read-%d %02x\n", |
... | ... | @@ -506,11 +554,26 @@ |
506 | 554 | dev_err(&spi->dev, "RXS timed out\n"); |
507 | 555 | goto out; |
508 | 556 | } |
509 | - /* prevent last RX_ONLY read from triggering | |
510 | - * more word i/o: switch to rx+tx | |
511 | - */ | |
512 | - if (c == 0 && tx == NULL) | |
513 | - mcspi_write_chconf0(spi, l); | |
557 | + | |
558 | + if (c == 2 && tx == NULL && | |
559 | + (l & OMAP2_MCSPI_CHCONF_TURBO)) { | |
560 | + omap2_mcspi_set_enable(spi, 0); | |
561 | + *rx++ = __raw_readl(rx_reg); | |
562 | +#ifdef VERBOSE | |
563 | + dev_dbg(&spi->dev, "read-%d %04x\n", | |
564 | + word_len, *(rx - 1)); | |
565 | +#endif | |
566 | + if (mcspi_wait_for_reg_bit(chstat_reg, | |
567 | + OMAP2_MCSPI_CHSTAT_RXS) < 0) { | |
568 | + dev_err(&spi->dev, | |
569 | + "RXS timed out\n"); | |
570 | + goto out; | |
571 | + } | |
572 | + c = 0; | |
573 | + } else if (c == 0 && tx == NULL) { | |
574 | + omap2_mcspi_set_enable(spi, 0); | |
575 | + } | |
576 | + | |
514 | 577 | *rx++ = __raw_readl(rx_reg); |
515 | 578 | #ifdef VERBOSE |
516 | 579 | dev_dbg(&spi->dev, "read-%d %04x\n", |
... | ... | @@ -544,11 +607,26 @@ |
544 | 607 | dev_err(&spi->dev, "RXS timed out\n"); |
545 | 608 | goto out; |
546 | 609 | } |
547 | - /* prevent last RX_ONLY read from triggering | |
548 | - * more word i/o: switch to rx+tx | |
549 | - */ | |
550 | - if (c == 0 && tx == NULL) | |
551 | - mcspi_write_chconf0(spi, l); | |
610 | + | |
611 | + if (c == 4 && tx == NULL && | |
612 | + (l & OMAP2_MCSPI_CHCONF_TURBO)) { | |
613 | + omap2_mcspi_set_enable(spi, 0); | |
614 | + *rx++ = __raw_readl(rx_reg); | |
615 | +#ifdef VERBOSE | |
616 | + dev_dbg(&spi->dev, "read-%d %08x\n", | |
617 | + word_len, *(rx - 1)); | |
618 | +#endif | |
619 | + if (mcspi_wait_for_reg_bit(chstat_reg, | |
620 | + OMAP2_MCSPI_CHSTAT_RXS) < 0) { | |
621 | + dev_err(&spi->dev, | |
622 | + "RXS timed out\n"); | |
623 | + goto out; | |
624 | + } | |
625 | + c = 0; | |
626 | + } else if (c == 0 && tx == NULL) { | |
627 | + omap2_mcspi_set_enable(spi, 0); | |
628 | + } | |
629 | + | |
552 | 630 | *rx++ = __raw_readl(rx_reg); |
553 | 631 | #ifdef VERBOSE |
554 | 632 | dev_dbg(&spi->dev, "read-%d %08x\n", |
... | ... | @@ -568,6 +646,7 @@ |
568 | 646 | dev_err(&spi->dev, "EOT timed out\n"); |
569 | 647 | } |
570 | 648 | out: |
649 | + omap2_mcspi_set_enable(spi, 1); | |
571 | 650 | return count - c; |
572 | 651 | } |
573 | 652 | |
... | ... | @@ -797,6 +876,7 @@ |
797 | 876 | struct spi_transfer *t = NULL; |
798 | 877 | int cs_active = 0; |
799 | 878 | struct omap2_mcspi_cs *cs; |
879 | + struct omap2_mcspi_device_config *cd; | |
800 | 880 | int par_override = 0; |
801 | 881 | int status = 0; |
802 | 882 | u32 chconf; |
... | ... | @@ -809,6 +889,7 @@ |
809 | 889 | |
810 | 890 | spi = m->spi; |
811 | 891 | cs = spi->controller_state; |
892 | + cd = spi->controller_data; | |
812 | 893 | |
813 | 894 | omap2_mcspi_set_enable(spi, 1); |
814 | 895 | list_for_each_entry(t, &m->transfers, transfer_list) { |
815 | 896 | |
... | ... | @@ -832,10 +913,19 @@ |
832 | 913 | |
833 | 914 | chconf = mcspi_cached_chconf0(spi); |
834 | 915 | chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; |
916 | + chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; | |
917 | + | |
835 | 918 | if (t->tx_buf == NULL) |
836 | 919 | chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; |
837 | 920 | else if (t->rx_buf == NULL) |
838 | 921 | chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; |
922 | + | |
923 | + if (cd && cd->turbo_mode && t->tx_buf == NULL) { | |
924 | + /* Turbo mode is for more than one word */ | |
925 | + if (t->len > ((cs->word_len + 7) >> 3)) | |
926 | + chconf |= OMAP2_MCSPI_CHCONF_TURBO; | |
927 | + } | |
928 | + | |
839 | 929 | mcspi_write_chconf0(spi, chconf); |
840 | 930 | |
841 | 931 | if (t->len) { |