Merge tag 'timers-urgent-2020-12-27' of git://git.kernel.org/pub/scm/linux/kernel...
[linux-2.6-microblaze.git] / drivers / dma / ti / k3-udma.c
index 82cf6c7..87157cb 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/soc/ti/k3-ringacc.h>
 #include <linux/soc/ti/ti_sci_protocol.h>
 #include <linux/soc/ti/ti_sci_inta_msi.h>
+#include <linux/dma/k3-event-router.h>
 #include <linux/dma/ti-cppi5.h>
 
 #include "../virt-dma.h"
@@ -55,14 +56,26 @@ struct udma_static_tr {
 
 struct udma_chan;
 
+enum k3_dma_type {
+       DMA_TYPE_UDMA = 0,
+       DMA_TYPE_BCDMA,
+       DMA_TYPE_PKTDMA,
+};
+
 enum udma_mmr {
        MMR_GCFG = 0,
+       MMR_BCHANRT,
        MMR_RCHANRT,
        MMR_TCHANRT,
        MMR_LAST,
 };
 
-static const char * const mmr_names[] = { "gcfg", "rchanrt", "tchanrt" };
+static const char * const mmr_names[] = {
+       [MMR_GCFG] = "gcfg",
+       [MMR_BCHANRT] = "bchanrt",
+       [MMR_RCHANRT] = "rchanrt",
+       [MMR_TCHANRT] = "tchanrt",
+};
 
 struct udma_tchan {
        void __iomem *reg_rt;
@@ -70,8 +83,12 @@ struct udma_tchan {
        int id;
        struct k3_ring *t_ring; /* Transmit ring */
        struct k3_ring *tc_ring; /* Transmit Completion ring */
+       int tflow_id; /* applicable only for PKTDMA */
+
 };
 
+#define udma_bchan udma_tchan
+
 struct udma_rflow {
        int id;
        struct k3_ring *fd_ring; /* Free Descriptor ring */
@@ -84,10 +101,29 @@ struct udma_rchan {
        int id;
 };
 
+struct udma_oes_offsets {
+       /* K3 UDMA Output Event Offset */
+       u32 udma_rchan;
+
+       /* BCDMA Output Event Offsets */
+       u32 bcdma_bchan_data;
+       u32 bcdma_bchan_ring;
+       u32 bcdma_tchan_data;
+       u32 bcdma_tchan_ring;
+       u32 bcdma_rchan_data;
+       u32 bcdma_rchan_ring;
+
+       /* PKTDMA Output Event Offsets */
+       u32 pktdma_tchan_flow;
+       u32 pktdma_rchan_flow;
+};
+
 #define UDMA_FLAG_PDMA_ACC32           BIT(0)
 #define UDMA_FLAG_PDMA_BURST           BIT(1)
+#define UDMA_FLAG_TDTYPE               BIT(2)
 
 struct udma_match_data {
+       enum k3_dma_type type;
        u32 psil_base;
        bool enable_memcpy_support;
        u32 flags;
@@ -95,7 +131,8 @@ struct udma_match_data {
 };
 
 struct udma_soc_data {
-       u32 rchan_oes_offset;
+       struct udma_oes_offsets oes;
+       u32 bcdma_trigger_event_offset;
 };
 
 struct udma_hwdesc {
@@ -116,6 +153,11 @@ struct udma_rx_flush {
        dma_addr_t buffer_paddr;
 };
 
+struct udma_tpl {
+       u8 levels;
+       u32 start_idx[3];
+};
+
 struct udma_dev {
        struct dma_device ddev;
        struct device *dev;
@@ -123,8 +165,9 @@ struct udma_dev {
        const struct udma_match_data *match_data;
        const struct udma_soc_data *soc_data;
 
-       u8 tpl_levels;
-       u32 tpl_start_idx[3];
+       struct udma_tpl bchan_tpl;
+       struct udma_tpl tchan_tpl;
+       struct udma_tpl rchan_tpl;
 
        size_t desc_align; /* alignment to use for descriptors */
 
@@ -138,16 +181,21 @@ struct udma_dev {
 
        struct udma_rx_flush rx_flush;
 
+       int bchan_cnt;
        int tchan_cnt;
        int echan_cnt;
        int rchan_cnt;
        int rflow_cnt;
+       int tflow_cnt;
+       unsigned long *bchan_map;
        unsigned long *tchan_map;
        unsigned long *rchan_map;
        unsigned long *rflow_gp_map;
        unsigned long *rflow_gp_map_allocated;
        unsigned long *rflow_in_use;
+       unsigned long *tflow_map;
 
+       struct udma_bchan *bchans;
        struct udma_tchan *tchans;
        struct udma_rchan *rchans;
        struct udma_rflow *rflows;
@@ -155,6 +203,7 @@ struct udma_dev {
        struct udma_chan *channels;
        u32 psil_base;
        u32 atype;
+       u32 asel;
 };
 
 struct udma_desc {
@@ -199,6 +248,7 @@ struct udma_chan_config {
        bool notdpkt; /* Suppress sending TDC packet */
        int remote_thread_id;
        u32 atype;
+       u32 asel;
        u32 src_thread;
        u32 dst_thread;
        enum psil_endpoint_type ep_type;
@@ -206,6 +256,13 @@ struct udma_chan_config {
        bool enable_burst;
        enum udma_tp_level channel_tpl; /* Channel Throughput Level */
 
+       u32 tr_trigger_type;
+
+       /* PKDMA mapped channel */
+       int mapped_channel_id;
+       /* PKTDMA default tflow or rflow for mapped channel */
+       int default_flow_id;
+
        enum dma_transfer_direction dir;
 };
 
@@ -213,11 +270,13 @@ struct udma_chan {
        struct virt_dma_chan vc;
        struct dma_slave_config cfg;
        struct udma_dev *ud;
+       struct device *dma_dev;
        struct udma_desc *desc;
        struct udma_desc *terminated_desc;
        struct udma_static_tr static_tr;
        char *name;
 
+       struct udma_bchan *bchan;
        struct udma_tchan *tchan;
        struct udma_rchan *rchan;
        struct udma_rflow *rflow;
@@ -353,10 +412,36 @@ static int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
                                                src_thread, dst_thread);
 }
 
+static void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel)
+{
+       struct device *chan_dev = &chan->dev->device;
+
+       if (asel == 0) {
+               /* No special handling for the channel */
+               chan->dev->chan_dma_dev = false;
+
+               chan_dev->dma_coherent = false;
+               chan_dev->dma_parms = NULL;
+       } else if (asel == 14 || asel == 15) {
+               chan->dev->chan_dma_dev = true;
+
+               chan_dev->dma_coherent = true;
+               dma_coerce_mask_and_coherent(chan_dev, DMA_BIT_MASK(48));
+               chan_dev->dma_parms = chan_dev->parent->dma_parms;
+       } else {
+               dev_warn(chan->device->dev, "Invalid ASEL value: %u\n", asel);
+
+               chan_dev->dma_coherent = false;
+               chan_dev->dma_parms = NULL;
+       }
+}
+
 static void udma_reset_uchan(struct udma_chan *uc)
 {
        memset(&uc->config, 0, sizeof(uc->config));
        uc->config.remote_thread_id = -1;
+       uc->config.mapped_channel_id = -1;
+       uc->config.default_flow_id = -1;
        uc->state = UDMA_CHAN_IS_IDLE;
 }
 
@@ -439,9 +524,7 @@ static void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
                        d->hwdesc[i].cppi5_desc_vaddr = NULL;
                }
        } else if (d->hwdesc[0].cppi5_desc_vaddr) {
-               struct udma_dev *ud = uc->ud;
-
-               dma_free_coherent(ud->dev, d->hwdesc[0].cppi5_desc_size,
+               dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
                                  d->hwdesc[0].cppi5_desc_vaddr,
                                  d->hwdesc[0].cppi5_desc_paddr);
 
@@ -670,8 +753,10 @@ static void udma_reset_counters(struct udma_chan *uc)
                val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PCNT_REG);
                udma_tchanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val);
 
-               val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
-               udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
+               if (!uc->bchan) {
+                       val = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
+                       udma_tchanrt_write(uc, UDMA_CHAN_RT_PEER_BCNT_REG, val);
+               }
        }
 
        if (uc->rchan) {
@@ -746,10 +831,16 @@ static void udma_start_desc(struct udma_chan *uc)
 {
        struct udma_chan_config *ucc = &uc->config;
 
-       if (ucc->pkt_mode && (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
+           (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
                int i;
 
-               /* Push all descriptors to ring for packet mode cyclic or RX */
+               /*
+                * UDMA only: Push all descriptors to ring for packet mode
+                * cyclic or RX
+                * PKTDMA supports pre-linked descriptor and cyclic is not
+                * supported
+                */
                for (i = 0; i < uc->desc->sglen; i++)
                        udma_push_to_ring(uc, i);
        } else {
@@ -1020,13 +1111,12 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
 {
        struct udma_chan *uc = data;
        struct udma_desc *d;
-       unsigned long flags;
        dma_addr_t paddr = 0;
 
        if (udma_pop_from_ring(uc, &paddr) || !paddr)
                return IRQ_HANDLED;
 
-       spin_lock_irqsave(&uc->vc.lock, flags);
+       spin_lock(&uc->vc.lock);
 
        /* Teardown completion message */
        if (cppi5_desc_is_tdcm(paddr)) {
@@ -1077,7 +1167,7 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
                }
        }
 out:
-       spin_unlock_irqrestore(&uc->vc.lock, flags);
+       spin_unlock(&uc->vc.lock);
 
        return IRQ_HANDLED;
 }
@@ -1086,9 +1176,8 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
 {
        struct udma_chan *uc = data;
        struct udma_desc *d;
-       unsigned long flags;
 
-       spin_lock_irqsave(&uc->vc.lock, flags);
+       spin_lock(&uc->vc.lock);
        d = uc->desc;
        if (d) {
                d->tr_idx = (d->tr_idx + 1) % d->sglen;
@@ -1103,7 +1192,7 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
                }
        }
 
-       spin_unlock_irqrestore(&uc->vc.lock, flags);
+       spin_unlock(&uc->vc.lock);
 
        return IRQ_HANDLED;
 }
@@ -1181,10 +1270,12 @@ static struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id)
        if (test_bit(id, ud->rflow_in_use))
                return ERR_PTR(-ENOENT);
 
-       /* GP rflow has to be allocated first */
-       if (!test_bit(id, ud->rflow_gp_map) &&
-           !test_bit(id, ud->rflow_gp_map_allocated))
-               return ERR_PTR(-EINVAL);
+       if (ud->rflow_gp_map) {
+               /* GP rflow has to be allocated first */
+               if (!test_bit(id, ud->rflow_gp_map) &&
+                   !test_bit(id, ud->rflow_gp_map_allocated))
+                       return ERR_PTR(-EINVAL);
+       }
 
        dev_dbg(ud->dev, "get rflow%d\n", id);
        set_bit(id, ud->rflow_in_use);
@@ -1215,10 +1306,10 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud,     \
        } else {                                                        \
                int start;                                              \
                                                                        \
-               if (tpl >= ud->tpl_levels)                              \
-                       tpl = ud->tpl_levels - 1;                       \
+               if (tpl >= ud->res##_tpl.levels)                        \
+                       tpl = ud->res##_tpl.levels - 1;                 \
                                                                        \
-               start = ud->tpl_start_idx[tpl];                         \
+               start = ud->res##_tpl.start_idx[tpl];                   \
                                                                        \
                id = find_next_zero_bit(ud->res##_map, ud->res##_cnt,   \
                                        start);                         \
@@ -1231,9 +1322,39 @@ static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud,      \
        return &ud->res##s[id];                                         \
 }
 
+UDMA_RESERVE_RESOURCE(bchan);
 UDMA_RESERVE_RESOURCE(tchan);
 UDMA_RESERVE_RESOURCE(rchan);
 
+static int bcdma_get_bchan(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       enum udma_tp_level tpl;
+
+       if (uc->bchan) {
+               dev_dbg(ud->dev, "chan%d: already have bchan%d allocated\n",
+                       uc->id, uc->bchan->id);
+               return 0;
+       }
+
+       /*
+        * Use normal channels for peripherals, and highest TPL channel for
+        * mem2mem
+        */
+       if (uc->config.tr_trigger_type)
+               tpl = 0;
+       else
+               tpl = ud->bchan_tpl.levels - 1;
+
+       uc->bchan = __udma_reserve_bchan(ud, tpl, -1);
+       if (IS_ERR(uc->bchan))
+               return PTR_ERR(uc->bchan);
+
+       uc->tchan = uc->bchan;
+
+       return 0;
+}
+
 static int udma_get_tchan(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1244,9 +1365,39 @@ static int udma_get_tchan(struct udma_chan *uc)
                return 0;
        }
 
-       uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl, -1);
+       /*
+        * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels.
+        * For PKTDMA mapped channels it is configured to a channel which must
+        * be used to service the peripheral.
+        */
+       uc->tchan = __udma_reserve_tchan(ud, uc->config.channel_tpl,
+                                        uc->config.mapped_channel_id);
+       if (IS_ERR(uc->tchan))
+               return PTR_ERR(uc->tchan);
+
+       if (ud->tflow_cnt) {
+               int tflow_id;
+
+               /* Only PKTDMA have support for tx flows */
+               if (uc->config.default_flow_id >= 0)
+                       tflow_id = uc->config.default_flow_id;
+               else
+                       tflow_id = uc->tchan->id;
+
+               if (test_bit(tflow_id, ud->tflow_map)) {
+                       dev_err(ud->dev, "tflow%d is in use\n", tflow_id);
+                       clear_bit(uc->tchan->id, ud->tchan_map);
+                       uc->tchan = NULL;
+                       return -ENOENT;
+               }
+
+               uc->tchan->tflow_id = tflow_id;
+               set_bit(tflow_id, ud->tflow_map);
+       } else {
+               uc->tchan->tflow_id = -1;
+       }
 
-       return PTR_ERR_OR_ZERO(uc->tchan);
+       return 0;
 }
 
 static int udma_get_rchan(struct udma_chan *uc)
@@ -1259,7 +1410,13 @@ static int udma_get_rchan(struct udma_chan *uc)
                return 0;
        }
 
-       uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl, -1);
+       /*
+        * mapped_channel_id is -1 for UDMA, BCDMA and PKTDMA unmapped channels.
+        * For PKTDMA mapped channels it is configured to a channel which must
+        * be used to service the peripheral.
+        */
+       uc->rchan = __udma_reserve_rchan(ud, uc->config.channel_tpl,
+                                        uc->config.mapped_channel_id);
 
        return PTR_ERR_OR_ZERO(uc->rchan);
 }
@@ -1287,8 +1444,11 @@ static int udma_get_chan_pair(struct udma_chan *uc)
 
        /* Can be optimized, but let's have it like this for now */
        end = min(ud->tchan_cnt, ud->rchan_cnt);
-       /* Try to use the highest TPL channel pair for MEM_TO_MEM channels */
-       chan_id = ud->tpl_start_idx[ud->tpl_levels - 1];
+       /*
+        * Try to use the highest TPL channel pair for MEM_TO_MEM channels
+        * Note: in UDMAP the channel TPL is symmetric between tchan and rchan
+        */
+       chan_id = ud->tchan_tpl.start_idx[ud->tchan_tpl.levels - 1];
        for (; chan_id < end; chan_id++) {
                if (!test_bit(chan_id, ud->tchan_map) &&
                    !test_bit(chan_id, ud->rchan_map))
@@ -1303,6 +1463,9 @@ static int udma_get_chan_pair(struct udma_chan *uc)
        uc->tchan = &ud->tchans[chan_id];
        uc->rchan = &ud->rchans[chan_id];
 
+       /* UDMA does not use tx flows */
+       uc->tchan->tflow_id = -1;
+
        return 0;
 }
 
@@ -1326,6 +1489,19 @@ static int udma_get_rflow(struct udma_chan *uc, int flow_id)
        return PTR_ERR_OR_ZERO(uc->rflow);
 }
 
