diff mbox series

[U-Boot,4/6] dma: ti: add driver to K3 UDMA

Message ID 20190122150359.15919-5-vigneshr@ti.com
State Superseded
Delegated to: Tom Rini
Headers show
Series AM65x: Add DMA support | expand

Commit Message

Raghavendra, Vignesh Jan. 22, 2019, 3:03 p.m. UTC
The UDMA-P is intended to perform similar (but significantly upgraded) functions
as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
supports the transmission and reception of various packet types.
The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
channels. Channels in the UDMA-P can be configured to be either Packet-Based or
Third-Party channels on a channel by channel basis.

The initial driver supports:
- MEM_TO_MEM (TR mode)
- DEV_TO_MEM (Packet mode)
- MEM_TO_DEV (Packet mode)

Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
Signed-off-by: Vignesh R <vigneshr@ti.com>
---
 drivers/dma/Kconfig               |    2 +
 drivers/dma/Makefile              |    2 +
 drivers/dma/ti/Kconfig            |   14 +
 drivers/dma/ti/Makefile           |    3 +
 drivers/dma/ti/k3-udma-hwdef.h    |  184 +++
 drivers/dma/ti/k3-udma.c          | 1749 +++++++++++++++++++++++++++++
 include/dt-bindings/dma/k3-udma.h |   31 +
 include/linux/soc/ti/ti-udma.h    |   24 +
 8 files changed, 2009 insertions(+)
 create mode 100644 drivers/dma/ti/Kconfig
 create mode 100644 drivers/dma/ti/Makefile
 create mode 100644 drivers/dma/ti/k3-udma-hwdef.h
 create mode 100644 drivers/dma/ti/k3-udma.c
 create mode 100644 include/dt-bindings/dma/k3-udma.h
 create mode 100644 include/linux/soc/ti/ti-udma.h

Comments

Tom Rini Jan. 22, 2019, 6:56 p.m. UTC | #1
On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:

> The UDMA-P is intended to perform similar (but significantly upgraded) functions
> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
> supports the transmission and reception of various packet types.
> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
> Third-Party channels on a channel by channel basis.
> 
> The initial driver supports:
> - MEM_TO_MEM (TR mode)
> - DEV_TO_MEM (Packet mode)
> - MEM_TO_DEV (Packet mode)
> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> Signed-off-by: Vignesh R <vigneshr@ti.com>

Reviewed-by: Tom Rini <trini@konsulko.com>

And the DT binding is common to Linux, and been reviewed there?  Or?
Thanks!
Peter Ujfalusi Jan. 23, 2019, 10:35 a.m. UTC | #2
Tom,

On 22/01/2019 20.56, Tom Rini wrote:
> On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:
> 
>> The UDMA-P is intended to perform similar (but significantly upgraded) functions
>> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
>> supports the transmission and reception of various packet types.
>> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
>> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
>> Third-Party channels on a channel by channel basis.
>>
>> The initial driver supports:
>> - MEM_TO_MEM (TR mode)
>> - DEV_TO_MEM (Packet mode)
>> - MEM_TO_DEV (Packet mode)
>>
>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
>> Signed-off-by: Vignesh R <vigneshr@ti.com>
> 
> Reviewed-by: Tom Rini <trini@konsulko.com>
> 
> And the DT binding is common to Linux, and been reviewed there?  Or?

The binding is the same for Linux but unfortunately it has not went
through a proper review yet due to the fact that I need to wait for the
interrupt support to arrive to mainline.

However I have sent an earlier version as RFC:
https://www.spinics.net/lists/dmaengine/msg16661.html

As for the bindings (and code):
The linux bindings are different:

- there is no ti,psi-proxy anymore.
- ringacc uses tisci to get GP ring range and we need
  ti,sci-rm-range-gp-rings property in DT for it
- UDMA also uses tisci to get resource ranges and needs:
  ti,sci-rm-range-tchan, ti,sci-rm-range-rchan, ti,sci-rm-range-rflow in DT
- UDMA does not have/need dma-channels property
- properties needed for interrupts (currently msi-parent)

FWIW this is how the MCU NAVSS looks like in Linux atm:
https://github.com/omap-audio/linux-audio/blob/peter/linux-next-wip/arch/arm64/boot/dts/ti/k3-am65-mcu.dtsi

and the binding documentation for UDMA:
https://github.com/omap-audio/linux-audio/blob/peter/linux-next-wip/Documentation/devicetree/bindings/dma/ti/k3-udma.txt

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
Raghavendra, Vignesh Jan. 23, 2019, 1:43 p.m. UTC | #3
Tom,

On 23/01/19 4:05 PM, Peter Ujfalusi wrote:
> Tom,
> 
> On 22/01/2019 20.56, Tom Rini wrote:
>> On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:
>>
>>> The UDMA-P is intended to perform similar (but significantly upgraded) functions
>>> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
>>> supports the transmission and reception of various packet types.
>>> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
>>> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
>>> Third-Party channels on a channel by channel basis.
>>>
>>> The initial driver supports:
>>> - MEM_TO_MEM (TR mode)
>>> - DEV_TO_MEM (Packet mode)
>>> - MEM_TO_DEV (Packet mode)
>>>
>>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
>>> Signed-off-by: Vignesh R <vigneshr@ti.com>
>>
>> Reviewed-by: Tom Rini <trini@konsulko.com>
>>
>> And the DT binding is common to Linux, and been reviewed there?  Or?

As Peter pointed out DT bindings are not yet frozen and may change.
Hence, I have added the nodes in -u-boot.dtsi. As and when DT bindings
are accepted, these would be moved to base dtsi.

UDMA support in kernel is currently blocked due to on going discussions
on how UDMA/ring interrupts needs to be modeled and supported (not a so
important for U-Boot UDMA support). But, I submitted patches for U-Boot
as UDMA is support is required to support networking on AM654 which is
essential for ease of booting the platform with U-Boot.

> 
> The binding is the same for Linux but unfortunately it has not went
> through a proper review yet due to the fact that I need to wait for the
> interrupt support to arrive to mainline.
> 
> However I have sent an earlier version as RFC:
> https://www.spinics.net/lists/dmaengine/msg16661.html
> 
> As for the bindings (and code):
> The linux bindings are different:
> 
> - there is no ti,psi-proxy anymore.

Will drop this as U-Boot driver does not use them either.

> - ringacc uses tisci to get GP ring range and we need
>   ti,sci-rm-range-gp-rings property in DT for it
> - UDMA also uses tisci to get resource ranges and needs:
>   ti,sci-rm-range-tchan, ti,sci-rm-range-rchan, ti,sci-rm-range-rflow in DT
> - UDMA does not have/need dma-channels property

I plan to align above bindings with Linux as when bindings are accepted
into kernel and then port it to U-Boot along with relevant TI-SCI driver
code.

Meanwhile would it be acceptable to add these nodes in -u-boot.dtsi to
get closer to enable tftp?
Tom Rini Jan. 23, 2019, 1:52 p.m. UTC | #4
On Wed, Jan 23, 2019 at 12:35:26PM +0200, Peter Ujfalusi wrote:
> Tom,
> 
> On 22/01/2019 20.56, Tom Rini wrote:
> > On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:
> > 
> >> The UDMA-P is intended to perform similar (but significantly upgraded) functions
> >> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
> >> supports the transmission and reception of various packet types.
> >> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
> >> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
> >> Third-Party channels on a channel by channel basis.
> >>
> >> The initial driver supports:
> >> - MEM_TO_MEM (TR mode)
> >> - DEV_TO_MEM (Packet mode)
> >> - MEM_TO_DEV (Packet mode)
> >>
> >> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> >> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> >> Signed-off-by: Vignesh R <vigneshr@ti.com>
> > 
> > Reviewed-by: Tom Rini <trini@konsulko.com>
> > 
> > And the DT binding is common to Linux, and been reviewed there?  Or?
> 
> The binding is the same for Linux but unfortunately it has not went
> through a proper review yet due to the fact that I need to wait for the
> interrupt support to arrive to mainline.
> 
> However I have sent an earlier version as RFC:
> https://www.spinics.net/lists/dmaengine/msg16661.html
> 
> As for the bindings (and code):
> The linux bindings are different:

OK.  But we generally don't want the bindings to be different as,
putting my DT guy hat on, they aren't supposed to be OS centric and all
of that.

That's not to say that we might not end up needing to figure out ways to
do something more simply in U-Boot than in Linux.

So, really, do we want the bindings to not match?  Thanks!
Tom Rini Jan. 23, 2019, 1:56 p.m. UTC | #5
On Wed, Jan 23, 2019 at 07:13:11PM +0530, Vignesh R wrote:
> Tom,
> 
> On 23/01/19 4:05 PM, Peter Ujfalusi wrote:
> > Tom,
> > 
> > On 22/01/2019 20.56, Tom Rini wrote:
> >> On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:
> >>
> >>> The UDMA-P is intended to perform similar (but significantly upgraded) functions
> >>> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
> >>> supports the transmission and reception of various packet types.
> >>> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
> >>> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
> >>> Third-Party channels on a channel by channel basis.
> >>>
> >>> The initial driver supports:
> >>> - MEM_TO_MEM (TR mode)
> >>> - DEV_TO_MEM (Packet mode)
> >>> - MEM_TO_DEV (Packet mode)
> >>>
> >>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> >>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
> >>> Signed-off-by: Vignesh R <vigneshr@ti.com>
> >>
> >> Reviewed-by: Tom Rini <trini@konsulko.com>
> >>
> >> And the DT binding is common to Linux, and been reviewed there?  Or?
> 
> As Peter pointed out DT bindings are not yet frozen and may change.
> Hence, I have added the nodes in -u-boot.dtsi. As and when DT bindings
> are accepted, these would be moved to base dtsi.
> 
> UDMA support in kernel is currently blocked due to on going discussions
> on how UDMA/ring interrupts needs to be modeled and supported (not a so
> important for U-Boot UDMA support). But, I submitted patches for U-Boot
> as UDMA is support is required to support networking on AM654 which is
> essential for ease of booting the platform with U-Boot.
> 
> > 
> > The binding is the same for Linux but unfortunately it has not went
> > through a proper review yet due to the fact that I need to wait for the
> > interrupt support to arrive to mainline.
> > 
> > However I have sent an earlier version as RFC:
> > https://www.spinics.net/lists/dmaengine/msg16661.html
> > 
> > As for the bindings (and code):
> > The linux bindings are different:
> > 
> > - there is no ti,psi-proxy anymore.
> 
> Will drop this as U-Boot driver does not use them either.
> 
> > - ringacc uses tisci to get GP ring range and we need
> >   ti,sci-rm-range-gp-rings property in DT for it
> > - UDMA also uses tisci to get resource ranges and needs:
> >   ti,sci-rm-range-tchan, ti,sci-rm-range-rchan, ti,sci-rm-range-rflow in DT
> > - UDMA does not have/need dma-channels property
> 
> I plan to align above bindings with Linux as when bindings are accepted
> into kernel and then port it to U-Boot along with relevant TI-SCI driver
> code.
> 
> Meanwhile would it be acceptable to add these nodes in -u-boot.dtsi to
> get closer to enable tftp?

