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