+static void bcdma_put_bchan(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+
+       if (uc->bchan) {
+               dev_dbg(ud->dev, "chan%d: put bchan%d\n", uc->id,
+                       uc->bchan->id);
+               clear_bit(uc->bchan->id, ud->bchan_map);
+               uc->bchan = NULL;
+               uc->tchan = NULL;
+       }
+}
+
 static void udma_put_rchan(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1346,6 +1522,10 @@ static void udma_put_tchan(struct udma_chan *uc)
                dev_dbg(ud->dev, "chan%d: put tchan%d\n", uc->id,
                        uc->tchan->id);
                clear_bit(uc->tchan->id, ud->tchan_map);
+
+               if (uc->tchan->tflow_id >= 0)
+                       clear_bit(uc->tchan->tflow_id, ud->tflow_map);
+
                uc->tchan = NULL;
        }
 }
@@ -1362,6 +1542,65 @@ static void udma_put_rflow(struct udma_chan *uc)
        }
 }
 
+static void bcdma_free_bchan_resources(struct udma_chan *uc)
+{
+       if (!uc->bchan)
+               return;
+
+       k3_ringacc_ring_free(uc->bchan->tc_ring);
+       k3_ringacc_ring_free(uc->bchan->t_ring);
+       uc->bchan->tc_ring = NULL;
+       uc->bchan->t_ring = NULL;
+       k3_configure_chan_coherency(&uc->vc.chan, 0);
+
+       bcdma_put_bchan(uc);
+}
+
+static int bcdma_alloc_bchan_resources(struct udma_chan *uc)
+{
+       struct k3_ring_cfg ring_cfg;
+       struct udma_dev *ud = uc->ud;
+       int ret;
+
+       ret = bcdma_get_bchan(uc);
+       if (ret)
+               return ret;
+
+       ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->bchan->id, -1,
+                                           &uc->bchan->t_ring,
+                                           &uc->bchan->tc_ring);
+       if (ret) {
+               ret = -EBUSY;
+               goto err_ring;
+       }
+
+       memset(&ring_cfg, 0, sizeof(ring_cfg));
+       ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
+       ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
+
+       k3_configure_chan_coherency(&uc->vc.chan, ud->asel);
+       ring_cfg.asel = ud->asel;
+       ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+
+       ret = k3_ringacc_ring_cfg(uc->bchan->t_ring, &ring_cfg);
+       if (ret)
+               goto err_ringcfg;
+
+       return 0;
+
+err_ringcfg:
+       k3_ringacc_ring_free(uc->bchan->tc_ring);
+       uc->bchan->tc_ring = NULL;
+       k3_ringacc_ring_free(uc->bchan->t_ring);
+       uc->bchan->t_ring = NULL;
+       k3_configure_chan_coherency(&uc->vc.chan, 0);
+err_ring:
+       bcdma_put_bchan(uc);
+
+       return ret;
+}
+
 static void udma_free_tx_resources(struct udma_chan *uc)
 {
        if (!uc->tchan)
@@ -1379,15 +1618,22 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
 {
        struct k3_ring_cfg ring_cfg;
        struct udma_dev *ud = uc->ud;
-       int ret;
+       struct udma_tchan *tchan;
+       int ring_idx, ret;
 
        ret = udma_get_tchan(uc);
        if (ret)
                return ret;
 
-       ret = k3_ringacc_request_rings_pair(ud->ringacc, uc->tchan->id, -1,
-                                           &uc->tchan->t_ring,
-                                           &uc->tchan->tc_ring);
+       tchan = uc->tchan;
+       if (tchan->tflow_id >= 0)
+               ring_idx = tchan->tflow_id;
+       else
+               ring_idx = ud->bchan_cnt + tchan->id;
+
+       ret = k3_ringacc_request_rings_pair(ud->ringacc, ring_idx, -1,
+                                           &tchan->t_ring,
+                                           &tchan->tc_ring);
        if (ret) {
                ret = -EBUSY;
                goto err_ring;
@@ -1396,10 +1642,18 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
        memset(&ring_cfg, 0, sizeof(ring_cfg));
        ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
        ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
-       ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       } else {
+               ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
 
-       ret = k3_ringacc_ring_cfg(uc->tchan->t_ring, &ring_cfg);
-       ret |= k3_ringacc_ring_cfg(uc->tchan->tc_ring, &ring_cfg);
+               k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel);
+               ring_cfg.asel = uc->config.asel;
+               ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+       }
+
+       ret = k3_ringacc_ring_cfg(tchan->t_ring, &ring_cfg);
+       ret |= k3_ringacc_ring_cfg(tchan->tc_ring, &ring_cfg);
 
        if (ret)
                goto err_ringcfg;
@@ -1452,14 +1706,23 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
        if (uc->config.dir == DMA_MEM_TO_MEM)
                return 0;
 
-       ret = udma_get_rflow(uc, uc->rchan->id);
+       if (uc->config.default_flow_id >= 0)
+               ret = udma_get_rflow(uc, uc->config.default_flow_id);
+       else
+               ret = udma_get_rflow(uc, uc->rchan->id);
+
        if (ret) {
                ret = -EBUSY;
                goto err_rflow;
        }
 
        rflow = uc->rflow;
-       fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id;
+       if (ud->tflow_cnt)
+               fd_ring_id = ud->tflow_cnt + rflow->id;
+       else
+               fd_ring_id = ud->bchan_cnt + ud->tchan_cnt + ud->echan_cnt +
+                            uc->rchan->id;
+
        ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1,
                                            &rflow->fd_ring, &rflow->r_ring);
        if (ret) {
@@ -1469,15 +1732,25 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
 
        memset(&ring_cfg, 0, sizeof(ring_cfg));
 
-       if (uc->config.pkt_mode)
-               ring_cfg.size = SG_MAX_SEGMENTS;
-       else
+       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               if (uc->config.pkt_mode)
+                       ring_cfg.size = SG_MAX_SEGMENTS;
+               else
+                       ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+
+               ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+       } else {
                ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+               ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
 
-       ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
-       ring_cfg.mode = K3_RINGACC_RING_MODE_MESSAGE;
+               k3_configure_chan_coherency(&uc->vc.chan, uc->config.asel);
+               ring_cfg.asel = uc->config.asel;
+               ring_cfg.dma_dev = dmaengine_get_dma_device(&uc->vc.chan);
+       }
 
        ret = k3_ringacc_ring_cfg(rflow->fd_ring, &ring_cfg);
+
        ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
        ret |= k3_ringacc_ring_cfg(rflow->r_ring, &ring_cfg);
 
@@ -1499,7 +1772,18 @@ err_rflow:
        return ret;
 }
 
-#define TISCI_TCHAN_VALID_PARAMS (                             \
+#define TISCI_BCDMA_BCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_EXTENDED_CH_TYPE_VALID)
+
+#define TISCI_BCDMA_TCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID)
+
+#define TISCI_BCDMA_RCHAN_VALID_PARAMS (                       \
+       TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID)
+
+#define TISCI_UDMA_TCHAN_VALID_PARAMS (                                \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_EINFO_VALID |      \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_FILT_PSWORDS_VALID |    \
@@ -1509,7 +1793,7 @@ err_rflow:
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |            \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
 
-#define TISCI_RCHAN_VALID_PARAMS (                             \
+#define TISCI_UDMA_RCHAN_VALID_PARAMS (                                \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |       \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |         \
        TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |            \
@@ -1534,7 +1818,7 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
        struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
        struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
 
-       req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS;
+       req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS;
        req_tx.nav_id = tisci_rm->tisci_dev_id;
        req_tx.index = tchan->id;
        req_tx.tx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR;
@@ -1548,7 +1832,7 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
                return ret;
        }
 
-       req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS;
+       req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS;
        req_rx.nav_id = tisci_rm->tisci_dev_id;
        req_rx.index = rchan->id;
        req_rx.rx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2;
@@ -1563,6 +1847,27 @@ static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
        return ret;
 }
 
+static int bcdma_tisci_m2m_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
+       struct udma_bchan *bchan = uc->bchan;
+       int ret = 0;
+
+       req_tx.valid_params = TISCI_BCDMA_BCHAN_VALID_PARAMS;
+       req_tx.nav_id = tisci_rm->tisci_dev_id;
+       req_tx.extended_ch_type = TI_SCI_RM_BCDMA_EXTENDED_CH_TYPE_BCHAN;
+       req_tx.index = bchan->id;
+
+       ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
+       if (ret)
+               dev_err(ud->dev, "bchan%d cfg failed %d\n", bchan->id, ret);
+
+       return ret;
+}
+
 static int udma_tisci_tx_channel_config(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1583,7 +1888,7 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
                fetch_size = sizeof(struct cppi5_desc_hdr_t);
        }
 
