xref: /freebsd/sys/dev/ath/if_ath_lna_div.c (revision 8d20be1e22095c27faf8fe8b2f0d089739cc742e)
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 compability 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 static HAL_BOOL
213 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
214     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
215 {
216 	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
217 		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
218 		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
219 }
220 
221 static void
222 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
223     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
224 {
225 	antcomb->quick_scan_cnt = 0;
226 
227 	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
228 		antcomb->rssi_lna2 = main_rssi_avg;
229 	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
230 		antcomb->rssi_lna1 = main_rssi_avg;
231 
232 	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
233 	case (0x10): /* LNA2 A-B */
234 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
235 		antcomb->first_quick_scan_conf =
236 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
237 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
238 		break;
239 	case (0x20): /* LNA1 A-B */
240 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
241 		antcomb->first_quick_scan_conf =
242 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
243 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
244 		break;
245 	case (0x21): /* LNA1 LNA2 */
246 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
247 		antcomb->first_quick_scan_conf =
248 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
249 		antcomb->second_quick_scan_conf =
250 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
251 		break;
252 	case (0x12): /* LNA2 LNA1 */
253 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
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 (0x13): /* LNA2 A+B */
260 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
261 		antcomb->first_quick_scan_conf =
262 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
263 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
264 		break;
265 	case (0x23): /* LNA1 A+B */
266 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
267 		antcomb->first_quick_scan_conf =
268 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
269 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
270 		break;
271 	default:
272 		break;
273 	}
274 }
275 
276 static void
277 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
278     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
279     int alt_rssi_avg, int alt_ratio)
280 {
281 	/* alt_good */
282 	switch (antcomb->quick_scan_cnt) {
283 	case 0:
284 		/* set alt to main, and alt to first conf */
285 		div_ant_conf->main_lna_conf = antcomb->main_conf;
286 		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
287 		break;
288 	case 1:
289 		/* set alt to main, and alt to first conf */
290 		div_ant_conf->main_lna_conf = antcomb->main_conf;
291 		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
292 		antcomb->rssi_first = main_rssi_avg;
293 		antcomb->rssi_second = alt_rssi_avg;
294 
295 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
296 			/* main is LNA1 */
297 			if (ath_is_alt_ant_ratio_better(alt_ratio,
298 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
299 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
300 						main_rssi_avg, alt_rssi_avg,
301 						antcomb->total_pkt_count))
302 				antcomb->first_ratio = AH_TRUE;
303 			else
304 				antcomb->first_ratio = AH_FALSE;
305 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
306 			if (ath_is_alt_ant_ratio_better(alt_ratio,
307 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
308 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
309 						main_rssi_avg, alt_rssi_avg,
310 						antcomb->total_pkt_count))
311 				antcomb->first_ratio = AH_TRUE;
312 			else
313 				antcomb->first_ratio = AH_FALSE;
314 		} else {
315 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
316 			    (alt_rssi_avg > main_rssi_avg +
317 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
318 			    (alt_rssi_avg > main_rssi_avg)) &&
319 			    (antcomb->total_pkt_count > 50))
320 				antcomb->first_ratio = AH_TRUE;
321 			else
322 				antcomb->first_ratio = AH_FALSE;
323 		}
324 		break;
325 	case 2:
326 		antcomb->alt_good = AH_FALSE;
327 		antcomb->scan_not_start = AH_FALSE;
328 		antcomb->scan = AH_FALSE;
329 		antcomb->rssi_first = main_rssi_avg;
330 		antcomb->rssi_third = alt_rssi_avg;
331 
332 		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
333 			antcomb->rssi_lna1 = alt_rssi_avg;
334 		else if (antcomb->second_quick_scan_conf ==
335 			 HAL_ANT_DIV_COMB_LNA2)
336 			antcomb->rssi_lna2 = alt_rssi_avg;
337 		else if (antcomb->second_quick_scan_conf ==
338 			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
339 			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
340 				antcomb->rssi_lna2 = main_rssi_avg;
341 			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
342 				antcomb->rssi_lna1 = main_rssi_avg;
343 		}
344 
345 		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
346 		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
347 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
348 		else
349 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
350 
351 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
352 			if (ath_is_alt_ant_ratio_better(alt_ratio,
353 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
354 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
355 						main_rssi_avg, alt_rssi_avg,
356 						antcomb->total_pkt_count))
357 				antcomb->second_ratio = AH_TRUE;
358 			else
359 				antcomb->second_ratio = AH_FALSE;
360 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
361 			if (ath_is_alt_ant_ratio_better(alt_ratio,
362 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
363 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
364 						main_rssi_avg, alt_rssi_avg,
365 						antcomb->total_pkt_count))
366 				antcomb->second_ratio = AH_TRUE;
367 			else
368 				antcomb->second_ratio = AH_FALSE;
369 		} else {
370 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
371 			    (alt_rssi_avg > main_rssi_avg +
372 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
373 			    (alt_rssi_avg > main_rssi_avg)) &&
374 			    (antcomb->total_pkt_count > 50))
375 				antcomb->second_ratio = AH_TRUE;
376 			else
377 				antcomb->second_ratio = AH_FALSE;
378 		}
379 
380 		/* set alt to the conf with maximun ratio */
381 		if (antcomb->first_ratio && antcomb->second_ratio) {
382 			if (antcomb->rssi_second > antcomb->rssi_third) {
383 				/* first alt*/
384 				if ((antcomb->first_quick_scan_conf ==
385 				    HAL_ANT_DIV_COMB_LNA1) ||
386 				    (antcomb->first_quick_scan_conf ==
387 				    HAL_ANT_DIV_COMB_LNA2))
388 					/* Set alt LNA1 or LNA2*/
389 					if (div_ant_conf->main_lna_conf ==
390 					    HAL_ANT_DIV_COMB_LNA2)
391 						div_ant_conf->alt_lna_conf =
392 							HAL_ANT_DIV_COMB_LNA1;
393 					else
394 						div_ant_conf->alt_lna_conf =
395 							HAL_ANT_DIV_COMB_LNA2;
396 				else
397 					/* Set alt to A+B or A-B */
398 					div_ant_conf->alt_lna_conf =
399 						antcomb->first_quick_scan_conf;
400 			} else if ((antcomb->second_quick_scan_conf ==
401 				   HAL_ANT_DIV_COMB_LNA1) ||
402 				   (antcomb->second_quick_scan_conf ==
403 				   HAL_ANT_DIV_COMB_LNA2)) {
404 				/* Set alt LNA1 or LNA2 */
405 				if (div_ant_conf->main_lna_conf ==
406 				    HAL_ANT_DIV_COMB_LNA2)
407 					div_ant_conf->alt_lna_conf =
408 						HAL_ANT_DIV_COMB_LNA1;
409 				else
410 					div_ant_conf->alt_lna_conf =
411 						HAL_ANT_DIV_COMB_LNA2;
412 			} else {
413 				/* Set alt to A+B or A-B */
414 				div_ant_conf->alt_lna_conf =
415 					antcomb->second_quick_scan_conf;
416 			}
417 		} else if (antcomb->first_ratio) {
418 			/* first alt */
419 			if ((antcomb->first_quick_scan_conf ==
420 			    HAL_ANT_DIV_COMB_LNA1) ||
421 			    (antcomb->first_quick_scan_conf ==
422 			    HAL_ANT_DIV_COMB_LNA2))
423 					/* Set alt LNA1 or LNA2 */
424 				if (div_ant_conf->main_lna_conf ==
425 				    HAL_ANT_DIV_COMB_LNA2)
426 					div_ant_conf->alt_lna_conf =
427 							HAL_ANT_DIV_COMB_LNA1;
428 				else
429 					div_ant_conf->alt_lna_conf =
430 							HAL_ANT_DIV_COMB_LNA2;
431 			else
432 				/* Set alt to A+B or A-B */
433 				div_ant_conf->alt_lna_conf =
434 						antcomb->first_quick_scan_conf;
435 		} else if (antcomb->second_ratio) {
436 				/* second alt */
437 			if ((antcomb->second_quick_scan_conf ==
438 			    HAL_ANT_DIV_COMB_LNA1) ||
439 			    (antcomb->second_quick_scan_conf ==
440 			    HAL_ANT_DIV_COMB_LNA2))
441 				/* Set alt LNA1 or LNA2 */
442 				if (div_ant_conf->main_lna_conf ==
443 				    HAL_ANT_DIV_COMB_LNA2)
444 					div_ant_conf->alt_lna_conf =
445 						HAL_ANT_DIV_COMB_LNA1;
446 				else
447 					div_ant_conf->alt_lna_conf =
448 						HAL_ANT_DIV_COMB_LNA2;
449 			else
450 				/* Set alt to A+B or A-B */
451 				div_ant_conf->alt_lna_conf =
452 						antcomb->second_quick_scan_conf;
453 		} else {
454 			/* main is largest */
455 			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
456 			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
457 				/* Set alt LNA1 or LNA2 */
458 				if (div_ant_conf->main_lna_conf ==
459 				    HAL_ANT_DIV_COMB_LNA2)
460 					div_ant_conf->alt_lna_conf =
461 							HAL_ANT_DIV_COMB_LNA1;
462 				else
463 					div_ant_conf->alt_lna_conf =
464 							HAL_ANT_DIV_COMB_LNA2;
465 			else
466 				/* Set alt to A+B or A-B */
467 				div_ant_conf->alt_lna_conf = antcomb->main_conf;
468 		}
469 		break;
470 	default:
471 		break;
472 	}
473 }
474 
475 static void
476 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
477     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
478     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
479 {
480 
481 	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
482 		switch ((pdiv_ant_conf->main_lna_conf << 4)
483 		    | pdiv_ant_conf->alt_lna_conf) {
484 		case (0x01): //A-B LNA2
485 			pdiv_ant_conf->fast_div_bias = 0x1;
486 			pdiv_ant_conf->main_gaintb   = 0;
487 			pdiv_ant_conf->alt_gaintb    = 0;
488 			break;
489 		case (0x02): //A-B LNA1
490 			pdiv_ant_conf->fast_div_bias = 0x1;
491 			pdiv_ant_conf->main_gaintb   = 0;
492 			pdiv_ant_conf->alt_gaintb    = 0;
493 			break;
494 		case (0x03): //A-B A+B
495 			pdiv_ant_conf->fast_div_bias = 0x1;
496 			pdiv_ant_conf->main_gaintb   = 0;
497 			pdiv_ant_conf->alt_gaintb    = 0;
498 			break;
499 		case (0x10): //LNA2 A-B
500 			if ((antcomb->scan == 0)
501 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
502 				pdiv_ant_conf->fast_div_bias = 0x3f;
503 			} else {
504 				pdiv_ant_conf->fast_div_bias = 0x1;
505 			}
506 			pdiv_ant_conf->main_gaintb   = 0;
507 			pdiv_ant_conf->alt_gaintb    = 0;
508 			break;
509 		case (0x12): //LNA2 LNA1
510 			pdiv_ant_conf->fast_div_bias = 0x1;
511 			pdiv_ant_conf->main_gaintb   = 0;
512 			pdiv_ant_conf->alt_gaintb    = 0;
513 			break;
514 			case (0x13): //LNA2 A+B
515 			if ((antcomb->scan == 0)
516 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
517 				pdiv_ant_conf->fast_div_bias = 0x3f;
518 			} else {
519 				pdiv_ant_conf->fast_div_bias = 0x1;
520 			}
521 			pdiv_ant_conf->main_gaintb   = 0;
522 			pdiv_ant_conf->alt_gaintb    = 0;
523 			break;
524 		case (0x20): //LNA1 A-B
525 			if ((antcomb->scan == 0)
526 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
527 				pdiv_ant_conf->fast_div_bias = 0x3f;
528 			} else {
529 				pdiv_ant_conf->fast_div_bias = 0x1;
530 			}
531 			pdiv_ant_conf->main_gaintb   = 0;
532 			pdiv_ant_conf->alt_gaintb    = 0;
533 			break;
534 		case (0x21): //LNA1 LNA2
535 			pdiv_ant_conf->fast_div_bias = 0x1;
536 			pdiv_ant_conf->main_gaintb   = 0;
537 			pdiv_ant_conf->alt_gaintb    = 0;
538 			break;
539 		case (0x23): //LNA1 A+B
540 			if ((antcomb->scan == 0)
541 			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
542 				pdiv_ant_conf->fast_div_bias = 0x3f;
543 			} else {
544 				pdiv_ant_conf->fast_div_bias = 0x1;
545 			}
546 			pdiv_ant_conf->main_gaintb   = 0;
547 			pdiv_ant_conf->alt_gaintb    = 0;
548 			break;
549 		case (0x30): //A+B A-B
550 			pdiv_ant_conf->fast_div_bias = 0x1;
551 			pdiv_ant_conf->main_gaintb   = 0;
552 			pdiv_ant_conf->alt_gaintb    = 0;
553 			break;
554 		case (0x31): //A+B LNA2
555 			pdiv_ant_conf->fast_div_bias = 0x1;
556 			pdiv_ant_conf->main_gaintb   = 0;
557 			pdiv_ant_conf->alt_gaintb    = 0;
558 			break;
559 		case (0x32): //A+B LNA1
560 			pdiv_ant_conf->fast_div_bias = 0x1;
561 			pdiv_ant_conf->main_gaintb   = 0;
562 			pdiv_ant_conf->alt_gaintb    = 0;
563 			break;
564 		default:
565 			break;
566 		}
567 	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
568 		switch ((pdiv_ant_conf->main_lna_conf << 4)
569 		    | pdiv_ant_conf->alt_lna_conf) {
570 		case (0x01): //A-B LNA2
571 			pdiv_ant_conf->fast_div_bias = 0x1;
572 			pdiv_ant_conf->main_gaintb   = 0;
573 			pdiv_ant_conf->alt_gaintb    = 0;
574 			break;
575 		case (0x02): //A-B LNA1
576 			pdiv_ant_conf->fast_div_bias = 0x1;
577 			pdiv_ant_conf->main_gaintb   = 0;
578 			pdiv_ant_conf->alt_gaintb    = 0;
579 			break;
580 		case (0x03): //A-B A+B
581 			pdiv_ant_conf->fast_div_bias = 0x1;
582 			pdiv_ant_conf->main_gaintb   = 0;
583 			pdiv_ant_conf->alt_gaintb    = 0;
584 			break;
585 		case (0x10): //LNA2 A-B
586 			if ((antcomb->scan == 0)
587 			    && (alt_ratio > alt_ant_ratio_th)) {
588 				pdiv_ant_conf->fast_div_bias = 0x1;
589 			} else {
590 				pdiv_ant_conf->fast_div_bias = 0x2;
591 			}
592 			pdiv_ant_conf->main_gaintb   = 0;
593 			pdiv_ant_conf->alt_gaintb    = 0;
594 			break;
595 		case (0x12): //LNA2 LNA1
596 			pdiv_ant_conf->fast_div_bias = 0x1;
597 			pdiv_ant_conf->main_gaintb   = 0;
598 			pdiv_ant_conf->alt_gaintb    = 0;
599 			break;
600 		case (0x13): //LNA2 A+B
601 			if ((antcomb->scan == 0)
602 			    && (alt_ratio > alt_ant_ratio_th)) {
603 				pdiv_ant_conf->fast_div_bias = 0x1;
604 			} else {
605 				pdiv_ant_conf->fast_div_bias = 0x2;
606 			}
607 			pdiv_ant_conf->main_gaintb   = 0;
608 			pdiv_ant_conf->alt_gaintb    = 0;
609 			break;
610 		case (0x20): //LNA1 A-B
611 			if ((antcomb->scan == 0)
612 			    && (alt_ratio > alt_ant_ratio_th)) {
613 				pdiv_ant_conf->fast_div_bias = 0x1;
614 			} else {
615 				pdiv_ant_conf->fast_div_bias = 0x2;
616 			}
617 			pdiv_ant_conf->main_gaintb   = 0;
618 			pdiv_ant_conf->alt_gaintb    = 0;
619 			break;
620 		case (0x21): //LNA1 LNA2
621 			pdiv_ant_conf->fast_div_bias = 0x1;
622 			pdiv_ant_conf->main_gaintb   = 0;
623 			pdiv_ant_conf->alt_gaintb    = 0;
624 			break;
625 		case (0x23): //LNA1 A+B
626 			if ((antcomb->scan == 0)
627 			    && (alt_ratio > alt_ant_ratio_th)) {
628 				pdiv_ant_conf->fast_div_bias = 0x1;
629 			} else {
630 				pdiv_ant_conf->fast_div_bias = 0x2;
631 			}
632 			pdiv_ant_conf->main_gaintb   = 0;
633 			pdiv_ant_conf->alt_gaintb    = 0;
634 			break;
635 		case (0x30): //A+B A-B
636 			pdiv_ant_conf->fast_div_bias = 0x1;
637 			pdiv_ant_conf->main_gaintb   = 0;
638 			pdiv_ant_conf->alt_gaintb    = 0;
639 			break;
640 		case (0x31): //A+B LNA2
641 			pdiv_ant_conf->fast_div_bias = 0x1;
642 			pdiv_ant_conf->main_gaintb   = 0;
643 			pdiv_ant_conf->alt_gaintb    = 0;
644 			break;
645 		case (0x32): //A+B LNA1
646 			pdiv_ant_conf->fast_div_bias = 0x1;
647 			pdiv_ant_conf->main_gaintb   = 0;
648 			pdiv_ant_conf->alt_gaintb    = 0;
649 			break;
650 		default:
651 			break;
652 		}
653 	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
654 		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
655 		case (0x01): //A-B LNA2
656 			pdiv_ant_conf->fast_div_bias = 0x3b;
657 			break;
658 		case (0x02): //A-B LNA1
659 			pdiv_ant_conf->fast_div_bias = 0x3d;
660 			break;
661 		case (0x03): //A-B A+B
662 			pdiv_ant_conf->fast_div_bias = 0x1;
663 			break;
664 		case (0x10): //LNA2 A-B
665 			pdiv_ant_conf->fast_div_bias = 0x7;
666 			break;
667 		case (0x12): //LNA2 LNA1
668 			pdiv_ant_conf->fast_div_bias = 0x2;
669 			break;
670 		case (0x13): //LNA2 A+B
671 			pdiv_ant_conf->fast_div_bias = 0x7;
672 			break;
673 		case (0x20): //LNA1 A-B
674 			pdiv_ant_conf->fast_div_bias = 0x6;
675 			break;
676 		case (0x21): //LNA1 LNA2
677 			pdiv_ant_conf->fast_div_bias = 0x0;
678 			break;
679 		case (0x23): //LNA1 A+B
680 			pdiv_ant_conf->fast_div_bias = 0x6;
681 			break;
682 		case (0x30): //A+B A-B
683 			pdiv_ant_conf->fast_div_bias = 0x1;
684 			break;
685 		case (0x31): //A+B LNA2
686 			pdiv_ant_conf->fast_div_bias = 0x3b;
687 			break;
688 		case (0x32): //A+B LNA1
689 			pdiv_ant_conf->fast_div_bias = 0x3d;
690 			break;
691 		default:
692 			break;
693 		}
694 	}
695 }
696 
697 /*
698  * AR9485/AR933x TODO:
699  * + Select a ratio based on whether RSSI is low or not; but I need
700  *   to figure out what "low_rssi_th" is sourced from.
701  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
702  * + .. and there's likely a bunch of other things to include in this.
703  */
704 
705 /* Antenna diversity and combining */
706 void
707 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
708     unsigned long ticks, int hz)
709 {
710 	HAL_ANT_COMB_CONFIG div_ant_conf;
711 	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
712 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
713 	int curr_main_set, curr_bias;
714 	int main_rssi = rs->rs_rssi_ctl[0];
715 	int alt_rssi = rs->rs_rssi_ctl[1];
716 	int rx_ant_conf, main_ant_conf, alt_ant_conf;
717 	HAL_BOOL short_scan = AH_FALSE;
718 
719 	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
720 	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
721 	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
722 
723 #if 0
724 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
725 	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
726 	    "FastDiv: %d\n",
727 	    __func__,
728 	    main_rssi,
729 	    alt_rssi,
730 	    main_ant_conf,
731 	    alt_ant_conf,
732 	    rx_ant_conf,
733 	    !!(rs->rs_rssi_ctl[2] & 0x80),
734 	    !!(rs->rs_rssi_ctl[2] & 0x40),
735 	    !!(rs->rs_rssi_ext[2] & 0x40));
736 #endif
737 
738 	/*
739 	 * If LNA diversity combining isn't enabled, don't run this.
740 	 */
741 	if (! sc->sc_dolnadiv)
742 		return;
743 
744 	/*
745 	 * XXX this is ugly, but the HAL code attaches the
746 	 * LNA diversity to the TX antenna settings.
747 	 * I don't know why.
748 	 */
749 	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
750 		return;
751 
752 	/* Record packet only when alt_rssi is positive */
753 	if (main_rssi > 0 && alt_rssi > 0) {
754 		antcomb->total_pkt_count++;
755 		antcomb->main_total_rssi += main_rssi;
756 		antcomb->alt_total_rssi  += alt_rssi;
757 		if (main_ant_conf == rx_ant_conf)
758 			antcomb->main_recv_cnt++;
759 		else
760 			antcomb->alt_recv_cnt++;
761 	}
762 
763 	/* Short scan check */
764 	if (antcomb->scan && antcomb->alt_good) {
765 		if (time_after(ticks, antcomb->scan_start_time +
766 		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
767 			short_scan = AH_TRUE;
768 		else
769 			if (antcomb->total_pkt_count ==
770 			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
771 				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
772 					    antcomb->total_pkt_count);
773 				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
774 					short_scan = AH_TRUE;
775 			}
776 	}
777 
778 #if 0
779 	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
780 	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
781 	    __func__,
782 	    antcomb->total_pkt_count,
783 	    !! (rs->rs_moreaggr),
784 	    !! (short_scan));
785 #endif
786 
787 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
788 	    rs->rs_moreaggr) && !short_scan)
789 		return;
790 
791 	if (antcomb->total_pkt_count) {
792 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
793 			     antcomb->total_pkt_count);
794 		main_rssi_avg = (antcomb->main_total_rssi /
795 				 antcomb->total_pkt_count);
796 		alt_rssi_avg = (antcomb->alt_total_rssi /
797 				 antcomb->total_pkt_count);
798 	}
799 
800 	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
801 
802 	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
803 	curr_alt_set = div_ant_conf.alt_lna_conf;
804 	curr_main_set = div_ant_conf.main_lna_conf;
805 	curr_bias = div_ant_conf.fast_div_bias;
806 
807 	antcomb->count++;
808 
809 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
810 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
811 			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
812 						  main_rssi_avg);
813 			antcomb->alt_good = AH_TRUE;
814 		} else {
815 			antcomb->alt_good = AH_FALSE;
816 		}
817 
818 		antcomb->count = 0;
819 		antcomb->scan = AH_TRUE;
820 		antcomb->scan_not_start = AH_TRUE;
821 	}
822 
823 	if (!antcomb->scan) {
824 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
825 			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
826 				/* Switch main and alt LNA */
827 				div_ant_conf.main_lna_conf =
828 						HAL_ANT_DIV_COMB_LNA2;
829 				div_ant_conf.alt_lna_conf  =
830 						HAL_ANT_DIV_COMB_LNA1;
831 			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
832 				div_ant_conf.main_lna_conf =
833 						HAL_ANT_DIV_COMB_LNA1;
834 				div_ant_conf.alt_lna_conf  =
835 						HAL_ANT_DIV_COMB_LNA2;
836 			}
837 
838 			goto div_comb_done;
839 		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
840 			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
841 			/* Set alt to another LNA */
842 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
843 				div_ant_conf.alt_lna_conf =
844 						HAL_ANT_DIV_COMB_LNA1;
845 			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
846 				div_ant_conf.alt_lna_conf =
847 						HAL_ANT_DIV_COMB_LNA2;
848 
849 			goto div_comb_done;
850 		}
851 
852 		if ((alt_rssi_avg < (main_rssi_avg +
853 		    antcomb->lna1_lna2_delta)))
854 			goto div_comb_done;
855 	}
856 
857 	if (!antcomb->scan_not_start) {
858 		switch (curr_alt_set) {
859 		case HAL_ANT_DIV_COMB_LNA2:
860 			antcomb->rssi_lna2 = alt_rssi_avg;
861 			antcomb->rssi_lna1 = main_rssi_avg;
862 			antcomb->scan = AH_TRUE;
863 			/* set to A+B */
864 			div_ant_conf.main_lna_conf =
865 				HAL_ANT_DIV_COMB_LNA1;
866 			div_ant_conf.alt_lna_conf  =
867 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
868 			break;
869 		case HAL_ANT_DIV_COMB_LNA1:
870 			antcomb->rssi_lna1 = alt_rssi_avg;
871 			antcomb->rssi_lna2 = main_rssi_avg;
872 			antcomb->scan = AH_TRUE;
873 			/* set to A+B */
874 			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
875 			div_ant_conf.alt_lna_conf  =
876 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
877 			break;
878 		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
879 			antcomb->rssi_add = alt_rssi_avg;
880 			antcomb->scan = AH_TRUE;
881 			/* set to A-B */
882 			div_ant_conf.alt_lna_conf =
883 				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
884 			break;
885 		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
886 			antcomb->rssi_sub = alt_rssi_avg;
887 			antcomb->scan = AH_FALSE;
888 			if (antcomb->rssi_lna2 >
889 			    (antcomb->rssi_lna1 +
890 			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
891 				/* use LNA2 as main LNA */
892 				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
893 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
894 					/* set to A+B */
895 					div_ant_conf.main_lna_conf =
896 						HAL_ANT_DIV_COMB_LNA2;
897 					div_ant_conf.alt_lna_conf  =
898 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
899 				} else if (antcomb->rssi_sub >
900 					   antcomb->rssi_lna1) {
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_MINUS_LNA2;
906 				} else {
907 					/* set to LNA1 */
908 					div_ant_conf.main_lna_conf =
909 						HAL_ANT_DIV_COMB_LNA2;
910 					div_ant_conf.alt_lna_conf =
911 						HAL_ANT_DIV_COMB_LNA1;
912 				}
913 			} else {
914 				/* use LNA1 as main LNA */
915 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
916 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
917 					/* set to A+B */
918 					div_ant_conf.main_lna_conf =
919 						HAL_ANT_DIV_COMB_LNA1;
920 					div_ant_conf.alt_lna_conf  =
921 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
922 				} else if (antcomb->rssi_sub >
923 					   antcomb->rssi_lna1) {
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_MINUS_LNA2;
929 				} else {
930 					/* set to LNA2 */
931 					div_ant_conf.main_lna_conf =
932 						HAL_ANT_DIV_COMB_LNA1;
933 					div_ant_conf.alt_lna_conf =
934 						HAL_ANT_DIV_COMB_LNA2;
935 				}
936 			}
937 			break;
938 		default:
939 			break;
940 		}
941 	} else {
942 		if (!antcomb->alt_good) {
943 			antcomb->scan_not_start = AH_FALSE;
944 			/* Set alt to another LNA */
945 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
946 				div_ant_conf.main_lna_conf =
947 						HAL_ANT_DIV_COMB_LNA2;
948 				div_ant_conf.alt_lna_conf =
949 						HAL_ANT_DIV_COMB_LNA1;
950 			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
951 				div_ant_conf.main_lna_conf =
952 						HAL_ANT_DIV_COMB_LNA1;
953 				div_ant_conf.alt_lna_conf =
954 						HAL_ANT_DIV_COMB_LNA2;
955 			}
956 			goto div_comb_done;
957 		}
958 	}
959 
960 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
961 					   main_rssi_avg, alt_rssi_avg,
962 					   alt_ratio);
963 
964 	antcomb->quick_scan_cnt++;
965 
966 div_comb_done:
967 #if 0
968 	ath_ant_div_conf_fast_divbias(&div_ant_conf);
969 #endif
970 
971 	ath_ant_adjust_fast_divbias(antcomb,
972 	    alt_ratio,
973 	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
974 	    div_ant_conf.antdiv_configgroup,
975 	    &div_ant_conf);
976 
977 	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
978 
979 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
980 	   __func__, antcomb->total_pkt_count);
981 
982 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
983 	   __func__, antcomb->main_total_rssi);
984 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
985 	   __func__, antcomb->alt_total_rssi);
986 
987 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
988 	   __func__, main_rssi_avg);
989 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
990 	   __func__, alt_rssi_avg);
991 
992 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
993 	   __func__, antcomb->main_recv_cnt);
994 	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
995 	   __func__, antcomb->alt_recv_cnt);
996 
997 //	if (curr_alt_set != div_ant_conf.alt_lna_conf)
998 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
999 		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1000 //	if (curr_main_set != div_ant_conf.main_lna_conf)
1001 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1002 		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
1003 //	if (curr_bias != div_ant_conf.fast_div_bias)
1004 		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1005 		    __func__, curr_bias, div_ant_conf.fast_div_bias);
1006 
1007 	antcomb->scan_start_time = ticks;
1008 	antcomb->total_pkt_count = 0;
1009 	antcomb->main_total_rssi = 0;
1010 	antcomb->alt_total_rssi = 0;
1011 	antcomb->main_recv_cnt = 0;
1012 	antcomb->alt_recv_cnt = 0;
1013 }
1014 
1015