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 /*
20  * AR9285
21  * ======
22  *
23  * EEPROM has 2 4-bit fields containing the card configuration.
24  *
25  * antdiv_ctl1:
26  * ------------
27  * bb_enable_ant_div_lnadiv : 1
28  * bb_ant_div_alt_gaintb    : 1
29  * bb_ant_div_main_gaintb   : 1
30  * bb_enable_ant_fast_div   : 1
31  *
32  * antdiv_ctl2:
33  * -----------
34  * bb_ant_div_alt_lnaconf  : 2
35  * bb_ant_div_main_lnaconf : 2
36  *
37  * The EEPROM bits are used as follows:
38  * ------------------------------------
39  *
40  * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
41  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
42  *
43  * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
44  *                                 1 -> Antenna config Alt/Main uses gaintable 1
45  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
46  *
47  * bb_enable_ant_fast_div        - Enable fast antenna diversity.
48  *                                 Set in AR_PHY_CCK_DETECT.
49  *
50  * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
51  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
52  *                                 10=LNA1
53  *                                 01=LNA2
54  *                                 11=LNA1+LNA2
55  *                                 00=LNA1-LNA2
56  *
57  * AR9485 / AR9565 / AR9331
58  * ========================
59  *
60  * The same bits are present in the EEPROM, but the location in the
61  * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
62  *
63  * ant_div_alt_lnaconf      ==> bit 0~1
64  * ant_div_main_lnaconf     ==> bit 2~3
65  * ant_div_alt_gaintb       ==> bit 4
66  * ant_div_main_gaintb      ==> bit 5
67  * enable_ant_div_lnadiv    ==> bit 6
68  * enable_ant_fast_div      ==> bit 7
69  */
70 
71 static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
72 					       int alt_ratio, int maxdelta,
73 					       int mindelta, int main_rssi_avg,
74 					       int alt_rssi_avg, int pkt_count)
75 {
76 	if (pkt_count <= 50)
77 		return false;
78 
79 	if (alt_rssi_avg > main_rssi_avg + mindelta)
80 		return true;
81 
82 	if (alt_ratio >= antcomb->ant_ratio2 &&
83 	    alt_rssi_avg >= antcomb->low_rssi_thresh &&
84 	    (alt_rssi_avg > main_rssi_avg + maxdelta))
85 		return true;
86 
87 	return false;
88 }
89 
90 static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
91 					      struct ath_ant_comb *antcomb,
92 					      int alt_ratio, int alt_rssi_avg,
93 					      int main_rssi_avg)
94 {
95 	bool result, set1, set2;
96 
97 	result = set1 = set2 = false;
98 
99 	if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
100 	    conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
101 		set1 = true;
102 
103 	if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
104 	    conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
105 		set2 = true;
106 
107 	switch (conf->div_group) {
108 	case 0:
109 		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
110 			result = true;
111 		break;
112 	case 1:
113 	case 2:
114 		if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
115 			break;
116 
117 		if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
118 		    (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
119 		    (alt_ratio > antcomb->ant_ratio))
120 			result = true;
121 
122 		break;
123 	case 3:
124 		if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
125 			break;
126 
127 		if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
128 		    (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
129 		    (alt_ratio > antcomb->ant_ratio))
130 			result = true;
131 
132 		break;
133 	}
134 
135 	return result;
136 }
137 
138 static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
139 				      struct ath_hw_antcomb_conf ant_conf,
140 				      int main_rssi_avg)
141 {
142 	antcomb->quick_scan_cnt = 0;
143 
144 	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
145 		antcomb->rssi_lna2 = main_rssi_avg;
146 	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
147 		antcomb->rssi_lna1 = main_rssi_avg;
148 
149 	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
150 	case 0x10: /* LNA2 A-B */
151 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
152 		antcomb->first_quick_scan_conf =
153 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
154 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
155 		break;
156 	case 0x20: /* LNA1 A-B */
157 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
158 		antcomb->first_quick_scan_conf =
159 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
160 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
161 		break;
162 	case 0x21: /* LNA1 LNA2 */
163 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
164 		antcomb->first_quick_scan_conf =
165 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
166 		antcomb->second_quick_scan_conf =
167 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
168 		break;
169 	case 0x12: /* LNA2 LNA1 */
170 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
171 		antcomb->first_quick_scan_conf =
172 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
173 		antcomb->second_quick_scan_conf =
174 			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
175 		break;
176 	case 0x13: /* LNA2 A+B */
177 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
178 		antcomb->first_quick_scan_conf =
179 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
180 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
181 		break;
182 	case 0x23: /* LNA1 A+B */
183 		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
184 		antcomb->first_quick_scan_conf =
185 			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
186 		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
187 		break;
188 	default:
189 		break;
190 	}
191 }
192 
193 static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
194 				  struct ath_hw_antcomb_conf *conf)
195 {
196 	/* set alt to the conf with maximun ratio */
197 	if (antcomb->first_ratio && antcomb->second_ratio) {
198 		if (antcomb->rssi_second > antcomb->rssi_third) {
199 			/* first alt*/
200 			if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
201 			    (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
202 				/* Set alt LNA1 or LNA2*/
203 				if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
204 					conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
205 				else
206 					conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
207 			else
208 				/* Set alt to A+B or A-B */
209 				conf->alt_lna_conf =
210 					antcomb->first_quick_scan_conf;
211 		} else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
212 			   (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
213 			/* Set alt LNA1 or LNA2 */
214 			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
215 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
216 			else
217 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
218 		} else {
219 			/* Set alt to A+B or A-B */
220 			conf->alt_lna_conf = antcomb->second_quick_scan_conf;
221 		}
222 	} else if (antcomb->first_ratio) {
223 		/* first alt */
224 		if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
225 		    (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
226 			/* Set alt LNA1 or LNA2 */
227 			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
228 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
229 			else
230 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
231 		else
232 			/* Set alt to A+B or A-B */
233 			conf->alt_lna_conf = antcomb->first_quick_scan_conf;
234 	} else if (antcomb->second_ratio) {
235 		/* second alt */
236 		if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
237 		    (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
238 			/* Set alt LNA1 or LNA2 */
239 			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
240 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
241 			else
242 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
243 		else
244 			/* Set alt to A+B or A-B */
245 			conf->alt_lna_conf = antcomb->second_quick_scan_conf;
246 	} else {
247 		/* main is largest */
248 		if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
249 		    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
250 			/* Set alt LNA1 or LNA2 */
251 			if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
252 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
253 			else
254 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
255 		else
256 			/* Set alt to A+B or A-B */
257 			conf->alt_lna_conf = antcomb->main_conf;
258 	}
259 }
260 
261 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
262 				       struct ath_hw_antcomb_conf *div_ant_conf,
263 				       int main_rssi_avg, int alt_rssi_avg,
264 				       int alt_ratio)
265 {
266 	/* alt_good */
267 	switch (antcomb->quick_scan_cnt) {
268 	case 0:
269 		/* set alt to main, and alt to first conf */
270 		div_ant_conf->main_lna_conf = antcomb->main_conf;
271 		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
272 		break;
273 	case 1:
274 		/* set alt to main, and alt to first conf */
275 		div_ant_conf->main_lna_conf = antcomb->main_conf;
276 		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
277 		antcomb->rssi_first = main_rssi_avg;
278 		antcomb->rssi_second = alt_rssi_avg;
279 
280 		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
281 			/* main is LNA1 */
282 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
283 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
284 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
285 						main_rssi_avg, alt_rssi_avg,
286 						antcomb->total_pkt_count))
287 				antcomb->first_ratio = true;
288 			else
289 				antcomb->first_ratio = false;
290 		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
291 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
292 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
293 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
294 						main_rssi_avg, alt_rssi_avg,
295 						antcomb->total_pkt_count))
296 				antcomb->first_ratio = true;
297 			else
298 				antcomb->first_ratio = false;
299 		} else {
300 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
301 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
302 						0,
303 						main_rssi_avg, alt_rssi_avg,
304 						antcomb->total_pkt_count))
305 				antcomb->first_ratio = true;
306 			else
307 				antcomb->first_ratio = false;
308 		}
309 		break;
310 	case 2:
311 		antcomb->alt_good = false;
312 		antcomb->scan_not_start = false;
313 		antcomb->scan = false;
314 		antcomb->rssi_first = main_rssi_avg;
315 		antcomb->rssi_third = alt_rssi_avg;
316 
317 		switch(antcomb->second_quick_scan_conf) {
318 		case ATH_ANT_DIV_COMB_LNA1:
319 			antcomb->rssi_lna1 = alt_rssi_avg;
320 			break;
321 		case ATH_ANT_DIV_COMB_LNA2:
322 			antcomb->rssi_lna2 = alt_rssi_avg;
323 			break;
324 		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
325 			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
326 				antcomb->rssi_lna2 = main_rssi_avg;
327 			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
328 				antcomb->rssi_lna1 = main_rssi_avg;
329 			break;
330 		default:
331 			break;
332 		}
333 
334 		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
335 		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
336 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
337 		else
338 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
339 
340 		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
341 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
342 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
343 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
344 						main_rssi_avg, alt_rssi_avg,
345 						antcomb->total_pkt_count))
346 				antcomb->second_ratio = true;
347 			else
348 				antcomb->second_ratio = false;
349 		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
350 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
351 						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
352 						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
353 						main_rssi_avg, alt_rssi_avg,
354 						antcomb->total_pkt_count))
355 				antcomb->second_ratio = true;
356 			else
357 				antcomb->second_ratio = false;
358 		} else {
359 			if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
360 						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
361 						0,
362 						main_rssi_avg, alt_rssi_avg,
363 						antcomb->total_pkt_count))
364 				antcomb->second_ratio = true;
365 			else
366 				antcomb->second_ratio = false;
367 		}
368 
369 		ath_ant_set_alt_ratio(antcomb, div_ant_conf);
370 
371 		break;
372 	default:
373 		break;
374 	}
375 }
376 
377 static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
378 					  struct ath_ant_comb *antcomb,
379 					  int alt_ratio)
380 {
381 	ant_conf->main_gaintb = 0;
382 	ant_conf->alt_gaintb = 0;
383 
384 	if (ant_conf->div_group == 0) {
385 		/* Adjust the fast_div_bias based on main and alt lna conf */
386 		switch ((ant_conf->main_lna_conf << 4) |
387 				ant_conf->alt_lna_conf) {
388 		case 0x01: /* A-B LNA2 */
389 			ant_conf->fast_div_bias = 0x3b;
390 			break;
391 		case 0x02: /* A-B LNA1 */
392 			ant_conf->fast_div_bias = 0x3d;
393 			break;
394 		case 0x03: /* A-B A+B */
395 			ant_conf->fast_div_bias = 0x1;
396 			break;
397 		case 0x10: /* LNA2 A-B */
398 			ant_conf->fast_div_bias = 0x7;
399 			break;
400 		case 0x12: /* LNA2 LNA1 */
401 			ant_conf->fast_div_bias = 0x2;
402 			break;
403 		case 0x13: /* LNA2 A+B */
404 			ant_conf->fast_div_bias = 0x7;
405 			break;
406 		case 0x20: /* LNA1 A-B */
407 			ant_conf->fast_div_bias = 0x6;
408 			break;
409 		case 0x21: /* LNA1 LNA2 */
410 			ant_conf->fast_div_bias = 0x0;
411 			break;
412 		case 0x23: /* LNA1 A+B */
413 			ant_conf->fast_div_bias = 0x6;
414 			break;
415 		case 0x30: /* A+B A-B */
416 			ant_conf->fast_div_bias = 0x1;
417 			break;
418 		case 0x31: /* A+B LNA2 */
419 			ant_conf->fast_div_bias = 0x3b;
420 			break;
421 		case 0x32: /* A+B LNA1 */
422 			ant_conf->fast_div_bias = 0x3d;
423 			break;
424 		default:
425 			break;
426 		}
427 	} else if (ant_conf->div_group == 1) {
428 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
429 		switch ((ant_conf->main_lna_conf << 4) |
430 			ant_conf->alt_lna_conf) {
431 		case 0x01: /* A-B LNA2 */
432 			ant_conf->fast_div_bias = 0x1;
433 			break;
434 		case 0x02: /* A-B LNA1 */
435 			ant_conf->fast_div_bias = 0x1;
436 			break;
437 		case 0x03: /* A-B A+B */
438 			ant_conf->fast_div_bias = 0x1;
439 			break;
440 		case 0x10: /* LNA2 A-B */
441 			if (!(antcomb->scan) &&
442 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
443 				ant_conf->fast_div_bias = 0x3f;
444 			else
445 				ant_conf->fast_div_bias = 0x1;
446 			break;
447 		case 0x12: /* LNA2 LNA1 */
448 			ant_conf->fast_div_bias = 0x1;
449 			break;
450 		case 0x13: /* LNA2 A+B */
451 			if (!(antcomb->scan) &&
452 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
453 				ant_conf->fast_div_bias = 0x3f;
454 			else
455 				ant_conf->fast_div_bias = 0x1;
456 			break;
457 		case 0x20: /* LNA1 A-B */
458 			if (!(antcomb->scan) &&
459 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
460 				ant_conf->fast_div_bias = 0x3f;
461 			else
462 				ant_conf->fast_div_bias = 0x1;
463 			break;
464 		case 0x21: /* LNA1 LNA2 */
465 			ant_conf->fast_div_bias = 0x1;
466 			break;
467 		case 0x23: /* LNA1 A+B */
468 			if (!(antcomb->scan) &&
469 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
470 				ant_conf->fast_div_bias = 0x3f;
471 			else
472 				ant_conf->fast_div_bias = 0x1;
473 			break;
474 		case 0x30: /* A+B A-B */
475 			ant_conf->fast_div_bias = 0x1;
476 			break;
477 		case 0x31: /* A+B LNA2 */
478 			ant_conf->fast_div_bias = 0x1;
479 			break;
480 		case 0x32: /* A+B LNA1 */
481 			ant_conf->fast_div_bias = 0x1;
482 			break;
483 		default:
484 			break;
485 		}
486 	} else if (ant_conf->div_group == 2) {
487 		/* Adjust the fast_div_bias based on main and alt_lna_conf */
488 		switch ((ant_conf->main_lna_conf << 4) |
489 				ant_conf->alt_lna_conf) {
490 		case 0x01: /* A-B LNA2 */
491 			ant_conf->fast_div_bias = 0x1;
492 			break;
493 		case 0x02: /* A-B LNA1 */
494 			ant_conf->fast_div_bias = 0x1;
495 			break;
496 		case 0x03: /* A-B A+B */
497 			ant_conf->fast_div_bias = 0x1;
498 			break;
499 		case 0x10: /* LNA2 A-B */
500 			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
501 				ant_conf->fast_div_bias = 0x1;
502 			else
503 				ant_conf->fast_div_bias = 0x2;
504 			break;
505 		case 0x12: /* LNA2 LNA1 */
506 			ant_conf->fast_div_bias = 0x1;
507 			break;
508 		case 0x13: /* LNA2 A+B */
509 			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
510 				ant_conf->fast_div_bias = 0x1;
511 			else
512 				ant_conf->fast_div_bias = 0x2;
513 			break;
514 		case 0x20: /* LNA1 A-B */
515 			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
516 				ant_conf->fast_div_bias = 0x1;
517 			else
518 				ant_conf->fast_div_bias = 0x2;
519 			break;
520 		case 0x21: /* LNA1 LNA2 */
521 			ant_conf->fast_div_bias = 0x1;
522 			break;
523 		case 0x23: /* LNA1 A+B */
524 			if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
525 				ant_conf->fast_div_bias = 0x1;
526 			else
527 				ant_conf->fast_div_bias = 0x2;
528 			break;
529 		case 0x30: /* A+B A-B */
530 			ant_conf->fast_div_bias = 0x1;
531 			break;
532 		case 0x31: /* A+B LNA2 */
533 			ant_conf->fast_div_bias = 0x1;
534 			break;
535 		case 0x32: /* A+B LNA1 */
536 			ant_conf->fast_div_bias = 0x1;
537 			break;
538 		default:
539 			break;
540 		}
541 
542 		if (antcomb->fast_div_bias)
543 			ant_conf->fast_div_bias = antcomb->fast_div_bias;
544 	} else if (ant_conf->div_group == 3) {
545 		switch ((ant_conf->main_lna_conf << 4) |
546 			ant_conf->alt_lna_conf) {
547 		case 0x01: /* A-B LNA2 */
548 			ant_conf->fast_div_bias = 0x1;
549 			break;
550 		case 0x02: /* A-B LNA1 */
551 			ant_conf->fast_div_bias = 0x39;
552 			break;
553 		case 0x03: /* A-B A+B */
554 			ant_conf->fast_div_bias = 0x1;
555 			break;
556 		case 0x10: /* LNA2 A-B */
557 			if ((antcomb->scan == 0) &&
558 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
559 				ant_conf->fast_div_bias = 0x3f;
560 			} else {
561 				ant_conf->fast_div_bias = 0x1;
562 			}
563 			break;
564 		case 0x12: /* LNA2 LNA1 */
565 			ant_conf->fast_div_bias = 0x39;
566 			break;
567 		case 0x13: /* LNA2 A+B */
568 			if ((antcomb->scan == 0) &&
569 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
570 				ant_conf->fast_div_bias = 0x3f;
571 			} else {
572 				ant_conf->fast_div_bias = 0x1;
573 			}
574 			break;
575 		case 0x20: /* LNA1 A-B */
576 			if ((antcomb->scan == 0) &&
577 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
578 				ant_conf->fast_div_bias = 0x3f;
579 			} else {
580 				ant_conf->fast_div_bias = 0x4;
581 			}
582 			break;
583 		case 0x21: /* LNA1 LNA2 */
584 			ant_conf->fast_div_bias = 0x6;
585 			break;
586 		case 0x23: /* LNA1 A+B */
587 			if ((antcomb->scan == 0) &&
588 			    (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
589 				ant_conf->fast_div_bias = 0x3f;
590 			} else {
591 				ant_conf->fast_div_bias = 0x6;
592 			}
593 			break;
594 		case 0x30: /* A+B A-B */
595 			ant_conf->fast_div_bias = 0x1;
596 			break;
597 		case 0x31: /* A+B LNA2 */
598 			ant_conf->fast_div_bias = 0x6;
599 			break;
600 		case 0x32: /* A+B LNA1 */
601 			ant_conf->fast_div_bias = 0x1;
602 			break;
603 		default:
604 			break;
605 		}
606 	}
607 }
608 
609 static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
610 			     struct ath_hw_antcomb_conf *conf,
611 			     int curr_alt_set, int alt_rssi_avg,
612 			     int main_rssi_avg)
613 {
614 	switch (curr_alt_set) {
615 	case ATH_ANT_DIV_COMB_LNA2:
616 		antcomb->rssi_lna2 = alt_rssi_avg;
617 		antcomb->rssi_lna1 = main_rssi_avg;
618 		antcomb->scan = true;
619 		/* set to A+B */
620 		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
621 		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
622 		break;
623 	case ATH_ANT_DIV_COMB_LNA1:
624 		antcomb->rssi_lna1 = alt_rssi_avg;
625 		antcomb->rssi_lna2 = main_rssi_avg;
626 		antcomb->scan = true;
627 		/* set to A+B */
628 		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
629 		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
630 		break;
631 	case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
632 		antcomb->rssi_add = alt_rssi_avg;
633 		antcomb->scan = true;
634 		/* set to A-B */
635 		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
636 		break;
637 	case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
638 		antcomb->rssi_sub = alt_rssi_avg;
639 		antcomb->scan = false;
640 		if (antcomb->rssi_lna2 >
641 		    (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
642 			/* use LNA2 as main LNA */
643 			if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
644 			    (antcomb->rssi_add > antcomb->rssi_sub)) {
645 				/* set to A+B */
646 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
647 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
648 			} else if (antcomb->rssi_sub >
649 				   antcomb->rssi_lna1) {
650 				/* set to A-B */
651 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
652 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
653 			} else {
654 				/* set to LNA1 */
655 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
656 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
657 			}
658 		} else {
659 			/* use LNA1 as main LNA */
660 			if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
661 			    (antcomb->rssi_add > antcomb->rssi_sub)) {
662 				/* set to A+B */
663 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
664 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
665 			} else if (antcomb->rssi_sub >
666 				   antcomb->rssi_lna1) {
667 				/* set to A-B */
668 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
669 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
670 			} else {
671 				/* set to LNA2 */
672 				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
673 				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
674 			}
675 		}
676 		break;
677 	default:
678 		break;
679 	}
680 }
681 
682 static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
683 			       struct ath_ant_comb *antcomb,
684 			       int alt_ratio, int alt_rssi_avg,
685 			       int main_rssi_avg, int curr_main_set,
686 			       int curr_alt_set)
687 {
688 	bool ret = false;
689 
690 	if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
691 				       alt_rssi_avg, main_rssi_avg)) {
692 		if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
693 			/*
694 			 * Switch main and alt LNA.
695 			 */
696 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
697 			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
698 		} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
699 			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
700 			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
701 		}
702 
703 		ret = true;
704 	} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
705 		   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
706 		/*
707 		  Set alt to another LNA.
708 		*/
709 		if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
710 			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
711 		else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
712 			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
713 
714 		ret = true;
715 	}
716 
717 	return ret;
718 }
719 
720 static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
721 {
722 	int alt_ratio;
723 
724 	if (!antcomb->scan || !antcomb->alt_good)
725 		return false;
726 
727 	if (time_after(jiffies, antcomb->scan_start_time +
728 		       msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
729 		return true;
730 
731 	if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
732 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
733 			     antcomb->total_pkt_count);
734 		if (alt_ratio < antcomb->ant_ratio)
735 			return true;
736 	}
737 
738 	return false;
739 }
740 
741 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
742 {
743 	struct ath_hw_antcomb_conf div_ant_conf;
744 	struct ath_ant_comb *antcomb = &sc->ant_comb;
745 	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
746 	int curr_main_set;
747 	int main_rssi = rs->rs_rssi_ctl0;
748 	int alt_rssi = rs->rs_rssi_ctl1;
749 	int rx_ant_conf,  main_ant_conf;
750 	bool short_scan = false, ret;
751 
752 	rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
753 		       ATH_ANT_RX_MASK;
754 	main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
755 			 ATH_ANT_RX_MASK;
756 
757 	if (alt_rssi >= antcomb->low_rssi_thresh) {
758 		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
759 		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
760 	} else {
761 		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
762 		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
763 	}
764 
765 	/* Record packet only when both main_rssi and  alt_rssi is positive */
766 	if (main_rssi > 0 && alt_rssi > 0) {
767 		antcomb->total_pkt_count++;
768 		antcomb->main_total_rssi += main_rssi;
769 		antcomb->alt_total_rssi  += alt_rssi;
770 
771 		if (main_ant_conf == rx_ant_conf)
772 			antcomb->main_recv_cnt++;
773 		else
774 			antcomb->alt_recv_cnt++;
775 	}
776 
777 	if (main_ant_conf == rx_ant_conf) {
778 		ANT_STAT_INC(ANT_MAIN, recv_cnt);
779 		ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
780 	} else {
781 		ANT_STAT_INC(ANT_ALT, recv_cnt);
782 		ANT_LNA_INC(ANT_ALT, rx_ant_conf);
783 	}
784 
785 	/* Short scan check */
786 	short_scan = ath_ant_short_scan_check(antcomb);
787 
788 	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
789 	     rs->rs_moreaggr) && !short_scan)
790 		return;
791 
792 	if (antcomb->total_pkt_count) {
793 		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
794 			     antcomb->total_pkt_count);
795 		main_rssi_avg = (antcomb->main_total_rssi /
796 				 antcomb->total_pkt_count);
797 		alt_rssi_avg = (antcomb->alt_total_rssi /
798 				 antcomb->total_pkt_count);
799 	}
800 
801 	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
802 	curr_alt_set = div_ant_conf.alt_lna_conf;
803 	curr_main_set = div_ant_conf.main_lna_conf;
804 	antcomb->count++;
805 
806 	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
807 		if (alt_ratio > antcomb->ant_ratio) {
808 			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
809 						  main_rssi_avg);
810 			antcomb->alt_good = true;
811 		} else {
812 			antcomb->alt_good = false;
813 		}
814 
815 		antcomb->count = 0;
816 		antcomb->scan = true;
817 		antcomb->scan_not_start = true;
818 	}
819 
820 	if (!antcomb->scan) {
821 		ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
822 					 alt_rssi_avg, main_rssi_avg,
823 					 curr_main_set, curr_alt_set);
824 		if (ret)
825 			goto div_comb_done;
826 	}
827 
828 	if (!antcomb->scan &&
829 	    (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
830 		goto div_comb_done;
831 
832 	if (!antcomb->scan_not_start) {
833 		ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
834 				 alt_rssi_avg, main_rssi_avg);
835 	} else {
836 		if (!antcomb->alt_good) {
837 			antcomb->scan_not_start = false;
838 			/* Set alt to another LNA */
839 			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
840 				div_ant_conf.main_lna_conf =
841 					ATH_ANT_DIV_COMB_LNA2;
842 				div_ant_conf.alt_lna_conf =
843 					ATH_ANT_DIV_COMB_LNA1;
844 			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
845 				div_ant_conf.main_lna_conf =
846 					ATH_ANT_DIV_COMB_LNA1;
847 				div_ant_conf.alt_lna_conf =
848 					ATH_ANT_DIV_COMB_LNA2;
849 			}
850 			goto div_comb_done;
851 		}
852 		ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
853 						   main_rssi_avg, alt_rssi_avg,
854 						   alt_ratio);
855 		antcomb->quick_scan_cnt++;
856 	}
857 
858 div_comb_done:
859 	ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
860 	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
861 	ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
862 
863 	antcomb->scan_start_time = jiffies;
864 	antcomb->total_pkt_count = 0;
865 	antcomb->main_total_rssi = 0;
866 	antcomb->alt_total_rssi = 0;
867 	antcomb->main_recv_cnt = 0;
868 	antcomb->alt_recv_cnt = 0;
869 }
870