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/malloc.h>
48 #include <sys/mutex.h>
49 #include <sys/errno.h>
51 #include <machine/bus.h>
52 #include <machine/resource.h>
55 #include <sys/socket.h>
58 #include <net/if_var.h>
59 #include <net/if_media.h>
60 #include <net/if_arp.h>
61 #include <net/ethernet.h> /* XXX for ether_sprintf */
63 #include <net80211/ieee80211_var.h>
68 #include <netinet/in.h>
69 #include <netinet/if_ether.h>
72 #include <dev/ath/if_athvar.h>
73 #include <dev/ath/if_ath_debug.h>
74 #include <dev/ath/if_ath_lna_div.h>
76 /* Linux compatibility macros */
78 * XXX these don't handle rounding, underflow, overflow, wrapping!
80 #define msecs_to_jiffies(a) ( (a) * hz / 1000 )
83 * Methods which are required
87 * Attach the LNA diversity to the given interface
90 ath_lna_div_attach(struct ath_softc *sc)
92 struct if_ath_ant_comb_state *ss;
93 HAL_ANT_COMB_CONFIG div_ant_conf;
95 /* Only do this if diversity is enabled */
96 if (! ath_hal_hasdivantcomb(sc->sc_ah))
99 ss = malloc(sizeof(struct if_ath_ant_comb_state),
100 M_TEMP, M_WAITOK | M_ZERO);
102 device_printf(sc->sc_dev, "%s: failed to allocate\n",
104 /* Don't fail at this point */
108 /* Fetch the hardware configuration */
109 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
110 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
112 /* Figure out what the hardware specific bits should be */
113 if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
114 (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
115 ss->lna1_lna2_delta = -9;
117 ss->lna1_lna2_delta = -3;
120 /* Let's flip this on */
128 * Detach the LNA diversity state from the given interface
131 ath_lna_div_detach(struct ath_softc *sc)
133 if (sc->sc_lna_div != NULL) {
134 free(sc->sc_lna_div, M_TEMP);
135 sc->sc_lna_div = NULL;
142 * Enable LNA diversity on the current channel if it's required.
145 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
152 * Handle ioctl requests from the diagnostic interface.
154 * The initial part of this code resembles ath_ioctl_diag();
155 * it's likely a good idea to reduce duplication between
156 * these two routines.
159 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
161 unsigned int id = ad->ad_id & ATH_DIAG_ID;
163 void *outdata = NULL;
164 u_int32_t insize = ad->ad_in_size;
165 u_int32_t outsize = ad->ad_out_size;
169 if (ad->ad_id & ATH_DIAG_IN) {
173 indata = malloc(insize, M_TEMP, M_NOWAIT);
174 if (indata == NULL) {
178 error = copyin(ad->ad_in_data, indata, insize);
182 if (ad->ad_id & ATH_DIAG_DYN) {
184 * Allocate a buffer for the results (otherwise the HAL
185 * returns a pointer to a buffer where we can read the
186 * results). Note that we depend on the HAL leaving this
187 * pointer for us to use below in reclaiming the buffer;
188 * may want to be more defensive.
190 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
191 if (outdata == NULL) {
200 if (outsize < ad->ad_out_size)
201 ad->ad_out_size = outsize;
202 if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
205 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
206 free(indata, M_TEMP);
207 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
208 free(outdata, M_TEMP);
213 * XXX need to low_rssi_thresh config from ath9k, to support CUS198
214 * antenna diversity correctly.
217 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
218 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
220 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
221 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
222 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
226 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
227 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
229 antcomb->quick_scan_cnt = 0;
231 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
232 antcomb->rssi_lna2 = main_rssi_avg;
233 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
234 antcomb->rssi_lna1 = main_rssi_avg;
236 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
237 case (0x10): /* LNA2 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_LNA1;
243 case (0x20): /* LNA1 A-B */
244 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
245 antcomb->first_quick_scan_conf =
246 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
247 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
249 case (0x21): /* LNA1 LNA2 */
250 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
251 antcomb->first_quick_scan_conf =
252 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
253 antcomb->second_quick_scan_conf =
254 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
256 case (0x12): /* LNA2 LNA1 */
257 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
258 antcomb->first_quick_scan_conf =
259 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
260 antcomb->second_quick_scan_conf =
261 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
263 case (0x13): /* LNA2 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_LNA1;
269 case (0x23): /* LNA1 A+B */
270 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
271 antcomb->first_quick_scan_conf =
272 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
273 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
281 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
282 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
283 int alt_rssi_avg, int alt_ratio)
286 switch (antcomb->quick_scan_cnt) {
288 /* set alt to main, and alt to first conf */
289 div_ant_conf->main_lna_conf = antcomb->main_conf;
290 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
293 /* set alt to main, and alt to first conf */
294 div_ant_conf->main_lna_conf = antcomb->main_conf;
295 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
296 antcomb->rssi_first = main_rssi_avg;
297 antcomb->rssi_second = alt_rssi_avg;
299 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
301 if (ath_is_alt_ant_ratio_better(alt_ratio,
302 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
303 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
304 main_rssi_avg, alt_rssi_avg,
305 antcomb->total_pkt_count))
306 antcomb->first_ratio = AH_TRUE;
308 antcomb->first_ratio = AH_FALSE;
309 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
310 if (ath_is_alt_ant_ratio_better(alt_ratio,
311 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
312 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
313 main_rssi_avg, alt_rssi_avg,
314 antcomb->total_pkt_count))
315 antcomb->first_ratio = AH_TRUE;
317 antcomb->first_ratio = AH_FALSE;
319 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
320 (alt_rssi_avg > main_rssi_avg +
321 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
322 (alt_rssi_avg > main_rssi_avg)) &&
323 (antcomb->total_pkt_count > 50))
324 antcomb->first_ratio = AH_TRUE;
326 antcomb->first_ratio = AH_FALSE;
330 antcomb->alt_good = AH_FALSE;
331 antcomb->scan_not_start = AH_FALSE;
332 antcomb->scan = AH_FALSE;
333 antcomb->rssi_first = main_rssi_avg;
334 antcomb->rssi_third = alt_rssi_avg;
336 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
337 antcomb->rssi_lna1 = alt_rssi_avg;
338 else if (antcomb->second_quick_scan_conf ==
339 HAL_ANT_DIV_COMB_LNA2)
340 antcomb->rssi_lna2 = alt_rssi_avg;
341 else if (antcomb->second_quick_scan_conf ==
342 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
343 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
344 antcomb->rssi_lna2 = main_rssi_avg;
345 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
346 antcomb->rssi_lna1 = main_rssi_avg;
349 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
350 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
351 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
353 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
355 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
356 if (ath_is_alt_ant_ratio_better(alt_ratio,
357 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
358 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
359 main_rssi_avg, alt_rssi_avg,
360 antcomb->total_pkt_count))
361 antcomb->second_ratio = AH_TRUE;
363 antcomb->second_ratio = AH_FALSE;
364 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
365 if (ath_is_alt_ant_ratio_better(alt_ratio,
366 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
367 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
368 main_rssi_avg, alt_rssi_avg,
369 antcomb->total_pkt_count))
370 antcomb->second_ratio = AH_TRUE;
372 antcomb->second_ratio = AH_FALSE;
374 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
375 (alt_rssi_avg > main_rssi_avg +
376 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
377 (alt_rssi_avg > main_rssi_avg)) &&
378 (antcomb->total_pkt_count > 50))
379 antcomb->second_ratio = AH_TRUE;
381 antcomb->second_ratio = AH_FALSE;
384 /* set alt to the conf with maximun ratio */
385 if (antcomb->first_ratio && antcomb->second_ratio) {
386 if (antcomb->rssi_second > antcomb->rssi_third) {
388 if ((antcomb->first_quick_scan_conf ==
389 HAL_ANT_DIV_COMB_LNA1) ||
390 (antcomb->first_quick_scan_conf ==
391 HAL_ANT_DIV_COMB_LNA2))
392 /* Set alt LNA1 or LNA2*/
393 if (div_ant_conf->main_lna_conf ==
394 HAL_ANT_DIV_COMB_LNA2)
395 div_ant_conf->alt_lna_conf =
396 HAL_ANT_DIV_COMB_LNA1;
398 div_ant_conf->alt_lna_conf =
399 HAL_ANT_DIV_COMB_LNA2;
401 /* Set alt to A+B or A-B */
402 div_ant_conf->alt_lna_conf =
403 antcomb->first_quick_scan_conf;
404 } else if ((antcomb->second_quick_scan_conf ==
405 HAL_ANT_DIV_COMB_LNA1) ||
406 (antcomb->second_quick_scan_conf ==
407 HAL_ANT_DIV_COMB_LNA2)) {
408 /* Set alt LNA1 or LNA2 */
409 if (div_ant_conf->main_lna_conf ==
410 HAL_ANT_DIV_COMB_LNA2)
411 div_ant_conf->alt_lna_conf =
412 HAL_ANT_DIV_COMB_LNA1;
414 div_ant_conf->alt_lna_conf =
415 HAL_ANT_DIV_COMB_LNA2;
417 /* Set alt to A+B or A-B */
418 div_ant_conf->alt_lna_conf =
419 antcomb->second_quick_scan_conf;
421 } else if (antcomb->first_ratio) {
423 if ((antcomb->first_quick_scan_conf ==
424 HAL_ANT_DIV_COMB_LNA1) ||
425 (antcomb->first_quick_scan_conf ==
426 HAL_ANT_DIV_COMB_LNA2))
427 /* Set alt LNA1 or LNA2 */
428 if (div_ant_conf->main_lna_conf ==
429 HAL_ANT_DIV_COMB_LNA2)
430 div_ant_conf->alt_lna_conf =
431 HAL_ANT_DIV_COMB_LNA1;
433 div_ant_conf->alt_lna_conf =
434 HAL_ANT_DIV_COMB_LNA2;
436 /* Set alt to A+B or A-B */
437 div_ant_conf->alt_lna_conf =
438 antcomb->first_quick_scan_conf;
439 } else if (antcomb->second_ratio) {
441 if ((antcomb->second_quick_scan_conf ==
442 HAL_ANT_DIV_COMB_LNA1) ||
443 (antcomb->second_quick_scan_conf ==
444 HAL_ANT_DIV_COMB_LNA2))
445 /* Set alt LNA1 or LNA2 */
446 if (div_ant_conf->main_lna_conf ==
447 HAL_ANT_DIV_COMB_LNA2)
448 div_ant_conf->alt_lna_conf =
449 HAL_ANT_DIV_COMB_LNA1;
451 div_ant_conf->alt_lna_conf =
452 HAL_ANT_DIV_COMB_LNA2;
454 /* Set alt to A+B or A-B */
455 div_ant_conf->alt_lna_conf =
456 antcomb->second_quick_scan_conf;
458 /* main is largest */
459 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
460 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
461 /* Set alt LNA1 or LNA2 */
462 if (div_ant_conf->main_lna_conf ==
463 HAL_ANT_DIV_COMB_LNA2)
464 div_ant_conf->alt_lna_conf =
465 HAL_ANT_DIV_COMB_LNA1;
467 div_ant_conf->alt_lna_conf =
468 HAL_ANT_DIV_COMB_LNA2;
470 /* Set alt to A+B or A-B */
471 div_ant_conf->alt_lna_conf = antcomb->main_conf;
480 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
481 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
482 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
485 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
486 switch ((pdiv_ant_conf->main_lna_conf << 4)
487 | pdiv_ant_conf->alt_lna_conf) {
488 case (0x01): //A-B LNA2
489 pdiv_ant_conf->fast_div_bias = 0x1;
490 pdiv_ant_conf->main_gaintb = 0;
491 pdiv_ant_conf->alt_gaintb = 0;
493 case (0x02): //A-B LNA1
494 pdiv_ant_conf->fast_div_bias = 0x1;
495 pdiv_ant_conf->main_gaintb = 0;
496 pdiv_ant_conf->alt_gaintb = 0;
498 case (0x03): //A-B A+B
499 pdiv_ant_conf->fast_div_bias = 0x1;
500 pdiv_ant_conf->main_gaintb = 0;
501 pdiv_ant_conf->alt_gaintb = 0;
503 case (0x10): //LNA2 A-B
504 if ((antcomb->scan == 0)
505 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
506 pdiv_ant_conf->fast_div_bias = 0x3f;
508 pdiv_ant_conf->fast_div_bias = 0x1;
510 pdiv_ant_conf->main_gaintb = 0;
511 pdiv_ant_conf->alt_gaintb = 0;
513 case (0x12): //LNA2 LNA1
514 pdiv_ant_conf->fast_div_bias = 0x1;
515 pdiv_ant_conf->main_gaintb = 0;
516 pdiv_ant_conf->alt_gaintb = 0;
518 case (0x13): //LNA2 A+B
519 if ((antcomb->scan == 0)
520 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
521 pdiv_ant_conf->fast_div_bias = 0x3f;
523 pdiv_ant_conf->fast_div_bias = 0x1;
525 pdiv_ant_conf->main_gaintb = 0;
526 pdiv_ant_conf->alt_gaintb = 0;
528 case (0x20): //LNA1 A-B
529 if ((antcomb->scan == 0)
530 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
531 pdiv_ant_conf->fast_div_bias = 0x3f;
533 pdiv_ant_conf->fast_div_bias = 0x1;
535 pdiv_ant_conf->main_gaintb = 0;
536 pdiv_ant_conf->alt_gaintb = 0;
538 case (0x21): //LNA1 LNA2
539 pdiv_ant_conf->fast_div_bias = 0x1;
540 pdiv_ant_conf->main_gaintb = 0;
541 pdiv_ant_conf->alt_gaintb = 0;
543 case (0x23): //LNA1 A+B
544 if ((antcomb->scan == 0)
545 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
546 pdiv_ant_conf->fast_div_bias = 0x3f;
548 pdiv_ant_conf->fast_div_bias = 0x1;
550 pdiv_ant_conf->main_gaintb = 0;
551 pdiv_ant_conf->alt_gaintb = 0;
553 case (0x30): //A+B A-B
554 pdiv_ant_conf->fast_div_bias = 0x1;
555 pdiv_ant_conf->main_gaintb = 0;
556 pdiv_ant_conf->alt_gaintb = 0;
558 case (0x31): //A+B LNA2
559 pdiv_ant_conf->fast_div_bias = 0x1;
560 pdiv_ant_conf->main_gaintb = 0;
561 pdiv_ant_conf->alt_gaintb = 0;
563 case (0x32): //A+B LNA1
564 pdiv_ant_conf->fast_div_bias = 0x1;
565 pdiv_ant_conf->main_gaintb = 0;
566 pdiv_ant_conf->alt_gaintb = 0;
571 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
572 switch ((pdiv_ant_conf->main_lna_conf << 4)
573 | pdiv_ant_conf->alt_lna_conf) {
574 case (0x01): //A-B LNA2
575 pdiv_ant_conf->fast_div_bias = 0x1;
576 pdiv_ant_conf->main_gaintb = 0;
577 pdiv_ant_conf->alt_gaintb = 0;
579 case (0x02): //A-B LNA1
580 pdiv_ant_conf->fast_div_bias = 0x1;
581 pdiv_ant_conf->main_gaintb = 0;
582 pdiv_ant_conf->alt_gaintb = 0;
584 case (0x03): //A-B A+B
585 pdiv_ant_conf->fast_div_bias = 0x1;
586 pdiv_ant_conf->main_gaintb = 0;
587 pdiv_ant_conf->alt_gaintb = 0;
589 case (0x10): //LNA2 A-B
590 if ((antcomb->scan == 0)
591 && (alt_ratio > alt_ant_ratio_th)) {
592 pdiv_ant_conf->fast_div_bias = 0x1;
594 pdiv_ant_conf->fast_div_bias = 0x2;
596 pdiv_ant_conf->main_gaintb = 0;
597 pdiv_ant_conf->alt_gaintb = 0;
599 case (0x12): //LNA2 LNA1
600 pdiv_ant_conf->fast_div_bias = 0x1;
601 pdiv_ant_conf->main_gaintb = 0;
602 pdiv_ant_conf->alt_gaintb = 0;
604 case (0x13): //LNA2 A+B
605 if ((antcomb->scan == 0)
606 && (alt_ratio > alt_ant_ratio_th)) {
607 pdiv_ant_conf->fast_div_bias = 0x1;
609 pdiv_ant_conf->fast_div_bias = 0x2;
611 pdiv_ant_conf->main_gaintb = 0;
612 pdiv_ant_conf->alt_gaintb = 0;
614 case (0x20): //LNA1 A-B
615 if ((antcomb->scan == 0)
616 && (alt_ratio > alt_ant_ratio_th)) {
617 pdiv_ant_conf->fast_div_bias = 0x1;
619 pdiv_ant_conf->fast_div_bias = 0x2;
621 pdiv_ant_conf->main_gaintb = 0;
622 pdiv_ant_conf->alt_gaintb = 0;
624 case (0x21): //LNA1 LNA2
625 pdiv_ant_conf->fast_div_bias = 0x1;
626 pdiv_ant_conf->main_gaintb = 0;
627 pdiv_ant_conf->alt_gaintb = 0;
629 case (0x23): //LNA1 A+B
630 if ((antcomb->scan == 0)
631 && (alt_ratio > alt_ant_ratio_th)) {
632 pdiv_ant_conf->fast_div_bias = 0x1;
634 pdiv_ant_conf->fast_div_bias = 0x2;
636 pdiv_ant_conf->main_gaintb = 0;
637 pdiv_ant_conf->alt_gaintb = 0;
639 case (0x30): //A+B A-B
640 pdiv_ant_conf->fast_div_bias = 0x1;
641 pdiv_ant_conf->main_gaintb = 0;
642 pdiv_ant_conf->alt_gaintb = 0;
644 case (0x31): //A+B LNA2
645 pdiv_ant_conf->fast_div_bias = 0x1;
646 pdiv_ant_conf->main_gaintb = 0;
647 pdiv_ant_conf->alt_gaintb = 0;
649 case (0x32): //A+B LNA1
650 pdiv_ant_conf->fast_div_bias = 0x1;
651 pdiv_ant_conf->main_gaintb = 0;
652 pdiv_ant_conf->alt_gaintb = 0;
657 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
658 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
659 case (0x01): //A-B LNA2
660 pdiv_ant_conf->fast_div_bias = 0x3b;
662 case (0x02): //A-B LNA1
663 pdiv_ant_conf->fast_div_bias = 0x3d;
665 case (0x03): //A-B A+B
666 pdiv_ant_conf->fast_div_bias = 0x1;
668 case (0x10): //LNA2 A-B
669 pdiv_ant_conf->fast_div_bias = 0x7;
671 case (0x12): //LNA2 LNA1
672 pdiv_ant_conf->fast_div_bias = 0x2;
674 case (0x13): //LNA2 A+B
675 pdiv_ant_conf->fast_div_bias = 0x7;
677 case (0x20): //LNA1 A-B
678 pdiv_ant_conf->fast_div_bias = 0x6;
680 case (0x21): //LNA1 LNA2
681 pdiv_ant_conf->fast_div_bias = 0x0;
683 case (0x23): //LNA1 A+B
684 pdiv_ant_conf->fast_div_bias = 0x6;
686 case (0x30): //A+B A-B
687 pdiv_ant_conf->fast_div_bias = 0x1;
689 case (0x31): //A+B LNA2
690 pdiv_ant_conf->fast_div_bias = 0x3b;
692 case (0x32): //A+B LNA1
693 pdiv_ant_conf->fast_div_bias = 0x3d;
702 * AR9485/AR933x TODO:
703 * + Select a ratio based on whether RSSI is low or not; but I need
704 * to figure out what "low_rssi_th" is sourced from.
705 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
706 * + .. and there's likely a bunch of other things to include in this.
709 /* Antenna diversity and combining */
711 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
712 unsigned long ticks, int hz)
714 HAL_ANT_COMB_CONFIG div_ant_conf;
715 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
716 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
717 int curr_main_set, curr_bias;
718 int main_rssi = rs->rs_rssi_ctl[0];
719 int alt_rssi = rs->rs_rssi_ctl[1];
720 int rx_ant_conf, main_ant_conf, alt_ant_conf;
721 HAL_BOOL short_scan = AH_FALSE;
723 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
724 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
725 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
728 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
729 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
737 !!(rs->rs_rssi_ctl[2] & 0x80),
738 !!(rs->rs_rssi_ctl[2] & 0x40),
739 !!(rs->rs_rssi_ext[2] & 0x40));
743 * If LNA diversity combining isn't enabled, don't run this.
745 if (! sc->sc_dolnadiv)
749 * XXX this is ugly, but the HAL code attaches the
750 * LNA diversity to the TX antenna settings.
753 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
756 /* Record packet only when alt_rssi is positive */
757 if (main_rssi > 0 && alt_rssi > 0) {
758 antcomb->total_pkt_count++;
759 antcomb->main_total_rssi += main_rssi;
760 antcomb->alt_total_rssi += alt_rssi;
761 if (main_ant_conf == rx_ant_conf)
762 antcomb->main_recv_cnt++;
764 antcomb->alt_recv_cnt++;
767 /* Short scan check */
768 if (antcomb->scan && antcomb->alt_good) {
769 if (ieee80211_time_after(ticks, antcomb->scan_start_time +
770 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
771 short_scan = AH_TRUE;
773 if (antcomb->total_pkt_count ==
774 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
775 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
776 antcomb->total_pkt_count);
777 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
778 short_scan = AH_TRUE;
783 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
784 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
786 antcomb->total_pkt_count,
787 !! (rs->rs_moreaggr),
791 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
792 rs->rs_moreaggr) && !short_scan)
795 if (antcomb->total_pkt_count) {
796 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
797 antcomb->total_pkt_count);
798 main_rssi_avg = (antcomb->main_total_rssi /
799 antcomb->total_pkt_count);
800 alt_rssi_avg = (antcomb->alt_total_rssi /
801 antcomb->total_pkt_count);
804 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
806 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
807 curr_alt_set = div_ant_conf.alt_lna_conf;
808 curr_main_set = div_ant_conf.main_lna_conf;
809 curr_bias = div_ant_conf.fast_div_bias;
813 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
814 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
815 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
817 antcomb->alt_good = AH_TRUE;
819 antcomb->alt_good = AH_FALSE;
823 antcomb->scan = AH_TRUE;
824 antcomb->scan_not_start = AH_TRUE;
827 if (!antcomb->scan) {
828 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
829 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
830 /* Switch main and alt LNA */
831 div_ant_conf.main_lna_conf =
832 HAL_ANT_DIV_COMB_LNA2;
833 div_ant_conf.alt_lna_conf =
834 HAL_ANT_DIV_COMB_LNA1;
835 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
836 div_ant_conf.main_lna_conf =
837 HAL_ANT_DIV_COMB_LNA1;
838 div_ant_conf.alt_lna_conf =
839 HAL_ANT_DIV_COMB_LNA2;
843 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
844 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
845 /* Set alt to another LNA */
846 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
847 div_ant_conf.alt_lna_conf =
848 HAL_ANT_DIV_COMB_LNA1;
849 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
850 div_ant_conf.alt_lna_conf =
851 HAL_ANT_DIV_COMB_LNA2;
856 if ((alt_rssi_avg < (main_rssi_avg +
857 antcomb->lna1_lna2_delta)))
861 if (!antcomb->scan_not_start) {
862 switch (curr_alt_set) {
863 case HAL_ANT_DIV_COMB_LNA2:
864 antcomb->rssi_lna2 = alt_rssi_avg;
865 antcomb->rssi_lna1 = main_rssi_avg;
866 antcomb->scan = AH_TRUE;
868 div_ant_conf.main_lna_conf =
869 HAL_ANT_DIV_COMB_LNA1;
870 div_ant_conf.alt_lna_conf =
871 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
873 case HAL_ANT_DIV_COMB_LNA1:
874 antcomb->rssi_lna1 = alt_rssi_avg;
875 antcomb->rssi_lna2 = main_rssi_avg;
876 antcomb->scan = AH_TRUE;
878 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
879 div_ant_conf.alt_lna_conf =
880 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
882 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
883 antcomb->rssi_add = alt_rssi_avg;
884 antcomb->scan = AH_TRUE;
886 div_ant_conf.alt_lna_conf =
887 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
889 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
890 antcomb->rssi_sub = alt_rssi_avg;
891 antcomb->scan = AH_FALSE;
892 if (antcomb->rssi_lna2 >
893 (antcomb->rssi_lna1 +
894 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
895 /* use LNA2 as main LNA */
896 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
897 (antcomb->rssi_add > antcomb->rssi_sub)) {
899 div_ant_conf.main_lna_conf =
900 HAL_ANT_DIV_COMB_LNA2;
901 div_ant_conf.alt_lna_conf =
902 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
903 } else if (antcomb->rssi_sub >
904 antcomb->rssi_lna1) {
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_MINUS_LNA2;
912 div_ant_conf.main_lna_conf =
913 HAL_ANT_DIV_COMB_LNA2;
914 div_ant_conf.alt_lna_conf =
915 HAL_ANT_DIV_COMB_LNA1;
918 /* use LNA1 as main LNA */
919 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
920 (antcomb->rssi_add > antcomb->rssi_sub)) {
922 div_ant_conf.main_lna_conf =
923 HAL_ANT_DIV_COMB_LNA1;
924 div_ant_conf.alt_lna_conf =
925 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
926 } else if (antcomb->rssi_sub >
927 antcomb->rssi_lna1) {
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_LNA1_MINUS_LNA2;
935 div_ant_conf.main_lna_conf =
936 HAL_ANT_DIV_COMB_LNA1;
937 div_ant_conf.alt_lna_conf =
938 HAL_ANT_DIV_COMB_LNA2;
946 if (!antcomb->alt_good) {
947 antcomb->scan_not_start = AH_FALSE;
948 /* Set alt to another LNA */
949 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
950 div_ant_conf.main_lna_conf =
951 HAL_ANT_DIV_COMB_LNA2;
952 div_ant_conf.alt_lna_conf =
953 HAL_ANT_DIV_COMB_LNA1;
954 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
955 div_ant_conf.main_lna_conf =
956 HAL_ANT_DIV_COMB_LNA1;
957 div_ant_conf.alt_lna_conf =
958 HAL_ANT_DIV_COMB_LNA2;
964 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
965 main_rssi_avg, alt_rssi_avg,
968 antcomb->quick_scan_cnt++;
972 ath_ant_div_conf_fast_divbias(&div_ant_conf);
975 ath_ant_adjust_fast_divbias(antcomb,
977 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
978 div_ant_conf.antdiv_configgroup,
981 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
983 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
984 __func__, antcomb->total_pkt_count);
986 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
987 __func__, antcomb->main_total_rssi);
988 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
989 __func__, antcomb->alt_total_rssi);
991 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
992 __func__, main_rssi_avg);
993 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
994 __func__, alt_rssi_avg);
996 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
997 __func__, antcomb->main_recv_cnt);
998 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
999 __func__, antcomb->alt_recv_cnt);
1001 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
1002 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1003 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1004 // if (curr_main_set != div_ant_conf.main_lna_conf)
1005 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1006 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1007 // if (curr_bias != div_ant_conf.fast_div_bias)
1008 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1009 __func__, curr_bias, div_ant_conf.fast_div_bias);
1011 antcomb->scan_start_time = ticks;
1012 antcomb->total_pkt_count = 0;
1013 antcomb->main_total_rssi = 0;
1014 antcomb->alt_total_rssi = 0;
1015 antcomb->main_recv_cnt = 0;
1016 antcomb->alt_recv_cnt = 0;