All of lore.kernel.org
 help / color / mirror / Atom feed
From: Bitterblue Smith <rtl8821cerfe2@gmail.com>
To: "linux-wireless@vger.kernel.org" <linux-wireless@vger.kernel.org>
Cc: Ping-Ke Shih <pkshih@realtek.com>
Subject: [PATCH rtw-next v3] wifi: rtw89: usb: Support switching to USB 3 mode
Date: Sat, 16 May 2026 15:49:35 +0300	[thread overview]
Message-ID: <fcdde81a-97ec-458f-8e70-ac024938f1d7@gmail.com> (raw)

The Realtek wifi 6/7 devices which support USB 3 are weird: when first
plugged in, they pretend to be USB 2. The driver needs to send some
commands to the device, which make it disappear and come back as a
USB 3 device.

Implement the required commands in rtw89.

Add a new function rtw89_usb_write32_quiet() to avoid the warnings
when writing to R_{AX,BE}_PAD_CTRL2. Even though the write succeeds,
usb_control_msg() returns -EPROTO, probably because the USB device
disappears immediately. This results in some confusing warnings in
the kernel log.

When a USB 3 device is plugged into a USB 2 port, rtw89 will try to
switch it to USB 3 mode only once. The device will disappear and come
back still in USB 2 mode, of course.

Tested with RTL8832AU, RTL8832BU, RTL8832CU, and RTL8912AU.

Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
v3:
 - Avoid crash when trying to switch.
 - Remove the unwanted module parameter.
 - Support RTL8922AU as well.
 - Use more bit names.
 - Hide the warnings when writing R_AX_PAD_CTRL2.
---
 drivers/net/wireless/realtek/rtw89/reg.h |  27 ++++++
 drivers/net/wireless/realtek/rtw89/usb.c | 112 +++++++++++++++++++++--
 2 files changed, 130 insertions(+), 9 deletions(-)

diff --git a/drivers/net/wireless/realtek/rtw89/reg.h b/drivers/net/wireless/realtek/rtw89/reg.h
index 716747c3a10e..c9816ead834d 100644
--- a/drivers/net/wireless/realtek/rtw89/reg.h
+++ b/drivers/net/wireless/realtek/rtw89/reg.h
@@ -164,6 +164,19 @@
 #define R_AX_DBG_PORT_SEL 0x00C0
 #define B_AX_DEBUG_ST_MASK GENMASK(31, 0)
 
+#define R_AX_PAD_CTRL2 0x00C4
+#define B_AX_FORCE_U3_CK BIT(23)
+#define B_AX_USB2_FORCE BIT(22)
+#define B_AX_USB3_FORCE BIT(21)
+#define B_AX_USB3_USB2_TRANSITION BIT(20)
+#define B_AX_USB23_SW_MODE_V1 GENMASK(19, 18)
+#define USB_MODE_U2 0x1
+#define USB_MODE_U3 0x2
+#define B_AX_NO_PDN_CHIPOFF_V1 BIT(17)
+#define B_AX_RSM_EN_V1 BIT(16)
+#define B_AX_MATCH_CNT GENMASK(15, 8)
+#define USB_SWITCH_DELAY 0xF
+
 #define R_AX_PMC_DBG_CTRL2 0x00CC
 #define B_AX_SYSON_DIS_PMCR_AX_WRMSK BIT(2)
 
@@ -4235,6 +4248,20 @@
 #define B_BE_TOGGLE BIT(31)
 #define B_BE_DATA_LINE_MASK GENMASK(30, 0)
 
+#define R_BE_PAD_CTRL2 0x00C4
+#define B_BE_USB23_SW_MODE BIT(31)
+#define B_BE_USB3_GEN_MODE BIT(30)
+#define B_BE_USB3_LANE_MODE BIT(29)
+#define B_BE_USB_AUTO_INSTALL_MASK BIT(28)
+#define B_BE_FORCE_CLK_U2 BIT(25)
+#define B_BE_FORCE_U2_CK BIT(24)
+#define B_BE_FORCE_U3_CK BIT(23)
+#define B_BE_USB2_FORCE BIT(22)
+#define B_BE_USB3_FORCE BIT(21)
+#define B_BE_NO_PDN_CHIPOFF_V1 BIT(17)
+#define B_BE_RSM_EN_V1 BIT(16)
+#define B_BE_MATCH_CNT GENMASK(15, 8)
+
 #define R_BE_PMC_DBG_CTRL2 0x00CC
 #define B_BE_EFUSE_BURN_GNT_MASK GENMASK(31, 24)
 #define B_BE_DIS_IOWRAP_TIMEOUT BIT(16)
