Add the rt linux 4.1.3-rt3 as base
[kvmfornfv.git] / kernel / drivers / net / wireless / ath / ath9k / antenna.c
1 /*
2  * Copyright (c) 2012 Qualcomm Atheros, Inc.
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include "ath9k.h"
18
19 /*
20  * AR9285
21  * ======
22  *
23  * EEPROM has 2 4-bit fields containing the card configuration.
24  *
25  * antdiv_ctl1:
26  * ------------
27  * bb_enable_ant_div_lnadiv : 1
28  * bb_ant_div_alt_gaintb    : 1
29  * bb_ant_div_main_gaintb   : 1
30  * bb_enable_ant_fast_div   : 1
31  *
32  * antdiv_ctl2:
33  * -----------
34  * bb_ant_div_alt_lnaconf  : 2
35  * bb_ant_div_main_lnaconf : 2
36  *
37  * The EEPROM bits are used as follows:
38  * ------------------------------------
39  *
40  * bb_enable_ant_div_lnadiv      - Enable LNA path rx antenna diversity/combining.
41  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
42  *
43  * bb_ant_div_[alt/main]_gaintb  - 0 -> Antenna config Alt/Main uses gaintable 0
44  *                                 1 -> Antenna config Alt/Main uses gaintable 1
45  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
46  *
47  * bb_enable_ant_fast_div        - Enable fast antenna diversity.
48  *                                 Set in AR_PHY_CCK_DETECT.
49  *
50  * bb_ant_div_[alt/main]_lnaconf - Alt/Main LNA diversity/combining input config.
51  *                                 Set in AR_PHY_MULTICHAIN_GAIN_CTL.
52  *                                 10=LNA1
53  *                                 01=LNA2
54  *                                 11=LNA1+LNA2
55  *                                 00=LNA1-LNA2
56  *
57  * AR9485 / AR9565 / AR9331
58  * ========================
59  *
60  * The same bits are present in the EEPROM, but the location in the
61  * EEPROM is different (ant_div_control in ar9300_BaseExtension_1).
62  *
63  * ant_div_alt_lnaconf      ==> bit 0~1
64  * ant_div_main_lnaconf     ==> bit 2~3
65  * ant_div_alt_gaintb       ==> bit 4
66  * ant_div_main_gaintb      ==> bit 5
67  * enable_ant_div_lnadiv    ==> bit 6
68  * enable_ant_fast_div      ==> bit 7
69  */
70
71 static inline bool ath_is_alt_ant_ratio_better(struct ath_ant_comb *antcomb,
72                                                int alt_ratio, int maxdelta,
73                                                int mindelta, int main_rssi_avg,
74                                                int alt_rssi_avg, int pkt_count)
75 {
76         if (pkt_count <= 50)
77                 return false;
78
79         if (alt_rssi_avg > main_rssi_avg + mindelta)
80                 return true;
81
82         if (alt_ratio >= antcomb->ant_ratio2 &&
83             alt_rssi_avg >= antcomb->low_rssi_thresh &&
84             (alt_rssi_avg > main_rssi_avg + maxdelta))
85                 return true;
86
87         return false;
88 }
89
90 static inline bool ath_ant_div_comb_alt_check(struct ath_hw_antcomb_conf *conf,
91                                               struct ath_ant_comb *antcomb,
92                                               int alt_ratio, int alt_rssi_avg,
93                                               int main_rssi_avg)
94 {
95         bool result, set1, set2;
96
97         result = set1 = set2 = false;
98
99         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2 &&
100             conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA1)
101                 set1 = true;
102
103         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA1 &&
104             conf->alt_lna_conf == ATH_ANT_DIV_COMB_LNA2)
105                 set2 = true;
106
107         switch (conf->div_group) {
108         case 0:
109                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
110                         result = true;
111                 break;
112         case 1:
113         case 2:
114                 if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
115                         break;
116
117                 if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 5))) ||
118                     (set2 && (alt_rssi_avg >= (main_rssi_avg - 2))) ||
119                     (alt_ratio > antcomb->ant_ratio))
120                         result = true;
121
122                 break;
123         case 3:
124                 if (alt_rssi_avg < 4 || alt_rssi_avg < antcomb->low_rssi_thresh)
125                         break;
126
127                 if ((set1 && (alt_rssi_avg >= (main_rssi_avg - 3))) ||
128                     (set2 && (alt_rssi_avg >= (main_rssi_avg + 3))) ||
129                     (alt_ratio > antcomb->ant_ratio))
130                         result = true;
131
132                 break;
133         }
134
135         return result;
136 }
137
138 static void ath_lnaconf_alt_good_scan(struct ath_ant_comb *antcomb,
139                                       struct ath_hw_antcomb_conf ant_conf,
140                                       int main_rssi_avg)
141 {
142         antcomb->quick_scan_cnt = 0;
143
144         if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
145                 antcomb->rssi_lna2 = main_rssi_avg;
146         else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
147                 antcomb->rssi_lna1 = main_rssi_avg;
148
149         switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
150         case 0x10: /* LNA2 A-B */
151                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
152                 antcomb->first_quick_scan_conf =
153                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
154                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
155                 break;
156         case 0x20: /* LNA1 A-B */
157                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
158                 antcomb->first_quick_scan_conf =
159                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
160                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
161                 break;
162         case 0x21: /* LNA1 LNA2 */
163                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
164                 antcomb->first_quick_scan_conf =
165                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
166                 antcomb->second_quick_scan_conf =
167                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
168                 break;
169         case 0x12: /* LNA2 LNA1 */
170                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
171                 antcomb->first_quick_scan_conf =
172                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
173                 antcomb->second_quick_scan_conf =
174                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
175                 break;
176         case 0x13: /* LNA2 A+B */
177                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
178                 antcomb->first_quick_scan_conf =
179                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
180                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
181                 break;
182         case 0x23: /* LNA1 A+B */
183                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
184                 antcomb->first_quick_scan_conf =
185                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
186                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
187                 break;
188         default:
189                 break;
190         }
191 }
192
193 static void ath_ant_set_alt_ratio(struct ath_ant_comb *antcomb,
194                                   struct ath_hw_antcomb_conf *conf)
195 {
196         /* set alt to the conf with maximun ratio */
197         if (antcomb->first_ratio && antcomb->second_ratio) {
198                 if (antcomb->rssi_second > antcomb->rssi_third) {
199                         /* first alt*/
200                         if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
201                             (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
202                                 /* Set alt LNA1 or LNA2*/
203                                 if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
204                                         conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
205                                 else
206                                         conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
207                         else
208                                 /* Set alt to A+B or A-B */
209                                 conf->alt_lna_conf =
210                                         antcomb->first_quick_scan_conf;
211                 } else if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
212                            (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2)) {
213                         /* Set alt LNA1 or LNA2 */
214                         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
215                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
216                         else
217                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
218                 } else {
219                         /* Set alt to A+B or A-B */
220                         conf->alt_lna_conf = antcomb->second_quick_scan_conf;
221                 }
222         } else if (antcomb->first_ratio) {
223                 /* first alt */
224                 if ((antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
225                     (antcomb->first_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
226                         /* Set alt LNA1 or LNA2 */
227                         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
228                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
229                         else
230                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
231                 else
232                         /* Set alt to A+B or A-B */
233                         conf->alt_lna_conf = antcomb->first_quick_scan_conf;
234         } else if (antcomb->second_ratio) {
235                 /* second alt */
236                 if ((antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1) ||
237                     (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA2))
238                         /* Set alt LNA1 or LNA2 */
239                         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
240                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
241                         else
242                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
243                 else
244                         /* Set alt to A+B or A-B */
245                         conf->alt_lna_conf = antcomb->second_quick_scan_conf;
246         } else {
247                 /* main is largest */
248                 if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
249                     (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
250                         /* Set alt LNA1 or LNA2 */
251                         if (conf->main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
252                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA1;
253                         else
254                                 conf->alt_lna_conf = ATH_ANT_DIV_COMB_LNA2;
255                 else
256                         /* Set alt to A+B or A-B */
257                         conf->alt_lna_conf = antcomb->main_conf;
258         }
259 }
260
261 static void ath_select_ant_div_from_quick_scan(struct ath_ant_comb *antcomb,
262                                        struct ath_hw_antcomb_conf *div_ant_conf,
263                                        int main_rssi_avg, int alt_rssi_avg,
264                                        int alt_ratio)
265 {
266         /* alt_good */
267         switch (antcomb->quick_scan_cnt) {
268         case 0:
269                 /* set alt to main, and alt to first conf */
270                 div_ant_conf->main_lna_conf = antcomb->main_conf;
271                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
272                 break;
273         case 1:
274                 /* set alt to main, and alt to first conf */
275                 div_ant_conf->main_lna_conf = antcomb->main_conf;
276                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
277                 antcomb->rssi_first = main_rssi_avg;
278                 antcomb->rssi_second = alt_rssi_avg;
279
280                 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
281                         /* main is LNA1 */
282                         if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
283                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
284                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
285                                                 main_rssi_avg, alt_rssi_avg,
286                                                 antcomb->total_pkt_count))
287                                 antcomb->first_ratio = true;
288                         else
289                                 antcomb->first_ratio = false;
290                 } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
291                         if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
292                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
293                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
294                                                 main_rssi_avg, alt_rssi_avg,
295                                                 antcomb->total_pkt_count))
296                                 antcomb->first_ratio = true;
297                         else
298                                 antcomb->first_ratio = false;
299                 } else {
300                         if (ath_is_alt_ant_ratio_better(antcomb, alt_ratio,
301                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
302                                                 0,
303                                                 main_rssi_avg, alt_rssi_avg,
304                                                 antcomb->total_pkt_count))
305                                 antcomb->first_ratio = true;
306                         else
307                                 antcomb->first_ratio = false;
308                 }
309                 break;
310         case 2:
311                 antcomb->alt_good = false;
312                 antcomb->scan_not_start = false;
313                 antcomb->scan = false;
314                 antcomb->rssi_first = main_rssi_avg;
315                 antcomb->rssi_third = alt_rssi_avg;
316
317                 switch(antcomb->second_quick_scan_conf) {
318                 case ATH_ANT_DIV_COMB_LNA1:
319                         antcomb->rssi_lna1 = alt_rssi_avg;
320                         break;
321                 case ATH_ANT_DIV_COMB_LNA2:
322                         antcomb->rssi_lna2 = alt_rssi_avg;
323                         break;
324                 case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
325                         if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
326                                 antcomb->rssi_lna2 = main_rssi_avg;
327                         else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
328                                 antcomb->rssi_lna1 = main_rssi_avg;
329                         break;
330                 default:
331                         break;
332                 }
333
334                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
335                     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
377 static void ath_ant_div_conf_fast_divbias(struct ath_hw_antcomb_conf *ant_conf,
378                                           struct ath_ant_comb *antcomb,
379                                           int alt_ratio)
380 {
381         ant_conf->main_gaintb = 0;
382         ant_conf->alt_gaintb = 0;
383
384         if (ant_conf->div_group == 0) {
385                 /* Adjust the fast_div_bias based on main and alt lna conf */
386                 switch ((ant_conf->main_lna_conf << 4) |
387                                 ant_conf->alt_lna_conf) {
388                 case 0x01: /* A-B LNA2 */
389                         ant_conf->fast_div_bias = 0x3b;
390                         break;
391                 case 0x02: /* A-B LNA1 */
392                         ant_conf->fast_div_bias = 0x3d;
393                         break;
394                 case 0x03: /* A-B A+B */
395                         ant_conf->fast_div_bias = 0x1;
396                         break;
397                 case 0x10: /* LNA2 A-B */
398                         ant_conf->fast_div_bias = 0x7;
399                         break;
400                 case 0x12: /* LNA2 LNA1 */
401                         ant_conf->fast_div_bias = 0x2;
402                         break;
403                 case 0x13: /* LNA2 A+B */
404                         ant_conf->fast_div_bias = 0x7;
405                         break;
406                 case 0x20: /* LNA1 A-B */
407                         ant_conf->fast_div_bias = 0x6;
408                         break;
409                 case 0x21: /* LNA1 LNA2 */
410                         ant_conf->fast_div_bias = 0x0;
411                         break;
412                 case 0x23: /* LNA1 A+B */
413                         ant_conf->fast_div_bias = 0x6;
414                         break;
415                 case 0x30: /* A+B A-B */
416                         ant_conf->fast_div_bias = 0x1;
417                         break;
418                 case 0x31: /* A+B LNA2 */
419                         ant_conf->fast_div_bias = 0x3b;
420                         break;
421                 case 0x32: /* A+B LNA1 */
422                         ant_conf->fast_div_bias = 0x3d;
423                         break;
424                 default:
425                         break;
426                 }
427         } else if (ant_conf->div_group == 1) {
428                 /* Adjust the fast_div_bias based on main and alt_lna_conf */
429                 switch ((ant_conf->main_lna_conf << 4) |
430                         ant_conf->alt_lna_conf) {
431                 case 0x01: /* A-B LNA2 */
432                         ant_conf->fast_div_bias = 0x1;
433                         break;
434                 case 0x02: /* A-B LNA1 */
435                         ant_conf->fast_div_bias = 0x1;
436                         break;
437                 case 0x03: /* A-B A+B */
438                         ant_conf->fast_div_bias = 0x1;
439                         break;
440                 case 0x10: /* LNA2 A-B */
441                         if (!(antcomb->scan) &&
442                             (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
443                                 ant_conf->fast_div_bias = 0x3f;
444                         else
445                                 ant_conf->fast_div_bias = 0x1;
446                         break;
447                 case 0x12: /* LNA2 LNA1 */
448                         ant_conf->fast_div_bias = 0x1;
449                         break;
450                 case 0x13: /* LNA2 A+B */
451                         if (!(antcomb->scan) &&
452                             (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
453                                 ant_conf->fast_div_bias = 0x3f;
454                         else
455                                 ant_conf->fast_div_bias = 0x1;
456                         break;
457                 case 0x20: /* LNA1 A-B */
458                         if (!(antcomb->scan) &&
459                             (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
460                                 ant_conf->fast_div_bias = 0x3f;
461                         else
462                                 ant_conf->fast_div_bias = 0x1;
463                         break;
464                 case 0x21: /* LNA1 LNA2 */
465                         ant_conf->fast_div_bias = 0x1;
466                         break;
467                 case 0x23: /* LNA1 A+B */
468                         if (!(antcomb->scan) &&
469                             (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO))
470                                 ant_conf->fast_div_bias = 0x3f;
471                         else
472                                 ant_conf->fast_div_bias = 0x1;
473                         break;
474                 case 0x30: /* A+B A-B */
475                         ant_conf->fast_div_bias = 0x1;
476                         break;
477                 case 0x31: /* A+B LNA2 */
478                         ant_conf->fast_div_bias = 0x1;
479                         break;
480                 case 0x32: /* A+B LNA1 */
481                         ant_conf->fast_div_bias = 0x1;
482                         break;
483                 default:
484                         break;
485                 }
486         } else if (ant_conf->div_group == 2) {
487                 /* Adjust the fast_div_bias based on main and alt_lna_conf */
488                 switch ((ant_conf->main_lna_conf << 4) |
489                                 ant_conf->alt_lna_conf) {
490                 case 0x01: /* A-B LNA2 */
491                         ant_conf->fast_div_bias = 0x1;
492                         break;
493                 case 0x02: /* A-B LNA1 */
494                         ant_conf->fast_div_bias = 0x1;
495                         break;
496                 case 0x03: /* A-B A+B */
497                         ant_conf->fast_div_bias = 0x1;
498                         break;
499                 case 0x10: /* LNA2 A-B */
500                         if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
501                                 ant_conf->fast_div_bias = 0x1;
502                         else
503                                 ant_conf->fast_div_bias = 0x2;
504                         break;
505                 case 0x12: /* LNA2 LNA1 */
506                         ant_conf->fast_div_bias = 0x1;
507                         break;
508                 case 0x13: /* LNA2 A+B */
509                         if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
510                                 ant_conf->fast_div_bias = 0x1;
511                         else
512                                 ant_conf->fast_div_bias = 0x2;
513                         break;
514                 case 0x20: /* LNA1 A-B */
515                         if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
516                                 ant_conf->fast_div_bias = 0x1;
517                         else
518                                 ant_conf->fast_div_bias = 0x2;
519                         break;
520                 case 0x21: /* LNA1 LNA2 */
521                         ant_conf->fast_div_bias = 0x1;
522                         break;
523                 case 0x23: /* LNA1 A+B */
524                         if (!antcomb->scan && (alt_ratio > antcomb->ant_ratio))
525                                 ant_conf->fast_div_bias = 0x1;
526                         else
527                                 ant_conf->fast_div_bias = 0x2;
528                         break;
529                 case 0x30: /* A+B A-B */
530                         ant_conf->fast_div_bias = 0x1;
531                         break;
532                 case 0x31: /* A+B LNA2 */
533                         ant_conf->fast_div_bias = 0x1;
534                         break;
535                 case 0x32: /* A+B LNA1 */
536                         ant_conf->fast_div_bias = 0x1;
537                         break;
538                 default:
539                         break;
540                 }
541
542                 if (antcomb->fast_div_bias)
543                         ant_conf->fast_div_bias = antcomb->fast_div_bias;
544         } else if (ant_conf->div_group == 3) {
545                 switch ((ant_conf->main_lna_conf << 4) |
546                         ant_conf->alt_lna_conf) {
547                 case 0x01: /* A-B LNA2 */
548                         ant_conf->fast_div_bias = 0x1;
549                         break;
550                 case 0x02: /* A-B LNA1 */
551                         ant_conf->fast_div_bias = 0x39;
552                         break;
553                 case 0x03: /* A-B A+B */
554                         ant_conf->fast_div_bias = 0x1;
555                         break;
556                 case 0x10: /* LNA2 A-B */
557                         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
589 static 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
662 static 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
700 static 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
721 void 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(ANT_MAIN, recv_cnt);
759                 ANT_LNA_INC(ANT_MAIN, rx_ant_conf);
760         } else {
761                 ANT_STAT_INC(ANT_ALT, recv_cnt);
762                 ANT_LNA_INC(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
838 div_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 }