staging: brcm80211: remove nested include statements
[firefly-linux-kernel-4.4.55.git] / drivers / staging / brcm80211 / brcmsmac / wlc_antsel.c
1 /*
2  * Copyright (c) 2010 Broadcom Corporation
3  *
4  * Permission to use, copy, modify, and/or distribute this software for any
5  * purpose with or without fee is hereby granted, provided that the above
6  * copyright notice and this permission notice appear in all copies.
7  *
8  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15  */
16
17 #include <wlc_cfg.h>
18
19 #ifdef WLANTSEL
20
21 #include <linux/kernel.h>
22 #include <linux/module.h>
23 #include <linux/pci.h>
24
25 #include <proto/802.1d.h>
26 #include <osl.h>
27 #include <bcmdefs.h>
28 #include <bcmutils.h>
29 #include <siutils.h>
30 #include <bcmdevs.h>
31 #include <sbhnddma.h>
32 #include <wlioctl.h>
33
34 #include "sbhndpio.h"
35 #include "d11.h"
36 #include "wlc_rate.h"
37 #include "wlc_key.h"
38 #include "wlc_scb.h"
39 #include "wlc_pub.h"
40 #include "wl_dbg.h"
41 #include "phy/wlc_phy_hal.h"
42 #include "wlc_bmac.h"
43 #include "wlc_channel.h"
44 #include "wlc_bsscfg.h"
45 #include "wlc_mac80211.h"
46 #include "wl_export.h"
47 #include "wlc_phy_shim.h"
48 #include "wlc_antsel.h"
49
50 /* useful macros */
51 #define WLC_ANTSEL_11N_0(ant)   ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
52 #define WLC_ANTSEL_11N_1(ant)   (((ant) & ANT_SELCFG_MASK) & 0xf)
53 #define WLC_ANTIDX_11N(ant)     (((WLC_ANTSEL_11N_0(ant)) << 2) + (WLC_ANTSEL_11N_1(ant)))
54 #define WLC_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
55 #define WLC_ANTSEL_11N(ant)     ((ant) & ANT_SELCFG_MASK)
56
57 /* antenna switch */
58 /* defines for no boardlevel antenna diversity */
59 #define ANT_SELCFG_DEF_2x2      0x01    /* default antenna configuration */
60
61 /* 2x3 antdiv defines and tables for GPIO communication */
62 #define ANT_SELCFG_NUM_2x3      3
63 #define ANT_SELCFG_DEF_2x3      0x01    /* default antenna configuration */
64
65 /* 2x4 antdiv rev4 defines and tables for GPIO communication */
66 #define ANT_SELCFG_NUM_2x4      4
67 #define ANT_SELCFG_DEF_2x4      0x02    /* default antenna configuration */
68
69 /* static functions */
70 static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel);
71 static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
72 static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
73 static void wlc_antsel_init_cfg(struct antsel_info *asi,
74                                 wlc_antselcfg_t *antsel,
75                                 bool auto_sel);
76
77 const u16 mimo_2x4_div_antselpat_tbl[] = {
78         0, 0, 0x9, 0xa,         /* ant0: 0 ant1: 2,3 */
79         0, 0, 0x5, 0x6,         /* ant0: 1 ant1: 2,3 */
80         0, 0, 0, 0,             /* n.a.              */
81         0, 0, 0, 0              /* n.a.              */
82 };
83
84 const u8 mimo_2x4_div_antselid_tbl[16] = {
85         0, 0, 0, 0, 0, 2, 3, 0,
86         0, 0, 1, 0, 0, 0, 0, 0  /* pat to antselid */
87 };
88
89 const u16 mimo_2x3_div_antselpat_tbl[] = {
90         16, 0, 1, 16,           /* ant0: 0 ant1: 1,2 */
91         16, 16, 16, 16,         /* n.a.              */
92         16, 2, 16, 16,          /* ant0: 2 ant1: 1   */
93         16, 16, 16, 16          /* n.a.              */
94 };
95
96 const u8 mimo_2x3_div_antselid_tbl[16] = {
97         0, 1, 2, 0, 0, 0, 0, 0,
98         0, 0, 0, 0, 0, 0, 0, 0  /* pat to antselid */
99 };
100
101 struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc,
102                                       struct osl_info *osh,
103                                       struct wlc_pub *pub,
104                                       struct wlc_hw_info *wlc_hw) {
105         struct antsel_info *asi;
106
107         asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
108         if (!asi) {
109                 WL_ERROR("wl%d: wlc_antsel_attach: out of mem\n", pub->unit);
110                 return NULL;
111         }
112
113         asi->wlc = wlc;
114         asi->pub = pub;
115         asi->antsel_type = ANTSEL_NA;
116         asi->antsel_avail = false;
117         asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
118
119         if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
120                 switch (asi->antsel_antswitch) {
121                 case ANTSWITCH_TYPE_1:
122                 case ANTSWITCH_TYPE_2:
123                 case ANTSWITCH_TYPE_3:
124                         /* 4321/2 board with 2x3 switch logic */
125                         asi->antsel_type = ANTSEL_2x3;
126                         /* Antenna selection availability */
127                         if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
128                             ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
129                                 asi->antsel_avail = true;
130                         } else
131                             if (((u16) getintvar(asi->pub->vars, "aa2g") ==
132                                  3)
133                                 || ((u16) getintvar(asi->pub->vars, "aa5g")
134                                     == 3)) {
135                                 asi->antsel_avail = false;
136                         } else {
137                                 asi->antsel_avail = false;
138                                 WL_ERROR("wlc_antsel_attach: 2o3 board cfg invalid\n");
139                                 ASSERT(0);
140                         }
141                         break;
142                 default:
143                         break;
144                 }
145         } else if ((asi->pub->sromrev == 4) &&
146                    ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
147                    ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
148                 /* hack to match old 4321CB2 cards with 2of3 antenna switch */
149                 asi->antsel_type = ANTSEL_2x3;
150                 asi->antsel_avail = true;
151         } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
152                 asi->antsel_type = ANTSEL_2x4;
153                 asi->antsel_avail = true;
154         }
155
156         /* Set the antenna selection type for the low driver */
157         wlc_bmac_antsel_type_set(wlc_hw, asi->antsel_type);
158
159         /* Init (auto/manual) antenna selection */
160         wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true);
161         wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true);
162
163         return asi;
164 }
165
166 void wlc_antsel_detach(struct antsel_info *asi)
167 {
168         if (!asi)
169                 return;
170
171         kfree(asi);
172 }
173
174 void wlc_antsel_init(struct antsel_info *asi)
175 {
176         if ((asi->antsel_type == ANTSEL_2x3) ||
177             (asi->antsel_type == ANTSEL_2x4))
178                 wlc_antsel_cfgupd(asi, &asi->antcfg_11n);
179 }
180
181 /* boardlevel antenna selection: init antenna selection structure */
182 static void
183 wlc_antsel_init_cfg(struct antsel_info *asi, wlc_antselcfg_t *antsel,
184                     bool auto_sel)
185 {
186         if (asi->antsel_type == ANTSEL_2x3) {
187                 u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
188                     ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
189                 antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
190                 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
191                 antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
192                 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
193                 antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
194
195         } else if (asi->antsel_type == ANTSEL_2x4) {
196
197                 antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
198                 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
199                 antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
200                 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
201                 antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
202
203         } else {                /* no antenna selection available */
204
205                 antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
206                 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
207                 antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
208                 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
209                 antsel->num_antcfg = 0;
210         }
211 }
212
213 void BCMFASTPATH
214 wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
215                       u8 antselid, u8 fbantselid, u8 *antcfg,
216                       u8 *fbantcfg)
217 {
218         u8 ant;
219
220         /* if use default, assign it and return */
221         if (usedef) {
222                 *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
223                 *fbantcfg = *antcfg;
224                 return;
225         }
226
227         if (!sel) {
228                 *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
229                 *fbantcfg = *antcfg;
230
231         } else {
232                 ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
233                 if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
234                         *antcfg = wlc_antsel_id2antcfg(asi, antselid);
235                         *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid);
236                 } else {
237                         *antcfg =
238                             asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
239                         *fbantcfg = *antcfg;
240                 }
241         }
242         return;
243 }
244
245 /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
246 u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
247 {
248         u8 antselid = 0;
249
250         if (asi->antsel_type == ANTSEL_2x4) {
251                 /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
252                 antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
253                 return antselid;
254
255         } else if (asi->antsel_type == ANTSEL_2x3) {
256                 /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
257                 antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
258                 return antselid;
259         }
260
261         return antselid;
262 }
263
264 /* boardlevel antenna selection: convert id to ant_cfg */
265 static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id)
266 {
267         u8 antcfg = ANT_SELCFG_DEF_2x2;
268
269         if (asi->antsel_type == ANTSEL_2x4) {
270                 /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
271                 antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
272                 return antcfg;
273
274         } else if (asi->antsel_type == ANTSEL_2x3) {
275                 /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
276                 antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
277                 return antcfg;
278         }
279
280         return antcfg;
281 }
282
283 /* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */
284 static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
285 {
286         u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg));
287         u16 mimo_antsel = 0;
288
289         if (asi->antsel_type == ANTSEL_2x4) {
290                 /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
291                 mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
292                 return mimo_antsel;
293
294         } else if (asi->antsel_type == ANTSEL_2x3) {
295                 /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
296                 mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
297                 return mimo_antsel;
298         }
299
300         return mimo_antsel;
301 }
302
303 /* boardlevel antenna selection: ucode interface control */
304 static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel)
305 {
306         struct wlc_info *wlc = asi->wlc;
307         u8 ant_cfg;
308         u16 mimo_antsel;
309
310         ASSERT(asi->antsel_type != ANTSEL_NA);
311
312         /* 1) Update TX antconfig for all frames that are not unicast data
313          *    (aka default TX)
314          */
315         ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
316         mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
317         wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
318         /* Update driver stats for currently selected default tx/rx antenna config */
319         asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
320
321         /* 2) Update RX antconfig for all frames that are not unicast data
322          *    (aka default RX)
323          */
324         ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
325         mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
326         wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
327         /* Update driver stats for currently selected default tx/rx antenna config */
328         asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
329
330         return 0;
331 }
332
333 #endif                          /* WLANTSEL */