1/*-
2 * SPDX-License-Identifier: BSD-2-Clause
3 *
4 * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 *    notice, this list of conditions and the following disclaimer,
12 *    without modification.
13 * 2. Redistributions in binary form must reproduce at minimum a disclaimer
14 *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
15 *    redistribution must be conditioned upon including a substantially
16 *    similar Disclaimer requirement for further binary redistribution.
17 *
18 * NO WARRANTY
19 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21 * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
22 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
23 * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
24 * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
27 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
29 * THE POSSIBILITY OF SUCH DAMAGES.
30 */
31#include <sys/cdefs.h>
32/*
33 * This module handles LNA diversity for those chips which implement LNA
34 * mixing (AR9285/AR9485.)
35 */
36#include "opt_ath.h"
37#include "opt_inet.h"
38#include "opt_wlan.h"
39
40#include <sys/param.h>
41#include <sys/systm.h>
42#include <sys/sysctl.h>
43#include <sys/kernel.h>
44#include <sys/lock.h>
45#include <sys/malloc.h>
46#include <sys/mutex.h>
47#include <sys/errno.h>
48
49#include <machine/bus.h>
50#include <machine/resource.h>
51#include <sys/bus.h>
52
53#include <sys/socket.h>
54
55#include <net/if.h>
56#include <net/if_var.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 compatibility 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
211/*
212 * XXX need to low_rssi_thresh config from ath9k, to support CUS198
213 * antenna diversity correctly.
214 */
215static HAL_BOOL
216ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
217    int main_rssi_avg, int alt_rssi_avg, int pkt_count)
218{
219	return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
220		(alt_rssi_avg > main_rssi_avg + maxdelta)) ||
221		(alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
222}
223
224static void
225ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
226    HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
227{
228	antcomb->quick_scan_cnt = 0;
229
230	if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
231		antcomb->rssi_lna2 = main_rssi_avg;
232	else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
233		antcomb->rssi_lna1 = main_rssi_avg;
234
235	switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
236	case (0x10): /* LNA2 A-B */
237		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
238		antcomb->first_quick_scan_conf =
239			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
240		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
241		break;
242	case (0x20): /* LNA1 A-B */
243		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
244		antcomb->first_quick_scan_conf =
245			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
246		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
247		break;
248	case (0x21): /* LNA1 LNA2 */
249		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
250		antcomb->first_quick_scan_conf =
251			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
252		antcomb->second_quick_scan_conf =
253			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
254		break;
255	case (0x12): /* LNA2 LNA1 */
256		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
257		antcomb->first_quick_scan_conf =
258			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
259		antcomb->second_quick_scan_conf =
260			HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
261		break;
262	case (0x13): /* LNA2 A+B */
263		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
264		antcomb->first_quick_scan_conf =
265			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
266		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
267		break;
268	case (0x23): /* LNA1 A+B */
269		antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
270		antcomb->first_quick_scan_conf =
271			HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
272		antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
273		break;
274	default:
275		break;
276	}
277}
278
279static void
280ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
281    HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
282    int alt_rssi_avg, int alt_ratio)
283{
284	/* alt_good */
285	switch (antcomb->quick_scan_cnt) {
286	case 0:
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->first_quick_scan_conf;
290		break;
291	case 1:
292		/* set alt to main, and alt to first conf */
293		div_ant_conf->main_lna_conf = antcomb->main_conf;
294		div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
295		antcomb->rssi_first = main_rssi_avg;
296		antcomb->rssi_second = alt_rssi_avg;
297
298		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
299			/* main is LNA1 */
300			if (ath_is_alt_ant_ratio_better(alt_ratio,
301						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
302						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
303						main_rssi_avg, alt_rssi_avg,
304						antcomb->total_pkt_count))
305				antcomb->first_ratio = AH_TRUE;
306			else
307				antcomb->first_ratio = AH_FALSE;
308		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
309			if (ath_is_alt_ant_ratio_better(alt_ratio,
310						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
311						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
312						main_rssi_avg, alt_rssi_avg,
313						antcomb->total_pkt_count))
314				antcomb->first_ratio = AH_TRUE;
315			else
316				antcomb->first_ratio = AH_FALSE;
317		} else {
318			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
319			    (alt_rssi_avg > main_rssi_avg +
320			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
321			    (alt_rssi_avg > main_rssi_avg)) &&
322			    (antcomb->total_pkt_count > 50))
323				antcomb->first_ratio = AH_TRUE;
324			else
325				antcomb->first_ratio = AH_FALSE;
326		}
327		break;
328	case 2:
329		antcomb->alt_good = AH_FALSE;
330		antcomb->scan_not_start = AH_FALSE;
331		antcomb->scan = AH_FALSE;
332		antcomb->rssi_first = main_rssi_avg;
333		antcomb->rssi_third = alt_rssi_avg;
334
335		if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
336			antcomb->rssi_lna1 = alt_rssi_avg;
337		else if (antcomb->second_quick_scan_conf ==
338			 HAL_ANT_DIV_COMB_LNA2)
339			antcomb->rssi_lna2 = alt_rssi_avg;
340		else if (antcomb->second_quick_scan_conf ==
341			 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
342			if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
343				antcomb->rssi_lna2 = main_rssi_avg;
344			else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
345				antcomb->rssi_lna1 = main_rssi_avg;
346		}
347
348		if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
349		    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
350			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
351		else
352			div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
353
354		if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
355			if (ath_is_alt_ant_ratio_better(alt_ratio,
356						ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
357						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
358						main_rssi_avg, alt_rssi_avg,
359						antcomb->total_pkt_count))
360				antcomb->second_ratio = AH_TRUE;
361			else
362				antcomb->second_ratio = AH_FALSE;
363		} else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
364			if (ath_is_alt_ant_ratio_better(alt_ratio,
365						ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
366						ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
367						main_rssi_avg, alt_rssi_avg,
368						antcomb->total_pkt_count))
369				antcomb->second_ratio = AH_TRUE;
370			else
371				antcomb->second_ratio = AH_FALSE;
372		} else {
373			if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
374			    (alt_rssi_avg > main_rssi_avg +
375			    ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
376			    (alt_rssi_avg > main_rssi_avg)) &&
377			    (antcomb->total_pkt_count > 50))
378				antcomb->second_ratio = AH_TRUE;
379			else
380				antcomb->second_ratio = AH_FALSE;
381		}
382
383		/* set alt to the conf with maximun ratio */
384		if (antcomb->first_ratio && antcomb->second_ratio) {
385			if (antcomb->rssi_second > antcomb->rssi_third) {
386				/* first alt*/
387				if ((antcomb->first_quick_scan_conf ==
388				    HAL_ANT_DIV_COMB_LNA1) ||
389				    (antcomb->first_quick_scan_conf ==
390				    HAL_ANT_DIV_COMB_LNA2))
391					/* Set alt LNA1 or LNA2*/
392					if (div_ant_conf->main_lna_conf ==
393					    HAL_ANT_DIV_COMB_LNA2)
394						div_ant_conf->alt_lna_conf =
395							HAL_ANT_DIV_COMB_LNA1;
396					else
397						div_ant_conf->alt_lna_conf =
398							HAL_ANT_DIV_COMB_LNA2;
399				else
400					/* Set alt to A+B or A-B */
401					div_ant_conf->alt_lna_conf =
402						antcomb->first_quick_scan_conf;
403			} else if ((antcomb->second_quick_scan_conf ==
404				   HAL_ANT_DIV_COMB_LNA1) ||
405				   (antcomb->second_quick_scan_conf ==
406				   HAL_ANT_DIV_COMB_LNA2)) {
407				/* Set alt LNA1 or LNA2 */
408				if (div_ant_conf->main_lna_conf ==
409				    HAL_ANT_DIV_COMB_LNA2)
410					div_ant_conf->alt_lna_conf =
411						HAL_ANT_DIV_COMB_LNA1;
412				else
413					div_ant_conf->alt_lna_conf =
414						HAL_ANT_DIV_COMB_LNA2;
415			} else {
416				/* Set alt to A+B or A-B */
417				div_ant_conf->alt_lna_conf =
418					antcomb->second_quick_scan_conf;
419			}
420		} else if (antcomb->first_ratio) {
421			/* first alt */
422			if ((antcomb->first_quick_scan_conf ==
423			    HAL_ANT_DIV_COMB_LNA1) ||
424			    (antcomb->first_quick_scan_conf ==
425			    HAL_ANT_DIV_COMB_LNA2))
426					/* Set alt LNA1 or LNA2 */
427				if (div_ant_conf->main_lna_conf ==
428				    HAL_ANT_DIV_COMB_LNA2)
429					div_ant_conf->alt_lna_conf =
430							HAL_ANT_DIV_COMB_LNA1;
431				else
432					div_ant_conf->alt_lna_conf =
433							HAL_ANT_DIV_COMB_LNA2;
434			else
435				/* Set alt to A+B or A-B */
436				div_ant_conf->alt_lna_conf =
437						antcomb->first_quick_scan_conf;
438		} else if (antcomb->second_ratio) {
439				/* second alt */
440			if ((antcomb->second_quick_scan_conf ==
441			    HAL_ANT_DIV_COMB_LNA1) ||
442			    (antcomb->second_quick_scan_conf ==
443			    HAL_ANT_DIV_COMB_LNA2))
444				/* Set alt LNA1 or LNA2 */
445				if (div_ant_conf->main_lna_conf ==
446				    HAL_ANT_DIV_COMB_LNA2)
447					div_ant_conf->alt_lna_conf =
448						HAL_ANT_DIV_COMB_LNA1;
449				else
450					div_ant_conf->alt_lna_conf =
451						HAL_ANT_DIV_COMB_LNA2;
452			else
453				/* Set alt to A+B or A-B */
454				div_ant_conf->alt_lna_conf =
455						antcomb->second_quick_scan_conf;
456		} else {
457			/* main is largest */
458			if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
459			    (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
460				/* Set alt LNA1 or LNA2 */
461				if (div_ant_conf->main_lna_conf ==
462				    HAL_ANT_DIV_COMB_LNA2)
463					div_ant_conf->alt_lna_conf =
464							HAL_ANT_DIV_COMB_LNA1;
465				else
466					div_ant_conf->alt_lna_conf =
467							HAL_ANT_DIV_COMB_LNA2;
468			else
469				/* Set alt to A+B or A-B */
470				div_ant_conf->alt_lna_conf = antcomb->main_conf;
471		}
472		break;
473	default:
474		break;
475	}
476}
477
478static void
479ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
480    int alt_ratio, int alt_ant_ratio_th, u_int config_group,
481    HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
482{
483
484	if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
485		switch ((pdiv_ant_conf->main_lna_conf << 4)
486		    | pdiv_ant_conf->alt_lna_conf) {
487		case (0x01): //A-B LNA2
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 (0x02): //A-B LNA1
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 (0x03): //A-B A+B
498			pdiv_ant_conf->fast_div_bias = 0x1;
499			pdiv_ant_conf->main_gaintb   = 0;
500			pdiv_ant_conf->alt_gaintb    = 0;
501			break;
502		case (0x10): //LNA2 A-B
503			if ((antcomb->scan == 0)
504			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
505				pdiv_ant_conf->fast_div_bias = 0x3f;
506			} else {
507				pdiv_ant_conf->fast_div_bias = 0x1;
508			}
509			pdiv_ant_conf->main_gaintb   = 0;
510			pdiv_ant_conf->alt_gaintb    = 0;
511			break;
512		case (0x12): //LNA2 LNA1
513			pdiv_ant_conf->fast_div_bias = 0x1;
514			pdiv_ant_conf->main_gaintb   = 0;
515			pdiv_ant_conf->alt_gaintb    = 0;
516			break;
517			case (0x13): //LNA2 A+B
518			if ((antcomb->scan == 0)
519			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
520				pdiv_ant_conf->fast_div_bias = 0x3f;
521			} else {
522				pdiv_ant_conf->fast_div_bias = 0x1;
523			}
524			pdiv_ant_conf->main_gaintb   = 0;
525			pdiv_ant_conf->alt_gaintb    = 0;
526			break;
527		case (0x20): //LNA1 A-B
528			if ((antcomb->scan == 0)
529			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
530				pdiv_ant_conf->fast_div_bias = 0x3f;
531			} else {
532				pdiv_ant_conf->fast_div_bias = 0x1;
533			}
534			pdiv_ant_conf->main_gaintb   = 0;
535			pdiv_ant_conf->alt_gaintb    = 0;
536			break;
537		case (0x21): //LNA1 LNA2
538			pdiv_ant_conf->fast_div_bias = 0x1;
539			pdiv_ant_conf->main_gaintb   = 0;
540			pdiv_ant_conf->alt_gaintb    = 0;
541			break;
542		case (0x23): //LNA1 A+B
543			if ((antcomb->scan == 0)
544			    && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
545				pdiv_ant_conf->fast_div_bias = 0x3f;
546			} else {
547				pdiv_ant_conf->fast_div_bias = 0x1;
548			}
549			pdiv_ant_conf->main_gaintb   = 0;
550			pdiv_ant_conf->alt_gaintb    = 0;
551			break;
552		case (0x30): //A+B A-B
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 (0x31): //A+B LNA2
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		case (0x32): //A+B LNA1
563			pdiv_ant_conf->fast_div_bias = 0x1;
564			pdiv_ant_conf->main_gaintb   = 0;
565			pdiv_ant_conf->alt_gaintb    = 0;
566			break;
567		default:
568			break;
569		}
570	} else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
571		switch ((pdiv_ant_conf->main_lna_conf << 4)
572		    | pdiv_ant_conf->alt_lna_conf) {
573		case (0x01): //A-B LNA2
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 (0x02): //A-B LNA1
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 (0x03): //A-B A+B
584			pdiv_ant_conf->fast_div_bias = 0x1;
585			pdiv_ant_conf->main_gaintb   = 0;
586			pdiv_ant_conf->alt_gaintb    = 0;
587			break;
588		case (0x10): //LNA2 A-B
589			if ((antcomb->scan == 0)
590			    && (alt_ratio > alt_ant_ratio_th)) {
591				pdiv_ant_conf->fast_div_bias = 0x1;
592			} else {
593				pdiv_ant_conf->fast_div_bias = 0x2;
594			}
595			pdiv_ant_conf->main_gaintb   = 0;
596			pdiv_ant_conf->alt_gaintb    = 0;
597			break;
598		case (0x12): //LNA2 LNA1
599			pdiv_ant_conf->fast_div_bias = 0x1;
600			pdiv_ant_conf->main_gaintb   = 0;
601			pdiv_ant_conf->alt_gaintb    = 0;
602			break;
603		case (0x13): //LNA2 A+B
604			if ((antcomb->scan == 0)
605			    && (alt_ratio > alt_ant_ratio_th)) {
606				pdiv_ant_conf->fast_div_bias = 0x1;
607			} else {
608				pdiv_ant_conf->fast_div_bias = 0x2;
609			}
610			pdiv_ant_conf->main_gaintb   = 0;
611			pdiv_ant_conf->alt_gaintb    = 0;
612			break;
613		case (0x20): //LNA1 A-B
614			if ((antcomb->scan == 0)
615			    && (alt_ratio > alt_ant_ratio_th)) {
616				pdiv_ant_conf->fast_div_bias = 0x1;
617			} else {
618				pdiv_ant_conf->fast_div_bias = 0x2;
619			}
620			pdiv_ant_conf->main_gaintb   = 0;
621			pdiv_ant_conf->alt_gaintb    = 0;
622			break;
623		case (0x21): //LNA1 LNA2
624			pdiv_ant_conf->fast_div_bias = 0x1;
625			pdiv_ant_conf->main_gaintb   = 0;
626			pdiv_ant_conf->alt_gaintb    = 0;
627			break;
628		case (0x23): //LNA1 A+B
629			if ((antcomb->scan == 0)
630			    && (alt_ratio > alt_ant_ratio_th)) {
631				pdiv_ant_conf->fast_div_bias = 0x1;
632			} else {
633				pdiv_ant_conf->fast_div_bias = 0x2;
634			}
635			pdiv_ant_conf->main_gaintb   = 0;
636			pdiv_ant_conf->alt_gaintb    = 0;
637			break;
638		case (0x30): //A+B A-B
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 (0x31): //A+B LNA2
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		case (0x32): //A+B LNA1
649			pdiv_ant_conf->fast_div_bias = 0x1;
650			pdiv_ant_conf->main_gaintb   = 0;
651			pdiv_ant_conf->alt_gaintb    = 0;
652			break;
653		default:
654			break;
655		}
656	} else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
657		switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
658		case (0x01): //A-B LNA2
659			pdiv_ant_conf->fast_div_bias = 0x3b;
660			break;
661		case (0x02): //A-B LNA1
662			pdiv_ant_conf->fast_div_bias = 0x3d;
663			break;
664		case (0x03): //A-B A+B
665			pdiv_ant_conf->fast_div_bias = 0x1;
666			break;
667		case (0x10): //LNA2 A-B
668			pdiv_ant_conf->fast_div_bias = 0x7;
669			break;
670		case (0x12): //LNA2 LNA1
671			pdiv_ant_conf->fast_div_bias = 0x2;
672			break;
673		case (0x13): //LNA2 A+B
674			pdiv_ant_conf->fast_div_bias = 0x7;
675			break;
676		case (0x20): //LNA1 A-B
677			pdiv_ant_conf->fast_div_bias = 0x6;
678			break;
679		case (0x21): //LNA1 LNA2
680			pdiv_ant_conf->fast_div_bias = 0x0;
681			break;
682		case (0x23): //LNA1 A+B
683			pdiv_ant_conf->fast_div_bias = 0x6;
684			break;
685		case (0x30): //A+B A-B
686			pdiv_ant_conf->fast_div_bias = 0x1;
687			break;
688		case (0x31): //A+B LNA2
689			pdiv_ant_conf->fast_div_bias = 0x3b;
690			break;
691		case (0x32): //A+B LNA1
692			pdiv_ant_conf->fast_div_bias = 0x3d;
693			break;
694		default:
695			break;
696		}
697	}
698}
699
700/*
701 * AR9485/AR933x TODO:
702 * + Select a ratio based on whether RSSI is low or not; but I need
703 *   to figure out what "low_rssi_th" is sourced from.
704 * + What's ath_ant_div_comb_alt_check() in the reference driver do?
705 * + .. and there's likely a bunch of other things to include in this.
706 */
707
708/* Antenna diversity and combining */
709void
710ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
711    unsigned long ticks, int hz)
712{
713	HAL_ANT_COMB_CONFIG div_ant_conf;
714	struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
715	int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
716	int curr_main_set, curr_bias;
717	int main_rssi = rs->rs_rssi_ctl[0];
718	int alt_rssi = rs->rs_rssi_ctl[1];
719	int rx_ant_conf, main_ant_conf, alt_ant_conf;
720	HAL_BOOL short_scan = AH_FALSE;
721
722	rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
723	main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
724	alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
725
726#if 0
727	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
728	    "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
729	    "FastDiv: %d\n",
730	    __func__,
731	    main_rssi,
732	    alt_rssi,
733	    main_ant_conf,
734	    alt_ant_conf,
735	    rx_ant_conf,
736	    !!(rs->rs_rssi_ctl[2] & 0x80),
737	    !!(rs->rs_rssi_ctl[2] & 0x40),
738	    !!(rs->rs_rssi_ext[2] & 0x40));
739#endif
740
741	/*
742	 * If LNA diversity combining isn't enabled, don't run this.
743	 */
744	if (! sc->sc_dolnadiv)
745		return;
746
747	/*
748	 * XXX this is ugly, but the HAL code attaches the
749	 * LNA diversity to the TX antenna settings.
750	 * I don't know why.
751	 */
752	if (sc->sc_txantenna != HAL_ANT_VARIABLE)
753		return;
754
755	/* Record packet only when alt_rssi is positive */
756	if (main_rssi > 0 && alt_rssi > 0) {
757		antcomb->total_pkt_count++;
758		antcomb->main_total_rssi += main_rssi;
759		antcomb->alt_total_rssi  += alt_rssi;
760		if (main_ant_conf == rx_ant_conf)
761			antcomb->main_recv_cnt++;
762		else
763			antcomb->alt_recv_cnt++;
764	}
765
766	/* Short scan check */
767	if (antcomb->scan && antcomb->alt_good) {
768		if (ieee80211_time_after(ticks, antcomb->scan_start_time +
769		    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
770			short_scan = AH_TRUE;
771		else
772			if (antcomb->total_pkt_count ==
773			    ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
774				alt_ratio = ((antcomb->alt_recv_cnt * 100) /
775					    antcomb->total_pkt_count);
776				if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
777					short_scan = AH_TRUE;
778			}
779	}
780
781#if 0
782	DPRINTF(sc, ATH_DEBUG_DIVERSITY,
783	    "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
784	    __func__,
785	    antcomb->total_pkt_count,
786	    !! (rs->rs_moreaggr),
787	    !! (short_scan));
788#endif
789
790	if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
791	    rs->rs_moreaggr) && !short_scan)
792		return;
793
794	if (antcomb->total_pkt_count) {
795		alt_ratio = ((antcomb->alt_recv_cnt * 100) /
796			     antcomb->total_pkt_count);
797		main_rssi_avg = (antcomb->main_total_rssi /
798				 antcomb->total_pkt_count);
799		alt_rssi_avg = (antcomb->alt_total_rssi /
800				 antcomb->total_pkt_count);
801	}
802
803	OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
804
805	ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
806	curr_alt_set = div_ant_conf.alt_lna_conf;
807	curr_main_set = div_ant_conf.main_lna_conf;
808	curr_bias = div_ant_conf.fast_div_bias;
809
810	antcomb->count++;
811
812	if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
813		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
814			ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
815						  main_rssi_avg);
816			antcomb->alt_good = AH_TRUE;
817		} else {
818			antcomb->alt_good = AH_FALSE;
819		}
820
821		antcomb->count = 0;
822		antcomb->scan = AH_TRUE;
823		antcomb->scan_not_start = AH_TRUE;
824	}
825
826	if (!antcomb->scan) {
827		if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
828			if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
829				/* Switch main and alt LNA */
830				div_ant_conf.main_lna_conf =
831						HAL_ANT_DIV_COMB_LNA2;
832				div_ant_conf.alt_lna_conf  =
833						HAL_ANT_DIV_COMB_LNA1;
834			} else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
835				div_ant_conf.main_lna_conf =
836						HAL_ANT_DIV_COMB_LNA1;
837				div_ant_conf.alt_lna_conf  =
838						HAL_ANT_DIV_COMB_LNA2;
839			}
840
841			goto div_comb_done;
842		} else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
843			   (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
844			/* Set alt to another LNA */
845			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
846				div_ant_conf.alt_lna_conf =
847						HAL_ANT_DIV_COMB_LNA1;
848			else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
849				div_ant_conf.alt_lna_conf =
850						HAL_ANT_DIV_COMB_LNA2;
851
852			goto div_comb_done;
853		}
854
855		if ((alt_rssi_avg < (main_rssi_avg +
856		    antcomb->lna1_lna2_delta)))
857			goto div_comb_done;
858	}
859
860	if (!antcomb->scan_not_start) {
861		switch (curr_alt_set) {
862		case HAL_ANT_DIV_COMB_LNA2:
863			antcomb->rssi_lna2 = alt_rssi_avg;
864			antcomb->rssi_lna1 = main_rssi_avg;
865			antcomb->scan = AH_TRUE;
866			/* set to A+B */
867			div_ant_conf.main_lna_conf =
868				HAL_ANT_DIV_COMB_LNA1;
869			div_ant_conf.alt_lna_conf  =
870				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
871			break;
872		case HAL_ANT_DIV_COMB_LNA1:
873			antcomb->rssi_lna1 = alt_rssi_avg;
874			antcomb->rssi_lna2 = main_rssi_avg;
875			antcomb->scan = AH_TRUE;
876			/* set to A+B */
877			div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
878			div_ant_conf.alt_lna_conf  =
879				HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
880			break;
881		case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
882			antcomb->rssi_add = alt_rssi_avg;
883			antcomb->scan = AH_TRUE;
884			/* set to A-B */
885			div_ant_conf.alt_lna_conf =
886				HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
887			break;
888		case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
889			antcomb->rssi_sub = alt_rssi_avg;
890			antcomb->scan = AH_FALSE;
891			if (antcomb->rssi_lna2 >
892			    (antcomb->rssi_lna1 +
893			    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
894				/* use LNA2 as main LNA */
895				if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
896				    (antcomb->rssi_add > antcomb->rssi_sub)) {
897					/* set to A+B */
898					div_ant_conf.main_lna_conf =
899						HAL_ANT_DIV_COMB_LNA2;
900					div_ant_conf.alt_lna_conf  =
901						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
902				} else if (antcomb->rssi_sub >
903					   antcomb->rssi_lna1) {
904					/* set to A-B */
905					div_ant_conf.main_lna_conf =
906						HAL_ANT_DIV_COMB_LNA2;
907					div_ant_conf.alt_lna_conf =
908						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
909				} else {
910					/* set to LNA1 */
911					div_ant_conf.main_lna_conf =
912						HAL_ANT_DIV_COMB_LNA2;
913					div_ant_conf.alt_lna_conf =
914						HAL_ANT_DIV_COMB_LNA1;
915				}
916			} else {
917				/* use LNA1 as main LNA */
918				if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
919				    (antcomb->rssi_add > antcomb->rssi_sub)) {
920					/* set to A+B */
921					div_ant_conf.main_lna_conf =
922						HAL_ANT_DIV_COMB_LNA1;
923					div_ant_conf.alt_lna_conf  =
924						HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
925				} else if (antcomb->rssi_sub >
926					   antcomb->rssi_lna1) {
927					/* set to A-B */
928					div_ant_conf.main_lna_conf =
929						HAL_ANT_DIV_COMB_LNA1;
930					div_ant_conf.alt_lna_conf =
931						HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
932				} else {
933					/* set to LNA2 */
934					div_ant_conf.main_lna_conf =
935						HAL_ANT_DIV_COMB_LNA1;
936					div_ant_conf.alt_lna_conf =
937						HAL_ANT_DIV_COMB_LNA2;
938				}
939			}
940			break;
941		default:
942			break;
943		}
944	} else {
945		if (!antcomb->alt_good) {
946			antcomb->scan_not_start = AH_FALSE;
947			/* Set alt to another LNA */
948			if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
949				div_ant_conf.main_lna_conf =
950						HAL_ANT_DIV_COMB_LNA2;
951				div_ant_conf.alt_lna_conf =
952						HAL_ANT_DIV_COMB_LNA1;
953			} else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
954				div_ant_conf.main_lna_conf =
955						HAL_ANT_DIV_COMB_LNA1;
956				div_ant_conf.alt_lna_conf =
957						HAL_ANT_DIV_COMB_LNA2;
958			}
959			goto div_comb_done;
960		}
961	}
962
963	ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
964					   main_rssi_avg, alt_rssi_avg,
965					   alt_ratio);
966
967	antcomb->quick_scan_cnt++;
968
969div_comb_done:
970#if 0
971	ath_ant_div_conf_fast_divbias(&div_ant_conf);
972#endif
973
974	ath_ant_adjust_fast_divbias(antcomb,
975	    alt_ratio,
976	    ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
977	    div_ant_conf.antdiv_configgroup,
978	    &div_ant_conf);
979
980	ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
981
982	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
983	   __func__, antcomb->total_pkt_count);
984
985	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
986	   __func__, antcomb->main_total_rssi);
987	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
988	   __func__, antcomb->alt_total_rssi);
989
990	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
991	   __func__, main_rssi_avg);
992	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
993	   __func__, alt_rssi_avg);
994
995	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
996	   __func__, antcomb->main_recv_cnt);
997	DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
998	   __func__, antcomb->alt_recv_cnt);
999
1000//	if (curr_alt_set != div_ant_conf.alt_lna_conf)
1001		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1002		    __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1003//	if (curr_main_set != div_ant_conf.main_lna_conf)
1004		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1005		    __func__, curr_main_set, div_ant_conf.main_lna_conf);
1006//	if (curr_bias != div_ant_conf.fast_div_bias)
1007		DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1008		    __func__, curr_bias, div_ant_conf.fast_div_bias);
1009
1010	antcomb->scan_start_time = ticks;
1011	antcomb->total_pkt_count = 0;
1012	antcomb->main_total_rssi = 0;
1013	antcomb->alt_total_rssi = 0;
1014	antcomb->main_recv_cnt = 0;
1015	antcomb->alt_recv_cnt = 0;
1016}
1017