diff --git a/drivers/net/wireless/realtek/rtw89/usb.c b/drivers/net/wireless/realtek/rtw89/usb.c
index c6d55e669776..bfe004a49ccc 100644
--- a/drivers/net/wireless/realtek/rtw89/usb.c
+++ b/drivers/net/wireless/realtek/rtw89/usb.c
@@ -12,7 +12,7 @@
 static void rtw89_usb_read_port_complete(struct urb *urb);
 
 static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
-				void *data, u16 len, u8 reqtype)
+				void *data, u16 len, u8 reqtype, bool warn)
 {
 	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
 	struct usb_device *udev = rtwusb->udev;
@@ -52,7 +52,7 @@ static void rtw89_usb_vendorreq(struct rtw89_dev *rtwdev, u32 addr,
 
 		if (ret == -ESHUTDOWN || ret == -ENODEV)
 			set_bit(RTW89_FLAG_UNPLUGGED, rtwdev->flags);
-		else if (ret < 0)
+		else if (ret < 0 && warn)
 			rtw89_warn(rtwdev,
 				   "usb %s%u 0x%x fail ret=%d value=0x%x attempt=%d\n",
 				   str_read_write(reqtype == RTW89_USB_VENQT_READ),
@@ -80,7 +80,7 @@ static u32 rtw89_usb_read_cmac(struct rtw89_dev *rtwdev, u32 addr)
 
 	for (count = 0; ; count++) {
 		rtw89_usb_vendorreq(rtwdev, addr32, &data, 4,
-				    RTW89_USB_VENQT_READ);
+				    RTW89_USB_VENQT_READ, true);
 
 		val32 = le32_to_cpu(data);
 		if (val32 != RTW89_R32_DEAD)
@@ -106,7 +106,8 @@ static u8 rtw89_usb_ops_read8(struct rtw89_dev *rtwdev, u32 addr)
 	if (ACCESS_CMAC(addr))
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_READ);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 1,
+			    RTW89_USB_VENQT_READ, true);
 
 	return data;
 }
@@ -118,7 +119,8 @@ static u16 rtw89_usb_ops_read16(struct rtw89_dev *rtwdev, u32 addr)
 	if (ACCESS_CMAC(addr))
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_READ);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 2,
+			    RTW89_USB_VENQT_READ, true);
 
 	return le16_to_cpu(data);
 }
@@ -131,7 +133,7 @@ static u32 rtw89_usb_ops_read32(struct rtw89_dev *rtwdev, u32 addr)
 		return rtw89_usb_read_cmac(rtwdev, addr);
 
 	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
-			    RTW89_USB_VENQT_READ);
+			    RTW89_USB_VENQT_READ, true);
 
 	return le32_to_cpu(data);
 }
@@ -140,21 +142,32 @@ static void rtw89_usb_ops_write8(struct rtw89_dev *rtwdev, u32 addr, u8 val)
 {
 	u8 data = val;
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 1, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 1,
+			    RTW89_USB_VENQT_WRITE, true);
 }
 
 static void rtw89_usb_ops_write16(struct rtw89_dev *rtwdev, u32 addr, u16 val)
 {
 	__le16 data = cpu_to_le16(val);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 2, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 2,
+			    RTW89_USB_VENQT_WRITE, true);
 }
 
 static void rtw89_usb_ops_write32(struct rtw89_dev *rtwdev, u32 addr, u32 val)
 {
 	__le32 data = cpu_to_le32(val);
 
-	rtw89_usb_vendorreq(rtwdev, addr, &data, 4, RTW89_USB_VENQT_WRITE);
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
+			    RTW89_USB_VENQT_WRITE, true);
+}
+
+static void rtw89_usb_write32_quiet(struct rtw89_dev *rtwdev, u32 addr, u32 val)
+{
+	__le32 data = cpu_to_le32(val);
+
+	rtw89_usb_vendorreq(rtwdev, addr, &data, 4,
+			    RTW89_USB_VENQT_WRITE, false);
 }
 
 static u32
@@ -1059,6 +1072,80 @@ static void rtw89_usb_intf_deinit(struct rtw89_dev *rtwdev,
 	usb_set_intfdata(intf, NULL);
 }
 
