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 | ||
a9d0fffa | 92 | antsel_info_t *wlc_antsel_attach(wlc_info_t *wlc, osl_t *osh, |
7cc4a4c0 JC |
93 | wlc_pub_t *pub, |
94 | wlc_hw_info_t *wlc_hw) { | |
a9533e7e HP |
95 | antsel_info_t *asi; |
96 | ||
5fcc1fcb | 97 | asi = kzalloc(sizeof(antsel_info_t), GFP_ATOMIC); |
ca8c1e59 | 98 | if (!asi) { |
97e17d0e | 99 | WL_ERROR(("wl%d: wlc_antsel_attach: out of mem\n", pub->unit)); |
a9533e7e HP |
100 | return NULL; |
101 | } | |
102 | ||
a9533e7e HP |
103 | asi->wlc = wlc; |
104 | asi->pub = pub; | |
105 | asi->antsel_type = ANTSEL_NA; | |
0965ae88 | 106 | asi->antsel_avail = false; |
41feb5ed | 107 | asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch"); |
a9533e7e HP |
108 | |
109 | if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) { | |
110 | switch (asi->antsel_antswitch) { | |
111 | case ANTSWITCH_TYPE_1: | |
112 | case ANTSWITCH_TYPE_2: | |
113 | case ANTSWITCH_TYPE_3: | |
114 | /* 4321/2 board with 2x3 switch logic */ | |
115 | asi->antsel_type = ANTSEL_2x3; | |
116 | /* Antenna selection availability */ | |
7d4df48e GKH |
117 | if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) || |
118 | ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) { | |
0f0881b0 | 119 | asi->antsel_avail = true; |
a9533e7e | 120 | } else |
7d4df48e | 121 | if (((u16) getintvar(asi->pub->vars, "aa2g") == |
a9533e7e | 122 | 3) |
7d4df48e | 123 | || ((u16) getintvar(asi->pub->vars, "aa5g") |
a9533e7e | 124 | == 3)) { |
0965ae88 | 125 | asi->antsel_avail = false; |
a9533e7e | 126 | } else { |
0965ae88 | 127 | asi->antsel_avail = false; |
a9533e7e HP |
128 | WL_ERROR(("wlc_antsel_attach: 2o3 board cfg invalid\n")); |
129 | ASSERT(0); | |
130 | } | |
131 | break; | |
132 | default: | |
133 | break; | |
134 | } | |
135 | } else if ((asi->pub->sromrev == 4) && | |
7d4df48e GKH |
136 | ((u16) getintvar(asi->pub->vars, "aa2g") == 7) && |
137 | ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) { | |
a9533e7e HP |
138 | /* hack to match old 4321CB2 cards with 2of3 antenna switch */ |
139 | asi->antsel_type = ANTSEL_2x3; | |
0f0881b0 | 140 | asi->antsel_avail = true; |
a9533e7e HP |
141 | } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) { |
142 | asi->antsel_type = ANTSEL_2x4; | |
0f0881b0 | 143 | asi->antsel_avail = true; |
a9533e7e HP |
144 | } |
145 | ||
146 | /* Set the antenna selection type for the low driver */ | |
147 | wlc_bmac_antsel_type_set(wlc_hw, asi->antsel_type); | |
148 | ||
149 | /* Init (auto/manual) antenna selection */ | |
0f0881b0 GKH |
150 | wlc_antsel_init_cfg(asi, &asi->antcfg_11n, true); |
151 | wlc_antsel_init_cfg(asi, &asi->antcfg_cur, true); | |
a9533e7e HP |
152 | |
153 | return asi; | |
154 | } | |
155 | ||
0d2f0724 | 156 | void wlc_antsel_detach(antsel_info_t *asi) |
a2627bc0 | 157 | { |
a9533e7e HP |
158 | if (!asi) |
159 | return; | |
160 | ||
161 | MFREE(asi->pub->osh, asi, sizeof(antsel_info_t)); | |
162 | } | |
163 | ||
7cc4a4c0 | 164 | void wlc_antsel_init(antsel_info_t *asi) |
a9533e7e HP |
165 | { |
166 | if ((asi->antsel_type == ANTSEL_2x3) || | |
167 | (asi->antsel_type == ANTSEL_2x4)) | |
168 | wlc_antsel_cfgupd(asi, &asi->antcfg_11n); | |
169 | } | |
170 | ||
171 | /* boardlevel antenna selection: init antenna selection structure */ | |
172 | static void | |
7cc4a4c0 | 173 | wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel, |
a9533e7e HP |
174 | bool auto_sel) |
175 | { | |
176 | if (asi->antsel_type == ANTSEL_2x3) { | |
41feb5ed | 177 | u8 antcfg_def = ANT_SELCFG_DEF_2x3 | |
a9533e7e HP |
178 | ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0); |
179 | antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def; | |
180 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def; | |
181 | antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def; | |
182 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def; | |
183 | antsel->num_antcfg = ANT_SELCFG_NUM_2x3; | |
184 | ||
185 | } else if (asi->antsel_type == ANTSEL_2x4) { | |
186 | ||
187 | antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4; | |
188 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4; | |
189 | antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4; | |
190 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4; | |
191 | antsel->num_antcfg = ANT_SELCFG_NUM_2x4; | |
192 | ||
193 | } else { /* no antenna selection available */ | |
194 | ||
195 | antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2; | |
196 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2; | |
197 | antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2; | |
198 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2; | |
199 | antsel->num_antcfg = 0; | |
200 | } | |
201 | } | |
202 | ||
203 | void BCMFASTPATH | |
7cc4a4c0 | 204 | wlc_antsel_antcfg_get(antsel_info_t *asi, bool usedef, bool sel, |
41feb5ed GKH |
205 | u8 antselid, u8 fbantselid, u8 *antcfg, |
206 | u8 *fbantcfg) | |
a9533e7e | 207 | { |
41feb5ed | 208 | u8 ant; |
a9533e7e HP |
209 | |
210 | /* if use default, assign it and return */ | |
211 | if (usedef) { | |
212 | *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF]; | |
213 | *fbantcfg = *antcfg; | |
214 | return; | |
215 | } | |
216 | ||
217 | if (!sel) { | |
218 | *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; | |
219 | *fbantcfg = *antcfg; | |
220 | ||
221 | } else { | |
222 | ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; | |
223 | if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) { | |
224 | *antcfg = wlc_antsel_id2antcfg(asi, antselid); | |
225 | *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid); | |
226 | } else { | |
227 | *antcfg = | |
228 | asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; | |
229 | *fbantcfg = *antcfg; | |
230 | } | |
231 | } | |
232 | return; | |
233 | } | |
234 | ||
235 | /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */ | |
7d4df48e | 236 | u8 wlc_antsel_antsel2id(antsel_info_t *asi, u16 antsel) |
a9533e7e | 237 | { |
41feb5ed | 238 | u8 antselid = 0; |
a9533e7e HP |
239 | |
240 | if (asi->antsel_type == ANTSEL_2x4) { | |
241 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ | |
242 | antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)]; | |
243 | return antselid; | |
244 | ||
245 | } else if (asi->antsel_type == ANTSEL_2x3) { | |
246 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ | |
247 | antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)]; | |
248 | return antselid; | |
249 | } | |
250 | ||
251 | return antselid; | |
252 | } | |
253 | ||
254 | /* boardlevel antenna selection: convert id to ant_cfg */ | |
41feb5ed | 255 | static u8 wlc_antsel_id2antcfg(antsel_info_t *asi, u8 id) |
a9533e7e | 256 | { |
41feb5ed | 257 | u8 antcfg = ANT_SELCFG_DEF_2x2; |
a9533e7e HP |
258 | |
259 | if (asi->antsel_type == ANTSEL_2x4) { | |
260 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ | |
261 | antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2)); | |
262 | return antcfg; | |
263 | ||
264 | } else if (asi->antsel_type == ANTSEL_2x3) { | |
265 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ | |
266 | antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1)); | |
267 | return antcfg; | |
268 | } | |
269 | ||
270 | return antcfg; | |
271 | } | |
272 | ||
273 | /* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */ | |
7d4df48e | 274 | static u16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, u8 ant_cfg) |
a9533e7e | 275 | { |
41feb5ed | 276 | u8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg)); |
7d4df48e | 277 | u16 mimo_antsel = 0; |
a9533e7e HP |
278 | |
279 | if (asi->antsel_type == ANTSEL_2x4) { | |
280 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ | |
281 | mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf); | |
282 | return mimo_antsel; | |
283 | ||
284 | } else if (asi->antsel_type == ANTSEL_2x3) { | |
285 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ | |
286 | mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf); | |
287 | return mimo_antsel; | |
288 | } | |
289 | ||
290 | return mimo_antsel; | |
291 | } | |
292 | ||
293 | /* boardlevel antenna selection: ucode interface control */ | |
7cc4a4c0 | 294 | static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel) |
a9533e7e HP |
295 | { |
296 | wlc_info_t *wlc = asi->wlc; | |
41feb5ed | 297 | u8 ant_cfg; |
7d4df48e | 298 | u16 mimo_antsel; |
a9533e7e HP |
299 | |
300 | ASSERT(asi->antsel_type != ANTSEL_NA); | |
301 | ||
302 | /* 1) Update TX antconfig for all frames that are not unicast data | |
303 | * (aka default TX) | |
304 | */ | |
305 | ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF]; | |
306 | mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg); | |
307 | wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel); | |
308 | /* Update driver stats for currently selected default tx/rx antenna config */ | |
309 | asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg; | |
310 | ||
311 | /* 2) Update RX antconfig for all frames that are not unicast data | |
312 | * (aka default RX) | |
313 | */ | |
314 | ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF]; | |
315 | mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg); | |
316 | wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel); | |
317 | /* Update driver stats for currently selected default tx/rx antenna config */ | |
318 | asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg; | |
319 | ||
320 | return 0; | |
321 | } | |
322 | ||
323 | #endif /* WLANTSEL */ |