cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

antsel.c (9668B)


      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
     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 void
     79brcms_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(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
    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 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
    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
    260void
    261brcms_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}