0001
0002
0003
0004
0005
0006
0007
0008
0009
0010
0011
0012
0013
0014
0015
0016
0017 #include <linux/slab.h>
0018 #include <net/mac80211.h>
0019
0020 #include "types.h"
0021 #include "main.h"
0022 #include "phy_shim.h"
0023 #include "antsel.h"
0024 #include "debug.h"
0025
0026 #define ANT_SELCFG_AUTO 0x80
0027 #define ANT_SELCFG_MASK 0x33
0028 #define ANT_SELCFG_TX_UNICAST 0
0029 #define ANT_SELCFG_RX_UNICAST 1
0030 #define ANT_SELCFG_TX_DEF 2
0031 #define ANT_SELCFG_RX_DEF 3
0032
0033
0034 #define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
0035 #define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
0036 #define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\
0037 (BRCMS_ANTSEL_11N_1(ant)))
0038 #define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
0039 #define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)
0040
0041
0042
0043 #define ANT_SELCFG_DEF_2x2 0x01
0044
0045
0046 #define ANT_SELCFG_NUM_2x3 3
0047 #define ANT_SELCFG_DEF_2x3 0x01
0048
0049
0050 #define ANT_SELCFG_NUM_2x4 4
0051 #define ANT_SELCFG_DEF_2x4 0x02
0052
0053 static const u16 mimo_2x4_div_antselpat_tbl[] = {
0054 0, 0, 0x9, 0xa,
0055 0, 0, 0x5, 0x6,
0056 0, 0, 0, 0,
0057 0, 0, 0, 0
0058 };
0059
0060 static const u8 mimo_2x4_div_antselid_tbl[16] = {
0061 0, 0, 0, 0, 0, 2, 3, 0,
0062 0, 0, 1, 0, 0, 0, 0, 0
0063 };
0064
0065 static const u16 mimo_2x3_div_antselpat_tbl[] = {
0066 16, 0, 1, 16,
0067 16, 16, 16, 16,
0068 16, 2, 16, 16,
0069 16, 16, 16, 16
0070 };
0071
0072 static const u8 mimo_2x3_div_antselid_tbl[16] = {
0073 0, 1, 2, 0, 0, 0, 0, 0,
0074 0, 0, 0, 0, 0, 0, 0, 0
0075 };
0076
0077
0078 static void
0079 brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,
0080 bool auto_sel)
0081 {
0082 if (asi->antsel_type == ANTSEL_2x3) {
0083 u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
0084 ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
0085 antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
0086 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
0087 antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
0088 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
0089 antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
0090
0091 } else if (asi->antsel_type == ANTSEL_2x4) {
0092
0093 antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
0094 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
0095 antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
0096 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
0097 antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
0098
0099 } else {
0100
0101 antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
0102 antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
0103 antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
0104 antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
0105 antsel->num_antcfg = 0;
0106 }
0107 }
0108
0109 struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)
0110 {
0111 struct antsel_info *asi;
0112 struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
0113
0114 asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
0115 if (!asi)
0116 return NULL;
0117
0118 asi->wlc = wlc;
0119 asi->pub = wlc->pub;
0120 asi->antsel_type = ANTSEL_NA;
0121 asi->antsel_avail = false;
0122 asi->antsel_antswitch = sprom->antswitch;
0123
0124 if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
0125 switch (asi->antsel_antswitch) {
0126 case ANTSWITCH_TYPE_1:
0127 case ANTSWITCH_TYPE_2:
0128 case ANTSWITCH_TYPE_3:
0129
0130 asi->antsel_type = ANTSEL_2x3;
0131
0132 if ((sprom->ant_available_bg == 7) ||
0133 (sprom->ant_available_a == 7)) {
0134 asi->antsel_avail = true;
0135 } else if (
0136 sprom->ant_available_bg == 3 ||
0137 sprom->ant_available_a == 3) {
0138 asi->antsel_avail = false;
0139 } else {
0140 asi->antsel_avail = false;
0141 brcms_err(wlc->hw->d11core,
0142 "antsel_attach: 2o3 "
0143 "board cfg invalid\n");
0144 }
0145
0146 break;
0147 default:
0148 break;
0149 }
0150 } else if ((asi->pub->sromrev == 4) &&
0151 (sprom->ant_available_bg == 7) &&
0152 (sprom->ant_available_a == 0)) {
0153
0154 asi->antsel_type = ANTSEL_2x3;
0155 asi->antsel_avail = true;
0156 } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
0157 asi->antsel_type = ANTSEL_2x4;
0158 asi->antsel_avail = true;
0159 }
0160
0161
0162 brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);
0163
0164
0165 brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);
0166 brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);
0167
0168 return asi;
0169 }
0170
0171 void brcms_c_antsel_detach(struct antsel_info *asi)
0172 {
0173 kfree(asi);
0174 }
0175
0176
0177
0178
0179
0180 static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
0181 {
0182 u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));
0183 u16 mimo_antsel = 0;
0184
0185 if (asi->antsel_type == ANTSEL_2x4) {
0186
0187 mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
0188 return mimo_antsel;
0189
0190 } else if (asi->antsel_type == ANTSEL_2x3) {
0191
0192 mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
0193 return mimo_antsel;
0194 }
0195
0196 return mimo_antsel;
0197 }
0198
0199
0200 static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
0201 struct brcms_antselcfg *antsel)
0202 {
0203 struct brcms_c_info *wlc = asi->wlc;
0204 u8 ant_cfg;
0205 u16 mimo_antsel;
0206
0207
0208
0209
0210 ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
0211 mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
0212 brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
0213
0214
0215
0216
0217 asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
0218
0219
0220
0221
0222 ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
0223 mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
0224 brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
0225
0226
0227
0228
0229 asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
0230
0231 return 0;
0232 }
0233
0234 void brcms_c_antsel_init(struct antsel_info *asi)
0235 {
0236 if ((asi->antsel_type == ANTSEL_2x3) ||
0237 (asi->antsel_type == ANTSEL_2x4))
0238 brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);
0239 }
0240
0241
0242 static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)
0243 {
0244 u8 antcfg = ANT_SELCFG_DEF_2x2;
0245
0246 if (asi->antsel_type == ANTSEL_2x4) {
0247
0248 antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
0249 return antcfg;
0250
0251 } else if (asi->antsel_type == ANTSEL_2x3) {
0252
0253 antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
0254 return antcfg;
0255 }
0256
0257 return antcfg;
0258 }
0259
0260 void
0261 brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
0262 u8 antselid, u8 fbantselid, u8 *antcfg,
0263 u8 *fbantcfg)
0264 {
0265 u8 ant;
0266
0267
0268 if (usedef) {
0269 *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
0270 *fbantcfg = *antcfg;
0271 return;
0272 }
0273
0274 if (!sel) {
0275 *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0276 *fbantcfg = *antcfg;
0277
0278 } else {
0279 ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0280 if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
0281 *antcfg = brcms_c_antsel_id2antcfg(asi, antselid);
0282 *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);
0283 } else {
0284 *antcfg =
0285 asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0286 *fbantcfg = *antcfg;
0287 }
0288 }
0289 return;
0290 }
0291
0292
0293 u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
0294 {
0295 u8 antselid = 0;
0296
0297 if (asi->antsel_type == ANTSEL_2x4) {
0298
0299 antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
0300 return antselid;
0301
0302 } else if (asi->antsel_type == ANTSEL_2x3) {
0303
0304 antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
0305 return antselid;
0306 }
0307
0308 return antselid;
0309 }