]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw88: Add USB PHY configuration
authorBitterblue Smith <rtl8821cerfe2@gmail.com>
Wed, 1 Jan 2025 16:16:32 +0000 (18:16 +0200)
committerPing-Ke Shih <pkshih@realtek.com>
Sun, 12 Jan 2025 01:51:19 +0000 (09:51 +0800)
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>
Acked-by: Ping-Ke Shih <pkshih@realtek.com>
Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/9d312b14-0146-4be8-9c50-ef432234db50@gmail.com
drivers/net/wireless/realtek/rtw88/reg.h
drivers/net/wireless/realtek/rtw88/usb.c

index e4d506cf9c331e4a815a83d5dd97500347415cbf..95a39ae74cd3e08559516107a4d4c984ef4e7706 100644 (file)
 
 #define REG_USB_MOD    0xf008
 #define REG_USB3_RXITV 0xf050
+#define REG_USB2_PHY_ADR       0xfe40
+#define REG_USB2_PHY_DAT       0xfe41
+#define REG_USB2_PHY_CMD       0xfe42
+#define BIT_USB2_PHY_CMD_TRG   0x81
 #define REG_USB_HRPWM  0xfe58
+#define REG_USB3_PHY_ADR       0xff0c
+#define REG_USB3_PHY_DAT_L     0xff0d
+#define REG_USB3_PHY_DAT_H     0xff0e
+#define BIT_USB3_PHY_ADR_WR    BIT(7)
+#define BIT_USB3_PHY_ADR_RD    BIT(6)
+#define BIT_USB3_PHY_ADR_MASK  GENMASK(5, 0)
 
 #define RF_MODE                0x00
 #define RF_MODOPT      0x01
index 1572b61cf87738be96d62f7d3f1a969ecff1de27..c4908db4ff0e8aae1dbe9083fca6ecdef2d1e95c 100644 (file)
@@ -1128,6 +1128,71 @@ 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, u8 addr, u16 data,
+                             enum usb_device_speed speed)
+{
+       if (speed == USB_SPEED_SUPER) {
+               rtw_write8(rtwdev, REG_USB3_PHY_DAT_L, data & 0xff);
+               rtw_write8(rtwdev, REG_USB3_PHY_DAT_H, data >> 8);
+               rtw_write8(rtwdev, REG_USB3_PHY_ADR, addr | BIT_USB3_PHY_ADR_WR);
+       } else if (speed == USB_SPEED_HIGH) {
+               rtw_write8(rtwdev, REG_USB2_PHY_DAT, data);
+               rtw_write8(rtwdev, REG_USB2_PHY_ADR, addr);
+               rtw_write8(rtwdev, REG_USB2_PHY_CMD, BIT_USB2_PHY_CMD_TRG);
+       }
+}
+
+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 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;
+
+       for ( ; para->offset != 0xffff; para++) {
+               if (!(para->cut_mask & BIT(rtwdev->hal.cut_version)))
+                       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;
@@ -1183,6 +1248,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. */