-       req_tx.valid_params = TISCI_TCHAN_VALID_PARAMS;
+       req_tx.valid_params = TISCI_UDMA_TCHAN_VALID_PARAMS;
        req_tx.nav_id = tisci_rm->tisci_dev_id;
        req_tx.index = tchan->id;
        req_tx.tx_chan_type = mode;
@@ -1591,6 +1896,40 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
        req_tx.tx_fetch_size = fetch_size >> 2;
        req_tx.txcq_qnum = tc_ring;
        req_tx.tx_atype = uc->config.atype;
+       if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
+           ud->match_data->flags & UDMA_FLAG_TDTYPE) {
+               /* wait for peer to complete the teardown for PDMAs */
+               req_tx.valid_params |=
+                               TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID;
+               req_tx.tx_tdtype = 1;
+       }
+
+       ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
+       if (ret)
+               dev_err(ud->dev, "tchan%d cfg failed %d\n", tchan->id, ret);
+
+       return ret;
+}
+
+static int bcdma_tisci_tx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct udma_tchan *tchan = uc->tchan;
+       struct ti_sci_msg_rm_udmap_tx_ch_cfg req_tx = { 0 };
+       int ret = 0;
+
+       req_tx.valid_params = TISCI_BCDMA_TCHAN_VALID_PARAMS;
+       req_tx.nav_id = tisci_rm->tisci_dev_id;
+       req_tx.index = tchan->id;
+       req_tx.tx_supr_tdpkt = uc->config.notdpkt;
+       if (ud->match_data->flags & UDMA_FLAG_TDTYPE) {
+               /* wait for peer to complete the teardown for PDMAs */
+               req_tx.valid_params |=
+                               TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_TDTYPE_VALID;
+               req_tx.tx_tdtype = 1;
+       }
 
        ret = tisci_ops->tx_ch_cfg(tisci_rm->tisci, &req_tx);
        if (ret)
@@ -1599,6 +1938,8 @@ static int udma_tisci_tx_channel_config(struct udma_chan *uc)
        return ret;
 }
 
+#define pktdma_tisci_tx_channel_config bcdma_tisci_tx_channel_config
+
 static int udma_tisci_rx_channel_config(struct udma_chan *uc)
 {
        struct udma_dev *ud = uc->ud;
@@ -1621,7 +1962,7 @@ static int udma_tisci_rx_channel_config(struct udma_chan *uc)
                fetch_size = sizeof(struct cppi5_desc_hdr_t);
        }
 
-       req_rx.valid_params = TISCI_RCHAN_VALID_PARAMS;
+       req_rx.valid_params = TISCI_UDMA_RCHAN_VALID_PARAMS;
        req_rx.nav_id = tisci_rm->tisci_dev_id;
        req_rx.index = rchan->id;
        req_rx.rx_fetch_size =  fetch_size >> 2;
@@ -1680,6 +2021,72 @@ static int udma_tisci_rx_channel_config(struct udma_chan *uc)
        return 0;
 }
 
+static int bcdma_tisci_rx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct udma_rchan *rchan = uc->rchan;
+       struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
+       int ret = 0;
+
+       req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS;
+       req_rx.nav_id = tisci_rm->tisci_dev_id;
+       req_rx.index = rchan->id;
+
+       ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx);
+       if (ret)
+               dev_err(ud->dev, "rchan%d cfg failed %d\n", rchan->id, ret);
+
+       return ret;
+}
+
+static int pktdma_tisci_rx_channel_config(struct udma_chan *uc)
+{
+       struct udma_dev *ud = uc->ud;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct ti_sci_rm_udmap_ops *tisci_ops = tisci_rm->tisci_udmap_ops;
+       struct ti_sci_msg_rm_udmap_rx_ch_cfg req_rx = { 0 };
+       struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 };
+       int ret = 0;
+
+       req_rx.valid_params = TISCI_BCDMA_RCHAN_VALID_PARAMS;
+       req_rx.nav_id = tisci_rm->tisci_dev_id;
+       req_rx.index = uc->rchan->id;
+
+       ret = tisci_ops->rx_ch_cfg(tisci_rm->tisci, &req_rx);
+       if (ret) {
+               dev_err(ud->dev, "rchan%d cfg failed %d\n", uc->rchan->id, ret);
+               return ret;
+       }
+
+       flow_req.valid_params =
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_EINFO_PRESENT_VALID |
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_PSINFO_PRESENT_VALID |
+               TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_ERROR_HANDLING_VALID;
+
+       flow_req.nav_id = tisci_rm->tisci_dev_id;
+       flow_req.flow_index = uc->rflow->id;
+
+       if (uc->config.needs_epib)
+               flow_req.rx_einfo_present = 1;
+       else
+               flow_req.rx_einfo_present = 0;
+       if (uc->config.psd_size)
+               flow_req.rx_psinfo_present = 1;
+       else
+               flow_req.rx_psinfo_present = 0;
+       flow_req.rx_error_handling = 1;
+
+       ret = tisci_ops->rx_flow_cfg(tisci_rm->tisci, &flow_req);
+
+       if (ret)
+               dev_err(ud->dev, "flow%d config failed: %d\n", uc->rflow->id,
+                       ret);
+
+       return ret;
+}
+
 static int udma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct udma_chan *uc = to_udma_chan(chan);
@@ -1689,6 +2096,8 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
        u32 irq_udma_idx;
        int ret;
 
+       uc->dma_dev = ud->dev;
+
        if (uc->config.pkt_mode || uc->config.dir == DMA_MEM_TO_MEM) {
                uc->use_dma_pool = true;
                /* in case of MEM_TO_MEM we have maximum of two TRs */
@@ -1784,7 +2193,7 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
                                        K3_PSIL_DST_THREAD_ID_OFFSET;
 
                irq_ring = uc->rflow->r_ring;
-               irq_udma_idx = soc_data->rchan_oes_offset + uc->rchan->id;
+               irq_udma_idx = soc_data->oes.udma_rchan + uc->rchan->id;
 
                ret = udma_tisci_rx_channel_config(uc);
                break;
@@ -1884,28 +2293,391 @@ err_cleanup:
        return ret;
 }
 
-static int udma_slave_config(struct dma_chan *chan,
-                            struct dma_slave_config *cfg)
+static int bcdma_alloc_chan_resources(struct dma_chan *chan)
 {
        struct udma_chan *uc = to_udma_chan(chan);
+       struct udma_dev *ud = to_udma_dev(chan->device);
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 irq_udma_idx, irq_ring_idx;
+       int ret;
 
-       memcpy(&uc->cfg, cfg, sizeof(uc->cfg));
+       /* Only TR mode is supported */
+       uc->config.pkt_mode = false;
 
-       return 0;
-}
+       /*
+        * Make sure that the completion is in a known state:
+        * No teardown, the channel is idle
+        */
+       reinit_completion(&uc->teardown_completed);
+       complete_all(&uc->teardown_completed);
+       uc->state = UDMA_CHAN_IS_IDLE;
 
