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 */
87 int
ath_lna_div_attach(struct ath_softc * sc)88 ath_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 */
128 int
ath_lna_div_detach(struct ath_softc * sc)129 ath_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 */
142 int
ath_lna_div_enable(struct ath_softc * sc,const struct ieee80211_channel * chan)143 ath_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 */
156 int
ath_lna_div_ioctl(struct ath_softc * sc,struct ath_diag * ad)157 ath_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;
203 bad:
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 */
215 static HAL_BOOL
ath_is_alt_ant_ratio_better(int alt_ratio,int maxdelta,int mindelta,int main_rssi_avg,int alt_rssi_avg,int pkt_count)216 ath_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
224 static void
ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state * antcomb,HAL_ANT_COMB_CONFIG * ant_conf,int main_rssi_avg)225 ath_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
279 static void
ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state * antcomb,HAL_ANT_COMB_CONFIG * div_ant_conf,int main_rssi_avg,int alt_rssi_avg,int alt_ratio)280 ath_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
478 static void
ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state * antcomb,int alt_ratio,int alt_ant_ratio_th,u_int config_group,HAL_ANT_COMB_CONFIG * pdiv_ant_conf)479 ath_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 */
709 void
ath_lna_rx_comb_scan(struct ath_softc * sc,struct ath_rx_status * rs,unsigned long _ticks,int _hz)710 ath_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
969 div_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
1018