* [PATCH] media: mtk-jpeg: only init/cancel work for multi-core variants
From: Icenowy Zheng @ 2026-06-01 7:32 UTC (permalink / raw)
To: Bin Liu, Mauro Carvalho Chehab, Matthias Brugger,
AngeloGioacchino Del Regno, Nicolas Dufresne, Fan Wu,
Hans Verkuil, irui wang, kyrie wu
Cc: linux-media, linux-kernel, linux-arm-kernel, linux-mediatek,
Icenowy Zheng, stable
Single-core variants of this hardware do not use the work at all, and
the worker function is set to NULL, which leads to warnings when
cancelling the work in release callback.
Skip the work init/cancel code when the JPEG hardware isn't multi-core.
Cc: stable@vger.kernel.org
Fixes: 34c519feef3e ("media: mtk-jpeg: fix use-after-free in release path due to uncancelled work")
Fixes: d40e95274925 ("media: mtk-jpeg: reconstructs the initialization mode of worker")
Signed-off-by: Icenowy Zheng <zhengxingda@iscas.ac.cn>
---
drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
index 8c684756d5fc2..83e54a7ef49c0 100644
--- a/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
+++ b/drivers/media/platform/mediatek/jpeg/mtk_jpeg_core.c
@@ -1160,7 +1160,8 @@ static int mtk_jpeg_open(struct file *file)
goto free;
}
- INIT_WORK(&ctx->jpeg_work, jpeg->variant->jpeg_worker);
+ if (jpeg->variant->multi_core)
+ INIT_WORK(&ctx->jpeg_work, jpeg->variant->jpeg_worker);
INIT_LIST_HEAD(&ctx->dst_done_queue);
spin_lock_init(&ctx->done_queue_lock);
v4l2_fh_init(&ctx->fh, vfd);
@@ -1202,7 +1203,8 @@ static int mtk_jpeg_release(struct file *file)
struct mtk_jpeg_dev *jpeg = video_drvdata(file);
struct mtk_jpeg_ctx *ctx = mtk_jpeg_file_to_ctx(file);
- cancel_work_sync(&ctx->jpeg_work);
+ if (jpeg->variant->multi_core)
+ cancel_work_sync(&ctx->jpeg_work);
mutex_lock(&jpeg->lock);
v4l2_m2m_ctx_release(ctx->fh.m2m_ctx);
v4l2_ctrl_handler_free(&ctx->ctrl_hdl);
--
2.52.0
^ permalink raw reply related
* [GIT,PULL,2/3] MediaTek ARM32 Device Tree updates for v7.2
From: AngeloGioacchino Del Regno @ 2026-06-01 9:12 UTC (permalink / raw)
To: arm-soc, soc; +Cc: linux-arm-kernel, linux-mediatek, matthias.bgg
In-Reply-To: <20260601091225.5223-1-angelogioacchino.delregno@collabora.com>
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/mediatek/linux.git/ tags/mtk-dts32-for-v7.2
for you to fetch changes up to ba6afff1d9b70028a5fc3df2d3acbee501c20a53:
arm: dts: mediatek: mt8135: fix pinctrl node name (2026-05-11 11:39:02 +0200)
----------------------------------------------------------------
MediaTek ARM32 DeviceTree updates
This adds support for the ARMv7 Timer node in the MT6589 SoC
and performs a couple of dtbs_check fixes in MT7623 and in
MT8135 devicetrees.
----------------------------------------------------------------
Akari Tsuyukusa (1):
arm: dts: mediatek: mt6589: Add Arm Generic Timer node
David Lechner (3):
arm: dts: mediatek: mt7623: fix pinctrl child node names
arm: dts: mediatek: mt7623: fix pinctrl controller node name
arm: dts: mediatek: mt8135: fix pinctrl node name
arch/arm/boot/dts/mediatek/mt6589.dtsi | 11 +++++++++++
arch/arm/boot/dts/mediatek/mt7623.dtsi | 64 ++++++++++++++++++++++++++++++++--------------------------------
arch/arm/boot/dts/mediatek/mt8135.dtsi | 2 +-
3 files changed, 44 insertions(+), 33 deletions(-)
^ permalink raw reply
* [GIT,PULL,1/3] MediaTek ARM64 Device Tree updates for v7.2
From: AngeloGioacchino Del Regno @ 2026-06-01 9:12 UTC (permalink / raw)
To: arm-soc, soc; +Cc: linux-arm-kernel, linux-mediatek, matthias.bgg
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/mediatek/linux.git/ tags/mtk-dts64-for-v7.2
for you to fetch changes up to 9897c586b09f79ebcf2e67a888743c046b20d254:
arm64: dts: mediatek: add LED and key support on Xiaomi AX3000T (2026-05-25 10:43:10 +0200)
----------------------------------------------------------------
MediaTek ARM64 DeviceTree updates
This adds improvements for already supported SoCs and devices.
In particular:
- Adds support for the MT7981 SoC's Crypto Accelerator
- Enables gpio-keys and leds found on the MT7981b based
Xiaomi AX3000T router
- Adds new variants of MT7988 BananaPi BPi-R4 Pro
- ...and some spare cleanups for all BPi-R4 Pro boards
- Adds a MediaTek MT6365 devicetree and uses it in all of
the relevant supported boards in place of MT6359, where
needed (the MT6365 PMIC is a fully compatible variant
of the MT6359 PMIC, but still not named MT6359).
- Adds correct power supplies for CPUs and devices on a
variety of MediaTek Chromebooks and Genio AIoT boards,
including:
- MT8186 Corsola Chromebooks
- MT8192 Asurada Chromebooks
- MT8195 Cherry Chromebooks
- MT8390 Genio based boards
- MT8395 Genio based boards
- Adds HDMI TX support for Ezurio Tungsten 510/700 boards.
----------------------------------------------------------------
Aleksander Jan Bajkowski (2):
arm64: dts: mediatek: add crypto offload support on MT7981
arm64: dts: mediatek: add LED and key support on Xiaomi AX3000T
Chen-Yu Tsai (21):
arm64: dts: mediatek: mt8192-asurada: Move PCIe DMA bounce buffer to host
arm64: dts: mediatek: mt8186-corsola-voltorb: Add MT6315 PMIC supplies
arm64: dts: mediatek: mt6359: Switch to proper ldo_vcn33_[12] regulators
arm64: dts: mediatek: mt8192-asurada: Add MT6359 PMIC supplies
arm64: dts: mediatek: mt8192-asurada: Add MT6315 PMIC supplies
arm64: dts: mediatek: mt8192-asurada: Add supplies for ChromeOS EC regulators
arm64: dts: mediatek: mt8192-asurada: Add CPU power supplies
arm64: dts: mediatek: mt8192-asurada: Add SPI NOR flash power supply
arm64: dts: mediatek: mt8192-asurada: Fix WiFi regulator description
arm64: dts: mediatek: mt8195-cherry: Add MT6359 PMIC supplies
arm64: dts: mediatek: mt8195-cherry: Add MT6315 PMIC supplies
arm64: dts: mediatek: mt8195-cherry: Add supplies for ChromeOS EC regulators
arm64: dts: mediatek: mt8195-cherry: Fix VBUS regulator description
arm64: dts: mediatek: mt8195-cherry: Add supply for SPI NOR flash
arm64: dts: mediatek: mt8195-cherry: Add vusb33 supplies for XHCI controllers
arm64: dts: mediatek: mt8188-geralt: Add MT6359 PMIC supplies
arm64: dts: mediatek: mt8188-geralt: Add little core CPU power supplies
arm64: dts: mediatek: mt8192-asurada: Fix SPI-NOR flash compatible
arm64: dts: mediatek: mt8192-asurada: Add (BT|WIFI)_KILL_1V8_L GPIO line names
arm64: dts: mediatek: mt8195-cherry: Fix names for EC controlled regulators
arm64: dts: mediatek: mt8195-cherry: Sort top level nodes correctly
Frank Wunderlich (4):
arm64: dts: mediatek: mt7988a-bpi-r4pro: rename mgmt port to lan5
arm64: dts: mediatek: mt7988a-bpi-r4pro: drop duplicate fan properties
arm64: dts: mediatek: mt7988a-bpi-r4pro: update gpio-leds
arm64: dts: mediatek: mt7988a-bpi-r4pro: rework pcie gpio-hog handling
Gary Bisson (1):
arm64: dts: mediatek: mt8390-tungsten-smarc: add HDMI support
Louis-Alexis Eyraud (9):
arm64: dts: mediatek: add MT6365 PMIC include
arm64: dts: mediatek: mt8390-genio-common: use MT6365 PMIC definitions
arm64: dts: mediatek: mt8395-genio-common: use MT6365 PMIC definitions
arm64: dts: mediatek: mt8395-radxa-nio-12l: use MT6365 PMIC definitions
arm64: dts: mediatek: mt8390-genio-common: add MT6319 PMIC support
arm64: dts: mediatek: mt8390-genio-common: add CPU power supplies
arm64: dts: mediatek: mt8390-genio-700-evk: add specific CPU power supplies
arm64: dts: mediatek: mt8395-genio-common: add MT6315 PMIC supplies
arm64: dts: mediatek: mt8395-genio-common: add MT6360 PMIC supplies
arch/arm64/boot/dts/mediatek/Makefile | 8 +++++
arch/arm64/boot/dts/mediatek/mt6359.dtsi | 22 +++-----------
arch/arm64/boot/dts/mediatek/mt6365.dtsi | 26 ++++++++++++++++
arch/arm64/boot/dts/mediatek/mt7981b-xiaomi-ax3000t.dts | 36 ++++++++++++++++++++++
arch/arm64/boot/dts/mediatek/mt7981b.dtsi | 15 ++++++++++
arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso | 20 +++++++++++++
arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso | 20 +++++++++++++
arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro.dtsi | 15 ++--------
arch/arm64/boot/dts/mediatek/mt8186-corsola-voltorb.dtsi | 4 +++
arch/arm64/boot/dts/mediatek/mt8188-geralt.dtsi | 41 ++++++++++++++++++++++++++
arch/arm64/boot/dts/mediatek/mt8192-asurada.dtsi | 82 +++++++++++++++++++++++++++++++++++++++++++++++----
arch/arm64/boot/dts/mediatek/mt8195-cherry.dtsi | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
arch/arm64/boot/dts/mediatek/mt8390-genio-700-evk.dts | 7 +++++
arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi | 80 +++++++++++++++++++++++++++++++++++++++++++++----
arch/arm64/boot/dts/mediatek/mt8390-grinn-genio-som.dtsi | 2 +-
arch/arm64/boot/dts/mediatek/mt8390-tungsten-smarc.dtsi | 122 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi | 46 ++++++++++++++++++++++++-----
arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l-8-hd-panel.dtso | 4 +--
arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 8 ++---
19 files changed, 595 insertions(+), 117 deletions(-)
create mode 100644 arch/arm64/boot/dts/mediatek/mt6365.dtsi
create mode 100644 arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn13.dtso
create mode 100644 arch/arm64/boot/dts/mediatek/mt7988a-bananapi-bpi-r4-pro-cn14.dtso
^ permalink raw reply
* [GIT,PULL,2/3] MediaTek SoC driver updates for v7.2
From: AngeloGioacchino Del Regno @ 2026-06-01 9:12 UTC (permalink / raw)
To: arm-soc, soc; +Cc: linux-arm-kernel, linux-mediatek, matthias.bgg, matthias.bgg
In-Reply-To: <20260601091225.5223-1-angelogioacchino.delregno@collabora.com>
The following changes since commit 254f49634ee16a731174d2ae34bc50bd5f45e731:
Linux 7.1-rc1 (2026-04-26 14:19:00 -0700)
are available in the Git repository at:
https://git.kernel.org/pub/scm/linux/kernel/git/mediatek/linux.git/ tags/mtk-soc-for-v7.2
for you to fetch changes up to 7d462de9f65b002b439b1b168bf3b5579b0de48b:
soc: mediatek: mtk-mmsys: Restore MT8167 routing masks lost during merge (2026-05-11 11:20:48 +0200)
----------------------------------------------------------------
MediaTek SoC driver updates
This adds subsys ID compatibility in MediaTek CMDQ, paving
the way for adding support for the MT8196 SoC, and fixes
the Multimedia System (MMSYS) routing masks for the MT8167
SoC.
----------------------------------------------------------------
Jason-JH Lin (2):
soc: mediatek: Use pkt_write function pointer for subsys ID compatibility
soc: mediatek: mtk-cmdq: Add cmdq_pkt_jump_rel_temp() for removing shift_pa
Luca Leonardo Scorcia (1):
soc: mediatek: mtk-mmsys: Restore MT8167 routing masks lost during merge
drivers/soc/mediatek/mt8167-mmsys.h | 11 ++++++++---
drivers/soc/mediatek/mtk-mmsys.c | 8 +++++---
drivers/soc/mediatek/mtk-mutex.c | 5 +++--
include/linux/soc/mediatek/mtk-cmdq.h | 24 ++++++++++++++++++++++++
4 files changed, 40 insertions(+), 8 deletions(-)
^ permalink raw reply
* Possible UaF bug in netdevice teardown path
From: Florian Westphal @ 2026-06-01 9:13 UTC (permalink / raw)
To: Lorenzo Bianconi, Tony Nguyen, Przemek Kitszel, Felix Fietkau,
Saeed Mahameed, Leon Romanovsky, Tariq Toukan, Mark Bloch
Cc: netdev, linux-mediatek, intel-wired-lan
Another sashiko drive-by report. TL;DR, do you need to apply this
pattern in your driver?
- metadata_dst_free(priv->md);
+ dst_release(&priv->md->dst);
Affects:
drivers/net/ethernet/airoha/airoha_eth.c
drivers/net/ethernet/intel/ice/ice_eswitch.c
drivers/net/ethernet/mediatek/mtk_eth_soc.c
drivers/net/ethernet/mellanox/mlx5/core/en_accel/macsec.c
Long version:
https://sashiko.dev/#/patchset/20260527135751.1031891-1-tristmd%40gmail.com
This isn't a bug introduced by this patch, but looking at this fix, do
other callers of metadata_dst_free() suffer from the same use-after-free
vulnerability?
In drivers like ice_eswitch and mlx5 MACsec, a metadata_dst is allocated
and references are taken on it via dst_hold() when packets are processed
(for example, via skb_dst_set()).
However, on their teardown paths, these drivers call metadata_dst_free(),
which unconditionally frees the memory without checking the reference count.
If packets holding these references are queued (like in a netem qdisc)
during teardown, does the memory get freed prematurely, causing a
use-after-free when the networking stack eventually calls dst_release()
on the dequeued packets?
^ permalink raw reply
* Re: [PATCH v3] wifi: mt76: mt792x: fix use-after-free in mt76_rx_poll_complete
From: Felix Fietkau @ 2026-06-01 11:12 UTC (permalink / raw)
To: JB Tsai, lorenzo
Cc: linux-wireless, linux-mediatek, Deren.Wu, Sean.Wang, Quan.Zhou,
Ryder.Lee, Leon.Yen, litien.chang, eason.lai
In-Reply-To: <20260506084315.3143553-1-jb.tsai@mediatek.com>
On 06.05.26 10:43, JB Tsai wrote:
> From: Eason Lai <Eason.Lai@mediatek.com>
>
> A use-after-free issue occurs in mt76_rx_poll_complete due to a race
> condition. The STA has already been removed, but the rx_status still
> had a pointer to the wcid in the STA.
>
> Use wcid_idx instead of storing the wcid pointer, and look up the wcid
> via rcu_dereference() by wcid_idx.
Unless I'm misreading something, it seems to me that this patch papers
over a different bug instead of fixing the root cause.
Right now the rx processing code relies on RCU to protect the wcid and
sta data structures.
The rcu lock/unlock around polling also seems correct to me.
Are the freed wcid pointers maybe related to a vif sta instead of an
actual station? The use of devm_kfree in mt7925_change_vif_links looks
suspicious to me.
Please let me know if I'm missing something here.
- Felix
^ permalink raw reply
* RE: [PATCH 01/11] net: wwan: t9xx: Add PCIe core
From: Jagielski, Jedrzej @ 2026-06-01 11:18 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-1-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>Registers the T900 device driver with the kernel. Set up all
>the fundamental configurations for the device: PCIe layer,
>Modem Host Cross Core Interface (MHCCIF), Reset Generation
>Unit (RGU), modem common control operations and build
>infrastructure.
>
>* PCIe layer code implements driver probe and removal, MSI-X
> interrupt initialization and de-initialization, and the way
> of resetting the device.
>* MHCCIF provides interrupt channels to communicate events
> such as handshake, PM and port enumeration.
>* RGU provides interrupt channels to generate notifications
> from the device so that the T900 driver could get the
> device reset.
>* Modem common control operations provide the basic read/write
> functions of the device's hardware registers,
> mask/unmask/get/clear functions of the device's interrupt
> registers and inquiry functions of the device's status.
>
>Signed-off-by: Jack Wu <jackbb_wu@compal.com>
>---
> drivers/net/wwan/Kconfig | 12 +
> drivers/net/wwan/Makefile | 1 +
> drivers/net/wwan/t9xx/Makefile | 10 +
> drivers/net/wwan/t9xx/mtk_dev.h | 108 +++
> drivers/net/wwan/t9xx/pcie/mtk_pci.c | 926 ++++++++++++++++++++++++++
> drivers/net/wwan/t9xx/pcie/mtk_pci.h | 219 ++++++
> drivers/net/wwan/t9xx/pcie/mtk_pci_drv_m9xx.c | 69 ++
> drivers/net/wwan/t9xx/pcie/mtk_pci_reg.h | 71 ++
> 8 files changed, 1416 insertions(+)
>
>diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
>index 88df55d78d90..4cee537c739f 100644
>--- a/drivers/net/wwan/Kconfig
>+++ b/drivers/net/wwan/Kconfig
>@@ -121,6 +121,18 @@ config MTK_T7XX
>
> If unsure, say N.
>
>+config MTK_T9XX
>+ tristate "MediaTek PCIe 5G WWAN modem T9xx device"
>+ depends on PCI
>+ select NET_DEVLINK
>+ help
>+ Enables MediaTek PCIe based 5G WWAN modem (T9xx series) device.
>+
>+ To compile this driver as a module, choose M here: the module will be
>+ called mtk_t9xx.
>+
>+ If unsure, say N.
>+
> endif # WWAN
>
> endmenu
>diff --git a/drivers/net/wwan/Makefile b/drivers/net/wwan/Makefile
>index 3960c0ae2445..7361eef4c472 100644
>--- a/drivers/net/wwan/Makefile
>+++ b/drivers/net/wwan/Makefile
>@@ -14,3 +14,4 @@ obj-$(CONFIG_QCOM_BAM_DMUX) += qcom_bam_dmux.o
> obj-$(CONFIG_RPMSG_WWAN_CTRL) += rpmsg_wwan_ctrl.o
> obj-$(CONFIG_IOSM) += iosm/
> obj-$(CONFIG_MTK_T7XX) += t7xx/
>+obj-$(CONFIG_MTK_T9XX) += t9xx/
>diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
>new file mode 100644
>index 000000000000..6f2dd3f91454
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/Makefile
>@@ -0,0 +1,10 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+
>+ccflags-y += -I$(src)/pcie
>+ccflags-y += -I$(src)
>+
>+obj-$(CONFIG_MTK_T9XX) += mtk_t9xx.o
>+
>+mtk_t9xx-y := \
>+ pcie/mtk_pci.o \
>+ pcie/mtk_pci_drv_m9xx.o
>diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
>new file mode 100644
>index 000000000000..8278a0e2875e
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_dev.h
>@@ -0,0 +1,108 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_DEV_H__
>+#define __MTK_DEV_H__
>+
>+#include <linux/dma-mapping.h>
>+#include <linux/dmapool.h>
>+#include <linux/kernel.h>
>+#include <linux/module.h>
>+#include <linux/sched.h>
>+#include <linux/slab.h>
>+#include <linux/spinlock.h>
>+
>+#define MTK_DEV_STR_LEN 16
>+
>+enum mtk_user_id {
>+ MTK_USER_MIN,
>+ MTK_USER_CTRL,
>+ MTK_USER_DATA,
>+ MTK_USER_MAX
>+};
>+
>+enum mtk_dev_evt_h2d {
>+ DEV_EVT_H2D_DEVICE_RESET = BIT(2),
>+ DEV_EVT_H2D_MAX = BIT(5)
>+};
>+
>+enum mtk_dev_evt_d2h {
>+ DEV_EVT_D2H_BOOT_FLOW_SYNC = BIT(4),
>+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP = BIT(5),
>+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD = BIT(6),
>+ DEV_EVT_D2H_MAX = BIT(11)
>+};
>+
>+struct mtk_md_dev;
>+
>+struct mtk_dev_ops {
>+ u32 (*get_dev_state)(struct mtk_md_dev *mdev);
>+ void (*ack_dev_state)(struct mtk_md_dev *mdev, u32 state);
>+ u32 (*get_dev_cfg)(struct mtk_md_dev *mdev);
>+ int (*register_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt,
>+ int (*evt_cb)(u32 status, void *data), void *data);
>+ void (*unregister_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
>+ void (*mask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
>+ void (*unmask_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
>+ void (*clear_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
>+ int (*send_dev_evt)(struct mtk_md_dev *mdev, u32 dev_evt);
>+};
>+
>+/* mtk_md_dev defines the structure of MTK modem device */
>+struct mtk_md_dev {
>+ struct device *dev;
>+ const struct mtk_dev_ops *dev_ops;
>+ void *hw_priv;
>+ u32 hw_ver;
>+ char dev_str[MTK_DEV_STR_LEN];
>+};
>+
>+static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
>+{
>+ return mdev->dev_ops->get_dev_state(mdev);
>+}
>+
>+static inline void mtk_dev_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
>+{
>+ return mdev->dev_ops->ack_dev_state(mdev, state);
>+}
>+
>+static inline u32 mtk_dev_get_dev_cfg(struct mtk_md_dev *mdev)
>+{
>+ return mdev->dev_ops->get_dev_cfg(mdev);
>+}
>+
>+static inline int mtk_dev_register_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt,
>+ int (*evt_cb)(u32 status, void *data), void *data)
>+{
>+ return mdev->dev_ops->register_dev_evt(mdev, dev_evt, evt_cb, data);
>+}
>+
>+static inline void mtk_dev_unregister_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
>+{
>+ mdev->dev_ops->unregister_dev_evt(mdev, dev_evt);
>+}
>+
>+static inline void mtk_dev_mask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
>+{
>+ mdev->dev_ops->mask_dev_evt(mdev, dev_evt);
>+}
>+
>+static inline void mtk_dev_unmask_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
>+{
>+ mdev->dev_ops->unmask_dev_evt(mdev, dev_evt);
>+}
>+
>+static inline void mtk_dev_clear_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
>+{
>+ mdev->dev_ops->clear_dev_evt(mdev, dev_evt);
>+}
>+
>+static inline int mtk_dev_send_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
>+{
>+ return mdev->dev_ops->send_dev_evt(mdev, dev_evt);
>+}
>+
>+#endif /* __MTK_DEV_H__ */
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>new file mode 100644
>index 000000000000..adec3ccdee08
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>@@ -0,0 +1,926 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include <linux/acpi.h>
>+#include <linux/aer.h>
>+#include <linux/bitfield.h>
>+#include <linux/debugfs.h>
>+#include <linux/delay.h>
>+#include <linux/device.h>
>+#include <linux/dma-mapping.h>
>+#include <linux/kernel.h>
>+#include <linux/module.h>
>+
>+#include "mtk_dev.h"
>+#include "mtk_pci.h"
>+#include "mtk_pci_reg.h"
>+
>+#define BAR_NUM 6
please add driver prefix
>+#define MTK_PCI_TRANSPARENT_ATR_SIZE (0x3F)
>+#define MTK_PCI_MINIMUM_ATR_SIZE (0x1000)
>+#define LE32_TO_U32(x) ((__force u32)(__le32)(x))
>+#define SET_HW_BITS(dest, chs, mhccif, dev) \
>+ ({ \
>+ if ((chs) & (dev))
what if any of these is equal to 0?
just skip do not log anything?
>+ (dest) |= FIELD_PREP(mhccif, 1); \
>+ })
>+
>+extern const struct mtk_pci_dev_cfg mtk_dev_cfg_0900;
>+
>+struct mtk_mhccif_cb {
>+ struct list_head entry;
>+ int (*evt_cb)(u32 status, void *data);
>+ void *data;
>+ u32 chs;
>+};
>+
>+u32 mtk_pci_mac_read32(struct mtk_pci_priv *priv, u64 addr)
>+{
>+ return ioread32(priv->mac_reg_base + addr);
>+}
>+
>+void mtk_pci_mac_write32(struct mtk_pci_priv *priv, u64 addr, u32 val)
>+{
>+ iowrite32(val, priv->mac_reg_base + addr);
>+}
>+
>+u32 mtk_pci_read32(struct mtk_md_dev *mdev, u64 addr)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ return ioread32(priv->ext_reg_base + addr);
>+}
>+
>+void mtk_pci_write32(struct mtk_md_dev *mdev, u64 addr, u32 val)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ iowrite32(val, priv->ext_reg_base + addr);
>+}
>+
would be lovely to have kdoc of the non-static functions from the series
>+int mtk_pci_setup_atr(struct mtk_md_dev *mdev, struct mtk_atr_cfg *cfg)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 addr, val, size_h, size_l;
>+ int atr_size, pos, offset;
>+
>+ if (cfg->transparent) {
>+ atr_size = MTK_PCI_TRANSPARENT_ATR_SIZE; /* No address conversion is performed */
>+ } else {
>+ if (cfg->size < MTK_PCI_MINIMUM_ATR_SIZE)
>+ cfg->size = MTK_PCI_MINIMUM_ATR_SIZE;
>+
>+ if (cfg->src_addr & (cfg->size - 1)) {
>+ dev_err((mdev)->dev, "Invalid atr src addr is not aligned to size\n");
>+ return -EFAULT;
>+ }
>+ if (cfg->trsl_addr & (cfg->size - 1)) {
>+ dev_err((mdev)->dev,
>+ "Invalid atr trsl addr is not aligned to size, %llx, %llx\n",
>+ cfg->trsl_addr, cfg->size - 1);
>+ return -EFAULT;
>+ }
>+ size_l = FIELD_GET(GENMASK_ULL(31, 0), cfg->size);
>+ size_h = FIELD_GET(GENMASK_ULL(63, 32), cfg->size);
>+ pos = ffs(size_l);
>+ if (pos) {
>+ atr_size = pos - 2;
>+ } else {
>+ pos = ffs(size_h);
>+ atr_size = pos + 30;
i believe better would be to have some defines instead of magic
>+ }
please put some breaks to have the code logically separated
>+ }
>+
>+ /* Calculate table offset */
>+ offset = ATR_PORT_OFFSET * cfg->port + ATR_TABLE_OFFSET * cfg->table;
>+ /* SRC_ADDR_H */
>+ addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_MSB + offset;
>+ val = (u32)(cfg->src_addr >> 32);
>+ mtk_pci_mac_write32(priv, addr, val);
>+ /* SRC_ADDR_L */
>+ addr = REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset;
>+ val = (u32)(cfg->src_addr & 0xFFFFF000) | (atr_size << 1) | 0x1;
>+ mtk_pci_mac_write32(priv, addr, val);
>+
>+ /* TRSL_ADDR_H */
>+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_MSB + offset;
>+ val = (u32)(cfg->trsl_addr >> 32);
>+ mtk_pci_mac_write32(priv, addr, val);
>+ /* TRSL_ADDR_L */
>+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_ADDR_LSB + offset;
>+ val = (u32)(cfg->trsl_addr & 0xFFFFF000);
>+ mtk_pci_mac_write32(priv, addr, val);
comments seem to be redundant imo; clearer would be to have just newline
instead
>+
>+ /* TRSL_PARAM */
>+ addr = REG_ATR_PCIE_WIN0_T0_TRSL_PARAM + offset;
>+ val = (cfg->trsl_param << 16) | cfg->trsl_id;
>+ mtk_pci_mac_write32(priv, addr, val);
again a lot of magic here
>+
>+ return 0;
>+}
>+
>+void mtk_pci_atr_disable(struct mtk_pci_priv *priv)
>+{
>+ int port, tbl, offset;
>+ u32 val;
>+
>+ /* Disable all ATR table for all ports */
>+ for (port = ATR_SRC_PCI_WIN0; port <= ATR_SRC_AXIS_3; port++)
>+ for (tbl = 0; tbl < ATR_TABLE_NUM_PER_ATR; tbl++) {
>+ /* Calculate table offset */
>+ offset = ATR_PORT_OFFSET * port + ATR_TABLE_OFFSET * tbl;
>+ val = mtk_pci_mac_read32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset);
>+ val = val & (~BIT(0));
>+ /* Disable table by SRC_ADDR_L */
>+ mtk_pci_mac_write32(priv, REG_ATR_PCIE_WIN0_T0_SRC_ADDR_LSB + offset, val);
>+ }
>+}
>+
>+static void mtk_pci_set_msix_merged(struct mtk_pci_priv *priv, int irq_cnt)
>+{
>+ mtk_pci_mac_write32(priv, REG_PCIE_CFG_MSIX, ffs(irq_cnt) * 2 - 1);
>+}
>+
>+u32 mtk_pci_get_dev_state(struct mtk_md_dev *mdev)
>+{
>+ return mtk_pci_mac_read32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7);
>+}
>+
>+void mtk_pci_ack_dev_state(struct mtk_md_dev *mdev, u32 state)
>+{
>+ mtk_pci_mac_write32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_7, state);
>+}
>+
>+int mtk_pci_get_irq_id(struct mtk_md_dev *mdev, enum mtk_irq_src irq_src)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ const int *irq_tbl = priv->cfg->irq_tbl;
>+ int irq_id = -EINVAL;
>+
>+ if (irq_src > MTK_IRQ_SRC_MIN && irq_src < MTK_IRQ_SRC_MAX) {
>+ irq_id = irq_tbl[irq_src];
>+ if (unlikely(irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX))
>+ irq_id = -EINVAL;
>+ }
>+
>+ return irq_id;
>+}
>+
>+int mtk_pci_get_virq_id(struct mtk_md_dev *mdev, int irq_id)
>+{
>+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ int nr = 0;
what's the point of zeroiniting if the value is assigned at
the next line?
>+
>+ nr = irq_id % priv->irq_cnt;
are we sure irq_cnt won't be equal to 0 in any scenario?
>+
>+ return pci_irq_vector(pdev, nr);
>+}
>+
>+int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,
>+ int (*irq_cb)(int irq_id, void *data), void *data)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ if (unlikely((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || !irq_cb))
>+ return -EINVAL;
>+
>+ if (priv->irq_cb_list[irq_id]) {
>+ dev_err((mdev)->dev,
>+ "Unable to register irq, irq_id=%d, it's already been register by %ps.\n",
>+ irq_id, priv->irq_cb_list[irq_id]);
>+ return -EFAULT;
>+ }
>+ priv->irq_cb_list[irq_id] = irq_cb;
>+ priv->irq_cb_data[irq_id] = data;
>+
>+ return 0;
>+}
>+
>+int mtk_pci_unregister_irq(struct mtk_md_dev *mdev, int irq_id)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ if (unlikely(irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX))
>+ return -EINVAL;
is it anyhow beneficial to put unlikely here and in case of other
appearances within the series?
>+
>+ if (!priv->irq_cb_list[irq_id]) {
>+ dev_err((mdev)->dev, "irq_id=%d has not been registered\n", irq_id);
>+ return -EFAULT;
>+ }
>+ priv->irq_cb_list[irq_id] = NULL;
>+ priv->irq_cb_data[irq_id] = NULL;
>+
>+ return 0;
>+}
>+
>+int mtk_pci_mask_irq(struct mtk_md_dev *mdev, int irq_id)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ if (unlikely((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || priv->irq_type != PCI_IRQ_MSIX)) {
same here
>+ dev_err(mdev->dev, "Failed to mask irq: input irq_id=%d\n", irq_id);
>+ return -EINVAL;
>+ }
>+
>+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, BIT(irq_id));
>+
>+ return 0;
>+}
>+
>+int mtk_pci_unmask_irq(struct mtk_md_dev *mdev, int irq_id)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ if (unlikely((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || priv->irq_type != PCI_IRQ_MSIX)) {
>+ dev_err(mdev->dev, "Failed to unmask irq: input irq_id=%d\n", irq_id);
>+ return -EINVAL;
>+ }
>+
>+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_SET_GRP0_0, BIT(irq_id));
>+
>+ return 0;
>+}
>+
>+int mtk_pci_clear_irq(struct mtk_md_dev *mdev, int irq_id)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ if (unlikely((irq_id < 0 || irq_id >= MTK_IRQ_CNT_MAX) || priv->irq_type != PCI_IRQ_MSIX)) {
>+ dev_err(mdev->dev, "Failed to clear irq: input irq_id=%d\n", irq_id);
>+ return -EINVAL;
>+ }
>+
>+ mtk_pci_mac_write32(priv, REG_MSIX_ISTATUS_HOST_GRP0_0, BIT(irq_id));
>+
>+ return 0;
>+}
>+
>+static u32 mtk_pci_ext_d2h_evt_hw_bits(u32 chs)
>+{
>+ u32 hw_bits = 0;
>+
>+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC,
>+ DEV_EVT_D2H_BOOT_FLOW_SYNC);
>+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP,
>+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP);
>+ SET_HW_BITS(hw_bits, chs, MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD,
>+ DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD);
>+
>+ return LE32_TO_U32(cpu_to_le32(hw_bits));
>+}
>+
>+static u32 mtk_pci_ext_d2h_evt_chs(u32 hw_bits)
>+{
>+ u32 chs = 0;
>+
>+ if (!hw_bits)
>+ return chs;
>+
>+ chs = FIELD_PREP(DEV_EVT_D2H_BOOT_FLOW_SYNC,
>+ FIELD_GET(MHCCIF_EP2RC_EVT_BOOT_FLOW_SYNC, hw_bits)) |
>+ FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_SAP,
>+ FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_SAP, hw_bits)) |
>+ FIELD_PREP(DEV_EVT_D2H_ASYNC_HS_NOTIFY_MD,
>+ FIELD_GET(MHCCIF_EP2RC_EVT_ASYNC_HS_NOTIFY_MD, hw_bits));
>+
>+ return chs;
>+}
>+
>+int mtk_pci_register_ext_evt(struct mtk_md_dev *mdev, u32 chs,
>+ int (*evt_cb)(u32 status, void *data), void *data)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ struct mtk_mhccif_cb *cb;
>+ int ret = 0;
>+
>+ if (!chs || !evt_cb)
>+ return -EINVAL;
>+
>+ spin_lock_bh(&priv->mhccif_lock);
>+ list_for_each_entry(cb, &priv->mhccif_cb_list, entry) {
>+ if (cb->chs & chs) {
>+ ret = -EFAULT;
>+ dev_err((mdev)->dev,
>+ "Unable to register evt, intersection: chs=0x%08x&0x%08x registered_cb=%ps\n",
>+ chs, cb->chs, cb->evt_cb);
>+ goto err_spin_unlock;
>+ }
>+ }
>+ cb = devm_kzalloc(mdev->dev, sizeof(*cb), GFP_ATOMIC);
>+ if (!cb) {
>+ ret = -ENOMEM;
>+ goto err_spin_unlock;
>+ }
>+ cb->evt_cb = evt_cb;
>+ cb->data = data;
>+ cb->chs = chs;
>+ list_add_tail(&cb->entry, &priv->mhccif_cb_list);
>+err_spin_unlock:
>+ spin_unlock_bh(&priv->mhccif_lock);
>+
>+ return ret;
>+}
>+
>+void mtk_pci_unregister_ext_evt(struct mtk_md_dev *mdev, u32 chs)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ struct mtk_mhccif_cb *cb, *next;
>+
>+ if (!chs)
>+ return;
>+
>+ spin_lock_bh(&priv->mhccif_lock);
>+ list_for_each_entry_safe(cb, next, &priv->mhccif_cb_list, entry) {
>+ if (cb->chs == chs) {
>+ list_del(&cb->entry);
>+ devm_kfree(mdev->dev, cb);
>+ goto out;
>+ }
>+ }
>+ dev_warn((mdev)->dev,
>+ "Unable to unregister evt, no chs=0x%08x has been registered.\n", chs);
>+out:
>+ spin_unlock_bh(&priv->mhccif_lock);
>+}
>+
>+void mtk_pci_mask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 hw_bits;
>+
>+ hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
one of these is inited at declaration, 2nd one isnt
please stay consistant, @hw_bits can be inited as well
>+
>+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
>+ MHCCIF_EP2RC_SW_INT_EAP_MASK_SET, hw_bits);
>+}
>+
>+void mtk_pci_unmask_ext_evt(struct mtk_md_dev *mdev, u32 chs)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 hw_bits;
>+
>+ hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
>+
>+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
>+ MHCCIF_EP2RC_SW_INT_EAP_MASK_CLR, hw_bits);
>+}
>+
>+void mtk_pci_clear_ext_evt(struct mtk_md_dev *mdev, u32 chs)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 hw_bits;
>+
>+ hw_bits = mtk_pci_ext_d2h_evt_hw_bits(chs);
>+
>+ mtk_pci_write32(mdev, priv->cfg->mhccif_rc_base_addr +
>+ MHCCIF_EP2RC_SW_INT_ACK, hw_bits);
>+}
>+
>+static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
>+{
>+ u32 hw_bits = 0;
>+
>+ SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
>+ DEV_EVT_H2D_DEVICE_RESET);
>+ SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DRM_DISABLE_AP,
>+ EXT_EVT_H2D_DRM_DISABLE_AP);
>+ return LE32_TO_U32(cpu_to_le32(hw_bits));
>+}
>+
missing kdoc here and there
>+int mtk_pci_send_ext_evt(struct mtk_md_dev *mdev, u32 ch)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 rc_base;
>+ u32 hw_bits;
please squash variables of the same type into single line
>+
>+ rc_base = priv->cfg->mhccif_rc_base_addr;
>+
>+ /* Only allow one ch to be triggered at a time */
>+ if (!is_power_of_2(ch)) {
>+ dev_err((mdev)->dev, "Unsupported ext evt ch=0x%08x\n", ch);
>+ return -EINVAL;
>+ }
>+
>+ hw_bits = mtk_pci_ext_h2d_evt_hw_bits(ch);
>+ mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_BSY, hw_bits);
>+ mtk_pci_write32(mdev, rc_base + MHCCIF_RC2EP_SW_TCHNUM, ffs(hw_bits) - 1);
>+ return 0;
>+}
>+
>+static u32 mtk_pci_get_ext_evt_hw_status(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ return mtk_pci_read32(mdev, priv->cfg->mhccif_rc_base_addr + MHCCIF_EP2RC_SW_INT_STS);
>+}
>+
>+int mtk_pci_fldr(struct mtk_md_dev *mdev)
>+{
>+#ifdef CONFIG_ACPI
>+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
>+ acpi_status acpi_ret;
>+ acpi_handle handle;
>+
>+ if (acpi_disabled) {
>+ dev_err((mdev)->dev, "Unsupported, acpi function isn't enable\n");
>+ return -ENODEV;
>+ }
>+ handle = ACPI_HANDLE(mdev->dev);
>+ if (!handle) {
>+ dev_err((mdev)->dev, "Unsupported, acpi handle isn't found\n");
>+ return -ENODEV;
>+ }
>+ if (!acpi_has_method(handle, "_RST")) {
>+ dev_err((mdev)->dev, "Unsupported, _RST method isn't found\n");
>+ return -ENODEV;
>+ }
>+ acpi_ret = acpi_evaluate_object(handle, "_RST", NULL, &buffer);
>+ if (ACPI_FAILURE(acpi_ret)) {
>+ dev_err((mdev)->dev, "Failed to execute _RST method: %s\n",
>+ acpi_format_exception(acpi_ret));
>+ return -EFAULT;
>+ }
>+ acpi_os_free(buffer.pointer);
>+
>+ return 0;
>+#else
#else /* !CONFIG_ACPI */
>+ dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");
>+
>+ return -ENODEV;
>+#endif
#endif /* CONFIG_ACPI */
>+}
>+
>+int mtk_pci_pldr(struct mtk_md_dev *mdev)
>+{
>+#ifdef CONFIG_ACPI
>+ struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
>+ struct pci_dev *bridge;
>+ acpi_status acpi_ret;
>+ acpi_handle handle;
>+
>+ if (acpi_disabled) {
>+ dev_err((mdev)->dev, "Unsupported, acpi function isn't enable\n");
>+ return -ENODEV;
>+ }
>+
>+ bridge = pci_upstream_bridge(to_pci_dev(mdev->dev));
>+ if (!bridge) {
>+ dev_err((mdev)->dev, "Unable to find bridge\n");
>+ return -ENODEV;
>+ }
>+
>+ handle = ACPI_HANDLE(&bridge->dev);
>+ if (!handle) {
>+ dev_err((mdev)->dev, "Unsupported, acpi handle isn't found\n");
>+ return -ENODEV;
>+ }
>+ if (!acpi_has_method(handle, "PXP._OFF") ||
>+ !acpi_has_method(handle, "PXP._ON")) {
>+ dev_err((mdev)->dev, "Unsupported, pldr method isn't supported\n");
>+ return -ENODEV;
>+ }
>+ acpi_ret = acpi_evaluate_object(handle, "PXP._OFF", NULL, &buffer);
>+ if (ACPI_FAILURE(acpi_ret)) {
>+ dev_err((mdev)->dev, "Failed to execute _OFF method: %s\n",
>+ acpi_format_exception(acpi_ret));
>+ return -EFAULT;
>+ }
>+ msleep(500);
please dont use magic number
also where this value has been derived from?
>+ acpi_ret = acpi_evaluate_object(handle, "PXP._ON", NULL, &buffer);
>+ if (ACPI_FAILURE(acpi_ret)) {
>+ dev_err((mdev)->dev, "Failed to execute _ON method: %s\n",
>+ acpi_format_exception(acpi_ret));
>+ return -EFAULT;
>+ }
>+ acpi_os_free(buffer.pointer);
pleae add some newlines
>+
>+ return 0;
>+#else
>+ dev_err((mdev)->dev, "Unsupported, CONFIG ACPI hasn't been set to 'y'\n");
>+
>+ return -ENODEV;
>+#endif
>+}
>+
>+u32 mtk_pci_get_dev_cfg(struct mtk_md_dev *mdev)
>+{
>+ u32 val;
>+
>+ val = mtk_pci_mac_read32(mdev->hw_priv, REG_PCIE_DEBUG_DUMMY_4);
>+ return (val >> MTK_CFG_INFO_BIT_SHIFT);
>+}
>+
>+static int mtk_pci_dev_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type)
>+{
>+ switch (type) {
>+ case RESET_MHCCIF:
>+ return mtk_pci_send_ext_evt(mdev, DEV_EVT_H2D_DEVICE_RESET);
>+ case RESET_FLDR:
>+ return mtk_pci_fldr(mdev);
>+ case RESET_PLDR:
>+ return mtk_pci_pldr(mdev);
>+ default:
>+ break;
>+ }
>+
>+ return -EINVAL;
please put return into default label
>+}
>+
>+int mtk_pci_reset(struct mtk_md_dev *mdev, enum mtk_reset_type type)
>+{
>+ return mtk_pci_dev_reset(mdev, type);
>+}
>+
>+bool mtk_pci_link_check(struct mtk_md_dev *mdev)
>+{
>+ return pci_device_is_present(to_pci_dev(mdev->dev));
>+}
>+
>+static void mtk_mhccif_isr_work(struct work_struct *work)
>+{
>+ struct mtk_pci_priv *priv = container_of(work, struct mtk_pci_priv, mhccif_work);
isn't this line > 80 chars?
>+ struct mtk_md_dev *mdev = priv->irq_desc->mdev;
>+ struct mtk_mhccif_cb *cb;
>+ u32 stat, mask, chs;
>+
>+ stat = mtk_pci_get_ext_evt_hw_status(mdev);
>+ mask = mtk_pci_read32(mdev, priv->cfg->mhccif_rc_base_addr
>+ + MHCCIF_EP2RC_SW_INT_EAP_MASK);
>+ if (unlikely(stat == U32_MAX && !(mtk_pci_link_check(mdev)))) {
>+ /* When link failed, we don't need to unmask/clear. */
>+ dev_err((mdev)->dev, "Failed to check link in MHCCIF handler.\n");
>+ return;
>+ }
>+
>+ stat &= ~mask;
>+ chs = mtk_pci_ext_d2h_evt_chs(stat);
>+ spin_lock_bh(&priv->mhccif_lock);
>+ list_for_each_entry(cb, &priv->mhccif_cb_list, entry) {
>+ if (cb->chs & chs)
>+ cb->evt_cb(cb->chs & chs, cb->data);
>+ }
>+ spin_unlock_bh(&priv->mhccif_lock);
>+
>+ mtk_pci_clear_irq(mdev, priv->mhccif_irq_id);
>+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
>+}
>+
>+static const struct pci_device_id t9xx_pci_table[] = {
>+ MTK_PCI_DEV_CFG(0x0900, mtk_dev_cfg_0900),
>+ CEI_PCI_DEV_CFG(0x01CA, mtk_dev_cfg_0900),
>+ {/* end: all zeroes */}
>+};
>+
>+MODULE_DEVICE_TABLE(pci, t9xx_pci_table);
>+
>+static int mtk_pci_bar_init(struct mtk_md_dev *mdev)
>+{
>+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ u32 bar[BAR_NUM];
>+ int i, ret;
>+
>+ for (i = 0; i < BAR_NUM; i++)
>+ pci_read_config_dword(to_pci_dev(mdev->dev),
>+ PCI_BASE_ADDRESS_0 + (i << 2), bar + i);
>+
>+ ret = pcim_iomap_regions(pdev, MTK_REQUESTED_BARS, mdev->dev_str);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to init MMIO. ret=%d\n", ret);
>+ return ret;
>+ }
>+
>+ /* get ioremapped memory */
>+ priv->mac_reg_base = pcim_iomap_table(pdev)[MTK_BAR_0_1_IDX];
>+ priv->bar23_addr = pcim_iomap_table(pdev)[MTK_BAR_2_3_IDX];
>+ if (!priv->mac_reg_base || !priv->bar23_addr) {
>+ dev_err((mdev)->dev, "Failed to init BAR.\n");
>+ return -EINVAL;
>+ }
>+ /* We use MD view base address "0" to observe registers */
>+ priv->ext_reg_base = priv->bar23_addr - ATR_PCIE_REG_TRSL_ADDR;
>+
>+ return 0;
>+}
>+
>+static void mtk_pci_bar_exit(struct mtk_md_dev *mdev)
>+{
>+ pcim_iounmap_region(to_pci_dev(mdev->dev), MTK_REQUESTED_BARS);
>+}
>+
>+static int mtk_mhccif_irq_cb(int irq_id, void *data)
>+{
>+ struct mtk_md_dev *mdev = data;
>+ struct mtk_pci_priv *priv;
>+
>+ priv = mdev->hw_priv;
>+ queue_work(system_highpri_wq, &priv->mhccif_work);
>+
>+ return 0;
>+}
>+
>+static int mtk_mhccif_init(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ int ret;
>+
>+ INIT_LIST_HEAD(&priv->mhccif_cb_list);
>+ spin_lock_init(&priv->mhccif_lock);
>+ INIT_WORK(&priv->mhccif_work, mtk_mhccif_isr_work);
>+
>+ ret = mtk_pci_get_irq_id(mdev, MTK_IRQ_SRC_MHCCIF);
>+ if (ret < 0) {
>+ dev_err((mdev)->dev, "Failed to get mhccif_irq_id. ret=%d\n", ret);
>+ goto err;
why cannot just return ret?
>+ }
>+ priv->mhccif_irq_id = ret;
>+
>+ ret = mtk_pci_register_irq(mdev, priv->mhccif_irq_id, mtk_mhccif_irq_cb, mdev);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to register mhccif_irq callback\n");
>+ goto err;
it's redundant
>+ }
>+
>+err:
>+ return ret;
>+}
>+
>+static void mtk_mhccif_exit(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+
>+ mtk_pci_unregister_irq(mdev, priv->mhccif_irq_id);
>+ cancel_work_sync(&priv->mhccif_work);
>+}
>+
>+static irqreturn_t mtk_pci_irq_handler(struct mtk_md_dev *mdev, u32 irq_state)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ int irq_id;
>+
>+ /* Check whether each set bit has a callback, if has, call it */
>+ do {
>+ irq_id = fls(irq_state) - 1;
are we sure irq_state cannot be 0?
>+ irq_state &= ~BIT(irq_id);
>+ if (likely(priv->irq_cb_list[irq_id]))
>+ priv->irq_cb_list[irq_id](irq_id, priv->irq_cb_data[irq_id]);
>+ else
>+ dev_err((mdev)->dev, "Unhandled irq_id=%d, no callback for it.\n", irq_id);
>+ } while (irq_state);
>+
>+ return IRQ_HANDLED;
>+}
>+
>+static irqreturn_t mtk_pci_irq_msix(int irq, void *data)
>+{
>+ struct mtk_pci_irq_desc *irq_desc = data;
>+ struct mtk_md_dev *mdev = irq_desc->mdev;
>+ struct mtk_pci_priv *priv;
>+ u32 irq_state, irq_enable;
>+
>+ priv = mdev->hw_priv;
>+ irq_state = mtk_pci_mac_read32(priv, REG_MSIX_ISTATUS_HOST_GRP0_0);
>+ irq_enable = mtk_pci_mac_read32(priv, REG_IMASK_HOST_MSIX_GRP0_0);
>+ irq_state &= irq_enable;
>+
>+ if (unlikely(!irq_state) ||
>+ unlikely(!((irq_state & GENMASK(priv->irq_cnt - 1, 0)) & irq_desc->msix_bits)))
>+ return IRQ_NONE;
>+
>+ /* Mask the bit and user needs to unmask by itself */
>+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, irq_state & ~BIT(30));
>+
>+ return mtk_pci_irq_handler(mdev, irq_state);
>+}
>+
>+static int mtk_pci_request_irq_msix(struct mtk_md_dev *mdev, int irq_cnt_allocated)
>+{
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ struct mtk_pci_irq_desc *irq_desc;
>+ struct pci_dev *pdev;
>+ int irq_cnt;
>+ int ret, i;
>+
>+ /* calculate the nearest 2's power number */
>+ irq_cnt = BIT(fls(irq_cnt_allocated) - 1);
>+ pdev = to_pci_dev(mdev->dev);
>+ irq_desc = priv->irq_desc;
>+ for (i = 0; i < irq_cnt; i++) {
>+ irq_desc[i].mdev = mdev;
>+ irq_desc[i].msix_bits = BIT(i);
>+ snprintf(irq_desc[i].name, MTK_IRQ_NAME_LEN, "msix%d-%s", i, mdev->dev_str);
>+ ret = pci_request_irq(pdev, i, mtk_pci_irq_msix, NULL,
>+ &irq_desc[i], irq_desc[i].name);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to request %s: ret=%d\n",
>+ irq_desc[i].name, ret);
>+ for (i--; i >= 0; i--)
>+ pci_free_irq(pdev, i, &irq_desc[i]);
>+ return ret;
>+ }
>+ }
>+ priv->irq_cnt = irq_cnt;
>+ priv->irq_type = PCI_IRQ_MSIX;
>+
>+ if (irq_cnt != MTK_IRQ_CNT_MAX)
>+ mtk_pci_set_msix_merged(priv, irq_cnt);
>+
>+ return 0;
>+}
>+
>+static int mtk_pci_request_irq(struct mtk_md_dev *mdev)
>+{
>+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
>+ int irq_cnt;
>+
>+ irq_cnt = pci_alloc_irq_vectors(pdev, MTK_IRQ_CNT_MIN, MTK_IRQ_CNT_MAX, PCI_IRQ_MSIX);
>+
>+ if (irq_cnt < MTK_IRQ_CNT_MIN) {
>+ dev_err(mdev->dev,
>+ "Unable to alloc pci irq vectors. ret=%d maxirqcnt=%d irqtype=0x%x\n",
>+ irq_cnt, MTK_IRQ_CNT_MAX, PCI_IRQ_MSIX);
>+ return -EFAULT;
>+ }
>+
>+ return mtk_pci_request_irq_msix(mdev, irq_cnt);
>+}
>+
>+static void mtk_pci_free_irq(struct mtk_md_dev *mdev)
>+{
>+ struct pci_dev *pdev = to_pci_dev(mdev->dev);
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ int i;
>+
>+ for (i = 0; i < priv->irq_cnt; i++)
>+ pci_free_irq(pdev, i, &priv->irq_desc[i]);
>+
>+ pci_free_irq_vectors(pdev);
>+}
>+
>+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,
>+ .get_dev_cfg = mtk_pci_get_dev_cfg,
>+ .register_dev_evt = mtk_pci_register_ext_evt,
>+ .unregister_dev_evt = mtk_pci_unregister_ext_evt,
>+ .mask_dev_evt = mtk_pci_mask_ext_evt,
>+ .unmask_dev_evt = mtk_pci_unmask_ext_evt,
>+ .clear_dev_evt = mtk_pci_clear_ext_evt,
>+ .send_dev_evt = mtk_pci_send_ext_evt,
>+};
>+
>+static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
>+{
>+ struct device *dev = &pdev->dev;
>+ struct mtk_pci_priv *priv;
>+ struct mtk_md_dev *mdev;
>+ int ret;
>+
>+ mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
>+ if (!mdev) {
>+ ret = -ENOMEM;
>+ goto out;
as for the rest of the labels please name what is done
eg log_err
>+ }
>+ mdev->dev_ops = &pci_hw_ops;
>+ mdev->dev = dev;
>+
>+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
>+ if (!priv) {
>+ ret = -ENOMEM;
>+ goto free_cntx_data;
>+ }
>+
>+ pci_set_drvdata(pdev, mdev);
>+ priv->cfg = (void *)id->driver_data;
>+ priv->mdev = mdev;
>+ mdev->hw_ver = pdev->device;
>+ mdev->hw_priv = priv;
>+ mdev->dev = dev;
>+ snprintf(mdev->dev_str, MTK_DEV_STR_LEN, "%02x%02x%d",
>+ pdev->bus->number, PCI_SLOT(pdev->devfn), PCI_FUNC(pdev->devfn));
>+ if (pdev->state_saved)
>+ pci_restore_state(pdev);
>+
>+ ret = pcim_enable_device(pdev);
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to enable pci device.\n");
>+ goto free_priv_data;
>+ }
>+
>+ ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(64));
>+ if (ret) {
>+ dev_err((mdev)->dev, "Failed to set DMA Mask and Coherent. (ret=%d)\n", ret);
>+ goto disable_device;
>+ }
>+
>+ ret = mtk_pci_bar_init(mdev);
>+ if (ret)
>+ goto disable_device;
>+
>+ ret = priv->cfg->atr_init(mdev);
>+ if (ret)
>+ goto free_bar;
>+
>+ ret = mtk_mhccif_init(mdev);
>+ if (ret)
>+ goto free_bar;
>+
>+ /* mask all irqs */
>+ if (priv->cfg->flag & MTK_CFG_IRQ_DFLT_MASK)
>+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, U32_MAX);
>+
>+ ret = mtk_pci_request_irq(mdev);
>+ if (ret)
>+ goto free_mhccif;
>+
>+ pci_set_master(pdev);
>+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
>+
>+ if (mtk_pci_link_check(mdev)) {
>+ pci_save_state(pdev);
>+ } else {
>+ ret = -EFAULT;
>+ goto clear_master;
#define EFAULT 14 /* Bad address */
does it suit here?
>+ }
>+
>+ priv->saved_state = pci_store_saved_state(pdev);
>+ if (!priv->saved_state) {
>+ ret = -EFAULT;
>+ goto clear_master;
>+ }
>+
>+ return 0;
>+
>+clear_master:
>+ pci_clear_master(pdev);
>+ mtk_pci_free_irq(mdev);
>+free_mhccif:
>+ mtk_mhccif_exit(mdev);
>+free_bar:
>+ mtk_pci_bar_exit(mdev);
>+disable_device:
>+ pci_disable_device(pdev);
>+free_priv_data:
>+ devm_kfree(dev, priv);
>+free_cntx_data:
>+ devm_kfree(dev, mdev);
>+out:
>+ dev_err(dev, "Failed to probe device, ret=%d\n", ret);
>+
>+ return ret;
>+}
>+
please also take a look on sashiko notes, there is some number of them
^ permalink raw reply
* RE: [PATCH 02/11] net: wwan: t9xx: Add control plane transaction layer
From: Jagielski, Jedrzej @ 2026-06-01 11:24 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-2-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>The control plane implements TX services that reside in the
>transaction layer. The services receive the packets from the
>port layer and call the corresponding DMA components to
>transmit data to the device. Meanwhile, TX services receive
>and manage the port control commands from the port layer.
>
>The control plane implements RX services that reside in the
>transaction layer. The services receive the downlink packets
>from the modem and transfer the packets to the corresponding
>port layer interfaces.
>
>Signed-off-by: Jack Wu <jackbb_wu@compal.com>
>---
> drivers/net/wwan/Kconfig | 5 ++++
> drivers/net/wwan/t9xx/Makefile | 5 ++--
> drivers/net/wwan/t9xx/mtk_ctrl_plane.c | 34 ++++++++++++++++++++++
> drivers/net/wwan/t9xx/mtk_ctrl_plane.h | 22 +++++++++++++++
> drivers/net/wwan/t9xx/mtk_dev.c | 44 +++++++++++++++++++++++++++++
> drivers/net/wwan/t9xx/mtk_dev.h | 4 +++
> drivers/net/wwan/t9xx/pcie/Makefile | 10 +++++++
> drivers/net/wwan/t9xx/pcie/mtk_pci.c | 8 ++----
> drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h | 21 ++++++++++++++
> 9 files changed, 146 insertions(+), 7 deletions(-)
>
>diff --git a/drivers/net/wwan/Kconfig b/drivers/net/wwan/Kconfig
>index 4cee537c739f..d8be12fb988c 100644
>--- a/drivers/net/wwan/Kconfig
>+++ b/drivers/net/wwan/Kconfig
>@@ -124,6 +124,7 @@ config MTK_T7XX
> config MTK_T9XX
> tristate "MediaTek PCIe 5G WWAN modem T9xx device"
> depends on PCI
>+ select MTK_T9XX_PCI
> select NET_DEVLINK
> help
> Enables MediaTek PCIe based 5G WWAN modem (T9xx series) device.
>@@ -133,6 +134,10 @@ config MTK_T9XX
>
> If unsure, say N.
>
>+config MTK_T9XX_PCI
>+ tristate
>+ depends on PCI && MTK_T9XX
>+
> endif # WWAN
>
> endmenu
>diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
>index 6f2dd3f91454..ae9d6f2344ab 100644
>--- a/drivers/net/wwan/t9xx/Makefile
>+++ b/drivers/net/wwan/t9xx/Makefile
>@@ -4,7 +4,8 @@ ccflags-y += -I$(src)/pcie
> ccflags-y += -I$(src)
>
> obj-$(CONFIG_MTK_T9XX) += mtk_t9xx.o
>+obj-$(CONFIG_MTK_T9XX_PCI) += pcie/
>
> mtk_t9xx-y := \
>- pcie/mtk_pci.o \
>- pcie/mtk_pci_drv_m9xx.o
>+ mtk_dev.o \
>+ mtk_ctrl_plane.o
>diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.c b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
>new file mode 100644
>index 000000000000..ae5e1797b817
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.c
>@@ -0,0 +1,34 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ * Copyright (c) 2022-2023, Intel Corporation.
>+ */
>+
>+#include <linux/device.h>
>+
>+#include "mtk_ctrl_plane.h"
>+
please add kdoc, especially there's EXPORT_SYMBOL
>+int mtk_ctrl_init(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_blk *ctrl_blk;
>+
>+ ctrl_blk = devm_kzalloc(mdev->dev, sizeof(*ctrl_blk), GFP_KERNEL);
>+ if (!ctrl_blk)
>+ return -ENOMEM;
>+
>+ ctrl_blk->mdev = mdev;
>+ mdev->ctrl_blk = ctrl_blk;
>+
>+ return 0;
>+}
>+EXPORT_SYMBOL(mtk_ctrl_init);
>+
>+int mtk_ctrl_exit(struct mtk_md_dev *mdev)
do we need int if 0 is always returned?
>+{
>+ struct mtk_ctrl_blk *ctrl_blk = mdev->ctrl_blk;
>+
>+ devm_kfree(mdev->dev, ctrl_blk);
>+
>+ return 0;
>+}
>+EXPORT_SYMBOL(mtk_ctrl_exit);
>diff --git a/drivers/net/wwan/t9xx/mtk_ctrl_plane.h b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
>new file mode 100644
>index 000000000000..8276be19b456
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_ctrl_plane.h
>@@ -0,0 +1,22 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2022, MediaTek Inc.
shouldn't 2026 be put?
>+ */
>+
>+#ifndef __MTK_CTRL_PLANE_H__
>+#define __MTK_CTRL_PLANE_H__
>+
>+#include <linux/kref.h>
>+#include <linux/skbuff.h>
>+
>+#include "mtk_dev.h"
>+
>+struct mtk_ctrl_blk {
>+ struct mtk_md_dev *mdev;
>+ struct mtk_ctrl_trans *trans;
>+};
>+
>+int mtk_ctrl_init(struct mtk_md_dev *mdev);
>+int mtk_ctrl_exit(struct mtk_md_dev *mdev);
>+
>+#endif /* __MTK_CTRL_PLANE_H__ */
>diff --git a/drivers/net/wwan/t9xx/mtk_dev.c b/drivers/net/wwan/t9xx/mtk_dev.c
>new file mode 100644
>index 000000000000..f254ca7ed877
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_dev.c
>@@ -0,0 +1,44 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include <linux/module.h>
>+
>+#include "mtk_dev.h"
>+
>+struct mtk_md_dev *mtk_dev_alloc(struct device *pdev, const struct mtk_dev_ops *dev_ops)
>+{
>+ struct mtk_md_dev *mdev;
>+
>+ mdev = devm_kzalloc(pdev, sizeof(*mdev), GFP_KERNEL);
>+ if (!mdev)
>+ return NULL;
>+
>+ mdev->dev_ops = dev_ops;
>+ mdev->dev = pdev;
>+ return mdev;
>+}
>+EXPORT_SYMBOL(mtk_dev_alloc);
>+
>+void mtk_dev_free(struct mtk_md_dev *mdev)
>+{
>+ struct device *dev = mdev->dev;
>+
>+ devm_kfree(dev, mdev);
>+}
>+EXPORT_SYMBOL(mtk_dev_free);
>+
>+static int __init mtk_common_drv_init(void)
>+{
>+ return 0;
>+}
>+module_init(mtk_common_drv_init);
>+
>+static void __exit mtk_common_drv_exit(void)
is it used anywhere here in the patch?
>+{
>+}
>+module_exit(mtk_common_drv_exit);
>+
>+MODULE_DESCRIPTION("MediaTek T9xx PCIe WWAN driver");
>+MODULE_LICENSE("GPL");
>diff --git a/drivers/net/wwan/t9xx/mtk_dev.h b/drivers/net/wwan/t9xx/mtk_dev.h
>index 8278a0e2875e..37eec1a358fa 100644
>--- a/drivers/net/wwan/t9xx/mtk_dev.h
>+++ b/drivers/net/wwan/t9xx/mtk_dev.h
>@@ -57,6 +57,7 @@ struct mtk_md_dev {
> void *hw_priv;
> u32 hw_ver;
> char dev_str[MTK_DEV_STR_LEN];
>+ void *ctrl_blk;
> };
>
> static inline u32 mtk_dev_get_dev_state(struct mtk_md_dev *mdev)
>@@ -105,4 +106,7 @@ static inline int mtk_dev_send_dev_evt(struct mtk_md_dev *mdev, u32 dev_evt)
> return mdev->dev_ops->send_dev_evt(mdev, dev_evt);
> }
>
>+struct mtk_md_dev *mtk_dev_alloc(struct device *pdev, const struct mtk_dev_ops *dev_ops);
>+void mtk_dev_free(struct mtk_md_dev *mdev);
>+
> #endif /* __MTK_DEV_H__ */
>diff --git a/drivers/net/wwan/t9xx/pcie/Makefile b/drivers/net/wwan/t9xx/pcie/Makefile
>new file mode 100644
>index 000000000000..7410d1796d27
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/Makefile
>@@ -0,0 +1,10 @@
>+# SPDX-License-Identifier: GPL-2.0-only
>+
>+ccflags-y += -I$(src)
>+ccflags-y += -I$(src)/..
>+
>+obj-$(CONFIG_MTK_T9XX_PCI) += mtk_t9xx_pcie.o
>+
>+mtk_t9xx_pcie-y := \
>+ mtk_pci_drv_m9xx.o \
>+ mtk_pci.o
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>index adec3ccdee08..518c32d55643 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>@@ -14,6 +14,7 @@
> #include <linux/module.h>
>
> #include "mtk_dev.h"
>+#include "mtk_trans_ctrl.h"
> #include "mtk_pci.h"
> #include "mtk_pci_reg.h"
>
>@@ -385,8 +386,7 @@ static u32 mtk_pci_ext_h2d_evt_hw_bits(u32 chs)
>
> SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DEVICE_RESET,
> DEV_EVT_H2D_DEVICE_RESET);
>- SET_HW_BITS(hw_bits, chs, MHCCIF_RC2EP_EVT_DRM_DISABLE_AP,
>- EXT_EVT_H2D_DRM_DISABLE_AP);
>+
> return LE32_TO_U32(cpu_to_le32(hw_bits));
> }
>
>@@ -779,13 +779,11 @@ static int mtk_pci_probe(struct pci_dev *pdev, const struct pci_device_id *id)
> struct mtk_md_dev *mdev;
> int ret;
>
>- mdev = devm_kzalloc(dev, sizeof(*mdev), GFP_KERNEL);
>+ mdev = mtk_dev_alloc(dev, &pci_hw_ops);
> if (!mdev) {
> ret = -ENOMEM;
> goto out;
> }
>- mdev->dev_ops = &pci_hw_ops;
>- mdev->dev = dev;
>
> priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
> if (!priv) {
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
>new file mode 100644
>index 000000000000..d6de4c43b529
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_trans_ctrl.h
>@@ -0,0 +1,21 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_TRANS_CTRL_H__
>+#define __MTK_TRANS_CTRL_H__
>+
>+#include <linux/kref.h>
>+#include <linux/list.h>
>+#include <linux/skbuff.h>
>+#include <linux/types.h>
>+
>+#include "mtk_dev.h"
>+
>+struct mtk_ctrl_trans {
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ struct mtk_md_dev *mdev;
>+};
>+
>+#endif
>
>--
>2.34.1
^ permalink raw reply
* [PATCH net v3 0/2] af_unix: Fix inq_len update issue
From: jianyu.li @ 2026-06-01 11:36 UTC (permalink / raw)
To: kuniyu, davem, edumazet, kuba, pabeni, horms
Cc: willemb, netdev, linux-kernel, linux-mediatek, black-ch.chen,
ivan.tseng, jianyu.li
From: Jianyu Li <jianyu.li@mediatek.com>
This series fix the problem that inq_len is inconsistent with
actual remaining byte count when only part of a skb is consumed.
Changes:
v3:
- Patch 2: Align macro definitions
v2: https://lore.kernel.org/netdev/20260528110033.3327744-1-jianyu.li@mediatek.com/
- Patch 1: Improve lock usage in unix_stream_read_generic()
- Patch 2: Follow reverse xmas tree ordering in partial_read test case
v1: https://lore.kernel.org/netdev/20260527065342.2463433-1-jianyu.li@mediatek.com/
Jianyu Li (2):
af_unix: Fix inq_len update problem in partial read
af_unix: Add test for SCM_INQ on partial read
net/unix/af_unix.c | 11 ++--
tools/testing/selftests/net/af_unix/scm_inq.c | 54 ++++++++++++++++++-
2 files changed, 58 insertions(+), 7 deletions(-)
--
2.45.2
^ permalink raw reply
* [PATCH net v3 2/2] af_unix: Add test for SCM_INQ on partial read
From: jianyu.li @ 2026-06-01 11:36 UTC (permalink / raw)
To: kuniyu, davem, edumazet, kuba, pabeni, horms
Cc: willemb, netdev, linux-kernel, linux-mediatek, black-ch.chen,
ivan.tseng, jianyu.li
In-Reply-To: <20260601113640.231897-1-jianyu.li@mediatek.com>
From: Jianyu Li <jianyu.li@mediatek.com>
Add test to verify that when a skb is partially consumed,
unix_inq_len() return correct remaining byte count.
Before:
# RUN scm_inq.stream.partial_read ...
# scm_inq.c:165:partial_read:Expected remain (512) == *(int *)CMSG_DATA(cmsg) (768)
# partial_read: Test terminated by assertion
# FAIL scm_inq.stream.partial_read
not ok 2 scm_inq.stream.partial_read
After:
# RUN scm_inq.stream.partial_read ...
# OK scm_inq.stream.partial_read
ok 2 scm_inq.stream.partial_read
Signed-off-by: Jianyu Li <jianyu.li@mediatek.com>
---
v3: Align macro definitions
v2: Follow reverse xmas tree ordering in partial_read test case
---
tools/testing/selftests/net/af_unix/scm_inq.c | 54 ++++++++++++++++++-
1 file changed, 52 insertions(+), 2 deletions(-)
diff --git a/tools/testing/selftests/net/af_unix/scm_inq.c b/tools/testing/selftests/net/af_unix/scm_inq.c
index 3a86be9bda..6268b5bf50 100644
--- a/tools/testing/selftests/net/af_unix/scm_inq.c
+++ b/tools/testing/selftests/net/af_unix/scm_inq.c
@@ -8,8 +8,9 @@
#include "kselftest_harness.h"
-#define NR_CHUNKS 100
-#define MSG_LEN 256
+#define NR_CHUNKS 100
+#define MSG_LEN 256
+#define NR_PARTIAL_READS 3
FIXTURE(scm_inq)
{
@@ -120,4 +121,53 @@ TEST_F(scm_inq, basic)
recv_chunks(_metadata, self);
}
+TEST_F(scm_inq, partial_read)
+{
+ char buf[MSG_LEN * NR_PARTIAL_READS] = {};
+ char cmsg_buf[CMSG_SPACE(sizeof(int))];
+ struct msghdr msg = {};
+ struct iovec iov = {};
+ struct cmsghdr *cmsg;
+ int err, inq, ret, i;
+ int remain;
+
+ err = setsockopt(self->fd[1], SOL_SOCKET, SO_INQ, &(int){1}, sizeof(int));
+ if (variant->type != SOCK_STREAM) {
+ ASSERT_EQ(-ENOPROTOOPT, -errno);
+ return;
+ }
+ ASSERT_EQ(0, err);
+
+ ret = send(self->fd[0], buf, sizeof(buf), 0);
+ ASSERT_EQ(sizeof(buf), ret);
+
+ msg.msg_iov = &iov;
+ msg.msg_iovlen = 1;
+ msg.msg_control = cmsg_buf;
+ msg.msg_controllen = sizeof(cmsg_buf);
+
+ iov.iov_base = buf;
+ iov.iov_len = MSG_LEN;
+
+ for (i = 0; i < NR_PARTIAL_READS; i++) {
+ remain = MSG_LEN * (NR_PARTIAL_READS - 1 - i);
+
+ memset(buf, 0, MSG_LEN);
+ memset(cmsg_buf, 0, sizeof(cmsg_buf));
+ ret = recvmsg(self->fd[1], &msg, 0);
+ ASSERT_EQ(MSG_LEN, ret);
+
+ cmsg = CMSG_FIRSTHDR(&msg);
+ ASSERT_NE(NULL, cmsg);
+ ASSERT_EQ(CMSG_LEN(sizeof(int)), cmsg->cmsg_len);
+ ASSERT_EQ(SOL_SOCKET, cmsg->cmsg_level);
+ ASSERT_EQ(SCM_INQ, cmsg->cmsg_type);
+ ASSERT_EQ(remain, *(int *)CMSG_DATA(cmsg));
+
+ ret = ioctl(self->fd[1], SIOCINQ, &inq);
+ ASSERT_EQ(0, ret);
+ ASSERT_EQ(remain, inq);
+ }
+}
+
TEST_HARNESS_MAIN
--
2.45.2
^ permalink raw reply related
* [PATCH net v3 1/2] af_unix: Fix inq_len update problem in partial read
From: jianyu.li @ 2026-06-01 11:36 UTC (permalink / raw)
To: kuniyu, davem, edumazet, kuba, pabeni, horms
Cc: willemb, netdev, linux-kernel, linux-mediatek, black-ch.chen,
ivan.tseng, jianyu.li
In-Reply-To: <20260601113640.231897-1-jianyu.li@mediatek.com>
From: Jianyu Li <jianyu.li@mediatek.com>
Currently inq_len is updated only when the whole skb is consumed.
If only part of the data is read, following SIOCINQ query would
get value greater than what actually left.
This change update inq_len timely in unix_stream_read_generic(),
and adjust unix_stream_read_skb() accordingly to prevent
repetitive update.
Fixes: f4e1fb04c123 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len().")
Signed-off-by: Jianyu Li <jianyu.li@mediatek.com>
---
v2: Improve lock usage in unix_stream_read_generic()
---
net/unix/af_unix.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/net/unix/af_unix.c b/net/unix/af_unix.c
index dc71ed79be..0d9cd977c7 100644
--- a/net/unix/af_unix.c
+++ b/net/unix/af_unix.c
@@ -2886,7 +2886,7 @@ static int unix_stream_read_skb(struct sock *sk, skb_read_actor_t recv_actor)
return -EAGAIN;
}
- WRITE_ONCE(u->inq_len, u->inq_len - skb->len);
+ WRITE_ONCE(u->inq_len, u->inq_len - unix_skb_len(skb));
#if IS_ENABLED(CONFIG_AF_UNIX_OOB)
if (skb == u->oob_skb) {
@@ -3063,11 +3063,12 @@ static int unix_stream_read_generic(struct unix_stream_read_state *state,
unix_detach_fds(&scm, skb);
}
- if (unix_skb_len(skb))
- break;
-
spin_lock(&sk->sk_receive_queue.lock);
- WRITE_ONCE(u->inq_len, u->inq_len - skb->len);
+ WRITE_ONCE(u->inq_len, u->inq_len - chunk);
+ if (unix_skb_len(skb)) {
+ spin_unlock(&sk->sk_receive_queue.lock);
+ break;
+ }
__skb_unlink(skb, &sk->sk_receive_queue);
spin_unlock(&sk->sk_receive_queue.lock);
--
2.45.2
^ permalink raw reply related
* RE: [PATCH 03/11] net: wwan: t9xx: Add control DMA interface
From: Jagielski, Jedrzej @ 2026-06-01 11:54 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-3-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>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@compal.com>
>---
> 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,
>+};
>+
^ permalink raw reply
* RE: [PATCH 06/11] net: wwan: t9xx: Add AT & MBIM WWAN ports
From: Jagielski, Jedrzej @ 2026-06-01 12:09 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-6-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>Adds AT & MBIM ports to the port infrastructure.
please use imperative mode in commit msg
>The WWAN initialization method is responsible for creating the
>corresponding ports using the WWAN framework infrastructure. The
>implemented WWAN port operations are start, stop, tx, tx_blocking
>and tx_poll.
>
>Signed-off-by: Jack Wu <jackbb_wu@compal.com>
>---
> drivers/net/wwan/t9xx/mtk_port.c | 27 ++
> drivers/net/wwan/t9xx/mtk_port.h | 15 ++
> drivers/net/wwan/t9xx/mtk_port_io.c | 332 ++++++++++++++++++++++++-
> drivers/net/wwan/t9xx/mtk_port_io.h | 5 +
> drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c | 8 +
> 5 files changed, 386 insertions(+), 1 deletion(-)
>
>diff --git a/drivers/net/wwan/t9xx/mtk_port.c b/drivers/net/wwan/t9xx/mtk_port.c
>index dbd279cf2a14..4032df99b5b0 100644
>--- a/drivers/net/wwan/t9xx/mtk_port.c
>+++ b/drivers/net/wwan/t9xx/mtk_port.c
>@@ -630,6 +630,7 @@ static int mtk_port_rx_dispatch(struct sk_buff *skb, void *priv, bool force_recv
> /* Support scatter gather transmission */
> if (port->rx_mtu > port->rx_frag_size) {
> ret = mtk_port_rx_dispatch_frag_skb(port, skb);
>+ /* -EIO means partial data dispatch complete, does not goto drop flow */
unclear how adding this comment is related to the patch
> if (ret < 0 && ret != -EIO)
> goto drop_frag_skb;
> } else {
>@@ -818,6 +819,29 @@ int mtk_port_ch_disable(struct mtk_port *port)
> return ret;
> }
>
>+static int mtk_port_enable_by_type(struct mtk_port_mngr *port_mngr, int tbl_type)
>+{
>+ struct mtk_port **ports;
>+ int ret, idx;
>+
>+ if (tbl_type < 0 || tbl_type >= PORT_TBL_MAX)
>+ return -EINVAL;
>+
>+ ports = kcalloc(port_mngr->port_cnt, sizeof(struct mtk_port *), GFP_KERNEL);
>+ if (!ports)
>+ return -ENOMEM;
>+
>+ ret = radix_tree_gang_lookup(&port_mngr->port_tbl[tbl_type],
>+ (void **)ports, 0, port_mngr->port_cnt);
>+ for (idx = 0; idx < ret; idx++) {
>+ if (ports[idx]->enable)
>+ ports_ops[ports[idx]->info.type]->enable(ports[idx]);
>+ }
>+
>+ kfree(ports);
>+ return 0;
>+}
>+
> static void mtk_port_disable(struct mtk_port_mngr *port_mngr)
> {
> struct mtk_port **ports;
>@@ -851,6 +875,9 @@ void mtk_port_mngr_fsm_state_handler(struct mtk_fsm_param *fsm_param, void *arg)
> case FSM_STATE_OFF:
> mtk_port_disable(port_mngr);
> break;
>+ case FSM_STATE_READY:
>+ mtk_port_enable_by_type(port_mngr, PORT_TBL_MD);
>+ break;
> default:
> break;
> }
>diff --git a/drivers/net/wwan/t9xx/mtk_port.h b/drivers/net/wwan/t9xx/mtk_port.h
>index a201c0007878..cf561add6318 100644
>--- a/drivers/net/wwan/t9xx/mtk_port.h
>+++ b/drivers/net/wwan/t9xx/mtk_port.h
>@@ -56,6 +56,10 @@ enum mtk_ccci_ch {
> /* to MD */
> CCCI_CONTROL_RX = 0x2000,
> CCCI_CONTROL_TX = 0x2001,
>+ CCCI_UART2_RX = 0x200A,
>+ CCCI_UART2_TX = 0x200C,
>+ CCCI_MBIM_RX = 0x20D0,
>+ CCCI_MBIM_TX = 0x20D1,
> };
>
> enum mtk_port_flag {
>@@ -73,6 +77,7 @@ enum mtk_port_tbl {
>
> enum mtk_port_type {
> PORT_TYPE_INTERNAL,
>+ PORT_TYPE_WWAN,
> PORT_TYPE_MAX
> };
>
>@@ -81,6 +86,13 @@ struct mtk_internal_port {
> int (*recv_cb)(void *arg, struct sk_buff *skb);
> };
>
>+struct mtk_wwan_port {
>+ /* w_lock protects wwan_port when recv data and disable port at the same time */
>+ struct mutex w_lock;
>+ int w_type;
>+ void *w_port;
>+};
>+
> struct mtk_port_cfg {
> enum mtk_ccci_ch tx_ch;
> enum mtk_ccci_ch rx_ch;
>@@ -108,8 +120,11 @@ struct mtk_port {
> wait_queue_head_t rx_wq;
> struct list_head stale_entry;
> char dev_str[MTK_DEV_STR_LEN];
>+ /* Serializes port write operations */
>+ struct mutex write_lock;
> struct mtk_port_mngr *port_mngr;
> struct mtk_internal_port i_priv;
>+ struct mtk_wwan_port w_priv;
> };
>
> struct mtk_port_mngr {
>diff --git a/drivers/net/wwan/t9xx/mtk_port_io.c b/drivers/net/wwan/t9xx/mtk_port_io.c
>index 9e7a1207cc03..ab8b1c5157ec 100644
>--- a/drivers/net/wwan/t9xx/mtk_port_io.c
>+++ b/drivers/net/wwan/t9xx/mtk_port_io.c
>@@ -3,6 +3,10 @@
> * Copyright (c) 2022, MediaTek Inc.
> */
> #include <linux/netdevice.h>
>+#include <linux/poll.h>
>+#include <linux/slab.h>
>+#include <linux/wait.h>
>+#include <linux/wwan.h>
>
> #include "mtk_port_io.h"
>
>@@ -39,6 +43,149 @@ static void mtk_port_struct_init(struct mtk_port *port)
> port->rx_buf_size = MTK_RX_BUF_SIZE;
> init_waitqueue_head(&port->trb_wq);
> init_waitqueue_head(&port->rx_wq);
>+ mutex_init(&port->write_lock);
>+}
>+
>+static int mtk_port_copy_data_from(void *to, union user_buf from, unsigned int len,
>+ unsigned int offset, bool from_user_space)
>+{
>+ int ret = 0;
like for the previous commits - please do not zeroinit when don't required
returbning 0 at the end is completely fine here
>+
>+ if (from_user_space) {
>+ ret = copy_from_user(to, from.ubuf + offset, len);
>+ if (ret)
>+ ret = -EFAULT;
#define EFAULT 14 /* Bad address */
i believe there are better suiting codes
>+ } else {
>+ memcpy(to, from.kbuf + offset, len);
>+ }
>+
>+ return ret;
>+}
>+
>+static int mtk_port_common_write_frag_skb(struct mtk_port *port, struct sk_buff *skb,
>+ union user_buf buf, u32 packet_size,
>+ u32 cur_pos, bool from_user_space)
>+{
>+ struct sk_buff *frag_skb, *tmp = NULL;
>+ u32 frag_size;
>+ int ret;
>+
>+ frag_size = min(packet_size, port->tx_frag_size);
>+ ret = mtk_port_copy_data_from(skb_put(skb, frag_size),
>+ buf, frag_size,
>+ cur_pos, from_user_space);
>+ if (ret) {
>+ dev_err(port->port_mngr->ctrl_blk->mdev->dev,
>+ "Failed to copy skb for port(%s)\n", port->info.name);
>+ goto err_reset_skb;
>+ }
>+ cur_pos += frag_size;
>+ packet_size -= frag_size;
>+ if (!packet_size)
>+ return cur_pos;
>+
>+ while (packet_size > 0) {
>+ frag_skb = __dev_alloc_skb(port->tx_mtu, GFP_KERNEL);
>+ if (!frag_skb) {
>+ ret = -ENOMEM;
>+ goto err_free_frag_list;
>+ }
>+
>+ frag_size = min(packet_size, port->tx_frag_size);
>+ ret = mtk_port_copy_data_from(skb_put(frag_skb, frag_size),
>+ buf, frag_size,
>+ cur_pos, from_user_space);
>+ if (ret) {
>+ dev_err(port->port_mngr->ctrl_blk->mdev->dev,
>+ "Failed to copy frag_skb for port(%s)\n", port->info.name);
>+ dev_kfree_skb_any(frag_skb);
>+ goto err_free_frag_list;
>+ }
>+ skb->data_len += frag_size;
>+ skb->len += frag_size;
>+ cur_pos += frag_size;
>+ packet_size -= frag_size;
>+ if (!tmp)
>+ skb_shinfo(skb)->frag_list = frag_skb;
>+ else
>+ tmp->next = frag_skb;
>+ tmp = frag_skb;
>+ }
>+ return cur_pos;
>+
>+err_free_frag_list:
>+ frag_skb = skb_shinfo(skb)->frag_list;
>+ while (frag_skb) {
>+ tmp = frag_skb->next;
>+ frag_skb->next = NULL;
>+ dev_kfree_skb_any(frag_skb);
>+ frag_skb = tmp;
>+ }
>+ skb_shinfo(skb)->frag_list = NULL;
>+err_reset_skb:
>+ skb->data_len = 0;
>+ return ret;
>+}
>+
>+static int mtk_port_common_write(struct mtk_port *port, union user_buf buf, unsigned int len,
>+ bool from_user_space)
>+{
>+ u32 packet_size, left_cnt = len, cur_pos;
>+ struct sk_buff *skb;
>+ int ret;
>+
>+ if (len == 0)
that's really successful path?
>+ return 0;
>+
>+start_write:
>+ ret = mtk_port_status_check(port);
>+ if (ret)
>+ goto end_write;
>+
>+ skb = __dev_alloc_skb(port->tx_mtu, GFP_KERNEL);
>+ if (!skb) {
>+ ret = -ENOMEM;
>+ goto end_write;
>+ }
>+
>+ skb_reserve(skb, sizeof(struct mtk_ccci_header));
>+
>+ packet_size = min(left_cnt, port->tx_mtu);
>+ cur_pos = len - left_cnt;
>+ /* Support scatter gather transmission */
>+ if (port->tx_mtu > port->tx_frag_size) {
>+ ret = mtk_port_common_write_frag_skb(port, skb, buf, packet_size,
>+ cur_pos, from_user_space);
>+ if (ret < 0)
>+ goto err_free_skb;
>+ } else {
>+ ret = mtk_port_copy_data_from(skb_put(skb, packet_size),
>+ buf, packet_size,
>+ cur_pos, from_user_space);
>+ if (ret) {
>+ dev_err(port->port_mngr->ctrl_blk->mdev->dev,
>+ "Failed to copy data for port(%s)\n", port->info.name);
>+ goto err_free_skb;
>+ }
>+ }
>+
>+ ret = mtk_port_send_data(port, skb);
>+ if (ret < 0) {
>+ if (ret == -EINTR)
>+ left_cnt -= packet_size;
>+ goto end_write;
>+ }
>+
>+ left_cnt -= ret;
>+ if (left_cnt)
>+ goto start_write;
>+ else
>+ goto end_write;
>+
>+err_free_skb:
>+ dev_kfree_skb_any(skb);
>+end_write:
>+ return (len > left_cnt) ? (len - left_cnt) : ret;
> }
>
> static int mtk_port_internal_init(struct mtk_port *port)
>@@ -109,7 +256,6 @@ static int mtk_port_internal_recv(struct mtk_port *port, struct sk_buff *skb)
> return ret;
>
> drop_data:
>- dev_kfree_skb_any(skb);
> return ret;
> }
>
>@@ -241,6 +387,190 @@ static const struct port_ops port_internal_ops = {
> .recv = mtk_port_internal_recv,
> };
>
>+static int mtk_port_wwan_open(struct wwan_port *w_port)
>+{
>+ struct mtk_port *port;
>+ int ret;
>+
>+ port = wwan_port_get_drvdata(w_port);
>+ ret = mtk_port_get_locked(port);
>+ if (ret)
>+ return ret;
>+
>+ ret = mtk_port_common_open(port);
>+ if (ret)
>+ mtk_port_put_locked(port);
>+
>+ return ret;
>+}
>+
>+static void mtk_port_wwan_close(struct wwan_port *w_port)
>+{
>+ struct mtk_port *port = wwan_port_get_drvdata(w_port);
>+
>+ mtk_port_common_close(port);
>+ mtk_port_put_locked(port);
>+}
>+
>+static int mtk_port_wwan_write(struct wwan_port *w_port, struct sk_buff *skb)
>+{
>+ struct mtk_port *port = wwan_port_get_drvdata(w_port);
>+ union user_buf user_buf;
>+
>+ if (unlikely(!skb->len)) {
>+ kfree_skb(skb);
>+ return 0;
>+ }
>+
>+ port->info.flags &= ~PORT_F_BLOCKING;
>+ user_buf.kbuf = (void *)skb->data;
>+ return mtk_port_common_write(port, user_buf, skb->len, false);
>+}
>+
>+static int mtk_port_wwan_write_blocking(struct wwan_port *w_port, struct sk_buff *skb)
>+{
>+ struct mtk_port *port = wwan_port_get_drvdata(w_port);
>+ union user_buf user_buf;
>+
>+ if (unlikely(!skb->len)) {
>+ kfree_skb(skb);
>+ return 0;
>+ }
>+
>+ port->info.flags |= PORT_F_BLOCKING;
>+ user_buf.kbuf = (void *)skb->data;
>+ return mtk_port_common_write(port, user_buf, skb->len, false);
>+}
>+
>+static __poll_t mtk_port_wwan_poll(struct wwan_port *w_port, struct file *file,
>+ struct poll_table_struct *poll)
>+{
>+ struct mtk_port *port = wwan_port_get_drvdata(w_port);
>+ union ctrl_hif_cmd_data hif_cmd;
>+ struct mtk_ctrl_blk *ctrl_blk;
>+ __poll_t mask = 0;
>+
>+ poll_wait(file, &port->trb_wq, poll);
>+ if (mtk_port_status_check(port))
>+ return EPOLLERR | EPOLLHUP;
>+
>+ ctrl_blk = port->port_mngr->ctrl_blk;
>+ hif_cmd.rx_ch = port->info.rx_ch;
>+ if (!ctrl_blk->ops->send_cmd(ctrl_blk->mdev, HIF_CTRL_CMD_CHECK_TX_FULL, &hif_cmd))
>+ mask |= EPOLLOUT | EPOLLWRNORM;
>+
>+ return mask;
>+}
>+
>+static const struct wwan_port_ops wwan_ops = {
>+ .start = mtk_port_wwan_open,
>+ .stop = mtk_port_wwan_close,
>+ .tx = mtk_port_wwan_write,
>+ .tx_blocking = mtk_port_wwan_write_blocking,
>+ .tx_poll = mtk_port_wwan_poll,
>+};
>+
>+static int mtk_port_wwan_init(struct mtk_port *port)
for the whole series - please assess where int over void
is really required
>+{
>+ mtk_port_struct_init(port);
>+ port->enable = false;
>+
>+ mutex_init(&port->w_priv.w_lock);
>+
>+ switch (port->info.rx_ch) {
>+ case CCCI_MBIM_RX:
>+ port->w_priv.w_type = WWAN_PORT_MBIM;
>+ break;
>+ case CCCI_UART2_RX:
>+ port->w_priv.w_type = WWAN_PORT_AT;
>+ break;
>+ default:
>+ port->w_priv.w_type = WWAN_PORT_UNKNOWN;
>+ break;
>+ }
>+
>+ return 0;
>+}
>+
>+static int mtk_port_wwan_exit(struct mtk_port *port)
>+{
>+ if (test_bit(PORT_S_ENABLE, &port->status))
>+ ports_ops[port->info.type]->disable(port);
>+
>+ return 0;
>+}
>+
>+static int mtk_port_wwan_enable(struct mtk_port *port)
>+{
>+ struct mtk_port_mngr *port_mngr;
>+ int ret = 0;
>+
>+ port_mngr = port->port_mngr;
>+
>+ if (test_bit(PORT_S_ENABLE, &port->status))
>+ return 0;
>+
>+ ret = mtk_port_ch_enable(port);
>+ if (ret && ret != -EBUSY)
>+ return ret;
>+
>+ port->w_priv.w_port = wwan_create_port(port_mngr->ctrl_blk->mdev->dev,
>+ port->w_priv.w_type,
>+ &wwan_ops, NULL, port);
>+ if (IS_ERR(port->w_priv.w_port)) {
>+ dev_warn(port_mngr->ctrl_blk->mdev->dev,
>+ "Failed to create wwan port for (%s)\n", port->info.name);
>+ return PTR_ERR(port->w_priv.w_port);
>+ }
>+
>+ set_bit(PORT_S_WR, &port->status);
>+ set_bit(PORT_S_ENABLE, &port->status);
>+
>+ return 0;
>+}
>+
>+static int mtk_port_wwan_disable(struct mtk_port *port)
>+{
>+ struct wwan_port *w_port;
>+
>+ if (!test_and_clear_bit(PORT_S_ENABLE, &port->status))
>+ return 0;
>+
>+ clear_bit(PORT_S_WR, &port->status);
>+ w_port = port->w_priv.w_port;
>+ mutex_lock(&port->w_priv.w_lock);
>+ port->w_priv.w_port = NULL;
>+ mutex_unlock(&port->w_priv.w_lock);
>+
>+ mtk_port_ch_disable(port);
>+ wwan_remove_port(w_port);
>+
>+ return 0;
>+}
>+
>+static int mtk_port_wwan_recv(struct mtk_port *port, struct sk_buff *skb)
>+{
>+ mutex_lock(&port->w_priv.w_lock);
>+ if (!port->w_priv.w_port) {
>+ mutex_unlock(&port->w_priv.w_lock);
>+ return -ENXIO;
>+ }
>+
>+ wwan_port_rx(port->w_priv.w_port, skb);
>+ mutex_unlock(&port->w_priv.w_lock);
>+ return 0;
>+}
>+
>+static const struct port_ops port_wwan_ops = {
>+ .init = mtk_port_wwan_init,
>+ .exit = mtk_port_wwan_exit,
>+ .reset = mtk_port_reset,
>+ .enable = mtk_port_wwan_enable,
>+ .disable = mtk_port_wwan_disable,
>+ .recv = mtk_port_wwan_recv,
>+};
>+
> const struct port_ops *ports_ops[PORT_TYPE_MAX] = {
> &port_internal_ops,
>+ &port_wwan_ops,
> };
>diff --git a/drivers/net/wwan/t9xx/mtk_port_io.h b/drivers/net/wwan/t9xx/mtk_port_io.h
>index 7d2cfe90334c..12f26d244f1f 100644
>--- a/drivers/net/wwan/t9xx/mtk_port_io.h
>+++ b/drivers/net/wwan/t9xx/mtk_port_io.h
>@@ -23,6 +23,11 @@ struct port_ops {
> int (*recv)(struct mtk_port *port, struct sk_buff *skb);
> };
>
>+union user_buf {
>+ void __user *ubuf;
>+ void *kbuf;
>+};
>+
> void *mtk_port_internal_open(struct mtk_md_dev *mdev, char *name, int flag);
> int mtk_port_internal_close(void *i_port);
> int mtk_port_internal_write(void *i_port, struct sk_buff *skb);
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c b/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c
>index 8611561dd67c..aab09cab360c 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_ctrl_cfg_m9xx.c
>@@ -16,6 +16,10 @@ static const int mtk_srv_cfg_m9xx[NR_CLDMA][HW_QUE_NUM] = {
>
> /* the number of RX GPDs should be at last two */
> static const struct queue_info mtk_queue_info_m9xx[] = {
>+ {CCCI_UART2_TX, CCCI_UART2_RX, CLDMA1, TXQ(5), RXQ(5),
>+ Q_MTU_3_5K, Q_MTU_3_5K, TX_GPD_NUM, RX_GPD_NUM, Q_FRAG_3_5K, Q_FRAG_3_5K, 0},
>+ {CCCI_MBIM_TX, CCCI_MBIM_RX, CLDMA1, TXQ(2), RXQ(2),
>+ Q_MTU_3_5K, Q_MTU_3_5K, TX_GPD_NUM, RX_GPD_NUM, Q_FRAG_3_5K, Q_FRAG_3_5K, 0},
> {CCCI_CONTROL_TX, CCCI_CONTROL_RX, CLDMA1, TXQ(0), RXQ(0),
> Q_MTU_3_5K, Q_MTU_3_5K, TX_GPD_NUM, RX_GPD_NUM, Q_FRAG_3_5K, Q_FRAG_3_5K, 0},
> {CCCI_SAP_CONTROL_TX, CCCI_SAP_CONTROL_RX, CLDMA0, TXQ(0), RXQ(0),
>@@ -23,6 +27,10 @@ static const struct queue_info mtk_queue_info_m9xx[] = {
> };
>
> static const struct mtk_port_cfg port_cfg_m9xx[] = {
>+ {CCCI_UART2_TX, CCCI_UART2_RX, PORT_TYPE_WWAN, "AT",
>+ PORT_F_ALLOW_DROP},
>+ {CCCI_MBIM_TX, CCCI_MBIM_RX, PORT_TYPE_WWAN, "MBIM",
>+ PORT_F_ALLOW_DROP},
> {CCCI_CONTROL_TX, CCCI_CONTROL_RX, PORT_TYPE_INTERNAL, "MDCTRL",
> PORT_F_ALLOW_DROP},
> {CCCI_SAP_CONTROL_TX, CCCI_SAP_CONTROL_RX, PORT_TYPE_INTERNAL, "SAPCTRL",
>
>--
>2.34.1
^ permalink raw reply
* Re: [PATCH mt76] wifi: mt76: mt7915: configure noise floor reporting on reset
From: Felix Fietkau @ 2026-06-01 12:09 UTC (permalink / raw)
To: David Bauer, Lorenzo Bianconi, Ryder Lee, Shayne Chen, Sean Wang,
Matthias Brugger, AngeloGioacchino Del Regno
Cc: linux-wireless, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <370e76e3-1d41-469b-8e50-8ace6b5622d2@david-bauer.net>
On 27.05.26 19:05, David Bauer wrote:
> Hi Felix,
>
> On 5/27/26 15:56, Felix Fietkau wrote:
>> On 16.05.26 16:49, David Bauer wrote:
>>> When performing a full system recovery of the MCU on a dual-phy
>>> platform, band 0 (usually 2.4GHz) stops reading correct noise floor
>>> data.
>>>
>>> This is due to noise floor reporting only being configured correctly
>>> for the second device PHY.
>>>
>>> Configure the respective registers correctly after restarting the MCU
>>> firmware to fix reported noise-floor values.
>>>
>>> Signed-off-by: David Bauer <mail@david-bauer.net>
>> Have you considered clearing MT76_STATE_RUNNING in mt7915_mac_restart instead?
>
> The call to mt7915_run is guarded by MT76_STATE_RUNNING being set per-phy.
>
> I think this is to not start the second PHY in case it was never started due to
> it not being present. We could in theory remove this check for the primary PHY
> and clear the flag prior calling mt7915_run.
>
> This seems a bit more hacky to me. Alternatively I can also refactor the entire
> mechanism to make it easier to understand and resolve this indirection in the
> process.
My suggestion would be to do this:
start_main = test_and_clear_bit(MT76_STATE_RUNNING, &dev->mphy.state);
start_ext = ext_phy &&
test_and_clear_bit(MT76_STATE_RUNNING, &ext_phy->state);
Then using those as conditions for calling mt7915_run in
mt7915_mac_restart.
That way the special case in mt7915_run disappears and the behavior
becomes easier to follow.
- Felix
^ permalink raw reply
* RE: [PATCH 09/11] net: wwan: t9xx: Introduce WWAN interface
From: Jagielski, Jedrzej @ 2026-06-01 12:19 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-9-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>Creates the WWAN interface which implements the wwan_ops
>for registration with the WWAN framework. WWAN interface
>also implements the net_device_ops functions used by the
>network devices. Network device operations include open,
>stop, start transmission and get states.
>
>Signed-off-by: Jack Wu <jackbb_wu@compal.com>
>---
> drivers/net/wwan/t9xx/Makefile | 3 +-
> drivers/net/wwan/t9xx/mtk_data_plane.c | 14 +-
> drivers/net/wwan/t9xx/mtk_data_plane.h | 2 +
> drivers/net/wwan/t9xx/mtk_wwan.c | 475 ++++++++++++++++++++++++++++
> drivers/net/wwan/t9xx/mtk_wwan.h | 17 +
> drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c | 15 +-
> drivers/net/wwan/t9xx/pcie/mtk_dpmaif_drv.c | 1 -
> 7 files changed, 521 insertions(+), 6 deletions(-)
>
>diff --git a/drivers/net/wwan/t9xx/Makefile b/drivers/net/wwan/t9xx/Makefile
>index 3776ccb4952f..6927216021cd 100644
>--- a/drivers/net/wwan/t9xx/Makefile
>+++ b/drivers/net/wwan/t9xx/Makefile
>@@ -12,4 +12,5 @@ mtk_t9xx-y := \
> mtk_port.o \
> mtk_port_io.o \
> mtk_fsm.o \
>- mtk_data_plane.o
>+ mtk_data_plane.o \
>+ mtk_wwan.o
>diff --git a/drivers/net/wwan/t9xx/mtk_data_plane.c b/drivers/net/wwan/t9xx/mtk_data_plane.c
>index d11c7de45e17..6266915f08bb 100644
>--- a/drivers/net/wwan/t9xx/mtk_data_plane.c
>+++ b/drivers/net/wwan/t9xx/mtk_data_plane.c
>@@ -6,10 +6,13 @@
> #include "mtk_data_plane.h"
> #include "mtk_dev.h"
> #include "mtk_fsm.h"
>+#include "mtk_wwan.h"
>
> static void mtk_data_stop(struct mtk_data_blk *data_blk, struct mtk_md_dev *mdev)
> {
>+ mtk_wwan_notify(data_blk, DATA_EVT_TX_STOP, 0xff);
> data_blk->hif_ops->stop(mdev);
>+ mtk_wwan_notify(data_blk, DATA_EVT_UNREG_DEV, 0);
> data_blk->hif_ops->clear(mdev);
> }
>
>@@ -31,6 +34,7 @@ static void mtk_data_fsm_callback(struct mtk_fsm_param *fsm_param, void *data)
> data_blk->hif_ops->start(data);
> break;
> case FSM_STATE_READY:
>+ mtk_wwan_notify(data_blk, DATA_EVT_REG_DEV, 0);
> break;
> default:
> break;
>@@ -54,15 +58,21 @@ int mtk_data_init(struct mtk_md_dev *mdev, struct mtk_data_hif_ops *ops)
> if (ret < 0)
> goto data_blk_free;
>
>+ ret = mtk_wwan_init(data_blk);
>+ if (ret < 0)
>+ goto hif_exit;
>+
> ret = mtk_fsm_notifier_register(mdev, MTK_USER_DATA, mtk_data_fsm_callback, mdev,
> FSM_PRIO_1, false);
> if (ret < 0) {
> dev_err(mdev->dev, "Failed to register FSM notifier\n");
>- goto hif_exit;
>+ goto wwan_exit;
> }
>
> return 0;
>
>+wwan_exit:
>+ mtk_wwan_exit(data_blk);
> hif_exit:
> data_blk->hif_ops->exit(mdev);
> data_blk_free:
>@@ -82,6 +92,8 @@ int mtk_data_exit(struct mtk_md_dev *mdev)
>
> mtk_fsm_notifier_unregister(mdev, MTK_USER_DATA);
>
>+ mtk_wwan_exit(data_blk);
>+
> data_blk->hif_ops->exit(mdev);
>
> devm_kfree(mdev->dev, data_blk);
>diff --git a/drivers/net/wwan/t9xx/mtk_data_plane.h b/drivers/net/wwan/t9xx/mtk_data_plane.h
>index 1464fab544f1..351b30ebbbe5 100644
>--- a/drivers/net/wwan/t9xx/mtk_data_plane.h
>+++ b/drivers/net/wwan/t9xx/mtk_data_plane.h
>@@ -31,6 +31,7 @@ struct mtk_data_trans_info {
>
> struct mtk_data_blk {
> struct mtk_md_dev *mdev;
>+ struct mtk_wwan_ctlb *wcb;
> void *dcb;
> struct mtk_data_hif_ops *hif_ops;
> struct mtk_data_trans_info trans_info;
>@@ -80,6 +81,7 @@ enum mtk_data_evt {
> DATA_EVT_MIN,
> DATA_EVT_TX_START,
> DATA_EVT_TX_STOP,
>+ DATA_EVT_RX_START,
> DATA_EVT_RX_STOP,
> DATA_EVT_REG_DEV,
> DATA_EVT_UNREG_DEV,
>diff --git a/drivers/net/wwan/t9xx/mtk_wwan.c b/drivers/net/wwan/t9xx/mtk_wwan.c
>new file mode 100644
>index 000000000000..a1534c312358
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_wwan.c
>@@ -0,0 +1,475 @@
>+// SPDX-License-Identifier: GPL-2.0-only
>+/*
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#include <linux/if_arp.h>
>+#include <linux/netdevice.h>
>+#include <linux/skbuff.h>
>+#include <linux/types.h>
>+#include <linux/wwan.h>
>+#include <net/pkt_sched.h>
>+
>+#include "mtk_data_plane.h"
>+#include "mtk_dev.h"
>+#include "mtk_wwan.h"
>+
>+#define MTK_NETDEV_MAX 20
>+#define MTK_DFLT_INTF_ID 0
>+#define MTK_NETDEV_WDT (HZ)
>+#define MTK_CMD_WDT (HZ)
>+#define MTK_MAX_INTF_ID (MTK_NETDEV_MAX - 1)
>+#define MTK_NAPI_POLL_WEIGHT 128
>+
>+static unsigned int napi_budget = MTK_NAPI_POLL_WEIGHT;
>+
>+struct mtk_wwan_instance {
>+ struct mtk_wwan_ctlb *wcb;
>+ struct net_device *netdev;
>+ unsigned int intf_id;
>+ struct mtk_data_hif_ops *hif_ops;
>+};
>+
>+struct mtk_wwan_ctlb {
>+ struct mtk_data_blk *data_blk;
>+ struct mtk_md_dev *mdev;
>+ struct mtk_wwan_instance __rcu *wwan_inst[MTK_NETDEV_MAX];
>+ struct net_device *dummy_dev;
>+ struct napi_struct **gro_napis;
>+ atomic_t napi_enabled;
>+ unsigned int active_cnt;
>+ bool reg_done;
>+};
>+
please add kdoc as exporting the func
>+int mtk_wwan_recv(struct mtk_data_blk *data_blk, struct sk_buff *skb)
>+{
>+ union mtk_data_pkt_info *pkt_info = DATA_SKB_CB(skb);
>+ struct mtk_wwan_instance *wwan_inst;
>+ unsigned char q_id;
>+
>+ if (unlikely(pkt_info->rx.ch_id > MTK_MAX_INTF_ID)) {
>+ dev_warn(data_blk->mdev->dev,
>+ "Invalid interface id=%d\n", pkt_info->rx.ch_id);
>+ goto free_skb;
>+ }
>+
>+ q_id = pkt_info->rx.q_id;
>+
>+ rcu_read_lock();
>+ wwan_inst = rcu_dereference(data_blk->wcb->wwan_inst[pkt_info->rx.ch_id]);
>+ if (unlikely(!wwan_inst)) {
>+ rcu_read_unlock();
>+ goto free_skb;
>+ }
>+
>+ skb->dev = wwan_inst->netdev;
>+
>+ napi_gro_receive(data_blk->wcb->gro_napis[q_id], skb);
>+
>+ rcu_read_unlock();
>+ return 0;
>+
>+free_skb:
>+ dev_kfree_skb_any(skb);
>+ return -EINVAL;
>+}
>+EXPORT_SYMBOL(mtk_wwan_recv);
>+
>+static void mtk_wwan_napi_enable(struct mtk_wwan_ctlb *wcb)
>+{
>+ int i;
>+
>+ if (atomic_cmpxchg(&wcb->napi_enabled, 0, 1) == 0) {
>+ for (i = 0; i < wcb->data_blk->trans_info.rxq_cnt; i++)
>+ napi_enable(wcb->data_blk->trans_info.napis[i]);
>+ }
>+}
>+
>+static void mtk_wwan_napi_disable(struct mtk_wwan_ctlb *wcb)
>+{
>+ int i;
>+
>+ if (atomic_cmpxchg(&wcb->napi_enabled, 1, 0) == 1) {
>+ for (i = 0; i < wcb->data_blk->trans_info.rxq_cnt; i++) {
>+ napi_synchronize(wcb->data_blk->trans_info.napis[i]);
>+ napi_disable(wcb->data_blk->trans_info.napis[i]);
>+ }
>+ }
>+}
>+
>+static int mtk_wwan_open(struct net_device *dev)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+ struct mtk_wwan_ctlb *wcb = wwan_inst->wcb;
>+ struct mtk_data_trans_ctl trans_ctl;
>+ int ret;
>+
>+ if (wcb->active_cnt == 0) {
>+ trans_ctl.enable = true;
>+ ret = mtk_wwan_cmd_execute(dev, DATA_CMD_TRANS_CTL, &trans_ctl);
>+ if (ret < 0) {
>+ dev_err(wcb->mdev->dev, "Failed to enable trans\n");
>+ return ret;
>+ }
>+ }
>+
>+ wcb->active_cnt++;
>+
>+ netif_tx_start_all_queues(dev);
>+ netif_carrier_on(dev);
>+
>+ return 0;
>+}
>+
>+static int mtk_wwan_stop(struct net_device *dev)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+ struct mtk_wwan_ctlb *wcb = wwan_inst->wcb;
>+ struct mtk_data_trans_ctl trans_ctl;
>+ int ret;
>+
>+ netif_carrier_off(dev);
>+ netif_tx_disable(dev);
>+
>+ if (wcb->active_cnt == 1) {
>+ trans_ctl.enable = false;
>+ ret = mtk_wwan_cmd_execute(dev, DATA_CMD_TRANS_CTL, &trans_ctl);
>+ if (ret < 0)
>+ dev_err(wcb->mdev->dev, "Failed to disable trans\n");
>+ }
>+ wcb->active_cnt--;
>+
>+ return 0;
>+}
>+
>+static netdev_tx_t mtk_wwan_start_xmit(struct sk_buff *skb, struct net_device *dev)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+ union mtk_data_pkt_info *pkt_info = DATA_SKB_CB(skb);
>+ int ret;
>+
>+ skb_set_queue_mapping(skb, 0);
>+ pkt_info->tx.intf_id = wwan_inst->intf_id;
>+
>+ ret = wwan_inst->hif_ops->send(wwan_inst->wcb->data_blk, DATA_PKT, skb);
>+ if (ret == -EBUSY)
>+ return NETDEV_TX_BUSY;
>+ else if (ret == -EINVAL)
>+ dev_kfree_skb_any(skb);
>+
>+ return NETDEV_TX_OK;
>+}
>+
>+static const struct net_device_ops mtk_netdev_ops = {
>+ .ndo_open = mtk_wwan_open,
>+ .ndo_stop = mtk_wwan_stop,
>+ .ndo_start_xmit = mtk_wwan_start_xmit,
>+};
>+
>+static int mtk_wwan_cmd_check(struct net_device *dev, enum mtk_data_cmd_type cmd)
>+{
>+ int ret = 0;
>+
>+ switch (cmd) {
>+ case DATA_CMD_TRANS_CTL:
>+ break;
>+ default:
>+ ret = -EOPNOTSUPP;
>+ break;
just return here
>+ }
>+
>+ return ret;
>+}
>+
>+static struct sk_buff *mtk_wwan_cmd_alloc(enum mtk_data_cmd_type cmd, unsigned int len)
>+{
>+ struct mtk_data_cmd *event;
>+ struct sk_buff *skb;
>+
>+ skb = dev_alloc_skb(sizeof(*event) + len);
>+ if (unlikely(!skb))
>+ return NULL;
>+
>+ skb_put(skb, len + sizeof(*event));
>+ event = (struct mtk_data_cmd *)skb->data;
>+ event->cmd = cmd;
>+ event->len = len;
>+
>+ return skb;
>+}
>+
>+static int mtk_wwan_cmd_send(struct net_device *dev, struct sk_buff *skb)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+
>+ return wwan_inst->hif_ops->send(wwan_inst->wcb->data_blk, DATA_CMD, skb);
>+}
>+
>+int mtk_wwan_cmd_execute(struct net_device *dev,
>+ enum mtk_data_cmd_type cmd, void *data)
>+{
>+ struct mtk_wwan_instance *wwan_inst;
>+ struct sk_buff *skb;
>+ int ret;
>+
>+ if (mtk_wwan_cmd_check(dev, cmd))
>+ return -EOPNOTSUPP;
>+
>+ skb = mtk_wwan_cmd_alloc(cmd, sizeof(void *));
>+ if (unlikely(!skb))
>+ return -ENOMEM;
>+
>+ SKB_TO_CMD_DATA(skb) = data;
>+
>+ ret = mtk_wwan_cmd_send(dev, skb);
>+ if (ret < 0) {
>+ wwan_inst = wwan_netdev_drvpriv(dev);
>+ dev_err(wwan_inst->wcb->mdev->dev,
>+ "Failed to excute command:ret=%d,cmd=%d\n", ret, cmd);
>+ }
>+
>+ dev_consume_skb_any(skb);
>+
>+ return ret;
>+}
>+
>+static int mtk_wwan_start_txq(struct mtk_wwan_ctlb *wcb, u32 qmask)
>+{
>+ struct mtk_wwan_instance *wwan_inst;
>+ struct net_device *dev;
>+ int i;
>+
>+ rcu_read_lock();
>+ for (i = 0; i < MTK_NETDEV_MAX; i++) {
>+ wwan_inst = rcu_dereference(wcb->wwan_inst[i]);
>+ if (!wwan_inst)
>+ continue;
>+
>+ dev = wwan_inst->netdev;
>+
>+ if (!(dev->flags & IFF_UP))
>+ continue;
>+
>+ netif_tx_wake_all_queues(dev);
>+ netif_carrier_on(dev);
>+ }
>+ rcu_read_unlock();
>+
>+ return 0;
>+}
>+
>+static int mtk_wwan_stop_txq(struct mtk_wwan_ctlb *wcb, u32 qmask)
>+{
>+ struct mtk_wwan_instance *wwan_inst;
>+ struct net_device *dev;
>+ int i;
>+
>+ rcu_read_lock();
>+ for (i = 0; i < MTK_NETDEV_MAX; i++) {
>+ wwan_inst = rcu_dereference(wcb->wwan_inst[i]);
>+ if (!wwan_inst)
>+ continue;
>+
>+ dev = wwan_inst->netdev;
>+
>+ if (!(dev->flags & IFF_UP))
>+ continue;
>+
>+ netif_carrier_off(dev);
>+ netif_tx_stop_all_queues(dev);
>+ }
>+ rcu_read_unlock();
>+
>+ return 0;
>+}
>+
>+static void mtk_wwan_napi_exit(struct mtk_wwan_ctlb *wcb)
>+{
>+ int i;
>+
>+ for (i = 0; i < wcb->data_blk->trans_info.rxq_cnt; i++) {
>+ if (!wcb->data_blk->trans_info.napis[i])
>+ continue;
>+ netif_napi_del(wcb->data_blk->trans_info.napis[i]);
>+ }
>+}
>+
>+static int mtk_wwan_napi_init(struct mtk_wwan_ctlb *wcb, struct net_device *dev)
>+{
>+ int i;
>+
>+ for (i = 0; i < wcb->data_blk->trans_info.rxq_cnt; i++) {
>+ if (!wcb->data_blk->trans_info.napis[i]) {
>+ dev_err(wcb->mdev->dev, "Invalid napi pointer, napi=%d\n", i);
>+ goto out;
>+ }
>+ netif_napi_add_weight(dev, wcb->data_blk->trans_info.napis[i],
>+ wcb->data_blk->hif_ops->poll, napi_budget);
>+ }
>+
>+ return 0;
>+
>+out:
>+ for (--i; i >= 0; i--)
>+ netif_napi_del(wcb->data_blk->trans_info.napis[i]);
>+ return -EINVAL;
>+}
>+
>+static void mtk_wwan_setup(struct net_device *dev)
>+{
>+ dev->watchdog_timeo = MTK_NETDEV_WDT;
>+ dev->mtu = ETH_DATA_LEN;
>+ dev->min_mtu = ETH_MIN_MTU;
>+
>+ dev->features = NETIF_F_SG;
>+ dev->hw_features = NETIF_F_SG;
>+
>+ dev->features |= NETIF_F_GRO;
>+ dev->hw_features |= NETIF_F_GRO;
could be squashed
>+
>+ dev->tx_queue_len = DEFAULT_TX_QUEUE_LEN;
>+
>+ dev->flags = IFF_NOARP;
>+ dev->type = ARPHRD_NONE;
>+
>+ dev->needs_free_netdev = true;
>+
>+ dev->netdev_ops = &mtk_netdev_ops;
>+}
>+
>+static int mtk_wwan_newlink(void *ctxt, struct net_device *dev, u32 intf_id,
>+ struct netlink_ext_ack *extack)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+ struct mtk_wwan_ctlb *wcb = ctxt;
>+ int ret;
>+
>+ if (intf_id > MTK_MAX_INTF_ID)
>+ return -EINVAL;
>+
>+ if (rcu_access_pointer(wcb->wwan_inst[intf_id]))
>+ return -EBUSY;
>+
>+ dev->max_mtu = wcb->data_blk->trans_info.max_mtu;
>+
>+ wwan_inst->wcb = wcb;
>+ wwan_inst->netdev = dev;
>+ wwan_inst->intf_id = intf_id;
>+ wwan_inst->hif_ops = wcb->data_blk->hif_ops;
>+
>+ ret = register_netdevice(dev);
>+ if (ret)
>+ return ret;
>+
>+ rcu_assign_pointer(wcb->wwan_inst[intf_id], wwan_inst);
>+
>+ netif_device_attach(dev);
>+
>+ return 0;
>+}
>+
>+static void mtk_wwan_dellink(void *ctxt, struct net_device *dev,
>+ struct list_head *head)
>+{
>+ struct mtk_wwan_instance *wwan_inst = wwan_netdev_drvpriv(dev);
>+ int intf_id = wwan_inst->intf_id;
>+ struct mtk_wwan_ctlb *wcb = ctxt;
>+
>+ if (WARN_ON(rcu_access_pointer(wcb->wwan_inst[intf_id]) != wwan_inst))
>+ return;
>+
>+ RCU_INIT_POINTER(wcb->wwan_inst[intf_id], NULL);
>+ unregister_netdevice_queue(dev, head);
>+}
>+
>+static const struct wwan_ops mtk_wwan_ops = {
>+ .priv_size = sizeof(struct mtk_wwan_instance),
>+ .setup = mtk_wwan_setup,
>+ .newlink = mtk_wwan_newlink,
>+ .dellink = mtk_wwan_dellink,
>+};
>+
>+void mtk_wwan_notify(struct mtk_data_blk *data_blk, enum mtk_data_evt evt, u64 data)
>+{
>+ struct mtk_wwan_ctlb *wcb;
>+
>+ if (unlikely(!data_blk || !data_blk->wcb))
>+ return;
>+
>+ wcb = data_blk->wcb;
>+
>+ switch (evt) {
>+ case DATA_EVT_TX_START:
>+ mtk_wwan_start_txq(wcb, data);
>+ break;
>+ case DATA_EVT_TX_STOP:
>+ mtk_wwan_stop_txq(wcb, data);
>+ break;
>+ case DATA_EVT_RX_START:
>+ mtk_wwan_napi_enable(wcb);
>+ break;
>+ case DATA_EVT_RX_STOP:
>+ mtk_wwan_napi_disable(wcb);
>+ break;
>+ case DATA_EVT_REG_DEV:
>+ if (!wcb->reg_done) {
>+ wwan_register_ops(wcb->mdev->dev, &mtk_wwan_ops, wcb, MTK_DFLT_INTF_ID);
>+ wcb->reg_done = true;
>+ }
>+ break;
>+ case DATA_EVT_UNREG_DEV:
>+ if (wcb->reg_done) {
>+ wwan_unregister_ops(wcb->mdev->dev);
>+ wcb->reg_done = false;
>+ }
>+ break;
>+ default:
no need to at least log that received an unsupported evt?
>+ break;
>+ }
>+}
>+EXPORT_SYMBOL(mtk_wwan_notify);
>+
>+int mtk_wwan_init(struct mtk_data_blk *data_blk)
>+{
>+ struct mtk_wwan_ctlb *wcb;
>+ int ret;
>+
>+ wcb = devm_kzalloc(data_blk->mdev->dev, sizeof(*wcb), GFP_KERNEL);
>+ if (unlikely(!wcb))
>+ return -ENOMEM;
>+
>+ wcb->mdev = data_blk->mdev;
>+ wcb->data_blk = data_blk;
>+
>+ wcb->dummy_dev = alloc_netdev_dummy(0);
>+ if (!wcb->dummy_dev) {
>+ devm_kfree(data_blk->mdev->dev, wcb);
>+ return -ENOMEM;
>+ }
>+
>+ data_blk->wcb = wcb;
>+
>+ wcb->gro_napis = data_blk->trans_info.napis;
>+ ret = mtk_wwan_napi_init(wcb, wcb->dummy_dev);
>+ if (ret < 0) {
>+ free_netdev(wcb->dummy_dev);
>+ devm_kfree(data_blk->mdev->dev, wcb);
>+ data_blk->wcb = NULL;
>+ return ret;
>+ }
>+
>+ return 0;
>+}
>+
>+void mtk_wwan_exit(struct mtk_data_blk *data_blk)
>+{
>+ struct mtk_wwan_ctlb *wcb = data_blk->wcb;
>+
>+ if (unlikely(!wcb))
was it verified how adding unlikely impacts performance?
>+ return;
>+
>+ mtk_wwan_napi_exit(wcb);
>+ free_netdev(wcb->dummy_dev);
>+ devm_kfree(data_blk->mdev->dev, wcb);
>+ data_blk->wcb = NULL;
>+}
>diff --git a/drivers/net/wwan/t9xx/mtk_wwan.h b/drivers/net/wwan/t9xx/mtk_wwan.h
>new file mode 100644
>index 000000000000..8005aeb1ed97
>--- /dev/null
>+++ b/drivers/net/wwan/t9xx/mtk_wwan.h
>@@ -0,0 +1,17 @@
>+/* SPDX-License-Identifier: GPL-2.0-only
>+ *
>+ * Copyright (c) 2022, MediaTek Inc.
>+ */
>+
>+#ifndef __MTK_WWAN_H__
>+#define __MTK_WWAN_H__
>+#include <linux/netdevice.h>
>+#include "mtk_data_plane.h"
>+
>+int mtk_wwan_init(struct mtk_data_blk *data_blk);
>+void mtk_wwan_exit(struct mtk_data_blk *data_blk);
>+int mtk_wwan_recv(struct mtk_data_blk *data_blk, struct sk_buff *skb);
>+void mtk_wwan_notify(struct mtk_data_blk *data_blk, enum mtk_data_evt evt, u64 data);
>+int mtk_wwan_cmd_execute(struct net_device *dev, enum mtk_data_cmd_type cmd, void *data);
>+
>+#endif /* __MTK_WWAN_H__ */
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>index 1c58dba738fa..43803587bfc3 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>@@ -20,9 +20,11 @@
> #include "mtk_dev.h"
> #include "mtk_dpmaif.h"
> #include "mtk_dpmaif_drv.h"
>+#include "mtk_dpmaif_reg.h"
> #include "mtk_dpmaif_ring.h"
> #include "mtk_pci_reg.h"
> #include "mtk_pci.h"
>+#include "mtk_wwan.h"
>
> #define MTK_DATA_WS_NAME_LEN 32
> #define DPMAIF_PIT_CNT_UPDATE_THRESHOLD 60
>@@ -741,6 +743,7 @@ static void mtk_dpmaif_sw_stop_rx(struct mtk_dpmaif_ctlb *dcb)
> }
>
> /* Stop PIT polling NAPI. */
>+ mtk_wwan_notify(dcb->data_blk, DATA_EVT_RX_STOP, 0xff);
> }
>
> static void mtk_dpmaif_sw_start_rx(struct mtk_dpmaif_ctlb *dcb)
>@@ -749,6 +752,7 @@ static void mtk_dpmaif_sw_start_rx(struct mtk_dpmaif_ctlb *dcb)
> int i;
>
> /* Start PIT polling NAPI. */
>+ mtk_wwan_notify(dcb->data_blk, DATA_EVT_RX_START, 0xff);
>
> for (i = 0; i < dcb->rxq_cnt; i++) {
> rxq = &dcb->rxqs[i];
>@@ -910,6 +914,7 @@ static int mtk_dpmaif_tx_rel_internal(struct dpmaif_txq *txq,
> tx_srv = &dcb->tx_srvs[srv_id];
> clear_bit(txq->id, &tx_srv->txq_drb_lack_sta);
> wake_up(&tx_srv->wait);
>+ mtk_wwan_notify(dcb->data_blk, DATA_EVT_TX_START, (u64)1 << txq->id);
> }
>
> return 0;
>@@ -1426,6 +1431,7 @@ static void mtk_dpmaif_tx_update_ring(struct mtk_dpmaif_ctlb *dcb, struct dpmaif
> if (drb_available_cnt < skb_drb_cnt) {
> skb_queue_head(&vq->list, skb);
> set_bit(q_id, &tx_srv->txq_drb_lack_sta);
>+ mtk_wwan_notify(dcb->data_blk, DATA_EVT_TX_STOP, (u64)1 << q_id);
> break;
> }
>
>@@ -2346,6 +2352,7 @@ static int mtk_dpmaif_update_rx_skb_info(struct sk_buff *skb,
>
> static int mtk_dpmaif_rx_skb(struct dpmaif_rxq *rxq, struct dpmaif_rx_record *rx_record)
> {
>+ struct mtk_dpmaif_ctlb *dcb = rxq->dcb;
> struct sk_buff *new_skb;
> int ret;
>
>@@ -2362,7 +2369,7 @@ static int mtk_dpmaif_rx_skb(struct dpmaif_rxq *rxq, struct dpmaif_rx_record *rx
> continue;
>
> /* Send skb to data port. */
>- /* Data would be sent to network stack here */
>+ mtk_wwan_recv(dcb->data_blk, new_skb);
> } while (!skb_queue_empty(&rx_record->rx_list));
>
> return ret;
>@@ -2600,10 +2607,12 @@ static int mtk_dpmaif_send_pkt(struct mtk_dpmaif_ctlb *dcb, struct sk_buff *skb)
> int ret = 0;
>
> vq = &dcb->tx_vqs[vq_id];
>- if (likely(skb_queue_len(&vq->list) < vq->max_len))
>+ if (likely(skb_queue_len(&vq->list) < vq->max_len)) {
> skb_queue_tail(&vq->list, skb);
>- else
>+ } else {
>+ mtk_wwan_notify(dcb->data_blk, DATA_EVT_TX_STOP, (u64)1 << vq_id);
> ret = -EBUSY;
>+ }
>
> wake_up(&dcb->tx_srvs[vq->srv_id].wait);
>
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif_drv.c b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif_drv.c
>index 077600389ab4..3fd2b33d1199 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif_drv.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif_drv.c
>@@ -8,7 +8,6 @@
> #include <linux/delay.h>
>
> #include "mtk_dev.h"
>-#include "mtk_data_plane.h"
> #include "mtk_dpmaif_drv.h"
> #include "mtk_dpmaif_reg.h"
> #include "mtk_pci.h"
>
>--
>2.34.1
^ permalink raw reply
* RE: [PATCH 10/11] net: wwan: t9xx: Add power management support
From: Jagielski, Jedrzej @ 2026-06-01 12:26 UTC (permalink / raw)
To: jackbb_wu@compal.com, Loic Poulain, Sergey Ryazanov,
Johannes Berg, Andrew Lunn, David S. Miller, Eric Dumazet,
Jakub Kicinski, Paolo Abeni, Wen-Zhi Huang, Shi-Wei Yeh,
Minano Tseng, Matthias Brugger, AngeloGioacchino Del Regno,
Simon Horman, Jonathan Corbet, Shuah Khan
Cc: linux-kernel@vger.kernel.org, netdev@vger.kernel.org,
linux-arm-kernel@lists.infradead.org,
linux-mediatek@lists.infradead.org, linux-doc@vger.kernel.org
In-Reply-To: <20260529-t9xx_driver_v1-v1-10-bdbfe2c01e57@compal.com>
From: Jack Wu via B4 Relay <devnull+jackbb_wu.compal.com@kernel.org>
Sent: Friday, May 29, 2026 12:32 PM
>From: Jack Wu <jackbb_wu@compal.com>
>
>Add s2idle (S0ix) power management support for the t9xx WWAN driver.
>
>In s2idle the modem remains powered. The driver must quiesce host-side
>DMA engines and service threads before the platform enters low-power
>state, then restore them on resume.
>
>- Suspend: park TRB service threads, stop CLDMA TX/RX queues,
> disable DPMAIF data path, mask MHCCIF and MSIX interrupts,
> save PCIe state
>- Resume: restore PCIe state, re-initialize ATR, unmask MHCCIF,
> resume CLDMA queues, re-enable DPMAIF data path, unpark TRB
> service threads
>
>Signed-off-by: Jack Wu <jackbb_wu@compal.com>
>---
> drivers/net/wwan/t9xx/pcie/mtk_cldma.c | 94 +++++++++++++++++++++++++++++++++
> drivers/net/wwan/t9xx/pcie/mtk_cldma.h | 3 ++
> drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c | 35 +++++++++++-
> drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h | 2 +
> drivers/net/wwan/t9xx/pcie/mtk_pci.c | 85 ++++++++++++++++++++++++++++-
> 5 files changed, 216 insertions(+), 3 deletions(-)
>
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
>index aacb4177d914..a5227eb546f4 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.c
>@@ -1113,6 +1113,100 @@ int mtk_cldma_exit(struct mtk_ctrl_trans *trans)
> return 0;
> }
>
>+void mtk_cldma_pm_suspend(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv;
>+ struct cldma_dev *cd = trans->dev;
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ struct rxq *rxq;
>+ struct txq *txq;
>+ int i, q;
>+
>+ for (i = 0; i < NR_CLDMA; i++) {
>+ drv_info = cd->cldma_drv_info[i];
>+ if (!drv_info)
>+ continue;
>+
>+ drv_ops = drv_info->drv_ops;
>+
>+ /* Stop TX queues and flush pending tx_done_work (suspend phase) */
>+ drv_ops->cldma_stop_queue(drv_info, DIR_TX, ALLQ);
>+ for (q = 0; q < HW_QUEUE_NUM; q++) {
>+ txq = drv_info->txq[q];
>+ if (txq)
>+ flush_work(&txq->tx_done_work);
>+ }
>+
>+ for (q = 0; q < HW_QUEUE_NUM; q++) {
>+ rxq = drv_info->rxq[q];
>+ if (!rxq)
>+ continue;
>+ atomic_set(&rxq->need_exit, 1);
>+ drv_ops->cldma_stop_queue(drv_info, DIR_RX, q);
>+ flush_work(&rxq->rx_done_work);
>+ }
>+ mtk_pci_mask_irq(mdev, drv_info->pci_ext_irq_id);
>+ }
>+}
>+
>+void mtk_cldma_pm_resume_early(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv;
>+ struct cldma_dev *cd = trans->dev;
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ struct rxq *rxq;
>+ int i, q;
>+
>+ for (i = 0; i < NR_CLDMA; i++) {
>+ drv_info = cd->cldma_drv_info[i];
>+ if (!drv_info)
>+ continue;
>+
>+ drv_ops = drv_info->drv_ops;
>+
>+ /* Resume RX queues from current HW ring position (no addr reset) */
>+ for (q = 0; q < HW_QUEUE_NUM; q++) {
>+ rxq = drv_info->rxq[q];
>+ if (!rxq)
>+ continue;
>+ atomic_set(&rxq->need_exit, 0);
>+ drv_ops->cldma_resume_queue(drv_info, DIR_RX, q);
>+ }
>+
>+ /* Unmask CLDMA L1 interrupt */
>+ mtk_pci_unmask_irq(mdev, drv_info->pci_ext_irq_id);
>+ }
>+}
>+
>+void mtk_cldma_pm_resume(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv;
>+ struct cldma_dev *cd = trans->dev;
>+ struct cldma_drv_info *drv_info;
>+ struct cldma_drv_ops *drv_ops;
>+ struct txq *txq;
>+ int i, q;
>+
>+ for (i = 0; i < NR_CLDMA; i++) {
>+ drv_info = cd->cldma_drv_info[i];
>+ if (!drv_info)
>+ continue;
>+
>+ drv_ops = drv_info->drv_ops;
>+
>+ /* Restart TX queues that have pending descriptors */
>+ for (q = 0; q < HW_QUEUE_NUM; q++) {
>+ txq = drv_info->txq[q];
>+ if (!txq)
>+ continue;
>+ if (atomic_read(&txq->req_budget) < txq->nr_gpds)
>+ mtk_cldma_start_xfer(drv_info, q);
>+ }
>+ }
>+}
>+
> 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;
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
>index 04f83ff0e37d..fd39985f75e7 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_cldma.h
>@@ -163,6 +163,9 @@ 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);
>+void mtk_cldma_pm_suspend(struct mtk_md_dev *mdev);
>+void mtk_cldma_pm_resume_early(struct mtk_md_dev *mdev);
>+void mtk_cldma_pm_resume(struct mtk_md_dev *mdev);
>
> #define drv_ops_name(NAME) cldma_drv_ops_##NAME
> #define cldma_regs_name(NAME) mtk_cldma_regs_##NAME
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>index 43803587bfc3..63273a85e532 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.c
>@@ -205,6 +205,7 @@ struct mtk_dpmaif_ctlb {
> struct dpmaif_irq_param *irq_params;
>
> bool dpmaif_sw_reset;
>+ bool trans_enabled;
> unsigned char rxq_cnt;
> unsigned char txq_cnt;
> };
>@@ -1687,10 +1688,16 @@ static void mtk_dpmaif_trans_disable(struct mtk_dpmaif_ctlb *dcb)
> static void mtk_dpmaif_trans_ctl(struct mtk_dpmaif_ctlb *dcb, bool enable)
> {
> if (enable) {
>- if (dcb->dpmaif_state == DPMAIF_STATE_PWRON)
>+ if (!dcb->trans_enabled &&
>+ dcb->dpmaif_state == DPMAIF_STATE_PWRON) {
>+ dcb->trans_enabled = true;
> mtk_dpmaif_trans_enable(dcb);
>+ }
> } else {
>- mtk_dpmaif_trans_disable(dcb);
>+ if (dcb->trans_enabled) {
should it be done unconditionally from DPMAIF_STATE_PWRON?
>+ dcb->trans_enabled = false;
>+ mtk_dpmaif_trans_disable(dcb);
>+ }
> }
> }
>
>@@ -2060,6 +2067,30 @@ static int mtk_dpmaif_stop(struct mtk_md_dev *mdev)
> return 0;
> }
>
>+void mtk_dpmaif_pm_suspend(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb;
>+
>+ if (!dcb)
>+ return;
>+
>+ mutex_lock(&dcb->trans_ctl_lock);
>+ mtk_dpmaif_trans_ctl(dcb, false);
>+ mutex_unlock(&dcb->trans_ctl_lock);
>+}
>+
>+void mtk_dpmaif_pm_resume(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb;
>+
>+ if (!dcb)
>+ return;
>+
>+ mutex_lock(&dcb->trans_ctl_lock);
>+ mtk_dpmaif_trans_ctl(dcb, true);
>+ mutex_unlock(&dcb->trans_ctl_lock);
>+}
>+
> static void mtk_dpmaif_clear(struct mtk_md_dev *mdev)
> {
> struct mtk_dpmaif_ctlb *dcb = ((struct mtk_data_blk *)(mdev->data_blk))->dcb;
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h
>index e7e2f333141c..20fd53fd44b5 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_dpmaif.h
>@@ -10,5 +10,7 @@
>
> int mtk_pcie_data_init(struct mtk_md_dev *mdev);
> int mtk_pcie_data_exit(struct mtk_md_dev *mdev);
>+void mtk_dpmaif_pm_suspend(struct mtk_md_dev *mdev);
>+void mtk_dpmaif_pm_resume(struct mtk_md_dev *mdev);
>
> #endif /* __MTK_DPMAIF_H__ */
>diff --git a/drivers/net/wwan/t9xx/pcie/mtk_pci.c b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>index baac3692f1e3..f659c9a7aa96 100644
>--- a/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>+++ b/drivers/net/wwan/t9xx/pcie/mtk_pci.c
>@@ -11,8 +11,10 @@
> #include <linux/device.h>
> #include <linux/dma-mapping.h>
> #include <linux/kernel.h>
>+#include <linux/kthread.h>
> #include <linux/module.h>
>
>+#include "mtk_cldma.h"
> #include "mtk_dev.h"
> #include "mtk_dpmaif.h"
> #include "mtk_trans_ctrl.h"
>@@ -199,7 +201,6 @@ int mtk_pci_register_irq(struct mtk_md_dev *mdev, int irq_id,
> }
> priv->irq_cb_list[irq_id] = irq_cb;
> priv->irq_cb_data[irq_id] = data;
>-
> return 0;
> }
>
>@@ -970,11 +971,93 @@ static const struct pci_error_handlers mtk_pci_err_handler = {
> .error_detected = mtk_pci_error_detected,
> };
>
>+static void mtk_pci_pm_trb_park(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv;
>+ int i;
>+
>+ for (i = 0; i < trans->trb_srv_num; i++)
>+ kthread_park(trans->trb_srv[i]->trb_thread);
>+}
>+
>+static void mtk_pci_pm_trb_unpark(struct mtk_md_dev *mdev)
>+{
>+ struct mtk_ctrl_trans *trans = ((struct mtk_ctrl_blk *)mdev->ctrl_blk)->ctrl_hw_priv;
>+ int i;
>+
>+ for (i = 0; i < trans->trb_srv_num; i++)
>+ kthread_unpark(trans->trb_srv[i]->trb_thread);
>+}
>+
>+static int __maybe_unused mtk_pci_pm_suspend(struct device *dev)
>+{
>+ struct pci_dev *pdev = to_pci_dev(dev);
>+ struct mtk_md_dev *mdev = pci_get_drvdata(pdev);
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
RCT
>+
>+ mtk_pci_pm_trb_park(mdev);
>+
>+ mtk_cldma_pm_suspend(mdev);
>+
>+ mtk_dpmaif_pm_suspend(mdev);
>+
>+ /* Mask MHCCIF interrupt */
>+ mtk_pci_mask_irq(mdev, priv->mhccif_irq_id);
>+
>+ /* Mask all MSI-X interrupts at the device level */
>+ mtk_pci_mac_write32(priv, REG_IMASK_HOST_MSIX_CLR_GRP0_0, U32_MAX);
>+
>+ /* Save PCI configuration space */
>+ pci_save_state(pdev);
>+
>+ return 0;
>+}
>+
>+static int __maybe_unused mtk_pci_pm_resume(struct device *dev)
>+{
>+ struct pci_dev *pdev = to_pci_dev(dev);
>+ struct mtk_md_dev *mdev = pci_get_drvdata(pdev);
>+ struct mtk_pci_priv *priv = mdev->hw_priv;
>+ int ret;
>+
>+ /* Restore PCIe configuration space (including MSI-X enable bits) */
these comments are rather obvious
>+ pci_restore_state(pdev);
>+
>+ /* Re-enable bus mastering for DMA */
>+ pci_set_master(pdev);
>+
>+ /* Restore ATR (address translation registers in MMIO BAR space) */
>+ ret = priv->cfg->atr_init(mdev);
>+ if (ret) {
>+ dev_err(mdev->dev, "PM: failed to re-init ATR on resume\n");
>+ return ret;
>+ }
>+
>+ /* Unmask MHCCIF interrupt */
>+ mtk_pci_unmask_irq(mdev, priv->mhccif_irq_id);
>+
>+ mtk_cldma_pm_resume_early(mdev);
>+
>+ /* Restart CLDMA TX queues that have pending descriptors */
>+ mtk_cldma_pm_resume(mdev);
>+
>+ mtk_dpmaif_pm_resume(mdev);
>+
>+ mtk_pci_pm_trb_unpark(mdev);
>+
>+ return 0;
>+}
>+
>+static const struct dev_pm_ops mtk_pci_pm_ops = {
>+ SET_SYSTEM_SLEEP_PM_OPS(mtk_pci_pm_suspend, mtk_pci_pm_resume)
>+};
>+
> static struct pci_driver mtk_pci_drv = {
> .name = "mtk_pci_drv",
> .id_table = t9xx_pci_table,
> .probe = mtk_pci_probe,
> .remove = mtk_pci_remove,
>+ .driver.pm = &mtk_pci_pm_ops,
> .err_handler = &mtk_pci_err_handler
> };
>
>
>--
>2.34.1
^ permalink raw reply
* Re: [PATCH v2 0/4] MediaTek MT6735+MT6328 SoC/PMIC pair base support
From: Mark Brown @ 2026-06-01 13:16 UTC (permalink / raw)
To: y.oudjana
Cc: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Flora Fu, Alexandre Mergnat,
Liam Girdwood, Dmitry Torokhov, devicetree, linux-kernel,
linux-arm-kernel, linux-mediatek, linux-input
In-Reply-To: <20260531-mt6735-pwrap-mt6328-pmic-v2-0-dac37bf92894@protonmail.com>
[-- Attachment #1: Type: text/plain, Size: 484 bytes --]
On Sun, May 31, 2026 at 11:10:41AM +0200, Yassine Oudjana via B4 Relay wrote:
> These patches are part of a larger effort to support the MT6735 SoC family in
> mainline Linux. More patches (unsent or sent and pending review or revision)
> can be found here[1].
Please send patches for unrelated subsystems as separate serieses to
each subsystem unless there's a depdendency, making everything into a
cross subsystem series just makes it harder to work out what's supposed
to happen.
[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 488 bytes --]
^ permalink raw reply
* [PATCH 0/3] Mediatek Genio EVK: add MT6365 PMIC supplies
From: Louis-Alexis Eyraud @ 2026-06-01 14:15 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: kernel, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, Louis-Alexis Eyraud
Several Mediatek and Radxa boards, based on MT8370, MT8390 or MT8395
SoC, integrate the MT6365 PMIC, that is a MT6359P variant.
This PMIC dt-bindings for recently gained property definitions for the
power inputs for its various buck and LDO regulators ([1]).
The goal of this series is to add the supplies for this PMIC regulators
in the following board devicetrees:
- MTK Genio 510-EVK
- MTK Genio 700-EVK
- MTK Genio 1200-EVK
- Radxa NIO-12L
The series is based on linux-next tree (tag: next-20260529) and has
been tested on Mediatek Genio 510, 700 and 1200-EVK boards.
[1]: https://lore.kernel.org/linux-mediatek/20260514091520.2718987-2-wenst@chromium.org/
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
Louis-Alexis Eyraud (3):
arm64: dts: mediatek: mt8390-genio-common: Add MT6365 PMIC supplies
arm64: dts: mediatek: mt8395-genio-common: Add MT6365 PMIC supplies
arm64: dts: mediatek: mt8395-radxa-nio-12l: Add MT6365 PMIC supplies
arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi | 17 +++++++++++++++++
arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi | 17 +++++++++++++++++
arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 17 +++++++++++++++++
3 files changed, 51 insertions(+)
---
base-commit: 697a0e31ee66f5ddb929c09895139779fff33f20
change-id: 20260601-mtk-genio-mt6359-pmic-supplies-6ac0686d4228
Best regards,
--
Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
^ permalink raw reply
* [PATCH 1/3] arm64: dts: mediatek: mt8390-genio-common: Add MT6365 PMIC supplies
From: Louis-Alexis Eyraud @ 2026-06-01 14:15 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: kernel, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, Louis-Alexis Eyraud
In-Reply-To: <20260601-mtk-genio-mt6359-pmic-supplies-v1-0-05750080ba59@collabora.com>
Mediatek Genio 510 and 700 EVK boards integrate a MT6365 PMIC, that has
a number of power inputs for its various buck and LDO regulators.
Add the supplies for this PMIC regulators to the board common
definition include file.
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi
index b0c97930a0e6..442205cf0bc1 100644
--- a/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8390-genio-common.dtsi
@@ -1304,6 +1304,23 @@ ethernet_phy0: ethernet-phy@1 {
&pmic {
interrupt-parent = <&pio>;
interrupts = <222 IRQ_TYPE_LEVEL_HIGH>;
+ vsys-smps-supply = <®_vsys>;
+ vsys-vcore-supply = <®_vsys>;
+ vsys-vgpu11-supply = <®_vsys>;
+ vsys-vgpu12-supply = <®_vsys>;
+ vsys-vpa-supply = <®_vsys>;
+ vsys-vproc1-supply = <®_vsys>;
+ vsys-vproc2-supply = <®_vsys>;
+ vsys-vpu-supply = <®_vsys>;
+ vsys-vs1-supply = <®_vsys>;
+ vsys-vs2-supply = <®_vsys>;
+ vsys-vmodem-supply = <®_vsys>;
+ vsys-ldo1-supply = <®_vsys>;
+ vsys-ldo2-supply = <®_vsys>;
+ vs1-ldo1-supply = <&mt6359_vs1_buck_reg>;
+ vs1-ldo2-supply = <&mt6359_vs1_buck_reg>;
+ vs2-ldo1-supply = <&mt6359_vs2_buck_reg>;
+ vs2-ldo2-supply = <&mt6359_vs2_buck_reg>;
mt6365keys: keys {
compatible = "mediatek,mt6365-keys", "mediatek,mt6359-keys";
--
2.54.0
^ permalink raw reply related
* [PATCH 3/3] arm64: dts: mediatek: mt8395-radxa-nio-12l: Add MT6365 PMIC supplies
From: Louis-Alexis Eyraud @ 2026-06-01 14:15 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: kernel, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, Louis-Alexis Eyraud
In-Reply-To: <20260601-mtk-genio-mt6359-pmic-supplies-v1-0-05750080ba59@collabora.com>
Radxa NIO-12L EVK board integrates a MT6365 PMIC, that has a number of
power inputs for its various buck and LDO regulators.
Add the supplies for this PMIC regulators to the board devicetree file.
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
index b279eed1bfc3..9cf0315da035 100644
--- a/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
+++ b/arch/arm64/boot/dts/mediatek/mt8395-radxa-nio-12l.dts
@@ -1033,6 +1033,23 @@ &pciephy {
&pmic {
interrupts-extended = <&pio 222 IRQ_TYPE_LEVEL_HIGH>;
+ vsys-smps-supply = <&vsys>;
+ vsys-vcore-supply = <&vsys>;
+ vsys-vgpu11-supply = <&vsys>;
+ vsys-vgpu12-supply = <&vsys>;
+ vsys-vpa-supply = <&vsys>;
+ vsys-vproc1-supply = <&vsys>;
+ vsys-vproc2-supply = <&vsys>;
+ vsys-vpu-supply = <&vsys>;
+ vsys-vs1-supply = <&vsys>;
+ vsys-vs2-supply = <&vsys>;
+ vsys-vmodem-supply = <&vsys>;
+ vsys-ldo1-supply = <&vsys_buck>;
+ vsys-ldo2-supply = <&vsys_buck>;
+ vs1-ldo1-supply = <&mt6359_vs1_buck_reg>;
+ vs1-ldo2-supply = <&mt6359_vs1_buck_reg>;
+ vs2-ldo1-supply = <&mt6359_vs2_buck_reg>;
+ vs2-ldo2-supply = <&mt6359_vs2_buck_reg>;
mt6365keys: keys {
compatible = "mediatek,mt6365-keys", "mediatek,mt6359-keys";
--
2.54.0
^ permalink raw reply related
* [PATCH 2/3] arm64: dts: mediatek: mt8395-genio-common: Add MT6365 PMIC supplies
From: Louis-Alexis Eyraud @ 2026-06-01 14:15 UTC (permalink / raw)
To: Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno
Cc: kernel, devicetree, linux-kernel, linux-arm-kernel,
linux-mediatek, Louis-Alexis Eyraud
In-Reply-To: <20260601-mtk-genio-mt6359-pmic-supplies-v1-0-05750080ba59@collabora.com>
Mediatek Genio 1200 EVK board integrates a MT6365 PMIC, that has a
number of power inputs for its various buck and LDO regulators.
Add the supplies for this PMIC regulators to the board common
definition include file.
Signed-off-by: Louis-Alexis Eyraud <louisalexis.eyraud@collabora.com>
---
arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi | 17 +++++++++++++++++
1 file changed, 17 insertions(+)
diff --git a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi
index edc5539bebde..7ea45b6bc663 100644
--- a/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi
+++ b/arch/arm64/boot/dts/mediatek/mt8395-genio-common.dtsi
@@ -1170,6 +1170,23 @@ pins {
&pmic {
interrupts-extended = <&pio 222 IRQ_TYPE_LEVEL_HIGH>;
+ vsys-smps-supply = <®_vsys>;
+ vsys-vcore-supply = <®_vsys>;
+ vsys-vgpu11-supply = <®_vsys>;
+ vsys-vgpu12-supply = <®_vsys>;
+ vsys-vpa-supply = <®_vsys>;
+ vsys-vproc1-supply = <®_vsys>;
+ vsys-vproc2-supply = <®_vsys>;
+ vsys-vpu-supply = <®_vsys>;
+ vsys-vs1-supply = <®_vsys>;
+ vsys-vs2-supply = <®_vsys>;
+ vsys-vmodem-supply = <®_vsys>;
+ vsys-ldo1-supply = <®_vsys_buck>;
+ vsys-ldo2-supply = <®_vsys_buck>;
+ vs1-ldo1-supply = <&mt6359_vs1_buck_reg>;
+ vs1-ldo2-supply = <&mt6359_vs1_buck_reg>;
+ vs2-ldo1-supply = <&mt6359_vs2_buck_reg>;
+ vs2-ldo2-supply = <&mt6359_vs2_buck_reg>;
mt6365keys: keys {
compatible = "mediatek,mt6365-keys", "mediatek,mt6359-keys";
--
2.54.0
^ permalink raw reply related
* [PATCH] dt-bindings: pinctrl: mediatek: mt6795: document the slew-rate property
From: Luca Leonardo Scorcia @ 2026-06-01 15:26 UTC (permalink / raw)
To: linux-mediatek
Cc: Luca Leonardo Scorcia, Linus Walleij, Rob Herring,
Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
AngeloGioacchino Del Regno, Sean Wang, linux-gpio, devicetree,
linux-kernel, linux-arm-kernel
The driver for MT6795 pinctrl already supports the slew-rate property.
Add its description to the documentation.
Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
---
.../bindings/pinctrl/mediatek,mt6795-pinctrl.yaml | 8 ++++++++
1 file changed, 8 insertions(+)
diff --git a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml
index 68e91c05f122..9a937f414cc9 100644
--- a/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/mediatek,mt6795-pinctrl.yaml
@@ -152,6 +152,14 @@ patternProperties:
$ref: /schemas/types.yaml#/definitions/uint32
enum: [0, 1, 2, 3]
+ slew-rate:
+ description: |
+ Set the slew rate. Valid arguments are described as below:
+ 0: Normal slew rate
+ 1: Slower slew
+ $ref: /schemas/types.yaml#/definitions/uint32
+ enum: [0, 1]
+
required:
- pinmux
--
2.43.0
^ permalink raw reply related
* Re: [PATCH] dt-bindings: pinctrl: mediatek: mt6795: document the slew-rate property
From: Linus Walleij @ 2026-06-01 17:53 UTC (permalink / raw)
To: Luca Leonardo Scorcia
Cc: linux-mediatek, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
Matthias Brugger, AngeloGioacchino Del Regno, Sean Wang,
linux-gpio, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <20260601152707.29039-1-l.scorcia@gmail.com>
Hi Luca,
thanks for your patch!
On Mon, Jun 1, 2026 at 5:27 PM Luca Leonardo Scorcia
<l.scorcia@gmail.com> wrote:
> The driver for MT6795 pinctrl already supports the slew-rate property.
> Add its description to the documentation.
>
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
(...)
> + slew-rate:
> + description: |
> + Set the slew rate. Valid arguments are described as below:
> + 0: Normal slew rate
> + 1: Slower slew
> + $ref: /schemas/types.yaml#/definitions/uint32
> + enum: [0, 1]
Ideally slew-rate is V/s (or V/us, any suitable prefixes).
Of course that would vary for any capacitance on the output line,
so that value would be for an infinite impedance (open circuit).
Do you know that value for the SoC to you can express it like
this and translate it in the driver?
Maybe we should even invent something like
slew-rate-volt-per-microsecond = <...>; to push this into
SI units.
That said, there are many SoC:s doing what you're doing
above so it's no strict requirement.
Yours,
Linus Walleij
^ permalink raw reply
* Re: [PATCH v2] wifi: mt76: mt7996: fix reading zeroed info->control.flags after mt76_tx_status_skb_add()
From: Roy Luo @ 2026-06-01 18:14 UTC (permalink / raw)
To: lorenzo@kernel.org
Cc: Ryder Lee, Shayne Chen (陳軒丞), nbd@nbd.name,
Roy-CH Luo, Chui-hao Chiu (邱垂浩),
AngeloGioacchino Del Regno, linux-kernel@vger.kernel.org,
linux-wireless@vger.kernel.org, Sean Wang,
Bo Jiao (焦波), linux-mediatek@lists.infradead.org,
matthias.bgg@gmail.com, linux-arm-kernel@lists.infradead.org
In-Reply-To: <ah0fGek5y8Nha0kd@lore-rh-laptop>
> > I mean the link_id is only corresponds to one specific flags bit of
> > mac80211_tx_control_flags. But there are other bits that aren't
> > handled. Wouldn't u32 flags make it more cleaner?
>
> Yes, I got your point, but my concern is if we need to sync link_id between
> mt7996_tx_prepare_skb() and mt7996_mac_write_txwi(). If so, I guess it is
> much better to pass link_id explicitly to mt7996_mac_write_txwi() since it
> does not just depended on mac80211_tx_control_flags and I think we should
> not duplicate the logic in mt7996_mac_write_txwi(). Got my point?
> If in the future (not required now) we need to pass mac80211_tx_control_flags
> to mt7996_mac_write_txwi(), we will do it easily.
>
> Regards,
> Lorenzo
>
> >
> > Ryder
> >
> >
Lorenzo,
I got your point and IIUC the problem being addressed in this patch is that
the link id assignment has unnecessary duplicated logic across different
places. However, the commit tile "fix reading zeroed info->control.flags"
seems a bit misleading to me - this patch does not fully address the problem
where the info->control.flags is cleared by memset in tx path when its
value might still be referenced, the field is still zeroed after
mt76_tx_status_skb_add() and whoever reads it afterward would get
incorrect value. With this patch, we avoid using the incorrect value for
link id, but the root cause remains.
The issue that Ryder tries to address in
https://lore.kernel.org/all/5ecac6a9b7d29526e8438dea105b58f5487c93aa.1778521232.git.ryder.lee@mediatek.com/
concerns the overlapping use of info->control and info->status in tx path,
and it remains valid even with this link id fix applied. We have to be
cautious when dealing with info->control in mt7996 tx path until the issue
is fully resolved.
Regards,
Roy
^ permalink raw reply
* Re: [PATCH net v3 1/2] af_unix: Fix inq_len update problem in partial read
From: Kuniyuki Iwashima @ 2026-06-01 18:34 UTC (permalink / raw)
To: jianyu.li
Cc: davem, edumazet, kuba, pabeni, horms, willemb, netdev,
linux-kernel, linux-mediatek, black-ch.chen, ivan.tseng
In-Reply-To: <20260601113640.231897-2-jianyu.li@mediatek.com>
On Mon, Jun 1, 2026 at 4:37 AM <jianyu.li@mediatek.com> wrote:
>
> From: Jianyu Li <jianyu.li@mediatek.com>
>
> Currently inq_len is updated only when the whole skb is consumed.
> If only part of the data is read, following SIOCINQ query would
> get value greater than what actually left.
>
> This change update inq_len timely in unix_stream_read_generic(),
> and adjust unix_stream_read_skb() accordingly to prevent
> repetitive update.
>
> Fixes: f4e1fb04c123 ("af_unix: Use cached value for SOCK_STREAM in unix_inq_len().")
> Signed-off-by: Jianyu Li <jianyu.li@mediatek.com>
> ---
> v2: Improve lock usage in unix_stream_read_generic()
For future submissions, since this patch has no change
since v2, you could carry my tag from v2.
Reviewed-by: Kuniyuki Iwashima <kuniyu@google.com>
Thanks !
^ permalink raw reply
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox