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