kernel/ath - Synchronize with FreeBSD
[dragonfly.git] / sys / dev / netif / ath / 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/malloc.h>
48 #include <sys/mutex.h>
49 #include <sys/errno.h>
50
51 #include <sys/bus.h>
52
53 #include <sys/socket.h>
54  
55 #include <net/if.h>
56 #include <net/if_var.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 <netproto/802_11/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/netif/ath/ath/if_athvar.h>
71 #include <dev/netif/ath/ath/if_ath_debug.h>
72 #include <dev/netif/ath/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 = kmalloc(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                 kfree(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 = kmalloc(insize, M_TEMP, M_INTWAIT);
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 = kmalloc(outsize, M_TEMP, M_INTWAIT);
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                 kfree(indata, M_TEMP);
205         if ((ad->ad_id & ATH_DIAG_DYN) && outdata != NULL)
206                 kfree(outdata, M_TEMP);
207         return (error);
208 }
209
210 /*
211  * XXX need to low_rssi_thresh config from ath9k, to support CUS198
212  * antenna diversity correctly.
213  */
214 static HAL_BOOL
215 ath_is_alt_ant_ratio_better(int alt_ratio, int maxdelta, int mindelta,
216     int main_rssi_avg, int alt_rssi_avg, int pkt_count)
217 {
218         return (((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
219                 (alt_rssi_avg > main_rssi_avg + maxdelta)) ||
220                 (alt_rssi_avg > main_rssi_avg + mindelta)) && (pkt_count > 50);
221 }
222
223 static void
224 ath_lnaconf_alt_good_scan(struct if_ath_ant_comb_state *antcomb,
225     HAL_ANT_COMB_CONFIG *ant_conf, int main_rssi_avg)
226 {
227         antcomb->quick_scan_cnt = 0;
228
229         if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA2)
230                 antcomb->rssi_lna2 = main_rssi_avg;
231         else if (ant_conf->main_lna_conf == HAL_ANT_DIV_COMB_LNA1)
232                 antcomb->rssi_lna1 = main_rssi_avg;
233
234         switch ((ant_conf->main_lna_conf << 4) | ant_conf->alt_lna_conf) {
235         case (0x10): /* LNA2 A-B */
236                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
237                 antcomb->first_quick_scan_conf =
238                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
239                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
240                 break;
241         case (0x20): /* LNA1 A-B */
242                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
243                 antcomb->first_quick_scan_conf =
244                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
245                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
246                 break;
247         case (0x21): /* LNA1 LNA2 */
248                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA2;
249                 antcomb->first_quick_scan_conf =
250                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
251                 antcomb->second_quick_scan_conf =
252                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
253                 break;
254         case (0x12): /* LNA2 LNA1 */
255                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1;
256                 antcomb->first_quick_scan_conf =
257                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
258                 antcomb->second_quick_scan_conf =
259                         HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
260                 break;
261         case (0x13): /* LNA2 A+B */
262                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
263                 antcomb->first_quick_scan_conf =
264                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
265                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA1;
266                 break;
267         case (0x23): /* LNA1 A+B */
268                 antcomb->main_conf = HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
269                 antcomb->first_quick_scan_conf =
270                         HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
271                 antcomb->second_quick_scan_conf = HAL_ANT_DIV_COMB_LNA2;
272                 break;
273         default:
274                 break;
275         }
276 }
277
278 static void
279 ath_select_ant_div_from_quick_scan(struct if_ath_ant_comb_state *antcomb,
280     HAL_ANT_COMB_CONFIG *div_ant_conf, int main_rssi_avg,
281     int alt_rssi_avg, int alt_ratio)
282 {
283         /* alt_good */
284         switch (antcomb->quick_scan_cnt) {
285         case 0:
286                 /* set alt to main, and alt to first conf */
287                 div_ant_conf->main_lna_conf = antcomb->main_conf;
288                 div_ant_conf->alt_lna_conf = antcomb->first_quick_scan_conf;
289                 break;
290         case 1:
291                 /* set alt to main, and alt to first conf */
292                 div_ant_conf->main_lna_conf = antcomb->main_conf;
293                 div_ant_conf->alt_lna_conf = antcomb->second_quick_scan_conf;
294                 antcomb->rssi_first = main_rssi_avg;
295                 antcomb->rssi_second = alt_rssi_avg;
296
297                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
298                         /* main is LNA1 */
299                         if (ath_is_alt_ant_ratio_better(alt_ratio,
300                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
301                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
302                                                 main_rssi_avg, alt_rssi_avg,
303                                                 antcomb->total_pkt_count))
304                                 antcomb->first_ratio = AH_TRUE;
305                         else
306                                 antcomb->first_ratio = AH_FALSE;
307                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
308                         if (ath_is_alt_ant_ratio_better(alt_ratio,
309                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
310                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
311                                                 main_rssi_avg, alt_rssi_avg,
312                                                 antcomb->total_pkt_count))
313                                 antcomb->first_ratio = AH_TRUE;
314                         else
315                                 antcomb->first_ratio = AH_FALSE;
316                 } else {
317                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
318                             (alt_rssi_avg > main_rssi_avg +
319                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
320                             (alt_rssi_avg > main_rssi_avg)) &&
321                             (antcomb->total_pkt_count > 50))
322                                 antcomb->first_ratio = AH_TRUE;
323                         else
324                                 antcomb->first_ratio = AH_FALSE;
325                 }
326                 break;
327         case 2:
328                 antcomb->alt_good = AH_FALSE;
329                 antcomb->scan_not_start = AH_FALSE;
330                 antcomb->scan = AH_FALSE;
331                 antcomb->rssi_first = main_rssi_avg;
332                 antcomb->rssi_third = alt_rssi_avg;
333
334                 if (antcomb->second_quick_scan_conf == HAL_ANT_DIV_COMB_LNA1)
335                         antcomb->rssi_lna1 = alt_rssi_avg;
336                 else if (antcomb->second_quick_scan_conf ==
337                          HAL_ANT_DIV_COMB_LNA2)
338                         antcomb->rssi_lna2 = alt_rssi_avg;
339                 else if (antcomb->second_quick_scan_conf ==
340                          HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2) {
341                         if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2)
342                                 antcomb->rssi_lna2 = main_rssi_avg;
343                         else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1)
344                                 antcomb->rssi_lna1 = main_rssi_avg;
345                 }
346
347                 if (antcomb->rssi_lna2 > antcomb->rssi_lna1 +
348                     ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)
349                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
350                 else
351                         div_ant_conf->main_lna_conf = HAL_ANT_DIV_COMB_LNA1;
352
353                 if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) {
354                         if (ath_is_alt_ant_ratio_better(alt_ratio,
355                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_HI,
356                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
357                                                 main_rssi_avg, alt_rssi_avg,
358                                                 antcomb->total_pkt_count))
359                                 antcomb->second_ratio = AH_TRUE;
360                         else
361                                 antcomb->second_ratio = AH_FALSE;
362                 } else if (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2) {
363                         if (ath_is_alt_ant_ratio_better(alt_ratio,
364                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_MID,
365                                                 ATH_ANT_DIV_COMB_LNA1_DELTA_LOW,
366                                                 main_rssi_avg, alt_rssi_avg,
367                                                 antcomb->total_pkt_count))
368                                 antcomb->second_ratio = AH_TRUE;
369                         else
370                                 antcomb->second_ratio = AH_FALSE;
371                 } else {
372                         if ((((alt_ratio >= ATH_ANT_DIV_COMB_ALT_ANT_RATIO2) &&
373                             (alt_rssi_avg > main_rssi_avg +
374                             ATH_ANT_DIV_COMB_LNA1_DELTA_HI)) ||
375                             (alt_rssi_avg > main_rssi_avg)) &&
376                             (antcomb->total_pkt_count > 50))
377                                 antcomb->second_ratio = AH_TRUE;
378                         else
379                                 antcomb->second_ratio = AH_FALSE;
380                 }
381
382                 /* set alt to the conf with maximun ratio */
383                 if (antcomb->first_ratio && antcomb->second_ratio) {
384                         if (antcomb->rssi_second > antcomb->rssi_third) {
385                                 /* first alt*/
386                                 if ((antcomb->first_quick_scan_conf ==
387                                     HAL_ANT_DIV_COMB_LNA1) ||
388                                     (antcomb->first_quick_scan_conf ==
389                                     HAL_ANT_DIV_COMB_LNA2))
390                                         /* Set alt LNA1 or LNA2*/
391                                         if (div_ant_conf->main_lna_conf ==
392                                             HAL_ANT_DIV_COMB_LNA2)
393                                                 div_ant_conf->alt_lna_conf =
394                                                         HAL_ANT_DIV_COMB_LNA1;
395                                         else
396                                                 div_ant_conf->alt_lna_conf =
397                                                         HAL_ANT_DIV_COMB_LNA2;
398                                 else
399                                         /* Set alt to A+B or A-B */
400                                         div_ant_conf->alt_lna_conf =
401                                                 antcomb->first_quick_scan_conf;
402                         } else if ((antcomb->second_quick_scan_conf ==
403                                    HAL_ANT_DIV_COMB_LNA1) ||
404                                    (antcomb->second_quick_scan_conf ==
405                                    HAL_ANT_DIV_COMB_LNA2)) {
406                                 /* Set alt LNA1 or LNA2 */
407                                 if (div_ant_conf->main_lna_conf ==
408                                     HAL_ANT_DIV_COMB_LNA2)
409                                         div_ant_conf->alt_lna_conf =
410                                                 HAL_ANT_DIV_COMB_LNA1;
411                                 else
412                                         div_ant_conf->alt_lna_conf =
413                                                 HAL_ANT_DIV_COMB_LNA2;
414                         } else {
415                                 /* Set alt to A+B or A-B */
416                                 div_ant_conf->alt_lna_conf =
417                                         antcomb->second_quick_scan_conf;
418                         }
419                 } else if (antcomb->first_ratio) {
420                         /* first alt */
421                         if ((antcomb->first_quick_scan_conf ==
422                             HAL_ANT_DIV_COMB_LNA1) ||
423                             (antcomb->first_quick_scan_conf ==
424                             HAL_ANT_DIV_COMB_LNA2))
425                                         /* Set alt LNA1 or LNA2 */
426                                 if (div_ant_conf->main_lna_conf ==
427                                     HAL_ANT_DIV_COMB_LNA2)
428                                         div_ant_conf->alt_lna_conf =
429                                                         HAL_ANT_DIV_COMB_LNA1;
430                                 else
431                                         div_ant_conf->alt_lna_conf =
432                                                         HAL_ANT_DIV_COMB_LNA2;
433                         else
434                                 /* Set alt to A+B or A-B */
435                                 div_ant_conf->alt_lna_conf =
436                                                 antcomb->first_quick_scan_conf;
437                 } else if (antcomb->second_ratio) {
438                                 /* second alt */
439                         if ((antcomb->second_quick_scan_conf ==
440                             HAL_ANT_DIV_COMB_LNA1) ||
441                             (antcomb->second_quick_scan_conf ==
442                             HAL_ANT_DIV_COMB_LNA2))
443                                 /* Set alt LNA1 or LNA2 */
444                                 if (div_ant_conf->main_lna_conf ==
445                                     HAL_ANT_DIV_COMB_LNA2)
446                                         div_ant_conf->alt_lna_conf =
447                                                 HAL_ANT_DIV_COMB_LNA1;
448                                 else
449                                         div_ant_conf->alt_lna_conf =
450                                                 HAL_ANT_DIV_COMB_LNA2;
451                         else
452                                 /* Set alt to A+B or A-B */
453                                 div_ant_conf->alt_lna_conf =
454                                                 antcomb->second_quick_scan_conf;
455                 } else {
456                         /* main is largest */
457                         if ((antcomb->main_conf == HAL_ANT_DIV_COMB_LNA1) ||
458                             (antcomb->main_conf == HAL_ANT_DIV_COMB_LNA2))
459                                 /* Set alt LNA1 or LNA2 */
460                                 if (div_ant_conf->main_lna_conf ==
461                                     HAL_ANT_DIV_COMB_LNA2)
462                                         div_ant_conf->alt_lna_conf =
463                                                         HAL_ANT_DIV_COMB_LNA1;
464                                 else
465                                         div_ant_conf->alt_lna_conf =
466                                                         HAL_ANT_DIV_COMB_LNA2;
467                         else
468                                 /* Set alt to A+B or A-B */
469                                 div_ant_conf->alt_lna_conf = antcomb->main_conf;
470                 }
471                 break;
472         default:
473                 break;
474         }
475 }
476
477 static void
478 ath_ant_adjust_fast_divbias(struct if_ath_ant_comb_state *antcomb,
479     int alt_ratio, int alt_ant_ratio_th, u_int config_group,
480     HAL_ANT_COMB_CONFIG *pdiv_ant_conf)
481 {
482
483         if (config_group == HAL_ANTDIV_CONFIG_GROUP_1) {
484                 switch ((pdiv_ant_conf->main_lna_conf << 4)
485                     | pdiv_ant_conf->alt_lna_conf) {
486                 case (0x01): //A-B LNA2
487                         pdiv_ant_conf->fast_div_bias = 0x1;
488                         pdiv_ant_conf->main_gaintb   = 0;
489                         pdiv_ant_conf->alt_gaintb    = 0;
490                         break;
491                 case (0x02): //A-B LNA1
492                         pdiv_ant_conf->fast_div_bias = 0x1;
493                         pdiv_ant_conf->main_gaintb   = 0;
494                         pdiv_ant_conf->alt_gaintb    = 0;
495                         break;
496                 case (0x03): //A-B A+B
497                         pdiv_ant_conf->fast_div_bias = 0x1;
498                         pdiv_ant_conf->main_gaintb   = 0;
499                         pdiv_ant_conf->alt_gaintb    = 0;
500                         break;
501                 case (0x10): //LNA2 A-B
502                         if ((antcomb->scan == 0)
503                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
504                                 pdiv_ant_conf->fast_div_bias = 0x3f;
505                         } else {
506                                 pdiv_ant_conf->fast_div_bias = 0x1;
507                         }
508                         pdiv_ant_conf->main_gaintb   = 0;
509                         pdiv_ant_conf->alt_gaintb    = 0;
510                         break;
511                 case (0x12): //LNA2 LNA1
512                         pdiv_ant_conf->fast_div_bias = 0x1;
513                         pdiv_ant_conf->main_gaintb   = 0;
514                         pdiv_ant_conf->alt_gaintb    = 0;
515                         break;
516                         case (0x13): //LNA2 A+B
517                         if ((antcomb->scan == 0)
518                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
519                                 pdiv_ant_conf->fast_div_bias = 0x3f;
520                         } else {
521                                 pdiv_ant_conf->fast_div_bias = 0x1;
522                         }
523                         pdiv_ant_conf->main_gaintb   = 0;
524                         pdiv_ant_conf->alt_gaintb    = 0;
525                         break;
526                 case (0x20): //LNA1 A-B
527                         if ((antcomb->scan == 0)
528                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
529                                 pdiv_ant_conf->fast_div_bias = 0x3f;
530                         } else {
531                                 pdiv_ant_conf->fast_div_bias = 0x1;
532                         }
533                         pdiv_ant_conf->main_gaintb   = 0;
534                         pdiv_ant_conf->alt_gaintb    = 0;
535                         break;
536                 case (0x21): //LNA1 LNA2
537                         pdiv_ant_conf->fast_div_bias = 0x1;
538                         pdiv_ant_conf->main_gaintb   = 0;
539                         pdiv_ant_conf->alt_gaintb    = 0;
540                         break;
541                 case (0x23): //LNA1 A+B
542                         if ((antcomb->scan == 0)
543                             && (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO)) {
544                                 pdiv_ant_conf->fast_div_bias = 0x3f;
545                         } else {
546                                 pdiv_ant_conf->fast_div_bias = 0x1;
547                         }
548                         pdiv_ant_conf->main_gaintb   = 0;
549                         pdiv_ant_conf->alt_gaintb    = 0;
550                         break;
551                 case (0x30): //A+B A-B
552                         pdiv_ant_conf->fast_div_bias = 0x1;
553                         pdiv_ant_conf->main_gaintb   = 0;
554                         pdiv_ant_conf->alt_gaintb    = 0;
555                         break;
556                 case (0x31): //A+B LNA2
557                         pdiv_ant_conf->fast_div_bias = 0x1;
558                         pdiv_ant_conf->main_gaintb   = 0;
559                         pdiv_ant_conf->alt_gaintb    = 0;
560                         break;
561                 case (0x32): //A+B LNA1
562                         pdiv_ant_conf->fast_div_bias = 0x1;
563                         pdiv_ant_conf->main_gaintb   = 0;
564                         pdiv_ant_conf->alt_gaintb    = 0;
565                         break;
566                 default:
567                         break;
568                 }
569         } else if (config_group == HAL_ANTDIV_CONFIG_GROUP_2) {
570                 switch ((pdiv_ant_conf->main_lna_conf << 4)
571                     | pdiv_ant_conf->alt_lna_conf) {
572                 case (0x01): //A-B LNA2
573                         pdiv_ant_conf->fast_div_bias = 0x1;
574                         pdiv_ant_conf->main_gaintb   = 0;
575                         pdiv_ant_conf->alt_gaintb    = 0;
576                         break;
577                 case (0x02): //A-B LNA1
578                         pdiv_ant_conf->fast_div_bias = 0x1;
579                         pdiv_ant_conf->main_gaintb   = 0;
580                         pdiv_ant_conf->alt_gaintb    = 0;
581                         break;
582                 case (0x03): //A-B A+B
583                         pdiv_ant_conf->fast_div_bias = 0x1;
584                         pdiv_ant_conf->main_gaintb   = 0;
585                         pdiv_ant_conf->alt_gaintb    = 0;
586                         break;
587                 case (0x10): //LNA2 A-B
588                         if ((antcomb->scan == 0)
589                             && (alt_ratio > alt_ant_ratio_th)) {
590                                 pdiv_ant_conf->fast_div_bias = 0x1;
591                         } else {
592                                 pdiv_ant_conf->fast_div_bias = 0x2;
593                         }
594                         pdiv_ant_conf->main_gaintb   = 0;
595                         pdiv_ant_conf->alt_gaintb    = 0;
596                         break;
597                 case (0x12): //LNA2 LNA1
598                         pdiv_ant_conf->fast_div_bias = 0x1;
599                         pdiv_ant_conf->main_gaintb   = 0;
600                         pdiv_ant_conf->alt_gaintb    = 0;
601                         break;
602                 case (0x13): //LNA2 A+B
603                         if ((antcomb->scan == 0)
604                             && (alt_ratio > alt_ant_ratio_th)) {
605                                 pdiv_ant_conf->fast_div_bias = 0x1;
606                         } else {
607                                 pdiv_ant_conf->fast_div_bias = 0x2;
608                         }
609                         pdiv_ant_conf->main_gaintb   = 0;
610                         pdiv_ant_conf->alt_gaintb    = 0;
611                         break;
612                 case (0x20): //LNA1 A-B
613                         if ((antcomb->scan == 0)
614                             && (alt_ratio > alt_ant_ratio_th)) {
615                                 pdiv_ant_conf->fast_div_bias = 0x1;
616                         } else {
617                                 pdiv_ant_conf->fast_div_bias = 0x2;
618                         }
619                         pdiv_ant_conf->main_gaintb   = 0;
620                         pdiv_ant_conf->alt_gaintb    = 0;
621                         break;
622                 case (0x21): //LNA1 LNA2
623                         pdiv_ant_conf->fast_div_bias = 0x1;
624                         pdiv_ant_conf->main_gaintb   = 0;
625                         pdiv_ant_conf->alt_gaintb    = 0;
626                         break;
627                 case (0x23): //LNA1 A+B
628                         if ((antcomb->scan == 0)
629                             && (alt_ratio > alt_ant_ratio_th)) {
630                                 pdiv_ant_conf->fast_div_bias = 0x1;
631                         } else {
632                                 pdiv_ant_conf->fast_div_bias = 0x2;
633                         }
634                         pdiv_ant_conf->main_gaintb   = 0;
635                         pdiv_ant_conf->alt_gaintb    = 0;
636                         break;
637                 case (0x30): //A+B A-B
638                         pdiv_ant_conf->fast_div_bias = 0x1;
639                         pdiv_ant_conf->main_gaintb   = 0;
640                         pdiv_ant_conf->alt_gaintb    = 0;
641                         break;
642                 case (0x31): //A+B LNA2
643                         pdiv_ant_conf->fast_div_bias = 0x1;
644                         pdiv_ant_conf->main_gaintb   = 0;
645                         pdiv_ant_conf->alt_gaintb    = 0;
646                         break;
647                 case (0x32): //A+B LNA1
648                         pdiv_ant_conf->fast_div_bias = 0x1;
649                         pdiv_ant_conf->main_gaintb   = 0;
650                         pdiv_ant_conf->alt_gaintb    = 0;
651                         break;
652                 default:
653                         break;
654                 }
655         } else { /* DEFAULT_ANTDIV_CONFIG_GROUP */
656                 switch ((pdiv_ant_conf->main_lna_conf << 4) | pdiv_ant_conf->alt_lna_conf) {
657                 case (0x01): //A-B LNA2
658                         pdiv_ant_conf->fast_div_bias = 0x3b;
659                         break;
660                 case (0x02): //A-B LNA1
661                         pdiv_ant_conf->fast_div_bias = 0x3d;
662                         break;
663                 case (0x03): //A-B A+B
664                         pdiv_ant_conf->fast_div_bias = 0x1;
665                         break;
666                 case (0x10): //LNA2 A-B
667                         pdiv_ant_conf->fast_div_bias = 0x7;
668                         break;
669                 case (0x12): //LNA2 LNA1
670                         pdiv_ant_conf->fast_div_bias = 0x2;
671                         break;
672                 case (0x13): //LNA2 A+B
673                         pdiv_ant_conf->fast_div_bias = 0x7;
674                         break;
675                 case (0x20): //LNA1 A-B
676                         pdiv_ant_conf->fast_div_bias = 0x6;
677                         break;
678                 case (0x21): //LNA1 LNA2
679                         pdiv_ant_conf->fast_div_bias = 0x0;
680                         break;
681                 case (0x23): //LNA1 A+B
682                         pdiv_ant_conf->fast_div_bias = 0x6;
683                         break;
684                 case (0x30): //A+B A-B
685                         pdiv_ant_conf->fast_div_bias = 0x1;
686                         break;
687                 case (0x31): //A+B LNA2
688                         pdiv_ant_conf->fast_div_bias = 0x3b;
689                         break;
690                 case (0x32): //A+B LNA1
691                         pdiv_ant_conf->fast_div_bias = 0x3d;
692                         break;
693                 default:
694                         break;
695                 }
696         }
697 }
698
699 /*
700  * AR9485/AR933x TODO:
701  * + Select a ratio based on whether RSSI is low or not; but I need
702  *   to figure out what "low_rssi_th" is sourced from.
703  * + What's ath_ant_div_comb_alt_check() in the reference driver do?
704  * + .. and there's likely a bunch of other things to include in this.
705  */
706
707 /* Antenna diversity and combining */
708 void
709 ath_lna_rx_comb_scan(struct ath_softc *sc, struct ath_rx_status *rs,
710     unsigned long ticks, int hz)
711 {
712         HAL_ANT_COMB_CONFIG div_ant_conf;
713         struct if_ath_ant_comb_state *antcomb = sc->sc_lna_div;
714         int alt_ratio = 0, alt_rssi_avg = 0, main_rssi_avg = 0, curr_alt_set;
715         int curr_main_set, curr_bias;
716         int main_rssi = rs->rs_rssi_ctl[0];
717         int alt_rssi = rs->rs_rssi_ctl[1];
718         int rx_ant_conf, main_ant_conf, alt_ant_conf;
719         HAL_BOOL short_scan = AH_FALSE;
720
721         rx_ant_conf = (rs->rs_rssi_ctl[2] >> 4) & ATH_ANT_RX_MASK;
722         main_ant_conf = (rs->rs_rssi_ctl[2] >> 2) & ATH_ANT_RX_MASK;
723         alt_ant_conf = (rs->rs_rssi_ctl[2] >> 0) & ATH_ANT_RX_MASK;
724
725 #if 0
726         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
727             "%s: RSSI %d/%d, conf %x/%x, rxconf %x, LNA: %d; ANT: %d; "
728             "FastDiv: %d\n",
729             __func__,
730             main_rssi,
731             alt_rssi,
732             main_ant_conf,
733             alt_ant_conf,
734             rx_ant_conf,
735             !!(rs->rs_rssi_ctl[2] & 0x80),
736             !!(rs->rs_rssi_ctl[2] & 0x40),
737             !!(rs->rs_rssi_ext[2] & 0x40));
738 #endif
739
740         /*
741          * If LNA diversity combining isn't enabled, don't run this.
742          */
743         if (! sc->sc_dolnadiv)
744                 return;
745
746         /*
747          * XXX this is ugly, but the HAL code attaches the
748          * LNA diversity to the TX antenna settings.
749          * I don't know why.
750          */
751         if (sc->sc_txantenna != HAL_ANT_VARIABLE)
752                 return;
753
754         /* Record packet only when alt_rssi is positive */
755         if (main_rssi > 0 && alt_rssi > 0) {
756                 antcomb->total_pkt_count++;
757                 antcomb->main_total_rssi += main_rssi;
758                 antcomb->alt_total_rssi  += alt_rssi;
759                 if (main_ant_conf == rx_ant_conf)
760                         antcomb->main_recv_cnt++;
761                 else
762                         antcomb->alt_recv_cnt++;
763         }
764
765         /* Short scan check */
766         if (antcomb->scan && antcomb->alt_good) {
767                 if (time_after(ticks, antcomb->scan_start_time +
768                     msecs_to_jiffies(ATH_ANT_DIV_COMB_SHORT_SCAN_INTR)))
769                         short_scan = AH_TRUE;
770                 else
771                         if (antcomb->total_pkt_count ==
772                             ATH_ANT_DIV_COMB_SHORT_SCAN_PKTCOUNT) {
773                                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
774                                             antcomb->total_pkt_count);
775                                 if (alt_ratio < ATH_ANT_DIV_COMB_ALT_ANT_RATIO)
776                                         short_scan = AH_TRUE;
777                         }
778         }
779
780 #if 0
781         DPRINTF(sc, ATH_DEBUG_DIVERSITY,
782             "%s: total pkt=%d, aggr=%d, short_scan=%d\n",
783             __func__,
784             antcomb->total_pkt_count,
785             !! (rs->rs_moreaggr),
786             !! (short_scan));
787 #endif
788
789         if (((antcomb->total_pkt_count < ATH_ANT_DIV_COMB_MAX_PKTCOUNT) ||
790             rs->rs_moreaggr) && !short_scan)
791                 return;
792
793         if (antcomb->total_pkt_count) {
794                 alt_ratio = ((antcomb->alt_recv_cnt * 100) /
795                              antcomb->total_pkt_count);
796                 main_rssi_avg = (antcomb->main_total_rssi /
797                                  antcomb->total_pkt_count);
798                 alt_rssi_avg = (antcomb->alt_total_rssi /
799                                  antcomb->total_pkt_count);
800         }
801
802         OS_MEMZERO(&div_ant_conf, sizeof(div_ant_conf));
803
804         ath_hal_div_comb_conf_get(sc->sc_ah, &div_ant_conf);
805         curr_alt_set = div_ant_conf.alt_lna_conf;
806         curr_main_set = div_ant_conf.main_lna_conf;
807         curr_bias = div_ant_conf.fast_div_bias;
808
809         antcomb->count++;
810
811         if (antcomb->count == ATH_ANT_DIV_COMB_MAX_COUNT) {
812                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
813                         ath_lnaconf_alt_good_scan(antcomb, &div_ant_conf,
814                                                   main_rssi_avg);
815                         antcomb->alt_good = AH_TRUE;
816                 } else {
817                         antcomb->alt_good = AH_FALSE;
818                 }
819
820                 antcomb->count = 0;
821                 antcomb->scan = AH_TRUE;
822                 antcomb->scan_not_start = AH_TRUE;
823         }
824
825         if (!antcomb->scan) {
826                 if (alt_ratio > ATH_ANT_DIV_COMB_ALT_ANT_RATIO) {
827                         if (curr_alt_set == HAL_ANT_DIV_COMB_LNA2) {
828                                 /* Switch main and alt LNA */
829                                 div_ant_conf.main_lna_conf =
830                                                 HAL_ANT_DIV_COMB_LNA2;
831                                 div_ant_conf.alt_lna_conf  =
832                                                 HAL_ANT_DIV_COMB_LNA1;
833                         } else if (curr_alt_set == HAL_ANT_DIV_COMB_LNA1) {
834                                 div_ant_conf.main_lna_conf =
835                                                 HAL_ANT_DIV_COMB_LNA1;
836                                 div_ant_conf.alt_lna_conf  =
837                                                 HAL_ANT_DIV_COMB_LNA2;
838                         }
839
840                         goto div_comb_done;
841                 } else if ((curr_alt_set != HAL_ANT_DIV_COMB_LNA1) &&
842                            (curr_alt_set != HAL_ANT_DIV_COMB_LNA2)) {
843                         /* Set alt to another LNA */
844                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2)
845                                 div_ant_conf.alt_lna_conf =
846                                                 HAL_ANT_DIV_COMB_LNA1;
847                         else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1)
848                                 div_ant_conf.alt_lna_conf =
849                                                 HAL_ANT_DIV_COMB_LNA2;
850
851                         goto div_comb_done;
852                 }
853
854                 if ((alt_rssi_avg < (main_rssi_avg +
855                     antcomb->lna1_lna2_delta)))
856                         goto div_comb_done;
857         }
858
859         if (!antcomb->scan_not_start) {
860                 switch (curr_alt_set) {
861                 case HAL_ANT_DIV_COMB_LNA2:
862                         antcomb->rssi_lna2 = alt_rssi_avg;
863                         antcomb->rssi_lna1 = main_rssi_avg;
864                         antcomb->scan = AH_TRUE;
865                         /* set to A+B */
866                         div_ant_conf.main_lna_conf =
867                                 HAL_ANT_DIV_COMB_LNA1;
868                         div_ant_conf.alt_lna_conf  =
869                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
870                         break;
871                 case HAL_ANT_DIV_COMB_LNA1:
872                         antcomb->rssi_lna1 = alt_rssi_avg;
873                         antcomb->rssi_lna2 = main_rssi_avg;
874                         antcomb->scan = AH_TRUE;
875                         /* set to A+B */
876                         div_ant_conf.main_lna_conf = HAL_ANT_DIV_COMB_LNA2;
877                         div_ant_conf.alt_lna_conf  =
878                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
879                         break;
880                 case HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2:
881                         antcomb->rssi_add = alt_rssi_avg;
882                         antcomb->scan = AH_TRUE;
883                         /* set to A-B */
884                         div_ant_conf.alt_lna_conf =
885                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
886                         break;
887                 case HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2:
888                         antcomb->rssi_sub = alt_rssi_avg;
889                         antcomb->scan = AH_FALSE;
890                         if (antcomb->rssi_lna2 >
891                             (antcomb->rssi_lna1 +
892                             ATH_ANT_DIV_COMB_LNA1_LNA2_SWITCH_DELTA)) {
893                                 /* use LNA2 as main LNA */
894                                 if ((antcomb->rssi_add > antcomb->rssi_lna1) &&
895                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
896                                         /* set to A+B */
897                                         div_ant_conf.main_lna_conf =
898                                                 HAL_ANT_DIV_COMB_LNA2;
899                                         div_ant_conf.alt_lna_conf  =
900                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
901                                 } else if (antcomb->rssi_sub >
902                                            antcomb->rssi_lna1) {
903                                         /* set to A-B */
904                                         div_ant_conf.main_lna_conf =
905                                                 HAL_ANT_DIV_COMB_LNA2;
906                                         div_ant_conf.alt_lna_conf =
907                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
908                                 } else {
909                                         /* set to LNA1 */
910                                         div_ant_conf.main_lna_conf =
911                                                 HAL_ANT_DIV_COMB_LNA2;
912                                         div_ant_conf.alt_lna_conf =
913                                                 HAL_ANT_DIV_COMB_LNA1;
914                                 }
915                         } else {
916                                 /* use LNA1 as main LNA */
917                                 if ((antcomb->rssi_add > antcomb->rssi_lna2) &&
918                                     (antcomb->rssi_add > antcomb->rssi_sub)) {
919                                         /* set to A+B */
920                                         div_ant_conf.main_lna_conf =
921                                                 HAL_ANT_DIV_COMB_LNA1;
922                                         div_ant_conf.alt_lna_conf  =
923                                                 HAL_ANT_DIV_COMB_LNA1_PLUS_LNA2;
924                                 } else if (antcomb->rssi_sub >
925                                            antcomb->rssi_lna1) {
926                                         /* set to A-B */
927                                         div_ant_conf.main_lna_conf =
928                                                 HAL_ANT_DIV_COMB_LNA1;
929                                         div_ant_conf.alt_lna_conf =
930                                                 HAL_ANT_DIV_COMB_LNA1_MINUS_LNA2;
931                                 } else {
932                                         /* set to LNA2 */
933                                         div_ant_conf.main_lna_conf =
934                                                 HAL_ANT_DIV_COMB_LNA1;
935                                         div_ant_conf.alt_lna_conf =
936                                                 HAL_ANT_DIV_COMB_LNA2;
937                                 }
938                         }
939                         break;
940                 default:
941                         break;
942                 }
943         } else {
944                 if (!antcomb->alt_good) {
945                         antcomb->scan_not_start = AH_FALSE;
946                         /* Set alt to another LNA */
947                         if (curr_main_set == HAL_ANT_DIV_COMB_LNA2) {
948                                 div_ant_conf.main_lna_conf =
949                                                 HAL_ANT_DIV_COMB_LNA2;
950                                 div_ant_conf.alt_lna_conf =
951                                                 HAL_ANT_DIV_COMB_LNA1;
952                         } else if (curr_main_set == HAL_ANT_DIV_COMB_LNA1) {
953                                 div_ant_conf.main_lna_conf =
954                                                 HAL_ANT_DIV_COMB_LNA1;
955                                 div_ant_conf.alt_lna_conf =
956                                                 HAL_ANT_DIV_COMB_LNA2;
957                         }
958                         goto div_comb_done;
959                 }
960         }
961
962         ath_select_ant_div_from_quick_scan(antcomb, &div_ant_conf,
963                                            main_rssi_avg, alt_rssi_avg,
964                                            alt_ratio);
965
966         antcomb->quick_scan_cnt++;
967
968 div_comb_done:
969 #if 0
970         ath_ant_div_conf_fast_divbias(&div_ant_conf);
971 #endif
972
973         ath_ant_adjust_fast_divbias(antcomb,
974             alt_ratio,
975             ATH_ANT_DIV_COMB_ALT_ANT_RATIO,
976             div_ant_conf.antdiv_configgroup,
977             &div_ant_conf);
978
979         ath_hal_div_comb_conf_set(sc->sc_ah, &div_ant_conf);
980
981         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: total_pkt_count=%d\n",
982            __func__, antcomb->total_pkt_count);
983
984         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_total_rssi=%d\n",
985            __func__, antcomb->main_total_rssi);
986         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_total_rssi=%d\n",
987            __func__, antcomb->alt_total_rssi);
988
989         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_rssi_avg=%d\n",
990            __func__, main_rssi_avg);
991         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_alt_rssi_avg=%d\n",
992            __func__, alt_rssi_avg);
993
994         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_recv_cnt=%d\n",
995            __func__, antcomb->main_recv_cnt);
996         DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: alt_recv_cnt=%d\n",
997            __func__, antcomb->alt_recv_cnt);
998
999 //      if (curr_alt_set != div_ant_conf.alt_lna_conf)
1000                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: lna_conf: %x -> %x\n",
1001                     __func__, curr_alt_set, div_ant_conf.alt_lna_conf);
1002 //      if (curr_main_set != div_ant_conf.main_lna_conf)
1003                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: main_lna_conf: %x -> %x\n",
1004                     __func__, curr_main_set, div_ant_conf.main_lna_conf);
1005 //      if (curr_bias != div_ant_conf.fast_div_bias)
1006                 DPRINTF(sc, ATH_DEBUG_DIVERSITY, "%s: fast_div_bias: %x -> %x\n",
1007                     __func__, curr_bias, div_ant_conf.fast_div_bias);
1008
1009         antcomb->scan_start_time = ticks;
1010         antcomb->total_pkt_count = 0;
1011         antcomb->main_total_rssi = 0;
1012         antcomb->alt_total_rssi = 0;
1013         antcomb->main_recv_cnt = 0;
1014         antcomb->alt_recv_cnt = 0;
1015 }
1016