-static struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
-                                           size_t tr_size, int tr_count,
-                                           enum dma_transfer_direction dir)
-{
-       struct udma_hwdesc *hwdesc;
-       struct cppi5_desc_hdr_t *tr_desc;
-       struct udma_desc *d;
-       u32 reload_count = 0;
-       u32 ring_id;
+       switch (uc->config.dir) {
+       case DMA_MEM_TO_MEM:
+               /* Non synchronized - mem to mem type of transfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-MEM\n", __func__,
+                       uc->id);
 
-       switch (tr_size) {
-       case 16:
+               ret = bcdma_alloc_bchan_resources(uc);
+               if (ret)
+                       return ret;
+
+               irq_ring_idx = uc->bchan->id + oes->bcdma_bchan_ring;
+               irq_udma_idx = uc->bchan->id + oes->bcdma_bchan_data;
+
+               ret = bcdma_tisci_m2m_channel_config(uc);
+               break;
+       case DMA_MEM_TO_DEV:
+               /* Slave transfer synchronized - mem to dev (TX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_tx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = ud->psil_base + uc->tchan->id;
+               uc->config.dst_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->tchan->id + oes->bcdma_tchan_ring;
+               irq_udma_idx = uc->tchan->id + oes->bcdma_tchan_data;
+
+               ret = bcdma_tisci_tx_channel_config(uc);
+               break;
+       case DMA_DEV_TO_MEM:
+               /* Slave transfer synchronized - dev to mem (RX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_rx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread = (ud->psil_base + uc->rchan->id) |
+                                       K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->rchan->id + oes->bcdma_rchan_ring;
+               irq_udma_idx = uc->rchan->id + oes->bcdma_rchan_data;
+
+               ret = bcdma_tisci_rx_channel_config(uc);
+               break;
+       default:
+               /* Can not happen */
+               dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n",
+                       __func__, uc->id, uc->config.dir);
+               return -EINVAL;
+       }
+
+       /* check if the channel configuration was successful */
+       if (ret)
+               goto err_res_free;
+
+       if (udma_is_chan_running(uc)) {
+               dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
+               udma_reset_chan(uc, false);
+               if (udma_is_chan_running(uc)) {
+                       dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+                       ret = -EBUSY;
+                       goto err_res_free;
+               }
+       }
+
+       uc->dma_dev = dmaengine_get_dma_device(chan);
+       if (uc->config.dir == DMA_MEM_TO_MEM  && !uc->config.tr_trigger_type) {
+               uc->config.hdesc_size = cppi5_trdesc_calc_size(
+                                       sizeof(struct cppi5_tr_type15_t), 2);
+
+               uc->hdesc_pool = dma_pool_create(uc->name, ud->ddev.dev,
+                                                uc->config.hdesc_size,
+                                                ud->desc_align,
+                                                0);
+               if (!uc->hdesc_pool) {
+                       dev_err(ud->ddev.dev,
+                               "Descriptor pool allocation failed\n");
+                       uc->use_dma_pool = false;
+                       return -ENOMEM;
+               }
+
+               uc->use_dma_pool = true;
+       } else if (uc->config.dir != DMA_MEM_TO_MEM) {
+               /* PSI-L pairing */
+               ret = navss_psil_pair(ud, uc->config.src_thread,
+                                     uc->config.dst_thread);
+               if (ret) {
+                       dev_err(ud->dev,
+                               "PSI-L pairing failed: 0x%04x -> 0x%04x\n",
+                               uc->config.src_thread, uc->config.dst_thread);
+                       goto err_res_free;
+               }
+
+               uc->psil_paired = true;
+       }
+
+       uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx);
+       if (uc->irq_num_ring <= 0) {
+               dev_err(ud->dev, "Failed to get ring irq (index: %u)\n",
+                       irq_ring_idx);
+               ret = -EINVAL;
+               goto err_psi_free;
+       }
+
+       ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler,
+                         IRQF_TRIGGER_HIGH, uc->name, uc);
+       if (ret) {
+               dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id);
+               goto err_irq_free;
+       }
+
+       /* Event from BCDMA (TR events) only needed for slave channels */
+       if (is_slave_direction(uc->config.dir)) {
+               uc->irq_num_udma = ti_sci_inta_msi_get_virq(ud->dev,
+                                                           irq_udma_idx);
+               if (uc->irq_num_udma <= 0) {
+                       dev_err(ud->dev, "Failed to get bcdma irq (index: %u)\n",
+                               irq_udma_idx);
+                       free_irq(uc->irq_num_ring, uc);
+                       ret = -EINVAL;
+                       goto err_irq_free;
+               }
+
+               ret = request_irq(uc->irq_num_udma, udma_udma_irq_handler, 0,
+                                 uc->name, uc);
+               if (ret) {
+                       dev_err(ud->dev, "chan%d: BCDMA irq request failed\n",
+                               uc->id);
+                       free_irq(uc->irq_num_ring, uc);
+                       goto err_irq_free;
+               }
+       } else {
+               uc->irq_num_udma = 0;
+       }
+
+       udma_reset_rings(uc);
+
+       INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work,
+                                 udma_check_tx_completion);
+       return 0;
+
+err_irq_free:
+       uc->irq_num_ring = 0;
+       uc->irq_num_udma = 0;
+err_psi_free:
+       if (uc->psil_paired)
+               navss_psil_unpair(ud, uc->config.src_thread,
+                                 uc->config.dst_thread);
+       uc->psil_paired = false;
+err_res_free:
+       bcdma_free_bchan_resources(uc);
+       udma_free_tx_resources(uc);
+       udma_free_rx_resources(uc);
+
+       udma_reset_uchan(uc);
+
+       if (uc->use_dma_pool) {
+               dma_pool_destroy(uc->hdesc_pool);
+               uc->use_dma_pool = false;
+       }
+
+       return ret;
+}
+
+static int bcdma_router_config(struct dma_chan *chan)
+{
+       struct k3_event_route_data *router_data = chan->route_data;
+       struct udma_chan *uc = to_udma_chan(chan);
+       u32 trigger_event;
+
+       if (!uc->bchan)
+               return -EINVAL;
+
+       if (uc->config.tr_trigger_type != 1 && uc->config.tr_trigger_type != 2)
+               return -EINVAL;
+
+       trigger_event = uc->ud->soc_data->bcdma_trigger_event_offset;
+       trigger_event += (uc->bchan->id * 2) + uc->config.tr_trigger_type - 1;
+
+       return router_data->set_event(router_data->priv, trigger_event);
+}
+
+static int pktdma_alloc_chan_resources(struct dma_chan *chan)
+{
+       struct udma_chan *uc = to_udma_chan(chan);
+       struct udma_dev *ud = to_udma_dev(chan->device);
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 irq_ring_idx;
+       int ret;
+
+       /*
+        * Make sure that the completion is in a known state:
+        * No teardown, the channel is idle
+        */
+       reinit_completion(&uc->teardown_completed);
+       complete_all(&uc->teardown_completed);
+       uc->state = UDMA_CHAN_IS_IDLE;
+
+       switch (uc->config.dir) {
+       case DMA_MEM_TO_DEV:
+               /* Slave transfer synchronized - mem to dev (TX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_tx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = ud->psil_base + uc->tchan->id;
+               uc->config.dst_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread |= K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->tchan->tflow_id + oes->pktdma_tchan_flow;
+
+               ret = pktdma_tisci_tx_channel_config(uc);
+               break;
+       case DMA_DEV_TO_MEM:
+               /* Slave transfer synchronized - dev to mem (RX) trasnfer */
+               dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+                       uc->id);
+
+               ret = udma_alloc_rx_resources(uc);
+               if (ret) {
+                       uc->config.remote_thread_id = -1;
+                       return ret;
+               }
+
+               uc->config.src_thread = uc->config.remote_thread_id;
+               uc->config.dst_thread = (ud->psil_base + uc->rchan->id) |
+                                       K3_PSIL_DST_THREAD_ID_OFFSET;
+
+               irq_ring_idx = uc->rflow->id + oes->pktdma_rchan_flow;
+
+               ret = pktdma_tisci_rx_channel_config(uc);
+               break;
+       default:
+               /* Can not happen */
+               dev_err(uc->ud->dev, "%s: chan%d invalid direction (%u)\n",
+                       __func__, uc->id, uc->config.dir);
+               return -EINVAL;
+       }
+
+       /* check if the channel configuration was successful */
+       if (ret)
+               goto err_res_free;
+
+       if (udma_is_chan_running(uc)) {
+               dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
+               udma_reset_chan(uc, false);
+               if (udma_is_chan_running(uc)) {
+                       dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+                       ret = -EBUSY;
+                       goto err_res_free;
+               }
+       }
+
+       uc->dma_dev = dmaengine_get_dma_device(chan);
+       uc->hdesc_pool = dma_pool_create(uc->name, uc->dma_dev,
+                                        uc->config.hdesc_size, ud->desc_align,
+                                        0);
+       if (!uc->hdesc_pool) {
+               dev_err(ud->ddev.dev,
+                       "Descriptor pool allocation failed\n");
+               uc->use_dma_pool = false;
+               ret = -ENOMEM;
+               goto err_res_free;
+       }
+
+       uc->use_dma_pool = true;
+
+       /* PSI-L pairing */
+       ret = navss_psil_pair(ud, uc->config.src_thread, uc->config.dst_thread);
+       if (ret) {
+               dev_err(ud->dev, "PSI-L pairing failed: 0x%04x -> 0x%04x\n",
+                       uc->config.src_thread, uc->config.dst_thread);
+               goto err_res_free;
+       }
+
+       uc->psil_paired = true;
+
+       uc->irq_num_ring = ti_sci_inta_msi_get_virq(ud->dev, irq_ring_idx);
+       if (uc->irq_num_ring <= 0) {
+               dev_err(ud->dev, "Failed to get ring irq (index: %u)\n",
+                       irq_ring_idx);
+               ret = -EINVAL;
+               goto err_psi_free;
+       }
+
+       ret = request_irq(uc->irq_num_ring, udma_ring_irq_handler,
+                         IRQF_TRIGGER_HIGH, uc->name, uc);
+       if (ret) {
+               dev_err(ud->dev, "chan%d: ring irq request failed\n", uc->id);
+               goto err_irq_free;
+       }
+
+       uc->irq_num_udma = 0;
+
+       udma_reset_rings(uc);
+
+       INIT_DELAYED_WORK_ONSTACK(&uc->tx_drain.work,
+                                 udma_check_tx_completion);
+
+       if (uc->tchan)
+               dev_dbg(ud->dev,
+                       "chan%d: tchan%d, tflow%d, Remote thread: 0x%04x\n",
+                       uc->id, uc->tchan->id, uc->tchan->tflow_id,
+                       uc->config.remote_thread_id);
+       else if (uc->rchan)
+               dev_dbg(ud->dev,
+                       "chan%d: rchan%d, rflow%d, Remote thread: 0x%04x\n",
+                       uc->id, uc->rchan->id, uc->rflow->id,
+                       uc->config.remote_thread_id);
+       return 0;
+
+err_irq_free:
+       uc->irq_num_ring = 0;
+err_psi_free:
+       navss_psil_unpair(ud, uc->config.src_thread, uc->config.dst_thread);
+       uc->psil_paired = false;
+err_res_free:
+       udma_free_tx_resources(uc);
+       udma_free_rx_resources(uc);
+
+       udma_reset_uchan(uc);
+
+       dma_pool_destroy(uc->hdesc_pool);
+       uc->use_dma_pool = false;
+
+       return ret;
+}
+
+static int udma_slave_config(struct dma_chan *chan,
+                            struct dma_slave_config *cfg)
+{
+       struct udma_chan *uc = to_udma_chan(chan);
+
+       memcpy(&uc->cfg, cfg, sizeof(uc->cfg));
+
+       return 0;
+}
+
+static struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
+                                           size_t tr_size, int tr_count,
+                                           enum dma_transfer_direction dir)
+{
+       struct udma_hwdesc *hwdesc;
+       struct cppi5_desc_hdr_t *tr_desc;
+       struct udma_desc *d;
+       u32 reload_count = 0;
+       u32 ring_id;
+
+       switch (tr_size) {
+       case 16:
        case 32:
        case 64:
        case 128:
@@ -2028,6 +2800,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
        size_t tr_size;
        int num_tr = 0;
        int tr_idx = 0;
+       u64 asel;
 
        /* estimate the number of TRs we will need */
        for_each_sg(sgl, sgent, sglen, i) {
@@ -2045,6 +2818,11 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
 
        d->sglen = sglen;
 
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               asel = 0;
+       else
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        tr_req = d->hwdesc[0].tr_req_base;
        for_each_sg(sgl, sgent, sglen, i) {
                dma_addr_t sg_addr = sg_dma_address(sgent);
@@ -2063,6 +2841,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
                              false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
                cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
 
+               sg_addr |= asel;
                tr_req[tr_idx].addr = sg_addr;
                tr_req[tr_idx].icnt0 = tr0_cnt0;
                tr_req[tr_idx].icnt1 = tr0_cnt1;
@@ -2092,6 +2871,205 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
        return d;
 }
 
+static struct udma_desc *
+udma_prep_slave_sg_triggered_tr(struct udma_chan *uc, struct scatterlist *sgl,
+                               unsigned int sglen,
+                               enum dma_transfer_direction dir,
+                               unsigned long tx_flags, void *context)
+{
+       struct scatterlist *sgent;
+       struct cppi5_tr_type15_t *tr_req = NULL;
+       enum dma_slave_buswidth dev_width;
+       u16 tr_cnt0, tr_cnt1;
+       dma_addr_t dev_addr;
+       struct udma_desc *d;
+       unsigned int i;
+       size_t tr_size, sg_len;
+       int num_tr = 0;
+       int tr_idx = 0;
+       u32 burst, trigger_size, port_window;
+       u64 asel;
+
+       if (dir == DMA_DEV_TO_MEM) {
+               dev_addr = uc->cfg.src_addr;
+               dev_width = uc->cfg.src_addr_width;
+               burst = uc->cfg.src_maxburst;
+               port_window = uc->cfg.src_port_window_size;
+       } else if (dir == DMA_MEM_TO_DEV) {
+               dev_addr = uc->cfg.dst_addr;
+               dev_width = uc->cfg.dst_addr_width;
+               burst = uc->cfg.dst_maxburst;
+               port_window = uc->cfg.dst_port_window_size;
+       } else {
+               dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
+               return NULL;
+       }
+
+       if (!burst)
+               burst = 1;
+
+       if (port_window) {
+               if (port_window != burst) {
+                       dev_err(uc->ud->dev,
+                               "The burst must be equal to port_window\n");
+                       return NULL;
+               }
+
+               tr_cnt0 = dev_width * port_window;
+               tr_cnt1 = 1;
+       } else {
+               tr_cnt0 = dev_width;
+               tr_cnt1 = burst;
+       }
+       trigger_size = tr_cnt0 * tr_cnt1;
+
+       /* estimate the number of TRs we will need */
+       for_each_sg(sgl, sgent, sglen, i) {
+               sg_len = sg_dma_len(sgent);
+
+               if (sg_len % trigger_size) {
+                       dev_err(uc->ud->dev,
+                               "Not aligned SG entry (%zu for %u)\n", sg_len,
+                               trigger_size);
+                       return NULL;
+               }
+
+               if (sg_len / trigger_size < SZ_64K)
+                       num_tr++;
+               else
+                       num_tr += 2;
+       }
+
+       /* Now allocate and setup the descriptor. */
+       tr_size = sizeof(struct cppi5_tr_type15_t);
+       d = udma_alloc_tr_desc(uc, tr_size, num_tr, dir);
+       if (!d)
+               return NULL;
+
+       d->sglen = sglen;
+
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA) {
+               asel = 0;
+       } else {
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+               dev_addr |= asel;
+       }
+
+       tr_req = d->hwdesc[0].tr_req_base;
+       for_each_sg(sgl, sgent, sglen, i) {
+               u16 tr0_cnt2, tr0_cnt3, tr1_cnt2;
+               dma_addr_t sg_addr = sg_dma_address(sgent);
+
+               sg_len = sg_dma_len(sgent);
+               num_tr = udma_get_tr_counters(sg_len / trigger_size, 0,
+                                             &tr0_cnt2, &tr0_cnt3, &tr1_cnt2);
+               if (num_tr < 0) {
+                       dev_err(uc->ud->dev, "size %zu is not supported\n",
+                               sg_len);
+                       udma_free_hwdesc(uc, d);
+                       kfree(d);
+                       return NULL;
+               }
+
+               cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15, false,
+                             true, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+               cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
+               cppi5_tr_set_trigger(&tr_req[tr_idx].flags,
+                                    uc->config.tr_trigger_type,
+                                    CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC, 0, 0);
+
+               sg_addr |= asel;
+               if (dir == DMA_DEV_TO_MEM) {
+                       tr_req[tr_idx].addr = dev_addr;
+                       tr_req[tr_idx].icnt0 = tr_cnt0;
+                       tr_req[tr_idx].icnt1 = tr_cnt1;
+                       tr_req[tr_idx].icnt2 = tr0_cnt2;
+                       tr_req[tr_idx].icnt3 = tr0_cnt3;
+                       tr_req[tr_idx].dim1 = (-1) * tr_cnt0;
+
+                       tr_req[tr_idx].daddr = sg_addr;
+                       tr_req[tr_idx].dicnt0 = tr_cnt0;
+                       tr_req[tr_idx].dicnt1 = tr_cnt1;
+                       tr_req[tr_idx].dicnt2 = tr0_cnt2;
+                       tr_req[tr_idx].dicnt3 = tr0_cnt3;
+                       tr_req[tr_idx].ddim1 = tr_cnt0;
+                       tr_req[tr_idx].ddim2 = trigger_size;
+                       tr_req[tr_idx].ddim3 = trigger_size * tr0_cnt2;
+               } else {
+                       tr_req[tr_idx].addr = sg_addr;
+                       tr_req[tr_idx].icnt0 = tr_cnt0;
+                       tr_req[tr_idx].icnt1 = tr_cnt1;
+                       tr_req[tr_idx].icnt2 = tr0_cnt2;
+                       tr_req[tr_idx].icnt3 = tr0_cnt3;
+                       tr_req[tr_idx].dim1 = tr_cnt0;
+                       tr_req[tr_idx].dim2 = trigger_size;
+                       tr_req[tr_idx].dim3 = trigger_size * tr0_cnt2;
+
+                       tr_req[tr_idx].daddr = dev_addr;
+                       tr_req[tr_idx].dicnt0 = tr_cnt0;
+                       tr_req[tr_idx].dicnt1 = tr_cnt1;
+                       tr_req[tr_idx].dicnt2 = tr0_cnt2;
+                       tr_req[tr_idx].dicnt3 = tr0_cnt3;
+                       tr_req[tr_idx].ddim1 = (-1) * tr_cnt0;
+               }
+
+               tr_idx++;
+
+               if (num_tr == 2) {
+                       cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE15,
+                                     false, true,
+                                     CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+                       cppi5_tr_csf_set(&tr_req[tr_idx].flags,
+                                        CPPI5_TR_CSF_SUPR_EVT);
+                       cppi5_tr_set_trigger(&tr_req[tr_idx].flags,
+                                            uc->config.tr_trigger_type,
+                                            CPPI5_TR_TRIGGER_TYPE_ICNT2_DEC,
+                                            0, 0);
+
+                       sg_addr += trigger_size * tr0_cnt2 * tr0_cnt3;
+                       if (dir == DMA_DEV_TO_MEM) {
+                               tr_req[tr_idx].addr = dev_addr;
+                               tr_req[tr_idx].icnt0 = tr_cnt0;
+                               tr_req[tr_idx].icnt1 = tr_cnt1;
+                               tr_req[tr_idx].icnt2 = tr1_cnt2;
+                               tr_req[tr_idx].icnt3 = 1;
+                               tr_req[tr_idx].dim1 = (-1) * tr_cnt0;
+
+                               tr_req[tr_idx].daddr = sg_addr;
+                               tr_req[tr_idx].dicnt0 = tr_cnt0;
+                               tr_req[tr_idx].dicnt1 = tr_cnt1;
+                               tr_req[tr_idx].dicnt2 = tr1_cnt2;
+                               tr_req[tr_idx].dicnt3 = 1;
+                               tr_req[tr_idx].ddim1 = tr_cnt0;
+                               tr_req[tr_idx].ddim2 = trigger_size;
+                       } else {
+                               tr_req[tr_idx].addr = sg_addr;
+                               tr_req[tr_idx].icnt0 = tr_cnt0;
+                               tr_req[tr_idx].icnt1 = tr_cnt1;
+                               tr_req[tr_idx].icnt2 = tr1_cnt2;
+                               tr_req[tr_idx].icnt3 = 1;
+                               tr_req[tr_idx].dim1 = tr_cnt0;
+                               tr_req[tr_idx].dim2 = trigger_size;
+
+                               tr_req[tr_idx].daddr = dev_addr;
+                               tr_req[tr_idx].dicnt0 = tr_cnt0;
+                               tr_req[tr_idx].dicnt1 = tr_cnt1;
+                               tr_req[tr_idx].dicnt2 = tr1_cnt2;
+                               tr_req[tr_idx].dicnt3 = 1;
+                               tr_req[tr_idx].ddim1 = (-1) * tr_cnt0;
+                       }
+                       tr_idx++;
+               }
+
+               d->residue += sg_len;
+       }
+
+       cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
+                        CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
+
+       return d;
+}
+
 static int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
                                   enum dma_slave_buswidth dev_width,
                                   u16 elcnt)
@@ -2156,6 +3134,7 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
        struct udma_desc *d;
        u32 ring_id;
        unsigned int i;
+       u64 asel;
 
        d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
        if (!d)
@@ -2169,6 +3148,11 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
        else
                ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
 
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               asel = 0;
+       else
+               asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        for_each_sg(sgl, sgent, sglen, i) {
                struct udma_hwdesc *hwdesc = &d->hwdesc[i];
                dma_addr_t sg_addr = sg_dma_address(sgent);
@@ -2203,14 +3187,16 @@ udma_prep_slave_sg_pkt(struct udma_chan *uc, struct scatterlist *sgl,
                }
 
                /* attach the sg buffer to the descriptor */
+               sg_addr |= asel;
                cppi5_hdesc_attach_buf(desc, sg_addr, sg_len, sg_addr, sg_len);
 
                /* Attach link as host buffer descriptor */
                if (h_desc)
                        cppi5_hdesc_link_hbdesc(h_desc,
-                                               hwdesc->cppi5_desc_paddr);
+                                               hwdesc->cppi5_desc_paddr | asel);
 
-               if (dir == DMA_MEM_TO_DEV)
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
+                   dir == DMA_MEM_TO_DEV)
                        h_desc = desc;
        }
 
