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, MERCHANTABILITY
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/errno.h>
50 #if defined(__DragonFly__)
53 #include <machine/bus.h>
54 #include <machine/resource.h>
58 #include <sys/socket.h>
61 #include <net/if_var.h>
62 #include <net/if_media.h>
63 #include <net/if_arp.h>
64 #include <net/ethernet.h> /* XXX for ether_sprintf */
66 #include <netproto/802_11/ieee80211_var.h>
71 #include <netinet/in.h>
72 #include <netinet/if_ether.h>
75 #include <dev/netif/ath/ath/if_athvar.h>
76 #include <dev/netif/ath/ath/if_ath_debug.h>
77 #include <dev/netif/ath/ath/if_ath_lna_div.h>
79 /* Linux compatibility macros */
81 * XXX these don't handle rounding, underflow, overflow, wrapping!
83 #define msecs_to_jiffies(a) ( (a) * hz / 1000 )
86 * Methods which are required
90 * Attach the LNA diversity to the given interface
93 ath_lna_div_attach(struct ath_softc *sc)
95 struct if_ath_ant_comb_state *ss;
96 HAL_ANT_COMB_CONFIG div_ant_conf;
98 /* Only do this if diversity is enabled */
99 if (! ath_hal_hasdivantcomb(sc->sc_ah))
102 ss = kmalloc(sizeof(struct if_ath_ant_comb_state),
103 M_TEMP, M_WAITOK | M_ZERO);
105 device_printf(sc->sc_dev, "%s: failed to allocate\n",
107 /* Don't fail at this point */
111 /* Fetch the hardware configuration */
112 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
113 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
115 /* Figure out what the hardware specific bits should be */
116 if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
117 (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
118 ss->lna1_lna2_delta = -9;
120 ss->lna1_lna2_delta = -3;
123 /* Let's flip this on */
131 * Detach the LNA diversity state from the given interface
134 ath_lna_div_detach(struct ath_softc *sc)
136 if (sc->sc_lna_div != NULL) {
137 kfree(sc->sc_lna_div, M_TEMP);
138 sc->sc_lna_div = NULL;
145 * Enable LNA diversity on the current channel if it's required.
148 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
155 * Handle ioctl requests from the diagnostic interface.
157 * The initial part of this code resembles ath_ioctl_diag();
158 * it's likely a good idea to reduce duplication between
159 * these two routines.
162 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
164 unsigned int id = ad->ad_id & ATH_DIAG_ID;
166 void *outdata = NULL;
167 u_int32_t insize = ad->ad_in_size;
168 u_int32_t outsize = ad->ad_out_size;
172 if (ad->ad_id & ATH_DIAG_IN) {
176 indata = kmalloc(insize, M_TEMP, M_INTWAIT);
177 if (indata == NULL) {
181 error = copyin(ad->ad_in_data, indata, insize);
185 if (ad->ad_id & ATH_DIAG_DYN) {
187 * Allocate a buffer for the results (otherwise the HAL
188 * returns a pointer to a buffer where we can read the
189 * results). Note that we depend on the HAL leaving this
190 * pointer for us to use below in reclaiming the buffer;
191 * may want to be more defensive.
193 outdata = kmalloc(outsize, M_TEMP, M_INTWAIT);
194 if (outdata == NULL) {
203 if (outsize < ad->ad_out_size)
204 ad->ad_out_size = outsize;
205 if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
208 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
209 kfree(indata, M_TEMP);
210 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
211 kfree(outdata, M_TEMP);
216 * XXX need to low_rssi_thresh config from ath9k, to support CUS198
217 * antenna diversity correctly.
220 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
221 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
223 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
224 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
225 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
229 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
230 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
232 antcomb->quick_scan_cnt = 0;
234 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
235 antcomb->rssi_lna2 = main_rssi_avg;
236 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
237 antcomb->rssi_lna1 = main_rssi_avg;
239 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
240 case (0x10): /* LNA2 A-B */
241 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
242 antcomb->first_quick_scan_conf =
243 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
244 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
246 case (0x20): /* LNA1 A-B */
247 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
248 antcomb->first_quick_scan_conf =
249 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
250 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
252 case (0x21): /* LNA1 LNA2 */
253 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
254 antcomb->first_quick_scan_conf =
255 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
256 antcomb->second_quick_scan_conf =
257 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
259 case (0x12): /* LNA2 LNA1 */
260 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
261 antcomb->first_quick_scan_conf =
262 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
263 antcomb->second_quick_scan_conf =
264 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
266 case (0x13): /* LNA2 A+B */
267 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
268 antcomb->first_quick_scan_conf =
269 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
270 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
272 case (0x23): /* LNA1 A+B */
273 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
274 antcomb->first_quick_scan_conf =
275 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
276 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
284 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
285 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
286 int alt_rssi_avg, int alt_ratio)
289 switch (antcomb->quick_scan_cnt) {
291 /* set alt to main, and alt to first conf */
292 div_ant_conf->main_lna_conf = antcomb->main_conf;
293 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
296 /* set alt to main, and alt to first conf */
297 div_ant_conf->main_lna_conf = antcomb->main_conf;
298 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
299 antcomb->rssi_first = main_rssi_avg;
300 antcomb->rssi_second = alt_rssi_avg;
302 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
304 if (ath_is_alt_ant_ratio_better(alt_ratio,
305 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
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;
312 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
313 if (ath_is_alt_ant_ratio_better(alt_ratio,
314 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
315 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
316 main_rssi_avg, alt_rssi_avg,
317 antcomb->total_pkt_count))
318 antcomb->first_ratio = AH_TRUE;
320 antcomb->first_ratio = AH_FALSE;
322 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
323 (alt_rssi_avg > main_rssi_avg +
324 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
325 (alt_rssi_avg > main_rssi_avg)) &&
326 (antcomb->total_pkt_count > 50))
327 antcomb->first_ratio = AH_TRUE;
329 antcomb->first_ratio = AH_FALSE;
333 antcomb->alt_good = AH_FALSE;
334 antcomb->scan_not_start = AH_FALSE;
335 antcomb->scan = AH_FALSE;
336 antcomb->rssi_first = main_rssi_avg;
337 antcomb->rssi_third = alt_rssi_avg;
339 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
340 antcomb->rssi_lna1 = alt_rssi_avg;
341 else if (antcomb->second_quick_scan_conf ==
342 HAL_ANT_DIV_COMB_LNA2)
343 antcomb->rssi_lna2 = alt_rssi_avg;
344 else if (antcomb->second_quick_scan_conf ==
345 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
346 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
347 antcomb->rssi_lna2 = main_rssi_avg;
348 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
349 antcomb->rssi_lna1 = main_rssi_avg;
352 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
353 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
354 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
356 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
358 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
359 if (ath_is_alt_ant_ratio_better(alt_ratio,
360 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
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;
367 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
368 if (ath_is_alt_ant_ratio_better(alt_ratio,
369 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
370 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
371 main_rssi_avg, alt_rssi_avg,
372 antcomb->total_pkt_count))
373 antcomb->second_ratio = AH_TRUE;
375 antcomb->second_ratio = AH_FALSE;
377 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
378 (alt_rssi_avg > main_rssi_avg +
379 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
380 (alt_rssi_avg > main_rssi_avg)) &&
381 (antcomb->total_pkt_count > 50))
382 antcomb->second_ratio = AH_TRUE;
384 antcomb->second_ratio = AH_FALSE;
387 /* set alt to the conf with maximun ratio */
388 if (antcomb->first_ratio && antcomb->second_ratio) {
389 if (antcomb->rssi_second > antcomb->rssi_third) {
391 if ((antcomb->first_quick_scan_conf ==
392 HAL_ANT_DIV_COMB_LNA1) ||
393 (antcomb->first_quick_scan_conf ==
394 HAL_ANT_DIV_COMB_LNA2))
395 /* Set alt LNA1 or LNA2*/
396 if (div_ant_conf->main_lna_conf ==
397 HAL_ANT_DIV_COMB_LNA2)
398 div_ant_conf->alt_lna_conf =
399 HAL_ANT_DIV_COMB_LNA1;
401 div_ant_conf->alt_lna_conf =
402 HAL_ANT_DIV_COMB_LNA2;
404 /* Set alt to A+B or A-B */
405 div_ant_conf->alt_lna_conf =
406 antcomb->first_quick_scan_conf;
407 } else if ((antcomb->second_quick_scan_conf ==
408 HAL_ANT_DIV_COMB_LNA1) ||
409 (antcomb->second_quick_scan_conf ==
410 HAL_ANT_DIV_COMB_LNA2)) {
411 /* Set alt LNA1 or LNA2 */
412 if (div_ant_conf->main_lna_conf ==
413 HAL_ANT_DIV_COMB_LNA2)
414 div_ant_conf->alt_lna_conf =
415 HAL_ANT_DIV_COMB_LNA1;
417 div_ant_conf->alt_lna_conf =
418 HAL_ANT_DIV_COMB_LNA2;
420 /* Set alt to A+B or A-B */
421 div_ant_conf->alt_lna_conf =
422 antcomb->second_quick_scan_conf;
424 } else if (antcomb->first_ratio) {
426 if ((antcomb->first_quick_scan_conf ==
427 HAL_ANT_DIV_COMB_LNA1) ||
428 (antcomb->first_quick_scan_conf ==
429 HAL_ANT_DIV_COMB_LNA2))
430 /* Set alt LNA1 or LNA2 */
431 if (div_ant_conf->main_lna_conf ==
432 HAL_ANT_DIV_COMB_LNA2)
433 div_ant_conf->alt_lna_conf =
434 HAL_ANT_DIV_COMB_LNA1;
436 div_ant_conf->alt_lna_conf =
437 HAL_ANT_DIV_COMB_LNA2;
439 /* Set alt to A+B or A-B */
440 div_ant_conf->alt_lna_conf =
441 antcomb->first_quick_scan_conf;
442 } else if (antcomb->second_ratio) {
444 if ((antcomb->second_quick_scan_conf ==
445 HAL_ANT_DIV_COMB_LNA1) ||
446 (antcomb->second_quick_scan_conf ==
447 HAL_ANT_DIV_COMB_LNA2))
448 /* Set alt LNA1 or LNA2 */
449 if (div_ant_conf->main_lna_conf ==
450 HAL_ANT_DIV_COMB_LNA2)
451 div_ant_conf->alt_lna_conf =
452 HAL_ANT_DIV_COMB_LNA1;
454 div_ant_conf->alt_lna_conf =
455 HAL_ANT_DIV_COMB_LNA2;
457 /* Set alt to A+B or A-B */
458 div_ant_conf->alt_lna_conf =
459 antcomb->second_quick_scan_conf;
461 /* main is largest */
462 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
463 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
464 /* Set alt LNA1 or LNA2 */
465 if (div_ant_conf->main_lna_conf ==
466 HAL_ANT_DIV_COMB_LNA2)
467 div_ant_conf->alt_lna_conf =
468 HAL_ANT_DIV_COMB_LNA1;
470 div_ant_conf->alt_lna_conf =
471 HAL_ANT_DIV_COMB_LNA2;
473 /* Set alt to A+B or A-B */
474 div_ant_conf->alt_lna_conf = antcomb->main_conf;
483 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
484 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
485 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
488 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
489 switch ((pdiv_ant_conf->main_lna_conf << 4)
490 | pdiv_ant_conf->alt_lna_conf) {
491 case (0x01): //A-B LNA2
492 pdiv_ant_conf->fast_div_bias = 0x1;
493 pdiv_ant_conf->main_gaintb = 0;
494 pdiv_ant_conf->alt_gaintb = 0;
496 case (0x02): //A-B LNA1
497 pdiv_ant_conf->fast_div_bias = 0x1;
498 pdiv_ant_conf->main_gaintb = 0;
499 pdiv_ant_conf->alt_gaintb = 0;
501 case (0x03): //A-B A+B
502 pdiv_ant_conf->fast_div_bias = 0x1;
503 pdiv_ant_conf->main_gaintb = 0;
504 pdiv_ant_conf->alt_gaintb = 0;
506 case (0x10): //LNA2 A-B
507 if ((antcomb->scan == 0)
508 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
509 pdiv_ant_conf->fast_div_bias = 0x3f;
511 pdiv_ant_conf->fast_div_bias = 0x1;
513 pdiv_ant_conf->main_gaintb = 0;
514 pdiv_ant_conf->alt_gaintb = 0;
516 case (0x12): //LNA2 LNA1
517 pdiv_ant_conf->fast_div_bias = 0x1;
518 pdiv_ant_conf->main_gaintb = 0;
519 pdiv_ant_conf->alt_gaintb = 0;
521 case (0x13): //LNA2 A+B
522 if ((antcomb->scan == 0)
523 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
524 pdiv_ant_conf->fast_div_bias = 0x3f;
526 pdiv_ant_conf->fast_div_bias = 0x1;
528 pdiv_ant_conf->main_gaintb = 0;
529 pdiv_ant_conf->alt_gaintb = 0;
531 case (0x20): //LNA1 A-B
532 if ((antcomb->scan == 0)
533 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
534 pdiv_ant_conf->fast_div_bias = 0x3f;
536 pdiv_ant_conf->fast_div_bias = 0x1;
538 pdiv_ant_conf->main_gaintb = 0;
539 pdiv_ant_conf->alt_gaintb = 0;
541 case (0x21): //LNA1 LNA2
542 pdiv_ant_conf->fast_div_bias = 0x1;
543 pdiv_ant_conf->main_gaintb = 0;
544 pdiv_ant_conf->alt_gaintb = 0;
546 case (0x23): //LNA1 A+B
547 if ((antcomb->scan == 0)
548 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
549 pdiv_ant_conf->fast_div_bias = 0x3f;
551 pdiv_ant_conf->fast_div_bias = 0x1;
553 pdiv_ant_conf->main_gaintb = 0;
554 pdiv_ant_conf->alt_gaintb = 0;
556 case (0x30): //A+B A-B
557 pdiv_ant_conf->fast_div_bias = 0x1;
558 pdiv_ant_conf->main_gaintb = 0;
559 pdiv_ant_conf->alt_gaintb = 0;
561 case (0x31): //A+B LNA2
562 pdiv_ant_conf->fast_div_bias = 0x1;
563 pdiv_ant_conf->main_gaintb = 0;
564 pdiv_ant_conf->alt_gaintb = 0;
566 case (0x32): //A+B LNA1
567 pdiv_ant_conf->fast_div_bias = 0x1;
568 pdiv_ant_conf->main_gaintb = 0;
569 pdiv_ant_conf->alt_gaintb = 0;
574 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
575 switch ((pdiv_ant_conf->main_lna_conf << 4)
576 | pdiv_ant_conf->alt_lna_conf) {
577 case (0x01): //A-B LNA2
578 pdiv_ant_conf->fast_div_bias = 0x1;
579 pdiv_ant_conf->main_gaintb = 0;
580 pdiv_ant_conf->alt_gaintb = 0;
582 case (0x02): //A-B LNA1
583 pdiv_ant_conf->fast_div_bias = 0x1;
584 pdiv_ant_conf->main_gaintb = 0;
585 pdiv_ant_conf->alt_gaintb = 0;
587 case (0x03): //A-B A+B
588 pdiv_ant_conf->fast_div_bias = 0x1;
589 pdiv_ant_conf->main_gaintb = 0;
590 pdiv_ant_conf->alt_gaintb = 0;
592 case (0x10): //LNA2 A-B
593 if ((antcomb->scan == 0)
594 && (alt_ratio > alt_ant_ratio_th)) {
595 pdiv_ant_conf->fast_div_bias = 0x1;
597 pdiv_ant_conf->fast_div_bias = 0x2;
599 pdiv_ant_conf->main_gaintb = 0;
600 pdiv_ant_conf->alt_gaintb = 0;
602 case (0x12): //LNA2 LNA1
603 pdiv_ant_conf->fast_div_bias = 0x1;
604 pdiv_ant_conf->main_gaintb = 0;
605 pdiv_ant_conf->alt_gaintb = 0;
607 case (0x13): //LNA2 A+B
608 if ((antcomb->scan == 0)
609 && (alt_ratio > alt_ant_ratio_th)) {
610 pdiv_ant_conf->fast_div_bias = 0x1;
612 pdiv_ant_conf->fast_div_bias = 0x2;
614 pdiv_ant_conf->main_gaintb = 0;
615 pdiv_ant_conf->alt_gaintb = 0;
617 case (0x20): //LNA1 A-B
618 if ((antcomb->scan == 0)
619 && (alt_ratio > alt_ant_ratio_th)) {
620 pdiv_ant_conf->fast_div_bias = 0x1;
622 pdiv_ant_conf->fast_div_bias = 0x2;
624 pdiv_ant_conf->main_gaintb = 0;
625 pdiv_ant_conf->alt_gaintb = 0;
627 case (0x21): //LNA1 LNA2
628 pdiv_ant_conf->fast_div_bias = 0x1;
629 pdiv_ant_conf->main_gaintb = 0;
630 pdiv_ant_conf->alt_gaintb = 0;
632 case (0x23): //LNA1 A+B
633 if ((antcomb->scan == 0)
634 && (alt_ratio > alt_ant_ratio_th)) {
635 pdiv_ant_conf->fast_div_bias = 0x1;
637 pdiv_ant_conf->fast_div_bias = 0x2;
639 pdiv_ant_conf->main_gaintb = 0;
640 pdiv_ant_conf->alt_gaintb = 0;
642 case (0x30): //A+B A-B
643 pdiv_ant_conf->fast_div_bias = 0x1;
644 pdiv_ant_conf->main_gaintb = 0;
645 pdiv_ant_conf->alt_gaintb = 0;
647 case (0x31): //A+B LNA2
648 pdiv_ant_conf->fast_div_bias = 0x1;
649 pdiv_ant_conf->main_gaintb = 0;
650 pdiv_ant_conf->alt_gaintb = 0;
652 case (0x32): //A+B LNA1
653 pdiv_ant_conf->fast_div_bias = 0x1;
654 pdiv_ant_conf->main_gaintb = 0;
655 pdiv_ant_conf->alt_gaintb = 0;
660 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
661 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
662 case (0x01): //A-B LNA2
663 pdiv_ant_conf->fast_div_bias = 0x3b;
665 case (0x02): //A-B LNA1
666 pdiv_ant_conf->fast_div_bias = 0x3d;
668 case (0x03): //A-B A+B
669 pdiv_ant_conf->fast_div_bias = 0x1;
671 case (0x10): //LNA2 A-B
672 pdiv_ant_conf->fast_div_bias = 0x7;
674 case (0x12): //LNA2 LNA1
675 pdiv_ant_conf->fast_div_bias = 0x2;
677 case (0x13): //LNA2 A+B
678 pdiv_ant_conf->fast_div_bias = 0x7;
680 case (0x20): //LNA1 A-B
681 pdiv_ant_conf->fast_div_bias = 0x6;
683 case (0x21): //LNA1 LNA2
684 pdiv_ant_conf->fast_div_bias = 0x0;
686 case (0x23): //LNA1 A+B
687 pdiv_ant_conf->fast_div_bias = 0x6;
689 case (0x30): //A+B A-B
690 pdiv_ant_conf->fast_div_bias = 0x1;
692 case (0x31): //A+B LNA2
693 pdiv_ant_conf->fast_div_bias = 0x3b;
695 case (0x32): //A+B LNA1
696 pdiv_ant_conf->fast_div_bias = 0x3d;
705 * AR9485/AR933x TODO:
706 * + Select a ratio based on whether RSSI is low or not; but I need
707 * to figure out what "low_rssi_th" is sourced from.
708 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
709 * + .. and there's likely a bunch of other things to include in this.
712 /* Antenna diversity and combining */
714 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
715 unsigned long ticks, int hz)
717 HAL_ANT_COMB_CONFIG div_ant_conf;
718 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
719 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
720 int curr_main_set, curr_bias;
721 int main_rssi = rs->rs_rssi_ctl[0];
722 int alt_rssi = rs->rs_rssi_ctl[1];
723 int rx_ant_conf, main_ant_conf, alt_ant_conf;
724 HAL_BOOL short_scan = AH_FALSE;
726 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
727 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
728 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
731 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
732 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
740 !!(rs->rs_rssi_ctl[2] & 0x80),
741 !!(rs->rs_rssi_ctl[2] & 0x40),
742 !!(rs->rs_rssi_ext[2] & 0x40));
746 * If LNA diversity combining isn't enabled, don't run this.
748 if (! sc->sc_dolnadiv)
752 * XXX this is ugly, but the HAL code attaches the
753 * LNA diversity to the TX antenna settings.
756 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
759 /* Record packet only when alt_rssi is positive */
760 if (main_rssi > 0 && alt_rssi > 0) {
761 antcomb->total_pkt_count++;
762 antcomb->main_total_rssi += main_rssi;
763 antcomb->alt_total_rssi += alt_rssi;
764 if (main_ant_conf == rx_ant_conf)
765 antcomb->main_recv_cnt++;
767 antcomb->alt_recv_cnt++;
770 /* Short scan check */
771 if (antcomb->scan && antcomb->alt_good) {
772 if (ieee80211_time_after(ticks, antcomb->scan_start_time +
773 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
774 short_scan = AH_TRUE;
776 if (antcomb->total_pkt_count ==
777 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
778 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
779 antcomb->total_pkt_count);
780 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
781 short_scan = AH_TRUE;
786 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
787 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
789 antcomb->total_pkt_count,
790 !! (rs->rs_moreaggr),
794 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
795 rs->rs_moreaggr) && !short_scan)
798 if (antcomb->total_pkt_count) {
799 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
800 antcomb->total_pkt_count);
801 main_rssi_avg = (antcomb->main_total_rssi /
802 antcomb->total_pkt_count);
803 alt_rssi_avg = (antcomb->alt_total_rssi /
804 antcomb->total_pkt_count);
807 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
809 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
810 curr_alt_set = div_ant_conf.alt_lna_conf;
811 curr_main_set = div_ant_conf.main_lna_conf;
812 curr_bias = div_ant_conf.fast_div_bias;
816 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
817 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
818 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
820 antcomb->alt_good = AH_TRUE;
822 antcomb->alt_good = AH_FALSE;
826 antcomb->scan = AH_TRUE;
827 antcomb->scan_not_start = AH_TRUE;
830 if (!antcomb->scan) {
831 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
832 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
833 /* Switch main and alt LNA */
834 div_ant_conf.main_lna_conf =
835 HAL_ANT_DIV_COMB_LNA2;
836 div_ant_conf.alt_lna_conf =
837 HAL_ANT_DIV_COMB_LNA1;
838 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
839 div_ant_conf.main_lna_conf =
840 HAL_ANT_DIV_COMB_LNA1;
841 div_ant_conf.alt_lna_conf =
842 HAL_ANT_DIV_COMB_LNA2;
846 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
847 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
848 /* Set alt to another LNA */
849 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
850 div_ant_conf.alt_lna_conf =
851 HAL_ANT_DIV_COMB_LNA1;
852 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
853 div_ant_conf.alt_lna_conf =
854 HAL_ANT_DIV_COMB_LNA2;
859 if ((alt_rssi_avg < (main_rssi_avg +
860 antcomb->lna1_lna2_delta)))
864 if (!antcomb->scan_not_start) {
865 switch (curr_alt_set) {
866 case HAL_ANT_DIV_COMB_LNA2:
867 antcomb->rssi_lna2 = alt_rssi_avg;
868 antcomb->rssi_lna1 = main_rssi_avg;
869 antcomb->scan = AH_TRUE;
871 div_ant_conf.main_lna_conf =
872 HAL_ANT_DIV_COMB_LNA1;
873 div_ant_conf.alt_lna_conf =
874 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
876 case HAL_ANT_DIV_COMB_LNA1:
877 antcomb->rssi_lna1 = alt_rssi_avg;
878 antcomb->rssi_lna2 = main_rssi_avg;
879 antcomb->scan = AH_TRUE;
881 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
882 div_ant_conf.alt_lna_conf =
883 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
885 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
886 antcomb->rssi_add = alt_rssi_avg;
887 antcomb->scan = AH_TRUE;
889 div_ant_conf.alt_lna_conf =
890 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
892 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
893 antcomb->rssi_sub = alt_rssi_avg;
894 antcomb->scan = AH_FALSE;
895 if (antcomb->rssi_lna2 >
896 (antcomb->rssi_lna1 +
897 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
898 /* use LNA2 as main LNA */
899 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
900 (antcomb->rssi_add > antcomb->rssi_sub)) {
902 div_ant_conf.main_lna_conf =
903 HAL_ANT_DIV_COMB_LNA2;
904 div_ant_conf.alt_lna_conf =
905 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
906 } else if (antcomb->rssi_sub >
907 antcomb->rssi_lna1) {
909 div_ant_conf.main_lna_conf =
910 HAL_ANT_DIV_COMB_LNA2;
911 div_ant_conf.alt_lna_conf =
912 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
915 div_ant_conf.main_lna_conf =
916 HAL_ANT_DIV_COMB_LNA2;
917 div_ant_conf.alt_lna_conf =
918 HAL_ANT_DIV_COMB_LNA1;
921 /* use LNA1 as main LNA */
922 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
923 (antcomb->rssi_add > antcomb->rssi_sub)) {
925 div_ant_conf.main_lna_conf =
926 HAL_ANT_DIV_COMB_LNA1;
927 div_ant_conf.alt_lna_conf =
928 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
929 } else if (antcomb->rssi_sub >
930 antcomb->rssi_lna1) {
932 div_ant_conf.main_lna_conf =
933 HAL_ANT_DIV_COMB_LNA1;
934 div_ant_conf.alt_lna_conf =
935 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
938 div_ant_conf.main_lna_conf =
939 HAL_ANT_DIV_COMB_LNA1;
940 div_ant_conf.alt_lna_conf =
941 HAL_ANT_DIV_COMB_LNA2;
949 if (!antcomb->alt_good) {
950 antcomb->scan_not_start = AH_FALSE;
951 /* Set alt to another LNA */
952 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
953 div_ant_conf.main_lna_conf =
954 HAL_ANT_DIV_COMB_LNA2;
955 div_ant_conf.alt_lna_conf =
956 HAL_ANT_DIV_COMB_LNA1;
957 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
958 div_ant_conf.main_lna_conf =
959 HAL_ANT_DIV_COMB_LNA1;
960 div_ant_conf.alt_lna_conf =
961 HAL_ANT_DIV_COMB_LNA2;
967 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
968 main_rssi_avg, alt_rssi_avg,
971 antcomb->quick_scan_cnt++;
975 ath_ant_div_conf_fast_divbias(&div_ant_conf);
978 ath_ant_adjust_fast_divbias(antcomb,
980 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
981 div_ant_conf.antdiv_configgroup,
984 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
986 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
987 __func__, antcomb->total_pkt_count);
989 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
990 __func__, antcomb->main_total_rssi);
991 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
992 __func__, antcomb->alt_total_rssi);
994 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
995 __func__, main_rssi_avg);
996 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
997 __func__, alt_rssi_avg);
999 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
1000 __func__, antcomb->main_recv_cnt);
1001 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
1002 __func__, antcomb->alt_recv_cnt);
1004 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
1005 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1006 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1007 // if (curr_main_set != div_ant_conf.main_lna_conf)
1008 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1009 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1010 // if (curr_bias != div_ant_conf.fast_div_bias)
1011 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1012 __func__, curr_bias, div_ant_conf.fast_div_bias);
1014 antcomb->scan_start_time = ticks;
1015 antcomb->total_pkt_count = 0;
1016 antcomb->main_total_rssi = 0;
1017 antcomb->alt_total_rssi = 0;
1018 antcomb->main_recv_cnt = 0;
1019 antcomb->alt_recv_cnt = 0;