Back to home page

OSCL-LXR

 
 

    


0001 /*
0002  * Copyright (c) 2010 Broadcom Corporation
0003  *
0004  * Permission to use, copy, modify, and/or distribute this software for any
0005  * purpose with or without fee is hereby granted, provided that the above
0006  * copyright notice and this permission notice appear in all copies.
0007  *
0008  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
0009  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
0010  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
0011  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
0012  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
0013  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
0014  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
0015  */
0016 
0017 #include <linux/slab.h>
0018 #include <net/mac80211.h>
0019 
0020 #include "types.h"
0021 #include "main.h"
0022 #include "phy_shim.h"
0023 #include "antsel.h"
0024 #include "debug.h"
0025 
0026 #define ANT_SELCFG_AUTO     0x80    /* bit indicates antenna sel AUTO */
0027 #define ANT_SELCFG_MASK     0x33    /* antenna configuration mask */
0028 #define ANT_SELCFG_TX_UNICAST   0   /* unicast tx antenna configuration */
0029 #define ANT_SELCFG_RX_UNICAST   1   /* unicast rx antenna configuration */
0030 #define ANT_SELCFG_TX_DEF   2   /* default tx antenna configuration */
0031 #define ANT_SELCFG_RX_DEF   3   /* default rx antenna configuration */
0032 
0033 /* useful macros */
0034 #define BRCMS_ANTSEL_11N_0(ant) ((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
0035 #define BRCMS_ANTSEL_11N_1(ant) (((ant) & ANT_SELCFG_MASK) & 0xf)
0036 #define BRCMS_ANTIDX_11N(ant)   (((BRCMS_ANTSEL_11N_0(ant)) << 2) +\
0037                 (BRCMS_ANTSEL_11N_1(ant)))
0038 #define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
0039 #define BRCMS_ANTSEL_11N(ant)   ((ant) & ANT_SELCFG_MASK)
0040 
0041 /* antenna switch */
0042 /* defines for no boardlevel antenna diversity */
0043 #define ANT_SELCFG_DEF_2x2  0x01    /* default antenna configuration */
0044 
0045 /* 2x3 antdiv defines and tables for GPIO communication */
0046 #define ANT_SELCFG_NUM_2x3  3
0047 #define ANT_SELCFG_DEF_2x3  0x01    /* default antenna configuration */
0048 
0049 /* 2x4 antdiv rev4 defines and tables for GPIO communication */
0050 #define ANT_SELCFG_NUM_2x4  4
0051 #define ANT_SELCFG_DEF_2x4  0x02    /* default antenna configuration */
0052 
0053 static const u16 mimo_2x4_div_antselpat_tbl[] = {
0054     0, 0, 0x9, 0xa,     /* ant0: 0 ant1: 2,3 */
0055     0, 0, 0x5, 0x6,     /* ant0: 1 ant1: 2,3 */
0056     0, 0, 0, 0,     /* n.a.              */
0057     0, 0, 0, 0      /* n.a.              */
0058 };
0059 
0060 static const u8 mimo_2x4_div_antselid_tbl[16] = {
0061     0, 0, 0, 0, 0, 2, 3, 0,
0062     0, 0, 1, 0, 0, 0, 0, 0  /* pat to antselid */
0063 };
0064 
0065 static const u16 mimo_2x3_div_antselpat_tbl[] = {
0066     16, 0, 1, 16,       /* ant0: 0 ant1: 1,2 */
0067     16, 16, 16, 16,     /* n.a.              */
0068     16, 2, 16, 16,      /* ant0: 2 ant1: 1   */
0069     16, 16, 16, 16      /* n.a.              */
0070 };
0071 
0072 static const u8 mimo_2x3_div_antselid_tbl[16] = {
0073     0, 1, 2, 0, 0, 0, 0, 0,
0074     0, 0, 0, 0, 0, 0, 0, 0  /* pat to antselid */
0075 };
0076 
0077 /* boardlevel antenna selection: init antenna selection structure */
0078 static void
0079 brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,
0080             bool auto_sel)
0081 {
0082     if (asi->antsel_type == ANTSEL_2x3) {
0083         u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
0084             ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
0085         antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
0086         antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
0087         antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
0088         antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
0089         antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
0090 
0091     } else if (asi->antsel_type == ANTSEL_2x4) {
0092 
0093         antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
0094         antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
0095         antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
0096         antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
0097         antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
0098 
0099     } else {        /* no antenna selection available */
0100 
0101         antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
0102         antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
0103         antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
0104         antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
0105         antsel->num_antcfg = 0;
0106     }
0107 }
0108 
0109 struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)
0110 {
0111     struct antsel_info *asi;
0112     struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom;
0113 
0114     asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
0115     if (!asi)
0116         return NULL;
0117 
0118     asi->wlc = wlc;
0119     asi->pub = wlc->pub;
0120     asi->antsel_type = ANTSEL_NA;
0121     asi->antsel_avail = false;
0122     asi->antsel_antswitch = sprom->antswitch;
0123 
0124     if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
0125         switch (asi->antsel_antswitch) {
0126         case ANTSWITCH_TYPE_1:
0127         case ANTSWITCH_TYPE_2:
0128         case ANTSWITCH_TYPE_3:
0129             /* 4321/2 board with 2x3 switch logic */
0130             asi->antsel_type = ANTSEL_2x3;
0131             /* Antenna selection availability */
0132             if ((sprom->ant_available_bg == 7) ||
0133                 (sprom->ant_available_a == 7)) {
0134                 asi->antsel_avail = true;
0135             } else if (
0136                 sprom->ant_available_bg == 3 ||
0137                 sprom->ant_available_a == 3) {
0138                 asi->antsel_avail = false;
0139             } else {
0140                 asi->antsel_avail = false;
0141                 brcms_err(wlc->hw->d11core,
0142                       "antsel_attach: 2o3 "
0143                       "board cfg invalid\n");
0144             }
0145 
0146             break;
0147         default:
0148             break;
0149         }
0150     } else if ((asi->pub->sromrev == 4) &&
0151            (sprom->ant_available_bg == 7) &&
0152            (sprom->ant_available_a == 0)) {
0153         /* hack to match old 4321CB2 cards with 2of3 antenna switch */
0154         asi->antsel_type = ANTSEL_2x3;
0155         asi->antsel_avail = true;
0156     } else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
0157         asi->antsel_type = ANTSEL_2x4;
0158         asi->antsel_avail = true;
0159     }
0160 
0161     /* Set the antenna selection type for the low driver */
0162     brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);
0163 
0164     /* Init (auto/manual) antenna selection */
0165     brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);
0166     brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);
0167 
0168     return asi;
0169 }
0170 
0171 void brcms_c_antsel_detach(struct antsel_info *asi)
0172 {
0173     kfree(asi);
0174 }
0175 
0176 /*
0177  * boardlevel antenna selection:
0178  *   convert ant_cfg to mimo_antsel (ucode interface)
0179  */
0180 static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
0181 {
0182     u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));
0183     u16 mimo_antsel = 0;
0184 
0185     if (asi->antsel_type == ANTSEL_2x4) {
0186         /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
0187         mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
0188         return mimo_antsel;
0189 
0190     } else if (asi->antsel_type == ANTSEL_2x3) {
0191         /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
0192         mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
0193         return mimo_antsel;
0194     }
0195 
0196     return mimo_antsel;
0197 }
0198 
0199 /* boardlevel antenna selection: ucode interface control */
0200 static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
0201                  struct brcms_antselcfg *antsel)
0202 {
0203     struct brcms_c_info *wlc = asi->wlc;
0204     u8 ant_cfg;
0205     u16 mimo_antsel;
0206 
0207     /* 1) Update TX antconfig for all frames that are not unicast data
0208      *    (aka default TX)
0209      */
0210     ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
0211     mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
0212     brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
0213     /*
0214      * Update driver stats for currently selected
0215      * default tx/rx antenna config
0216      */
0217     asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
0218 
0219     /* 2) Update RX antconfig for all frames that are not unicast data
0220      *    (aka default RX)
0221      */
0222     ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
0223     mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
0224     brcms_b_write_shm(wlc->hw, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
0225     /*
0226      * Update driver stats for currently selected
0227      * default tx/rx antenna config
0228      */
0229     asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
0230 
0231     return 0;
0232 }
0233 
0234 void brcms_c_antsel_init(struct antsel_info *asi)
0235 {
0236     if ((asi->antsel_type == ANTSEL_2x3) ||
0237         (asi->antsel_type == ANTSEL_2x4))
0238         brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);
0239 }
0240 
0241 /* boardlevel antenna selection: convert id to ant_cfg */
0242 static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)
0243 {
0244     u8 antcfg = ANT_SELCFG_DEF_2x2;
0245 
0246     if (asi->antsel_type == ANTSEL_2x4) {
0247         /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
0248         antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
0249         return antcfg;
0250 
0251     } else if (asi->antsel_type == ANTSEL_2x3) {
0252         /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
0253         antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
0254         return antcfg;
0255     }
0256 
0257     return antcfg;
0258 }
0259 
0260 void
0261 brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
0262               u8 antselid, u8 fbantselid, u8 *antcfg,
0263               u8 *fbantcfg)
0264 {
0265     u8 ant;
0266 
0267     /* if use default, assign it and return */
0268     if (usedef) {
0269         *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
0270         *fbantcfg = *antcfg;
0271         return;
0272     }
0273 
0274     if (!sel) {
0275         *antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0276         *fbantcfg = *antcfg;
0277 
0278     } else {
0279         ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0280         if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
0281             *antcfg = brcms_c_antsel_id2antcfg(asi, antselid);
0282             *fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);
0283         } else {
0284             *antcfg =
0285                 asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
0286             *fbantcfg = *antcfg;
0287         }
0288     }
0289     return;
0290 }
0291 
0292 /* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
0293 u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
0294 {
0295     u8 antselid = 0;
0296 
0297     if (asi->antsel_type == ANTSEL_2x4) {
0298         /* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
0299         antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
0300         return antselid;
0301 
0302     } else if (asi->antsel_type == ANTSEL_2x3) {
0303         /* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
0304         antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
0305         return antselid;
0306     }
0307 
0308     return antselid;
0309 }