The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/contrib/dev/rtw88/ps.c

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    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 
   13 static 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 
   27 int 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 
   39 static 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 
   49 int 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 
   68 void 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         if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
   87                 request |= POWER_TX_WAKE;
   88 
   89         rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);
   90 
   91         /* Check firmware get the power requset and ack via cpwm register */
   92         ret = read_poll_timeout_atomic(rtw_read8, polling,
   93                                        (polling ^ confirm) & BIT_RPWM_TOGGLE,
   94                                        100, 15000, true, rtwdev,
   95                                        rtwdev->hci.cpwm_addr);
   96         if (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, even
  100                  * register read/write; or firmware is locked in some state and
  101                  * cannot get the request. It should be treated as fatal error
  102                  * and requires an entire analysis about the firmware/hardware.
  103                  */
  104                 WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",
  105                      enter ? "entering" : "leaving");
  106         }
  107 }
  108 EXPORT_SYMBOL(rtw_power_mode_change);
  109 
  110 static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
  111 {
  112         rtw_hci_deep_ps(rtwdev, false);
  113 }
  114 
  115 static int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)
  116 {
  117         int i;
  118 
  119         /* Driver needs to wait for firmware to leave LPS state
  120          * successfully. Firmware will send null packet to inform AP,
  121          * and see if AP sends an ACK back, then firmware will restore
  122          * the REG_TCR register.
  123          *
  124          * If driver does not wait for firmware, null packet with
  125          * PS bit could be sent due to incorrect REG_TCR setting.
  126          *
  127          * In our test, 100ms should be enough for firmware to finish
  128          * the flow. If REG_TCR Register is still incorrect after 100ms,
  129          * just modify it directly, and throw a warn message.
  130          */
  131         for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {
  132                 if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)
  133                         return 0;
  134                 msleep(20);
  135         }
  136 
  137         return -EBUSY;
  138 }
  139 
  140 static  int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)
  141 {
  142         if (wait_for_completion_timeout(&rtwdev->lps_leave_check,
  143                                         LEAVE_LPS_TIMEOUT))
  144                 return 0;
  145         return -EBUSY;
  146 }
  147 
  148 static void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)
  149 {
  150         bool ret = false;
  151         struct rtw_fw_state *fw;
  152 
  153         if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
  154                 fw = &rtwdev->wow_fw;
  155         else
  156                 fw = &rtwdev->fw;
  157 
  158         if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
  159                 ret = __rtw_fw_leave_lps_check_c2h(rtwdev);
  160         else
  161                 ret = __rtw_fw_leave_lps_check_reg(rtwdev);
  162 
  163         if (ret) {
  164                 rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);
  165                 rtw_warn(rtwdev, "firmware failed to leave lps state\n");
  166         }
  167 }
  168 
  169 static void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)
  170 {
  171         struct rtw_fw_state *fw;
  172 
  173         if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
  174                 fw = &rtwdev->wow_fw;
  175         else
  176                 fw = &rtwdev->fw;
  177 
  178         if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
  179                 reinit_completion(&rtwdev->lps_leave_check);
  180 }
  181 
  182 static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
  183 {
  184         struct rtw_lps_conf *conf = &rtwdev->lps_conf;
  185 
  186         conf->state = RTW_ALL_ON;
  187         conf->awake_interval = 1;
  188         conf->rlbm = 0;
  189         conf->smart_ps = 0;
  190 
  191         rtw_hci_link_ps(rtwdev, false);
  192         rtw_fw_leave_lps_check_prepare(rtwdev);
  193         rtw_fw_set_pwr_mode(rtwdev);
  194         rtw_fw_leave_lps_check(rtwdev);
  195 
  196         clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
  197 
  198         rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);
  199 }
  200 
  201 enum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)
  202 {
  203         if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
  204                 return rtwdev->lps_conf.wow_deep_mode;
  205         else
  206                 return rtwdev->lps_conf.deep_mode;
  207 }
  208 
  209 static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)
  210 {
  211         if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)
  212                 return;
  213 
  214         if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {
  215                 rtw_dbg(rtwdev, RTW_DBG_PS,
  216                         "Should enter LPS before entering deep PS\n");
  217                 return;
  218         }
  219 
  220         if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
  221                 rtw_fw_set_pg_info(rtwdev);
  222 
  223         rtw_hci_deep_ps(rtwdev, true);
  224 }
  225 
  226 static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
  227 {
  228         struct rtw_lps_conf *conf = &rtwdev->lps_conf;
  229 
  230         conf->state = RTW_RF_OFF;
  231         conf->awake_interval = 1;
  232         conf->rlbm = 1;
  233         conf->smart_ps = 2;
  234 
  235         rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);
  236 
  237         rtw_fw_set_pwr_mode(rtwdev);
  238         rtw_hci_link_ps(rtwdev, true);
  239 
  240         set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
  241 }
  242 
  243 static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
  244 {
  245         struct rtw_lps_conf *conf = &rtwdev->lps_conf;
  246 
  247         if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
  248                 return;
  249 
  250         conf->mode = RTW_MODE_LPS;
  251         conf->port_id = port_id;
  252 
  253         rtw_enter_lps_core(rtwdev);
  254 }
  255 
  256 static void __rtw_leave_lps(struct rtw_dev *rtwdev)
  257 {
  258         struct rtw_lps_conf *conf = &rtwdev->lps_conf;
  259 
  260         if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {
  261                 rtw_dbg(rtwdev, RTW_DBG_PS,
  262                         "Should leave deep PS before leaving LPS\n");
  263                 __rtw_leave_lps_deep(rtwdev);
  264         }
  265 
  266         if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
  267                 return;
  268 
  269         conf->mode = RTW_MODE_ACTIVE;
  270 
  271         rtw_leave_lps_core(rtwdev);
  272 }
  273 
  274 void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
  275 {
  276         lockdep_assert_held(&rtwdev->mutex);
  277 
  278         if (rtwdev->coex.stat.wl_force_lps_ctrl)
  279                 return;
  280 
  281         __rtw_enter_lps(rtwdev, port_id);
  282         __rtw_enter_lps_deep(rtwdev);
  283 }
  284 
  285 void rtw_leave_lps(struct rtw_dev *rtwdev)
  286 {
  287         lockdep_assert_held(&rtwdev->mutex);
  288 
  289         __rtw_leave_lps_deep(rtwdev);
  290         __rtw_leave_lps(rtwdev);
  291 }
  292 
  293 void rtw_leave_lps_deep(struct rtw_dev *rtwdev)
  294 {
  295         lockdep_assert_held(&rtwdev->mutex);
  296 
  297         __rtw_leave_lps_deep(rtwdev);
  298 }

Cache object: ff93413fdcb7c93ea94cef530764bf2c


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.