xref: /freebsd/sys/dev/ath/if_ath_lna_div.c (revision 0afa8e065e14bb8fd338d75690e0238c00167d40)
1 /*-
2  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
3  *
4  * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
5  * All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer,
12  *    without modification.
13  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15  *    redistribution must be conditioned upon including a substantially
16  *    similar Disclaimer requirement for further binary redistribution.
17  *
18  * NO WARRANTY
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29  * THE POSSIBILITY OF SUCH DAMAGES.
30  *
31  * $FreeBSD$
32  */
33 #include <sys/cdefs.h>
34 __FBSDID("$FreeBSD$");
35 
36 /*
37  * This module handles LNA diversity for those chips which implement LNA
38  * mixing (AR9285/AR9485.)
39  */
40 #include "opt_ath.h"
41 #include "opt_inet.h"
42 #include "opt_wlan.h"
43 
44 #include <sys/param.h>
45 #include <sys/systm.h>
46 #include <sys/sysctl.h>
47 #include <sys/kernel.h>
48 #include <sys/lock.h>
49 #include <sys/malloc.h>
50 #include <sys/mutex.h>
51 #include <sys/errno.h>
52 
53 #include <machine/bus.h>
54 #include <machine/resource.h>
55 #include <sys/bus.h>
56 
57 #include <sys/socket.h>
58 
59 #include <net/if.h>
60 #include <net/if_var.h>
61 #include <net/if_media.h>
62 #include <net/if_arp.h>
63 #include <net/ethernet.h>		/* XXX for ether_sprintf */
64 
65 #include <net80211/ieee80211_var.h>
66 
67 #include <net/bpf.h>
68 
69 #ifdef INET
70 #include <netinet/in.h>
71 #include <netinet/if_ether.h>
72 #endif
73 
74 #include <dev/ath/if_athvar.h>
75 #include <dev/ath/if_ath_debug.h>
76 #include <dev/ath/if_ath_lna_div.h>
77 
78 /* Linux compatibility macros */
79 /*
80  * XXX these don't handle rounding, underflow, overflow, wrapping!
81  */
82 #define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
83 
84 /*
85  * Methods which are required
86  */
87 
88 /*
89  * Attach the LNA diversity to the given interface
90  */
91 int
92 ath_lna_div_attach(struct ath_softc *sc)
93 {
94 	struct if_ath_ant_comb_state *ss;
95 	HAL_ANT_COMB_CONFIG div_ant_conf;
96 
97 	/* Only do this if diversity is enabled */
98 	if (! ath_hal_hasdivantcomb(sc->sc_ah))
99 		return (0);
100 
101 	ss = malloc(sizeof(struct if_ath_ant_comb_state),
102 	    M_TEMP, M_WAITOK | M_ZERO);
103 	if (ss == NULL) {
104 		device_printf(sc->sc_dev, "%s: failed to allocate\n",
105 		    __func__);
106 		/* Don't fail at this point */
107 		return (0);
108 	}
109 
110 	/* Fetch the hardware configuration */
111 	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
112 	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
113 
114 	/* Figure out what the hardware specific bits should be */
115 	if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
116 	    (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
117 		ss->lna1_lna2_delta = -9;
118 	} else {
119 		ss->lna1_lna2_delta = -3;
120 	}
121 
122 	/* Let's flip this on */
123 	sc->sc_lna_div = ss;
124 	sc->sc_dolnadiv = 1;
125 
126 	return (0);
127 }
128 
129 /*
130  * Detach the LNA diversity state from the given interface
131  */
132 int
133 ath_lna_div_detach(struct ath_softc *sc)
134 {
135 	if (sc->sc_lna_div != NULL) {
136 		free(sc->sc_lna_div, M_TEMP);
137 		sc->sc_lna_div = NULL;
138 	}
139 	sc->sc_dolnadiv = 0;
140 	return (0);
141 }
142 
143 /*
144  * Enable LNA diversity on the current channel if it's required.
145  */
146 int
147 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
148 {
149 
150 	return (0);
151 }
152 
153 /*
154  * Handle ioctl requests from the diagnostic interface.
155  *
156  * The initial part of this code resembles ath_ioctl_diag();
157  * it's likely a good idea to reduce duplication between
158  * these two routines.
159  */
160 int
161 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
162 {
163 	unsigned int id = ad->ad_id & ATH_DIAG_ID;
164 	void *indata = NULL;
165 	void *outdata = NULL;
166 	u_int32_t insize = ad->ad_in_size;
167 	u_int32_t outsize = ad->ad_out_size;
168 	int error = 0;
169 //	int val;
170 
171 	if (ad->ad_id & ATH_DIAG_IN) {
172 		/*
173 		 * Copy in data.
174 		 */
175 		indata = malloc(insize, M_TEMP, M_NOWAIT);
176 		if (indata == NULL) {
177 			error = ENOMEM;
178 			goto bad;
179 		}
180 		error = copyin(ad->ad_in_data, indata, insize);
181 		if (error)
182 			goto bad;
183 	}
184 	if (ad->ad_id & ATH_DIAG_DYN) {
185 		/*
186 		 * Allocate a buffer for the results (otherwise the HAL
187 		 * returns a pointer to a buffer where we can read the
188 		 * results).  Note that we depend on the HAL leaving this
189 		 * pointer for us to use below in reclaiming the buffer;
190 		 * may want to be more defensive.
191 		 */
192 		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
193 		if (outdata == NULL) {
194 			error = ENOMEM;
195 			goto bad;
196 		}
197 	}
198 	switch (id) {
199 		default:
200 			error = EINVAL;
201 			goto bad;
202 	}
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))
206 		error = EFAULT;
207 bad:
208 	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
209 		free(indata, M_TEMP);
210 	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
211 		free(outdata, M_TEMP);
212 	return (error);
213 }
214 
215 /*
216  * XXX need to low_rssi_thresh config from ath9k, to support CUS198
217  * antenna diversity correctly.
218  */
219 static HAL_BOOL
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)
222 {
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);
226 }
227 
228 static void
229 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
230     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
231 {
232 	antcomb->quick_scan_cnt = 0;
233 
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;
238 
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;
245 		break;
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;
251 		break;
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;
258 		break;
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;
265 		break;
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;
271 		break;
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;
277 		break;
278 	default:
279 		break;
280 	}
281 }
282 
283 static void
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)
287 {
288 	/* alt_good */
289 	switch (antcomb->quick_scan_cnt) {
290 	case 0:
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;
294 		break;
295 	case 1:
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;
301 
302 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
303 			/* main is 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;
310 			else
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;
319 			else
320 				antcomb->first_ratio = AH_FALSE;
321 		} else {
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;
328 			else
329 				antcomb->first_ratio = AH_FALSE;
330 		}
331 		break;
332 	case 2:
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;
338 
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;
350 		}
351 
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;
355 		else
356 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
357 
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;
365 			else
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;
374 			else
375 				antcomb->second_ratio = AH_FALSE;
376 		} else {
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;
383 			else
384 				antcomb->second_ratio = AH_FALSE;
385 		}
386 
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) {
390 				/* first alt*/
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;
400 					else
401 						div_ant_conf->alt_lna_conf =
402 							HAL_ANT_DIV_COMB_LNA2;
403 				else
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;
416 				else
417 					div_ant_conf->alt_lna_conf =
418 						HAL_ANT_DIV_COMB_LNA2;
419 			} else {
420 				/* Set alt to A+B or A-B */
421 				div_ant_conf->alt_lna_conf =
422 					antcomb->second_quick_scan_conf;
423 			}
424 		} else if (antcomb->first_ratio) {
425 			/* first alt */
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;
435 				else
436 					div_ant_conf->alt_lna_conf =
437 							HAL_ANT_DIV_COMB_LNA2;
438 			else
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) {
443 				/* second alt */
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;
453 				else
454 					div_ant_conf->alt_lna_conf =
455 						HAL_ANT_DIV_COMB_LNA2;
456 			else
457 				/* Set alt to A+B or A-B */
458 				div_ant_conf->alt_lna_conf =
459 						antcomb->second_quick_scan_conf;
460 		} else {
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;
469 				else
470 					div_ant_conf->alt_lna_conf =
471 							HAL_ANT_DIV_COMB_LNA2;
472 			else
473 				/* Set alt to A+B or A-B */
474 				div_ant_conf->alt_lna_conf = antcomb->main_conf;
475 		}
476 		break;
477 	default:
478 		break;
479 	}
480 }
481 
482 static void
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)
486 {
487 
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;
495 			break;
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;
500 			break;
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;
505 			break;
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;
510 			} else {
511 				pdiv_ant_conf->fast_div_bias = 0x1;
512 			}
513 			pdiv_ant_conf->main_gaintb   = 0;
514 			pdiv_ant_conf->alt_gaintb    = 0;
515 			break;
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;
520 			break;
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;
525 			} else {
526 				pdiv_ant_conf->fast_div_bias = 0x1;
527 			}
528 			pdiv_ant_conf->main_gaintb   = 0;
529 			pdiv_ant_conf->alt_gaintb    = 0;
530 			break;
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;
535 			} else {
536 				pdiv_ant_conf->fast_div_bias = 0x1;
537 			}
538 			pdiv_ant_conf->main_gaintb   = 0;
539 			pdiv_ant_conf->alt_gaintb    = 0;
540 			break;
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;
545 			break;
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;
550 			} else {
551 				pdiv_ant_conf->fast_div_bias = 0x1;
552 			}
553 			pdiv_ant_conf->main_gaintb   = 0;
554 			pdiv_ant_conf->alt_gaintb    = 0;
555 			break;
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;
560 			break;
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;
565 			break;
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;
570 			break;
571 		default:
572 			break;
573 		}
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;
581 			break;
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;
586 			break;
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;
591 			break;
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;
596 			} else {
597 				pdiv_ant_conf->fast_div_bias = 0x2;
598 			}
599 			pdiv_ant_conf->main_gaintb   = 0;
600 			pdiv_ant_conf->alt_gaintb    = 0;
601 			break;
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;
606 			break;
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;
611 			} else {
612 				pdiv_ant_conf->fast_div_bias = 0x2;
613 			}
614 			pdiv_ant_conf->main_gaintb   = 0;
615 			pdiv_ant_conf->alt_gaintb    = 0;
616 			break;
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;
621 			} else {
622 				pdiv_ant_conf->fast_div_bias = 0x2;
623 			}
624 			pdiv_ant_conf->main_gaintb   = 0;
625 			pdiv_ant_conf->alt_gaintb    = 0;
626 			break;
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;
631 			break;
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;
636 			} else {
637 				pdiv_ant_conf->fast_div_bias = 0x2;
638 			}
639 			pdiv_ant_conf->main_gaintb   = 0;
640 			pdiv_ant_conf->alt_gaintb    = 0;
641 			break;
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;
646 			break;
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;
651 			break;
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;
656 			break;
657 		default:
658 			break;
659 		}
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;
664 			break;
665 		case (0x02): //A-B LNA1
666 			pdiv_ant_conf->fast_div_bias = 0x3d;
667 			break;
668 		case (0x03): //A-B A+B
669 			pdiv_ant_conf->fast_div_bias = 0x1;
670 			break;
671 		case (0x10): //LNA2 A-B
672 			pdiv_ant_conf->fast_div_bias = 0x7;
673 			break;
674 		case (0x12): //LNA2 LNA1
675 			pdiv_ant_conf->fast_div_bias = 0x2;
676 			break;
677 		case (0x13): //LNA2 A+B
678 			pdiv_ant_conf->fast_div_bias = 0x7;
679 			break;
680 		case (0x20): //LNA1 A-B
681 			pdiv_ant_conf->fast_div_bias = 0x6;
682 			break;
683 		case (0x21): //LNA1 LNA2
684 			pdiv_ant_conf->fast_div_bias = 0x0;
685 			break;
686 		case (0x23): //LNA1 A+B
687 			pdiv_ant_conf->fast_div_bias = 0x6;
688 			break;
689 		case (0x30): //A+B A-B
690 			pdiv_ant_conf->fast_div_bias = 0x1;
691 			break;
692 		case (0x31): //A+B LNA2
693 			pdiv_ant_conf->fast_div_bias = 0x3b;
694 			break;
695 		case (0x32): //A+B LNA1
696 			pdiv_ant_conf->fast_div_bias = 0x3d;
697 			break;
698 		default:
699 			break;
700 		}
701 	}
702 }
703 
704 /*
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.
710  */
711 
712 /* Antenna diversity and combining */
713 void
714 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
715     unsigned long ticks, int hz)
716 {
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;
725 
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;
729 
730 #if 0
731 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
732 	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
733 	    "FastDiv: %d\n",
734 	    __func__,
735 	    main_rssi,
736 	    alt_rssi,
737 	    main_ant_conf,
738 	    alt_ant_conf,
739 	    rx_ant_conf,
740 	    !!(rs->rs_rssi_ctl[2] & 0x80),
741 	    !!(rs->rs_rssi_ctl[2] & 0x40),
742 	    !!(rs->rs_rssi_ext[2] & 0x40));
743 #endif
744 
745 	/*
746 	 * If LNA diversity combining isn't enabled, don't run this.
747 	 */
748 	if (! sc->sc_dolnadiv)
749 		return;
750 
751 	/*
752 	 * XXX this is ugly, but the HAL code attaches the
753 	 * LNA diversity to the TX antenna settings.
754 	 * I don't know why.
755 	 */
756 	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
757 		return;
758 
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++;
766 		else
767 			antcomb->alt_recv_cnt++;
768 	}
769 
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;
775 		else
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;
782 			}
783 	}
784 
785 #if 0
786 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
787 	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
788 	    __func__,
789 	    antcomb->total_pkt_count,
790 	    !! (rs->rs_moreaggr),
791 	    !! (short_scan));
792 #endif
793 
794 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
795 	    rs->rs_moreaggr) && !short_scan)
796 		return;
797 
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);
805 	}
806 
807 	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
808 
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;
813 
814 	antcomb->count++;
815 
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,
819 						  main_rssi_avg);
820 			antcomb->alt_good = AH_TRUE;
821 		} else {
822 			antcomb->alt_good = AH_FALSE;
823 		}
824 
825 		antcomb->count = 0;
826 		antcomb->scan = AH_TRUE;
827 		antcomb->scan_not_start = AH_TRUE;
828 	}
829 
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;
843 			}
844 
845 			goto div_comb_done;
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;
855 
856 			goto div_comb_done;
857 		}
858 
859 		if ((alt_rssi_avg < (main_rssi_avg +
860 		    antcomb->lna1_lna2_delta)))
861 			goto div_comb_done;
862 	}
863 
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;
870 			/* set to A+B */
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;
875 			break;
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;
880 			/* set to A+B */
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;
884 			break;
885 		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
886 			antcomb->rssi_add = alt_rssi_avg;
887 			antcomb->scan = AH_TRUE;
888 			/* set to A-B */
889 			div_ant_conf.alt_lna_conf =
890 				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
891 			break;
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)) {
901 					/* set to A+B */
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) {
908 					/* set to A-B */
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;
913 				} else {
914 					/* set to LNA1 */
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;
919 				}
920 			} else {
921 				/* use LNA1 as main LNA */
922 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
923 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
924 					/* set to A+B */
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) {
931 					/* set to A-B */
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;
936 				} else {
937 					/* set to 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;
942 				}
943 			}
944 			break;
945 		default:
946 			break;
947 		}
948 	} else {
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;
962 			}
963 			goto div_comb_done;
964 		}
965 	}
966 
967 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
968 					   main_rssi_avg, alt_rssi_avg,
969 					   alt_ratio);
970 
971 	antcomb->quick_scan_cnt++;
972 
973 div_comb_done:
974 #if 0
975 	ath_ant_div_conf_fast_divbias(&div_ant_conf);
976 #endif
977 
978 	ath_ant_adjust_fast_divbias(antcomb,
979 	    alt_ratio,
980 	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
981 	    div_ant_conf.antdiv_configgroup,
982 	    &div_ant_conf);
983 
984 	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
985 
986 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
987 	   __func__, antcomb->total_pkt_count);
988 
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);
993 
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);
998 
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);
1003 
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);
1013 
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;
1020 }
1021