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:
parent
abf53902dc
commit
69dc14b51c
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user