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