Commit | Line | Data |
---|---|---|
e3037485 YHC |
1 | // SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause |
2 | /* Copyright(c) 2018-2019 Realtek Corporation | |
3 | */ | |
4 | ||
5 | #include "main.h" | |
27e117e4 | 6 | #include "reg.h" |
e3037485 YHC |
7 | #include "fw.h" |
8 | #include "ps.h" | |
9 | #include "mac.h" | |
4136214f | 10 | #include "coex.h" |
e3037485 YHC |
11 | #include "debug.h" |
12 | ||
13 | static 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); | |
3c519605 | 22 | clear_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags); |
e3037485 YHC |
23 | |
24 | return ret; | |
25 | } | |
26 | ||
27 | int rtw_enter_ips(struct rtw_dev *rtwdev) | |
28 | { | |
3c519605 | 29 | set_bit(RTW_FLAG_INACTIVE_PS, rtwdev->flags); |
e3037485 | 30 | |
4136214f YHC |
31 | rtw_coex_ips_notify(rtwdev, COEX_IPS_ENTER); |
32 | ||
e3037485 YHC |
33 | rtw_core_stop(rtwdev); |
34 | ||
35 | return 0; | |
36 | } | |
37 | ||
38 | static void rtw_restore_port_cfg_iter(void *data, u8 *mac, | |
39 | struct ieee80211_vif *vif) | |
40 | { | |
41 | struct rtw_dev *rtwdev = data; | |
42 | struct rtw_vif *rtwvif = (struct rtw_vif *)vif->drv_priv; | |
43 | u32 config = ~0; | |
44 | ||
45 | rtw_vif_port_config(rtwdev, rtwvif, config); | |
46 | } | |
47 | ||
48 | int rtw_leave_ips(struct rtw_dev *rtwdev) | |
49 | { | |
50 | int ret; | |
51 | ||
52 | ret = rtw_ips_pwr_up(rtwdev); | |
53 | if (ret) { | |
54 | rtw_err(rtwdev, "failed to leave ips state\n"); | |
55 | return ret; | |
56 | } | |
57 | ||
58 | rtw_iterate_vifs_atomic(rtwdev, rtw_restore_port_cfg_iter, rtwdev); | |
59 | ||
4136214f YHC |
60 | rtw_coex_ips_notify(rtwdev, COEX_IPS_LEAVE); |
61 | ||
e3037485 YHC |
62 | return 0; |
63 | } | |
64 | ||
27e117e4 YHC |
65 | void rtw_power_mode_change(struct rtw_dev *rtwdev, bool enter) |
66 | { | |
67 | u8 request, confirm, polling; | |
68 | u8 polling_cnt; | |
69 | u8 retry_cnt = 0; | |
70 | ||
71 | retry: | |
72 | request = rtw_read8(rtwdev, rtwdev->hci.rpwm_addr); | |
73 | confirm = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr); | |
74 | ||
75 | /* toggle to request power mode, others remain 0 */ | |
76 | request ^= request | BIT_RPWM_TOGGLE; | |
77 | if (!enter) | |
78 | request |= POWER_MODE_ACK; | |
79 | else | |
80 | request |= POWER_MODE_LCLK; | |
81 | ||
82 | rtw_write8(rtwdev, rtwdev->hci.rpwm_addr, request); | |
83 | ||
84 | /* check confirm power mode has left power save state */ | |
85 | if (!enter) { | |
86 | for (polling_cnt = 0; polling_cnt < 3; polling_cnt++) { | |
87 | polling = rtw_read8(rtwdev, rtwdev->hci.cpwm_addr); | |
88 | if ((polling ^ confirm) & BIT_RPWM_TOGGLE) | |
89 | return; | |
90 | mdelay(20); | |
91 | } | |
92 | ||
93 | /* in case of fw/hw missed the request, retry 3 times */ | |
94 | if (retry_cnt < 3) { | |
95 | rtw_warn(rtwdev, "failed to leave deep PS, retry=%d\n", | |
96 | retry_cnt); | |
97 | retry_cnt++; | |
98 | goto retry; | |
99 | } | |
100 | ||
101 | /* Hit here means that driver failed to change hardware | |
102 | * power mode to active state after retry 3 times. | |
103 | * If the power state is locked at Deep sleep, most of | |
104 | * the hardware circuits is not working, even register | |
105 | * read/write. It should be treated as fatal error and | |
106 | * requires an entire analysis about the firmware/hardware | |
107 | */ | |
108 | WARN_ON("Hardware power state locked\n"); | |
109 | } | |
110 | } | |
111 | EXPORT_SYMBOL(rtw_power_mode_change); | |
112 | ||
113 | static void __rtw_leave_lps_deep(struct rtw_dev *rtwdev) | |
114 | { | |
115 | rtw_hci_deep_ps(rtwdev, false); | |
116 | } | |
117 | ||
e3037485 YHC |
118 | static void rtw_leave_lps_core(struct rtw_dev *rtwdev) |
119 | { | |
120 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; | |
121 | ||
122 | conf->state = RTW_ALL_ON; | |
123 | conf->awake_interval = 1; | |
124 | conf->rlbm = 0; | |
125 | conf->smart_ps = 0; | |
126 | ||
127 | rtw_fw_set_pwr_mode(rtwdev); | |
3c519605 | 128 | clear_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags); |
4136214f YHC |
129 | |
130 | rtw_coex_lps_notify(rtwdev, COEX_LPS_DISABLE); | |
e3037485 YHC |
131 | } |
132 | ||
27e117e4 YHC |
133 | static void __rtw_enter_lps_deep(struct rtw_dev *rtwdev) |
134 | { | |
d3be4d11 YHC |
135 | if (rtwdev->lps_conf.deep_mode == LPS_DEEP_MODE_NONE) |
136 | return; | |
137 | ||
27e117e4 YHC |
138 | if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) { |
139 | rtw_dbg(rtwdev, RTW_DBG_PS, | |
140 | "Should enter LPS before entering deep PS\n"); | |
141 | return; | |
142 | } | |
143 | ||
144 | rtw_hci_deep_ps(rtwdev, true); | |
145 | } | |
146 | ||
e3037485 YHC |
147 | static void rtw_enter_lps_core(struct rtw_dev *rtwdev) |
148 | { | |
149 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; | |
150 | ||
151 | conf->state = RTW_RF_OFF; | |
152 | conf->awake_interval = 1; | |
153 | conf->rlbm = 1; | |
154 | conf->smart_ps = 2; | |
155 | ||
4136214f YHC |
156 | rtw_coex_lps_notify(rtwdev, COEX_LPS_ENABLE); |
157 | ||
e3037485 | 158 | rtw_fw_set_pwr_mode(rtwdev); |
3c519605 | 159 | set_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags); |
e3037485 YHC |
160 | } |
161 | ||
27e117e4 | 162 | static void __rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) |
e3037485 YHC |
163 | { |
164 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; | |
165 | ||
3d391c06 | 166 | if (test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) |
e3037485 YHC |
167 | return; |
168 | ||
169 | conf->mode = RTW_MODE_LPS; | |
3d391c06 | 170 | conf->port_id = port_id; |
e3037485 YHC |
171 | |
172 | rtw_enter_lps_core(rtwdev); | |
173 | } | |
174 | ||
27e117e4 | 175 | static void __rtw_leave_lps(struct rtw_dev *rtwdev) |
e3037485 YHC |
176 | { |
177 | struct rtw_lps_conf *conf = &rtwdev->lps_conf; | |
178 | ||
27e117e4 YHC |
179 | if (test_and_clear_bit(RTW_FLAG_LEISURE_PS_DEEP, rtwdev->flags)) { |
180 | rtw_dbg(rtwdev, RTW_DBG_PS, | |
181 | "Should leave deep PS before leaving LPS\n"); | |
182 | __rtw_leave_lps_deep(rtwdev); | |
183 | } | |
d3e20fd1 | 184 | |
3d391c06 | 185 | if (!test_bit(RTW_FLAG_LEISURE_PS, rtwdev->flags)) |
e3037485 YHC |
186 | return; |
187 | ||
188 | conf->mode = RTW_MODE_ACTIVE; | |
e3037485 YHC |
189 | |
190 | rtw_leave_lps_core(rtwdev); | |
191 | } | |
27e117e4 YHC |
192 | |
193 | void rtw_enter_lps(struct rtw_dev *rtwdev, u8 port_id) | |
194 | { | |
195 | lockdep_assert_held(&rtwdev->mutex); | |
196 | ||
3a068a2a YHC |
197 | if (rtwdev->coex.stat.wl_force_lps_ctrl) |
198 | return; | |
199 | ||
27e117e4 YHC |
200 | __rtw_enter_lps(rtwdev, port_id); |
201 | __rtw_enter_lps_deep(rtwdev); | |
202 | } | |
203 | ||
204 | void rtw_leave_lps(struct rtw_dev *rtwdev) | |
205 | { | |
206 | lockdep_assert_held(&rtwdev->mutex); | |
207 | ||
208 | __rtw_leave_lps_deep(rtwdev); | |
209 | __rtw_leave_lps(rtwdev); | |
210 | } | |
211 | ||
212 | void rtw_leave_lps_deep(struct rtw_dev *rtwdev) | |
213 | { | |
214 | lockdep_assert_held(&rtwdev->mutex); | |
215 | ||
216 | __rtw_leave_lps_deep(rtwdev); | |
217 | } |