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