2 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
8 * 1. Redistributions of source code must retain the above copyright
9 * notice, this list of conditions and the following disclaimer,
10 * without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 * similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 * redistribution must be conditioned upon including a substantially
14 * similar Disclaimer requirement for further binary redistribution.
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
35 * This module handles LNA diversity for those chips which implement LNA
36 * mixing (AR9285/AR9485.)
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/sysctl.h>
45 #include <sys/kernel.h>
47 #include <sys/mutex.h>
48 #include <sys/errno.h>
50 #include <machine/bus.h>
51 #include <machine/resource.h>
54 #include <sys/socket.h>
57 #include <net/if_media.h>
58 #include <net/if_arp.h>
59 #include <net/ethernet.h> /* XXX for ether_sprintf */
61 #include <net80211/ieee80211_var.h>
66 #include <netinet/in.h>
67 #include <netinet/if_ether.h>
70 #include <dev/ath/if_athvar.h>
71 #include <dev/ath/if_ath_debug.h>
72 #include <dev/ath/if_ath_lna_div.h>
74 /* Linux compability macros */
76 * XXX these don't handle rounding, underflow, overflow, wrapping!
78 #define msecs_to_jiffies(a) ( (a) * hz / 1000 )
81 * Methods which are required
85 * Attach the LNA diversity to the given interface
88 ath_lna_div_attach(struct ath_softc *sc)
90 struct if_ath_ant_comb_state *ss;
91 HAL_ANT_COMB_CONFIG div_ant_conf;
93 /* Only do this if diversity is enabled */
94 if (! ath_hal_hasdivantcomb(sc->sc_ah))
97 ss = malloc(sizeof(struct if_ath_ant_comb_state),
98 M_TEMP, M_WAITOK | M_ZERO);
100 device_printf(sc->sc_dev, "%s: failed to allocate\n",
102 /* Don't fail at this point */
106 /* Fetch the hardware configuration */
107 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
108 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
110 /* Figure out what the hardware specific bits should be */
111 if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
112 (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
113 ss->lna1_lna2_delta = -9;
115 ss->lna1_lna2_delta = -3;
118 /* Let's flip this on */
126 * Detach the LNA diversity state from the given interface
129 ath_lna_div_detach(struct ath_softc *sc)
131 if (sc->sc_lna_div != NULL) {
132 free(sc->sc_lna_div, M_TEMP);
133 sc->sc_lna_div = NULL;
140 * Enable LNA diversity on the current channel if it's required.
143 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
150 * Handle ioctl requests from the diagnostic interface.
152 * The initial part of this code resembles ath_ioctl_diag();
153 * it's likely a good idea to reduce duplication between
154 * these two routines.
157 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
159 unsigned int id = ad->ad_id & ATH_DIAG_ID;
161 void *outdata = NULL;
162 u_int32_t insize = ad->ad_in_size;
163 u_int32_t outsize = ad->ad_out_size;
167 if (ad->ad_id & ATH_DIAG_IN) {
171 indata = malloc(insize, M_TEMP, M_NOWAIT);
172 if (indata == NULL) {
176 error = copyin(ad->ad_in_data, indata, insize);
180 if (ad->ad_id & ATH_DIAG_DYN) {
182 * Allocate a buffer for the results (otherwise the HAL
183 * returns a pointer to a buffer where we can read the
184 * results). Note that we depend on the HAL leaving this
185 * pointer for us to use below in reclaiming the buffer;
186 * may want to be more defensive.
188 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
189 if (outdata == NULL) {
198 if (outsize < ad->ad_out_size)
199 ad->ad_out_size = outsize;
200 if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
203 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
204 free(indata, M_TEMP);
205 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
206 free(outdata, M_TEMP);
211 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
212 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
214 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
215 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
216 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
220 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
221 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
223 antcomb->quick_scan_cnt = 0;
225 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
226 antcomb->rssi_lna2 = main_rssi_avg;
227 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
228 antcomb->rssi_lna1 = main_rssi_avg;
230 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
231 case (0x10): /* LNA2 A-B */
232 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
233 antcomb->first_quick_scan_conf =
234 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
235 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
237 case (0x20): /* LNA1 A-B */
238 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
239 antcomb->first_quick_scan_conf =
240 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
241 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
243 case (0x21): /* LNA1 LNA2 */
244 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
245 antcomb->first_quick_scan_conf =
246 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
247 antcomb->second_quick_scan_conf =
248 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
250 case (0x12): /* LNA2 LNA1 */
251 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
252 antcomb->first_quick_scan_conf =
253 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
254 antcomb->second_quick_scan_conf =
255 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
257 case (0x13): /* LNA2 A+B */
258 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
259 antcomb->first_quick_scan_conf =
260 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
261 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
263 case (0x23): /* LNA1 A+B */
264 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
265 antcomb->first_quick_scan_conf =
266 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
267 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
275 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
276 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
277 int alt_rssi_avg, int alt_ratio)
280 switch (antcomb->quick_scan_cnt) {
282 /* set alt to main, and alt to first conf */
283 div_ant_conf->main_lna_conf = antcomb->main_conf;
284 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
287 /* set alt to main, and alt to first conf */
288 div_ant_conf->main_lna_conf = antcomb->main_conf;
289 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
290 antcomb->rssi_first = main_rssi_avg;
291 antcomb->rssi_second = alt_rssi_avg;
293 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
295 if (ath_is_alt_ant_ratio_better(alt_ratio,
296 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
297 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
298 main_rssi_avg, alt_rssi_avg,
299 antcomb->total_pkt_count))
300 antcomb->first_ratio = AH_TRUE;
302 antcomb->first_ratio = AH_FALSE;
303 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
304 if (ath_is_alt_ant_ratio_better(alt_ratio,
305 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
306 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
307 main_rssi_avg, alt_rssi_avg,
308 antcomb->total_pkt_count))
309 antcomb->first_ratio = AH_TRUE;
311 antcomb->first_ratio = AH_FALSE;
313 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
314 (alt_rssi_avg > main_rssi_avg +
315 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
316 (alt_rssi_avg > main_rssi_avg)) &&
317 (antcomb->total_pkt_count > 50))
318 antcomb->first_ratio = AH_TRUE;
320 antcomb->first_ratio = AH_FALSE;
324 antcomb->alt_good = AH_FALSE;
325 antcomb->scan_not_start = AH_FALSE;
326 antcomb->scan = AH_FALSE;
327 antcomb->rssi_first = main_rssi_avg;
328 antcomb->rssi_third = alt_rssi_avg;
330 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
331 antcomb->rssi_lna1 = alt_rssi_avg;
332 else if (antcomb->second_quick_scan_conf ==
333 HAL_ANT_DIV_COMB_LNA2)
334 antcomb->rssi_lna2 = alt_rssi_avg;
335 else if (antcomb->second_quick_scan_conf ==
336 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
337 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
338 antcomb->rssi_lna2 = main_rssi_avg;
339 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
340 antcomb->rssi_lna1 = main_rssi_avg;
343 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
344 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
345 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
347 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
349 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
350 if (ath_is_alt_ant_ratio_better(alt_ratio,
351 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
352 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
353 main_rssi_avg, alt_rssi_avg,
354 antcomb->total_pkt_count))
355 antcomb->second_ratio = AH_TRUE;
357 antcomb->second_ratio = AH_FALSE;
358 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
359 if (ath_is_alt_ant_ratio_better(alt_ratio,
360 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
361 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
362 main_rssi_avg, alt_rssi_avg,
363 antcomb->total_pkt_count))
364 antcomb->second_ratio = AH_TRUE;
366 antcomb->second_ratio = AH_FALSE;
368 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
369 (alt_rssi_avg > main_rssi_avg +
370 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
371 (alt_rssi_avg > main_rssi_avg)) &&
372 (antcomb->total_pkt_count > 50))
373 antcomb->second_ratio = AH_TRUE;
375 antcomb->second_ratio = AH_FALSE;
378 /* set alt to the conf with maximun ratio */
379 if (antcomb->first_ratio && antcomb->second_ratio) {
380 if (antcomb->rssi_second > antcomb->rssi_third) {
382 if ((antcomb->first_quick_scan_conf ==
383 HAL_ANT_DIV_COMB_LNA1) ||
384 (antcomb->first_quick_scan_conf ==
385 HAL_ANT_DIV_COMB_LNA2))
386 /* Set alt LNA1 or LNA2*/
387 if (div_ant_conf->main_lna_conf ==
388 HAL_ANT_DIV_COMB_LNA2)
389 div_ant_conf->alt_lna_conf =
390 HAL_ANT_DIV_COMB_LNA1;
392 div_ant_conf->alt_lna_conf =
393 HAL_ANT_DIV_COMB_LNA2;
395 /* Set alt to A+B or A-B */
396 div_ant_conf->alt_lna_conf =
397 antcomb->first_quick_scan_conf;
398 } else if ((antcomb->second_quick_scan_conf ==
399 HAL_ANT_DIV_COMB_LNA1) ||
400 (antcomb->second_quick_scan_conf ==
401 HAL_ANT_DIV_COMB_LNA2)) {
402 /* Set alt LNA1 or LNA2 */
403 if (div_ant_conf->main_lna_conf ==
404 HAL_ANT_DIV_COMB_LNA2)
405 div_ant_conf->alt_lna_conf =
406 HAL_ANT_DIV_COMB_LNA1;
408 div_ant_conf->alt_lna_conf =
409 HAL_ANT_DIV_COMB_LNA2;
411 /* Set alt to A+B or A-B */
412 div_ant_conf->alt_lna_conf =
413 antcomb->second_quick_scan_conf;
415 } else if (antcomb->first_ratio) {
417 if ((antcomb->first_quick_scan_conf ==
418 HAL_ANT_DIV_COMB_LNA1) ||
419 (antcomb->first_quick_scan_conf ==
420 HAL_ANT_DIV_COMB_LNA2))
421 /* Set alt LNA1 or LNA2 */
422 if (div_ant_conf->main_lna_conf ==
423 HAL_ANT_DIV_COMB_LNA2)
424 div_ant_conf->alt_lna_conf =
425 HAL_ANT_DIV_COMB_LNA1;
427 div_ant_conf->alt_lna_conf =
428 HAL_ANT_DIV_COMB_LNA2;
430 /* Set alt to A+B or A-B */
431 div_ant_conf->alt_lna_conf =
432 antcomb->first_quick_scan_conf;
433 } else if (antcomb->second_ratio) {
435 if ((antcomb->second_quick_scan_conf ==
436 HAL_ANT_DIV_COMB_LNA1) ||
437 (antcomb->second_quick_scan_conf ==
438 HAL_ANT_DIV_COMB_LNA2))
439 /* Set alt LNA1 or LNA2 */
440 if (div_ant_conf->main_lna_conf ==
441 HAL_ANT_DIV_COMB_LNA2)
442 div_ant_conf->alt_lna_conf =
443 HAL_ANT_DIV_COMB_LNA1;
445 div_ant_conf->alt_lna_conf =
446 HAL_ANT_DIV_COMB_LNA2;
448 /* Set alt to A+B or A-B */
449 div_ant_conf->alt_lna_conf =
450 antcomb->second_quick_scan_conf;
452 /* main is largest */
453 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
454 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
455 /* Set alt LNA1 or LNA2 */
456 if (div_ant_conf->main_lna_conf ==
457 HAL_ANT_DIV_COMB_LNA2)
458 div_ant_conf->alt_lna_conf =
459 HAL_ANT_DIV_COMB_LNA1;
461 div_ant_conf->alt_lna_conf =
462 HAL_ANT_DIV_COMB_LNA2;
464 /* Set alt to A+B or A-B */
465 div_ant_conf->alt_lna_conf = antcomb->main_conf;
474 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
475 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
476 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
479 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
480 switch ((pdiv_ant_conf->main_lna_conf << 4)
481 | pdiv_ant_conf->alt_lna_conf) {
482 case (0x01): //A-B LNA2
483 pdiv_ant_conf->fast_div_bias = 0x1;
484 pdiv_ant_conf->main_gaintb = 0;
485 pdiv_ant_conf->alt_gaintb = 0;
487 case (0x02): //A-B LNA1
488 pdiv_ant_conf->fast_div_bias = 0x1;
489 pdiv_ant_conf->main_gaintb = 0;
490 pdiv_ant_conf->alt_gaintb = 0;
492 case (0x03): //A-B A+B
493 pdiv_ant_conf->fast_div_bias = 0x1;
494 pdiv_ant_conf->main_gaintb = 0;
495 pdiv_ant_conf->alt_gaintb = 0;
497 case (0x10): //LNA2 A-B
498 if ((antcomb->scan == 0)
499 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
500 pdiv_ant_conf->fast_div_bias = 0x3f;
502 pdiv_ant_conf->fast_div_bias = 0x1;
504 pdiv_ant_conf->main_gaintb = 0;
505 pdiv_ant_conf->alt_gaintb = 0;
507 case (0x12): //LNA2 LNA1
508 pdiv_ant_conf->fast_div_bias = 0x1;
509 pdiv_ant_conf->main_gaintb = 0;
510 pdiv_ant_conf->alt_gaintb = 0;
512 case (0x13): //LNA2 A+B
513 if ((antcomb->scan == 0)
514 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
515 pdiv_ant_conf->fast_div_bias = 0x3f;
517 pdiv_ant_conf->fast_div_bias = 0x1;
519 pdiv_ant_conf->main_gaintb = 0;
520 pdiv_ant_conf->alt_gaintb = 0;
522 case (0x20): //LNA1 A-B
523 if ((antcomb->scan == 0)
524 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
525 pdiv_ant_conf->fast_div_bias = 0x3f;
527 pdiv_ant_conf->fast_div_bias = 0x1;
529 pdiv_ant_conf->main_gaintb = 0;
530 pdiv_ant_conf->alt_gaintb = 0;
532 case (0x21): //LNA1 LNA2
533 pdiv_ant_conf->fast_div_bias = 0x1;
534 pdiv_ant_conf->main_gaintb = 0;
535 pdiv_ant_conf->alt_gaintb = 0;
537 case (0x23): //LNA1 A+B
538 if ((antcomb->scan == 0)
539 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
540 pdiv_ant_conf->fast_div_bias = 0x3f;
542 pdiv_ant_conf->fast_div_bias = 0x1;
544 pdiv_ant_conf->main_gaintb = 0;
545 pdiv_ant_conf->alt_gaintb = 0;
547 case (0x30): //A+B A-B
548 pdiv_ant_conf->fast_div_bias = 0x1;
549 pdiv_ant_conf->main_gaintb = 0;
550 pdiv_ant_conf->alt_gaintb = 0;
552 case (0x31): //A+B LNA2
553 pdiv_ant_conf->fast_div_bias = 0x1;
554 pdiv_ant_conf->main_gaintb = 0;
555 pdiv_ant_conf->alt_gaintb = 0;
557 case (0x32): //A+B LNA1
558 pdiv_ant_conf->fast_div_bias = 0x1;
559 pdiv_ant_conf->main_gaintb = 0;
560 pdiv_ant_conf->alt_gaintb = 0;
565 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
566 switch ((pdiv_ant_conf->main_lna_conf << 4)
567 | pdiv_ant_conf->alt_lna_conf) {
568 case (0x01): //A-B LNA2
569 pdiv_ant_conf->fast_div_bias = 0x1;
570 pdiv_ant_conf->main_gaintb = 0;
571 pdiv_ant_conf->alt_gaintb = 0;
573 case (0x02): //A-B LNA1
574 pdiv_ant_conf->fast_div_bias = 0x1;
575 pdiv_ant_conf->main_gaintb = 0;
576 pdiv_ant_conf->alt_gaintb = 0;
578 case (0x03): //A-B A+B
579 pdiv_ant_conf->fast_div_bias = 0x1;
580 pdiv_ant_conf->main_gaintb = 0;
581 pdiv_ant_conf->alt_gaintb = 0;
583 case (0x10): //LNA2 A-B
584 if ((antcomb->scan == 0)
585 && (alt_ratio > alt_ant_ratio_th)) {
586 pdiv_ant_conf->fast_div_bias = 0x1;
588 pdiv_ant_conf->fast_div_bias = 0x2;
590 pdiv_ant_conf->main_gaintb = 0;
591 pdiv_ant_conf->alt_gaintb = 0;
593 case (0x12): //LNA2 LNA1
594 pdiv_ant_conf->fast_div_bias = 0x1;
595 pdiv_ant_conf->main_gaintb = 0;
596 pdiv_ant_conf->alt_gaintb = 0;
598 case (0x13): //LNA2 A+B
599 if ((antcomb->scan == 0)
600 && (alt_ratio > alt_ant_ratio_th)) {
601 pdiv_ant_conf->fast_div_bias = 0x1;
603 pdiv_ant_conf->fast_div_bias = 0x2;
605 pdiv_ant_conf->main_gaintb = 0;
606 pdiv_ant_conf->alt_gaintb = 0;
608 case (0x20): //LNA1 A-B
609 if ((antcomb->scan == 0)
610 && (alt_ratio > alt_ant_ratio_th)) {
611 pdiv_ant_conf->fast_div_bias = 0x1;
613 pdiv_ant_conf->fast_div_bias = 0x2;
615 pdiv_ant_conf->main_gaintb = 0;
616 pdiv_ant_conf->alt_gaintb = 0;
618 case (0x21): //LNA1 LNA2
619 pdiv_ant_conf->fast_div_bias = 0x1;
620 pdiv_ant_conf->main_gaintb = 0;
621 pdiv_ant_conf->alt_gaintb = 0;
623 case (0x23): //LNA1 A+B
624 if ((antcomb->scan == 0)
625 && (alt_ratio > alt_ant_ratio_th)) {
626 pdiv_ant_conf->fast_div_bias = 0x1;
628 pdiv_ant_conf->fast_div_bias = 0x2;
630 pdiv_ant_conf->main_gaintb = 0;
631 pdiv_ant_conf->alt_gaintb = 0;
633 case (0x30): //A+B A-B
634 pdiv_ant_conf->fast_div_bias = 0x1;
635 pdiv_ant_conf->main_gaintb = 0;
636 pdiv_ant_conf->alt_gaintb = 0;
638 case (0x31): //A+B LNA2
639 pdiv_ant_conf->fast_div_bias = 0x1;
640 pdiv_ant_conf->main_gaintb = 0;
641 pdiv_ant_conf->alt_gaintb = 0;
643 case (0x32): //A+B LNA1
644 pdiv_ant_conf->fast_div_bias = 0x1;
645 pdiv_ant_conf->main_gaintb = 0;
646 pdiv_ant_conf->alt_gaintb = 0;
651 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
652 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
653 case (0x01): //A-B LNA2
654 pdiv_ant_conf->fast_div_bias = 0x3b;
656 case (0x02): //A-B LNA1
657 pdiv_ant_conf->fast_div_bias = 0x3d;
659 case (0x03): //A-B A+B
660 pdiv_ant_conf->fast_div_bias = 0x1;
662 case (0x10): //LNA2 A-B
663 pdiv_ant_conf->fast_div_bias = 0x7;
665 case (0x12): //LNA2 LNA1
666 pdiv_ant_conf->fast_div_bias = 0x2;
668 case (0x13): //LNA2 A+B
669 pdiv_ant_conf->fast_div_bias = 0x7;
671 case (0x20): //LNA1 A-B
672 pdiv_ant_conf->fast_div_bias = 0x6;
674 case (0x21): //LNA1 LNA2
675 pdiv_ant_conf->fast_div_bias = 0x0;
677 case (0x23): //LNA1 A+B
678 pdiv_ant_conf->fast_div_bias = 0x6;
680 case (0x30): //A+B A-B
681 pdiv_ant_conf->fast_div_bias = 0x1;
683 case (0x31): //A+B LNA2
684 pdiv_ant_conf->fast_div_bias = 0x3b;
686 case (0x32): //A+B LNA1
687 pdiv_ant_conf->fast_div_bias = 0x3d;
696 * AR9485/AR933x TODO:
697 * + Select a ratio based on whether RSSI is low or not; but I need
698 * to figure out what "low_rssi_th" is sourced from.
699 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
700 * + .. and there's likely a bunch of other things to include in this.
703 /* Antenna diversity and combining */
705 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
706 unsigned long ticks, int hz)
708 HAL_ANT_COMB_CONFIG div_ant_conf;
709 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
710 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
711 int curr_main_set, curr_bias;
712 int main_rssi = rs->rs_rssi_ctl[0];
713 int alt_rssi = rs->rs_rssi_ctl[1];
714 int rx_ant_conf, main_ant_conf, alt_ant_conf;
715 HAL_BOOL short_scan = AH_FALSE;
717 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
718 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
719 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
722 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
723 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
731 !!(rs->rs_rssi_ctl[2] & 0x80),
732 !!(rs->rs_rssi_ctl[2] & 0x40),
733 !!(rs->rs_rssi_ext[2] & 0x40));
737 * If LNA diversity combining isn't enabled, don't run this.
739 if (! sc->sc_dolnadiv)
743 * XXX this is ugly, but the HAL code attaches the
744 * LNA diversity to the TX antenna settings.
747 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
750 /* Record packet only when alt_rssi is positive */
751 if (main_rssi > 0 && alt_rssi > 0) {
752 antcomb->total_pkt_count++;
753 antcomb->main_total_rssi += main_rssi;
754 antcomb->alt_total_rssi += alt_rssi;
755 if (main_ant_conf == rx_ant_conf)
756 antcomb->main_recv_cnt++;
758 antcomb->alt_recv_cnt++;
761 /* Short scan check */
762 if (antcomb->scan && antcomb->alt_good) {
763 if (time_after(ticks, antcomb->scan_start_time +
764 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
765 short_scan = AH_TRUE;
767 if (antcomb->total_pkt_count ==
768 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
769 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
770 antcomb->total_pkt_count);
771 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
772 short_scan = AH_TRUE;
777 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
778 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
780 antcomb->total_pkt_count,
781 !! (rs->rs_moreaggr),
785 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
786 rs->rs_moreaggr) && !short_scan)
789 if (antcomb->total_pkt_count) {
790 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
791 antcomb->total_pkt_count);
792 main_rssi_avg = (antcomb->main_total_rssi /
793 antcomb->total_pkt_count);
794 alt_rssi_avg = (antcomb->alt_total_rssi /
795 antcomb->total_pkt_count);
798 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
800 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
801 curr_alt_set = div_ant_conf.alt_lna_conf;
802 curr_main_set = div_ant_conf.main_lna_conf;
803 curr_bias = div_ant_conf.fast_div_bias;
807 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
808 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
809 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
811 antcomb->alt_good = AH_TRUE;
813 antcomb->alt_good = AH_FALSE;
817 antcomb->scan = AH_TRUE;
818 antcomb->scan_not_start = AH_TRUE;
821 if (!antcomb->scan) {
822 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
823 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
824 /* Switch main and alt LNA */
825 div_ant_conf.main_lna_conf =
826 HAL_ANT_DIV_COMB_LNA2;
827 div_ant_conf.alt_lna_conf =
828 HAL_ANT_DIV_COMB_LNA1;
829 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
830 div_ant_conf.main_lna_conf =
831 HAL_ANT_DIV_COMB_LNA1;
832 div_ant_conf.alt_lna_conf =
833 HAL_ANT_DIV_COMB_LNA2;
837 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
838 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
839 /* Set alt to another LNA */
840 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
841 div_ant_conf.alt_lna_conf =
842 HAL_ANT_DIV_COMB_LNA1;
843 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
844 div_ant_conf.alt_lna_conf =
845 HAL_ANT_DIV_COMB_LNA2;
850 if ((alt_rssi_avg < (main_rssi_avg +
851 antcomb->lna1_lna2_delta)))
855 if (!antcomb->scan_not_start) {
856 switch (curr_alt_set) {
857 case HAL_ANT_DIV_COMB_LNA2:
858 antcomb->rssi_lna2 = alt_rssi_avg;
859 antcomb->rssi_lna1 = main_rssi_avg;
860 antcomb->scan = AH_TRUE;
862 div_ant_conf.main_lna_conf =
863 HAL_ANT_DIV_COMB_LNA1;
864 div_ant_conf.alt_lna_conf =
865 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
867 case HAL_ANT_DIV_COMB_LNA1:
868 antcomb->rssi_lna1 = alt_rssi_avg;
869 antcomb->rssi_lna2 = main_rssi_avg;
870 antcomb->scan = AH_TRUE;
872 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
873 div_ant_conf.alt_lna_conf =
874 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
876 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
877 antcomb->rssi_add = alt_rssi_avg;
878 antcomb->scan = AH_TRUE;
880 div_ant_conf.alt_lna_conf =
881 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
883 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
884 antcomb->rssi_sub = alt_rssi_avg;
885 antcomb->scan = AH_FALSE;
886 if (antcomb->rssi_lna2 >
887 (antcomb->rssi_lna1 +
888 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
889 /* use LNA2 as main LNA */
890 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
891 (antcomb->rssi_add > antcomb->rssi_sub)) {
893 div_ant_conf.main_lna_conf =
894 HAL_ANT_DIV_COMB_LNA2;
895 div_ant_conf.alt_lna_conf =
896 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
897 } else if (antcomb->rssi_sub >
898 antcomb->rssi_lna1) {
900 div_ant_conf.main_lna_conf =
901 HAL_ANT_DIV_COMB_LNA2;
902 div_ant_conf.alt_lna_conf =
903 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
906 div_ant_conf.main_lna_conf =
907 HAL_ANT_DIV_COMB_LNA2;
908 div_ant_conf.alt_lna_conf =
909 HAL_ANT_DIV_COMB_LNA1;
912 /* use LNA1 as main LNA */
913 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
914 (antcomb->rssi_add > antcomb->rssi_sub)) {
916 div_ant_conf.main_lna_conf =
917 HAL_ANT_DIV_COMB_LNA1;
918 div_ant_conf.alt_lna_conf =
919 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
920 } else if (antcomb->rssi_sub >
921 antcomb->rssi_lna1) {
923 div_ant_conf.main_lna_conf =
924 HAL_ANT_DIV_COMB_LNA1;
925 div_ant_conf.alt_lna_conf =
926 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
929 div_ant_conf.main_lna_conf =
930 HAL_ANT_DIV_COMB_LNA1;
931 div_ant_conf.alt_lna_conf =
932 HAL_ANT_DIV_COMB_LNA2;
940 if (!antcomb->alt_good) {
941 antcomb->scan_not_start = AH_FALSE;
942 /* Set alt to another LNA */
943 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
944 div_ant_conf.main_lna_conf =
945 HAL_ANT_DIV_COMB_LNA2;
946 div_ant_conf.alt_lna_conf =
947 HAL_ANT_DIV_COMB_LNA1;
948 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
949 div_ant_conf.main_lna_conf =
950 HAL_ANT_DIV_COMB_LNA1;
951 div_ant_conf.alt_lna_conf =
952 HAL_ANT_DIV_COMB_LNA2;
958 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
959 main_rssi_avg, alt_rssi_avg,
962 antcomb->quick_scan_cnt++;
966 ath_ant_div_conf_fast_divbias(&div_ant_conf);
969 ath_ant_adjust_fast_divbias(antcomb,
971 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
972 div_ant_conf.antdiv_configgroup,
975 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
977 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
978 __func__, antcomb->total_pkt_count);
980 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
981 __func__, antcomb->main_total_rssi);
982 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
983 __func__, antcomb->alt_total_rssi);
985 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
986 __func__, main_rssi_avg);
987 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
988 __func__, alt_rssi_avg);
990 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
991 __func__, antcomb->main_recv_cnt);
992 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
993 __func__, antcomb->alt_recv_cnt);
995 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
996 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
997 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
998 // if (curr_main_set != div_ant_conf.main_lna_conf)
999 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1000 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1001 // if (curr_bias != div_ant_conf.fast_div_bias)
1002 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1003 __func__, curr_bias, div_ant_conf.fast_div_bias);
1005 antcomb->scan_start_time = ticks;
1006 antcomb->total_pkt_count = 0;
1007 antcomb->main_total_rssi = 0;
1008 antcomb->alt_total_rssi = 0;
1009 antcomb->main_recv_cnt = 0;
1010 antcomb->alt_recv_cnt = 0;