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
71static 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 alt_rssi_avg, 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
90static 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 alt_rssi_avg,
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
138static 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
193static 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
261static 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 alt_rssi_avg,
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
377static 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
589static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
590 struct ath_hw_antcomb_conf *conf,
591 int curr_alt_set, int alt_rssi_avg,
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
662static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
663 struct ath_ant_comb *antcomb,
664 int alt_ratio, int alt_rssi_avg,
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
700static 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
721void 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, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
726 int curr_main_set;
727 int main_rssi = rs->rs_rssi_ctl[0];
728 int alt_rssi = 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
838div_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

source code of linux/drivers/net/wireless/ath/ath9k/antenna.c