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