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