Linux-mediatek Archive on lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH] dt-bindings: mediatek: cec: Correct the compatibles for mt7623-mt8167
From: Krzysztof Kozlowski @ 2026-06-24  7:58 UTC (permalink / raw)
  To: Luca Leonardo Scorcia
  Cc: linux-mediatek, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu, Jitao shi,
	dri-devel, devicetree, linux-kernel, linux-arm-kernel
In-Reply-To: <20260623135757.5111-1-l.scorcia@gmail.com>

On Tue, Jun 23, 2026 at 03:57:53PM +0200, Luca Leonardo Scorcia wrote:
> The HDMI CEC driver for both mt7623 and mt8167 is actually the same as
> mt8173-cec and the mt7623n.dtsi board include file already uses mt8173-cec
> compatible as a fallback, but the documentation lists them as separate
> entries. Correct the binding by adding the correct fallback.
> 
> This change fixes a dtbs_check error.

which one? here you paste it (can be trimmed a bit)


> 
> Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
> ---
>  .../bindings/display/mediatek/mediatek,cec.yaml       | 11 +++++++----
>  1 file changed, 7 insertions(+), 4 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
> index 080cf321209e..4d741ba415e8 100644
> --- a/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
> +++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
> @@ -15,10 +15,13 @@ description: |
>  
>  properties:
>    compatible:
> -    enum:
> -      - mediatek,mt7623-cec
> -      - mediatek,mt8167-cec
> -      - mediatek,mt8173-cec
> +    oneOf:
> +      - const: mediatek,mt8173-cec
> +      - items:
> +        - enum:

"This change fixes a dtbs_check error."
... and introduces new other errors, so error count stays the same. Not
great.

It does not look like you tested the bindings, at least after quick
look. Please run 'make dt_binding_check' (see
Documentation/devicetree/bindings/writing-schema.rst for instructions).
Maybe you need to update your dtschema and yamllint. Don't rely on
distro packages for dtschema and be sure you are using the latest
released dtschema.

Best regards,
Krzysztof



^ permalink raw reply

* [PATCH v5 1/3] Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
From: Chris Lu @ 2026-06-24  7:55 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, linux-bluetooth,
	linux-kernel, linux-mediatek, Chris Lu, Paul Menzel
In-Reply-To: <20260624075505.1318804-1-chris.lu@mediatek.com>

Add BTMTK_WMT_PKT_* enum to represent WMT download packet sequence flags,
improving code readability. Replace magic numbers (1, 2, 3) in
btmtk_setup_firmware_79xx() with descriptive enum values:

