1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17#include <linux/export.h>
18#include "ath9k.h"
19#include "reg.h"
20#include "hw-ops.h"
21
22const char *ath9k_hw_wow_event_to_string(u32 wow_event)
23{
24 if (wow_event & AH_WOW_MAGIC_PATTERN_EN)
25 return "Magic pattern";
26 if (wow_event & AH_WOW_USER_PATTERN_EN)
27 return "User pattern";
28 if (wow_event & AH_WOW_LINK_CHANGE)
29 return "Link change";
30 if (wow_event & AH_WOW_BEACON_MISS)
31 return "Beacon miss";
32
33 return "unknown reason";
34}
35EXPORT_SYMBOL(ath9k_hw_wow_event_to_string);
36
37static void ath9k_hw_set_powermode_wow_sleep(struct ath_hw *ah)
38{
39 struct ath_common *common = ath9k_hw_common(ah);
40
41 REG_SET_BIT(ah, AR_STA_ID1, AR_STA_ID1_PWR_SAV);
42
43
44 REG_WRITE(ah, AR_CR, AR_CR_RXD);
45
46 if (!ath9k_hw_wait(ah, AR_CR, AR_CR_RXE, 0, AH_WAIT_TIMEOUT)) {
47 ath_err(common, "Failed to stop Rx DMA in 10ms AR_CR=0x%08x AR_DIAG_SW=0x%08x\n",
48 REG_READ(ah, AR_CR), REG_READ(ah, AR_DIAG_SW));
49 return;
50 }
51
52 REG_WRITE(ah, AR_RTC_FORCE_WAKE, AR_RTC_FORCE_WAKE_ON_INT);
53}
54
55static void ath9k_wow_create_keep_alive_pattern(struct ath_hw *ah)
56{
57 struct ath_common *common = ath9k_hw_common(ah);
58 u8 sta_mac_addr[ETH_ALEN], ap_mac_addr[ETH_ALEN];
59 u32 ctl[13] = {0};
60 u32 data_word[KAL_NUM_DATA_WORDS];
61 u8 i;
62 u32 wow_ka_data_word0;
63
64 memcpy(sta_mac_addr, common->macaddr, ETH_ALEN);
65 memcpy(ap_mac_addr, common->curbssid, ETH_ALEN);
66
67
68 ctl[0] = (KAL_FRAME_LEN | (MAX_RATE_POWER << 16));
69 ctl[1] = 0;
70 ctl[3] = 0xb;
71 ctl[4] = 0;
72 ctl[7] = (ah->txchainmask) << 2;
73 ctl[2] = 0xf << 16;
74
75 for (i = 0; i < KAL_NUM_DESC_WORDS; i++)
76 REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
77
78 REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + i * 4), ctl[i]);
79
80 data_word[0] = (KAL_FRAME_TYPE << 2) | (KAL_FRAME_SUB_TYPE << 4) |
81 (KAL_TO_DS << 8) | (KAL_DURATION_ID << 16);
82 data_word[1] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
83 (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
84 data_word[2] = (sta_mac_addr[1] << 24) | (sta_mac_addr[0] << 16) |
85 (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
86 data_word[3] = (sta_mac_addr[5] << 24) | (sta_mac_addr[4] << 16) |
87 (sta_mac_addr[3] << 8) | (sta_mac_addr[2]);
88 data_word[4] = (ap_mac_addr[3] << 24) | (ap_mac_addr[2] << 16) |
89 (ap_mac_addr[1] << 8) | (ap_mac_addr[0]);
90 data_word[5] = (ap_mac_addr[5] << 8) | (ap_mac_addr[4]);
91
92 if (AR_SREV_9462_20(ah)) {
93
94
95 REG_WRITE(ah, (AR_WOW_KA_DESC_WORD2 + (12 * 4)), 0);
96 wow_ka_data_word0 = AR_WOW_TXBUF(13);
97 } else {
98 wow_ka_data_word0 = AR_WOW_TXBUF(12);
99 }
100
101 for (i = 0; i < KAL_NUM_DATA_WORDS; i++)
102 REG_WRITE(ah, (wow_ka_data_word0 + i*4), data_word[i]);
103
104}
105
106void ath9k_hw_wow_apply_pattern(struct ath_hw *ah, u8 *user_pattern,
107 u8 *user_mask, int pattern_count,
108 int pattern_len)
109{
110 int i;
111 u32 pattern_val, mask_val;
112 u32 set, clr;
113
114
115 if (pattern_count >= MAX_NUM_PATTERN)
116 return;
117
118 REG_SET_BIT(ah, AR_WOW_PATTERN, BIT(pattern_count));
119
120
121 for (i = 0; i < MAX_PATTERN_SIZE; i += 4) {
122 memcpy(&pattern_val, user_pattern, 4);
123 REG_WRITE(ah, (AR_WOW_TB_PATTERN(pattern_count) + i),
124 pattern_val);
125 user_pattern += 4;
126 }
127
128
129 for (i = 0; i < MAX_PATTERN_MASK_SIZE; i += 4) {
130 memcpy(&mask_val, user_mask, 4);
131 REG_WRITE(ah, (AR_WOW_TB_MASK(pattern_count) + i), mask_val);
132 user_mask += 4;
133 }
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155 ah->wow_event_mask |= BIT(pattern_count + AR_WOW_PAT_FOUND_SHIFT);
156
157 if (pattern_count < 4) {
158
159 set = (pattern_len & AR_WOW_LENGTH_MAX) <<
160 AR_WOW_LEN1_SHIFT(pattern_count);
161 clr = AR_WOW_LENGTH1_MASK(pattern_count);
162 REG_RMW(ah, AR_WOW_LENGTH1, set, clr);
163 } else {
164
165 set = (pattern_len & AR_WOW_LENGTH_MAX) <<
166 AR_WOW_LEN2_SHIFT(pattern_count);
167 clr = AR_WOW_LENGTH2_MASK(pattern_count);
168 REG_RMW(ah, AR_WOW_LENGTH2, set, clr);
169 }
170
171}
172EXPORT_SYMBOL(ath9k_hw_wow_apply_pattern);
173
174u32 ath9k_hw_wow_wakeup(struct ath_hw *ah)
175{
176 u32 wow_status = 0;
177 u32 val = 0, rval;
178
179
180
181
182
183 rval = REG_READ(ah, AR_WOW_PATTERN);
184 val = AR_WOW_STATUS(rval);
185
186
187
188
189
190
191
192 val &= ah->wow_event_mask;
193
194 if (val) {
195 if (val & AR_WOW_MAGIC_PAT_FOUND)
196 wow_status |= AH_WOW_MAGIC_PATTERN_EN;
197 if (AR_WOW_PATTERN_FOUND(val))
198 wow_status |= AH_WOW_USER_PATTERN_EN;
199 if (val & AR_WOW_KEEP_ALIVE_FAIL)
200 wow_status |= AH_WOW_LINK_CHANGE;
201 if (val & AR_WOW_BEACON_FAIL)
202 wow_status |= AH_WOW_BEACON_MISS;
203 }
204
205
206
207
208
209
210
211
212 REG_RMW(ah, AR_PCIE_PM_CTRL, AR_PMCTRL_WOW_PME_CLR,
213 AR_PMCTRL_PWR_STATE_D1D3);
214
215
216
217
218 REG_WRITE(ah, AR_WOW_PATTERN,
219 AR_WOW_CLEAR_EVENTS(REG_READ(ah, AR_WOW_PATTERN)));
220
221
222
223
224 REG_WRITE(ah, AR_RSSI_THR, INIT_RSSI_THR);
225
226
227
228
229
230
231
232
233 if (ah->is_pciexpress)
234 ath9k_hw_configpcipowersave(ah, false);
235
236 ah->wow_event_mask = 0;
237
238 return wow_status;
239}
240EXPORT_SYMBOL(ath9k_hw_wow_wakeup);
241
242void ath9k_hw_wow_enable(struct ath_hw *ah, u32 pattern_enable)
243{
244 u32 wow_event_mask;
245 u32 set, clr;
246
247
248
249
250
251
252
253 wow_event_mask = ah->wow_event_mask;
254
255
256
257
258
259
260 if (ah->is_pciexpress) {
261
262
263
264
265
266 set = AR_WA_RESET_EN | AR_WA_POR_SHORT;
267 clr = AR_WA_UNTIE_RESET_EN | AR_WA_D3_L1_DISABLE;
268 REG_RMW(ah, AR_WA, set, clr);
269 }
270
271
272
273
274 set = AR_PMCTRL_HOST_PME_EN | AR_PMCTRL_PWR_PM_CTRL_ENA |
275 AR_PMCTRL_AUX_PWR_DET | AR_PMCTRL_WOW_PME_CLR;
276
277
278
279
280
281 REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
282 clr = AR_PMCTRL_WOW_PME_CLR;
283 REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
284
285
286
287
288
289
290
291
292
293
294
295
296
297 set = AR_WOW_BACK_OFF_SHIFT(AR_WOW_PAT_BACKOFF);
298 REG_SET_BIT(ah, AR_WOW_PATTERN, set);
299
300 set = AR_WOW_AIFS_CNT(AR_WOW_CNT_AIFS_CNT) |
301 AR_WOW_SLOT_CNT(AR_WOW_CNT_SLOT_CNT) |
302 AR_WOW_KEEP_ALIVE_CNT(AR_WOW_CNT_KA_CNT);
303 REG_SET_BIT(ah, AR_WOW_COUNT, set);
304
305 if (pattern_enable & AH_WOW_BEACON_MISS)
306 set = AR_WOW_BEACON_TIMO;
307
308 else
309 set = AR_WOW_BEACON_TIMO_MAX;
310
311 REG_WRITE(ah, AR_WOW_BCN_TIMO, set);
312
313
314
315
316 if (!pattern_enable)
317 set = AR_WOW_KEEP_ALIVE_NEVER;
318 else
319 set = KAL_TIMEOUT * 32;
320
321 REG_WRITE(ah, AR_WOW_KEEP_ALIVE_TIMO, set);
322
323
324
325
326
327 set = KAL_DELAY * 1000;
328 REG_WRITE(ah, AR_WOW_KEEP_ALIVE_DELAY, set);
329
330
331
332
333 ath9k_wow_create_keep_alive_pattern(ah);
334
335
336
337
338 set = 0;
339
340 clr = AR_WOW_KEEP_ALIVE_AUTO_DIS;
341
342 if (pattern_enable & AH_WOW_LINK_CHANGE)
343 wow_event_mask |= AR_WOW_KEEP_ALIVE_FAIL;
344 else
345 set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
346
347 set = AR_WOW_KEEP_ALIVE_FAIL_DIS;
348 REG_RMW(ah, AR_WOW_KEEP_ALIVE, set, clr);
349
350
351
352
353
354 REG_RMW_FIELD(ah, AR_RSSI_THR, AR_RSSI_THR_BM_THR,
355 AR_WOW_BMISSTHRESHOLD);
356
357 set = 0;
358 clr = 0;
359
360 if (pattern_enable & AH_WOW_BEACON_MISS) {
361 set = AR_WOW_BEACON_FAIL_EN;
362 wow_event_mask |= AR_WOW_BEACON_FAIL;
363 } else {
364 clr = AR_WOW_BEACON_FAIL_EN;
365 }
366
367 REG_RMW(ah, AR_WOW_BCN_EN, set, clr);
368
369 set = 0;
370 clr = 0;
371
372
373
374 if (pattern_enable & AH_WOW_MAGIC_PATTERN_EN) {
375 set = AR_WOW_MAGIC_EN;
376 wow_event_mask |= AR_WOW_MAGIC_PAT_FOUND;
377 } else {
378 clr = AR_WOW_MAGIC_EN;
379 }
380 set |= AR_WOW_MAC_INTR_EN;
381 REG_RMW(ah, AR_WOW_PATTERN, set, clr);
382
383 REG_WRITE(ah, AR_WOW_PATTERN_MATCH_LT_256B,
384 AR_WOW_PATTERN_SUPPORTED);
385
386
387
388
389 clr = 0;
390 set = AR_PMCTRL_PWR_STATE_D1D3 | AR_PMCTRL_HOST_PME_EN |
391 AR_PMCTRL_PWR_PM_CTRL_ENA;
392
393 clr = AR_PCIE_PM_CTRL_ENA;
394 REG_RMW(ah, AR_PCIE_PM_CTRL, set, clr);
395
396
397
398
399
400
401
402
403
404 clr = AR_PMCTRL_PWR_STATE_D1D3;
405 REG_CLR_BIT(ah, AR_PCIE_PM_CTRL, clr);
406
407 set = AR_PMCTRL_PWR_STATE_D1D3_REAL;
408 REG_SET_BIT(ah, AR_PCIE_PM_CTRL, set);
409
410 REG_CLR_BIT(ah, AR_STA_ID1, AR_STA_ID1_PRESERVE_SEQNUM);
411
412
413 set = BIT(13);
414 REG_SET_BIT(ah, AR_PCIE_PHY_REG3, set);
415
416 clr = BIT(5);
417 REG_CLR_BIT(ah, AR_PCU_MISC_MODE3, clr);
418
419 ath9k_hw_set_powermode_wow_sleep(ah);
420 ah->wow_event_mask = wow_event_mask;
421}
422EXPORT_SYMBOL(ath9k_hw_wow_enable);
423