]> CyberLeo.Net >> Repos - FreeBSD/releng/9.2.git/blob - sys/dev/ath/ath_hal/ar9002/ar9285_diversity.c
- Copy stable/9 to releng/9.2 as part of the 9.2-RELEASE cycle.
[FreeBSD/releng/9.2.git] / sys / dev / ath / ath_hal / ar9002 / ar9285_diversity.c
1 /*
2  * Copyright (c) 2008-2010 Atheros Communications Inc.
3  * Copyright (c) 2011 Adrian Chadd, Xenion Pty Ltd.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer.
10  * 2. Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *
14  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
15  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
17  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
18  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
19  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
20  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
21  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
22  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
23  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
24  * SUCH DAMAGE.
25  *
26  * $FreeBSD$
27  */
28 #include "opt_ah.h"
29
30 #include "ah.h"
31 #include "ah_desc.h"
32 #include "ah_internal.h"
33 #include "ah_eeprom_v4k.h"
34
35 #include "ar9002/ar9280.h"
36 #include "ar9002/ar9285_diversity.h"
37 #include "ar9002/ar9285.h"
38 #include "ar5416/ar5416reg.h"
39 #include "ar5416/ar5416phy.h"
40 #include "ar9002/ar9285phy.h"
41 #include "ar9002/ar9285_phy.h"
42
43
44 /* Linux compability macros */
45 /*
46  * XXX these don't handle rounding, underflow, overflow, wrapping!
47  */
48 #define msecs_to_jiffies(a)             ( (a) * hz / 1000 )
49 #define time_after(a, b)                ( (long) (b) - (long) (a) < 0 )
50
51 static HAL_BOOL
52 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
53     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
54 {
55         return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
56                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
57                 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
58 }
59
60 static void
61 ath_lnaconf_alt_good_scan(struct ar9285_ant_comb *antcomb,
62     struct ar9285_antcomb_conf ant_conf, int main_rssi_avg)
63 {
64         antcomb->quick_scan_cnt = 0;
65
66         if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA2)
67                 antcomb->rssi_lna2 = main_rssi_avg;
68         else if (ant_conf.main_lna_conf == ATH_ANT_DIV_COMB_LNA1)
69                 antcomb->rssi_lna1 = main_rssi_avg;
70
71         switch ((ant_conf.main_lna_conf << 4) | ant_conf.alt_lna_conf) {
72         case (0x10): /* LNA2 A-B */
73                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
74                 antcomb->first_quick_scan_conf =
75                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
76                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
77                 break;
78         case (0x20): /* LNA1 A-B */
79                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
80                 antcomb->first_quick_scan_conf =
81                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
82                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
83                 break;
84         case (0x21): /* LNA1 LNA2 */
85                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA2;
86                 antcomb->first_quick_scan_conf =
87                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
88                 antcomb->second_quick_scan_conf =
89                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
90                 break;
91         case (0x12): /* LNA2 LNA1 */
92                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1;
93                 antcomb->first_quick_scan_conf =
94                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
95                 antcomb->second_quick_scan_conf =
96                         ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
97                 break;
98         case (0x13): /* LNA2 A+B */
99                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
100                 antcomb->first_quick_scan_conf =
101                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
102                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA1;
103                 break;
104         case (0x23): /* LNA1 A+B */
105                 antcomb->main_conf = ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
106                 antcomb->first_quick_scan_conf =
107                         ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
108                 antcomb->second_quick_scan_conf = ATH_ANT_DIV_COMB_LNA2;
109                 break;
110         default:
111                 break;
112         }
113 }
114
115 static void
116 ath_select_ant_div_from_quick_scan(struct ar9285_ant_comb *antcomb,
117     struct ar9285_antcomb_conf *div_ant_conf, int main_rssi_avg,
118     int alt_rssi_avg, int alt_ratio)
119 {
120         /* alt_good */
121         switch (antcomb->quick_scan_cnt) {
122         case 0:
123                 /* set alt to main, and alt to first conf */
124                 div_ant_conf->main_lna_conf = antcomb->main_conf;
125                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
126                 break;
127         case 1:
128                 /* set alt to main, and alt to first conf */
129                 div_ant_conf->main_lna_conf = antcomb->main_conf;
130                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
131                 antcomb->rssi_first = main_rssi_avg;
132                 antcomb->rssi_second = alt_rssi_avg;
133
134                 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
135                         /* main is LNA1 */
136                         if (ath_is_alt_ant_ratio_better(alt_ratio,
137                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
138                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
139                                                 main_rssi_avg, alt_rssi_avg,
140                                                 antcomb->total_pkt_count))
141                                 antcomb->first_ratio = AH_TRUE;
142                         else
143                                 antcomb->first_ratio = AH_FALSE;
144                 } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
145                         if (ath_is_alt_ant_ratio_better(alt_ratio,
146                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
147                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
148                                                 main_rssi_avg, alt_rssi_avg,
149                                                 antcomb->total_pkt_count))
150                                 antcomb->first_ratio = AH_TRUE;
151                         else
152                                 antcomb->first_ratio = AH_FALSE;
153                 } else {
154                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
155                             (alt_rssi_avg > main_rssi_avg +
156                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
157                             (alt_rssi_avg > main_rssi_avg)) &&
158                             (antcomb->total_pkt_count > 50))
159                                 antcomb->first_ratio = AH_TRUE;
160                         else
161                                 antcomb->first_ratio = AH_FALSE;
162                 }
163                 break;
164         case 2:
165                 antcomb->alt_good = AH_FALSE;
166                 antcomb->scan_not_start = AH_FALSE;
167                 antcomb->scan = AH_FALSE;
168                 antcomb->rssi_first = main_rssi_avg;
169                 antcomb->rssi_third = alt_rssi_avg;
170
171                 if (antcomb->second_quick_scan_conf == ATH_ANT_DIV_COMB_LNA1)
172                         antcomb->rssi_lna1 = alt_rssi_avg;
173                 else if (antcomb->second_quick_scan_conf ==
174                          ATH_ANT_DIV_COMB_LNA2)
175                         antcomb->rssi_lna2 = alt_rssi_avg;
176                 else if (antcomb->second_quick_scan_conf ==
177                          ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
178                         if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2)
179                                 antcomb->rssi_lna2 = main_rssi_avg;
180                         else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1)
181                                 antcomb->rssi_lna1 = main_rssi_avg;
182                 }
183
184                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
185                     ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
186                         div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
187                 else
188                         div_ant_conf->main_lna_conf = ATH_ANT_DIV_COMB_LNA1;
189
190                 if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) {
191                         if (ath_is_alt_ant_ratio_better(alt_ratio,
192                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
193                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
194                                                 main_rssi_avg, alt_rssi_avg,
195                                                 antcomb->total_pkt_count))
196                                 antcomb->second_ratio = AH_TRUE;
197                         else
198                                 antcomb->second_ratio = AH_FALSE;
199                 } else if (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2) {
200                         if (ath_is_alt_ant_ratio_better(alt_ratio,
201                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
202                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
203                                                 main_rssi_avg, alt_rssi_avg,
204                                                 antcomb->total_pkt_count))
205                                 antcomb->second_ratio = AH_TRUE;
206                         else
207                                 antcomb->second_ratio = AH_FALSE;
208                 } else {
209                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
210                             (alt_rssi_avg > main_rssi_avg +
211                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
212                             (alt_rssi_avg > main_rssi_avg)) &&
213                             (antcomb->total_pkt_count > 50))
214                                 antcomb->second_ratio = AH_TRUE;
215                         else
216                                 antcomb->second_ratio = AH_FALSE;
217                 }
218
219                 /* set alt to the conf with maximun ratio */
220                 if (antcomb->first_ratio && antcomb->second_ratio) {
221                         if (antcomb->rssi_second > antcomb->rssi_third) {
222                                 /* first alt*/
223                                 if ((antcomb->first_quick_scan_conf ==
224                                     ATH_ANT_DIV_COMB_LNA1) ||
225                                     (antcomb->first_quick_scan_conf ==
226                                     ATH_ANT_DIV_COMB_LNA2))
227                                         /* Set alt LNA1 or LNA2*/
228                                         if (div_ant_conf->main_lna_conf ==
229                                             ATH_ANT_DIV_COMB_LNA2)
230                                                 div_ant_conf->alt_lna_conf =
231                                                         ATH_ANT_DIV_COMB_LNA1;
232                                         else
233                                                 div_ant_conf->alt_lna_conf =
234                                                         ATH_ANT_DIV_COMB_LNA2;
235                                 else
236                                         /* Set alt to A+B or A-B */
237                                         div_ant_conf->alt_lna_conf =
238                                                 antcomb->first_quick_scan_conf;
239                         } else if ((antcomb->second_quick_scan_conf ==
240                                    ATH_ANT_DIV_COMB_LNA1) ||
241                                    (antcomb->second_quick_scan_conf ==
242                                    ATH_ANT_DIV_COMB_LNA2)) {
243                                 /* Set alt LNA1 or LNA2 */
244                                 if (div_ant_conf->main_lna_conf ==
245                                     ATH_ANT_DIV_COMB_LNA2)
246                                         div_ant_conf->alt_lna_conf =
247                                                 ATH_ANT_DIV_COMB_LNA1;
248                                 else
249                                         div_ant_conf->alt_lna_conf =
250                                                 ATH_ANT_DIV_COMB_LNA2;
251                         } else {
252                                 /* Set alt to A+B or A-B */
253                                 div_ant_conf->alt_lna_conf =
254                                         antcomb->second_quick_scan_conf;
255                         }
256                 } else if (antcomb->first_ratio) {
257                         /* first alt */
258                         if ((antcomb->first_quick_scan_conf ==
259                             ATH_ANT_DIV_COMB_LNA1) ||
260                             (antcomb->first_quick_scan_conf ==
261                             ATH_ANT_DIV_COMB_LNA2))
262                                         /* Set alt LNA1 or LNA2 */
263                                 if (div_ant_conf->main_lna_conf ==
264                                     ATH_ANT_DIV_COMB_LNA2)
265                                         div_ant_conf->alt_lna_conf =
266                                                         ATH_ANT_DIV_COMB_LNA1;
267                                 else
268                                         div_ant_conf->alt_lna_conf =
269                                                         ATH_ANT_DIV_COMB_LNA2;
270                         else
271                                 /* Set alt to A+B or A-B */
272                                 div_ant_conf->alt_lna_conf =
273                                                 antcomb->first_quick_scan_conf;
274                 } else if (antcomb->second_ratio) {
275                                 /* second alt */
276                         if ((antcomb->second_quick_scan_conf ==
277                             ATH_ANT_DIV_COMB_LNA1) ||
278                             (antcomb->second_quick_scan_conf ==
279                             ATH_ANT_DIV_COMB_LNA2))
280                                 /* Set alt LNA1 or LNA2 */
281                                 if (div_ant_conf->main_lna_conf ==
282                                     ATH_ANT_DIV_COMB_LNA2)
283                                         div_ant_conf->alt_lna_conf =
284                                                 ATH_ANT_DIV_COMB_LNA1;
285                                 else
286                                         div_ant_conf->alt_lna_conf =
287                                                 ATH_ANT_DIV_COMB_LNA2;
288                         else
289                                 /* Set alt to A+B or A-B */
290                                 div_ant_conf->alt_lna_conf =
291                                                 antcomb->second_quick_scan_conf;
292                 } else {
293                         /* main is largest */
294                         if ((antcomb->main_conf == ATH_ANT_DIV_COMB_LNA1) ||
295                             (antcomb->main_conf == ATH_ANT_DIV_COMB_LNA2))
296                                 /* Set alt LNA1 or LNA2 */
297                                 if (div_ant_conf->main_lna_conf ==
298                                     ATH_ANT_DIV_COMB_LNA2)
299                                         div_ant_conf->alt_lna_conf =
300                                                         ATH_ANT_DIV_COMB_LNA1;
301                                 else
302                                         div_ant_conf->alt_lna_conf =
303                                                         ATH_ANT_DIV_COMB_LNA2;
304                         else
305                                 /* Set alt to A+B or A-B */
306                                 div_ant_conf->alt_lna_conf = antcomb->main_conf;
307                 }
308                 break;
309         default:
310                 break;
311         }
312 }
313
314 static void
315 ath_ant_div_conf_fast_divbias(struct ar9285_antcomb_conf *ant_conf)
316 {
317         /* Adjust the fast_div_bias based on main and alt lna conf */
318         switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
319         case (0x01): /* A-B LNA2 */
320                 ant_conf->fast_div_bias = 0x3b;
321                 break;
322         case (0x02): /* A-B LNA1 */
323                 ant_conf->fast_div_bias = 0x3d;
324                 break;
325         case (0x03): /* A-B A+B */
326                 ant_conf->fast_div_bias = 0x1;
327                 break;
328         case (0x10): /* LNA2 A-B */
329                 ant_conf->fast_div_bias = 0x7;
330                 break;
331         case (0x12): /* LNA2 LNA1 */
332                 ant_conf->fast_div_bias = 0x2;
333                 break;
334         case (0x13): /* LNA2 A+B */
335                 ant_conf->fast_div_bias = 0x7;
336                 break;
337         case (0x20): /* LNA1 A-B */
338                 ant_conf->fast_div_bias = 0x6;
339                 break;
340         case (0x21): /* LNA1 LNA2 */
341                 ant_conf->fast_div_bias = 0x0;
342                 break;
343         case (0x23): /* LNA1 A+B */
344                 ant_conf->fast_div_bias = 0x6;
345                 break;
346         case (0x30): /* A+B A-B */
347                 ant_conf->fast_div_bias = 0x1;
348                 break;
349         case (0x31): /* A+B LNA2 */
350                 ant_conf->fast_div_bias = 0x3b;
351                 break;
352         case (0x32): /* A+B LNA1 */
353                 ant_conf->fast_div_bias = 0x3d;
354                 break;
355         default:
356                 break;
357         }
358 }
359
360 /* Antenna diversity and combining */
361 void
362 ar9285_ant_comb_scan(struct ath_hal *ah, struct ath_rx_status *rs,
363     unsigned long ticks, int hz)
364 {
365         struct ar9285_antcomb_conf div_ant_conf;
366         struct ar9285_ant_comb *antcomb = &AH9285(ah)->ant_comb;
367         int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
368         int curr_main_set, curr_bias;
369         int main_rssi = rs->rs_rssi_ctl[0];
370         int alt_rssi = rs->rs_rssi_ctl[1];
371         int rx_ant_conf, main_ant_conf, alt_ant_conf;
372         HAL_BOOL short_scan = AH_FALSE;
373
374         rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
375         main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
376         alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
377
378 #if 0
379         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; FastDiv: %d\n",
380             __func__, main_rssi, alt_rssi, main_ant_conf,alt_ant_conf, rx_ant_conf,
381             !!(rs->rs_rssi_ctl[2] & 0x80), !!(rs->rs_rssi_ctl[2] & 0x40), !!(rs->rs_rssi_ext[2] & 0x40));
382 #endif
383
384         if (! ar9285_check_div_comb(ah))
385                 return;
386
387         if (AH5212(ah)->ah_diversity == AH_FALSE)
388                 return;
389
390 #if 0
391         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main: %d, alt: %d, rx_ant_conf: %x, main_ant_conf: %x\n",
392             __func__, main_rssi, alt_rssi, rx_ant_conf, main_ant_conf);
393 #endif
394
395         /* Record packet only when alt_rssi is positive */
396         if (main_rssi > 0 && alt_rssi > 0) {
397                 antcomb->total_pkt_count++;
398                 antcomb->main_total_rssi += main_rssi;
399                 antcomb->alt_total_rssi  += alt_rssi;
400                 if (main_ant_conf == rx_ant_conf)
401                         antcomb->main_recv_cnt++;
402                 else
403                         antcomb->alt_recv_cnt++;
404         }
405
406         /* Short scan check */
407         if (antcomb->scan && antcomb->alt_good) {
408                 if (time_after(ticks, antcomb->scan_start_time +
409                     msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
410                         short_scan = AH_TRUE;
411                 else
412                         if (antcomb->total_pkt_count ==
413                             ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
414                                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
415                                             antcomb->total_pkt_count);
416                                 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
417                                         short_scan = AH_TRUE;
418                         }
419         }
420
421         if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
422             rs->rs_moreaggr) && !short_scan)
423                 return;
424
425         if (antcomb->total_pkt_count) {
426                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
427                              antcomb->total_pkt_count);
428                 main_rssi_avg = (antcomb->main_total_rssi /
429                                  antcomb->total_pkt_count);
430                 alt_rssi_avg = (antcomb->alt_total_rssi /
431                                  antcomb->total_pkt_count);
432         }
433
434         ar9285_antdiv_comb_conf_get(ah, &div_ant_conf);
435         curr_alt_set = div_ant_conf.alt_lna_conf;
436         curr_main_set = div_ant_conf.main_lna_conf;
437         curr_bias = div_ant_conf.fast_div_bias;
438
439         antcomb->count++;
440
441         if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
442                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
443                         ath_lnaconf_alt_good_scan(antcomb, div_ant_conf,
444                                                   main_rssi_avg);
445                         antcomb->alt_good = AH_TRUE;
446                 } else {
447                         antcomb->alt_good = AH_FALSE;
448                 }
449
450                 antcomb->count = 0;
451                 antcomb->scan = AH_TRUE;
452                 antcomb->scan_not_start = AH_TRUE;
453         }
454
455         if (!antcomb->scan) {
456                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
457                         if (curr_alt_set == ATH_ANT_DIV_COMB_LNA2) {
458                                 /* Switch main and alt LNA */
459                                 div_ant_conf.main_lna_conf =
460                                                 ATH_ANT_DIV_COMB_LNA2;
461                                 div_ant_conf.alt_lna_conf  =
462                                                 ATH_ANT_DIV_COMB_LNA1;
463                         } else if (curr_alt_set == ATH_ANT_DIV_COMB_LNA1) {
464                                 div_ant_conf.main_lna_conf =
465                                                 ATH_ANT_DIV_COMB_LNA1;
466                                 div_ant_conf.alt_lna_conf  =
467                                                 ATH_ANT_DIV_COMB_LNA2;
468                         }
469
470                         goto div_comb_done;
471                 } else if ((curr_alt_set != ATH_ANT_DIV_COMB_LNA1) &&
472                            (curr_alt_set != ATH_ANT_DIV_COMB_LNA2)) {
473                         /* Set alt to another LNA */
474                         if (curr_main_set == ATH_ANT_DIV_COMB_LNA2)
475                                 div_ant_conf.alt_lna_conf =
476                                                 ATH_ANT_DIV_COMB_LNA1;
477                         else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1)
478                                 div_ant_conf.alt_lna_conf =
479                                                 ATH_ANT_DIV_COMB_LNA2;
480
481                         goto div_comb_done;
482                 }
483
484                 if ((alt_rssi_avg < (main_rssi_avg +
485                     ATH_ANT_DIV_COMB_LNA1_LNA2_DELTA)))
486                         goto div_comb_done;
487         }
488
489         if (!antcomb->scan_not_start) {
490                 switch (curr_alt_set) {
491                 case ATH_ANT_DIV_COMB_LNA2:
492                         antcomb->rssi_lna2 = alt_rssi_avg;
493                         antcomb->rssi_lna1 = main_rssi_avg;
494                         antcomb->scan = AH_TRUE;
495                         /* set to A+B */
496                         div_ant_conf.main_lna_conf =
497                                 ATH_ANT_DIV_COMB_LNA1;
498                         div_ant_conf.alt_lna_conf  =
499                                 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
500                         break;
501                 case ATH_ANT_DIV_COMB_LNA1:
502                         antcomb->rssi_lna1 = alt_rssi_avg;
503                         antcomb->rssi_lna2 = main_rssi_avg;
504                         antcomb->scan = AH_TRUE;
505                         /* set to A+B */
506                         div_ant_conf.main_lna_conf = ATH_ANT_DIV_COMB_LNA2;
507                         div_ant_conf.alt_lna_conf  =
508                                 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
509                         break;
510                 case ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2:
511                         antcomb->rssi_add = alt_rssi_avg;
512                         antcomb->scan = AH_TRUE;
513                         /* set to A-B */
514                         div_ant_conf.alt_lna_conf =
515                                 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
516                         break;
517                 case ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2:
518                         antcomb->rssi_sub = alt_rssi_avg;
519                         antcomb->scan = AH_FALSE;
520                         if (antcomb->rssi_lna2 >
521                             (antcomb->rssi_lna1 +
522                             ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
523                                 /* use LNA2 as main LNA */
524                                 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
525                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
526                                         /* set to A+B */
527                                         div_ant_conf.main_lna_conf =
528                                                 ATH_ANT_DIV_COMB_LNA2;
529                                         div_ant_conf.alt_lna_conf  =
530                                                 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
531                                 } else if (antcomb->rssi_sub >
532                                            antcomb->rssi_lna1) {
533                                         /* set to A-B */
534                                         div_ant_conf.main_lna_conf =
535                                                 ATH_ANT_DIV_COMB_LNA2;
536                                         div_ant_conf.alt_lna_conf =
537                                                 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
538                                 } else {
539                                         /* set to LNA1 */
540                                         div_ant_conf.main_lna_conf =
541                                                 ATH_ANT_DIV_COMB_LNA2;
542                                         div_ant_conf.alt_lna_conf =
543                                                 ATH_ANT_DIV_COMB_LNA1;
544                                 }
545                         } else {
546                                 /* use LNA1 as main LNA */
547                                 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
548                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
549                                         /* set to A+B */
550                                         div_ant_conf.main_lna_conf =
551                                                 ATH_ANT_DIV_COMB_LNA1;
552                                         div_ant_conf.alt_lna_conf  =
553                                                 ATH_ANT_DIV_COMB_LNA1_PLUS_LNA2;
554                                 } else if (antcomb->rssi_sub >
555                                            antcomb->rssi_lna1) {
556                                         /* set to A-B */
557                                         div_ant_conf.main_lna_conf =
558                                                 ATH_ANT_DIV_COMB_LNA1;
559                                         div_ant_conf.alt_lna_conf =
560                                                 ATH_ANT_DIV_COMB_LNA1_MINUS_LNA2;
561                                 } else {
562                                         /* set to LNA2 */
563                                         div_ant_conf.main_lna_conf =
564                                                 ATH_ANT_DIV_COMB_LNA1;
565                                         div_ant_conf.alt_lna_conf =
566                                                 ATH_ANT_DIV_COMB_LNA2;
567                                 }
568                         }
569                         break;
570                 default:
571                         break;
572                 }
573         } else {
574                 if (!antcomb->alt_good) {
575                         antcomb->scan_not_start = AH_FALSE;
576                         /* Set alt to another LNA */
577                         if (curr_main_set == ATH_ANT_DIV_COMB_LNA2) {
578                                 div_ant_conf.main_lna_conf =
579                                                 ATH_ANT_DIV_COMB_LNA2;
580                                 div_ant_conf.alt_lna_conf =
581                                                 ATH_ANT_DIV_COMB_LNA1;
582                         } else if (curr_main_set == ATH_ANT_DIV_COMB_LNA1) {
583                                 div_ant_conf.main_lna_conf =
584                                                 ATH_ANT_DIV_COMB_LNA1;
585                                 div_ant_conf.alt_lna_conf =
586                                                 ATH_ANT_DIV_COMB_LNA2;
587                         }
588                         goto div_comb_done;
589                 }
590         }
591
592         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
593                                            main_rssi_avg, alt_rssi_avg,
594                                            alt_ratio);
595
596         antcomb->quick_scan_cnt++;
597
598 div_comb_done:
599         ath_ant_div_conf_fast_divbias(&div_ant_conf);
600
601         ar9285_antdiv_comb_conf_set(ah, &div_ant_conf);
602
603         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
604            __func__, antcomb->total_pkt_count);
605
606         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
607            __func__, antcomb->main_total_rssi);
608         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
609            __func__, antcomb->alt_total_rssi);
610
611         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
612            __func__, main_rssi_avg);
613         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
614            __func__, alt_rssi_avg);
615
616         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
617            __func__, antcomb->main_recv_cnt);
618         HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
619            __func__, antcomb->alt_recv_cnt);
620
621 //      if (curr_alt_set != div_ant_conf.alt_lna_conf)
622                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
623                     __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
624 //      if (curr_main_set != div_ant_conf.main_lna_conf)
625                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
626                     __func__, curr_main_set, div_ant_conf.main_lna_conf);
627 //      if (curr_bias != div_ant_conf.fast_div_bias)
628                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
629                     __func__, curr_bias, div_ant_conf.fast_div_bias);
630
631         antcomb->scan_start_time = ticks;
632         antcomb->total_pkt_count = 0;
633         antcomb->main_total_rssi = 0;
634         antcomb->alt_total_rssi = 0;
635         antcomb->main_recv_cnt = 0;
636         antcomb->alt_recv_cnt = 0;
637 }
638
639 /*
640  * Set the antenna switch to control RX antenna diversity.
641  *
642  * If a fixed configuration is used, the LNA and div bias
643  * settings are fixed and the antenna diversity scanning routine
644  * is disabled.
645  *
646  * If a variable configuration is used, a default is programmed
647  * in and sampling commences per RXed packet.
648  *
649  * Since this is called from ar9285SetBoardValues() to setup
650  * diversity, it means that after a reset or scan, any current
651  * software diversity combining settings will be lost and won't
652  * re-appear until after the first successful sample run.
653  * Please keep this in mind if you're seeing weird performance
654  * that happens to relate to scan/diversity timing.
655  */
656 HAL_BOOL
657 ar9285SetAntennaSwitch(struct ath_hal *ah, HAL_ANT_SETTING settings)
658 {
659         int regVal;
660         const HAL_EEPROM_v4k *ee = AH_PRIVATE(ah)->ah_eeprom;
661         const MODAL_EEP4K_HEADER *pModal = &ee->ee_base.modalHeader;
662         uint8_t ant_div_control1, ant_div_control2;
663
664         if (pModal->version < 3) {
665                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: not supported\n",
666             __func__);
667                 return AH_FALSE;        /* Can't do diversity */
668         }
669
670         /* Store settings */
671         AH5212(ah)->ah_antControl = settings;
672         AH5212(ah)->ah_diversity = (settings == HAL_ANT_VARIABLE);
673         
674         /* XXX don't fiddle if the PHY is in sleep mode or ! chan */
675
676         /* Begin setting the relevant registers */
677
678         ant_div_control1 = pModal->antdiv_ctl1;
679         ant_div_control2 = pModal->antdiv_ctl2;
680
681         regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
682         regVal &= (~(AR_PHY_9285_ANT_DIV_CTL_ALL));
683
684         /* enable antenna diversity only if diversityControl == HAL_ANT_VARIABLE */
685         if (settings == HAL_ANT_VARIABLE)
686             regVal |= SM(ant_div_control1, AR_PHY_9285_ANT_DIV_CTL);
687
688         if (settings == HAL_ANT_VARIABLE) {
689             HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_VARIABLE\n",
690               __func__);
691             regVal |= SM(ant_div_control2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
692             regVal |= SM((ant_div_control2 >> 2), AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
693             regVal |= SM((ant_div_control1 >> 1), AR_PHY_9285_ANT_DIV_ALT_GAINTB);
694             regVal |= SM((ant_div_control1 >> 2), AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
695         } else {
696             if (settings == HAL_ANT_FIXED_A) {
697                 /* Diversity disabled, RX = LNA1 */
698                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_A\n",
699                     __func__);
700                 regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
701                 regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
702                 regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
703                 regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
704             }
705             else if (settings == HAL_ANT_FIXED_B) {
706                 /* Diversity disabled, RX = LNA2 */
707                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY, "%s: HAL_ANT_FIXED_B\n",
708                     __func__);
709                 regVal |= SM(ATH_ANT_DIV_COMB_LNA1, AR_PHY_9285_ANT_DIV_ALT_LNACONF);
710                 regVal |= SM(ATH_ANT_DIV_COMB_LNA2, AR_PHY_9285_ANT_DIV_MAIN_LNACONF);
711                 regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_1, AR_PHY_9285_ANT_DIV_ALT_GAINTB);
712                 regVal |= SM(AR_PHY_9285_ANT_DIV_GAINTB_0, AR_PHY_9285_ANT_DIV_MAIN_GAINTB);
713             }
714         }
715
716         OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
717         regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
718         regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
719         regVal &= (~AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
720         if (settings == HAL_ANT_VARIABLE)
721             regVal |= SM((ant_div_control1 >> 3), AR_PHY_CCK_DETECT_BB_ENABLE_ANT_FAST_DIV);
722
723         OS_REG_WRITE(ah, AR_PHY_CCK_DETECT, regVal);
724         regVal = OS_REG_READ(ah, AR_PHY_CCK_DETECT);
725
726         /*
727          * If Diversity combining is available and the diversity setting
728          * is to allow variable diversity, enable it by default.
729          *
730          * This will be eventually overridden by the software antenna
731          * diversity logic.
732          *
733          * Note that yes, this following section overrides the above
734          * settings for the LNA configuration and fast-bias.
735          */
736         if (ar9285_check_div_comb(ah) && AH5212(ah)->ah_diversity == AH_TRUE) {
737                 // If support DivComb, set MAIN to LNA1 and ALT to LNA2 at the first beginning
738                 HALDEBUG(ah, HAL_DEBUG_DIVERSITY,
739                     "%s: Enable initial settings for combined diversity\n",
740                     __func__);
741                 regVal = OS_REG_READ(ah, AR_PHY_MULTICHAIN_GAIN_CTL);
742                 regVal &= (~(AR_PHY_9285_ANT_DIV_MAIN_LNACONF | AR_PHY_9285_ANT_DIV_ALT_LNACONF));
743                 regVal |= (ATH_ANT_DIV_COMB_LNA1 << AR_PHY_9285_ANT_DIV_MAIN_LNACONF_S);
744                 regVal |= (ATH_ANT_DIV_COMB_LNA2 << AR_PHY_9285_ANT_DIV_ALT_LNACONF_S);
745                 regVal &= (~(AR_PHY_9285_FAST_DIV_BIAS));
746                 regVal |= (0 << AR_PHY_9285_FAST_DIV_BIAS_S);
747                 OS_REG_WRITE(ah, AR_PHY_MULTICHAIN_GAIN_CTL, regVal);
748         }
749
750         return AH_TRUE;
751 }