linux/drivers/net/wireless/broadcom/brcm80211/brcmsmac/antsel.c
<<
>>
Prefs
   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}
 310