Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  main.c   Sprache: C

 
/*
 * Copyright (c) 2010 Broadcom Corporation
 * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de>
 *
 * Permission to use, copy, modify, and/or distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */


#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt

#include <linux/pci_ids.h>
#include <linux/if_ether.h>
#include <net/cfg80211.h>
#include <net/mac80211.h>
#include <brcm_hw_ids.h>
#include <aiutils.h>
#include <chipcommon.h>
#include "rate.h"
#include "scb.h"
#include "phy/phy_hal.h"
#include "channel.h"
#include "antsel.h"
#include "stf.h"
#include "ampdu.h"
#include "mac80211_if.h"
#include "ucode_loader.h"
#include "main.h"
#include "soc.h"
#include "dma.h"
#include "debug.h"
#include "brcms_trace_events.h"

/* watchdog timer, in unit of ms */
#define TIMER_INTERVAL_WATCHDOG  1000
/* radio monitor timer, in unit of ms */
#define TIMER_INTERVAL_RADIOCHK  800

/* beacon interval, in unit of 1024TU */
#define BEACON_INTERVAL_DEFAULT  100

/* n-mode support capability */
/* 2x2 includes both 1x1 & 2x2 devices
 * reserved #define 2 for future when we want to separate 1x1 & 2x2 and
 * control it independently
 */

#define WL_11N_2x2   1
#define WL_11N_3x3   3
#define WL_11N_4x4   4

#define EDCF_ACI_MASK   0x60
#define EDCF_ACI_SHIFT   5
#define EDCF_ECWMIN_MASK  0x0f
#define EDCF_ECWMAX_SHIFT  4
#define EDCF_AIFSN_MASK   0x0f
#define EDCF_AIFSN_MAX   15
#define EDCF_ECWMAX_MASK  0xf0

#define EDCF_AC_BE_TXOP_STA  0x0000
#define EDCF_AC_BK_TXOP_STA  0x0000
#define EDCF_AC_VO_ACI_STA  0x62
#define EDCF_AC_VO_ECW_STA  0x32
#define EDCF_AC_VI_ACI_STA  0x42
#define EDCF_AC_VI_ECW_STA  0x43
#define EDCF_AC_BK_ECW_STA  0xA4
#define EDCF_AC_VI_TXOP_STA  0x005e
#define EDCF_AC_VO_TXOP_STA  0x002f
#define EDCF_AC_BE_ACI_STA  0x03
#define EDCF_AC_BE_ECW_STA  0xA4
#define EDCF_AC_BK_ACI_STA  0x27
#define EDCF_AC_VO_TXOP_AP  0x002f

#define EDCF_TXOP2USEC(txop)  ((txop) << 5)
#define EDCF_ECW2CW(exp)  ((1 << (exp)) - 1)

#define APHY_SYMBOL_TIME  4
#define APHY_PREAMBLE_TIME  16
#define APHY_SIGNAL_TIME  4
#define APHY_SIFS_TIME   16
#define APHY_SERVICE_NBITS  16
#define APHY_TAIL_NBITS   6
#define BPHY_SIFS_TIME   10
#define BPHY_PLCP_SHORT_TIME  96

#define PREN_PREAMBLE   24
#define PREN_MM_EXT   12
#define PREN_PREAMBLE_EXT  4

#define DOT11_MAC_HDR_LEN  24
#define DOT11_ACK_LEN   10
#define DOT11_BA_LEN   4
#define DOT11_OFDM_SIGNAL_EXTENSION 6
#define DOT11_MIN_FRAG_LEN  256
#define DOT11_RTS_LEN   16
#define DOT11_CTS_LEN   10
#define DOT11_BA_BITMAP_LEN  128
#define DOT11_MAXNUMFRAGS  16
#define DOT11_MAX_FRAG_LEN  2346

#define BPHY_PLCP_TIME   192
#define RIFS_11N_TIME   2

/* length of the BCN template area */
#define BCN_TMPL_LEN   512

/* brcms_bss_info flag bit values */
#define BRCMS_BSS_HT   0x0020 /* BSS is HT (MIMO) capable */

/* chip rx buffer offset */
#define BRCMS_HWRXOFF   38

/* rfdisable delay timer 500 ms, runs of ALP clock */
#define RFDISABLE_DEFAULT  10000000

#define BRCMS_TEMPSENSE_PERIOD  10 /* 10 second timeout */

/* synthpu_dly times in us */
#define SYNTHPU_DLY_APHY_US  3700
#define SYNTHPU_DLY_BPHY_US  1050
#define SYNTHPU_DLY_NPHY_US  2048
#define SYNTHPU_DLY_LPPHY_US  300

#define ANTCNT    10 /* vanilla M_MAX_ANTCNT val */

/* Per-AC retry limit register definitions; uses defs.h bitfield macros */
#define EDCF_SHORT_S   0
#define EDCF_SFB_S   4
#define EDCF_LONG_S   8
#define EDCF_LFB_S   12
#define EDCF_SHORT_M   BITFIELD_MASK(4)
#define EDCF_SFB_M   BITFIELD_MASK(4)
#define EDCF_LONG_M   BITFIELD_MASK(4)
#define EDCF_LFB_M   BITFIELD_MASK(4)

#define RETRY_SHORT_DEF   7 /* Default Short retry Limit */
#define RETRY_SHORT_MAX   255 /* Maximum Short retry Limit */
#define RETRY_LONG_DEF   4 /* Default Long retry count */
#define RETRY_SHORT_FB   3 /* Short count for fb rate */
#define RETRY_LONG_FB   2 /* Long count for fb rate */

#define APHY_CWMIN   15
#define PHY_CWMAX   1023

#define EDCF_AIFSN_MIN   1

#define FRAGNUM_MASK   0xF

#define APHY_SLOT_TIME   9
#define BPHY_SLOT_TIME   20

#define WL_SPURAVOID_OFF  0
#define WL_SPURAVOID_ON1  1
#define WL_SPURAVOID_ON2  2

/* invalid core flags, use the saved coreflags */
#define BRCMS_USE_COREFLAGS  0xffffffff

/* values for PLCPHdr_override */
#define BRCMS_PLCP_AUTO   -1
#define BRCMS_PLCP_SHORT  0
#define BRCMS_PLCP_LONG   1

/* values for g_protection_override and n_protection_override */
#define BRCMS_PROTECTION_AUTO  -1
#define BRCMS_PROTECTION_OFF  0
#define BRCMS_PROTECTION_ON  1
#define BRCMS_PROTECTION_MMHDR_ONLY 2
#define BRCMS_PROTECTION_CTS_ONLY 3

/* values for g_protection_control and n_protection_control */
#define BRCMS_PROTECTION_CTL_OFF 0
#define BRCMS_PROTECTION_CTL_LOCAL 1
#define BRCMS_PROTECTION_CTL_OVERLAP 2

/* values for n_protection */
#define BRCMS_N_PROTECTION_OFF  0
#define BRCMS_N_PROTECTION_OPTIONAL 1
#define BRCMS_N_PROTECTION_20IN40 2
#define BRCMS_N_PROTECTION_MIXEDMODE 3

/* values for band specific 40MHz capabilities */
#define BRCMS_N_BW_20ALL  0
#define BRCMS_N_BW_40ALL  1
#define BRCMS_N_BW_20IN2G_40IN5G 2

/* bitflags for SGI support (sgi_rx iovar) */
#define BRCMS_N_SGI_20   0x01
#define BRCMS_N_SGI_40   0x02

/* defines used by the nrate iovar */
/* MSC in use,indicates b0-6 holds an mcs */
#define NRATE_MCS_INUSE   0x00000080
/* rate/mcs value */
#define NRATE_RATE_MASK   0x0000007f
/* stf mode mask: siso, cdd, stbc, sdm */
#define NRATE_STF_MASK   0x0000ff00
/* stf mode shift */
#define NRATE_STF_SHIFT   8
/* bit indicate to override mcs only */
#define NRATE_OVERRIDE_MCS_ONLY  0x40000000
#define NRATE_SGI_MASK   0x00800000 /* sgi mode */
#define NRATE_SGI_SHIFT   23  /* sgi mode */
#define NRATE_LDPC_CODING  0x00400000 /* adv coding in use */
#define NRATE_LDPC_SHIFT  22  /* ldpc shift */

#define NRATE_STF_SISO   0  /* stf mode SISO */
#define NRATE_STF_CDD   1  /* stf mode CDD */
#define NRATE_STF_STBC   2  /* stf mode STBC */
#define NRATE_STF_SDM   3  /* stf mode SDM */

#define MAX_DMA_SEGS   4

/* # of entries in Tx FIFO */
#define NTXD    64
/* Max # of entries in Rx FIFO based on 4kb page size */
#define NRXD    256

/* Amount of headroom to leave in Tx FIFO */
#define TX_HEADROOM   4

/* try to keep this # rbufs posted to the chip */
#define NRXBUFPOST   32

/* max # frames to process in brcms_c_recv() */
#define RXBND    8
/* max # tx status to process in wlc_txstatus() */
#define TXSBND    8

