diff options
author | Bitterblue Smith <rtl8821cerfe2@gmail.com> | 2024-07-11 01:11:33 +0300 |
---|---|---|
committer | Ping-Ke Shih <pkshih@realtek.com> | 2024-07-17 12:19:59 +0800 |
commit | 315c23a64e99552502dd4d18d6ddc073fad9a7c3 (patch) | |
tree | 458aead5aa5383deeabb1e152982a1171bb12b5a /drivers/net/wireless/realtek/rtw88/usb.c | |
parent | d64270128bf551d68f47122c8893ea78bfbad8be (diff) |
wifi: rtw88: usb: Support USB 3 with RTL8822CU/RTL8822BU
The Realtek wifi 5 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 rtw88.
When a USB 3 device is plugged into a USB 2 port, rtw88 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.
Some people experience heavy interference in the 2.4 GHz band in
USB 3 mode, so add a module parameter switch_usb_mode with the
default value 1 to let people disable the switching.
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/77906c62-5674-426f-bde1-1b2a12a0339d@gmail.com
Diffstat (limited to 'drivers/net/wireless/realtek/rtw88/usb.c')
-rw-r--r-- | drivers/net/wireless/realtek/rtw88/usb.c | 84 |
1 files changed, 84 insertions, 0 deletions
diff --git a/drivers/net/wireless/realtek/rtw88/usb.c b/drivers/net/wireless/realtek/rtw88/usb.c index a55ca5a24227..251a5726f3ee 100644 --- a/drivers/net/wireless/realtek/rtw88/usb.c +++ b/drivers/net/wireless/realtek/rtw88/usb.c @@ -14,6 +14,11 @@ #include "ps.h" #include "usb.h" +static bool rtw_switch_usb_mode = true; +module_param_named(switch_usb_mode, rtw_switch_usb_mode, bool, 0644); +MODULE_PARM_DESC(switch_usb_mode, + "Set to N to disable switching to USB 3 mode to avoid potential interference in the 2.4 GHz band (default: Y)"); + #define RTW_USB_MAX_RXQ_LEN 512 struct rtw_usb_txcb { @@ -841,6 +846,77 @@ static void rtw_usb_intf_deinit(struct rtw_dev *rtwdev, usb_set_intfdata(intf, NULL); } +static int rtw_usb_switch_mode_new(struct rtw_dev *rtwdev) +{ + enum usb_device_speed cur_speed; + u8 id = rtwdev->chip->id; + bool can_switch; + u32 pad_ctrl2; + + if (rtw_read8(rtwdev, REG_SYS_CFG2 + 3) == 0x20) + cur_speed = USB_SPEED_SUPER; + else + cur_speed = USB_SPEED_HIGH; + + if (cur_speed == USB_SPEED_SUPER) + return 0; + + pad_ctrl2 = rtw_read32(rtwdev, REG_PAD_CTRL2); + + can_switch = !!(pad_ctrl2 & (BIT_MASK_USB23_SW_MODE_V1 | + BIT_USB3_USB2_TRANSITION)); + + if (!can_switch) { + rtw_dbg(rtwdev, RTW_DBG_USB, + "Switching to USB 3 mode unsupported by the chip\n"); + return 0; + } + + /* At this point cur_speed is USB_SPEED_HIGH. If we already tried + * to switch don't try again - it's a USB 2 port. + */ + if (u32_get_bits(pad_ctrl2, BIT_MASK_USB23_SW_MODE_V1) == BIT_USB_MODE_U3) + return 0; + + /* Enable IO wrapper timeout */ + if (id == RTW_CHIP_TYPE_8822B || id == RTW_CHIP_TYPE_8821C) + rtw_write8_clr(rtwdev, REG_SW_MDIO + 3, BIT(0)); + + u32p_replace_bits(&pad_ctrl2, BIT_USB_MODE_U3, BIT_MASK_USB23_SW_MODE_V1); + pad_ctrl2 |= BIT_RSM_EN_V1; + + rtw_write32(rtwdev, REG_PAD_CTRL2, pad_ctrl2); + rtw_write8(rtwdev, REG_PAD_CTRL2 + 1, 4); + + rtw_write16_set(rtwdev, REG_SYS_PW_CTRL, BIT_APFM_OFFMAC); + usleep_range(1000, 1001); + rtw_write32_set(rtwdev, REG_PAD_CTRL2, BIT_NO_PDN_CHIPOFF_V1); + + return 1; +} + +static int rtw_usb_switch_mode(struct rtw_dev *rtwdev) +{ + u8 id = rtwdev->chip->id; + + if (id != RTW_CHIP_TYPE_8822C && id != RTW_CHIP_TYPE_8822B) + return 0; + + if (!rtwdev->efuse.usb_mode_switch) { + rtw_dbg(rtwdev, RTW_DBG_USB, + "Switching to USB 3 mode disabled by chip's efuse\n"); + return 0; + } + + if (!rtw_switch_usb_mode) { + rtw_dbg(rtwdev, RTW_DBG_USB, + "Switching to USB 3 mode disabled by module parameter\n"); + return 0; + } + + return rtw_usb_switch_mode_new(rtwdev); +} + int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) { struct rtw_dev *rtwdev; @@ -896,6 +972,14 @@ int rtw_usb_probe(struct usb_interface *intf, const struct usb_device_id *id) goto err_destroy_rxwq; } + ret = rtw_usb_switch_mode(rtwdev); + if (ret) { + /* Not a fail, but we do need to skip rtw_register_hw. */ + rtw_dbg(rtwdev, RTW_DBG_USB, "switching to USB 3 mode\n"); + ret = 0; + goto err_destroy_rxwq; + } + ret = rtw_register_hw(rtwdev, rtwdev->hw); if (ret) { rtw_err(rtwdev, "failed to register hw\n"); |