]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
wifi: rtw89: mac: reset power state before switching to power on
authorPing-Ke Shih <pkshih@realtek.com>
Tue, 23 Dec 2025 03:06:43 +0000 (11:06 +0800)
committerPing-Ke Shih <pkshih@realtek.com>
Fri, 26 Dec 2025 02:53:18 +0000 (10:53 +0800)
To run power on function properly, reset power states (off/on/PS) to
initial state. Otherwise, it might be unusable due to fail to power on.

Since a USB adapter might play as USB mass storage with driver and then
switch to WiFi adapter, causing it stays on power-on state when doing WiFi
USB probe. Exclude this case.

Signed-off-by: Ping-Ke Shih <pkshih@realtek.com>
Link: https://patch.msgid.link/20251223030651.480633-5-pkshih@realtek.com
drivers/net/wireless/realtek/rtw89/mac.c
drivers/net/wireless/realtek/rtw89/mac.h
drivers/net/wireless/realtek/rtw89/mac_be.c
drivers/net/wireless/realtek/rtw89/reg.h

index d78fbe73e365775326c9c4a8ad99bf3394950a19..0cea01e322c6db3dd0a95009d5b1f7d195c77652 100644 (file)
@@ -1478,13 +1478,11 @@ static void rtw89_mac_power_switch_boot_mode(struct rtw89_dev *rtwdev)
 
 static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)
 {
-#define PWR_ACT 1
        const struct rtw89_mac_gen_def *mac = rtwdev->chip->mac_def;
        const struct rtw89_chip_info *chip = rtwdev->chip;
        const struct rtw89_pwr_cfg * const *cfg_seq;
        int (*cfg_func)(struct rtw89_dev *rtwdev);
        int ret;
-       u8 val;
 
        rtw89_mac_power_switch_boot_mode(rtwdev);
 
@@ -1499,10 +1497,10 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)
        if (test_bit(RTW89_FLAG_FW_RDY, rtwdev->flags))
                __rtw89_leave_ps_mode(rtwdev);
 
-       val = rtw89_read32_mask(rtwdev, R_AX_IC_PWR_STATE, B_AX_WLMAC_PWR_STE_MASK);
-       if (on && val == PWR_ACT) {
-               rtw89_err(rtwdev, "MAC has already powered on\n");
-               return -EBUSY;
+       if (on) {
+               ret = mac->reset_pwr_state(rtwdev);
+               if (ret)
+                       return ret;
        }
 
        ret = cfg_func ? cfg_func(rtwdev) : rtw89_mac_pwr_seq(rtwdev, cfg_seq);
@@ -1529,7 +1527,6 @@ static int rtw89_mac_power_switch(struct rtw89_dev *rtwdev, bool on)
        }
 
        return 0;
-#undef PWR_ACT
 }
 
 int rtw89_mac_pwr_on(struct rtw89_dev *rtwdev)
@@ -3931,6 +3928,29 @@ static int rtw89_mac_feat_init(struct rtw89_dev *rtwdev)
        return 0;
 }
 
