1 /*
2  * Copyright (c) 2008-2010 Atheros Communications Inc.
3  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
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  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 #include "opt_ah.h"
29 
30 #include "ah.h"
31 #include "ah_desc.h"
32 #include "ah_internal.h"
33 #include "ah_eeprom_v4k.h"
34 
35 #include "ar9002/ar9280.h"
36 #include "ar9002/ar9285_diversity.h"
37 #include "ar9002/ar9285.h"
38 #include "ar5416/ar5416reg.h"
39 #include "ar5416/ar5416phy.h"
40 #include "ar9002/ar9285phy.h"
41 #include "ar9002/ar9285_phy.h"
42 
43 
44 /* Linux compability macros */
45 /*
46  * XXX these don't handle rounding, underflow, overflow, wrapping!
47  */
48 #define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
49 #define	time_after(a, b)		( (long) (b) - (long) (a) < 0 )
50 
51 static HAL_BOOL
52 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
53     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
54 {
55 	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
56 		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
57 		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
58 }
59 
60 static void
61 ath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb,
62     struct ar9285_antcomb_conf ant_conf, int main_rssi_avg)
63 {
64 	antcomb->quick_scan_cnt = 0;
65 
66 	if (ant_conf.main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
67 		antcomb->rssi_lna2 = main_rssi_avg;
68 	else if (ant_conf.main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
69 		antcomb->rssi_lna1 = main_rssi_avg;
70 
71 	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
72 	case (0x10): /* LNA2 A-B */
73 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
74 		antcomb->first_quick_scan_conf =
75 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
76 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
77 		break;
78 	case (0x20): /* LNA1 A-B */
79 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
80 		antcomb->first_quick_scan_conf =
81 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
82 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
83 		break;
84 	case (0x21): /* LNA1 LNA2 */
85 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
86 		antcomb->first_quick_scan_conf =
87 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
88 		antcomb->second_quick_scan_conf =
89 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
90 		break;
91 	case (0x12): /* LNA2 LNA1 */
92 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
93 		antcomb->first_quick_scan_conf =
94 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
95 		antcomb->second_quick_scan_conf =
96 			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
97 		break;
98 	case (0x13): /* LNA2 A+B */
99 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
100 		antcomb->first_quick_scan_conf =
101 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
102 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
103 		break;
104 	case (0x23): /* LNA1 A+B */
105 		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
106 		antcomb->first_quick_scan_conf =
107 			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
108 		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
109 		break;
110 	default:
111 		break;
112 	}
113 }
114 
115 static void
116 ath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb,
117     struct ar9285_antcomb_conf *div_ant_conf, int main_rssi_avg,
118     int alt_rssi_avg, int alt_ratio)
119 {
120 	/* alt_good */
121 	switch (antcomb->quick_scan_cnt) {
122 	case 0:
123 		/* set alt to main, and alt to first conf */
124 		div_ant_conf->main_lna_conf = antcomb->main_conf;
125 		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
126 		break;
127 	case 1:
128 		/* set alt to main, and alt to first conf */
129 		div_ant_conf->main_lna_conf = antcomb->main_conf;
130 		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
131 		antcomb->rssi_first = main_rssi_avg;
132 		antcomb->rssi_second = alt_rssi_avg;
133 
134 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
135 			/* main is LNA1 */
136 			if (ath_is_alt_ant_ratio_better(alt_ratio,
137 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
138 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
139 						main_rssi_avg, alt_rssi_avg,
140 						antcomb->total_pkt_count))
141 				antcomb->first_ratio = AH_TRUE;
142 			else
143 				antcomb->first_ratio = AH_FALSE;
144 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
145 			if (ath_is_alt_ant_ratio_better(alt_ratio,
146 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
147 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
148 						main_rssi_avg, alt_rssi_avg,
149 						antcomb->total_pkt_count))
150 				antcomb->first_ratio = AH_TRUE;
151 			else
152 				antcomb->first_ratio = AH_FALSE;
153 		} else {
154 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
155 			    (alt_rssi_avg > main_rssi_avg +
156 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
157 			    (alt_rssi_avg > main_rssi_avg)) &&
158 			    (antcomb->total_pkt_count > 50))
159 				antcomb->first_ratio = AH_TRUE;
160 			else
161 				antcomb->first_ratio = AH_FALSE;
162 		}
163 		break;
164 	case 2:
165 		antcomb->alt_good = AH_FALSE;
166 		antcomb->scan_not_start = AH_FALSE;
167 		antcomb->scan = AH_FALSE;
168 		antcomb->rssi_first = main_rssi_avg;
169 		antcomb->rssi_third = alt_rssi_avg;
170 
171 		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
172 			antcomb->rssi_lna1 = alt_rssi_avg;
173 		else if (antcomb->second_quick_scan_conf ==
174 			 HAL_ANT_DIV_COMB_LNA2)
175 			antcomb->rssi_lna2 = alt_rssi_avg;
176 		else if (antcomb->second_quick_scan_conf ==
177 			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
178 			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
179 				antcomb->rssi_lna2 = main_rssi_avg;
180 			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
181 				antcomb->rssi_lna1 = main_rssi_avg;
182 		}
183 
184 		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
185 		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
186 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
187 		else
188 			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
189 
190 		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
191 			if (ath_is_alt_ant_ratio_better(alt_ratio,
192 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
193 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
194 						main_rssi_avg, alt_rssi_avg,
195 						antcomb->total_pkt_count))
196 				antcomb->second_ratio = AH_TRUE;
197 			else
198 				antcomb->second_ratio = AH_FALSE;
199 		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
200 			if (ath_is_alt_ant_ratio_better(alt_ratio,
201 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
202 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
203 						main_rssi_avg, alt_rssi_avg,
204 						antcomb->total_pkt_count))
205 				antcomb->second_ratio = AH_TRUE;
206 			else
207 				antcomb->second_ratio = AH_FALSE;
208 		} else {
209 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
210 			    (alt_rssi_avg > main_rssi_avg +
211 			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
212 			    (alt_rssi_avg > main_rssi_avg)) &&
213 			    (antcomb->total_pkt_count > 50))
214 				antcomb->second_ratio = AH_TRUE;
215 			else
216 				antcomb->second_ratio = AH_FALSE;
217 		}
218 
219 		/* set alt to the conf with maximun ratio */
220 		if (antcomb->first_ratio && antcomb->second_ratio) {
221 			if (antcomb->rssi_second > antcomb->rssi_third) {
222 				/* first alt*/
223 				if ((antcomb->first_quick_scan_conf ==
224 				    HAL_ANT_DIV_COMB_LNA1) ||
225 				    (antcomb->first_quick_scan_conf ==
226 				    HAL_ANT_DIV_COMB_LNA2))
227 					/* Set alt LNA1 or LNA2*/
228 					if (div_ant_conf->main_lna_conf ==
229 					    HAL_ANT_DIV_COMB_LNA2)
230 						div_ant_conf->alt_lna_conf =
231 							HAL_ANT_DIV_COMB_LNA1;
232 					else
233 						div_ant_conf->alt_lna_conf =
234 							HAL_ANT_DIV_COMB_LNA2;
235 				else
236 					/* Set alt to A+B or A-B */
237 					div_ant_conf->alt_lna_conf =
238 						antcomb->first_quick_scan_conf;
239 			} else if ((antcomb->second_quick_scan_conf ==
240 				   HAL_ANT_DIV_COMB_LNA1) ||
241 				   (antcomb->second_quick_scan_conf ==
242 				   HAL_ANT_DIV_COMB_LNA2)) {
243 				/* Set alt LNA1 or LNA2 */
244 				if (div_ant_conf->main_lna_conf ==
245 				    HAL_ANT_DIV_COMB_LNA2)
246 					div_ant_conf->alt_lna_conf =
247 						HAL_ANT_DIV_COMB_LNA1;
248 				else
249 					div_ant_conf->alt_lna_conf =
250 						HAL_ANT_DIV_COMB_LNA2;
251 			} else {
252 				/* Set alt to A+B or A-B */
253 				div_ant_conf->alt_lna_conf =
254 					antcomb->second_quick_scan_conf;
255 			}
256 		} else if (antcomb->first_ratio) {
257 			/* first alt */
258 			if ((antcomb->first_quick_scan_conf ==
259 			    HAL_ANT_DIV_COMB_LNA1) ||
260 			    (antcomb->first_quick_scan_conf ==
261 			    HAL_ANT_DIV_COMB_LNA2))
262 					/* Set alt LNA1 or LNA2 */
263 				if (div_ant_conf->main_lna_conf ==
264 				    HAL_ANT_DIV_COMB_LNA2)
265 					div_ant_conf->alt_lna_conf =
266 							HAL_ANT_DIV_COMB_LNA1;
267 				else
268 					div_ant_conf->alt_lna_conf =
269 							HAL_ANT_DIV_COMB_LNA2;
270 			else
271 				/* Set alt to A+B or A-B */
272 				div_ant_conf->alt_lna_conf =
273 						antcomb->first_quick_scan_conf;
274 		} else if (antcomb->second_ratio) {
275 				/* second alt */
276 			if ((antcomb->second_quick_scan_conf ==
277 			    HAL_ANT_DIV_COMB_LNA1) ||
278 			    (antcomb->second_quick_scan_conf ==
279 			    HAL_ANT_DIV_COMB_LNA2))
280 				/* Set alt LNA1 or LNA2 */
281 				if (div_ant_conf->main_lna_conf ==
282 				    HAL_ANT_DIV_COMB_LNA2)
283 					div_ant_conf->alt_lna_conf =
284 						HAL_ANT_DIV_COMB_LNA1;
285 				else
286 					div_ant_conf->alt_lna_conf =
287 						HAL_ANT_DIV_COMB_LNA2;
288 			else
289 				/* Set alt to A+B or A-B */
290 				div_ant_conf->alt_lna_conf =
291 						antcomb->second_quick_scan_conf;
292 		} else {
293 			/* main is largest */
294 			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
295 			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
296 				/* Set alt LNA1 or LNA2 */
297 				if (div_ant_conf->main_lna_conf ==
298 				    HAL_ANT_DIV_COMB_LNA2)
299 					div_ant_conf->alt_lna_conf =
300 							HAL_ANT_DIV_COMB_LNA1;
301 				else
302 					div_ant_conf->alt_lna_conf =
303 							HAL_ANT_DIV_COMB_LNA2;
304 			else
305 				/* Set alt to A+B or A-B */
306 				div_ant_conf->alt_lna_conf = antcomb->main_conf;
307 		}
308 		break;
309 	default:
310 		break;
311 	}
312 }
313 
314 static void
315 ath_ant_div_conf_fast_divbias(struct ar9285_antcomb_conf *ant_conf)
316 {
317 	/* Adjust the fast_div_bias based on main and alt lna conf */
318 	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
319 	case (0x01): /* A-B LNA2 */
320 		ant_conf->fast_div_bias = 0x3b;
321 		break;
322 	case (0x02): /* A-B LNA1 */
323 		ant_conf->fast_div_bias = 0x3d;
324 		break;
325 	case (0x03): /* A-B A+B */
326 		ant_conf->fast_div_bias = 0x1;
327 		break;
328 	case (0x10): /* LNA2 A-B */
329 		ant_conf->fast_div_bias = 0x7;
330 		break;
331 	case (0x12): /* LNA2 LNA1 */
332 		ant_conf->fast_div_bias = 0x2;
333 		break;
334 	case (0x13): /* LNA2 A+B */
335 		ant_conf->fast_div_bias = 0x7;
336 		break;
337 	case (0x20): /* LNA1 A-B */
338 		ant_conf->fast_div_bias = 0x6;
339 		break;
340 	case (0x21): /* LNA1 LNA2 */
341 		ant_conf->fast_div_bias = 0x0;
342 		break;
343 	case (0x23): /* LNA1 A+B */
344 		ant_conf->fast_div_bias = 0x6;
345 		break;
346 	case (0x30): /* A+B A-B */
347 		ant_conf->fast_div_bias = 0x1;
348 		break;
349 	case (0x31): /* A+B LNA2 */
350 		ant_conf->fast_div_bias = 0x3b;
351 		break;
352 	case (0x32): /* A+B LNA1 */
353 		ant_conf->fast_div_bias = 0x3d;
354 		break;
355 	default:
356 		break;
357 	}
358 }
359 
360 /* Antenna diversity and combining */
361 void
362 ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
363     unsigned long ticks, int hz)
364 {
365 	struct ar9285_antcomb_conf div_ant_conf;
366 	struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb;
367 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
368 	int curr_main_set, curr_bias;
369 	int main_rssi = rs->rs_rssi_ctl[0];
370 	int alt_rssi = rs->rs_rssi_ctl[1];
371 	int rx_ant_conf, main_ant_conf, alt_ant_conf;
372 	HAL_BOOL short_scan = AH_FALSE;
373 
374 	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
375 	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
376 	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
377 
378 #if 0
379 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; FastDiv: %d\n",
380 	    __func__, main_rssi, alt_rssi, main_ant_conf,alt_ant_conf, rx_ant_conf,
381 	    !!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40));
382 #endif
383 
384 	if (! ar9285_check_div_comb(ah))
385 		return;
386 
387 	if (AH5212(ah)->ah_diversity == AH_FALSE)
388 		return;
389 
390 #if 0
391 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n",
392 	    __func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf);
393 #endif
394 
395 	/* Record packet only when alt_rssi is positive */
396 	if (main_rssi > 0 && alt_rssi > 0) {
397 		antcomb->total_pkt_count++;
398 		antcomb->main_total_rssi += main_rssi;
399 		antcomb->alt_total_rssi  += alt_rssi;
400 		if (main_ant_conf == rx_ant_conf)
401 			antcomb->main_recv_cnt++;
402 		else
403 			antcomb->alt_recv_cnt++;
404 	}
405 
406 	/* Short scan check */
407 	if (antcomb->scan && antcomb->alt_good) {
408 		if (time_after(ticks, antcomb->scan_start_time +
409 		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
410 			short_scan = AH_TRUE;
411 		else
412 			if (antcomb->total_pkt_count ==
413 			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
414 				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
415 					    antcomb->total_pkt_count);
416 				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
417 					short_scan = AH_TRUE;
418 			}
419 	}
420 
421 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
422 	    rs->rs_moreaggr) && !short_scan)
423 		return;
424 
425 	if (antcomb->total_pkt_count) {
426 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
427 			     antcomb->total_pkt_count);
428 		main_rssi_avg = (antcomb->main_total_rssi /
429 				 antcomb->total_pkt_count);
430 		alt_rssi_avg = (antcomb->alt_total_rssi /
431 				 antcomb->total_pkt_count);
432 	}
433 
434 	ar9285_antdiv_comb_conf_get(ah, &div_ant_conf);
435 	curr_alt_set = div_ant_conf.alt_lna_conf;
436 	curr_main_set = div_ant_conf.main_lna_conf;
437 	curr_bias = div_ant_conf.fast_div_bias;
438 
439 	antcomb->count++;
440 
441 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
442 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
443 			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
444 						  main_rssi_avg);
445 			antcomb->alt_good = AH_TRUE;
446 		} else {
447 			antcomb->alt_good = AH_FALSE;
448 		}
449 
450 		antcomb->count = 0;
451 		antcomb->scan = AH_TRUE;
452 		antcomb->scan_not_start = AH_TRUE;
453 	}
454 
455 	if (!antcomb->scan) {
456 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
457 			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
458 				/* Switch main and alt LNA */
459 				div_ant_conf.main_lna_conf =
460 						HAL_ANT_DIV_COMB_LNA2;
461 				div_ant_conf.alt_lna_conf  =
462 						HAL_ANT_DIV_COMB_LNA1;
463 			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
464 				div_ant_conf.main_lna_conf =
465 						HAL_ANT_DIV_COMB_LNA1;
466 				div_ant_conf.alt_lna_conf  =
467 						HAL_ANT_DIV_COMB_LNA2;
468 			}
469 
470 			goto div_comb_done;
471 		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
472 			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
473 			/* Set alt to another LNA */
474 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
475 				div_ant_conf.alt_lna_conf =
476 						HAL_ANT_DIV_COMB_LNA1;
477 			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
478 				div_ant_conf.alt_lna_conf =
479 						HAL_ANT_DIV_COMB_LNA2;
480 
481 			goto div_comb_done;
482 		}
483 
484 		if ((alt_rssi_avg < (main_rssi_avg +
485 		    ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
486 			goto div_comb_done;
487 	}
488 
489 	if (!antcomb->scan_not_start) {
490 		switch (curr_alt_set) {
491 		case HAL_ANT_DIV_COMB_LNA2:
492 			antcomb->rssi_lna2 = alt_rssi_avg;
493 			antcomb->rssi_lna1 = main_rssi_avg;
494 			antcomb->scan = AH_TRUE;
495 			/* set to A+B */
496 			div_ant_conf.main_lna_conf =
497 				HAL_ANT_DIV_COMB_LNA1;
498 			div_ant_conf.alt_lna_conf  =
499 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
500 			break;
501 		case HAL_ANT_DIV_COMB_LNA1:
502 			antcomb->rssi_lna1 = alt_rssi_avg;
503 			antcomb->rssi_lna2 = main_rssi_avg;
504 			antcomb->scan = AH_TRUE;
505 			/* set to A+B */
506 			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
507 			div_ant_conf.alt_lna_conf  =
508 				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
509 			break;
510 		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
511 			antcomb->rssi_add = alt_rssi_avg;
512 			antcomb->scan = AH_TRUE;
513 			/* set to A-B */
514 			div_ant_conf.alt_lna_conf =
515 				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
516 			break;
517 		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
518 			antcomb->rssi_sub = alt_rssi_avg;
519 			antcomb->scan = AH_FALSE;
520 			if (antcomb->rssi_lna2 >
521 			    (antcomb->rssi_lna1 +
522 			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
523 				/* use LNA2 as main LNA */
524 				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
525 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
526 					/* set to A+B */
527 					div_ant_conf.main_lna_conf =
528 						HAL_ANT_DIV_COMB_LNA2;
529 					div_ant_conf.alt_lna_conf  =
530 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
531 				} else if (antcomb->rssi_sub >
532 					   antcomb->rssi_lna1) {
533 					/* set to A-B */
534 					div_ant_conf.main_lna_conf =
535 						HAL_ANT_DIV_COMB_LNA2;
536 					div_ant_conf.alt_lna_conf =
537 						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
538 				} else {
539 					/* set to LNA1 */
540 					div_ant_conf.main_lna_conf =
541 						HAL_ANT_DIV_COMB_LNA2;
542 					div_ant_conf.alt_lna_conf =
543 						HAL_ANT_DIV_COMB_LNA1;
544 				}
545 			} else {
546 				/* use LNA1 as main LNA */
547 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
548 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
549 					/* set to A+B */
550 					div_ant_conf.main_lna_conf =
551 						HAL_ANT_DIV_COMB_LNA1;
552 					div_ant_conf.alt_lna_conf  =
553 						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
554 				} else if (antcomb->rssi_sub >
555 					   antcomb->rssi_lna1) {
556 					/* set to A-B */
557 					div_ant_conf.main_lna_conf =
558 						HAL_ANT_DIV_COMB_LNA1;
559 					div_ant_conf.alt_lna_conf =
560 						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
561 				} else {
562 					/* set to LNA2 */
563 					div_ant_conf.main_lna_conf =
564 						HAL_ANT_DIV_COMB_LNA1;
565 					div_ant_conf.alt_lna_conf =
566 						HAL_ANT_DIV_COMB_LNA2;
567 				}
568 			}
569 			break;
570 		default:
571 			break;
572 		}
573 	} else {
574 		if (!antcomb->alt_good) {
575 			antcomb->scan_not_start = AH_FALSE;
576 			/* Set alt to another LNA */
577 			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
578 				div_ant_conf.main_lna_conf =
579 						HAL_ANT_DIV_COMB_LNA2;
580 				div_ant_conf.alt_lna_conf =
581 						HAL_ANT_DIV_COMB_LNA1;
582 			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
583 				div_ant_conf.main_lna_conf =
584 						HAL_ANT_DIV_COMB_LNA1;
585 				div_ant_conf.alt_lna_conf =
586 						HAL_ANT_DIV_COMB_LNA2;
587 			}
588 			goto div_comb_done;
589 		}
590 	}
591 
592 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
593 					   main_rssi_avg, alt_rssi_avg,
594 					   alt_ratio);
595 
596 	antcomb->quick_scan_cnt++;
597 
598 div_comb_done:
599 	ath_ant_div_conf_fast_divbias(&div_ant_conf);
600 
601 	ar9285_antdiv_comb_conf_set(ah, &div_ant_conf);
602 
603 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
604 	   __func__, antcomb->total_pkt_count);
605 
606 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
607 	   __func__, antcomb->main_total_rssi);
608 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
609 	   __func__, antcomb->alt_total_rssi);
610 
611 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
612 	   __func__, main_rssi_avg);
613 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
614 	   __func__, alt_rssi_avg);
615 
616 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
617 	   __func__, antcomb->main_recv_cnt);
618 	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
619 	   __func__, antcomb->alt_recv_cnt);
620 
621 //	if (curr_alt_set != div_ant_conf.alt_lna_conf)
622 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
623 		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
624 //	if (curr_main_set != div_ant_conf.main_lna_conf)
625 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
626 		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
627 //	if (curr_bias != div_ant_conf.fast_div_bias)
628 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
629 		    __func__, curr_bias, div_ant_conf.fast_div_bias);
630 
631 	antcomb->scan_start_time = ticks;
632 	antcomb->total_pkt_count = 0;
633 	antcomb->main_total_rssi = 0;
634 	antcomb->alt_total_rssi = 0;
635 	antcomb->main_recv_cnt = 0;
636 	antcomb->alt_recv_cnt = 0;
637 }
638 
639 /*
640  * Set the antenna switch to control RX antenna diversity.
641  *
642  * If a fixed configuration is used, the LNA and div bias
643  * settings are fixed and the antenna diversity scanning routine
644  * is disabled.
645  *
646  * If a variable configuration is used, a default is programmed
647  * in and sampling commences per RXed packet.
648  *
649  * Since this is called from ar9285SetBoardValues() to setup
650  * diversity, it means that after a reset or scan, any current
651  * software diversity combining settings will be lost and won't
652  * re-appear until after the first successful sample run.
653  * Please keep this in mind if you're seeing weird performance
654  * that happens to relate to scan/diversity timing.
655  */
656 HAL_BOOL
657 ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
658 {
659 	int regVal;
660 	const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
661 	const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
662 	uint8_t ant_div_control1, ant_div_control2;
663 
664 	if (pModal->version < 3) {
665 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: not supported\n",
666 	    __func__);
667 		return AH_FALSE;	/* Can't do diversity */
668 	}
669 
670 	/* Store settings */
671 	AH5212(ah)->ah_antControl = settings;
672 	AH5212(ah)->ah_diversity = (settings == HAL_ANT_VARIABLE);
673 
674 	/* XXX don't fiddle if the PHY is in sleep mode or ! chan */
675 
676 	/* Begin setting the relevant registers */
677 
678 	ant_div_control1 = pModal->antdiv_ctl1;
679 	ant_div_control2 = pModal->antdiv_ctl2;
680 
681 	regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
682 	regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
683 
684 	/* enable antenna diversity only if diversityControl == HAL_ANT_VARIABLE */
685 	if (settings == HAL_ANT_VARIABLE)
686 	    regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL);
687 
688 	if (settings == HAL_ANT_VARIABLE) {
689 	    HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_VARIABLE\n",
690 	      __func__);
691 	    regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
692 	    regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
693 	    regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
694 	    regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
695 	} else {
696 	    if (settings == HAL_ANT_FIXED_A) {
697 		/* Diversity disabled, RX = LNA1 */
698 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n",
699 		    __func__);
700 		regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
701 		regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
702 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
703 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
704 	    }
705 	    else if (settings == HAL_ANT_FIXED_B) {
706 		/* Diversity disabled, RX = LNA2 */
707 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n",
708 		    __func__);
709 		regVal |= SM(HAL_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
710 		regVal |= SM(HAL_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
711 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
712 		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
713 	    }
714 	}
715 
716 	OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
717 	regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
718 	regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
719 	regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
720 	if (settings == HAL_ANT_VARIABLE)
721 	    regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
722 
723 	OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
724 	regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
725 
726 	/*
727 	 * If Diversity combining is available and the diversity setting
728 	 * is to allow variable diversity, enable it by default.
729 	 *
730 	 * This will be eventually overridden by the software antenna
731 	 * diversity logic.
732 	 *
733 	 * Note that yes, this following section overrides the above
734 	 * settings for the LNA configuration and fast-bias.
735 	 */
736 	if (ar9285_check_div_comb(ah) && AH5212(ah)->ah_diversity == AH_TRUE) {
737 		// If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning
738 		HALDEBUG(ah, HAL_DEBUG_DIVERSITY,
739 		    "%s: Enable initial settings for combined diversity\n",
740 		    __func__);
741 		regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
742 		regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF));
743 		regVal |= (HAL_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
744 		regVal |= (HAL_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
745 		regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
746 		regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
747 		OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
748 	}
749 
750 	return AH_TRUE;
751 }
752