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