2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
4 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer,
12 * without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 * redistribution must be conditioned upon including a substantially
16 * similar Disclaimer requirement for further binary redistribution.
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
37 * This module handles LNA diversity for those chips which implement LNA
38 * mixing (AR9285/AR9485.)
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/sysctl.h>
47 #include <sys/kernel.h>
49 #include <sys/malloc.h>
50 #include <sys/mutex.h>
51 #include <sys/errno.h>
53 #include <machine/bus.h>
54 #include <machine/resource.h>
57 #include <sys/socket.h>
60 #include <net/if_var.h>
61 #include <net/if_media.h>
62 #include <net/if_arp.h>
63 #include <net/ethernet.h> /* XXX for ether_sprintf */
65 #include <net80211/ieee80211_var.h>
70 #include <netinet/in.h>
71 #include <netinet/if_ether.h>
74 #include <dev/ath/if_athvar.h>
75 #include <dev/ath/if_ath_debug.h>
76 #include <dev/ath/if_ath_lna_div.h>
78 /* Linux compatibility macros */
80 * XXX these don't handle rounding, underflow, overflow, wrapping!
82 #define msecs_to_jiffies(a) ( (a) * hz / 1000 )
85 * Methods which are required
89 * Attach the LNA diversity to the given interface
92 ath_lna_div_attach(struct ath_softc *sc)
94 struct if_ath_ant_comb_state *ss;
95 HAL_ANT_COMB_CONFIG div_ant_conf;
97 /* Only do this if diversity is enabled */
98 if (! ath_hal_hasdivantcomb(sc->sc_ah))
101 ss = malloc(sizeof(struct if_ath_ant_comb_state),
102 M_TEMP, M_WAITOK | M_ZERO);
104 device_printf(sc->sc_dev, "%s: failed to allocate\n",
106 /* Don't fail at this point */
110 /* Fetch the hardware configuration */
111 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
112 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
114 /* Figure out what the hardware specific bits should be */
115 if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
116 (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
117 ss->lna1_lna2_delta = -9;
119 ss->lna1_lna2_delta = -3;
122 /* Let's flip this on */
130 * Detach the LNA diversity state from the given interface
133 ath_lna_div_detach(struct ath_softc *sc)
135 if (sc->sc_lna_div != NULL) {
136 free(sc->sc_lna_div, M_TEMP);
137 sc->sc_lna_div = NULL;
144 * Enable LNA diversity on the current channel if it's required.
147 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
154 * Handle ioctl requests from the diagnostic interface.
156 * The initial part of this code resembles ath_ioctl_diag();
157 * it's likely a good idea to reduce duplication between
158 * these two routines.
161 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
163 unsigned int id = ad->ad_id & ATH_DIAG_ID;
165 void *outdata = NULL;
166 u_int32_t insize = ad->ad_in_size;
167 u_int32_t outsize = ad->ad_out_size;
171 if (ad->ad_id & ATH_DIAG_IN) {
175 indata = malloc(insize, M_TEMP, M_NOWAIT);
176 if (indata == NULL) {
180 error = copyin(ad->ad_in_data, indata, insize);
184 if (ad->ad_id & ATH_DIAG_DYN) {
186 * Allocate a buffer for the results (otherwise the HAL
187 * returns a pointer to a buffer where we can read the
188 * results). Note that we depend on the HAL leaving this
189 * pointer for us to use below in reclaiming the buffer;
190 * may want to be more defensive.
192 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
193 if (outdata == NULL) {
202 if (outsize < ad->ad_out_size)
203 ad->ad_out_size = outsize;
204 if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
207 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
208 free(indata, M_TEMP);
209 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
210 free(outdata, M_TEMP);
215 * XXX need to low_rssi_thresh config from ath9k, to support CUS198
216 * antenna diversity correctly.
219 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
220 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
222 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
223 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
224 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
228 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
229 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
231 antcomb->quick_scan_cnt = 0;
233 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
234 antcomb->rssi_lna2 = main_rssi_avg;
235 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
236 antcomb->rssi_lna1 = main_rssi_avg;
238 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
239 case (0x10): /* LNA2 A-B */
240 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
241 antcomb->first_quick_scan_conf =
242 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
243 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
245 case (0x20): /* LNA1 A-B */
246 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
247 antcomb->first_quick_scan_conf =
248 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
249 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
251 case (0x21): /* LNA1 LNA2 */
252 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
253 antcomb->first_quick_scan_conf =
254 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
255 antcomb->second_quick_scan_conf =
256 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
258 case (0x12): /* LNA2 LNA1 */
259 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
260 antcomb->first_quick_scan_conf =
261 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
262 antcomb->second_quick_scan_conf =
263 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
265 case (0x13): /* LNA2 A+B */
266 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
267 antcomb->first_quick_scan_conf =
268 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
269 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
271 case (0x23): /* LNA1 A+B */
272 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
273 antcomb->first_quick_scan_conf =
274 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
275 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
283 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
284 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
285 int alt_rssi_avg, int alt_ratio)
288 switch (antcomb->quick_scan_cnt) {
290 /* set alt to main, and alt to first conf */
291 div_ant_conf->main_lna_conf = antcomb->main_conf;
292 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
295 /* set alt to main, and alt to first conf */
296 div_ant_conf->main_lna_conf = antcomb->main_conf;
297 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
298 antcomb->rssi_first = main_rssi_avg;
299 antcomb->rssi_second = alt_rssi_avg;
301 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
303 if (ath_is_alt_ant_ratio_better(alt_ratio,
304 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
305 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
306 main_rssi_avg, alt_rssi_avg,
307 antcomb->total_pkt_count))
308 antcomb->first_ratio = AH_TRUE;
310 antcomb->first_ratio = AH_FALSE;
311 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
312 if (ath_is_alt_ant_ratio_better(alt_ratio,
313 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
314 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
315 main_rssi_avg, alt_rssi_avg,
316 antcomb->total_pkt_count))
317 antcomb->first_ratio = AH_TRUE;
319 antcomb->first_ratio = AH_FALSE;
321 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
322 (alt_rssi_avg > main_rssi_avg +
323 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
324 (alt_rssi_avg > main_rssi_avg)) &&
325 (antcomb->total_pkt_count > 50))
326 antcomb->first_ratio = AH_TRUE;
328 antcomb->first_ratio = AH_FALSE;
332 antcomb->alt_good = AH_FALSE;
333 antcomb->scan_not_start = AH_FALSE;
334 antcomb->scan = AH_FALSE;
335 antcomb->rssi_first = main_rssi_avg;
336 antcomb->rssi_third = alt_rssi_avg;
338 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
339 antcomb->rssi_lna1 = alt_rssi_avg;
340 else if (antcomb->second_quick_scan_conf ==
341 HAL_ANT_DIV_COMB_LNA2)
342 antcomb->rssi_lna2 = alt_rssi_avg;
343 else if (antcomb->second_quick_scan_conf ==
344 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
345 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
346 antcomb->rssi_lna2 = main_rssi_avg;
347 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
348 antcomb->rssi_lna1 = main_rssi_avg;
351 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
352 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
353 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
355 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
357 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
358 if (ath_is_alt_ant_ratio_better(alt_ratio,
359 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
360 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
361 main_rssi_avg, alt_rssi_avg,
362 antcomb->total_pkt_count))
363 antcomb->second_ratio = AH_TRUE;
365 antcomb->second_ratio = AH_FALSE;
366 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
367 if (ath_is_alt_ant_ratio_better(alt_ratio,
368 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
369 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
370 main_rssi_avg, alt_rssi_avg,
371 antcomb->total_pkt_count))
372 antcomb->second_ratio = AH_TRUE;
374 antcomb->second_ratio = AH_FALSE;
376 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
377 (alt_rssi_avg > main_rssi_avg +
378 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
379 (alt_rssi_avg > main_rssi_avg)) &&
380 (antcomb->total_pkt_count > 50))
381 antcomb->second_ratio = AH_TRUE;
383 antcomb->second_ratio = AH_FALSE;
386 /* set alt to the conf with maximun ratio */
387 if (antcomb->first_ratio && antcomb->second_ratio) {
388 if (antcomb->rssi_second > antcomb->rssi_third) {
390 if ((antcomb->first_quick_scan_conf ==
391 HAL_ANT_DIV_COMB_LNA1) ||
392 (antcomb->first_quick_scan_conf ==
393 HAL_ANT_DIV_COMB_LNA2))
394 /* Set alt LNA1 or LNA2*/
395 if (div_ant_conf->main_lna_conf ==
396 HAL_ANT_DIV_COMB_LNA2)
397 div_ant_conf->alt_lna_conf =
398 HAL_ANT_DIV_COMB_LNA1;
400 div_ant_conf->alt_lna_conf =
401 HAL_ANT_DIV_COMB_LNA2;
403 /* Set alt to A+B or A-B */
404 div_ant_conf->alt_lna_conf =
405 antcomb->first_quick_scan_conf;
406 } else if ((antcomb->second_quick_scan_conf ==
407 HAL_ANT_DIV_COMB_LNA1) ||
408 (antcomb->second_quick_scan_conf ==
409 HAL_ANT_DIV_COMB_LNA2)) {
410 /* Set alt LNA1 or LNA2 */
411 if (div_ant_conf->main_lna_conf ==
412 HAL_ANT_DIV_COMB_LNA2)
413 div_ant_conf->alt_lna_conf =
414 HAL_ANT_DIV_COMB_LNA1;
416 div_ant_conf->alt_lna_conf =
417 HAL_ANT_DIV_COMB_LNA2;
419 /* Set alt to A+B or A-B */
420 div_ant_conf->alt_lna_conf =
421 antcomb->second_quick_scan_conf;
423 } else if (antcomb->first_ratio) {
425 if ((antcomb->first_quick_scan_conf ==
426 HAL_ANT_DIV_COMB_LNA1) ||
427 (antcomb->first_quick_scan_conf ==
428 HAL_ANT_DIV_COMB_LNA2))
429 /* Set alt LNA1 or LNA2 */
430 if (div_ant_conf->main_lna_conf ==
431 HAL_ANT_DIV_COMB_LNA2)
432 div_ant_conf->alt_lna_conf =
433 HAL_ANT_DIV_COMB_LNA1;
435 div_ant_conf->alt_lna_conf =
436 HAL_ANT_DIV_COMB_LNA2;
438 /* Set alt to A+B or A-B */
439 div_ant_conf->alt_lna_conf =
440 antcomb->first_quick_scan_conf;
441 } else if (antcomb->second_ratio) {
443 if ((antcomb->second_quick_scan_conf ==
444 HAL_ANT_DIV_COMB_LNA1) ||
445 (antcomb->second_quick_scan_conf ==
446 HAL_ANT_DIV_COMB_LNA2))
447 /* Set alt LNA1 or LNA2 */
448 if (div_ant_conf->main_lna_conf ==
449 HAL_ANT_DIV_COMB_LNA2)
450 div_ant_conf->alt_lna_conf =
451 HAL_ANT_DIV_COMB_LNA1;
453 div_ant_conf->alt_lna_conf =
454 HAL_ANT_DIV_COMB_LNA2;
456 /* Set alt to A+B or A-B */
457 div_ant_conf->alt_lna_conf =
458 antcomb->second_quick_scan_conf;
460 /* main is largest */
461 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
462 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
463 /* Set alt LNA1 or LNA2 */
464 if (div_ant_conf->main_lna_conf ==
465 HAL_ANT_DIV_COMB_LNA2)
466 div_ant_conf->alt_lna_conf =
467 HAL_ANT_DIV_COMB_LNA1;
469 div_ant_conf->alt_lna_conf =
470 HAL_ANT_DIV_COMB_LNA2;
472 /* Set alt to A+B or A-B */
473 div_ant_conf->alt_lna_conf = antcomb->main_conf;
482 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
483 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
484 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
487 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
488 switch ((pdiv_ant_conf->main_lna_conf << 4)
489 | pdiv_ant_conf->alt_lna_conf) {
490 case (0x01): //A-B LNA2
491 pdiv_ant_conf->fast_div_bias = 0x1;
492 pdiv_ant_conf->main_gaintb = 0;
493 pdiv_ant_conf->alt_gaintb = 0;
495 case (0x02): //A-B LNA1
496 pdiv_ant_conf->fast_div_bias = 0x1;
497 pdiv_ant_conf->main_gaintb = 0;
498 pdiv_ant_conf->alt_gaintb = 0;
500 case (0x03): //A-B A+B
501 pdiv_ant_conf->fast_div_bias = 0x1;
502 pdiv_ant_conf->main_gaintb = 0;
503 pdiv_ant_conf->alt_gaintb = 0;
505 case (0x10): //LNA2 A-B
506 if ((antcomb->scan == 0)
507 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
508 pdiv_ant_conf->fast_div_bias = 0x3f;
510 pdiv_ant_conf->fast_div_bias = 0x1;
512 pdiv_ant_conf->main_gaintb = 0;
513 pdiv_ant_conf->alt_gaintb = 0;
515 case (0x12): //LNA2 LNA1
516 pdiv_ant_conf->fast_div_bias = 0x1;
517 pdiv_ant_conf->main_gaintb = 0;
518 pdiv_ant_conf->alt_gaintb = 0;
520 case (0x13): //LNA2 A+B
521 if ((antcomb->scan == 0)
522 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
523 pdiv_ant_conf->fast_div_bias = 0x3f;
525 pdiv_ant_conf->fast_div_bias = 0x1;
527 pdiv_ant_conf->main_gaintb = 0;
528 pdiv_ant_conf->alt_gaintb = 0;
530 case (0x20): //LNA1 A-B
531 if ((antcomb->scan == 0)
532 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
533 pdiv_ant_conf->fast_div_bias = 0x3f;
535 pdiv_ant_conf->fast_div_bias = 0x1;
537 pdiv_ant_conf->main_gaintb = 0;
538 pdiv_ant_conf->alt_gaintb = 0;
540 case (0x21): //LNA1 LNA2
541 pdiv_ant_conf->fast_div_bias = 0x1;
542 pdiv_ant_conf->main_gaintb = 0;
543 pdiv_ant_conf->alt_gaintb = 0;
545 case (0x23): //LNA1 A+B
546 if ((antcomb->scan == 0)
547 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
548 pdiv_ant_conf->fast_div_bias = 0x3f;
550 pdiv_ant_conf->fast_div_bias = 0x1;
552 pdiv_ant_conf->main_gaintb = 0;
553 pdiv_ant_conf->alt_gaintb = 0;
555 case (0x30): //A+B A-B
556 pdiv_ant_conf->fast_div_bias = 0x1;
557 pdiv_ant_conf->main_gaintb = 0;
558 pdiv_ant_conf->alt_gaintb = 0;
560 case (0x31): //A+B LNA2
561 pdiv_ant_conf->fast_div_bias = 0x1;
562 pdiv_ant_conf->main_gaintb = 0;
563 pdiv_ant_conf->alt_gaintb = 0;
565 case (0x32): //A+B LNA1
566 pdiv_ant_conf->fast_div_bias = 0x1;
567 pdiv_ant_conf->main_gaintb = 0;
568 pdiv_ant_conf->alt_gaintb = 0;
573 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
574 switch ((pdiv_ant_conf->main_lna_conf << 4)
575 | pdiv_ant_conf->alt_lna_conf) {
576 case (0x01): //A-B LNA2
577 pdiv_ant_conf->fast_div_bias = 0x1;
578 pdiv_ant_conf->main_gaintb = 0;
579 pdiv_ant_conf->alt_gaintb = 0;
581 case (0x02): //A-B LNA1
582 pdiv_ant_conf->fast_div_bias = 0x1;
583 pdiv_ant_conf->main_gaintb = 0;
584 pdiv_ant_conf->alt_gaintb = 0;
586 case (0x03): //A-B A+B
587 pdiv_ant_conf->fast_div_bias = 0x1;
588 pdiv_ant_conf->main_gaintb = 0;
589 pdiv_ant_conf->alt_gaintb = 0;
591 case (0x10): //LNA2 A-B
592 if ((antcomb->scan == 0)
593 && (alt_ratio > alt_ant_ratio_th)) {
594 pdiv_ant_conf->fast_div_bias = 0x1;
596 pdiv_ant_conf->fast_div_bias = 0x2;
598 pdiv_ant_conf->main_gaintb = 0;
599 pdiv_ant_conf->alt_gaintb = 0;
601 case (0x12): //LNA2 LNA1
602 pdiv_ant_conf->fast_div_bias = 0x1;
603 pdiv_ant_conf->main_gaintb = 0;
604 pdiv_ant_conf->alt_gaintb = 0;
606 case (0x13): //LNA2 A+B
607 if ((antcomb->scan == 0)
608 && (alt_ratio > alt_ant_ratio_th)) {
609 pdiv_ant_conf->fast_div_bias = 0x1;
611 pdiv_ant_conf->fast_div_bias = 0x2;
613 pdiv_ant_conf->main_gaintb = 0;
614 pdiv_ant_conf->alt_gaintb = 0;
616 case (0x20): //LNA1 A-B
617 if ((antcomb->scan == 0)
618 && (alt_ratio > alt_ant_ratio_th)) {
619 pdiv_ant_conf->fast_div_bias = 0x1;
621 pdiv_ant_conf->fast_div_bias = 0x2;
623 pdiv_ant_conf->main_gaintb = 0;
624 pdiv_ant_conf->alt_gaintb = 0;
626 case (0x21): //LNA1 LNA2
627 pdiv_ant_conf->fast_div_bias = 0x1;
628 pdiv_ant_conf->main_gaintb = 0;
629 pdiv_ant_conf->alt_gaintb = 0;
631 case (0x23): //LNA1 A+B
632 if ((antcomb->scan == 0)
633 && (alt_ratio > alt_ant_ratio_th)) {
634 pdiv_ant_conf->fast_div_bias = 0x1;
636 pdiv_ant_conf->fast_div_bias = 0x2;
638 pdiv_ant_conf->main_gaintb = 0;
639 pdiv_ant_conf->alt_gaintb = 0;
641 case (0x30): //A+B A-B
642 pdiv_ant_conf->fast_div_bias = 0x1;
643 pdiv_ant_conf->main_gaintb = 0;
644 pdiv_ant_conf->alt_gaintb = 0;
646 case (0x31): //A+B LNA2
647 pdiv_ant_conf->fast_div_bias = 0x1;
648 pdiv_ant_conf->main_gaintb = 0;
649 pdiv_ant_conf->alt_gaintb = 0;
651 case (0x32): //A+B LNA1
652 pdiv_ant_conf->fast_div_bias = 0x1;
653 pdiv_ant_conf->main_gaintb = 0;
654 pdiv_ant_conf->alt_gaintb = 0;
659 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
660 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
661 case (0x01): //A-B LNA2
662 pdiv_ant_conf->fast_div_bias = 0x3b;
664 case (0x02): //A-B LNA1
665 pdiv_ant_conf->fast_div_bias = 0x3d;
667 case (0x03): //A-B A+B
668 pdiv_ant_conf->fast_div_bias = 0x1;
670 case (0x10): //LNA2 A-B
671 pdiv_ant_conf->fast_div_bias = 0x7;
673 case (0x12): //LNA2 LNA1
674 pdiv_ant_conf->fast_div_bias = 0x2;
676 case (0x13): //LNA2 A+B
677 pdiv_ant_conf->fast_div_bias = 0x7;
679 case (0x20): //LNA1 A-B
680 pdiv_ant_conf->fast_div_bias = 0x6;
682 case (0x21): //LNA1 LNA2
683 pdiv_ant_conf->fast_div_bias = 0x0;
685 case (0x23): //LNA1 A+B
686 pdiv_ant_conf->fast_div_bias = 0x6;
688 case (0x30): //A+B A-B
689 pdiv_ant_conf->fast_div_bias = 0x1;
691 case (0x31): //A+B LNA2
692 pdiv_ant_conf->fast_div_bias = 0x3b;
694 case (0x32): //A+B LNA1
695 pdiv_ant_conf->fast_div_bias = 0x3d;
704 * AR9485/AR933x TODO:
705 * + Select a ratio based on whether RSSI is low or not; but I need
706 * to figure out what "low_rssi_th" is sourced from.
707 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
708 * + .. and there's likely a bunch of other things to include in this.
711 /* Antenna diversity and combining */
713 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
714 unsigned long ticks, int hz)
716 HAL_ANT_COMB_CONFIG div_ant_conf;
717 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
718 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
719 int curr_main_set, curr_bias;
720 int main_rssi = rs->rs_rssi_ctl[0];
721 int alt_rssi = rs->rs_rssi_ctl[1];
722 int rx_ant_conf, main_ant_conf, alt_ant_conf;
723 HAL_BOOL short_scan = AH_FALSE;
725 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
726 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
727 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
730 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
731 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
739 !!(rs->rs_rssi_ctl[2] & 0x80),
740 !!(rs->rs_rssi_ctl[2] & 0x40),
741 !!(rs->rs_rssi_ext[2] & 0x40));
745 * If LNA diversity combining isn't enabled, don't run this.
747 if (! sc->sc_dolnadiv)
751 * XXX this is ugly, but the HAL code attaches the
752 * LNA diversity to the TX antenna settings.
755 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
758 /* Record packet only when alt_rssi is positive */
759 if (main_rssi > 0 && alt_rssi > 0) {
760 antcomb->total_pkt_count++;
761 antcomb->main_total_rssi += main_rssi;
762 antcomb->alt_total_rssi += alt_rssi;
763 if (main_ant_conf == rx_ant_conf)
764 antcomb->main_recv_cnt++;
766 antcomb->alt_recv_cnt++;
769 /* Short scan check */
770 if (antcomb->scan && antcomb->alt_good) {
771 if (ieee80211_time_after(ticks, antcomb->scan_start_time +
772 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
773 short_scan = AH_TRUE;
775 if (antcomb->total_pkt_count ==
776 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
777 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
778 antcomb->total_pkt_count);
779 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
780 short_scan = AH_TRUE;
785 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
786 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
788 antcomb->total_pkt_count,
789 !! (rs->rs_moreaggr),
793 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
794 rs->rs_moreaggr) && !short_scan)
797 if (antcomb->total_pkt_count) {
798 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
799 antcomb->total_pkt_count);
800 main_rssi_avg = (antcomb->main_total_rssi /
801 antcomb->total_pkt_count);
802 alt_rssi_avg = (antcomb->alt_total_rssi /
803 antcomb->total_pkt_count);
806 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
808 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
809 curr_alt_set = div_ant_conf.alt_lna_conf;
810 curr_main_set = div_ant_conf.main_lna_conf;
811 curr_bias = div_ant_conf.fast_div_bias;
815 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
816 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
817 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
819 antcomb->alt_good = AH_TRUE;
821 antcomb->alt_good = AH_FALSE;
825 antcomb->scan = AH_TRUE;
826 antcomb->scan_not_start = AH_TRUE;
829 if (!antcomb->scan) {
830 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
831 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
832 /* Switch main and alt LNA */
833 div_ant_conf.main_lna_conf =
834 HAL_ANT_DIV_COMB_LNA2;
835 div_ant_conf.alt_lna_conf =
836 HAL_ANT_DIV_COMB_LNA1;
837 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
838 div_ant_conf.main_lna_conf =
839 HAL_ANT_DIV_COMB_LNA1;
840 div_ant_conf.alt_lna_conf =
841 HAL_ANT_DIV_COMB_LNA2;
845 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
846 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
847 /* Set alt to another LNA */
848 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
849 div_ant_conf.alt_lna_conf =
850 HAL_ANT_DIV_COMB_LNA1;
851 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
852 div_ant_conf.alt_lna_conf =
853 HAL_ANT_DIV_COMB_LNA2;
858 if ((alt_rssi_avg < (main_rssi_avg +
859 antcomb->lna1_lna2_delta)))
863 if (!antcomb->scan_not_start) {
864 switch (curr_alt_set) {
865 case HAL_ANT_DIV_COMB_LNA2:
866 antcomb->rssi_lna2 = alt_rssi_avg;
867 antcomb->rssi_lna1 = main_rssi_avg;
868 antcomb->scan = AH_TRUE;
870 div_ant_conf.main_lna_conf =
871 HAL_ANT_DIV_COMB_LNA1;
872 div_ant_conf.alt_lna_conf =
873 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
875 case HAL_ANT_DIV_COMB_LNA1:
876 antcomb->rssi_lna1 = alt_rssi_avg;
877 antcomb->rssi_lna2 = main_rssi_avg;
878 antcomb->scan = AH_TRUE;
880 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
881 div_ant_conf.alt_lna_conf =
882 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
884 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
885 antcomb->rssi_add = alt_rssi_avg;
886 antcomb->scan = AH_TRUE;
888 div_ant_conf.alt_lna_conf =
889 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
891 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
892 antcomb->rssi_sub = alt_rssi_avg;
893 antcomb->scan = AH_FALSE;
894 if (antcomb->rssi_lna2 >
895 (antcomb->rssi_lna1 +
896 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
897 /* use LNA2 as main LNA */
898 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
899 (antcomb->rssi_add > antcomb->rssi_sub)) {
901 div_ant_conf.main_lna_conf =
902 HAL_ANT_DIV_COMB_LNA2;
903 div_ant_conf.alt_lna_conf =
904 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
905 } else if (antcomb->rssi_sub >
906 antcomb->rssi_lna1) {
908 div_ant_conf.main_lna_conf =
909 HAL_ANT_DIV_COMB_LNA2;
910 div_ant_conf.alt_lna_conf =
911 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
914 div_ant_conf.main_lna_conf =
915 HAL_ANT_DIV_COMB_LNA2;
916 div_ant_conf.alt_lna_conf =
917 HAL_ANT_DIV_COMB_LNA1;
920 /* use LNA1 as main LNA */
921 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
922 (antcomb->rssi_add > antcomb->rssi_sub)) {
924 div_ant_conf.main_lna_conf =
925 HAL_ANT_DIV_COMB_LNA1;
926 div_ant_conf.alt_lna_conf =
927 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
928 } else if (antcomb->rssi_sub >
929 antcomb->rssi_lna1) {
931 div_ant_conf.main_lna_conf =
932 HAL_ANT_DIV_COMB_LNA1;
933 div_ant_conf.alt_lna_conf =
934 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
937 div_ant_conf.main_lna_conf =
938 HAL_ANT_DIV_COMB_LNA1;
939 div_ant_conf.alt_lna_conf =
940 HAL_ANT_DIV_COMB_LNA2;
948 if (!antcomb->alt_good) {
949 antcomb->scan_not_start = AH_FALSE;
950 /* Set alt to another LNA */
951 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
952 div_ant_conf.main_lna_conf =
953 HAL_ANT_DIV_COMB_LNA2;
954 div_ant_conf.alt_lna_conf =
955 HAL_ANT_DIV_COMB_LNA1;
956 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
957 div_ant_conf.main_lna_conf =
958 HAL_ANT_DIV_COMB_LNA1;
959 div_ant_conf.alt_lna_conf =
960 HAL_ANT_DIV_COMB_LNA2;
966 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
967 main_rssi_avg, alt_rssi_avg,
970 antcomb->quick_scan_cnt++;
974 ath_ant_div_conf_fast_divbias(&div_ant_conf);
977 ath_ant_adjust_fast_divbias(antcomb,
979 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
980 div_ant_conf.antdiv_configgroup,
983 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
985 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
986 __func__, antcomb->total_pkt_count);
988 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
989 __func__, antcomb->main_total_rssi);
990 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
991 __func__, antcomb->alt_total_rssi);
993 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
994 __func__, main_rssi_avg);
995 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
996 __func__, alt_rssi_avg);
998 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
999 __func__, antcomb->main_recv_cnt);
1000 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
1001 __func__, antcomb->alt_recv_cnt);
1003 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
1004 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1005 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1006 // if (curr_main_set != div_ant_conf.main_lna_conf)
1007 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1008 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1009 // if (curr_bias != div_ant_conf.fast_div_bias)
1010 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1011 __func__, curr_bias, div_ant_conf.fast_div_bias);
1013 antcomb->scan_start_time = ticks;
1014 antcomb->total_pkt_count = 0;
1015 antcomb->main_total_rssi = 0;
1016 antcomb->alt_total_rssi = 0;
1017 antcomb->main_recv_cnt = 0;
1018 antcomb->alt_recv_cnt = 0;