1 /* 2 * Copyright (c) 2012 Qualcomm Atheros, Inc. 3 * 4 * Permission to use, copy, modify, and/or distribute this software for any 5 * purpose with or without fee is hereby granted, provided that the above 6 * copyright notice and this permission notice appear in all copies. 7 * 8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 15 */ 16 17 #include "ath9k.h" 18 19 /* 20 * AR9285 21 * ====== 22 * 23 * EEPROM has 2 4-bit fields containing the card configuration. 24 * 25 * antdiv_ctl1: 26 * ------------ 27 * bb_enable_ant_div_lnadiv : 1 28 * bb_ant_div_alt_gaintb : 1 29 * bb_ant_div_main_gaintb : 1 30 * bb_enable_ant_fast_div : 1 31 * 32 * antdiv_ctl2: 33 * ----------- 34 * bb_ant_div_alt_lnaconf : 2 35 * bb_ant_div_main_lnaconf : 2 36 * 37 * The EEPROM bits are used as follows: 38 * ------------------------------------ 39 * 40 * bb_enable_ant_div_lnadiv - Enable LNA path rx antenna diversity/combining. 41 * Set in AR_PHY_MULTICHAIN_GAIN_CTL. 42 * 43 * bb_ant_div_[alt/main]_gaintb - 0 -> Antenna config Alt/Main uses gaintable 0 44 * 1 -> Antenna config Alt/Main uses gaintable 1 45 * Set in AR_PHY_MULTICHAIN_GAIN_CTL. 46 * 47 * bb_enable_ant_fast_div - Enable fast antenna diversity. 48 * Set in AR_PHY_CCK_DETECT. 49 * 50 * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config. 51 * Set in AR_PHY_MULTICHAIN_GAIN_CTL. 52 * 10=LNA1 53 * 01=LNA2 54 * 11=LNA1+LNA2 55 * 00=LNA1-LNA2 56 * 57 * AR9485 / AR9565 / AR9331 58 * ======================== 59 * 60 * The same bits are present in the EEPROM, but the location in the 61 * EEPROM is different (ant_div_control in ar9300_BaseExtension_1). 62 * 63 * ant_div_alt_lnaconf ==> bit 0~1 64 * ant_div_main_lnaconf ==> bit 2~3 65 * ant_div_alt_gaintb ==> bit 4 66 * ant_div_main_gaintb ==> bit 5 67 * enable_ant_div_lnadiv ==> bit 6 68 * enable_ant_fast_div ==> bit 7 69 */ 70 71 static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb, 72 int alt_ratio, int maxdelta, 73 int mindelta, int main_rssi_avg, 74 int alt_rssi_avg, int pkt_count) 75 { 76 if (pkt_count <= 50) 77 return false; 78 79 if (alt_rssi_avg > main_rssi_avg + mindelta) 80 return true; 81 82 if (alt_ratio >= antcomb->ant_ratio2 && 83 alt_rssi_avg >= antcomb->low_rssi_thresh && 84 (alt_rssi_avg > main_rssi_avg + maxdelta)) 85 return true; 86 87 return false; 88 } 89 90 static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf, 91 struct ath_ant_comb *antcomb, 92 int alt_ratio, int alt_rssi_avg, 93 int main_rssi_avg) 94 { 95 bool result, set1, set2; 96 97 result = set1 = set2 = false; 98 99 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 && 100 conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1) 101 set1 = true; 102 103 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 && 104 conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2) 105 set2 = true; 106 107 switch (conf->div_group) { 108 case 0: 109 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) 110 result = true; 111 break; 112 case 1: 113 case 2: 114 if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) 115 break; 116 117 if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) || 118 (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) || 119 (alt_ratio > antcomb->ant_ratio)) 120 result = true; 121 122 break; 123 case 3: 124 if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh) 125 break; 126 127 if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) || 128 (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) || 129 (alt_ratio > antcomb->ant_ratio)) 130 result = true; 131 132 break; 133 } 134 135 return result; 136 } 137 138 static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb, 139 struct ath_hw_antcomb_conf ant_conf, 140 int main_rssi_avg) 141 { 142 antcomb->quick_scan_cnt = 0; 143 144 if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 145 antcomb->rssi_lna2 = main_rssi_avg; 146 else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1) 147 antcomb->rssi_lna1 = main_rssi_avg; 148 149 switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) { 150 case 0x10: /* LNA2 A-B */ 151 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 152 antcomb->first_quick_scan_conf = 153 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 154 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; 155 break; 156 case 0x20: /* LNA1 A-B */ 157 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 158 antcomb->first_quick_scan_conf = 159 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 160 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; 161 break; 162 case 0x21: /* LNA1 LNA2 */ 163 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2; 164 antcomb->first_quick_scan_conf = 165 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 166 antcomb->second_quick_scan_conf = 167 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 168 break; 169 case 0x12: /* LNA2 LNA1 */ 170 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1; 171 antcomb->first_quick_scan_conf = 172 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 173 antcomb->second_quick_scan_conf = 174 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 175 break; 176 case 0x13: /* LNA2 A+B */ 177 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 178 antcomb->first_quick_scan_conf = 179 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 180 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1; 181 break; 182 case 0x23: /* LNA1 A+B */ 183 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 184 antcomb->first_quick_scan_conf = 185 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 186 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2; 187 break; 188 default: 189 break; 190 } 191 } 192 193 static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb, 194 struct ath_hw_antcomb_conf *conf) 195 { 196 /* set alt to the conf with maximun ratio */ 197 if (antcomb->first_ratio && antcomb->second_ratio) { 198 if (antcomb->rssi_second > antcomb->rssi_third) { 199 /* first alt*/ 200 if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || 201 (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) 202 /* Set alt LNA1 or LNA2*/ 203 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 204 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 205 else 206 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 207 else 208 /* Set alt to A+B or A-B */ 209 conf->alt_lna_conf = 210 antcomb->first_quick_scan_conf; 211 } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || 212 (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) { 213 /* Set alt LNA1 or LNA2 */ 214 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 215 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 216 else 217 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 218 } else { 219 /* Set alt to A+B or A-B */ 220 conf->alt_lna_conf = antcomb->second_quick_scan_conf; 221 } 222 } else if (antcomb->first_ratio) { 223 /* first alt */ 224 if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || 225 (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) 226 /* Set alt LNA1 or LNA2 */ 227 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 228 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 229 else 230 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 231 else 232 /* Set alt to A+B or A-B */ 233 conf->alt_lna_conf = antcomb->first_quick_scan_conf; 234 } else if (antcomb->second_ratio) { 235 /* second alt */ 236 if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) || 237 (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) 238 /* Set alt LNA1 or LNA2 */ 239 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 240 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 241 else 242 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 243 else 244 /* Set alt to A+B or A-B */ 245 conf->alt_lna_conf = antcomb->second_quick_scan_conf; 246 } else { 247 /* main is largest */ 248 if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) || 249 (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)) 250 /* Set alt LNA1 or LNA2 */ 251 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2) 252 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 253 else 254 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 255 else 256 /* Set alt to A+B or A-B */ 257 conf->alt_lna_conf = antcomb->main_conf; 258 } 259 } 260 261 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb, 262 struct ath_hw_antcomb_conf *div_ant_conf, 263 int main_rssi_avg, int alt_rssi_avg, 264 int alt_ratio) 265 { 266 /* alt_good */ 267 switch (antcomb->quick_scan_cnt) { 268 case 0: 269 /* set alt to main, and alt to first conf */ 270 div_ant_conf->main_lna_conf = antcomb->main_conf; 271 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf; 272 break; 273 case 1: 274 /* set alt to main, and alt to first conf */ 275 div_ant_conf->main_lna_conf = antcomb->main_conf; 276 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf; 277 antcomb->rssi_first = main_rssi_avg; 278 antcomb->rssi_second = alt_rssi_avg; 279 280 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { 281 /* main is LNA1 */ 282 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 283 ATH_ANT_DIV_COMB_LNA1_DELTA_HI, 284 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, 285 main_rssi_avg, alt_rssi_avg, 286 antcomb->total_pkt_count)) 287 antcomb->first_ratio = true; 288 else 289 antcomb->first_ratio = false; 290 } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { 291 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 292 ATH_ANT_DIV_COMB_LNA1_DELTA_MID, 293 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, 294 main_rssi_avg, alt_rssi_avg, 295 antcomb->total_pkt_count)) 296 antcomb->first_ratio = true; 297 else 298 antcomb->first_ratio = false; 299 } else { 300 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 301 ATH_ANT_DIV_COMB_LNA1_DELTA_HI, 302 0, 303 main_rssi_avg, alt_rssi_avg, 304 antcomb->total_pkt_count)) 305 antcomb->first_ratio = true; 306 else 307 antcomb->first_ratio = false; 308 } 309 break; 310 case 2: 311 antcomb->alt_good = false; 312 antcomb->scan_not_start = false; 313 antcomb->scan = false; 314 antcomb->rssi_first = main_rssi_avg; 315 antcomb->rssi_third = alt_rssi_avg; 316 317 switch(antcomb->second_quick_scan_conf) { 318 case ATH_ANT_DIV_COMB_LNA1: 319 antcomb->rssi_lna1 = alt_rssi_avg; 320 break; 321 case ATH_ANT_DIV_COMB_LNA2: 322 antcomb->rssi_lna2 = alt_rssi_avg; 323 break; 324 case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: 325 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) 326 antcomb->rssi_lna2 = main_rssi_avg; 327 else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) 328 antcomb->rssi_lna1 = main_rssi_avg; 329 break; 330 default: 331 break; 332 } 333 334 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 + 335 div_ant_conf->lna1_lna2_switch_delta) 336 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 337 else 338 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 339 340 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) { 341 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 342 ATH_ANT_DIV_COMB_LNA1_DELTA_HI, 343 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, 344 main_rssi_avg, alt_rssi_avg, 345 antcomb->total_pkt_count)) 346 antcomb->second_ratio = true; 347 else 348 antcomb->second_ratio = false; 349 } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) { 350 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 351 ATH_ANT_DIV_COMB_LNA1_DELTA_MID, 352 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW, 353 main_rssi_avg, alt_rssi_avg, 354 antcomb->total_pkt_count)) 355 antcomb->second_ratio = true; 356 else 357 antcomb->second_ratio = false; 358 } else { 359 if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio, 360 ATH_ANT_DIV_COMB_LNA1_DELTA_HI, 361 0, 362 main_rssi_avg, alt_rssi_avg, 363 antcomb->total_pkt_count)) 364 antcomb->second_ratio = true; 365 else 366 antcomb->second_ratio = false; 367 } 368 369 ath_ant_set_alt_ratio(antcomb, div_ant_conf); 370 371 break; 372 default: 373 break; 374 } 375 } 376 377 static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf, 378 struct ath_ant_comb *antcomb, 379 int alt_ratio) 380 { 381 ant_conf->main_gaintb = 0; 382 ant_conf->alt_gaintb = 0; 383 384 if (ant_conf->div_group == 0) { 385 /* Adjust the fast_div_bias based on main and alt lna conf */ 386 switch ((ant_conf->main_lna_conf << 4) | 387 ant_conf->alt_lna_conf) { 388 case 0x01: /* A-B LNA2 */ 389 ant_conf->fast_div_bias = 0x3b; 390 break; 391 case 0x02: /* A-B LNA1 */ 392 ant_conf->fast_div_bias = 0x3d; 393 break; 394 case 0x03: /* A-B A+B */ 395 ant_conf->fast_div_bias = 0x1; 396 break; 397 case 0x10: /* LNA2 A-B */ 398 ant_conf->fast_div_bias = 0x7; 399 break; 400 case 0x12: /* LNA2 LNA1 */ 401 ant_conf->fast_div_bias = 0x2; 402 break; 403 case 0x13: /* LNA2 A+B */ 404 ant_conf->fast_div_bias = 0x7; 405 break; 406 case 0x20: /* LNA1 A-B */ 407 ant_conf->fast_div_bias = 0x6; 408 break; 409 case 0x21: /* LNA1 LNA2 */ 410 ant_conf->fast_div_bias = 0x0; 411 break; 412 case 0x23: /* LNA1 A+B */ 413 ant_conf->fast_div_bias = 0x6; 414 break; 415 case 0x30: /* A+B A-B */ 416 ant_conf->fast_div_bias = 0x1; 417 break; 418 case 0x31: /* A+B LNA2 */ 419 ant_conf->fast_div_bias = 0x3b; 420 break; 421 case 0x32: /* A+B LNA1 */ 422 ant_conf->fast_div_bias = 0x3d; 423 break; 424 default: 425 break; 426 } 427 } else if (ant_conf->div_group == 1) { 428 /* Adjust the fast_div_bias based on main and alt_lna_conf */ 429 switch ((ant_conf->main_lna_conf << 4) | 430 ant_conf->alt_lna_conf) { 431 case 0x01: /* A-B LNA2 */ 432 ant_conf->fast_div_bias = 0x1; 433 break; 434 case 0x02: /* A-B LNA1 */ 435 ant_conf->fast_div_bias = 0x1; 436 break; 437 case 0x03: /* A-B A+B */ 438 ant_conf->fast_div_bias = 0x1; 439 break; 440 case 0x10: /* LNA2 A-B */ 441 if (!(antcomb->scan) && 442 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) 443 ant_conf->fast_div_bias = 0x3f; 444 else 445 ant_conf->fast_div_bias = 0x1; 446 break; 447 case 0x12: /* LNA2 LNA1 */ 448 ant_conf->fast_div_bias = 0x1; 449 break; 450 case 0x13: /* LNA2 A+B */ 451 if (!(antcomb->scan) && 452 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) 453 ant_conf->fast_div_bias = 0x3f; 454 else 455 ant_conf->fast_div_bias = 0x1; 456 break; 457 case 0x20: /* LNA1 A-B */ 458 if (!(antcomb->scan) && 459 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) 460 ant_conf->fast_div_bias = 0x3f; 461 else 462 ant_conf->fast_div_bias = 0x1; 463 break; 464 case 0x21: /* LNA1 LNA2 */ 465 ant_conf->fast_div_bias = 0x1; 466 break; 467 case 0x23: /* LNA1 A+B */ 468 if (!(antcomb->scan) && 469 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) 470 ant_conf->fast_div_bias = 0x3f; 471 else 472 ant_conf->fast_div_bias = 0x1; 473 break; 474 case 0x30: /* A+B A-B */ 475 ant_conf->fast_div_bias = 0x1; 476 break; 477 case 0x31: /* A+B LNA2 */ 478 ant_conf->fast_div_bias = 0x1; 479 break; 480 case 0x32: /* A+B LNA1 */ 481 ant_conf->fast_div_bias = 0x1; 482 break; 483 default: 484 break; 485 } 486 } else if (ant_conf->div_group == 2) { 487 /* Adjust the fast_div_bias based on main and alt_lna_conf */ 488 switch ((ant_conf->main_lna_conf << 4) | 489 ant_conf->alt_lna_conf) { 490 case 0x01: /* A-B LNA2 */ 491 ant_conf->fast_div_bias = 0x1; 492 break; 493 case 0x02: /* A-B LNA1 */ 494 ant_conf->fast_div_bias = 0x1; 495 break; 496 case 0x03: /* A-B A+B */ 497 ant_conf->fast_div_bias = 0x1; 498 break; 499 case 0x10: /* LNA2 A-B */ 500 if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) 501 ant_conf->fast_div_bias = 0x1; 502 else 503 ant_conf->fast_div_bias = 0x2; 504 break; 505 case 0x12: /* LNA2 LNA1 */ 506 ant_conf->fast_div_bias = 0x1; 507 break; 508 case 0x13: /* LNA2 A+B */ 509 if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) 510 ant_conf->fast_div_bias = 0x1; 511 else 512 ant_conf->fast_div_bias = 0x2; 513 break; 514 case 0x20: /* LNA1 A-B */ 515 if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) 516 ant_conf->fast_div_bias = 0x1; 517 else 518 ant_conf->fast_div_bias = 0x2; 519 break; 520 case 0x21: /* LNA1 LNA2 */ 521 ant_conf->fast_div_bias = 0x1; 522 break; 523 case 0x23: /* LNA1 A+B */ 524 if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio)) 525 ant_conf->fast_div_bias = 0x1; 526 else 527 ant_conf->fast_div_bias = 0x2; 528 break; 529 case 0x30: /* A+B A-B */ 530 ant_conf->fast_div_bias = 0x1; 531 break; 532 case 0x31: /* A+B LNA2 */ 533 ant_conf->fast_div_bias = 0x1; 534 break; 535 case 0x32: /* A+B LNA1 */ 536 ant_conf->fast_div_bias = 0x1; 537 break; 538 default: 539 break; 540 } 541 542 if (antcomb->fast_div_bias) 543 ant_conf->fast_div_bias = antcomb->fast_div_bias; 544 } else if (ant_conf->div_group == 3) { 545 switch ((ant_conf->main_lna_conf << 4) | 546 ant_conf->alt_lna_conf) { 547 case 0x01: /* A-B LNA2 */ 548 ant_conf->fast_div_bias = 0x1; 549 break; 550 case 0x02: /* A-B LNA1 */ 551 ant_conf->fast_div_bias = 0x39; 552 break; 553 case 0x03: /* A-B A+B */ 554 ant_conf->fast_div_bias = 0x1; 555 break; 556 case 0x10: /* LNA2 A-B */ 557 ant_conf->fast_div_bias = 0x2; 558 break; 559 case 0x12: /* LNA2 LNA1 */ 560 ant_conf->fast_div_bias = 0x3f; 561 break; 562 case 0x13: /* LNA2 A+B */ 563 ant_conf->fast_div_bias = 0x2; 564 break; 565 case 0x20: /* LNA1 A-B */ 566 ant_conf->fast_div_bias = 0x3; 567 break; 568 case 0x21: /* LNA1 LNA2 */ 569 ant_conf->fast_div_bias = 0x3; 570 break; 571 case 0x23: /* LNA1 A+B */ 572 ant_conf->fast_div_bias = 0x3; 573 break; 574 case 0x30: /* A+B A-B */ 575 ant_conf->fast_div_bias = 0x1; 576 break; 577 case 0x31: /* A+B LNA2 */ 578 ant_conf->fast_div_bias = 0x6; 579 break; 580 case 0x32: /* A+B LNA1 */ 581 ant_conf->fast_div_bias = 0x1; 582 break; 583 default: 584 break; 585 } 586 } 587 } 588 589 static void ath_ant_try_scan(struct ath_ant_comb *antcomb, 590 struct ath_hw_antcomb_conf *conf, 591 int curr_alt_set, int alt_rssi_avg, 592 int main_rssi_avg) 593 { 594 switch (curr_alt_set) { 595 case ATH_ANT_DIV_COMB_LNA2: 596 antcomb->rssi_lna2 = alt_rssi_avg; 597 antcomb->rssi_lna1 = main_rssi_avg; 598 antcomb->scan = true; 599 /* set to A+B */ 600 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 601 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 602 break; 603 case ATH_ANT_DIV_COMB_LNA1: 604 antcomb->rssi_lna1 = alt_rssi_avg; 605 antcomb->rssi_lna2 = main_rssi_avg; 606 antcomb->scan = true; 607 /* set to A+B */ 608 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 609 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 610 break; 611 case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: 612 antcomb->rssi_add = alt_rssi_avg; 613 antcomb->scan = true; 614 /* set to A-B */ 615 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 616 break; 617 case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: 618 antcomb->rssi_sub = alt_rssi_avg; 619 antcomb->scan = false; 620 if (antcomb->rssi_lna2 > 621 (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) { 622 /* use LNA2 as main LNA */ 623 if ((antcomb->rssi_add > antcomb->rssi_lna1) && 624 (antcomb->rssi_add > antcomb->rssi_sub)) { 625 /* set to A+B */ 626 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 627 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 628 } else if (antcomb->rssi_sub > 629 antcomb->rssi_lna1) { 630 /* set to A-B */ 631 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 632 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 633 } else { 634 /* set to LNA1 */ 635 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 636 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 637 } 638 } else { 639 /* use LNA1 as main LNA */ 640 if ((antcomb->rssi_add > antcomb->rssi_lna2) && 641 (antcomb->rssi_add > antcomb->rssi_sub)) { 642 /* set to A+B */ 643 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 644 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 645 } else if (antcomb->rssi_sub > 646 antcomb->rssi_lna1) { 647 /* set to A-B */ 648 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 649 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 650 } else { 651 /* set to LNA2 */ 652 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 653 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 654 } 655 } 656 break; 657 default: 658 break; 659 } 660 } 661 662 static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf, 663 struct ath_ant_comb *antcomb, 664 int alt_ratio, int alt_rssi_avg, 665 int main_rssi_avg, int curr_main_set, 666 int curr_alt_set) 667 { 668 bool ret = false; 669 670 if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio, 671 alt_rssi_avg, main_rssi_avg)) { 672 if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { 673 /* 674 * Switch main and alt LNA. 675 */ 676 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 677 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 678 } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { 679 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 680 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 681 } 682 683 ret = true; 684 } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && 685 (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { 686 /* 687 Set alt to another LNA. 688 */ 689 if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) 690 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 691 else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) 692 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 693 694 ret = true; 695 } 696 697 return ret; 698 } 699 700 static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb) 701 { 702 int alt_ratio; 703 704 if (!antcomb->scan || !antcomb->alt_good) 705 return false; 706 707 if (time_after(jiffies, antcomb->scan_start_time + 708 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) 709 return true; 710 711 if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { 712 alt_ratio = ((antcomb->alt_recv_cnt * 100) / 713 antcomb->total_pkt_count); 714 if (alt_ratio < antcomb->ant_ratio) 715 return true; 716 } 717 718 return false; 719 } 720 721 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) 722 { 723 struct ath_hw_antcomb_conf div_ant_conf; 724 struct ath_ant_comb *antcomb = &sc->ant_comb; 725 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; 726 int curr_main_set; 727 int main_rssi = rs->rs_rssi_ctl[0]; 728 int alt_rssi = rs->rs_rssi_ctl[1]; 729 int rx_ant_conf, main_ant_conf; 730 bool short_scan = false, ret; 731 732 rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) & 733 ATH_ANT_RX_MASK; 734 main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) & 735 ATH_ANT_RX_MASK; 736 737 if (alt_rssi >= antcomb->low_rssi_thresh) { 738 antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO; 739 antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2; 740 } else { 741 antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI; 742 antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI; 743 } 744 745 /* Record packet only when both main_rssi and alt_rssi is positive */ 746 if (main_rssi > 0 && alt_rssi > 0) { 747 antcomb->total_pkt_count++; 748 antcomb->main_total_rssi += main_rssi; 749 antcomb->alt_total_rssi += alt_rssi; 750 751 if (main_ant_conf == rx_ant_conf) 752 antcomb->main_recv_cnt++; 753 else 754 antcomb->alt_recv_cnt++; 755 } 756 757 if (main_ant_conf == rx_ant_conf) { 758 ANT_STAT_INC(sc, ANT_MAIN, recv_cnt); 759 ANT_LNA_INC(sc, ANT_MAIN, rx_ant_conf); 760 } else { 761 ANT_STAT_INC(sc, ANT_ALT, recv_cnt); 762 ANT_LNA_INC(sc, ANT_ALT, rx_ant_conf); 763 } 764 765 /* Short scan check */ 766 short_scan = ath_ant_short_scan_check(antcomb); 767 768 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || 769 rs->rs_moreaggr) && !short_scan) 770 return; 771 772 if (antcomb->total_pkt_count) { 773 alt_ratio = ((antcomb->alt_recv_cnt * 100) / 774 antcomb->total_pkt_count); 775 main_rssi_avg = (antcomb->main_total_rssi / 776 antcomb->total_pkt_count); 777 alt_rssi_avg = (antcomb->alt_total_rssi / 778 antcomb->total_pkt_count); 779 } 780 781 ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); 782 curr_alt_set = div_ant_conf.alt_lna_conf; 783 curr_main_set = div_ant_conf.main_lna_conf; 784 antcomb->count++; 785 786 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { 787 if (alt_ratio > antcomb->ant_ratio) { 788 ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, 789 main_rssi_avg); 790 antcomb->alt_good = true; 791 } else { 792 antcomb->alt_good = false; 793 } 794 795 antcomb->count = 0; 796 antcomb->scan = true; 797 antcomb->scan_not_start = true; 798 } 799 800 if (!antcomb->scan) { 801 ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio, 802 alt_rssi_avg, main_rssi_avg, 803 curr_main_set, curr_alt_set); 804 if (ret) 805 goto div_comb_done; 806 } 807 808 if (!antcomb->scan && 809 (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta))) 810 goto div_comb_done; 811 812 if (!antcomb->scan_not_start) { 813 ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set, 814 alt_rssi_avg, main_rssi_avg); 815 } else { 816 if (!antcomb->alt_good) { 817 antcomb->scan_not_start = false; 818 /* Set alt to another LNA */ 819 if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { 820 div_ant_conf.main_lna_conf = 821 ATH_ANT_DIV_COMB_LNA2; 822 div_ant_conf.alt_lna_conf = 823 ATH_ANT_DIV_COMB_LNA1; 824 } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { 825 div_ant_conf.main_lna_conf = 826 ATH_ANT_DIV_COMB_LNA1; 827 div_ant_conf.alt_lna_conf = 828 ATH_ANT_DIV_COMB_LNA2; 829 } 830 goto div_comb_done; 831 } 832 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, 833 main_rssi_avg, alt_rssi_avg, 834 alt_ratio); 835 antcomb->quick_scan_cnt++; 836 } 837 838 div_comb_done: 839 ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); 840 ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); 841 ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg); 842 843 antcomb->scan_start_time = jiffies; 844 antcomb->total_pkt_count = 0; 845 antcomb->main_total_rssi = 0; 846 antcomb->alt_total_rssi = 0; 847 antcomb->main_recv_cnt = 0; 848 antcomb->alt_recv_cnt = 0; 849 } 850