1 // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
2 /* Copyright(c) 2019-2020  Realtek Corporation
3  */
4 
5 #include "coex.h"
6 #include "core.h"
7 #include "debug.h"
8 #include "fw.h"
9 #include "mac.h"
10 #include "ps.h"
11 #include "reg.h"
12 #include "util.h"
13 
14 static int rtw89_fw_leave_lps_check(struct rtw89_dev *rtwdev, u8 macid)
15 {
16 	u32 pwr_en_bit = 0xE;
17 	u32 chk_msk = pwr_en_bit << (4 * macid);
18 	u32 polling;
19 	int ret;
20 
21 	ret = read_poll_timeout_atomic(rtw89_read32_mask, polling, !polling,
22 				       1000, 50000, false, rtwdev,
23 				       R_AX_PPWRBIT_SETTING, chk_msk);
24 	if (ret) {
25 		rtw89_info(rtwdev, "rtw89: failed to leave lps state\n");
26 		return -EBUSY;
27 	}
28 
29 	return 0;
30 }
31 
32 static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev)
33 {
34 	if (!rtwdev->ps_mode)
35 		return;
36 
37 	if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
38 		return;
39 
40 	rtw89_mac_power_mode_change(rtwdev, true);
41 }
42 
43 void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
44 {
45 	if (!rtwdev->ps_mode)
46 		return;
47 
48 	if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
49 		rtw89_mac_power_mode_change(rtwdev, false);
50 }
51 
52 static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
53 {
54 	struct rtw89_lps_parm lps_param = {
55 		.macid = mac_id,
56 		.psmode = RTW89_MAC_AX_PS_MODE_LEGACY,
57 		.lastrpwm = RTW89_LAST_RPWM_PS,
58 	};
59 
60 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL);
61 	rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
62 }
63 
64 static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id)
65 {
66 	struct rtw89_lps_parm lps_param = {
67 		.macid = mac_id,
68 		.psmode = RTW89_MAC_AX_PS_MODE_ACTIVE,
69 		.lastrpwm = RTW89_LAST_RPWM_ACTIVE,
70 	};
71 
72 	rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
73 	rtw89_fw_leave_lps_check(rtwdev, 0);
74 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
75 }
76 
77 void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
78 {
79 	lockdep_assert_held(&rtwdev->mutex);
80 
81 	__rtw89_leave_ps_mode(rtwdev);
82 }
83 
84 void rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
85 {
86 	lockdep_assert_held(&rtwdev->mutex);
87 
88 	if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
89 		return;
90 
91 	__rtw89_enter_lps(rtwdev, mac_id);
92 	__rtw89_enter_ps_mode(rtwdev);
93 }
94 
95 static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
96 {
97 	if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
98 		return;
99 
100 	__rtw89_leave_ps_mode(rtwdev);
101 	__rtw89_leave_lps(rtwdev, rtwvif->mac_id);
102 }
103 
104 void rtw89_leave_lps(struct rtw89_dev *rtwdev)
105 {
106 	struct rtw89_vif *rtwvif;
107 
108 	lockdep_assert_held(&rtwdev->mutex);
109 
110 	if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
111 		return;
112 
113 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
114 		rtw89_leave_lps_vif(rtwdev, rtwvif);
115 }
116 
117 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
118 {
119 	struct rtw89_vif *rtwvif;
120 
121 	set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
122 
123 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
124 		rtw89_mac_vif_deinit(rtwdev, rtwvif);
125 
126 	rtw89_core_stop(rtwdev);
127 }
128 
129 void rtw89_leave_ips(struct rtw89_dev *rtwdev)
130 {
131 	struct rtw89_vif *rtwvif;
132 	int ret;
133 
134 	ret = rtw89_core_start(rtwdev);
135 	if (ret)
136 		rtw89_err(rtwdev, "failed to leave idle state\n");
137 
138 	rtw89_set_channel(rtwdev);
139 
140 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
141 		rtw89_mac_vif_init(rtwdev, rtwvif);
142 
143 	clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
144 }
145 
146 void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl)
147 {
148 	if (btc_ctrl)
149 		rtw89_leave_lps(rtwdev);
150 }
151