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