Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2012 Qualcomm Atheros, Inc.
0003  *
0004  * Permission to use, copy, modify, and/or distribute this software for any
0005  * purpose with or without fee is hereby granted, provided that the above
0006  * copyright notice and this permission notice appear in all copies.
0007  *
0008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
0011  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
0013  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
0014  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0015  */
0016 
0017 #include "ath9k.h"
0018 
0019 /*
0020  * AR9285
0021  * ======
0022  *
0023  * EEPROM has 2 4-bit fields containing the card configuration.
0024  *
0025  * antdiv_ctl1:
0026  * ------------
0027  * bb_enable_ant_div_lnadiv : 1
0028  * bb_ant_div_alt_gaintb    : 1
0029  * bb_ant_div_main_gaintb   : 1
0030  * bb_enable_ant_fast_div   : 1
0031  *
0032  * antdiv_ctl2:
0033  * -----------
0034  * bb_ant_div_alt_lnaconf  : 2
0035  * bb_ant_div_main_lnaconf : 2
0036  *
0037  * The EEPROM bits are used as follows:
0038  * ------------------------------------
0039  *
0040  * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
0041  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
0042  *
0043  * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
0044  *                                 1 -> Antenna config Alt/Main uses gaintable 1
0045  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
0046  *
0047  * bb_enable_ant_fast_div        - Enable fast antenna diversity.
0048  *                                 Set in AR_PHY_CCK_DETECT.
0049  *
0050  * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
0051  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
0052  *                                 10=LNA1
0053  *                                 01=LNA2
0054  *                                 11=LNA1+LNA2
0055  *                                 00=LNA1-LNA2
0056  *
0057  * AR9485 / AR9565 / AR9331
0058  * ========================
0059  *
0060  * The same bits are present in the EEPROM, but the location in the
0061  * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
0062  *
0063  * ant_div_alt_lnaconf      ==> bit 0~1
0064  * ant_div_main_lnaconf     ==> bit 2~3
0065  * ant_div_alt_gaintb       ==> bit 4
0066  * ant_div_main_gaintb      ==> bit 5
0067  * enable_ant_div_lnadiv    ==> bit 6
0068  * enable_ant_fast_div      ==> bit 7
0069  */
0070 
0071 static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
0072                            int alt_ratio, int maxdelta,
0073                            int mindelta, int main_rssi_avg,
0074                            int alt_rssi_avg, int pkt_count)
0075 {
0076     if (pkt_count <= 50)
0077         return false;
0078 
0079     if (alt_rssi_avg > main_rssi_avg + mindelta)
0080         return true;
0081 
0082     if (alt_ratio >= antcomb->ant_ratio2 &&
0083         alt_rssi_avg >= antcomb->low_rssi_thresh &&
0084         (alt_rssi_avg > main_rssi_avg + maxdelta))
0085         return true;
0086 
0087     return false;
0088 }
0089 
0090 static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
0091                           struct ath_ant_comb *antcomb,
0092                           int alt_ratio, int alt_rssi_avg,
0093                           int main_rssi_avg)
0094 {
0095     bool result, set1, set2;
0096 
0097     result = set1 = set2 = false;
0098 
0099     if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
0100         conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
0101         set1 = true;
0102 
0103     if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
0104         conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0105         set2 = true;
0106 
0107     switch (conf->div_group) {
0108     case 0:
0109         if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
0110             result = true;
0111         break;
0112     case 1:
0113     case 2:
0114         if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
0115             break;
0116 
0117         if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
0118             (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
0119             (alt_ratio > antcomb->ant_ratio))
0120             result = true;
0121 
0122         break;
0123     case 3:
0124         if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
0125             break;
0126 
0127         if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
0128             (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
0129             (alt_ratio > antcomb->ant_ratio))
0130             result = true;
0131 
0132         break;
0133     }
0134 
0135     return result;
0136 }
0137 
0138 static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
0139                       struct ath_hw_antcomb_conf ant_conf,
0140                       int main_rssi_avg)
0141 {
0142     antcomb->quick_scan_cnt = 0;
0143 
0144     if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0145         antcomb->rssi_lna2 = main_rssi_avg;
0146     else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
0147         antcomb->rssi_lna1 = main_rssi_avg;
0148 
0149     switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
0150     case 0x10: /* LNA2 A-B */
0151         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0152         antcomb->first_quick_scan_conf =
0153             ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0154         antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
0155         break;
0156     case 0x20: /* LNA1 A-B */
0157         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0158         antcomb->first_quick_scan_conf =
0159             ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0160         antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
0161         break;
0162     case 0x21: /* LNA1 LNA2 */
0163         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
0164         antcomb->first_quick_scan_conf =
0165             ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0166         antcomb->second_quick_scan_conf =
0167             ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0168         break;
0169     case 0x12: /* LNA2 LNA1 */
0170         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
0171         antcomb->first_quick_scan_conf =
0172             ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0173         antcomb->second_quick_scan_conf =
0174             ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0175         break;
0176     case 0x13: /* LNA2 A+B */
0177         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0178         antcomb->first_quick_scan_conf =
0179             ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0180         antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
0181         break;
0182     case 0x23: /* LNA1 A+B */
0183         antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0184         antcomb->first_quick_scan_conf =
0185             ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0186         antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
0187         break;
0188     default:
0189         break;
0190     }
0191 }
0192 
0193 static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
0194                   struct ath_hw_antcomb_conf *conf)
0195 {
0196     /* set alt to the conf with maximun ratio */
0197     if (antcomb->first_ratio && antcomb->second_ratio) {
0198         if (antcomb->rssi_second > antcomb->rssi_third) {
0199             /* first alt*/
0200             if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
0201                 (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
0202                 /* Set alt LNA1 or LNA2*/
0203                 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0204                     conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0205                 else
0206                     conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0207             else
0208                 /* Set alt to A+B or A-B */
0209                 conf->alt_lna_conf =
0210                     antcomb->first_quick_scan_conf;
0211         } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
0212                (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
0213             /* Set alt LNA1 or LNA2 */
0214             if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0215                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0216             else
0217                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0218         } else {
0219             /* Set alt to A+B or A-B */
0220             conf->alt_lna_conf = antcomb->second_quick_scan_conf;
0221         }
0222     } else if (antcomb->first_ratio) {
0223         /* first alt */
0224         if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
0225             (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
0226             /* Set alt LNA1 or LNA2 */
0227             if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0228                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0229             else
0230                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0231         else
0232             /* Set alt to A+B or A-B */
0233             conf->alt_lna_conf = antcomb->first_quick_scan_conf;
0234     } else if (antcomb->second_ratio) {
0235         /* second alt */
0236         if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
0237             (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
0238             /* Set alt LNA1 or LNA2 */
0239             if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0240                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0241             else
0242                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0243         else
0244             /* Set alt to A+B or A-B */
0245             conf->alt_lna_conf = antcomb->second_quick_scan_conf;
0246     } else {
0247         /* main is largest */
0248         if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
0249             (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
0250             /* Set alt LNA1 or LNA2 */
0251             if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
0252                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0253             else
0254                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0255         else
0256             /* Set alt to A+B or A-B */
0257             conf->alt_lna_conf = antcomb->main_conf;
0258     }
0259 }
0260 
0261 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
0262                        struct ath_hw_antcomb_conf *div_ant_conf,
0263                        int main_rssi_avg, int alt_rssi_avg,
0264                        int alt_ratio)
0265 {
0266     /* alt_good */
0267     switch (antcomb->quick_scan_cnt) {
0268     case 0:
0269         /* set alt to main, and alt to first conf */
0270         div_ant_conf->main_lna_conf = antcomb->main_conf;
0271         div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
0272         break;
0273     case 1:
0274         /* set alt to main, and alt to first conf */
0275         div_ant_conf->main_lna_conf = antcomb->main_conf;
0276         div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
0277         antcomb->rssi_first = main_rssi_avg;
0278         antcomb->rssi_second = alt_rssi_avg;
0279 
0280         if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
0281             /* main is LNA1 */
0282             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0283                         ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
0284                         ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
0285                         main_rssi_avg, alt_rssi_avg,
0286                         antcomb->total_pkt_count))
0287                 antcomb->first_ratio = true;
0288             else
0289                 antcomb->first_ratio = false;
0290         } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
0291             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0292                         ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
0293                         ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
0294                         main_rssi_avg, alt_rssi_avg,
0295                         antcomb->total_pkt_count))
0296                 antcomb->first_ratio = true;
0297             else
0298                 antcomb->first_ratio = false;
0299         } else {
0300             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0301                         ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
0302                         0,
0303                         main_rssi_avg, alt_rssi_avg,
0304                         antcomb->total_pkt_count))
0305                 antcomb->first_ratio = true;
0306             else
0307                 antcomb->first_ratio = false;
0308         }
0309         break;
0310     case 2:
0311         antcomb->alt_good = false;
0312         antcomb->scan_not_start = false;
0313         antcomb->scan = false;
0314         antcomb->rssi_first = main_rssi_avg;
0315         antcomb->rssi_third = alt_rssi_avg;
0316 
0317         switch(antcomb->second_quick_scan_conf) {
0318         case ATH_ANT_DIV_COMB_LNA1:
0319             antcomb->rssi_lna1 = alt_rssi_avg;
0320             break;
0321         case ATH_ANT_DIV_COMB_LNA2:
0322             antcomb->rssi_lna2 = alt_rssi_avg;
0323             break;
0324         case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
0325             if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
0326                 antcomb->rssi_lna2 = main_rssi_avg;
0327             else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
0328                 antcomb->rssi_lna1 = main_rssi_avg;
0329             break;
0330         default:
0331             break;
0332         }
0333 
0334         if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
0335             div_ant_conf->lna1_lna2_switch_delta)
0336             div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0337         else
0338             div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0339 
0340         if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
0341             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0342                         ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
0343                         ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
0344                         main_rssi_avg, alt_rssi_avg,
0345                         antcomb->total_pkt_count))
0346                 antcomb->second_ratio = true;
0347             else
0348                 antcomb->second_ratio = false;
0349         } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
0350             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0351                         ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
0352                         ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
0353                         main_rssi_avg, alt_rssi_avg,
0354                         antcomb->total_pkt_count))
0355                 antcomb->second_ratio = true;
0356             else
0357                 antcomb->second_ratio = false;
0358         } else {
0359             if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
0360                         ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
0361                         0,
0362                         main_rssi_avg, alt_rssi_avg,
0363                         antcomb->total_pkt_count))
0364                 antcomb->second_ratio = true;
0365             else
0366                 antcomb->second_ratio = false;
0367         }
0368 
0369         ath_ant_set_alt_ratio(antcomb, div_ant_conf);
0370 
0371         break;
0372     default:
0373         break;
0374     }
0375 }
0376 
0377 static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
0378                       struct ath_ant_comb *antcomb,
0379                       int alt_ratio)
0380 {
0381     ant_conf->main_gaintb = 0;
0382     ant_conf->alt_gaintb = 0;
0383 
0384     if (ant_conf->div_group == 0) {
0385         /* Adjust the fast_div_bias based on main and alt lna conf */
0386         switch ((ant_conf->main_lna_conf << 4) |
0387                 ant_conf->alt_lna_conf) {
0388         case 0x01: /* A-B LNA2 */
0389             ant_conf->fast_div_bias = 0x3b;
0390             break;
0391         case 0x02: /* A-B LNA1 */
0392             ant_conf->fast_div_bias = 0x3d;
0393             break;
0394         case 0x03: /* A-B A+B */
0395             ant_conf->fast_div_bias = 0x1;
0396             break;
0397         case 0x10: /* LNA2 A-B */
0398             ant_conf->fast_div_bias = 0x7;
0399             break;
0400         case 0x12: /* LNA2 LNA1 */
0401             ant_conf->fast_div_bias = 0x2;
0402             break;
0403         case 0x13: /* LNA2 A+B */
0404             ant_conf->fast_div_bias = 0x7;
0405             break;
0406         case 0x20: /* LNA1 A-B */
0407             ant_conf->fast_div_bias = 0x6;
0408             break;
0409         case 0x21: /* LNA1 LNA2 */
0410             ant_conf->fast_div_bias = 0x0;
0411             break;
0412         case 0x23: /* LNA1 A+B */
0413             ant_conf->fast_div_bias = 0x6;
0414             break;
0415         case 0x30: /* A+B A-B */
0416             ant_conf->fast_div_bias = 0x1;
0417             break;
0418         case 0x31: /* A+B LNA2 */
0419             ant_conf->fast_div_bias = 0x3b;
0420             break;
0421         case 0x32: /* A+B LNA1 */
0422             ant_conf->fast_div_bias = 0x3d;
0423             break;
0424         default:
0425             break;
0426         }
0427     } else if (ant_conf->div_group == 1) {
0428         /* Adjust the fast_div_bias based on main and alt_lna_conf */
0429         switch ((ant_conf->main_lna_conf << 4) |
0430             ant_conf->alt_lna_conf) {
0431         case 0x01: /* A-B LNA2 */
0432             ant_conf->fast_div_bias = 0x1;
0433             break;
0434         case 0x02: /* A-B LNA1 */
0435             ant_conf->fast_div_bias = 0x1;
0436             break;
0437         case 0x03: /* A-B A+B */
0438             ant_conf->fast_div_bias = 0x1;
0439             break;
0440         case 0x10: /* LNA2 A-B */
0441             if (!(antcomb->scan) &&
0442                 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
0443                 ant_conf->fast_div_bias = 0x3f;
0444             else
0445                 ant_conf->fast_div_bias = 0x1;
0446             break;
0447         case 0x12: /* LNA2 LNA1 */
0448             ant_conf->fast_div_bias = 0x1;
0449             break;
0450         case 0x13: /* LNA2 A+B */
0451             if (!(antcomb->scan) &&
0452                 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
0453                 ant_conf->fast_div_bias = 0x3f;
0454             else
0455                 ant_conf->fast_div_bias = 0x1;
0456             break;
0457         case 0x20: /* LNA1 A-B */
0458             if (!(antcomb->scan) &&
0459                 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
0460                 ant_conf->fast_div_bias = 0x3f;
0461             else
0462                 ant_conf->fast_div_bias = 0x1;
0463             break;
0464         case 0x21: /* LNA1 LNA2 */
0465             ant_conf->fast_div_bias = 0x1;
0466             break;
0467         case 0x23: /* LNA1 A+B */
0468             if (!(antcomb->scan) &&
0469                 (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
0470                 ant_conf->fast_div_bias = 0x3f;
0471             else
0472                 ant_conf->fast_div_bias = 0x1;
0473             break;
0474         case 0x30: /* A+B A-B */
0475             ant_conf->fast_div_bias = 0x1;
0476             break;
0477         case 0x31: /* A+B LNA2 */
0478             ant_conf->fast_div_bias = 0x1;
0479             break;
0480         case 0x32: /* A+B LNA1 */
0481             ant_conf->fast_div_bias = 0x1;
0482             break;
0483         default:
0484             break;
0485         }
0486     } else if (ant_conf->div_group == 2) {
0487         /* Adjust the fast_div_bias based on main and alt_lna_conf */
0488         switch ((ant_conf->main_lna_conf << 4) |
0489                 ant_conf->alt_lna_conf) {
0490         case 0x01: /* A-B LNA2 */
0491             ant_conf->fast_div_bias = 0x1;
0492             break;
0493         case 0x02: /* A-B LNA1 */
0494             ant_conf->fast_div_bias = 0x1;
0495             break;
0496         case 0x03: /* A-B A+B */
0497             ant_conf->fast_div_bias = 0x1;
0498             break;
0499         case 0x10: /* LNA2 A-B */
0500             if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
0501                 ant_conf->fast_div_bias = 0x1;
0502             else
0503                 ant_conf->fast_div_bias = 0x2;
0504             break;
0505         case 0x12: /* LNA2 LNA1 */
0506             ant_conf->fast_div_bias = 0x1;
0507             break;
0508         case 0x13: /* LNA2 A+B */
0509             if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
0510                 ant_conf->fast_div_bias = 0x1;
0511             else
0512                 ant_conf->fast_div_bias = 0x2;
0513             break;
0514         case 0x20: /* LNA1 A-B */
0515             if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
0516                 ant_conf->fast_div_bias = 0x1;
0517             else
0518                 ant_conf->fast_div_bias = 0x2;
0519             break;
0520         case 0x21: /* LNA1 LNA2 */
0521             ant_conf->fast_div_bias = 0x1;
0522             break;
0523         case 0x23: /* LNA1 A+B */
0524             if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
0525                 ant_conf->fast_div_bias = 0x1;
0526             else
0527                 ant_conf->fast_div_bias = 0x2;
0528             break;
0529         case 0x30: /* A+B A-B */
0530             ant_conf->fast_div_bias = 0x1;
0531             break;
0532         case 0x31: /* A+B LNA2 */
0533             ant_conf->fast_div_bias = 0x1;
0534             break;
0535         case 0x32: /* A+B LNA1 */
0536             ant_conf->fast_div_bias = 0x1;
0537             break;
0538         default:
0539             break;
0540         }
0541 
0542         if (antcomb->fast_div_bias)
0543             ant_conf->fast_div_bias = antcomb->fast_div_bias;
0544     } else if (ant_conf->div_group == 3) {
0545         switch ((ant_conf->main_lna_conf << 4) |
0546             ant_conf->alt_lna_conf) {
0547         case 0x01: /* A-B LNA2 */
0548             ant_conf->fast_div_bias = 0x1;
0549             break;
0550         case 0x02: /* A-B LNA1 */
0551             ant_conf->fast_div_bias = 0x39;
0552             break;
0553         case 0x03: /* A-B A+B */
0554             ant_conf->fast_div_bias = 0x1;
0555             break;
0556         case 0x10: /* LNA2 A-B */
0557             ant_conf->fast_div_bias = 0x2;
0558             break;
0559         case 0x12: /* LNA2 LNA1 */
0560             ant_conf->fast_div_bias = 0x3f;
0561             break;
0562         case 0x13: /* LNA2 A+B */
0563             ant_conf->fast_div_bias = 0x2;
0564             break;
0565         case 0x20: /* LNA1 A-B */
0566             ant_conf->fast_div_bias = 0x3;
0567             break;
0568         case 0x21: /* LNA1 LNA2 */
0569             ant_conf->fast_div_bias = 0x3;
0570             break;
0571         case 0x23: /* LNA1 A+B */
0572             ant_conf->fast_div_bias = 0x3;
0573             break;
0574         case 0x30: /* A+B A-B */
0575             ant_conf->fast_div_bias = 0x1;
0576             break;
0577         case 0x31: /* A+B LNA2 */
0578             ant_conf->fast_div_bias = 0x6;
0579             break;
0580         case 0x32: /* A+B LNA1 */
0581             ant_conf->fast_div_bias = 0x1;
0582             break;
0583         default:
0584             break;
0585         }
0586     }
0587 }
0588 
0589 static void ath_ant_try_scan(struct ath_ant_comb *antcomb,
0590                  struct ath_hw_antcomb_conf *conf,
0591                  int curr_alt_set, int alt_rssi_avg,
0592                  int main_rssi_avg)
0593 {
0594     switch (curr_alt_set) {
0595     case ATH_ANT_DIV_COMB_LNA2:
0596         antcomb->rssi_lna2 = alt_rssi_avg;
0597         antcomb->rssi_lna1 = main_rssi_avg;
0598         antcomb->scan = true;
0599         /* set to A+B */
0600         conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0601         conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0602         break;
0603     case ATH_ANT_DIV_COMB_LNA1:
0604         antcomb->rssi_lna1 = alt_rssi_avg;
0605         antcomb->rssi_lna2 = main_rssi_avg;
0606         antcomb->scan = true;
0607         /* set to A+B */
0608         conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0609         conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0610         break;
0611     case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
0612         antcomb->rssi_add = alt_rssi_avg;
0613         antcomb->scan = true;
0614         /* set to A-B */
0615         conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0616         break;
0617     case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
0618         antcomb->rssi_sub = alt_rssi_avg;
0619         antcomb->scan = false;
0620         if (antcomb->rssi_lna2 >
0621             (antcomb->rssi_lna1 + conf->lna1_lna2_switch_delta)) {
0622             /* use LNA2 as main LNA */
0623             if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
0624                 (antcomb->rssi_add > antcomb->rssi_sub)) {
0625                 /* set to A+B */
0626                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0627                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0628             } else if (antcomb->rssi_sub >
0629                    antcomb->rssi_lna1) {
0630                 /* set to A-B */
0631                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0632                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0633             } else {
0634                 /* set to LNA1 */
0635                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0636                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0637             }
0638         } else {
0639             /* use LNA1 as main LNA */
0640             if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
0641                 (antcomb->rssi_add > antcomb->rssi_sub)) {
0642                 /* set to A+B */
0643                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0644                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
0645             } else if (antcomb->rssi_sub >
0646                    antcomb->rssi_lna1) {
0647                 /* set to A-B */
0648                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0649                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
0650             } else {
0651                 /* set to LNA2 */
0652                 conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0653                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0654             }
0655         }
0656         break;
0657     default:
0658         break;
0659     }
0660 }
0661 
0662 static bool ath_ant_try_switch(struct ath_hw_antcomb_conf *div_ant_conf,
0663                    struct ath_ant_comb *antcomb,
0664                    int alt_ratio, int alt_rssi_avg,
0665                    int main_rssi_avg, int curr_main_set,
0666                    int curr_alt_set)
0667 {
0668     bool ret = false;
0669 
0670     if (ath_ant_div_comb_alt_check(div_ant_conf, antcomb, alt_ratio,
0671                        alt_rssi_avg, main_rssi_avg)) {
0672         if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
0673             /*
0674              * Switch main and alt LNA.
0675              */
0676             div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0677             div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0678         } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
0679             div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0680             div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0681         }
0682 
0683         ret = true;
0684     } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
0685            (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
0686         /*
0687           Set alt to another LNA.
0688         */
0689         if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
0690             div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
0691         else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
0692             div_ant_conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
0693 
0694         ret = true;
0695     }
0696 
0697     return ret;
0698 }
0699 
0700 static bool ath_ant_short_scan_check(struct ath_ant_comb *antcomb)
0701 {
0702     int alt_ratio;
0703 
0704     if (!antcomb->scan || !antcomb->alt_good)
0705         return false;
0706 
0707     if (time_after(jiffies, antcomb->scan_start_time +
0708                msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
0709         return true;
0710 
0711     if (antcomb->total_pkt_count == ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
0712         alt_ratio = ((antcomb->alt_recv_cnt * 100) /
0713                  antcomb->total_pkt_count);
0714         if (alt_ratio < antcomb->ant_ratio)
0715             return true;
0716     }
0717 
0718     return false;
0719 }
0720 
0721 void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
0722 {
0723     struct ath_hw_antcomb_conf div_ant_conf;
0724     struct ath_ant_comb *antcomb = &sc->ant_comb;
0725     int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
0726     int curr_main_set;
0727     int main_rssi = rs->rs_rssi_ctl[0];
0728     int alt_rssi = rs->rs_rssi_ctl[1];
0729     int rx_ant_conf,  main_ant_conf;
0730     bool short_scan = false, ret;
0731 
0732     rx_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_CURRENT_SHIFT) &
0733                ATH_ANT_RX_MASK;
0734     main_ant_conf = (rs->rs_rssi_ctl[2] >> ATH_ANT_RX_MAIN_SHIFT) &
0735              ATH_ANT_RX_MASK;
0736 
0737     if (alt_rssi >= antcomb->low_rssi_thresh) {
0738         antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO;
0739         antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2;
0740     } else {
0741         antcomb->ant_ratio = ATH_ANT_DIV_COMB_ALT_ANT_RATIO_LOW_RSSI;
0742         antcomb->ant_ratio2 = ATH_ANT_DIV_COMB_ALT_ANT_RATIO2_LOW_RSSI;
0743     }
0744 
0745     /* Record packet only when both main_rssi and  alt_rssi is positive */
0746     if (main_rssi > 0 && alt_rssi > 0) {
0747         antcomb->total_pkt_count++;
0748         antcomb->main_total_rssi += main_rssi;
0749         antcomb->alt_total_rssi  += alt_rssi;
0750 
0751         if (main_ant_conf == rx_ant_conf)
0752             antcomb->main_recv_cnt++;
0753         else
0754             antcomb->alt_recv_cnt++;
0755     }
0756 
0757     if (main_ant_conf == rx_ant_conf) {
0758         ANT_STAT_INC(sc, ANT_MAIN, recv_cnt);
0759         ANT_LNA_INC(sc, ANT_MAIN, rx_ant_conf);
0760     } else {
0761         ANT_STAT_INC(sc, ANT_ALT, recv_cnt);
0762         ANT_LNA_INC(sc, ANT_ALT, rx_ant_conf);
0763     }
0764 
0765     /* Short scan check */
0766     short_scan = ath_ant_short_scan_check(antcomb);
0767 
0768     if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
0769          rs->rs_moreaggr) && !short_scan)
0770         return;
0771 
0772     if (antcomb->total_pkt_count) {
0773         alt_ratio = ((antcomb->alt_recv_cnt * 100) /
0774                  antcomb->total_pkt_count);
0775         main_rssi_avg = (antcomb->main_total_rssi /
0776                  antcomb->total_pkt_count);
0777         alt_rssi_avg = (antcomb->alt_total_rssi /
0778                  antcomb->total_pkt_count);
0779     }
0780 
0781     ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
0782     curr_alt_set = div_ant_conf.alt_lna_conf;
0783     curr_main_set = div_ant_conf.main_lna_conf;
0784     antcomb->count++;
0785 
0786     if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
0787         if (alt_ratio > antcomb->ant_ratio) {
0788             ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
0789                           main_rssi_avg);
0790             antcomb->alt_good = true;
0791         } else {
0792             antcomb->alt_good = false;
0793         }
0794 
0795         antcomb->count = 0;
0796         antcomb->scan = true;
0797         antcomb->scan_not_start = true;
0798     }
0799 
0800     if (!antcomb->scan) {
0801         ret = ath_ant_try_switch(&div_ant_conf, antcomb, alt_ratio,
0802                      alt_rssi_avg, main_rssi_avg,
0803                      curr_main_set, curr_alt_set);
0804         if (ret)
0805             goto div_comb_done;
0806     }
0807 
0808     if (!antcomb->scan &&
0809         (alt_rssi_avg < (main_rssi_avg + div_ant_conf.lna1_lna2_delta)))
0810         goto div_comb_done;
0811 
0812     if (!antcomb->scan_not_start) {
0813         ath_ant_try_scan(antcomb, &div_ant_conf, curr_alt_set,
0814                  alt_rssi_avg, main_rssi_avg);
0815     } else {
0816         if (!antcomb->alt_good) {
0817             antcomb->scan_not_start = false;
0818             /* Set alt to another LNA */
0819             if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
0820                 div_ant_conf.main_lna_conf =
0821                     ATH_ANT_DIV_COMB_LNA2;
0822                 div_ant_conf.alt_lna_conf =
0823                     ATH_ANT_DIV_COMB_LNA1;
0824             } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
0825                 div_ant_conf.main_lna_conf =
0826                     ATH_ANT_DIV_COMB_LNA1;
0827                 div_ant_conf.alt_lna_conf =
0828                     ATH_ANT_DIV_COMB_LNA2;
0829             }
0830             goto div_comb_done;
0831         }
0832         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
0833                            main_rssi_avg, alt_rssi_avg,
0834                            alt_ratio);
0835         antcomb->quick_scan_cnt++;
0836     }
0837 
0838 div_comb_done:
0839     ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
0840     ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
0841     ath9k_debug_stat_ant(sc, &div_ant_conf, main_rssi_avg, alt_rssi_avg);
0842 
0843     antcomb->scan_start_time = jiffies;
0844     antcomb->total_pkt_count = 0;
0845     antcomb->main_total_rssi = 0;
0846     antcomb->alt_total_rssi = 0;
0847     antcomb->main_recv_cnt = 0;
0848     antcomb->alt_recv_cnt = 0;
0849 }