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