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