Path: blob/main/sys/contrib/dev/broadcom/brcm80211/brcmsmac/antsel.c
178665 views
/*1* Copyright (c) 2010 Broadcom Corporation2*3* Permission to use, copy, modify, and/or distribute this software for any4* purpose with or without fee is hereby granted, provided that the above5* copyright notice and this permission notice appear in all copies.6*7* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES8* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF9* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY10* SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES11* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION12* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN13* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.14*/1516#include <linux/slab.h>17#include <net/mac80211.h>1819#include "types.h"20#include "main.h"21#include "phy_shim.h"22#include "antsel.h"23#include "debug.h"2425#define ANT_SELCFG_AUTO 0x80 /* bit indicates antenna sel AUTO */26#define ANT_SELCFG_MASK 0x33 /* antenna configuration mask */27#define ANT_SELCFG_TX_UNICAST 0 /* unicast tx antenna configuration */28#define ANT_SELCFG_RX_UNICAST 1 /* unicast rx antenna configuration */29#define ANT_SELCFG_TX_DEF 2 /* default tx antenna configuration */30#define ANT_SELCFG_RX_DEF 3 /* default rx antenna configuration */3132/* useful macros */33#define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)34#define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)35#define BRCMS_ANTIDX_11N(ant) (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\36(BRCMS_ANTSEL_11N_1(ant)))37#define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)38#define BRCMS_ANTSEL_11N(ant) ((ant) & ANT_SELCFG_MASK)3940/* antenna switch */41/* defines for no boardlevel antenna diversity */42#define ANT_SELCFG_DEF_2x2 0x01 /* default antenna configuration */4344/* 2x3 antdiv defines and tables for GPIO communication */45#define ANT_SELCFG_NUM_2x3 346#define ANT_SELCFG_DEF_2x3 0x01 /* default antenna configuration */4748/* 2x4 antdiv rev4 defines and tables for GPIO communication */49#define ANT_SELCFG_NUM_2x4 450#define ANT_SELCFG_DEF_2x4 0x02 /* default antenna configuration */5152static const u16 mimo_2x4_div_antselpat_tbl[] = {530, 0, 0x9, 0xa, /* ant0: 0 ant1: 2,3 */540, 0, 0x5, 0x6, /* ant0: 1 ant1: 2,3 */550, 0, 0, 0, /* n.a. */560, 0, 0, 0 /* n.a. */57};5859static const u8 mimo_2x4_div_antselid_tbl[16] = {600, 0, 0, 0, 0, 2, 3, 0,610, 0, 1, 0, 0, 0, 0, 0 /* pat to antselid */62};6364static const u16 mimo_2x3_div_antselpat_tbl[] = {6516, 0, 1, 16, /* ant0: 0 ant1: 1,2 */6616, 16, 16, 16, /* n.a. */6716, 2, 16, 16, /* ant0: 2 ant1: 1 */6816, 16, 16, 16 /* n.a. */69};7071static const u8 mimo_2x3_div_antselid_tbl[16] = {720, 1, 2, 0, 0, 0, 0, 0,730, 0, 0, 0, 0, 0, 0, 0 /* pat to antselid */74};7576/* boardlevel antenna selection: init antenna selection structure */77static void78brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,79bool auto_sel)80{81if (asi->antsel_type == ANTSEL_2x3) {82u8 antcfg_def = ANT_SELCFG_DEF_2x3 |83((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);84antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;85antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;86antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;87antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;88antsel->num_antcfg = ANT_SELCFG_NUM_2x3;8990} else if (asi->antsel_type == ANTSEL_2x4) {9192antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;93antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;94antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;95antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;96antsel->num_antcfg = ANT_SELCFG_NUM_2x4;9798} else { /* no antenna selection available */99100antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;101antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;102antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;103antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;104antsel->num_antcfg = 0;105}106}107108struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)109{110struct antsel_info *asi;111struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;112113asi = kzalloc(sizeof(*asi), GFP_ATOMIC);114if (!asi)115return NULL;116117asi->wlc = wlc;118asi->pub = wlc->pub;119asi->antsel_type = ANTSEL_NA;120asi->antsel_avail = false;121asi->antsel_antswitch = sprom->antswitch;122123if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {124switch (asi->antsel_antswitch) {125case ANTSWITCH_TYPE_1:126case ANTSWITCH_TYPE_2:127case ANTSWITCH_TYPE_3:128/* 4321/2 board with 2x3 switch logic */129asi->antsel_type = ANTSEL_2x3;130/* Antenna selection availability */131if ((sprom->ant_available_bg == 7) ||132(sprom->ant_available_a == 7)) {133asi->antsel_avail = true;134} else if (135sprom->ant_available_bg == 3 ||136sprom->ant_available_a == 3) {137asi->antsel_avail = false;138} else {139asi->antsel_avail = false;140brcms_err(wlc->hw->d11core,141"antsel_attach: 2o3 "142"board cfg invalid\n");143}144145break;146default:147break;148}149} else if ((asi->pub->sromrev == 4) &&150(sprom->ant_available_bg == 7) &&151(sprom->ant_available_a == 0)) {152/* hack to match old 4321CB2 cards with 2of3 antenna switch */153asi->antsel_type = ANTSEL_2x3;154asi->antsel_avail = true;155} else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {156asi->antsel_type = ANTSEL_2x4;157asi->antsel_avail = true;158}159160/* Set the antenna selection type for the low driver */161brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);162163/* Init (auto/manual) antenna selection */164brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);165brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);166167return asi;168}169170void brcms_c_antsel_detach(struct antsel_info *asi)171{172kfree(asi);173}174175/*176* boardlevel antenna selection:177* convert ant_cfg to mimo_antsel (ucode interface)178*/179static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)180{181u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));182u16 mimo_antsel = 0;183184if (asi->antsel_type == ANTSEL_2x4) {185/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */186mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);187return mimo_antsel;188189} else if (asi->antsel_type == ANTSEL_2x3) {190/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */191mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);192return mimo_antsel;193}194195return mimo_antsel;196}197198/* boardlevel antenna selection: ucode interface control */199static int brcms_c_antsel_cfgupd(struct antsel_info *asi,200struct brcms_antselcfg *antsel)201{202struct brcms_c_info *wlc = asi->wlc;203u8 ant_cfg;204u16 mimo_antsel;205206/* 1) Update TX antconfig for all frames that are not unicast data207* (aka default TX)208*/209ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];210mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);211brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);212/*213* Update driver stats for currently selected214* default tx/rx antenna config215*/216asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;217218/* 2) Update RX antconfig for all frames that are not unicast data219* (aka default RX)220*/221ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];222mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);223brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);224/*225* Update driver stats for currently selected226* default tx/rx antenna config227*/228asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;229230return 0;231}232233void brcms_c_antsel_init(struct antsel_info *asi)234{235if ((asi->antsel_type == ANTSEL_2x3) ||236(asi->antsel_type == ANTSEL_2x4))237brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);238}239240/* boardlevel antenna selection: convert id to ant_cfg */241static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)242{243u8 antcfg = ANT_SELCFG_DEF_2x2;244245if (asi->antsel_type == ANTSEL_2x4) {246/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */247antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));248return antcfg;249250} else if (asi->antsel_type == ANTSEL_2x3) {251/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */252antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));253return antcfg;254}255256return antcfg;257}258259void260brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,261u8 antselid, u8 fbantselid, u8 *antcfg,262u8 *fbantcfg)263{264u8 ant;265266/* if use default, assign it and return */267if (usedef) {268*antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];269*fbantcfg = *antcfg;270return;271}272273if (!sel) {274*antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];275*fbantcfg = *antcfg;276277} else {278ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];279if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {280*antcfg = brcms_c_antsel_id2antcfg(asi, antselid);281*fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);282} else {283*antcfg =284asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];285*fbantcfg = *antcfg;286}287}288return;289}290291/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */292u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)293{294u8 antselid = 0;295296if (asi->antsel_type == ANTSEL_2x4) {297/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */298antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];299return antselid;300301} else if (asi->antsel_type == ANTSEL_2x3) {302/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */303antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];304return antselid;305}306307return antselid;308}309310311