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