cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ps.c (7116B)


      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	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}
    108EXPORT_SYMBOL(rtw_power_mode_change);
    109
    110static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
    111{
    112	rtw_hci_deep_ps(rtwdev, false);
    113}
    114
    115static 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
    140static  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
    148static 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
    169static 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
    182static 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
    201enum 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
    209static 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
    226static 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
    243static 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
    256static 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
    274void 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
    285void 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
    293void rtw_leave_lps_deep(struct rtw_dev *rtwdev)
    294{
    295	lockdep_assert_held(&rtwdev->mutex);
    296
    297	__rtw_leave_lps_deep(rtwdev);
    298}