if_ath_lna_div.c revision 332320
1/*-
2 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer,
10 *    without modification.
11 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13 *    redistribution must be conditioned upon including a substantially
14 *    similar Disclaimer requirement for further binary redistribution.
15 *
16 * NO WARRANTY
17 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27 * THE POSSIBILITY OF SUCH DAMAGES.
28 *
29 * $FreeBSD: stable/10/sys/dev/ath/if_ath_lna_div.c 332320 2018-04-09 12:53:15Z emaste $
30 */
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD: stable/10/sys/dev/ath/if_ath_lna_div.c 332320 2018-04-09 12:53:15Z emaste $");
33
34/*
35 * This module handles LNA diversity for those chips which implement LNA
36 * mixing (AR9285/AR9485.)
37 */
38#include "opt_ath.h"
39#include "opt_inet.h"
40#include "opt_wlan.h"
41
42#include <sys/param.h>
43#include <sys/systm.h>
44#include <sys/sysctl.h>
45#include <sys/kernel.h>
46#include <sys/lock.h>
47#include <sys/mutex.h>
48#include <sys/errno.h>
49
50#include <machine/bus.h>
51#include <machine/resource.h>
52#include <sys/bus.h>
53
54#include <sys/socket.h>
55
56#include <net/if.h>
57#include <net/if_media.h>
58#include <net/if_arp.h>
59#include <net/ethernet.h>		/* XXX for ether_sprintf */
60
61#include <net80211/ieee80211_var.h>
62
63#include <net/bpf.h>
64
65#ifdef INET
66#include <netinet/in.h>
67#include <netinet/if_ether.h>
68#endif
69
70#include <dev/ath/if_athvar.h>
71#include <dev/ath/if_ath_debug.h>
72#include <dev/ath/if_ath_lna_div.h>
73
74/* Linux compability macros */
75/*
76 * XXX these don't handle rounding, underflow, overflow, wrapping!
77 */
78#define	msecs_to_jiffies(a)		( (a) * hz / 1000 )
79
80/*
81 * Methods which are required
82 */
83
84/*
85 * Attach the LNA diversity to the given interface
86 */
87int
88ath_lna_div_attach(struct ath_softc *sc)
89{
90	struct if_ath_ant_comb_state *ss;
91	HAL_ANT_COMB_CONFIG div_ant_conf;
92
93	/* Only do this if diversity is enabled */
94	if (! ath_hal_hasdivantcomb(sc->sc_ah))
95		return (0);
96
97	ss = malloc(sizeof(struct if_ath_ant_comb_state),
98	    M_TEMP, M_WAITOK | M_ZERO);
99	if (ss == NULL) {
100		device_printf(sc->sc_dev, "%s: failed to allocate\n",
101		    __func__);
102		/* Don't fail at this point */
103		return (0);
104	}
105
106	/* Fetch the hardware configuration */
107	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
108	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
109
110	/* Figure out what the hardware specific bits should be */
111	if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
112	    (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
113		ss->lna1_lna2_delta = -9;
114	} else {
115		ss->lna1_lna2_delta = -3;
116	}
117
118	/* Let's flip this on */
119	sc->sc_lna_div = ss;
120	sc->sc_dolnadiv = 1;
121
122	return (0);
123}
124
125/*
126 * Detach the LNA diversity state from the given interface
127 */
128int
129ath_lna_div_detach(struct ath_softc *sc)
130{
131	if (sc->sc_lna_div != NULL) {
132		free(sc->sc_lna_div, M_TEMP);
133		sc->sc_lna_div = NULL;
134	}
135	sc->sc_dolnadiv = 0;
136	return (0);
137}
138
139/*
140 * Enable LNA diversity on the current channel if it's required.
141 */
142int
143ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
144{
145
146	return (0);
147}
148
149/*
150 * Handle ioctl requests from the diagnostic interface.
151 *
152 * The initial part of this code resembles ath_ioctl_diag();
153 * it's likely a good idea to reduce duplication between
154 * these two routines.
155 */
156int
157ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
158{
159	unsigned int id = ad->ad_id & ATH_DIAG_ID;
160	void *indata = NULL;
161	void *outdata = NULL;
162	u_int32_t insize = ad->ad_in_size;
163	u_int32_t outsize = ad->ad_out_size;
164	int error = 0;
165//	int val;
166
167	if (ad->ad_id & ATH_DIAG_IN) {
168		/*
169		 * Copy in data.
170		 */
171		indata = malloc(insize, M_TEMP, M_NOWAIT);
172		if (indata == NULL) {
173			error = ENOMEM;
174			goto bad;
175		}
176		error = copyin(ad->ad_in_data, indata, insize);
177		if (error)
178			goto bad;
179	}
180	if (ad->ad_id & ATH_DIAG_DYN) {
181		/*
182		 * Allocate a buffer for the results (otherwise the HAL
183		 * returns a pointer to a buffer where we can read the
184		 * results).  Note that we depend on the HAL leaving this
185		 * pointer for us to use below in reclaiming the buffer;
186		 * may want to be more defensive.
187		 */
188		outdata = malloc(outsize, M_TEMP, M_NOWAIT | M_ZERO);
189		if (outdata == NULL) {
190			error = ENOMEM;
191			goto bad;
192		}
193	}
194	switch (id) {
195		default:
196			error = EINVAL;
197			goto bad;
198	}
199	if (outsize < ad->ad_out_size)
200		ad->ad_out_size = outsize;
201	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
202		error = EFAULT;
203bad:
204	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
205		free(indata, M_TEMP);
206	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
207		free(outdata, M_TEMP);
208	return (error);
209}
210
211static HAL_BOOL
212ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
213    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
214{
215	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
216		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
217		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
218}
219
220static void
221ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
222    HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
223{
224	antcomb->quick_scan_cnt = 0;
225
226	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
227		antcomb->rssi_lna2 = main_rssi_avg;
228	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
229		antcomb->rssi_lna1 = main_rssi_avg;
230
231	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
232	case (0x10): /* LNA2 A-B */
233		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
234		antcomb->first_quick_scan_conf =
235			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
236		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
237		break;
238	case (0x20): /* LNA1 A-B */
239		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
240		antcomb->first_quick_scan_conf =
241			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
242		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
243		break;
244	case (0x21): /* LNA1 LNA2 */
245		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
246		antcomb->first_quick_scan_conf =
247			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
248		antcomb->second_quick_scan_conf =
249			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
250		break;
251	case (0x12): /* LNA2 LNA1 */
252		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
253		antcomb->first_quick_scan_conf =
254			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
255		antcomb->second_quick_scan_conf =
256			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
257		break;
258	case (0x13): /* LNA2 A+B */
259		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
260		antcomb->first_quick_scan_conf =
261			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
262		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
263		break;
264	case (0x23): /* LNA1 A+B */
265		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
266		antcomb->first_quick_scan_conf =
267			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
268		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
269		break;
270	default:
271		break;
272	}
273}
274
275static void
276ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
277    HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
278    int alt_rssi_avg, int alt_ratio)
279{
280	/* alt_good */
281	switch (antcomb->quick_scan_cnt) {
282	case 0:
283		/* set alt to main, and alt to first conf */
284		div_ant_conf->main_lna_conf = antcomb->main_conf;
285		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
286		break;
287	case 1:
288		/* set alt to main, and alt to first conf */
289		div_ant_conf->main_lna_conf = antcomb->main_conf;
290		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
291		antcomb->rssi_first = main_rssi_avg;
292		antcomb->rssi_second = alt_rssi_avg;
293
294		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
295			/* main is LNA1 */
296			if (ath_is_alt_ant_ratio_better(alt_ratio,
297						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
298						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
299						main_rssi_avg, alt_rssi_avg,
300						antcomb->total_pkt_count))
301				antcomb->first_ratio = AH_TRUE;
302			else
303				antcomb->first_ratio = AH_FALSE;
304		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
305			if (ath_is_alt_ant_ratio_better(alt_ratio,
306						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
307						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
308						main_rssi_avg, alt_rssi_avg,
309						antcomb->total_pkt_count))
310				antcomb->first_ratio = AH_TRUE;
311			else
312				antcomb->first_ratio = AH_FALSE;
313		} else {
314			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
315			    (alt_rssi_avg > main_rssi_avg +
316			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
317			    (alt_rssi_avg > main_rssi_avg)) &&
318			    (antcomb->total_pkt_count > 50))
319				antcomb->first_ratio = AH_TRUE;
320			else
321				antcomb->first_ratio = AH_FALSE;
322		}
323		break;
324	case 2:
325		antcomb->alt_good = AH_FALSE;
326		antcomb->scan_not_start = AH_FALSE;
327		antcomb->scan = AH_FALSE;
328		antcomb->rssi_first = main_rssi_avg;
329		antcomb->rssi_third = alt_rssi_avg;
330
331		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
332			antcomb->rssi_lna1 = alt_rssi_avg;
333		else if (antcomb->second_quick_scan_conf ==
334			 HAL_ANT_DIV_COMB_LNA2)
335			antcomb->rssi_lna2 = alt_rssi_avg;
336		else if (antcomb->second_quick_scan_conf ==
337			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
338			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
339				antcomb->rssi_lna2 = main_rssi_avg;
340			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
341				antcomb->rssi_lna1 = main_rssi_avg;
342		}
343
344		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
345		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
346			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
347		else
348			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
349
350		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
351			if (ath_is_alt_ant_ratio_better(alt_ratio,
352						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
353						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
354						main_rssi_avg, alt_rssi_avg,
355						antcomb->total_pkt_count))
356				antcomb->second_ratio = AH_TRUE;
357			else
358				antcomb->second_ratio = AH_FALSE;
359		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
360			if (ath_is_alt_ant_ratio_better(alt_ratio,
361						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
362						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
363						main_rssi_avg, alt_rssi_avg,
364						antcomb->total_pkt_count))
365				antcomb->second_ratio = AH_TRUE;
366			else
367				antcomb->second_ratio = AH_FALSE;
368		} else {
369			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
370			    (alt_rssi_avg > main_rssi_avg +
371			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
372			    (alt_rssi_avg > main_rssi_avg)) &&
373			    (antcomb->total_pkt_count > 50))
374				antcomb->second_ratio = AH_TRUE;
375			else
376				antcomb->second_ratio = AH_FALSE;
377		}
378
379		/* set alt to the conf with maximun ratio */
380		if (antcomb->first_ratio && antcomb->second_ratio) {
381			if (antcomb->rssi_second > antcomb->rssi_third) {
382				/* first alt*/
383				if ((antcomb->first_quick_scan_conf ==
384				    HAL_ANT_DIV_COMB_LNA1) ||
385				    (antcomb->first_quick_scan_conf ==
386				    HAL_ANT_DIV_COMB_LNA2))
387					/* Set alt LNA1 or LNA2*/
388					if (div_ant_conf->main_lna_conf ==
389					    HAL_ANT_DIV_COMB_LNA2)
390						div_ant_conf->alt_lna_conf =
391							HAL_ANT_DIV_COMB_LNA1;
392					else
393						div_ant_conf->alt_lna_conf =
394							HAL_ANT_DIV_COMB_LNA2;
395				else
396					/* Set alt to A+B or A-B */
397					div_ant_conf->alt_lna_conf =
398						antcomb->first_quick_scan_conf;
399			} else if ((antcomb->second_quick_scan_conf ==
400				   HAL_ANT_DIV_COMB_LNA1) ||
401				   (antcomb->second_quick_scan_conf ==
402				   HAL_ANT_DIV_COMB_LNA2)) {
403				/* Set alt LNA1 or LNA2 */
404				if (div_ant_conf->main_lna_conf ==
405				    HAL_ANT_DIV_COMB_LNA2)
406					div_ant_conf->alt_lna_conf =
407						HAL_ANT_DIV_COMB_LNA1;
408				else
409					div_ant_conf->alt_lna_conf =
410						HAL_ANT_DIV_COMB_LNA2;
411			} else {
412				/* Set alt to A+B or A-B */
413				div_ant_conf->alt_lna_conf =
414					antcomb->second_quick_scan_conf;
415			}
416		} else if (antcomb->first_ratio) {
417			/* first alt */
418			if ((antcomb->first_quick_scan_conf ==
419			    HAL_ANT_DIV_COMB_LNA1) ||
420			    (antcomb->first_quick_scan_conf ==
421			    HAL_ANT_DIV_COMB_LNA2))
422					/* Set alt LNA1 or LNA2 */
423				if (div_ant_conf->main_lna_conf ==
424				    HAL_ANT_DIV_COMB_LNA2)
425					div_ant_conf->alt_lna_conf =
426							HAL_ANT_DIV_COMB_LNA1;
427				else
428					div_ant_conf->alt_lna_conf =
429							HAL_ANT_DIV_COMB_LNA2;
430			else
431				/* Set alt to A+B or A-B */
432				div_ant_conf->alt_lna_conf =
433						antcomb->first_quick_scan_conf;
434		} else if (antcomb->second_ratio) {
435				/* second alt */
436			if ((antcomb->second_quick_scan_conf ==
437			    HAL_ANT_DIV_COMB_LNA1) ||
438			    (antcomb->second_quick_scan_conf ==
439			    HAL_ANT_DIV_COMB_LNA2))
440				/* Set alt LNA1 or LNA2 */
441				if (div_ant_conf->main_lna_conf ==
442				    HAL_ANT_DIV_COMB_LNA2)
443					div_ant_conf->alt_lna_conf =
444						HAL_ANT_DIV_COMB_LNA1;
445				else
446					div_ant_conf->alt_lna_conf =
447						HAL_ANT_DIV_COMB_LNA2;
448			else
449				/* Set alt to A+B or A-B */
450				div_ant_conf->alt_lna_conf =
451						antcomb->second_quick_scan_conf;
452		} else {
453			/* main is largest */
454			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
455			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
456				/* Set alt LNA1 or LNA2 */
457				if (div_ant_conf->main_lna_conf ==
458				    HAL_ANT_DIV_COMB_LNA2)
459					div_ant_conf->alt_lna_conf =
460							HAL_ANT_DIV_COMB_LNA1;
461				else
462					div_ant_conf->alt_lna_conf =
463							HAL_ANT_DIV_COMB_LNA2;
464			else
465				/* Set alt to A+B or A-B */
466				div_ant_conf->alt_lna_conf = antcomb->main_conf;
467		}
468		break;
469	default:
470		break;
471	}
472}
473
474static void
475ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
476    int alt_ratio, int alt_ant_ratio_th, u_int config_group,
477    HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
478{
479
480	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
481		switch ((pdiv_ant_conf->main_lna_conf << 4)
482		    | pdiv_ant_conf->alt_lna_conf) {
483		case (0x01): //A-B LNA2
484			pdiv_ant_conf->fast_div_bias = 0x1;
485			pdiv_ant_conf->main_gaintb   = 0;
486			pdiv_ant_conf->alt_gaintb    = 0;
487			break;
488		case (0x02): //A-B LNA1
489			pdiv_ant_conf->fast_div_bias = 0x1;
490			pdiv_ant_conf->main_gaintb   = 0;
491			pdiv_ant_conf->alt_gaintb    = 0;
492			break;
493		case (0x03): //A-B A+B
494			pdiv_ant_conf->fast_div_bias = 0x1;
495			pdiv_ant_conf->main_gaintb   = 0;
496			pdiv_ant_conf->alt_gaintb    = 0;
497			break;
498		case (0x10): //LNA2 A-B
499			if ((antcomb->scan == 0)
500			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
501				pdiv_ant_conf->fast_div_bias = 0x3f;
502			} else {
503				pdiv_ant_conf->fast_div_bias = 0x1;
504			}
505			pdiv_ant_conf->main_gaintb   = 0;
506			pdiv_ant_conf->alt_gaintb    = 0;
507			break;
508		case (0x12): //LNA2 LNA1
509			pdiv_ant_conf->fast_div_bias = 0x1;
510			pdiv_ant_conf->main_gaintb   = 0;
511			pdiv_ant_conf->alt_gaintb    = 0;
512			break;
513			case (0x13): //LNA2 A+B
514			if ((antcomb->scan == 0)
515			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
516				pdiv_ant_conf->fast_div_bias = 0x3f;
517			} else {
518				pdiv_ant_conf->fast_div_bias = 0x1;
519			}
520			pdiv_ant_conf->main_gaintb   = 0;
521			pdiv_ant_conf->alt_gaintb    = 0;
522			break;
523		case (0x20): //LNA1 A-B
524			if ((antcomb->scan == 0)
525			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
526				pdiv_ant_conf->fast_div_bias = 0x3f;
527			} else {
528				pdiv_ant_conf->fast_div_bias = 0x1;
529			}
530			pdiv_ant_conf->main_gaintb   = 0;
531			pdiv_ant_conf->alt_gaintb    = 0;
532			break;
533		case (0x21): //LNA1 LNA2
534			pdiv_ant_conf->fast_div_bias = 0x1;
535			pdiv_ant_conf->main_gaintb   = 0;
536			pdiv_ant_conf->alt_gaintb    = 0;
537			break;
538		case (0x23): //LNA1 A+B
539			if ((antcomb->scan == 0)
540			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
541				pdiv_ant_conf->fast_div_bias = 0x3f;
542			} else {
543				pdiv_ant_conf->fast_div_bias = 0x1;
544			}
545			pdiv_ant_conf->main_gaintb   = 0;
546			pdiv_ant_conf->alt_gaintb    = 0;
547			break;
548		case (0x30): //A+B A-B
549			pdiv_ant_conf->fast_div_bias = 0x1;
550			pdiv_ant_conf->main_gaintb   = 0;
551			pdiv_ant_conf->alt_gaintb    = 0;
552			break;
553		case (0x31): //A+B LNA2
554			pdiv_ant_conf->fast_div_bias = 0x1;
555			pdiv_ant_conf->main_gaintb   = 0;
556			pdiv_ant_conf->alt_gaintb    = 0;
557			break;
558		case (0x32): //A+B LNA1
559			pdiv_ant_conf->fast_div_bias = 0x1;
560			pdiv_ant_conf->main_gaintb   = 0;
561			pdiv_ant_conf->alt_gaintb    = 0;
562			break;
563		default:
564			break;
565		}
566	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
567		switch ((pdiv_ant_conf->main_lna_conf << 4)
568		    | pdiv_ant_conf->alt_lna_conf) {
569		case (0x01): //A-B LNA2
570			pdiv_ant_conf->fast_div_bias = 0x1;
571			pdiv_ant_conf->main_gaintb   = 0;
572			pdiv_ant_conf->alt_gaintb    = 0;
573			break;
574		case (0x02): //A-B LNA1
575			pdiv_ant_conf->fast_div_bias = 0x1;
576			pdiv_ant_conf->main_gaintb   = 0;
577			pdiv_ant_conf->alt_gaintb    = 0;
578			break;
579		case (0x03): //A-B A+B
580			pdiv_ant_conf->fast_div_bias = 0x1;
581			pdiv_ant_conf->main_gaintb   = 0;
582			pdiv_ant_conf->alt_gaintb    = 0;
583			break;
584		case (0x10): //LNA2 A-B
585			if ((antcomb->scan == 0)
586			    && (alt_ratio > alt_ant_ratio_th)) {
587				pdiv_ant_conf->fast_div_bias = 0x1;
588			} else {
589				pdiv_ant_conf->fast_div_bias = 0x2;
590			}
591			pdiv_ant_conf->main_gaintb   = 0;
592			pdiv_ant_conf->alt_gaintb    = 0;
593			break;
594		case (0x12): //LNA2 LNA1
595			pdiv_ant_conf->fast_div_bias = 0x1;
596			pdiv_ant_conf->main_gaintb   = 0;
597			pdiv_ant_conf->alt_gaintb    = 0;
598			break;
599		case (0x13): //LNA2 A+B
600			if ((antcomb->scan == 0)
601			    && (alt_ratio > alt_ant_ratio_th)) {
602				pdiv_ant_conf->fast_div_bias = 0x1;
603			} else {
604				pdiv_ant_conf->fast_div_bias = 0x2;
605			}
606			pdiv_ant_conf->main_gaintb   = 0;
607			pdiv_ant_conf->alt_gaintb    = 0;
608			break;
609		case (0x20): //LNA1 A-B
610			if ((antcomb->scan == 0)
611			    && (alt_ratio > alt_ant_ratio_th)) {
612				pdiv_ant_conf->fast_div_bias = 0x1;
613			} else {
614				pdiv_ant_conf->fast_div_bias = 0x2;
615			}
616			pdiv_ant_conf->main_gaintb   = 0;
617			pdiv_ant_conf->alt_gaintb    = 0;
618			break;
619		case (0x21): //LNA1 LNA2
620			pdiv_ant_conf->fast_div_bias = 0x1;
621			pdiv_ant_conf->main_gaintb   = 0;
622			pdiv_ant_conf->alt_gaintb    = 0;
623			break;
624		case (0x23): //LNA1 A+B
625			if ((antcomb->scan == 0)
626			    && (alt_ratio > alt_ant_ratio_th)) {
627				pdiv_ant_conf->fast_div_bias = 0x1;
628			} else {
629				pdiv_ant_conf->fast_div_bias = 0x2;
630			}
631			pdiv_ant_conf->main_gaintb   = 0;
632			pdiv_ant_conf->alt_gaintb    = 0;
633			break;
634		case (0x30): //A+B A-B
635			pdiv_ant_conf->fast_div_bias = 0x1;
636			pdiv_ant_conf->main_gaintb   = 0;
637			pdiv_ant_conf->alt_gaintb    = 0;
638			break;
639		case (0x31): //A+B LNA2
640			pdiv_ant_conf->fast_div_bias = 0x1;
641			pdiv_ant_conf->main_gaintb   = 0;
642			pdiv_ant_conf->alt_gaintb    = 0;
643			break;
644		case (0x32): //A+B LNA1
645			pdiv_ant_conf->fast_div_bias = 0x1;
646			pdiv_ant_conf->main_gaintb   = 0;
647			pdiv_ant_conf->alt_gaintb    = 0;
648			break;
649		default:
650			break;
651		}
652	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
653		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
654		case (0x01): //A-B LNA2
655			pdiv_ant_conf->fast_div_bias = 0x3b;
656			break;
657		case (0x02): //A-B LNA1
658			pdiv_ant_conf->fast_div_bias = 0x3d;
659			break;
660		case (0x03): //A-B A+B
661			pdiv_ant_conf->fast_div_bias = 0x1;
662			break;
663		case (0x10): //LNA2 A-B
664			pdiv_ant_conf->fast_div_bias = 0x7;
665			break;
666		case (0x12): //LNA2 LNA1
667			pdiv_ant_conf->fast_div_bias = 0x2;
668			break;
669		case (0x13): //LNA2 A+B
670			pdiv_ant_conf->fast_div_bias = 0x7;
671			break;
672		case (0x20): //LNA1 A-B
673			pdiv_ant_conf->fast_div_bias = 0x6;
674			break;
675		case (0x21): //LNA1 LNA2
676			pdiv_ant_conf->fast_div_bias = 0x0;
677			break;
678		case (0x23): //LNA1 A+B
679			pdiv_ant_conf->fast_div_bias = 0x6;
680			break;
681		case (0x30): //A+B A-B
682			pdiv_ant_conf->fast_div_bias = 0x1;
683			break;
684		case (0x31): //A+B LNA2
685			pdiv_ant_conf->fast_div_bias = 0x3b;
686			break;
687		case (0x32): //A+B LNA1
688			pdiv_ant_conf->fast_div_bias = 0x3d;
689			break;
690		default:
691			break;
692		}
693	}
694}
695
696/*
697 * AR9485/AR933x TODO:
698 * + Select a ratio based on whether RSSI is low or not; but I need
699 *   to figure out what "low_rssi_th" is sourced from.
700 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
701 * + .. and there's likely a bunch of other things to include in this.
702 */
703
704/* Antenna diversity and combining */
705void
706ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
707    unsigned long ticks, int hz)
708{
709	HAL_ANT_COMB_CONFIG div_ant_conf;
710	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
711	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
712	int curr_main_set, curr_bias;
713	int main_rssi = rs->rs_rssi_ctl[0];
714	int alt_rssi = rs->rs_rssi_ctl[1];
715	int rx_ant_conf, main_ant_conf, alt_ant_conf;
716	HAL_BOOL short_scan = AH_FALSE;
717
718	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
719	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
720	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
721
722#if 0
723	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
724	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
725	    "FastDiv: %d\n",
726	    __func__,
727	    main_rssi,
728	    alt_rssi,
729	    main_ant_conf,
730	    alt_ant_conf,
731	    rx_ant_conf,
732	    !!(rs->rs_rssi_ctl[2] & 0x80),
733	    !!(rs->rs_rssi_ctl[2] & 0x40),
734	    !!(rs->rs_rssi_ext[2] & 0x40));
735#endif
736
737	/*
738	 * If LNA diversity combining isn't enabled, don't run this.
739	 */
740	if (! sc->sc_dolnadiv)
741		return;
742
743	/*
744	 * XXX this is ugly, but the HAL code attaches the
745	 * LNA diversity to the TX antenna settings.
746	 * I don't know why.
747	 */
748	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
749		return;
750
751	/* Record packet only when alt_rssi is positive */
752	if (main_rssi > 0 && alt_rssi > 0) {
753		antcomb->total_pkt_count++;
754		antcomb->main_total_rssi += main_rssi;
755		antcomb->alt_total_rssi  += alt_rssi;
756		if (main_ant_conf == rx_ant_conf)
757			antcomb->main_recv_cnt++;
758		else
759			antcomb->alt_recv_cnt++;
760	}
761
762	/* Short scan check */
763	if (antcomb->scan && antcomb->alt_good) {
764		if (time_after(ticks, antcomb->scan_start_time +
765		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
766			short_scan = AH_TRUE;
767		else
768			if (antcomb->total_pkt_count ==
769			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
770				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
771					    antcomb->total_pkt_count);
772				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
773					short_scan = AH_TRUE;
774			}
775	}
776
777#if 0
778	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
779	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
780	    __func__,
781	    antcomb->total_pkt_count,
782	    !! (rs->rs_moreaggr),
783	    !! (short_scan));
784#endif
785
786	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
787	    rs->rs_moreaggr) && !short_scan)
788		return;
789
790	if (antcomb->total_pkt_count) {
791		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
792			     antcomb->total_pkt_count);
793		main_rssi_avg = (antcomb->main_total_rssi /
794				 antcomb->total_pkt_count);
795		alt_rssi_avg = (antcomb->alt_total_rssi /
796				 antcomb->total_pkt_count);
797	}
798
799	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
800
801	ath_hal_div_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	curr_bias = div_ant_conf.fast_div_bias;
805
806	antcomb->count++;
807
808	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
809		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
810			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
811						  main_rssi_avg);
812			antcomb->alt_good = AH_TRUE;
813		} else {
814			antcomb->alt_good = AH_FALSE;
815		}
816
817		antcomb->count = 0;
818		antcomb->scan = AH_TRUE;
819		antcomb->scan_not_start = AH_TRUE;
820	}
821
822	if (!antcomb->scan) {
823		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
824			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
825				/* Switch main and alt LNA */
826				div_ant_conf.main_lna_conf =
827						HAL_ANT_DIV_COMB_LNA2;
828				div_ant_conf.alt_lna_conf  =
829						HAL_ANT_DIV_COMB_LNA1;
830			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
831				div_ant_conf.main_lna_conf =
832						HAL_ANT_DIV_COMB_LNA1;
833				div_ant_conf.alt_lna_conf  =
834						HAL_ANT_DIV_COMB_LNA2;
835			}
836
837			goto div_comb_done;
838		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
839			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
840			/* Set alt to another LNA */
841			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
842				div_ant_conf.alt_lna_conf =
843						HAL_ANT_DIV_COMB_LNA1;
844			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
845				div_ant_conf.alt_lna_conf =
846						HAL_ANT_DIV_COMB_LNA2;
847
848			goto div_comb_done;
849		}
850
851		if ((alt_rssi_avg < (main_rssi_avg +
852		    antcomb->lna1_lna2_delta)))
853			goto div_comb_done;
854	}
855
856	if (!antcomb->scan_not_start) {
857		switch (curr_alt_set) {
858		case HAL_ANT_DIV_COMB_LNA2:
859			antcomb->rssi_lna2 = alt_rssi_avg;
860			antcomb->rssi_lna1 = main_rssi_avg;
861			antcomb->scan = AH_TRUE;
862			/* set to A+B */
863			div_ant_conf.main_lna_conf =
864				HAL_ANT_DIV_COMB_LNA1;
865			div_ant_conf.alt_lna_conf  =
866				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
867			break;
868		case HAL_ANT_DIV_COMB_LNA1:
869			antcomb->rssi_lna1 = alt_rssi_avg;
870			antcomb->rssi_lna2 = main_rssi_avg;
871			antcomb->scan = AH_TRUE;
872			/* set to A+B */
873			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
874			div_ant_conf.alt_lna_conf  =
875				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
876			break;
877		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
878			antcomb->rssi_add = alt_rssi_avg;
879			antcomb->scan = AH_TRUE;
880			/* set to A-B */
881			div_ant_conf.alt_lna_conf =
882				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
883			break;
884		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
885			antcomb->rssi_sub = alt_rssi_avg;
886			antcomb->scan = AH_FALSE;
887			if (antcomb->rssi_lna2 >
888			    (antcomb->rssi_lna1 +
889			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
890				/* use LNA2 as main LNA */
891				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
892				    (antcomb->rssi_add > antcomb->rssi_sub)) {
893					/* set to A+B */
894					div_ant_conf.main_lna_conf =
895						HAL_ANT_DIV_COMB_LNA2;
896					div_ant_conf.alt_lna_conf  =
897						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
898				} else if (antcomb->rssi_sub >
899					   antcomb->rssi_lna1) {
900					/* set to A-B */
901					div_ant_conf.main_lna_conf =
902						HAL_ANT_DIV_COMB_LNA2;
903					div_ant_conf.alt_lna_conf =
904						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
905				} else {
906					/* set to LNA1 */
907					div_ant_conf.main_lna_conf =
908						HAL_ANT_DIV_COMB_LNA2;
909					div_ant_conf.alt_lna_conf =
910						HAL_ANT_DIV_COMB_LNA1;
911				}
912			} else {
913				/* use LNA1 as main LNA */
914				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
915				    (antcomb->rssi_add > antcomb->rssi_sub)) {
916					/* set to A+B */
917					div_ant_conf.main_lna_conf =
918						HAL_ANT_DIV_COMB_LNA1;
919					div_ant_conf.alt_lna_conf  =
920						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
921				} else if (antcomb->rssi_sub >
922					   antcomb->rssi_lna1) {
923					/* set to A-B */
924					div_ant_conf.main_lna_conf =
925						HAL_ANT_DIV_COMB_LNA1;
926					div_ant_conf.alt_lna_conf =
927						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
928				} else {
929					/* set to LNA2 */
930					div_ant_conf.main_lna_conf =
931						HAL_ANT_DIV_COMB_LNA1;
932					div_ant_conf.alt_lna_conf =
933						HAL_ANT_DIV_COMB_LNA2;
934				}
935			}
936			break;
937		default:
938			break;
939		}
940	} else {
941		if (!antcomb->alt_good) {
942			antcomb->scan_not_start = AH_FALSE;
943			/* Set alt to another LNA */
944			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
945				div_ant_conf.main_lna_conf =
946						HAL_ANT_DIV_COMB_LNA2;
947				div_ant_conf.alt_lna_conf =
948						HAL_ANT_DIV_COMB_LNA1;
949			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
950				div_ant_conf.main_lna_conf =
951						HAL_ANT_DIV_COMB_LNA1;
952				div_ant_conf.alt_lna_conf =
953						HAL_ANT_DIV_COMB_LNA2;
954			}
955			goto div_comb_done;
956		}
957	}
958
959	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
960					   main_rssi_avg, alt_rssi_avg,
961					   alt_ratio);
962
963	antcomb->quick_scan_cnt++;
964
965div_comb_done:
966#if 0
967	ath_ant_div_conf_fast_divbias(&div_ant_conf);
968#endif
969
970	ath_ant_adjust_fast_divbias(antcomb,
971	    alt_ratio,
972	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
973	    div_ant_conf.antdiv_configgroup,
974	    &div_ant_conf);
975
976	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
977
978	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
979	   __func__, antcomb->total_pkt_count);
980
981	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
982	   __func__, antcomb->main_total_rssi);
983	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
984	   __func__, antcomb->alt_total_rssi);
985
986	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
987	   __func__, main_rssi_avg);
988	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
989	   __func__, alt_rssi_avg);
990
991	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
992	   __func__, antcomb->main_recv_cnt);
993	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
994	   __func__, antcomb->alt_recv_cnt);
995
996//	if (curr_alt_set != div_ant_conf.alt_lna_conf)
997		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
998		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
999//	if (curr_main_set != div_ant_conf.main_lna_conf)
1000		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1001		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
1002//	if (curr_bias != div_ant_conf.fast_div_bias)
1003		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1004		    __func__, curr_bias, div_ant_conf.fast_div_bias);
1005
1006	antcomb->scan_start_time = ticks;
1007	antcomb->total_pkt_count = 0;
1008	antcomb->main_total_rssi = 0;
1009	antcomb->alt_total_rssi = 0;
1010	antcomb->main_recv_cnt = 0;
1011	antcomb->alt_recv_cnt = 0;
1012}
1013
1014