dmaengine/dw_dmac: Add support for DMA_SLAVE_CONFIG
authorViresh Kumar <viresh.kumar@st.com>
Wed, 1 Feb 2012 10:42:26 +0000 (16:12 +0530)
committerVinod Koul <vinod.koul@linux.intel.com>
Wed, 22 Feb 2012 12:45:38 +0000 (18:15 +0530)
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: Viresh Kumar <viresh.kumar@st.com>
Signed-off-by: Vinod Koul <vinod.koul@linux.intel.com>
drivers/dma/dw_dmac.c
drivers/dma/dw_dmac_regs.h

index 9f4310f013f5020029b89f72867ffcbfcc00d85c..0e4b5c6a2f86449c064c176ddfefef85c8198b57 100644 (file)
@@ -9,6 +9,7 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+#include <linux/bitops.h>
 #include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/dmaengine.h>
  * which does not support descriptor writeback.
  */
 
-#define DWC_DEFAULT_CTLLO(private) ({                          \
-               struct dw_dma_slave *__slave = (private);       \
-               int dms = __slave ? __slave->dst_master : 0;    \
-               int sms = __slave ? __slave->src_master : 1;    \
-               u8 smsize = __slave ? __slave->src_msize : DW_DMA_MSIZE_16; \
-               u8 dmsize = __slave ? __slave->dst_msize : DW_DMA_MSIZE_16; \
+#define DWC_DEFAULT_CTLLO(_chan) ({                            \
+               struct dw_dma_slave *__slave = (_chan->private);        \
+               struct dw_dma_chan *_dwc = to_dw_dma_chan(_chan);       \
+               struct dma_slave_config *_sconfig = &_dwc->dma_sconfig; \
+               int _dms = __slave ? __slave->dst_master : 0;   \
+               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_SRC_MSIZE(smsize)                   \
+               (DWC_CTLL_DST_MSIZE(_dmsize)                    \
+                | DWC_CTLL_SRC_MSIZE(_smsize)                  \
                 | DWC_CTLL_LLP_D_EN                            \
                 | DWC_CTLL_LLP_S_EN                            \
-                | DWC_CTLL_DMS(dms)                            \
-                | DWC_CTLL_SMS(sms));                          \
+                | DWC_CTLL_DMS(_dms)                           \
+                | DWC_CTLL_SMS(_sms));                         \
        })
 
 /*
@@ -656,7 +661,7 @@ dwc_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
        else
                src_width = dst_width = 0;
 
-       ctllo = DWC_DEFAULT_CTLLO(chan->private)
+       ctllo = DWC_DEFAULT_CTLLO(chan)
                        | DWC_CTLL_DST_WIDTH(dst_width)
                        | DWC_CTLL_SRC_WIDTH(src_width)
                        | DWC_CTLL_DST_INC
@@ -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_slave     *dws = chan->private;
+       struct dma_slave_config *sconfig = &dwc->dma_sconfig;
        struct dw_desc          *prev;
        struct dw_desc          *first;
        u32                     ctllo;
@@ -732,17 +738,20 @@ dwc_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (unlikely(!dws || !sg_len))
                return NULL;
 
-       reg_width = dws->reg_width;
        prev = first = NULL;
 
        switch (direction) {
        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_FIX
-                               | DWC_CTLL_SRC_INC
-                               | DWC_CTLL_FC(dws->fc));
-               reg = dws->tx_reg;
+                               | DWC_CTLL_SRC_INC);
+
+               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) {
                        struct dw_desc  *desc;
                        u32             len, dlen, mem;
@@ -800,13 +809,16 @@ slave_sg_todev_fill_desc:
                }
                break;
        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_DST_INC
-                               | DWC_CTLL_SRC_FIX
-                               | DWC_CTLL_FC(dws->fc));
+                               | DWC_CTLL_SRC_FIX);
+
+               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) {
                        struct dw_desc  *desc;
                        u32             len, dlen, mem;
@@ -884,6 +896,39 @@ err_desc_get:
        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,
                       unsigned long arg)
 {
@@ -933,8 +978,11 @@ static int dwc_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
                /* Flush all pending and queued descriptors */
                list_for_each_entry_safe(desc, _desc, &list, desc_node)
                        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 0;
 }
@@ -1167,11 +1215,11 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
                enum dma_transfer_direction direction)
 {
        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           *retval = NULL;
        struct dw_desc                  *desc;
        struct dw_desc                  *last = NULL;
-       struct dw_dma_slave             *dws = chan->private;
        unsigned long                   was_cyclic;
        unsigned int                    reg_width;
        unsigned int                    periods;
@@ -1195,7 +1243,12 @@ struct dw_cyclic_desc *dw_dma_cyclic_prep(struct dma_chan *chan,
        }
 
        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;
 
        /* 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,
 
                switch (direction) {
                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.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+                       desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
                                        | DWC_CTLL_DST_WIDTH(reg_width)
                                        | DWC_CTLL_SRC_WIDTH(reg_width)
                                        | DWC_CTLL_DST_FIX
                                        | DWC_CTLL_SRC_INC
-                                       | DWC_CTLL_FC(dws->fc)
                                        | 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;
                case DMA_DEV_TO_MEM:
                        desc->lli.dar = buf_addr + (period_len * i);
-                       desc->lli.sar = dws->rx_reg;
-                       desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan->private)
+                       desc->lli.sar = sconfig->src_addr;
+                       desc->lli.ctllo = (DWC_DEFAULT_CTLLO(chan)
                                        | DWC_CTLL_SRC_WIDTH(reg_width)
                                        | DWC_CTLL_DST_WIDTH(reg_width)
                                        | DWC_CTLL_DST_INC
                                        | DWC_CTLL_SRC_FIX
-                                       | DWC_CTLL_FC(dws->fc)
                                        | 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;
                default:
                        break;
index 5eef6946a36713bd7413ecd29c1751291c4cc41c..2005d301803d4c5e7e660926ee137fad5fd2b3ce 100644 (file)
@@ -153,6 +153,9 @@ struct dw_dma_chan {
        struct dw_cyclic_desc   *cdesc;
 
        unsigned int            descs_allocated;
+
+       /* configuration passed via DMA_SLAVE_CONFIG */
+       struct dma_slave_config dma_sconfig;
 };
 
 static inline struct dw_dma_chan_regs __iomem *