850 lines · c
1/*2 * Copyright (c) 2012 Qualcomm Atheros, Inc.3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.15 */16 17#include "ath9k.h"18 19/*20 * AR928521 * ======22 *23 * EEPROM has 2 4-bit fields containing the card configuration.24 *25 * antdiv_ctl1:26 * ------------27 * bb_enable_ant_div_lnadiv : 128 * bb_ant_div_alt_gaintb : 129 * bb_ant_div_main_gaintb : 130 * bb_enable_ant_fast_div : 131 *32 * antdiv_ctl2:33 * -----------34 * bb_ant_div_alt_lnaconf : 235 * bb_ant_div_main_lnaconf : 236 *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 044 * 1 -> Antenna config Alt/Main uses gaintable 145 * 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=LNA153 * 01=LNA254 * 11=LNA1+LNA255 * 00=LNA1-LNA256 *57 * AR9485 / AR9565 / AR933158 * ========================59 *60 * The same bits are present in the EEPROM, but the location in the61 * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).62 *63 * ant_div_alt_lnaconf ==> bit 0~164 * ant_div_main_lnaconf ==> bit 2~365 * ant_div_alt_gaintb ==> bit 466 * ant_div_main_gaintb ==> bit 567 * enable_ant_div_lnadiv ==> bit 668 * enable_ant_fast_div ==> bit 769 */70 71static 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 90static 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 138static 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 193static 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 else206 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;207 else208 /* 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 else217 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 else230 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;231 else232 /* 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 else242 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;243 else244 /* 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 else254 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;255 else256 /* Set alt to A+B or A-B */257 conf->alt_lna_conf = antcomb->main_conf;258 }259}260 261static 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 else289 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 else298 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 else307 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 else338 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 else348 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 else357 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 else366 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 377static 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 else445 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 else455 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 else462 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 else472 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 else503 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 else512 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 else518 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 else527 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 589static 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_lna2) {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 662static 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 700static 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 721void 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 else754 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 838div_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