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