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