Commit 327e6970 authored by Viresh Kumar's avatar Viresh Kumar Committed by Vinod Koul

dmaengine/dw_dmac: Add support for DMA_SLAVE_CONFIG

This patch adds support for DMA_SLAVE_CONFIG in dwc DMAC controller. Fields in
struct dw_dma_slave for passing similar data are preserved in this patch untill
all existing users are fixed.

That will be handled later in this patchset.
Signed-off-by: default avatarViresh Kumar <viresh.kumar@st.com>
Signed-off-by: default avatarVinod Koul <vinod.koul@linux.intel.com>
parent 6bc711f6
...@@ -9,6 +9,7 @@ ...@@ -9,6 +9,7 @@
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/bitops.h>
#include <linux/clk.h> #include <linux/clk.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/dmaengine.h> #include <linux/dmaengine.h>
...@@ -33,19 +34,23 @@ ...@@ -33,19 +34,23 @@
* which does not support descriptor writeback. * which does not support descriptor writeback.
*/ */
#define DWC_DEFAULT_CTLLO(private) ({ \ #define DWC_DEFAULT_CTLLO(_chan) ({ \
struct dw_dma_slave *__slave = (private); \ struct dw_dma_slave *__slave = (_chan->private); \
int dms = __slave ? __slave->dst_master : 0; \ struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan); \
int sms = __slave ? __slave->src_master : 1; \ struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
u8 smsize = __slave ? __slave->src_msize : DW_DMA_MSIZE_16; \ int _dms = __slave ? __slave->dst_master : 0; \
u8 dmsize = __slave ? __slave->dst_msize : DW_DMA_MSIZE_16; \ int _sms = __slave ? __slave->src_master : 1; \
u8 _smsize = __slave ? _sconfig->src_maxburst : \
DW_DMA_MSIZE_16; \
u8 _dmsize = __slave ? _sconfig->dst_maxburst : \
DW_DMA_MSIZE_16; \
\ \
(DWC_CTLL_DST_MSIZE(dmsize) \ (DWC_CTLL_DST_MSIZE(_dmsize) \
| DWC_CTLL_SRC_MSIZE(smsize) \ | DWC_CTLL_SRC_MSIZE(_smsize) \
| DWC_CTLL_LLP_D_EN \ | DWC_CTLL_LLP_D_EN \
| DWC_CTLL_LLP_S_EN \ | DWC_CTLL_LLP_S_EN \
| DWC_CTLL_DMS(dms) \ | DWC_CTLL_DMS(_dms) \
| DWC_CTLL_SMS(sms)); \ | DWC_CTLL_SMS(_sms)); \
}) })
/* /*
...@@ -656,7 +661,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src, ...@@ -656,7 +661,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
else else
src_width = dst_width = 0; src_width = dst_width = 0;
ctllo = DWC_DEFAULT_CTLLO(chan->private) ctllo = DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(dst_width) | DWC_CTLL_DST_WIDTH(dst_width)
| DWC_CTLL_SRC_WIDTH(src_width) | DWC_CTLL_SRC_WIDTH(src_width)
| DWC_CTLL_DST_INC | DWC_CTLL_DST_INC
...@@ -717,6 +722,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -717,6 +722,7 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
{ {
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dw_dma_slave *dws = chan->private; struct dw_dma_slave *dws = chan->private;
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_desc *prev; struct dw_desc *prev;
struct dw_desc *first; struct dw_desc *first;
u32 ctllo; u32 ctllo;
...@@ -732,17 +738,20 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -732,17 +738,20 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
if (unlikely(!dws || !sg_len)) if (unlikely(!dws || !sg_len))
return NULL; return NULL;
reg_width = dws->reg_width;
prev = first = NULL; prev = first = NULL;
switch (direction) { switch (direction) {
case DMA_MEM_TO_DEV: case DMA_MEM_TO_DEV:
ctllo = (DWC_DEFAULT_CTLLO(chan->private) reg_width = __fls(sconfig->dst_addr_width);
reg = sconfig->dst_addr;
ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_FIX | DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC | DWC_CTLL_SRC_INC);
| DWC_CTLL_FC(dws->fc));
reg = dws->tx_reg; ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
DWC_CTLL_FC(DW_DMA_FC_D_M2P);
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, dlen, mem; u32 len, dlen, mem;
...@@ -800,13 +809,16 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -800,13 +809,16 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
} }
break; break;
case DMA_DEV_TO_MEM: case DMA_DEV_TO_MEM:
ctllo = (DWC_DEFAULT_CTLLO(chan->private) reg_width = __fls(sconfig->src_addr_width);
reg = sconfig->src_addr;
ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_INC | DWC_CTLL_DST_INC
| DWC_CTLL_SRC_FIX | DWC_CTLL_SRC_FIX);
| DWC_CTLL_FC(dws->fc));
ctllo |= sconfig->device_fc ? DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
DWC_CTLL_FC(DW_DMA_FC_D_P2M);
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, dlen, mem; u32 len, dlen, mem;
...@@ -884,6 +896,39 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl, ...@@ -884,6 +896,39 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
return NULL; return NULL;
} }
/*
* Fix sconfig's burst size according to dw_dmac. We need to convert them as:
* 1 -> 0, 4 -> 1, 8 -> 2, 16 -> 3.
*
* NOTE: burst size 2 is not supported by controller.
*
* This can be done by finding least significant bit set: n & (n - 1)
*/
static inline void convert_burst(u32 *maxburst)
{
if (*maxburst > 1)
*maxburst = fls(*maxburst) - 2;
else
*maxburst = 0;
}
static int
set_runtime_config(struct dma_chan *chan, struct dma_slave_config *sconfig)
{
struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
/* Check if it is chan is configured for slave transfers */
if (!chan->private)
return -EINVAL;
memcpy(&dwc->dma_sconfig, sconfig, sizeof(*sconfig));
convert_burst(&dwc->dma_sconfig.src_maxburst);
convert_burst(&dwc->dma_sconfig.dst_maxburst);
return 0;
}
static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
unsigned long arg) unsigned long arg)
{ {
...@@ -933,8 +978,11 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd, ...@@ -933,8 +978,11 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
/* Flush all pending and queued descriptors */ /* Flush all pending and queued descriptors */
list_for_each_entry_safe(desc, _desc, &list, desc_node) list_for_each_entry_safe(desc, _desc, &list, desc_node)
dwc_descriptor_complete(dwc, desc, false); dwc_descriptor_complete(dwc, desc, false);
} else } else if (cmd == DMA_SLAVE_CONFIG) {
return set_runtime_config(chan, (struct dma_slave_config *)arg);
} else {
return -ENXIO; return -ENXIO;
}
return 0; return 0;
} }
...@@ -1167,11 +1215,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, ...@@ -1167,11 +1215,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
enum dma_transfer_direction direction) enum dma_transfer_direction direction)
{ {
struct dw_dma_chan *dwc = to_dw_dma_chan(chan); struct dw_dma_chan *dwc = to_dw_dma_chan(chan);
struct dma_slave_config *sconfig = &dwc->dma_sconfig;
struct dw_cyclic_desc *cdesc; struct dw_cyclic_desc *cdesc;
struct dw_cyclic_desc *retval = NULL; struct dw_cyclic_desc *retval = NULL;
struct dw_desc *desc; struct dw_desc *desc;
struct dw_desc *last = NULL; struct dw_desc *last = NULL;
struct dw_dma_slave *dws = chan->private;
unsigned long was_cyclic; unsigned long was_cyclic;
unsigned int reg_width; unsigned int reg_width;
unsigned int periods; unsigned int periods;
...@@ -1195,7 +1243,12 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, ...@@ -1195,7 +1243,12 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
} }
retval = ERR_PTR(-EINVAL); retval = ERR_PTR(-EINVAL);
reg_width = dws->reg_width;
if (direction == DMA_MEM_TO_DEV)
reg_width = __ffs(sconfig->dst_addr_width);
else
reg_width = __ffs(sconfig->src_addr_width);
periods = buf_len / period_len; periods = buf_len / period_len;
/* Check for too big/unaligned periods and unaligned DMA buffer. */ /* Check for too big/unaligned periods and unaligned DMA buffer. */
...@@ -1228,26 +1281,34 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan, ...@@ -1228,26 +1281,34 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
switch (direction) { switch (direction) {
case DMA_MEM_TO_DEV: case DMA_MEM_TO_DEV:
desc->lli.dar = dws->tx_reg; desc->lli.dar = sconfig->dst_addr;
desc->lli.sar = buf_addr + (period_len * i); desc->lli.sar = buf_addr + (period_len * i);
desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_FIX | DWC_CTLL_DST_FIX
| DWC_CTLL_SRC_INC | DWC_CTLL_SRC_INC
| DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN); | DWC_CTLL_INT_EN);
desc->lli.ctllo |= sconfig->device_fc ?
DWC_CTLL_FC(DW_DMA_FC_P_M2P) :
DWC_CTLL_FC(DW_DMA_FC_D_M2P);
break; break;
case DMA_DEV_TO_MEM: case DMA_DEV_TO_MEM:
desc->lli.dar = buf_addr + (period_len * i); desc->lli.dar = buf_addr + (period_len * i);
desc->lli.sar = dws->rx_reg; desc->lli.sar = sconfig->src_addr;
desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private) desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
| DWC_CTLL_SRC_WIDTH(reg_width) | DWC_CTLL_SRC_WIDTH(reg_width)
| DWC_CTLL_DST_WIDTH(reg_width) | DWC_CTLL_DST_WIDTH(reg_width)
| DWC_CTLL_DST_INC | DWC_CTLL_DST_INC
| DWC_CTLL_SRC_FIX | DWC_CTLL_SRC_FIX
| DWC_CTLL_FC(dws->fc)
| DWC_CTLL_INT_EN); | DWC_CTLL_INT_EN);
desc->lli.ctllo |= sconfig->device_fc ?
DWC_CTLL_FC(DW_DMA_FC_P_P2M) :
DWC_CTLL_FC(DW_DMA_FC_D_P2M);
break; break;
default: default:
break; break;
......
...@@ -153,6 +153,9 @@ struct dw_dma_chan { ...@@ -153,6 +153,9 @@ struct dw_dma_chan {
struct dw_cyclic_desc *cdesc; struct dw_cyclic_desc *cdesc;
unsigned int descs_allocated; unsigned int descs_allocated;
/* configuration passed via DMA_SLAVE_CONFIG */
struct dma_slave_config dma_sconfig;
}; };
static inline struct dw_dma_chan_regs __iomem * static inline struct dw_dma_chan_regs __iomem *
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment