ar9285_diversity.c revision 221694
1193323Sed/*
2193323Sed * Copyright (c) 2008-2010 Atheros Communications Inc.
3193323Sed * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
4193323Sed *
5193323Sed * Redistribution and use in source and binary forms, with or without
6193323Sed * modification, are permitted provided that the following conditions
7193323Sed * are met:
8193323Sed * 1. Redistributions of source code must retain the above copyright
9193323Sed *    notice, this list of conditions and the following disclaimer.
10193323Sed * 2. Redistributions in binary form must reproduce the above copyright
11193323Sed *    notice, this list of conditions and the following disclaimer in the
12193323Sed *    documentation and/or other materials provided with the distribution.
13193323Sed *
14193323Sed * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15193323Sed * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16193323Sed * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17193323Sed * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18193323Sed * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19193323Sed * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20193323Sed * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21193323Sed * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22193323Sed * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23193323Sed * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24193323Sed * SUCH DAMAGE.
25193323Sed *
26193323Sed * $FreeBSD: head/sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c 221694 2011-05-09 15:19:49Z adrian $
27193323Sed */
28193323Sed#include "opt_ah.h"
29193323Sed
30193323Sed#include "ah.h"
31193323Sed#include "ah_desc.h"
32193323Sed#include "ah_internal.h"
33193323Sed#include "ah_eeprom_v4k.h"
34193323Sed
35193323Sed#include "ar9002/ar9280.h"
36193323Sed#include "ar9002/ar9285_diversity.h"
37193323Sed#include "ar9002/ar9285.h"
38193323Sed#include "ar5416/ar5416reg.h"
39193323Sed#include "ar5416/ar5416phy.h"
40193323Sed#include "ar9002/ar9285phy.h"
41193323Sed#include "ar9002/ar9285_phy.h"
42193323Sed
43193323Sed
44193323Sed/* Linux compability macros */
45193323Sed/*
46193323Sed * XXX these don't handle rounding, underflow, overflow, wrapping!
47193323Sed */
48193323Sed#define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
49193323Sed#define	time_after(a, b)		( (long) (b) - (long) (a) < 0 )
50193323Sed
51193323Sedstatic HAL_BOOL
52193323Sedath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
53193323Sed    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
54193323Sed{
55193323Sed	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
56193323Sed		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
57193323Sed		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
58193323Sed}
59194710Sed
60194710Sedstatic void
61194710Sedath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb,
62194710Sed    struct ar9285_antcomb_conf ant_conf, int main_rssi_avg)
63194710Sed{
64194710Sed	antcomb->quick_scan_cnt = 0;
65194710Sed
66194710Sed	if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
67194710Sed		antcomb->rssi_lna2 = main_rssi_avg;
68194710Sed	else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
69194710Sed		antcomb->rssi_lna1 = main_rssi_avg;
70194710Sed
71194710Sed	switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
72194710Sed	case (0x10): /* LNA2 A-B */
73194710Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
74194710Sed		antcomb->first_quick_scan_conf =
75194710Sed			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
76194710Sed		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
77194710Sed		break;
78194710Sed	case (0x20): /* LNA1 A-B */
79194710Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
80194710Sed		antcomb->first_quick_scan_conf =
81194710Sed			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
82194710Sed		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
83194710Sed		break;
84194710Sed	case (0x21): /* LNA1 LNA2 */
85194710Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
86194710Sed		antcomb->first_quick_scan_conf =
87194710Sed			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
88194710Sed		antcomb->second_quick_scan_conf =
89194710Sed			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
90194710Sed		break;
91194710Sed	case (0x12): /* LNA2 LNA1 */
92194710Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
93194710Sed		antcomb->first_quick_scan_conf =
94194710Sed			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
95194710Sed		antcomb->second_quick_scan_conf =
96194710Sed			ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
97194710Sed		break;
98194710Sed	case (0x13): /* LNA2 A+B */
99194710Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
100194710Sed		antcomb->first_quick_scan_conf =
101194710Sed			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
102194710Sed		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
103194710Sed		break;
104194710Sed	case (0x23): /* LNA1 A+B */
105193323Sed		antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
106193323Sed		antcomb->first_quick_scan_conf =
107193323Sed			ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
108193323Sed		antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
109193323Sed		break;
110193323Sed	default:
111193323Sed		break;
112193323Sed	}
113193323Sed}
114193323Sed
115193323Sedstatic void
116193323Sedath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb,
117193323Sed    struct ar9285_antcomb_conf *div_ant_conf, int main_rssi_avg,
118193323Sed    int alt_rssi_avg, int alt_ratio)
119193323Sed{
120193323Sed	/* alt_good */
121193323Sed	switch (antcomb->quick_scan_cnt) {
122193323Sed	case 0:
123193323Sed		/* set alt to main, and alt to first conf */
124193323Sed		div_ant_conf->main_lna_conf = antcomb->main_conf;
125193323Sed		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
126193323Sed		break;
127193323Sed	case 1:
128193323Sed		/* set alt to main, and alt to first conf */
129193323Sed		div_ant_conf->main_lna_conf = antcomb->main_conf;
130193323Sed		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
131193323Sed		antcomb->rssi_first = main_rssi_avg;
132193323Sed		antcomb->rssi_second = alt_rssi_avg;
133193323Sed
134193323Sed		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
135193323Sed			/* main is LNA1 */
136193323Sed			if (ath_is_alt_ant_ratio_better(alt_ratio,
137193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
138193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
139193323Sed						main_rssi_avg, alt_rssi_avg,
140193323Sed						antcomb->total_pkt_count))
141193323Sed				antcomb->first_ratio = AH_TRUE;
142193323Sed			else
143193323Sed				antcomb->first_ratio = AH_FALSE;
144193323Sed		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
145193323Sed			if (ath_is_alt_ant_ratio_better(alt_ratio,
146193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
147193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
148193323Sed						main_rssi_avg, alt_rssi_avg,
149193323Sed						antcomb->total_pkt_count))
150193323Sed				antcomb->first_ratio = AH_TRUE;
151193323Sed			else
152193323Sed				antcomb->first_ratio = AH_FALSE;
153193323Sed		} else {
154193323Sed			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
155193323Sed			    (alt_rssi_avg > main_rssi_avg +
156193323Sed			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
157193323Sed			    (alt_rssi_avg > main_rssi_avg)) &&
158193323Sed			    (antcomb->total_pkt_count > 50))
159193323Sed				antcomb->first_ratio = AH_TRUE;
160193323Sed			else
161193323Sed				antcomb->first_ratio = AH_FALSE;
162193323Sed		}
163193323Sed		break;
164193323Sed	case 2:
165193323Sed		antcomb->alt_good = AH_FALSE;
166193323Sed		antcomb->scan_not_start = AH_FALSE;
167193323Sed		antcomb->scan = AH_FALSE;
168193323Sed		antcomb->rssi_first = main_rssi_avg;
169193323Sed		antcomb->rssi_third = alt_rssi_avg;
170193323Sed
171193323Sed		if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
172193323Sed			antcomb->rssi_lna1 = alt_rssi_avg;
173193323Sed		else if (antcomb->second_quick_scan_conf ==
174193323Sed			 ATH_ANT_DIV_COMB_LNA2)
175193323Sed			antcomb->rssi_lna2 = alt_rssi_avg;
176193323Sed		else if (antcomb->second_quick_scan_conf ==
177193323Sed			 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
178193323Sed			if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
179193323Sed				antcomb->rssi_lna2 = main_rssi_avg;
180193323Sed			else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
181193323Sed				antcomb->rssi_lna1 = main_rssi_avg;
182193323Sed		}
183193323Sed
184193323Sed		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
185193323Sed		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
186193323Sed			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
187193323Sed		else
188193323Sed			div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
189193323Sed
190193323Sed		if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
191193323Sed			if (ath_is_alt_ant_ratio_better(alt_ratio,
192193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
193193323Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
194193323Sed						main_rssi_avg, alt_rssi_avg,
195193323Sed						antcomb->total_pkt_count))
196193323Sed				antcomb->second_ratio = AH_TRUE;
197193323Sed			else
198193323Sed				antcomb->second_ratio = AH_FALSE;
199193323Sed		} else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
200193323Sed			if (ath_is_alt_ant_ratio_better(alt_ratio,
201194710Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
202194710Sed						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
203194710Sed						main_rssi_avg, alt_rssi_avg,
204194710Sed						antcomb->total_pkt_count))
205194710Sed				antcomb->second_ratio = AH_TRUE;
206194710Sed			else
207194710Sed				antcomb->second_ratio = AH_FALSE;
208194710Sed		} else {
209194710Sed			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
210194710Sed			    (alt_rssi_avg > main_rssi_avg +
211194710Sed			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
212194710Sed			    (alt_rssi_avg > main_rssi_avg)) &&
213194710Sed			    (antcomb->total_pkt_count > 50))
214194710Sed				antcomb->second_ratio = AH_TRUE;
215194710Sed			else
216194710Sed				antcomb->second_ratio = AH_FALSE;
217194710Sed		}
218194710Sed
219194710Sed		/* set alt to the conf with maximun ratio */
220194710Sed		if (antcomb->first_ratio && antcomb->second_ratio) {
221194710Sed			if (antcomb->rssi_second > antcomb->rssi_third) {
222194710Sed				/* first alt*/
223194710Sed				if ((antcomb->first_quick_scan_conf ==
224194710Sed				    ATH_ANT_DIV_COMB_LNA1) ||
225193323Sed				    (antcomb->first_quick_scan_conf ==
226193323Sed				    ATH_ANT_DIV_COMB_LNA2))
227193323Sed					/* Set alt LNA1 or LNA2*/
228193323Sed					if (div_ant_conf->main_lna_conf ==
229193323Sed					    ATH_ANT_DIV_COMB_LNA2)
230193323Sed						div_ant_conf->alt_lna_conf =
231193323Sed							ATH_ANT_DIV_COMB_LNA1;
232193323Sed					else
233193323Sed						div_ant_conf->alt_lna_conf =
234193323Sed							ATH_ANT_DIV_COMB_LNA2;
235193323Sed				else
236193323Sed					/* Set alt to A+B or A-B */
237193323Sed					div_ant_conf->alt_lna_conf =
238193323Sed						antcomb->first_quick_scan_conf;
239193323Sed			} else if ((antcomb->second_quick_scan_conf ==
240193323Sed				   ATH_ANT_DIV_COMB_LNA1) ||
241193323Sed				   (antcomb->second_quick_scan_conf ==
242193323Sed				   ATH_ANT_DIV_COMB_LNA2)) {
243193323Sed				/* Set alt LNA1 or LNA2 */
244193323Sed				if (div_ant_conf->main_lna_conf ==
245193323Sed				    ATH_ANT_DIV_COMB_LNA2)
246193323Sed					div_ant_conf->alt_lna_conf =
247193323Sed						ATH_ANT_DIV_COMB_LNA1;
248193323Sed				else
249193323Sed					div_ant_conf->alt_lna_conf =
250193323Sed						ATH_ANT_DIV_COMB_LNA2;
251193323Sed			} else {
252193323Sed				/* Set alt to A+B or A-B */
253193323Sed				div_ant_conf->alt_lna_conf =
254193323Sed					antcomb->second_quick_scan_conf;
255193323Sed			}
256193323Sed		} else if (antcomb->first_ratio) {
257193323Sed			/* first alt */
258193323Sed			if ((antcomb->first_quick_scan_conf ==
259193323Sed			    ATH_ANT_DIV_COMB_LNA1) ||
260193323Sed			    (antcomb->first_quick_scan_conf ==
261193323Sed			    ATH_ANT_DIV_COMB_LNA2))
262193323Sed					/* Set alt LNA1 or LNA2 */
263193323Sed				if (div_ant_conf->main_lna_conf ==
264193323Sed				    ATH_ANT_DIV_COMB_LNA2)
265193323Sed					div_ant_conf->alt_lna_conf =
266193323Sed							ATH_ANT_DIV_COMB_LNA1;
267193323Sed				else
268193323Sed					div_ant_conf->alt_lna_conf =
269195098Sed							ATH_ANT_DIV_COMB_LNA2;
270193323Sed			else
271193323Sed				/* Set alt to A+B or A-B */
272193323Sed				div_ant_conf->alt_lna_conf =
273193323Sed						antcomb->first_quick_scan_conf;
274193323Sed		} else if (antcomb->second_ratio) {
275193323Sed				/* second alt */
276193323Sed			if ((antcomb->second_quick_scan_conf ==
277193323Sed			    ATH_ANT_DIV_COMB_LNA1) ||
278193323Sed			    (antcomb->second_quick_scan_conf ==
279193323Sed			    ATH_ANT_DIV_COMB_LNA2))
280193323Sed				/* Set alt LNA1 or LNA2 */
281193323Sed				if (div_ant_conf->main_lna_conf ==
282193323Sed				    ATH_ANT_DIV_COMB_LNA2)
283193323Sed					div_ant_conf->alt_lna_conf =
284193323Sed						ATH_ANT_DIV_COMB_LNA1;
285193323Sed				else
286193323Sed					div_ant_conf->alt_lna_conf =
287193323Sed						ATH_ANT_DIV_COMB_LNA2;
288193323Sed			else
289193323Sed				/* Set alt to A+B or A-B */
290193323Sed				div_ant_conf->alt_lna_conf =
291193323Sed						antcomb->second_quick_scan_conf;
292193323Sed		} else {
293193323Sed			/* main is largest */
294193323Sed			if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
295193323Sed			    (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
296193323Sed				/* Set alt LNA1 or LNA2 */
297193323Sed				if (div_ant_conf->main_lna_conf ==
298193323Sed				    ATH_ANT_DIV_COMB_LNA2)
299193323Sed					div_ant_conf->alt_lna_conf =
300193323Sed							ATH_ANT_DIV_COMB_LNA1;
301193323Sed				else
302193323Sed					div_ant_conf->alt_lna_conf =
303193323Sed							ATH_ANT_DIV_COMB_LNA2;
304193323Sed			else
305193323Sed				/* Set alt to A+B or A-B */
306193323Sed				div_ant_conf->alt_lna_conf = antcomb->main_conf;
307193323Sed		}
308193323Sed		break;
309193323Sed	default:
310193323Sed		break;
311193323Sed	}
312193323Sed}
313193323Sed
314193323Sedstatic void
315193323Sedath_ant_div_conf_fast_divbias(struct ar9285_antcomb_conf *ant_conf)
316193323Sed{
317193323Sed	/* Adjust the fast_div_bias based on main and alt lna conf */
318193323Sed	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
319193323Sed	case (0x01): /* A-B LNA2 */
320193323Sed		ant_conf->fast_div_bias = 0x3b;
321193323Sed		break;
322193323Sed	case (0x02): /* A-B LNA1 */
323193323Sed		ant_conf->fast_div_bias = 0x3d;
324193323Sed		break;
325193323Sed	case (0x03): /* A-B A+B */
326193323Sed		ant_conf->fast_div_bias = 0x1;
327193323Sed		break;
328193323Sed	case (0x10): /* LNA2 A-B */
329193323Sed		ant_conf->fast_div_bias = 0x7;
330193323Sed		break;
331193323Sed	case (0x12): /* LNA2 LNA1 */
332193323Sed		ant_conf->fast_div_bias = 0x2;
333193323Sed		break;
334193323Sed	case (0x13): /* LNA2 A+B */
335193323Sed		ant_conf->fast_div_bias = 0x7;
336193323Sed		break;
337193323Sed	case (0x20): /* LNA1 A-B */
338193323Sed		ant_conf->fast_div_bias = 0x6;
339193323Sed		break;
340193323Sed	case (0x21): /* LNA1 LNA2 */
341193323Sed		ant_conf->fast_div_bias = 0x0;
342193323Sed		break;
343193323Sed	case (0x23): /* LNA1 A+B */
344193323Sed		ant_conf->fast_div_bias = 0x6;
345193323Sed		break;
346193323Sed	case (0x30): /* A+B A-B */
347193323Sed		ant_conf->fast_div_bias = 0x1;
348193323Sed		break;
349193323Sed	case (0x31): /* A+B LNA2 */
350193323Sed		ant_conf->fast_div_bias = 0x3b;
351193323Sed		break;
352193323Sed	case (0x32): /* A+B LNA1 */
353193323Sed		ant_conf->fast_div_bias = 0x3d;
354193323Sed		break;
355193323Sed	default:
356193323Sed		break;
357193323Sed	}
358193323Sed}
359193323Sed
360193323Sed/* Antenna diversity and combining */
361193323Sedvoid
362193323Sedar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
363193323Sed    unsigned long ticks, int hz)
364193323Sed{
365194612Sed	struct ar9285_antcomb_conf div_ant_conf;
366194612Sed	struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb;
367194612Sed	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
368194612Sed	int curr_main_set, curr_bias;
369194612Sed	int main_rssi = rs->rs_rssi_ctl[0];
370194612Sed	int alt_rssi = rs->rs_rssi_ctl[1];
371194612Sed	int rx_ant_conf, main_ant_conf;
372194612Sed	HAL_BOOL short_scan = AH_FALSE;
373194612Sed
374194612Sed	if (! ar9285_check_div_comb(ah))
375194612Sed		return;
376194612Sed
377194612Sed	if (AH5212(ah)->ah_diversity == AH_FALSE)
378194612Sed		return;
379194612Sed
380194612Sed	rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
381194612Sed		       ATH_ANT_RX_MASK;
382194612Sed	main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
383194612Sed			 ATH_ANT_RX_MASK;
384193323Sed
385193323Sed#if 0
386193323Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n",
387193323Sed	    __func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf);
388193323Sed#endif
389193323Sed
390193323Sed	/* Record packet only when alt_rssi is positive */
391193323Sed	if (alt_rssi > 0) {
392193323Sed		antcomb->total_pkt_count++;
393193323Sed		antcomb->main_total_rssi += main_rssi;
394193323Sed		antcomb->alt_total_rssi  += alt_rssi;
395193323Sed		if (main_ant_conf == rx_ant_conf)
396193323Sed			antcomb->main_recv_cnt++;
397193323Sed		else
398193323Sed			antcomb->alt_recv_cnt++;
399193323Sed	}
400193323Sed
401193323Sed	/* Short scan check */
402193323Sed	if (antcomb->scan && antcomb->alt_good) {
403193323Sed		if (time_after(ticks, antcomb->scan_start_time +
404193323Sed		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
405193323Sed			short_scan = AH_TRUE;
406193323Sed		else
407193323Sed			if (antcomb->total_pkt_count ==
408193323Sed			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
409193323Sed				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
410193323Sed					    antcomb->total_pkt_count);
411193323Sed				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
412193323Sed					short_scan = AH_TRUE;
413193323Sed			}
414193323Sed	}
415193323Sed
416193323Sed	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
417193323Sed	    rs->rs_moreaggr) && !short_scan)
418193323Sed		return;
419193323Sed
420193323Sed	if (antcomb->total_pkt_count) {
421193323Sed		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
422193323Sed			     antcomb->total_pkt_count);
423193323Sed		main_rssi_avg = (antcomb->main_total_rssi /
424193323Sed				 antcomb->total_pkt_count);
425194710Sed		alt_rssi_avg = (antcomb->alt_total_rssi /
426194710Sed				 antcomb->total_pkt_count);
427194710Sed	}
428194710Sed
429194710Sed	ar9285_antdiv_comb_conf_get(ah, &div_ant_conf);
430194710Sed	curr_alt_set = div_ant_conf.alt_lna_conf;
431194710Sed	curr_main_set = div_ant_conf.main_lna_conf;
432194710Sed	curr_bias = div_ant_conf.fast_div_bias;
433194710Sed
434194710Sed	antcomb->count++;
435194710Sed
436194710Sed	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
437194710Sed		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
438194710Sed			ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
439194710Sed						  main_rssi_avg);
440194710Sed			antcomb->alt_good = AH_TRUE;
441194710Sed		} else {
442194710Sed			antcomb->alt_good = AH_FALSE;
443194710Sed		}
444194710Sed
445194710Sed		antcomb->count = 0;
446194710Sed		antcomb->scan = AH_TRUE;
447194710Sed		antcomb->scan_not_start = AH_TRUE;
448194710Sed	}
449194710Sed
450194710Sed	if (!antcomb->scan) {
451194710Sed		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
452194710Sed			if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
453194710Sed				/* Switch main and alt LNA */
454194710Sed				div_ant_conf.main_lna_conf =
455193323Sed						ATH_ANT_DIV_COMB_LNA2;
456193323Sed				div_ant_conf.alt_lna_conf  =
457193323Sed						ATH_ANT_DIV_COMB_LNA1;
458193323Sed			} else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
459193323Sed				div_ant_conf.main_lna_conf =
460193323Sed						ATH_ANT_DIV_COMB_LNA1;
461193323Sed				div_ant_conf.alt_lna_conf  =
462193323Sed						ATH_ANT_DIV_COMB_LNA2;
463193323Sed			}
464193323Sed
465193323Sed			goto div_comb_done;
466193323Sed		} else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
467193323Sed			   (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
468193323Sed			/* Set alt to another LNA */
469193323Sed			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
470193323Sed				div_ant_conf.alt_lna_conf =
471193323Sed						ATH_ANT_DIV_COMB_LNA1;
472193323Sed			else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
473193323Sed				div_ant_conf.alt_lna_conf =
474193323Sed						ATH_ANT_DIV_COMB_LNA2;
475193323Sed
476193323Sed			goto div_comb_done;
477193323Sed		}
478193323Sed
479193323Sed		if ((alt_rssi_avg < (main_rssi_avg +
480193323Sed		    ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
481193323Sed			goto div_comb_done;
482193323Sed	}
483193323Sed
484193323Sed	if (!antcomb->scan_not_start) {
485193323Sed		switch (curr_alt_set) {
486193323Sed		case ATH_ANT_DIV_COMB_LNA2:
487193323Sed			antcomb->rssi_lna2 = alt_rssi_avg;
488193323Sed			antcomb->rssi_lna1 = main_rssi_avg;
489193323Sed			antcomb->scan = AH_TRUE;
490193323Sed			/* set to A+B */
491193323Sed			div_ant_conf.main_lna_conf =
492193323Sed				ATH_ANT_DIV_COMB_LNA1;
493193323Sed			div_ant_conf.alt_lna_conf  =
494193323Sed				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
495193323Sed			break;
496193323Sed		case ATH_ANT_DIV_COMB_LNA1:
497193323Sed			antcomb->rssi_lna1 = alt_rssi_avg;
498193323Sed			antcomb->rssi_lna2 = main_rssi_avg;
499193323Sed			antcomb->scan = AH_TRUE;
500193323Sed			/* set to A+B */
501193323Sed			div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
502193323Sed			div_ant_conf.alt_lna_conf  =
503193323Sed				ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
504193323Sed			break;
505193323Sed		case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
506193323Sed			antcomb->rssi_add = alt_rssi_avg;
507193323Sed			antcomb->scan = AH_TRUE;
508193323Sed			/* set to A-B */
509193323Sed			div_ant_conf.alt_lna_conf =
510193323Sed				ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
511193323Sed			break;
512193323Sed		case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
513193323Sed			antcomb->rssi_sub = alt_rssi_avg;
514193323Sed			antcomb->scan = AH_FALSE;
515193323Sed			if (antcomb->rssi_lna2 >
516193323Sed			    (antcomb->rssi_lna1 +
517193323Sed			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
518193323Sed				/* use LNA2 as main LNA */
519193323Sed				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
520193323Sed				    (antcomb->rssi_add > antcomb->rssi_sub)) {
521193323Sed					/* set to A+B */
522193323Sed					div_ant_conf.main_lna_conf =
523193323Sed						ATH_ANT_DIV_COMB_LNA2;
524193323Sed					div_ant_conf.alt_lna_conf  =
525193323Sed						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
526194710Sed				} else if (antcomb->rssi_sub >
527194710Sed					   antcomb->rssi_lna1) {
528194710Sed					/* set to A-B */
529194710Sed					div_ant_conf.main_lna_conf =
530193323Sed						ATH_ANT_DIV_COMB_LNA2;
531194710Sed					div_ant_conf.alt_lna_conf =
532194710Sed						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
533194710Sed				} else {
534194710Sed					/* set to LNA1 */
535194710Sed					div_ant_conf.main_lna_conf =
536194710Sed						ATH_ANT_DIV_COMB_LNA2;
537194710Sed					div_ant_conf.alt_lna_conf =
538193323Sed						ATH_ANT_DIV_COMB_LNA1;
539194710Sed				}
540194710Sed			} else {
541194710Sed				/* use LNA1 as main LNA */
542194710Sed				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
543194710Sed				    (antcomb->rssi_add > antcomb->rssi_sub)) {
544194710Sed					/* set to A+B */
545193323Sed					div_ant_conf.main_lna_conf =
546194710Sed						ATH_ANT_DIV_COMB_LNA1;
547194710Sed					div_ant_conf.alt_lna_conf  =
548194710Sed						ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
549193323Sed				} else if (antcomb->rssi_sub >
550193323Sed					   antcomb->rssi_lna1) {
551193323Sed					/* set to A-B */
552194710Sed					div_ant_conf.main_lna_conf =
553194710Sed						ATH_ANT_DIV_COMB_LNA1;
554194710Sed					div_ant_conf.alt_lna_conf =
555194710Sed						ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
556194710Sed				} else {
557194710Sed					/* set to LNA2 */
558194710Sed					div_ant_conf.main_lna_conf =
559194710Sed						ATH_ANT_DIV_COMB_LNA1;
560194710Sed					div_ant_conf.alt_lna_conf =
561194710Sed						ATH_ANT_DIV_COMB_LNA2;
562194710Sed				}
563194710Sed			}
564194710Sed			break;
565193323Sed		default:
566193323Sed			break;
567193323Sed		}
568193323Sed	} else {
569194710Sed		if (!antcomb->alt_good) {
570194710Sed			antcomb->scan_not_start = AH_FALSE;
571194710Sed			/* Set alt to another LNA */
572193323Sed			if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
573193323Sed				div_ant_conf.main_lna_conf =
574193323Sed						ATH_ANT_DIV_COMB_LNA2;
575193323Sed				div_ant_conf.alt_lna_conf =
576194710Sed						ATH_ANT_DIV_COMB_LNA1;
577194710Sed			} else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
578194710Sed				div_ant_conf.main_lna_conf =
579194710Sed						ATH_ANT_DIV_COMB_LNA1;
580193323Sed				div_ant_conf.alt_lna_conf =
581194710Sed						ATH_ANT_DIV_COMB_LNA2;
582194710Sed			}
583194710Sed			goto div_comb_done;
584194710Sed		}
585194710Sed	}
586194710Sed
587194710Sed	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
588193323Sed					   main_rssi_avg, alt_rssi_avg,
589193323Sed					   alt_ratio);
590193323Sed
591193323Sed	antcomb->quick_scan_cnt++;
592193323Sed
593194710Seddiv_comb_done:
594193323Sed	ath_ant_div_conf_fast_divbias(&div_ant_conf);
595194710Sed
596194710Sed	ar9285_antdiv_comb_conf_set(ah, &div_ant_conf);
597194710Sed
598194710Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
599194710Sed	   __func__, antcomb->total_pkt_count);
600194710Sed
601194710Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
602194710Sed	   __func__, antcomb->main_total_rssi);
603194710Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
604194710Sed	   __func__, antcomb->alt_total_rssi);
605194710Sed
606194710Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
607194710Sed	   __func__, main_rssi_avg);
608193323Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
609193323Sed	   __func__, alt_rssi_avg);
610193323Sed
611194710Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
612194710Sed	   __func__, antcomb->main_recv_cnt);
613193323Sed	HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
614193323Sed	   __func__, antcomb->alt_recv_cnt);
615193323Sed
616193323Sed	if (curr_alt_set != div_ant_conf.alt_lna_conf)
617193323Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
618193323Sed		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
619193323Sed	if (curr_main_set != div_ant_conf.main_lna_conf)
620193323Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
621193323Sed		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
622193323Sed	if (curr_bias != div_ant_conf.fast_div_bias)
623193323Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
624193323Sed		    __func__, curr_bias, div_ant_conf.fast_div_bias);
625194710Sed
626193323Sed	antcomb->scan_start_time = ticks;
627194710Sed	antcomb->total_pkt_count = 0;
628194710Sed	antcomb->main_total_rssi = 0;
629194710Sed	antcomb->alt_total_rssi = 0;
630194710Sed	antcomb->main_recv_cnt = 0;
631194710Sed	antcomb->alt_recv_cnt = 0;
632194710Sed}
633194710Sed
634194710Sed/*
635194710Sed * Set the antenna switch to control RX antenna diversity.
636194710Sed *
637194710Sed * If a fixed configuration is used, the LNA and div bias
638194710Sed * settings are fixed and the antenna diversity scanning routine
639193323Sed * is disabled.
640193323Sed *
641193323Sed * If a variable configuration is used, a default is programmed
642193323Sed * in and sampling commences per RXed packet.
643193323Sed *
644193323Sed * Since this is called from ar9285SetBoardValues() to setup
645193323Sed * diversity, it means that after a reset or scan, any current
646193323Sed * software diversity combining settings will be lost and won't
647193323Sed * re-appear until after the first successful sample run.
648193323Sed * Please keep this in mind if you're seeing weird performance
649193323Sed * that happens to relate to scan/diversity timing.
650194612Sed */
651194612SedHAL_BOOL
652194612Sedar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
653194612Sed{
654194612Sed	int regVal;
655194612Sed	const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
656194612Sed	const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
657194612Sed	uint8_t ant_div_control1, ant_div_control2;
658194612Sed
659194612Sed	if (pModal->version < 3) {
660194612Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: not supported\n",
661194612Sed	    __func__);
662194612Sed		return AH_FALSE;	/* Can't do diversity */
663194612Sed	}
664194612Sed
665194612Sed	/* Store settings */
666194612Sed	AH5212(ah)->ah_antControl = settings;
667194612Sed	AH5212(ah)->ah_diversity = (settings == HAL_ANT_VARIABLE);
668194612Sed
669194612Sed	/* XXX don't fiddle if the PHY is in sleep mode or ! chan */
670194612Sed
671194612Sed	/* Begin setting the relevant registers */
672194612Sed
673194612Sed	ant_div_control1 = pModal->antdiv_ctl1;
674194612Sed	ant_div_control2 = pModal->antdiv_ctl2;
675194612Sed
676194612Sed	regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
677193323Sed	regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
678193323Sed
679193323Sed	/* enable antenna diversity only if diversityControl == HAL_ANT_VARIABLE */
680193323Sed	if (settings == HAL_ANT_VARIABLE)
681193323Sed	    regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL);
682193323Sed
683193323Sed	if (settings == HAL_ANT_VARIABLE) {
684193323Sed	    HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_VARIABLE\n",
685193323Sed	      __func__);
686193323Sed	    regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
687193323Sed	    regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
688193323Sed	    regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
689193323Sed	    regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
690193323Sed	} else {
691194612Sed	    if (settings == HAL_ANT_FIXED_A) {
692194612Sed		/* Diversity disabled, RX = LNA1 */
693193323Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n",
694193323Sed		    __func__);
695193323Sed		regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
696193323Sed		regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
697193323Sed		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
698193323Sed		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
699193323Sed	    }
700193323Sed	    else if (settings == HAL_ANT_FIXED_B) {
701193323Sed		/* Diversity disabled, RX = LNA2 */
702194710Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n",
703193323Sed		    __func__);
704193323Sed		regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
705193323Sed		regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
706193323Sed		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
707193323Sed		regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
708193323Sed	    }
709193323Sed	}
710193323Sed
711193323Sed	OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
712193323Sed	regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
713194710Sed	regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
714194710Sed	regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
715194710Sed	if (settings == HAL_ANT_VARIABLE)
716194710Sed	    regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
717194710Sed
718194710Sed	OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
719194710Sed	regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
720194710Sed
721194710Sed	/*
722194710Sed	 * If Diversity combining is available and the diversity setting
723194710Sed	 * is to allow variable diversity, enable it by default.
724194710Sed	 *
725194710Sed	 * This will be eventually overridden by the software antenna
726194710Sed	 * diversity logic.
727194710Sed	 *
728194710Sed	 * Note that yes, this following section overrides the above
729194710Sed	 * settings for the LNA configuration and fast-bias.
730194710Sed	 */
731193323Sed	if (ar9285_check_div_comb(ah) && AH5212(ah)->ah_diversity == AH_TRUE) {
732193323Sed		// If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning
733193323Sed		HALDEBUG(ah, HAL_DEBUG_DIVERSITY,
734193323Sed		    "%s: Enable initial settings for combined diversity\n",
735193323Sed		    __func__);
736193323Sed		regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
737193323Sed		regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF));
738193323Sed		regVal |= (ATH_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
739193323Sed		regVal |= (ATH_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
740193323Sed		regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
741193323Sed		regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
742193323Sed		OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
743193323Sed	}
744193323Sed
745193323Sed	return AH_TRUE;
746193323Sed}
747193323Sed