162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2010 Broadcom Corporation 362306a36Sopenharmony_ci * Copyright (c) 2013 Hauke Mehrtens <hauke@hauke-m.de> 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Permission to use, copy, modify, and/or distribute this software for any 662306a36Sopenharmony_ci * purpose with or without fee is hereby granted, provided that the above 762306a36Sopenharmony_ci * copyright notice and this permission notice appear in all copies. 862306a36Sopenharmony_ci * 962306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 1062306a36Sopenharmony_ci * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 1162306a36Sopenharmony_ci * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY 1262306a36Sopenharmony_ci * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 1362306a36Sopenharmony_ci * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION 1462306a36Sopenharmony_ci * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN 1562306a36Sopenharmony_ci * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 1662306a36Sopenharmony_ci */ 1762306a36Sopenharmony_ci 1862306a36Sopenharmony_ci#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 1962306a36Sopenharmony_ci 2062306a36Sopenharmony_ci#include <linux/pci_ids.h> 2162306a36Sopenharmony_ci#include <linux/if_ether.h> 2262306a36Sopenharmony_ci#include <net/cfg80211.h> 2362306a36Sopenharmony_ci#include <net/mac80211.h> 2462306a36Sopenharmony_ci#include <brcm_hw_ids.h> 2562306a36Sopenharmony_ci#include <aiutils.h> 2662306a36Sopenharmony_ci#include <chipcommon.h> 2762306a36Sopenharmony_ci#include "rate.h" 2862306a36Sopenharmony_ci#include "scb.h" 2962306a36Sopenharmony_ci#include "phy/phy_hal.h" 3062306a36Sopenharmony_ci#include "channel.h" 3162306a36Sopenharmony_ci#include "antsel.h" 3262306a36Sopenharmony_ci#include "stf.h" 3362306a36Sopenharmony_ci#include "ampdu.h" 3462306a36Sopenharmony_ci#include "mac80211_if.h" 3562306a36Sopenharmony_ci#include "ucode_loader.h" 3662306a36Sopenharmony_ci#include "main.h" 3762306a36Sopenharmony_ci#include "soc.h" 3862306a36Sopenharmony_ci#include "dma.h" 3962306a36Sopenharmony_ci#include "debug.h" 4062306a36Sopenharmony_ci#include "brcms_trace_events.h" 4162306a36Sopenharmony_ci 4262306a36Sopenharmony_ci/* watchdog timer, in unit of ms */ 4362306a36Sopenharmony_ci#define TIMER_INTERVAL_WATCHDOG 1000 4462306a36Sopenharmony_ci/* radio monitor timer, in unit of ms */ 4562306a36Sopenharmony_ci#define TIMER_INTERVAL_RADIOCHK 800 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci/* beacon interval, in unit of 1024TU */ 4862306a36Sopenharmony_ci#define BEACON_INTERVAL_DEFAULT 100 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* n-mode support capability */ 5162306a36Sopenharmony_ci/* 2x2 includes both 1x1 & 2x2 devices 5262306a36Sopenharmony_ci * reserved #define 2 for future when we want to separate 1x1 & 2x2 and 5362306a36Sopenharmony_ci * control it independently 5462306a36Sopenharmony_ci */ 5562306a36Sopenharmony_ci#define WL_11N_2x2 1 5662306a36Sopenharmony_ci#define WL_11N_3x3 3 5762306a36Sopenharmony_ci#define WL_11N_4x4 4 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci#define EDCF_ACI_MASK 0x60 6062306a36Sopenharmony_ci#define EDCF_ACI_SHIFT 5 6162306a36Sopenharmony_ci#define EDCF_ECWMIN_MASK 0x0f 6262306a36Sopenharmony_ci#define EDCF_ECWMAX_SHIFT 4 6362306a36Sopenharmony_ci#define EDCF_AIFSN_MASK 0x0f 6462306a36Sopenharmony_ci#define EDCF_AIFSN_MAX 15 6562306a36Sopenharmony_ci#define EDCF_ECWMAX_MASK 0xf0 6662306a36Sopenharmony_ci 6762306a36Sopenharmony_ci#define EDCF_AC_BE_TXOP_STA 0x0000 6862306a36Sopenharmony_ci#define EDCF_AC_BK_TXOP_STA 0x0000 6962306a36Sopenharmony_ci#define EDCF_AC_VO_ACI_STA 0x62 7062306a36Sopenharmony_ci#define EDCF_AC_VO_ECW_STA 0x32 7162306a36Sopenharmony_ci#define EDCF_AC_VI_ACI_STA 0x42 7262306a36Sopenharmony_ci#define EDCF_AC_VI_ECW_STA 0x43 7362306a36Sopenharmony_ci#define EDCF_AC_BK_ECW_STA 0xA4 7462306a36Sopenharmony_ci#define EDCF_AC_VI_TXOP_STA 0x005e 7562306a36Sopenharmony_ci#define EDCF_AC_VO_TXOP_STA 0x002f 7662306a36Sopenharmony_ci#define EDCF_AC_BE_ACI_STA 0x03 7762306a36Sopenharmony_ci#define EDCF_AC_BE_ECW_STA 0xA4 7862306a36Sopenharmony_ci#define EDCF_AC_BK_ACI_STA 0x27 7962306a36Sopenharmony_ci#define EDCF_AC_VO_TXOP_AP 0x002f 8062306a36Sopenharmony_ci 8162306a36Sopenharmony_ci#define EDCF_TXOP2USEC(txop) ((txop) << 5) 8262306a36Sopenharmony_ci#define EDCF_ECW2CW(exp) ((1 << (exp)) - 1) 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci#define APHY_SYMBOL_TIME 4 8562306a36Sopenharmony_ci#define APHY_PREAMBLE_TIME 16 8662306a36Sopenharmony_ci#define APHY_SIGNAL_TIME 4 8762306a36Sopenharmony_ci#define APHY_SIFS_TIME 16 8862306a36Sopenharmony_ci#define APHY_SERVICE_NBITS 16 8962306a36Sopenharmony_ci#define APHY_TAIL_NBITS 6 9062306a36Sopenharmony_ci#define BPHY_SIFS_TIME 10 9162306a36Sopenharmony_ci#define BPHY_PLCP_SHORT_TIME 96 9262306a36Sopenharmony_ci 9362306a36Sopenharmony_ci#define PREN_PREAMBLE 24 9462306a36Sopenharmony_ci#define PREN_MM_EXT 12 9562306a36Sopenharmony_ci#define PREN_PREAMBLE_EXT 4 9662306a36Sopenharmony_ci 9762306a36Sopenharmony_ci#define DOT11_MAC_HDR_LEN 24 9862306a36Sopenharmony_ci#define DOT11_ACK_LEN 10 9962306a36Sopenharmony_ci#define DOT11_BA_LEN 4 10062306a36Sopenharmony_ci#define DOT11_OFDM_SIGNAL_EXTENSION 6 10162306a36Sopenharmony_ci#define DOT11_MIN_FRAG_LEN 256 10262306a36Sopenharmony_ci#define DOT11_RTS_LEN 16 10362306a36Sopenharmony_ci#define DOT11_CTS_LEN 10 10462306a36Sopenharmony_ci#define DOT11_BA_BITMAP_LEN 128 10562306a36Sopenharmony_ci#define DOT11_MAXNUMFRAGS 16 10662306a36Sopenharmony_ci#define DOT11_MAX_FRAG_LEN 2346 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci#define BPHY_PLCP_TIME 192 10962306a36Sopenharmony_ci#define RIFS_11N_TIME 2 11062306a36Sopenharmony_ci 11162306a36Sopenharmony_ci/* length of the BCN template area */ 11262306a36Sopenharmony_ci#define BCN_TMPL_LEN 512 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci/* brcms_bss_info flag bit values */ 11562306a36Sopenharmony_ci#define BRCMS_BSS_HT 0x0020 /* BSS is HT (MIMO) capable */ 11662306a36Sopenharmony_ci 11762306a36Sopenharmony_ci/* chip rx buffer offset */ 11862306a36Sopenharmony_ci#define BRCMS_HWRXOFF 38 11962306a36Sopenharmony_ci 12062306a36Sopenharmony_ci/* rfdisable delay timer 500 ms, runs of ALP clock */ 12162306a36Sopenharmony_ci#define RFDISABLE_DEFAULT 10000000 12262306a36Sopenharmony_ci 12362306a36Sopenharmony_ci#define BRCMS_TEMPSENSE_PERIOD 10 /* 10 second timeout */ 12462306a36Sopenharmony_ci 12562306a36Sopenharmony_ci/* synthpu_dly times in us */ 12662306a36Sopenharmony_ci#define SYNTHPU_DLY_APHY_US 3700 12762306a36Sopenharmony_ci#define SYNTHPU_DLY_BPHY_US 1050 12862306a36Sopenharmony_ci#define SYNTHPU_DLY_NPHY_US 2048 12962306a36Sopenharmony_ci#define SYNTHPU_DLY_LPPHY_US 300 13062306a36Sopenharmony_ci 13162306a36Sopenharmony_ci#define ANTCNT 10 /* vanilla M_MAX_ANTCNT val */ 13262306a36Sopenharmony_ci 13362306a36Sopenharmony_ci/* Per-AC retry limit register definitions; uses defs.h bitfield macros */ 13462306a36Sopenharmony_ci#define EDCF_SHORT_S 0 13562306a36Sopenharmony_ci#define EDCF_SFB_S 4 13662306a36Sopenharmony_ci#define EDCF_LONG_S 8 13762306a36Sopenharmony_ci#define EDCF_LFB_S 12 13862306a36Sopenharmony_ci#define EDCF_SHORT_M BITFIELD_MASK(4) 13962306a36Sopenharmony_ci#define EDCF_SFB_M BITFIELD_MASK(4) 14062306a36Sopenharmony_ci#define EDCF_LONG_M BITFIELD_MASK(4) 14162306a36Sopenharmony_ci#define EDCF_LFB_M BITFIELD_MASK(4) 14262306a36Sopenharmony_ci 14362306a36Sopenharmony_ci#define RETRY_SHORT_DEF 7 /* Default Short retry Limit */ 14462306a36Sopenharmony_ci#define RETRY_SHORT_MAX 255 /* Maximum Short retry Limit */ 14562306a36Sopenharmony_ci#define RETRY_LONG_DEF 4 /* Default Long retry count */ 14662306a36Sopenharmony_ci#define RETRY_SHORT_FB 3 /* Short count for fb rate */ 14762306a36Sopenharmony_ci#define RETRY_LONG_FB 2 /* Long count for fb rate */ 14862306a36Sopenharmony_ci 14962306a36Sopenharmony_ci#define APHY_CWMIN 15 15062306a36Sopenharmony_ci#define PHY_CWMAX 1023 15162306a36Sopenharmony_ci 15262306a36Sopenharmony_ci#define EDCF_AIFSN_MIN 1 15362306a36Sopenharmony_ci 15462306a36Sopenharmony_ci#define FRAGNUM_MASK 0xF 15562306a36Sopenharmony_ci 15662306a36Sopenharmony_ci#define APHY_SLOT_TIME 9 15762306a36Sopenharmony_ci#define BPHY_SLOT_TIME 20 15862306a36Sopenharmony_ci 15962306a36Sopenharmony_ci#define WL_SPURAVOID_OFF 0 16062306a36Sopenharmony_ci#define WL_SPURAVOID_ON1 1 16162306a36Sopenharmony_ci#define WL_SPURAVOID_ON2 2 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci/* invalid core flags, use the saved coreflags */ 16462306a36Sopenharmony_ci#define BRCMS_USE_COREFLAGS 0xffffffff 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_ci/* values for PLCPHdr_override */ 16762306a36Sopenharmony_ci#define BRCMS_PLCP_AUTO -1 16862306a36Sopenharmony_ci#define BRCMS_PLCP_SHORT 0 16962306a36Sopenharmony_ci#define BRCMS_PLCP_LONG 1 17062306a36Sopenharmony_ci 17162306a36Sopenharmony_ci/* values for g_protection_override and n_protection_override */ 17262306a36Sopenharmony_ci#define BRCMS_PROTECTION_AUTO -1 17362306a36Sopenharmony_ci#define BRCMS_PROTECTION_OFF 0 17462306a36Sopenharmony_ci#define BRCMS_PROTECTION_ON 1 17562306a36Sopenharmony_ci#define BRCMS_PROTECTION_MMHDR_ONLY 2 17662306a36Sopenharmony_ci#define BRCMS_PROTECTION_CTS_ONLY 3 17762306a36Sopenharmony_ci 17862306a36Sopenharmony_ci/* values for g_protection_control and n_protection_control */ 17962306a36Sopenharmony_ci#define BRCMS_PROTECTION_CTL_OFF 0 18062306a36Sopenharmony_ci#define BRCMS_PROTECTION_CTL_LOCAL 1 18162306a36Sopenharmony_ci#define BRCMS_PROTECTION_CTL_OVERLAP 2 18262306a36Sopenharmony_ci 18362306a36Sopenharmony_ci/* values for n_protection */ 18462306a36Sopenharmony_ci#define BRCMS_N_PROTECTION_OFF 0 18562306a36Sopenharmony_ci#define BRCMS_N_PROTECTION_OPTIONAL 1 18662306a36Sopenharmony_ci#define BRCMS_N_PROTECTION_20IN40 2 18762306a36Sopenharmony_ci#define BRCMS_N_PROTECTION_MIXEDMODE 3 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_ci/* values for band specific 40MHz capabilities */ 19062306a36Sopenharmony_ci#define BRCMS_N_BW_20ALL 0 19162306a36Sopenharmony_ci#define BRCMS_N_BW_40ALL 1 19262306a36Sopenharmony_ci#define BRCMS_N_BW_20IN2G_40IN5G 2 19362306a36Sopenharmony_ci 19462306a36Sopenharmony_ci/* bitflags for SGI support (sgi_rx iovar) */ 19562306a36Sopenharmony_ci#define BRCMS_N_SGI_20 0x01 19662306a36Sopenharmony_ci#define BRCMS_N_SGI_40 0x02 19762306a36Sopenharmony_ci 19862306a36Sopenharmony_ci/* defines used by the nrate iovar */ 19962306a36Sopenharmony_ci/* MSC in use,indicates b0-6 holds an mcs */ 20062306a36Sopenharmony_ci#define NRATE_MCS_INUSE 0x00000080 20162306a36Sopenharmony_ci/* rate/mcs value */ 20262306a36Sopenharmony_ci#define NRATE_RATE_MASK 0x0000007f 20362306a36Sopenharmony_ci/* stf mode mask: siso, cdd, stbc, sdm */ 20462306a36Sopenharmony_ci#define NRATE_STF_MASK 0x0000ff00 20562306a36Sopenharmony_ci/* stf mode shift */ 20662306a36Sopenharmony_ci#define NRATE_STF_SHIFT 8 20762306a36Sopenharmony_ci/* bit indicate to override mcs only */ 20862306a36Sopenharmony_ci#define NRATE_OVERRIDE_MCS_ONLY 0x40000000 20962306a36Sopenharmony_ci#define NRATE_SGI_MASK 0x00800000 /* sgi mode */ 21062306a36Sopenharmony_ci#define NRATE_SGI_SHIFT 23 /* sgi mode */ 21162306a36Sopenharmony_ci#define NRATE_LDPC_CODING 0x00400000 /* adv coding in use */ 21262306a36Sopenharmony_ci#define NRATE_LDPC_SHIFT 22 /* ldpc shift */ 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_ci#define NRATE_STF_SISO 0 /* stf mode SISO */ 21562306a36Sopenharmony_ci#define NRATE_STF_CDD 1 /* stf mode CDD */ 21662306a36Sopenharmony_ci#define NRATE_STF_STBC 2 /* stf mode STBC */ 21762306a36Sopenharmony_ci#define NRATE_STF_SDM 3 /* stf mode SDM */ 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_ci#define MAX_DMA_SEGS 4 22062306a36Sopenharmony_ci 22162306a36Sopenharmony_ci/* # of entries in Tx FIFO */ 22262306a36Sopenharmony_ci#define NTXD 64 22362306a36Sopenharmony_ci/* Max # of entries in Rx FIFO based on 4kb page size */ 22462306a36Sopenharmony_ci#define NRXD 256 22562306a36Sopenharmony_ci 22662306a36Sopenharmony_ci/* Amount of headroom to leave in Tx FIFO */ 22762306a36Sopenharmony_ci#define TX_HEADROOM 4 22862306a36Sopenharmony_ci 22962306a36Sopenharmony_ci/* try to keep this # rbufs posted to the chip */ 23062306a36Sopenharmony_ci#define NRXBUFPOST 32 23162306a36Sopenharmony_ci 23262306a36Sopenharmony_ci/* max # frames to process in brcms_c_recv() */ 23362306a36Sopenharmony_ci#define RXBND 8 23462306a36Sopenharmony_ci/* max # tx status to process in wlc_txstatus() */ 23562306a36Sopenharmony_ci#define TXSBND 8 23662306a36Sopenharmony_ci 23762306a36Sopenharmony_ci/* brcmu_format_flags() bit description structure */ 23862306a36Sopenharmony_cistruct brcms_c_bit_desc { 23962306a36Sopenharmony_ci u32 bit; 24062306a36Sopenharmony_ci const char *name; 24162306a36Sopenharmony_ci}; 24262306a36Sopenharmony_ci 24362306a36Sopenharmony_ci/* 24462306a36Sopenharmony_ci * The following table lists the buffer memory allocated to xmt fifos in HW. 24562306a36Sopenharmony_ci * the size is in units of 256bytes(one block), total size is HW dependent 24662306a36Sopenharmony_ci * ucode has default fifo partition, sw can overwrite if necessary 24762306a36Sopenharmony_ci * 24862306a36Sopenharmony_ci * This is documented in twiki under the topic UcodeTxFifo. Please ensure 24962306a36Sopenharmony_ci * the twiki is updated before making changes. 25062306a36Sopenharmony_ci */ 25162306a36Sopenharmony_ci 25262306a36Sopenharmony_ci/* Starting corerev for the fifo size table */ 25362306a36Sopenharmony_ci#define XMTFIFOTBL_STARTREV 17 25462306a36Sopenharmony_ci 25562306a36Sopenharmony_cistruct d11init { 25662306a36Sopenharmony_ci __le16 addr; 25762306a36Sopenharmony_ci __le16 size; 25862306a36Sopenharmony_ci __le32 value; 25962306a36Sopenharmony_ci}; 26062306a36Sopenharmony_ci 26162306a36Sopenharmony_cistruct edcf_acparam { 26262306a36Sopenharmony_ci u8 ACI; 26362306a36Sopenharmony_ci u8 ECW; 26462306a36Sopenharmony_ci u16 TXOP; 26562306a36Sopenharmony_ci} __packed; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci/* debug/trace */ 26862306a36Sopenharmony_ciuint brcm_msg_level; 26962306a36Sopenharmony_ci 27062306a36Sopenharmony_ci/* TX FIFO number to WME/802.1E Access Category */ 27162306a36Sopenharmony_cistatic const u8 wme_fifo2ac[] = { 27262306a36Sopenharmony_ci IEEE80211_AC_BK, 27362306a36Sopenharmony_ci IEEE80211_AC_BE, 27462306a36Sopenharmony_ci IEEE80211_AC_VI, 27562306a36Sopenharmony_ci IEEE80211_AC_VO, 27662306a36Sopenharmony_ci IEEE80211_AC_BE, 27762306a36Sopenharmony_ci IEEE80211_AC_BE 27862306a36Sopenharmony_ci}; 27962306a36Sopenharmony_ci 28062306a36Sopenharmony_ci/* ieee80211 Access Category to TX FIFO number */ 28162306a36Sopenharmony_cistatic const u8 wme_ac2fifo[] = { 28262306a36Sopenharmony_ci TX_AC_VO_FIFO, 28362306a36Sopenharmony_ci TX_AC_VI_FIFO, 28462306a36Sopenharmony_ci TX_AC_BE_FIFO, 28562306a36Sopenharmony_ci TX_AC_BK_FIFO 28662306a36Sopenharmony_ci}; 28762306a36Sopenharmony_ci 28862306a36Sopenharmony_cistatic const u16 xmtfifo_sz[][NFIFO] = { 28962306a36Sopenharmony_ci /* corerev 17: 5120, 49152, 49152, 5376, 4352, 1280 */ 29062306a36Sopenharmony_ci {20, 192, 192, 21, 17, 5}, 29162306a36Sopenharmony_ci /* corerev 18: */ 29262306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 29362306a36Sopenharmony_ci /* corerev 19: */ 29462306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 29562306a36Sopenharmony_ci /* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */ 29662306a36Sopenharmony_ci {20, 192, 192, 21, 17, 5}, 29762306a36Sopenharmony_ci /* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */ 29862306a36Sopenharmony_ci {9, 58, 22, 14, 14, 5}, 29962306a36Sopenharmony_ci /* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */ 30062306a36Sopenharmony_ci {20, 192, 192, 21, 17, 5}, 30162306a36Sopenharmony_ci /* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */ 30262306a36Sopenharmony_ci {20, 192, 192, 21, 17, 5}, 30362306a36Sopenharmony_ci /* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */ 30462306a36Sopenharmony_ci {9, 58, 22, 14, 14, 5}, 30562306a36Sopenharmony_ci /* corerev 25: */ 30662306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 30762306a36Sopenharmony_ci /* corerev 26: */ 30862306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 30962306a36Sopenharmony_ci /* corerev 27: */ 31062306a36Sopenharmony_ci {0, 0, 0, 0, 0, 0}, 31162306a36Sopenharmony_ci /* corerev 28: 2304, 14848, 5632, 3584, 3584, 1280 */ 31262306a36Sopenharmony_ci {9, 58, 22, 14, 14, 5}, 31362306a36Sopenharmony_ci}; 31462306a36Sopenharmony_ci 31562306a36Sopenharmony_ci#ifdef DEBUG 31662306a36Sopenharmony_cistatic const char * const fifo_names[] = { 31762306a36Sopenharmony_ci "AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" }; 31862306a36Sopenharmony_ci#else 31962306a36Sopenharmony_cistatic const char fifo_names[6][1]; 32062306a36Sopenharmony_ci#endif 32162306a36Sopenharmony_ci 32262306a36Sopenharmony_ci#ifdef DEBUG 32362306a36Sopenharmony_ci/* pointer to most recently allocated wl/wlc */ 32462306a36Sopenharmony_cistatic struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL); 32562306a36Sopenharmony_ci#endif 32662306a36Sopenharmony_ci 32762306a36Sopenharmony_ci/* Mapping of ieee80211 AC numbers to tx fifos */ 32862306a36Sopenharmony_cistatic const u8 ac_to_fifo_mapping[IEEE80211_NUM_ACS] = { 32962306a36Sopenharmony_ci [IEEE80211_AC_VO] = TX_AC_VO_FIFO, 33062306a36Sopenharmony_ci [IEEE80211_AC_VI] = TX_AC_VI_FIFO, 33162306a36Sopenharmony_ci [IEEE80211_AC_BE] = TX_AC_BE_FIFO, 33262306a36Sopenharmony_ci [IEEE80211_AC_BK] = TX_AC_BK_FIFO, 33362306a36Sopenharmony_ci}; 33462306a36Sopenharmony_ci 33562306a36Sopenharmony_ci/* Mapping of tx fifos to ieee80211 AC numbers */ 33662306a36Sopenharmony_cistatic const u8 fifo_to_ac_mapping[IEEE80211_NUM_ACS] = { 33762306a36Sopenharmony_ci [TX_AC_BK_FIFO] = IEEE80211_AC_BK, 33862306a36Sopenharmony_ci [TX_AC_BE_FIFO] = IEEE80211_AC_BE, 33962306a36Sopenharmony_ci [TX_AC_VI_FIFO] = IEEE80211_AC_VI, 34062306a36Sopenharmony_ci [TX_AC_VO_FIFO] = IEEE80211_AC_VO, 34162306a36Sopenharmony_ci}; 34262306a36Sopenharmony_ci 34362306a36Sopenharmony_cistatic u8 brcms_ac_to_fifo(u8 ac) 34462306a36Sopenharmony_ci{ 34562306a36Sopenharmony_ci if (ac >= ARRAY_SIZE(ac_to_fifo_mapping)) 34662306a36Sopenharmony_ci return TX_AC_BE_FIFO; 34762306a36Sopenharmony_ci return ac_to_fifo_mapping[ac]; 34862306a36Sopenharmony_ci} 34962306a36Sopenharmony_ci 35062306a36Sopenharmony_cistatic u8 brcms_fifo_to_ac(u8 fifo) 35162306a36Sopenharmony_ci{ 35262306a36Sopenharmony_ci if (fifo >= ARRAY_SIZE(fifo_to_ac_mapping)) 35362306a36Sopenharmony_ci return IEEE80211_AC_BE; 35462306a36Sopenharmony_ci return fifo_to_ac_mapping[fifo]; 35562306a36Sopenharmony_ci} 35662306a36Sopenharmony_ci 35762306a36Sopenharmony_ci/* Find basic rate for a given rate */ 35862306a36Sopenharmony_cistatic u8 brcms_basic_rate(struct brcms_c_info *wlc, u32 rspec) 35962306a36Sopenharmony_ci{ 36062306a36Sopenharmony_ci if (is_mcs_rate(rspec)) 36162306a36Sopenharmony_ci return wlc->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK] 36262306a36Sopenharmony_ci .leg_ofdm]; 36362306a36Sopenharmony_ci return wlc->band->basic_rate[rspec & RSPEC_RATE_MASK]; 36462306a36Sopenharmony_ci} 36562306a36Sopenharmony_ci 36662306a36Sopenharmony_cistatic u16 frametype(u32 rspec, u8 mimoframe) 36762306a36Sopenharmony_ci{ 36862306a36Sopenharmony_ci if (is_mcs_rate(rspec)) 36962306a36Sopenharmony_ci return mimoframe; 37062306a36Sopenharmony_ci return is_cck_rate(rspec) ? FT_CCK : FT_OFDM; 37162306a36Sopenharmony_ci} 37262306a36Sopenharmony_ci 37362306a36Sopenharmony_ci/* currently the best mechanism for determining SIFS is the band in use */ 37462306a36Sopenharmony_cistatic u16 get_sifs(struct brcms_band *band) 37562306a36Sopenharmony_ci{ 37662306a36Sopenharmony_ci return band->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : 37762306a36Sopenharmony_ci BPHY_SIFS_TIME; 37862306a36Sopenharmony_ci} 37962306a36Sopenharmony_ci 38062306a36Sopenharmony_ci/* 38162306a36Sopenharmony_ci * Detect Card removed. 38262306a36Sopenharmony_ci * Even checking an sbconfig register read will not false trigger when the core 38362306a36Sopenharmony_ci * is in reset it breaks CF address mechanism. Accessing gphy phyversion will 38462306a36Sopenharmony_ci * cause SB error if aphy is in reset on 4306B0-DB. Need a simple accessible 38562306a36Sopenharmony_ci * reg with fixed 0/1 pattern (some platforms return all 0). 38662306a36Sopenharmony_ci * If clocks are present, call the sb routine which will figure out if the 38762306a36Sopenharmony_ci * device is removed. 38862306a36Sopenharmony_ci */ 38962306a36Sopenharmony_cistatic bool brcms_deviceremoved(struct brcms_c_info *wlc) 39062306a36Sopenharmony_ci{ 39162306a36Sopenharmony_ci u32 macctrl; 39262306a36Sopenharmony_ci 39362306a36Sopenharmony_ci if (!wlc->hw->clk) 39462306a36Sopenharmony_ci return ai_deviceremoved(wlc->hw->sih); 39562306a36Sopenharmony_ci macctrl = bcma_read32(wlc->hw->d11core, 39662306a36Sopenharmony_ci D11REGOFFS(maccontrol)); 39762306a36Sopenharmony_ci return (macctrl & (MCTL_PSM_JMP_0 | MCTL_IHR_EN)) != MCTL_IHR_EN; 39862306a36Sopenharmony_ci} 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci/* sum the individual fifo tx pending packet counts */ 40162306a36Sopenharmony_cistatic int brcms_txpktpendtot(struct brcms_c_info *wlc) 40262306a36Sopenharmony_ci{ 40362306a36Sopenharmony_ci int i; 40462306a36Sopenharmony_ci int pending = 0; 40562306a36Sopenharmony_ci 40662306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) 40762306a36Sopenharmony_ci if (wlc->hw->di[i]) 40862306a36Sopenharmony_ci pending += dma_txpending(wlc->hw->di[i]); 40962306a36Sopenharmony_ci return pending; 41062306a36Sopenharmony_ci} 41162306a36Sopenharmony_ci 41262306a36Sopenharmony_cistatic bool brcms_is_mband_unlocked(struct brcms_c_info *wlc) 41362306a36Sopenharmony_ci{ 41462306a36Sopenharmony_ci return wlc->pub->_nbands > 1 && !wlc->bandlocked; 41562306a36Sopenharmony_ci} 41662306a36Sopenharmony_ci 41762306a36Sopenharmony_cistatic int brcms_chspec_bw(u16 chanspec) 41862306a36Sopenharmony_ci{ 41962306a36Sopenharmony_ci if (CHSPEC_IS40(chanspec)) 42062306a36Sopenharmony_ci return BRCMS_40_MHZ; 42162306a36Sopenharmony_ci if (CHSPEC_IS20(chanspec)) 42262306a36Sopenharmony_ci return BRCMS_20_MHZ; 42362306a36Sopenharmony_ci 42462306a36Sopenharmony_ci return BRCMS_10_MHZ; 42562306a36Sopenharmony_ci} 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_cistatic void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg) 42862306a36Sopenharmony_ci{ 42962306a36Sopenharmony_ci if (cfg == NULL) 43062306a36Sopenharmony_ci return; 43162306a36Sopenharmony_ci 43262306a36Sopenharmony_ci kfree(cfg->current_bss); 43362306a36Sopenharmony_ci kfree(cfg); 43462306a36Sopenharmony_ci} 43562306a36Sopenharmony_ci 43662306a36Sopenharmony_cistatic void brcms_c_detach_mfree(struct brcms_c_info *wlc) 43762306a36Sopenharmony_ci{ 43862306a36Sopenharmony_ci if (wlc == NULL) 43962306a36Sopenharmony_ci return; 44062306a36Sopenharmony_ci 44162306a36Sopenharmony_ci brcms_c_bsscfg_mfree(wlc->bsscfg); 44262306a36Sopenharmony_ci kfree(wlc->pub); 44362306a36Sopenharmony_ci kfree(wlc->modulecb); 44462306a36Sopenharmony_ci kfree(wlc->default_bss); 44562306a36Sopenharmony_ci kfree(wlc->protection); 44662306a36Sopenharmony_ci kfree(wlc->stf); 44762306a36Sopenharmony_ci kfree(wlc->bandstate[0]); 44862306a36Sopenharmony_ci if (wlc->corestate) 44962306a36Sopenharmony_ci kfree(wlc->corestate->macstat_snapshot); 45062306a36Sopenharmony_ci kfree(wlc->corestate); 45162306a36Sopenharmony_ci if (wlc->hw) 45262306a36Sopenharmony_ci kfree(wlc->hw->bandstate[0]); 45362306a36Sopenharmony_ci kfree(wlc->hw); 45462306a36Sopenharmony_ci if (wlc->beacon) 45562306a36Sopenharmony_ci dev_kfree_skb_any(wlc->beacon); 45662306a36Sopenharmony_ci if (wlc->probe_resp) 45762306a36Sopenharmony_ci dev_kfree_skb_any(wlc->probe_resp); 45862306a36Sopenharmony_ci 45962306a36Sopenharmony_ci kfree(wlc); 46062306a36Sopenharmony_ci} 46162306a36Sopenharmony_ci 46262306a36Sopenharmony_cistatic struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit) 46362306a36Sopenharmony_ci{ 46462306a36Sopenharmony_ci struct brcms_bss_cfg *cfg; 46562306a36Sopenharmony_ci 46662306a36Sopenharmony_ci cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC); 46762306a36Sopenharmony_ci if (cfg == NULL) 46862306a36Sopenharmony_ci goto fail; 46962306a36Sopenharmony_ci 47062306a36Sopenharmony_ci cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); 47162306a36Sopenharmony_ci if (cfg->current_bss == NULL) 47262306a36Sopenharmony_ci goto fail; 47362306a36Sopenharmony_ci 47462306a36Sopenharmony_ci return cfg; 47562306a36Sopenharmony_ci 47662306a36Sopenharmony_ci fail: 47762306a36Sopenharmony_ci brcms_c_bsscfg_mfree(cfg); 47862306a36Sopenharmony_ci return NULL; 47962306a36Sopenharmony_ci} 48062306a36Sopenharmony_ci 48162306a36Sopenharmony_cistatic struct brcms_c_info * 48262306a36Sopenharmony_cibrcms_c_attach_malloc(uint unit, uint *err, uint devid) 48362306a36Sopenharmony_ci{ 48462306a36Sopenharmony_ci struct brcms_c_info *wlc; 48562306a36Sopenharmony_ci 48662306a36Sopenharmony_ci wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC); 48762306a36Sopenharmony_ci if (wlc == NULL) { 48862306a36Sopenharmony_ci *err = 1002; 48962306a36Sopenharmony_ci goto fail; 49062306a36Sopenharmony_ci } 49162306a36Sopenharmony_ci 49262306a36Sopenharmony_ci /* allocate struct brcms_c_pub state structure */ 49362306a36Sopenharmony_ci wlc->pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC); 49462306a36Sopenharmony_ci if (wlc->pub == NULL) { 49562306a36Sopenharmony_ci *err = 1003; 49662306a36Sopenharmony_ci goto fail; 49762306a36Sopenharmony_ci } 49862306a36Sopenharmony_ci wlc->pub->wlc = wlc; 49962306a36Sopenharmony_ci 50062306a36Sopenharmony_ci /* allocate struct brcms_hardware state structure */ 50162306a36Sopenharmony_ci 50262306a36Sopenharmony_ci wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC); 50362306a36Sopenharmony_ci if (wlc->hw == NULL) { 50462306a36Sopenharmony_ci *err = 1005; 50562306a36Sopenharmony_ci goto fail; 50662306a36Sopenharmony_ci } 50762306a36Sopenharmony_ci wlc->hw->wlc = wlc; 50862306a36Sopenharmony_ci 50962306a36Sopenharmony_ci wlc->hw->bandstate[0] = 51062306a36Sopenharmony_ci kcalloc(MAXBANDS, sizeof(struct brcms_hw_band), GFP_ATOMIC); 51162306a36Sopenharmony_ci if (wlc->hw->bandstate[0] == NULL) { 51262306a36Sopenharmony_ci *err = 1006; 51362306a36Sopenharmony_ci goto fail; 51462306a36Sopenharmony_ci } else { 51562306a36Sopenharmony_ci int i; 51662306a36Sopenharmony_ci 51762306a36Sopenharmony_ci for (i = 1; i < MAXBANDS; i++) 51862306a36Sopenharmony_ci wlc->hw->bandstate[i] = (struct brcms_hw_band *) 51962306a36Sopenharmony_ci ((unsigned long)wlc->hw->bandstate[0] + 52062306a36Sopenharmony_ci (sizeof(struct brcms_hw_band) * i)); 52162306a36Sopenharmony_ci } 52262306a36Sopenharmony_ci 52362306a36Sopenharmony_ci wlc->modulecb = 52462306a36Sopenharmony_ci kcalloc(BRCMS_MAXMODULES, sizeof(struct modulecb), 52562306a36Sopenharmony_ci GFP_ATOMIC); 52662306a36Sopenharmony_ci if (wlc->modulecb == NULL) { 52762306a36Sopenharmony_ci *err = 1009; 52862306a36Sopenharmony_ci goto fail; 52962306a36Sopenharmony_ci } 53062306a36Sopenharmony_ci 53162306a36Sopenharmony_ci wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC); 53262306a36Sopenharmony_ci if (wlc->default_bss == NULL) { 53362306a36Sopenharmony_ci *err = 1010; 53462306a36Sopenharmony_ci goto fail; 53562306a36Sopenharmony_ci } 53662306a36Sopenharmony_ci 53762306a36Sopenharmony_ci wlc->bsscfg = brcms_c_bsscfg_malloc(unit); 53862306a36Sopenharmony_ci if (wlc->bsscfg == NULL) { 53962306a36Sopenharmony_ci *err = 1011; 54062306a36Sopenharmony_ci goto fail; 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci 54362306a36Sopenharmony_ci wlc->protection = kzalloc(sizeof(struct brcms_protection), 54462306a36Sopenharmony_ci GFP_ATOMIC); 54562306a36Sopenharmony_ci if (wlc->protection == NULL) { 54662306a36Sopenharmony_ci *err = 1016; 54762306a36Sopenharmony_ci goto fail; 54862306a36Sopenharmony_ci } 54962306a36Sopenharmony_ci 55062306a36Sopenharmony_ci wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC); 55162306a36Sopenharmony_ci if (wlc->stf == NULL) { 55262306a36Sopenharmony_ci *err = 1017; 55362306a36Sopenharmony_ci goto fail; 55462306a36Sopenharmony_ci } 55562306a36Sopenharmony_ci 55662306a36Sopenharmony_ci wlc->bandstate[0] = 55762306a36Sopenharmony_ci kcalloc(MAXBANDS, sizeof(struct brcms_band), GFP_ATOMIC); 55862306a36Sopenharmony_ci if (wlc->bandstate[0] == NULL) { 55962306a36Sopenharmony_ci *err = 1025; 56062306a36Sopenharmony_ci goto fail; 56162306a36Sopenharmony_ci } else { 56262306a36Sopenharmony_ci int i; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci for (i = 1; i < MAXBANDS; i++) 56562306a36Sopenharmony_ci wlc->bandstate[i] = (struct brcms_band *) 56662306a36Sopenharmony_ci ((unsigned long)wlc->bandstate[0] 56762306a36Sopenharmony_ci + (sizeof(struct brcms_band)*i)); 56862306a36Sopenharmony_ci } 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC); 57162306a36Sopenharmony_ci if (wlc->corestate == NULL) { 57262306a36Sopenharmony_ci *err = 1026; 57362306a36Sopenharmony_ci goto fail; 57462306a36Sopenharmony_ci } 57562306a36Sopenharmony_ci 57662306a36Sopenharmony_ci wlc->corestate->macstat_snapshot = 57762306a36Sopenharmony_ci kzalloc(sizeof(struct macstat), GFP_ATOMIC); 57862306a36Sopenharmony_ci if (wlc->corestate->macstat_snapshot == NULL) { 57962306a36Sopenharmony_ci *err = 1027; 58062306a36Sopenharmony_ci goto fail; 58162306a36Sopenharmony_ci } 58262306a36Sopenharmony_ci 58362306a36Sopenharmony_ci return wlc; 58462306a36Sopenharmony_ci 58562306a36Sopenharmony_ci fail: 58662306a36Sopenharmony_ci brcms_c_detach_mfree(wlc); 58762306a36Sopenharmony_ci return NULL; 58862306a36Sopenharmony_ci} 58962306a36Sopenharmony_ci 59062306a36Sopenharmony_ci/* 59162306a36Sopenharmony_ci * Update the slot timing for standard 11b/g (20us slots) 59262306a36Sopenharmony_ci * or shortslot 11g (9us slots) 59362306a36Sopenharmony_ci * The PSM needs to be suspended for this call. 59462306a36Sopenharmony_ci */ 59562306a36Sopenharmony_cistatic void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw, 59662306a36Sopenharmony_ci bool shortslot) 59762306a36Sopenharmony_ci{ 59862306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 59962306a36Sopenharmony_ci 60062306a36Sopenharmony_ci if (shortslot) { 60162306a36Sopenharmony_ci /* 11g short slot: 11a timing */ 60262306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(ifs_slot), 0x0207); 60362306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME); 60462306a36Sopenharmony_ci } else { 60562306a36Sopenharmony_ci /* 11g long slot: 11b timing */ 60662306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(ifs_slot), 0x0212); 60762306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME); 60862306a36Sopenharmony_ci } 60962306a36Sopenharmony_ci} 61062306a36Sopenharmony_ci 61162306a36Sopenharmony_ci/* 61262306a36Sopenharmony_ci * calculate frame duration of a given rate and length, return 61362306a36Sopenharmony_ci * time in usec unit 61462306a36Sopenharmony_ci */ 61562306a36Sopenharmony_cistatic uint brcms_c_calc_frame_time(struct brcms_c_info *wlc, u32 ratespec, 61662306a36Sopenharmony_ci u8 preamble_type, uint mac_len) 61762306a36Sopenharmony_ci{ 61862306a36Sopenharmony_ci uint nsyms, dur = 0, Ndps, kNdps; 61962306a36Sopenharmony_ci uint rate = rspec2rate(ratespec); 62062306a36Sopenharmony_ci 62162306a36Sopenharmony_ci if (rate == 0) { 62262306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: WAR: using rate of 1 mbps\n", 62362306a36Sopenharmony_ci wlc->pub->unit); 62462306a36Sopenharmony_ci rate = BRCM_RATE_1M; 62562306a36Sopenharmony_ci } 62662306a36Sopenharmony_ci 62762306a36Sopenharmony_ci if (is_mcs_rate(ratespec)) { 62862306a36Sopenharmony_ci uint mcs = ratespec & RSPEC_RATE_MASK; 62962306a36Sopenharmony_ci int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); 63062306a36Sopenharmony_ci 63162306a36Sopenharmony_ci dur = PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); 63262306a36Sopenharmony_ci if (preamble_type == BRCMS_MM_PREAMBLE) 63362306a36Sopenharmony_ci dur += PREN_MM_EXT; 63462306a36Sopenharmony_ci /* 1000Ndbps = kbps * 4 */ 63562306a36Sopenharmony_ci kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), 63662306a36Sopenharmony_ci rspec_issgi(ratespec)) * 4; 63762306a36Sopenharmony_ci 63862306a36Sopenharmony_ci if (rspec_stc(ratespec) == 0) 63962306a36Sopenharmony_ci nsyms = 64062306a36Sopenharmony_ci CEIL((APHY_SERVICE_NBITS + 8 * mac_len + 64162306a36Sopenharmony_ci APHY_TAIL_NBITS) * 1000, kNdps); 64262306a36Sopenharmony_ci else 64362306a36Sopenharmony_ci /* STBC needs to have even number of symbols */ 64462306a36Sopenharmony_ci nsyms = 64562306a36Sopenharmony_ci 2 * 64662306a36Sopenharmony_ci CEIL((APHY_SERVICE_NBITS + 8 * mac_len + 64762306a36Sopenharmony_ci APHY_TAIL_NBITS) * 1000, 2 * kNdps); 64862306a36Sopenharmony_ci 64962306a36Sopenharmony_ci dur += APHY_SYMBOL_TIME * nsyms; 65062306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_2G) 65162306a36Sopenharmony_ci dur += DOT11_OFDM_SIGNAL_EXTENSION; 65262306a36Sopenharmony_ci } else if (is_ofdm_rate(rate)) { 65362306a36Sopenharmony_ci dur = APHY_PREAMBLE_TIME; 65462306a36Sopenharmony_ci dur += APHY_SIGNAL_TIME; 65562306a36Sopenharmony_ci /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ 65662306a36Sopenharmony_ci Ndps = rate * 2; 65762306a36Sopenharmony_ci /* NSyms = CEILING((SERVICE + 8*NBytes + TAIL) / Ndbps) */ 65862306a36Sopenharmony_ci nsyms = 65962306a36Sopenharmony_ci CEIL((APHY_SERVICE_NBITS + 8 * mac_len + APHY_TAIL_NBITS), 66062306a36Sopenharmony_ci Ndps); 66162306a36Sopenharmony_ci dur += APHY_SYMBOL_TIME * nsyms; 66262306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_2G) 66362306a36Sopenharmony_ci dur += DOT11_OFDM_SIGNAL_EXTENSION; 66462306a36Sopenharmony_ci } else { 66562306a36Sopenharmony_ci /* 66662306a36Sopenharmony_ci * calc # bits * 2 so factor of 2 in rate (1/2 mbps) 66762306a36Sopenharmony_ci * will divide out 66862306a36Sopenharmony_ci */ 66962306a36Sopenharmony_ci mac_len = mac_len * 8 * 2; 67062306a36Sopenharmony_ci /* calc ceiling of bits/rate = microseconds of air time */ 67162306a36Sopenharmony_ci dur = (mac_len + rate - 1) / rate; 67262306a36Sopenharmony_ci if (preamble_type & BRCMS_SHORT_PREAMBLE) 67362306a36Sopenharmony_ci dur += BPHY_PLCP_SHORT_TIME; 67462306a36Sopenharmony_ci else 67562306a36Sopenharmony_ci dur += BPHY_PLCP_TIME; 67662306a36Sopenharmony_ci } 67762306a36Sopenharmony_ci return dur; 67862306a36Sopenharmony_ci} 67962306a36Sopenharmony_ci 68062306a36Sopenharmony_cistatic void brcms_c_write_inits(struct brcms_hardware *wlc_hw, 68162306a36Sopenharmony_ci const struct d11init *inits) 68262306a36Sopenharmony_ci{ 68362306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 68462306a36Sopenharmony_ci int i; 68562306a36Sopenharmony_ci uint offset; 68662306a36Sopenharmony_ci u16 size; 68762306a36Sopenharmony_ci u32 value; 68862306a36Sopenharmony_ci 68962306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); 69062306a36Sopenharmony_ci 69162306a36Sopenharmony_ci for (i = 0; inits[i].addr != cpu_to_le16(0xffff); i++) { 69262306a36Sopenharmony_ci size = le16_to_cpu(inits[i].size); 69362306a36Sopenharmony_ci offset = le16_to_cpu(inits[i].addr); 69462306a36Sopenharmony_ci value = le32_to_cpu(inits[i].value); 69562306a36Sopenharmony_ci if (size == 2) 69662306a36Sopenharmony_ci bcma_write16(core, offset, value); 69762306a36Sopenharmony_ci else if (size == 4) 69862306a36Sopenharmony_ci bcma_write32(core, offset, value); 69962306a36Sopenharmony_ci else 70062306a36Sopenharmony_ci break; 70162306a36Sopenharmony_ci } 70262306a36Sopenharmony_ci} 70362306a36Sopenharmony_ci 70462306a36Sopenharmony_cistatic void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs) 70562306a36Sopenharmony_ci{ 70662306a36Sopenharmony_ci u8 idx; 70762306a36Sopenharmony_ci static const u16 addr[] = { 70862306a36Sopenharmony_ci M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, 70962306a36Sopenharmony_ci M_HOST_FLAGS5 71062306a36Sopenharmony_ci }; 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci for (idx = 0; idx < MHFMAX; idx++) 71362306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]); 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; 71962306a36Sopenharmony_ci 72062306a36Sopenharmony_ci /* init microcode host flags */ 72162306a36Sopenharmony_ci brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs); 72262306a36Sopenharmony_ci 72362306a36Sopenharmony_ci /* do band-specific ucode IHR, SHM, and SCR inits */ 72462306a36Sopenharmony_ci if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { 72562306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band)) 72662306a36Sopenharmony_ci brcms_c_write_inits(wlc_hw, ucode->d11n0bsinitvals16); 72762306a36Sopenharmony_ci else 72862306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 72962306a36Sopenharmony_ci "%s: wl%d: unsupported phy in corerev %d\n", 73062306a36Sopenharmony_ci __func__, wlc_hw->unit, 73162306a36Sopenharmony_ci wlc_hw->corerev); 73262306a36Sopenharmony_ci } else { 73362306a36Sopenharmony_ci if (D11REV_IS(wlc_hw->corerev, 24)) { 73462306a36Sopenharmony_ci if (BRCMS_ISLCNPHY(wlc_hw->band)) 73562306a36Sopenharmony_ci brcms_c_write_inits(wlc_hw, 73662306a36Sopenharmony_ci ucode->d11lcn0bsinitvals24); 73762306a36Sopenharmony_ci else 73862306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 73962306a36Sopenharmony_ci "%s: wl%d: unsupported phy in core rev %d\n", 74062306a36Sopenharmony_ci __func__, wlc_hw->unit, 74162306a36Sopenharmony_ci wlc_hw->corerev); 74262306a36Sopenharmony_ci } else { 74362306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 74462306a36Sopenharmony_ci "%s: wl%d: unsupported corerev %d\n", 74562306a36Sopenharmony_ci __func__, wlc_hw->unit, wlc_hw->corerev); 74662306a36Sopenharmony_ci } 74762306a36Sopenharmony_ci } 74862306a36Sopenharmony_ci} 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_cistatic void brcms_b_core_ioctl(struct brcms_hardware *wlc_hw, u32 m, u32 v) 75162306a36Sopenharmony_ci{ 75262306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 75362306a36Sopenharmony_ci u32 ioctl = bcma_aread32(core, BCMA_IOCTL) & ~m; 75462306a36Sopenharmony_ci 75562306a36Sopenharmony_ci bcma_awrite32(core, BCMA_IOCTL, ioctl | v); 75662306a36Sopenharmony_ci} 75762306a36Sopenharmony_ci 75862306a36Sopenharmony_cistatic void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk) 75962306a36Sopenharmony_ci{ 76062306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d: clk %d\n", wlc_hw->unit, clk); 76162306a36Sopenharmony_ci 76262306a36Sopenharmony_ci wlc_hw->phyclk = clk; 76362306a36Sopenharmony_ci 76462306a36Sopenharmony_ci if (OFF == clk) { /* clear gmode bit, put phy into reset */ 76562306a36Sopenharmony_ci 76662306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC | SICF_GMODE), 76762306a36Sopenharmony_ci (SICF_PRST | SICF_FGC)); 76862306a36Sopenharmony_ci udelay(1); 76962306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_PRST); 77062306a36Sopenharmony_ci udelay(1); 77162306a36Sopenharmony_ci 77262306a36Sopenharmony_ci } else { /* take phy out of reset */ 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_FGC), SICF_FGC); 77562306a36Sopenharmony_ci udelay(1); 77662306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); 77762306a36Sopenharmony_ci udelay(1); 77862306a36Sopenharmony_ci 77962306a36Sopenharmony_ci } 78062306a36Sopenharmony_ci} 78162306a36Sopenharmony_ci 78262306a36Sopenharmony_ci/* low-level band switch utility routine */ 78362306a36Sopenharmony_cistatic void brcms_c_setxband(struct brcms_hardware *wlc_hw, uint bandunit) 78462306a36Sopenharmony_ci{ 78562306a36Sopenharmony_ci brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, 78662306a36Sopenharmony_ci bandunit); 78762306a36Sopenharmony_ci 78862306a36Sopenharmony_ci wlc_hw->band = wlc_hw->bandstate[bandunit]; 78962306a36Sopenharmony_ci 79062306a36Sopenharmony_ci /* 79162306a36Sopenharmony_ci * BMAC_NOTE: 79262306a36Sopenharmony_ci * until we eliminate need for wlc->band refs in low level code 79362306a36Sopenharmony_ci */ 79462306a36Sopenharmony_ci wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit]; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci /* set gmode core flag */ 79762306a36Sopenharmony_ci if (wlc_hw->sbclk && !wlc_hw->noreset) { 79862306a36Sopenharmony_ci u32 gmode = 0; 79962306a36Sopenharmony_ci 80062306a36Sopenharmony_ci if (bandunit == 0) 80162306a36Sopenharmony_ci gmode = SICF_GMODE; 80262306a36Sopenharmony_ci 80362306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_GMODE, gmode); 80462306a36Sopenharmony_ci } 80562306a36Sopenharmony_ci} 80662306a36Sopenharmony_ci 80762306a36Sopenharmony_ci/* switch to new band but leave it inactive */ 80862306a36Sopenharmony_cistatic u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit) 80962306a36Sopenharmony_ci{ 81062306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 81162306a36Sopenharmony_ci u32 macintmask; 81262306a36Sopenharmony_ci u32 macctrl; 81362306a36Sopenharmony_ci 81462306a36Sopenharmony_ci brcms_dbg_mac80211(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); 81562306a36Sopenharmony_ci macctrl = bcma_read32(wlc_hw->d11core, 81662306a36Sopenharmony_ci D11REGOFFS(maccontrol)); 81762306a36Sopenharmony_ci WARN_ON((macctrl & MCTL_EN_MAC) != 0); 81862306a36Sopenharmony_ci 81962306a36Sopenharmony_ci /* disable interrupts */ 82062306a36Sopenharmony_ci macintmask = brcms_intrsoff(wlc->wl); 82162306a36Sopenharmony_ci 82262306a36Sopenharmony_ci /* radio off */ 82362306a36Sopenharmony_ci wlc_phy_switch_radio(wlc_hw->band->pi, OFF); 82462306a36Sopenharmony_ci 82562306a36Sopenharmony_ci brcms_b_core_phy_clk(wlc_hw, OFF); 82662306a36Sopenharmony_ci 82762306a36Sopenharmony_ci brcms_c_setxband(wlc_hw, bandunit); 82862306a36Sopenharmony_ci 82962306a36Sopenharmony_ci return macintmask; 83062306a36Sopenharmony_ci} 83162306a36Sopenharmony_ci 83262306a36Sopenharmony_ci/* process an individual struct tx_status */ 83362306a36Sopenharmony_cistatic bool 83462306a36Sopenharmony_cibrcms_c_dotxstatus(struct brcms_c_info *wlc, struct tx_status *txs) 83562306a36Sopenharmony_ci{ 83662306a36Sopenharmony_ci struct sk_buff *p = NULL; 83762306a36Sopenharmony_ci uint queue = NFIFO; 83862306a36Sopenharmony_ci struct dma_pub *dma = NULL; 83962306a36Sopenharmony_ci struct d11txh *txh = NULL; 84062306a36Sopenharmony_ci struct scb *scb = NULL; 84162306a36Sopenharmony_ci int tx_frame_count; 84262306a36Sopenharmony_ci uint supr_status; 84362306a36Sopenharmony_ci bool lastframe; 84462306a36Sopenharmony_ci struct ieee80211_hdr *h; 84562306a36Sopenharmony_ci struct ieee80211_tx_info *tx_info; 84662306a36Sopenharmony_ci struct ieee80211_tx_rate *txrate; 84762306a36Sopenharmony_ci int i; 84862306a36Sopenharmony_ci bool fatal = true; 84962306a36Sopenharmony_ci 85062306a36Sopenharmony_ci trace_brcms_txstatus(&wlc->hw->d11core->dev, txs->framelen, 85162306a36Sopenharmony_ci txs->frameid, txs->status, txs->lasttxtime, 85262306a36Sopenharmony_ci txs->sequence, txs->phyerr, txs->ackphyrxsh); 85362306a36Sopenharmony_ci 85462306a36Sopenharmony_ci /* discard intermediate indications for ucode with one legitimate case: 85562306a36Sopenharmony_ci * e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, 85662306a36Sopenharmony_ci * but the subsequent tx of DATA failed. so it will start rts/cts 85762306a36Sopenharmony_ci * from the beginning (resetting the rts transmission count) 85862306a36Sopenharmony_ci */ 85962306a36Sopenharmony_ci if (!(txs->status & TX_STATUS_AMPDU) 86062306a36Sopenharmony_ci && (txs->status & TX_STATUS_INTERMEDIATE)) { 86162306a36Sopenharmony_ci brcms_dbg_tx(wlc->hw->d11core, "INTERMEDIATE but not AMPDU\n"); 86262306a36Sopenharmony_ci fatal = false; 86362306a36Sopenharmony_ci goto out; 86462306a36Sopenharmony_ci } 86562306a36Sopenharmony_ci 86662306a36Sopenharmony_ci queue = txs->frameid & TXFID_QUEUE_MASK; 86762306a36Sopenharmony_ci if (queue >= NFIFO) { 86862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "queue %u >= NFIFO\n", queue); 86962306a36Sopenharmony_ci goto out; 87062306a36Sopenharmony_ci } 87162306a36Sopenharmony_ci 87262306a36Sopenharmony_ci dma = wlc->hw->di[queue]; 87362306a36Sopenharmony_ci 87462306a36Sopenharmony_ci p = dma_getnexttxp(wlc->hw->di[queue], DMA_RANGE_TRANSMITTED); 87562306a36Sopenharmony_ci if (p == NULL) { 87662306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "dma_getnexttxp returned null!\n"); 87762306a36Sopenharmony_ci goto out; 87862306a36Sopenharmony_ci } 87962306a36Sopenharmony_ci 88062306a36Sopenharmony_ci txh = (struct d11txh *) (p->data); 88162306a36Sopenharmony_ci 88262306a36Sopenharmony_ci if (txs->phyerr) 88362306a36Sopenharmony_ci brcms_dbg_tx(wlc->hw->d11core, "phyerr 0x%x, rate 0x%x\n", 88462306a36Sopenharmony_ci txs->phyerr, txh->MainRates); 88562306a36Sopenharmony_ci 88662306a36Sopenharmony_ci if (txs->frameid != le16_to_cpu(txh->TxFrameID)) { 88762306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "frameid != txh->TxFrameID\n"); 88862306a36Sopenharmony_ci goto out; 88962306a36Sopenharmony_ci } 89062306a36Sopenharmony_ci tx_info = IEEE80211_SKB_CB(p); 89162306a36Sopenharmony_ci h = (struct ieee80211_hdr *)((u8 *) (txh + 1) + D11_PHY_HDR_LEN); 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci if (tx_info->rate_driver_data[0]) 89462306a36Sopenharmony_ci scb = &wlc->pri_scb; 89562306a36Sopenharmony_ci 89662306a36Sopenharmony_ci if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) { 89762306a36Sopenharmony_ci brcms_c_ampdu_dotxstatus(wlc->ampdu, scb, p, txs); 89862306a36Sopenharmony_ci fatal = false; 89962306a36Sopenharmony_ci goto out; 90062306a36Sopenharmony_ci } 90162306a36Sopenharmony_ci 90262306a36Sopenharmony_ci /* 90362306a36Sopenharmony_ci * brcms_c_ampdu_dotxstatus() will trace tx descriptors for AMPDU 90462306a36Sopenharmony_ci * frames; this traces them for the rest. 90562306a36Sopenharmony_ci */ 90662306a36Sopenharmony_ci trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, sizeof(*txh)); 90762306a36Sopenharmony_ci 90862306a36Sopenharmony_ci supr_status = txs->status & TX_STATUS_SUPR_MASK; 90962306a36Sopenharmony_ci if (supr_status == TX_STATUS_SUPR_BADCH) { 91062306a36Sopenharmony_ci unsigned xfts = le16_to_cpu(txh->XtraFrameTypes); 91162306a36Sopenharmony_ci brcms_dbg_tx(wlc->hw->d11core, 91262306a36Sopenharmony_ci "Pkt tx suppressed, dest chan %u, current %d\n", 91362306a36Sopenharmony_ci (xfts >> XFTS_CHANNEL_SHIFT) & 0xff, 91462306a36Sopenharmony_ci CHSPEC_CHANNEL(wlc->default_bss->chanspec)); 91562306a36Sopenharmony_ci } 91662306a36Sopenharmony_ci 91762306a36Sopenharmony_ci tx_frame_count = 91862306a36Sopenharmony_ci (txs->status & TX_STATUS_FRM_RTX_MASK) >> TX_STATUS_FRM_RTX_SHIFT; 91962306a36Sopenharmony_ci 92062306a36Sopenharmony_ci lastframe = !ieee80211_has_morefrags(h->frame_control); 92162306a36Sopenharmony_ci 92262306a36Sopenharmony_ci if (!lastframe) { 92362306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "Not last frame!\n"); 92462306a36Sopenharmony_ci } else { 92562306a36Sopenharmony_ci /* 92662306a36Sopenharmony_ci * Set information to be consumed by Minstrel ht. 92762306a36Sopenharmony_ci * 92862306a36Sopenharmony_ci * The "fallback limit" is the number of tx attempts a given 92962306a36Sopenharmony_ci * MPDU is sent at the "primary" rate. Tx attempts beyond that 93062306a36Sopenharmony_ci * limit are sent at the "secondary" rate. 93162306a36Sopenharmony_ci * A 'short frame' does not exceed RTS treshold. 93262306a36Sopenharmony_ci */ 93362306a36Sopenharmony_ci u16 sfbl, /* Short Frame Rate Fallback Limit */ 93462306a36Sopenharmony_ci lfbl, /* Long Frame Rate Fallback Limit */ 93562306a36Sopenharmony_ci fbl; 93662306a36Sopenharmony_ci 93762306a36Sopenharmony_ci if (queue < IEEE80211_NUM_ACS) { 93862306a36Sopenharmony_ci sfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], 93962306a36Sopenharmony_ci EDCF_SFB); 94062306a36Sopenharmony_ci lfbl = GFIELD(wlc->wme_retries[wme_fifo2ac[queue]], 94162306a36Sopenharmony_ci EDCF_LFB); 94262306a36Sopenharmony_ci } else { 94362306a36Sopenharmony_ci sfbl = wlc->SFBL; 94462306a36Sopenharmony_ci lfbl = wlc->LFBL; 94562306a36Sopenharmony_ci } 94662306a36Sopenharmony_ci 94762306a36Sopenharmony_ci txrate = tx_info->status.rates; 94862306a36Sopenharmony_ci if (txrate[0].flags & IEEE80211_TX_RC_USE_RTS_CTS) 94962306a36Sopenharmony_ci fbl = lfbl; 95062306a36Sopenharmony_ci else 95162306a36Sopenharmony_ci fbl = sfbl; 95262306a36Sopenharmony_ci 95362306a36Sopenharmony_ci ieee80211_tx_info_clear_status(tx_info); 95462306a36Sopenharmony_ci 95562306a36Sopenharmony_ci if ((tx_frame_count > fbl) && (txrate[1].idx >= 0)) { 95662306a36Sopenharmony_ci /* 95762306a36Sopenharmony_ci * rate selection requested a fallback rate 95862306a36Sopenharmony_ci * and we used it 95962306a36Sopenharmony_ci */ 96062306a36Sopenharmony_ci txrate[0].count = fbl; 96162306a36Sopenharmony_ci txrate[1].count = tx_frame_count - fbl; 96262306a36Sopenharmony_ci } else { 96362306a36Sopenharmony_ci /* 96462306a36Sopenharmony_ci * rate selection did not request fallback rate, or 96562306a36Sopenharmony_ci * we didn't need it 96662306a36Sopenharmony_ci */ 96762306a36Sopenharmony_ci txrate[0].count = tx_frame_count; 96862306a36Sopenharmony_ci /* 96962306a36Sopenharmony_ci * rc80211_minstrel.c:minstrel_tx_status() expects 97062306a36Sopenharmony_ci * unused rates to be marked with idx = -1 97162306a36Sopenharmony_ci */ 97262306a36Sopenharmony_ci txrate[1].idx = -1; 97362306a36Sopenharmony_ci txrate[1].count = 0; 97462306a36Sopenharmony_ci } 97562306a36Sopenharmony_ci 97662306a36Sopenharmony_ci /* clear the rest of the rates */ 97762306a36Sopenharmony_ci for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) { 97862306a36Sopenharmony_ci txrate[i].idx = -1; 97962306a36Sopenharmony_ci txrate[i].count = 0; 98062306a36Sopenharmony_ci } 98162306a36Sopenharmony_ci 98262306a36Sopenharmony_ci if (txs->status & TX_STATUS_ACK_RCV) 98362306a36Sopenharmony_ci tx_info->flags |= IEEE80211_TX_STAT_ACK; 98462306a36Sopenharmony_ci } 98562306a36Sopenharmony_ci 98662306a36Sopenharmony_ci if (lastframe) { 98762306a36Sopenharmony_ci /* remove PLCP & Broadcom tx descriptor header */ 98862306a36Sopenharmony_ci skb_pull(p, D11_PHY_HDR_LEN); 98962306a36Sopenharmony_ci skb_pull(p, D11_TXH_LEN); 99062306a36Sopenharmony_ci ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw, p); 99162306a36Sopenharmony_ci } else { 99262306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 99362306a36Sopenharmony_ci "%s: Not last frame => not calling tx_status\n", 99462306a36Sopenharmony_ci __func__); 99562306a36Sopenharmony_ci } 99662306a36Sopenharmony_ci 99762306a36Sopenharmony_ci fatal = false; 99862306a36Sopenharmony_ci 99962306a36Sopenharmony_ci out: 100062306a36Sopenharmony_ci if (fatal) { 100162306a36Sopenharmony_ci if (txh) 100262306a36Sopenharmony_ci trace_brcms_txdesc(&wlc->hw->d11core->dev, txh, 100362306a36Sopenharmony_ci sizeof(*txh)); 100462306a36Sopenharmony_ci brcmu_pkt_buf_free_skb(p); 100562306a36Sopenharmony_ci } 100662306a36Sopenharmony_ci 100762306a36Sopenharmony_ci if (dma && queue < NFIFO) { 100862306a36Sopenharmony_ci u16 ac_queue = brcms_fifo_to_ac(queue); 100962306a36Sopenharmony_ci if (dma->txavail > TX_HEADROOM && queue < TX_BCMC_FIFO && 101062306a36Sopenharmony_ci ieee80211_queue_stopped(wlc->pub->ieee_hw, ac_queue)) 101162306a36Sopenharmony_ci ieee80211_wake_queue(wlc->pub->ieee_hw, ac_queue); 101262306a36Sopenharmony_ci dma_kick_tx(dma); 101362306a36Sopenharmony_ci } 101462306a36Sopenharmony_ci 101562306a36Sopenharmony_ci return fatal; 101662306a36Sopenharmony_ci} 101762306a36Sopenharmony_ci 101862306a36Sopenharmony_ci/* process tx completion events in BMAC 101962306a36Sopenharmony_ci * Return true if more tx status need to be processed. false otherwise. 102062306a36Sopenharmony_ci */ 102162306a36Sopenharmony_cistatic bool 102262306a36Sopenharmony_cibrcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal) 102362306a36Sopenharmony_ci{ 102462306a36Sopenharmony_ci struct bcma_device *core; 102562306a36Sopenharmony_ci struct tx_status txstatus, *txs; 102662306a36Sopenharmony_ci u32 s1, s2; 102762306a36Sopenharmony_ci uint n = 0; 102862306a36Sopenharmony_ci /* 102962306a36Sopenharmony_ci * Param 'max_tx_num' indicates max. # tx status to process before 103062306a36Sopenharmony_ci * break out. 103162306a36Sopenharmony_ci */ 103262306a36Sopenharmony_ci uint max_tx_num = bound ? TXSBND : -1; 103362306a36Sopenharmony_ci 103462306a36Sopenharmony_ci txs = &txstatus; 103562306a36Sopenharmony_ci core = wlc_hw->d11core; 103662306a36Sopenharmony_ci *fatal = false; 103762306a36Sopenharmony_ci 103862306a36Sopenharmony_ci while (n < max_tx_num) { 103962306a36Sopenharmony_ci s1 = bcma_read32(core, D11REGOFFS(frmtxstatus)); 104062306a36Sopenharmony_ci if (s1 == 0xffffffff) { 104162306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, 104262306a36Sopenharmony_ci __func__); 104362306a36Sopenharmony_ci *fatal = true; 104462306a36Sopenharmony_ci return false; 104562306a36Sopenharmony_ci } 104662306a36Sopenharmony_ci /* only process when valid */ 104762306a36Sopenharmony_ci if (!(s1 & TXS_V)) 104862306a36Sopenharmony_ci break; 104962306a36Sopenharmony_ci 105062306a36Sopenharmony_ci s2 = bcma_read32(core, D11REGOFFS(frmtxstatus2)); 105162306a36Sopenharmony_ci txs->status = s1 & TXS_STATUS_MASK; 105262306a36Sopenharmony_ci txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT; 105362306a36Sopenharmony_ci txs->sequence = s2 & TXS_SEQ_MASK; 105462306a36Sopenharmony_ci txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT; 105562306a36Sopenharmony_ci txs->lasttxtime = 0; 105662306a36Sopenharmony_ci 105762306a36Sopenharmony_ci *fatal = brcms_c_dotxstatus(wlc_hw->wlc, txs); 105862306a36Sopenharmony_ci if (*fatal) 105962306a36Sopenharmony_ci return false; 106062306a36Sopenharmony_ci n++; 106162306a36Sopenharmony_ci } 106262306a36Sopenharmony_ci 106362306a36Sopenharmony_ci return n >= max_tx_num; 106462306a36Sopenharmony_ci} 106562306a36Sopenharmony_ci 106662306a36Sopenharmony_cistatic void brcms_c_tbtt(struct brcms_c_info *wlc) 106762306a36Sopenharmony_ci{ 106862306a36Sopenharmony_ci if (wlc->bsscfg->type == BRCMS_TYPE_ADHOC) 106962306a36Sopenharmony_ci /* 107062306a36Sopenharmony_ci * DirFrmQ is now valid...defer setting until end 107162306a36Sopenharmony_ci * of ATIM window 107262306a36Sopenharmony_ci */ 107362306a36Sopenharmony_ci wlc->qvalid |= MCMD_DIRFRMQVAL; 107462306a36Sopenharmony_ci} 107562306a36Sopenharmony_ci 107662306a36Sopenharmony_ci/* set initial host flags value */ 107762306a36Sopenharmony_cistatic void 107862306a36Sopenharmony_cibrcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init) 107962306a36Sopenharmony_ci{ 108062306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 108162306a36Sopenharmony_ci 108262306a36Sopenharmony_ci memset(mhfs, 0, MHFMAX * sizeof(u16)); 108362306a36Sopenharmony_ci 108462306a36Sopenharmony_ci mhfs[MHF2] |= mhf2_init; 108562306a36Sopenharmony_ci 108662306a36Sopenharmony_ci /* prohibit use of slowclock on multifunction boards */ 108762306a36Sopenharmony_ci if (wlc_hw->boardflags & BFL_NOPLLDOWN) 108862306a36Sopenharmony_ci mhfs[MHF1] |= MHF1_FORCEFASTCLK; 108962306a36Sopenharmony_ci 109062306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) { 109162306a36Sopenharmony_ci mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR; 109262306a36Sopenharmony_ci mhfs[MHF1] |= MHF1_IQSWAP_WAR; 109362306a36Sopenharmony_ci } 109462306a36Sopenharmony_ci} 109562306a36Sopenharmony_ci 109662306a36Sopenharmony_cistatic uint 109762306a36Sopenharmony_cidmareg(uint direction, uint fifonum) 109862306a36Sopenharmony_ci{ 109962306a36Sopenharmony_ci if (direction == DMA_TX) 110062306a36Sopenharmony_ci return offsetof(struct d11regs, fifo64regs[fifonum].dmaxmt); 110162306a36Sopenharmony_ci return offsetof(struct d11regs, fifo64regs[fifonum].dmarcv); 110262306a36Sopenharmony_ci} 110362306a36Sopenharmony_ci 110462306a36Sopenharmony_cistatic bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme) 110562306a36Sopenharmony_ci{ 110662306a36Sopenharmony_ci uint i; 110762306a36Sopenharmony_ci char name[8]; 110862306a36Sopenharmony_ci /* 110962306a36Sopenharmony_ci * ucode host flag 2 needed for pio mode, independent of band and fifo 111062306a36Sopenharmony_ci */ 111162306a36Sopenharmony_ci u16 pio_mhf2 = 0; 111262306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 111362306a36Sopenharmony_ci uint unit = wlc_hw->unit; 111462306a36Sopenharmony_ci 111562306a36Sopenharmony_ci /* name and offsets for dma_attach */ 111662306a36Sopenharmony_ci snprintf(name, sizeof(name), "wl%d", unit); 111762306a36Sopenharmony_ci 111862306a36Sopenharmony_ci if (wlc_hw->di[0] == NULL) { /* Init FIFOs */ 111962306a36Sopenharmony_ci int dma_attach_err = 0; 112062306a36Sopenharmony_ci 112162306a36Sopenharmony_ci /* 112262306a36Sopenharmony_ci * FIFO 0 112362306a36Sopenharmony_ci * TX: TX_AC_BK_FIFO (TX AC Background data packets) 112462306a36Sopenharmony_ci * RX: RX_FIFO (RX data packets) 112562306a36Sopenharmony_ci */ 112662306a36Sopenharmony_ci wlc_hw->di[0] = dma_attach(name, wlc, 112762306a36Sopenharmony_ci (wme ? dmareg(DMA_TX, 0) : 0), 112862306a36Sopenharmony_ci dmareg(DMA_RX, 0), 112962306a36Sopenharmony_ci (wme ? NTXD : 0), NRXD, 113062306a36Sopenharmony_ci RXBUFSZ, -1, NRXBUFPOST, 113162306a36Sopenharmony_ci BRCMS_HWRXOFF); 113262306a36Sopenharmony_ci dma_attach_err |= (NULL == wlc_hw->di[0]); 113362306a36Sopenharmony_ci 113462306a36Sopenharmony_ci /* 113562306a36Sopenharmony_ci * FIFO 1 113662306a36Sopenharmony_ci * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets) 113762306a36Sopenharmony_ci * (legacy) TX_DATA_FIFO (TX data packets) 113862306a36Sopenharmony_ci * RX: UNUSED 113962306a36Sopenharmony_ci */ 114062306a36Sopenharmony_ci wlc_hw->di[1] = dma_attach(name, wlc, 114162306a36Sopenharmony_ci dmareg(DMA_TX, 1), 0, 114262306a36Sopenharmony_ci NTXD, 0, 0, -1, 0, 0); 114362306a36Sopenharmony_ci dma_attach_err |= (NULL == wlc_hw->di[1]); 114462306a36Sopenharmony_ci 114562306a36Sopenharmony_ci /* 114662306a36Sopenharmony_ci * FIFO 2 114762306a36Sopenharmony_ci * TX: TX_AC_VI_FIFO (TX AC Video data packets) 114862306a36Sopenharmony_ci * RX: UNUSED 114962306a36Sopenharmony_ci */ 115062306a36Sopenharmony_ci wlc_hw->di[2] = dma_attach(name, wlc, 115162306a36Sopenharmony_ci dmareg(DMA_TX, 2), 0, 115262306a36Sopenharmony_ci NTXD, 0, 0, -1, 0, 0); 115362306a36Sopenharmony_ci dma_attach_err |= (NULL == wlc_hw->di[2]); 115462306a36Sopenharmony_ci /* 115562306a36Sopenharmony_ci * FIFO 3 115662306a36Sopenharmony_ci * TX: TX_AC_VO_FIFO (TX AC Voice data packets) 115762306a36Sopenharmony_ci * (legacy) TX_CTL_FIFO (TX control & mgmt packets) 115862306a36Sopenharmony_ci */ 115962306a36Sopenharmony_ci wlc_hw->di[3] = dma_attach(name, wlc, 116062306a36Sopenharmony_ci dmareg(DMA_TX, 3), 116162306a36Sopenharmony_ci 0, NTXD, 0, 0, -1, 116262306a36Sopenharmony_ci 0, 0); 116362306a36Sopenharmony_ci dma_attach_err |= (NULL == wlc_hw->di[3]); 116462306a36Sopenharmony_ci/* Cleaner to leave this as if with AP defined */ 116562306a36Sopenharmony_ci 116662306a36Sopenharmony_ci if (dma_attach_err) { 116762306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 116862306a36Sopenharmony_ci "wl%d: wlc_attach: dma_attach failed\n", 116962306a36Sopenharmony_ci unit); 117062306a36Sopenharmony_ci return false; 117162306a36Sopenharmony_ci } 117262306a36Sopenharmony_ci 117362306a36Sopenharmony_ci /* get pointer to dma engine tx flow control variable */ 117462306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) 117562306a36Sopenharmony_ci if (wlc_hw->di[i]) 117662306a36Sopenharmony_ci wlc_hw->txavail[i] = 117762306a36Sopenharmony_ci (uint *) dma_getvar(wlc_hw->di[i], 117862306a36Sopenharmony_ci "&txavail"); 117962306a36Sopenharmony_ci } 118062306a36Sopenharmony_ci 118162306a36Sopenharmony_ci /* initial ucode host flags */ 118262306a36Sopenharmony_ci brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2); 118362306a36Sopenharmony_ci 118462306a36Sopenharmony_ci return true; 118562306a36Sopenharmony_ci} 118662306a36Sopenharmony_ci 118762306a36Sopenharmony_cistatic void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw) 118862306a36Sopenharmony_ci{ 118962306a36Sopenharmony_ci uint j; 119062306a36Sopenharmony_ci 119162306a36Sopenharmony_ci for (j = 0; j < NFIFO; j++) { 119262306a36Sopenharmony_ci if (wlc_hw->di[j]) { 119362306a36Sopenharmony_ci dma_detach(wlc_hw->di[j]); 119462306a36Sopenharmony_ci wlc_hw->di[j] = NULL; 119562306a36Sopenharmony_ci } 119662306a36Sopenharmony_ci } 119762306a36Sopenharmony_ci} 119862306a36Sopenharmony_ci 119962306a36Sopenharmony_ci/* 120062306a36Sopenharmony_ci * Initialize brcms_c_info default values ... 120162306a36Sopenharmony_ci * may get overrides later in this function 120262306a36Sopenharmony_ci * BMAC_NOTES, move low out and resolve the dangling ones 120362306a36Sopenharmony_ci */ 120462306a36Sopenharmony_cistatic void brcms_b_info_init(struct brcms_hardware *wlc_hw) 120562306a36Sopenharmony_ci{ 120662306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_hw->wlc; 120762306a36Sopenharmony_ci 120862306a36Sopenharmony_ci /* set default sw macintmask value */ 120962306a36Sopenharmony_ci wlc->defmacintmask = DEF_MACINTMASK; 121062306a36Sopenharmony_ci 121162306a36Sopenharmony_ci /* various 802.11g modes */ 121262306a36Sopenharmony_ci wlc_hw->shortslot = false; 121362306a36Sopenharmony_ci 121462306a36Sopenharmony_ci wlc_hw->SFBL = RETRY_SHORT_FB; 121562306a36Sopenharmony_ci wlc_hw->LFBL = RETRY_LONG_FB; 121662306a36Sopenharmony_ci 121762306a36Sopenharmony_ci /* default mac retry limits */ 121862306a36Sopenharmony_ci wlc_hw->SRL = RETRY_SHORT_DEF; 121962306a36Sopenharmony_ci wlc_hw->LRL = RETRY_LONG_DEF; 122062306a36Sopenharmony_ci wlc_hw->chanspec = ch20mhz_chspec(1); 122162306a36Sopenharmony_ci} 122262306a36Sopenharmony_ci 122362306a36Sopenharmony_cistatic void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw) 122462306a36Sopenharmony_ci{ 122562306a36Sopenharmony_ci /* delay before first read of ucode state */ 122662306a36Sopenharmony_ci udelay(40); 122762306a36Sopenharmony_ci 122862306a36Sopenharmony_ci /* wait until ucode is no longer asleep */ 122962306a36Sopenharmony_ci SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) == 123062306a36Sopenharmony_ci DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly); 123162306a36Sopenharmony_ci} 123262306a36Sopenharmony_ci 123362306a36Sopenharmony_ci/* control chip clock to save power, enable dynamic clock or force fast clock */ 123462306a36Sopenharmony_cistatic void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, enum bcma_clkmode mode) 123562306a36Sopenharmony_ci{ 123662306a36Sopenharmony_ci if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) { 123762306a36Sopenharmony_ci /* new chips with PMU, CCS_FORCEHT will distribute the HT clock 123862306a36Sopenharmony_ci * on backplane, but mac core will still run on ALP(not HT) when 123962306a36Sopenharmony_ci * it enters powersave mode, which means the FCA bit may not be 124062306a36Sopenharmony_ci * set. Should wakeup mac if driver wants it to run on HT. 124162306a36Sopenharmony_ci */ 124262306a36Sopenharmony_ci 124362306a36Sopenharmony_ci if (wlc_hw->clk) { 124462306a36Sopenharmony_ci if (mode == BCMA_CLKMODE_FAST) { 124562306a36Sopenharmony_ci bcma_set32(wlc_hw->d11core, 124662306a36Sopenharmony_ci D11REGOFFS(clk_ctl_st), 124762306a36Sopenharmony_ci CCS_FORCEHT); 124862306a36Sopenharmony_ci 124962306a36Sopenharmony_ci udelay(64); 125062306a36Sopenharmony_ci 125162306a36Sopenharmony_ci SPINWAIT( 125262306a36Sopenharmony_ci ((bcma_read32(wlc_hw->d11core, 125362306a36Sopenharmony_ci D11REGOFFS(clk_ctl_st)) & 125462306a36Sopenharmony_ci CCS_HTAVAIL) == 0), 125562306a36Sopenharmony_ci PMU_MAX_TRANSITION_DLY); 125662306a36Sopenharmony_ci WARN_ON(!(bcma_read32(wlc_hw->d11core, 125762306a36Sopenharmony_ci D11REGOFFS(clk_ctl_st)) & 125862306a36Sopenharmony_ci CCS_HTAVAIL)); 125962306a36Sopenharmony_ci } else { 126062306a36Sopenharmony_ci if ((ai_get_pmurev(wlc_hw->sih) == 0) && 126162306a36Sopenharmony_ci (bcma_read32(wlc_hw->d11core, 126262306a36Sopenharmony_ci D11REGOFFS(clk_ctl_st)) & 126362306a36Sopenharmony_ci (CCS_FORCEHT | CCS_HTAREQ))) 126462306a36Sopenharmony_ci SPINWAIT( 126562306a36Sopenharmony_ci ((bcma_read32(wlc_hw->d11core, 126662306a36Sopenharmony_ci offsetof(struct d11regs, 126762306a36Sopenharmony_ci clk_ctl_st)) & 126862306a36Sopenharmony_ci CCS_HTAVAIL) == 0), 126962306a36Sopenharmony_ci PMU_MAX_TRANSITION_DLY); 127062306a36Sopenharmony_ci bcma_mask32(wlc_hw->d11core, 127162306a36Sopenharmony_ci D11REGOFFS(clk_ctl_st), 127262306a36Sopenharmony_ci ~CCS_FORCEHT); 127362306a36Sopenharmony_ci } 127462306a36Sopenharmony_ci } 127562306a36Sopenharmony_ci wlc_hw->forcefastclk = (mode == BCMA_CLKMODE_FAST); 127662306a36Sopenharmony_ci } else { 127762306a36Sopenharmony_ci 127862306a36Sopenharmony_ci /* old chips w/o PMU, force HT through cc, 127962306a36Sopenharmony_ci * then use FCA to verify mac is running fast clock 128062306a36Sopenharmony_ci */ 128162306a36Sopenharmony_ci 128262306a36Sopenharmony_ci wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode); 128362306a36Sopenharmony_ci 128462306a36Sopenharmony_ci /* check fast clock is available (if core is not in reset) */ 128562306a36Sopenharmony_ci if (wlc_hw->forcefastclk && wlc_hw->clk) 128662306a36Sopenharmony_ci WARN_ON(!(bcma_aread32(wlc_hw->d11core, BCMA_IOST) & 128762306a36Sopenharmony_ci SISF_FCLKA)); 128862306a36Sopenharmony_ci 128962306a36Sopenharmony_ci /* 129062306a36Sopenharmony_ci * keep the ucode wake bit on if forcefastclk is on since we 129162306a36Sopenharmony_ci * do not want ucode to put us back to slow clock when it dozes 129262306a36Sopenharmony_ci * for PM mode. Code below matches the wake override bit with 129362306a36Sopenharmony_ci * current forcefastclk state. Only setting bit in wake_override 129462306a36Sopenharmony_ci * instead of waking ucode immediately since old code had this 129562306a36Sopenharmony_ci * behavior. Older code set wlc->forcefastclk but only had the 129662306a36Sopenharmony_ci * wake happen if the wakup_ucode work (protected by an up 129762306a36Sopenharmony_ci * check) was executed just below. 129862306a36Sopenharmony_ci */ 129962306a36Sopenharmony_ci if (wlc_hw->forcefastclk) 130062306a36Sopenharmony_ci mboolset(wlc_hw->wake_override, 130162306a36Sopenharmony_ci BRCMS_WAKE_OVERRIDE_FORCEFAST); 130262306a36Sopenharmony_ci else 130362306a36Sopenharmony_ci mboolclr(wlc_hw->wake_override, 130462306a36Sopenharmony_ci BRCMS_WAKE_OVERRIDE_FORCEFAST); 130562306a36Sopenharmony_ci } 130662306a36Sopenharmony_ci} 130762306a36Sopenharmony_ci 130862306a36Sopenharmony_ci/* set or clear ucode host flag bits 130962306a36Sopenharmony_ci * it has an optimization for no-change write 131062306a36Sopenharmony_ci * it only writes through shared memory when the core has clock; 131162306a36Sopenharmony_ci * pre-CLK changes should use wlc_write_mhf to get around the optimization 131262306a36Sopenharmony_ci * 131362306a36Sopenharmony_ci * 131462306a36Sopenharmony_ci * bands values are: BRCM_BAND_AUTO <--- Current band only 131562306a36Sopenharmony_ci * BRCM_BAND_5G <--- 5G band only 131662306a36Sopenharmony_ci * BRCM_BAND_2G <--- 2G band only 131762306a36Sopenharmony_ci * BRCM_BAND_ALL <--- All bands 131862306a36Sopenharmony_ci */ 131962306a36Sopenharmony_civoid 132062306a36Sopenharmony_cibrcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val, 132162306a36Sopenharmony_ci int bands) 132262306a36Sopenharmony_ci{ 132362306a36Sopenharmony_ci u16 save; 132462306a36Sopenharmony_ci u16 addr[MHFMAX] = { 132562306a36Sopenharmony_ci M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4, 132662306a36Sopenharmony_ci M_HOST_FLAGS5 132762306a36Sopenharmony_ci }; 132862306a36Sopenharmony_ci struct brcms_hw_band *band; 132962306a36Sopenharmony_ci 133062306a36Sopenharmony_ci if ((val & ~mask) || idx >= MHFMAX) 133162306a36Sopenharmony_ci return; /* error condition */ 133262306a36Sopenharmony_ci 133362306a36Sopenharmony_ci switch (bands) { 133462306a36Sopenharmony_ci /* Current band only or all bands, 133562306a36Sopenharmony_ci * then set the band to current band 133662306a36Sopenharmony_ci */ 133762306a36Sopenharmony_ci case BRCM_BAND_AUTO: 133862306a36Sopenharmony_ci case BRCM_BAND_ALL: 133962306a36Sopenharmony_ci band = wlc_hw->band; 134062306a36Sopenharmony_ci break; 134162306a36Sopenharmony_ci case BRCM_BAND_5G: 134262306a36Sopenharmony_ci band = wlc_hw->bandstate[BAND_5G_INDEX]; 134362306a36Sopenharmony_ci break; 134462306a36Sopenharmony_ci case BRCM_BAND_2G: 134562306a36Sopenharmony_ci band = wlc_hw->bandstate[BAND_2G_INDEX]; 134662306a36Sopenharmony_ci break; 134762306a36Sopenharmony_ci default: 134862306a36Sopenharmony_ci band = NULL; /* error condition */ 134962306a36Sopenharmony_ci } 135062306a36Sopenharmony_ci 135162306a36Sopenharmony_ci if (band) { 135262306a36Sopenharmony_ci save = band->mhfs[idx]; 135362306a36Sopenharmony_ci band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val; 135462306a36Sopenharmony_ci 135562306a36Sopenharmony_ci /* optimization: only write through if changed, and 135662306a36Sopenharmony_ci * changed band is the current band 135762306a36Sopenharmony_ci */ 135862306a36Sopenharmony_ci if (wlc_hw->clk && (band->mhfs[idx] != save) 135962306a36Sopenharmony_ci && (band == wlc_hw->band)) 136062306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, addr[idx], 136162306a36Sopenharmony_ci (u16) band->mhfs[idx]); 136262306a36Sopenharmony_ci } 136362306a36Sopenharmony_ci 136462306a36Sopenharmony_ci if (bands == BRCM_BAND_ALL) { 136562306a36Sopenharmony_ci wlc_hw->bandstate[0]->mhfs[idx] = 136662306a36Sopenharmony_ci (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val; 136762306a36Sopenharmony_ci wlc_hw->bandstate[1]->mhfs[idx] = 136862306a36Sopenharmony_ci (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val; 136962306a36Sopenharmony_ci } 137062306a36Sopenharmony_ci} 137162306a36Sopenharmony_ci 137262306a36Sopenharmony_ci/* set the maccontrol register to desired reset state and 137362306a36Sopenharmony_ci * initialize the sw cache of the register 137462306a36Sopenharmony_ci */ 137562306a36Sopenharmony_cistatic void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw) 137662306a36Sopenharmony_ci{ 137762306a36Sopenharmony_ci /* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */ 137862306a36Sopenharmony_ci wlc_hw->maccontrol = 0; 137962306a36Sopenharmony_ci wlc_hw->suspended_fifos = 0; 138062306a36Sopenharmony_ci wlc_hw->wake_override = 0; 138162306a36Sopenharmony_ci wlc_hw->mute_override = 0; 138262306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE); 138362306a36Sopenharmony_ci} 138462306a36Sopenharmony_ci 138562306a36Sopenharmony_ci/* 138662306a36Sopenharmony_ci * write the software state of maccontrol and 138762306a36Sopenharmony_ci * overrides to the maccontrol register 138862306a36Sopenharmony_ci */ 138962306a36Sopenharmony_cistatic void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw) 139062306a36Sopenharmony_ci{ 139162306a36Sopenharmony_ci u32 maccontrol = wlc_hw->maccontrol; 139262306a36Sopenharmony_ci 139362306a36Sopenharmony_ci /* OR in the wake bit if overridden */ 139462306a36Sopenharmony_ci if (wlc_hw->wake_override) 139562306a36Sopenharmony_ci maccontrol |= MCTL_WAKE; 139662306a36Sopenharmony_ci 139762306a36Sopenharmony_ci /* set AP and INFRA bits for mute if needed */ 139862306a36Sopenharmony_ci if (wlc_hw->mute_override) { 139962306a36Sopenharmony_ci maccontrol &= ~(MCTL_AP); 140062306a36Sopenharmony_ci maccontrol |= MCTL_INFRA; 140162306a36Sopenharmony_ci } 140262306a36Sopenharmony_ci 140362306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(maccontrol), 140462306a36Sopenharmony_ci maccontrol); 140562306a36Sopenharmony_ci} 140662306a36Sopenharmony_ci 140762306a36Sopenharmony_ci/* set or clear maccontrol bits */ 140862306a36Sopenharmony_civoid brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val) 140962306a36Sopenharmony_ci{ 141062306a36Sopenharmony_ci u32 maccontrol; 141162306a36Sopenharmony_ci u32 new_maccontrol; 141262306a36Sopenharmony_ci 141362306a36Sopenharmony_ci if (val & ~mask) 141462306a36Sopenharmony_ci return; /* error condition */ 141562306a36Sopenharmony_ci maccontrol = wlc_hw->maccontrol; 141662306a36Sopenharmony_ci new_maccontrol = (maccontrol & ~mask) | val; 141762306a36Sopenharmony_ci 141862306a36Sopenharmony_ci /* if the new maccontrol value is the same as the old, nothing to do */ 141962306a36Sopenharmony_ci if (new_maccontrol == maccontrol) 142062306a36Sopenharmony_ci return; 142162306a36Sopenharmony_ci 142262306a36Sopenharmony_ci /* something changed, cache the new value */ 142362306a36Sopenharmony_ci wlc_hw->maccontrol = new_maccontrol; 142462306a36Sopenharmony_ci 142562306a36Sopenharmony_ci /* write the new values with overrides applied */ 142662306a36Sopenharmony_ci brcms_c_mctrl_write(wlc_hw); 142762306a36Sopenharmony_ci} 142862306a36Sopenharmony_ci 142962306a36Sopenharmony_civoid brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw, 143062306a36Sopenharmony_ci u32 override_bit) 143162306a36Sopenharmony_ci{ 143262306a36Sopenharmony_ci if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) { 143362306a36Sopenharmony_ci mboolset(wlc_hw->wake_override, override_bit); 143462306a36Sopenharmony_ci return; 143562306a36Sopenharmony_ci } 143662306a36Sopenharmony_ci 143762306a36Sopenharmony_ci mboolset(wlc_hw->wake_override, override_bit); 143862306a36Sopenharmony_ci 143962306a36Sopenharmony_ci brcms_c_mctrl_write(wlc_hw); 144062306a36Sopenharmony_ci brcms_b_wait_for_wake(wlc_hw); 144162306a36Sopenharmony_ci} 144262306a36Sopenharmony_ci 144362306a36Sopenharmony_civoid brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw, 144462306a36Sopenharmony_ci u32 override_bit) 144562306a36Sopenharmony_ci{ 144662306a36Sopenharmony_ci mboolclr(wlc_hw->wake_override, override_bit); 144762306a36Sopenharmony_ci 144862306a36Sopenharmony_ci if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) 144962306a36Sopenharmony_ci return; 145062306a36Sopenharmony_ci 145162306a36Sopenharmony_ci brcms_c_mctrl_write(wlc_hw); 145262306a36Sopenharmony_ci} 145362306a36Sopenharmony_ci 145462306a36Sopenharmony_ci/* When driver needs ucode to stop beaconing, it has to make sure that 145562306a36Sopenharmony_ci * MCTL_AP is clear and MCTL_INFRA is set 145662306a36Sopenharmony_ci * Mode MCTL_AP MCTL_INFRA 145762306a36Sopenharmony_ci * AP 1 1 145862306a36Sopenharmony_ci * STA 0 1 <--- This will ensure no beacons 145962306a36Sopenharmony_ci * IBSS 0 0 146062306a36Sopenharmony_ci */ 146162306a36Sopenharmony_cistatic void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw) 146262306a36Sopenharmony_ci{ 146362306a36Sopenharmony_ci wlc_hw->mute_override = 1; 146462306a36Sopenharmony_ci 146562306a36Sopenharmony_ci /* if maccontrol already has AP == 0 and INFRA == 1 without this 146662306a36Sopenharmony_ci * override, then there is no change to write 146762306a36Sopenharmony_ci */ 146862306a36Sopenharmony_ci if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) 146962306a36Sopenharmony_ci return; 147062306a36Sopenharmony_ci 147162306a36Sopenharmony_ci brcms_c_mctrl_write(wlc_hw); 147262306a36Sopenharmony_ci} 147362306a36Sopenharmony_ci 147462306a36Sopenharmony_ci/* Clear the override on AP and INFRA bits */ 147562306a36Sopenharmony_cistatic void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw) 147662306a36Sopenharmony_ci{ 147762306a36Sopenharmony_ci if (wlc_hw->mute_override == 0) 147862306a36Sopenharmony_ci return; 147962306a36Sopenharmony_ci 148062306a36Sopenharmony_ci wlc_hw->mute_override = 0; 148162306a36Sopenharmony_ci 148262306a36Sopenharmony_ci /* if maccontrol already has AP == 0 and INFRA == 1 without this 148362306a36Sopenharmony_ci * override, then there is no change to write 148462306a36Sopenharmony_ci */ 148562306a36Sopenharmony_ci if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA) 148662306a36Sopenharmony_ci return; 148762306a36Sopenharmony_ci 148862306a36Sopenharmony_ci brcms_c_mctrl_write(wlc_hw); 148962306a36Sopenharmony_ci} 149062306a36Sopenharmony_ci 149162306a36Sopenharmony_ci/* 149262306a36Sopenharmony_ci * Write a MAC address to the given match reg offset in the RXE match engine. 149362306a36Sopenharmony_ci */ 149462306a36Sopenharmony_cistatic void 149562306a36Sopenharmony_cibrcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset, 149662306a36Sopenharmony_ci const u8 *addr) 149762306a36Sopenharmony_ci{ 149862306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 149962306a36Sopenharmony_ci u16 mac_l; 150062306a36Sopenharmony_ci u16 mac_m; 150162306a36Sopenharmony_ci u16 mac_h; 150262306a36Sopenharmony_ci 150362306a36Sopenharmony_ci brcms_dbg_rx(core, "wl%d: brcms_b_set_addrmatch\n", wlc_hw->unit); 150462306a36Sopenharmony_ci 150562306a36Sopenharmony_ci mac_l = addr[0] | (addr[1] << 8); 150662306a36Sopenharmony_ci mac_m = addr[2] | (addr[3] << 8); 150762306a36Sopenharmony_ci mac_h = addr[4] | (addr[5] << 8); 150862306a36Sopenharmony_ci 150962306a36Sopenharmony_ci /* enter the MAC addr into the RXE match registers */ 151062306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(rcm_ctl), 151162306a36Sopenharmony_ci RCM_INC_DATA | match_reg_offset); 151262306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_l); 151362306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_m); 151462306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(rcm_mat_data), mac_h); 151562306a36Sopenharmony_ci} 151662306a36Sopenharmony_ci 151762306a36Sopenharmony_civoid 151862306a36Sopenharmony_cibrcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len, 151962306a36Sopenharmony_ci void *buf) 152062306a36Sopenharmony_ci{ 152162306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 152262306a36Sopenharmony_ci u32 word; 152362306a36Sopenharmony_ci __le32 word_le; 152462306a36Sopenharmony_ci __be32 word_be; 152562306a36Sopenharmony_ci bool be_bit; 152662306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); 152762306a36Sopenharmony_ci 152862306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tplatewrptr), offset); 152962306a36Sopenharmony_ci 153062306a36Sopenharmony_ci /* if MCTL_BIGEND bit set in mac control register, 153162306a36Sopenharmony_ci * the chip swaps data in fifo, as well as data in 153262306a36Sopenharmony_ci * template ram 153362306a36Sopenharmony_ci */ 153462306a36Sopenharmony_ci be_bit = (bcma_read32(core, D11REGOFFS(maccontrol)) & MCTL_BIGEND) != 0; 153562306a36Sopenharmony_ci 153662306a36Sopenharmony_ci while (len > 0) { 153762306a36Sopenharmony_ci memcpy(&word, buf, sizeof(u32)); 153862306a36Sopenharmony_ci 153962306a36Sopenharmony_ci if (be_bit) { 154062306a36Sopenharmony_ci word_be = cpu_to_be32(word); 154162306a36Sopenharmony_ci word = *(u32 *)&word_be; 154262306a36Sopenharmony_ci } else { 154362306a36Sopenharmony_ci word_le = cpu_to_le32(word); 154462306a36Sopenharmony_ci word = *(u32 *)&word_le; 154562306a36Sopenharmony_ci } 154662306a36Sopenharmony_ci 154762306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tplatewrdata), word); 154862306a36Sopenharmony_ci 154962306a36Sopenharmony_ci buf = (u8 *) buf + sizeof(u32); 155062306a36Sopenharmony_ci len -= sizeof(u32); 155162306a36Sopenharmony_ci } 155262306a36Sopenharmony_ci} 155362306a36Sopenharmony_ci 155462306a36Sopenharmony_cistatic void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin) 155562306a36Sopenharmony_ci{ 155662306a36Sopenharmony_ci wlc_hw->band->CWmin = newmin; 155762306a36Sopenharmony_ci 155862306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), 155962306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_CWMIN); 156062306a36Sopenharmony_ci (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); 156162306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmin); 156262306a36Sopenharmony_ci} 156362306a36Sopenharmony_ci 156462306a36Sopenharmony_cistatic void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax) 156562306a36Sopenharmony_ci{ 156662306a36Sopenharmony_ci wlc_hw->band->CWmax = newmax; 156762306a36Sopenharmony_ci 156862306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), 156962306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_CWMAX); 157062306a36Sopenharmony_ci (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); 157162306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), newmax); 157262306a36Sopenharmony_ci} 157362306a36Sopenharmony_ci 157462306a36Sopenharmony_civoid brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw) 157562306a36Sopenharmony_ci{ 157662306a36Sopenharmony_ci bool fastclk; 157762306a36Sopenharmony_ci 157862306a36Sopenharmony_ci /* request FAST clock if not on */ 157962306a36Sopenharmony_ci fastclk = wlc_hw->forcefastclk; 158062306a36Sopenharmony_ci if (!fastclk) 158162306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 158262306a36Sopenharmony_ci 158362306a36Sopenharmony_ci wlc_phy_bw_state_set(wlc_hw->band->pi, bw); 158462306a36Sopenharmony_ci 158562306a36Sopenharmony_ci brcms_b_phy_reset(wlc_hw); 158662306a36Sopenharmony_ci wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi)); 158762306a36Sopenharmony_ci 158862306a36Sopenharmony_ci /* restore the clk */ 158962306a36Sopenharmony_ci if (!fastclk) 159062306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); 159162306a36Sopenharmony_ci} 159262306a36Sopenharmony_ci 159362306a36Sopenharmony_cistatic void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw) 159462306a36Sopenharmony_ci{ 159562306a36Sopenharmony_ci u16 v; 159662306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_hw->wlc; 159762306a36Sopenharmony_ci /* update SYNTHPU_DLY */ 159862306a36Sopenharmony_ci 159962306a36Sopenharmony_ci if (BRCMS_ISLCNPHY(wlc->band)) 160062306a36Sopenharmony_ci v = SYNTHPU_DLY_LPPHY_US; 160162306a36Sopenharmony_ci else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) 160262306a36Sopenharmony_ci v = SYNTHPU_DLY_NPHY_US; 160362306a36Sopenharmony_ci else 160462306a36Sopenharmony_ci v = SYNTHPU_DLY_BPHY_US; 160562306a36Sopenharmony_ci 160662306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v); 160762306a36Sopenharmony_ci} 160862306a36Sopenharmony_ci 160962306a36Sopenharmony_cistatic void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw) 161062306a36Sopenharmony_ci{ 161162306a36Sopenharmony_ci u16 phyctl; 161262306a36Sopenharmony_ci u16 phytxant = wlc_hw->bmac_phytxant; 161362306a36Sopenharmony_ci u16 mask = PHY_TXC_ANT_MASK; 161462306a36Sopenharmony_ci 161562306a36Sopenharmony_ci /* set the Probe Response frame phy control word */ 161662306a36Sopenharmony_ci phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS); 161762306a36Sopenharmony_ci phyctl = (phyctl & ~mask) | phytxant; 161862306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl); 161962306a36Sopenharmony_ci 162062306a36Sopenharmony_ci /* set the Response (ACK/CTS) frame phy control word */ 162162306a36Sopenharmony_ci phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD); 162262306a36Sopenharmony_ci phyctl = (phyctl & ~mask) | phytxant; 162362306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl); 162462306a36Sopenharmony_ci} 162562306a36Sopenharmony_ci 162662306a36Sopenharmony_cistatic u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw, 162762306a36Sopenharmony_ci u8 rate) 162862306a36Sopenharmony_ci{ 162962306a36Sopenharmony_ci uint i; 163062306a36Sopenharmony_ci u8 plcp_rate = 0; 163162306a36Sopenharmony_ci struct plcp_signal_rate_lookup { 163262306a36Sopenharmony_ci u8 rate; 163362306a36Sopenharmony_ci u8 signal_rate; 163462306a36Sopenharmony_ci }; 163562306a36Sopenharmony_ci /* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */ 163662306a36Sopenharmony_ci const struct plcp_signal_rate_lookup rate_lookup[] = { 163762306a36Sopenharmony_ci {BRCM_RATE_6M, 0xB}, 163862306a36Sopenharmony_ci {BRCM_RATE_9M, 0xF}, 163962306a36Sopenharmony_ci {BRCM_RATE_12M, 0xA}, 164062306a36Sopenharmony_ci {BRCM_RATE_18M, 0xE}, 164162306a36Sopenharmony_ci {BRCM_RATE_24M, 0x9}, 164262306a36Sopenharmony_ci {BRCM_RATE_36M, 0xD}, 164362306a36Sopenharmony_ci {BRCM_RATE_48M, 0x8}, 164462306a36Sopenharmony_ci {BRCM_RATE_54M, 0xC} 164562306a36Sopenharmony_ci }; 164662306a36Sopenharmony_ci 164762306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) { 164862306a36Sopenharmony_ci if (rate == rate_lookup[i].rate) { 164962306a36Sopenharmony_ci plcp_rate = rate_lookup[i].signal_rate; 165062306a36Sopenharmony_ci break; 165162306a36Sopenharmony_ci } 165262306a36Sopenharmony_ci } 165362306a36Sopenharmony_ci 165462306a36Sopenharmony_ci /* Find the SHM pointer to the rate table entry by looking in the 165562306a36Sopenharmony_ci * Direct-map Table 165662306a36Sopenharmony_ci */ 165762306a36Sopenharmony_ci return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2)); 165862306a36Sopenharmony_ci} 165962306a36Sopenharmony_ci 166062306a36Sopenharmony_cistatic void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw) 166162306a36Sopenharmony_ci{ 166262306a36Sopenharmony_ci u8 rate; 166362306a36Sopenharmony_ci u8 rates[8] = { 166462306a36Sopenharmony_ci BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M, 166562306a36Sopenharmony_ci BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M 166662306a36Sopenharmony_ci }; 166762306a36Sopenharmony_ci u16 entry_ptr; 166862306a36Sopenharmony_ci u16 pctl1; 166962306a36Sopenharmony_ci uint i; 167062306a36Sopenharmony_ci 167162306a36Sopenharmony_ci if (!BRCMS_PHY_11N_CAP(wlc_hw->band)) 167262306a36Sopenharmony_ci return; 167362306a36Sopenharmony_ci 167462306a36Sopenharmony_ci /* walk the phy rate table and update the entries */ 167562306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(rates); i++) { 167662306a36Sopenharmony_ci rate = rates[i]; 167762306a36Sopenharmony_ci 167862306a36Sopenharmony_ci entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate); 167962306a36Sopenharmony_ci 168062306a36Sopenharmony_ci /* read the SHM Rate Table entry OFDM PCTL1 values */ 168162306a36Sopenharmony_ci pctl1 = 168262306a36Sopenharmony_ci brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS); 168362306a36Sopenharmony_ci 168462306a36Sopenharmony_ci /* modify the value */ 168562306a36Sopenharmony_ci pctl1 &= ~PHY_TXC1_MODE_MASK; 168662306a36Sopenharmony_ci pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT); 168762306a36Sopenharmony_ci 168862306a36Sopenharmony_ci /* Update the SHM Rate Table entry OFDM PCTL1 values */ 168962306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS, 169062306a36Sopenharmony_ci pctl1); 169162306a36Sopenharmony_ci } 169262306a36Sopenharmony_ci} 169362306a36Sopenharmony_ci 169462306a36Sopenharmony_ci/* band-specific init */ 169562306a36Sopenharmony_cistatic void brcms_b_bsinit(struct brcms_c_info *wlc, u16 chanspec) 169662306a36Sopenharmony_ci{ 169762306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 169862306a36Sopenharmony_ci 169962306a36Sopenharmony_ci brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: bandunit %d\n", wlc_hw->unit, 170062306a36Sopenharmony_ci wlc_hw->band->bandunit); 170162306a36Sopenharmony_ci 170262306a36Sopenharmony_ci brcms_c_ucode_bsinit(wlc_hw); 170362306a36Sopenharmony_ci 170462306a36Sopenharmony_ci wlc_phy_init(wlc_hw->band->pi, chanspec); 170562306a36Sopenharmony_ci 170662306a36Sopenharmony_ci brcms_c_ucode_txant_set(wlc_hw); 170762306a36Sopenharmony_ci 170862306a36Sopenharmony_ci /* 170962306a36Sopenharmony_ci * cwmin is band-specific, update hardware 171062306a36Sopenharmony_ci * with value for current band 171162306a36Sopenharmony_ci */ 171262306a36Sopenharmony_ci brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin); 171362306a36Sopenharmony_ci brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax); 171462306a36Sopenharmony_ci 171562306a36Sopenharmony_ci brcms_b_update_slot_timing(wlc_hw, 171662306a36Sopenharmony_ci wlc_hw->band->bandtype == BRCM_BAND_5G ? 171762306a36Sopenharmony_ci true : wlc_hw->shortslot); 171862306a36Sopenharmony_ci 171962306a36Sopenharmony_ci /* write phytype and phyvers */ 172062306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype); 172162306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev); 172262306a36Sopenharmony_ci 172362306a36Sopenharmony_ci /* 172462306a36Sopenharmony_ci * initialize the txphyctl1 rate table since 172562306a36Sopenharmony_ci * shmem is shared between bands 172662306a36Sopenharmony_ci */ 172762306a36Sopenharmony_ci brcms_upd_ofdm_pctl1_table(wlc_hw); 172862306a36Sopenharmony_ci 172962306a36Sopenharmony_ci brcms_b_upd_synthpu(wlc_hw); 173062306a36Sopenharmony_ci} 173162306a36Sopenharmony_ci 173262306a36Sopenharmony_ci/* Perform a soft reset of the PHY PLL */ 173362306a36Sopenharmony_civoid brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw) 173462306a36Sopenharmony_ci{ 173562306a36Sopenharmony_ci ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_addr), 173662306a36Sopenharmony_ci ~0, 0); 173762306a36Sopenharmony_ci udelay(1); 173862306a36Sopenharmony_ci ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 173962306a36Sopenharmony_ci 0x4, 0); 174062306a36Sopenharmony_ci udelay(1); 174162306a36Sopenharmony_ci ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 174262306a36Sopenharmony_ci 0x4, 4); 174362306a36Sopenharmony_ci udelay(1); 174462306a36Sopenharmony_ci ai_cc_reg(wlc_hw->sih, offsetof(struct chipcregs, chipcontrol_data), 174562306a36Sopenharmony_ci 0x4, 0); 174662306a36Sopenharmony_ci udelay(1); 174762306a36Sopenharmony_ci} 174862306a36Sopenharmony_ci 174962306a36Sopenharmony_ci/* light way to turn on phy clock without reset for NPHY only 175062306a36Sopenharmony_ci * refer to brcms_b_core_phy_clk for full version 175162306a36Sopenharmony_ci */ 175262306a36Sopenharmony_civoid brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk) 175362306a36Sopenharmony_ci{ 175462306a36Sopenharmony_ci /* support(necessary for NPHY and HYPHY) only */ 175562306a36Sopenharmony_ci if (!BRCMS_ISNPHY(wlc_hw->band)) 175662306a36Sopenharmony_ci return; 175762306a36Sopenharmony_ci 175862306a36Sopenharmony_ci if (ON == clk) 175962306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_FGC, SICF_FGC); 176062306a36Sopenharmony_ci else 176162306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_FGC, 0); 176262306a36Sopenharmony_ci 176362306a36Sopenharmony_ci} 176462306a36Sopenharmony_ci 176562306a36Sopenharmony_civoid brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk) 176662306a36Sopenharmony_ci{ 176762306a36Sopenharmony_ci if (ON == clk) 176862306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, SICF_MPCLKE); 176962306a36Sopenharmony_ci else 177062306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_MPCLKE, 0); 177162306a36Sopenharmony_ci} 177262306a36Sopenharmony_ci 177362306a36Sopenharmony_civoid brcms_b_phy_reset(struct brcms_hardware *wlc_hw) 177462306a36Sopenharmony_ci{ 177562306a36Sopenharmony_ci struct brcms_phy_pub *pih = wlc_hw->band->pi; 177662306a36Sopenharmony_ci u32 phy_bw_clkbits; 177762306a36Sopenharmony_ci 177862306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d: reset phy\n", wlc_hw->unit); 177962306a36Sopenharmony_ci 178062306a36Sopenharmony_ci if (pih == NULL) 178162306a36Sopenharmony_ci return; 178262306a36Sopenharmony_ci 178362306a36Sopenharmony_ci phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi); 178462306a36Sopenharmony_ci 178562306a36Sopenharmony_ci /* Specific reset sequence required for NPHY rev 3 and 4 */ 178662306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) && 178762306a36Sopenharmony_ci NREV_LE(wlc_hw->band->phyrev, 4)) { 178862306a36Sopenharmony_ci /* Set the PHY bandwidth */ 178962306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, SICF_BWMASK, phy_bw_clkbits); 179062306a36Sopenharmony_ci 179162306a36Sopenharmony_ci udelay(1); 179262306a36Sopenharmony_ci 179362306a36Sopenharmony_ci /* Perform a soft reset of the PHY PLL */ 179462306a36Sopenharmony_ci brcms_b_core_phypll_reset(wlc_hw); 179562306a36Sopenharmony_ci 179662306a36Sopenharmony_ci /* reset the PHY */ 179762306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, (SICF_PRST | SICF_PCLKE), 179862306a36Sopenharmony_ci (SICF_PRST | SICF_PCLKE)); 179962306a36Sopenharmony_ci } else { 180062306a36Sopenharmony_ci brcms_b_core_ioctl(wlc_hw, 180162306a36Sopenharmony_ci (SICF_PRST | SICF_PCLKE | SICF_BWMASK), 180262306a36Sopenharmony_ci (SICF_PRST | SICF_PCLKE | phy_bw_clkbits)); 180362306a36Sopenharmony_ci } 180462306a36Sopenharmony_ci 180562306a36Sopenharmony_ci udelay(2); 180662306a36Sopenharmony_ci brcms_b_core_phy_clk(wlc_hw, ON); 180762306a36Sopenharmony_ci 180862306a36Sopenharmony_ci wlc_phy_anacore(pih, ON); 180962306a36Sopenharmony_ci} 181062306a36Sopenharmony_ci 181162306a36Sopenharmony_ci/* switch to and initialize new band */ 181262306a36Sopenharmony_cistatic void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit, 181362306a36Sopenharmony_ci u16 chanspec) { 181462306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_hw->wlc; 181562306a36Sopenharmony_ci u32 macintmask; 181662306a36Sopenharmony_ci 181762306a36Sopenharmony_ci /* Enable the d11 core before accessing it */ 181862306a36Sopenharmony_ci if (!bcma_core_is_enabled(wlc_hw->d11core)) { 181962306a36Sopenharmony_ci bcma_core_enable(wlc_hw->d11core, 0); 182062306a36Sopenharmony_ci brcms_c_mctrl_reset(wlc_hw); 182162306a36Sopenharmony_ci } 182262306a36Sopenharmony_ci 182362306a36Sopenharmony_ci macintmask = brcms_c_setband_inact(wlc, bandunit); 182462306a36Sopenharmony_ci 182562306a36Sopenharmony_ci if (!wlc_hw->up) 182662306a36Sopenharmony_ci return; 182762306a36Sopenharmony_ci 182862306a36Sopenharmony_ci brcms_b_core_phy_clk(wlc_hw, ON); 182962306a36Sopenharmony_ci 183062306a36Sopenharmony_ci /* band-specific initializations */ 183162306a36Sopenharmony_ci brcms_b_bsinit(wlc, chanspec); 183262306a36Sopenharmony_ci 183362306a36Sopenharmony_ci /* 183462306a36Sopenharmony_ci * If there are any pending software interrupt bits, 183562306a36Sopenharmony_ci * then replace these with a harmless nonzero value 183662306a36Sopenharmony_ci * so brcms_c_dpc() will re-enable interrupts when done. 183762306a36Sopenharmony_ci */ 183862306a36Sopenharmony_ci if (wlc->macintstatus) 183962306a36Sopenharmony_ci wlc->macintstatus = MI_DMAINT; 184062306a36Sopenharmony_ci 184162306a36Sopenharmony_ci /* restore macintmask */ 184262306a36Sopenharmony_ci brcms_intrsrestore(wlc->wl, macintmask); 184362306a36Sopenharmony_ci 184462306a36Sopenharmony_ci /* ucode should still be suspended.. */ 184562306a36Sopenharmony_ci WARN_ON((bcma_read32(wlc_hw->d11core, D11REGOFFS(maccontrol)) & 184662306a36Sopenharmony_ci MCTL_EN_MAC) != 0); 184762306a36Sopenharmony_ci} 184862306a36Sopenharmony_ci 184962306a36Sopenharmony_cistatic bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw) 185062306a36Sopenharmony_ci{ 185162306a36Sopenharmony_ci 185262306a36Sopenharmony_ci /* reject unsupported corerev */ 185362306a36Sopenharmony_ci if (!CONF_HAS(D11CONF, wlc_hw->corerev)) { 185462306a36Sopenharmony_ci wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n", 185562306a36Sopenharmony_ci wlc_hw->corerev); 185662306a36Sopenharmony_ci return false; 185762306a36Sopenharmony_ci } 185862306a36Sopenharmony_ci 185962306a36Sopenharmony_ci return true; 186062306a36Sopenharmony_ci} 186162306a36Sopenharmony_ci 186262306a36Sopenharmony_ci/* Validate some board info parameters */ 186362306a36Sopenharmony_cistatic bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw) 186462306a36Sopenharmony_ci{ 186562306a36Sopenharmony_ci uint boardrev = wlc_hw->boardrev; 186662306a36Sopenharmony_ci 186762306a36Sopenharmony_ci /* 4 bits each for board type, major, minor, and tiny version */ 186862306a36Sopenharmony_ci uint brt = (boardrev & 0xf000) >> 12; 186962306a36Sopenharmony_ci uint b0 = (boardrev & 0xf00) >> 8; 187062306a36Sopenharmony_ci uint b1 = (boardrev & 0xf0) >> 4; 187162306a36Sopenharmony_ci uint b2 = boardrev & 0xf; 187262306a36Sopenharmony_ci 187362306a36Sopenharmony_ci /* voards from other vendors are always considered valid */ 187462306a36Sopenharmony_ci if (ai_get_boardvendor(wlc_hw->sih) != PCI_VENDOR_ID_BROADCOM) 187562306a36Sopenharmony_ci return true; 187662306a36Sopenharmony_ci 187762306a36Sopenharmony_ci /* do some boardrev sanity checks when boardvendor is Broadcom */ 187862306a36Sopenharmony_ci if (boardrev == 0) 187962306a36Sopenharmony_ci return false; 188062306a36Sopenharmony_ci 188162306a36Sopenharmony_ci if (boardrev <= 0xff) 188262306a36Sopenharmony_ci return true; 188362306a36Sopenharmony_ci 188462306a36Sopenharmony_ci if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9) 188562306a36Sopenharmony_ci || (b2 > 9)) 188662306a36Sopenharmony_ci return false; 188762306a36Sopenharmony_ci 188862306a36Sopenharmony_ci return true; 188962306a36Sopenharmony_ci} 189062306a36Sopenharmony_ci 189162306a36Sopenharmony_cistatic void brcms_c_get_macaddr(struct brcms_hardware *wlc_hw, u8 etheraddr[ETH_ALEN]) 189262306a36Sopenharmony_ci{ 189362306a36Sopenharmony_ci struct ssb_sprom *sprom = &wlc_hw->d11core->bus->sprom; 189462306a36Sopenharmony_ci 189562306a36Sopenharmony_ci /* If macaddr exists, use it (Sromrev4, CIS, ...). */ 189662306a36Sopenharmony_ci if (!is_zero_ether_addr(sprom->il0mac)) { 189762306a36Sopenharmony_ci memcpy(etheraddr, sprom->il0mac, ETH_ALEN); 189862306a36Sopenharmony_ci return; 189962306a36Sopenharmony_ci } 190062306a36Sopenharmony_ci 190162306a36Sopenharmony_ci if (wlc_hw->_nbands > 1) 190262306a36Sopenharmony_ci memcpy(etheraddr, sprom->et1mac, ETH_ALEN); 190362306a36Sopenharmony_ci else 190462306a36Sopenharmony_ci memcpy(etheraddr, sprom->il0mac, ETH_ALEN); 190562306a36Sopenharmony_ci} 190662306a36Sopenharmony_ci 190762306a36Sopenharmony_ci/* power both the pll and external oscillator on/off */ 190862306a36Sopenharmony_cistatic void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want) 190962306a36Sopenharmony_ci{ 191062306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d: want %d\n", wlc_hw->unit, want); 191162306a36Sopenharmony_ci 191262306a36Sopenharmony_ci /* 191362306a36Sopenharmony_ci * dont power down if plldown is false or 191462306a36Sopenharmony_ci * we must poll hw radio disable 191562306a36Sopenharmony_ci */ 191662306a36Sopenharmony_ci if (!want && wlc_hw->pllreq) 191762306a36Sopenharmony_ci return; 191862306a36Sopenharmony_ci 191962306a36Sopenharmony_ci wlc_hw->sbclk = want; 192062306a36Sopenharmony_ci if (!wlc_hw->sbclk) { 192162306a36Sopenharmony_ci wlc_hw->clk = false; 192262306a36Sopenharmony_ci if (wlc_hw->band && wlc_hw->band->pi) 192362306a36Sopenharmony_ci wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); 192462306a36Sopenharmony_ci } 192562306a36Sopenharmony_ci} 192662306a36Sopenharmony_ci 192762306a36Sopenharmony_ci/* 192862306a36Sopenharmony_ci * Return true if radio is disabled, otherwise false. 192962306a36Sopenharmony_ci * hw radio disable signal is an external pin, users activate it asynchronously 193062306a36Sopenharmony_ci * this function could be called when driver is down and w/o clock 193162306a36Sopenharmony_ci * it operates on different registers depending on corerev and boardflag. 193262306a36Sopenharmony_ci */ 193362306a36Sopenharmony_cistatic bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw) 193462306a36Sopenharmony_ci{ 193562306a36Sopenharmony_ci bool v, clk, xtal; 193662306a36Sopenharmony_ci u32 flags = 0; 193762306a36Sopenharmony_ci 193862306a36Sopenharmony_ci xtal = wlc_hw->sbclk; 193962306a36Sopenharmony_ci if (!xtal) 194062306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, ON); 194162306a36Sopenharmony_ci 194262306a36Sopenharmony_ci /* may need to take core out of reset first */ 194362306a36Sopenharmony_ci clk = wlc_hw->clk; 194462306a36Sopenharmony_ci if (!clk) { 194562306a36Sopenharmony_ci /* 194662306a36Sopenharmony_ci * mac no longer enables phyclk automatically when driver 194762306a36Sopenharmony_ci * accesses phyreg throughput mac. This can be skipped since 194862306a36Sopenharmony_ci * only mac reg is accessed below 194962306a36Sopenharmony_ci */ 195062306a36Sopenharmony_ci if (D11REV_GE(wlc_hw->corerev, 18)) 195162306a36Sopenharmony_ci flags |= SICF_PCLKE; 195262306a36Sopenharmony_ci 195362306a36Sopenharmony_ci /* 195462306a36Sopenharmony_ci * TODO: test suspend/resume 195562306a36Sopenharmony_ci * 195662306a36Sopenharmony_ci * AI chip doesn't restore bar0win2 on 195762306a36Sopenharmony_ci * hibernation/resume, need sw fixup 195862306a36Sopenharmony_ci */ 195962306a36Sopenharmony_ci 196062306a36Sopenharmony_ci bcma_core_enable(wlc_hw->d11core, flags); 196162306a36Sopenharmony_ci brcms_c_mctrl_reset(wlc_hw); 196262306a36Sopenharmony_ci } 196362306a36Sopenharmony_ci 196462306a36Sopenharmony_ci v = ((bcma_read32(wlc_hw->d11core, 196562306a36Sopenharmony_ci D11REGOFFS(phydebug)) & PDBG_RFD) != 0); 196662306a36Sopenharmony_ci 196762306a36Sopenharmony_ci /* put core back into reset */ 196862306a36Sopenharmony_ci if (!clk) 196962306a36Sopenharmony_ci bcma_core_disable(wlc_hw->d11core, 0); 197062306a36Sopenharmony_ci 197162306a36Sopenharmony_ci if (!xtal) 197262306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, OFF); 197362306a36Sopenharmony_ci 197462306a36Sopenharmony_ci return v; 197562306a36Sopenharmony_ci} 197662306a36Sopenharmony_ci 197762306a36Sopenharmony_cistatic bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo) 197862306a36Sopenharmony_ci{ 197962306a36Sopenharmony_ci struct dma_pub *di = wlc_hw->di[fifo]; 198062306a36Sopenharmony_ci return dma_rxreset(di); 198162306a36Sopenharmony_ci} 198262306a36Sopenharmony_ci 198362306a36Sopenharmony_ci/* d11 core reset 198462306a36Sopenharmony_ci * ensure fask clock during reset 198562306a36Sopenharmony_ci * reset dma 198662306a36Sopenharmony_ci * reset d11(out of reset) 198762306a36Sopenharmony_ci * reset phy(out of reset) 198862306a36Sopenharmony_ci * clear software macintstatus for fresh new start 198962306a36Sopenharmony_ci * one testing hack wlc_hw->noreset will bypass the d11/phy reset 199062306a36Sopenharmony_ci */ 199162306a36Sopenharmony_civoid brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags) 199262306a36Sopenharmony_ci{ 199362306a36Sopenharmony_ci uint i; 199462306a36Sopenharmony_ci bool fastclk; 199562306a36Sopenharmony_ci 199662306a36Sopenharmony_ci if (flags == BRCMS_USE_COREFLAGS) 199762306a36Sopenharmony_ci flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0); 199862306a36Sopenharmony_ci 199962306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d: core reset\n", wlc_hw->unit); 200062306a36Sopenharmony_ci 200162306a36Sopenharmony_ci /* request FAST clock if not on */ 200262306a36Sopenharmony_ci fastclk = wlc_hw->forcefastclk; 200362306a36Sopenharmony_ci if (!fastclk) 200462306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 200562306a36Sopenharmony_ci 200662306a36Sopenharmony_ci /* reset the dma engines except first time thru */ 200762306a36Sopenharmony_ci if (bcma_core_is_enabled(wlc_hw->d11core)) { 200862306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) 200962306a36Sopenharmony_ci if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) 201062306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, "wl%d: %s: " 201162306a36Sopenharmony_ci "dma_txreset[%d]: cannot stop dma\n", 201262306a36Sopenharmony_ci wlc_hw->unit, __func__, i); 201362306a36Sopenharmony_ci 201462306a36Sopenharmony_ci if ((wlc_hw->di[RX_FIFO]) 201562306a36Sopenharmony_ci && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) 201662306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, "wl%d: %s: dma_rxreset" 201762306a36Sopenharmony_ci "[%d]: cannot stop dma\n", 201862306a36Sopenharmony_ci wlc_hw->unit, __func__, RX_FIFO); 201962306a36Sopenharmony_ci } 202062306a36Sopenharmony_ci /* if noreset, just stop the psm and return */ 202162306a36Sopenharmony_ci if (wlc_hw->noreset) { 202262306a36Sopenharmony_ci wlc_hw->wlc->macintstatus = 0; /* skip wl_dpc after down */ 202362306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0); 202462306a36Sopenharmony_ci return; 202562306a36Sopenharmony_ci } 202662306a36Sopenharmony_ci 202762306a36Sopenharmony_ci /* 202862306a36Sopenharmony_ci * mac no longer enables phyclk automatically when driver accesses 202962306a36Sopenharmony_ci * phyreg throughput mac, AND phy_reset is skipped at early stage when 203062306a36Sopenharmony_ci * band->pi is invalid. need to enable PHY CLK 203162306a36Sopenharmony_ci */ 203262306a36Sopenharmony_ci if (D11REV_GE(wlc_hw->corerev, 18)) 203362306a36Sopenharmony_ci flags |= SICF_PCLKE; 203462306a36Sopenharmony_ci 203562306a36Sopenharmony_ci /* 203662306a36Sopenharmony_ci * reset the core 203762306a36Sopenharmony_ci * In chips with PMU, the fastclk request goes through d11 core 203862306a36Sopenharmony_ci * reg 0x1e0, which is cleared by the core_reset. have to re-request it. 203962306a36Sopenharmony_ci * 204062306a36Sopenharmony_ci * This adds some delay and we can optimize it by also requesting 204162306a36Sopenharmony_ci * fastclk through chipcommon during this period if necessary. But 204262306a36Sopenharmony_ci * that has to work coordinate with other driver like mips/arm since 204362306a36Sopenharmony_ci * they may touch chipcommon as well. 204462306a36Sopenharmony_ci */ 204562306a36Sopenharmony_ci wlc_hw->clk = false; 204662306a36Sopenharmony_ci bcma_core_enable(wlc_hw->d11core, flags); 204762306a36Sopenharmony_ci wlc_hw->clk = true; 204862306a36Sopenharmony_ci if (wlc_hw->band && wlc_hw->band->pi) 204962306a36Sopenharmony_ci wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true); 205062306a36Sopenharmony_ci 205162306a36Sopenharmony_ci brcms_c_mctrl_reset(wlc_hw); 205262306a36Sopenharmony_ci 205362306a36Sopenharmony_ci if (ai_get_cccaps(wlc_hw->sih) & CC_CAP_PMU) 205462306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 205562306a36Sopenharmony_ci 205662306a36Sopenharmony_ci brcms_b_phy_reset(wlc_hw); 205762306a36Sopenharmony_ci 205862306a36Sopenharmony_ci /* turn on PHY_PLL */ 205962306a36Sopenharmony_ci brcms_b_core_phypll_ctl(wlc_hw, true); 206062306a36Sopenharmony_ci 206162306a36Sopenharmony_ci /* clear sw intstatus */ 206262306a36Sopenharmony_ci wlc_hw->wlc->macintstatus = 0; 206362306a36Sopenharmony_ci 206462306a36Sopenharmony_ci /* restore the clk setting */ 206562306a36Sopenharmony_ci if (!fastclk) 206662306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); 206762306a36Sopenharmony_ci} 206862306a36Sopenharmony_ci 206962306a36Sopenharmony_ci/* txfifo sizes needs to be modified(increased) since the newer cores 207062306a36Sopenharmony_ci * have more memory. 207162306a36Sopenharmony_ci */ 207262306a36Sopenharmony_cistatic void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw) 207362306a36Sopenharmony_ci{ 207462306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 207562306a36Sopenharmony_ci u16 fifo_nu; 207662306a36Sopenharmony_ci u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk; 207762306a36Sopenharmony_ci u16 txfifo_def, txfifo_def1; 207862306a36Sopenharmony_ci u16 txfifo_cmd; 207962306a36Sopenharmony_ci 208062306a36Sopenharmony_ci /* tx fifos start at TXFIFO_START_BLK from the Base address */ 208162306a36Sopenharmony_ci txfifo_startblk = TXFIFO_START_BLK; 208262306a36Sopenharmony_ci 208362306a36Sopenharmony_ci /* sequence of operations: reset fifo, set fifo size, reset fifo */ 208462306a36Sopenharmony_ci for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) { 208562306a36Sopenharmony_ci 208662306a36Sopenharmony_ci txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu]; 208762306a36Sopenharmony_ci txfifo_def = (txfifo_startblk & 0xff) | 208862306a36Sopenharmony_ci (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT); 208962306a36Sopenharmony_ci txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) | 209062306a36Sopenharmony_ci ((((txfifo_endblk - 209162306a36Sopenharmony_ci 1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT); 209262306a36Sopenharmony_ci txfifo_cmd = 209362306a36Sopenharmony_ci TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT); 209462306a36Sopenharmony_ci 209562306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); 209662306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(xmtfifodef), txfifo_def); 209762306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(xmtfifodef1), txfifo_def1); 209862306a36Sopenharmony_ci 209962306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(xmtfifocmd), txfifo_cmd); 210062306a36Sopenharmony_ci 210162306a36Sopenharmony_ci txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu]; 210262306a36Sopenharmony_ci } 210362306a36Sopenharmony_ci /* 210462306a36Sopenharmony_ci * need to propagate to shm location to be in sync since ucode/hw won't 210562306a36Sopenharmony_ci * do this 210662306a36Sopenharmony_ci */ 210762306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_FIFOSIZE0, 210862306a36Sopenharmony_ci wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]); 210962306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_FIFOSIZE1, 211062306a36Sopenharmony_ci wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]); 211162306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_FIFOSIZE2, 211262306a36Sopenharmony_ci ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw-> 211362306a36Sopenharmony_ci xmtfifo_sz[TX_AC_BK_FIFO])); 211462306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_FIFOSIZE3, 211562306a36Sopenharmony_ci ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw-> 211662306a36Sopenharmony_ci xmtfifo_sz[TX_BCMC_FIFO])); 211762306a36Sopenharmony_ci} 211862306a36Sopenharmony_ci 211962306a36Sopenharmony_ci/* This function is used for changing the tsf frac register 212062306a36Sopenharmony_ci * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz 212162306a36Sopenharmony_ci * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz 212262306a36Sopenharmony_ci * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz 212362306a36Sopenharmony_ci * HTPHY Formula is 2^26/freq(MHz) e.g. 212462306a36Sopenharmony_ci * For spuron2 - 126MHz -> 2^26/126 = 532610.0 212562306a36Sopenharmony_ci * - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082 212662306a36Sopenharmony_ci * For spuron: 123MHz -> 2^26/123 = 545600.5 212762306a36Sopenharmony_ci * - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341 212862306a36Sopenharmony_ci * For spur off: 120MHz -> 2^26/120 = 559240.5 212962306a36Sopenharmony_ci * - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889 213062306a36Sopenharmony_ci */ 213162306a36Sopenharmony_ci 213262306a36Sopenharmony_civoid brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode) 213362306a36Sopenharmony_ci{ 213462306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 213562306a36Sopenharmony_ci 213662306a36Sopenharmony_ci if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43224) || 213762306a36Sopenharmony_ci (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) { 213862306a36Sopenharmony_ci if (spurmode == WL_SPURAVOID_ON2) { /* 126Mhz */ 213962306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x2082); 214062306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); 214162306a36Sopenharmony_ci } else if (spurmode == WL_SPURAVOID_ON1) { /* 123Mhz */ 214262306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x5341); 214362306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); 214462306a36Sopenharmony_ci } else { /* 120Mhz */ 214562306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x8889); 214662306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0x8); 214762306a36Sopenharmony_ci } 214862306a36Sopenharmony_ci } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { 214962306a36Sopenharmony_ci if (spurmode == WL_SPURAVOID_ON1) { /* 82Mhz */ 215062306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0x7CE0); 215162306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); 215262306a36Sopenharmony_ci } else { /* 80Mhz */ 215362306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_l), 0xCCCD); 215462306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(tsf_clk_frac_h), 0xC); 215562306a36Sopenharmony_ci } 215662306a36Sopenharmony_ci } 215762306a36Sopenharmony_ci} 215862306a36Sopenharmony_ci 215962306a36Sopenharmony_civoid brcms_c_start_station(struct brcms_c_info *wlc, u8 *addr) 216062306a36Sopenharmony_ci{ 216162306a36Sopenharmony_ci memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); 216262306a36Sopenharmony_ci wlc->bsscfg->type = BRCMS_TYPE_STATION; 216362306a36Sopenharmony_ci} 216462306a36Sopenharmony_ci 216562306a36Sopenharmony_civoid brcms_c_start_ap(struct brcms_c_info *wlc, u8 *addr, const u8 *bssid, 216662306a36Sopenharmony_ci u8 *ssid, size_t ssid_len) 216762306a36Sopenharmony_ci{ 216862306a36Sopenharmony_ci brcms_c_set_ssid(wlc, ssid, ssid_len); 216962306a36Sopenharmony_ci 217062306a36Sopenharmony_ci memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); 217162306a36Sopenharmony_ci memcpy(wlc->bsscfg->BSSID, bssid, sizeof(wlc->bsscfg->BSSID)); 217262306a36Sopenharmony_ci wlc->bsscfg->type = BRCMS_TYPE_AP; 217362306a36Sopenharmony_ci 217462306a36Sopenharmony_ci brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, MCTL_AP | MCTL_INFRA); 217562306a36Sopenharmony_ci} 217662306a36Sopenharmony_ci 217762306a36Sopenharmony_civoid brcms_c_start_adhoc(struct brcms_c_info *wlc, u8 *addr) 217862306a36Sopenharmony_ci{ 217962306a36Sopenharmony_ci memcpy(wlc->pub->cur_etheraddr, addr, sizeof(wlc->pub->cur_etheraddr)); 218062306a36Sopenharmony_ci wlc->bsscfg->type = BRCMS_TYPE_ADHOC; 218162306a36Sopenharmony_ci 218262306a36Sopenharmony_ci brcms_b_mctrl(wlc->hw, MCTL_AP | MCTL_INFRA, 0); 218362306a36Sopenharmony_ci} 218462306a36Sopenharmony_ci 218562306a36Sopenharmony_ci/* Initialize GPIOs that are controlled by D11 core */ 218662306a36Sopenharmony_cistatic void brcms_c_gpio_init(struct brcms_c_info *wlc) 218762306a36Sopenharmony_ci{ 218862306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 218962306a36Sopenharmony_ci u32 gc, gm; 219062306a36Sopenharmony_ci 219162306a36Sopenharmony_ci /* use GPIO select 0 to get all gpio signals from the gpio out reg */ 219262306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0); 219362306a36Sopenharmony_ci 219462306a36Sopenharmony_ci /* 219562306a36Sopenharmony_ci * Common GPIO setup: 219662306a36Sopenharmony_ci * G0 = LED 0 = WLAN Activity 219762306a36Sopenharmony_ci * G1 = LED 1 = WLAN 2.4 GHz Radio State 219862306a36Sopenharmony_ci * G2 = LED 2 = WLAN 5 GHz Radio State 219962306a36Sopenharmony_ci * G4 = radio disable input (HI enabled, LO disabled) 220062306a36Sopenharmony_ci */ 220162306a36Sopenharmony_ci 220262306a36Sopenharmony_ci gc = gm = 0; 220362306a36Sopenharmony_ci 220462306a36Sopenharmony_ci /* Allocate GPIOs for mimo antenna diversity feature */ 220562306a36Sopenharmony_ci if (wlc_hw->antsel_type == ANTSEL_2x3) { 220662306a36Sopenharmony_ci /* Enable antenna diversity, use 2x3 mode */ 220762306a36Sopenharmony_ci brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, 220862306a36Sopenharmony_ci MHF3_ANTSEL_EN, BRCM_BAND_ALL); 220962306a36Sopenharmony_ci brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 221062306a36Sopenharmony_ci MHF3_ANTSEL_MODE, BRCM_BAND_ALL); 221162306a36Sopenharmony_ci 221262306a36Sopenharmony_ci /* init superswitch control */ 221362306a36Sopenharmony_ci wlc_phy_antsel_init(wlc_hw->band->pi, false); 221462306a36Sopenharmony_ci 221562306a36Sopenharmony_ci } else if (wlc_hw->antsel_type == ANTSEL_2x4) { 221662306a36Sopenharmony_ci gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13); 221762306a36Sopenharmony_ci /* 221862306a36Sopenharmony_ci * The board itself is powered by these GPIOs 221962306a36Sopenharmony_ci * (when not sending pattern) so set them high 222062306a36Sopenharmony_ci */ 222162306a36Sopenharmony_ci bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_oe), 222262306a36Sopenharmony_ci (BOARD_GPIO_12 | BOARD_GPIO_13)); 222362306a36Sopenharmony_ci bcma_set16(wlc_hw->d11core, D11REGOFFS(psm_gpio_out), 222462306a36Sopenharmony_ci (BOARD_GPIO_12 | BOARD_GPIO_13)); 222562306a36Sopenharmony_ci 222662306a36Sopenharmony_ci /* Enable antenna diversity, use 2x4 mode */ 222762306a36Sopenharmony_ci brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN, 222862306a36Sopenharmony_ci MHF3_ANTSEL_EN, BRCM_BAND_ALL); 222962306a36Sopenharmony_ci brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0, 223062306a36Sopenharmony_ci BRCM_BAND_ALL); 223162306a36Sopenharmony_ci 223262306a36Sopenharmony_ci /* Configure the desired clock to be 4Mhz */ 223362306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV, 223462306a36Sopenharmony_ci ANTSEL_CLKDIV_4MHZ); 223562306a36Sopenharmony_ci } 223662306a36Sopenharmony_ci 223762306a36Sopenharmony_ci /* 223862306a36Sopenharmony_ci * gpio 9 controls the PA. ucode is responsible 223962306a36Sopenharmony_ci * for wiggling out and oe 224062306a36Sopenharmony_ci */ 224162306a36Sopenharmony_ci if (wlc_hw->boardflags & BFL_PACTRL) 224262306a36Sopenharmony_ci gm |= gc |= BOARD_GPIO_PACTRL; 224362306a36Sopenharmony_ci 224462306a36Sopenharmony_ci /* apply to gpiocontrol register */ 224562306a36Sopenharmony_ci bcma_chipco_gpio_control(&wlc_hw->d11core->bus->drv_cc, gm, gc); 224662306a36Sopenharmony_ci} 224762306a36Sopenharmony_ci 224862306a36Sopenharmony_cistatic void brcms_ucode_write(struct brcms_hardware *wlc_hw, 224962306a36Sopenharmony_ci const __le32 ucode[], const size_t nbytes) 225062306a36Sopenharmony_ci{ 225162306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 225262306a36Sopenharmony_ci uint i; 225362306a36Sopenharmony_ci uint count; 225462306a36Sopenharmony_ci 225562306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); 225662306a36Sopenharmony_ci 225762306a36Sopenharmony_ci count = (nbytes / sizeof(u32)); 225862306a36Sopenharmony_ci 225962306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), 226062306a36Sopenharmony_ci OBJADDR_AUTO_INC | OBJADDR_UCM_SEL); 226162306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 226262306a36Sopenharmony_ci for (i = 0; i < count; i++) 226362306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), le32_to_cpu(ucode[i])); 226462306a36Sopenharmony_ci 226562306a36Sopenharmony_ci} 226662306a36Sopenharmony_ci 226762306a36Sopenharmony_cistatic void brcms_ucode_download(struct brcms_hardware *wlc_hw) 226862306a36Sopenharmony_ci{ 226962306a36Sopenharmony_ci struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; 227062306a36Sopenharmony_ci 227162306a36Sopenharmony_ci if (wlc_hw->ucode_loaded) 227262306a36Sopenharmony_ci return; 227362306a36Sopenharmony_ci 227462306a36Sopenharmony_ci if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { 227562306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band)) { 227662306a36Sopenharmony_ci brcms_ucode_write(wlc_hw, ucode->bcm43xx_16_mimo, 227762306a36Sopenharmony_ci ucode->bcm43xx_16_mimosz); 227862306a36Sopenharmony_ci wlc_hw->ucode_loaded = true; 227962306a36Sopenharmony_ci } else 228062306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 228162306a36Sopenharmony_ci "%s: wl%d: unsupported phy in corerev %d\n", 228262306a36Sopenharmony_ci __func__, wlc_hw->unit, wlc_hw->corerev); 228362306a36Sopenharmony_ci } else if (D11REV_IS(wlc_hw->corerev, 24)) { 228462306a36Sopenharmony_ci if (BRCMS_ISLCNPHY(wlc_hw->band)) { 228562306a36Sopenharmony_ci brcms_ucode_write(wlc_hw, ucode->bcm43xx_24_lcn, 228662306a36Sopenharmony_ci ucode->bcm43xx_24_lcnsz); 228762306a36Sopenharmony_ci wlc_hw->ucode_loaded = true; 228862306a36Sopenharmony_ci } else { 228962306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 229062306a36Sopenharmony_ci "%s: wl%d: unsupported phy in corerev %d\n", 229162306a36Sopenharmony_ci __func__, wlc_hw->unit, wlc_hw->corerev); 229262306a36Sopenharmony_ci } 229362306a36Sopenharmony_ci } 229462306a36Sopenharmony_ci} 229562306a36Sopenharmony_ci 229662306a36Sopenharmony_civoid brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant) 229762306a36Sopenharmony_ci{ 229862306a36Sopenharmony_ci /* update sw state */ 229962306a36Sopenharmony_ci wlc_hw->bmac_phytxant = phytxant; 230062306a36Sopenharmony_ci 230162306a36Sopenharmony_ci /* push to ucode if up */ 230262306a36Sopenharmony_ci if (!wlc_hw->up) 230362306a36Sopenharmony_ci return; 230462306a36Sopenharmony_ci brcms_c_ucode_txant_set(wlc_hw); 230562306a36Sopenharmony_ci 230662306a36Sopenharmony_ci} 230762306a36Sopenharmony_ci 230862306a36Sopenharmony_ciu16 brcms_b_get_txant(struct brcms_hardware *wlc_hw) 230962306a36Sopenharmony_ci{ 231062306a36Sopenharmony_ci return (u16) wlc_hw->wlc->stf->txant; 231162306a36Sopenharmony_ci} 231262306a36Sopenharmony_ci 231362306a36Sopenharmony_civoid brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type) 231462306a36Sopenharmony_ci{ 231562306a36Sopenharmony_ci wlc_hw->antsel_type = antsel_type; 231662306a36Sopenharmony_ci 231762306a36Sopenharmony_ci /* Update the antsel type for phy module to use */ 231862306a36Sopenharmony_ci wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type); 231962306a36Sopenharmony_ci} 232062306a36Sopenharmony_ci 232162306a36Sopenharmony_cistatic void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw) 232262306a36Sopenharmony_ci{ 232362306a36Sopenharmony_ci bool fatal = false; 232462306a36Sopenharmony_ci uint unit; 232562306a36Sopenharmony_ci uint intstatus, idx; 232662306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 232762306a36Sopenharmony_ci 232862306a36Sopenharmony_ci unit = wlc_hw->unit; 232962306a36Sopenharmony_ci 233062306a36Sopenharmony_ci for (idx = 0; idx < NFIFO; idx++) { 233162306a36Sopenharmony_ci /* read intstatus register and ignore any non-error bits */ 233262306a36Sopenharmony_ci intstatus = 233362306a36Sopenharmony_ci bcma_read32(core, 233462306a36Sopenharmony_ci D11REGOFFS(intctrlregs[idx].intstatus)) & 233562306a36Sopenharmony_ci I_ERRORS; 233662306a36Sopenharmony_ci if (!intstatus) 233762306a36Sopenharmony_ci continue; 233862306a36Sopenharmony_ci 233962306a36Sopenharmony_ci brcms_dbg_int(core, "wl%d: intstatus%d 0x%x\n", 234062306a36Sopenharmony_ci unit, idx, intstatus); 234162306a36Sopenharmony_ci 234262306a36Sopenharmony_ci if (intstatus & I_RO) { 234362306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: receive fifo " 234462306a36Sopenharmony_ci "overflow\n", unit, idx); 234562306a36Sopenharmony_ci fatal = true; 234662306a36Sopenharmony_ci } 234762306a36Sopenharmony_ci 234862306a36Sopenharmony_ci if (intstatus & I_PC) { 234962306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: descriptor error\n", 235062306a36Sopenharmony_ci unit, idx); 235162306a36Sopenharmony_ci fatal = true; 235262306a36Sopenharmony_ci } 235362306a36Sopenharmony_ci 235462306a36Sopenharmony_ci if (intstatus & I_PD) { 235562306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: data error\n", unit, 235662306a36Sopenharmony_ci idx); 235762306a36Sopenharmony_ci fatal = true; 235862306a36Sopenharmony_ci } 235962306a36Sopenharmony_ci 236062306a36Sopenharmony_ci if (intstatus & I_DE) { 236162306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: descriptor protocol " 236262306a36Sopenharmony_ci "error\n", unit, idx); 236362306a36Sopenharmony_ci fatal = true; 236462306a36Sopenharmony_ci } 236562306a36Sopenharmony_ci 236662306a36Sopenharmony_ci if (intstatus & I_RU) 236762306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: receive descriptor " 236862306a36Sopenharmony_ci "underflow\n", idx, unit); 236962306a36Sopenharmony_ci 237062306a36Sopenharmony_ci if (intstatus & I_XU) { 237162306a36Sopenharmony_ci brcms_err(core, "wl%d: fifo %d: transmit fifo " 237262306a36Sopenharmony_ci "underflow\n", idx, unit); 237362306a36Sopenharmony_ci fatal = true; 237462306a36Sopenharmony_ci } 237562306a36Sopenharmony_ci 237662306a36Sopenharmony_ci if (fatal) { 237762306a36Sopenharmony_ci brcms_fatal_error(wlc_hw->wlc->wl); /* big hammer */ 237862306a36Sopenharmony_ci break; 237962306a36Sopenharmony_ci } else 238062306a36Sopenharmony_ci bcma_write32(core, 238162306a36Sopenharmony_ci D11REGOFFS(intctrlregs[idx].intstatus), 238262306a36Sopenharmony_ci intstatus); 238362306a36Sopenharmony_ci } 238462306a36Sopenharmony_ci} 238562306a36Sopenharmony_ci 238662306a36Sopenharmony_civoid brcms_c_intrson(struct brcms_c_info *wlc) 238762306a36Sopenharmony_ci{ 238862306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 238962306a36Sopenharmony_ci wlc->macintmask = wlc->defmacintmask; 239062306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); 239162306a36Sopenharmony_ci} 239262306a36Sopenharmony_ci 239362306a36Sopenharmony_ciu32 brcms_c_intrsoff(struct brcms_c_info *wlc) 239462306a36Sopenharmony_ci{ 239562306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 239662306a36Sopenharmony_ci u32 macintmask; 239762306a36Sopenharmony_ci 239862306a36Sopenharmony_ci if (!wlc_hw->clk) 239962306a36Sopenharmony_ci return 0; 240062306a36Sopenharmony_ci 240162306a36Sopenharmony_ci macintmask = wlc->macintmask; /* isr can still happen */ 240262306a36Sopenharmony_ci 240362306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), 0); 240462306a36Sopenharmony_ci (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(macintmask)); 240562306a36Sopenharmony_ci udelay(1); /* ensure int line is no longer driven */ 240662306a36Sopenharmony_ci wlc->macintmask = 0; 240762306a36Sopenharmony_ci 240862306a36Sopenharmony_ci /* return previous macintmask; resolve race between us and our isr */ 240962306a36Sopenharmony_ci return wlc->macintstatus ? 0 : macintmask; 241062306a36Sopenharmony_ci} 241162306a36Sopenharmony_ci 241262306a36Sopenharmony_civoid brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask) 241362306a36Sopenharmony_ci{ 241462306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 241562306a36Sopenharmony_ci if (!wlc_hw->clk) 241662306a36Sopenharmony_ci return; 241762306a36Sopenharmony_ci 241862306a36Sopenharmony_ci wlc->macintmask = macintmask; 241962306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(macintmask), wlc->macintmask); 242062306a36Sopenharmony_ci} 242162306a36Sopenharmony_ci 242262306a36Sopenharmony_ci/* assumes that the d11 MAC is enabled */ 242362306a36Sopenharmony_cistatic void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw, 242462306a36Sopenharmony_ci uint tx_fifo) 242562306a36Sopenharmony_ci{ 242662306a36Sopenharmony_ci u8 fifo = 1 << tx_fifo; 242762306a36Sopenharmony_ci 242862306a36Sopenharmony_ci /* Two clients of this code, 11h Quiet period and scanning. */ 242962306a36Sopenharmony_ci 243062306a36Sopenharmony_ci /* only suspend if not already suspended */ 243162306a36Sopenharmony_ci if ((wlc_hw->suspended_fifos & fifo) == fifo) 243262306a36Sopenharmony_ci return; 243362306a36Sopenharmony_ci 243462306a36Sopenharmony_ci /* force the core awake only if not already */ 243562306a36Sopenharmony_ci if (wlc_hw->suspended_fifos == 0) 243662306a36Sopenharmony_ci brcms_c_ucode_wake_override_set(wlc_hw, 243762306a36Sopenharmony_ci BRCMS_WAKE_OVERRIDE_TXFIFO); 243862306a36Sopenharmony_ci 243962306a36Sopenharmony_ci wlc_hw->suspended_fifos |= fifo; 244062306a36Sopenharmony_ci 244162306a36Sopenharmony_ci if (wlc_hw->di[tx_fifo]) { 244262306a36Sopenharmony_ci /* 244362306a36Sopenharmony_ci * Suspending AMPDU transmissions in the middle can cause 244462306a36Sopenharmony_ci * underflow which may result in mismatch between ucode and 244562306a36Sopenharmony_ci * driver so suspend the mac before suspending the FIFO 244662306a36Sopenharmony_ci */ 244762306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc_hw->band)) 244862306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc_hw->wlc); 244962306a36Sopenharmony_ci 245062306a36Sopenharmony_ci dma_txsuspend(wlc_hw->di[tx_fifo]); 245162306a36Sopenharmony_ci 245262306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc_hw->band)) 245362306a36Sopenharmony_ci brcms_c_enable_mac(wlc_hw->wlc); 245462306a36Sopenharmony_ci } 245562306a36Sopenharmony_ci} 245662306a36Sopenharmony_ci 245762306a36Sopenharmony_cistatic void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw, 245862306a36Sopenharmony_ci uint tx_fifo) 245962306a36Sopenharmony_ci{ 246062306a36Sopenharmony_ci /* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case 246162306a36Sopenharmony_ci * but need to be done here for PIO otherwise the watchdog will catch 246262306a36Sopenharmony_ci * the inconsistency and fire 246362306a36Sopenharmony_ci */ 246462306a36Sopenharmony_ci /* Two clients of this code, 11h Quiet period and scanning. */ 246562306a36Sopenharmony_ci if (wlc_hw->di[tx_fifo]) 246662306a36Sopenharmony_ci dma_txresume(wlc_hw->di[tx_fifo]); 246762306a36Sopenharmony_ci 246862306a36Sopenharmony_ci /* allow core to sleep again */ 246962306a36Sopenharmony_ci if (wlc_hw->suspended_fifos == 0) 247062306a36Sopenharmony_ci return; 247162306a36Sopenharmony_ci else { 247262306a36Sopenharmony_ci wlc_hw->suspended_fifos &= ~(1 << tx_fifo); 247362306a36Sopenharmony_ci if (wlc_hw->suspended_fifos == 0) 247462306a36Sopenharmony_ci brcms_c_ucode_wake_override_clear(wlc_hw, 247562306a36Sopenharmony_ci BRCMS_WAKE_OVERRIDE_TXFIFO); 247662306a36Sopenharmony_ci } 247762306a36Sopenharmony_ci} 247862306a36Sopenharmony_ci 247962306a36Sopenharmony_ci/* precondition: requires the mac core to be enabled */ 248062306a36Sopenharmony_cistatic void brcms_b_mute(struct brcms_hardware *wlc_hw, bool mute_tx) 248162306a36Sopenharmony_ci{ 248262306a36Sopenharmony_ci static const u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0}; 248362306a36Sopenharmony_ci u8 *ethaddr = wlc_hw->wlc->pub->cur_etheraddr; 248462306a36Sopenharmony_ci 248562306a36Sopenharmony_ci if (mute_tx) { 248662306a36Sopenharmony_ci /* suspend tx fifos */ 248762306a36Sopenharmony_ci brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO); 248862306a36Sopenharmony_ci brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO); 248962306a36Sopenharmony_ci brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO); 249062306a36Sopenharmony_ci brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO); 249162306a36Sopenharmony_ci 249262306a36Sopenharmony_ci /* zero the address match register so we do not send ACKs */ 249362306a36Sopenharmony_ci brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, null_ether_addr); 249462306a36Sopenharmony_ci } else { 249562306a36Sopenharmony_ci /* resume tx fifos */ 249662306a36Sopenharmony_ci brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO); 249762306a36Sopenharmony_ci brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO); 249862306a36Sopenharmony_ci brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO); 249962306a36Sopenharmony_ci brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO); 250062306a36Sopenharmony_ci 250162306a36Sopenharmony_ci /* Restore address */ 250262306a36Sopenharmony_ci brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET, ethaddr); 250362306a36Sopenharmony_ci } 250462306a36Sopenharmony_ci 250562306a36Sopenharmony_ci wlc_phy_mute_upd(wlc_hw->band->pi, mute_tx, 0); 250662306a36Sopenharmony_ci 250762306a36Sopenharmony_ci if (mute_tx) 250862306a36Sopenharmony_ci brcms_c_ucode_mute_override_set(wlc_hw); 250962306a36Sopenharmony_ci else 251062306a36Sopenharmony_ci brcms_c_ucode_mute_override_clear(wlc_hw); 251162306a36Sopenharmony_ci} 251262306a36Sopenharmony_ci 251362306a36Sopenharmony_civoid 251462306a36Sopenharmony_cibrcms_c_mute(struct brcms_c_info *wlc, bool mute_tx) 251562306a36Sopenharmony_ci{ 251662306a36Sopenharmony_ci brcms_b_mute(wlc->hw, mute_tx); 251762306a36Sopenharmony_ci} 251862306a36Sopenharmony_ci 251962306a36Sopenharmony_ci/* 252062306a36Sopenharmony_ci * Read and clear macintmask and macintstatus and intstatus registers. 252162306a36Sopenharmony_ci * This routine should be called with interrupts off 252262306a36Sopenharmony_ci * Return: 252362306a36Sopenharmony_ci * -1 if brcms_deviceremoved(wlc) evaluates to true; 252462306a36Sopenharmony_ci * 0 if the interrupt is not for us, or we are in some special cases; 252562306a36Sopenharmony_ci * device interrupt status bits otherwise. 252662306a36Sopenharmony_ci */ 252762306a36Sopenharmony_cistatic inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr) 252862306a36Sopenharmony_ci{ 252962306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 253062306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 253162306a36Sopenharmony_ci u32 macintstatus, mask; 253262306a36Sopenharmony_ci 253362306a36Sopenharmony_ci /* macintstatus includes a DMA interrupt summary bit */ 253462306a36Sopenharmony_ci macintstatus = bcma_read32(core, D11REGOFFS(macintstatus)); 253562306a36Sopenharmony_ci mask = in_isr ? wlc->macintmask : wlc->defmacintmask; 253662306a36Sopenharmony_ci 253762306a36Sopenharmony_ci trace_brcms_macintstatus(&core->dev, in_isr, macintstatus, mask); 253862306a36Sopenharmony_ci 253962306a36Sopenharmony_ci /* detect cardbus removed, in power down(suspend) and in reset */ 254062306a36Sopenharmony_ci if (brcms_deviceremoved(wlc)) 254162306a36Sopenharmony_ci return -1; 254262306a36Sopenharmony_ci 254362306a36Sopenharmony_ci /* brcms_deviceremoved() succeeds even when the core is still resetting, 254462306a36Sopenharmony_ci * handle that case here. 254562306a36Sopenharmony_ci */ 254662306a36Sopenharmony_ci if (macintstatus == 0xffffffff) 254762306a36Sopenharmony_ci return 0; 254862306a36Sopenharmony_ci 254962306a36Sopenharmony_ci /* defer unsolicited interrupts */ 255062306a36Sopenharmony_ci macintstatus &= mask; 255162306a36Sopenharmony_ci 255262306a36Sopenharmony_ci /* if not for us */ 255362306a36Sopenharmony_ci if (macintstatus == 0) 255462306a36Sopenharmony_ci return 0; 255562306a36Sopenharmony_ci 255662306a36Sopenharmony_ci /* turn off the interrupts */ 255762306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintmask), 0); 255862306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(macintmask)); 255962306a36Sopenharmony_ci wlc->macintmask = 0; 256062306a36Sopenharmony_ci 256162306a36Sopenharmony_ci /* clear device interrupts */ 256262306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintstatus), macintstatus); 256362306a36Sopenharmony_ci 256462306a36Sopenharmony_ci /* MI_DMAINT is indication of non-zero intstatus */ 256562306a36Sopenharmony_ci if (macintstatus & MI_DMAINT) 256662306a36Sopenharmony_ci /* 256762306a36Sopenharmony_ci * only fifo interrupt enabled is I_RI in 256862306a36Sopenharmony_ci * RX_FIFO. If MI_DMAINT is set, assume it 256962306a36Sopenharmony_ci * is set and clear the interrupt. 257062306a36Sopenharmony_ci */ 257162306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intstatus), 257262306a36Sopenharmony_ci DEF_RXINTMASK); 257362306a36Sopenharmony_ci 257462306a36Sopenharmony_ci return macintstatus; 257562306a36Sopenharmony_ci} 257662306a36Sopenharmony_ci 257762306a36Sopenharmony_ci/* Update wlc->macintstatus and wlc->intstatus[]. */ 257862306a36Sopenharmony_ci/* Return true if they are updated successfully. false otherwise */ 257962306a36Sopenharmony_cibool brcms_c_intrsupd(struct brcms_c_info *wlc) 258062306a36Sopenharmony_ci{ 258162306a36Sopenharmony_ci u32 macintstatus; 258262306a36Sopenharmony_ci 258362306a36Sopenharmony_ci /* read and clear macintstatus and intstatus registers */ 258462306a36Sopenharmony_ci macintstatus = wlc_intstatus(wlc, false); 258562306a36Sopenharmony_ci 258662306a36Sopenharmony_ci /* device is removed */ 258762306a36Sopenharmony_ci if (macintstatus == 0xffffffff) 258862306a36Sopenharmony_ci return false; 258962306a36Sopenharmony_ci 259062306a36Sopenharmony_ci /* update interrupt status in software */ 259162306a36Sopenharmony_ci wlc->macintstatus |= macintstatus; 259262306a36Sopenharmony_ci 259362306a36Sopenharmony_ci return true; 259462306a36Sopenharmony_ci} 259562306a36Sopenharmony_ci 259662306a36Sopenharmony_ci/* 259762306a36Sopenharmony_ci * First-level interrupt processing. 259862306a36Sopenharmony_ci * Return true if this was our interrupt 259962306a36Sopenharmony_ci * and if further brcms_c_dpc() processing is required, 260062306a36Sopenharmony_ci * false otherwise. 260162306a36Sopenharmony_ci */ 260262306a36Sopenharmony_cibool brcms_c_isr(struct brcms_c_info *wlc) 260362306a36Sopenharmony_ci{ 260462306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 260562306a36Sopenharmony_ci u32 macintstatus; 260662306a36Sopenharmony_ci 260762306a36Sopenharmony_ci if (!wlc_hw->up || !wlc->macintmask) 260862306a36Sopenharmony_ci return false; 260962306a36Sopenharmony_ci 261062306a36Sopenharmony_ci /* read and clear macintstatus and intstatus registers */ 261162306a36Sopenharmony_ci macintstatus = wlc_intstatus(wlc, true); 261262306a36Sopenharmony_ci 261362306a36Sopenharmony_ci if (macintstatus == 0xffffffff) { 261462306a36Sopenharmony_ci brcms_err(wlc_hw->d11core, 261562306a36Sopenharmony_ci "DEVICEREMOVED detected in the ISR code path\n"); 261662306a36Sopenharmony_ci return false; 261762306a36Sopenharmony_ci } 261862306a36Sopenharmony_ci 261962306a36Sopenharmony_ci /* it is not for us */ 262062306a36Sopenharmony_ci if (macintstatus == 0) 262162306a36Sopenharmony_ci return false; 262262306a36Sopenharmony_ci 262362306a36Sopenharmony_ci /* save interrupt status bits */ 262462306a36Sopenharmony_ci wlc->macintstatus = macintstatus; 262562306a36Sopenharmony_ci 262662306a36Sopenharmony_ci return true; 262762306a36Sopenharmony_ci 262862306a36Sopenharmony_ci} 262962306a36Sopenharmony_ci 263062306a36Sopenharmony_civoid brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc) 263162306a36Sopenharmony_ci{ 263262306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 263362306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 263462306a36Sopenharmony_ci u32 mc, mi; 263562306a36Sopenharmony_ci 263662306a36Sopenharmony_ci brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, 263762306a36Sopenharmony_ci wlc_hw->band->bandunit); 263862306a36Sopenharmony_ci 263962306a36Sopenharmony_ci /* 264062306a36Sopenharmony_ci * Track overlapping suspend requests 264162306a36Sopenharmony_ci */ 264262306a36Sopenharmony_ci wlc_hw->mac_suspend_depth++; 264362306a36Sopenharmony_ci if (wlc_hw->mac_suspend_depth > 1) 264462306a36Sopenharmony_ci return; 264562306a36Sopenharmony_ci 264662306a36Sopenharmony_ci /* force the core awake */ 264762306a36Sopenharmony_ci brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND); 264862306a36Sopenharmony_ci 264962306a36Sopenharmony_ci mc = bcma_read32(core, D11REGOFFS(maccontrol)); 265062306a36Sopenharmony_ci 265162306a36Sopenharmony_ci if (mc == 0xffffffff) { 265262306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, 265362306a36Sopenharmony_ci __func__); 265462306a36Sopenharmony_ci brcms_down(wlc->wl); 265562306a36Sopenharmony_ci return; 265662306a36Sopenharmony_ci } 265762306a36Sopenharmony_ci WARN_ON(mc & MCTL_PSM_JMP_0); 265862306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_PSM_RUN)); 265962306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_EN_MAC)); 266062306a36Sopenharmony_ci 266162306a36Sopenharmony_ci mi = bcma_read32(core, D11REGOFFS(macintstatus)); 266262306a36Sopenharmony_ci if (mi == 0xffffffff) { 266362306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, 266462306a36Sopenharmony_ci __func__); 266562306a36Sopenharmony_ci brcms_down(wlc->wl); 266662306a36Sopenharmony_ci return; 266762306a36Sopenharmony_ci } 266862306a36Sopenharmony_ci WARN_ON(mi & MI_MACSSPNDD); 266962306a36Sopenharmony_ci 267062306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0); 267162306a36Sopenharmony_ci 267262306a36Sopenharmony_ci SPINWAIT(!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD), 267362306a36Sopenharmony_ci BRCMS_MAX_MAC_SUSPEND); 267462306a36Sopenharmony_ci 267562306a36Sopenharmony_ci if (!(bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD)) { 267662306a36Sopenharmony_ci brcms_err(core, "wl%d: wlc_suspend_mac_and_wait: waited %d uS" 267762306a36Sopenharmony_ci " and MI_MACSSPNDD is still not on.\n", 267862306a36Sopenharmony_ci wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND); 267962306a36Sopenharmony_ci brcms_err(core, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, " 268062306a36Sopenharmony_ci "psm_brc 0x%04x\n", wlc_hw->unit, 268162306a36Sopenharmony_ci bcma_read32(core, D11REGOFFS(psmdebug)), 268262306a36Sopenharmony_ci bcma_read32(core, D11REGOFFS(phydebug)), 268362306a36Sopenharmony_ci bcma_read16(core, D11REGOFFS(psm_brc))); 268462306a36Sopenharmony_ci } 268562306a36Sopenharmony_ci 268662306a36Sopenharmony_ci mc = bcma_read32(core, D11REGOFFS(maccontrol)); 268762306a36Sopenharmony_ci if (mc == 0xffffffff) { 268862306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, 268962306a36Sopenharmony_ci __func__); 269062306a36Sopenharmony_ci brcms_down(wlc->wl); 269162306a36Sopenharmony_ci return; 269262306a36Sopenharmony_ci } 269362306a36Sopenharmony_ci WARN_ON(mc & MCTL_PSM_JMP_0); 269462306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_PSM_RUN)); 269562306a36Sopenharmony_ci WARN_ON(mc & MCTL_EN_MAC); 269662306a36Sopenharmony_ci} 269762306a36Sopenharmony_ci 269862306a36Sopenharmony_civoid brcms_c_enable_mac(struct brcms_c_info *wlc) 269962306a36Sopenharmony_ci{ 270062306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 270162306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 270262306a36Sopenharmony_ci u32 mc, mi; 270362306a36Sopenharmony_ci 270462306a36Sopenharmony_ci brcms_dbg_mac80211(core, "wl%d: bandunit %d\n", wlc_hw->unit, 270562306a36Sopenharmony_ci wlc->band->bandunit); 270662306a36Sopenharmony_ci 270762306a36Sopenharmony_ci /* 270862306a36Sopenharmony_ci * Track overlapping suspend requests 270962306a36Sopenharmony_ci */ 271062306a36Sopenharmony_ci wlc_hw->mac_suspend_depth--; 271162306a36Sopenharmony_ci if (wlc_hw->mac_suspend_depth > 0) 271262306a36Sopenharmony_ci return; 271362306a36Sopenharmony_ci 271462306a36Sopenharmony_ci mc = bcma_read32(core, D11REGOFFS(maccontrol)); 271562306a36Sopenharmony_ci WARN_ON(mc & MCTL_PSM_JMP_0); 271662306a36Sopenharmony_ci WARN_ON(mc & MCTL_EN_MAC); 271762306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_PSM_RUN)); 271862306a36Sopenharmony_ci 271962306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC); 272062306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintstatus), MI_MACSSPNDD); 272162306a36Sopenharmony_ci 272262306a36Sopenharmony_ci mc = bcma_read32(core, D11REGOFFS(maccontrol)); 272362306a36Sopenharmony_ci WARN_ON(mc & MCTL_PSM_JMP_0); 272462306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_EN_MAC)); 272562306a36Sopenharmony_ci WARN_ON(!(mc & MCTL_PSM_RUN)); 272662306a36Sopenharmony_ci 272762306a36Sopenharmony_ci mi = bcma_read32(core, D11REGOFFS(macintstatus)); 272862306a36Sopenharmony_ci WARN_ON(mi & MI_MACSSPNDD); 272962306a36Sopenharmony_ci 273062306a36Sopenharmony_ci brcms_c_ucode_wake_override_clear(wlc_hw, 273162306a36Sopenharmony_ci BRCMS_WAKE_OVERRIDE_MACSUSPEND); 273262306a36Sopenharmony_ci} 273362306a36Sopenharmony_ci 273462306a36Sopenharmony_civoid brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode) 273562306a36Sopenharmony_ci{ 273662306a36Sopenharmony_ci wlc_hw->hw_stf_ss_opmode = stf_mode; 273762306a36Sopenharmony_ci 273862306a36Sopenharmony_ci if (wlc_hw->clk) 273962306a36Sopenharmony_ci brcms_upd_ofdm_pctl1_table(wlc_hw); 274062306a36Sopenharmony_ci} 274162306a36Sopenharmony_ci 274262306a36Sopenharmony_cistatic bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw) 274362306a36Sopenharmony_ci{ 274462306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 274562306a36Sopenharmony_ci u32 w, val; 274662306a36Sopenharmony_ci struct wiphy *wiphy = wlc_hw->wlc->wiphy; 274762306a36Sopenharmony_ci 274862306a36Sopenharmony_ci /* Validate dchip register access */ 274962306a36Sopenharmony_ci 275062306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 275162306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 275262306a36Sopenharmony_ci w = bcma_read32(core, D11REGOFFS(objdata)); 275362306a36Sopenharmony_ci 275462306a36Sopenharmony_ci /* Can we write and read back a 32bit register? */ 275562306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 275662306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 275762306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), (u32) 0xaa5555aa); 275862306a36Sopenharmony_ci 275962306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 276062306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 276162306a36Sopenharmony_ci val = bcma_read32(core, D11REGOFFS(objdata)); 276262306a36Sopenharmony_ci if (val != (u32) 0xaa5555aa) { 276362306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " 276462306a36Sopenharmony_ci "expected 0xaa5555aa\n", wlc_hw->unit, val); 276562306a36Sopenharmony_ci return false; 276662306a36Sopenharmony_ci } 276762306a36Sopenharmony_ci 276862306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 276962306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 277062306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), (u32) 0x55aaaa55); 277162306a36Sopenharmony_ci 277262306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 277362306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 277462306a36Sopenharmony_ci val = bcma_read32(core, D11REGOFFS(objdata)); 277562306a36Sopenharmony_ci if (val != (u32) 0x55aaaa55) { 277662306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, " 277762306a36Sopenharmony_ci "expected 0x55aaaa55\n", wlc_hw->unit, val); 277862306a36Sopenharmony_ci return false; 277962306a36Sopenharmony_ci } 278062306a36Sopenharmony_ci 278162306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), OBJADDR_SHM_SEL | 0); 278262306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 278362306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), w); 278462306a36Sopenharmony_ci 278562306a36Sopenharmony_ci /* clear CFPStart */ 278662306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tsf_cfpstart), 0); 278762306a36Sopenharmony_ci 278862306a36Sopenharmony_ci w = bcma_read32(core, D11REGOFFS(maccontrol)); 278962306a36Sopenharmony_ci if ((w != (MCTL_IHR_EN | MCTL_WAKE)) && 279062306a36Sopenharmony_ci (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) { 279162306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = " 279262306a36Sopenharmony_ci "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w, 279362306a36Sopenharmony_ci (MCTL_IHR_EN | MCTL_WAKE), 279462306a36Sopenharmony_ci (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE)); 279562306a36Sopenharmony_ci return false; 279662306a36Sopenharmony_ci } 279762306a36Sopenharmony_ci 279862306a36Sopenharmony_ci return true; 279962306a36Sopenharmony_ci} 280062306a36Sopenharmony_ci 280162306a36Sopenharmony_ci#define PHYPLL_WAIT_US 100000 280262306a36Sopenharmony_ci 280362306a36Sopenharmony_civoid brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on) 280462306a36Sopenharmony_ci{ 280562306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 280662306a36Sopenharmony_ci u32 tmp; 280762306a36Sopenharmony_ci 280862306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d\n", wlc_hw->unit); 280962306a36Sopenharmony_ci 281062306a36Sopenharmony_ci tmp = 0; 281162306a36Sopenharmony_ci 281262306a36Sopenharmony_ci if (on) { 281362306a36Sopenharmony_ci if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { 281462306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(clk_ctl_st), 281562306a36Sopenharmony_ci CCS_ERSRC_REQ_HT | 281662306a36Sopenharmony_ci CCS_ERSRC_REQ_D11PLL | 281762306a36Sopenharmony_ci CCS_ERSRC_REQ_PHYPLL); 281862306a36Sopenharmony_ci SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & 281962306a36Sopenharmony_ci CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT, 282062306a36Sopenharmony_ci PHYPLL_WAIT_US); 282162306a36Sopenharmony_ci 282262306a36Sopenharmony_ci tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); 282362306a36Sopenharmony_ci if ((tmp & CCS_ERSRC_AVAIL_HT) != CCS_ERSRC_AVAIL_HT) 282462306a36Sopenharmony_ci brcms_err(core, "%s: turn on PHY PLL failed\n", 282562306a36Sopenharmony_ci __func__); 282662306a36Sopenharmony_ci } else { 282762306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(clk_ctl_st), 282862306a36Sopenharmony_ci tmp | CCS_ERSRC_REQ_D11PLL | 282962306a36Sopenharmony_ci CCS_ERSRC_REQ_PHYPLL); 283062306a36Sopenharmony_ci SPINWAIT((bcma_read32(core, D11REGOFFS(clk_ctl_st)) & 283162306a36Sopenharmony_ci (CCS_ERSRC_AVAIL_D11PLL | 283262306a36Sopenharmony_ci CCS_ERSRC_AVAIL_PHYPLL)) != 283362306a36Sopenharmony_ci (CCS_ERSRC_AVAIL_D11PLL | 283462306a36Sopenharmony_ci CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US); 283562306a36Sopenharmony_ci 283662306a36Sopenharmony_ci tmp = bcma_read32(core, D11REGOFFS(clk_ctl_st)); 283762306a36Sopenharmony_ci if ((tmp & 283862306a36Sopenharmony_ci (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) 283962306a36Sopenharmony_ci != 284062306a36Sopenharmony_ci (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) 284162306a36Sopenharmony_ci brcms_err(core, "%s: turn on PHY PLL failed\n", 284262306a36Sopenharmony_ci __func__); 284362306a36Sopenharmony_ci } 284462306a36Sopenharmony_ci } else { 284562306a36Sopenharmony_ci /* 284662306a36Sopenharmony_ci * Since the PLL may be shared, other cores can still 284762306a36Sopenharmony_ci * be requesting it; so we'll deassert the request but 284862306a36Sopenharmony_ci * not wait for status to comply. 284962306a36Sopenharmony_ci */ 285062306a36Sopenharmony_ci bcma_mask32(core, D11REGOFFS(clk_ctl_st), 285162306a36Sopenharmony_ci ~CCS_ERSRC_REQ_PHYPLL); 285262306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(clk_ctl_st)); 285362306a36Sopenharmony_ci } 285462306a36Sopenharmony_ci} 285562306a36Sopenharmony_ci 285662306a36Sopenharmony_cistatic void brcms_c_coredisable(struct brcms_hardware *wlc_hw) 285762306a36Sopenharmony_ci{ 285862306a36Sopenharmony_ci bool dev_gone; 285962306a36Sopenharmony_ci 286062306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d: disable core\n", wlc_hw->unit); 286162306a36Sopenharmony_ci 286262306a36Sopenharmony_ci dev_gone = brcms_deviceremoved(wlc_hw->wlc); 286362306a36Sopenharmony_ci 286462306a36Sopenharmony_ci if (dev_gone) 286562306a36Sopenharmony_ci return; 286662306a36Sopenharmony_ci 286762306a36Sopenharmony_ci if (wlc_hw->noreset) 286862306a36Sopenharmony_ci return; 286962306a36Sopenharmony_ci 287062306a36Sopenharmony_ci /* radio off */ 287162306a36Sopenharmony_ci wlc_phy_switch_radio(wlc_hw->band->pi, OFF); 287262306a36Sopenharmony_ci 287362306a36Sopenharmony_ci /* turn off analog core */ 287462306a36Sopenharmony_ci wlc_phy_anacore(wlc_hw->band->pi, OFF); 287562306a36Sopenharmony_ci 287662306a36Sopenharmony_ci /* turn off PHYPLL to save power */ 287762306a36Sopenharmony_ci brcms_b_core_phypll_ctl(wlc_hw, false); 287862306a36Sopenharmony_ci 287962306a36Sopenharmony_ci wlc_hw->clk = false; 288062306a36Sopenharmony_ci bcma_core_disable(wlc_hw->d11core, 0); 288162306a36Sopenharmony_ci wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); 288262306a36Sopenharmony_ci} 288362306a36Sopenharmony_ci 288462306a36Sopenharmony_cistatic void brcms_c_flushqueues(struct brcms_c_info *wlc) 288562306a36Sopenharmony_ci{ 288662306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 288762306a36Sopenharmony_ci uint i; 288862306a36Sopenharmony_ci 288962306a36Sopenharmony_ci /* free any posted tx packets */ 289062306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) { 289162306a36Sopenharmony_ci if (wlc_hw->di[i]) { 289262306a36Sopenharmony_ci dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL); 289362306a36Sopenharmony_ci if (i < TX_BCMC_FIFO) 289462306a36Sopenharmony_ci ieee80211_wake_queue(wlc->pub->ieee_hw, 289562306a36Sopenharmony_ci brcms_fifo_to_ac(i)); 289662306a36Sopenharmony_ci } 289762306a36Sopenharmony_ci } 289862306a36Sopenharmony_ci 289962306a36Sopenharmony_ci /* free any posted rx packets */ 290062306a36Sopenharmony_ci dma_rxreclaim(wlc_hw->di[RX_FIFO]); 290162306a36Sopenharmony_ci} 290262306a36Sopenharmony_ci 290362306a36Sopenharmony_cistatic u16 290462306a36Sopenharmony_cibrcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel) 290562306a36Sopenharmony_ci{ 290662306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 290762306a36Sopenharmony_ci u16 objoff = D11REGOFFS(objdata); 290862306a36Sopenharmony_ci 290962306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); 291062306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 291162306a36Sopenharmony_ci if (offset & 2) 291262306a36Sopenharmony_ci objoff += 2; 291362306a36Sopenharmony_ci 291462306a36Sopenharmony_ci return bcma_read16(core, objoff); 291562306a36Sopenharmony_ci} 291662306a36Sopenharmony_ci 291762306a36Sopenharmony_cistatic void 291862306a36Sopenharmony_cibrcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v, 291962306a36Sopenharmony_ci u32 sel) 292062306a36Sopenharmony_ci{ 292162306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 292262306a36Sopenharmony_ci u16 objoff = D11REGOFFS(objdata); 292362306a36Sopenharmony_ci 292462306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), sel | (offset >> 2)); 292562306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 292662306a36Sopenharmony_ci if (offset & 2) 292762306a36Sopenharmony_ci objoff += 2; 292862306a36Sopenharmony_ci 292962306a36Sopenharmony_ci bcma_wflush16(core, objoff, v); 293062306a36Sopenharmony_ci} 293162306a36Sopenharmony_ci 293262306a36Sopenharmony_ci/* 293362306a36Sopenharmony_ci * Read a single u16 from shared memory. 293462306a36Sopenharmony_ci * SHM 'offset' needs to be an even address 293562306a36Sopenharmony_ci */ 293662306a36Sopenharmony_ciu16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset) 293762306a36Sopenharmony_ci{ 293862306a36Sopenharmony_ci return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL); 293962306a36Sopenharmony_ci} 294062306a36Sopenharmony_ci 294162306a36Sopenharmony_ci/* 294262306a36Sopenharmony_ci * Write a single u16 to shared memory. 294362306a36Sopenharmony_ci * SHM 'offset' needs to be an even address 294462306a36Sopenharmony_ci */ 294562306a36Sopenharmony_civoid brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v) 294662306a36Sopenharmony_ci{ 294762306a36Sopenharmony_ci brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL); 294862306a36Sopenharmony_ci} 294962306a36Sopenharmony_ci 295062306a36Sopenharmony_ci/* 295162306a36Sopenharmony_ci * Copy a buffer to shared memory of specified type . 295262306a36Sopenharmony_ci * SHM 'offset' needs to be an even address and 295362306a36Sopenharmony_ci * Buffer length 'len' must be an even number of bytes 295462306a36Sopenharmony_ci * 'sel' selects the type of memory 295562306a36Sopenharmony_ci */ 295662306a36Sopenharmony_civoid 295762306a36Sopenharmony_cibrcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset, 295862306a36Sopenharmony_ci const void *buf, int len, u32 sel) 295962306a36Sopenharmony_ci{ 296062306a36Sopenharmony_ci u16 v; 296162306a36Sopenharmony_ci const u8 *p = (const u8 *)buf; 296262306a36Sopenharmony_ci int i; 296362306a36Sopenharmony_ci 296462306a36Sopenharmony_ci if (len <= 0 || (offset & 1) || (len & 1)) 296562306a36Sopenharmony_ci return; 296662306a36Sopenharmony_ci 296762306a36Sopenharmony_ci for (i = 0; i < len; i += 2) { 296862306a36Sopenharmony_ci v = p[i] | (p[i + 1] << 8); 296962306a36Sopenharmony_ci brcms_b_write_objmem(wlc_hw, offset + i, v, sel); 297062306a36Sopenharmony_ci } 297162306a36Sopenharmony_ci} 297262306a36Sopenharmony_ci 297362306a36Sopenharmony_ci/* 297462306a36Sopenharmony_ci * Copy a piece of shared memory of specified type to a buffer . 297562306a36Sopenharmony_ci * SHM 'offset' needs to be an even address and 297662306a36Sopenharmony_ci * Buffer length 'len' must be an even number of bytes 297762306a36Sopenharmony_ci * 'sel' selects the type of memory 297862306a36Sopenharmony_ci */ 297962306a36Sopenharmony_civoid 298062306a36Sopenharmony_cibrcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf, 298162306a36Sopenharmony_ci int len, u32 sel) 298262306a36Sopenharmony_ci{ 298362306a36Sopenharmony_ci u16 v; 298462306a36Sopenharmony_ci u8 *p = (u8 *) buf; 298562306a36Sopenharmony_ci int i; 298662306a36Sopenharmony_ci 298762306a36Sopenharmony_ci if (len <= 0 || (offset & 1) || (len & 1)) 298862306a36Sopenharmony_ci return; 298962306a36Sopenharmony_ci 299062306a36Sopenharmony_ci for (i = 0; i < len; i += 2) { 299162306a36Sopenharmony_ci v = brcms_b_read_objmem(wlc_hw, offset + i, sel); 299262306a36Sopenharmony_ci p[i] = v & 0xFF; 299362306a36Sopenharmony_ci p[i + 1] = (v >> 8) & 0xFF; 299462306a36Sopenharmony_ci } 299562306a36Sopenharmony_ci} 299662306a36Sopenharmony_ci 299762306a36Sopenharmony_ci/* Copy a buffer to shared memory. 299862306a36Sopenharmony_ci * SHM 'offset' needs to be an even address and 299962306a36Sopenharmony_ci * Buffer length 'len' must be an even number of bytes 300062306a36Sopenharmony_ci */ 300162306a36Sopenharmony_cistatic void brcms_c_copyto_shm(struct brcms_c_info *wlc, uint offset, 300262306a36Sopenharmony_ci const void *buf, int len) 300362306a36Sopenharmony_ci{ 300462306a36Sopenharmony_ci brcms_b_copyto_objmem(wlc->hw, offset, buf, len, OBJADDR_SHM_SEL); 300562306a36Sopenharmony_ci} 300662306a36Sopenharmony_ci 300762306a36Sopenharmony_cistatic void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, 300862306a36Sopenharmony_ci u16 SRL, u16 LRL) 300962306a36Sopenharmony_ci{ 301062306a36Sopenharmony_ci wlc_hw->SRL = SRL; 301162306a36Sopenharmony_ci wlc_hw->LRL = LRL; 301262306a36Sopenharmony_ci 301362306a36Sopenharmony_ci /* write retry limit to SCR, shouldn't need to suspend */ 301462306a36Sopenharmony_ci if (wlc_hw->up) { 301562306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), 301662306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); 301762306a36Sopenharmony_ci (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); 301862306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->SRL); 301962306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objaddr), 302062306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); 302162306a36Sopenharmony_ci (void)bcma_read32(wlc_hw->d11core, D11REGOFFS(objaddr)); 302262306a36Sopenharmony_ci bcma_write32(wlc_hw->d11core, D11REGOFFS(objdata), wlc_hw->LRL); 302362306a36Sopenharmony_ci } 302462306a36Sopenharmony_ci} 302562306a36Sopenharmony_ci 302662306a36Sopenharmony_cistatic void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, u32 req_bit) 302762306a36Sopenharmony_ci{ 302862306a36Sopenharmony_ci if (set) { 302962306a36Sopenharmony_ci if (mboolisset(wlc_hw->pllreq, req_bit)) 303062306a36Sopenharmony_ci return; 303162306a36Sopenharmony_ci 303262306a36Sopenharmony_ci mboolset(wlc_hw->pllreq, req_bit); 303362306a36Sopenharmony_ci 303462306a36Sopenharmony_ci if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { 303562306a36Sopenharmony_ci if (!wlc_hw->sbclk) 303662306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, ON); 303762306a36Sopenharmony_ci } 303862306a36Sopenharmony_ci } else { 303962306a36Sopenharmony_ci if (!mboolisset(wlc_hw->pllreq, req_bit)) 304062306a36Sopenharmony_ci return; 304162306a36Sopenharmony_ci 304262306a36Sopenharmony_ci mboolclr(wlc_hw->pllreq, req_bit); 304362306a36Sopenharmony_ci 304462306a36Sopenharmony_ci if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) { 304562306a36Sopenharmony_ci if (wlc_hw->sbclk) 304662306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, OFF); 304762306a36Sopenharmony_ci } 304862306a36Sopenharmony_ci } 304962306a36Sopenharmony_ci} 305062306a36Sopenharmony_ci 305162306a36Sopenharmony_cistatic void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail) 305262306a36Sopenharmony_ci{ 305362306a36Sopenharmony_ci wlc_hw->antsel_avail = antsel_avail; 305462306a36Sopenharmony_ci} 305562306a36Sopenharmony_ci 305662306a36Sopenharmony_ci/* 305762306a36Sopenharmony_ci * conditions under which the PM bit should be set in outgoing frames 305862306a36Sopenharmony_ci * and STAY_AWAKE is meaningful 305962306a36Sopenharmony_ci */ 306062306a36Sopenharmony_cistatic bool brcms_c_ps_allowed(struct brcms_c_info *wlc) 306162306a36Sopenharmony_ci{ 306262306a36Sopenharmony_ci /* not supporting PS so always return false for now */ 306362306a36Sopenharmony_ci return false; 306462306a36Sopenharmony_ci} 306562306a36Sopenharmony_ci 306662306a36Sopenharmony_cistatic void brcms_c_statsupd(struct brcms_c_info *wlc) 306762306a36Sopenharmony_ci{ 306862306a36Sopenharmony_ci int i; 306962306a36Sopenharmony_ci struct macstat *macstats; 307062306a36Sopenharmony_ci#ifdef DEBUG 307162306a36Sopenharmony_ci u16 delta; 307262306a36Sopenharmony_ci u16 rxf0ovfl; 307362306a36Sopenharmony_ci u16 txfunfl[NFIFO]; 307462306a36Sopenharmony_ci#endif /* DEBUG */ 307562306a36Sopenharmony_ci 307662306a36Sopenharmony_ci /* if driver down, make no sense to update stats */ 307762306a36Sopenharmony_ci if (!wlc->pub->up) 307862306a36Sopenharmony_ci return; 307962306a36Sopenharmony_ci 308062306a36Sopenharmony_ci macstats = wlc->core->macstat_snapshot; 308162306a36Sopenharmony_ci 308262306a36Sopenharmony_ci#ifdef DEBUG 308362306a36Sopenharmony_ci /* save last rx fifo 0 overflow count */ 308462306a36Sopenharmony_ci rxf0ovfl = macstats->rxf0ovfl; 308562306a36Sopenharmony_ci 308662306a36Sopenharmony_ci /* save last tx fifo underflow count */ 308762306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) 308862306a36Sopenharmony_ci txfunfl[i] = macstats->txfunfl[i]; 308962306a36Sopenharmony_ci#endif /* DEBUG */ 309062306a36Sopenharmony_ci 309162306a36Sopenharmony_ci /* Read mac stats from contiguous shared memory */ 309262306a36Sopenharmony_ci brcms_b_copyfrom_objmem(wlc->hw, M_UCODE_MACSTAT, macstats, 309362306a36Sopenharmony_ci sizeof(*macstats), OBJADDR_SHM_SEL); 309462306a36Sopenharmony_ci 309562306a36Sopenharmony_ci#ifdef DEBUG 309662306a36Sopenharmony_ci /* check for rx fifo 0 overflow */ 309762306a36Sopenharmony_ci delta = (u16)(macstats->rxf0ovfl - rxf0ovfl); 309862306a36Sopenharmony_ci if (delta) 309962306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %u rx fifo 0 overflows!\n", 310062306a36Sopenharmony_ci wlc->pub->unit, delta); 310162306a36Sopenharmony_ci 310262306a36Sopenharmony_ci /* check for tx fifo underflows */ 310362306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) { 310462306a36Sopenharmony_ci delta = macstats->txfunfl[i] - txfunfl[i]; 310562306a36Sopenharmony_ci if (delta) 310662306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 310762306a36Sopenharmony_ci "wl%d: %u tx fifo %d underflows!\n", 310862306a36Sopenharmony_ci wlc->pub->unit, delta, i); 310962306a36Sopenharmony_ci } 311062306a36Sopenharmony_ci#endif /* DEBUG */ 311162306a36Sopenharmony_ci 311262306a36Sopenharmony_ci /* merge counters from dma module */ 311362306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) { 311462306a36Sopenharmony_ci if (wlc->hw->di[i]) 311562306a36Sopenharmony_ci dma_counterreset(wlc->hw->di[i]); 311662306a36Sopenharmony_ci } 311762306a36Sopenharmony_ci} 311862306a36Sopenharmony_ci 311962306a36Sopenharmony_cistatic void brcms_b_reset(struct brcms_hardware *wlc_hw) 312062306a36Sopenharmony_ci{ 312162306a36Sopenharmony_ci /* reset the core */ 312262306a36Sopenharmony_ci if (!brcms_deviceremoved(wlc_hw->wlc)) 312362306a36Sopenharmony_ci brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); 312462306a36Sopenharmony_ci 312562306a36Sopenharmony_ci /* purge the dma rings */ 312662306a36Sopenharmony_ci brcms_c_flushqueues(wlc_hw->wlc); 312762306a36Sopenharmony_ci} 312862306a36Sopenharmony_ci 312962306a36Sopenharmony_civoid brcms_c_reset(struct brcms_c_info *wlc) 313062306a36Sopenharmony_ci{ 313162306a36Sopenharmony_ci brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); 313262306a36Sopenharmony_ci 313362306a36Sopenharmony_ci /* slurp up hw mac counters before core reset */ 313462306a36Sopenharmony_ci brcms_c_statsupd(wlc); 313562306a36Sopenharmony_ci 313662306a36Sopenharmony_ci /* reset our snapshot of macstat counters */ 313762306a36Sopenharmony_ci memset(wlc->core->macstat_snapshot, 0, sizeof(struct macstat)); 313862306a36Sopenharmony_ci 313962306a36Sopenharmony_ci brcms_b_reset(wlc->hw); 314062306a36Sopenharmony_ci} 314162306a36Sopenharmony_ci 314262306a36Sopenharmony_civoid brcms_c_init_scb(struct scb *scb) 314362306a36Sopenharmony_ci{ 314462306a36Sopenharmony_ci int i; 314562306a36Sopenharmony_ci 314662306a36Sopenharmony_ci memset(scb, 0, sizeof(struct scb)); 314762306a36Sopenharmony_ci scb->flags = SCB_WMECAP | SCB_HTCAP; 314862306a36Sopenharmony_ci for (i = 0; i < NUMPRIO; i++) { 314962306a36Sopenharmony_ci scb->seqnum[i] = 0; 315062306a36Sopenharmony_ci } 315162306a36Sopenharmony_ci 315262306a36Sopenharmony_ci scb->magic = SCB_MAGIC; 315362306a36Sopenharmony_ci} 315462306a36Sopenharmony_ci 315562306a36Sopenharmony_ci/* d11 core init 315662306a36Sopenharmony_ci * reset PSM 315762306a36Sopenharmony_ci * download ucode/PCM 315862306a36Sopenharmony_ci * let ucode run to suspended 315962306a36Sopenharmony_ci * download ucode inits 316062306a36Sopenharmony_ci * config other core registers 316162306a36Sopenharmony_ci * init dma 316262306a36Sopenharmony_ci */ 316362306a36Sopenharmony_cistatic void brcms_b_coreinit(struct brcms_c_info *wlc) 316462306a36Sopenharmony_ci{ 316562306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 316662306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 316762306a36Sopenharmony_ci u32 bcnint_us; 316862306a36Sopenharmony_ci uint i = 0; 316962306a36Sopenharmony_ci bool fifosz_fixup = false; 317062306a36Sopenharmony_ci int err = 0; 317162306a36Sopenharmony_ci u16 buf[NFIFO]; 317262306a36Sopenharmony_ci struct brcms_ucode *ucode = &wlc_hw->wlc->wl->ucode; 317362306a36Sopenharmony_ci 317462306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d: core init\n", wlc_hw->unit); 317562306a36Sopenharmony_ci 317662306a36Sopenharmony_ci /* reset PSM */ 317762306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE)); 317862306a36Sopenharmony_ci 317962306a36Sopenharmony_ci brcms_ucode_download(wlc_hw); 318062306a36Sopenharmony_ci /* 318162306a36Sopenharmony_ci * FIFOSZ fixup. driver wants to controls the fifo allocation. 318262306a36Sopenharmony_ci */ 318362306a36Sopenharmony_ci fifosz_fixup = true; 318462306a36Sopenharmony_ci 318562306a36Sopenharmony_ci /* let the PSM run to the suspended state, set mode to BSS STA */ 318662306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintstatus), -1); 318762306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, ~0, 318862306a36Sopenharmony_ci (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE)); 318962306a36Sopenharmony_ci 319062306a36Sopenharmony_ci /* wait for ucode to self-suspend after auto-init */ 319162306a36Sopenharmony_ci SPINWAIT(((bcma_read32(core, D11REGOFFS(macintstatus)) & 319262306a36Sopenharmony_ci MI_MACSSPNDD) == 0), 1000 * 1000); 319362306a36Sopenharmony_ci if ((bcma_read32(core, D11REGOFFS(macintstatus)) & MI_MACSSPNDD) == 0) 319462306a36Sopenharmony_ci brcms_err(core, "wl%d: wlc_coreinit: ucode did not self-" 319562306a36Sopenharmony_ci "suspend!\n", wlc_hw->unit); 319662306a36Sopenharmony_ci 319762306a36Sopenharmony_ci brcms_c_gpio_init(wlc); 319862306a36Sopenharmony_ci 319962306a36Sopenharmony_ci bcma_aread32(core, BCMA_IOST); 320062306a36Sopenharmony_ci 320162306a36Sopenharmony_ci if (D11REV_IS(wlc_hw->corerev, 17) || D11REV_IS(wlc_hw->corerev, 23)) { 320262306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band)) 320362306a36Sopenharmony_ci brcms_c_write_inits(wlc_hw, ucode->d11n0initvals16); 320462306a36Sopenharmony_ci else 320562306a36Sopenharmony_ci brcms_err(core, "%s: wl%d: unsupported phy in corerev" 320662306a36Sopenharmony_ci " %d\n", __func__, wlc_hw->unit, 320762306a36Sopenharmony_ci wlc_hw->corerev); 320862306a36Sopenharmony_ci } else if (D11REV_IS(wlc_hw->corerev, 24)) { 320962306a36Sopenharmony_ci if (BRCMS_ISLCNPHY(wlc_hw->band)) 321062306a36Sopenharmony_ci brcms_c_write_inits(wlc_hw, ucode->d11lcn0initvals24); 321162306a36Sopenharmony_ci else 321262306a36Sopenharmony_ci brcms_err(core, "%s: wl%d: unsupported phy in corerev" 321362306a36Sopenharmony_ci " %d\n", __func__, wlc_hw->unit, 321462306a36Sopenharmony_ci wlc_hw->corerev); 321562306a36Sopenharmony_ci } else { 321662306a36Sopenharmony_ci brcms_err(core, "%s: wl%d: unsupported corerev %d\n", 321762306a36Sopenharmony_ci __func__, wlc_hw->unit, wlc_hw->corerev); 321862306a36Sopenharmony_ci } 321962306a36Sopenharmony_ci 322062306a36Sopenharmony_ci /* For old ucode, txfifo sizes needs to be modified(increased) */ 322162306a36Sopenharmony_ci if (fifosz_fixup) 322262306a36Sopenharmony_ci brcms_b_corerev_fifofixup(wlc_hw); 322362306a36Sopenharmony_ci 322462306a36Sopenharmony_ci /* check txfifo allocations match between ucode and driver */ 322562306a36Sopenharmony_ci buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0); 322662306a36Sopenharmony_ci if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) { 322762306a36Sopenharmony_ci i = TX_AC_BE_FIFO; 322862306a36Sopenharmony_ci err = -1; 322962306a36Sopenharmony_ci } 323062306a36Sopenharmony_ci buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1); 323162306a36Sopenharmony_ci if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) { 323262306a36Sopenharmony_ci i = TX_AC_VI_FIFO; 323362306a36Sopenharmony_ci err = -1; 323462306a36Sopenharmony_ci } 323562306a36Sopenharmony_ci buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2); 323662306a36Sopenharmony_ci buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff; 323762306a36Sopenharmony_ci buf[TX_AC_BK_FIFO] &= 0xff; 323862306a36Sopenharmony_ci if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) { 323962306a36Sopenharmony_ci i = TX_AC_BK_FIFO; 324062306a36Sopenharmony_ci err = -1; 324162306a36Sopenharmony_ci } 324262306a36Sopenharmony_ci if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) { 324362306a36Sopenharmony_ci i = TX_AC_VO_FIFO; 324462306a36Sopenharmony_ci err = -1; 324562306a36Sopenharmony_ci } 324662306a36Sopenharmony_ci buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3); 324762306a36Sopenharmony_ci buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff; 324862306a36Sopenharmony_ci buf[TX_BCMC_FIFO] &= 0xff; 324962306a36Sopenharmony_ci if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) { 325062306a36Sopenharmony_ci i = TX_BCMC_FIFO; 325162306a36Sopenharmony_ci err = -1; 325262306a36Sopenharmony_ci } 325362306a36Sopenharmony_ci if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) { 325462306a36Sopenharmony_ci i = TX_ATIM_FIFO; 325562306a36Sopenharmony_ci err = -1; 325662306a36Sopenharmony_ci } 325762306a36Sopenharmony_ci if (err != 0) 325862306a36Sopenharmony_ci brcms_err(core, "wlc_coreinit: txfifo mismatch: ucode size %d" 325962306a36Sopenharmony_ci " driver size %d index %d\n", buf[i], 326062306a36Sopenharmony_ci wlc_hw->xmtfifo_sz[i], i); 326162306a36Sopenharmony_ci 326262306a36Sopenharmony_ci /* make sure we can still talk to the mac */ 326362306a36Sopenharmony_ci WARN_ON(bcma_read32(core, D11REGOFFS(maccontrol)) == 0xffffffff); 326462306a36Sopenharmony_ci 326562306a36Sopenharmony_ci /* band-specific inits done by wlc_bsinit() */ 326662306a36Sopenharmony_ci 326762306a36Sopenharmony_ci /* Set up frame burst size and antenna swap threshold init values */ 326862306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST); 326962306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT); 327062306a36Sopenharmony_ci 327162306a36Sopenharmony_ci /* enable one rx interrupt per received frame */ 327262306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(intrcvlazy[0]), (1 << IRL_FC_SHIFT)); 327362306a36Sopenharmony_ci 327462306a36Sopenharmony_ci /* set the station mode (BSS STA) */ 327562306a36Sopenharmony_ci brcms_b_mctrl(wlc_hw, 327662306a36Sopenharmony_ci (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP), 327762306a36Sopenharmony_ci (MCTL_INFRA | MCTL_DISCARD_PMQ)); 327862306a36Sopenharmony_ci 327962306a36Sopenharmony_ci /* set up Beacon interval */ 328062306a36Sopenharmony_ci bcnint_us = 0x8000 << 10; 328162306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tsf_cfprep), 328262306a36Sopenharmony_ci (bcnint_us << CFPREP_CBI_SHIFT)); 328362306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tsf_cfpstart), bcnint_us); 328462306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintstatus), MI_GP1); 328562306a36Sopenharmony_ci 328662306a36Sopenharmony_ci /* write interrupt mask */ 328762306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(intctrlregs[RX_FIFO].intmask), 328862306a36Sopenharmony_ci DEF_RXINTMASK); 328962306a36Sopenharmony_ci 329062306a36Sopenharmony_ci /* allow the MAC to control the PHY clock (dynamic on/off) */ 329162306a36Sopenharmony_ci brcms_b_macphyclk_set(wlc_hw, ON); 329262306a36Sopenharmony_ci 329362306a36Sopenharmony_ci /* program dynamic clock control fast powerup delay register */ 329462306a36Sopenharmony_ci wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih); 329562306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(scc_fastpwrup_dly), wlc->fastpwrup_dly); 329662306a36Sopenharmony_ci 329762306a36Sopenharmony_ci /* tell the ucode the corerev */ 329862306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev); 329962306a36Sopenharmony_ci 330062306a36Sopenharmony_ci /* tell the ucode MAC capabilities */ 330162306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L, 330262306a36Sopenharmony_ci (u16) (wlc_hw->machwcap & 0xffff)); 330362306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H, 330462306a36Sopenharmony_ci (u16) ((wlc_hw-> 330562306a36Sopenharmony_ci machwcap >> 16) & 0xffff)); 330662306a36Sopenharmony_ci 330762306a36Sopenharmony_ci /* write retry limits to SCR, this done after PSM init */ 330862306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), 330962306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_SRC_LMT); 331062306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 331162306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), wlc_hw->SRL); 331262306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objaddr), 331362306a36Sopenharmony_ci OBJADDR_SCR_SEL | S_DOT11_LRC_LMT); 331462306a36Sopenharmony_ci (void)bcma_read32(core, D11REGOFFS(objaddr)); 331562306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(objdata), wlc_hw->LRL); 331662306a36Sopenharmony_ci 331762306a36Sopenharmony_ci /* write rate fallback retry limits */ 331862306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL); 331962306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL); 332062306a36Sopenharmony_ci 332162306a36Sopenharmony_ci bcma_mask16(core, D11REGOFFS(ifs_ctl), 0x0FFF); 332262306a36Sopenharmony_ci bcma_write16(core, D11REGOFFS(ifs_aifsn), EDCF_AIFSN_MIN); 332362306a36Sopenharmony_ci 332462306a36Sopenharmony_ci /* init the tx dma engines */ 332562306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) { 332662306a36Sopenharmony_ci if (wlc_hw->di[i]) 332762306a36Sopenharmony_ci dma_txinit(wlc_hw->di[i]); 332862306a36Sopenharmony_ci } 332962306a36Sopenharmony_ci 333062306a36Sopenharmony_ci /* init the rx dma engine(s) and post receive buffers */ 333162306a36Sopenharmony_ci dma_rxinit(wlc_hw->di[RX_FIFO]); 333262306a36Sopenharmony_ci dma_rxfill(wlc_hw->di[RX_FIFO]); 333362306a36Sopenharmony_ci} 333462306a36Sopenharmony_ci 333562306a36Sopenharmony_cistatic void brcms_b_init(struct brcms_hardware *wlc_hw, u16 chanspec) 333662306a36Sopenharmony_ci{ 333762306a36Sopenharmony_ci u32 macintmask; 333862306a36Sopenharmony_ci bool fastclk; 333962306a36Sopenharmony_ci struct brcms_c_info *wlc = wlc_hw->wlc; 334062306a36Sopenharmony_ci 334162306a36Sopenharmony_ci /* request FAST clock if not on */ 334262306a36Sopenharmony_ci fastclk = wlc_hw->forcefastclk; 334362306a36Sopenharmony_ci if (!fastclk) 334462306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 334562306a36Sopenharmony_ci 334662306a36Sopenharmony_ci /* disable interrupts */ 334762306a36Sopenharmony_ci macintmask = brcms_intrsoff(wlc->wl); 334862306a36Sopenharmony_ci 334962306a36Sopenharmony_ci /* set up the specified band and chanspec */ 335062306a36Sopenharmony_ci brcms_c_setxband(wlc_hw, chspec_bandunit(chanspec)); 335162306a36Sopenharmony_ci wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); 335262306a36Sopenharmony_ci 335362306a36Sopenharmony_ci /* do one-time phy inits and calibration */ 335462306a36Sopenharmony_ci wlc_phy_cal_init(wlc_hw->band->pi); 335562306a36Sopenharmony_ci 335662306a36Sopenharmony_ci /* core-specific initialization */ 335762306a36Sopenharmony_ci brcms_b_coreinit(wlc); 335862306a36Sopenharmony_ci 335962306a36Sopenharmony_ci /* band-specific inits */ 336062306a36Sopenharmony_ci brcms_b_bsinit(wlc, chanspec); 336162306a36Sopenharmony_ci 336262306a36Sopenharmony_ci /* restore macintmask */ 336362306a36Sopenharmony_ci brcms_intrsrestore(wlc->wl, macintmask); 336462306a36Sopenharmony_ci 336562306a36Sopenharmony_ci /* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac 336662306a36Sopenharmony_ci * is suspended and brcms_c_enable_mac() will clear this override bit. 336762306a36Sopenharmony_ci */ 336862306a36Sopenharmony_ci mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND); 336962306a36Sopenharmony_ci 337062306a36Sopenharmony_ci /* 337162306a36Sopenharmony_ci * initialize mac_suspend_depth to 1 to match ucode 337262306a36Sopenharmony_ci * initial suspended state 337362306a36Sopenharmony_ci */ 337462306a36Sopenharmony_ci wlc_hw->mac_suspend_depth = 1; 337562306a36Sopenharmony_ci 337662306a36Sopenharmony_ci /* restore the clk */ 337762306a36Sopenharmony_ci if (!fastclk) 337862306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); 337962306a36Sopenharmony_ci} 338062306a36Sopenharmony_ci 338162306a36Sopenharmony_cistatic void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc, 338262306a36Sopenharmony_ci u16 chanspec) 338362306a36Sopenharmony_ci{ 338462306a36Sopenharmony_ci /* Save our copy of the chanspec */ 338562306a36Sopenharmony_ci wlc->chanspec = chanspec; 338662306a36Sopenharmony_ci 338762306a36Sopenharmony_ci /* Set the chanspec and power limits for this locale */ 338862306a36Sopenharmony_ci brcms_c_channel_set_chanspec(wlc->cmi, chanspec, BRCMS_TXPWR_MAX); 338962306a36Sopenharmony_ci 339062306a36Sopenharmony_ci if (wlc->stf->ss_algosel_auto) 339162306a36Sopenharmony_ci brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel, 339262306a36Sopenharmony_ci chanspec); 339362306a36Sopenharmony_ci 339462306a36Sopenharmony_ci brcms_c_stf_ss_update(wlc, wlc->band); 339562306a36Sopenharmony_ci} 339662306a36Sopenharmony_ci 339762306a36Sopenharmony_cistatic void 339862306a36Sopenharmony_cibrcms_default_rateset(struct brcms_c_info *wlc, struct brcms_c_rateset *rs) 339962306a36Sopenharmony_ci{ 340062306a36Sopenharmony_ci brcms_c_rateset_default(rs, NULL, wlc->band->phytype, 340162306a36Sopenharmony_ci wlc->band->bandtype, false, BRCMS_RATE_MASK_FULL, 340262306a36Sopenharmony_ci (bool) (wlc->pub->_n_enab & SUPPORT_11N), 340362306a36Sopenharmony_ci brcms_chspec_bw(wlc->default_bss->chanspec), 340462306a36Sopenharmony_ci wlc->stf->txstreams); 340562306a36Sopenharmony_ci} 340662306a36Sopenharmony_ci 340762306a36Sopenharmony_ci/* derive wlc->band->basic_rate[] table from 'rateset' */ 340862306a36Sopenharmony_cistatic void brcms_c_rate_lookup_init(struct brcms_c_info *wlc, 340962306a36Sopenharmony_ci struct brcms_c_rateset *rateset) 341062306a36Sopenharmony_ci{ 341162306a36Sopenharmony_ci u8 rate; 341262306a36Sopenharmony_ci u8 mandatory; 341362306a36Sopenharmony_ci u8 cck_basic = 0; 341462306a36Sopenharmony_ci u8 ofdm_basic = 0; 341562306a36Sopenharmony_ci u8 *br = wlc->band->basic_rate; 341662306a36Sopenharmony_ci uint i; 341762306a36Sopenharmony_ci 341862306a36Sopenharmony_ci /* incoming rates are in 500kbps units as in 802.11 Supported Rates */ 341962306a36Sopenharmony_ci memset(br, 0, BRCM_MAXRATE + 1); 342062306a36Sopenharmony_ci 342162306a36Sopenharmony_ci /* For each basic rate in the rates list, make an entry in the 342262306a36Sopenharmony_ci * best basic lookup. 342362306a36Sopenharmony_ci */ 342462306a36Sopenharmony_ci for (i = 0; i < rateset->count; i++) { 342562306a36Sopenharmony_ci /* only make an entry for a basic rate */ 342662306a36Sopenharmony_ci if (!(rateset->rates[i] & BRCMS_RATE_FLAG)) 342762306a36Sopenharmony_ci continue; 342862306a36Sopenharmony_ci 342962306a36Sopenharmony_ci /* mask off basic bit */ 343062306a36Sopenharmony_ci rate = (rateset->rates[i] & BRCMS_RATE_MASK); 343162306a36Sopenharmony_ci 343262306a36Sopenharmony_ci if (rate > BRCM_MAXRATE) { 343362306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "brcms_c_rate_lookup_init: " 343462306a36Sopenharmony_ci "invalid rate 0x%X in rate set\n", 343562306a36Sopenharmony_ci rateset->rates[i]); 343662306a36Sopenharmony_ci continue; 343762306a36Sopenharmony_ci } 343862306a36Sopenharmony_ci 343962306a36Sopenharmony_ci br[rate] = rate; 344062306a36Sopenharmony_ci } 344162306a36Sopenharmony_ci 344262306a36Sopenharmony_ci /* The rate lookup table now has non-zero entries for each 344362306a36Sopenharmony_ci * basic rate, equal to the basic rate: br[basicN] = basicN 344462306a36Sopenharmony_ci * 344562306a36Sopenharmony_ci * To look up the best basic rate corresponding to any 344662306a36Sopenharmony_ci * particular rate, code can use the basic_rate table 344762306a36Sopenharmony_ci * like this 344862306a36Sopenharmony_ci * 344962306a36Sopenharmony_ci * basic_rate = wlc->band->basic_rate[tx_rate] 345062306a36Sopenharmony_ci * 345162306a36Sopenharmony_ci * Make sure there is a best basic rate entry for 345262306a36Sopenharmony_ci * every rate by walking up the table from low rates 345362306a36Sopenharmony_ci * to high, filling in holes in the lookup table 345462306a36Sopenharmony_ci */ 345562306a36Sopenharmony_ci 345662306a36Sopenharmony_ci for (i = 0; i < wlc->band->hw_rateset.count; i++) { 345762306a36Sopenharmony_ci rate = wlc->band->hw_rateset.rates[i]; 345862306a36Sopenharmony_ci 345962306a36Sopenharmony_ci if (br[rate] != 0) { 346062306a36Sopenharmony_ci /* This rate is a basic rate. 346162306a36Sopenharmony_ci * Keep track of the best basic rate so far by 346262306a36Sopenharmony_ci * modulation type. 346362306a36Sopenharmony_ci */ 346462306a36Sopenharmony_ci if (is_ofdm_rate(rate)) 346562306a36Sopenharmony_ci ofdm_basic = rate; 346662306a36Sopenharmony_ci else 346762306a36Sopenharmony_ci cck_basic = rate; 346862306a36Sopenharmony_ci 346962306a36Sopenharmony_ci continue; 347062306a36Sopenharmony_ci } 347162306a36Sopenharmony_ci 347262306a36Sopenharmony_ci /* This rate is not a basic rate so figure out the 347362306a36Sopenharmony_ci * best basic rate less than this rate and fill in 347462306a36Sopenharmony_ci * the hole in the table 347562306a36Sopenharmony_ci */ 347662306a36Sopenharmony_ci 347762306a36Sopenharmony_ci br[rate] = is_ofdm_rate(rate) ? ofdm_basic : cck_basic; 347862306a36Sopenharmony_ci 347962306a36Sopenharmony_ci if (br[rate] != 0) 348062306a36Sopenharmony_ci continue; 348162306a36Sopenharmony_ci 348262306a36Sopenharmony_ci if (is_ofdm_rate(rate)) { 348362306a36Sopenharmony_ci /* 348462306a36Sopenharmony_ci * In 11g and 11a, the OFDM mandatory rates 348562306a36Sopenharmony_ci * are 6, 12, and 24 Mbps 348662306a36Sopenharmony_ci */ 348762306a36Sopenharmony_ci if (rate >= BRCM_RATE_24M) 348862306a36Sopenharmony_ci mandatory = BRCM_RATE_24M; 348962306a36Sopenharmony_ci else if (rate >= BRCM_RATE_12M) 349062306a36Sopenharmony_ci mandatory = BRCM_RATE_12M; 349162306a36Sopenharmony_ci else 349262306a36Sopenharmony_ci mandatory = BRCM_RATE_6M; 349362306a36Sopenharmony_ci } else { 349462306a36Sopenharmony_ci /* In 11b, all CCK rates are mandatory 1 - 11 Mbps */ 349562306a36Sopenharmony_ci mandatory = rate; 349662306a36Sopenharmony_ci } 349762306a36Sopenharmony_ci 349862306a36Sopenharmony_ci br[rate] = mandatory; 349962306a36Sopenharmony_ci } 350062306a36Sopenharmony_ci} 350162306a36Sopenharmony_ci 350262306a36Sopenharmony_cistatic void brcms_c_bandinit_ordered(struct brcms_c_info *wlc, 350362306a36Sopenharmony_ci u16 chanspec) 350462306a36Sopenharmony_ci{ 350562306a36Sopenharmony_ci struct brcms_c_rateset default_rateset; 350662306a36Sopenharmony_ci uint parkband; 350762306a36Sopenharmony_ci uint i, band_order[2]; 350862306a36Sopenharmony_ci 350962306a36Sopenharmony_ci /* 351062306a36Sopenharmony_ci * We might have been bandlocked during down and the chip 351162306a36Sopenharmony_ci * power-cycled (hibernate). Figure out the right band to park on 351262306a36Sopenharmony_ci */ 351362306a36Sopenharmony_ci if (wlc->bandlocked || wlc->pub->_nbands == 1) { 351462306a36Sopenharmony_ci /* updated in brcms_c_bandlock() */ 351562306a36Sopenharmony_ci parkband = wlc->band->bandunit; 351662306a36Sopenharmony_ci band_order[0] = band_order[1] = parkband; 351762306a36Sopenharmony_ci } else { 351862306a36Sopenharmony_ci /* park on the band of the specified chanspec */ 351962306a36Sopenharmony_ci parkband = chspec_bandunit(chanspec); 352062306a36Sopenharmony_ci 352162306a36Sopenharmony_ci /* order so that parkband initialize last */ 352262306a36Sopenharmony_ci band_order[0] = parkband ^ 1; 352362306a36Sopenharmony_ci band_order[1] = parkband; 352462306a36Sopenharmony_ci } 352562306a36Sopenharmony_ci 352662306a36Sopenharmony_ci /* make each band operational, software state init */ 352762306a36Sopenharmony_ci for (i = 0; i < wlc->pub->_nbands; i++) { 352862306a36Sopenharmony_ci uint j = band_order[i]; 352962306a36Sopenharmony_ci 353062306a36Sopenharmony_ci wlc->band = wlc->bandstate[j]; 353162306a36Sopenharmony_ci 353262306a36Sopenharmony_ci brcms_default_rateset(wlc, &default_rateset); 353362306a36Sopenharmony_ci 353462306a36Sopenharmony_ci /* fill in hw_rate */ 353562306a36Sopenharmony_ci brcms_c_rateset_filter(&default_rateset, &wlc->band->hw_rateset, 353662306a36Sopenharmony_ci false, BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, 353762306a36Sopenharmony_ci (bool) (wlc->pub->_n_enab & SUPPORT_11N)); 353862306a36Sopenharmony_ci 353962306a36Sopenharmony_ci /* init basic rate lookup */ 354062306a36Sopenharmony_ci brcms_c_rate_lookup_init(wlc, &default_rateset); 354162306a36Sopenharmony_ci } 354262306a36Sopenharmony_ci 354362306a36Sopenharmony_ci /* sync up phy/radio chanspec */ 354462306a36Sopenharmony_ci brcms_c_set_phy_chanspec(wlc, chanspec); 354562306a36Sopenharmony_ci} 354662306a36Sopenharmony_ci 354762306a36Sopenharmony_ci/* 354862306a36Sopenharmony_ci * Set or clear filtering related maccontrol bits based on 354962306a36Sopenharmony_ci * specified filter flags 355062306a36Sopenharmony_ci */ 355162306a36Sopenharmony_civoid brcms_c_mac_promisc(struct brcms_c_info *wlc, uint filter_flags) 355262306a36Sopenharmony_ci{ 355362306a36Sopenharmony_ci u32 promisc_bits = 0; 355462306a36Sopenharmony_ci 355562306a36Sopenharmony_ci wlc->filter_flags = filter_flags; 355662306a36Sopenharmony_ci 355762306a36Sopenharmony_ci if (filter_flags & FIF_OTHER_BSS) 355862306a36Sopenharmony_ci promisc_bits |= MCTL_PROMISC; 355962306a36Sopenharmony_ci 356062306a36Sopenharmony_ci if (filter_flags & FIF_BCN_PRBRESP_PROMISC) 356162306a36Sopenharmony_ci promisc_bits |= MCTL_BCNS_PROMISC; 356262306a36Sopenharmony_ci 356362306a36Sopenharmony_ci if (filter_flags & FIF_FCSFAIL) 356462306a36Sopenharmony_ci promisc_bits |= MCTL_KEEPBADFCS; 356562306a36Sopenharmony_ci 356662306a36Sopenharmony_ci if (filter_flags & (FIF_CONTROL | FIF_PSPOLL)) 356762306a36Sopenharmony_ci promisc_bits |= MCTL_KEEPCONTROL; 356862306a36Sopenharmony_ci 356962306a36Sopenharmony_ci brcms_b_mctrl(wlc->hw, 357062306a36Sopenharmony_ci MCTL_PROMISC | MCTL_BCNS_PROMISC | 357162306a36Sopenharmony_ci MCTL_KEEPCONTROL | MCTL_KEEPBADFCS, 357262306a36Sopenharmony_ci promisc_bits); 357362306a36Sopenharmony_ci} 357462306a36Sopenharmony_ci 357562306a36Sopenharmony_ci/* 357662306a36Sopenharmony_ci * ucode, hwmac update 357762306a36Sopenharmony_ci * Channel dependent updates for ucode and hw 357862306a36Sopenharmony_ci */ 357962306a36Sopenharmony_cistatic void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc) 358062306a36Sopenharmony_ci{ 358162306a36Sopenharmony_ci /* enable or disable any active IBSSs depending on whether or not 358262306a36Sopenharmony_ci * we are on the home channel 358362306a36Sopenharmony_ci */ 358462306a36Sopenharmony_ci if (wlc->home_chanspec == wlc_phy_chanspec_get(wlc->band->pi)) { 358562306a36Sopenharmony_ci if (wlc->pub->associated) { 358662306a36Sopenharmony_ci /* 358762306a36Sopenharmony_ci * BMAC_NOTE: This is something that should be fixed 358862306a36Sopenharmony_ci * in ucode inits. I think that the ucode inits set 358962306a36Sopenharmony_ci * up the bcn templates and shm values with a bogus 359062306a36Sopenharmony_ci * beacon. This should not be done in the inits. If 359162306a36Sopenharmony_ci * ucode needs to set up a beacon for testing, the 359262306a36Sopenharmony_ci * test routines should write it down, not expect the 359362306a36Sopenharmony_ci * inits to populate a bogus beacon. 359462306a36Sopenharmony_ci */ 359562306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) 359662306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, 359762306a36Sopenharmony_ci M_BCN_TXTSF_OFFSET, 0); 359862306a36Sopenharmony_ci } 359962306a36Sopenharmony_ci } else { 360062306a36Sopenharmony_ci /* disable an active IBSS if we are not on the home channel */ 360162306a36Sopenharmony_ci } 360262306a36Sopenharmony_ci} 360362306a36Sopenharmony_ci 360462306a36Sopenharmony_cistatic void brcms_c_write_rate_shm(struct brcms_c_info *wlc, u8 rate, 360562306a36Sopenharmony_ci u8 basic_rate) 360662306a36Sopenharmony_ci{ 360762306a36Sopenharmony_ci u8 phy_rate, index; 360862306a36Sopenharmony_ci u8 basic_phy_rate, basic_index; 360962306a36Sopenharmony_ci u16 dir_table, basic_table; 361062306a36Sopenharmony_ci u16 basic_ptr; 361162306a36Sopenharmony_ci 361262306a36Sopenharmony_ci /* Shared memory address for the table we are reading */ 361362306a36Sopenharmony_ci dir_table = is_ofdm_rate(basic_rate) ? M_RT_DIRMAP_A : M_RT_DIRMAP_B; 361462306a36Sopenharmony_ci 361562306a36Sopenharmony_ci /* Shared memory address for the table we are writing */ 361662306a36Sopenharmony_ci basic_table = is_ofdm_rate(rate) ? M_RT_BBRSMAP_A : M_RT_BBRSMAP_B; 361762306a36Sopenharmony_ci 361862306a36Sopenharmony_ci /* 361962306a36Sopenharmony_ci * for a given rate, the LS-nibble of the PLCP SIGNAL field is 362062306a36Sopenharmony_ci * the index into the rate table. 362162306a36Sopenharmony_ci */ 362262306a36Sopenharmony_ci phy_rate = rate_info[rate] & BRCMS_RATE_MASK; 362362306a36Sopenharmony_ci basic_phy_rate = rate_info[basic_rate] & BRCMS_RATE_MASK; 362462306a36Sopenharmony_ci index = phy_rate & 0xf; 362562306a36Sopenharmony_ci basic_index = basic_phy_rate & 0xf; 362662306a36Sopenharmony_ci 362762306a36Sopenharmony_ci /* Find the SHM pointer to the ACK rate entry by looking in the 362862306a36Sopenharmony_ci * Direct-map Table 362962306a36Sopenharmony_ci */ 363062306a36Sopenharmony_ci basic_ptr = brcms_b_read_shm(wlc->hw, (dir_table + basic_index * 2)); 363162306a36Sopenharmony_ci 363262306a36Sopenharmony_ci /* Update the SHM BSS-basic-rate-set mapping table with the pointer 363362306a36Sopenharmony_ci * to the correct basic rate for the given incoming rate 363462306a36Sopenharmony_ci */ 363562306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, (basic_table + index * 2), basic_ptr); 363662306a36Sopenharmony_ci} 363762306a36Sopenharmony_ci 363862306a36Sopenharmony_cistatic const struct brcms_c_rateset * 363962306a36Sopenharmony_cibrcms_c_rateset_get_hwrs(struct brcms_c_info *wlc) 364062306a36Sopenharmony_ci{ 364162306a36Sopenharmony_ci const struct brcms_c_rateset *rs_dflt; 364262306a36Sopenharmony_ci 364362306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) { 364462306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_5G) 364562306a36Sopenharmony_ci rs_dflt = &ofdm_mimo_rates; 364662306a36Sopenharmony_ci else 364762306a36Sopenharmony_ci rs_dflt = &cck_ofdm_mimo_rates; 364862306a36Sopenharmony_ci } else if (wlc->band->gmode) 364962306a36Sopenharmony_ci rs_dflt = &cck_ofdm_rates; 365062306a36Sopenharmony_ci else 365162306a36Sopenharmony_ci rs_dflt = &cck_rates; 365262306a36Sopenharmony_ci 365362306a36Sopenharmony_ci return rs_dflt; 365462306a36Sopenharmony_ci} 365562306a36Sopenharmony_ci 365662306a36Sopenharmony_cistatic void brcms_c_set_ratetable(struct brcms_c_info *wlc) 365762306a36Sopenharmony_ci{ 365862306a36Sopenharmony_ci const struct brcms_c_rateset *rs_dflt; 365962306a36Sopenharmony_ci struct brcms_c_rateset rs; 366062306a36Sopenharmony_ci u8 rate, basic_rate; 366162306a36Sopenharmony_ci uint i; 366262306a36Sopenharmony_ci 366362306a36Sopenharmony_ci rs_dflt = brcms_c_rateset_get_hwrs(wlc); 366462306a36Sopenharmony_ci 366562306a36Sopenharmony_ci brcms_c_rateset_copy(rs_dflt, &rs); 366662306a36Sopenharmony_ci brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); 366762306a36Sopenharmony_ci 366862306a36Sopenharmony_ci /* walk the phy rate table and update SHM basic rate lookup table */ 366962306a36Sopenharmony_ci for (i = 0; i < rs.count; i++) { 367062306a36Sopenharmony_ci rate = rs.rates[i] & BRCMS_RATE_MASK; 367162306a36Sopenharmony_ci 367262306a36Sopenharmony_ci /* for a given rate brcms_basic_rate returns the rate at 367362306a36Sopenharmony_ci * which a response ACK/CTS should be sent. 367462306a36Sopenharmony_ci */ 367562306a36Sopenharmony_ci basic_rate = brcms_basic_rate(wlc, rate); 367662306a36Sopenharmony_ci if (basic_rate == 0) 367762306a36Sopenharmony_ci /* This should only happen if we are using a 367862306a36Sopenharmony_ci * restricted rateset. 367962306a36Sopenharmony_ci */ 368062306a36Sopenharmony_ci basic_rate = rs.rates[0] & BRCMS_RATE_MASK; 368162306a36Sopenharmony_ci 368262306a36Sopenharmony_ci brcms_c_write_rate_shm(wlc, rate, basic_rate); 368362306a36Sopenharmony_ci } 368462306a36Sopenharmony_ci} 368562306a36Sopenharmony_ci 368662306a36Sopenharmony_ci/* band-specific init */ 368762306a36Sopenharmony_cistatic void brcms_c_bsinit(struct brcms_c_info *wlc) 368862306a36Sopenharmony_ci{ 368962306a36Sopenharmony_ci brcms_dbg_info(wlc->hw->d11core, "wl%d: bandunit %d\n", 369062306a36Sopenharmony_ci wlc->pub->unit, wlc->band->bandunit); 369162306a36Sopenharmony_ci 369262306a36Sopenharmony_ci /* write ucode ACK/CTS rate table */ 369362306a36Sopenharmony_ci brcms_c_set_ratetable(wlc); 369462306a36Sopenharmony_ci 369562306a36Sopenharmony_ci /* update some band specific mac configuration */ 369662306a36Sopenharmony_ci brcms_c_ucode_mac_upd(wlc); 369762306a36Sopenharmony_ci 369862306a36Sopenharmony_ci /* init antenna selection */ 369962306a36Sopenharmony_ci brcms_c_antsel_init(wlc->asi); 370062306a36Sopenharmony_ci 370162306a36Sopenharmony_ci} 370262306a36Sopenharmony_ci 370362306a36Sopenharmony_ci/* formula: IDLE_BUSY_RATIO_X_16 = (100-duty_cycle)/duty_cycle*16 */ 370462306a36Sopenharmony_cistatic int 370562306a36Sopenharmony_cibrcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle, bool isOFDM, 370662306a36Sopenharmony_ci bool writeToShm) 370762306a36Sopenharmony_ci{ 370862306a36Sopenharmony_ci int idle_busy_ratio_x_16 = 0; 370962306a36Sopenharmony_ci uint offset = 371062306a36Sopenharmony_ci isOFDM ? M_TX_IDLE_BUSY_RATIO_X_16_OFDM : 371162306a36Sopenharmony_ci M_TX_IDLE_BUSY_RATIO_X_16_CCK; 371262306a36Sopenharmony_ci if (duty_cycle > 100 || duty_cycle < 0) { 371362306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 371462306a36Sopenharmony_ci "wl%d: duty cycle value off limit\n", 371562306a36Sopenharmony_ci wlc->pub->unit); 371662306a36Sopenharmony_ci return -EINVAL; 371762306a36Sopenharmony_ci } 371862306a36Sopenharmony_ci if (duty_cycle) 371962306a36Sopenharmony_ci idle_busy_ratio_x_16 = (100 - duty_cycle) * 16 / duty_cycle; 372062306a36Sopenharmony_ci /* Only write to shared memory when wl is up */ 372162306a36Sopenharmony_ci if (writeToShm) 372262306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, offset, (u16) idle_busy_ratio_x_16); 372362306a36Sopenharmony_ci 372462306a36Sopenharmony_ci if (isOFDM) 372562306a36Sopenharmony_ci wlc->tx_duty_cycle_ofdm = (u16) duty_cycle; 372662306a36Sopenharmony_ci else 372762306a36Sopenharmony_ci wlc->tx_duty_cycle_cck = (u16) duty_cycle; 372862306a36Sopenharmony_ci 372962306a36Sopenharmony_ci return 0; 373062306a36Sopenharmony_ci} 373162306a36Sopenharmony_ci 373262306a36Sopenharmony_ci/* push sw hps and wake state through hardware */ 373362306a36Sopenharmony_cistatic void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc) 373462306a36Sopenharmony_ci{ 373562306a36Sopenharmony_ci u32 v1, v2; 373662306a36Sopenharmony_ci bool hps; 373762306a36Sopenharmony_ci bool awake_before; 373862306a36Sopenharmony_ci 373962306a36Sopenharmony_ci hps = brcms_c_ps_allowed(wlc); 374062306a36Sopenharmony_ci 374162306a36Sopenharmony_ci brcms_dbg_mac80211(wlc->hw->d11core, "wl%d: hps %d\n", wlc->pub->unit, 374262306a36Sopenharmony_ci hps); 374362306a36Sopenharmony_ci 374462306a36Sopenharmony_ci v1 = bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); 374562306a36Sopenharmony_ci v2 = MCTL_WAKE; 374662306a36Sopenharmony_ci if (hps) 374762306a36Sopenharmony_ci v2 |= MCTL_HPS; 374862306a36Sopenharmony_ci 374962306a36Sopenharmony_ci brcms_b_mctrl(wlc->hw, MCTL_WAKE | MCTL_HPS, v2); 375062306a36Sopenharmony_ci 375162306a36Sopenharmony_ci awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0)); 375262306a36Sopenharmony_ci 375362306a36Sopenharmony_ci if (!awake_before) 375462306a36Sopenharmony_ci brcms_b_wait_for_wake(wlc->hw); 375562306a36Sopenharmony_ci} 375662306a36Sopenharmony_ci 375762306a36Sopenharmony_ci/* 375862306a36Sopenharmony_ci * Write this BSS config's MAC address to core. 375962306a36Sopenharmony_ci * Updates RXE match engine. 376062306a36Sopenharmony_ci */ 376162306a36Sopenharmony_cistatic void brcms_c_set_mac(struct brcms_bss_cfg *bsscfg) 376262306a36Sopenharmony_ci{ 376362306a36Sopenharmony_ci struct brcms_c_info *wlc = bsscfg->wlc; 376462306a36Sopenharmony_ci 376562306a36Sopenharmony_ci /* enter the MAC addr into the RXE match registers */ 376662306a36Sopenharmony_ci brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, wlc->pub->cur_etheraddr); 376762306a36Sopenharmony_ci 376862306a36Sopenharmony_ci brcms_c_ampdu_macaddr_upd(wlc); 376962306a36Sopenharmony_ci} 377062306a36Sopenharmony_ci 377162306a36Sopenharmony_ci/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl). 377262306a36Sopenharmony_ci * Updates RXE match engine. 377362306a36Sopenharmony_ci */ 377462306a36Sopenharmony_cistatic void brcms_c_set_bssid(struct brcms_bss_cfg *bsscfg) 377562306a36Sopenharmony_ci{ 377662306a36Sopenharmony_ci /* we need to update BSSID in RXE match registers */ 377762306a36Sopenharmony_ci brcms_c_set_addrmatch(bsscfg->wlc, RCM_BSSID_OFFSET, bsscfg->BSSID); 377862306a36Sopenharmony_ci} 377962306a36Sopenharmony_ci 378062306a36Sopenharmony_civoid brcms_c_set_ssid(struct brcms_c_info *wlc, u8 *ssid, size_t ssid_len) 378162306a36Sopenharmony_ci{ 378262306a36Sopenharmony_ci u8 len = min_t(u8, sizeof(wlc->bsscfg->SSID), ssid_len); 378362306a36Sopenharmony_ci memset(wlc->bsscfg->SSID, 0, sizeof(wlc->bsscfg->SSID)); 378462306a36Sopenharmony_ci 378562306a36Sopenharmony_ci memcpy(wlc->bsscfg->SSID, ssid, len); 378662306a36Sopenharmony_ci wlc->bsscfg->SSID_len = len; 378762306a36Sopenharmony_ci} 378862306a36Sopenharmony_ci 378962306a36Sopenharmony_cistatic void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot) 379062306a36Sopenharmony_ci{ 379162306a36Sopenharmony_ci wlc_hw->shortslot = shortslot; 379262306a36Sopenharmony_ci 379362306a36Sopenharmony_ci if (wlc_hw->band->bandtype == BRCM_BAND_2G && wlc_hw->up) { 379462306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc_hw->wlc); 379562306a36Sopenharmony_ci brcms_b_update_slot_timing(wlc_hw, shortslot); 379662306a36Sopenharmony_ci brcms_c_enable_mac(wlc_hw->wlc); 379762306a36Sopenharmony_ci } 379862306a36Sopenharmony_ci} 379962306a36Sopenharmony_ci 380062306a36Sopenharmony_ci/* 380162306a36Sopenharmony_ci * Suspend the MAC and update the slot timing 380262306a36Sopenharmony_ci * for standard 11b/g (20us slots) or shortslot 11g (9us slots). 380362306a36Sopenharmony_ci */ 380462306a36Sopenharmony_cistatic void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot) 380562306a36Sopenharmony_ci{ 380662306a36Sopenharmony_ci /* use the override if it is set */ 380762306a36Sopenharmony_ci if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO) 380862306a36Sopenharmony_ci shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON); 380962306a36Sopenharmony_ci 381062306a36Sopenharmony_ci if (wlc->shortslot == shortslot) 381162306a36Sopenharmony_ci return; 381262306a36Sopenharmony_ci 381362306a36Sopenharmony_ci wlc->shortslot = shortslot; 381462306a36Sopenharmony_ci 381562306a36Sopenharmony_ci brcms_b_set_shortslot(wlc->hw, shortslot); 381662306a36Sopenharmony_ci} 381762306a36Sopenharmony_ci 381862306a36Sopenharmony_cistatic void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, u16 chanspec) 381962306a36Sopenharmony_ci{ 382062306a36Sopenharmony_ci if (wlc->home_chanspec != chanspec) { 382162306a36Sopenharmony_ci wlc->home_chanspec = chanspec; 382262306a36Sopenharmony_ci 382362306a36Sopenharmony_ci if (wlc->pub->associated) 382462306a36Sopenharmony_ci wlc->bsscfg->current_bss->chanspec = chanspec; 382562306a36Sopenharmony_ci } 382662306a36Sopenharmony_ci} 382762306a36Sopenharmony_ci 382862306a36Sopenharmony_civoid 382962306a36Sopenharmony_cibrcms_b_set_chanspec(struct brcms_hardware *wlc_hw, u16 chanspec, 383062306a36Sopenharmony_ci bool mute_tx, struct txpwr_limits *txpwr) 383162306a36Sopenharmony_ci{ 383262306a36Sopenharmony_ci uint bandunit; 383362306a36Sopenharmony_ci 383462306a36Sopenharmony_ci brcms_dbg_mac80211(wlc_hw->d11core, "wl%d: 0x%x\n", wlc_hw->unit, 383562306a36Sopenharmony_ci chanspec); 383662306a36Sopenharmony_ci 383762306a36Sopenharmony_ci wlc_hw->chanspec = chanspec; 383862306a36Sopenharmony_ci 383962306a36Sopenharmony_ci /* Switch bands if necessary */ 384062306a36Sopenharmony_ci if (wlc_hw->_nbands > 1) { 384162306a36Sopenharmony_ci bandunit = chspec_bandunit(chanspec); 384262306a36Sopenharmony_ci if (wlc_hw->band->bandunit != bandunit) { 384362306a36Sopenharmony_ci /* brcms_b_setband disables other bandunit, 384462306a36Sopenharmony_ci * use light band switch if not up yet 384562306a36Sopenharmony_ci */ 384662306a36Sopenharmony_ci if (wlc_hw->up) { 384762306a36Sopenharmony_ci wlc_phy_chanspec_radio_set(wlc_hw-> 384862306a36Sopenharmony_ci bandstate[bandunit]-> 384962306a36Sopenharmony_ci pi, chanspec); 385062306a36Sopenharmony_ci brcms_b_setband(wlc_hw, bandunit, chanspec); 385162306a36Sopenharmony_ci } else { 385262306a36Sopenharmony_ci brcms_c_setxband(wlc_hw, bandunit); 385362306a36Sopenharmony_ci } 385462306a36Sopenharmony_ci } 385562306a36Sopenharmony_ci } 385662306a36Sopenharmony_ci 385762306a36Sopenharmony_ci wlc_phy_initcal_enable(wlc_hw->band->pi, !mute_tx); 385862306a36Sopenharmony_ci 385962306a36Sopenharmony_ci if (!wlc_hw->up) { 386062306a36Sopenharmony_ci if (wlc_hw->clk) 386162306a36Sopenharmony_ci wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, 386262306a36Sopenharmony_ci chanspec); 386362306a36Sopenharmony_ci wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec); 386462306a36Sopenharmony_ci } else { 386562306a36Sopenharmony_ci wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec); 386662306a36Sopenharmony_ci wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec); 386762306a36Sopenharmony_ci 386862306a36Sopenharmony_ci /* Update muting of the channel */ 386962306a36Sopenharmony_ci brcms_b_mute(wlc_hw, mute_tx); 387062306a36Sopenharmony_ci } 387162306a36Sopenharmony_ci} 387262306a36Sopenharmony_ci 387362306a36Sopenharmony_ci/* switch to and initialize new band */ 387462306a36Sopenharmony_cistatic void brcms_c_setband(struct brcms_c_info *wlc, 387562306a36Sopenharmony_ci uint bandunit) 387662306a36Sopenharmony_ci{ 387762306a36Sopenharmony_ci wlc->band = wlc->bandstate[bandunit]; 387862306a36Sopenharmony_ci 387962306a36Sopenharmony_ci if (!wlc->pub->up) 388062306a36Sopenharmony_ci return; 388162306a36Sopenharmony_ci 388262306a36Sopenharmony_ci /* wait for at least one beacon before entering sleeping state */ 388362306a36Sopenharmony_ci brcms_c_set_ps_ctrl(wlc); 388462306a36Sopenharmony_ci 388562306a36Sopenharmony_ci /* band-specific initializations */ 388662306a36Sopenharmony_ci brcms_c_bsinit(wlc); 388762306a36Sopenharmony_ci} 388862306a36Sopenharmony_ci 388962306a36Sopenharmony_cistatic void brcms_c_set_chanspec(struct brcms_c_info *wlc, u16 chanspec) 389062306a36Sopenharmony_ci{ 389162306a36Sopenharmony_ci uint bandunit; 389262306a36Sopenharmony_ci u16 old_chanspec = wlc->chanspec; 389362306a36Sopenharmony_ci 389462306a36Sopenharmony_ci if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) { 389562306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %s: Bad channel %d\n", 389662306a36Sopenharmony_ci wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec)); 389762306a36Sopenharmony_ci return; 389862306a36Sopenharmony_ci } 389962306a36Sopenharmony_ci 390062306a36Sopenharmony_ci /* Switch bands if necessary */ 390162306a36Sopenharmony_ci if (wlc->pub->_nbands > 1) { 390262306a36Sopenharmony_ci bandunit = chspec_bandunit(chanspec); 390362306a36Sopenharmony_ci if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) { 390462306a36Sopenharmony_ci if (wlc->bandlocked) { 390562306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 390662306a36Sopenharmony_ci "wl%d: %s: chspec %d band is locked!\n", 390762306a36Sopenharmony_ci wlc->pub->unit, __func__, 390862306a36Sopenharmony_ci CHSPEC_CHANNEL(chanspec)); 390962306a36Sopenharmony_ci return; 391062306a36Sopenharmony_ci } 391162306a36Sopenharmony_ci /* 391262306a36Sopenharmony_ci * should the setband call come after the 391362306a36Sopenharmony_ci * brcms_b_chanspec() ? if the setband updates 391462306a36Sopenharmony_ci * (brcms_c_bsinit) use low level calls to inspect and 391562306a36Sopenharmony_ci * set state, the state inspected may be from the wrong 391662306a36Sopenharmony_ci * band, or the following brcms_b_set_chanspec() may 391762306a36Sopenharmony_ci * undo the work. 391862306a36Sopenharmony_ci */ 391962306a36Sopenharmony_ci brcms_c_setband(wlc, bandunit); 392062306a36Sopenharmony_ci } 392162306a36Sopenharmony_ci } 392262306a36Sopenharmony_ci 392362306a36Sopenharmony_ci /* sync up phy/radio chanspec */ 392462306a36Sopenharmony_ci brcms_c_set_phy_chanspec(wlc, chanspec); 392562306a36Sopenharmony_ci 392662306a36Sopenharmony_ci /* init antenna selection */ 392762306a36Sopenharmony_ci if (brcms_chspec_bw(old_chanspec) != brcms_chspec_bw(chanspec)) { 392862306a36Sopenharmony_ci brcms_c_antsel_init(wlc->asi); 392962306a36Sopenharmony_ci 393062306a36Sopenharmony_ci /* Fix the hardware rateset based on bw. 393162306a36Sopenharmony_ci * Mainly add MCS32 for 40Mhz, remove MCS 32 for 20Mhz 393262306a36Sopenharmony_ci */ 393362306a36Sopenharmony_ci brcms_c_rateset_bw_mcs_filter(&wlc->band->hw_rateset, 393462306a36Sopenharmony_ci wlc->band->mimo_cap_40 ? brcms_chspec_bw(chanspec) : 0); 393562306a36Sopenharmony_ci } 393662306a36Sopenharmony_ci 393762306a36Sopenharmony_ci /* update some mac configuration since chanspec changed */ 393862306a36Sopenharmony_ci brcms_c_ucode_mac_upd(wlc); 393962306a36Sopenharmony_ci} 394062306a36Sopenharmony_ci 394162306a36Sopenharmony_ci/* 394262306a36Sopenharmony_ci * This function changes the phytxctl for beacon based on current 394362306a36Sopenharmony_ci * beacon ratespec AND txant setting as per this table: 394462306a36Sopenharmony_ci * ratespec CCK ant = wlc->stf->txant 394562306a36Sopenharmony_ci * OFDM ant = 3 394662306a36Sopenharmony_ci */ 394762306a36Sopenharmony_civoid brcms_c_beacon_phytxctl_txant_upd(struct brcms_c_info *wlc, 394862306a36Sopenharmony_ci u32 bcn_rspec) 394962306a36Sopenharmony_ci{ 395062306a36Sopenharmony_ci u16 phyctl; 395162306a36Sopenharmony_ci u16 phytxant = wlc->stf->phytxant; 395262306a36Sopenharmony_ci u16 mask = PHY_TXC_ANT_MASK; 395362306a36Sopenharmony_ci 395462306a36Sopenharmony_ci /* for non-siso rates or default setting, use the available chains */ 395562306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) 395662306a36Sopenharmony_ci phytxant = brcms_c_stf_phytxchain_sel(wlc, bcn_rspec); 395762306a36Sopenharmony_ci 395862306a36Sopenharmony_ci phyctl = brcms_b_read_shm(wlc->hw, M_BCN_PCTLWD); 395962306a36Sopenharmony_ci phyctl = (phyctl & ~mask) | phytxant; 396062306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_BCN_PCTLWD, phyctl); 396162306a36Sopenharmony_ci} 396262306a36Sopenharmony_ci 396362306a36Sopenharmony_ci/* 396462306a36Sopenharmony_ci * centralized protection config change function to simplify debugging, no 396562306a36Sopenharmony_ci * consistency checking this should be called only on changes to avoid overhead 396662306a36Sopenharmony_ci * in periodic function 396762306a36Sopenharmony_ci */ 396862306a36Sopenharmony_civoid brcms_c_protection_upd(struct brcms_c_info *wlc, uint idx, int val) 396962306a36Sopenharmony_ci{ 397062306a36Sopenharmony_ci /* 397162306a36Sopenharmony_ci * Cannot use brcms_dbg_* here because this function is called 397262306a36Sopenharmony_ci * before wlc is sufficiently initialized. 397362306a36Sopenharmony_ci */ 397462306a36Sopenharmony_ci BCMMSG(wlc->wiphy, "idx %d, val %d\n", idx, val); 397562306a36Sopenharmony_ci 397662306a36Sopenharmony_ci switch (idx) { 397762306a36Sopenharmony_ci case BRCMS_PROT_G_SPEC: 397862306a36Sopenharmony_ci wlc->protection->_g = (bool) val; 397962306a36Sopenharmony_ci break; 398062306a36Sopenharmony_ci case BRCMS_PROT_G_OVR: 398162306a36Sopenharmony_ci wlc->protection->g_override = (s8) val; 398262306a36Sopenharmony_ci break; 398362306a36Sopenharmony_ci case BRCMS_PROT_G_USER: 398462306a36Sopenharmony_ci wlc->protection->gmode_user = (u8) val; 398562306a36Sopenharmony_ci break; 398662306a36Sopenharmony_ci case BRCMS_PROT_OVERLAP: 398762306a36Sopenharmony_ci wlc->protection->overlap = (s8) val; 398862306a36Sopenharmony_ci break; 398962306a36Sopenharmony_ci case BRCMS_PROT_N_USER: 399062306a36Sopenharmony_ci wlc->protection->nmode_user = (s8) val; 399162306a36Sopenharmony_ci break; 399262306a36Sopenharmony_ci case BRCMS_PROT_N_CFG: 399362306a36Sopenharmony_ci wlc->protection->n_cfg = (s8) val; 399462306a36Sopenharmony_ci break; 399562306a36Sopenharmony_ci case BRCMS_PROT_N_CFG_OVR: 399662306a36Sopenharmony_ci wlc->protection->n_cfg_override = (s8) val; 399762306a36Sopenharmony_ci break; 399862306a36Sopenharmony_ci case BRCMS_PROT_N_NONGF: 399962306a36Sopenharmony_ci wlc->protection->nongf = (bool) val; 400062306a36Sopenharmony_ci break; 400162306a36Sopenharmony_ci case BRCMS_PROT_N_NONGF_OVR: 400262306a36Sopenharmony_ci wlc->protection->nongf_override = (s8) val; 400362306a36Sopenharmony_ci break; 400462306a36Sopenharmony_ci case BRCMS_PROT_N_PAM_OVR: 400562306a36Sopenharmony_ci wlc->protection->n_pam_override = (s8) val; 400662306a36Sopenharmony_ci break; 400762306a36Sopenharmony_ci case BRCMS_PROT_N_OBSS: 400862306a36Sopenharmony_ci wlc->protection->n_obss = (bool) val; 400962306a36Sopenharmony_ci break; 401062306a36Sopenharmony_ci 401162306a36Sopenharmony_ci default: 401262306a36Sopenharmony_ci break; 401362306a36Sopenharmony_ci } 401462306a36Sopenharmony_ci 401562306a36Sopenharmony_ci} 401662306a36Sopenharmony_ci 401762306a36Sopenharmony_cistatic void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val) 401862306a36Sopenharmony_ci{ 401962306a36Sopenharmony_ci if (wlc->pub->up) { 402062306a36Sopenharmony_ci brcms_c_update_beacon(wlc); 402162306a36Sopenharmony_ci brcms_c_update_probe_resp(wlc, true); 402262306a36Sopenharmony_ci } 402362306a36Sopenharmony_ci} 402462306a36Sopenharmony_ci 402562306a36Sopenharmony_cistatic void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val) 402662306a36Sopenharmony_ci{ 402762306a36Sopenharmony_ci wlc->stf->ldpc = val; 402862306a36Sopenharmony_ci 402962306a36Sopenharmony_ci if (wlc->pub->up) { 403062306a36Sopenharmony_ci brcms_c_update_beacon(wlc); 403162306a36Sopenharmony_ci brcms_c_update_probe_resp(wlc, true); 403262306a36Sopenharmony_ci wlc_phy_ldpc_override_set(wlc->band->pi, (val ? true : false)); 403362306a36Sopenharmony_ci } 403462306a36Sopenharmony_ci} 403562306a36Sopenharmony_ci 403662306a36Sopenharmony_civoid brcms_c_wme_setparams(struct brcms_c_info *wlc, u16 aci, 403762306a36Sopenharmony_ci const struct ieee80211_tx_queue_params *params, 403862306a36Sopenharmony_ci bool suspend) 403962306a36Sopenharmony_ci{ 404062306a36Sopenharmony_ci int i; 404162306a36Sopenharmony_ci struct shm_acparams acp_shm; 404262306a36Sopenharmony_ci u16 *shm_entry; 404362306a36Sopenharmony_ci 404462306a36Sopenharmony_ci /* Only apply params if the core is out of reset and has clocks */ 404562306a36Sopenharmony_ci if (!wlc->clk) { 404662306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %s : no-clock\n", 404762306a36Sopenharmony_ci wlc->pub->unit, __func__); 404862306a36Sopenharmony_ci return; 404962306a36Sopenharmony_ci } 405062306a36Sopenharmony_ci 405162306a36Sopenharmony_ci memset(&acp_shm, 0, sizeof(struct shm_acparams)); 405262306a36Sopenharmony_ci /* fill in shm ac params struct */ 405362306a36Sopenharmony_ci acp_shm.txop = params->txop; 405462306a36Sopenharmony_ci /* convert from units of 32us to us for ucode */ 405562306a36Sopenharmony_ci wlc->edcf_txop[aci & 0x3] = acp_shm.txop = 405662306a36Sopenharmony_ci EDCF_TXOP2USEC(acp_shm.txop); 405762306a36Sopenharmony_ci acp_shm.aifs = (params->aifs & EDCF_AIFSN_MASK); 405862306a36Sopenharmony_ci 405962306a36Sopenharmony_ci if (aci == IEEE80211_AC_VI && acp_shm.txop == 0 406062306a36Sopenharmony_ci && acp_shm.aifs < EDCF_AIFSN_MAX) 406162306a36Sopenharmony_ci acp_shm.aifs++; 406262306a36Sopenharmony_ci 406362306a36Sopenharmony_ci if (acp_shm.aifs < EDCF_AIFSN_MIN 406462306a36Sopenharmony_ci || acp_shm.aifs > EDCF_AIFSN_MAX) { 406562306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: edcf_setparams: bad " 406662306a36Sopenharmony_ci "aifs %d\n", wlc->pub->unit, acp_shm.aifs); 406762306a36Sopenharmony_ci } else { 406862306a36Sopenharmony_ci acp_shm.cwmin = params->cw_min; 406962306a36Sopenharmony_ci acp_shm.cwmax = params->cw_max; 407062306a36Sopenharmony_ci acp_shm.cwcur = acp_shm.cwmin; 407162306a36Sopenharmony_ci acp_shm.bslots = 407262306a36Sopenharmony_ci bcma_read16(wlc->hw->d11core, D11REGOFFS(tsf_random)) & 407362306a36Sopenharmony_ci acp_shm.cwcur; 407462306a36Sopenharmony_ci acp_shm.reggap = acp_shm.bslots + acp_shm.aifs; 407562306a36Sopenharmony_ci /* Indicate the new params to the ucode */ 407662306a36Sopenharmony_ci acp_shm.status = brcms_b_read_shm(wlc->hw, (M_EDCF_QINFO + 407762306a36Sopenharmony_ci wme_ac2fifo[aci] * 407862306a36Sopenharmony_ci M_EDCF_QLEN + 407962306a36Sopenharmony_ci M_EDCF_STATUS_OFF)); 408062306a36Sopenharmony_ci acp_shm.status |= WME_STATUS_NEWAC; 408162306a36Sopenharmony_ci 408262306a36Sopenharmony_ci /* Fill in shm acparam table */ 408362306a36Sopenharmony_ci shm_entry = (u16 *) &acp_shm; 408462306a36Sopenharmony_ci for (i = 0; i < (int)sizeof(struct shm_acparams); i += 2) 408562306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, 408662306a36Sopenharmony_ci M_EDCF_QINFO + 408762306a36Sopenharmony_ci wme_ac2fifo[aci] * M_EDCF_QLEN + i, 408862306a36Sopenharmony_ci *shm_entry++); 408962306a36Sopenharmony_ci } 409062306a36Sopenharmony_ci 409162306a36Sopenharmony_ci if (suspend) 409262306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc); 409362306a36Sopenharmony_ci 409462306a36Sopenharmony_ci brcms_c_update_beacon(wlc); 409562306a36Sopenharmony_ci brcms_c_update_probe_resp(wlc, false); 409662306a36Sopenharmony_ci 409762306a36Sopenharmony_ci if (suspend) 409862306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 409962306a36Sopenharmony_ci} 410062306a36Sopenharmony_ci 410162306a36Sopenharmony_cistatic void brcms_c_edcf_setparams(struct brcms_c_info *wlc, bool suspend) 410262306a36Sopenharmony_ci{ 410362306a36Sopenharmony_ci u16 aci; 410462306a36Sopenharmony_ci int i_ac; 410562306a36Sopenharmony_ci struct ieee80211_tx_queue_params txq_pars; 410662306a36Sopenharmony_ci static const struct edcf_acparam default_edcf_acparams[] = { 410762306a36Sopenharmony_ci {EDCF_AC_BE_ACI_STA, EDCF_AC_BE_ECW_STA, EDCF_AC_BE_TXOP_STA}, 410862306a36Sopenharmony_ci {EDCF_AC_BK_ACI_STA, EDCF_AC_BK_ECW_STA, EDCF_AC_BK_TXOP_STA}, 410962306a36Sopenharmony_ci {EDCF_AC_VI_ACI_STA, EDCF_AC_VI_ECW_STA, EDCF_AC_VI_TXOP_STA}, 411062306a36Sopenharmony_ci {EDCF_AC_VO_ACI_STA, EDCF_AC_VO_ECW_STA, EDCF_AC_VO_TXOP_STA} 411162306a36Sopenharmony_ci }; /* ucode needs these parameters during its initialization */ 411262306a36Sopenharmony_ci const struct edcf_acparam *edcf_acp = &default_edcf_acparams[0]; 411362306a36Sopenharmony_ci 411462306a36Sopenharmony_ci for (i_ac = 0; i_ac < IEEE80211_NUM_ACS; i_ac++, edcf_acp++) { 411562306a36Sopenharmony_ci /* find out which ac this set of params applies to */ 411662306a36Sopenharmony_ci aci = (edcf_acp->ACI & EDCF_ACI_MASK) >> EDCF_ACI_SHIFT; 411762306a36Sopenharmony_ci 411862306a36Sopenharmony_ci /* fill in shm ac params struct */ 411962306a36Sopenharmony_ci txq_pars.txop = edcf_acp->TXOP; 412062306a36Sopenharmony_ci txq_pars.aifs = edcf_acp->ACI; 412162306a36Sopenharmony_ci 412262306a36Sopenharmony_ci /* CWmin = 2^(ECWmin) - 1 */ 412362306a36Sopenharmony_ci txq_pars.cw_min = EDCF_ECW2CW(edcf_acp->ECW & EDCF_ECWMIN_MASK); 412462306a36Sopenharmony_ci /* CWmax = 2^(ECWmax) - 1 */ 412562306a36Sopenharmony_ci txq_pars.cw_max = EDCF_ECW2CW((edcf_acp->ECW & EDCF_ECWMAX_MASK) 412662306a36Sopenharmony_ci >> EDCF_ECWMAX_SHIFT); 412762306a36Sopenharmony_ci brcms_c_wme_setparams(wlc, aci, &txq_pars, suspend); 412862306a36Sopenharmony_ci } 412962306a36Sopenharmony_ci 413062306a36Sopenharmony_ci if (suspend) { 413162306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc); 413262306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 413362306a36Sopenharmony_ci } 413462306a36Sopenharmony_ci} 413562306a36Sopenharmony_ci 413662306a36Sopenharmony_cistatic void brcms_c_radio_monitor_start(struct brcms_c_info *wlc) 413762306a36Sopenharmony_ci{ 413862306a36Sopenharmony_ci /* Don't start the timer if HWRADIO feature is disabled */ 413962306a36Sopenharmony_ci if (wlc->radio_monitor) 414062306a36Sopenharmony_ci return; 414162306a36Sopenharmony_ci 414262306a36Sopenharmony_ci wlc->radio_monitor = true; 414362306a36Sopenharmony_ci brcms_b_pllreq(wlc->hw, true, BRCMS_PLLREQ_RADIO_MON); 414462306a36Sopenharmony_ci brcms_add_timer(wlc->radio_timer, TIMER_INTERVAL_RADIOCHK, true); 414562306a36Sopenharmony_ci} 414662306a36Sopenharmony_ci 414762306a36Sopenharmony_cistatic bool brcms_c_radio_monitor_stop(struct brcms_c_info *wlc) 414862306a36Sopenharmony_ci{ 414962306a36Sopenharmony_ci if (!wlc->radio_monitor) 415062306a36Sopenharmony_ci return true; 415162306a36Sopenharmony_ci 415262306a36Sopenharmony_ci wlc->radio_monitor = false; 415362306a36Sopenharmony_ci brcms_b_pllreq(wlc->hw, false, BRCMS_PLLREQ_RADIO_MON); 415462306a36Sopenharmony_ci return brcms_del_timer(wlc->radio_timer); 415562306a36Sopenharmony_ci} 415662306a36Sopenharmony_ci 415762306a36Sopenharmony_ci/* read hwdisable state and propagate to wlc flag */ 415862306a36Sopenharmony_cistatic void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc) 415962306a36Sopenharmony_ci{ 416062306a36Sopenharmony_ci if (wlc->pub->hw_off) 416162306a36Sopenharmony_ci return; 416262306a36Sopenharmony_ci 416362306a36Sopenharmony_ci if (brcms_b_radio_read_hwdisabled(wlc->hw)) 416462306a36Sopenharmony_ci mboolset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); 416562306a36Sopenharmony_ci else 416662306a36Sopenharmony_ci mboolclr(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE); 416762306a36Sopenharmony_ci} 416862306a36Sopenharmony_ci 416962306a36Sopenharmony_ci/* update hwradio status and return it */ 417062306a36Sopenharmony_cibool brcms_c_check_radio_disabled(struct brcms_c_info *wlc) 417162306a36Sopenharmony_ci{ 417262306a36Sopenharmony_ci brcms_c_radio_hwdisable_upd(wlc); 417362306a36Sopenharmony_ci 417462306a36Sopenharmony_ci return mboolisset(wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE) ? 417562306a36Sopenharmony_ci true : false; 417662306a36Sopenharmony_ci} 417762306a36Sopenharmony_ci 417862306a36Sopenharmony_ci/* periodical query hw radio button while driver is "down" */ 417962306a36Sopenharmony_cistatic void brcms_c_radio_timer(void *arg) 418062306a36Sopenharmony_ci{ 418162306a36Sopenharmony_ci struct brcms_c_info *wlc = (struct brcms_c_info *) arg; 418262306a36Sopenharmony_ci 418362306a36Sopenharmony_ci if (brcms_deviceremoved(wlc)) { 418462306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", 418562306a36Sopenharmony_ci wlc->pub->unit, __func__); 418662306a36Sopenharmony_ci brcms_down(wlc->wl); 418762306a36Sopenharmony_ci return; 418862306a36Sopenharmony_ci } 418962306a36Sopenharmony_ci 419062306a36Sopenharmony_ci brcms_c_radio_hwdisable_upd(wlc); 419162306a36Sopenharmony_ci} 419262306a36Sopenharmony_ci 419362306a36Sopenharmony_ci/* common low-level watchdog code */ 419462306a36Sopenharmony_cistatic void brcms_b_watchdog(struct brcms_c_info *wlc) 419562306a36Sopenharmony_ci{ 419662306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 419762306a36Sopenharmony_ci 419862306a36Sopenharmony_ci if (!wlc_hw->up) 419962306a36Sopenharmony_ci return; 420062306a36Sopenharmony_ci 420162306a36Sopenharmony_ci /* increment second count */ 420262306a36Sopenharmony_ci wlc_hw->now++; 420362306a36Sopenharmony_ci 420462306a36Sopenharmony_ci /* Check for FIFO error interrupts */ 420562306a36Sopenharmony_ci brcms_b_fifoerrors(wlc_hw); 420662306a36Sopenharmony_ci 420762306a36Sopenharmony_ci /* make sure RX dma has buffers */ 420862306a36Sopenharmony_ci dma_rxfill(wlc->hw->di[RX_FIFO]); 420962306a36Sopenharmony_ci 421062306a36Sopenharmony_ci wlc_phy_watchdog(wlc_hw->band->pi); 421162306a36Sopenharmony_ci} 421262306a36Sopenharmony_ci 421362306a36Sopenharmony_ci/* common watchdog code */ 421462306a36Sopenharmony_cistatic void brcms_c_watchdog(struct brcms_c_info *wlc) 421562306a36Sopenharmony_ci{ 421662306a36Sopenharmony_ci brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); 421762306a36Sopenharmony_ci 421862306a36Sopenharmony_ci if (!wlc->pub->up) 421962306a36Sopenharmony_ci return; 422062306a36Sopenharmony_ci 422162306a36Sopenharmony_ci if (brcms_deviceremoved(wlc)) { 422262306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %s: dead chip\n", 422362306a36Sopenharmony_ci wlc->pub->unit, __func__); 422462306a36Sopenharmony_ci brcms_down(wlc->wl); 422562306a36Sopenharmony_ci return; 422662306a36Sopenharmony_ci } 422762306a36Sopenharmony_ci 422862306a36Sopenharmony_ci /* increment second count */ 422962306a36Sopenharmony_ci wlc->pub->now++; 423062306a36Sopenharmony_ci 423162306a36Sopenharmony_ci brcms_c_radio_hwdisable_upd(wlc); 423262306a36Sopenharmony_ci /* if radio is disable, driver may be down, quit here */ 423362306a36Sopenharmony_ci if (wlc->pub->radio_disabled) 423462306a36Sopenharmony_ci return; 423562306a36Sopenharmony_ci 423662306a36Sopenharmony_ci brcms_b_watchdog(wlc); 423762306a36Sopenharmony_ci 423862306a36Sopenharmony_ci /* 423962306a36Sopenharmony_ci * occasionally sample mac stat counters to 424062306a36Sopenharmony_ci * detect 16-bit counter wrap 424162306a36Sopenharmony_ci */ 424262306a36Sopenharmony_ci if ((wlc->pub->now % SW_TIMER_MAC_STAT_UPD) == 0) 424362306a36Sopenharmony_ci brcms_c_statsupd(wlc); 424462306a36Sopenharmony_ci 424562306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc->band) && 424662306a36Sopenharmony_ci ((wlc->pub->now - wlc->tempsense_lasttime) >= 424762306a36Sopenharmony_ci BRCMS_TEMPSENSE_PERIOD)) { 424862306a36Sopenharmony_ci wlc->tempsense_lasttime = wlc->pub->now; 424962306a36Sopenharmony_ci brcms_c_tempsense_upd(wlc); 425062306a36Sopenharmony_ci } 425162306a36Sopenharmony_ci} 425262306a36Sopenharmony_ci 425362306a36Sopenharmony_cistatic void brcms_c_watchdog_by_timer(void *arg) 425462306a36Sopenharmony_ci{ 425562306a36Sopenharmony_ci struct brcms_c_info *wlc = (struct brcms_c_info *) arg; 425662306a36Sopenharmony_ci 425762306a36Sopenharmony_ci brcms_c_watchdog(wlc); 425862306a36Sopenharmony_ci} 425962306a36Sopenharmony_ci 426062306a36Sopenharmony_cistatic bool brcms_c_timers_init(struct brcms_c_info *wlc, int unit) 426162306a36Sopenharmony_ci{ 426262306a36Sopenharmony_ci wlc->wdtimer = brcms_init_timer(wlc->wl, brcms_c_watchdog_by_timer, 426362306a36Sopenharmony_ci wlc, "watchdog"); 426462306a36Sopenharmony_ci if (!wlc->wdtimer) { 426562306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for wdtimer " 426662306a36Sopenharmony_ci "failed\n", unit); 426762306a36Sopenharmony_ci goto fail; 426862306a36Sopenharmony_ci } 426962306a36Sopenharmony_ci 427062306a36Sopenharmony_ci wlc->radio_timer = brcms_init_timer(wlc->wl, brcms_c_radio_timer, 427162306a36Sopenharmony_ci wlc, "radio"); 427262306a36Sopenharmony_ci if (!wlc->radio_timer) { 427362306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: wl_init_timer for radio_timer " 427462306a36Sopenharmony_ci "failed\n", unit); 427562306a36Sopenharmony_ci goto fail; 427662306a36Sopenharmony_ci } 427762306a36Sopenharmony_ci 427862306a36Sopenharmony_ci return true; 427962306a36Sopenharmony_ci 428062306a36Sopenharmony_ci fail: 428162306a36Sopenharmony_ci return false; 428262306a36Sopenharmony_ci} 428362306a36Sopenharmony_ci 428462306a36Sopenharmony_ci/* 428562306a36Sopenharmony_ci * Initialize brcms_c_info default values ... 428662306a36Sopenharmony_ci * may get overrides later in this function 428762306a36Sopenharmony_ci */ 428862306a36Sopenharmony_cistatic void brcms_c_info_init(struct brcms_c_info *wlc, int unit) 428962306a36Sopenharmony_ci{ 429062306a36Sopenharmony_ci int i; 429162306a36Sopenharmony_ci 429262306a36Sopenharmony_ci /* Save our copy of the chanspec */ 429362306a36Sopenharmony_ci wlc->chanspec = ch20mhz_chspec(1); 429462306a36Sopenharmony_ci 429562306a36Sopenharmony_ci /* various 802.11g modes */ 429662306a36Sopenharmony_ci wlc->shortslot = false; 429762306a36Sopenharmony_ci wlc->shortslot_override = BRCMS_SHORTSLOT_AUTO; 429862306a36Sopenharmony_ci 429962306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_G_OVR, BRCMS_PROTECTION_AUTO); 430062306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_G_SPEC, false); 430162306a36Sopenharmony_ci 430262306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG_OVR, 430362306a36Sopenharmony_ci BRCMS_PROTECTION_AUTO); 430462306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_CFG, BRCMS_N_PROTECTION_OFF); 430562306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF_OVR, 430662306a36Sopenharmony_ci BRCMS_PROTECTION_AUTO); 430762306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_NONGF, false); 430862306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, AUTO); 430962306a36Sopenharmony_ci 431062306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_OVERLAP, 431162306a36Sopenharmony_ci BRCMS_PROTECTION_CTL_OVERLAP); 431262306a36Sopenharmony_ci 431362306a36Sopenharmony_ci /* 802.11g draft 4.0 NonERP elt advertisement */ 431462306a36Sopenharmony_ci wlc->include_legacy_erp = true; 431562306a36Sopenharmony_ci 431662306a36Sopenharmony_ci wlc->stf->ant_rx_ovr = ANT_RX_DIV_DEF; 431762306a36Sopenharmony_ci wlc->stf->txant = ANT_TX_DEF; 431862306a36Sopenharmony_ci 431962306a36Sopenharmony_ci wlc->prb_resp_timeout = BRCMS_PRB_RESP_TIMEOUT; 432062306a36Sopenharmony_ci 432162306a36Sopenharmony_ci wlc->usr_fragthresh = DOT11_DEFAULT_FRAG_LEN; 432262306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) 432362306a36Sopenharmony_ci wlc->fragthresh[i] = DOT11_DEFAULT_FRAG_LEN; 432462306a36Sopenharmony_ci wlc->RTSThresh = DOT11_DEFAULT_RTS_LEN; 432562306a36Sopenharmony_ci 432662306a36Sopenharmony_ci /* default rate fallback retry limits */ 432762306a36Sopenharmony_ci wlc->SFBL = RETRY_SHORT_FB; 432862306a36Sopenharmony_ci wlc->LFBL = RETRY_LONG_FB; 432962306a36Sopenharmony_ci 433062306a36Sopenharmony_ci /* default mac retry limits */ 433162306a36Sopenharmony_ci wlc->SRL = RETRY_SHORT_DEF; 433262306a36Sopenharmony_ci wlc->LRL = RETRY_LONG_DEF; 433362306a36Sopenharmony_ci 433462306a36Sopenharmony_ci /* WME QoS mode is Auto by default */ 433562306a36Sopenharmony_ci wlc->pub->_ampdu = AMPDU_AGG_HOST; 433662306a36Sopenharmony_ci} 433762306a36Sopenharmony_ci 433862306a36Sopenharmony_cistatic uint brcms_c_attach_module(struct brcms_c_info *wlc) 433962306a36Sopenharmony_ci{ 434062306a36Sopenharmony_ci uint err = 0; 434162306a36Sopenharmony_ci uint unit; 434262306a36Sopenharmony_ci unit = wlc->pub->unit; 434362306a36Sopenharmony_ci 434462306a36Sopenharmony_ci wlc->asi = brcms_c_antsel_attach(wlc); 434562306a36Sopenharmony_ci if (wlc->asi == NULL) { 434662306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: attach: antsel_attach " 434762306a36Sopenharmony_ci "failed\n", unit); 434862306a36Sopenharmony_ci err = 44; 434962306a36Sopenharmony_ci goto fail; 435062306a36Sopenharmony_ci } 435162306a36Sopenharmony_ci 435262306a36Sopenharmony_ci wlc->ampdu = brcms_c_ampdu_attach(wlc); 435362306a36Sopenharmony_ci if (wlc->ampdu == NULL) { 435462306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: attach: ampdu_attach " 435562306a36Sopenharmony_ci "failed\n", unit); 435662306a36Sopenharmony_ci err = 50; 435762306a36Sopenharmony_ci goto fail; 435862306a36Sopenharmony_ci } 435962306a36Sopenharmony_ci 436062306a36Sopenharmony_ci if ((brcms_c_stf_attach(wlc) != 0)) { 436162306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: attach: stf_attach " 436262306a36Sopenharmony_ci "failed\n", unit); 436362306a36Sopenharmony_ci err = 68; 436462306a36Sopenharmony_ci goto fail; 436562306a36Sopenharmony_ci } 436662306a36Sopenharmony_ci fail: 436762306a36Sopenharmony_ci return err; 436862306a36Sopenharmony_ci} 436962306a36Sopenharmony_ci 437062306a36Sopenharmony_cistruct brcms_pub *brcms_c_pub(struct brcms_c_info *wlc) 437162306a36Sopenharmony_ci{ 437262306a36Sopenharmony_ci return wlc->pub; 437362306a36Sopenharmony_ci} 437462306a36Sopenharmony_ci 437562306a36Sopenharmony_ci/* low level attach 437662306a36Sopenharmony_ci * run backplane attach, init nvram 437762306a36Sopenharmony_ci * run phy attach 437862306a36Sopenharmony_ci * initialize software state for each core and band 437962306a36Sopenharmony_ci * put the whole chip in reset(driver down state), no clock 438062306a36Sopenharmony_ci */ 438162306a36Sopenharmony_cistatic int brcms_b_attach(struct brcms_c_info *wlc, struct bcma_device *core, 438262306a36Sopenharmony_ci uint unit, bool piomode) 438362306a36Sopenharmony_ci{ 438462306a36Sopenharmony_ci struct brcms_hardware *wlc_hw; 438562306a36Sopenharmony_ci uint err = 0; 438662306a36Sopenharmony_ci uint j; 438762306a36Sopenharmony_ci bool wme = false; 438862306a36Sopenharmony_ci struct shared_phy_params sha_params; 438962306a36Sopenharmony_ci struct wiphy *wiphy = wlc->wiphy; 439062306a36Sopenharmony_ci struct pci_dev *pcidev = core->bus->host_pci; 439162306a36Sopenharmony_ci struct ssb_sprom *sprom = &core->bus->sprom; 439262306a36Sopenharmony_ci 439362306a36Sopenharmony_ci if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) 439462306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, 439562306a36Sopenharmony_ci pcidev->vendor, 439662306a36Sopenharmony_ci pcidev->device); 439762306a36Sopenharmony_ci else 439862306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d: vendor 0x%x device 0x%x\n", unit, 439962306a36Sopenharmony_ci core->bus->boardinfo.vendor, 440062306a36Sopenharmony_ci core->bus->boardinfo.type); 440162306a36Sopenharmony_ci 440262306a36Sopenharmony_ci wme = true; 440362306a36Sopenharmony_ci 440462306a36Sopenharmony_ci wlc_hw = wlc->hw; 440562306a36Sopenharmony_ci wlc_hw->wlc = wlc; 440662306a36Sopenharmony_ci wlc_hw->unit = unit; 440762306a36Sopenharmony_ci wlc_hw->band = wlc_hw->bandstate[0]; 440862306a36Sopenharmony_ci wlc_hw->_piomode = piomode; 440962306a36Sopenharmony_ci 441062306a36Sopenharmony_ci /* populate struct brcms_hardware with default values */ 441162306a36Sopenharmony_ci brcms_b_info_init(wlc_hw); 441262306a36Sopenharmony_ci 441362306a36Sopenharmony_ci /* 441462306a36Sopenharmony_ci * Do the hardware portion of the attach. Also initialize software 441562306a36Sopenharmony_ci * state that depends on the particular hardware we are running. 441662306a36Sopenharmony_ci */ 441762306a36Sopenharmony_ci wlc_hw->sih = ai_attach(core->bus); 441862306a36Sopenharmony_ci if (wlc_hw->sih == NULL) { 441962306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n", 442062306a36Sopenharmony_ci unit); 442162306a36Sopenharmony_ci err = 11; 442262306a36Sopenharmony_ci goto fail; 442362306a36Sopenharmony_ci } 442462306a36Sopenharmony_ci 442562306a36Sopenharmony_ci /* verify again the device is supported */ 442662306a36Sopenharmony_ci if (!brcms_c_chipmatch(core)) { 442762306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported device\n", 442862306a36Sopenharmony_ci unit); 442962306a36Sopenharmony_ci err = 12; 443062306a36Sopenharmony_ci goto fail; 443162306a36Sopenharmony_ci } 443262306a36Sopenharmony_ci 443362306a36Sopenharmony_ci if (core->bus->hosttype == BCMA_HOSTTYPE_PCI) { 443462306a36Sopenharmony_ci wlc_hw->vendorid = pcidev->vendor; 443562306a36Sopenharmony_ci wlc_hw->deviceid = pcidev->device; 443662306a36Sopenharmony_ci } else { 443762306a36Sopenharmony_ci wlc_hw->vendorid = core->bus->boardinfo.vendor; 443862306a36Sopenharmony_ci wlc_hw->deviceid = core->bus->boardinfo.type; 443962306a36Sopenharmony_ci } 444062306a36Sopenharmony_ci 444162306a36Sopenharmony_ci wlc_hw->d11core = core; 444262306a36Sopenharmony_ci wlc_hw->corerev = core->id.rev; 444362306a36Sopenharmony_ci 444462306a36Sopenharmony_ci /* validate chip, chiprev and corerev */ 444562306a36Sopenharmony_ci if (!brcms_c_isgoodchip(wlc_hw)) { 444662306a36Sopenharmony_ci err = 13; 444762306a36Sopenharmony_ci goto fail; 444862306a36Sopenharmony_ci } 444962306a36Sopenharmony_ci 445062306a36Sopenharmony_ci /* initialize power control registers */ 445162306a36Sopenharmony_ci ai_clkctl_init(wlc_hw->sih); 445262306a36Sopenharmony_ci 445362306a36Sopenharmony_ci /* request fastclock and force fastclock for the rest of attach 445462306a36Sopenharmony_ci * bring the d11 core out of reset. 445562306a36Sopenharmony_ci * For PMU chips, the first wlc_clkctl_clk is no-op since core-clk 445662306a36Sopenharmony_ci * is still false; But it will be called again inside wlc_corereset, 445762306a36Sopenharmony_ci * after d11 is out of reset. 445862306a36Sopenharmony_ci */ 445962306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 446062306a36Sopenharmony_ci brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); 446162306a36Sopenharmony_ci 446262306a36Sopenharmony_ci if (!brcms_b_validate_chip_access(wlc_hw)) { 446362306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access " 446462306a36Sopenharmony_ci "failed\n", unit); 446562306a36Sopenharmony_ci err = 14; 446662306a36Sopenharmony_ci goto fail; 446762306a36Sopenharmony_ci } 446862306a36Sopenharmony_ci 446962306a36Sopenharmony_ci /* get the board rev, used just below */ 447062306a36Sopenharmony_ci j = sprom->board_rev; 447162306a36Sopenharmony_ci /* promote srom boardrev of 0xFF to 1 */ 447262306a36Sopenharmony_ci if (j == BOARDREV_PROMOTABLE) 447362306a36Sopenharmony_ci j = BOARDREV_PROMOTED; 447462306a36Sopenharmony_ci wlc_hw->boardrev = (u16) j; 447562306a36Sopenharmony_ci if (!brcms_c_validboardtype(wlc_hw)) { 447662306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom " 447762306a36Sopenharmony_ci "board type (0x%x)" " or revision level (0x%x)\n", 447862306a36Sopenharmony_ci unit, ai_get_boardtype(wlc_hw->sih), 447962306a36Sopenharmony_ci wlc_hw->boardrev); 448062306a36Sopenharmony_ci err = 15; 448162306a36Sopenharmony_ci goto fail; 448262306a36Sopenharmony_ci } 448362306a36Sopenharmony_ci wlc_hw->sromrev = sprom->revision; 448462306a36Sopenharmony_ci wlc_hw->boardflags = sprom->boardflags_lo + (sprom->boardflags_hi << 16); 448562306a36Sopenharmony_ci wlc_hw->boardflags2 = sprom->boardflags2_lo + (sprom->boardflags2_hi << 16); 448662306a36Sopenharmony_ci 448762306a36Sopenharmony_ci if (wlc_hw->boardflags & BFL_NOPLLDOWN) 448862306a36Sopenharmony_ci brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED); 448962306a36Sopenharmony_ci 449062306a36Sopenharmony_ci /* check device id(srom, nvram etc.) to set bands */ 449162306a36Sopenharmony_ci if (wlc_hw->deviceid == BCM43224_D11N_ID || 449262306a36Sopenharmony_ci wlc_hw->deviceid == BCM43224_D11N_ID_VEN1 || 449362306a36Sopenharmony_ci wlc_hw->deviceid == BCM43224_CHIP_ID) 449462306a36Sopenharmony_ci /* Dualband boards */ 449562306a36Sopenharmony_ci wlc_hw->_nbands = 2; 449662306a36Sopenharmony_ci else 449762306a36Sopenharmony_ci wlc_hw->_nbands = 1; 449862306a36Sopenharmony_ci 449962306a36Sopenharmony_ci if ((ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM43225)) 450062306a36Sopenharmony_ci wlc_hw->_nbands = 1; 450162306a36Sopenharmony_ci 450262306a36Sopenharmony_ci /* BMAC_NOTE: remove init of pub values when brcms_c_attach() 450362306a36Sopenharmony_ci * unconditionally does the init of these values 450462306a36Sopenharmony_ci */ 450562306a36Sopenharmony_ci wlc->vendorid = wlc_hw->vendorid; 450662306a36Sopenharmony_ci wlc->deviceid = wlc_hw->deviceid; 450762306a36Sopenharmony_ci wlc->pub->sih = wlc_hw->sih; 450862306a36Sopenharmony_ci wlc->pub->corerev = wlc_hw->corerev; 450962306a36Sopenharmony_ci wlc->pub->sromrev = wlc_hw->sromrev; 451062306a36Sopenharmony_ci wlc->pub->boardrev = wlc_hw->boardrev; 451162306a36Sopenharmony_ci wlc->pub->boardflags = wlc_hw->boardflags; 451262306a36Sopenharmony_ci wlc->pub->boardflags2 = wlc_hw->boardflags2; 451362306a36Sopenharmony_ci wlc->pub->_nbands = wlc_hw->_nbands; 451462306a36Sopenharmony_ci 451562306a36Sopenharmony_ci wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc); 451662306a36Sopenharmony_ci 451762306a36Sopenharmony_ci if (wlc_hw->physhim == NULL) { 451862306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach " 451962306a36Sopenharmony_ci "failed\n", unit); 452062306a36Sopenharmony_ci err = 25; 452162306a36Sopenharmony_ci goto fail; 452262306a36Sopenharmony_ci } 452362306a36Sopenharmony_ci 452462306a36Sopenharmony_ci /* pass all the parameters to wlc_phy_shared_attach in one struct */ 452562306a36Sopenharmony_ci sha_params.sih = wlc_hw->sih; 452662306a36Sopenharmony_ci sha_params.physhim = wlc_hw->physhim; 452762306a36Sopenharmony_ci sha_params.unit = unit; 452862306a36Sopenharmony_ci sha_params.corerev = wlc_hw->corerev; 452962306a36Sopenharmony_ci sha_params.vid = wlc_hw->vendorid; 453062306a36Sopenharmony_ci sha_params.did = wlc_hw->deviceid; 453162306a36Sopenharmony_ci sha_params.chip = ai_get_chip_id(wlc_hw->sih); 453262306a36Sopenharmony_ci sha_params.chiprev = ai_get_chiprev(wlc_hw->sih); 453362306a36Sopenharmony_ci sha_params.chippkg = ai_get_chippkg(wlc_hw->sih); 453462306a36Sopenharmony_ci sha_params.sromrev = wlc_hw->sromrev; 453562306a36Sopenharmony_ci sha_params.boardtype = ai_get_boardtype(wlc_hw->sih); 453662306a36Sopenharmony_ci sha_params.boardrev = wlc_hw->boardrev; 453762306a36Sopenharmony_ci sha_params.boardflags = wlc_hw->boardflags; 453862306a36Sopenharmony_ci sha_params.boardflags2 = wlc_hw->boardflags2; 453962306a36Sopenharmony_ci 454062306a36Sopenharmony_ci /* alloc and save pointer to shared phy state area */ 454162306a36Sopenharmony_ci wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params); 454262306a36Sopenharmony_ci if (!wlc_hw->phy_sh) { 454362306a36Sopenharmony_ci err = 16; 454462306a36Sopenharmony_ci goto fail; 454562306a36Sopenharmony_ci } 454662306a36Sopenharmony_ci 454762306a36Sopenharmony_ci /* initialize software state for each core and band */ 454862306a36Sopenharmony_ci for (j = 0; j < wlc_hw->_nbands; j++) { 454962306a36Sopenharmony_ci /* 455062306a36Sopenharmony_ci * band0 is always 2.4Ghz 455162306a36Sopenharmony_ci * band1, if present, is 5Ghz 455262306a36Sopenharmony_ci */ 455362306a36Sopenharmony_ci 455462306a36Sopenharmony_ci brcms_c_setxband(wlc_hw, j); 455562306a36Sopenharmony_ci 455662306a36Sopenharmony_ci wlc_hw->band->bandunit = j; 455762306a36Sopenharmony_ci wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; 455862306a36Sopenharmony_ci wlc->band->bandunit = j; 455962306a36Sopenharmony_ci wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G; 456062306a36Sopenharmony_ci wlc->core->coreidx = core->core_index; 456162306a36Sopenharmony_ci 456262306a36Sopenharmony_ci wlc_hw->machwcap = bcma_read32(core, D11REGOFFS(machwcap)); 456362306a36Sopenharmony_ci wlc_hw->machwcap_backup = wlc_hw->machwcap; 456462306a36Sopenharmony_ci 456562306a36Sopenharmony_ci /* init tx fifo size */ 456662306a36Sopenharmony_ci WARN_ON(wlc_hw->corerev < XMTFIFOTBL_STARTREV || 456762306a36Sopenharmony_ci (wlc_hw->corerev - XMTFIFOTBL_STARTREV) > 456862306a36Sopenharmony_ci ARRAY_SIZE(xmtfifo_sz)); 456962306a36Sopenharmony_ci wlc_hw->xmtfifo_sz = 457062306a36Sopenharmony_ci xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)]; 457162306a36Sopenharmony_ci WARN_ON(!wlc_hw->xmtfifo_sz[0]); 457262306a36Sopenharmony_ci 457362306a36Sopenharmony_ci /* Get a phy for this band */ 457462306a36Sopenharmony_ci wlc_hw->band->pi = 457562306a36Sopenharmony_ci wlc_phy_attach(wlc_hw->phy_sh, core, 457662306a36Sopenharmony_ci wlc_hw->band->bandtype, 457762306a36Sopenharmony_ci wlc->wiphy); 457862306a36Sopenharmony_ci if (wlc_hw->band->pi == NULL) { 457962306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_" 458062306a36Sopenharmony_ci "attach failed\n", unit); 458162306a36Sopenharmony_ci err = 17; 458262306a36Sopenharmony_ci goto fail; 458362306a36Sopenharmony_ci } 458462306a36Sopenharmony_ci 458562306a36Sopenharmony_ci wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap); 458662306a36Sopenharmony_ci 458762306a36Sopenharmony_ci wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype, 458862306a36Sopenharmony_ci &wlc_hw->band->phyrev, 458962306a36Sopenharmony_ci &wlc_hw->band->radioid, 459062306a36Sopenharmony_ci &wlc_hw->band->radiorev); 459162306a36Sopenharmony_ci wlc_hw->band->abgphy_encore = 459262306a36Sopenharmony_ci wlc_phy_get_encore(wlc_hw->band->pi); 459362306a36Sopenharmony_ci wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi); 459462306a36Sopenharmony_ci wlc_hw->band->core_flags = 459562306a36Sopenharmony_ci wlc_phy_get_coreflags(wlc_hw->band->pi); 459662306a36Sopenharmony_ci 459762306a36Sopenharmony_ci /* verify good phy_type & supported phy revision */ 459862306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc_hw->band)) { 459962306a36Sopenharmony_ci if (NCONF_HAS(wlc_hw->band->phyrev)) 460062306a36Sopenharmony_ci goto good_phy; 460162306a36Sopenharmony_ci else 460262306a36Sopenharmony_ci goto bad_phy; 460362306a36Sopenharmony_ci } else if (BRCMS_ISLCNPHY(wlc_hw->band)) { 460462306a36Sopenharmony_ci if (LCNCONF_HAS(wlc_hw->band->phyrev)) 460562306a36Sopenharmony_ci goto good_phy; 460662306a36Sopenharmony_ci else 460762306a36Sopenharmony_ci goto bad_phy; 460862306a36Sopenharmony_ci } else { 460962306a36Sopenharmony_ci bad_phy: 461062306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported " 461162306a36Sopenharmony_ci "phy type/rev (%d/%d)\n", unit, 461262306a36Sopenharmony_ci wlc_hw->band->phytype, wlc_hw->band->phyrev); 461362306a36Sopenharmony_ci err = 18; 461462306a36Sopenharmony_ci goto fail; 461562306a36Sopenharmony_ci } 461662306a36Sopenharmony_ci 461762306a36Sopenharmony_ci good_phy: 461862306a36Sopenharmony_ci /* 461962306a36Sopenharmony_ci * BMAC_NOTE: wlc->band->pi should not be set below and should 462062306a36Sopenharmony_ci * be done in the high level attach. However we can not make 462162306a36Sopenharmony_ci * that change until all low level access is changed to 462262306a36Sopenharmony_ci * wlc_hw->band->pi. Instead do the wlc->band->pi init below, 462362306a36Sopenharmony_ci * keeping wlc_hw->band->pi as well for incremental update of 462462306a36Sopenharmony_ci * low level fns, and cut over low only init when all fns 462562306a36Sopenharmony_ci * updated. 462662306a36Sopenharmony_ci */ 462762306a36Sopenharmony_ci wlc->band->pi = wlc_hw->band->pi; 462862306a36Sopenharmony_ci wlc->band->phytype = wlc_hw->band->phytype; 462962306a36Sopenharmony_ci wlc->band->phyrev = wlc_hw->band->phyrev; 463062306a36Sopenharmony_ci wlc->band->radioid = wlc_hw->band->radioid; 463162306a36Sopenharmony_ci wlc->band->radiorev = wlc_hw->band->radiorev; 463262306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d: phy %u/%u radio %x/%u\n", unit, 463362306a36Sopenharmony_ci wlc->band->phytype, wlc->band->phyrev, 463462306a36Sopenharmony_ci wlc->band->radioid, wlc->band->radiorev); 463562306a36Sopenharmony_ci /* default contention windows size limits */ 463662306a36Sopenharmony_ci wlc_hw->band->CWmin = APHY_CWMIN; 463762306a36Sopenharmony_ci wlc_hw->band->CWmax = PHY_CWMAX; 463862306a36Sopenharmony_ci 463962306a36Sopenharmony_ci if (!brcms_b_attach_dmapio(wlc, j, wme)) { 464062306a36Sopenharmony_ci err = 19; 464162306a36Sopenharmony_ci goto fail; 464262306a36Sopenharmony_ci } 464362306a36Sopenharmony_ci } 464462306a36Sopenharmony_ci 464562306a36Sopenharmony_ci /* disable core to match driver "down" state */ 464662306a36Sopenharmony_ci brcms_c_coredisable(wlc_hw); 464762306a36Sopenharmony_ci 464862306a36Sopenharmony_ci /* Match driver "down" state */ 464962306a36Sopenharmony_ci bcma_host_pci_down(wlc_hw->d11core->bus); 465062306a36Sopenharmony_ci 465162306a36Sopenharmony_ci /* turn off pll and xtal to match driver "down" state */ 465262306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, OFF); 465362306a36Sopenharmony_ci 465462306a36Sopenharmony_ci /* ******************************************************************* 465562306a36Sopenharmony_ci * The hardware is in the DOWN state at this point. D11 core 465662306a36Sopenharmony_ci * or cores are in reset with clocks off, and the board PLLs 465762306a36Sopenharmony_ci * are off if possible. 465862306a36Sopenharmony_ci * 465962306a36Sopenharmony_ci * Beyond this point, wlc->sbclk == false and chip registers 466062306a36Sopenharmony_ci * should not be touched. 466162306a36Sopenharmony_ci ********************************************************************* 466262306a36Sopenharmony_ci */ 466362306a36Sopenharmony_ci 466462306a36Sopenharmony_ci /* init etheraddr state variables */ 466562306a36Sopenharmony_ci brcms_c_get_macaddr(wlc_hw, wlc_hw->etheraddr); 466662306a36Sopenharmony_ci 466762306a36Sopenharmony_ci if (is_broadcast_ether_addr(wlc_hw->etheraddr) || 466862306a36Sopenharmony_ci is_zero_ether_addr(wlc_hw->etheraddr)) { 466962306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr\n", 467062306a36Sopenharmony_ci unit); 467162306a36Sopenharmony_ci err = 22; 467262306a36Sopenharmony_ci goto fail; 467362306a36Sopenharmony_ci } 467462306a36Sopenharmony_ci 467562306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "deviceid 0x%x nbands %d board 0x%x\n", 467662306a36Sopenharmony_ci wlc_hw->deviceid, wlc_hw->_nbands, 467762306a36Sopenharmony_ci ai_get_boardtype(wlc_hw->sih)); 467862306a36Sopenharmony_ci 467962306a36Sopenharmony_ci return err; 468062306a36Sopenharmony_ci 468162306a36Sopenharmony_ci fail: 468262306a36Sopenharmony_ci wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit, 468362306a36Sopenharmony_ci err); 468462306a36Sopenharmony_ci return err; 468562306a36Sopenharmony_ci} 468662306a36Sopenharmony_ci 468762306a36Sopenharmony_cistatic bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc) 468862306a36Sopenharmony_ci{ 468962306a36Sopenharmony_ci int aa; 469062306a36Sopenharmony_ci uint unit; 469162306a36Sopenharmony_ci int bandtype; 469262306a36Sopenharmony_ci struct ssb_sprom *sprom = &wlc->hw->d11core->bus->sprom; 469362306a36Sopenharmony_ci 469462306a36Sopenharmony_ci unit = wlc->pub->unit; 469562306a36Sopenharmony_ci bandtype = wlc->band->bandtype; 469662306a36Sopenharmony_ci 469762306a36Sopenharmony_ci /* get antennas available */ 469862306a36Sopenharmony_ci if (bandtype == BRCM_BAND_5G) 469962306a36Sopenharmony_ci aa = sprom->ant_available_a; 470062306a36Sopenharmony_ci else 470162306a36Sopenharmony_ci aa = sprom->ant_available_bg; 470262306a36Sopenharmony_ci 470362306a36Sopenharmony_ci if ((aa < 1) || (aa > 15)) { 470462306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "wl%d: %s: Invalid antennas available in" 470562306a36Sopenharmony_ci " srom (0x%x), using 3\n", unit, __func__, aa); 470662306a36Sopenharmony_ci aa = 3; 470762306a36Sopenharmony_ci } 470862306a36Sopenharmony_ci 470962306a36Sopenharmony_ci /* reset the defaults if we have a single antenna */ 471062306a36Sopenharmony_ci if (aa == 1) { 471162306a36Sopenharmony_ci wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_0; 471262306a36Sopenharmony_ci wlc->stf->txant = ANT_TX_FORCE_0; 471362306a36Sopenharmony_ci } else if (aa == 2) { 471462306a36Sopenharmony_ci wlc->stf->ant_rx_ovr = ANT_RX_DIV_FORCE_1; 471562306a36Sopenharmony_ci wlc->stf->txant = ANT_TX_FORCE_1; 471662306a36Sopenharmony_ci } else { 471762306a36Sopenharmony_ci } 471862306a36Sopenharmony_ci 471962306a36Sopenharmony_ci /* Compute Antenna Gain */ 472062306a36Sopenharmony_ci if (bandtype == BRCM_BAND_5G) 472162306a36Sopenharmony_ci wlc->band->antgain = sprom->antenna_gain.a1; 472262306a36Sopenharmony_ci else 472362306a36Sopenharmony_ci wlc->band->antgain = sprom->antenna_gain.a0; 472462306a36Sopenharmony_ci 472562306a36Sopenharmony_ci return true; 472662306a36Sopenharmony_ci} 472762306a36Sopenharmony_ci 472862306a36Sopenharmony_cistatic void brcms_c_bss_default_init(struct brcms_c_info *wlc) 472962306a36Sopenharmony_ci{ 473062306a36Sopenharmony_ci u16 chanspec; 473162306a36Sopenharmony_ci struct brcms_band *band; 473262306a36Sopenharmony_ci struct brcms_bss_info *bi = wlc->default_bss; 473362306a36Sopenharmony_ci 473462306a36Sopenharmony_ci /* init default and target BSS with some sane initial values */ 473562306a36Sopenharmony_ci memset(bi, 0, sizeof(*bi)); 473662306a36Sopenharmony_ci bi->beacon_period = BEACON_INTERVAL_DEFAULT; 473762306a36Sopenharmony_ci 473862306a36Sopenharmony_ci /* fill the default channel as the first valid channel 473962306a36Sopenharmony_ci * starting from the 2G channels 474062306a36Sopenharmony_ci */ 474162306a36Sopenharmony_ci chanspec = ch20mhz_chspec(1); 474262306a36Sopenharmony_ci wlc->home_chanspec = bi->chanspec = chanspec; 474362306a36Sopenharmony_ci 474462306a36Sopenharmony_ci /* find the band of our default channel */ 474562306a36Sopenharmony_ci band = wlc->band; 474662306a36Sopenharmony_ci if (wlc->pub->_nbands > 1 && 474762306a36Sopenharmony_ci band->bandunit != chspec_bandunit(chanspec)) 474862306a36Sopenharmony_ci band = wlc->bandstate[OTHERBANDUNIT(wlc)]; 474962306a36Sopenharmony_ci 475062306a36Sopenharmony_ci /* init bss rates to the band specific default rate set */ 475162306a36Sopenharmony_ci brcms_c_rateset_default(&bi->rateset, NULL, band->phytype, 475262306a36Sopenharmony_ci band->bandtype, false, BRCMS_RATE_MASK_FULL, 475362306a36Sopenharmony_ci (bool) (wlc->pub->_n_enab & SUPPORT_11N), 475462306a36Sopenharmony_ci brcms_chspec_bw(chanspec), wlc->stf->txstreams); 475562306a36Sopenharmony_ci 475662306a36Sopenharmony_ci if (wlc->pub->_n_enab & SUPPORT_11N) 475762306a36Sopenharmony_ci bi->flags |= BRCMS_BSS_HT; 475862306a36Sopenharmony_ci} 475962306a36Sopenharmony_ci 476062306a36Sopenharmony_cistatic void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap) 476162306a36Sopenharmony_ci{ 476262306a36Sopenharmony_ci uint i; 476362306a36Sopenharmony_ci struct brcms_band *band; 476462306a36Sopenharmony_ci 476562306a36Sopenharmony_ci for (i = 0; i < wlc->pub->_nbands; i++) { 476662306a36Sopenharmony_ci band = wlc->bandstate[i]; 476762306a36Sopenharmony_ci if (band->bandtype == BRCM_BAND_5G) { 476862306a36Sopenharmony_ci if ((bwcap == BRCMS_N_BW_40ALL) 476962306a36Sopenharmony_ci || (bwcap == BRCMS_N_BW_20IN2G_40IN5G)) 477062306a36Sopenharmony_ci band->mimo_cap_40 = true; 477162306a36Sopenharmony_ci else 477262306a36Sopenharmony_ci band->mimo_cap_40 = false; 477362306a36Sopenharmony_ci } else { 477462306a36Sopenharmony_ci if (bwcap == BRCMS_N_BW_40ALL) 477562306a36Sopenharmony_ci band->mimo_cap_40 = true; 477662306a36Sopenharmony_ci else 477762306a36Sopenharmony_ci band->mimo_cap_40 = false; 477862306a36Sopenharmony_ci } 477962306a36Sopenharmony_ci } 478062306a36Sopenharmony_ci} 478162306a36Sopenharmony_ci 478262306a36Sopenharmony_cistatic void brcms_c_timers_deinit(struct brcms_c_info *wlc) 478362306a36Sopenharmony_ci{ 478462306a36Sopenharmony_ci /* free timer state */ 478562306a36Sopenharmony_ci if (wlc->wdtimer) { 478662306a36Sopenharmony_ci brcms_free_timer(wlc->wdtimer); 478762306a36Sopenharmony_ci wlc->wdtimer = NULL; 478862306a36Sopenharmony_ci } 478962306a36Sopenharmony_ci if (wlc->radio_timer) { 479062306a36Sopenharmony_ci brcms_free_timer(wlc->radio_timer); 479162306a36Sopenharmony_ci wlc->radio_timer = NULL; 479262306a36Sopenharmony_ci } 479362306a36Sopenharmony_ci} 479462306a36Sopenharmony_ci 479562306a36Sopenharmony_cistatic void brcms_c_detach_module(struct brcms_c_info *wlc) 479662306a36Sopenharmony_ci{ 479762306a36Sopenharmony_ci if (wlc->asi) { 479862306a36Sopenharmony_ci brcms_c_antsel_detach(wlc->asi); 479962306a36Sopenharmony_ci wlc->asi = NULL; 480062306a36Sopenharmony_ci } 480162306a36Sopenharmony_ci 480262306a36Sopenharmony_ci if (wlc->ampdu) { 480362306a36Sopenharmony_ci brcms_c_ampdu_detach(wlc->ampdu); 480462306a36Sopenharmony_ci wlc->ampdu = NULL; 480562306a36Sopenharmony_ci } 480662306a36Sopenharmony_ci 480762306a36Sopenharmony_ci brcms_c_stf_detach(wlc); 480862306a36Sopenharmony_ci} 480962306a36Sopenharmony_ci 481062306a36Sopenharmony_ci/* 481162306a36Sopenharmony_ci * low level detach 481262306a36Sopenharmony_ci */ 481362306a36Sopenharmony_cistatic void brcms_b_detach(struct brcms_c_info *wlc) 481462306a36Sopenharmony_ci{ 481562306a36Sopenharmony_ci uint i; 481662306a36Sopenharmony_ci struct brcms_hw_band *band; 481762306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 481862306a36Sopenharmony_ci 481962306a36Sopenharmony_ci brcms_b_detach_dmapio(wlc_hw); 482062306a36Sopenharmony_ci 482162306a36Sopenharmony_ci band = wlc_hw->band; 482262306a36Sopenharmony_ci for (i = 0; i < wlc_hw->_nbands; i++) { 482362306a36Sopenharmony_ci if (band->pi) { 482462306a36Sopenharmony_ci /* Detach this band's phy */ 482562306a36Sopenharmony_ci wlc_phy_detach(band->pi); 482662306a36Sopenharmony_ci band->pi = NULL; 482762306a36Sopenharmony_ci } 482862306a36Sopenharmony_ci band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)]; 482962306a36Sopenharmony_ci } 483062306a36Sopenharmony_ci 483162306a36Sopenharmony_ci /* Free shared phy state */ 483262306a36Sopenharmony_ci kfree(wlc_hw->phy_sh); 483362306a36Sopenharmony_ci 483462306a36Sopenharmony_ci wlc_phy_shim_detach(wlc_hw->physhim); 483562306a36Sopenharmony_ci 483662306a36Sopenharmony_ci if (wlc_hw->sih) { 483762306a36Sopenharmony_ci ai_detach(wlc_hw->sih); 483862306a36Sopenharmony_ci wlc_hw->sih = NULL; 483962306a36Sopenharmony_ci } 484062306a36Sopenharmony_ci} 484162306a36Sopenharmony_ci 484262306a36Sopenharmony_ci/* 484362306a36Sopenharmony_ci * Return a count of the number of driver callbacks still pending. 484462306a36Sopenharmony_ci * 484562306a36Sopenharmony_ci * General policy is that brcms_c_detach can only dealloc/free software states. 484662306a36Sopenharmony_ci * It can NOT touch hardware registers since the d11core may be in reset and 484762306a36Sopenharmony_ci * clock may not be available. 484862306a36Sopenharmony_ci * One exception is sb register access, which is possible if crystal is turned 484962306a36Sopenharmony_ci * on after "down" state, driver should avoid software timer with the exception 485062306a36Sopenharmony_ci * of radio_monitor. 485162306a36Sopenharmony_ci */ 485262306a36Sopenharmony_ciuint brcms_c_detach(struct brcms_c_info *wlc) 485362306a36Sopenharmony_ci{ 485462306a36Sopenharmony_ci uint callbacks; 485562306a36Sopenharmony_ci 485662306a36Sopenharmony_ci if (wlc == NULL) 485762306a36Sopenharmony_ci return 0; 485862306a36Sopenharmony_ci 485962306a36Sopenharmony_ci brcms_b_detach(wlc); 486062306a36Sopenharmony_ci 486162306a36Sopenharmony_ci /* delete software timers */ 486262306a36Sopenharmony_ci callbacks = 0; 486362306a36Sopenharmony_ci if (!brcms_c_radio_monitor_stop(wlc)) 486462306a36Sopenharmony_ci callbacks++; 486562306a36Sopenharmony_ci 486662306a36Sopenharmony_ci brcms_c_channel_mgr_detach(wlc->cmi); 486762306a36Sopenharmony_ci 486862306a36Sopenharmony_ci brcms_c_timers_deinit(wlc); 486962306a36Sopenharmony_ci 487062306a36Sopenharmony_ci brcms_c_detach_module(wlc); 487162306a36Sopenharmony_ci 487262306a36Sopenharmony_ci brcms_c_detach_mfree(wlc); 487362306a36Sopenharmony_ci return callbacks; 487462306a36Sopenharmony_ci} 487562306a36Sopenharmony_ci 487662306a36Sopenharmony_ci/* update state that depends on the current value of "ap" */ 487762306a36Sopenharmony_cistatic void brcms_c_ap_upd(struct brcms_c_info *wlc) 487862306a36Sopenharmony_ci{ 487962306a36Sopenharmony_ci /* STA-BSS; short capable */ 488062306a36Sopenharmony_ci wlc->PLCPHdr_override = BRCMS_PLCP_SHORT; 488162306a36Sopenharmony_ci} 488262306a36Sopenharmony_ci 488362306a36Sopenharmony_ci/* Initialize just the hardware when coming out of POR or S3/S5 system states */ 488462306a36Sopenharmony_cistatic void brcms_b_hw_up(struct brcms_hardware *wlc_hw) 488562306a36Sopenharmony_ci{ 488662306a36Sopenharmony_ci if (wlc_hw->wlc->pub->hw_up) 488762306a36Sopenharmony_ci return; 488862306a36Sopenharmony_ci 488962306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); 489062306a36Sopenharmony_ci 489162306a36Sopenharmony_ci /* 489262306a36Sopenharmony_ci * Enable pll and xtal, initialize the power control registers, 489362306a36Sopenharmony_ci * and force fastclock for the remainder of brcms_c_up(). 489462306a36Sopenharmony_ci */ 489562306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, ON); 489662306a36Sopenharmony_ci ai_clkctl_init(wlc_hw->sih); 489762306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 489862306a36Sopenharmony_ci 489962306a36Sopenharmony_ci /* 490062306a36Sopenharmony_ci * TODO: test suspend/resume 490162306a36Sopenharmony_ci * 490262306a36Sopenharmony_ci * AI chip doesn't restore bar0win2 on 490362306a36Sopenharmony_ci * hibernation/resume, need sw fixup 490462306a36Sopenharmony_ci */ 490562306a36Sopenharmony_ci 490662306a36Sopenharmony_ci /* 490762306a36Sopenharmony_ci * Inform phy that a POR reset has occurred so 490862306a36Sopenharmony_ci * it does a complete phy init 490962306a36Sopenharmony_ci */ 491062306a36Sopenharmony_ci wlc_phy_por_inform(wlc_hw->band->pi); 491162306a36Sopenharmony_ci 491262306a36Sopenharmony_ci wlc_hw->ucode_loaded = false; 491362306a36Sopenharmony_ci wlc_hw->wlc->pub->hw_up = true; 491462306a36Sopenharmony_ci 491562306a36Sopenharmony_ci if ((wlc_hw->boardflags & BFL_FEM) 491662306a36Sopenharmony_ci && (ai_get_chip_id(wlc_hw->sih) == BCMA_CHIP_ID_BCM4313)) { 491762306a36Sopenharmony_ci if (! 491862306a36Sopenharmony_ci (wlc_hw->boardrev >= 0x1250 491962306a36Sopenharmony_ci && (wlc_hw->boardflags & BFL_FEM_BT))) 492062306a36Sopenharmony_ci ai_epa_4313war(wlc_hw->sih); 492162306a36Sopenharmony_ci } 492262306a36Sopenharmony_ci} 492362306a36Sopenharmony_ci 492462306a36Sopenharmony_cistatic int brcms_b_up_prep(struct brcms_hardware *wlc_hw) 492562306a36Sopenharmony_ci{ 492662306a36Sopenharmony_ci brcms_dbg_info(wlc_hw->d11core, "wl%d\n", wlc_hw->unit); 492762306a36Sopenharmony_ci 492862306a36Sopenharmony_ci /* 492962306a36Sopenharmony_ci * Enable pll and xtal, initialize the power control registers, 493062306a36Sopenharmony_ci * and force fastclock for the remainder of brcms_c_up(). 493162306a36Sopenharmony_ci */ 493262306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, ON); 493362306a36Sopenharmony_ci ai_clkctl_init(wlc_hw->sih); 493462306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 493562306a36Sopenharmony_ci 493662306a36Sopenharmony_ci /* 493762306a36Sopenharmony_ci * Configure pci/pcmcia here instead of in brcms_c_attach() 493862306a36Sopenharmony_ci * to allow mfg hotswap: down, hotswap (chip power cycle), up. 493962306a36Sopenharmony_ci */ 494062306a36Sopenharmony_ci bcma_host_pci_irq_ctl(wlc_hw->d11core->bus, wlc_hw->d11core, 494162306a36Sopenharmony_ci true); 494262306a36Sopenharmony_ci 494362306a36Sopenharmony_ci /* 494462306a36Sopenharmony_ci * Need to read the hwradio status here to cover the case where the 494562306a36Sopenharmony_ci * system is loaded with the hw radio disabled. We do not want to 494662306a36Sopenharmony_ci * bring the driver up in this case. 494762306a36Sopenharmony_ci */ 494862306a36Sopenharmony_ci if (brcms_b_radio_read_hwdisabled(wlc_hw)) { 494962306a36Sopenharmony_ci /* put SB PCI in down state again */ 495062306a36Sopenharmony_ci bcma_host_pci_down(wlc_hw->d11core->bus); 495162306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, OFF); 495262306a36Sopenharmony_ci return -ENOMEDIUM; 495362306a36Sopenharmony_ci } 495462306a36Sopenharmony_ci 495562306a36Sopenharmony_ci bcma_host_pci_up(wlc_hw->d11core->bus); 495662306a36Sopenharmony_ci 495762306a36Sopenharmony_ci /* reset the d11 core */ 495862306a36Sopenharmony_ci brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS); 495962306a36Sopenharmony_ci 496062306a36Sopenharmony_ci return 0; 496162306a36Sopenharmony_ci} 496262306a36Sopenharmony_ci 496362306a36Sopenharmony_cistatic int brcms_b_up_finish(struct brcms_hardware *wlc_hw) 496462306a36Sopenharmony_ci{ 496562306a36Sopenharmony_ci wlc_hw->up = true; 496662306a36Sopenharmony_ci wlc_phy_hw_state_upd(wlc_hw->band->pi, true); 496762306a36Sopenharmony_ci 496862306a36Sopenharmony_ci /* FULLY enable dynamic power control and d11 core interrupt */ 496962306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_DYNAMIC); 497062306a36Sopenharmony_ci brcms_intrson(wlc_hw->wlc->wl); 497162306a36Sopenharmony_ci return 0; 497262306a36Sopenharmony_ci} 497362306a36Sopenharmony_ci 497462306a36Sopenharmony_ci/* 497562306a36Sopenharmony_ci * Write WME tunable parameters for retransmit/max rate 497662306a36Sopenharmony_ci * from wlc struct to ucode 497762306a36Sopenharmony_ci */ 497862306a36Sopenharmony_cistatic void brcms_c_wme_retries_write(struct brcms_c_info *wlc) 497962306a36Sopenharmony_ci{ 498062306a36Sopenharmony_ci int ac; 498162306a36Sopenharmony_ci 498262306a36Sopenharmony_ci /* Need clock to do this */ 498362306a36Sopenharmony_ci if (!wlc->clk) 498462306a36Sopenharmony_ci return; 498562306a36Sopenharmony_ci 498662306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 498762306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_AC_TXLMT_ADDR(ac), 498862306a36Sopenharmony_ci wlc->wme_retries[ac]); 498962306a36Sopenharmony_ci} 499062306a36Sopenharmony_ci 499162306a36Sopenharmony_ci/* make interface operational */ 499262306a36Sopenharmony_ciint brcms_c_up(struct brcms_c_info *wlc) 499362306a36Sopenharmony_ci{ 499462306a36Sopenharmony_ci struct ieee80211_channel *ch; 499562306a36Sopenharmony_ci 499662306a36Sopenharmony_ci brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); 499762306a36Sopenharmony_ci 499862306a36Sopenharmony_ci /* HW is turned off so don't try to access it */ 499962306a36Sopenharmony_ci if (wlc->pub->hw_off || brcms_deviceremoved(wlc)) 500062306a36Sopenharmony_ci return -ENOMEDIUM; 500162306a36Sopenharmony_ci 500262306a36Sopenharmony_ci if (!wlc->pub->hw_up) { 500362306a36Sopenharmony_ci brcms_b_hw_up(wlc->hw); 500462306a36Sopenharmony_ci wlc->pub->hw_up = true; 500562306a36Sopenharmony_ci } 500662306a36Sopenharmony_ci 500762306a36Sopenharmony_ci if ((wlc->pub->boardflags & BFL_FEM) 500862306a36Sopenharmony_ci && (ai_get_chip_id(wlc->hw->sih) == BCMA_CHIP_ID_BCM4313)) { 500962306a36Sopenharmony_ci if (wlc->pub->boardrev >= 0x1250 501062306a36Sopenharmony_ci && (wlc->pub->boardflags & BFL_FEM_BT)) 501162306a36Sopenharmony_ci brcms_b_mhf(wlc->hw, MHF5, MHF5_4313_GPIOCTRL, 501262306a36Sopenharmony_ci MHF5_4313_GPIOCTRL, BRCM_BAND_ALL); 501362306a36Sopenharmony_ci else 501462306a36Sopenharmony_ci brcms_b_mhf(wlc->hw, MHF4, MHF4_EXTPA_ENABLE, 501562306a36Sopenharmony_ci MHF4_EXTPA_ENABLE, BRCM_BAND_ALL); 501662306a36Sopenharmony_ci } 501762306a36Sopenharmony_ci 501862306a36Sopenharmony_ci /* 501962306a36Sopenharmony_ci * Need to read the hwradio status here to cover the case where the 502062306a36Sopenharmony_ci * system is loaded with the hw radio disabled. We do not want to bring 502162306a36Sopenharmony_ci * the driver up in this case. If radio is disabled, abort up, lower 502262306a36Sopenharmony_ci * power, start radio timer and return 0(for NDIS) don't call 502362306a36Sopenharmony_ci * radio_update to avoid looping brcms_c_up. 502462306a36Sopenharmony_ci * 502562306a36Sopenharmony_ci * brcms_b_up_prep() returns either 0 or -BCME_RADIOOFF only 502662306a36Sopenharmony_ci */ 502762306a36Sopenharmony_ci if (!wlc->pub->radio_disabled) { 502862306a36Sopenharmony_ci int status = brcms_b_up_prep(wlc->hw); 502962306a36Sopenharmony_ci if (status == -ENOMEDIUM) { 503062306a36Sopenharmony_ci if (!mboolisset 503162306a36Sopenharmony_ci (wlc->pub->radio_disabled, WL_RADIO_HW_DISABLE)) { 503262306a36Sopenharmony_ci struct brcms_bss_cfg *bsscfg = wlc->bsscfg; 503362306a36Sopenharmony_ci mboolset(wlc->pub->radio_disabled, 503462306a36Sopenharmony_ci WL_RADIO_HW_DISABLE); 503562306a36Sopenharmony_ci if (bsscfg->type == BRCMS_TYPE_STATION || 503662306a36Sopenharmony_ci bsscfg->type == BRCMS_TYPE_ADHOC) 503762306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 503862306a36Sopenharmony_ci "wl%d: up: rfdisable -> " 503962306a36Sopenharmony_ci "bsscfg_disable()\n", 504062306a36Sopenharmony_ci wlc->pub->unit); 504162306a36Sopenharmony_ci } 504262306a36Sopenharmony_ci } 504362306a36Sopenharmony_ci } 504462306a36Sopenharmony_ci 504562306a36Sopenharmony_ci if (wlc->pub->radio_disabled) { 504662306a36Sopenharmony_ci brcms_c_radio_monitor_start(wlc); 504762306a36Sopenharmony_ci return 0; 504862306a36Sopenharmony_ci } 504962306a36Sopenharmony_ci 505062306a36Sopenharmony_ci /* brcms_b_up_prep has done brcms_c_corereset(). so clk is on, set it */ 505162306a36Sopenharmony_ci wlc->clk = true; 505262306a36Sopenharmony_ci 505362306a36Sopenharmony_ci brcms_c_radio_monitor_stop(wlc); 505462306a36Sopenharmony_ci 505562306a36Sopenharmony_ci /* Set EDCF hostflags */ 505662306a36Sopenharmony_ci brcms_b_mhf(wlc->hw, MHF1, MHF1_EDCF, MHF1_EDCF, BRCM_BAND_ALL); 505762306a36Sopenharmony_ci 505862306a36Sopenharmony_ci brcms_init(wlc->wl); 505962306a36Sopenharmony_ci wlc->pub->up = true; 506062306a36Sopenharmony_ci 506162306a36Sopenharmony_ci if (wlc->bandinit_pending) { 506262306a36Sopenharmony_ci ch = wlc->pub->ieee_hw->conf.chandef.chan; 506362306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc); 506462306a36Sopenharmony_ci brcms_c_set_chanspec(wlc, ch20mhz_chspec(ch->hw_value)); 506562306a36Sopenharmony_ci wlc->bandinit_pending = false; 506662306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 506762306a36Sopenharmony_ci } 506862306a36Sopenharmony_ci 506962306a36Sopenharmony_ci brcms_b_up_finish(wlc->hw); 507062306a36Sopenharmony_ci 507162306a36Sopenharmony_ci /* Program the TX wme params with the current settings */ 507262306a36Sopenharmony_ci brcms_c_wme_retries_write(wlc); 507362306a36Sopenharmony_ci 507462306a36Sopenharmony_ci /* start one second watchdog timer */ 507562306a36Sopenharmony_ci brcms_add_timer(wlc->wdtimer, TIMER_INTERVAL_WATCHDOG, true); 507662306a36Sopenharmony_ci wlc->WDarmed = true; 507762306a36Sopenharmony_ci 507862306a36Sopenharmony_ci /* ensure antenna config is up to date */ 507962306a36Sopenharmony_ci brcms_c_stf_phy_txant_upd(wlc); 508062306a36Sopenharmony_ci /* ensure LDPC config is in sync */ 508162306a36Sopenharmony_ci brcms_c_ht_update_ldpc(wlc, wlc->stf->ldpc); 508262306a36Sopenharmony_ci 508362306a36Sopenharmony_ci return 0; 508462306a36Sopenharmony_ci} 508562306a36Sopenharmony_ci 508662306a36Sopenharmony_cistatic int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw) 508762306a36Sopenharmony_ci{ 508862306a36Sopenharmony_ci bool dev_gone; 508962306a36Sopenharmony_ci uint callbacks = 0; 509062306a36Sopenharmony_ci 509162306a36Sopenharmony_ci if (!wlc_hw->up) 509262306a36Sopenharmony_ci return callbacks; 509362306a36Sopenharmony_ci 509462306a36Sopenharmony_ci dev_gone = brcms_deviceremoved(wlc_hw->wlc); 509562306a36Sopenharmony_ci 509662306a36Sopenharmony_ci /* disable interrupts */ 509762306a36Sopenharmony_ci if (dev_gone) 509862306a36Sopenharmony_ci wlc_hw->wlc->macintmask = 0; 509962306a36Sopenharmony_ci else { 510062306a36Sopenharmony_ci /* now disable interrupts */ 510162306a36Sopenharmony_ci brcms_intrsoff(wlc_hw->wlc->wl); 510262306a36Sopenharmony_ci 510362306a36Sopenharmony_ci /* ensure we're running on the pll clock again */ 510462306a36Sopenharmony_ci brcms_b_clkctl_clk(wlc_hw, BCMA_CLKMODE_FAST); 510562306a36Sopenharmony_ci } 510662306a36Sopenharmony_ci /* down phy at the last of this stage */ 510762306a36Sopenharmony_ci callbacks += wlc_phy_down(wlc_hw->band->pi); 510862306a36Sopenharmony_ci 510962306a36Sopenharmony_ci return callbacks; 511062306a36Sopenharmony_ci} 511162306a36Sopenharmony_ci 511262306a36Sopenharmony_cistatic int brcms_b_down_finish(struct brcms_hardware *wlc_hw) 511362306a36Sopenharmony_ci{ 511462306a36Sopenharmony_ci uint callbacks = 0; 511562306a36Sopenharmony_ci bool dev_gone; 511662306a36Sopenharmony_ci 511762306a36Sopenharmony_ci if (!wlc_hw->up) 511862306a36Sopenharmony_ci return callbacks; 511962306a36Sopenharmony_ci 512062306a36Sopenharmony_ci wlc_hw->up = false; 512162306a36Sopenharmony_ci wlc_phy_hw_state_upd(wlc_hw->band->pi, false); 512262306a36Sopenharmony_ci 512362306a36Sopenharmony_ci dev_gone = brcms_deviceremoved(wlc_hw->wlc); 512462306a36Sopenharmony_ci 512562306a36Sopenharmony_ci if (dev_gone) { 512662306a36Sopenharmony_ci wlc_hw->sbclk = false; 512762306a36Sopenharmony_ci wlc_hw->clk = false; 512862306a36Sopenharmony_ci wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false); 512962306a36Sopenharmony_ci 513062306a36Sopenharmony_ci /* reclaim any posted packets */ 513162306a36Sopenharmony_ci brcms_c_flushqueues(wlc_hw->wlc); 513262306a36Sopenharmony_ci } else { 513362306a36Sopenharmony_ci 513462306a36Sopenharmony_ci /* Reset and disable the core */ 513562306a36Sopenharmony_ci if (bcma_core_is_enabled(wlc_hw->d11core)) { 513662306a36Sopenharmony_ci if (bcma_read32(wlc_hw->d11core, 513762306a36Sopenharmony_ci D11REGOFFS(maccontrol)) & MCTL_EN_MAC) 513862306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc_hw->wlc); 513962306a36Sopenharmony_ci callbacks += brcms_reset(wlc_hw->wlc->wl); 514062306a36Sopenharmony_ci brcms_c_coredisable(wlc_hw); 514162306a36Sopenharmony_ci } 514262306a36Sopenharmony_ci 514362306a36Sopenharmony_ci /* turn off primary xtal and pll */ 514462306a36Sopenharmony_ci if (!wlc_hw->noreset) { 514562306a36Sopenharmony_ci bcma_host_pci_down(wlc_hw->d11core->bus); 514662306a36Sopenharmony_ci brcms_b_xtal(wlc_hw, OFF); 514762306a36Sopenharmony_ci } 514862306a36Sopenharmony_ci } 514962306a36Sopenharmony_ci 515062306a36Sopenharmony_ci return callbacks; 515162306a36Sopenharmony_ci} 515262306a36Sopenharmony_ci 515362306a36Sopenharmony_ci/* 515462306a36Sopenharmony_ci * Mark the interface nonoperational, stop the software mechanisms, 515562306a36Sopenharmony_ci * disable the hardware, free any transient buffer state. 515662306a36Sopenharmony_ci * Return a count of the number of driver callbacks still pending. 515762306a36Sopenharmony_ci */ 515862306a36Sopenharmony_ciuint brcms_c_down(struct brcms_c_info *wlc) 515962306a36Sopenharmony_ci{ 516062306a36Sopenharmony_ci 516162306a36Sopenharmony_ci uint callbacks = 0; 516262306a36Sopenharmony_ci int i; 516362306a36Sopenharmony_ci 516462306a36Sopenharmony_ci brcms_dbg_info(wlc->hw->d11core, "wl%d\n", wlc->pub->unit); 516562306a36Sopenharmony_ci 516662306a36Sopenharmony_ci /* check if we are already in the going down path */ 516762306a36Sopenharmony_ci if (wlc->going_down) { 516862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 516962306a36Sopenharmony_ci "wl%d: %s: Driver going down so return\n", 517062306a36Sopenharmony_ci wlc->pub->unit, __func__); 517162306a36Sopenharmony_ci return 0; 517262306a36Sopenharmony_ci } 517362306a36Sopenharmony_ci if (!wlc->pub->up) 517462306a36Sopenharmony_ci return callbacks; 517562306a36Sopenharmony_ci 517662306a36Sopenharmony_ci wlc->going_down = true; 517762306a36Sopenharmony_ci 517862306a36Sopenharmony_ci callbacks += brcms_b_bmac_down_prep(wlc->hw); 517962306a36Sopenharmony_ci 518062306a36Sopenharmony_ci brcms_deviceremoved(wlc); 518162306a36Sopenharmony_ci 518262306a36Sopenharmony_ci /* Call any registered down handlers */ 518362306a36Sopenharmony_ci for (i = 0; i < BRCMS_MAXMODULES; i++) { 518462306a36Sopenharmony_ci if (wlc->modulecb[i].down_fn) 518562306a36Sopenharmony_ci callbacks += 518662306a36Sopenharmony_ci wlc->modulecb[i].down_fn(wlc->modulecb[i].hdl); 518762306a36Sopenharmony_ci } 518862306a36Sopenharmony_ci 518962306a36Sopenharmony_ci /* cancel the watchdog timer */ 519062306a36Sopenharmony_ci if (wlc->WDarmed) { 519162306a36Sopenharmony_ci if (!brcms_del_timer(wlc->wdtimer)) 519262306a36Sopenharmony_ci callbacks++; 519362306a36Sopenharmony_ci wlc->WDarmed = false; 519462306a36Sopenharmony_ci } 519562306a36Sopenharmony_ci 519662306a36Sopenharmony_ci wlc->pub->up = false; 519762306a36Sopenharmony_ci 519862306a36Sopenharmony_ci wlc_phy_mute_upd(wlc->band->pi, false, PHY_MUTE_ALL); 519962306a36Sopenharmony_ci 520062306a36Sopenharmony_ci callbacks += brcms_b_down_finish(wlc->hw); 520162306a36Sopenharmony_ci 520262306a36Sopenharmony_ci /* brcms_b_down_finish has done brcms_c_coredisable(). so clk is off */ 520362306a36Sopenharmony_ci wlc->clk = false; 520462306a36Sopenharmony_ci 520562306a36Sopenharmony_ci wlc->going_down = false; 520662306a36Sopenharmony_ci return callbacks; 520762306a36Sopenharmony_ci} 520862306a36Sopenharmony_ci 520962306a36Sopenharmony_ci/* Set the current gmode configuration */ 521062306a36Sopenharmony_ciint brcms_c_set_gmode(struct brcms_c_info *wlc, u8 gmode, bool config) 521162306a36Sopenharmony_ci{ 521262306a36Sopenharmony_ci int ret = 0; 521362306a36Sopenharmony_ci uint i; 521462306a36Sopenharmony_ci struct brcms_c_rateset rs; 521562306a36Sopenharmony_ci /* Default to 54g Auto */ 521662306a36Sopenharmony_ci /* Advertise and use shortslot (-1/0/1 Auto/Off/On) */ 521762306a36Sopenharmony_ci s8 shortslot = BRCMS_SHORTSLOT_AUTO; 521862306a36Sopenharmony_ci bool ofdm_basic = false; /* Make 6, 12, and 24 basic rates */ 521962306a36Sopenharmony_ci struct brcms_band *band; 522062306a36Sopenharmony_ci 522162306a36Sopenharmony_ci /* if N-support is enabled, allow Gmode set as long as requested 522262306a36Sopenharmony_ci * Gmode is not GMODE_LEGACY_B 522362306a36Sopenharmony_ci */ 522462306a36Sopenharmony_ci if ((wlc->pub->_n_enab & SUPPORT_11N) && gmode == GMODE_LEGACY_B) 522562306a36Sopenharmony_ci return -ENOTSUPP; 522662306a36Sopenharmony_ci 522762306a36Sopenharmony_ci /* verify that we are dealing with 2G band and grab the band pointer */ 522862306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_2G) 522962306a36Sopenharmony_ci band = wlc->band; 523062306a36Sopenharmony_ci else if ((wlc->pub->_nbands > 1) && 523162306a36Sopenharmony_ci (wlc->bandstate[OTHERBANDUNIT(wlc)]->bandtype == BRCM_BAND_2G)) 523262306a36Sopenharmony_ci band = wlc->bandstate[OTHERBANDUNIT(wlc)]; 523362306a36Sopenharmony_ci else 523462306a36Sopenharmony_ci return -EINVAL; 523562306a36Sopenharmony_ci 523662306a36Sopenharmony_ci /* update configuration value */ 523762306a36Sopenharmony_ci if (config) 523862306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, gmode); 523962306a36Sopenharmony_ci 524062306a36Sopenharmony_ci /* Clear rateset override */ 524162306a36Sopenharmony_ci memset(&rs, 0, sizeof(rs)); 524262306a36Sopenharmony_ci 524362306a36Sopenharmony_ci switch (gmode) { 524462306a36Sopenharmony_ci case GMODE_LEGACY_B: 524562306a36Sopenharmony_ci shortslot = BRCMS_SHORTSLOT_OFF; 524662306a36Sopenharmony_ci brcms_c_rateset_copy(&gphy_legacy_rates, &rs); 524762306a36Sopenharmony_ci 524862306a36Sopenharmony_ci break; 524962306a36Sopenharmony_ci 525062306a36Sopenharmony_ci case GMODE_LRS: 525162306a36Sopenharmony_ci break; 525262306a36Sopenharmony_ci 525362306a36Sopenharmony_ci case GMODE_AUTO: 525462306a36Sopenharmony_ci /* Accept defaults */ 525562306a36Sopenharmony_ci break; 525662306a36Sopenharmony_ci 525762306a36Sopenharmony_ci case GMODE_ONLY: 525862306a36Sopenharmony_ci ofdm_basic = true; 525962306a36Sopenharmony_ci break; 526062306a36Sopenharmony_ci 526162306a36Sopenharmony_ci case GMODE_PERFORMANCE: 526262306a36Sopenharmony_ci shortslot = BRCMS_SHORTSLOT_ON; 526362306a36Sopenharmony_ci ofdm_basic = true; 526462306a36Sopenharmony_ci break; 526562306a36Sopenharmony_ci 526662306a36Sopenharmony_ci default: 526762306a36Sopenharmony_ci /* Error */ 526862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: %s: invalid gmode %d\n", 526962306a36Sopenharmony_ci wlc->pub->unit, __func__, gmode); 527062306a36Sopenharmony_ci return -ENOTSUPP; 527162306a36Sopenharmony_ci } 527262306a36Sopenharmony_ci 527362306a36Sopenharmony_ci band->gmode = gmode; 527462306a36Sopenharmony_ci 527562306a36Sopenharmony_ci wlc->shortslot_override = shortslot; 527662306a36Sopenharmony_ci 527762306a36Sopenharmony_ci /* Use the default 11g rateset */ 527862306a36Sopenharmony_ci if (!rs.count) 527962306a36Sopenharmony_ci brcms_c_rateset_copy(&cck_ofdm_rates, &rs); 528062306a36Sopenharmony_ci 528162306a36Sopenharmony_ci if (ofdm_basic) { 528262306a36Sopenharmony_ci for (i = 0; i < rs.count; i++) { 528362306a36Sopenharmony_ci if (rs.rates[i] == BRCM_RATE_6M 528462306a36Sopenharmony_ci || rs.rates[i] == BRCM_RATE_12M 528562306a36Sopenharmony_ci || rs.rates[i] == BRCM_RATE_24M) 528662306a36Sopenharmony_ci rs.rates[i] |= BRCMS_RATE_FLAG; 528762306a36Sopenharmony_ci } 528862306a36Sopenharmony_ci } 528962306a36Sopenharmony_ci 529062306a36Sopenharmony_ci /* Set default bss rateset */ 529162306a36Sopenharmony_ci wlc->default_bss->rateset.count = rs.count; 529262306a36Sopenharmony_ci memcpy(wlc->default_bss->rateset.rates, rs.rates, 529362306a36Sopenharmony_ci sizeof(wlc->default_bss->rateset.rates)); 529462306a36Sopenharmony_ci 529562306a36Sopenharmony_ci return ret; 529662306a36Sopenharmony_ci} 529762306a36Sopenharmony_ci 529862306a36Sopenharmony_ciint brcms_c_set_nmode(struct brcms_c_info *wlc) 529962306a36Sopenharmony_ci{ 530062306a36Sopenharmony_ci uint i; 530162306a36Sopenharmony_ci s32 nmode = AUTO; 530262306a36Sopenharmony_ci 530362306a36Sopenharmony_ci if (wlc->stf->txstreams == WL_11N_3x3) 530462306a36Sopenharmony_ci nmode = WL_11N_3x3; 530562306a36Sopenharmony_ci else 530662306a36Sopenharmony_ci nmode = WL_11N_2x2; 530762306a36Sopenharmony_ci 530862306a36Sopenharmony_ci /* force GMODE_AUTO if NMODE is ON */ 530962306a36Sopenharmony_ci brcms_c_set_gmode(wlc, GMODE_AUTO, true); 531062306a36Sopenharmony_ci if (nmode == WL_11N_3x3) 531162306a36Sopenharmony_ci wlc->pub->_n_enab = SUPPORT_HT; 531262306a36Sopenharmony_ci else 531362306a36Sopenharmony_ci wlc->pub->_n_enab = SUPPORT_11N; 531462306a36Sopenharmony_ci wlc->default_bss->flags |= BRCMS_BSS_HT; 531562306a36Sopenharmony_ci /* add the mcs rates to the default and hw ratesets */ 531662306a36Sopenharmony_ci brcms_c_rateset_mcs_build(&wlc->default_bss->rateset, 531762306a36Sopenharmony_ci wlc->stf->txstreams); 531862306a36Sopenharmony_ci for (i = 0; i < wlc->pub->_nbands; i++) 531962306a36Sopenharmony_ci memcpy(wlc->bandstate[i]->hw_rateset.mcs, 532062306a36Sopenharmony_ci wlc->default_bss->rateset.mcs, MCSSET_LEN); 532162306a36Sopenharmony_ci 532262306a36Sopenharmony_ci return 0; 532362306a36Sopenharmony_ci} 532462306a36Sopenharmony_ci 532562306a36Sopenharmony_cistatic int 532662306a36Sopenharmony_cibrcms_c_set_internal_rateset(struct brcms_c_info *wlc, 532762306a36Sopenharmony_ci struct brcms_c_rateset *rs_arg) 532862306a36Sopenharmony_ci{ 532962306a36Sopenharmony_ci struct brcms_c_rateset rs, new; 533062306a36Sopenharmony_ci uint bandunit; 533162306a36Sopenharmony_ci 533262306a36Sopenharmony_ci memcpy(&rs, rs_arg, sizeof(struct brcms_c_rateset)); 533362306a36Sopenharmony_ci 533462306a36Sopenharmony_ci /* check for bad count value */ 533562306a36Sopenharmony_ci if ((rs.count == 0) || (rs.count > BRCMS_NUMRATES)) 533662306a36Sopenharmony_ci return -EINVAL; 533762306a36Sopenharmony_ci 533862306a36Sopenharmony_ci /* try the current band */ 533962306a36Sopenharmony_ci bandunit = wlc->band->bandunit; 534062306a36Sopenharmony_ci memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); 534162306a36Sopenharmony_ci if (brcms_c_rate_hwrs_filter_sort_validate 534262306a36Sopenharmony_ci (&new, &wlc->bandstate[bandunit]->hw_rateset, true, 534362306a36Sopenharmony_ci wlc->stf->txstreams)) 534462306a36Sopenharmony_ci goto good; 534562306a36Sopenharmony_ci 534662306a36Sopenharmony_ci /* try the other band */ 534762306a36Sopenharmony_ci if (brcms_is_mband_unlocked(wlc)) { 534862306a36Sopenharmony_ci bandunit = OTHERBANDUNIT(wlc); 534962306a36Sopenharmony_ci memcpy(&new, &rs, sizeof(struct brcms_c_rateset)); 535062306a36Sopenharmony_ci if (brcms_c_rate_hwrs_filter_sort_validate(&new, 535162306a36Sopenharmony_ci &wlc-> 535262306a36Sopenharmony_ci bandstate[bandunit]-> 535362306a36Sopenharmony_ci hw_rateset, true, 535462306a36Sopenharmony_ci wlc->stf->txstreams)) 535562306a36Sopenharmony_ci goto good; 535662306a36Sopenharmony_ci } 535762306a36Sopenharmony_ci 535862306a36Sopenharmony_ci return -EBADE; 535962306a36Sopenharmony_ci 536062306a36Sopenharmony_ci good: 536162306a36Sopenharmony_ci /* apply new rateset */ 536262306a36Sopenharmony_ci memcpy(&wlc->default_bss->rateset, &new, 536362306a36Sopenharmony_ci sizeof(struct brcms_c_rateset)); 536462306a36Sopenharmony_ci memcpy(&wlc->bandstate[bandunit]->defrateset, &new, 536562306a36Sopenharmony_ci sizeof(struct brcms_c_rateset)); 536662306a36Sopenharmony_ci return 0; 536762306a36Sopenharmony_ci} 536862306a36Sopenharmony_ci 536962306a36Sopenharmony_cistatic void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc) 537062306a36Sopenharmony_ci{ 537162306a36Sopenharmony_ci wlc_phy_ofdm_rateset_war(wlc->band->pi, false); 537262306a36Sopenharmony_ci} 537362306a36Sopenharmony_ci 537462306a36Sopenharmony_ciint brcms_c_set_channel(struct brcms_c_info *wlc, u16 channel) 537562306a36Sopenharmony_ci{ 537662306a36Sopenharmony_ci u16 chspec = ch20mhz_chspec(channel); 537762306a36Sopenharmony_ci 537862306a36Sopenharmony_ci if (channel > MAXCHANNEL) 537962306a36Sopenharmony_ci return -EINVAL; 538062306a36Sopenharmony_ci 538162306a36Sopenharmony_ci if (!brcms_c_valid_chanspec_db(wlc->cmi, chspec)) 538262306a36Sopenharmony_ci return -EINVAL; 538362306a36Sopenharmony_ci 538462306a36Sopenharmony_ci 538562306a36Sopenharmony_ci if (!wlc->pub->up && brcms_is_mband_unlocked(wlc)) { 538662306a36Sopenharmony_ci if (wlc->band->bandunit != chspec_bandunit(chspec)) 538762306a36Sopenharmony_ci wlc->bandinit_pending = true; 538862306a36Sopenharmony_ci else 538962306a36Sopenharmony_ci wlc->bandinit_pending = false; 539062306a36Sopenharmony_ci } 539162306a36Sopenharmony_ci 539262306a36Sopenharmony_ci wlc->default_bss->chanspec = chspec; 539362306a36Sopenharmony_ci /* brcms_c_BSSinit() will sanitize the rateset before 539462306a36Sopenharmony_ci * using it.. */ 539562306a36Sopenharmony_ci if (wlc->pub->up && (wlc_phy_chanspec_get(wlc->band->pi) != chspec)) { 539662306a36Sopenharmony_ci brcms_c_set_home_chanspec(wlc, chspec); 539762306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc); 539862306a36Sopenharmony_ci brcms_c_set_chanspec(wlc, chspec); 539962306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 540062306a36Sopenharmony_ci } 540162306a36Sopenharmony_ci return 0; 540262306a36Sopenharmony_ci} 540362306a36Sopenharmony_ci 540462306a36Sopenharmony_ciint brcms_c_set_rate_limit(struct brcms_c_info *wlc, u16 srl, u16 lrl) 540562306a36Sopenharmony_ci{ 540662306a36Sopenharmony_ci int ac; 540762306a36Sopenharmony_ci 540862306a36Sopenharmony_ci if (srl < 1 || srl > RETRY_SHORT_MAX || 540962306a36Sopenharmony_ci lrl < 1 || lrl > RETRY_SHORT_MAX) 541062306a36Sopenharmony_ci return -EINVAL; 541162306a36Sopenharmony_ci 541262306a36Sopenharmony_ci wlc->SRL = srl; 541362306a36Sopenharmony_ci wlc->LRL = lrl; 541462306a36Sopenharmony_ci 541562306a36Sopenharmony_ci brcms_b_retrylimit_upd(wlc->hw, wlc->SRL, wlc->LRL); 541662306a36Sopenharmony_ci 541762306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) { 541862306a36Sopenharmony_ci wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], 541962306a36Sopenharmony_ci EDCF_SHORT, wlc->SRL); 542062306a36Sopenharmony_ci wlc->wme_retries[ac] = SFIELD(wlc->wme_retries[ac], 542162306a36Sopenharmony_ci EDCF_LONG, wlc->LRL); 542262306a36Sopenharmony_ci } 542362306a36Sopenharmony_ci brcms_c_wme_retries_write(wlc); 542462306a36Sopenharmony_ci 542562306a36Sopenharmony_ci return 0; 542662306a36Sopenharmony_ci} 542762306a36Sopenharmony_ci 542862306a36Sopenharmony_civoid brcms_c_get_current_rateset(struct brcms_c_info *wlc, 542962306a36Sopenharmony_ci struct brcm_rateset *currs) 543062306a36Sopenharmony_ci{ 543162306a36Sopenharmony_ci struct brcms_c_rateset *rs; 543262306a36Sopenharmony_ci 543362306a36Sopenharmony_ci if (wlc->pub->associated) 543462306a36Sopenharmony_ci rs = &wlc->bsscfg->current_bss->rateset; 543562306a36Sopenharmony_ci else 543662306a36Sopenharmony_ci rs = &wlc->default_bss->rateset; 543762306a36Sopenharmony_ci 543862306a36Sopenharmony_ci /* Copy only legacy rateset section */ 543962306a36Sopenharmony_ci currs->count = rs->count; 544062306a36Sopenharmony_ci memcpy(&currs->rates, &rs->rates, rs->count); 544162306a36Sopenharmony_ci} 544262306a36Sopenharmony_ci 544362306a36Sopenharmony_ciint brcms_c_set_rateset(struct brcms_c_info *wlc, struct brcm_rateset *rs) 544462306a36Sopenharmony_ci{ 544562306a36Sopenharmony_ci struct brcms_c_rateset internal_rs; 544662306a36Sopenharmony_ci int bcmerror; 544762306a36Sopenharmony_ci 544862306a36Sopenharmony_ci if (rs->count > BRCMS_NUMRATES) 544962306a36Sopenharmony_ci return -ENOBUFS; 545062306a36Sopenharmony_ci 545162306a36Sopenharmony_ci memset(&internal_rs, 0, sizeof(internal_rs)); 545262306a36Sopenharmony_ci 545362306a36Sopenharmony_ci /* Copy only legacy rateset section */ 545462306a36Sopenharmony_ci internal_rs.count = rs->count; 545562306a36Sopenharmony_ci memcpy(&internal_rs.rates, &rs->rates, internal_rs.count); 545662306a36Sopenharmony_ci 545762306a36Sopenharmony_ci /* merge rateset coming in with the current mcsset */ 545862306a36Sopenharmony_ci if (wlc->pub->_n_enab & SUPPORT_11N) { 545962306a36Sopenharmony_ci struct brcms_bss_info *mcsset_bss; 546062306a36Sopenharmony_ci if (wlc->pub->associated) 546162306a36Sopenharmony_ci mcsset_bss = wlc->bsscfg->current_bss; 546262306a36Sopenharmony_ci else 546362306a36Sopenharmony_ci mcsset_bss = wlc->default_bss; 546462306a36Sopenharmony_ci memcpy(internal_rs.mcs, &mcsset_bss->rateset.mcs[0], 546562306a36Sopenharmony_ci MCSSET_LEN); 546662306a36Sopenharmony_ci } 546762306a36Sopenharmony_ci 546862306a36Sopenharmony_ci bcmerror = brcms_c_set_internal_rateset(wlc, &internal_rs); 546962306a36Sopenharmony_ci if (!bcmerror) 547062306a36Sopenharmony_ci brcms_c_ofdm_rateset_war(wlc); 547162306a36Sopenharmony_ci 547262306a36Sopenharmony_ci return bcmerror; 547362306a36Sopenharmony_ci} 547462306a36Sopenharmony_ci 547562306a36Sopenharmony_cistatic void brcms_c_time_lock(struct brcms_c_info *wlc) 547662306a36Sopenharmony_ci{ 547762306a36Sopenharmony_ci bcma_set32(wlc->hw->d11core, D11REGOFFS(maccontrol), MCTL_TBTTHOLD); 547862306a36Sopenharmony_ci /* Commit the write */ 547962306a36Sopenharmony_ci bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); 548062306a36Sopenharmony_ci} 548162306a36Sopenharmony_ci 548262306a36Sopenharmony_cistatic void brcms_c_time_unlock(struct brcms_c_info *wlc) 548362306a36Sopenharmony_ci{ 548462306a36Sopenharmony_ci bcma_mask32(wlc->hw->d11core, D11REGOFFS(maccontrol), ~MCTL_TBTTHOLD); 548562306a36Sopenharmony_ci /* Commit the write */ 548662306a36Sopenharmony_ci bcma_read32(wlc->hw->d11core, D11REGOFFS(maccontrol)); 548762306a36Sopenharmony_ci} 548862306a36Sopenharmony_ci 548962306a36Sopenharmony_ciint brcms_c_set_beacon_period(struct brcms_c_info *wlc, u16 period) 549062306a36Sopenharmony_ci{ 549162306a36Sopenharmony_ci u32 bcnint_us; 549262306a36Sopenharmony_ci 549362306a36Sopenharmony_ci if (period == 0) 549462306a36Sopenharmony_ci return -EINVAL; 549562306a36Sopenharmony_ci 549662306a36Sopenharmony_ci wlc->default_bss->beacon_period = period; 549762306a36Sopenharmony_ci 549862306a36Sopenharmony_ci bcnint_us = period << 10; 549962306a36Sopenharmony_ci brcms_c_time_lock(wlc); 550062306a36Sopenharmony_ci bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfprep), 550162306a36Sopenharmony_ci (bcnint_us << CFPREP_CBI_SHIFT)); 550262306a36Sopenharmony_ci bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_cfpstart), bcnint_us); 550362306a36Sopenharmony_ci brcms_c_time_unlock(wlc); 550462306a36Sopenharmony_ci 550562306a36Sopenharmony_ci return 0; 550662306a36Sopenharmony_ci} 550762306a36Sopenharmony_ci 550862306a36Sopenharmony_ciu16 brcms_c_get_phy_type(struct brcms_c_info *wlc, int phyidx) 550962306a36Sopenharmony_ci{ 551062306a36Sopenharmony_ci return wlc->band->phytype; 551162306a36Sopenharmony_ci} 551262306a36Sopenharmony_ci 551362306a36Sopenharmony_civoid brcms_c_set_shortslot_override(struct brcms_c_info *wlc, s8 sslot_override) 551462306a36Sopenharmony_ci{ 551562306a36Sopenharmony_ci wlc->shortslot_override = sslot_override; 551662306a36Sopenharmony_ci 551762306a36Sopenharmony_ci /* 551862306a36Sopenharmony_ci * shortslot is an 11g feature, so no more work if we are 551962306a36Sopenharmony_ci * currently on the 5G band 552062306a36Sopenharmony_ci */ 552162306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_5G) 552262306a36Sopenharmony_ci return; 552362306a36Sopenharmony_ci 552462306a36Sopenharmony_ci if (wlc->pub->up && wlc->pub->associated) { 552562306a36Sopenharmony_ci /* let watchdog or beacon processing update shortslot */ 552662306a36Sopenharmony_ci } else if (wlc->pub->up) { 552762306a36Sopenharmony_ci /* unassociated shortslot is off */ 552862306a36Sopenharmony_ci brcms_c_switch_shortslot(wlc, false); 552962306a36Sopenharmony_ci } else { 553062306a36Sopenharmony_ci /* driver is down, so just update the brcms_c_info 553162306a36Sopenharmony_ci * value */ 553262306a36Sopenharmony_ci if (wlc->shortslot_override == BRCMS_SHORTSLOT_AUTO) 553362306a36Sopenharmony_ci wlc->shortslot = false; 553462306a36Sopenharmony_ci else 553562306a36Sopenharmony_ci wlc->shortslot = 553662306a36Sopenharmony_ci (wlc->shortslot_override == 553762306a36Sopenharmony_ci BRCMS_SHORTSLOT_ON); 553862306a36Sopenharmony_ci } 553962306a36Sopenharmony_ci} 554062306a36Sopenharmony_ci 554162306a36Sopenharmony_ci/* 554262306a36Sopenharmony_ci * register watchdog and down handlers. 554362306a36Sopenharmony_ci */ 554462306a36Sopenharmony_ciint brcms_c_module_register(struct brcms_pub *pub, 554562306a36Sopenharmony_ci const char *name, struct brcms_info *hdl, 554662306a36Sopenharmony_ci int (*d_fn)(void *handle)) 554762306a36Sopenharmony_ci{ 554862306a36Sopenharmony_ci struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; 554962306a36Sopenharmony_ci int i; 555062306a36Sopenharmony_ci 555162306a36Sopenharmony_ci /* find an empty entry and just add, no duplication check! */ 555262306a36Sopenharmony_ci for (i = 0; i < BRCMS_MAXMODULES; i++) { 555362306a36Sopenharmony_ci if (wlc->modulecb[i].name[0] == '\0') { 555462306a36Sopenharmony_ci strncpy(wlc->modulecb[i].name, name, 555562306a36Sopenharmony_ci sizeof(wlc->modulecb[i].name) - 1); 555662306a36Sopenharmony_ci wlc->modulecb[i].hdl = hdl; 555762306a36Sopenharmony_ci wlc->modulecb[i].down_fn = d_fn; 555862306a36Sopenharmony_ci return 0; 555962306a36Sopenharmony_ci } 556062306a36Sopenharmony_ci } 556162306a36Sopenharmony_ci 556262306a36Sopenharmony_ci return -ENOSR; 556362306a36Sopenharmony_ci} 556462306a36Sopenharmony_ci 556562306a36Sopenharmony_ci/* unregister module callbacks */ 556662306a36Sopenharmony_ciint brcms_c_module_unregister(struct brcms_pub *pub, const char *name, 556762306a36Sopenharmony_ci struct brcms_info *hdl) 556862306a36Sopenharmony_ci{ 556962306a36Sopenharmony_ci struct brcms_c_info *wlc = (struct brcms_c_info *) pub->wlc; 557062306a36Sopenharmony_ci int i; 557162306a36Sopenharmony_ci 557262306a36Sopenharmony_ci if (wlc == NULL) 557362306a36Sopenharmony_ci return -ENODATA; 557462306a36Sopenharmony_ci 557562306a36Sopenharmony_ci for (i = 0; i < BRCMS_MAXMODULES; i++) { 557662306a36Sopenharmony_ci if (!strcmp(wlc->modulecb[i].name, name) && 557762306a36Sopenharmony_ci (wlc->modulecb[i].hdl == hdl)) { 557862306a36Sopenharmony_ci memset(&wlc->modulecb[i], 0, sizeof(wlc->modulecb[i])); 557962306a36Sopenharmony_ci return 0; 558062306a36Sopenharmony_ci } 558162306a36Sopenharmony_ci } 558262306a36Sopenharmony_ci 558362306a36Sopenharmony_ci /* table not found! */ 558462306a36Sopenharmony_ci return -ENODATA; 558562306a36Sopenharmony_ci} 558662306a36Sopenharmony_ci 558762306a36Sopenharmony_cistatic bool brcms_c_chipmatch_pci(struct bcma_device *core) 558862306a36Sopenharmony_ci{ 558962306a36Sopenharmony_ci struct pci_dev *pcidev = core->bus->host_pci; 559062306a36Sopenharmony_ci u16 vendor = pcidev->vendor; 559162306a36Sopenharmony_ci u16 device = pcidev->device; 559262306a36Sopenharmony_ci 559362306a36Sopenharmony_ci if (vendor != PCI_VENDOR_ID_BROADCOM) { 559462306a36Sopenharmony_ci pr_err("unknown vendor id %04x\n", vendor); 559562306a36Sopenharmony_ci return false; 559662306a36Sopenharmony_ci } 559762306a36Sopenharmony_ci 559862306a36Sopenharmony_ci if (device == BCM43224_D11N_ID_VEN1 || device == BCM43224_CHIP_ID) 559962306a36Sopenharmony_ci return true; 560062306a36Sopenharmony_ci if ((device == BCM43224_D11N_ID) || (device == BCM43225_D11N2G_ID)) 560162306a36Sopenharmony_ci return true; 560262306a36Sopenharmony_ci if (device == BCM4313_D11N2G_ID || device == BCM4313_CHIP_ID) 560362306a36Sopenharmony_ci return true; 560462306a36Sopenharmony_ci if ((device == BCM43236_D11N_ID) || (device == BCM43236_D11N2G_ID)) 560562306a36Sopenharmony_ci return true; 560662306a36Sopenharmony_ci 560762306a36Sopenharmony_ci pr_err("unknown device id %04x\n", device); 560862306a36Sopenharmony_ci return false; 560962306a36Sopenharmony_ci} 561062306a36Sopenharmony_ci 561162306a36Sopenharmony_cistatic bool brcms_c_chipmatch_soc(struct bcma_device *core) 561262306a36Sopenharmony_ci{ 561362306a36Sopenharmony_ci struct bcma_chipinfo *chipinfo = &core->bus->chipinfo; 561462306a36Sopenharmony_ci 561562306a36Sopenharmony_ci if (chipinfo->id == BCMA_CHIP_ID_BCM4716) 561662306a36Sopenharmony_ci return true; 561762306a36Sopenharmony_ci 561862306a36Sopenharmony_ci pr_err("unknown chip id %04x\n", chipinfo->id); 561962306a36Sopenharmony_ci return false; 562062306a36Sopenharmony_ci} 562162306a36Sopenharmony_ci 562262306a36Sopenharmony_cibool brcms_c_chipmatch(struct bcma_device *core) 562362306a36Sopenharmony_ci{ 562462306a36Sopenharmony_ci switch (core->bus->hosttype) { 562562306a36Sopenharmony_ci case BCMA_HOSTTYPE_PCI: 562662306a36Sopenharmony_ci return brcms_c_chipmatch_pci(core); 562762306a36Sopenharmony_ci case BCMA_HOSTTYPE_SOC: 562862306a36Sopenharmony_ci return brcms_c_chipmatch_soc(core); 562962306a36Sopenharmony_ci default: 563062306a36Sopenharmony_ci pr_err("unknown host type: %i\n", core->bus->hosttype); 563162306a36Sopenharmony_ci return false; 563262306a36Sopenharmony_ci } 563362306a36Sopenharmony_ci} 563462306a36Sopenharmony_ci 563562306a36Sopenharmony_ciu16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate) 563662306a36Sopenharmony_ci{ 563762306a36Sopenharmony_ci u16 table_ptr; 563862306a36Sopenharmony_ci u8 phy_rate, index; 563962306a36Sopenharmony_ci 564062306a36Sopenharmony_ci /* get the phy specific rate encoding for the PLCP SIGNAL field */ 564162306a36Sopenharmony_ci if (is_ofdm_rate(rate)) 564262306a36Sopenharmony_ci table_ptr = M_RT_DIRMAP_A; 564362306a36Sopenharmony_ci else 564462306a36Sopenharmony_ci table_ptr = M_RT_DIRMAP_B; 564562306a36Sopenharmony_ci 564662306a36Sopenharmony_ci /* for a given rate, the LS-nibble of the PLCP SIGNAL field is 564762306a36Sopenharmony_ci * the index into the rate table. 564862306a36Sopenharmony_ci */ 564962306a36Sopenharmony_ci phy_rate = rate_info[rate] & BRCMS_RATE_MASK; 565062306a36Sopenharmony_ci index = phy_rate & 0xf; 565162306a36Sopenharmony_ci 565262306a36Sopenharmony_ci /* Find the SHM pointer to the rate table entry by looking in the 565362306a36Sopenharmony_ci * Direct-map Table 565462306a36Sopenharmony_ci */ 565562306a36Sopenharmony_ci return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2)); 565662306a36Sopenharmony_ci} 565762306a36Sopenharmony_ci 565862306a36Sopenharmony_ci/* 565962306a36Sopenharmony_ci * bcmc_fid_generate: 566062306a36Sopenharmony_ci * Generate frame ID for a BCMC packet. The frag field is not used 566162306a36Sopenharmony_ci * for MC frames so is used as part of the sequence number. 566262306a36Sopenharmony_ci */ 566362306a36Sopenharmony_cistatic inline u16 566462306a36Sopenharmony_cibcmc_fid_generate(struct brcms_c_info *wlc, struct brcms_bss_cfg *bsscfg, 566562306a36Sopenharmony_ci struct d11txh *txh) 566662306a36Sopenharmony_ci{ 566762306a36Sopenharmony_ci u16 frameid; 566862306a36Sopenharmony_ci 566962306a36Sopenharmony_ci frameid = le16_to_cpu(txh->TxFrameID) & ~(TXFID_SEQ_MASK | 567062306a36Sopenharmony_ci TXFID_QUEUE_MASK); 567162306a36Sopenharmony_ci frameid |= 567262306a36Sopenharmony_ci (((wlc-> 567362306a36Sopenharmony_ci mc_fid_counter++) << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | 567462306a36Sopenharmony_ci TX_BCMC_FIFO; 567562306a36Sopenharmony_ci 567662306a36Sopenharmony_ci return frameid; 567762306a36Sopenharmony_ci} 567862306a36Sopenharmony_ci 567962306a36Sopenharmony_cistatic uint 568062306a36Sopenharmony_cibrcms_c_calc_ack_time(struct brcms_c_info *wlc, u32 rspec, 568162306a36Sopenharmony_ci u8 preamble_type) 568262306a36Sopenharmony_ci{ 568362306a36Sopenharmony_ci uint dur = 0; 568462306a36Sopenharmony_ci 568562306a36Sopenharmony_ci /* 568662306a36Sopenharmony_ci * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that 568762306a36Sopenharmony_ci * is less than or equal to the rate of the immediately previous 568862306a36Sopenharmony_ci * frame in the FES 568962306a36Sopenharmony_ci */ 569062306a36Sopenharmony_ci rspec = brcms_basic_rate(wlc, rspec); 569162306a36Sopenharmony_ci /* ACK frame len == 14 == 2(fc) + 2(dur) + 6(ra) + 4(fcs) */ 569262306a36Sopenharmony_ci dur = 569362306a36Sopenharmony_ci brcms_c_calc_frame_time(wlc, rspec, preamble_type, 569462306a36Sopenharmony_ci (DOT11_ACK_LEN + FCS_LEN)); 569562306a36Sopenharmony_ci return dur; 569662306a36Sopenharmony_ci} 569762306a36Sopenharmony_ci 569862306a36Sopenharmony_cistatic uint 569962306a36Sopenharmony_cibrcms_c_calc_cts_time(struct brcms_c_info *wlc, u32 rspec, 570062306a36Sopenharmony_ci u8 preamble_type) 570162306a36Sopenharmony_ci{ 570262306a36Sopenharmony_ci return brcms_c_calc_ack_time(wlc, rspec, preamble_type); 570362306a36Sopenharmony_ci} 570462306a36Sopenharmony_ci 570562306a36Sopenharmony_cistatic uint 570662306a36Sopenharmony_cibrcms_c_calc_ba_time(struct brcms_c_info *wlc, u32 rspec, 570762306a36Sopenharmony_ci u8 preamble_type) 570862306a36Sopenharmony_ci{ 570962306a36Sopenharmony_ci /* 571062306a36Sopenharmony_ci * Spec 9.6: ack rate is the highest rate in BSSBasicRateSet that 571162306a36Sopenharmony_ci * is less than or equal to the rate of the immediately previous 571262306a36Sopenharmony_ci * frame in the FES 571362306a36Sopenharmony_ci */ 571462306a36Sopenharmony_ci rspec = brcms_basic_rate(wlc, rspec); 571562306a36Sopenharmony_ci /* BA len == 32 == 16(ctl hdr) + 4(ba len) + 8(bitmap) + 4(fcs) */ 571662306a36Sopenharmony_ci return brcms_c_calc_frame_time(wlc, rspec, preamble_type, 571762306a36Sopenharmony_ci (DOT11_BA_LEN + DOT11_BA_BITMAP_LEN + 571862306a36Sopenharmony_ci FCS_LEN)); 571962306a36Sopenharmony_ci} 572062306a36Sopenharmony_ci 572162306a36Sopenharmony_ci/* brcms_c_compute_frame_dur() 572262306a36Sopenharmony_ci * 572362306a36Sopenharmony_ci * Calculate the 802.11 MAC header DUR field for MPDU 572462306a36Sopenharmony_ci * DUR for a single frame = 1 SIFS + 1 ACK 572562306a36Sopenharmony_ci * DUR for a frame with following frags = 3 SIFS + 2 ACK + next frag time 572662306a36Sopenharmony_ci * 572762306a36Sopenharmony_ci * rate MPDU rate in unit of 500kbps 572862306a36Sopenharmony_ci * next_frag_len next MPDU length in bytes 572962306a36Sopenharmony_ci * preamble_type use short/GF or long/MM PLCP header 573062306a36Sopenharmony_ci */ 573162306a36Sopenharmony_cistatic u16 573262306a36Sopenharmony_cibrcms_c_compute_frame_dur(struct brcms_c_info *wlc, u32 rate, 573362306a36Sopenharmony_ci u8 preamble_type, uint next_frag_len) 573462306a36Sopenharmony_ci{ 573562306a36Sopenharmony_ci u16 dur, sifs; 573662306a36Sopenharmony_ci 573762306a36Sopenharmony_ci sifs = get_sifs(wlc->band); 573862306a36Sopenharmony_ci 573962306a36Sopenharmony_ci dur = sifs; 574062306a36Sopenharmony_ci dur += (u16) brcms_c_calc_ack_time(wlc, rate, preamble_type); 574162306a36Sopenharmony_ci 574262306a36Sopenharmony_ci if (next_frag_len) { 574362306a36Sopenharmony_ci /* Double the current DUR to get 2 SIFS + 2 ACKs */ 574462306a36Sopenharmony_ci dur *= 2; 574562306a36Sopenharmony_ci /* add another SIFS and the frag time */ 574662306a36Sopenharmony_ci dur += sifs; 574762306a36Sopenharmony_ci dur += 574862306a36Sopenharmony_ci (u16) brcms_c_calc_frame_time(wlc, rate, preamble_type, 574962306a36Sopenharmony_ci next_frag_len); 575062306a36Sopenharmony_ci } 575162306a36Sopenharmony_ci return dur; 575262306a36Sopenharmony_ci} 575362306a36Sopenharmony_ci 575462306a36Sopenharmony_ci/* The opposite of brcms_c_calc_frame_time */ 575562306a36Sopenharmony_cistatic uint 575662306a36Sopenharmony_cibrcms_c_calc_frame_len(struct brcms_c_info *wlc, u32 ratespec, 575762306a36Sopenharmony_ci u8 preamble_type, uint dur) 575862306a36Sopenharmony_ci{ 575962306a36Sopenharmony_ci uint nsyms, mac_len, Ndps, kNdps; 576062306a36Sopenharmony_ci uint rate = rspec2rate(ratespec); 576162306a36Sopenharmony_ci 576262306a36Sopenharmony_ci if (is_mcs_rate(ratespec)) { 576362306a36Sopenharmony_ci uint mcs = ratespec & RSPEC_RATE_MASK; 576462306a36Sopenharmony_ci int tot_streams = mcs_2_txstreams(mcs) + rspec_stc(ratespec); 576562306a36Sopenharmony_ci dur -= PREN_PREAMBLE + (tot_streams * PREN_PREAMBLE_EXT); 576662306a36Sopenharmony_ci /* payload calculation matches that of regular ofdm */ 576762306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_2G) 576862306a36Sopenharmony_ci dur -= DOT11_OFDM_SIGNAL_EXTENSION; 576962306a36Sopenharmony_ci /* kNdbps = kbps * 4 */ 577062306a36Sopenharmony_ci kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), 577162306a36Sopenharmony_ci rspec_issgi(ratespec)) * 4; 577262306a36Sopenharmony_ci nsyms = dur / APHY_SYMBOL_TIME; 577362306a36Sopenharmony_ci mac_len = 577462306a36Sopenharmony_ci ((nsyms * kNdps) - 577562306a36Sopenharmony_ci ((APHY_SERVICE_NBITS + APHY_TAIL_NBITS) * 1000)) / 8000; 577662306a36Sopenharmony_ci } else if (is_ofdm_rate(ratespec)) { 577762306a36Sopenharmony_ci dur -= APHY_PREAMBLE_TIME; 577862306a36Sopenharmony_ci dur -= APHY_SIGNAL_TIME; 577962306a36Sopenharmony_ci /* Ndbps = Mbps * 4 = rate(500Kbps) * 2 */ 578062306a36Sopenharmony_ci Ndps = rate * 2; 578162306a36Sopenharmony_ci nsyms = dur / APHY_SYMBOL_TIME; 578262306a36Sopenharmony_ci mac_len = 578362306a36Sopenharmony_ci ((nsyms * Ndps) - 578462306a36Sopenharmony_ci (APHY_SERVICE_NBITS + APHY_TAIL_NBITS)) / 8; 578562306a36Sopenharmony_ci } else { 578662306a36Sopenharmony_ci if (preamble_type & BRCMS_SHORT_PREAMBLE) 578762306a36Sopenharmony_ci dur -= BPHY_PLCP_SHORT_TIME; 578862306a36Sopenharmony_ci else 578962306a36Sopenharmony_ci dur -= BPHY_PLCP_TIME; 579062306a36Sopenharmony_ci mac_len = dur * rate; 579162306a36Sopenharmony_ci /* divide out factor of 2 in rate (1/2 mbps) */ 579262306a36Sopenharmony_ci mac_len = mac_len / 8 / 2; 579362306a36Sopenharmony_ci } 579462306a36Sopenharmony_ci return mac_len; 579562306a36Sopenharmony_ci} 579662306a36Sopenharmony_ci 579762306a36Sopenharmony_ci/* 579862306a36Sopenharmony_ci * Return true if the specified rate is supported by the specified band. 579962306a36Sopenharmony_ci * BRCM_BAND_AUTO indicates the current band. 580062306a36Sopenharmony_ci */ 580162306a36Sopenharmony_cistatic bool brcms_c_valid_rate(struct brcms_c_info *wlc, u32 rspec, int band, 580262306a36Sopenharmony_ci bool verbose) 580362306a36Sopenharmony_ci{ 580462306a36Sopenharmony_ci struct brcms_c_rateset *hw_rateset; 580562306a36Sopenharmony_ci uint i; 580662306a36Sopenharmony_ci 580762306a36Sopenharmony_ci if ((band == BRCM_BAND_AUTO) || (band == wlc->band->bandtype)) 580862306a36Sopenharmony_ci hw_rateset = &wlc->band->hw_rateset; 580962306a36Sopenharmony_ci else if (wlc->pub->_nbands > 1) 581062306a36Sopenharmony_ci hw_rateset = &wlc->bandstate[OTHERBANDUNIT(wlc)]->hw_rateset; 581162306a36Sopenharmony_ci else 581262306a36Sopenharmony_ci /* other band specified and we are a single band device */ 581362306a36Sopenharmony_ci return false; 581462306a36Sopenharmony_ci 581562306a36Sopenharmony_ci /* check if this is a mimo rate */ 581662306a36Sopenharmony_ci if (is_mcs_rate(rspec)) { 581762306a36Sopenharmony_ci if ((rspec & RSPEC_RATE_MASK) >= MCS_TABLE_SIZE) 581862306a36Sopenharmony_ci goto error; 581962306a36Sopenharmony_ci 582062306a36Sopenharmony_ci return isset(hw_rateset->mcs, (rspec & RSPEC_RATE_MASK)); 582162306a36Sopenharmony_ci } 582262306a36Sopenharmony_ci 582362306a36Sopenharmony_ci for (i = 0; i < hw_rateset->count; i++) 582462306a36Sopenharmony_ci if (hw_rateset->rates[i] == rspec2rate(rspec)) 582562306a36Sopenharmony_ci return true; 582662306a36Sopenharmony_ci error: 582762306a36Sopenharmony_ci if (verbose) 582862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "wl%d: valid_rate: rate spec 0x%x " 582962306a36Sopenharmony_ci "not in hw_rateset\n", wlc->pub->unit, rspec); 583062306a36Sopenharmony_ci 583162306a36Sopenharmony_ci return false; 583262306a36Sopenharmony_ci} 583362306a36Sopenharmony_ci 583462306a36Sopenharmony_cistatic u32 583562306a36Sopenharmony_cimac80211_wlc_set_nrate(struct brcms_c_info *wlc, struct brcms_band *cur_band, 583662306a36Sopenharmony_ci u32 int_val) 583762306a36Sopenharmony_ci{ 583862306a36Sopenharmony_ci struct bcma_device *core = wlc->hw->d11core; 583962306a36Sopenharmony_ci u8 stf = (int_val & NRATE_STF_MASK) >> NRATE_STF_SHIFT; 584062306a36Sopenharmony_ci u8 rate = int_val & NRATE_RATE_MASK; 584162306a36Sopenharmony_ci u32 rspec; 584262306a36Sopenharmony_ci bool ismcs = ((int_val & NRATE_MCS_INUSE) == NRATE_MCS_INUSE); 584362306a36Sopenharmony_ci bool issgi = ((int_val & NRATE_SGI_MASK) >> NRATE_SGI_SHIFT); 584462306a36Sopenharmony_ci bool override_mcs_only = ((int_val & NRATE_OVERRIDE_MCS_ONLY) 584562306a36Sopenharmony_ci == NRATE_OVERRIDE_MCS_ONLY); 584662306a36Sopenharmony_ci 584762306a36Sopenharmony_ci if (!ismcs) 584862306a36Sopenharmony_ci return (u32) rate; 584962306a36Sopenharmony_ci 585062306a36Sopenharmony_ci /* validate the combination of rate/mcs/stf is allowed */ 585162306a36Sopenharmony_ci if ((wlc->pub->_n_enab & SUPPORT_11N) && ismcs) { 585262306a36Sopenharmony_ci /* mcs only allowed when nmode */ 585362306a36Sopenharmony_ci if (stf > PHY_TXC1_MODE_SDM) { 585462306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Invalid stf\n", 585562306a36Sopenharmony_ci wlc->pub->unit, __func__); 585662306a36Sopenharmony_ci goto done; 585762306a36Sopenharmony_ci } 585862306a36Sopenharmony_ci 585962306a36Sopenharmony_ci /* mcs 32 is a special case, DUP mode 40 only */ 586062306a36Sopenharmony_ci if (rate == 32) { 586162306a36Sopenharmony_ci if (!CHSPEC_IS40(wlc->home_chanspec) || 586262306a36Sopenharmony_ci ((stf != PHY_TXC1_MODE_SISO) 586362306a36Sopenharmony_ci && (stf != PHY_TXC1_MODE_CDD))) { 586462306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Invalid mcs 32\n", 586562306a36Sopenharmony_ci wlc->pub->unit, __func__); 586662306a36Sopenharmony_ci goto done; 586762306a36Sopenharmony_ci } 586862306a36Sopenharmony_ci /* mcs > 7 must use stf SDM */ 586962306a36Sopenharmony_ci } else if (rate > HIGHEST_SINGLE_STREAM_MCS) { 587062306a36Sopenharmony_ci /* mcs > 7 must use stf SDM */ 587162306a36Sopenharmony_ci if (stf != PHY_TXC1_MODE_SDM) { 587262306a36Sopenharmony_ci brcms_dbg_mac80211(core, "wl%d: enabling " 587362306a36Sopenharmony_ci "SDM mode for mcs %d\n", 587462306a36Sopenharmony_ci wlc->pub->unit, rate); 587562306a36Sopenharmony_ci stf = PHY_TXC1_MODE_SDM; 587662306a36Sopenharmony_ci } 587762306a36Sopenharmony_ci } else { 587862306a36Sopenharmony_ci /* 587962306a36Sopenharmony_ci * MCS 0-7 may use SISO, CDD, and for 588062306a36Sopenharmony_ci * phy_rev >= 3 STBC 588162306a36Sopenharmony_ci */ 588262306a36Sopenharmony_ci if ((stf > PHY_TXC1_MODE_STBC) || 588362306a36Sopenharmony_ci (!BRCMS_STBC_CAP_PHY(wlc) 588462306a36Sopenharmony_ci && (stf == PHY_TXC1_MODE_STBC))) { 588562306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Invalid STBC\n", 588662306a36Sopenharmony_ci wlc->pub->unit, __func__); 588762306a36Sopenharmony_ci goto done; 588862306a36Sopenharmony_ci } 588962306a36Sopenharmony_ci } 589062306a36Sopenharmony_ci } else if (is_ofdm_rate(rate)) { 589162306a36Sopenharmony_ci if ((stf != PHY_TXC1_MODE_CDD) && (stf != PHY_TXC1_MODE_SISO)) { 589262306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Invalid OFDM\n", 589362306a36Sopenharmony_ci wlc->pub->unit, __func__); 589462306a36Sopenharmony_ci goto done; 589562306a36Sopenharmony_ci } 589662306a36Sopenharmony_ci } else if (is_cck_rate(rate)) { 589762306a36Sopenharmony_ci if ((cur_band->bandtype != BRCM_BAND_2G) 589862306a36Sopenharmony_ci || (stf != PHY_TXC1_MODE_SISO)) { 589962306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Invalid CCK\n", 590062306a36Sopenharmony_ci wlc->pub->unit, __func__); 590162306a36Sopenharmony_ci goto done; 590262306a36Sopenharmony_ci } 590362306a36Sopenharmony_ci } else { 590462306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: Unknown rate type\n", 590562306a36Sopenharmony_ci wlc->pub->unit, __func__); 590662306a36Sopenharmony_ci goto done; 590762306a36Sopenharmony_ci } 590862306a36Sopenharmony_ci /* make sure multiple antennae are available for non-siso rates */ 590962306a36Sopenharmony_ci if ((stf != PHY_TXC1_MODE_SISO) && (wlc->stf->txstreams == 1)) { 591062306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: SISO antenna but !SISO " 591162306a36Sopenharmony_ci "request\n", wlc->pub->unit, __func__); 591262306a36Sopenharmony_ci goto done; 591362306a36Sopenharmony_ci } 591462306a36Sopenharmony_ci 591562306a36Sopenharmony_ci rspec = rate; 591662306a36Sopenharmony_ci if (ismcs) { 591762306a36Sopenharmony_ci rspec |= RSPEC_MIMORATE; 591862306a36Sopenharmony_ci /* For STBC populate the STC field of the ratespec */ 591962306a36Sopenharmony_ci if (stf == PHY_TXC1_MODE_STBC) { 592062306a36Sopenharmony_ci u8 stc; 592162306a36Sopenharmony_ci stc = 1; /* Nss for single stream is always 1 */ 592262306a36Sopenharmony_ci rspec |= (stc << RSPEC_STC_SHIFT); 592362306a36Sopenharmony_ci } 592462306a36Sopenharmony_ci } 592562306a36Sopenharmony_ci 592662306a36Sopenharmony_ci rspec |= (stf << RSPEC_STF_SHIFT); 592762306a36Sopenharmony_ci 592862306a36Sopenharmony_ci if (override_mcs_only) 592962306a36Sopenharmony_ci rspec |= RSPEC_OVERRIDE_MCS_ONLY; 593062306a36Sopenharmony_ci 593162306a36Sopenharmony_ci if (issgi) 593262306a36Sopenharmony_ci rspec |= RSPEC_SHORT_GI; 593362306a36Sopenharmony_ci 593462306a36Sopenharmony_ci if ((rate != 0) 593562306a36Sopenharmony_ci && !brcms_c_valid_rate(wlc, rspec, cur_band->bandtype, true)) 593662306a36Sopenharmony_ci return rate; 593762306a36Sopenharmony_ci 593862306a36Sopenharmony_ci return rspec; 593962306a36Sopenharmony_cidone: 594062306a36Sopenharmony_ci return rate; 594162306a36Sopenharmony_ci} 594262306a36Sopenharmony_ci 594362306a36Sopenharmony_ci/* 594462306a36Sopenharmony_ci * Compute PLCP, but only requires actual rate and length of pkt. 594562306a36Sopenharmony_ci * Rate is given in the driver standard multiple of 500 kbps. 594662306a36Sopenharmony_ci * le is set for 11 Mbps rate if necessary. 594762306a36Sopenharmony_ci * Broken out for PRQ. 594862306a36Sopenharmony_ci */ 594962306a36Sopenharmony_ci 595062306a36Sopenharmony_cistatic void brcms_c_cck_plcp_set(struct brcms_c_info *wlc, int rate_500, 595162306a36Sopenharmony_ci uint length, u8 *plcp) 595262306a36Sopenharmony_ci{ 595362306a36Sopenharmony_ci u16 usec = 0; 595462306a36Sopenharmony_ci u8 le = 0; 595562306a36Sopenharmony_ci 595662306a36Sopenharmony_ci switch (rate_500) { 595762306a36Sopenharmony_ci case BRCM_RATE_1M: 595862306a36Sopenharmony_ci usec = length << 3; 595962306a36Sopenharmony_ci break; 596062306a36Sopenharmony_ci case BRCM_RATE_2M: 596162306a36Sopenharmony_ci usec = length << 2; 596262306a36Sopenharmony_ci break; 596362306a36Sopenharmony_ci case BRCM_RATE_5M5: 596462306a36Sopenharmony_ci usec = (length << 4) / 11; 596562306a36Sopenharmony_ci if ((length << 4) - (usec * 11) > 0) 596662306a36Sopenharmony_ci usec++; 596762306a36Sopenharmony_ci break; 596862306a36Sopenharmony_ci case BRCM_RATE_11M: 596962306a36Sopenharmony_ci usec = (length << 3) / 11; 597062306a36Sopenharmony_ci if ((length << 3) - (usec * 11) > 0) { 597162306a36Sopenharmony_ci usec++; 597262306a36Sopenharmony_ci if ((usec * 11) - (length << 3) >= 8) 597362306a36Sopenharmony_ci le = D11B_PLCP_SIGNAL_LE; 597462306a36Sopenharmony_ci } 597562306a36Sopenharmony_ci break; 597662306a36Sopenharmony_ci 597762306a36Sopenharmony_ci default: 597862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 597962306a36Sopenharmony_ci "brcms_c_cck_plcp_set: unsupported rate %d\n", 598062306a36Sopenharmony_ci rate_500); 598162306a36Sopenharmony_ci rate_500 = BRCM_RATE_1M; 598262306a36Sopenharmony_ci usec = length << 3; 598362306a36Sopenharmony_ci break; 598462306a36Sopenharmony_ci } 598562306a36Sopenharmony_ci /* PLCP signal byte */ 598662306a36Sopenharmony_ci plcp[0] = rate_500 * 5; /* r (500kbps) * 5 == r (100kbps) */ 598762306a36Sopenharmony_ci /* PLCP service byte */ 598862306a36Sopenharmony_ci plcp[1] = (u8) (le | D11B_PLCP_SIGNAL_LOCKED); 598962306a36Sopenharmony_ci /* PLCP length u16, little endian */ 599062306a36Sopenharmony_ci plcp[2] = usec & 0xff; 599162306a36Sopenharmony_ci plcp[3] = (usec >> 8) & 0xff; 599262306a36Sopenharmony_ci /* PLCP CRC16 */ 599362306a36Sopenharmony_ci plcp[4] = 0; 599462306a36Sopenharmony_ci plcp[5] = 0; 599562306a36Sopenharmony_ci} 599662306a36Sopenharmony_ci 599762306a36Sopenharmony_ci/* Rate: 802.11 rate code, length: PSDU length in octets */ 599862306a36Sopenharmony_cistatic void brcms_c_compute_mimo_plcp(u32 rspec, uint length, u8 *plcp) 599962306a36Sopenharmony_ci{ 600062306a36Sopenharmony_ci u8 mcs = (u8) (rspec & RSPEC_RATE_MASK); 600162306a36Sopenharmony_ci plcp[0] = mcs; 600262306a36Sopenharmony_ci if (rspec_is40mhz(rspec) || (mcs == 32)) 600362306a36Sopenharmony_ci plcp[0] |= MIMO_PLCP_40MHZ; 600462306a36Sopenharmony_ci BRCMS_SET_MIMO_PLCP_LEN(plcp, length); 600562306a36Sopenharmony_ci plcp[3] = rspec_mimoplcp3(rspec); /* rspec already holds this byte */ 600662306a36Sopenharmony_ci plcp[3] |= 0x7; /* set smoothing, not sounding ppdu & reserved */ 600762306a36Sopenharmony_ci plcp[4] = 0; /* number of extension spatial streams bit 0 & 1 */ 600862306a36Sopenharmony_ci plcp[5] = 0; 600962306a36Sopenharmony_ci} 601062306a36Sopenharmony_ci 601162306a36Sopenharmony_ci/* Rate: 802.11 rate code, length: PSDU length in octets */ 601262306a36Sopenharmony_cistatic void 601362306a36Sopenharmony_cibrcms_c_compute_ofdm_plcp(u32 rspec, u32 length, u8 *plcp) 601462306a36Sopenharmony_ci{ 601562306a36Sopenharmony_ci u8 rate_signal; 601662306a36Sopenharmony_ci u32 tmp = 0; 601762306a36Sopenharmony_ci int rate = rspec2rate(rspec); 601862306a36Sopenharmony_ci 601962306a36Sopenharmony_ci /* 602062306a36Sopenharmony_ci * encode rate per 802.11a-1999 sec 17.3.4.1, with lsb 602162306a36Sopenharmony_ci * transmitted first 602262306a36Sopenharmony_ci */ 602362306a36Sopenharmony_ci rate_signal = rate_info[rate] & BRCMS_RATE_MASK; 602462306a36Sopenharmony_ci memset(plcp, 0, D11_PHY_HDR_LEN); 602562306a36Sopenharmony_ci D11A_PHY_HDR_SRATE((struct ofdm_phy_hdr *) plcp, rate_signal); 602662306a36Sopenharmony_ci 602762306a36Sopenharmony_ci tmp = (length & 0xfff) << 5; 602862306a36Sopenharmony_ci plcp[2] |= (tmp >> 16) & 0xff; 602962306a36Sopenharmony_ci plcp[1] |= (tmp >> 8) & 0xff; 603062306a36Sopenharmony_ci plcp[0] |= tmp & 0xff; 603162306a36Sopenharmony_ci} 603262306a36Sopenharmony_ci 603362306a36Sopenharmony_ci/* Rate: 802.11 rate code, length: PSDU length in octets */ 603462306a36Sopenharmony_cistatic void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, u32 rspec, 603562306a36Sopenharmony_ci uint length, u8 *plcp) 603662306a36Sopenharmony_ci{ 603762306a36Sopenharmony_ci int rate = rspec2rate(rspec); 603862306a36Sopenharmony_ci 603962306a36Sopenharmony_ci brcms_c_cck_plcp_set(wlc, rate, length, plcp); 604062306a36Sopenharmony_ci} 604162306a36Sopenharmony_ci 604262306a36Sopenharmony_cistatic void 604362306a36Sopenharmony_cibrcms_c_compute_plcp(struct brcms_c_info *wlc, u32 rspec, 604462306a36Sopenharmony_ci uint length, u8 *plcp) 604562306a36Sopenharmony_ci{ 604662306a36Sopenharmony_ci if (is_mcs_rate(rspec)) 604762306a36Sopenharmony_ci brcms_c_compute_mimo_plcp(rspec, length, plcp); 604862306a36Sopenharmony_ci else if (is_ofdm_rate(rspec)) 604962306a36Sopenharmony_ci brcms_c_compute_ofdm_plcp(rspec, length, plcp); 605062306a36Sopenharmony_ci else 605162306a36Sopenharmony_ci brcms_c_compute_cck_plcp(wlc, rspec, length, plcp); 605262306a36Sopenharmony_ci} 605362306a36Sopenharmony_ci 605462306a36Sopenharmony_ci/* brcms_c_compute_rtscts_dur() 605562306a36Sopenharmony_ci * 605662306a36Sopenharmony_ci * Calculate the 802.11 MAC header DUR field for an RTS or CTS frame 605762306a36Sopenharmony_ci * DUR for normal RTS/CTS w/ frame = 3 SIFS + 1 CTS + next frame time + 1 ACK 605862306a36Sopenharmony_ci * DUR for CTS-TO-SELF w/ frame = 2 SIFS + next frame time + 1 ACK 605962306a36Sopenharmony_ci * 606062306a36Sopenharmony_ci * cts cts-to-self or rts/cts 606162306a36Sopenharmony_ci * rts_rate rts or cts rate in unit of 500kbps 606262306a36Sopenharmony_ci * rate next MPDU rate in unit of 500kbps 606362306a36Sopenharmony_ci * frame_len next MPDU frame length in bytes 606462306a36Sopenharmony_ci */ 606562306a36Sopenharmony_ciu16 606662306a36Sopenharmony_cibrcms_c_compute_rtscts_dur(struct brcms_c_info *wlc, bool cts_only, 606762306a36Sopenharmony_ci u32 rts_rate, 606862306a36Sopenharmony_ci u32 frame_rate, u8 rts_preamble_type, 606962306a36Sopenharmony_ci u8 frame_preamble_type, uint frame_len, bool ba) 607062306a36Sopenharmony_ci{ 607162306a36Sopenharmony_ci u16 dur, sifs; 607262306a36Sopenharmony_ci 607362306a36Sopenharmony_ci sifs = get_sifs(wlc->band); 607462306a36Sopenharmony_ci 607562306a36Sopenharmony_ci if (!cts_only) { 607662306a36Sopenharmony_ci /* RTS/CTS */ 607762306a36Sopenharmony_ci dur = 3 * sifs; 607862306a36Sopenharmony_ci dur += 607962306a36Sopenharmony_ci (u16) brcms_c_calc_cts_time(wlc, rts_rate, 608062306a36Sopenharmony_ci rts_preamble_type); 608162306a36Sopenharmony_ci } else { 608262306a36Sopenharmony_ci /* CTS-TO-SELF */ 608362306a36Sopenharmony_ci dur = 2 * sifs; 608462306a36Sopenharmony_ci } 608562306a36Sopenharmony_ci 608662306a36Sopenharmony_ci dur += 608762306a36Sopenharmony_ci (u16) brcms_c_calc_frame_time(wlc, frame_rate, frame_preamble_type, 608862306a36Sopenharmony_ci frame_len); 608962306a36Sopenharmony_ci if (ba) 609062306a36Sopenharmony_ci dur += 609162306a36Sopenharmony_ci (u16) brcms_c_calc_ba_time(wlc, frame_rate, 609262306a36Sopenharmony_ci BRCMS_SHORT_PREAMBLE); 609362306a36Sopenharmony_ci else 609462306a36Sopenharmony_ci dur += 609562306a36Sopenharmony_ci (u16) brcms_c_calc_ack_time(wlc, frame_rate, 609662306a36Sopenharmony_ci frame_preamble_type); 609762306a36Sopenharmony_ci return dur; 609862306a36Sopenharmony_ci} 609962306a36Sopenharmony_ci 610062306a36Sopenharmony_cistatic u16 brcms_c_phytxctl1_calc(struct brcms_c_info *wlc, u32 rspec) 610162306a36Sopenharmony_ci{ 610262306a36Sopenharmony_ci u16 phyctl1 = 0; 610362306a36Sopenharmony_ci u16 bw; 610462306a36Sopenharmony_ci 610562306a36Sopenharmony_ci if (BRCMS_ISLCNPHY(wlc->band)) { 610662306a36Sopenharmony_ci bw = PHY_TXC1_BW_20MHZ; 610762306a36Sopenharmony_ci } else { 610862306a36Sopenharmony_ci bw = rspec_get_bw(rspec); 610962306a36Sopenharmony_ci /* 10Mhz is not supported yet */ 611062306a36Sopenharmony_ci if (bw < PHY_TXC1_BW_20MHZ) { 611162306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "phytxctl1_calc: bw %d is " 611262306a36Sopenharmony_ci "not supported yet, set to 20L\n", bw); 611362306a36Sopenharmony_ci bw = PHY_TXC1_BW_20MHZ; 611462306a36Sopenharmony_ci } 611562306a36Sopenharmony_ci } 611662306a36Sopenharmony_ci 611762306a36Sopenharmony_ci if (is_mcs_rate(rspec)) { 611862306a36Sopenharmony_ci uint mcs = rspec & RSPEC_RATE_MASK; 611962306a36Sopenharmony_ci 612062306a36Sopenharmony_ci /* bw, stf, coding-type is part of rspec_phytxbyte2 returns */ 612162306a36Sopenharmony_ci phyctl1 = rspec_phytxbyte2(rspec); 612262306a36Sopenharmony_ci /* set the upper byte of phyctl1 */ 612362306a36Sopenharmony_ci phyctl1 |= (mcs_table[mcs].tx_phy_ctl3 << 8); 612462306a36Sopenharmony_ci } else if (is_cck_rate(rspec) && !BRCMS_ISLCNPHY(wlc->band) 612562306a36Sopenharmony_ci && !BRCMS_ISSSLPNPHY(wlc->band)) { 612662306a36Sopenharmony_ci /* 612762306a36Sopenharmony_ci * In CCK mode LPPHY overloads OFDM Modulation bits with CCK 612862306a36Sopenharmony_ci * Data Rate. Eventually MIMOPHY would also be converted to 612962306a36Sopenharmony_ci * this format 613062306a36Sopenharmony_ci */ 613162306a36Sopenharmony_ci /* 0 = 1Mbps; 1 = 2Mbps; 2 = 5.5Mbps; 3 = 11Mbps */ 613262306a36Sopenharmony_ci phyctl1 = (bw | (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); 613362306a36Sopenharmony_ci } else { /* legacy OFDM/CCK */ 613462306a36Sopenharmony_ci s16 phycfg; 613562306a36Sopenharmony_ci /* get the phyctl byte from rate phycfg table */ 613662306a36Sopenharmony_ci phycfg = brcms_c_rate_legacy_phyctl(rspec2rate(rspec)); 613762306a36Sopenharmony_ci if (phycfg == -1) { 613862306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "phytxctl1_calc: wrong " 613962306a36Sopenharmony_ci "legacy OFDM/CCK rate\n"); 614062306a36Sopenharmony_ci phycfg = 0; 614162306a36Sopenharmony_ci } 614262306a36Sopenharmony_ci /* set the upper byte of phyctl1 */ 614362306a36Sopenharmony_ci phyctl1 = 614462306a36Sopenharmony_ci (bw | (phycfg << 8) | 614562306a36Sopenharmony_ci (rspec_stf(rspec) << PHY_TXC1_MODE_SHIFT)); 614662306a36Sopenharmony_ci } 614762306a36Sopenharmony_ci return phyctl1; 614862306a36Sopenharmony_ci} 614962306a36Sopenharmony_ci 615062306a36Sopenharmony_ci/* 615162306a36Sopenharmony_ci * Add struct d11txh, struct cck_phy_hdr. 615262306a36Sopenharmony_ci * 615362306a36Sopenharmony_ci * 'p' data must start with 802.11 MAC header 615462306a36Sopenharmony_ci * 'p' must allow enough bytes of local headers to be "pushed" onto the packet 615562306a36Sopenharmony_ci * 615662306a36Sopenharmony_ci * headroom == D11_PHY_HDR_LEN + D11_TXH_LEN (D11_TXH_LEN is now 104 bytes) 615762306a36Sopenharmony_ci * 615862306a36Sopenharmony_ci */ 615962306a36Sopenharmony_cistatic u16 616062306a36Sopenharmony_cibrcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc, struct ieee80211_hw *hw, 616162306a36Sopenharmony_ci struct sk_buff *p, struct scb *scb, uint frag, 616262306a36Sopenharmony_ci uint nfrags, uint queue, uint next_frag_len) 616362306a36Sopenharmony_ci{ 616462306a36Sopenharmony_ci struct ieee80211_hdr *h; 616562306a36Sopenharmony_ci struct d11txh *txh; 616662306a36Sopenharmony_ci u8 *plcp, plcp_fallback[D11_PHY_HDR_LEN]; 616762306a36Sopenharmony_ci int len, phylen, rts_phylen; 616862306a36Sopenharmony_ci u16 mch, phyctl, xfts, mainrates; 616962306a36Sopenharmony_ci u16 seq = 0, mcl = 0, status = 0, frameid = 0; 617062306a36Sopenharmony_ci u32 rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; 617162306a36Sopenharmony_ci u32 rts_rspec[2] = { BRCM_RATE_1M, BRCM_RATE_1M }; 617262306a36Sopenharmony_ci bool use_rts = false; 617362306a36Sopenharmony_ci bool use_cts = false; 617462306a36Sopenharmony_ci bool use_rifs = false; 617562306a36Sopenharmony_ci u8 preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; 617662306a36Sopenharmony_ci u8 rts_preamble_type[2] = { BRCMS_LONG_PREAMBLE, BRCMS_LONG_PREAMBLE }; 617762306a36Sopenharmony_ci u8 *rts_plcp, rts_plcp_fallback[D11_PHY_HDR_LEN]; 617862306a36Sopenharmony_ci struct ieee80211_rts *rts = NULL; 617962306a36Sopenharmony_ci bool qos; 618062306a36Sopenharmony_ci uint ac; 618162306a36Sopenharmony_ci bool hwtkmic = false; 618262306a36Sopenharmony_ci u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ; 618362306a36Sopenharmony_ci#define ANTCFG_NONE 0xFF 618462306a36Sopenharmony_ci u8 antcfg = ANTCFG_NONE; 618562306a36Sopenharmony_ci u8 fbantcfg = ANTCFG_NONE; 618662306a36Sopenharmony_ci uint phyctl1_stf = 0; 618762306a36Sopenharmony_ci u16 durid = 0; 618862306a36Sopenharmony_ci struct ieee80211_tx_rate *txrate[2]; 618962306a36Sopenharmony_ci int k; 619062306a36Sopenharmony_ci struct ieee80211_tx_info *tx_info; 619162306a36Sopenharmony_ci bool is_mcs; 619262306a36Sopenharmony_ci u16 mimo_txbw; 619362306a36Sopenharmony_ci u8 mimo_preamble_type; 619462306a36Sopenharmony_ci 619562306a36Sopenharmony_ci /* locate 802.11 MAC header */ 619662306a36Sopenharmony_ci h = (struct ieee80211_hdr *)(p->data); 619762306a36Sopenharmony_ci qos = ieee80211_is_data_qos(h->frame_control); 619862306a36Sopenharmony_ci 619962306a36Sopenharmony_ci /* compute length of frame in bytes for use in PLCP computations */ 620062306a36Sopenharmony_ci len = p->len; 620162306a36Sopenharmony_ci phylen = len + FCS_LEN; 620262306a36Sopenharmony_ci 620362306a36Sopenharmony_ci /* Get tx_info */ 620462306a36Sopenharmony_ci tx_info = IEEE80211_SKB_CB(p); 620562306a36Sopenharmony_ci 620662306a36Sopenharmony_ci /* add PLCP */ 620762306a36Sopenharmony_ci plcp = skb_push(p, D11_PHY_HDR_LEN); 620862306a36Sopenharmony_ci 620962306a36Sopenharmony_ci /* add Broadcom tx descriptor header */ 621062306a36Sopenharmony_ci txh = (struct d11txh *) skb_push(p, D11_TXH_LEN); 621162306a36Sopenharmony_ci memset(txh, 0, D11_TXH_LEN); 621262306a36Sopenharmony_ci 621362306a36Sopenharmony_ci /* setup frameid */ 621462306a36Sopenharmony_ci if (tx_info->flags & IEEE80211_TX_CTL_ASSIGN_SEQ) { 621562306a36Sopenharmony_ci /* non-AP STA should never use BCMC queue */ 621662306a36Sopenharmony_ci if (queue == TX_BCMC_FIFO) { 621762306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 621862306a36Sopenharmony_ci "wl%d: %s: ASSERT queue == TX_BCMC!\n", 621962306a36Sopenharmony_ci wlc->pub->unit, __func__); 622062306a36Sopenharmony_ci frameid = bcmc_fid_generate(wlc, NULL, txh); 622162306a36Sopenharmony_ci } else { 622262306a36Sopenharmony_ci /* Increment the counter for first fragment */ 622362306a36Sopenharmony_ci if (tx_info->flags & IEEE80211_TX_CTL_FIRST_FRAGMENT) 622462306a36Sopenharmony_ci scb->seqnum[p->priority]++; 622562306a36Sopenharmony_ci 622662306a36Sopenharmony_ci /* extract fragment number from frame first */ 622762306a36Sopenharmony_ci seq = le16_to_cpu(h->seq_ctrl) & FRAGNUM_MASK; 622862306a36Sopenharmony_ci seq |= (scb->seqnum[p->priority] << SEQNUM_SHIFT); 622962306a36Sopenharmony_ci h->seq_ctrl = cpu_to_le16(seq); 623062306a36Sopenharmony_ci 623162306a36Sopenharmony_ci frameid = ((seq << TXFID_SEQ_SHIFT) & TXFID_SEQ_MASK) | 623262306a36Sopenharmony_ci (queue & TXFID_QUEUE_MASK); 623362306a36Sopenharmony_ci } 623462306a36Sopenharmony_ci } 623562306a36Sopenharmony_ci frameid |= queue & TXFID_QUEUE_MASK; 623662306a36Sopenharmony_ci 623762306a36Sopenharmony_ci /* set the ignpmq bit for all pkts tx'd in PS mode and for beacons */ 623862306a36Sopenharmony_ci if (ieee80211_is_beacon(h->frame_control)) 623962306a36Sopenharmony_ci mcl |= TXC_IGNOREPMQ; 624062306a36Sopenharmony_ci 624162306a36Sopenharmony_ci txrate[0] = tx_info->control.rates; 624262306a36Sopenharmony_ci txrate[1] = txrate[0] + 1; 624362306a36Sopenharmony_ci 624462306a36Sopenharmony_ci /* 624562306a36Sopenharmony_ci * if rate control algorithm didn't give us a fallback 624662306a36Sopenharmony_ci * rate, use the primary rate 624762306a36Sopenharmony_ci */ 624862306a36Sopenharmony_ci if (txrate[1]->idx < 0) 624962306a36Sopenharmony_ci txrate[1] = txrate[0]; 625062306a36Sopenharmony_ci 625162306a36Sopenharmony_ci for (k = 0; k < hw->max_rates; k++) { 625262306a36Sopenharmony_ci is_mcs = txrate[k]->flags & IEEE80211_TX_RC_MCS ? true : false; 625362306a36Sopenharmony_ci if (!is_mcs) { 625462306a36Sopenharmony_ci if ((txrate[k]->idx >= 0) 625562306a36Sopenharmony_ci && (txrate[k]->idx < 625662306a36Sopenharmony_ci hw->wiphy->bands[tx_info->band]->n_bitrates)) { 625762306a36Sopenharmony_ci rspec[k] = 625862306a36Sopenharmony_ci hw->wiphy->bands[tx_info->band]-> 625962306a36Sopenharmony_ci bitrates[txrate[k]->idx].hw_value; 626062306a36Sopenharmony_ci } else { 626162306a36Sopenharmony_ci rspec[k] = BRCM_RATE_1M; 626262306a36Sopenharmony_ci } 626362306a36Sopenharmony_ci } else { 626462306a36Sopenharmony_ci rspec[k] = mac80211_wlc_set_nrate(wlc, wlc->band, 626562306a36Sopenharmony_ci NRATE_MCS_INUSE | txrate[k]->idx); 626662306a36Sopenharmony_ci } 626762306a36Sopenharmony_ci 626862306a36Sopenharmony_ci /* 626962306a36Sopenharmony_ci * Currently only support same setting for primay and 627062306a36Sopenharmony_ci * fallback rates. Unify flags for each rate into a 627162306a36Sopenharmony_ci * single value for the frame 627262306a36Sopenharmony_ci */ 627362306a36Sopenharmony_ci use_rts |= 627462306a36Sopenharmony_ci txrate[k]-> 627562306a36Sopenharmony_ci flags & IEEE80211_TX_RC_USE_RTS_CTS ? true : false; 627662306a36Sopenharmony_ci use_cts |= 627762306a36Sopenharmony_ci txrate[k]-> 627862306a36Sopenharmony_ci flags & IEEE80211_TX_RC_USE_CTS_PROTECT ? true : false; 627962306a36Sopenharmony_ci 628062306a36Sopenharmony_ci 628162306a36Sopenharmony_ci /* 628262306a36Sopenharmony_ci * (1) RATE: 628362306a36Sopenharmony_ci * determine and validate primary rate 628462306a36Sopenharmony_ci * and fallback rates 628562306a36Sopenharmony_ci */ 628662306a36Sopenharmony_ci if (!rspec_active(rspec[k])) { 628762306a36Sopenharmony_ci rspec[k] = BRCM_RATE_1M; 628862306a36Sopenharmony_ci } else { 628962306a36Sopenharmony_ci if (!is_multicast_ether_addr(h->addr1)) { 629062306a36Sopenharmony_ci /* set tx antenna config */ 629162306a36Sopenharmony_ci brcms_c_antsel_antcfg_get(wlc->asi, false, 629262306a36Sopenharmony_ci false, 0, 0, &antcfg, &fbantcfg); 629362306a36Sopenharmony_ci } 629462306a36Sopenharmony_ci } 629562306a36Sopenharmony_ci } 629662306a36Sopenharmony_ci 629762306a36Sopenharmony_ci phyctl1_stf = wlc->stf->ss_opmode; 629862306a36Sopenharmony_ci 629962306a36Sopenharmony_ci if (wlc->pub->_n_enab & SUPPORT_11N) { 630062306a36Sopenharmony_ci for (k = 0; k < hw->max_rates; k++) { 630162306a36Sopenharmony_ci /* 630262306a36Sopenharmony_ci * apply siso/cdd to single stream mcs's or ofdm 630362306a36Sopenharmony_ci * if rspec is auto selected 630462306a36Sopenharmony_ci */ 630562306a36Sopenharmony_ci if (((is_mcs_rate(rspec[k]) && 630662306a36Sopenharmony_ci is_single_stream(rspec[k] & RSPEC_RATE_MASK)) || 630762306a36Sopenharmony_ci is_ofdm_rate(rspec[k])) 630862306a36Sopenharmony_ci && ((rspec[k] & RSPEC_OVERRIDE_MCS_ONLY) 630962306a36Sopenharmony_ci || !(rspec[k] & RSPEC_OVERRIDE))) { 631062306a36Sopenharmony_ci rspec[k] &= ~(RSPEC_STF_MASK | RSPEC_STC_MASK); 631162306a36Sopenharmony_ci 631262306a36Sopenharmony_ci /* For SISO MCS use STBC if possible */ 631362306a36Sopenharmony_ci if (is_mcs_rate(rspec[k]) 631462306a36Sopenharmony_ci && BRCMS_STF_SS_STBC_TX(wlc, scb)) { 631562306a36Sopenharmony_ci u8 stc; 631662306a36Sopenharmony_ci 631762306a36Sopenharmony_ci /* Nss for single stream is always 1 */ 631862306a36Sopenharmony_ci stc = 1; 631962306a36Sopenharmony_ci rspec[k] |= (PHY_TXC1_MODE_STBC << 632062306a36Sopenharmony_ci RSPEC_STF_SHIFT) | 632162306a36Sopenharmony_ci (stc << RSPEC_STC_SHIFT); 632262306a36Sopenharmony_ci } else 632362306a36Sopenharmony_ci rspec[k] |= 632462306a36Sopenharmony_ci (phyctl1_stf << RSPEC_STF_SHIFT); 632562306a36Sopenharmony_ci } 632662306a36Sopenharmony_ci 632762306a36Sopenharmony_ci /* 632862306a36Sopenharmony_ci * Is the phy configured to use 40MHZ frames? If 632962306a36Sopenharmony_ci * so then pick the desired txbw 633062306a36Sopenharmony_ci */ 633162306a36Sopenharmony_ci if (brcms_chspec_bw(wlc->chanspec) == BRCMS_40_MHZ) { 633262306a36Sopenharmony_ci /* default txbw is 20in40 SB */ 633362306a36Sopenharmony_ci mimo_ctlchbw = mimo_txbw = 633462306a36Sopenharmony_ci CHSPEC_SB_UPPER(wlc_phy_chanspec_get( 633562306a36Sopenharmony_ci wlc->band->pi)) 633662306a36Sopenharmony_ci ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ; 633762306a36Sopenharmony_ci 633862306a36Sopenharmony_ci if (is_mcs_rate(rspec[k])) { 633962306a36Sopenharmony_ci /* mcs 32 must be 40b/w DUP */ 634062306a36Sopenharmony_ci if ((rspec[k] & RSPEC_RATE_MASK) 634162306a36Sopenharmony_ci == 32) { 634262306a36Sopenharmony_ci mimo_txbw = 634362306a36Sopenharmony_ci PHY_TXC1_BW_40MHZ_DUP; 634462306a36Sopenharmony_ci /* use override */ 634562306a36Sopenharmony_ci } else if (wlc->mimo_40txbw != AUTO) 634662306a36Sopenharmony_ci mimo_txbw = wlc->mimo_40txbw; 634762306a36Sopenharmony_ci /* else check if dst is using 40 Mhz */ 634862306a36Sopenharmony_ci else if (scb->flags & SCB_IS40) 634962306a36Sopenharmony_ci mimo_txbw = PHY_TXC1_BW_40MHZ; 635062306a36Sopenharmony_ci } else if (is_ofdm_rate(rspec[k])) { 635162306a36Sopenharmony_ci if (wlc->ofdm_40txbw != AUTO) 635262306a36Sopenharmony_ci mimo_txbw = wlc->ofdm_40txbw; 635362306a36Sopenharmony_ci } else if (wlc->cck_40txbw != AUTO) { 635462306a36Sopenharmony_ci mimo_txbw = wlc->cck_40txbw; 635562306a36Sopenharmony_ci } 635662306a36Sopenharmony_ci } else { 635762306a36Sopenharmony_ci /* 635862306a36Sopenharmony_ci * mcs32 is 40 b/w only. 635962306a36Sopenharmony_ci * This is possible for probe packets on 636062306a36Sopenharmony_ci * a STA during SCAN 636162306a36Sopenharmony_ci */ 636262306a36Sopenharmony_ci if ((rspec[k] & RSPEC_RATE_MASK) == 32) 636362306a36Sopenharmony_ci /* mcs 0 */ 636462306a36Sopenharmony_ci rspec[k] = RSPEC_MIMORATE; 636562306a36Sopenharmony_ci 636662306a36Sopenharmony_ci mimo_txbw = PHY_TXC1_BW_20MHZ; 636762306a36Sopenharmony_ci } 636862306a36Sopenharmony_ci 636962306a36Sopenharmony_ci /* Set channel width */ 637062306a36Sopenharmony_ci rspec[k] &= ~RSPEC_BW_MASK; 637162306a36Sopenharmony_ci if ((k == 0) || ((k > 0) && is_mcs_rate(rspec[k]))) 637262306a36Sopenharmony_ci rspec[k] |= (mimo_txbw << RSPEC_BW_SHIFT); 637362306a36Sopenharmony_ci else 637462306a36Sopenharmony_ci rspec[k] |= (mimo_ctlchbw << RSPEC_BW_SHIFT); 637562306a36Sopenharmony_ci 637662306a36Sopenharmony_ci /* Disable short GI, not supported yet */ 637762306a36Sopenharmony_ci rspec[k] &= ~RSPEC_SHORT_GI; 637862306a36Sopenharmony_ci 637962306a36Sopenharmony_ci mimo_preamble_type = BRCMS_MM_PREAMBLE; 638062306a36Sopenharmony_ci if (txrate[k]->flags & IEEE80211_TX_RC_GREEN_FIELD) 638162306a36Sopenharmony_ci mimo_preamble_type = BRCMS_GF_PREAMBLE; 638262306a36Sopenharmony_ci 638362306a36Sopenharmony_ci if ((txrate[k]->flags & IEEE80211_TX_RC_MCS) 638462306a36Sopenharmony_ci && (!is_mcs_rate(rspec[k]))) { 638562306a36Sopenharmony_ci brcms_warn(wlc->hw->d11core, 638662306a36Sopenharmony_ci "wl%d: %s: IEEE80211_TX_RC_MCS != is_mcs_rate(rspec)\n", 638762306a36Sopenharmony_ci wlc->pub->unit, __func__); 638862306a36Sopenharmony_ci } 638962306a36Sopenharmony_ci 639062306a36Sopenharmony_ci if (is_mcs_rate(rspec[k])) { 639162306a36Sopenharmony_ci preamble_type[k] = mimo_preamble_type; 639262306a36Sopenharmony_ci 639362306a36Sopenharmony_ci /* 639462306a36Sopenharmony_ci * if SGI is selected, then forced mm 639562306a36Sopenharmony_ci * for single stream 639662306a36Sopenharmony_ci */ 639762306a36Sopenharmony_ci if ((rspec[k] & RSPEC_SHORT_GI) 639862306a36Sopenharmony_ci && is_single_stream(rspec[k] & 639962306a36Sopenharmony_ci RSPEC_RATE_MASK)) 640062306a36Sopenharmony_ci preamble_type[k] = BRCMS_MM_PREAMBLE; 640162306a36Sopenharmony_ci } 640262306a36Sopenharmony_ci 640362306a36Sopenharmony_ci /* should be better conditionalized */ 640462306a36Sopenharmony_ci if (!is_mcs_rate(rspec[0]) 640562306a36Sopenharmony_ci && (tx_info->control.rates[0]. 640662306a36Sopenharmony_ci flags & IEEE80211_TX_RC_USE_SHORT_PREAMBLE)) 640762306a36Sopenharmony_ci preamble_type[k] = BRCMS_SHORT_PREAMBLE; 640862306a36Sopenharmony_ci } 640962306a36Sopenharmony_ci } else { 641062306a36Sopenharmony_ci for (k = 0; k < hw->max_rates; k++) { 641162306a36Sopenharmony_ci /* Set ctrlchbw as 20Mhz */ 641262306a36Sopenharmony_ci rspec[k] &= ~RSPEC_BW_MASK; 641362306a36Sopenharmony_ci rspec[k] |= (PHY_TXC1_BW_20MHZ << RSPEC_BW_SHIFT); 641462306a36Sopenharmony_ci 641562306a36Sopenharmony_ci /* for nphy, stf of ofdm frames must follow policies */ 641662306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc->band) && is_ofdm_rate(rspec[k])) { 641762306a36Sopenharmony_ci rspec[k] &= ~RSPEC_STF_MASK; 641862306a36Sopenharmony_ci rspec[k] |= phyctl1_stf << RSPEC_STF_SHIFT; 641962306a36Sopenharmony_ci } 642062306a36Sopenharmony_ci } 642162306a36Sopenharmony_ci } 642262306a36Sopenharmony_ci 642362306a36Sopenharmony_ci /* Reset these for use with AMPDU's */ 642462306a36Sopenharmony_ci txrate[0]->count = 0; 642562306a36Sopenharmony_ci txrate[1]->count = 0; 642662306a36Sopenharmony_ci 642762306a36Sopenharmony_ci /* (2) PROTECTION, may change rspec */ 642862306a36Sopenharmony_ci if ((ieee80211_is_data(h->frame_control) || 642962306a36Sopenharmony_ci ieee80211_is_mgmt(h->frame_control)) && 643062306a36Sopenharmony_ci (phylen > wlc->RTSThresh) && !is_multicast_ether_addr(h->addr1)) 643162306a36Sopenharmony_ci use_rts = true; 643262306a36Sopenharmony_ci 643362306a36Sopenharmony_ci /* (3) PLCP: determine PLCP header and MAC duration, 643462306a36Sopenharmony_ci * fill struct d11txh */ 643562306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, rspec[0], phylen, plcp); 643662306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, rspec[1], phylen, plcp_fallback); 643762306a36Sopenharmony_ci memcpy(&txh->FragPLCPFallback, 643862306a36Sopenharmony_ci plcp_fallback, sizeof(txh->FragPLCPFallback)); 643962306a36Sopenharmony_ci 644062306a36Sopenharmony_ci /* Length field now put in CCK FBR CRC field */ 644162306a36Sopenharmony_ci if (is_cck_rate(rspec[1])) { 644262306a36Sopenharmony_ci txh->FragPLCPFallback[4] = phylen & 0xff; 644362306a36Sopenharmony_ci txh->FragPLCPFallback[5] = (phylen & 0xff00) >> 8; 644462306a36Sopenharmony_ci } 644562306a36Sopenharmony_ci 644662306a36Sopenharmony_ci /* MIMO-RATE: need validation ?? */ 644762306a36Sopenharmony_ci mainrates = is_ofdm_rate(rspec[0]) ? 644862306a36Sopenharmony_ci D11A_PHY_HDR_GRATE((struct ofdm_phy_hdr *) plcp) : 644962306a36Sopenharmony_ci plcp[0]; 645062306a36Sopenharmony_ci 645162306a36Sopenharmony_ci /* DUR field for main rate */ 645262306a36Sopenharmony_ci if (!ieee80211_is_pspoll(h->frame_control) && 645362306a36Sopenharmony_ci !is_multicast_ether_addr(h->addr1) && !use_rifs) { 645462306a36Sopenharmony_ci durid = 645562306a36Sopenharmony_ci brcms_c_compute_frame_dur(wlc, rspec[0], preamble_type[0], 645662306a36Sopenharmony_ci next_frag_len); 645762306a36Sopenharmony_ci h->duration_id = cpu_to_le16(durid); 645862306a36Sopenharmony_ci } else if (use_rifs) { 645962306a36Sopenharmony_ci /* NAV protect to end of next max packet size */ 646062306a36Sopenharmony_ci durid = 646162306a36Sopenharmony_ci (u16) brcms_c_calc_frame_time(wlc, rspec[0], 646262306a36Sopenharmony_ci preamble_type[0], 646362306a36Sopenharmony_ci DOT11_MAX_FRAG_LEN); 646462306a36Sopenharmony_ci durid += RIFS_11N_TIME; 646562306a36Sopenharmony_ci h->duration_id = cpu_to_le16(durid); 646662306a36Sopenharmony_ci } 646762306a36Sopenharmony_ci 646862306a36Sopenharmony_ci /* DUR field for fallback rate */ 646962306a36Sopenharmony_ci if (ieee80211_is_pspoll(h->frame_control)) 647062306a36Sopenharmony_ci txh->FragDurFallback = h->duration_id; 647162306a36Sopenharmony_ci else if (is_multicast_ether_addr(h->addr1) || use_rifs) 647262306a36Sopenharmony_ci txh->FragDurFallback = 0; 647362306a36Sopenharmony_ci else { 647462306a36Sopenharmony_ci durid = brcms_c_compute_frame_dur(wlc, rspec[1], 647562306a36Sopenharmony_ci preamble_type[1], next_frag_len); 647662306a36Sopenharmony_ci txh->FragDurFallback = cpu_to_le16(durid); 647762306a36Sopenharmony_ci } 647862306a36Sopenharmony_ci 647962306a36Sopenharmony_ci /* (4) MAC-HDR: MacTxControlLow */ 648062306a36Sopenharmony_ci if (frag == 0) 648162306a36Sopenharmony_ci mcl |= TXC_STARTMSDU; 648262306a36Sopenharmony_ci 648362306a36Sopenharmony_ci if (!is_multicast_ether_addr(h->addr1)) 648462306a36Sopenharmony_ci mcl |= TXC_IMMEDACK; 648562306a36Sopenharmony_ci 648662306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_5G) 648762306a36Sopenharmony_ci mcl |= TXC_FREQBAND_5G; 648862306a36Sopenharmony_ci 648962306a36Sopenharmony_ci if (CHSPEC_IS40(wlc_phy_chanspec_get(wlc->band->pi))) 649062306a36Sopenharmony_ci mcl |= TXC_BW_40; 649162306a36Sopenharmony_ci 649262306a36Sopenharmony_ci /* set AMIC bit if using hardware TKIP MIC */ 649362306a36Sopenharmony_ci if (hwtkmic) 649462306a36Sopenharmony_ci mcl |= TXC_AMIC; 649562306a36Sopenharmony_ci 649662306a36Sopenharmony_ci txh->MacTxControlLow = cpu_to_le16(mcl); 649762306a36Sopenharmony_ci 649862306a36Sopenharmony_ci /* MacTxControlHigh */ 649962306a36Sopenharmony_ci mch = 0; 650062306a36Sopenharmony_ci 650162306a36Sopenharmony_ci /* Set fallback rate preamble type */ 650262306a36Sopenharmony_ci if ((preamble_type[1] == BRCMS_SHORT_PREAMBLE) || 650362306a36Sopenharmony_ci (preamble_type[1] == BRCMS_GF_PREAMBLE)) { 650462306a36Sopenharmony_ci if (rspec2rate(rspec[1]) != BRCM_RATE_1M) 650562306a36Sopenharmony_ci mch |= TXC_PREAMBLE_DATA_FB_SHORT; 650662306a36Sopenharmony_ci } 650762306a36Sopenharmony_ci 650862306a36Sopenharmony_ci /* MacFrameControl */ 650962306a36Sopenharmony_ci memcpy(&txh->MacFrameControl, &h->frame_control, sizeof(u16)); 651062306a36Sopenharmony_ci txh->TxFesTimeNormal = cpu_to_le16(0); 651162306a36Sopenharmony_ci 651262306a36Sopenharmony_ci txh->TxFesTimeFallback = cpu_to_le16(0); 651362306a36Sopenharmony_ci 651462306a36Sopenharmony_ci /* TxFrameRA */ 651562306a36Sopenharmony_ci memcpy(&txh->TxFrameRA, &h->addr1, ETH_ALEN); 651662306a36Sopenharmony_ci 651762306a36Sopenharmony_ci /* TxFrameID */ 651862306a36Sopenharmony_ci txh->TxFrameID = cpu_to_le16(frameid); 651962306a36Sopenharmony_ci 652062306a36Sopenharmony_ci /* 652162306a36Sopenharmony_ci * TxStatus, Note the case of recreating the first frag of a suppressed 652262306a36Sopenharmony_ci * frame then we may need to reset the retry cnt's via the status reg 652362306a36Sopenharmony_ci */ 652462306a36Sopenharmony_ci txh->TxStatus = cpu_to_le16(status); 652562306a36Sopenharmony_ci 652662306a36Sopenharmony_ci /* 652762306a36Sopenharmony_ci * extra fields for ucode AMPDU aggregation, the new fields are added to 652862306a36Sopenharmony_ci * the END of previous structure so that it's compatible in driver. 652962306a36Sopenharmony_ci */ 653062306a36Sopenharmony_ci txh->MaxNMpdus = cpu_to_le16(0); 653162306a36Sopenharmony_ci txh->MaxABytes_MRT = cpu_to_le16(0); 653262306a36Sopenharmony_ci txh->MaxABytes_FBR = cpu_to_le16(0); 653362306a36Sopenharmony_ci txh->MinMBytes = cpu_to_le16(0); 653462306a36Sopenharmony_ci 653562306a36Sopenharmony_ci /* (5) RTS/CTS: determine RTS/CTS PLCP header and MAC duration, 653662306a36Sopenharmony_ci * furnish struct d11txh */ 653762306a36Sopenharmony_ci /* RTS PLCP header and RTS frame */ 653862306a36Sopenharmony_ci if (use_rts || use_cts) { 653962306a36Sopenharmony_ci if (use_rts && use_cts) 654062306a36Sopenharmony_ci use_cts = false; 654162306a36Sopenharmony_ci 654262306a36Sopenharmony_ci for (k = 0; k < 2; k++) { 654362306a36Sopenharmony_ci rts_rspec[k] = brcms_c_rspec_to_rts_rspec(wlc, rspec[k], 654462306a36Sopenharmony_ci false, 654562306a36Sopenharmony_ci mimo_ctlchbw); 654662306a36Sopenharmony_ci } 654762306a36Sopenharmony_ci 654862306a36Sopenharmony_ci if (!is_ofdm_rate(rts_rspec[0]) && 654962306a36Sopenharmony_ci !((rspec2rate(rts_rspec[0]) == BRCM_RATE_1M) || 655062306a36Sopenharmony_ci (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { 655162306a36Sopenharmony_ci rts_preamble_type[0] = BRCMS_SHORT_PREAMBLE; 655262306a36Sopenharmony_ci mch |= TXC_PREAMBLE_RTS_MAIN_SHORT; 655362306a36Sopenharmony_ci } 655462306a36Sopenharmony_ci 655562306a36Sopenharmony_ci if (!is_ofdm_rate(rts_rspec[1]) && 655662306a36Sopenharmony_ci !((rspec2rate(rts_rspec[1]) == BRCM_RATE_1M) || 655762306a36Sopenharmony_ci (wlc->PLCPHdr_override == BRCMS_PLCP_LONG))) { 655862306a36Sopenharmony_ci rts_preamble_type[1] = BRCMS_SHORT_PREAMBLE; 655962306a36Sopenharmony_ci mch |= TXC_PREAMBLE_RTS_FB_SHORT; 656062306a36Sopenharmony_ci } 656162306a36Sopenharmony_ci 656262306a36Sopenharmony_ci /* RTS/CTS additions to MacTxControlLow */ 656362306a36Sopenharmony_ci if (use_cts) { 656462306a36Sopenharmony_ci txh->MacTxControlLow |= cpu_to_le16(TXC_SENDCTS); 656562306a36Sopenharmony_ci } else { 656662306a36Sopenharmony_ci txh->MacTxControlLow |= cpu_to_le16(TXC_SENDRTS); 656762306a36Sopenharmony_ci txh->MacTxControlLow |= cpu_to_le16(TXC_LONGFRAME); 656862306a36Sopenharmony_ci } 656962306a36Sopenharmony_ci 657062306a36Sopenharmony_ci /* RTS PLCP header */ 657162306a36Sopenharmony_ci rts_plcp = txh->RTSPhyHeader; 657262306a36Sopenharmony_ci if (use_cts) 657362306a36Sopenharmony_ci rts_phylen = DOT11_CTS_LEN + FCS_LEN; 657462306a36Sopenharmony_ci else 657562306a36Sopenharmony_ci rts_phylen = DOT11_RTS_LEN + FCS_LEN; 657662306a36Sopenharmony_ci 657762306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, rts_rspec[0], rts_phylen, rts_plcp); 657862306a36Sopenharmony_ci 657962306a36Sopenharmony_ci /* fallback rate version of RTS PLCP header */ 658062306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, rts_rspec[1], rts_phylen, 658162306a36Sopenharmony_ci rts_plcp_fallback); 658262306a36Sopenharmony_ci memcpy(&txh->RTSPLCPFallback, rts_plcp_fallback, 658362306a36Sopenharmony_ci sizeof(txh->RTSPLCPFallback)); 658462306a36Sopenharmony_ci 658562306a36Sopenharmony_ci /* RTS frame fields... */ 658662306a36Sopenharmony_ci rts = (struct ieee80211_rts *)&txh->rts_frame; 658762306a36Sopenharmony_ci 658862306a36Sopenharmony_ci durid = brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec[0], 658962306a36Sopenharmony_ci rspec[0], rts_preamble_type[0], 659062306a36Sopenharmony_ci preamble_type[0], phylen, false); 659162306a36Sopenharmony_ci rts->duration = cpu_to_le16(durid); 659262306a36Sopenharmony_ci /* fallback rate version of RTS DUR field */ 659362306a36Sopenharmony_ci durid = brcms_c_compute_rtscts_dur(wlc, use_cts, 659462306a36Sopenharmony_ci rts_rspec[1], rspec[1], 659562306a36Sopenharmony_ci rts_preamble_type[1], 659662306a36Sopenharmony_ci preamble_type[1], phylen, false); 659762306a36Sopenharmony_ci txh->RTSDurFallback = cpu_to_le16(durid); 659862306a36Sopenharmony_ci 659962306a36Sopenharmony_ci if (use_cts) { 660062306a36Sopenharmony_ci rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | 660162306a36Sopenharmony_ci IEEE80211_STYPE_CTS); 660262306a36Sopenharmony_ci 660362306a36Sopenharmony_ci memcpy(&rts->ra, &h->addr2, ETH_ALEN); 660462306a36Sopenharmony_ci } else { 660562306a36Sopenharmony_ci rts->frame_control = cpu_to_le16(IEEE80211_FTYPE_CTL | 660662306a36Sopenharmony_ci IEEE80211_STYPE_RTS); 660762306a36Sopenharmony_ci 660862306a36Sopenharmony_ci memcpy(&rts->ra, &h->addr1, ETH_ALEN); 660962306a36Sopenharmony_ci memcpy(&rts->ta, &h->addr2, ETH_ALEN); 661062306a36Sopenharmony_ci } 661162306a36Sopenharmony_ci 661262306a36Sopenharmony_ci /* mainrate 661362306a36Sopenharmony_ci * low 8 bits: main frag rate/mcs, 661462306a36Sopenharmony_ci * high 8 bits: rts/cts rate/mcs 661562306a36Sopenharmony_ci */ 661662306a36Sopenharmony_ci mainrates |= (is_ofdm_rate(rts_rspec[0]) ? 661762306a36Sopenharmony_ci D11A_PHY_HDR_GRATE( 661862306a36Sopenharmony_ci (struct ofdm_phy_hdr *) rts_plcp) : 661962306a36Sopenharmony_ci rts_plcp[0]) << 8; 662062306a36Sopenharmony_ci } else { 662162306a36Sopenharmony_ci memset(txh->RTSPhyHeader, 0, D11_PHY_HDR_LEN); 662262306a36Sopenharmony_ci memset(&txh->rts_frame, 0, sizeof(struct ieee80211_rts)); 662362306a36Sopenharmony_ci memset(txh->RTSPLCPFallback, 0, sizeof(txh->RTSPLCPFallback)); 662462306a36Sopenharmony_ci txh->RTSDurFallback = 0; 662562306a36Sopenharmony_ci } 662662306a36Sopenharmony_ci 662762306a36Sopenharmony_ci#ifdef SUPPORT_40MHZ 662862306a36Sopenharmony_ci /* add null delimiter count */ 662962306a36Sopenharmony_ci if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) && is_mcs_rate(rspec)) 663062306a36Sopenharmony_ci txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 663162306a36Sopenharmony_ci brcm_c_ampdu_null_delim_cnt(wlc->ampdu, scb, rspec, phylen); 663262306a36Sopenharmony_ci 663362306a36Sopenharmony_ci#endif 663462306a36Sopenharmony_ci 663562306a36Sopenharmony_ci /* 663662306a36Sopenharmony_ci * Now that RTS/RTS FB preamble types are updated, write 663762306a36Sopenharmony_ci * the final value 663862306a36Sopenharmony_ci */ 663962306a36Sopenharmony_ci txh->MacTxControlHigh = cpu_to_le16(mch); 664062306a36Sopenharmony_ci 664162306a36Sopenharmony_ci /* 664262306a36Sopenharmony_ci * MainRates (both the rts and frag plcp rates have 664362306a36Sopenharmony_ci * been calculated now) 664462306a36Sopenharmony_ci */ 664562306a36Sopenharmony_ci txh->MainRates = cpu_to_le16(mainrates); 664662306a36Sopenharmony_ci 664762306a36Sopenharmony_ci /* XtraFrameTypes */ 664862306a36Sopenharmony_ci xfts = frametype(rspec[1], wlc->mimoft); 664962306a36Sopenharmony_ci xfts |= (frametype(rts_rspec[0], wlc->mimoft) << XFTS_RTS_FT_SHIFT); 665062306a36Sopenharmony_ci xfts |= (frametype(rts_rspec[1], wlc->mimoft) << XFTS_FBRRTS_FT_SHIFT); 665162306a36Sopenharmony_ci xfts |= CHSPEC_CHANNEL(wlc_phy_chanspec_get(wlc->band->pi)) << 665262306a36Sopenharmony_ci XFTS_CHANNEL_SHIFT; 665362306a36Sopenharmony_ci txh->XtraFrameTypes = cpu_to_le16(xfts); 665462306a36Sopenharmony_ci 665562306a36Sopenharmony_ci /* PhyTxControlWord */ 665662306a36Sopenharmony_ci phyctl = frametype(rspec[0], wlc->mimoft); 665762306a36Sopenharmony_ci if ((preamble_type[0] == BRCMS_SHORT_PREAMBLE) || 665862306a36Sopenharmony_ci (preamble_type[0] == BRCMS_GF_PREAMBLE)) { 665962306a36Sopenharmony_ci if (rspec2rate(rspec[0]) != BRCM_RATE_1M) 666062306a36Sopenharmony_ci phyctl |= PHY_TXC_SHORT_HDR; 666162306a36Sopenharmony_ci } 666262306a36Sopenharmony_ci 666362306a36Sopenharmony_ci /* phytxant is properly bit shifted */ 666462306a36Sopenharmony_ci phyctl |= brcms_c_stf_d11hdrs_phyctl_txant(wlc, rspec[0]); 666562306a36Sopenharmony_ci txh->PhyTxControlWord = cpu_to_le16(phyctl); 666662306a36Sopenharmony_ci 666762306a36Sopenharmony_ci /* PhyTxControlWord_1 */ 666862306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) { 666962306a36Sopenharmony_ci u16 phyctl1 = 0; 667062306a36Sopenharmony_ci 667162306a36Sopenharmony_ci phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[0]); 667262306a36Sopenharmony_ci txh->PhyTxControlWord_1 = cpu_to_le16(phyctl1); 667362306a36Sopenharmony_ci phyctl1 = brcms_c_phytxctl1_calc(wlc, rspec[1]); 667462306a36Sopenharmony_ci txh->PhyTxControlWord_1_Fbr = cpu_to_le16(phyctl1); 667562306a36Sopenharmony_ci 667662306a36Sopenharmony_ci if (use_rts || use_cts) { 667762306a36Sopenharmony_ci phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[0]); 667862306a36Sopenharmony_ci txh->PhyTxControlWord_1_Rts = cpu_to_le16(phyctl1); 667962306a36Sopenharmony_ci phyctl1 = brcms_c_phytxctl1_calc(wlc, rts_rspec[1]); 668062306a36Sopenharmony_ci txh->PhyTxControlWord_1_FbrRts = cpu_to_le16(phyctl1); 668162306a36Sopenharmony_ci } 668262306a36Sopenharmony_ci 668362306a36Sopenharmony_ci /* 668462306a36Sopenharmony_ci * For mcs frames, if mixedmode(overloaded with long preamble) 668562306a36Sopenharmony_ci * is going to be set, fill in non-zero MModeLen and/or 668662306a36Sopenharmony_ci * MModeFbrLen it will be unnecessary if they are separated 668762306a36Sopenharmony_ci */ 668862306a36Sopenharmony_ci if (is_mcs_rate(rspec[0]) && 668962306a36Sopenharmony_ci (preamble_type[0] == BRCMS_MM_PREAMBLE)) { 669062306a36Sopenharmony_ci u16 mmodelen = 669162306a36Sopenharmony_ci brcms_c_calc_lsig_len(wlc, rspec[0], phylen); 669262306a36Sopenharmony_ci txh->MModeLen = cpu_to_le16(mmodelen); 669362306a36Sopenharmony_ci } 669462306a36Sopenharmony_ci 669562306a36Sopenharmony_ci if (is_mcs_rate(rspec[1]) && 669662306a36Sopenharmony_ci (preamble_type[1] == BRCMS_MM_PREAMBLE)) { 669762306a36Sopenharmony_ci u16 mmodefbrlen = 669862306a36Sopenharmony_ci brcms_c_calc_lsig_len(wlc, rspec[1], phylen); 669962306a36Sopenharmony_ci txh->MModeFbrLen = cpu_to_le16(mmodefbrlen); 670062306a36Sopenharmony_ci } 670162306a36Sopenharmony_ci } 670262306a36Sopenharmony_ci 670362306a36Sopenharmony_ci ac = skb_get_queue_mapping(p); 670462306a36Sopenharmony_ci if ((scb->flags & SCB_WMECAP) && qos && wlc->edcf_txop[ac]) { 670562306a36Sopenharmony_ci uint frag_dur, dur, dur_fallback; 670662306a36Sopenharmony_ci 670762306a36Sopenharmony_ci /* WME: Update TXOP threshold */ 670862306a36Sopenharmony_ci if (!(tx_info->flags & IEEE80211_TX_CTL_AMPDU) && frag == 0) { 670962306a36Sopenharmony_ci frag_dur = 671062306a36Sopenharmony_ci brcms_c_calc_frame_time(wlc, rspec[0], 671162306a36Sopenharmony_ci preamble_type[0], phylen); 671262306a36Sopenharmony_ci 671362306a36Sopenharmony_ci if (rts) { 671462306a36Sopenharmony_ci /* 1 RTS or CTS-to-self frame */ 671562306a36Sopenharmony_ci dur = 671662306a36Sopenharmony_ci brcms_c_calc_cts_time(wlc, rts_rspec[0], 671762306a36Sopenharmony_ci rts_preamble_type[0]); 671862306a36Sopenharmony_ci dur_fallback = 671962306a36Sopenharmony_ci brcms_c_calc_cts_time(wlc, rts_rspec[1], 672062306a36Sopenharmony_ci rts_preamble_type[1]); 672162306a36Sopenharmony_ci /* (SIFS + CTS) + SIFS + frame + SIFS + ACK */ 672262306a36Sopenharmony_ci dur += le16_to_cpu(rts->duration); 672362306a36Sopenharmony_ci dur_fallback += 672462306a36Sopenharmony_ci le16_to_cpu(txh->RTSDurFallback); 672562306a36Sopenharmony_ci } else if (use_rifs) { 672662306a36Sopenharmony_ci dur = frag_dur; 672762306a36Sopenharmony_ci dur_fallback = 0; 672862306a36Sopenharmony_ci } else { 672962306a36Sopenharmony_ci /* frame + SIFS + ACK */ 673062306a36Sopenharmony_ci dur = frag_dur; 673162306a36Sopenharmony_ci dur += 673262306a36Sopenharmony_ci brcms_c_compute_frame_dur(wlc, rspec[0], 673362306a36Sopenharmony_ci preamble_type[0], 0); 673462306a36Sopenharmony_ci 673562306a36Sopenharmony_ci dur_fallback = 673662306a36Sopenharmony_ci brcms_c_calc_frame_time(wlc, rspec[1], 673762306a36Sopenharmony_ci preamble_type[1], 673862306a36Sopenharmony_ci phylen); 673962306a36Sopenharmony_ci dur_fallback += 674062306a36Sopenharmony_ci brcms_c_compute_frame_dur(wlc, rspec[1], 674162306a36Sopenharmony_ci preamble_type[1], 0); 674262306a36Sopenharmony_ci } 674362306a36Sopenharmony_ci /* NEED to set TxFesTimeNormal (hard) */ 674462306a36Sopenharmony_ci txh->TxFesTimeNormal = cpu_to_le16((u16) dur); 674562306a36Sopenharmony_ci /* 674662306a36Sopenharmony_ci * NEED to set fallback rate version of 674762306a36Sopenharmony_ci * TxFesTimeNormal (hard) 674862306a36Sopenharmony_ci */ 674962306a36Sopenharmony_ci txh->TxFesTimeFallback = 675062306a36Sopenharmony_ci cpu_to_le16((u16) dur_fallback); 675162306a36Sopenharmony_ci 675262306a36Sopenharmony_ci /* 675362306a36Sopenharmony_ci * update txop byte threshold (txop minus intraframe 675462306a36Sopenharmony_ci * overhead) 675562306a36Sopenharmony_ci */ 675662306a36Sopenharmony_ci if (wlc->edcf_txop[ac] >= (dur - frag_dur)) { 675762306a36Sopenharmony_ci uint newfragthresh; 675862306a36Sopenharmony_ci 675962306a36Sopenharmony_ci newfragthresh = 676062306a36Sopenharmony_ci brcms_c_calc_frame_len(wlc, 676162306a36Sopenharmony_ci rspec[0], preamble_type[0], 676262306a36Sopenharmony_ci (wlc->edcf_txop[ac] - 676362306a36Sopenharmony_ci (dur - frag_dur))); 676462306a36Sopenharmony_ci /* range bound the fragthreshold */ 676562306a36Sopenharmony_ci if (newfragthresh < DOT11_MIN_FRAG_LEN) 676662306a36Sopenharmony_ci newfragthresh = 676762306a36Sopenharmony_ci DOT11_MIN_FRAG_LEN; 676862306a36Sopenharmony_ci else if (newfragthresh > 676962306a36Sopenharmony_ci wlc->usr_fragthresh) 677062306a36Sopenharmony_ci newfragthresh = 677162306a36Sopenharmony_ci wlc->usr_fragthresh; 677262306a36Sopenharmony_ci /* update the fragthresh and do txc update */ 677362306a36Sopenharmony_ci if (wlc->fragthresh[queue] != 677462306a36Sopenharmony_ci (u16) newfragthresh) 677562306a36Sopenharmony_ci wlc->fragthresh[queue] = 677662306a36Sopenharmony_ci (u16) newfragthresh; 677762306a36Sopenharmony_ci } else { 677862306a36Sopenharmony_ci brcms_warn(wlc->hw->d11core, 677962306a36Sopenharmony_ci "wl%d: %s txop invalid for rate %d\n", 678062306a36Sopenharmony_ci wlc->pub->unit, fifo_names[queue], 678162306a36Sopenharmony_ci rspec2rate(rspec[0])); 678262306a36Sopenharmony_ci } 678362306a36Sopenharmony_ci 678462306a36Sopenharmony_ci if (dur > wlc->edcf_txop[ac]) 678562306a36Sopenharmony_ci brcms_warn(wlc->hw->d11core, 678662306a36Sopenharmony_ci "wl%d: %s: %s txop exceeded phylen %d/%d dur %d/%d\n", 678762306a36Sopenharmony_ci wlc->pub->unit, __func__, 678862306a36Sopenharmony_ci fifo_names[queue], 678962306a36Sopenharmony_ci phylen, wlc->fragthresh[queue], 679062306a36Sopenharmony_ci dur, wlc->edcf_txop[ac]); 679162306a36Sopenharmony_ci } 679262306a36Sopenharmony_ci } 679362306a36Sopenharmony_ci 679462306a36Sopenharmony_ci return 0; 679562306a36Sopenharmony_ci} 679662306a36Sopenharmony_ci 679762306a36Sopenharmony_cistatic int brcms_c_tx(struct brcms_c_info *wlc, struct sk_buff *skb) 679862306a36Sopenharmony_ci{ 679962306a36Sopenharmony_ci struct dma_pub *dma; 680062306a36Sopenharmony_ci int fifo, ret = -ENOSPC; 680162306a36Sopenharmony_ci struct d11txh *txh; 680262306a36Sopenharmony_ci u16 frameid = INVALIDFID; 680362306a36Sopenharmony_ci 680462306a36Sopenharmony_ci fifo = brcms_ac_to_fifo(skb_get_queue_mapping(skb)); 680562306a36Sopenharmony_ci dma = wlc->hw->di[fifo]; 680662306a36Sopenharmony_ci txh = (struct d11txh *)(skb->data); 680762306a36Sopenharmony_ci 680862306a36Sopenharmony_ci if (dma->txavail == 0) { 680962306a36Sopenharmony_ci /* 681062306a36Sopenharmony_ci * We sometimes get a frame from mac80211 after stopping 681162306a36Sopenharmony_ci * the queues. This only ever seems to be a single frame 681262306a36Sopenharmony_ci * and is seems likely to be a race. TX_HEADROOM should 681362306a36Sopenharmony_ci * ensure that we have enough space to handle these stray 681462306a36Sopenharmony_ci * packets, so warn if there isn't. If we're out of space 681562306a36Sopenharmony_ci * in the tx ring and the tx queue isn't stopped then 681662306a36Sopenharmony_ci * we've really got a bug; warn loudly if that happens. 681762306a36Sopenharmony_ci */ 681862306a36Sopenharmony_ci brcms_warn(wlc->hw->d11core, 681962306a36Sopenharmony_ci "Received frame for tx with no space in DMA ring\n"); 682062306a36Sopenharmony_ci WARN_ON(!ieee80211_queue_stopped(wlc->pub->ieee_hw, 682162306a36Sopenharmony_ci skb_get_queue_mapping(skb))); 682262306a36Sopenharmony_ci return -ENOSPC; 682362306a36Sopenharmony_ci } 682462306a36Sopenharmony_ci 682562306a36Sopenharmony_ci /* When a BC/MC frame is being committed to the BCMC fifo 682662306a36Sopenharmony_ci * via DMA (NOT PIO), update ucode or BSS info as appropriate. 682762306a36Sopenharmony_ci */ 682862306a36Sopenharmony_ci if (fifo == TX_BCMC_FIFO) 682962306a36Sopenharmony_ci frameid = le16_to_cpu(txh->TxFrameID); 683062306a36Sopenharmony_ci 683162306a36Sopenharmony_ci /* Commit BCMC sequence number in the SHM frame ID location */ 683262306a36Sopenharmony_ci if (frameid != INVALIDFID) { 683362306a36Sopenharmony_ci /* 683462306a36Sopenharmony_ci * To inform the ucode of the last mcast frame posted 683562306a36Sopenharmony_ci * so that it can clear moredata bit 683662306a36Sopenharmony_ci */ 683762306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_BCMC_FID, frameid); 683862306a36Sopenharmony_ci } 683962306a36Sopenharmony_ci 684062306a36Sopenharmony_ci ret = brcms_c_txfifo(wlc, fifo, skb); 684162306a36Sopenharmony_ci /* 684262306a36Sopenharmony_ci * The only reason for brcms_c_txfifo to fail is because 684362306a36Sopenharmony_ci * there weren't any DMA descriptors, but we've already 684462306a36Sopenharmony_ci * checked for that. So if it does fail yell loudly. 684562306a36Sopenharmony_ci */ 684662306a36Sopenharmony_ci WARN_ON_ONCE(ret); 684762306a36Sopenharmony_ci 684862306a36Sopenharmony_ci return ret; 684962306a36Sopenharmony_ci} 685062306a36Sopenharmony_ci 685162306a36Sopenharmony_cibool brcms_c_sendpkt_mac80211(struct brcms_c_info *wlc, struct sk_buff *sdu, 685262306a36Sopenharmony_ci struct ieee80211_hw *hw) 685362306a36Sopenharmony_ci{ 685462306a36Sopenharmony_ci uint fifo; 685562306a36Sopenharmony_ci struct scb *scb = &wlc->pri_scb; 685662306a36Sopenharmony_ci 685762306a36Sopenharmony_ci fifo = brcms_ac_to_fifo(skb_get_queue_mapping(sdu)); 685862306a36Sopenharmony_ci brcms_c_d11hdrs_mac80211(wlc, hw, sdu, scb, 0, 1, fifo, 0); 685962306a36Sopenharmony_ci if (!brcms_c_tx(wlc, sdu)) 686062306a36Sopenharmony_ci return true; 686162306a36Sopenharmony_ci 686262306a36Sopenharmony_ci /* packet discarded */ 686362306a36Sopenharmony_ci dev_kfree_skb_any(sdu); 686462306a36Sopenharmony_ci return false; 686562306a36Sopenharmony_ci} 686662306a36Sopenharmony_ci 686762306a36Sopenharmony_ciint 686862306a36Sopenharmony_cibrcms_c_txfifo(struct brcms_c_info *wlc, uint fifo, struct sk_buff *p) 686962306a36Sopenharmony_ci{ 687062306a36Sopenharmony_ci struct dma_pub *dma = wlc->hw->di[fifo]; 687162306a36Sopenharmony_ci int ret; 687262306a36Sopenharmony_ci u16 queue; 687362306a36Sopenharmony_ci 687462306a36Sopenharmony_ci ret = dma_txfast(wlc, dma, p); 687562306a36Sopenharmony_ci if (ret < 0) 687662306a36Sopenharmony_ci wiphy_err(wlc->wiphy, "txfifo: fatal, toss frames !!!\n"); 687762306a36Sopenharmony_ci 687862306a36Sopenharmony_ci /* 687962306a36Sopenharmony_ci * Stop queue if DMA ring is full. Reserve some free descriptors, 688062306a36Sopenharmony_ci * as we sometimes receive a frame from mac80211 after the queues 688162306a36Sopenharmony_ci * are stopped. 688262306a36Sopenharmony_ci */ 688362306a36Sopenharmony_ci queue = skb_get_queue_mapping(p); 688462306a36Sopenharmony_ci if (dma->txavail <= TX_HEADROOM && fifo < TX_BCMC_FIFO && 688562306a36Sopenharmony_ci !ieee80211_queue_stopped(wlc->pub->ieee_hw, queue)) 688662306a36Sopenharmony_ci ieee80211_stop_queue(wlc->pub->ieee_hw, queue); 688762306a36Sopenharmony_ci 688862306a36Sopenharmony_ci return ret; 688962306a36Sopenharmony_ci} 689062306a36Sopenharmony_ci 689162306a36Sopenharmony_ciu32 689262306a36Sopenharmony_cibrcms_c_rspec_to_rts_rspec(struct brcms_c_info *wlc, u32 rspec, 689362306a36Sopenharmony_ci bool use_rspec, u16 mimo_ctlchbw) 689462306a36Sopenharmony_ci{ 689562306a36Sopenharmony_ci u32 rts_rspec = 0; 689662306a36Sopenharmony_ci 689762306a36Sopenharmony_ci if (use_rspec) 689862306a36Sopenharmony_ci /* use frame rate as rts rate */ 689962306a36Sopenharmony_ci rts_rspec = rspec; 690062306a36Sopenharmony_ci else if (wlc->band->gmode && wlc->protection->_g && !is_cck_rate(rspec)) 690162306a36Sopenharmony_ci /* Use 11Mbps as the g protection RTS target rate and fallback. 690262306a36Sopenharmony_ci * Use the brcms_basic_rate() lookup to find the best basic rate 690362306a36Sopenharmony_ci * under the target in case 11 Mbps is not Basic. 690462306a36Sopenharmony_ci * 6 and 9 Mbps are not usually selected by rate selection, but 690562306a36Sopenharmony_ci * even if the OFDM rate we are protecting is 6 or 9 Mbps, 11 690662306a36Sopenharmony_ci * is more robust. 690762306a36Sopenharmony_ci */ 690862306a36Sopenharmony_ci rts_rspec = brcms_basic_rate(wlc, BRCM_RATE_11M); 690962306a36Sopenharmony_ci else 691062306a36Sopenharmony_ci /* calculate RTS rate and fallback rate based on the frame rate 691162306a36Sopenharmony_ci * RTS must be sent at a basic rate since it is a 691262306a36Sopenharmony_ci * control frame, sec 9.6 of 802.11 spec 691362306a36Sopenharmony_ci */ 691462306a36Sopenharmony_ci rts_rspec = brcms_basic_rate(wlc, rspec); 691562306a36Sopenharmony_ci 691662306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) { 691762306a36Sopenharmony_ci /* set rts txbw to correct side band */ 691862306a36Sopenharmony_ci rts_rspec &= ~RSPEC_BW_MASK; 691962306a36Sopenharmony_ci 692062306a36Sopenharmony_ci /* 692162306a36Sopenharmony_ci * if rspec/rspec_fallback is 40MHz, then send RTS on both 692262306a36Sopenharmony_ci * 20MHz channel (DUP), otherwise send RTS on control channel 692362306a36Sopenharmony_ci */ 692462306a36Sopenharmony_ci if (rspec_is40mhz(rspec) && !is_cck_rate(rts_rspec)) 692562306a36Sopenharmony_ci rts_rspec |= (PHY_TXC1_BW_40MHZ_DUP << RSPEC_BW_SHIFT); 692662306a36Sopenharmony_ci else 692762306a36Sopenharmony_ci rts_rspec |= (mimo_ctlchbw << RSPEC_BW_SHIFT); 692862306a36Sopenharmony_ci 692962306a36Sopenharmony_ci /* pick siso/cdd as default for ofdm */ 693062306a36Sopenharmony_ci if (is_ofdm_rate(rts_rspec)) { 693162306a36Sopenharmony_ci rts_rspec &= ~RSPEC_STF_MASK; 693262306a36Sopenharmony_ci rts_rspec |= (wlc->stf->ss_opmode << RSPEC_STF_SHIFT); 693362306a36Sopenharmony_ci } 693462306a36Sopenharmony_ci } 693562306a36Sopenharmony_ci return rts_rspec; 693662306a36Sopenharmony_ci} 693762306a36Sopenharmony_ci 693862306a36Sopenharmony_ci/* Update beacon listen interval in shared memory */ 693962306a36Sopenharmony_cistatic void brcms_c_bcn_li_upd(struct brcms_c_info *wlc) 694062306a36Sopenharmony_ci{ 694162306a36Sopenharmony_ci /* wake up every DTIM is the default */ 694262306a36Sopenharmony_ci if (wlc->bcn_li_dtim == 1) 694362306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_BCN_LI, 0); 694462306a36Sopenharmony_ci else 694562306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_BCN_LI, 694662306a36Sopenharmony_ci (wlc->bcn_li_dtim << 8) | wlc->bcn_li_bcn); 694762306a36Sopenharmony_ci} 694862306a36Sopenharmony_ci 694962306a36Sopenharmony_cistatic void 695062306a36Sopenharmony_cibrcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr, 695162306a36Sopenharmony_ci u32 *tsf_h_ptr) 695262306a36Sopenharmony_ci{ 695362306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 695462306a36Sopenharmony_ci 695562306a36Sopenharmony_ci /* read the tsf timer low, then high to get an atomic read */ 695662306a36Sopenharmony_ci *tsf_l_ptr = bcma_read32(core, D11REGOFFS(tsf_timerlow)); 695762306a36Sopenharmony_ci *tsf_h_ptr = bcma_read32(core, D11REGOFFS(tsf_timerhigh)); 695862306a36Sopenharmony_ci} 695962306a36Sopenharmony_ci 696062306a36Sopenharmony_ci/* 696162306a36Sopenharmony_ci * recover 64bit TSF value from the 16bit TSF value in the rx header 696262306a36Sopenharmony_ci * given the assumption that the TSF passed in header is within 65ms 696362306a36Sopenharmony_ci * of the current tsf. 696462306a36Sopenharmony_ci * 696562306a36Sopenharmony_ci * 6 5 4 4 3 2 1 696662306a36Sopenharmony_ci * 3.......6.......8.......0.......2.......4.......6.......8......0 696762306a36Sopenharmony_ci * |<---------- tsf_h ----------->||<--- tsf_l -->||<-RxTSFTime ->| 696862306a36Sopenharmony_ci * 696962306a36Sopenharmony_ci * The RxTSFTime are the lowest 16 bits and provided by the ucode. The 697062306a36Sopenharmony_ci * tsf_l is filled in by brcms_b_recv, which is done earlier in the 697162306a36Sopenharmony_ci * receive call sequence after rx interrupt. Only the higher 16 bits 697262306a36Sopenharmony_ci * are used. Finally, the tsf_h is read from the tsf register. 697362306a36Sopenharmony_ci */ 697462306a36Sopenharmony_cistatic u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc, 697562306a36Sopenharmony_ci struct d11rxhdr *rxh) 697662306a36Sopenharmony_ci{ 697762306a36Sopenharmony_ci u32 tsf_h, tsf_l; 697862306a36Sopenharmony_ci u16 rx_tsf_0_15, rx_tsf_16_31; 697962306a36Sopenharmony_ci 698062306a36Sopenharmony_ci brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); 698162306a36Sopenharmony_ci 698262306a36Sopenharmony_ci rx_tsf_16_31 = (u16)(tsf_l >> 16); 698362306a36Sopenharmony_ci rx_tsf_0_15 = rxh->RxTSFTime; 698462306a36Sopenharmony_ci 698562306a36Sopenharmony_ci /* 698662306a36Sopenharmony_ci * a greater tsf time indicates the low 16 bits of 698762306a36Sopenharmony_ci * tsf_l wrapped, so decrement the high 16 bits. 698862306a36Sopenharmony_ci */ 698962306a36Sopenharmony_ci if ((u16)tsf_l < rx_tsf_0_15) { 699062306a36Sopenharmony_ci rx_tsf_16_31 -= 1; 699162306a36Sopenharmony_ci if (rx_tsf_16_31 == 0xffff) 699262306a36Sopenharmony_ci tsf_h -= 1; 699362306a36Sopenharmony_ci } 699462306a36Sopenharmony_ci 699562306a36Sopenharmony_ci return ((u64)tsf_h << 32) | (((u32)rx_tsf_16_31 << 16) + rx_tsf_0_15); 699662306a36Sopenharmony_ci} 699762306a36Sopenharmony_ci 699862306a36Sopenharmony_cistatic void 699962306a36Sopenharmony_ciprep_mac80211_status(struct brcms_c_info *wlc, struct d11rxhdr *rxh, 700062306a36Sopenharmony_ci struct sk_buff *p, 700162306a36Sopenharmony_ci struct ieee80211_rx_status *rx_status) 700262306a36Sopenharmony_ci{ 700362306a36Sopenharmony_ci int channel; 700462306a36Sopenharmony_ci u32 rspec; 700562306a36Sopenharmony_ci unsigned char *plcp; 700662306a36Sopenharmony_ci 700762306a36Sopenharmony_ci /* fill in TSF and flag its presence */ 700862306a36Sopenharmony_ci rx_status->mactime = brcms_c_recover_tsf64(wlc, rxh); 700962306a36Sopenharmony_ci rx_status->flag |= RX_FLAG_MACTIME_START; 701062306a36Sopenharmony_ci 701162306a36Sopenharmony_ci channel = BRCMS_CHAN_CHANNEL(rxh->RxChan); 701262306a36Sopenharmony_ci 701362306a36Sopenharmony_ci rx_status->band = 701462306a36Sopenharmony_ci channel > 14 ? NL80211_BAND_5GHZ : NL80211_BAND_2GHZ; 701562306a36Sopenharmony_ci rx_status->freq = 701662306a36Sopenharmony_ci ieee80211_channel_to_frequency(channel, rx_status->band); 701762306a36Sopenharmony_ci 701862306a36Sopenharmony_ci rx_status->signal = wlc_phy_rssi_compute(wlc->hw->band->pi, rxh); 701962306a36Sopenharmony_ci 702062306a36Sopenharmony_ci /* noise */ 702162306a36Sopenharmony_ci /* qual */ 702262306a36Sopenharmony_ci rx_status->antenna = 702362306a36Sopenharmony_ci (rxh->PhyRxStatus_0 & PRXS0_RXANT_UPSUBBAND) ? 1 : 0; 702462306a36Sopenharmony_ci 702562306a36Sopenharmony_ci plcp = p->data; 702662306a36Sopenharmony_ci 702762306a36Sopenharmony_ci rspec = brcms_c_compute_rspec(rxh, plcp); 702862306a36Sopenharmony_ci if (is_mcs_rate(rspec)) { 702962306a36Sopenharmony_ci rx_status->rate_idx = rspec & RSPEC_RATE_MASK; 703062306a36Sopenharmony_ci rx_status->encoding = RX_ENC_HT; 703162306a36Sopenharmony_ci if (rspec_is40mhz(rspec)) 703262306a36Sopenharmony_ci rx_status->bw = RATE_INFO_BW_40; 703362306a36Sopenharmony_ci } else { 703462306a36Sopenharmony_ci switch (rspec2rate(rspec)) { 703562306a36Sopenharmony_ci case BRCM_RATE_1M: 703662306a36Sopenharmony_ci rx_status->rate_idx = 0; 703762306a36Sopenharmony_ci break; 703862306a36Sopenharmony_ci case BRCM_RATE_2M: 703962306a36Sopenharmony_ci rx_status->rate_idx = 1; 704062306a36Sopenharmony_ci break; 704162306a36Sopenharmony_ci case BRCM_RATE_5M5: 704262306a36Sopenharmony_ci rx_status->rate_idx = 2; 704362306a36Sopenharmony_ci break; 704462306a36Sopenharmony_ci case BRCM_RATE_11M: 704562306a36Sopenharmony_ci rx_status->rate_idx = 3; 704662306a36Sopenharmony_ci break; 704762306a36Sopenharmony_ci case BRCM_RATE_6M: 704862306a36Sopenharmony_ci rx_status->rate_idx = 4; 704962306a36Sopenharmony_ci break; 705062306a36Sopenharmony_ci case BRCM_RATE_9M: 705162306a36Sopenharmony_ci rx_status->rate_idx = 5; 705262306a36Sopenharmony_ci break; 705362306a36Sopenharmony_ci case BRCM_RATE_12M: 705462306a36Sopenharmony_ci rx_status->rate_idx = 6; 705562306a36Sopenharmony_ci break; 705662306a36Sopenharmony_ci case BRCM_RATE_18M: 705762306a36Sopenharmony_ci rx_status->rate_idx = 7; 705862306a36Sopenharmony_ci break; 705962306a36Sopenharmony_ci case BRCM_RATE_24M: 706062306a36Sopenharmony_ci rx_status->rate_idx = 8; 706162306a36Sopenharmony_ci break; 706262306a36Sopenharmony_ci case BRCM_RATE_36M: 706362306a36Sopenharmony_ci rx_status->rate_idx = 9; 706462306a36Sopenharmony_ci break; 706562306a36Sopenharmony_ci case BRCM_RATE_48M: 706662306a36Sopenharmony_ci rx_status->rate_idx = 10; 706762306a36Sopenharmony_ci break; 706862306a36Sopenharmony_ci case BRCM_RATE_54M: 706962306a36Sopenharmony_ci rx_status->rate_idx = 11; 707062306a36Sopenharmony_ci break; 707162306a36Sopenharmony_ci default: 707262306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 707362306a36Sopenharmony_ci "%s: Unknown rate\n", __func__); 707462306a36Sopenharmony_ci } 707562306a36Sopenharmony_ci 707662306a36Sopenharmony_ci /* 707762306a36Sopenharmony_ci * For 5GHz, we should decrease the index as it is 707862306a36Sopenharmony_ci * a subset of the 2.4G rates. See bitrates field 707962306a36Sopenharmony_ci * of brcms_band_5GHz_nphy (in mac80211_if.c). 708062306a36Sopenharmony_ci */ 708162306a36Sopenharmony_ci if (rx_status->band == NL80211_BAND_5GHZ) 708262306a36Sopenharmony_ci rx_status->rate_idx -= BRCMS_LEGACY_5G_RATE_OFFSET; 708362306a36Sopenharmony_ci 708462306a36Sopenharmony_ci /* Determine short preamble and rate_idx */ 708562306a36Sopenharmony_ci if (is_cck_rate(rspec)) { 708662306a36Sopenharmony_ci if (rxh->PhyRxStatus_0 & PRXS0_SHORTH) 708762306a36Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; 708862306a36Sopenharmony_ci } else if (is_ofdm_rate(rspec)) { 708962306a36Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_SHORTPRE; 709062306a36Sopenharmony_ci } else { 709162306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "%s: Unknown modulation\n", 709262306a36Sopenharmony_ci __func__); 709362306a36Sopenharmony_ci } 709462306a36Sopenharmony_ci } 709562306a36Sopenharmony_ci 709662306a36Sopenharmony_ci if (plcp3_issgi(plcp[3])) 709762306a36Sopenharmony_ci rx_status->enc_flags |= RX_ENC_FLAG_SHORT_GI; 709862306a36Sopenharmony_ci 709962306a36Sopenharmony_ci if (rxh->RxStatus1 & RXS_DECERR) { 710062306a36Sopenharmony_ci rx_status->flag |= RX_FLAG_FAILED_PLCP_CRC; 710162306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_PLCP_CRC\n", 710262306a36Sopenharmony_ci __func__); 710362306a36Sopenharmony_ci } 710462306a36Sopenharmony_ci if (rxh->RxStatus1 & RXS_FCSERR) { 710562306a36Sopenharmony_ci rx_status->flag |= RX_FLAG_FAILED_FCS_CRC; 710662306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, "%s: RX_FLAG_FAILED_FCS_CRC\n", 710762306a36Sopenharmony_ci __func__); 710862306a36Sopenharmony_ci } 710962306a36Sopenharmony_ci} 711062306a36Sopenharmony_ci 711162306a36Sopenharmony_cistatic void 711262306a36Sopenharmony_cibrcms_c_recvctl(struct brcms_c_info *wlc, struct d11rxhdr *rxh, 711362306a36Sopenharmony_ci struct sk_buff *p) 711462306a36Sopenharmony_ci{ 711562306a36Sopenharmony_ci int len_mpdu; 711662306a36Sopenharmony_ci struct ieee80211_rx_status rx_status; 711762306a36Sopenharmony_ci struct ieee80211_hdr *hdr; 711862306a36Sopenharmony_ci 711962306a36Sopenharmony_ci memset(&rx_status, 0, sizeof(rx_status)); 712062306a36Sopenharmony_ci prep_mac80211_status(wlc, rxh, p, &rx_status); 712162306a36Sopenharmony_ci 712262306a36Sopenharmony_ci /* mac header+body length, exclude CRC and plcp header */ 712362306a36Sopenharmony_ci len_mpdu = p->len - D11_PHY_HDR_LEN - FCS_LEN; 712462306a36Sopenharmony_ci skb_pull(p, D11_PHY_HDR_LEN); 712562306a36Sopenharmony_ci __skb_trim(p, len_mpdu); 712662306a36Sopenharmony_ci 712762306a36Sopenharmony_ci /* unmute transmit */ 712862306a36Sopenharmony_ci if (wlc->hw->suspended_fifos) { 712962306a36Sopenharmony_ci hdr = (struct ieee80211_hdr *)p->data; 713062306a36Sopenharmony_ci if (ieee80211_is_beacon(hdr->frame_control)) 713162306a36Sopenharmony_ci brcms_b_mute(wlc->hw, false); 713262306a36Sopenharmony_ci } 713362306a36Sopenharmony_ci 713462306a36Sopenharmony_ci memcpy(IEEE80211_SKB_RXCB(p), &rx_status, sizeof(rx_status)); 713562306a36Sopenharmony_ci ieee80211_rx_irqsafe(wlc->pub->ieee_hw, p); 713662306a36Sopenharmony_ci} 713762306a36Sopenharmony_ci 713862306a36Sopenharmony_ci/* calculate frame duration for Mixed-mode L-SIG spoofing, return 713962306a36Sopenharmony_ci * number of bytes goes in the length field 714062306a36Sopenharmony_ci * 714162306a36Sopenharmony_ci * Formula given by HT PHY Spec v 1.13 714262306a36Sopenharmony_ci * len = 3(nsyms + nstream + 3) - 3 714362306a36Sopenharmony_ci */ 714462306a36Sopenharmony_ciu16 714562306a36Sopenharmony_cibrcms_c_calc_lsig_len(struct brcms_c_info *wlc, u32 ratespec, 714662306a36Sopenharmony_ci uint mac_len) 714762306a36Sopenharmony_ci{ 714862306a36Sopenharmony_ci uint nsyms, len = 0, kNdps; 714962306a36Sopenharmony_ci 715062306a36Sopenharmony_ci if (is_mcs_rate(ratespec)) { 715162306a36Sopenharmony_ci uint mcs = ratespec & RSPEC_RATE_MASK; 715262306a36Sopenharmony_ci int tot_streams = (mcs_2_txstreams(mcs) + 1) + 715362306a36Sopenharmony_ci rspec_stc(ratespec); 715462306a36Sopenharmony_ci 715562306a36Sopenharmony_ci /* 715662306a36Sopenharmony_ci * the payload duration calculation matches that 715762306a36Sopenharmony_ci * of regular ofdm 715862306a36Sopenharmony_ci */ 715962306a36Sopenharmony_ci /* 1000Ndbps = kbps * 4 */ 716062306a36Sopenharmony_ci kNdps = mcs_2_rate(mcs, rspec_is40mhz(ratespec), 716162306a36Sopenharmony_ci rspec_issgi(ratespec)) * 4; 716262306a36Sopenharmony_ci 716362306a36Sopenharmony_ci if (rspec_stc(ratespec) == 0) 716462306a36Sopenharmony_ci nsyms = 716562306a36Sopenharmony_ci CEIL((APHY_SERVICE_NBITS + 8 * mac_len + 716662306a36Sopenharmony_ci APHY_TAIL_NBITS) * 1000, kNdps); 716762306a36Sopenharmony_ci else 716862306a36Sopenharmony_ci /* STBC needs to have even number of symbols */ 716962306a36Sopenharmony_ci nsyms = 717062306a36Sopenharmony_ci 2 * 717162306a36Sopenharmony_ci CEIL((APHY_SERVICE_NBITS + 8 * mac_len + 717262306a36Sopenharmony_ci APHY_TAIL_NBITS) * 1000, 2 * kNdps); 717362306a36Sopenharmony_ci 717462306a36Sopenharmony_ci /* (+3) account for HT-SIG(2) and HT-STF(1) */ 717562306a36Sopenharmony_ci nsyms += (tot_streams + 3); 717662306a36Sopenharmony_ci /* 717762306a36Sopenharmony_ci * 3 bytes/symbol @ legacy 6Mbps rate 717862306a36Sopenharmony_ci * (-3) excluding service bits and tail bits 717962306a36Sopenharmony_ci */ 718062306a36Sopenharmony_ci len = (3 * nsyms) - 3; 718162306a36Sopenharmony_ci } 718262306a36Sopenharmony_ci 718362306a36Sopenharmony_ci return (u16) len; 718462306a36Sopenharmony_ci} 718562306a36Sopenharmony_ci 718662306a36Sopenharmony_cistatic void 718762306a36Sopenharmony_cibrcms_c_mod_prb_rsp_rate_table(struct brcms_c_info *wlc, uint frame_len) 718862306a36Sopenharmony_ci{ 718962306a36Sopenharmony_ci const struct brcms_c_rateset *rs_dflt; 719062306a36Sopenharmony_ci struct brcms_c_rateset rs; 719162306a36Sopenharmony_ci u8 rate; 719262306a36Sopenharmony_ci u16 entry_ptr; 719362306a36Sopenharmony_ci u8 plcp[D11_PHY_HDR_LEN]; 719462306a36Sopenharmony_ci u16 dur, sifs; 719562306a36Sopenharmony_ci uint i; 719662306a36Sopenharmony_ci 719762306a36Sopenharmony_ci sifs = get_sifs(wlc->band); 719862306a36Sopenharmony_ci 719962306a36Sopenharmony_ci rs_dflt = brcms_c_rateset_get_hwrs(wlc); 720062306a36Sopenharmony_ci 720162306a36Sopenharmony_ci brcms_c_rateset_copy(rs_dflt, &rs); 720262306a36Sopenharmony_ci brcms_c_rateset_mcs_upd(&rs, wlc->stf->txstreams); 720362306a36Sopenharmony_ci 720462306a36Sopenharmony_ci /* 720562306a36Sopenharmony_ci * walk the phy rate table and update MAC core SHM 720662306a36Sopenharmony_ci * basic rate table entries 720762306a36Sopenharmony_ci */ 720862306a36Sopenharmony_ci for (i = 0; i < rs.count; i++) { 720962306a36Sopenharmony_ci rate = rs.rates[i] & BRCMS_RATE_MASK; 721062306a36Sopenharmony_ci 721162306a36Sopenharmony_ci entry_ptr = brcms_b_rate_shm_offset(wlc->hw, rate); 721262306a36Sopenharmony_ci 721362306a36Sopenharmony_ci /* Calculate the Probe Response PLCP for the given rate */ 721462306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, rate, frame_len, plcp); 721562306a36Sopenharmony_ci 721662306a36Sopenharmony_ci /* 721762306a36Sopenharmony_ci * Calculate the duration of the Probe Response 721862306a36Sopenharmony_ci * frame plus SIFS for the MAC 721962306a36Sopenharmony_ci */ 722062306a36Sopenharmony_ci dur = (u16) brcms_c_calc_frame_time(wlc, rate, 722162306a36Sopenharmony_ci BRCMS_LONG_PREAMBLE, frame_len); 722262306a36Sopenharmony_ci dur += sifs; 722362306a36Sopenharmony_ci 722462306a36Sopenharmony_ci /* Update the SHM Rate Table entry Probe Response values */ 722562306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS, 722662306a36Sopenharmony_ci (u16) (plcp[0] + (plcp[1] << 8))); 722762306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_PLCP_POS + 2, 722862306a36Sopenharmony_ci (u16) (plcp[2] + (plcp[3] << 8))); 722962306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, entry_ptr + M_RT_PRS_DUR_POS, dur); 723062306a36Sopenharmony_ci } 723162306a36Sopenharmony_ci} 723262306a36Sopenharmony_ci 723362306a36Sopenharmony_ciint brcms_c_get_header_len(void) 723462306a36Sopenharmony_ci{ 723562306a36Sopenharmony_ci return TXOFF; 723662306a36Sopenharmony_ci} 723762306a36Sopenharmony_ci 723862306a36Sopenharmony_cistatic void brcms_c_beacon_write(struct brcms_c_info *wlc, 723962306a36Sopenharmony_ci struct sk_buff *beacon, u16 tim_offset, 724062306a36Sopenharmony_ci u16 dtim_period, bool bcn0, bool bcn1) 724162306a36Sopenharmony_ci{ 724262306a36Sopenharmony_ci size_t len; 724362306a36Sopenharmony_ci struct ieee80211_tx_info *tx_info; 724462306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 724562306a36Sopenharmony_ci struct ieee80211_hw *ieee_hw = brcms_c_pub(wlc)->ieee_hw; 724662306a36Sopenharmony_ci 724762306a36Sopenharmony_ci /* Get tx_info */ 724862306a36Sopenharmony_ci tx_info = IEEE80211_SKB_CB(beacon); 724962306a36Sopenharmony_ci 725062306a36Sopenharmony_ci len = min_t(size_t, beacon->len, BCN_TMPL_LEN); 725162306a36Sopenharmony_ci wlc->bcn_rspec = ieee80211_get_tx_rate(ieee_hw, tx_info)->hw_value; 725262306a36Sopenharmony_ci 725362306a36Sopenharmony_ci brcms_c_compute_plcp(wlc, wlc->bcn_rspec, 725462306a36Sopenharmony_ci len + FCS_LEN - D11_PHY_HDR_LEN, beacon->data); 725562306a36Sopenharmony_ci 725662306a36Sopenharmony_ci /* "Regular" and 16 MBSS but not for 4 MBSS */ 725762306a36Sopenharmony_ci /* Update the phytxctl for the beacon based on the rspec */ 725862306a36Sopenharmony_ci brcms_c_beacon_phytxctl_txant_upd(wlc, wlc->bcn_rspec); 725962306a36Sopenharmony_ci 726062306a36Sopenharmony_ci if (bcn0) { 726162306a36Sopenharmony_ci /* write the probe response into the template region */ 726262306a36Sopenharmony_ci brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE, 726362306a36Sopenharmony_ci (len + 3) & ~3, beacon->data); 726462306a36Sopenharmony_ci 726562306a36Sopenharmony_ci /* write beacon length to SCR */ 726662306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len); 726762306a36Sopenharmony_ci } 726862306a36Sopenharmony_ci if (bcn1) { 726962306a36Sopenharmony_ci /* write the probe response into the template region */ 727062306a36Sopenharmony_ci brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE, 727162306a36Sopenharmony_ci (len + 3) & ~3, beacon->data); 727262306a36Sopenharmony_ci 727362306a36Sopenharmony_ci /* write beacon length to SCR */ 727462306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len); 727562306a36Sopenharmony_ci } 727662306a36Sopenharmony_ci 727762306a36Sopenharmony_ci if (tim_offset != 0) { 727862306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, 727962306a36Sopenharmony_ci tim_offset + D11B_PHY_HDR_LEN); 728062306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, dtim_period); 728162306a36Sopenharmony_ci } else { 728262306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_TIMBPOS_INBEACON, 728362306a36Sopenharmony_ci len + D11B_PHY_HDR_LEN); 728462306a36Sopenharmony_ci brcms_b_write_shm(wlc_hw, M_DOT11_DTIMPERIOD, 0); 728562306a36Sopenharmony_ci } 728662306a36Sopenharmony_ci} 728762306a36Sopenharmony_ci 728862306a36Sopenharmony_cistatic void brcms_c_update_beacon_hw(struct brcms_c_info *wlc, 728962306a36Sopenharmony_ci struct sk_buff *beacon, u16 tim_offset, 729062306a36Sopenharmony_ci u16 dtim_period) 729162306a36Sopenharmony_ci{ 729262306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 729362306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 729462306a36Sopenharmony_ci 729562306a36Sopenharmony_ci /* Hardware beaconing for this config */ 729662306a36Sopenharmony_ci u32 both_valid = MCMD_BCN0VLD | MCMD_BCN1VLD; 729762306a36Sopenharmony_ci 729862306a36Sopenharmony_ci /* Check if both templates are in use, if so sched. an interrupt 729962306a36Sopenharmony_ci * that will call back into this routine 730062306a36Sopenharmony_ci */ 730162306a36Sopenharmony_ci if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) 730262306a36Sopenharmony_ci /* clear any previous status */ 730362306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(macintstatus), MI_BCNTPL); 730462306a36Sopenharmony_ci 730562306a36Sopenharmony_ci if (wlc->beacon_template_virgin) { 730662306a36Sopenharmony_ci wlc->beacon_template_virgin = false; 730762306a36Sopenharmony_ci brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, 730862306a36Sopenharmony_ci true); 730962306a36Sopenharmony_ci /* mark beacon0 valid */ 731062306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); 731162306a36Sopenharmony_ci return; 731262306a36Sopenharmony_ci } 731362306a36Sopenharmony_ci 731462306a36Sopenharmony_ci /* Check that after scheduling the interrupt both of the 731562306a36Sopenharmony_ci * templates are still busy. if not clear the int. & remask 731662306a36Sopenharmony_ci */ 731762306a36Sopenharmony_ci if ((bcma_read32(core, D11REGOFFS(maccommand)) & both_valid) == both_valid) { 731862306a36Sopenharmony_ci wlc->defmacintmask |= MI_BCNTPL; 731962306a36Sopenharmony_ci return; 732062306a36Sopenharmony_ci } 732162306a36Sopenharmony_ci 732262306a36Sopenharmony_ci if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN0VLD)) { 732362306a36Sopenharmony_ci brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, true, 732462306a36Sopenharmony_ci false); 732562306a36Sopenharmony_ci /* mark beacon0 valid */ 732662306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN0VLD); 732762306a36Sopenharmony_ci return; 732862306a36Sopenharmony_ci } 732962306a36Sopenharmony_ci if (!(bcma_read32(core, D11REGOFFS(maccommand)) & MCMD_BCN1VLD)) { 733062306a36Sopenharmony_ci brcms_c_beacon_write(wlc, beacon, tim_offset, dtim_period, 733162306a36Sopenharmony_ci false, true); 733262306a36Sopenharmony_ci /* mark beacon0 valid */ 733362306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(maccommand), MCMD_BCN1VLD); 733462306a36Sopenharmony_ci } 733562306a36Sopenharmony_ci} 733662306a36Sopenharmony_ci 733762306a36Sopenharmony_ci/* 733862306a36Sopenharmony_ci * Update all beacons for the system. 733962306a36Sopenharmony_ci */ 734062306a36Sopenharmony_civoid brcms_c_update_beacon(struct brcms_c_info *wlc) 734162306a36Sopenharmony_ci{ 734262306a36Sopenharmony_ci struct brcms_bss_cfg *bsscfg = wlc->bsscfg; 734362306a36Sopenharmony_ci 734462306a36Sopenharmony_ci if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || 734562306a36Sopenharmony_ci bsscfg->type == BRCMS_TYPE_ADHOC)) { 734662306a36Sopenharmony_ci /* Clear the soft intmask */ 734762306a36Sopenharmony_ci wlc->defmacintmask &= ~MI_BCNTPL; 734862306a36Sopenharmony_ci if (!wlc->beacon) 734962306a36Sopenharmony_ci return; 735062306a36Sopenharmony_ci brcms_c_update_beacon_hw(wlc, wlc->beacon, 735162306a36Sopenharmony_ci wlc->beacon_tim_offset, 735262306a36Sopenharmony_ci wlc->beacon_dtim_period); 735362306a36Sopenharmony_ci } 735462306a36Sopenharmony_ci} 735562306a36Sopenharmony_ci 735662306a36Sopenharmony_civoid brcms_c_set_new_beacon(struct brcms_c_info *wlc, struct sk_buff *beacon, 735762306a36Sopenharmony_ci u16 tim_offset, u16 dtim_period) 735862306a36Sopenharmony_ci{ 735962306a36Sopenharmony_ci if (!beacon) 736062306a36Sopenharmony_ci return; 736162306a36Sopenharmony_ci if (wlc->beacon) 736262306a36Sopenharmony_ci dev_kfree_skb_any(wlc->beacon); 736362306a36Sopenharmony_ci wlc->beacon = beacon; 736462306a36Sopenharmony_ci 736562306a36Sopenharmony_ci /* add PLCP */ 736662306a36Sopenharmony_ci skb_push(wlc->beacon, D11_PHY_HDR_LEN); 736762306a36Sopenharmony_ci wlc->beacon_tim_offset = tim_offset; 736862306a36Sopenharmony_ci wlc->beacon_dtim_period = dtim_period; 736962306a36Sopenharmony_ci brcms_c_update_beacon(wlc); 737062306a36Sopenharmony_ci} 737162306a36Sopenharmony_ci 737262306a36Sopenharmony_civoid brcms_c_set_new_probe_resp(struct brcms_c_info *wlc, 737362306a36Sopenharmony_ci struct sk_buff *probe_resp) 737462306a36Sopenharmony_ci{ 737562306a36Sopenharmony_ci if (!probe_resp) 737662306a36Sopenharmony_ci return; 737762306a36Sopenharmony_ci if (wlc->probe_resp) 737862306a36Sopenharmony_ci dev_kfree_skb_any(wlc->probe_resp); 737962306a36Sopenharmony_ci wlc->probe_resp = probe_resp; 738062306a36Sopenharmony_ci 738162306a36Sopenharmony_ci /* add PLCP */ 738262306a36Sopenharmony_ci skb_push(wlc->probe_resp, D11_PHY_HDR_LEN); 738362306a36Sopenharmony_ci brcms_c_update_probe_resp(wlc, false); 738462306a36Sopenharmony_ci} 738562306a36Sopenharmony_ci 738662306a36Sopenharmony_civoid brcms_c_enable_probe_resp(struct brcms_c_info *wlc, bool enable) 738762306a36Sopenharmony_ci{ 738862306a36Sopenharmony_ci /* 738962306a36Sopenharmony_ci * prevent ucode from sending probe responses by setting the timeout 739062306a36Sopenharmony_ci * to 1, it can not send it in that time frame. 739162306a36Sopenharmony_ci */ 739262306a36Sopenharmony_ci wlc->prb_resp_timeout = enable ? BRCMS_PRB_RESP_TIMEOUT : 1; 739362306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); 739462306a36Sopenharmony_ci /* TODO: if (enable) => also deactivate receiving of probe request */ 739562306a36Sopenharmony_ci} 739662306a36Sopenharmony_ci 739762306a36Sopenharmony_ci/* Write ssid into shared memory */ 739862306a36Sopenharmony_cistatic void 739962306a36Sopenharmony_cibrcms_c_shm_ssid_upd(struct brcms_c_info *wlc, struct brcms_bss_cfg *cfg) 740062306a36Sopenharmony_ci{ 740162306a36Sopenharmony_ci u8 *ssidptr = cfg->SSID; 740262306a36Sopenharmony_ci u16 base = M_SSID; 740362306a36Sopenharmony_ci u8 ssidbuf[IEEE80211_MAX_SSID_LEN]; 740462306a36Sopenharmony_ci 740562306a36Sopenharmony_ci /* padding the ssid with zero and copy it into shm */ 740662306a36Sopenharmony_ci memset(ssidbuf, 0, IEEE80211_MAX_SSID_LEN); 740762306a36Sopenharmony_ci memcpy(ssidbuf, ssidptr, cfg->SSID_len); 740862306a36Sopenharmony_ci 740962306a36Sopenharmony_ci brcms_c_copyto_shm(wlc, base, ssidbuf, IEEE80211_MAX_SSID_LEN); 741062306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_SSIDLEN, (u16) cfg->SSID_len); 741162306a36Sopenharmony_ci} 741262306a36Sopenharmony_ci 741362306a36Sopenharmony_cistatic void 741462306a36Sopenharmony_cibrcms_c_bss_update_probe_resp(struct brcms_c_info *wlc, 741562306a36Sopenharmony_ci struct brcms_bss_cfg *cfg, 741662306a36Sopenharmony_ci struct sk_buff *probe_resp, 741762306a36Sopenharmony_ci bool suspend) 741862306a36Sopenharmony_ci{ 741962306a36Sopenharmony_ci int len; 742062306a36Sopenharmony_ci 742162306a36Sopenharmony_ci len = min_t(size_t, probe_resp->len, BCN_TMPL_LEN); 742262306a36Sopenharmony_ci 742362306a36Sopenharmony_ci if (suspend) 742462306a36Sopenharmony_ci brcms_c_suspend_mac_and_wait(wlc); 742562306a36Sopenharmony_ci 742662306a36Sopenharmony_ci /* write the probe response into the template region */ 742762306a36Sopenharmony_ci brcms_b_write_template_ram(wlc->hw, T_PRS_TPL_BASE, 742862306a36Sopenharmony_ci (len + 3) & ~3, probe_resp->data); 742962306a36Sopenharmony_ci 743062306a36Sopenharmony_ci /* write the length of the probe response frame (+PLCP/-FCS) */ 743162306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_PRB_RESP_FRM_LEN, (u16) len); 743262306a36Sopenharmony_ci 743362306a36Sopenharmony_ci /* write the SSID and SSID length */ 743462306a36Sopenharmony_ci brcms_c_shm_ssid_upd(wlc, cfg); 743562306a36Sopenharmony_ci 743662306a36Sopenharmony_ci /* 743762306a36Sopenharmony_ci * Write PLCP headers and durations for probe response frames 743862306a36Sopenharmony_ci * at all rates. Use the actual frame length covered by the 743962306a36Sopenharmony_ci * PLCP header for the call to brcms_c_mod_prb_rsp_rate_table() 744062306a36Sopenharmony_ci * by subtracting the PLCP len and adding the FCS. 744162306a36Sopenharmony_ci */ 744262306a36Sopenharmony_ci brcms_c_mod_prb_rsp_rate_table(wlc, 744362306a36Sopenharmony_ci (u16)len + FCS_LEN - D11_PHY_HDR_LEN); 744462306a36Sopenharmony_ci 744562306a36Sopenharmony_ci if (suspend) 744662306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 744762306a36Sopenharmony_ci} 744862306a36Sopenharmony_ci 744962306a36Sopenharmony_civoid brcms_c_update_probe_resp(struct brcms_c_info *wlc, bool suspend) 745062306a36Sopenharmony_ci{ 745162306a36Sopenharmony_ci struct brcms_bss_cfg *bsscfg = wlc->bsscfg; 745262306a36Sopenharmony_ci 745362306a36Sopenharmony_ci /* update AP or IBSS probe responses */ 745462306a36Sopenharmony_ci if (wlc->pub->up && (bsscfg->type == BRCMS_TYPE_AP || 745562306a36Sopenharmony_ci bsscfg->type == BRCMS_TYPE_ADHOC)) { 745662306a36Sopenharmony_ci if (!wlc->probe_resp) 745762306a36Sopenharmony_ci return; 745862306a36Sopenharmony_ci brcms_c_bss_update_probe_resp(wlc, bsscfg, wlc->probe_resp, 745962306a36Sopenharmony_ci suspend); 746062306a36Sopenharmony_ci } 746162306a36Sopenharmony_ci} 746262306a36Sopenharmony_ci 746362306a36Sopenharmony_ciint brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo, 746462306a36Sopenharmony_ci uint *blocks) 746562306a36Sopenharmony_ci{ 746662306a36Sopenharmony_ci if (fifo >= NFIFO) 746762306a36Sopenharmony_ci return -EINVAL; 746862306a36Sopenharmony_ci 746962306a36Sopenharmony_ci *blocks = wlc_hw->xmtfifo_sz[fifo]; 747062306a36Sopenharmony_ci 747162306a36Sopenharmony_ci return 0; 747262306a36Sopenharmony_ci} 747362306a36Sopenharmony_ci 747462306a36Sopenharmony_civoid 747562306a36Sopenharmony_cibrcms_c_set_addrmatch(struct brcms_c_info *wlc, int match_reg_offset, 747662306a36Sopenharmony_ci const u8 *addr) 747762306a36Sopenharmony_ci{ 747862306a36Sopenharmony_ci brcms_b_set_addrmatch(wlc->hw, match_reg_offset, addr); 747962306a36Sopenharmony_ci if (match_reg_offset == RCM_BSSID_OFFSET) 748062306a36Sopenharmony_ci memcpy(wlc->bsscfg->BSSID, addr, ETH_ALEN); 748162306a36Sopenharmony_ci} 748262306a36Sopenharmony_ci 748362306a36Sopenharmony_ci/* 748462306a36Sopenharmony_ci * Flag 'scan in progress' to withhold dynamic phy calibration 748562306a36Sopenharmony_ci */ 748662306a36Sopenharmony_civoid brcms_c_scan_start(struct brcms_c_info *wlc) 748762306a36Sopenharmony_ci{ 748862306a36Sopenharmony_ci wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, true); 748962306a36Sopenharmony_ci} 749062306a36Sopenharmony_ci 749162306a36Sopenharmony_civoid brcms_c_scan_stop(struct brcms_c_info *wlc) 749262306a36Sopenharmony_ci{ 749362306a36Sopenharmony_ci wlc_phy_hold_upd(wlc->band->pi, PHY_HOLD_FOR_SCAN, false); 749462306a36Sopenharmony_ci} 749562306a36Sopenharmony_ci 749662306a36Sopenharmony_civoid brcms_c_associate_upd(struct brcms_c_info *wlc, bool state) 749762306a36Sopenharmony_ci{ 749862306a36Sopenharmony_ci wlc->pub->associated = state; 749962306a36Sopenharmony_ci} 750062306a36Sopenharmony_ci 750162306a36Sopenharmony_ci/* 750262306a36Sopenharmony_ci * When a remote STA/AP is removed by Mac80211, or when it can no longer accept 750362306a36Sopenharmony_ci * AMPDU traffic, packets pending in hardware have to be invalidated so that 750462306a36Sopenharmony_ci * when later on hardware releases them, they can be handled appropriately. 750562306a36Sopenharmony_ci */ 750662306a36Sopenharmony_civoid brcms_c_inval_dma_pkts(struct brcms_hardware *hw, 750762306a36Sopenharmony_ci struct ieee80211_sta *sta, 750862306a36Sopenharmony_ci void (*dma_callback_fn)) 750962306a36Sopenharmony_ci{ 751062306a36Sopenharmony_ci struct dma_pub *dmah; 751162306a36Sopenharmony_ci int i; 751262306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) { 751362306a36Sopenharmony_ci dmah = hw->di[i]; 751462306a36Sopenharmony_ci if (dmah != NULL) 751562306a36Sopenharmony_ci dma_walk_packets(dmah, dma_callback_fn, sta); 751662306a36Sopenharmony_ci } 751762306a36Sopenharmony_ci} 751862306a36Sopenharmony_ci 751962306a36Sopenharmony_ciint brcms_c_get_curband(struct brcms_c_info *wlc) 752062306a36Sopenharmony_ci{ 752162306a36Sopenharmony_ci return wlc->band->bandunit; 752262306a36Sopenharmony_ci} 752362306a36Sopenharmony_ci 752462306a36Sopenharmony_cibool brcms_c_tx_flush_completed(struct brcms_c_info *wlc) 752562306a36Sopenharmony_ci{ 752662306a36Sopenharmony_ci int i; 752762306a36Sopenharmony_ci 752862306a36Sopenharmony_ci /* Kick DMA to send any pending AMPDU */ 752962306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(wlc->hw->di); i++) 753062306a36Sopenharmony_ci if (wlc->hw->di[i]) 753162306a36Sopenharmony_ci dma_kick_tx(wlc->hw->di[i]); 753262306a36Sopenharmony_ci 753362306a36Sopenharmony_ci return !brcms_txpktpendtot(wlc); 753462306a36Sopenharmony_ci} 753562306a36Sopenharmony_ci 753662306a36Sopenharmony_civoid brcms_c_set_beacon_listen_interval(struct brcms_c_info *wlc, u8 interval) 753762306a36Sopenharmony_ci{ 753862306a36Sopenharmony_ci wlc->bcn_li_bcn = interval; 753962306a36Sopenharmony_ci if (wlc->pub->up) 754062306a36Sopenharmony_ci brcms_c_bcn_li_upd(wlc); 754162306a36Sopenharmony_ci} 754262306a36Sopenharmony_ci 754362306a36Sopenharmony_ciu64 brcms_c_tsf_get(struct brcms_c_info *wlc) 754462306a36Sopenharmony_ci{ 754562306a36Sopenharmony_ci u32 tsf_h, tsf_l; 754662306a36Sopenharmony_ci u64 tsf; 754762306a36Sopenharmony_ci 754862306a36Sopenharmony_ci brcms_b_read_tsf(wlc->hw, &tsf_l, &tsf_h); 754962306a36Sopenharmony_ci 755062306a36Sopenharmony_ci tsf = tsf_h; 755162306a36Sopenharmony_ci tsf <<= 32; 755262306a36Sopenharmony_ci tsf |= tsf_l; 755362306a36Sopenharmony_ci 755462306a36Sopenharmony_ci return tsf; 755562306a36Sopenharmony_ci} 755662306a36Sopenharmony_ci 755762306a36Sopenharmony_civoid brcms_c_tsf_set(struct brcms_c_info *wlc, u64 tsf) 755862306a36Sopenharmony_ci{ 755962306a36Sopenharmony_ci u32 tsf_h, tsf_l; 756062306a36Sopenharmony_ci 756162306a36Sopenharmony_ci brcms_c_time_lock(wlc); 756262306a36Sopenharmony_ci 756362306a36Sopenharmony_ci tsf_l = tsf; 756462306a36Sopenharmony_ci tsf_h = (tsf >> 32); 756562306a36Sopenharmony_ci 756662306a36Sopenharmony_ci /* read the tsf timer low, then high to get an atomic read */ 756762306a36Sopenharmony_ci bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerlow), tsf_l); 756862306a36Sopenharmony_ci bcma_write32(wlc->hw->d11core, D11REGOFFS(tsf_timerhigh), tsf_h); 756962306a36Sopenharmony_ci 757062306a36Sopenharmony_ci brcms_c_time_unlock(wlc); 757162306a36Sopenharmony_ci} 757262306a36Sopenharmony_ci 757362306a36Sopenharmony_ciint brcms_c_set_tx_power(struct brcms_c_info *wlc, int txpwr) 757462306a36Sopenharmony_ci{ 757562306a36Sopenharmony_ci uint qdbm; 757662306a36Sopenharmony_ci 757762306a36Sopenharmony_ci /* Remove override bit and clip to max qdbm value */ 757862306a36Sopenharmony_ci qdbm = min_t(uint, txpwr * BRCMS_TXPWR_DB_FACTOR, 0xff); 757962306a36Sopenharmony_ci return wlc_phy_txpower_set(wlc->band->pi, qdbm, false); 758062306a36Sopenharmony_ci} 758162306a36Sopenharmony_ci 758262306a36Sopenharmony_ciint brcms_c_get_tx_power(struct brcms_c_info *wlc) 758362306a36Sopenharmony_ci{ 758462306a36Sopenharmony_ci uint qdbm; 758562306a36Sopenharmony_ci bool override; 758662306a36Sopenharmony_ci 758762306a36Sopenharmony_ci wlc_phy_txpower_get(wlc->band->pi, &qdbm, &override); 758862306a36Sopenharmony_ci 758962306a36Sopenharmony_ci /* Return qdbm units */ 759062306a36Sopenharmony_ci return (int)(qdbm / BRCMS_TXPWR_DB_FACTOR); 759162306a36Sopenharmony_ci} 759262306a36Sopenharmony_ci 759362306a36Sopenharmony_ci/* Process received frames */ 759462306a36Sopenharmony_ci/* 759562306a36Sopenharmony_ci * Return true if more frames need to be processed. false otherwise. 759662306a36Sopenharmony_ci * Param 'bound' indicates max. # frames to process before break out. 759762306a36Sopenharmony_ci */ 759862306a36Sopenharmony_cistatic void brcms_c_recv(struct brcms_c_info *wlc, struct sk_buff *p) 759962306a36Sopenharmony_ci{ 760062306a36Sopenharmony_ci struct d11rxhdr *rxh; 760162306a36Sopenharmony_ci struct ieee80211_hdr *h; 760262306a36Sopenharmony_ci uint len; 760362306a36Sopenharmony_ci bool is_amsdu; 760462306a36Sopenharmony_ci 760562306a36Sopenharmony_ci /* frame starts with rxhdr */ 760662306a36Sopenharmony_ci rxh = (struct d11rxhdr *) (p->data); 760762306a36Sopenharmony_ci 760862306a36Sopenharmony_ci /* strip off rxhdr */ 760962306a36Sopenharmony_ci skb_pull(p, BRCMS_HWRXOFF); 761062306a36Sopenharmony_ci 761162306a36Sopenharmony_ci /* MAC inserts 2 pad bytes for a4 headers or QoS or A-MSDU subframes */ 761262306a36Sopenharmony_ci if (rxh->RxStatus1 & RXS_PBPRES) { 761362306a36Sopenharmony_ci if (p->len < 2) { 761462306a36Sopenharmony_ci brcms_err(wlc->hw->d11core, 761562306a36Sopenharmony_ci "wl%d: recv: rcvd runt of len %d\n", 761662306a36Sopenharmony_ci wlc->pub->unit, p->len); 761762306a36Sopenharmony_ci goto toss; 761862306a36Sopenharmony_ci } 761962306a36Sopenharmony_ci skb_pull(p, 2); 762062306a36Sopenharmony_ci } 762162306a36Sopenharmony_ci 762262306a36Sopenharmony_ci h = (struct ieee80211_hdr *)(p->data + D11_PHY_HDR_LEN); 762362306a36Sopenharmony_ci len = p->len; 762462306a36Sopenharmony_ci 762562306a36Sopenharmony_ci if (rxh->RxStatus1 & RXS_FCSERR) { 762662306a36Sopenharmony_ci if (!(wlc->filter_flags & FIF_FCSFAIL)) 762762306a36Sopenharmony_ci goto toss; 762862306a36Sopenharmony_ci } 762962306a36Sopenharmony_ci 763062306a36Sopenharmony_ci /* check received pkt has at least frame control field */ 763162306a36Sopenharmony_ci if (len < D11_PHY_HDR_LEN + sizeof(h->frame_control)) 763262306a36Sopenharmony_ci goto toss; 763362306a36Sopenharmony_ci 763462306a36Sopenharmony_ci /* not supporting A-MSDU */ 763562306a36Sopenharmony_ci is_amsdu = rxh->RxStatus2 & RXS_AMSDU_MASK; 763662306a36Sopenharmony_ci if (is_amsdu) 763762306a36Sopenharmony_ci goto toss; 763862306a36Sopenharmony_ci 763962306a36Sopenharmony_ci brcms_c_recvctl(wlc, rxh, p); 764062306a36Sopenharmony_ci return; 764162306a36Sopenharmony_ci 764262306a36Sopenharmony_ci toss: 764362306a36Sopenharmony_ci brcmu_pkt_buf_free_skb(p); 764462306a36Sopenharmony_ci} 764562306a36Sopenharmony_ci 764662306a36Sopenharmony_ci/* Process received frames */ 764762306a36Sopenharmony_ci/* 764862306a36Sopenharmony_ci * Return true if more frames need to be processed. false otherwise. 764962306a36Sopenharmony_ci * Param 'bound' indicates max. # frames to process before break out. 765062306a36Sopenharmony_ci */ 765162306a36Sopenharmony_cistatic bool 765262306a36Sopenharmony_cibrcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound) 765362306a36Sopenharmony_ci{ 765462306a36Sopenharmony_ci struct sk_buff *p; 765562306a36Sopenharmony_ci struct sk_buff *next = NULL; 765662306a36Sopenharmony_ci struct sk_buff_head recv_frames; 765762306a36Sopenharmony_ci 765862306a36Sopenharmony_ci uint n = 0; 765962306a36Sopenharmony_ci uint bound_limit = bound ? RXBND : -1; 766062306a36Sopenharmony_ci bool morepending = false; 766162306a36Sopenharmony_ci 766262306a36Sopenharmony_ci skb_queue_head_init(&recv_frames); 766362306a36Sopenharmony_ci 766462306a36Sopenharmony_ci /* gather received frames */ 766562306a36Sopenharmony_ci do { 766662306a36Sopenharmony_ci /* !give others some time to run! */ 766762306a36Sopenharmony_ci if (n >= bound_limit) 766862306a36Sopenharmony_ci break; 766962306a36Sopenharmony_ci 767062306a36Sopenharmony_ci morepending = dma_rx(wlc_hw->di[fifo], &recv_frames); 767162306a36Sopenharmony_ci n++; 767262306a36Sopenharmony_ci } while (morepending); 767362306a36Sopenharmony_ci 767462306a36Sopenharmony_ci /* post more rbufs */ 767562306a36Sopenharmony_ci dma_rxfill(wlc_hw->di[fifo]); 767662306a36Sopenharmony_ci 767762306a36Sopenharmony_ci /* process each frame */ 767862306a36Sopenharmony_ci skb_queue_walk_safe(&recv_frames, p, next) { 767962306a36Sopenharmony_ci struct d11rxhdr_le *rxh_le; 768062306a36Sopenharmony_ci struct d11rxhdr *rxh; 768162306a36Sopenharmony_ci 768262306a36Sopenharmony_ci skb_unlink(p, &recv_frames); 768362306a36Sopenharmony_ci rxh_le = (struct d11rxhdr_le *)p->data; 768462306a36Sopenharmony_ci rxh = (struct d11rxhdr *)p->data; 768562306a36Sopenharmony_ci 768662306a36Sopenharmony_ci /* fixup rx header endianness */ 768762306a36Sopenharmony_ci rxh->RxFrameSize = le16_to_cpu(rxh_le->RxFrameSize); 768862306a36Sopenharmony_ci rxh->PhyRxStatus_0 = le16_to_cpu(rxh_le->PhyRxStatus_0); 768962306a36Sopenharmony_ci rxh->PhyRxStatus_1 = le16_to_cpu(rxh_le->PhyRxStatus_1); 769062306a36Sopenharmony_ci rxh->PhyRxStatus_2 = le16_to_cpu(rxh_le->PhyRxStatus_2); 769162306a36Sopenharmony_ci rxh->PhyRxStatus_3 = le16_to_cpu(rxh_le->PhyRxStatus_3); 769262306a36Sopenharmony_ci rxh->PhyRxStatus_4 = le16_to_cpu(rxh_le->PhyRxStatus_4); 769362306a36Sopenharmony_ci rxh->PhyRxStatus_5 = le16_to_cpu(rxh_le->PhyRxStatus_5); 769462306a36Sopenharmony_ci rxh->RxStatus1 = le16_to_cpu(rxh_le->RxStatus1); 769562306a36Sopenharmony_ci rxh->RxStatus2 = le16_to_cpu(rxh_le->RxStatus2); 769662306a36Sopenharmony_ci rxh->RxTSFTime = le16_to_cpu(rxh_le->RxTSFTime); 769762306a36Sopenharmony_ci rxh->RxChan = le16_to_cpu(rxh_le->RxChan); 769862306a36Sopenharmony_ci 769962306a36Sopenharmony_ci brcms_c_recv(wlc_hw->wlc, p); 770062306a36Sopenharmony_ci } 770162306a36Sopenharmony_ci 770262306a36Sopenharmony_ci return morepending; 770362306a36Sopenharmony_ci} 770462306a36Sopenharmony_ci 770562306a36Sopenharmony_ci/* second-level interrupt processing 770662306a36Sopenharmony_ci * Return true if another dpc needs to be re-scheduled. false otherwise. 770762306a36Sopenharmony_ci * Param 'bounded' indicates if applicable loops should be bounded. 770862306a36Sopenharmony_ci */ 770962306a36Sopenharmony_cibool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded) 771062306a36Sopenharmony_ci{ 771162306a36Sopenharmony_ci u32 macintstatus; 771262306a36Sopenharmony_ci struct brcms_hardware *wlc_hw = wlc->hw; 771362306a36Sopenharmony_ci struct bcma_device *core = wlc_hw->d11core; 771462306a36Sopenharmony_ci 771562306a36Sopenharmony_ci if (brcms_deviceremoved(wlc)) { 771662306a36Sopenharmony_ci brcms_err(core, "wl%d: %s: dead chip\n", wlc_hw->unit, 771762306a36Sopenharmony_ci __func__); 771862306a36Sopenharmony_ci brcms_down(wlc->wl); 771962306a36Sopenharmony_ci return false; 772062306a36Sopenharmony_ci } 772162306a36Sopenharmony_ci 772262306a36Sopenharmony_ci /* grab and clear the saved software intstatus bits */ 772362306a36Sopenharmony_ci macintstatus = wlc->macintstatus; 772462306a36Sopenharmony_ci wlc->macintstatus = 0; 772562306a36Sopenharmony_ci 772662306a36Sopenharmony_ci brcms_dbg_int(core, "wl%d: macintstatus 0x%x\n", 772762306a36Sopenharmony_ci wlc_hw->unit, macintstatus); 772862306a36Sopenharmony_ci 772962306a36Sopenharmony_ci WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */ 773062306a36Sopenharmony_ci 773162306a36Sopenharmony_ci /* tx status */ 773262306a36Sopenharmony_ci if (macintstatus & MI_TFS) { 773362306a36Sopenharmony_ci bool fatal; 773462306a36Sopenharmony_ci if (brcms_b_txstatus(wlc->hw, bounded, &fatal)) 773562306a36Sopenharmony_ci wlc->macintstatus |= MI_TFS; 773662306a36Sopenharmony_ci if (fatal) { 773762306a36Sopenharmony_ci brcms_err(core, "MI_TFS: fatal\n"); 773862306a36Sopenharmony_ci goto fatal; 773962306a36Sopenharmony_ci } 774062306a36Sopenharmony_ci } 774162306a36Sopenharmony_ci 774262306a36Sopenharmony_ci if (macintstatus & (MI_TBTT | MI_DTIM_TBTT)) 774362306a36Sopenharmony_ci brcms_c_tbtt(wlc); 774462306a36Sopenharmony_ci 774562306a36Sopenharmony_ci /* ATIM window end */ 774662306a36Sopenharmony_ci if (macintstatus & MI_ATIMWINEND) { 774762306a36Sopenharmony_ci brcms_dbg_info(core, "end of ATIM window\n"); 774862306a36Sopenharmony_ci bcma_set32(core, D11REGOFFS(maccommand), wlc->qvalid); 774962306a36Sopenharmony_ci wlc->qvalid = 0; 775062306a36Sopenharmony_ci } 775162306a36Sopenharmony_ci 775262306a36Sopenharmony_ci /* 775362306a36Sopenharmony_ci * received data or control frame, MI_DMAINT is 775462306a36Sopenharmony_ci * indication of RX_FIFO interrupt 775562306a36Sopenharmony_ci */ 775662306a36Sopenharmony_ci if (macintstatus & MI_DMAINT) 775762306a36Sopenharmony_ci if (brcms_b_recv(wlc_hw, RX_FIFO, bounded)) 775862306a36Sopenharmony_ci wlc->macintstatus |= MI_DMAINT; 775962306a36Sopenharmony_ci 776062306a36Sopenharmony_ci /* noise sample collected */ 776162306a36Sopenharmony_ci if (macintstatus & MI_BG_NOISE) 776262306a36Sopenharmony_ci wlc_phy_noise_sample_intr(wlc_hw->band->pi); 776362306a36Sopenharmony_ci 776462306a36Sopenharmony_ci if (macintstatus & MI_GP0) { 776562306a36Sopenharmony_ci brcms_err(core, "wl%d: PSM microcode watchdog fired at %d " 776662306a36Sopenharmony_ci "(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now); 776762306a36Sopenharmony_ci 776862306a36Sopenharmony_ci printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n", 776962306a36Sopenharmony_ci __func__, ai_get_chip_id(wlc_hw->sih), 777062306a36Sopenharmony_ci ai_get_chiprev(wlc_hw->sih)); 777162306a36Sopenharmony_ci brcms_fatal_error(wlc_hw->wlc->wl); 777262306a36Sopenharmony_ci } 777362306a36Sopenharmony_ci 777462306a36Sopenharmony_ci /* gptimer timeout */ 777562306a36Sopenharmony_ci if (macintstatus & MI_TO) 777662306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(gptimer), 0); 777762306a36Sopenharmony_ci 777862306a36Sopenharmony_ci if (macintstatus & MI_RFDISABLE) { 777962306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d: BMAC Detected a change on the" 778062306a36Sopenharmony_ci " RF Disable Input\n", wlc_hw->unit); 778162306a36Sopenharmony_ci brcms_rfkill_set_hw_state(wlc->wl); 778262306a36Sopenharmony_ci } 778362306a36Sopenharmony_ci 778462306a36Sopenharmony_ci /* BCN template is available */ 778562306a36Sopenharmony_ci if (macintstatus & MI_BCNTPL) 778662306a36Sopenharmony_ci brcms_c_update_beacon(wlc); 778762306a36Sopenharmony_ci 778862306a36Sopenharmony_ci /* it isn't done and needs to be resched if macintstatus is non-zero */ 778962306a36Sopenharmony_ci return wlc->macintstatus != 0; 779062306a36Sopenharmony_ci 779162306a36Sopenharmony_ci fatal: 779262306a36Sopenharmony_ci brcms_fatal_error(wlc_hw->wlc->wl); 779362306a36Sopenharmony_ci return wlc->macintstatus != 0; 779462306a36Sopenharmony_ci} 779562306a36Sopenharmony_ci 779662306a36Sopenharmony_civoid brcms_c_init(struct brcms_c_info *wlc, bool mute_tx) 779762306a36Sopenharmony_ci{ 779862306a36Sopenharmony_ci struct bcma_device *core = wlc->hw->d11core; 779962306a36Sopenharmony_ci struct ieee80211_channel *ch = wlc->pub->ieee_hw->conf.chandef.chan; 780062306a36Sopenharmony_ci u16 chanspec; 780162306a36Sopenharmony_ci 780262306a36Sopenharmony_ci brcms_dbg_info(core, "wl%d\n", wlc->pub->unit); 780362306a36Sopenharmony_ci 780462306a36Sopenharmony_ci chanspec = ch20mhz_chspec(ch->hw_value); 780562306a36Sopenharmony_ci 780662306a36Sopenharmony_ci brcms_b_init(wlc->hw, chanspec); 780762306a36Sopenharmony_ci 780862306a36Sopenharmony_ci /* update beacon listen interval */ 780962306a36Sopenharmony_ci brcms_c_bcn_li_upd(wlc); 781062306a36Sopenharmony_ci 781162306a36Sopenharmony_ci /* write ethernet address to core */ 781262306a36Sopenharmony_ci brcms_c_set_mac(wlc->bsscfg); 781362306a36Sopenharmony_ci brcms_c_set_bssid(wlc->bsscfg); 781462306a36Sopenharmony_ci 781562306a36Sopenharmony_ci /* Update tsf_cfprep if associated and up */ 781662306a36Sopenharmony_ci if (wlc->pub->associated && wlc->pub->up) { 781762306a36Sopenharmony_ci u32 bi; 781862306a36Sopenharmony_ci 781962306a36Sopenharmony_ci /* get beacon period and convert to uS */ 782062306a36Sopenharmony_ci bi = wlc->bsscfg->current_bss->beacon_period << 10; 782162306a36Sopenharmony_ci /* 782262306a36Sopenharmony_ci * update since init path would reset 782362306a36Sopenharmony_ci * to default value 782462306a36Sopenharmony_ci */ 782562306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(tsf_cfprep), 782662306a36Sopenharmony_ci bi << CFPREP_CBI_SHIFT); 782762306a36Sopenharmony_ci 782862306a36Sopenharmony_ci /* Update maccontrol PM related bits */ 782962306a36Sopenharmony_ci brcms_c_set_ps_ctrl(wlc); 783062306a36Sopenharmony_ci } 783162306a36Sopenharmony_ci 783262306a36Sopenharmony_ci brcms_c_bandinit_ordered(wlc, chanspec); 783362306a36Sopenharmony_ci 783462306a36Sopenharmony_ci /* init probe response timeout */ 783562306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_PRS_MAXTIME, wlc->prb_resp_timeout); 783662306a36Sopenharmony_ci 783762306a36Sopenharmony_ci /* init max burst txop (framebursting) */ 783862306a36Sopenharmony_ci brcms_b_write_shm(wlc->hw, M_MBURST_TXOP, 783962306a36Sopenharmony_ci (wlc-> 784062306a36Sopenharmony_ci _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP)); 784162306a36Sopenharmony_ci 784262306a36Sopenharmony_ci /* initialize maximum allowed duty cycle */ 784362306a36Sopenharmony_ci brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true); 784462306a36Sopenharmony_ci brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true); 784562306a36Sopenharmony_ci 784662306a36Sopenharmony_ci /* 784762306a36Sopenharmony_ci * Update some shared memory locations related to 784862306a36Sopenharmony_ci * max AMPDU size allowed to received 784962306a36Sopenharmony_ci */ 785062306a36Sopenharmony_ci brcms_c_ampdu_shm_upd(wlc->ampdu); 785162306a36Sopenharmony_ci 785262306a36Sopenharmony_ci /* band-specific inits */ 785362306a36Sopenharmony_ci brcms_c_bsinit(wlc); 785462306a36Sopenharmony_ci 785562306a36Sopenharmony_ci /* Enable EDCF mode (while the MAC is suspended) */ 785662306a36Sopenharmony_ci bcma_set16(core, D11REGOFFS(ifs_ctl), IFS_USEEDCF); 785762306a36Sopenharmony_ci brcms_c_edcf_setparams(wlc, false); 785862306a36Sopenharmony_ci 785962306a36Sopenharmony_ci /* read the ucode version if we have not yet done so */ 786062306a36Sopenharmony_ci if (wlc->ucode_rev == 0) { 786162306a36Sopenharmony_ci u16 rev; 786262306a36Sopenharmony_ci u16 patch; 786362306a36Sopenharmony_ci 786462306a36Sopenharmony_ci rev = brcms_b_read_shm(wlc->hw, M_BOM_REV_MAJOR); 786562306a36Sopenharmony_ci patch = brcms_b_read_shm(wlc->hw, M_BOM_REV_MINOR); 786662306a36Sopenharmony_ci wlc->ucode_rev = (rev << NBITS(u16)) | patch; 786762306a36Sopenharmony_ci snprintf(wlc->wiphy->fw_version, 786862306a36Sopenharmony_ci sizeof(wlc->wiphy->fw_version), "%u.%u", rev, patch); 786962306a36Sopenharmony_ci } 787062306a36Sopenharmony_ci 787162306a36Sopenharmony_ci /* ..now really unleash hell (allow the MAC out of suspend) */ 787262306a36Sopenharmony_ci brcms_c_enable_mac(wlc); 787362306a36Sopenharmony_ci 787462306a36Sopenharmony_ci /* suspend the tx fifos and mute the phy for preism cac time */ 787562306a36Sopenharmony_ci if (mute_tx) 787662306a36Sopenharmony_ci brcms_b_mute(wlc->hw, true); 787762306a36Sopenharmony_ci 787862306a36Sopenharmony_ci /* enable the RF Disable Delay timer */ 787962306a36Sopenharmony_ci bcma_write32(core, D11REGOFFS(rfdisabledly), RFDISABLE_DEFAULT); 788062306a36Sopenharmony_ci 788162306a36Sopenharmony_ci /* 788262306a36Sopenharmony_ci * Initialize WME parameters; if they haven't been set by some other 788362306a36Sopenharmony_ci * mechanism (IOVar, etc) then read them from the hardware. 788462306a36Sopenharmony_ci */ 788562306a36Sopenharmony_ci if (GFIELD(wlc->wme_retries[0], EDCF_SHORT) == 0) { 788662306a36Sopenharmony_ci /* Uninitialized; read from HW */ 788762306a36Sopenharmony_ci int ac; 788862306a36Sopenharmony_ci 788962306a36Sopenharmony_ci for (ac = 0; ac < IEEE80211_NUM_ACS; ac++) 789062306a36Sopenharmony_ci wlc->wme_retries[ac] = 789162306a36Sopenharmony_ci brcms_b_read_shm(wlc->hw, M_AC_TXLMT_ADDR(ac)); 789262306a36Sopenharmony_ci } 789362306a36Sopenharmony_ci} 789462306a36Sopenharmony_ci 789562306a36Sopenharmony_ci/* 789662306a36Sopenharmony_ci * The common driver entry routine. Error codes should be unique 789762306a36Sopenharmony_ci */ 789862306a36Sopenharmony_cistruct brcms_c_info * 789962306a36Sopenharmony_cibrcms_c_attach(struct brcms_info *wl, struct bcma_device *core, uint unit, 790062306a36Sopenharmony_ci bool piomode, uint *perr) 790162306a36Sopenharmony_ci{ 790262306a36Sopenharmony_ci struct brcms_c_info *wlc; 790362306a36Sopenharmony_ci uint err = 0; 790462306a36Sopenharmony_ci uint i, j; 790562306a36Sopenharmony_ci struct brcms_pub *pub; 790662306a36Sopenharmony_ci 790762306a36Sopenharmony_ci /* allocate struct brcms_c_info state and its substructures */ 790862306a36Sopenharmony_ci wlc = brcms_c_attach_malloc(unit, &err, 0); 790962306a36Sopenharmony_ci if (wlc == NULL) 791062306a36Sopenharmony_ci goto fail; 791162306a36Sopenharmony_ci wlc->wiphy = wl->wiphy; 791262306a36Sopenharmony_ci pub = wlc->pub; 791362306a36Sopenharmony_ci 791462306a36Sopenharmony_ci#if defined(DEBUG) 791562306a36Sopenharmony_ci wlc_info_dbg = wlc; 791662306a36Sopenharmony_ci#endif 791762306a36Sopenharmony_ci 791862306a36Sopenharmony_ci wlc->band = wlc->bandstate[0]; 791962306a36Sopenharmony_ci wlc->core = wlc->corestate; 792062306a36Sopenharmony_ci wlc->wl = wl; 792162306a36Sopenharmony_ci pub->unit = unit; 792262306a36Sopenharmony_ci pub->_piomode = piomode; 792362306a36Sopenharmony_ci wlc->bandinit_pending = false; 792462306a36Sopenharmony_ci wlc->beacon_template_virgin = true; 792562306a36Sopenharmony_ci 792662306a36Sopenharmony_ci /* populate struct brcms_c_info with default values */ 792762306a36Sopenharmony_ci brcms_c_info_init(wlc, unit); 792862306a36Sopenharmony_ci 792962306a36Sopenharmony_ci /* update sta/ap related parameters */ 793062306a36Sopenharmony_ci brcms_c_ap_upd(wlc); 793162306a36Sopenharmony_ci 793262306a36Sopenharmony_ci /* 793362306a36Sopenharmony_ci * low level attach steps(all hw accesses go 793462306a36Sopenharmony_ci * inside, no more in rest of the attach) 793562306a36Sopenharmony_ci */ 793662306a36Sopenharmony_ci err = brcms_b_attach(wlc, core, unit, piomode); 793762306a36Sopenharmony_ci if (err) 793862306a36Sopenharmony_ci goto fail; 793962306a36Sopenharmony_ci 794062306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_PAM_OVR, OFF); 794162306a36Sopenharmony_ci 794262306a36Sopenharmony_ci pub->phy_11ncapable = BRCMS_PHY_11N_CAP(wlc->band); 794362306a36Sopenharmony_ci 794462306a36Sopenharmony_ci /* disable allowed duty cycle */ 794562306a36Sopenharmony_ci wlc->tx_duty_cycle_ofdm = 0; 794662306a36Sopenharmony_ci wlc->tx_duty_cycle_cck = 0; 794762306a36Sopenharmony_ci 794862306a36Sopenharmony_ci brcms_c_stf_phy_chain_calc(wlc); 794962306a36Sopenharmony_ci 795062306a36Sopenharmony_ci /* txchain 1: txant 0, txchain 2: txant 1 */ 795162306a36Sopenharmony_ci if (BRCMS_ISNPHY(wlc->band) && (wlc->stf->txstreams == 1)) 795262306a36Sopenharmony_ci wlc->stf->txant = wlc->stf->hw_txchain - 1; 795362306a36Sopenharmony_ci 795462306a36Sopenharmony_ci /* push to BMAC driver */ 795562306a36Sopenharmony_ci wlc_phy_stf_chain_init(wlc->band->pi, wlc->stf->hw_txchain, 795662306a36Sopenharmony_ci wlc->stf->hw_rxchain); 795762306a36Sopenharmony_ci 795862306a36Sopenharmony_ci /* pull up some info resulting from the low attach */ 795962306a36Sopenharmony_ci for (i = 0; i < NFIFO; i++) 796062306a36Sopenharmony_ci wlc->core->txavail[i] = wlc->hw->txavail[i]; 796162306a36Sopenharmony_ci 796262306a36Sopenharmony_ci memcpy(&wlc->perm_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); 796362306a36Sopenharmony_ci memcpy(&pub->cur_etheraddr, &wlc->hw->etheraddr, ETH_ALEN); 796462306a36Sopenharmony_ci 796562306a36Sopenharmony_ci for (j = 0; j < wlc->pub->_nbands; j++) { 796662306a36Sopenharmony_ci wlc->band = wlc->bandstate[j]; 796762306a36Sopenharmony_ci 796862306a36Sopenharmony_ci if (!brcms_c_attach_stf_ant_init(wlc)) { 796962306a36Sopenharmony_ci err = 24; 797062306a36Sopenharmony_ci goto fail; 797162306a36Sopenharmony_ci } 797262306a36Sopenharmony_ci 797362306a36Sopenharmony_ci /* default contention windows size limits */ 797462306a36Sopenharmony_ci wlc->band->CWmin = APHY_CWMIN; 797562306a36Sopenharmony_ci wlc->band->CWmax = PHY_CWMAX; 797662306a36Sopenharmony_ci 797762306a36Sopenharmony_ci /* init gmode value */ 797862306a36Sopenharmony_ci if (wlc->band->bandtype == BRCM_BAND_2G) { 797962306a36Sopenharmony_ci wlc->band->gmode = GMODE_AUTO; 798062306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_G_USER, 798162306a36Sopenharmony_ci wlc->band->gmode); 798262306a36Sopenharmony_ci } 798362306a36Sopenharmony_ci 798462306a36Sopenharmony_ci /* init _n_enab supported mode */ 798562306a36Sopenharmony_ci if (BRCMS_PHY_11N_CAP(wlc->band)) { 798662306a36Sopenharmony_ci pub->_n_enab = SUPPORT_11N; 798762306a36Sopenharmony_ci brcms_c_protection_upd(wlc, BRCMS_PROT_N_USER, 798862306a36Sopenharmony_ci ((pub->_n_enab == 798962306a36Sopenharmony_ci SUPPORT_11N) ? WL_11N_2x2 : 799062306a36Sopenharmony_ci WL_11N_3x3)); 799162306a36Sopenharmony_ci } 799262306a36Sopenharmony_ci 799362306a36Sopenharmony_ci /* init per-band default rateset, depend on band->gmode */ 799462306a36Sopenharmony_ci brcms_default_rateset(wlc, &wlc->band->defrateset); 799562306a36Sopenharmony_ci 799662306a36Sopenharmony_ci /* fill in hw_rateset */ 799762306a36Sopenharmony_ci brcms_c_rateset_filter(&wlc->band->defrateset, 799862306a36Sopenharmony_ci &wlc->band->hw_rateset, false, 799962306a36Sopenharmony_ci BRCMS_RATES_CCK_OFDM, BRCMS_RATE_MASK, 800062306a36Sopenharmony_ci (bool) (wlc->pub->_n_enab & SUPPORT_11N)); 800162306a36Sopenharmony_ci } 800262306a36Sopenharmony_ci 800362306a36Sopenharmony_ci /* 800462306a36Sopenharmony_ci * update antenna config due to 800562306a36Sopenharmony_ci * wlc->stf->txant/txchain/ant_rx_ovr change 800662306a36Sopenharmony_ci */ 800762306a36Sopenharmony_ci brcms_c_stf_phy_txant_upd(wlc); 800862306a36Sopenharmony_ci 800962306a36Sopenharmony_ci /* attach each modules */ 801062306a36Sopenharmony_ci err = brcms_c_attach_module(wlc); 801162306a36Sopenharmony_ci if (err != 0) 801262306a36Sopenharmony_ci goto fail; 801362306a36Sopenharmony_ci 801462306a36Sopenharmony_ci if (!brcms_c_timers_init(wlc, unit)) { 801562306a36Sopenharmony_ci wiphy_err(wl->wiphy, "wl%d: %s: init_timer failed\n", unit, 801662306a36Sopenharmony_ci __func__); 801762306a36Sopenharmony_ci err = 32; 801862306a36Sopenharmony_ci goto fail; 801962306a36Sopenharmony_ci } 802062306a36Sopenharmony_ci 802162306a36Sopenharmony_ci /* depend on rateset, gmode */ 802262306a36Sopenharmony_ci wlc->cmi = brcms_c_channel_mgr_attach(wlc); 802362306a36Sopenharmony_ci if (!wlc->cmi) { 802462306a36Sopenharmony_ci wiphy_err(wl->wiphy, "wl%d: %s: channel_mgr_attach failed" 802562306a36Sopenharmony_ci "\n", unit, __func__); 802662306a36Sopenharmony_ci err = 33; 802762306a36Sopenharmony_ci goto fail; 802862306a36Sopenharmony_ci } 802962306a36Sopenharmony_ci 803062306a36Sopenharmony_ci /* init default when all parameters are ready, i.e. ->rateset */ 803162306a36Sopenharmony_ci brcms_c_bss_default_init(wlc); 803262306a36Sopenharmony_ci 803362306a36Sopenharmony_ci /* 803462306a36Sopenharmony_ci * Complete the wlc default state initializations.. 803562306a36Sopenharmony_ci */ 803662306a36Sopenharmony_ci 803762306a36Sopenharmony_ci wlc->bsscfg->wlc = wlc; 803862306a36Sopenharmony_ci 803962306a36Sopenharmony_ci wlc->mimoft = FT_HT; 804062306a36Sopenharmony_ci wlc->mimo_40txbw = AUTO; 804162306a36Sopenharmony_ci wlc->ofdm_40txbw = AUTO; 804262306a36Sopenharmony_ci wlc->cck_40txbw = AUTO; 804362306a36Sopenharmony_ci brcms_c_update_mimo_band_bwcap(wlc, BRCMS_N_BW_20IN2G_40IN5G); 804462306a36Sopenharmony_ci 804562306a36Sopenharmony_ci /* Set default values of SGI */ 804662306a36Sopenharmony_ci if (BRCMS_SGI_CAP_PHY(wlc)) { 804762306a36Sopenharmony_ci brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | 804862306a36Sopenharmony_ci BRCMS_N_SGI_40)); 804962306a36Sopenharmony_ci } else if (BRCMS_ISSSLPNPHY(wlc->band)) { 805062306a36Sopenharmony_ci brcms_c_ht_update_sgi_rx(wlc, (BRCMS_N_SGI_20 | 805162306a36Sopenharmony_ci BRCMS_N_SGI_40)); 805262306a36Sopenharmony_ci } else { 805362306a36Sopenharmony_ci brcms_c_ht_update_sgi_rx(wlc, 0); 805462306a36Sopenharmony_ci } 805562306a36Sopenharmony_ci 805662306a36Sopenharmony_ci brcms_b_antsel_set(wlc->hw, wlc->asi->antsel_avail); 805762306a36Sopenharmony_ci 805862306a36Sopenharmony_ci if (perr) 805962306a36Sopenharmony_ci *perr = 0; 806062306a36Sopenharmony_ci 806162306a36Sopenharmony_ci return wlc; 806262306a36Sopenharmony_ci 806362306a36Sopenharmony_ci fail: 806462306a36Sopenharmony_ci wiphy_err(wl->wiphy, "wl%d: %s: failed with err %d\n", 806562306a36Sopenharmony_ci unit, __func__, err); 806662306a36Sopenharmony_ci if (wlc) 806762306a36Sopenharmony_ci brcms_c_detach(wlc); 806862306a36Sopenharmony_ci 806962306a36Sopenharmony_ci if (perr) 807062306a36Sopenharmony_ci *perr = err; 807162306a36Sopenharmony_ci return NULL; 807262306a36Sopenharmony_ci} 8073