* [PATCH v4 01/19] dmaengine: ti: k3-udma: move macros to header file
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:36 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 02/19] dmaengine: ti: k3-udma: move structs and enums " Sai Sree Kartheek Adivi
` (18 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Move macros defined in k3-udma.c to k3-udma.h for better separation and
re-use.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma.c | 62 ---------------------------------------
drivers/dma/ti/k3-udma.h | 63 ++++++++++++++++++++++++++++++++++++++++
2 files changed, 63 insertions(+), 62 deletions(-)
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index aa2dc762140f6..4cc64763de1f6 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -39,21 +39,6 @@ struct udma_static_tr {
u16 bstcnt; /* RPSTR1 */
};
-#define K3_UDMA_MAX_RFLOWS 1024
-#define K3_UDMA_DEFAULT_RING_SIZE 16
-
-/* How SRC/DST tag should be updated by UDMA in the descriptor's Word 3 */
-#define UDMA_RFLOW_SRCTAG_NONE 0
-#define UDMA_RFLOW_SRCTAG_CFG_TAG 1
-#define UDMA_RFLOW_SRCTAG_FLOW_ID 2
-#define UDMA_RFLOW_SRCTAG_SRC_TAG 4
-
-#define UDMA_RFLOW_DSTTAG_NONE 0
-#define UDMA_RFLOW_DSTTAG_CFG_TAG 1
-#define UDMA_RFLOW_DSTTAG_FLOW_ID 2
-#define UDMA_RFLOW_DSTTAG_DST_TAG_LO 4
-#define UDMA_RFLOW_DSTTAG_DST_TAG_HI 5
-
struct udma_chan;
enum k3_dma_type {
@@ -118,15 +103,6 @@ struct udma_oes_offsets {
u32 pktdma_rchan_flow;
};
-#define UDMA_FLAG_PDMA_ACC32 BIT(0)
-#define UDMA_FLAG_PDMA_BURST BIT(1)
-#define UDMA_FLAG_TDTYPE BIT(2)
-#define UDMA_FLAG_BURST_SIZE BIT(3)
-#define UDMA_FLAGS_J7_CLASS (UDMA_FLAG_PDMA_ACC32 | \
- UDMA_FLAG_PDMA_BURST | \
- UDMA_FLAG_TDTYPE | \
- UDMA_FLAG_BURST_SIZE)
-
struct udma_match_data {
enum k3_dma_type type;
u32 psil_base;
@@ -1837,38 +1813,6 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
return ret;
}
-#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 | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID | \
- 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_ATYPE_VALID)
-
-#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 | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_SHORT_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_LONG_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID | \
- TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
-
static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
{
struct udma_dev *ud = uc->ud;
@@ -5398,12 +5342,6 @@ static enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
}
}
-#define TI_UDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
- BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
- BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
- BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
- BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
-
static int udma_probe(struct platform_device *pdev)
{
struct device_node *navss_node = pdev->dev.parent->of_node;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 9062a237cd167..750720cd06911 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -97,6 +97,69 @@
/* Address Space Select */
#define K3_ADDRESS_ASEL_SHIFT 48
+#define K3_UDMA_MAX_RFLOWS 1024
+#define K3_UDMA_DEFAULT_RING_SIZE 16
+
+/* How SRC/DST tag should be updated by UDMA in the descriptor's Word 3 */
+#define UDMA_RFLOW_SRCTAG_NONE 0
+#define UDMA_RFLOW_SRCTAG_CFG_TAG 1
+#define UDMA_RFLOW_SRCTAG_FLOW_ID 2
+#define UDMA_RFLOW_SRCTAG_SRC_TAG 4
+
+#define UDMA_RFLOW_DSTTAG_NONE 0
+#define UDMA_RFLOW_DSTTAG_CFG_TAG 1
+#define UDMA_RFLOW_DSTTAG_FLOW_ID 2
+#define UDMA_RFLOW_DSTTAG_DST_TAG_LO 4
+#define UDMA_RFLOW_DSTTAG_DST_TAG_HI 5
+
+#define UDMA_FLAG_PDMA_ACC32 BIT(0)
+#define UDMA_FLAG_PDMA_BURST BIT(1)
+#define UDMA_FLAG_TDTYPE BIT(2)
+#define UDMA_FLAG_BURST_SIZE BIT(3)
+#define UDMA_FLAGS_J7_CLASS (UDMA_FLAG_PDMA_ACC32 | \
+ UDMA_FLAG_PDMA_BURST | \
+ UDMA_FLAG_TDTYPE | \
+ UDMA_FLAG_BURST_SIZE)
+
+#define TI_UDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
+ BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
+ BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
+
+/* TI_SCI 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 | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID | \
+ 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_ATYPE_VALID)
+
+#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 | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_SHORT_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_LONG_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID | \
+ TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
+
struct udma_dev;
struct udma_tchan;
struct udma_rchan;
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 01/19] dmaengine: ti: k3-udma: move macros to header file
2026-01-30 11:01 ` [PATCH v4 01/19] dmaengine: ti: k3-udma: move macros to header file Sai Sree Kartheek Adivi
@ 2026-01-30 15:36 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:36 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:41PM +0530, Sai Sree Kartheek Adivi wrote:
> Move macros defined in k3-udma.c to k3-udma.h for better separation and
> re-use.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/ti/k3-udma.c | 62 ---------------------------------------
> drivers/dma/ti/k3-udma.h | 63 ++++++++++++++++++++++++++++++++++++++++
> 2 files changed, 63 insertions(+), 62 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index aa2dc762140f6..4cc64763de1f6 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -39,21 +39,6 @@ struct udma_static_tr {
> u16 bstcnt; /* RPSTR1 */
> };
>
> -#define K3_UDMA_MAX_RFLOWS 1024
> -#define K3_UDMA_DEFAULT_RING_SIZE 16
> -
> -/* How SRC/DST tag should be updated by UDMA in the descriptor's Word 3 */
> -#define UDMA_RFLOW_SRCTAG_NONE 0
> -#define UDMA_RFLOW_SRCTAG_CFG_TAG 1
> -#define UDMA_RFLOW_SRCTAG_FLOW_ID 2
> -#define UDMA_RFLOW_SRCTAG_SRC_TAG 4
> -
> -#define UDMA_RFLOW_DSTTAG_NONE 0
> -#define UDMA_RFLOW_DSTTAG_CFG_TAG 1
> -#define UDMA_RFLOW_DSTTAG_FLOW_ID 2
> -#define UDMA_RFLOW_DSTTAG_DST_TAG_LO 4
> -#define UDMA_RFLOW_DSTTAG_DST_TAG_HI 5
> -
> struct udma_chan;
>
> enum k3_dma_type {
> @@ -118,15 +103,6 @@ struct udma_oes_offsets {
> u32 pktdma_rchan_flow;
> };
>
> -#define UDMA_FLAG_PDMA_ACC32 BIT(0)
> -#define UDMA_FLAG_PDMA_BURST BIT(1)
> -#define UDMA_FLAG_TDTYPE BIT(2)
> -#define UDMA_FLAG_BURST_SIZE BIT(3)
> -#define UDMA_FLAGS_J7_CLASS (UDMA_FLAG_PDMA_ACC32 | \
> - UDMA_FLAG_PDMA_BURST | \
> - UDMA_FLAG_TDTYPE | \
> - UDMA_FLAG_BURST_SIZE)
> -
> struct udma_match_data {
> enum k3_dma_type type;
> u32 psil_base;
> @@ -1837,38 +1813,6 @@ static int udma_alloc_rx_resources(struct udma_chan *uc)
> return ret;
> }
>
> -#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 | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID | \
> - 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_ATYPE_VALID)
> -
> -#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 | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_SHORT_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_LONG_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID | \
> - TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
> -
> static int udma_tisci_m2m_channel_config(struct udma_chan *uc)
> {
> struct udma_dev *ud = uc->ud;
> @@ -5398,12 +5342,6 @@ static enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
> }
> }
>
> -#define TI_UDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
> - BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
> - BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
> - BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
> - BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
> -
> static int udma_probe(struct platform_device *pdev)
> {
> struct device_node *navss_node = pdev->dev.parent->of_node;
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 9062a237cd167..750720cd06911 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -97,6 +97,69 @@
> /* Address Space Select */
> #define K3_ADDRESS_ASEL_SHIFT 48
>
> +#define K3_UDMA_MAX_RFLOWS 1024
> +#define K3_UDMA_DEFAULT_RING_SIZE 16
> +
> +/* How SRC/DST tag should be updated by UDMA in the descriptor's Word 3 */
> +#define UDMA_RFLOW_SRCTAG_NONE 0
> +#define UDMA_RFLOW_SRCTAG_CFG_TAG 1
> +#define UDMA_RFLOW_SRCTAG_FLOW_ID 2
> +#define UDMA_RFLOW_SRCTAG_SRC_TAG 4
> +
> +#define UDMA_RFLOW_DSTTAG_NONE 0
> +#define UDMA_RFLOW_DSTTAG_CFG_TAG 1
> +#define UDMA_RFLOW_DSTTAG_FLOW_ID 2
> +#define UDMA_RFLOW_DSTTAG_DST_TAG_LO 4
> +#define UDMA_RFLOW_DSTTAG_DST_TAG_HI 5
> +
> +#define UDMA_FLAG_PDMA_ACC32 BIT(0)
> +#define UDMA_FLAG_PDMA_BURST BIT(1)
> +#define UDMA_FLAG_TDTYPE BIT(2)
> +#define UDMA_FLAG_BURST_SIZE BIT(3)
> +#define UDMA_FLAGS_J7_CLASS (UDMA_FLAG_PDMA_ACC32 | \
> + UDMA_FLAG_PDMA_BURST | \
> + UDMA_FLAG_TDTYPE | \
> + UDMA_FLAG_BURST_SIZE)
> +
> +#define TI_UDMAC_BUSWIDTHS (BIT(DMA_SLAVE_BUSWIDTH_1_BYTE) | \
> + BIT(DMA_SLAVE_BUSWIDTH_2_BYTES) | \
> + BIT(DMA_SLAVE_BUSWIDTH_3_BYTES) | \
> + BIT(DMA_SLAVE_BUSWIDTH_4_BYTES) | \
> + BIT(DMA_SLAVE_BUSWIDTH_8_BYTES))
> +
> +/* TI_SCI 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 | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_TX_SUPR_TDPKT_VALID | \
> + 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_ATYPE_VALID)
> +
> +#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 | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_SHORT_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_IGNORE_LONG_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_START_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_RX_FLOWID_CNT_VALID | \
> + TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID)
> +
> struct udma_dev;
> struct udma_tchan;
> struct udma_rchan;
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 02/19] dmaengine: ti: k3-udma: move structs and enums to header file
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 01/19] dmaengine: ti: k3-udma: move macros to header file Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:37 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 03/19] dmaengine: ti: k3-udma: move static inline helper functions " Sai Sree Kartheek Adivi
` (17 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Move struct and enum definitions in k3-udma.c to k3-udma.h header file
for better separation and re-use.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma.c | 259 +-------------------------------------
drivers/dma/ti/k3-udma.h | 261 +++++++++++++++++++++++++++++++++++++++
2 files changed, 262 insertions(+), 258 deletions(-)
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 4cc64763de1f6..e0684d83f9791 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -24,8 +24,8 @@
#include <linux/workqueue.h>
#include <linux/completion.h>
#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/soc/ti/ti_sci_protocol.h>
#include <linux/dma/k3-event-router.h>
#include <linux/dma/ti-cppi5.h>
@@ -33,28 +33,6 @@
#include "k3-udma.h"
#include "k3-psil-priv.h"
-struct udma_static_tr {
- u8 elsize; /* RPSTR0 */
- u16 elcnt; /* RPSTR0 */
- u16 bstcnt; /* RPSTR1 */
-};
-
-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[] = {
[MMR_GCFG] = "gcfg",
[MMR_BCHANRT] = "bchanrt",
@@ -62,234 +40,6 @@ static const char * const mmr_names[] = {
[MMR_TCHANRT] = "tchanrt",
};
-struct udma_tchan {
- void __iomem *reg_rt;
-
- 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 */
- struct k3_ring *r_ring; /* Receive ring */
-};
-
-struct udma_rchan {
- void __iomem *reg_rt;
-
- 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;
-};
-
-struct udma_match_data {
- enum k3_dma_type type;
- u32 psil_base;
- bool enable_memcpy_support;
- u32 flags;
- u32 statictr_z_mask;
- u8 burst_size[3];
- struct udma_soc_data *soc_data;
-};
-
-struct udma_soc_data {
- struct udma_oes_offsets oes;
- u32 bcdma_trigger_event_offset;
-};
-
-struct udma_hwdesc {
- size_t cppi5_desc_size;
- void *cppi5_desc_vaddr;
- dma_addr_t cppi5_desc_paddr;
-
- /* TR descriptor internal pointers */
- void *tr_req_base;
- struct cppi5_tr_resp_t *tr_resp_base;
-};
-
-struct udma_rx_flush {
- struct udma_hwdesc hwdescs[2];
-
- size_t buffer_size;
- void *buffer_vaddr;
- dma_addr_t buffer_paddr;
-};
-
-struct udma_tpl {
- u8 levels;
- u32 start_idx[3];
-};
-
-struct udma_dev {
- struct dma_device ddev;
- struct device *dev;
- void __iomem *mmrs[MMR_LAST];
- const struct udma_match_data *match_data;
- const struct udma_soc_data *soc_data;
-
- struct udma_tpl bchan_tpl;
- struct udma_tpl tchan_tpl;
- struct udma_tpl rchan_tpl;
-
- size_t desc_align; /* alignment to use for descriptors */
-
- struct udma_tisci_rm tisci_rm;
-
- struct k3_ringacc *ringacc;
-
- struct work_struct purge_work;
- struct list_head desc_to_purge;
- spinlock_t lock;
-
- 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;
-
- struct udma_chan *channels;
- u32 psil_base;
- u32 atype;
- u32 asel;
-};
-
-struct udma_desc {
- struct virt_dma_desc vd;
-
- bool terminated;
-
- enum dma_transfer_direction dir;
-
- struct udma_static_tr static_tr;
- u32 residue;
-
- unsigned int sglen;
- unsigned int desc_idx; /* Only used for cyclic in packet mode */
- unsigned int tr_idx;
-
- u32 metadata_size;
- void *metadata; /* pointer to provided metadata buffer (EPIP, PSdata) */
-
- unsigned int hwdesc_count;
- struct udma_hwdesc hwdesc[];
-};
-
-enum udma_chan_state {
- UDMA_CHAN_IS_IDLE = 0, /* not active, no teardown is in progress */
- UDMA_CHAN_IS_ACTIVE, /* Normal operation */
- UDMA_CHAN_IS_TERMINATING, /* channel is being terminated */
-};
-
-struct udma_tx_drain {
- struct delayed_work work;
- ktime_t tstamp;
- u32 residue;
-};
-
-struct udma_chan_config {
- 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 */
- u32 hdesc_size; /* Size of a packet descriptor in packet mode */
- 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;
- bool enable_acc32;
- bool enable_burst;
- enum udma_tp_level channel_tpl; /* Channel Throughput Level */
-
- u32 tr_trigger_type;
- unsigned long tx_flags;
-
- /* PKDMA mapped channel */
- int mapped_channel_id;
- /* PKTDMA default tflow or rflow for mapped channel */
- int default_flow_id;
-
- enum dma_transfer_direction dir;
-};
-
-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;
-
- bool psil_paired;
-
- int irq_num_ring;
- int irq_num_udma;
-
- bool cyclic;
- bool paused;
-
- enum udma_chan_state state;
- struct completion teardown_completed;
-
- struct udma_tx_drain tx_drain;
-
- /* Channel configuration parameters */
- struct udma_chan_config config;
- /* Channel configuration parameters (backup) */
- struct udma_chan_config backup_config;
-
- /* dmapool for packet mode descriptors */
- bool use_dma_pool;
- struct dma_pool *hdesc_pool;
-
- u32 id;
-};
-
static inline struct udma_dev *to_udma_dev(struct dma_device *d)
{
return container_of(d, struct udma_dev, ddev);
@@ -4073,13 +3823,6 @@ 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)
{
struct udma_chan_config *ucc;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 750720cd06911..37aa9ba5b4d18 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -6,7 +6,12 @@
#ifndef K3_UDMA_H_
#define K3_UDMA_H_
+#include <linux/dmaengine.h>
#include <linux/soc/ti/ti_sci_protocol.h>
+#include <linux/dma/ti-cppi5.h>
+
+#include "../virt-dma.h"
+#include "k3-psil-priv.h"
/* Global registers */
#define UDMA_REV_REG 0x0
@@ -164,6 +169,7 @@ struct udma_dev;
struct udma_tchan;
struct udma_rchan;
struct udma_rflow;
+struct udma_chan;
enum udma_rm_range {
RM_RANGE_BCHAN = 0,
@@ -186,6 +192,261 @@ struct udma_tisci_rm {
struct ti_sci_resource *rm_ranges[RM_RANGE_LAST];
};
+struct udma_static_tr {
+ u8 elsize; /* RPSTR0 */
+ u16 elcnt; /* RPSTR0 */
+ u16 bstcnt; /* RPSTR1 */
+};
+
+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,
+};
+
+struct udma_filter_param {
+ int remote_thread_id;
+ u32 atype;
+ u32 asel;
+ u32 tr_trigger_type;
+};
+
+struct udma_tchan {
+ void __iomem *reg_rt;
+
+ 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 */
+ struct k3_ring *r_ring; /* Receive ring */
+};
+
+struct udma_rchan {
+ void __iomem *reg_rt;
+
+ 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;
+};
+
+struct udma_match_data {
+ enum k3_dma_type type;
+ u32 psil_base;
+ bool enable_memcpy_support;
+ u32 flags;
+ u32 statictr_z_mask;
+ u8 burst_size[3];
+ struct udma_soc_data *soc_data;
+};
+
+struct udma_soc_data {
+ struct udma_oes_offsets oes;
+ u32 bcdma_trigger_event_offset;
+};
+
+struct udma_hwdesc {
+ size_t cppi5_desc_size;
+ void *cppi5_desc_vaddr;
+ dma_addr_t cppi5_desc_paddr;
+
+ /* TR descriptor internal pointers */
+ void *tr_req_base;
+ struct cppi5_tr_resp_t *tr_resp_base;
+};
+
+struct udma_rx_flush {
+ struct udma_hwdesc hwdescs[2];
+
+ size_t buffer_size;
+ void *buffer_vaddr;
+ dma_addr_t buffer_paddr;
+};
+
+struct udma_tpl {
+ u8 levels;
+ u32 start_idx[3];
+};
+
+struct udma_dev {
+ struct dma_device ddev;
+ struct device *dev;
+ void __iomem *mmrs[MMR_LAST];
+ const struct udma_match_data *match_data;
+ const struct udma_soc_data *soc_data;
+
+ struct udma_tpl bchan_tpl;
+ struct udma_tpl tchan_tpl;
+ struct udma_tpl rchan_tpl;
+
+ size_t desc_align; /* alignment to use for descriptors */
+
+ struct udma_tisci_rm tisci_rm;
+
+ struct k3_ringacc *ringacc;
+
+ struct work_struct purge_work;
+ struct list_head desc_to_purge;
+ spinlock_t lock;
+
+ 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;
+
+ struct udma_chan *channels;
+ u32 psil_base;
+ u32 atype;
+ u32 asel;
+};
+
+struct udma_desc {
+ struct virt_dma_desc vd;
+
+ bool terminated;
+
+ enum dma_transfer_direction dir;
+
+ struct udma_static_tr static_tr;
+ u32 residue;
+
+ unsigned int sglen;
+ unsigned int desc_idx; /* Only used for cyclic in packet mode */
+ unsigned int tr_idx;
+
+ u32 metadata_size;
+ void *metadata; /* pointer to provided metadata buffer (EPIP, PSdata) */
+
+ unsigned int hwdesc_count;
+ struct udma_hwdesc hwdesc[];
+};
+
+enum udma_chan_state {
+ UDMA_CHAN_IS_IDLE = 0, /* not active, no teardown is in progress */
+ UDMA_CHAN_IS_ACTIVE, /* Normal operation */
+ UDMA_CHAN_IS_TERMINATING, /* channel is being terminated */
+};
+
+struct udma_tx_drain {
+ struct delayed_work work;
+ ktime_t tstamp;
+ u32 residue;
+};
+
+struct udma_chan_config {
+ 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 */
+ u32 hdesc_size; /* Size of a packet descriptor in packet mode */
+ 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;
+ bool enable_acc32;
+ bool enable_burst;
+ enum udma_tp_level channel_tpl; /* Channel Throughput Level */
+
+ u32 tr_trigger_type;
+ unsigned long tx_flags;
+
+ /* PKDMA mapped channel */
+ int mapped_channel_id;
+ /* PKTDMA default tflow or rflow for mapped channel */
+ int default_flow_id;
+
+ enum dma_transfer_direction dir;
+};
+
+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;
+
+ bool psil_paired;
+
+ int irq_num_ring;
+ int irq_num_udma;
+
+ bool cyclic;
+ bool paused;
+
+ enum udma_chan_state state;
+ struct completion teardown_completed;
+
+ struct udma_tx_drain tx_drain;
+
+ /* Channel configuration parameters */
+ struct udma_chan_config config;
+ /* Channel configuration parameters (backup) */
+ struct udma_chan_config backup_config;
+
+ /* dmapool for packet mode descriptors */
+ bool use_dma_pool;
+ struct dma_pool *hdesc_pool;
+
+ u32 id;
+};
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 02/19] dmaengine: ti: k3-udma: move structs and enums to header file
2026-01-30 11:01 ` [PATCH v4 02/19] dmaengine: ti: k3-udma: move structs and enums " Sai Sree Kartheek Adivi
@ 2026-01-30 15:37 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:37 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:42PM +0530, Sai Sree Kartheek Adivi wrote:
> Move struct and enum definitions in k3-udma.c to k3-udma.h header file
> for better separation and re-use.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/ti/k3-udma.c | 259 +-------------------------------------
> drivers/dma/ti/k3-udma.h | 261 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 262 insertions(+), 258 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 4cc64763de1f6..e0684d83f9791 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -24,8 +24,8 @@
> #include <linux/workqueue.h>
> #include <linux/completion.h>
> #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/soc/ti/ti_sci_protocol.h>
> #include <linux/dma/k3-event-router.h>
> #include <linux/dma/ti-cppi5.h>
>
> @@ -33,28 +33,6 @@
> #include "k3-udma.h"
> #include "k3-psil-priv.h"
>
> -struct udma_static_tr {
> - u8 elsize; /* RPSTR0 */
> - u16 elcnt; /* RPSTR0 */
> - u16 bstcnt; /* RPSTR1 */
> -};
> -
> -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[] = {
> [MMR_GCFG] = "gcfg",
> [MMR_BCHANRT] = "bchanrt",
> @@ -62,234 +40,6 @@ static const char * const mmr_names[] = {
> [MMR_TCHANRT] = "tchanrt",
> };
>
> -struct udma_tchan {
> - void __iomem *reg_rt;
> -
> - 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 */
> - struct k3_ring *r_ring; /* Receive ring */
> -};
> -
> -struct udma_rchan {
> - void __iomem *reg_rt;
> -
> - 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;
> -};
> -
> -struct udma_match_data {
> - enum k3_dma_type type;
> - u32 psil_base;
> - bool enable_memcpy_support;
> - u32 flags;
> - u32 statictr_z_mask;
> - u8 burst_size[3];
> - struct udma_soc_data *soc_data;
> -};
> -
> -struct udma_soc_data {
> - struct udma_oes_offsets oes;
> - u32 bcdma_trigger_event_offset;
> -};
> -
> -struct udma_hwdesc {
> - size_t cppi5_desc_size;
> - void *cppi5_desc_vaddr;
> - dma_addr_t cppi5_desc_paddr;
> -
> - /* TR descriptor internal pointers */
> - void *tr_req_base;
> - struct cppi5_tr_resp_t *tr_resp_base;
> -};
> -
> -struct udma_rx_flush {
> - struct udma_hwdesc hwdescs[2];
> -
> - size_t buffer_size;
> - void *buffer_vaddr;
> - dma_addr_t buffer_paddr;
> -};
> -
> -struct udma_tpl {
> - u8 levels;
> - u32 start_idx[3];
> -};
> -
> -struct udma_dev {
> - struct dma_device ddev;
> - struct device *dev;
> - void __iomem *mmrs[MMR_LAST];
> - const struct udma_match_data *match_data;
> - const struct udma_soc_data *soc_data;
> -
> - struct udma_tpl bchan_tpl;
> - struct udma_tpl tchan_tpl;
> - struct udma_tpl rchan_tpl;
> -
> - size_t desc_align; /* alignment to use for descriptors */
> -
> - struct udma_tisci_rm tisci_rm;
> -
> - struct k3_ringacc *ringacc;
> -
> - struct work_struct purge_work;
> - struct list_head desc_to_purge;
> - spinlock_t lock;
> -
> - 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;
> -
> - struct udma_chan *channels;
> - u32 psil_base;
> - u32 atype;
> - u32 asel;
> -};
> -
> -struct udma_desc {
> - struct virt_dma_desc vd;
> -
> - bool terminated;
> -
> - enum dma_transfer_direction dir;
> -
> - struct udma_static_tr static_tr;
> - u32 residue;
> -
> - unsigned int sglen;
> - unsigned int desc_idx; /* Only used for cyclic in packet mode */
> - unsigned int tr_idx;
> -
> - u32 metadata_size;
> - void *metadata; /* pointer to provided metadata buffer (EPIP, PSdata) */
> -
> - unsigned int hwdesc_count;
> - struct udma_hwdesc hwdesc[];
> -};
> -
> -enum udma_chan_state {
> - UDMA_CHAN_IS_IDLE = 0, /* not active, no teardown is in progress */
> - UDMA_CHAN_IS_ACTIVE, /* Normal operation */
> - UDMA_CHAN_IS_TERMINATING, /* channel is being terminated */
> -};
> -
> -struct udma_tx_drain {
> - struct delayed_work work;
> - ktime_t tstamp;
> - u32 residue;
> -};
> -
> -struct udma_chan_config {
> - 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 */
> - u32 hdesc_size; /* Size of a packet descriptor in packet mode */
> - 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;
> - bool enable_acc32;
> - bool enable_burst;
> - enum udma_tp_level channel_tpl; /* Channel Throughput Level */
> -
> - u32 tr_trigger_type;
> - unsigned long tx_flags;
> -
> - /* PKDMA mapped channel */
> - int mapped_channel_id;
> - /* PKTDMA default tflow or rflow for mapped channel */
> - int default_flow_id;
> -
> - enum dma_transfer_direction dir;
> -};
> -
> -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;
> -
> - bool psil_paired;
> -
> - int irq_num_ring;
> - int irq_num_udma;
> -
> - bool cyclic;
> - bool paused;
> -
> - enum udma_chan_state state;
> - struct completion teardown_completed;
> -
> - struct udma_tx_drain tx_drain;
> -
> - /* Channel configuration parameters */
> - struct udma_chan_config config;
> - /* Channel configuration parameters (backup) */
> - struct udma_chan_config backup_config;
> -
> - /* dmapool for packet mode descriptors */
> - bool use_dma_pool;
> - struct dma_pool *hdesc_pool;
> -
> - u32 id;
> -};
> -
> static inline struct udma_dev *to_udma_dev(struct dma_device *d)
> {
> return container_of(d, struct udma_dev, ddev);
> @@ -4073,13 +3823,6 @@ 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)
> {
> struct udma_chan_config *ucc;
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 750720cd06911..37aa9ba5b4d18 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -6,7 +6,12 @@
> #ifndef K3_UDMA_H_
> #define K3_UDMA_H_
>
> +#include <linux/dmaengine.h>
> #include <linux/soc/ti/ti_sci_protocol.h>
> +#include <linux/dma/ti-cppi5.h>
> +
> +#include "../virt-dma.h"
> +#include "k3-psil-priv.h"
>
> /* Global registers */
> #define UDMA_REV_REG 0x0
> @@ -164,6 +169,7 @@ struct udma_dev;
> struct udma_tchan;
> struct udma_rchan;
> struct udma_rflow;
> +struct udma_chan;
>
> enum udma_rm_range {
> RM_RANGE_BCHAN = 0,
> @@ -186,6 +192,261 @@ struct udma_tisci_rm {
> struct ti_sci_resource *rm_ranges[RM_RANGE_LAST];
> };
>
> +struct udma_static_tr {
> + u8 elsize; /* RPSTR0 */
> + u16 elcnt; /* RPSTR0 */
> + u16 bstcnt; /* RPSTR1 */
> +};
> +
> +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,
> +};
> +
> +struct udma_filter_param {
> + int remote_thread_id;
> + u32 atype;
> + u32 asel;
> + u32 tr_trigger_type;
> +};
> +
> +struct udma_tchan {
> + void __iomem *reg_rt;
> +
> + 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 */
> + struct k3_ring *r_ring; /* Receive ring */
> +};
> +
> +struct udma_rchan {
> + void __iomem *reg_rt;
> +
> + 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;
> +};
> +
> +struct udma_match_data {
> + enum k3_dma_type type;
> + u32 psil_base;
> + bool enable_memcpy_support;
> + u32 flags;
> + u32 statictr_z_mask;
> + u8 burst_size[3];
> + struct udma_soc_data *soc_data;
> +};
> +
> +struct udma_soc_data {
> + struct udma_oes_offsets oes;
> + u32 bcdma_trigger_event_offset;
> +};
> +
> +struct udma_hwdesc {
> + size_t cppi5_desc_size;
> + void *cppi5_desc_vaddr;
> + dma_addr_t cppi5_desc_paddr;
> +
> + /* TR descriptor internal pointers */
> + void *tr_req_base;
> + struct cppi5_tr_resp_t *tr_resp_base;
> +};
> +
> +struct udma_rx_flush {
> + struct udma_hwdesc hwdescs[2];
> +
> + size_t buffer_size;
> + void *buffer_vaddr;
> + dma_addr_t buffer_paddr;
> +};
> +
> +struct udma_tpl {
> + u8 levels;
> + u32 start_idx[3];
> +};
> +
> +struct udma_dev {
> + struct dma_device ddev;
> + struct device *dev;
> + void __iomem *mmrs[MMR_LAST];
> + const struct udma_match_data *match_data;
> + const struct udma_soc_data *soc_data;
> +
> + struct udma_tpl bchan_tpl;
> + struct udma_tpl tchan_tpl;
> + struct udma_tpl rchan_tpl;
> +
> + size_t desc_align; /* alignment to use for descriptors */
> +
> + struct udma_tisci_rm tisci_rm;
> +
> + struct k3_ringacc *ringacc;
> +
> + struct work_struct purge_work;
> + struct list_head desc_to_purge;
> + spinlock_t lock;
> +
> + 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;
> +
> + struct udma_chan *channels;
> + u32 psil_base;
> + u32 atype;
> + u32 asel;
> +};
> +
> +struct udma_desc {
> + struct virt_dma_desc vd;
> +
> + bool terminated;
> +
> + enum dma_transfer_direction dir;
> +
> + struct udma_static_tr static_tr;
> + u32 residue;
> +
> + unsigned int sglen;
> + unsigned int desc_idx; /* Only used for cyclic in packet mode */
> + unsigned int tr_idx;
> +
> + u32 metadata_size;
> + void *metadata; /* pointer to provided metadata buffer (EPIP, PSdata) */
> +
> + unsigned int hwdesc_count;
> + struct udma_hwdesc hwdesc[];
> +};
> +
> +enum udma_chan_state {
> + UDMA_CHAN_IS_IDLE = 0, /* not active, no teardown is in progress */
> + UDMA_CHAN_IS_ACTIVE, /* Normal operation */
> + UDMA_CHAN_IS_TERMINATING, /* channel is being terminated */
> +};
> +
> +struct udma_tx_drain {
> + struct delayed_work work;
> + ktime_t tstamp;
> + u32 residue;
> +};
> +
> +struct udma_chan_config {
> + 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 */
> + u32 hdesc_size; /* Size of a packet descriptor in packet mode */
> + 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;
> + bool enable_acc32;
> + bool enable_burst;
> + enum udma_tp_level channel_tpl; /* Channel Throughput Level */
> +
> + u32 tr_trigger_type;
> + unsigned long tx_flags;
> +
> + /* PKDMA mapped channel */
> + int mapped_channel_id;
> + /* PKTDMA default tflow or rflow for mapped channel */
> + int default_flow_id;
> +
> + enum dma_transfer_direction dir;
> +};
> +
> +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;
> +
> + bool psil_paired;
> +
> + int irq_num_ring;
> + int irq_num_udma;
> +
> + bool cyclic;
> + bool paused;
> +
> + enum udma_chan_state state;
> + struct completion teardown_completed;
> +
> + struct udma_tx_drain tx_drain;
> +
> + /* Channel configuration parameters */
> + struct udma_chan_config config;
> + /* Channel configuration parameters (backup) */
> + struct udma_chan_config backup_config;
> +
> + /* dmapool for packet mode descriptors */
> + bool use_dma_pool;
> + struct dma_pool *hdesc_pool;
> +
> + u32 id;
> +};
> +
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 03/19] dmaengine: ti: k3-udma: move static inline helper functions to header file
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 01/19] dmaengine: ti: k3-udma: move macros to header file Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 02/19] dmaengine: ti: k3-udma: move structs and enums " Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:38 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 04/19] dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c Sai Sree Kartheek Adivi
` (16 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Move static inline helper functions in k3-udma.c to k3-udma.h header
file for better separation and re-use.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma.c | 108 --------------------------------------
drivers/dma/ti/k3-udma.h | 109 +++++++++++++++++++++++++++++++++++++++
2 files changed, 109 insertions(+), 108 deletions(-)
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index e0684d83f9791..4adcd679c6997 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -40,91 +40,6 @@ static const char * const mmr_names[] = {
[MMR_TCHANRT] = "tchanrt",
};
-static inline struct udma_dev *to_udma_dev(struct dma_device *d)
-{
- return container_of(d, struct udma_dev, ddev);
-}
-
-static inline struct udma_chan *to_udma_chan(struct dma_chan *c)
-{
- return container_of(c, struct udma_chan, vc.chan);
-}
-
-static inline struct udma_desc *to_udma_desc(struct dma_async_tx_descriptor *t)
-{
- return container_of(t, struct udma_desc, vd.tx);
-}
-
-/* Generic register access functions */
-static inline u32 udma_read(void __iomem *base, int reg)
-{
- return readl(base + reg);
-}
-
-static inline void udma_write(void __iomem *base, int reg, u32 val)
-{
- writel(val, base + reg);
-}
-
-static inline void udma_update_bits(void __iomem *base, int reg,
- u32 mask, u32 val)
-{
- u32 tmp, orig;
-
- orig = readl(base + reg);
- tmp = orig & ~mask;
- tmp |= (val & mask);
-
- if (tmp != orig)
- writel(tmp, base + reg);
-}
-
-/* TCHANRT */
-static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
-{
- if (!uc->tchan)
- return 0;
- return udma_read(uc->tchan->reg_rt, reg);
-}
-
-static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
-{
- if (!uc->tchan)
- return;
- udma_write(uc->tchan->reg_rt, reg, val);
-}
-
-static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
- u32 mask, u32 val)
-{
- if (!uc->tchan)
- return;
- udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
-}
-
-/* RCHANRT */
-static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
-{
- if (!uc->rchan)
- return 0;
- return udma_read(uc->rchan->reg_rt, reg);
-}
-
-static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
-{
- if (!uc->rchan)
- return;
- udma_write(uc->rchan->reg_rt, reg, val);
-}
-
-static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
- u32 mask, u32 val)
-{
- if (!uc->rchan)
- return;
- udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
-}
-
static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
@@ -216,17 +131,6 @@ static void udma_dump_chan_stdata(struct udma_chan *uc)
}
}
-static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
- int idx)
-{
- return d->hwdesc[idx].cppi5_desc_paddr;
-}
-
-static inline void *udma_curr_cppi5_desc_vaddr(struct udma_desc *d, int idx)
-{
- return d->hwdesc[idx].cppi5_desc_vaddr;
-}
-
static struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
dma_addr_t paddr)
{
@@ -369,11 +273,6 @@ static bool udma_is_chan_paused(struct udma_chan *uc)
return false;
}
-static inline dma_addr_t udma_get_rx_flush_hwdesc_paddr(struct udma_chan *uc)
-{
- return uc->ud->rx_flush.hwdescs[uc->config.pkt_mode].cppi5_desc_paddr;
-}
-
static int udma_push_to_ring(struct udma_chan *uc, int idx)
{
struct udma_desc *d = uc->desc;
@@ -775,13 +674,6 @@ static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
d->desc_idx = (d->desc_idx + 1) % d->sglen;
}
-static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
-{
- struct cppi5_host_desc_t *h_desc = d->hwdesc[0].cppi5_desc_vaddr;
-
- memcpy(d->metadata, h_desc->epib, d->metadata_size);
-}
-
static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
{
u32 peer_bcnt, bcnt;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 37aa9ba5b4d18..3a786b3eddc67 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -447,6 +447,115 @@ struct udma_chan {
u32 id;
};
+/* K3 UDMA helper functions */
+static inline struct udma_dev *to_udma_dev(struct dma_device *d)
+{
+ return container_of(d, struct udma_dev, ddev);
+}
+
+static inline struct udma_chan *to_udma_chan(struct dma_chan *c)
+{
+ return container_of(c, struct udma_chan, vc.chan);
+}
+
+static inline struct udma_desc *to_udma_desc(struct dma_async_tx_descriptor *t)
+{
+ return container_of(t, struct udma_desc, vd.tx);
+}
+
+/* Generic register access functions */
+static inline u32 udma_read(void __iomem *base, int reg)
+{
+ return readl(base + reg);
+}
+
+static inline void udma_write(void __iomem *base, int reg, u32 val)
+{
+ writel(val, base + reg);
+}
+
+static inline void udma_update_bits(void __iomem *base, int reg,
+ u32 mask, u32 val)
+{
+ u32 tmp, orig;
+
+ orig = readl(base + reg);
+ tmp = orig & ~mask;
+ tmp |= (val & mask);
+
+ if (tmp != orig)
+ writel(tmp, base + reg);
+}
+
+/* TCHANRT */
+static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
+{
+ if (!uc->tchan)
+ return 0;
+ return udma_read(uc->tchan->reg_rt, reg);
+}
+
+static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
+{
+ if (!uc->tchan)
+ return;
+ udma_write(uc->tchan->reg_rt, reg, val);
+}
+
+static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
+ u32 mask, u32 val)
+{
+ if (!uc->tchan)
+ return;
+ udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
+}
+
+/* RCHANRT */
+static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
+{
+ if (!uc->rchan)
+ return 0;
+ return udma_read(uc->rchan->reg_rt, reg);
+}
+
+static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
+{
+ if (!uc->rchan)
+ return;
+ udma_write(uc->rchan->reg_rt, reg, val);
+}
+
+static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
+ u32 mask, u32 val)
+{
+ if (!uc->rchan)
+ return;
+ udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
+}
+
+static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
+ int idx)
+{
+ return d->hwdesc[idx].cppi5_desc_paddr;
+}
+
+static inline void *udma_curr_cppi5_desc_vaddr(struct udma_desc *d, int idx)
+{
+ return d->hwdesc[idx].cppi5_desc_vaddr;
+}
+
+static inline dma_addr_t udma_get_rx_flush_hwdesc_paddr(struct udma_chan *uc)
+{
+ return uc->ud->rx_flush.hwdescs[uc->config.pkt_mode].cppi5_desc_paddr;
+}
+
+static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
+{
+ struct cppi5_host_desc_t *h_desc = d->hwdesc[0].cppi5_desc_vaddr;
+
+ memcpy(d->metadata, h_desc->epib, d->metadata_size);
+}
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 03/19] dmaengine: ti: k3-udma: move static inline helper functions to header file
2026-01-30 11:01 ` [PATCH v4 03/19] dmaengine: ti: k3-udma: move static inline helper functions " Sai Sree Kartheek Adivi
@ 2026-01-30 15:38 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:38 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:43PM +0530, Sai Sree Kartheek Adivi wrote:
> Move static inline helper functions in k3-udma.c to k3-udma.h header
> file for better separation and re-use.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/ti/k3-udma.c | 108 --------------------------------------
> drivers/dma/ti/k3-udma.h | 109 +++++++++++++++++++++++++++++++++++++++
> 2 files changed, 109 insertions(+), 108 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index e0684d83f9791..4adcd679c6997 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -40,91 +40,6 @@ static const char * const mmr_names[] = {
> [MMR_TCHANRT] = "tchanrt",
> };
>
> -static inline struct udma_dev *to_udma_dev(struct dma_device *d)
> -{
> - return container_of(d, struct udma_dev, ddev);
> -}
> -
> -static inline struct udma_chan *to_udma_chan(struct dma_chan *c)
> -{
> - return container_of(c, struct udma_chan, vc.chan);
> -}
> -
> -static inline struct udma_desc *to_udma_desc(struct dma_async_tx_descriptor *t)
> -{
> - return container_of(t, struct udma_desc, vd.tx);
> -}
> -
> -/* Generic register access functions */
> -static inline u32 udma_read(void __iomem *base, int reg)
> -{
> - return readl(base + reg);
> -}
> -
> -static inline void udma_write(void __iomem *base, int reg, u32 val)
> -{
> - writel(val, base + reg);
> -}
> -
> -static inline void udma_update_bits(void __iomem *base, int reg,
> - u32 mask, u32 val)
> -{
> - u32 tmp, orig;
> -
> - orig = readl(base + reg);
> - tmp = orig & ~mask;
> - tmp |= (val & mask);
> -
> - if (tmp != orig)
> - writel(tmp, base + reg);
> -}
> -
> -/* TCHANRT */
> -static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
> -{
> - if (!uc->tchan)
> - return 0;
> - return udma_read(uc->tchan->reg_rt, reg);
> -}
> -
> -static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
> -{
> - if (!uc->tchan)
> - return;
> - udma_write(uc->tchan->reg_rt, reg, val);
> -}
> -
> -static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
> - u32 mask, u32 val)
> -{
> - if (!uc->tchan)
> - return;
> - udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
> -}
> -
> -/* RCHANRT */
> -static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
> -{
> - if (!uc->rchan)
> - return 0;
> - return udma_read(uc->rchan->reg_rt, reg);
> -}
> -
> -static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
> -{
> - if (!uc->rchan)
> - return;
> - udma_write(uc->rchan->reg_rt, reg, val);
> -}
> -
> -static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
> - u32 mask, u32 val)
> -{
> - if (!uc->rchan)
> - return;
> - udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
> -}
> -
> static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> {
> struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
> @@ -216,17 +131,6 @@ static void udma_dump_chan_stdata(struct udma_chan *uc)
> }
> }
>
> -static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
> - int idx)
> -{
> - return d->hwdesc[idx].cppi5_desc_paddr;
> -}
> -
> -static inline void *udma_curr_cppi5_desc_vaddr(struct udma_desc *d, int idx)
> -{
> - return d->hwdesc[idx].cppi5_desc_vaddr;
> -}
> -
> static struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> dma_addr_t paddr)
> {
> @@ -369,11 +273,6 @@ static bool udma_is_chan_paused(struct udma_chan *uc)
> return false;
> }
>
> -static inline dma_addr_t udma_get_rx_flush_hwdesc_paddr(struct udma_chan *uc)
> -{
> - return uc->ud->rx_flush.hwdescs[uc->config.pkt_mode].cppi5_desc_paddr;
> -}
> -
> static int udma_push_to_ring(struct udma_chan *uc, int idx)
> {
> struct udma_desc *d = uc->desc;
> @@ -775,13 +674,6 @@ static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
> d->desc_idx = (d->desc_idx + 1) % d->sglen;
> }
>
> -static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
> -{
> - struct cppi5_host_desc_t *h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> -
> - memcpy(d->metadata, h_desc->epib, d->metadata_size);
> -}
> -
> static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
> {
> u32 peer_bcnt, bcnt;
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 37aa9ba5b4d18..3a786b3eddc67 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -447,6 +447,115 @@ struct udma_chan {
> u32 id;
> };
>
> +/* K3 UDMA helper functions */
> +static inline struct udma_dev *to_udma_dev(struct dma_device *d)
> +{
> + return container_of(d, struct udma_dev, ddev);
> +}
> +
> +static inline struct udma_chan *to_udma_chan(struct dma_chan *c)
> +{
> + return container_of(c, struct udma_chan, vc.chan);
> +}
> +
> +static inline struct udma_desc *to_udma_desc(struct dma_async_tx_descriptor *t)
> +{
> + return container_of(t, struct udma_desc, vd.tx);
> +}
> +
> +/* Generic register access functions */
> +static inline u32 udma_read(void __iomem *base, int reg)
> +{
> + return readl(base + reg);
> +}
> +
> +static inline void udma_write(void __iomem *base, int reg, u32 val)
> +{
> + writel(val, base + reg);
> +}
> +
> +static inline void udma_update_bits(void __iomem *base, int reg,
> + u32 mask, u32 val)
> +{
> + u32 tmp, orig;
> +
> + orig = readl(base + reg);
> + tmp = orig & ~mask;
> + tmp |= (val & mask);
> +
> + if (tmp != orig)
> + writel(tmp, base + reg);
> +}
> +
> +/* TCHANRT */
> +static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
> +{
> + if (!uc->tchan)
> + return 0;
> + return udma_read(uc->tchan->reg_rt, reg);
> +}
> +
> +static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
> +{
> + if (!uc->tchan)
> + return;
> + udma_write(uc->tchan->reg_rt, reg, val);
> +}
> +
> +static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
> + u32 mask, u32 val)
> +{
> + if (!uc->tchan)
> + return;
> + udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
> +}
> +
> +/* RCHANRT */
> +static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
> +{
> + if (!uc->rchan)
> + return 0;
> + return udma_read(uc->rchan->reg_rt, reg);
> +}
> +
> +static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
> +{
> + if (!uc->rchan)
> + return;
> + udma_write(uc->rchan->reg_rt, reg, val);
> +}
> +
> +static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
> + u32 mask, u32 val)
> +{
> + if (!uc->rchan)
> + return;
> + udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
> +}
> +
> +static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
> + int idx)
> +{
> + return d->hwdesc[idx].cppi5_desc_paddr;
> +}
> +
> +static inline void *udma_curr_cppi5_desc_vaddr(struct udma_desc *d, int idx)
> +{
> + return d->hwdesc[idx].cppi5_desc_vaddr;
> +}
> +
> +static inline dma_addr_t udma_get_rx_flush_hwdesc_paddr(struct udma_chan *uc)
> +{
> + return uc->ud->rx_flush.hwdescs[uc->config.pkt_mode].cppi5_desc_paddr;
> +}
> +
> +static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
> +{
> + struct cppi5_host_desc_t *h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> +
> + memcpy(d->metadata, h_desc->epib, d->metadata_size);
> +}
> +
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 04/19] dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (2 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 03/19] dmaengine: ti: k3-udma: move static inline helper functions " Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:43 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 05/19] dmaengine: ti: k3-udma: move ring management functions " Sai Sree Kartheek Adivi
` (15 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Refactor the K3 UDMA driver by moving all DMA descriptor handling
functions from k3-udma.c to a new common library, k3-udma-common.c.
This prepares the driver for supporting new K3 UDMA v2 variant
(used in AM62L) that can reuse the same descriptor handling logic.
To ensure this common code works correctly across all build
configurations (built-in vs modules), introduce a hidden Kconfig symbol:
CONFIG_TI_K3_UDMA_COMMON.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/Kconfig | 5 +
drivers/dma/ti/Makefile | 1 +
drivers/dma/ti/k3-udma-common.c | 1243 +++++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.c | 1218 ------------------------------
drivers/dma/ti/k3-udma.h | 54 ++
5 files changed, 1303 insertions(+), 1218 deletions(-)
create mode 100644 drivers/dma/ti/k3-udma-common.c
diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
index cbc30ab627832..712e456015459 100644
--- a/drivers/dma/ti/Kconfig
+++ b/drivers/dma/ti/Kconfig
@@ -42,12 +42,17 @@ config TI_K3_UDMA
select DMA_ENGINE
select DMA_VIRTUAL_CHANNELS
select SOC_TI
+ select TI_K3_UDMA_COMMON
select TI_K3_RINGACC
select TI_K3_PSIL
help
Enable support for the TI UDMA (Unified DMA) controller. This
DMA engine is used in AM65x and j721e.
+config TI_K3_UDMA_COMMON
+ tristate
+ default n
+
config TI_K3_UDMA_GLUE_LAYER
tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
depends on ARCH_K3 || COMPILE_TEST
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
index d376c117cecf6..3b91c02e55eaf 100644
--- a/drivers/dma/ti/Makefile
+++ b/drivers/dma/ti/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
+obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
k3-psil-lib-objs := k3-psil.o \
k3-psil-am654.o \
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
new file mode 100644
index 0000000000000..9cb35759c70bb
--- /dev/null
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -0,0 +1,1243 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ */
+
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/platform_device.h>
+#include <linux/soc/ti/k3-ringacc.h>
+
+#include "k3-udma.h"
+
+struct dma_descriptor_metadata_ops metadata_ops = {
+ .attach = udma_attach_metadata,
+ .get_ptr = udma_get_metadata_ptr,
+ .set_len = udma_set_metadata_len,
+};
+
+struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
+ dma_addr_t paddr)
+{
+ struct udma_desc *d = uc->terminated_desc;
+
+ if (d) {
+ dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
+ d->desc_idx);
+
+ if (desc_paddr != paddr)
+ d = NULL;
+ }
+
+ if (!d) {
+ d = uc->desc;
+ if (d) {
+ dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
+ d->desc_idx);
+
+ if (desc_paddr != paddr)
+ d = NULL;
+ }
+ }
+
+ return d;
+}
+EXPORT_SYMBOL_GPL(udma_udma_desc_from_paddr);
+
+void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
+{
+ if (uc->use_dma_pool) {
+ int i;
+
+ for (i = 0; i < d->hwdesc_count; i++) {
+ if (!d->hwdesc[i].cppi5_desc_vaddr)
+ continue;
+
+ dma_pool_free(uc->hdesc_pool,
+ d->hwdesc[i].cppi5_desc_vaddr,
+ d->hwdesc[i].cppi5_desc_paddr);
+
+ d->hwdesc[i].cppi5_desc_vaddr = NULL;
+ }
+ } else if (d->hwdesc[0].cppi5_desc_vaddr) {
+ dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
+ d->hwdesc[0].cppi5_desc_vaddr,
+ d->hwdesc[0].cppi5_desc_paddr);
+
+ d->hwdesc[0].cppi5_desc_vaddr = NULL;
+ }
+}
+
+void udma_purge_desc_work(struct work_struct *work)
+{
+ struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
+ struct virt_dma_desc *vd, *_vd;
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&ud->lock, flags);
+ list_splice_tail_init(&ud->desc_to_purge, &head);
+ spin_unlock_irqrestore(&ud->lock, flags);
+
+ list_for_each_entry_safe(vd, _vd, &head, node) {
+ struct udma_chan *uc = to_udma_chan(vd->tx.chan);
+ struct udma_desc *d = to_udma_desc(&vd->tx);
+
+ udma_free_hwdesc(uc, d);
+ list_del(&vd->node);
+ kfree(d);
+ }
+
+ /* If more to purge, schedule the work again */
+ if (!list_empty(&ud->desc_to_purge))
+ schedule_work(&ud->purge_work);
+}
+EXPORT_SYMBOL_GPL(udma_purge_desc_work);
+
+void udma_desc_free(struct virt_dma_desc *vd)
+{
+ struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
+ struct udma_chan *uc = to_udma_chan(vd->tx.chan);
+ struct udma_desc *d = to_udma_desc(&vd->tx);
+ unsigned long flags;
+
+ if (uc->terminated_desc == d)
+ uc->terminated_desc = NULL;
+
+ if (uc->use_dma_pool) {
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return;
+ }
+
+ spin_lock_irqsave(&ud->lock, flags);
+ list_add_tail(&vd->node, &ud->desc_to_purge);
+ spin_unlock_irqrestore(&ud->lock, flags);
+
+ schedule_work(&ud->purge_work);
+}
+EXPORT_SYMBOL_GPL(udma_desc_free);
+
+bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr)
+{
+ if (uc->config.dir != DMA_DEV_TO_MEM)
+ return false;
+
+ if (addr == udma_get_rx_flush_hwdesc_paddr(uc))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(udma_desc_is_rx_flush);
+
+bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
+{
+ u32 peer_bcnt, bcnt;
+
+ /*
+ * Only TX towards PDMA is affected.
+ * If DMA_PREP_INTERRUPT is not set by consumer then skip the transfer
+ * completion calculation, consumer must ensure that there is no stale
+ * data in DMA fabric in this case.
+ */
+ if (uc->config.ep_type == PSIL_EP_NATIVE ||
+ uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
+ return true;
+
+ peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
+ bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+
+ /* Transfer is incomplete, store current residue and time stamp */
+ if (peer_bcnt < bcnt) {
+ uc->tx_drain.residue = bcnt - peer_bcnt;
+ uc->tx_drain.tstamp = ktime_get();
+ return false;
+ }
+
+ return true;
+}
+EXPORT_SYMBOL_GPL(udma_is_desc_really_done);
+
+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:
+ break;
+ default:
+ dev_err(uc->ud->dev, "Unsupported TR size of %zu\n", tr_size);
+ return NULL;
+ }
+
+ /* We have only one descriptor containing multiple TRs */
+ d = kzalloc(sizeof(*d) + sizeof(d->hwdesc[0]), GFP_NOWAIT);
+ if (!d)
+ return NULL;
+
+ d->sglen = tr_count;
+
+ d->hwdesc_count = 1;
+ hwdesc = &d->hwdesc[0];
+
+ /* Allocate memory for DMA ring descriptor */
+ if (uc->use_dma_pool) {
+ hwdesc->cppi5_desc_size = uc->config.hdesc_size;
+ hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
+ GFP_NOWAIT,
+ &hwdesc->cppi5_desc_paddr);
+ } else {
+ hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size,
+ tr_count);
+ hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
+ uc->ud->desc_align);
+ hwdesc->cppi5_desc_vaddr = dma_alloc_coherent(uc->ud->dev,
+ hwdesc->cppi5_desc_size,
+ &hwdesc->cppi5_desc_paddr,
+ GFP_NOWAIT);
+ }
+
+ if (!hwdesc->cppi5_desc_vaddr) {
+ kfree(d);
+ return NULL;
+ }
+
+ /* Start of the TR req records */
+ hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
+ /* Start address of the TR response array */
+ hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size * tr_count;
+
+ tr_desc = hwdesc->cppi5_desc_vaddr;
+
+ if (uc->cyclic)
+ reload_count = CPPI5_INFO0_TRDESC_RLDCNT_INFINITE;
+
+ if (dir == DMA_DEV_TO_MEM)
+ ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
+ else
+ ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
+
+ cppi5_trdesc_init(tr_desc, tr_count, tr_size, 0, reload_count);
+ cppi5_desc_set_pktids(tr_desc, uc->id,
+ CPPI5_INFO1_DESC_FLOWID_DEFAULT);
+ cppi5_desc_set_retpolicy(tr_desc, 0, ring_id);
+
+ return d;
+}
+
+/**
+ * udma_get_tr_counters - calculate TR counters for a given length
+ * @len: Length of the transfer
+ * @align_to: Preferred alignment
+ * @tr0_cnt0: First TR icnt0
+ * @tr0_cnt1: First TR icnt1
+ * @tr1_cnt0: Second (if used) TR icnt0
+ *
+ * For len < SZ_64K only one TR is enough, tr1_cnt0 is not updated
+ * For len >= SZ_64K two TRs are used in a simple way:
+ * First TR: SZ_64K-alignment blocks (tr0_cnt0, tr0_cnt1)
+ * Second TR: the remaining length (tr1_cnt0)
+ *
+ * Returns the number of TRs the length needs (1 or 2)
+ * -EINVAL if the length can not be supported
+ */
+int udma_get_tr_counters(size_t len, unsigned long align_to,
+ u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0)
+{
+ if (len < SZ_64K) {
+ *tr0_cnt0 = len;
+ *tr0_cnt1 = 1;
+
+ return 1;
+ }
+
+ if (align_to > 3)
+ align_to = 3;
+
+realign:
+ *tr0_cnt0 = SZ_64K - BIT(align_to);
+ if (len / *tr0_cnt0 >= SZ_64K) {
+ if (align_to) {
+ align_to--;
+ goto realign;
+ }
+ return -EINVAL;
+ }
+
+ *tr0_cnt1 = len / *tr0_cnt0;
+ *tr1_cnt0 = len % *tr0_cnt0;
+
+ return 2;
+}
+
+struct udma_desc *
+udma_prep_slave_sg_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 udma_desc *d;
+ struct cppi5_tr_type1_t *tr_req = NULL;
+ u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
+ unsigned int i;
+ 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) {
+ if (sg_dma_len(sgent) < SZ_64K)
+ num_tr++;
+ else
+ num_tr += 2;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ tr_size = sizeof(struct cppi5_tr_type1_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;
+
+ tr_req = d->hwdesc[0].tr_req_base;
+ for_each_sg(sgl, sgent, sglen, i) {
+ dma_addr_t sg_addr = sg_dma_address(sgent);
+
+ num_tr = udma_get_tr_counters(sg_dma_len(sgent), __ffs(sg_addr),
+ &tr0_cnt0, &tr0_cnt1, &tr1_cnt0);
+ if (num_tr < 0) {
+ dev_err(uc->ud->dev, "size %u is not supported\n",
+ sg_dma_len(sgent));
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
+ 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;
+ tr_req[tr_idx].dim1 = tr0_cnt0;
+ tr_idx++;
+
+ if (num_tr == 2) {
+ cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
+ false, false,
+ CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+ cppi5_tr_csf_set(&tr_req[tr_idx].flags,
+ CPPI5_TR_CSF_SUPR_EVT);
+
+ tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
+ tr_req[tr_idx].icnt0 = tr1_cnt0;
+ tr_req[tr_idx].icnt1 = 1;
+ tr_req[tr_idx].dim1 = tr1_cnt0;
+ tr_idx++;
+ }
+
+ d->residue += sg_dma_len(sgent);
+ }
+
+ cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
+ CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
+
+ return d;
+}
+
+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;
+ u32 csf = CPPI5_TR_CSF_SUPR_EVT;
+ 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;
+ csf |= CPPI5_TR_CSF_EOL_ICNT0;
+ } 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, csf);
+ 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, csf);
+ 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, csf | CPPI5_TR_CSF_EOP);
+
+ return d;
+}
+
+int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
+ enum dma_slave_buswidth dev_width,
+ u16 elcnt)
+{
+ if (uc->config.ep_type != PSIL_EP_PDMA_XY)
+ return 0;
+
+ /* Bus width translates to the element size (ES) */
+ switch (dev_width) {
+ case DMA_SLAVE_BUSWIDTH_1_BYTE:
+ d->static_tr.elsize = 0;
+ break;
+ case DMA_SLAVE_BUSWIDTH_2_BYTES:
+ d->static_tr.elsize = 1;
+ break;
+ case DMA_SLAVE_BUSWIDTH_3_BYTES:
+ d->static_tr.elsize = 2;
+ break;
+ case DMA_SLAVE_BUSWIDTH_4_BYTES:
+ d->static_tr.elsize = 3;
+ break;
+ case DMA_SLAVE_BUSWIDTH_8_BYTES:
+ d->static_tr.elsize = 4;
+ break;
+ default: /* not reached */
+ return -EINVAL;
+ }
+
+ d->static_tr.elcnt = elcnt;
+
+ if (uc->config.pkt_mode || !uc->cyclic) {
+ /*
+ * PDMA must close the packet when the channel is in packet mode.
+ * For TR mode when the channel is not cyclic we also need PDMA
+ * to close the packet otherwise the transfer will stall because
+ * PDMA holds on the data it has received from the peripheral.
+ */
+ unsigned int div = dev_width * elcnt;
+
+ if (uc->cyclic)
+ d->static_tr.bstcnt = d->residue / d->sglen / div;
+ else
+ d->static_tr.bstcnt = d->residue / div;
+ } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
+ uc->config.dir == DMA_DEV_TO_MEM &&
+ uc->cyclic) {
+ /*
+ * For cyclic mode with BCDMA we have to set EOP in each TR to
+ * prevent short packet errors seen on channel teardown. So the
+ * PDMA must close the packet after every TR transfer by setting
+ * burst count equal to the number of bytes transferred.
+ */
+ struct cppi5_tr_type1_t *tr_req = d->hwdesc[0].tr_req_base;
+
+ d->static_tr.bstcnt =
+ (tr_req->icnt0 * tr_req->icnt1) / dev_width;
+ } else {
+ d->static_tr.bstcnt = 0;
+ }
+
+ if (uc->config.dir == DMA_DEV_TO_MEM &&
+ d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask)
+ return -EINVAL;
+
+ return 0;
+}
+
+struct udma_desc *
+udma_prep_slave_sg_pkt(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_host_desc_t *h_desc = NULL;
+ struct udma_desc *d;
+ u32 ring_id;
+ unsigned int i;
+ u64 asel;
+
+ d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
+ if (!d)
+ return NULL;
+
+ d->sglen = sglen;
+ d->hwdesc_count = sglen;
+
+ if (dir == DMA_DEV_TO_MEM)
+ ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
+ 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);
+ struct cppi5_host_desc_t *desc;
+ size_t sg_len = sg_dma_len(sgent);
+
+ hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
+ GFP_NOWAIT,
+ &hwdesc->cppi5_desc_paddr);
+ if (!hwdesc->cppi5_desc_vaddr) {
+ dev_err(uc->ud->dev,
+ "descriptor%d allocation failed\n", i);
+
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ d->residue += sg_len;
+ hwdesc->cppi5_desc_size = uc->config.hdesc_size;
+ desc = hwdesc->cppi5_desc_vaddr;
+
+ if (i == 0) {
+ cppi5_hdesc_init(desc, 0, 0);
+ /* Flow and Packed ID */
+ cppi5_desc_set_pktids(&desc->hdr, uc->id,
+ CPPI5_INFO1_DESC_FLOWID_DEFAULT);
+ cppi5_desc_set_retpolicy(&desc->hdr, 0, ring_id);
+ } else {
+ cppi5_hdesc_reset_hbdesc(desc);
+ cppi5_desc_set_retpolicy(&desc->hdr, 0, 0xffff);
+ }
+
+ /* 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 | asel);
+
+ if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
+ dir == DMA_MEM_TO_DEV)
+ h_desc = desc;
+ }
+
+ if (d->residue >= SZ_4M) {
+ dev_err(uc->ud->dev,
+ "%s: Transfer size %u is over the supported 4M range\n",
+ __func__, d->residue);
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ h_desc = d->hwdesc[0].cppi5_desc_vaddr;
+ cppi5_hdesc_set_pktlen(h_desc, d->residue);
+
+ return d;
+}
+
+int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
+ void *data, size_t len)
+{
+ struct udma_desc *d = to_udma_desc(desc);
+ struct udma_chan *uc = to_udma_chan(desc->chan);
+ struct cppi5_host_desc_t *h_desc;
+ u32 psd_size = len;
+ u32 flags = 0;
+
+ if (!uc->config.pkt_mode || !uc->config.metadata_size)
+ return -EOPNOTSUPP;
+
+ if (!data || len > uc->config.metadata_size)
+ return -EINVAL;
+
+ if (uc->config.needs_epib && len < CPPI5_INFO0_HDESC_EPIB_SIZE)
+ return -EINVAL;
+
+ h_desc = d->hwdesc[0].cppi5_desc_vaddr;
+ if (d->dir == DMA_MEM_TO_DEV)
+ memcpy(h_desc->epib, data, len);
+
+ if (uc->config.needs_epib)
+ psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
+
+ d->metadata = data;
+ d->metadata_size = len;
+ if (uc->config.needs_epib)
+ flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
+
+ cppi5_hdesc_update_flags(h_desc, flags);
+ cppi5_hdesc_update_psdata_size(h_desc, psd_size);
+
+ return 0;
+}
+
+void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
+ size_t *payload_len, size_t *max_len)
+{
+ struct udma_desc *d = to_udma_desc(desc);
+ struct udma_chan *uc = to_udma_chan(desc->chan);
+ struct cppi5_host_desc_t *h_desc;
+
+ if (!uc->config.pkt_mode || !uc->config.metadata_size)
+ return ERR_PTR(-EOPNOTSUPP);
+
+ h_desc = d->hwdesc[0].cppi5_desc_vaddr;
+
+ *max_len = uc->config.metadata_size;
+
+ *payload_len = cppi5_hdesc_epib_present(&h_desc->hdr) ?
+ CPPI5_INFO0_HDESC_EPIB_SIZE : 0;
+ *payload_len += cppi5_hdesc_get_psdata_size(h_desc);
+
+ return h_desc->epib;
+}
+
+int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
+ size_t payload_len)
+{
+ struct udma_desc *d = to_udma_desc(desc);
+ struct udma_chan *uc = to_udma_chan(desc->chan);
+ struct cppi5_host_desc_t *h_desc;
+ u32 psd_size = payload_len;
+ u32 flags = 0;
+
+ if (!uc->config.pkt_mode || !uc->config.metadata_size)
+ return -EOPNOTSUPP;
+
+ if (payload_len > uc->config.metadata_size)
+ return -EINVAL;
+
+ if (uc->config.needs_epib && payload_len < CPPI5_INFO0_HDESC_EPIB_SIZE)
+ return -EINVAL;
+
+ h_desc = d->hwdesc[0].cppi5_desc_vaddr;
+
+ if (uc->config.needs_epib) {
+ psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
+ flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
+ }
+
+ cppi5_hdesc_update_flags(h_desc, flags);
+ cppi5_hdesc_update_psdata_size(h_desc, psd_size);
+
+ return 0;
+}
+
+struct dma_async_tx_descriptor *
+udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
+ unsigned int sglen, enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct udma_desc *d;
+ u32 burst;
+
+ 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,
+ dmaengine_get_direction_text(uc->config.dir),
+ dmaengine_get_direction_text(dir));
+ return NULL;
+ }
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_width = uc->cfg.src_addr_width;
+ burst = uc->cfg.src_maxburst;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_width = uc->cfg.dst_addr_width;
+ burst = uc->cfg.dst_maxburst;
+ } else {
+ dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ if (!burst)
+ burst = 1;
+
+ uc->config.tx_flags = tx_flags;
+
+ if (uc->config.pkt_mode)
+ d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
+ context);
+ 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;
+
+ d->dir = dir;
+ d->desc_idx = 0;
+ d->tr_idx = 0;
+
+ /* static TR for remote PDMA */
+ if (udma_configure_statictr(uc, d, dev_width, burst)) {
+ dev_err(uc->ud->dev,
+ "%s: StaticTR Z is limited to maximum %u (%u)\n",
+ __func__, uc->ud->match_data->statictr_z_mask,
+ d->static_tr.bstcnt);
+
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ if (uc->config.metadata_size)
+ d->vd.tx.metadata_ops = &metadata_ops;
+
+ return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
+}
+EXPORT_SYMBOL_GPL(udma_prep_slave_sg);
+
+struct udma_desc *
+udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
+ size_t buf_len, size_t period_len,
+ enum dma_transfer_direction dir, unsigned long flags)
+{
+ struct udma_desc *d;
+ size_t tr_size, period_addr;
+ struct cppi5_tr_type1_t *tr_req;
+ unsigned int periods = buf_len / period_len;
+ u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
+ unsigned int i;
+ int num_tr;
+ u32 period_csf = 0;
+
+ num_tr = udma_get_tr_counters(period_len, __ffs(buf_addr), &tr0_cnt0,
+ &tr0_cnt1, &tr1_cnt0);
+ if (num_tr < 0) {
+ dev_err(uc->ud->dev, "size %zu is not supported\n",
+ period_len);
+ return NULL;
+ }
+
+ /* Now allocate and setup the descriptor. */
+ tr_size = sizeof(struct cppi5_tr_type1_t);
+ d = udma_alloc_tr_desc(uc, tr_size, periods * num_tr, dir);
+ if (!d)
+ return NULL;
+
+ tr_req = d->hwdesc[0].tr_req_base;
+ 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 BCDMA <-> PDMA transfers, the EOP flag needs to be set on the
+ * last TR of a descriptor, to mark the packet as complete.
+ * This is required for getting the teardown completion message in case
+ * of TX, and to avoid short-packet error in case of RX.
+ *
+ * As we are in cyclic mode, we do not know which period might be the
+ * last one, so set the flag for each period.
+ */
+ if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
+ uc->ud->match_data->type == DMA_TYPE_BCDMA) {
+ period_csf = CPPI5_TR_CSF_EOP;
+ }
+
+ for (i = 0; i < periods; i++) {
+ int tr_idx = i * num_tr;
+
+ cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
+ false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+
+ tr_req[tr_idx].addr = period_addr;
+ tr_req[tr_idx].icnt0 = tr0_cnt0;
+ tr_req[tr_idx].icnt1 = tr0_cnt1;
+ tr_req[tr_idx].dim1 = tr0_cnt0;
+
+ if (num_tr == 2) {
+ cppi5_tr_csf_set(&tr_req[tr_idx].flags,
+ CPPI5_TR_CSF_SUPR_EVT);
+ tr_idx++;
+
+ cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
+ false, false,
+ CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+
+ tr_req[tr_idx].addr = period_addr + tr0_cnt1 * tr0_cnt0;
+ tr_req[tr_idx].icnt0 = tr1_cnt0;
+ tr_req[tr_idx].icnt1 = 1;
+ tr_req[tr_idx].dim1 = tr1_cnt0;
+ }
+
+ if (!(flags & DMA_PREP_INTERRUPT))
+ period_csf |= CPPI5_TR_CSF_SUPR_EVT;
+
+ if (period_csf)
+ cppi5_tr_csf_set(&tr_req[tr_idx].flags, period_csf);
+
+ period_addr += period_len;
+ }
+
+ return d;
+}
+
+struct udma_desc *
+udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
+ size_t buf_len, size_t period_len,
+ enum dma_transfer_direction dir, unsigned long flags)
+{
+ struct udma_desc *d;
+ u32 ring_id;
+ int i;
+ int periods = buf_len / period_len;
+
+ if (periods > (K3_UDMA_DEFAULT_RING_SIZE - 1))
+ return NULL;
+
+ if (period_len >= SZ_4M)
+ return NULL;
+
+ d = kzalloc(struct_size(d, hwdesc, periods), GFP_NOWAIT);
+ if (!d)
+ return NULL;
+
+ d->hwdesc_count = periods;
+
+ /* TODO: re-check this... */
+ if (dir == DMA_DEV_TO_MEM)
+ ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
+ 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);
+ struct cppi5_host_desc_t *h_desc;
+
+ hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
+ GFP_NOWAIT,
+ &hwdesc->cppi5_desc_paddr);
+ if (!hwdesc->cppi5_desc_vaddr) {
+ dev_err(uc->ud->dev,
+ "descriptor%d allocation failed\n", i);
+
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ hwdesc->cppi5_desc_size = uc->config.hdesc_size;
+ h_desc = hwdesc->cppi5_desc_vaddr;
+
+ cppi5_hdesc_init(h_desc, 0, 0);
+ cppi5_hdesc_set_pktlen(h_desc, period_len);
+
+ /* Flow and Packed ID */
+ cppi5_desc_set_pktids(&h_desc->hdr, uc->id,
+ CPPI5_INFO1_DESC_FLOWID_DEFAULT);
+ cppi5_desc_set_retpolicy(&h_desc->hdr, 0, ring_id);
+
+ /* attach each period to a new descriptor */
+ cppi5_hdesc_attach_buf(h_desc,
+ period_addr, period_len,
+ period_addr, period_len);
+ }
+
+ return d;
+}
+
+struct dma_async_tx_descriptor *
+udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
+ size_t period_len, enum dma_transfer_direction dir,
+ unsigned long flags)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ enum dma_slave_buswidth dev_width;
+ struct udma_desc *d;
+ u32 burst;
+
+ if (dir != uc->config.dir) {
+ dev_err(chan->device->dev,
+ "%s: chan%d is for %s, not supporting %s\n",
+ __func__, uc->id,
+ dmaengine_get_direction_text(uc->config.dir),
+ dmaengine_get_direction_text(dir));
+ return NULL;
+ }
+
+ uc->cyclic = true;
+
+ if (dir == DMA_DEV_TO_MEM) {
+ dev_width = uc->cfg.src_addr_width;
+ burst = uc->cfg.src_maxburst;
+ } else if (dir == DMA_MEM_TO_DEV) {
+ dev_width = uc->cfg.dst_addr_width;
+ burst = uc->cfg.dst_maxburst;
+ } else {
+ dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
+ return NULL;
+ }
+
+ if (!burst)
+ burst = 1;
+
+ if (uc->config.pkt_mode)
+ d = udma_prep_dma_cyclic_pkt(uc, buf_addr, buf_len, period_len,
+ dir, flags);
+ else
+ d = udma_prep_dma_cyclic_tr(uc, buf_addr, buf_len, period_len,
+ dir, flags);
+
+ if (!d)
+ return NULL;
+
+ d->sglen = buf_len / period_len;
+
+ d->dir = dir;
+ d->residue = buf_len;
+
+ /* static TR for remote PDMA */
+ if (udma_configure_statictr(uc, d, dev_width, burst)) {
+ dev_err(uc->ud->dev,
+ "%s: StaticTR Z is limited to maximum %u (%u)\n",
+ __func__, uc->ud->match_data->statictr_z_mask,
+ d->static_tr.bstcnt);
+
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return NULL;
+ }
+
+ if (uc->config.metadata_size)
+ d->vd.tx.metadata_ops = &metadata_ops;
+
+ return vchan_tx_prep(&uc->vc, &d->vd, flags);
+}
+EXPORT_SYMBOL_GPL(udma_prep_dma_cyclic);
+
+struct dma_async_tx_descriptor *
+udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long tx_flags)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ struct udma_desc *d;
+ 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;
+ u32 csf = CPPI5_TR_CSF_SUPR_EVT;
+
+ if (uc->config.dir != DMA_MEM_TO_MEM) {
+ dev_err(chan->device->dev,
+ "%s: chan%d is for %s, not supporting %s\n",
+ __func__, uc->id,
+ dmaengine_get_direction_text(uc->config.dir),
+ dmaengine_get_direction_text(DMA_MEM_TO_MEM));
+ return NULL;
+ }
+
+ num_tr = udma_get_tr_counters(len, __ffs(src | dest), &tr0_cnt0,
+ &tr0_cnt1, &tr1_cnt0);
+ if (num_tr < 0) {
+ dev_err(uc->ud->dev, "size %zu is not supported\n",
+ len);
+ return NULL;
+ }
+
+ d = udma_alloc_tr_desc(uc, tr_size, num_tr, DMA_MEM_TO_MEM);
+ if (!d)
+ return NULL;
+
+ d->dir = DMA_MEM_TO_MEM;
+ d->desc_idx = 0;
+ 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;
+ } else {
+ csf |= CPPI5_TR_CSF_EOL_ICNT0;
+ }
+
+ tr_req = d->hwdesc[0].tr_req_base;
+
+ cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
+ CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+ cppi5_tr_csf_set(&tr_req[0].flags, csf);
+
+ 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, csf);
+
+ 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, csf | CPPI5_TR_CSF_EOP);
+
+ if (uc->config.metadata_size)
+ d->vd.tx.metadata_ops = &metadata_ops;
+
+ return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
+}
+EXPORT_SYMBOL_GPL(udma_prep_dma_memcpy);
+
+void udma_desc_pre_callback(struct virt_dma_chan *vc,
+ struct virt_dma_desc *vd,
+ struct dmaengine_result *result)
+{
+ struct udma_chan *uc = to_udma_chan(&vc->chan);
+ struct udma_desc *d;
+ u8 status;
+
+ if (!vd)
+ return;
+
+ d = to_udma_desc(&vd->tx);
+
+ if (d->metadata_size)
+ udma_fetch_epib(uc, d);
+
+ if (result) {
+ void *desc_vaddr = udma_curr_cppi5_desc_vaddr(d, d->desc_idx);
+
+ if (cppi5_desc_get_type(desc_vaddr) ==
+ CPPI5_INFO0_DESC_TYPE_VAL_HOST) {
+ /* Provide residue information for the client */
+ result->residue = d->residue -
+ cppi5_hdesc_get_pktlen(desc_vaddr);
+ if (result->residue)
+ result->result = DMA_TRANS_ABORTED;
+ else
+ result->result = DMA_TRANS_NOERROR;
+ } else {
+ result->residue = 0;
+ /* Propagate TR Response errors to the client */
+ status = d->hwdesc[0].tr_resp_base->status;
+ if (status)
+ result->result = DMA_TRANS_ABORTED;
+ else
+ result->result = DMA_TRANS_NOERROR;
+ }
+ }
+}
+EXPORT_SYMBOL_GPL(udma_desc_pre_callback);
+
+MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 4adcd679c6997..0a1291829611f 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -131,105 +131,6 @@ static void udma_dump_chan_stdata(struct udma_chan *uc)
}
}
-static struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
- dma_addr_t paddr)
-{
- struct udma_desc *d = uc->terminated_desc;
-
- if (d) {
- dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
- d->desc_idx);
-
- if (desc_paddr != paddr)
- d = NULL;
- }
-
- if (!d) {
- d = uc->desc;
- if (d) {
- dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
- d->desc_idx);
-
- if (desc_paddr != paddr)
- d = NULL;
- }
- }
-
- return d;
-}
-
-static void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
-{
- if (uc->use_dma_pool) {
- int i;
-
- for (i = 0; i < d->hwdesc_count; i++) {
- if (!d->hwdesc[i].cppi5_desc_vaddr)
- continue;
-
- dma_pool_free(uc->hdesc_pool,
- d->hwdesc[i].cppi5_desc_vaddr,
- d->hwdesc[i].cppi5_desc_paddr);
-
- d->hwdesc[i].cppi5_desc_vaddr = NULL;
- }
- } else if (d->hwdesc[0].cppi5_desc_vaddr) {
- dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
- d->hwdesc[0].cppi5_desc_vaddr,
- d->hwdesc[0].cppi5_desc_paddr);
-
- d->hwdesc[0].cppi5_desc_vaddr = NULL;
- }
-}
-
-static void udma_purge_desc_work(struct work_struct *work)
-{
- struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
- struct virt_dma_desc *vd, *_vd;
- unsigned long flags;
- LIST_HEAD(head);
-
- spin_lock_irqsave(&ud->lock, flags);
- list_splice_tail_init(&ud->desc_to_purge, &head);
- spin_unlock_irqrestore(&ud->lock, flags);
-
- list_for_each_entry_safe(vd, _vd, &head, node) {
- struct udma_chan *uc = to_udma_chan(vd->tx.chan);
- struct udma_desc *d = to_udma_desc(&vd->tx);
-
- udma_free_hwdesc(uc, d);
- list_del(&vd->node);
- kfree(d);
- }
-
- /* If more to purge, schedule the work again */
- if (!list_empty(&ud->desc_to_purge))
- schedule_work(&ud->purge_work);
-}
-
-static void udma_desc_free(struct virt_dma_desc *vd)
-{
- struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
- struct udma_chan *uc = to_udma_chan(vd->tx.chan);
- struct udma_desc *d = to_udma_desc(&vd->tx);
- unsigned long flags;
-
- if (uc->terminated_desc == d)
- uc->terminated_desc = NULL;
-
- if (uc->use_dma_pool) {
- udma_free_hwdesc(uc, d);
- kfree(d);
- return;
- }
-
- spin_lock_irqsave(&ud->lock, flags);
- list_add_tail(&vd->node, &ud->desc_to_purge);
- spin_unlock_irqrestore(&ud->lock, flags);
-
- schedule_work(&ud->purge_work);
-}
-
static bool udma_is_chan_running(struct udma_chan *uc)
{
u32 trt_ctl = 0;
@@ -303,17 +204,6 @@ static int udma_push_to_ring(struct udma_chan *uc, int idx)
return k3_ringacc_ring_push(ring, &paddr);
}
-static bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr)
-{
- if (uc->config.dir != DMA_DEV_TO_MEM)
- return false;
-
- if (addr == udma_get_rx_flush_hwdesc_paddr(uc))
- return true;
-
- return false;
-}
-
static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
{
struct k3_ring *ring = NULL;
@@ -674,33 +564,6 @@ static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
d->desc_idx = (d->desc_idx + 1) % d->sglen;
}
-static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
-{
- u32 peer_bcnt, bcnt;
-
- /*
- * Only TX towards PDMA is affected.
- * If DMA_PREP_INTERRUPT is not set by consumer then skip the transfer
- * completion calculation, consumer must ensure that there is no stale
- * data in DMA fabric in this case.
- */
- if (uc->config.ep_type == PSIL_EP_NATIVE ||
- uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
- return true;
-
- peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
- bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
-
- /* Transfer is incomplete, store current residue and time stamp */
- if (peer_bcnt < bcnt) {
- uc->tx_drain.residue = bcnt - peer_bcnt;
- uc->tx_drain.tstamp = ktime_get();
- return false;
- }
-
- return true;
-}
-
static void udma_check_tx_completion(struct work_struct *work)
{
struct udma_chan *uc = container_of(work, typeof(*uc),
@@ -2344,1047 +2207,6 @@ static int udma_slave_config(struct dma_chan *chan,
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:
- break;
- default:
- dev_err(uc->ud->dev, "Unsupported TR size of %zu\n", tr_size);
- return NULL;
- }
-
- /* We have only one descriptor containing multiple TRs */
- d = kzalloc(sizeof(*d) + sizeof(d->hwdesc[0]), GFP_NOWAIT);
- if (!d)
- return NULL;
-
- d->sglen = tr_count;
-
- d->hwdesc_count = 1;
- hwdesc = &d->hwdesc[0];
-
- /* Allocate memory for DMA ring descriptor */
- if (uc->use_dma_pool) {
- hwdesc->cppi5_desc_size = uc->config.hdesc_size;
- hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
- GFP_NOWAIT,
- &hwdesc->cppi5_desc_paddr);
- } else {
- hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size,
- tr_count);
- hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
- uc->ud->desc_align);
- hwdesc->cppi5_desc_vaddr = dma_alloc_coherent(uc->ud->dev,
- hwdesc->cppi5_desc_size,
- &hwdesc->cppi5_desc_paddr,
- GFP_NOWAIT);
- }
-
- if (!hwdesc->cppi5_desc_vaddr) {
- kfree(d);
- return NULL;
- }
-
- /* Start of the TR req records */
- hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
- /* Start address of the TR response array */
- hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size * tr_count;
-
- tr_desc = hwdesc->cppi5_desc_vaddr;
-
- if (uc->cyclic)
- reload_count = CPPI5_INFO0_TRDESC_RLDCNT_INFINITE;
-
- if (dir == DMA_DEV_TO_MEM)
- ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
- else
- ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
-
- cppi5_trdesc_init(tr_desc, tr_count, tr_size, 0, reload_count);
- cppi5_desc_set_pktids(tr_desc, uc->id,
- CPPI5_INFO1_DESC_FLOWID_DEFAULT);
- cppi5_desc_set_retpolicy(tr_desc, 0, ring_id);
-
- return d;
-}
-
-/**
- * udma_get_tr_counters - calculate TR counters for a given length
- * @len: Length of the trasnfer
- * @align_to: Preferred alignment
- * @tr0_cnt0: First TR icnt0
- * @tr0_cnt1: First TR icnt1
- * @tr1_cnt0: Second (if used) TR icnt0
- *
- * For len < SZ_64K only one TR is enough, tr1_cnt0 is not updated
- * For len >= SZ_64K two TRs are used in a simple way:
- * First TR: SZ_64K-alignment blocks (tr0_cnt0, tr0_cnt1)
- * Second TR: the remaining length (tr1_cnt0)
- *
- * Returns the number of TRs the length needs (1 or 2)
- * -EINVAL if the length can not be supported
- */
-static int udma_get_tr_counters(size_t len, unsigned long align_to,
- u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0)
-{
- if (len < SZ_64K) {
- *tr0_cnt0 = len;
- *tr0_cnt1 = 1;
-
- return 1;
- }
-
- if (align_to > 3)
- align_to = 3;
-
-realign:
- *tr0_cnt0 = SZ_64K - BIT(align_to);
- if (len / *tr0_cnt0 >= SZ_64K) {
- if (align_to) {
- align_to--;
- goto realign;
- }
- return -EINVAL;
- }
-
- *tr0_cnt1 = len / *tr0_cnt0;
- *tr1_cnt0 = len % *tr0_cnt0;
-
- return 2;
-}
-
-static struct udma_desc *
-udma_prep_slave_sg_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 udma_desc *d;
- struct cppi5_tr_type1_t *tr_req = NULL;
- u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
- unsigned int i;
- 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) {
- if (sg_dma_len(sgent) < SZ_64K)
- num_tr++;
- else
- num_tr += 2;
- }
-
- /* Now allocate and setup the descriptor. */
- tr_size = sizeof(struct cppi5_tr_type1_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;
-
- tr_req = d->hwdesc[0].tr_req_base;
- for_each_sg(sgl, sgent, sglen, i) {
- dma_addr_t sg_addr = sg_dma_address(sgent);
-
- num_tr = udma_get_tr_counters(sg_dma_len(sgent), __ffs(sg_addr),
- &tr0_cnt0, &tr0_cnt1, &tr1_cnt0);
- if (num_tr < 0) {
- dev_err(uc->ud->dev, "size %u is not supported\n",
- sg_dma_len(sgent));
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
- 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;
- tr_req[tr_idx].dim1 = tr0_cnt0;
- tr_idx++;
-
- if (num_tr == 2) {
- cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
- false, false,
- CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
- cppi5_tr_csf_set(&tr_req[tr_idx].flags,
- CPPI5_TR_CSF_SUPR_EVT);
-
- tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
- tr_req[tr_idx].icnt0 = tr1_cnt0;
- tr_req[tr_idx].icnt1 = 1;
- tr_req[tr_idx].dim1 = tr1_cnt0;
- tr_idx++;
- }
-
- d->residue += sg_dma_len(sgent);
- }
-
- cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
- CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
-
- 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;
- u32 csf = CPPI5_TR_CSF_SUPR_EVT;
- 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;
- csf |= CPPI5_TR_CSF_EOL_ICNT0;
- } 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, csf);
- 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, csf);
- 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, csf | 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)
-{
- if (uc->config.ep_type != PSIL_EP_PDMA_XY)
- return 0;
-
- /* Bus width translates to the element size (ES) */
- switch (dev_width) {
- case DMA_SLAVE_BUSWIDTH_1_BYTE:
- d->static_tr.elsize = 0;
- break;
- case DMA_SLAVE_BUSWIDTH_2_BYTES:
- d->static_tr.elsize = 1;
- break;
- case DMA_SLAVE_BUSWIDTH_3_BYTES:
- d->static_tr.elsize = 2;
- break;
- case DMA_SLAVE_BUSWIDTH_4_BYTES:
- d->static_tr.elsize = 3;
- break;
- case DMA_SLAVE_BUSWIDTH_8_BYTES:
- d->static_tr.elsize = 4;
- break;
- default: /* not reached */
- return -EINVAL;
- }
-
- d->static_tr.elcnt = elcnt;
-
- if (uc->config.pkt_mode || !uc->cyclic) {
- /*
- * PDMA must close the packet when the channel is in packet mode.
- * For TR mode when the channel is not cyclic we also need PDMA
- * to close the packet otherwise the transfer will stall because
- * PDMA holds on the data it has received from the peripheral.
- */
- unsigned int div = dev_width * elcnt;
-
- if (uc->cyclic)
- d->static_tr.bstcnt = d->residue / d->sglen / div;
- else
- d->static_tr.bstcnt = d->residue / div;
- } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
- uc->config.dir == DMA_DEV_TO_MEM &&
- uc->cyclic) {
- /*
- * For cyclic mode with BCDMA we have to set EOP in each TR to
- * prevent short packet errors seen on channel teardown. So the
- * PDMA must close the packet after every TR transfer by setting
- * burst count equal to the number of bytes transferred.
- */
- struct cppi5_tr_type1_t *tr_req = d->hwdesc[0].tr_req_base;
-
- d->static_tr.bstcnt =
- (tr_req->icnt0 * tr_req->icnt1) / dev_width;
- } else {
- d->static_tr.bstcnt = 0;
- }
-
- if (uc->config.dir == DMA_DEV_TO_MEM &&
- d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask)
- return -EINVAL;
-
- return 0;
-}
-
-static struct udma_desc *
-udma_prep_slave_sg_pkt(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_host_desc_t *h_desc = NULL;
- struct udma_desc *d;
- u32 ring_id;
- unsigned int i;
- u64 asel;
-
- d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
- if (!d)
- return NULL;
-
- d->sglen = sglen;
- d->hwdesc_count = sglen;
-
- if (dir == DMA_DEV_TO_MEM)
- ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
- 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);
- struct cppi5_host_desc_t *desc;
- size_t sg_len = sg_dma_len(sgent);
-
- hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
- GFP_NOWAIT,
- &hwdesc->cppi5_desc_paddr);
- if (!hwdesc->cppi5_desc_vaddr) {
- dev_err(uc->ud->dev,
- "descriptor%d allocation failed\n", i);
-
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- d->residue += sg_len;
- hwdesc->cppi5_desc_size = uc->config.hdesc_size;
- desc = hwdesc->cppi5_desc_vaddr;
-
- if (i == 0) {
- cppi5_hdesc_init(desc, 0, 0);
- /* Flow and Packed ID */
- cppi5_desc_set_pktids(&desc->hdr, uc->id,
- CPPI5_INFO1_DESC_FLOWID_DEFAULT);
- cppi5_desc_set_retpolicy(&desc->hdr, 0, ring_id);
- } else {
- cppi5_hdesc_reset_hbdesc(desc);
- cppi5_desc_set_retpolicy(&desc->hdr, 0, 0xffff);
- }
-
- /* 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 | asel);
-
- if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
- dir == DMA_MEM_TO_DEV)
- h_desc = desc;
- }
-
- if (d->residue >= SZ_4M) {
- dev_err(uc->ud->dev,
- "%s: Transfer size %u is over the supported 4M range\n",
- __func__, d->residue);
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- h_desc = d->hwdesc[0].cppi5_desc_vaddr;
- cppi5_hdesc_set_pktlen(h_desc, d->residue);
-
- return d;
-}
-
-static int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
- void *data, size_t len)
-{
- struct udma_desc *d = to_udma_desc(desc);
- struct udma_chan *uc = to_udma_chan(desc->chan);
- struct cppi5_host_desc_t *h_desc;
- u32 psd_size = len;
- u32 flags = 0;
-
- if (!uc->config.pkt_mode || !uc->config.metadata_size)
- return -ENOTSUPP;
-
- if (!data || len > uc->config.metadata_size)
- return -EINVAL;
-
- if (uc->config.needs_epib && len < CPPI5_INFO0_HDESC_EPIB_SIZE)
- return -EINVAL;
-
- h_desc = d->hwdesc[0].cppi5_desc_vaddr;
- if (d->dir == DMA_MEM_TO_DEV)
- memcpy(h_desc->epib, data, len);
-
- if (uc->config.needs_epib)
- psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
-
- d->metadata = data;
- d->metadata_size = len;
- if (uc->config.needs_epib)
- flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
-
- cppi5_hdesc_update_flags(h_desc, flags);
- cppi5_hdesc_update_psdata_size(h_desc, psd_size);
-
- return 0;
-}
-
-static void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
- size_t *payload_len, size_t *max_len)
-{
- struct udma_desc *d = to_udma_desc(desc);
- struct udma_chan *uc = to_udma_chan(desc->chan);
- struct cppi5_host_desc_t *h_desc;
-
- if (!uc->config.pkt_mode || !uc->config.metadata_size)
- return ERR_PTR(-ENOTSUPP);
-
- h_desc = d->hwdesc[0].cppi5_desc_vaddr;
-
- *max_len = uc->config.metadata_size;
-
- *payload_len = cppi5_hdesc_epib_present(&h_desc->hdr) ?
- CPPI5_INFO0_HDESC_EPIB_SIZE : 0;
- *payload_len += cppi5_hdesc_get_psdata_size(h_desc);
-
- return h_desc->epib;
-}
-
-static int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
- size_t payload_len)
-{
- struct udma_desc *d = to_udma_desc(desc);
- struct udma_chan *uc = to_udma_chan(desc->chan);
- struct cppi5_host_desc_t *h_desc;
- u32 psd_size = payload_len;
- u32 flags = 0;
-
- if (!uc->config.pkt_mode || !uc->config.metadata_size)
- return -ENOTSUPP;
-
- if (payload_len > uc->config.metadata_size)
- return -EINVAL;
-
- if (uc->config.needs_epib && payload_len < CPPI5_INFO0_HDESC_EPIB_SIZE)
- return -EINVAL;
-
- h_desc = d->hwdesc[0].cppi5_desc_vaddr;
-
- if (uc->config.needs_epib) {
- psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
- flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
- }
-
- cppi5_hdesc_update_flags(h_desc, flags);
- cppi5_hdesc_update_psdata_size(h_desc, psd_size);
-
- return 0;
-}
-
-static struct dma_descriptor_metadata_ops metadata_ops = {
- .attach = udma_attach_metadata,
- .get_ptr = udma_get_metadata_ptr,
- .set_len = udma_set_metadata_len,
-};
-
-static struct dma_async_tx_descriptor *
-udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
- unsigned int sglen, enum dma_transfer_direction dir,
- unsigned long tx_flags, void *context)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- enum dma_slave_buswidth dev_width;
- struct udma_desc *d;
- u32 burst;
-
- 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,
- dmaengine_get_direction_text(uc->config.dir),
- dmaengine_get_direction_text(dir));
- return NULL;
- }
-
- if (dir == DMA_DEV_TO_MEM) {
- dev_width = uc->cfg.src_addr_width;
- burst = uc->cfg.src_maxburst;
- } else if (dir == DMA_MEM_TO_DEV) {
- dev_width = uc->cfg.dst_addr_width;
- burst = uc->cfg.dst_maxburst;
- } else {
- dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
- return NULL;
- }
-
- if (!burst)
- burst = 1;
-
- uc->config.tx_flags = tx_flags;
-
- if (uc->config.pkt_mode)
- d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
- context);
- 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;
-
- d->dir = dir;
- d->desc_idx = 0;
- d->tr_idx = 0;
-
- /* static TR for remote PDMA */
- if (udma_configure_statictr(uc, d, dev_width, burst)) {
- dev_err(uc->ud->dev,
- "%s: StaticTR Z is limited to maximum %u (%u)\n",
- __func__, uc->ud->match_data->statictr_z_mask,
- d->static_tr.bstcnt);
-
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- if (uc->config.metadata_size)
- d->vd.tx.metadata_ops = &metadata_ops;
-
- return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
-}
-
-static struct udma_desc *
-udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
- size_t buf_len, size_t period_len,
- enum dma_transfer_direction dir, unsigned long flags)
-{
- struct udma_desc *d;
- size_t tr_size, period_addr;
- struct cppi5_tr_type1_t *tr_req;
- unsigned int periods = buf_len / period_len;
- u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
- unsigned int i;
- int num_tr;
- u32 period_csf = 0;
-
- num_tr = udma_get_tr_counters(period_len, __ffs(buf_addr), &tr0_cnt0,
- &tr0_cnt1, &tr1_cnt0);
- if (num_tr < 0) {
- dev_err(uc->ud->dev, "size %zu is not supported\n",
- period_len);
- return NULL;
- }
-
- /* Now allocate and setup the descriptor. */
- tr_size = sizeof(struct cppi5_tr_type1_t);
- d = udma_alloc_tr_desc(uc, tr_size, periods * num_tr, dir);
- if (!d)
- return NULL;
-
- tr_req = d->hwdesc[0].tr_req_base;
- 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 BCDMA <-> PDMA transfers, the EOP flag needs to be set on the
- * last TR of a descriptor, to mark the packet as complete.
- * This is required for getting the teardown completion message in case
- * of TX, and to avoid short-packet error in case of RX.
- *
- * As we are in cyclic mode, we do not know which period might be the
- * last one, so set the flag for each period.
- */
- if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
- uc->ud->match_data->type == DMA_TYPE_BCDMA) {
- period_csf = CPPI5_TR_CSF_EOP;
- }
-
- for (i = 0; i < periods; i++) {
- int tr_idx = i * num_tr;
-
- cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
- false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
-
- tr_req[tr_idx].addr = period_addr;
- tr_req[tr_idx].icnt0 = tr0_cnt0;
- tr_req[tr_idx].icnt1 = tr0_cnt1;
- tr_req[tr_idx].dim1 = tr0_cnt0;
-
- if (num_tr == 2) {
- cppi5_tr_csf_set(&tr_req[tr_idx].flags,
- CPPI5_TR_CSF_SUPR_EVT);
- tr_idx++;
-
- cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
- false, false,
- CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
-
- tr_req[tr_idx].addr = period_addr + tr0_cnt1 * tr0_cnt0;
- tr_req[tr_idx].icnt0 = tr1_cnt0;
- tr_req[tr_idx].icnt1 = 1;
- tr_req[tr_idx].dim1 = tr1_cnt0;
- }
-
- if (!(flags & DMA_PREP_INTERRUPT))
- period_csf |= CPPI5_TR_CSF_SUPR_EVT;
-
- if (period_csf)
- cppi5_tr_csf_set(&tr_req[tr_idx].flags, period_csf);
-
- period_addr += period_len;
- }
-
- return d;
-}
-
-static struct udma_desc *
-udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
- size_t buf_len, size_t period_len,
- enum dma_transfer_direction dir, unsigned long flags)
-{
- struct udma_desc *d;
- u32 ring_id;
- int i;
- int periods = buf_len / period_len;
-
- if (periods > (K3_UDMA_DEFAULT_RING_SIZE - 1))
- return NULL;
-
- if (period_len >= SZ_4M)
- return NULL;
-
- d = kzalloc(struct_size(d, hwdesc, periods), GFP_NOWAIT);
- if (!d)
- return NULL;
-
- d->hwdesc_count = periods;
-
- /* TODO: re-check this... */
- if (dir == DMA_DEV_TO_MEM)
- ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
- 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);
- struct cppi5_host_desc_t *h_desc;
-
- hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
- GFP_NOWAIT,
- &hwdesc->cppi5_desc_paddr);
- if (!hwdesc->cppi5_desc_vaddr) {
- dev_err(uc->ud->dev,
- "descriptor%d allocation failed\n", i);
-
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- hwdesc->cppi5_desc_size = uc->config.hdesc_size;
- h_desc = hwdesc->cppi5_desc_vaddr;
-
- cppi5_hdesc_init(h_desc, 0, 0);
- cppi5_hdesc_set_pktlen(h_desc, period_len);
-
- /* Flow and Packed ID */
- cppi5_desc_set_pktids(&h_desc->hdr, uc->id,
- CPPI5_INFO1_DESC_FLOWID_DEFAULT);
- cppi5_desc_set_retpolicy(&h_desc->hdr, 0, ring_id);
-
- /* attach each period to a new descriptor */
- cppi5_hdesc_attach_buf(h_desc,
- period_addr, period_len,
- period_addr, period_len);
- }
-
- return d;
-}
-
-static struct dma_async_tx_descriptor *
-udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
- size_t period_len, enum dma_transfer_direction dir,
- unsigned long flags)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- enum dma_slave_buswidth dev_width;
- struct udma_desc *d;
- u32 burst;
-
- if (dir != uc->config.dir) {
- dev_err(chan->device->dev,
- "%s: chan%d is for %s, not supporting %s\n",
- __func__, uc->id,
- dmaengine_get_direction_text(uc->config.dir),
- dmaengine_get_direction_text(dir));
- return NULL;
- }
-
- uc->cyclic = true;
-
- if (dir == DMA_DEV_TO_MEM) {
- dev_width = uc->cfg.src_addr_width;
- burst = uc->cfg.src_maxburst;
- } else if (dir == DMA_MEM_TO_DEV) {
- dev_width = uc->cfg.dst_addr_width;
- burst = uc->cfg.dst_maxburst;
- } else {
- dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
- return NULL;
- }
-
- if (!burst)
- burst = 1;
-
- if (uc->config.pkt_mode)
- d = udma_prep_dma_cyclic_pkt(uc, buf_addr, buf_len, period_len,
- dir, flags);
- else
- d = udma_prep_dma_cyclic_tr(uc, buf_addr, buf_len, period_len,
- dir, flags);
-
- if (!d)
- return NULL;
-
- d->sglen = buf_len / period_len;
-
- d->dir = dir;
- d->residue = buf_len;
-
- /* static TR for remote PDMA */
- if (udma_configure_statictr(uc, d, dev_width, burst)) {
- dev_err(uc->ud->dev,
- "%s: StaticTR Z is limited to maximum %u (%u)\n",
- __func__, uc->ud->match_data->statictr_z_mask,
- d->static_tr.bstcnt);
-
- udma_free_hwdesc(uc, d);
- kfree(d);
- return NULL;
- }
-
- if (uc->config.metadata_size)
- d->vd.tx.metadata_ops = &metadata_ops;
-
- return vchan_tx_prep(&uc->vc, &d->vd, flags);
-}
-
-static struct dma_async_tx_descriptor *
-udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
- size_t len, unsigned long tx_flags)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- struct udma_desc *d;
- 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;
- u32 csf = CPPI5_TR_CSF_SUPR_EVT;
-
- if (uc->config.dir != DMA_MEM_TO_MEM) {
- dev_err(chan->device->dev,
- "%s: chan%d is for %s, not supporting %s\n",
- __func__, uc->id,
- dmaengine_get_direction_text(uc->config.dir),
- dmaengine_get_direction_text(DMA_MEM_TO_MEM));
- return NULL;
- }
-
- num_tr = udma_get_tr_counters(len, __ffs(src | dest), &tr0_cnt0,
- &tr0_cnt1, &tr1_cnt0);
- if (num_tr < 0) {
- dev_err(uc->ud->dev, "size %zu is not supported\n",
- len);
- return NULL;
- }
-
- d = udma_alloc_tr_desc(uc, tr_size, num_tr, DMA_MEM_TO_MEM);
- if (!d)
- return NULL;
-
- d->dir = DMA_MEM_TO_MEM;
- d->desc_idx = 0;
- 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;
- } else {
- csf |= CPPI5_TR_CSF_EOL_ICNT0;
- }
-
- tr_req = d->hwdesc[0].tr_req_base;
-
- cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
- CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
- cppi5_tr_csf_set(&tr_req[0].flags, csf);
-
- 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, csf);
-
- 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, csf | CPPI5_TR_CSF_EOP);
-
- if (uc->config.metadata_size)
- d->vd.tx.metadata_ops = &metadata_ops;
-
- return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
-}
-
static void udma_issue_pending(struct dma_chan *chan)
{
struct udma_chan *uc = to_udma_chan(chan);
@@ -3587,46 +2409,6 @@ static void udma_synchronize(struct dma_chan *chan)
udma_reset_rings(uc);
}
-static void udma_desc_pre_callback(struct virt_dma_chan *vc,
- struct virt_dma_desc *vd,
- struct dmaengine_result *result)
-{
- struct udma_chan *uc = to_udma_chan(&vc->chan);
- struct udma_desc *d;
- u8 status;
-
- if (!vd)
- return;
-
- d = to_udma_desc(&vd->tx);
-
- if (d->metadata_size)
- udma_fetch_epib(uc, d);
-
- if (result) {
- void *desc_vaddr = udma_curr_cppi5_desc_vaddr(d, d->desc_idx);
-
- if (cppi5_desc_get_type(desc_vaddr) ==
- CPPI5_INFO0_DESC_TYPE_VAL_HOST) {
- /* Provide residue information for the client */
- result->residue = d->residue -
- cppi5_hdesc_get_pktlen(desc_vaddr);
- if (result->residue)
- result->result = DMA_TRANS_ABORTED;
- else
- result->result = DMA_TRANS_NOERROR;
- } else {
- result->residue = 0;
- /* Propagate TR Response errors to the client */
- status = d->hwdesc[0].tr_resp_base->status;
- if (status)
- result->result = DMA_TRANS_ABORTED;
- else
- result->result = DMA_TRANS_NOERROR;
- }
- }
-}
-
/*
* This tasklet handles the completion of a DMA descriptor by
* calling its callback and freeing it.
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 3a786b3eddc67..7c807bd9e178b 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -556,6 +556,60 @@ static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
memcpy(d->metadata, h_desc->epib, d->metadata_size);
}
+/* Common functions */
+struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
+ dma_addr_t paddr);
+void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d);
+void udma_purge_desc_work(struct work_struct *work);
+void udma_desc_free(struct virt_dma_desc *vd);
+bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr);
+bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d);
+struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
+ size_t tr_size, int tr_count,
+ enum dma_transfer_direction dir);
+int udma_get_tr_counters(size_t len, unsigned long align_to,
+ u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0);
+struct udma_desc *udma_prep_slave_sg_tr(struct udma_chan *uc,
+ struct scatterlist *sgl, unsigned int sglen,
+ enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context);
+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);
+int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
+ enum dma_slave_buswidth dev_width, u16 elcnt);
+struct udma_desc *udma_prep_slave_sg_pkt(struct udma_chan *uc,
+ struct scatterlist *sgl, unsigned int sglen,
+ enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context);
+int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
+ void *data, size_t len);
+void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
+ size_t *payload_len, size_t *max_len);
+int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
+ size_t payload_len);
+struct dma_async_tx_descriptor *udma_prep_slave_sg(struct dma_chan *chan,
+ struct scatterlist *sgl, unsigned int sglen,
+ enum dma_transfer_direction dir,
+ unsigned long tx_flags, void *context);
+struct udma_desc *udma_prep_dma_cyclic_tr(struct udma_chan *uc,
+ dma_addr_t buf_addr, size_t buf_len, size_t period_len,
+ enum dma_transfer_direction dir, unsigned long flags);
+struct udma_desc *udma_prep_dma_cyclic_pkt(struct udma_chan *uc,
+ dma_addr_t buf_addr, size_t buf_len, size_t period_len,
+ enum dma_transfer_direction dir, unsigned long flags);
+struct dma_async_tx_descriptor *udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
+ size_t buf_len, size_t period_len,
+ enum dma_transfer_direction dir,
+ unsigned long flags);
+struct dma_async_tx_descriptor *udma_prep_dma_memcpy(struct dma_chan *chan,
+ dma_addr_t dest, dma_addr_t src,
+ size_t len, unsigned long tx_flags);
+void udma_desc_pre_callback(struct virt_dma_chan *vc,
+ struct virt_dma_desc *vd,
+ struct dmaengine_result *result);
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 04/19] dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c
2026-01-30 11:01 ` [PATCH v4 04/19] dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c Sai Sree Kartheek Adivi
@ 2026-01-30 15:43 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:43 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:44PM +0530, Sai Sree Kartheek Adivi wrote:
> Refactor the K3 UDMA driver by moving all DMA descriptor handling
> functions from k3-udma.c to a new common library, k3-udma-common.c.
>
> This prepares the driver for supporting new K3 UDMA v2 variant
> (used in AM62L) that can reuse the same descriptor handling logic.
>
> To ensure this common code works correctly across all build
> configurations (built-in vs modules), introduce a hidden Kconfig symbol:
> CONFIG_TI_K3_UDMA_COMMON.
>
> No functional changes intended.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/Kconfig | 5 +
> drivers/dma/ti/Makefile | 1 +
> drivers/dma/ti/k3-udma-common.c | 1243 +++++++++++++++++++++++++++++++
> drivers/dma/ti/k3-udma.c | 1218 ------------------------------
Can you trt use good git option (-C), let git to detect copy?
Frank
> drivers/dma/ti/k3-udma.h | 54 ++
> 5 files changed, 1303 insertions(+), 1218 deletions(-)
> create mode 100644 drivers/dma/ti/k3-udma-common.c
>
> diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
> index cbc30ab627832..712e456015459 100644
> --- a/drivers/dma/ti/Kconfig
> +++ b/drivers/dma/ti/Kconfig
> @@ -42,12 +42,17 @@ config TI_K3_UDMA
> select DMA_ENGINE
> select DMA_VIRTUAL_CHANNELS
> select SOC_TI
> + select TI_K3_UDMA_COMMON
> select TI_K3_RINGACC
> select TI_K3_PSIL
> help
> Enable support for the TI UDMA (Unified DMA) controller. This
> DMA engine is used in AM65x and j721e.
>
> +config TI_K3_UDMA_COMMON
> + tristate
> + default n
> +
> config TI_K3_UDMA_GLUE_LAYER
> tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
> depends on ARCH_K3 || COMPILE_TEST
> diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
> index d376c117cecf6..3b91c02e55eaf 100644
> --- a/drivers/dma/ti/Makefile
> +++ b/drivers/dma/ti/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
> obj-$(CONFIG_TI_EDMA) += edma.o
> obj-$(CONFIG_DMA_OMAP) += omap-dma.o
> obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
> +obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
> obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
> k3-psil-lib-objs := k3-psil.o \
> k3-psil-am654.o \
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> new file mode 100644
> index 0000000000000..9cb35759c70bb
> --- /dev/null
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -0,0 +1,1243 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Copyright (C) 2025 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
> + */
> +
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmapool.h>
> +#include <linux/platform_device.h>
> +#include <linux/soc/ti/k3-ringacc.h>
> +
> +#include "k3-udma.h"
> +
> +struct dma_descriptor_metadata_ops metadata_ops = {
> + .attach = udma_attach_metadata,
> + .get_ptr = udma_get_metadata_ptr,
> + .set_len = udma_set_metadata_len,
> +};
> +
> +struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> + dma_addr_t paddr)
> +{
> + struct udma_desc *d = uc->terminated_desc;
> +
> + if (d) {
> + dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
> + d->desc_idx);
> +
> + if (desc_paddr != paddr)
> + d = NULL;
> + }
> +
> + if (!d) {
> + d = uc->desc;
> + if (d) {
> + dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
> + d->desc_idx);
> +
> + if (desc_paddr != paddr)
> + d = NULL;
> + }
> + }
> +
> + return d;
> +}
> +EXPORT_SYMBOL_GPL(udma_udma_desc_from_paddr);
> +
> +void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
> +{
> + if (uc->use_dma_pool) {
> + int i;
> +
> + for (i = 0; i < d->hwdesc_count; i++) {
> + if (!d->hwdesc[i].cppi5_desc_vaddr)
> + continue;
> +
> + dma_pool_free(uc->hdesc_pool,
> + d->hwdesc[i].cppi5_desc_vaddr,
> + d->hwdesc[i].cppi5_desc_paddr);
> +
> + d->hwdesc[i].cppi5_desc_vaddr = NULL;
> + }
> + } else if (d->hwdesc[0].cppi5_desc_vaddr) {
> + dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
> + d->hwdesc[0].cppi5_desc_vaddr,
> + d->hwdesc[0].cppi5_desc_paddr);
> +
> + d->hwdesc[0].cppi5_desc_vaddr = NULL;
> + }
> +}
> +
> +void udma_purge_desc_work(struct work_struct *work)
> +{
> + struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
> + struct virt_dma_desc *vd, *_vd;
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&ud->lock, flags);
> + list_splice_tail_init(&ud->desc_to_purge, &head);
> + spin_unlock_irqrestore(&ud->lock, flags);
> +
> + list_for_each_entry_safe(vd, _vd, &head, node) {
> + struct udma_chan *uc = to_udma_chan(vd->tx.chan);
> + struct udma_desc *d = to_udma_desc(&vd->tx);
> +
> + udma_free_hwdesc(uc, d);
> + list_del(&vd->node);
> + kfree(d);
> + }
> +
> + /* If more to purge, schedule the work again */
> + if (!list_empty(&ud->desc_to_purge))
> + schedule_work(&ud->purge_work);
> +}
> +EXPORT_SYMBOL_GPL(udma_purge_desc_work);
> +
> +void udma_desc_free(struct virt_dma_desc *vd)
> +{
> + struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
> + struct udma_chan *uc = to_udma_chan(vd->tx.chan);
> + struct udma_desc *d = to_udma_desc(&vd->tx);
> + unsigned long flags;
> +
> + if (uc->terminated_desc == d)
> + uc->terminated_desc = NULL;
> +
> + if (uc->use_dma_pool) {
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return;
> + }
> +
> + spin_lock_irqsave(&ud->lock, flags);
> + list_add_tail(&vd->node, &ud->desc_to_purge);
> + spin_unlock_irqrestore(&ud->lock, flags);
> +
> + schedule_work(&ud->purge_work);
> +}
> +EXPORT_SYMBOL_GPL(udma_desc_free);
> +
> +bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr)
> +{
> + if (uc->config.dir != DMA_DEV_TO_MEM)
> + return false;
> +
> + if (addr == udma_get_rx_flush_hwdesc_paddr(uc))
> + return true;
> +
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(udma_desc_is_rx_flush);
> +
> +bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
> +{
> + u32 peer_bcnt, bcnt;
> +
> + /*
> + * Only TX towards PDMA is affected.
> + * If DMA_PREP_INTERRUPT is not set by consumer then skip the transfer
> + * completion calculation, consumer must ensure that there is no stale
> + * data in DMA fabric in this case.
> + */
> + if (uc->config.ep_type == PSIL_EP_NATIVE ||
> + uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
> + return true;
> +
> + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> +
> + /* Transfer is incomplete, store current residue and time stamp */
> + if (peer_bcnt < bcnt) {
> + uc->tx_drain.residue = bcnt - peer_bcnt;
> + uc->tx_drain.tstamp = ktime_get();
> + return false;
> + }
> +
> + return true;
> +}
> +EXPORT_SYMBOL_GPL(udma_is_desc_really_done);
> +
> +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:
> + break;
> + default:
> + dev_err(uc->ud->dev, "Unsupported TR size of %zu\n", tr_size);
> + return NULL;
> + }
> +
> + /* We have only one descriptor containing multiple TRs */
> + d = kzalloc(sizeof(*d) + sizeof(d->hwdesc[0]), GFP_NOWAIT);
> + if (!d)
> + return NULL;
> +
> + d->sglen = tr_count;
> +
> + d->hwdesc_count = 1;
> + hwdesc = &d->hwdesc[0];
> +
> + /* Allocate memory for DMA ring descriptor */
> + if (uc->use_dma_pool) {
> + hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> + hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> + GFP_NOWAIT,
> + &hwdesc->cppi5_desc_paddr);
> + } else {
> + hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size,
> + tr_count);
> + hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
> + uc->ud->desc_align);
> + hwdesc->cppi5_desc_vaddr = dma_alloc_coherent(uc->ud->dev,
> + hwdesc->cppi5_desc_size,
> + &hwdesc->cppi5_desc_paddr,
> + GFP_NOWAIT);
> + }
> +
> + if (!hwdesc->cppi5_desc_vaddr) {
> + kfree(d);
> + return NULL;
> + }
> +
> + /* Start of the TR req records */
> + hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
> + /* Start address of the TR response array */
> + hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size * tr_count;
> +
> + tr_desc = hwdesc->cppi5_desc_vaddr;
> +
> + if (uc->cyclic)
> + reload_count = CPPI5_INFO0_TRDESC_RLDCNT_INFINITE;
> +
> + if (dir == DMA_DEV_TO_MEM)
> + ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> + else
> + ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
> +
> + cppi5_trdesc_init(tr_desc, tr_count, tr_size, 0, reload_count);
> + cppi5_desc_set_pktids(tr_desc, uc->id,
> + CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> + cppi5_desc_set_retpolicy(tr_desc, 0, ring_id);
> +
> + return d;
> +}
> +
> +/**
> + * udma_get_tr_counters - calculate TR counters for a given length
> + * @len: Length of the transfer
> + * @align_to: Preferred alignment
> + * @tr0_cnt0: First TR icnt0
> + * @tr0_cnt1: First TR icnt1
> + * @tr1_cnt0: Second (if used) TR icnt0
> + *
> + * For len < SZ_64K only one TR is enough, tr1_cnt0 is not updated
> + * For len >= SZ_64K two TRs are used in a simple way:
> + * First TR: SZ_64K-alignment blocks (tr0_cnt0, tr0_cnt1)
> + * Second TR: the remaining length (tr1_cnt0)
> + *
> + * Returns the number of TRs the length needs (1 or 2)
> + * -EINVAL if the length can not be supported
> + */
> +int udma_get_tr_counters(size_t len, unsigned long align_to,
> + u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0)
> +{
> + if (len < SZ_64K) {
> + *tr0_cnt0 = len;
> + *tr0_cnt1 = 1;
> +
> + return 1;
> + }
> +
> + if (align_to > 3)
> + align_to = 3;
> +
> +realign:
> + *tr0_cnt0 = SZ_64K - BIT(align_to);
> + if (len / *tr0_cnt0 >= SZ_64K) {
> + if (align_to) {
> + align_to--;
> + goto realign;
> + }
> + return -EINVAL;
> + }
> +
> + *tr0_cnt1 = len / *tr0_cnt0;
> + *tr1_cnt0 = len % *tr0_cnt0;
> +
> + return 2;
> +}
> +
> +struct udma_desc *
> +udma_prep_slave_sg_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 udma_desc *d;
> + struct cppi5_tr_type1_t *tr_req = NULL;
> + u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
> + unsigned int i;
> + 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) {
> + if (sg_dma_len(sgent) < SZ_64K)
> + num_tr++;
> + else
> + num_tr += 2;
> + }
> +
> + /* Now allocate and setup the descriptor. */
> + tr_size = sizeof(struct cppi5_tr_type1_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;
> +
> + tr_req = d->hwdesc[0].tr_req_base;
> + for_each_sg(sgl, sgent, sglen, i) {
> + dma_addr_t sg_addr = sg_dma_address(sgent);
> +
> + num_tr = udma_get_tr_counters(sg_dma_len(sgent), __ffs(sg_addr),
> + &tr0_cnt0, &tr0_cnt1, &tr1_cnt0);
> + if (num_tr < 0) {
> + dev_err(uc->ud->dev, "size %u is not supported\n",
> + sg_dma_len(sgent));
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> + 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;
> + tr_req[tr_idx].dim1 = tr0_cnt0;
> + tr_idx++;
> +
> + if (num_tr == 2) {
> + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
> + false, false,
> + CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> + cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> + CPPI5_TR_CSF_SUPR_EVT);
> +
> + tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
> + tr_req[tr_idx].icnt0 = tr1_cnt0;
> + tr_req[tr_idx].icnt1 = 1;
> + tr_req[tr_idx].dim1 = tr1_cnt0;
> + tr_idx++;
> + }
> +
> + d->residue += sg_dma_len(sgent);
> + }
> +
> + cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
> + CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
> +
> + return d;
> +}
> +
> +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;
> + u32 csf = CPPI5_TR_CSF_SUPR_EVT;
> + 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;
> + csf |= CPPI5_TR_CSF_EOL_ICNT0;
> + } 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, csf);
> + 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, csf);
> + 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, csf | CPPI5_TR_CSF_EOP);
> +
> + return d;
> +}
> +
> +int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
> + enum dma_slave_buswidth dev_width,
> + u16 elcnt)
> +{
> + if (uc->config.ep_type != PSIL_EP_PDMA_XY)
> + return 0;
> +
> + /* Bus width translates to the element size (ES) */
> + switch (dev_width) {
> + case DMA_SLAVE_BUSWIDTH_1_BYTE:
> + d->static_tr.elsize = 0;
> + break;
> + case DMA_SLAVE_BUSWIDTH_2_BYTES:
> + d->static_tr.elsize = 1;
> + break;
> + case DMA_SLAVE_BUSWIDTH_3_BYTES:
> + d->static_tr.elsize = 2;
> + break;
> + case DMA_SLAVE_BUSWIDTH_4_BYTES:
> + d->static_tr.elsize = 3;
> + break;
> + case DMA_SLAVE_BUSWIDTH_8_BYTES:
> + d->static_tr.elsize = 4;
> + break;
> + default: /* not reached */
> + return -EINVAL;
> + }
> +
> + d->static_tr.elcnt = elcnt;
> +
> + if (uc->config.pkt_mode || !uc->cyclic) {
> + /*
> + * PDMA must close the packet when the channel is in packet mode.
> + * For TR mode when the channel is not cyclic we also need PDMA
> + * to close the packet otherwise the transfer will stall because
> + * PDMA holds on the data it has received from the peripheral.
> + */
> + unsigned int div = dev_width * elcnt;
> +
> + if (uc->cyclic)
> + d->static_tr.bstcnt = d->residue / d->sglen / div;
> + else
> + d->static_tr.bstcnt = d->residue / div;
> + } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
> + uc->config.dir == DMA_DEV_TO_MEM &&
> + uc->cyclic) {
> + /*
> + * For cyclic mode with BCDMA we have to set EOP in each TR to
> + * prevent short packet errors seen on channel teardown. So the
> + * PDMA must close the packet after every TR transfer by setting
> + * burst count equal to the number of bytes transferred.
> + */
> + struct cppi5_tr_type1_t *tr_req = d->hwdesc[0].tr_req_base;
> +
> + d->static_tr.bstcnt =
> + (tr_req->icnt0 * tr_req->icnt1) / dev_width;
> + } else {
> + d->static_tr.bstcnt = 0;
> + }
> +
> + if (uc->config.dir == DMA_DEV_TO_MEM &&
> + d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask)
> + return -EINVAL;
> +
> + return 0;
> +}
> +
> +struct udma_desc *
> +udma_prep_slave_sg_pkt(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_host_desc_t *h_desc = NULL;
> + struct udma_desc *d;
> + u32 ring_id;
> + unsigned int i;
> + u64 asel;
> +
> + d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
> + if (!d)
> + return NULL;
> +
> + d->sglen = sglen;
> + d->hwdesc_count = sglen;
> +
> + if (dir == DMA_DEV_TO_MEM)
> + ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> + 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);
> + struct cppi5_host_desc_t *desc;
> + size_t sg_len = sg_dma_len(sgent);
> +
> + hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> + GFP_NOWAIT,
> + &hwdesc->cppi5_desc_paddr);
> + if (!hwdesc->cppi5_desc_vaddr) {
> + dev_err(uc->ud->dev,
> + "descriptor%d allocation failed\n", i);
> +
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + d->residue += sg_len;
> + hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> + desc = hwdesc->cppi5_desc_vaddr;
> +
> + if (i == 0) {
> + cppi5_hdesc_init(desc, 0, 0);
> + /* Flow and Packed ID */
> + cppi5_desc_set_pktids(&desc->hdr, uc->id,
> + CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> + cppi5_desc_set_retpolicy(&desc->hdr, 0, ring_id);
> + } else {
> + cppi5_hdesc_reset_hbdesc(desc);
> + cppi5_desc_set_retpolicy(&desc->hdr, 0, 0xffff);
> + }
> +
> + /* 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 | asel);
> +
> + if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
> + dir == DMA_MEM_TO_DEV)
> + h_desc = desc;
> + }
> +
> + if (d->residue >= SZ_4M) {
> + dev_err(uc->ud->dev,
> + "%s: Transfer size %u is over the supported 4M range\n",
> + __func__, d->residue);
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> + cppi5_hdesc_set_pktlen(h_desc, d->residue);
> +
> + return d;
> +}
> +
> +int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
> + void *data, size_t len)
> +{
> + struct udma_desc *d = to_udma_desc(desc);
> + struct udma_chan *uc = to_udma_chan(desc->chan);
> + struct cppi5_host_desc_t *h_desc;
> + u32 psd_size = len;
> + u32 flags = 0;
> +
> + if (!uc->config.pkt_mode || !uc->config.metadata_size)
> + return -EOPNOTSUPP;
> +
> + if (!data || len > uc->config.metadata_size)
> + return -EINVAL;
> +
> + if (uc->config.needs_epib && len < CPPI5_INFO0_HDESC_EPIB_SIZE)
> + return -EINVAL;
> +
> + h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> + if (d->dir == DMA_MEM_TO_DEV)
> + memcpy(h_desc->epib, data, len);
> +
> + if (uc->config.needs_epib)
> + psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
> +
> + d->metadata = data;
> + d->metadata_size = len;
> + if (uc->config.needs_epib)
> + flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
> +
> + cppi5_hdesc_update_flags(h_desc, flags);
> + cppi5_hdesc_update_psdata_size(h_desc, psd_size);
> +
> + return 0;
> +}
> +
> +void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
> + size_t *payload_len, size_t *max_len)
> +{
> + struct udma_desc *d = to_udma_desc(desc);
> + struct udma_chan *uc = to_udma_chan(desc->chan);
> + struct cppi5_host_desc_t *h_desc;
> +
> + if (!uc->config.pkt_mode || !uc->config.metadata_size)
> + return ERR_PTR(-EOPNOTSUPP);
> +
> + h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> +
> + *max_len = uc->config.metadata_size;
> +
> + *payload_len = cppi5_hdesc_epib_present(&h_desc->hdr) ?
> + CPPI5_INFO0_HDESC_EPIB_SIZE : 0;
> + *payload_len += cppi5_hdesc_get_psdata_size(h_desc);
> +
> + return h_desc->epib;
> +}
> +
> +int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
> + size_t payload_len)
> +{
> + struct udma_desc *d = to_udma_desc(desc);
> + struct udma_chan *uc = to_udma_chan(desc->chan);
> + struct cppi5_host_desc_t *h_desc;
> + u32 psd_size = payload_len;
> + u32 flags = 0;
> +
> + if (!uc->config.pkt_mode || !uc->config.metadata_size)
> + return -EOPNOTSUPP;
> +
> + if (payload_len > uc->config.metadata_size)
> + return -EINVAL;
> +
> + if (uc->config.needs_epib && payload_len < CPPI5_INFO0_HDESC_EPIB_SIZE)
> + return -EINVAL;
> +
> + h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> +
> + if (uc->config.needs_epib) {
> + psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
> + flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
> + }
> +
> + cppi5_hdesc_update_flags(h_desc, flags);
> + cppi5_hdesc_update_psdata_size(h_desc, psd_size);
> +
> + return 0;
> +}
> +
> +struct dma_async_tx_descriptor *
> +udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
> + unsigned int sglen, enum dma_transfer_direction dir,
> + unsigned long tx_flags, void *context)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + enum dma_slave_buswidth dev_width;
> + struct udma_desc *d;
> + u32 burst;
> +
> + 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,
> + dmaengine_get_direction_text(uc->config.dir),
> + dmaengine_get_direction_text(dir));
> + return NULL;
> + }
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_width = uc->cfg.src_addr_width;
> + burst = uc->cfg.src_maxburst;
> + } else if (dir == DMA_MEM_TO_DEV) {
> + dev_width = uc->cfg.dst_addr_width;
> + burst = uc->cfg.dst_maxburst;
> + } else {
> + dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
> + return NULL;
> + }
> +
> + if (!burst)
> + burst = 1;
> +
> + uc->config.tx_flags = tx_flags;
> +
> + if (uc->config.pkt_mode)
> + d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
> + context);
> + 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;
> +
> + d->dir = dir;
> + d->desc_idx = 0;
> + d->tr_idx = 0;
> +
> + /* static TR for remote PDMA */
> + if (udma_configure_statictr(uc, d, dev_width, burst)) {
> + dev_err(uc->ud->dev,
> + "%s: StaticTR Z is limited to maximum %u (%u)\n",
> + __func__, uc->ud->match_data->statictr_z_mask,
> + d->static_tr.bstcnt);
> +
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + if (uc->config.metadata_size)
> + d->vd.tx.metadata_ops = &metadata_ops;
> +
> + return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
> +}
> +EXPORT_SYMBOL_GPL(udma_prep_slave_sg);
> +
> +struct udma_desc *
> +udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
> + size_t buf_len, size_t period_len,
> + enum dma_transfer_direction dir, unsigned long flags)
> +{
> + struct udma_desc *d;
> + size_t tr_size, period_addr;
> + struct cppi5_tr_type1_t *tr_req;
> + unsigned int periods = buf_len / period_len;
> + u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
> + unsigned int i;
> + int num_tr;
> + u32 period_csf = 0;
> +
> + num_tr = udma_get_tr_counters(period_len, __ffs(buf_addr), &tr0_cnt0,
> + &tr0_cnt1, &tr1_cnt0);
> + if (num_tr < 0) {
> + dev_err(uc->ud->dev, "size %zu is not supported\n",
> + period_len);
> + return NULL;
> + }
> +
> + /* Now allocate and setup the descriptor. */
> + tr_size = sizeof(struct cppi5_tr_type1_t);
> + d = udma_alloc_tr_desc(uc, tr_size, periods * num_tr, dir);
> + if (!d)
> + return NULL;
> +
> + tr_req = d->hwdesc[0].tr_req_base;
> + 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 BCDMA <-> PDMA transfers, the EOP flag needs to be set on the
> + * last TR of a descriptor, to mark the packet as complete.
> + * This is required for getting the teardown completion message in case
> + * of TX, and to avoid short-packet error in case of RX.
> + *
> + * As we are in cyclic mode, we do not know which period might be the
> + * last one, so set the flag for each period.
> + */
> + if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
> + uc->ud->match_data->type == DMA_TYPE_BCDMA) {
> + period_csf = CPPI5_TR_CSF_EOP;
> + }
> +
> + for (i = 0; i < periods; i++) {
> + int tr_idx = i * num_tr;
> +
> + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> + false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> +
> + tr_req[tr_idx].addr = period_addr;
> + tr_req[tr_idx].icnt0 = tr0_cnt0;
> + tr_req[tr_idx].icnt1 = tr0_cnt1;
> + tr_req[tr_idx].dim1 = tr0_cnt0;
> +
> + if (num_tr == 2) {
> + cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> + CPPI5_TR_CSF_SUPR_EVT);
> + tr_idx++;
> +
> + cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
> + false, false,
> + CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> +
> + tr_req[tr_idx].addr = period_addr + tr0_cnt1 * tr0_cnt0;
> + tr_req[tr_idx].icnt0 = tr1_cnt0;
> + tr_req[tr_idx].icnt1 = 1;
> + tr_req[tr_idx].dim1 = tr1_cnt0;
> + }
> +
> + if (!(flags & DMA_PREP_INTERRUPT))
> + period_csf |= CPPI5_TR_CSF_SUPR_EVT;
> +
> + if (period_csf)
> + cppi5_tr_csf_set(&tr_req[tr_idx].flags, period_csf);
> +
> + period_addr += period_len;
> + }
> +
> + return d;
> +}
> +
> +struct udma_desc *
> +udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
> + size_t buf_len, size_t period_len,
> + enum dma_transfer_direction dir, unsigned long flags)
> +{
> + struct udma_desc *d;
> + u32 ring_id;
> + int i;
> + int periods = buf_len / period_len;
> +
> + if (periods > (K3_UDMA_DEFAULT_RING_SIZE - 1))
> + return NULL;
> +
> + if (period_len >= SZ_4M)
> + return NULL;
> +
> + d = kzalloc(struct_size(d, hwdesc, periods), GFP_NOWAIT);
> + if (!d)
> + return NULL;
> +
> + d->hwdesc_count = periods;
> +
> + /* TODO: re-check this... */
> + if (dir == DMA_DEV_TO_MEM)
> + ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> + 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);
> + struct cppi5_host_desc_t *h_desc;
> +
> + hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> + GFP_NOWAIT,
> + &hwdesc->cppi5_desc_paddr);
> + if (!hwdesc->cppi5_desc_vaddr) {
> + dev_err(uc->ud->dev,
> + "descriptor%d allocation failed\n", i);
> +
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> + h_desc = hwdesc->cppi5_desc_vaddr;
> +
> + cppi5_hdesc_init(h_desc, 0, 0);
> + cppi5_hdesc_set_pktlen(h_desc, period_len);
> +
> + /* Flow and Packed ID */
> + cppi5_desc_set_pktids(&h_desc->hdr, uc->id,
> + CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> + cppi5_desc_set_retpolicy(&h_desc->hdr, 0, ring_id);
> +
> + /* attach each period to a new descriptor */
> + cppi5_hdesc_attach_buf(h_desc,
> + period_addr, period_len,
> + period_addr, period_len);
> + }
> +
> + return d;
> +}
> +
> +struct dma_async_tx_descriptor *
> +udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
> + size_t period_len, enum dma_transfer_direction dir,
> + unsigned long flags)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + enum dma_slave_buswidth dev_width;
> + struct udma_desc *d;
> + u32 burst;
> +
> + if (dir != uc->config.dir) {
> + dev_err(chan->device->dev,
> + "%s: chan%d is for %s, not supporting %s\n",
> + __func__, uc->id,
> + dmaengine_get_direction_text(uc->config.dir),
> + dmaengine_get_direction_text(dir));
> + return NULL;
> + }
> +
> + uc->cyclic = true;
> +
> + if (dir == DMA_DEV_TO_MEM) {
> + dev_width = uc->cfg.src_addr_width;
> + burst = uc->cfg.src_maxburst;
> + } else if (dir == DMA_MEM_TO_DEV) {
> + dev_width = uc->cfg.dst_addr_width;
> + burst = uc->cfg.dst_maxburst;
> + } else {
> + dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
> + return NULL;
> + }
> +
> + if (!burst)
> + burst = 1;
> +
> + if (uc->config.pkt_mode)
> + d = udma_prep_dma_cyclic_pkt(uc, buf_addr, buf_len, period_len,
> + dir, flags);
> + else
> + d = udma_prep_dma_cyclic_tr(uc, buf_addr, buf_len, period_len,
> + dir, flags);
> +
> + if (!d)
> + return NULL;
> +
> + d->sglen = buf_len / period_len;
> +
> + d->dir = dir;
> + d->residue = buf_len;
> +
> + /* static TR for remote PDMA */
> + if (udma_configure_statictr(uc, d, dev_width, burst)) {
> + dev_err(uc->ud->dev,
> + "%s: StaticTR Z is limited to maximum %u (%u)\n",
> + __func__, uc->ud->match_data->statictr_z_mask,
> + d->static_tr.bstcnt);
> +
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return NULL;
> + }
> +
> + if (uc->config.metadata_size)
> + d->vd.tx.metadata_ops = &metadata_ops;
> +
> + return vchan_tx_prep(&uc->vc, &d->vd, flags);
> +}
> +EXPORT_SYMBOL_GPL(udma_prep_dma_cyclic);
> +
> +struct dma_async_tx_descriptor *
> +udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
> + size_t len, unsigned long tx_flags)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + struct udma_desc *d;
> + 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;
> + u32 csf = CPPI5_TR_CSF_SUPR_EVT;
> +
> + if (uc->config.dir != DMA_MEM_TO_MEM) {
> + dev_err(chan->device->dev,
> + "%s: chan%d is for %s, not supporting %s\n",
> + __func__, uc->id,
> + dmaengine_get_direction_text(uc->config.dir),
> + dmaengine_get_direction_text(DMA_MEM_TO_MEM));
> + return NULL;
> + }
> +
> + num_tr = udma_get_tr_counters(len, __ffs(src | dest), &tr0_cnt0,
> + &tr0_cnt1, &tr1_cnt0);
> + if (num_tr < 0) {
> + dev_err(uc->ud->dev, "size %zu is not supported\n",
> + len);
> + return NULL;
> + }
> +
> + d = udma_alloc_tr_desc(uc, tr_size, num_tr, DMA_MEM_TO_MEM);
> + if (!d)
> + return NULL;
> +
> + d->dir = DMA_MEM_TO_MEM;
> + d->desc_idx = 0;
> + 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;
> + } else {
> + csf |= CPPI5_TR_CSF_EOL_ICNT0;
> + }
> +
> + tr_req = d->hwdesc[0].tr_req_base;
> +
> + cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
> + CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> + cppi5_tr_csf_set(&tr_req[0].flags, csf);
> +
> + 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, csf);
> +
> + 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, csf | CPPI5_TR_CSF_EOP);
> +
> + if (uc->config.metadata_size)
> + d->vd.tx.metadata_ops = &metadata_ops;
> +
> + return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
> +}
> +EXPORT_SYMBOL_GPL(udma_prep_dma_memcpy);
> +
> +void udma_desc_pre_callback(struct virt_dma_chan *vc,
> + struct virt_dma_desc *vd,
> + struct dmaengine_result *result)
> +{
> + struct udma_chan *uc = to_udma_chan(&vc->chan);
> + struct udma_desc *d;
> + u8 status;
> +
> + if (!vd)
> + return;
> +
> + d = to_udma_desc(&vd->tx);
> +
> + if (d->metadata_size)
> + udma_fetch_epib(uc, d);
> +
> + if (result) {
> + void *desc_vaddr = udma_curr_cppi5_desc_vaddr(d, d->desc_idx);
> +
> + if (cppi5_desc_get_type(desc_vaddr) ==
> + CPPI5_INFO0_DESC_TYPE_VAL_HOST) {
> + /* Provide residue information for the client */
> + result->residue = d->residue -
> + cppi5_hdesc_get_pktlen(desc_vaddr);
> + if (result->residue)
> + result->result = DMA_TRANS_ABORTED;
> + else
> + result->result = DMA_TRANS_NOERROR;
> + } else {
> + result->residue = 0;
> + /* Propagate TR Response errors to the client */
> + status = d->hwdesc[0].tr_resp_base->status;
> + if (status)
> + result->result = DMA_TRANS_ABORTED;
> + else
> + result->result = DMA_TRANS_NOERROR;
> + }
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_desc_pre_callback);
> +
> +MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
> +MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 4adcd679c6997..0a1291829611f 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -131,105 +131,6 @@ static void udma_dump_chan_stdata(struct udma_chan *uc)
> }
> }
>
> -static struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> - dma_addr_t paddr)
> -{
> - struct udma_desc *d = uc->terminated_desc;
> -
> - if (d) {
> - dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
> - d->desc_idx);
> -
> - if (desc_paddr != paddr)
> - d = NULL;
> - }
> -
> - if (!d) {
> - d = uc->desc;
> - if (d) {
> - dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
> - d->desc_idx);
> -
> - if (desc_paddr != paddr)
> - d = NULL;
> - }
> - }
> -
> - return d;
> -}
> -
> -static void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
> -{
> - if (uc->use_dma_pool) {
> - int i;
> -
> - for (i = 0; i < d->hwdesc_count; i++) {
> - if (!d->hwdesc[i].cppi5_desc_vaddr)
> - continue;
> -
> - dma_pool_free(uc->hdesc_pool,
> - d->hwdesc[i].cppi5_desc_vaddr,
> - d->hwdesc[i].cppi5_desc_paddr);
> -
> - d->hwdesc[i].cppi5_desc_vaddr = NULL;
> - }
> - } else if (d->hwdesc[0].cppi5_desc_vaddr) {
> - dma_free_coherent(uc->dma_dev, d->hwdesc[0].cppi5_desc_size,
> - d->hwdesc[0].cppi5_desc_vaddr,
> - d->hwdesc[0].cppi5_desc_paddr);
> -
> - d->hwdesc[0].cppi5_desc_vaddr = NULL;
> - }
> -}
> -
> -static void udma_purge_desc_work(struct work_struct *work)
> -{
> - struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
> - struct virt_dma_desc *vd, *_vd;
> - unsigned long flags;
> - LIST_HEAD(head);
> -
> - spin_lock_irqsave(&ud->lock, flags);
> - list_splice_tail_init(&ud->desc_to_purge, &head);
> - spin_unlock_irqrestore(&ud->lock, flags);
> -
> - list_for_each_entry_safe(vd, _vd, &head, node) {
> - struct udma_chan *uc = to_udma_chan(vd->tx.chan);
> - struct udma_desc *d = to_udma_desc(&vd->tx);
> -
> - udma_free_hwdesc(uc, d);
> - list_del(&vd->node);
> - kfree(d);
> - }
> -
> - /* If more to purge, schedule the work again */
> - if (!list_empty(&ud->desc_to_purge))
> - schedule_work(&ud->purge_work);
> -}
> -
> -static void udma_desc_free(struct virt_dma_desc *vd)
> -{
> - struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
> - struct udma_chan *uc = to_udma_chan(vd->tx.chan);
> - struct udma_desc *d = to_udma_desc(&vd->tx);
> - unsigned long flags;
> -
> - if (uc->terminated_desc == d)
> - uc->terminated_desc = NULL;
> -
> - if (uc->use_dma_pool) {
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return;
> - }
> -
> - spin_lock_irqsave(&ud->lock, flags);
> - list_add_tail(&vd->node, &ud->desc_to_purge);
> - spin_unlock_irqrestore(&ud->lock, flags);
> -
> - schedule_work(&ud->purge_work);
> -}
> -
> static bool udma_is_chan_running(struct udma_chan *uc)
> {
> u32 trt_ctl = 0;
> @@ -303,17 +204,6 @@ static int udma_push_to_ring(struct udma_chan *uc, int idx)
> return k3_ringacc_ring_push(ring, &paddr);
> }
>
> -static bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr)
> -{
> - if (uc->config.dir != DMA_DEV_TO_MEM)
> - return false;
> -
> - if (addr == udma_get_rx_flush_hwdesc_paddr(uc))
> - return true;
> -
> - return false;
> -}
> -
> static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
> {
> struct k3_ring *ring = NULL;
> @@ -674,33 +564,6 @@ static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
> d->desc_idx = (d->desc_idx + 1) % d->sglen;
> }
>
> -static bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
> -{
> - u32 peer_bcnt, bcnt;
> -
> - /*
> - * Only TX towards PDMA is affected.
> - * If DMA_PREP_INTERRUPT is not set by consumer then skip the transfer
> - * completion calculation, consumer must ensure that there is no stale
> - * data in DMA fabric in this case.
> - */
> - if (uc->config.ep_type == PSIL_EP_NATIVE ||
> - uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
> - return true;
> -
> - peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> - bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> -
> - /* Transfer is incomplete, store current residue and time stamp */
> - if (peer_bcnt < bcnt) {
> - uc->tx_drain.residue = bcnt - peer_bcnt;
> - uc->tx_drain.tstamp = ktime_get();
> - return false;
> - }
> -
> - return true;
> -}
> -
> static void udma_check_tx_completion(struct work_struct *work)
> {
> struct udma_chan *uc = container_of(work, typeof(*uc),
> @@ -2344,1047 +2207,6 @@ static int udma_slave_config(struct dma_chan *chan,
> 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:
> - break;
> - default:
> - dev_err(uc->ud->dev, "Unsupported TR size of %zu\n", tr_size);
> - return NULL;
> - }
> -
> - /* We have only one descriptor containing multiple TRs */
> - d = kzalloc(sizeof(*d) + sizeof(d->hwdesc[0]), GFP_NOWAIT);
> - if (!d)
> - return NULL;
> -
> - d->sglen = tr_count;
> -
> - d->hwdesc_count = 1;
> - hwdesc = &d->hwdesc[0];
> -
> - /* Allocate memory for DMA ring descriptor */
> - if (uc->use_dma_pool) {
> - hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> - hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> - GFP_NOWAIT,
> - &hwdesc->cppi5_desc_paddr);
> - } else {
> - hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size,
> - tr_count);
> - hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
> - uc->ud->desc_align);
> - hwdesc->cppi5_desc_vaddr = dma_alloc_coherent(uc->ud->dev,
> - hwdesc->cppi5_desc_size,
> - &hwdesc->cppi5_desc_paddr,
> - GFP_NOWAIT);
> - }
> -
> - if (!hwdesc->cppi5_desc_vaddr) {
> - kfree(d);
> - return NULL;
> - }
> -
> - /* Start of the TR req records */
> - hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
> - /* Start address of the TR response array */
> - hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size * tr_count;
> -
> - tr_desc = hwdesc->cppi5_desc_vaddr;
> -
> - if (uc->cyclic)
> - reload_count = CPPI5_INFO0_TRDESC_RLDCNT_INFINITE;
> -
> - if (dir == DMA_DEV_TO_MEM)
> - ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> - else
> - ring_id = k3_ringacc_get_ring_id(uc->tchan->tc_ring);
> -
> - cppi5_trdesc_init(tr_desc, tr_count, tr_size, 0, reload_count);
> - cppi5_desc_set_pktids(tr_desc, uc->id,
> - CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> - cppi5_desc_set_retpolicy(tr_desc, 0, ring_id);
> -
> - return d;
> -}
> -
> -/**
> - * udma_get_tr_counters - calculate TR counters for a given length
> - * @len: Length of the trasnfer
> - * @align_to: Preferred alignment
> - * @tr0_cnt0: First TR icnt0
> - * @tr0_cnt1: First TR icnt1
> - * @tr1_cnt0: Second (if used) TR icnt0
> - *
> - * For len < SZ_64K only one TR is enough, tr1_cnt0 is not updated
> - * For len >= SZ_64K two TRs are used in a simple way:
> - * First TR: SZ_64K-alignment blocks (tr0_cnt0, tr0_cnt1)
> - * Second TR: the remaining length (tr1_cnt0)
> - *
> - * Returns the number of TRs the length needs (1 or 2)
> - * -EINVAL if the length can not be supported
> - */
> -static int udma_get_tr_counters(size_t len, unsigned long align_to,
> - u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0)
> -{
> - if (len < SZ_64K) {
> - *tr0_cnt0 = len;
> - *tr0_cnt1 = 1;
> -
> - return 1;
> - }
> -
> - if (align_to > 3)
> - align_to = 3;
> -
> -realign:
> - *tr0_cnt0 = SZ_64K - BIT(align_to);
> - if (len / *tr0_cnt0 >= SZ_64K) {
> - if (align_to) {
> - align_to--;
> - goto realign;
> - }
> - return -EINVAL;
> - }
> -
> - *tr0_cnt1 = len / *tr0_cnt0;
> - *tr1_cnt0 = len % *tr0_cnt0;
> -
> - return 2;
> -}
> -
> -static struct udma_desc *
> -udma_prep_slave_sg_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 udma_desc *d;
> - struct cppi5_tr_type1_t *tr_req = NULL;
> - u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
> - unsigned int i;
> - 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) {
> - if (sg_dma_len(sgent) < SZ_64K)
> - num_tr++;
> - else
> - num_tr += 2;
> - }
> -
> - /* Now allocate and setup the descriptor. */
> - tr_size = sizeof(struct cppi5_tr_type1_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;
> -
> - tr_req = d->hwdesc[0].tr_req_base;
> - for_each_sg(sgl, sgent, sglen, i) {
> - dma_addr_t sg_addr = sg_dma_address(sgent);
> -
> - num_tr = udma_get_tr_counters(sg_dma_len(sgent), __ffs(sg_addr),
> - &tr0_cnt0, &tr0_cnt1, &tr1_cnt0);
> - if (num_tr < 0) {
> - dev_err(uc->ud->dev, "size %u is not supported\n",
> - sg_dma_len(sgent));
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> - 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;
> - tr_req[tr_idx].dim1 = tr0_cnt0;
> - tr_idx++;
> -
> - if (num_tr == 2) {
> - cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
> - false, false,
> - CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> - cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> - CPPI5_TR_CSF_SUPR_EVT);
> -
> - tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
> - tr_req[tr_idx].icnt0 = tr1_cnt0;
> - tr_req[tr_idx].icnt1 = 1;
> - tr_req[tr_idx].dim1 = tr1_cnt0;
> - tr_idx++;
> - }
> -
> - d->residue += sg_dma_len(sgent);
> - }
> -
> - cppi5_tr_csf_set(&tr_req[tr_idx - 1].flags,
> - CPPI5_TR_CSF_SUPR_EVT | CPPI5_TR_CSF_EOP);
> -
> - 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;
> - u32 csf = CPPI5_TR_CSF_SUPR_EVT;
> - 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;
> - csf |= CPPI5_TR_CSF_EOL_ICNT0;
> - } 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, csf);
> - 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, csf);
> - 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, csf | 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)
> -{
> - if (uc->config.ep_type != PSIL_EP_PDMA_XY)
> - return 0;
> -
> - /* Bus width translates to the element size (ES) */
> - switch (dev_width) {
> - case DMA_SLAVE_BUSWIDTH_1_BYTE:
> - d->static_tr.elsize = 0;
> - break;
> - case DMA_SLAVE_BUSWIDTH_2_BYTES:
> - d->static_tr.elsize = 1;
> - break;
> - case DMA_SLAVE_BUSWIDTH_3_BYTES:
> - d->static_tr.elsize = 2;
> - break;
> - case DMA_SLAVE_BUSWIDTH_4_BYTES:
> - d->static_tr.elsize = 3;
> - break;
> - case DMA_SLAVE_BUSWIDTH_8_BYTES:
> - d->static_tr.elsize = 4;
> - break;
> - default: /* not reached */
> - return -EINVAL;
> - }
> -
> - d->static_tr.elcnt = elcnt;
> -
> - if (uc->config.pkt_mode || !uc->cyclic) {
> - /*
> - * PDMA must close the packet when the channel is in packet mode.
> - * For TR mode when the channel is not cyclic we also need PDMA
> - * to close the packet otherwise the transfer will stall because
> - * PDMA holds on the data it has received from the peripheral.
> - */
> - unsigned int div = dev_width * elcnt;
> -
> - if (uc->cyclic)
> - d->static_tr.bstcnt = d->residue / d->sglen / div;
> - else
> - d->static_tr.bstcnt = d->residue / div;
> - } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
> - uc->config.dir == DMA_DEV_TO_MEM &&
> - uc->cyclic) {
> - /*
> - * For cyclic mode with BCDMA we have to set EOP in each TR to
> - * prevent short packet errors seen on channel teardown. So the
> - * PDMA must close the packet after every TR transfer by setting
> - * burst count equal to the number of bytes transferred.
> - */
> - struct cppi5_tr_type1_t *tr_req = d->hwdesc[0].tr_req_base;
> -
> - d->static_tr.bstcnt =
> - (tr_req->icnt0 * tr_req->icnt1) / dev_width;
> - } else {
> - d->static_tr.bstcnt = 0;
> - }
> -
> - if (uc->config.dir == DMA_DEV_TO_MEM &&
> - d->static_tr.bstcnt > uc->ud->match_data->statictr_z_mask)
> - return -EINVAL;
> -
> - return 0;
> -}
> -
> -static struct udma_desc *
> -udma_prep_slave_sg_pkt(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_host_desc_t *h_desc = NULL;
> - struct udma_desc *d;
> - u32 ring_id;
> - unsigned int i;
> - u64 asel;
> -
> - d = kzalloc(struct_size(d, hwdesc, sglen), GFP_NOWAIT);
> - if (!d)
> - return NULL;
> -
> - d->sglen = sglen;
> - d->hwdesc_count = sglen;
> -
> - if (dir == DMA_DEV_TO_MEM)
> - ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> - 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);
> - struct cppi5_host_desc_t *desc;
> - size_t sg_len = sg_dma_len(sgent);
> -
> - hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> - GFP_NOWAIT,
> - &hwdesc->cppi5_desc_paddr);
> - if (!hwdesc->cppi5_desc_vaddr) {
> - dev_err(uc->ud->dev,
> - "descriptor%d allocation failed\n", i);
> -
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - d->residue += sg_len;
> - hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> - desc = hwdesc->cppi5_desc_vaddr;
> -
> - if (i == 0) {
> - cppi5_hdesc_init(desc, 0, 0);
> - /* Flow and Packed ID */
> - cppi5_desc_set_pktids(&desc->hdr, uc->id,
> - CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> - cppi5_desc_set_retpolicy(&desc->hdr, 0, ring_id);
> - } else {
> - cppi5_hdesc_reset_hbdesc(desc);
> - cppi5_desc_set_retpolicy(&desc->hdr, 0, 0xffff);
> - }
> -
> - /* 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 | asel);
> -
> - if (uc->ud->match_data->type == DMA_TYPE_PKTDMA ||
> - dir == DMA_MEM_TO_DEV)
> - h_desc = desc;
> - }
> -
> - if (d->residue >= SZ_4M) {
> - dev_err(uc->ud->dev,
> - "%s: Transfer size %u is over the supported 4M range\n",
> - __func__, d->residue);
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> - cppi5_hdesc_set_pktlen(h_desc, d->residue);
> -
> - return d;
> -}
> -
> -static int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
> - void *data, size_t len)
> -{
> - struct udma_desc *d = to_udma_desc(desc);
> - struct udma_chan *uc = to_udma_chan(desc->chan);
> - struct cppi5_host_desc_t *h_desc;
> - u32 psd_size = len;
> - u32 flags = 0;
> -
> - if (!uc->config.pkt_mode || !uc->config.metadata_size)
> - return -ENOTSUPP;
> -
> - if (!data || len > uc->config.metadata_size)
> - return -EINVAL;
> -
> - if (uc->config.needs_epib && len < CPPI5_INFO0_HDESC_EPIB_SIZE)
> - return -EINVAL;
> -
> - h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> - if (d->dir == DMA_MEM_TO_DEV)
> - memcpy(h_desc->epib, data, len);
> -
> - if (uc->config.needs_epib)
> - psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
> -
> - d->metadata = data;
> - d->metadata_size = len;
> - if (uc->config.needs_epib)
> - flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
> -
> - cppi5_hdesc_update_flags(h_desc, flags);
> - cppi5_hdesc_update_psdata_size(h_desc, psd_size);
> -
> - return 0;
> -}
> -
> -static void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
> - size_t *payload_len, size_t *max_len)
> -{
> - struct udma_desc *d = to_udma_desc(desc);
> - struct udma_chan *uc = to_udma_chan(desc->chan);
> - struct cppi5_host_desc_t *h_desc;
> -
> - if (!uc->config.pkt_mode || !uc->config.metadata_size)
> - return ERR_PTR(-ENOTSUPP);
> -
> - h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> -
> - *max_len = uc->config.metadata_size;
> -
> - *payload_len = cppi5_hdesc_epib_present(&h_desc->hdr) ?
> - CPPI5_INFO0_HDESC_EPIB_SIZE : 0;
> - *payload_len += cppi5_hdesc_get_psdata_size(h_desc);
> -
> - return h_desc->epib;
> -}
> -
> -static int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
> - size_t payload_len)
> -{
> - struct udma_desc *d = to_udma_desc(desc);
> - struct udma_chan *uc = to_udma_chan(desc->chan);
> - struct cppi5_host_desc_t *h_desc;
> - u32 psd_size = payload_len;
> - u32 flags = 0;
> -
> - if (!uc->config.pkt_mode || !uc->config.metadata_size)
> - return -ENOTSUPP;
> -
> - if (payload_len > uc->config.metadata_size)
> - return -EINVAL;
> -
> - if (uc->config.needs_epib && payload_len < CPPI5_INFO0_HDESC_EPIB_SIZE)
> - return -EINVAL;
> -
> - h_desc = d->hwdesc[0].cppi5_desc_vaddr;
> -
> - if (uc->config.needs_epib) {
> - psd_size -= CPPI5_INFO0_HDESC_EPIB_SIZE;
> - flags |= CPPI5_INFO0_HDESC_EPIB_PRESENT;
> - }
> -
> - cppi5_hdesc_update_flags(h_desc, flags);
> - cppi5_hdesc_update_psdata_size(h_desc, psd_size);
> -
> - return 0;
> -}
> -
> -static struct dma_descriptor_metadata_ops metadata_ops = {
> - .attach = udma_attach_metadata,
> - .get_ptr = udma_get_metadata_ptr,
> - .set_len = udma_set_metadata_len,
> -};
> -
> -static struct dma_async_tx_descriptor *
> -udma_prep_slave_sg(struct dma_chan *chan, struct scatterlist *sgl,
> - unsigned int sglen, enum dma_transfer_direction dir,
> - unsigned long tx_flags, void *context)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - enum dma_slave_buswidth dev_width;
> - struct udma_desc *d;
> - u32 burst;
> -
> - 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,
> - dmaengine_get_direction_text(uc->config.dir),
> - dmaengine_get_direction_text(dir));
> - return NULL;
> - }
> -
> - if (dir == DMA_DEV_TO_MEM) {
> - dev_width = uc->cfg.src_addr_width;
> - burst = uc->cfg.src_maxburst;
> - } else if (dir == DMA_MEM_TO_DEV) {
> - dev_width = uc->cfg.dst_addr_width;
> - burst = uc->cfg.dst_maxburst;
> - } else {
> - dev_err(chan->device->dev, "%s: bad direction?\n", __func__);
> - return NULL;
> - }
> -
> - if (!burst)
> - burst = 1;
> -
> - uc->config.tx_flags = tx_flags;
> -
> - if (uc->config.pkt_mode)
> - d = udma_prep_slave_sg_pkt(uc, sgl, sglen, dir, tx_flags,
> - context);
> - 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;
> -
> - d->dir = dir;
> - d->desc_idx = 0;
> - d->tr_idx = 0;
> -
> - /* static TR for remote PDMA */
> - if (udma_configure_statictr(uc, d, dev_width, burst)) {
> - dev_err(uc->ud->dev,
> - "%s: StaticTR Z is limited to maximum %u (%u)\n",
> - __func__, uc->ud->match_data->statictr_z_mask,
> - d->static_tr.bstcnt);
> -
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - if (uc->config.metadata_size)
> - d->vd.tx.metadata_ops = &metadata_ops;
> -
> - return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
> -}
> -
> -static struct udma_desc *
> -udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
> - size_t buf_len, size_t period_len,
> - enum dma_transfer_direction dir, unsigned long flags)
> -{
> - struct udma_desc *d;
> - size_t tr_size, period_addr;
> - struct cppi5_tr_type1_t *tr_req;
> - unsigned int periods = buf_len / period_len;
> - u16 tr0_cnt0, tr0_cnt1, tr1_cnt0;
> - unsigned int i;
> - int num_tr;
> - u32 period_csf = 0;
> -
> - num_tr = udma_get_tr_counters(period_len, __ffs(buf_addr), &tr0_cnt0,
> - &tr0_cnt1, &tr1_cnt0);
> - if (num_tr < 0) {
> - dev_err(uc->ud->dev, "size %zu is not supported\n",
> - period_len);
> - return NULL;
> - }
> -
> - /* Now allocate and setup the descriptor. */
> - tr_size = sizeof(struct cppi5_tr_type1_t);
> - d = udma_alloc_tr_desc(uc, tr_size, periods * num_tr, dir);
> - if (!d)
> - return NULL;
> -
> - tr_req = d->hwdesc[0].tr_req_base;
> - 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 BCDMA <-> PDMA transfers, the EOP flag needs to be set on the
> - * last TR of a descriptor, to mark the packet as complete.
> - * This is required for getting the teardown completion message in case
> - * of TX, and to avoid short-packet error in case of RX.
> - *
> - * As we are in cyclic mode, we do not know which period might be the
> - * last one, so set the flag for each period.
> - */
> - if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
> - uc->ud->match_data->type == DMA_TYPE_BCDMA) {
> - period_csf = CPPI5_TR_CSF_EOP;
> - }
> -
> - for (i = 0; i < periods; i++) {
> - int tr_idx = i * num_tr;
> -
> - cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> - false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> -
> - tr_req[tr_idx].addr = period_addr;
> - tr_req[tr_idx].icnt0 = tr0_cnt0;
> - tr_req[tr_idx].icnt1 = tr0_cnt1;
> - tr_req[tr_idx].dim1 = tr0_cnt0;
> -
> - if (num_tr == 2) {
> - cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> - CPPI5_TR_CSF_SUPR_EVT);
> - tr_idx++;
> -
> - cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1,
> - false, false,
> - CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> -
> - tr_req[tr_idx].addr = period_addr + tr0_cnt1 * tr0_cnt0;
> - tr_req[tr_idx].icnt0 = tr1_cnt0;
> - tr_req[tr_idx].icnt1 = 1;
> - tr_req[tr_idx].dim1 = tr1_cnt0;
> - }
> -
> - if (!(flags & DMA_PREP_INTERRUPT))
> - period_csf |= CPPI5_TR_CSF_SUPR_EVT;
> -
> - if (period_csf)
> - cppi5_tr_csf_set(&tr_req[tr_idx].flags, period_csf);
> -
> - period_addr += period_len;
> - }
> -
> - return d;
> -}
> -
> -static struct udma_desc *
> -udma_prep_dma_cyclic_pkt(struct udma_chan *uc, dma_addr_t buf_addr,
> - size_t buf_len, size_t period_len,
> - enum dma_transfer_direction dir, unsigned long flags)
> -{
> - struct udma_desc *d;
> - u32 ring_id;
> - int i;
> - int periods = buf_len / period_len;
> -
> - if (periods > (K3_UDMA_DEFAULT_RING_SIZE - 1))
> - return NULL;
> -
> - if (period_len >= SZ_4M)
> - return NULL;
> -
> - d = kzalloc(struct_size(d, hwdesc, periods), GFP_NOWAIT);
> - if (!d)
> - return NULL;
> -
> - d->hwdesc_count = periods;
> -
> - /* TODO: re-check this... */
> - if (dir == DMA_DEV_TO_MEM)
> - ring_id = k3_ringacc_get_ring_id(uc->rflow->r_ring);
> - 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);
> - struct cppi5_host_desc_t *h_desc;
> -
> - hwdesc->cppi5_desc_vaddr = dma_pool_zalloc(uc->hdesc_pool,
> - GFP_NOWAIT,
> - &hwdesc->cppi5_desc_paddr);
> - if (!hwdesc->cppi5_desc_vaddr) {
> - dev_err(uc->ud->dev,
> - "descriptor%d allocation failed\n", i);
> -
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - hwdesc->cppi5_desc_size = uc->config.hdesc_size;
> - h_desc = hwdesc->cppi5_desc_vaddr;
> -
> - cppi5_hdesc_init(h_desc, 0, 0);
> - cppi5_hdesc_set_pktlen(h_desc, period_len);
> -
> - /* Flow and Packed ID */
> - cppi5_desc_set_pktids(&h_desc->hdr, uc->id,
> - CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> - cppi5_desc_set_retpolicy(&h_desc->hdr, 0, ring_id);
> -
> - /* attach each period to a new descriptor */
> - cppi5_hdesc_attach_buf(h_desc,
> - period_addr, period_len,
> - period_addr, period_len);
> - }
> -
> - return d;
> -}
> -
> -static struct dma_async_tx_descriptor *
> -udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr, size_t buf_len,
> - size_t period_len, enum dma_transfer_direction dir,
> - unsigned long flags)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - enum dma_slave_buswidth dev_width;
> - struct udma_desc *d;
> - u32 burst;
> -
> - if (dir != uc->config.dir) {
> - dev_err(chan->device->dev,
> - "%s: chan%d is for %s, not supporting %s\n",
> - __func__, uc->id,
> - dmaengine_get_direction_text(uc->config.dir),
> - dmaengine_get_direction_text(dir));
> - return NULL;
> - }
> -
> - uc->cyclic = true;
> -
> - if (dir == DMA_DEV_TO_MEM) {
> - dev_width = uc->cfg.src_addr_width;
> - burst = uc->cfg.src_maxburst;
> - } else if (dir == DMA_MEM_TO_DEV) {
> - dev_width = uc->cfg.dst_addr_width;
> - burst = uc->cfg.dst_maxburst;
> - } else {
> - dev_err(uc->ud->dev, "%s: bad direction?\n", __func__);
> - return NULL;
> - }
> -
> - if (!burst)
> - burst = 1;
> -
> - if (uc->config.pkt_mode)
> - d = udma_prep_dma_cyclic_pkt(uc, buf_addr, buf_len, period_len,
> - dir, flags);
> - else
> - d = udma_prep_dma_cyclic_tr(uc, buf_addr, buf_len, period_len,
> - dir, flags);
> -
> - if (!d)
> - return NULL;
> -
> - d->sglen = buf_len / period_len;
> -
> - d->dir = dir;
> - d->residue = buf_len;
> -
> - /* static TR for remote PDMA */
> - if (udma_configure_statictr(uc, d, dev_width, burst)) {
> - dev_err(uc->ud->dev,
> - "%s: StaticTR Z is limited to maximum %u (%u)\n",
> - __func__, uc->ud->match_data->statictr_z_mask,
> - d->static_tr.bstcnt);
> -
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return NULL;
> - }
> -
> - if (uc->config.metadata_size)
> - d->vd.tx.metadata_ops = &metadata_ops;
> -
> - return vchan_tx_prep(&uc->vc, &d->vd, flags);
> -}
> -
> -static struct dma_async_tx_descriptor *
> -udma_prep_dma_memcpy(struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
> - size_t len, unsigned long tx_flags)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - struct udma_desc *d;
> - 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;
> - u32 csf = CPPI5_TR_CSF_SUPR_EVT;
> -
> - if (uc->config.dir != DMA_MEM_TO_MEM) {
> - dev_err(chan->device->dev,
> - "%s: chan%d is for %s, not supporting %s\n",
> - __func__, uc->id,
> - dmaengine_get_direction_text(uc->config.dir),
> - dmaengine_get_direction_text(DMA_MEM_TO_MEM));
> - return NULL;
> - }
> -
> - num_tr = udma_get_tr_counters(len, __ffs(src | dest), &tr0_cnt0,
> - &tr0_cnt1, &tr1_cnt0);
> - if (num_tr < 0) {
> - dev_err(uc->ud->dev, "size %zu is not supported\n",
> - len);
> - return NULL;
> - }
> -
> - d = udma_alloc_tr_desc(uc, tr_size, num_tr, DMA_MEM_TO_MEM);
> - if (!d)
> - return NULL;
> -
> - d->dir = DMA_MEM_TO_MEM;
> - d->desc_idx = 0;
> - 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;
> - } else {
> - csf |= CPPI5_TR_CSF_EOL_ICNT0;
> - }
> -
> - tr_req = d->hwdesc[0].tr_req_base;
> -
> - cppi5_tr_init(&tr_req[0].flags, CPPI5_TR_TYPE15, false, true,
> - CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> - cppi5_tr_csf_set(&tr_req[0].flags, csf);
> -
> - 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, csf);
> -
> - 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, csf | CPPI5_TR_CSF_EOP);
> -
> - if (uc->config.metadata_size)
> - d->vd.tx.metadata_ops = &metadata_ops;
> -
> - return vchan_tx_prep(&uc->vc, &d->vd, tx_flags);
> -}
> -
> static void udma_issue_pending(struct dma_chan *chan)
> {
> struct udma_chan *uc = to_udma_chan(chan);
> @@ -3587,46 +2409,6 @@ static void udma_synchronize(struct dma_chan *chan)
> udma_reset_rings(uc);
> }
>
> -static void udma_desc_pre_callback(struct virt_dma_chan *vc,
> - struct virt_dma_desc *vd,
> - struct dmaengine_result *result)
> -{
> - struct udma_chan *uc = to_udma_chan(&vc->chan);
> - struct udma_desc *d;
> - u8 status;
> -
> - if (!vd)
> - return;
> -
> - d = to_udma_desc(&vd->tx);
> -
> - if (d->metadata_size)
> - udma_fetch_epib(uc, d);
> -
> - if (result) {
> - void *desc_vaddr = udma_curr_cppi5_desc_vaddr(d, d->desc_idx);
> -
> - if (cppi5_desc_get_type(desc_vaddr) ==
> - CPPI5_INFO0_DESC_TYPE_VAL_HOST) {
> - /* Provide residue information for the client */
> - result->residue = d->residue -
> - cppi5_hdesc_get_pktlen(desc_vaddr);
> - if (result->residue)
> - result->result = DMA_TRANS_ABORTED;
> - else
> - result->result = DMA_TRANS_NOERROR;
> - } else {
> - result->residue = 0;
> - /* Propagate TR Response errors to the client */
> - status = d->hwdesc[0].tr_resp_base->status;
> - if (status)
> - result->result = DMA_TRANS_ABORTED;
> - else
> - result->result = DMA_TRANS_NOERROR;
> - }
> - }
> -}
> -
> /*
> * This tasklet handles the completion of a DMA descriptor by
> * calling its callback and freeing it.
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 3a786b3eddc67..7c807bd9e178b 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -556,6 +556,60 @@ static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
> memcpy(d->metadata, h_desc->epib, d->metadata_size);
> }
>
> +/* Common functions */
> +struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> + dma_addr_t paddr);
> +void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d);
> +void udma_purge_desc_work(struct work_struct *work);
> +void udma_desc_free(struct virt_dma_desc *vd);
> +bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr);
> +bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d);
> +struct udma_desc *udma_alloc_tr_desc(struct udma_chan *uc,
> + size_t tr_size, int tr_count,
> + enum dma_transfer_direction dir);
> +int udma_get_tr_counters(size_t len, unsigned long align_to,
> + u16 *tr0_cnt0, u16 *tr0_cnt1, u16 *tr1_cnt0);
> +struct udma_desc *udma_prep_slave_sg_tr(struct udma_chan *uc,
> + struct scatterlist *sgl, unsigned int sglen,
> + enum dma_transfer_direction dir,
> + unsigned long tx_flags, void *context);
> +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);
> +int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
> + enum dma_slave_buswidth dev_width, u16 elcnt);
> +struct udma_desc *udma_prep_slave_sg_pkt(struct udma_chan *uc,
> + struct scatterlist *sgl, unsigned int sglen,
> + enum dma_transfer_direction dir,
> + unsigned long tx_flags, void *context);
> +int udma_attach_metadata(struct dma_async_tx_descriptor *desc,
> + void *data, size_t len);
> +void *udma_get_metadata_ptr(struct dma_async_tx_descriptor *desc,
> + size_t *payload_len, size_t *max_len);
> +int udma_set_metadata_len(struct dma_async_tx_descriptor *desc,
> + size_t payload_len);
> +struct dma_async_tx_descriptor *udma_prep_slave_sg(struct dma_chan *chan,
> + struct scatterlist *sgl, unsigned int sglen,
> + enum dma_transfer_direction dir,
> + unsigned long tx_flags, void *context);
> +struct udma_desc *udma_prep_dma_cyclic_tr(struct udma_chan *uc,
> + dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> + enum dma_transfer_direction dir, unsigned long flags);
> +struct udma_desc *udma_prep_dma_cyclic_pkt(struct udma_chan *uc,
> + dma_addr_t buf_addr, size_t buf_len, size_t period_len,
> + enum dma_transfer_direction dir, unsigned long flags);
> +struct dma_async_tx_descriptor *udma_prep_dma_cyclic(struct dma_chan *chan, dma_addr_t buf_addr,
> + size_t buf_len, size_t period_len,
> + enum dma_transfer_direction dir,
> + unsigned long flags);
> +struct dma_async_tx_descriptor *udma_prep_dma_memcpy(struct dma_chan *chan,
> + dma_addr_t dest, dma_addr_t src,
> + size_t len, unsigned long tx_flags);
> +void udma_desc_pre_callback(struct virt_dma_chan *vc,
> + struct virt_dma_desc *vd,
> + struct dmaengine_result *result);
> +
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 05/19] dmaengine: ti: k3-udma: move ring management functions to k3-udma-common.c
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (3 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 04/19] dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:46 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 06/19] dmaengine: ti: k3-udma: Add variant-specific function pointers to udma_dev Sai Sree Kartheek Adivi
` (14 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Relocate the ring management functions such as push, pop, and reset
from k3-udma.c to k3-udma-common.c file. These operations are
common across multiple K3 UDMA variants and will be reused by
future implementations like K3 UDMA v2.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 103 ++++++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.c | 100 -------------------------------
drivers/dma/ti/k3-udma.h | 4 ++
3 files changed, 107 insertions(+), 100 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 9cb35759c70bb..4dcf986f84d87 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -1239,5 +1239,108 @@ void udma_desc_pre_callback(struct virt_dma_chan *vc,
}
EXPORT_SYMBOL_GPL(udma_desc_pre_callback);
+int udma_push_to_ring(struct udma_chan *uc, int idx)
+{
+ struct udma_desc *d = uc->desc;
+ struct k3_ring *ring = NULL;
+ dma_addr_t paddr;
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ ring = uc->rflow->fd_ring;
+ break;
+ case DMA_MEM_TO_DEV:
+ case DMA_MEM_TO_MEM:
+ ring = uc->tchan->t_ring;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* RX flush packet: idx == -1 is only passed in case of DEV_TO_MEM */
+ if (idx == -1) {
+ paddr = udma_get_rx_flush_hwdesc_paddr(uc);
+ } else {
+ paddr = udma_curr_cppi5_desc_paddr(d, idx);
+
+ wmb(); /* Ensure that writes are not moved over this point */
+ }
+
+ return k3_ringacc_ring_push(ring, &paddr);
+}
+EXPORT_SYMBOL_GPL(udma_push_to_ring);
+
+int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
+{
+ struct k3_ring *ring = NULL;
+ int ret;
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ ring = uc->rflow->r_ring;
+ break;
+ case DMA_MEM_TO_DEV:
+ case DMA_MEM_TO_MEM:
+ ring = uc->tchan->tc_ring;
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ ret = k3_ringacc_ring_pop(ring, addr);
+ if (ret)
+ return ret;
+
+ rmb(); /* Ensure that reads are not moved before this point */
+
+ /* Teardown completion */
+ if (cppi5_desc_is_tdcm(*addr))
+ return 0;
+
+ /* Check for flush descriptor */
+ if (udma_desc_is_rx_flush(uc, *addr))
+ return -ENOENT;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_pop_from_ring);
+
+void udma_reset_rings(struct udma_chan *uc)
+{
+ struct k3_ring *ring1 = NULL;
+ struct k3_ring *ring2 = NULL;
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ if (uc->rchan) {
+ ring1 = uc->rflow->fd_ring;
+ ring2 = uc->rflow->r_ring;
+ }
+ break;
+ case DMA_MEM_TO_DEV:
+ case DMA_MEM_TO_MEM:
+ if (uc->tchan) {
+ ring1 = uc->tchan->t_ring;
+ ring2 = uc->tchan->tc_ring;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (ring1)
+ k3_ringacc_ring_reset_dma(ring1,
+ k3_ringacc_ring_get_occ(ring1));
+ if (ring2)
+ k3_ringacc_ring_reset(ring2);
+
+ /* make sure we are not leaking memory by stalled descriptor */
+ if (uc->terminated_desc) {
+ udma_desc_free(&uc->terminated_desc->vd);
+ uc->terminated_desc = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_reset_rings);
+
MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 0a1291829611f..214a1ca1e1776 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -174,106 +174,6 @@ static bool udma_is_chan_paused(struct udma_chan *uc)
return false;
}
-static int udma_push_to_ring(struct udma_chan *uc, int idx)
-{
- struct udma_desc *d = uc->desc;
- struct k3_ring *ring = NULL;
- dma_addr_t paddr;
-
- switch (uc->config.dir) {
- case DMA_DEV_TO_MEM:
- ring = uc->rflow->fd_ring;
- break;
- case DMA_MEM_TO_DEV:
- case DMA_MEM_TO_MEM:
- ring = uc->tchan->t_ring;
- break;
- default:
- return -EINVAL;
- }
-
- /* RX flush packet: idx == -1 is only passed in case of DEV_TO_MEM */
- if (idx == -1) {
- paddr = udma_get_rx_flush_hwdesc_paddr(uc);
- } else {
- paddr = udma_curr_cppi5_desc_paddr(d, idx);
-
- wmb(); /* Ensure that writes are not moved over this point */
- }
-
- return k3_ringacc_ring_push(ring, &paddr);
-}
-
-static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
-{
- struct k3_ring *ring = NULL;
- int ret;
-
- switch (uc->config.dir) {
- case DMA_DEV_TO_MEM:
- ring = uc->rflow->r_ring;
- break;
- case DMA_MEM_TO_DEV:
- case DMA_MEM_TO_MEM:
- ring = uc->tchan->tc_ring;
- break;
- default:
- return -ENOENT;
- }
-
- ret = k3_ringacc_ring_pop(ring, addr);
- if (ret)
- return ret;
-
- rmb(); /* Ensure that reads are not moved before this point */
-
- /* Teardown completion */
- if (cppi5_desc_is_tdcm(*addr))
- return 0;
-
- /* Check for flush descriptor */
- if (udma_desc_is_rx_flush(uc, *addr))
- return -ENOENT;
-
- return 0;
-}
-
-static void udma_reset_rings(struct udma_chan *uc)
-{
- struct k3_ring *ring1 = NULL;
- struct k3_ring *ring2 = NULL;
-
- switch (uc->config.dir) {
- case DMA_DEV_TO_MEM:
- if (uc->rchan) {
- ring1 = uc->rflow->fd_ring;
- ring2 = uc->rflow->r_ring;
- }
- break;
- case DMA_MEM_TO_DEV:
- case DMA_MEM_TO_MEM:
- if (uc->tchan) {
- ring1 = uc->tchan->t_ring;
- ring2 = uc->tchan->tc_ring;
- }
- break;
- default:
- break;
- }
-
- if (ring1)
- k3_ringacc_ring_reset_dma(ring1,
- k3_ringacc_ring_get_occ(ring1));
- if (ring2)
- k3_ringacc_ring_reset(ring2);
-
- /* make sure we are not leaking memory by stalled descriptor */
- if (uc->terminated_desc) {
- udma_desc_free(&uc->terminated_desc->vd);
- uc->terminated_desc = NULL;
- }
-}
-
static void udma_decrement_byte_counters(struct udma_chan *uc, u32 val)
{
if (uc->desc->dir == DMA_DEV_TO_MEM) {
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 7c807bd9e178b..4c6e5b946d5cf 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -610,6 +610,10 @@ void udma_desc_pre_callback(struct virt_dma_chan *vc,
struct virt_dma_desc *vd,
struct dmaengine_result *result);
+int udma_push_to_ring(struct udma_chan *uc, int idx);
+int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr);
+void udma_reset_rings(struct udma_chan *uc);
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 05/19] dmaengine: ti: k3-udma: move ring management functions to k3-udma-common.c
2026-01-30 11:01 ` [PATCH v4 05/19] dmaengine: ti: k3-udma: move ring management functions " Sai Sree Kartheek Adivi
@ 2026-01-30 15:46 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:46 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:45PM +0530, Sai Sree Kartheek Adivi wrote:
> Relocate the ring management functions such as push, pop, and reset
> from k3-udma.c to k3-udma-common.c file. These operations are
> common across multiple K3 UDMA variants and will be reused by
> future implementations like K3 UDMA v2.
>
> No functional changes intended.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/ti/k3-udma-common.c | 103 ++++++++++++++++++++++++++++++++
> drivers/dma/ti/k3-udma.c | 100 -------------------------------
> drivers/dma/ti/k3-udma.h | 4 ++
> 3 files changed, 107 insertions(+), 100 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index 9cb35759c70bb..4dcf986f84d87 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -1239,5 +1239,108 @@ void udma_desc_pre_callback(struct virt_dma_chan *vc,
> }
> EXPORT_SYMBOL_GPL(udma_desc_pre_callback);
>
> +int udma_push_to_ring(struct udma_chan *uc, int idx)
> +{
> + struct udma_desc *d = uc->desc;
> + struct k3_ring *ring = NULL;
> + dma_addr_t paddr;
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + ring = uc->rflow->fd_ring;
> + break;
> + case DMA_MEM_TO_DEV:
> + case DMA_MEM_TO_MEM:
> + ring = uc->tchan->t_ring;
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + /* RX flush packet: idx == -1 is only passed in case of DEV_TO_MEM */
> + if (idx == -1) {
> + paddr = udma_get_rx_flush_hwdesc_paddr(uc);
> + } else {
> + paddr = udma_curr_cppi5_desc_paddr(d, idx);
> +
> + wmb(); /* Ensure that writes are not moved over this point */
> + }
> +
> + return k3_ringacc_ring_push(ring, &paddr);
> +}
> +EXPORT_SYMBOL_GPL(udma_push_to_ring);
> +
> +int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
> +{
> + struct k3_ring *ring = NULL;
> + int ret;
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + ring = uc->rflow->r_ring;
> + break;
> + case DMA_MEM_TO_DEV:
> + case DMA_MEM_TO_MEM:
> + ring = uc->tchan->tc_ring;
> + break;
> + default:
> + return -ENOENT;
> + }
> +
> + ret = k3_ringacc_ring_pop(ring, addr);
> + if (ret)
> + return ret;
> +
> + rmb(); /* Ensure that reads are not moved before this point */
> +
> + /* Teardown completion */
> + if (cppi5_desc_is_tdcm(*addr))
> + return 0;
> +
> + /* Check for flush descriptor */
> + if (udma_desc_is_rx_flush(uc, *addr))
> + return -ENOENT;
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(udma_pop_from_ring);
> +
> +void udma_reset_rings(struct udma_chan *uc)
> +{
> + struct k3_ring *ring1 = NULL;
> + struct k3_ring *ring2 = NULL;
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + if (uc->rchan) {
> + ring1 = uc->rflow->fd_ring;
> + ring2 = uc->rflow->r_ring;
> + }
> + break;
> + case DMA_MEM_TO_DEV:
> + case DMA_MEM_TO_MEM:
> + if (uc->tchan) {
> + ring1 = uc->tchan->t_ring;
> + ring2 = uc->tchan->tc_ring;
> + }
> + break;
> + default:
> + break;
> + }
> +
> + if (ring1)
> + k3_ringacc_ring_reset_dma(ring1,
> + k3_ringacc_ring_get_occ(ring1));
> + if (ring2)
> + k3_ringacc_ring_reset(ring2);
> +
> + /* make sure we are not leaking memory by stalled descriptor */
> + if (uc->terminated_desc) {
> + udma_desc_free(&uc->terminated_desc->vd);
> + uc->terminated_desc = NULL;
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_reset_rings);
> +
> MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
> MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 0a1291829611f..214a1ca1e1776 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -174,106 +174,6 @@ static bool udma_is_chan_paused(struct udma_chan *uc)
> return false;
> }
>
> -static int udma_push_to_ring(struct udma_chan *uc, int idx)
> -{
> - struct udma_desc *d = uc->desc;
> - struct k3_ring *ring = NULL;
> - dma_addr_t paddr;
> -
> - switch (uc->config.dir) {
> - case DMA_DEV_TO_MEM:
> - ring = uc->rflow->fd_ring;
> - break;
> - case DMA_MEM_TO_DEV:
> - case DMA_MEM_TO_MEM:
> - ring = uc->tchan->t_ring;
> - break;
> - default:
> - return -EINVAL;
> - }
> -
> - /* RX flush packet: idx == -1 is only passed in case of DEV_TO_MEM */
> - if (idx == -1) {
> - paddr = udma_get_rx_flush_hwdesc_paddr(uc);
> - } else {
> - paddr = udma_curr_cppi5_desc_paddr(d, idx);
> -
> - wmb(); /* Ensure that writes are not moved over this point */
> - }
> -
> - return k3_ringacc_ring_push(ring, &paddr);
> -}
> -
> -static int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr)
> -{
> - struct k3_ring *ring = NULL;
> - int ret;
> -
> - switch (uc->config.dir) {
> - case DMA_DEV_TO_MEM:
> - ring = uc->rflow->r_ring;
> - break;
> - case DMA_MEM_TO_DEV:
> - case DMA_MEM_TO_MEM:
> - ring = uc->tchan->tc_ring;
> - break;
> - default:
> - return -ENOENT;
> - }
> -
> - ret = k3_ringacc_ring_pop(ring, addr);
> - if (ret)
> - return ret;
> -
> - rmb(); /* Ensure that reads are not moved before this point */
> -
> - /* Teardown completion */
> - if (cppi5_desc_is_tdcm(*addr))
> - return 0;
> -
> - /* Check for flush descriptor */
> - if (udma_desc_is_rx_flush(uc, *addr))
> - return -ENOENT;
> -
> - return 0;
> -}
> -
> -static void udma_reset_rings(struct udma_chan *uc)
> -{
> - struct k3_ring *ring1 = NULL;
> - struct k3_ring *ring2 = NULL;
> -
> - switch (uc->config.dir) {
> - case DMA_DEV_TO_MEM:
> - if (uc->rchan) {
> - ring1 = uc->rflow->fd_ring;
> - ring2 = uc->rflow->r_ring;
> - }
> - break;
> - case DMA_MEM_TO_DEV:
> - case DMA_MEM_TO_MEM:
> - if (uc->tchan) {
> - ring1 = uc->tchan->t_ring;
> - ring2 = uc->tchan->tc_ring;
> - }
> - break;
> - default:
> - break;
> - }
> -
> - if (ring1)
> - k3_ringacc_ring_reset_dma(ring1,
> - k3_ringacc_ring_get_occ(ring1));
> - if (ring2)
> - k3_ringacc_ring_reset(ring2);
> -
> - /* make sure we are not leaking memory by stalled descriptor */
> - if (uc->terminated_desc) {
> - udma_desc_free(&uc->terminated_desc->vd);
> - uc->terminated_desc = NULL;
> - }
> -}
> -
> static void udma_decrement_byte_counters(struct udma_chan *uc, u32 val)
> {
> if (uc->desc->dir == DMA_DEV_TO_MEM) {
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 7c807bd9e178b..4c6e5b946d5cf 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -610,6 +610,10 @@ void udma_desc_pre_callback(struct virt_dma_chan *vc,
> struct virt_dma_desc *vd,
> struct dmaengine_result *result);
>
> +int udma_push_to_ring(struct udma_chan *uc, int idx);
> +int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr);
> +void udma_reset_rings(struct udma_chan *uc);
> +
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 06/19] dmaengine: ti: k3-udma: Add variant-specific function pointers to udma_dev
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (4 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 05/19] dmaengine: ti: k3-udma: move ring management functions " Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:52 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 07/19] dmaengine: ti: k3-udma: move udma utility functions to k3-udma-common.c Sai Sree Kartheek Adivi
` (13 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Introduce function pointers in the udma_dev structure to allow
variant-specific implementations for certain operations.
This prepares the driver for supporting multiple K3 UDMA variants,
such as UDMA v2, with minimal code duplication.
No functional changes intended in this commit.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-private.c | 10 +++++--
drivers/dma/ti/k3-udma.c | 46 +++++++++++++++++++-------------
drivers/dma/ti/k3-udma.h | 12 +++++++++
3 files changed, 47 insertions(+), 21 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c
index 624360423ef17..44c097fff5ee6 100644
--- a/drivers/dma/ti/k3-udma-private.c
+++ b/drivers/dma/ti/k3-udma-private.c
@@ -8,13 +8,19 @@
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
- return navss_psil_pair(ud, src_thread, dst_thread);
+ if (ud->psil_pair)
+ return ud->psil_pair(ud, src_thread, dst_thread);
+
+ return 0;
}
EXPORT_SYMBOL(xudma_navss_psil_pair);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
- return navss_psil_unpair(ud, src_thread, dst_thread);
+ if (ud->psil_unpair)
+ return ud->psil_unpair(ud, src_thread, dst_thread);
+
+ return 0;
}
EXPORT_SYMBOL(xudma_navss_psil_unpair);
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 214a1ca1e1776..397e890283eaa 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -40,7 +40,7 @@ static const char * const mmr_names[] = {
[MMR_TCHANRT] = "tchanrt",
};
-static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
+int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
{
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
@@ -50,8 +50,8 @@ static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
src_thread, dst_thread);
}
-static int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
- u32 dst_thread)
+int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
+ u32 dst_thread)
{
struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
@@ -329,7 +329,7 @@ static int udma_start(struct udma_chan *uc)
}
/* Make sure that we clear the teardown bit, if it is set */
- udma_reset_chan(uc, false);
+ uc->ud->reset_chan(uc, false);
/* Push descriptors before we start the channel */
udma_start_desc(uc);
@@ -521,8 +521,8 @@ static void udma_check_tx_completion(struct work_struct *work)
if (uc->desc) {
struct udma_desc *d = uc->desc;
- udma_decrement_byte_counters(uc, d->residue);
- udma_start(uc);
+ uc->ud->decrement_byte_counters(uc, d->residue);
+ uc->ud->start(uc);
vchan_cookie_complete(&d->vd);
break;
}
@@ -554,7 +554,7 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
}
if (!uc->desc)
- udma_start(uc);
+ uc->ud->start(uc);
goto out;
}
@@ -576,8 +576,8 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
vchan_cyclic_callback(&d->vd);
} else {
if (udma_is_desc_really_done(uc, d)) {
- udma_decrement_byte_counters(uc, d->residue);
- udma_start(uc);
+ uc->ud->decrement_byte_counters(uc, d->residue);
+ uc->ud->start(uc);
vchan_cookie_complete(&d->vd);
} else {
schedule_delayed_work(&uc->tx_drain.work,
@@ -612,8 +612,8 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
vchan_cyclic_callback(&d->vd);
} else {
/* TODO: figure out the real amount of data */
- udma_decrement_byte_counters(uc, d->residue);
- udma_start(uc);
+ uc->ud->decrement_byte_counters(uc, d->residue);
+ uc->ud->start(uc);
vchan_cookie_complete(&d->vd);
}
}
@@ -1654,7 +1654,7 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
if (udma_is_chan_running(uc)) {
dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
- udma_reset_chan(uc, false);
+ ud->reset_chan(uc, false);
if (udma_is_chan_running(uc)) {
dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
ret = -EBUSY;
@@ -1821,7 +1821,7 @@ static int bcdma_alloc_chan_resources(struct dma_chan *chan)
if (udma_is_chan_running(uc)) {
dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
- udma_reset_chan(uc, false);
+ ud->reset_chan(uc, false);
if (udma_is_chan_running(uc)) {
dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
ret = -EBUSY;
@@ -2014,7 +2014,7 @@ static int pktdma_alloc_chan_resources(struct dma_chan *chan)
if (udma_is_chan_running(uc)) {
dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
- udma_reset_chan(uc, false);
+ ud->reset_chan(uc, false);
if (udma_is_chan_running(uc)) {
dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
ret = -EBUSY;
@@ -2123,7 +2123,7 @@ static void udma_issue_pending(struct dma_chan *chan)
*/
if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
udma_is_chan_running(uc)))
- udma_start(uc);
+ uc->ud->start(uc);
}
spin_unlock_irqrestore(&uc->vc.lock, flags);
@@ -2265,7 +2265,7 @@ static int udma_terminate_all(struct dma_chan *chan)
spin_lock_irqsave(&uc->vc.lock, flags);
if (udma_is_chan_running(uc))
- udma_stop(uc);
+ uc->ud->stop(uc);
if (uc->desc) {
uc->terminated_desc = uc->desc;
@@ -2297,11 +2297,11 @@ static void udma_synchronize(struct dma_chan *chan)
dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
uc->id);
udma_dump_chan_stdata(uc);
- udma_reset_chan(uc, true);
+ uc->ud->reset_chan(uc, true);
}
}
- udma_reset_chan(uc, false);
+ uc->ud->reset_chan(uc, false);
if (udma_is_chan_running(uc))
dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
@@ -2355,7 +2355,7 @@ static void udma_free_chan_resources(struct dma_chan *chan)
udma_terminate_all(chan);
if (uc->terminated_desc) {
- udma_reset_chan(uc, false);
+ ud->reset_chan(uc, false);
udma_reset_rings(uc);
}
@@ -3694,6 +3694,14 @@ static int udma_probe(struct platform_device *pdev)
ud->soc_data = soc->data;
}
+ // Setup function pointers
+ ud->start = udma_start;
+ ud->stop = udma_stop;
+ ud->reset_chan = udma_reset_chan;
+ ud->decrement_byte_counters = udma_decrement_byte_counters;
+ ud->psil_pair = navss_psil_pair;
+ ud->psil_unpair = navss_psil_unpair;
+
ret = udma_get_mmrs(pdev, ud);
if (ret)
return ret;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 4c6e5b946d5cf..2f5fbea446fed 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -344,6 +344,15 @@ struct udma_dev {
u32 psil_base;
u32 atype;
u32 asel;
+
+ int (*start)(struct udma_chan *uc);
+ int (*stop)(struct udma_chan *uc);
+ int (*reset_chan)(struct udma_chan *uc, bool hard);
+ void (*decrement_byte_counters)(struct udma_chan *uc, u32 val);
+ int (*psil_pair)(struct udma_dev *ud, u32 src_thread,
+ u32 dst_thread);
+ int (*psil_unpair)(struct udma_dev *ud, u32 src_thread,
+ u32 dst_thread);
};
struct udma_desc {
@@ -614,6 +623,9 @@ int udma_push_to_ring(struct udma_chan *uc, int idx);
int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr);
void udma_reset_rings(struct udma_chan *uc);
+int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
+int navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 06/19] dmaengine: ti: k3-udma: Add variant-specific function pointers to udma_dev
2026-01-30 11:01 ` [PATCH v4 06/19] dmaengine: ti: k3-udma: Add variant-specific function pointers to udma_dev Sai Sree Kartheek Adivi
@ 2026-01-30 15:52 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:52 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:46PM +0530, Sai Sree Kartheek Adivi wrote:
> Introduce function pointers in the udma_dev structure to allow
> variant-specific implementations for certain operations.
> This prepares the driver for supporting multiple K3 UDMA variants,
> such as UDMA v2, with minimal code duplication.
>
> No functional changes intended in this commit.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> drivers/dma/ti/k3-udma-private.c | 10 +++++--
> drivers/dma/ti/k3-udma.c | 46 +++++++++++++++++++-------------
> drivers/dma/ti/k3-udma.h | 12 +++++++++
> 3 files changed, 47 insertions(+), 21 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c
> index 624360423ef17..44c097fff5ee6 100644
> --- a/drivers/dma/ti/k3-udma-private.c
> +++ b/drivers/dma/ti/k3-udma-private.c
> @@ -8,13 +8,19 @@
>
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> {
> - return navss_psil_pair(ud, src_thread, dst_thread);
> + if (ud->psil_pair)
> + return ud->psil_pair(ud, src_thread, dst_thread);
> +
> + return 0;
> }
> EXPORT_SYMBOL(xudma_navss_psil_pair);
>
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> {
> - return navss_psil_unpair(ud, src_thread, dst_thread);
> + if (ud->psil_unpair)
> + return ud->psil_unpair(ud, src_thread, dst_thread);
> +
> + return 0;
> }
> EXPORT_SYMBOL(xudma_navss_psil_unpair);
>
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 214a1ca1e1776..397e890283eaa 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -40,7 +40,7 @@ static const char * const mmr_names[] = {
> [MMR_TCHANRT] = "tchanrt",
> };
>
> -static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> +int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> {
> struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
>
> @@ -50,8 +50,8 @@ static int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread)
> src_thread, dst_thread);
> }
>
> -static int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> - u32 dst_thread)
> +int navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> + u32 dst_thread)
> {
> struct udma_tisci_rm *tisci_rm = &ud->tisci_rm;
>
> @@ -329,7 +329,7 @@ static int udma_start(struct udma_chan *uc)
> }
>
> /* Make sure that we clear the teardown bit, if it is set */
> - udma_reset_chan(uc, false);
> + uc->ud->reset_chan(uc, false);
>
> /* Push descriptors before we start the channel */
> udma_start_desc(uc);
> @@ -521,8 +521,8 @@ static void udma_check_tx_completion(struct work_struct *work)
> if (uc->desc) {
> struct udma_desc *d = uc->desc;
>
> - udma_decrement_byte_counters(uc, d->residue);
> - udma_start(uc);
> + uc->ud->decrement_byte_counters(uc, d->residue);
> + uc->ud->start(uc);
> vchan_cookie_complete(&d->vd);
> break;
> }
> @@ -554,7 +554,7 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
> }
>
> if (!uc->desc)
> - udma_start(uc);
> + uc->ud->start(uc);
>
> goto out;
> }
> @@ -576,8 +576,8 @@ static irqreturn_t udma_ring_irq_handler(int irq, void *data)
> vchan_cyclic_callback(&d->vd);
> } else {
> if (udma_is_desc_really_done(uc, d)) {
> - udma_decrement_byte_counters(uc, d->residue);
> - udma_start(uc);
> + uc->ud->decrement_byte_counters(uc, d->residue);
> + uc->ud->start(uc);
> vchan_cookie_complete(&d->vd);
> } else {
> schedule_delayed_work(&uc->tx_drain.work,
> @@ -612,8 +612,8 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
> vchan_cyclic_callback(&d->vd);
> } else {
> /* TODO: figure out the real amount of data */
> - udma_decrement_byte_counters(uc, d->residue);
> - udma_start(uc);
> + uc->ud->decrement_byte_counters(uc, d->residue);
> + uc->ud->start(uc);
> vchan_cookie_complete(&d->vd);
> }
> }
> @@ -1654,7 +1654,7 @@ static int udma_alloc_chan_resources(struct dma_chan *chan)
>
> if (udma_is_chan_running(uc)) {
> dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
> - udma_reset_chan(uc, false);
> + ud->reset_chan(uc, false);
> if (udma_is_chan_running(uc)) {
> dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
> ret = -EBUSY;
> @@ -1821,7 +1821,7 @@ static int bcdma_alloc_chan_resources(struct dma_chan *chan)
>
> if (udma_is_chan_running(uc)) {
> dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
> - udma_reset_chan(uc, false);
> + ud->reset_chan(uc, false);
> if (udma_is_chan_running(uc)) {
> dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
> ret = -EBUSY;
> @@ -2014,7 +2014,7 @@ static int pktdma_alloc_chan_resources(struct dma_chan *chan)
>
> if (udma_is_chan_running(uc)) {
> dev_warn(ud->dev, "chan%d: is running!\n", uc->id);
> - udma_reset_chan(uc, false);
> + ud->reset_chan(uc, false);
> if (udma_is_chan_running(uc)) {
> dev_err(ud->dev, "chan%d: won't stop!\n", uc->id);
> ret = -EBUSY;
> @@ -2123,7 +2123,7 @@ static void udma_issue_pending(struct dma_chan *chan)
> */
> if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
> udma_is_chan_running(uc)))
> - udma_start(uc);
> + uc->ud->start(uc);
> }
>
> spin_unlock_irqrestore(&uc->vc.lock, flags);
> @@ -2265,7 +2265,7 @@ static int udma_terminate_all(struct dma_chan *chan)
> spin_lock_irqsave(&uc->vc.lock, flags);
>
> if (udma_is_chan_running(uc))
> - udma_stop(uc);
> + uc->ud->stop(uc);
>
> if (uc->desc) {
> uc->terminated_desc = uc->desc;
> @@ -2297,11 +2297,11 @@ static void udma_synchronize(struct dma_chan *chan)
> dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
> uc->id);
> udma_dump_chan_stdata(uc);
> - udma_reset_chan(uc, true);
> + uc->ud->reset_chan(uc, true);
> }
> }
>
> - udma_reset_chan(uc, false);
> + uc->ud->reset_chan(uc, false);
> if (udma_is_chan_running(uc))
> dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
>
> @@ -2355,7 +2355,7 @@ static void udma_free_chan_resources(struct dma_chan *chan)
>
> udma_terminate_all(chan);
> if (uc->terminated_desc) {
> - udma_reset_chan(uc, false);
> + ud->reset_chan(uc, false);
> udma_reset_rings(uc);
> }
>
> @@ -3694,6 +3694,14 @@ static int udma_probe(struct platform_device *pdev)
> ud->soc_data = soc->data;
> }
>
> + // Setup function pointers
> + ud->start = udma_start;
> + ud->stop = udma_stop;
> + ud->reset_chan = udma_reset_chan;
> + ud->decrement_byte_counters = udma_decrement_byte_counters;
> + ud->psil_pair = navss_psil_pair;
> + ud->psil_unpair = navss_psil_unpair;
> +
> ret = udma_get_mmrs(pdev, ud);
> if (ret)
> return ret;
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 4c6e5b946d5cf..2f5fbea446fed 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -344,6 +344,15 @@ struct udma_dev {
> u32 psil_base;
> u32 atype;
> u32 asel;
> +
> + int (*start)(struct udma_chan *uc);
> + int (*stop)(struct udma_chan *uc);
> + int (*reset_chan)(struct udma_chan *uc, bool hard);
> + void (*decrement_byte_counters)(struct udma_chan *uc, u32 val);
> + int (*psil_pair)(struct udma_dev *ud, u32 src_thread,
> + u32 dst_thread);
> + int (*psil_unpair)(struct udma_dev *ud, u32 src_thread,
> + u32 dst_thread);
> };
>
> struct udma_desc {
> @@ -614,6 +623,9 @@ int udma_push_to_ring(struct udma_chan *uc, int idx);
> int udma_pop_from_ring(struct udma_chan *uc, dma_addr_t *addr);
> void udma_reset_rings(struct udma_chan *uc);
>
> +int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> +int navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> +
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 07/19] dmaengine: ti: k3-udma: move udma utility functions to k3-udma-common.c
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (5 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 06/19] dmaengine: ti: k3-udma: Add variant-specific function pointers to udma_dev Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:58 ` Frank Li
2026-01-30 11:01 ` [PATCH v4 08/19] dmaengine: ti: k3-udma: move resource management " Sai Sree Kartheek Adivi
` (12 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Relocate udma utility functions from k3-udma.c to k3-udma-common.c file.
The implementation of these functions is largely shared between K3 UDMA
and K3 UDMA v2. This refactor improves code reuse and maintainability
across multiple variants.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 549 ++++++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.c | 531 ------------------------------
drivers/dma/ti/k3-udma.h | 28 ++
3 files changed, 577 insertions(+), 531 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 4dcf986f84d87..472eedc4663a9 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -4,6 +4,7 @@
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
+#include <linux/delay.h>
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
@@ -46,6 +47,28 @@ struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
}
EXPORT_SYMBOL_GPL(udma_udma_desc_from_paddr);
+void udma_start_desc(struct udma_chan *uc)
+{
+ struct udma_chan_config *ucc = &uc->config;
+
+ if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
+ (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
+ int i;
+
+ /*
+ * 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 {
+ udma_push_to_ring(uc, 0);
+ }
+}
+EXPORT_SYMBOL_GPL(udma_start_desc);
+
void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
{
if (uc->use_dma_pool) {
@@ -1342,5 +1365,531 @@ void udma_reset_rings(struct udma_chan *uc)
}
EXPORT_SYMBOL_GPL(udma_reset_rings);
+u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id)
+{
+ int i;
+
+ for (i = 0; i < tpl_map->levels; i++) {
+ if (chan_id >= tpl_map->start_idx[i])
+ return i;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_get_chan_tpl_index);
+
+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;
+ }
+}
+EXPORT_SYMBOL_GPL(k3_configure_chan_coherency);
+
+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;
+}
+EXPORT_SYMBOL_GPL(udma_reset_uchan);
+
+void udma_dump_chan_stdata(struct udma_chan *uc)
+{
+ struct device *dev = uc->ud->dev;
+ u32 offset;
+ int i;
+
+ if (uc->config.dir == DMA_MEM_TO_DEV || uc->config.dir == DMA_MEM_TO_MEM) {
+ dev_dbg(dev, "TCHAN State data:\n");
+ for (i = 0; i < 32; i++) {
+ offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
+ dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i,
+ udma_tchanrt_read(uc, offset));
+ }
+ }
+
+ if (uc->config.dir == DMA_DEV_TO_MEM || uc->config.dir == DMA_MEM_TO_MEM) {
+ dev_dbg(dev, "RCHAN State data:\n");
+ for (i = 0; i < 32; i++) {
+ offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
+ dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i,
+ udma_rchanrt_read(uc, offset));
+ }
+ }
+}
+
+bool udma_is_chan_running(struct udma_chan *uc)
+{
+ u32 trt_ctl = 0;
+ u32 rrt_ctl = 0;
+
+ if (uc->tchan)
+ trt_ctl = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
+ if (uc->rchan)
+ rrt_ctl = udma_rchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
+
+ if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN)
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(udma_is_chan_running);
+
+bool udma_chan_needs_reconfiguration(struct udma_chan *uc)
+{
+ /* Only PDMAs have staticTR */
+ if (uc->config.ep_type == PSIL_EP_NATIVE)
+ return false;
+
+ /* Check if the staticTR configuration has changed for TX */
+ if (memcmp(&uc->static_tr, &uc->desc->static_tr, sizeof(uc->static_tr)))
+ return true;
+
+ return false;
+}
+EXPORT_SYMBOL_GPL(udma_chan_needs_reconfiguration);
+
+void udma_cyclic_packet_elapsed(struct udma_chan *uc)
+{
+ struct udma_desc *d = uc->desc;
+ struct cppi5_host_desc_t *h_desc;
+
+ h_desc = d->hwdesc[d->desc_idx].cppi5_desc_vaddr;
+ cppi5_hdesc_reset_to_original(h_desc);
+ udma_push_to_ring(uc, d->desc_idx);
+ d->desc_idx = (d->desc_idx + 1) % d->sglen;
+}
+EXPORT_SYMBOL_GPL(udma_cyclic_packet_elapsed);
+
+void udma_check_tx_completion(struct work_struct *work)
+{
+ struct udma_chan *uc = container_of(work, typeof(*uc),
+ tx_drain.work.work);
+ bool desc_done = true;
+ u32 residue_diff;
+ ktime_t time_diff;
+ unsigned long delay;
+ unsigned long flags;
+
+ while (1) {
+ spin_lock_irqsave(&uc->vc.lock, flags);
+
+ if (uc->desc) {
+ /* Get previous residue and time stamp */
+ residue_diff = uc->tx_drain.residue;
+ time_diff = uc->tx_drain.tstamp;
+ /*
+ * Get current residue and time stamp or see if
+ * transfer is complete
+ */
+ desc_done = udma_is_desc_really_done(uc, uc->desc);
+ }
+
+ if (!desc_done) {
+ /*
+ * Find the time delta and residue delta w.r.t
+ * previous poll
+ */
+ time_diff = ktime_sub(uc->tx_drain.tstamp,
+ time_diff) + 1;
+ residue_diff -= uc->tx_drain.residue;
+ if (residue_diff) {
+ /*
+ * Try to guess when we should check
+ * next time by calculating rate at
+ * which data is being drained at the
+ * peer device
+ */
+ delay = (time_diff / residue_diff) *
+ uc->tx_drain.residue;
+ } else {
+ /* No progress, check again in 1 second */
+ schedule_delayed_work(&uc->tx_drain.work, HZ);
+ break;
+ }
+
+ spin_unlock_irqrestore(&uc->vc.lock, flags);
+
+ usleep_range(ktime_to_us(delay),
+ ktime_to_us(delay) + 10);
+ continue;
+ }
+
+ if (uc->desc) {
+ struct udma_desc *d = uc->desc;
+
+ uc->ud->decrement_byte_counters(uc, d->residue);
+ uc->ud->start(uc);
+ vchan_cookie_complete(&d->vd);
+ break;
+ }
+
+ break;
+ }
+
+ spin_unlock_irqrestore(&uc->vc.lock, flags);
+}
+EXPORT_SYMBOL_GPL(udma_check_tx_completion);
+
+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;
+}
+EXPORT_SYMBOL_GPL(udma_slave_config);
+
+void udma_issue_pending(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&uc->vc.lock, flags);
+
+ /* If we have something pending and no active descriptor, then */
+ if (vchan_issue_pending(&uc->vc) && !uc->desc) {
+ /*
+ * start a descriptor if the channel is NOT [marked as
+ * terminating _and_ it is still running (teardown has not
+ * completed yet)].
+ */
+ if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
+ udma_is_chan_running(uc)))
+ uc->ud->start(uc);
+ }
+
+ spin_unlock_irqrestore(&uc->vc.lock, flags);
+}
+EXPORT_SYMBOL_GPL(udma_issue_pending);
+
+int udma_terminate_all(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&uc->vc.lock, flags);
+
+ if (udma_is_chan_running(uc))
+ uc->ud->stop(uc);
+
+ if (uc->desc) {
+ uc->terminated_desc = uc->desc;
+ uc->desc = NULL;
+ uc->terminated_desc->terminated = true;
+ cancel_delayed_work(&uc->tx_drain.work);
+ }
+
+ uc->paused = false;
+
+ vchan_get_all_descriptors(&uc->vc, &head);
+ spin_unlock_irqrestore(&uc->vc.lock, flags);
+ vchan_dma_desc_free_list(&uc->vc, &head);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_terminate_all);
+
+void udma_synchronize(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ unsigned long timeout = msecs_to_jiffies(1000);
+
+ vchan_synchronize(&uc->vc);
+
+ if (uc->state == UDMA_CHAN_IS_TERMINATING) {
+ timeout = wait_for_completion_timeout(&uc->teardown_completed,
+ timeout);
+ if (!timeout) {
+ dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
+ uc->id);
+ udma_dump_chan_stdata(uc);
+ uc->ud->reset_chan(uc, true);
+ }
+ }
+
+ uc->ud->reset_chan(uc, false);
+ if (udma_is_chan_running(uc))
+ dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
+
+ cancel_delayed_work_sync(&uc->tx_drain.work);
+ udma_reset_rings(uc);
+}
+EXPORT_SYMBOL_GPL(udma_synchronize);
+
+/*
+ * This tasklet handles the completion of a DMA descriptor by
+ * calling its callback and freeing it.
+ */
+void udma_vchan_complete(struct tasklet_struct *t)
+{
+ struct virt_dma_chan *vc = from_tasklet(vc, t, task);
+ struct virt_dma_desc *vd, *_vd;
+ struct dmaengine_desc_callback cb;
+ LIST_HEAD(head);
+
+ spin_lock_irq(&vc->lock);
+ list_splice_tail_init(&vc->desc_completed, &head);
+ vd = vc->cyclic;
+ if (vd) {
+ vc->cyclic = NULL;
+ dmaengine_desc_get_callback(&vd->tx, &cb);
+ } else {
+ memset(&cb, 0, sizeof(cb));
+ }
+ spin_unlock_irq(&vc->lock);
+
+ udma_desc_pre_callback(vc, vd, NULL);
+ dmaengine_desc_callback_invoke(&cb, NULL);
+
+ list_for_each_entry_safe(vd, _vd, &head, node) {
+ struct dmaengine_result result;
+
+ dmaengine_desc_get_callback(&vd->tx, &cb);
+
+ list_del(&vd->node);
+
+ udma_desc_pre_callback(vc, vd, &result);
+ dmaengine_desc_callback_invoke(&cb, &result);
+
+ vchan_vdesc_fini(vd);
+ }
+}
+EXPORT_SYMBOL_GPL(udma_vchan_complete);
+
+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);
+}
+EXPORT_SYMBOL_GPL(udma_mark_resource_ranges);
+
+int udma_setup_rx_flush(struct udma_dev *ud)
+{
+ struct udma_rx_flush *rx_flush = &ud->rx_flush;
+ struct cppi5_desc_hdr_t *tr_desc;
+ struct cppi5_tr_type1_t *tr_req;
+ struct cppi5_host_desc_t *desc;
+ struct device *dev = ud->dev;
+ struct udma_hwdesc *hwdesc;
+ size_t tr_size;
+
+ /* Allocate 1K buffer for discarded data on RX channel teardown */
+ rx_flush->buffer_size = SZ_1K;
+ rx_flush->buffer_vaddr = devm_kzalloc(dev, rx_flush->buffer_size,
+ GFP_KERNEL);
+ if (!rx_flush->buffer_vaddr)
+ return -ENOMEM;
+
+ rx_flush->buffer_paddr = dma_map_single(dev, rx_flush->buffer_vaddr,
+ rx_flush->buffer_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, rx_flush->buffer_paddr))
+ return -ENOMEM;
+
+ /* Set up descriptor to be used for TR mode */
+ hwdesc = &rx_flush->hwdescs[0];
+ tr_size = sizeof(struct cppi5_tr_type1_t);
+ hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size, 1);
+ hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
+ ud->desc_align);
+
+ hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
+ GFP_KERNEL);
+ if (!hwdesc->cppi5_desc_vaddr)
+ return -ENOMEM;
+
+ hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
+ hwdesc->cppi5_desc_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
+ return -ENOMEM;
+
+ /* Start of the TR req records */
+ hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
+ /* Start address of the TR response array */
+ hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size;
+
+ tr_desc = hwdesc->cppi5_desc_vaddr;
+ cppi5_trdesc_init(tr_desc, 1, tr_size, 0, 0);
+ cppi5_desc_set_pktids(tr_desc, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
+ cppi5_desc_set_retpolicy(tr_desc, 0, 0);
+
+ tr_req = hwdesc->tr_req_base;
+ cppi5_tr_init(&tr_req->flags, CPPI5_TR_TYPE1, false, false,
+ CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
+ cppi5_tr_csf_set(&tr_req->flags, CPPI5_TR_CSF_SUPR_EVT);
+
+ tr_req->addr = rx_flush->buffer_paddr;
+ tr_req->icnt0 = rx_flush->buffer_size;
+ tr_req->icnt1 = 1;
+
+ dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
+ hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
+
+ /* Set up descriptor to be used for packet mode */
+ hwdesc = &rx_flush->hwdescs[1];
+ hwdesc->cppi5_desc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
+ CPPI5_INFO0_HDESC_EPIB_SIZE +
+ CPPI5_INFO0_HDESC_PSDATA_MAX_SIZE,
+ ud->desc_align);
+
+ hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
+ GFP_KERNEL);
+ if (!hwdesc->cppi5_desc_vaddr)
+ return -ENOMEM;
+
+ hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
+ hwdesc->cppi5_desc_size,
+ DMA_TO_DEVICE);
+ if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
+ return -ENOMEM;
+
+ desc = hwdesc->cppi5_desc_vaddr;
+ cppi5_hdesc_init(desc, 0, 0);
+ cppi5_desc_set_pktids(&desc->hdr, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
+ cppi5_desc_set_retpolicy(&desc->hdr, 0, 0);
+
+ cppi5_hdesc_attach_buf(desc,
+ rx_flush->buffer_paddr, rx_flush->buffer_size,
+ rx_flush->buffer_paddr, rx_flush->buffer_size);
+
+ dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
+ hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_setup_rx_flush);
+
+#ifdef CONFIG_DEBUG_FS
+void udma_dbg_summary_show_chan(struct seq_file *s,
+ struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ struct udma_chan_config *ucc = &uc->config;
+
+ seq_printf(s, " %-13s| %s", dma_chan_name(chan),
+ chan->dbg_client_name ?: "in-use");
+ 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_puts(s, ")\n");
+ return;
+ }
+
+ if (ucc->ep_type == PSIL_EP_NATIVE) {
+ seq_puts(s, "PSI-L Native");
+ if (ucc->metadata_size) {
+ seq_printf(s, "[%s", ucc->needs_epib ? " EPIB" : "");
+ if (ucc->psd_size)
+ seq_printf(s, " PSDsize:%u", ucc->psd_size);
+ seq_puts(s, " ]");
+ }
+ } else {
+ seq_puts(s, "PDMA");
+ if (ucc->enable_acc32 || ucc->enable_burst)
+ seq_printf(s, "[%s%s ]",
+ ucc->enable_acc32 ? " ACC32" : "",
+ ucc->enable_burst ? " BURST" : "");
+ }
+
+ seq_printf(s, ", %s)\n", ucc->pkt_mode ? "Packet mode" : "TR mode");
+}
+
+void udma_dbg_summary_show(struct seq_file *s,
+ struct dma_device *dma_dev)
+{
+ struct dma_chan *chan;
+
+ list_for_each_entry(chan, &dma_dev->channels, device_node) {
+ if (chan->client_count)
+ udma_dbg_summary_show_chan(s, chan);
+ }
+}
+EXPORT_SYMBOL_GPL(udma_dbg_summary_show);
+#endif /* CONFIG_DEBUG_FS */
+
+enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
+{
+ const struct udma_match_data *match_data = ud->match_data;
+ u8 tpl;
+
+ if (!match_data->enable_memcpy_support)
+ return DMAENGINE_ALIGN_8_BYTES;
+
+ /* Get the highest TPL level the device supports for memcpy */
+ if (ud->bchan_cnt)
+ tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, 0);
+ else if (ud->tchan_cnt)
+ tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, 0);
+ else
+ return DMAENGINE_ALIGN_8_BYTES;
+
+ switch (match_data->burst_size[tpl]) {
+ case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES:
+ return DMAENGINE_ALIGN_256_BYTES;
+ case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES:
+ return DMAENGINE_ALIGN_128_BYTES;
+ case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES:
+ fallthrough;
+ default:
+ return DMAENGINE_ALIGN_64_BYTES;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_get_copy_align);
+
MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 397e890283eaa..e86c811a15eb9 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -61,92 +61,6 @@ 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 u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id)
-{
- int i;
-
- for (i = 0; i < tpl_map->levels; i++) {
- if (chan_id >= tpl_map->start_idx[i])
- return i;
- }
-
- return 0;
-}
-
-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;
-}
-
-static void udma_dump_chan_stdata(struct udma_chan *uc)
-{
- struct device *dev = uc->ud->dev;
- u32 offset;
- int i;
-
- if (uc->config.dir == DMA_MEM_TO_DEV || uc->config.dir == DMA_MEM_TO_MEM) {
- dev_dbg(dev, "TCHAN State data:\n");
- for (i = 0; i < 32; i++) {
- offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
- dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i,
- udma_tchanrt_read(uc, offset));
- }
- }
-
- if (uc->config.dir == DMA_DEV_TO_MEM || uc->config.dir == DMA_MEM_TO_MEM) {
- dev_dbg(dev, "RCHAN State data:\n");
- for (i = 0; i < 32; i++) {
- offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
- dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i,
- udma_rchanrt_read(uc, offset));
- }
- }
-}
-
-static bool udma_is_chan_running(struct udma_chan *uc)
-{
- u32 trt_ctl = 0;
- u32 rrt_ctl = 0;
-
- if (uc->tchan)
- trt_ctl = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
- if (uc->rchan)
- rrt_ctl = udma_rchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
-
- if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN)
- return true;
-
- return false;
-}
-
static bool udma_is_chan_paused(struct udma_chan *uc)
{
u32 val, pause_mask;
@@ -275,40 +189,6 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard)
return 0;
}
-static void udma_start_desc(struct udma_chan *uc)
-{
- struct udma_chan_config *ucc = &uc->config;
-
- if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
- (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
- int i;
-
- /*
- * 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 {
- udma_push_to_ring(uc, 0);
- }
-}
-
-static bool udma_chan_needs_reconfiguration(struct udma_chan *uc)
-{
- /* Only PDMAs have staticTR */
- if (uc->config.ep_type == PSIL_EP_NATIVE)
- return false;
-
- /* Check if the staticTR configuration has changed for TX */
- if (memcmp(&uc->static_tr, &uc->desc->static_tr, sizeof(uc->static_tr)))
- return true;
-
- return false;
-}
-
static int udma_start(struct udma_chan *uc)
{
struct virt_dma_desc *vd = vchan_next_desc(&uc->vc);
@@ -453,86 +333,6 @@ static int udma_stop(struct udma_chan *uc)
return 0;
}
-static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
-{
- struct udma_desc *d = uc->desc;
- struct cppi5_host_desc_t *h_desc;
-
- h_desc = d->hwdesc[d->desc_idx].cppi5_desc_vaddr;
- cppi5_hdesc_reset_to_original(h_desc);
- udma_push_to_ring(uc, d->desc_idx);
- d->desc_idx = (d->desc_idx + 1) % d->sglen;
-}
-
-static void udma_check_tx_completion(struct work_struct *work)
-{
- struct udma_chan *uc = container_of(work, typeof(*uc),
- tx_drain.work.work);
- bool desc_done = true;
- u32 residue_diff;
- ktime_t time_diff;
- unsigned long delay;
- unsigned long flags;
-
- while (1) {
- spin_lock_irqsave(&uc->vc.lock, flags);
-
- if (uc->desc) {
- /* Get previous residue and time stamp */
- residue_diff = uc->tx_drain.residue;
- time_diff = uc->tx_drain.tstamp;
- /*
- * Get current residue and time stamp or see if
- * transfer is complete
- */
- desc_done = udma_is_desc_really_done(uc, uc->desc);
- }
-
- if (!desc_done) {
- /*
- * Find the time delta and residue delta w.r.t
- * previous poll
- */
- time_diff = ktime_sub(uc->tx_drain.tstamp,
- time_diff) + 1;
- residue_diff -= uc->tx_drain.residue;
- if (residue_diff) {
- /*
- * Try to guess when we should check
- * next time by calculating rate at
- * which data is being drained at the
- * peer device
- */
- delay = (time_diff / residue_diff) *
- uc->tx_drain.residue;
- } else {
- /* No progress, check again in 1 second */
- schedule_delayed_work(&uc->tx_drain.work, HZ);
- break;
- }
-
- spin_unlock_irqrestore(&uc->vc.lock, flags);
-
- usleep_range(ktime_to_us(delay),
- ktime_to_us(delay) + 10);
- continue;
- }
-
- if (uc->desc) {
- struct udma_desc *d = uc->desc;
-
- uc->ud->decrement_byte_counters(uc, d->residue);
- uc->ud->start(uc);
- vchan_cookie_complete(&d->vd);
- break;
- }
-
- break;
- }
-
- spin_unlock_irqrestore(&uc->vc.lock, flags);
-}
-
static irqreturn_t udma_ring_irq_handler(int irq, void *data)
{
struct udma_chan *uc = data;
@@ -2097,38 +1897,6 @@ static int pktdma_alloc_chan_resources(struct dma_chan *chan)
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 void udma_issue_pending(struct dma_chan *chan)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- unsigned long flags;
-
- spin_lock_irqsave(&uc->vc.lock, flags);
-
- /* If we have something pending and no active descriptor, then */
- if (vchan_issue_pending(&uc->vc) && !uc->desc) {
- /*
- * start a descriptor if the channel is NOT [marked as
- * terminating _and_ it is still running (teardown has not
- * completed yet)].
- */
- if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
- udma_is_chan_running(uc)))
- uc->ud->start(uc);
- }
-
- spin_unlock_irqrestore(&uc->vc.lock, flags);
-}
-
static enum dma_status udma_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
@@ -2256,98 +2024,6 @@ static int udma_resume(struct dma_chan *chan)
return 0;
}
-static int udma_terminate_all(struct dma_chan *chan)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- unsigned long flags;
- LIST_HEAD(head);
-
- spin_lock_irqsave(&uc->vc.lock, flags);
-
- if (udma_is_chan_running(uc))
- uc->ud->stop(uc);
-
- if (uc->desc) {
- uc->terminated_desc = uc->desc;
- uc->desc = NULL;
- uc->terminated_desc->terminated = true;
- cancel_delayed_work(&uc->tx_drain.work);
- }
-
- uc->paused = false;
-
- vchan_get_all_descriptors(&uc->vc, &head);
- spin_unlock_irqrestore(&uc->vc.lock, flags);
- vchan_dma_desc_free_list(&uc->vc, &head);
-
- return 0;
-}
-
-static void udma_synchronize(struct dma_chan *chan)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- unsigned long timeout = msecs_to_jiffies(1000);
-
- vchan_synchronize(&uc->vc);
-
- if (uc->state == UDMA_CHAN_IS_TERMINATING) {
- timeout = wait_for_completion_timeout(&uc->teardown_completed,
- timeout);
- if (!timeout) {
- dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
- uc->id);
- udma_dump_chan_stdata(uc);
- uc->ud->reset_chan(uc, true);
- }
- }
-
- uc->ud->reset_chan(uc, false);
- if (udma_is_chan_running(uc))
- dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
-
- cancel_delayed_work_sync(&uc->tx_drain.work);
- udma_reset_rings(uc);
-}
-
-/*
- * This tasklet handles the completion of a DMA descriptor by
- * calling its callback and freeing it.
- */
-static void udma_vchan_complete(struct tasklet_struct *t)
-{
- struct virt_dma_chan *vc = from_tasklet(vc, t, task);
- struct virt_dma_desc *vd, *_vd;
- struct dmaengine_desc_callback cb;
- LIST_HEAD(head);
-
- spin_lock_irq(&vc->lock);
- list_splice_tail_init(&vc->desc_completed, &head);
- vd = vc->cyclic;
- if (vd) {
- vc->cyclic = NULL;
- dmaengine_desc_get_callback(&vd->tx, &cb);
- } else {
- memset(&cb, 0, sizeof(cb));
- }
- spin_unlock_irq(&vc->lock);
-
- udma_desc_pre_callback(vc, vd, NULL);
- dmaengine_desc_callback_invoke(&cb, NULL);
-
- list_for_each_entry_safe(vd, _vd, &head, node) {
- struct dmaengine_result result;
-
- dmaengine_desc_get_callback(&vd->tx, &cb);
-
- list_del(&vd->node);
-
- udma_desc_pre_callback(vc, vd, &result);
- dmaengine_desc_callback_invoke(&cb, &result);
-
- vchan_vdesc_fini(vd);
- }
-}
-
static void udma_free_chan_resources(struct dma_chan *chan)
{
struct udma_chan *uc = to_udma_chan(chan);
@@ -2822,17 +2498,6 @@ 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",
@@ -3463,202 +3128,6 @@ static int setup_resources(struct udma_dev *ud)
return ch_count;
}
-static int udma_setup_rx_flush(struct udma_dev *ud)
-{
- struct udma_rx_flush *rx_flush = &ud->rx_flush;
- struct cppi5_desc_hdr_t *tr_desc;
- struct cppi5_tr_type1_t *tr_req;
- struct cppi5_host_desc_t *desc;
- struct device *dev = ud->dev;
- struct udma_hwdesc *hwdesc;
- size_t tr_size;
-
- /* Allocate 1K buffer for discarded data on RX channel teardown */
- rx_flush->buffer_size = SZ_1K;
- rx_flush->buffer_vaddr = devm_kzalloc(dev, rx_flush->buffer_size,
- GFP_KERNEL);
- if (!rx_flush->buffer_vaddr)
- return -ENOMEM;
-
- rx_flush->buffer_paddr = dma_map_single(dev, rx_flush->buffer_vaddr,
- rx_flush->buffer_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, rx_flush->buffer_paddr))
- return -ENOMEM;
-
- /* Set up descriptor to be used for TR mode */
- hwdesc = &rx_flush->hwdescs[0];
- tr_size = sizeof(struct cppi5_tr_type1_t);
- hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size, 1);
- hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
- ud->desc_align);
-
- hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
- GFP_KERNEL);
- if (!hwdesc->cppi5_desc_vaddr)
- return -ENOMEM;
-
- hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
- hwdesc->cppi5_desc_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
- return -ENOMEM;
-
- /* Start of the TR req records */
- hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
- /* Start address of the TR response array */
- hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size;
-
- tr_desc = hwdesc->cppi5_desc_vaddr;
- cppi5_trdesc_init(tr_desc, 1, tr_size, 0, 0);
- cppi5_desc_set_pktids(tr_desc, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
- cppi5_desc_set_retpolicy(tr_desc, 0, 0);
-
- tr_req = hwdesc->tr_req_base;
- cppi5_tr_init(&tr_req->flags, CPPI5_TR_TYPE1, false, false,
- CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
- cppi5_tr_csf_set(&tr_req->flags, CPPI5_TR_CSF_SUPR_EVT);
-
- tr_req->addr = rx_flush->buffer_paddr;
- tr_req->icnt0 = rx_flush->buffer_size;
- tr_req->icnt1 = 1;
-
- dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
- hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
-
- /* Set up descriptor to be used for packet mode */
- hwdesc = &rx_flush->hwdescs[1];
- hwdesc->cppi5_desc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
- CPPI5_INFO0_HDESC_EPIB_SIZE +
- CPPI5_INFO0_HDESC_PSDATA_MAX_SIZE,
- ud->desc_align);
-
- hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
- GFP_KERNEL);
- if (!hwdesc->cppi5_desc_vaddr)
- return -ENOMEM;
-
- hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
- hwdesc->cppi5_desc_size,
- DMA_TO_DEVICE);
- if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
- return -ENOMEM;
-
- desc = hwdesc->cppi5_desc_vaddr;
- cppi5_hdesc_init(desc, 0, 0);
- cppi5_desc_set_pktids(&desc->hdr, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
- cppi5_desc_set_retpolicy(&desc->hdr, 0, 0);
-
- cppi5_hdesc_attach_buf(desc,
- rx_flush->buffer_paddr, rx_flush->buffer_size,
- rx_flush->buffer_paddr, rx_flush->buffer_size);
-
- dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
- hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
- return 0;
-}
-
-#ifdef CONFIG_DEBUG_FS
-static void udma_dbg_summary_show_chan(struct seq_file *s,
- struct dma_chan *chan)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- struct udma_chan_config *ucc = &uc->config;
-
- seq_printf(s, " %-13s| %s", dma_chan_name(chan),
- chan->dbg_client_name ?: "in-use");
- 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");
- return;
- }
-
- if (ucc->ep_type == PSIL_EP_NATIVE) {
- seq_printf(s, "PSI-L Native");
- if (ucc->metadata_size) {
- seq_printf(s, "[%s", ucc->needs_epib ? " EPIB" : "");
- if (ucc->psd_size)
- seq_printf(s, " PSDsize:%u", ucc->psd_size);
- seq_printf(s, " ]");
- }
- } else {
- seq_printf(s, "PDMA");
- if (ucc->enable_acc32 || ucc->enable_burst)
- seq_printf(s, "[%s%s ]",
- ucc->enable_acc32 ? " ACC32" : "",
- ucc->enable_burst ? " BURST" : "");
- }
-
- seq_printf(s, ", %s)\n", ucc->pkt_mode ? "Packet mode" : "TR mode");
-}
-
-static void udma_dbg_summary_show(struct seq_file *s,
- struct dma_device *dma_dev)
-{
- struct dma_chan *chan;
-
- list_for_each_entry(chan, &dma_dev->channels, device_node) {
- if (chan->client_count)
- udma_dbg_summary_show_chan(s, chan);
- }
-}
-#endif /* CONFIG_DEBUG_FS */
-
-static enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
-{
- const struct udma_match_data *match_data = ud->match_data;
- u8 tpl;
-
- if (!match_data->enable_memcpy_support)
- return DMAENGINE_ALIGN_8_BYTES;
-
- /* Get the highest TPL level the device supports for memcpy */
- if (ud->bchan_cnt)
- tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, 0);
- else if (ud->tchan_cnt)
- tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, 0);
- else
- return DMAENGINE_ALIGN_8_BYTES;
-
- switch (match_data->burst_size[tpl]) {
- case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES:
- return DMAENGINE_ALIGN_256_BYTES;
- case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES:
- return DMAENGINE_ALIGN_128_BYTES;
- case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES:
- fallthrough;
- default:
- return DMAENGINE_ALIGN_64_BYTES;
- }
-}
-
static int udma_probe(struct platform_device *pdev)
{
struct device_node *navss_node = pdev->dev.parent->of_node;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 2f5fbea446fed..797e8b0c5b85e 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -625,6 +625,34 @@ void udma_reset_rings(struct udma_chan *uc);
int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
+void udma_start_desc(struct udma_chan *uc);
+u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id);
+void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel);
+void udma_reset_uchan(struct udma_chan *uc);
+void udma_dump_chan_stdata(struct udma_chan *uc);
+bool udma_is_chan_running(struct udma_chan *uc);
+
+bool udma_chan_needs_reconfiguration(struct udma_chan *uc);
+void udma_cyclic_packet_elapsed(struct udma_chan *uc);
+void udma_check_tx_completion(struct work_struct *work);
+int udma_slave_config(struct dma_chan *chan,
+ struct dma_slave_config *cfg);
+void udma_issue_pending(struct dma_chan *chan);
+int udma_terminate_all(struct dma_chan *chan);
+void udma_synchronize(struct dma_chan *chan);
+void udma_vchan_complete(struct tasklet_struct *t);
+void udma_mark_resource_ranges(struct udma_dev *ud, unsigned long *map,
+ struct ti_sci_resource_desc *rm_desc,
+ char *name);
+int udma_setup_rx_flush(struct udma_dev *ud);
+enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud);
+
+#ifdef CONFIG_DEBUG_FS
+void udma_dbg_summary_show_chan(struct seq_file *s,
+ struct dma_chan *chan);
+void udma_dbg_summary_show(struct seq_file *s,
+ struct dma_device *dma_dev);
+#endif /* CONFIG_DEBUG_FS */
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 07/19] dmaengine: ti: k3-udma: move udma utility functions to k3-udma-common.c
2026-01-30 11:01 ` [PATCH v4 07/19] dmaengine: ti: k3-udma: move udma utility functions to k3-udma-common.c Sai Sree Kartheek Adivi
@ 2026-01-30 15:58 ` Frank Li
0 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:58 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:47PM +0530, Sai Sree Kartheek Adivi wrote:
> Relocate udma utility functions from k3-udma.c to k3-udma-common.c file.
>
> The implementation of these functions is largely shared between K3 UDMA
> and K3 UDMA v2. This refactor improves code reuse and maintainability
> across multiple variants.
>
> No functional changes intended.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/k3-udma-common.c | 549 ++++++++++++++++++++++++++++++++
> drivers/dma/ti/k3-udma.c | 531 ------------------------------
> drivers/dma/ti/k3-udma.h | 28 ++
> 3 files changed, 577 insertions(+), 531 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index 4dcf986f84d87..472eedc4663a9 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -4,6 +4,7 @@
> * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
> */
>
> +#include <linux/delay.h>
> #include <linux/dmaengine.h>
> #include <linux/dma-mapping.h>
> #include <linux/dmapool.h>
> @@ -46,6 +47,28 @@ struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> }
> EXPORT_SYMBOL_GPL(udma_udma_desc_from_paddr);
>
> +void udma_start_desc(struct udma_chan *uc)
> +{
> + struct udma_chan_config *ucc = &uc->config;
> +
> + if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
> + (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
> + int i;
> +
> + /*
> + * 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 {
> + udma_push_to_ring(uc, 0);
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_start_desc);
> +
Not sure if naming pollution in future. suggest use "ti_udma". You can
rename after these relocation patches.
Frank
> void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
> {
> if (uc->use_dma_pool) {
> @@ -1342,5 +1365,531 @@ void udma_reset_rings(struct udma_chan *uc)
> }
> EXPORT_SYMBOL_GPL(udma_reset_rings);
>
> +u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id)
> +{
> + int i;
> +
> + for (i = 0; i < tpl_map->levels; i++) {
> + if (chan_id >= tpl_map->start_idx[i])
> + return i;
> + }
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(udma_get_chan_tpl_index);
> +
> +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;
> + }
> +}
> +EXPORT_SYMBOL_GPL(k3_configure_chan_coherency);
> +
> +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;
> +}
> +EXPORT_SYMBOL_GPL(udma_reset_uchan);
> +
> +void udma_dump_chan_stdata(struct udma_chan *uc)
> +{
> + struct device *dev = uc->ud->dev;
> + u32 offset;
> + int i;
> +
> + if (uc->config.dir == DMA_MEM_TO_DEV || uc->config.dir == DMA_MEM_TO_MEM) {
> + dev_dbg(dev, "TCHAN State data:\n");
> + for (i = 0; i < 32; i++) {
> + offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
> + dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i,
> + udma_tchanrt_read(uc, offset));
> + }
> + }
> +
> + if (uc->config.dir == DMA_DEV_TO_MEM || uc->config.dir == DMA_MEM_TO_MEM) {
> + dev_dbg(dev, "RCHAN State data:\n");
> + for (i = 0; i < 32; i++) {
> + offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
> + dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i,
> + udma_rchanrt_read(uc, offset));
> + }
> + }
> +}
> +
> +bool udma_is_chan_running(struct udma_chan *uc)
> +{
> + u32 trt_ctl = 0;
> + u32 rrt_ctl = 0;
> +
> + if (uc->tchan)
> + trt_ctl = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> + if (uc->rchan)
> + rrt_ctl = udma_rchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> +
> + if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN)
> + return true;
> +
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(udma_is_chan_running);
> +
> +bool udma_chan_needs_reconfiguration(struct udma_chan *uc)
> +{
> + /* Only PDMAs have staticTR */
> + if (uc->config.ep_type == PSIL_EP_NATIVE)
> + return false;
> +
> + /* Check if the staticTR configuration has changed for TX */
> + if (memcmp(&uc->static_tr, &uc->desc->static_tr, sizeof(uc->static_tr)))
> + return true;
> +
> + return false;
> +}
> +EXPORT_SYMBOL_GPL(udma_chan_needs_reconfiguration);
> +
> +void udma_cyclic_packet_elapsed(struct udma_chan *uc)
> +{
> + struct udma_desc *d = uc->desc;
> + struct cppi5_host_desc_t *h_desc;
> +
> + h_desc = d->hwdesc[d->desc_idx].cppi5_desc_vaddr;
> + cppi5_hdesc_reset_to_original(h_desc);
> + udma_push_to_ring(uc, d->desc_idx);
> + d->desc_idx = (d->desc_idx + 1) % d->sglen;
> +}
> +EXPORT_SYMBOL_GPL(udma_cyclic_packet_elapsed);
> +
> +void udma_check_tx_completion(struct work_struct *work)
> +{
> + struct udma_chan *uc = container_of(work, typeof(*uc),
> + tx_drain.work.work);
> + bool desc_done = true;
> + u32 residue_diff;
> + ktime_t time_diff;
> + unsigned long delay;
> + unsigned long flags;
> +
> + while (1) {
> + spin_lock_irqsave(&uc->vc.lock, flags);
> +
> + if (uc->desc) {
> + /* Get previous residue and time stamp */
> + residue_diff = uc->tx_drain.residue;
> + time_diff = uc->tx_drain.tstamp;
> + /*
> + * Get current residue and time stamp or see if
> + * transfer is complete
> + */
> + desc_done = udma_is_desc_really_done(uc, uc->desc);
> + }
> +
> + if (!desc_done) {
> + /*
> + * Find the time delta and residue delta w.r.t
> + * previous poll
> + */
> + time_diff = ktime_sub(uc->tx_drain.tstamp,
> + time_diff) + 1;
> + residue_diff -= uc->tx_drain.residue;
> + if (residue_diff) {
> + /*
> + * Try to guess when we should check
> + * next time by calculating rate at
> + * which data is being drained at the
> + * peer device
> + */
> + delay = (time_diff / residue_diff) *
> + uc->tx_drain.residue;
> + } else {
> + /* No progress, check again in 1 second */
> + schedule_delayed_work(&uc->tx_drain.work, HZ);
> + break;
> + }
> +
> + spin_unlock_irqrestore(&uc->vc.lock, flags);
> +
> + usleep_range(ktime_to_us(delay),
> + ktime_to_us(delay) + 10);
> + continue;
> + }
> +
> + if (uc->desc) {
> + struct udma_desc *d = uc->desc;
> +
> + uc->ud->decrement_byte_counters(uc, d->residue);
> + uc->ud->start(uc);
> + vchan_cookie_complete(&d->vd);
> + break;
> + }
> +
> + break;
> + }
> +
> + spin_unlock_irqrestore(&uc->vc.lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(udma_check_tx_completion);
> +
> +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;
> +}
> +EXPORT_SYMBOL_GPL(udma_slave_config);
> +
> +void udma_issue_pending(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + unsigned long flags;
> +
> + spin_lock_irqsave(&uc->vc.lock, flags);
> +
> + /* If we have something pending and no active descriptor, then */
> + if (vchan_issue_pending(&uc->vc) && !uc->desc) {
> + /*
> + * start a descriptor if the channel is NOT [marked as
> + * terminating _and_ it is still running (teardown has not
> + * completed yet)].
> + */
> + if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
> + udma_is_chan_running(uc)))
> + uc->ud->start(uc);
> + }
> +
> + spin_unlock_irqrestore(&uc->vc.lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(udma_issue_pending);
> +
> +int udma_terminate_all(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + unsigned long flags;
> + LIST_HEAD(head);
> +
> + spin_lock_irqsave(&uc->vc.lock, flags);
> +
> + if (udma_is_chan_running(uc))
> + uc->ud->stop(uc);
> +
> + if (uc->desc) {
> + uc->terminated_desc = uc->desc;
> + uc->desc = NULL;
> + uc->terminated_desc->terminated = true;
> + cancel_delayed_work(&uc->tx_drain.work);
> + }
> +
> + uc->paused = false;
> +
> + vchan_get_all_descriptors(&uc->vc, &head);
> + spin_unlock_irqrestore(&uc->vc.lock, flags);
> + vchan_dma_desc_free_list(&uc->vc, &head);
> +
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(udma_terminate_all);
> +
> +void udma_synchronize(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + unsigned long timeout = msecs_to_jiffies(1000);
> +
> + vchan_synchronize(&uc->vc);
> +
> + if (uc->state == UDMA_CHAN_IS_TERMINATING) {
> + timeout = wait_for_completion_timeout(&uc->teardown_completed,
> + timeout);
> + if (!timeout) {
> + dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
> + uc->id);
> + udma_dump_chan_stdata(uc);
> + uc->ud->reset_chan(uc, true);
> + }
> + }
> +
> + uc->ud->reset_chan(uc, false);
> + if (udma_is_chan_running(uc))
> + dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
> +
> + cancel_delayed_work_sync(&uc->tx_drain.work);
> + udma_reset_rings(uc);
> +}
> +EXPORT_SYMBOL_GPL(udma_synchronize);
> +
> +/*
> + * This tasklet handles the completion of a DMA descriptor by
> + * calling its callback and freeing it.
> + */
> +void udma_vchan_complete(struct tasklet_struct *t)
> +{
> + struct virt_dma_chan *vc = from_tasklet(vc, t, task);
> + struct virt_dma_desc *vd, *_vd;
> + struct dmaengine_desc_callback cb;
> + LIST_HEAD(head);
> +
> + spin_lock_irq(&vc->lock);
> + list_splice_tail_init(&vc->desc_completed, &head);
> + vd = vc->cyclic;
> + if (vd) {
> + vc->cyclic = NULL;
> + dmaengine_desc_get_callback(&vd->tx, &cb);
> + } else {
> + memset(&cb, 0, sizeof(cb));
> + }
> + spin_unlock_irq(&vc->lock);
> +
> + udma_desc_pre_callback(vc, vd, NULL);
> + dmaengine_desc_callback_invoke(&cb, NULL);
> +
> + list_for_each_entry_safe(vd, _vd, &head, node) {
> + struct dmaengine_result result;
> +
> + dmaengine_desc_get_callback(&vd->tx, &cb);
> +
> + list_del(&vd->node);
> +
> + udma_desc_pre_callback(vc, vd, &result);
> + dmaengine_desc_callback_invoke(&cb, &result);
> +
> + vchan_vdesc_fini(vd);
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_vchan_complete);
> +
> +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);
> +}
> +EXPORT_SYMBOL_GPL(udma_mark_resource_ranges);
> +
> +int udma_setup_rx_flush(struct udma_dev *ud)
> +{
> + struct udma_rx_flush *rx_flush = &ud->rx_flush;
> + struct cppi5_desc_hdr_t *tr_desc;
> + struct cppi5_tr_type1_t *tr_req;
> + struct cppi5_host_desc_t *desc;
> + struct device *dev = ud->dev;
> + struct udma_hwdesc *hwdesc;
> + size_t tr_size;
> +
> + /* Allocate 1K buffer for discarded data on RX channel teardown */
> + rx_flush->buffer_size = SZ_1K;
> + rx_flush->buffer_vaddr = devm_kzalloc(dev, rx_flush->buffer_size,
> + GFP_KERNEL);
> + if (!rx_flush->buffer_vaddr)
> + return -ENOMEM;
> +
> + rx_flush->buffer_paddr = dma_map_single(dev, rx_flush->buffer_vaddr,
> + rx_flush->buffer_size,
> + DMA_TO_DEVICE);
> + if (dma_mapping_error(dev, rx_flush->buffer_paddr))
> + return -ENOMEM;
> +
> + /* Set up descriptor to be used for TR mode */
> + hwdesc = &rx_flush->hwdescs[0];
> + tr_size = sizeof(struct cppi5_tr_type1_t);
> + hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size, 1);
> + hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
> + ud->desc_align);
> +
> + hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
> + GFP_KERNEL);
> + if (!hwdesc->cppi5_desc_vaddr)
> + return -ENOMEM;
> +
> + hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
> + hwdesc->cppi5_desc_size,
> + DMA_TO_DEVICE);
> + if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
> + return -ENOMEM;
> +
> + /* Start of the TR req records */
> + hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
> + /* Start address of the TR response array */
> + hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size;
> +
> + tr_desc = hwdesc->cppi5_desc_vaddr;
> + cppi5_trdesc_init(tr_desc, 1, tr_size, 0, 0);
> + cppi5_desc_set_pktids(tr_desc, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> + cppi5_desc_set_retpolicy(tr_desc, 0, 0);
> +
> + tr_req = hwdesc->tr_req_base;
> + cppi5_tr_init(&tr_req->flags, CPPI5_TR_TYPE1, false, false,
> + CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> + cppi5_tr_csf_set(&tr_req->flags, CPPI5_TR_CSF_SUPR_EVT);
> +
> + tr_req->addr = rx_flush->buffer_paddr;
> + tr_req->icnt0 = rx_flush->buffer_size;
> + tr_req->icnt1 = 1;
> +
> + dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
> + hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
> +
> + /* Set up descriptor to be used for packet mode */
> + hwdesc = &rx_flush->hwdescs[1];
> + hwdesc->cppi5_desc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
> + CPPI5_INFO0_HDESC_EPIB_SIZE +
> + CPPI5_INFO0_HDESC_PSDATA_MAX_SIZE,
> + ud->desc_align);
> +
> + hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
> + GFP_KERNEL);
> + if (!hwdesc->cppi5_desc_vaddr)
> + return -ENOMEM;
> +
> + hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
> + hwdesc->cppi5_desc_size,
> + DMA_TO_DEVICE);
> + if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
> + return -ENOMEM;
> +
> + desc = hwdesc->cppi5_desc_vaddr;
> + cppi5_hdesc_init(desc, 0, 0);
> + cppi5_desc_set_pktids(&desc->hdr, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> + cppi5_desc_set_retpolicy(&desc->hdr, 0, 0);
> +
> + cppi5_hdesc_attach_buf(desc,
> + rx_flush->buffer_paddr, rx_flush->buffer_size,
> + rx_flush->buffer_paddr, rx_flush->buffer_size);
> +
> + dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
> + hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(udma_setup_rx_flush);
> +
> +#ifdef CONFIG_DEBUG_FS
> +void udma_dbg_summary_show_chan(struct seq_file *s,
> + struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + struct udma_chan_config *ucc = &uc->config;
> +
> + seq_printf(s, " %-13s| %s", dma_chan_name(chan),
> + chan->dbg_client_name ?: "in-use");
> + 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_puts(s, ")\n");
> + return;
> + }
> +
> + if (ucc->ep_type == PSIL_EP_NATIVE) {
> + seq_puts(s, "PSI-L Native");
> + if (ucc->metadata_size) {
> + seq_printf(s, "[%s", ucc->needs_epib ? " EPIB" : "");
> + if (ucc->psd_size)
> + seq_printf(s, " PSDsize:%u", ucc->psd_size);
> + seq_puts(s, " ]");
> + }
> + } else {
> + seq_puts(s, "PDMA");
> + if (ucc->enable_acc32 || ucc->enable_burst)
> + seq_printf(s, "[%s%s ]",
> + ucc->enable_acc32 ? " ACC32" : "",
> + ucc->enable_burst ? " BURST" : "");
> + }
> +
> + seq_printf(s, ", %s)\n", ucc->pkt_mode ? "Packet mode" : "TR mode");
> +}
> +
> +void udma_dbg_summary_show(struct seq_file *s,
> + struct dma_device *dma_dev)
> +{
> + struct dma_chan *chan;
> +
> + list_for_each_entry(chan, &dma_dev->channels, device_node) {
> + if (chan->client_count)
> + udma_dbg_summary_show_chan(s, chan);
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_dbg_summary_show);
> +#endif /* CONFIG_DEBUG_FS */
> +
> +enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
> +{
> + const struct udma_match_data *match_data = ud->match_data;
> + u8 tpl;
> +
> + if (!match_data->enable_memcpy_support)
> + return DMAENGINE_ALIGN_8_BYTES;
> +
> + /* Get the highest TPL level the device supports for memcpy */
> + if (ud->bchan_cnt)
> + tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, 0);
> + else if (ud->tchan_cnt)
> + tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, 0);
> + else
> + return DMAENGINE_ALIGN_8_BYTES;
> +
> + switch (match_data->burst_size[tpl]) {
> + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES:
> + return DMAENGINE_ALIGN_256_BYTES;
> + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES:
> + return DMAENGINE_ALIGN_128_BYTES;
> + case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES:
> + fallthrough;
> + default:
> + return DMAENGINE_ALIGN_64_BYTES;
> + }
> +}
> +EXPORT_SYMBOL_GPL(udma_get_copy_align);
> +
> MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
> MODULE_LICENSE("GPL v2");
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index 397e890283eaa..e86c811a15eb9 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -61,92 +61,6 @@ 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 u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id)
> -{
> - int i;
> -
> - for (i = 0; i < tpl_map->levels; i++) {
> - if (chan_id >= tpl_map->start_idx[i])
> - return i;
> - }
> -
> - return 0;
> -}
> -
> -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;
> -}
> -
> -static void udma_dump_chan_stdata(struct udma_chan *uc)
> -{
> - struct device *dev = uc->ud->dev;
> - u32 offset;
> - int i;
> -
> - if (uc->config.dir == DMA_MEM_TO_DEV || uc->config.dir == DMA_MEM_TO_MEM) {
> - dev_dbg(dev, "TCHAN State data:\n");
> - for (i = 0; i < 32; i++) {
> - offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
> - dev_dbg(dev, "TRT_STDATA[%02d]: 0x%08x\n", i,
> - udma_tchanrt_read(uc, offset));
> - }
> - }
> -
> - if (uc->config.dir == DMA_DEV_TO_MEM || uc->config.dir == DMA_MEM_TO_MEM) {
> - dev_dbg(dev, "RCHAN State data:\n");
> - for (i = 0; i < 32; i++) {
> - offset = UDMA_CHAN_RT_STDATA_REG + i * 4;
> - dev_dbg(dev, "RRT_STDATA[%02d]: 0x%08x\n", i,
> - udma_rchanrt_read(uc, offset));
> - }
> - }
> -}
> -
> -static bool udma_is_chan_running(struct udma_chan *uc)
> -{
> - u32 trt_ctl = 0;
> - u32 rrt_ctl = 0;
> -
> - if (uc->tchan)
> - trt_ctl = udma_tchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> - if (uc->rchan)
> - rrt_ctl = udma_rchanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> -
> - if (trt_ctl & UDMA_CHAN_RT_CTL_EN || rrt_ctl & UDMA_CHAN_RT_CTL_EN)
> - return true;
> -
> - return false;
> -}
> -
> static bool udma_is_chan_paused(struct udma_chan *uc)
> {
> u32 val, pause_mask;
> @@ -275,40 +189,6 @@ static int udma_reset_chan(struct udma_chan *uc, bool hard)
> return 0;
> }
>
> -static void udma_start_desc(struct udma_chan *uc)
> -{
> - struct udma_chan_config *ucc = &uc->config;
> -
> - if (uc->ud->match_data->type == DMA_TYPE_UDMA && ucc->pkt_mode &&
> - (uc->cyclic || ucc->dir == DMA_DEV_TO_MEM)) {
> - int i;
> -
> - /*
> - * 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 {
> - udma_push_to_ring(uc, 0);
> - }
> -}
> -
> -static bool udma_chan_needs_reconfiguration(struct udma_chan *uc)
> -{
> - /* Only PDMAs have staticTR */
> - if (uc->config.ep_type == PSIL_EP_NATIVE)
> - return false;
> -
> - /* Check if the staticTR configuration has changed for TX */
> - if (memcmp(&uc->static_tr, &uc->desc->static_tr, sizeof(uc->static_tr)))
> - return true;
> -
> - return false;
> -}
> -
> static int udma_start(struct udma_chan *uc)
> {
> struct virt_dma_desc *vd = vchan_next_desc(&uc->vc);
> @@ -453,86 +333,6 @@ static int udma_stop(struct udma_chan *uc)
> return 0;
> }
>
> -static void udma_cyclic_packet_elapsed(struct udma_chan *uc)
> -{
> - struct udma_desc *d = uc->desc;
> - struct cppi5_host_desc_t *h_desc;
> -
> - h_desc = d->hwdesc[d->desc_idx].cppi5_desc_vaddr;
> - cppi5_hdesc_reset_to_original(h_desc);
> - udma_push_to_ring(uc, d->desc_idx);
> - d->desc_idx = (d->desc_idx + 1) % d->sglen;
> -}
> -
> -static void udma_check_tx_completion(struct work_struct *work)
> -{
> - struct udma_chan *uc = container_of(work, typeof(*uc),
> - tx_drain.work.work);
> - bool desc_done = true;
> - u32 residue_diff;
> - ktime_t time_diff;
> - unsigned long delay;
> - unsigned long flags;
> -
> - while (1) {
> - spin_lock_irqsave(&uc->vc.lock, flags);
> -
> - if (uc->desc) {
> - /* Get previous residue and time stamp */
> - residue_diff = uc->tx_drain.residue;
> - time_diff = uc->tx_drain.tstamp;
> - /*
> - * Get current residue and time stamp or see if
> - * transfer is complete
> - */
> - desc_done = udma_is_desc_really_done(uc, uc->desc);
> - }
> -
> - if (!desc_done) {
> - /*
> - * Find the time delta and residue delta w.r.t
> - * previous poll
> - */
> - time_diff = ktime_sub(uc->tx_drain.tstamp,
> - time_diff) + 1;
> - residue_diff -= uc->tx_drain.residue;
> - if (residue_diff) {
> - /*
> - * Try to guess when we should check
> - * next time by calculating rate at
> - * which data is being drained at the
> - * peer device
> - */
> - delay = (time_diff / residue_diff) *
> - uc->tx_drain.residue;
> - } else {
> - /* No progress, check again in 1 second */
> - schedule_delayed_work(&uc->tx_drain.work, HZ);
> - break;
> - }
> -
> - spin_unlock_irqrestore(&uc->vc.lock, flags);
> -
> - usleep_range(ktime_to_us(delay),
> - ktime_to_us(delay) + 10);
> - continue;
> - }
> -
> - if (uc->desc) {
> - struct udma_desc *d = uc->desc;
> -
> - uc->ud->decrement_byte_counters(uc, d->residue);
> - uc->ud->start(uc);
> - vchan_cookie_complete(&d->vd);
> - break;
> - }
> -
> - break;
> - }
> -
> - spin_unlock_irqrestore(&uc->vc.lock, flags);
> -}
> -
> static irqreturn_t udma_ring_irq_handler(int irq, void *data)
> {
> struct udma_chan *uc = data;
> @@ -2097,38 +1897,6 @@ static int pktdma_alloc_chan_resources(struct dma_chan *chan)
> 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 void udma_issue_pending(struct dma_chan *chan)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - unsigned long flags;
> -
> - spin_lock_irqsave(&uc->vc.lock, flags);
> -
> - /* If we have something pending and no active descriptor, then */
> - if (vchan_issue_pending(&uc->vc) && !uc->desc) {
> - /*
> - * start a descriptor if the channel is NOT [marked as
> - * terminating _and_ it is still running (teardown has not
> - * completed yet)].
> - */
> - if (!(uc->state == UDMA_CHAN_IS_TERMINATING &&
> - udma_is_chan_running(uc)))
> - uc->ud->start(uc);
> - }
> -
> - spin_unlock_irqrestore(&uc->vc.lock, flags);
> -}
> -
> static enum dma_status udma_tx_status(struct dma_chan *chan,
> dma_cookie_t cookie,
> struct dma_tx_state *txstate)
> @@ -2256,98 +2024,6 @@ static int udma_resume(struct dma_chan *chan)
> return 0;
> }
>
> -static int udma_terminate_all(struct dma_chan *chan)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - unsigned long flags;
> - LIST_HEAD(head);
> -
> - spin_lock_irqsave(&uc->vc.lock, flags);
> -
> - if (udma_is_chan_running(uc))
> - uc->ud->stop(uc);
> -
> - if (uc->desc) {
> - uc->terminated_desc = uc->desc;
> - uc->desc = NULL;
> - uc->terminated_desc->terminated = true;
> - cancel_delayed_work(&uc->tx_drain.work);
> - }
> -
> - uc->paused = false;
> -
> - vchan_get_all_descriptors(&uc->vc, &head);
> - spin_unlock_irqrestore(&uc->vc.lock, flags);
> - vchan_dma_desc_free_list(&uc->vc, &head);
> -
> - return 0;
> -}
> -
> -static void udma_synchronize(struct dma_chan *chan)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - unsigned long timeout = msecs_to_jiffies(1000);
> -
> - vchan_synchronize(&uc->vc);
> -
> - if (uc->state == UDMA_CHAN_IS_TERMINATING) {
> - timeout = wait_for_completion_timeout(&uc->teardown_completed,
> - timeout);
> - if (!timeout) {
> - dev_warn(uc->ud->dev, "chan%d teardown timeout!\n",
> - uc->id);
> - udma_dump_chan_stdata(uc);
> - uc->ud->reset_chan(uc, true);
> - }
> - }
> -
> - uc->ud->reset_chan(uc, false);
> - if (udma_is_chan_running(uc))
> - dev_warn(uc->ud->dev, "chan%d refused to stop!\n", uc->id);
> -
> - cancel_delayed_work_sync(&uc->tx_drain.work);
> - udma_reset_rings(uc);
> -}
> -
> -/*
> - * This tasklet handles the completion of a DMA descriptor by
> - * calling its callback and freeing it.
> - */
> -static void udma_vchan_complete(struct tasklet_struct *t)
> -{
> - struct virt_dma_chan *vc = from_tasklet(vc, t, task);
> - struct virt_dma_desc *vd, *_vd;
> - struct dmaengine_desc_callback cb;
> - LIST_HEAD(head);
> -
> - spin_lock_irq(&vc->lock);
> - list_splice_tail_init(&vc->desc_completed, &head);
> - vd = vc->cyclic;
> - if (vd) {
> - vc->cyclic = NULL;
> - dmaengine_desc_get_callback(&vd->tx, &cb);
> - } else {
> - memset(&cb, 0, sizeof(cb));
> - }
> - spin_unlock_irq(&vc->lock);
> -
> - udma_desc_pre_callback(vc, vd, NULL);
> - dmaengine_desc_callback_invoke(&cb, NULL);
> -
> - list_for_each_entry_safe(vd, _vd, &head, node) {
> - struct dmaengine_result result;
> -
> - dmaengine_desc_get_callback(&vd->tx, &cb);
> -
> - list_del(&vd->node);
> -
> - udma_desc_pre_callback(vc, vd, &result);
> - dmaengine_desc_callback_invoke(&cb, &result);
> -
> - vchan_vdesc_fini(vd);
> - }
> -}
> -
> static void udma_free_chan_resources(struct dma_chan *chan)
> {
> struct udma_chan *uc = to_udma_chan(chan);
> @@ -2822,17 +2498,6 @@ 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",
> @@ -3463,202 +3128,6 @@ static int setup_resources(struct udma_dev *ud)
> return ch_count;
> }
>
> -static int udma_setup_rx_flush(struct udma_dev *ud)
> -{
> - struct udma_rx_flush *rx_flush = &ud->rx_flush;
> - struct cppi5_desc_hdr_t *tr_desc;
> - struct cppi5_tr_type1_t *tr_req;
> - struct cppi5_host_desc_t *desc;
> - struct device *dev = ud->dev;
> - struct udma_hwdesc *hwdesc;
> - size_t tr_size;
> -
> - /* Allocate 1K buffer for discarded data on RX channel teardown */
> - rx_flush->buffer_size = SZ_1K;
> - rx_flush->buffer_vaddr = devm_kzalloc(dev, rx_flush->buffer_size,
> - GFP_KERNEL);
> - if (!rx_flush->buffer_vaddr)
> - return -ENOMEM;
> -
> - rx_flush->buffer_paddr = dma_map_single(dev, rx_flush->buffer_vaddr,
> - rx_flush->buffer_size,
> - DMA_TO_DEVICE);
> - if (dma_mapping_error(dev, rx_flush->buffer_paddr))
> - return -ENOMEM;
> -
> - /* Set up descriptor to be used for TR mode */
> - hwdesc = &rx_flush->hwdescs[0];
> - tr_size = sizeof(struct cppi5_tr_type1_t);
> - hwdesc->cppi5_desc_size = cppi5_trdesc_calc_size(tr_size, 1);
> - hwdesc->cppi5_desc_size = ALIGN(hwdesc->cppi5_desc_size,
> - ud->desc_align);
> -
> - hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
> - GFP_KERNEL);
> - if (!hwdesc->cppi5_desc_vaddr)
> - return -ENOMEM;
> -
> - hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
> - hwdesc->cppi5_desc_size,
> - DMA_TO_DEVICE);
> - if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
> - return -ENOMEM;
> -
> - /* Start of the TR req records */
> - hwdesc->tr_req_base = hwdesc->cppi5_desc_vaddr + tr_size;
> - /* Start address of the TR response array */
> - hwdesc->tr_resp_base = hwdesc->tr_req_base + tr_size;
> -
> - tr_desc = hwdesc->cppi5_desc_vaddr;
> - cppi5_trdesc_init(tr_desc, 1, tr_size, 0, 0);
> - cppi5_desc_set_pktids(tr_desc, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> - cppi5_desc_set_retpolicy(tr_desc, 0, 0);
> -
> - tr_req = hwdesc->tr_req_base;
> - cppi5_tr_init(&tr_req->flags, CPPI5_TR_TYPE1, false, false,
> - CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> - cppi5_tr_csf_set(&tr_req->flags, CPPI5_TR_CSF_SUPR_EVT);
> -
> - tr_req->addr = rx_flush->buffer_paddr;
> - tr_req->icnt0 = rx_flush->buffer_size;
> - tr_req->icnt1 = 1;
> -
> - dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
> - hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
> -
> - /* Set up descriptor to be used for packet mode */
> - hwdesc = &rx_flush->hwdescs[1];
> - hwdesc->cppi5_desc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
> - CPPI5_INFO0_HDESC_EPIB_SIZE +
> - CPPI5_INFO0_HDESC_PSDATA_MAX_SIZE,
> - ud->desc_align);
> -
> - hwdesc->cppi5_desc_vaddr = devm_kzalloc(dev, hwdesc->cppi5_desc_size,
> - GFP_KERNEL);
> - if (!hwdesc->cppi5_desc_vaddr)
> - return -ENOMEM;
> -
> - hwdesc->cppi5_desc_paddr = dma_map_single(dev, hwdesc->cppi5_desc_vaddr,
> - hwdesc->cppi5_desc_size,
> - DMA_TO_DEVICE);
> - if (dma_mapping_error(dev, hwdesc->cppi5_desc_paddr))
> - return -ENOMEM;
> -
> - desc = hwdesc->cppi5_desc_vaddr;
> - cppi5_hdesc_init(desc, 0, 0);
> - cppi5_desc_set_pktids(&desc->hdr, 0, CPPI5_INFO1_DESC_FLOWID_DEFAULT);
> - cppi5_desc_set_retpolicy(&desc->hdr, 0, 0);
> -
> - cppi5_hdesc_attach_buf(desc,
> - rx_flush->buffer_paddr, rx_flush->buffer_size,
> - rx_flush->buffer_paddr, rx_flush->buffer_size);
> -
> - dma_sync_single_for_device(dev, hwdesc->cppi5_desc_paddr,
> - hwdesc->cppi5_desc_size, DMA_TO_DEVICE);
> - return 0;
> -}
> -
> -#ifdef CONFIG_DEBUG_FS
> -static void udma_dbg_summary_show_chan(struct seq_file *s,
> - struct dma_chan *chan)
> -{
> - struct udma_chan *uc = to_udma_chan(chan);
> - struct udma_chan_config *ucc = &uc->config;
> -
> - seq_printf(s, " %-13s| %s", dma_chan_name(chan),
> - chan->dbg_client_name ?: "in-use");
> - 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");
> - return;
> - }
> -
> - if (ucc->ep_type == PSIL_EP_NATIVE) {
> - seq_printf(s, "PSI-L Native");
> - if (ucc->metadata_size) {
> - seq_printf(s, "[%s", ucc->needs_epib ? " EPIB" : "");
> - if (ucc->psd_size)
> - seq_printf(s, " PSDsize:%u", ucc->psd_size);
> - seq_printf(s, " ]");
> - }
> - } else {
> - seq_printf(s, "PDMA");
> - if (ucc->enable_acc32 || ucc->enable_burst)
> - seq_printf(s, "[%s%s ]",
> - ucc->enable_acc32 ? " ACC32" : "",
> - ucc->enable_burst ? " BURST" : "");
> - }
> -
> - seq_printf(s, ", %s)\n", ucc->pkt_mode ? "Packet mode" : "TR mode");
> -}
> -
> -static void udma_dbg_summary_show(struct seq_file *s,
> - struct dma_device *dma_dev)
> -{
> - struct dma_chan *chan;
> -
> - list_for_each_entry(chan, &dma_dev->channels, device_node) {
> - if (chan->client_count)
> - udma_dbg_summary_show_chan(s, chan);
> - }
> -}
> -#endif /* CONFIG_DEBUG_FS */
> -
> -static enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
> -{
> - const struct udma_match_data *match_data = ud->match_data;
> - u8 tpl;
> -
> - if (!match_data->enable_memcpy_support)
> - return DMAENGINE_ALIGN_8_BYTES;
> -
> - /* Get the highest TPL level the device supports for memcpy */
> - if (ud->bchan_cnt)
> - tpl = udma_get_chan_tpl_index(&ud->bchan_tpl, 0);
> - else if (ud->tchan_cnt)
> - tpl = udma_get_chan_tpl_index(&ud->tchan_tpl, 0);
> - else
> - return DMAENGINE_ALIGN_8_BYTES;
> -
> - switch (match_data->burst_size[tpl]) {
> - case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_256_BYTES:
> - return DMAENGINE_ALIGN_256_BYTES;
> - case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_128_BYTES:
> - return DMAENGINE_ALIGN_128_BYTES;
> - case TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES:
> - fallthrough;
> - default:
> - return DMAENGINE_ALIGN_64_BYTES;
> - }
> -}
> -
> static int udma_probe(struct platform_device *pdev)
> {
> struct device_node *navss_node = pdev->dev.parent->of_node;
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 2f5fbea446fed..797e8b0c5b85e 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -625,6 +625,34 @@ void udma_reset_rings(struct udma_chan *uc);
>
> int navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> int navss_psil_unpair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> +void udma_start_desc(struct udma_chan *uc);
> +u8 udma_get_chan_tpl_index(struct udma_tpl *tpl_map, int chan_id);
> +void k3_configure_chan_coherency(struct dma_chan *chan, u32 asel);
> +void udma_reset_uchan(struct udma_chan *uc);
> +void udma_dump_chan_stdata(struct udma_chan *uc);
> +bool udma_is_chan_running(struct udma_chan *uc);
> +
> +bool udma_chan_needs_reconfiguration(struct udma_chan *uc);
> +void udma_cyclic_packet_elapsed(struct udma_chan *uc);
> +void udma_check_tx_completion(struct work_struct *work);
> +int udma_slave_config(struct dma_chan *chan,
> + struct dma_slave_config *cfg);
> +void udma_issue_pending(struct dma_chan *chan);
> +int udma_terminate_all(struct dma_chan *chan);
> +void udma_synchronize(struct dma_chan *chan);
> +void udma_vchan_complete(struct tasklet_struct *t);
> +void udma_mark_resource_ranges(struct udma_dev *ud, unsigned long *map,
> + struct ti_sci_resource_desc *rm_desc,
> + char *name);
> +int udma_setup_rx_flush(struct udma_dev *ud);
> +enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud);
> +
> +#ifdef CONFIG_DEBUG_FS
> +void udma_dbg_summary_show_chan(struct seq_file *s,
> + struct dma_chan *chan);
> +void udma_dbg_summary_show(struct seq_file *s,
> + struct dma_device *dma_dev);
> +#endif /* CONFIG_DEBUG_FS */
>
> /* Direct access to UDMA low lever resources for the glue layer */
> int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 08/19] dmaengine: ti: k3-udma: move resource management functions to k3-udma-common.c
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (6 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 07/19] dmaengine: ti: k3-udma: move udma utility functions to k3-udma-common.c Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 09/19] dmaengine: ti: k3-udma: refactor resource setup functions Sai Sree Kartheek Adivi
` (11 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Move functions responsible for allocation and release of udma
resources such as channels, rings and flows from k3-udma.c
to the common k3-udma-common.c file.
The implementation of these functions is largely shared between K3 UDMA
and K3 UDMA v2. This refactor improves code reuse and maintainability
across multiple variants.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 442 ++++++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.c | 423 ------------------------------
drivers/dma/ti/k3-udma.h | 21 ++
3 files changed, 463 insertions(+), 423 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 472eedc4663a9..882d27b3c9ee5 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -1891,5 +1891,447 @@ enum dmaengine_alignment udma_get_copy_align(struct udma_dev *ud)
}
EXPORT_SYMBOL_GPL(udma_get_copy_align);
+/**
+ * __udma_alloc_gp_rflow_range - alloc range of GP RX flows
+ * @ud: UDMA device
+ * @from: Start the search from this flow id number
+ * @cnt: Number of consecutive flow ids to allocate
+ *
+ * Allocate range of RX flow ids for future use, those flows can be requested
+ * only using explicit flow id number. if @from is set to -1 it will try to find
+ * first free range. if @from is positive value it will force allocation only
+ * of the specified range of flows.
+ *
+ * Returns -ENOMEM if can't find free range.
+ * -EEXIST if requested range is busy.
+ * -EINVAL if wrong input values passed.
+ * Returns flow id on success.
+ */
+int __udma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
+{
+ int start, tmp_from;
+ DECLARE_BITMAP(tmp, K3_UDMA_MAX_RFLOWS);
+
+ tmp_from = from;
+ if (tmp_from < 0)
+ tmp_from = ud->rchan_cnt;
+ /* default flows can't be allocated and accessible only by id */
+ if (tmp_from < ud->rchan_cnt)
+ return -EINVAL;
+
+ if (tmp_from + cnt > ud->rflow_cnt)
+ return -EINVAL;
+
+ bitmap_or(tmp, ud->rflow_gp_map, ud->rflow_gp_map_allocated,
+ ud->rflow_cnt);
+
+ start = bitmap_find_next_zero_area(tmp,
+ ud->rflow_cnt,
+ tmp_from, cnt, 0);
+ if (start >= ud->rflow_cnt)
+ return -ENOMEM;
+
+ if (from >= 0 && start != from)
+ return -EEXIST;
+
+ bitmap_set(ud->rflow_gp_map_allocated, start, cnt);
+ return start;
+}
+EXPORT_SYMBOL_GPL(__udma_alloc_gp_rflow_range);
+
+int __udma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
+{
+ if (from < ud->rchan_cnt)
+ return -EINVAL;
+ if (from + cnt > ud->rflow_cnt)
+ return -EINVAL;
+
+ bitmap_clear(ud->rflow_gp_map_allocated, from, cnt);
+ return 0;
+}
+EXPORT_SYMBOL_GPL(__udma_free_gp_rflow_range);
+
+struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id)
+{
+ /*
+ * Attempt to request rflow by ID can be made for any rflow
+ * if not in use with assumption that caller knows what's doing.
+ * TI-SCI FW will perform additional permission check ant way, it's
+ * safe
+ */
+
+ if (id < 0 || id >= ud->rflow_cnt)
+ return ERR_PTR(-ENOENT);
+
+ if (test_bit(id, ud->rflow_in_use))
+ return ERR_PTR(-ENOENT);
+
+ 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);
+ return &ud->rflows[id];
+}
+EXPORT_SYMBOL_GPL(__udma_get_rflow);
+
+void __udma_put_rflow(struct udma_dev *ud, struct udma_rflow *rflow)
+{
+ if (!test_bit(rflow->id, ud->rflow_in_use)) {
+ dev_err(ud->dev, "attempt to put unused rflow%d\n", rflow->id);
+ return;
+ }
+
+ dev_dbg(ud->dev, "put rflow%d\n", rflow->id);
+ clear_bit(rflow->id, ud->rflow_in_use);
+}
+EXPORT_SYMBOL_GPL(__udma_put_rflow);
+
+#define UDMA_RESERVE_RESOURCE(res) \
+struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \
+ enum udma_tp_level tpl, \
+ 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 { \
+ int start; \
+ \
+ if (tpl >= ud->res##_tpl.levels) \
+ tpl = ud->res##_tpl.levels - 1; \
+ \
+ start = ud->res##_tpl.start_idx[tpl]; \
+ \
+ id = find_next_zero_bit(ud->res##_map, ud->res##_cnt, \
+ start); \
+ if (id == ud->res##_cnt) { \
+ return ERR_PTR(-ENOENT); \
+ } \
+ } \
+ \
+ set_bit(id, ud->res##_map); \
+ return &ud->res##s[id]; \
+}
+
+UDMA_RESERVE_RESOURCE(bchan);
+EXPORT_SYMBOL_GPL(__udma_reserve_bchan);
+UDMA_RESERVE_RESOURCE(tchan);
+EXPORT_SYMBOL_GPL(__udma_reserve_tchan);
+UDMA_RESERVE_RESOURCE(rchan);
+EXPORT_SYMBOL_GPL(__udma_reserve_rchan);
+
+int udma_get_tchan(struct udma_chan *uc)
+{
+ struct udma_dev *ud = uc->ud;
+ int ret;
+
+ if (uc->tchan) {
+ dev_dbg(ud->dev, "chan%d: already have tchan%d allocated\n",
+ uc->id, uc->tchan->id);
+ return 0;
+ }
+
+ /*
+ * 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)) {
+ ret = PTR_ERR(uc->tchan);
+ uc->tchan = NULL;
+ return ret;
+ }
+
+ 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 0;
+}
+EXPORT_SYMBOL_GPL(udma_get_tchan);
+
+int udma_get_rchan(struct udma_chan *uc)
+{
+ struct udma_dev *ud = uc->ud;
+ int ret;
+
+ if (uc->rchan) {
+ dev_dbg(ud->dev, "chan%d: already have rchan%d allocated\n",
+ uc->id, uc->rchan->id);
+ return 0;
+ }
+
+ /*
+ * 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);
+ if (IS_ERR(uc->rchan)) {
+ ret = PTR_ERR(uc->rchan);
+ uc->rchan = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_get_rchan);
+
+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);
+ /*
+ * 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))
+ 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];
+
+ /* UDMA does not use tx flows */
+ uc->tchan->tflow_id = -1;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_get_chan_pair);
+
+int udma_get_rflow(struct udma_chan *uc, int flow_id)
+{
+ struct udma_dev *ud = uc->ud;
+ int ret;
+
+ if (!uc->rchan) {
+ dev_err(ud->dev, "chan%d: does not have rchan??\n", uc->id);
+ return -EINVAL;
+ }
+
+ if (uc->rflow) {
+ dev_dbg(ud->dev, "chan%d: already have rflow%d allocated\n",
+ uc->id, uc->rflow->id);
+ return 0;
+ }
+
+ uc->rflow = __udma_get_rflow(ud, flow_id);
+ if (IS_ERR(uc->rflow)) {
+ ret = PTR_ERR(uc->rflow);
+ uc->rflow = NULL;
+ return ret;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(udma_get_rflow);
+
+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;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_put_rchan);
+
+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);
+
+ if (uc->tchan->tflow_id >= 0)
+ clear_bit(uc->tchan->tflow_id, ud->tflow_map);
+
+ uc->tchan = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_put_tchan);
+
+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);
+ __udma_put_rflow(ud, uc->rflow);
+ uc->rflow = NULL;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_put_rflow);
+
+void udma_free_tx_resources(struct udma_chan *uc)
+{
+ if (!uc->tchan)
+ return;
+
+ k3_ringacc_ring_free(uc->tchan->t_ring);
+ k3_ringacc_ring_free(uc->tchan->tc_ring);
+ uc->tchan->t_ring = NULL;
+ uc->tchan->tc_ring = NULL;
+
+ udma_put_tchan(uc);
+}
+EXPORT_SYMBOL_GPL(udma_free_tx_resources);
+
+void udma_free_rx_resources(struct udma_chan *uc)
+{
+ if (!uc->rchan)
+ return;
+
+ if (uc->rflow) {
+ struct udma_rflow *rflow = uc->rflow;
+
+ k3_ringacc_ring_free(rflow->fd_ring);
+ k3_ringacc_ring_free(rflow->r_ring);
+ rflow->fd_ring = NULL;
+ rflow->r_ring = NULL;
+
+ udma_put_rflow(uc);
+ }
+
+ udma_put_rchan(uc);
+}
+EXPORT_SYMBOL_GPL(udma_free_rx_resources);
+
+void udma_free_chan_resources(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ struct udma_dev *ud = to_udma_dev(chan->device);
+
+ udma_terminate_all(chan);
+ if (uc->terminated_desc) {
+ ud->reset_chan(uc, false);
+ udma_reset_rings(uc);
+ }
+
+ cancel_delayed_work_sync(&uc->tx_drain.work);
+
+ if (uc->irq_num_ring > 0) {
+ free_irq(uc->irq_num_ring, uc);
+
+ uc->irq_num_ring = 0;
+ }
+ if (uc->irq_num_udma > 0) {
+ free_irq(uc->irq_num_udma, uc);
+
+ uc->irq_num_udma = 0;
+ }
+
+ /* Release PSI-L pairing */
+ if (uc->psil_paired && ud->psil_unpair) {
+ ud->psil_unpair(ud, uc->config.src_thread,
+ uc->config.dst_thread);
+ uc->psil_paired = false;
+ }
+
+ 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);
+
+ if (uc->use_dma_pool) {
+ dma_pool_destroy(uc->hdesc_pool);
+ uc->use_dma_pool = false;
+ }
+}
+EXPORT_SYMBOL_GPL(udma_free_chan_resources);
+
+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;
+ }
+}
+EXPORT_SYMBOL_GPL(bcdma_put_bchan);
+
+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);
+}
+EXPORT_SYMBOL_GPL(bcdma_free_bchan_resources);
+
MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index e86c811a15eb9..fa9a464f4b953 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -423,135 +423,6 @@ static irqreturn_t udma_udma_irq_handler(int irq, void *data)
return IRQ_HANDLED;
}
-/**
- * __udma_alloc_gp_rflow_range - alloc range of GP RX flows
- * @ud: UDMA device
- * @from: Start the search from this flow id number
- * @cnt: Number of consecutive flow ids to allocate
- *
- * Allocate range of RX flow ids for future use, those flows can be requested
- * only using explicit flow id number. if @from is set to -1 it will try to find
- * first free range. if @from is positive value it will force allocation only
- * of the specified range of flows.
- *
- * Returns -ENOMEM if can't find free range.
- * -EEXIST if requested range is busy.
- * -EINVAL if wrong input values passed.
- * Returns flow id on success.
- */
-static int __udma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
-{
- int start, tmp_from;
- DECLARE_BITMAP(tmp, K3_UDMA_MAX_RFLOWS);
-
- tmp_from = from;
- if (tmp_from < 0)
- tmp_from = ud->rchan_cnt;
- /* default flows can't be allocated and accessible only by id */
- if (tmp_from < ud->rchan_cnt)
- return -EINVAL;
-
- if (tmp_from + cnt > ud->rflow_cnt)
- return -EINVAL;
-
- bitmap_or(tmp, ud->rflow_gp_map, ud->rflow_gp_map_allocated,
- ud->rflow_cnt);
-
- start = bitmap_find_next_zero_area(tmp,
- ud->rflow_cnt,
- tmp_from, cnt, 0);
- if (start >= ud->rflow_cnt)
- return -ENOMEM;
-
- if (from >= 0 && start != from)
- return -EEXIST;
-
- bitmap_set(ud->rflow_gp_map_allocated, start, cnt);
- return start;
-}
-
-static int __udma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt)
-{
- if (from < ud->rchan_cnt)
- return -EINVAL;
- if (from + cnt > ud->rflow_cnt)
- return -EINVAL;
-
- bitmap_clear(ud->rflow_gp_map_allocated, from, cnt);
- return 0;
-}
-
-static struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id)
-{
- /*
- * Attempt to request rflow by ID can be made for any rflow
- * if not in use with assumption that caller knows what's doing.
- * TI-SCI FW will perform additional permission check ant way, it's
- * safe
- */
-
- if (id < 0 || id >= ud->rflow_cnt)
- return ERR_PTR(-ENOENT);
-
- if (test_bit(id, ud->rflow_in_use))
- return ERR_PTR(-ENOENT);
-
- 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);
- return &ud->rflows[id];
-}
-
-static void __udma_put_rflow(struct udma_dev *ud, struct udma_rflow *rflow)
-{
- if (!test_bit(rflow->id, ud->rflow_in_use)) {
- dev_err(ud->dev, "attempt to put unused rflow%d\n", rflow->id);
- return;
- }
-
- dev_dbg(ud->dev, "put rflow%d\n", rflow->id);
- clear_bit(rflow->id, ud->rflow_in_use);
-}
-
-#define UDMA_RESERVE_RESOURCE(res) \
-static struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \
- enum udma_tp_level tpl, \
- 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 { \
- int start; \
- \
- if (tpl >= ud->res##_tpl.levels) \
- tpl = ud->res##_tpl.levels - 1; \
- \
- start = ud->res##_tpl.start_idx[tpl]; \
- \
- id = find_next_zero_bit(ud->res##_map, ud->res##_cnt, \
- start); \
- if (id == ud->res##_cnt) { \
- return ERR_PTR(-ENOENT); \
- } \
- } \
- \
- set_bit(id, ud->res##_map); \
- 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;
@@ -585,223 +456,6 @@ static int bcdma_get_bchan(struct udma_chan *uc)
return 0;
}
-static int udma_get_tchan(struct udma_chan *uc)
-{
- struct udma_dev *ud = uc->ud;
- int ret;
-
- if (uc->tchan) {
- dev_dbg(ud->dev, "chan%d: already have tchan%d allocated\n",
- uc->id, uc->tchan->id);
- return 0;
- }
-
- /*
- * 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)) {
- ret = PTR_ERR(uc->tchan);
- uc->tchan = NULL;
- return ret;
- }
-
- 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 0;
-}
-
-static int udma_get_rchan(struct udma_chan *uc)
-{
- struct udma_dev *ud = uc->ud;
- int ret;
-
- if (uc->rchan) {
- dev_dbg(ud->dev, "chan%d: already have rchan%d allocated\n",
- uc->id, uc->rchan->id);
- return 0;
- }
-
- /*
- * 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);
- if (IS_ERR(uc->rchan)) {
- ret = PTR_ERR(uc->rchan);
- uc->rchan = NULL;
- return ret;
- }
-
- 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);
- /*
- * 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))
- 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];
-
- /* UDMA does not use tx flows */
- uc->tchan->tflow_id = -1;
-
- return 0;
-}
-
-static int udma_get_rflow(struct udma_chan *uc, int flow_id)
-{
- struct udma_dev *ud = uc->ud;
- int ret;
-
- if (!uc->rchan) {
- dev_err(ud->dev, "chan%d: does not have rchan??\n", uc->id);
- return -EINVAL;
- }
-
- if (uc->rflow) {
- dev_dbg(ud->dev, "chan%d: already have rflow%d allocated\n",
- uc->id, uc->rflow->id);
- return 0;
- }
-
- uc->rflow = __udma_get_rflow(ud, flow_id);
- if (IS_ERR(uc->rflow)) {
- ret = PTR_ERR(uc->rflow);
- uc->rflow = NULL;
- return ret;
- }
-
- return 0;
-}
-
-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;
-
- 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);
-
- if (uc->tchan->tflow_id >= 0)
- clear_bit(uc->tchan->tflow_id, ud->tflow_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);
- __udma_put_rflow(ud, uc->rflow);
- uc->rflow = NULL;
- }
-}
-
-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;
@@ -847,19 +501,6 @@ static int bcdma_alloc_bchan_resources(struct udma_chan *uc)
return ret;
}
-static void udma_free_tx_resources(struct udma_chan *uc)
-{
- if (!uc->tchan)
- return;
-
- k3_ringacc_ring_free(uc->tchan->t_ring);
- k3_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_ring_cfg ring_cfg;
@@ -917,25 +558,6 @@ static int udma_alloc_tx_resources(struct udma_chan *uc)
return ret;
}
-static void udma_free_rx_resources(struct udma_chan *uc)
-{
- if (!uc->rchan)
- return;
-
- if (uc->rflow) {
- struct udma_rflow *rflow = uc->rflow;
-
- k3_ringacc_ring_free(rflow->fd_ring);
- k3_ringacc_ring_free(rflow->r_ring);
- rflow->fd_ring = NULL;
- rflow->r_ring = NULL;
-
- udma_put_rflow(uc);
- }
-
- udma_put_rchan(uc);
-}
-
static int udma_alloc_rx_resources(struct udma_chan *uc)
{
struct udma_dev *ud = uc->ud;
@@ -2024,51 +1646,6 @@ static int udma_resume(struct dma_chan *chan)
return 0;
}
-static void udma_free_chan_resources(struct dma_chan *chan)
-{
- struct udma_chan *uc = to_udma_chan(chan);
- struct udma_dev *ud = to_udma_dev(chan->device);
-
- udma_terminate_all(chan);
- if (uc->terminated_desc) {
- ud->reset_chan(uc, false);
- udma_reset_rings(uc);
- }
-
- cancel_delayed_work_sync(&uc->tx_drain.work);
-
- if (uc->irq_num_ring > 0) {
- free_irq(uc->irq_num_ring, uc);
-
- uc->irq_num_ring = 0;
- }
- if (uc->irq_num_udma > 0) {
- free_irq(uc->irq_num_udma, uc);
-
- uc->irq_num_udma = 0;
- }
-
- /* Release PSI-L pairing */
- if (uc->psil_paired) {
- navss_psil_unpair(ud, uc->config.src_thread,
- uc->config.dst_thread);
- uc->psil_paired = false;
- }
-
- 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);
-
- if (uc->use_dma_pool) {
- dma_pool_destroy(uc->hdesc_pool);
- uc->use_dma_pool = false;
- }
-}
-
static struct platform_driver udma_driver;
static struct platform_driver bcdma_driver;
static struct platform_driver pktdma_driver;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 797e8b0c5b85e..e4b512d9ffb2e 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -654,6 +654,27 @@ void udma_dbg_summary_show(struct seq_file *s,
struct dma_device *dma_dev);
#endif /* CONFIG_DEBUG_FS */
+int __udma_alloc_gp_rflow_range(struct udma_dev *ud, int from, int cnt);
+int __udma_free_gp_rflow_range(struct udma_dev *ud, int from, int cnt);
+struct udma_rflow *__udma_get_rflow(struct udma_dev *ud, int id);
+void __udma_put_rflow(struct udma_dev *ud, struct udma_rflow *rflow);
+int udma_get_tchan(struct udma_chan *uc);
+int udma_get_rchan(struct udma_chan *uc);
+int udma_get_chan_pair(struct udma_chan *uc);
+int udma_get_rflow(struct udma_chan *uc, int flow_id);
+void udma_put_rchan(struct udma_chan *uc);
+void udma_put_tchan(struct udma_chan *uc);
+void udma_put_rflow(struct udma_chan *uc);
+void udma_free_tx_resources(struct udma_chan *uc);
+void udma_free_rx_resources(struct udma_chan *uc);
+void udma_free_chan_resources(struct dma_chan *chan);
+void bcdma_put_bchan(struct udma_chan *uc);
+void bcdma_free_bchan_resources(struct udma_chan *uc);
+
+struct udma_bchan *__udma_reserve_bchan(struct udma_dev *ud, enum udma_tp_level tpl, int id);
+struct udma_tchan *__udma_reserve_tchan(struct udma_dev *ud, enum udma_tp_level tpl, int id);
+struct udma_rchan *__udma_reserve_rchan(struct udma_dev *ud, enum udma_tp_level tpl, int id);
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 09/19] dmaengine: ti: k3-udma: refactor resource setup functions
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (7 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 08/19] dmaengine: ti: k3-udma: move resource management " Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 10/19] dmaengine: ti: k3-udma: move inclusion of k3-udma-private.c to k3-udma-common.c Sai Sree Kartheek Adivi
` (10 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
The implementation of setup_resources, bcdma_setup_resources and
pktdma_setup_resources is largely shared between all K3 UDMA variants
with the only major difference being SCI resources setup. So,
- Move the functions to k3-udma-common.c.
- Split SCI resource setup for bcdma and pktdma into separate functions
in variant specific driver (k3-udma.c).
- Add function pointers for setup_sci_resources in udma_dev and call
them as part of the actual resource setup implementations in
k3-udma-common.c to retain the existing functionality.
- Also since setup_resources call udma_setup_resources which is not
required for all K3 UDMA variants, Add a function pointer in udma_dev
and use that to call udma_setup_resources.
This refactor improves code reuse and maintainability across multiple
variants.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 204 ++++++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.c | 182 +---------------------------
drivers/dma/ti/k3-udma.h | 8 ++
3 files changed, 218 insertions(+), 176 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 882d27b3c9ee5..b419b23c401a1 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -8,7 +8,9 @@
#include <linux/dmaengine.h>
#include <linux/dma-mapping.h>
#include <linux/dmapool.h>
+#include <linux/of.h>
#include <linux/platform_device.h>
+#include <linux/soc/ti/ti_sci_inta_msi.h>
#include <linux/soc/ti/k3-ringacc.h>
#include "k3-udma.h"
@@ -2333,5 +2335,207 @@ void bcdma_free_bchan_resources(struct udma_chan *uc)
}
EXPORT_SYMBOL_GPL(bcdma_free_bchan_resources);
+int bcdma_setup_resources(struct udma_dev *ud)
+{
+ int ret;
+ struct device *dev = ud->dev;
+ 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;
+
+ if (ud->bcdma_setup_sci_resources) {
+ ret = ud->bcdma_setup_sci_resources(ud);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+int pktdma_setup_resources(struct udma_dev *ud)
+{
+ int ret;
+ struct device *dev = ud->dev;
+ 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->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),
+ 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;
+
+ if (ud->pktdma_setup_sci_resources) {
+ ret = ud->pktdma_setup_sci_resources(ud);
+ if (ret)
+ return ret;
+ }
+
+ return 0;
+}
+
+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 = ud->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)
+ return -ENODEV;
+
+ ud->channels = devm_kcalloc(dev, ch_count, sizeof(*ud->channels),
+ GFP_KERNEL);
+ if (!ud->channels)
+ return -ENOMEM;
+
+ 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));
+ break;
+ default:
+ break;
+ }
+
+ return ch_count;
+}
+EXPORT_SYMBOL_GPL(setup_resources);
+
MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index fa9a464f4b953..83cf3d01f67fb 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -2083,7 +2083,7 @@ static const char * const range_names[] = {
[RM_RANGE_TFLOW] = "ti,sci-rm-range-tflow",
};
-static int udma_setup_resources(struct udma_dev *ud)
+int udma_setup_resources(struct udma_dev *ud)
{
int ret, i, j;
struct device *dev = ud->dev;
@@ -2245,74 +2245,13 @@ static int udma_setup_resources(struct udma_dev *ud)
return 0;
}
-static int bcdma_setup_resources(struct udma_dev *ud)
+static int bcdma_setup_sci_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++) {
@@ -2476,51 +2415,13 @@ static int bcdma_setup_resources(struct udma_dev *ud)
return 0;
}
-static int pktdma_setup_resources(struct udma_dev *ud)
+static int pktdma_setup_sci_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->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),
- 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++) {
@@ -2631,80 +2532,6 @@ static int pktdma_setup_resources(struct udma_dev *ud)
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)
- return -ENODEV;
-
- ud->channels = devm_kcalloc(dev, ch_count, sizeof(*ud->channels),
- GFP_KERNEL);
- if (!ud->channels)
- return -ENOMEM;
-
- 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));
- break;
- default:
- break;
- }
-
- return ch_count;
-}
-
static int udma_probe(struct platform_device *pdev)
{
struct device_node *navss_node = pdev->dev.parent->of_node;
@@ -2747,6 +2574,9 @@ static int udma_probe(struct platform_device *pdev)
ud->decrement_byte_counters = udma_decrement_byte_counters;
ud->psil_pair = navss_psil_pair;
ud->psil_unpair = navss_psil_unpair;
+ ud->udma_setup_resources = udma_setup_resources;
+ ud->bcdma_setup_sci_resources = bcdma_setup_sci_resources;
+ ud->pktdma_setup_sci_resources = pktdma_setup_sci_resources;
ret = udma_get_mmrs(pdev, ud);
if (ret)
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index e4b512d9ffb2e..6a95eb1ece064 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -353,6 +353,9 @@ struct udma_dev {
u32 dst_thread);
int (*psil_unpair)(struct udma_dev *ud, u32 src_thread,
u32 dst_thread);
+ int (*udma_setup_resources)(struct udma_dev *ud);
+ int (*bcdma_setup_sci_resources)(struct udma_dev *ud);
+ int (*pktdma_setup_sci_resources)(struct udma_dev *ud);
};
struct udma_desc {
@@ -675,6 +678,11 @@ struct udma_bchan *__udma_reserve_bchan(struct udma_dev *ud, enum udma_tp_level
struct udma_tchan *__udma_reserve_tchan(struct udma_dev *ud, enum udma_tp_level tpl, int id);
struct udma_rchan *__udma_reserve_rchan(struct udma_dev *ud, enum udma_tp_level tpl, int id);
+int udma_setup_resources(struct udma_dev *ud);
+int bcdma_setup_resources(struct udma_dev *ud);
+int pktdma_setup_resources(struct udma_dev *ud);
+int setup_resources(struct udma_dev *ud);
+
/* Direct access to UDMA low lever resources for the glue layer */
int xudma_navss_psil_pair(struct udma_dev *ud, u32 src_thread, u32 dst_thread);
int xudma_navss_psil_unpair(struct udma_dev *ud, u32 src_thread,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 10/19] dmaengine: ti: k3-udma: move inclusion of k3-udma-private.c to k3-udma-common.c
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (8 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 09/19] dmaengine: ti: k3-udma: refactor resource setup functions Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci Sai Sree Kartheek Adivi
` (9 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Relocate the #include directive for k3-udma-private.c to
k3-udma-common.c so that the code can be shared between other UDMA
variants (like k3-udma-v2). This change improves modularity and prepares
for variant-specific implementations.
No functional changes intended.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 3 +++
drivers/dma/ti/k3-udma.c | 2 --
2 files changed, 3 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index b419b23c401a1..0ffc6becc402e 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -2539,3 +2539,6 @@ EXPORT_SYMBOL_GPL(setup_resources);
MODULE_DESCRIPTION("Texas Instruments K3 UDMA Common Library");
MODULE_LICENSE("GPL v2");
+
+/* Private interfaces to UDMA */
+#include "k3-udma-private.c"
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index 83cf3d01f67fb..a8d01d955651a 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -2857,5 +2857,3 @@ module_platform_driver(udma_driver);
MODULE_DESCRIPTION("Texas Instruments UDMA support");
MODULE_LICENSE("GPL v2");
-/* Private interfaces to UDMA */
-#include "k3-udma-private.c"
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (9 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 10/19] dmaengine: ti: k3-udma: move inclusion of k3-udma-private.c to k3-udma-common.c Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 18:40 ` kernel test robot
` (2 more replies)
2026-01-30 11:01 ` [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2 Sai Sree Kartheek Adivi
` (8 subsequent siblings)
19 siblings, 3 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Handle absence of tisci with direct register writes. This will support
platforms that do not have tisci firmware like AM62L.
Remove TI_SCI_INTA_IRQCHIP dependency for TI_K3_RINGACC in Kconfig as
it is required conditionally (only for devices that has TI_SCI).
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/soc/ti/Kconfig | 1 -
drivers/soc/ti/k3-ringacc.c | 188 ++++++++++++++++++++++++++----
include/linux/soc/ti/k3-ringacc.h | 17 +++
3 files changed, 181 insertions(+), 25 deletions(-)
diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
index 163aadd589d3a..5499c0def527a 100644
--- a/drivers/soc/ti/Kconfig
+++ b/drivers/soc/ti/Kconfig
@@ -53,7 +53,6 @@ config WKUP_M3_IPC
config TI_K3_RINGACC
tristate "K3 Ring accelerator Sub System"
depends on ARCH_K3 || COMPILE_TEST
- depends on TI_SCI_INTA_IRQCHIP
help
Say y here to support the K3 Ring accelerator module.
The Ring Accelerator (RINGACC or RA) provides hardware acceleration
diff --git a/drivers/soc/ti/k3-ringacc.c b/drivers/soc/ti/k3-ringacc.c
index 7602b8a909b05..fd7c960a3fa2a 100644
--- a/drivers/soc/ti/k3-ringacc.c
+++ b/drivers/soc/ti/k3-ringacc.c
@@ -45,6 +45,53 @@ struct k3_ring_rt_regs {
u32 hwindx;
};
+#define K3_RINGACC_RT_CFG_REGS_OFS 0x40
+#define K3_DMARING_CFG_ADDR_HI_MASK GENMASK(3, 0)
+#define K3_DMARING_CFG_ASEL_SHIFT 16
+#define K3_DMARING_CFG_SIZE_MASK GENMASK(15, 0)
+
+/**
+ * struct k3_ring_cfg_regs - The RA Configuration Registers region
+ *
+ * @ba_lo: Ring Base Address Low Register
+ * @ba_hi: Ring Base Address High Register
+ * @size: Ring Size Register
+ */
+struct k3_ring_cfg_regs {
+ u32 ba_lo;
+ u32 ba_hi;
+ u32 size;
+};
+
+#define K3_RINGACC_RT_INT_REGS_OFS 0x140
+#define K3_RINGACC_RT_INT_ENABLE_SET_COMPLETE BIT(0)
+#define K3_RINGACC_RT_INT_ENABLE_SET_TR BIT(2)
+
+/**
+ * struct k3_ring_intr_regs {
+ *
+ * @enable_set: Ring Interrupt Enable Register
+ * @resv_1: Reserved
+ * @clr: Ring Interrupt Clear Register
+ * @resv_2: Reserved
+ * @status_set: Ring Interrupt Status Set Register
+ * @resv_3: Reserved
+ * @status: Ring Interrupt Status Register
+ * @resv_4: Reserved
+ * @status_masked: Ring Interrupt Status Masked Register
+ */
+struct k3_ring_intr_regs {
+ u32 enable_set;
+ u32 resv_1;
+ u32 clr;
+ u32 resv_2;
+ u32 status_set;
+ u32 resv_3;
+ u32 status;
+ u32 resv_4;
+ u32 status_masked;
+};
+
#define K3_RINGACC_RT_REGS_STEP 0x1000
#define K3_DMARING_RT_REGS_STEP 0x2000
#define K3_DMARING_RT_REGS_REVERSE_OFS 0x1000
@@ -138,6 +185,8 @@ struct k3_ring_state {
* struct k3_ring - RA Ring descriptor
*
* @rt: Ring control/status registers
+ * @cfg: Ring config registers
+ * @intr: Ring interrupt registers
* @fifos: Ring queues registers
* @proxy: Ring Proxy Datapath registers
* @ring_mem_dma: Ring buffer dma address
@@ -157,6 +206,8 @@ struct k3_ring_state {
*/
struct k3_ring {
struct k3_ring_rt_regs __iomem *rt;
+ struct k3_ring_cfg_regs __iomem *cfg;
+ struct k3_ring_intr_regs __iomem *intr;
struct k3_ring_fifo_regs __iomem *fifos;
struct k3_ringacc_proxy_target_regs __iomem *proxy;
dma_addr_t ring_mem_dma;
@@ -466,15 +517,31 @@ static void k3_ringacc_ring_reset_sci(struct k3_ring *ring)
struct k3_ringacc *ringacc = ring->parent;
int ret;
- ring_cfg.nav_id = ringacc->tisci_dev_id;
- ring_cfg.index = ring->ring_id;
- ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID;
- ring_cfg.count = ring->size;
+ if (!ringacc->tisci) {
+ u32 reg;
- ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
- if (ret)
- dev_err(ringacc->dev, "TISCI reset ring fail (%d) ring_idx %d\n",
- ret, ring->ring_id);
+ if (!ring->cfg)
+ return;
+
+ reg = readl(&ring->cfg->size);
+ reg &= ~K3_DMARING_CFG_SIZE_MASK;
+ writel(reg, &ring->cfg->size);
+
+ /* Ensure the register clear operation completes before writing new value */
+ wmb();
+ reg |= ring->size;
+ writel(reg, &ring->cfg->size);
+ } else {
+ ring_cfg.nav_id = ringacc->tisci_dev_id;
+ ring_cfg.index = ring->ring_id;
+ ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_RING_COUNT_VALID;
+ ring_cfg.count = ring->size;
+
+ ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
+ if (ret)
+ dev_err(ringacc->dev, "TISCI reset ring fail (%d) ring_idx %d\n",
+ ret, ring->ring_id);
+ }
}
void k3_ringacc_ring_reset(struct k3_ring *ring)
@@ -500,10 +567,25 @@ static void k3_ringacc_ring_reconfig_qmode_sci(struct k3_ring *ring,
ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_RING_MODE_VALID;
ring_cfg.mode = mode;
- ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
- if (ret)
- dev_err(ringacc->dev, "TISCI reconf qmode fail (%d) ring_idx %d\n",
- ret, ring->ring_id);
+ if (!ringacc->tisci) {
+ u32 reg;
+
+ writel(ring_cfg.addr_lo, &ring->cfg->ba_lo);
+ writel((ring_cfg.addr_hi & K3_DMARING_CFG_ADDR_HI_MASK) +
+ (ring_cfg.asel << K3_DMARING_CFG_ASEL_SHIFT),
+ &ring->cfg->ba_hi);
+
+ reg = readl(&ring->cfg->size);
+ reg &= ~K3_DMARING_CFG_SIZE_MASK;
+ reg |= ring_cfg.count & K3_DMARING_CFG_SIZE_MASK;
+
+ writel(reg, &ring->cfg->size);
+ } else {
+ ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
+ if (ret)
+ dev_err(ringacc->dev, "TISCI reconf qmode fail (%d) ring_idx %d\n",
+ ret, ring->ring_id);
+ }
}
void k3_ringacc_ring_reset_dma(struct k3_ring *ring, u32 occ)
@@ -575,10 +657,25 @@ static void k3_ringacc_ring_free_sci(struct k3_ring *ring)
ring_cfg.index = ring->ring_id;
ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER;
- ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
- if (ret)
- dev_err(ringacc->dev, "TISCI ring free fail (%d) ring_idx %d\n",
- ret, ring->ring_id);
+ if (!ringacc->tisci) {
+ u32 reg;
+
+ writel(ring_cfg.addr_lo, &ring->cfg->ba_lo);
+ writel((ring_cfg.addr_hi & K3_DMARING_CFG_ADDR_HI_MASK) +
+ (ring_cfg.asel << K3_DMARING_CFG_ASEL_SHIFT),
+ &ring->cfg->ba_hi);
+
+ reg = readl(&ring->cfg->size);
+ reg &= ~K3_DMARING_CFG_SIZE_MASK;
+ reg |= ring_cfg.count & K3_DMARING_CFG_SIZE_MASK;
+
+ writel(reg, &ring->cfg->size);
+ } else {
+ ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
+ if (ret)
+ dev_err(ringacc->dev, "TISCI ring free fail (%d) ring_idx %d\n",
+ ret, ring->ring_id);
+ }
}
int k3_ringacc_ring_free(struct k3_ring *ring)
@@ -669,15 +766,30 @@ int k3_ringacc_get_ring_irq_num(struct k3_ring *ring)
}
EXPORT_SYMBOL_GPL(k3_ringacc_get_ring_irq_num);
+u32 k3_ringacc_ring_get_irq_status(struct k3_ring *ring)
+{
+ struct k3_ringacc *ringacc = ring->parent;
+ struct k3_ring *ring2 = &ringacc->rings[ring->ring_id];
+
+ return readl(&ring2->intr->status);
+}
+EXPORT_SYMBOL_GPL(k3_ringacc_ring_get_irq_status);
+
+void k3_ringacc_ring_clear_irq(struct k3_ring *ring)
+{
+ struct k3_ringacc *ringacc = ring->parent;
+ struct k3_ring *ring2 = &ringacc->rings[ring->ring_id];
+
+ writel(0xFF, &ring2->intr->status);
+}
+EXPORT_SYMBOL_GPL(k3_ringacc_ring_clear_irq);
+
static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring)
{
struct ti_sci_msg_rm_ring_cfg ring_cfg = { 0 };
struct k3_ringacc *ringacc = ring->parent;
int ret;
- if (!ringacc->tisci)
- return -EINVAL;
-
ring_cfg.nav_id = ringacc->tisci_dev_id;
ring_cfg.index = ring->ring_id;
ring_cfg.valid_params = TI_SCI_MSG_VALUE_RM_ALL_NO_ORDER;
@@ -688,6 +800,24 @@ static int k3_ringacc_ring_cfg_sci(struct k3_ring *ring)
ring_cfg.size = ring->elm_size;
ring_cfg.asel = ring->asel;
+ if (!ringacc->tisci) {
+ u32 reg;
+
+ writel(ring_cfg.addr_lo, &ring->cfg->ba_lo);
+ writel((ring_cfg.addr_hi & K3_DMARING_CFG_ADDR_HI_MASK) +
+ (ring_cfg.asel << K3_DMARING_CFG_ASEL_SHIFT),
+ &ring->cfg->ba_hi);
+
+ reg = readl(&ring->cfg->size);
+ reg &= ~K3_DMARING_CFG_SIZE_MASK;
+ reg |= ring_cfg.count & K3_DMARING_CFG_SIZE_MASK;
+
+ writel(reg, &ring->cfg->size);
+ writel(K3_RINGACC_RT_INT_ENABLE_SET_COMPLETE | K3_RINGACC_RT_INT_ENABLE_SET_TR,
+ &ring->intr->enable_set);
+ return 0;
+ }
+
ret = ringacc->tisci_ring_ops->set_cfg(ringacc->tisci, &ring_cfg);
if (ret)
dev_err(ringacc->dev, "TISCI config ring fail (%d) ring_idx %d\n",
@@ -1346,8 +1476,11 @@ static int k3_ringacc_probe_dt(struct k3_ringacc *ringacc)
return PTR_ERR(ringacc->rm_gp_range);
}
- return ti_sci_inta_msi_domain_alloc_irqs(ringacc->dev,
- ringacc->rm_gp_range);
+ if (IS_ENABLED(CONFIG_TI_K3_UDMA))
+ return ti_sci_inta_msi_domain_alloc_irqs(ringacc->dev,
+ ringacc->rm_gp_range);
+ else
+ return 0;
}
static const struct k3_ringacc_soc_data k3_ringacc_soc_data_sr1 = {
@@ -1480,9 +1613,12 @@ struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev,
mutex_init(&ringacc->req_lock);
- base_rt = devm_platform_ioremap_resource_byname(pdev, "ringrt");
- if (IS_ERR(base_rt))
- return ERR_CAST(base_rt);
+ base_rt = data->base_rt;
+ if (!base_rt) {
+ base_rt = devm_platform_ioremap_resource_byname(pdev, "ringrt");
+ if (IS_ERR(base_rt))
+ return ERR_CAST(base_rt);
+ }
ringacc->rings = devm_kzalloc(dev,
sizeof(*ringacc->rings) *
@@ -1498,6 +1634,10 @@ struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev,
struct k3_ring *ring = &ringacc->rings[i];
ring->rt = base_rt + K3_DMARING_RT_REGS_STEP * i;
+ ring->cfg = base_rt + K3_RINGACC_RT_CFG_REGS_OFS +
+ K3_DMARING_RT_REGS_STEP * i;
+ ring->intr = base_rt + K3_RINGACC_RT_INT_REGS_OFS +
+ K3_DMARING_RT_REGS_STEP * i;
ring->parent = ringacc;
ring->ring_id = i;
ring->proxy_id = K3_RINGACC_PROXY_NOT_USED;
diff --git a/include/linux/soc/ti/k3-ringacc.h b/include/linux/soc/ti/k3-ringacc.h
index 39b022b925986..9f2d141c988bd 100644
--- a/include/linux/soc/ti/k3-ringacc.h
+++ b/include/linux/soc/ti/k3-ringacc.h
@@ -158,6 +158,22 @@ u32 k3_ringacc_get_ring_id(struct k3_ring *ring);
*/
int k3_ringacc_get_ring_irq_num(struct k3_ring *ring);
+/**
+ * k3_ringacc_ring_get_irq_status - Get the irq status for the ring
+ * @ring: pointer on ring
+ *
+ * Returns the interrupt status
+ */
+u32 k3_ringacc_ring_get_irq_status(struct k3_ring *ring);
+
+/**
+ * k3_ringacc_ring_clear_irq - Clear all interrupts
+ * @ring: pointer on ring
+ *
+ * Clears all the interrupts on the ring
+ */
+void k3_ringacc_ring_clear_irq(struct k3_ring *ring);
+
/**
* k3_ringacc_ring_cfg - ring configure
* @ring: pointer on ring
@@ -262,6 +278,7 @@ struct k3_ringacc_init_data {
const struct ti_sci_handle *tisci;
u32 tisci_dev_id;
u32 num_rings;
+ void __iomem *base_rt;
};
struct k3_ringacc *k3_ringacc_dmarings_init(struct platform_device *pdev,
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
2026-01-30 11:01 ` [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci Sai Sree Kartheek Adivi
@ 2026-01-30 18:40 ` kernel test robot
2026-01-30 18:40 ` kernel test robot
2026-02-03 6:05 ` Péter Ujfalusi
2 siblings, 0 replies; 45+ messages in thread
From: kernel test robot @ 2026-01-30 18:40 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul, robh, krzk+dt,
conor+dt, nm, ssantosh, dmaengine, devicetree, linux-kernel,
linux-arm-kernel, vigneshr
Cc: llvm, oe-kbuild-all, r-sharma3, gehariprasath
Hi Sai,
kernel test robot noticed the following build errors:
[auto build test ERROR on vkoul-dmaengine/next]
[also build test ERROR on next-20260129]
[cannot apply to linus/master v6.19-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sai-Sree-Kartheek-Adivi/dmaengine-ti-k3-udma-move-macros-to-header-file/20260130-191306
base: https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
patch link: https://lore.kernel.org/r/20260130110159.359501-12-s-adivi%40ti.com
patch subject: [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
config: arm-randconfig-001-20260131 (https://download.01.org/0day-ci/archive/20260131/202601310206.DLRWFXou-lkp@intel.com/config)
compiler: clang version 22.0.0git (https://github.com/llvm/llvm-project 9b8addffa70cee5b2acc5454712d9cf78ce45710)
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260131/202601310206.DLRWFXou-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601310206.DLRWFXou-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
In file included from drivers/soc/ti/k3-ringacc.c:17:
>> include/linux/soc/ti/ti_sci_inta_msi.h:17:15: warning: declaration of 'struct msi_domain_info' will not be visible outside of this function [-Wvisibility]
17 | struct msi_domain_info *info,
| ^
>> drivers/soc/ti/k3-ringacc.c:1506:11: error: no member named 'domain' in 'struct dev_msi_info'
1506 | dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
| ~~~~~~~~ ^
drivers/soc/ti/k3-ringacc.c:1508:16: error: no member named 'domain' in 'struct dev_msi_info'
1508 | if (!dev->msi.domain)
| ~~~~~~~~ ^
1 warning and 2 errors generated.
Kconfig warnings: (for reference only)
WARNING: unmet direct dependencies detected for CAN_DEV
Depends on [n]: NETDEVICES [=n] && CAN [=m]
Selected by [m]:
- CAN [=m] && NET [=y]
vim +1506 drivers/soc/ti/k3-ringacc.c
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1497
40a2a7c395cf5d9 Grygorii Strashko 2020-07-24 1498 static int k3_ringacc_init(struct platform_device *pdev,
40a2a7c395cf5d9 Grygorii Strashko 2020-07-24 1499 struct k3_ringacc *ringacc)
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1500 {
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1501 const struct soc_device_attribute *soc;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1502 void __iomem *base_fifo, *base_rt;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1503 struct device *dev = &pdev->dev;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1504 int ret, i;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1505
34fff62827b254f Thomas Gleixner 2021-12-10 @1506 dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1507 DOMAIN_BUS_TI_SCI_INTA_MSI);
e50a76355c1d858 Jayesh Choudhary 2023-07-28 1508 if (!dev->msi.domain)
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1509 return -EPROBE_DEFER;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1510
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1511 ret = k3_ringacc_probe_dt(ringacc);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1512 if (ret)
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1513 return ret;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1514
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1515 soc = soc_device_match(k3_ringacc_socinfo);
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1516 if (soc && soc->data) {
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1517 const struct k3_ringacc_soc_data *soc_data = soc->data;
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1518
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1519 ringacc->dma_ring_reset_quirk = soc_data->dma_ring_reset_quirk;
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1520 }
95e7be062aea6d2 Grygorii Strashko 2020-09-11 1521
f9dbb99748bab00 Zhang Zekun 2023-08-09 1522 base_rt = devm_platform_ioremap_resource_byname(pdev, "rt");
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1523 if (IS_ERR(base_rt))
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1524 return PTR_ERR(base_rt);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1525
f9dbb99748bab00 Zhang Zekun 2023-08-09 1526 base_fifo = devm_platform_ioremap_resource_byname(pdev, "fifos");
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1527 if (IS_ERR(base_fifo))
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1528 return PTR_ERR(base_fifo);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1529
f9dbb99748bab00 Zhang Zekun 2023-08-09 1530 ringacc->proxy_gcfg = devm_platform_ioremap_resource_byname(pdev, "proxy_gcfg");
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1531 if (IS_ERR(ringacc->proxy_gcfg))
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1532 return PTR_ERR(ringacc->proxy_gcfg);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1533
f9dbb99748bab00 Zhang Zekun 2023-08-09 1534 ringacc->proxy_target_base = devm_platform_ioremap_resource_byname(pdev,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1535 "proxy_target");
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1536 if (IS_ERR(ringacc->proxy_target_base))
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1537 return PTR_ERR(ringacc->proxy_target_base);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1538
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1539 ringacc->num_proxies = readl(&ringacc->proxy_gcfg->config) &
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1540 K3_RINGACC_PROXY_CFG_THREADS_MASK;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1541
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1542 ringacc->rings = devm_kzalloc(dev,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1543 sizeof(*ringacc->rings) *
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1544 ringacc->num_rings,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1545 GFP_KERNEL);
a8eba8dde5fbf0b Christophe JAILLET 2021-12-23 1546 ringacc->rings_inuse = devm_bitmap_zalloc(dev, ringacc->num_rings,
a8eba8dde5fbf0b Christophe JAILLET 2021-12-23 1547 GFP_KERNEL);
a8eba8dde5fbf0b Christophe JAILLET 2021-12-23 1548 ringacc->proxy_inuse = devm_bitmap_zalloc(dev, ringacc->num_proxies,
a8eba8dde5fbf0b Christophe JAILLET 2021-12-23 1549 GFP_KERNEL);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1550
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1551 if (!ringacc->rings || !ringacc->rings_inuse || !ringacc->proxy_inuse)
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1552 return -ENOMEM;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1553
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1554 for (i = 0; i < ringacc->num_rings; i++) {
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1555 ringacc->rings[i].rt = base_rt +
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1556 K3_RINGACC_RT_REGS_STEP * i;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1557 ringacc->rings[i].fifos = base_fifo +
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1558 K3_RINGACC_FIFO_REGS_STEP * i;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1559 ringacc->rings[i].parent = ringacc;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1560 ringacc->rings[i].ring_id = i;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1561 ringacc->rings[i].proxy_id = K3_RINGACC_PROXY_NOT_USED;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1562 }
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1563
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1564 ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1565
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1566 dev_info(dev, "Ring Accelerator probed rings:%u, gp-rings[%u,%u] sci-dev-id:%u\n",
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1567 ringacc->num_rings,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1568 ringacc->rm_gp_range->desc[0].start,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1569 ringacc->rm_gp_range->desc[0].num,
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1570 ringacc->tisci_dev_id);
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1571 dev_info(dev, "dma-ring-reset-quirk: %s\n",
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1572 ringacc->dma_ring_reset_quirk ? "enabled" : "disabled");
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1573 dev_info(dev, "RA Proxy rev. %08x, num_proxies:%u\n",
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1574 readl(&ringacc->proxy_gcfg->revision), ringacc->num_proxies);
40a2a7c395cf5d9 Grygorii Strashko 2020-07-24 1575
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1576 return 0;
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1577 }
3277e8aa2504d97 Grygorii Strashko 2020-01-15 1578
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
2026-01-30 11:01 ` [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci Sai Sree Kartheek Adivi
2026-01-30 18:40 ` kernel test robot
@ 2026-01-30 18:40 ` kernel test robot
2026-02-03 6:05 ` Péter Ujfalusi
2 siblings, 0 replies; 45+ messages in thread
From: kernel test robot @ 2026-01-30 18:40 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul, robh, krzk+dt,
conor+dt, nm, ssantosh, dmaengine, devicetree, linux-kernel,
linux-arm-kernel, vigneshr
Cc: oe-kbuild-all, r-sharma3, gehariprasath
Hi Sai,
kernel test robot noticed the following build errors:
[auto build test ERROR on vkoul-dmaengine/next]
[also build test ERROR on next-20260129]
[cannot apply to linus/master v6.19-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sai-Sree-Kartheek-Adivi/dmaengine-ti-k3-udma-move-macros-to-header-file/20260130-191306
base: https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
patch link: https://lore.kernel.org/r/20260130110159.359501-12-s-adivi%40ti.com
patch subject: [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
config: nios2-allmodconfig (https://download.01.org/0day-ci/archive/20260131/202601310246.hIZd2WuJ-lkp@intel.com/config)
compiler: nios2-linux-gcc (GCC) 11.5.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260131/202601310246.hIZd2WuJ-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601310246.hIZd2WuJ-lkp@intel.com/
All error/warnings (new ones prefixed by >>):
In file included from drivers/soc/ti/k3-ringacc.c:17:
>> include/linux/soc/ti/ti_sci_inta_msi.h:17:43: warning: 'struct msi_domain_info' declared inside parameter list will not be visible outside of this definition or declaration
17 | struct msi_domain_info *info,
| ^~~~~~~~~~~~~~~
drivers/soc/ti/k3-ringacc.c: In function 'k3_ringacc_init':
>> drivers/soc/ti/k3-ringacc.c:1506:17: error: 'struct dev_msi_info' has no member named 'domain'
1506 | dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
| ^
drivers/soc/ti/k3-ringacc.c:1508:22: error: 'struct dev_msi_info' has no member named 'domain'
1508 | if (!dev->msi.domain)
| ^
--
In file included from k3-ringacc.c:17:
>> include/linux/soc/ti/ti_sci_inta_msi.h:17:43: warning: 'struct msi_domain_info' declared inside parameter list will not be visible outside of this definition or declaration
17 | struct msi_domain_info *info,
| ^~~~~~~~~~~~~~~
k3-ringacc.c: In function 'k3_ringacc_init':
k3-ringacc.c:1506:17: error: 'struct dev_msi_info' has no member named 'domain'
1506 | dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
| ^
k3-ringacc.c:1508:22: error: 'struct dev_msi_info' has no member named 'domain'
1508 | if (!dev->msi.domain)
| ^
vim +1506 drivers/soc/ti/k3-ringacc.c
95e7be062aea6d Grygorii Strashko 2020-09-11 1497
40a2a7c395cf5d Grygorii Strashko 2020-07-24 1498 static int k3_ringacc_init(struct platform_device *pdev,
40a2a7c395cf5d Grygorii Strashko 2020-07-24 1499 struct k3_ringacc *ringacc)
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1500 {
95e7be062aea6d Grygorii Strashko 2020-09-11 1501 const struct soc_device_attribute *soc;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1502 void __iomem *base_fifo, *base_rt;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1503 struct device *dev = &pdev->dev;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1504 int ret, i;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1505
34fff62827b254 Thomas Gleixner 2021-12-10 @1506 dev->msi.domain = of_msi_get_domain(dev, dev->of_node,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1507 DOMAIN_BUS_TI_SCI_INTA_MSI);
e50a76355c1d85 Jayesh Choudhary 2023-07-28 1508 if (!dev->msi.domain)
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1509 return -EPROBE_DEFER;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1510
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1511 ret = k3_ringacc_probe_dt(ringacc);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1512 if (ret)
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1513 return ret;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1514
95e7be062aea6d Grygorii Strashko 2020-09-11 1515 soc = soc_device_match(k3_ringacc_socinfo);
95e7be062aea6d Grygorii Strashko 2020-09-11 1516 if (soc && soc->data) {
95e7be062aea6d Grygorii Strashko 2020-09-11 1517 const struct k3_ringacc_soc_data *soc_data = soc->data;
95e7be062aea6d Grygorii Strashko 2020-09-11 1518
95e7be062aea6d Grygorii Strashko 2020-09-11 1519 ringacc->dma_ring_reset_quirk = soc_data->dma_ring_reset_quirk;
95e7be062aea6d Grygorii Strashko 2020-09-11 1520 }
95e7be062aea6d Grygorii Strashko 2020-09-11 1521
f9dbb99748bab0 Zhang Zekun 2023-08-09 1522 base_rt = devm_platform_ioremap_resource_byname(pdev, "rt");
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1523 if (IS_ERR(base_rt))
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1524 return PTR_ERR(base_rt);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1525
f9dbb99748bab0 Zhang Zekun 2023-08-09 1526 base_fifo = devm_platform_ioremap_resource_byname(pdev, "fifos");
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1527 if (IS_ERR(base_fifo))
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1528 return PTR_ERR(base_fifo);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1529
f9dbb99748bab0 Zhang Zekun 2023-08-09 1530 ringacc->proxy_gcfg = devm_platform_ioremap_resource_byname(pdev, "proxy_gcfg");
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1531 if (IS_ERR(ringacc->proxy_gcfg))
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1532 return PTR_ERR(ringacc->proxy_gcfg);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1533
f9dbb99748bab0 Zhang Zekun 2023-08-09 1534 ringacc->proxy_target_base = devm_platform_ioremap_resource_byname(pdev,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1535 "proxy_target");
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1536 if (IS_ERR(ringacc->proxy_target_base))
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1537 return PTR_ERR(ringacc->proxy_target_base);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1538
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1539 ringacc->num_proxies = readl(&ringacc->proxy_gcfg->config) &
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1540 K3_RINGACC_PROXY_CFG_THREADS_MASK;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1541
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1542 ringacc->rings = devm_kzalloc(dev,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1543 sizeof(*ringacc->rings) *
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1544 ringacc->num_rings,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1545 GFP_KERNEL);
a8eba8dde5fbf0 Christophe JAILLET 2021-12-23 1546 ringacc->rings_inuse = devm_bitmap_zalloc(dev, ringacc->num_rings,
a8eba8dde5fbf0 Christophe JAILLET 2021-12-23 1547 GFP_KERNEL);
a8eba8dde5fbf0 Christophe JAILLET 2021-12-23 1548 ringacc->proxy_inuse = devm_bitmap_zalloc(dev, ringacc->num_proxies,
a8eba8dde5fbf0 Christophe JAILLET 2021-12-23 1549 GFP_KERNEL);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1550
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1551 if (!ringacc->rings || !ringacc->rings_inuse || !ringacc->proxy_inuse)
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1552 return -ENOMEM;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1553
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1554 for (i = 0; i < ringacc->num_rings; i++) {
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1555 ringacc->rings[i].rt = base_rt +
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1556 K3_RINGACC_RT_REGS_STEP * i;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1557 ringacc->rings[i].fifos = base_fifo +
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1558 K3_RINGACC_FIFO_REGS_STEP * i;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1559 ringacc->rings[i].parent = ringacc;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1560 ringacc->rings[i].ring_id = i;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1561 ringacc->rings[i].proxy_id = K3_RINGACC_PROXY_NOT_USED;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1562 }
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1563
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1564 ringacc->tisci_ring_ops = &ringacc->tisci->ops.rm_ring_ops;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1565
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1566 dev_info(dev, "Ring Accelerator probed rings:%u, gp-rings[%u,%u] sci-dev-id:%u\n",
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1567 ringacc->num_rings,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1568 ringacc->rm_gp_range->desc[0].start,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1569 ringacc->rm_gp_range->desc[0].num,
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1570 ringacc->tisci_dev_id);
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1571 dev_info(dev, "dma-ring-reset-quirk: %s\n",
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1572 ringacc->dma_ring_reset_quirk ? "enabled" : "disabled");
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1573 dev_info(dev, "RA Proxy rev. %08x, num_proxies:%u\n",
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1574 readl(&ringacc->proxy_gcfg->revision), ringacc->num_proxies);
40a2a7c395cf5d Grygorii Strashko 2020-07-24 1575
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1576 return 0;
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1577 }
3277e8aa2504d9 Grygorii Strashko 2020-01-15 1578
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci
2026-01-30 11:01 ` [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci Sai Sree Kartheek Adivi
2026-01-30 18:40 ` kernel test robot
2026-01-30 18:40 ` kernel test robot
@ 2026-02-03 6:05 ` Péter Ujfalusi
2 siblings, 0 replies; 45+ messages in thread
From: Péter Ujfalusi @ 2026-02-03 6:05 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, vkoul, robh, krzk+dt, conor+dt, nm,
ssantosh, dmaengine, devicetree, linux-kernel, linux-arm-kernel,
vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
> Handle absence of tisci with direct register writes. This will support
> platforms that do not have tisci firmware like AM62L.
>
> Remove TI_SCI_INTA_IRQCHIP dependency for TI_K3_RINGACC in Kconfig as
> it is required conditionally (only for devices that has TI_SCI).
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/soc/ti/Kconfig | 1 -
> drivers/soc/ti/k3-ringacc.c | 188 ++++++++++++++++++++++++++----
> include/linux/soc/ti/k3-ringacc.h | 17 +++
> 3 files changed, 181 insertions(+), 25 deletions(-)
>
> diff --git a/drivers/soc/ti/Kconfig b/drivers/soc/ti/Kconfig
> index 163aadd589d3a..5499c0def527a 100644
> --- a/drivers/soc/ti/Kconfig
> +++ b/drivers/soc/ti/Kconfig
> @@ -53,7 +53,6 @@ config WKUP_M3_IPC
> config TI_K3_RINGACC
> tristate "K3 Ring accelerator Sub System"
> depends on ARCH_K3 || COMPILE_TEST
> - depends on TI_SCI_INTA_IRQCHIP
I guess (based on robot report) that the selects from
TI_SCI_INTA_IRQCHIP and TI_SCI_INTA_MSI_DOMAIN are needed to be added here
--
Péter
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (10 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 11/19] drivers: soc: ti: k3-ringacc: handle absence of tisci Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 15:35 ` Frank Li
2026-02-05 9:04 ` Krzysztof Kozlowski
2026-01-30 11:01 ` [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2 Sai Sree Kartheek Adivi
` (7 subsequent siblings)
19 siblings, 2 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
New binding document for
Texas Instruments K3 Block Copy DMA (BCDMA) V2.
BCDMA V2 is introduced as part of AM62L.
Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
.../bindings/dma/ti/ti,k3-bcdma-v2.yaml | 116 ++++++++++++++++++
1 file changed, 116 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
diff --git a/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml b/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
new file mode 100644
index 0000000000000..b0bfb19ba863a
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
@@ -0,0 +1,116 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2024-2025 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/ti/ti,k3-bcdma-v2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments K3 DMSS BCDMA V2
+
+maintainers:
+ - Sai Sree Kartheek Adivi <s-adivi@ti.com>
+
+description: |
+ The BCDMA V2 is intended to perform similar functions as the TR mode channels
+ of K3 UDMA-P. BCDMA V2 includes block copy channels and Split channels.
+
+ Block copy channels mainly used for memory to memory transfers, but with
+ optional triggers a block copy channel can service peripherals by accessing
+ directly to memory mapped registers or area.
+
+ Split channels can be used to service PSI-L based peripherals. The peripherals
+ can be PSI-L native or legacy, non PSI-L native peripherals with PDMAs. PDMA
+ is tasked to act as a bridge between the PSI-L fabric and the legacy peripheral.
+
+allOf:
+ - $ref: /schemas/dma/dma-controller.yaml#
+
+properties:
+ compatible:
+ const: ti,am62l-dmss-bcdma
+
+ reg:
+ items:
+ - description: BCDMA Control /Status
+ - description: Block Copy Channel Realtime
+ - description: Channel Realtime
+ - description: Ring Realtime
+
+ reg-names:
+ items:
+ - const: gcfg
+ - const: bchanrt
+ - const: chanrt
+ - const: ringrt
+
+ "#dma-cells":
+ const: 4
+ description: |
+ cell 1: Channel operation mode
+ 0 - split channel / no trigger
+ 1 - internal channel event
+ 2 - external signal
+ 3 - timer manager event
+
+ cell 2: parameter for the trigger:
+ if cell 1 is 0 (disable / no trigger):
+ Unused, ignored
+ if cell 1 is 1 (internal channel event):
+ channel number whose TR event should trigger the current channel.
+ if cell 1 is 2 or 3 (external signal or timer manager event):
+ index of global interfaces that come into the DMA.
+
+ Please refer to the device documentation for global interface indexes.
+
+ cell 3: Channel identification for the peripheral
+ if cell 1 is 0 (split channel / no trigger):
+ PSI-L thread ID of the remote (to BCDMA) end.
+ Valid ranges for thread ID depends on the data movement direction:
+ for source thread IDs (rx): 0 - 0x7fff
+ for destination thread IDs (tx): 0x8000 - 0xffff
+
+ Please refer to the device documentation for the PSI-L thread map and
+ also the PSI-L peripheral chapter for the correct thread ID.
+
+ if cell 1 is 1 or 2 or 3 (MEM_TO_MEM and/or trigger type):
+ Unused, provide 0.
+
+ cell 4: ASEL value for the channel
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - "#dma-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ main {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ main_bcdma: dma-controller@485c4000 {
+ compatible = "ti,am62l-dmss-bcdma";
+ reg = <0x00 0x485c4000 0x00 0x4000>,
+ <0x00 0x48880000 0x00 0x10000>,
+ <0x00 0x48800000 0x00 0x80000>,
+ <0x00 0x47000000 0x00 0x200000>;
+ reg-names = "gcfg", "bchanrt", "chanrt", "ringrt";
+ #dma-cells = <4>;
+ };
+
+ crypto@40800000 {
+ compatible = "ti,am62l-dthev2";
+ reg = <0x00 0x40800000 0x00 0x14000>;
+
+ dmas = <&main_bcdma 0 0 0x4700 0>,
+ /* rx: Split channel, no trigger, PSI-L thread id, ASEL value */
+ <&main_bcdma 0 0 0xc701 0>,
+ /* tx1: Split channel, no trigger, PSI-L thread id, ASEL value */
+ <&main_bcdma 0 0 0xc700 0>;
+ /* tx2: Split channel, no trigger, PSI-L thread id, ASEL value */
+ dma-names = "rx", "tx1", "tx2";
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2
2026-01-30 11:01 ` [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2 Sai Sree Kartheek Adivi
@ 2026-01-30 15:35 ` Frank Li
2026-02-05 9:04 ` Krzysztof Kozlowski
1 sibling, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:35 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:52PM +0530, Sai Sree Kartheek Adivi wrote:
> New binding document for
> Texas Instruments K3 Block Copy DMA (BCDMA) V2.
>
> BCDMA V2 is introduced as part of AM62L.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> .../bindings/dma/ti/ti,k3-bcdma-v2.yaml | 116 ++++++++++++++++++
> 1 file changed, 116 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
>
> diff --git a/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml b/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
> new file mode 100644
> index 0000000000000..b0bfb19ba863a
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
> @@ -0,0 +1,116 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright (C) 2024-2025 Texas Instruments Incorporated
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/dma/ti/ti,k3-bcdma-v2.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments K3 DMSS BCDMA V2
> +
> +maintainers:
> + - Sai Sree Kartheek Adivi <s-adivi@ti.com>
> +
> +description: |
> + The BCDMA V2 is intended to perform similar functions as the TR mode channels
> + of K3 UDMA-P. BCDMA V2 includes block copy channels and Split channels.
> +
> + Block copy channels mainly used for memory to memory transfers, but with
> + optional triggers a block copy channel can service peripherals by accessing
> + directly to memory mapped registers or area.
> +
> + Split channels can be used to service PSI-L based peripherals. The peripherals
> + can be PSI-L native or legacy, non PSI-L native peripherals with PDMAs. PDMA
> + is tasked to act as a bridge between the PSI-L fabric and the legacy peripheral.
> +
> +allOf:
> + - $ref: /schemas/dma/dma-controller.yaml#
> +
> +properties:
> + compatible:
> + const: ti,am62l-dmss-bcdma
> +
> + reg:
> + items:
> + - description: BCDMA Control /Status
> + - description: Block Copy Channel Realtime
> + - description: Channel Realtime
> + - description: Ring Realtime
> +
> + reg-names:
> + items:
> + - const: gcfg
> + - const: bchanrt
> + - const: chanrt
> + - const: ringrt
> +
> + "#dma-cells":
> + const: 4
look like only this difference compare v3. can you consider to combine to
one file
Frank
> + description: |
> + cell 1: Channel operation mode
> + 0 - split channel / no trigger
> + 1 - internal channel event
> + 2 - external signal
> + 3 - timer manager event
> +
> + cell 2: parameter for the trigger:
> + if cell 1 is 0 (disable / no trigger):
> + Unused, ignored
> + if cell 1 is 1 (internal channel event):
> + channel number whose TR event should trigger the current channel.
> + if cell 1 is 2 or 3 (external signal or timer manager event):
> + index of global interfaces that come into the DMA.
> +
> + Please refer to the device documentation for global interface indexes.
> +
> + cell 3: Channel identification for the peripheral
> + if cell 1 is 0 (split channel / no trigger):
> + PSI-L thread ID of the remote (to BCDMA) end.
> + Valid ranges for thread ID depends on the data movement direction:
> + for source thread IDs (rx): 0 - 0x7fff
> + for destination thread IDs (tx): 0x8000 - 0xffff
> +
> + Please refer to the device documentation for the PSI-L thread map and
> + also the PSI-L peripheral chapter for the correct thread ID.
> +
> + if cell 1 is 1 or 2 or 3 (MEM_TO_MEM and/or trigger type):
> + Unused, provide 0.
> +
> + cell 4: ASEL value for the channel
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - "#dma-cells"
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + main {
> + #address-cells = <2>;
> + #size-cells = <2>;
> +
> + main_bcdma: dma-controller@485c4000 {
> + compatible = "ti,am62l-dmss-bcdma";
> + reg = <0x00 0x485c4000 0x00 0x4000>,
> + <0x00 0x48880000 0x00 0x10000>,
> + <0x00 0x48800000 0x00 0x80000>,
> + <0x00 0x47000000 0x00 0x200000>;
> + reg-names = "gcfg", "bchanrt", "chanrt", "ringrt";
> + #dma-cells = <4>;
> + };
> +
> + crypto@40800000 {
> + compatible = "ti,am62l-dthev2";
> + reg = <0x00 0x40800000 0x00 0x14000>;
> +
> + dmas = <&main_bcdma 0 0 0x4700 0>,
> + /* rx: Split channel, no trigger, PSI-L thread id, ASEL value */
> + <&main_bcdma 0 0 0xc701 0>,
> + /* tx1: Split channel, no trigger, PSI-L thread id, ASEL value */
> + <&main_bcdma 0 0 0xc700 0>;
> + /* tx2: Split channel, no trigger, PSI-L thread id, ASEL value */
> + dma-names = "rx", "tx1", "tx2";
> + };
> + };
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2
2026-01-30 11:01 ` [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2 Sai Sree Kartheek Adivi
2026-01-30 15:35 ` Frank Li
@ 2026-02-05 9:04 ` Krzysztof Kozlowski
1 sibling, 0 replies; 45+ messages in thread
From: Krzysztof Kozlowski @ 2026-02-05 9:04 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul, robh, krzk+dt,
conor+dt, nm, ssantosh, dmaengine, devicetree, linux-kernel,
linux-arm-kernel, vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/2026 12:01, Sai Sree Kartheek Adivi wrote:
> New binding document for
> Texas Instruments K3 Block Copy DMA (BCDMA) V2.
>
> BCDMA V2 is introduced as part of AM62L.
>
> Reviewed-by: Krzysztof Kozlowski <krzysztof.kozlowski@linaro.org>
NAK, you ignored my review
...
> +
> + crypto@40800000 {
> + compatible = "ti,am62l-dthev2";
NAK, you already received review. You cannot add more things here in hiding.
> + reg = <0x00 0x40800000 0x00 0x14000>;
> +
> + dmas = <&main_bcdma 0 0 0x4700 0>,
> + /* rx: Split channel, no trigger, PSI-L thread id, ASEL value */
> + <&main_bcdma 0 0 0xc701 0>,
> + /* tx1: Split channel, no trigger, PSI-L thread id, ASEL value */
> + <&main_bcdma 0 0 0xc700 0>;
> + /* tx2: Split channel, no trigger, PSI-L thread id, ASEL value */
> + dma-names = "rx", "tx1", "tx2";
> + };
> + };
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (11 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 12/19] dt-bindings: dma: ti: Add K3 BCDMA V2 Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 12:44 ` Rob Herring (Arm)
` (2 more replies)
2026-01-30 11:01 ` [PATCH v4 14/19] dmaengine: ti: k3-psil-am62l: Add AM62Lx PSIL and PDMA data Sai Sree Kartheek Adivi
` (6 subsequent siblings)
19 siblings, 3 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
New binding document for
Texas Instruments K3 Packet DMA (PKTDMA) V2.
PKTDMA V2 is introduced as part of AM62L.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
.../bindings/dma/ti/ti,k3-pktdma-v2.yaml | 90 +++++++++++++++++++
1 file changed, 90 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
diff --git a/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml b/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
new file mode 100644
index 0000000000000..e8afa6f6738b7
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
@@ -0,0 +1,90 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+# Copyright (C) 2024-2025 Texas Instruments Incorporated
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/ti/ti,k3-pktdma-v2.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Texas Instruments K3 DMSS PKTDMA V2
+
+maintainers:
+ - Sai Sree Kartheek Adivi <s-adivi@ti.com>
+
+description: |
+ The PKTDMA V2 is intended to perform similar functions as the packet mode
+ channels of K3 UDMA-P. PKTDMA V2 only includes Split channels to service
+ PSI-L based peripherals.
+
+ The peripherals can be PSI-L native or legacy, non PSI-L native peripherals
+ with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the
+ legacy peripheral.
+
+allOf:
+ - $ref: /schemas/dma/dma-controller.yaml#
+
+properties:
+ compatible:
+ const: ti,am62l-dmss-pktdma
+
+ reg:
+ items:
+ - description: Packet DMA Control /Status
+ - description: Channel Realtime
+ - description: Ring Realtime
+
+ reg-names:
+ items:
+ - const: gcfg
+ - const: chanrt
+ - const: ringrt
+
+ "#dma-cells":
+ const: 2
+ description: |
+ cell 1: Channel identification for the peripheral
+ PSI-L thread ID of the remote (to BCDMA) end.
+ Valid ranges for thread ID depends on the data movement direction:
+ for source thread IDs (rx): 0 - 0x7fff
+ for destination thread IDs (tx): 0x8000 - 0xffff
+
+ Please refer to the device documentation for the PSI-L thread map and
+ also the PSI-L peripheral chapter for the correct thread ID.
+
+ cell 2: ASEL value for the channel
+
+required:
+ - compatible
+ - reg
+ - reg-names
+ - "#dma-cells"
+
+unevaluatedProperties: false
+
+examples:
+ - |
+ main {
+ #address-cells = <2>;
+ #size-cells = <2>;
+
+ main_pktdma: dma-controller@485c0000 {
+ compatible = "ti,am62l-dmss-pktdma";
+ reg = <0x00 0x485c0000 0x00 0x4000>,
+ <0x00 0x48900000 0x00 0x80000>,
+ <0x00 0x47200000 0x00 0x100000>;
+ reg-names = "gcfg", "chanrt", "ringrt";
+ #dma-cells = <2>;
+ };
+
+ serial@2800000 {
+ compatible = "ti,am64-uart", "ti,am654-uart";
+ reg = <0x00 0x02800000 0x00 0x100>;
+ power-domains = <&scmi_pds 89>;
+ clocks = <&scmi_clk 358>;
+ clock-names = "fclk";
+ dmas = <&main_pktdma 0xc400 0>,
+ /* tx: PSI-L thread ID, ASEL value */
+ <&main_pktdma 0x4400 0>;
+ /* rx: PSI-L thread ID, ASEL value */
+ dma-names = "tx", "rx";
+ };
+ };
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2
2026-01-30 11:01 ` [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2 Sai Sree Kartheek Adivi
@ 2026-01-30 12:44 ` Rob Herring (Arm)
2026-01-30 15:34 ` Frank Li
2026-02-05 9:06 ` Krzysztof Kozlowski
2 siblings, 0 replies; 45+ messages in thread
From: Rob Herring (Arm) @ 2026-01-30 12:44 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, nm, linux-arm-kernel, r-sharma3, gehariprasath,
conor+dt, linux-kernel, vigneshr, krzk+dt, dmaengine, devicetree,
vkoul, ssantosh
On Fri, 30 Jan 2026 16:31:53 +0530, Sai Sree Kartheek Adivi wrote:
> New binding document for
> Texas Instruments K3 Packet DMA (PKTDMA) V2.
>
> PKTDMA V2 is introduced as part of AM62L.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> .../bindings/dma/ti/ti,k3-pktdma-v2.yaml | 90 +++++++++++++++++++
> 1 file changed, 90 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
>
My bot found errors running 'make dt_binding_check' on your patch:
yamllint warnings/errors:
dtschema/dtc warnings/errors:
/builds/robherring/dt-review-ci/linux/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.example.dtb: serial@2800000 (ti,am64-uart): 'oneOf' conditional failed, one must be fixed:
'interrupts' is a required property
'interrupts-extended' is a required property
from schema $id: http://devicetree.org/schemas/serial/8250_omap.yaml
doc reference errors (make refcheckdocs):
See https://patchwork.kernel.org/project/devicetree/patch/20260130110159.359501-14-s-adivi@ti.com
The base for the series is generally the latest rc1. A different dependency
should be noted in *this* patch.
If you already ran 'make dt_binding_check' and didn't see the above
error(s), then make sure 'yamllint' is installed and dt-schema is up to
date:
pip3 install dtschema --upgrade
Please check and re-submit after running the above command yourself. Note
that DT_SCHEMA_FILES can be set to your schema file to speed up checking
your schema. However, it must be unset to test all examples with your schema.
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2
2026-01-30 11:01 ` [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2 Sai Sree Kartheek Adivi
2026-01-30 12:44 ` Rob Herring (Arm)
@ 2026-01-30 15:34 ` Frank Li
2026-02-05 9:06 ` Krzysztof Kozlowski
2 siblings, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 15:34 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:53PM +0530, Sai Sree Kartheek Adivi wrote:
> New binding document for
> Texas Instruments K3 Packet DMA (PKTDMA) V2.
>
> PKTDMA V2 is introduced as part of AM62L.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> .../bindings/dma/ti/ti,k3-pktdma-v2.yaml | 90 +++++++++++++++++++
> 1 file changed, 90 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
>
> diff --git a/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml b/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
> new file mode 100644
> index 0000000000000..e8afa6f6738b7
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
> @@ -0,0 +1,90 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +# Copyright (C) 2024-2025 Texas Instruments Incorporated
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/dma/ti/ti,k3-pktdma-v2.yaml#
filename need use one of compatible string.
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Texas Instruments K3 DMSS PKTDMA V2
> +
> +maintainers:
> + - Sai Sree Kartheek Adivi <s-adivi@ti.com>
> +
> +description: |
Needn't |
> + The PKTDMA V2 is intended to perform similar functions as the packet mode
> + channels of K3 UDMA-P. PKTDMA V2 only includes Split channels to service
> + PSI-L based peripherals.
> +
> + The peripherals can be PSI-L native or legacy, non PSI-L native peripherals
> + with PDMAs. PDMA is tasked to act as a bridge between the PSI-L fabric and the
> + legacy peripheral.
> +
> +allOf:
> + - $ref: /schemas/dma/dma-controller.yaml#
> +
> +properties:
> + compatible:
> + const: ti,am62l-dmss-pktdma
> +
> + reg:
> + items:
> + - description: Packet DMA Control /Status
> + - description: Channel Realtime
> + - description: Ring Realtime
> +
> + reg-names:
> + items:
> + - const: gcfg
> + - const: chanrt
> + - const: ringrt
> +
> + "#dma-cells":
> + const: 2
> + description: |
> + cell 1: Channel identification for the peripheral
> + PSI-L thread ID of the remote (to BCDMA) end.
> + Valid ranges for thread ID depends on the data movement direction:
> + for source thread IDs (rx): 0 - 0x7fff
> + for destination thread IDs (tx): 0x8000 - 0xffff
> +
> + Please refer to the device documentation for the PSI-L thread map and
> + also the PSI-L peripheral chapter for the correct thread ID.
> +
> + cell 2: ASEL value for the channel
> +
> +required:
> + - compatible
> + - reg
> + - reg-names
> + - "#dma-cells"
No irq and clock?
> +
> +unevaluatedProperties: false
> +
> +examples:
> + - |
> + main {
> + #address-cells = <2>;
> + #size-cells = <2>;
If use 32bit address, needn't main
> +
> + main_pktdma: dma-controller@485c0000 {
Need need label
> + compatible = "ti,am62l-dmss-pktdma";
> + reg = <0x00 0x485c0000 0x00 0x4000>,
> + <0x00 0x48900000 0x00 0x80000>,
> + <0x00 0x47200000 0x00 0x100000>;
> + reg-names = "gcfg", "chanrt", "ringrt";
> + #dma-cells = <2>;
> + };
> +
> + serial@2800000 {
Needn't this in example.
Frank
> + compatible = "ti,am64-uart", "ti,am654-uart";
> + reg = <0x00 0x02800000 0x00 0x100>;
> + power-domains = <&scmi_pds 89>;
> + clocks = <&scmi_clk 358>;
> + clock-names = "fclk";
> + dmas = <&main_pktdma 0xc400 0>,
> + /* tx: PSI-L thread ID, ASEL value */
> + <&main_pktdma 0x4400 0>;
> + /* rx: PSI-L thread ID, ASEL value */
> + dma-names = "tx", "rx";
> + };
> + };
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2
2026-01-30 11:01 ` [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2 Sai Sree Kartheek Adivi
2026-01-30 12:44 ` Rob Herring (Arm)
2026-01-30 15:34 ` Frank Li
@ 2026-02-05 9:06 ` Krzysztof Kozlowski
2 siblings, 0 replies; 45+ messages in thread
From: Krzysztof Kozlowski @ 2026-02-05 9:06 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul, robh, krzk+dt,
conor+dt, nm, ssantosh, dmaengine, devicetree, linux-kernel,
linux-arm-kernel, vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/2026 12:01, Sai Sree Kartheek Adivi wrote:
> New binding document for
> Texas Instruments K3 Packet DMA (PKTDMA) V2.
>
> PKTDMA V2 is introduced as part of AM62L.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> .../bindings/dma/ti/ti,k3-pktdma-v2.yaml | 90 +++++++++++++++++++
You ignored previous comments and repeated same mistake(s) here.
Additionally you added more things which we did not ask you to add...
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 14/19] dmaengine: ti: k3-psil-am62l: Add AM62Lx PSIL and PDMA data
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (12 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 13/19] dt-bindings: dma: ti: Add K3 PKTDMA V2 Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2 Sai Sree Kartheek Adivi
` (5 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Add PSIL and PDMA data for AM62Lx SoC.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/Makefile | 3 +-
drivers/dma/ti/k3-psil-am62l.c | 132 +++++++++++++++++++++++++++++++++
drivers/dma/ti/k3-psil-priv.h | 1 +
drivers/dma/ti/k3-psil.c | 1 +
4 files changed, 136 insertions(+), 1 deletion(-)
create mode 100644 drivers/dma/ti/k3-psil-am62l.c
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
index 3b91c02e55eaf..41bfba944dc6c 100644
--- a/drivers/dma/ti/Makefile
+++ b/drivers/dma/ti/Makefile
@@ -14,6 +14,7 @@ k3-psil-lib-objs := k3-psil.o \
k3-psil-am62.o \
k3-psil-am62a.o \
k3-psil-j784s4.o \
- k3-psil-am62p.o
+ k3-psil-am62p.o \
+ k3-psil-am62l.o
obj-$(CONFIG_TI_K3_PSIL) += k3-psil-lib.o
obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o
diff --git a/drivers/dma/ti/k3-psil-am62l.c b/drivers/dma/ti/k3-psil-am62l.c
new file mode 100644
index 0000000000000..45f5aac32f6a0
--- /dev/null
+++ b/drivers/dma/ti/k3-psil-am62l.c
@@ -0,0 +1,132 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) 2024-2025 Texas Instruments Incorporated - https://www.ti.com
+ */
+
+#include <linux/kernel.h>
+
+#include "k3-psil-priv.h"
+
+#define PSIL_PDMA_XY_TR(x, ch) \
+ { \
+ .thread_id = x, \
+ .ep_config = { \
+ .ep_type = PSIL_EP_PDMA_XY, \
+ .mapped_channel_id = ch, \
+ .default_flow_id = -1, \
+ }, \
+ }
+
+#define PSIL_PDMA_XY_PKT(x, ch) \
+ { \
+ .thread_id = x, \
+ .ep_config = { \
+ .ep_type = PSIL_EP_PDMA_XY, \
+ .mapped_channel_id = ch, \
+ .pkt_mode = 1, \
+ .default_flow_id = -1 \
+ }, \
+ }
+
+#define PSIL_ETHERNET(x, ch, flow_base, flow_cnt) \
+ { \
+ .thread_id = x, \
+ .ep_config = { \
+ .ep_type = PSIL_EP_NATIVE, \
+ .pkt_mode = 1, \
+ .needs_epib = 1, \
+ .psd_size = 16, \
+ .mapped_channel_id = ch, \
+ .flow_start = flow_base, \
+ .flow_num = flow_cnt, \
+ .default_flow_id = flow_base, \
+ }, \
+ }
+
+#define PSIL_PDMA_MCASP(x, ch) \
+ { \
+ .thread_id = x, \
+ .ep_config = { \
+ .ep_type = PSIL_EP_PDMA_XY, \
+ .pdma_acc32 = 1, \
+ .pdma_burst = 1, \
+ .mapped_channel_id = ch, \
+ }, \
+ }
+
+/* PSI-L source thread IDs, used for RX (DMA_DEV_TO_MEM) */
+static struct psil_ep am62l_src_ep_map[] = {
+ /* PDMA_MAIN1 - UART0-6 */
+ PSIL_PDMA_XY_PKT(0x4400, 0),
+ PSIL_PDMA_XY_PKT(0x4401, 2),
+ PSIL_PDMA_XY_PKT(0x4402, 4),
+ PSIL_PDMA_XY_PKT(0x4403, 6),
+ PSIL_PDMA_XY_PKT(0x4404, 8),
+ PSIL_PDMA_XY_PKT(0x4405, 10),
+ PSIL_PDMA_XY_PKT(0x4406, 12),
+ /* PDMA_MAIN0 - SPI0 - CH0-3 */
+ PSIL_PDMA_XY_TR(0x4300, 16),
+ /* PDMA_MAIN0 - SPI1 - CH0-3 */
+ PSIL_PDMA_XY_TR(0x4301, 24),
+ /* PDMA_MAIN0 - SPI2 - CH0-3 */
+ PSIL_PDMA_XY_TR(0x4302, 32),
+ /* PDMA_MAIN0 - SPI3 - CH0-3 */
+ PSIL_PDMA_XY_TR(0x4303, 40),
+ /* PDMA_MAIN2 - MCASP0-2 */
+ PSIL_PDMA_MCASP(0x4500, 48),
+ PSIL_PDMA_MCASP(0x4501, 50),
+ PSIL_PDMA_MCASP(0x4502, 52),
+ /* PDMA_MAIN0 - AES */
+ PSIL_PDMA_XY_TR(0x4700, 65),
+ /* PDMA_MAIN0 - ADC */
+ PSIL_PDMA_XY_TR(0x4503, 80),
+ PSIL_PDMA_XY_TR(0x4504, 81),
+ PSIL_ETHERNET(0x4600, 96, 96, 16),
+};
+
+/* PSI-L destination thread IDs, used for TX (DMA_MEM_TO_DEV) */
+static struct psil_ep am62l_dst_ep_map[] = {
+ /* PDMA_MAIN1 - UART0-6 */
+ PSIL_PDMA_XY_PKT(0xC400, 1),
+ PSIL_PDMA_XY_PKT(0xC401, 3),
+ PSIL_PDMA_XY_PKT(0xC402, 5),
+ PSIL_PDMA_XY_PKT(0xC403, 7),
+ PSIL_PDMA_XY_PKT(0xC404, 9),
+ PSIL_PDMA_XY_PKT(0xC405, 11),
+ PSIL_PDMA_XY_PKT(0xC406, 13),
+ /* PDMA_MAIN0 - SPI0 - CH0-3 */
+ PSIL_PDMA_XY_TR(0xC300, 17),
+ /* PDMA_MAIN0 - SPI1 - CH0-3 */
+ PSIL_PDMA_XY_TR(0xC301, 25),
+ /* PDMA_MAIN0 - SPI2 - CH0-3 */
+ PSIL_PDMA_XY_TR(0xC302, 33),
+ /* PDMA_MAIN0 - SPI3 - CH0-3 */
+ PSIL_PDMA_XY_TR(0xC303, 41),
+ /* PDMA_MAIN2 - MCASP0-2 */
+ PSIL_PDMA_MCASP(0xC500, 49),
+ PSIL_PDMA_MCASP(0xC501, 51),
+ PSIL_PDMA_MCASP(0xC502, 53),
+ /* PDMA_MAIN0 - SHA */
+ PSIL_PDMA_XY_TR(0xC700, 64),
+ /* PDMA_MAIN0 - AES */
+ PSIL_PDMA_XY_TR(0xC701, 66),
+ /* PDMA_MAIN0 - CRC32 - CH0-1 */
+ PSIL_PDMA_XY_TR(0xC702, 67),
+ /* CPSW3G */
+ PSIL_ETHERNET(0xc600, 64, 64, 2),
+ PSIL_ETHERNET(0xc601, 66, 66, 2),
+ PSIL_ETHERNET(0xc602, 68, 68, 2),
+ PSIL_ETHERNET(0xc603, 70, 70, 2),
+ PSIL_ETHERNET(0xc604, 72, 72, 2),
+ PSIL_ETHERNET(0xc605, 74, 74, 2),
+ PSIL_ETHERNET(0xc606, 76, 76, 2),
+ PSIL_ETHERNET(0xc607, 78, 78, 2),
+};
+
+struct psil_ep_map am62l_ep_map = {
+ .name = "am62l",
+ .src = am62l_src_ep_map,
+ .src_count = ARRAY_SIZE(am62l_src_ep_map),
+ .dst = am62l_dst_ep_map,
+ .dst_count = ARRAY_SIZE(am62l_dst_ep_map),
+};
diff --git a/drivers/dma/ti/k3-psil-priv.h b/drivers/dma/ti/k3-psil-priv.h
index a577be97e3447..961b73df7a6bb 100644
--- a/drivers/dma/ti/k3-psil-priv.h
+++ b/drivers/dma/ti/k3-psil-priv.h
@@ -46,5 +46,6 @@ extern struct psil_ep_map am62_ep_map;
extern struct psil_ep_map am62a_ep_map;
extern struct psil_ep_map j784s4_ep_map;
extern struct psil_ep_map am62p_ep_map;
+extern struct psil_ep_map am62l_ep_map;
#endif /* K3_PSIL_PRIV_H_ */
diff --git a/drivers/dma/ti/k3-psil.c b/drivers/dma/ti/k3-psil.c
index c4b6f0df46861..2a843f36261bc 100644
--- a/drivers/dma/ti/k3-psil.c
+++ b/drivers/dma/ti/k3-psil.c
@@ -28,6 +28,7 @@ static const struct soc_device_attribute k3_soc_devices[] = {
{ .family = "J784S4", .data = &j784s4_ep_map },
{ .family = "AM62PX", .data = &am62p_ep_map },
{ .family = "J722S", .data = &am62p_ep_map },
+ { .family = "AM62LX", .data = &am62l_ep_map },
{ /* sentinel */ }
};
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (13 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 14/19] dmaengine: ti: k3-psil-am62l: Add AM62Lx PSIL and PDMA data Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 16:08 ` Frank Li
2026-02-03 6:37 ` Péter Ujfalusi
2026-01-30 11:01 ` [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2 Sai Sree Kartheek Adivi
` (4 subsequent siblings)
19 siblings, 2 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Add support for BCDMA_V2.
The BCDMA_V2 is different than the existing BCDMA supported by the
k3-udma driver.
The changes in BCDMA_V2 are:
- Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
needs to set in the RT_CTL register.
- Static channel mapping: Each channel is mapped to a single peripheral.
- Direct IRQs: There is no INT-A and interrupt lines from DMA are
directly connected to GIC.
- Remote side configuration handled by DMA. So no need to write to PEER
registers to START / STOP / PAUSE / TEARDOWN.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/Kconfig | 16 +-
drivers/dma/ti/Makefile | 1 +
drivers/dma/ti/k3-udma-common.c | 75 +-
drivers/dma/ti/k3-udma-v2.c | 1283 +++++++++++++++++++++++++++++
drivers/dma/ti/k3-udma.h | 117 +--
include/linux/soc/ti/k3-ringacc.h | 3 +
6 files changed, 1429 insertions(+), 66 deletions(-)
create mode 100644 drivers/dma/ti/k3-udma-v2.c
diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
index 712e456015459..ada2ea8aca4b0 100644
--- a/drivers/dma/ti/Kconfig
+++ b/drivers/dma/ti/Kconfig
@@ -49,6 +49,18 @@ config TI_K3_UDMA
Enable support for the TI UDMA (Unified DMA) controller. This
DMA engine is used in AM65x and j721e.
+config TI_K3_UDMA_V2
+ tristate "Texas Instruments K3 UDMA v2 support"
+ depends on ARCH_K3
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ select TI_K3_UDMA_COMMON
+ select TI_K3_RINGACC
+ select TI_K3_PSIL
+ help
+ Enable support for the TI UDMA (Unified DMA) v2 controller. This
+ DMA engine is used in AM62L.
+
config TI_K3_UDMA_COMMON
tristate
default n
@@ -56,14 +68,14 @@ config TI_K3_UDMA_COMMON
config TI_K3_UDMA_GLUE_LAYER
tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
depends on ARCH_K3 || COMPILE_TEST
- depends on TI_K3_UDMA
+ depends on TI_K3_UDMA || TI_K3_UDMA_V2
help
Say y here to support the K3 NAVSS DMA glue interface
If unsure, say N.
config TI_K3_PSIL
tristate
- default TI_K3_UDMA
+ default TI_K3_UDMA || TI_K3_UDMA_V2
config TI_DMA_CROSSBAR
bool
diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
index 41bfba944dc6c..296aa3421e71b 100644
--- a/drivers/dma/ti/Makefile
+++ b/drivers/dma/ti/Makefile
@@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_DMA_OMAP) += omap-dma.o
obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
+obj-$(CONFIG_TI_K3_UDMA_V2) += k3-udma-v2.o
obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
k3-psil-lib-objs := k3-psil.o \
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 0ffc6becc402e..ba0fc048234ac 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -171,8 +171,13 @@ bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
return true;
- peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
- bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+ if (uc->ud->match_data->type >= DMA_TYPE_BCDMA_V2) {
+ peer_bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
+ bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+ } else {
+ peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
+ bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+ }
/* Transfer is incomplete, store current residue and time stamp */
if (peer_bcnt < bcnt) {
@@ -319,6 +324,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;
+ u32 extra_flags = 0;
u64 asel;
/* estimate the number of TRs we will need */
@@ -342,6 +348,9 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
else
asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
+ if (dir == DMA_MEM_TO_DEV && uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)
+ extra_flags = CPPI5_TR_CSF_EOP;
+
tr_req = d->hwdesc[0].tr_req_base;
for_each_sg(sgl, sgent, sglen, i) {
dma_addr_t sg_addr = sg_dma_address(sgent);
@@ -358,7 +367,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
- cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
+ cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT | extra_flags);
sg_addr |= asel;
tr_req[tr_idx].addr = sg_addr;
@@ -372,7 +381,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
false, false,
CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
cppi5_tr_csf_set(&tr_req[tr_idx].flags,
- CPPI5_TR_CSF_SUPR_EVT);
+ CPPI5_TR_CSF_SUPR_EVT | extra_flags);
tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
tr_req[tr_idx].icnt0 = tr1_cnt0;
@@ -632,7 +641,8 @@ int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
d->static_tr.bstcnt = d->residue / d->sglen / div;
else
d->static_tr.bstcnt = d->residue / div;
- } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
+ } else if ((uc->ud->match_data->type == DMA_TYPE_BCDMA ||
+ uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) &&
uc->config.dir == DMA_DEV_TO_MEM &&
uc->cyclic) {
/*
@@ -952,7 +962,8 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
* last one, so set the flag for each period.
*/
if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
- uc->ud->match_data->type == DMA_TYPE_BCDMA) {
+ (uc->ud->match_data->type == DMA_TYPE_BCDMA ||
+ uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)) {
period_csf = CPPI5_TR_CSF_EOP;
}
@@ -1807,7 +1818,8 @@ void udma_dbg_summary_show_chan(struct seq_file *s,
switch (uc->config.dir) {
case DMA_MEM_TO_MEM:
- if (uc->ud->match_data->type == DMA_TYPE_BCDMA) {
+ if (uc->ud->match_data->type == DMA_TYPE_BCDMA ||
+ uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) {
seq_printf(s, "bchan%d)\n", uc->bchan->id);
return;
}
@@ -2052,6 +2064,8 @@ int udma_get_tchan(struct udma_chan *uc)
uc->tchan = NULL;
return ret;
}
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2)
+ uc->chan = uc->tchan;
if (ud->tflow_cnt) {
int tflow_id;
@@ -2102,6 +2116,8 @@ int udma_get_rchan(struct udma_chan *uc)
uc->rchan = NULL;
return ret;
}
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2)
+ uc->chan = uc->rchan;
return 0;
}
@@ -2379,16 +2395,26 @@ int bcdma_setup_resources(struct udma_dev *ud)
ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
+ bitmap_zero(ud->bchan_map, ud->bchan_cnt);
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);
+ bitmap_zero(ud->tchan_map, ud->tchan_cnt);
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);
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ ud->rchan_map = ud->tchan_map;
+ ud->rchans = ud->tchans;
+ ud->chan_map = ud->tchan_map;
+ ud->chans = ud->tchans;
+ } else {
+ ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+ sizeof(unsigned long), GFP_KERNEL);
+ bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+ 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),
@@ -2472,6 +2498,7 @@ int setup_resources(struct udma_dev *ud)
ret = ud->udma_setup_resources(ud);
break;
case DMA_TYPE_BCDMA:
+ case DMA_TYPE_BCDMA_V2:
ret = bcdma_setup_resources(ud);
break;
case DMA_TYPE_PKTDMA:
@@ -2484,11 +2511,18 @@ int setup_resources(struct udma_dev *ud)
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 (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ ch_count = ud->bchan_cnt + ud->tchan_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);
+ } else {
+ 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)
return -ENODEV;
@@ -2520,6 +2554,15 @@ int setup_resources(struct udma_dev *ud)
ud->rchan_cnt - bitmap_weight(ud->rchan_map,
ud->rchan_cnt));
break;
+ case DMA_TYPE_BCDMA_V2:
+ dev_info(dev,
+ "Channels: %d (bchan: %u, chan: %u)\n",
+ ch_count,
+ ud->bchan_cnt - bitmap_weight(ud->bchan_map,
+ ud->bchan_cnt),
+ ud->chan_cnt - bitmap_weight(ud->chan_map,
+ ud->chan_cnt));
+ break;
case DMA_TYPE_PKTDMA:
dev_info(dev,
"Channels: %d (tchan: %u, rchan: %u)\n",
diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
new file mode 100644
index 0000000000000..af06d25fd598b
--- /dev/null
+++ b/drivers/dma/ti/k3-udma-v2.c
@@ -0,0 +1,1283 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Derived from K3 UDMA driver (k3-udma.c)
+ * Copyright (C) 2024-2025 Texas Instruments Incorporated - http://www.ti.com
+ * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
+ * Author: Sai Sree Kartheek Adivi <s-adivi@ti.com>
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/list.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/sys_soc.h>
+#include <linux/of.h>
+#include <linux/of_dma.h>
+#include <linux/of_irq.h>
+#include <linux/workqueue.h>
+#include <linux/completion.h>
+#include <linux/iopoll.h>
+#include <linux/soc/ti/k3-ringacc.h>
+
+#include "../virt-dma.h"
+#include "k3-udma.h"
+#include "k3-psil-priv.h"
+
+static const char * const v2_mmr_names[] = {
+ [V2_MMR_GCFG] = "gcfg",
+ [V2_MMR_BCHANRT] = "bchanrt",
+ [V2_MMR_CHANRT] = "chanrt",
+};
+
+static int udma_v2_check_chan_autopair_completion(struct udma_chan *uc)
+{
+ u32 val;
+
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
+ if (val & UDMA_CHAN_RT_CTL_PAIR_TIMEOUT)
+ return -ETIMEDOUT;
+ else if (val & UDMA_CHAN_RT_CTL_PAIR_COMPLETE)
+ return 1;
+
+ /* timeout didn't occur and also pairing didn't happen yet. */
+ return 0;
+}
+
+static bool udma_v2_is_chan_paused(struct udma_chan *uc)
+{
+ u32 val, pause_mask;
+
+ if (uc->config.dir == DMA_MEM_TO_MEM) {
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
+ pause_mask = UDMA_CHAN_RT_CTL_PAUSE;
+ } else {
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_PDMA_STATE_REG);
+ pause_mask = UDMA_CHAN_RT_PDMA_STATE_PAUSE;
+ }
+
+ if (val & pause_mask)
+ return true;
+
+ return false;
+}
+
+static void udma_v2_decrement_byte_counters(struct udma_chan *uc, u32 val)
+{
+ udma_chanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
+ udma_chanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
+ if (uc->config.ep_type != PSIL_EP_NATIVE)
+ udma_chanrt_write(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG, val);
+}
+
+static void udma_v2_reset_counters(struct udma_chan *uc)
+{
+ u32 val;
+
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+ udma_chanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
+
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG);
+ udma_chanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
+
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_PCNT_REG);
+ udma_chanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val);
+
+ if (!uc->bchan) {
+ val = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
+ udma_chanrt_write(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG, val);
+ }
+}
+
+static int udma_v2_reset_chan(struct udma_chan *uc, bool hard)
+{
+ udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0);
+
+ /* Reset all counters */
+ udma_v2_reset_counters(uc);
+
+ /* Hard reset: re-initialize the channel to reset */
+ if (hard) {
+ struct udma_chan_config ucc_backup;
+ int ret;
+
+ memcpy(&ucc_backup, &uc->config, sizeof(uc->config));
+ uc->ud->ddev.device_free_chan_resources(&uc->vc.chan);
+
+ /* restore the channel configuration */
+ memcpy(&uc->config, &ucc_backup, sizeof(uc->config));
+ ret = uc->ud->ddev.device_alloc_chan_resources(&uc->vc.chan);
+ if (ret)
+ return ret;
+
+ /*
+ * Setting forced teardown after forced reset helps recovering
+ * the rchan.
+ */
+ if (uc->config.dir == DMA_DEV_TO_MEM)
+ udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN |
+ UDMA_CHAN_RT_CTL_FTDOWN,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN |
+ UDMA_CHAN_RT_CTL_FTDOWN);
+ }
+ uc->state = UDMA_CHAN_IS_IDLE;
+
+ return 0;
+}
+
+static int udma_v2_start(struct udma_chan *uc)
+{
+ struct virt_dma_desc *vd = vchan_next_desc(&uc->vc);
+ struct udma_dev *ud = uc->ud;
+ int status, ret;
+
+ if (!vd) {
+ uc->desc = NULL;
+ return -ENOENT;
+ }
+
+ list_del(&vd->node);
+
+ uc->desc = to_udma_desc(&vd->tx);
+
+ /* Channel is already running and does not need reconfiguration */
+ if (udma_is_chan_running(uc) && !udma_chan_needs_reconfiguration(uc)) {
+ udma_start_desc(uc);
+ goto out;
+ }
+
+ /* Make sure that we clear the teardown bit, if it is set */
+ ud->reset_chan(uc, false);
+
+ /* Push descriptors before we start the channel */
+ udma_start_desc(uc);
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ /* Config remote TR */
+ if (uc->config.ep_type == PSIL_EP_PDMA_XY) {
+ u32 val = PDMA_STATIC_TR_Y(uc->desc->static_tr.elcnt) |
+ PDMA_STATIC_TR_X(uc->desc->static_tr.elsize);
+ const struct udma_match_data *match_data =
+ uc->ud->match_data;
+
+ if (uc->config.enable_acc32)
+ val |= PDMA_STATIC_TR_XY_ACC32;
+ if (uc->config.enable_burst)
+ val |= PDMA_STATIC_TR_XY_BURST;
+
+ udma_chanrt_write(uc,
+ UDMA_CHAN_RT_STATIC_TR_XY_REG,
+ val);
+
+ udma_chanrt_write(uc,
+ UDMA_CHAN_RT_STATIC_TR_Z_REG,
+ PDMA_STATIC_TR_Z(uc->desc->static_tr.bstcnt,
+ match_data->statictr_z_mask));
+
+ /* save the current staticTR configuration */
+ memcpy(&uc->static_tr, &uc->desc->static_tr,
+ sizeof(uc->static_tr));
+ }
+
+ udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_AUTOPAIR);
+
+ /* Poll for autopair completion */
+ ret = read_poll_timeout_atomic(udma_v2_check_chan_autopair_completion,
+ status, status != 0, 100, 500, false, uc);
+
+ if (status <= 0)
+ return ret;
+
+ break;
+ case DMA_MEM_TO_DEV:
+ /* Config remote TR */
+ if (uc->config.ep_type == PSIL_EP_PDMA_XY) {
+ u32 val = PDMA_STATIC_TR_Y(uc->desc->static_tr.elcnt) |
+ PDMA_STATIC_TR_X(uc->desc->static_tr.elsize);
+
+ if (uc->config.enable_acc32)
+ val |= PDMA_STATIC_TR_XY_ACC32;
+ if (uc->config.enable_burst)
+ val |= PDMA_STATIC_TR_XY_BURST;
+
+ udma_chanrt_write(uc,
+ UDMA_CHAN_RT_STATIC_TR_XY_REG,
+ val);
+
+ /* save the current staticTR configuration */
+ memcpy(&uc->static_tr, &uc->desc->static_tr,
+ sizeof(uc->static_tr));
+ }
+
+ udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_AUTOPAIR);
+
+ /* Poll for autopair completion */
+ ret = read_poll_timeout_atomic(udma_v2_check_chan_autopair_completion,
+ status, status != 0, 100, 500, false, uc);
+
+ if (status <= 0)
+ return -ETIMEDOUT;
+
+ break;
+ case DMA_MEM_TO_MEM:
+ udma_bchanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN);
+ udma_bchanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN);
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ uc->state = UDMA_CHAN_IS_ACTIVE;
+out:
+
+ return 0;
+}
+
+static int udma_v2_stop(struct udma_chan *uc)
+{
+ uc->state = UDMA_CHAN_IS_TERMINATING;
+ reinit_completion(&uc->teardown_completed);
+
+ if (uc->config.dir == DMA_DEV_TO_MEM) {
+ if (!uc->cyclic && !uc->desc)
+ udma_push_to_ring(uc, -1);
+ }
+
+ udma_chanrt_write(uc, UDMA_CHAN_RT_PEER_REG(8), UDMA_CHAN_RT_PEER_REG8_FLUSH);
+ udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN,
+ UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN);
+
+ return 0;
+}
+
+static irqreturn_t udma_v2_udma_irq_handler(int irq, void *data)
+{
+ struct udma_chan *uc = data;
+ struct udma_dev *ud = uc->ud;
+ struct udma_desc *d;
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ k3_ringacc_ring_clear_irq(uc->rflow->r_ring);
+ break;
+ case DMA_MEM_TO_DEV:
+ case DMA_MEM_TO_MEM:
+ k3_ringacc_ring_clear_irq(uc->tchan->tc_ring);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ spin_lock(&uc->vc.lock);
+ d = uc->desc;
+ if (d) {
+ d->tr_idx = (d->tr_idx + 1) % d->sglen;
+
+ if (uc->cyclic) {
+ vchan_cyclic_callback(&d->vd);
+ } else {
+ /* TODO: figure out the real amount of data */
+ ud->decrement_byte_counters(uc, d->residue);
+ ud->start(uc);
+ vchan_cookie_complete(&d->vd);
+ }
+ }
+
+ spin_unlock(&uc->vc.lock);
+
+ return IRQ_HANDLED;
+}
+
+static irqreturn_t udma_v2_ring_irq_handler(int irq, void *data)
+{
+ struct udma_chan *uc = data;
+ struct udma_dev *ud = uc->ud;
+ struct udma_desc *d;
+ dma_addr_t paddr = 0;
+ u32 intr_status, reg;
+
+ switch (uc->config.dir) {
+ case DMA_DEV_TO_MEM:
+ intr_status = k3_ringacc_ring_get_irq_status(uc->rflow->r_ring);
+ break;
+ case DMA_MEM_TO_DEV:
+ case DMA_MEM_TO_MEM:
+ intr_status = k3_ringacc_ring_get_irq_status(uc->tchan->tc_ring);
+ break;
+ default:
+ return -ENOENT;
+ }
+
+ reg = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
+
+ if (intr_status & K3_RINGACC_RT_INT_STATUS_TR) {
+ /* check teardown status */
+ if ((reg & UDMA_CHAN_RT_CTL_TDOWN) && !(reg & UDMA_CHAN_RT_CTL_EN))
+ complete_all(&uc->teardown_completed);
+ return udma_v2_udma_irq_handler(irq, data);
+ }
+
+ if (udma_pop_from_ring(uc, &paddr) || !paddr)
+ return IRQ_HANDLED;
+
+ spin_lock(&uc->vc.lock);
+
+ /* Teardown completion message */
+ if (cppi5_desc_is_tdcm(paddr)) {
+ complete_all(&uc->teardown_completed);
+
+ if (uc->terminated_desc) {
+ udma_desc_free(&uc->terminated_desc->vd);
+ uc->terminated_desc = NULL;
+ }
+
+ if (!uc->desc)
+ ud->start(uc);
+
+ goto out;
+ }
+
+ d = udma_udma_desc_from_paddr(uc, paddr);
+
+ if (d) {
+ dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
+ d->desc_idx);
+ if (desc_paddr != paddr) {
+ dev_err(uc->ud->dev, "not matching descriptors!\n");
+ goto out;
+ }
+
+ if (d == uc->desc) {
+ /* active descriptor */
+ if (uc->cyclic) {
+ udma_cyclic_packet_elapsed(uc);
+ vchan_cyclic_callback(&d->vd);
+ } else {
+ if (udma_is_desc_really_done(uc, d)) {
+ ud->decrement_byte_counters(uc, d->residue);
+ ud->start(uc);
+ vchan_cookie_complete(&d->vd);
+ } else {
+ schedule_delayed_work(&uc->tx_drain.work,
+ 0);
+ }
+ }
+ } else {
+ /*
+ * terminated descriptor, mark the descriptor as
+ * completed to update the channel's cookie marker
+ */
+ dma_cookie_complete(&d->vd.tx);
+ }
+ }
+out:
+ spin_unlock(&uc->vc.lock);
+
+ return IRQ_HANDLED;
+}
+
+static int bcdma_v2_get_bchan(struct udma_chan *uc)
+{
+ struct udma_dev *ud = uc->ud;
+ enum udma_tp_level tpl;
+ int ret;
+
+ 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, uc->id);
+ if (IS_ERR(uc->bchan)) {
+ ret = PTR_ERR(uc->bchan);
+ uc->bchan = NULL;
+ return ret;
+ }
+ uc->chan = uc->bchan;
+ uc->tchan = uc->bchan;
+
+ return 0;
+}
+
+static int bcdma_v2_alloc_bchan_resources(struct udma_chan *uc)
+{
+ struct k3_ring_cfg ring_cfg;
+ struct udma_dev *ud = uc->ud;
+ int ret;
+
+ ret = bcdma_v2_get_bchan(uc);
+ if (ret)
+ return ret;
+
+ ret = k3_ringacc_request_rings_pair(ud->ringacc, ud->match_data->chan_cnt + uc->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 int udma_v2_alloc_tx_resources(struct udma_chan *uc)
+{
+ struct k3_ring_cfg ring_cfg;
+ struct udma_dev *ud = uc->ud;
+ struct udma_tchan *tchan;
+ int ring_idx, ret;
+
+ ret = udma_get_tchan(uc);
+ if (ret)
+ return ret;
+
+ tchan = uc->tchan;
+ if (tchan->tflow_id >= 0)
+ ring_idx = tchan->tflow_id;
+ else
+ ring_idx = 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;
+ }
+
+ 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, 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;
+
+ return 0;
+
+err_ringcfg:
+ k3_ringacc_ring_free(uc->tchan->tc_ring);
+ uc->tchan->tc_ring = NULL;
+ k3_ringacc_ring_free(uc->tchan->t_ring);
+ uc->tchan->t_ring = NULL;
+err_ring:
+ udma_put_tchan(uc);
+
+ return ret;
+}
+
+static int udma_v2_alloc_rx_resources(struct udma_chan *uc)
+{
+ struct udma_dev *ud = uc->ud;
+ struct k3_ring_cfg ring_cfg;
+ struct udma_rflow *rflow;
+ 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->config.dir == DMA_MEM_TO_MEM)
+ return 0;
+
+ 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;
+ if (ud->tflow_cnt)
+ fd_ring_id = ud->tflow_cnt + rflow->id;
+ else
+ fd_ring_id = uc->rchan->id;
+ ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1,
+ &rflow->fd_ring, &rflow->r_ring);
+ if (ret) {
+ ret = -EBUSY;
+ goto err_ring;
+ }
+
+ memset(&ring_cfg, 0, sizeof(ring_cfg));
+
+ ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
+ ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
+ ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
+
+ 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);
+
+ if (ret)
+ goto err_ringcfg;
+
+ return 0;
+
+err_ringcfg:
+ k3_ringacc_ring_free(rflow->r_ring);
+ rflow->r_ring = NULL;
+ k3_ringacc_ring_free(rflow->fd_ring);
+ rflow->fd_ring = NULL;
+err_ring:
+ udma_put_rflow(uc);
+err_rflow:
+ udma_put_rchan(uc);
+
+ return ret;
+}
+
+static int bcdma_v2_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ struct udma_dev *ud = to_udma_dev(chan->device);
+ u32 irq_ring_idx;
+ __be32 addr[2] = {0, 0};
+ struct of_phandle_args out_irq;
+ int ret;
+
+ /* Only TR mode is supported */
+ uc->config.pkt_mode = false;
+
+ /*
+ * 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_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);
+
+ ret = bcdma_v2_alloc_bchan_resources(uc);
+ if (ret)
+ return ret;
+
+ irq_ring_idx = ud->match_data->chan_cnt + uc->id;
+ break;
+ case DMA_MEM_TO_DEV:
+ /* Slave transfer synchronized - mem to dev (TX) transfer */
+ dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+ uc->id);
+
+ ret = udma_v2_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;
+
+ break;
+ case DMA_DEV_TO_MEM:
+ /* Slave transfer synchronized - dev to mem (RX) transfer */
+ dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+ uc->id);
+
+ ret = udma_v2_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;
+
+ 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);
+ ud->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;
+ ret = -ENOMEM;
+ goto err_res_free;
+ }
+
+ uc->use_dma_pool = true;
+ } else if (uc->config.dir != DMA_MEM_TO_MEM) {
+ uc->psil_paired = true;
+ }
+
+ out_irq.np = dev_of_node(ud->dev);
+ out_irq.args_count = 1;
+ out_irq.args[0] = irq_ring_idx;
+ ret = of_irq_parse_raw(addr, &out_irq);
+ if (ret)
+ return ret;
+
+ uc->irq_num_ring = irq_create_of_mapping(&out_irq);
+
+ ret = devm_request_irq(ud->dev, uc->irq_num_ring, udma_v2_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;
+ }
+
+ 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_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 enum dma_status udma_v2_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *txstate)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ enum dma_status ret;
+ unsigned long flags;
+
+ spin_lock_irqsave(&uc->vc.lock, flags);
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+
+ if (!udma_is_chan_running(uc))
+ ret = DMA_COMPLETE;
+
+ if (ret == DMA_IN_PROGRESS && udma_v2_is_chan_paused(uc))
+ ret = DMA_PAUSED;
+
+ if (ret == DMA_COMPLETE || !txstate)
+ goto out;
+
+ if (uc->desc && uc->desc->vd.tx.cookie == cookie) {
+ u32 peer_bcnt = 0;
+ u32 bcnt = 0;
+ u32 residue = uc->desc->residue;
+ u32 delay = 0;
+
+ if (uc->desc->dir == DMA_MEM_TO_DEV) {
+ bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG);
+
+ if (uc->config.ep_type != PSIL_EP_NATIVE) {
+ peer_bcnt = udma_chanrt_read(uc, 0x810);
+
+ if (bcnt > peer_bcnt)
+ delay = bcnt - peer_bcnt;
+ }
+ } else if (uc->desc->dir == DMA_DEV_TO_MEM) {
+ bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+
+ if (uc->config.ep_type != PSIL_EP_NATIVE) {
+ peer_bcnt = udma_chanrt_read(uc, 0x810);
+
+ if (peer_bcnt > bcnt)
+ delay = peer_bcnt - bcnt;
+ }
+ } else {
+ bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
+ }
+
+ if (bcnt && !(bcnt % uc->desc->residue))
+ residue = 0;
+ else
+ residue -= bcnt % uc->desc->residue;
+
+ if (!residue && (uc->config.dir == DMA_DEV_TO_MEM || !delay)) {
+ ret = DMA_COMPLETE;
+ delay = 0;
+ }
+
+ dma_set_residue(txstate, residue);
+ dma_set_in_flight_bytes(txstate, delay);
+
+ } else {
+ ret = DMA_COMPLETE;
+ }
+
+out:
+ spin_unlock_irqrestore(&uc->vc.lock, flags);
+ return ret;
+}
+
+static int udma_v2_pause(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+
+ /* pause the channel */
+ udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_PAUSE, UDMA_CHAN_RT_CTL_PAUSE);
+
+ return 0;
+}
+
+static int udma_v2_resume(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+
+ /* resume the channel */
+ udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_PAUSE, 0);
+
+ return 0;
+}
+
+static struct platform_driver bcdma_v2_driver;
+
+static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
+{
+ struct udma_chan_config *ucc;
+ struct psil_endpoint_config *ep_config;
+ struct udma_v2_filter_param *filter_param;
+ struct udma_chan *uc;
+ struct udma_dev *ud;
+
+ if (chan->device->dev->driver != &bcdma_v2_driver.driver)
+ return false;
+
+ uc = to_udma_chan(chan);
+ ucc = &uc->config;
+ ud = uc->ud;
+ filter_param = param;
+
+ 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->asel = filter_param->asel;
+ ucc->tr_trigger_type = filter_param->tr_trigger_type;
+
+ 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 {
+ ucc->dir = DMA_DEV_TO_MEM;
+ }
+
+ ep_config = psil_get_ep_config(ucc->remote_thread_id);
+ if (IS_ERR(ep_config)) {
+ dev_err(ud->dev, "No configuration for 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;
+ }
+
+ ucc->pkt_mode = ep_config->pkt_mode;
+ ucc->channel_tpl = ep_config->channel_tpl;
+ ucc->notdpkt = ep_config->notdpkt;
+ ucc->ep_type = ep_config->ep_type;
+
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2 &&
+ 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;
+ }
+
+ ucc->needs_epib = ep_config->needs_epib;
+ ucc->psd_size = ep_config->psd_size;
+ ucc->metadata_size =
+ (ucc->needs_epib ? CPPI5_INFO0_HDESC_EPIB_SIZE : 0) +
+ ucc->psd_size;
+
+ if (ucc->ep_type != PSIL_EP_NATIVE) {
+ const struct udma_match_data *match_data = ud->match_data;
+
+ if ((match_data->flags & UDMA_FLAG_PDMA_ACC32) && ep_config->pdma_acc32)
+ ucc->enable_acc32 = true;
+ else
+ ucc->enable_acc32 = false;
+
+ if ((match_data->flags & UDMA_FLAG_PDMA_BURST) && ep_config->pdma_burst)
+ ucc->enable_burst = true;
+ else
+ ucc->enable_burst = false;
+ }
+ if (ucc->pkt_mode)
+ ucc->hdesc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
+ ucc->metadata_size, ud->desc_align);
+
+ dev_dbg(ud->dev, "chan%d: Remote thread: 0x%04x (%s)\n", uc->id,
+ 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_v2_of_xlate(struct of_phandle_args *dma_spec,
+ struct of_dma *ofdma)
+{
+ struct udma_dev *ud = ofdma->of_dma_data;
+ dma_cap_mask_t mask = ud->ddev.cap_mask;
+ struct udma_v2_filter_param filter_param;
+ struct dma_chan *chan;
+
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ if (dma_spec->args_count != 4)
+ return NULL;
+
+ filter_param.tr_trigger_type = dma_spec->args[0];
+ filter_param.trigger_param = dma_spec->args[1];
+ filter_param.remote_thread_id = dma_spec->args[2];
+ filter_param.asel = dma_spec->args[3];
+ } 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)
+ filter_param.asel = dma_spec->args[1];
+ else
+ filter_param.asel = 0;
+ }
+
+ chan = __dma_request_channel(&mask, udma_v2_dma_filter_fn, &filter_param,
+ ofdma->of_node);
+ if (!chan) {
+ dev_err(ud->dev, "get channel fail in %s.\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ return chan;
+}
+
+static struct udma_match_data bcdma_v2_am62l_data = {
+ .type = DMA_TYPE_BCDMA_V2,
+ .psil_base = 0x2000, /* for tchan and rchan, not applicable to bchan */
+ .enable_memcpy_support = true, /* Supported via bchan */
+ .flags = UDMA_FLAGS_J7_CLASS,
+ .statictr_z_mask = GENMASK(23, 0),
+ .burst_size = {
+ TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */
+ 0, /* No H Channels */
+ 0, /* No UH Channels */
+ },
+ .bchan_cnt = 16,
+ .chan_cnt = 128,
+ .tchan_cnt = 128,
+ .rchan_cnt = 128,
+};
+
+static const struct of_device_id udma_of_match[] = {
+ {
+ .compatible = "ti,am62l-dmss-bcdma",
+ .data = &bcdma_v2_am62l_data,
+ },
+ { /* Sentinel */ },
+};
+
+static const struct soc_device_attribute k3_soc_devices[] = {
+ { .family = "AM62LX", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, udma_of_match);
+
+static int udma_v2_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
+{
+ int i;
+
+ ud->mmrs[V2_MMR_GCFG] = devm_platform_ioremap_resource_byname(pdev,
+ v2_mmr_names[V2_MMR_GCFG]);
+ if (IS_ERR(ud->mmrs[V2_MMR_GCFG]))
+ return PTR_ERR(ud->mmrs[V2_MMR_GCFG]);
+
+ ud->bchan_cnt = ud->match_data->bchan_cnt;
+ /* There are no tchan and rchan in BCDMA_V2.
+ * Duplicate chan as tchan and rchan to keep the common code
+ * in k3-udma-common.c functional for BCDMA_V2.
+ */
+ ud->chan_cnt = ud->match_data->chan_cnt;
+ ud->tchan_cnt = ud->match_data->chan_cnt;
+ ud->rchan_cnt = ud->match_data->chan_cnt;
+ ud->rflow_cnt = ud->chan_cnt;
+
+ for (i = 1; i < V2_MMR_LAST; i++) {
+ if (i == V2_MMR_BCHANRT && ud->bchan_cnt == 0)
+ continue;
+ if (i == V2_MMR_CHANRT && ud->chan_cnt == 0)
+ continue;
+
+ ud->mmrs[i] = devm_platform_ioremap_resource_byname(pdev, v2_mmr_names[i]);
+ if (IS_ERR(ud->mmrs[i]))
+ return PTR_ERR(ud->mmrs[i]);
+ }
+
+ return 0;
+}
+
+static int udma_v2_probe(struct platform_device *pdev)
+{
+ const struct soc_device_attribute *soc;
+ struct device *dev = &pdev->dev;
+ struct udma_dev *ud;
+ const struct of_device_id *match;
+ int i, ret;
+ int ch_count;
+
+ ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48));
+ if (ret)
+ dev_err(dev, "failed to set dma mask stuff\n");
+
+ ud = devm_kzalloc(dev, sizeof(*ud), GFP_KERNEL);
+ if (!ud)
+ return -ENOMEM;
+
+ 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;
+
+ ud->soc_data = ud->match_data->soc_data;
+ if (!ud->soc_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;
+ }
+ // Setup function pointers
+ ud->start = udma_v2_start;
+ ud->stop = udma_v2_stop;
+ ud->reset_chan = udma_v2_reset_chan;
+ ud->decrement_byte_counters = udma_v2_decrement_byte_counters;
+ ud->bcdma_setup_sci_resources = NULL;
+
+ ret = udma_v2_get_mmrs(pdev, ud);
+ if (ret)
+ return ret;
+
+ struct k3_ringacc_init_data ring_init_data = {0};
+
+ ring_init_data.num_rings = ud->bchan_cnt + ud->chan_cnt;
+
+ ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data);
+
+ if (IS_ERR(ud->ringacc))
+ return PTR_ERR(ud->ringacc);
+
+ dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask);
+
+ dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+ ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
+
+ ud->ddev.device_config = udma_slave_config;
+ ud->ddev.device_prep_slave_sg = udma_prep_slave_sg;
+ ud->ddev.device_issue_pending = udma_issue_pending;
+ ud->ddev.device_tx_status = udma_v2_tx_status;
+ ud->ddev.device_pause = udma_v2_pause;
+ ud->ddev.device_resume = udma_v2_resume;
+ ud->ddev.device_terminate_all = udma_terminate_all;
+ ud->ddev.device_synchronize = udma_synchronize;
+#ifdef CONFIG_DEBUG_FS
+ ud->ddev.dbg_summary_show = udma_dbg_summary_show;
+#endif
+
+ ud->ddev.device_alloc_chan_resources =
+ bcdma_v2_alloc_chan_resources;
+
+ 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);
+ ud->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+ ud->ddev.desc_metadata_modes = DESC_METADATA_CLIENT |
+ DESC_METADATA_ENGINE;
+ 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);
+ }
+
+ ud->ddev.dev = dev;
+ ud->dev = dev;
+ ud->psil_base = ud->match_data->psil_base;
+
+ INIT_LIST_HEAD(&ud->ddev.channels);
+ INIT_LIST_HEAD(&ud->desc_to_purge);
+
+ ch_count = setup_resources(ud);
+ if (ch_count <= 0)
+ return ch_count;
+
+ spin_lock_init(&ud->lock);
+ INIT_WORK(&ud->purge_work, udma_purge_desc_work);
+
+ ud->desc_align = 64;
+ if (ud->desc_align < dma_get_cache_alignment())
+ ud->desc_align = dma_get_cache_alignment();
+
+ ret = udma_setup_rx_flush(ud);
+ 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[V2_MMR_BCHANRT] + i * 0x1000;
+ }
+
+ for (i = 0; i < ud->tchan_cnt; i++) {
+ struct udma_tchan *tchan = &ud->tchans[i];
+
+ tchan->id = i;
+ tchan->reg_rt = ud->mmrs[V2_MMR_CHANRT] + i * 0x1000;
+ }
+
+ for (i = 0; i < ud->rchan_cnt; i++) {
+ struct udma_rchan *rchan = &ud->rchans[i];
+
+ rchan->id = i;
+ rchan->reg_rt = ud->mmrs[V2_MMR_CHANRT] + i * 0x1000;
+ }
+
+ for (i = 0; i < ud->rflow_cnt; i++) {
+ struct udma_rflow *rflow = &ud->rflows[i];
+
+ rflow->id = i;
+ rflow->reg_rt = ud->rflow_rt + i * 0x2000;
+ }
+
+ for (i = 0; i < ch_count; i++) {
+ struct udma_chan *uc = &ud->channels[i];
+
+ 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);
+
+ vchan_init(&uc->vc, &ud->ddev);
+ /* Use custom vchan completion handling */
+ tasklet_setup(&uc->vc.task, udma_vchan_complete);
+ init_completion(&uc->teardown_completed);
+ INIT_DELAYED_WORK(&uc->tx_drain.work, udma_check_tx_completion);
+ }
+
+ /* Configure the copy_align to the maximum burst size the device supports */
+ ud->ddev.copy_align = udma_get_copy_align(ud);
+
+ ret = dma_async_device_register(&ud->ddev);
+ if (ret) {
+ dev_err(dev, "failed to register slave DMA engine: %d\n", ret);
+ return ret;
+ }
+
+ platform_set_drvdata(pdev, ud);
+
+ ret = of_dma_controller_register(dev->of_node, udma_v2_of_xlate, ud);
+ if (ret) {
+ dev_err(dev, "failed to register of_dma controller\n");
+ dma_async_device_unregister(&ud->ddev);
+ }
+
+ return ret;
+}
+
+static int __maybe_unused udma_v2_pm_suspend(struct device *dev)
+{
+ struct udma_dev *ud = dev_get_drvdata(dev);
+ struct dma_device *dma_dev = &ud->ddev;
+ struct dma_chan *chan;
+ struct udma_chan *uc;
+
+ list_for_each_entry(chan, &dma_dev->channels, device_node) {
+ if (chan->client_count) {
+ uc = to_udma_chan(chan);
+ /* backup the channel configuration */
+ memcpy(&uc->backup_config, &uc->config,
+ sizeof(struct udma_chan_config));
+ dev_dbg(dev, "Suspending channel %s\n",
+ dma_chan_name(chan));
+ ud->ddev.device_free_chan_resources(chan);
+ }
+ }
+
+ return 0;
+}
+
+static int __maybe_unused udma_v2_pm_resume(struct device *dev)
+{
+ struct udma_dev *ud = dev_get_drvdata(dev);
+ struct dma_device *dma_dev = &ud->ddev;
+ struct dma_chan *chan;
+ struct udma_chan *uc;
+ int ret;
+
+ list_for_each_entry(chan, &dma_dev->channels, device_node) {
+ if (chan->client_count) {
+ uc = to_udma_chan(chan);
+ /* restore the channel configuration */
+ memcpy(&uc->config, &uc->backup_config,
+ sizeof(struct udma_chan_config));
+ dev_dbg(dev, "Resuming channel %s\n",
+ dma_chan_name(chan));
+ ret = ud->ddev.device_alloc_chan_resources(chan);
+ if (ret)
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static const struct dev_pm_ops udma_pm_ops = {
+ SET_LATE_SYSTEM_SLEEP_PM_OPS(udma_v2_pm_suspend, udma_v2_pm_resume)
+};
+
+static struct platform_driver bcdma_v2_driver = {
+ .driver = {
+ .name = "ti-udma-v2",
+ .of_match_table = udma_of_match,
+ .suppress_bind_attrs = true,
+ .pm = &udma_pm_ops,
+ },
+ .probe = udma_v2_probe,
+};
+
+module_platform_driver(bcdma_v2_driver);
+MODULE_DESCRIPTION("Texas Instruments K3 UDMA v2 support");
+MODULE_LICENSE("GPL");
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 6a95eb1ece064..ca726ea9864ae 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -28,6 +28,11 @@
#define UDMA_CHAN_RT_SWTRIG_REG 0x8
#define UDMA_CHAN_RT_STDATA_REG 0x80
+#define UDMA_CHAN_RT_STATIC_TR_XY_REG 0x800
+#define UDMA_CHAN_RT_STATIC_TR_Z_REG 0x804
+#define UDMA_CHAN_RT_PERIPH_BCNT_REG 0x810
+#define UDMA_CHAN_RT_PDMA_STATE_REG 0x80c
+
#define UDMA_CHAN_RT_PEER_REG(i) (0x200 + ((i) * 0x4))
#define UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG \
UDMA_CHAN_RT_PEER_REG(0) /* PSI-L: 0x400 */
@@ -67,8 +72,16 @@
#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_AUTOPAIR BIT(23)
+#define UDMA_CHAN_RT_CTL_PAIR_TIMEOUT BIT(17)
+#define UDMA_CHAN_RT_CTL_PAIR_COMPLETE BIT(16)
#define UDMA_CHAN_RT_CTL_ERROR BIT(0)
+/* UDMA_CHAN_RT_PDMA_STATE_REG */
+#define UDMA_CHAN_RT_PDMA_STATE_IN_EVT BIT(31)
+#define UDMA_CHAN_RT_PDMA_STATE_TDOWN BIT(30)
+#define UDMA_CHAN_RT_PDMA_STATE_PAUSE BIT(29)
+
/* UDMA_CHAN_RT_PEER_RT_EN_REG */
#define UDMA_PEER_RT_EN_ENABLE BIT(31)
#define UDMA_PEER_RT_EN_TEARDOWN BIT(30)
@@ -99,6 +112,9 @@
*/
#define PDMA_STATIC_TR_Z(x, mask) ((x) & (mask))
+/* UDMA_CHAN_RT_PEER_REG(8) */
+#define UDMA_CHAN_RT_PEER_REG8_FLUSH 0x09000000
+
/* Address Space Select */
#define K3_ADDRESS_ASEL_SHIFT 48
@@ -202,6 +218,8 @@ enum k3_dma_type {
DMA_TYPE_UDMA = 0,
DMA_TYPE_BCDMA,
DMA_TYPE_PKTDMA,
+ DMA_TYPE_BCDMA_V2,
+ DMA_TYPE_PKTDMA_V2,
};
enum udma_mmr {
@@ -212,6 +230,13 @@ enum udma_mmr {
MMR_LAST,
};
+enum udma_v2_mmr {
+ V2_MMR_GCFG = 0,
+ V2_MMR_BCHANRT,
+ V2_MMR_CHANRT,
+ V2_MMR_LAST,
+};
+
struct udma_filter_param {
int remote_thread_id;
u32 atype;
@@ -219,6 +244,13 @@ struct udma_filter_param {
u32 tr_trigger_type;
};
+struct udma_v2_filter_param {
+ u32 tr_trigger_type;
+ u32 trigger_param;
+ int remote_thread_id;
+ u32 asel;
+};
+
struct udma_tchan {
void __iomem *reg_rt;
@@ -230,17 +262,13 @@ struct udma_tchan {
};
#define udma_bchan udma_tchan
+#define udma_rchan udma_tchan
struct udma_rflow {
int id;
struct k3_ring *fd_ring; /* Free Descriptor ring */
struct k3_ring *r_ring; /* Receive ring */
-};
-
-struct udma_rchan {
void __iomem *reg_rt;
-
- int id;
};
struct udma_oes_offsets {
@@ -268,6 +296,12 @@ struct udma_match_data {
u32 statictr_z_mask;
u8 burst_size[3];
struct udma_soc_data *soc_data;
+ u32 bchan_cnt;
+ u32 chan_cnt;
+ u32 tchan_cnt;
+ u32 rchan_cnt;
+ u32 tflow_cnt;
+ u32 rflow_cnt;
};
struct udma_soc_data {
@@ -302,6 +336,7 @@ struct udma_dev {
struct dma_device ddev;
struct device *dev;
void __iomem *mmrs[MMR_LAST];
+ void __iomem *rflow_rt;
const struct udma_match_data *match_data;
const struct udma_soc_data *soc_data;
@@ -322,12 +357,14 @@ struct udma_dev {
struct udma_rx_flush rx_flush;
int bchan_cnt;
+ int chan_cnt;
int tchan_cnt;
int echan_cnt;
int rchan_cnt;
int rflow_cnt;
int tflow_cnt;
unsigned long *bchan_map;
+ unsigned long *chan_map;
unsigned long *tchan_map;
unsigned long *rchan_map;
unsigned long *rflow_gp_map;
@@ -336,6 +373,7 @@ struct udma_dev {
unsigned long *tflow_map;
struct udma_bchan *bchans;
+ struct udma_tchan *chans;
struct udma_tchan *tchans;
struct udma_rchan *rchans;
struct udma_rflow *rflows;
@@ -430,6 +468,7 @@ struct udma_chan {
char *name;
struct udma_bchan *bchan;
+ struct udma_tchan *chan;
struct udma_tchan *tchan;
struct udma_rchan *rchan;
struct udma_rflow *rflow;
@@ -499,51 +538,33 @@ static inline void udma_update_bits(void __iomem *base, int reg,
writel(tmp, base + reg);
}
-/* TCHANRT */
-static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
-{
- if (!uc->tchan)
- return 0;
- return udma_read(uc->tchan->reg_rt, reg);
+#define _UDMA_REG_ACCESS(channel) \
+static inline u32 udma_##channel##rt_read(struct udma_chan *uc, int reg) \
+{ \
+ if (!uc->channel) \
+ return 0; \
+ return udma_read(uc->channel->reg_rt, reg); \
+} \
+\
+static inline void udma_##channel##rt_write(struct udma_chan *uc, int reg, u32 val) \
+{ \
+ if (!uc->channel) \
+ return; \
+ udma_write(uc->channel->reg_rt, reg, val); \
+} \
+\
+static inline void udma_##channel##rt_update_bits(struct udma_chan *uc, int reg, \
+ u32 mask, u32 val) \
+{ \
+ if (!uc->channel) \
+ return; \
+ udma_update_bits(uc->channel->reg_rt, reg, mask, val); \
}
-static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
-{
- if (!uc->tchan)
- return;
- udma_write(uc->tchan->reg_rt, reg, val);
-}
-
-static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
- u32 mask, u32 val)
-{
- if (!uc->tchan)
- return;
- udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
-}
-
-/* RCHANRT */
-static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
-{
- if (!uc->rchan)
- return 0;
- return udma_read(uc->rchan->reg_rt, reg);
-}
-
-static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
-{
- if (!uc->rchan)
- return;
- udma_write(uc->rchan->reg_rt, reg, val);
-}
-
-static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
- u32 mask, u32 val)
-{
- if (!uc->rchan)
- return;
- udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
-}
+_UDMA_REG_ACCESS(chan);
+_UDMA_REG_ACCESS(bchan);
+_UDMA_REG_ACCESS(tchan);
+_UDMA_REG_ACCESS(rchan);
static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
int idx)
diff --git a/include/linux/soc/ti/k3-ringacc.h b/include/linux/soc/ti/k3-ringacc.h
index 9f2d141c988bd..1ac01b97dd923 100644
--- a/include/linux/soc/ti/k3-ringacc.h
+++ b/include/linux/soc/ti/k3-ringacc.h
@@ -10,6 +10,9 @@
#include <linux/types.h>
+#define K3_RINGACC_RT_INT_STATUS_COMPLETE BIT(0)
+#define K3_RINGACC_RT_INT_STATUS_TR BIT(2)
+
struct device_node;
/**
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
2026-01-30 11:01 ` [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2 Sai Sree Kartheek Adivi
@ 2026-01-30 16:08 ` Frank Li
2026-02-03 6:37 ` Péter Ujfalusi
1 sibling, 0 replies; 45+ messages in thread
From: Frank Li @ 2026-01-30 16:08 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi
Cc: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
r-sharma3, gehariprasath
On Fri, Jan 30, 2026 at 04:31:55PM +0530, Sai Sree Kartheek Adivi wrote:
> Add support for BCDMA_V2.
>
> The BCDMA_V2 is different than the existing BCDMA supported by the
> k3-udma driver.
>
> The changes in BCDMA_V2 are:
> - Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
> needs to set in the RT_CTL register.
> - Static channel mapping: Each channel is mapped to a single peripheral.
> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
> directly connected to GIC.
> - Remote side configuration handled by DMA. So no need to write to PEER
> registers to START / STOP / PAUSE / TEARDOWN.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/Kconfig | 16 +-
> drivers/dma/ti/Makefile | 1 +
> drivers/dma/ti/k3-udma-common.c | 75 +-
> drivers/dma/ti/k3-udma-v2.c | 1283 +++++++++++++++++++++++++++++
> drivers/dma/ti/k3-udma.h | 117 +--
> include/linux/soc/ti/k3-ringacc.h | 3 +
> 6 files changed, 1429 insertions(+), 66 deletions(-)
> create mode 100644 drivers/dma/ti/k3-udma-v2.c
>
> diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
> index 712e456015459..ada2ea8aca4b0 100644
> --- a/drivers/dma/ti/Kconfig
> +++ b/drivers/dma/ti/Kconfig
> @@ -49,6 +49,18 @@ config TI_K3_UDMA
> Enable support for the TI UDMA (Unified DMA) controller. This
> DMA engine is used in AM65x and j721e.
>
> +config TI_K3_UDMA_V2
> + tristate "Texas Instruments K3 UDMA v2 support"
> + depends on ARCH_K3
> + select DMA_ENGINE
> + select DMA_VIRTUAL_CHANNELS
> + select TI_K3_UDMA_COMMON
> + select TI_K3_RINGACC
> + select TI_K3_PSIL
> + help
> + Enable support for the TI UDMA (Unified DMA) v2 controller. This
> + DMA engine is used in AM62L.
> +
> config TI_K3_UDMA_COMMON
> tristate
> default n
> @@ -56,14 +68,14 @@ config TI_K3_UDMA_COMMON
> config TI_K3_UDMA_GLUE_LAYER
> tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
> depends on ARCH_K3 || COMPILE_TEST
> - depends on TI_K3_UDMA
> + depends on TI_K3_UDMA || TI_K3_UDMA_V2
> help
> Say y here to support the K3 NAVSS DMA glue interface
> If unsure, say N.
>
> config TI_K3_PSIL
> tristate
> - default TI_K3_UDMA
> + default TI_K3_UDMA || TI_K3_UDMA_V2
>
> config TI_DMA_CROSSBAR
> bool
> diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
> index 41bfba944dc6c..296aa3421e71b 100644
> --- a/drivers/dma/ti/Makefile
> +++ b/drivers/dma/ti/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
> obj-$(CONFIG_TI_EDMA) += edma.o
> obj-$(CONFIG_DMA_OMAP) += omap-dma.o
> obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
> +obj-$(CONFIG_TI_K3_UDMA_V2) += k3-udma-v2.o
> obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
> obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
> k3-psil-lib-objs := k3-psil.o \
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index 0ffc6becc402e..ba0fc048234ac 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -171,8 +171,13 @@ bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
> uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
> return true;
>
> - peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> - bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + if (uc->ud->match_data->type >= DMA_TYPE_BCDMA_V2) {
> + peer_bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + } else {
> + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + }
>
> /* Transfer is incomplete, store current residue and time stamp */
> if (peer_bcnt < bcnt) {
> @@ -319,6 +324,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;
> + u32 extra_flags = 0;
> u64 asel;
>
> /* estimate the number of TRs we will need */
> @@ -342,6 +348,9 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
> else
> asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
>
> + if (dir == DMA_MEM_TO_DEV && uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)
> + extra_flags = CPPI5_TR_CSF_EOP;
> +
> tr_req = d->hwdesc[0].tr_req_base;
> for_each_sg(sgl, sgent, sglen, i) {
> dma_addr_t sg_addr = sg_dma_address(sgent);
> @@ -358,7 +367,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
>
> cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> - cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
> + cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>
> sg_addr |= asel;
> tr_req[tr_idx].addr = sg_addr;
> @@ -372,7 +381,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
> false, false,
> CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> - CPPI5_TR_CSF_SUPR_EVT);
> + CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>
> tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
> tr_req[tr_idx].icnt0 = tr1_cnt0;
> @@ -632,7 +641,8 @@ int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
> d->static_tr.bstcnt = d->residue / d->sglen / div;
> else
> d->static_tr.bstcnt = d->residue / div;
> - } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
> + } else if ((uc->ud->match_data->type == DMA_TYPE_BCDMA ||
> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) &&
> uc->config.dir == DMA_DEV_TO_MEM &&
> uc->cyclic) {
> /*
> @@ -952,7 +962,8 @@ udma_prep_dma_cyclic_tr(struct udma_chan *uc, dma_addr_t buf_addr,
> * last one, so set the flag for each period.
> */
> if (uc->config.ep_type == PSIL_EP_PDMA_XY &&
> - uc->ud->match_data->type == DMA_TYPE_BCDMA) {
> + (uc->ud->match_data->type == DMA_TYPE_BCDMA ||
> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)) {
> period_csf = CPPI5_TR_CSF_EOP;
> }
>
> @@ -1807,7 +1818,8 @@ void udma_dbg_summary_show_chan(struct seq_file *s,
>
> switch (uc->config.dir) {
> case DMA_MEM_TO_MEM:
> - if (uc->ud->match_data->type == DMA_TYPE_BCDMA) {
> + if (uc->ud->match_data->type == DMA_TYPE_BCDMA ||
> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) {
Can you improve your match_data? use one flags for each feature.?
for example:
SUPPORT_FEATURE_1, SUPPORT_FEATURE_2
here just one if
if (match_data->feature | SUPPORT_FEATURE_1)
....
at driver data
for BCDMA
feature = SUPPORT_FEATURE_1;
for BCDMA_V2
feature = SUPPORT_FEATURE_1 : SUPPORT_FEATURE_2;
you can also use bool bit field
Frank Li
> seq_printf(s, "bchan%d)\n", uc->bchan->id);
> return;
> }
> @@ -2052,6 +2064,8 @@ int udma_get_tchan(struct udma_chan *uc)
> uc->tchan = NULL;
> return ret;
> }
> + if (ud->match_data->type == DMA_TYPE_BCDMA_V2)
> + uc->chan = uc->tchan;
>
> if (ud->tflow_cnt) {
> int tflow_id;
> @@ -2102,6 +2116,8 @@ int udma_get_rchan(struct udma_chan *uc)
> uc->rchan = NULL;
> return ret;
> }
> + if (ud->match_data->type == DMA_TYPE_BCDMA_V2)
> + uc->chan = uc->rchan;
>
> return 0;
> }
> @@ -2379,16 +2395,26 @@ int bcdma_setup_resources(struct udma_dev *ud)
>
> ud->bchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->bchan_cnt),
> sizeof(unsigned long), GFP_KERNEL);
> + bitmap_zero(ud->bchan_map, ud->bchan_cnt);
> 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);
> + bitmap_zero(ud->tchan_map, ud->tchan_cnt);
> 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);
> + if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
> + ud->rchan_map = ud->tchan_map;
> + ud->rchans = ud->tchans;
> + ud->chan_map = ud->tchan_map;
> + ud->chans = ud->tchans;
> + } else {
> + ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
> + sizeof(unsigned long), GFP_KERNEL);
> + bitmap_zero(ud->rchan_map, ud->rchan_cnt);
> + 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),
> @@ -2472,6 +2498,7 @@ int setup_resources(struct udma_dev *ud)
> ret = ud->udma_setup_resources(ud);
> break;
> case DMA_TYPE_BCDMA:
> + case DMA_TYPE_BCDMA_V2:
> ret = bcdma_setup_resources(ud);
> break;
> case DMA_TYPE_PKTDMA:
> @@ -2484,11 +2511,18 @@ int setup_resources(struct udma_dev *ud)
> 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 (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
> + ch_count = ud->bchan_cnt + ud->tchan_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);
> + } else {
> + 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)
> return -ENODEV;
>
> @@ -2520,6 +2554,15 @@ int setup_resources(struct udma_dev *ud)
> ud->rchan_cnt - bitmap_weight(ud->rchan_map,
> ud->rchan_cnt));
> break;
> + case DMA_TYPE_BCDMA_V2:
> + dev_info(dev,
> + "Channels: %d (bchan: %u, chan: %u)\n",
> + ch_count,
> + ud->bchan_cnt - bitmap_weight(ud->bchan_map,
> + ud->bchan_cnt),
> + ud->chan_cnt - bitmap_weight(ud->chan_map,
> + ud->chan_cnt));
> + break;
> case DMA_TYPE_PKTDMA:
> dev_info(dev,
> "Channels: %d (tchan: %u, rchan: %u)\n",
> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
> new file mode 100644
> index 0000000000000..af06d25fd598b
> --- /dev/null
> +++ b/drivers/dma/ti/k3-udma-v2.c
> @@ -0,0 +1,1283 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + * Derived from K3 UDMA driver (k3-udma.c)
> + * Copyright (C) 2024-2025 Texas Instruments Incorporated - http://www.ti.com
> + * Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
> + * Author: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/module.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmapool.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/list.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/sys_soc.h>
> +#include <linux/of.h>
> +#include <linux/of_dma.h>
> +#include <linux/of_irq.h>
> +#include <linux/workqueue.h>
> +#include <linux/completion.h>
> +#include <linux/iopoll.h>
> +#include <linux/soc/ti/k3-ringacc.h>
> +
> +#include "../virt-dma.h"
> +#include "k3-udma.h"
> +#include "k3-psil-priv.h"
> +
> +static const char * const v2_mmr_names[] = {
> + [V2_MMR_GCFG] = "gcfg",
> + [V2_MMR_BCHANRT] = "bchanrt",
> + [V2_MMR_CHANRT] = "chanrt",
> +};
> +
> +static int udma_v2_check_chan_autopair_completion(struct udma_chan *uc)
> +{
> + u32 val;
> +
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> + if (val & UDMA_CHAN_RT_CTL_PAIR_TIMEOUT)
> + return -ETIMEDOUT;
> + else if (val & UDMA_CHAN_RT_CTL_PAIR_COMPLETE)
> + return 1;
> +
> + /* timeout didn't occur and also pairing didn't happen yet. */
> + return 0;
> +}
> +
> +static bool udma_v2_is_chan_paused(struct udma_chan *uc)
> +{
> + u32 val, pause_mask;
> +
> + if (uc->config.dir == DMA_MEM_TO_MEM) {
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> + pause_mask = UDMA_CHAN_RT_CTL_PAUSE;
> + } else {
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_PDMA_STATE_REG);
> + pause_mask = UDMA_CHAN_RT_PDMA_STATE_PAUSE;
> + }
> +
> + if (val & pause_mask)
> + return true;
> +
> + return false;
> +}
> +
> +static void udma_v2_decrement_byte_counters(struct udma_chan *uc, u32 val)
> +{
> + udma_chanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
> + udma_chanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
> + if (uc->config.ep_type != PSIL_EP_NATIVE)
> + udma_chanrt_write(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG, val);
> +}
> +
> +static void udma_v2_reset_counters(struct udma_chan *uc)
> +{
> + u32 val;
> +
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + udma_chanrt_write(uc, UDMA_CHAN_RT_BCNT_REG, val);
> +
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG);
> + udma_chanrt_write(uc, UDMA_CHAN_RT_SBCNT_REG, val);
> +
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_PCNT_REG);
> + udma_chanrt_write(uc, UDMA_CHAN_RT_PCNT_REG, val);
> +
> + if (!uc->bchan) {
> + val = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
> + udma_chanrt_write(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG, val);
> + }
> +}
> +
> +static int udma_v2_reset_chan(struct udma_chan *uc, bool hard)
> +{
> + udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG, 0);
> +
> + /* Reset all counters */
> + udma_v2_reset_counters(uc);
> +
> + /* Hard reset: re-initialize the channel to reset */
> + if (hard) {
> + struct udma_chan_config ucc_backup;
> + int ret;
> +
> + memcpy(&ucc_backup, &uc->config, sizeof(uc->config));
> + uc->ud->ddev.device_free_chan_resources(&uc->vc.chan);
> +
> + /* restore the channel configuration */
> + memcpy(&uc->config, &ucc_backup, sizeof(uc->config));
> + ret = uc->ud->ddev.device_alloc_chan_resources(&uc->vc.chan);
> + if (ret)
> + return ret;
> +
> + /*
> + * Setting forced teardown after forced reset helps recovering
> + * the rchan.
> + */
> + if (uc->config.dir == DMA_DEV_TO_MEM)
> + udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN |
> + UDMA_CHAN_RT_CTL_FTDOWN,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN |
> + UDMA_CHAN_RT_CTL_FTDOWN);
> + }
> + uc->state = UDMA_CHAN_IS_IDLE;
> +
> + return 0;
> +}
> +
> +static int udma_v2_start(struct udma_chan *uc)
> +{
> + struct virt_dma_desc *vd = vchan_next_desc(&uc->vc);
> + struct udma_dev *ud = uc->ud;
> + int status, ret;
> +
> + if (!vd) {
> + uc->desc = NULL;
> + return -ENOENT;
> + }
> +
> + list_del(&vd->node);
> +
> + uc->desc = to_udma_desc(&vd->tx);
> +
> + /* Channel is already running and does not need reconfiguration */
> + if (udma_is_chan_running(uc) && !udma_chan_needs_reconfiguration(uc)) {
> + udma_start_desc(uc);
> + goto out;
> + }
> +
> + /* Make sure that we clear the teardown bit, if it is set */
> + ud->reset_chan(uc, false);
> +
> + /* Push descriptors before we start the channel */
> + udma_start_desc(uc);
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + /* Config remote TR */
> + if (uc->config.ep_type == PSIL_EP_PDMA_XY) {
> + u32 val = PDMA_STATIC_TR_Y(uc->desc->static_tr.elcnt) |
> + PDMA_STATIC_TR_X(uc->desc->static_tr.elsize);
> + const struct udma_match_data *match_data =
> + uc->ud->match_data;
> +
> + if (uc->config.enable_acc32)
> + val |= PDMA_STATIC_TR_XY_ACC32;
> + if (uc->config.enable_burst)
> + val |= PDMA_STATIC_TR_XY_BURST;
> +
> + udma_chanrt_write(uc,
> + UDMA_CHAN_RT_STATIC_TR_XY_REG,
> + val);
> +
> + udma_chanrt_write(uc,
> + UDMA_CHAN_RT_STATIC_TR_Z_REG,
> + PDMA_STATIC_TR_Z(uc->desc->static_tr.bstcnt,
> + match_data->statictr_z_mask));
> +
> + /* save the current staticTR configuration */
> + memcpy(&uc->static_tr, &uc->desc->static_tr,
> + sizeof(uc->static_tr));
> + }
> +
> + udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_AUTOPAIR);
> +
> + /* Poll for autopair completion */
> + ret = read_poll_timeout_atomic(udma_v2_check_chan_autopair_completion,
> + status, status != 0, 100, 500, false, uc);
> +
> + if (status <= 0)
> + return ret;
> +
> + break;
> + case DMA_MEM_TO_DEV:
> + /* Config remote TR */
> + if (uc->config.ep_type == PSIL_EP_PDMA_XY) {
> + u32 val = PDMA_STATIC_TR_Y(uc->desc->static_tr.elcnt) |
> + PDMA_STATIC_TR_X(uc->desc->static_tr.elsize);
> +
> + if (uc->config.enable_acc32)
> + val |= PDMA_STATIC_TR_XY_ACC32;
> + if (uc->config.enable_burst)
> + val |= PDMA_STATIC_TR_XY_BURST;
> +
> + udma_chanrt_write(uc,
> + UDMA_CHAN_RT_STATIC_TR_XY_REG,
> + val);
> +
> + /* save the current staticTR configuration */
> + memcpy(&uc->static_tr, &uc->desc->static_tr,
> + sizeof(uc->static_tr));
> + }
> +
> + udma_chanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_AUTOPAIR);
> +
> + /* Poll for autopair completion */
> + ret = read_poll_timeout_atomic(udma_v2_check_chan_autopair_completion,
> + status, status != 0, 100, 500, false, uc);
> +
> + if (status <= 0)
> + return -ETIMEDOUT;
> +
> + break;
> + case DMA_MEM_TO_MEM:
> + udma_bchanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN);
> + udma_bchanrt_write(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN);
> +
> + break;
> + default:
> + return -EINVAL;
> + }
> +
> + uc->state = UDMA_CHAN_IS_ACTIVE;
> +out:
> +
> + return 0;
> +}
> +
> +static int udma_v2_stop(struct udma_chan *uc)
> +{
> + uc->state = UDMA_CHAN_IS_TERMINATING;
> + reinit_completion(&uc->teardown_completed);
> +
> + if (uc->config.dir == DMA_DEV_TO_MEM) {
> + if (!uc->cyclic && !uc->desc)
> + udma_push_to_ring(uc, -1);
> + }
> +
> + udma_chanrt_write(uc, UDMA_CHAN_RT_PEER_REG(8), UDMA_CHAN_RT_PEER_REG8_FLUSH);
> + udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN,
> + UDMA_CHAN_RT_CTL_EN | UDMA_CHAN_RT_CTL_TDOWN);
> +
> + return 0;
> +}
> +
> +static irqreturn_t udma_v2_udma_irq_handler(int irq, void *data)
> +{
> + struct udma_chan *uc = data;
> + struct udma_dev *ud = uc->ud;
> + struct udma_desc *d;
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + k3_ringacc_ring_clear_irq(uc->rflow->r_ring);
> + break;
> + case DMA_MEM_TO_DEV:
> + case DMA_MEM_TO_MEM:
> + k3_ringacc_ring_clear_irq(uc->tchan->tc_ring);
> + break;
> + default:
> + return -ENOENT;
> + }
> +
> + spin_lock(&uc->vc.lock);
> + d = uc->desc;
> + if (d) {
> + d->tr_idx = (d->tr_idx + 1) % d->sglen;
> +
> + if (uc->cyclic) {
> + vchan_cyclic_callback(&d->vd);
> + } else {
> + /* TODO: figure out the real amount of data */
> + ud->decrement_byte_counters(uc, d->residue);
> + ud->start(uc);
> + vchan_cookie_complete(&d->vd);
> + }
> + }
> +
> + spin_unlock(&uc->vc.lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static irqreturn_t udma_v2_ring_irq_handler(int irq, void *data)
> +{
> + struct udma_chan *uc = data;
> + struct udma_dev *ud = uc->ud;
> + struct udma_desc *d;
> + dma_addr_t paddr = 0;
> + u32 intr_status, reg;
> +
> + switch (uc->config.dir) {
> + case DMA_DEV_TO_MEM:
> + intr_status = k3_ringacc_ring_get_irq_status(uc->rflow->r_ring);
> + break;
> + case DMA_MEM_TO_DEV:
> + case DMA_MEM_TO_MEM:
> + intr_status = k3_ringacc_ring_get_irq_status(uc->tchan->tc_ring);
> + break;
> + default:
> + return -ENOENT;
> + }
> +
> + reg = udma_chanrt_read(uc, UDMA_CHAN_RT_CTL_REG);
> +
> + if (intr_status & K3_RINGACC_RT_INT_STATUS_TR) {
> + /* check teardown status */
> + if ((reg & UDMA_CHAN_RT_CTL_TDOWN) && !(reg & UDMA_CHAN_RT_CTL_EN))
> + complete_all(&uc->teardown_completed);
> + return udma_v2_udma_irq_handler(irq, data);
> + }
> +
> + if (udma_pop_from_ring(uc, &paddr) || !paddr)
> + return IRQ_HANDLED;
> +
> + spin_lock(&uc->vc.lock);
> +
> + /* Teardown completion message */
> + if (cppi5_desc_is_tdcm(paddr)) {
> + complete_all(&uc->teardown_completed);
> +
> + if (uc->terminated_desc) {
> + udma_desc_free(&uc->terminated_desc->vd);
> + uc->terminated_desc = NULL;
> + }
> +
> + if (!uc->desc)
> + ud->start(uc);
> +
> + goto out;
> + }
> +
> + d = udma_udma_desc_from_paddr(uc, paddr);
> +
> + if (d) {
> + dma_addr_t desc_paddr = udma_curr_cppi5_desc_paddr(d,
> + d->desc_idx);
> + if (desc_paddr != paddr) {
> + dev_err(uc->ud->dev, "not matching descriptors!\n");
> + goto out;
> + }
> +
> + if (d == uc->desc) {
> + /* active descriptor */
> + if (uc->cyclic) {
> + udma_cyclic_packet_elapsed(uc);
> + vchan_cyclic_callback(&d->vd);
> + } else {
> + if (udma_is_desc_really_done(uc, d)) {
> + ud->decrement_byte_counters(uc, d->residue);
> + ud->start(uc);
> + vchan_cookie_complete(&d->vd);
> + } else {
> + schedule_delayed_work(&uc->tx_drain.work,
> + 0);
> + }
> + }
> + } else {
> + /*
> + * terminated descriptor, mark the descriptor as
> + * completed to update the channel's cookie marker
> + */
> + dma_cookie_complete(&d->vd.tx);
> + }
> + }
> +out:
> + spin_unlock(&uc->vc.lock);
> +
> + return IRQ_HANDLED;
> +}
> +
> +static int bcdma_v2_get_bchan(struct udma_chan *uc)
> +{
> + struct udma_dev *ud = uc->ud;
> + enum udma_tp_level tpl;
> + int ret;
> +
> + 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, uc->id);
> + if (IS_ERR(uc->bchan)) {
> + ret = PTR_ERR(uc->bchan);
> + uc->bchan = NULL;
> + return ret;
> + }
> + uc->chan = uc->bchan;
> + uc->tchan = uc->bchan;
> +
> + return 0;
> +}
> +
> +static int bcdma_v2_alloc_bchan_resources(struct udma_chan *uc)
> +{
> + struct k3_ring_cfg ring_cfg;
> + struct udma_dev *ud = uc->ud;
> + int ret;
> +
> + ret = bcdma_v2_get_bchan(uc);
> + if (ret)
> + return ret;
> +
> + ret = k3_ringacc_request_rings_pair(ud->ringacc, ud->match_data->chan_cnt + uc->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 int udma_v2_alloc_tx_resources(struct udma_chan *uc)
> +{
> + struct k3_ring_cfg ring_cfg;
> + struct udma_dev *ud = uc->ud;
> + struct udma_tchan *tchan;
> + int ring_idx, ret;
> +
> + ret = udma_get_tchan(uc);
> + if (ret)
> + return ret;
> +
> + tchan = uc->tchan;
> + if (tchan->tflow_id >= 0)
> + ring_idx = tchan->tflow_id;
> + else
> + ring_idx = 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;
> + }
> +
> + 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, 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;
> +
> + return 0;
> +
> +err_ringcfg:
> + k3_ringacc_ring_free(uc->tchan->tc_ring);
> + uc->tchan->tc_ring = NULL;
> + k3_ringacc_ring_free(uc->tchan->t_ring);
> + uc->tchan->t_ring = NULL;
> +err_ring:
> + udma_put_tchan(uc);
> +
> + return ret;
> +}
> +
> +static int udma_v2_alloc_rx_resources(struct udma_chan *uc)
> +{
> + struct udma_dev *ud = uc->ud;
> + struct k3_ring_cfg ring_cfg;
> + struct udma_rflow *rflow;
> + 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->config.dir == DMA_MEM_TO_MEM)
> + return 0;
> +
> + 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;
> + if (ud->tflow_cnt)
> + fd_ring_id = ud->tflow_cnt + rflow->id;
> + else
> + fd_ring_id = uc->rchan->id;
> + ret = k3_ringacc_request_rings_pair(ud->ringacc, fd_ring_id, -1,
> + &rflow->fd_ring, &rflow->r_ring);
> + if (ret) {
> + ret = -EBUSY;
> + goto err_ring;
> + }
> +
> + memset(&ring_cfg, 0, sizeof(ring_cfg));
> +
> + ring_cfg.elm_size = K3_RINGACC_RING_ELSIZE_8;
> + ring_cfg.size = K3_UDMA_DEFAULT_RING_SIZE;
> + ring_cfg.mode = K3_RINGACC_RING_MODE_RING;
> +
> + 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);
> +
> + if (ret)
> + goto err_ringcfg;
> +
> + return 0;
> +
> +err_ringcfg:
> + k3_ringacc_ring_free(rflow->r_ring);
> + rflow->r_ring = NULL;
> + k3_ringacc_ring_free(rflow->fd_ring);
> + rflow->fd_ring = NULL;
> +err_ring:
> + udma_put_rflow(uc);
> +err_rflow:
> + udma_put_rchan(uc);
> +
> + return ret;
> +}
> +
> +static int bcdma_v2_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + struct udma_dev *ud = to_udma_dev(chan->device);
> + u32 irq_ring_idx;
> + __be32 addr[2] = {0, 0};
> + struct of_phandle_args out_irq;
> + int ret;
> +
> + /* Only TR mode is supported */
> + uc->config.pkt_mode = false;
> +
> + /*
> + * 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_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);
> +
> + ret = bcdma_v2_alloc_bchan_resources(uc);
> + if (ret)
> + return ret;
> +
> + irq_ring_idx = ud->match_data->chan_cnt + uc->id;
> + break;
> + case DMA_MEM_TO_DEV:
> + /* Slave transfer synchronized - mem to dev (TX) transfer */
> + dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
> + uc->id);
> +
> + ret = udma_v2_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;
> +
> + break;
> + case DMA_DEV_TO_MEM:
> + /* Slave transfer synchronized - dev to mem (RX) transfer */
> + dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
> + uc->id);
> +
> + ret = udma_v2_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;
> +
> + 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);
> + ud->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;
> + ret = -ENOMEM;
> + goto err_res_free;
> + }
> +
> + uc->use_dma_pool = true;
> + } else if (uc->config.dir != DMA_MEM_TO_MEM) {
> + uc->psil_paired = true;
> + }
> +
> + out_irq.np = dev_of_node(ud->dev);
> + out_irq.args_count = 1;
> + out_irq.args[0] = irq_ring_idx;
> + ret = of_irq_parse_raw(addr, &out_irq);
> + if (ret)
> + return ret;
> +
> + uc->irq_num_ring = irq_create_of_mapping(&out_irq);
> +
> + ret = devm_request_irq(ud->dev, uc->irq_num_ring, udma_v2_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;
> + }
> +
> + 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_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 enum dma_status udma_v2_tx_status(struct dma_chan *chan,
> + dma_cookie_t cookie,
> + struct dma_tx_state *txstate)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + enum dma_status ret;
> + unsigned long flags;
> +
> + spin_lock_irqsave(&uc->vc.lock, flags);
> +
> + ret = dma_cookie_status(chan, cookie, txstate);
> +
> + if (!udma_is_chan_running(uc))
> + ret = DMA_COMPLETE;
> +
> + if (ret == DMA_IN_PROGRESS && udma_v2_is_chan_paused(uc))
> + ret = DMA_PAUSED;
> +
> + if (ret == DMA_COMPLETE || !txstate)
> + goto out;
> +
> + if (uc->desc && uc->desc->vd.tx.cookie == cookie) {
> + u32 peer_bcnt = 0;
> + u32 bcnt = 0;
> + u32 residue = uc->desc->residue;
> + u32 delay = 0;
> +
> + if (uc->desc->dir == DMA_MEM_TO_DEV) {
> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_SBCNT_REG);
> +
> + if (uc->config.ep_type != PSIL_EP_NATIVE) {
> + peer_bcnt = udma_chanrt_read(uc, 0x810);
> +
> + if (bcnt > peer_bcnt)
> + delay = bcnt - peer_bcnt;
> + }
> + } else if (uc->desc->dir == DMA_DEV_TO_MEM) {
> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> +
> + if (uc->config.ep_type != PSIL_EP_NATIVE) {
> + peer_bcnt = udma_chanrt_read(uc, 0x810);
> +
> + if (peer_bcnt > bcnt)
> + delay = peer_bcnt - bcnt;
> + }
> + } else {
> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + }
> +
> + if (bcnt && !(bcnt % uc->desc->residue))
> + residue = 0;
> + else
> + residue -= bcnt % uc->desc->residue;
> +
> + if (!residue && (uc->config.dir == DMA_DEV_TO_MEM || !delay)) {
> + ret = DMA_COMPLETE;
> + delay = 0;
> + }
> +
> + dma_set_residue(txstate, residue);
> + dma_set_in_flight_bytes(txstate, delay);
> +
> + } else {
> + ret = DMA_COMPLETE;
> + }
> +
> +out:
> + spin_unlock_irqrestore(&uc->vc.lock, flags);
> + return ret;
> +}
> +
> +static int udma_v2_pause(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> +
> + /* pause the channel */
> + udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_PAUSE, UDMA_CHAN_RT_CTL_PAUSE);
> +
> + return 0;
> +}
> +
> +static int udma_v2_resume(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> +
> + /* resume the channel */
> + udma_chanrt_update_bits(uc, UDMA_CHAN_RT_CTL_REG,
> + UDMA_CHAN_RT_CTL_PAUSE, 0);
> +
> + return 0;
> +}
> +
> +static struct platform_driver bcdma_v2_driver;
> +
> +static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
> +{
> + struct udma_chan_config *ucc;
> + struct psil_endpoint_config *ep_config;
> + struct udma_v2_filter_param *filter_param;
> + struct udma_chan *uc;
> + struct udma_dev *ud;
> +
> + if (chan->device->dev->driver != &bcdma_v2_driver.driver)
> + return false;
> +
> + uc = to_udma_chan(chan);
> + ucc = &uc->config;
> + ud = uc->ud;
> + filter_param = param;
> +
> + 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->asel = filter_param->asel;
> + ucc->tr_trigger_type = filter_param->tr_trigger_type;
> +
> + 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 {
> + ucc->dir = DMA_DEV_TO_MEM;
> + }
> +
> + ep_config = psil_get_ep_config(ucc->remote_thread_id);
> + if (IS_ERR(ep_config)) {
> + dev_err(ud->dev, "No configuration for 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;
> + }
> +
> + ucc->pkt_mode = ep_config->pkt_mode;
> + ucc->channel_tpl = ep_config->channel_tpl;
> + ucc->notdpkt = ep_config->notdpkt;
> + ucc->ep_type = ep_config->ep_type;
> +
> + if (ud->match_data->type == DMA_TYPE_BCDMA_V2 &&
> + 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;
> + }
> +
> + ucc->needs_epib = ep_config->needs_epib;
> + ucc->psd_size = ep_config->psd_size;
> + ucc->metadata_size =
> + (ucc->needs_epib ? CPPI5_INFO0_HDESC_EPIB_SIZE : 0) +
> + ucc->psd_size;
> +
> + if (ucc->ep_type != PSIL_EP_NATIVE) {
> + const struct udma_match_data *match_data = ud->match_data;
> +
> + if ((match_data->flags & UDMA_FLAG_PDMA_ACC32) && ep_config->pdma_acc32)
> + ucc->enable_acc32 = true;
> + else
> + ucc->enable_acc32 = false;
> +
> + if ((match_data->flags & UDMA_FLAG_PDMA_BURST) && ep_config->pdma_burst)
> + ucc->enable_burst = true;
> + else
> + ucc->enable_burst = false;
> + }
> + if (ucc->pkt_mode)
> + ucc->hdesc_size = ALIGN(sizeof(struct cppi5_host_desc_t) +
> + ucc->metadata_size, ud->desc_align);
> +
> + dev_dbg(ud->dev, "chan%d: Remote thread: 0x%04x (%s)\n", uc->id,
> + 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_v2_of_xlate(struct of_phandle_args *dma_spec,
> + struct of_dma *ofdma)
> +{
> + struct udma_dev *ud = ofdma->of_dma_data;
> + dma_cap_mask_t mask = ud->ddev.cap_mask;
> + struct udma_v2_filter_param filter_param;
> + struct dma_chan *chan;
> +
> + if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
> + if (dma_spec->args_count != 4)
> + return NULL;
> +
> + filter_param.tr_trigger_type = dma_spec->args[0];
> + filter_param.trigger_param = dma_spec->args[1];
> + filter_param.remote_thread_id = dma_spec->args[2];
> + filter_param.asel = dma_spec->args[3];
> + } 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)
> + filter_param.asel = dma_spec->args[1];
> + else
> + filter_param.asel = 0;
> + }
> +
> + chan = __dma_request_channel(&mask, udma_v2_dma_filter_fn, &filter_param,
> + ofdma->of_node);
> + if (!chan) {
> + dev_err(ud->dev, "get channel fail in %s.\n", __func__);
> + return ERR_PTR(-EINVAL);
> + }
> +
> + return chan;
> +}
> +
> +static struct udma_match_data bcdma_v2_am62l_data = {
> + .type = DMA_TYPE_BCDMA_V2,
> + .psil_base = 0x2000, /* for tchan and rchan, not applicable to bchan */
> + .enable_memcpy_support = true, /* Supported via bchan */
> + .flags = UDMA_FLAGS_J7_CLASS,
> + .statictr_z_mask = GENMASK(23, 0),
> + .burst_size = {
> + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */
> + 0, /* No H Channels */
> + 0, /* No UH Channels */
> + },
> + .bchan_cnt = 16,
> + .chan_cnt = 128,
> + .tchan_cnt = 128,
> + .rchan_cnt = 128,
> +};
> +
> +static const struct of_device_id udma_of_match[] = {
> + {
> + .compatible = "ti,am62l-dmss-bcdma",
> + .data = &bcdma_v2_am62l_data,
> + },
> + { /* Sentinel */ },
> +};
> +
> +static const struct soc_device_attribute k3_soc_devices[] = {
> + { .family = "AM62LX", },
> + { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, udma_of_match);
> +
> +static int udma_v2_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
> +{
> + int i;
> +
> + ud->mmrs[V2_MMR_GCFG] = devm_platform_ioremap_resource_byname(pdev,
> + v2_mmr_names[V2_MMR_GCFG]);
> + if (IS_ERR(ud->mmrs[V2_MMR_GCFG]))
> + return PTR_ERR(ud->mmrs[V2_MMR_GCFG]);
> +
> + ud->bchan_cnt = ud->match_data->bchan_cnt;
> + /* There are no tchan and rchan in BCDMA_V2.
> + * Duplicate chan as tchan and rchan to keep the common code
> + * in k3-udma-common.c functional for BCDMA_V2.
> + */
> + ud->chan_cnt = ud->match_data->chan_cnt;
> + ud->tchan_cnt = ud->match_data->chan_cnt;
> + ud->rchan_cnt = ud->match_data->chan_cnt;
> + ud->rflow_cnt = ud->chan_cnt;
> +
> + for (i = 1; i < V2_MMR_LAST; i++) {
> + if (i == V2_MMR_BCHANRT && ud->bchan_cnt == 0)
> + continue;
> + if (i == V2_MMR_CHANRT && ud->chan_cnt == 0)
> + continue;
> +
> + ud->mmrs[i] = devm_platform_ioremap_resource_byname(pdev, v2_mmr_names[i]);
> + if (IS_ERR(ud->mmrs[i]))
> + return PTR_ERR(ud->mmrs[i]);
> + }
> +
> + return 0;
> +}
> +
> +static int udma_v2_probe(struct platform_device *pdev)
> +{
> + const struct soc_device_attribute *soc;
> + struct device *dev = &pdev->dev;
> + struct udma_dev *ud;
> + const struct of_device_id *match;
> + int i, ret;
> + int ch_count;
> +
> + ret = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(48));
> + if (ret)
> + dev_err(dev, "failed to set dma mask stuff\n");
> +
> + ud = devm_kzalloc(dev, sizeof(*ud), GFP_KERNEL);
> + if (!ud)
> + return -ENOMEM;
> +
> + 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;
> +
> + ud->soc_data = ud->match_data->soc_data;
> + if (!ud->soc_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;
> + }
> + // Setup function pointers
> + ud->start = udma_v2_start;
> + ud->stop = udma_v2_stop;
> + ud->reset_chan = udma_v2_reset_chan;
> + ud->decrement_byte_counters = udma_v2_decrement_byte_counters;
> + ud->bcdma_setup_sci_resources = NULL;
> +
> + ret = udma_v2_get_mmrs(pdev, ud);
> + if (ret)
> + return ret;
> +
> + struct k3_ringacc_init_data ring_init_data = {0};
> +
> + ring_init_data.num_rings = ud->bchan_cnt + ud->chan_cnt;
> +
> + ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data);
> +
> + if (IS_ERR(ud->ringacc))
> + return PTR_ERR(ud->ringacc);
> +
> + dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask);
> +
> + dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
> + ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
> +
> + ud->ddev.device_config = udma_slave_config;
> + ud->ddev.device_prep_slave_sg = udma_prep_slave_sg;
> + ud->ddev.device_issue_pending = udma_issue_pending;
> + ud->ddev.device_tx_status = udma_v2_tx_status;
> + ud->ddev.device_pause = udma_v2_pause;
> + ud->ddev.device_resume = udma_v2_resume;
> + ud->ddev.device_terminate_all = udma_terminate_all;
> + ud->ddev.device_synchronize = udma_synchronize;
> +#ifdef CONFIG_DEBUG_FS
> + ud->ddev.dbg_summary_show = udma_dbg_summary_show;
> +#endif
> +
> + ud->ddev.device_alloc_chan_resources =
> + bcdma_v2_alloc_chan_resources;
> +
> + 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);
> + ud->ddev.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
> + ud->ddev.desc_metadata_modes = DESC_METADATA_CLIENT |
> + DESC_METADATA_ENGINE;
> + 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);
> + }
> +
> + ud->ddev.dev = dev;
> + ud->dev = dev;
> + ud->psil_base = ud->match_data->psil_base;
> +
> + INIT_LIST_HEAD(&ud->ddev.channels);
> + INIT_LIST_HEAD(&ud->desc_to_purge);
> +
> + ch_count = setup_resources(ud);
> + if (ch_count <= 0)
> + return ch_count;
> +
> + spin_lock_init(&ud->lock);
> + INIT_WORK(&ud->purge_work, udma_purge_desc_work);
> +
> + ud->desc_align = 64;
> + if (ud->desc_align < dma_get_cache_alignment())
> + ud->desc_align = dma_get_cache_alignment();
> +
> + ret = udma_setup_rx_flush(ud);
> + 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[V2_MMR_BCHANRT] + i * 0x1000;
> + }
> +
> + for (i = 0; i < ud->tchan_cnt; i++) {
> + struct udma_tchan *tchan = &ud->tchans[i];
> +
> + tchan->id = i;
> + tchan->reg_rt = ud->mmrs[V2_MMR_CHANRT] + i * 0x1000;
> + }
> +
> + for (i = 0; i < ud->rchan_cnt; i++) {
> + struct udma_rchan *rchan = &ud->rchans[i];
> +
> + rchan->id = i;
> + rchan->reg_rt = ud->mmrs[V2_MMR_CHANRT] + i * 0x1000;
> + }
> +
> + for (i = 0; i < ud->rflow_cnt; i++) {
> + struct udma_rflow *rflow = &ud->rflows[i];
> +
> + rflow->id = i;
> + rflow->reg_rt = ud->rflow_rt + i * 0x2000;
> + }
> +
> + for (i = 0; i < ch_count; i++) {
> + struct udma_chan *uc = &ud->channels[i];
> +
> + 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);
> +
> + vchan_init(&uc->vc, &ud->ddev);
> + /* Use custom vchan completion handling */
> + tasklet_setup(&uc->vc.task, udma_vchan_complete);
> + init_completion(&uc->teardown_completed);
> + INIT_DELAYED_WORK(&uc->tx_drain.work, udma_check_tx_completion);
> + }
> +
> + /* Configure the copy_align to the maximum burst size the device supports */
> + ud->ddev.copy_align = udma_get_copy_align(ud);
> +
> + ret = dma_async_device_register(&ud->ddev);
> + if (ret) {
> + dev_err(dev, "failed to register slave DMA engine: %d\n", ret);
> + return ret;
> + }
> +
> + platform_set_drvdata(pdev, ud);
> +
> + ret = of_dma_controller_register(dev->of_node, udma_v2_of_xlate, ud);
> + if (ret) {
> + dev_err(dev, "failed to register of_dma controller\n");
> + dma_async_device_unregister(&ud->ddev);
> + }
> +
> + return ret;
> +}
> +
> +static int __maybe_unused udma_v2_pm_suspend(struct device *dev)
> +{
> + struct udma_dev *ud = dev_get_drvdata(dev);
> + struct dma_device *dma_dev = &ud->ddev;
> + struct dma_chan *chan;
> + struct udma_chan *uc;
> +
> + list_for_each_entry(chan, &dma_dev->channels, device_node) {
> + if (chan->client_count) {
> + uc = to_udma_chan(chan);
> + /* backup the channel configuration */
> + memcpy(&uc->backup_config, &uc->config,
> + sizeof(struct udma_chan_config));
> + dev_dbg(dev, "Suspending channel %s\n",
> + dma_chan_name(chan));
> + ud->ddev.device_free_chan_resources(chan);
> + }
> + }
> +
> + return 0;
> +}
> +
> +static int __maybe_unused udma_v2_pm_resume(struct device *dev)
> +{
> + struct udma_dev *ud = dev_get_drvdata(dev);
> + struct dma_device *dma_dev = &ud->ddev;
> + struct dma_chan *chan;
> + struct udma_chan *uc;
> + int ret;
> +
> + list_for_each_entry(chan, &dma_dev->channels, device_node) {
> + if (chan->client_count) {
> + uc = to_udma_chan(chan);
> + /* restore the channel configuration */
> + memcpy(&uc->config, &uc->backup_config,
> + sizeof(struct udma_chan_config));
> + dev_dbg(dev, "Resuming channel %s\n",
> + dma_chan_name(chan));
> + ret = ud->ddev.device_alloc_chan_resources(chan);
> + if (ret)
> + return ret;
> + }
> + }
> +
> + return 0;
> +}
> +
> +static const struct dev_pm_ops udma_pm_ops = {
> + SET_LATE_SYSTEM_SLEEP_PM_OPS(udma_v2_pm_suspend, udma_v2_pm_resume)
> +};
> +
> +static struct platform_driver bcdma_v2_driver = {
> + .driver = {
> + .name = "ti-udma-v2",
> + .of_match_table = udma_of_match,
> + .suppress_bind_attrs = true,
> + .pm = &udma_pm_ops,
> + },
> + .probe = udma_v2_probe,
> +};
> +
> +module_platform_driver(bcdma_v2_driver);
> +MODULE_DESCRIPTION("Texas Instruments K3 UDMA v2 support");
> +MODULE_LICENSE("GPL");
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 6a95eb1ece064..ca726ea9864ae 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -28,6 +28,11 @@
> #define UDMA_CHAN_RT_SWTRIG_REG 0x8
> #define UDMA_CHAN_RT_STDATA_REG 0x80
>
> +#define UDMA_CHAN_RT_STATIC_TR_XY_REG 0x800
> +#define UDMA_CHAN_RT_STATIC_TR_Z_REG 0x804
> +#define UDMA_CHAN_RT_PERIPH_BCNT_REG 0x810
> +#define UDMA_CHAN_RT_PDMA_STATE_REG 0x80c
> +
> #define UDMA_CHAN_RT_PEER_REG(i) (0x200 + ((i) * 0x4))
> #define UDMA_CHAN_RT_PEER_STATIC_TR_XY_REG \
> UDMA_CHAN_RT_PEER_REG(0) /* PSI-L: 0x400 */
> @@ -67,8 +72,16 @@
> #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_AUTOPAIR BIT(23)
> +#define UDMA_CHAN_RT_CTL_PAIR_TIMEOUT BIT(17)
> +#define UDMA_CHAN_RT_CTL_PAIR_COMPLETE BIT(16)
> #define UDMA_CHAN_RT_CTL_ERROR BIT(0)
>
> +/* UDMA_CHAN_RT_PDMA_STATE_REG */
> +#define UDMA_CHAN_RT_PDMA_STATE_IN_EVT BIT(31)
> +#define UDMA_CHAN_RT_PDMA_STATE_TDOWN BIT(30)
> +#define UDMA_CHAN_RT_PDMA_STATE_PAUSE BIT(29)
> +
> /* UDMA_CHAN_RT_PEER_RT_EN_REG */
> #define UDMA_PEER_RT_EN_ENABLE BIT(31)
> #define UDMA_PEER_RT_EN_TEARDOWN BIT(30)
> @@ -99,6 +112,9 @@
> */
> #define PDMA_STATIC_TR_Z(x, mask) ((x) & (mask))
>
> +/* UDMA_CHAN_RT_PEER_REG(8) */
> +#define UDMA_CHAN_RT_PEER_REG8_FLUSH 0x09000000
> +
> /* Address Space Select */
> #define K3_ADDRESS_ASEL_SHIFT 48
>
> @@ -202,6 +218,8 @@ enum k3_dma_type {
> DMA_TYPE_UDMA = 0,
> DMA_TYPE_BCDMA,
> DMA_TYPE_PKTDMA,
> + DMA_TYPE_BCDMA_V2,
> + DMA_TYPE_PKTDMA_V2,
> };
>
> enum udma_mmr {
> @@ -212,6 +230,13 @@ enum udma_mmr {
> MMR_LAST,
> };
>
> +enum udma_v2_mmr {
> + V2_MMR_GCFG = 0,
> + V2_MMR_BCHANRT,
> + V2_MMR_CHANRT,
> + V2_MMR_LAST,
> +};
> +
> struct udma_filter_param {
> int remote_thread_id;
> u32 atype;
> @@ -219,6 +244,13 @@ struct udma_filter_param {
> u32 tr_trigger_type;
> };
>
> +struct udma_v2_filter_param {
> + u32 tr_trigger_type;
> + u32 trigger_param;
> + int remote_thread_id;
> + u32 asel;
> +};
> +
> struct udma_tchan {
> void __iomem *reg_rt;
>
> @@ -230,17 +262,13 @@ struct udma_tchan {
> };
>
> #define udma_bchan udma_tchan
> +#define udma_rchan udma_tchan
>
> struct udma_rflow {
> int id;
> struct k3_ring *fd_ring; /* Free Descriptor ring */
> struct k3_ring *r_ring; /* Receive ring */
> -};
> -
> -struct udma_rchan {
> void __iomem *reg_rt;
> -
> - int id;
> };
>
> struct udma_oes_offsets {
> @@ -268,6 +296,12 @@ struct udma_match_data {
> u32 statictr_z_mask;
> u8 burst_size[3];
> struct udma_soc_data *soc_data;
> + u32 bchan_cnt;
> + u32 chan_cnt;
> + u32 tchan_cnt;
> + u32 rchan_cnt;
> + u32 tflow_cnt;
> + u32 rflow_cnt;
> };
>
> struct udma_soc_data {
> @@ -302,6 +336,7 @@ struct udma_dev {
> struct dma_device ddev;
> struct device *dev;
> void __iomem *mmrs[MMR_LAST];
> + void __iomem *rflow_rt;
> const struct udma_match_data *match_data;
> const struct udma_soc_data *soc_data;
>
> @@ -322,12 +357,14 @@ struct udma_dev {
> struct udma_rx_flush rx_flush;
>
> int bchan_cnt;
> + int chan_cnt;
> int tchan_cnt;
> int echan_cnt;
> int rchan_cnt;
> int rflow_cnt;
> int tflow_cnt;
> unsigned long *bchan_map;
> + unsigned long *chan_map;
> unsigned long *tchan_map;
> unsigned long *rchan_map;
> unsigned long *rflow_gp_map;
> @@ -336,6 +373,7 @@ struct udma_dev {
> unsigned long *tflow_map;
>
> struct udma_bchan *bchans;
> + struct udma_tchan *chans;
> struct udma_tchan *tchans;
> struct udma_rchan *rchans;
> struct udma_rflow *rflows;
> @@ -430,6 +468,7 @@ struct udma_chan {
> char *name;
>
> struct udma_bchan *bchan;
> + struct udma_tchan *chan;
> struct udma_tchan *tchan;
> struct udma_rchan *rchan;
> struct udma_rflow *rflow;
> @@ -499,51 +538,33 @@ static inline void udma_update_bits(void __iomem *base, int reg,
> writel(tmp, base + reg);
> }
>
> -/* TCHANRT */
> -static inline u32 udma_tchanrt_read(struct udma_chan *uc, int reg)
> -{
> - if (!uc->tchan)
> - return 0;
> - return udma_read(uc->tchan->reg_rt, reg);
> +#define _UDMA_REG_ACCESS(channel) \
> +static inline u32 udma_##channel##rt_read(struct udma_chan *uc, int reg) \
> +{ \
> + if (!uc->channel) \
> + return 0; \
> + return udma_read(uc->channel->reg_rt, reg); \
> +} \
> +\
> +static inline void udma_##channel##rt_write(struct udma_chan *uc, int reg, u32 val) \
> +{ \
> + if (!uc->channel) \
> + return; \
> + udma_write(uc->channel->reg_rt, reg, val); \
> +} \
> +\
> +static inline void udma_##channel##rt_update_bits(struct udma_chan *uc, int reg, \
> + u32 mask, u32 val) \
> +{ \
> + if (!uc->channel) \
> + return; \
> + udma_update_bits(uc->channel->reg_rt, reg, mask, val); \
> }
>
> -static inline void udma_tchanrt_write(struct udma_chan *uc, int reg, u32 val)
> -{
> - if (!uc->tchan)
> - return;
> - udma_write(uc->tchan->reg_rt, reg, val);
> -}
> -
> -static inline void udma_tchanrt_update_bits(struct udma_chan *uc, int reg,
> - u32 mask, u32 val)
> -{
> - if (!uc->tchan)
> - return;
> - udma_update_bits(uc->tchan->reg_rt, reg, mask, val);
> -}
> -
> -/* RCHANRT */
> -static inline u32 udma_rchanrt_read(struct udma_chan *uc, int reg)
> -{
> - if (!uc->rchan)
> - return 0;
> - return udma_read(uc->rchan->reg_rt, reg);
> -}
> -
> -static inline void udma_rchanrt_write(struct udma_chan *uc, int reg, u32 val)
> -{
> - if (!uc->rchan)
> - return;
> - udma_write(uc->rchan->reg_rt, reg, val);
> -}
> -
> -static inline void udma_rchanrt_update_bits(struct udma_chan *uc, int reg,
> - u32 mask, u32 val)
> -{
> - if (!uc->rchan)
> - return;
> - udma_update_bits(uc->rchan->reg_rt, reg, mask, val);
> -}
> +_UDMA_REG_ACCESS(chan);
> +_UDMA_REG_ACCESS(bchan);
> +_UDMA_REG_ACCESS(tchan);
> +_UDMA_REG_ACCESS(rchan);
>
> static inline dma_addr_t udma_curr_cppi5_desc_paddr(struct udma_desc *d,
> int idx)
> diff --git a/include/linux/soc/ti/k3-ringacc.h b/include/linux/soc/ti/k3-ringacc.h
> index 9f2d141c988bd..1ac01b97dd923 100644
> --- a/include/linux/soc/ti/k3-ringacc.h
> +++ b/include/linux/soc/ti/k3-ringacc.h
> @@ -10,6 +10,9 @@
>
> #include <linux/types.h>
>
> +#define K3_RINGACC_RT_INT_STATUS_COMPLETE BIT(0)
> +#define K3_RINGACC_RT_INT_STATUS_TR BIT(2)
> +
> struct device_node;
>
> /**
> --
> 2.34.1
>
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
2026-01-30 11:01 ` [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2 Sai Sree Kartheek Adivi
2026-01-30 16:08 ` Frank Li
@ 2026-02-03 6:37 ` Péter Ujfalusi
2026-02-03 8:22 ` Sai Sree Kartheek Adivi
1 sibling, 1 reply; 45+ messages in thread
From: Péter Ujfalusi @ 2026-02-03 6:37 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, vkoul, robh, krzk+dt, conor+dt, nm,
ssantosh, dmaengine, devicetree, linux-kernel, linux-arm-kernel,
vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
> Add support for BCDMA_V2.
>
> The BCDMA_V2 is different than the existing BCDMA supported by the
> k3-udma driver.
>
> The changes in BCDMA_V2 are:
> - Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
> needs to set in the RT_CTL register.
> - Static channel mapping: Each channel is mapped to a single peripheral.
> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
> directly connected to GIC.
> - Remote side configuration handled by DMA. So no need to write to PEER
> registers to START / STOP / PAUSE / TEARDOWN.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/Kconfig | 16 +-
> drivers/dma/ti/Makefile | 1 +
> drivers/dma/ti/k3-udma-common.c | 75 +-
> drivers/dma/ti/k3-udma-v2.c | 1283 +++++++++++++++++++++++++++++
> drivers/dma/ti/k3-udma.h | 117 +--
> include/linux/soc/ti/k3-ringacc.h | 3 +
> 6 files changed, 1429 insertions(+), 66 deletions(-)
> create mode 100644 drivers/dma/ti/k3-udma-v2.c
>
> diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
> index 712e456015459..ada2ea8aca4b0 100644
> --- a/drivers/dma/ti/Kconfig
> +++ b/drivers/dma/ti/Kconfig
> @@ -49,6 +49,18 @@ config TI_K3_UDMA
> Enable support for the TI UDMA (Unified DMA) controller. This
> DMA engine is used in AM65x and j721e.
>
> +config TI_K3_UDMA_V2
> + tristate "Texas Instruments K3 UDMA v2 support"
> + depends on ARCH_K3
> + select DMA_ENGINE
> + select DMA_VIRTUAL_CHANNELS
> + select TI_K3_UDMA_COMMON
> + select TI_K3_RINGACC
> + select TI_K3_PSIL
> + help
> + Enable support for the TI UDMA (Unified DMA) v2 controller. This
> + DMA engine is used in AM62L.
> +
> config TI_K3_UDMA_COMMON
> tristate
> default n
> @@ -56,14 +68,14 @@ config TI_K3_UDMA_COMMON
> config TI_K3_UDMA_GLUE_LAYER
> tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
> depends on ARCH_K3 || COMPILE_TEST
> - depends on TI_K3_UDMA
> + depends on TI_K3_UDMA || TI_K3_UDMA_V2
At this point the glue layer should not have dependency on UDMA_V2 as it
only receives BCDMA support, which is not used by the glue?
> help
> Say y here to support the K3 NAVSS DMA glue interface
> If unsure, say N.
>
> config TI_K3_PSIL
> tristate
> - default TI_K3_UDMA
> + default TI_K3_UDMA || TI_K3_UDMA_V2
>
> config TI_DMA_CROSSBAR
> bool
> diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
> index 41bfba944dc6c..296aa3421e71b 100644
> --- a/drivers/dma/ti/Makefile
> +++ b/drivers/dma/ti/Makefile
> @@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
> obj-$(CONFIG_TI_EDMA) += edma.o
> obj-$(CONFIG_DMA_OMAP) += omap-dma.o
> obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
> +obj-$(CONFIG_TI_K3_UDMA_V2) += k3-udma-v2.o
> obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
> obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
> k3-psil-lib-objs := k3-psil.o \
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index 0ffc6becc402e..ba0fc048234ac 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -171,8 +171,13 @@ bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
> uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
> return true;
>
> - peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> - bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + if (uc->ud->match_data->type >= DMA_TYPE_BCDMA_V2) {
> + peer_bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + } else {
> + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
> + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
> + }
>
> /* Transfer is incomplete, store current residue and time stamp */
> if (peer_bcnt < bcnt) {
> @@ -319,6 +324,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;
> + u32 extra_flags = 0;
nitpick: reverse christmas tree order
> u64 asel;
>
> /* estimate the number of TRs we will need */
> @@ -342,6 +348,9 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
> else
> asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
>
> + if (dir == DMA_MEM_TO_DEV && uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)
I would add the evaluation order in reverse to skip checking direction
for UDMA_V1.
> + extra_flags = CPPI5_TR_CSF_EOP;
> +
> tr_req = d->hwdesc[0].tr_req_base;
> for_each_sg(sgl, sgent, sglen, i) {
> dma_addr_t sg_addr = sg_dma_address(sgent);
> @@ -358,7 +367,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
>
> cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
> false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> - cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
> + cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>
> sg_addr |= asel;
> tr_req[tr_idx].addr = sg_addr;
> @@ -372,7 +381,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
> false, false,
> CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
> cppi5_tr_csf_set(&tr_req[tr_idx].flags,
> - CPPI5_TR_CSF_SUPR_EVT);
> + CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>
> tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
> tr_req[tr_idx].icnt0 = tr1_cnt0;
> @@ -632,7 +641,8 @@ int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
> d->static_tr.bstcnt = d->residue / d->sglen / div;
> else
> d->static_tr.bstcnt = d->residue / div;
> - } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
> + } else if ((uc->ud->match_data->type == DMA_TYPE_BCDMA ||
> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) &&
Have you thought of adding a version member to struct udma_match_data
and use that instead of distinct different types for BCDMA/PKTDMA?
Here for example you would not need any change as the code is common for
both v1 and v2.
> uc->config.dir == DMA_DEV_TO_MEM &&
> uc->cyclic) {
> /*
...
> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
> new file mode 100644
> index 0000000000000..af06d25fd598b
> --- /dev/null
> +++ b/drivers/dma/ti/k3-udma-v2.c
...
> +static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
> +{
> + struct udma_chan_config *ucc;
> + struct psil_endpoint_config *ep_config;
> + struct udma_v2_filter_param *filter_param;
> + struct udma_chan *uc;
> + struct udma_dev *ud;
nitpick: reverse christmas tree order
also in few other places.
--
Péter
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
2026-02-03 6:37 ` Péter Ujfalusi
@ 2026-02-03 8:22 ` Sai Sree Kartheek Adivi
2026-02-03 18:14 ` Péter Ujfalusi
0 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-02-03 8:22 UTC (permalink / raw)
To: Péter Ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr
Cc: r-sharma3, gehariprasath
On 03/02/26 12:07, Péter Ujfalusi wrote:
>
> On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
>> Add support for BCDMA_V2.
>>
>> The BCDMA_V2 is different than the existing BCDMA supported by the
>> k3-udma driver.
>>
>> The changes in BCDMA_V2 are:
>> - Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
>> needs to set in the RT_CTL register.
>> - Static channel mapping: Each channel is mapped to a single peripheral.
>> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
>> directly connected to GIC.
>> - Remote side configuration handled by DMA. So no need to write to PEER
>> registers to START / STOP / PAUSE / TEARDOWN.
>>
>> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
>> ---
>> drivers/dma/ti/Kconfig | 16 +-
>> drivers/dma/ti/Makefile | 1 +
>> drivers/dma/ti/k3-udma-common.c | 75 +-
>> drivers/dma/ti/k3-udma-v2.c | 1283 +++++++++++++++++++++++++++++
>> drivers/dma/ti/k3-udma.h | 117 +--
>> include/linux/soc/ti/k3-ringacc.h | 3 +
>> 6 files changed, 1429 insertions(+), 66 deletions(-)
>> create mode 100644 drivers/dma/ti/k3-udma-v2.c
>>
>> diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
>> index 712e456015459..ada2ea8aca4b0 100644
>> --- a/drivers/dma/ti/Kconfig
>> +++ b/drivers/dma/ti/Kconfig
>> @@ -49,6 +49,18 @@ config TI_K3_UDMA
>> Enable support for the TI UDMA (Unified DMA) controller. This
>> DMA engine is used in AM65x and j721e.
>>
>> +config TI_K3_UDMA_V2
>> + tristate "Texas Instruments K3 UDMA v2 support"
>> + depends on ARCH_K3
>> + select DMA_ENGINE
>> + select DMA_VIRTUAL_CHANNELS
>> + select TI_K3_UDMA_COMMON
>> + select TI_K3_RINGACC
>> + select TI_K3_PSIL
>> + help
>> + Enable support for the TI UDMA (Unified DMA) v2 controller. This
>> + DMA engine is used in AM62L.
>> +
>> config TI_K3_UDMA_COMMON
>> tristate
>> default n
>> @@ -56,14 +68,14 @@ config TI_K3_UDMA_COMMON
>> config TI_K3_UDMA_GLUE_LAYER
>> tristate "Texas Instruments UDMA Glue layer for non DMAengine users"
>> depends on ARCH_K3 || COMPILE_TEST
>> - depends on TI_K3_UDMA
>> + depends on TI_K3_UDMA || TI_K3_UDMA_V2
> At this point the glue layer should not have dependency on UDMA_V2 as it
> only receives BCDMA support, which is not used by the glue?
>
>> help
>> Say y here to support the K3 NAVSS DMA glue interface
>> If unsure, say N.
>>
>> config TI_K3_PSIL
>> tristate
>> - default TI_K3_UDMA
>> + default TI_K3_UDMA || TI_K3_UDMA_V2
>>
>> config TI_DMA_CROSSBAR
>> bool
>> diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
>> index 41bfba944dc6c..296aa3421e71b 100644
>> --- a/drivers/dma/ti/Makefile
>> +++ b/drivers/dma/ti/Makefile
>> @@ -3,6 +3,7 @@ obj-$(CONFIG_TI_CPPI41) += cppi41.o
>> obj-$(CONFIG_TI_EDMA) += edma.o
>> obj-$(CONFIG_DMA_OMAP) += omap-dma.o
>> obj-$(CONFIG_TI_K3_UDMA) += k3-udma.o
>> +obj-$(CONFIG_TI_K3_UDMA_V2) += k3-udma-v2.o
>> obj-$(CONFIG_TI_K3_UDMA_COMMON) += k3-udma-common.o
>> obj-$(CONFIG_TI_K3_UDMA_GLUE_LAYER) += k3-udma-glue.o
>> k3-psil-lib-objs := k3-psil.o \
>> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
>> index 0ffc6becc402e..ba0fc048234ac 100644
>> --- a/drivers/dma/ti/k3-udma-common.c
>> +++ b/drivers/dma/ti/k3-udma-common.c
>> @@ -171,8 +171,13 @@ bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d)
>> uc->config.dir != DMA_MEM_TO_DEV || !(uc->config.tx_flags & DMA_PREP_INTERRUPT))
>> return true;
>>
>> - peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
>> - bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
>> + if (uc->ud->match_data->type >= DMA_TYPE_BCDMA_V2) {
>> + peer_bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_PERIPH_BCNT_REG);
>> + bcnt = udma_chanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
>> + } else {
>> + peer_bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_PEER_BCNT_REG);
>> + bcnt = udma_tchanrt_read(uc, UDMA_CHAN_RT_BCNT_REG);
>> + }
>>
>> /* Transfer is incomplete, store current residue and time stamp */
>> if (peer_bcnt < bcnt) {
>> @@ -319,6 +324,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;
>> + u32 extra_flags = 0;
> nitpick: reverse christmas tree order
>
>> u64 asel;
>>
>> /* estimate the number of TRs we will need */
>> @@ -342,6 +348,9 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
>> else
>> asel = (u64)uc->config.asel << K3_ADDRESS_ASEL_SHIFT;
>>
>> + if (dir == DMA_MEM_TO_DEV && uc->ud->match_data->type == DMA_TYPE_BCDMA_V2)
> I would add the evaluation order in reverse to skip checking direction
> for UDMA_V1.
>
>> + extra_flags = CPPI5_TR_CSF_EOP;
>> +
>> tr_req = d->hwdesc[0].tr_req_base;
>> for_each_sg(sgl, sgent, sglen, i) {
>> dma_addr_t sg_addr = sg_dma_address(sgent);
>> @@ -358,7 +367,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
>>
>> cppi5_tr_init(&tr_req[tr_idx].flags, CPPI5_TR_TYPE1, false,
>> false, CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
>> - cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT);
>> + cppi5_tr_csf_set(&tr_req[tr_idx].flags, CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>>
>> sg_addr |= asel;
>> tr_req[tr_idx].addr = sg_addr;
>> @@ -372,7 +381,7 @@ udma_prep_slave_sg_tr(struct udma_chan *uc, struct scatterlist *sgl,
>> false, false,
>> CPPI5_TR_EVENT_SIZE_COMPLETION, 0);
>> cppi5_tr_csf_set(&tr_req[tr_idx].flags,
>> - CPPI5_TR_CSF_SUPR_EVT);
>> + CPPI5_TR_CSF_SUPR_EVT | extra_flags);
>>
>> tr_req[tr_idx].addr = sg_addr + tr0_cnt1 * tr0_cnt0;
>> tr_req[tr_idx].icnt0 = tr1_cnt0;
>> @@ -632,7 +641,8 @@ int udma_configure_statictr(struct udma_chan *uc, struct udma_desc *d,
>> d->static_tr.bstcnt = d->residue / d->sglen / div;
>> else
>> d->static_tr.bstcnt = d->residue / div;
>> - } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
>> + } else if ((uc->ud->match_data->type == DMA_TYPE_BCDMA ||
>> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) &&
> Have you thought of adding a version member to struct udma_match_data
> and use that instead of distinct different types for BCDMA/PKTDMA?
>
> Here for example you would not need any change as the code is common for
> both v1 and v2.
Hi Peter,
I'm preparing a v5 and wanted to align with you on the handling of
different dma
variants (udma, bcdma, pktdma & v1, v2).
Frank suggested moving toward feature flags (capabilities) in the
match_data,
rather than checking type. [1]
I want to get your thoughts on Frank's suggestion before I proceed. Do
you have
any strong objections to using feature flags? I see merit in that
approach for
scaling to possible future DMA variants in K3 SoCs.
Thanks,
Kartheek
[1] https://lore.kernel.org/all/aXzXYMixFpuornQF@lizhi-Precision-Tower-5810/
>
>> uc->config.dir == DMA_DEV_TO_MEM &&
>> uc->cyclic) {
>> /*
> ...
>
>> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
>> new file mode 100644
>> index 0000000000000..af06d25fd598b
>> --- /dev/null
>> +++ b/drivers/dma/ti/k3-udma-v2.c
> ...
>
>> +static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
>> +{
>> + struct udma_chan_config *ucc;
>> + struct psil_endpoint_config *ep_config;
>> + struct udma_v2_filter_param *filter_param;
>> + struct udma_chan *uc;
>> + struct udma_dev *ud;
> nitpick: reverse christmas tree order
> also in few other places.
>
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
2026-02-03 8:22 ` Sai Sree Kartheek Adivi
@ 2026-02-03 18:14 ` Péter Ujfalusi
0 siblings, 0 replies; 45+ messages in thread
From: Péter Ujfalusi @ 2026-02-03 18:14 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, vkoul, robh, krzk+dt, conor+dt, nm,
ssantosh, dmaengine, devicetree, linux-kernel, linux-arm-kernel,
vigneshr
Cc: r-sharma3, gehariprasath
Hi Kartheek,
On 03/02/2026 10:22, Sai Sree Kartheek Adivi wrote:
>>> @@ -632,7 +641,8 @@ int udma_configure_statictr(struct udma_chan *uc,
>>> struct udma_desc *d,
>>> d->static_tr.bstcnt = d->residue / d->sglen / div;
>>> else
>>> d->static_tr.bstcnt = d->residue / div;
>>> - } else if (uc->ud->match_data->type == DMA_TYPE_BCDMA &&
>>> + } else if ((uc->ud->match_data->type == DMA_TYPE_BCDMA ||
>>> + uc->ud->match_data->type == DMA_TYPE_BCDMA_V2) &&
>> Have you thought of adding a version member to struct udma_match_data
>> and use that instead of distinct different types for BCDMA/PKTDMA?
>>
>> Here for example you would not need any change as the code is common for
>> both v1 and v2.
>
> Hi Peter,
>
>
> I'm preparing a v5 and wanted to align with you on the handling of
> different dma
>
> variants (udma, bcdma, pktdma & v1, v2).
>
>
> Frank suggested moving toward feature flags (capabilities) in the
> match_data,
>
> rather than checking type. [1]
>
>
> I want to get your thoughts on Frank's suggestion before I proceed. Do
> you have
>
> any strong objections to using feature flags? I see merit in that
> approach for
>
> scaling to possible future DMA variants in K3 SoCs.
You have several differences here and there (small and big) between the
v1 and v2, if you want to feature flag these out you would need to have
a meaningful flag for each and every one of them.
I find this a daunting task to be honest, so I would go with the simpler
way to just use version to cover _all_ differences in one step.
How one should be handling things if
A) feature = FEATURE_1
B) feature = FEATURE_2
C) feature = FEATURE_1 | FEATURE_2
D) feature = FEATURE_1 | FEATURE_2 | FEATURE_3
E) feature = FEATURE_1 | FEATURE_3
...
I think this might get out of hand easily, but you know the hardware
better, which way fits better, which will scale better for the future.
Also, you set a FEATURE flag for V2, but it might be that in V3 revision
the same thing must be done in a third way, so you would need to
allocate a bit array to say that this feature have this three ways of
handling, etc.
Either way will help to make the code a bit cleaner, which is already in
good shape.
--
Péter
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (14 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 15/19] dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2 Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-02-03 6:25 ` Péter Ujfalusi
2026-01-30 11:01 ` [PATCH v4 17/19] dmaengine: ti: k3-udma-v2: Update glue layer to support " Sai Sree Kartheek Adivi
` (3 subsequent siblings)
19 siblings, 1 reply; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
The PKTDMA V2 is different than the existing PKTDMA supported by the
k3-udma driver.
The changes in PKTDMA V2 are:
- Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
needs to set in the RT_CTL register.
- Static channel mapping: Each channel is mapped to a single
peripheral.
- Direct IRQs: There is no INT-A and interrupt lines from DMA are
directly connected to GIC.
- Remote side configuration handled by DMA. So no need to write to
PEER registers to START / STOP / PAUSE / TEARDOWN.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 29 ++++-
drivers/dma/ti/k3-udma-v2.c | 219 ++++++++++++++++++++++++++++++--
drivers/dma/ti/k3-udma.h | 3 +
3 files changed, 232 insertions(+), 19 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index ba0fc048234ac..d6459bcc17599 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -2461,12 +2461,21 @@ int pktdma_setup_resources(struct udma_dev *ud)
ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
sizeof(unsigned long), GFP_KERNEL);
+ bitmap_zero(ud->tchan_map, ud->tchan_cnt);
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);
+ if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ ud->rchan_map = ud->tchan_map;
+ ud->rchans = ud->tchans;
+ ud->chan_map = ud->tchan_map;
+ ud->chans = ud->tchans;
+ } else {
+ ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
+ sizeof(unsigned long), GFP_KERNEL);
+ bitmap_zero(ud->rchan_map, ud->rchan_cnt);
+ 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);
@@ -2474,6 +2483,8 @@ int pktdma_setup_resources(struct udma_dev *ud)
GFP_KERNEL);
ud->tflow_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tflow_cnt),
sizeof(unsigned long), GFP_KERNEL);
+ bitmap_zero(ud->tflow_map, ud->tflow_cnt);
+
if (!ud->tchan_map || !ud->rchan_map || !ud->tflow_map || !ud->tchans ||
!ud->rchans || !ud->rflows || !ud->rflow_in_use)
@@ -2502,6 +2513,7 @@ int setup_resources(struct udma_dev *ud)
ret = bcdma_setup_resources(ud);
break;
case DMA_TYPE_PKTDMA:
+ case DMA_TYPE_PKTDMA_V2:
ret = pktdma_setup_resources(ud);
break;
default:
@@ -2511,7 +2523,7 @@ int setup_resources(struct udma_dev *ud)
if (ret)
return ret;
- if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ if (ud->match_data->type >= DMA_TYPE_BCDMA_V2) {
ch_count = ud->bchan_cnt + ud->tchan_cnt;
if (ud->bchan_cnt)
ch_count -= bitmap_weight(ud->bchan_map, ud->bchan_cnt);
@@ -2572,6 +2584,13 @@ int setup_resources(struct udma_dev *ud)
ud->rchan_cnt - bitmap_weight(ud->rchan_map,
ud->rchan_cnt));
break;
+ case DMA_TYPE_PKTDMA_V2:
+ dev_info(dev,
+ "Channels: %d (tchan + rchan: %u)\n",
+ ch_count,
+ ud->chan_cnt - bitmap_weight(ud->chan_map,
+ ud->chan_cnt));
+ break;
default:
break;
}
diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
index af06d25fd598b..6761a079025ba 100644
--- a/drivers/dma/ti/k3-udma-v2.c
+++ b/drivers/dma/ti/k3-udma-v2.c
@@ -744,6 +744,146 @@ static int bcdma_v2_alloc_chan_resources(struct dma_chan *chan)
return ret;
}
+static int pktdma_v2_alloc_chan_resources(struct dma_chan *chan)
+{
+ struct udma_chan *uc = to_udma_chan(chan);
+ struct udma_dev *ud = to_udma_dev(chan->device);
+ u32 irq_ring_idx;
+ __be32 addr[2] = {0, 0};
+ struct of_phandle_args out_irq;
+ 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) transfer */
+ dev_dbg(uc->ud->dev, "%s: chan%d as MEM-to-DEV\n", __func__,
+ uc->id);
+
+ ret = udma_v2_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->config.mapped_channel_id;
+ break;
+ case DMA_DEV_TO_MEM:
+ /* Slave transfer synchronized - dev to mem (RX) transfer */
+ dev_dbg(uc->ud->dev, "%s: chan%d as DEV-to-MEM\n", __func__,
+ uc->id);
+
+ ret = udma_v2_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->config.mapped_channel_id;
+ udma_write(uc->rflow->reg_rt, UDMA_RX_FLOWRT_RFA, BIT(28));
+ 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);
+ ud->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;
+
+ uc->psil_paired = true;
+
+ out_irq.np = dev_of_node(ud->dev);
+ out_irq.args_count = 1;
+ out_irq.args[0] = irq_ring_idx;
+ ret = of_irq_parse_raw(addr, &out_irq);
+ if (ret)
+ return ret;
+
+ uc->irq_num_ring = irq_create_of_mapping(&out_irq);
+
+ ret = devm_request_irq(ud->dev, uc->irq_num_ring, udma_v2_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_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 enum dma_status udma_v2_tx_status(struct dma_chan *chan,
dma_cookie_t cookie,
struct dma_tx_state *txstate)
@@ -838,6 +978,7 @@ static int udma_v2_resume(struct dma_chan *chan)
}
static struct platform_driver bcdma_v2_driver;
+static struct platform_driver pktdma_v2_driver;
static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
{
@@ -847,7 +988,8 @@ static bool udma_v2_dma_filter_fn(struct dma_chan *chan, void *param)
struct udma_chan *uc;
struct udma_dev *ud;
- if (chan->device->dev->driver != &bcdma_v2_driver.driver)
+ if (chan->device->dev->driver != &bcdma_v2_driver.driver &&
+ chan->device->dev->driver != &pktdma_v2_driver.driver)
return false;
uc = to_udma_chan(chan);
@@ -890,7 +1032,7 @@ static bool udma_v2_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_BCDMA_V2 &&
+ if (ud->match_data->type >= DMA_TYPE_BCDMA_V2 &&
ep_config->mapped_channel_id >= 0) {
ucc->mapped_channel_id = ep_config->mapped_channel_id;
ucc->default_flow_id = ep_config->default_flow_id;
@@ -989,11 +1131,33 @@ static struct udma_match_data bcdma_v2_am62l_data = {
.rchan_cnt = 128,
};
+static struct udma_match_data pktdma_v2_am62l_data = {
+ .type = DMA_TYPE_PKTDMA_V2,
+ .psil_base = 0x1000,
+ .enable_memcpy_support = false, /* PKTDMA does not support MEM_TO_MEM */
+ .flags = UDMA_FLAGS_J7_CLASS,
+ .statictr_z_mask = GENMASK(23, 0),
+ .burst_size = {
+ TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */
+ 0, /* No H Channels */
+ 0, /* No UH Channels */
+ },
+ .tchan_cnt = 97,
+ .rchan_cnt = 97,
+ .chan_cnt = 97,
+ .tflow_cnt = 112,
+ .rflow_cnt = 112,
+};
+
static const struct of_device_id udma_of_match[] = {
{
.compatible = "ti,am62l-dmss-bcdma",
.data = &bcdma_v2_am62l_data,
},
+ {
+ .compatible = "ti,am62l-dmss-pktdma",
+ .data = &pktdma_v2_am62l_data,
+ },
{ /* Sentinel */ },
};
@@ -1012,15 +1176,22 @@ static int udma_v2_get_mmrs(struct platform_device *pdev, struct udma_dev *ud)
if (IS_ERR(ud->mmrs[V2_MMR_GCFG]))
return PTR_ERR(ud->mmrs[V2_MMR_GCFG]);
- ud->bchan_cnt = ud->match_data->bchan_cnt;
- /* There are no tchan and rchan in BCDMA_V2.
+ /* There are no tchan and rchan in BCDMA_V2 and PKTDMA_V2.
* Duplicate chan as tchan and rchan to keep the common code
- * in k3-udma-common.c functional for BCDMA_V2.
+ * in k3-udma-common.c functional.
*/
- ud->chan_cnt = ud->match_data->chan_cnt;
- ud->tchan_cnt = ud->match_data->chan_cnt;
- ud->rchan_cnt = ud->match_data->chan_cnt;
- ud->rflow_cnt = ud->chan_cnt;
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ ud->bchan_cnt = ud->match_data->bchan_cnt;
+ ud->chan_cnt = ud->match_data->chan_cnt;
+ ud->tchan_cnt = ud->match_data->chan_cnt;
+ ud->rchan_cnt = ud->match_data->chan_cnt;
+ ud->rflow_cnt = ud->chan_cnt;
+ } else if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ ud->chan_cnt = ud->match_data->chan_cnt;
+ ud->tchan_cnt = ud->match_data->tchan_cnt;
+ ud->rchan_cnt = ud->match_data->rchan_cnt;
+ ud->rflow_cnt = ud->match_data->rflow_cnt;
+ }
for (i = 1; i < V2_MMR_LAST; i++) {
if (i == V2_MMR_BCHANRT && ud->bchan_cnt == 0)
@@ -1075,6 +1246,7 @@ static int udma_v2_probe(struct platform_device *pdev)
ud->reset_chan = udma_v2_reset_chan;
ud->decrement_byte_counters = udma_v2_decrement_byte_counters;
ud->bcdma_setup_sci_resources = NULL;
+ ud->pktdma_setup_sci_resources = NULL;
ret = udma_v2_get_mmrs(pdev, ud);
if (ret)
@@ -1082,7 +1254,14 @@ static int udma_v2_probe(struct platform_device *pdev)
struct k3_ringacc_init_data ring_init_data = {0};
- ring_init_data.num_rings = ud->bchan_cnt + ud->chan_cnt;
+ if (ud->match_data->type == DMA_TYPE_BCDMA_V2) {
+ ring_init_data.num_rings = ud->bchan_cnt + ud->chan_cnt;
+ } else if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ ring_init_data.num_rings = ud->rflow_cnt;
+
+ ud->rflow_rt = devm_platform_ioremap_resource_byname(pdev, "ringrt");
+ ring_init_data.base_rt = ud->rflow_rt;
+ }
ud->ringacc = k3_ringacc_dmarings_init(pdev, &ring_init_data);
@@ -1091,8 +1270,10 @@ static int udma_v2_probe(struct platform_device *pdev)
dma_cap_set(DMA_SLAVE, ud->ddev.cap_mask);
- dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
- ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
+ if (ud->match_data->type != DMA_TYPE_PKTDMA_V2) {
+ dma_cap_set(DMA_CYCLIC, ud->ddev.cap_mask);
+ ud->ddev.device_prep_dma_cyclic = udma_prep_dma_cyclic;
+ }
ud->ddev.device_config = udma_slave_config;
ud->ddev.device_prep_slave_sg = udma_prep_slave_sg;
@@ -1106,8 +1287,18 @@ static int udma_v2_probe(struct platform_device *pdev)
ud->ddev.dbg_summary_show = udma_dbg_summary_show;
#endif
- ud->ddev.device_alloc_chan_resources =
- bcdma_v2_alloc_chan_resources;
+ switch (ud->match_data->type) {
+ case DMA_TYPE_BCDMA_V2:
+ ud->ddev.device_alloc_chan_resources =
+ bcdma_v2_alloc_chan_resources;
+ break;
+ case DMA_TYPE_PKTDMA_V2:
+ ud->ddev.device_alloc_chan_resources =
+ pktdma_v2_alloc_chan_resources;
+ break;
+ default:
+ return -EINVAL;
+ }
ud->ddev.device_free_chan_resources = udma_free_chan_resources;
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index ca726ea9864ae..b91e645831f64 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -23,8 +23,11 @@
#define UDMA_RX_FLOW_ID_FW_OES_REG 0x80
#define UDMA_RX_FLOW_ID_FW_STATUS_REG 0x88
+#define UDMA_RX_FLOWRT_RFA 0x8
+
/* BCHANRT/TCHANRT/RCHANRT registers */
#define UDMA_CHAN_RT_CTL_REG 0x0
+#define UDMA_CHAN_RT_CFG_REG 0x4
#define UDMA_CHAN_RT_SWTRIG_REG 0x8
#define UDMA_CHAN_RT_STDATA_REG 0x80
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2
2026-01-30 11:01 ` [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2 Sai Sree Kartheek Adivi
@ 2026-02-03 6:25 ` Péter Ujfalusi
2026-02-03 7:51 ` Sai Sree Kartheek Adivi
0 siblings, 1 reply; 45+ messages in thread
From: Péter Ujfalusi @ 2026-02-03 6:25 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, vkoul, robh, krzk+dt, conor+dt, nm,
ssantosh, dmaengine, devicetree, linux-kernel, linux-arm-kernel,
vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
> The PKTDMA V2 is different than the existing PKTDMA supported by the
> k3-udma driver.
>
> The changes in PKTDMA V2 are:
> - Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
> needs to set in the RT_CTL register.
> - Static channel mapping: Each channel is mapped to a single
> peripheral.
> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
> directly connected to GIC.
> - Remote side configuration handled by DMA. So no need to write to
> PEER registers to START / STOP / PAUSE / TEARDOWN.
Plus I suppose..
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/k3-udma-common.c | 29 ++++-
> drivers/dma/ti/k3-udma-v2.c | 219 ++++++++++++++++++++++++++++++--
> drivers/dma/ti/k3-udma.h | 3 +
> 3 files changed, 232 insertions(+), 19 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index ba0fc048234ac..d6459bcc17599 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -2461,12 +2461,21 @@ int pktdma_setup_resources(struct udma_dev *ud)
>
> ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
> sizeof(unsigned long), GFP_KERNEL);
> + bitmap_zero(ud->tchan_map, ud->tchan_cnt);
> 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);
> + if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
> + ud->rchan_map = ud->tchan_map;
> + ud->rchans = ud->tchans;
> + ud->chan_map = ud->tchan_map;
> + ud->chans = ud->tchans;
It has single channel space and the TX/RX functionality alternates
within the space?
chX: TX, chX+1: TX, chX+2: RX, chX+3: TX, etc?
> + } else {
> + ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
> + sizeof(unsigned long), GFP_KERNEL);
> + bitmap_zero(ud->rchan_map, ud->rchan_cnt);
> + ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
> + GFP_KERNEL);
> + }
...
> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
> index af06d25fd598b..6761a079025ba 100644
> --- a/drivers/dma/ti/k3-udma-v2.c
> +++ b/drivers/dma/ti/k3-udma-v2.c
> @@ -744,6 +744,146 @@ static int bcdma_v2_alloc_chan_resources(struct dma_chan *chan)
> return ret;
> }
>
> +static int pktdma_v2_alloc_chan_resources(struct dma_chan *chan)
> +{
> + struct udma_chan *uc = to_udma_chan(chan);
> + struct udma_dev *ud = to_udma_dev(chan->device);
> + u32 irq_ring_idx;
> + __be32 addr[2] = {0, 0};
> + struct of_phandle_args out_irq;
> + int ret;
Nitpick: revers christmas tree declaration order.
--
Péter
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2
2026-02-03 6:25 ` Péter Ujfalusi
@ 2026-02-03 7:51 ` Sai Sree Kartheek Adivi
0 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-02-03 7:51 UTC (permalink / raw)
To: Péter Ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr
Cc: r-sharma3, gehariprasath
On 03/02/26 11:55, Péter Ujfalusi wrote:
>
> On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
>> The PKTDMA V2 is different than the existing PKTDMA supported by the
>> k3-udma driver.
>>
>> The changes in PKTDMA V2 are:
>> - Autopair: There is no longer a need for PSIL pair and AUTOPAIR bit
>> needs to set in the RT_CTL register.
>> - Static channel mapping: Each channel is mapped to a single
>> peripheral.
>> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
>> directly connected to GIC.
>> - Remote side configuration handled by DMA. So no need to write to
>> PEER registers to START / STOP / PAUSE / TEARDOWN.
> Plus I suppose..
>
>> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
>> ---
>> drivers/dma/ti/k3-udma-common.c | 29 ++++-
>> drivers/dma/ti/k3-udma-v2.c | 219 ++++++++++++++++++++++++++++++--
>> drivers/dma/ti/k3-udma.h | 3 +
>> 3 files changed, 232 insertions(+), 19 deletions(-)
>>
>> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
>> index ba0fc048234ac..d6459bcc17599 100644
>> --- a/drivers/dma/ti/k3-udma-common.c
>> +++ b/drivers/dma/ti/k3-udma-common.c
>> @@ -2461,12 +2461,21 @@ int pktdma_setup_resources(struct udma_dev *ud)
>>
>> ud->tchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->tchan_cnt),
>> sizeof(unsigned long), GFP_KERNEL);
>> + bitmap_zero(ud->tchan_map, ud->tchan_cnt);
>> 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);
>> + if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
>> + ud->rchan_map = ud->tchan_map;
>> + ud->rchans = ud->tchans;
>> + ud->chan_map = ud->tchan_map;
>> + ud->chans = ud->tchans;
> It has single channel space and the TX/RX functionality alternates
> within the space?
> chX: TX, chX+1: TX, chX+2: RX, chX+3: TX, etc?
Yes Peter, that is the case.
>
>> + } else {
>> + ud->rchan_map = devm_kmalloc_array(dev, BITS_TO_LONGS(ud->rchan_cnt),
>> + sizeof(unsigned long), GFP_KERNEL);
>> + bitmap_zero(ud->rchan_map, ud->rchan_cnt);
>> + ud->rchans = devm_kcalloc(dev, ud->rchan_cnt, sizeof(*ud->rchans),
>> + GFP_KERNEL);
>> + }
> ...
>
>> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
>> index af06d25fd598b..6761a079025ba 100644
>> --- a/drivers/dma/ti/k3-udma-v2.c
>> +++ b/drivers/dma/ti/k3-udma-v2.c
>> @@ -744,6 +744,146 @@ static int bcdma_v2_alloc_chan_resources(struct dma_chan *chan)
>> return ret;
>> }
>>
>> +static int pktdma_v2_alloc_chan_resources(struct dma_chan *chan)
>> +{
>> + struct udma_chan *uc = to_udma_chan(chan);
>> + struct udma_dev *ud = to_udma_dev(chan->device);
>> + u32 irq_ring_idx;
>> + __be32 addr[2] = {0, 0};
>> + struct of_phandle_args out_irq;
>> + int ret;
> Nitpick: revers christmas tree declaration order.
>
^ permalink raw reply [flat|nested] 45+ messages in thread
* [PATCH v4 17/19] dmaengine: ti: k3-udma-v2: Update glue layer to support PKTDMA V2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (15 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 16/19] dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2 Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 18/19] dmaengine: ti: k3-udma: Validate resource ID and fix logging in reservation Sai Sree Kartheek Adivi
` (2 subsequent siblings)
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
Update glue layer to support PKTDMA V2 for non DMAengine users.
The updates include
- Handling absence of TISCI
- Direct IRQs
- Autopair: Lack of PSIL pair.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-glue.c | 91 ++++++++++++++++++++++----------
drivers/dma/ti/k3-udma-private.c | 38 ++++++++++++-
drivers/dma/ti/k3-udma.h | 2 +
3 files changed, 102 insertions(+), 29 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-glue.c b/drivers/dma/ti/k3-udma-glue.c
index f87d244cc2d67..1e7b3225ef07f 100644
--- a/drivers/dma/ti/k3-udma-glue.c
+++ b/drivers/dma/ti/k3-udma-glue.c
@@ -244,6 +244,9 @@ static int k3_udma_glue_cfg_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
const struct udma_tisci_rm *tisci_rm = tx_chn->common.tisci_rm;
struct ti_sci_msg_rm_udmap_tx_ch_cfg req;
+ if (!tisci_rm->tisci)
+ return 0;
+
memset(&req, 0, sizeof(req));
req.valid_params = TI_SCI_MSG_VALUE_RM_UDMAP_CH_PAUSE_ON_ERR_VALID |
@@ -502,21 +505,26 @@ int k3_udma_glue_enable_tx_chn(struct k3_udma_glue_tx_channel *tx_chn)
{
int ret;
- ret = xudma_navss_psil_pair(tx_chn->common.udmax,
- tx_chn->common.src_thread,
- tx_chn->common.dst_thread);
- if (ret) {
- dev_err(tx_chn->common.dev, "PSI-L request err %d\n", ret);
- return ret;
- }
+ if (tx_chn->common.udmax->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_AUTOPAIR | UDMA_CHAN_RT_CTL_EN);
+ } else {
+ ret = xudma_navss_psil_pair(tx_chn->common.udmax,
+ tx_chn->common.src_thread,
+ tx_chn->common.dst_thread);
+ if (ret) {
+ dev_err(tx_chn->common.dev, "PSI-L request err %d\n", ret);
+ return ret;
+ }
- tx_chn->psil_paired = true;
+ tx_chn->psil_paired = true;
- xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG,
- UDMA_PEER_RT_EN_ENABLE);
+ xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_PEER_RT_EN_REG,
+ UDMA_PEER_RT_EN_ENABLE);
- xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG,
- UDMA_CHAN_RT_CTL_EN);
+ xudma_tchanrt_write(tx_chn->udma_tchanx, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN);
+ }
k3_udma_glue_dump_tx_rt_chn(tx_chn, "txchn en");
return 0;
@@ -682,7 +690,6 @@ static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
TI_SCI_MSG_VALUE_RM_UDMAP_CH_CHAN_TYPE_VALID |
TI_SCI_MSG_VALUE_RM_UDMAP_CH_ATYPE_VALID;
- req.nav_id = tisci_rm->tisci_dev_id;
req.index = rx_chn->udma_rchan_id;
req.rx_fetch_size = rx_chn->common.hdesc_size >> 2;
/*
@@ -702,11 +709,18 @@ static int k3_udma_glue_cfg_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
req.rx_chan_type = TI_SCI_RM_UDMAP_CHAN_TYPE_PKT_PBRR;
req.rx_atype = rx_chn->common.atype_asel;
+ if (!tisci_rm->tisci) {
+ // TODO: look at the chan settings
+ xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CFG_REG,
+ UDMA_CHAN_RT_CTL_TDOWN | UDMA_CHAN_RT_CTL_PAUSE);
+ return 0;
+ }
+
+ req.nav_id = tisci_rm->tisci_dev_id;
ret = tisci_rm->tisci_udmap_ops->rx_ch_cfg(tisci_rm->tisci, &req);
if (ret)
dev_err(rx_chn->common.dev, "rchan%d cfg failed %d\n",
- rx_chn->udma_rchan_id, ret);
-
+ rx_chn->udma_rchan_id, ret);
return ret;
}
@@ -755,8 +769,11 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
}
if (xudma_is_pktdma(rx_chn->common.udmax)) {
- rx_ringfdq_id = flow->udma_rflow_id +
+ if (tisci_rm->tisci)
+ rx_ringfdq_id = flow->udma_rflow_id +
xudma_get_rflow_ring_offset(rx_chn->common.udmax);
+ else
+ rx_ringfdq_id = flow->udma_rflow_id;
rx_ring_id = 0;
} else {
rx_ring_id = flow_cfg->ring_rxq_id;
@@ -803,6 +820,13 @@ static int k3_udma_glue_cfg_rx_flow(struct k3_udma_glue_rx_channel *rx_chn,
rx_ringfdq_id = k3_ringacc_get_ring_id(flow->ringrxfdq);
}
+ if (!tisci_rm->tisci) {
+ xudma_rflowrt_write(flow->udma_rflow, UDMA_RX_FLOWRT_RFA,
+ UDMA_CHAN_RT_CTL_TDOWN | UDMA_CHAN_RT_CTL_PAUSE);
+ rx_chn->flows_ready++;
+ return 0;
+ }
+
memset(&req, 0, sizeof(req));
req.valid_params =
@@ -1307,6 +1331,9 @@ int k3_udma_glue_rx_flow_enable(struct k3_udma_glue_rx_channel *rx_chn,
if (!rx_chn->remote)
return -EINVAL;
+ if (!tisci_rm->tisci)
+ return 0;
+
rx_ring_id = k3_ringacc_get_ring_id(flow->ringrx);
rx_ringfdq_id = k3_ringacc_get_ring_id(flow->ringrxfdq);
@@ -1348,6 +1375,9 @@ int k3_udma_glue_rx_flow_disable(struct k3_udma_glue_rx_channel *rx_chn,
if (!rx_chn->remote)
return -EINVAL;
+ if (!tisci_rm->tisci)
+ return 0;
+
memset(&req, 0, sizeof(req));
req.valid_params =
TI_SCI_MSG_VALUE_RM_UDMAP_FLOW_DEST_QNUM_VALID |
@@ -1383,21 +1413,26 @@ int k3_udma_glue_enable_rx_chn(struct k3_udma_glue_rx_channel *rx_chn)
if (rx_chn->flows_ready < rx_chn->flow_num)
return -EINVAL;
- ret = xudma_navss_psil_pair(rx_chn->common.udmax,
- rx_chn->common.src_thread,
- rx_chn->common.dst_thread);
- if (ret) {
- dev_err(rx_chn->common.dev, "PSI-L request err %d\n", ret);
- return ret;
- }
+ if (rx_chn->common.udmax->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_AUTOPAIR | UDMA_CHAN_RT_CTL_EN);
+ } else {
+ ret = xudma_navss_psil_pair(rx_chn->common.udmax,
+ rx_chn->common.src_thread,
+ rx_chn->common.dst_thread);
+ if (ret) {
+ dev_err(rx_chn->common.dev, "PSI-L request err %d\n", ret);
+ return ret;
+ }
- rx_chn->psil_paired = true;
+ rx_chn->psil_paired = true;
- xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG,
- UDMA_CHAN_RT_CTL_EN);
+ xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_CTL_REG,
+ UDMA_CHAN_RT_CTL_EN);
- xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_PEER_RT_EN_REG,
- UDMA_PEER_RT_EN_ENABLE);
+ xudma_rchanrt_write(rx_chn->udma_rchanx, UDMA_CHAN_RT_PEER_RT_EN_REG,
+ UDMA_PEER_RT_EN_ENABLE);
+ }
k3_udma_glue_dump_rx_rt_chn(rx_chn, "rxrt en");
return 0;
diff --git a/drivers/dma/ti/k3-udma-private.c b/drivers/dma/ti/k3-udma-private.c
index 44c097fff5ee6..0d07c1ea09586 100644
--- a/drivers/dma/ti/k3-udma-private.c
+++ b/drivers/dma/ti/k3-udma-private.c
@@ -3,6 +3,10 @@
* Copyright (C) 2019 Texas Instruments Incorporated - http://www.ti.com
* Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
*/
+#include <linux/irq.h>
+#include <linux/irqchip.h>
+#include <linux/irqdomain.h>
+#include <linux/interrupt.h>
#include <linux/of.h>
#include <linux/of_platform.h>
@@ -165,15 +169,32 @@ void xudma_##res##rt_write(struct udma_##res *p, int reg, u32 val) \
EXPORT_SYMBOL(xudma_##res##rt_write)
XUDMA_RT_IO_FUNCTIONS(tchan);
XUDMA_RT_IO_FUNCTIONS(rchan);
+XUDMA_RT_IO_FUNCTIONS(rflow);
int xudma_is_pktdma(struct udma_dev *ud)
{
- return ud->match_data->type == DMA_TYPE_PKTDMA;
+ return (ud->match_data->type == DMA_TYPE_PKTDMA ||
+ ud->match_data->type == DMA_TYPE_PKTDMA_V2);
}
EXPORT_SYMBOL(xudma_is_pktdma);
int xudma_pktdma_tflow_get_irq(struct udma_dev *ud, int udma_tflow_id)
{
+ if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ __be32 addr[2] = {0, 0};
+ struct of_phandle_args out_irq;
+ int ret;
+
+ out_irq.np = dev_of_node(ud->dev);
+ out_irq.args_count = 1;
+ out_irq.args[0] = udma_tflow_id;
+ ret = of_irq_parse_raw(addr, &out_irq);
+ if (ret)
+ return ret;
+
+ return irq_create_of_mapping(&out_irq);
+ }
+
const struct udma_oes_offsets *oes = &ud->soc_data->oes;
return msi_get_virq(ud->dev, udma_tflow_id + oes->pktdma_tchan_flow);
@@ -182,6 +203,21 @@ EXPORT_SYMBOL(xudma_pktdma_tflow_get_irq);
int xudma_pktdma_rflow_get_irq(struct udma_dev *ud, int udma_rflow_id)
{
+ if (ud->match_data->type == DMA_TYPE_PKTDMA_V2) {
+ __be32 addr[2] = {0, 0};
+ struct of_phandle_args out_irq;
+ int ret;
+
+ out_irq.np = dev_of_node(ud->dev);
+ out_irq.args_count = 1;
+ out_irq.args[0] = udma_rflow_id;
+ ret = of_irq_parse_raw(addr, &out_irq);
+ if (ret)
+ return ret;
+
+ return irq_create_of_mapping(&out_irq);
+ }
+
const struct udma_oes_offsets *oes = &ud->soc_data->oes;
return msi_get_virq(ud->dev, udma_rflow_id + oes->pktdma_rchan_flow);
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index b91e645831f64..3ae2400e67990 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -739,6 +739,8 @@ u32 xudma_rchanrt_read(struct udma_rchan *rchan, int reg);
void xudma_rchanrt_write(struct udma_rchan *rchan, int reg, u32 val);
bool xudma_rflow_is_gp(struct udma_dev *ud, int id);
int xudma_get_rflow_ring_offset(struct udma_dev *ud);
+u32 xudma_rflowrt_read(struct udma_rflow *rflow, int reg);
+void xudma_rflowrt_write(struct udma_rflow *rflow, int reg, u32 val);
int xudma_is_pktdma(struct udma_dev *ud);
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 18/19] dmaengine: ti: k3-udma: Validate resource ID and fix logging in reservation
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (16 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 17/19] dmaengine: ti: k3-udma-v2: Update glue layer to support " Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 11:01 ` [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing Sai Sree Kartheek Adivi
2026-02-03 6:42 ` [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Péter Ujfalusi
19 siblings, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
The `__udma_reserve_##res` macro currently lacks a bounds check for
the provided `id`. If a caller passes an ID exceeding the resource
count (`ud->res##_cnt`), `test_bit()` performs an out-of-bounds
memory access on the bitmap.
Additionally, the macro returns `-ENOENT` when a resource is already
in use, which is semantically incorrect. The logging logic is also
broken, printing the literal "res##<id>" instead of the resource
name.
Update the macro to:
1. Validate `id` against `ud->res##_cnt` and return `-EINVAL` if out
of bounds.
2. Return `-EBUSY` instead of `-ENOENT` when a resource is already
reserved, correctly reflecting the resource state.
3. Use the stringification operator `#res` to correctly print the
resource name (e.g., "tchan") in error messages.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 9 +++++++--
1 file changed, 7 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index d6459bcc17599..05b2b6b962a06 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -2011,9 +2011,14 @@ struct udma_##res *__udma_reserve_##res(struct udma_dev *ud, \
int id) \
{ \
if (id >= 0) { \
+ if (id >= ud->res##_cnt) { \
+ dev_err(ud->dev, \
+ #res " id %d is out of bounds.\n", id); \
+ return ERR_PTR(-EINVAL); \
+ } \
if (test_bit(id, ud->res##_map)) { \
- dev_err(ud->dev, "res##%d is in use\n", id); \
- return ERR_PTR(-ENOENT); \
+ dev_err(ud->dev, #res "%d is in use\n", id); \
+ return ERR_PTR(-EBUSY); \
} \
} else { \
int start; \
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (17 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 18/19] dmaengine: ti: k3-udma: Validate resource ID and fix logging in reservation Sai Sree Kartheek Adivi
@ 2026-01-30 11:01 ` Sai Sree Kartheek Adivi
2026-01-30 20:37 ` kernel test robot
2026-02-11 7:42 ` Sai Sree Kartheek Adivi
2026-02-03 6:42 ` [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Péter Ujfalusi
19 siblings, 2 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-01-30 11:01 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr,
s-adivi
Cc: r-sharma3, gehariprasath
The driver currently uses a worker thread to free processed
descriptors. Under high load (e.g., CRYPTO_MANAGER_EXTRA_TESTS), the
descriptor allocation rate can significantly outpace the worker
thread's execution rate.
This leads to false resource exhaustion where dma_alloc_coherent()
fails even though many descriptors are waiting in the purge queue to
be freed.
Remove the lazy freeing mechanism (desc_to_purge list and worker) and
instead free the descriptors immediately in the completion callback.
This eliminates the latency gap between hardware completion and
software reclaim, preventing the pool exhaustion during stress tests.
Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
---
drivers/dma/ti/k3-udma-common.c | 40 +++------------------------------
drivers/dma/ti/k3-udma-v2.c | 2 --
drivers/dma/ti/k3-udma.c | 2 --
drivers/dma/ti/k3-udma.h | 3 ---
4 files changed, 3 insertions(+), 44 deletions(-)
diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
index 05b2b6b962a06..f9da00298b60f 100644
--- a/drivers/dma/ti/k3-udma-common.c
+++ b/drivers/dma/ti/k3-udma-common.c
@@ -95,32 +95,6 @@ void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
}
}
-void udma_purge_desc_work(struct work_struct *work)
-{
- struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
- struct virt_dma_desc *vd, *_vd;
- unsigned long flags;
- LIST_HEAD(head);
-
- spin_lock_irqsave(&ud->lock, flags);
- list_splice_tail_init(&ud->desc_to_purge, &head);
- spin_unlock_irqrestore(&ud->lock, flags);
-
- list_for_each_entry_safe(vd, _vd, &head, node) {
- struct udma_chan *uc = to_udma_chan(vd->tx.chan);
- struct udma_desc *d = to_udma_desc(&vd->tx);
-
- udma_free_hwdesc(uc, d);
- list_del(&vd->node);
- kfree(d);
- }
-
- /* If more to purge, schedule the work again */
- if (!list_empty(&ud->desc_to_purge))
- schedule_work(&ud->purge_work);
-}
-EXPORT_SYMBOL_GPL(udma_purge_desc_work);
-
void udma_desc_free(struct virt_dma_desc *vd)
{
struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
@@ -131,17 +105,9 @@ void udma_desc_free(struct virt_dma_desc *vd)
if (uc->terminated_desc == d)
uc->terminated_desc = NULL;
- if (uc->use_dma_pool) {
- udma_free_hwdesc(uc, d);
- kfree(d);
- return;
- }
-
- spin_lock_irqsave(&ud->lock, flags);
- list_add_tail(&vd->node, &ud->desc_to_purge);
- spin_unlock_irqrestore(&ud->lock, flags);
-
- schedule_work(&ud->purge_work);
+ udma_free_hwdesc(uc, d);
+ kfree(d);
+ return;
}
EXPORT_SYMBOL_GPL(udma_desc_free);
diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
index 6761a079025ba..d33382cc0356a 100644
--- a/drivers/dma/ti/k3-udma-v2.c
+++ b/drivers/dma/ti/k3-udma-v2.c
@@ -1320,14 +1320,12 @@ static int udma_v2_probe(struct platform_device *pdev)
ud->psil_base = ud->match_data->psil_base;
INIT_LIST_HEAD(&ud->ddev.channels);
- INIT_LIST_HEAD(&ud->desc_to_purge);
ch_count = setup_resources(ud);
if (ch_count <= 0)
return ch_count;
spin_lock_init(&ud->lock);
- INIT_WORK(&ud->purge_work, udma_purge_desc_work);
ud->desc_align = 64;
if (ud->desc_align < dma_get_cache_alignment())
diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
index a8d01d955651a..34d458b4a0dbc 100644
--- a/drivers/dma/ti/k3-udma.c
+++ b/drivers/dma/ti/k3-udma.c
@@ -2704,14 +2704,12 @@ static int udma_probe(struct platform_device *pdev)
ud->psil_base = ud->match_data->psil_base;
INIT_LIST_HEAD(&ud->ddev.channels);
- INIT_LIST_HEAD(&ud->desc_to_purge);
ch_count = setup_resources(ud);
if (ch_count <= 0)
return ch_count;
spin_lock_init(&ud->lock);
- INIT_WORK(&ud->purge_work, udma_purge_desc_work);
ud->desc_align = 64;
if (ud->desc_align < dma_get_cache_alignment())
diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
index 3ae2400e67990..67de9feb9906b 100644
--- a/drivers/dma/ti/k3-udma.h
+++ b/drivers/dma/ti/k3-udma.h
@@ -353,8 +353,6 @@ struct udma_dev {
struct k3_ringacc *ringacc;
- struct work_struct purge_work;
- struct list_head desc_to_purge;
spinlock_t lock;
struct udma_rx_flush rx_flush;
@@ -596,7 +594,6 @@ static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
dma_addr_t paddr);
void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d);
-void udma_purge_desc_work(struct work_struct *work);
void udma_desc_free(struct virt_dma_desc *vd);
bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr);
bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d);
--
2.34.1
^ permalink raw reply related [flat|nested] 45+ messages in thread* Re: [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
2026-01-30 11:01 ` [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing Sai Sree Kartheek Adivi
@ 2026-01-30 20:37 ` kernel test robot
2026-02-05 9:07 ` Krzysztof Kozlowski
2026-02-11 7:42 ` Sai Sree Kartheek Adivi
1 sibling, 1 reply; 45+ messages in thread
From: kernel test robot @ 2026-01-30 20:37 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul, robh, krzk+dt,
conor+dt, nm, ssantosh, dmaengine, devicetree, linux-kernel,
linux-arm-kernel, vigneshr
Cc: oe-kbuild-all, r-sharma3, gehariprasath
Hi Sai,
kernel test robot noticed the following build warnings:
[auto build test WARNING on vkoul-dmaengine/next]
[also build test WARNING on next-20260130]
[cannot apply to linus/master v6.19-rc7]
[If your patch is applied to the wrong git tree, kindly drop us a note.
And when submitting patch, we suggest to use '--base' as documented in
https://git-scm.com/docs/git-format-patch#_base_tree_information]
url: https://github.com/intel-lab-lkp/linux/commits/Sai-Sree-Kartheek-Adivi/dmaengine-ti-k3-udma-move-macros-to-header-file/20260130-191306
base: https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
patch link: https://lore.kernel.org/r/20260130110159.359501-20-s-adivi%40ti.com
patch subject: [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
config: arm64-defconfig (https://download.01.org/0day-ci/archive/20260131/202601310444.S9H39g4c-lkp@intel.com/config)
compiler: aarch64-linux-gcc (GCC) 15.2.0
reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260131/202601310444.S9H39g4c-lkp@intel.com/reproduce)
If you fix the issue in a separate patch/commit (i.e. not just a new version of
the same patch/commit), kindly add following tags
| Reported-by: kernel test robot <lkp@intel.com>
| Closes: https://lore.kernel.org/oe-kbuild-all/202601310444.S9H39g4c-lkp@intel.com/
All warnings (new ones prefixed by >>):
drivers/dma/ti/k3-udma-common.c: In function 'udma_desc_free':
>> drivers/dma/ti/k3-udma-common.c:103:23: warning: unused variable 'flags' [-Wunused-variable]
103 | unsigned long flags;
| ^~~~~
>> drivers/dma/ti/k3-udma-common.c:100:26: warning: unused variable 'ud' [-Wunused-variable]
100 | struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
| ^~
vim +/flags +103 drivers/dma/ti/k3-udma-common.c
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 97
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 98 void udma_desc_free(struct virt_dma_desc *vd)
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 99 {
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 @100 struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 101 struct udma_chan *uc = to_udma_chan(vd->tx.chan);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 102 struct udma_desc *d = to_udma_desc(&vd->tx);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 @103 unsigned long flags;
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 104
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 105 if (uc->terminated_desc == d)
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 106 uc->terminated_desc = NULL;
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 107
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 108 udma_free_hwdesc(uc, d);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 109 kfree(d);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 110 return;
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 111 }
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 112 EXPORT_SYMBOL_GPL(udma_desc_free);
f30a784b467d1f Sai Sree Kartheek Adivi 2026-01-30 113
--
0-DAY CI Kernel Test Service
https://github.com/intel/lkp-tests/wiki
^ permalink raw reply [flat|nested] 45+ messages in thread* Re: [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
2026-01-30 20:37 ` kernel test robot
@ 2026-02-05 9:07 ` Krzysztof Kozlowski
0 siblings, 0 replies; 45+ messages in thread
From: Krzysztof Kozlowski @ 2026-02-05 9:07 UTC (permalink / raw)
To: kernel test robot, Sai Sree Kartheek Adivi, peter.ujfalusi, vkoul,
robh, krzk+dt, conor+dt, nm, ssantosh, dmaengine, devicetree,
linux-kernel, linux-arm-kernel, vigneshr
Cc: oe-kbuild-all, r-sharma3, gehariprasath
On 30/01/2026 21:37, kernel test robot wrote:
> Hi Sai,
>
> kernel test robot noticed the following build warnings:
>
> [auto build test WARNING on vkoul-dmaengine/next]
> [also build test WARNING on next-20260130]
> [cannot apply to linus/master v6.19-rc7]
> [If your patch is applied to the wrong git tree, kindly drop us a note.
> And when submitting patch, we suggest to use '--base' as documented in
> https://git-scm.com/docs/git-format-patch#_base_tree_information]
>
> url: https://github.com/intel-lab-lkp/linux/commits/Sai-Sree-Kartheek-Adivi/dmaengine-ti-k3-udma-move-macros-to-header-file/20260130-191306
> base: https://git.kernel.org/pub/scm/linux/kernel/git/vkoul/dmaengine.git next
> patch link: https://lore.kernel.org/r/20260130110159.359501-20-s-adivi%40ti.com
> patch subject: [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
> config: arm64-defconfig (https://download.01.org/0day-ci/archive/20260131/202601310444.S9H39g4c-lkp@intel.com/config)
> compiler: aarch64-linux-gcc (GCC) 15.2.0
> reproduce (this is a W=1 build): (https://download.01.org/0day-ci/archive/20260131/202601310444.S9H39g4c-lkp@intel.com/reproduce)
>
> If you fix the issue in a separate patch/commit (i.e. not just a new version of
> the same patch/commit), kindly add following tags
> | Reported-by: kernel test robot <lkp@intel.com>
> | Closes: https://lore.kernel.org/oe-kbuild-all/202601310444.S9H39g4c-lkp@intel.com/
>
> All warnings (new ones prefixed by >>):
>
> drivers/dma/ti/k3-udma-common.c: In function 'udma_desc_free':
>>> drivers/dma/ti/k3-udma-common.c:103:23: warning: unused variable 'flags' [-Wunused-variable]
> 103 | unsigned long flags;
> | ^~~~~
>>> drivers/dma/ti/k3-udma-common.c:100:26: warning: unused variable 'ud' [-Wunused-variable]
> 100 | struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
> | ^~
>
Did you compile test your own code before sending?
Best regards,
Krzysztof
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
2026-01-30 11:01 ` [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing Sai Sree Kartheek Adivi
2026-01-30 20:37 ` kernel test robot
@ 2026-02-11 7:42 ` Sai Sree Kartheek Adivi
1 sibling, 0 replies; 45+ messages in thread
From: Sai Sree Kartheek Adivi @ 2026-02-11 7:42 UTC (permalink / raw)
To: peter.ujfalusi, vkoul, robh, krzk+dt, conor+dt, nm, ssantosh,
dmaengine, devicetree, linux-kernel, linux-arm-kernel, vigneshr
Cc: r-sharma3, gehariprasath
On 30/01/26 16:31, Sai Sree Kartheek Adivi wrote:
> The driver currently uses a worker thread to free processed
> descriptors. Under high load (e.g., CRYPTO_MANAGER_EXTRA_TESTS), the
> descriptor allocation rate can significantly outpace the worker
> thread's execution rate.
>
> This leads to false resource exhaustion where dma_alloc_coherent()
> fails even though many descriptors are waiting in the purge queue to
> be freed.
>
> Remove the lazy freeing mechanism (desc_to_purge list and worker) and
> instead free the descriptors immediately in the completion callback.
>
> This eliminates the latency gap between hardware completion and
> software reclaim, preventing the pool exhaustion during stress tests.
Please discard this patch.
Further testing revealed that dma_free_coherent triggers a
WARN_ON(irqs_disabled()) in this path due to atomic context.
I'll send a v5 with valid fix. Apologies for the noise.
>
> Signed-off-by: Sai Sree Kartheek Adivi <s-adivi@ti.com>
> ---
> drivers/dma/ti/k3-udma-common.c | 40 +++------------------------------
> drivers/dma/ti/k3-udma-v2.c | 2 --
> drivers/dma/ti/k3-udma.c | 2 --
> drivers/dma/ti/k3-udma.h | 3 ---
> 4 files changed, 3 insertions(+), 44 deletions(-)
>
> diff --git a/drivers/dma/ti/k3-udma-common.c b/drivers/dma/ti/k3-udma-common.c
> index 05b2b6b962a06..f9da00298b60f 100644
> --- a/drivers/dma/ti/k3-udma-common.c
> +++ b/drivers/dma/ti/k3-udma-common.c
> @@ -95,32 +95,6 @@ void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d)
> }
> }
>
> -void udma_purge_desc_work(struct work_struct *work)
> -{
> - struct udma_dev *ud = container_of(work, typeof(*ud), purge_work);
> - struct virt_dma_desc *vd, *_vd;
> - unsigned long flags;
> - LIST_HEAD(head);
> -
> - spin_lock_irqsave(&ud->lock, flags);
> - list_splice_tail_init(&ud->desc_to_purge, &head);
> - spin_unlock_irqrestore(&ud->lock, flags);
> -
> - list_for_each_entry_safe(vd, _vd, &head, node) {
> - struct udma_chan *uc = to_udma_chan(vd->tx.chan);
> - struct udma_desc *d = to_udma_desc(&vd->tx);
> -
> - udma_free_hwdesc(uc, d);
> - list_del(&vd->node);
> - kfree(d);
> - }
> -
> - /* If more to purge, schedule the work again */
> - if (!list_empty(&ud->desc_to_purge))
> - schedule_work(&ud->purge_work);
> -}
> -EXPORT_SYMBOL_GPL(udma_purge_desc_work);
> -
> void udma_desc_free(struct virt_dma_desc *vd)
> {
> struct udma_dev *ud = to_udma_dev(vd->tx.chan->device);
> @@ -131,17 +105,9 @@ void udma_desc_free(struct virt_dma_desc *vd)
> if (uc->terminated_desc == d)
> uc->terminated_desc = NULL;
>
> - if (uc->use_dma_pool) {
> - udma_free_hwdesc(uc, d);
> - kfree(d);
> - return;
> - }
> -
> - spin_lock_irqsave(&ud->lock, flags);
> - list_add_tail(&vd->node, &ud->desc_to_purge);
> - spin_unlock_irqrestore(&ud->lock, flags);
> -
> - schedule_work(&ud->purge_work);
> + udma_free_hwdesc(uc, d);
> + kfree(d);
> + return;
> }
> EXPORT_SYMBOL_GPL(udma_desc_free);
>
> diff --git a/drivers/dma/ti/k3-udma-v2.c b/drivers/dma/ti/k3-udma-v2.c
> index 6761a079025ba..d33382cc0356a 100644
> --- a/drivers/dma/ti/k3-udma-v2.c
> +++ b/drivers/dma/ti/k3-udma-v2.c
> @@ -1320,14 +1320,12 @@ static int udma_v2_probe(struct platform_device *pdev)
> ud->psil_base = ud->match_data->psil_base;
>
> INIT_LIST_HEAD(&ud->ddev.channels);
> - INIT_LIST_HEAD(&ud->desc_to_purge);
>
> ch_count = setup_resources(ud);
> if (ch_count <= 0)
> return ch_count;
>
> spin_lock_init(&ud->lock);
> - INIT_WORK(&ud->purge_work, udma_purge_desc_work);
>
> ud->desc_align = 64;
> if (ud->desc_align < dma_get_cache_alignment())
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> index a8d01d955651a..34d458b4a0dbc 100644
> --- a/drivers/dma/ti/k3-udma.c
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -2704,14 +2704,12 @@ static int udma_probe(struct platform_device *pdev)
> ud->psil_base = ud->match_data->psil_base;
>
> INIT_LIST_HEAD(&ud->ddev.channels);
> - INIT_LIST_HEAD(&ud->desc_to_purge);
>
> ch_count = setup_resources(ud);
> if (ch_count <= 0)
> return ch_count;
>
> spin_lock_init(&ud->lock);
> - INIT_WORK(&ud->purge_work, udma_purge_desc_work);
>
> ud->desc_align = 64;
> if (ud->desc_align < dma_get_cache_alignment())
> diff --git a/drivers/dma/ti/k3-udma.h b/drivers/dma/ti/k3-udma.h
> index 3ae2400e67990..67de9feb9906b 100644
> --- a/drivers/dma/ti/k3-udma.h
> +++ b/drivers/dma/ti/k3-udma.h
> @@ -353,8 +353,6 @@ struct udma_dev {
>
> struct k3_ringacc *ringacc;
>
> - struct work_struct purge_work;
> - struct list_head desc_to_purge;
> spinlock_t lock;
>
> struct udma_rx_flush rx_flush;
> @@ -596,7 +594,6 @@ static inline void udma_fetch_epib(struct udma_chan *uc, struct udma_desc *d)
> struct udma_desc *udma_udma_desc_from_paddr(struct udma_chan *uc,
> dma_addr_t paddr);
> void udma_free_hwdesc(struct udma_chan *uc, struct udma_desc *d);
> -void udma_purge_desc_work(struct work_struct *work);
> void udma_desc_free(struct virt_dma_desc *vd);
> bool udma_desc_is_rx_flush(struct udma_chan *uc, dma_addr_t addr);
> bool udma_is_desc_really_done(struct udma_chan *uc, struct udma_desc *d);
^ permalink raw reply [flat|nested] 45+ messages in thread
* Re: [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2
2026-01-30 11:01 [PATCH v4 00/19] dmaengine: ti: Add support for BCDMA v2 and PKTDMA v2 Sai Sree Kartheek Adivi
` (18 preceding siblings ...)
2026-01-30 11:01 ` [PATCH v4 19/19] dmaengine: ti: k3-udma: switch to synchronous descriptor freeing Sai Sree Kartheek Adivi
@ 2026-02-03 6:42 ` Péter Ujfalusi
19 siblings, 0 replies; 45+ messages in thread
From: Péter Ujfalusi @ 2026-02-03 6:42 UTC (permalink / raw)
To: Sai Sree Kartheek Adivi, vkoul, robh, krzk+dt, conor+dt, nm,
ssantosh, dmaengine, devicetree, linux-kernel, linux-arm-kernel,
vigneshr
Cc: r-sharma3, gehariprasath
Hi
On 30/01/2026 13:01, Sai Sree Kartheek Adivi wrote:
> This series adds support for the BCDMA_V2 and PKTDMA_V2 which is
> introduced in AM62L.
>
> The key differences between the existing DMA and DMA V2 are:
> - Absence of TISCI: Instead of configuring via TISCI calls, direct
> register writes are required.
> - Autopair: There is no longer a need for PSIL pair and instead AUTOPAIR
> bit needs to set in the RT_CTL register.
> - Static channel mapping: Each channel is mapped to a single peripheral.
> - Direct IRQs: There is no INT-A and interrupt lines from DMA are
> directly connected to GIC.
> - Remote side configuration handled by DMA. So no need to write to PEER
> registers to START / STOP / PAUSE / TEARDOWN.
I think this looks really good overall, I have few nitpicks around
variable declaration order and one bigger ask for the possibility of
introducing version member to struct udma_match_data to cut down on
changes and have more targeted handling of the differences between the
two variants.
>
> Changes from v3 to v4:
> - Rename the dt-binding files to add "ti," prefix.
> - Update cell description in dt-bindings and add client examples.
> - Update k3_ring_intr_regs reg names
> - Rename soc specific data to bcdma_v2_data and pktdma_v2_data to
> bcdma_v2_am62l_data and pktdma_v2_am62l_data.
> - Add a new patch [18/19] to fix a null pointer dereference issue when
> trying to reserve a channel id that is out of bounds in
> udma_reserve_##res macro. Also fix logging issues in this macro.
> - Add a new patch [19/19] to switch to synchronous descriptor freeing to
> avoid running out of memory during stress tests.
> - Fix checkpatch warnings.
> link to v3:
> https://lore.kernel.org/linux-arm-kernel/20250623053716.1493974-1-s-adivi@ti.com
>
> Changes from v2 to v3:
> - Fix checkpatch errors & spellings.
> link to v2:
> https://lore.kernel.org/linux-arm-kernel/20250612071521.3116831-1-s-adivi@ti.com
>
> Changes from v1 to v2:
> - Split refactoring of k3-udma driver into multiple commits
> - Fix bcdma v2 and pktdma v2 dt-binding examples
> - Fix compatibles in k3-udma-v2.c
> - move udma_is_desc_really_done to k3-udma-common.c as the difference
> between k3-udma and k3-udma-v2 implementation is minor.
> - remove udma_ prefix to function pointers in udma_dev
> - reorder the commits to first refactor the existing code completely and
> then introduce k3-udma-v2 related commits.
> - remove redundant includes in k3-udma-common.c
> - remove ti_sci_ dependency for k3_ringacc in Kconfig
> - refactor setup_resources functions to remove ti_sci_ code from common
> logic.
> link to v1:
> https://lore.kernel.org/linux-arm-kernel/20250428072032.946008-1-s-adivi@ti.com
>
> Sai Sree Kartheek Adivi (19):
> dmaengine: ti: k3-udma: move macros to header file
> dmaengine: ti: k3-udma: move structs and enums to header file
> dmaengine: ti: k3-udma: move static inline helper functions to header
> file
> dmaengine: ti: k3-udma: move descriptor management to k3-udma-common.c
> dmaengine: ti: k3-udma: move ring management functions to
> k3-udma-common.c
> dmaengine: ti: k3-udma: Add variant-specific function pointers to
> udma_dev
> dmaengine: ti: k3-udma: move udma utility functions to
> k3-udma-common.c
> dmaengine: ti: k3-udma: move resource management functions to
> k3-udma-common.c
> dmaengine: ti: k3-udma: refactor resource setup functions
> dmaengine: ti: k3-udma: move inclusion of k3-udma-private.c to
> k3-udma-common.c
> drivers: soc: ti: k3-ringacc: handle absence of tisci
> dt-bindings: dma: ti: Add K3 BCDMA V2
> dt-bindings: dma: ti: Add K3 PKTDMA V2
> dmaengine: ti: k3-psil-am62l: Add AM62Lx PSIL and PDMA data
> dmaengine: ti: k3-udma-v2: New driver for K3 BCDMA_V2
> dmaengine: ti: k3-udma-v2: Add support for PKTDMA V2
> dmaengine: ti: k3-udma-v2: Update glue layer to support PKTDMA V2
> dmaengine: ti: k3-udma: Validate resource ID and fix logging in
> reservation
> dmaengine: ti: k3-udma: switch to synchronous descriptor freeing
>
> .../bindings/dma/ti/ti,k3-bcdma-v2.yaml | 116 +
> .../bindings/dma/ti/ti,k3-pktdma-v2.yaml | 90 +
> drivers/dma/ti/Kconfig | 21 +-
> drivers/dma/ti/Makefile | 5 +-
> drivers/dma/ti/k3-psil-am62l.c | 132 +
> drivers/dma/ti/k3-psil-priv.h | 1 +
> drivers/dma/ti/k3-psil.c | 1 +
> drivers/dma/ti/k3-udma-common.c | 2577 ++++++++++++++
> drivers/dma/ti/k3-udma-glue.c | 91 +-
> drivers/dma/ti/k3-udma-private.c | 48 +-
> drivers/dma/ti/k3-udma-v2.c | 1472 ++++++++
> drivers/dma/ti/k3-udma.c | 3095 +----------------
> drivers/dma/ti/k3-udma.h | 583 ++++
> drivers/soc/ti/Kconfig | 1 -
> drivers/soc/ti/k3-ringacc.c | 188 +-
> include/linux/soc/ti/k3-ringacc.h | 20 +
> 16 files changed, 5402 insertions(+), 3039 deletions(-)
> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-bcdma-v2.yaml
> create mode 100644 Documentation/devicetree/bindings/dma/ti/ti,k3-pktdma-v2.yaml
> create mode 100644 drivers/dma/ti/k3-psil-am62l.c
> create mode 100644 drivers/dma/ti/k3-udma-common.c
> create mode 100644 drivers/dma/ti/k3-udma-v2.c
>
--
Péter
^ permalink raw reply [flat|nested] 45+ messages in thread