]> git.sur5r.net Git - u-boot/blobdiff - drivers/dma/ti-edma3.c
Merge branch 'master' of git://git.denx.de/u-boot-usb
[u-boot] / drivers / dma / ti-edma3.c
index 8184ded9fa81a22f040328e13d8b4a937b4935e8..2131e10a408c8fcff113dbb1111d2e66afce2e5c 100644 (file)
@@ -1,3 +1,4 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Enhanced Direct Memory Access (EDMA3) Controller
  *
@@ -5,12 +6,13 @@
  *     Texas Instruments Incorporated, <www.ti.com>
  *
  * Author: Ivan Khoronzhuk <ivan.khoronzhuk@ti.com>
- *
- * SPDX-License-Identifier:     GPL-2.0+
  */
 
 #include <asm/io.h>
 #include <common.h>
+#include <dm.h>
+#include <dma.h>
+#include <asm/omap_common.h>
 #include <asm/ti-common/ti-edma3.h>
 
 #define EDMA3_SL_BASE(slot)                    (0x4000 + ((slot) << 5))
 #define EDMA3_QEESR                            0x108c
 #define EDMA3_QSECR                            0x1094
 
+#define EDMA_FILL_BUFFER_SIZE                  512
+
+struct ti_edma3_priv {
+       u32 base;
+};
+
+static u8 edma_fill_buffer[EDMA_FILL_BUFFER_SIZE] __aligned(ARCH_DMA_MINALIGN);
+
 /**
  * qedma3_start - start qdma on a channel
  * @base: base address of edma
@@ -382,3 +392,189 @@ void qedma3_stop(u32 base, struct edma3_channel_config *cfg)
        /* Clear the channel map */
        __raw_writel(0, base + EDMA3_QCHMAP(cfg->chnum));
 }
