Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 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> |
| 22 | #include <qmath.h> |
| 23 | #include <bcmdefs.h> |
| 24 | #include <osl.h> |
| 25 | #include <bcmutils.h> |
| 26 | #include <siutils.h> |
| 27 | #include <bcmendian.h> |
| 28 | #include <wlioctl.h> |
| 29 | #include <wlc_phy_radio.h> |
| 30 | #include <bitfuncs.h> |
| 31 | |
| 32 | #include <proto/802.11.h> |
| 33 | #include <d11.h> |
| 34 | #include <wlc_rate.h> |
| 35 | #include <wlc_key.h> |
| 36 | #include <wlc_pub.h> |
| 37 | #include <wlc_bsscfg.h> |
| 38 | #include <wl_dbg.h> |
| 39 | #include <wlc_mac80211.h> |
| 40 | #include <wlc_bmac.h> |
| 41 | #include <wlc_phy_hal.h> |
| 42 | #include <wl_export.h> |
| 43 | #include <wlc_antsel.h> |
| 44 | #include <wlc_phy_shim.h> |
| 45 | |
| 46 | /* useful macros */ |
| 47 | #define WLC_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf) |
| 48 | #define WLC_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf) |
| 49 | #define WLC_ANTIDX_11N(ant) (((WLC_ANTSEL_11N_0(ant)) << 2) + (WLC_ANTSEL_11N_1(ant))) |
| 50 | #define WLC_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) |
| 51 | #define WLC_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK) |
| 52 | |
| 53 | /* antenna switch */ |
| 54 | /* defines for no boardlevel antenna diversity */ |
| 55 | #define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */ |
| 56 | |
| 57 | /* 2x3 antdiv defines and tables for GPIO communication */ |
| 58 | #define ANT_SELCFG_NUM_2x3 3 |
| 59 | #define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */ |
| 60 | |
| 61 | /* 2x4 antdiv rev4 defines and tables for GPIO communication */ |
| 62 | #define ANT_SELCFG_NUM_2x4 4 |
| 63 | #define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */ |
| 64 | |
| 65 | /* static functions */ |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 66 | static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel); |
| 67 | static uint8 wlc_antsel_id2antcfg(antsel_info_t *asi, uint8 id); |
| 68 | static uint16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, uint8 ant_cfg); |
| 69 | static void wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel, |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 70 | bool auto_sel); |
| 71 | |
| 72 | const uint16 mimo_2x4_div_antselpat_tbl[] = { |
| 73 | 0, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */ |
| 74 | 0, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */ |
| 75 | 0, 0, 0, 0, /* n.a. */ |
| 76 | 0, 0, 0, 0 /* n.a. */ |
| 77 | }; |
| 78 | |
| 79 | const uint8 mimo_2x4_div_antselid_tbl[16] = { |
| 80 | 0, 0, 0, 0, 0, 2, 3, 0, |
| 81 | 0, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */ |
| 82 | }; |
| 83 | |
| 84 | const uint16 mimo_2x3_div_antselpat_tbl[] = { |
| 85 | 16, 0, 1, 16, /* ant0: 0 ant1: 1,2 */ |
| 86 | 16, 16, 16, 16, /* n.a. */ |
| 87 | 16, 2, 16, 16, /* ant0: 2 ant1: 1 */ |
| 88 | 16, 16, 16, 16 /* n.a. */ |
| 89 | }; |
| 90 | |
| 91 | const uint8 mimo_2x3_div_antselid_tbl[16] = { |
| 92 | 0, 1, 2, 0, 0, 0, 0, 0, |
| 93 | 0, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */ |
| 94 | }; |
| 95 | |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 96 | antsel_info_t *BCMNMIATTACHFN(wlc_antsel_attach) (wlc_info_t *wlc, osl_t *osh, |
| 97 | wlc_pub_t *pub, |
| 98 | wlc_hw_info_t *wlc_hw) { |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 99 | antsel_info_t *asi; |
| 100 | |
Jason Cooper | ca8c1e5 | 2010-09-14 09:45:33 -0400 | [diff] [blame^] | 101 | asi = (antsel_info_t *) MALLOC(osh, sizeof(antsel_info_t)); |
| 102 | if (!asi) { |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 103 | WL_ERROR(("wl%d: wlc_antsel_attach: out of mem, malloced %d bytes\n", pub->unit, MALLOCED(osh))); |
| 104 | return NULL; |
| 105 | } |
| 106 | |
| 107 | bzero((char *)asi, sizeof(antsel_info_t)); |
| 108 | |
| 109 | asi->wlc = wlc; |
| 110 | asi->pub = pub; |
| 111 | asi->antsel_type = ANTSEL_NA; |
| 112 | asi->antsel_avail = FALSE; |
| 113 | asi->antsel_antswitch = (uint8) getintvar(asi->pub->vars, "antswitch"); |
| 114 | |
| 115 | if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) { |
| 116 | switch (asi->antsel_antswitch) { |
| 117 | case ANTSWITCH_TYPE_1: |
| 118 | case ANTSWITCH_TYPE_2: |
| 119 | case ANTSWITCH_TYPE_3: |
| 120 | /* 4321/2 board with 2x3 switch logic */ |
| 121 | asi->antsel_type = ANTSEL_2x3; |
| 122 | /* Antenna selection availability */ |
| 123 | if (((uint16) getintvar(asi->pub->vars, "aa2g") == 7) || |
| 124 | ((uint16) getintvar(asi->pub->vars, "aa5g") == 7)) { |
| 125 | asi->antsel_avail = TRUE; |
| 126 | } else |
| 127 | if (((uint16) getintvar(asi->pub->vars, "aa2g") == |
| 128 | 3) |
| 129 | || ((uint16) getintvar(asi->pub->vars, "aa5g") |
| 130 | == 3)) { |
| 131 | asi->antsel_avail = FALSE; |
| 132 | } else { |
| 133 | asi->antsel_avail = FALSE; |
| 134 | WL_ERROR(("wlc_antsel_attach: 2o3 board cfg invalid\n")); |
| 135 | ASSERT(0); |
| 136 | } |
| 137 | break; |
| 138 | default: |
| 139 | break; |
| 140 | } |
| 141 | } else if ((asi->pub->sromrev == 4) && |
| 142 | ((uint16) getintvar(asi->pub->vars, "aa2g") == 7) && |
| 143 | ((uint16) getintvar(asi->pub->vars, "aa5g") == 0)) { |
| 144 | /* hack to match old 4321CB2 cards with 2of3 antenna switch */ |
| 145 | asi->antsel_type = ANTSEL_2x3; |
| 146 | asi->antsel_avail = TRUE; |
| 147 | } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) { |
| 148 | asi->antsel_type = ANTSEL_2x4; |
| 149 | asi->antsel_avail = TRUE; |
| 150 | } |
| 151 | |
| 152 | /* Set the antenna selection type for the low driver */ |
| 153 | wlc_bmac_antsel_type_set(wlc_hw, asi->antsel_type); |
| 154 | |
| 155 | /* Init (auto/manual) antenna selection */ |
| 156 | wlc_antsel_init_cfg(asi, &asi->antcfg_11n, TRUE); |
| 157 | wlc_antsel_init_cfg(asi, &asi->antcfg_cur, TRUE); |
| 158 | |
| 159 | return asi; |
| 160 | } |
| 161 | |
Jason Cooper | a2627bc | 2010-09-14 09:45:31 -0400 | [diff] [blame] | 162 | void BCMATTACHFN(wlc_antsel_detach) (antsel_info_t *asi) |
| 163 | { |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 164 | if (!asi) |
| 165 | return; |
| 166 | |
| 167 | MFREE(asi->pub->osh, asi, sizeof(antsel_info_t)); |
| 168 | } |
| 169 | |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 170 | void wlc_antsel_init(antsel_info_t *asi) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 171 | { |
| 172 | if ((asi->antsel_type == ANTSEL_2x3) || |
| 173 | (asi->antsel_type == ANTSEL_2x4)) |
| 174 | wlc_antsel_cfgupd(asi, &asi->antcfg_11n); |
| 175 | } |
| 176 | |
| 177 | /* boardlevel antenna selection: init antenna selection structure */ |
| 178 | static void |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 179 | wlc_antsel_init_cfg(antsel_info_t *asi, wlc_antselcfg_t *antsel, |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 180 | bool auto_sel) |
| 181 | { |
| 182 | if (asi->antsel_type == ANTSEL_2x3) { |
| 183 | uint8 antcfg_def = ANT_SELCFG_DEF_2x3 | |
| 184 | ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0); |
| 185 | antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def; |
| 186 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def; |
| 187 | antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def; |
| 188 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def; |
| 189 | antsel->num_antcfg = ANT_SELCFG_NUM_2x3; |
| 190 | |
| 191 | } else if (asi->antsel_type == ANTSEL_2x4) { |
| 192 | |
| 193 | antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4; |
| 194 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4; |
| 195 | antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4; |
| 196 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4; |
| 197 | antsel->num_antcfg = ANT_SELCFG_NUM_2x4; |
| 198 | |
| 199 | } else { /* no antenna selection available */ |
| 200 | |
| 201 | antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2; |
| 202 | antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2; |
| 203 | antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2; |
| 204 | antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2; |
| 205 | antsel->num_antcfg = 0; |
| 206 | } |
| 207 | } |
| 208 | |
| 209 | void BCMFASTPATH |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 210 | wlc_antsel_antcfg_get(antsel_info_t *asi, bool usedef, bool sel, |
| 211 | uint8 antselid, uint8 fbantselid, uint8 *antcfg, |
| 212 | uint8 *fbantcfg) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 213 | { |
| 214 | uint8 ant; |
| 215 | |
| 216 | /* if use default, assign it and return */ |
| 217 | if (usedef) { |
| 218 | *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF]; |
| 219 | *fbantcfg = *antcfg; |
| 220 | return; |
| 221 | } |
| 222 | |
| 223 | if (!sel) { |
| 224 | *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; |
| 225 | *fbantcfg = *antcfg; |
| 226 | |
| 227 | } else { |
| 228 | ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; |
| 229 | if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) { |
| 230 | *antcfg = wlc_antsel_id2antcfg(asi, antselid); |
| 231 | *fbantcfg = wlc_antsel_id2antcfg(asi, fbantselid); |
| 232 | } else { |
| 233 | *antcfg = |
| 234 | asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST]; |
| 235 | *fbantcfg = *antcfg; |
| 236 | } |
| 237 | } |
| 238 | return; |
| 239 | } |
| 240 | |
| 241 | /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */ |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 242 | uint8 wlc_antsel_antsel2id(antsel_info_t *asi, uint16 antsel) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 243 | { |
| 244 | uint8 antselid = 0; |
| 245 | |
| 246 | if (asi->antsel_type == ANTSEL_2x4) { |
| 247 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ |
| 248 | antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)]; |
| 249 | return antselid; |
| 250 | |
| 251 | } else if (asi->antsel_type == ANTSEL_2x3) { |
| 252 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ |
| 253 | antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)]; |
| 254 | return antselid; |
| 255 | } |
| 256 | |
| 257 | return antselid; |
| 258 | } |
| 259 | |
| 260 | /* boardlevel antenna selection: convert id to ant_cfg */ |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 261 | static uint8 wlc_antsel_id2antcfg(antsel_info_t *asi, uint8 id) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 262 | { |
| 263 | uint8 antcfg = ANT_SELCFG_DEF_2x2; |
| 264 | |
| 265 | if (asi->antsel_type == ANTSEL_2x4) { |
| 266 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ |
| 267 | antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2)); |
| 268 | return antcfg; |
| 269 | |
| 270 | } else if (asi->antsel_type == ANTSEL_2x3) { |
| 271 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ |
| 272 | antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1)); |
| 273 | return antcfg; |
| 274 | } |
| 275 | |
| 276 | return antcfg; |
| 277 | } |
| 278 | |
| 279 | /* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */ |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 280 | static uint16 wlc_antsel_antcfg2antsel(antsel_info_t *asi, uint8 ant_cfg) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 281 | { |
| 282 | uint8 idx = WLC_ANTIDX_11N(WLC_ANTSEL_11N(ant_cfg)); |
| 283 | uint16 mimo_antsel = 0; |
| 284 | |
| 285 | if (asi->antsel_type == ANTSEL_2x4) { |
| 286 | /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */ |
| 287 | mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf); |
| 288 | return mimo_antsel; |
| 289 | |
| 290 | } else if (asi->antsel_type == ANTSEL_2x3) { |
| 291 | /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */ |
| 292 | mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf); |
| 293 | return mimo_antsel; |
| 294 | } |
| 295 | |
| 296 | return mimo_antsel; |
| 297 | } |
| 298 | |
| 299 | /* boardlevel antenna selection: ucode interface control */ |
Jason Cooper | 7cc4a4c | 2010-09-14 09:45:30 -0400 | [diff] [blame] | 300 | static int wlc_antsel_cfgupd(antsel_info_t *asi, wlc_antselcfg_t *antsel) |
Henry Ptasinski | a9533e7 | 2010-09-08 21:04:42 -0700 | [diff] [blame] | 301 | { |
| 302 | wlc_info_t *wlc = asi->wlc; |
| 303 | uint8 ant_cfg; |
| 304 | uint16 mimo_antsel; |
| 305 | |
| 306 | ASSERT(asi->antsel_type != ANTSEL_NA); |
| 307 | |
| 308 | /* 1) Update TX antconfig for all frames that are not unicast data |
| 309 | * (aka default TX) |
| 310 | */ |
| 311 | ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF]; |
| 312 | mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg); |
| 313 | wlc_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel); |
| 314 | /* Update driver stats for currently selected default tx/rx antenna config */ |
| 315 | asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg; |
| 316 | |
| 317 | /* 2) Update RX antconfig for all frames that are not unicast data |
| 318 | * (aka default RX) |
| 319 | */ |
| 320 | ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF]; |
| 321 | mimo_antsel = wlc_antsel_antcfg2antsel(asi, ant_cfg); |
| 322 | wlc_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel); |
| 323 | /* Update driver stats for currently selected default tx/rx antenna config */ |
| 324 | asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg; |
| 325 | |
| 326 | return 0; |
| 327 | } |
| 328 | |
| 329 | #endif /* WLANTSEL */ |