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>
This commit is contained in:
Viresh Kumar 2011-04-18 14:54:56 +05:30 committed by Vinod Koul
parent abf53902dc
commit 69dc14b51c

View File

@ -693,15 +693,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
reg = dws->tx_reg; reg = dws->tx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc; struct dw_desc *desc;
u32 len; u32 len, dlen, mem;
u32 mem;
desc = dwc_desc_get(dwc);
if (!desc) {
dev_err(chan2dev(chan),
"not enough descriptors available\n");
goto err_desc_get;
}
mem = sg_phys(sg); mem = sg_phys(sg);
len = sg_dma_len(sg); len = sg_dma_len(sg);
@ -709,10 +701,27 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (unlikely(mem & 3 || len & 3)) if (unlikely(mem & 3 || len & 3))
mem_width = 0; mem_width = 0;
slave_sg_todev_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 = mem; desc->lli.sar = mem;
desc->lli.dar = reg; desc->lli.dar = reg;
desc->lli.ctllo = ctllo | DWC_CTLL_SRC_WIDTH(mem_width); 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) { if (!first) {
first = desc; first = desc;
@ -726,7 +735,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
&first->tx_list); &first->tx_list);
} }
prev = desc; prev = desc;
total_len += len; total_len += dlen;
if (len)
goto slave_sg_todev_fill_desc;
} }
break; break;
case DMA_FROM_DEVICE: case DMA_FROM_DEVICE:
@ -739,15 +751,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
reg = dws->rx_reg; reg = dws->rx_reg;
for_each_sg(sgl, sg, sg_len, i) { for_each_sg(sgl, sg, sg_len, i) {
struct dw_desc *desc; struct dw_desc *desc;
u32 len; u32 len, dlen, mem;
u32 mem;
desc = dwc_desc_get(dwc);
if (!desc) {
dev_err(chan2dev(chan),
"not enough descriptors available\n");
goto err_desc_get;
}
mem = sg_phys(sg); mem = sg_phys(sg);
len = sg_dma_len(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)) if (unlikely(mem & 3 || len & 3))
mem_width = 0; 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.sar = reg;
desc->lli.dar = mem; desc->lli.dar = mem;
desc->lli.ctllo = ctllo | DWC_CTLL_DST_WIDTH(mem_width); 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) { if (!first) {
first = desc; first = desc;
@ -772,7 +792,10 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
&first->tx_list); &first->tx_list);
} }
prev = desc; prev = desc;
total_len += len; total_len += dlen;
if (len)
goto slave_sg_fromdev_fill_desc;
} }
break; break;
default: default: