xref: /linux/drivers/net/wireless/ath/ath9k/antenna.c (revision 0d456bad36d42d16022be045c8a53ddbb59ee478)
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 	ant_conf->main_gaintb = 0;
315 	ant_conf->alt_gaintb = 0;
316 
317 	if (ant_conf->div_group == 0) {
318 		/* Adjust the fast_div_bias based on main and alt lna conf */
319 		switch ((ant_conf->main_lna_conf << 4) |
320 				ant_conf->alt_lna_conf) {
321 		case 0x01: /* A-B LNA2 */
322 			ant_conf->fast_div_bias = 0x3b;
323 			break;
324 		case 0x02: /* A-B LNA1 */
325 			ant_conf->fast_div_bias = 0x3d;
326 			break;
327 		case 0x03: /* A-B A+B */
328 			ant_conf->fast_div_bias = 0x1;
329 			break;
330 		case 0x10: /* LNA2 A-B */
331 			ant_conf->fast_div_bias = 0x7;
332 			break;
333 		case 0x12: /* LNA2 LNA1 */
334 			ant_conf->fast_div_bias = 0x2;
335 			break;
336 		case 0x13: /* LNA2 A+B */
337 			ant_conf->fast_div_bias = 0x7;
338 			break;
339 		case 0x20: /* LNA1 A-B */
340 			ant_conf->fast_div_bias = 0x6;
341 			break;
342 		case 0x21: /* LNA1 LNA2 */
343 			ant_conf->fast_div_bias = 0x0;
344 			break;
345 		case 0x23: /* LNA1 A+B */
346 			ant_conf->fast_div_bias = 0x6;
347 			break;
348 		case 0x30: /* A+B A-B */
349 			ant_conf->fast_div_bias = 0x1;
350 			break;
351 		case 0x31: /* A+B LNA2 */
352 			ant_conf->fast_div_bias = 0x3b;
353 			break;
354 		case 0x32: /* A+B LNA1 */
355 			ant_conf->fast_div_bias = 0x3d;
356 			break;
357 		default:
358 			break;
359 		}
360 	} else if (ant_conf->div_group == 1) {
361 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
362 		switch ((ant_conf->main_lna_conf << 4) |
363 			ant_conf->alt_lna_conf) {
364 		case 0x01: /* A-B LNA2 */
365 			ant_conf->fast_div_bias = 0x1;
366 			break;
367 		case 0x02: /* A-B LNA1 */
368 			ant_conf->fast_div_bias = 0x1;
369 			break;
370 		case 0x03: /* A-B A+B */
371 			ant_conf->fast_div_bias = 0x1;
372 			break;
373 		case 0x10: /* LNA2 A-B */
374 			if (!(antcomb->scan) &&
375 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
376 				ant_conf->fast_div_bias = 0x3f;
377 			else
378 				ant_conf->fast_div_bias = 0x1;
379 			break;
380 		case 0x12: /* LNA2 LNA1 */
381 			ant_conf->fast_div_bias = 0x1;
382 			break;
383 		case 0x13: /* LNA2 A+B */
384 			if (!(antcomb->scan) &&
385 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
386 				ant_conf->fast_div_bias = 0x3f;
387 			else
388 				ant_conf->fast_div_bias = 0x1;
389 			break;
390 		case 0x20: /* LNA1 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 			break;
397 		case 0x21: /* LNA1 LNA2 */
398 			ant_conf->fast_div_bias = 0x1;
399 			break;
400 		case 0x23: /* LNA1 A+B */
401 			if (!(antcomb->scan) &&
402 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
403 				ant_conf->fast_div_bias = 0x3f;
404 			else
405 				ant_conf->fast_div_bias = 0x1;
406 			break;
407 		case 0x30: /* A+B A-B */
408 			ant_conf->fast_div_bias = 0x1;
409 			break;
410 		case 0x31: /* A+B LNA2 */
411 			ant_conf->fast_div_bias = 0x1;
412 			break;
413 		case 0x32: /* A+B LNA1 */
414 			ant_conf->fast_div_bias = 0x1;
415 			break;
416 		default:
417 			break;
418 		}
419 	} else if (ant_conf->div_group == 2) {
420 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
421 		switch ((ant_conf->main_lna_conf << 4) |
422 				ant_conf->alt_lna_conf) {
423 		case 0x01: /* A-B LNA2 */
424 			ant_conf->fast_div_bias = 0x1;
425 			break;
426 		case 0x02: /* A-B LNA1 */
427 			ant_conf->fast_div_bias = 0x1;
428 			break;
429 		case 0x03: /* A-B A+B */
430 			ant_conf->fast_div_bias = 0x1;
431 			break;
432 		case 0x10: /* LNA2 A-B */
433 			if (!(antcomb->scan) &&
434 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
435 				ant_conf->fast_div_bias = 0x1;
436 			else
437 				ant_conf->fast_div_bias = 0x2;
438 			break;
439 		case 0x12: /* LNA2 LNA1 */
440 			ant_conf->fast_div_bias = 0x1;
441 			break;
442 		case 0x13: /* LNA2 A+B */
443 			if (!(antcomb->scan) &&
444 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
445 				ant_conf->fast_div_bias = 0x1;
446 			else
447 				ant_conf->fast_div_bias = 0x2;
448 			break;
449 		case 0x20: /* LNA1 A-B */
450 			if (!(antcomb->scan) &&
451 				(alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
452 				ant_conf->fast_div_bias = 0x1;
453 			else
454 				ant_conf->fast_div_bias = 0x2;
455 			break;
456 		case 0x21: /* LNA1 LNA2 */
457 			ant_conf->fast_div_bias = 0x1;
458 			break;
459 		case 0x23: /* LNA1 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 			break;
466 		case 0x30: /* A+B A-B */
467 			ant_conf->fast_div_bias = 0x1;
468 			break;
469 		case 0x31: /* A+B LNA2 */
470 			ant_conf->fast_div_bias = 0x1;
471 			break;
472 		case 0x32: /* A+B LNA1 */
473 			ant_conf->fast_div_bias = 0x1;
474 			break;
475 		default:
476 			break;
477 		}
478 	} else if (ant_conf->div_group == 3) {
479 		switch ((ant_conf->main_lna_conf << 4) |
480 			ant_conf->alt_lna_conf) {
481 		case 0x01: /* A-B LNA2 */
482 			ant_conf->fast_div_bias = 0x1;
483 			break;
484 		case 0x02: /* A-B LNA1 */
485 			ant_conf->fast_div_bias = 0x39;
486 			break;
487 		case 0x03: /* A-B A+B */
488 			ant_conf->fast_div_bias = 0x1;
489 			break;
490 		case 0x10: /* LNA2 A-B */
491 			if ((antcomb->scan == 0) &&
492 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
493 				ant_conf->fast_div_bias = 0x3f;
494 			} else {
495 				ant_conf->fast_div_bias = 0x1;
496 			}
497 			break;
498 		case 0x12: /* LNA2 LNA1 */
499 			ant_conf->fast_div_bias = 0x39;
500 			break;
501 		case 0x13: /* LNA2 A+B */
502 			if ((antcomb->scan == 0) &&
503 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
504 				ant_conf->fast_div_bias = 0x3f;
505 			} else {
506 				ant_conf->fast_div_bias = 0x1;
507 			}
508 			break;
509 		case 0x20: /* LNA1 A-B */
510 			if ((antcomb->scan == 0) &&
511 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
512 				ant_conf->fast_div_bias = 0x3f;
513 			} else {
514 				ant_conf->fast_div_bias = 0x4;
515 			}
516 			break;
517 		case 0x21: /* LNA1 LNA2 */
518 			ant_conf->fast_div_bias = 0x6;
519 			break;
520 		case 0x23: /* LNA1 A+B */
521 			if ((antcomb->scan == 0) &&
522 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
523 				ant_conf->fast_div_bias = 0x3f;
524 			} else {
525 				ant_conf->fast_div_bias = 0x6;
526 			}
527 			break;
528 		case 0x30: /* A+B A-B */
529 			ant_conf->fast_div_bias = 0x1;
530 			break;
531 		case 0x31: /* A+B LNA2 */
532 			ant_conf->fast_div_bias = 0x6;
533 			break;
534 		case 0x32: /* A+B LNA1 */
535 			ant_conf->fast_div_bias = 0x1;
536 			break;
537 		default:
538 			break;
539 		}
540 	}
541 }
542 
543 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
544 {
545 	struct ath_hw_antcomb_conf div_ant_conf;
546 	struct ath_ant_comb *antcomb = &sc->ant_comb;
547 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
548 	int curr_main_set;
549 	int main_rssi = rs->rs_rssi_ctl0;
550 	int alt_rssi = rs->rs_rssi_ctl1;
551 	int rx_ant_conf,  main_ant_conf;
552 	bool short_scan = false;
553 
554 	rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
555 		       ATH_ANT_RX_MASK;
556 	main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
557 			 ATH_ANT_RX_MASK;
558 
559 	/* Record packet only when both main_rssi and  alt_rssi is positive */
560 	if (main_rssi > 0 && alt_rssi > 0) {
561 		antcomb->total_pkt_count++;
562 		antcomb->main_total_rssi += main_rssi;
563 		antcomb->alt_total_rssi  += alt_rssi;
564 		if (main_ant_conf == rx_ant_conf)
565 			antcomb->main_recv_cnt++;
566 		else
567 			antcomb->alt_recv_cnt++;
568 	}
569 
570 	/* Short scan check */
571 	if (antcomb->scan && antcomb->alt_good) {
572 		if (time_after(jiffies, antcomb->scan_start_time +
573 		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
574 			short_scan = true;
575 		else
576 			if (antcomb->total_pkt_count ==
577 			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
578 				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
579 					    antcomb->total_pkt_count);
580 				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
581 					short_scan = true;
582 			}
583 	}
584 
585 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
586 	    rs->rs_moreaggr) && !short_scan)
587 		return;
588 
589 	if (antcomb->total_pkt_count) {
590 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
591 			     antcomb->total_pkt_count);
592 		main_rssi_avg = (antcomb->main_total_rssi /
593 				 antcomb->total_pkt_count);
594 		alt_rssi_avg = (antcomb->alt_total_rssi /
595 				 antcomb->total_pkt_count);
596 	}
597 
598 
599 	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
600 	curr_alt_set = div_ant_conf.alt_lna_conf;
601 	curr_main_set = div_ant_conf.main_lna_conf;
602 
603 	antcomb->count++;
604 
605 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
606 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
607 			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
608 						  main_rssi_avg);
609 			antcomb->alt_good = true;
610 		} else {
611 			antcomb->alt_good = false;
612 		}
613 
614 		antcomb->count = 0;
615 		antcomb->scan = true;
616 		antcomb->scan_not_start = true;
617 	}
618 
619 	if (!antcomb->scan) {
620 		if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
621 					alt_ratio, curr_main_set, curr_alt_set,
622 					alt_rssi_avg, main_rssi_avg)) {
623 			if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
624 				/* Switch main and alt LNA */
625 				div_ant_conf.main_lna_conf =
626 						ATH_ANT_DIV_COMB_LNA2;
627 				div_ant_conf.alt_lna_conf  =
628 						ATH_ANT_DIV_COMB_LNA1;
629 			} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
630 				div_ant_conf.main_lna_conf =
631 						ATH_ANT_DIV_COMB_LNA1;
632 				div_ant_conf.alt_lna_conf  =
633 						ATH_ANT_DIV_COMB_LNA2;
634 			}
635 
636 			goto div_comb_done;
637 		} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
638 			   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
639 			/* Set alt to another LNA */
640 			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
641 				div_ant_conf.alt_lna_conf =
642 						ATH_ANT_DIV_COMB_LNA1;
643 			else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
644 				div_ant_conf.alt_lna_conf =
645 						ATH_ANT_DIV_COMB_LNA2;
646 
647 			goto div_comb_done;
648 		}
649 
650 		if ((alt_rssi_avg < (main_rssi_avg +
651 				     div_ant_conf.lna1_lna2_delta)))
652 			goto div_comb_done;
653 	}
654 
655 	if (!antcomb->scan_not_start) {
656 		switch (curr_alt_set) {
657 		case ATH_ANT_DIV_COMB_LNA2:
658 			antcomb->rssi_lna2 = alt_rssi_avg;
659 			antcomb->rssi_lna1 = main_rssi_avg;
660 			antcomb->scan = true;
661 			/* set to A+B */
662 			div_ant_conf.main_lna_conf =
663 				ATH_ANT_DIV_COMB_LNA1;
664 			div_ant_conf.alt_lna_conf  =
665 				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
666 			break;
667 		case ATH_ANT_DIV_COMB_LNA1:
668 			antcomb->rssi_lna1 = alt_rssi_avg;
669 			antcomb->rssi_lna2 = main_rssi_avg;
670 			antcomb->scan = true;
671 			/* set to A+B */
672 			div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
673 			div_ant_conf.alt_lna_conf  =
674 				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
675 			break;
676 		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
677 			antcomb->rssi_add = alt_rssi_avg;
678 			antcomb->scan = true;
679 			/* set to A-B */
680 			div_ant_conf.alt_lna_conf =
681 				ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
682 			break;
683 		case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
684 			antcomb->rssi_sub = alt_rssi_avg;
685 			antcomb->scan = false;
686 			if (antcomb->rssi_lna2 >
687 			    (antcomb->rssi_lna1 +
688 			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
689 				/* use LNA2 as main LNA */
690 				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
691 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
692 					/* set to A+B */
693 					div_ant_conf.main_lna_conf =
694 						ATH_ANT_DIV_COMB_LNA2;
695 					div_ant_conf.alt_lna_conf  =
696 						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
697 				} else if (antcomb->rssi_sub >
698 					   antcomb->rssi_lna1) {
699 					/* set to A-B */
700 					div_ant_conf.main_lna_conf =
701 						ATH_ANT_DIV_COMB_LNA2;
702 					div_ant_conf.alt_lna_conf =
703 						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
704 				} else {
705 					/* set to LNA1 */
706 					div_ant_conf.main_lna_conf =
707 						ATH_ANT_DIV_COMB_LNA2;
708 					div_ant_conf.alt_lna_conf =
709 						ATH_ANT_DIV_COMB_LNA1;
710 				}
711 			} else {
712 				/* use LNA1 as main LNA */
713 				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
714 				    (antcomb->rssi_add > antcomb->rssi_sub)) {
715 					/* set to A+B */
716 					div_ant_conf.main_lna_conf =
717 						ATH_ANT_DIV_COMB_LNA1;
718 					div_ant_conf.alt_lna_conf  =
719 						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
720 				} else if (antcomb->rssi_sub >
721 					   antcomb->rssi_lna1) {
722 					/* set to A-B */
723 					div_ant_conf.main_lna_conf =
724 						ATH_ANT_DIV_COMB_LNA1;
725 					div_ant_conf.alt_lna_conf =
726 						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
727 				} else {
728 					/* set to LNA2 */
729 					div_ant_conf.main_lna_conf =
730 						ATH_ANT_DIV_COMB_LNA1;
731 					div_ant_conf.alt_lna_conf =
732 						ATH_ANT_DIV_COMB_LNA2;
733 				}
734 			}
735 			break;
736 		default:
737 			break;
738 		}
739 	} else {
740 		if (!antcomb->alt_good) {
741 			antcomb->scan_not_start = false;
742 			/* Set alt to another LNA */
743 			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
744 				div_ant_conf.main_lna_conf =
745 						ATH_ANT_DIV_COMB_LNA2;
746 				div_ant_conf.alt_lna_conf =
747 						ATH_ANT_DIV_COMB_LNA1;
748 			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
749 				div_ant_conf.main_lna_conf =
750 						ATH_ANT_DIV_COMB_LNA1;
751 				div_ant_conf.alt_lna_conf =
752 						ATH_ANT_DIV_COMB_LNA2;
753 			}
754 			goto div_comb_done;
755 		}
756 	}
757 
758 	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
759 					   main_rssi_avg, alt_rssi_avg,
760 					   alt_ratio);
761 
762 	antcomb->quick_scan_cnt++;
763 
764 div_comb_done:
765 	ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
766 	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
767 
768 	antcomb->scan_start_time = jiffies;
769 	antcomb->total_pkt_count = 0;
770 	antcomb->main_total_rssi = 0;
771 	antcomb->alt_total_rssi = 0;
772 	antcomb->main_recv_cnt = 0;
773 	antcomb->alt_recv_cnt = 0;
774 }
775 
776 void ath_ant_comb_update(struct ath_softc *sc)
777 {
778 	struct ath_hw *ah = sc->sc_ah;
779 	struct ath_common *common = ath9k_hw_common(ah);
780 	struct ath_hw_antcomb_conf div_ant_conf;
781 	u8 lna_conf;
782 
783 	ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
784 
785 	if (sc->ant_rx == 1)
786 		lna_conf = ATH_ANT_DIV_COMB_LNA1;
787 	else
788 		lna_conf = ATH_ANT_DIV_COMB_LNA2;
789 
790 	div_ant_conf.main_lna_conf = lna_conf;
791 	div_ant_conf.alt_lna_conf = lna_conf;
792 
793 	ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
794 
795 	if (common->antenna_diversity)
796 		ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
797 }
798