Path: blob/master/drivers/net/wireless/realtek/rtw88/ps.c
25924 views
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause1/* Copyright(c) 2018-2019 Realtek Corporation2*/34#include "main.h"5#include "reg.h"6#include "fw.h"7#include "ps.h"8#include "mac.h"9#include "coex.h"10#include "debug.h"1112static int rtw_ips_pwr_up(struct rtw_dev *rtwdev)13{14int ret;1516ret = rtw_core_start(rtwdev);17if (ret)18rtw_err(rtwdev, "leave idle state failed\n");1920rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);21rtw_set_channel(rtwdev);2223return ret;24}2526int rtw_enter_ips(struct rtw_dev *rtwdev)27{28if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags))29return 0;3031rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);3233rtw_core_stop(rtwdev);34rtw_hci_link_ps(rtwdev, true);3536return 0;37}3839static void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif)40{41struct rtw_dev *rtwdev = data;42struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;43u32 config = ~0;4445rtw_vif_port_config(rtwdev, rtwvif, config);46}4748int rtw_leave_ips(struct rtw_dev *rtwdev)49{50int ret;5152if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))53return 0;5455rtw_hci_link_ps(rtwdev, false);5657ret = rtw_ips_pwr_up(rtwdev);58if (ret) {59rtw_err(rtwdev, "failed to leave ips state\n");60return ret;61}6263rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev);6465return 0;66}6768void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)69{70u8 request, confirm, polling;71int ret;7273request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);74confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);7576/* toggle to request power mode, others remain 0 */77request ^= request | BIT_RPWM_TOGGLE;78if (enter) {79request |= POWER_MODE_LCLK;80if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)81request |= POWER_MODE_PG;82}83/* Each request require an ack from firmware */84request |= POWER_MODE_ACK;8586if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))87request |= POWER_TX_WAKE;8889rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);9091/* Check firmware get the power requset and ack via cpwm register */92ret = read_poll_timeout_atomic(rtw_read8, polling,93(polling ^ confirm) & BIT_RPWM_TOGGLE,94100, 15000, true, rtwdev,95rtwdev->hci.cpwm_addr);96if (ret) {97/* Hit here means that driver failed to get an ack from firmware.98* The reason could be that hardware is locked at Deep sleep,99* so most of the hardware circuits are not working, even100* register read/write; or firmware is locked in some state and101* cannot get the request. It should be treated as fatal error102* and requires an entire analysis about the firmware/hardware.103*/104WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",105enter ? "entering" : "leaving");106rtw_fw_dump_dbg_info(rtwdev);107}108}109EXPORT_SYMBOL(rtw_power_mode_change);110111static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)112{113rtw_hci_deep_ps(rtwdev, false);114}115116static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)117{118int i;119120/* Driver needs to wait for firmware to leave LPS state121* successfully. Firmware will send null packet to inform AP,122* and see if AP sends an ACK back, then firmware will restore123* the REG_TCR register.124*125* If driver does not wait for firmware, null packet with126* PS bit could be sent due to incorrect REG_TCR setting.127*128* In our test, 100ms should be enough for firmware to finish129* the flow. If REG_TCR Register is still incorrect after 100ms,130* just modify it directly, and throw a warn message.131*/132for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {133if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)134return 0;135msleep(20);136}137138return -EBUSY;139}140141static int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)142{143if (wait_for_completion_timeout(&rtwdev->lps_leave_check,144LEAVE_LPS_TIMEOUT))145return 0;146return -EBUSY;147}148149static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)150{151bool ret = false;152struct rtw_fw_state *fw;153154if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))155fw = &rtwdev->wow_fw;156else157fw = &rtwdev->fw;158159if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))160ret = __rtw_fw_leave_lps_check_c2h(rtwdev);161else162ret = __rtw_fw_leave_lps_check_reg(rtwdev);163164if (ret) {165rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);166rtw_warn(rtwdev, "firmware failed to leave lps state\n");167rtw_fw_dump_dbg_info(rtwdev);168}169}170171static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)172{173struct rtw_fw_state *fw;174175if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))176fw = &rtwdev->wow_fw;177else178fw = &rtwdev->fw;179180if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))181reinit_completion(&rtwdev->lps_leave_check);182}183184static void rtw_leave_lps_core(struct rtw_dev *rtwdev)185{186struct rtw_lps_conf *conf = &rtwdev->lps_conf;187188conf->state = RTW_ALL_ON;189conf->awake_interval = 1;190conf->rlbm = 0;191conf->smart_ps = 0;192193rtw_hci_link_ps(rtwdev, false);194rtw_fw_leave_lps_check_prepare(rtwdev);195rtw_fw_set_pwr_mode(rtwdev);196rtw_fw_leave_lps_check(rtwdev);197198clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);199200rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);201}202203enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)204{205if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))206return rtwdev->lps_conf.wow_deep_mode;207else208return rtwdev->lps_conf.deep_mode;209}210211static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)212{213if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)214return;215216if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {217rtw_dbg(rtwdev, RTW_DBG_PS,218"Should enter LPS before entering deep PS\n");219return;220}221222if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)223rtw_fw_set_pg_info(rtwdev);224225rtw_hci_deep_ps(rtwdev, true);226}227228static void rtw_enter_lps_core(struct rtw_dev *rtwdev)229{230struct rtw_lps_conf *conf = &rtwdev->lps_conf;231232conf->state = RTW_RF_OFF;233conf->awake_interval = 1;234conf->rlbm = 1;235conf->smart_ps = 2;236237rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);238239rtw_fw_set_pwr_mode(rtwdev);240rtw_hci_link_ps(rtwdev, true);241242set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);243}244245static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)246{247struct rtw_lps_conf *conf = &rtwdev->lps_conf;248249if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))250return;251252conf->mode = RTW_MODE_LPS;253conf->port_id = port_id;254255rtw_enter_lps_core(rtwdev);256}257258static void __rtw_leave_lps(struct rtw_dev *rtwdev)259{260struct rtw_lps_conf *conf = &rtwdev->lps_conf;261262if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {263rtw_dbg(rtwdev, RTW_DBG_PS,264"Should leave deep PS before leaving LPS\n");265__rtw_leave_lps_deep(rtwdev);266}267268if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))269return;270271conf->mode = RTW_MODE_ACTIVE;272273rtw_leave_lps_core(rtwdev);274}275276void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)277{278lockdep_assert_held(&rtwdev->mutex);279280if (rtwdev->coex.stat.wl_force_lps_ctrl)281return;282283__rtw_enter_lps(rtwdev, port_id);284__rtw_enter_lps_deep(rtwdev);285}286287void rtw_leave_lps(struct rtw_dev *rtwdev)288{289lockdep_assert_held(&rtwdev->mutex);290291__rtw_leave_lps_deep(rtwdev);292__rtw_leave_lps(rtwdev);293}294295void rtw_leave_lps_deep(struct rtw_dev *rtwdev)296{297lockdep_assert_held(&rtwdev->mutex);298299__rtw_leave_lps_deep(rtwdev);300}301302struct rtw_vif_recalc_lps_iter_data {303struct rtw_dev *rtwdev;304struct ieee80211_vif *found_vif;305int count;306};307308static void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data,309struct ieee80211_vif *vif)310{311if (data->count < 0)312return;313314if (vif->type != NL80211_IFTYPE_STATION) {315data->count = -1;316return;317}318319data->count++;320data->found_vif = vif;321}322323static void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif)324{325__rtw_vif_recalc_lps(data, vif);326}327328void rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif)329{330struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev };331332if (new_vif)333__rtw_vif_recalc_lps(&data, new_vif);334rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data);335336if (data.count == 1 && data.found_vif->cfg.ps) {337rtwdev->ps_enabled = true;338} else {339rtwdev->ps_enabled = false;340rtw_leave_lps(rtwdev);341}342}343344345