1 /*
2  * Copyright (c) 2012 Qualcomm Atheros, Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16 
17 #include "ath9k.h"
18 
19 static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
20 					       int mindelta, int main_rssi_avg,
21 					       int alt_rssi_avg, int pkt_count)
22 {
23 	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
24 		 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
25 		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
26 }
27 
28 static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
29 					      int curr_main_set, int curr_alt_set,
30 					      int alt_rssi_avg, int main_rssi_avg)
31 {
32 	bool result = false;
33 	switch (div_group) {
34 	case 0:
35 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
36 			result = true;
37 		break;
38 	case 1:
39 	case 2:
40 		if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
41 		      (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
42 		      (alt_rssi_avg >= (main_rssi_avg - 5))) ||
43 		     ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
44 		      (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
45 		      (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
46 		    (alt_rssi_avg >= 4))
47 			result = true;
48 		else
49 			result = false;
50 		break;
51 	}
52 
53 	return result;
54 }
55 
56 static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
57 				      struct ath_hw_antcomb_conf ant_conf,
58 				      int main_rssi_avg)
59 {
60 	antcomb->quick_scan_cnt = 0;
61 
62 	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
63 		antcomb->rssi_lna2 = main_rssi_avg;
64 	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
65 		antcomb->rssi_lna1 = main_rssi_avg;
66 
67 	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
68 	case 0x10: /* LNA2 A-B */
69 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
70 		antcomb->first_quick_scan_conf =
71 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
72 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
73 		break;
74 	case 0x20: /* LNA1 A-B */
75 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
76 		antcomb->first_quick_scan_conf =
77 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
78 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
79 		break;
80 	case 0x21: /* LNA1 LNA2 */
81 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
82 		antcomb->first_quick_scan_conf =
83 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
84 		antcomb->second_quick_scan_conf =
85 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
86 		break;
87 	case 0x12: /* LNA2 LNA1 */
88 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
89 		antcomb->first_quick_scan_conf =
90 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
91 		antcomb->second_quick_scan_conf =
92 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
93 		break;
94 	case 0x13: /* LNA2 A+B */
95 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
96 		antcomb->first_quick_scan_conf =
97 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
98 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
99 		break;
100 	case 0x23: /* LNA1 A+B */
101 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
102 		antcomb->first_quick_scan_conf =
103 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
104 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
105 		break;
106 	default:
107 		break;
108 	}
109 }
110 
111 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
112 				       struct ath_hw_antcomb_conf *div_ant_conf,
113 				       int main_rssi_avg, int alt_rssi_avg,
114 				       int alt_ratio)
115 {
116 	/* alt_good */
117 	switch (antcomb->quick_scan_cnt) {
118 	case 0:
119 		/* set alt to main, and alt to first conf */
120 		div_ant_conf->main_lna_conf = antcomb->main_conf;
121 		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
122 		break;
123 	case 1:
124 		/* set alt to main, and alt to first conf */
125 		div_ant_conf->main_lna_conf = antcomb->main_conf;
126 		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
127 		antcomb->rssi_first = main_rssi_avg;
128 		antcomb->rssi_second = alt_rssi_avg;
129 
130 		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
131 			/* main is LNA1 */
132 			if (ath_is_alt_ant_ratio_better(alt_ratio,
133 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
134 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
135 						main_rssi_avg, alt_rssi_avg,
136 						antcomb->total_pkt_count))
137 				antcomb->first_ratio = true;
138 			else
139 				antcomb->first_ratio = false;
140 		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
141 			if (ath_is_alt_ant_ratio_better(alt_ratio,
142 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
143 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
144 						main_rssi_avg, alt_rssi_avg,
145 						antcomb->total_pkt_count))
146 				antcomb->first_ratio = true;
147 			else
148 				antcomb->first_ratio = false;
149 		} else {
150 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
151 			      (alt_rssi_avg > main_rssi_avg +
152 			       ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
153 			     (alt_rssi_avg > main_rssi_avg)) &&
154 			    (antcomb->total_pkt_count > 50))
155 				antcomb->first_ratio = true;
156 			else
157 				antcomb->first_ratio = false;
158 		}
159 		break;
160 	case 2:
161 		antcomb->alt_good = false;
162 		antcomb->scan_not_start = false;
163 		antcomb->scan = false;
164 		antcomb->rssi_first = main_rssi_avg;
165 		antcomb->rssi_third = alt_rssi_avg;
166 
167 		if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
168 			antcomb->rssi_lna1 = alt_rssi_avg;
169 		else if (antcomb->second_quick_scan_conf ==
170 			 ATH_ANT_DIV_COMB_LNA2)
171 			antcomb->rssi_lna2 = alt_rssi_avg;
172 		else if (antcomb->second_quick_scan_conf ==
173 			 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
174 			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
175 				antcomb->rssi_lna2 = main_rssi_avg;
176 			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
177 				antcomb->rssi_lna1 = main_rssi_avg;
178 		}
179 
180 		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
181 		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
182 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
183 		else
184 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
185 
186 		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
187 			if (ath_is_alt_ant_ratio_better(alt_ratio,
188 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
189 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
190 						main_rssi_avg, alt_rssi_avg,
191 						antcomb->total_pkt_count))
192 				antcomb->second_ratio = true;
193 			else
194 				antcomb->second_ratio = false;
195 		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
196 			if (ath_is_alt_ant_ratio_better(alt_ratio,
197 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
198 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
199 						main_rssi_avg, alt_rssi_avg,
200 						antcomb->total_pkt_count))
201 				antcomb->second_ratio = true;
202 			else
203 				antcomb->second_ratio = false;
204 		} else {
205 			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
206 			      (alt_rssi_avg > main_rssi_avg +
207 			       ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
208 			     (alt_rssi_avg > main_rssi_avg)) &&
209 			    (antcomb->total_pkt_count > 50))
210 				antcomb->second_ratio = true;
211 			else
212 				antcomb->second_ratio = false;
213 		}
214 
215 		/* set alt to the conf with maximun ratio */
216 		if (antcomb->first_ratio && antcomb->second_ratio) {
217 			if (antcomb->rssi_second > antcomb->rssi_third) {
218 				/* first alt*/
219 				if ((antcomb->first_quick_scan_conf ==
220 				    ATH_ANT_DIV_COMB_LNA1) ||
221 				    (antcomb->first_quick_scan_conf ==
222 				    ATH_ANT_DIV_COMB_LNA2))
223 					/* Set alt LNA1 or LNA2*/
224 					if (div_ant_conf->main_lna_conf ==
225 					    ATH_ANT_DIV_COMB_LNA2)
226 						div_ant_conf->alt_lna_conf =
227 							ATH_ANT_DIV_COMB_LNA1;
228 					else
229 						div_ant_conf->alt_lna_conf =
230 							ATH_ANT_DIV_COMB_LNA2;
231 				else
232 					/* Set alt to A+B or A-B */
233 					div_ant_conf->alt_lna_conf =
234 						antcomb->first_quick_scan_conf;
235 			} else if ((antcomb->second_quick_scan_conf ==
236 				   ATH_ANT_DIV_COMB_LNA1) ||
237 				   (antcomb->second_quick_scan_conf ==
238 				   ATH_ANT_DIV_COMB_LNA2)) {
239 				/* Set alt LNA1 or LNA2 */
240 				if (div_ant_conf->main_lna_conf ==
241 				    ATH_ANT_DIV_COMB_LNA2)
242 					div_ant_conf->alt_lna_conf =
243 						ATH_ANT_DIV_COMB_LNA1;
244 				else
245 					div_ant_conf->alt_lna_conf =
246 						ATH_ANT_DIV_COMB_LNA2;
247 			} else {
248 				/* Set alt to A+B or A-B */
249 				div_ant_conf->alt_lna_conf =
250 					antcomb->second_quick_scan_conf;
251 			}
252 		} else if (antcomb->first_ratio) {
253 			/* first alt */
254 			if ((antcomb->first_quick_scan_conf ==
255 			    ATH_ANT_DIV_COMB_LNA1) ||
256 			    (antcomb->first_quick_scan_conf ==
257 			    ATH_ANT_DIV_COMB_LNA2))
258 					/* Set alt LNA1 or LNA2 */
259 				if (div_ant_conf->main_lna_conf ==
260 				    ATH_ANT_DIV_COMB_LNA2)
261 					div_ant_conf->alt_lna_conf =
262 							ATH_ANT_DIV_COMB_LNA1;
263 				else
264 					div_ant_conf->alt_lna_conf =
265 							ATH_ANT_DIV_COMB_LNA2;
266 			else
267 				/* Set alt to A+B or A-B */
268 				div_ant_conf->alt_lna_conf =
269 						antcomb->first_quick_scan_conf;
270 		} else if (antcomb->second_ratio) {
271 				/* second alt */
272 			if ((antcomb->second_quick_scan_conf ==
273 			    ATH_ANT_DIV_COMB_LNA1) ||
274 			    (antcomb->second_quick_scan_conf ==
275 			    ATH_ANT_DIV_COMB_LNA2))
276 				/* Set alt LNA1 or LNA2 */
277 				if (div_ant_conf->main_lna_conf ==
278 				    ATH_ANT_DIV_COMB_LNA2)
279 					div_ant_conf->alt_lna_conf =
280 						ATH_ANT_DIV_COMB_LNA1;
281 				else
282 					div_ant_conf->alt_lna_conf =
283 						ATH_ANT_DIV_COMB_LNA2;
284 			else
285 				/* Set alt to A+B or A-B */
286 				div_ant_conf->alt_lna_conf =
287 						antcomb->second_quick_scan_conf;
288 		} else {
289 			/* main is largest */
290 			if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
291 			    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
292 				/* Set alt LNA1 or LNA2 */
293 				if (div_ant_conf->main_lna_conf ==
294 				    ATH_ANT_DIV_COMB_LNA2)
295 					div_ant_conf->alt_lna_conf =
296 							ATH_ANT_DIV_COMB_LNA1;
297 				else
298 					div_ant_conf->alt_lna_conf =
299 							ATH_ANT_DIV_COMB_LNA2;
300 			else
301 				/* Set alt to A+B or A-B */
302 				div_ant_conf->alt_lna_conf = antcomb->main_conf;
303 		}
304 		break;
305 	default:
306 		break;
307 	}
308 }
309 
310 static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
311 					  struct ath_ant_comb *antcomb,
312 					  int alt_ratio)
313 {
314 	if (ant_conf->div_group == 0) {
315 		/* Adjust the fast_div_bias based on main and alt lna conf */
316 		switch ((ant_conf->main_lna_conf << 4) |
317 				ant_conf->alt_lna_conf) {
318 		case 0x01: /* A-B LNA2 */
319 			ant_conf->fast_div_bias = 0x3b;
320 			break;
321 		case 0x02: /* A-B LNA1 */
322 			ant_conf->fast_div_bias = 0x3d;
323 			break;
324 		case 0x03: /* A-B A+B */
325 			ant_conf->fast_div_bias = 0x1;
326 			break;
327 		case 0x10: /* LNA2 A-B */
328 			ant_conf->fast_div_bias = 0x7;
329 			break;
330 		case 0x12: /* LNA2 LNA1 */
331 			ant_conf->fast_div_bias = 0x2;
332 			break;
333 		case 0x13: /* LNA2 A+B */
334 			ant_conf->fast_div_bias = 0x7;
335 			break;
336 		case 0x20: /* LNA1 A-B */
337 			ant_conf->fast_div_bias = 0x6;
338 			break;
339 		case 0x21: /* LNA1 LNA2 */
340 			ant_conf->fast_div_bias = 0x0;
341 			break;
342 		case 0x23: /* LNA1 A+B */
343 			ant_conf->fast_div_bias = 0x6;
344 			break;
345 		case 0x30: /* A+B A-B */
346 			ant_conf->fast_div_bias = 0x1;
347 			break;
348 		case 0x31: /* A+B LNA2 */
349 			ant_conf->fast_div_bias = 0x3b;
350 			break;
351 		case 0x32: /* A+B LNA1 */
352 			ant_conf->fast_div_bias = 0x3d;
353 			break;
354 		default:
355 			break;
356 		}
357 	} else if (ant_conf->div_group == 1) {
358 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
359 		switch ((ant_conf->main_lna_conf << 4) |
360 			ant_conf->alt_lna_conf) {
361 		case 0x01: /* A-B LNA2 */
362 			ant_conf->fast_div_bias = 0x1;
363 			ant_conf->main_gaintb = 0;
364 			ant_conf->alt_gaintb = 0;
365 			break;
366 		case 0x02: /* A-B LNA1 */
367 			ant_conf->fast_div_bias = 0x1;
368 			ant_conf->main_gaintb = 0;
369 			ant_conf->alt_gaintb = 0;
370 			break;
371 		case 0x03: /* A-B A+B */
372 			ant_conf->fast_div_bias = 0x1;
373 			ant_conf->main_gaintb = 0;
374 			ant_conf->alt_gaintb = 0;
375 			break;
376 		case 0x10: /* LNA2 A-B */
377 			if (!(antcomb->scan) &&
378 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
379 				ant_conf->fast_div_bias = 0x3f;
380 			else
381 				ant_conf->fast_div_bias = 0x1;
382 			ant_conf->main_gaintb = 0;
383 			ant_conf->alt_gaintb = 0;
384 			break;
385 		case 0x12: /* LNA2 LNA1 */
386 			ant_conf->fast_div_bias = 0x1;
387 			ant_conf->main_gaintb = 0;
388 			ant_conf->alt_gaintb = 0;
389 			break;
390 		case 0x13: /* LNA2 A+B */
391 			if (!(antcomb->scan) &&
392 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
393 				ant_conf->fast_div_bias = 0x3f;
394 			else
395 				ant_conf->fast_div_bias = 0x1;
396 			ant_conf->main_gaintb = 0;
397 			ant_conf->alt_gaintb = 0;
398 			break;
399 		case 0x20: /* LNA1 A-B */
400 			if (!(antcomb->scan) &&
401 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
402 				ant_conf->fast_div_bias = 0x3f;
403 			else
404 				ant_conf->fast_div_bias = 0x1;
405 			ant_conf->main_gaintb = 0;
406 			ant_conf->alt_gaintb = 0;
407 			break;
408 		case 0x21: /* LNA1 LNA2 */
409 			ant_conf->fast_div_bias = 0x1;
410 			ant_conf->main_gaintb = 0;
411 			ant_conf->alt_gaintb = 0;
412 			break;
413 		case 0x23: /* LNA1 A+B */
414 			if (!(antcomb->scan) &&
415 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
416 				ant_conf->fast_div_bias = 0x3f;
417 			else
418 				ant_conf->fast_div_bias = 0x1;
419 			ant_conf->main_gaintb = 0;
420 			ant_conf->alt_gaintb = 0;
421 			break;
422 		case 0x30: /* A+B A-B */
423 			ant_conf->fast_div_bias = 0x1;
424 			ant_conf->main_gaintb = 0;
425 			ant_conf->alt_gaintb = 0;
426 			break;
427 		case 0x31: /* A+B LNA2 */
428 			ant_conf->fast_div_bias = 0x1;
429 			ant_conf->main_gaintb = 0;
430 			ant_conf->alt_gaintb = 0;
431 			break;
432 		case 0x32: /* A+B LNA1 */
433 			ant_conf->fast_div_bias = 0x1;
434 			ant_conf->main_gaintb = 0;
435 			ant_conf->alt_gaintb = 0;
436 			break;
437 		default:
438 			break;
439 		}
440 	} else if (ant_conf->div_group == 2) {
441 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
442 		switch ((ant_conf->main_lna_conf << 4) |
443 				ant_conf->alt_lna_conf) {
444 		case 0x01: /* A-B LNA2 */
445 			ant_conf->fast_div_bias = 0x1;
446 			ant_conf->main_gaintb = 0;
447 			ant_conf->alt_gaintb = 0;
448 			break;
449 		case 0x02: /* A-B LNA1 */
450 			ant_conf->fast_div_bias = 0x1;
451 			ant_conf->main_gaintb = 0;
452 			ant_conf->alt_gaintb = 0;
453 			break;
454 		case 0x03: /* A-B A+B */
455 			ant_conf->fast_div_bias = 0x1;
456 			ant_conf->main_gaintb = 0;
457 			ant_conf->alt_gaintb = 0;
458 			break;
459 		case 0x10: /* LNA2 A-B */
460 			if (!(antcomb->scan) &&
461 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
462 				ant_conf->fast_div_bias = 0x1;
463 			else
464 				ant_conf->fast_div_bias = 0x2;
465 			ant_conf->main_gaintb = 0;
466 			ant_conf->alt_gaintb = 0;
467 			break;
468 		case 0x12: /* LNA2 LNA1 */
469 			ant_conf->fast_div_bias = 0x1;
470 			ant_conf->main_gaintb = 0;
471 			ant_conf->alt_gaintb = 0;
472 			break;
473 		case 0x13: /* LNA2 A+B */
474 			if (!(antcomb->scan) &&
475 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
476 				ant_conf->fast_div_bias = 0x1;
477 			else
478 				ant_conf->fast_div_bias = 0x2;
479 			ant_conf->main_gaintb = 0;
480 			ant_conf->alt_gaintb = 0;
481 			break;
482 		case 0x20: /* LNA1 A-B */
483 			if (!(antcomb->scan) &&
484 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
485 				ant_conf->fast_div_bias = 0x1;
486 			else
487 				ant_conf->fast_div_bias = 0x2;
488 			ant_conf->main_gaintb = 0;
489 			ant_conf->alt_gaintb = 0;
490 			break;
491 		case 0x21: /* LNA1 LNA2 */
492 			ant_conf->fast_div_bias = 0x1;
493 			ant_conf->main_gaintb = 0;
494 			ant_conf->alt_gaintb = 0;
495 			break;
496 		case 0x23: /* LNA1 A+B */
497 			if (!(antcomb->scan) &&
498 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
499 				ant_conf->fast_div_bias = 0x1;
500 			else
501 				ant_conf->fast_div_bias = 0x2;
502 			ant_conf->main_gaintb = 0;
503 			ant_conf->alt_gaintb = 0;
504 			break;
505 		case 0x30: /* A+B A-B */
506 			ant_conf->fast_div_bias = 0x1;
507 			ant_conf->main_gaintb = 0;
508 			ant_conf->alt_gaintb = 0;
509 			break;
510 		case 0x31: /* A+B LNA2 */
511 			ant_conf->fast_div_bias = 0x1;
512 			ant_conf->main_gaintb = 0;
513 			ant_conf->alt_gaintb = 0;
514 			break;
515 		case 0x32: /* A+B LNA1 */
516 			ant_conf->fast_div_bias = 0x1;
517 			ant_conf->main_gaintb = 0;
518 			ant_conf->alt_gaintb = 0;
519 			break;
520 		default:
521 			break;
522 		}
523 	}
524 }
525 
526 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
527 {
528 	struct ath_hw_antcomb_conf div_ant_conf;
529 	struct ath_ant_comb *antcomb = &sc->ant_comb;
530 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
531 	int curr_main_set;
532 	int main_rssi = rs->rs_rssi_ctl0;
533 	int alt_rssi = rs->rs_rssi_ctl1;
534 	int rx_ant_conf,  main_ant_conf;
535 	bool short_scan = false;
536 
537 	rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
538 		       ATH_ANT_RX_MASK;
539 	main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
540 			 ATH_ANT_RX_MASK;
541 
542 	/* Record packet only when both main_rssi and  alt_rssi is positive */
543 	if (main_rssi > 0 && alt_rssi > 0) {
544 		antcomb->total_pkt_count++;
545 		antcomb->main_total_rssi += main_rssi;
546 		antcomb->alt_total_rssi  += alt_rssi;
547 		if (main_ant_conf == rx_ant_conf)
548 			antcomb->main_recv_cnt++;
549 		else
550 			antcomb->alt_recv_cnt++;
551 	}
552 
553 	/* Short scan check */
554 	if (antcomb->scan && antcomb->alt_good) {
555 		if (time_after(jiffies, antcomb->scan_start_time +
556 		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
557 			short_scan = true;
558 		else
559 			if (antcomb->total_pkt_count ==
560 			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
561 				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
562 					    antcomb->total_pkt_count);
563 				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
564 					short_scan = true;
565 			}
566 	}
567 
568 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
569 	    rs->rs_moreaggr) && !short_scan)
570 		return;
571 
572 	if (antcomb->total_pkt_count) {
573 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
574 			     antcomb->total_pkt_count);
575 		main_rssi_avg = (antcomb->main_total_rssi /
576 				 antcomb->total_pkt_count);
577 		alt_rssi_avg = (antcomb->alt_total_rssi /
578 				 antcomb->total_pkt_count);
579 	}
580 
581 
582 	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
583 	curr_alt_set = div_ant_conf.alt_lna_conf;
584 	curr_main_set = div_ant_conf.main_lna_conf;
585 
586 	antcomb->count++;
587 
588 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
589 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
590 			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
591 						  main_rssi_avg);
592 			antcomb->alt_good = true;
593 		} else {
594 			antcomb->alt_good = false;
595 		}
596 
597 		antcomb->count = 0;
598 		antcomb->scan = true;
599 		antcomb->scan_not_start = true;
600 	}
601 
602 	if (!antcomb->scan) {
603 		if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
604 					alt_ratio, curr_main_set, curr_alt_set,
605 					alt_rssi_avg, main_rssi_avg)) {
606 			if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
607 				/* Switch main and alt LNA */
608 				div_ant_conf.main_lna_conf =
609 						ATH_ANT_DIV_COMB_LNA2;
610 				div_ant_conf.alt_lna_conf  =
611 						ATH_ANT_DIV_COMB_LNA1;
612 			} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
613 				div_ant_conf.main_lna_conf =
614 						ATH_ANT_DIV_COMB_LNA1;
615 				div_ant_conf.alt_lna_conf  =
616 						ATH_ANT_DIV_COMB_LNA2;
617 			}
618 
619 			goto div_comb_done;
620 		} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
621 			   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
622 			/* Set alt to another LNA */
623 			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
624 				div_ant_conf.alt_lna_conf =
625 						ATH_ANT_DIV_COMB_LNA1;
626 			else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
627 				div_ant_conf.alt_lna_conf =
628 						ATH_ANT_DIV_COMB_LNA2;
629 
630 			goto div_comb_done;
631 		}
632 
633 		if ((alt_rssi_avg < (main_rssi_avg +
634 				     div_ant_conf.lna1_lna2_delta)))
635 			goto div_comb_done;
636 	}
637 
638 	if (!antcomb->scan_not_start) {
639 		switch (curr_alt_set) {
640 		case ATH_ANT_DIV_COMB_LNA2:
641 			antcomb->rssi_lna2 = alt_rssi_avg;
642 			antcomb->rssi_lna1 = main_rssi_avg;
643 			antcomb->scan = true;
644 			/* set to A+B */
645 			div_ant_conf.main_lna_conf =
646 				ATH_ANT_DIV_COMB_LNA1;
647 			div_ant_conf.alt_lna_conf  =
648 				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
649 			break;
650 		case ATH_ANT_DIV_COMB_LNA1:
651 			antcomb->rssi_lna1 = alt_rssi_avg;
652 			antcomb->rssi_lna2 = main_rssi_avg;
653 			antcomb->scan = true;
654 			/* set to A+B */
655 			div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
656 			div_ant_conf.alt_lna_conf  =
657 				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
658 			break;
659 		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
660 			antcomb->rssi_add = alt_rssi_avg;
661 			antcomb->scan = true;
662 			/* set to A-B */
663 			div_ant_conf.alt_lna_conf =
664 				ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
665 			break;
666 		case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
667 			antcomb->rssi_sub = alt_rssi_avg;
668 			antcomb->scan = false;
669 			if (antcomb->rssi_lna2 >
670 			    (antcomb->rssi_lna1 +
671 			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
672 				/* use LNA2 as main LNA */
673 				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
674 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
675 					/* set to A+B */
676 					div_ant_conf.main_lna_conf =
677 						ATH_ANT_DIV_COMB_LNA2;
678 					div_ant_conf.alt_lna_conf  =
679 						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
680 				} else if (antcomb->rssi_sub >
681 					   antcomb->rssi_lna1) {
682 					/* set to A-B */
683 					div_ant_conf.main_lna_conf =
684 						ATH_ANT_DIV_COMB_LNA2;
685 					div_ant_conf.alt_lna_conf =
686 						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
687 				} else {
688 					/* set to LNA1 */
689 					div_ant_conf.main_lna_conf =
690 						ATH_ANT_DIV_COMB_LNA2;
691 					div_ant_conf.alt_lna_conf =
692 						ATH_ANT_DIV_COMB_LNA1;
693 				}
694 			} else {
695 				/* use LNA1 as main LNA */
696 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
697 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
698 					/* set to A+B */
699 					div_ant_conf.main_lna_conf =
700 						ATH_ANT_DIV_COMB_LNA1;
701 					div_ant_conf.alt_lna_conf  =
702 						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
703 				} else if (antcomb->rssi_sub >
704 					   antcomb->rssi_lna1) {
705 					/* set to A-B */
706 					div_ant_conf.main_lna_conf =
707 						ATH_ANT_DIV_COMB_LNA1;
708 					div_ant_conf.alt_lna_conf =
709 						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
710 				} else {
711 					/* set to LNA2 */
712 					div_ant_conf.main_lna_conf =
713 						ATH_ANT_DIV_COMB_LNA1;
714 					div_ant_conf.alt_lna_conf =
715 						ATH_ANT_DIV_COMB_LNA2;
716 				}
717 			}
718 			break;
719 		default:
720 			break;
721 		}
722 	} else {
723 		if (!antcomb->alt_good) {
724 			antcomb->scan_not_start = false;
725 			/* Set alt to another LNA */
726 			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
727 				div_ant_conf.main_lna_conf =
728 						ATH_ANT_DIV_COMB_LNA2;
729 				div_ant_conf.alt_lna_conf =
730 						ATH_ANT_DIV_COMB_LNA1;
731 			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
732 				div_ant_conf.main_lna_conf =
733 						ATH_ANT_DIV_COMB_LNA1;
734 				div_ant_conf.alt_lna_conf =
735 						ATH_ANT_DIV_COMB_LNA2;
736 			}
737 			goto div_comb_done;
738 		}
739 	}
740 
741 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
742 					   main_rssi_avg, alt_rssi_avg,
743 					   alt_ratio);
744 
745 	antcomb->quick_scan_cnt++;
746 
747 div_comb_done:
748 	ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
749 	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
750 
751 	antcomb->scan_start_time = jiffies;
752 	antcomb->total_pkt_count = 0;
753 	antcomb->main_total_rssi = 0;
754 	antcomb->alt_total_rssi = 0;
755 	antcomb->main_recv_cnt = 0;
756 	antcomb->alt_recv_cnt = 0;
757 }
758 
759 void ath_ant_comb_update(struct ath_softc *sc)
760 {
761 	struct ath_hw *ah = sc->sc_ah;
762 	struct ath_hw_antcomb_conf div_ant_conf;
763 	u8 lna_conf;
764 
765 	ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
766 
767 	if (sc->ant_rx == 1)
768 		lna_conf = ATH_ANT_DIV_COMB_LNA1;
769 	else
770 		lna_conf = ATH_ANT_DIV_COMB_LNA2;
771 
772 	div_ant_conf.main_lna_conf = lna_conf;
773 	div_ant_conf.alt_lna_conf = lna_conf;
774 
775 	ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
776 }
777