]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - sys/dev/ath/if_ath_lna_div.c
- Copy stable/10@285827 to releng/10.2 in preparation for 10.2-RC1
[FreeBSD/releng/10.2.git] / sys / dev / ath / if_ath_lna_div.c
1 /*-
2  * Copyright (c) 2013 Adrian Chadd <adrian@FreeBSD.org>
3  * All rights reserved.
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  *    without modification.
11  * 2. Redistributions in binary form must reproduce at minimum a disclaimer
12  *    similar to the "NO WARRANTY" disclaimer below ("Disclaimer") and any
13  *    redistribution must be conditioned upon including a substantially
14  *    similar Disclaimer requirement for further binary redistribution.
15  *
16  * NO WARRANTY
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF NONINFRINGEMENT, MERCHANTIBILITY
20  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
21  * THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR SPECIAL, EXEMPLARY,
22  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
25  * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
27  * THE POSSIBILITY OF SUCH DAMAGES.
28  *
29  * $FreeBSD$
30  */
31 #include <sys/cdefs.h>
32 __FBSDID("$FreeBSD$");
33
34 /*
35  * This module handles LNA diversity for those chips which implement LNA
36  * mixing (AR9285/AR9485.)
37  */
38 #include "opt_ath.h"
39 #include "opt_inet.h"
40 #include "opt_wlan.h"
41
42 #include <sys/param.h>
43 #include <sys/systm.h> 
44 #include <sys/sysctl.h>
45 #include <sys/kernel.h>
46 #include <sys/lock.h>
47 #include <sys/mutex.h>
48 #include <sys/errno.h>
49
50 #include <machine/bus.h>
51 #include <machine/resource.h>
52 #include <sys/bus.h>
53
54 #include <sys/socket.h>
55  
56 #include <net/if.h>
57 #include <net/if_media.h>
58 #include <net/if_arp.h>
59 #include <net/ethernet.h>               /* XXX for ether_sprintf */
60
61 #include <net80211/ieee80211_var.h>
62
63 #include <net/bpf.h>
64
65 #ifdef INET
66 #include <netinet/in.h>
67 #include <netinet/if_ether.h>
68 #endif
69
70 #include <dev/ath/if_athvar.h>
71 #include <dev/ath/if_ath_debug.h>
72 #include <dev/ath/if_ath_lna_div.h>
73
74 /* Linux compability macros */
75 /*
76  * XXX these don't handle rounding, underflow, overflow, wrapping!
77  */
78 #define msecs_to_jiffies(a)             ( (a) * hz / 1000 )
79
80 /*
81  * Methods which are required
82  */
83
84 /*
85  * Attach the LNA diversity to the given interface
86  */
87 int
88 ath_lna_div_attach(struct ath_softc *sc)
89 {
90         struct if_ath_ant_comb_state *ss;
91         HAL_ANT_COMB_CONFIG div_ant_conf;
92
93         /* Only do this if diversity is enabled */
94         if (! ath_hal_hasdivantcomb(sc->sc_ah))
95                 return (0);
96
97         ss = malloc(sizeof(struct if_ath_ant_comb_state),
98             M_TEMP, M_WAITOK | M_ZERO);
99         if (ss == NULL) {
100                 device_printf(sc->sc_dev, "%s: failed to allocate\n",
101                     __func__);
102                 /* Don't fail at this point */
103                 return (0);
104         }
105
106         /* Fetch the hardware configuration */
107         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
108         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
109
110         /* Figure out what the hardware specific bits should be */
111         if ((div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_1) ||
112             (div_ant_conf.antdiv_configgroup == HAL_ANTDIV_CONFIG_GROUP_2)) {
113                 ss->lna1_lna2_delta = -9;
114         } else {
115                 ss->lna1_lna2_delta = -3;
116         }
117
118         /* Let's flip this on */
119         sc->sc_lna_div = ss;
120         sc->sc_dolnadiv = 1;
121
122         return (0);
123 }
124
125 /*
126  * Detach the LNA diversity state from the given interface
127  */
128 int
129 ath_lna_div_detach(struct ath_softc *sc)
130 {
131         if (sc->sc_lna_div != NULL) {
132                 free(sc->sc_lna_div, M_TEMP);
133                 sc->sc_lna_div = NULL;
134         }
135         sc->sc_dolnadiv = 0;
136         return (0);
137 }
138
139 /*
140  * Enable LNA diversity on the current channel if it's required.
141  */
142 int
143 ath_lna_div_enable(struct ath_softc *sc, const struct ieee80211_channel *chan)
144 {
145
146         return (0);
147 }
148
149 /*
150  * Handle ioctl requests from the diagnostic interface.
151  *
152  * The initial part of this code resembles ath_ioctl_diag();
153  * it's likely a good idea to reduce duplication between
154  * these two routines.
155  */
156 int
157 ath_lna_div_ioctl(struct ath_softc *sc, struct ath_diag *ad)
158 {
159         unsigned int id = ad->ad_id & ATH_DIAG_ID;
160         void *indata = NULL;
161         void *outdata = NULL;
162         u_int32_t insize = ad->ad_in_size;
163         u_int32_t outsize = ad->ad_out_size;
164         int error = 0;
165 //      int val;
166
167         if (ad->ad_id & ATH_DIAG_IN) {
168                 /*
169                  * Copy in data.
170                  */
171                 indata = malloc(insize, M_TEMP, M_NOWAIT);
172                 if (indata == NULL) {
173                         error = ENOMEM;
174                         goto bad;
175                 }
176                 error = copyin(ad->ad_in_data, indata, insize);
177                 if (error)
178                         goto bad;
179         }
180         if (ad->ad_id & ATH_DIAG_DYN) {
181                 /*
182                  * Allocate a buffer for the results (otherwise the HAL
183                  * returns a pointer to a buffer where we can read the
184                  * results).  Note that we depend on the HAL leaving this
185                  * pointer for us to use below in reclaiming the buffer;
186                  * may want to be more defensive.
187                  */
188                 outdata = malloc(outsize, M_TEMP, M_NOWAIT);
189                 if (outdata == NULL) {
190                         error = ENOMEM;
191                         goto bad;
192                 }
193         }
194         switch (id) {
195                 default:
196                         error = EINVAL;
197         }
198         if (outsize < ad->ad_out_size)
199                 ad->ad_out_size = outsize;
200         if (outdata && copyout(outdata, ad->ad_out_data, ad->ad_out_size))
201                 error = EFAULT;
202 bad:
203         if ((ad->ad_id & ATH_DIAG_IN) && indata != NULL)
204                 free(indata, M_TEMP);
205         if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
206                 free(outdata, M_TEMP);
207         return (error);
208 }
209
210 static HAL_BOOL
211 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
212     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
213 {
214         return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
215                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
216                 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
217 }
218
219 static void
220 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
221     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
222 {
223         antcomb->quick_scan_cnt = 0;
224
225         if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
226                 antcomb->rssi_lna2 = main_rssi_avg;
227         else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
228                 antcomb->rssi_lna1 = main_rssi_avg;
229
230         switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
231         case (0x10): /* LNA2 A-B */
232                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
233                 antcomb->first_quick_scan_conf =
234                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
235                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
236                 break;
237         case (0x20): /* LNA1 A-B */
238                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
239                 antcomb->first_quick_scan_conf =
240                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
241                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
242                 break;
243         case (0x21): /* LNA1 LNA2 */
244                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
245                 antcomb->first_quick_scan_conf =
246                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
247                 antcomb->second_quick_scan_conf =
248                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
249                 break;
250         case (0x12): /* LNA2 LNA1 */
251                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
252                 antcomb->first_quick_scan_conf =
253                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
254                 antcomb->second_quick_scan_conf =
255                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
256                 break;
257         case (0x13): /* LNA2 A+B */
258                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
259                 antcomb->first_quick_scan_conf =
260                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
261                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
262                 break;
263         case (0x23): /* LNA1 A+B */
264                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
265                 antcomb->first_quick_scan_conf =
266                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
267                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
268                 break;
269         default:
270                 break;
271         }
272 }
273
274 static void
275 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
276     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
277     int alt_rssi_avg, int alt_ratio)
278 {
279         /* alt_good */
280         switch (antcomb->quick_scan_cnt) {
281         case 0:
282                 /* set alt to main, and alt to first conf */
283                 div_ant_conf->main_lna_conf = antcomb->main_conf;
284                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
285                 break;
286         case 1:
287                 /* set alt to main, and alt to first conf */
288                 div_ant_conf->main_lna_conf = antcomb->main_conf;
289                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
290                 antcomb->rssi_first = main_rssi_avg;
291                 antcomb->rssi_second = alt_rssi_avg;
292
293                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
294                         /* main is LNA1 */
295                         if (ath_is_alt_ant_ratio_better(alt_ratio,
296                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
297                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
298                                                 main_rssi_avg, alt_rssi_avg,
299                                                 antcomb->total_pkt_count))
300                                 antcomb->first_ratio = AH_TRUE;
301                         else
302                                 antcomb->first_ratio = AH_FALSE;
303                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
304                         if (ath_is_alt_ant_ratio_better(alt_ratio,
305                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
306                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
307                                                 main_rssi_avg, alt_rssi_avg,
308                                                 antcomb->total_pkt_count))
309                                 antcomb->first_ratio = AH_TRUE;
310                         else
311                                 antcomb->first_ratio = AH_FALSE;
312                 } else {
313                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
314                             (alt_rssi_avg > main_rssi_avg +
315                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
316                             (alt_rssi_avg > main_rssi_avg)) &&
317                             (antcomb->total_pkt_count > 50))
318                                 antcomb->first_ratio = AH_TRUE;
319                         else
320                                 antcomb->first_ratio = AH_FALSE;
321                 }
322                 break;
323         case 2:
324                 antcomb->alt_good = AH_FALSE;
325                 antcomb->scan_not_start = AH_FALSE;
326                 antcomb->scan = AH_FALSE;
327                 antcomb->rssi_first = main_rssi_avg;
328                 antcomb->rssi_third = alt_rssi_avg;
329
330                 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
331                         antcomb->rssi_lna1 = alt_rssi_avg;
332                 else if (antcomb->second_quick_scan_conf ==
333                          HAL_ANT_DIV_COMB_LNA2)
334                         antcomb->rssi_lna2 = alt_rssi_avg;
335                 else if (antcomb->second_quick_scan_conf ==
336                          HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
337                         if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
338                                 antcomb->rssi_lna2 = main_rssi_avg;
339                         else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
340                                 antcomb->rssi_lna1 = main_rssi_avg;
341                 }
342
343                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
344                     ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
345                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
346                 else
347                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
348
349                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
350                         if (ath_is_alt_ant_ratio_better(alt_ratio,
351                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
352                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
353                                                 main_rssi_avg, alt_rssi_avg,
354                                                 antcomb->total_pkt_count))
355                                 antcomb->second_ratio = AH_TRUE;
356                         else
357                                 antcomb->second_ratio = AH_FALSE;
358                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
359                         if (ath_is_alt_ant_ratio_better(alt_ratio,
360                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
361                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
362                                                 main_rssi_avg, alt_rssi_avg,
363                                                 antcomb->total_pkt_count))
364                                 antcomb->second_ratio = AH_TRUE;
365                         else
366                                 antcomb->second_ratio = AH_FALSE;
367                 } else {
368                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
369                             (alt_rssi_avg > main_rssi_avg +
370                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
371                             (alt_rssi_avg > main_rssi_avg)) &&
372                             (antcomb->total_pkt_count > 50))
373                                 antcomb->second_ratio = AH_TRUE;
374                         else
375                                 antcomb->second_ratio = AH_FALSE;
376                 }
377
378                 /* set alt to the conf with maximun ratio */
379                 if (antcomb->first_ratio && antcomb->second_ratio) {
380                         if (antcomb->rssi_second > antcomb->rssi_third) {
381                                 /* first alt*/
382                                 if ((antcomb->first_quick_scan_conf ==
383                                     HAL_ANT_DIV_COMB_LNA1) ||
384                                     (antcomb->first_quick_scan_conf ==
385                                     HAL_ANT_DIV_COMB_LNA2))
386                                         /* Set alt LNA1 or LNA2*/
387                                         if (div_ant_conf->main_lna_conf ==
388                                             HAL_ANT_DIV_COMB_LNA2)
389                                                 div_ant_conf->alt_lna_conf =
390                                                         HAL_ANT_DIV_COMB_LNA1;
391                                         else
392                                                 div_ant_conf->alt_lna_conf =
393                                                         HAL_ANT_DIV_COMB_LNA2;
394                                 else
395                                         /* Set alt to A+B or A-B */
396                                         div_ant_conf->alt_lna_conf =
397                                                 antcomb->first_quick_scan_conf;
398                         } else if ((antcomb->second_quick_scan_conf ==
399                                    HAL_ANT_DIV_COMB_LNA1) ||
400                                    (antcomb->second_quick_scan_conf ==
401                                    HAL_ANT_DIV_COMB_LNA2)) {
402                                 /* Set alt LNA1 or LNA2 */
403                                 if (div_ant_conf->main_lna_conf ==
404                                     HAL_ANT_DIV_COMB_LNA2)
405                                         div_ant_conf->alt_lna_conf =
406                                                 HAL_ANT_DIV_COMB_LNA1;
407                                 else
408                                         div_ant_conf->alt_lna_conf =
409                                                 HAL_ANT_DIV_COMB_LNA2;
410                         } else {
411                                 /* Set alt to A+B or A-B */
412                                 div_ant_conf->alt_lna_conf =
413                                         antcomb->second_quick_scan_conf;
414                         }
415                 } else if (antcomb->first_ratio) {
416                         /* first alt */
417                         if ((antcomb->first_quick_scan_conf ==
418                             HAL_ANT_DIV_COMB_LNA1) ||
419                             (antcomb->first_quick_scan_conf ==
420                             HAL_ANT_DIV_COMB_LNA2))
421                                         /* Set alt LNA1 or LNA2 */
422                                 if (div_ant_conf->main_lna_conf ==
423                                     HAL_ANT_DIV_COMB_LNA2)
424                                         div_ant_conf->alt_lna_conf =
425                                                         HAL_ANT_DIV_COMB_LNA1;
426                                 else
427                                         div_ant_conf->alt_lna_conf =
428                                                         HAL_ANT_DIV_COMB_LNA2;
429                         else
430                                 /* Set alt to A+B or A-B */
431                                 div_ant_conf->alt_lna_conf =
432                                                 antcomb->first_quick_scan_conf;
433                 } else if (antcomb->second_ratio) {
434                                 /* second alt */
435                         if ((antcomb->second_quick_scan_conf ==
436                             HAL_ANT_DIV_COMB_LNA1) ||
437                             (antcomb->second_quick_scan_conf ==
438                             HAL_ANT_DIV_COMB_LNA2))
439                                 /* Set alt LNA1 or LNA2 */
440                                 if (div_ant_conf->main_lna_conf ==
441                                     HAL_ANT_DIV_COMB_LNA2)
442                                         div_ant_conf->alt_lna_conf =
443                                                 HAL_ANT_DIV_COMB_LNA1;
444                                 else
445                                         div_ant_conf->alt_lna_conf =
446                                                 HAL_ANT_DIV_COMB_LNA2;
447                         else
448                                 /* Set alt to A+B or A-B */
449                                 div_ant_conf->alt_lna_conf =
450                                                 antcomb->second_quick_scan_conf;
451                 } else {
452                         /* main is largest */
453                         if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
454                             (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
455                                 /* Set alt LNA1 or LNA2 */
456                                 if (div_ant_conf->main_lna_conf ==
457                                     HAL_ANT_DIV_COMB_LNA2)
458                                         div_ant_conf->alt_lna_conf =
459                                                         HAL_ANT_DIV_COMB_LNA1;
460                                 else
461                                         div_ant_conf->alt_lna_conf =
462                                                         HAL_ANT_DIV_COMB_LNA2;
463                         else
464                                 /* Set alt to A+B or A-B */
465                                 div_ant_conf->alt_lna_conf = antcomb->main_conf;
466                 }
467                 break;
468         default:
469                 break;
470         }
471 }
472
473 static void
474 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
475     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
476     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
477 {
478
479         if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
480                 switch ((pdiv_ant_conf->main_lna_conf << 4)
481                     | pdiv_ant_conf->alt_lna_conf) {
482                 case (0x01): //A-B LNA2
483                         pdiv_ant_conf->fast_div_bias = 0x1;
484                         pdiv_ant_conf->main_gaintb   = 0;
485                         pdiv_ant_conf->alt_gaintb    = 0;
486                         break;
487                 case (0x02): //A-B LNA1
488                         pdiv_ant_conf->fast_div_bias = 0x1;
489                         pdiv_ant_conf->main_gaintb   = 0;
490                         pdiv_ant_conf->alt_gaintb    = 0;
491                         break;
492                 case (0x03): //A-B A+B
493                         pdiv_ant_conf->fast_div_bias = 0x1;
494                         pdiv_ant_conf->main_gaintb   = 0;
495                         pdiv_ant_conf->alt_gaintb    = 0;
496                         break;
497                 case (0x10): //LNA2 A-B
498                         if ((antcomb->scan == 0)
499                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
500                                 pdiv_ant_conf->fast_div_bias = 0x3f;
501                         } else {
502                                 pdiv_ant_conf->fast_div_bias = 0x1;
503                         }
504                         pdiv_ant_conf->main_gaintb   = 0;
505                         pdiv_ant_conf->alt_gaintb    = 0;
506                         break;
507                 case (0x12): //LNA2 LNA1
508                         pdiv_ant_conf->fast_div_bias = 0x1;
509                         pdiv_ant_conf->main_gaintb   = 0;
510                         pdiv_ant_conf->alt_gaintb    = 0;
511                         break;
512                         case (0x13): //LNA2 A+B
513                         if ((antcomb->scan == 0)
514                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
515                                 pdiv_ant_conf->fast_div_bias = 0x3f;
516                         } else {
517                                 pdiv_ant_conf->fast_div_bias = 0x1;
518                         }
519                         pdiv_ant_conf->main_gaintb   = 0;
520                         pdiv_ant_conf->alt_gaintb    = 0;
521                         break;
522                 case (0x20): //LNA1 A-B
523                         if ((antcomb->scan == 0)
524                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
525                                 pdiv_ant_conf->fast_div_bias = 0x3f;
526                         } else {
527                                 pdiv_ant_conf->fast_div_bias = 0x1;
528                         }
529                         pdiv_ant_conf->main_gaintb   = 0;
530                         pdiv_ant_conf->alt_gaintb    = 0;
531                         break;
532                 case (0x21): //LNA1 LNA2
533                         pdiv_ant_conf->fast_div_bias = 0x1;
534                         pdiv_ant_conf->main_gaintb   = 0;
535                         pdiv_ant_conf->alt_gaintb    = 0;
536                         break;
537                 case (0x23): //LNA1 A+B
538                         if ((antcomb->scan == 0)
539                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
540                                 pdiv_ant_conf->fast_div_bias = 0x3f;
541                         } else {
542                                 pdiv_ant_conf->fast_div_bias = 0x1;
543                         }
544                         pdiv_ant_conf->main_gaintb   = 0;
545                         pdiv_ant_conf->alt_gaintb    = 0;
546                         break;
547                 case (0x30): //A+B A-B
548                         pdiv_ant_conf->fast_div_bias = 0x1;
549                         pdiv_ant_conf->main_gaintb   = 0;
550                         pdiv_ant_conf->alt_gaintb    = 0;
551                         break;
552                 case (0x31): //A+B LNA2
553                         pdiv_ant_conf->fast_div_bias = 0x1;
554                         pdiv_ant_conf->main_gaintb   = 0;
555                         pdiv_ant_conf->alt_gaintb    = 0;
556                         break;
557                 case (0x32): //A+B LNA1
558                         pdiv_ant_conf->fast_div_bias = 0x1;
559                         pdiv_ant_conf->main_gaintb   = 0;
560                         pdiv_ant_conf->alt_gaintb    = 0;
561                         break;
562                 default:
563                         break;
564                 }
565         } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
566                 switch ((pdiv_ant_conf->main_lna_conf << 4)
567                     | pdiv_ant_conf->alt_lna_conf) {
568                 case (0x01): //A-B LNA2
569                         pdiv_ant_conf->fast_div_bias = 0x1;
570                         pdiv_ant_conf->main_gaintb   = 0;
571                         pdiv_ant_conf->alt_gaintb    = 0;
572                         break;
573                 case (0x02): //A-B LNA1
574                         pdiv_ant_conf->fast_div_bias = 0x1;
575                         pdiv_ant_conf->main_gaintb   = 0;
576                         pdiv_ant_conf->alt_gaintb    = 0;
577                         break;
578                 case (0x03): //A-B A+B
579                         pdiv_ant_conf->fast_div_bias = 0x1;
580                         pdiv_ant_conf->main_gaintb   = 0;
581                         pdiv_ant_conf->alt_gaintb    = 0;
582                         break;
583                 case (0x10): //LNA2 A-B
584                         if ((antcomb->scan == 0)
585                             && (alt_ratio > alt_ant_ratio_th)) {
586                                 pdiv_ant_conf->fast_div_bias = 0x1;
587                         } else {
588                                 pdiv_ant_conf->fast_div_bias = 0x2;
589                         }
590                         pdiv_ant_conf->main_gaintb   = 0;
591                         pdiv_ant_conf->alt_gaintb    = 0;
592                         break;
593                 case (0x12): //LNA2 LNA1
594                         pdiv_ant_conf->fast_div_bias = 0x1;
595                         pdiv_ant_conf->main_gaintb   = 0;
596                         pdiv_ant_conf->alt_gaintb    = 0;
597                         break;
598                 case (0x13): //LNA2 A+B
599                         if ((antcomb->scan == 0)
600                             && (alt_ratio > alt_ant_ratio_th)) {
601                                 pdiv_ant_conf->fast_div_bias = 0x1;
602                         } else {
603                                 pdiv_ant_conf->fast_div_bias = 0x2;
604                         }
605                         pdiv_ant_conf->main_gaintb   = 0;
606                         pdiv_ant_conf->alt_gaintb    = 0;
607                         break;
608                 case (0x20): //LNA1 A-B
609                         if ((antcomb->scan == 0)
610                             && (alt_ratio > alt_ant_ratio_th)) {
611                                 pdiv_ant_conf->fast_div_bias = 0x1;
612                         } else {
613                                 pdiv_ant_conf->fast_div_bias = 0x2;
614                         }
615                         pdiv_ant_conf->main_gaintb   = 0;
616                         pdiv_ant_conf->alt_gaintb    = 0;
617                         break;
618                 case (0x21): //LNA1 LNA2
619                         pdiv_ant_conf->fast_div_bias = 0x1;
620                         pdiv_ant_conf->main_gaintb   = 0;
621                         pdiv_ant_conf->alt_gaintb    = 0;
622                         break;
623                 case (0x23): //LNA1 A+B
624                         if ((antcomb->scan == 0)
625                             && (alt_ratio > alt_ant_ratio_th)) {
626                                 pdiv_ant_conf->fast_div_bias = 0x1;
627                         } else {
628                                 pdiv_ant_conf->fast_div_bias = 0x2;
629                         }
630                         pdiv_ant_conf->main_gaintb   = 0;
631                         pdiv_ant_conf->alt_gaintb    = 0;
632                         break;
633                 case (0x30): //A+B A-B
634                         pdiv_ant_conf->fast_div_bias = 0x1;
635                         pdiv_ant_conf->main_gaintb   = 0;
636                         pdiv_ant_conf->alt_gaintb    = 0;
637                         break;
638                 case (0x31): //A+B LNA2
639                         pdiv_ant_conf->fast_div_bias = 0x1;
640                         pdiv_ant_conf->main_gaintb   = 0;
641                         pdiv_ant_conf->alt_gaintb    = 0;
642                         break;
643                 case (0x32): //A+B LNA1
644                         pdiv_ant_conf->fast_div_bias = 0x1;
645                         pdiv_ant_conf->main_gaintb   = 0;
646                         pdiv_ant_conf->alt_gaintb    = 0;
647                         break;
648                 default:
649                         break;
650                 }
651         } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
652                 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
653                 case (0x01): //A-B LNA2
654                         pdiv_ant_conf->fast_div_bias = 0x3b;
655                         break;
656                 case (0x02): //A-B LNA1
657                         pdiv_ant_conf->fast_div_bias = 0x3d;
658                         break;
659                 case (0x03): //A-B A+B
660                         pdiv_ant_conf->fast_div_bias = 0x1;
661                         break;
662                 case (0x10): //LNA2 A-B
663                         pdiv_ant_conf->fast_div_bias = 0x7;
664                         break;
665                 case (0x12): //LNA2 LNA1
666                         pdiv_ant_conf->fast_div_bias = 0x2;
667                         break;
668                 case (0x13): //LNA2 A+B
669                         pdiv_ant_conf->fast_div_bias = 0x7;
670                         break;
671                 case (0x20): //LNA1 A-B
672                         pdiv_ant_conf->fast_div_bias = 0x6;
673                         break;
674                 case (0x21): //LNA1 LNA2
675                         pdiv_ant_conf->fast_div_bias = 0x0;
676                         break;
677                 case (0x23): //LNA1 A+B
678                         pdiv_ant_conf->fast_div_bias = 0x6;
679                         break;
680                 case (0x30): //A+B A-B
681                         pdiv_ant_conf->fast_div_bias = 0x1;
682                         break;
683                 case (0x31): //A+B LNA2
684                         pdiv_ant_conf->fast_div_bias = 0x3b;
685                         break;
686                 case (0x32): //A+B LNA1
687                         pdiv_ant_conf->fast_div_bias = 0x3d;
688                         break;
689                 default:
690                         break;
691                 }
692         }
693 }
694
695 /*
696  * AR9485/AR933x TODO:
697  * + Select a ratio based on whether RSSI is low or not; but I need
698  *   to figure out what "low_rssi_th" is sourced from.
699  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
700  * + .. and there's likely a bunch of other things to include in this.
701  */
702
703 /* Antenna diversity and combining */
704 void
705 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
706     unsigned long ticks, int hz)
707 {
708         HAL_ANT_COMB_CONFIG div_ant_conf;
709         struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
710         int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
711         int curr_main_set, curr_bias;
712         int main_rssi = rs->rs_rssi_ctl[0];
713         int alt_rssi = rs->rs_rssi_ctl[1];
714         int rx_ant_conf, main_ant_conf, alt_ant_conf;
715         HAL_BOOL short_scan = AH_FALSE;
716
717         rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
718         main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
719         alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
720
721 #if 0
722         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
723             "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
724             "FastDiv: %d\n",
725             __func__,
726             main_rssi,
727             alt_rssi,
728             main_ant_conf,
729             alt_ant_conf,
730             rx_ant_conf,
731             !!(rs->rs_rssi_ctl[2] & 0x80),
732             !!(rs->rs_rssi_ctl[2] & 0x40),
733             !!(rs->rs_rssi_ext[2] & 0x40));
734 #endif
735
736         /*
737          * If LNA diversity combining isn't enabled, don't run this.
738          */
739         if (! sc->sc_dolnadiv)
740                 return;
741
742         /*
743          * XXX this is ugly, but the HAL code attaches the
744          * LNA diversity to the TX antenna settings.
745          * I don't know why.
746          */
747         if (sc->sc_txantenna != HAL_ANT_VARIABLE)
748                 return;
749
750         /* Record packet only when alt_rssi is positive */
751         if (main_rssi > 0 && alt_rssi > 0) {
752                 antcomb->total_pkt_count++;
753                 antcomb->main_total_rssi += main_rssi;
754                 antcomb->alt_total_rssi  += alt_rssi;
755                 if (main_ant_conf == rx_ant_conf)
756                         antcomb->main_recv_cnt++;
757                 else
758                         antcomb->alt_recv_cnt++;
759         }
760
761         /* Short scan check */
762         if (antcomb->scan && antcomb->alt_good) {
763                 if (time_after(ticks, antcomb->scan_start_time +
764                     msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
765                         short_scan = AH_TRUE;
766                 else
767                         if (antcomb->total_pkt_count ==
768                             ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
769                                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
770                                             antcomb->total_pkt_count);
771                                 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
772                                         short_scan = AH_TRUE;
773                         }
774         }
775
776 #if 0
777         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
778             "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
779             __func__,
780             antcomb->total_pkt_count,
781             !! (rs->rs_moreaggr),
782             !! (short_scan));
783 #endif
784
785         if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
786             rs->rs_moreaggr) && !short_scan)
787                 return;
788
789         if (antcomb->total_pkt_count) {
790                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
791                              antcomb->total_pkt_count);
792                 main_rssi_avg = (antcomb->main_total_rssi /
793                                  antcomb->total_pkt_count);
794                 alt_rssi_avg = (antcomb->alt_total_rssi /
795                                  antcomb->total_pkt_count);
796         }
797
798         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
799
800         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
801         curr_alt_set = div_ant_conf.alt_lna_conf;
802         curr_main_set = div_ant_conf.main_lna_conf;
803         curr_bias = div_ant_conf.fast_div_bias;
804
805         antcomb->count++;
806
807         if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
808                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
809                         ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
810                                                   main_rssi_avg);
811                         antcomb->alt_good = AH_TRUE;
812                 } else {
813                         antcomb->alt_good = AH_FALSE;
814                 }
815
816                 antcomb->count = 0;
817                 antcomb->scan = AH_TRUE;
818                 antcomb->scan_not_start = AH_TRUE;
819         }
820
821         if (!antcomb->scan) {
822                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
823                         if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
824                                 /* Switch main and alt LNA */
825                                 div_ant_conf.main_lna_conf =
826                                                 HAL_ANT_DIV_COMB_LNA2;
827                                 div_ant_conf.alt_lna_conf  =
828                                                 HAL_ANT_DIV_COMB_LNA1;
829                         } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
830                                 div_ant_conf.main_lna_conf =
831                                                 HAL_ANT_DIV_COMB_LNA1;
832                                 div_ant_conf.alt_lna_conf  =
833                                                 HAL_ANT_DIV_COMB_LNA2;
834                         }
835
836                         goto div_comb_done;
837                 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
838                            (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
839                         /* Set alt to another LNA */
840                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
841                                 div_ant_conf.alt_lna_conf =
842                                                 HAL_ANT_DIV_COMB_LNA1;
843                         else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
844                                 div_ant_conf.alt_lna_conf =
845                                                 HAL_ANT_DIV_COMB_LNA2;
846
847                         goto div_comb_done;
848                 }
849
850                 if ((alt_rssi_avg < (main_rssi_avg +
851                     antcomb->lna1_lna2_delta)))
852                         goto div_comb_done;
853         }
854
855         if (!antcomb->scan_not_start) {
856                 switch (curr_alt_set) {
857                 case HAL_ANT_DIV_COMB_LNA2:
858                         antcomb->rssi_lna2 = alt_rssi_avg;
859                         antcomb->rssi_lna1 = main_rssi_avg;
860                         antcomb->scan = AH_TRUE;
861                         /* set to A+B */
862                         div_ant_conf.main_lna_conf =
863                                 HAL_ANT_DIV_COMB_LNA1;
864                         div_ant_conf.alt_lna_conf  =
865                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
866                         break;
867                 case HAL_ANT_DIV_COMB_LNA1:
868                         antcomb->rssi_lna1 = alt_rssi_avg;
869                         antcomb->rssi_lna2 = main_rssi_avg;
870                         antcomb->scan = AH_TRUE;
871                         /* set to A+B */
872                         div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
873                         div_ant_conf.alt_lna_conf  =
874                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
875                         break;
876                 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
877                         antcomb->rssi_add = alt_rssi_avg;
878                         antcomb->scan = AH_TRUE;
879                         /* set to A-B */
880                         div_ant_conf.alt_lna_conf =
881                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
882                         break;
883                 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
884                         antcomb->rssi_sub = alt_rssi_avg;
885                         antcomb->scan = AH_FALSE;
886                         if (antcomb->rssi_lna2 >
887                             (antcomb->rssi_lna1 +
888                             ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
889                                 /* use LNA2 as main LNA */
890                                 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
891                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
892                                         /* set to A+B */
893                                         div_ant_conf.main_lna_conf =
894                                                 HAL_ANT_DIV_COMB_LNA2;
895                                         div_ant_conf.alt_lna_conf  =
896                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
897                                 } else if (antcomb->rssi_sub >
898                                            antcomb->rssi_lna1) {
899                                         /* set to A-B */
900                                         div_ant_conf.main_lna_conf =
901                                                 HAL_ANT_DIV_COMB_LNA2;
902                                         div_ant_conf.alt_lna_conf =
903                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
904                                 } else {
905                                         /* set to LNA1 */
906                                         div_ant_conf.main_lna_conf =
907                                                 HAL_ANT_DIV_COMB_LNA2;
908                                         div_ant_conf.alt_lna_conf =
909                                                 HAL_ANT_DIV_COMB_LNA1;
910                                 }
911                         } else {
912                                 /* use LNA1 as main LNA */
913                                 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
914                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
915                                         /* set to A+B */
916                                         div_ant_conf.main_lna_conf =
917                                                 HAL_ANT_DIV_COMB_LNA1;
918                                         div_ant_conf.alt_lna_conf  =
919                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
920                                 } else if (antcomb->rssi_sub >
921                                            antcomb->rssi_lna1) {
922                                         /* set to A-B */
923                                         div_ant_conf.main_lna_conf =
924                                                 HAL_ANT_DIV_COMB_LNA1;
925                                         div_ant_conf.alt_lna_conf =
926                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
927                                 } else {
928                                         /* set to LNA2 */
929                                         div_ant_conf.main_lna_conf =
930                                                 HAL_ANT_DIV_COMB_LNA1;
931                                         div_ant_conf.alt_lna_conf =
932                                                 HAL_ANT_DIV_COMB_LNA2;
933                                 }
934                         }
935                         break;
936                 default:
937                         break;
938                 }
939         } else {
940                 if (!antcomb->alt_good) {
941                         antcomb->scan_not_start = AH_FALSE;
942                         /* Set alt to another LNA */
943                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
944                                 div_ant_conf.main_lna_conf =
945                                                 HAL_ANT_DIV_COMB_LNA2;
946                                 div_ant_conf.alt_lna_conf =
947                                                 HAL_ANT_DIV_COMB_LNA1;
948                         } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
949                                 div_ant_conf.main_lna_conf =
950                                                 HAL_ANT_DIV_COMB_LNA1;
951                                 div_ant_conf.alt_lna_conf =
952                                                 HAL_ANT_DIV_COMB_LNA2;
953                         }
954                         goto div_comb_done;
955                 }
956         }
957
958         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
959                                            main_rssi_avg, alt_rssi_avg,
960                                            alt_ratio);
961
962         antcomb->quick_scan_cnt++;
963
964 div_comb_done:
965 #if 0
966         ath_ant_div_conf_fast_divbias(&div_ant_conf);
967 #endif
968
969         ath_ant_adjust_fast_divbias(antcomb,
970             alt_ratio,
971             ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
972             div_ant_conf.antdiv_configgroup,
973             &div_ant_conf);
974
975         ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
976
977         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
978            __func__, antcomb->total_pkt_count);
979
980         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
981            __func__, antcomb->main_total_rssi);
982         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
983            __func__, antcomb->alt_total_rssi);
984
985         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
986            __func__, main_rssi_avg);
987         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
988            __func__, alt_rssi_avg);
989
990         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
991            __func__, antcomb->main_recv_cnt);
992         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
993            __func__, antcomb->alt_recv_cnt);
994
995 //      if (curr_alt_set != div_ant_conf.alt_lna_conf)
996                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
997                     __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
998 //      if (curr_main_set != div_ant_conf.main_lna_conf)
999                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1000                     __func__, curr_main_set, div_ant_conf.main_lna_conf);
1001 //      if (curr_bias != div_ant_conf.fast_div_bias)
1002                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1003                     __func__, curr_bias, div_ant_conf.fast_div_bias);
1004
1005         antcomb->scan_start_time = ticks;
1006         antcomb->total_pkt_count = 0;
1007         antcomb->main_total_rssi = 0;
1008         antcomb->alt_total_rssi = 0;
1009         antcomb->main_recv_cnt = 0;
1010         antcomb->alt_recv_cnt = 0;
1011 }
1012