/*
 * The following table lists the buffer memory allocated to xmt fifos in HW.
 * the size is in units of 256bytes(one block), total size is HW dependent
 * ucode has default fifo partition, sw can overwrite if necessary
 *
 * This is documented in twiki under the topic UcodeTxFifo. Please ensure
 * the twiki is updated before making changes.
 */


/* Starting corerev for the fifo size table */
#define XMTFIFOTBL_STARTREV 17

struct d11init {
 __le16 addr;
 __le16 size;
 __le32 value;
};

struct edcf_acparam {
 u8 ACI;
 u8 ECW;
 u16 TXOP;
} __packed;

/* debug/trace */
uint brcm_msg_level;

/* TX FIFO number to WME/802.1E Access Category */
static const u8 wme_fifo2ac[] = {
 IEEE80211_AC_BK,
 IEEE80211_AC_BE,
 IEEE80211_AC_VI,
 IEEE80211_AC_VO,
 IEEE80211_AC_BE,
 IEEE80211_AC_BE
};

/* ieee80211 Access Category to TX FIFO number */
static const u8 wme_ac2fifo[] = {
 TX_AC_VO_FIFO,
 TX_AC_VI_FIFO,
 TX_AC_BE_FIFO,
 TX_AC_BK_FIFO
};

static const u16 xmtfifo_sz[][NFIFO] = {
 /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */
 {20, 192, 192, 21, 17, 5},
 /* corerev 18: */
 {0, 0, 0, 0, 0, 0},
 /* corerev 19: */
 {0, 0, 0, 0, 0, 0},
 /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */
 {20, 192, 192, 21, 17, 5},
 /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */
 {9, 58, 22, 14, 14, 5},
 /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */
 {20, 192, 192, 21, 17, 5},
 /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */
 {20, 192, 192, 21, 17, 5},
 /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */
 {9, 58, 22, 14, 14, 5},
 /* corerev 25: */
 {0, 0, 0, 0, 0, 0},
 /* corerev 26: */
 {0, 0, 0, 0, 0, 0},
 /* corerev 27: */
 {0, 0, 0, 0, 0, 0},
 /* corerev 28: 2304, 14848, 5632, 3584, 3584, 1280 */
 {9, 58, 22, 14, 14, 5},
};

#ifdef DEBUG
static const char * const fifo_names[] = {
 "AC_BK""AC_BE""AC_VI""AC_VO""BCMC""ATIM" };
#else
static const char fifo_names[6][1];
#endif

#ifdef DEBUG
/* pointer to most recently allocated wl/wlc */
static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL);
#endif

/* Mapping of ieee80211 AC numbers to tx fifos */
static const u8 ac_to_fifo_mapping[IEEE80211_NUM_ACS] = {
 [IEEE80211_AC_VO] = TX_AC_VO_FIFO,
 [IEEE80211_AC_VI] = TX_AC_VI_FIFO,
 [IEEE80211_AC_BE] = TX_AC_BE_FIFO,
 [IEEE80211_AC_BK] = TX_AC_BK_FIFO,
};

/* Mapping of tx fifos to ieee80211 AC numbers */
static const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = {
 [TX_AC_BK_FIFO] = IEEE80211_AC_BK,
 [TX_AC_BE_FIFO] = IEEE80211_AC_BE,
 [TX_AC_VI_FIFO] = IEEE80211_AC_VI,
 [TX_AC_VO_FIFO] = IEEE80211_AC_VO,
};

static u8 brcms_ac_to_fifo(u8 ac)
{
 if (ac >= ARRAY_SIZE(ac_to_fifo_mapping))
  return TX_AC_BE_FIFO;
 return ac_to_fifo_mapping[ac];
}

static u8 brcms_fifo_to_ac(u8 fifo)
{
 if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping))
  return IEEE80211_AC_BE;
 return fifo_to_ac_mapping[fifo];
}

/* Find basic rate for a given rate */
static u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec)
{
 if (is_mcs_rate(rspec))
  return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK]
         .leg_ofdm];
 return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK];
}

static u16 frametype(u32 rspec, u8 mimoframe)
{
 if (is_mcs_rate(rspec))
  return mimoframe;
 return is_cck_rate(rspec) ? FT_CCK : FT_OFDM;
}

/* currently the best mechanism for determining SIFS is the band in use */
static u16 get_sifs(struct brcms_band *band)
{
 return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME :
     BPHY_SIFS_TIME;
}

/*
 * Detect Card removed.
 * Even checking an sbconfig register read will not false trigger when the core
 * is in reset it breaks CF address mechanism. Accessing gphy phyversion will
 * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible
 * reg with fixed 0/1 pattern (some platforms return all 0).
 * If clocks are present, call the sb routine which will figure out if the
 * device is removed.
 */

static bool brcms_deviceremoved(struct brcms_c_info *wlc)
{
 u32 macctrl;

 if (!wlc->hw->clk)
  return ai_deviceremoved(wlc->hw->sih);
 macctrl = bcma_read32(wlc->hw->d11core,
         D11REGOFFS(maccontrol));
 return (macctrl & (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN;
}

/* sum the individual fifo tx pending packet counts */
static int brcms_txpktpendtot(struct brcms_c_info *wlc)
{
 int i;
 int pending = 0;

 for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++)
  if (wlc->hw->di[i])
   pending += dma_txpending(wlc->hw->di[i]);
 return pending;
}

static bool brcms_is_mband_unlocked(struct brcms_c_info *wlc)
{
 return wlc->pub->_nbands > 1 && !wlc->bandlocked;
}

static int brcms_chspec_bw(u16 chanspec)
{
 if (CHSPEC_IS40(chanspec))
  return BRCMS_40_MHZ;
 if (CHSPEC_IS20(chanspec))
  return BRCMS_20_MHZ;

 return BRCMS_10_MHZ;
}

static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg)
{
 if (cfg == NULL)
  return;

 kfree(cfg->current_bss);
 kfree(cfg);
}

static void brcms_c_detach_mfree(struct brcms_c_info *wlc)
{
 if (wlc == NULL)
  return;

 brcms_c_bsscfg_mfree(wlc->bsscfg);
 kfree(wlc->pub);
 kfree(wlc->modulecb);
 kfree(wlc->default_bss);
 kfree(wlc->protection);
 kfree(wlc->stf);
 kfree(wlc->bandstate[0]);
 if (wlc->corestate)
  kfree(wlc->corestate->macstat_snapshot);
 kfree(wlc->corestate);
 if (wlc->hw)
  kfree(wlc->hw->bandstate[0]);
 kfree(wlc->hw);
 if (wlc->beacon)
  dev_kfree_skb_any(wlc->beacon);
 if (wlc->probe_resp)
  dev_kfree_skb_any(wlc->probe_resp);

 kfree(wlc);
}

static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
{
 struct brcms_bss_cfg *cfg;

 cfg = kzalloc(sizeof(*cfg), GFP_ATOMIC);
 if (cfg == NULL)
  goto fail;

 cfg->current_bss = kzalloc(sizeof(*cfg->current_bss), GFP_ATOMIC);
 if (cfg->current_bss == NULL)
  goto fail;

 return cfg;

 fail:
 brcms_c_bsscfg_mfree(cfg);
 return NULL;
}

static struct brcms_c_info *
brcms_c_attach_malloc(uint unit, uint *err, uint devid)
{
 struct brcms_c_info *wlc;

 wlc = kzalloc(sizeof(*wlc), GFP_ATOMIC);
 if (wlc == NULL) {
  *err = 1002;
  goto fail;
 }

 /* allocate struct brcms_c_pub state structure */
 wlc->pub = kzalloc(sizeof(*wlc->pub), GFP_ATOMIC);
 if (wlc->pub == NULL) {
  *err = 1003;
  goto fail;
 }
 wlc->pub->wlc = wlc;

 /* allocate struct brcms_hardware state structure */

 wlc->hw = kzalloc(sizeof(*wlc->hw), GFP_ATOMIC);
 if (wlc->hw == NULL) {
  *err = 1005;
  goto fail;
 }
 wlc->hw->wlc = wlc;

 wlc->hw->bandstate[0] =
  kcalloc(MAXBANDS, sizeof(struct brcms_hw_band), GFP_ATOMIC);
 if (wlc->hw->bandstate[0] == NULL) {
  *err = 1006;
  goto fail;
 } else {
  int i;

  for (i = 1; i < MAXBANDS; i++)
   wlc->hw->bandstate[i] = (struct brcms_hw_band *)
       ((unsigned long)wlc->hw->bandstate[0] +
        (sizeof(struct brcms_hw_band) * i));
 }

 wlc->modulecb =
  kcalloc(BRCMS_MAXMODULES, sizeof(struct modulecb),
   GFP_ATOMIC);
 if (wlc->modulecb == NULL) {
  *err = 1009;
  goto fail;
 }

 wlc->default_bss = kzalloc(sizeof(*wlc->default_bss), GFP_ATOMIC);
 if (wlc->default_bss == NULL) {
  *err = 1010;
  goto fail;
 }

 wlc->bsscfg = brcms_c_bsscfg_malloc(unit);
 if (wlc->bsscfg == NULL) {
  *err = 1011;
  goto fail;
 }

 wlc->protection = kzalloc(sizeof(*wlc->protection), GFP_ATOMIC);
 if (wlc->protection == NULL) {
  *err = 1016;
  goto fail;
 }

 wlc->stf = kzalloc(sizeof(*wlc->stf), GFP_ATOMIC);
 if (wlc->stf == NULL) {
  *err = 1017;
  goto fail;
 }

 wlc->bandstate[0] =
  kcalloc(MAXBANDS, sizeof(*wlc->bandstate[0]), GFP_ATOMIC);
 if (wlc->bandstate[0] == NULL) {
  *err = 1025;
  goto fail;
 } else {
  int i;

  for (i = 1; i < MAXBANDS; i++)
   wlc->bandstate[i] = (struct brcms_band *)
    ((unsigned long)wlc->bandstate[0]
    + (sizeof(struct brcms_band)*i));
 }

 wlc->corestate = kzalloc(sizeof(*wlc->corestate), GFP_ATOMIC);
 if (wlc->corestate == NULL) {
  *err = 1026;
  goto fail;
 }

 wlc->corestate->macstat_snapshot =
  kzalloc(sizeof(*wlc->corestate->macstat_snapshot), GFP_ATOMIC);
 if (wlc->corestate->macstat_snapshot == NULL) {
  *err = 1027;
  goto fail;
 }

 return wlc;

 fail:
 brcms_c_detach_mfree(wlc);
 return NULL;
}

