diff options
author | Vignesh R <vigneshr@ti.com> | 2017-04-11 07:52:25 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2017-04-21 13:03:53 -0400 |
commit | c687c46e9e4527c4b4d82bc3cca58c1b08bcfb83 (patch) | |
tree | 1e7db7fdf3adb9e07d1e528c277c84fe54f41c51 /drivers/spi | |
parent | 2bca34455b257d75080d87e800ae14afe49001bf (diff) |
spi: spi-ti-qspi: Use bounce buffer if read buffer is not DMA'ble
Flash filesystems like JFFS2, UBIFS and MTD block layer can provide
vmalloc'd or kmap'd buffers that cannot be mapped using dma_map_sg() and
can potentially be in memory region above 32bit addressable region(ie
buffers belonging to memory region backed by LPAE) of DMA, implement
spi_flash_can_dma() interface to inform SPI core not to map such
buffers.
When buffers are not mapped for DMA, then use a pre allocated bounce
buffer(64K = typical flash erase sector size) to read from flash and
then do a copy to actual destination buffer. This is approach is much
faster than using memcpy using CPU and also reduces CPU load.
With this patch, UBIFS read speed is ~18MB/s and CPU utilization <20% on
DRA74 Rev H EVM. Performance degradation is negligible when compared
with non bounce buffer case while using UBIFS.
Signed-off-by: Vignesh R <vigneshr@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi')
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 66 |
1 files changed, 59 insertions, 7 deletions
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index 804914ebfd9d..23a06148b8ae 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c | |||
@@ -33,6 +33,7 @@ | |||
33 | #include <linux/pinctrl/consumer.h> | 33 | #include <linux/pinctrl/consumer.h> |
34 | #include <linux/mfd/syscon.h> | 34 | #include <linux/mfd/syscon.h> |
35 | #include <linux/regmap.h> | 35 | #include <linux/regmap.h> |
36 | #include <linux/sizes.h> | ||
36 | 37 | ||
37 | #include <linux/spi/spi.h> | 38 | #include <linux/spi/spi.h> |
38 | 39 | ||
@@ -57,6 +58,8 @@ struct ti_qspi { | |||
57 | struct ti_qspi_regs ctx_reg; | 58 | struct ti_qspi_regs ctx_reg; |
58 | 59 | ||
59 | dma_addr_t mmap_phys_base; | 60 | dma_addr_t mmap_phys_base; |
61 | dma_addr_t rx_bb_dma_addr; | ||
62 | void *rx_bb_addr; | ||
60 | struct dma_chan *rx_chan; | 63 | struct dma_chan *rx_chan; |
61 | 64 | ||
62 | u32 spi_max_frequency; | 65 | u32 spi_max_frequency; |
@@ -126,6 +129,8 @@ struct ti_qspi { | |||
126 | #define QSPI_SETUP_ADDR_SHIFT 8 | 129 | #define QSPI_SETUP_ADDR_SHIFT 8 |
127 | #define QSPI_SETUP_DUMMY_SHIFT 10 | 130 | #define QSPI_SETUP_DUMMY_SHIFT 10 |
128 | 131 | ||
132 | #define QSPI_DMA_BUFFER_SIZE SZ_64K | ||
133 | |||
129 | static inline unsigned long ti_qspi_read(struct ti_qspi *qspi, | 134 | static inline unsigned long ti_qspi_read(struct ti_qspi *qspi, |
130 | unsigned long reg) | 135 | unsigned long reg) |
131 | { | 136 | { |
@@ -429,6 +434,35 @@ static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, | |||
429 | return 0; | 434 | return 0; |
430 | } | 435 | } |
431 | 436 | ||
437 | static int ti_qspi_dma_bounce_buffer(struct ti_qspi *qspi, | ||
438 | struct spi_flash_read_message *msg) | ||
439 | { | ||
440 | size_t readsize = msg->len; | ||
441 | void *to = msg->buf; | ||
442 | dma_addr_t dma_src = qspi->mmap_phys_base + msg->from; | ||
443 | int ret = 0; | ||
444 | |||
445 | /* | ||
446 | * Use bounce buffer as FS like jffs2, ubifs may pass | ||
447 | * buffers that does not belong to kernel lowmem region. | ||
448 | */ | ||
449 | while (readsize != 0) { | ||
450 | size_t xfer_len = min_t(size_t, QSPI_DMA_BUFFER_SIZE, | ||
451 | readsize); | ||
452 | |||
453 | ret = ti_qspi_dma_xfer(qspi, qspi->rx_bb_dma_addr, | ||
454 | dma_src, xfer_len); | ||
455 | if (ret != 0) | ||
456 | return ret; | ||
457 | memcpy(to, qspi->rx_bb_addr, xfer_len); | ||
458 | readsize -= xfer_len; | ||
459 | dma_src += xfer_len; | ||
460 | to += xfer_len; | ||
461 | } | ||
462 | |||
463 | return ret; | ||
464 | } | ||
465 | |||
432 | static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg, | 466 | static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg, |
433 | loff_t from) | 467 | loff_t from) |
434 | { | 468 | { |
@@ -496,6 +530,12 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, | |||
496 | QSPI_SPI_SETUP_REG(spi->chip_select)); | 530 | QSPI_SPI_SETUP_REG(spi->chip_select)); |
497 | } | 531 | } |
498 | 532 | ||
533 | static bool ti_qspi_spi_flash_can_dma(struct spi_device *spi, | ||
534 | struct spi_flash_read_message *msg) | ||
535 | { | ||
536 | return virt_addr_valid(msg->buf); | ||
537 | } | ||
538 | |||
499 | static int ti_qspi_spi_flash_read(struct spi_device *spi, | 539 | static int ti_qspi_spi_flash_read(struct spi_device *spi, |
500 | struct spi_flash_read_message *msg) | 540 | struct spi_flash_read_message *msg) |
501 | { | 541 | { |
@@ -509,15 +549,12 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi, | |||
509 | ti_qspi_setup_mmap_read(spi, msg); | 549 | ti_qspi_setup_mmap_read(spi, msg); |
510 | 550 | ||
511 | if (qspi->rx_chan) { | 551 | if (qspi->rx_chan) { |
512 | if (msg->cur_msg_mapped) { | 552 | if (msg->cur_msg_mapped) |
513 | ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from); | 553 | ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from); |
514 | if (ret) | 554 | else |
515 | goto err_unlock; | 555 | ret = ti_qspi_dma_bounce_buffer(qspi, msg); |
516 | } else { | 556 | if (ret) |
517 | dev_err(qspi->dev, "Invalid address for DMA\n"); | ||
518 | ret = -EIO; | ||
519 | goto err_unlock; | 557 | goto err_unlock; |
520 | } | ||
521 | } else { | 558 | } else { |
522 | memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); | 559 | memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); |
523 | } | 560 | } |
@@ -718,6 +755,17 @@ static int ti_qspi_probe(struct platform_device *pdev) | |||
718 | ret = 0; | 755 | ret = 0; |
719 | goto no_dma; | 756 | goto no_dma; |
720 | } | 757 | } |
758 | qspi->rx_bb_addr = dma_alloc_coherent(qspi->dev, | ||
759 | QSPI_DMA_BUFFER_SIZE, | ||
760 | &qspi->rx_bb_dma_addr, | ||
761 | GFP_KERNEL | GFP_DMA); | ||
762 | if (!qspi->rx_bb_addr) { | ||
763 | dev_err(qspi->dev, | ||
764 | "dma_alloc_coherent failed, using PIO mode\n"); | ||
765 | dma_release_channel(qspi->rx_chan); | ||
766 | goto no_dma; | ||
767 | } | ||
768 | master->spi_flash_can_dma = ti_qspi_spi_flash_can_dma; | ||
721 | master->dma_rx = qspi->rx_chan; | 769 | master->dma_rx = qspi->rx_chan; |
722 | init_completion(&qspi->transfer_complete); | 770 | init_completion(&qspi->transfer_complete); |
723 | if (res_mmap) | 771 | if (res_mmap) |
@@ -757,6 +805,10 @@ static int ti_qspi_remove(struct platform_device *pdev) | |||
757 | pm_runtime_put_sync(&pdev->dev); | 805 | pm_runtime_put_sync(&pdev->dev); |
758 | pm_runtime_disable(&pdev->dev); | 806 | pm_runtime_disable(&pdev->dev); |
759 | 807 | ||
808 | if (qspi->rx_bb_addr) | ||
809 | dma_free_coherent(qspi->dev, QSPI_DMA_BUFFER_SIZE, | ||
810 | qspi->rx_bb_addr, | ||
811 | qspi->rx_bb_dma_addr); | ||
760 | if (qspi->rx_chan) | 812 | if (qspi->rx_chan) |
761 | dma_release_channel(qspi->rx_chan); | 813 | dma_release_channel(qspi->rx_chan); |
762 | 814 | ||