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