... I knew I should have read the whole thread first.  So, how likely do
we think the kernel bindings are to see big changes?  I wonder if we
should re-align things now, or not.
Raghavendra, Vignesh Jan. 25, 2019, 2:23 p.m. UTC | #6
On 23/01/19 7:26 PM, Tom Rini wrote:
> On Wed, Jan 23, 2019 at 07:13:11PM +0530, Vignesh R wrote:
>> Tom,
>>
>> On 23/01/19 4:05 PM, Peter Ujfalusi wrote:
>>> Tom,
>>>
>>> On 22/01/2019 20.56, Tom Rini wrote:
>>>> On Tue, Jan 22, 2019 at 08:33:57PM +0530, Vignesh R wrote:
>>>>
>>>>> The UDMA-P is intended to perform similar (but significantly upgraded) functions
>>>>> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
>>>>> supports the transmission and reception of various packet types.
>>>>> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
>>>>> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
>>>>> Third-Party channels on a channel by channel basis.
>>>>>
>>>>> The initial driver supports:
>>>>> - MEM_TO_MEM (TR mode)
>>>>> - DEV_TO_MEM (Packet mode)
>>>>> - MEM_TO_DEV (Packet mode)
>>>>>
>>>>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>>>>> Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com>
>>>>> Signed-off-by: Vignesh R <vigneshr@ti.com>
>>>>
>>>> Reviewed-by: Tom Rini <trini@konsulko.com>
>>>>
>>>> And the DT binding is common to Linux, and been reviewed there?  Or?
>>
>> As Peter pointed out DT bindings are not yet frozen and may change.
>> Hence, I have added the nodes in -u-boot.dtsi. As and when DT bindings
>> are accepted, these would be moved to base dtsi.
>>
>> UDMA support in kernel is currently blocked due to on going discussions
>> on how UDMA/ring interrupts needs to be modeled and supported (not a so
>> important for U-Boot UDMA support). But, I submitted patches for U-Boot
>> as UDMA is support is required to support networking on AM654 which is
>> essential for ease of booting the platform with U-Boot.
>>
>>>
>>> The binding is the same for Linux but unfortunately it has not went
>>> through a proper review yet due to the fact that I need to wait for the
>>> interrupt support to arrive to mainline.
>>>
>>> However I have sent an earlier version as RFC:
>>> https://www.spinics.net/lists/dmaengine/msg16661.html
>>>
>>> As for the bindings (and code):
>>> The linux bindings are different:
>>>
>>> - there is no ti,psi-proxy anymore.
>>
>> Will drop this as U-Boot driver does not use them either.
>>
>>> - ringacc uses tisci to get GP ring range and we need
>>>   ti,sci-rm-range-gp-rings property in DT for it
>>> - UDMA also uses tisci to get resource ranges and needs:
>>>   ti,sci-rm-range-tchan, ti,sci-rm-range-rchan, ti,sci-rm-range-rflow in DT
>>> - UDMA does not have/need dma-channels property
>>
>> I plan to align above bindings with Linux as when bindings are accepted
>> into kernel and then port it to U-Boot along with relevant TI-SCI driver
>> code.
>>
>> Meanwhile would it be acceptable to add these nodes in -u-boot.dtsi to
>> get closer to enable tftp?
> 
> ... I knew I should have read the whole thread first.  So, how likely do
> we think the kernel bindings are to see big changes?  I wonder if we
> should re-align things now, or not.
> 

I have posted v2 with bindings that are in full alignment with proposed
Linux bindings that Peter pointed out above. IMO, I dont expect any big
change in these bindings in upstream.
diff mbox series

Patch

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 1820676d7a18..4f37ba7d35eb 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -57,4 +57,6 @@  config APBH_DMA_BURST8
 
 endif
 
+source "drivers/dma/ti/Kconfig"
+
 endmenu # menu "DMA Support"
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index b5f9147e0a54..afab324461b9 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -13,3 +13,5 @@  obj-$(CONFIG_SANDBOX_DMA) += sandbox-dma-test.o
 obj-$(CONFIG_TI_KSNAV) += keystone_nav.o keystone_nav_cfg.o
 obj-$(CONFIG_TI_EDMA3) += ti-edma3.o
 obj-$(CONFIG_DMA_LPC32XX) += lpc32xx_dma.o
