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