/*
 * Update the slot timing for standard 11b/g (20us slots)
 * or shortslot 11g (9us slots)
 * The PSM needs to be suspended for this call.
 */

static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw,
     bool shortslot)
{
 struct bcma_device *core = wlc_hw->d11core;

 if (shortslot) {
  /* 11g short slot: 11a timing */
  bcma_write16(core, D11REGOFFS(ifs_slot), 0x0207);
  brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME);
 } else {
  /* 11g long slot: 11b timing */
  bcma_write16(core, D11REGOFFS(ifs_slot), 0x0212);
  brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
 }
}

/*
 * calculate frame duration of a given rate and length, return
 * time in usec unit
 */

static uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec,
        u8 preamble_type, uint mac_len)
{
 uint nsyms, dur = 0, Ndps, kNdps;
 uint rate = rspec2rate(ratespec);

 if (rate == 0) {
  brcms_err(wlc->hw->d11core, "wl%d: WAR: using rate of 1 mbps\n",
     wlc->pub->unit);
  rate = BRCM_RATE_1M;
 }

 if (is_mcs_rate(ratespec)) {
  uint mcs = ratespec & RSPEC_RATE_MASK;
  int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec);

  dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT);
  if (preamble_type == BRCMS_MM_PREAMBLE)
   dur += PREN_MM_EXT;
  /* 1000Ndbps = kbps * 4 */
  kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec),
       rspec_issgi(ratespec)) * 4;

  if (rspec_stc(ratespec) == 0)
   nsyms =
       CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
      APHY_TAIL_NBITS) * 1000, kNdps);
  else
   /* STBC needs to have even number of symbols */
   nsyms =
       2 *
       CEIL((APHY_SERVICE_NBITS + 8 * mac_len +
      APHY_TAIL_NBITS) * 1000, 2 * kNdps);

  dur += APHY_SYMBOL_TIME * nsyms;
  if (wlc->band->bandtype == BRCM_BAND_2G)
   dur += DOT11_OFDM_SIGNAL_EXTENSION;
 } else if (is_ofdm_rate(rate)) {
  dur = APHY_PREAMBLE_TIME;
  dur += APHY_SIGNAL_TIME;
  /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */
  Ndps = rate * 2;
  /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */
  nsyms =
      CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS),
    Ndps);
  dur += APHY_SYMBOL_TIME * nsyms;
  if (wlc->band->bandtype == BRCM_BAND_2G)
   dur += DOT11_OFDM_SIGNAL_EXTENSION;
 } else {
  /*
 * calc # bits * 2 so factor of 2 in rate (1/2 mbps)
 * will divide out
 */

  mac_len = mac_len * 8 * 2;
  /* calc ceiling of bits/rate = microseconds of air time */
  dur = (mac_len + rate - 1) / rate;
  if (preamble_type & BRCMS_SHORT_PREAMBLE)
   dur += BPHY_PLCP_SHORT_TIME;
  else
   dur += BPHY_PLCP_TIME;
 }
 return dur;
}

static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
    const struct d11init *inits)
{
 struct bcma_device *core = wlc_hw->d11core;
 int i;
 uint offset;
 u16 size;
 u32 value;

 brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit);

 for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) {
  size = le16_to_cpu(inits[i].size);
  offset = le16_to_cpu(inits[i].addr);
  value = le32_to_cpu(inits[i].value);
  if (size == 2)
   bcma_write16(core, offset, value);
  else if (size == 4)
   bcma_write32(core, offset, value);
  else
   break;
 }
}

static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs)
{
 u8 idx;
 static const u16 addr[] = {
  M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
  M_HOST_FLAGS5
 };

 for (idx = 0; idx < MHFMAX; idx++)
  brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]);
}

static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw)
{
 struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode;

 /* init microcode host flags */
 brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs);

 /* do band-specific ucode IHR, SHM, and SCR inits */
 if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) {
  if (BRCMS_ISNPHY(wlc_hw->band))
   brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16);
  else
   brcms_err(wlc_hw->d11core,
      "%s: wl%d: unsupported phy in corerev %d\n",
      __func__, wlc_hw->unit,
      wlc_hw->corerev);
 } else {
  if (D11REV_IS(wlc_hw->corerev, 24)) {
   if (BRCMS_ISLCNPHY(wlc_hw->band))
    brcms_c_write_inits(wlc_hw,
          ucode->d11lcn0bsinitvals24);
   else
    brcms_err(wlc_hw->d11core,
       "%s: wl%d: unsupported phy in core rev %d\n",
       __func__, wlc_hw->unit,
       wlc_hw->corerev);
  } else {
   brcms_err(wlc_hw->d11core,
      "%s: wl%d: unsupported corerev %d\n",
      __func__, wlc_hw->unit, wlc_hw->corerev);
  }
 }
}

static void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v)
{
 struct bcma_device *core = wlc_hw->d11core;
 u32 ioctl = bcma_aread32(core, BCMA_IOCTL) & ~m;

 bcma_awrite32(core, BCMA_IOCTL, ioctl | v);
}

static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk)
{
 brcms_dbg_info(wlc_hw->d11core, "wl%d: clk %d\n", wlc_hw->unit, clk);

 wlc_hw->phyclk = clk;

 if (OFF == clk) { /* clear gmode bit, put phy into reset */

  brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC | SICF_GMODE),
       (SICF_PRST | SICF_FGC));
  udelay(1);
  brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_PRST);
  udelay(1);

 } else {  /* take phy out of reset */

  brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_FGC);
  udelay(1);
  brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0);
  udelay(1);

 }
}

/* low-level band switch utility routine */
static void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit)
{
 brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit,
      bandunit);

 wlc_hw->band = wlc_hw->bandstate[bandunit];

 /*
 * BMAC_NOTE:
 *   until we eliminate need for wlc->band refs in low level code
 */

 wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit];

 /* set gmode core flag */
 if (wlc_hw->sbclk && !wlc_hw->noreset) {
  u32 gmode = 0;

  if (bandunit == 0)
   gmode = SICF_GMODE;

  brcms_b_core_ioctl(wlc_hw, SICF_GMODE, gmode);
 }
}

/* switch to new band but leave it inactive */
static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit)
{
 struct brcms_hardware *wlc_hw = wlc->hw;
 u32 macintmask;
 u32 macctrl;

 brcms_dbg_mac80211(wlc_hw->d11core, "wl%d\n", wlc_hw->unit);
 macctrl = bcma_read32(wlc_hw->d11core,
         D11REGOFFS(maccontrol));
 WARN_ON((macctrl & MCTL_EN_MAC) != 0);

 /* disable interrupts */
 macintmask = brcms_intrsoff(wlc->wl);

 /* radio off */
 wlc_phy_switch_radio(wlc_hw->band->pi, OFF);

 brcms_b_core_phy_clk(wlc_hw, OFF);

 brcms_c_setxband(wlc_hw, bandunit);

 return macintmask;
}

