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
  19static inline bool ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta,
  20                                               int mindelta, int main_rssi_avg,
  21                                               int alt_rssi_avg, int pkt_count)
  22{
  23        return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
  24                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
  25                (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
  26}
  27
  28static inline bool ath_ant_div_comb_alt_check(u8 div_group, int alt_ratio,
  29                                              int curr_main_set, int curr_alt_set,
  30                                              int alt_rssi_avg, int main_rssi_avg)
  31{
  32        bool result = false;
  33        switch (div_group) {
  34        case 0:
  35                if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
  36                        result = true;
  37                break;
  38        case 1:
  39        case 2:
  40                if ((((curr_main_set == ATH_ANT_DIV_COMB_LNA2) &&
  41                      (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) &&
  42                      (alt_rssi_avg >= (main_rssi_avg - 5))) ||
  43                     ((curr_main_set == ATH_ANT_DIV_COMB_LNA1) &&
  44                      (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) &&
  45                      (alt_rssi_avg >= (main_rssi_avg - 2)))) &&
  46                    (alt_rssi_avg >= 4))
  47                        result = true;
  48                else
  49                        result = false;
  50                break;
  51        }
  52
  53        return result;
  54}
  55
  56static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
  57                                      struct ath_hw_antcomb_conf ant_conf,
  58                                      int main_rssi_avg)
  59{
  60        antcomb->quick_scan_cnt = 0;
  61
  62        if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
  63                antcomb->rssi_lna2 = main_rssi_avg;
  64        else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
  65                antcomb->rssi_lna1 = main_rssi_avg;
  66
  67        switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
  68        case 0x10: /* LNA2 A-B */
  69                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
  70                antcomb->first_quick_scan_conf =
  71                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
  72                antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
  73                break;
  74        case 0x20: /* LNA1 A-B */
  75                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
  76                antcomb->first_quick_scan_conf =
  77                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
  78                antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
  79                break;
  80        case 0x21: /* LNA1 LNA2 */
  81                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
  82                antcomb->first_quick_scan_conf =
  83                        ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
  84                antcomb->second_quick_scan_conf =
  85                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
  86                break;
  87        case 0x12: /* LNA2 LNA1 */
  88                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
  89                antcomb->first_quick_scan_conf =
  90                        ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
  91                antcomb->second_quick_scan_conf =
  92                        ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
  93                break;
  94        case 0x13: /* LNA2 A+B */
  95                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
  96                antcomb->first_quick_scan_conf =
  97                        ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
  98                antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
  99                break;
 100        case 0x23: /* LNA1 A+B */
 101                antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
 102                antcomb->first_quick_scan_conf =
 103                        ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
 104                antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
 105                break;
 106        default:
 107                break;
 108        }
 109}
 110
 111static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
 112                                       struct ath_hw_antcomb_conf *div_ant_conf,
 113                                       int main_rssi_avg, int alt_rssi_avg,
 114                                       int alt_ratio)
 115{
 116        /* alt_good */
 117        switch (antcomb->quick_scan_cnt) {
 118        case 0:
 119                /* set alt to main, and alt to first conf */
 120                div_ant_conf->main_lna_conf = antcomb->main_conf;
 121                div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
 122                break;
 123        case 1:
 124                /* set alt to main, and alt to first conf */
 125                div_ant_conf->main_lna_conf = antcomb->main_conf;
 126                div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
 127                antcomb->rssi_first = main_rssi_avg;
 128                antcomb->rssi_second = alt_rssi_avg;
 129
 130                if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
 131                        /* main is LNA1 */
 132                        if (ath_is_alt_ant_ratio_better(alt_ratio,
 133                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
 134                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
 135                                                main_rssi_avg, alt_rssi_avg,
 136                                                antcomb->total_pkt_count))
 137                                antcomb->first_ratio = true;
 138                        else
 139                                antcomb->first_ratio = false;
 140                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
 141                        if (ath_is_alt_ant_ratio_better(alt_ratio,
 142                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
 143                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
 144                                                main_rssi_avg, alt_rssi_avg,
 145                                                antcomb->total_pkt_count))
 146                                antcomb->first_ratio = true;
 147                        else
 148                                antcomb->first_ratio = false;
 149                } else {
 150                        if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
 151                              (alt_rssi_avg > main_rssi_avg +
 152                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
 153                             (alt_rssi_avg > main_rssi_avg)) &&
 154                            (antcomb->total_pkt_count > 50))
 155                                antcomb->first_ratio = true;
 156                        else
 157                                antcomb->first_ratio = false;
 158                }
 159                break;
 160        case 2:
 161                antcomb->alt_good = false;
 162                antcomb->scan_not_start = false;
 163                antcomb->scan = false;
 164                antcomb->rssi_first = main_rssi_avg;
 165                antcomb->rssi_third = alt_rssi_avg;
 166
 167                if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
 168                        antcomb->rssi_lna1 = alt_rssi_avg;
 169                else if (antcomb->second_quick_scan_conf ==
 170                         ATH_ANT_DIV_COMB_LNA2)
 171                        antcomb->rssi_lna2 = alt_rssi_avg;
 172                else if (antcomb->second_quick_scan_conf ==
 173                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
 174                        if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
 175                                antcomb->rssi_lna2 = main_rssi_avg;
 176                        else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
 177                                antcomb->rssi_lna1 = main_rssi_avg;
 178                }
 179
 180                if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
 181                    ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
 182                        div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
 183                else
 184                        div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
 185
 186                if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
 187                        if (ath_is_alt_ant_ratio_better(alt_ratio,
 188                                                ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
 189                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
 190                                                main_rssi_avg, alt_rssi_avg,
 191                                                antcomb->total_pkt_count))
 192                                antcomb->second_ratio = true;
 193                        else
 194                                antcomb->second_ratio = false;
 195                } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
 196                        if (ath_is_alt_ant_ratio_better(alt_ratio,
 197                                                ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
 198                                                ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
 199                                                main_rssi_avg, alt_rssi_avg,
 200                                                antcomb->total_pkt_count))
 201                                antcomb->second_ratio = true;
 202                        else
 203                                antcomb->second_ratio = false;
 204                } else {
 205                        if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
 206                              (alt_rssi_avg > main_rssi_avg +
 207                               ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
 208                             (alt_rssi_avg > main_rssi_avg)) &&
 209                            (antcomb->total_pkt_count > 50))
 210                                antcomb->second_ratio = true;
 211                        else
 212                                antcomb->second_ratio = false;
 213                }
 214
 215                /* set alt to the conf with maximun ratio */
 216                if (antcomb->first_ratio && antcomb->second_ratio) {
 217                        if (antcomb->rssi_second > antcomb->rssi_third) {
 218                                /* first alt*/
 219                                if ((antcomb->first_quick_scan_conf ==
 220                                    ATH_ANT_DIV_COMB_LNA1) ||
 221                                    (antcomb->first_quick_scan_conf ==
 222                                    ATH_ANT_DIV_COMB_LNA2))
 223                                        /* Set alt LNA1 or LNA2*/
 224                                        if (div_ant_conf->main_lna_conf ==
 225                                            ATH_ANT_DIV_COMB_LNA2)
 226                                                div_ant_conf->alt_lna_conf =
 227                                                        ATH_ANT_DIV_COMB_LNA1;
 228                                        else
 229                                                div_ant_conf->alt_lna_conf =
 230                                                        ATH_ANT_DIV_COMB_LNA2;
 231                                else
 232                                        /* Set alt to A+B or A-B */
 233                                        div_ant_conf->alt_lna_conf =
 234                                                antcomb->first_quick_scan_conf;
 235                        } else if ((antcomb->second_quick_scan_conf ==
 236                                   ATH_ANT_DIV_COMB_LNA1) ||
 237                                   (antcomb->second_quick_scan_conf ==
 238                                   ATH_ANT_DIV_COMB_LNA2)) {
 239                                /* Set alt LNA1 or LNA2 */
 240                                if (div_ant_conf->main_lna_conf ==
 241                                    ATH_ANT_DIV_COMB_LNA2)
 242                                        div_ant_conf->alt_lna_conf =
 243                                                ATH_ANT_DIV_COMB_LNA1;
 244                                else
 245                                        div_ant_conf->alt_lna_conf =
 246                                                ATH_ANT_DIV_COMB_LNA2;
 247                        } else {
 248                                /* Set alt to A+B or A-B */
 249                                div_ant_conf->alt_lna_conf =
 250                                        antcomb->second_quick_scan_conf;
 251                        }
 252                } else if (antcomb->first_ratio) {
 253                        /* first alt */
 254                        if ((antcomb->first_quick_scan_conf ==
 255                            ATH_ANT_DIV_COMB_LNA1) ||
 256                            (antcomb->first_quick_scan_conf ==
 257                            ATH_ANT_DIV_COMB_LNA2))
 258                                        /* Set alt LNA1 or LNA2 */
 259                                if (div_ant_conf->main_lna_conf ==
 260                                    ATH_ANT_DIV_COMB_LNA2)
 261                                        div_ant_conf->alt_lna_conf =
 262                                                        ATH_ANT_DIV_COMB_LNA1;
 263                                else
 264                                        div_ant_conf->alt_lna_conf =
 265                                                        ATH_ANT_DIV_COMB_LNA2;
 266                        else
 267                                /* Set alt to A+B or A-B */
 268                                div_ant_conf->alt_lna_conf =
 269                                                antcomb->first_quick_scan_conf;
 270                } else if (antcomb->second_ratio) {
 271                                /* second alt */
 272                        if ((antcomb->second_quick_scan_conf ==
 273                            ATH_ANT_DIV_COMB_LNA1) ||
 274                            (antcomb->second_quick_scan_conf ==
 275                            ATH_ANT_DIV_COMB_LNA2))
 276                                /* Set alt LNA1 or LNA2 */
 277                                if (div_ant_conf->main_lna_conf ==
 278                                    ATH_ANT_DIV_COMB_LNA2)
 279                                        div_ant_conf->alt_lna_conf =
 280                                                ATH_ANT_DIV_COMB_LNA1;
 281                                else
 282                                        div_ant_conf->alt_lna_conf =
 283                                                ATH_ANT_DIV_COMB_LNA2;
 284                        else
 285                                /* Set alt to A+B or A-B */
 286                                div_ant_conf->alt_lna_conf =
 287                                                antcomb->second_quick_scan_conf;
 288                } else {
 289                        /* main is largest */
 290                        if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
 291                            (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
 292                                /* Set alt LNA1 or LNA2 */
 293                                if (div_ant_conf->main_lna_conf ==
 294                                    ATH_ANT_DIV_COMB_LNA2)
 295                                        div_ant_conf->alt_lna_conf =
 296                                                        ATH_ANT_DIV_COMB_LNA1;
 297                                else
 298                                        div_ant_conf->alt_lna_conf =
 299                                                        ATH_ANT_DIV_COMB_LNA2;
 300                        else
 301                                /* Set alt to A+B or A-B */
 302                                div_ant_conf->alt_lna_conf = antcomb->main_conf;
 303                }
 304                break;
 305        default:
 306                break;
 307        }
 308}
 309
 310static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
 311                                          struct ath_ant_comb *antcomb,
 312                                          int alt_ratio)
 313{
 314        ant_conf->main_gaintb = 0;
 315        ant_conf->alt_gaintb = 0;
 316
 317        if (ant_conf->div_group == 0) {
 318                /* Adjust the fast_div_bias based on main and alt lna conf */
 319                switch ((ant_conf->main_lna_conf << 4) |
 320                                ant_conf->alt_lna_conf) {
 321                case 0x01: /* A-B LNA2 */
 322                        ant_conf->fast_div_bias = 0x3b;
 323                        break;
 324                case 0x02: /* A-B LNA1 */
 325                        ant_conf->fast_div_bias = 0x3d;
 326                        break;
 327                case 0x03: /* A-B A+B */
 328                        ant_conf->fast_div_bias = 0x1;
 329                        break;
 330                case 0x10: /* LNA2 A-B */
 331                        ant_conf->fast_div_bias = 0x7;
 332                        break;
 333                case 0x12: /* LNA2 LNA1 */
 334                        ant_conf->fast_div_bias = 0x2;
 335                        break;
 336                case 0x13: /* LNA2 A+B */
 337                        ant_conf->fast_div_bias = 0x7;
 338                        break;
 339                case 0x20: /* LNA1 A-B */
 340                        ant_conf->fast_div_bias = 0x6;
 341                        break;
 342                case 0x21: /* LNA1 LNA2 */
 343                        ant_conf->fast_div_bias = 0x0;
 344                        break;
 345                case 0x23: /* LNA1 A+B */
 346                        ant_conf->fast_div_bias = 0x6;
 347                        break;
 348                case 0x30: /* A+B A-B */
 349                        ant_conf->fast_div_bias = 0x1;
 350                        break;
 351                case 0x31: /* A+B LNA2 */
 352                        ant_conf->fast_div_bias = 0x3b;
 353                        break;
 354                case 0x32: /* A+B LNA1 */
 355                        ant_conf->fast_div_bias = 0x3d;
 356                        break;
 357                default:
 358                        break;
 359                }
 360        } else if (ant_conf->div_group == 1) {
 361                /* Adjust the fast_div_bias based on main and alt_lna_conf */
 362                switch ((ant_conf->main_lna_conf << 4) |
 363                        ant_conf->alt_lna_conf) {
 364                case 0x01: /* A-B LNA2 */
 365                        ant_conf->fast_div_bias = 0x1;
 366                        break;
 367                case 0x02: /* A-B LNA1 */
 368                        ant_conf->fast_div_bias = 0x1;
 369                        break;
 370                case 0x03: /* A-B A+B */
 371                        ant_conf->fast_div_bias = 0x1;
 372                        break;
 373                case 0x10: /* LNA2 A-B */
 374                        if (!(antcomb->scan) &&
 375                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 376                                ant_conf->fast_div_bias = 0x3f;
 377                        else
 378                                ant_conf->fast_div_bias = 0x1;
 379                        break;
 380                case 0x12: /* LNA2 LNA1 */
 381                        ant_conf->fast_div_bias = 0x1;
 382                        break;
 383                case 0x13: /* LNA2 A+B */
 384                        if (!(antcomb->scan) &&
 385                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 386                                ant_conf->fast_div_bias = 0x3f;
 387                        else
 388                                ant_conf->fast_div_bias = 0x1;
 389                        break;
 390                case 0x20: /* LNA1 A-B */
 391                        if (!(antcomb->scan) &&
 392                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 393                                ant_conf->fast_div_bias = 0x3f;
 394                        else
 395                                ant_conf->fast_div_bias = 0x1;
 396                        break;
 397                case 0x21: /* LNA1 LNA2 */
 398                        ant_conf->fast_div_bias = 0x1;
 399                        break;
 400                case 0x23: /* LNA1 A+B */
 401                        if (!(antcomb->scan) &&
 402                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 403                                ant_conf->fast_div_bias = 0x3f;
 404                        else
 405                                ant_conf->fast_div_bias = 0x1;
 406                        break;
 407                case 0x30: /* A+B A-B */
 408                        ant_conf->fast_div_bias = 0x1;
 409                        break;
 410                case 0x31: /* A+B LNA2 */
 411                        ant_conf->fast_div_bias = 0x1;
 412                        break;
 413                case 0x32: /* A+B LNA1 */
 414                        ant_conf->fast_div_bias = 0x1;
 415                        break;
 416                default:
 417                        break;
 418                }
 419        } else if (ant_conf->div_group == 2) {
 420                /* Adjust the fast_div_bias based on main and alt_lna_conf */
 421                switch ((ant_conf->main_lna_conf << 4) |
 422                                ant_conf->alt_lna_conf) {
 423                case 0x01: /* A-B LNA2 */
 424                        ant_conf->fast_div_bias = 0x1;
 425                        break;
 426                case 0x02: /* A-B LNA1 */
 427                        ant_conf->fast_div_bias = 0x1;
 428                        break;
 429                case 0x03: /* A-B A+B */
 430                        ant_conf->fast_div_bias = 0x1;
 431                        break;
 432                case 0x10: /* LNA2 A-B */
 433                        if (!(antcomb->scan) &&
 434                                (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 435                                ant_conf->fast_div_bias = 0x1;
 436                        else
 437                                ant_conf->fast_div_bias = 0x2;
 438                        break;
 439                case 0x12: /* LNA2 LNA1 */
 440                        ant_conf->fast_div_bias = 0x1;
 441                        break;
 442                case 0x13: /* LNA2 A+B */
 443                        if (!(antcomb->scan) &&
 444                                (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 445                                ant_conf->fast_div_bias = 0x1;
 446                        else
 447                                ant_conf->fast_div_bias = 0x2;
 448                        break;
 449                case 0x20: /* LNA1 A-B */
 450                        if (!(antcomb->scan) &&
 451                                (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 452                                ant_conf->fast_div_bias = 0x1;
 453                        else
 454                                ant_conf->fast_div_bias = 0x2;
 455                        break;
 456                case 0x21: /* LNA1 LNA2 */
 457                        ant_conf->fast_div_bias = 0x1;
 458                        break;
 459                case 0x23: /* LNA1 A+B */
 460                        if (!(antcomb->scan) &&
 461                                (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
 462                                ant_conf->fast_div_bias = 0x1;
 463                        else
 464                                ant_conf->fast_div_bias = 0x2;
 465                        break;
 466                case 0x30: /* A+B A-B */
 467                        ant_conf->fast_div_bias = 0x1;
 468                        break;
 469                case 0x31: /* A+B LNA2 */
 470                        ant_conf->fast_div_bias = 0x1;
 471                        break;
 472                case 0x32: /* A+B LNA1 */
 473                        ant_conf->fast_div_bias = 0x1;
 474                        break;
 475                default:
 476                        break;
 477                }
 478        } else if (ant_conf->div_group == 3) {
 479                switch ((ant_conf->main_lna_conf << 4) |
 480                        ant_conf->alt_lna_conf) {
 481                case 0x01: /* A-B LNA2 */
 482                        ant_conf->fast_div_bias = 0x1;
 483                        break;
 484                case 0x02: /* A-B LNA1 */
 485                        ant_conf->fast_div_bias = 0x39;
 486                        break;
 487                case 0x03: /* A-B A+B */
 488                        ant_conf->fast_div_bias = 0x1;
 489                        break;
 490                case 0x10: /* LNA2 A-B */
 491                        if ((antcomb->scan == 0) &&
 492                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
 493                                ant_conf->fast_div_bias = 0x3f;
 494                        } else {
 495                                ant_conf->fast_div_bias = 0x1;
 496                        }
 497                        break;
 498                case 0x12: /* LNA2 LNA1 */
 499                        ant_conf->fast_div_bias = 0x39;
 500                        break;
 501                case 0x13: /* LNA2 A+B */
 502                        if ((antcomb->scan == 0) &&
 503                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
 504                                ant_conf->fast_div_bias = 0x3f;
 505                        } else {
 506                                ant_conf->fast_div_bias = 0x1;
 507                        }
 508                        break;
 509                case 0x20: /* LNA1 A-B */
 510                        if ((antcomb->scan == 0) &&
 511                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
 512                                ant_conf->fast_div_bias = 0x3f;
 513                        } else {
 514                                ant_conf->fast_div_bias = 0x4;
 515                        }
 516                        break;
 517                case 0x21: /* LNA1 LNA2 */
 518                        ant_conf->fast_div_bias = 0x6;
 519                        break;
 520                case 0x23: /* LNA1 A+B */
 521                        if ((antcomb->scan == 0) &&
 522                            (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
 523                                ant_conf->fast_div_bias = 0x3f;
 524                        } else {
 525                                ant_conf->fast_div_bias = 0x6;
 526                        }
 527                        break;
 528                case 0x30: /* A+B A-B */
 529                        ant_conf->fast_div_bias = 0x1;
 530                        break;
 531                case 0x31: /* A+B LNA2 */
 532                        ant_conf->fast_div_bias = 0x6;
 533                        break;
 534                case 0x32: /* A+B LNA1 */
 535                        ant_conf->fast_div_bias = 0x1;
 536                        break;
 537                default:
 538                        break;
 539                }
 540        }
 541}
 542
 543void ath_ant_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs)
 544{
 545        struct ath_hw_antcomb_conf div_ant_conf;
 546        struct ath_ant_comb *antcomb = &sc->ant_comb;
 547        int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
 548        int curr_main_set;
 549        int main_rssi = rs->rs_rssi_ctl0;
 550        int alt_rssi = rs->rs_rssi_ctl1;
 551        int rx_ant_conf,  main_ant_conf;
 552        bool short_scan = false;
 553
 554        rx_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_CURRENT_SHIFT) &
 555                       ATH_ANT_RX_MASK;
 556        main_ant_conf = (rs->rs_rssi_ctl2 >> ATH_ANT_RX_MAIN_SHIFT) &
 557                         ATH_ANT_RX_MASK;
 558
 559        /* Record packet only when both main_rssi and  alt_rssi is positive */
 560        if (main_rssi > 0 && alt_rssi > 0) {
 561                antcomb->total_pkt_count++;
 562                antcomb->main_total_rssi += main_rssi;
 563                antcomb->alt_total_rssi  += alt_rssi;
 564                if (main_ant_conf == rx_ant_conf)
 565                        antcomb->main_recv_cnt++;
 566                else
 567                        antcomb->alt_recv_cnt++;
 568        }
 569
 570        /* Short scan check */
 571        if (antcomb->scan && antcomb->alt_good) {
 572                if (time_after(jiffies, antcomb->scan_start_time +
 573                    msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
 574                        short_scan = true;
 575                else
 576                        if (antcomb->total_pkt_count ==
 577                            ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
 578                                alt_ratio = ((antcomb->alt_recv_cnt * 100) /
 579                                            antcomb->total_pkt_count);
 580                                if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
 581                                        short_scan = true;
 582                        }
 583        }
 584
 585        if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
 586            rs->rs_moreaggr) && !short_scan)
 587                return;
 588
 589        if (antcomb->total_pkt_count) {
 590                alt_ratio = ((antcomb->alt_recv_cnt * 100) /
 591                             antcomb->total_pkt_count);
 592                main_rssi_avg = (antcomb->main_total_rssi /
 593                                 antcomb->total_pkt_count);
 594                alt_rssi_avg = (antcomb->alt_total_rssi /
 595                                 antcomb->total_pkt_count);
 596        }
 597
 598
 599        ath9k_hw_antdiv_comb_conf_get(sc->sc_ah, &div_ant_conf);
 600        curr_alt_set = div_ant_conf.alt_lna_conf;
 601        curr_main_set = div_ant_conf.main_lna_conf;
 602
 603        antcomb->count++;
 604
 605        if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
 606                if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
 607                        ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
 608                                                  main_rssi_avg);
 609                        antcomb->alt_good = true;
 610                } else {
 611                        antcomb->alt_good = false;
 612                }
 613
 614                antcomb->count = 0;
 615                antcomb->scan = true;
 616                antcomb->scan_not_start = true;
 617        }
 618
 619        if (!antcomb->scan) {
 620                if (ath_ant_div_comb_alt_check(div_ant_conf.div_group,
 621                                        alt_ratio, curr_main_set, curr_alt_set,
 622                                        alt_rssi_avg, main_rssi_avg)) {
 623                        if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
 624                                /* Switch main and alt LNA */
 625                                div_ant_conf.main_lna_conf =
 626                                                ATH_ANT_DIV_COMB_LNA2;
 627                                div_ant_conf.alt_lna_conf  =
 628                                                ATH_ANT_DIV_COMB_LNA1;
 629                        } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
 630                                div_ant_conf.main_lna_conf =
 631                                                ATH_ANT_DIV_COMB_LNA1;
 632                                div_ant_conf.alt_lna_conf  =
 633                                                ATH_ANT_DIV_COMB_LNA2;
 634                        }
 635
 636                        goto div_comb_done;
 637                } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
 638                           (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
 639                        /* Set alt to another LNA */
 640                        if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
 641                                div_ant_conf.alt_lna_conf =
 642                                                ATH_ANT_DIV_COMB_LNA1;
 643                        else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
 644                                div_ant_conf.alt_lna_conf =
 645                                                ATH_ANT_DIV_COMB_LNA2;
 646
 647                        goto div_comb_done;
 648                }
 649
 650                if ((alt_rssi_avg < (main_rssi_avg +
 651                                     div_ant_conf.lna1_lna2_delta)))
 652                        goto div_comb_done;
 653        }
 654
 655        if (!antcomb->scan_not_start) {
 656                switch (curr_alt_set) {
 657                case ATH_ANT_DIV_COMB_LNA2:
 658                        antcomb->rssi_lna2 = alt_rssi_avg;
 659                        antcomb->rssi_lna1 = main_rssi_avg;
 660                        antcomb->scan = true;
 661                        /* set to A+B */
 662                        div_ant_conf.main_lna_conf =
 663                                ATH_ANT_DIV_COMB_LNA1;
 664                        div_ant_conf.alt_lna_conf  =
 665                                ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
 666                        break;
 667                case ATH_ANT_DIV_COMB_LNA1:
 668                        antcomb->rssi_lna1 = alt_rssi_avg;
 669                        antcomb->rssi_lna2 = main_rssi_avg;
 670                        antcomb->scan = true;
 671                        /* set to A+B */
 672                        div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
 673                        div_ant_conf.alt_lna_conf  =
 674                                ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
 675                        break;
 676                case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
 677                        antcomb->rssi_add = alt_rssi_avg;
 678                        antcomb->scan = true;
 679                        /* set to A-B */
 680                        div_ant_conf.alt_lna_conf =
 681                                ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
 682                        break;
 683                case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
 684                        antcomb->rssi_sub = alt_rssi_avg;
 685                        antcomb->scan = false;
 686                        if (antcomb->rssi_lna2 >
 687                            (antcomb->rssi_lna1 +
 688                            ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
 689                                /* use LNA2 as main LNA */
 690                                if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
 691                                    (antcomb->rssi_add > antcomb->rssi_sub)) {
 692                                        /* set to A+B */
 693                                        div_ant_conf.main_lna_conf =
 694                                                ATH_ANT_DIV_COMB_LNA2;
 695                                        div_ant_conf.alt_lna_conf  =
 696                                                ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
 697                                } else if (antcomb->rssi_sub >
 698                                           antcomb->rssi_lna1) {
 699                                        /* set to A-B */
 700                                        div_ant_conf.main_lna_conf =
 701                                                ATH_ANT_DIV_COMB_LNA2;
 702                                        div_ant_conf.alt_lna_conf =
 703                                                ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
 704                                } else {
 705                                        /* set to LNA1 */
 706                                        div_ant_conf.main_lna_conf =
 707                                                ATH_ANT_DIV_COMB_LNA2;
 708                                        div_ant_conf.alt_lna_conf =
 709                                                ATH_ANT_DIV_COMB_LNA1;
 710                                }
 711                        } else {
 712                                /* use LNA1 as main LNA */
 713                                if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
 714                                    (antcomb->rssi_add > antcomb->rssi_sub)) {
 715                                        /* set to A+B */
 716                                        div_ant_conf.main_lna_conf =
 717                                                ATH_ANT_DIV_COMB_LNA1;
 718                                        div_ant_conf.alt_lna_conf  =
 719                                                ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
 720                                } else if (antcomb->rssi_sub >
 721                                           antcomb->rssi_lna1) {
 722                                        /* set to A-B */
 723                                        div_ant_conf.main_lna_conf =
 724                                                ATH_ANT_DIV_COMB_LNA1;
 725                                        div_ant_conf.alt_lna_conf =
 726                                                ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
 727                                } else {
 728                                        /* set to LNA2 */
 729                                        div_ant_conf.main_lna_conf =
 730                                                ATH_ANT_DIV_COMB_LNA1;
 731                                        div_ant_conf.alt_lna_conf =
 732                                                ATH_ANT_DIV_COMB_LNA2;
 733                                }
 734                        }
 735                        break;
 736                default:
 737                        break;
 738                }
 739        } else {
 740                if (!antcomb->alt_good) {
 741                        antcomb->scan_not_start = false;
 742                        /* Set alt to another LNA */
 743                        if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
 744                                div_ant_conf.main_lna_conf =
 745                                                ATH_ANT_DIV_COMB_LNA2;
 746                                div_ant_conf.alt_lna_conf =
 747                                                ATH_ANT_DIV_COMB_LNA1;
 748                        } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
 749                                div_ant_conf.main_lna_conf =
 750                                                ATH_ANT_DIV_COMB_LNA1;
 751                                div_ant_conf.alt_lna_conf =
 752                                                ATH_ANT_DIV_COMB_LNA2;
 753                        }
 754                        goto div_comb_done;
 755                }
 756        }
 757
 758        ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
 759                                           main_rssi_avg, alt_rssi_avg,
 760                                           alt_ratio);
 761
 762        antcomb->quick_scan_cnt++;
 763
 764div_comb_done:
 765        ath_ant_div_conf_fast_divbias(&div_ant_conf, antcomb, alt_ratio);
 766        ath9k_hw_antdiv_comb_conf_set(sc->sc_ah, &div_ant_conf);
 767
 768        antcomb->scan_start_time = jiffies;
 769        antcomb->total_pkt_count = 0;
 770        antcomb->main_total_rssi = 0;
 771        antcomb->alt_total_rssi = 0;
 772        antcomb->main_recv_cnt = 0;
 773        antcomb->alt_recv_cnt = 0;
 774}
 775
 776void ath_ant_comb_update(struct ath_softc *sc)
 777{
 778        struct ath_hw *ah = sc->sc_ah;
 779        struct ath_common *common = ath9k_hw_common(ah);
 780        struct ath_hw_antcomb_conf div_ant_conf;
 781        u8 lna_conf;
 782
 783        ath9k_hw_antdiv_comb_conf_get(ah, &div_ant_conf);
 784
 785        if (sc->ant_rx == 1)
 786                lna_conf = ATH_ANT_DIV_COMB_LNA1;
 787        else
 788                lna_conf = ATH_ANT_DIV_COMB_LNA2;
 789
 790        div_ant_conf.main_lna_conf = lna_conf;
 791        div_ant_conf.alt_lna_conf = lna_conf;
 792
 793        ath9k_hw_antdiv_comb_conf_set(ah, &div_ant_conf);
 794
 795        if (common->antenna_diversity)
 796                ath9k_hw_antctrl_shared_chain_lnadiv(ah, true);
 797}
 798