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