diff options
author | Geert Uytterhoeven <geert+renesas@glider.be> | 2014-08-06 08:59:03 -0400 |
---|---|---|
committer | Mark Brown <broonie@linaro.org> | 2014-08-16 18:13:14 -0400 |
commit | 3e81b59208fa4697c6c3b6eefb92892b47f8b1e7 (patch) | |
tree | 9553dd8862a015ff91b0d26e24bc3be042552b9b | |
parent | 7d1311b93e58ed55f3a31cc8f94c4b8fe988a2b9 (diff) |
spi: sh-msiof: Fix leaking of unused DMA descriptors
If dmaengine_prep_slave_sg() or dmaengine_submit() fail, we may leak
unused DMA descriptors.
As per Documentation/dmaengine.txt, once a DMA descriptor has been
obtained, it must be submitted. Hence:
- First prepare and submit all DMA descriptors,
- Prepare the SPI controller for DMA,
- Start DMA by calling dma_async_issue_pending(),
- Make sure to call dmaengine_terminate_all() on all descriptors that
haven't completed.
Reported-by: Laurent Pinchart <laurent.pinchart@ideasonboard.com>
Signed-off-by: Geert Uytterhoeven <geert+renesas@glider.be>
Signed-off-by: Mark Brown <broonie@linaro.org>
-rw-r--r-- | drivers/spi/spi-sh-msiof.c | 71 |
1 files changed, 38 insertions, 33 deletions
diff --git a/drivers/spi/spi-sh-msiof.c b/drivers/spi/spi-sh-msiof.c index 2a4354dcd661..887c2084130f 100644 --- a/drivers/spi/spi-sh-msiof.c +++ b/drivers/spi/spi-sh-msiof.c | |||
@@ -636,48 +636,38 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, | |||
636 | dma_cookie_t cookie; | 636 | dma_cookie_t cookie; |
637 | int ret; | 637 | int ret; |
638 | 638 | ||
639 | if (tx) { | 639 | /* First prepare and submit the DMA request(s), as this may fail */ |
640 | ier_bits |= IER_TDREQE | IER_TDMAE; | ||
641 | dma_sync_single_for_device(p->master->dma_tx->device->dev, | ||
642 | p->tx_dma_addr, len, DMA_TO_DEVICE); | ||
643 | desc_tx = dmaengine_prep_slave_single(p->master->dma_tx, | ||
644 | p->tx_dma_addr, len, DMA_TO_DEVICE, | ||
645 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
646 | if (!desc_tx) | ||
647 | return -EAGAIN; | ||
648 | } | ||
649 | |||
650 | if (rx) { | 640 | if (rx) { |
651 | ier_bits |= IER_RDREQE | IER_RDMAE; | 641 | ier_bits |= IER_RDREQE | IER_RDMAE; |
652 | desc_rx = dmaengine_prep_slave_single(p->master->dma_rx, | 642 | desc_rx = dmaengine_prep_slave_single(p->master->dma_rx, |
653 | p->rx_dma_addr, len, DMA_FROM_DEVICE, | 643 | p->rx_dma_addr, len, DMA_FROM_DEVICE, |
654 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | 644 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); |
655 | if (!desc_rx) | 645 | if (!desc_rx) { |
656 | return -EAGAIN; | 646 | ret = -EAGAIN; |
657 | } | 647 | goto no_dma_rx; |
658 | 648 | } | |
659 | /* 1 stage FIFO watermarks for DMA */ | ||
660 | sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1); | ||
661 | |||
662 | /* setup msiof transfer mode registers (32-bit words) */ | ||
663 | sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4); | ||
664 | |||
665 | sh_msiof_write(p, IER, ier_bits); | ||
666 | |||
667 | reinit_completion(&p->done); | ||
668 | 649 | ||
669 | if (rx) { | ||
670 | desc_rx->callback = sh_msiof_dma_complete; | 650 | desc_rx->callback = sh_msiof_dma_complete; |
671 | desc_rx->callback_param = p; | 651 | desc_rx->callback_param = p; |
672 | cookie = dmaengine_submit(desc_rx); | 652 | cookie = dmaengine_submit(desc_rx); |
673 | if (dma_submit_error(cookie)) { | 653 | if (dma_submit_error(cookie)) { |
674 | ret = cookie; | 654 | ret = cookie; |
675 | goto stop_ier; | 655 | goto no_dma_rx; |
676 | } | 656 | } |
677 | dma_async_issue_pending(p->master->dma_rx); | ||
678 | } | 657 | } |
679 | 658 | ||
680 | if (tx) { | 659 | if (tx) { |
660 | ier_bits |= IER_TDREQE | IER_TDMAE; | ||
661 | dma_sync_single_for_device(p->master->dma_tx->device->dev, | ||
662 | p->tx_dma_addr, len, DMA_TO_DEVICE); | ||
663 | desc_tx = dmaengine_prep_slave_single(p->master->dma_tx, | ||
664 | p->tx_dma_addr, len, DMA_TO_DEVICE, | ||
665 | DMA_PREP_INTERRUPT | DMA_CTRL_ACK); | ||
666 | if (!desc_tx) { | ||
667 | ret = -EAGAIN; | ||
668 | goto no_dma_tx; | ||
669 | } | ||
670 | |||
681 | if (rx) { | 671 | if (rx) { |
682 | /* No callback */ | 672 | /* No callback */ |
683 | desc_tx->callback = NULL; | 673 | desc_tx->callback = NULL; |
@@ -688,15 +678,30 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, | |||
688 | cookie = dmaengine_submit(desc_tx); | 678 | cookie = dmaengine_submit(desc_tx); |
689 | if (dma_submit_error(cookie)) { | 679 | if (dma_submit_error(cookie)) { |
690 | ret = cookie; | 680 | ret = cookie; |
691 | goto stop_rx; | 681 | goto no_dma_tx; |
692 | } | 682 | } |
693 | dma_async_issue_pending(p->master->dma_tx); | ||
694 | } | 683 | } |
695 | 684 | ||
685 | /* 1 stage FIFO watermarks for DMA */ | ||
686 | sh_msiof_write(p, FCTR, FCTR_TFWM_1 | FCTR_RFWM_1); | ||
687 | |||
688 | /* setup msiof transfer mode registers (32-bit words) */ | ||
689 | sh_msiof_spi_set_mode_regs(p, tx, rx, 32, len / 4); | ||
690 | |||
691 | sh_msiof_write(p, IER, ier_bits); | ||
692 | |||
693 | reinit_completion(&p->done); | ||
694 | |||
695 | /* Now start DMA */ | ||
696 | if (tx) | ||
697 | dma_async_issue_pending(p->master->dma_rx); | ||
698 | if (rx) | ||
699 | dma_async_issue_pending(p->master->dma_tx); | ||
700 | |||
696 | ret = sh_msiof_spi_start(p, rx); | 701 | ret = sh_msiof_spi_start(p, rx); |
697 | if (ret) { | 702 | if (ret) { |
698 | dev_err(&p->pdev->dev, "failed to start hardware\n"); | 703 | dev_err(&p->pdev->dev, "failed to start hardware\n"); |
699 | goto stop_tx; | 704 | goto stop_dma; |
700 | } | 705 | } |
701 | 706 | ||
702 | /* wait for tx fifo to be emptied / rx fifo to be filled */ | 707 | /* wait for tx fifo to be emptied / rx fifo to be filled */ |
@@ -726,14 +731,14 @@ static int sh_msiof_dma_once(struct sh_msiof_spi_priv *p, const void *tx, | |||
726 | stop_reset: | 731 | stop_reset: |
727 | sh_msiof_reset_str(p); | 732 | sh_msiof_reset_str(p); |
728 | sh_msiof_spi_stop(p, rx); | 733 | sh_msiof_spi_stop(p, rx); |
729 | stop_tx: | 734 | stop_dma: |
730 | if (tx) | 735 | if (tx) |
731 | dmaengine_terminate_all(p->master->dma_tx); | 736 | dmaengine_terminate_all(p->master->dma_tx); |
732 | stop_rx: | 737 | no_dma_tx: |
733 | if (rx) | 738 | if (rx) |
734 | dmaengine_terminate_all(p->master->dma_rx); | 739 | dmaengine_terminate_all(p->master->dma_rx); |
735 | stop_ier: | ||
736 | sh_msiof_write(p, IER, 0); | 740 | sh_msiof_write(p, IER, 0); |
741 | no_dma_rx: | ||
737 | return ret; | 742 | return ret; |
738 | } | 743 | } |
739 | 744 | ||