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 ATH_ANT_DIV_COMB_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 if ((antcomb->scan == 0) && 558 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { 559 ant_conf->fast_div_bias = 0x3f; 560 } else { 561 ant_conf->fast_div_bias = 0x1; 562 } 563 break; 564 case 0x12: /* LNA2 LNA1 */ 565 ant_conf->fast_div_bias = 0x39; 566 break; 567 case 0x13: /* LNA2 A+B */ 568 if ((antcomb->scan == 0) && 569 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { 570 ant_conf->fast_div_bias = 0x3f; 571 } else { 572 ant_conf->fast_div_bias = 0x1; 573 } 574 break; 575 case 0x20: /* LNA1 A-B */ 576 if ((antcomb->scan == 0) && 577 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { 578 ant_conf->fast_div_bias = 0x3f; 579 } else { 580 ant_conf->fast_div_bias = 0x4; 581 } 582 break; 583 case 0x21: /* LNA1 LNA2 */ 584 ant_conf->fast_div_bias = 0x6; 585 break; 586 case 0x23: /* LNA1 A+B */ 587 if ((antcomb->scan == 0) && 588 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) { 589 ant_conf->fast_div_bias = 0x3f; 590 } else { 591 ant_conf->fast_div_bias = 0x6; 592 } 593 break; 594 case 0x30: /* A+B A-B */ 595 ant_conf->fast_div_bias = 0x1; 596 break; 597 case 0x31: /* A+B LNA2 */ 598 ant_conf->fast_div_bias = 0x6; 599 break; 600 case 0x32: /* A+B LNA1 */ 601 ant_conf->fast_div_bias = 0x1; 602 break; 603 default: 604 break; 605 } 606 } 607 } 608 609 static void ath_ant_try_scan(struct ath_ant_comb *antcomb, 610 struct ath_hw_antcomb_conf *conf, 611 int curr_alt_set, int alt_rssi_avg, 612 int main_rssi_avg) 613 { 614 switch (curr_alt_set) { 615 case ATH_ANT_DIV_COMB_LNA2: 616 antcomb->rssi_lna2 = alt_rssi_avg; 617 antcomb->rssi_lna1 = main_rssi_avg; 618 antcomb->scan = true; 619 /* set to A+B */ 620 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 621 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 622 break; 623 case ATH_ANT_DIV_COMB_LNA1: 624 antcomb->rssi_lna1 = alt_rssi_avg; 625 antcomb->rssi_lna2 = main_rssi_avg; 626 antcomb->scan = true; 627 /* set to A+B */ 628 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 629 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 630 break; 631 case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2: 632 antcomb->rssi_add = alt_rssi_avg; 633 antcomb->scan = true; 634 /* set to A-B */ 635 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 636 break; 637 case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2: 638 antcomb->rssi_sub = alt_rssi_avg; 639 antcomb->scan = false; 640 if (antcomb->rssi_lna2 > 641 (antcomb->rssi_lna1 + ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) { 642 /* use LNA2 as main LNA */ 643 if ((antcomb->rssi_add > antcomb->rssi_lna1) && 644 (antcomb->rssi_add > antcomb->rssi_sub)) { 645 /* set to A+B */ 646 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 647 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 648 } else if (antcomb->rssi_sub > 649 antcomb->rssi_lna1) { 650 /* set to A-B */ 651 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 652 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 653 } else { 654 /* set to LNA1 */ 655 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 656 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 657 } 658 } else { 659 /* use LNA1 as main LNA */ 660 if ((antcomb->rssi_add > antcomb->rssi_lna2) && 661 (antcomb->rssi_add > antcomb->rssi_sub)) { 662 /* set to A+B */ 663 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 664 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2; 665 } else if (antcomb->rssi_sub > 666 antcomb->rssi_lna1) { 667 /* set to A-B */ 668 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 669 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2; 670 } else { 671 /* set to LNA2 */ 672 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 673 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 674 } 675 } 676 break; 677 default: 678 break; 679 } 680 } 681 682 static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf, 683 struct ath_ant_comb *antcomb, 684 int alt_ratio, int alt_rssi_avg, 685 int main_rssi_avg, int curr_main_set, 686 int curr_alt_set) 687 { 688 bool ret = false; 689 690 if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio, 691 alt_rssi_avg, main_rssi_avg)) { 692 if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) { 693 /* 694 * Switch main and alt LNA. 695 */ 696 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2; 697 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 698 } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) { 699 div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1; 700 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 701 } 702 703 ret = true; 704 } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) && 705 (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) { 706 /* 707 Set alt to another LNA. 708 */ 709 if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) 710 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1; 711 else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) 712 div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2; 713 714 ret = true; 715 } 716 717 return ret; 718 } 719 720 static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb) 721 { 722 int alt_ratio; 723 724 if (!antcomb->scan || !antcomb->alt_good) 725 return false; 726 727 if (time_after(jiffies, antcomb->scan_start_time + 728 msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR))) 729 return true; 730 731 if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) { 732 alt_ratio = ((antcomb->alt_recv_cnt * 100) / 733 antcomb->total_pkt_count); 734 if (alt_ratio < antcomb->ant_ratio) 735 return true; 736 } 737 738 return false; 739 } 740 741 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs) 742 { 743 struct ath_hw_antcomb_conf div_ant_conf; 744 struct ath_ant_comb *antcomb = &sc->ant_comb; 745 int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set; 746 int curr_main_set; 747 int main_rssi = rs->rs_rssi_ctl0; 748 int alt_rssi = rs->rs_rssi_ctl1; 749 int rx_ant_conf, main_ant_conf; 750 bool short_scan = false, ret; 751 752 rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) & 753 ATH_ANT_RX_MASK; 754 main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) & 755 ATH_ANT_RX_MASK; 756 757 if (alt_rssi >= antcomb->low_rssi_thresh) { 758 antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO; 759 antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2; 760 } else { 761 antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI; 762 antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI; 763 } 764 765 /* Record packet only when both main_rssi and alt_rssi is positive */ 766 if (main_rssi > 0 && alt_rssi > 0) { 767 antcomb->total_pkt_count++; 768 antcomb->main_total_rssi += main_rssi; 769 antcomb->alt_total_rssi += alt_rssi; 770 771 if (main_ant_conf == rx_ant_conf) 772 antcomb->main_recv_cnt++; 773 else 774 antcomb->alt_recv_cnt++; 775 } 776 777 if (main_ant_conf == rx_ant_conf) { 778 ANT_STAT_INC(ANT_MAIN, recv_cnt); 779 ANT_LNA_INC(ANT_MAIN, rx_ant_conf); 780 } else { 781 ANT_STAT_INC(ANT_ALT, recv_cnt); 782 ANT_LNA_INC(ANT_ALT, rx_ant_conf); 783 } 784 785 /* Short scan check */ 786 short_scan = ath_ant_short_scan_check(antcomb); 787 788 if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) || 789 rs->rs_moreaggr) && !short_scan) 790 return; 791 792 if (antcomb->total_pkt_count) { 793 alt_ratio = ((antcomb->alt_recv_cnt * 100) / 794 antcomb->total_pkt_count); 795 main_rssi_avg = (antcomb->main_total_rssi / 796 antcomb->total_pkt_count); 797 alt_rssi_avg = (antcomb->alt_total_rssi / 798 antcomb->total_pkt_count); 799 } 800 801 ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf); 802 curr_alt_set = div_ant_conf.alt_lna_conf; 803 curr_main_set = div_ant_conf.main_lna_conf; 804 antcomb->count++; 805 806 if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) { 807 if (alt_ratio > antcomb->ant_ratio) { 808 ath_lnaconf_alt_good_scan(antcomb, div_ant_conf, 809 main_rssi_avg); 810 antcomb->alt_good = true; 811 } else { 812 antcomb->alt_good = false; 813 } 814 815 antcomb->count = 0; 816 antcomb->scan = true; 817 antcomb->scan_not_start = true; 818 } 819 820 if (!antcomb->scan) { 821 ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio, 822 alt_rssi_avg, main_rssi_avg, 823 curr_main_set, curr_alt_set); 824 if (ret) 825 goto div_comb_done; 826 } 827 828 if (!antcomb->scan && 829 (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta))) 830 goto div_comb_done; 831 832 if (!antcomb->scan_not_start) { 833 ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set, 834 alt_rssi_avg, main_rssi_avg); 835 } else { 836 if (!antcomb->alt_good) { 837 antcomb->scan_not_start = false; 838 /* Set alt to another LNA */ 839 if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) { 840 div_ant_conf.main_lna_conf = 841 ATH_ANT_DIV_COMB_LNA2; 842 div_ant_conf.alt_lna_conf = 843 ATH_ANT_DIV_COMB_LNA1; 844 } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) { 845 div_ant_conf.main_lna_conf = 846 ATH_ANT_DIV_COMB_LNA1; 847 div_ant_conf.alt_lna_conf = 848 ATH_ANT_DIV_COMB_LNA2; 849 } 850 goto div_comb_done; 851 } 852 ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf, 853 main_rssi_avg, alt_rssi_avg, 854 alt_ratio); 855 antcomb->quick_scan_cnt++; 856 } 857 858 div_comb_done: 859 ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio); 860 ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf); 861 ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg); 862 863 antcomb->scan_start_time = jiffies; 864 antcomb->total_pkt_count = 0; 865 antcomb->main_total_rssi = 0; 866 antcomb->alt_total_rssi = 0; 867 antcomb->main_recv_cnt = 0; 868 antcomb->alt_recv_cnt = 0; 869 } 870