+static int rtw89_mac_reset_pwr_state_ax(struct rtw89_dev *rtwdev)
+{
+       u8 val;
+
+       val = rtw89_read32_mask(rtwdev, R_AX_IC_PWR_STATE, B_AX_WLMAC_PWR_STE_MASK);
+       if (val == MAC_AX_MAC_ON) {
+               /*
+                * A USB adapter might play as USB mass storage with driver and
+                * then switch to WiFi adapter, causing it stays on power-on
+                * state when doing WiFi USB probe. Return EAGAIN to caller to
+                * power-off and power-on again to reset the state.
+                */
+               if (rtwdev->hci.type == RTW89_HCI_TYPE_USB &&
+                   !test_bit(RTW89_FLAG_PROBE_DONE, rtwdev->flags))
+                       return -EAGAIN;
+
+               rtw89_err(rtwdev, "MAC has already powered on\n");
+               return -EBUSY;
+       }
+
+       return 0;
+}
+
 static void rtw89_disable_fw_watchdog(struct rtw89_dev *rtwdev)
 {
        u32 val32;
@@ -7206,6 +7226,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_ax = {
        .set_cpuio = set_cpuio_ax,
        .dle_quota_change = dle_quota_change_ax,
 
+       .reset_pwr_state = rtw89_mac_reset_pwr_state_ax,
        .disable_cpu = rtw89_mac_disable_cpu_ax,
        .fwdl_enable_wcpu = rtw89_mac_enable_cpu_ax,
        .fwdl_get_status = rtw89_fw_get_rdy_ax,
index 0007229d675378a483cf5875d9e14fee327ab20c..9ec70729e9e1c251e886d7c70b12b46a3fbd628d 100644 (file)
@@ -1052,6 +1052,7 @@ struct rtw89_mac_gen_def {
                         struct rtw89_cpuio_ctrl *ctrl_para, bool wd);
        int (*dle_quota_change)(struct rtw89_dev *rtwdev, bool band1_en);
 
+       int (*reset_pwr_state)(struct rtw89_dev *rtwdev);
        void (*disable_cpu)(struct rtw89_dev *rtwdev);
        int (*fwdl_enable_wcpu)(struct rtw89_dev *rtwdev, u8 boot_reason,
                                bool dlfw, bool include_bb);
index 556e5f98e8d4195bfe207001289cf4ed8f510b5d..65c0c0de3a30ce6a434c59176c34817d86cdbe21 100644 (file)
@@ -425,6 +425,135 @@ int rtw89_mac_read_xtal_si_be(struct rtw89_dev *rtwdev, u8 offset, u8 *val)
        return 0;
 }
 
+static int rtw89_mac_reset_pwr_state_be(struct rtw89_dev *rtwdev)
+{
+       u32 val32;
+       int ret;
+
+       val32 = rtw89_read32(rtwdev, R_BE_SYSON_FSM_MON);
+       val32 &= WLAN_FSM_MASK;
+       val32 |= WLAN_FSM_SET;
+       rtw89_write32(rtwdev, R_BE_SYSON_FSM_MON, val32);
+
+       ret = read_poll_timeout(rtw89_read32_mask, val32, val32 == WLAN_FSM_IDLE,
+                               1000, 2000000, false,
+                               rtwdev, R_BE_SYSON_FSM_MON, WLAN_FSM_STATE_MASK);
+       if (ret) {
+               rtw89_err(rtwdev, "[ERR]Polling WLAN PMC timeout= %X\n", val32);
+               return ret;
+       }
+
+       val32 = rtw89_read32_mask(rtwdev, R_BE_IC_PWR_STATE, B_BE_WLMAC_PWR_STE_MASK);
+       if (val32 == MAC_AX_MAC_OFF) {
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL,
+                                       B_BE_HAXIDMA_IO_ST | B_BE_HAXIDMA_BACKUP_RESTORE_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling HAXI IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling WLAN IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON);
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS);
+       } else if (val32 == MAC_AX_MAC_ON) {
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL,
+                                       B_BE_HAXIDMA_IO_ST | B_BE_HAXIDMA_BACKUP_RESTORE_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling HAXI IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling WLAN IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON);
+               rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_OFFMAC);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, val32 == MAC_AX_MAC_OFF,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_OFFMAC);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling MAC state timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON);
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS);
+       } else if (val32 == MAC_AX_MAC_LPS) {
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HAXIDMA_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL,
+                                       B_BE_HAXIDMA_IO_ST | B_BE_HAXIDMA_BACKUP_RESTORE_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling HAXI IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_EN);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, !val32,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_HCI_OPT_CTRL, B_BE_HCI_WLAN_IO_ST);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling WLAN IO timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_set(rtwdev, R_BE_WLLPS_CTRL, B_BE_FORCE_LEAVE_LPS);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, val32 == MAC_AX_MAC_ON,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_IC_PWR_STATE, B_BE_WLMAC_PWR_STE_MASK);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling MAC STS timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON);
+               rtw89_write32_set(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_OFFMAC);
+
+               ret = read_poll_timeout(rtw89_read32_mask, val32, val32 == MAC_AX_MAC_OFF,
+                                       1000, 2000000, false,
+                                       rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_OFFMAC);
+               if (ret) {
+                       rtw89_err(rtwdev, "[ERR]Polling MAC state timeout= %X\n", val32);
+                       return ret;
+               }
+
+               rtw89_write32_clr(rtwdev, R_BE_WLLPS_CTRL, B_BE_FORCE_LEAVE_LPS);
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_EN_WLON);
+               rtw89_write32_clr(rtwdev, R_BE_SYS_PW_CTRL, B_BE_APFM_SWLPS);
+       }
+
+       return 0;
+}
+
 static void rtw89_mac_disable_cpu_be(struct rtw89_dev *rtwdev)
 {
        u32 val32;
@@ -2623,6 +2752,7 @@ const struct rtw89_mac_gen_def rtw89_mac_gen_be = {
        .set_cpuio = set_cpuio_be,
        .dle_quota_change = dle_quota_change_be,
 
+       .reset_pwr_state = rtw89_mac_reset_pwr_state_be,
        .disable_cpu = rtw89_mac_disable_cpu_be,
        .fwdl_enable_wcpu = rtw89_mac_fwdl_enable_wcpu_be,
        .fwdl_get_status = fwdl_get_status_be,
index 5b4a459cf29c17fce2f3ed737aa3ca405f45a983..fb641cefa4c98c19d64d346ea49b1f76f610e126 100644 (file)
 #define R_AX_WLLPS_CTRL 0x0090
 #define B_AX_LPSOP_ASWRM BIT(17)
 #define B_AX_LPSOP_DSWRM BIT(9)
+#define B_AX_FORCE_LEAVE_LPS BIT(3)
 #define B_AX_DIS_WLBT_LPSEN_LOPC BIT(1)
 #define SW_LPS_OPTION 0x0001A0B2
 
 #define R_AX_IC_PWR_STATE 0x03F0
 #define B_AX_WHOLE_SYS_PWR_STE_MASK GENMASK(25, 16)
 #define B_AX_WLMAC_PWR_STE_MASK GENMASK(9, 8)
+#define MAC_AX_MAC_OFF 0
+#define MAC_AX_MAC_ON 1
+#define MAC_AX_MAC_LPS 2
 #define B_AX_UART_HCISYS_PWR_STE_MASK GENMASK(7, 6)
 #define B_AX_SDIO_HCISYS_PWR_STE_MASK GENMASK(5, 4)
 #define B_AX_USB_HCISYS_PWR_STE_MASK GENMASK(3, 2)
 #define B_AX_B1_ISR_ERR_USRCTL_REINIT BIT(0)
 
 #define R_AX_AFE_CTRL1 0x0024
+#define B_AX_CMAC_CLK_SEL BIT(21)
 
 #define B_AX_R_SYM_WLCMAC1_P4_PC_EN BIT(4)
 #define B_AX_R_SYM_WLCMAC1_P3_PC_EN BIT(3)
 #define B_AX_R_SYM_FEN_WLBBFUN_1 BIT(16)
 #define B_AX_R_SYM_ISO_CMAC12PP BIT(5)
 
+#define R_AX_SYSON_FSM_MON 0x00A0
+#define B_AX_FSM_MON_SEL_MASK GENMASK(26, 24)
+#define B_AX_DOP_ELDO BIT(23)
+#define B_AX_FSM_MON_UPD BIT(15)
+#define B_AX_FSM_PAR_MASK GENMASK(14, 0)
+
 #define R_AX_CMAC_REG_START 0xC000
 
 #define R_AX_CMAC_FUNC_EN 0xC000
 #define B_BE_LPSROP_LOWPWRPLL BIT(7)
 #define B_BE_LPSROP_DSWRSD_SEL_MASK GENMASK(5, 4)
 
+#define R_BE_SYSON_FSM_MON 0x00A0
+#define B_BE_FSM_MON_SEL_MASK GENMASK(26, 24)
+#define B_BE_DOP_ELDO BIT(23)
+#define B_BE_AFE_PLL_BYPASS BIT(22)
+#define B_BE_PON_SWR_BYPASS BIT(21)
+#define B_BE_PON_ADIE_BYPASS BIT(20)
+#define B_BE_AFE_LS_BYPASS BIT(19)
+#define B_BE_BTPMC_XTAL_SI_BYPASS BIT(17)
+#define B_BE_WLPMC_XTAL_SI_BYPASS BIT(16)
+#define B_BE_FSM_MON_UPD BIT(15)
+#define B_BE_FSM_PAR_MASK GENMASK(14, 0)
+#define WLAN_FSM_MASK 0xFFFFFF
+#define WLAN_FSM_SET 0x4000000
+#define WLAN_FSM_STATE_MASK 0x1FF
+#define WLAN_FSM_IDLE 0
+
 #define R_BE_EFUSE_CTRL_2_V1 0x00A4
 #define B_BE_EF_ENT BIT(31)
 #define B_BE_EF_TCOLUMN_EN BIT(29)