+
+void __edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,
+                     void *dst, void *src, size_t len, size_t s_len)
+{
+       struct edma3_slot_config        slot;
+       struct edma3_channel_config     edma_channel;
+       int                             b_cnt_value = 1;
+       int                             rem_bytes  = 0;
+       int                             a_cnt_value = len;
+       unsigned int                    addr = (unsigned int) (dst);
+       unsigned int                    max_acnt  = 0x7FFFU;
+
+       if (len > s_len) {
+               b_cnt_value = (len / s_len);
+               rem_bytes = (len % s_len);
+               a_cnt_value = s_len;
+       } else if (len > max_acnt) {
+               b_cnt_value = (len / max_acnt);
+               rem_bytes  = (len % max_acnt);
+               a_cnt_value = max_acnt;
+       }
+
+       slot.opt        = 0;
+       slot.src        = ((unsigned int) src);
+       slot.acnt       = a_cnt_value;
+       slot.bcnt       = b_cnt_value;
+       slot.ccnt       = 1;
+       if (len == s_len)
+               slot.src_bidx = a_cnt_value;
+       else
+               slot.src_bidx = 0;
+       slot.dst_bidx   = a_cnt_value;
+       slot.src_cidx   = 0;
+       slot.dst_cidx   = 0;
+       slot.link       = EDMA3_PARSET_NULL_LINK;
+       slot.bcntrld    = 0;
+       slot.opt        = EDMA3_SLOPT_TRANS_COMP_INT_ENB |
+                         EDMA3_SLOPT_COMP_CODE(0) |
+                         EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;
+
+       edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);
+       edma_channel.slot = edma_slot_num;
+       edma_channel.chnum = 0;
+       edma_channel.complete_code = 0;
+        /* set event trigger to dst update */
+       edma_channel.trigger_slot_word = EDMA3_TWORD(dst);
+
+       qedma3_start(edma3_base_addr, &edma_channel);
+       edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr);
+
+       while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))
+               ;
+       qedma3_stop(edma3_base_addr, &edma_channel);
+
+       if (rem_bytes != 0) {
+               slot.opt        = 0;
+               if (len == s_len)
+                       slot.src =
+                               (b_cnt_value * max_acnt) + ((unsigned int) src);
+               else
+                       slot.src = (unsigned int) src;
+               slot.acnt       = rem_bytes;
+               slot.bcnt       = 1;
+               slot.ccnt       = 1;
+               slot.src_bidx   = rem_bytes;
+               slot.dst_bidx   = rem_bytes;
+               slot.src_cidx   = 0;
+               slot.dst_cidx   = 0;
+               slot.link       = EDMA3_PARSET_NULL_LINK;
+               slot.bcntrld    = 0;
+               slot.opt        = EDMA3_SLOPT_TRANS_COMP_INT_ENB |
+                                 EDMA3_SLOPT_COMP_CODE(0) |
+                                 EDMA3_SLOPT_STATIC | EDMA3_SLOPT_AB_SYNC;
+               edma3_slot_configure(edma3_base_addr, edma_slot_num, &slot);
+               edma_channel.slot = edma_slot_num;
+               edma_channel.chnum = 0;
+               edma_channel.complete_code = 0;
+               /* set event trigger to dst update */
+               edma_channel.trigger_slot_word = EDMA3_TWORD(dst);
+
+               qedma3_start(edma3_base_addr, &edma_channel);
+               edma3_set_dest_addr(edma3_base_addr, edma_channel.slot, addr +
+                                   (max_acnt * b_cnt_value));
+               while (edma3_check_for_transfer(edma3_base_addr, &edma_channel))
+                       ;
+               qedma3_stop(edma3_base_addr, &edma_channel);
+       }
+}
+
+void __edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,
+                 void *dst, u8 val, size_t len)
+{
+       int xfer_len;
+       int max_xfer = EDMA_FILL_BUFFER_SIZE * 65535;
+
+       memset((void *)edma_fill_buffer, val, sizeof(edma_fill_buffer));
+
+       while (len) {
+               xfer_len = len;
+               if (xfer_len > max_xfer)
+                       xfer_len = max_xfer;
+
+               __edma3_transfer(edma3_base_addr, edma_slot_num, dst,
+                                edma_fill_buffer, xfer_len,
+                                EDMA_FILL_BUFFER_SIZE);
+               len -= xfer_len;
+               dst += xfer_len;
+       }
+}
+
+#ifndef CONFIG_DMA
+
+void edma3_transfer(unsigned long edma3_base_addr, unsigned int edma_slot_num,
+                   void *dst, void *src, size_t len)
+{
+       __edma3_transfer(edma3_base_addr, edma_slot_num, dst, src, len, len);
+}
+
+void edma3_fill(unsigned long edma3_base_addr, unsigned int edma_slot_num,
+               void *dst, u8 val, size_t len)
+{
+       __edma3_fill(edma3_base_addr, edma_slot_num, dst, val, len);
+}
+
+#else
+
+static int ti_edma3_transfer(struct udevice *dev, int direction, void *dst,
+                            void *src, size_t len)
+{
+       struct ti_edma3_priv *priv = dev_get_priv(dev);
+
+       /* enable edma3 clocks */
+       enable_edma3_clocks();
+
+       switch (direction) {
+       case DMA_MEM_TO_MEM:
+               __edma3_transfer(priv->base, 1, dst, src, len, len);
+               break;
+       default:
+               pr_err("Transfer type not implemented in DMA driver\n");
+               break;
+       }
+
+       /* disable edma3 clocks */
+       disable_edma3_clocks();
+
+       return 0;
+}
+
+static int ti_edma3_ofdata_to_platdata(struct udevice *dev)
+{
+       struct ti_edma3_priv *priv = dev_get_priv(dev);
+
+       priv->base = devfdt_get_addr(dev);
+
+       return 0;
+}
+
+static int ti_edma3_probe(struct udevice *dev)
+{
+       struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+
+       uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM;
+
+       return 0;
+}
+
+static const struct dma_ops ti_edma3_ops = {
+       .transfer       = ti_edma3_transfer,
+};
+
+static const struct udevice_id ti_edma3_ids[] = {
+       { .compatible = "ti,edma3" },
+       { }
+};
+
+U_BOOT_DRIVER(ti_edma3) = {
+       .name   = "ti_edma3",
+       .id     = UCLASS_DMA,
+       .of_match = ti_edma3_ids,
+       .ops    = &ti_edma3_ops,
+       .ofdata_to_platdata = ti_edma3_ofdata_to_platdata,
+       .probe  = ti_edma3_probe,
+       .priv_auto_alloc_size = sizeof(struct ti_edma3_priv),
+};
+#endif /* CONFIG_DMA */