xref: /freebsd/sys/dev/ath/if_ath_lna_div.c (revision d6b92ffa)
1 /*-
2  * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
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.
15  *
16  * NO WARRANTY
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.
28  *
29  * $FreeBSD$
30  */
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33 
34 /*
35  * This module handles LNA diversity for those chips which implement LNA
36  * mixing (AR9285/AR9485.)
37  */
38 #include "opt_ath.h"
39 #include "opt_inet.h"
40 #include "opt_wlan.h"
41 
42 #include <sys/param.h>
43 #include <sys/systm.h>
44 #include <sys/sysctl.h>
45 #include <sys/kernel.h>
46 #include <sys/lock.h>
47 #include <sys/malloc.h>
48 #include <sys/mutex.h>
49 #include <sys/errno.h>
50 
51 #include <machine/bus.h>
52 #include <machine/resource.h>
53 #include <sys/bus.h>
54 
55 #include <sys/socket.h>
56 
57 #include <net/if.h>
58 #include <net/if_var.h>
59 #include <net/if_media.h>
60 #include <net/if_arp.h>
61 #include <net/ethernet.h>		/* XXX for ether_sprintf */
62 
63 #include <net80211/ieee80211_var.h>
64 
65 #include <net/bpf.h>
66 
67 #ifdef INET
68 #include <netinet/in.h>
69 #include <netinet/if_ether.h>
70 #endif
71 
72 #include <dev/ath/if_athvar.h>
73 #include <dev/ath/if_ath_debug.h>
74 #include <dev/ath/if_ath_lna_div.h>
75 
76 /* Linux compatibility macros */
77 /*
78  * XXX these don't handle rounding, underflow, overflow, wrapping!
79  */
80 #define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
81 
82 /*
83  * Methods which are required
84  */
85 
86 /*
87  * Attach the LNA diversity to the given interface
88  */
89 int
90 ath_lna_div_attach(struct ath_softc *sc)
91 {
92 	struct if_ath_ant_comb_state *ss;
93 	HAL_ANT_COMB_CONFIG div_ant_conf;
94 
95 	/* Only do this if diversity is enabled */
96 	if (! ath_hal_hasdivantcomb(sc->sc_ah))
97 		return (0);
98 
99 	ss = malloc(sizeof(struct if_ath_ant_comb_state),
100 	    M_TEMP, M_WAITOK | M_ZERO);
101 	if (ss == NULL) {
102 		device_printf(sc->sc_dev, "%s: failed to allocate\n",
103 		    __func__);
104 		/* Don't fail at this point */
105 		return (0);
106 	}
107 
108 	/* Fetch the hardware configuration */
109 	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
110 	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
111 
112 	/* Figure out what the hardware specific bits should be */
113 	if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
114 	    (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
115 		ss->lna1_lna2_delta = -9;
116 	} else {
117 		ss->lna1_lna2_delta = -3;
118 	}
119 
120 	/* Let's flip this on */
121 	sc->sc_lna_div = ss;
122 	sc->sc_dolnadiv = 1;
123 
124 	return (0);
125 }
126 
127 /*
128  * Detach the LNA diversity state from the given interface
129  */
130 int
131 ath_lna_div_detach(struct ath_softc *sc)
132 {
133 	if (sc->sc_lna_div != NULL) {
134 		free(sc->sc_lna_div, M_TEMP);
135 		sc->sc_lna_div = NULL;
136 	}
137 	sc->sc_dolnadiv = 0;
138 	return (0);
139 }
140 
141 /*
142  * Enable LNA diversity on the current channel if it's required.
143  */
144 int
145 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
146 {
147 
148 	return (0);
149 }
150 
151 /*
152  * Handle ioctl requests from the diagnostic interface.
153  *
154  * The initial part of this code resembles ath_ioctl_diag();
155  * it's likely a good idea to reduce duplication between
156  * these two routines.
157  */
158 int
159 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
160 {
161 	unsigned int id = ad->ad_id & ATH_DIAG_ID;
162 	void *indata = NULL;
163 	void *outdata = NULL;
164 	u_int32_t insize = ad->ad_in_size;
165 	u_int32_t outsize = ad->ad_out_size;
166 	int error = 0;
167 //	int val;
168 
169 	if (ad->ad_id & ATH_DIAG_IN) {
170 		/*
171 		 * Copy in data.
172 		 */
173 		indata = malloc(insize, M_TEMP, M_NOWAIT);
174 		if (indata == NULL) {
175 			error = ENOMEM;
176 			goto bad;
177 		}
178 		error = copyin(ad->ad_in_data, indata, insize);
179 		if (error)
180 			goto bad;
181 	}
182 	if (ad->ad_id & ATH_DIAG_DYN) {
183 		/*
184 		 * Allocate a buffer for the results (otherwise the HAL
185 		 * returns a pointer to a buffer where we can read the
186 		 * results).  Note that we depend on the HAL leaving this
187 		 * pointer for us to use below in reclaiming the buffer;
188 		 * may want to be more defensive.
189 		 */
190 		outdata = malloc(outsize, M_TEMP, M_NOWAIT);
191 		if (outdata == NULL) {
192 			error = ENOMEM;
193 			goto bad;
194 		}
195 	}
196 	switch (id) {
197 		default:
198 			error = EINVAL;
199 	}
200 	if (outsize < ad->ad_out_size)
201 		ad->ad_out_size = outsize;
202 	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
203 		error = EFAULT;
204 bad:
205 	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
206 		free(indata, M_TEMP);
207 	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
208 		free(outdata, M_TEMP);
209 	return (error);
210 }
211 
212 /*
213  * XXX need to low_rssi_thresh config from ath9k, to support CUS198
214  * antenna diversity correctly.
215  */
216 static HAL_BOOL
217 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
218     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
219 {
220 	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
221 		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
222 		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
223 }
224 
225 static void
226 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
227     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
228 {
229 	antcomb->quick_scan_cnt = 0;
230 
231 	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
232 		antcomb->rssi_lna2 = main_rssi_avg;
233 	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
234 		antcomb->rssi_lna1 = main_rssi_avg;
235 
236 	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
237 	case (0x10): /* LNA2 A-B */
238 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
239 		antcomb->first_quick_scan_conf =
240 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
241 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
242 		break;
243 	case (0x20): /* LNA1 A-B */
244 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
245 		antcomb->first_quick_scan_conf =
246 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
247 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
248 		break;
249 	case (0x21): /* LNA1 LNA2 */
250 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
251 		antcomb->first_quick_scan_conf =
252 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
253 		antcomb->second_quick_scan_conf =
254 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
255 		break;
256 	case (0x12): /* LNA2 LNA1 */
257 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
258 		antcomb->first_quick_scan_conf =
259 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
260 		antcomb->second_quick_scan_conf =
261 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
262 		break;
263 	case (0x13): /* LNA2 A+B */
264 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
265 		antcomb->first_quick_scan_conf =
266 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
267 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
268 		break;
269 	case (0x23): /* LNA1 A+B */
270 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
271 		antcomb->first_quick_scan_conf =
272 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
273 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
274 		break;
275 	default:
276 		break;
277 	}
278 }
279 
280 static void
281 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
282     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
283     int alt_rssi_avg, int alt_ratio)
284 {
285 	/* alt_good */
286 	switch (antcomb->quick_scan_cnt) {
287 	case 0:
288 		/* set alt to main, and alt to first conf */
289 		div_ant_conf->main_lna_conf = antcomb->main_conf;
290 		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
291 		break;
292 	case 1:
293 		/* set alt to main, and alt to first conf */
294 		div_ant_conf->main_lna_conf = antcomb->main_conf;
295 		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
296 		antcomb->rssi_first = main_rssi_avg;
297 		antcomb->rssi_second = alt_rssi_avg;
298 
299 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
300 			/* main is LNA1 */
301 			if (ath_is_alt_ant_ratio_better(alt_ratio,
302 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
303 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
304 						main_rssi_avg, alt_rssi_avg,
305 						antcomb->total_pkt_count))
306 				antcomb->first_ratio = AH_TRUE;
307 			else
308 				antcomb->first_ratio = AH_FALSE;
309 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
310 			if (ath_is_alt_ant_ratio_better(alt_ratio,
311 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
312 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
313 						main_rssi_avg, alt_rssi_avg,
314 						antcomb->total_pkt_count))
315 				antcomb->first_ratio = AH_TRUE;
316 			else
317 				antcomb->first_ratio = AH_FALSE;
318 		} else {
319 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
320 			    (alt_rssi_avg > main_rssi_avg +
321 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
322 			    (alt_rssi_avg > main_rssi_avg)) &&
323 			    (antcomb->total_pkt_count > 50))
324 				antcomb->first_ratio = AH_TRUE;
325 			else
326 				antcomb->first_ratio = AH_FALSE;
327 		}
328 		break;
329 	case 2:
330 		antcomb->alt_good = AH_FALSE;
331 		antcomb->scan_not_start = AH_FALSE;
332 		antcomb->scan = AH_FALSE;
333 		antcomb->rssi_first = main_rssi_avg;
334 		antcomb->rssi_third = alt_rssi_avg;
335 
336 		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
337 			antcomb->rssi_lna1 = alt_rssi_avg;
338 		else if (antcomb->second_quick_scan_conf ==
339 			 HAL_ANT_DIV_COMB_LNA2)
340 			antcomb->rssi_lna2 = alt_rssi_avg;
341 		else if (antcomb->second_quick_scan_conf ==
342 			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
343 			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
344 				antcomb->rssi_lna2 = main_rssi_avg;
345 			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
346 				antcomb->rssi_lna1 = main_rssi_avg;
347 		}
348 
349 		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
350 		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
351 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
352 		else
353 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
354 
355 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
356 			if (ath_is_alt_ant_ratio_better(alt_ratio,
357 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
358 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
359 						main_rssi_avg, alt_rssi_avg,
360 						antcomb->total_pkt_count))
361 				antcomb->second_ratio = AH_TRUE;
362 			else
363 				antcomb->second_ratio = AH_FALSE;
364 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
365 			if (ath_is_alt_ant_ratio_better(alt_ratio,
366 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
367 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
368 						main_rssi_avg, alt_rssi_avg,
369 						antcomb->total_pkt_count))
370 				antcomb->second_ratio = AH_TRUE;
371 			else
372 				antcomb->second_ratio = AH_FALSE;
373 		} else {
374 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
375 			    (alt_rssi_avg > main_rssi_avg +
376 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
377 			    (alt_rssi_avg > main_rssi_avg)) &&
378 			    (antcomb->total_pkt_count > 50))
379 				antcomb->second_ratio = AH_TRUE;
380 			else
381 				antcomb->second_ratio = AH_FALSE;
382 		}
383 
384 		/* set alt to the conf with maximun ratio */
385 		if (antcomb->first_ratio && antcomb->second_ratio) {
386 			if (antcomb->rssi_second > antcomb->rssi_third) {
387 				/* first alt*/
388 				if ((antcomb->first_quick_scan_conf ==
389 				    HAL_ANT_DIV_COMB_LNA1) ||
390 				    (antcomb->first_quick_scan_conf ==
391 				    HAL_ANT_DIV_COMB_LNA2))
392 					/* Set alt LNA1 or LNA2*/
393 					if (div_ant_conf->main_lna_conf ==
394 					    HAL_ANT_DIV_COMB_LNA2)
395 						div_ant_conf->alt_lna_conf =
396 							HAL_ANT_DIV_COMB_LNA1;
397 					else
398 						div_ant_conf->alt_lna_conf =
399 							HAL_ANT_DIV_COMB_LNA2;
400 				else
401 					/* Set alt to A+B or A-B */
402 					div_ant_conf->alt_lna_conf =
403 						antcomb->first_quick_scan_conf;
404 			} else if ((antcomb->second_quick_scan_conf ==
405 				   HAL_ANT_DIV_COMB_LNA1) ||
406 				   (antcomb->second_quick_scan_conf ==
407 				   HAL_ANT_DIV_COMB_LNA2)) {
408 				/* Set alt LNA1 or LNA2 */
409 				if (div_ant_conf->main_lna_conf ==
410 				    HAL_ANT_DIV_COMB_LNA2)
411 					div_ant_conf->alt_lna_conf =
412 						HAL_ANT_DIV_COMB_LNA1;
413 				else
414 					div_ant_conf->alt_lna_conf =
415 						HAL_ANT_DIV_COMB_LNA2;
416 			} else {
417 				/* Set alt to A+B or A-B */
418 				div_ant_conf->alt_lna_conf =
419 					antcomb->second_quick_scan_conf;
420 			}
421 		} else if (antcomb->first_ratio) {
422 			/* first alt */
423 			if ((antcomb->first_quick_scan_conf ==
424 			    HAL_ANT_DIV_COMB_LNA1) ||
425 			    (antcomb->first_quick_scan_conf ==
426 			    HAL_ANT_DIV_COMB_LNA2))
427 					/* Set alt LNA1 or LNA2 */
428 				if (div_ant_conf->main_lna_conf ==
429 				    HAL_ANT_DIV_COMB_LNA2)
430 					div_ant_conf->alt_lna_conf =
431 							HAL_ANT_DIV_COMB_LNA1;
432 				else
433 					div_ant_conf->alt_lna_conf =
434 							HAL_ANT_DIV_COMB_LNA2;
435 			else
436 				/* Set alt to A+B or A-B */
437 				div_ant_conf->alt_lna_conf =
438 						antcomb->first_quick_scan_conf;
439 		} else if (antcomb->second_ratio) {
440 				/* second alt */
441 			if ((antcomb->second_quick_scan_conf ==
442 			    HAL_ANT_DIV_COMB_LNA1) ||
443 			    (antcomb->second_quick_scan_conf ==
444 			    HAL_ANT_DIV_COMB_LNA2))
445 				/* Set alt LNA1 or LNA2 */
446 				if (div_ant_conf->main_lna_conf ==
447 				    HAL_ANT_DIV_COMB_LNA2)
448 					div_ant_conf->alt_lna_conf =
449 						HAL_ANT_DIV_COMB_LNA1;
450 				else
451 					div_ant_conf->alt_lna_conf =
452 						HAL_ANT_DIV_COMB_LNA2;
453 			else
454 				/* Set alt to A+B or A-B */
455 				div_ant_conf->alt_lna_conf =
456 						antcomb->second_quick_scan_conf;
457 		} else {
458 			/* main is largest */
459 			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
460 			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
461 				/* Set alt LNA1 or LNA2 */
462 				if (div_ant_conf->main_lna_conf ==
463 				    HAL_ANT_DIV_COMB_LNA2)
464 					div_ant_conf->alt_lna_conf =
465 							HAL_ANT_DIV_COMB_LNA1;
466 				else
467 					div_ant_conf->alt_lna_conf =
468 							HAL_ANT_DIV_COMB_LNA2;
469 			else
470 				/* Set alt to A+B or A-B */
471 				div_ant_conf->alt_lna_conf = antcomb->main_conf;
472 		}
473 		break;
474 	default:
475 		break;
476 	}
477 }
478 
479 static void
480 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
481     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
482     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
483 {
484 
485 	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
486 		switch ((pdiv_ant_conf->main_lna_conf << 4)
487 		    | pdiv_ant_conf->alt_lna_conf) {
488 		case (0x01): //A-B LNA2
489 			pdiv_ant_conf->fast_div_bias = 0x1;
490 			pdiv_ant_conf->main_gaintb   = 0;
491 			pdiv_ant_conf->alt_gaintb    = 0;
492 			break;
493 		case (0x02): //A-B LNA1
494 			pdiv_ant_conf->fast_div_bias = 0x1;
495 			pdiv_ant_conf->main_gaintb   = 0;
496 			pdiv_ant_conf->alt_gaintb    = 0;
497 			break;
498 		case (0x03): //A-B A+B
499 			pdiv_ant_conf->fast_div_bias = 0x1;
500 			pdiv_ant_conf->main_gaintb   = 0;
501 			pdiv_ant_conf->alt_gaintb    = 0;
502 			break;
503 		case (0x10): //LNA2 A-B
504 			if ((antcomb->scan == 0)
505 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
506 				pdiv_ant_conf->fast_div_bias = 0x3f;
507 			} else {
508 				pdiv_ant_conf->fast_div_bias = 0x1;
509 			}
510 			pdiv_ant_conf->main_gaintb   = 0;
511 			pdiv_ant_conf->alt_gaintb    = 0;
512 			break;
513 		case (0x12): //LNA2 LNA1
514 			pdiv_ant_conf->fast_div_bias = 0x1;
515 			pdiv_ant_conf->main_gaintb   = 0;
516 			pdiv_ant_conf->alt_gaintb    = 0;
517 			break;
518 			case (0x13): //LNA2 A+B
519 			if ((antcomb->scan == 0)
520 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
521 				pdiv_ant_conf->fast_div_bias = 0x3f;
522 			} else {
523 				pdiv_ant_conf->fast_div_bias = 0x1;
524 			}
525 			pdiv_ant_conf->main_gaintb   = 0;
526 			pdiv_ant_conf->alt_gaintb    = 0;
527 			break;
528 		case (0x20): //LNA1 A-B
529 			if ((antcomb->scan == 0)
530 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
531 				pdiv_ant_conf->fast_div_bias = 0x3f;
532 			} else {
533 				pdiv_ant_conf->fast_div_bias = 0x1;
534 			}
535 			pdiv_ant_conf->main_gaintb   = 0;
536 			pdiv_ant_conf->alt_gaintb    = 0;
537 			break;
538 		case (0x21): //LNA1 LNA2
539 			pdiv_ant_conf->fast_div_bias = 0x1;
540 			pdiv_ant_conf->main_gaintb   = 0;
541 			pdiv_ant_conf->alt_gaintb    = 0;
542 			break;
543 		case (0x23): //LNA1 A+B
544 			if ((antcomb->scan == 0)
545 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
546 				pdiv_ant_conf->fast_div_bias = 0x3f;
547 			} else {
548 				pdiv_ant_conf->fast_div_bias = 0x1;
549 			}
550 			pdiv_ant_conf->main_gaintb   = 0;
551 			pdiv_ant_conf->alt_gaintb    = 0;
552 			break;
553 		case (0x30): //A+B A-B
554 			pdiv_ant_conf->fast_div_bias = 0x1;
555 			pdiv_ant_conf->main_gaintb   = 0;
556 			pdiv_ant_conf->alt_gaintb    = 0;
557 			break;
558 		case (0x31): //A+B LNA2
559 			pdiv_ant_conf->fast_div_bias = 0x1;
560 			pdiv_ant_conf->main_gaintb   = 0;
561 			pdiv_ant_conf->alt_gaintb    = 0;
562 			break;
563 		case (0x32): //A+B LNA1
564 			pdiv_ant_conf->fast_div_bias = 0x1;
565 			pdiv_ant_conf->main_gaintb   = 0;
566 			pdiv_ant_conf->alt_gaintb    = 0;
567 			break;
568 		default:
569 			break;
570 		}
571 	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
572 		switch ((pdiv_ant_conf->main_lna_conf << 4)
573 		    | pdiv_ant_conf->alt_lna_conf) {
574 		case (0x01): //A-B LNA2
575 			pdiv_ant_conf->fast_div_bias = 0x1;
576 			pdiv_ant_conf->main_gaintb   = 0;
577 			pdiv_ant_conf->alt_gaintb    = 0;
578 			break;
579 		case (0x02): //A-B LNA1
580 			pdiv_ant_conf->fast_div_bias = 0x1;
581 			pdiv_ant_conf->main_gaintb   = 0;
582 			pdiv_ant_conf->alt_gaintb    = 0;
583 			break;
584 		case (0x03): //A-B A+B
585 			pdiv_ant_conf->fast_div_bias = 0x1;
586 			pdiv_ant_conf->main_gaintb   = 0;
587 			pdiv_ant_conf->alt_gaintb    = 0;
588 			break;
589 		case (0x10): //LNA2 A-B
590 			if ((antcomb->scan == 0)
591 			    && (alt_ratio > alt_ant_ratio_th)) {
592 				pdiv_ant_conf->fast_div_bias = 0x1;
593 			} else {
594 				pdiv_ant_conf->fast_div_bias = 0x2;
595 			}
596 			pdiv_ant_conf->main_gaintb   = 0;
597 			pdiv_ant_conf->alt_gaintb    = 0;
598 			break;
599 		case (0x12): //LNA2 LNA1
600 			pdiv_ant_conf->fast_div_bias = 0x1;
601 			pdiv_ant_conf->main_gaintb   = 0;
602 			pdiv_ant_conf->alt_gaintb    = 0;
603 			break;
604 		case (0x13): //LNA2 A+B
605 			if ((antcomb->scan == 0)
606 			    && (alt_ratio > alt_ant_ratio_th)) {
607 				pdiv_ant_conf->fast_div_bias = 0x1;
608 			} else {
609 				pdiv_ant_conf->fast_div_bias = 0x2;
610 			}
611 			pdiv_ant_conf->main_gaintb   = 0;
612 			pdiv_ant_conf->alt_gaintb    = 0;
613 			break;
614 		case (0x20): //LNA1 A-B
615 			if ((antcomb->scan == 0)
616 			    && (alt_ratio > alt_ant_ratio_th)) {
617 				pdiv_ant_conf->fast_div_bias = 0x1;
618 			} else {
619 				pdiv_ant_conf->fast_div_bias = 0x2;
620 			}
621 			pdiv_ant_conf->main_gaintb   = 0;
622 			pdiv_ant_conf->alt_gaintb    = 0;
623 			break;
624 		case (0x21): //LNA1 LNA2
625 			pdiv_ant_conf->fast_div_bias = 0x1;
626 			pdiv_ant_conf->main_gaintb   = 0;
627 			pdiv_ant_conf->alt_gaintb    = 0;
628 			break;
629 		case (0x23): //LNA1 A+B
630 			if ((antcomb->scan == 0)
631 			    && (alt_ratio > alt_ant_ratio_th)) {
632 				pdiv_ant_conf->fast_div_bias = 0x1;
633 			} else {
634 				pdiv_ant_conf->fast_div_bias = 0x2;
635 			}
636 			pdiv_ant_conf->main_gaintb   = 0;
637 			pdiv_ant_conf->alt_gaintb    = 0;
638 			break;
639 		case (0x30): //A+B A-B
640 			pdiv_ant_conf->fast_div_bias = 0x1;
641 			pdiv_ant_conf->main_gaintb   = 0;
642 			pdiv_ant_conf->alt_gaintb    = 0;
643 			break;
644 		case (0x31): //A+B LNA2
645 			pdiv_ant_conf->fast_div_bias = 0x1;
646 			pdiv_ant_conf->main_gaintb   = 0;
647 			pdiv_ant_conf->alt_gaintb    = 0;
648 			break;
649 		case (0x32): //A+B LNA1
650 			pdiv_ant_conf->fast_div_bias = 0x1;
651 			pdiv_ant_conf->main_gaintb   = 0;
652 			pdiv_ant_conf->alt_gaintb    = 0;
653 			break;
654 		default:
655 			break;
656 		}
657 	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
658 		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
659 		case (0x01): //A-B LNA2
660 			pdiv_ant_conf->fast_div_bias = 0x3b;
661 			break;
662 		case (0x02): //A-B LNA1
663 			pdiv_ant_conf->fast_div_bias = 0x3d;
664 			break;
665 		case (0x03): //A-B A+B
666 			pdiv_ant_conf->fast_div_bias = 0x1;
667 			break;
668 		case (0x10): //LNA2 A-B
669 			pdiv_ant_conf->fast_div_bias = 0x7;
670 			break;
671 		case (0x12): //LNA2 LNA1
672 			pdiv_ant_conf->fast_div_bias = 0x2;
673 			break;
674 		case (0x13): //LNA2 A+B
675 			pdiv_ant_conf->fast_div_bias = 0x7;
676 			break;
677 		case (0x20): //LNA1 A-B
678 			pdiv_ant_conf->fast_div_bias = 0x6;
679 			break;
680 		case (0x21): //LNA1 LNA2
681 			pdiv_ant_conf->fast_div_bias = 0x0;
682 			break;
683 		case (0x23): //LNA1 A+B
684 			pdiv_ant_conf->fast_div_bias = 0x6;
685 			break;
686 		case (0x30): //A+B A-B
687 			pdiv_ant_conf->fast_div_bias = 0x1;
688 			break;
689 		case (0x31): //A+B LNA2
690 			pdiv_ant_conf->fast_div_bias = 0x3b;
691 			break;
692 		case (0x32): //A+B LNA1
693 			pdiv_ant_conf->fast_div_bias = 0x3d;
694 			break;
695 		default:
696 			break;
697 		}
698 	}
699 }
700 
701 /*
702  * AR9485/AR933x TODO:
703  * + Select a ratio based on whether RSSI is low or not; but I need
704  *   to figure out what "low_rssi_th" is sourced from.
705  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
706  * + .. and there's likely a bunch of other things to include in this.
707  */
708 
709 /* Antenna diversity and combining */
710 void
711 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
712     unsigned long ticks, int hz)
713 {
714 	HAL_ANT_COMB_CONFIG div_ant_conf;
715 	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
716 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
717 	int curr_main_set, curr_bias;
718 	int main_rssi = rs->rs_rssi_ctl[0];
719 	int alt_rssi = rs->rs_rssi_ctl[1];
720 	int rx_ant_conf, main_ant_conf, alt_ant_conf;
721 	HAL_BOOL short_scan = AH_FALSE;
722 
723 	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
724 	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
725 	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
726 
727 #if 0
728 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
729 	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
730 	    "FastDiv: %d\n",
731 	    __func__,
732 	    main_rssi,
733 	    alt_rssi,
734 	    main_ant_conf,
735 	    alt_ant_conf,
736 	    rx_ant_conf,
737 	    !!(rs->rs_rssi_ctl[2] & 0x80),
738 	    !!(rs->rs_rssi_ctl[2] & 0x40),
739 	    !!(rs->rs_rssi_ext[2] & 0x40));
740 #endif
741 
742 	/*
743 	 * If LNA diversity combining isn't enabled, don't run this.
744 	 */
745 	if (! sc->sc_dolnadiv)
746 		return;
747 
748 	/*
749 	 * XXX this is ugly, but the HAL code attaches the
750 	 * LNA diversity to the TX antenna settings.
751 	 * I don't know why.
752 	 */
753 	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
754 		return;
755 
756 	/* Record packet only when alt_rssi is positive */
757 	if (main_rssi > 0 && alt_rssi > 0) {
758 		antcomb->total_pkt_count++;
759 		antcomb->main_total_rssi += main_rssi;
760 		antcomb->alt_total_rssi  += alt_rssi;
761 		if (main_ant_conf == rx_ant_conf)
762 			antcomb->main_recv_cnt++;
763 		else
764 			antcomb->alt_recv_cnt++;
765 	}
766 
767 	/* Short scan check */
768 	if (antcomb->scan && antcomb->alt_good) {
769 		if (ieee80211_time_after(ticks, antcomb->scan_start_time +
770 		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
771 			short_scan = AH_TRUE;
772 		else
773 			if (antcomb->total_pkt_count ==
774 			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
775 				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
776 					    antcomb->total_pkt_count);
777 				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
778 					short_scan = AH_TRUE;
779 			}
780 	}
781 
782 #if 0
783 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
784 	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
785 	    __func__,
786 	    antcomb->total_pkt_count,
787 	    !! (rs->rs_moreaggr),
788 	    !! (short_scan));
789 #endif
790 
791 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
792 	    rs->rs_moreaggr) && !short_scan)
793 		return;
794 
795 	if (antcomb->total_pkt_count) {
796 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
797 			     antcomb->total_pkt_count);
798 		main_rssi_avg = (antcomb->main_total_rssi /
799 				 antcomb->total_pkt_count);
800 		alt_rssi_avg = (antcomb->alt_total_rssi /
801 				 antcomb->total_pkt_count);
802 	}
803 
804 	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
805 
806 	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
807 	curr_alt_set = div_ant_conf.alt_lna_conf;
808 	curr_main_set = div_ant_conf.main_lna_conf;
809 	curr_bias = div_ant_conf.fast_div_bias;
810 
811 	antcomb->count++;
812 
813 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
814 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
815 			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
816 						  main_rssi_avg);
817 			antcomb->alt_good = AH_TRUE;
818 		} else {
819 			antcomb->alt_good = AH_FALSE;
820 		}
821 
822 		antcomb->count = 0;
823 		antcomb->scan = AH_TRUE;
824 		antcomb->scan_not_start = AH_TRUE;
825 	}
826 
827 	if (!antcomb->scan) {
828 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
829 			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
830 				/* Switch main and alt LNA */
831 				div_ant_conf.main_lna_conf =
832 						HAL_ANT_DIV_COMB_LNA2;
833 				div_ant_conf.alt_lna_conf  =
834 						HAL_ANT_DIV_COMB_LNA1;
835 			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
836 				div_ant_conf.main_lna_conf =
837 						HAL_ANT_DIV_COMB_LNA1;
838 				div_ant_conf.alt_lna_conf  =
839 						HAL_ANT_DIV_COMB_LNA2;
840 			}
841 
842 			goto div_comb_done;
843 		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
844 			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
845 			/* Set alt to another LNA */
846 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
847 				div_ant_conf.alt_lna_conf =
848 						HAL_ANT_DIV_COMB_LNA1;
849 			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
850 				div_ant_conf.alt_lna_conf =
851 						HAL_ANT_DIV_COMB_LNA2;
852 
853 			goto div_comb_done;
854 		}
855 
856 		if ((alt_rssi_avg < (main_rssi_avg +
857 		    antcomb->lna1_lna2_delta)))
858 			goto div_comb_done;
859 	}
860 
861 	if (!antcomb->scan_not_start) {
862 		switch (curr_alt_set) {
863 		case HAL_ANT_DIV_COMB_LNA2:
864 			antcomb->rssi_lna2 = alt_rssi_avg;
865 			antcomb->rssi_lna1 = main_rssi_avg;
866 			antcomb->scan = AH_TRUE;
867 			/* set to A+B */
868 			div_ant_conf.main_lna_conf =
869 				HAL_ANT_DIV_COMB_LNA1;
870 			div_ant_conf.alt_lna_conf  =
871 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
872 			break;
873 		case HAL_ANT_DIV_COMB_LNA1:
874 			antcomb->rssi_lna1 = alt_rssi_avg;
875 			antcomb->rssi_lna2 = main_rssi_avg;
876 			antcomb->scan = AH_TRUE;
877 			/* set to A+B */
878 			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
879 			div_ant_conf.alt_lna_conf  =
880 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
881 			break;
882 		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
883 			antcomb->rssi_add = alt_rssi_avg;
884 			antcomb->scan = AH_TRUE;
885 			/* set to A-B */
886 			div_ant_conf.alt_lna_conf =
887 				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
888 			break;
889 		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
890 			antcomb->rssi_sub = alt_rssi_avg;
891 			antcomb->scan = AH_FALSE;
892 			if (antcomb->rssi_lna2 >
893 			    (antcomb->rssi_lna1 +
894 			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
895 				/* use LNA2 as main LNA */
896 				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
897 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
898 					/* set to A+B */
899 					div_ant_conf.main_lna_conf =
900 						HAL_ANT_DIV_COMB_LNA2;
901 					div_ant_conf.alt_lna_conf  =
902 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
903 				} else if (antcomb->rssi_sub >
904 					   antcomb->rssi_lna1) {
905 					/* set to A-B */
906 					div_ant_conf.main_lna_conf =
907 						HAL_ANT_DIV_COMB_LNA2;
908 					div_ant_conf.alt_lna_conf =
909 						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
910 				} else {
911 					/* set to LNA1 */
912 					div_ant_conf.main_lna_conf =
913 						HAL_ANT_DIV_COMB_LNA2;
914 					div_ant_conf.alt_lna_conf =
915 						HAL_ANT_DIV_COMB_LNA1;
916 				}
917 			} else {
918 				/* use LNA1 as main LNA */
919 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
920 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
921 					/* set to A+B */
922 					div_ant_conf.main_lna_conf =
923 						HAL_ANT_DIV_COMB_LNA1;
924 					div_ant_conf.alt_lna_conf  =
925 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
926 				} else if (antcomb->rssi_sub >
927 					   antcomb->rssi_lna1) {
928 					/* set to A-B */
929 					div_ant_conf.main_lna_conf =
930 						HAL_ANT_DIV_COMB_LNA1;
931 					div_ant_conf.alt_lna_conf =
932 						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
933 				} else {
934 					/* set to LNA2 */
935 					div_ant_conf.main_lna_conf =
936 						HAL_ANT_DIV_COMB_LNA1;
937 					div_ant_conf.alt_lna_conf =
938 						HAL_ANT_DIV_COMB_LNA2;
939 				}
940 			}
941 			break;
942 		default:
943 			break;
944 		}
945 	} else {
946 		if (!antcomb->alt_good) {
947 			antcomb->scan_not_start = AH_FALSE;
948 			/* Set alt to another LNA */
949 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
950 				div_ant_conf.main_lna_conf =
951 						HAL_ANT_DIV_COMB_LNA2;
952 				div_ant_conf.alt_lna_conf =
953 						HAL_ANT_DIV_COMB_LNA1;
954 			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
955 				div_ant_conf.main_lna_conf =
956 						HAL_ANT_DIV_COMB_LNA1;
957 				div_ant_conf.alt_lna_conf =
958 						HAL_ANT_DIV_COMB_LNA2;
959 			}
960 			goto div_comb_done;
961 		}
962 	}
963 
964 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
965 					   main_rssi_avg, alt_rssi_avg,
966 					   alt_ratio);
967 
968 	antcomb->quick_scan_cnt++;
969 
970 div_comb_done:
971 #if 0
972 	ath_ant_div_conf_fast_divbias(&div_ant_conf);
973 #endif
974 
975 	ath_ant_adjust_fast_divbias(antcomb,
976 	    alt_ratio,
977 	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
978 	    div_ant_conf.antdiv_configgroup,
979 	    &div_ant_conf);
980 
981 	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
982 
983 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
984 	   __func__, antcomb->total_pkt_count);
985 
986 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
987 	   __func__, antcomb->main_total_rssi);
988 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
989 	   __func__, antcomb->alt_total_rssi);
990 
991 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
992 	   __func__, main_rssi_avg);
993 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
994 	   __func__, alt_rssi_avg);
995 
996 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
997 	   __func__, antcomb->main_recv_cnt);
998 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
999 	   __func__, antcomb->alt_recv_cnt);
1000 
1001 //	if (curr_alt_set != div_ant_conf.alt_lna_conf)
1002 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1003 		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1004 //	if (curr_main_set != div_ant_conf.main_lna_conf)
1005 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1006 		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
1007 //	if (curr_bias != div_ant_conf.fast_div_bias)
1008 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1009 		    __func__, curr_bias, div_ant_conf.fast_div_bias);
1010 
1011 	antcomb->scan_start_time = ticks;
1012 	antcomb->total_pkt_count = 0;
1013 	antcomb->main_total_rssi = 0;
1014 	antcomb->alt_total_rssi = 0;
1015 	antcomb->main_recv_cnt = 0;
1016 	antcomb->alt_recv_cnt = 0;
1017 }
1018 
1019