OSDN Git Service

rtw88: add C2H response for checking firmware leave lps
authorChin-Yen Lee <timlee@realtek.com>
Fri, 30 Oct 2020 08:48:25 +0000 (16:48 +0800)
committerKalle Valo <kvalo@codeaurora.org>
Sat, 7 Nov 2020 15:51:04 +0000 (17:51 +0200)
Originally driver checks if firmware has left lps via reading the setting
of REG_TCR register. But this way may fail when firmware is frequently
changing power state. Therefore, firmware provides a safer option for
driver. When firmware leaves lps successfully, it sends a C2H response
to inform driver.

Signed-off-by: Chin-Yen Lee <timlee@realtek.com>
Signed-off-by: Tzu-En Huang <tehuang@realtek.com>
Signed-off-by: Kalle Valo <kvalo@codeaurora.org>
Link: https://lore.kernel.org/r/20201030084826.9034-4-tehuang@realtek.com
drivers/net/wireless/realtek/rtw88/fw.c
drivers/net/wireless/realtek/rtw88/fw.h
drivers/net/wireless/realtek/rtw88/main.c
drivers/net/wireless/realtek/rtw88/main.h
drivers/net/wireless/realtek/rtw88/ps.c
drivers/net/wireless/realtek/rtw88/ps.h

index 042015b..29fffac 100644 (file)
@@ -183,6 +183,9 @@ void rtw_fw_c2h_cmd_rx_irqsafe(struct rtw_dev *rtwdev, u32 pkt_offset,
        case C2H_BT_MP_INFO:
                rtw_coex_info_response(rtwdev, skb);
                break;
+       case C2H_WLAN_RFON:
+               complete(&rtwdev->lps_leave_check);
+               break;
        default:
                /* pass offset for further operation */
                *((u32 *)skb->cb) = pkt_offset;
index 5b74830..39c905c 100644 (file)
@@ -31,6 +31,7 @@ enum rtw_c2h_cmd_id {
        C2H_RA_RPT = 0x0c,
        C2H_HW_FEATURE_REPORT = 0x19,
        C2H_WLAN_INFO = 0x27,
+       C2H_WLAN_RFON = 0x32,
        C2H_HW_FEATURE_DUMP = 0xfd,
        C2H_HALMAC = 0xff,
 };
index 259ad84..5c46f5d 100644 (file)
@@ -1652,6 +1652,7 @@ int rtw_core_init(struct rtw_dev *rtwdev)
        mutex_init(&rtwdev->hal.tx_power_mutex);
 
        init_waitqueue_head(&rtwdev->coex.wait);
+       init_completion(&rtwdev->lps_leave_check);
 
        rtwdev->sec.total_cam_num = 32;
        rtwdev->hal.current_channel = 1;
index 82d4645..b635e18 100644 (file)
@@ -1741,6 +1741,7 @@ struct rtw_dev {
        /* lps power state & handler work */
        struct rtw_lps_conf lps_conf;
        bool ps_enabled;
+       struct completion lps_leave_check;
 
        struct dentry *debugfs;
 
index 8ed2436..233b3df 100644 (file)
@@ -109,7 +109,7 @@ static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
        rtw_hci_deep_ps(rtwdev, false);
 }
 
-static void rtw_fw_leave_lps_state_check(struct rtw_dev *rtwdev)
+static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)
 {
        int i;
 
@@ -127,12 +127,53 @@ static void rtw_fw_leave_lps_state_check(struct rtw_dev *rtwdev)
         */
        for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {
                if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)
-                       return;
+                       return 0;
                msleep(20);
        }
 
-       rtw_write32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN, 0);
-       rtw_warn(rtwdev, "firmware failed to restore hardware setting\n");
+       return -EBUSY;
+}
+
+static  int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)
+{
+       if (wait_for_completion_timeout(&rtwdev->lps_leave_check,
+                                       LEAVE_LPS_TIMEOUT))
+               return 0;
+       return -EBUSY;
+}
+
+static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)
+{
+       bool ret = false;
+       struct rtw_fw_state *fw;
+
+       if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
+               fw = &rtwdev->wow_fw;
+       else
+               fw = &rtwdev->fw;
+
+       if (fw->feature & FW_FEATURE_LPS_C2H)
+               ret = __rtw_fw_leave_lps_check_c2h(rtwdev);
+       else
+               ret = __rtw_fw_leave_lps_check_reg(rtwdev);
+
+       if (ret) {
+               rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);
+               rtw_warn(rtwdev, "firmware failed to leave lps state\n");
+       }
+}
+
+static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)
+{
+       struct rtw_fw_state *fw;
+
+       if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
+               fw = &rtwdev->wow_fw;
+       else
+               fw = &rtwdev->fw;
+
+       if (fw->feature & FW_FEATURE_LPS_C2H)
+               reinit_completion(&rtwdev->lps_leave_check);
 }
 
 static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
@@ -145,8 +186,9 @@ static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
        conf->smart_ps = 0;
 
        rtw_hci_link_ps(rtwdev, false);
+       rtw_fw_leave_lps_check_prepare(rtwdev);
        rtw_fw_set_pwr_mode(rtwdev);
-       rtw_fw_leave_lps_state_check(rtwdev);
+       rtw_fw_leave_lps_check(rtwdev);
 
        clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
 
index 19afcec..915b200 100644 (file)
@@ -12,6 +12,7 @@
 #define POWER_MODE_LCLK                BIT(0)
 
 #define LEAVE_LPS_TRY_CNT      5
+#define LEAVE_LPS_TIMEOUT      msecs_to_jiffies(100)
 
 int rtw_enter_ips(struct rtw_dev *rtwdev);
 int rtw_leave_ips(struct rtw_dev *rtwdev);