]>
Commit | Line | Data |
---|---|---|
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 | 62 | static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel); |
41feb5ed | 63 | static u8 wlc_antsel_id2antcfg(antsel_info_t *asi, u8 id); |
7d4df48e | 64 | static u16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, u8 ant_cfg); |
7cc4a4c0 | 65 | static void wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel, |
a9533e7e HP |
66 | bool auto_sel); |
67 | ||
7d4df48e | 68 | const 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 | 75 | const 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 | 80 | const 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 | 87 | const 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 |
92 | antsel_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 | 158 | void 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 | 166 | void 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 */ | |
174 | static void | |
7cc4a4c0 | 175 | wlc_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 | ||
205 | void BCMFASTPATH | |
7cc4a4c0 | 206 | wlc_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 | 238 | u8 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 | 257 | static 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 | 276 | static 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 | 296 | static 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 */ |