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