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