162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
262306a36Sopenharmony_ci/* Copyright(c) 2018-2019  Realtek Corporation
362306a36Sopenharmony_ci */
462306a36Sopenharmony_ci
562306a36Sopenharmony_ci#include "main.h"
662306a36Sopenharmony_ci#include "reg.h"
762306a36Sopenharmony_ci#include "fw.h"
862306a36Sopenharmony_ci#include "ps.h"
962306a36Sopenharmony_ci#include "mac.h"
1062306a36Sopenharmony_ci#include "coex.h"
1162306a36Sopenharmony_ci#include "debug.h"
1262306a36Sopenharmony_ci
1362306a36Sopenharmony_cistatic int rtw_ips_pwr_up(struct rtw_dev *rtwdev)
1462306a36Sopenharmony_ci{
1562306a36Sopenharmony_ci	int ret;
1662306a36Sopenharmony_ci
1762306a36Sopenharmony_ci	ret = rtw_core_start(rtwdev);
1862306a36Sopenharmony_ci	if (ret)
1962306a36Sopenharmony_ci		rtw_err(rtwdev, "leave idle state failed\n");
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci	rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE);
2262306a36Sopenharmony_ci	rtw_set_channel(rtwdev);
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_ci	return ret;
2562306a36Sopenharmony_ci}
2662306a36Sopenharmony_ci
2762306a36Sopenharmony_ciint rtw_enter_ips(struct rtw_dev *rtwdev)
2862306a36Sopenharmony_ci{
2962306a36Sopenharmony_ci	if (!test_bit(RTW_FLAG_POWERON, rtwdev->flags))
3062306a36Sopenharmony_ci		return 0;
3162306a36Sopenharmony_ci
3262306a36Sopenharmony_ci	rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	rtw_core_stop(rtwdev);
3562306a36Sopenharmony_ci	rtw_hci_link_ps(rtwdev, true);
3662306a36Sopenharmony_ci
3762306a36Sopenharmony_ci	return 0;
3862306a36Sopenharmony_ci}
3962306a36Sopenharmony_ci
4062306a36Sopenharmony_cistatic void rtw_restore_port_cfg_iter(void *data, struct ieee80211_vif *vif)
4162306a36Sopenharmony_ci{
4262306a36Sopenharmony_ci	struct rtw_dev *rtwdev = data;
4362306a36Sopenharmony_ci	struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv;
4462306a36Sopenharmony_ci	u32 config = ~0;
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci	rtw_vif_port_config(rtwdev, rtwvif, config);
4762306a36Sopenharmony_ci}
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ciint rtw_leave_ips(struct rtw_dev *rtwdev)
5062306a36Sopenharmony_ci{
5162306a36Sopenharmony_ci	int ret;
5262306a36Sopenharmony_ci
5362306a36Sopenharmony_ci	if (test_bit(RTW_FLAG_POWERON, rtwdev->flags))
5462306a36Sopenharmony_ci		return 0;
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	rtw_hci_link_ps(rtwdev, false);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	ret = rtw_ips_pwr_up(rtwdev);
5962306a36Sopenharmony_ci	if (ret) {
6062306a36Sopenharmony_ci		rtw_err(rtwdev, "failed to leave ips state\n");
6162306a36Sopenharmony_ci		return ret;
6262306a36Sopenharmony_ci	}
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	rtw_iterate_vifs(rtwdev, rtw_restore_port_cfg_iter, rtwdev);
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return 0;
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_civoid rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter)
7062306a36Sopenharmony_ci{
7162306a36Sopenharmony_ci	u8 request, confirm, polling;
7262306a36Sopenharmony_ci	int ret;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
7562306a36Sopenharmony_ci	confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);
7662306a36Sopenharmony_ci
7762306a36Sopenharmony_ci	/* toggle to request power mode, others remain 0 */
7862306a36Sopenharmony_ci	request ^= request | BIT_RPWM_TOGGLE;
7962306a36Sopenharmony_ci	if (enter) {
8062306a36Sopenharmony_ci		request |= POWER_MODE_LCLK;
8162306a36Sopenharmony_ci		if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
8262306a36Sopenharmony_ci			request |= POWER_MODE_PG;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci	/* Each request require an ack from firmware */
8562306a36Sopenharmony_ci	request |= POWER_MODE_ACK;
8662306a36Sopenharmony_ci
8762306a36Sopenharmony_ci	if (rtw_fw_feature_check(&rtwdev->fw, FW_FEATURE_TX_WAKE))
8862306a36Sopenharmony_ci		request |= POWER_TX_WAKE;
8962306a36Sopenharmony_ci
9062306a36Sopenharmony_ci	rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	/* Check firmware get the power requset and ack via cpwm register */
9362306a36Sopenharmony_ci	ret = read_poll_timeout_atomic(rtw_read8, polling,
9462306a36Sopenharmony_ci				       (polling ^ confirm) & BIT_RPWM_TOGGLE,
9562306a36Sopenharmony_ci				       100, 15000, true, rtwdev,
9662306a36Sopenharmony_ci				       rtwdev->hci.cpwm_addr);
9762306a36Sopenharmony_ci	if (ret) {
9862306a36Sopenharmony_ci		/* Hit here means that driver failed to get an ack from firmware.
9962306a36Sopenharmony_ci		 * The reason could be that hardware is locked at Deep sleep,
10062306a36Sopenharmony_ci		 * so most of the hardware circuits are not working, even
10162306a36Sopenharmony_ci		 * register read/write; or firmware is locked in some state and
10262306a36Sopenharmony_ci		 * cannot get the request. It should be treated as fatal error
10362306a36Sopenharmony_ci		 * and requires an entire analysis about the firmware/hardware.
10462306a36Sopenharmony_ci		 */
10562306a36Sopenharmony_ci		WARN(1, "firmware failed to ack driver for %s Deep Power mode\n",
10662306a36Sopenharmony_ci		     enter ? "entering" : "leaving");
10762306a36Sopenharmony_ci	}
10862306a36Sopenharmony_ci}
10962306a36Sopenharmony_ciEXPORT_SYMBOL(rtw_power_mode_change);
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_cistatic void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
11262306a36Sopenharmony_ci{
11362306a36Sopenharmony_ci	rtw_hci_deep_ps(rtwdev, false);
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int __rtw_fw_leave_lps_check_reg(struct rtw_dev *rtwdev)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	int i;
11962306a36Sopenharmony_ci
12062306a36Sopenharmony_ci	/* Driver needs to wait for firmware to leave LPS state
12162306a36Sopenharmony_ci	 * successfully. Firmware will send null packet to inform AP,
12262306a36Sopenharmony_ci	 * and see if AP sends an ACK back, then firmware will restore
12362306a36Sopenharmony_ci	 * the REG_TCR register.
12462306a36Sopenharmony_ci	 *
12562306a36Sopenharmony_ci	 * If driver does not wait for firmware, null packet with
12662306a36Sopenharmony_ci	 * PS bit could be sent due to incorrect REG_TCR setting.
12762306a36Sopenharmony_ci	 *
12862306a36Sopenharmony_ci	 * In our test, 100ms should be enough for firmware to finish
12962306a36Sopenharmony_ci	 * the flow. If REG_TCR Register is still incorrect after 100ms,
13062306a36Sopenharmony_ci	 * just modify it directly, and throw a warn message.
13162306a36Sopenharmony_ci	 */
13262306a36Sopenharmony_ci	for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {
13362306a36Sopenharmony_ci		if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)
13462306a36Sopenharmony_ci			return 0;
13562306a36Sopenharmony_ci		msleep(20);
13662306a36Sopenharmony_ci	}
13762306a36Sopenharmony_ci
13862306a36Sopenharmony_ci	return -EBUSY;
13962306a36Sopenharmony_ci}
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_cistatic  int __rtw_fw_leave_lps_check_c2h(struct rtw_dev *rtwdev)
14262306a36Sopenharmony_ci{
14362306a36Sopenharmony_ci	if (wait_for_completion_timeout(&rtwdev->lps_leave_check,
14462306a36Sopenharmony_ci					LEAVE_LPS_TIMEOUT))
14562306a36Sopenharmony_ci		return 0;
14662306a36Sopenharmony_ci	return -EBUSY;
14762306a36Sopenharmony_ci}
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_cistatic void rtw_fw_leave_lps_check(struct rtw_dev *rtwdev)
15062306a36Sopenharmony_ci{
15162306a36Sopenharmony_ci	bool ret = false;
15262306a36Sopenharmony_ci	struct rtw_fw_state *fw;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
15562306a36Sopenharmony_ci		fw = &rtwdev->wow_fw;
15662306a36Sopenharmony_ci	else
15762306a36Sopenharmony_ci		fw = &rtwdev->fw;
15862306a36Sopenharmony_ci
15962306a36Sopenharmony_ci	if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
16062306a36Sopenharmony_ci		ret = __rtw_fw_leave_lps_check_c2h(rtwdev);
16162306a36Sopenharmony_ci	else
16262306a36Sopenharmony_ci		ret = __rtw_fw_leave_lps_check_reg(rtwdev);
16362306a36Sopenharmony_ci
16462306a36Sopenharmony_ci	if (ret) {
16562306a36Sopenharmony_ci		rtw_write32_clr(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN);
16662306a36Sopenharmony_ci		rtw_warn(rtwdev, "firmware failed to leave lps state\n");
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci}
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_cistatic void rtw_fw_leave_lps_check_prepare(struct rtw_dev *rtwdev)
17162306a36Sopenharmony_ci{
17262306a36Sopenharmony_ci	struct rtw_fw_state *fw;
17362306a36Sopenharmony_ci
17462306a36Sopenharmony_ci	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
17562306a36Sopenharmony_ci		fw = &rtwdev->wow_fw;
17662306a36Sopenharmony_ci	else
17762306a36Sopenharmony_ci		fw = &rtwdev->fw;
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	if (rtw_fw_feature_check(fw, FW_FEATURE_LPS_C2H))
18062306a36Sopenharmony_ci		reinit_completion(&rtwdev->lps_leave_check);
18162306a36Sopenharmony_ci}
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_cistatic void rtw_leave_lps_core(struct rtw_dev *rtwdev)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	conf->state = RTW_ALL_ON;
18862306a36Sopenharmony_ci	conf->awake_interval = 1;
18962306a36Sopenharmony_ci	conf->rlbm = 0;
19062306a36Sopenharmony_ci	conf->smart_ps = 0;
19162306a36Sopenharmony_ci
19262306a36Sopenharmony_ci	rtw_hci_link_ps(rtwdev, false);
19362306a36Sopenharmony_ci	rtw_fw_leave_lps_check_prepare(rtwdev);
19462306a36Sopenharmony_ci	rtw_fw_set_pwr_mode(rtwdev);
19562306a36Sopenharmony_ci	rtw_fw_leave_lps_check(rtwdev);
19662306a36Sopenharmony_ci
19762306a36Sopenharmony_ci	clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
19862306a36Sopenharmony_ci
19962306a36Sopenharmony_ci	rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);
20062306a36Sopenharmony_ci}
20162306a36Sopenharmony_ci
20262306a36Sopenharmony_cienum rtw_lps_deep_mode rtw_get_lps_deep_mode(struct rtw_dev *rtwdev)
20362306a36Sopenharmony_ci{
20462306a36Sopenharmony_ci	if (test_bit(RTW_FLAG_WOWLAN, rtwdev->flags))
20562306a36Sopenharmony_ci		return rtwdev->lps_conf.wow_deep_mode;
20662306a36Sopenharmony_ci	else
20762306a36Sopenharmony_ci		return rtwdev->lps_conf.deep_mode;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistatic void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)
21162306a36Sopenharmony_ci{
21262306a36Sopenharmony_ci	if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_NONE)
21362306a36Sopenharmony_ci		return;
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {
21662306a36Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_PS,
21762306a36Sopenharmony_ci			"Should enter LPS before entering deep PS\n");
21862306a36Sopenharmony_ci		return;
21962306a36Sopenharmony_ci	}
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (rtw_get_lps_deep_mode(rtwdev) == LPS_DEEP_MODE_PG)
22262306a36Sopenharmony_ci		rtw_fw_set_pg_info(rtwdev);
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_ci	rtw_hci_deep_ps(rtwdev, true);
22562306a36Sopenharmony_ci}
22662306a36Sopenharmony_ci
22762306a36Sopenharmony_cistatic void rtw_enter_lps_core(struct rtw_dev *rtwdev)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
23062306a36Sopenharmony_ci
23162306a36Sopenharmony_ci	conf->state = RTW_RF_OFF;
23262306a36Sopenharmony_ci	conf->awake_interval = 1;
23362306a36Sopenharmony_ci	conf->rlbm = 1;
23462306a36Sopenharmony_ci	conf->smart_ps = 2;
23562306a36Sopenharmony_ci
23662306a36Sopenharmony_ci	rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);
23762306a36Sopenharmony_ci
23862306a36Sopenharmony_ci	rtw_fw_set_pwr_mode(rtwdev);
23962306a36Sopenharmony_ci	rtw_hci_link_ps(rtwdev, true);
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
24262306a36Sopenharmony_ci}
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_cistatic void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
24562306a36Sopenharmony_ci{
24662306a36Sopenharmony_ci	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_ci	if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
24962306a36Sopenharmony_ci		return;
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_ci	conf->mode = RTW_MODE_LPS;
25262306a36Sopenharmony_ci	conf->port_id = port_id;
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci	rtw_enter_lps_core(rtwdev);
25562306a36Sopenharmony_ci}
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic void __rtw_leave_lps(struct rtw_dev *rtwdev)
25862306a36Sopenharmony_ci{
25962306a36Sopenharmony_ci	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {
26262306a36Sopenharmony_ci		rtw_dbg(rtwdev, RTW_DBG_PS,
26362306a36Sopenharmony_ci			"Should leave deep PS before leaving LPS\n");
26462306a36Sopenharmony_ci		__rtw_leave_lps_deep(rtwdev);
26562306a36Sopenharmony_ci	}
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
26862306a36Sopenharmony_ci		return;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	conf->mode = RTW_MODE_ACTIVE;
27162306a36Sopenharmony_ci
27262306a36Sopenharmony_ci	rtw_leave_lps_core(rtwdev);
27362306a36Sopenharmony_ci}
27462306a36Sopenharmony_ci
27562306a36Sopenharmony_civoid rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
27662306a36Sopenharmony_ci{
27762306a36Sopenharmony_ci	lockdep_assert_held(&rtwdev->mutex);
27862306a36Sopenharmony_ci
27962306a36Sopenharmony_ci	if (rtwdev->coex.stat.wl_force_lps_ctrl)
28062306a36Sopenharmony_ci		return;
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci	__rtw_enter_lps(rtwdev, port_id);
28362306a36Sopenharmony_ci	__rtw_enter_lps_deep(rtwdev);
28462306a36Sopenharmony_ci}
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_civoid rtw_leave_lps(struct rtw_dev *rtwdev)
28762306a36Sopenharmony_ci{
28862306a36Sopenharmony_ci	lockdep_assert_held(&rtwdev->mutex);
28962306a36Sopenharmony_ci
29062306a36Sopenharmony_ci	__rtw_leave_lps_deep(rtwdev);
29162306a36Sopenharmony_ci	__rtw_leave_lps(rtwdev);
29262306a36Sopenharmony_ci}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_civoid rtw_leave_lps_deep(struct rtw_dev *rtwdev)
29562306a36Sopenharmony_ci{
29662306a36Sopenharmony_ci	lockdep_assert_held(&rtwdev->mutex);
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	__rtw_leave_lps_deep(rtwdev);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_cistruct rtw_vif_recalc_lps_iter_data {
30262306a36Sopenharmony_ci	struct rtw_dev *rtwdev;
30362306a36Sopenharmony_ci	struct ieee80211_vif *found_vif;
30462306a36Sopenharmony_ci	int count;
30562306a36Sopenharmony_ci};
30662306a36Sopenharmony_ci
30762306a36Sopenharmony_cistatic void __rtw_vif_recalc_lps(struct rtw_vif_recalc_lps_iter_data *data,
30862306a36Sopenharmony_ci				 struct ieee80211_vif *vif)
30962306a36Sopenharmony_ci{
31062306a36Sopenharmony_ci	if (data->count < 0)
31162306a36Sopenharmony_ci		return;
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_ci	if (vif->type != NL80211_IFTYPE_STATION) {
31462306a36Sopenharmony_ci		data->count = -1;
31562306a36Sopenharmony_ci		return;
31662306a36Sopenharmony_ci	}
31762306a36Sopenharmony_ci
31862306a36Sopenharmony_ci	data->count++;
31962306a36Sopenharmony_ci	data->found_vif = vif;
32062306a36Sopenharmony_ci}
32162306a36Sopenharmony_ci
32262306a36Sopenharmony_cistatic void rtw_vif_recalc_lps_iter(void *data, struct ieee80211_vif *vif)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	__rtw_vif_recalc_lps(data, vif);
32562306a36Sopenharmony_ci}
32662306a36Sopenharmony_ci
32762306a36Sopenharmony_civoid rtw_recalc_lps(struct rtw_dev *rtwdev, struct ieee80211_vif *new_vif)
32862306a36Sopenharmony_ci{
32962306a36Sopenharmony_ci	struct rtw_vif_recalc_lps_iter_data data = { .rtwdev = rtwdev };
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	if (new_vif)
33262306a36Sopenharmony_ci		__rtw_vif_recalc_lps(&data, new_vif);
33362306a36Sopenharmony_ci	rtw_iterate_vifs(rtwdev, rtw_vif_recalc_lps_iter, &data);
33462306a36Sopenharmony_ci
33562306a36Sopenharmony_ci	if (data.count == 1 && data.found_vif->cfg.ps) {
33662306a36Sopenharmony_ci		rtwdev->ps_enabled = true;
33762306a36Sopenharmony_ci	} else {
33862306a36Sopenharmony_ci		rtwdev->ps_enabled = false;
33962306a36Sopenharmony_ci		rtw_leave_lps(rtwdev);
34062306a36Sopenharmony_ci	}
34162306a36Sopenharmony_ci}
342