linux/drivers/net/wireless/realtek/rtw88/ps.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
   2/* Copyright(c) 2018-2019  Realtek Corporation
   3 */
   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_set_channel(rtwdev);
  22        clear_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags);
  23
  24        return ret;
  25}
  26
  27int rtw_enter_ips(struct rtw_dev *rtwdev)
  28{
  29        set_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags);
  30
  31        rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);
  32
  33        rtw_core_stop(rtwdev);
  34        rtw_hci_link_ps(rtwdev, true);
  35
  36        return 0;
  37}
  38
  39static void rtw_restore_port_cfg_iter(void *data, u8 *mac,
  40                                      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        rtw_hci_link_ps(rtwdev, false);
  54
  55        ret = rtw_ips_pwr_up(rtwdev);
  56        if (ret) {
  57                rtw_err(rtwdev, "failed to leave ips state\n");
  58                return ret;
  59        }
  60
  61        rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev);
  62
  63        rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);
  64
  65        return 0;
  66}
  67
  68void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)
  69{
  70        u8 request, confirm, polling;
  71        int ret;
  72
  73        request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
  74        confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);
  75
  76        /* toggle to request power mode, others remain 0 */
  77        request ^= request | BIT_RPWM_TOGGLE;
  78        if (enter) {
  79                request |= POWER_MODE_LCLK;
  80                if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
  81                        request |= POWER_MODE_PG;
  82        }
  83        /* Each request require an ack from firmware */
  84        request |= POWER_MODE_ACK;
  85
  86        rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);
  87
  88        /* Check firmware get the power requset and ack via cpwm register */
  89        ret = read_poll_timeout_atomic(rtw_read8, polling,
  90                                       (polling ^ confirm) & BIT_RPWM_TOGGLE,
  91                                       100, 15000, true, rtwdev,
  92                                       rtwdev->hci.cpwm_addr);
  93        if (ret) {
  94                /* Hit here means that driver failed to get an ack from firmware.
  95                 * The reason could be that hardware is locked at Deep sleep,
  96                 * so most of the hardware circuits are not working, even
  97                 * register read/write; or firmware is locked in some state and
  98                 * cannot get the request. It should be treated as fatal error
  99                 * and requires an entire analysis about the firmware/hardware.
 100                 */
 101                WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",
 102                     enter ? "entering" : "leaving");
 103        }
 104}
 105EXPORT_SYMBOL(rtw_power_mode_change);
 106
 107static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
 108{
 109        rtw_hci_deep_ps(rtwdev, false);
 110}
 111
 112static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)
 113{
 114        int i;
 115
 116        /* Driver needs to wait for firmware to leave LPS state
 117         * successfully. Firmware will send null packet to inform AP,
 118         * and see if AP sends an ACK back, then firmware will restore
 119         * the REG_TCR register.
 120         *
 121         * If driver does not wait for firmware, null packet with
 122         * PS bit could be sent due to incorrect REG_TCR setting.
 123         *
 124         * In our test, 100ms should be enough for firmware to finish
 125         * the flow. If REG_TCR Register is still incorrect after 100ms,
 126         * just modify it directly, and throw a warn message.
 127         */
 128        for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {
 129                if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)
 130                        return 0;
 131                msleep(20);
 132        }
 133
 134        return -EBUSY;
 135}
 136
 137static  int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)
 138{
 139        if (wait_for_completion_timeout(&rtwdev->lps_leave_check,
 140                                        LEAVE_LPS_TIMEOUT))
 141                return 0;
 142        return -EBUSY;
 143}
 144
 145static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)
 146{
 147        bool ret = false;
 148        struct rtw_fw_state *fw;
 149
 150        if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
 151                fw = &rtwdev->wow_fw;
 152        else
 153                fw = &rtwdev->fw;
 154
 155        if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
 156                ret = __rtw_fw_leave_lps_check_c2h(rtwdev);
 157        else
 158                ret = __rtw_fw_leave_lps_check_reg(rtwdev);
 159
 160        if (ret) {
 161                rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);
 162                rtw_warn(rtwdev, "firmware failed to leave lps state\n");
 163        }
 164}
 165
 166static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)
 167{
 168        struct rtw_fw_state *fw;
 169
 170        if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
 171                fw = &rtwdev->wow_fw;
 172        else
 173                fw = &rtwdev->fw;
 174
 175        if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
 176                reinit_completion(&rtwdev->lps_leave_check);
 177}
 178
 179static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
 180{
 181        struct rtw_lps_conf *conf = &rtwdev->lps_conf;
 182
 183        conf->state = RTW_ALL_ON;
 184        conf->awake_interval = 1;
 185        conf->rlbm = 0;
 186        conf->smart_ps = 0;
 187
 188        rtw_hci_link_ps(rtwdev, false);
 189        rtw_fw_leave_lps_check_prepare(rtwdev);
 190        rtw_fw_set_pwr_mode(rtwdev);
 191        rtw_fw_leave_lps_check(rtwdev);
 192
 193        clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
 194
 195        rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);
 196}
 197
 198enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)
 199{
 200        if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
 201                return rtwdev->lps_conf.wow_deep_mode;
 202        else
 203                return rtwdev->lps_conf.deep_mode;
 204}
 205
 206static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)
 207{
 208        if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)
 209                return;
 210
 211        if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {
 212                rtw_dbg(rtwdev, RTW_DBG_PS,
 213                        "Should enter LPS before entering deep PS\n");
 214                return;
 215        }
 216
 217        if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
 218                rtw_fw_set_pg_info(rtwdev);
 219
 220        rtw_hci_deep_ps(rtwdev, true);
 221}
 222
 223static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
 224{
 225        struct rtw_lps_conf *conf = &rtwdev->lps_conf;
 226
 227        conf->state = RTW_RF_OFF;
 228        conf->awake_interval = 1;
 229        conf->rlbm = 1;
 230        conf->smart_ps = 2;
 231
 232        rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);
 233
 234        rtw_fw_set_pwr_mode(rtwdev);
 235        rtw_hci_link_ps(rtwdev, true);
 236
 237        set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
 238}
 239
 240static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
 241{
 242        struct rtw_lps_conf *conf = &rtwdev->lps_conf;
 243
 244        if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
 245                return;
 246
 247        conf->mode = RTW_MODE_LPS;
 248        conf->port_id = port_id;
 249
 250        rtw_enter_lps_core(rtwdev);
 251}
 252
 253static void __rtw_leave_lps(struct rtw_dev *rtwdev)
 254{
 255        struct rtw_lps_conf *conf = &rtwdev->lps_conf;
 256
 257        if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {
 258                rtw_dbg(rtwdev, RTW_DBG_PS,
 259                        "Should leave deep PS before leaving LPS\n");
 260                __rtw_leave_lps_deep(rtwdev);
 261        }
 262
 263        if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
 264                return;
 265
 266        conf->mode = RTW_MODE_ACTIVE;
 267
 268        rtw_leave_lps_core(rtwdev);
 269}
 270
 271void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
 272{
 273        lockdep_assert_held(&rtwdev->mutex);
 274
 275        if (rtwdev->coex.stat.wl_force_lps_ctrl)
 276                return;
 277
 278        __rtw_enter_lps(rtwdev, port_id);
 279        __rtw_enter_lps_deep(rtwdev);
 280}
 281
 282void rtw_leave_lps(struct rtw_dev *rtwdev)
 283{
 284        lockdep_assert_held(&rtwdev->mutex);
 285
 286        __rtw_leave_lps_deep(rtwdev);
 287        __rtw_leave_lps(rtwdev);
 288}
 289
 290void rtw_leave_lps_deep(struct rtw_dev *rtwdev)
 291{
 292        lockdep_assert_held(&rtwdev->mutex);
 293
 294        __rtw_leave_lps_deep(rtwdev);
 295}
 296