/* process an individual struct tx_status */
static bool
brcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs)
{
 struct sk_buff *p = NULL;
 uint queue = NFIFO;
 struct dma_pub *dma = NULL;
 struct d11txh *txh = NULL;
 struct scb *scb = NULL;
 int tx_frame_count;
 uint supr_status;
 bool lastframe;
 struct ieee80211_hdr *h;
 struct ieee80211_tx_info *tx_info;
 struct ieee80211_tx_rate *txrate;
 int i;
 bool fatal = true;

 trace_brcms_txstatus(&wlc->hw->d11core->dev, txs->framelen,
        txs->frameid, txs->status, txs->lasttxtime,
        txs->sequence, txs->phyerr, txs->ackphyrxsh);

 /* discard intermediate indications for ucode with one legitimate case:
 *   e.g. if "useRTS" is set. ucode did a successful rts/cts exchange,
 *   but the subsequent tx of DATA failed. so it will start rts/cts
 *   from the beginning (resetting the rts transmission count)
 */

 if (!(txs->status & TX_STATUS_AMPDU)
     && (txs->status & TX_STATUS_INTERMEDIATE)) {
  brcms_dbg_tx(wlc->hw->d11core, "INTERMEDIATE but not AMPDU\n");
  fatal = false;
  goto out;
 }

 queue = txs->frameid & TXFID_QUEUE_MASK;
 if (queue >= NFIFO) {
  brcms_err(wlc->hw->d11core, "queue %u >= NFIFO\n", queue);
  goto out;
 }

 dma = wlc->hw->di[queue];

 p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED);
 if (p == NULL) {
  brcms_err(wlc->hw->d11core, "dma_getnexttxp returned null!\n");
  goto out;
 }

 txh = (struct d11txh *) (p->data);

 if (txs->phyerr)
  brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n",
        txs->phyerr, txh->MainRates);

 if (txs->frameid != le16_to_cpu(txh->TxFrameID)) {
  brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n");
  goto out;
 }
 tx_info = IEEE80211_SKB_CB(p);
 h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN);

 if (tx_info->rate_driver_data[0])
  scb = &wlc->pri_scb;

 if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
  brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs);
  fatal = false;
  goto out;
 }

 /*
 * brcms_c_ampdu_dotxstatus() will trace tx descriptors for AMPDU
 * frames; this traces them for the rest.
 */

 trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh));

 supr_status = txs->status & TX_STATUS_SUPR_MASK;
 if (supr_status == TX_STATUS_SUPR_BADCH) {
  unsigned xfts = le16_to_cpu(txh->XtraFrameTypes);
  brcms_dbg_tx(wlc->hw->d11core,
        "Pkt tx suppressed, dest chan %u, current %d\n",
        (xfts >> XFTS_CHANNEL_SHIFT) & 0xff,
        CHSPEC_CHANNEL(wlc->default_bss->chanspec));
 }

 tx_frame_count =
     (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT;

 lastframe = !ieee80211_has_morefrags(h->frame_control);

 if (!lastframe) {
  brcms_err(wlc->hw->d11core, "Not last frame!\n");
 } else {
  /*
 * Set information to be consumed by Minstrel ht.
 *
 * The "fallback limit" is the number of tx attempts a given
 * MPDU is sent at the "primary" rate. Tx attempts beyond that
 * limit are sent at the "secondary" rate.
 * A 'short frame' does not exceed RTS threshold.
 */

  u16 sfbl, /* Short Frame Rate Fallback Limit */
      lfbl, /* Long Frame Rate Fallback Limit */
      fbl;

  if (queue < IEEE80211_NUM_ACS) {
   sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]],
          EDCF_SFB);
   lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]],
          EDCF_LFB);
  } else {
   sfbl = wlc->SFBL;
   lfbl = wlc->LFBL;
  }

  txrate = tx_info->status.rates;
  if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS)
   fbl = lfbl;
  else
   fbl = sfbl;

  ieee80211_tx_info_clear_status(tx_info);

  if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) {
   /*
 * rate selection requested a fallback rate
 * and we used it
 */

   txrate[0].count = fbl;
   txrate[1].count = tx_frame_count - fbl;
  } else {
   /*
 * rate selection did not request fallback rate, or
 * we didn't need it
 */

   txrate[0].count = tx_frame_count;
   /*
 * rc80211_minstrel.c:minstrel_tx_status() expects
 * unused rates to be marked with idx = -1
 */

   txrate[1].idx = -1;
   txrate[1].count = 0;
  }

  /* clear the rest of the rates */
  for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
   txrate[i].idx = -1;
   txrate[i].count = 0;
  }

  if (txs->status & TX_STATUS_ACK_RCV)
   tx_info->flags |= IEEE80211_TX_STAT_ACK;
 }

 if (lastframe) {
  /* remove PLCP & Broadcom tx descriptor header */
  skb_pull(p, D11_PHY_HDR_LEN);
  skb_pull(p, D11_TXH_LEN);
  ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p);
 } else {
  brcms_err(wlc->hw->d11core,
     "%s: Not last frame => not calling tx_status\n",
     __func__);
 }

 fatal = false;

 out:
 if (fatal) {
  if (txh)
   trace_brcms_txdesc(&wlc->hw->d11core->dev, txh,
        sizeof(*txh));
  brcmu_pkt_buf_free_skb(p);
 }

 if (dma && queue < NFIFO) {
  u16 ac_queue = brcms_fifo_to_ac(queue);
  if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO &&
      ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue))
   ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue);
  dma_kick_tx(dma);
 }

 return fatal;
}

/* process tx completion events in BMAC
 * Return true if more tx status need to be processed. false otherwise.
 */

static bool
brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
{
 struct bcma_device *core;
 struct tx_status txstatus, *txs;
 u32 s1, s2;
 uint n = 0;
 /*
 * Param 'max_tx_num' indicates max. # tx status to process before
 * break out.
 */

 uint max_tx_num = bound ? TXSBND : -1;

 txs = &txstatus;
 core = wlc_hw->d11core;
 *fatal = false;

 while (n < max_tx_num) {
  s1 = bcma_read32(core, D11REGOFFS(frmtxstatus));
  if (s1 == 0xffffffff) {
   brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit,
      __func__);
   *fatal = true;
   return false;
  }
  /* only process when valid */
  if (!(s1 & TXS_V))
   break;

  s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2));
  txs->status = s1 & TXS_STATUS_MASK;
  txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
  txs->sequence = s2 & TXS_SEQ_MASK;
  txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT;
  txs->lasttxtime = 0;

  *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs);
  if (*fatal)
   return false;
  n++;
 }

 return n >= max_tx_num;
}

static void brcms_c_tbtt(struct brcms_c_info *wlc)
{
 if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC)
  /*
 * DirFrmQ is now valid...defer setting until end
 * of ATIM window
 */

  wlc->qvalid |= MCMD_DIRFRMQVAL;
}

/* set initial host flags value */
static void
brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init)
{
 struct brcms_hardware *wlc_hw = wlc->hw;

 memset(mhfs, 0, MHFMAX * sizeof(u16));

 mhfs[MHF2] |= mhf2_init;

 /* prohibit use of slowclock on multifunction boards */
 if (wlc_hw->boardflags & BFL_NOPLLDOWN)
  mhfs[MHF1] |= MHF1_FORCEFASTCLK;

 if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
  mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
  mhfs[MHF1] |= MHF1_IQSWAP_WAR;
 }
}

static uint
dmareg(uint direction, uint fifonum)
{
 if (direction == DMA_TX)
  return offsetof(struct d11regs, fifo64regs[fifonum].dmaxmt);
 return offsetof(struct d11regs, fifo64regs[fifonum].dmarcv);
}

static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme)
{
 uint i;
 char name[8];
 /*
 * ucode host flag 2 needed for pio mode, independent of band and fifo
 */

 u16 pio_mhf2 = 0;
 struct brcms_hardware *wlc_hw = wlc->hw;
 uint unit = wlc_hw->unit;

 /* name and offsets for dma_attach */
 snprintf(name, sizeof(name), "wl%d", unit);

 if (wlc_hw->di[0] == NULL) { /* Init FIFOs */
  int dma_attach_err = 0;

  /*
 * FIFO 0
 * TX: TX_AC_BK_FIFO (TX AC Background data packets)
 * RX: RX_FIFO (RX data packets)
 */

  wlc_hw->di[0] = dma_attach(name, wlc,
        (wme ? dmareg(DMA_TX, 0) : 0),
        dmareg(DMA_RX, 0),
        (wme ? NTXD : 0), NRXD,
        RXBUFSZ, -1, NRXBUFPOST,
        BRCMS_HWRXOFF);
  dma_attach_err |= (NULL == wlc_hw->di[0]);

  /*
 * FIFO 1
 * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets)
 *   (legacy) TX_DATA_FIFO (TX data packets)
 * RX: UNUSED
 */

  wlc_hw->di[1] = dma_attach(name, wlc,
        dmareg(DMA_TX, 1), 0,
        NTXD, 0, 0, -1, 0, 0);
  dma_attach_err |= (NULL == wlc_hw->di[1]);

  /*
 * FIFO 2
 * TX: TX_AC_VI_FIFO (TX AC Video data packets)
 * RX: UNUSED
 */

  wlc_hw->di[2] = dma_attach(name, wlc,
        dmareg(DMA_TX, 2), 0,
        NTXD, 0, 0, -1, 0, 0);
  dma_attach_err |= (NULL == wlc_hw->di[2]);
  /*
 * FIFO 3
 * TX: TX_AC_VO_FIFO (TX AC Voice data packets)
 *   (legacy) TX_CTL_FIFO (TX control & mgmt packets)
 */

  wlc_hw->di[3] = dma_attach(name, wlc,
        dmareg(DMA_TX, 3),
        0, NTXD, 0, 0, -1,
        0, 0);
  dma_attach_err |= (NULL == wlc_hw->di[3]);
/* Cleaner to leave this as if with AP defined */

  if (dma_attach_err) {
   brcms_err(wlc_hw->d11core,
      "wl%d: wlc_attach: dma_attach failed\n",
      unit);
   return false;
  }

  /* get pointer to dma engine tx flow control variable */
  for (i = 0; i < NFIFO; i++)
   if (wlc_hw->di[i])
    wlc_hw->txavail[i] =
        (uint *) dma_getvar(wlc_hw->di[i],
       "&txavail");
 }

 /* initial ucode host flags */
 brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2);

 return true;
}

