dmaengine: dma-jz4780: Add support for the JZ4770 SoC
authorPaul Cercueil <paul@crapouillou.net>
Wed, 29 Aug 2018 21:32:49 +0000 (23:32 +0200)
committerVinod Koul <vkoul@kernel.org>
Tue, 11 Sep 2018 07:29:26 +0000 (12:59 +0530)
The JZ4770 SoC has two DMA cores, each one featuring six DMA channels.
The major change is that each channel's clock can be enabled or disabled
through register writes.

Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Signed-off-by: Vinod Koul <vkoul@kernel.org>
drivers/dma/dma-jz4780.c

index 7683de9..184d1a2 100644 (file)
@@ -29,6 +29,9 @@
 #define JZ_DMA_REG_DIRQP       0x04
 #define JZ_DMA_REG_DDR         0x08
 #define JZ_DMA_REG_DDRS                0x0c
+#define JZ_DMA_REG_DCKE                0x10
+#define JZ_DMA_REG_DCKES       0x14
+#define JZ_DMA_REG_DCKEC       0x18
 #define JZ_DMA_REG_DMACP       0x1c
 #define JZ_DMA_REG_DSIRQP      0x20
 #define JZ_DMA_REG_DSIRQM      0x24
 
 #define JZ4780_DMA_CTRL_OFFSET 0x1000
 
+/* macros for use with jz4780_dma_soc_data.flags */
+#define JZ_SOC_DATA_ALLOW_LEGACY_DT    BIT(0)
+#define JZ_SOC_DATA_PROGRAMMABLE_DMA   BIT(1)
+#define JZ_SOC_DATA_PER_CHAN_PM                BIT(2)
+
 /**
  * struct jz4780_dma_hwdesc - descriptor structure read by the DMA controller.
  * @dcm: value for the DCM (channel command) register
@@ -133,6 +141,8 @@ struct jz4780_dma_chan {
 
 struct jz4780_dma_soc_data {
        unsigned int nb_channels;
+       unsigned int transfer_ord_max;
+       unsigned long flags;
 };
 
 struct jz4780_dma_dev {
@@ -195,6 +205,20 @@ static inline void jz4780_dma_ctrl_writel(struct jz4780_dma_dev *jzdma,
        writel(val, jzdma->ctrl_base + reg);
 }
 
+static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
+       unsigned int chn)
+{
+       if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
+               jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
+}
+
+static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
+       unsigned int chn)
+{
+       if (jzdma->soc_data->flags & JZ_SOC_DATA_PER_CHAN_PM)
+               jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
+}
+
 static struct jz4780_dma_desc *jz4780_dma_desc_alloc(
        struct jz4780_dma_chan *jzchan, unsigned int count,
        enum dma_transaction_type type)
@@ -229,8 +253,10 @@ static void jz4780_dma_desc_free(struct virt_dma_desc *vdesc)
        kfree(desc);
 }
 
-static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
+static uint32_t jz4780_dma_transfer_size(struct jz4780_dma_chan *jzchan,
+       unsigned long val, uint32_t *shift)
 {
+       struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
        int ord = ffs(val) - 1;
 
        /*
@@ -242,8 +268,8 @@ static uint32_t jz4780_dma_transfer_size(unsigned long val, uint32_t *shift)
         */
        if (ord == 3)
                ord = 2;
-       else if (ord > 7)
-               ord = 7;
+       else if (ord > jzdma->soc_data->transfer_ord_max)
+               ord = jzdma->soc_data->transfer_ord_max;
 
        *shift = ord;
 
@@ -295,7 +321,7 @@ static int jz4780_dma_setup_hwdesc(struct jz4780_dma_chan *jzchan,
         * divisible by the transfer size, and we must not use more than the
         * maximum burst specified by the user.
         */
-       tsz = jz4780_dma_transfer_size(addr | len | (width * maxburst),
+       tsz = jz4780_dma_transfer_size(jzchan, addr | len | (width * maxburst),
                                       &jzchan->transfer_shift);
 
        switch (width) {
@@ -424,7 +450,7 @@ static struct dma_async_tx_descriptor *jz4780_dma_prep_dma_memcpy(
        if (!desc)
                return NULL;
 
-       tsz = jz4780_dma_transfer_size(dest | src | len,
+       tsz = jz4780_dma_transfer_size(jzchan, dest | src | len,
                                       &jzchan->transfer_shift);
 
        jzchan->transfer_type = JZ_DMA_DRT_AUTO;
@@ -485,6 +511,9 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
                        (jzchan->curr_hwdesc + 1) % jzchan->desc->count;
        }
 
+       /* Enable the channel's clock. */
+       jz4780_dma_chan_enable(jzdma, jzchan->id);
+
        /* Use 4-word descriptors. */
        jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DCS, 0);
 
@@ -532,6 +561,8 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
                jzchan->desc = NULL;
        }
 
+       jz4780_dma_chan_disable(jzdma, jzchan->id);
+
        vchan_get_all_descriptors(&jzchan->vchan, &head);
 
        spin_unlock_irqrestore(&jzchan->vchan.lock, flags);
@@ -543,8 +574,10 @@ static int jz4780_dma_terminate_all(struct dma_chan *chan)
 static void jz4780_dma_synchronize(struct dma_chan *chan)
 {
        struct jz4780_dma_chan *jzchan = to_jz4780_dma_chan(chan);
+       struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
 
        vchan_synchronize(&jzchan->vchan);
+       jz4780_dma_chan_disable(jzdma, jzchan->id);
 }
 
 static int jz4780_dma_config(struct dma_chan *chan,
@@ -812,13 +845,16 @@ static int jz4780_dma_probe(struct platform_device *pdev)
                jzdma->ctrl_base = devm_ioremap_resource(dev, res);
                if (IS_ERR(jzdma->ctrl_base))
                        return PTR_ERR(jzdma->ctrl_base);
-       } else {
+       } else if (soc_data->flags & JZ_SOC_DATA_ALLOW_LEGACY_DT) {
                /*
                 * On JZ4780, if the second memory resource was not supplied,
                 * assume we're using an old devicetree, and calculate the
                 * offset to the control registers.
                 */
                jzdma->ctrl_base = jzdma->chn_base + JZ4780_DMA_CTRL_OFFSET;
+       } else {
+               dev_err(dev, "failed to get I/O memory\n");
+               return -EINVAL;
        }
 
        ret = platform_get_irq(pdev, 0);
@@ -879,7 +915,9 @@ static int jz4780_dma_probe(struct platform_device *pdev)
         */
        jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
                          JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
-       jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
+
+       if (soc_data->flags & JZ_SOC_DATA_PROGRAMMABLE_DMA)
+               jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
 
        INIT_LIST_HEAD(&dd->channels);
 
@@ -935,11 +973,20 @@ static int jz4780_dma_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct jz4780_dma_soc_data jz4770_dma_soc_data = {
+       .nb_channels = 6,
+       .transfer_ord_max = 6,
+       .flags = JZ_SOC_DATA_PER_CHAN_PM,
+};
+
 static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
        .nb_channels = 32,
+       .transfer_ord_max = 7,
+       .flags = JZ_SOC_DATA_ALLOW_LEGACY_DT | JZ_SOC_DATA_PROGRAMMABLE_DMA,
 };
 
 static const struct of_device_id jz4780_dma_dt_match[] = {
+       { .compatible = "ingenic,jz4770-dma", .data = &jz4770_dma_soc_data },
        { .compatible = "ingenic,jz4780-dma", .data = &jz4780_dma_soc_data },
        {},
 };