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(struct antsel_info), 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