dmaengine/dw_dmac: Divide one sg to many desc, if sg len is greater than DWC_MAX_COUNT
Viresh Kumar [Mon, 18 Apr 2011 09:24:56 +0000 (14:24 +0530)]
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>

drivers/dma/dw_dmac.c

index f209ff8..a9755e3 100644 (file)
@@ -693,9 +693,15 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                reg = dws->tx_reg;
                for_each_sg(sgl, sg, sg_len, i) {
                        struct dw_desc  *desc;
-                       u32             len;
-                       u32             mem;
+                       u32             len, dlen, mem;
 
+                       mem = sg_phys(sg);
+                       len = sg_dma_len(sg);
+                       mem_width = 2;
+                       if (unlikely(mem & 3 || len & 3))
+                               mem_width = 0;
+
+slave_sg_todev_fill_desc:
                        desc = dwc_desc_get(dwc);
                        if (!desc) {
                                dev_err(chan2dev(chan),
@@ -703,16 +709,19 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                                goto err_desc_get;
                        }
 
-                       mem = sg_phys(sg);
-                       len = sg_dma_len(sg);
-                       mem_width = 2;
-                       if (unlikely(mem & 3 || len & 3))
-                               mem_width = 0;
-
                        desc->lli.sar = mem;
                        desc->lli.dar = reg;
                        desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width);
-                       desc->lli.ctlhi = len >> mem_width;
+                       if ((len >> mem_width) > DWC_MAX_COUNT) {
+                               dlen = DWC_MAX_COUNT << mem_width;
+                               mem += dlen;
+                               len -= dlen;
+                       } else {
+                               dlen = len;
+                               len = 0;
+                       }
+
+                       desc->lli.ctlhi = dlen >> mem_width;
 
                        if (!first) {
                                first = desc;
@@ -726,7 +735,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                                                &first->tx_list);
                        }
                        prev = desc;
-                       total_len += len;
+                       total_len += dlen;
+
+                       if (len)
+                               goto slave_sg_todev_fill_desc;
                }
                break;
        case DMA_FROM_DEVICE:
@@ -739,15 +751,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                reg = dws->rx_reg;
                for_each_sg(sgl, sg, sg_len, i) {
                        struct dw_desc  *desc;
-                       u32             len;
-                       u32             mem;
-
-                       desc = dwc_desc_get(dwc);
-                       if (!desc) {
-                               dev_err(chan2dev(chan),
-                                       "not enough descriptors available\n");
-                               goto err_desc_get;
-                       }
+                       u32             len, dlen, mem;
 
                        mem = sg_phys(sg);
                        len = sg_dma_len(sg);
@@ -755,10 +759,26 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                        if (unlikely(mem & 3 || len & 3))
                                mem_width = 0;
 
+slave_sg_fromdev_fill_desc:
+                       desc = dwc_desc_get(dwc);
+                       if (!desc) {
+                               dev_err(chan2dev(chan),
+                                               "not enough descriptors available\n");
+                               goto err_desc_get;
+                       }
+
                        desc->lli.sar = reg;
                        desc->lli.dar = mem;
                        desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width);
-                       desc->lli.ctlhi = len >> reg_width;
+                       if ((len >> reg_width) > DWC_MAX_COUNT) {
+                               dlen = DWC_MAX_COUNT << reg_width;
+                               mem += dlen;
+                               len -= dlen;
+                       } else {
+                               dlen = len;
+                               len = 0;
+                       }
+                       desc->lli.ctlhi = dlen >> reg_width;
 
                        if (!first) {
                                first = desc;
@@ -772,7 +792,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
                                                &first->tx_list);
                        }
                        prev = desc;
-                       total_len += len;
+                       total_len += dlen;
+
+                       if (len)
+                               goto slave_sg_fromdev_fill_desc;
                }
                break;
        default: