310 lines · c
1/*2 * Copyright (c) 2010 Broadcom Corporation3 *4 * Permission to use, copy, modify, and/or distribute this software for any5 * purpose with or without fee is hereby granted, provided that the above6 * copyright notice and this permission notice appear in all copies.7 *8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN14 * 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 347#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 451#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */52 53static 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 60static 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 65static 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 72static 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 */78static void79brcms_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 109struct 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 171void 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 */180static 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 */200static 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 data208 * (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 selected215 * default tx/rx antenna config216 */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 data220 * (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 selected227 * default tx/rx antenna config228 */229 asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;230 231 return 0;232}233 234void 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 */242static 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 260void261brcms_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 */293u8 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