static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw)
{
 uint j;

 for (j = 0; j < NFIFO; j++) {
  if (wlc_hw->di[j]) {
   dma_detach(wlc_hw->di[j]);
   wlc_hw->di[j] = NULL;
  }
 }
}

/*
 * Initialize brcms_c_info default values ...
 * may get overrides later in this function
 *  BMAC_NOTES, move low out and resolve the dangling ones
 */

static void brcms_b_info_init(struct brcms_hardware *wlc_hw)
{
 struct brcms_c_info *wlc = wlc_hw->wlc;

 /* set default sw macintmask value */
 wlc->defmacintmask = DEF_MACINTMASK;

 /* various 802.11g modes */
 wlc_hw->shortslot = false;

 wlc_hw->SFBL = RETRY_SHORT_FB;
 wlc_hw->LFBL = RETRY_LONG_FB;

 /* default mac retry limits */
 wlc_hw->SRL = RETRY_SHORT_DEF;
 wlc_hw->LRL = RETRY_LONG_DEF;
 wlc_hw->chanspec = ch20mhz_chspec(1);
}

static void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw)
{
 /* delay before first read of ucode state */
 udelay(40);

 /* wait until ucode is no longer asleep */
 SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) ==
    DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly);
}

/* control chip clock to save power, enable dynamic clock or force fast clock */
static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, enum bcma_clkmode mode)
{
 if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) {
  /* new chips with PMU, CCS_FORCEHT will distribute the HT clock
 * on backplane, but mac core will still run on ALP(not HT) when
 * it enters powersave mode, which means the FCA bit may not be
 * set. Should wakeup mac if driver wants it to run on HT.
 */


  if (wlc_hw->clk) {
   if (mode == BCMA_CLKMODE_FAST) {
    bcma_set32(wlc_hw->d11core,
        D11REGOFFS(clk_ctl_st),
        CCS_FORCEHT);

    udelay(64);

    SPINWAIT(
        ((bcma_read32(wlc_hw->d11core,
          D11REGOFFS(clk_ctl_st)) &
          CCS_HTAVAIL) == 0),
          PMU_MAX_TRANSITION_DLY);
    WARN_ON(!(bcma_read32(wlc_hw->d11core,
     D11REGOFFS(clk_ctl_st)) &
     CCS_HTAVAIL));
   } else {
    if ((ai_get_pmurev(wlc_hw->sih) == 0) &&
        (bcma_read32(wlc_hw->d11core,
     D11REGOFFS(clk_ctl_st)) &
     (CCS_FORCEHT | CCS_HTAREQ)))
     SPINWAIT(
         ((bcma_read32(wlc_hw->d11core,
           offsetof(struct d11regs,
             clk_ctl_st)) &
           CCS_HTAVAIL) == 0),
           PMU_MAX_TRANSITION_DLY);
    bcma_mask32(wlc_hw->d11core,
     D11REGOFFS(clk_ctl_st),
     ~CCS_FORCEHT);
   }
  }
  wlc_hw->forcefastclk = (mode == BCMA_CLKMODE_FAST);
 } else {

  /* old chips w/o PMU, force HT through cc,
 * then use FCA to verify mac is running fast clock
 */


  wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode);

  /* check fast clock is available (if core is not in reset) */
  if (wlc_hw->forcefastclk && wlc_hw->clk)
   WARN_ON(!(bcma_aread32(wlc_hw->d11core, BCMA_IOST) &
      SISF_FCLKA));

  /*
 * keep the ucode wake bit on if forcefastclk is on since we
 * do not want ucode to put us back to slow clock when it dozes
 * for PM mode. Code below matches the wake override bit with
 * current forcefastclk state. Only setting bit in wake_override
 * instead of waking ucode immediately since old code had this
 * behavior. Older code set wlc->forcefastclk but only had the
 * wake happen if the wakup_ucode work (protected by an up
 * check) was executed just below.
 */

  if (wlc_hw->forcefastclk)
   mboolset(wlc_hw->wake_override,
     BRCMS_WAKE_OVERRIDE_FORCEFAST);
  else
   mboolclr(wlc_hw->wake_override,
     BRCMS_WAKE_OVERRIDE_FORCEFAST);
 }
}

/* set or clear ucode host flag bits
 * it has an optimization for no-change write
 * it only writes through shared memory when the core has clock;
 * pre-CLK changes should use wlc_write_mhf to get around the optimization
 *
 *
 * bands values are: BRCM_BAND_AUTO <--- Current band only
 *                   BRCM_BAND_5G   <--- 5G band only
 *                   BRCM_BAND_2G   <--- 2G band only
 *                   BRCM_BAND_ALL  <--- All bands
 */

void
brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val,
      int bands)
{
 u16 save;
 u16 addr[MHFMAX] = {
  M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
  M_HOST_FLAGS5
 };
 struct brcms_hw_band *band;

 if ((val & ~mask) || idx >= MHFMAX)
  return/* error condition */

 switch (bands) {
  /* Current band only or all bands,
 * then set the band to current band
 */

 case BRCM_BAND_AUTO:
 case BRCM_BAND_ALL:
  band = wlc_hw->band;
  break;
 case BRCM_BAND_5G:
  band = wlc_hw->bandstate[BAND_5G_INDEX];
  break;
 case BRCM_BAND_2G:
  band = wlc_hw->bandstate[BAND_2G_INDEX];
  break;
 default:
  band = NULL; /* error condition */
 }

 if (band) {
  save = band->mhfs[idx];
  band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val;

  /* optimization: only write through if changed, and
 * changed band is the current band
 */

  if (wlc_hw->clk && (band->mhfs[idx] != save)
      && (band == wlc_hw->band))
   brcms_b_write_shm(wlc_hw, addr[idx],
        (u16) band->mhfs[idx]);
 }

 if (bands == BRCM_BAND_ALL) {
  wlc_hw->bandstate[0]->mhfs[idx] =
      (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val;
  wlc_hw->bandstate[1]->mhfs[idx] =
      (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val;
 }
}

/* set the maccontrol register to desired reset state and
 * initialize the sw cache of the register
 */

static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw)
{
 /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */
 wlc_hw->maccontrol = 0;
 wlc_hw->suspended_fifos = 0;
 wlc_hw->wake_override = 0;
 wlc_hw->mute_override = 0;
 brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE);
}

/*
 * write the software state of maccontrol and
 * overrides to the maccontrol register
 */

static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw)
{
 u32 maccontrol = wlc_hw->maccontrol;

 /* OR in the wake bit if overridden */
 if (wlc_hw->wake_override)
  maccontrol |= MCTL_WAKE;

 /* set AP and INFRA bits for mute if needed */
 if (wlc_hw->mute_override) {
  maccontrol &= ~(MCTL_AP);
  maccontrol |= MCTL_INFRA;
 }

 bcma_write32(wlc_hw->d11core, D11REGOFFS(maccontrol),
       maccontrol);
}

/* set or clear maccontrol bits */
void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val)
{
 u32 maccontrol;
 u32 new_maccontrol;

 if (val & ~mask)
  return/* error condition */
 maccontrol = wlc_hw->maccontrol;
 new_maccontrol = (maccontrol & ~mask) | val;

 /* if the new maccontrol value is the same as the old, nothing to do */
 if (new_maccontrol == maccontrol)
  return;

 /* something changed, cache the new value */
 wlc_hw->maccontrol = new_maccontrol;

 /* write the new values with overrides applied */
 brcms_c_mctrl_write(wlc_hw);
}

void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
     u32 override_bit)
{
 if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) {
  mboolset(wlc_hw->wake_override, override_bit);
  return;
 }

 mboolset(wlc_hw->wake_override, override_bit);

 brcms_c_mctrl_write(wlc_hw);
 brcms_b_wait_for_wake(wlc_hw);
}

void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
       u32 override_bit)
{
 mboolclr(wlc_hw->wake_override, override_bit);

 if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE))
  return;

 brcms_c_mctrl_write(wlc_hw);
}