@@ -2333,7 +3319,8 @@ udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        struct udma_desc *d;
        u32 burst;
 
-       if (dir != uc->config.dir) {
+       if (dir != uc->config.dir &&
+           (uc->config.dir == DMA_MEM_TO_MEM && !uc->config.tr_trigger_type)) {
                dev_err(chan->device->dev,
                        "%s: chan%d is for %s, not supporting %s\n",
                        __func__, uc->id,
@@ -2359,9 +3346,12 @@ udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
        if (uc->config.pkt_mode)
                d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
                                           context);
-       else
+       else if (is_slave_direction(uc->config.dir))
                d = udma_prep_slave_sg_tr(uc, sgl, sglen, dir, tx_flags,
                                          context);
+       else
+               d = udma_prep_slave_sg_triggered_tr(uc, sgl, sglen, dir,
+                                                   tx_flags, context);
 
        if (!d)
                return NULL;
@@ -2415,7 +3405,12 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
                return NULL;
 
        tr_req = d->hwdesc[0].tr_req_base;
-       period_addr = buf_addr;
+       if (uc->ud->match_data->type == DMA_TYPE_UDMA)
+               period_addr = buf_addr;
+       else
+               period_addr = buf_addr |
+                       ((u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT);
+
        for (i = 0; i < periods; i++) {
                int tr_idx = i * num_tr;
 
@@ -2480,6 +3475,9 @@ udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
        else
                ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
 
+       if (uc->ud->match_data->type != DMA_TYPE_UDMA)
+               buf_addr |= (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+
        for (i = 0; i < periods; i++) {
                struct udma_hwdesc *hwdesc = &d->hwdesc[i];
                dma_addr_t period_addr = buf_addr + (period_len * i);
@@ -2621,6 +3619,11 @@ udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
        d->tr_idx = 0;
        d->residue = len;
 
+       if (uc->ud->match_data->type != DMA_TYPE_UDMA) {
+               src |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT;
+               dest |= (u64)uc->ud->asel << K3_ADDRESS_ASEL_SHIFT;
+       }
+
        tr_req = d->hwdesc[0].tr_req_base;
 
        cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
@@ -2978,6 +3981,7 @@ static void udma_free_chan_resources(struct dma_chan *chan)
        vchan_free_chan_resources(&uc->vc);
        tasklet_kill(&uc->vc.task);
 
+       bcdma_free_bchan_resources(uc);
        udma_free_tx_resources(uc);
        udma_free_rx_resources(uc);
        udma_reset_uchan(uc);
@@ -2989,10 +3993,14 @@ static void udma_free_chan_resources(struct dma_chan *chan)
 }
 
 static struct platform_driver udma_driver;
+static struct platform_driver bcdma_driver;
+static struct platform_driver pktdma_driver;
 
 struct udma_filter_param {
        int remote_thread_id;
        u32 atype;
+       u32 asel;
+       u32 tr_trigger_type;
 };
 
 static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
@@ -3003,7 +4011,9 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
        struct udma_chan *uc;
        struct udma_dev *ud;
 
-       if (chan->device->dev->driver != &udma_driver.driver)
+       if (chan->device->dev->driver != &udma_driver.driver &&
+           chan->device->dev->driver != &bcdma_driver.driver &&
+           chan->device->dev->driver != &pktdma_driver.driver)
                return false;
 
        uc = to_udma_chan(chan);
@@ -3017,13 +4027,25 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                return false;
        }
 
+       if (filter_param->asel > 15) {
+               dev_err(ud->dev, "Invalid channel asel: %u\n",
+                       filter_param->asel);
+               return false;
+       }
+
        ucc->remote_thread_id = filter_param->remote_thread_id;
        ucc->atype = filter_param->atype;
+       ucc->asel = filter_param->asel;
+       ucc->tr_trigger_type = filter_param->tr_trigger_type;
 
-       if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET)
+       if (ucc->tr_trigger_type) {
+               ucc->dir = DMA_MEM_TO_MEM;
+               goto triggered_bchan;
+       } else if (ucc->remote_thread_id & K3_PSIL_DST_THREAD_ID_OFFSET) {
                ucc->dir = DMA_MEM_TO_DEV;
-       else
+       } else {
                ucc->dir = DMA_DEV_TO_MEM;
+       }
 
        ep_config = psil_get_ep_config(ucc->remote_thread_id);
        if (IS_ERR(ep_config)) {
@@ -3032,6 +4054,19 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                ucc->dir = DMA_MEM_TO_MEM;
                ucc->remote_thread_id = -1;
                ucc->atype = 0;
+               ucc->asel = 0;
+               return false;
+       }
+
+       if (ud->match_data->type == DMA_TYPE_BCDMA &&
+           ep_config->pkt_mode) {
+               dev_err(ud->dev,
+                       "Only TR mode is supported (psi-l thread 0x%04x)\n",
+                       ucc->remote_thread_id);
+               ucc->dir = DMA_MEM_TO_MEM;
+               ucc->remote_thread_id = -1;
+               ucc->atype = 0;
+               ucc->asel = 0;
                return false;
        }
 
@@ -3040,6 +4075,15 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
        ucc->notdpkt = ep_config->notdpkt;
        ucc->ep_type = ep_config->ep_type;
 
+       if (ud->match_data->type == DMA_TYPE_PKTDMA &&
+           ep_config->mapped_channel_id >= 0) {
+               ucc->mapped_channel_id = ep_config->mapped_channel_id;
+               ucc->default_flow_id = ep_config->default_flow_id;
+       } else {
+               ucc->mapped_channel_id = -1;
+               ucc->default_flow_id = -1;
+       }
+
        if (ucc->ep_type != PSIL_EP_NATIVE) {
                const struct udma_match_data *match_data = ud->match_data;
 
@@ -3063,6 +4107,13 @@ static bool udma_dma_filter_fn(struct dma_chan *chan, void *param)
                ucc->remote_thread_id, dmaengine_get_direction_text(ucc->dir));
 
        return true;
+
+triggered_bchan:
+       dev_dbg(ud->dev, "chan%d: triggered channel (type: %u)\n", uc->id,
+               ucc->tr_trigger_type);
+
+       return true;
+
 }
 
 static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
@@ -3073,14 +4124,33 @@ static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
        struct udma_filter_param filter_param;
        struct dma_chan *chan;
 
-       if (dma_spec->args_count != 1 && dma_spec->args_count != 2)
-               return NULL;
+       if (ud->match_data->type == DMA_TYPE_BCDMA) {
+               if (dma_spec->args_count != 3)
+                       return NULL;
 
-       filter_param.remote_thread_id = dma_spec->args[0];
-       if (dma_spec->args_count == 2)
-               filter_param.atype = dma_spec->args[1];
-       else
+               filter_param.tr_trigger_type = dma_spec->args[0];
+               filter_param.remote_thread_id = dma_spec->args[1];
+               filter_param.asel = dma_spec->args[2];
                filter_param.atype = 0;
+       } else {
+               if (dma_spec->args_count != 1 && dma_spec->args_count != 2)
+                       return NULL;
+
+               filter_param.remote_thread_id = dma_spec->args[0];
+               filter_param.tr_trigger_type = 0;
+               if (dma_spec->args_count == 2) {
+                       if (ud->match_data->type == DMA_TYPE_UDMA) {
+                               filter_param.atype = dma_spec->args[1];
+                               filter_param.asel = 0;
+                       } else {
+                               filter_param.atype = 0;
+                               filter_param.asel = dma_spec->args[1];
+                       }
+               } else {
+                       filter_param.atype = 0;
+                       filter_param.asel = 0;
+               }
+       }
 
        chan = __dma_request_channel(&mask, udma_dma_filter_fn, &filter_param,
                                     ofdma->of_node);
@@ -3093,28 +4163,48 @@ static struct dma_chan *udma_of_xlate(struct of_phandle_args *dma_spec,
 }
 
 static struct udma_match_data am654_main_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x1000,
        .enable_memcpy_support = true,
        .statictr_z_mask = GENMASK(11, 0),
 };
 
 static struct udma_match_data am654_mcu_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x6000,
        .enable_memcpy_support = false,
        .statictr_z_mask = GENMASK(11, 0),
 };
 
 static struct udma_match_data j721e_main_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x1000,
        .enable_memcpy_support = true,
