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