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