/* When driver needs ucode to stop beaconing, it has to make sure that
 * MCTL_AP is clear and MCTL_INFRA is set
 * Mode           MCTL_AP        MCTL_INFRA
 * AP                1              1
 * STA               0              1 <--- This will ensure no beacons
 * IBSS              0              0
 */

static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw)
{
 wlc_hw->mute_override = 1;

 /* if maccontrol already has AP == 0 and INFRA == 1 without this
 * override, then there is no change to write
 */

 if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
  return;

 brcms_c_mctrl_write(wlc_hw);
}

/* Clear the override on AP and INFRA bits */
static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw)
{
 if (wlc_hw->mute_override == 0)
  return;

 wlc_hw->mute_override = 0;

 /* if maccontrol already has AP == 0 and INFRA == 1 without this
 * override, then there is no change to write
 */

 if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
  return;

 brcms_c_mctrl_write(wlc_hw);
}

/*
 * Write a MAC address to the given match reg offset in the RXE match engine.
 */

static void
brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset,
         const u8 *addr)
{
 struct bcma_device *core = wlc_hw->d11core;
 u16 mac_l;
 u16 mac_m;
 u16 mac_h;

 brcms_dbg_rx(core, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit);

 mac_l = addr[0] | (addr[1] << 8);
 mac_m = addr[2] | (addr[3] << 8);
 mac_h = addr[4] | (addr[5] << 8);

 /* enter the MAC addr into the RXE match registers */
 bcma_write16(core, D11REGOFFS(rcm_ctl),
       RCM_INC_DATA | match_reg_offset);
 bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_l);
 bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_m);
 bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_h);
}

void
brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len,
       void *buf)
{
 struct bcma_device *core = wlc_hw->d11core;
 u32 word;
 __le32 word_le;
 __be32 word_be;
 bool be_bit;
 brcms_dbg_info(core, "wl%d\n", wlc_hw->unit);

 bcma_write32(core, D11REGOFFS(tplatewrptr), offset);

 /* if MCTL_BIGEND bit set in mac control register,
 * the chip swaps data in fifo, as well as data in
 * template ram
 */

 be_bit = (bcma_read32(core, D11REGOFFS(maccontrol)) & MCTL_BIGEND) != 0;

 while (len > 0) {
  memcpy(&word, buf, sizeof(u32));

  if (be_bit) {
   word_be = cpu_to_be32(word);
   word = *(u32 *)&word_be;
  } else {
   word_le = cpu_to_le32(word);
   word = *(u32 *)&word_le;
  }

  bcma_write32(core, D11REGOFFS(tplatewrdata), word);

  buf = (u8 *) buf + sizeof(u32);
  len -= sizeof(u32);
 }
}

static void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin)
{
 wlc_hw->band->CWmin = newmin;

 bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr),
       OBJADDR_SCR_SEL | S_DOT11_CWMIN);
 (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr));
 bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmin);
}

static void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax)
{
 wlc_hw->band->CWmax = newmax;

 bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr),
       OBJADDR_SCR_SEL | S_DOT11_CWMAX);
 (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr));
 bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmax);
}

void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw)
{
 bool fastclk;

 /* request FAST clock if not on */
 fastclk = wlc_hw->forcefastclk;
 if (!fastclk)
  brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST);

 wlc_phy_bw_state_set(wlc_hw->band->pi, bw);

 brcms_b_phy_reset(wlc_hw);
 wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi));

 /* restore the clk */
 if (!fastclk)
  brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC);
}

static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw)
{
 u16 v;
 struct brcms_c_info *wlc = wlc_hw->wlc;
 /* update SYNTHPU_DLY */

 if (BRCMS_ISLCNPHY(wlc->band))
  v = SYNTHPU_DLY_LPPHY_US;
 else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3)))
  v = SYNTHPU_DLY_NPHY_US;
 else
  v = SYNTHPU_DLY_BPHY_US;

 brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v);
}

static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw)
{
 u16 phyctl;
 u16 phytxant = wlc_hw->bmac_phytxant;
 u16 mask = PHY_TXC_ANT_MASK;

 /* set the Probe Response frame phy control word */
 phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
 phyctl = (phyctl & ~mask) | phytxant;
 brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);

 /* set the Response (ACK/CTS) frame phy control word */
 phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD);
 phyctl = (phyctl & ~mask) | phytxant;
 brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
}

static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
      u8 rate)
{
 uint i;
 u8 plcp_rate = 0;
 struct plcp_signal_rate_lookup {
  u8 rate;
  u8 signal_rate;
 };
 /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
 const struct plcp_signal_rate_lookup rate_lookup[] = {
  {BRCM_RATE_6M, 0xB},
  {BRCM_RATE_9M, 0xF},
  {BRCM_RATE_12M, 0xA},
  {BRCM_RATE_18M, 0xE},
  {BRCM_RATE_24M, 0x9},
  {BRCM_RATE_36M, 0xD},
  {BRCM_RATE_48M, 0x8},
  {BRCM_RATE_54M, 0xC}
 };

 for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
  if (rate == rate_lookup[i].rate) {
   plcp_rate = rate_lookup[i].signal_rate;
   break;
  }
 }

 /* Find the SHM pointer to the rate table entry by looking in the
 * Direct-map Table
 */

 return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
}

static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw)
{
 u8 rate;
 u8 rates[8] = {
  BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M,
  BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M
 };
 u16 entry_ptr;
 u16 pctl1;
 uint i;

 if (!BRCMS_PHY_11N_CAP(wlc_hw->band))
  return;

 /* walk the phy rate table and update the entries */
 for (i = 0; i < ARRAY_SIZE(rates); i++) {
  rate = rates[i];

  entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate);

  /* read the SHM Rate Table entry OFDM PCTL1 values */
  pctl1 =
      brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);

  /* modify the value */
  pctl1 &= ~PHY_TXC1_MODE_MASK;
  pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);

  /* Update the SHM Rate Table entry OFDM PCTL1 values */
  brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
       pctl1);
 }
}

/* band-specific init */
static void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec)
{
 struct brcms_hardware *wlc_hw = wlc->hw;

 brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit,
      wlc_hw->band->bandunit);

 brcms_c_ucode_bsinit(wlc_hw);

 wlc_phy_init(wlc_hw->band->pi, chanspec);

 brcms_c_ucode_txant_set(wlc_hw);

 /*
 * cwmin is band-specific, update hardware
 * with value for current band
 */

 brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
 brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax);

 brcms_b_update_slot_timing(wlc_hw,
       wlc_hw->band->bandtype == BRCM_BAND_5G ?
       true : wlc_hw->shortslot);

 /* write phytype and phyvers */
 brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype);
 brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev);

 /*
 * initialize the txphyctl1 rate table since
 * shmem is shared between bands
 */

 brcms_upd_ofdm_pctl1_table(wlc_hw);

 brcms_b_upd_synthpu(wlc_hw);
}

/* Perform a soft reset of the PHY PLL */
void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw)
{
 ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr),
    ~0, 0);
 udelay(1);
 ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data),
    0x4, 0);
 udelay(1);
 ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data),
    0x4, 4);
 udelay(1);
 ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data),
    0x4, 0);
 udelay(1);
}

/* light way to turn on phy clock without reset for NPHY only
 *  refer to brcms_b_core_phy_clk for full version
 */

void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk)
{
 /* support(necessary for NPHY and HYPHY) only */
 if (!BRCMS_ISNPHY(wlc_hw->band))
  return;

 if (ON == clk)
  brcms_b_core_ioctl(wlc_hw, SICF_FGC, SICF_FGC);
 else
  brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0);

}

void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk)
{
 if (ON == clk)
  brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, SICF_MPCLKE);
 else
  brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, 0);
}

void brcms_b_phy_reset(struct brcms_hardware *wlc_hw)
{
 struct brcms_phy_pub *pih = wlc_hw->band->pi;
 u32 phy_bw_clkbits;

 brcms_dbg_info(wlc_hw->d11core, "wl%d: reset phy\n", wlc_hw->unit);

 if (pih == NULL)
  return;

 phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi);

 /* Specific reset sequence required for NPHY rev 3 and 4 */
 if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) &&
     NREV_LE(wlc_hw->band->phyrev, 4)) {
  /* Set the PHY bandwidth */
  brcms_b_core_ioctl(wlc_hw, SICF_BWMASK, phy_bw_clkbits);

  udelay(1);

  /* Perform a soft reset of the PHY PLL */
  brcms_b_core_phypll_reset(wlc_hw);

  /* reset the PHY */
  brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE),
       (SICF_PRST | SICF_PCLKE));
 } else {
  brcms_b_core_ioctl(wlc_hw,
       (SICF_PRST | SICF_PCLKE | SICF_BWMASK),
       (SICF_PRST | SICF_PCLKE | phy_bw_clkbits));
 }

 udelay(2);
 brcms_b_core_phy_clk(wlc_hw, ON);

 wlc_phy_anacore(pih, ON);
}

