staging: brcm80211: remove osl source files from driver
[deliverable/linux.git] / drivers / staging / brcm80211 / brcmsmac / 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
3327989a 19#include <linux/kernel.h>
c6ac24e9
BR
20#include <linux/module.h>
21#include <linux/pci.h>
45575664 22
45575664 23#include <bcmdefs.h>
a9533e7e
HP
24#include <bcmutils.h>
25#include <siutils.h>
a52ba66c 26#include <bcmdevs.h>
a52ba66c 27#include <sbhnddma.h>
45575664
AS
28#include <wlioctl.h>
29
45575664
AS
30#include "d11.h"
31#include "wlc_rate.h"
32#include "wlc_key.h"
33#include "wlc_scb.h"
34#include "wlc_pub.h"
35#include "wl_dbg.h"
36#include "phy/wlc_phy_hal.h"
37#include "wlc_bmac.h"
38#include "wlc_channel.h"
39#include "wlc_bsscfg.h"
7ae92624 40#include "wlc_main.h"
45575664
AS
41#include "wl_export.h"
42#include "wlc_phy_shim.h"
43#include "wlc_antsel.h"
a9533e7e
HP
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 */
299f8a46
RV
65static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel);
66static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id);
67static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
68static void wlc_antsel_init_cfg(struct antsel_info *asi,
69 wlc_antselcfg_t *antsel,
a9533e7e
HP
70 bool auto_sel);
71
7d4df48e 72const u16 mimo_2x4_div_antselpat_tbl[] = {
a9533e7e
HP
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
41feb5ed 79const u8 mimo_2x4_div_antselid_tbl[16] = {
a9533e7e
HP
80 0, 0, 0, 0, 0, 2, 3, 0,
81 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */
82};
83
7d4df48e 84const u16 mimo_2x3_div_antselpat_tbl[] = {
a9533e7e
HP
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
41feb5ed 91const u8 mimo_2x3_div_antselid_tbl[16] = {
a9533e7e
HP
92 0, 1, 2, 0, 0, 0, 0, 0,
93 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */
94};
95
d769f4ce
AS
96struct antsel_info *wlc_antsel_attach(struct wlc_info *wlc)
97{
299f8a46 98 struct antsel_info *asi;
a9533e7e 99
299f8a46 100 asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
ca8c1e59 101 if (!asi) {
d769f4ce
AS
102 WL_ERROR("wl%d: wlc_antsel_attach: out of mem\n",
103 wlc->pub->unit);
a9533e7e
HP
104 return NULL;
105 }
106
a9533e7e 107 asi->wlc = wlc;
d769f4ce 108 asi->pub = wlc->pub;
a9533e7e 109 asi->antsel_type = ANTSEL_NA;
0965ae88 110 asi->antsel_avail = false;
41feb5ed 111 asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
a9533e7e
HP
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 */
7d4df48e
GKH
121 if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
122 ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
0f0881b0 123 asi->antsel_avail = true;
a9533e7e 124 } else
7d4df48e 125 if (((u16) getintvar(asi->pub->vars, "aa2g") ==
a9533e7e 126 3)
7d4df48e 127 || ((u16) getintvar(asi->pub->vars, "aa5g")
a9533e7e 128 == 3)) {
0965ae88 129 asi->antsel_avail = false;
a9533e7e 130 } else {
0965ae88 131 asi->antsel_avail = false;
f4528696 132 WL_ERROR("wlc_antsel_attach: 2o3 board cfg invalid\n");
a9533e7e
HP
133 ASSERT(0);
134 }
135 break;
136 default:
137 break;
138 }
139 } else if ((asi->pub->sromrev == 4) &&
7d4df48e
GKH
140 ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
141 ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
a9533e7e
HP
142 /* hack to match old 4321CB2 cards with 2of3 antenna switch */
143 asi->antsel_type = ANTSEL_2x3;
0f0881b0 144 asi->antsel_avail = true;
a9533e7e
HP
145 } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
146 asi->antsel_type = ANTSEL_2x4;
0f0881b0 147 asi->antsel_avail = true;
a9533e7e
HP
148 }
149
150 /* Set the antenna selection type for the low driver */
d769f4ce 151 wlc_bmac_antsel_type_set(wlc->hw, asi->antsel_type);
a9533e7e
HP
152
153 /* Init (auto/manual) antenna selection */
0f0881b0
GKH
154 wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true);
155 wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true);
a9533e7e
HP
156
157 return asi;
158}
159
299f8a46 160void wlc_antsel_detach(struct antsel_info *asi)
a2627bc0 161{
182acb3c 162 kfree(asi);
a9533e7e
HP
163}
164
299f8a46 165void wlc_antsel_init(struct antsel_info *asi)
a9533e7e
HP
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 */
173static void
299f8a46 174wlc_antsel_init_cfg(struct antsel_info *asi, wlc_antselcfg_t *antsel,
a9533e7e
HP
175 bool auto_sel)
176{
177 if (asi->antsel_type == ANTSEL_2x3) {
41feb5ed 178 u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
a9533e7e
HP
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
204void BCMFASTPATH
299f8a46 205wlc_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
41feb5ed
GKH
206 u8 antselid, u8 fbantselid, u8 *antcfg,
207 u8 *fbantcfg)
a9533e7e 208{
41feb5ed 209 u8 ant;
a9533e7e
HP
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 */
299f8a46 237u8 wlc_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
a9533e7e 238{
41feb5ed 239 u8 antselid = 0;
a9533e7e
HP
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 */
299f8a46 256static u8 wlc_antsel_id2antcfg(struct antsel_info *asi, u8 id)
a9533e7e 257{
41feb5ed 258 u8 antcfg = ANT_SELCFG_DEF_2x2;
a9533e7e
HP
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) */
299f8a46 275static u16 wlc_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
a9533e7e 276{
41feb5ed 277 u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg));
7d4df48e 278 u16 mimo_antsel = 0;
a9533e7e
HP
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 */
299f8a46 295static int wlc_antsel_cfgupd(struct antsel_info *asi, wlc_antselcfg_t *antsel)
a9533e7e 296{
c6a9e1fc 297 struct wlc_info *wlc = asi->wlc;
41feb5ed 298 u8 ant_cfg;
7d4df48e 299 u16 mimo_antsel;
a9533e7e
HP
300
301 ASSERT(asi->antsel_type != ANTSEL_NA);
302
303 /* 1) Update TX antconfig for all frames that are not unicast data
304 * (aka default TX)
305 */
306 ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
307 mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
308 wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
309 /* Update driver stats for currently selected default tx/rx antenna config */
310 asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
311
312 /* 2) Update RX antconfig for all frames that are not unicast data
313 * (aka default RX)
314 */
315 ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
316 mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg);
317 wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
318 /* Update driver stats for currently selected default tx/rx antenna config */
319 asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
320
321 return 0;
322}
This page took 0.12768 seconds and 5 git commands to generate.