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:
Родитель
abf53902dc
Коммит
69dc14b51c
|
@ -693,15 +693,7 @@ 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;
|
||||
|
||||
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);
|
||||
|
@ -709,10 +701,27 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
|
|||
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),
|
||||
"not enough descriptors available\n");
|
||||
goto err_desc_get;
|
||||
}
|
||||
|
||||
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:
|
||||
|
|
Загрузка…
Ссылка в новой задаче