diff options
author | Roman Tereshonkov <roman.tereshonkov@nokia.com> | 2010-04-13 06:41:51 -0400 |
---|---|---|
committer | Grant Likely <grant.likely@secretlab.ca> | 2010-05-25 02:23:15 -0400 |
commit | 4743a0f88c4000dfa3c422ecc4d750d3a3410550 (patch) | |
tree | 5e5e75696651a949129e3bf5488e6e85818766d4 /drivers/spi | |
parent | 8b66c13474e1683d53255f3b2948231b61cdaefd (diff) |
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>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/omap2_mcspi.c | 132 |
1 files changed, 111 insertions, 21 deletions
diff --git a/drivers/spi/omap2_mcspi.c b/drivers/spi/omap2_mcspi.c index e6d08b844156..6df85537bd19 100644 --- a/drivers/spi/omap2_mcspi.c +++ b/drivers/spi/omap2_mcspi.c | |||
@@ -38,7 +38,7 @@ | |||
38 | 38 | ||
39 | #include <plat/dma.h> | 39 | #include <plat/dma.h> |
40 | #include <plat/clock.h> | 40 | #include <plat/clock.h> |
41 | 41 | #include <plat/mcspi.h> | |
42 | 42 | ||
43 | #define OMAP2_MCSPI_MAX_FREQ 48000000 | 43 | #define OMAP2_MCSPI_MAX_FREQ 48000000 |
44 | 44 | ||
@@ -229,6 +229,8 @@ static void omap2_mcspi_set_enable(const struct spi_device *spi, int enable) | |||
229 | 229 | ||
230 | l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; | 230 | l = enable ? OMAP2_MCSPI_CHCTRL_EN : 0; |
231 | mcspi_write_cs_reg(spi, OMAP2_MCSPI_CHCTRL0, l); | 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 | static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) | 236 | static void omap2_mcspi_force_cs(struct spi_device *spi, int cs_active) |
@@ -303,11 +305,14 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
303 | unsigned int count, c; | 305 | unsigned int count, c; |
304 | unsigned long base, tx_reg, rx_reg; | 306 | unsigned long base, tx_reg, rx_reg; |
305 | int word_len, data_type, element_count; | 307 | int word_len, data_type, element_count; |
308 | int elements; | ||
309 | u32 l; | ||
306 | u8 * rx; | 310 | u8 * rx; |
307 | const u8 * tx; | 311 | const u8 * tx; |
308 | 312 | ||
309 | mcspi = spi_master_get_devdata(spi->master); | 313 | mcspi = spi_master_get_devdata(spi->master); |
310 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; | 314 | mcspi_dma = &mcspi->dma_channels[spi->chip_select]; |
315 | l = mcspi_cached_chconf0(spi); | ||
311 | 316 | ||
312 | count = xfer->len; | 317 | count = xfer->len; |
313 | c = count; | 318 | c = count; |
@@ -346,8 +351,12 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
346 | } | 351 | } |
347 | 352 | ||
348 | if (rx != NULL) { | 353 | if (rx != NULL) { |
354 | elements = element_count - 1; | ||
355 | if (l & OMAP2_MCSPI_CHCONF_TURBO) | ||
356 | elements--; | ||
357 | |||
349 | omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, | 358 | omap_set_dma_transfer_params(mcspi_dma->dma_rx_channel, |
350 | data_type, element_count - 1, 1, | 359 | data_type, elements, 1, |
351 | OMAP_DMA_SYNC_ELEMENT, | 360 | OMAP_DMA_SYNC_ELEMENT, |
352 | mcspi_dma->dma_rx_sync_dev, 1); | 361 | mcspi_dma->dma_rx_sync_dev, 1); |
353 | 362 | ||
@@ -379,17 +388,42 @@ omap2_mcspi_txrx_dma(struct spi_device *spi, struct spi_transfer *xfer) | |||
379 | wait_for_completion(&mcspi_dma->dma_rx_completion); | 388 | wait_for_completion(&mcspi_dma->dma_rx_completion); |
380 | dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); | 389 | dma_unmap_single(NULL, xfer->rx_dma, count, DMA_FROM_DEVICE); |
381 | omap2_mcspi_set_enable(spi, 0); | 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 | if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) | 416 | if (likely(mcspi_read_cs_reg(spi, OMAP2_MCSPI_CHSTAT0) |
383 | & OMAP2_MCSPI_CHSTAT_RXS)) { | 417 | & OMAP2_MCSPI_CHSTAT_RXS)) { |
384 | u32 w; | 418 | u32 w; |
385 | 419 | ||
386 | w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); | 420 | w = mcspi_read_cs_reg(spi, OMAP2_MCSPI_RX0); |
387 | if (word_len <= 8) | 421 | if (word_len <= 8) |
388 | ((u8 *)xfer->rx_buf)[element_count - 1] = w; | 422 | ((u8 *)xfer->rx_buf)[elements] = w; |
389 | else if (word_len <= 16) | 423 | else if (word_len <= 16) |
390 | ((u16 *)xfer->rx_buf)[element_count - 1] = w; | 424 | ((u16 *)xfer->rx_buf)[elements] = w; |
391 | else /* word_len <= 32 */ | 425 | else /* word_len <= 32 */ |
392 | ((u32 *)xfer->rx_buf)[element_count - 1] = w; | 426 | ((u32 *)xfer->rx_buf)[elements] = w; |
393 | } else { | 427 | } else { |
394 | dev_err(&spi->dev, "DMA RX last word empty"); | 428 | dev_err(&spi->dev, "DMA RX last word empty"); |
395 | count -= (word_len <= 8) ? 1 : | 429 | count -= (word_len <= 8) ? 1 : |
@@ -433,7 +467,6 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
433 | word_len = cs->word_len; | 467 | word_len = cs->word_len; |
434 | 468 | ||
435 | l = mcspi_cached_chconf0(spi); | 469 | l = mcspi_cached_chconf0(spi); |
436 | l &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; | ||
437 | 470 | ||
438 | /* We store the pre-calculated register addresses on stack to speed | 471 | /* We store the pre-calculated register addresses on stack to speed |
439 | * up the transfer loop. */ | 472 | * up the transfer loop. */ |
@@ -468,11 +501,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
468 | dev_err(&spi->dev, "RXS timed out\n"); | 501 | dev_err(&spi->dev, "RXS timed out\n"); |
469 | goto out; | 502 | goto out; |
470 | } | 503 | } |
471 | /* prevent last RX_ONLY read from triggering | 504 | |
472 | * more word i/o: switch to rx+tx | 505 | if (c == 1 && tx == NULL && |
473 | */ | 506 | (l & OMAP2_MCSPI_CHCONF_TURBO)) { |
474 | if (c == 0 && tx == NULL) | 507 | omap2_mcspi_set_enable(spi, 0); |
475 | mcspi_write_chconf0(spi, l); | 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 | *rx++ = __raw_readl(rx_reg); | 524 | *rx++ = __raw_readl(rx_reg); |
477 | #ifdef VERBOSE | 525 | #ifdef VERBOSE |
478 | dev_dbg(&spi->dev, "read-%d %02x\n", | 526 | dev_dbg(&spi->dev, "read-%d %02x\n", |
@@ -506,11 +554,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
506 | dev_err(&spi->dev, "RXS timed out\n"); | 554 | dev_err(&spi->dev, "RXS timed out\n"); |
507 | goto out; | 555 | goto out; |
508 | } | 556 | } |
509 | /* prevent last RX_ONLY read from triggering | 557 | |
510 | * more word i/o: switch to rx+tx | 558 | if (c == 2 && tx == NULL && |
511 | */ | 559 | (l & OMAP2_MCSPI_CHCONF_TURBO)) { |
512 | if (c == 0 && tx == NULL) | 560 | omap2_mcspi_set_enable(spi, 0); |
513 | mcspi_write_chconf0(spi, l); | 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 | *rx++ = __raw_readl(rx_reg); | 577 | *rx++ = __raw_readl(rx_reg); |
515 | #ifdef VERBOSE | 578 | #ifdef VERBOSE |
516 | dev_dbg(&spi->dev, "read-%d %04x\n", | 579 | dev_dbg(&spi->dev, "read-%d %04x\n", |
@@ -544,11 +607,26 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
544 | dev_err(&spi->dev, "RXS timed out\n"); | 607 | dev_err(&spi->dev, "RXS timed out\n"); |
545 | goto out; | 608 | goto out; |
546 | } | 609 | } |
547 | /* prevent last RX_ONLY read from triggering | 610 | |
548 | * more word i/o: switch to rx+tx | 611 | if (c == 4 && tx == NULL && |
549 | */ | 612 | (l & OMAP2_MCSPI_CHCONF_TURBO)) { |
550 | if (c == 0 && tx == NULL) | 613 | omap2_mcspi_set_enable(spi, 0); |
551 | mcspi_write_chconf0(spi, l); | 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 | *rx++ = __raw_readl(rx_reg); | 630 | *rx++ = __raw_readl(rx_reg); |
553 | #ifdef VERBOSE | 631 | #ifdef VERBOSE |
554 | dev_dbg(&spi->dev, "read-%d %08x\n", | 632 | dev_dbg(&spi->dev, "read-%d %08x\n", |
@@ -568,6 +646,7 @@ omap2_mcspi_txrx_pio(struct spi_device *spi, struct spi_transfer *xfer) | |||
568 | dev_err(&spi->dev, "EOT timed out\n"); | 646 | dev_err(&spi->dev, "EOT timed out\n"); |
569 | } | 647 | } |
570 | out: | 648 | out: |
649 | omap2_mcspi_set_enable(spi, 1); | ||
571 | return count - c; | 650 | return count - c; |
572 | } | 651 | } |
573 | 652 | ||
@@ -797,6 +876,7 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
797 | struct spi_transfer *t = NULL; | 876 | struct spi_transfer *t = NULL; |
798 | int cs_active = 0; | 877 | int cs_active = 0; |
799 | struct omap2_mcspi_cs *cs; | 878 | struct omap2_mcspi_cs *cs; |
879 | struct omap2_mcspi_device_config *cd; | ||
800 | int par_override = 0; | 880 | int par_override = 0; |
801 | int status = 0; | 881 | int status = 0; |
802 | u32 chconf; | 882 | u32 chconf; |
@@ -809,6 +889,7 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
809 | 889 | ||
810 | spi = m->spi; | 890 | spi = m->spi; |
811 | cs = spi->controller_state; | 891 | cs = spi->controller_state; |
892 | cd = spi->controller_data; | ||
812 | 893 | ||
813 | omap2_mcspi_set_enable(spi, 1); | 894 | omap2_mcspi_set_enable(spi, 1); |
814 | list_for_each_entry(t, &m->transfers, transfer_list) { | 895 | list_for_each_entry(t, &m->transfers, transfer_list) { |
@@ -832,10 +913,19 @@ static void omap2_mcspi_work(struct work_struct *work) | |||
832 | 913 | ||
833 | chconf = mcspi_cached_chconf0(spi); | 914 | chconf = mcspi_cached_chconf0(spi); |
834 | chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; | 915 | chconf &= ~OMAP2_MCSPI_CHCONF_TRM_MASK; |
916 | chconf &= ~OMAP2_MCSPI_CHCONF_TURBO; | ||
917 | |||
835 | if (t->tx_buf == NULL) | 918 | if (t->tx_buf == NULL) |
836 | chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; | 919 | chconf |= OMAP2_MCSPI_CHCONF_TRM_RX_ONLY; |
837 | else if (t->rx_buf == NULL) | 920 | else if (t->rx_buf == NULL) |
838 | chconf |= OMAP2_MCSPI_CHCONF_TRM_TX_ONLY; | 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 | mcspi_write_chconf0(spi, chconf); | 929 | mcspi_write_chconf0(spi, chconf); |
840 | 930 | ||
841 | if (t->len) { | 931 | if (t->len) { |