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
71static 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
90static 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
138static 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
193static 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
261static 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		    div_ant_conf->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
377static 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			ant_conf->fast_div_bias = 0x2;
558			break;
559		case 0x12: /* LNA2 LNA1 */
560			ant_conf->fast_div_bias = 0x3f;
561			break;
562		case 0x13: /* LNA2 A+B */
563			ant_conf->fast_div_bias = 0x2;
564			break;
565		case 0x20: /* LNA1 A-B */
566			ant_conf->fast_div_bias = 0x3;
567			break;
568		case 0x21: /* LNA1 LNA2 */
569			ant_conf->fast_div_bias = 0x3;
570			break;
571		case 0x23: /* LNA1 A+B */
572			ant_conf->fast_div_bias = 0x3;
573			break;
574		case 0x30: /* A+B A-B */
575			ant_conf->fast_div_bias = 0x1;
576			break;
577		case 0x31: /* A+B LNA2 */
578			ant_conf->fast_div_bias = 0x6;
579			break;
580		case 0x32: /* A+B LNA1 */
581			ant_conf->fast_div_bias = 0x1;
582			break;
583		default:
584			break;
585		}
586	}
587}
588
589static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
590			     struct ath_hw_antcomb_conf *conf,
591			     int curr_alt_set, int alt_rssi_avg,
592			     int main_rssi_avg)
593{
594	switch (curr_alt_set) {
595	case ATH_ANT_DIV_COMB_LNA2:
596		antcomb->rssi_lna2 = alt_rssi_avg;
597		antcomb->rssi_lna1 = main_rssi_avg;
598		antcomb->scan = true;
599		/* set to A+B */
600		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
601		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
602		break;
603	case ATH_ANT_DIV_COMB_LNA1:
604		antcomb->rssi_lna1 = alt_rssi_avg;
605		antcomb->rssi_lna2 = main_rssi_avg;
606		antcomb->scan = true;
607		/* set to A+B */
608		conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
609		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
610		break;
611	case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
612		antcomb->rssi_add = alt_rssi_avg;
613		antcomb->scan = true;
614		/* set to A-B */
615		conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
616		break;
617	case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
618		antcomb->rssi_sub = alt_rssi_avg;
619		antcomb->scan = false;
620		if (antcomb->rssi_lna2 >
621		    (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) {
622			/* use LNA2 as main LNA */
623			if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
624			    (antcomb->rssi_add > antcomb->rssi_sub)) {
625				/* set to A+B */
626				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
627				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
628			} else if (antcomb->rssi_sub >
629				   antcomb->rssi_lna1) {
630				/* set to A-B */
631				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
632				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
633			} else {
634				/* set to LNA1 */
635				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
636				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
637			}
638		} else {
639			/* use LNA1 as main LNA */
640			if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
641			    (antcomb->rssi_add > antcomb->rssi_sub)) {
642				/* set to A+B */
643				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
644				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
645			} else if (antcomb->rssi_sub >
646				   antcomb->rssi_lna1) {
647				/* set to A-B */
648				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
649				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
650			} else {
651				/* set to LNA2 */
652				conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
653				conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
654			}
655		}
656		break;
657	default:
658		break;
659	}
660}
661
662static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
663			       struct ath_ant_comb *antcomb,
664			       int alt_ratio, int alt_rssi_avg,
665			       int main_rssi_avg, int curr_main_set,
666			       int curr_alt_set)
667{
668	bool ret = false;
669
670	if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
671				       alt_rssi_avg, main_rssi_avg)) {
672		if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
673			/*
674			 * Switch main and alt LNA.
675			 */
676			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
677			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
678		} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
679			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
680			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
681		}
682
683		ret = true;
684	} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
685		   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
686		/*
687		  Set alt to another LNA.
688		*/
689		if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
690			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
691		else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
692			div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
693
694		ret = true;
695	}
696
697	return ret;
698}
699
700static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
701{
702	int alt_ratio;
703
704	if (!antcomb->scan || !antcomb->alt_good)
705		return false;
706
707	if (time_after(jiffies, antcomb->scan_start_time +
708		       msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
709		return true;
710
711	if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
712		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
713			     antcomb->total_pkt_count);
714		if (alt_ratio < antcomb->ant_ratio)
715			return true;
716	}
717
718	return false;
719}
720
721void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
722{
723	struct ath_hw_antcomb_conf div_ant_conf;
724	struct ath_ant_comb *antcomb = &sc->ant_comb;
725	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
726	int curr_main_set;
727	int main_rssi = rs->rs_rssi_ctl[0];
728	int alt_rssi = rs->rs_rssi_ctl[1];
729	int rx_ant_conf,  main_ant_conf;
730	bool short_scan = false, ret;
731
732	rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
733		       ATH_ANT_RX_MASK;
734	main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
735			 ATH_ANT_RX_MASK;
736
737	if (alt_rssi >= antcomb->low_rssi_thresh) {
738		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
739		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
740	} else {
741		antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
742		antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
743	}
744
745	/* Record packet only when both main_rssi and  alt_rssi is positive */
746	if (main_rssi > 0 && alt_rssi > 0) {
747		antcomb->total_pkt_count++;
748		antcomb->main_total_rssi += main_rssi;
749		antcomb->alt_total_rssi  += alt_rssi;
750
751		if (main_ant_conf == rx_ant_conf)
752			antcomb->main_recv_cnt++;
753		else
754			antcomb->alt_recv_cnt++;
755	}
756
757	if (main_ant_conf == rx_ant_conf) {
758		ANT_STAT_INC(ANT_MAIN, recv_cnt);
759		ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
760	} else {
761		ANT_STAT_INC(ANT_ALT, recv_cnt);
762		ANT_LNA_INC(ANT_ALT, rx_ant_conf);
763	}
764
765	/* Short scan check */
766	short_scan = ath_ant_short_scan_check(antcomb);
767
768	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
769	     rs->rs_moreaggr) && !short_scan)
770		return;
771
772	if (antcomb->total_pkt_count) {
773		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
774			     antcomb->total_pkt_count);
775		main_rssi_avg = (antcomb->main_total_rssi /
776				 antcomb->total_pkt_count);
777		alt_rssi_avg = (antcomb->alt_total_rssi /
778				 antcomb->total_pkt_count);
779	}
780
781	ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
782	curr_alt_set = div_ant_conf.alt_lna_conf;
783	curr_main_set = div_ant_conf.main_lna_conf;
784	antcomb->count++;
785
786	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
787		if (alt_ratio > antcomb->ant_ratio) {
788			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
789						  main_rssi_avg);
790			antcomb->alt_good = true;
791		} else {
792			antcomb->alt_good = false;
793		}
794
795		antcomb->count = 0;
796		antcomb->scan = true;
797		antcomb->scan_not_start = true;
798	}
799
800	if (!antcomb->scan) {
801		ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
802					 alt_rssi_avg, main_rssi_avg,
803					 curr_main_set, curr_alt_set);
804		if (ret)
805			goto div_comb_done;
806	}
807
808	if (!antcomb->scan &&
809	    (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
810		goto div_comb_done;
811
812	if (!antcomb->scan_not_start) {
813		ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
814				 alt_rssi_avg, main_rssi_avg);
815	} else {
816		if (!antcomb->alt_good) {
817			antcomb->scan_not_start = false;
818			/* Set alt to another LNA */
819			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
820				div_ant_conf.main_lna_conf =
821					ATH_ANT_DIV_COMB_LNA2;
822				div_ant_conf.alt_lna_conf =
823					ATH_ANT_DIV_COMB_LNA1;
824			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
825				div_ant_conf.main_lna_conf =
826					ATH_ANT_DIV_COMB_LNA1;
827				div_ant_conf.alt_lna_conf =
828					ATH_ANT_DIV_COMB_LNA2;
829			}
830			goto div_comb_done;
831		}
832		ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
833						   main_rssi_avg, alt_rssi_avg,
834						   alt_ratio);
835		antcomb->quick_scan_cnt++;
836	}
837
838div_comb_done:
839	ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
840	ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
841	ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
842
843	antcomb->scan_start_time = jiffies;
844	antcomb->total_pkt_count = 0;
845	antcomb->main_total_rssi = 0;
846	antcomb->alt_total_rssi = 0;
847	antcomb->main_recv_cnt = 0;
848	antcomb->alt_recv_cnt = 0;
849}
850