RE: [PATCH 03/11] net: wwan: t9xx: Add control DMA interface

From: Jagielski, Jedrzej

Date: Mon Jun 01 2026 - 07:55:33 EST


From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@xxxxxxxxxx>
Sent: Friday, May 29, 2026 12:32 PM

>From: Jack Wu <jackbb_wu@xxxxxxxxxx>
>
>Cross Layer Direct Memory Access(CLDMA) is the hardware
>interface used by the control plane and designated to
>translate data between the host and the device. It supports
>8 hardware queues for the device AP and modem respectively.
>
>CLDMA driver uses General Purpose Descriptor (GPD) to
>describe transaction information that can be recognized by
>CLDMA hardware. Once CLDMA hardware transaction is started,
>it would fetch and parse GPD to transfer data correctly.
>To facilitate the CLDMA transaction, a GPD ring for each
>queue is used. Once the transaction is started, CLDMA
>hardware will traverse the GPD ring to transfer data between
>the host and the device until no GPD is available.
>
>CLDMA TX flow:
>Once a TX service receives the TX data from the port layer,
>it uses APIs exported by the CLDMA driver to configure GPD
>with the DMA address of TX data. After that, the service
>triggers CLDMA to fetch the first available GPD to transfer
>data.
>
>CLDMA RX flow:
>When there is RX data from the MD, CLDMA hardware asserts an
>interrupt to notify the host to fetch data and dispatch it
>to FSM (for handshake messages) or the port layer.
>After CLDMA opening is finished, All RX GPDs are fulfilled
>and ready to receive data from the device.
>
>Signed-off-by: Jack Wu <jackbb_wu@xxxxxxxxxx>
>---
> drivers/net/wwan/t9xx/mtk_ctrl_plane.c | 3 +-
> drivers/net/wwan/t9xx/mtk_ctrl_plane.h | 52 +-
> drivers/net/wwan/t9xx/pcie/Makefile | 7 +-
> drivers/net/wwan/t9xx/pcie/mtk_cldma.c | 1220 +++++++++++++++++++++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma.h | 170 ++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.c | 373 +++++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h | 177 ++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c | 182 ++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h | 103 ++
> drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c | 23 +
> drivers/net/wwan/t9xx/pcie/mtk_pci.c | 38 +
> drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h | 1 +
> drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c | 569 +++++++++++
> drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h | 84 ++
> 14 files changed, 2998 insertions(+), 4 deletions(-)
>
>diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
>index ae5e1797b817..ca32827c1a20 100644
>--- a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
>+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
>@@ -8,7 +8,7 @@
>
> #include "mtk_ctrl_plane.h"
>
>-int mtk_ctrl_init(struct mtk_md_dev *mdev)
>+int mtk_ctrl_init(struct mtk_md_dev *mdev, struct mtk_ctrl_hif_ops *ops)
> {
> struct mtk_ctrl_blk *ctrl_blk;
>
>@@ -18,6 +18,7 @@ int mtk_ctrl_init(struct mtk_md_dev *mdev)
>
> ctrl_blk->mdev = mdev;
> mdev->ctrl_blk = ctrl_blk;
>+ ctrl_blk->ops = ops;
>
> return 0;
> }
>diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
>index 8276be19b456..6d4be89680d6 100644
>--- a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
>+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
>@@ -11,12 +11,60 @@
>
> #include "mtk_dev.h"
>
>+enum mtk_trb_cmd_type {
>+ TRB_CMD_MIN,
>+ TRB_CMD_ENABLE,
>+ TRB_CMD_TX,
>+ TRB_CMD_DISABLE,
>+ TRB_CMD_STOP,
>+ TRB_CMD_RECOVER,
>+ TRB_CMD_MAX,
>+};
>+
>+enum mtk_hif_dev_ctrl_cmd {
>+ HIF_CTRL_CMD_CHECK_TX_FULL,
>+};
>+
>+struct trb_open_priv {
>+ u8 log_rg_offset;
>+ u32 tx_mtu;
>+ u32 rx_mtu;
>+ u32 tx_frag_size;
>+ u32 rx_frag_size;
>+ int (*rx_done)(struct sk_buff *skb, void *priv, bool force_recv);
>+};
>+
>+struct trb {
>+ u32 channel_id;
>+ enum mtk_trb_cmd_type cmd;
>+ int status;
>+ struct kref kref;
>+ void *priv;
>+ int (*trb_complete)(struct sk_buff *skb);
>+};
>+
>+union ctrl_hif_cmd_data {
>+ u32 rx_ch;
>+};
>+
>+struct mtk_ctrl_hif_ops {
>+ int (*init)(struct mtk_md_dev *mdev);
>+ int (*exit)(struct mtk_md_dev *mdev);
>+ int (*submit_skb)(struct mtk_md_dev *mdev, struct sk_buff *skb, bool force_send);
>+ int (*send_cmd)(struct mtk_md_dev *mdev, int cmd, void *data);
>+};
>+
>+struct mtk_ctrl_cfg;
>+struct mtk_ctrl_trans;
>+
> struct mtk_ctrl_blk {
> struct mtk_md_dev *mdev;
>- struct mtk_ctrl_trans *trans;
>+ struct mtk_ctrl_hif_ops *ops;
>+ void *ctrl_hw_priv;
>+ struct mtk_ctrl_cfg *cfg;
> };
>
>-int mtk_ctrl_init(struct mtk_md_dev *mdev);
>+int mtk_ctrl_init(struct mtk_md_dev *mdev, struct mtk_ctrl_hif_ops *ops);
> int mtk_ctrl_exit(struct mtk_md_dev *mdev);
>
> #endif /* __MTK_CTRL_PLANE_H__ */
>diff --git a/drivers/net/wwan/t9xx/pcie/Makefile b/drivers/net/wwan/t9xx/pcie/Makefile
>index 7410d1796d27..5252f158b058 100644
>--- a/drivers/net/wwan/t9xx/pcie/Makefile
>+++ b/drivers/net/wwan/t9xx/pcie/Makefile
>@@ -7,4 +7,9 @@ obj-$(CONFIG_MTK_T9XX_PCI) += mtk_t9xx_pcie.o
>
> mtk_t9xx_pcie-y := \
> mtk_pci_drv_m9xx.o \
>- mtk_pci.o
>+ mtk_cldma_drv_m9xx.o \
>+ mtk_ctrl_cfg_m9xx.o \
>+ mtk_pci.o \
>+ mtk_trans_ctrl.o \
>+ mtk_cldma.o \
>+ mtk_cldma_drv.o
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
>new file mode 100644
>index 000000000000..48067a010890
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
>@@ -0,0 +1,1220 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include <linux/delay.h>
>+#include <linux/device.h>
>+#include <linux/dma-mapping.h>
>+#include <linux/dmapool.h>
>+#include <linux/err.h>
>+#include <linux/interrupt.h>
>+#include <linux/kdev_t.h>
>+#include <linux/kernel.h>
>+#include <linux/kthread.h>
>+#include <linux/list.h>
>+#include <linux/module.h>
>+#include <linux/mutex.h>
>+#include <linux/netdevice.h>
>+#include <linux/sched.h>
>+#include <linux/skbuff.h>
>+#include <linux/slab.h>
>+#include <linux/timer.h>
>+#include <linux/wait.h>
>+#include <linux/workqueue.h>
>+#include "mtk_pci.h"
>+#include "mtk_cldma.h"
>+#include "mtk_cldma_drv.h"
>+#include "mtk_dev.h"
>+
>+#define cldma_drv_ops_null NULL
>+#define DMA_POOL_NAME_LEN (64)
>+#define WAIT_HWO_ROUND (10)
>+#define WAIT_HWO_TIME (5)
>+#define CLDMA_RETRY_DELAY_MS (100)
>+#define NO_BUDGET (0)
>+
>+static const int mtk_cldma_hw_id_tbl[NR_CLDMA] = {
>+ [CLDMA0] = CLDMA0_HW_ID,
>+ [CLDMA1] = CLDMA1_HW_ID,
>+ [CLDMA4] = CLDMA4_HW_ID,
>+};
>+
>+static inline void mtk_cldma_clr_bd_dsc(struct cldma_drv_info *drv_info,
>+ struct bd_dsc *bd_dsc_pool, int nr_bds)
>+{
>+ struct bd_dsc *bd_dsc;
>+ int i;
>+
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = bd_dsc_pool + i;
>+ dma_unmap_single(drv_info->mdev->dev, bd_dsc->data_dma_addr,
>+ bd_dsc->data_len, DMA_TO_DEVICE);
>+ bd_dsc->data_dma_addr = 0;
>+ bd_dsc->data_len = 0;
>+ if (bd_dsc->bd->tx_bd.bd_flags & CLDMA_BD_FLAG_EOL) {
>+ bd_dsc->bd->tx_bd.bd_flags &= ~CLDMA_BD_FLAG_EOL;
>+ break;
>+ }
>+ }
>+}
>+
>+static void mtk_cldma_tx_done_work(struct work_struct *work)
>+{
>+ struct txq *txq = container_of(work, struct txq, tx_done_work);
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_ctrl_trans *trans;
>+ struct mtk_md_dev *mdev;
>+ struct tx_req *req;
>+ unsigned int state;
>+ int i, hif_id;
>+ struct trb *trb;
>+ u32 txqno;

please stick to RCT

>+
>+ drv_info = txq->drv_info;
>+ hif_id = drv_info->hif_id;
>+ txqno = txq->txqno;
>+ mdev = drv_info->mdev;
>+ drv_ops = drv_info->drv_ops;
>+ trans = drv_info->cd->trans;
>+
>+again:
>+ for (i = 0; i < txq->nr_gpds; i++) {
>+ req = txq->req_pool + txq->free_idx;
>+
>+ rmb(); /* ensure HWO setup done before HWO read */
>+
>+ if (!req->data_vm_addr || (req->gpd->tx_gpd.gpd_flags & CLDMA_GPD_FLAG_HWO))
>+ break;
>+
>+ if (txq->nr_bds)
>+ mtk_cldma_clr_bd_dsc(drv_info, req->bd_dsc_pool, txq->nr_bds);
>+ else
>+ dma_unmap_single(mdev->dev, req->data_dma_addr,
>+ req->data_len, DMA_TO_DEVICE);
>+
>+ trb = (struct trb *)req->skb->cb;
>+ trb->status = 0;
>+ trb->trb_complete(req->skb);
>+
>+ req->data_vm_addr = NULL;
>+ req->data_dma_addr = 0;
>+ req->data_len = 0;
>+ req->skb = NULL;
>+
>+ txq->free_idx = (txq->free_idx + 1) % txq->nr_gpds;
>+ if (atomic_fetch_inc(&txq->req_budget) == NO_BUDGET)
>+ wake_up(&trans->trb_srv[trans->srv_cfg[hif_id][txqno]]->trb_waitq);
>+ }
>+
>+ state = drv_ops->cldma_check_intr_status(drv_info, DIR_TX, txqno, QUEUE_XFER_DONE);
>+ if (state) {
>+ if (unlikely(state == LINK_ERROR_VAL))
>+ goto out;
>+
>+ drv_ops->cldma_clr_intr_status(drv_info, DIR_TX, txqno, QUEUE_XFER_DONE);
>+
>+ cond_resched();
>+
>+ goto again;

are we sure we won't be locked here?

>+ }
>+
>+out:
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_TX, txqno, QUEUE_XFER_DONE);
>+}
>+
>+static void mtk_cldma_rx_skb_adjust(struct mtk_md_dev *mdev, struct rxq *rxq,
>+ struct rx_req *req)
>+{
>+ struct bd_dsc *bd_dsc;
>+ int i;
>+
>+ for (i = 0; i < rxq->nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ if (bd_dsc->data_dma_addr) {
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ bd_dsc->data_dma_addr = 0;
>+ }
>+ bd_dsc->skb->len = 0;
>+ skb_reset_tail_pointer(bd_dsc->skb);
>+ skb_put(bd_dsc->skb,
>+ le16_to_cpu(bd_dsc->bd->rx_bd.data_recv_len));
>+ if (req->skb != bd_dsc->skb) {
>+ req->skb->len += bd_dsc->skb->len;
>+ req->skb->data_len += bd_dsc->skb->len;
>+ }
>+ bd_dsc->bd->rx_bd.data_recv_len = 0;
>+ bd_dsc->skb = NULL;
>+ }
>+ if (!rxq->nr_bds) {
>+ if (req->data_dma_addr) {
>+ dma_unmap_single(mdev->dev, req->data_dma_addr,
>+ req->mtu, DMA_FROM_DEVICE);
>+ req->data_dma_addr = 0;
>+ }
>+ req->skb->len = 0;
>+ skb_reset_tail_pointer(req->skb);
>+ skb_put(req->skb, le16_to_cpu(req->gpd->rx_gpd.data_recv_len));
>+ }
>+
>+ req->gpd->rx_gpd.data_recv_len = 0;
>+}
>+
>+static int mtk_cldma_reload_rx_skb(struct mtk_md_dev *mdev, struct rxq *rxq,
>+ struct rx_req *req)
>+{
>+ struct sk_buff *tail = NULL;
>+ struct bd_dsc *bd_dsc;
>+ int nr_bds;
>+ int i, err;
>+
>+ nr_bds = rxq->nr_bds;
>+
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ bd_dsc->skb = __dev_alloc_skb(req->frag_size, GFP_KERNEL);
>+ if (!bd_dsc->skb) {
>+ dev_warn((mdev)->dev, "Failed to alloc SKB\n");
>+ err = -ENOMEM;
>+ goto err_free_skb;
>+ }
>+ bd_dsc->skb->next = NULL;
>+ bd_dsc->data_dma_addr = dma_map_single(mdev->dev, bd_dsc->skb->data,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ err = dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr);
>+ if (unlikely(err)) {
>+ dev_warn((mdev)->dev, "Failed to map SKB data\n");
>+ err = -EFAULT;
>+ goto err_free_skb;
>+ }
>+ bd_dsc->bd->rx_bd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->data_dma_addr) >> 32);
>+ bd_dsc->bd->rx_bd.data_buff_ptr_l =
>+ cpu_to_le32(bd_dsc->data_dma_addr);
>+ if (tail) {
>+ tail->next = bd_dsc->skb;
>+ tail = bd_dsc->skb;
>+ continue;
>+ }
>+ if (!req->skb) {
>+ req->skb = bd_dsc->skb;
>+ } else {
>+ skb_shinfo(req->skb)->frag_list = bd_dsc->skb;
>+ tail = bd_dsc->skb;
>+ }
>+ }
>+ if (!nr_bds) {
>+ req->skb = __dev_alloc_skb(req->mtu, GFP_KERNEL);
>+ if (!req->skb) {
>+ err = -ENOMEM;
>+ goto err_free_skb;
>+ }
>+
>+ req->data_dma_addr = dma_map_single(mdev->dev, req->skb->data,
>+ req->mtu, DMA_FROM_DEVICE);
>+ err = dma_mapping_error(mdev->dev, req->data_dma_addr);
>+ if (unlikely(err)) {
>+ dev_warn((mdev)->dev, "Failed to map SKB data\n");
>+ err = -EFAULT;
>+ goto err_free_skb;
>+ }
>+ req->gpd->rx_gpd.data_buff_ptr_h = cpu_to_le32((u64)req->data_dma_addr >> 32);
>+ req->gpd->rx_gpd.data_buff_ptr_l = cpu_to_le32(req->data_dma_addr);
>+ }
>+ return 0;
>+
>+err_free_skb:
>+ if (nr_bds) {
>+ if (req->skb)
>+ skb_shinfo(req->skb)->frag_list = NULL;
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ if (!bd_dsc->skb)
>+ break;
>+ if (!dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr))
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ bd_dsc->data_dma_addr = 0;
>+ bd_dsc->skb->next = NULL;
>+ dev_kfree_skb_any(bd_dsc->skb);
>+ }
>+ } else {
>+ req->data_dma_addr = 0;
>+ if (req->skb)
>+ dev_kfree_skb_any(req->skb);
>+ }
>+ req->skb = NULL;
>+
>+ return err;
>+}
>+
>+static int mtk_cldma_check_rx_req(struct cldma_drv_info *drv_info, struct rxq *rxq)
>+{
>+ struct rx_req *req = rxq->req_pool + rxq->free_idx;
>+ u64 curr_addr;
>+ int i;
>+
>+ curr_addr = drv_info->drv_ops->cldma_get_rx_curr_addr(drv_info, rxq->rxqno);
>+ if (unlikely(!curr_addr))
>+ return -ENXIO;
>+
>+ if (req->gpd_dma_addr == curr_addr)
>+ return -EAGAIN;
>+ for (i = 0; i < WAIT_HWO_ROUND; i++) {
>+ udelay(WAIT_HWO_TIME);
>+ if (!(req->gpd->rx_gpd.gpd_flags & CLDMA_GPD_FLAG_HWO))
>+ break;
>+ }
>+ if (i == WAIT_HWO_ROUND) {
>+ dev_err((drv_info->mdev)->dev, "Failed to check HWO=0\n");
>+ return -EAGAIN;
>+ }
>+
>+ return 0;
>+}
>+
>+static bool mtk_cldma_rx_check_again(struct rxq *rxq)
>+{
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ bool need_check_again = false;
>+ struct mtk_md_dev *mdev;
>+ int rxqno;
>+ u32 state;
>+
>+ drv_info = rxq->drv_info;
>+ drv_ops = drv_info->drv_ops;
>+ mdev = drv_info->mdev;
>+ rxqno = rxq->rxqno;
>+
>+ do {
>+ state = drv_ops->cldma_check_intr_status(drv_info, DIR_RX,
>+ rxqno, QUEUE_XFER_DONE);
>+ if (state) {
>+ if (unlikely(state == LINK_ERROR_VAL))
>+ break;
>+
>+ drv_ops->cldma_clr_intr_status(drv_info, DIR_RX,
>+ rxqno, QUEUE_XFER_DONE);
>+ cond_resched();
>+ return true;
>+ }
>+ } while (need_check_again);
>+
>+ return false;
>+}
>+
>+static void mtk_cldma_rx_done_work(struct work_struct *work)
>+{
>+ struct rxq *rxq = container_of(work, struct rxq, rx_done_work);
>+ struct rx_req *req = NULL, *pre_req = NULL;
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_md_dev *mdev;
>+ int i, err, idx;
>+
>+ drv_info = rxq->drv_info;
>+ mdev = drv_info->mdev;
>+ drv_ops = drv_info->drv_ops;
>+
>+again:
>+ for (i = 0; i < rxq->nr_gpds; i++) {
>+ req = rxq->req_pool + rxq->free_idx;
>+ if (!req->skb) {
>+ dev_err((mdev)->dev,
>+ "Failed to get valid req cldma%d rxq%d req%d\n",
>+ drv_info->hw_id, rxq->rxqno, rxq->free_idx);
>+ goto err_out;
>+ }
>+
>+ if (req->gpd->rx_gpd.gpd_flags & CLDMA_GPD_FLAG_HWO)
>+ break;
>+
>+ mtk_cldma_rx_skb_adjust(mdev, rxq, req);
>+ do {
>+ err = rxq->rx_done(req->skb, rxq->arg,
>+ atomic_read(&rxq->need_exit) ? true : false);
>+ if (err == -EAGAIN)
>+ usleep_range(1000, 2000);
>+ else
>+ req->skb = NULL;
>+ } while (err == -EAGAIN);
>+
>+ err = mtk_cldma_reload_rx_skb(mdev, rxq, req);
>+ if (err)
>+ goto err_out;
>+
>+ wmb(); /* ensure addr set done before HWO setup done */
>+
>+ idx = rxq->free_idx == 0 ? rxq->nr_gpds - 1 : rxq->free_idx - 1;
>+ pre_req = rxq->req_pool + idx;
>+ pre_req->gpd->rx_gpd.gpd_flags |= CLDMA_GPD_FLAG_HWO;
>+ rxq->free_idx = (rxq->free_idx + 1) % rxq->nr_gpds;
>+ }
>+
>+ err = mtk_cldma_check_rx_req(drv_info, rxq);
>+ if (!err)
>+ goto again;

unclear for me
repeat when 0 is returned
do not repeat when -EAGAIN is returned by mtk_cldma_check_rx_req?

>+ else if (err == -ENXIO)
>+ goto out;
>+
>+ if (!atomic_read(&rxq->need_exit))
>+ drv_ops->cldma_resume_queue(drv_info, DIR_RX, rxq->rxqno);
>+
>+ if (mtk_cldma_rx_check_again(rxq))
>+ goto again;
>+
>+out:
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_RX, rxq->rxqno, QUEUE_XFER_DONE);
>+ drv_ops->cldma_clear_ip_busy(drv_info);
>+err_out:
>+ ;
>+}
>+
>+static int mtk_cldma_alloc_tx_bd(struct cldma_drv_info *drv_info, struct txq *txq,
>+ struct tx_req *req)
>+{
>+ struct bd_dsc *bd_dsc, *last_bd_dsc = NULL;
>+ int i;
>+
>+ req->bd_dsc_pool = devm_kcalloc(drv_info->mdev->dev, txq->nr_bds,
>+ sizeof(*bd_dsc), GFP_KERNEL);
>+ if (!req->bd_dsc_pool)
>+ return -ENOMEM;
>+
>+ for (i = 0; i < txq->nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ bd_dsc->bd = dma_pool_zalloc(drv_info->bd_dma_pool, GFP_KERNEL,
>+ &bd_dsc->bd_dma_addr);
>+ if (!bd_dsc->bd)
>+ return -ENOMEM;
>+ if (!last_bd_dsc) {
>+ req->gpd->tx_gpd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->bd_dma_addr) >> 32);
>+ req->gpd->tx_gpd.data_buff_ptr_l =
>+ cpu_to_le32(bd_dsc->bd_dma_addr);
>+ } else {
>+ last_bd_dsc->bd->tx_bd.next_bd_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->bd_dma_addr) >> 32);
>+ last_bd_dsc->bd->tx_bd.next_bd_ptr_l =
>+ cpu_to_le32(bd_dsc->bd_dma_addr);
>+ }
>+ last_bd_dsc = bd_dsc;
>+ }
>+ return 0;
>+}
>+
>+static struct txq *mtk_cldma_txq_alloc(struct cldma_drv_info *drv_info, struct sk_buff *skb)
>+{
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct mtk_md_dev *mdev;
>+ struct bd_dsc *bd_dsc;
>+ struct tx_req *next;
>+ struct tx_req *req;
>+ u16 tx_frag_size;
>+ struct txq *txq;
>+ int i, j, err;
>+
>+ mdev = drv_info->mdev;
>+ ctrl_blk = mdev->ctrl_blk;
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ drv_ops = drv_info->drv_ops;
>+
>+ txq = devm_kzalloc(mdev->dev, sizeof(*txq), GFP_KERNEL);
>+ if (!txq)
>+ return NULL;
>+
>+ txq->que = radix_tree_lookup(&trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ txq->drv_info = drv_info;
>+ txq->txqno = txq->que->txqno;
>+ txq->nr_gpds = txq->que->tx_nr_gpds;
>+ atomic_set(&txq->req_budget, txq->que->tx_nr_gpds);
>+ txq->is_stopping = false;
>+ tx_frag_size = txq->que->tx_frag_size;
>+ if (txq->que->tx_mtu > tx_frag_size && tx_frag_size)
>+ txq->nr_bds = (txq->que->tx_mtu + tx_frag_size - 1) / tx_frag_size;
>+
>+ txq->req_pool = devm_kcalloc(mdev->dev, txq->nr_gpds, sizeof(*req), GFP_KERNEL);
>+ if (!txq->req_pool)
>+ goto err_free_txq;
>+
>+ for (i = 0; i < txq->nr_gpds; i++) {
>+ req = txq->req_pool + i;
>+ req->mtu = txq->que->tx_mtu;
>+ req->frag_size = tx_frag_size;
>+ req->gpd = dma_pool_zalloc(drv_info->gpd_dma_pool, GFP_KERNEL, &req->gpd_dma_addr);
>+ if (!req->gpd)
>+ goto err_free_req;
>+ if (txq->nr_bds) {
>+ err = mtk_cldma_alloc_tx_bd(drv_info, txq, req);
>+ if (err)
>+ goto err_free_req;
>+ req->gpd->tx_gpd.gpd_flags |= CLDMA_GPD_FLAG_BDP;
>+ }
>+ }
>+
>+ for (i = 0; i < txq->nr_gpds; i++) {
>+ req = txq->req_pool + i;
>+ next = txq->req_pool + ((i + 1) % txq->nr_gpds);
>+ req->gpd->tx_gpd.gpd_flags |= CLDMA_GPD_FLAG_IOC;
>+ req->gpd->tx_gpd.next_gpd_ptr_h = cpu_to_le32((u64)(next->gpd_dma_addr) >> 32);
>+ req->gpd->tx_gpd.next_gpd_ptr_l = cpu_to_le32(next->gpd_dma_addr);
>+ }
>+
>+ INIT_WORK(&txq->tx_done_work, mtk_cldma_tx_done_work);
>+
>+ drv_ops->cldma_stop_queue(drv_info, DIR_TX, txq->txqno);
>+ txq->tx_started = false;
>+ drv_ops->cldma_setup_start_addr(drv_info, DIR_TX, txq->txqno,
>+ txq->req_pool[0].gpd_dma_addr);
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_TX, txq->txqno, QUEUE_ERROR);
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_TX, txq->txqno, QUEUE_XFER_DONE);
>+
>+ drv_info->txq[txq->txqno] = txq;
>+ return txq;
>+
>+err_free_req:
>+ for (i = 0; i < txq->nr_gpds; i++) {
>+ req = txq->req_pool + i;
>+ if (!req->gpd)
>+ break;
>+ if (req->bd_dsc_pool) {
>+ for (j = 0; j < txq->nr_bds; j++) {
>+ bd_dsc = req->bd_dsc_pool + j;
>+ if (!bd_dsc->bd)
>+ break;
>+ dma_pool_free(drv_info->bd_dma_pool, bd_dsc->bd,
>+ bd_dsc->bd_dma_addr);
>+ }
>+ devm_kfree(mdev->dev, req->bd_dsc_pool);
>+ }
>+ dma_pool_free(drv_info->gpd_dma_pool, req->gpd, req->gpd_dma_addr);
>+ }
>+ devm_kfree(mdev->dev, txq->req_pool);
>+err_free_txq:
>+ devm_kfree(mdev->dev, txq);
>+ return NULL;
>+}
>+
>+static int mtk_cldma_txq_free(struct cldma_drv_info *drv_info, u32 txqno)
>+{
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct mtk_md_dev *mdev;
>+ struct bd_dsc *bd_dsc;
>+ struct tx_req *req;
>+ struct txq *txq;
>+ struct trb *trb;
>+ int irq_id;
>+ int i, j;
>+
>+ mdev = drv_info->mdev;
>+ ctrl_blk = mdev->ctrl_blk;
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ drv_ops = drv_info->drv_ops;
>+
>+ txq = drv_info->txq[txqno];
>+ drv_info->txq[txqno] = NULL;
>+ /* stop HW tx transaction */
>+ drv_ops->cldma_stop_queue(drv_info, DIR_TX, txqno);
>+ txq->tx_started = false;
>+
>+ irq_id = mtk_pci_get_virq_id(mdev, drv_info->pci_ext_irq_id);
>+ synchronize_irq(irq_id);
>+ /* flush on-going work */
>+ flush_work(&txq->tx_done_work);
>+ drv_ops->cldma_mask_intr(drv_info, DIR_TX, txqno, QUEUE_XFER_DONE);
>+ drv_ops->cldma_mask_intr(drv_info, DIR_TX, txqno, QUEUE_ERROR);
>+
>+ /* free tx req resource */
>+ for (i = 0; i < txq->nr_gpds; i++) {
>+ req = txq->req_pool + txq->free_idx;
>+ if (req->skb && req->data_len) {
>+ if (!txq->nr_bds)
>+ dma_unmap_single(mdev->dev, req->data_dma_addr,
>+ req->data_len, DMA_TO_DEVICE);
>+ for (j = 0; j < txq->nr_bds; j++) {
>+ bd_dsc = req->bd_dsc_pool + j;
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ bd_dsc->data_len, DMA_TO_DEVICE);
>+ }
>+ trb = (struct trb *)req->skb->cb;
>+ trb->status = -EPIPE;
>+ trb->trb_complete(req->skb);
>+ }
>+ for (j = 0; j < txq->nr_bds; j++) {
>+ bd_dsc = req->bd_dsc_pool + j;
>+ dma_pool_free(drv_info->bd_dma_pool, bd_dsc->bd,
>+ bd_dsc->bd_dma_addr);
>+ }
>+ if (req->bd_dsc_pool)
>+ devm_kfree(mdev->dev, req->bd_dsc_pool);
>+ dma_pool_free(drv_info->gpd_dma_pool, req->gpd, req->gpd_dma_addr);
>+ txq->free_idx = (txq->free_idx + 1) % txq->nr_gpds;
>+ }
>+
>+ devm_kfree(mdev->dev, txq->req_pool);
>+ devm_kfree(mdev->dev, txq);
>+
>+ return 0;
>+}
>+
>+static int mtk_cldma_alloc_rx_bd(struct cldma_drv_info *drv_info, struct rx_req *req,
>+ int nr_bds)
>+{
>+ struct bd_dsc *bd_dsc, *last_bd_dsc = NULL;
>+ struct sk_buff *tail = NULL;
>+ struct mtk_md_dev *mdev;
>+ u32 left_size;
>+ int err;
>+ int i;
>+
>+ mdev = drv_info->mdev;
>+ left_size = req->mtu;
>+
>+ req->bd_dsc_pool = devm_kcalloc(mdev->dev, nr_bds,
>+ sizeof(*bd_dsc), GFP_KERNEL);
>+ if (!req->bd_dsc_pool)
>+ return -ENOMEM;
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ bd_dsc->bd = dma_pool_zalloc(drv_info->bd_dma_pool, GFP_KERNEL,
>+ &bd_dsc->bd_dma_addr);
>+ if (!bd_dsc->bd)
>+ return -ENOMEM;
>+
>+ bd_dsc->skb = __dev_alloc_skb(req->frag_size, GFP_KERNEL);
>+ if (!bd_dsc->skb)
>+ return -ENOMEM;
>+ bd_dsc->skb->next = NULL;
>+ bd_dsc->data_dma_addr =
>+ dma_map_single(mdev->dev, bd_dsc->skb->data,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ err = dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr);
>+ if (unlikely(err))
>+ return -ENOMEM;
>+
>+ bd_dsc->bd->rx_bd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->data_dma_addr) >> 32);
>+ bd_dsc->bd->rx_bd.data_buff_ptr_l =
>+ cpu_to_le32(bd_dsc->data_dma_addr);
>+ bd_dsc->bd->rx_bd.data_allow_len =
>+ cpu_to_le16(min(req->frag_size, left_size));
>+ left_size -= min(req->frag_size, left_size);
>+ if (!last_bd_dsc) {
>+ req->gpd->rx_gpd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->bd_dma_addr) >> 32);
>+ req->gpd->rx_gpd.data_buff_ptr_l =
>+ cpu_to_le32(bd_dsc->bd_dma_addr);
>+ } else {
>+ last_bd_dsc->bd->rx_bd.next_bd_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->bd_dma_addr) >> 32);
>+ last_bd_dsc->bd->rx_bd.next_bd_ptr_l =
>+ cpu_to_le32(bd_dsc->bd_dma_addr);
>+ }
>+ last_bd_dsc = bd_dsc;
>+ if (tail) {
>+ tail->next = bd_dsc->skb;
>+ tail = bd_dsc->skb;
>+ continue;
>+ }
>+ if (!req->skb) {
>+ req->skb = bd_dsc->skb;
>+ } else {
>+ skb_shinfo(req->skb)->frag_list = bd_dsc->skb;
>+ tail = bd_dsc->skb;
>+ }
>+ }
>+ last_bd_dsc->bd->rx_bd.bd_flags |= CLDMA_BD_FLAG_EOL;
>+ return 0;
>+}
>+
>+static void mtk_cldma_rxq_alloc_cancel(struct cldma_drv_info *drv_info, struct rx_req *req,
>+ int nr_bds)
>+{
>+ struct mtk_md_dev *mdev;
>+ struct bd_dsc *bd_dsc;
>+ int i;
>+
>+ mdev = drv_info->mdev;
>+
>+ if (nr_bds) {
>+ if (req->skb)
>+ skb_shinfo(req->skb)->frag_list = NULL;
>+ if (req->bd_dsc_pool) {
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ if (!bd_dsc->bd)
>+ break;
>+ if (bd_dsc->skb) {
>+ if (!dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr))
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ bd_dsc->data_dma_addr = 0;
>+ bd_dsc->skb->next = NULL;
>+ dev_kfree_skb_any(bd_dsc->skb);
>+ }
>+ dma_pool_free(drv_info->bd_dma_pool, bd_dsc->bd,
>+ bd_dsc->bd_dma_addr);
>+ }
>+ devm_kfree(mdev->dev, req->bd_dsc_pool);
>+ }
>+ } else {
>+ if (req->skb) {
>+ if (!dma_mapping_error(mdev->dev, req->data_dma_addr))
>+ dma_unmap_single(mdev->dev, req->data_dma_addr,
>+ req->mtu, DMA_FROM_DEVICE);
>+ req->data_dma_addr = 0;
>+ dev_kfree_skb_any(req->skb);
>+ }
>+ }
>+ dma_pool_free(drv_info->gpd_dma_pool, req->gpd, req->gpd_dma_addr);
>+}
>+
>+static struct rxq *mtk_cldma_rxq_alloc(struct cldma_drv_info *drv_info, struct sk_buff *skb)
>+{
>+ struct trb_open_priv *trb_open_priv = (struct trb_open_priv *)skb->data;
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct mtk_md_dev *mdev;
>+ struct rx_req *next;
>+ struct rx_req *req;
>+ u16 rx_frag_size;
>+ struct rxq *rxq;
>+ int err;
>+ int i;
>+
>+ mdev = drv_info->mdev;
>+ ctrl_blk = mdev->ctrl_blk;
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ drv_ops = drv_info->drv_ops;
>+
>+ rxq = devm_kzalloc(mdev->dev, sizeof(*rxq), GFP_KERNEL);
>+ if (!rxq)
>+ return NULL;
>+
>+ rxq->que = radix_tree_lookup(&trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ if (rxq->que->rx_nr_gpds < MIN_GPD_NUM) {
>+ dev_err((mdev)->dev,
>+ "Failed to alloc cldma%d rxq%d due to gpd number < 2\n",
>+ drv_info->hw_id, rxq->rxqno);
>+ goto err_free_rxq;
>+ }
>+ rxq->drv_info = drv_info;
>+ rxq->rxqno = rxq->que->rxqno;
>+ rxq->nr_gpds = rxq->que->rx_nr_gpds;
>+ rxq->arg = trb->priv;
>+ rxq->rx_done = trb_open_priv->rx_done;
>+ atomic_set(&rxq->need_exit, 0);
>+ rx_frag_size = rxq->que->rx_frag_size;
>+ if (rxq->que->rx_mtu > rx_frag_size && rx_frag_size)
>+ rxq->nr_bds = (rxq->que->rx_mtu + rx_frag_size - 1) / rx_frag_size;
>+
>+ rxq->req_pool = devm_kcalloc(mdev->dev, rxq->nr_gpds, sizeof(*req), GFP_KERNEL);
>+ if (!rxq->req_pool)
>+ goto err_free_rxq;
>+
>+ /* setup rx request */
>+ for (i = 0; i < rxq->nr_gpds; i++) {
>+ req = rxq->req_pool + i;
>+ req->mtu = rxq->que->rx_mtu;
>+ req->frag_size = rx_frag_size;
>+ req->gpd = dma_pool_zalloc(drv_info->gpd_dma_pool, GFP_KERNEL, &req->gpd_dma_addr);
>+ if (!req->gpd)
>+ goto err_free_req;
>+ if (rxq->nr_bds) {
>+ err = mtk_cldma_alloc_rx_bd(drv_info, req, rxq->nr_bds);
>+ if (err)
>+ goto err_free_req;
>+ req->gpd->rx_gpd.gpd_flags |= CLDMA_GPD_FLAG_BDP;
>+ } else {
>+ req->skb = __dev_alloc_skb(req->mtu, GFP_KERNEL);
>+ if (!req->skb)
>+ goto err_free_req;
>+ req->data_dma_addr = dma_map_single(mdev->dev, req->skb->data,
>+ req->mtu, DMA_FROM_DEVICE);
>+ err = dma_mapping_error(mdev->dev, req->data_dma_addr);
>+ if (unlikely(err))
>+ goto err_free_req;
>+ }
>+ }
>+
>+ for (i = 0; i < rxq->nr_gpds; i++) {
>+ req = rxq->req_pool + i;
>+ next = rxq->req_pool + ((i + 1) % rxq->nr_gpds);
>+ req->gpd->rx_gpd.gpd_flags |= CLDMA_GPD_FLAG_IOC;
>+ req->gpd->rx_gpd.data_allow_len = cpu_to_le16(req->mtu);
>+ req->gpd->rx_gpd.next_gpd_ptr_h = cpu_to_le32((u64)(next->gpd_dma_addr) >> 32);
>+ req->gpd->rx_gpd.next_gpd_ptr_l = cpu_to_le32(next->gpd_dma_addr);
>+ if (!rxq->nr_bds) {
>+ req->gpd->rx_gpd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(req->data_dma_addr) >> 32);
>+ req->gpd->rx_gpd.data_buff_ptr_l = cpu_to_le32(req->data_dma_addr);
>+ }
>+ if (i != rxq->nr_gpds - 1)
>+ req->gpd->rx_gpd.gpd_flags |= CLDMA_GPD_FLAG_HWO;
>+ }
>+
>+ INIT_WORK(&rxq->rx_done_work, mtk_cldma_rx_done_work);
>+
>+ drv_info->rxq[rxq->rxqno] = rxq;
>+ drv_ops->cldma_stop_queue(drv_info, DIR_RX, rxq->rxqno);
>+ drv_ops->cldma_setup_start_addr(drv_info, DIR_RX,
>+ rxq->rxqno, rxq->req_pool[0].gpd_dma_addr);
>+ drv_ops->cldma_start_queue(drv_info, DIR_RX, rxq->rxqno);
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_RX, rxq->rxqno, QUEUE_ERROR);
>+ drv_ops->cldma_unmask_intr(drv_info, DIR_RX, rxq->rxqno, QUEUE_XFER_DONE);
>+
>+ return rxq;
>+
>+err_free_req:
>+ for (i = 0; i < rxq->nr_gpds; i++) {
>+ req = rxq->req_pool + i;
>+ if (!req->gpd)
>+ break;
>+ mtk_cldma_rxq_alloc_cancel(drv_info, req, rxq->nr_bds);
>+ }
>+
>+ devm_kfree(mdev->dev, rxq->req_pool);
>+err_free_rxq:
>+ devm_kfree(mdev->dev, rxq);
>+ return NULL;
>+}
>+
>+static int mtk_cldma_rxq_free(struct cldma_drv_info *drv_info, u32 rxqno)

