linux/drivers/net/wireless/ath/ath9k/antenna.c
<<
>>
Prefs
   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
  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                                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
 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                        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
 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                        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
 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_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
 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                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
 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