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