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