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