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_ps_power_mode_change_with_hci(struct rtw89_dev *rtwdev,
33 						bool enter)
34 {
35 	ieee80211_stop_queues(rtwdev->hw);
36 	rtwdev->hci.paused = true;
37 	flush_work(&rtwdev->txq_work);
38 	ieee80211_wake_queues(rtwdev->hw);
39 
40 	rtw89_hci_pause(rtwdev, true);
41 	rtw89_mac_power_mode_change(rtwdev, enter);
42 	rtw89_hci_switch_mode(rtwdev, enter);
43 	rtw89_hci_pause(rtwdev, false);
44 
45 	rtwdev->hci.paused = false;
46 
47 	if (!enter) {
48 		local_bh_disable();
49 		napi_schedule(&rtwdev->napi);
50 		local_bh_enable();
51 	}
52 }
53 
54 static void rtw89_ps_power_mode_change(struct rtw89_dev *rtwdev, bool enter)
55 {
56 	if (rtwdev->chip->low_power_hci_modes & BIT(rtwdev->ps_mode))
57 		rtw89_ps_power_mode_change_with_hci(rtwdev, enter);
58 	else
59 		rtw89_mac_power_mode_change(rtwdev, enter);
60 }
61 
62 static void __rtw89_enter_ps_mode(struct rtw89_dev *rtwdev)
63 {
64 	if (!rtwdev->ps_mode)
65 		return;
66 
67 	if (test_and_set_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
68 		return;
69 
70 	rtw89_ps_power_mode_change(rtwdev, true);
71 }
72 
73 void __rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
74 {
75 	if (!rtwdev->ps_mode)
76 		return;
77 
78 	if (test_and_clear_bit(RTW89_FLAG_LOW_POWER_MODE, rtwdev->flags))
79 		rtw89_ps_power_mode_change(rtwdev, false);
80 }
81 
82 static void __rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
83 {
84 	struct rtw89_lps_parm lps_param = {
85 		.macid = mac_id,
86 		.psmode = RTW89_MAC_AX_PS_MODE_LEGACY,
87 		.lastrpwm = RTW89_LAST_RPWM_PS,
88 	};
89 
90 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_FW_CTRL);
91 	rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
92 }
93 
94 static void __rtw89_leave_lps(struct rtw89_dev *rtwdev, u8 mac_id)
95 {
96 	struct rtw89_lps_parm lps_param = {
97 		.macid = mac_id,
98 		.psmode = RTW89_MAC_AX_PS_MODE_ACTIVE,
99 		.lastrpwm = RTW89_LAST_RPWM_ACTIVE,
100 	};
101 
102 	rtw89_fw_h2c_lps_parm(rtwdev, &lps_param);
103 	rtw89_fw_leave_lps_check(rtwdev, 0);
104 	rtw89_btc_ntfy_radio_state(rtwdev, BTC_RFCTRL_WL_ON);
105 }
106 
107 void rtw89_leave_ps_mode(struct rtw89_dev *rtwdev)
108 {
109 	lockdep_assert_held(&rtwdev->mutex);
110 
111 	__rtw89_leave_ps_mode(rtwdev);
112 }
113 
114 void rtw89_enter_lps(struct rtw89_dev *rtwdev, u8 mac_id)
115 {
116 	lockdep_assert_held(&rtwdev->mutex);
117 
118 	if (test_and_set_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
119 		return;
120 
121 	__rtw89_enter_lps(rtwdev, mac_id);
122 	__rtw89_enter_ps_mode(rtwdev);
123 }
124 
125 static void rtw89_leave_lps_vif(struct rtw89_dev *rtwdev, struct rtw89_vif *rtwvif)
126 {
127 	if (rtwvif->wifi_role != RTW89_WIFI_ROLE_STATION)
128 		return;
129 
130 	__rtw89_leave_ps_mode(rtwdev);
131 	__rtw89_leave_lps(rtwdev, rtwvif->mac_id);
132 }
133 
134 void rtw89_leave_lps(struct rtw89_dev *rtwdev)
135 {
136 	struct rtw89_vif *rtwvif;
137 
138 	lockdep_assert_held(&rtwdev->mutex);
139 
140 	if (!test_and_clear_bit(RTW89_FLAG_LEISURE_PS, rtwdev->flags))
141 		return;
142 
143 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
144 		rtw89_leave_lps_vif(rtwdev, rtwvif);
145 }
146 
147 void rtw89_enter_ips(struct rtw89_dev *rtwdev)
148 {
149 	struct rtw89_vif *rtwvif;
150 
151 	set_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
152 
153 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
154 		rtw89_mac_vif_deinit(rtwdev, rtwvif);
155 
156 	rtw89_core_stop(rtwdev);
157 }
158 
159 void rtw89_leave_ips(struct rtw89_dev *rtwdev)
160 {
161 	struct rtw89_vif *rtwvif;
162 	int ret;
163 
164 	ret = rtw89_core_start(rtwdev);
165 	if (ret)
166 		rtw89_err(rtwdev, "failed to leave idle state\n");
167 
168 	rtw89_set_channel(rtwdev);
169 
170 	rtw89_for_each_rtwvif(rtwdev, rtwvif)
171 		rtw89_mac_vif_init(rtwdev, rtwvif);
172 
173 	clear_bit(RTW89_FLAG_INACTIVE_PS, rtwdev->flags);
174 }
175 
176 void rtw89_set_coex_ctrl_lps(struct rtw89_dev *rtwdev, bool btc_ctrl)
177 {
178 	if (btc_ctrl)
179 		rtw89_leave_lps(rtwdev);
180 }
181