1 | /* |
2 | * Copyright (c) 2012 Qualcomm Atheros, Inc. |
3 | * |
4 | * Permission to use, copy, modify, and/or distribute this software for any |
5 | * purpose with or without fee is hereby granted, provided that the above |
6 | * copyright notice and this permission notice appear in all copies. |
7 | * |
8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
15 | */ |
16 | |
17 | #include "ath9k.h" |
18 | |
19 | /* |
20 | * AR9285 |
21 | * ====== |
22 | * |
23 | * EEPROM has 2 4-bit fields containing the card configuration. |
24 | * |
25 | * antdiv_ctl1: |
26 | * ------------ |
27 | * bb_enable_ant_div_lnadiv : 1 |
28 | * bb_ant_div_alt_gaintb : 1 |
29 | * bb_ant_div_main_gaintb : 1 |
30 | * bb_enable_ant_fast_div : 1 |
31 | * |
32 | * antdiv_ctl2: |
33 | * ----------- |
34 | * bb_ant_div_alt_lnaconf : 2 |
35 | * bb_ant_div_main_lnaconf : 2 |
36 | * |
37 | * The EEPROM bits are used as follows: |
38 | * ------------------------------------ |
39 | * |
40 | * bb_enable_ant_div_lnadiv - Enable LNA path rx antenna diversity/combining. |
41 | * Set in AR_PHY_MULTICHAIN_GAIN_CTL. |
42 | * |
43 | * bb_ant_div_[alt/main]_gaintb - 0 -> Antenna config Alt/Main uses gaintable 0 |
44 | * 1 -> Antenna config Alt/Main uses gaintable 1 |
45 | * Set in AR_PHY_MULTICHAIN_GAIN_CTL. |
46 | * |
47 | * bb_enable_ant_fast_div - Enable fast antenna diversity. |
48 | * Set in AR_PHY_CCK_DETECT. |
49 | * |
50 | * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config. |
51 | * Set in AR_PHY_MULTICHAIN_GAIN_CTL. |
52 | * 10=LNA1 |
53 | * 01=LNA2 |
54 | * 11=LNA1+LNA2 |
55 | * 00=LNA1-LNA2 |
56 | * |
57 | * AR9485 / AR9565 / AR9331 |
58 | * ======================== |
59 | * |
60 | * The same bits are present in the EEPROM, but the location in the |
61 | * EEPROM is different (ant_div_control in ar9300_BaseExtension_1). |
62 | * |
63 | * ant_div_alt_lnaconf ==> bit 0~1 |
64 | * ant_div_main_lnaconf ==> bit 2~3 |
65 | * ant_div_alt_gaintb ==> bit 4 |
66 | * ant_div_main_gaintb ==> bit 5 |
67 | * enable_ant_div_lnadiv ==> bit 6 |
68 | * enable_ant_fast_div ==> bit 7 |
69 | */ |
70 | |
71 | static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb, |
72 | int alt_ratio, int maxdelta, |
73 | int mindelta, int main_rssi_avg, |
74 | int , int pkt_count) |
75 | { |
76 | if (pkt_count <= 50) |
77 | return false; |
78 | |
79 | if (alt_rssi_avg > main_rssi_avg + mindelta) |
80 | return true; |
81 | |
82 | if (alt_ratio >= antcomb->ant_ratio2 && |
83 | alt_rssi_avg >= antcomb->low_rssi_thresh && |
84 | (alt_rssi_avg > main_rssi_avg + maxdelta)) |
85 | return true; |
86 | |
87 | return false; |
88 | } |
89 | |
90 | static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf, |
91 | struct ath_ant_comb *antcomb, |
92 | int alt_ratio, int , |
93 | int main_rssi_avg) |
94 | { |
95 | bool result, set1, set2; |
96 | |
97 | result = set1 = set2 = false; |
98 | |
99 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 && |
100 | conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1) |
101 | set1 = true; |
102 | |
103 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 && |
104 | conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
105 | set2 = true; |
106 | |
107 | switch (conf->div_group) { |
108 | case 0: |
109 | if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) |
110 | result = true; |
111 | break; |
112 | case 1: |
113 | case 2: |
114 | if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) |
115 | break; |
116 | |
117 | if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) || |
118 | (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) || |
119 | (alt_ratio > antcomb->ant_ratio)) |
120 | result = true; |
121 | |
122 | break; |
123 | case 3: |
124 | if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) |
125 | break; |
126 | |
127 | if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) || |
128 | (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) || |
129 | (alt_ratio > antcomb->ant_ratio)) |
130 | result = true; |
131 | |
132 | break; |
133 | } |
134 | |
135 | return result; |
136 | } |
137 | |
138 | static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, |
139 | struct ath_hw_antcomb_conf ant_conf, |
140 | int main_rssi_avg) |
141 | { |
142 | antcomb->quick_scan_cnt = 0; |
143 | |
144 | if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
145 | antcomb->rssi_lna2 = main_rssi_avg; |
146 | else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) |
147 | antcomb->rssi_lna1 = main_rssi_avg; |
148 | |
149 | switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { |
150 | case 0x10: /* LNA2 A-B */ |
151 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
152 | antcomb->first_quick_scan_conf = |
153 | ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
154 | antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; |
155 | break; |
156 | case 0x20: /* LNA1 A-B */ |
157 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
158 | antcomb->first_quick_scan_conf = |
159 | ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
160 | antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; |
161 | break; |
162 | case 0x21: /* LNA1 LNA2 */ |
163 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; |
164 | antcomb->first_quick_scan_conf = |
165 | ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
166 | antcomb->second_quick_scan_conf = |
167 | ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
168 | break; |
169 | case 0x12: /* LNA2 LNA1 */ |
170 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; |
171 | antcomb->first_quick_scan_conf = |
172 | ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
173 | antcomb->second_quick_scan_conf = |
174 | ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
175 | break; |
176 | case 0x13: /* LNA2 A+B */ |
177 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
178 | antcomb->first_quick_scan_conf = |
179 | ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
180 | antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; |
181 | break; |
182 | case 0x23: /* LNA1 A+B */ |
183 | antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
184 | antcomb->first_quick_scan_conf = |
185 | ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
186 | antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; |
187 | break; |
188 | default: |
189 | break; |
190 | } |
191 | } |
192 | |
193 | static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb, |
194 | struct ath_hw_antcomb_conf *conf) |
195 | { |
196 | /* set alt to the conf with maximun ratio */ |
197 | if (antcomb->first_ratio && antcomb->second_ratio) { |
198 | if (antcomb->rssi_second > antcomb->rssi_third) { |
199 | /* first alt*/ |
200 | if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || |
201 | (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) |
202 | /* Set alt LNA1 or LNA2*/ |
203 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
204 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
205 | else |
206 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
207 | else |
208 | /* Set alt to A+B or A-B */ |
209 | conf->alt_lna_conf = |
210 | antcomb->first_quick_scan_conf; |
211 | } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || |
212 | (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) { |
213 | /* Set alt LNA1 or LNA2 */ |
214 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
215 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
216 | else |
217 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
218 | } else { |
219 | /* Set alt to A+B or A-B */ |
220 | conf->alt_lna_conf = antcomb->second_quick_scan_conf; |
221 | } |
222 | } else if (antcomb->first_ratio) { |
223 | /* first alt */ |
224 | if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || |
225 | (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) |
226 | /* Set alt LNA1 or LNA2 */ |
227 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
228 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
229 | else |
230 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
231 | else |
232 | /* Set alt to A+B or A-B */ |
233 | conf->alt_lna_conf = antcomb->first_quick_scan_conf; |
234 | } else if (antcomb->second_ratio) { |
235 | /* second alt */ |
236 | if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || |
237 | (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) |
238 | /* Set alt LNA1 or LNA2 */ |
239 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
240 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
241 | else |
242 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
243 | else |
244 | /* Set alt to A+B or A-B */ |
245 | conf->alt_lna_conf = antcomb->second_quick_scan_conf; |
246 | } else { |
247 | /* main is largest */ |
248 | if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || |
249 | (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) |
250 | /* Set alt LNA1 or LNA2 */ |
251 | if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) |
252 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
253 | else |
254 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
255 | else |
256 | /* Set alt to A+B or A-B */ |
257 | conf->alt_lna_conf = antcomb->main_conf; |
258 | } |
259 | } |
260 | |
261 | static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, |
262 | struct ath_hw_antcomb_conf *div_ant_conf, |
263 | int main_rssi_avg, int , |
264 | int alt_ratio) |
265 | { |
266 | /* alt_good */ |
267 | switch (antcomb->quick_scan_cnt) { |
268 | case 0: |
269 | /* set alt to main, and alt to first conf */ |
270 | div_ant_conf->main_lna_conf = antcomb->main_conf; |
271 | div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; |
272 | break; |
273 | case 1: |
274 | /* set alt to main, and alt to first conf */ |
275 | div_ant_conf->main_lna_conf = antcomb->main_conf; |
276 | div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; |
277 | antcomb->rssi_first = main_rssi_avg; |
278 | antcomb->rssi_second = alt_rssi_avg; |
279 | |
280 | if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { |
281 | /* main is LNA1 */ |
282 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
283 | ATH_ANT_DIV_COMB_LNA1_DELTA_HI, |
284 | ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, |
285 | main_rssi_avg, alt_rssi_avg, |
286 | pkt_count: antcomb->total_pkt_count)) |
287 | antcomb->first_ratio = true; |
288 | else |
289 | antcomb->first_ratio = false; |
290 | } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { |
291 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
292 | ATH_ANT_DIV_COMB_LNA1_DELTA_MID, |
293 | ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, |
294 | main_rssi_avg, alt_rssi_avg, |
295 | pkt_count: antcomb->total_pkt_count)) |
296 | antcomb->first_ratio = true; |
297 | else |
298 | antcomb->first_ratio = false; |
299 | } else { |
300 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
301 | ATH_ANT_DIV_COMB_LNA1_DELTA_HI, |
302 | mindelta: 0, |
303 | main_rssi_avg, alt_rssi_avg, |
304 | pkt_count: antcomb->total_pkt_count)) |
305 | antcomb->first_ratio = true; |
306 | else |
307 | antcomb->first_ratio = false; |
308 | } |
309 | break; |
310 | case 2: |
311 | antcomb->alt_good = false; |
312 | antcomb->scan_not_start = false; |
313 | antcomb->scan = false; |
314 | antcomb->rssi_first = main_rssi_avg; |
315 | antcomb->rssi_third = alt_rssi_avg; |
316 | |
317 | switch(antcomb->second_quick_scan_conf) { |
318 | case ATH_ANT_DIV_COMB_LNA1: |
319 | antcomb->rssi_lna1 = alt_rssi_avg; |
320 | break; |
321 | case ATH_ANT_DIV_COMB_LNA2: |
322 | antcomb->rssi_lna2 = alt_rssi_avg; |
323 | break; |
324 | case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: |
325 | if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) |
326 | antcomb->rssi_lna2 = main_rssi_avg; |
327 | else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) |
328 | antcomb->rssi_lna1 = main_rssi_avg; |
329 | break; |
330 | default: |
331 | break; |
332 | } |
333 | |
334 | if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + |
335 | div_ant_conf->lna1_lna2_switch_delta) |
336 | div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
337 | else |
338 | div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
339 | |
340 | if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { |
341 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
342 | ATH_ANT_DIV_COMB_LNA1_DELTA_HI, |
343 | ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, |
344 | main_rssi_avg, alt_rssi_avg, |
345 | pkt_count: antcomb->total_pkt_count)) |
346 | antcomb->second_ratio = true; |
347 | else |
348 | antcomb->second_ratio = false; |
349 | } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { |
350 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
351 | ATH_ANT_DIV_COMB_LNA1_DELTA_MID, |
352 | ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, |
353 | main_rssi_avg, alt_rssi_avg, |
354 | pkt_count: antcomb->total_pkt_count)) |
355 | antcomb->second_ratio = true; |
356 | else |
357 | antcomb->second_ratio = false; |
358 | } else { |
359 | if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, |
360 | ATH_ANT_DIV_COMB_LNA1_DELTA_HI, |
361 | mindelta: 0, |
362 | main_rssi_avg, alt_rssi_avg, |
363 | pkt_count: antcomb->total_pkt_count)) |
364 | antcomb->second_ratio = true; |
365 | else |
366 | antcomb->second_ratio = false; |
367 | } |
368 | |
369 | ath_ant_set_alt_ratio(antcomb, conf: div_ant_conf); |
370 | |
371 | break; |
372 | default: |
373 | break; |
374 | } |
375 | } |
376 | |
377 | static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, |
378 | struct ath_ant_comb *antcomb, |
379 | int alt_ratio) |
380 | { |
381 | ant_conf->main_gaintb = 0; |
382 | ant_conf->alt_gaintb = 0; |
383 | |
384 | if (ant_conf->div_group == 0) { |
385 | /* Adjust the fast_div_bias based on main and alt lna conf */ |
386 | switch ((ant_conf->main_lna_conf << 4) | |
387 | ant_conf->alt_lna_conf) { |
388 | case 0x01: /* A-B LNA2 */ |
389 | ant_conf->fast_div_bias = 0x3b; |
390 | break; |
391 | case 0x02: /* A-B LNA1 */ |
392 | ant_conf->fast_div_bias = 0x3d; |
393 | break; |
394 | case 0x03: /* A-B A+B */ |
395 | ant_conf->fast_div_bias = 0x1; |
396 | break; |
397 | case 0x10: /* LNA2 A-B */ |
398 | ant_conf->fast_div_bias = 0x7; |
399 | break; |
400 | case 0x12: /* LNA2 LNA1 */ |
401 | ant_conf->fast_div_bias = 0x2; |
402 | break; |
403 | case 0x13: /* LNA2 A+B */ |
404 | ant_conf->fast_div_bias = 0x7; |
405 | break; |
406 | case 0x20: /* LNA1 A-B */ |
407 | ant_conf->fast_div_bias = 0x6; |
408 | break; |
409 | case 0x21: /* LNA1 LNA2 */ |
410 | ant_conf->fast_div_bias = 0x0; |
411 | break; |
412 | case 0x23: /* LNA1 A+B */ |
413 | ant_conf->fast_div_bias = 0x6; |
414 | break; |
415 | case 0x30: /* A+B A-B */ |
416 | ant_conf->fast_div_bias = 0x1; |
417 | break; |
418 | case 0x31: /* A+B LNA2 */ |
419 | ant_conf->fast_div_bias = 0x3b; |
420 | break; |
421 | case 0x32: /* A+B LNA1 */ |
422 | ant_conf->fast_div_bias = 0x3d; |
423 | break; |
424 | default: |
425 | break; |
426 | } |
427 | } else if (ant_conf->div_group == 1) { |
428 | /* Adjust the fast_div_bias based on main and alt_lna_conf */ |
429 | switch ((ant_conf->main_lna_conf << 4) | |
430 | ant_conf->alt_lna_conf) { |
431 | case 0x01: /* A-B LNA2 */ |
432 | ant_conf->fast_div_bias = 0x1; |
433 | break; |
434 | case 0x02: /* A-B LNA1 */ |
435 | ant_conf->fast_div_bias = 0x1; |
436 | break; |
437 | case 0x03: /* A-B A+B */ |
438 | ant_conf->fast_div_bias = 0x1; |
439 | break; |
440 | case 0x10: /* LNA2 A-B */ |
441 | if (!(antcomb->scan) && |
442 | (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) |
443 | ant_conf->fast_div_bias = 0x3f; |
444 | else |
445 | ant_conf->fast_div_bias = 0x1; |
446 | break; |
447 | case 0x12: /* LNA2 LNA1 */ |
448 | ant_conf->fast_div_bias = 0x1; |
449 | break; |
450 | case 0x13: /* LNA2 A+B */ |
451 | if (!(antcomb->scan) && |
452 | (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) |
453 | ant_conf->fast_div_bias = 0x3f; |
454 | else |
455 | ant_conf->fast_div_bias = 0x1; |
456 | break; |
457 | case 0x20: /* LNA1 A-B */ |
458 | if (!(antcomb->scan) && |
459 | (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) |
460 | ant_conf->fast_div_bias = 0x3f; |
461 | else |
462 | ant_conf->fast_div_bias = 0x1; |
463 | break; |
464 | case 0x21: /* LNA1 LNA2 */ |
465 | ant_conf->fast_div_bias = 0x1; |
466 | break; |
467 | case 0x23: /* LNA1 A+B */ |
468 | if (!(antcomb->scan) && |
469 | (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) |
470 | ant_conf->fast_div_bias = 0x3f; |
471 | else |
472 | ant_conf->fast_div_bias = 0x1; |
473 | break; |
474 | case 0x30: /* A+B A-B */ |
475 | ant_conf->fast_div_bias = 0x1; |
476 | break; |
477 | case 0x31: /* A+B LNA2 */ |
478 | ant_conf->fast_div_bias = 0x1; |
479 | break; |
480 | case 0x32: /* A+B LNA1 */ |
481 | ant_conf->fast_div_bias = 0x1; |
482 | break; |
483 | default: |
484 | break; |
485 | } |
486 | } else if (ant_conf->div_group == 2) { |
487 | /* Adjust the fast_div_bias based on main and alt_lna_conf */ |
488 | switch ((ant_conf->main_lna_conf << 4) | |
489 | ant_conf->alt_lna_conf) { |
490 | case 0x01: /* A-B LNA2 */ |
491 | ant_conf->fast_div_bias = 0x1; |
492 | break; |
493 | case 0x02: /* A-B LNA1 */ |
494 | ant_conf->fast_div_bias = 0x1; |
495 | break; |
496 | case 0x03: /* A-B A+B */ |
497 | ant_conf->fast_div_bias = 0x1; |
498 | break; |
499 | case 0x10: /* LNA2 A-B */ |
500 | if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) |
501 | ant_conf->fast_div_bias = 0x1; |
502 | else |
503 | ant_conf->fast_div_bias = 0x2; |
504 | break; |
505 | case 0x12: /* LNA2 LNA1 */ |
506 | ant_conf->fast_div_bias = 0x1; |
507 | break; |
508 | case 0x13: /* LNA2 A+B */ |
509 | if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) |
510 | ant_conf->fast_div_bias = 0x1; |
511 | else |
512 | ant_conf->fast_div_bias = 0x2; |
513 | break; |
514 | case 0x20: /* LNA1 A-B */ |
515 | if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) |
516 | ant_conf->fast_div_bias = 0x1; |
517 | else |
518 | ant_conf->fast_div_bias = 0x2; |
519 | break; |
520 | case 0x21: /* LNA1 LNA2 */ |
521 | ant_conf->fast_div_bias = 0x1; |
522 | break; |
523 | case 0x23: /* LNA1 A+B */ |
524 | if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) |
525 | ant_conf->fast_div_bias = 0x1; |
526 | else |
527 | ant_conf->fast_div_bias = 0x2; |
528 | break; |
529 | case 0x30: /* A+B A-B */ |
530 | ant_conf->fast_div_bias = 0x1; |
531 | break; |
532 | case 0x31: /* A+B LNA2 */ |
533 | ant_conf->fast_div_bias = 0x1; |
534 | break; |
535 | case 0x32: /* A+B LNA1 */ |
536 | ant_conf->fast_div_bias = 0x1; |
537 | break; |
538 | default: |
539 | break; |
540 | } |
541 | |
542 | if (antcomb->fast_div_bias) |
543 | ant_conf->fast_div_bias = antcomb->fast_div_bias; |
544 | } else if (ant_conf->div_group == 3) { |
545 | switch ((ant_conf->main_lna_conf << 4) | |
546 | ant_conf->alt_lna_conf) { |
547 | case 0x01: /* A-B LNA2 */ |
548 | ant_conf->fast_div_bias = 0x1; |
549 | break; |
550 | case 0x02: /* A-B LNA1 */ |
551 | ant_conf->fast_div_bias = 0x39; |
552 | break; |
553 | case 0x03: /* A-B A+B */ |
554 | ant_conf->fast_div_bias = 0x1; |
555 | break; |
556 | case 0x10: /* LNA2 A-B */ |
557 | ant_conf->fast_div_bias = 0x2; |
558 | break; |
559 | case 0x12: /* LNA2 LNA1 */ |
560 | ant_conf->fast_div_bias = 0x3f; |
561 | break; |
562 | case 0x13: /* LNA2 A+B */ |
563 | ant_conf->fast_div_bias = 0x2; |
564 | break; |
565 | case 0x20: /* LNA1 A-B */ |
566 | ant_conf->fast_div_bias = 0x3; |
567 | break; |
568 | case 0x21: /* LNA1 LNA2 */ |
569 | ant_conf->fast_div_bias = 0x3; |
570 | break; |
571 | case 0x23: /* LNA1 A+B */ |
572 | ant_conf->fast_div_bias = 0x3; |
573 | break; |
574 | case 0x30: /* A+B A-B */ |
575 | ant_conf->fast_div_bias = 0x1; |
576 | break; |
577 | case 0x31: /* A+B LNA2 */ |
578 | ant_conf->fast_div_bias = 0x6; |
579 | break; |
580 | case 0x32: /* A+B LNA1 */ |
581 | ant_conf->fast_div_bias = 0x1; |
582 | break; |
583 | default: |
584 | break; |
585 | } |
586 | } |
587 | } |
588 | |
589 | static void ath_ant_try_scan(struct ath_ant_comb *antcomb, |
590 | struct ath_hw_antcomb_conf *conf, |
591 | int curr_alt_set, int , |
592 | int main_rssi_avg) |
593 | { |
594 | switch (curr_alt_set) { |
595 | case ATH_ANT_DIV_COMB_LNA2: |
596 | antcomb->rssi_lna2 = alt_rssi_avg; |
597 | antcomb->rssi_lna1 = main_rssi_avg; |
598 | antcomb->scan = true; |
599 | /* set to A+B */ |
600 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
601 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
602 | break; |
603 | case ATH_ANT_DIV_COMB_LNA1: |
604 | antcomb->rssi_lna1 = alt_rssi_avg; |
605 | antcomb->rssi_lna2 = main_rssi_avg; |
606 | antcomb->scan = true; |
607 | /* set to A+B */ |
608 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
609 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
610 | break; |
611 | case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: |
612 | antcomb->rssi_add = alt_rssi_avg; |
613 | antcomb->scan = true; |
614 | /* set to A-B */ |
615 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
616 | break; |
617 | case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: |
618 | antcomb->rssi_sub = alt_rssi_avg; |
619 | antcomb->scan = false; |
620 | if (antcomb->rssi_lna2 > |
621 | (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) { |
622 | /* use LNA2 as main LNA */ |
623 | if ((antcomb->rssi_add > antcomb->rssi_lna1) && |
624 | (antcomb->rssi_add > antcomb->rssi_sub)) { |
625 | /* set to A+B */ |
626 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
627 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
628 | } else if (antcomb->rssi_sub > |
629 | antcomb->rssi_lna1) { |
630 | /* set to A-B */ |
631 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
632 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
633 | } else { |
634 | /* set to LNA1 */ |
635 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
636 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
637 | } |
638 | } else { |
639 | /* use LNA1 as main LNA */ |
640 | if ((antcomb->rssi_add > antcomb->rssi_lna2) && |
641 | (antcomb->rssi_add > antcomb->rssi_sub)) { |
642 | /* set to A+B */ |
643 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
644 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; |
645 | } else if (antcomb->rssi_sub > |
646 | antcomb->rssi_lna2) { |
647 | /* set to A-B */ |
648 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
649 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; |
650 | } else { |
651 | /* set to LNA2 */ |
652 | conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
653 | conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
654 | } |
655 | } |
656 | break; |
657 | default: |
658 | break; |
659 | } |
660 | } |
661 | |
662 | static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf, |
663 | struct ath_ant_comb *antcomb, |
664 | int alt_ratio, int , |
665 | int main_rssi_avg, int curr_main_set, |
666 | int curr_alt_set) |
667 | { |
668 | bool ret = false; |
669 | |
670 | if (ath_ant_div_comb_alt_check(conf: div_ant_conf, antcomb, alt_ratio, |
671 | alt_rssi_avg, main_rssi_avg)) { |
672 | if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { |
673 | /* |
674 | * Switch main and alt LNA. |
675 | */ |
676 | div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
677 | div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
678 | } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { |
679 | div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
680 | div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
681 | } |
682 | |
683 | ret = true; |
684 | } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && |
685 | (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { |
686 | /* |
687 | Set alt to another LNA. |
688 | */ |
689 | if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) |
690 | div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; |
691 | else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) |
692 | div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; |
693 | |
694 | ret = true; |
695 | } |
696 | |
697 | return ret; |
698 | } |
699 | |
700 | static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb) |
701 | { |
702 | int alt_ratio; |
703 | |
704 | if (!antcomb->scan || !antcomb->alt_good) |
705 | return false; |
706 | |
707 | if (time_after(jiffies, antcomb->scan_start_time + |
708 | msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) |
709 | return true; |
710 | |
711 | if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { |
712 | alt_ratio = ((antcomb->alt_recv_cnt * 100) / |
713 | antcomb->total_pkt_count); |
714 | if (alt_ratio < antcomb->ant_ratio) |
715 | return true; |
716 | } |
717 | |
718 | return false; |
719 | } |
720 | |
721 | void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) |
722 | { |
723 | struct ath_hw_antcomb_conf div_ant_conf; |
724 | struct ath_ant_comb *antcomb = &sc->ant_comb; |
725 | int alt_ratio = 0, = 0, main_rssi_avg = 0, curr_alt_set; |
726 | int curr_main_set; |
727 | int main_rssi = rs->rs_rssi_ctl[0]; |
728 | int = rs->rs_rssi_ctl[1]; |
729 | int rx_ant_conf, main_ant_conf; |
730 | bool short_scan = false, ret; |
731 | |
732 | rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & |
733 | ATH_ANT_RX_MASK; |
734 | main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & |
735 | ATH_ANT_RX_MASK; |
736 | |
737 | if (alt_rssi >= antcomb->low_rssi_thresh) { |
738 | antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO; |
739 | antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2; |
740 | } else { |
741 | antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI; |
742 | antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI; |
743 | } |
744 | |
745 | /* Record packet only when both main_rssi and alt_rssi is positive */ |
746 | if (main_rssi > 0 && alt_rssi > 0) { |
747 | antcomb->total_pkt_count++; |
748 | antcomb->main_total_rssi += main_rssi; |
749 | antcomb->alt_total_rssi += alt_rssi; |
750 | |
751 | if (main_ant_conf == rx_ant_conf) |
752 | antcomb->main_recv_cnt++; |
753 | else |
754 | antcomb->alt_recv_cnt++; |
755 | } |
756 | |
757 | if (main_ant_conf == rx_ant_conf) { |
758 | ANT_STAT_INC(sc, ANT_MAIN, recv_cnt); |
759 | ANT_LNA_INC(sc, ANT_MAIN, rx_ant_conf); |
760 | } else { |
761 | ANT_STAT_INC(sc, ANT_ALT, recv_cnt); |
762 | ANT_LNA_INC(sc, ANT_ALT, rx_ant_conf); |
763 | } |
764 | |
765 | /* Short scan check */ |
766 | short_scan = ath_ant_short_scan_check(antcomb); |
767 | |
768 | if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || |
769 | rs->rs_moreaggr) && !short_scan) |
770 | return; |
771 | |
772 | if (antcomb->total_pkt_count) { |
773 | alt_ratio = ((antcomb->alt_recv_cnt * 100) / |
774 | antcomb->total_pkt_count); |
775 | main_rssi_avg = (antcomb->main_total_rssi / |
776 | antcomb->total_pkt_count); |
777 | alt_rssi_avg = (antcomb->alt_total_rssi / |
778 | antcomb->total_pkt_count); |
779 | } |
780 | |
781 | ath9k_hw_antdiv_comb_conf_get(ah: sc->sc_ah, antconf: &div_ant_conf); |
782 | curr_alt_set = div_ant_conf.alt_lna_conf; |
783 | curr_main_set = div_ant_conf.main_lna_conf; |
784 | antcomb->count++; |
785 | |
786 | if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { |
787 | if (alt_ratio > antcomb->ant_ratio) { |
788 | ath_lnaconf_alt_good_scan(antcomb, ant_conf: div_ant_conf, |
789 | main_rssi_avg); |
790 | antcomb->alt_good = true; |
791 | } else { |
792 | antcomb->alt_good = false; |
793 | } |
794 | |
795 | antcomb->count = 0; |
796 | antcomb->scan = true; |
797 | antcomb->scan_not_start = true; |
798 | } |
799 | |
800 | if (!antcomb->scan) { |
801 | ret = ath_ant_try_switch(div_ant_conf: &div_ant_conf, antcomb, alt_ratio, |
802 | alt_rssi_avg, main_rssi_avg, |
803 | curr_main_set, curr_alt_set); |
804 | if (ret) |
805 | goto div_comb_done; |
806 | } |
807 | |
808 | if (!antcomb->scan && |
809 | (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta))) |
810 | goto div_comb_done; |
811 | |
812 | if (!antcomb->scan_not_start) { |
813 | ath_ant_try_scan(antcomb, conf: &div_ant_conf, curr_alt_set, |
814 | alt_rssi_avg, main_rssi_avg); |
815 | } else { |
816 | if (!antcomb->alt_good) { |
817 | antcomb->scan_not_start = false; |
818 | /* Set alt to another LNA */ |
819 | if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { |
820 | div_ant_conf.main_lna_conf = |
821 | ATH_ANT_DIV_COMB_LNA2; |
822 | div_ant_conf.alt_lna_conf = |
823 | ATH_ANT_DIV_COMB_LNA1; |
824 | } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { |
825 | div_ant_conf.main_lna_conf = |
826 | ATH_ANT_DIV_COMB_LNA1; |
827 | div_ant_conf.alt_lna_conf = |
828 | ATH_ANT_DIV_COMB_LNA2; |
829 | } |
830 | goto div_comb_done; |
831 | } |
832 | ath_select_ant_div_from_quick_scan(antcomb, div_ant_conf: &div_ant_conf, |
833 | main_rssi_avg, alt_rssi_avg, |
834 | alt_ratio); |
835 | antcomb->quick_scan_cnt++; |
836 | } |
837 | |
838 | div_comb_done: |
839 | ath_ant_div_conf_fast_divbias(ant_conf: &div_ant_conf, antcomb, alt_ratio); |
840 | ath9k_hw_antdiv_comb_conf_set(ah: sc->sc_ah, antconf: &div_ant_conf); |
841 | ath9k_debug_stat_ant(sc, div_ant_conf: &div_ant_conf, main_rssi_avg, alt_rssi_avg); |
842 | |
843 | antcomb->scan_start_time = jiffies; |
844 | antcomb->total_pkt_count = 0; |
845 | antcomb->main_total_rssi = 0; |
846 | antcomb->alt_total_rssi = 0; |
847 | antcomb->main_recv_cnt = 0; |
848 | antcomb->alt_recv_cnt = 0; |
849 | } |
850 | |