/* switch to and initialize new band */
static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit,
       u16 chanspec) {
 struct brcms_c_info *wlc = wlc_hw->wlc;
 u32 macintmask;

 /* Enable the d11 core before accessing it */
 if (!bcma_core_is_enabled(wlc_hw->d11core)) {
  bcma_core_enable(wlc_hw->d11core, 0);
  brcms_c_mctrl_reset(wlc_hw);
 }

 macintmask = brcms_c_setband_inact(wlc, bandunit);

 if (!wlc_hw->up)
  return;

 brcms_b_core_phy_clk(wlc_hw, ON);

 /* band-specific initializations */
 brcms_b_bsinit(wlc, chanspec);

 /*
 * If there are any pending software interrupt bits,
 * then replace these with a harmless nonzero value
 * so brcms_c_dpc() will re-enable interrupts when done.
 */

 if (wlc->macintstatus)
  wlc->macintstatus = MI_DMAINT;

 /* restore macintmask */
 brcms_intrsrestore(wlc->wl, macintmask);

 /* ucode should still be suspended.. */
 WARN_ON((bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) &
   MCTL_EN_MAC) != 0);
}

static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw)
{

 /* reject unsupported corerev */
 if (!CONF_HAS(D11CONF, wlc_hw->corerev)) {
  wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n",
     wlc_hw->corerev);
  return false;
 }

 return true;
}

/* Validate some board info parameters */
static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw)
{
 uint boardrev = wlc_hw->boardrev;

 /* 4 bits each for board type, major, minor, and tiny version */
 uint brt = (boardrev & 0xf000) >> 12;
 uint b0 = (boardrev & 0xf00) >> 8;
 uint b1 = (boardrev & 0xf0) >> 4;
 uint b2 = boardrev & 0xf;

 /* voards from other vendors are always considered valid */
 if (ai_get_boardvendor(wlc_hw->sih) != PCI_VENDOR_ID_BROADCOM)
  return true;

 /* do some boardrev sanity checks when boardvendor is Broadcom */
 if (boardrev == 0)
  return false;

 if (boardrev <= 0xff)
  return true;

 if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9)
  || (b2 > 9))
  return false;

 return true;
}

static void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ALEN])
{
 struct ssb_sprom *sprom = &wlc_hw->d11core->bus->sprom;

 /* If macaddr exists, use it (Sromrev4, CIS, ...). */
 if (!is_zero_ether_addr(sprom->il0mac)) {
  memcpy(etheraddr, sprom->il0mac, ETH_ALEN);
  return;
 }

 if (wlc_hw->_nbands > 1)
  memcpy(etheraddr, sprom->et1mac, ETH_ALEN);
 else
  memcpy(etheraddr, sprom->il0mac, ETH_ALEN);
}

/* power both the pll and external oscillator on/off */
static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want)
{
 brcms_dbg_info(wlc_hw->d11core, "wl%d: want %d\n", wlc_hw->unit, want);

 /*
 * dont power down if plldown is false or
 * we must poll hw radio disable
 */

 if (!want && wlc_hw->pllreq)
  return;

 wlc_hw->sbclk = want;
 if (!wlc_hw->sbclk) {
  wlc_hw->clk = false;
  if (wlc_hw->band && wlc_hw->band->pi)
   wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
 }
}

/*
 * Return true if radio is disabled, otherwise false.
 * hw radio disable signal is an external pin, users activate it asynchronously
 * this function could be called when driver is down and w/o clock
 * it operates on different registers depending on corerev and boardflag.
 */

static bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw)
{
 bool v, clk, xtal;
 u32 flags = 0;

 xtal = wlc_hw->sbclk;
 if (!xtal)
  brcms_b_xtal(wlc_hw, ON);

 /* may need to take core out of reset first */
 clk = wlc_hw->clk;
 if (!clk) {
  /*
 * mac no longer enables phyclk automatically when driver
 * accesses phyreg throughput mac. This can be skipped since
 * only mac reg is accessed below
 */

  if (D11REV_GE(wlc_hw->corerev, 18))
   flags |= SICF_PCLKE;

  /*
 * TODO: test suspend/resume
 *
 * AI chip doesn't restore bar0win2 on
 * hibernation/resume, need sw fixup
 */


  bcma_core_enable(wlc_hw->d11core, flags);
  brcms_c_mctrl_reset(wlc_hw);
 }

 v = ((bcma_read32(wlc_hw->d11core,
     D11REGOFFS(phydebug)) & PDBG_RFD) != 0);

 /* put core back into reset */
 if (!clk)
  bcma_core_disable(wlc_hw->d11core, 0);

 if (!xtal)
  brcms_b_xtal(wlc_hw, OFF);

 return v;
}

static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo)
{
 struct dma_pub *di = wlc_hw->di[fifo];
 return dma_rxreset(di);
}

/* d11 core reset
 *   ensure fask clock during reset
 *   reset dma
 *   reset d11(out of reset)
 *   reset phy(out of reset)
 *   clear software macintstatus for fresh new start
 * one testing hack wlc_hw->noreset will bypass the d11/phy reset
 */

void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags)
{
 uint i;
 bool fastclk;

 if (flags == BRCMS_USE_COREFLAGS)
  flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0);

 brcms_dbg_info(wlc_hw->d11core, "wl%d: core reset\n", wlc_hw->unit);

 /* request FAST clock if not on  */
 fastclk = wlc_hw->forcefastclk;
 if (!fastclk)
  brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST);

 /* reset the dma engines except first time thru */
 if (bcma_core_is_enabled(wlc_hw->d11core)) {
  for (i = 0; i < NFIFO; i++)
   if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i])))
    brcms_err(wlc_hw->d11core, "wl%d: %s: "
       "dma_txreset[%d]: cannot stop dma\n",
        wlc_hw->unit, __func__, i);

  if ((wlc_hw->di[RX_FIFO])
      && (!wlc_dma_rxreset(wlc_hw, RX_FIFO)))
   brcms_err(wlc_hw->d11core, "wl%d: %s: dma_rxreset"
      "[%d]: cannot stop dma\n",
      wlc_hw->unit, __func__, RX_FIFO);
 }
 /* if noreset, just stop the psm and return */
 if (wlc_hw->noreset) {
  wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */
  brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0);
  return;
 }

 /*
 * mac no longer enables phyclk automatically when driver accesses
 * phyreg throughput mac, AND phy_reset is skipped at early stage when
 * band->pi is invalid. need to enable PHY CLK
 */

 if (D11REV_GE(wlc_hw->corerev, 18))
  flags |= SICF_PCLKE;

 /*
 * reset the core
 * In chips with PMU, the fastclk request goes through d11 core
 * reg 0x1e0, which is cleared by the core_reset. have to re-request it.
 *
 * This adds some delay and we can optimize it by also requesting
 * fastclk through chipcommon during this period if necessary. But
 * that has to work coordinate with other driver like mips/arm since
 * they may touch chipcommon as well.
 */

 wlc_hw->clk = false;
 bcma_core_enable(wlc_hw->d11core, flags);
 wlc_hw->clk = true;
 if (wlc_hw->band && wlc_hw->band->pi)
  wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true);

 brcms_c_mctrl_reset(wlc_hw);

 if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU)
  brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST);

 brcms_b_phy_reset(wlc_hw);

 /* turn on PHY_PLL */
 brcms_b_core_phypll_ctl(wlc_hw, true);

 /* clear sw intstatus */
 wlc_hw->wlc->macintstatus = 0;

 /* restore the clk setting */
 if (!fastclk)
  brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC);
}

/* txfifo sizes needs to be modified(increased) since the newer cores
 * have more memory.
 */

static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw)
{
 struct bcma_device *core = wlc_hw->d11core;
 u16 fifo_nu;
 u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk;
 u16 txfifo_def, txfifo_def1;
 u16 txfifo_cmd;

 /* tx fifos start at TXFIFO_START_BLK from the Base address */
 txfifo_startblk = TXFIFO_START_BLK;

 /* sequence of operations:  reset fifo, set fifo size, reset fifo */
 for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) {

  txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu];
  txfifo_def = (txfifo_startblk & 0xff) |
      (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT);
  txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) |
      ((((txfifo_endblk -
   1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT);
  txfifo_cmd =
      TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT);

  bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd);
  bcma_write16(core, D11REGOFFS(xmtfifodef), txfifo_def);
  bcma_write16(core, D11REGOFFS(xmtfifodef1), txfifo_def1);

  bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd);

  txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu];
 }
 /*
 * need to propagate to shm location to be in sync since ucode/hw won't
 * do this
 */

 brcms_b_write_shm(wlc_hw, M_FIFOSIZE0,
      wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]);
 brcms_b_write_shm(wlc_hw, M_FIFOSIZE1,
      wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]);
 brcms_b_write_shm(wlc_hw, M_FIFOSIZE2,
      ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw->
       xmtfifo_sz[TX_AC_BK_FIFO]));
 brcms_b_write_shm(wlc_hw, M_FIFOSIZE3,
      ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw->
       xmtfifo_sz[TX_BCMC_FIFO]));
}

