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