-       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST,
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
        .statictr_z_mask = GENMASK(23, 0),
 };
 
 static struct udma_match_data j721e_mcu_data = {
+       .type = DMA_TYPE_UDMA,
        .psil_base = 0x6000,
        .enable_memcpy_support = false, /* MEM_TO_MEM is slow via MCU UDMA */
-       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST,
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
+       .statictr_z_mask = GENMASK(23, 0),
+};
+
+static struct udma_match_data am64_bcdma_data = {
+       .type = DMA_TYPE_BCDMA,
+       .psil_base = 0x2000, /* for tchan and rchan, not applicable to bchan */
+       .enable_memcpy_support = true, /* Supported via bchan */
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
+       .statictr_z_mask = GENMASK(23, 0),
+};
+
+static struct udma_match_data am64_pktdma_data = {
+       .type = DMA_TYPE_PKTDMA,
+       .psil_base = 0x1000,
+       .enable_memcpy_support = false, /* PKTDMA does not support MEM_TO_MEM */
+       .flags = UDMA_FLAG_PDMA_ACC32 | UDMA_FLAG_PDMA_BURST | UDMA_FLAG_TDTYPE,
        .statictr_z_mask = GENMASK(23, 0),
 };
 
@@ -3136,30 +4226,105 @@ static const struct of_device_id udma_of_match[] = {
        { /* Sentinel */ },
 };
 
+static const struct of_device_id bcdma_of_match[] = {
+       {
+               .compatible = "ti,am64-dmss-bcdma",
+               .data = &am64_bcdma_data,
+       },
+       { /* Sentinel */ },
+};
+
+static const struct of_device_id pktdma_of_match[] = {
+       {
+               .compatible = "ti,am64-dmss-pktdma",
+               .data = &am64_pktdma_data,
+       },
+       { /* Sentinel */ },
+};
+
 static struct udma_soc_data am654_soc_data = {
-       .rchan_oes_offset = 0x200,
+       .oes = {
+               .udma_rchan = 0x200,
+       },
 };
 
 static struct udma_soc_data j721e_soc_data = {
-       .rchan_oes_offset = 0x400,
+       .oes = {
+               .udma_rchan = 0x400,
+       },
 };
 
 static struct udma_soc_data j7200_soc_data = {
-       .rchan_oes_offset = 0x80,
+       .oes = {
+               .udma_rchan = 0x80,
+       },
+};
+
+static struct udma_soc_data am64_soc_data = {
+       .oes = {
+               .bcdma_bchan_data = 0x2200,
+               .bcdma_bchan_ring = 0x2400,
+               .bcdma_tchan_data = 0x2800,
+               .bcdma_tchan_ring = 0x2a00,
+               .bcdma_rchan_data = 0x2e00,
+               .bcdma_rchan_ring = 0x3000,
+               .pktdma_tchan_flow = 0x1200,
+               .pktdma_rchan_flow = 0x1600,
+       },
+       .bcdma_trigger_event_offset = 0xc400,
 };
 
 static const struct soc_device_attribute k3_soc_devices[] = {
        { .family = "AM65X", .data = &am654_soc_data },
        { .family = "J721E", .data = &j721e_soc_data },
        { .family = "J7200", .data = &j7200_soc_data },
+       { .family = "AM64X", .data = &am64_soc_data },
        { /* sentinel */ }
 };
 
 static int udma_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
 {
+       u32 cap2, cap3, cap4;
        int i;
 
-       for (i = 0; i < MMR_LAST; i++) {
+       ud->mmrs[MMR_GCFG] = devm_platform_ioremap_resource_byname(pdev, mmr_names[MMR_GCFG]);
+       if (IS_ERR(ud->mmrs[MMR_GCFG]))
+               return PTR_ERR(ud->mmrs[MMR_GCFG]);
+
+       cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28);
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
+               ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
+               ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2);
+               ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
+               break;
+       case DMA_TYPE_BCDMA:
+               ud->bchan_cnt = BCDMA_CAP2_BCHAN_CNT(cap2);
+               ud->tchan_cnt = BCDMA_CAP2_TCHAN_CNT(cap2);
+               ud->rchan_cnt = BCDMA_CAP2_RCHAN_CNT(cap2);
+               break;
+       case DMA_TYPE_PKTDMA:
+               cap4 = udma_read(ud->mmrs[MMR_GCFG], 0x30);
+               ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
+               ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
+               ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
+               ud->tflow_cnt = PKTDMA_CAP4_TFLOW_CNT(cap4);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       for (i = 1; i < MMR_LAST; i++) {
+               if (i == MMR_BCHANRT && ud->bchan_cnt == 0)
+                       continue;
+               if (i == MMR_TCHANRT && ud->tchan_cnt == 0)
+                       continue;
+               if (i == MMR_RCHANRT && ud->rchan_cnt == 0)
+                       continue;
+
                ud->mmrs[i] = devm_platform_ioremap_resource_byname(pdev, mmr_names[i]);
                if (IS_ERR(ud->mmrs[i]))
                        return PTR_ERR(ud->mmrs[i]);
@@ -3168,48 +4333,58 @@ static int udma_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
        return 0;
 }
 
+static void udma_mark_resource_ranges(struct udma_dev *ud, unsigned long *map,
+                                     struct ti_sci_resource_desc *rm_desc,
+                                     char *name)
+{
+       bitmap_clear(map, rm_desc->start, rm_desc->num);
+       bitmap_clear(map, rm_desc->start_sec, rm_desc->num_sec);
+       dev_dbg(ud->dev, "ti_sci resource range for %s: %d:%d | %d:%d\n", name,
+               rm_desc->start, rm_desc->num, rm_desc->start_sec,
+               rm_desc->num_sec);
+}
+
+static const char * const range_names[] = {
+       [RM_RANGE_BCHAN] = "ti,sci-rm-range-bchan",
+       [RM_RANGE_TCHAN] = "ti,sci-rm-range-tchan",
+       [RM_RANGE_RCHAN] = "ti,sci-rm-range-rchan",
+       [RM_RANGE_RFLOW] = "ti,sci-rm-range-rflow",
+       [RM_RANGE_TFLOW] = "ti,sci-rm-range-tflow",
+};
+
 static int udma_setup_resources(struct udma_dev *ud)
 {
+       int ret, i, j;
        struct device *dev = ud->dev;
-       int ch_count, ret, i, j;
-       u32 cap2, cap3;
-       struct ti_sci_resource_desc *rm_desc;
        struct ti_sci_resource *rm_res, irq_res;
        struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
-       static const char * const range_names[] = { "ti,sci-rm-range-tchan",
-                                                   "ti,sci-rm-range-rchan",
-                                                   "ti,sci-rm-range-rflow" };
-
-       cap2 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(2));
-       cap3 = udma_read(ud->mmrs[MMR_GCFG], UDMA_CAP_REG(3));
-
-       ud->rflow_cnt = UDMA_CAP3_RFLOW_CNT(cap3);
-       ud->tchan_cnt = UDMA_CAP2_TCHAN_CNT(cap2);
-       ud->echan_cnt = UDMA_CAP2_ECHAN_CNT(cap2);
-       ud->rchan_cnt = UDMA_CAP2_RCHAN_CNT(cap2);
-       ch_count  = ud->tchan_cnt + ud->rchan_cnt;
+       u32 cap3;
 
        /* Set up the throughput level start indexes */
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
        if (of_device_is_compatible(dev->of_node,
                                    "ti,am654-navss-main-udmap")) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = 8;
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = 8;
        } else if (of_device_is_compatible(dev->of_node,
                                           "ti,am654-navss-mcu-udmap")) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = 2;
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = 2;
        } else if (UDMA_CAP3_UCHAN_CNT(cap3)) {
-               ud->tpl_levels = 3;
-               ud->tpl_start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
-               ud->tpl_start_idx[0] = ud->tpl_start_idx[1] +
-                                      UDMA_CAP3_HCHAN_CNT(cap3);
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
        } else if (UDMA_CAP3_HCHAN_CNT(cap3)) {
-               ud->tpl_levels = 2;
-               ud->tpl_start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
        } else {
-               ud->tpl_levels = 1;
+               ud->tchan_tpl.levels = 1;
        }
 
+       ud->rchan_tpl.levels = ud->tchan_tpl.levels;
+       ud->rchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
+       ud->rchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
+
        ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
                                           sizeof(unsigned long), GFP_KERNEL);
        ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
@@ -3247,11 +4422,15 @@ static int udma_setup_resources(struct udma_dev *ud)
        bitmap_set(ud->rflow_gp_map, 0, ud->rflow_cnt);
 
        /* Get resource ranges from tisci */
-       for (i = 0; i < RM_RANGE_LAST; i++)
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_BCHAN || i == RM_RANGE_TFLOW)
+                       continue;
+
                tisci_rm->rm_ranges[i] =
                        devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
                                                    tisci_rm->tisci_dev_id,
                                                    (char *)range_names[i]);
+       }
 
        /* tchan ranges */
        rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
@@ -3259,13 +4438,9 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_zero(ud->tchan_map, ud->tchan_cnt);
        } else {
                bitmap_fill(ud->tchan_map, ud->tchan_cnt);
-               for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->tchan_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: tchan: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
-               }
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                 &rm_res->desc[i], "tchan");
        }
        irq_res.sets = rm_res->sets;
 
@@ -3275,13 +4450,9 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_zero(ud->rchan_map, ud->rchan_cnt);
        } else {
                bitmap_fill(ud->rchan_map, ud->rchan_cnt);
-               for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->rchan_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: rchan: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
-               }
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                 &rm_res->desc[i], "rchan");
        }
 
        irq_res.sets += rm_res->sets;
@@ -3290,12 +4461,21 @@ static int udma_setup_resources(struct udma_dev *ud)
        for (i = 0; i < rm_res->sets; i++) {
                irq_res.desc[i].start = rm_res->desc[i].start;
                irq_res.desc[i].num = rm_res->desc[i].num;
+               irq_res.desc[i].start_sec = rm_res->desc[i].start_sec;
+               irq_res.desc[i].num_sec = rm_res->desc[i].num_sec;
        }
        rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
        for (j = 0; j < rm_res->sets; j++, i++) {
-               irq_res.desc[i].start = rm_res->desc[j].start +
-                                       ud->soc_data->rchan_oes_offset;
-               irq_res.desc[i].num = rm_res->desc[j].num;
+               if (rm_res->desc[j].num) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                       ud->soc_data->oes.udma_rchan;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+               }
+               if (rm_res->desc[j].num_sec) {
+                       irq_res.desc[i].start_sec = rm_res->desc[j].start_sec +
+                                       ud->soc_data->oes.udma_rchan;
+                       irq_res.desc[i].num_sec = rm_res->desc[j].num_sec;
+               }
        }
        ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
        kfree(irq_res.desc);
