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 коммит произвёл Vinod Koul
Родитель abf53902dc
Коммит 69dc14b51c
1 изменённых файлов: 45 добавлений и 22 удалений

Просмотреть файл

@ -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: