Merge tag 'docs-5.11-2' of git://git.lwn.net/linux
[linux-2.6-microblaze.git] / drivers / dma / at_xdmac.c
index 3b53115..fe45ad5 100644 (file)
 #define                AT_XDMAC_FIFO_SZ(i)     (((i) >> 5) & 0x7FF)            /* Number of Bytes */
 #define                AT_XDMAC_NB_REQ(i)      ((((i) >> 16) & 0x3F) + 1)      /* Number of Peripheral Requests Minus One */
 #define AT_XDMAC_GCFG          0x04    /* Global Configuration Register */
+#define                AT_XDMAC_WRHP(i)                (((i) & 0xF) << 4)
+#define                AT_XDMAC_WRMP(i)                (((i) & 0xF) << 8)
+#define                AT_XDMAC_WRLP(i)                (((i) & 0xF) << 12)
+#define                AT_XDMAC_RDHP(i)                (((i) & 0xF) << 16)
+#define                AT_XDMAC_RDMP(i)                (((i) & 0xF) << 20)
+#define                AT_XDMAC_RDLP(i)                (((i) & 0xF) << 24)
+#define                AT_XDMAC_RDSG(i)                (((i) & 0xF) << 28)
+#define AT_XDMAC_GCFG_M2M      (AT_XDMAC_RDLP(0xF) | AT_XDMAC_WRLP(0xF))
+#define AT_XDMAC_GCFG_P2M      (AT_XDMAC_RDSG(0x1) | AT_XDMAC_RDHP(0x3) | \
+                               AT_XDMAC_WRHP(0x5))
 #define AT_XDMAC_GWAC          0x08    /* Global Weighted Arbiter Configuration Register */
+#define                AT_XDMAC_PW0(i)         (((i) & 0xF) << 0)
+#define                AT_XDMAC_PW1(i)         (((i) & 0xF) << 4)
+#define                AT_XDMAC_PW2(i)         (((i) & 0xF) << 8)
+#define                AT_XDMAC_PW3(i)         (((i) & 0xF) << 12)
+#define AT_XDMAC_GWAC_M2M      0
+#define AT_XDMAC_GWAC_P2M      (AT_XDMAC_PW0(0xF) | AT_XDMAC_PW2(0xF))
+
 #define AT_XDMAC_GIE           0x0C    /* Global Interrupt Enable Register */
 #define AT_XDMAC_GID           0x10    /* Global Interrupt Disable Register */
 #define AT_XDMAC_GIM           0x14    /* Global Interrupt Mask Register */
 #define AT_XDMAC_GE            0x1C    /* Global Channel Enable Register */
 #define AT_XDMAC_GD            0x20    /* Global Channel Disable Register */
 #define AT_XDMAC_GS            0x24    /* Global Channel Status Register */
-#define AT_XDMAC_GRS           0x28    /* Global Channel Read Suspend Register */
-#define AT_XDMAC_GWS           0x2C    /* Global Write Suspend Register */
-#define AT_XDMAC_GRWS          0x30    /* Global Channel Read Write Suspend Register */
-#define AT_XDMAC_GRWR          0x34    /* Global Channel Read Write Resume Register */
-#define AT_XDMAC_GSWR          0x38    /* Global Channel Software Request Register */
-#define AT_XDMAC_GSWS          0x3C    /* Global channel Software Request Status Register */
-#define AT_XDMAC_GSWF          0x40    /* Global Channel Software Flush Request Register */
 #define AT_XDMAC_VERSION       0xFFC   /* XDMAC Version Register */
 
 /* Channel relative registers offsets */
 #define AT_XDMAC_CSUS          0x30    /* Channel Source Microblock Stride */
 #define AT_XDMAC_CDUS          0x34    /* Channel Destination Microblock Stride */
 
-#define AT_XDMAC_CHAN_REG_BASE 0x50    /* Channel registers base address */
-
 /* Microblock control members */
 #define AT_XDMAC_MBR_UBC_UBLEN_MAX     0xFFFFFFUL      /* Maximum Microblock Length */
 #define AT_XDMAC_MBR_UBC_NDE           (0x1 << 24)     /* Next Descriptor Enable */
@@ -179,6 +187,29 @@ enum atc_status {
        AT_XDMAC_CHAN_IS_PAUSED,
 };
 
+struct at_xdmac_layout {
+       /* Global Channel Read Suspend Register */
+       u8                              grs;
+       /* Global Write Suspend Register */
+       u8                              gws;
+       /* Global Channel Read Write Suspend Register */
+       u8                              grws;
+       /* Global Channel Read Write Resume Register */
+       u8                              grwr;
+       /* Global Channel Software Request Register */
+       u8                              gswr;
+       /* Global channel Software Request Status Register */
+       u8                              gsws;
+       /* Global Channel Software Flush Request Register */
+       u8                              gswf;
+       /* Channel reg base */
+       u8                              chan_cc_reg_base;
+       /* Source/Destination Interface must be specified or not */
+       bool                            sdif;
+       /* AXI queue priority configuration supported */
+       bool                            axi_config;
+};
+
 /* ----- Channels ----- */
 struct at_xdmac_chan {
        struct dma_chan                 chan;
@@ -212,6 +243,7 @@ struct at_xdmac {
        struct clk              *clk;
        u32                     save_gim;
        struct dma_pool         *at_xdmac_desc_pool;
+       const struct at_xdmac_layout    *layout;
        struct at_xdmac_chan    chan[];
 };
 
@@ -244,9 +276,35 @@ struct at_xdmac_desc {
        struct list_head                xfer_node;
 } __aligned(sizeof(u64));
 
+static const struct at_xdmac_layout at_xdmac_sama5d4_layout = {
+       .grs = 0x28,
+       .gws = 0x2C,
+       .grws = 0x30,
+       .grwr = 0x34,
+       .gswr = 0x38,
+       .gsws = 0x3C,
+       .gswf = 0x40,
+       .chan_cc_reg_base = 0x50,
+       .sdif = true,
+       .axi_config = false,
+};
+
+static const struct at_xdmac_layout at_xdmac_sama7g5_layout = {
+       .grs = 0x30,
+       .gws = 0x38,
+       .grws = 0x40,
+       .grwr = 0x44,
+       .gswr = 0x48,
+       .gsws = 0x4C,
+       .gswf = 0x50,
+       .chan_cc_reg_base = 0x60,
+       .sdif = false,
+       .axi_config = true,
+};
+
 static inline void __iomem *at_xdmac_chan_reg_base(struct at_xdmac *atxdmac, unsigned int chan_nb)
 {
-       return atxdmac->regs + (AT_XDMAC_CHAN_REG_BASE + chan_nb * 0x40);
+       return atxdmac->regs + (atxdmac->layout->chan_cc_reg_base + chan_nb * 0x40);
 }
 
 #define at_xdmac_read(atxdmac, reg) readl_relaxed((atxdmac)->regs + (reg))
@@ -345,8 +403,10 @@ static void at_xdmac_start_xfer(struct at_xdmac_chan *atchan,
        first->active_xfer = true;
 
        /* Tell xdmac where to get the first descriptor. */
-       reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys)
-             | AT_XDMAC_CNDA_NDAIF(atchan->memif);
+       reg = AT_XDMAC_CNDA_NDA(first->tx_dma_desc.phys);
+       if (atxdmac->layout->sdif)
+               reg |= AT_XDMAC_CNDA_NDAIF(atchan->memif);
+
        at_xdmac_chan_write(atchan, AT_XDMAC_CNDA, reg);
 
        /*
@@ -541,6 +601,7 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                                      enum dma_transfer_direction direction)
 {
        struct at_xdmac_chan    *atchan = to_at_xdmac_chan(chan);
+       struct at_xdmac         *atxdmac = to_at_xdmac(atchan->chan.device);
        int                     csize, dwidth;
 
        if (direction == DMA_DEV_TO_MEM) {
@@ -548,12 +609,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                        AT91_XDMAC_DT_PERID(atchan->perid)
                        | AT_XDMAC_CC_DAM_INCREMENTED_AM
                        | AT_XDMAC_CC_SAM_FIXED_AM
-                       | AT_XDMAC_CC_DIF(atchan->memif)
-                       | AT_XDMAC_CC_SIF(atchan->perif)
                        | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
                        | AT_XDMAC_CC_DSYNC_PER2MEM
                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                        | AT_XDMAC_CC_TYPE_PER_TRAN;
+               if (atxdmac->layout->sdif)
+                       atchan->cfg |= AT_XDMAC_CC_DIF(atchan->memif) |
+                                      AT_XDMAC_CC_SIF(atchan->perif);
+
                csize = ffs(atchan->sconfig.src_maxburst) - 1;
                if (csize < 0) {
                        dev_err(chan2dev(chan), "invalid src maxburst value\n");
@@ -571,12 +634,14 @@ static int at_xdmac_compute_chan_conf(struct dma_chan *chan,
                        AT91_XDMAC_DT_PERID(atchan->perid)
                        | AT_XDMAC_CC_DAM_FIXED_AM
                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                       | AT_XDMAC_CC_DIF(atchan->perif)
-                       | AT_XDMAC_CC_SIF(atchan->memif)
                        | AT_XDMAC_CC_SWREQ_HWR_CONNECTED
                        | AT_XDMAC_CC_DSYNC_MEM2PER
                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                        | AT_XDMAC_CC_TYPE_PER_TRAN;
+               if (atxdmac->layout->sdif)
+                       atchan->cfg |= AT_XDMAC_CC_DIF(atchan->perif) |
+                                      AT_XDMAC_CC_SIF(atchan->memif);
+
                csize = ffs(atchan->sconfig.dst_maxburst) - 1;
                if (csize < 0) {
                        dev_err(chan2dev(chan), "invalid src maxburst value\n");
@@ -866,10 +931,12 @@ at_xdmac_interleaved_queue_desc(struct dma_chan *chan,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
 
@@ -1048,12 +1115,14 @@ at_xdmac_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_DAM_INCREMENTED_AM
                                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
        unsigned long           irqflags;
@@ -1154,12 +1223,14 @@ static struct at_xdmac_desc *at_xdmac_memset_create_desc(struct dma_chan *chan,
         * ERRATA: Even if useless for memory transfers, the PERID has to not
         * match the one of another channel. If not, it could lead to spurious
         * flag status.
+        * For SAMA7G5x case, the SIF and DIF fields are no longer used.
+        * Thus, no need to have the SIF/DIF interfaces here.
+        * For SAMA5D4x and SAMA5D2x the SIF and DIF are already configured as
+        * zero.
         */
-       u32                     chan_cc = AT_XDMAC_CC_PERID(0x3f)
+       u32                     chan_cc = AT_XDMAC_CC_PERID(0x7f)
                                        | AT_XDMAC_CC_DAM_UBS_AM
                                        | AT_XDMAC_CC_SAM_INCREMENTED_AM
-                                       | AT_XDMAC_CC_DIF(0)
-                                       | AT_XDMAC_CC_SIF(0)
                                        | AT_XDMAC_CC_MBSIZE_SIXTEEN
                                        | AT_XDMAC_CC_MEMSET_HW_MODE
                                        | AT_XDMAC_CC_TYPE_MEM_TRAN;
@@ -1438,7 +1509,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
        mask = AT_XDMAC_CC_TYPE | AT_XDMAC_CC_DSYNC;
        value = AT_XDMAC_CC_TYPE_PER_TRAN | AT_XDMAC_CC_DSYNC_PER2MEM;
        if ((desc->lld.mbr_cfg & mask) == value) {
-               at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
+               at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
                while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
                        cpu_relax();
        }
@@ -1496,7 +1567,7 @@ at_xdmac_tx_status(struct dma_chan *chan, dma_cookie_t cookie,
         * FIFO flush ensures that data are really written.
         */
        if ((desc->lld.mbr_cfg & mask) == value) {
-               at_xdmac_write(atxdmac, AT_XDMAC_GSWF, atchan->mask);
+               at_xdmac_write(atxdmac, atxdmac->layout->gswf, atchan->mask);
                while (!(at_xdmac_chan_read(atchan, AT_XDMAC_CIS) & AT_XDMAC_CIS_FIS))
                        cpu_relax();
        }
@@ -1761,7 +1832,7 @@ static int at_xdmac_device_pause(struct dma_chan *chan)
                return 0;
 
        spin_lock_irqsave(&atchan->lock, flags);
-       at_xdmac_write(atxdmac, AT_XDMAC_GRWS, atchan->mask);
+       at_xdmac_write(atxdmac, atxdmac->layout->grws, atchan->mask);
        while (at_xdmac_chan_read(atchan, AT_XDMAC_CC)
               & (AT_XDMAC_CC_WRIP | AT_XDMAC_CC_RDIP))
                cpu_relax();
@@ -1784,7 +1855,7 @@ static int at_xdmac_device_resume(struct dma_chan *chan)
                return 0;
        }
 
-       at_xdmac_write(atxdmac, AT_XDMAC_GRWR, atchan->mask);
+       at_xdmac_write(atxdmac, atxdmac->layout->grwr, atchan->mask);
        clear_bit(AT_XDMAC_CHAN_IS_PAUSED, &atchan->status);
        spin_unlock_irqrestore(&atchan->lock, flags);
 
@@ -1947,6 +2018,30 @@ static int atmel_xdmac_resume(struct device *dev)
 }
 #endif /* CONFIG_PM_SLEEP */
 
