/* * Copyright (c) 2010 Broadcom Corporation * * 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 #include #include #include #include #include #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" /* * Indication for txflowcontrol that all priority bits in * TXQ_STOP_FOR_PRIOFC_MASK are to be considered. */ #define ALLPRIO -1 /* 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_MIN_BEACON_PERIOD 1 #define DOT11_MAX_BEACON_PERIOD 0xFFFF #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 */ /* precedences numbers for wlc queues. These are twice as may levels as * 802.1D priorities. * Odd numbers are used for HI priority traffic at same precedence levels * These constants are used ONLY by wlc_prio2prec_map. Do not use them * elsewhere. */ #define _BRCMS_PREC_NONE 0 /* None = - */ #define _BRCMS_PREC_BK 2 /* BK - Background */ #define _BRCMS_PREC_BE 4 /* BE - Best-effort */ #define _BRCMS_PREC_EE 6 /* EE - Excellent-effort */ #define _BRCMS_PREC_CL 8 /* CL - Controlled Load */ #define _BRCMS_PREC_VI 10 /* Vi - Video */ #define _BRCMS_PREC_VO 12 /* Vo - Voice */ #define _BRCMS_PREC_NC 14 /* NC - Network Control */ /* 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 /* Max # of entries in Tx FIFO based on 4kb page size */ #define NTXD 256 /* Max # of entries in Rx FIFO based on 4kb page size */ #define NRXD 256 /* try to keep this # rbufs posted to the chip */ #define NRXBUFPOST 32 /* data msg txq hiwat mark */ #define BRCMS_DATAHIWAT 50 /* max # frames to process in brcms_c_recv() */ #define RXBND 8 /* max # tx status to process in wlc_txstatus() */ #define TXSBND 8 /* brcmu_format_flags() bit description structure */ struct brcms_c_bit_desc { u32 bit; const char *name; }; /* * 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 20 struct d11init { __le16 addr; __le16 size; __le32 value; }; struct edcf_acparam { u8 ACI; u8 ECW; u16 TXOP; } __packed; const u8 prio2fifo[NUMPRIO] = { TX_AC_BE_FIFO, /* 0 BE AC_BE Best Effort */ TX_AC_BK_FIFO, /* 1 BK AC_BK Background */ TX_AC_BK_FIFO, /* 2 -- AC_BK Background */ TX_AC_BE_FIFO, /* 3 EE AC_BE Best Effort */ TX_AC_VI_FIFO, /* 4 CL AC_VI Video */ TX_AC_VI_FIFO, /* 5 VI AC_VI Video */ TX_AC_VO_FIFO, /* 6 VO AC_VO Voice */ TX_AC_VO_FIFO /* 7 NC AC_VO Voice */ }; /* debug/trace */ uint brcm_msg_level = #if defined(DEBUG) LOG_ERROR_VAL; #else 0; #endif /* DEBUG */ /* 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 }; /* 802.1D Priority to precedence queue mapping */ const u8 wlc_prio2prec_map[] = { _BRCMS_PREC_BE, /* 0 BE - Best-effort */ _BRCMS_PREC_BK, /* 1 BK - Background */ _BRCMS_PREC_NONE, /* 2 None = - */ _BRCMS_PREC_EE, /* 3 EE - Excellent-effort */ _BRCMS_PREC_CL, /* 4 CL - Controlled Load */ _BRCMS_PREC_VI, /* 5 Vi - Video */ _BRCMS_PREC_VO, /* 6 Vo - Voice */ _BRCMS_PREC_NC, /* 7 NC - Network Control */ }; static const u16 xmtfifo_sz[][NFIFO] = { /* 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}, }; #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][0]; #endif #ifdef DEBUG /* pointer to most recently allocated wl/wlc */ static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); #endif /* 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 s16 brcms_txpktpendtot(struct brcms_c_info *wlc) { return wlc->core->txpktpend[0] + wlc->core->txpktpend[1] + wlc->core->txpktpend[2] + wlc->core->txpktpend[3]; } 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]); kfree(wlc->corestate->macstat_snapshot); kfree(wlc->corestate); kfree(wlc->hw->bandstate[0]); kfree(wlc->hw); /* free the wlc */ kfree(wlc); wlc = NULL; } static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) { struct brcms_bss_cfg *cfg; cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC); if (cfg == NULL) goto fail; cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), 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(struct brcms_c_info), GFP_ATOMIC); if (wlc == NULL) { *err = 1002; goto fail; } /* allocate struct brcms_c_pub state structure */ wlc->pub = kzalloc(sizeof(struct brcms_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(struct brcms_hardware), GFP_ATOMIC); if (wlc->hw == NULL) { *err = 1005; goto fail; } wlc->hw->wlc = wlc; wlc->hw->bandstate[0] = kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, 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 = kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC); if (wlc->modulecb == NULL) { *err = 1009; goto fail; } wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), 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(struct brcms_protection), GFP_ATOMIC); if (wlc->protection == NULL) { *err = 1016; goto fail; } wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC); if (wlc->stf == NULL) { *err = 1017; goto fail; } wlc->bandstate[0] = kzalloc(sizeof(struct brcms_band)*MAXBANDS, 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(struct brcms_core), GFP_ATOMIC); if (wlc->corestate == NULL) { *err = 1026; goto fail; } wlc->corestate->macstat_snapshot = kzalloc(sizeof(struct macstat), 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) { wiphy_err(wlc->wiphy, "wl%d: WAR: using rate of 1 mbps\n", wlc->pub->unit); rate = BRCM_RATE_1M; } BCMMSG(wlc->wiphy, "wl%d: rspec 0x%x, preamble_type %d, len%d\n", wlc->pub->unit, ratespec, preamble_type, mac_len); 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; BCMMSG(wlc_hw->wlc->wiphy, "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; 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 wiphy *wiphy = wlc_hw->wlc->wiphy; 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, 23)) { if (BRCMS_ISNPHY(wlc_hw->band)) brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); else wiphy_err(wiphy, "%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 wiphy_err(wiphy, "%s: wl%d: unsupported phy in" " core rev %d\n", __func__, wlc_hw->unit, wlc_hw->corerev); } else { wiphy_err(wiphy, "%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) { BCMMSG(wlc_hw->wlc->wiphy, "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) { BCMMSG(wlc_hw->wlc->wiphy, "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; BCMMSG(wlc->wiphy, "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; uint queue; struct d11txh *txh; struct scb *scb = NULL; bool free_pdu; int tx_rts, tx_frame_count, tx_rts_count; uint totlen, supr_status; bool lastframe; struct ieee80211_hdr *h; u16 mcl; struct ieee80211_tx_info *tx_info; struct ieee80211_tx_rate *txrate; int i; /* 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)) { BCMMSG(wlc->wiphy, "INTERMEDIATE but not AMPDU\n"); return false; } queue = txs->frameid & TXFID_QUEUE_MASK; if (queue >= NFIFO) { p = NULL; goto fatal; } p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); if (p == NULL) goto fatal; txh = (struct d11txh *) (p->data); mcl = le16_to_cpu(txh->MacTxControlLow); if (txs->phyerr) { if (brcm_msg_level & LOG_ERROR_VAL) { wiphy_err(wlc->wiphy, "phyerr 0x%x, rate 0x%x\n", txs->phyerr, txh->MainRates); brcms_c_print_txdesc(txh); } brcms_c_print_txstatus(txs); } if (txs->frameid != le16_to_cpu(txh->TxFrameID)) goto fatal; tx_info = IEEE80211_SKB_CB(p); h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); if (tx_info->control.sta) scb = &wlc->pri_scb; if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); return false; } supr_status = txs->status & TX_STATUS_SUPR_MASK; if (supr_status == TX_STATUS_SUPR_BADCH) BCMMSG(wlc->wiphy, "%s: Pkt tx suppressed, possibly channel %d\n", __func__, CHSPEC_CHANNEL(wlc->default_bss->chanspec)); tx_rts = le16_to_cpu(txh->MacTxControlLow) & TXC_SENDRTS; tx_frame_count = (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT; tx_rts_count = (txs->status & TX_STATUS_RTS_RTX_MASK) >> TX_STATUS_RTS_RTX_SHIFT; lastframe = !ieee80211_has_morefrags(h->frame_control); if (!lastframe) { wiphy_err(wlc->wiphy, "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 treshold. */ 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; } totlen = p->len; free_pdu = true; brcms_c_txfifo_complete(wlc, queue, 1); 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 { wiphy_err(wlc->wiphy, "%s: Not last frame => not calling " "tx_status\n", __func__); } return false; fatal: if (p) brcmu_pkt_buf_free_skb(p); return true; } /* 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) { bool morepending = false; struct brcms_c_info *wlc = wlc_hw->wlc; 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; BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit); txs = &txstatus; core = wlc_hw->d11core; *fatal = false; s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); while (!(*fatal) && (s1 & TXS_V)) { if (s1 == 0xffffffff) { wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit, __func__); return morepending; } 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); /* !give others some time to run! */ if (++n >= max_tx_num) break; s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); } if (*fatal) return 0; if (n >= max_tx_num) morepending = true; if (!pktq_empty(&wlc->pkt_queue->q)) brcms_c_send_q(wlc); return morepending; } static void brcms_c_tbtt(struct brcms_c_info *wlc) { if (!wlc->bsscfg->BSS) /* * 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; struct wiphy *wiphy = wlc->wiphy; /* 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_hw->sih, wlc_hw->d11core, (wme ? dmareg(DMA_TX, 0) : 0), dmareg(DMA_RX, 0), (wme ? NTXD : 0), NRXD, RXBUFSZ, -1, NRXBUFPOST, BRCMS_HWRXOFF, &brcm_msg_level); 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_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 1), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); 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_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 2), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); 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_hw->sih, wlc_hw->d11core, dmareg(DMA_TX, 3), 0, NTXD, 0, 0, -1, 0, 0, &brcm_msg_level); dma_attach_err |= (NULL == wlc_hw->di[3]); /* Cleaner to leave this as if with AP defined */ if (dma_attach_err) { wiphy_err(wiphy, "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; BCMMSG(wlc_hw->wlc->wiphy, "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; BCMMSG(wlc_hw->wlc->wiphy, "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; BCMMSG(wlc->wiphy, "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) { BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit); 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; bool phy_in_reset = false; BCMMSG(wlc_hw->wlc->wiphy, "wl%d\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)); phy_in_reset = true; } 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); if (pih) 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, 6); return; } if (wlc_hw->_nbands > 1) memcpy(etheraddr, sprom->et1mac, 6); else memcpy(etheraddr, sprom->il0mac, 6); } /* power both the pll and external oscillator on/off */ static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) { BCMMSG(wlc_hw->wlc->wiphy, "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 */ 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); BCMMSG(wlc_hw->wlc->wiphy, "wl%d\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]))) wiphy_err(wlc_hw->wlc->wiphy, "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))) wiphy_err(wlc_hw->wlc->wiphy, "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 */ 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) == BCM43224_CHIP_ID) || (ai_get_chip_id(wlc_hw->sih) == BCM43225_CHIP_ID)) { 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); } } } /* 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; BCMMSG(wlc_hw->wlc->wiphy, "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_c_info *wlc; struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; wlc = wlc_hw->wlc; if (wlc_hw->ucode_loaded) return; if (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 wiphy_err(wlc->wiphy, "%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 { wiphy_err(wlc->wiphy, "%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; struct wiphy *wiphy = wlc_hw->wlc->wiphy; 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; BCMMSG(wlc_hw->wlc->wiphy, "wl%d: intstatus%d 0x%x\n", unit, idx, intstatus); if (intstatus & I_RO) { wiphy_err(wiphy, "wl%d: fifo %d: receive fifo " "overflow\n", unit, idx); fatal = true; } if (intstatus & I_PC) { wiphy_err(wiphy, "wl%d: fifo %d: descriptor error\n", unit, idx); fatal = true; } if (intstatus & I_PD) { wiphy_err(wiphy, "wl%d: fifo %d: data error\n", unit, idx); fatal = true; } if (intstatus & I_DE) { wiphy_err(wiphy, "wl%d: fifo %d: descriptor protocol " "error\n", unit, idx); fatal = true; } if (intstatus & I_RU) wiphy_err(wiphy, "wl%d: fifo %d: receive descriptor " "underflow\n", idx, unit); if (intstatus & I_XU) { wiphy_err(wiphy, "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 * driver so suspend the mac before suspending the FIFO */ if (BRCMS_PHY_11N_CA