/* This function is used for changing the tsf frac register
 * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz
 * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz
 * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz
 * HTPHY Formula is 2^26/freq(MHz) e.g.
 * For spuron2 - 126MHz -> 2^26/126 = 532610.0
 *  - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082
 * For spuron: 123MHz -> 2^26/123    = 545600.5
 *  - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341
 * For spur off: 120MHz -> 2^26/120    = 559240.5
 *  - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889
 */


void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode)
{
 struct bcma_device *core = wlc_hw->d11core;

 if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43224) ||
     (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) {
  if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x2082);
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8);
  } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x5341);
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8);
  } else { /* 120Mhz */
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x8889);
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8);
  }
 } else if (BRCMS_ISLCNPHY(wlc_hw->band)) {
  if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x7CE0);
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC);
  } else { /* 80Mhz */
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0xCCCD);
   bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC);
  }
 }
}

void brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr)
{
 memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
 wlc->bsscfg->type = BRCMS_TYPE_STATION;
}

void brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid,
        u8 *ssid, size_t ssid_len)
{
 brcms_c_set_ssid(wlc, ssid, ssid_len);

 memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
 memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID));
 wlc->bsscfg->type = BRCMS_TYPE_AP;

 brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA);
}

void brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr)
{
 memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr));
 wlc->bsscfg->type = BRCMS_TYPE_ADHOC;

 brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0);
}

/* Initialize GPIOs that are controlled by D11 core */
static void brcms_c_gpio_init(struct brcms_c_info *wlc)
{
 struct brcms_hardware *wlc_hw = wlc->hw;
 u32 gc, gm;

 /* use GPIO select 0 to get all gpio signals from the gpio out reg */
 brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0);

 /*
 * Common GPIO setup:
 *      G0 = LED 0 = WLAN Activity
 *      G1 = LED 1 = WLAN 2.4 GHz Radio State
 *      G2 = LED 2 = WLAN 5 GHz Radio State
 *      G4 = radio disable input (HI enabled, LO disabled)
 */


 gc = gm = 0;

 /* Allocate GPIOs for mimo antenna diversity feature */
 if (wlc_hw->antsel_type == ANTSEL_2x3) {
  /* Enable antenna diversity, use 2x3 mode */
  brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
        MHF3_ANTSEL_EN, BRCM_BAND_ALL);
  brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE,
        MHF3_ANTSEL_MODE, BRCM_BAND_ALL);

  /* init superswitch control */
  wlc_phy_antsel_init(wlc_hw->band->pi, false);

 } else if (wlc_hw->antsel_type == ANTSEL_2x4) {
  gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13);
  /*
 * The board itself is powered by these GPIOs
 * (when not sending pattern) so set them high
 */

  bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_oe),
      (BOARD_GPIO_12 | BOARD_GPIO_13));
  bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_out),
      (BOARD_GPIO_12 | BOARD_GPIO_13));

  /* Enable antenna diversity, use 2x4 mode */
  brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
        MHF3_ANTSEL_EN, BRCM_BAND_ALL);
  brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0,
        BRCM_BAND_ALL);

  /* Configure the desired clock to be 4Mhz */
  brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV,
       ANTSEL_CLKDIV_4MHZ);
 }

 /*
 * gpio 9 controls the PA. ucode is responsible
 * for wiggling out and oe
 */

 if (wlc_hw->boardflags & BFL_PACTRL)
  gm |= gc |= BOARD_GPIO_PACTRL;

 /* apply to gpiocontrol register */
 bcma_chipco_gpio_control(&wlc_hw->d11core->bus->drv_cc, gm, gc);
}

static void brcms_ucode_write(struct brcms_hardware *wlc_hw,
         const __le32 ucode[], const size_t nbytes)
{
 struct bcma_device *core = wlc_hw->d11core;
 uint i;
 uint count;

 brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit);

 count = (nbytes / sizeof(u32));

 bcma_write32(core, D11REGOFFS(objaddr),
       OBJADDR_AUTO_INC | OBJADDR_UCM_SEL);
 (void)bcma_read32(core, D11REGOFFS(objaddr));
 for (i = 0; i < count; i++)
  bcma_write32(core, D11REGOFFS(objdata), le32_to_cpu(ucode[i]));

}

static void brcms_ucode_download(struct brcms_hardware *wlc_hw)
{
 struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode;

 if (wlc_hw->ucode_loaded)
  return;

 if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) {
  if (BRCMS_ISNPHY(wlc_hw->band)) {
   brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo,
       ucode->bcm43xx_16_mimosz);
   wlc_hw->ucode_loaded = true;
  } else
   brcms_err(wlc_hw->d11core,
      "%s: wl%d: unsupported phy in corerev %d\n",
      __func__, wlc_hw->unit, wlc_hw->corerev);
 } else if (D11REV_IS(wlc_hw->corerev, 24)) {
  if (BRCMS_ISLCNPHY(wlc_hw->band)) {
   brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn,
       ucode->bcm43xx_24_lcnsz);
   wlc_hw->ucode_loaded = true;
  } else {
   brcms_err(wlc_hw->d11core,
      "%s: wl%d: unsupported phy in corerev %d\n",
      __func__, wlc_hw->unit, wlc_hw->corerev);
  }
 }
}

void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant)
{
 /* update sw state */
 wlc_hw->bmac_phytxant = phytxant;

 /* push to ucode if up */
 if (!wlc_hw->up)
  return;
 brcms_c_ucode_txant_set(wlc_hw);

}

u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw)
{
 return (u16) wlc_hw->wlc->stf->txant;
}

void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type)
{
 wlc_hw->antsel_type = antsel_type;

 /* Update the antsel type for phy module to use */
 wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type);
}

static void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw)
{
 bool fatal = false;
 uint unit;
 uint intstatus, idx;
 struct bcma_device *core = wlc_hw->d11core;

 unit = wlc_hw->unit;

 for (idx = 0; idx < NFIFO; idx++) {
  /* read intstatus register and ignore any non-error bits */
  intstatus =
   bcma_read32(core,
        D11REGOFFS(intctrlregs[idx].intstatus)) &
   I_ERRORS;
  if (!intstatus)
   continue;

  brcms_dbg_int(core, "wl%d: intstatus%d 0x%x\n",
         unit, idx, intstatus);

  if (intstatus & I_RO) {
   brcms_err(core, "wl%d: fifo %d: receive fifo "
      "overflow\n", unit, idx);
   fatal = true;
  }

  if (intstatus & I_PC) {
   brcms_err(core, "wl%d: fifo %d: descriptor error\n",
      unit, idx);
   fatal = true;
  }

  if (intstatus & I_PD) {
   brcms_err(core, "wl%d: fifo %d: data error\n", unit,
      idx);
   fatal = true;
  }

  if (intstatus & I_DE) {
   brcms_err(core, "wl%d: fifo %d: descriptor protocol "
      "error\n", unit, idx);
   fatal = true;
  }

  if (intstatus & I_RU)
   brcms_err(core, "wl%d: fifo %d: receive descriptor "
      "underflow\n", idx, unit);

  if (intstatus & I_XU) {
   brcms_err(core, "wl%d: fifo %d: transmit fifo "
      "underflow\n", idx, unit);
   fatal = true;
  }

  if (fatal) {
   brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */
   break;
  } else
   bcma_write32(core,
         D11REGOFFS(intctrlregs[idx].intstatus),
         intstatus);
 }
}

void brcms_c_intrson(struct brcms_c_info *wlc)
{
 struct brcms_hardware *wlc_hw = wlc->hw;
 wlc->macintmask = wlc->defmacintmask;
 bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask);
}

u32 brcms_c_intrsoff(struct brcms_c_info *wlc)
{
 struct brcms_hardware *wlc_hw = wlc->hw;
 u32 macintmask;

 if (!wlc_hw->clk)
  return 0;

 macintmask = wlc->macintmask; /* isr can still happen */

 bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), 0);
 (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(macintmask));
 udelay(1);  /* ensure int line is no longer driven */
 wlc->macintmask = 0;

 /* return previous macintmask; resolve race between us and our isr */
 return wlc->macintstatus ? 0 : macintmask;
}

void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask)
{
 struct brcms_hardware *wlc_hw = wlc->hw;
 if (!wlc_hw->clk)
  return;

 wlc->macintmask = macintmask;
 bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask);
}

/* assumes that the d11 MAC is enabled */
static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw,
        uint tx_fifo)
{
 u8 fifo = 1 << tx_fifo;

 /* Two clients of this code, 11h Quiet period and scanning. */

 /* only suspend if not already suspended */
 if ((wlc_hw->suspended_fifos & fifo) == fifo)
  return;

 /* force the core awake only if not already */
 if (wlc_hw->suspended_fifos == 0)
  brcms_c_ucode_wake_override_set(wlc_hw,
      BRCMS_WAKE_OVERRIDE_TXFIFO);

 wlc_hw->suspended_fifos |= fifo;

 if (wlc_hw->di[tx_fifo]) {
  /*
 * Suspending AMPDU transmissions in the middle can cause
 * underflow which may result in mismatch between ucode and
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=90 H=92 G=90

¤ Dauer der Verarbeitung: 0.62 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge