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