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