344 lines · c
1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause2/* Copyright(c) 2018-2019 Realtek Corporation3 */4 5#include "main.h"6#include "reg.h"7#include "fw.h"8#include "ps.h"9#include "mac.h"10#include "coex.h"11#include "debug.h"12 13static int rtw_ips_pwr_up(struct rtw_dev *rtwdev)14{15 int ret;16 17 ret = rtw_core_start(rtwdev);18 if (ret)19 rtw_err(rtwdev, "leave idle state failed\n");20 21 rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);22 rtw_set_channel(rtwdev);23 24 return ret;25}26 27int rtw_enter_ips(struct rtw_dev *rtwdev)28{29 if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags))30 return 0;31 32 rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);33 34 rtw_core_stop(rtwdev);35 rtw_hci_link_ps(rtwdev, true);36 37 return 0;38}39 40static void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif)41{42 struct rtw_dev *rtwdev = data;43 struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;44 u32 config = ~0;45 46 rtw_vif_port_config(rtwdev, rtwvif, config);47}48 49int rtw_leave_ips(struct rtw_dev *rtwdev)50{51 int ret;52 53 if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))54 return 0;55 56 rtw_hci_link_ps(rtwdev, false);57 58 ret = rtw_ips_pwr_up(rtwdev);59 if (ret) {60 rtw_err(rtwdev, "failed to leave ips state\n");61 return ret;62 }63 64 rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev);65 66 return 0;67}68 69void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)70{71 u8 request, confirm, polling;72 int ret;73 74 request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);75 confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);76 77 /* toggle to request power mode, others remain 0 */78 request ^= request | BIT_RPWM_TOGGLE;79 if (enter) {80 request |= POWER_MODE_LCLK;81 if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)82 request |= POWER_MODE_PG;83 }84 /* Each request require an ack from firmware */85 request |= POWER_MODE_ACK;86 87 if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))88 request |= POWER_TX_WAKE;89 90 rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);91 92 /* Check firmware get the power requset and ack via cpwm register */93 ret = read_poll_timeout_atomic(rtw_read8, polling,94 (polling ^ confirm) & BIT_RPWM_TOGGLE,95 100, 15000, true, rtwdev,96 rtwdev->hci.cpwm_addr);97 if (ret) {98 /* Hit here means that driver failed to get an ack from firmware.99 * The reason could be that hardware is locked at Deep sleep,100 * so most of the hardware circuits are not working, even101 * register read/write; or firmware is locked in some state and102 * cannot get the request. It should be treated as fatal error103 * and requires an entire analysis about the firmware/hardware.104 */105 WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",106 enter ? "entering" : "leaving");107 rtw_fw_dump_dbg_info(rtwdev);108 }109}110EXPORT_SYMBOL(rtw_power_mode_change);111 112static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)113{114 rtw_hci_deep_ps(rtwdev, false);115}116 117static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)118{119 int i;120 121 /* Driver needs to wait for firmware to leave LPS state122 * successfully. Firmware will send null packet to inform AP,123 * and see if AP sends an ACK back, then firmware will restore124 * the REG_TCR register.125 *126 * If driver does not wait for firmware, null packet with127 * PS bit could be sent due to incorrect REG_TCR setting.128 *129 * In our test, 100ms should be enough for firmware to finish130 * the flow. If REG_TCR Register is still incorrect after 100ms,131 * just modify it directly, and throw a warn message.132 */133 for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {134 if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)135 return 0;136 msleep(20);137 }138 139 return -EBUSY;140}141 142static int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)143{144 if (wait_for_completion_timeout(&rtwdev->lps_leave_check,145 LEAVE_LPS_TIMEOUT))146 return 0;147 return -EBUSY;148}149 150static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)151{152 bool ret = false;153 struct rtw_fw_state *fw;154 155 if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))156 fw = &rtwdev->wow_fw;157 else158 fw = &rtwdev->fw;159 160 if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))161 ret = __rtw_fw_leave_lps_check_c2h(rtwdev);162 else163 ret = __rtw_fw_leave_lps_check_reg(rtwdev);164 165 if (ret) {166 rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);167 rtw_warn(rtwdev, "firmware failed to leave lps state\n");168 rtw_fw_dump_dbg_info(rtwdev);169 }170}171 172static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)173{174 struct rtw_fw_state *fw;175 176 if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))177 fw = &rtwdev->wow_fw;178 else179 fw = &rtwdev->fw;180 181 if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))182 reinit_completion(&rtwdev->lps_leave_check);183}184 185static void rtw_leave_lps_core(struct rtw_dev *rtwdev)186{187 struct rtw_lps_conf *conf = &rtwdev->lps_conf;188 189 conf->state = RTW_ALL_ON;190 conf->awake_interval = 1;191 conf->rlbm = 0;192 conf->smart_ps = 0;193 194 rtw_hci_link_ps(rtwdev, false);195 rtw_fw_leave_lps_check_prepare(rtwdev);196 rtw_fw_set_pwr_mode(rtwdev);197 rtw_fw_leave_lps_check(rtwdev);198 199 clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);200 201 rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);202}203 204enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)205{206 if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))207 return rtwdev->lps_conf.wow_deep_mode;208 else209 return rtwdev->lps_conf.deep_mode;210}211 212static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)213{214 if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)215 return;216 217 if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {218 rtw_dbg(rtwdev, RTW_DBG_PS,219 "Should enter LPS before entering deep PS\n");220 return;221 }222 223 if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)224 rtw_fw_set_pg_info(rtwdev);225 226 rtw_hci_deep_ps(rtwdev, true);227}228 229static void rtw_enter_lps_core(struct rtw_dev *rtwdev)230{231 struct rtw_lps_conf *conf = &rtwdev->lps_conf;232 233 conf->state = RTW_RF_OFF;234 conf->awake_interval = 1;235 conf->rlbm = 1;236 conf->smart_ps = 2;237 238 rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);239 240 rtw_fw_set_pwr_mode(rtwdev);241 rtw_hci_link_ps(rtwdev, true);242 243 set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);244}245 246static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)247{248 struct rtw_lps_conf *conf = &rtwdev->lps_conf;249 250 if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))251 return;252 253 conf->mode = RTW_MODE_LPS;254 conf->port_id = port_id;255 256 rtw_enter_lps_core(rtwdev);257}258 259static void __rtw_leave_lps(struct rtw_dev *rtwdev)260{261 struct rtw_lps_conf *conf = &rtwdev->lps_conf;262 263 if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {264 rtw_dbg(rtwdev, RTW_DBG_PS,265 "Should leave deep PS before leaving LPS\n");266 __rtw_leave_lps_deep(rtwdev);267 }268 269 if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))270 return;271 272 conf->mode = RTW_MODE_ACTIVE;273 274 rtw_leave_lps_core(rtwdev);275}276 277void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)278{279 lockdep_assert_held(&rtwdev->mutex);280 281 if (rtwdev->coex.stat.wl_force_lps_ctrl)282 return;283 284 __rtw_enter_lps(rtwdev, port_id);285 __rtw_enter_lps_deep(rtwdev);286}287 288void rtw_leave_lps(struct rtw_dev *rtwdev)289{290 lockdep_assert_held(&rtwdev->mutex);291 292 __rtw_leave_lps_deep(rtwdev);293 __rtw_leave_lps(rtwdev);294}295 296void rtw_leave_lps_deep(struct rtw_dev *rtwdev)297{298 lockdep_assert_held(&rtwdev->mutex);299 300 __rtw_leave_lps_deep(rtwdev);301}302 303struct rtw_vif_recalc_lps_iter_data {304 struct rtw_dev *rtwdev;305 struct ieee80211_vif *found_vif;306 int count;307};308 309static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data,310 struct ieee80211_vif *vif)311{312 if (data->count < 0)313 return;314 315 if (vif->type != NL80211_IFTYPE_STATION) {316 data->count = -1;317 return;318 }319 320 data->count++;321 data->found_vif = vif;322}323 324static void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif)325{326 __rtw_vif_recalc_lps(data, vif);327}328 329void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif)330{331 struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev };332 333 if (new_vif)334 __rtw_vif_recalc_lps(&data, new_vif);335 rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data);336 337 if (data.count == 1 && data.found_vif->cfg.ps) {338 rtwdev->ps_enabled = true;339 } else {340 rtwdev->ps_enabled = false;341 rtw_leave_lps(rtwdev);342 }343}344