diff options
author | Viresh Kumar <viresh.kumar@st.com> | 2011-04-18 05:24:56 -0400 |
---|---|---|
committer | Vinod Koul <vinod.koul@intel.com> | 2011-05-13 10:10:00 -0400 |
commit | 69dc14b51c1aad9d82afd8f96bf4e4835089bffc (patch) | |
tree | 494e852ee6531c5e91720246baf53623e45e4112 /drivers/dma/dw_dmac.c | |
parent | abf53902dcc6d44d2e06b09817fa67857aa686fe (diff) |
dmaengine/dw_dmac: Divide one sg to many desc, if sg len is greater than DWC_MAX_COUNT
If len passed in sg for slave_sg transfers is greater than DWC_MAX_COUNT, then
driver programmes controller incorrectly. This patch adds code to handle this
situation by allocation more than one desc for same sg.
Signed-off-by: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/dw_dmac.c')
-rw-r--r-- | drivers/dma/dw_dmac.c | 65 |
1 files changed, 44 insertions, 21 deletions
diff --git a/drivers/dma/dw_dmac.c b/drivers/dma/dw_dmac.c index f209ff8835e8..a9755e3dd603 100644 --- a/drivers/dma/dw_dmac.c +++ b/drivers/dma/dw_dmac.c | |||
@@ -693,9 +693,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
693 | reg = dws->tx_reg; | 693 | reg = dws->tx_reg; |
694 | for_each_sg(sgl, sg, sg_len, i) { | 694 | for_each_sg(sgl, sg, sg_len, i) { |
695 | struct dw_desc *desc; | 695 | struct dw_desc *desc; |
696 | u32 len; | 696 | u32 len, dlen, mem; |
697 | u32 mem; | ||
698 | 697 | ||
698 | mem = sg_phys(sg); | ||
699 | len = sg_dma_len(sg); | ||
700 | mem_width = 2; | ||
701 | if (unlikely(mem & 3 || len & 3)) | ||
702 | mem_width = 0; | ||
703 | |||
704 | slave_sg_todev_fill_desc: | ||
699 | desc = dwc_desc_get(dwc); | 705 | desc = dwc_desc_get(dwc); |
700 | if (!desc) { | 706 | if (!desc) { |
701 | dev_err(chan2dev(chan), | 707 | dev_err(chan2dev(chan), |
@@ -703,16 +709,19 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
703 | goto err_desc_get; | 709 | goto err_desc_get; |
704 | } | 710 | } |
705 | 711 | ||
706 | mem = sg_phys(sg); | ||
707 | len = sg_dma_len(sg); | ||
708 | mem_width = 2; | ||
709 | if (unlikely(mem & 3 || len & 3)) | ||
710 | mem_width = 0; | ||
711 | |||
712 | desc->lli.sar = mem; | 712 | desc->lli.sar = mem; |
713 | desc->lli.dar = reg; | 713 | desc->lli.dar = reg; |
714 | desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); | 714 | desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); |
715 | desc->lli.ctlhi = len >> mem_width; | 715 | if ((len >> mem_width) > DWC_MAX_COUNT) { |
716 | dlen = DWC_MAX_COUNT << mem_width; | ||
717 | mem += dlen; | ||
718 | len -= dlen; | ||
719 | } else { | ||
720 | dlen = len; | ||
721 | len = 0; | ||
722 | } | ||
723 | |||
724 | desc->lli.ctlhi = dlen >> mem_width; | ||
716 | 725 | ||
717 | if (!first) { | 726 | if (!first) { |
718 | first = desc; | 727 | first = desc; |
@@ -726,7 +735,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
726 | &first->tx_list); | 735 | &first->tx_list); |
727 | } | 736 | } |
728 | prev = desc; | 737 | prev = desc; |
729 | total_len += len; | 738 | total_len += dlen; |
739 | |||
740 | if (len) | ||
741 | goto slave_sg_todev_fill_desc; | ||
730 | } | 742 | } |
731 | break; | 743 | break; |
732 | case DMA_FROM_DEVICE: | 744 | case DMA_FROM_DEVICE: |
@@ -739,15 +751,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
739 | reg = dws->rx_reg; | 751 | reg = dws->rx_reg; |
740 | for_each_sg(sgl, sg, sg_len, i) { | 752 | for_each_sg(sgl, sg, sg_len, i) { |
741 | struct dw_desc *desc; | 753 | struct dw_desc *desc; |
742 | u32 len; | 754 | u32 len, dlen, mem; |
743 | u32 mem; | ||
744 | |||
745 | desc = dwc_desc_get(dwc); | ||
746 | if (!desc) { | ||
747 | dev_err(chan2dev(chan), | ||
748 | "not enough descriptors available\n"); | ||
749 | goto err_desc_get; | ||
750 | } | ||
751 | 755 | ||
752 | mem = sg_phys(sg); | 756 | mem = sg_phys(sg); |
753 | len = sg_dma_len(sg); | 757 | len = sg_dma_len(sg); |
@@ -755,10 +759,26 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
755 | if (unlikely(mem & 3 || len & 3)) | 759 | if (unlikely(mem & 3 || len & 3)) |
756 | mem_width = 0; | 760 | mem_width = 0; |
757 | 761 | ||
762 | slave_sg_fromdev_fill_desc: | ||
763 | desc = dwc_desc_get(dwc); | ||
764 | if (!desc) { | ||
765 | dev_err(chan2dev(chan), | ||
766 | "not enough descriptors available\n"); | ||
767 | goto err_desc_get; | ||
768 | } | ||
769 | |||
758 | desc->lli.sar = reg; | 770 | desc->lli.sar = reg; |
759 | desc->lli.dar = mem; | 771 | desc->lli.dar = mem; |
760 | desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); | 772 | desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); |
761 | desc->lli.ctlhi = len >> reg_width; | 773 | if ((len >> reg_width) > DWC_MAX_COUNT) { |
774 | dlen = DWC_MAX_COUNT << reg_width; | ||
775 | mem += dlen; | ||
776 | len -= dlen; | ||
777 | } else { | ||
778 | dlen = len; | ||
779 | len = 0; | ||
780 | } | ||
781 | desc->lli.ctlhi = dlen >> reg_width; | ||
762 | 782 | ||
763 | if (!first) { | 783 | if (!first) { |
764 | first = desc; | 784 | first = desc; |
@@ -772,7 +792,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, | |||
772 | &first->tx_list); | 792 | &first->tx_list); |
773 | } | 793 | } |
774 | prev = desc; | 794 | prev = desc; |
775 | total_len += len; | 795 | total_len += dlen; |
796 | |||
797 | if (len) | ||
798 | goto slave_sg_fromdev_fill_desc; | ||
776 | } | 799 | } |
777 | break; | 800 | break; |
778 | default: | 801 | default: |