@@ -3311,15 +4491,344 @@ static int udma_setup_resources(struct udma_dev *ud)
                bitmap_clear(ud->rflow_gp_map, ud->rchan_cnt,
                             ud->rflow_cnt - ud->rchan_cnt);
        } else {
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rflow_gp_map,
+                                                 &rm_res->desc[i], "gp-rflow");
+       }
+
+       return 0;
+}
+
+static int bcdma_setup_resources(struct udma_dev *ud)
+{
+       int ret, i, j;
+       struct device *dev = ud->dev;
+       struct ti_sci_resource *rm_res, irq_res;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 cap;
+
+       /* Set up the throughput level start indexes */
+       cap = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+       if (BCDMA_CAP3_UBCHAN_CNT(cap)) {
+               ud->bchan_tpl.levels = 3;
+               ud->bchan_tpl.start_idx[1] = BCDMA_CAP3_UBCHAN_CNT(cap);
+               ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap);
+       } else if (BCDMA_CAP3_HBCHAN_CNT(cap)) {
+               ud->bchan_tpl.levels = 2;
+               ud->bchan_tpl.start_idx[0] = BCDMA_CAP3_HBCHAN_CNT(cap);
+       } else {
+               ud->bchan_tpl.levels = 1;
+       }
+
+       cap = udma_read(ud->mmrs[MMR_GCFG], 0x30);
+       if (BCDMA_CAP4_URCHAN_CNT(cap)) {
+               ud->rchan_tpl.levels = 3;
+               ud->rchan_tpl.start_idx[1] = BCDMA_CAP4_URCHAN_CNT(cap);
+               ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap);
+       } else if (BCDMA_CAP4_HRCHAN_CNT(cap)) {
+               ud->rchan_tpl.levels = 2;
+               ud->rchan_tpl.start_idx[0] = BCDMA_CAP4_HRCHAN_CNT(cap);
+       } else {
+               ud->rchan_tpl.levels = 1;
+       }
+
+       if (BCDMA_CAP4_UTCHAN_CNT(cap)) {
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = BCDMA_CAP4_UTCHAN_CNT(cap);
+               ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap);
+       } else if (BCDMA_CAP4_HTCHAN_CNT(cap)) {
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = BCDMA_CAP4_HTCHAN_CNT(cap);
+       } else {
+               ud->tchan_tpl.levels = 1;
+       }
+
+       ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->bchans = devm_kcalloc(dev, ud->bchan_cnt, sizeof(*ud->bchans),
+                                 GFP_KERNEL);
+       ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
+                                 GFP_KERNEL);
+       ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
+                                 GFP_KERNEL);
+       /* BCDMA do not really have flows, but the driver expect it */
+       ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                       sizeof(unsigned long),
+                                       GFP_KERNEL);
+       ud->rflows = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rflows),
+                                 GFP_KERNEL);
+
+       if (!ud->bchan_map || !ud->tchan_map || !ud->rchan_map ||
+           !ud->rflow_in_use || !ud->bchans || !ud->tchans || !ud->rchans ||
+           !ud->rflows)
+               return -ENOMEM;
+
+       /* Get resource ranges from tisci */
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_RFLOW || i == RM_RANGE_TFLOW)
+                       continue;
+               if (i == RM_RANGE_BCHAN && ud->bchan_cnt == 0)
+                       continue;
+               if (i == RM_RANGE_TCHAN && ud->tchan_cnt == 0)
+                       continue;
+               if (i == RM_RANGE_RCHAN && ud->rchan_cnt == 0)
+                       continue;
+
+               tisci_rm->rm_ranges[i] =
+                       devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
+                                                   tisci_rm->tisci_dev_id,
+                                                   (char *)range_names[i]);
+       }
+
+       irq_res.sets = 0;
+
+       /* bchan ranges */
+       if (ud->bchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->bchan_map, ud->bchan_cnt);
+               } else {
+                       bitmap_fill(ud->bchan_map, ud->bchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->bchan_map,
+                                                         &rm_res->desc[i],
+                                                         "bchan");
+               }
+               irq_res.sets += rm_res->sets;
+       }
+
+       /* tchan ranges */
+       if (ud->tchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->tchan_map, ud->tchan_cnt);
+               } else {
+                       bitmap_fill(ud->tchan_map, ud->tchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                         &rm_res->desc[i],
+                                                         "tchan");
+               }
+               irq_res.sets += rm_res->sets * 2;
+       }
+
+       /* rchan ranges */
+       if (ud->rchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+               if (IS_ERR(rm_res)) {
+                       bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+               } else {
+                       bitmap_fill(ud->rchan_map, ud->rchan_cnt);
+                       for (i = 0; i < rm_res->sets; i++)
+                               udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                         &rm_res->desc[i],
+                                                         "rchan");
+               }
+               irq_res.sets += rm_res->sets * 2;
+       }
+
+       irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL);
+       if (ud->bchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_BCHAN];
                for (i = 0; i < rm_res->sets; i++) {
-                       rm_desc = &rm_res->desc[i];
-                       bitmap_clear(ud->rflow_gp_map, rm_desc->start,
-                                    rm_desc->num);
-                       dev_dbg(dev, "ti-sci-res: rflow: %d:%d\n",
-                               rm_desc->start, rm_desc->num);
+                       irq_res.desc[i].start = rm_res->desc[i].start +
+                                               oes->bcdma_bchan_ring;
+                       irq_res.desc[i].num = rm_res->desc[i].num;
+               }
+       }
+       if (ud->tchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+               for (j = 0; j < rm_res->sets; j++, i += 2) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                               oes->bcdma_tchan_data;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+
+                       irq_res.desc[i + 1].start = rm_res->desc[j].start +
+                                               oes->bcdma_tchan_ring;
+                       irq_res.desc[i + 1].num = rm_res->desc[j].num;
                }
        }
+       if (ud->rchan_cnt) {
+               rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+               for (j = 0; j < rm_res->sets; j++, i += 2) {
+                       irq_res.desc[i].start = rm_res->desc[j].start +
+                                               oes->bcdma_rchan_data;
+                       irq_res.desc[i].num = rm_res->desc[j].num;
+
+                       irq_res.desc[i + 1].start = rm_res->desc[j].start +
+                                               oes->bcdma_rchan_ring;
+                       irq_res.desc[i + 1].num = rm_res->desc[j].num;
+               }
+       }
+
+       ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
+       kfree(irq_res.desc);
+       if (ret) {
+               dev_err(ud->dev, "Failed to allocate MSI interrupts\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int pktdma_setup_resources(struct udma_dev *ud)
+{
+       int ret, i, j;
+       struct device *dev = ud->dev;
+       struct ti_sci_resource *rm_res, irq_res;
+       struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
+       const struct udma_oes_offsets *oes = &ud->soc_data->oes;
+       u32 cap3;
+
+       /* Set up the throughput level start indexes */
+       cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+       if (UDMA_CAP3_UCHAN_CNT(cap3)) {
+               ud->tchan_tpl.levels = 3;
+               ud->tchan_tpl.start_idx[1] = UDMA_CAP3_UCHAN_CNT(cap3);
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+       } else if (UDMA_CAP3_HCHAN_CNT(cap3)) {
+               ud->tchan_tpl.levels = 2;
+               ud->tchan_tpl.start_idx[0] = UDMA_CAP3_HCHAN_CNT(cap3);
+       } else {
+               ud->tchan_tpl.levels = 1;
+       }
+
+       ud->tchan_tpl.levels = ud->tchan_tpl.levels;
+       ud->tchan_tpl.start_idx[0] = ud->tchan_tpl.start_idx[0];
+       ud->tchan_tpl.start_idx[1] = ud->tchan_tpl.start_idx[1];
+
+       ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->tchans = devm_kcalloc(dev, ud->tchan_cnt, sizeof(*ud->tchans),
+                                 GFP_KERNEL);
+       ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+       ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
+                                 GFP_KERNEL);
+       ud->rflow_in_use = devm_kcalloc(dev, BITS_TO_LONGS(ud->rflow_cnt),
+                                       sizeof(unsigned long),
+                                       GFP_KERNEL);
+       ud->rflows = devm_kcalloc(dev, ud->rflow_cnt, sizeof(*ud->rflows),
+                                 GFP_KERNEL);
+       ud->tflow_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tflow_cnt),
+                                          sizeof(unsigned long), GFP_KERNEL);
+
+       if (!ud->tchan_map || !ud->rchan_map || !ud->tflow_map || !ud->tchans ||
+           !ud->rchans || !ud->rflows || !ud->rflow_in_use)
+               return -ENOMEM;
+
+       /* Get resource ranges from tisci */
+       for (i = 0; i < RM_RANGE_LAST; i++) {
+               if (i == RM_RANGE_BCHAN)
+                       continue;
+
+               tisci_rm->rm_ranges[i] =
+                       devm_ti_sci_get_of_resource(tisci_rm->tisci, dev,
+                                                   tisci_rm->tisci_dev_id,
+                                                   (char *)range_names[i]);
+       }
+
+       /* tchan ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TCHAN];
+       if (IS_ERR(rm_res)) {
+               bitmap_zero(ud->tchan_map, ud->tchan_cnt);
+       } else {
+               bitmap_fill(ud->tchan_map, ud->tchan_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tchan_map,
+                                                 &rm_res->desc[i], "tchan");
+       }
+
+       /* rchan ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RCHAN];
+       if (IS_ERR(rm_res)) {
+               bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+       } else {
+               bitmap_fill(ud->rchan_map, ud->rchan_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rchan_map,
+                                                 &rm_res->desc[i], "rchan");
+       }
+
+       /* rflow ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW];
+       if (IS_ERR(rm_res)) {
+               /* all rflows are assigned exclusively to Linux */
+               bitmap_zero(ud->rflow_in_use, ud->rflow_cnt);
+       } else {
+               bitmap_fill(ud->rflow_in_use, ud->rflow_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->rflow_in_use,
+                                                 &rm_res->desc[i], "rflow");
+       }
+       irq_res.sets = rm_res->sets;
+
+       /* tflow ranges */
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW];
+       if (IS_ERR(rm_res)) {
+               /* all tflows are assigned exclusively to Linux */
+               bitmap_zero(ud->tflow_map, ud->tflow_cnt);
+       } else {
+               bitmap_fill(ud->tflow_map, ud->tflow_cnt);
+               for (i = 0; i < rm_res->sets; i++)
+                       udma_mark_resource_ranges(ud, ud->tflow_map,
+                                                 &rm_res->desc[i], "tflow");
+       }
+       irq_res.sets += rm_res->sets;
+
+       irq_res.desc = kcalloc(irq_res.sets, sizeof(*irq_res.desc), GFP_KERNEL);
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_TFLOW];
+       for (i = 0; i < rm_res->sets; i++) {
+               irq_res.desc[i].start = rm_res->desc[i].start +
+                                       oes->pktdma_tchan_flow;
+               irq_res.desc[i].num = rm_res->desc[i].num;
+       }
+       rm_res = tisci_rm->rm_ranges[RM_RANGE_RFLOW];
+       for (j = 0; j < rm_res->sets; j++, i++) {
+               irq_res.desc[i].start = rm_res->desc[j].start +
+                                       oes->pktdma_rchan_flow;
+               irq_res.desc[i].num = rm_res->desc[j].num;
+       }
+       ret = ti_sci_inta_msi_domain_alloc_irqs(ud->dev, &irq_res);
+       kfree(irq_res.desc);
+       if (ret) {
+               dev_err(ud->dev, "Failed to allocate MSI interrupts\n");
+               return ret;
+       }
+
+       return 0;
+}
+
+static int setup_resources(struct udma_dev *ud)
+{
+       struct device *dev = ud->dev;
+       int ch_count, ret;
+
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ret = udma_setup_resources(ud);
+               break;
+       case DMA_TYPE_BCDMA:
+               ret = bcdma_setup_resources(ud);
+               break;
+       case DMA_TYPE_PKTDMA:
+               ret = pktdma_setup_resources(ud);
+               break;
+       default:
+               return -EINVAL;
+       }
 
