diff options
author | Vignesh R <vigneshr@ti.com> | 2016-08-17 05:52:37 -0400 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2016-08-17 07:24:28 -0400 |
commit | 5720ec0a6d2605930934f3f154b048e8be3d8a40 (patch) | |
tree | b1f3ac74bd8391060ddc2cfd599fd68e252484b3 /drivers/spi/spi-ti-qspi.c | |
parent | 29b4817d4018df78086157ea3a55c1d9424a7cfc (diff) |
spi: spi-ti-qspi: Add DMA support for QSPI mmap read
Use mem-to-mem DMA to read from flash when reading in mmap mode. This
gives improved read performance and reduces CPU load.
With this patch the raw-read throughput is ~16MB/s on DRA74 EVM. And CPU
load is <20%. UBIFS read throughput ~13 MB/s.
Signed-off-by: Vignesh R <vigneshr@ti.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
Diffstat (limited to 'drivers/spi/spi-ti-qspi.c')
-rw-r--r-- | drivers/spi/spi-ti-qspi.c | 139 |
1 files changed, 122 insertions, 17 deletions
diff --git a/drivers/spi/spi-ti-qspi.c b/drivers/spi/spi-ti-qspi.c index ac0b072815a3..caeac66a3977 100644 --- a/drivers/spi/spi-ti-qspi.c +++ b/drivers/spi/spi-ti-qspi.c | |||
@@ -41,6 +41,8 @@ struct ti_qspi_regs { | |||
41 | }; | 41 | }; |
42 | 42 | ||
43 | struct ti_qspi { | 43 | struct ti_qspi { |
44 | struct completion transfer_complete; | ||
45 | |||
44 | /* list synchronization */ | 46 | /* list synchronization */ |
45 | struct mutex list_lock; | 47 | struct mutex list_lock; |
46 | 48 | ||
@@ -54,6 +56,9 @@ struct ti_qspi { | |||
54 | 56 | ||
55 | struct ti_qspi_regs ctx_reg; | 57 | struct ti_qspi_regs ctx_reg; |
56 | 58 | ||
59 | dma_addr_t mmap_phys_base; | ||
60 | struct dma_chan *rx_chan; | ||
61 | |||
57 | u32 spi_max_frequency; | 62 | u32 spi_max_frequency; |
58 | u32 cmd; | 63 | u32 cmd; |
59 | u32 dc; | 64 | u32 dc; |
@@ -379,6 +384,72 @@ static int qspi_transfer_msg(struct ti_qspi *qspi, struct spi_transfer *t, | |||
379 | return 0; | 384 | return 0; |
380 | } | 385 | } |
381 | 386 | ||
387 | static void ti_qspi_dma_callback(void *param) | ||
388 | { | ||
389 | struct ti_qspi *qspi = param; | ||
390 | |||
391 | complete(&qspi->transfer_complete); | ||
392 | } | ||
393 | |||
394 | static int ti_qspi_dma_xfer(struct ti_qspi *qspi, dma_addr_t dma_dst, | ||
395 | dma_addr_t dma_src, size_t len) | ||
396 | { | ||
397 | struct dma_chan *chan = qspi->rx_chan; | ||
398 | struct dma_device *dma_dev = chan->device; | ||
399 | dma_cookie_t cookie; | ||
400 | enum dma_ctrl_flags flags = DMA_CTRL_ACK | DMA_PREP_INTERRUPT; | ||
401 | struct dma_async_tx_descriptor *tx; | ||
402 | int ret; | ||
403 | |||
404 | tx = dma_dev->device_prep_dma_memcpy(chan, dma_dst, dma_src, | ||
405 | len, flags); | ||
406 | if (!tx) { | ||
407 | dev_err(qspi->dev, "device_prep_dma_memcpy error\n"); | ||
408 | return -EIO; | ||
409 | } | ||
410 | |||
411 | tx->callback = ti_qspi_dma_callback; | ||
412 | tx->callback_param = qspi; | ||
413 | cookie = tx->tx_submit(tx); | ||
414 | |||
415 | ret = dma_submit_error(cookie); | ||
416 | if (ret) { | ||
417 | dev_err(qspi->dev, "dma_submit_error %d\n", cookie); | ||
418 | return -EIO; | ||
419 | } | ||
420 | |||
421 | dma_async_issue_pending(chan); | ||
422 | ret = wait_for_completion_timeout(&qspi->transfer_complete, | ||
423 | msecs_to_jiffies(len)); | ||
424 | if (ret <= 0) { | ||
425 | dmaengine_terminate_sync(chan); | ||
426 | dev_err(qspi->dev, "DMA wait_for_completion_timeout\n"); | ||
427 | return -ETIMEDOUT; | ||
428 | } | ||
429 | |||
430 | return 0; | ||
431 | } | ||
432 | |||
433 | static int ti_qspi_dma_xfer_sg(struct ti_qspi *qspi, struct sg_table rx_sg, | ||
434 | loff_t from) | ||
435 | { | ||
436 | struct scatterlist *sg; | ||
437 | dma_addr_t dma_src = qspi->mmap_phys_base + from; | ||
438 | dma_addr_t dma_dst; | ||
439 | int i, len, ret; | ||
440 | |||
441 | for_each_sg(rx_sg.sgl, sg, rx_sg.nents, i) { | ||
442 | dma_dst = sg_dma_address(sg); | ||
443 | len = sg_dma_len(sg); | ||
444 | ret = ti_qspi_dma_xfer(qspi, dma_dst, dma_src, len); | ||
445 | if (ret) | ||
446 | return ret; | ||
447 | dma_src += len; | ||
448 | } | ||
449 | |||
450 | return 0; | ||
451 | } | ||
452 | |||
382 | static void ti_qspi_enable_memory_map(struct spi_device *spi) | 453 | static void ti_qspi_enable_memory_map(struct spi_device *spi) |
383 | { | 454 | { |
384 | struct ti_qspi *qspi = spi_master_get_devdata(spi->master); | 455 | struct ti_qspi *qspi = spi_master_get_devdata(spi->master); |
@@ -426,7 +497,7 @@ static void ti_qspi_setup_mmap_read(struct spi_device *spi, | |||
426 | QSPI_SPI_SETUP_REG(spi->chip_select)); | 497 | QSPI_SPI_SETUP_REG(spi->chip_select)); |
427 | } | 498 | } |
428 | 499 | ||
429 | static int ti_qspi_spi_flash_read(struct spi_device *spi, | 500 | static int ti_qspi_spi_flash_read(struct spi_device *spi, |
430 | struct spi_flash_read_message *msg) | 501 | struct spi_flash_read_message *msg) |
431 | { | 502 | { |
432 | struct ti_qspi *qspi = spi_master_get_devdata(spi->master); | 503 | struct ti_qspi *qspi = spi_master_get_devdata(spi->master); |
@@ -437,9 +508,23 @@ static int ti_qspi_spi_flash_read(struct spi_device *spi, | |||
437 | if (!qspi->mmap_enabled) | 508 | if (!qspi->mmap_enabled) |
438 | ti_qspi_enable_memory_map(spi); | 509 | ti_qspi_enable_memory_map(spi); |
439 | ti_qspi_setup_mmap_read(spi, msg); | 510 | ti_qspi_setup_mmap_read(spi, msg); |
440 | memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); | 511 | |
512 | if (qspi->rx_chan) { | ||
513 | if (msg->cur_msg_mapped) { | ||
514 | ret = ti_qspi_dma_xfer_sg(qspi, msg->rx_sg, msg->from); | ||
515 | if (ret) | ||
516 | goto err_unlock; | ||
517 | } else { | ||
518 | dev_err(qspi->dev, "Invalid address for DMA\n"); | ||
519 | ret = -EIO; | ||
520 | goto err_unlock; | ||
521 | } | ||
522 | } else { | ||
523 | memcpy_fromio(msg->buf, qspi->mmap_base + msg->from, msg->len); | ||
524 | } | ||
441 | msg->retlen = msg->len; | 525 | msg->retlen = msg->len; |
442 | 526 | ||
527 | err_unlock: | ||
443 | mutex_unlock(&qspi->list_lock); | 528 | mutex_unlock(&qspi->list_lock); |
444 | 529 | ||
445 | return ret; | 530 | return ret; |
@@ -536,6 +621,7 @@ static int ti_qspi_probe(struct platform_device *pdev) | |||
536 | struct device_node *np = pdev->dev.of_node; | 621 | struct device_node *np = pdev->dev.of_node; |
537 | u32 max_freq; | 622 | u32 max_freq; |
538 | int ret = 0, num_cs, irq; | 623 | int ret = 0, num_cs, irq; |
624 | dma_cap_mask_t mask; | ||
539 | 625 | ||
540 | master = spi_alloc_master(&pdev->dev, sizeof(*qspi)); | 626 | master = spi_alloc_master(&pdev->dev, sizeof(*qspi)); |
541 | if (!master) | 627 | if (!master) |
@@ -550,6 +636,7 @@ static int ti_qspi_probe(struct platform_device *pdev) | |||
550 | master->dev.of_node = pdev->dev.of_node; | 636 | master->dev.of_node = pdev->dev.of_node; |
551 | master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | | 637 | master->bits_per_word_mask = SPI_BPW_MASK(32) | SPI_BPW_MASK(16) | |
552 | SPI_BPW_MASK(8); | 638 | SPI_BPW_MASK(8); |
639 | master->spi_flash_read = ti_qspi_spi_flash_read; | ||
553 | 640 | ||
554 | if (!of_property_read_u32(np, "num-cs", &num_cs)) | 641 | if (!of_property_read_u32(np, "num-cs", &num_cs)) |
555 | master->num_chipselect = num_cs; | 642 | master->num_chipselect = num_cs; |
@@ -592,17 +679,6 @@ static int ti_qspi_probe(struct platform_device *pdev) | |||
592 | goto free_master; | 679 | goto free_master; |
593 | } | 680 | } |
594 | 681 | ||
595 | if (res_mmap) { | ||
596 | qspi->mmap_base = devm_ioremap_resource(&pdev->dev, | ||
597 | res_mmap); | ||
598 | master->spi_flash_read = ti_qspi_spi_flash_read; | ||
599 | if (IS_ERR(qspi->mmap_base)) { | ||
600 | dev_err(&pdev->dev, | ||
601 | "falling back to PIO mode\n"); | ||
602 | master->spi_flash_read = NULL; | ||
603 | } | ||
604 | } | ||
605 | qspi->mmap_enabled = false; | ||
606 | 682 | ||
607 | if (of_property_read_bool(np, "syscon-chipselects")) { | 683 | if (of_property_read_bool(np, "syscon-chipselects")) { |
608 | qspi->ctrl_base = | 684 | qspi->ctrl_base = |
@@ -633,11 +709,37 @@ static int ti_qspi_probe(struct platform_device *pdev) | |||
633 | if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) | 709 | if (!of_property_read_u32(np, "spi-max-frequency", &max_freq)) |
634 | qspi->spi_max_frequency = max_freq; | 710 | qspi->spi_max_frequency = max_freq; |
635 | 711 | ||
636 | ret = devm_spi_register_master(&pdev->dev, master); | 712 | dma_cap_zero(mask); |
637 | if (ret) | 713 | dma_cap_set(DMA_MEMCPY, mask); |
638 | goto free_master; | ||
639 | 714 | ||
640 | return 0; | 715 | qspi->rx_chan = dma_request_chan_by_mask(&mask); |
716 | if (!qspi->rx_chan) { | ||
717 | dev_err(qspi->dev, | ||
718 | "No Rx DMA available, trying mmap mode\n"); | ||
719 | ret = 0; | ||
720 | goto no_dma; | ||
721 | } | ||
722 | master->dma_rx = qspi->rx_chan; | ||
723 | init_completion(&qspi->transfer_complete); | ||
724 | if (res_mmap) | ||
725 | qspi->mmap_phys_base = (dma_addr_t)res_mmap->start; | ||
726 | |||
727 | no_dma: | ||
728 | if (!qspi->rx_chan && res_mmap) { | ||
729 | qspi->mmap_base = devm_ioremap_resource(&pdev->dev, res_mmap); | ||
730 | if (IS_ERR(qspi->mmap_base)) { | ||
731 | dev_info(&pdev->dev, | ||
732 | "mmap failed with error %ld using PIO mode\n", | ||
733 | PTR_ERR(qspi->mmap_base)); | ||
734 | qspi->mmap_base = NULL; | ||
735 | master->spi_flash_read = NULL; | ||
736 | } | ||
737 | } | ||
738 | qspi->mmap_enabled = false; | ||
739 | |||
740 | ret = devm_spi_register_master(&pdev->dev, master); | ||
741 | if (!ret) | ||
742 | return 0; | ||
641 | 743 | ||
642 | free_master: | 744 | free_master: |
643 | spi_master_put(master); | 745 | spi_master_put(master); |
@@ -656,6 +758,9 @@ static int ti_qspi_remove(struct platform_device *pdev) | |||
656 | pm_runtime_put_sync(&pdev->dev); | 758 | pm_runtime_put_sync(&pdev->dev); |
657 | pm_runtime_disable(&pdev->dev); | 759 | pm_runtime_disable(&pdev->dev); |
658 | 760 | ||
761 | if (qspi->rx_chan) | ||
762 | dma_release_channel(qspi->rx_chan); | ||
763 | |||
659 | return 0; | 764 | return 0; |
660 | } | 765 | } |
661 | 766 | ||