- BTMTK_WMT_PKT_START (1): First packet of a sequence
- BTMTK_WMT_PKT_CONTINUE (2): Continuation packet
- BTMTK_WMT_PKT_END (3): Final packet of a sequence

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
---
 drivers/bluetooth/btmtk.c | 6 +++---
 drivers/bluetooth/btmtk.h | 6 ++++++
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 02a96342e964..21c08ee1cdbf 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -230,12 +230,12 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 			while (dl_size > 0) {
 				dlen = min_t(int, 250, dl_size);
 				if (first_block == 1) {
-					flag = 1;
+					flag = BTMTK_WMT_PKT_START;
 					first_block = 0;
 				} else if (dl_size - dlen <= 0) {
-					flag = 3;
+					flag = BTMTK_WMT_PKT_END;
 				} else {
-					flag = 2;
+					flag = BTMTK_WMT_PKT_CONTINUE;
 				}
 
 				wmt_params.flag = flag;
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index c83c24897c95..51c18dde0a80 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -66,6 +66,12 @@ enum {
 	BTMTK_WMT_ON_PROGRESS,
 };
 
+enum {
+	BTMTK_WMT_PKT_START = 1,
+	BTMTK_WMT_PKT_CONTINUE = 2,
+	BTMTK_WMT_PKT_END = 3,
+};
+
 struct btmtk_wmt_hdr {
 	u8	dir;
 	u8	op;
-- 
2.45.2



^ permalink raw reply related

* [PATCH v5 3/3] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-06-24  7:55 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, linux-bluetooth,
	linux-kernel, linux-mediatek, Chris Lu, Paul Menzel
In-Reply-To: <20260624075505.1318804-1-chris.lu@mediatek.com>

Add support for MT7928 (internal device ID is MT7935) which requires
additional firmware (CBMCU firmware) loading before Bluetooth firmware.

CBMCU is a new component on MT7928 to handle common part shared across
the combo chip (Wi-Fi/Bluetooth's subsystem), providing a better user
experience through improved coordination between subsystems.

Implement two-phase CBMCU firmware download: Phase 1 loads section with
type 0x5 containing global descriptor, section maps and signature data;
Phase 2 loads remaining firmware sections. Add retry mechanism for
concurrent download protection.

After CBMCU firmware loads successfully, the driver continues to load
corresponding BT firmware based on device ID through fallthrough to
case 0x7922/0x7925.

The firmware(CBMCU_CODE_MT7935_1_1.bin/BT_RAM_CODE_MT7935_1_1_hdr.bin)
required for MT7928 will be scheduled for upload to linux-firmware at
a later stage.

MT7928 bring-up kernel log:
[  159.784050] usb 1-3: New USB device found, idVendor=0e8d, idProduct=7935, bcdDevice= 1.00
[  159.784107] usb 1-3: New USB device strings: Mfr=5, Product=6, SerialNumber=7
[  159.784140] usb 1-3: Product: Wireless_Device
[  159.784166] usb 1-3: Manufacturer: MediaTek Inc.
[  159.784192] usb 1-3: SerialNumber: 000000000
[  159.795736] Bluetooth: hci1: Loading CBMCU firmware: mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin
[  159.807197] Bluetooth: hci1: CBMCU HW ver: 0x7935, SW ver: 0x0000, Build Time: 20260601T161751+0800
[  160.123155] Bluetooth: hci1: CBMCU firmware download completed
[  160.143013] Bluetooth: hci1: Loading BT firmware: mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin
[  160.152775] Bluetooth: hci1: BT HW ver: 0x7935, SW ver: 0x0000, Build Time: 20260527000816
[  163.242266] Bluetooth: hci1: Device setup in 3367430 usecs
[  163.242280] Bluetooth: hci1: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
[  163.355900] Bluetooth: hci1: AOSP extensions version v2.00
[  163.355956] Bluetooth: hci1: AOSP quality report is supported
[  163.357902] Bluetooth: MGMT ver 1.23

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
Reviewed-by: Paul Menzel <pmenzel@molgen.mpg.de>
Reviewed-by: Sean Wang <sean.wang@mediatek.com>
---
 drivers/bluetooth/btmtk.c | 355 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btmtk.h |   3 +
 2 files changed, 357 insertions(+), 1 deletion(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index dc40820bfea0..1b08db862c20 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -21,6 +21,12 @@
 #define MTK_FW_ROM_PATCH_SEC_MAP_SIZE	64
 #define MTK_SEC_MAP_COMMON_SIZE	12
 #define MTK_SEC_MAP_NEED_SEND_SIZE	52
+#define MTK_SEC_MAP_LENGTH_SIZE	4
+#define MTK_SEC_CBMCU_DESC	0x5
+
+/* CBMCU WMT command flags */
+#define BTMTK_CBMCU_FLAG_QUERY_STATUS	0xF0
+#define BTMTK_CBMCU_FLAG_ENABLE_PATCH	0xF1
 
 /* It is for mt79xx iso data transmission setting */
 #define MTK_ISO_THRESHOLD	264
@@ -120,6 +126,11 @@ void btmtk_fw_get_filename(char *buf, size_t size, u32 dev_id, u32 fw_ver,
 		snprintf(buf, size,
 			 "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
 			 dev_id & 0xffff, dev_id & 0xffff, (fw_ver & 0xff) + 1);
+	/* MT7928 */
+	else if (dev_id == 0x7935)
+		snprintf(buf, size,
+			 "mediatek/mt7928/BT_RAM_CODE_MT%04x_1_1_hdr.bin",
+			 dev_id & 0xffff);
 	else if (dev_id == 0x7961 && fw_flavor)
 		snprintf(buf, size,
 			 "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
@@ -736,6 +747,7 @@ static int btmtk_usb_hci_wmt_sync(struct hci_dev *hdev,
 			status = BTMTK_WMT_ON_UNDONE;
 		break;
 	case BTMTK_WMT_PATCH_DWNLD:
+	case BTMTK_WMT_CBMCU_DWNLD:
 		if (wmt_evt->whdr.flag == 2)
 			status = BTMTK_WMT_PATCH_DONE;
 		else if (wmt_evt->whdr.flag == 1)
@@ -872,6 +884,336 @@ static u32 btmtk_usb_reset_done(struct hci_dev *hdev)
 	return val & MTK_BT_RST_DONE;
 }
 
+static int btmtk_cbmcu_patch_status(struct hci_dev *hdev,
+				    wmt_cmd_sync_func_t wmt_cmd_sync,
+				    u8 *patch_status)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	int status, err, retry = 20;
+
+	do {
+		wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+		wmt_params.flag = BTMTK_CBMCU_FLAG_QUERY_STATUS;
+		wmt_params.dlen = 0;
+		wmt_params.data = NULL;
+		wmt_params.status = &status;
+
+		err = wmt_cmd_sync(hdev, &wmt_params);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to query CBMCU patch status (%d)", err);
+			return err;
+		}
+
+		*patch_status = (u8)status;
+
+		if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
+			msleep(100);
+			retry--;
+		} else {
+			break;
+		}
+	} while (retry > 0);
+
+	return 0;
+}
+
+static int btmtk_query_cbmcu_section(struct hci_dev *hdev,
+				     wmt_cmd_sync_func_t wmt_cmd_sync,
+				     u8 cbmcu_type,
+				     const u8 *section_map,
+				     u32 cert_len)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	u8 cmd[64];
+	int status, err;
+
+	cmd[0] = 0;
+	cmd[1] = cbmcu_type;
+
+	if (cbmcu_type == 0)
+		put_unaligned_le32(cert_len, &cmd[2]);
+	else
+		memcpy(&cmd[2], section_map, MTK_SEC_MAP_NEED_SEND_SIZE);
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.flag = 0;
+	wmt_params.dlen = cbmcu_type ?
+		MTK_SEC_MAP_NEED_SEND_SIZE + 2 :
+		MTK_SEC_MAP_LENGTH_SIZE + 2;
+	wmt_params.data = cmd;
+	wmt_params.status = &status;
+
+	err = wmt_cmd_sync(hdev, &wmt_params);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to query CBMCU section (%d)", err);
+		return err;
+	}
+
+	/* Query should return UNDONE status for successful section query */
+	if (status != BTMTK_WMT_PATCH_UNDONE) {
+		bt_dev_err(hdev, "CBMCU section query status error (%d)", status);
+		return -EIO;
+	}
+
+	return 0;
+}
+
+static int btmtk_download_cbmcu_section(struct hci_dev *hdev,
+					wmt_cmd_sync_func_t wmt_cmd_sync,
+					const u8 *fw_data,
+					u32 dl_size)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	u32 sent_len, total_size = dl_size;
+	int err;
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.status = NULL;
+
+	while (dl_size > 0) {
+		sent_len = min_t(u32, 250, dl_size);
+
+		if (dl_size == total_size)
+			wmt_params.flag = BTMTK_WMT_PKT_START;
+		else if (dl_size == sent_len)
+			wmt_params.flag = BTMTK_WMT_PKT_END;
+		else
+			wmt_params.flag = BTMTK_WMT_PKT_CONTINUE;
+
+		wmt_params.dlen = sent_len;
+		wmt_params.data = fw_data;
+
+		err = wmt_cmd_sync(hdev, &wmt_params);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to send CBMCU section data (%d)", err);
+			return err;
+		}
+
+		dl_size -= sent_len;
+		fw_data += sent_len;
+	}
+
+	return 0;
+}
+
+static int btmtk_enable_cbmcu_patch(struct hci_dev *hdev,
+				    wmt_cmd_sync_func_t wmt_cmd_sync)
+{
+	struct btmtk_hci_wmt_params wmt_params;
+	int err;
+
+	wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
+	wmt_params.flag = BTMTK_CBMCU_FLAG_ENABLE_PATCH;
+	wmt_params.dlen = 0;
+	wmt_params.data = NULL;
+	wmt_params.status = NULL;
+
+	err = wmt_cmd_sync(hdev, &wmt_params);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to enable CBMCU patch (%d)", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static int btmtk_load_cbmcu_firmware(struct hci_dev *hdev,
+				     const char *fwname,
+				     wmt_cmd_sync_func_t wmt_cmd_sync,
+				     u32 dev_id)
+{
+	struct btmtk_patch_header *hdr;
+	struct btmtk_global_desc *globaldesc;
+	struct btmtk_section_map *sectionmap;
+	const struct firmware *fw;
+	const u8 *fw_ptr;
+	u8 *cert_buf = NULL;
+	u32 section_num, section_offset, dl_size, cert_len;
+	int i, err;
+
+	err = request_firmware(&fw, fwname, &hdev->dev);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to load CBMCU firmware file %s (%d)",
+			   fwname, err);
+		return err;
+	}
+
+	if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE) {
+		bt_dev_err(hdev, "CBMCU firmware too small: size=%zu, min=%u",
+			   fw->size,
+			   MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	fw_ptr = fw->data;
+	hdr = (struct btmtk_patch_header *)fw_ptr;
+	globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
+	section_num = le32_to_cpu(globaldesc->section_num);
+
+	if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
+		       (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num) {
+		bt_dev_err(hdev, "CBMCU firmware truncated: size=%zu, expected=%zu (section_num=%u)",
+			   fw->size,
+			   MTK_FW_ROM_PATCH_HEADER_SIZE + MTK_FW_ROM_PATCH_GD_SIZE +
+			   (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
+			   section_num);
+		err = -EINVAL;
+		goto err_release_fw;
+	}
+
+	bt_dev_info(hdev, "CBMCU HW ver: 0x%04x, SW ver: 0x%04x, Build Time: %s",
+		    dev_id & 0xffff, le16_to_cpu(hdr->swver), hdr->datetime);
+
+	/* Phase 1: Download section type MTK_SEC_CBMCU_DESC */
+	for (i = 0; i < section_num; i++) {
+		sectionmap = (struct btmtk_section_map *)
+			(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+			 MTK_FW_ROM_PATCH_GD_SIZE +
+			 MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+		/* Only process MTK_SEC_CBMCU_DESC section in Phase 1 */
+		if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) != MTK_SEC_CBMCU_DESC)
+			continue;
+
+		section_offset = le32_to_cpu(sectionmap->secoffset);
+		dl_size = le32_to_cpu(sectionmap->secsize);
+
+		if (dl_size == 0)
+			continue;
+
+		if (section_offset > fw->size ||
+		    dl_size > fw->size - section_offset) {
+			bt_dev_err(hdev, "CBMCU Phase 1 section out of bounds");
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
+		cert_len = MTK_FW_ROM_PATCH_GD_SIZE +
+			   MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num +
+			   dl_size;
+
+		/* Query cbmcu section */
+		err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 0, NULL,
+						cert_len);
+		if (err < 0)
+			goto err_release_fw;
+
+		cert_buf = kmalloc(cert_len, GFP_KERNEL);
+		if (!cert_buf) {
+			err = -ENOMEM;
+			goto err_release_fw;
+		}
+
+		/* Copy Global Descriptor + All Section Maps */
+		memcpy(cert_buf,
+		       fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE,
+		       MTK_FW_ROM_PATCH_GD_SIZE + MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num);
+
+		/* Copy Phase 1 section data */
+		memcpy(cert_buf + MTK_FW_ROM_PATCH_GD_SIZE +
+		       MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
+		       fw_ptr + section_offset,
+		       dl_size);
+
+		/* Download Phase 1 section */
+		err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+						   cert_buf, cert_len);
+		kfree(cert_buf);
+		cert_buf = NULL;
+
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to download CBMCU Phase 1 section (%d)", err);
+			goto err_release_fw;
+		}
+
+		break;
+	}
+
+	/* Phase 2: Download other sections (type != MTK_SEC_CBMCU_DESC) */
+	for (i = 0; i < section_num; i++) {
+		sectionmap = (struct btmtk_section_map *)
+			(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
+			 MTK_FW_ROM_PATCH_GD_SIZE +
+			 MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
+
+		/* Skip MTK_SEC_CBMCU_DESC section in Phase 2 */
+		if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) == MTK_SEC_CBMCU_DESC)
+			continue;
+
+		section_offset = le32_to_cpu(sectionmap->secoffset);
+		dl_size = le32_to_cpu(sectionmap->bin_info_spec.dlsize);
+
+		if (dl_size == 0)
+			continue;
+
+		if (section_offset > fw->size ||
+		    dl_size > fw->size - section_offset) {
+			bt_dev_err(hdev, "CBMCU Phase 2 section %d out of bounds", i);
+			err = -EINVAL;
+			goto err_release_fw;
+		}
+
+		/* Query cbmcu section */
+		err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync, 1,
+						(u8 *)&sectionmap->bin_info_spec,
+						0);
+		if (err < 0)
+			goto err_release_fw;
+
+		/* Download section data */
+		err = btmtk_download_cbmcu_section(hdev, wmt_cmd_sync,
+						   fw_ptr + section_offset,
+						   dl_size);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to download CBMCU section %d (%d)", i, err);
+			goto err_release_fw;
+		}
+	}
+
+	bt_dev_info(hdev, "CBMCU firmware download completed");
+
+err_release_fw:
+	release_firmware(fw);
+	return err;
+}
+
+static int btmtk_setup_cbmcu_firmware(struct hci_dev *hdev,
+				      wmt_cmd_sync_func_t wmt_cmd_sync,
+				      u32 dev_id)
+{
+	char cbmcu_fwname[64];
+	u8 patch_status;
+	int err;
+
+	err = btmtk_cbmcu_patch_status(hdev, wmt_cmd_sync, &patch_status);
+	if (err < 0)
+		return err;
+
+	bt_dev_dbg(hdev, "CBMCU patch status: 0x%02x", patch_status);
+
+	if (patch_status != BTMTK_WMT_PATCH_UNDONE)
+		return 0;
+
+	snprintf(cbmcu_fwname, sizeof(cbmcu_fwname),
+		 "mediatek/mt7928/CBMCU_CODE_MT%04x_1_1.bin",
+		 dev_id & 0xffff);
+
+	bt_dev_info(hdev, "Loading CBMCU firmware: %s", cbmcu_fwname);
+
+	err = btmtk_load_cbmcu_firmware(hdev, cbmcu_fwname, wmt_cmd_sync, dev_id);
+	if (err < 0) {
+		bt_dev_err(hdev, "Failed to download CBMCU firmware (%d)", err);
+		return err;
+	}
+
+	err = btmtk_enable_cbmcu_patch(hdev, wmt_cmd_sync);
+	if (err < 0)
+		return err;
+
+	return 0;
+}
+
 int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
 {
 	u32 val;
@@ -896,7 +1238,7 @@ int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
 		if (err < 0)
 			return err;
 		msleep(100);
-	} else if (dev_id == 0x7925 || dev_id == 0x6639) {
+	} else if (dev_id == 0x7925 || dev_id == 0x6639 || dev_id == 0x7935) {
 		err = btmtk_usb_uhw_reg_read(hdev, MTK_BT_RESET_REG_CONNV3, &val);
 		if (err < 0)
 			return err;
@@ -1381,6 +1723,15 @@ int btmtk_usb_setup(struct hci_dev *hdev)
 	case 0x7668:
 		fwname = FIRMWARE_MT7668;
 		break;
+	case 0x7935:
+		/* Requires CBMCU firmware before BT firmware */
+		err = btmtk_setup_cbmcu_firmware(hdev, btmtk_usb_hci_wmt_sync,
+						 dev_id);
+		if (err < 0) {
+			bt_dev_err(hdev, "Failed to set up CBMCU firmware (%d)", err);
+			return err;
+		}
+		fallthrough;
 	case 0x7922:
 	case 0x7925:
 		/*
@@ -1598,3 +1949,5 @@ MODULE_FIRMWARE(FIRMWARE_MT7922);
 MODULE_FIRMWARE(FIRMWARE_MT7961);
 MODULE_FIRMWARE(FIRMWARE_MT7925);
 MODULE_FIRMWARE(FIRMWARE_MT7927);
+MODULE_FIRMWARE(FIRMWARE_MT7928);
+MODULE_FIRMWARE(FIRMWARE_MT7928_CBMCU);
diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
index 51c18dde0a80..5fe4964b031b 100644
--- a/drivers/bluetooth/btmtk.h
+++ b/drivers/bluetooth/btmtk.h
@@ -9,6 +9,8 @@
 #define FIRMWARE_MT7961		"mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
 #define FIRMWARE_MT7925		"mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
 #define FIRMWARE_MT7927		"mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
+#define FIRMWARE_MT7928		"mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin"
+#define FIRMWARE_MT7928_CBMCU	"mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin"
 
 #define HCI_EV_WMT 0xe4
 #define HCI_WMT_MAX_EVENT_SIZE		64
@@ -54,6 +56,7 @@ enum {
 	BTMTK_WMT_RST = 0x7,
 	BTMTK_WMT_REGISTER = 0x8,
 	BTMTK_WMT_SEMAPHORE = 0x17,
+	BTMTK_WMT_CBMCU_DWNLD = 0x58,
 };
 
 enum {
-- 
2.45.2



^ permalink raw reply related

* [PATCH v5 0/3] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu @ 2026-06-24  7:55 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, linux-bluetooth,
	linux-kernel, linux-mediatek, Chris Lu

This patch series adds support for MT7928 (device ID 0x7935) to the
btmtk driver, which requires a new two-stage firmware loading process
with CBMCU firmware.

Patch 1 refactors existing firmware download code by replacing magic
numbers with a descriptive BTMTK_WMT_PKT_* enum, making the packet
sequencing logic clearer.

Patch 2 improves BT firmware logging to provide more useful information
for debugging: adds firmware filename before loading and displays chip ID
as HW version instead of firmware's hwver field.

Patch 3 implements MT7928 firmware download flow, which requires loading
CBMCU firmware before Bluetooth firmware. The CBMCU firmware uses a
two-phase download sequence: Phase 1 downloads the section containing
global descriptor and signature data, Phase 2 downloads the remaining
firmware sections. After CBMCU firmware completes, the driver continues
to load the Bluetooth firmware following the standard flow.

Tested on MT7928 hardware with successful firmware loading and
Bluetooth functionality verification.

Changes in v5:
- Split into three patches: refactoring, logging improvement, and
  new feature
- Add Patch 2 to improve BT firmware logging independently
  * Add firmware filename before loading
  * Display chip ID (dev_id) as HW version
  * Use clearer log format with separate HW/SW version fields
- Apply same logging improvements to CBMCU firmware in Patch 3
- Better separation of concerns for easier review

Changes in v4:
- Split into two patches: refactoring and new feature
- Add BTMTK_WMT_PKT_* enum to improve code readability
- Replace magic numbers (0xF0, 0xF1) with descriptive macros
- Define MTK_SEC_CBMCU_DESC macro for section type
- Add MT7928 marketing name comment
- Include firmware filename in error messages
- Add detailed size information in firmware validation errors
- Use BTMTK_WMT_PKT_* enum in CBMCU download function

Changes in v3:
- Add firmware size validation with bounds checking
- Improve error messages with context information
- Add section offset validation for both phases

Changes in v2:
- Simplified enum usage by consolidating status definitions
- Improved code maintainability

Chris Lu (3):
  Bluetooth: btmtk: Replace magic numbers with WMT packet flag enum
  Bluetooth: btmtk: Improve BT firmware logging
  Bluetooth: btmtk: Add MT7928 support

 drivers/bluetooth/btmtk.c | 365 +++++++++++++++++++++++++++++++++++++-
 drivers/bluetooth/btmtk.h |   9 +
 2 files changed, 370 insertions(+), 4 deletions(-)

--
2.45.2


^ permalink raw reply

* [PATCH v5 2/3] Bluetooth: btmtk: Improve BT firmware logging
From: Chris Lu @ 2026-06-24  7:55 UTC (permalink / raw)
  To: Marcel Holtmann, Johan Hedberg, Luiz Von Dentz
  Cc: Sean Wang, Will Lee, SS Wu, Steve Lee, linux-bluetooth,
	linux-kernel, linux-mediatek, Chris Lu
In-Reply-To: <20260624075505.1318804-1-chris.lu@mediatek.com>

Improve firmware loading log messages to provide more useful information:

- Add firmware filename before loading to help identify which file
  is being loaded
- Display chip ID (dev_id) as HW version instead of firmware's hwver
  field, which provides more meaningful hardware identification

log output with MT7922
[  212.878783] Bluetooth: hci1: Loading BT firmware: mediatek/BT_RAM_CODE_MT7922_1_1_hdr.bin
[  212.889614] Bluetooth: hci1: BT HW ver: 0x7922, SW ver: 0x008a, Build Time: 20260224103448
[  216.048877] Bluetooth: hci1: Device setup in 3096530 usecs
[  216.048890] Bluetooth: hci1: HCI Enhanced Setup Synchronous Connection command is advertised, but not supported.
[  216.114179] Bluetooth: hci1: AOSP extensions version v1.00
[  216.114220] Bluetooth: hci1: AOSP quality report is supported
[  216.116782] Bluetooth: MGMT ver 1.23

Signed-off-by: Chris Lu <chris.lu@mediatek.com>
---
 drivers/bluetooth/btmtk.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
index 21c08ee1cdbf..dc40820bfea0 100644
--- a/drivers/bluetooth/btmtk.c
+++ b/drivers/bluetooth/btmtk.c
@@ -147,6 +147,8 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 	u32 section_num, dl_size, section_offset;
 	u8 cmd[64];
 
+	bt_dev_info(hdev, "Loading BT firmware: %s", fwname);
+
 	err = request_firmware(&fw, fwname, &hdev->dev);
 	if (err < 0) {
 		bt_dev_err(hdev, "Failed to load firmware file (%d)", err);
@@ -159,8 +161,8 @@ int btmtk_setup_firmware_79xx(struct hci_dev *hdev, const char *fwname,
 	globaldesc = (struct btmtk_global_desc *)(fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE);
 	section_num = le32_to_cpu(globaldesc->section_num);
 
-	bt_dev_info(hdev, "HW/SW Version: 0x%04x%04x, Build Time: %s",
-		    le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver), hdr->datetime);
+	bt_dev_info(hdev, "BT HW ver: 0x%04x, SW ver: 0x%04x, Build Time: %s",
+		    dev_id & 0xffff, le16_to_cpu(hdr->swver), hdr->datetime);
 
 	for (i = 0; i < section_num; i++) {
 		first_block = 1;
-- 
2.45.2



^ permalink raw reply related

* [PATCH v2] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-24  6:23 UTC (permalink / raw)
  To: Yassine Oudjana, Michael Turquette, Stephen Boyd
  Cc: Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
	linux-mediatek, linux-kernel, linux-arm-kernel, Myeonghun Pak,
	Ijae Kim

mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
clk_mt6735_apmixed_remove() unregisters them on driver removal.

If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
probe returns the error directly and the remove callback is not run. This
leaves the registered PLL clocks behind on the probe failure path.

Unregister the PLLs in that failure branch before returning the error.

Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>

---
Changes in v2:
- Unregister PLLs directly in the provider-registration failure branch.
- Wrap the commit message line flagged by checkpatch.

 drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 5 ++++-
 1 file changed, 4 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
index 9e30c089a2..69d9ce1210 100644
--- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
@@ -102,9 +102,12 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
 
 	ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
 					  clk_data);
-	if (ret)
+	if (ret) {
 		dev_err(&pdev->dev,
 			"Failed to register clock provider: %d\n", ret);
+		mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
+					clk_data);
+	}
 
 	return ret;
 }
-- 
2.47.1


^ permalink raw reply related

* Re: [PATCH] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-24  6:21 UTC (permalink / raw)
  To: Brian Masney
  Cc: Yassine Oudjana, Michael Turquette, Stephen Boyd,
	Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
	linux-mediatek, linux-kernel, linux-arm-kernel, Ijae Kim
In-Reply-To: <ajqZoOv-TvmOMLCh@redhat.com>

Hi,

Thanks for the review.

I will send a v2 with mtk_clk_unregister_plls() moved directly into the
devm_of_clk_add_hw_provider() failure branch, and I will make sure it passes
checkpatch before sending.

Best regards,
Myeonghun

2026년 6월 23일 (화) 오후 11:35, Brian Masney <bmasney@redhat.com>님이 작성:
>
> Hi Myeonghun,
>
> On Tue, Jun 23, 2026 at 06:41:11PM +0900, Myeonghun Pak wrote:
> > mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
> > clk_mt6735_apmixed_remove() unregisters them on driver removal.
> >
> > If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
> > probe returns the error directly and the remove callback is not run. This
> > leaves the registered PLL clocks behind on the probe failure path.
> >
> > Add an unregister_plls error path so provider registration failures unwind the
> > PLLs before returning the error.
>
> Please run your patches through checkpatch.pl before sending.
>
> >
> > Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
> > Co-developed-by: Ijae Kim <ae878000@gmail.com>
> > Signed-off-by: Ijae Kim <ae878000@gmail.com>
> > Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> >
> > ---
> >  drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 9 ++++++++-
> >  1 file changed, 8 insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > index 9e30c089a2..04cf9665ec 100644
> > --- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > +++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> > @@ -102,10 +102,17 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
> >
> >       ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
> >                                         clk_data);
> > -     if (ret)
> > +     if (ret) {
> >               dev_err(&pdev->dev,
> >                       "Failed to register clock provider: %d\n", ret);
> > +             goto unregister_plls;
> > +     }
> > +
> > +     return 0;
> >
> > +unregister_plls:
> > +     mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
> > +                             clk_data);
> >       return ret;
> >  }
>
> This fix is correct. Since only one path will encounter this, personally
> I would just put the call to mtk_clk_unregister_plls() inside the if
> statement above to simplify this further.
>
> Brian
>


^ permalink raw reply

* Re: [PATCH v4 0/2] Bluetooth: btmtksdio: teardown fixes
From: Sergey Senozhatsky @ 2026-06-24  5:19 UTC (permalink / raw)
  To: Marcel Holtmann, Luiz Augusto von Dentz, Sean Wang
  Cc: Tomasz Figa, linux-bluetooth, linux-kernel, linux-arm-kernel,
	linux-mediatek, Sergey Senozhatsky
In-Reply-To: <20260618031338.1011410-1-senozhatsky@chromium.org>

On (26/06/18 12:13), Sergey Senozhatsky wrote:
> This fixes several teardown issues:
> 
>      INFO: task kworker/u17:0:189 blocked for more than 122 seconds.
>      __cancel_work_timer+0x3f4/0x460
>      cancel_work_sync+0x1c/0x2c
>      btmtksdio_flush+0x2c/0x40
>      hci_dev_open_sync+0x10c4/0x2190
>      [..]
> 
> close/flush can deadlock when run concurrently with btmtksdio_txrx_work().
> In addition btmtksdio_txrx_work() re-enables interrupts regardless of
> close/flush being executed on another CPU.
> 
> v3 -> v4:
> - fix commit message linter warnings/errors (tabs, subject line over 80
>   chars).
> 
> Sergey Senozhatsky (2):
>   Bluetooth: btmtksdio: test for BUS IO errors in btmtksdio_txrx_work()
>   Bluetooth: btmtksdio: call cancel_work_sync() out of host lock scope

Do the patches look good enough to pick up?


^ permalink raw reply

* Re: [PATCH] Input: mtk-pmic-keys: Count available keys during probe instead of pre-counting
From: Dmitry Torokhov @ 2026-06-24  4:14 UTC (permalink / raw)
  To: Rosen Penev
  Cc: linux-input, Matthias Brugger, AngeloGioacchino Del Regno,
	open list:ARM/Mediatek SoC support,
	moderated list:ARM/Mediatek SoC support,
	moderated list:ARM/Mediatek SoC support
In-Reply-To: <20260528235600.312045-1-rosenp@gmail.com>

Hi Rosen,

On Thu, May 28, 2026 at 04:56:00PM -0700, Rosen Penev wrote:
> Replace the separate of_get_available_child_count() pre-count and
> validation step with a single pass through for_each_child_of_node_scoped().
> Skip unavailable child nodes and bail out if more than
> MTK_PMIC_MAX_KEY_COUNT available keys are found. Set nkeys after the
> loop so suspend/resume iterate only over initialized entries.
> 
> Also use a key variable in the loop for clarity.
> 
> Use of_device_get_match_data() to fetch the PMIC key register data directly
> instead of open-coding an of_match_device() lookup.

Please split this out into a separate patch.

> 
> This also lets the driver drop the of_device.h include.
> 
> Assisted-by: OpenCode:BigPickle
> Signed-off-by: Rosen Penev <rosenp@gmail.com>
> ---
>  drivers/input/keyboard/mtk-pmic-keys.c | 53 ++++++++++++--------------
>  1 file changed, 24 insertions(+), 29 deletions(-)
> 
> diff --git a/drivers/input/keyboard/mtk-pmic-keys.c b/drivers/input/keyboard/mtk-pmic-keys.c
> index c78d9f6d97c4..e34856693ee2 100644
> --- a/drivers/input/keyboard/mtk-pmic-keys.c
> +++ b/drivers/input/keyboard/mtk-pmic-keys.c
> @@ -16,7 +16,6 @@
>  #include <linux/mfd/mt6397/core.h>
>  #include <linux/mfd/mt6397/registers.h>
>  #include <linux/module.h>
> -#include <linux/of_device.h>
>  #include <linux/of.h>
>  #include <linux/platform_device.h>
>  #include <linux/regmap.h>
> @@ -147,6 +146,7 @@ struct mtk_pmic_keys {
>  	struct input_dev *input_dev;
>  	struct device *dev;
>  	struct regmap *regmap;
> +	unsigned int nkeys;
>  	struct mtk_pmic_keys_info keys[MTK_PMIC_MAX_KEY_COUNT];
>  };
>  
> @@ -267,7 +267,7 @@ static int mtk_pmic_keys_suspend(struct device *dev)
>  	struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
>  	int index;
>  
> -	for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
> +	for (index = 0; index < keys->nkeys; index++) {
>  		if (keys->keys[index].wakeup) {
>  			enable_irq_wake(keys->keys[index].irq);
>  			if (keys->keys[index].irq_r > 0)
> @@ -283,7 +283,7 @@ static int mtk_pmic_keys_resume(struct device *dev)
>  	struct mtk_pmic_keys *keys = dev_get_drvdata(dev);
>  	int index;
>  
> -	for (index = 0; index < MTK_PMIC_MAX_KEY_COUNT; index++) {
> +	for (index = 0; index < keys->nkeys; index++) {
>  		if (keys->keys[index].wakeup) {
>  			disable_irq_wake(keys->keys[index].irq);
>  			if (keys->keys[index].irq_r > 0)
> @@ -325,24 +325,23 @@ MODULE_DEVICE_TABLE(of, of_mtk_pmic_keys_match_tbl);
>  static int mtk_pmic_keys_probe(struct platform_device *pdev)
>  {
>  	int error, index = 0;
> -	unsigned int keycount;
>  	struct mt6397_chip *pmic_chip = dev_get_drvdata(pdev->dev.parent);
>  	struct device_node *node = pdev->dev.of_node;
>  	static const char *const irqnames[] = { "powerkey", "homekey" };
>  	static const char *const irqnames_r[] = { "powerkey_r", "homekey_r" };
>  	struct mtk_pmic_keys *keys;
>  	const struct mtk_pmic_regs *mtk_pmic_regs;
> +	struct mtk_pmic_keys_info *key;
>  	struct input_dev *input_dev;
> -	const struct of_device_id *of_id =
> -		of_match_device(of_mtk_pmic_keys_match_tbl, &pdev->dev);
>  
>  	keys = devm_kzalloc(&pdev->dev, sizeof(*keys), GFP_KERNEL);
>  	if (!keys)
>  		return -ENOMEM;
> -
>  	keys->dev = &pdev->dev;
>  	keys->regmap = pmic_chip->regmap;
> -	mtk_pmic_regs = of_id->data;
> +	mtk_pmic_regs = of_device_get_match_data(&pdev->dev);
> +	if (!mtk_pmic_regs)
> +		return -EINVAL;
>  
>  	keys->input_dev = input_dev = devm_input_allocate_device(keys->dev);
>  	if (!input_dev) {
> @@ -356,31 +355,26 @@ static int mtk_pmic_keys_probe(struct platform_device *pdev)
>  	input_dev->id.product = 0x0001;
>  	input_dev->id.version = 0x0001;
>  
> -	keycount = of_get_available_child_count(node);
> -	if (keycount > MTK_PMIC_MAX_KEY_COUNT ||
> -	    keycount > ARRAY_SIZE(irqnames)) {
> -		dev_err(keys->dev, "too many keys defined (%d)\n", keycount);
> -		return -EINVAL;
> -	}
> +	for_each_available_child_of_node_scoped(node, child) {

Let's keep using for_each_child_of_node_scoped() and check
of_device_is_available() inside the loop. This will allow marking a key
as disabled without shifting it's meaning (power key vs home key).

In the rest of the driver we should be able to determine if key is set
up checking for key->irq > 0.

Thanks.

-- 
Dmitry


^ permalink raw reply

* Re: [PATCH 1/2] wifi: mt76: mt7927: set band index for sniffer mode
From: Devin Wittmayer @ 2026-06-24  3:31 UTC (permalink / raw)
  To: Sean Wang, Felix Fietkau, Lorenzo Bianconi
  Cc: linux-wireless, linux-mediatek, Sean Wang, Devin Wittmayer
In-Reply-To: <20260613225144.2414283-1-sean.wang@kernel.org>

Tested on a real MT7927 (Filogic 380, PCIe). Two monitors on different
bands capture at the same time with the series, and drop back onto one
band without it.

I tried each patch on its own too. [1/2] by itself still collapses both
monitors onto one channel. With [2/2] they stay apart, but then one of
them stops capturing, so it looks like both are needed.

Single-band monitor and a normal station connection were both fine.

Tested-by: Devin Wittmayer <lucid_duck@justthetip.ca>


^ permalink raw reply

* [PATCH v4 1/1] wifi: mt76: mt792x: fix use-after-free in mt76_rx_poll_complete
From: Eason Lai @ 2026-06-24  3:05 UTC (permalink / raw)
  To: nbd, lorenzo
  Cc: linux-wireless, linux-mediatek, Yf.Luo, kun.wu, deren.wu,
	sean.wang, quan.zhou, ryder.lee, leon.yen, litien.chang, jb.tsai,
	eason.lai, Eason Lai

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.

Set the links' wcid pointers to be NULL for a MLD in
mt7925_sta_pre_rcu_remove()

BUG: KASAN: invalid-access in mt76_rx_poll_complete+0x280/0x470
Call trace:
dump_backtrace+0xec/0x128
show_stack+0x18/0x28
dump_stack_lvl+0x40/0xc8
print_report+0x1b8/0x710
kasan_report+0xe0/0x144
do_bad_area+0x120/0x260
do_tag_check_fault+0x20/0x34
do_mem_abort+0x54/0xa8
el1_abort+0x3c/0x5c
el1h_64_sync_handler+0x40/0xcc
el1h_64_sync+0x7c/0x80
mt76_rx_poll_complete+0x280/0x470
mt76_dma_rx_poll+0x114/0x51c
mt792x_poll_rx+0x60/0xf8
napi_threaded_poll_loop+0xe0/0x450
napi_threaded_poll+0x80/0x9c
kthread+0x11c/0x158
ret_from_fork+0x10/0x20

Fixes: c948b5da6bbe ("wifi: mt76: mt7925: add Mediatek Wi-Fi7 driver for
mt7925 chips")

Signed-off-by: Eason Lai <Eason.Lai@mediatek.com>
---
v2: fix mt76x02 build errors
v3: fix mt76x02 build error due to variable set but not used
v4: fix use-after-free in MLO
---
 .../net/wireless/mediatek/mt76/mt7925/main.c  | 36 ++++++++++++++++++-
 1 file changed, 35 insertions(+), 1 deletion(-)

diff --git a/drivers/net/wireless/mediatek/mt76/mt7925/main.c b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
index 73d3722739d0..e3fb6392eda2 100644
--- a/drivers/net/wireless/mediatek/mt76/mt7925/main.c
+++ b/drivers/net/wireless/mediatek/mt76/mt7925/main.c
@@ -2410,6 +2410,40 @@ static void mt7925_channel_switch_rx_beacon(struct ieee80211_hw *hw,
 	}
 }
 
+static void mt7925_sta_pre_rcu_remove(struct ieee80211_hw *hw,
+				      struct ieee80211_vif *vif,
+				      struct ieee80211_sta *sta)
+{
+	struct mt76_phy *phy = hw->priv;
+	struct mt76_dev *dev = phy->dev;
+	struct mt76_wcid *wcid = (struct mt76_wcid *)sta->drv_priv;
+
+	mutex_lock(&dev->mutex);
+	spin_lock_bh(&dev->status_lock);
+
+	if (ieee80211_vif_is_mld(vif)) {
+		struct mt792x_sta *msta = (struct mt792x_sta *)sta->drv_priv;
+		struct mt792x_vif *mvif = (struct mt792x_vif *)vif->drv_priv;
+		unsigned long valid = mvif->valid_links;
+		struct mt792x_link_sta *mlink;
+		unsigned int link_id;
+
+		for_each_set_bit(link_id, &valid, IEEE80211_MLD_MAX_NUM_LINKS) {
+			mlink = mt792x_sta_to_link(msta, link_id);
+			if (!mlink || !mlink->wcid.sta)
+				continue;
+			if (mlink->wcid.idx < ARRAY_SIZE(dev->wcid))
+				rcu_assign_pointer(dev->wcid[mlink->wcid.idx],
+						   NULL);
+		}
+	} else {
+		rcu_assign_pointer(dev->wcid[wcid->idx], NULL);
+	}
+
+	spin_unlock_bh(&dev->status_lock);
+	mutex_unlock(&dev->mutex);
+}
+
 const struct ieee80211_ops mt7925_ops = {
 	.tx = mt792x_tx,
 	.start = mt7925_start,
@@ -2422,7 +2456,7 @@ const struct ieee80211_ops mt7925_ops = {
 	.start_ap = mt7925_start_ap,
 	.stop_ap = mt7925_stop_ap,
 	.sta_state = mt76_sta_state,
-	.sta_pre_rcu_remove = mt76_sta_pre_rcu_remove,
+	.sta_pre_rcu_remove = mt7925_sta_pre_rcu_remove,
 	.set_key = mt7925_set_key,
 	.sta_set_decap_offload = mt7925_sta_set_decap_offload,
 #if IS_ENABLED(CONFIG_IPV6)
-- 
2.45.2



^ permalink raw reply related

* Re: [PATCH v4 2/2] Bluetooth: btmtk: Add MT7928 support
From: Chris Lu (陸稚泓) @ 2026-06-24  2:32 UTC (permalink / raw)
  To: pmenzel@molgen.mpg.de
  Cc: Will-CY Lee (李政穎),
	Steve Lee (李視誠), luiz.dentz@gmail.com,
	marcel@holtmann.org, SS Wu (巫憲欣),
	linux-kernel@vger.kernel.org, johan.hedberg@gmail.com, Sean Wang,
	linux-bluetooth@vger.kernel.org,
	linux-mediatek@lists.infradead.org
In-Reply-To: <6e44158f-7bc7-4568-ab03-2bc215c2c679@molgen.mpg.de>

Hi Paul,

On Tue, 2026-06-23 at 10:05 +0200, Paul Menzel wrote:
> 
> External email : Please do not click links or open attachments until
> you have verified the sender or the content.
> 
> 
> Dear Chris,
> 
> 
> Thank you for your patch.
> 
> Am 23.06.26 um 05:41 schrieb Chris Lu:
> > Add support for MT7928 (internal device ID is MT7935) which
> > requires
> > additional firmware (CBMCU firmware) loading before Bluetooth
> > firmware.
> > 
> > CBMCU is a new component on MT7928 to handle common part shared
> > across
> > the combo chip (Wi-Fi/Bluetooth's subsystem), providing a better
> > user
> > experience through improved coordination between subsystems.
> > 
> > Implement two-phase CBMCU firmware download: Phase 1 loads section
> > with
> > type 0x5 containing global descriptor, section maps and signature
> > data;
> > Phase 2 loads remaining firmware sections. Add retry mechanism for
> > concurrent download protection.
> > 
> > After CBMCU firmware loads successfully, the driver continues to
> > load
> > corresponding BT firmware based on device ID through fallthrough to
> > case 0x7922/0x7925.
> > 
> > The
> > firmware(CBMCU_CODE_MT7935_1_1.bin/BT_RAM_CODE_MT7935_1_1_hdr.bin)
> > required for MT7928 will be scheduled for upload to linux-firmware
> > at
> > a later stage.
> > 
> > MT7928 bringup kernel log:
> 
> bring-up
> 
> > [  475.742336] usb 1-3: New USB device found, idVendor=0e8d,
> > idProduct=7935, bcdDevice= 1.00
> > [  475.742399] usb 1-3: New USB device strings: Mfr=5, Product=6,
> > SerialNumber=7
> > [  475.742436] usb 1-3: Product: Wireless_Device
> > [  475.742466] usb 1-3: Manufacturer: MediaTek Inc.
> > [  475.742495] usb 1-3: SerialNumber: 000000000
> > [  475.766697] Bluetooth: hci1: CBMCU Version: 0x00000000, Build
> > Time: 20260601T161751+0800
> > [  476.144693] Bluetooth: hci1: CBMCU firmware download completed
> > [  476.190083] Bluetooth: hci1: HW/SW Version: 0x00000000, Build
> > Time: 20260527000816
> 
> Can we improve these logs to include the device name and to have one
> line (or two lines) less?
> 
>      Bluetooth: MT7928: CBMCU firmware <name> (version: 0x<short>,
> build
> time: 20260601T161751+0800) update complete
>      Bluetooth: MT7928: device firmware updated to <name>
> 
> The second line is unrelated to this change.

Regarding the version, since the firmware is still under internal
development, the information currently seems meaningless. It will be an
important information for MTK debugging in the future.

As for displaying the IC code, that's a great suggestion. I'll submit a
new version and reconsider how to present these logs.

> 
> > [  479.073470] Bluetooth: hci1: Device setup in 3238661 usecs
> > [  479.073489] Bluetooth: hci1: HCI Enhanced Setup Synchronous
> > Connection command is advertised, but not supported.
> > [  479.177477] Bluetooth: hci1: AOSP extensions version v2.00
> > [  479.177506] Bluetooth: hci1: AOSP quality report is supported
> > [  479.178814] Bluetooth: MGMT ver 1.23
> > 
> > Signed-off-by: Chris Lu <chris.lu@mediatek.com>
> > ---
> >   drivers/bluetooth/btmtk.c | 352
> > +++++++++++++++++++++++++++++++++++++-
> >   drivers/bluetooth/btmtk.h |   3 +
> >   2 files changed, 354 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/bluetooth/btmtk.c b/drivers/bluetooth/btmtk.c
> > index 21c08ee1cdbf..dcbd431cc364 100644
> > --- a/drivers/bluetooth/btmtk.c
> > +++ b/drivers/bluetooth/btmtk.c
> > @@ -21,6 +21,12 @@
> >   #define MTK_FW_ROM_PATCH_SEC_MAP_SIZE       64
> >   #define MTK_SEC_MAP_COMMON_SIZE     12
> >   #define MTK_SEC_MAP_NEED_SEND_SIZE  52
> > +#define MTK_SEC_MAP_LENGTH_SIZE      4
> > +#define MTK_SEC_CBMCU_DESC   0x5
> > +
> > +/* CBMCU WMT command flags */
> > +#define BTMTK_CBMCU_FLAG_QUERY_STATUS        0xF0
> > +#define BTMTK_CBMCU_FLAG_ENABLE_PATCH        0xF1
> > 
> >   /* It is for mt79xx iso data transmission setting */
> >   #define MTK_ISO_THRESHOLD   264
> > @@ -120,6 +126,11 @@ void btmtk_fw_get_filename(char *buf, size_t
> > size, u32 dev_id, u32 fw_ver,
> >               snprintf(buf, size,
> >                       
> > "mediatek/mt%04x/BT_RAM_CODE_MT%04x_1_%x_hdr.bin",
> >                        dev_id & 0xffff, dev_id & 0xffff, (fw_ver &
> > 0xff) + 1);
> > +     /* MT7928 */
> > +     else if (dev_id == 0x7935)
> > +             snprintf(buf, size,
> > +                     
> > "mediatek/mt7928/BT_RAM_CODE_MT%04x_1_1_hdr.bin",
> > +                      dev_id & 0xffff);
> >       else if (dev_id == 0x7961 && fw_flavor)
> >               snprintf(buf, size,
> >                        "mediatek/BT_RAM_CODE_MT%04x_1a_%x_hdr.bin",
> > @@ -734,6 +745,7 @@ static int btmtk_usb_hci_wmt_sync(struct
> > hci_dev *hdev,
> >                       status = BTMTK_WMT_ON_UNDONE;
> >               break;
> >       case BTMTK_WMT_PATCH_DWNLD:
> > +     case BTMTK_WMT_CBMCU_DWNLD:
> >               if (wmt_evt->whdr.flag == 2)
> >                       status = BTMTK_WMT_PATCH_DONE;
> >               else if (wmt_evt->whdr.flag == 1)
> > @@ -870,6 +882,333 @@ static u32 btmtk_usb_reset_done(struct
> > hci_dev *hdev)
> >       return val & MTK_BT_RST_DONE;
> >   }
> > 
> > +static int btmtk_cbmcu_patch_status(struct hci_dev *hdev,
> > +                                 wmt_cmd_sync_func_t wmt_cmd_sync,
> > +                                 u8 *patch_status)
> > +{
> > +     struct btmtk_hci_wmt_params wmt_params;
> > +     int status, err, retry = 20;
> > +
> > +     do {
> > +             wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> > +             wmt_params.flag = BTMTK_CBMCU_FLAG_QUERY_STATUS;
> > +             wmt_params.dlen = 0;
> > +             wmt_params.data = NULL;
> > +             wmt_params.status = &status;
> > +
> > +             err = wmt_cmd_sync(hdev, &wmt_params);
> > +             if (err < 0) {
> > +                     bt_dev_err(hdev, "Failed to query CBMCU patch
> > status (%d)", err);
> > +                     return err;
> > +             }
> > +
> > +             *patch_status = (u8)status;
> > +
> > +             if (*patch_status == BTMTK_WMT_PATCH_PROGRESS) {
> > +                     msleep(100);
> > +                     retry--;
> > +             } else {
> > +                     break;
> > +             }
> > +     } while (retry > 0);
> > +
> > +     return 0;
> > +}
> > +
> > +static int btmtk_query_cbmcu_section(struct hci_dev *hdev,
> > +                                  wmt_cmd_sync_func_t
> > wmt_cmd_sync,
> > +                                  u8 cbmcu_type,
> > +                                  const u8 *section_map,
> > +                                  u32 cert_len)
> > +{
> > +     struct btmtk_hci_wmt_params wmt_params;
> > +     u8 cmd[64];
> > +     int status, err;
> > +
> > +     cmd[0] = 0;
> > +     cmd[1] = cbmcu_type;
> > +
> > +     if (cbmcu_type == 0)
> > +             put_unaligned_le32(cert_len, &cmd[2]);
> > +     else
> > +             memcpy(&cmd[2], section_map,
> > MTK_SEC_MAP_NEED_SEND_SIZE);
> > +
> > +     wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> > +     wmt_params.flag = 0;
> > +     wmt_params.dlen = cbmcu_type ?
> > +             MTK_SEC_MAP_NEED_SEND_SIZE + 2 :
> > +             MTK_SEC_MAP_LENGTH_SIZE + 2;
> > +     wmt_params.data = cmd;
> > +     wmt_params.status = &status;
> > +
> > +     err = wmt_cmd_sync(hdev, &wmt_params);
> > +     if (err < 0) {
> > +             bt_dev_err(hdev, "Failed to query CBMCU section
> > (%d)", err);
> > +             return err;
> > +     }
> > +
> > +     /* Query should return UNDONE status for successful section
> > query */
> > +     if (status != BTMTK_WMT_PATCH_UNDONE) {
> > +             bt_dev_err(hdev, "CBMCU section query status error
> > (%d)", status);
> > +             return -EIO;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int btmtk_download_cbmcu_section(struct hci_dev *hdev,
> > +                                     wmt_cmd_sync_func_t
> > wmt_cmd_sync,
> > +                                     const u8 *fw_data,
> > +                                     u32 dl_size)
> > +{
> > +     struct btmtk_hci_wmt_params wmt_params;
> > +     u32 sent_len, total_size = dl_size;
> > +     int err;
> > +
> > +     wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> > +     wmt_params.status = NULL;
> > +
> > +     while (dl_size > 0) {
> > +             sent_len = min_t(u32, 250, dl_size);
> > +
> > +             if (dl_size == total_size)
> > +                     wmt_params.flag = BTMTK_WMT_PKT_START;
> > +             else if (dl_size == sent_len)
> > +                     wmt_params.flag = BTMTK_WMT_PKT_END;
> > +             else
> > +                     wmt_params.flag = BTMTK_WMT_PKT_CONTINUE;
> > +
> > +             wmt_params.dlen = sent_len;
> > +             wmt_params.data = fw_data;
> > +
> > +             err = wmt_cmd_sync(hdev, &wmt_params);
> > +             if (err < 0) {
> > +                     bt_dev_err(hdev, "Failed to send CBMCU
> > section data (%d)", err);
> > +                     return err;
> > +             }
> > +
> > +             dl_size -= sent_len;
> > +             fw_data += sent_len;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int btmtk_enable_cbmcu_patch(struct hci_dev *hdev,
> > +                                 wmt_cmd_sync_func_t wmt_cmd_sync)
> > +{
> > +     struct btmtk_hci_wmt_params wmt_params;
> > +     int err;
> > +
> > +     wmt_params.op = BTMTK_WMT_CBMCU_DWNLD;
> > +     wmt_params.flag = BTMTK_CBMCU_FLAG_ENABLE_PATCH;
> > +     wmt_params.dlen = 0;
> > +     wmt_params.data = NULL;
> > +     wmt_params.status = NULL;
> > +
> > +     err = wmt_cmd_sync(hdev, &wmt_params);
> > +     if (err < 0) {
> > +             bt_dev_err(hdev, "Failed to enable CBMCU patch (%d)",
> > err);
> > +             return err;
> > +     }
> > +
> > +     return 0;
> > +}
> > +
> > +static int btmtk_load_cbmcu_firmware(struct hci_dev *hdev,
> > +                                  const char *fwname,
> > +                                  wmt_cmd_sync_func_t
> > wmt_cmd_sync)
> > +{
> > +     struct btmtk_patch_header *hdr;
> > +     struct btmtk_global_desc *globaldesc;
> > +     struct btmtk_section_map *sectionmap;
> > +     const struct firmware *fw;
> > +     const u8 *fw_ptr;
> > +     u8 *cert_buf = NULL;
> > +     u32 section_num, section_offset, dl_size, cert_len;
> > +     int i, err;
> > +
> > +     err = request_firmware(&fw, fwname, &hdev->dev);
> > +     if (err < 0) {
> > +             bt_dev_err(hdev, "Failed to load CBMCU firmware file
> > %s (%d)",
> > +                        fwname, err);
> > +             return err;
> > +     }
> > +
> > +     if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE +
> > MTK_FW_ROM_PATCH_GD_SIZE) {
> > +             bt_dev_err(hdev, "CBMCU firmware too small: size=%zu,
> > min=%u",
> > +                        fw->size,
> > +                        MTK_FW_ROM_PATCH_HEADER_SIZE +
> > MTK_FW_ROM_PATCH_GD_SIZE);
> > +             err = -EINVAL;
> > +             goto err_release_fw;
> > +     }
> > +
> > +     fw_ptr = fw->data;
> > +     hdr = (struct btmtk_patch_header *)fw_ptr;
> > +     globaldesc = (struct btmtk_global_desc *)(fw_ptr +
> > MTK_FW_ROM_PATCH_HEADER_SIZE);
> > +     section_num = le32_to_cpu(globaldesc->section_num);
> > +
> > +     if (fw->size < MTK_FW_ROM_PATCH_HEADER_SIZE +
> > MTK_FW_ROM_PATCH_GD_SIZE +
> > +                    (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE *
> > section_num) {
> > +             bt_dev_err(hdev, "CBMCU firmware truncated: size=%zu,
> > expected=%zu (section_num=%u)",
> > +                        fw->size,
> > +                        MTK_FW_ROM_PATCH_HEADER_SIZE +
> > MTK_FW_ROM_PATCH_GD_SIZE +
> > +                        (size_t)MTK_FW_ROM_PATCH_SEC_MAP_SIZE *
> > section_num,
> > +                        section_num);
> > +             err = -EINVAL;
> > +             goto err_release_fw;
> > +     }
> > +
> > +     bt_dev_info(hdev, "CBMCU Version: 0x%04x%04x, Build Time:
> > %s",
> > +                 le16_to_cpu(hdr->hwver), le16_to_cpu(hdr->swver),
> > hdr->datetime);
> > +
> > +     /* Phase 1: Download section type MTK_SEC_CBMCU_DESC */
> > +     for (i = 0; i < section_num; i++) {
> > +             sectionmap = (struct btmtk_section_map *)
> > +                     (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
> > +                      MTK_FW_ROM_PATCH_GD_SIZE +
> > +                      MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
> > +
> > +             /* Only process MTK_SEC_CBMCU_DESC section in Phase 1
> > */
> > +             if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) !=
> > MTK_SEC_CBMCU_DESC)
> > +                     continue;
> > +
> > +             section_offset = le32_to_cpu(sectionmap->secoffset);
> > +             dl_size = le32_to_cpu(sectionmap->secsize);
> > +
> > +             if (dl_size == 0)
> > +                     continue;
> > +
> > +             if (section_offset > fw->size ||
> > +                 dl_size > fw->size - section_offset) {
> > +                     bt_dev_err(hdev, "CBMCU Phase 1 section out
> > of bounds");
> > +                     err = -EINVAL;
> > +                     goto err_release_fw;
> > +             }
> > +
> > +             cert_len = MTK_FW_ROM_PATCH_GD_SIZE +
> > +                        MTK_FW_ROM_PATCH_SEC_MAP_SIZE *
> > section_num +
> > +                        dl_size;
> > +
> > +             /* Query cbmcu section */
> > +             err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync,
> > 0, NULL,
> > +                                             cert_len);
> > +             if (err < 0)
> > +                     goto err_release_fw;
> > +
> > +             cert_buf = kmalloc(cert_len, GFP_KERNEL);
> > +             if (!cert_buf) {
> > +                     err = -ENOMEM;
> > +                     goto err_release_fw;
> > +             }
> > +
> > +             /* Copy Global Descriptor + All Section Maps */
> > +             memcpy(cert_buf,
> > +                    fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE,
> > +                    MTK_FW_ROM_PATCH_GD_SIZE +
> > MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num);
> > +
> > +             /* Copy Phase 1 section data */
> > +             memcpy(cert_buf + MTK_FW_ROM_PATCH_GD_SIZE +
> > +                    MTK_FW_ROM_PATCH_SEC_MAP_SIZE * section_num,
> > +                    fw_ptr + section_offset,
> > +                    dl_size);
> > +
> > +             /* Download Phase 1 section */
> > +             err = btmtk_download_cbmcu_section(hdev,
> > wmt_cmd_sync,
> > +                                                cert_buf,
> > cert_len);
> > +             kfree(cert_buf);
> > +             cert_buf = NULL;
> > +
> > +             if (err < 0) {
> > +                     bt_dev_err(hdev, "Failed to download CBMCU
> > Phase 1 section (%d)", err);
> > +                     goto err_release_fw;
> > +             }
> > +
> > +             break;
> > +     }
> > +
> > +     /* Phase 2: Download other sections (type !=
> > MTK_SEC_CBMCU_DESC) */
> > +     for (i = 0; i < section_num; i++) {
> > +             sectionmap = (struct btmtk_section_map *)
> > +                     (fw_ptr + MTK_FW_ROM_PATCH_HEADER_SIZE +
> > +                      MTK_FW_ROM_PATCH_GD_SIZE +
> > +                      MTK_FW_ROM_PATCH_SEC_MAP_SIZE * i);
> > +
> > +             /* Skip MTK_SEC_CBMCU_DESC section in Phase 2 */
> > +             if ((le32_to_cpu(sectionmap->sectype) & 0xFFFF) ==
> > MTK_SEC_CBMCU_DESC)
> > +                     continue;
> > +
> > +             section_offset = le32_to_cpu(sectionmap->secoffset);
> > +             dl_size = le32_to_cpu(sectionmap-
> > >bin_info_spec.dlsize);
> > +
> > +             if (dl_size == 0)
> > +                     continue;
> > +
> > +             if (section_offset > fw->size ||
> > +                 dl_size > fw->size - section_offset) {
> > +                     bt_dev_err(hdev, "CBMCU Phase 2 section %d
> > out of bounds", i);
> > +                     err = -EINVAL;
> > +                     goto err_release_fw;
> > +             }
> > +
> > +             /* Query cbmcu section */
> > +             err = btmtk_query_cbmcu_section(hdev, wmt_cmd_sync,
> > 1,
> > +                                             (u8 *)&sectionmap-
> > >bin_info_spec,
> > +                                             0);
> > +             if (err < 0)
> > +                     goto err_release_fw;
> > +
> > +             /* Download section data */
> > +             err = btmtk_download_cbmcu_section(hdev,
> > wmt_cmd_sync,
> > +                                                fw_ptr +
> > section_offset,
> > +                                                dl_size);
> > +             if (err < 0) {
> > +                     bt_dev_err(hdev, "Failed to download CBMCU
> > section %d (%d)", i, err);
> > +                     goto err_release_fw;
> > +             }
> > +     }
> > +
> > +     bt_dev_info(hdev, "CBMCU firmware download completed");
> > +
> > +err_release_fw:
> > +     release_firmware(fw);
> > +     return err;
> > +}
> > +
> > +static int btmtk_setup_cbmcu_firmware(struct hci_dev *hdev,
> > +                                   wmt_cmd_sync_func_t
> > wmt_cmd_sync,
> > +                                   u32 dev_id)
> > +{
> > +     char cbmcu_fwname[64];
> > +     u8 patch_status;
> > +     int err;
> > +
> > +     err = btmtk_cbmcu_patch_status(hdev, wmt_cmd_sync,
> > &patch_status);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     bt_dev_dbg(hdev, "CBMCU patch status: 0x%02x", patch_status);
> > +
> > +     if (patch_status != BTMTK_WMT_PATCH_UNDONE)
> > +             return 0;
> > +
> > +     snprintf(cbmcu_fwname, sizeof(cbmcu_fwname),
> > +              "mediatek/mt7928/CBMCU_CODE_MT%04x_1_1.bin",
> > +              dev_id & 0xffff);
> > +
> > +     err = btmtk_load_cbmcu_firmware(hdev, cbmcu_fwname,
> > wmt_cmd_sync);
> > +     if (err < 0) {
> > +             bt_dev_err(hdev, "Failed to download CBMCU firmware
> > (%d)", err);
> > +             return err;
> > +     }
> > +
> > +     err = btmtk_enable_cbmcu_patch(hdev, wmt_cmd_sync);
> > +     if (err < 0)
> > +             return err;
> > +
> > +     return 0;
> > +}
> > +
> >   int btmtk_usb_subsys_reset(struct hci_dev *hdev, u32 dev_id)
> >   {
> >       u32 val;
> > @@ -894,7 +1233,7 @@ int btmtk_usb_subsys_reset(struct hci_dev
> > *hdev, u32 dev_id)
> >               if (err < 0)
> >                       return err;
> >               msleep(100);
> > -     } else if (dev_id == 0x7925 || dev_id == 0x6639) {
> > +     } else if (dev_id == 0x7925 || dev_id == 0x6639 || dev_id ==
> > 0x7935) {
> >               err = btmtk_usb_uhw_reg_read(hdev,
> > MTK_BT_RESET_REG_CONNV3, &val);
> >               if (err < 0)
> >                       return err;
> > @@ -1379,6 +1718,15 @@ int btmtk_usb_setup(struct hci_dev *hdev)
> >       case 0x7668:
> >               fwname = FIRMWARE_MT7668;
> >               break;
> > +     case 0x7935:
> > +             /* Requires CBMCU firmware before BT firmware */
> > +             err = btmtk_setup_cbmcu_firmware(hdev,
> > btmtk_usb_hci_wmt_sync,
> > +                                              dev_id);
> > +             if (err < 0) {
> > +                     bt_dev_err(hdev, "Failed to set up CBMCU
> > firmware (%d)", err);
> > +                     return err;
> > +             }
> > +             fallthrough;
> >       case 0x7922:
> >       case 0x7925:
> >               /*
> > @@ -1596,3 +1944,5 @@ MODULE_FIRMWARE(FIRMWARE_MT7922);
> >   MODULE_FIRMWARE(FIRMWARE_MT7961);
> >   MODULE_FIRMWARE(FIRMWARE_MT7925);
> >   MODULE_FIRMWARE(FIRMWARE_MT7927);
> > +MODULE_FIRMWARE(FIRMWARE_MT7928);
> > +MODULE_FIRMWARE(FIRMWARE_MT7928_CBMCU);
> > diff --git a/drivers/bluetooth/btmtk.h b/drivers/bluetooth/btmtk.h
> > index 51c18dde0a80..5fe4964b031b 100644
> > --- a/drivers/bluetooth/btmtk.h
> > +++ b/drivers/bluetooth/btmtk.h
> > @@ -9,6 +9,8 @@
> >   #define FIRMWARE_MT7961            
> > "mediatek/BT_RAM_CODE_MT7961_1_2_hdr.bin"
> >   #define FIRMWARE_MT7925            
> > "mediatek/mt7925/BT_RAM_CODE_MT7925_1_1_hdr.bin"
> >   #define FIRMWARE_MT7927            
> > "mediatek/mt7927/BT_RAM_CODE_MT6639_2_1_hdr.bin"
> > +#define FIRMWARE_MT7928             
> > "mediatek/mt7928/BT_RAM_CODE_MT7935_1_1_hdr.bin"
> > +#define FIRMWARE_MT7928_CBMCU       
> > "mediatek/mt7928/CBMCU_CODE_MT7935_1_1.bin"
> > 
> >   #define HCI_EV_WMT 0xe4
> >   #define HCI_WMT_MAX_EVENT_SIZE              64
> > @@ -54,6 +56,7 @@ enum {
> >       BTMTK_WMT_RST = 0x7,
> >       BTMTK_WMT_REGISTER = 0x8,
> >       BTMTK_WMT_SEMAPHORE = 0x17,
> > +     BTMTK_WMT_CBMCU_DWNLD = 0x58,
> >   };
> > 
> >   enum {
> 
> gemini/gemini-3.1-pro-preview commented on three things [1].
> 
> 
> Kind regards,
> 
> Paul
> 
> 
Chris Lu

> [1]:
> https://urldefense.com/v3/__https://sashiko.dev/*/message/20260623034121.691031-3-chris.lu*40mediatek.com__;IyU!!CTRNKA9wMg0ARbw!ii42FfNLcQuMCshcfe6bdBeLaQhxFj2FZIYjOj4uB6QunDTHpFPsHGHtHPrvdtmgaiEM53wIdFbFEJKzcECI1Q$


^ permalink raw reply

* [PATCH] mmc: cqhci: Remove unused intmask parameter from cqhci_irq()
From: Chanwoo Lee @ 2026-06-24  2:19 UTC (permalink / raw)
  To: Adrian Hunter, Asutosh Das, Ritesh Harjani, Ulf Hansson,
	Chaotian Jing, Matthias Brugger, AngeloGioacchino Del Regno,
	Kamal Dasu, Al Cooper, Broadcom internal kernel review list,
	Florian Fainelli, Haibo Chen, Frank Li, Sascha Hauer,
	Pengutronix Kernel Team, Fabio Estevam, Michal Simek,
	Thierry Reding, Jonathan Hunter,
	open list:EMMC CMDQ HOST CONTROLLER INTERFACE (CQHCI) DRIVER,
	open list, moderated list:ARM/Mediatek SoC support,
	moderated list:ARM/Mediatek SoC support,
	open list:ARM/QUALCOMM MAILING LIST,
	open list:TEGRA ARCHITECTURE SUPPORT
  Cc: Chanwoo Lee
In-Reply-To: <CGME20260624021955epcas1p2ba2fa4eb8bd0aafaf7b46bde2cf524be@epcas1p2.samsung.com>

The intmask parameter of cqhci_irq() is never used within the function
body. The function reads the CQHCI interrupt status directly via
cqhci_readl() and processes interrupts independently of the SDHCI
intmask value passed by callers.

Signed-off-by: Chanwoo Lee <cw9316.lee@samsung.com>
---
 drivers/mmc/host/cqhci-core.c       | 3 +--
 drivers/mmc/host/cqhci.h            | 3 +--
 drivers/mmc/host/mtk-sd.c           | 2 +-
 drivers/mmc/host/sdhci-brcmstb.c    | 2 +-
 drivers/mmc/host/sdhci-esdhc-imx.c  | 2 +-
 drivers/mmc/host/sdhci-msm.c        | 2 +-
 drivers/mmc/host/sdhci-of-arasan.c  | 2 +-
 drivers/mmc/host/sdhci-of-dwcmshc.c | 2 +-
 drivers/mmc/host/sdhci-pci-core.c   | 2 +-
 drivers/mmc/host/sdhci-pci-gli.c    | 2 +-
 drivers/mmc/host/sdhci-tegra.c      | 2 +-
 drivers/mmc/host/sdhci_am654.c      | 2 +-
 12 files changed, 12 insertions(+), 14 deletions(-)

diff --git a/drivers/mmc/host/cqhci-core.c b/drivers/mmc/host/cqhci-core.c
index 178277d90c31..98ceb0b9a6d1 100644
--- a/drivers/mmc/host/cqhci-core.c
+++ b/drivers/mmc/host/cqhci-core.c
@@ -819,8 +819,7 @@ static void cqhci_finish_mrq(struct mmc_host *mmc, unsigned int tag)
 	mmc_cqe_request_done(mmc, mrq);
 }
 
-irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
-		      int data_error)
+irqreturn_t cqhci_irq(struct mmc_host *mmc, int cmd_error, int data_error)
 {
 	u32 status;
 	unsigned long tag = 0, comp_status;
diff --git a/drivers/mmc/host/cqhci.h b/drivers/mmc/host/cqhci.h
index 3668856531c1..8fbbc48c3f85 100644
--- a/drivers/mmc/host/cqhci.h
+++ b/drivers/mmc/host/cqhci.h
@@ -315,8 +315,7 @@ static inline u32 cqhci_readl(struct cqhci_host *host, int reg)
 
 struct platform_device;
 
-irqreturn_t cqhci_irq(struct mmc_host *mmc, u32 intmask, int cmd_error,
-		      int data_error);
+irqreturn_t cqhci_irq(struct mmc_host *mmc, int cmd_error, int data_error);
 int cqhci_init(struct cqhci_host *cq_host, struct mmc_host *mmc, bool dma64);
 struct cqhci_host *cqhci_pltfm_init(struct platform_device *pdev);
 int cqhci_deactivate(struct mmc_host *mmc);
diff --git a/drivers/mmc/host/mtk-sd.c b/drivers/mmc/host/mtk-sd.c
index b2680cc054bd..01ea3adbdf3b 100644
--- a/drivers/mmc/host/mtk-sd.c
+++ b/drivers/mmc/host/mtk-sd.c
@@ -1805,7 +1805,7 @@ static irqreturn_t msdc_cmdq_irq(struct msdc_host *host, u32 intsts)
 			cmd_err, dat_err, intsts);
 	}
 
-	return cqhci_irq(mmc, 0, cmd_err, dat_err);
+	return cqhci_irq(mmc, cmd_err, dat_err);
 }
 
 static irqreturn_t msdc_irq(int irq, void *dev_id)
diff --git a/drivers/mmc/host/sdhci-brcmstb.c b/drivers/mmc/host/sdhci-brcmstb.c
index 57e45951644e..1de2f05fd958 100644
--- a/drivers/mmc/host/sdhci-brcmstb.c
+++ b/drivers/mmc/host/sdhci-brcmstb.c
@@ -430,7 +430,7 @@ static u32 sdhci_brcmstb_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-esdhc-imx.c b/drivers/mmc/host/sdhci-esdhc-imx.c
index 18ecddd6df6f..d0fa83f67a80 100644
--- a/drivers/mmc/host/sdhci-esdhc-imx.c
+++ b/drivers/mmc/host/sdhci-esdhc-imx.c
@@ -1503,7 +1503,7 @@ static u32 esdhc_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-msm.c b/drivers/mmc/host/sdhci-msm.c
index 0882ce74e0c9..ceed47ccfda8 100644
--- a/drivers/mmc/host/sdhci-msm.c
+++ b/drivers/mmc/host/sdhci-msm.c
@@ -2165,7 +2165,7 @@ static u32 sdhci_msm_cqe_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 	return 0;
 }
 
diff --git a/drivers/mmc/host/sdhci-of-arasan.c b/drivers/mmc/host/sdhci-of-arasan.c
index 785d3acb18c5..4ca73e7d799e 100644
--- a/drivers/mmc/host/sdhci-of-arasan.c
+++ b/drivers/mmc/host/sdhci-of-arasan.c
@@ -555,7 +555,7 @@ static u32 sdhci_arasan_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index eef53455b8ee..4c5fa6a6931d 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -624,7 +624,7 @@ static u32 dwcmshc_cqe_irq_handler(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-pci-core.c b/drivers/mmc/host/sdhci-pci-core.c
index c347fac24515..b121d896a804 100644
--- a/drivers/mmc/host/sdhci-pci-core.c
+++ b/drivers/mmc/host/sdhci-pci-core.c
@@ -215,7 +215,7 @@ static u32 sdhci_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-pci-gli.c b/drivers/mmc/host/sdhci-pci-gli.c
index 6e4084407662..b55618566d65 100644
--- a/drivers/mmc/host/sdhci-pci-gli.c
+++ b/drivers/mmc/host/sdhci-pci-gli.c
@@ -1760,7 +1760,7 @@ static u32 sdhci_gl9763e_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci-tegra.c b/drivers/mmc/host/sdhci-tegra.c
index 820ce4dae58b..221e48b59f48 100644
--- a/drivers/mmc/host/sdhci-tegra.c
+++ b/drivers/mmc/host/sdhci-tegra.c
@@ -1280,7 +1280,7 @@ static u32 sdhci_tegra_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
diff --git a/drivers/mmc/host/sdhci_am654.c b/drivers/mmc/host/sdhci_am654.c
index d235b0aecfdb..2a27db2f558b 100644
--- a/drivers/mmc/host/sdhci_am654.c
+++ b/drivers/mmc/host/sdhci_am654.c
@@ -462,7 +462,7 @@ static u32 sdhci_am654_cqhci_irq(struct sdhci_host *host, u32 intmask)
 	if (!sdhci_cqe_irq(host, intmask, &cmd_error, &data_error))
 		return intmask;
 
-	cqhci_irq(host->mmc, intmask, cmd_error, data_error);
+	cqhci_irq(host->mmc, cmd_error, data_error);
 
 	return 0;
 }
-- 
2.43.0



^ permalink raw reply related

* [PATCH] i2c: mediatek: fix WRRD for SoCs without auto_restart option
From: Roman Vivchar via B4 Relay @ 2026-06-23 21:40 UTC (permalink / raw)
  To: Qii Wang, Andi Shyti, Matthias Brugger,
	AngeloGioacchino Del Regno
  Cc: linux-i2c, linux-kernel, linux-arm-kernel, linux-mediatek,
	Roman Vivchar

From: Roman Vivchar <rva333@protonmail.com>

MediaTek mt65xx family SoCs have no auto restart, however, they still
support the WRRD mode in the hardware. Because auto_restart is set to 0,
the WRRD mode will be never enabled, leading to read errors.

Fix this by removing auto_restart check from the WRRD enable path.

Signed-off-by: Roman Vivchar <rva333@protonmail.com>
---
This is a preparation for the mt6572/6595 upstreaming.

mt65xx family SoCs don't have auto restart, but vendor kernels keep using
WRRD mode. Lack of the WRRD mode makes i2c reads impossible from both
userspace and kernel drivers.

Without patch (mt6595, da9210 buck at 0x68):
~ # i2cget -y 1 0x68 0x00
Error: Read failed
~ # i2cget -y 1 0x68 0x01
Error: Read failed

With patch:
~ # i2cget -y 1 0x68 0x00
0x80
~ # i2cget -y 1 0x68 0x01
0x00

Same behavior observed on mt6572 devices.

This change doesn't affect SoCs with auto restart option.
---
 drivers/i2c/busses/i2c-mt65xx.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/i2c/busses/i2c-mt65xx.c b/drivers/i2c/busses/i2c-mt65xx.c
index 126040ca05f1..307925fb78e3 100644
--- a/drivers/i2c/busses/i2c-mt65xx.c
+++ b/drivers/i2c/busses/i2c-mt65xx.c
@@ -1258,7 +1258,7 @@ static int mtk_i2c_transfer(struct i2c_adapter *adap,
 	i2c->auto_restart = i2c->dev_comp->auto_restart;
 
 	/* checking if we can skip restart and optimize using WRRD mode */
-	if (i2c->auto_restart && num == 2) {
+	if (num == 2) {
 		if (!(msgs[0].flags & I2C_M_RD) && (msgs[1].flags & I2C_M_RD) &&
 		    msgs[0].addr == msgs[1].addr) {
 			i2c->auto_restart = 0;

---
base-commit: 8cd9520d35a6c38db6567e97dd93b1f11f185dc6
change-id: 20260623-6572-6595-i2c-6ec9c4e6a6a6

Best regards,
--  
Roman Vivchar <rva333@protonmail.com>




^ permalink raw reply related

* Re: [PATCH v3 net] net: airoha: Fix TX scheduler queue mask loop upper bound
From: patchwork-bot+netdevbpf @ 2026-06-23 20:40 UTC (permalink / raw)
  To: Wayen Yan
  Cc: netdev, lorenzo, horms, pabeni, kuba, edumazet, andrew+netdev,
	angelogioacchino.delregno, matthias.bgg, linux-arm-kernel,
	linux-mediatek
In-Reply-To: <178187479434.2400840.1312143943526335838@gmail.com>

Hello:

This patch was applied to netdev/net.git (main)
by Jakub Kicinski <kuba@kernel.org>:

On Fri, 19 Jun 2026 21:12:06 +0800 you wrote:
> In airoha_qdma_set_chan_tx_sched(), the loop clearing queue mask was
> using AIROHA_NUM_TX_RING (32) instead of AIROHA_NUM_QOS_QUEUES (8).
> 
> Each channel has 8 queues, and TXQ_DISABLE_CHAN_QUEUE_MASK(channel, i)
> computes BIT(i + (channel * 8)). With i ranging 0..31, this causes:
> - channel 0: clears bit 0..31 (all 4 channels) instead of 0..7
> - channel 1: clears bit 8..31 (channels 1-3) instead of 8..15
> - channel 2: clears bit 16..31 (channels 2-3) instead of 16..23
> - channel 3: clears bit 24..31 (channel 3 only) - correct by accident
> 
> [...]

Here is the summary with links:
  - [v3,net] net: airoha: Fix TX scheduler queue mask loop upper bound
    https://git.kernel.org/netdev/net/c/245043dfc210

You are awesome, thank you!
-- 
Deet-doot-dot, I am a bot.
https://korg.docs.kernel.org/patchwork/pwbot.html




^ permalink raw reply

* Re: [PATCH net v2] net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
From: Lorenzo Bianconi @ 2026-06-23 20:32 UTC (permalink / raw)
  To: Simon Horman
  Cc: andrew+netdev, davem, edumazet, kuba, pabeni, leto.liu,
	linux-arm-kernel, linux-mediatek, netdev, brown.huang
In-Reply-To: <20260623105341.1076863-3-horms@kernel.org>

[-- Attachment #1: Type: text/plain, Size: 5732 bytes --]

On Jun 23, Simon Horman wrote:
> This is an AI-generated review of your patch. The human sending this
> email has considered the AI review valid, or at least plausible.
> Full review at: https://sashiko.dev
> 
> Simon says: I'd appreciate it if you could look over the first two issues
>             flagged below in the context of this patch. While the third
> 	    and final issue looks like it is in possible separate follow-up
> 	    territory.
> 
> ---
> net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
>     
> This patch introduces a retry loop to airoha_qdma_set_trtcm_param() to mitigate
> bus timeouts when configuring QDMA parameters. It attempts to verify that the
> configuration is correctly applied to the hardware.
> 
> > diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> > index 3370c3df7c107..bb5c0599a4eed 100644
> > --- a/drivers/net/ethernet/airoha/airoha_eth.c
> > +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> > @@ -2673,14 +2673,30 @@ static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
> >  		     FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
> >  		     FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
> >  		     FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
> > +	int i;
> >  
> > -	airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> > -	airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> > +	for (i = 0; i < 100; i++) {
> > +		u32 data;
> >  
> > -	return read_poll_timeout(airoha_qdma_rr, val,
> > -				 val & TRTCM_PARAM_RW_DONE_MASK,
> > -				 USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
> > -				 qdma, REG_TRTCM_CFG_PARAM(addr));
> > +		airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> > +		wmb();
> > +		airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> > +
> > +		if (read_poll_timeout(airoha_qdma_rr, data,
> > +				      data & TRTCM_PARAM_RW_DONE_MASK,
> > +				      USEC_PER_MSEC, 10 * USEC_PER_MSEC,
> > +				      true, qdma, REG_TRTCM_CFG_PARAM(addr)))
> > +			return -ETIMEDOUT;
> 
> [Severity: High]
> If a bus timeout causes the write phase's read_poll_timeout() in
> airoha_qdma_set_trtcm_param() to fail, this immediately returns -ETIMEDOUT,
> aborting the 100-iteration retry loop.
> 
> Doesn't this defeat the mitigation?

I agree we just run 'continue' here instead of returing -ETIMEDOUT

> 
> > +
> > +		if (airoha_qdma_get_trtcm_param(qdma, channel, addr, param,
> > +						mode, &data, NULL))
> > +			continue;
> 
> [Severity: High]
> If airoha_qdma_get_trtcm_param() times out during the verification phase, it
> indicates the hardware hasn't finished the read command (DONE bit is still 0).
> 
> The continue statement then immediately starts the next iteration, writing new
> commands to DATA_LOW and CFG_PARAM.
> 
> Can writing to command registers while the hardware state machine is actively
> busy violate hardware protocols and lead to unrecoverable lockups?

@Brown and @Leto: can you please comment on this case?

> 
> > +
> > +		if (data == val)
> > +			return 0;
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but if the bus drops the command
> write to REG_TRTCM_CFG_PARAM, the hardware never executes the command and the
> TRTCM_PARAM_RW_DONE_MASK bit remains set from the previous operation.
> 
> Consequently, read_poll_timeout() returns an immediate false success.
> 
> If both the set and get command writes are dropped by the bus, the get
> operation will read back the newly written DATA_LOW register value.
> 
> Will the data == val check evaluate to true and falsely report success without
> the hardware ever processing the configuration?

This seems quite a 'pathological' scenario, is it really an issue?

Regards,
Lorenzo

> 
> > +	}
> > +
> > +	return -EBUSY;
> >  }
> >  
> 
> [ ... ]
> 
> >  static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
> >  					u32 addr, enum trtcm_mode_type mode,
> >  					bool enable, u32 enable_mask)
> >  {
> >  	u32 val;
> >  
> >  	if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
> >  					mode, &val, NULL))
> >  		return -EINVAL;
> 
> [Severity: High]
> Standalone calls to airoha_qdma_get_trtcm_param(), like this one in
> airoha_qdma_set_trtcm_config(), lack a retry loop entirely.
> 
> Does this leave read-modify-write cycles fully vulnerable to the same bus
> timeouts the patch attempts to mitigate?
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but the patch mitigates bus
> timeouts in airoha_qdma_set_trtcm_param() while entirely missing
> airoha_qdma_set_rl_param().
> 
> The latter performs the exact same write-and-poll sequence on identical QDMA
> configuration registers:
> 
> drivers/net/ethernet/airoha/airoha_eth.c:airoha_qdma_set_rl_param() {
>     ...
>     return read_poll_timeout(..., val & RATE_LIMIT_PARAM_RW_DONE_MASK, ...);
> }
> 
> If bus timeouts affect QDMA access broadly as stated in the commit message, are
> the rate-limiting configuration paths randomly vulnerable to the same failures?
> 
> [Severity: High]
> This isn't a bug introduced by this patch, but in airoha_tc_remove_htb_queue(),
> hardware teardown incorrectly uses queue + 1 instead of queue:
> 
> drivers/net/ethernet/airoha/airoha_eth.c:airoha_tc_remove_htb_queue() {
>     ...
>     airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
>     ...
> }
> 
> Does this leave the removed queue's hardware limits permanently active
> (resource leak) and inadvertently disable the rate limits for queue + 1,
> corrupting the QoS state of another active channel?

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 228 bytes --]

^ permalink raw reply

* Re: [PATCH] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Brian Masney @ 2026-06-23 14:35 UTC (permalink / raw)
  To: Myeonghun Pak
  Cc: Yassine Oudjana, Michael Turquette, Stephen Boyd,
	Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
	linux-mediatek, linux-kernel, linux-arm-kernel, Ijae Kim
In-Reply-To: <20260623094125.95791-1-mhun512@gmail.com>

Hi Myeonghun,

On Tue, Jun 23, 2026 at 06:41:11PM +0900, Myeonghun Pak wrote:
> mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
> clk_mt6735_apmixed_remove() unregisters them on driver removal.
> 
> If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
> probe returns the error directly and the remove callback is not run. This
> leaves the registered PLL clocks behind on the probe failure path.
> 
> Add an unregister_plls error path so provider registration failures unwind the
> PLLs before returning the error.

Please run your patches through checkpatch.pl before sending.

> 
> Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
> Co-developed-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Ijae Kim <ae878000@gmail.com>
> Signed-off-by: Myeonghun Pak <mhun512@gmail.com>
> 
> ---
>  drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 9 ++++++++-
>  1 file changed, 8 insertions(+), 1 deletion(-)
> 
> diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> index 9e30c089a2..04cf9665ec 100644
> --- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> +++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
> @@ -102,10 +102,17 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
>  
>  	ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
>  					  clk_data);
> -	if (ret)
> +	if (ret) {
>  		dev_err(&pdev->dev,
>  			"Failed to register clock provider: %d\n", ret);
> +		goto unregister_plls;
> +	}
> +
> +	return 0;
>  
> +unregister_plls:
> +	mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
> +				clk_data);
>  	return ret;
>  }

This fix is correct. Since only one path will encounter this, personally
I would just put the call to mtk_clk_unregister_plls() inside the if
statement above to simplify this further.

Brian



^ permalink raw reply

* Re: [PATCH] mtd: nand: mtk-ecc: stop on ECC idle timeouts
From: Miquel Raynal @ 2026-06-23 14:08 UTC (permalink / raw)
  To: Pengpeng Hou
  Cc: Richard Weinberger, Vignesh Raghavendra, Matthias Brugger,
	AngeloGioacchino Del Regno, Jorge Ramirez-Ortiz, Boris Brezillon,
	linux-mtd, linux-kernel, linux-arm-kernel, linux-mediatek
In-Reply-To: <20260623135729.52304-1-pengpeng@iscas.ac.cn>

Hello,

> diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c
> index c75bb8b80..96703f0a4 100644
> --- a/drivers/mtd/nand/ecc-mtk.c
> +++ b/drivers/mtd/nand/ecc-mtk.c
> @@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = {
>  	[ECC_DECIRQ_STA] =      0x144,
>  };
>  
> -static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
> -				     enum mtk_ecc_operation op)
> +static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc,
> +				    enum mtk_ecc_operation op)

Looks good, but could you follow up with a patch which removes the
inline keyword as well? It does not seem to serve any purpose there.


^ permalink raw reply

* [PATCH] dt-bindings: mediatek: cec: Correct the compatibles for mt7623-mt8167
From: Luca Leonardo Scorcia @ 2026-06-23 13:57 UTC (permalink / raw)
  To: linux-mediatek
  Cc: Luca Leonardo Scorcia, Chun-Kuang Hu, Philipp Zabel, David Airlie,
	Simona Vetter, Maarten Lankhorst, Maxime Ripard,
	Thomas Zimmermann, Rob Herring, Krzysztof Kozlowski, Conor Dooley,
	Matthias Brugger, AngeloGioacchino Del Regno, CK Hu, Jitao shi,
	dri-devel, devicetree, linux-kernel, linux-arm-kernel

The HDMI CEC driver for both mt7623 and mt8167 is actually the same as
mt8173-cec and the mt7623n.dtsi board include file already uses mt8173-cec
compatible as a fallback, but the documentation lists them as separate
entries. Correct the binding by adding the correct fallback.

This change fixes a dtbs_check error.

Signed-off-by: Luca Leonardo Scorcia <l.scorcia@gmail.com>
---
 .../bindings/display/mediatek/mediatek,cec.yaml       | 11 +++++++----
 1 file changed, 7 insertions(+), 4 deletions(-)

diff --git a/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml b/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
index 080cf321209e..4d741ba415e8 100644
--- a/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
+++ b/Documentation/devicetree/bindings/display/mediatek/mediatek,cec.yaml
@@ -15,10 +15,13 @@ description: |
 
 properties:
   compatible:
-    enum:
-      - mediatek,mt7623-cec
-      - mediatek,mt8167-cec
-      - mediatek,mt8173-cec
+    oneOf:
+      - const: mediatek,mt8173-cec
+      - items:
+        - enum:
+          - mediatek,mt7623-cec
+          - mediatek,mt8167-cec
+        - const: mediatek,mt8173-cec
 
   reg:
     maxItems: 1
-- 
2.43.0



^ permalink raw reply related

* [PATCH] mtd: nand: mtk-ecc: stop on ECC idle timeouts
From: Pengpeng Hou @ 2026-06-23 13:57 UTC (permalink / raw)
  To: Miquel Raynal, Richard Weinberger, Vignesh Raghavendra,
	Matthias Brugger, AngeloGioacchino Del Regno, Jorge Ramirez-Ortiz,
	Boris Brezillon
  Cc: Pengpeng Hou, linux-mtd, linux-kernel, linux-arm-kernel,
	linux-mediatek

mtk_ecc_wait_idle() logs when the encoder or decoder does not become
idle, but returns void. Callers can therefore configure a non-idle ECC
engine or read parity bytes after an unconfirmed encoder idle state.

Return the idle poll result and propagate it from the enable and encode
paths that require the engine to be idle before continuing.

Fixes: 1d6b1e464950 ("mtd: mediatek: driver for MTK Smart Device")
Signed-off-by: Pengpeng Hou <pengpeng@iscas.ac.cn>
---
 drivers/mtd/nand/ecc-mtk.c | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/drivers/mtd/nand/ecc-mtk.c b/drivers/mtd/nand/ecc-mtk.c
index c75bb8b80..96703f0a4 100644
--- a/drivers/mtd/nand/ecc-mtk.c
+++ b/drivers/mtd/nand/ecc-mtk.c
@@ -123,8 +123,8 @@ static int mt7622_ecc_regs[] = {
 	[ECC_DECIRQ_STA] =      0x144,
 };
 
-static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
-				     enum mtk_ecc_operation op)
+static inline int mtk_ecc_wait_idle(struct mtk_ecc *ecc,
+				    enum mtk_ecc_operation op)
 {
 	struct device *dev = ecc->dev;
 	u32 val;
@@ -136,6 +136,8 @@ static inline void mtk_ecc_wait_idle(struct mtk_ecc *ecc,
 	if (ret)
 		dev_warn(dev, "%s NOT idle\n",
 			 op == ECC_ENCODE ? "encoder" : "decoder");
+
+	return ret;
 }
 
 static irqreturn_t mtk_ecc_irq(int irq, void *id)
@@ -312,7 +314,11 @@ int mtk_ecc_enable(struct mtk_ecc *ecc, struct mtk_ecc_config *config)
 		return ret;
 	}
 
-	mtk_ecc_wait_idle(ecc, op);
+	ret = mtk_ecc_wait_idle(ecc, op);
+	if (ret) {
+		mutex_unlock(&ecc->lock);
+		return ret;
+	}
 
 	ret = mtk_ecc_config(ecc, config);
 	if (ret) {
@@ -412,7 +418,9 @@ int mtk_ecc_encode(struct mtk_ecc *ecc, struct mtk_ecc_config *config,
 	if (ret)
 		goto timeout;
 
-	mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+	ret = mtk_ecc_wait_idle(ecc, ECC_ENCODE);
+	if (ret)
+		goto timeout;
 
 	/* Program ECC bytes to OOB: per sector oob = FDM + ECC + SPARE */
 	len = (config->strength * ecc->caps->parity_bits + 7) >> 3;
-- 
2.50.1 (Apple Git-155)



^ permalink raw reply related

* [PATCH] remoteproc: mediatek: Unregister init IPI on share buffer failure
From: Haoxiang Li @ 2026-06-23 13:38 UTC (permalink / raw)
  To: andersson, mathieu.poirier, matthias.bgg,
	angelogioacchino.delregno, olivia.wen
  Cc: linux-remoteproc, linux-kernel, linux-arm-kernel, linux-mediatek,
	Haoxiang Li

scp_rproc_init() registers the SCP init IPI before allocating the IPI
share buffer. If the share buffer allocation fails, the error path jumps
directly to release_dev_mem and leaves the init IPI registered.

Route that error path through the IPI unregister label so the registered
handler is cleared before unwinding the remaining resources.

Fixes: c08a82494500 ("remoteproc: mediatek: Support setting DRAM and IPI shared buffer sizes")
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
 drivers/remoteproc/mtk_scp.c | 5 +++--
 1 file changed, 3 insertions(+), 2 deletions(-)

diff --git a/drivers/remoteproc/mtk_scp.c b/drivers/remoteproc/mtk_scp.c
index 85a74c9ec521..648b69f96624 100644
--- a/drivers/remoteproc/mtk_scp.c
+++ b/drivers/remoteproc/mtk_scp.c
@@ -1227,7 +1227,7 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev,
 	if (!scp->share_buf) {
 		dev_err(dev, "Failed to allocate IPI share buffer\n");
 		ret = -ENOMEM;
-		goto release_dev_mem;
+		goto unregister_ipi;
 	}
 
 	init_waitqueue_head(&scp->run.wq);
@@ -1248,9 +1248,10 @@ static struct mtk_scp *scp_rproc_init(struct platform_device *pdev,
 
 remove_subdev:
 	scp_remove_rpmsg_subdev(scp);
-	scp_ipi_unregister(scp, SCP_IPI_INIT);
 	kfree(scp->share_buf);
 	scp->share_buf = NULL;
+unregister_ipi:
+	scp_ipi_unregister(scp, SCP_IPI_INIT);
 release_dev_mem:
 	scp_unmap_memory_region(scp);
 	for (i = 0; i < SCP_IPI_MAX; i++)
-- 
2.25.1



^ permalink raw reply related

* Re: [PATCH net v2] net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
From: Simon Horman @ 2026-06-23 10:53 UTC (permalink / raw)
  To: lorenzo
  Cc: Simon Horman, andrew+netdev, davem, edumazet, kuba, pabeni,
	leto.liu, linux-arm-kernel, linux-mediatek, netdev, brown.huang
In-Reply-To: <20260622-airoha_qdma_set_trtcm_param-retry-fix-v2-1-95a83ac99dbd@kernel.org>

This is an AI-generated review of your patch. The human sending this
email has considered the AI review valid, or at least plausible.
Full review at: https://sashiko.dev

Simon says: I'd appreciate it if you could look over the first two issues
            flagged below in the context of this patch. While the third
	    and final issue looks like it is in possible separate follow-up
	    territory.

---
net: airoha: Add retry mechanism to airoha_qdma_set_trtcm_param()
    
This patch introduces a retry loop to airoha_qdma_set_trtcm_param() to mitigate
bus timeouts when configuring QDMA parameters. It attempts to verify that the
configuration is correctly applied to the hardware.

> diff --git a/drivers/net/ethernet/airoha/airoha_eth.c b/drivers/net/ethernet/airoha/airoha_eth.c
> index 3370c3df7c107..bb5c0599a4eed 100644
> --- a/drivers/net/ethernet/airoha/airoha_eth.c
> +++ b/drivers/net/ethernet/airoha/airoha_eth.c
> @@ -2673,14 +2673,30 @@ static int airoha_qdma_set_trtcm_param(struct airoha_qdma *qdma, int channel,
>  		     FIELD_PREP(TRTCM_METER_GROUP_MASK, group) |
>  		     FIELD_PREP(TRTCM_PARAM_INDEX_MASK, idx) |
>  		     FIELD_PREP(TRTCM_PARAM_RATE_TYPE_MASK, mode);
> +	int i;
>  
> -	airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> -	airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> +	for (i = 0; i < 100; i++) {
> +		u32 data;
>  
> -	return read_poll_timeout(airoha_qdma_rr, val,
> -				 val & TRTCM_PARAM_RW_DONE_MASK,
> -				 USEC_PER_MSEC, 10 * USEC_PER_MSEC, true,
> -				 qdma, REG_TRTCM_CFG_PARAM(addr));
> +		airoha_qdma_wr(qdma, REG_TRTCM_DATA_LOW(addr), val);
> +		wmb();
> +		airoha_qdma_wr(qdma, REG_TRTCM_CFG_PARAM(addr), config);
> +
> +		if (read_poll_timeout(airoha_qdma_rr, data,
> +				      data & TRTCM_PARAM_RW_DONE_MASK,
> +				      USEC_PER_MSEC, 10 * USEC_PER_MSEC,
> +				      true, qdma, REG_TRTCM_CFG_PARAM(addr)))
> +			return -ETIMEDOUT;

[Severity: High]
If a bus timeout causes the write phase's read_poll_timeout() in
airoha_qdma_set_trtcm_param() to fail, this immediately returns -ETIMEDOUT,
aborting the 100-iteration retry loop.

Doesn't this defeat the mitigation?

> +
> +		if (airoha_qdma_get_trtcm_param(qdma, channel, addr, param,
> +						mode, &data, NULL))
> +			continue;

[Severity: High]
If airoha_qdma_get_trtcm_param() times out during the verification phase, it
indicates the hardware hasn't finished the read command (DONE bit is still 0).

The continue statement then immediately starts the next iteration, writing new
commands to DATA_LOW and CFG_PARAM.

Can writing to command registers while the hardware state machine is actively
busy violate hardware protocols and lead to unrecoverable lockups?

> +
> +		if (data == val)
> +			return 0;

[Severity: High]
This isn't a bug introduced by this patch, but if the bus drops the command
write to REG_TRTCM_CFG_PARAM, the hardware never executes the command and the
TRTCM_PARAM_RW_DONE_MASK bit remains set from the previous operation.

Consequently, read_poll_timeout() returns an immediate false success.

If both the set and get command writes are dropped by the bus, the get
operation will read back the newly written DATA_LOW register value.

Will the data == val check evaluate to true and falsely report success without
the hardware ever processing the configuration?

> +	}
> +
> +	return -EBUSY;
>  }
>  

[ ... ]

>  static int airoha_qdma_set_trtcm_config(struct airoha_qdma *qdma, int channel,
>  					u32 addr, enum trtcm_mode_type mode,
>  					bool enable, u32 enable_mask)
>  {
>  	u32 val;
>  
>  	if (airoha_qdma_get_trtcm_param(qdma, channel, addr, TRTCM_MISC_MODE,
>  					mode, &val, NULL))
>  		return -EINVAL;

[Severity: High]
Standalone calls to airoha_qdma_get_trtcm_param(), like this one in
airoha_qdma_set_trtcm_config(), lack a retry loop entirely.

Does this leave read-modify-write cycles fully vulnerable to the same bus
timeouts the patch attempts to mitigate?

[Severity: High]
This isn't a bug introduced by this patch, but the patch mitigates bus
timeouts in airoha_qdma_set_trtcm_param() while entirely missing
airoha_qdma_set_rl_param().

The latter performs the exact same write-and-poll sequence on identical QDMA
configuration registers:

drivers/net/ethernet/airoha/airoha_eth.c:airoha_qdma_set_rl_param() {
    ...
    return read_poll_timeout(..., val & RATE_LIMIT_PARAM_RW_DONE_MASK, ...);
}

If bus timeouts affect QDMA access broadly as stated in the commit message, are
the rate-limiting configuration paths randomly vulnerable to the same failures?

[Severity: High]
This isn't a bug introduced by this patch, but in airoha_tc_remove_htb_queue(),
hardware teardown incorrectly uses queue + 1 instead of queue:

drivers/net/ethernet/airoha/airoha_eth.c:airoha_tc_remove_htb_queue() {
    ...
    airoha_qdma_set_tx_rate_limit(netdev, queue + 1, 0, 0);
    ...
}

Does this leave the removed queue's hardware limits permanently active
(resource leak) and inadvertently disable the rate limits for queue + 1,
corrupting the QoS state of another active channel?


^ permalink raw reply

* [PATCH] clk: mediatek: mt6735: Unregister PLLs on probe failure
From: Myeonghun Pak @ 2026-06-23  9:41 UTC (permalink / raw)
  To: Yassine Oudjana, Michael Turquette, Stephen Boyd
  Cc: Matthias Brugger, AngeloGioacchino Del Regno, linux-clk,
	linux-mediatek, linux-kernel, linux-arm-kernel, Myeonghun Pak,
	Ijae Kim

mtk_clk_register_plls() registers the apmixedsys PLL clocks manually, while
clk_mt6735_apmixed_remove() unregisters them on driver removal.

If devm_of_clk_add_hw_provider() fails after the PLL registration succeeds,
probe returns the error directly and the remove callback is not run. This
leaves the registered PLL clocks behind on the probe failure path.

Add an unregister_plls error path so provider registration failures unwind the
PLLs before returning the error.

Fixes: 43c04ed79189 ("clk: mediatek: Add drivers for MediaTek MT6735 main clock and reset drivers")
Co-developed-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Ijae Kim <ae878000@gmail.com>
Signed-off-by: Myeonghun Pak <mhun512@gmail.com>

---
 drivers/clk/mediatek/clk-mt6735-apmixedsys.c | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
index 9e30c089a2..04cf9665ec 100644
--- a/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
+++ b/drivers/clk/mediatek/clk-mt6735-apmixedsys.c
@@ -102,10 +102,17 @@ static int clk_mt6735_apmixed_probe(struct platform_device *pdev)
 
 	ret = devm_of_clk_add_hw_provider(&pdev->dev, of_clk_hw_onecell_get,
 					  clk_data);
-	if (ret)
+	if (ret) {
 		dev_err(&pdev->dev,
 			"Failed to register clock provider: %d\n", ret);
+		goto unregister_plls;
+	}
+
+	return 0;
 
+unregister_plls:
+	mtk_clk_unregister_plls(apmixedsys_plls, ARRAY_SIZE(apmixedsys_plls),
+				clk_data);
 	return ret;
 }
 
-- 
2.47.1


^ permalink raw reply related

* [PATCH] usb: mtu3: unmap request DMA on queue failure
From: Haoxiang Li @ 2026-06-23  9:33 UTC (permalink / raw)
  To: chunfeng.yun, gregkh
  Cc: linux-usb, linux-arm-kernel, linux-mediatek, linux-kernel,
	Haoxiang Li, stable

mtu3_gadget_queue() maps the request before checking whether
the QMU GPD ring can accept another transfer. the request is
returned with -EAGAIN before it is linked on the endpoint
request list if mtu3_prepare_transfer() fails.

Normal completion and dequeue paths unmap requests from
mtu3_req_complete(), but this error path never reaches that
helper, so the DMA mapping is left active. Unmap the request
before returning from the failed queue path.

Fixes: df2069acb005 ("usb: Add MediaTek USB3 DRD driver")
Cc: stable@vger.kernel.org
Signed-off-by: Haoxiang Li <haoxiang_li2024@163.com>
---
 drivers/usb/mtu3/mtu3_gadget.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/drivers/usb/mtu3/mtu3_gadget.c b/drivers/usb/mtu3/mtu3_gadget.c
index da29f467943f..f224f2ee379a 100644
--- a/drivers/usb/mtu3/mtu3_gadget.c
+++ b/drivers/usb/mtu3/mtu3_gadget.c
@@ -305,6 +305,7 @@ static int mtu3_gadget_queue(struct usb_ep *ep,
 
 	if (mtu3_prepare_transfer(mep)) {
 		ret = -EAGAIN;
+		usb_gadget_unmap_request(&mtu->g, req, mep->is_in);
 		goto error;
 	}
 
-- 
2.25.1



^ permalink raw reply related

* [PATCH v4 2/4] iio: adc: mt6323-auxadc: add mt6323 PMIC AUXADC driver
From: Roman Vivchar via B4 Relay @ 2026-06-23  8:16 UTC (permalink / raw)
  To: Jonathan Cameron, David Lechner, Nuno Sá, Andy Shevchenko,
	Rob Herring, Krzysztof Kozlowski, Conor Dooley, Matthias Brugger,
	AngeloGioacchino Del Regno, Lee Jones
  Cc: linux-iio, devicetree, linux-kernel, linux-arm-kernel,
	linux-mediatek, Ben Grisdale, Roman Vivchar, Andy Shevchenko
In-Reply-To: <20260623-mt6323-adc-v4-0-299680ad3194@protonmail.com>

From: Roman Vivchar <rva333@protonmail.com>

The mt6323 AUXADC is a 15-bit ADC used for system monitoring. This driver
provides support for reading various channels including battery and
charger voltages, battery and chip temperature, current sensing and
accessory detection.

Add a driver for the AUXADC found in the MediaTek mt6323 PMIC.

Tested-by: Ben Grisdale <bengris32@protonmail.ch> # Amazon Echo Dot (2nd Generation)
Signed-off-by: Roman Vivchar <rva333@protonmail.com>
Reviewed-by: Andy Shevchenko <andriy.shevchenko@intel.com>
---
 MAINTAINERS                     |   1 +
 drivers/iio/adc/Kconfig         |  11 ++
 drivers/iio/adc/Makefile        |   1 +
 drivers/iio/adc/mt6323-auxadc.c | 314 ++++++++++++++++++++++++++++++++++++++++
 4 files changed, 327 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 2551c8cd9e9d..fb40128451dd 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -16260,6 +16260,7 @@ MEDIATEK MT6323 PMIC AUXADC DRIVER
 M:	Roman Vivchar <rva333@protonmail.com>
 L:	linux-iio@vger.kernel.org
 S:	Maintained
+F:	drivers/iio/adc/mt6323-auxadc.c
 F:	include/dt-bindings/iio/adc/mediatek,mt6323-auxadc.h
 
 MEDIATEK MT6735 CLOCK & RESET DRIVERS
diff --git a/drivers/iio/adc/Kconfig b/drivers/iio/adc/Kconfig
index 60038ae8dfc4..a03614b46041 100644
--- a/drivers/iio/adc/Kconfig
+++ b/drivers/iio/adc/Kconfig
@@ -1137,6 +1137,17 @@ config MCP3911
 	  This driver can also be built as a module. If so, the module will be
 	  called mcp3911.
 
+config MEDIATEK_MT6323_AUXADC
+	tristate "MediaTek MT6323 PMIC AUXADC driver"
+	depends on MFD_MT6397
+	help
+	  Say yes here to enable support for MediaTek MT6323 PMIC Auxiliary ADC.
+	  This driver provides multiple channels for system monitoring,
+	  such as battery voltage, PMIC temperature, and others.
+
+	  This driver can also be built as a module. If so, the module will be
+	  called mt6323-auxadc.
+
 config MEDIATEK_MT6359_AUXADC
 	tristate "MediaTek MT6359 PMIC AUXADC driver"
 	depends on MFD_MT6397
diff --git a/drivers/iio/adc/Makefile b/drivers/iio/adc/Makefile
index c76550415ff1..58161750d6e3 100644
--- a/drivers/iio/adc/Makefile
+++ b/drivers/iio/adc/Makefile
@@ -99,6 +99,7 @@ obj-$(CONFIG_MCP320X) += mcp320x.o
 obj-$(CONFIG_MCP3422) += mcp3422.o
 obj-$(CONFIG_MCP3564) += mcp3564.o
 obj-$(CONFIG_MCP3911) += mcp3911.o
+obj-$(CONFIG_MEDIATEK_MT6323_AUXADC) += mt6323-auxadc.o
 obj-$(CONFIG_MEDIATEK_MT6359_AUXADC) += mt6359-auxadc.o
 obj-$(CONFIG_MEDIATEK_MT6360_ADC) += mt6360-adc.o
 obj-$(CONFIG_MEDIATEK_MT6370_ADC) += mt6370-adc.o
diff --git a/drivers/iio/adc/mt6323-auxadc.c b/drivers/iio/adc/mt6323-auxadc.c
new file mode 100644
index 000000000000..c450fb6f09cb
--- /dev/null
+++ b/drivers/iio/adc/mt6323-auxadc.c
@@ -0,0 +1,314 @@
+// SPDX-License-Identifier: GPL-2.0-only
+/*
+ * Copyright (c) 2026 Roman Vivchar <rva333@protonmail.com>
+ *
+ * Based on drivers/iio/adc/mt6359-auxadc.c
+ */
+
+#include <linux/array_size.h>
+#include <linux/bitfield.h>
+#include <linux/bits.h>
+#include <linux/cleanup.h>
+#include <linux/delay.h>
+#include <linux/iio/iio.h>
+#include <linux/mod_devicetable.h>
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/stringify.h>
+#include <linux/time.h>
+#include <linux/types.h>
+
+#include <linux/mfd/mt6323/registers.h>
+
+#include <dt-bindings/iio/adc/mediatek,mt6323-auxadc.h>
+
+#define AUXADC_STRUP_CON10_RSTB_SEL	BIT(7)
+#define AUXADC_STRUP_CON10_RSTB_SW	BIT(5)
+
+#define AUXADC_TOP_CKPDN2_CTL_CK	BIT(5)
+
+#define AUXADC_TRIM_CH2_MASK		GENMASK(11, 10)
+#define AUXADC_TRIM_CH4_MASK		GENMASK(9, 8)
+#define AUXADC_TRIM_CH5_MASK		GENMASK(5, 4)
+#define AUXADC_TRIM_CH6_MASK		GENMASK(3, 2)
+
+#define AUXADC_CON27_VREF18_ENB_MD	BIT(15)
+#define AUXADC_CON27_MD_STATUS		BIT(0)
+
+#define AUXADC_CON19_GPS_STATUS		BIT(1)
+
+#define AUXADC_CON26_VREF18_SELB	BIT(1)
+#define AUXADC_CON26_DECI_GDLY_SEL	BIT(0)
+
+#define AUXADC_CON11_VBUF_EN		BIT(4)
+
+#define AUXADC_CON19_DECI_GDLY_MASK	GENMASK(15, 14)
+#define AUXADC_ADC19_BUSY_MASK		GENMASK(15, 1)
+#define AUXADC_READY_MASK		BIT(15)
+#define AUXADC_DATA_MASK		GENMASK(14, 0)
+
+#define AUXADC_CON9_OSR_MASK		GENMASK(12, 10)
+#define AUXADC_DEFAULT_OSR		3
+
+#define MTK_PMIC_IIO_CHAN(_name, _chan, _addr)                  \
+{                                                               \
+	.type = IIO_VOLTAGE,                                    \
+	.indexed = 1,                                           \
+	.channel = _chan,                                       \
+	.address = _addr,                                       \
+	.datasheet_name = __stringify(_name),                   \
+	.info_mask_separate = BIT(IIO_CHAN_INFO_RAW) |          \
+			      BIT(IIO_CHAN_INFO_SCALE),         \
+}
+
+/*
+ * AUXADC reports everything in mV, including temperature and
+ * current channels. Channel macros are mapped such that their
+ * ID matches their respective hardware bit position in CON22.
+ */
+static const struct iio_chan_spec mt6323_auxadc_channels[] = {
+	MTK_PMIC_IIO_CHAN(baton2,    MT6323_AUXADC_BATON2,    MT6323_AUXADC_ADC6),
+	MTK_PMIC_IIO_CHAN(ch6,       MT6323_AUXADC_CH6,       MT6323_AUXADC_ADC11),
+	MTK_PMIC_IIO_CHAN(bat_temp,  MT6323_AUXADC_BAT_TEMP,  MT6323_AUXADC_ADC5),
+	MTK_PMIC_IIO_CHAN(chip_temp, MT6323_AUXADC_CHIP_TEMP, MT6323_AUXADC_ADC4),
+	MTK_PMIC_IIO_CHAN(vcdt,      MT6323_AUXADC_VCDT,      MT6323_AUXADC_ADC2),
+	MTK_PMIC_IIO_CHAN(baton1,    MT6323_AUXADC_BATON1,    MT6323_AUXADC_ADC3),
+	MTK_PMIC_IIO_CHAN(isense,    MT6323_AUXADC_ISENSE,    MT6323_AUXADC_ADC1),
+	MTK_PMIC_IIO_CHAN(batsns,    MT6323_AUXADC_BATSNS,    MT6323_AUXADC_ADC0),
+	MTK_PMIC_IIO_CHAN(accdet,    MT6323_AUXADC_ACCDET,    MT6323_AUXADC_ADC7),
+};
+
+/*
+ * The MediaTek MT6323 (as well as a lot of other PMICs) has the following hierarchy:
+ * PMIC AUXADC <- PMIC MFD <- SoC PWRAP (wrapper for PWRAP FSM)
+ *
+ * Therefore, PWRAP regmap should be obtained using dev->parent->parent.
+ */
+struct mt6323_auxadc {
+	struct regmap *regmap;
+	/* AUXADC doesn't support reading multiple channels simultaneously. */
+	struct mutex lock;
+};
+
+static int mt6323_auxadc_prepare_channel(struct mt6323_auxadc *auxadc)
+{
+	struct regmap *map = auxadc->regmap;
+	u32 val;
+	int ret;
+
+	ret = regmap_read(map, MT6323_AUXADC_CON19, &val);
+	if (ret)
+		return ret;
+
+	/* The ADC is idle. */
+	if (!(val & AUXADC_CON19_DECI_GDLY_MASK))
+		return 0;
+
+	ret = regmap_read_poll_timeout(map, MT6323_AUXADC_ADC19,
+				       val, !(val & AUXADC_ADC19_BUSY_MASK),
+				       10, 500);
+	if (ret)
+		return ret;
+
+	return regmap_clear_bits(map, MT6323_AUXADC_CON19,
+				 AUXADC_CON19_DECI_GDLY_MASK);
+}
+
+static int mt6323_auxadc_request(struct mt6323_auxadc *auxadc,
+				 unsigned long channel)
+{
+	struct regmap *map = auxadc->regmap;
+	int ret;
+
+	ret = regmap_set_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+	if (ret)
+		return ret;
+
+	return regmap_set_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+}
+
+static int mt6323_auxadc_release(struct mt6323_auxadc *auxadc,
+				 unsigned long channel)
+{
+	struct regmap *map = auxadc->regmap;
+	int ret;
+
+	ret = regmap_clear_bits(map, MT6323_AUXADC_CON22, BIT(channel));
+	if (ret)
+		return ret;
+
+	return regmap_clear_bits(map, MT6323_AUXADC_CON11, AUXADC_CON11_VBUF_EN);
+}
+
+static int mt6323_auxadc_read(struct mt6323_auxadc *auxadc,
+			      const struct iio_chan_spec *chan, int *out)
+{
+	struct regmap *map = auxadc->regmap;
+	u32 val;
+	int ret;
+
+	ret = regmap_read_poll_timeout(map, chan->address,
+				       val, (val & AUXADC_READY_MASK),
+				       1 * USEC_PER_MSEC, 100 * USEC_PER_MSEC);
+	if (ret)
+		return ret;
+
+	*out = FIELD_GET(AUXADC_DATA_MASK, val);
+
+	return 0;
+}
+
+static int mt6323_auxadc_read_raw(struct iio_dev *indio_dev,
+				  const struct iio_chan_spec *chan,
+				  int *val, int *val2, long mask)
+{
+	struct mt6323_auxadc *auxadc = iio_priv(indio_dev);
+	int ret, mult;
+
+	switch (mask) {
+	case IIO_CHAN_INFO_SCALE:
+		if (chan->channel == MT6323_AUXADC_ISENSE ||
+		    chan->channel == MT6323_AUXADC_BATSNS)
+			mult = 4;
+		else
+			mult = 1;
+
+		/* 1800mV full range with 15-bit resolution. */
+		*val = mult * 1800;
+		*val2 = 15;
+
+		return IIO_VAL_FRACTIONAL_LOG2;
+	case IIO_CHAN_INFO_RAW: {
+		guard(mutex)(&auxadc->lock);
+
+		ret = mt6323_auxadc_prepare_channel(auxadc);
+		if (ret)
+			return ret;
+
+		ret = mt6323_auxadc_request(auxadc, chan->channel);
+		if (ret)
+			return ret;
+
+		/* Hardware limitation: the AUXADC needs a delay to become ready. */
+		fsleep(300);
+
+		ret = mt6323_auxadc_read(auxadc, chan, val);
+
+		if (mt6323_auxadc_release(auxadc, chan->channel))
+			dev_err(&indio_dev->dev,
+				"failed to release channel %d\n", chan->channel);
+
+		if (ret)
+			return ret;
+
+		return IIO_VAL_INT;
+	}
+	default:
+		return -EINVAL;
+	}
+}
+
+static int mt6323_auxadc_init(struct mt6323_auxadc *auxadc)
+{
+	struct regmap *map = auxadc->regmap;
+	int ret;
+
+	ret = regmap_set_bits(map, MT6323_STRUP_CON10,
+			      AUXADC_STRUP_CON10_RSTB_SW |
+			      AUXADC_STRUP_CON10_RSTB_SEL);
+	if (ret)
+		return ret;
+
+	ret = regmap_set_bits(map, MT6323_TOP_CKPDN2, AUXADC_TOP_CKPDN2_CTL_CK);
+	if (ret)
+		return ret;
+
+	ret = regmap_update_bits(map, MT6323_AUXADC_CON10,
+				 AUXADC_TRIM_CH2_MASK | AUXADC_TRIM_CH4_MASK |
+				 AUXADC_TRIM_CH5_MASK | AUXADC_TRIM_CH6_MASK,
+				 FIELD_PREP(AUXADC_TRIM_CH2_MASK, 1) |
+				 FIELD_PREP(AUXADC_TRIM_CH4_MASK, 1) |
+				 FIELD_PREP(AUXADC_TRIM_CH5_MASK, 1) |
+				 FIELD_PREP(AUXADC_TRIM_CH6_MASK, 1));
+	if (ret)
+		return ret;
+
+	ret = regmap_set_bits(map, MT6323_AUXADC_CON27,
+			      AUXADC_CON27_VREF18_ENB_MD |
+			      AUXADC_CON27_MD_STATUS);
+	if (ret)
+		return ret;
+
+	ret = regmap_set_bits(map, MT6323_AUXADC_CON19, AUXADC_CON19_GPS_STATUS);
+	if (ret)
+		return ret;
+
+	ret = regmap_set_bits(map, MT6323_AUXADC_CON26,
+			      AUXADC_CON26_VREF18_SELB |
+			      AUXADC_CON26_DECI_GDLY_SEL);
+	if (ret)
+		return ret;
+
+	return regmap_update_bits(map, MT6323_AUXADC_CON9, AUXADC_CON9_OSR_MASK,
+				  FIELD_PREP(AUXADC_CON9_OSR_MASK, AUXADC_DEFAULT_OSR));
+}
+
+static const struct iio_info mt6323_auxadc_iio_info = {
+	.read_raw = mt6323_auxadc_read_raw,
+};
+
+static int mt6323_auxadc_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct mt6323_auxadc *auxadc;
+	struct regmap *regmap;
+	struct iio_dev *iio;
+	int ret;
+
+	regmap = dev_get_regmap(dev->parent->parent, NULL);
+	if (!regmap)
+		return dev_err_probe(dev, -ENODEV, "failed to get regmap\n");
+
+	iio = devm_iio_device_alloc(dev, sizeof(*auxadc));
+	if (!iio)
+		return -ENOMEM;
+
+	auxadc = iio_priv(iio);
+	auxadc->regmap = regmap;
+
+	ret = devm_mutex_init(dev, &auxadc->lock);
+	if (ret)
+		return ret;
+
+	ret = mt6323_auxadc_init(auxadc);
+	if (ret)
+		return dev_err_probe(dev, ret, "failed to initialize auxadc\n");
+
+	iio->name = "mt6323-auxadc";
+	iio->info = &mt6323_auxadc_iio_info;
+	iio->modes = INDIO_DIRECT_MODE;
+	iio->channels = mt6323_auxadc_channels;
+	iio->num_channels = ARRAY_SIZE(mt6323_auxadc_channels);
+
+	return devm_iio_device_register(dev, iio);
+}
+
+static const struct of_device_id mt6323_auxadc_of_match[] = {
+	{ .compatible = "mediatek,mt6323-auxadc" },
+	{ }
+};
+MODULE_DEVICE_TABLE(of, mt6323_auxadc_of_match);
+
+static struct platform_driver mt6323_auxadc_driver = {
+	.driver = {
+		.name = "mt6323-auxadc",
+		.of_match_table = mt6323_auxadc_of_match,
+	},
+	.probe	= mt6323_auxadc_probe,
+};
+module_platform_driver(mt6323_auxadc_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("MediaTek MT6323 PMIC AUXADC Driver");

-- 
2.54.0




^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox