| /* |
| * 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 |
| |
| /* 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 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(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) { |
| 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; |
| 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; |
| 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; |
| 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); |
| mcl = le16_to_cpu(txh->MacTxControlLow); |
| |
| 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_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) { |
| 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 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; |
| |
| 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 == true) |
| 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; |
| bool phy_in_reset = false; |
| |
| 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)); |
| 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, 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_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, 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 |
| * driver so suspend the mac before suspending the FIFO |
| */ |
| if (BRCMS_PHY_11N_CAP(wlc_hw->band)) |
| brcms_c_suspend_mac_and_wait(wlc_hw->wlc); |
| |
| dma_txsuspend(wlc_hw->di[tx_fifo]); |
| |
| if (BRCMS_PHY_11N_CAP(wlc_hw->band)) |
| brcms_c_enable_mac(wlc_hw->wlc); |
| } |
| } |
| |
| static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, |
| uint tx_fifo) |
| { |
| /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case |
| * but need to be done here for PIO otherwise the watchdog will catch |
| * the inconsistency and fire |
| */ |
| /* Two clients of this code, 11h Quiet period and scanning. */ |
| if (wlc_hw->di[tx_fifo]) |
| dma_txresume(wlc_hw->di[tx_fifo]); |
| |
| /* allow core to sleep again */ |
| if (wlc_hw->suspended_fifos == 0) |
| return; |
| else { |
| wlc_hw->suspended_fifos &= ~(1 << tx_fifo); |
| if (wlc_hw->suspended_fifos == 0) |
| brcms_c_ucode_wake_override_clear(wlc_hw, |
| BRCMS_WAKE_OVERRIDE_TXFIFO); |
| } |
| } |
| |
| /* precondition: requires the mac core to be enabled */ |
| static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) |
| { |
| static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; |
| u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr; |
| |
| if (mute_tx) { |
| /* suspend tx fifos */ |
| brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); |
| brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); |
| brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO); |
| brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); |
| |
| /* zero the address match register so we do not send ACKs */ |
| brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); |
| } else { |
| /* resume tx fifos */ |
| brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); |
| brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO); |
| brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO); |
| brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); |
| |
| /* Restore address */ |
| brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr); |
| } |
| |
| wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); |
| |
| if (mute_tx) |
| brcms_c_ucode_mute_override_set(wlc_hw); |
| else |
| brcms_c_ucode_mute_override_clear(wlc_hw); |
| } |
| |
| void |
| brcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) |
| { |
| brcms_b_mute(wlc->hw, mute_tx); |
| } |
| |
| /* |
| * Read and clear macintmask and macintstatus and intstatus registers. |
| * This routine should be called with interrupts off |
| * Return: |
| * -1 if brcms_deviceremoved(wlc) evaluates to true; |
| * 0 if the interrupt is not for us, or we are in some special cases; |
| * device interrupt status bits otherwise. |
| */ |
| static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) |
| { |
| struct brcms_hardware *wlc_hw = wlc->hw; |
| struct bcma_device *core = wlc_hw->d11core; |
| u32 macintstatus, mask; |
| |
| /* macintstatus includes a DMA interrupt summary bit */ |
| macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); |
| mask = in_isr ? wlc->macintmask : wlc->defmacintmask; |
| |
| trace_brcms_macintstatus(&core->dev, in_isr, macintstatus, mask); |
| |
| /* detect cardbus removed, in power down(suspend) and in reset */ |
| if (brcms_deviceremoved(wlc)) |
| return -1; |
| |
| /* brcms_deviceremoved() succeeds even when the core is still resetting, |
| * handle that case here. |
| */ |
| if (macintstatus == 0xffffffff) |
| return 0; |
| |
| /* defer unsolicited interrupts */ |
| macintstatus &= mask; |
| |
| /* if not for us */ |
| if (macintstatus == 0) |
| return 0; |
| |
| /* turn off the interrupts */ |
| bcma_write32(core, D11REGOFFS(macintmask), 0); |
| (void)bcma_read32(core, D11REGOFFS(macintmask)); |
| wlc->macintmask = 0; |
| |
| /* clear device interrupts */ |
| bcma_write32(core, D11REGOFFS(macintstatus), macintstatus); |
| |
| /* MI_DMAINT is indication of non-zero intstatus */ |
| if (macintstatus & MI_DMAINT) |
| /* |
| * only fifo interrupt enabled is I_RI in |
| * RX_FIFO. If MI_DMAINT is set, assume it |
| * is set and clear the interrupt. |
| */ |
| bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intstatus), |
| DEF_RXINTMASK); |
| |
| return macintstatus; |
| } |
| |
| /* Update wlc->macintstatus and wlc->intstatus[]. */ |
| /* Return true if they are updated successfully. false otherwise */ |
| bool brcms_c_intrsupd(struct brcms_c_info *wlc) |
| { |
| u32 macintstatus; |
| |
| /* read and clear macintstatus and intstatus registers */ |
| macintstatus = wlc_intstatus(wlc, false); |
| |
| /* device is removed */ |
| if (macintstatus == 0xffffffff) |
| return false; |
| |
| /* update interrupt status in software */ |
| wlc->macintstatus |= macintstatus; |
| |
| return true; |
| } |
| |
| /* |
| * First-level interrupt processing. |
| * Return true if this was our interrupt |
| * and if further brcms_c_dpc() processing is required, |
| * false otherwise. |
| */ |
| bool brcms_c_isr(struct brcms_c_info *wlc) |
| { |
| struct brcms_hardware *wlc_hw = wlc->hw; |
| u32 macintstatus; |
| |
| if (!wlc_hw->up || !wlc->macintmask) |
| return false; |
| |
| /* read and clear macintstatus and intstatus registers */ |
| macintstatus = wlc_intstatus(wlc, true); |
| |
| if (macintstatus == 0xffffffff) { |
| brcms_err(wlc_hw->d11core, |
| "DEVICEREMOVED detected in the ISR code path\n"); |
| return false; |
| } |
| |
| /* it is not for us */ |
| if (macintstatus == 0) |
| return false; |
| |
| /* save interrupt status bits */ |
| wlc->macintstatus = macintstatus; |
| |
| return true; |
| |
| } |
| |
| void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) |
| { |
| struct brcms_hardware *wlc_hw = wlc->hw; |
| struct bcma_device *core = wlc_hw->d11core; |
| u32 mc, mi; |
| |
| brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, |
| wlc_hw->band->bandunit); |
| |
| /* |
| * Track overlapping suspend requests |
| */ |
| wlc_hw->mac_suspend_depth++; |
| if (wlc_hw->mac_suspend_depth > 1) |
| return; |
| |
| /* force the core awake */ |
| brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); |
| |
| mc = bcma_read32(core, D11REGOFFS(maccontrol)); |
| |
| if (mc == 0xffffffff) { |
| brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, |
| __func__); |
| brcms_down(wlc->wl); |
| return; |
| } |
| WARN_ON(mc & MCTL_PSM_JMP_0); |
| WARN_ON(!(mc & MCTL_PSM_RUN)); |
| WARN_ON(!(mc & MCTL_EN_MAC)); |
| |
| mi = bcma_read32(core, D11REGOFFS(macintstatus)); |
| if (mi == 0xffffffff) { |
| brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, |
| __func__); |
| brcms_down(wlc->wl); |
| return; |
| } |
| WARN_ON(mi & MI_MACSSPNDD); |
| |
| brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0); |
| |
| SPINWAIT(!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD), |
| BRCMS_MAX_MAC_SUSPEND); |
| |
| if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { |
| brcms_err(core, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" |
| " and MI_MACSSPNDD is still not on.\n", |
| wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); |
| brcms_err(core, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " |
| "psm_brc 0x%04x\n", wlc_hw->unit, |
| bcma_read32(core, D11REGOFFS(psmdebug)), |
| bcma_read32(core, D11REGOFFS(phydebug)), |
| bcma_read16(core, D11REGOFFS(psm_brc))); |
| } |
| |
| mc = bcma_read32(core, D11REGOFFS(maccontrol)); |
| if (mc == 0xffffffff) { |
| brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, |
| __func__); |
| brcms_down(wlc->wl); |
| return; |
| } |
| WARN_ON(mc & MCTL_PSM_JMP_0); |
| WARN_ON(!(mc & MCTL_PSM_RUN)); |
| WARN_ON(mc & MCTL_EN_MAC); |
| } |
| |
| void brcms_c_enable_mac(struct brcms_c_info *wlc) |
| { |
| struct brcms_hardware *wlc_hw = wlc->hw; |
| struct bcma_device *core = wlc_hw->d11core; |
| u32 mc, mi; |
| |
| brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, |
| wlc->band->bandunit); |
| |
| /* |
| * Track overlapping suspend requests |
| */ |
| wlc_hw->mac_suspend_depth--; |
| if (wlc_hw->mac_suspend_depth > 0) |
| return; |
| |
| mc = bcma_read32(core, D11REGOFFS(maccontrol)); |
| WARN_ON(mc & MCTL_PSM_JMP_0); |
| WARN_ON(mc & MCTL_EN_MAC); |
| WARN_ON(!(mc & MCTL_PSM_RUN)); |
| |
| brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC); |
| bcma_write32(core, D11REGOFFS(macintstatus), MI_MACSSPNDD); |
| |
| mc = bcma_read32(core, D11REGOFFS(maccontrol)); |
| WARN_ON(mc & MCTL_PSM_JMP_0); |
| WARN_ON(!(mc & MCTL_EN_MAC)); |
| WARN_ON(!(mc & MCTL_PSM_RUN)); |
| |
| mi = bcma_read32(core, D11REGOFFS(macintstatus)); |
| WARN_ON(mi & MI_MACSSPNDD); |
| |
| brcms_c_ucode_wake_override_clear(wlc_hw, |
| BRCMS_WAKE_OVERRIDE_MACSUSPEND); |
| } |
| |
| void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode) |
| { |
| wlc_hw->hw_stf_ss_opmode = stf_mode; |
| |
| if (wlc_hw->clk) |
| brcms_upd_ofdm_pctl1_table(wlc_hw); |
| } |
| |
| static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) |
| { |
| struct bcma_device *core = wlc_hw->d11core; |
| u32 w, val; |
| struct wiphy *wiphy = wlc_hw->wlc->wiphy; |
| |
| /* Validate dchip register access */ |
| |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| w = bcma_read32(core, D11REGOFFS(objdata)); |
| |
| /* Can we write and read back a 32bit register? */ |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| bcma_write32(core, D11REGOFFS(objdata), (u32) 0xaa5555aa); |
| |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| val = bcma_read32(core, D11REGOFFS(objdata)); |
| if (val != (u32) 0xaa5555aa) { |
| wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " |
| "expected 0xaa5555aa\n", wlc_hw->unit, val); |
| return false; |
| } |
| |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| bcma_write32(core, D11REGOFFS(objdata), (u32) 0x55aaaa55); |
| |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| val = bcma_read32(core, D11REGOFFS(objdata)); |
| if (val != (u32) 0x55aaaa55) { |
| wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " |
| "expected 0x55aaaa55\n", wlc_hw->unit, val); |
| return false; |
| } |
| |
| bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| bcma_write32(core, D11REGOFFS(objdata), w); |
| |
| /* clear CFPStart */ |
| bcma_write32(core, D11REGOFFS(tsf_cfpstart), 0); |
| |
| w = bcma_read32(core, D11REGOFFS(maccontrol)); |
| if ((w != (MCTL_IHR_EN | MCTL_WAKE)) && |
| (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) { |
| wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = " |
| "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, |
| (MCTL_IHR_EN | MCTL_WAKE), |
| (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)); |
| return false; |
| } |
| |
| return true; |
| } |
| |
| #define PHYPLL_WAIT_US 100000 |
| |
| void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) |
| { |
| struct bcma_device *core = wlc_hw->d11core; |
| u32 tmp; |
| |
| brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); |
| |
| tmp = 0; |
| |
| if (on) { |
| if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { |
| bcma_set32(core, D11REGOFFS(clk_ctl_st), |
| CCS_ERSRC_REQ_HT | |
| CCS_ERSRC_REQ_D11PLL | |
| CCS_ERSRC_REQ_PHYPLL); |
| SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & |
| CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT, |
| PHYPLL_WAIT_US); |
| |
| tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); |
| if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) |
| brcms_err(core, "%s: turn on PHY PLL failed\n", |
| __func__); |
| } else { |
| bcma_set32(core, D11REGOFFS(clk_ctl_st), |
| tmp | CCS_ERSRC_REQ_D11PLL | |
| CCS_ERSRC_REQ_PHYPLL); |
| SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & |
| (CCS_ERSRC_AVAIL_D11PLL | |
| CCS_ERSRC_AVAIL_PHYPLL)) != |
| (CCS_ERSRC_AVAIL_D11PLL | |
| CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US); |
| |
| tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); |
| if ((tmp & |
| (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) |
| != |
| (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) |
| brcms_err(core, "%s: turn on PHY PLL failed\n", |
| __func__); |
| } |
| } else { |
| /* |
| * Since the PLL may be shared, other cores can still |
| * be requesting it; so we'll deassert the request but |
| * not wait for status to comply. |
| */ |
| bcma_mask32(core, D11REGOFFS(clk_ctl_st), |
| ~CCS_ERSRC_REQ_PHYPLL); |
| (void)bcma_read32(core, D11REGOFFS(clk_ctl_st)); |
| } |
| } |
| |
| static void brcms_c_coredisable(struct brcms_hardware *wlc_hw) |
| { |
| bool dev_gone; |
| |
| brcms_dbg_info(wlc_hw->d11core, "wl%d: disable core\n", wlc_hw->unit); |
| |
| dev_gone = brcms_deviceremoved(wlc_hw->wlc); |
| |
| if (dev_gone) |
| return; |
| |
| if (wlc_hw->noreset) |
| return; |
| |
| /* radio off */ |
| wlc_phy_switch_radio(wlc_hw->band->pi, OFF); |
| |
| /* turn off analog core */ |
| wlc_phy_anacore(wlc_hw->band->pi, OFF); |
| |
| /* turn off PHYPLL to save power */ |
| brcms_b_core_phypll_ctl(wlc_hw, false); |
| |
| wlc_hw->clk = false; |
| bcma_core_disable(wlc_hw->d11core, 0); |
| wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); |
| } |
| |
| static void brcms_c_flushqueues(struct brcms_c_info *wlc) |
| { |
| struct brcms_hardware *wlc_hw = wlc->hw; |
| uint i; |
| |
| /* free any posted tx packets */ |
| for (i = 0; i < NFIFO; i++) { |
| if (wlc_hw->di[i]) { |
| dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); |
| if (i < TX_BCMC_FIFO) |
| ieee80211_wake_queue(wlc->pub->ieee_hw, |
| brcms_fifo_to_ac(i)); |
| } |
| } |
| |
| /* free any posted rx packets */ |
| dma_rxreclaim(wlc_hw->di[RX_FIFO]); |
| } |
| |
| static u16 |
| brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel) |
| { |
| struct bcma_device *core = wlc_hw->d11core; |
| u16 objoff = D11REGOFFS(objdata); |
| |
| bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| if (offset & 2) |
| objoff += 2; |
| |
| return bcma_read16(core, objoff); |
| } |
| |
| static void |
| brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v, |
| u32 sel) |
| { |
| struct bcma_device *core = wlc_hw->d11core; |
| u16 objoff = D11REGOFFS(objdata); |
| |
| bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); |
| (void)bcma_read32(core, D11REGOFFS(objaddr)); |
| if (offset & 2) |
| objoff += 2; |
| |
| bcma_wflush16(core, objoff, v); |
| } |
| |
| /* |
| * Read a single u16 from shared memory. |
| * SHM 'offset' needs to be an even address |
| */ |
| u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset) |
| { |
| return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL); |
| } |
| |
| /* |
| * Write a single u16 to shared memory. |
| * SHM 'offset' needs to be an even address |
| */ |
| void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v) |
| { |
| brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL); |
| } |
| |
| /* |
| * Copy a buffer to shared memory of specified type . |
| * SHM 'offset' needs to be an even address and |
| * Buffer length 'len' must be an even number of bytes |
| * 'sel' selects the type of memory |
| */ |
| void |
| brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, |
| const void *buf, int len, u32 sel) |
| { |
| u16 v; |
| const u8 *p = (const u8 *)buf; |
| int i; |
| |
| if (len <= 0 || (offset & 1) || (len & 1)) |
| return; |
| |
| for (i = 0; i < len; i += 2) { |
| v = p[i] | (p[i + 1] << 8); |
| brcms_b_write_objmem(wlc_hw, offset + i, v, sel); |
| } |
| } |
| |
| /* |
| * Copy a piece of shared memory of specified type to a buffer . |
| * SHM 'offset' needs to be an even address and |
| * Buffer length 'len' must be an even number of bytes |
| * 'sel' selects the type of memory |
| */ |
| void |
| brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, |
| int len, u32 sel) |
| { |
| u16 v; |
| u8 *p = (u8 *) buf; |
| int i; |
| |
| if (len <= 0 || (offset & 1) || (len & 1)) |
| return; |
| |
| for (i = 0; i < len; i += 2) { |
| v = brcms_b_read_objmem(wlc_hw, offset + i, sel); |
| p[i] = v & 0xFF; |
| p[i + 1] = (v >> 8) & 0xFF; |
| } |
| } |
| |
| /* Copy a buffer to shared memory. |
| * SHM 'offset' needs to be an even address and |
| * Buffer length 'len' must be an even number of bytes |
| */ |
| static void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, |
| const void *buf, int len) |
| { |
| brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); |
| } |
| |
| static void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, |
| u16 SRL, u16 LRL) |
| { |
| wlc_hw->SRL = SRL; |
| wlc_hw->LRL = LRL; |
| |
| /* write retry limit to SCR, shouldn't need to suspend */ |
| if (wlc_hw->up) { |
| bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), |
| OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); |
| (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); |
| bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->SRL); |
| bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), |
| OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); |
| (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); |
| bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->LRL); |
| } |
| } |
| |
| static void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit) |
| { |
| if (set) { |
| if (mboolisset(wlc_hw->pllreq, req_bit)) |
| return; |
| |
| mboolset(wlc_hw->pllreq, req_bit); |
| |
| if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { |
| if (!wlc_hw->sbclk) |
| brcms_b_xtal(wlc_hw, ON); |
| } |
| } else { |
| if (!mboolisset(wlc_hw->pllreq, req_bit)) |
| return; |
| |
| mboolclr(wlc_hw->pllreq, req_bit); |
| |
| if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { |
| if (wlc_hw->sbclk) |
| brcms_b_xtal(wlc_hw, OFF); |
| } |
| } |
| } |
| |
| static void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) |
| { |
| wlc_hw->antsel_avail = antsel_avail; |
| } |
| |
| /* |
| * conditions under which the PM bit should be set in outgoing frames |
| * and STAY_AWAKE is meaningful |
| */ |
| static bool brcms_c_ps_allowed(struct brcms_c_info *wlc) |
| { |
| /* not supporting PS so always return false for now */ |
| return false; |
| } |
| |
| static void brcms_c_statsupd(struct brcms_c_info *wlc) |
| { |
| int i; |
| struct macstat *macstats; |
| #ifdef DEBUG |
| u16 delta; |
| u16 rxf0ovfl; |
| u16 txfunfl[NFIFO]; |
| #endif /* DEBUG */ |
| |
| /* if driver down, make no sense to update stats */ |
| if (!wlc->pub->up) |
| return; |
| |
| macstats = wlc->core->macstat_snapshot; |
| |
| #ifdef DEBUG |
| /* save last rx fifo 0 overflow count */ |
| rxf0ovfl = macstats->rxf0ovfl; |
| |
| /* save last tx fifo underflow count */ |
| for (i = 0; i < NFIFO; i++) |
| txfunfl[i] = macstats->txfunfl[i]; |
| #endif /* DEBUG */ |
| |
| /* Read mac stats from contiguous shared memory */ |
| brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats, |
| sizeof(*macstats), OBJADDR_SHM_SEL); |
| |
| #ifdef DEBUG |
| /* check for rx fifo 0 overflow */ |
| delta = (u16)(macstats->rxf0ovfl - rxf0ovfl); |
| if (delta) |
| brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", |
| wlc->pub->unit, delta); |
| |
| /* check for tx fifo underflows */ |
| for (i = 0; i < NFIFO; i++) { |
| delta = macstats->txfunfl[i] - txfunfl[i]; |
| if (delta) |
| brcms_err(wlc->hw->d11core, |
| "wl%d: %u tx fifo %d underflows!\n", |
| wlc->pub->unit, delta, i); |
| } |
| #endif /* DEBUG */ |
| |
| /* merge counters from dma module */ |
| for (i = 0; i < NFIFO; i++) { |
|