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	u8 polling_cnt;
72	u8 retry_cnt = 0;
73
74	for (retry_cnt = 0; retry_cnt < 3; retry_cnt++) {
75		request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr);
76		confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);
77
78		/* toggle to request power mode, others remain 0 */
79		request ^= request | BIT_RPWM_TOGGLE;
80		if (!enter) {
81			request |= POWER_MODE_ACK;
82		} else {
83			request |= POWER_MODE_LCLK;
84			if (rtw_fw_lps_deep_mode == LPS_DEEP_MODE_PG)
85				request |= POWER_MODE_PG;
86		}
87
88		rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request);
89
90		if (enter)
91			return;
92
93		/* check confirm power mode has left power save state */
94		for (polling_cnt = 0; polling_cnt < 50; polling_cnt++) {
95			polling = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr);
96			if ((polling ^ confirm) & BIT_RPWM_TOGGLE)
97				return;
98			udelay(100);
99		}
100
101		/* in case of fw/hw missed the request, retry */
102		rtw_warn(rtwdev, "failed to leave deep PS, retry=%d\n",
103			 retry_cnt);
104	}
105
106	/* Hit here means that driver failed to change hardware power mode to
107	 * active state after retry 3 times. If the power state is locked at
108	 * Deep sleep, most of the hardware circuits is not working, even
109	 * register read/write. It should be treated as fatal error and
110	 * requires an entire analysis about the firmware/hardware
111	 */
112	WARN(1, "Hardware power state locked\n");
113}
114EXPORT_SYMBOL(rtw_power_mode_change);
115
116static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev)
117{
118	rtw_hci_deep_ps(rtwdev, false);
119}
120
121static void rtw_fw_leave_lps_state_check(struct rtw_dev *rtwdev)
122{
123	int i;
124
125	/* Driver needs to wait for firmware to leave LPS state
126	 * successfully. Firmware will send null packet to inform AP,
127	 * and see if AP sends an ACK back, then firmware will restore
128	 * the REG_TCR register.
129	 *
130	 * If driver does not wait for firmware, null packet with
131	 * PS bit could be sent due to incorrect REG_TCR setting.
132	 *
133	 * In our test, 100ms should be enough for firmware to finish
134	 * the flow. If REG_TCR Register is still incorrect after 100ms,
135	 * just modify it directly, and throw a warn message.
136	 */
137	for (i = 0 ; i < LEAVE_LPS_TRY_CNT; i++) {
138		if (rtw_read32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN) == 0)
139			return;
140		msleep(20);
141	}
142
143	rtw_write32_mask(rtwdev, REG_TCR, BIT_PWRMGT_HWDATA_EN, 0);
144	rtw_warn(rtwdev, "firmware failed to restore hardware setting\n");
145}
146
147static void rtw_leave_lps_core(struct rtw_dev *rtwdev)
148{
149	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
150
151	conf->state = RTW_ALL_ON;
152	conf->awake_interval = 1;
153	conf->rlbm = 0;
154	conf->smart_ps = 0;
155
156	rtw_hci_link_ps(rtwdev, false);
157	rtw_fw_set_pwr_mode(rtwdev);
158	rtw_fw_leave_lps_state_check(rtwdev);
159
160	clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
161
162	rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE);
163}
164
165static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev)
166{
167	if (rtwdev->lps_conf.deep_mode == LPS_DEEP_MODE_NONE)
168		return;
169
170	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) {
171		rtw_dbg(rtwdev, RTW_DBG_PS,
172			"Should enter LPS before entering deep PS\n");
173		return;
174	}
175
176	if (rtw_fw_lps_deep_mode == LPS_DEEP_MODE_PG)
177		rtw_fw_set_pg_info(rtwdev);
178
179	rtw_hci_deep_ps(rtwdev, true);
180}
181
182static void rtw_enter_lps_core(struct rtw_dev *rtwdev)
183{
184	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
185
186	conf->state = RTW_RF_OFF;
187	conf->awake_interval = 1;
188	conf->rlbm = 1;
189	conf->smart_ps = 2;
190
191	rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE);
192
193	rtw_fw_set_pwr_mode(rtwdev);
194	rtw_hci_link_ps(rtwdev, true);
195
196	set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags);
197}
198
199static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
200{
201	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
202
203	if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
204		return;
205
206	conf->mode = RTW_MODE_LPS;
207	conf->port_id = port_id;
208
209	rtw_enter_lps_core(rtwdev);
210}
211
212static void __rtw_leave_lps(struct rtw_dev *rtwdev)
213{
214	struct rtw_lps_conf *conf = &rtwdev->lps_conf;
215
216	if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) {
217		rtw_dbg(rtwdev, RTW_DBG_PS,
218			"Should leave deep PS before leaving LPS\n");
219		__rtw_leave_lps_deep(rtwdev);
220	}
221
222	if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags))
223		return;
224
225	conf->mode = RTW_MODE_ACTIVE;
226
227	rtw_leave_lps_core(rtwdev);
228}
229
230void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id)
231{
232	lockdep_assert_held(&rtwdev->mutex);
233
234	if (rtwdev->coex.stat.wl_force_lps_ctrl)
235		return;
236
237	__rtw_enter_lps(rtwdev, port_id);
238	__rtw_enter_lps_deep(rtwdev);
239}
240
241void rtw_leave_lps(struct rtw_dev *rtwdev)
242{
243	lockdep_assert_held(&rtwdev->mutex);
244
245	__rtw_leave_lps_deep(rtwdev);
246	__rtw_leave_lps(rtwdev);
247}
248
249void rtw_leave_lps_deep(struct rtw_dev *rtwdev)
250{
251	lockdep_assert_held(&rtwdev->mutex);
252
253	__rtw_leave_lps_deep(rtwdev);
254}
255