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