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>
34 * This module handles LNA diversity for those chips which implement LNA
35 * mixing (AR9285/AR9485.)
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 #include <sys/sysctl.h>
44 #include <sys/kernel.h>
46 #include <sys/malloc.h>
47 #include <sys/mutex.h>
48 #include <sys/errno.h>
50 #include <sys/socket.h>
53 #include <net/if_var.h>
54 #include <net/if_media.h>
55 #include <net/if_arp.h>
56 #include <net/ethernet.h> /* XXX for ether_sprintf */
58 #include <netproto/802_11/ieee80211_var.h>
63 #include <netinet/in.h>
64 #include <netinet/if_ether.h>
67 #include <machine/resource.h>
69 #include <dev/netif/ath/ath/if_athvar.h>
70 #include <dev/netif/ath/ath/if_ath_debug.h>
71 #include <dev/netif/ath/ath/if_ath_lna_div.h>
73 /* Linux compability macros */
75 * XXX these don't handle rounding, underflow, overflow, wrapping!
77 #define msecs_to_jiffies(a) ( (a) * hz / 1000 )
80 * Methods which are required
84 * Attach the LNA diversity to the given interface
87 ath_lna_div_attach(struct ath_softc *sc)
89 struct if_ath_ant_comb_state *ss;
90 HAL_ANT_COMB_CONFIG div_ant_conf;
92 /* Only do this if diversity is enabled */
93 if (! ath_hal_hasdivantcomb(sc->sc_ah))
96 ss = malloc(sizeof(struct if_ath_ant_comb_state),
97 M_TEMP, M_WAITOK | M_ZERO);
99 device_printf(sc->sc_dev, "%s: failed to allocate\n",
101 /* Don't fail at this point */
105 /* Fetch the hardware configuration */
106 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
107 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
109 /* Figure out what the hardware specific bits should be */
110 if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
111 (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
112 ss->lna1_lna2_delta = -9;
114 ss->lna1_lna2_delta = -3;
117 /* Let's flip this on */
125 * Detach the LNA diversity state from the given interface
128 ath_lna_div_detach(struct ath_softc *sc)
130 if (sc->sc_lna_div != NULL) {
131 free(sc->sc_lna_div, M_TEMP);
132 sc->sc_lna_div = NULL;
139 * Enable LNA diversity on the current channel if it's required.
142 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
149 * Handle ioctl requests from the diagnostic interface.
151 * The initial part of this code resembles ath_ioctl_diag();
152 * it's likely a good idea to reduce duplication between
153 * these two routines.
156 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
158 unsigned int id = ad->ad_id & ATH_DIAG_ID;
160 void *outdata = NULL;
161 u_int32_t insize = ad->ad_in_size;
162 u_int32_t outsize = ad->ad_out_size;
166 if (ad->ad_id & ATH_DIAG_IN) {
170 indata = malloc(insize, M_TEMP, M_NOWAIT);
171 if (indata == NULL) {
175 error = copyin(ad->ad_in_data, indata, insize);
179 if (ad->ad_id & ATH_DIAG_DYN) {
181 * Allocate a buffer for the results (otherwise the HAL
182 * returns a pointer to a buffer where we can read the
183 * results). Note that we depend on the HAL leaving this
184 * pointer for us to use below in reclaiming the buffer;
185 * may want to be more defensive.
187 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
188 if (outdata == NULL) {
197 if (outsize < ad->ad_out_size)
198 ad->ad_out_size = outsize;
199 if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
202 if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
203 free(indata, M_TEMP);
204 if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
205 free(outdata, M_TEMP);
210 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
211 int main_rssi_avg, int alt_rssi_avg, int pkt_count)
213 return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
214 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
215 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
219 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
220 HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
222 antcomb->quick_scan_cnt = 0;
224 if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
225 antcomb->rssi_lna2 = main_rssi_avg;
226 else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
227 antcomb->rssi_lna1 = main_rssi_avg;
229 switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
230 case (0x10): /* LNA2 A-B */
231 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
232 antcomb->first_quick_scan_conf =
233 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
234 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
236 case (0x20): /* LNA1 A-B */
237 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
238 antcomb->first_quick_scan_conf =
239 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
240 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
242 case (0x21): /* LNA1 LNA2 */
243 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
244 antcomb->first_quick_scan_conf =
245 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
246 antcomb->second_quick_scan_conf =
247 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
249 case (0x12): /* LNA2 LNA1 */
250 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
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 (0x13): /* LNA2 A+B */
257 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
258 antcomb->first_quick_scan_conf =
259 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
260 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
262 case (0x23): /* LNA1 A+B */
263 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
264 antcomb->first_quick_scan_conf =
265 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
266 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
274 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
275 HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
276 int alt_rssi_avg, int alt_ratio)
279 switch (antcomb->quick_scan_cnt) {
281 /* set alt to main, and alt to first conf */
282 div_ant_conf->main_lna_conf = antcomb->main_conf;
283 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
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->second_quick_scan_conf;
289 antcomb->rssi_first = main_rssi_avg;
290 antcomb->rssi_second = alt_rssi_avg;
292 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
294 if (ath_is_alt_ant_ratio_better(alt_ratio,
295 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
296 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
297 main_rssi_avg, alt_rssi_avg,
298 antcomb->total_pkt_count))
299 antcomb->first_ratio = AH_TRUE;
301 antcomb->first_ratio = AH_FALSE;
302 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
303 if (ath_is_alt_ant_ratio_better(alt_ratio,
304 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
305 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
306 main_rssi_avg, alt_rssi_avg,
307 antcomb->total_pkt_count))
308 antcomb->first_ratio = AH_TRUE;
310 antcomb->first_ratio = AH_FALSE;
312 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
313 (alt_rssi_avg > main_rssi_avg +
314 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
315 (alt_rssi_avg > main_rssi_avg)) &&
316 (antcomb->total_pkt_count > 50))
317 antcomb->first_ratio = AH_TRUE;
319 antcomb->first_ratio = AH_FALSE;
323 antcomb->alt_good = AH_FALSE;
324 antcomb->scan_not_start = AH_FALSE;
325 antcomb->scan = AH_FALSE;
326 antcomb->rssi_first = main_rssi_avg;
327 antcomb->rssi_third = alt_rssi_avg;
329 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
330 antcomb->rssi_lna1 = alt_rssi_avg;
331 else if (antcomb->second_quick_scan_conf ==
332 HAL_ANT_DIV_COMB_LNA2)
333 antcomb->rssi_lna2 = alt_rssi_avg;
334 else if (antcomb->second_quick_scan_conf ==
335 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
336 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
337 antcomb->rssi_lna2 = main_rssi_avg;
338 else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
339 antcomb->rssi_lna1 = main_rssi_avg;
342 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
343 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
344 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
346 div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
348 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
349 if (ath_is_alt_ant_ratio_better(alt_ratio,
350 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
351 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
352 main_rssi_avg, alt_rssi_avg,
353 antcomb->total_pkt_count))
354 antcomb->second_ratio = AH_TRUE;
356 antcomb->second_ratio = AH_FALSE;
357 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
358 if (ath_is_alt_ant_ratio_better(alt_ratio,
359 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
360 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
361 main_rssi_avg, alt_rssi_avg,
362 antcomb->total_pkt_count))
363 antcomb->second_ratio = AH_TRUE;
365 antcomb->second_ratio = AH_FALSE;
367 if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
368 (alt_rssi_avg > main_rssi_avg +
369 ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
370 (alt_rssi_avg > main_rssi_avg)) &&
371 (antcomb->total_pkt_count > 50))
372 antcomb->second_ratio = AH_TRUE;
374 antcomb->second_ratio = AH_FALSE;
377 /* set alt to the conf with maximun ratio */
378 if (antcomb->first_ratio && antcomb->second_ratio) {
379 if (antcomb->rssi_second > antcomb->rssi_third) {
381 if ((antcomb->first_quick_scan_conf ==
382 HAL_ANT_DIV_COMB_LNA1) ||
383 (antcomb->first_quick_scan_conf ==
384 HAL_ANT_DIV_COMB_LNA2))
385 /* Set alt LNA1 or LNA2*/
386 if (div_ant_conf->main_lna_conf ==
387 HAL_ANT_DIV_COMB_LNA2)
388 div_ant_conf->alt_lna_conf =
389 HAL_ANT_DIV_COMB_LNA1;
391 div_ant_conf->alt_lna_conf =
392 HAL_ANT_DIV_COMB_LNA2;
394 /* Set alt to A+B or A-B */
395 div_ant_conf->alt_lna_conf =
396 antcomb->first_quick_scan_conf;
397 } else if ((antcomb->second_quick_scan_conf ==
398 HAL_ANT_DIV_COMB_LNA1) ||
399 (antcomb->second_quick_scan_conf ==
400 HAL_ANT_DIV_COMB_LNA2)) {
401 /* Set alt LNA1 or LNA2 */
402 if (div_ant_conf->main_lna_conf ==
403 HAL_ANT_DIV_COMB_LNA2)
404 div_ant_conf->alt_lna_conf =
405 HAL_ANT_DIV_COMB_LNA1;
407 div_ant_conf->alt_lna_conf =
408 HAL_ANT_DIV_COMB_LNA2;
410 /* Set alt to A+B or A-B */
411 div_ant_conf->alt_lna_conf =
412 antcomb->second_quick_scan_conf;
414 } else if (antcomb->first_ratio) {
416 if ((antcomb->first_quick_scan_conf ==
417 HAL_ANT_DIV_COMB_LNA1) ||
418 (antcomb->first_quick_scan_conf ==
419 HAL_ANT_DIV_COMB_LNA2))
420 /* Set alt LNA1 or LNA2 */
421 if (div_ant_conf->main_lna_conf ==
422 HAL_ANT_DIV_COMB_LNA2)
423 div_ant_conf->alt_lna_conf =
424 HAL_ANT_DIV_COMB_LNA1;
426 div_ant_conf->alt_lna_conf =
427 HAL_ANT_DIV_COMB_LNA2;
429 /* Set alt to A+B or A-B */
430 div_ant_conf->alt_lna_conf =
431 antcomb->first_quick_scan_conf;
432 } else if (antcomb->second_ratio) {
434 if ((antcomb->second_quick_scan_conf ==
435 HAL_ANT_DIV_COMB_LNA1) ||
436 (antcomb->second_quick_scan_conf ==
437 HAL_ANT_DIV_COMB_LNA2))
438 /* Set alt LNA1 or LNA2 */
439 if (div_ant_conf->main_lna_conf ==
440 HAL_ANT_DIV_COMB_LNA2)
441 div_ant_conf->alt_lna_conf =
442 HAL_ANT_DIV_COMB_LNA1;
444 div_ant_conf->alt_lna_conf =
445 HAL_ANT_DIV_COMB_LNA2;
447 /* Set alt to A+B or A-B */
448 div_ant_conf->alt_lna_conf =
449 antcomb->second_quick_scan_conf;
451 /* main is largest */
452 if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
453 (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
454 /* Set alt LNA1 or LNA2 */
455 if (div_ant_conf->main_lna_conf ==
456 HAL_ANT_DIV_COMB_LNA2)
457 div_ant_conf->alt_lna_conf =
458 HAL_ANT_DIV_COMB_LNA1;
460 div_ant_conf->alt_lna_conf =
461 HAL_ANT_DIV_COMB_LNA2;
463 /* Set alt to A+B or A-B */
464 div_ant_conf->alt_lna_conf = antcomb->main_conf;
473 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
474 int alt_ratio, int alt_ant_ratio_th, u_int config_group,
475 HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
478 if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
479 switch ((pdiv_ant_conf->main_lna_conf << 4)
480 | pdiv_ant_conf->alt_lna_conf) {
481 case (0x01): //A-B LNA2
482 pdiv_ant_conf->fast_div_bias = 0x1;
483 pdiv_ant_conf->main_gaintb = 0;
484 pdiv_ant_conf->alt_gaintb = 0;
486 case (0x02): //A-B LNA1
487 pdiv_ant_conf->fast_div_bias = 0x1;
488 pdiv_ant_conf->main_gaintb = 0;
489 pdiv_ant_conf->alt_gaintb = 0;
491 case (0x03): //A-B A+B
492 pdiv_ant_conf->fast_div_bias = 0x1;
493 pdiv_ant_conf->main_gaintb = 0;
494 pdiv_ant_conf->alt_gaintb = 0;
496 case (0x10): //LNA2 A-B
497 if ((antcomb->scan == 0)
498 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
499 pdiv_ant_conf->fast_div_bias = 0x3f;
501 pdiv_ant_conf->fast_div_bias = 0x1;
503 pdiv_ant_conf->main_gaintb = 0;
504 pdiv_ant_conf->alt_gaintb = 0;
506 case (0x12): //LNA2 LNA1
507 pdiv_ant_conf->fast_div_bias = 0x1;
508 pdiv_ant_conf->main_gaintb = 0;
509 pdiv_ant_conf->alt_gaintb = 0;
511 case (0x13): //LNA2 A+B
512 if ((antcomb->scan == 0)
513 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
514 pdiv_ant_conf->fast_div_bias = 0x3f;
516 pdiv_ant_conf->fast_div_bias = 0x1;
518 pdiv_ant_conf->main_gaintb = 0;
519 pdiv_ant_conf->alt_gaintb = 0;
521 case (0x20): //LNA1 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 (0x21): //LNA1 LNA2
532 pdiv_ant_conf->fast_div_bias = 0x1;
533 pdiv_ant_conf->main_gaintb = 0;
534 pdiv_ant_conf->alt_gaintb = 0;
536 case (0x23): //LNA1 A+B
537 if ((antcomb->scan == 0)
538 && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
539 pdiv_ant_conf->fast_div_bias = 0x3f;
541 pdiv_ant_conf->fast_div_bias = 0x1;
543 pdiv_ant_conf->main_gaintb = 0;
544 pdiv_ant_conf->alt_gaintb = 0;
546 case (0x30): //A+B A-B
547 pdiv_ant_conf->fast_div_bias = 0x1;
548 pdiv_ant_conf->main_gaintb = 0;
549 pdiv_ant_conf->alt_gaintb = 0;
551 case (0x31): //A+B LNA2
552 pdiv_ant_conf->fast_div_bias = 0x1;
553 pdiv_ant_conf->main_gaintb = 0;
554 pdiv_ant_conf->alt_gaintb = 0;
556 case (0x32): //A+B LNA1
557 pdiv_ant_conf->fast_div_bias = 0x1;
558 pdiv_ant_conf->main_gaintb = 0;
559 pdiv_ant_conf->alt_gaintb = 0;
564 } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
565 switch ((pdiv_ant_conf->main_lna_conf << 4)
566 | pdiv_ant_conf->alt_lna_conf) {
567 case (0x01): //A-B LNA2
568 pdiv_ant_conf->fast_div_bias = 0x1;
569 pdiv_ant_conf->main_gaintb = 0;
570 pdiv_ant_conf->alt_gaintb = 0;
572 case (0x02): //A-B LNA1
573 pdiv_ant_conf->fast_div_bias = 0x1;
574 pdiv_ant_conf->main_gaintb = 0;
575 pdiv_ant_conf->alt_gaintb = 0;
577 case (0x03): //A-B A+B
578 pdiv_ant_conf->fast_div_bias = 0x1;
579 pdiv_ant_conf->main_gaintb = 0;
580 pdiv_ant_conf->alt_gaintb = 0;
582 case (0x10): //LNA2 A-B
583 if ((antcomb->scan == 0)
584 && (alt_ratio > alt_ant_ratio_th)) {
585 pdiv_ant_conf->fast_div_bias = 0x1;
587 pdiv_ant_conf->fast_div_bias = 0x2;
589 pdiv_ant_conf->main_gaintb = 0;
590 pdiv_ant_conf->alt_gaintb = 0;
592 case (0x12): //LNA2 LNA1
593 pdiv_ant_conf->fast_div_bias = 0x1;
594 pdiv_ant_conf->main_gaintb = 0;
595 pdiv_ant_conf->alt_gaintb = 0;
597 case (0x13): //LNA2 A+B
598 if ((antcomb->scan == 0)
599 && (alt_ratio > alt_ant_ratio_th)) {
600 pdiv_ant_conf->fast_div_bias = 0x1;
602 pdiv_ant_conf->fast_div_bias = 0x2;
604 pdiv_ant_conf->main_gaintb = 0;
605 pdiv_ant_conf->alt_gaintb = 0;
607 case (0x20): //LNA1 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 (0x21): //LNA1 LNA2
618 pdiv_ant_conf->fast_div_bias = 0x1;
619 pdiv_ant_conf->main_gaintb = 0;
620 pdiv_ant_conf->alt_gaintb = 0;
622 case (0x23): //LNA1 A+B
623 if ((antcomb->scan == 0)
624 && (alt_ratio > alt_ant_ratio_th)) {
625 pdiv_ant_conf->fast_div_bias = 0x1;
627 pdiv_ant_conf->fast_div_bias = 0x2;
629 pdiv_ant_conf->main_gaintb = 0;
630 pdiv_ant_conf->alt_gaintb = 0;
632 case (0x30): //A+B A-B
633 pdiv_ant_conf->fast_div_bias = 0x1;
634 pdiv_ant_conf->main_gaintb = 0;
635 pdiv_ant_conf->alt_gaintb = 0;
637 case (0x31): //A+B LNA2
638 pdiv_ant_conf->fast_div_bias = 0x1;
639 pdiv_ant_conf->main_gaintb = 0;
640 pdiv_ant_conf->alt_gaintb = 0;
642 case (0x32): //A+B LNA1
643 pdiv_ant_conf->fast_div_bias = 0x1;
644 pdiv_ant_conf->main_gaintb = 0;
645 pdiv_ant_conf->alt_gaintb = 0;
650 } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
651 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
652 case (0x01): //A-B LNA2
653 pdiv_ant_conf->fast_div_bias = 0x3b;
655 case (0x02): //A-B LNA1
656 pdiv_ant_conf->fast_div_bias = 0x3d;
658 case (0x03): //A-B A+B
659 pdiv_ant_conf->fast_div_bias = 0x1;
661 case (0x10): //LNA2 A-B
662 pdiv_ant_conf->fast_div_bias = 0x7;
664 case (0x12): //LNA2 LNA1
665 pdiv_ant_conf->fast_div_bias = 0x2;
667 case (0x13): //LNA2 A+B
668 pdiv_ant_conf->fast_div_bias = 0x7;
670 case (0x20): //LNA1 A-B
671 pdiv_ant_conf->fast_div_bias = 0x6;
673 case (0x21): //LNA1 LNA2
674 pdiv_ant_conf->fast_div_bias = 0x0;
676 case (0x23): //LNA1 A+B
677 pdiv_ant_conf->fast_div_bias = 0x6;
679 case (0x30): //A+B A-B
680 pdiv_ant_conf->fast_div_bias = 0x1;
682 case (0x31): //A+B LNA2
683 pdiv_ant_conf->fast_div_bias = 0x3b;
685 case (0x32): //A+B LNA1
686 pdiv_ant_conf->fast_div_bias = 0x3d;
695 * AR9485/AR933x TODO:
696 * + Select a ratio based on whether RSSI is low or not; but I need
697 * to figure out what "low_rssi_th" is sourced from.
698 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
699 * + .. and there's likely a bunch of other things to include in this.
702 /* Antenna diversity and combining */
704 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
705 unsigned long ticks, int hz)
707 HAL_ANT_COMB_CONFIG div_ant_conf;
708 struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
709 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
710 int curr_main_set, curr_bias;
711 int main_rssi = rs->rs_rssi_ctl[0];
712 int alt_rssi = rs->rs_rssi_ctl[1];
713 int rx_ant_conf, main_ant_conf, alt_ant_conf;
714 HAL_BOOL short_scan = AH_FALSE;
716 rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
717 main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
718 alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
721 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
722 "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
730 !!(rs->rs_rssi_ctl[2] & 0x80),
731 !!(rs->rs_rssi_ctl[2] & 0x40),
732 !!(rs->rs_rssi_ext[2] & 0x40));
736 * If LNA diversity combining isn't enabled, don't run this.
738 if (! sc->sc_dolnadiv)
742 * XXX this is ugly, but the HAL code attaches the
743 * LNA diversity to the TX antenna settings.
746 if (sc->sc_txantenna != HAL_ANT_VARIABLE)
749 /* Record packet only when alt_rssi is positive */
750 if (main_rssi > 0 && alt_rssi > 0) {
751 antcomb->total_pkt_count++;
752 antcomb->main_total_rssi += main_rssi;
753 antcomb->alt_total_rssi += alt_rssi;
754 if (main_ant_conf == rx_ant_conf)
755 antcomb->main_recv_cnt++;
757 antcomb->alt_recv_cnt++;
760 /* Short scan check */
761 if (antcomb->scan && antcomb->alt_good) {
762 if (time_after(ticks, antcomb->scan_start_time +
763 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
764 short_scan = AH_TRUE;
766 if (antcomb->total_pkt_count ==
767 ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
768 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
769 antcomb->total_pkt_count);
770 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
771 short_scan = AH_TRUE;
776 DPRINTF(sc, ATH_DEBUG_DIVERSITY,
777 "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
779 antcomb->total_pkt_count,
780 !! (rs->rs_moreaggr),
784 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
785 rs->rs_moreaggr) && !short_scan)
788 if (antcomb->total_pkt_count) {
789 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
790 antcomb->total_pkt_count);
791 main_rssi_avg = (antcomb->main_total_rssi /
792 antcomb->total_pkt_count);
793 alt_rssi_avg = (antcomb->alt_total_rssi /
794 antcomb->total_pkt_count);
797 OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
799 ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
800 curr_alt_set = div_ant_conf.alt_lna_conf;
801 curr_main_set = div_ant_conf.main_lna_conf;
802 curr_bias = div_ant_conf.fast_div_bias;
806 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
807 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
808 ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
810 antcomb->alt_good = AH_TRUE;
812 antcomb->alt_good = AH_FALSE;
816 antcomb->scan = AH_TRUE;
817 antcomb->scan_not_start = AH_TRUE;
820 if (!antcomb->scan) {
821 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
822 if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
823 /* Switch main and alt LNA */
824 div_ant_conf.main_lna_conf =
825 HAL_ANT_DIV_COMB_LNA2;
826 div_ant_conf.alt_lna_conf =
827 HAL_ANT_DIV_COMB_LNA1;
828 } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
829 div_ant_conf.main_lna_conf =
830 HAL_ANT_DIV_COMB_LNA1;
831 div_ant_conf.alt_lna_conf =
832 HAL_ANT_DIV_COMB_LNA2;
836 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
837 (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
838 /* Set alt to another LNA */
839 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
840 div_ant_conf.alt_lna_conf =
841 HAL_ANT_DIV_COMB_LNA1;
842 else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
843 div_ant_conf.alt_lna_conf =
844 HAL_ANT_DIV_COMB_LNA2;
849 if ((alt_rssi_avg < (main_rssi_avg +
850 antcomb->lna1_lna2_delta)))
854 if (!antcomb->scan_not_start) {
855 switch (curr_alt_set) {
856 case HAL_ANT_DIV_COMB_LNA2:
857 antcomb->rssi_lna2 = alt_rssi_avg;
858 antcomb->rssi_lna1 = main_rssi_avg;
859 antcomb->scan = AH_TRUE;
861 div_ant_conf.main_lna_conf =
862 HAL_ANT_DIV_COMB_LNA1;
863 div_ant_conf.alt_lna_conf =
864 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
866 case HAL_ANT_DIV_COMB_LNA1:
867 antcomb->rssi_lna1 = alt_rssi_avg;
868 antcomb->rssi_lna2 = main_rssi_avg;
869 antcomb->scan = AH_TRUE;
871 div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
872 div_ant_conf.alt_lna_conf =
873 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
875 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
876 antcomb->rssi_add = alt_rssi_avg;
877 antcomb->scan = AH_TRUE;
879 div_ant_conf.alt_lna_conf =
880 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
882 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
883 antcomb->rssi_sub = alt_rssi_avg;
884 antcomb->scan = AH_FALSE;
885 if (antcomb->rssi_lna2 >
886 (antcomb->rssi_lna1 +
887 ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
888 /* use LNA2 as main LNA */
889 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
890 (antcomb->rssi_add > antcomb->rssi_sub)) {
892 div_ant_conf.main_lna_conf =
893 HAL_ANT_DIV_COMB_LNA2;
894 div_ant_conf.alt_lna_conf =
895 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
896 } else if (antcomb->rssi_sub >
897 antcomb->rssi_lna1) {
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_MINUS_LNA2;
905 div_ant_conf.main_lna_conf =
906 HAL_ANT_DIV_COMB_LNA2;
907 div_ant_conf.alt_lna_conf =
908 HAL_ANT_DIV_COMB_LNA1;
911 /* use LNA1 as main LNA */
912 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
913 (antcomb->rssi_add > antcomb->rssi_sub)) {
915 div_ant_conf.main_lna_conf =
916 HAL_ANT_DIV_COMB_LNA1;
917 div_ant_conf.alt_lna_conf =
918 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
919 } else if (antcomb->rssi_sub >
920 antcomb->rssi_lna1) {
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_MINUS_LNA2;
928 div_ant_conf.main_lna_conf =
929 HAL_ANT_DIV_COMB_LNA1;
930 div_ant_conf.alt_lna_conf =
931 HAL_ANT_DIV_COMB_LNA2;
939 if (!antcomb->alt_good) {
940 antcomb->scan_not_start = AH_FALSE;
941 /* Set alt to another LNA */
942 if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
943 div_ant_conf.main_lna_conf =
944 HAL_ANT_DIV_COMB_LNA2;
945 div_ant_conf.alt_lna_conf =
946 HAL_ANT_DIV_COMB_LNA1;
947 } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
948 div_ant_conf.main_lna_conf =
949 HAL_ANT_DIV_COMB_LNA1;
950 div_ant_conf.alt_lna_conf =
951 HAL_ANT_DIV_COMB_LNA2;
957 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
958 main_rssi_avg, alt_rssi_avg,
961 antcomb->quick_scan_cnt++;
965 ath_ant_div_conf_fast_divbias(&div_ant_conf);
968 ath_ant_adjust_fast_divbias(antcomb,
970 ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
971 div_ant_conf.antdiv_configgroup,
974 ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
976 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
977 __func__, antcomb->total_pkt_count);
979 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
980 __func__, antcomb->main_total_rssi);
981 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
982 __func__, antcomb->alt_total_rssi);
984 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
985 __func__, main_rssi_avg);
986 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
987 __func__, alt_rssi_avg);
989 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
990 __func__, antcomb->main_recv_cnt);
991 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
992 __func__, antcomb->alt_recv_cnt);
994 // if (curr_alt_set != div_ant_conf.alt_lna_conf)
995 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
996 __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
997 // if (curr_main_set != div_ant_conf.main_lna_conf)
998 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
999 __func__, curr_main_set, div_ant_conf.main_lna_conf);
1000 // if (curr_bias != div_ant_conf.fast_div_bias)
1001 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1002 __func__, curr_bias, div_ant_conf.fast_div_bias);
1004 antcomb->scan_start_time = ticks;
1005 antcomb->total_pkt_count = 0;
1006 antcomb->main_total_rssi = 0;
1007 antcomb->alt_total_rssi = 0;
1008 antcomb->main_recv_cnt = 0;
1009 antcomb->alt_recv_cnt = 0;