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$
30 */
31#include <sys/cdefs.h>
32__FBSDID("$FreeBSD$");
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);
189		if (outdata == NULL) {
190			error = ENOMEM;
191			goto bad;
192		}
193	}
194	switch (id) {
195		default:
196			error = EINVAL;
197	}
198	if (outsize < ad->ad_out_size)
199		ad->ad_out_size = outsize;
200	if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
201		error = EFAULT;
202bad:
203	if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
204		free(indata, M_TEMP);
205	if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
206		free(outdata, M_TEMP);
207	return (error);
208}
209
210static HAL_BOOL
211ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
212    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
213{
214	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
215		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
216		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
217}
218
219static void
220ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
221    HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
222{
223	antcomb->quick_scan_cnt = 0;
224
225	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
226		antcomb->rssi_lna2 = main_rssi_avg;
227	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
228		antcomb->rssi_lna1 = main_rssi_avg;
229
230	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
231	case (0x10): /* LNA2 A-B */
232		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
233		antcomb->first_quick_scan_conf =
234			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
235		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
236		break;
237	case (0x20): /* LNA1 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_LNA2;
242		break;
243	case (0x21): /* LNA1 LNA2 */
244		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
245		antcomb->first_quick_scan_conf =
246			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
247		antcomb->second_quick_scan_conf =
248			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
249		break;
250	case (0x12): /* LNA2 LNA1 */
251		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
252		antcomb->first_quick_scan_conf =
253			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
254		antcomb->second_quick_scan_conf =
255			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
256		break;
257	case (0x13): /* LNA2 A+B */
258		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
259		antcomb->first_quick_scan_conf =
260			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
261		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
262		break;
263	case (0x23): /* LNA1 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_LNA2;
268		break;
269	default:
270		break;
271	}
272}
273
274static void
275ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
276    HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
277    int alt_rssi_avg, int alt_ratio)
278{
279	/* alt_good */
280	switch (antcomb->quick_scan_cnt) {
281	case 0:
282		/* set alt to main, and alt to first conf */
283		div_ant_conf->main_lna_conf = antcomb->main_conf;
284		div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
285		break;
286	case 1:
287		/* set alt to main, and alt to first conf */
288		div_ant_conf->main_lna_conf = antcomb->main_conf;
289		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
290		antcomb->rssi_first = main_rssi_avg;
291		antcomb->rssi_second = alt_rssi_avg;
292
293		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
294			/* main is LNA1 */
295			if (ath_is_alt_ant_ratio_better(alt_ratio,
296						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
297						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
298						main_rssi_avg, alt_rssi_avg,
299						antcomb->total_pkt_count))
300				antcomb->first_ratio = AH_TRUE;
301			else
302				antcomb->first_ratio = AH_FALSE;
303		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
304			if (ath_is_alt_ant_ratio_better(alt_ratio,
305						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
306						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
307						main_rssi_avg, alt_rssi_avg,
308						antcomb->total_pkt_count))
309				antcomb->first_ratio = AH_TRUE;
310			else
311				antcomb->first_ratio = AH_FALSE;
312		} else {
313			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
314			    (alt_rssi_avg > main_rssi_avg +
315			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
316			    (alt_rssi_avg > main_rssi_avg)) &&
317			    (antcomb->total_pkt_count > 50))
318				antcomb->first_ratio = AH_TRUE;
319			else
320				antcomb->first_ratio = AH_FALSE;
321		}
322		break;
323	case 2:
324		antcomb->alt_good = AH_FALSE;
325		antcomb->scan_not_start = AH_FALSE;
326		antcomb->scan = AH_FALSE;
327		antcomb->rssi_first = main_rssi_avg;
328		antcomb->rssi_third = alt_rssi_avg;
329
330		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
331			antcomb->rssi_lna1 = alt_rssi_avg;
332		else if (antcomb->second_quick_scan_conf ==
333			 HAL_ANT_DIV_COMB_LNA2)
334			antcomb->rssi_lna2 = alt_rssi_avg;
335		else if (antcomb->second_quick_scan_conf ==
336			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
337			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
338				antcomb->rssi_lna2 = main_rssi_avg;
339			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
340				antcomb->rssi_lna1 = main_rssi_avg;
341		}
342
343		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
344		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
345			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
346		else
347			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
348
349		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
350			if (ath_is_alt_ant_ratio_better(alt_ratio,
351						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
352						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
353						main_rssi_avg, alt_rssi_avg,
354						antcomb->total_pkt_count))
355				antcomb->second_ratio = AH_TRUE;
356			else
357				antcomb->second_ratio = AH_FALSE;
358		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
359			if (ath_is_alt_ant_ratio_better(alt_ratio,
360						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
361						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
362						main_rssi_avg, alt_rssi_avg,
363						antcomb->total_pkt_count))
364				antcomb->second_ratio = AH_TRUE;
365			else
366				antcomb->second_ratio = AH_FALSE;
367		} else {
368			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
369			    (alt_rssi_avg > main_rssi_avg +
370			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
371			    (alt_rssi_avg > main_rssi_avg)) &&
372			    (antcomb->total_pkt_count > 50))
373				antcomb->second_ratio = AH_TRUE;
374			else
375				antcomb->second_ratio = AH_FALSE;
376		}
377
378		/* set alt to the conf with maximun ratio */
379		if (antcomb->first_ratio && antcomb->second_ratio) {
380			if (antcomb->rssi_second > antcomb->rssi_third) {
381				/* first alt*/
382				if ((antcomb->first_quick_scan_conf ==
383				    HAL_ANT_DIV_COMB_LNA1) ||
384				    (antcomb->first_quick_scan_conf ==
385				    HAL_ANT_DIV_COMB_LNA2))
386					/* Set alt LNA1 or LNA2*/
387					if (div_ant_conf->main_lna_conf ==
388					    HAL_ANT_DIV_COMB_LNA2)
389						div_ant_conf->alt_lna_conf =
390							HAL_ANT_DIV_COMB_LNA1;
391					else
392						div_ant_conf->alt_lna_conf =
393							HAL_ANT_DIV_COMB_LNA2;
394				else
395					/* Set alt to A+B or A-B */
396					div_ant_conf->alt_lna_conf =
397						antcomb->first_quick_scan_conf;
398			} else if ((antcomb->second_quick_scan_conf ==
399				   HAL_ANT_DIV_COMB_LNA1) ||
400				   (antcomb->second_quick_scan_conf ==
401				   HAL_ANT_DIV_COMB_LNA2)) {
402				/* Set alt LNA1 or LNA2 */
403				if (div_ant_conf->main_lna_conf ==
404				    HAL_ANT_DIV_COMB_LNA2)
405					div_ant_conf->alt_lna_conf =
406						HAL_ANT_DIV_COMB_LNA1;
407				else
408					div_ant_conf->alt_lna_conf =
409						HAL_ANT_DIV_COMB_LNA2;
410			} else {
411				/* Set alt to A+B or A-B */
412				div_ant_conf->alt_lna_conf =
413					antcomb->second_quick_scan_conf;
414			}
415		} else if (antcomb->first_ratio) {
416			/* first alt */
417			if ((antcomb->first_quick_scan_conf ==
418			    HAL_ANT_DIV_COMB_LNA1) ||
419			    (antcomb->first_quick_scan_conf ==
420			    HAL_ANT_DIV_COMB_LNA2))
421					/* Set alt LNA1 or LNA2 */
422				if (div_ant_conf->main_lna_conf ==
423				    HAL_ANT_DIV_COMB_LNA2)
424					div_ant_conf->alt_lna_conf =
425							HAL_ANT_DIV_COMB_LNA1;
426				else
427					div_ant_conf->alt_lna_conf =
428							HAL_ANT_DIV_COMB_LNA2;
429			else
430				/* Set alt to A+B or A-B */
431				div_ant_conf->alt_lna_conf =
432						antcomb->first_quick_scan_conf;
433		} else if (antcomb->second_ratio) {
434				/* second alt */
435			if ((antcomb->second_quick_scan_conf ==
436			    HAL_ANT_DIV_COMB_LNA1) ||
437			    (antcomb->second_quick_scan_conf ==
438			    HAL_ANT_DIV_COMB_LNA2))
439				/* Set alt LNA1 or LNA2 */
440				if (div_ant_conf->main_lna_conf ==
441				    HAL_ANT_DIV_COMB_LNA2)
442					div_ant_conf->alt_lna_conf =
443						HAL_ANT_DIV_COMB_LNA1;
444				else
445					div_ant_conf->alt_lna_conf =
446						HAL_ANT_DIV_COMB_LNA2;
447			else
448				/* Set alt to A+B or A-B */
449				div_ant_conf->alt_lna_conf =
450						antcomb->second_quick_scan_conf;
451		} else {
452			/* main is largest */
453			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
454			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
455				/* Set alt LNA1 or LNA2 */
456				if (div_ant_conf->main_lna_conf ==
457				    HAL_ANT_DIV_COMB_LNA2)
458					div_ant_conf->alt_lna_conf =
459							HAL_ANT_DIV_COMB_LNA1;
460				else
461					div_ant_conf->alt_lna_conf =
462							HAL_ANT_DIV_COMB_LNA2;
463			else
464				/* Set alt to A+B or A-B */
465				div_ant_conf->alt_lna_conf = antcomb->main_conf;
466		}
467		break;
468	default:
469		break;
470	}
471}
472
473static void
474ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
475    int alt_ratio, int alt_ant_ratio_th, u_int config_group,
476    HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
477{
478
479	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
480		switch ((pdiv_ant_conf->main_lna_conf << 4)
481		    | pdiv_ant_conf->alt_lna_conf) {
482		case (0x01): //A-B LNA2
483			pdiv_ant_conf->fast_div_bias = 0x1;
484			pdiv_ant_conf->main_gaintb   = 0;
485			pdiv_ant_conf->alt_gaintb    = 0;
486			break;
487		case (0x02): //A-B LNA1
488			pdiv_ant_conf->fast_div_bias = 0x1;
489			pdiv_ant_conf->main_gaintb   = 0;
490			pdiv_ant_conf->alt_gaintb    = 0;
491			break;
492		case (0x03): //A-B A+B
493			pdiv_ant_conf->fast_div_bias = 0x1;
494			pdiv_ant_conf->main_gaintb   = 0;
495			pdiv_ant_conf->alt_gaintb    = 0;
496			break;
497		case (0x10): //LNA2 A-B
498			if ((antcomb->scan == 0)
499			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
500				pdiv_ant_conf->fast_div_bias = 0x3f;
501			} else {
502				pdiv_ant_conf->fast_div_bias = 0x1;
503			}
504			pdiv_ant_conf->main_gaintb   = 0;
505			pdiv_ant_conf->alt_gaintb    = 0;
506			break;
507		case (0x12): //LNA2 LNA1
508			pdiv_ant_conf->fast_div_bias = 0x1;
509			pdiv_ant_conf->main_gaintb   = 0;
510			pdiv_ant_conf->alt_gaintb    = 0;
511			break;
512			case (0x13): //LNA2 A+B
513			if ((antcomb->scan == 0)
514			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
515				pdiv_ant_conf->fast_div_bias = 0x3f;
516			} else {
517				pdiv_ant_conf->fast_div_bias = 0x1;
518			}
519			pdiv_ant_conf->main_gaintb   = 0;
520			pdiv_ant_conf->alt_gaintb    = 0;
521			break;
522		case (0x20): //LNA1 A-B
523			if ((antcomb->scan == 0)
524			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
525				pdiv_ant_conf->fast_div_bias = 0x3f;
526			} else {
527				pdiv_ant_conf->fast_div_bias = 0x1;
528			}
529			pdiv_ant_conf->main_gaintb   = 0;
530			pdiv_ant_conf->alt_gaintb    = 0;
531			break;
532		case (0x21): //LNA1 LNA2
533			pdiv_ant_conf->fast_div_bias = 0x1;
534			pdiv_ant_conf->main_gaintb   = 0;
535			pdiv_ant_conf->alt_gaintb    = 0;
536			break;
537		case (0x23): //LNA1 A+B
538			if ((antcomb->scan == 0)
539			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
540				pdiv_ant_conf->fast_div_bias = 0x3f;
541			} else {
542				pdiv_ant_conf->fast_div_bias = 0x1;
543			}
544			pdiv_ant_conf->main_gaintb   = 0;
545			pdiv_ant_conf->alt_gaintb    = 0;
546			break;
547		case (0x30): //A+B A-B
548			pdiv_ant_conf->fast_div_bias = 0x1;
549			pdiv_ant_conf->main_gaintb   = 0;
550			pdiv_ant_conf->alt_gaintb    = 0;
551			break;
552		case (0x31): //A+B LNA2
553			pdiv_ant_conf->fast_div_bias = 0x1;
554			pdiv_ant_conf->main_gaintb   = 0;
555			pdiv_ant_conf->alt_gaintb    = 0;
556			break;
557		case (0x32): //A+B LNA1
558			pdiv_ant_conf->fast_div_bias = 0x1;
559			pdiv_ant_conf->main_gaintb   = 0;
560			pdiv_ant_conf->alt_gaintb    = 0;
561			break;
562		default:
563			break;
564		}
565	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
566		switch ((pdiv_ant_conf->main_lna_conf << 4)
567		    | pdiv_ant_conf->alt_lna_conf) {
568		case (0x01): //A-B LNA2
569			pdiv_ant_conf->fast_div_bias = 0x1;
570			pdiv_ant_conf->main_gaintb   = 0;
571			pdiv_ant_conf->alt_gaintb    = 0;
572			break;
573		case (0x02): //A-B LNA1
574			pdiv_ant_conf->fast_div_bias = 0x1;
575			pdiv_ant_conf->main_gaintb   = 0;
576			pdiv_ant_conf->alt_gaintb    = 0;
577			break;
578		case (0x03): //A-B A+B
579			pdiv_ant_conf->fast_div_bias = 0x1;
580			pdiv_ant_conf->main_gaintb   = 0;
581			pdiv_ant_conf->alt_gaintb    = 0;
582			break;
583		case (0x10): //LNA2 A-B
584			if ((antcomb->scan == 0)
585			    && (alt_ratio > alt_ant_ratio_th)) {
586				pdiv_ant_conf->fast_div_bias = 0x1;
587			} else {
588				pdiv_ant_conf->fast_div_bias = 0x2;
589			}
590			pdiv_ant_conf->main_gaintb   = 0;
591			pdiv_ant_conf->alt_gaintb    = 0;
592			break;
593		case (0x12): //LNA2 LNA1
594			pdiv_ant_conf->fast_div_bias = 0x1;
595			pdiv_ant_conf->main_gaintb   = 0;
596			pdiv_ant_conf->alt_gaintb    = 0;
597			break;
598		case (0x13): //LNA2 A+B
599			if ((antcomb->scan == 0)
600			    && (alt_ratio > alt_ant_ratio_th)) {
601				pdiv_ant_conf->fast_div_bias = 0x1;
602			} else {
603				pdiv_ant_conf->fast_div_bias = 0x2;
604			}
605			pdiv_ant_conf->main_gaintb   = 0;
606			pdiv_ant_conf->alt_gaintb    = 0;
607			break;
608		case (0x20): //LNA1 A-B
609			if ((antcomb->scan == 0)
610			    && (alt_ratio > alt_ant_ratio_th)) {
611				pdiv_ant_conf->fast_div_bias = 0x1;
612			} else {
613				pdiv_ant_conf->fast_div_bias = 0x2;
614			}
615			pdiv_ant_conf->main_gaintb   = 0;
616			pdiv_ant_conf->alt_gaintb    = 0;
617			break;
618		case (0x21): //LNA1 LNA2
619			pdiv_ant_conf->fast_div_bias = 0x1;
620			pdiv_ant_conf->main_gaintb   = 0;
621			pdiv_ant_conf->alt_gaintb    = 0;
622			break;
623		case (0x23): //LNA1 A+B
624			if ((antcomb->scan == 0)
625			    && (alt_ratio > alt_ant_ratio_th)) {
626				pdiv_ant_conf->fast_div_bias = 0x1;
627			} else {
628				pdiv_ant_conf->fast_div_bias = 0x2;
629			}
630			pdiv_ant_conf->main_gaintb   = 0;
631			pdiv_ant_conf->alt_gaintb    = 0;
632			break;
633		case (0x30): //A+B A-B
634			pdiv_ant_conf->fast_div_bias = 0x1;
635			pdiv_ant_conf->main_gaintb   = 0;
636			pdiv_ant_conf->alt_gaintb    = 0;
637			break;
638		case (0x31): //A+B LNA2
639			pdiv_ant_conf->fast_div_bias = 0x1;
640			pdiv_ant_conf->main_gaintb   = 0;
641			pdiv_ant_conf->alt_gaintb    = 0;
642			break;
643		case (0x32): //A+B LNA1
644			pdiv_ant_conf->fast_div_bias = 0x1;
645			pdiv_ant_conf->main_gaintb   = 0;
646			pdiv_ant_conf->alt_gaintb    = 0;
647			break;
648		default:
649			break;
650		}
651	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
652		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
653		case (0x01): //A-B LNA2
654			pdiv_ant_conf->fast_div_bias = 0x3b;
655			break;
656		case (0x02): //A-B LNA1
657			pdiv_ant_conf->fast_div_bias = 0x3d;
658			break;
659		case (0x03): //A-B A+B
660			pdiv_ant_conf->fast_div_bias = 0x1;
661			break;
662		case (0x10): //LNA2 A-B
663			pdiv_ant_conf->fast_div_bias = 0x7;
664			break;
665		case (0x12): //LNA2 LNA1
666			pdiv_ant_conf->fast_div_bias = 0x2;
667			break;
668		case (0x13): //LNA2 A+B
669			pdiv_ant_conf->fast_div_bias = 0x7;
670			break;
671		case (0x20): //LNA1 A-B
672			pdiv_ant_conf->fast_div_bias = 0x6;
673			break;
674		case (0x21): //LNA1 LNA2
675			pdiv_ant_conf->fast_div_bias = 0x0;
676			break;
677		case (0x23): //LNA1 A+B
678			pdiv_ant_conf->fast_div_bias = 0x6;
679			break;
680		case (0x30): //A+B A-B
681			pdiv_ant_conf->fast_div_bias = 0x1;
682			break;
683		case (0x31): //A+B LNA2
684			pdiv_ant_conf->fast_div_bias = 0x3b;
685			break;
686		case (0x32): //A+B LNA1
687			pdiv_ant_conf->fast_div_bias = 0x3d;
688			break;
689		default:
690			break;
691		}
692	}
693}
694
695/*
696 * AR9485/AR933x TODO:
697 * + Select a ratio based on whether RSSI is low or not; but I need
698 *   to figure out what "low_rssi_th" is sourced from.
699 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
700 * + .. and there's likely a bunch of other things to include in this.
701 */
702
703/* Antenna diversity and combining */
704void
705ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
706    unsigned long ticks, int hz)
707{
708	HAL_ANT_COMB_CONFIG div_ant_conf;
709	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
710	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
711	int curr_main_set, curr_bias;
712	int main_rssi = rs->rs_rssi_ctl[0];
713	int alt_rssi = rs->rs_rssi_ctl[1];
714	int rx_ant_conf, main_ant_conf, alt_ant_conf;
715	HAL_BOOL short_scan = AH_FALSE;
716
717	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
718	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
719	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
720
721#if 0
722	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
723	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
724	    "FastDiv: %d\n",
725	    __func__,
726	    main_rssi,
727	    alt_rssi,
728	    main_ant_conf,
729	    alt_ant_conf,
730	    rx_ant_conf,
731	    !!(rs->rs_rssi_ctl[2] & 0x80),
732	    !!(rs->rs_rssi_ctl[2] & 0x40),
733	    !!(rs->rs_rssi_ext[2] & 0x40));
734#endif
735
736	/*
737	 * If LNA diversity combining isn't enabled, don't run this.
738	 */
739	if (! sc->sc_dolnadiv)
740		return;
741
742	/*
743	 * XXX this is ugly, but the HAL code attaches the
744	 * LNA diversity to the TX antenna settings.
745	 * I don't know why.
746	 */
747	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
748		return;
749
750	/* Record packet only when alt_rssi is positive */
751	if (main_rssi > 0 && alt_rssi > 0) {
752		antcomb->total_pkt_count++;
753		antcomb->main_total_rssi += main_rssi;
754		antcomb->alt_total_rssi  += alt_rssi;
755		if (main_ant_conf == rx_ant_conf)
756			antcomb->main_recv_cnt++;
757		else
758			antcomb->alt_recv_cnt++;
759	}
760
761	/* Short scan check */
762	if (antcomb->scan && antcomb->alt_good) {
763		if (time_after(ticks, antcomb->scan_start_time +
764		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
765			short_scan = AH_TRUE;
766		else
767			if (antcomb->total_pkt_count ==
768			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
769				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
770					    antcomb->total_pkt_count);
771				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
772					short_scan = AH_TRUE;
773			}
774	}
775
776#if 0
777	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
778	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
779	    __func__,
780	    antcomb->total_pkt_count,
781	    !! (rs->rs_moreaggr),
782	    !! (short_scan));
783#endif
784
785	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
786	    rs->rs_moreaggr) && !short_scan)
787		return;
788
789	if (antcomb->total_pkt_count) {
790		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
791			     antcomb->total_pkt_count);
792		main_rssi_avg = (antcomb->main_total_rssi /
793				 antcomb->total_pkt_count);
794		alt_rssi_avg = (antcomb->alt_total_rssi /
795				 antcomb->total_pkt_count);
796	}
797
798	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
799
800	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
801	curr_alt_set = div_ant_conf.alt_lna_conf;
802	curr_main_set = div_ant_conf.main_lna_conf;
803	curr_bias = div_ant_conf.fast_div_bias;
804
805	antcomb->count++;
806
807	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
808		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
809			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
810						  main_rssi_avg);
811			antcomb->alt_good = AH_TRUE;
812		} else {
813			antcomb->alt_good = AH_FALSE;
814		}
815
816		antcomb->count = 0;
817		antcomb->scan = AH_TRUE;
818		antcomb->scan_not_start = AH_TRUE;
819	}
820
821	if (!antcomb->scan) {
822		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
823			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
824				/* Switch main and alt LNA */
825				div_ant_conf.main_lna_conf =
826						HAL_ANT_DIV_COMB_LNA2;
827				div_ant_conf.alt_lna_conf  =
828						HAL_ANT_DIV_COMB_LNA1;
829			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
830				div_ant_conf.main_lna_conf =
831						HAL_ANT_DIV_COMB_LNA1;
832				div_ant_conf.alt_lna_conf  =
833						HAL_ANT_DIV_COMB_LNA2;
834			}
835
836			goto div_comb_done;
837		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
838			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
839			/* Set alt to another LNA */
840			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
841				div_ant_conf.alt_lna_conf =
842						HAL_ANT_DIV_COMB_LNA1;
843			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
844				div_ant_conf.alt_lna_conf =
845						HAL_ANT_DIV_COMB_LNA2;
846
847			goto div_comb_done;
848		}
849
850		if ((alt_rssi_avg < (main_rssi_avg +
851		    antcomb->lna1_lna2_delta)))
852			goto div_comb_done;
853	}
854
855	if (!antcomb->scan_not_start) {
856		switch (curr_alt_set) {
857		case HAL_ANT_DIV_COMB_LNA2:
858			antcomb->rssi_lna2 = alt_rssi_avg;
859			antcomb->rssi_lna1 = main_rssi_avg;
860			antcomb->scan = AH_TRUE;
861			/* set to A+B */
862			div_ant_conf.main_lna_conf =
863				HAL_ANT_DIV_COMB_LNA1;
864			div_ant_conf.alt_lna_conf  =
865				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
866			break;
867		case HAL_ANT_DIV_COMB_LNA1:
868			antcomb->rssi_lna1 = alt_rssi_avg;
869			antcomb->rssi_lna2 = main_rssi_avg;
870			antcomb->scan = AH_TRUE;
871			/* set to A+B */
872			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
873			div_ant_conf.alt_lna_conf  =
874				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
875			break;
876		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
877			antcomb->rssi_add = alt_rssi_avg;
878			antcomb->scan = AH_TRUE;
879			/* set to A-B */
880			div_ant_conf.alt_lna_conf =
881				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
882			break;
883		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
884			antcomb->rssi_sub = alt_rssi_avg;
885			antcomb->scan = AH_FALSE;
886			if (antcomb->rssi_lna2 >
887			    (antcomb->rssi_lna1 +
888			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
889				/* use LNA2 as main LNA */
890				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
891				    (antcomb->rssi_add > antcomb->rssi_sub)) {
892					/* set to A+B */
893					div_ant_conf.main_lna_conf =
894						HAL_ANT_DIV_COMB_LNA2;
895					div_ant_conf.alt_lna_conf  =
896						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
897				} else if (antcomb->rssi_sub >
898					   antcomb->rssi_lna1) {
899					/* set to A-B */
900					div_ant_conf.main_lna_conf =
901						HAL_ANT_DIV_COMB_LNA2;
902					div_ant_conf.alt_lna_conf =
903						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
904				} else {
905					/* set to LNA1 */
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;
910				}
911			} else {
912				/* use LNA1 as main LNA */
913				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
914				    (antcomb->rssi_add > antcomb->rssi_sub)) {
915					/* set to A+B */
916					div_ant_conf.main_lna_conf =
917						HAL_ANT_DIV_COMB_LNA1;
918					div_ant_conf.alt_lna_conf  =
919						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
920				} else if (antcomb->rssi_sub >
921					   antcomb->rssi_lna1) {
922					/* set to A-B */
923					div_ant_conf.main_lna_conf =
924						HAL_ANT_DIV_COMB_LNA1;
925					div_ant_conf.alt_lna_conf =
926						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
927				} else {
928					/* set to LNA2 */
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_LNA2;
933				}
934			}
935			break;
936		default:
937			break;
938		}
939	} else {
940		if (!antcomb->alt_good) {
941			antcomb->scan_not_start = AH_FALSE;
942			/* Set alt to another LNA */
943			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
944				div_ant_conf.main_lna_conf =
945						HAL_ANT_DIV_COMB_LNA2;
946				div_ant_conf.alt_lna_conf =
947						HAL_ANT_DIV_COMB_LNA1;
948			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
949				div_ant_conf.main_lna_conf =
950						HAL_ANT_DIV_COMB_LNA1;
951				div_ant_conf.alt_lna_conf =
952						HAL_ANT_DIV_COMB_LNA2;
953			}
954			goto div_comb_done;
955		}
956	}
957
958	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
959					   main_rssi_avg, alt_rssi_avg,
960					   alt_ratio);
961
962	antcomb->quick_scan_cnt++;
963
964div_comb_done:
965#if 0
966	ath_ant_div_conf_fast_divbias(&div_ant_conf);
967#endif
968
969	ath_ant_adjust_fast_divbias(antcomb,
970	    alt_ratio,
971	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
972	    div_ant_conf.antdiv_configgroup,
973	    &div_ant_conf);
974
975	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
976
977	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
978	   __func__, antcomb->total_pkt_count);
979
980	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
981	   __func__, antcomb->main_total_rssi);
982	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
983	   __func__, antcomb->alt_total_rssi);
984
985	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
986	   __func__, main_rssi_avg);
987	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
988	   __func__, alt_rssi_avg);
989
990	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
991	   __func__, antcomb->main_recv_cnt);
992	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
993	   __func__, antcomb->alt_recv_cnt);
994
995//	if (curr_alt_set != div_ant_conf.alt_lna_conf)
996		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
997		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
998//	if (curr_main_set != div_ant_conf.main_lna_conf)
999		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1000		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
1001//	if (curr_bias != div_ant_conf.fast_div_bias)
1002		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1003		    __func__, curr_bias, div_ant_conf.fast_div_bias);
1004
1005	antcomb->scan_start_time = ticks;
1006	antcomb->total_pkt_count = 0;
1007	antcomb->main_total_rssi = 0;
1008	antcomb->alt_total_rssi = 0;
1009	antcomb->main_recv_cnt = 0;
1010	antcomb->alt_recv_cnt = 0;
1011}
1012
1013