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