+
+obj-y += ti/
diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
new file mode 100644
index 000000000000..3d5498326c42
--- /dev/null
+++ b/drivers/dma/ti/Kconfig
@@ -0,0 +1,14 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+
+if ARCH_K3
+
+config TI_K3_NAVSS_UDMA
+        bool "Texas Instruments UDMA"
+        depends on ARCH_K3
+        select DMA
+        select TI_K3_NAVSS_RINGACC
+        select TI_K3_NAVSS_PSILCFG
+        default n
+        help
+          Support for UDMA used in K3 devices.
+endif
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
new file mode 100644
index 000000000000..de2f9ac91a46
--- /dev/null
+++ b/drivers/dma/ti/Makefile
@@ -0,0 +1,3 @@ 
+# SPDX-License-Identifier: GPL-2.0+
+
+obj-$(CONFIG_TI_K3_NAVSS_UDMA) += k3-udma.o
diff --git a/drivers/dma/ti/k3-udma-hwdef.h b/drivers/dma/ti/k3-udma-hwdef.h
new file mode 100644
index 000000000000..c88399a815ea
--- /dev/null
+++ b/drivers/dma/ti/k3-udma-hwdef.h
@@ -0,0 +1,184 @@ 
+/* SPDX-License-Identifier: GPL-2.0+ */
+/*
+ *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef K3_NAVSS_UDMA_HWDEF_H_
+#define K3_NAVSS_UDMA_HWDEF_H_
+
+#define UDMA_PSIL_DST_THREAD_ID_OFFSET 0x8000
+
+/* Global registers */
+#define UDMA_REV_REG			0x0
+#define UDMA_PERF_CTL_REG		0x4
+#define UDMA_EMU_CTL_REG		0x8
+#define UDMA_PSIL_TO_REG		0x10
+#define UDMA_UTC_CTL_REG		0x1c
+#define UDMA_CAP_REG(i)			(0x20 + (i * 4))
+#define UDMA_RX_FLOW_ID_FW_OES_REG	0x80
+#define UDMA_RX_FLOW_ID_FW_STATUS_REG	0x88
+
+/* RX Flow regs */
+#define UDMA_RFLOW_RFA_REG		0x0
+#define UDMA_RFLOW_RFB_REG		0x4
+#define UDMA_RFLOW_RFC_REG		0x8
+#define UDMA_RFLOW_RFD_REG		0xc
+#define UDMA_RFLOW_RFE_REG		0x10
+#define UDMA_RFLOW_RFF_REG		0x14
+#define UDMA_RFLOW_RFG_REG		0x18
+#define UDMA_RFLOW_RFH_REG		0x1c
+
+#define UDMA_RFLOW_REG(x) (UDMA_RFLOW_RF##x##_REG)
+
+/* TX chan regs */
+#define UDMA_TCHAN_TCFG_REG		0x0
+#define UDMA_TCHAN_TCREDIT_REG		0x4
+#define UDMA_TCHAN_TCQ_REG		0x14
+#define UDMA_TCHAN_TOES_REG(i)		(0x20 + (i) * 4)
+#define UDMA_TCHAN_TEOES_REG		0x60
+#define UDMA_TCHAN_TPRI_CTRL_REG	0x64
+#define UDMA_TCHAN_THREAD_ID_REG	0x68
+#define UDMA_TCHAN_TFIFO_DEPTH_REG	0x70
+#define UDMA_TCHAN_TST_SCHED_REG	0x80
+
+/* RX chan regs */
+#define UDMA_RCHAN_RCFG_REG		0x0
+#define UDMA_RCHAN_RCQ_REG		0x14
+#define UDMA_RCHAN_ROES_REG(i)		(0x20 + (i) * 4)
+#define UDMA_RCHAN_REOES_REG		0x60
+#define UDMA_RCHAN_RPRI_CTRL_REG	0x64
+#define UDMA_RCHAN_THREAD_ID_REG	0x68
+#define UDMA_RCHAN_RST_SCHED_REG	0x80
+#define UDMA_RCHAN_RFLOW_RNG_REG	0xf0
+
+/* TX chan RT regs */
+#define UDMA_TCHAN_RT_CTL_REG		0x0
+#define UDMA_TCHAN_RT_SWTRIG_REG	0x8
+#define UDMA_TCHAN_RT_STDATA_REG	0x80
+
+#define UDMA_TCHAN_RT_PEERn_REG(i)	(0x200 + (i * 0x4))
+#define UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG	\
+	UDMA_TCHAN_RT_PEERn_REG(0)	/* PSI-L: 0x400 */
+#define UDMA_TCHAN_RT_PEER_STATIC_TR_Z_REG	\
+	UDMA_TCHAN_RT_PEERn_REG(1)	/* PSI-L: 0x401 */
+#define UDMA_TCHAN_RT_PEER_BCNT_REG		\
+	UDMA_TCHAN_RT_PEERn_REG(4)	/* PSI-L: 0x404 */
+#define UDMA_TCHAN_RT_PEER_RT_EN_REG		\
+	UDMA_TCHAN_RT_PEERn_REG(8)	/* PSI-L: 0x408 */
+
+#define UDMA_TCHAN_RT_PCNT_REG		0x400
+#define UDMA_TCHAN_RT_BCNT_REG		0x408
+#define UDMA_TCHAN_RT_SBCNT_REG		0x410
+
+/* RX chan RT regs */
+#define UDMA_RCHAN_RT_CTL_REG		0x0
+#define UDMA_RCHAN_RT_SWTRIG_REG	0x8
+#define UDMA_RCHAN_RT_STDATA_REG	0x80
+
+#define UDMA_RCHAN_RT_PEERn_REG(i)	(0x200 + (i * 0x4))
+#define UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG	\
+	UDMA_RCHAN_RT_PEERn_REG(0)	/* PSI-L: 0x400 */
+#define UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG	\
+	UDMA_RCHAN_RT_PEERn_REG(1)	/* PSI-L: 0x401 */
+#define UDMA_RCHAN_RT_PEER_BCNT_REG		\
+	UDMA_RCHAN_RT_PEERn_REG(4)	/* PSI-L: 0x404 */
+#define UDMA_RCHAN_RT_PEER_RT_EN_REG		\
+	UDMA_RCHAN_RT_PEERn_REG(8)	/* PSI-L: 0x408 */
+
+#define UDMA_RCHAN_RT_PCNT_REG		0x400
+#define UDMA_RCHAN_RT_BCNT_REG		0x408
+#define UDMA_RCHAN_RT_SBCNT_REG		0x410
+
+/* UDMA_TCHAN_TCFG_REG/UDMA_RCHAN_RCFG_REG */
+#define UDMA_CHAN_CFG_PAUSE_ON_ERR		BIT(31)
+#define UDMA_TCHAN_CFG_FILT_EINFO		BIT(30)
+#define UDMA_TCHAN_CFG_FILT_PSWORDS		BIT(29)
+#define UDMA_CHAN_CFG_ATYPE_MASK		GENMASK(25, 24)
+#define UDMA_CHAN_CFG_ATYPE_SHIFT		24
+#define UDMA_CHAN_CFG_CHAN_TYPE_MASK		GENMASK(19, 16)
+#define UDMA_CHAN_CFG_CHAN_TYPE_SHIFT		16
+/*
+ * PBVR - using pass by value rings
+ * PBRR - using pass by reference rings
+ * 3RDP - Third Party DMA
+ * BC - Block Copy
+ * SB - single buffer packet mode enabled
+ */
+#define UDMA_CHAN_CFG_CHAN_TYPE_PACKET_PBRR \
+	(2 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
+#define UDMA_CHAN_CFG_CHAN_TYPE_PACKET_SB_PBRR \
+	(3 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
+#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_PBRR \
+	(10 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
+#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_PBVR \
+	(11 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
+#define UDMA_CHAN_CFG_CHAN_TYPE_3RDP_BC_PBRR \
+	(12 << UDMA_CHAN_CFG_CHAN_TYPE_SHIFT)
+#define UDMA_RCHAN_CFG_IGNORE_SHORT		BIT(15)
+#define UDMA_RCHAN_CFG_IGNORE_LONG		BIT(14)
+#define UDMA_TCHAN_CFG_SUPR_TDPKT		BIT(8)
+#define UDMA_CHAN_CFG_FETCH_SIZE_MASK		GENMASK(6, 0)
+#define UDMA_CHAN_CFG_FETCH_SIZE_SHIFT		0
+
+/* UDMA_TCHAN_RT_CTL_REG/UDMA_RCHAN_RT_CTL_REG */
+#define UDMA_CHAN_RT_CTL_EN	BIT(31)
+#define UDMA_CHAN_RT_CTL_TDOWN	BIT(30)
+#define UDMA_CHAN_RT_CTL_PAUSE	BIT(29)
+#define UDMA_CHAN_RT_CTL_FTDOWN	BIT(28)
+#define UDMA_CHAN_RT_CTL_ERROR	BIT(0)
+
+/* UDMA_TCHAN_RT_PEER_RT_EN_REG/UDMA_RCHAN_RT_PEER_RT_EN_REG (PSI-L: 0x408) */
+#define UDMA_PEER_RT_EN_ENABLE		BIT(31)
+#define UDMA_PEER_RT_EN_TEARDOWN	BIT(30)
+#define UDMA_PEER_RT_EN_PAUSE		BIT(29)
+#define UDMA_PEER_RT_EN_FLUSH		BIT(28)
+#define UDMA_PEER_RT_EN_IDLE		BIT(1)
+
+/* RX Flow reg RFA */
+#define UDMA_RFLOW_RFA_EINFO			BIT(30)
+#define UDMA_RFLOW_RFA_PSINFO			BIT(29)
+#define UDMA_RFLOW_RFA_ERR_HANDLING		BIT(28)
+#define UDMA_RFLOW_RFA_DESC_TYPE_MASK		GENMASK(27, 26)
+#define UDMA_RFLOW_RFA_DESC_TYPE_SHIFT		26
+#define UDMA_RFLOW_RFA_PS_LOC			BIT(25)
+#define UDMA_RFLOW_RFA_SOP_OFF_MASK		GENMASK(24, 16)
+#define UDMA_RFLOW_RFA_SOP_OFF_SHIFT		16
+#define UDMA_RFLOW_RFA_DEST_QNUM_MASK		GENMASK(15, 0)
+#define UDMA_RFLOW_RFA_DEST_QNUM_SHIFT		0
+
+/* RX Flow reg RFC */
+#define UDMA_RFLOW_RFC_SRC_TAG_HI_SEL_SHIFT	28
+#define UDMA_RFLOW_RFC_SRC_TAG_LO_SEL_SHIFT	24
+#define UDMA_RFLOW_RFC_DST_TAG_HI_SEL_SHIFT	20
+#define UDMA_RFLOW_RFC_DST_TAG_LO_SE_SHIFT	16
+
+/*
+ * UDMA_TCHAN_RT_PEER_STATIC_TR_XY_REG /
+ * UDMA_RCHAN_RT_PEER_STATIC_TR_XY_REG
+ */
+#define PDMA_STATIC_TR_X_MASK		GENMASK(26, 24)
+#define PDMA_STATIC_TR_X_SHIFT		(24)
+#define PDMA_STATIC_TR_Y_MASK		GENMASK(11, 0)
+#define PDMA_STATIC_TR_Y_SHIFT		(0)
+
+#define PDMA_STATIC_TR_Y(x)	\
+	(((x) << PDMA_STATIC_TR_Y_SHIFT) & PDMA_STATIC_TR_Y_MASK)
+#define PDMA_STATIC_TR_X(x)	\
+	(((x) << PDMA_STATIC_TR_X_SHIFT) & PDMA_STATIC_TR_X_MASK)
+
+/*
+ * UDMA_TCHAN_RT_PEER_STATIC_TR_Z_REG /
+ * UDMA_RCHAN_RT_PEER_STATIC_TR_Z_REG
+ */
+#define PDMA_STATIC_TR_Z_MASK		GENMASK(11, 0)
+#define PDMA_STATIC_TR_Z_SHIFT		(0)
+#define PDMA_STATIC_TR_Z(x)	\
+	(((x) << PDMA_STATIC_TR_Z_SHIFT) & PDMA_STATIC_TR_Z_MASK)
+
+#endif /* K3_NAVSS_UDMA_HWDEF_H_ */
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
new file mode 100644
index 000000000000..af3f649790bf
--- /dev/null
+++ b/drivers/dma/ti/k3-udma.c
@@ -0,0 +1,1749 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+#define pr_fmt(fmt) "udma: " fmt
+
+#include <common.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <malloc.h>
+#include <asm/dma-mapping.h>
+#include <dm.h>
+#include <dm/read.h>
+#include <dm/of_access.h>
+#include <dma.h>
+#include <dma-uclass.h>
+#include <linux/delay.h>
+#include <dt-bindings/dma/k3-udma.h>
+#include <linux/soc/ti/k3-navss-ringacc.h>
+#include <linux/soc/ti/cppi5.h>
+#include <linux/soc/ti/ti-udma.h>
+#include <linux/soc/ti/ti_sci_protocol.h>
+
+#include "k3-udma-hwdef.h"
+
+#if BITS_PER_LONG == 64
+#define RINGACC_RING_USE_PROXY	(0)
+#else
+#define RINGACC_RING_USE_PROXY	(1)
+#endif
+
+struct udma_chan;
+
+enum udma_mmr {
+	MMR_GCFG = 0,
+	MMR_RCHANRT,
+	MMR_TCHANRT,
+	MMR_LAST,
+};
+
+static const char * const mmr_names[] = {
+	"gcfg", "rchanrt", "tchanrt"
+};
+
+struct udma_tchan {
+	void __iomem *reg_rt;
+
+	int id;
+	struct k3_nav_ring *t_ring; /* Transmit ring */
+	struct k3_nav_ring *tc_ring; /* Transmit Completion ring */
+};
+
+struct udma_rchan {
+	void __iomem *reg_rt;
+
+	int id;
+	struct k3_nav_ring *fd_ring; /* Free Descriptor ring */
+	struct k3_nav_ring *r_ring; /* Receive ring*/
+};
+
+struct udma_rflow {
+	int id;
+};
+
+struct udma_dev {
+	struct device *dev;
+	void __iomem *mmrs[MMR_LAST];
+
+	struct k3_nav_ringacc *ringacc;
+
+	u32 features;
+
+	int tchan_cnt;
+	int echan_cnt;
+	int rchan_cnt;
+	int rflow_cnt;
+	unsigned long *tchan_map;
+	unsigned long *rchan_map;
+	unsigned long *rflow_map;
+
+	struct udma_tchan *tchans;
+	struct udma_rchan *rchans;
+	struct udma_rflow *rflows;
+
+	struct udma_chan *channels;
+	u32 psil_base;
+
+	u32 ch_count;
+	const struct ti_sci_handle *tisci;
+	const struct ti_sci_rm_udmap_ops *tisci_udmap_ops;
+	const struct ti_sci_rm_psil_ops *tisci_psil_ops;
+	u32  tisci_dev_id;
+	u32  tisci_navss_dev_id;
+};
+
+struct udma_chan {
+	struct udma_dev *ud;
+	char name[20];
+
+	struct udma_tchan *tchan;
+	struct udma_rchan *rchan;
+	struct udma_rflow *rflow;
+
+	u32 bcnt; /* number of bytes completed since the start of the channel */
+
+	bool pkt_mode; /* TR or packet */
+	bool needs_epib; /* EPIB is needed for the communication or not */
+	u32 psd_size; /* size of Protocol Specific Data */
+	u32 metadata_size; /* (needs_epib ? 16:0) + psd_size */
+	int slave_thread_id;
+	u32 src_thread;
+	u32 dst_thread;
+	u32 static_tr_type;
+
+	u32 id;
+	enum dma_direction dir;
+
+	struct cppi5_host_desc_t *desc_tx;
+	u32 hdesc_size;
+	bool in_use;
+	void	*desc_rx;
+	u32	num_rx_bufs;
+	u32	desc_rx_cur;
+
+};
+
+#define UDMA_CH_1000(ch)		(ch * 0x1000)
+#define UDMA_CH_100(ch)			(ch * 0x100)
+#define UDMA_CH_40(ch)			(ch * 0x40)
+
+#ifdef PKTBUFSRX
+#define UDMA_RX_DESC_NUM PKTBUFSRX
+#else
+#define UDMA_RX_DESC_NUM 4
+#endif
+
+#ifdef DEBUG
+#define	k3_udma_dbg(arg...) pr_err(arg)
+#define	k3_udma_dev_dbg(dev, arg...) dev_err(dev, arg)
+static void k3_udma_print_buf(ulong addr, const void *data, uint width,
+			      uint count, uint linelen)
+{
+	print_buffer(addr, data, width, count, linelen);
+}
+#else
+#define	k3_udma_dbg(arg...)
+#define	k3_udma_dev_dbg(arg...)
+static void k3_udma_print_buf(ulong addr, const void *data, uint width,
+			      uint count, uint linelen)
+{}
+#endif
+
+/* Generic register access functions */
+static inline u32 udma_read(void __iomem *base, int reg)
+{
+	u32 v;
+
+	v = __raw_readl(base + reg);
+	k3_udma_dbg("READL(32): v(%08X)<--reg(%p)\n", v, base + reg);
+	return v;
+}
+
+static inline void udma_write(void __iomem *base, int reg, u32 val)
+{
+	k3_udma_dbg("WRITEL(32): v(%08X)-->reg(%p)\n", val, base + reg);
+	__raw_writel(val, base + reg);
+}
+
+static inline void udma_update_bits(void __iomem *base, int reg,
+				    u32 mask, u32 val)
+{
+	u32 tmp, orig;
+
+	orig = udma_read(base, reg);
+	tmp = orig & ~mask;
+	tmp |= (val & mask);
+
+	if (tmp != orig)
+		udma_write(base, reg, tmp);
+}
+
+/* TCHANRT */
+static inline u32 udma_tchanrt_read(struct udma_tchan *tchan, int reg)
+{
+	if (!tchan)
+		return 0;
+	return udma_read(tchan->reg_rt, reg);
+}
+
+static inline void udma_tchanrt_write(struct udma_tchan *tchan,
+				      int reg, u32 val)
+{
+	if (!tchan)
+		return;
+	udma_write(tchan->reg_rt, reg, val);
+}
+
+/* RCHANRT */
+static inline u32 udma_rchanrt_read(struct udma_rchan *rchan, int reg)
+{
+	if (!rchan)
+		return 0;
+	return udma_read(rchan->reg_rt, reg);
+}
+
+static inline void udma_rchanrt_write(struct udma_rchan *rchan,
+				      int reg, u32 val)
+{
+	if (!rchan)
+		return;
+	udma_write(rchan->reg_rt, reg, val);
+}
+
+static inline int udma_navss_psil_pair(struct udma_dev *ud, u32 src_thread,
+				       u32 dst_thread)
+{
+	dst_thread |= UDMA_PSIL_DST_THREAD_ID_OFFSET;
+	return ud->tisci_psil_ops->pair(ud->tisci,
+					ud->tisci_navss_dev_id,
+					src_thread, dst_thread);
+}
+
+static inline int udma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
+					 u32 dst_thread)
+{
+	dst_thread |= UDMA_PSIL_DST_THREAD_ID_OFFSET;
+	return ud->tisci_psil_ops->unpair(ud->tisci,
+					  ud->tisci_navss_dev_id,
+					  src_thread, dst_thread);
+}
+
+static inline char *udma_get_dir_text(enum dma_direction dir)
+{
+	switch (dir) {
+	case DMA_DEV_TO_MEM:
+		return "DEV_TO_MEM";
+	case DMA_MEM_TO_DEV:
+		return "MEM_TO_DEV";
+	case DMA_MEM_TO_MEM:
+		return "MEM_TO_MEM";
+	case DMA_DEV_TO_DEV:
+		return "DEV_TO_DEV";
+	default:
+		break;
+	}
+
+	return "invalid";
+}
+
+static inline bool udma_is_chan_running(struct udma_chan *uc)
+{
+	u32 trt_ctl = 0;
+	u32 rrt_ctl = 0;
+
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		rrt_ctl = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_CTL_REG);
+		k3_udma_dbg("%s: rrt_ctl: 0x%08x (peer: 0x%08x)\n",
+			    __func__, rrt_ctl,
+			    udma_rchanrt_read(uc->rchan,
+					      UDMA_RCHAN_RT_PEER_RT_EN_REG));
+		break;
+	case DMA_MEM_TO_DEV:
+		trt_ctl = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG);
+		k3_udma_dbg("%s: trt_ctl: 0x%08x (peer: 0x%08x)\n",
+			    __func__, trt_ctl,
+			    udma_tchanrt_read(uc->tchan,
+					      UDMA_TCHAN_RT_PEER_RT_EN_REG));
+		break;
+	case DMA_MEM_TO_MEM:
+		trt_ctl = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG);
+		rrt_ctl = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_CTL_REG);
+		break;
+	default:
+		break;
+	}
+
+	if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN)
+		return true;
+
+	return false;
+}
+
+static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
+{
+	struct k3_nav_ring *ring = NULL;
+	int ret = -ENOENT;
+
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		ring = uc->rchan->r_ring;
+		break;
+	case DMA_MEM_TO_DEV:
+		ring = uc->tchan->tc_ring;
+		break;
+	case DMA_MEM_TO_MEM:
+		ring = uc->tchan->tc_ring;
+		break;
+	default:
+		break;
+	}
+
+	if (ring && k3_nav_ringacc_ring_get_occ(ring))
+		ret = k3_nav_ringacc_ring_pop(ring, addr);
+
+	return ret;
+}
+
+static void udma_reset_rings(struct udma_chan *uc)
+{
+	struct k3_nav_ring *ring1 = NULL;
+	struct k3_nav_ring *ring2 = NULL;
+
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		ring1 = uc->rchan->fd_ring;
+		ring2 = uc->rchan->r_ring;
+		break;
+	case DMA_MEM_TO_DEV:
+		ring1 = uc->tchan->t_ring;
+		ring2 = uc->tchan->tc_ring;
+		break;
+	case DMA_MEM_TO_MEM:
+		ring1 = uc->tchan->t_ring;
+		ring2 = uc->tchan->tc_ring;
+		break;
+	default:
+		break;
+	}
+
+	if (ring1)
+		k3_nav_ringacc_ring_reset_dma(ring1, 0);
+	if (ring2)
+		k3_nav_ringacc_ring_reset(ring2);
+}
+
+static void udma_reset_counters(struct udma_chan *uc)
+{
+	u32 val;
+
+	if (uc->tchan) {
+		val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_BCNT_REG);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_BCNT_REG, val);
+
+		val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_SBCNT_REG);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_SBCNT_REG, val);
+
+		val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PCNT_REG);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PCNT_REG, val);
+
+		val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_BCNT_REG, val);
+	}
+
+	if (uc->rchan) {
+		val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_BCNT_REG);
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_BCNT_REG, val);
+
+		val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_SBCNT_REG);
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_SBCNT_REG, val);
+
+		val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_PCNT_REG);
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PCNT_REG, val);
+
+		val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_PEER_BCNT_REG);
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_BCNT_REG, val);
+	}
+
+	uc->bcnt = 0;
+}
+
+static inline int udma_stop_hard(struct udma_chan *uc)
+{
+	k3_udma_dbg("%s: ENTER (chan%d)\n", __func__, uc->id);
+
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG, 0);
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, 0);
+		break;
+	case DMA_MEM_TO_DEV:
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, 0);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG, 0);
+		break;
+	case DMA_MEM_TO_MEM:
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, 0);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int udma_start(struct udma_chan *uc)
+{
+	/* Channel is already running, no need to proceed further */
+	if (udma_is_chan_running(uc))
+		goto out;
+
+	k3_udma_dbg("%s: chan:%d dir:%s (static_tr_type: %d)\n",
+		    __func__, uc->id, udma_get_dir_text(uc->dir),
+		    uc->static_tr_type);
+
+	/* Make sure that we clear the teardown bit, if it is set */
+	udma_stop_hard(uc);
+
+	/* Reset all counters */
+	udma_reset_counters(uc);
+
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG,
+				   UDMA_CHAN_RT_CTL_EN);
+
+		/* Enable remote */
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG,
+				   UDMA_PEER_RT_EN_ENABLE);
+
+		k3_udma_dbg("%s(rx): RT_CTL:0x%08x PEER RT_ENABLE:0x%08x\n",
+			    __func__,
+			    udma_rchanrt_read(uc->rchan,
+					      UDMA_RCHAN_RT_CTL_REG),
+			    udma_rchanrt_read(uc->rchan,
+					      UDMA_RCHAN_RT_PEER_RT_EN_REG));
+		break;
+	case DMA_MEM_TO_DEV:
+		/* Enable remote */
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG,
+				   UDMA_PEER_RT_EN_ENABLE);
+
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG,
+				   UDMA_CHAN_RT_CTL_EN);
+
+		k3_udma_dbg("%s(tx): RT_CTL:0x%08x PEER RT_ENABLE:0x%08x\n",
+			    __func__,
+			    udma_rchanrt_read(uc->rchan,
+					      UDMA_TCHAN_RT_CTL_REG),
+			    udma_rchanrt_read(uc->rchan,
+					      UDMA_TCHAN_RT_PEER_RT_EN_REG));
+		break;
+	case DMA_MEM_TO_MEM:
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG,
+				   UDMA_CHAN_RT_CTL_EN);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG,
+				   UDMA_CHAN_RT_CTL_EN);
+
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	k3_udma_dbg("%s: DONE chan:%d\n", __func__, uc->id);
+out:
+	return 0;
+}
+
+static inline void udma_stop_mem2dev(struct udma_chan *uc, bool sync)
+{
+	int i = 0;
+	u32 val;
+
+	udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG,
+			   UDMA_CHAN_RT_CTL_EN |
+			   UDMA_CHAN_RT_CTL_TDOWN);
+
+	val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG);
+
+	while (sync && (val & UDMA_CHAN_RT_CTL_EN)) {
+		val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_CTL_REG);
+		udelay(1);
+		if (i > 1000) {
+			printf(" %s TIMEOUT !\n", __func__);
+			break;
+		}
+		i++;
+	}
+
+	val = udma_tchanrt_read(uc->tchan, UDMA_TCHAN_RT_PEER_RT_EN_REG);
+	if (val & UDMA_PEER_RT_EN_ENABLE)
+		printf("%s: peer not stopped TIMEOUT !\n", __func__);
+}
+
+static inline void udma_stop_dev2mem(struct udma_chan *uc, bool sync)
+{
+	int i = 0;
+	u32 val;
+
+	udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG,
+			   UDMA_PEER_RT_EN_ENABLE |
+			   UDMA_PEER_RT_EN_TEARDOWN);
+
+	val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_CTL_REG);
+
+	while (sync && (val & UDMA_CHAN_RT_CTL_EN)) {
+		val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_CTL_REG);
+		udelay(1);
+		if (i > 1000) {
+			printf("%s TIMEOUT !\n", __func__);
+			break;
+		}
+		i++;
+	}
+
+	val = udma_rchanrt_read(uc->rchan, UDMA_RCHAN_RT_PEER_RT_EN_REG);
+	if (val & UDMA_PEER_RT_EN_ENABLE)
+		printf("%s: peer not stopped TIMEOUT !\n", __func__);
+}
+
+static inline int udma_stop(struct udma_chan *uc)
+{
+	k3_udma_dbg("%s: chan:%d dir:%s\n",
+		    __func__, uc->id, udma_get_dir_text(uc->dir));
+
+	udma_reset_counters(uc);
+	switch (uc->dir) {
+	case DMA_DEV_TO_MEM:
+		udma_stop_dev2mem(uc, true);
+		break;
+	case DMA_MEM_TO_DEV:
+		udma_stop_mem2dev(uc, true);
+		break;
+	case DMA_MEM_TO_MEM:
+		udma_rchanrt_write(uc->rchan, UDMA_RCHAN_RT_CTL_REG, 0);
+		udma_tchanrt_write(uc->tchan, UDMA_TCHAN_RT_CTL_REG, 0);
+		break;
+	default:
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static void udma_poll_completion(struct udma_chan *uc, dma_addr_t *paddr)
+{
+	int i = 1;
+
+	while (udma_pop_from_ring(uc, paddr)) {
+		udelay(1);
+		if (!(i % 1000000))
+			printf(".");
+		i++;
+	}
+}
+
+#define UDMA_RESERVE_RESOURCE(res)					\
+static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud,	\
+					       int id)			\
+{									\
+	if (id >= 0) {							\
+		if (test_bit(id, ud->res##_map)) {			\
+			dev_err(ud->dev, "res##%d is in use\n", id);	\
+			return ERR_PTR(-ENOENT);			\
+		}							\
+	} else {							\
+		id = find_first_zero_bit(ud->res##_map, ud->res##_cnt); \
+		if (id == ud->res##_cnt) {				\
+			return ERR_PTR(-ENOENT);			\
+		}							\
+	}								\
+									\
+	__set_bit(id, ud->res##_map);					\
+	return &ud->res##s[id];						\
+}
+
+UDMA_RESERVE_RESOURCE(tchan);
+UDMA_RESERVE_RESOURCE(rchan);
+UDMA_RESERVE_RESOURCE(rflow);
+
+static int udma_get_tchan(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->tchan) {
+		dev_dbg(ud->dev, "chan%d: already have tchan%d allocated\n",
+			uc->id, uc->tchan->id);
+		return 0;
+	}
+
+	uc->tchan = __udma_reserve_tchan(ud, -1);
+	if (IS_ERR(uc->tchan))
+		return PTR_ERR(uc->tchan);
+
+	k3_udma_dbg("chan%d: got tchan%d\n", uc->id, uc->tchan->id);
+
+	if (udma_is_chan_running(uc)) {
+		dev_warn(ud->dev, "chan%d: tchan%d is running!\n", uc->id,
+			 uc->tchan->id);
+		udma_stop(uc);
+		if (udma_is_chan_running(uc))
+			dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+	}
+
+	return 0;
+}
+
+static int udma_get_rchan(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->rchan) {
+		dev_dbg(ud->dev, "chan%d: already have rchan%d allocated\n",
+			uc->id, uc->rchan->id);
+		return 0;
+	}
+
+	uc->rchan = __udma_reserve_rchan(ud, -1);
+	if (IS_ERR(uc->rchan))
+		return PTR_ERR(uc->rchan);
+
+	k3_udma_dbg("chan%d: got rchan%d\n", uc->id, uc->rchan->id);
+
+	if (udma_is_chan_running(uc)) {
+		dev_warn(ud->dev, "chan%d: rchan%d is running!\n", uc->id,
+			 uc->rchan->id);
+		udma_stop(uc);
+		if (udma_is_chan_running(uc))
+			dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+	}
+
+	return 0;
+}
+
+static int udma_get_chan_pair(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+	int chan_id, end;
+
+	if ((uc->tchan && uc->rchan) && uc->tchan->id == uc->rchan->id) {
+		dev_info(ud->dev, "chan%d: already have %d pair allocated\n",
+			 uc->id, uc->tchan->id);
+		return 0;
+	}
+
+	if (uc->tchan) {
+		dev_err(ud->dev, "chan%d: already have tchan%d allocated\n",
+			uc->id, uc->tchan->id);
+		return -EBUSY;
+	} else if (uc->rchan) {
+		dev_err(ud->dev, "chan%d: already have rchan%d allocated\n",
+			uc->id, uc->rchan->id);
+		return -EBUSY;
+	}
+
+	/* Can be optimized, but let's have it like this for now */
+	end = min(ud->tchan_cnt, ud->rchan_cnt);
+	for (chan_id = 0; chan_id < end; chan_id++) {
+		if (!test_bit(chan_id, ud->tchan_map) &&
+		    !test_bit(chan_id, ud->rchan_map))
+			break;
+	}
+
+	if (chan_id == end)
+		return -ENOENT;
+
+	__set_bit(chan_id, ud->tchan_map);
+	__set_bit(chan_id, ud->rchan_map);
+	uc->tchan = &ud->tchans[chan_id];
+	uc->rchan = &ud->rchans[chan_id];
+
+	k3_udma_dbg("chan%d: got t/rchan%d pair\n", uc->id, chan_id);
+
+	if (udma_is_chan_running(uc)) {
+		dev_warn(ud->dev, "chan%d: t/rchan%d pair is running!\n",
+			 uc->id, chan_id);
+		udma_stop(uc);
+		if (udma_is_chan_running(uc))
+			dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
+	}
+
+	return 0;
+}
+
+static int udma_get_rflow(struct udma_chan *uc, int flow_id)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->rflow) {
+		dev_dbg(ud->dev, "chan%d: already have rflow%d allocated\n",
+			uc->id, uc->rflow->id);
+		return 0;
+	}
+
+	if (!uc->rchan)
+		dev_warn(ud->dev, "chan%d: does not have rchan??\n", uc->id);
+
+	uc->rflow = __udma_reserve_rflow(ud, flow_id);
+	if (IS_ERR(uc->rflow))
+		return PTR_ERR(uc->rflow);
+
+	k3_udma_dbg("chan%d: got rflow%d\n", uc->id, uc->rflow->id);
+	return 0;
+}
+
+static void udma_put_rchan(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->rchan) {
+		dev_dbg(ud->dev, "chan%d: put rchan%d\n", uc->id,
+			uc->rchan->id);
+		__clear_bit(uc->rchan->id, ud->rchan_map);
+		uc->rchan = NULL;
+	}
+}
+
+static void udma_put_tchan(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->tchan) {
+		dev_dbg(ud->dev, "chan%d: put tchan%d\n", uc->id,
+			uc->tchan->id);
+		__clear_bit(uc->tchan->id, ud->tchan_map);
+		uc->tchan = NULL;
+	}
+}
+
+static void udma_put_rflow(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+
+	if (uc->rflow) {
+		dev_dbg(ud->dev, "chan%d: put rflow%d\n", uc->id,
+			uc->rflow->id);
+		__clear_bit(uc->rflow->id, ud->rflow_map);
+		uc->rflow = NULL;
+	}
+}
+
+static void udma_free_tx_resources(struct udma_chan *uc)
+{
+	if (!uc->tchan)
+		return;
+
+	k3_nav_ringacc_ring_free(uc->tchan->t_ring);
+	k3_nav_ringacc_ring_free(uc->tchan->tc_ring);
+	uc->tchan->t_ring = NULL;
+	uc->tchan->tc_ring = NULL;
+
+	udma_put_tchan(uc);
+}
+
+static int udma_alloc_tx_resources(struct udma_chan *uc)
+{
+	struct k3_nav_ring_cfg ring_cfg;
+	struct udma_dev *ud = uc->ud;
+	int ret;
+
+	ret = udma_get_tchan(uc);
+	if (ret)
+		return ret;
+
+	uc->tchan->t_ring = k3_nav_ringacc_request_ring(
+				ud->ringacc, uc->tchan->id,
+				RINGACC_RING_USE_PROXY);
+	if (!uc->tchan->t_ring) {
+		ret = -EBUSY;
+		goto err_tx_ring;
+	}
+
+	uc->tchan->tc_ring = k3_nav_ringacc_request_ring(
+				ud->ringacc, -1, RINGACC_RING_USE_PROXY);
+	if (!uc->tchan->tc_ring) {
+		ret = -EBUSY;
+		goto err_txc_ring;
+	}
+
+	memset(&ring_cfg, 0, sizeof(ring_cfg));
+	ring_cfg.size = 16;
+	ring_cfg.elm_size = K3_NAV_RINGACC_RING_ELSIZE_8;
+	ring_cfg.mode = K3_NAV_RINGACC_RING_MODE_MESSAGE;
+
+	ret = k3_nav_ringacc_ring_cfg(uc->tchan->t_ring, &ring_cfg);
+	ret |= k3_nav_ringacc_ring_cfg(uc->tchan->tc_ring, &ring_cfg);
+
+	if (ret)
+		goto err_ringcfg;
+
+	return 0;
+
+err_ringcfg:
+	k3_nav_ringacc_ring_free(uc->tchan->tc_ring);
+	uc->tchan->tc_ring = NULL;
+err_txc_ring:
+	k3_nav_ringacc_ring_free(uc->tchan->t_ring);
+	uc->tchan->t_ring = NULL;
+err_tx_ring:
+	udma_put_tchan(uc);
+
+	return ret;
+}
+
+static void udma_free_rx_resources(struct udma_chan *uc)
+{
+	if (!uc->rchan)
+		return;
+
+	k3_nav_ringacc_ring_free(uc->rchan->fd_ring);
+	k3_nav_ringacc_ring_free(uc->rchan->r_ring);
+	uc->rchan->fd_ring = NULL;
+	uc->rchan->r_ring = NULL;
+
+	udma_put_rflow(uc);
+	udma_put_rchan(uc);
+}
+
+static int udma_alloc_rx_resources(struct udma_chan *uc)
+{
+	struct k3_nav_ring_cfg ring_cfg;
+	struct udma_dev *ud = uc->ud;
+	int fd_ring_id;
+	int ret;
+
+	ret = udma_get_rchan(uc);
+	if (ret)
+		return ret;
+
+	/* For MEM_TO_MEM we don't need rflow or rings */
+	if (uc->dir == DMA_MEM_TO_MEM)
+		return 0;
+
+	ret = udma_get_rflow(uc, uc->rchan->id);
+	if (ret) {
+		ret = -EBUSY;
+		goto err_rflow;
+	}
+
+	fd_ring_id = ud->tchan_cnt + ud->echan_cnt + uc->rchan->id;
+
+	uc->rchan->fd_ring = k3_nav_ringacc_request_ring(
+				ud->ringacc, fd_ring_id,
+				RINGACC_RING_USE_PROXY);
+	if (!uc->rchan->fd_ring) {
+		ret = -EBUSY;
+		goto err_rx_ring;
+	}
+
+	uc->rchan->r_ring = k3_nav_ringacc_request_ring(
+				ud->ringacc, -1, RINGACC_RING_USE_PROXY);
+	if (!uc->rchan->r_ring) {
+		ret = -EBUSY;
+		goto err_rxc_ring;
+	}
+
+	memset(&ring_cfg, 0, sizeof(ring_cfg));
+	ring_cfg.size = 16;
+	ring_cfg.elm_size = K3_NAV_RINGACC_RING_ELSIZE_8;
+	ring_cfg.mode = K3_NAV_RINGACC_RING_MODE_MESSAGE;
+
+	ret = k3_nav_ringacc_ring_cfg(uc->rchan->fd_ring, &ring_cfg);
+	ret |= k3_nav_ringacc_ring_cfg(uc->rchan->r_ring, &ring_cfg);
+
+	if (ret)
+		goto err_ringcfg;
+
+	return 0;
+
+err_ringcfg:
+	k3_nav_ringacc_ring_free(uc->rchan->r_ring);
+	uc->rchan->r_ring = NULL;
+err_rxc_ring:
+	k3_nav_ringacc_ring_free(uc->rchan->fd_ring);
+	uc->rchan->fd_ring = NULL;
+err_rx_ring:
+	udma_put_rflow(uc);
+err_rflow:
+	udma_put_rchan(uc);
+
+	return ret;
+}
+
+static int udma_alloc_tchan_sci_req(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+	int tc_ring = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
+	struct ti_sci_msg_rm_udmap_tx_ch_cfg req;
+	u32 mode;
+	int ret;
+
+	if (uc->pkt_mode)
+		mode = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR;
+	else
+		mode = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR;
+
+	req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID;
+	req.nav_id = ud->tisci_dev_id;
+	req.index = uc->tchan->id;
+	req.tx_chan_type = mode;
+	if (uc->dir == DMA_MEM_TO_MEM)
+		req.tx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2;
+	else
+		req.tx_fetch_size = cppi5_hdesc_calc_size(uc->needs_epib,
+							  uc->psd_size,
+							  0) >> 2;
+	req.txcq_qnum = tc_ring;
+
+	ret = ud->tisci_udmap_ops->tx_ch_cfg(ud->tisci, &req);
+	if (ret)
+		dev_err(ud->dev, "tisci tx alloc failed %d\n", ret);
+
+	return ret;
+}
+
+static int udma_alloc_rchan_sci_req(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+	int fd_ring = k3_nav_ringacc_get_ring_id(uc->rchan->fd_ring);
+	int rx_ring = k3_nav_ringacc_get_ring_id(uc->rchan->r_ring);
+	int tc_ring = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
+	struct ti_sci_msg_rm_udmap_rx_ch_cfg req = { 0 };
+	struct ti_sci_msg_rm_udmap_flow_cfg flow_req = { 0 };
+	u32 mode;
+	int ret;
+
+	if (uc->pkt_mode)
+		mode = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR;
+	else
+		mode = TI_SCI_RM_UDMAP_CHAN_TYPE_3RDP_BCOPY_PBRR;
+
+	req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_FETCH_SIZE_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_CQ_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID;
+	req.nav_id = ud->tisci_dev_id;
+	req.index = uc->rchan->id;
+	req.rx_chan_type = mode;
+	if (uc->dir == DMA_MEM_TO_MEM) {
+		req.rx_fetch_size = sizeof(struct cppi5_desc_hdr_t) >> 2;
+		req.rxcq_qnum = tc_ring;
+	} else {
+		req.rx_fetch_size = cppi5_hdesc_calc_size(uc->needs_epib,
+							  uc->psd_size,
+							  0) >> 2;
+		req.rxcq_qnum = rx_ring;
+	}
+	if (uc->rflow->id != uc->rchan->id && uc->dir != DMA_MEM_TO_MEM) {
+		req.flowid_start = uc->rflow->id;
+		req.flowid_cnt = 1;
+		req.valid_params |=
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID;
+	}
+
+	ret = ud->tisci_udmap_ops->rx_ch_cfg(ud->tisci, &req);
+	if (ret) {
+		dev_err(ud->dev, "tisci rx %u cfg failed %d\n",
+			uc->rchan->id, ret);
+		return ret;
+	}
+	if (uc->dir == DMA_MEM_TO_MEM)
+		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 |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_DESC_TYPE_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_DEST_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_SRC_TAG_HI_SEL_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_SRC_TAG_LO_SEL_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_DEST_TAG_HI_SEL_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_DEST_TAG_LO_SEL_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_FDQ0_SZ0_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_FDQ1_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_FDQ2_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_FDQ3_QNUM_VALID |
+			TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_PS_LOCATION_VALID;
+
+	flow_req.nav_id = ud->tisci_dev_id;
+	flow_req.flow_index = uc->rflow->id;
+
+	if (uc->needs_epib)
+		flow_req.rx_einfo_present = 1;
+	else
+		flow_req.rx_einfo_present = 0;
+
+	if (uc->psd_size)
+		flow_req.rx_psinfo_present = 1;
+	else
+		flow_req.rx_psinfo_present = 0;
+
+	flow_req.rx_error_handling = 0;
+	flow_req.rx_desc_type = 0;
+	flow_req.rx_dest_qnum = rx_ring;
+	flow_req.rx_src_tag_hi_sel = 2;
+	flow_req.rx_src_tag_lo_sel = 4;
+	flow_req.rx_dest_tag_hi_sel = 5;
+	flow_req.rx_dest_tag_lo_sel = 4;
+	flow_req.rx_fdq0_sz0_qnum = fd_ring;
+	flow_req.rx_fdq1_qnum = fd_ring;
+	flow_req.rx_fdq2_qnum = fd_ring;
+	flow_req.rx_fdq3_qnum = fd_ring;
+	flow_req.rx_ps_location = 0;
+
+	ret = ud->tisci_udmap_ops->rx_flow_cfg(ud->tisci, &flow_req);
+	if (ret)
+		dev_err(ud->dev, "tisci rx %u flow %u cfg failed %d\n",
+			uc->rchan->id, uc->rflow->id, ret);
+
+	return ret;
+}
+
+static int udma_alloc_chan_resources(struct udma_chan *uc)
+{
+	struct udma_dev *ud = uc->ud;
+	int ret;
+
+	k3_udma_dbg("%s: chan:%d as %s\n",
+		    __func__, uc->id, udma_get_dir_text(uc->dir));
+
+	switch (uc->dir) {
+	case DMA_MEM_TO_MEM:
+		/* Non synchronized - mem to mem type of transfer */
+		ret = udma_get_chan_pair(uc);
+		if (ret)
+			return ret;
+
+		ret = udma_alloc_tx_resources(uc);
+		if (ret)
+			goto err_free_res;
+
+		ret = udma_alloc_rx_resources(uc);
+		if (ret)
+			goto err_free_res;
+
+		uc->src_thread = ud->psil_base + uc->tchan->id;
+		uc->dst_thread = (ud->psil_base + uc->rchan->id) | 0x8000;
+		break;
+	case DMA_MEM_TO_DEV:
+		/* Slave transfer synchronized - mem to dev (TX) trasnfer */
+		ret = udma_alloc_tx_resources(uc);
+		if (ret)
+			goto err_free_res;
+
+		uc->src_thread = ud->psil_base + uc->tchan->id;
+		uc->dst_thread = uc->slave_thread_id;
+		if (!(uc->dst_thread & 0x8000))
+			uc->dst_thread |= 0x8000;
+
+		break;
+	case DMA_DEV_TO_MEM:
+		/* Slave transfer synchronized - dev to mem (RX) trasnfer */
+		ret = udma_alloc_rx_resources(uc);
+		if (ret)
+			goto err_free_res;
+
+		uc->src_thread = uc->slave_thread_id;
+		uc->dst_thread = (ud->psil_base + uc->rchan->id) | 0x8000;
+
+		break;
+	default:
+		/* Can not happen */
+		k3_udma_dbg("%s: chan:%d invalid direction (%u)\n",
+			    __func__, uc->id, uc->dir);
+		return -EINVAL;
+	}
+
+	/* We have channel indexes and rings */
+	if (uc->dir == DMA_MEM_TO_MEM) {
+		ret = udma_alloc_tchan_sci_req(uc);
+		if (ret)
+			goto err_free_res;
+
+		ret = udma_alloc_rchan_sci_req(uc);
+		if (ret)
+			goto err_free_res;
+	} else {
+		/* Slave transfer */
+		if (uc->dir == DMA_MEM_TO_DEV) {
+			ret = udma_alloc_tchan_sci_req(uc);
+			if (ret)
+				goto err_free_res;
+		} else {
+			ret = udma_alloc_rchan_sci_req(uc);
+			if (ret)
+				goto err_free_res;
+		}
+	}
+
+	/* PSI-L pairing */
+	ret = udma_navss_psil_pair(ud, uc->src_thread, uc->dst_thread);
+	if (ret) {
+		dev_err(ud->dev, "k3_nav_psil_request_link fail\n");
+		goto err_free_res;
+	}
+
+	return 0;
+
+err_free_res:
+	udma_free_tx_resources(uc);
+	udma_free_rx_resources(uc);
+	uc->slave_thread_id = -1;
+	return ret;
+}
+
+static void udma_free_chan_resources(struct udma_chan *uc)
+{
+	/* Some configuration to UDMA-P channel: disable, reset, whatever */
+
+	/* Release PSI-L pairing */
+	udma_navss_psil_unpair(uc->ud, uc->src_thread, uc->dst_thread);
+
+	/* Reset the rings for a new start */
+	udma_reset_rings(uc);
+	udma_free_tx_resources(uc);
+	udma_free_rx_resources(uc);
+
+	uc->slave_thread_id = -1;
+	uc->dir = DMA_MEM_TO_MEM;
+}
+
+static int udma_get_mmrs(struct udevice *dev)
+{
+	struct udma_dev *ud = dev_get_priv(dev);
+	int i;
+
+	for (i = 0; i < MMR_LAST; i++) {
+		ud->mmrs[i] = (uint32_t *)devfdt_get_addr_name(dev,
+				mmr_names[i]);
+		if (!ud->mmrs[i])
+			return -EINVAL;
+	}
+
+	return 0;
+}
+
+#define UDMA_MAX_CHANNELS	192
+
+static int udma_probe(struct udevice *dev)
+{
+	struct dma_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	struct udma_dev *ud = dev_get_priv(dev);
+	int i, ret;
+	u32 cap2, cap3;
+	struct udevice *tmp;
+	struct udevice *tisci_dev = NULL;
+
+	ret = udma_get_mmrs(dev);
+	if (ret)
+		return ret;
+
+	ret = uclass_get_device_by_phandle(UCLASS_MISC, dev,
+					   "ti,ringacc", &tmp);
+	ud->ringacc = dev_get_priv(tmp);
+	if (IS_ERR(ud->ringacc))
+		return PTR_ERR(ud->ringacc);
+
+	ud->ch_count = dev_read_u32_default(dev, "dma-channels", 0);
+	if (!ud->ch_count) {
+		dev_info(dev, "Missing dma-channels property, using %u.\n",
+			 UDMA_MAX_CHANNELS);
+		ud->ch_count = UDMA_MAX_CHANNELS;
+	}
+
+	ud->psil_base = dev_read_u32_default(dev, "ti,psil-base", 0);
+	if (!ud->psil_base) {
+		dev_info(dev,
+			 "Missing ti,psil-base property, using %d.\n", ret);
+		return -EINVAL;
+	}
+
+	ret = uclass_get_device_by_name(UCLASS_FIRMWARE, "dmsc", &tisci_dev);
+	if (ret) {
+		debug("TISCI RA RM get failed (%d)\n", ret);
+		ud->tisci = NULL;
+		return 0;
+	}
+	ud->tisci = (struct ti_sci_handle *)
+			 (ti_sci_get_handle_from_sysfw(tisci_dev));
+
+	ret = dev_read_u32_default(dev, "ti,sci", 0);
+	if (!ret) {
+		dev_err(dev, "TISCI RA RM disabled\n");
+		ud->tisci = NULL;
+	}
+
+	if (ud->tisci) {
+		ofnode navss_ofnode = ofnode_get_parent(dev_ofnode(dev));
+
+		ud->tisci_dev_id = -1;
+		ret = dev_read_u32(dev, "ti,sci-dev-id", &ud->tisci_dev_id);
+		if (ret) {
+			dev_err(dev, "ti,sci-dev-id read failure %d\n", ret);
+			return ret;
+		}
+
+		ud->tisci_navss_dev_id = -1;
+		ret = ofnode_read_u32(navss_ofnode, "ti,sci-dev-id",
+				      &ud->tisci_navss_dev_id);
+		if (ret) {
+			dev_err(dev, "navss sci-dev-id read failure %d\n", ret);
+			return ret;
+		}
+
+		ud->tisci_udmap_ops = &ud->tisci->ops.rm_udmap_ops;
+		ud->tisci_psil_ops = &ud->tisci->ops.rm_psil_ops;
+	}
+
+	cap2 = udma_read(ud->mmrs[MMR_GCFG], 0x28);
+	cap3 = udma_read(ud->mmrs[MMR_GCFG], 0x2c);
+
+	ud->rflow_cnt = cap3 & 0x3fff;
+	ud->tchan_cnt = cap2 & 0x1ff;
+	ud->echan_cnt = (cap2 >> 9) & 0x1ff;
+	ud->rchan_cnt = (cap2 >> 18) & 0x1ff;
+	if ((ud->tchan_cnt + ud->rchan_cnt) != ud->ch_count) {
+		dev_info(dev,
+			 "Channel count mismatch: %u != tchan(%u) + rchan(%u)\n",
+			 ud->ch_count, ud->tchan_cnt, ud->rchan_cnt);
+		ud->ch_count  = ud->tchan_cnt + ud->rchan_cnt;
+	}
+
+	dev_info(dev,
+		 "Number of channels: %u (tchan: %u, echan: %u, rchan: %u dev-id %u)\n",
+		 ud->ch_count, ud->tchan_cnt, ud->echan_cnt, ud->rchan_cnt,
+		 ud->tisci_dev_id);
+	dev_info(dev, "Number of rflows: %u\n", ud->rflow_cnt);
+
+	ud->channels = devm_kcalloc(dev, ud->ch_count, sizeof(*ud->channels),
+				    GFP_KERNEL);
+	ud->tchan_map = devm_kcalloc(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_kcalloc(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_map = 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);
+
+	if (!ud->channels || !ud->tchan_map || !ud->rchan_map ||
+	    !ud->rflow_map || !ud->tchans || !ud->rchans || !ud->rflows)
+		return -ENOMEM;
+
+	for (i = 0; i < ud->tchan_cnt; i++) {
+		struct udma_tchan *tchan = &ud->tchans[i];
+
+		tchan->id = i;
+		tchan->reg_rt = ud->mmrs[MMR_TCHANRT] + UDMA_CH_1000(i);
+	}
+
+	for (i = 0; i < ud->rchan_cnt; i++) {
+		struct udma_rchan *rchan = &ud->rchans[i];
+
+		rchan->id = i;
+		rchan->reg_rt = ud->mmrs[MMR_RCHANRT] + UDMA_CH_1000(i);
+	}
+
+	for (i = 0; i < ud->rflow_cnt; i++) {
+		struct udma_rflow *rflow = &ud->rflows[i];
+
+		rflow->id = i;
+	}
+
+	for (i = 0; i < ud->ch_count; i++) {
+		struct udma_chan *uc = &ud->channels[i];
+
+		uc->ud = ud;
+		uc->id = i;
+		uc->slave_thread_id = -1;
+		uc->tchan = NULL;
+		uc->rchan = NULL;
+		uc->dir = DMA_MEM_TO_MEM;
+		sprintf(uc->name, "UDMA chan%d\n", i);
+		if (!i)
+			uc->in_use = true;
+	}
+
+	k3_udma_dbg("UDMA(rev: 0x%08x) CAP0-3: 0x%08x, 0x%08x, 0x%08x, 0x%08x\n",
+		    udma_read(ud->mmrs[MMR_GCFG], 0),
+		    udma_read(ud->mmrs[MMR_GCFG], 0x20),
+		    udma_read(ud->mmrs[MMR_GCFG], 0x24),
+		    udma_read(ud->mmrs[MMR_GCFG], 0x28),
+		    udma_read(ud->mmrs[MMR_GCFG], 0x2c));
+
+	uc_priv->supported = DMA_SUPPORTS_MEM_TO_MEM | DMA_SUPPORTS_MEM_TO_DEV;
+
+	return ret;
+}
+
+static int *udma_prep_dma_memcpy(struct udma_chan *uc, dma_addr_t dest,
+				 dma_addr_t src, size_t len)
+{
+	u32 tc_ring_id = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
+	struct cppi5_tr_type15_t *tr_req;
+	int num_tr;
+	size_t tr_size = sizeof(struct cppi5_tr_type15_t);
+	u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
+	unsigned long dummy;
+	void *tr_desc;
+	size_t desc_size;
+
+	if (len < SZ_64K) {
+		num_tr = 1;
+		tr0_cnt0 = len;
+		tr0_cnt1 = 1;
+	} else {
+		unsigned long align_to = __ffs(src | dest);
+
+		if (align_to > 3)
+			align_to = 3;
+		/*
+		 * Keep simple: tr0: SZ_64K-alignment blocks,
+		 *		tr1: the remaining
+		 */
+		num_tr = 2;
+		tr0_cnt0 = (SZ_64K - BIT(align_to));
+		if (len / tr0_cnt0 >= SZ_64K) {
+			dev_err(uc->ud->dev, "size %zu is not supported\n",
+				len);
+			return NULL;
+		}
+
+		tr0_cnt1 = len / tr0_cnt0;
+		tr1_cnt0 = len % tr0_cnt0;
+	}
+
+	desc_size = cppi5_trdesc_calc_size(num_tr, tr_size);
+	tr_desc = dma_alloc_coherent(desc_size, &dummy);
+	if (!tr_desc)
+		return NULL;
+	memset(tr_desc, 0, desc_size);
+
+	cppi5_trdesc_init(tr_desc, num_tr, tr_size, 0, 0);
+	cppi5_desc_set_pktids(tr_desc, uc->id, 0x3fff);
+	cppi5_desc_set_retpolicy(tr_desc, 0, tc_ring_id);
+
+	tr_req = tr_desc + tr_size;
+
+	cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
+		      CPPI5_TR_EVENT_SIZE_COMPLETION, 1);
+	cppi5_tr_csf_set(&tr_req[0].flags, CPPI5_TR_CSF_SUPR_EVT);
+
+	tr_req[0].addr = src;
+	tr_req[0].icnt0 = tr0_cnt0;
+	tr_req[0].icnt1 = tr0_cnt1;
+	tr_req[0].icnt2 = 1;
+	tr_req[0].icnt3 = 1;
+	tr_req[0].dim1 = tr0_cnt0;
+
+	tr_req[0].daddr = dest;
+	tr_req[0].dicnt0 = tr0_cnt0;
+	tr_req[0].dicnt1 = tr0_cnt1;
+	tr_req[0].dicnt2 = 1;
+	tr_req[0].dicnt3 = 1;
+	tr_req[0].ddim1 = tr0_cnt0;
+
+	if (num_tr == 2) {
+		cppi5_tr_init(&tr_req[1].flags, CPPI5_TR_TYPE15, false, true,
+			      CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+		cppi5_tr_csf_set(&tr_req[1].flags, CPPI5_TR_CSF_SUPR_EVT);
+
+		tr_req[1].addr = src + tr0_cnt1 * tr0_cnt0;
+		tr_req[1].icnt0 = tr1_cnt0;
+		tr_req[1].icnt1 = 1;
+		tr_req[1].icnt2 = 1;
+		tr_req[1].icnt3 = 1;
+
+		tr_req[1].daddr = dest + tr0_cnt1 * tr0_cnt0;
+		tr_req[1].dicnt0 = tr1_cnt0;
+		tr_req[1].dicnt1 = 1;
+		tr_req[1].dicnt2 = 1;
+		tr_req[1].dicnt3 = 1;
+	}
+
+	cppi5_tr_csf_set(&tr_req[num_tr - 1].flags, CPPI5_TR_CSF_EOP);
+
+	k3_udma_print_buf((ulong)tr_desc, tr_desc, 4, desc_size / 4, 0);
+
+	flush_dcache_range((u64)tr_desc,
+			   ALIGN((u64)tr_desc + desc_size,
+				 ARCH_DMA_MINALIGN));
+
+	k3_nav_ringacc_ring_push(uc->tchan->t_ring, &tr_desc);
+
+	return 0;
+}
+
+static int udma_transfer(struct udevice *dev, int direction,
+			 void *dst, void *src, size_t len)
+{
+	struct udma_dev *ud = dev_get_priv(dev);
+	/* Channel0 is reserved for memcpy */
+	struct udma_chan *uc = &ud->channels[0];
+	dma_addr_t paddr = 0;
+	int ret;
+
+	ret = udma_alloc_chan_resources(uc);
+	if (ret)
+		return ret;
+
+	udma_prep_dma_memcpy(uc, (dma_addr_t)dst, (dma_addr_t)src, len);
+	udma_start(uc);
+	udma_poll_completion(uc, &paddr);
+	udma_stop(uc);
+
+	udma_free_chan_resources(uc);
+	return 0;
+}
+
+static int udma_request(struct dma *dma)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct udma_chan *uc;
+	unsigned long dummy;
+	int ret;
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+
+	uc = &ud->channels[dma->id];
+	ret = udma_alloc_chan_resources(uc);
+	if (ret) {
+		dev_err(dma->dev, "alloc dma res failed %d\n", ret);
+		return -EINVAL;
+	}
+
+	uc->hdesc_size = cppi5_hdesc_calc_size(uc->needs_epib,
+					       uc->psd_size, 0);
+	uc->hdesc_size = ALIGN(uc->hdesc_size, ARCH_DMA_MINALIGN);
+
+	if (uc->dir == DMA_MEM_TO_DEV) {
+		uc->desc_tx = dma_alloc_coherent(uc->hdesc_size, &dummy);
+		memset(uc->desc_tx, 0, uc->hdesc_size);
+	} else {
+		uc->desc_rx = dma_alloc_coherent(
+				uc->hdesc_size * UDMA_RX_DESC_NUM, &dummy);
+		memset(uc->desc_rx, 0, uc->hdesc_size * UDMA_RX_DESC_NUM);
+	}
+
+	uc->in_use = true;
+	uc->desc_rx_cur = 0;
+	uc->num_rx_bufs = 0;
+
+	return 0;
+}
+
+static int udma_free(struct dma *dma)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct udma_chan *uc;
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	if (udma_is_chan_running(uc))
+		udma_stop(uc);
+	udma_free_chan_resources(uc);
+
+	uc->in_use = false;
+
+	return 0;
+}
+
+static int udma_enable(struct dma *dma)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct udma_chan *uc;
+	int ret;
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	ret = udma_start(uc);
+
+	return ret;
+}
+
+static int udma_disable(struct dma *dma)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct udma_chan *uc;
+	int ret = 0;
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	if (udma_is_chan_running(uc))
+		ret = udma_stop(uc);
+	else
+		dev_err(dma->dev, "%s not running\n", __func__);
+
+	return ret;
+}
+
+static int udma_send(struct dma *dma, void *src, size_t len, void *metadata)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct cppi5_host_desc_t *desc_tx;
+	dma_addr_t dma_src = (dma_addr_t)src;
+	struct ti_udma_drv_packet_data packet_data = { 0 };
+	dma_addr_t paddr;
+	struct udma_chan *uc;
+	u32 tc_ring_id;
+	int ret;
+
+	if (!metadata)
+		packet_data = *((struct ti_udma_drv_packet_data *)metadata);
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	if (uc->dir != DMA_MEM_TO_DEV)
+		return -EINVAL;
+
+	tc_ring_id = k3_nav_ringacc_get_ring_id(uc->tchan->tc_ring);
+
+	desc_tx = uc->desc_tx;
+
+	cppi5_hdesc_reset_hbdesc(desc_tx);
+
+	cppi5_hdesc_init(desc_tx,
+			 uc->needs_epib ? CPPI5_INFO0_HDESC_EPIB_PRESENT : 0,
+			 uc->psd_size);
+	cppi5_hdesc_set_pktlen(desc_tx, len);
+	cppi5_hdesc_attach_buf(desc_tx, dma_src, len, dma_src, len);
+	cppi5_desc_set_pktids(&desc_tx->hdr, uc->id, 0x3fff);
+	cppi5_desc_set_retpolicy(&desc_tx->hdr, 0, tc_ring_id);
+	/* pass below information from caller */
+	cppi5_hdesc_set_pkttype(desc_tx, packet_data.pkt_type);
+	cppi5_desc_set_tags_ids(&desc_tx->hdr, 0, packet_data.dest_tag);
+
+	k3_udma_print_buf((ulong)desc_tx, desc_tx, 4, uc->hdesc_size / 4, 0);
+
+	flush_dcache_range((u64)dma_src,
+			   ALIGN((u64)dma_src + len,
+				 ARCH_DMA_MINALIGN));
+
+	flush_dcache_range((u64)desc_tx,
+			   ALIGN((u64)desc_tx + uc->hdesc_size,
+				 ARCH_DMA_MINALIGN));
+	ret = k3_nav_ringacc_ring_push(uc->tchan->t_ring, &uc->desc_tx);
+	if (ret) {
+		dev_err(dma->dev, "TX dma push fail ch_id %lu %d\n",
+			dma->id, ret);
+		return ret;
+	}
+
+	udma_poll_completion(uc, &paddr);
+
+	return 0;
+}
+
+static int udma_receive(struct dma *dma, void **dst, void *metadata)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct cppi5_host_desc_t *desc_rx;
+	dma_addr_t buf_dma;
+	struct udma_chan *uc;
+	u32 buf_dma_len, pkt_len;
+	u32 port_id = 0;
+	int ret;
+
+	k3_udma_dev_dbg(dma->dev, "%s\n", __func__);
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	if (uc->dir != DMA_DEV_TO_MEM)
+		return -EINVAL;
+	if (!uc->num_rx_bufs)
+		return -EINVAL;
+
+	ret = k3_nav_ringacc_ring_pop(uc->rchan->r_ring, &desc_rx);
+	if (ret && ret != -ENODATA) {
+		dev_err(dma->dev, "rx dma fail ch_id:%lu %d\n", dma->id, ret);
+		return ret;
+	} else if (ret == -ENODATA) {
+		return 0;
+	}
+
+	k3_udma_print_buf((ulong)desc_rx, desc_rx, 4, uc->hdesc_size / 4, 0);
+
+	/* invalidate cache data */
+	invalidate_dcache_range((ulong)desc_rx,
+				(ulong)(desc_rx + uc->hdesc_size));
+
+	cppi5_hdesc_get_obuf(desc_rx, &buf_dma, &buf_dma_len);
+	pkt_len = cppi5_hdesc_get_pktlen(desc_rx);
+
+	/* invalidate cache data */
+	invalidate_dcache_range((ulong)buf_dma,
+				(ulong)(buf_dma + buf_dma_len));
+
+	cppi5_desc_get_tags_ids(&desc_rx->hdr, &port_id, NULL);
+	k3_udma_dev_dbg(dma->dev, "%s rx port_id:%d\n", __func__, port_id);
+
+	*dst = (void *)buf_dma;
+	uc->num_rx_bufs--;
+
+	return pkt_len;
+}
+
+static int udma_of_xlate(struct dma *dma, struct ofnode_phandle_args *args)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct udma_chan *uc = &ud->channels[0];
+	ofnode chconf_node, slave_node;
+	char prop[50];
+	u32 val;
+
+	for (val = 0; val < ud->ch_count; val++) {
+		uc = &ud->channels[val];
+		if (!uc->in_use)
+			break;
+	}
+
+	if (val == ud->ch_count)
+		return -EBUSY;
+
+	uc->dir = DMA_DEV_TO_MEM;
+	if (args->args[2] == UDMA_DIR_TX)
+		uc->dir = DMA_MEM_TO_DEV;
+
+	slave_node = ofnode_get_by_phandle(args->args[0]);
+	if (!ofnode_valid(slave_node)) {
+		dev_err(ud->dev, "slave node is missing\n");
+		return -EINVAL;
+	}
+
+	snprintf(prop, sizeof(prop), "ti,psil-config%u", args->args[1]);
+	chconf_node = ofnode_find_subnode(slave_node, prop);
+	if (!ofnode_valid(chconf_node)) {
+		dev_err(ud->dev, "Channel configuration node is missing\n");
+		return -EINVAL;
+	}
+
+	if (!ofnode_read_u32(chconf_node, "linux,udma-mode", &val)) {
+		if (val == UDMA_PKT_MODE)
+			uc->pkt_mode = true;
+	}
+
+	if (!ofnode_read_u32(chconf_node, "statictr-type", &val))
+		uc->static_tr_type = val;
+
+	uc->needs_epib = ofnode_read_bool(chconf_node, "ti,needs-epib");
+	if (!ofnode_read_u32(chconf_node, "ti,psd-size", &val))
+		uc->psd_size = val;
+	uc->metadata_size = (uc->needs_epib ? 16 : 0) + uc->psd_size;
+
+	if (ofnode_read_u32(slave_node, "ti,psil-base", &val)) {
+		dev_err(ud->dev, "ti,psil-base is missing\n");
+		return -EINVAL;
+	}
+
+	uc->slave_thread_id = val + args->args[1];
+
+	dma->id = uc->id;
+	k3_udma_dbg("Allocated dma chn:%lu epib:%d psdata:%u meta:%u thread_id:%x\n",
+		    dma->id, uc->needs_epib,
+		    uc->psd_size, uc->metadata_size,
+		    uc->slave_thread_id);
+
+	return 0;
+}
+
+int udma_prepare_rcv_buf(struct dma *dma, void *dst, size_t size)
+{
+	struct udma_dev *ud = dev_get_priv(dma->dev);
+	struct cppi5_host_desc_t *desc_rx;
+	dma_addr_t dma_dst;
+	struct udma_chan *uc;
+	u32 desc_num;
+
+	if (dma->id >= (ud->rchan_cnt + ud->tchan_cnt)) {
+		dev_err(dma->dev, "invalid dma ch_id %lu\n", dma->id);
+		return -EINVAL;
+	}
+	uc = &ud->channels[dma->id];
+
+	if (uc->dir != DMA_DEV_TO_MEM)
+		return -EINVAL;
+
+	if (uc->num_rx_bufs >= UDMA_RX_DESC_NUM)
+		return -EINVAL;
+
+	desc_num = uc->desc_rx_cur % UDMA_RX_DESC_NUM;
+	desc_rx = uc->desc_rx + (desc_num * uc->hdesc_size);
+	dma_dst = (dma_addr_t)dst;
+
+	cppi5_hdesc_reset_hbdesc(desc_rx);
+
+	cppi5_hdesc_init(desc_rx,
+			 uc->needs_epib ? CPPI5_INFO0_HDESC_EPIB_PRESENT : 0,
+			 uc->psd_size);
+	cppi5_hdesc_set_pktlen(desc_rx, size);
+	cppi5_hdesc_attach_buf(desc_rx, dma_dst, size, dma_dst, size);
+
+	flush_dcache_range((u64)desc_rx,
+			   ALIGN((u64)desc_rx + uc->hdesc_size,
+				 ARCH_DMA_MINALIGN));
+
+	k3_nav_ringacc_ring_push(uc->rchan->fd_ring, &desc_rx);
+
+	uc->num_rx_bufs++;
+	uc->desc_rx_cur++;
+
+	return 0;
+}
+
+static const struct dma_ops udma_ops = {
+	.transfer	= udma_transfer,
+	.of_xlate	= udma_of_xlate,
+	.request	= udma_request,
+	.free		= udma_free,
+	.enable		= udma_enable,
+	.disable	= udma_disable,
+	.send		= udma_send,
+	.receive	= udma_receive,
+	.prepare_rcv_buf = udma_prepare_rcv_buf,
+};
+
+static const struct udevice_id udma_ids[] = {
+	{ .compatible = "ti,k3-navss-udmap" },
+	{ }
+};
+
+U_BOOT_DRIVER(ti_edma3) = {
+	.name	= "ti-udma",
+	.id	= UCLASS_DMA,
+	.of_match = udma_ids,
+	.ops	= &udma_ops,
+	.probe	= udma_probe,
+	.priv_auto_alloc_size = sizeof(struct udma_dev),
+};
diff --git a/include/dt-bindings/dma/k3-udma.h b/include/dt-bindings/dma/k3-udma.h
new file mode 100644
index 000000000000..670e1232b485
--- /dev/null
+++ b/include/dt-bindings/dma/k3-udma.h
@@ -0,0 +1,31 @@ 
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ */
+
+#ifndef __DT_TI_UDMA_H
+#define __DT_TI_UDMA_H
+
+#define UDMA_TR_MODE		0
+#define UDMA_PKT_MODE		1
+
+#define UDMA_DIR_TX		0
+#define UDMA_DIR_RX		1
+
+#define PSIL_STATIC_TR_NONE	0
+#define PSIL_STATIC_TR_XY	1
+#define PSIL_STATIC_TR_MCAN	2
+
+#define UDMA_PDMA_TR_XY(id)				\
+	ti,psil-config##id {				\
+		linux,udma-mode = <UDMA_TR_MODE>;	\
+		statictr-type = <PSIL_STATIC_TR_XY>;	\
+	}
+
+#define UDMA_PDMA_PKT_XY(id)				\
+	ti,psil-config##id {				\
+		linux,udma-mode = <UDMA_PKT_MODE>;	\
+		statictr-type = <PSIL_STATIC_TR_XY>;	\
+	}
+
+#endif /* __DT_TI_UDMA_H */
diff --git a/include/linux/soc/ti/ti-udma.h b/include/linux/soc/ti/ti-udma.h
new file mode 100644
index 000000000000..e9d4226c48d9
--- /dev/null
+++ b/include/linux/soc/ti/ti-udma.h
@@ -0,0 +1,24 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
+ *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#ifndef __TI_UDMA_H
+#define __TI_UDMA_H
+
+/**
+ * struct ti_udma_drv_packet_data - TI UDMA transfer specific data
+ *
+ * @pkt_type: Packet Type - specific for each DMA client HW
+ * @dest_tag: Destination tag The source pointer.
+ *
+ * TI UDMA transfer specific data passed as part of DMA transfer to
+ * the DMA client HW in UDMA descriptors.
+ */
+struct ti_udma_drv_packet_data {
+	u32	pkt_type;
+	u32	dest_tag;
+};
+
+#endif /* __TI_UDMA_H */