+static int rtw89_usb_switch_mode_ax(struct rtw89_dev *rtwdev)
+{
+	u32 pad_ctrl2;
+
+	/* No known USB 3 devices with this chip. */
+	if (rtwdev->chip->chip_id == RTL8851B)
+		return 0;
+
+	pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_AX_PAD_CTRL2);
+
+	rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n",
+		    __func__, pad_ctrl2);
+
+	/* Already tried to switch but it's a USB 2 port. */
+	if (u32_get_bits(pad_ctrl2, B_AX_MATCH_CNT) == USB_SWITCH_DELAY)
+		return 0;
+
+	/* Add delay to prevent some platforms would not detect USB switch */
+	u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_AX_MATCH_CNT);
+
+	pad_ctrl2 &= ~(B_AX_FORCE_U3_CK | B_AX_USB2_FORCE |
+		       B_AX_USB3_FORCE | B_AX_USB3_USB2_TRANSITION);
+
+	u32p_replace_bits(&pad_ctrl2, USB_MODE_U3, B_AX_USB23_SW_MODE_V1);
+
+	pad_ctrl2 |= B_AX_NO_PDN_CHIPOFF_V1 | B_AX_RSM_EN_V1;
+
+	rtw89_usb_write32_quiet(rtwdev, R_AX_PAD_CTRL2, pad_ctrl2);
+
+	return 1;
+}
+
+static int rtw89_usb_switch_mode_be(struct rtw89_dev *rtwdev)
+{
+	u32 pad_ctrl2;
+
+	pad_ctrl2 = rtw89_usb_ops_read32(rtwdev, R_BE_PAD_CTRL2);
+
+	rtw89_debug(rtwdev, RTW89_DBG_HCI, "%s: pad_ctrl2: %#x\n",
+		    __func__, pad_ctrl2);
+
+	/* Already tried to switch but it's a USB 2 port. */
+	if (u32_get_bits(pad_ctrl2, B_BE_MATCH_CNT) == USB_SWITCH_DELAY)
+		return 0;
+
+	/* Add delay to prevent some platforms would not detect USB switch */
+	u32p_replace_bits(&pad_ctrl2, USB_SWITCH_DELAY, B_BE_MATCH_CNT);
+
+	pad_ctrl2 |= B_BE_RSM_EN_V1 | B_BE_NO_PDN_CHIPOFF_V1 |
+		     B_BE_USB_AUTO_INSTALL_MASK | B_BE_USB23_SW_MODE;
+
+	pad_ctrl2 &= ~(B_BE_USB3_FORCE | B_BE_USB2_FORCE |
+		       B_BE_FORCE_U3_CK | B_BE_FORCE_U2_CK |
+		       B_BE_FORCE_CLK_U2 | B_BE_USB3_GEN_MODE |
+		       B_BE_USB3_LANE_MODE);
+
+	rtw89_usb_write32_quiet(rtwdev, R_BE_PAD_CTRL2, pad_ctrl2);
+
+	return 1;
+}
+
+static int rtw89_usb_switch_mode(struct rtw89_dev *rtwdev)
+{
+	struct rtw89_usb *rtwusb = rtw89_usb_priv(rtwdev);
+
+	if (rtwusb->udev->speed == USB_SPEED_SUPER)
+		return 0;
+
+	if (rtwdev->chip->chip_gen == RTW89_CHIP_AX)
+		return rtw89_usb_switch_mode_ax(rtwdev);
+
+	return rtw89_usb_switch_mode_be(rtwdev);
+}
+
 int rtw89_usb_probe(struct usb_interface *intf,
 		    const struct usb_device_id *id)
 {
@@ -1091,6 +1178,13 @@ int rtw89_usb_probe(struct usb_interface *intf,
 		goto err_free_hw;
 	}
 
+	ret = rtw89_usb_switch_mode(rtwdev);
+	if (ret) {
+		/* Not a fail, but we do need to skip rtw89_core_register. */
+		ret = 0;
+		goto err_intf_deinit;
+	}
+
 	if (rtwusb->udev->speed == USB_SPEED_SUPER)
 		rtwdev->hci.dle_type = RTW89_HCI_DLE_TYPE_USB3;
 	else
-- 
2.53.0


                 reply	other threads:[~2026-05-16 12:49 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=fcdde81a-97ec-458f-8e70-ac024938f1d7@gmail.com \
    --to=rtl8821cerfe2@gmail.com \
    --cc=linux-wireless@vger.kernel.org \
    --cc=pkshih@realtek.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.