please make it void

>+{
>+ struct cldma_drv_ops *drv_ops;
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct mtk_md_dev *mdev;
>+ struct bd_dsc *bd_dsc;
>+ struct rx_req *req;
>+ struct rxq *rxq;
>+ int irq_id;
>+ int i, j;
>+
>+ mdev = drv_info->mdev;
>+ ctrl_blk = mdev->ctrl_blk;
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ drv_ops = drv_info->drv_ops;
>+
>+ rxq = drv_info->rxq[rxqno];
>+ drv_info->rxq[rxqno] = NULL;
>+
>+ /* stop HW rx transaction */
>+ atomic_set(&rxq->need_exit, 1);
>+ drv_ops->cldma_stop_queue(drv_info, DIR_RX, rxqno);
>+
>+ irq_id = mtk_pci_get_virq_id(mdev, drv_info->pci_ext_irq_id);
>+ synchronize_irq(irq_id);
>+ /* flush on-going work */
>+ flush_work(&rxq->rx_done_work);
>+ /* mask L2 RX interrupt again to avoid race condition causing use-after-free issue */
>+ drv_ops->cldma_mask_intr(drv_info, DIR_RX, rxqno, QUEUE_XFER_DONE);
>+ drv_ops->cldma_mask_intr(drv_info, DIR_RX, rxqno, QUEUE_ERROR);
>+
>+ /* free rx req resource */
>+ for (i = 0; i < rxq->nr_gpds; i++) {
>+ req = rxq->req_pool + rxq->free_idx;
>+ if (!(req->gpd->rx_gpd.gpd_flags & CLDMA_GPD_FLAG_HWO) &&
>+ le16_to_cpu(req->gpd->rx_gpd.data_recv_len)) {
>+ mtk_cldma_rx_skb_adjust(mdev, rxq, req);
>+ rxq->rx_done(req->skb, rxq->arg, true);
>+ req->skb = NULL;
>+ }
>+ if (req->skb) {
>+ if (rxq->nr_bds) {
>+ skb_shinfo(req->skb)->frag_list = NULL;
>+ } else {
>+ if (req->data_dma_addr)
>+ dma_unmap_single(mdev->dev, req->data_dma_addr,
>+ req->mtu, DMA_FROM_DEVICE);
>+ dev_kfree_skb_any(req->skb);
>+ }
>+ }
>+ for (j = 0; j < rxq->nr_bds; j++) {
>+ bd_dsc = req->bd_dsc_pool + j;
>+ if (bd_dsc->skb) {
>+ if (bd_dsc->data_dma_addr)
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ req->frag_size, DMA_FROM_DEVICE);
>+ bd_dsc->skb->next = NULL;
>+ dev_kfree_skb_any(bd_dsc->skb);
>+ }
>+ dma_pool_free(drv_info->bd_dma_pool,
>+ bd_dsc->bd, bd_dsc->bd_dma_addr);
>+ }
>+ if (req->bd_dsc_pool)
>+ devm_kfree(mdev->dev, req->bd_dsc_pool);
>+ dma_pool_free(drv_info->gpd_dma_pool, req->gpd, req->gpd_dma_addr);
>+ rxq->free_idx = (rxq->free_idx + 1) % rxq->nr_gpds;
>+ }
>+
>+ devm_kfree(mdev->dev, rxq->req_pool);
>+ devm_kfree(mdev->dev, rxq);
>+
>+ return 0;
>+}
>+
>+static int mtk_cldma_start_xfer(struct cldma_drv_info *drv_info, u32 qno)
>+{
>+ struct cldma_drv_ops *drv_ops;
>+ struct txq *txq;
>+ int ret = 0;
>+ u32 val;
>+
>+ txq = drv_info->txq[qno];
>+ drv_ops = drv_info->drv_ops;
>+
>+ val = drv_ops->cldma_get_tx_start_addr(drv_info, qno);
>+ if (unlikely(!val)) {
>+ drv_ops->cldma_drv_init(drv_info);
>+ txq = drv_info->txq[qno];
>+ drv_ops->cldma_setup_start_addr(drv_info, DIR_TX, qno,
>+ txq->req_pool[txq->free_idx].gpd_dma_addr);
>+ drv_ops->cldma_start_queue(drv_info, DIR_TX, qno);
>+ txq->tx_started = true;
>+ } else if (unlikely(val == LINK_ERROR_VAL)) {
>+ ret = -EIO;
>+ } else {
>+ if (unlikely(!txq->tx_started)) {
>+ drv_ops->cldma_start_queue(drv_info, DIR_TX, qno);
>+ txq->tx_started = true;
>+ } else {
>+ drv_ops->cldma_resume_queue(drv_info, DIR_TX, qno);
>+ }
>+ }
>+
>+ return ret;

just return 0, no need to zeroinit ret

>+}
>+
>+int mtk_cldma_init(struct mtk_ctrl_trans *trans)
>+{
>+ struct cldma_dev *cd;
>+
>+ cd = devm_kzalloc(trans->mdev->dev, sizeof(*cd), GFP_KERNEL);
>+ if (!cd)
>+ return -ENOMEM;
>+
>+ cd->trans = trans;
>+ trans->dev = cd;
>+
>+ return 0;
>+}
>+
>+int mtk_cldma_exit(struct mtk_ctrl_trans *trans)

void?

>+{
>+ if (!trans->dev)
>+ return 0;
>+
>+ devm_kfree(trans->mdev->dev, trans->dev);
>+ trans->dev = NULL;
>+
>+ return 0;
>+}
>+
>+static int mtk_cldma_open(struct cldma_dev *cd, struct sk_buff *skb)
>+{
>+ struct trb_open_priv *trb_open_priv = (struct trb_open_priv *)skb->data;
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_info *drv_info;
>+ struct queue_info *que;
>+ struct txq *txq;
>+ struct rxq *rxq;
>+ int err = 0;

please be consistent within the series
either you name 'ret' either 'err'

>+
>+ que = radix_tree_lookup(&cd->trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ drv_info = cd->cldma_drv_info[que->hif_id];
>+ if (!drv_info) {
>+ err = -EIO;
>+ goto out;
>+ }
>+
>+ if (que->tx_mtu == 0 || que->rx_mtu == 0) {
>+ dev_err((cd->trans->mdev)->dev,
>+ "Failed to enable cldma%d txq%d rxq%d due to wrong mtu\n",
>+ drv_info->hw_id, que->txqno, que->rxqno);
>+ err = -EINVAL;
>+ goto out;
>+ }
>+
>+ trb_open_priv->tx_mtu = que->tx_mtu;
>+ trb_open_priv->rx_mtu = que->rx_mtu;
>+ trb_open_priv->tx_frag_size = que->tx_frag_size;
>+ trb_open_priv->rx_frag_size = que->rx_frag_size;
>+
>+ if (drv_info->txq[que->txqno] || drv_info->rxq[que->rxqno]) {
>+ err = -EBUSY;
>+ goto out;
>+ }
>+
>+ txq = mtk_cldma_txq_alloc(drv_info, skb);
>+ if (!txq) {
>+ err = -ENOMEM;
>+ goto out;
>+ }
>+
>+ rxq = mtk_cldma_rxq_alloc(drv_info, skb);
>+ if (!rxq) {
>+ err = -ENOMEM;
>+ mtk_cldma_txq_free(drv_info, txq->txqno);
>+ goto out;
>+ }
>+
>+out:
>+ trb->status = err;
>+ trb->trb_complete(skb);
>+
>+ return err;
>+}
>+
>+static int mtk_cldma_tx(struct cldma_dev *cd, struct sk_buff *skb)
>+{
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_info *drv_info;
>+ struct mtk_md_dev *mdev;
>+ struct queue_info *que;
>+ struct txq *txq;
>+ int err = 0;

no need to zeroinit

>+
>+ que = radix_tree_lookup(&cd->trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ drv_info = cd->cldma_drv_info[que->hif_id];
>+ if (unlikely(!drv_info))
>+ return -EPIPE;
>+ txq = drv_info->txq[que->txqno];
>+ if (unlikely(!txq) || txq->is_stopping)
>+ return -EPIPE;
>+
>+ mdev = drv_info->mdev;
>+
>+ err = mtk_cldma_start_xfer(drv_info, que->txqno);
>+ if (unlikely(err))
>+ dev_err((mdev)->dev, "Failed to trigger cldma tx\n");
>+
>+ return err;
>+}
>+
>+static int mtk_cldma_close(struct cldma_dev *cd, struct sk_buff *skb)
>+{
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_info *drv_info;
>+ struct queue_info *que;
>+
>+ que = radix_tree_lookup(&cd->trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ drv_info = cd->cldma_drv_info[que->hif_id];
>+ if (unlikely(!drv_info))
>+ return -EPIPE;
>+
>+ if (drv_info->txq[que->txqno])
>+ mtk_cldma_txq_free(drv_info, que->txqno);
>+ if (drv_info->rxq[que->rxqno])
>+ mtk_cldma_rxq_free(drv_info, que->rxqno);
>+
>+ trb->status = 0;
>+ trb->trb_complete(skb);
>+
>+ return 0;
>+}
>+
>+static int mtk_cldma_txbuf_set(struct cldma_drv_info *drv_info, struct sk_buff *skb,
>+ struct tx_req *req, int nr_bds)
>+{
>+ struct sk_buff *curr_skb, *next_skb;
>+ struct mtk_md_dev *mdev;
>+ struct bd_dsc *bd_dsc;
>+ int err;
>+ int i;
>+
>+ mdev = drv_info->mdev;
>+
>+ if (nr_bds) {
>+ bd_dsc = req->bd_dsc_pool;
>+ curr_skb = skb;
>+ for (i = 0; i < nr_bds && curr_skb; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ if (req->bd_dsc_pool == bd_dsc) {
>+ bd_dsc->data_len = skb->len - skb->data_len;
>+ next_skb = skb_shinfo(skb)->frag_list;
>+ } else {
>+ bd_dsc->data_len = curr_skb->len;
>+ next_skb = curr_skb->next;
>+ }
>+ bd_dsc->data_dma_addr = dma_map_single(mdev->dev, curr_skb->data,
>+ bd_dsc->data_len, DMA_TO_DEVICE);
>+ err = dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr);
>+ if (unlikely(err))
>+ goto err_unmap_buffer;
>+
>+ bd_dsc->bd->tx_bd.data_buff_ptr_h =
>+ cpu_to_le32((u64)(bd_dsc->data_dma_addr) >> 32);
>+ bd_dsc->bd->tx_bd.data_buff_ptr_l = cpu_to_le32(bd_dsc->data_dma_addr);
>+ bd_dsc->bd->tx_bd.data_buffer_len = cpu_to_le16(bd_dsc->data_len);
>+ curr_skb = next_skb;
>+ }
>+ bd_dsc->bd->tx_bd.bd_flags = CLDMA_BD_FLAG_EOL;
>+ } else {
>+ req->data_dma_addr = dma_map_single(mdev->dev, skb->data,
>+ skb->len, DMA_TO_DEVICE);
>+ err = dma_mapping_error(mdev->dev, req->data_dma_addr);
>+ if (unlikely(err)) {
>+ req->data_dma_addr = 0;
>+ goto err_exit;
>+ }
>+
>+ req->gpd->tx_gpd.data_buff_ptr_h = cpu_to_le32((u64)(req->data_dma_addr) >> 32);
>+ req->gpd->tx_gpd.data_buff_ptr_l = cpu_to_le32(req->data_dma_addr);
>+ }
>+
>+ return 0;
>+
>+err_unmap_buffer:
>+ for (i = 0; i < nr_bds; i++) {
>+ bd_dsc = req->bd_dsc_pool + i;
>+ if (dma_mapping_error(mdev->dev, bd_dsc->data_dma_addr)) {
>+ bd_dsc->data_dma_addr = 0;
>+ break;
>+ }
>+ dma_unmap_single(mdev->dev, bd_dsc->data_dma_addr,
>+ bd_dsc->data_len, DMA_TO_DEVICE);
>+ bd_dsc->data_dma_addr = 0;
>+ }
>+err_exit:
>+ dev_err((mdev)->dev, "Failed to map dma! error:%d\n", err);
>+ return -EAGAIN;
>+}
>+
>+int mtk_cldma_submit_tx(void *dev, struct sk_buff *skb)
>+{
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_dev *cd = dev;
>+ struct queue_info *que;
>+ struct tx_req *req;
>+ struct txq *txq;
>+ int ret;
>+
>+ que = radix_tree_lookup(&cd->trans->queue_tbl, trb->channel_id & 0xFFFF);
>+ drv_info = cd->cldma_drv_info[que->hif_id];
>+ if (unlikely(!drv_info)) {
>+ ret = -EINVAL;
>+ goto out;

why cannot return directly?

>+ }
>+
>+ txq = drv_info->txq[que->txqno];
>+ if (unlikely(!txq)) {
>+ ret = -EINVAL;
>+ goto out;
>+ }
>+
>+ if (!atomic_read(&txq->req_budget)) {
>+ ret = -EAGAIN;
>+ goto out;
>+ }
>+
>+ req = txq->req_pool + txq->wr_idx;
>+ req->gpd->tx_gpd.debug_id = 0x01;
>+ ret = mtk_cldma_txbuf_set(drv_info, skb, req, txq->nr_bds);
>+ if (ret)
>+ goto out;

>+ req->gpd->tx_gpd.data_buff_len = cpu_to_le16(skb->len);
>+
>+ wmb(); /* ensure data msg set done before HWO setup */
>+
>+ req->gpd->tx_gpd.gpd_flags |= CLDMA_GPD_FLAG_HWO;
>+
>+ wmb(); /* ensure HWO setup done before req msg setup */
>+
>+ req->data_len = skb->len;
>+ req->skb = skb;
>+ req->data_vm_addr = skb->data;
>+ txq->wr_idx = (txq->wr_idx + 1) % txq->nr_gpds;
>+ atomic_dec(&txq->req_budget);
>+
>+out:
>+ return ret;
>+}
>+
>+int mtk_cldma_get_tx_budget(void *dev, enum mtk_hif_id hif_id, u32 qno)
>+{
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_dev *cd = dev;
>+ struct txq *txq;
>+
>+ if (unlikely(hif_id >= NR_CLDMA || qno >= HW_QUE_NUM || !cd))
>+ return -EINVAL;
>+
>+ drv_info = cd->cldma_drv_info[hif_id];
>+ if (!drv_info)
>+ return -EINVAL;
>+ txq = drv_info->txq[qno];
>+ if (!txq)
>+ return -EINVAL;
>+ return atomic_read(&txq->req_budget);
>+}
>+
>+static int (*trb_act_tbl[TRB_CMD_MAX])(struct cldma_dev *cd, struct sk_buff *skb) = {
>+ [TRB_CMD_ENABLE] = mtk_cldma_open,
>+ [TRB_CMD_TX] = mtk_cldma_tx,
>+ [TRB_CMD_DISABLE] = mtk_cldma_close,
>+};
>+
>+int mtk_cldma_trb_process(void *dev, struct sk_buff *skb)
>+{
>+ struct cldma_dev *cd;
>+ struct trb *trb;
>+
>+ if (!dev || !skb)
>+ return -EINVAL;
>+
>+ cd = (struct cldma_dev *)dev;
>+ trb = (struct trb *)skb->cb;
>+
>+ if (!(trb->cmd > TRB_CMD_MIN && trb->cmd < TRB_CMD_STOP))
>+ return -EINVAL;
>+
>+ return trb_act_tbl[trb->cmd](cd, skb);
>+}
>+
>+int mtk_cldma_check_ch_cfg(void *dev, struct queue_info *que)
>+{
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_dev *cd = dev;
>+ struct mtk_md_dev *mdev;
>+ struct txq *txq;
>+ struct rxq *rxq;
>+
>+ mdev = cd->trans->mdev;
>+ drv_info = cd->cldma_drv_info[que->hif_id];
>+
>+ if (unlikely(!drv_info)) {

what's te benefit of using unlikely here?

>+ dev_err((mdev)->dev, "CLDMA%d has not been initialized\n",
>+ mtk_cldma_hw_id_tbl[que->hif_id]);
>+ return -EINVAL;
>+ }
>+
>+ txq = drv_info->txq[que->txqno];
>+ rxq = drv_info->rxq[que->rxqno];
>+ if (unlikely(!txq || !rxq)) {
>+ dev_err((mdev)->dev,
>+ "CLDMA%d txq%d rxq%d has not been enabled\n",
>+ mtk_cldma_hw_id_tbl[que->hif_id], que->txqno, que->rxqno);
>+ return -EINVAL;
>+ }
>+
>+ if (que->tx_mtu != txq->que->tx_mtu || que->rx_mtu != rxq->que->rx_mtu) {
>+ dev_err((mdev)->dev,
>+ "Channel:%08x tx_mtu:%08x rx_mtu:%08x do not match ch cfg\n",
>+ que->tx_chl, que->tx_mtu, que->rx_mtu);
>+ return -EINVAL;
>+ }
>+
>+ return 0;
>+}
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
>new file mode 100644
>index 000000000000..246d28d3d798
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
>@@ -0,0 +1,170 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_CLDMA_H__
>+#define __MTK_CLDMA_H__
>+
>+#include <linux/dma-mapping.h>
>+#include <linux/dmapool.h>
>+#include <linux/interrupt.h>
>+#include <linux/list.h>
>+#include <linux/spinlock.h>
>+#include <linux/types.h>
>+
>+#include "mtk_ctrl_plane.h"
>+#include "mtk_trans_ctrl.h"
>+
>+struct mtk_fsm_param;
>+
>+#define TXQ(N) (N)
>+#define RXQ(N) (N)
>+
>+#define CLDMA_GPD_FLAG_HWO BIT(0)
>+#define CLDMA_GPD_FLAG_BDP BIT(1)
>+#define CLDMA_GPD_FLAG_BPS BIT(2)
>+#define CLDMA_GPD_FLAG_IOC BIT(7)
>+#define CLDMA_BD_FLAG_EOL BIT(0)
>+
>+union gpd {
>+ struct {
>+ u8 gpd_flags;
>+ u8 non_used1;
>+ __le16 data_allow_len;
>+ __le32 next_gpd_ptr_h;
>+ __le32 next_gpd_ptr_l;
>+ __le32 data_buff_ptr_h;
>+ __le32 data_buff_ptr_l;
>+ __le16 data_recv_len;
>+ u8 non_used2;
>+ u8 debug_id;
>+ } rx_gpd;
>+
>+ struct {
>+ u8 gpd_flags;
>+ u8 non_used1;
>+ u8 non_used2;
>+ u8 debug_id;
>+ __le32 next_gpd_ptr_h;
>+ __le32 next_gpd_ptr_l;
>+ __le32 data_buff_ptr_h;
>+ __le32 data_buff_ptr_l;
>+ __le16 data_buff_len;
>+ __le16 non_used3;
>+ } tx_gpd;
>+} __packed;
>+
>+union bd {
>+ struct {
>+ u8 bd_flags;
>+ u8 non_used1;
>+ __le16 data_allow_len;
>+ __le32 next_bd_ptr_h;
>+ __le32 next_bd_ptr_l;
>+ __le32 data_buff_ptr_h;
>+ __le32 data_buff_ptr_l;
>+ __le16 data_recv_len;
>+ __le16 non_used2;
>+ } rx_bd;
>+
>+ struct {
>+ u8 bd_flags;
>+ u8 non_used1;
>+ __le16 non_used2;
>+ __le32 next_bd_ptr_h;
>+ __le32 next_bd_ptr_l;
>+ __le32 data_buff_ptr_h;
>+ __le32 data_buff_ptr_l;
>+ __le16 data_buffer_len;
>+ u8 extension_len;
>+ u8 non_used3;
>+ } tx_bd;
>+} __packed;
>+
>+struct bd_dsc {
>+ union bd *bd;
>+ struct sk_buff *skb;
>+ dma_addr_t bd_dma_addr;
>+ dma_addr_t data_dma_addr;
>+ size_t data_len;
>+};
>+
>+struct rx_req {
>+ union gpd *gpd;
>+ u32 mtu;
>+ struct sk_buff *skb;
>+ size_t data_len;
>+ dma_addr_t gpd_dma_addr;
>+ dma_addr_t data_dma_addr;
>+ u32 frag_size;
>+ struct bd_dsc *bd_dsc_pool;
>+};
>+
>+struct rxq {
>+ struct cldma_drv_info *drv_info;
>+ u32 rxqno;
>+ struct queue_info *que;
>+ struct work_struct rx_done_work;
>+ struct rx_req *req_pool;
>+ u32 nr_gpds;
>+ u32 free_idx;
>+ unsigned short rx_done_cnt;
>+ void *arg;
>+ int (*rx_done)(struct sk_buff *skb, void *priv, bool force_recv);
>+ u32 nr_bds;
>+ atomic_t need_exit;
>+};
>+
>+struct tx_req {
>+ union gpd *gpd;
>+ u32 mtu;
>+ void *data_vm_addr;
>+ size_t data_len;
>+ dma_addr_t data_dma_addr;
>+ dma_addr_t gpd_dma_addr;
>+ struct sk_buff *skb;
>+ int (*trb_complete)(struct sk_buff *skb);
>+ u32 frag_size;
>+ struct bd_dsc *bd_dsc_pool;
>+};
>+
>+struct txq {
>+ struct cldma_drv_info *drv_info;
>+ u32 txqno;
>+ struct queue_info *que;
>+ struct work_struct tx_done_work;
>+ struct tx_req *req_pool;
>+ u32 nr_gpds;
>+ atomic_t req_budget;
>+ u32 wr_idx;
>+ u32 free_idx;
>+ bool tx_started;
>+ bool is_stopping;
>+ unsigned short tx_done_cnt;
>+ u32 nr_bds;
>+};
>+
>+struct cldma_dev {
>+ struct cldma_drv_info *cldma_drv_info[NR_CLDMA];
>+ struct mtk_ctrl_trans *trans;
>+};
>+
>+struct cldma_drv_info_desc {
>+ u32 hw_ver;
>+ struct cldma_drv_ops *drv_ops;
>+ struct cldma_hw_regs *hw_regs;
>+};
>+
>+int mtk_cldma_init(struct mtk_ctrl_trans *trans);
>+int mtk_cldma_exit(struct mtk_ctrl_trans *trans);
>+int mtk_cldma_submit_tx(void *dev, struct sk_buff *skb);
>+int mtk_cldma_get_tx_budget(void *dev, enum mtk_hif_id hif_id, u32 qno);
>+int mtk_cldma_trb_process(void *dev, struct sk_buff *skb);
>+void mtk_cldma_fsm_state_listener(struct mtk_fsm_param *param, struct mtk_ctrl_trans *trans);
>+int mtk_cldma_check_ch_cfg(void *dev, struct queue_info *que);
>+
>+#define drv_ops_name(NAME) cldma_drv_ops_##NAME
>+#define cldma_regs_name(NAME) mtk_cldma_regs_##NAME
>+
>+#endif
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.c
>new file mode 100644
>index 000000000000..d5eb2ab9a425
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.c
>@@ -0,0 +1,373 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2023, MediaTek Inc.
>+ */
>+
>+#include <linux/delay.h>
>+#include <linux/device.h>
>+#include <linux/dma-mapping.h>
>+#include <linux/dmapool.h>
>+#include <linux/err.h>
>+#include <linux/interrupt.h>
>+#include <linux/kdev_t.h>
>+#include <linux/kernel.h>
>+#include <linux/kthread.h>
>+#include <linux/list.h>
>+#include <linux/module.h>
>+#include <linux/mutex.h>
>+#include <linux/netdevice.h>
>+#include <linux/sched.h>
>+#include <linux/skbuff.h>
>+#include <linux/slab.h>
>+#include <linux/timer.h>
>+#include <linux/wait.h>
>+#include <linux/workqueue.h>
>+
>+#include "mtk_cldma_drv.h"
>+#include "mtk_dev.h"
>+#include "mtk_pci.h"
>+#include "mtk_pci_reg.h"
>+
>+#define WAIT_QUEUE_STOP (70)
>+
>+void mtk_cldma_drv_init(struct cldma_drv_info *drv_info)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ struct mtk_md_dev *mdev;
>+ int base;
>+ u32 val;
>+
>+ mdev = drv_info->mdev;
>+ base = drv_info->base_addr;
>+ hw_regs = drv_info->hw_regs;
>+
>+ /* set CLDMA to 64 bit mode GPD */
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_ul_cfg);
>+ val = (val & (~(0x7 << 5))) | ((0x4) << 5);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ul_cfg, val);
>+
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_so_cfg);
>+ val = (val & (~(0x7 << 10))) | ((0x4) << 10) | (1 << 2);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_so_cfg, val);
>+
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_rx_work_to_reg_mask_set, ALLQ);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ip_busy_to_pcie_mask_set,
>+ ALLQ << 16);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ip_busy_to_pcie_mask_clr,
>+ ALLQ << 24);
>+
>+ /* enable interrupt to PCIe */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_int_mask, 0);
>+
>+ /* disable illegal memory check */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ul_dummy_0, 1);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_so_dummy_0, 1);
>+}
>+
>+void mtk_cldma_setup_start_addr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, dma_addr_t addr)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ unsigned int addr_l;
>+ unsigned int addr_h;
>+ int base;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX) {
>+ addr_l = base + hw_regs->reg_cldma_ul_start_addrl_0 + qno * HW_QUEUE_NUM;
>+ addr_h = base + hw_regs->reg_cldma_ul_start_addrh_0 + qno * HW_QUEUE_NUM;
>+ } else {
>+ addr_l = base + hw_regs->reg_cldma_so_start_addrl_0 + qno * HW_QUEUE_NUM;
>+ addr_h = base + hw_regs->reg_cldma_so_start_addrh_0 + qno * HW_QUEUE_NUM;
>+ }
>+
>+ mtk_pci_write32(drv_info->mdev, addr_l, (u32)addr);
>+ mtk_pci_write32(drv_info->mdev, addr_h, (u32)((u64)addr >> 32));
>+}
>+
>+void mtk_cldma_mask_intr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ int base;
>+ u32 addr;
>+ u32 val;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_l2timsr0;
>+ else
>+ addr = base + hw_regs->reg_cldma_l2rimsr0;
>+
>+ if (qno == ALLQ)
>+ val = qno << type;
>+ else
>+ val = BIT(qno) << type;
>+
>+ mtk_pci_write32(drv_info->mdev, addr, val);
>+}
>+
>+void mtk_cldma_unmask_intr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ int base;
>+ u32 addr;
>+ u32 val;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_l2timcr0;
>+ else
>+ addr = base + hw_regs->reg_cldma_l2rimcr0;
>+
>+ if (qno == ALLQ)
>+ val = qno << type;
>+ else
>+ val = BIT(qno) << type;
>+
>+ mtk_pci_write32(drv_info->mdev, addr, val);
>+}
>+
>+void mtk_cldma_clr_intr_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ struct mtk_md_dev *mdev;
>+ int base;
>+ u32 addr;
>+ u32 val;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+ mdev = drv_info->mdev;
>+
>+ if (type == QUEUE_ERROR) {
>+ if (dir == DIR_TX) {
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l3tisar0);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l3tisar0, val);
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l3tisar1);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l3tisar1, val);
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l3tisar2);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l3tisar2, val);
>+ } else {
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l3risar0);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l3risar0, val);
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l3risar1);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l3risar1, val);
>+ }
>+ }
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_l2tisar0;
>+ else
>+ addr = base + hw_regs->reg_cldma_l2risar0;
>+
>+ if (qno == ALLQ)
>+ val = qno << type;
>+ else
>+ val = BIT(qno) << type;
>+
>+ mtk_pci_write32(mdev, addr, val);
>+ val = mtk_pci_read32(mdev, addr);
>+}
>+
>+u32 mtk_cldma_check_intr_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ int base;
>+ u32 addr;
>+ u32 val;
>+ u32 sta;

please squash

>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_l2tisar0;
>+ else
>+ addr = base + hw_regs->reg_cldma_l2risar0;
>+
>+ val = mtk_pci_read32(drv_info->mdev, addr);
>+ if (val == LINK_ERROR_VAL)
>+ sta = val;
>+ else if (qno == ALLQ)
>+ sta = (val >> type) & 0xFF;
>+ else
>+ sta = (val >> type) & BIT(qno);
>+
>+ return sta;
>+}
>+
>+void mtk_cldma_start_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ u32 val = BIT(qno);
>+ int base;
>+ u32 addr;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_ul_start_cmd;
>+ else
>+ addr = base + hw_regs->reg_cldma_so_start_cmd;
>+
>+ mtk_pci_write32(drv_info->mdev, addr, val);
>+}
>+
>+void mtk_cldma_resume_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ u32 val = BIT(qno);
>+ int base;
>+ u32 addr;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_ul_resume_cmd;
>+ else
>+ addr = base + hw_regs->reg_cldma_so_resume_cmd;
>+
>+ mtk_pci_write32(drv_info->mdev, addr, val);
>+}
>+
>+u32 mtk_cldma_queue_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ int base;
>+ u32 addr;
>+ u32 val;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_ul_status;
>+ else
>+ addr = base + hw_regs->reg_cldma_so_status;
>+
>+ val = mtk_pci_read32(drv_info->mdev, addr);
>+
>+ if (qno == ALLQ || val == LINK_ERROR_VAL)
>+ return val;
>+
>+ return val & BIT(qno);
>+}
>+
>+u32 mtk_cldma_stop_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno)
>+{
>+ u32 val = (qno == ALLQ) ? qno : BIT(qno);
>+ struct cldma_hw_regs *hw_regs;
>+ unsigned int active;
>+ int cnt = 0;
>+ int base;
>+ u32 addr;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+
>+ if (dir == DIR_TX)
>+ addr = base + hw_regs->reg_cldma_ul_stop_cmd;
>+ else
>+ addr = base + hw_regs->reg_cldma_so_stop_cmd;
>+
>+ mtk_pci_write32(drv_info->mdev, addr, val);
>+
>+ do {
>+ active = drv_info->drv_ops->cldma_queue_status(drv_info, dir, qno);
>+ if (active == LINK_ERROR_VAL || !active)
>+ break;
>+ usleep_range(WAIT_QUEUE_STOP, 2 * WAIT_QUEUE_STOP);
>+ } while (++cnt < 10);
>+
>+ return active;
>+}
>+
>+void mtk_cldma_clear_ip_busy(struct cldma_drv_info *drv_info)
>+{
>+ mtk_pci_write32(drv_info->mdev, drv_info->base_addr +
>+ drv_info->hw_regs->reg_cldma_ip_busy, 0x01);
>+}
>+
>+void mtk_cldma_get_intr_status(struct cldma_drv_info *drv_info, u32 *tx_sta, u32 *rx_sta)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ struct mtk_md_dev *mdev;
>+ u32 tx_mask, rx_mask;
>+ int base;
>+
>+ mdev = drv_info->mdev;
>+ base = drv_info->base_addr;
>+ hw_regs = drv_info->hw_regs;
>+
>+ *tx_sta = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l2tisar0);
>+ tx_mask = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l2timr0);
>+ *rx_sta = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l2risar0);
>+ rx_mask = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_l2rimr0);
>+
>+ *tx_sta = (*tx_sta) & (~tx_mask);
>+ *rx_sta = (*rx_sta) & (~rx_mask);
>+
>+ if (*tx_sta) {
>+ /* TX XFER_DONE and QUEUE_ERROR mask */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l2timsr0, *tx_sta);
>+ /* TX XFER_DONE clear */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l2tisar0,
>+ (*tx_sta) & (0xFF << QUEUE_XFER_DONE));
>+ }
>+
>+ if (*rx_sta) {
>+ /* RX XFER_DONE and QUEUE_ERROR mask */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l2rimsr0, *rx_sta);
>+ /* RX XFER_DONE clear */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_l2risar0,
>+ (*rx_sta) & (0xFF << QUEUE_XFER_DONE));
>+ }
>+}
>+
>+u32 mtk_cldma_get_tx_start_addr(struct cldma_drv_info *drv_info, u32 qno)
>+{
>+ u32 addr, val;
>+
>+ addr = drv_info->base_addr + drv_info->hw_regs->reg_cldma_ul_start_addrl_0 +
>+ qno * HW_QUEUE_NUM;
>+ val = mtk_pci_read32(drv_info->mdev, addr);
>+
>+ return val;
>+}
>+
>+u64 mtk_cldma_get_rx_curr_addr(struct cldma_drv_info *drv_info, u32 qno)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ u32 curr_addr_h, curr_addr_l;
>+ struct mtk_md_dev *mdev;
>+ u64 curr_addr;
>+ int base;
>+ u64 addr;
>+
>+ hw_regs = drv_info->hw_regs;
>+ base = drv_info->base_addr;
>+ mdev = drv_info->mdev;
>+
>+ addr = base + hw_regs->reg_cldma_so_current_addrh_0 +
>+ (u64)qno * HW_QUEUE_NUM;
>+ curr_addr_h = mtk_pci_read32(mdev, addr);
>+ addr = base + hw_regs->reg_cldma_so_current_addrl_0 +
>+ (u64)qno * HW_QUEUE_NUM;
>+ curr_addr_l = mtk_pci_read32(mdev, addr);
>+ curr_addr = ((u64)curr_addr_h << 32) | curr_addr_l;
>+ if (curr_addr_h == LINK_ERROR_VAL && curr_addr_l == LINK_ERROR_VAL)
>+ curr_addr = 0;
>+ return curr_addr;
>+}
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h
>new file mode 100644
>index 000000000000..8763c23abf54
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv.h
>@@ -0,0 +1,177 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2023, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_CLDMA_DRV_H__
>+#define __MTK_CLDMA_DRV_H__
>+
>+#define HW_QUEUE_NUM (8)
>+#define ALLQ (0xFF)
>+#define LINK_ERROR_VAL (0xFFFFFFFF)
>+#define CLDMA0_HW_ID (0)
>+#define CLDMA1_HW_ID (1)
>+#define CLDMA4_HW_ID (4)
>+
>+struct cldma_hw_regs {
>+ u8 cldma_rx_skb_pool_max_size;
>+ u8 cldma_rx_skb_reload_threshold;
>+ u8 tq_err_int_offset;
>+ u8 tq_active_start_err_int_offset;
>+ u8 rq_err_int_offset;
>+ u8 rq_active_start_err_int_offset;
>+ u16 reg_cldma_so_cfg;
>+ u16 reg_cldma_so_start_addrl_0;
>+ u16 reg_cldma_so_start_addrh_0;
>+ u16 reg_cldma_so_current_addrl_0;
>+ u16 reg_cldma_so_current_addrh_0;
>+ u16 reg_cldma_so_status;
>+ u16 reg_cldma_debug_id_en;
>+ u16 reg_cldma_so_last_update_addrl_0;
>+ u16 reg_cldma_so_last_update_addrh_0;
>+ u16 reg_cldma_l2rimr0;
>+ u16 reg_cldma_l2rimr1;
>+ u16 reg_cldma_l2rimcr0;
>+ u16 reg_cldma_l2rimcr1;
>+ u16 reg_cldma_l2rimsr0;
>+ u16 reg_cldma_l2rimsr1;
>+ u16 reg_cldma_int_mask;
>+ u16 reg_cldma4_int_mask;
>+ u16 reg_cldma_slp_mem_ctl;
>+ u16 reg_cldma_busy_mask;
>+ u16 reg_cldma_ip_busy_to_pcie_mask;
>+ u16 reg_cldma_ip_busy_to_pcie_mask_set;
>+ u16 reg_cldma_ip_busy_to_pcie_mask_clr;
>+ u16 reg_cldma_ip_busy_to_ap_mask;
>+ u16 reg_cldma_ip_busy_to_ap_mask_set;
>+ u16 reg_cldma_ip_busy_to_ap_mask_clr;
>+ u16 reg_cldma_ip_busy_to_md_mask_set;
>+ u16 reg_cldma_rx_work_to_reg_mask_set;
>+ u16 reg_infra_rst4_set;
>+ u16 reg_infra_rst4_clr;
>+ u16 reg_infra_rst2_set;
>+ u16 reg_infra_rst2_clr;
>+ u16 reg_infra_rst0_set;
>+ u16 reg_infra_rst0_clr;
>+ u32 tq_err_int_bitmask;
>+ u32 tq_active_start_err_int_bitmask;
>+ u32 rq_err_int_bitmask;
>+ u32 cldma0_base_addr;
>+ u32 cldma1_base_addr;
>+ u32 cldma4_base_addr;
>+ u32 rq_active_start_err_int_bitmask;
>+ u32 reg_cldma_ul_start_addrl_0;
>+ u32 reg_cldma_ul_start_addrh_0;
>+ u32 reg_cldma_ul_current_addrl_0;
>+ u32 reg_cldma_ul_current_addrh_0;
>+ u32 reg_cldma_ul_status;
>+ u32 reg_cldma_ul_start_cmd;
>+ u32 reg_cldma_ul_resume_cmd;
>+ u32 reg_cldma_ul_stop_cmd;
>+ u32 reg_cldma_ul_error;
>+ u32 reg_cldma_ul_cfg;
>+ u32 reg_cldma_ul_dummy_0;
>+ u32 reg_cldma_so_error;
>+ u32 reg_cldma_so_start_cmd;
>+ u32 reg_cldma_so_resume_cmd;
>+ u32 reg_cldma_so_stop_cmd;
>+ u32 reg_cldma_so_dummy_0;
>+ u32 reg_cldma_l2tisar0;
>+ u32 reg_cldma_l2tisar1;
>+ u32 reg_cldma_l2timr0;
>+ u32 reg_cldma_l2timr1;
>+ u32 reg_cldma_l2timcr0;
>+ u32 reg_cldma_l2timcr1;
>+ u32 reg_cldma_l2timsr0;
>+ u32 reg_cldma_l2timsr1;
>+ u32 reg_cldma_l2risar0;
>+ u32 reg_cldma_l2risar1;
>+ u32 reg_cldma_l3tisar0;
>+ u32 reg_cldma_l3tisar1;
>+ u32 reg_cldma_l3tisar2;
>+ u32 reg_cldma_l3risar0;
>+ u32 reg_cldma_l3risar1;
>+ u32 reg_cldma_ip_busy;
>+};
>+
>+enum mtk_ip_busy_src {
>+ IP_BUSY_TXDONE = 0,
>+ IP_BUSY_TXEMPTY = 8,
>+ IP_BUSY_TXACTIVE = 16,
>+ IP_BUSY_RXDONE = 24
>+};
>+
>+enum mtk_intr_type {
>+ QUEUE_XFER_DONE = 0,
>+ QUEUE_EMPTY = 8,
>+ QUEUE_ERROR = 16,
>+ QUEUE_ACTIVE_START = 24,
>+ INVALID_TYPE
>+};
>+
>+enum mtk_tx_rx {
>+ DIR_TX,
>+ DIR_RX,
>+ DIR_MAX
>+};
>+
>+struct cldma_drv_info {
>+ int hif_id;
>+ int hw_id;
>+ int base_addr;
>+ int pci_ext_irq_id;
>+ struct mtk_md_dev *mdev;
>+ struct cldma_dev *cd;
>+ struct txq *txq[HW_QUEUE_NUM];
>+ struct rxq *rxq[HW_QUEUE_NUM];
>+ struct dma_pool *gpd_dma_pool;
>+ struct dma_pool *bd_dma_pool;
>+ struct workqueue_struct *wq;
>+ struct cldma_hw_regs *hw_regs;
>+ struct cldma_drv_ops *drv_ops;
>+};
>+
>+struct cldma_drv_ops {
>+ void (*cldma_drv_init)(struct cldma_drv_info *drv_info);
>+ void (*cldma_drv_reset)(struct cldma_drv_info *drv_info);
>+ void (*cldma_setup_start_addr)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, dma_addr_t addr);
>+ void (*cldma_mask_intr)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+ void (*cldma_unmask_intr)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+ void (*cldma_clr_intr_status)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+ u32 (*cldma_check_intr_status)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+ void (*cldma_start_queue)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+ void (*cldma_resume_queue)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+ u32 (*cldma_queue_status)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+ u32 (*cldma_stop_queue)(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+ void (*cldma_clear_ip_busy)(struct cldma_drv_info *drv_info);
>+ void (*cldma_get_intr_status)(struct cldma_drv_info *drv_info, u32 *tx_sta, u32 *rx_sta);
>+ u32 (*cldma_get_tx_start_addr)(struct cldma_drv_info *drv_info, u32 qno);
>+ u64 (*cldma_get_rx_curr_addr)(struct cldma_drv_info *drv_info, u32 qno);
>+};
>+
>+void mtk_cldma_drv_init(struct cldma_drv_info *drv_info);
>+void mtk_cldma_setup_start_addr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, dma_addr_t addr);
>+void mtk_cldma_mask_intr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+void mtk_cldma_unmask_intr(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+void mtk_cldma_clr_intr_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+u32 mtk_cldma_check_intr_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir,
>+ u32 qno, enum mtk_intr_type type);
>+void mtk_cldma_start_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+void mtk_cldma_resume_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+u32 mtk_cldma_queue_status(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+u32 mtk_cldma_stop_queue(struct cldma_drv_info *drv_info, enum mtk_tx_rx dir, u32 qno);
>+void mtk_cldma_clear_ip_busy(struct cldma_drv_info *drv_info);
>+void mtk_cldma_get_intr_status(struct cldma_drv_info *drv_info, u32 *tx_sta, u32 *rx_sta);
>+u32 mtk_cldma_get_tx_start_addr(struct cldma_drv_info *drv_info, u32 qno);
>+u64 mtk_cldma_get_rx_curr_addr(struct cldma_drv_info *drv_info, u32 qno);
>+
>+#endif
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c
>new file mode 100644
>index 000000000000..240a9f58f658
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.c
>@@ -0,0 +1,182 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2023, MediaTek Inc.
>+ */
>+
>+#include <linux/delay.h>
>+#include <linux/device.h>
>+#include <linux/dma-mapping.h>
>+#include <linux/dmapool.h>
>+#include <linux/err.h>
>+#include <linux/interrupt.h>
>+#include <linux/kdev_t.h>
>+#include <linux/kernel.h>
>+#include <linux/kthread.h>
>+#include <linux/list.h>
>+#include <linux/module.h>
>+#include <linux/mutex.h>
>+#include <linux/netdevice.h>
>+#include <linux/sched.h>
>+#include <linux/skbuff.h>
>+#include <linux/slab.h>
>+#include <linux/timer.h>
>+#include <linux/wait.h>
>+#include <linux/workqueue.h>
>+
>+#include "mtk_cldma_drv.h"
>+#include "mtk_cldma_drv_m9xx.h"
>+#include "mtk_dev.h"
>+#include "mtk_pci.h"
>+#include "mtk_pci_reg.h"
>+#include "mtk_trans_ctrl.h"
>+
>+struct cldma_hw_regs mtk_cldma_regs_m9xx = {
>+ .cldma0_base_addr = CLDMA0_BASE_ADDR,
>+ .cldma1_base_addr = CLDMA1_BASE_ADDR,
>+ .cldma4_base_addr = CLDMA4_BASE_ADDR,
>+ .cldma_rx_skb_pool_max_size = CLDMA_RX_SKB_POOL_MAX_SIZE,
>+ .cldma_rx_skb_reload_threshold = CLDMA_RX_SKB_RELOAD_THRESHOLD,
>+ .tq_err_int_offset = TQ_ERR_INT_OFFSET,
>+ .tq_err_int_bitmask = TQ_ERR_INT_BITMASK,
>+ .tq_active_start_err_int_offset = TQ_ACTIVE_START_ERR_INT_OFFSET,
>+ .tq_active_start_err_int_bitmask = TQ_ACTIVE_START_ERR_INT_BITMASK,
>+ .rq_err_int_offset = RQ_ERR_INT_OFFSET,
>+ .rq_err_int_bitmask = RQ_ERR_INT_BITMASK,
>+ .rq_active_start_err_int_offset = RQ_ACTIVE_START_ERR_INT_OFFSET,
>+ .rq_active_start_err_int_bitmask = RQ_ACTIVE_START_ERR_INT_BITMASK,
>+ .reg_cldma_ul_start_addrl_0 = REG_CLDMA_UL_START_ADDRL_0,
>+ .reg_cldma_ul_start_addrh_0 = REG_CLDMA_UL_START_ADDRH_0,
>+ .reg_cldma_ul_current_addrl_0 = REG_CLDMA_UL_CURRENT_ADDRL_0,
>+ .reg_cldma_ul_current_addrh_0 = REG_CLDMA_UL_CURRENT_ADDRH_0,
>+ .reg_cldma_ul_status = REG_CLDMA_UL_STATUS,
>+ .reg_cldma_ul_start_cmd = REG_CLDMA_UL_START_CMD,
>+ .reg_cldma_ul_resume_cmd = REG_CLDMA_UL_RESUME_CMD,
>+ .reg_cldma_ul_stop_cmd = REG_CLDMA_UL_STOP_CMD,
>+ .reg_cldma_ul_error = REG_CLDMA_UL_ERROR,
>+ .reg_cldma_ul_cfg = REG_CLDMA_UL_CFG,
>+ .reg_cldma_ul_dummy_0 = REG_CLDMA_UL_DUMMY_0,
>+ .reg_cldma_so_error = REG_CLDMA_SO_ERROR,
>+ .reg_cldma_so_start_cmd = REG_CLDMA_SO_START_CMD,
>+ .reg_cldma_so_resume_cmd = REG_CLDMA_SO_RESUME_CMD,
>+ .reg_cldma_so_stop_cmd = REG_CLDMA_SO_STOP_CMD,
>+ .reg_cldma_so_dummy_0 = REG_CLDMA_SO_DUMMY_0,
>+ .reg_cldma_so_cfg = REG_CLDMA_SO_CFG,
>+ .reg_cldma_so_start_addrl_0 = REG_CLDMA_SO_START_ADDRL_0,
>+ .reg_cldma_so_start_addrh_0 = REG_CLDMA_SO_START_ADDRH_0,
>+ .reg_cldma_so_current_addrl_0 = REG_CLDMA_SO_CUR_ADDRL_0,
>+ .reg_cldma_so_current_addrh_0 = REG_CLDMA_SO_CUR_ADDRH_0,
>+ .reg_cldma_so_status = REG_CLDMA_SO_STATUS,
>+ .reg_cldma_debug_id_en = REG_CLDMA_DEBUG_ID_EN,
>+ .reg_cldma_so_last_update_addrl_0 = REG_CLDMA_SO_LAST_UPDATE_ADDRL_0,
>+ .reg_cldma_so_last_update_addrh_0 = REG_CLDMA_SO_LAST_UPDATE_ADDRH_0,
>+ .reg_cldma_l2tisar0 = REG_CLDMA_L2TISAR0,
>+ .reg_cldma_l2tisar1 = REG_CLDMA_L2TISAR1,
>+ .reg_cldma_l2timr0 = REG_CLDMA_L2TIMR0,
>+ .reg_cldma_l2timr1 = REG_CLDMA_L2TIMR1,
>+ .reg_cldma_l2timcr0 = REG_CLDMA_L2TIMCR0,
>+ .reg_cldma_l2timcr1 = REG_CLDMA_L2TIMCR1,
>+ .reg_cldma_l2timsr0 = REG_CLDMA_L2TIMSR0,
>+ .reg_cldma_l2timsr1 = REG_CLDMA_L2TIMSR1,
>+ .reg_cldma_l3tisar0 = REG_CLDMA_L3TISAR0,
>+ .reg_cldma_l3tisar1 = REG_CLDMA_L3TISAR1,
>+ .reg_cldma_l3tisar2 = REG_CLDMA_L3TISAR2,
>+ .reg_cldma_l2risar0 = REG_CLDMA_L2RISAR0,
>+ .reg_cldma_l2risar1 = REG_CLDMA_L2RISAR1,
>+ .reg_cldma_l2rimr0 = REG_CLDMA_L2RIMR0,
>+ .reg_cldma_l2rimr1 = REG_CLDMA_L2RIMR1,
>+ .reg_cldma_l2rimcr0 = REG_CLDMA_L2RIMCR0,
>+ .reg_cldma_l2rimcr1 = REG_CLDMA_L2RIMCR1,
>+ .reg_cldma_l2rimsr0 = REG_CLDMA_L2RIMSR0,
>+ .reg_cldma_l2rimsr1 = REG_CLDMA_L2RIMSR1,
>+ .reg_cldma_l3risar0 = REG_CLDMA_L3RISAR0,
>+ .reg_cldma_l3risar1 = REG_CLDMA_L3RISAR1,
>+ .reg_cldma_ip_busy = REG_CLDMA_IP_BUSY,
>+ .reg_cldma_int_mask = REG_CLDMA_INT_EAP_USIP_MASK,
>+ .reg_cldma4_int_mask = REG_CLDMA_INT_WF_MASK,
>+ .reg_cldma_ip_busy_to_pcie_mask = REG_CLDMA_IP_BUSY_TO_PCIE_MASK,
>+ .reg_cldma_ip_busy_to_pcie_mask_set = REG_CLDMA_IP_BUSY_TO_PCIE_MASK_SET,
>+ .reg_cldma_ip_busy_to_pcie_mask_clr = REG_CLDMA_IP_BUSY_TO_PCIE_MASK_CLR,
>+ .reg_cldma_ip_busy_to_ap_mask = REG_CLDMA_IP_BUSY_TO_AP_MASK,
>+ .reg_cldma_ip_busy_to_ap_mask_set = REG_CLDMA_IP_BUSY_TO_AP_MASK_SET,
>+ .reg_cldma_ip_busy_to_ap_mask_clr = REG_CLDMA_IP_BUSY_TO_AP_MASK_CLR,
>+ .reg_cldma_ip_busy_to_md_mask_set = REG_CLDMA_IP_BUSY_TO_MD_MASK_SET,
>+ .reg_cldma_rx_work_to_reg_mask_set = REG_CLDMA_RX_WORK_TO_REG_MASK_SET,
>+ .reg_infra_rst0_set = REG_INFRA_RST0_SET,
>+ .reg_infra_rst0_clr = REG_INFRA_RST0_CLR,
>+};
>+
>+static void mtk_cldma_drv_init_m9xx(struct cldma_drv_info *drv_info)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ struct mtk_md_dev *mdev;
>+ int base;
>+ u32 val;
>+
>+ mdev = drv_info->mdev;
>+ base = drv_info->base_addr;
>+ hw_regs = drv_info->hw_regs;
>+
>+ /* set CLDMA to 64 bit mode GPD */
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_ul_cfg);
>+
>+ val = (val & (~(0x7 << 5))) | ((0x4) << 5);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ul_cfg, val);
>+
>+ val = mtk_pci_read32(mdev, base + hw_regs->reg_cldma_so_cfg);
>+ val = (val & (~(0x7 << 10))) | ((0x4) << 10) | (1 << 2);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_so_cfg, val);
>+
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_rx_work_to_reg_mask_set, ALLQ);
>+
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ip_busy_to_pcie_mask_set,
>+ ALLQ << 16);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ip_busy_to_pcie_mask_clr,
>+ ALLQ << 24);
>+
>+ /* enable interrupt to PCIe */
>+ if (drv_info->hw_id == CLDMA4_HW_ID)
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma4_int_mask, 0);
>+ else
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_int_mask, 0);
>+
>+ /* disable illegal memory check */
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_ul_dummy_0, 1);
>+ mtk_pci_write32(mdev, base + hw_regs->reg_cldma_so_dummy_0, 1);
>+}
>+
>+static void mtk_cldma_drv_reset_m9xx(struct cldma_drv_info *drv_info)
>+{
>+ struct cldma_hw_regs *hw_regs;
>+ struct mtk_md_dev *mdev;
>+ u32 val;
>+
>+ mdev = drv_info->mdev;
>+ hw_regs = drv_info->hw_regs;
>+
>+ val = mtk_pci_read32(mdev, REG_DEV_INFRA_BASE + hw_regs->reg_infra_rst0_set);
>+
>+ val |= 1 << (REG_CLDMA0_RST_SET_BIT + drv_info->hw_id);
>+ mtk_pci_write32(mdev, REG_DEV_INFRA_BASE + hw_regs->reg_infra_rst0_set, val);
>+ udelay(1);
>+ val = mtk_pci_read32(mdev, REG_DEV_INFRA_BASE + hw_regs->reg_infra_rst0_clr);
>+ val |= 1 << (REG_CLDMA0_RST_CLR_BIT + drv_info->hw_id);
>+ mtk_pci_write32(mdev, REG_DEV_INFRA_BASE + hw_regs->reg_infra_rst0_clr, val);
>+}
>+
>+struct cldma_drv_ops cldma_drv_ops_m9xx = {
>+ .cldma_drv_init = mtk_cldma_drv_init_m9xx,
>+ .cldma_drv_reset = mtk_cldma_drv_reset_m9xx,
>+ .cldma_setup_start_addr = mtk_cldma_setup_start_addr,
>+ .cldma_mask_intr = mtk_cldma_mask_intr,
>+ .cldma_unmask_intr = mtk_cldma_unmask_intr,
>+ .cldma_clr_intr_status = mtk_cldma_clr_intr_status,
>+ .cldma_check_intr_status = mtk_cldma_check_intr_status,
>+ .cldma_start_queue = mtk_cldma_start_queue,
>+ .cldma_resume_queue = mtk_cldma_resume_queue,
>+ .cldma_queue_status = mtk_cldma_queue_status,
>+ .cldma_stop_queue = mtk_cldma_stop_queue,
>+ .cldma_clear_ip_busy = mtk_cldma_clear_ip_busy,
>+ .cldma_get_intr_status = mtk_cldma_get_intr_status,
>+ .cldma_get_tx_start_addr = mtk_cldma_get_tx_start_addr,
>+ .cldma_get_rx_curr_addr = mtk_cldma_get_rx_curr_addr,
>+};
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h
>new file mode 100644
>index 000000000000..2c63c43ff065
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma_drv_m9xx.h
>@@ -0,0 +1,103 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2023, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_CLDMA_DRV_M9XX_H__
>+#define __MTK_CLDMA_DRV_M9XX_H__
>+
>+#define CLDMA0_BASE_ADDR (0x1021C000)
>+#define CLDMA1_BASE_ADDR (0x1021E000)
>+#define CLDMA4_BASE_ADDR (0x10224000)
>+
>+#define CLDMA_RX_SKB_POOL_MAX_SIZE (64)
>+#define CLDMA_RX_SKB_RELOAD_THRESHOLD (16)
>+
>+/* L2TISAR0 */
>+#define TQ_ERR_INT_OFFSET (16)
>+#define TQ_ERR_INT_BITMASK (0x00FF0000)
>+#define TQ_ACTIVE_START_ERR_INT_OFFSET (24)
>+#define TQ_ACTIVE_START_ERR_INT_BITMASK (0xFF000000)
>+
>+/* L2RISAR0 */
>+#define RQ_ERR_INT_OFFSET (16)
>+#define RQ_ERR_INT_BITMASK (0x00FF0000)
>+#define RQ_ACTIVE_START_ERR_INT_OFFSET (24)
>+#define RQ_ACTIVE_START_ERR_INT_BITMASK (0xFF000000)
>+
>+/* CLDMA IN(Tx) */
>+#define REG_CLDMA_UL_START_ADDRL_0 (0x0004)
>+#define REG_CLDMA_UL_START_ADDRH_0 (0x0008)
>+#define REG_CLDMA_UL_CURRENT_ADDRL_0 (0x0044)
>+#define REG_CLDMA_UL_CURRENT_ADDRH_0 (0x0048)
>+#define REG_CLDMA_UL_STATUS (0x0084)
>+#define REG_CLDMA_UL_START_CMD (0x0088)
>+#define REG_CLDMA_UL_RESUME_CMD (0x008C)
>+#define REG_CLDMA_UL_STOP_CMD (0x0090)
>+#define REG_CLDMA_UL_ERROR (0x0094)
>+#define REG_CLDMA_UL_CFG (0x0098)
>+#define REG_CLDMA_UL_DUMMY_0 (0x009C)
>+
>+/* CLDMA OUT(Rx) */
>+#define REG_CLDMA_SO_ERROR (0x0400 + 0x0100)
>+#define REG_CLDMA_SO_START_CMD (0x0400 + 0x01BC)
>+#define REG_CLDMA_SO_RESUME_CMD (0x0400 + 0x01C0)
>+#define REG_CLDMA_SO_STOP_CMD (0x0400 + 0x01C4)
>+#define REG_CLDMA_SO_DUMMY_0 (0x0400 + 0x0108)
>+#define REG_CLDMA_SO_CFG (0x0400 + 0x0004)
>+#define REG_CLDMA_SO_START_ADDRL_0 (0x0400 + 0x0078)
>+#define REG_CLDMA_SO_START_ADDRH_0 (0x0400 + 0x007C)
>+#define REG_CLDMA_SO_CUR_ADDRL_0 (0x0400 + 0x00B8)
>+#define REG_CLDMA_SO_CUR_ADDRH_0 (0x0400 + 0x00BC)
>+#define REG_CLDMA_SO_STATUS (0x0400 + 0x00F8)
>+#define REG_CLDMA_DEBUG_ID_EN (0x0400 + 0x00FC)
>+#define REG_CLDMA_SO_LAST_UPDATE_ADDRL_0 (0x0400 + 0x01C8)
>+#define REG_CLDMA_SO_LAST_UPDATE_ADDRH_0 (0x0400 + 0x01CC)
>+
>+/* CLDMA MISC */
>+#define REG_CLDMA_L2TISAR0 (0x0800 + 0x0010)
>+#define REG_CLDMA_L2TISAR1 (0x0800 + 0x0014)
>+#define REG_CLDMA_L2TIMR0 (0x0800 + 0x0018)
>+#define REG_CLDMA_L2TIMR1 (0x0800 + 0x001C)
>+#define REG_CLDMA_L2TIMCR0 (0x0800 + 0x0020)
>+#define REG_CLDMA_L2TIMCR1 (0x0800 + 0x0024)
>+#define REG_CLDMA_L2TIMSR0 (0x0800 + 0x0028)
>+#define REG_CLDMA_L2TIMSR1 (0x0800 + 0x002C)
>+#define REG_CLDMA_L3TISAR0 (0x0800 + 0x0030)
>+#define REG_CLDMA_L3TISAR1 (0x0800 + 0x0034)
>+#define REG_CLDMA_L2RISAR0 (0x0800 + 0x0050)
>+#define REG_CLDMA_L2RISAR1 (0x0800 + 0x0054)
>+#define REG_CLDMA_L3RISAR0 (0x0800 + 0x0070)
>+#define REG_CLDMA_L3RISAR1 (0x0800 + 0x0074)
>+#define REG_CLDMA_IP_BUSY (0x0800 + 0x00B4)
>+#define REG_CLDMA_L3TISAR2 (0x0800 + 0x00C0)
>+
>+#define REG_CLDMA_L2RIMR0 (0x0800 + 0x00E8)
>+#define REG_CLDMA_L2RIMR1 (0x0800 + 0x00EC)
>+#define REG_CLDMA_L2RIMCR0 (0x0800 + 0x00F0)
>+#define REG_CLDMA_L2RIMCR1 (0x0800 + 0x00F4)
>+#define REG_CLDMA_L2RIMSR0 (0x0800 + 0x00F8)
>+#define REG_CLDMA_L2RIMSR1 (0x0800 + 0x00FC)
>+
>+#define REG_CLDMA_INT_EAP_USIP_MASK (0x0800 + 0x011C)
>+#define REG_CLDMA_INT_WF_MASK (0x0800 + 0x0120)
>+#define REG_CLDMA_RQ1_GPD_DONE_CNT (0x0800 + 0x0174)
>+#define REG_CLDMA_TQ1_GPD_DONE_CNT (0x0800 + 0x0184)
>+
>+#define REG_CLDMA_IP_BUSY_TO_PCIE_MASK (0x0800 + 0x0194)
>+#define REG_CLDMA_IP_BUSY_TO_PCIE_MASK_SET (0x0800 + 0x0198)
>+#define REG_CLDMA_IP_BUSY_TO_PCIE_MASK_CLR (0x0800 + 0x019C)
>+
>+#define REG_CLDMA_IP_BUSY_TO_AP_MASK (0x0800 + 0x0200)
>+#define REG_CLDMA_IP_BUSY_TO_AP_MASK_SET (0x0800 + 0x0204)
>+#define REG_CLDMA_IP_BUSY_TO_AP_MASK_CLR (0x0800 + 0x0208)
>+#define REG_CLDMA_IP_BUSY_TO_MD_MASK_SET (0x0800 + 0x0210)
>+#define REG_CLDMA_RX_WORK_TO_REG_MASK_SET (0x0800 + 0x021C)
>+
>+/* CLDMA RESET */
>+#define REG_INFRA_RST0_SET (0x120)
>+#define REG_INFRA_RST0_CLR (0x124)
>+#define REG_CLDMA0_RST_SET_BIT (8)
>+#define REG_CLDMA0_RST_CLR_BIT (8)
>+
>+#endif
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c b/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c
>new file mode 100644
>index 000000000000..bf3f87723167
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c
>@@ -0,0 +1,23 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include "mtk_cldma.h"
>+#include "mtk_trans_ctrl.h"
>+
>+#define TRB_SRV_NUM (1)
>+
>+static const int mtk_srv_cfg_m9xx[NR_CLDMA][HW_QUE_NUM] = {
>+ {0},
>+ {0},
>+};
>+
>+static const struct queue_info mtk_queue_info_m9xx[] = {
>+};
>+
>+struct mtk_ctrl_info mtk_ctrl_info_m9xx = {
>+ .queue_info = (struct queue_info *)mtk_queue_info_m9xx,
>+ .queue_info_num = ARRAY_SIZE(mtk_queue_info_m9xx),
>+ .trb_srv_num = TRB_SRV_NUM,
>+};
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>index 518c32d55643..d604c9cb06ea 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>@@ -760,6 +760,28 @@ static void mtk_pci_free_irq(struct mtk_md_dev *mdev)
> pci_free_irq_vectors(pdev);
> }
>
>+static int mtk_pci_dev_init(struct mtk_md_dev *mdev)
>+{
>+ int ret;
>+
>+ ret = mtk_trans_ctrl_init(mdev);
>+ if (ret) {
>+ dev_err(mdev->dev, "Failed to initialize control plane: %d\n", ret);
>+ return ret;
>+ }
>+
>+ return 0;
>+}
>+
>+static void mtk_pci_dev_exit(struct mtk_md_dev *mdev)
>+{
>+ mtk_trans_ctrl_exit(mdev);
>+}
>+
>+static int mtk_pci_dev_start(struct mtk_md_dev *mdev)
>+{
>+ return 0;
>+}
> static const struct mtk_dev_ops pci_hw_ops = {
> .get_dev_state = mtk_pci_get_dev_state,
> .ack_dev_state = mtk_pci_ack_dev_state,
>@@ -834,6 +856,12 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> if (ret)
> goto free_mhccif;
>
>+ ret = mtk_pci_dev_init(mdev);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to init dev.\n");
>+ goto free_irq;
>+ }
>+
> pci_set_master(pdev);
> mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
>
>@@ -850,10 +878,20 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> goto clear_master;
> }
>
>+ ret = mtk_pci_dev_start(mdev);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to start dev.\n");
>+ goto free_saved_state;
>+ }
>+
> return 0;
>
>+free_saved_state:
>+ pci_load_and_free_saved_state(pdev, &priv->saved_state);
> clear_master:
> pci_clear_master(pdev);
>+ mtk_pci_dev_exit(mdev);
>+free_irq:
> mtk_pci_free_irq(mdev);
> free_mhccif:
> mtk_mhccif_exit(mdev);
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h b/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h
>index d033dbf4b0af..0f16e6954397 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h
>@@ -21,6 +21,7 @@
> #define REG_IMASK_HOST_MSIX_SET_GRP0_0 0x3000
> #define REG_IMASK_HOST_MSIX_CLR_GRP0_0 0x3080
> #define REG_IMASK_HOST_MSIX_GRP0_0 0x3100
>+#define REG_DEV_INFRA_BASE 0x10001000
>
> /* mhccif registers */
> #define MHCCIF_RC2EP_SW_BSY 0x4
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c
>new file mode 100644
>index 000000000000..7fad64d214aa
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.c
>@@ -0,0 +1,569 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include <linux/device.h>
>+#include <linux/freezer.h>
>+#include <linux/hashtable.h>
>+#include <linux/kthread.h>
>+#include <linux/list.h>
>+#include <linux/nospec.h>
>+#include <linux/sched.h>
>+#include <linux/wait.h>
>+
>+#include "mtk_cldma.h"
>+#include "mtk_ctrl_plane.h"
>+#include "mtk_dev.h"
>+#include "mtk_pci.h"
>+#include "mtk_trans_ctrl.h"
>+
>+#define MTK_DFLT_PORT_NAME_LEN (20)
>+extern struct mtk_ctrl_info ctrl_info_name(m9xx);
>+
>+static struct mtk_ctrl_info_desc mtk_ctrl_info_tbl[] = {
>+ {2304, &ctrl_info_name(m9xx)},
>+ {0, NULL},
>+};
>+
>+#define RX_CH_ID_SHIFT 16
>+#define PORT_MTU_MASK 0xFFFF
>+#define QUEUE_CHL_MASK 0xFFFF
>+
>+static bool mtk_queue_list_is_full(struct mtk_ctrl_trans *trans, struct queue_info *que)
>+{
>+ return trans->trans_list[que->hif_id].skb_list[que->txqno].qlen >= SKB_LIST_MAX_LEN;
>+}
>+
>+static bool mtk_ctrl_chs_is_busy_or_empty(struct trb_srv *srv)
>+{
>+ struct srv_que *srv_que;
>+ int i;
>+
>+ for (i = 0; i < NR_CLDMA; i++)
>+ list_for_each_entry(srv_que, &srv->srv_q_list[i], list)
>+ if (!skb_queue_empty(&srv->trans->trans_list[i].skb_list[srv_que->qno]) &&
>+ mtk_cldma_get_tx_budget(srv->trans->dev, i, srv_que->qno))
>+ return false;
>+
>+ return true;
>+}
>+
>+static void mtk_ctrl_ch_flush(struct sk_buff_head *skb_list)
>+{
>+ struct sk_buff *skb;
>+ struct trb *trb;
>+
>+ while (!skb_queue_empty(skb_list)) {
>+ skb = skb_dequeue(skb_list);
>+ trb = (struct trb *)skb->cb;
>+ trb->status = -EIO;
>+ trb->trb_complete(skb);
>+ }
>+}
>+
>+static void mtk_ctrl_chs_flush(struct trb_srv *srv)
>+{
>+ struct srv_que *srv_que;
>+ int i;
>+
>+ for (i = 0; i < NR_CLDMA; i++)
>+ list_for_each_entry(srv_que, &srv->srv_q_list[i], list)
>+ mtk_ctrl_ch_flush(&srv->trans->trans_list[i].skb_list[srv_que->qno]);
>+}
>+
>+static int mtk_ch_status_check(struct mtk_ctrl_trans *trans, struct sk_buff *skb)
>+{
>+ struct trb *trb = (struct trb *)skb->cb;
>+ struct trb_open_priv *trb_open_priv;
>+ struct queue_info *que;
>+ int ret = 0;
>+
>+ que = radix_tree_lookup(&trans->queue_tbl, trb->channel_id & QUEUE_CHL_MASK);
>+
>+ switch (trb->cmd) {
>+ case TRB_CMD_ENABLE:
>+ trb_open_priv = (struct trb_open_priv *)skb->data;
>+ trb_open_priv->log_rg_offset = que->log_rg_offset;
>+ trans->usr_cnt[que->hif_id][que->txqno]++;
>+ if (trans->usr_cnt[que->hif_id][que->txqno] == 1)
>+ break;
>+ trb_open_priv->tx_mtu = que->tx_mtu;
>+ trb_open_priv->rx_mtu = que->rx_mtu;
>+ trb_open_priv->tx_frag_size = que->tx_frag_size;
>+ trb_open_priv->rx_frag_size = que->rx_frag_size;
>+ if (mtk_cldma_check_ch_cfg(trans->dev, que)) {
>+ trb->status = -EINVAL;
>+ ret = -EINVAL;
>+ } else {
>+ trb->status = -EBUSY;
>+ ret = -EBUSY;
>+ }
>+ trb->trb_complete(skb);
>+ break;
>+ case TRB_CMD_DISABLE:
>+ if (trans->usr_cnt[que->hif_id][que->txqno] > 0) {
>+ trans->usr_cnt[que->hif_id][que->txqno]--;
>+ if (!trans->usr_cnt[que->hif_id][que->txqno])
>+ break;
>+ }
>+ trb->status = -EBUSY;
>+ trb->trb_complete(skb);
>+ ret = -EBUSY;
>+ break;
>+ default:
>+ dev_err((trans->mdev)->dev, "Invalid trb command(%d)\n", trb->cmd);
>+ ret = -EINVAL;
>+ break;
>+ }
>+ return ret;
>+}
>+
>+static void mtk_ctrl_trb_handler(struct trb_srv *srv, struct trans_list *trans_list, u32 qno)
>+{
>+ struct sk_buff_head *skb_list = &trans_list->skb_list[qno];
>+ struct mtk_ctrl_trans *trans = srv->trans;
>+ struct sk_buff *skb, *skb_next;
>+ struct trb *trb, *trb_next;
>+ bool kick = false;
>+ int loop = 0;
>+ int err;
>+
>+ do {
>+ skb = skb_peek(skb_list);
>+ if (!skb)
>+ break;
>+ trb = (struct trb *)skb->cb;
>+
>+ switch (trb->cmd) {
>+ case TRB_CMD_ENABLE:
>+ case TRB_CMD_DISABLE:
>+ skb_unlink(skb, skb_list);
>+ err = mtk_ch_status_check(trans, skb);
>+ if (!err) {
>+ kick = true;
>+ if (trb->cmd == TRB_CMD_DISABLE)
>+ mtk_ctrl_ch_flush(skb_list);
>+ }
>+ break;
>+ case TRB_CMD_TX:
>+ err = mtk_cldma_submit_tx(trans->dev, skb);
>+ if (err) {
>+ if (trans_list->tx_burst_cnt[qno])
>+ kick = true;
>+ else if (err == -EAGAIN)

so how EAGAIN is actually used here?

>+ return;
>+ break;
>+ }
>+
>+ trans_list->tx_burst_cnt[qno]++;
>+ if (trans_list->tx_burst_cnt[qno] >= TX_BURST_MAX_CNT ||
>+ skb_queue_is_last(skb_list, skb)) {
>+ kick = true;
>+ } else {
>+ skb_next = skb_peek_next(skb, skb_list);
>+ trb_next = (struct trb *)skb_next->cb;
>+ if (trb_next->cmd != TRB_CMD_TX)
>+ kick = true;
>+ }
>+
>+ skb_unlink(skb, skb_list);
>+ break;
>+ default:
>+ skb_unlink(skb, skb_list);
>+ }
>+
>+ if (kick) {
>+ mtk_cldma_trb_process(trans->dev, skb);
>+ trans_list->tx_burst_cnt[qno] = 0;
>+ kick = false;
>+ }
>+
>+ loop++;
>+ } while (loop < TRB_NUM_PER_ROUND);
>+}
>+
>+static void mtk_ctrl_trb_process(struct trb_srv *srv)
>+{
>+ struct mtk_ctrl_trans *trans = srv->trans;
>+ struct srv_que *srv_que;
>+ int i;
>+
>+ for (i = 0; i < NR_CLDMA; i++)
>+ list_for_each_entry(srv_que, &srv->srv_q_list[i], list)
>+ mtk_ctrl_trb_handler(srv, &trans->trans_list[i], srv_que->qno);
>+}
>+
>+static int mtk_ctrl_trb_thread(void *args)
>+{
>+ struct trb_srv *srv = args;
>+
>+ for (;;) {
>+ wait_event_interruptible(srv->trb_waitq,
>+ !mtk_ctrl_chs_is_busy_or_empty(srv) ||
>+ kthread_should_stop() || kthread_should_park());
>+ if (kthread_should_stop())
>+ break;
>+
>+ if (kthread_should_park())
>+ kthread_parkme();
>+
>+ do {
>+ mtk_ctrl_trb_process(srv);
>+ cond_resched();
>+ } while (!mtk_ctrl_chs_is_busy_or_empty(srv) && !kthread_should_stop() &&
>+ !kthread_should_park());
>+ }
>+ mtk_ctrl_chs_flush(srv);
>+ return 0;
>+}
>+
>+static int mtk_ctrl_trb_srv_init(struct mtk_ctrl_trans *trans)
>+{
>+ struct srv_que *srv_que;
>+ struct trb_srv *srv;
>+ int i, j;
>+ int ret;
>+
>+ for (i = 0; i < trans->trb_srv_num; i++) {
>+ srv = devm_kzalloc(trans->mdev->dev, sizeof(*srv), GFP_KERNEL);
>+ if (!srv) {
>+ ret = -ENOMEM;
>+ goto err_free_srv;
>+ }
>+
>+ srv->trans = trans;
>+ srv->srv_id = i;
>+ trans->trb_srv[i] = srv;
>+
>+ init_waitqueue_head(&srv->trb_waitq);
>+ for (j = 0; j < NR_CLDMA; j++)
>+ INIT_LIST_HEAD(&srv->srv_q_list[j]);
>+ }
>+
>+ for (i = 0; i < NR_CLDMA; i++)
>+ for (j = 0; j < HW_QUE_NUM; j++) {
>+ if (trans->srv_cfg[i][j] < 0 ||
>+ trans->srv_cfg[i][j] >= trans->trb_srv_num)
>+ trans->srv_cfg[i][j] = 0;
>+ srv_que = devm_kzalloc(trans->mdev->dev, sizeof(*srv_que), GFP_KERNEL);
>+ if (!srv_que) {
>+ ret = -ENOMEM;
>+ goto err_free_srv_que;
>+ }
>+ srv_que->hif_id = i;
>+ srv_que->qno = j;
>+ list_add_tail(&srv_que->list,
>+ &trans->trb_srv[trans->srv_cfg[i][j]]->srv_q_list[i]);
>+ }
>+
>+ for (i = 0; i < trans->trb_srv_num; i++)
>+ trans->trb_srv[i]->trb_thread = kthread_run(mtk_ctrl_trb_thread, trans->trb_srv[i],
>+ "mtk_trb_srv%d_%s", i,
>+ trans->mdev->dev_str);
>+
>+ return 0;
>+err_free_srv_que:
>+ for (i = 0; i < trans->trb_srv_num; i++) {
>+ for (j = 0; j < NR_CLDMA; j++) {
>+ struct srv_que *next_srv_que;
>+
>+ list_for_each_entry_safe(srv_que, next_srv_que,
>+ &trans->trb_srv[i]->srv_q_list[j], list) {
>+ list_del(&srv_que->list);
>+ devm_kfree(trans->mdev->dev, srv_que);
>+ }
>+ }
>+ }
>+err_free_srv:
>+ for (i = 0; i < trans->trb_srv_num; i++) {
>+ if (!trans->trb_srv[i])
>+ break;
>+ devm_kfree(trans->mdev->dev, trans->trb_srv[i]);
>+ trans->trb_srv[i] = NULL;
>+ }
>+
>+ return ret;
>+}
>+
>+static void mtk_ctrl_trb_srv_exit(struct mtk_ctrl_trans *trans)
>+{
>+ struct srv_que *srv_que, *next_srv_que;
>+ struct trb_srv *srv;
>+ int i, j;
>+
>+ for (i = 0; i < trans->trb_srv_num; i++) {
>+ srv = trans->trb_srv[i];
>+ kthread_stop(srv->trb_thread);
>+ for (j = 0; j < NR_CLDMA; j++) {
>+ list_for_each_entry_safe(srv_que, next_srv_que,
>+ &trans->trb_srv[i]->srv_q_list[j], list) {
>+ list_del(&srv_que->list);
>+ devm_kfree(trans->mdev->dev, srv_que);
>+ }
>+ }
>+ devm_kfree(trans->mdev->dev, srv);
>+ trans->trb_srv[i] = NULL;
>+ }
>+}
>+
>+static void mtk_ctrl_remove_radix_tree(struct mtk_ctrl_trans *trans)
>+{
>+ struct queue_info **queues;
>+ int ret, idx;
>+
>+ queues = kcalloc(trans->queues_cnt, sizeof(struct queue_info *), GFP_KERNEL);
>+ if (!queues)
>+ return;
>+
>+ ret = radix_tree_gang_lookup(&trans->queue_tbl, (void **)queues,
>+ 0, trans->queues_cnt);
>+ for (idx = 0; idx < ret; idx++) {
>+ radix_tree_delete(&trans->queue_tbl, queues[idx]->rx_chl & QUEUE_CHL_MASK);
>+ kfree(queues[idx]);
>+ }
>+ kfree(queues);
>+}
>+
>+static void mtk_ctrl_queue_info_update(struct radix_tree_root *queue_tbl, u32 port_chl_mtu)
>+{
>+ struct queue_info *queue;
>+ u32 rx_chl, mtu;
>+
>+ if (!port_chl_mtu)
>+ return;
>+
>+ rx_chl = port_chl_mtu >> RX_CH_ID_SHIFT;
>+ mtu = port_chl_mtu & PORT_MTU_MASK;
>+ queue = radix_tree_lookup(queue_tbl, rx_chl);
>+ if (!queue)
>+ return;
>+
>+ queue->tx_mtu = mtu;
>+ queue->rx_mtu = mtu;
>+ queue->tx_frag_size = mtu;
>+ queue->rx_frag_size = mtu;
>+}
>+
>+static unsigned int ctrl_port_chl_mtu;
>+
>+static int mtk_pcie_hif_init(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
>+ struct queue_info *queue, *queue_info;
>+ struct mtk_ctrl_trans *trans;
>+ int i, j;
>+ int ret;
>+
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ trans->ctrl_blk = ctrl_blk;
>+ queue_info = trans->queue_info;
>+
>+ INIT_RADIX_TREE(&trans->queue_tbl, GFP_KERNEL);
>+ for (i = 0; i < trans->queue_info_num; i++) {
>+ queue = kmemdup(queue_info + i, sizeof(*queue), GFP_KERNEL);
>+ if (!queue) {
>+ ret = -ENOMEM;
>+ goto err_free_radix_tree;
>+ }
>+ if (queue->txqno >= HW_QUE_NUM || queue->rxqno >= HW_QUE_NUM ||
>+ queue->hif_id >= NR_CLDMA) {
>+ dev_err((mdev)->dev, "Failed to get correct queue info %x\n",
>+ queue->rx_chl);
>+ ret = -EINVAL;
>+ goto err_free_radix_tree;
>+ }
>+ ret = radix_tree_insert(&trans->queue_tbl, queue->rx_chl & QUEUE_CHL_MASK, queue);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Insert %x fail, ret: %d", queue->rx_chl, ret);
>+ kfree(queue);
>+ goto err_free_radix_tree;
>+ }
>+ trans->queues_cnt++;
>+ }
>+
>+ mtk_ctrl_queue_info_update(&trans->queue_tbl, ctrl_port_chl_mtu);
>+
>+ for (i = 0; i < NR_CLDMA; i++) {
>+ for (j = 0; j < HW_QUE_NUM; j++) {
>+ skb_queue_head_init(&trans->trans_list[i].skb_list[j]);
>+ trans->trans_list[i].tx_burst_cnt[j] = 0;
>+ }
>+ }
>+ ret = mtk_cldma_init(trans);
>+ if (ret)
>+ goto err_free_radix_tree;
>+
>+ ret = mtk_ctrl_trb_srv_init(trans);
>+ if (ret)
>+ goto err_cldma_exit;
>+
>+ atomic_set(&trans->available, 1);
>+
>+ return 0;
>+
>+err_cldma_exit:
>+ mtk_cldma_exit(trans);
>+err_free_radix_tree:
>+ mtk_ctrl_remove_radix_tree(trans);
>+
>+ return ret;
>+}
>+
>+static int mtk_pcie_hif_exit(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+
>+ trans = ctrl_blk->ctrl_hw_priv;
>+
>+ atomic_set(&trans->available, 0);
>+ mtk_ctrl_trb_srv_exit(trans);
>+ mtk_ctrl_remove_radix_tree(trans);
>+ mtk_cldma_exit(trans);
>+
>+ return 0;
>+}
>+
>+static int mtk_pcie_hif_submit_skb(struct mtk_md_dev *mdev, struct sk_buff *skb, bool force_send)
>+{
>+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct queue_info *que;
>+ struct trb *trb;
>+
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ trb = (struct trb *)skb->cb;
>+
>+ if (trb->cmd == TRB_CMD_STOP || trb->cmd == TRB_CMD_RECOVER) {
>+ trb->trb_complete(skb);
>+ return 0;
>+ }
>+
>+ que = radix_tree_lookup(&trans->queue_tbl, trb->channel_id & QUEUE_CHL_MASK);
>+ if (!que) {
>+ dev_warn((mdev)->dev, "lookup que fail, ch_id: %x, que: 0x%p\n",
>+ trb->channel_id, que);
>+ return -EINVAL;
>+ }
>+
>+ if (!atomic_read(&trans->available))
>+ return -EIO;
>+
>+ if (mtk_queue_list_is_full(trans, que) && !force_send)
>+ return -EAGAIN;
>+
>+ if (trb->cmd == TRB_CMD_DISABLE)
>+ skb_queue_head(&trans->trans_list[que->hif_id].skb_list[que->txqno], skb);
>+ else
>+ skb_queue_tail(&trans->trans_list[que->hif_id].skb_list[que->txqno], skb);
>+
>+ wake_up(&trans->trb_srv[trans->srv_cfg[que->hif_id][que->txqno]]->trb_waitq);
>+
>+ return 0;
>+}
>+
>+static int mtk_pcie_hif_cmd_func(struct mtk_md_dev *mdev, int cmd, void *data)
>+{
>+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
>+ struct mtk_ctrl_trans *trans;
>+ struct queue_info *que;
>+ int ret = 0;
>+
>+ switch (cmd) {
>+ case HIF_CTRL_CMD_CHECK_TX_FULL:
>+ trans = ctrl_blk->ctrl_hw_priv;
>+ que = radix_tree_lookup(&trans->queue_tbl,
>+ ((union ctrl_hif_cmd_data *)data)->rx_ch & QUEUE_CHL_MASK);
>+ if (!que) {
>+ dev_warn((mdev)->dev, "Failed to find que to check tx full\n");
>+ return -EINVAL;
>+ }
>+ return mtk_queue_list_is_full(trans, que);
>+ default:
>+ ret = -EINVAL;
>+ break;
>+ }
>+
>+ return ret;

just return 0 no need to zeroinit

>+}
>+
>+static struct mtk_ctrl_hif_ops pcie_ctrl_ops = {
>+ .init = mtk_pcie_hif_init,
>+ .exit = mtk_pcie_hif_exit,
>+ .submit_skb = mtk_pcie_hif_submit_skb,
>+ .send_cmd = mtk_pcie_hif_cmd_func,
>+};
>+