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>
53 #include <sys/socket.h>
56 #include <net/if_var.h>
57 #include <net/if_media.h>
58 #include <net/if_arp.h>
59 #include <net/ethernet.h> /* XXX for ether_sprintf */
61 #include <netproto/802_11/ieee80211_var.h>
66 #include <netinet/in.h>
67 #include <netinet/if_ether.h>
70 #include <dev/netif/ath/ath/if_athvar.h>
71 #include <dev/netif/ath/ath/if_ath_debug.h>
72 #include <dev/netif/ath/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 = kmalloc(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 kfree(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 = kmalloc(insize, M_TEMP, M_INTWAIT);
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 = kmalloc(outsize, M_TEMP, M_INTWAIT);
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 kfree(indata, M_TEMP);
205 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
206 kfree(outdata, M_TEMP);
211 * XXX need to low_rssi_thresh config from ath9k, to support CUS198
212 * antenna diversity correctly.
215 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
216 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
218 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
219 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
220 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
224 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
225 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
227 antcomb->quick_scan_cnt = 0;
229 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
230 antcomb->rssi_lna2 = main_rssi_avg;
231 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
232 antcomb->rssi_lna1 = main_rssi_avg;
234 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
235 case (0x10): /* LNA2 A-B */
236 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
237 antcomb->first_quick_scan_conf =
238 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
239 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
241 case (0x20): /* LNA1 A-B */
242 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
243 antcomb->first_quick_scan_conf =
244 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
245 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
247 case (0x21): /* LNA1 LNA2 */
248 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
249 antcomb->first_quick_scan_conf =
250 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
251 antcomb->second_quick_scan_conf =
252 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
254 case (0x12): /* LNA2 LNA1 */
255 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
256 antcomb->first_quick_scan_conf =
257 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
258 antcomb->second_quick_scan_conf =
259 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
261 case (0x13): /* LNA2 A+B */
262 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
263 antcomb->first_quick_scan_conf =
264 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
265 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
267 case (0x23): /* LNA1 A+B */
268 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
269 antcomb->first_quick_scan_conf =
270 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
271 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
279 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
280 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
281 int alt_rssi_avg, int alt_ratio)
284 switch (antcomb->quick_scan_cnt) {
286 /* set alt to main, and alt to first conf */
287 div_ant_conf->main_lna_conf = antcomb->main_conf;
288 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
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->second_quick_scan_conf;
294 antcomb->rssi_first = main_rssi_avg;
295 antcomb->rssi_second = alt_rssi_avg;
297 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
299 if (ath_is_alt_ant_ratio_better(alt_ratio,
300 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
301 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
302 main_rssi_avg, alt_rssi_avg,
303 antcomb->total_pkt_count))
304 antcomb->first_ratio = AH_TRUE;
306 antcomb->first_ratio = AH_FALSE;
307 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
308 if (ath_is_alt_ant_ratio_better(alt_ratio,
309 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
310 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
311 main_rssi_avg, alt_rssi_avg,
312 antcomb->total_pkt_count))
313 antcomb->first_ratio = AH_TRUE;
315 antcomb->first_ratio = AH_FALSE;
317 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
318 (alt_rssi_avg > main_rssi_avg +
319 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
320 (alt_rssi_avg > main_rssi_avg)) &&
321 (antcomb->total_pkt_count > 50))
322 antcomb->first_ratio = AH_TRUE;
324 antcomb->first_ratio = AH_FALSE;
328 antcomb->alt_good = AH_FALSE;
329 antcomb->scan_not_start = AH_FALSE;
330 antcomb->scan = AH_FALSE;
331 antcomb->rssi_first = main_rssi_avg;
332 antcomb->rssi_third = alt_rssi_avg;
334 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
335 antcomb->rssi_lna1 = alt_rssi_avg;
336 else if (antcomb->second_quick_scan_conf ==
337 HAL_ANT_DIV_COMB_LNA2)
338 antcomb->rssi_lna2 = alt_rssi_avg;
339 else if (antcomb->second_quick_scan_conf ==
340 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
341 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
342 antcomb->rssi_lna2 = main_rssi_avg;
343 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
344 antcomb->rssi_lna1 = main_rssi_avg;
347 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
348 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
349 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
351 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
353 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
354 if (ath_is_alt_ant_ratio_better(alt_ratio,
355 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
356 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
357 main_rssi_avg, alt_rssi_avg,
358 antcomb->total_pkt_count))
359 antcomb->second_ratio = AH_TRUE;
361 antcomb->second_ratio = AH_FALSE;
362 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
363 if (ath_is_alt_ant_ratio_better(alt_ratio,
364 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
365 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
366 main_rssi_avg, alt_rssi_avg,
367 antcomb->total_pkt_count))
368 antcomb->second_ratio = AH_TRUE;
370 antcomb->second_ratio = AH_FALSE;
372 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
373 (alt_rssi_avg > main_rssi_avg +
374 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
375 (alt_rssi_avg > main_rssi_avg)) &&
376 (antcomb->total_pkt_count > 50))
377 antcomb->second_ratio = AH_TRUE;
379 antcomb->second_ratio = AH_FALSE;
382 /* set alt to the conf with maximun ratio */
383 if (antcomb->first_ratio && antcomb->second_ratio) {
384 if (antcomb->rssi_second > antcomb->rssi_third) {
386 if ((antcomb->first_quick_scan_conf ==
387 HAL_ANT_DIV_COMB_LNA1) ||
388 (antcomb->first_quick_scan_conf ==
389 HAL_ANT_DIV_COMB_LNA2))
390 /* Set alt LNA1 or LNA2*/
391 if (div_ant_conf->main_lna_conf ==
392 HAL_ANT_DIV_COMB_LNA2)
393 div_ant_conf->alt_lna_conf =
394 HAL_ANT_DIV_COMB_LNA1;
396 div_ant_conf->alt_lna_conf =
397 HAL_ANT_DIV_COMB_LNA2;
399 /* Set alt to A+B or A-B */
400 div_ant_conf->alt_lna_conf =
401 antcomb->first_quick_scan_conf;
402 } else if ((antcomb->second_quick_scan_conf ==
403 HAL_ANT_DIV_COMB_LNA1) ||
404 (antcomb->second_quick_scan_conf ==
405 HAL_ANT_DIV_COMB_LNA2)) {
406 /* Set alt LNA1 or LNA2 */
407 if (div_ant_conf->main_lna_conf ==
408 HAL_ANT_DIV_COMB_LNA2)
409 div_ant_conf->alt_lna_conf =
410 HAL_ANT_DIV_COMB_LNA1;
412 div_ant_conf->alt_lna_conf =
413 HAL_ANT_DIV_COMB_LNA2;
415 /* Set alt to A+B or A-B */
416 div_ant_conf->alt_lna_conf =
417 antcomb->second_quick_scan_conf;
419 } else if (antcomb->first_ratio) {
421 if ((antcomb->first_quick_scan_conf ==
422 HAL_ANT_DIV_COMB_LNA1) ||
423 (antcomb->first_quick_scan_conf ==
424 HAL_ANT_DIV_COMB_LNA2))
425 /* Set alt LNA1 or LNA2 */
426 if (div_ant_conf->main_lna_conf ==
427 HAL_ANT_DIV_COMB_LNA2)
428 div_ant_conf->alt_lna_conf =
429 HAL_ANT_DIV_COMB_LNA1;
431 div_ant_conf->alt_lna_conf =
432 HAL_ANT_DIV_COMB_LNA2;
434 /* Set alt to A+B or A-B */
435 div_ant_conf->alt_lna_conf =
436 antcomb->first_quick_scan_conf;
437 } else if (antcomb->second_ratio) {
439 if ((antcomb->second_quick_scan_conf ==
440 HAL_ANT_DIV_COMB_LNA1) ||
441 (antcomb->second_quick_scan_conf ==
442 HAL_ANT_DIV_COMB_LNA2))
443 /* Set alt LNA1 or LNA2 */
444 if (div_ant_conf->main_lna_conf ==
445 HAL_ANT_DIV_COMB_LNA2)
446 div_ant_conf->alt_lna_conf =
447 HAL_ANT_DIV_COMB_LNA1;
449 div_ant_conf->alt_lna_conf =
450 HAL_ANT_DIV_COMB_LNA2;
452 /* Set alt to A+B or A-B */
453 div_ant_conf->alt_lna_conf =
454 antcomb->second_quick_scan_conf;
456 /* main is largest */
457 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
458 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
459 /* Set alt LNA1 or LNA2 */
460 if (div_ant_conf->main_lna_conf ==
461 HAL_ANT_DIV_COMB_LNA2)
462 div_ant_conf->alt_lna_conf =
463 HAL_ANT_DIV_COMB_LNA1;
465 div_ant_conf->alt_lna_conf =
466 HAL_ANT_DIV_COMB_LNA2;
468 /* Set alt to A+B or A-B */
469 div_ant_conf->alt_lna_conf = antcomb->main_conf;
478 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
479 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
480 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
483 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
484 switch ((pdiv_ant_conf->main_lna_conf << 4)
485 | pdiv_ant_conf->alt_lna_conf) {
486 case (0x01): //A-B LNA2
487 pdiv_ant_conf->fast_div_bias = 0x1;
488 pdiv_ant_conf->main_gaintb = 0;
489 pdiv_ant_conf->alt_gaintb = 0;
491 case (0x02): //A-B LNA1
492 pdiv_ant_conf->fast_div_bias = 0x1;
493 pdiv_ant_conf->main_gaintb = 0;
494 pdiv_ant_conf->alt_gaintb = 0;
496 case (0x03): //A-B A+B
497 pdiv_ant_conf->fast_div_bias = 0x1;
498 pdiv_ant_conf->main_gaintb = 0;
499 pdiv_ant_conf->alt_gaintb = 0;
501 case (0x10): //LNA2 A-B
502 if ((antcomb->scan == 0)
503 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
504 pdiv_ant_conf->fast_div_bias = 0x3f;
506 pdiv_ant_conf->fast_div_bias = 0x1;
508 pdiv_ant_conf->main_gaintb = 0;
509 pdiv_ant_conf->alt_gaintb = 0;
511 case (0x12): //LNA2 LNA1
512 pdiv_ant_conf->fast_div_bias = 0x1;
513 pdiv_ant_conf->main_gaintb = 0;
514 pdiv_ant_conf->alt_gaintb = 0;
516 case (0x13): //LNA2 A+B
517 if ((antcomb->scan == 0)
518 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
519 pdiv_ant_conf->fast_div_bias = 0x3f;
521 pdiv_ant_conf->fast_div_bias = 0x1;
523 pdiv_ant_conf->main_gaintb = 0;
524 pdiv_ant_conf->alt_gaintb = 0;
526 case (0x20): //LNA1 A-B
527 if ((antcomb->scan == 0)
528 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
529 pdiv_ant_conf->fast_div_bias = 0x3f;
531 pdiv_ant_conf->fast_div_bias = 0x1;
533 pdiv_ant_conf->main_gaintb = 0;
534 pdiv_ant_conf->alt_gaintb = 0;
536 case (0x21): //LNA1 LNA2
537 pdiv_ant_conf->fast_div_bias = 0x1;
538 pdiv_ant_conf->main_gaintb = 0;
539 pdiv_ant_conf->alt_gaintb = 0;
541 case (0x23): //LNA1 A+B
542 if ((antcomb->scan == 0)
543 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
544 pdiv_ant_conf->fast_div_bias = 0x3f;
546 pdiv_ant_conf->fast_div_bias = 0x1;
548 pdiv_ant_conf->main_gaintb = 0;
549 pdiv_ant_conf->alt_gaintb = 0;
551 case (0x30): //A+B A-B
552 pdiv_ant_conf->fast_div_bias = 0x1;
553 pdiv_ant_conf->main_gaintb = 0;
554 pdiv_ant_conf->alt_gaintb = 0;
556 case (0x31): //A+B LNA2
557 pdiv_ant_conf->fast_div_bias = 0x1;
558 pdiv_ant_conf->main_gaintb = 0;
559 pdiv_ant_conf->alt_gaintb = 0;
561 case (0x32): //A+B LNA1
562 pdiv_ant_conf->fast_div_bias = 0x1;
563 pdiv_ant_conf->main_gaintb = 0;
564 pdiv_ant_conf->alt_gaintb = 0;
569 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
570 switch ((pdiv_ant_conf->main_lna_conf << 4)
571 | pdiv_ant_conf->alt_lna_conf) {
572 case (0x01): //A-B LNA2
573 pdiv_ant_conf->fast_div_bias = 0x1;
574 pdiv_ant_conf->main_gaintb = 0;
575 pdiv_ant_conf->alt_gaintb = 0;
577 case (0x02): //A-B LNA1
578 pdiv_ant_conf->fast_div_bias = 0x1;
579 pdiv_ant_conf->main_gaintb = 0;
580 pdiv_ant_conf->alt_gaintb = 0;
582 case (0x03): //A-B A+B
583 pdiv_ant_conf->fast_div_bias = 0x1;
584 pdiv_ant_conf->main_gaintb = 0;
585 pdiv_ant_conf->alt_gaintb = 0;
587 case (0x10): //LNA2 A-B
588 if ((antcomb->scan == 0)
589 && (alt_ratio > alt_ant_ratio_th)) {
590 pdiv_ant_conf->fast_div_bias = 0x1;
592 pdiv_ant_conf->fast_div_bias = 0x2;
594 pdiv_ant_conf->main_gaintb = 0;
595 pdiv_ant_conf->alt_gaintb = 0;
597 case (0x12): //LNA2 LNA1
598 pdiv_ant_conf->fast_div_bias = 0x1;
599 pdiv_ant_conf->main_gaintb = 0;
600 pdiv_ant_conf->alt_gaintb = 0;
602 case (0x13): //LNA2 A+B
603 if ((antcomb->scan == 0)
604 && (alt_ratio > alt_ant_ratio_th)) {
605 pdiv_ant_conf->fast_div_bias = 0x1;
607 pdiv_ant_conf->fast_div_bias = 0x2;
609 pdiv_ant_conf->main_gaintb = 0;
610 pdiv_ant_conf->alt_gaintb = 0;
612 case (0x20): //LNA1 A-B
613 if ((antcomb->scan == 0)
614 && (alt_ratio > alt_ant_ratio_th)) {
615 pdiv_ant_conf->fast_div_bias = 0x1;
617 pdiv_ant_conf->fast_div_bias = 0x2;
619 pdiv_ant_conf->main_gaintb = 0;
620 pdiv_ant_conf->alt_gaintb = 0;
622 case (0x21): //LNA1 LNA2
623 pdiv_ant_conf->fast_div_bias = 0x1;
624 pdiv_ant_conf->main_gaintb = 0;
625 pdiv_ant_conf->alt_gaintb = 0;
627 case (0x23): //LNA1 A+B
628 if ((antcomb->scan == 0)
629 && (alt_ratio > alt_ant_ratio_th)) {
630 pdiv_ant_conf->fast_div_bias = 0x1;
632 pdiv_ant_conf->fast_div_bias = 0x2;
634 pdiv_ant_conf->main_gaintb = 0;
635 pdiv_ant_conf->alt_gaintb = 0;
637 case (0x30): //A+B A-B
638 pdiv_ant_conf->fast_div_bias = 0x1;
639 pdiv_ant_conf->main_gaintb = 0;
640 pdiv_ant_conf->alt_gaintb = 0;
642 case (0x31): //A+B LNA2
643 pdiv_ant_conf->fast_div_bias = 0x1;
644 pdiv_ant_conf->main_gaintb = 0;
645 pdiv_ant_conf->alt_gaintb = 0;
647 case (0x32): //A+B LNA1
648 pdiv_ant_conf->fast_div_bias = 0x1;
649 pdiv_ant_conf->main_gaintb = 0;
650 pdiv_ant_conf->alt_gaintb = 0;
655 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
656 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
657 case (0x01): //A-B LNA2
658 pdiv_ant_conf->fast_div_bias = 0x3b;
660 case (0x02): //A-B LNA1
661 pdiv_ant_conf->fast_div_bias = 0x3d;
663 case (0x03): //A-B A+B
664 pdiv_ant_conf->fast_div_bias = 0x1;
666 case (0x10): //LNA2 A-B
667 pdiv_ant_conf->fast_div_bias = 0x7;
669 case (0x12): //LNA2 LNA1
670 pdiv_ant_conf->fast_div_bias = 0x2;
672 case (0x13): //LNA2 A+B
673 pdiv_ant_conf->fast_div_bias = 0x7;
675 case (0x20): //LNA1 A-B
676 pdiv_ant_conf->fast_div_bias = 0x6;
678 case (0x21): //LNA1 LNA2
679 pdiv_ant_conf->fast_div_bias = 0x0;
681 case (0x23): //LNA1 A+B
682 pdiv_ant_conf->fast_div_bias = 0x6;
684 case (0x30): //A+B A-B
685 pdiv_ant_conf->fast_div_bias = 0x1;
687 case (0x31): //A+B LNA2
688 pdiv_ant_conf->fast_div_bias = 0x3b;
690 case (0x32): //A+B LNA1
691 pdiv_ant_conf->fast_div_bias = 0x3d;
700 * AR9485/AR933x TODO:
701 * + Select a ratio based on whether RSSI is low or not; but I need
702 * to figure out what "low_rssi_th" is sourced from.
703 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
704 * + .. and there's likely a bunch of other things to include in this.
707 /* Antenna diversity and combining */
709 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
710 unsigned long ticks, int hz)
712 HAL_ANT_COMB_CONFIG div_ant_conf;
713 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
714 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
715 int curr_main_set, curr_bias;
716 int main_rssi = rs->rs_rssi_ctl[0];
717 int alt_rssi = rs->rs_rssi_ctl[1];
718 int rx_ant_conf, main_ant_conf, alt_ant_conf;
719 HAL_BOOL short_scan = AH_FALSE;
721 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
722 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
723 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
726 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
727 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
735 !!(rs->rs_rssi_ctl[2] & 0x80),
736 !!(rs->rs_rssi_ctl[2] & 0x40),
737 !!(rs->rs_rssi_ext[2] & 0x40));
741 * If LNA diversity combining isn't enabled, don't run this.
743 if (! sc->sc_dolnadiv)
747 * XXX this is ugly, but the HAL code attaches the
748 * LNA diversity to the TX antenna settings.
751 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
754 /* Record packet only when alt_rssi is positive */
755 if (main_rssi > 0 && alt_rssi > 0) {
756 antcomb->total_pkt_count++;
757 antcomb->main_total_rssi += main_rssi;
758 antcomb->alt_total_rssi += alt_rssi;
759 if (main_ant_conf == rx_ant_conf)
760 antcomb->main_recv_cnt++;
762 antcomb->alt_recv_cnt++;
765 /* Short scan check */
766 if (antcomb->scan && antcomb->alt_good) {
767 if (time_after(ticks, antcomb->scan_start_time +
768 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
769 short_scan = AH_TRUE;
771 if (antcomb->total_pkt_count ==
772 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
773 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
774 antcomb->total_pkt_count);
775 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
776 short_scan = AH_TRUE;
781 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
782 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
784 antcomb->total_pkt_count,
785 !! (rs->rs_moreaggr),
789 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
790 rs->rs_moreaggr) && !short_scan)
793 if (antcomb->total_pkt_count) {
794 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
795 antcomb->total_pkt_count);
796 main_rssi_avg = (antcomb->main_total_rssi /
797 antcomb->total_pkt_count);
798 alt_rssi_avg = (antcomb->alt_total_rssi /
799 antcomb->total_pkt_count);
802 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
804 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
805 curr_alt_set = div_ant_conf.alt_lna_conf;
806 curr_main_set = div_ant_conf.main_lna_conf;
807 curr_bias = div_ant_conf.fast_div_bias;
811 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
812 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
813 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
815 antcomb->alt_good = AH_TRUE;
817 antcomb->alt_good = AH_FALSE;
821 antcomb->scan = AH_TRUE;
822 antcomb->scan_not_start = AH_TRUE;
825 if (!antcomb->scan) {
826 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
827 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
828 /* Switch main and alt LNA */
829 div_ant_conf.main_lna_conf =
830 HAL_ANT_DIV_COMB_LNA2;
831 div_ant_conf.alt_lna_conf =
832 HAL_ANT_DIV_COMB_LNA1;
833 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
834 div_ant_conf.main_lna_conf =
835 HAL_ANT_DIV_COMB_LNA1;
836 div_ant_conf.alt_lna_conf =
837 HAL_ANT_DIV_COMB_LNA2;
841 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
842 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
843 /* Set alt to another LNA */
844 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
845 div_ant_conf.alt_lna_conf =
846 HAL_ANT_DIV_COMB_LNA1;
847 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
848 div_ant_conf.alt_lna_conf =
849 HAL_ANT_DIV_COMB_LNA2;
854 if ((alt_rssi_avg < (main_rssi_avg +
855 antcomb->lna1_lna2_delta)))
859 if (!antcomb->scan_not_start) {
860 switch (curr_alt_set) {
861 case HAL_ANT_DIV_COMB_LNA2:
862 antcomb->rssi_lna2 = alt_rssi_avg;
863 antcomb->rssi_lna1 = main_rssi_avg;
864 antcomb->scan = AH_TRUE;
866 div_ant_conf.main_lna_conf =
867 HAL_ANT_DIV_COMB_LNA1;
868 div_ant_conf.alt_lna_conf =
869 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
871 case HAL_ANT_DIV_COMB_LNA1:
872 antcomb->rssi_lna1 = alt_rssi_avg;
873 antcomb->rssi_lna2 = main_rssi_avg;
874 antcomb->scan = AH_TRUE;
876 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
877 div_ant_conf.alt_lna_conf =
878 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
880 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
881 antcomb->rssi_add = alt_rssi_avg;
882 antcomb->scan = AH_TRUE;
884 div_ant_conf.alt_lna_conf =
885 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
887 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
888 antcomb->rssi_sub = alt_rssi_avg;
889 antcomb->scan = AH_FALSE;
890 if (antcomb->rssi_lna2 >
891 (antcomb->rssi_lna1 +
892 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
893 /* use LNA2 as main LNA */
894 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
895 (antcomb->rssi_add > antcomb->rssi_sub)) {
897 div_ant_conf.main_lna_conf =
898 HAL_ANT_DIV_COMB_LNA2;
899 div_ant_conf.alt_lna_conf =
900 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
901 } else if (antcomb->rssi_sub >
902 antcomb->rssi_lna1) {
904 div_ant_conf.main_lna_conf =
905 HAL_ANT_DIV_COMB_LNA2;
906 div_ant_conf.alt_lna_conf =
907 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
910 div_ant_conf.main_lna_conf =
911 HAL_ANT_DIV_COMB_LNA2;
912 div_ant_conf.alt_lna_conf =
913 HAL_ANT_DIV_COMB_LNA1;
916 /* use LNA1 as main LNA */
917 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
918 (antcomb->rssi_add > antcomb->rssi_sub)) {
920 div_ant_conf.main_lna_conf =
921 HAL_ANT_DIV_COMB_LNA1;
922 div_ant_conf.alt_lna_conf =
923 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
924 } else if (antcomb->rssi_sub >
925 antcomb->rssi_lna1) {
927 div_ant_conf.main_lna_conf =
928 HAL_ANT_DIV_COMB_LNA1;
929 div_ant_conf.alt_lna_conf =
930 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
933 div_ant_conf.main_lna_conf =
934 HAL_ANT_DIV_COMB_LNA1;
935 div_ant_conf.alt_lna_conf =
936 HAL_ANT_DIV_COMB_LNA2;
944 if (!antcomb->alt_good) {
945 antcomb->scan_not_start = AH_FALSE;
946 /* Set alt to another LNA */
947 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
948 div_ant_conf.main_lna_conf =
949 HAL_ANT_DIV_COMB_LNA2;
950 div_ant_conf.alt_lna_conf =
951 HAL_ANT_DIV_COMB_LNA1;
952 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
953 div_ant_conf.main_lna_conf =
954 HAL_ANT_DIV_COMB_LNA1;
955 div_ant_conf.alt_lna_conf =
956 HAL_ANT_DIV_COMB_LNA2;
962 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
963 main_rssi_avg, alt_rssi_avg,
966 antcomb->quick_scan_cnt++;
970 ath_ant_div_conf_fast_divbias(&div_ant_conf);
973 ath_ant_adjust_fast_divbias(antcomb,
975 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
976 div_ant_conf.antdiv_configgroup,
979 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
981 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
982 __func__, antcomb->total_pkt_count);
984 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
985 __func__, antcomb->main_total_rssi);
986 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
987 __func__, antcomb->alt_total_rssi);
989 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
990 __func__, main_rssi_avg);
991 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
992 __func__, alt_rssi_avg);
994 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
995 __func__, antcomb->main_recv_cnt);
996 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
997 __func__, antcomb->alt_recv_cnt);
999 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
1000 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1001 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1002 // if (curr_main_set != div_ant_conf.main_lna_conf)
1003 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1004 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1005 // if (curr_bias != div_ant_conf.fast_div_bias)
1006 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1007 __func__, curr_bias, div_ant_conf.fast_div_bias);
1009 antcomb->scan_start_time = ticks;
1010 antcomb->total_pkt_count = 0;
1011 antcomb->main_total_rssi = 0;
1012 antcomb->alt_total_rssi = 0;
1013 antcomb->main_recv_cnt = 0;
1014 antcomb->alt_recv_cnt = 0;