diff mbox series

wifi: rtw88: Add USB PHY configuration

Message ID a3b87566-4e11-4fc2-8c51-db592e56af13@gmail.com (mailing list archive)
State New
Delegated to: Ping-Ke Shih
Headers show
Series wifi: rtw88: Add USB PHY configuration | expand

Commit Message

Bitterblue Smith Dec. 19, 2024, 4:20 p.m. UTC
Add some extra configuration for USB devices. Currently only RTL8822BU
version (cut) D needs this. The new code makes use of the existing
usb3_param_8822b array from rtw8822b.c.

A user reported that TP-Link Archer T3U in USB 3 mode was randomly
disconnecting from USB:

[ 26.036502] usb 2-2: new SuperSpeed USB device number 3 using xhci_hcd
...
[ 27.576491] usb 2-2: USB disconnect, device number 3
[ 28.621528] usb 2-2: new SuperSpeed USB device number 4 using xhci_hcd
...
[ 45.984521] usb 2-2: USB disconnect, device number 4
...
[ 46.845585] usb 2-2: new SuperSpeed USB device number 5 using xhci_hcd
...
[ 94.400380] usb 2-2: USB disconnect, device number 5
...
[ 95.590421] usb 2-2: new SuperSpeed USB device number 6 using xhci_hcd

This patch fixes that.

Link: https://github.com/lwfinger/rtw88/issues/262.
Signed-off-by: Bitterblue Smith <rtl8821cerfe2@gmail.com>
---
 drivers/net/wireless/realtek/rtw88/usb.c | 70 ++++++++++++++++++++++++
 1 file changed, 70 insertions(+)
diff mbox series

Patch

diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c
index b405f8317021..b5ef7e31df19 100644
--- a/drivers/net/wireless/realtek/rtw88/usb.c
+++ b/drivers/net/wireless/realtek/rtw88/usb.c
@@ -1127,6 +1127,73 @@  static int rtw_usb_switch_mode(struct rtw_dev *rtwdev)
 		return rtw_usb_switch_mode_new(rtwdev);
 }
 
+#define USB_REG_PAGE	0xf4
+#define USB_PHY_PAGE0	0x9b
+#define USB_PHY_PAGE1	0xbb
+
+static void rtw_usb_phy_write(struct rtw_dev *rtwdev, u16 addr, u16 data,
+			      enum usb_device_speed speed)
+{
+	if (speed == USB_SPEED_SUPER) {
+		rtw_write8(rtwdev, 0xff0d, (u8)data);
+		rtw_write8(rtwdev, 0xff0e, (u8)(data >> 8));
+		rtw_write8(rtwdev, 0xff0c, addr | BIT(7));
+	} else if (speed == USB_SPEED_HIGH) {
+		rtw_write8(rtwdev, 0xfe41, (u8)data);
+		rtw_write8(rtwdev, 0xfe40, addr);
+		rtw_write8(rtwdev, 0xfe42, 0x81);
+	}
+}
+
+static void rtw_usb_page_switch(struct rtw_dev *rtwdev,
+				enum usb_device_speed speed, u8 page)
+{
+	if (speed == USB_SPEED_SUPER)
+		return;
+
+	rtw_usb_phy_write(rtwdev, USB_REG_PAGE, page, speed);
+}
+
+static void rtw_usb_phy_cfg(struct rtw_dev *rtwdev,
+			    enum usb_device_speed speed)
+{
+	const struct rtw_intf_phy_para *para = NULL;
+	u16 cut, offset;
+
+	if (!rtwdev->chip->intf_table)
+		return;
+
+	if (speed == USB_SPEED_SUPER)
+		para = rtwdev->chip->intf_table->usb3_para;
+	else if (speed == USB_SPEED_HIGH)
+		para = rtwdev->chip->intf_table->usb2_para;
+
+	if (!para)
+		return;
+
+	cut = BIT(0) << rtwdev->hal.cut_version;
+
+	for ( ; para->offset != 0xffff; para++) {
+		if (!(para->cut_mask & cut))
+			continue;
+
+		offset = para->offset;
+
+		if (para->ip_sel == RTW_IP_SEL_MAC) {
+			rtw_write8(rtwdev, offset, para->value);
+		} else {
+			if (offset > 0x100)
+				rtw_usb_page_switch(rtwdev, speed, USB_PHY_PAGE1);
+			else
+				rtw_usb_page_switch(rtwdev, speed, USB_PHY_PAGE0);
+
+			offset &= 0xff;
+
+			rtw_usb_phy_write(rtwdev, offset, para->value, speed);
+		}
+	}
+}
+
 int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 {
 	struct rtw_dev *rtwdev;
@@ -1182,6 +1249,9 @@  int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id)
 		goto err_destroy_rxwq;
 	}
 
+	rtw_usb_phy_cfg(rtwdev, USB_SPEED_HIGH);
+	rtw_usb_phy_cfg(rtwdev, USB_SPEED_SUPER);
+
 	ret = rtw_usb_switch_mode(rtwdev);
 	if (ret) {
 		/* Not a fail, but we do need to skip rtw_register_hw. */