+       if (ret)
+               return ret;
+
+       ch_count  = ud->bchan_cnt + ud->tchan_cnt + ud->rchan_cnt;
+       if (ud->bchan_cnt)
+               ch_count -= bitmap_weight(ud->bchan_map, ud->bchan_cnt);
        ch_count -= bitmap_weight(ud->tchan_map, ud->tchan_cnt);
        ch_count -= bitmap_weight(ud->rchan_map, ud->rchan_cnt);
        if (!ch_count)
@@ -3330,12 +4839,40 @@ static int udma_setup_resources(struct udma_dev *ud)
        if (!ud->channels)
                return -ENOMEM;
 
-       dev_info(dev, "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n",
-                ch_count,
-                ud->tchan_cnt - bitmap_weight(ud->tchan_map, ud->tchan_cnt),
-                ud->rchan_cnt - bitmap_weight(ud->rchan_map, ud->rchan_cnt),
-                ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map,
-                                              ud->rflow_cnt));
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               dev_info(dev,
+                        "Channels: %d (tchan: %u, rchan: %u, gp-rflow: %u)\n",
+                        ch_count,
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt),
+                        ud->rflow_cnt - bitmap_weight(ud->rflow_gp_map,
+                                                      ud->rflow_cnt));
+               break;
+       case DMA_TYPE_BCDMA:
+               dev_info(dev,
+                        "Channels: %d (bchan: %u, tchan: %u, rchan: %u)\n",
+                        ch_count,
+                        ud->bchan_cnt - bitmap_weight(ud->bchan_map,
+                                                      ud->bchan_cnt),
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt));
+               break;
+       case DMA_TYPE_PKTDMA:
+               dev_info(dev,
+                        "Channels: %d (tchan: %u, rchan: %u)\n",
+                        ch_count,
+                        ud->tchan_cnt - bitmap_weight(ud->tchan_map,
+                                                      ud->tchan_cnt),
+                        ud->rchan_cnt - bitmap_weight(ud->rchan_map,
+                                                      ud->rchan_cnt));
+       default:
+               break;
+       }
 
        return ch_count;
 }
@@ -3444,20 +4981,33 @@ static void udma_dbg_summary_show_chan(struct seq_file *s,
 
        seq_printf(s, " %-13s| %s", dma_chan_name(chan),
                   chan->dbg_client_name ?: "in-use");
-       seq_printf(s, " (%s, ", dmaengine_get_direction_text(uc->config.dir));
+       if (ucc->tr_trigger_type)
+               seq_puts(s, " (triggered, ");
+       else
+               seq_printf(s, " (%s, ",
+                          dmaengine_get_direction_text(uc->config.dir));
 
        switch (uc->config.dir) {
        case DMA_MEM_TO_MEM:
+               if (uc->ud->match_data->type == DMA_TYPE_BCDMA) {
+                       seq_printf(s, "bchan%d)\n", uc->bchan->id);
+                       return;
+               }
+
                seq_printf(s, "chan%d pair [0x%04x -> 0x%04x], ", uc->tchan->id,
                           ucc->src_thread, ucc->dst_thread);
                break;
        case DMA_DEV_TO_MEM:
                seq_printf(s, "rchan%d [0x%04x -> 0x%04x], ", uc->rchan->id,
                           ucc->src_thread, ucc->dst_thread);
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA)
+                       seq_printf(s, "rflow%d, ", uc->rflow->id);
                break;
        case DMA_MEM_TO_DEV:
                seq_printf(s, "tchan%d [0x%04x -> 0x%04x], ", uc->tchan->id,
                           ucc->src_thread, ucc->dst_thread);
+               if (uc->ud->match_data->type == DMA_TYPE_PKTDMA)
+                       seq_printf(s, "tflow%d, ", uc->tchan->tflow_id);
                break;
        default:
                seq_printf(s, ")\n");
@@ -3519,6 +5069,25 @@ static int udma_probe(struct platform_device *pdev)
        if (!ud)
                return -ENOMEM;
 
+       match = of_match_node(udma_of_match, dev->of_node);
+       if (!match)
+               match = of_match_node(bcdma_of_match, dev->of_node);
+       if (!match) {
+               match = of_match_node(pktdma_of_match, dev->of_node);
+               if (!match) {
+                       dev_err(dev, "No compatible match found\n");
+                       return -ENODEV;
+               }
+       }
+       ud->match_data = match->data;
+
+       soc = soc_device_match(k3_soc_devices);
+       if (!soc) {
+               dev_err(dev, "No compatible SoC found\n");
+               return -ENODEV;
+       }
+       ud->soc_data = soc->data;
+
        ret = udma_get_mmrs(pdev, ud);
        if (ret)
                return ret;
@@ -3542,16 +5111,44 @@ static int udma_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = of_property_read_u32(dev->of_node, "ti,udma-atype", &ud->atype);
-       if (!ret && ud->atype > 2) {
-               dev_err(dev, "Invalid atype: %u\n", ud->atype);
-               return -EINVAL;
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ret = of_property_read_u32(dev->of_node, "ti,udma-atype",
+                                          &ud->atype);
+               if (!ret && ud->atype > 2) {
+                       dev_err(dev, "Invalid atype: %u\n", ud->atype);
+                       return -EINVAL;
+               }
+       } else {
+               ret = of_property_read_u32(dev->of_node, "ti,asel",
+                                          &ud->asel);
+               if (!ret && ud->asel > 15) {
+                       dev_err(dev, "Invalid asel: %u\n", ud->asel);
+                       return -EINVAL;
+               }
        }
 
        ud->tisci_rm.tisci_udmap_ops = &ud->tisci_rm.tisci->ops.rm_udmap_ops;
        ud->tisci_rm.tisci_psil_ops = &ud->tisci_rm.tisci->ops.rm_psil_ops;
 
-       ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc");
+       if (ud->match_data->type == DMA_TYPE_UDMA) {
+               ud->ringacc = of_k3_ringacc_get_by_phandle(dev->of_node, "ti,ringacc");
+       } else {
+               struct k3_ringacc_init_data ring_init_data;
+
+               ring_init_data.tisci = ud->tisci_rm.tisci;
+               ring_init_data.tisci_dev_id = ud->tisci_rm.tisci_dev_id;
+               if (ud->match_data->type == DMA_TYPE_BCDMA) {
+                       ring_init_data.num_rings = ud->bchan_cnt +
+                                                  ud->tchan_cnt +
+                                                  ud->rchan_cnt;
+               } else {
+                       ring_init_data.num_rings = ud->rflow_cnt +
+                                                  ud->tflow_cnt;
+               }
+
+               ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data);
+       }
+
        if (IS_ERR(ud->ringacc))
                return PTR_ERR(ud->ringacc);
 
@@ -3562,27 +5159,15 @@ static int udma_probe(struct platform_device *pdev)
                return -EPROBE_DEFER;
        }
 
-       match = of_match_node(udma_of_match, dev->of_node);
-       if (!match) {
-               dev_err(dev, "No compatible match found\n");
-               return -ENODEV;
-       }
-       ud->match_data = match->data;
-
-       soc = soc_device_match(k3_soc_devices);
-       if (!soc) {
-               dev_err(dev, "No compatible SoC found\n");
-               return -ENODEV;
-       }
-       ud->soc_data = soc->data;
-
        dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask);
-       dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+       /* cyclic operation is not supported via PKTDMA */
+       if (ud->match_data->type != DMA_TYPE_PKTDMA) {
+               dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+               ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
+       }
 
-       ud->ddev.device_alloc_chan_resources = udma_alloc_chan_resources;
        ud->ddev.device_config = udma_slave_config;
        ud->ddev.device_prep_slave_sg = udma_prep_slave_sg;
-       ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
        ud->ddev.device_issue_pending = udma_issue_pending;
        ud->ddev.device_tx_status = udma_tx_status;
        ud->ddev.device_pause = udma_pause;
@@ -3593,7 +5178,25 @@ static int udma_probe(struct platform_device *pdev)
        ud->ddev.dbg_summary_show = udma_dbg_summary_show;
 #endif
 
+       switch (ud->match_data->type) {
+       case DMA_TYPE_UDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       udma_alloc_chan_resources;
+               break;
+       case DMA_TYPE_BCDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       bcdma_alloc_chan_resources;
+               ud->ddev.device_router_config = bcdma_router_config;
+               break;
+       case DMA_TYPE_PKTDMA:
+               ud->ddev.device_alloc_chan_resources =
+                                       pktdma_alloc_chan_resources;
+               break;
+       default:
+               return -EINVAL;
+       }
        ud->ddev.device_free_chan_resources = udma_free_chan_resources;
+
        ud->ddev.src_addr_widths = TI_UDMAC_BUSWIDTHS;
        ud->ddev.dst_addr_widths = TI_UDMAC_BUSWIDTHS;
        ud->ddev.directions = BIT(DMA_DEV_TO_MEM) | BIT(DMA_MEM_TO_DEV);
@@ -3601,7 +5204,8 @@ static int udma_probe(struct platform_device *pdev)
        ud->ddev.copy_align = DMAENGINE_ALIGN_8_BYTES;
        ud->ddev.desc_metadata_modes = DESC_METADATA_CLIENT |
                                       DESC_METADATA_ENGINE;
-       if (ud->match_data->enable_memcpy_support) {
+       if (ud->match_data->enable_memcpy_support &&
+           !(ud->match_data->type == DMA_TYPE_BCDMA && ud->bchan_cnt == 0)) {
                dma_cap_set(DMA_MEMCPY, ud->ddev.cap_mask);
                ud->ddev.device_prep_dma_memcpy = udma_prep_dma_memcpy;
                ud->ddev.directions |= BIT(DMA_MEM_TO_MEM);
@@ -3614,7 +5218,7 @@ static int udma_probe(struct platform_device *pdev)
        INIT_LIST_HEAD(&ud->ddev.channels);
        INIT_LIST_HEAD(&ud->desc_to_purge);
 
-       ch_count = udma_setup_resources(ud);
+       ch_count = setup_resources(ud);
        if (ch_count <= 0)
                return ch_count;
 
@@ -3629,6 +5233,13 @@ static int udma_probe(struct platform_device *pdev)
        if (ret)
                return ret;
 
+       for (i = 0; i < ud->bchan_cnt; i++) {
+               struct udma_bchan *bchan = &ud->bchans[i];
+
+               bchan->id = i;
+               bchan->reg_rt = ud->mmrs[MMR_BCHANRT] + i * 0x1000;
+       }
+
        for (i = 0; i < ud->tchan_cnt; i++) {
                struct udma_tchan *tchan = &ud->tchans[i];
 
@@ -3655,9 +5266,12 @@ static int udma_probe(struct platform_device *pdev)
                uc->ud = ud;
                uc->vc.desc_free = udma_desc_free;
                uc->id = i;
+               uc->bchan = NULL;
                uc->tchan = NULL;
                uc->rchan = NULL;
                uc->config.remote_thread_id = -1;
+               uc->config.mapped_channel_id = -1;
+               uc->config.default_flow_id = -1;
                uc->config.dir = DMA_MEM_TO_MEM;
                uc->name = devm_kasprintf(dev, GFP_KERNEL, "%s chan%d",
                                          dev_name(dev), i);
@@ -3696,5 +5310,25 @@ static struct platform_driver udma_driver = {
 };
 builtin_platform_driver(udma_driver);
 
+static struct platform_driver bcdma_driver = {
+       .driver = {
+               .name   = "ti-bcdma",
+               .of_match_table = bcdma_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe          = udma_probe,
+};
+builtin_platform_driver(bcdma_driver);
+
+static struct platform_driver pktdma_driver = {
+       .driver = {
+               .name   = "ti-pktdma",
+               .of_match_table = pktdma_of_match,
+               .suppress_bind_attrs = true,
+       },
+       .probe          = udma_probe,
+};
+builtin_platform_driver(pktdma_driver);
+
 /* Private interfaces to UDMA */
 #include "k3-udma-private.c"