+static void at_xdmac_axi_config(struct platform_device *pdev)
+{
+       struct at_xdmac *atxdmac = (struct at_xdmac *)platform_get_drvdata(pdev);
+       bool dev_m2m = false;
+       u32 dma_requests;
+
+       if (!atxdmac->layout->axi_config)
+               return; /* Not supported */
+
+       if (!of_property_read_u32(pdev->dev.of_node, "dma-requests",
+                                 &dma_requests)) {
+               dev_info(&pdev->dev, "controller in mem2mem mode.\n");
+               dev_m2m = true;
+       }
+
+       if (dev_m2m) {
+               at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_M2M);
+               at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_M2M);
+       } else {
+               at_xdmac_write(atxdmac, AT_XDMAC_GCFG, AT_XDMAC_GCFG_P2M);
+               at_xdmac_write(atxdmac, AT_XDMAC_GWAC, AT_XDMAC_GWAC_P2M);
+       }
+}
+
 static int at_xdmac_probe(struct platform_device *pdev)
 {
        struct at_xdmac *atxdmac;
@@ -1986,6 +2081,10 @@ static int at_xdmac_probe(struct platform_device *pdev)
        atxdmac->regs = base;
        atxdmac->irq = irq;
 
+       atxdmac->layout = of_device_get_match_data(&pdev->dev);
+       if (!atxdmac->layout)
+               return -ENODEV;
+
        atxdmac->clk = devm_clk_get(&pdev->dev, "dma_clk");
        if (IS_ERR(atxdmac->clk)) {
                dev_err(&pdev->dev, "can't get dma_clk\n");
@@ -2087,6 +2186,8 @@ static int at_xdmac_probe(struct platform_device *pdev)
        dev_info(&pdev->dev, "%d channels, mapped at 0x%p\n",
                 nr_channels, atxdmac->regs);
 
+       at_xdmac_axi_config(pdev);
+
        return 0;
 
 err_dma_unregister:
@@ -2128,6 +2229,10 @@ static const struct dev_pm_ops atmel_xdmac_dev_pm_ops = {
 static const struct of_device_id atmel_xdmac_dt_ids[] = {
        {
                .compatible = "atmel,sama5d4-dma",
+               .data = &at_xdmac_sama5d4_layout,
+       }, {
+               .compatible = "microchip,sama7g5-dma",
+               .data = &at_xdmac_sama7g5_layout,
        }, {
                /* sentinel */
        }