From d8c8e90f28e80b094c1ba52884bcfdfbf0fd7441 Mon Sep 17 00:00:00 2001
From: Henry Ptasinski <henryp@broadcom.com>
Date: Tue, 5 Jul 2011 22:08:37 -0700
Subject: [PATCH] wireless-testing: add brcm80211

Add the brcm80211 tree to drivers/net/wireless, and disable the version that's
in drivers/staging.  This version includes the sources currently in staging,
plus any changes that have been sent out for review.

Sources in staging will be deleted in a followup patch.

Signed-off-by: Henry Ptasinski <henryp@broadcom.com>
---
 drivers/net/wireless/Kconfig                       |    1 +
 drivers/net/wireless/Makefile                      |    3 +
 drivers/net/wireless/brcm80211/Kconfig             |   40 +
 drivers/net/wireless/brcm80211/Makefile            |   24 +
 drivers/net/wireless/brcm80211/brcmfmac/Makefile   |   39 +
 drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h  |   32 +
 drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c   |  642 +
 .../net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c | 1196 +
 drivers/net/wireless/brcm80211/brcmfmac/dhd.h      |  904 +
 drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h  |   78 +
 drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c  |  502 +
 .../net/wireless/brcm80211/brcmfmac/dhd_common.c   | 1196 +
 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h  |   70 +
 .../net/wireless/brcm80211/brcmfmac/dhd_linux.c    | 1734 ++
 .../net/wireless/brcm80211/brcmfmac/dhd_proto.h    |   75 +
 drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 6771 +++++
 .../net/wireless/brcm80211/brcmfmac/sdio_host.h    |  347 +
 .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.c  | 4152 +++
 .../net/wireless/brcm80211/brcmfmac/wl_cfg80211.h  |  356 +
 drivers/net/wireless/brcm80211/brcmsmac/Makefile   |   58 +
 drivers/net/wireless/brcm80211/brcmsmac/aiutils.c  | 2279 ++
 drivers/net/wireless/brcm80211/brcmsmac/aiutils.h  |  584 +
 drivers/net/wireless/brcm80211/brcmsmac/alloc.c    |  275 +
 drivers/net/wireless/brcm80211/brcmsmac/alloc.h    |   19 +
 drivers/net/wireless/brcm80211/brcmsmac/ampdu.c    | 1219 +
 drivers/net/wireless/brcm80211/brcmsmac/ampdu.h    |   30 +
 drivers/net/wireless/brcm80211/brcmsmac/antsel.c   |  311 +
 drivers/net/wireless/brcm80211/brcmsmac/antsel.h   |   29 +
 drivers/net/wireless/brcm80211/brcmsmac/bmac.c     | 3593 +++
 drivers/net/wireless/brcm80211/brcmsmac/bmac.h     |  174 +
 drivers/net/wireless/brcm80211/brcmsmac/channel.c  | 1559 ++
 drivers/net/wireless/brcm80211/brcmsmac/channel.h  |  132 +
 drivers/net/wireless/brcm80211/brcmsmac/d11.h      | 1775 ++
 drivers/net/wireless/brcm80211/brcmsmac/dma.c      | 1917 ++
 drivers/net/wireless/brcm80211/brcmsmac/dma.h      |  250 +
 .../net/wireless/brcm80211/brcmsmac/mac80211_if.c  | 1933 ++
 .../net/wireless/brcm80211/brcmsmac/mac80211_if.h  |  108 +
 drivers/net/wireless/brcm80211/brcmsmac/main.c     | 6102 ++++
 drivers/net/wireless/brcm80211/brcmsmac/main.h     | 1025 +
 drivers/net/wireless/brcm80211/brcmsmac/nicpci.c   |  850 +
 drivers/net/wireless/brcm80211/brcmsmac/nicpci.h   |   85 +
 drivers/net/wireless/brcm80211/brcmsmac/otp.c      |  544 +
 drivers/net/wireless/brcm80211/brcmsmac/otp.h      |   47 +
 .../net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c  | 3225 +++
 .../net/wireless/brcm80211/brcmsmac/phy/phy_hal.h  |  294 +
 .../net/wireless/brcm80211/brcmsmac/phy/phy_int.h  | 1235 +
 .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c  | 5294 ++++
 .../net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h  |  121 +
 .../net/wireless/brcm80211/brcmsmac/phy/phy_n.c    |29082 ++++++++++++++++++++
 .../wireless/brcm80211/brcmsmac/phy/phy_qmath.c    |  294 +
 .../wireless/brcm80211/brcmsmac/phy/phy_qmath.h    |   42 +
 .../wireless/brcm80211/brcmsmac/phy/phy_radio.h    | 1533 ++
 .../net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h |  167 +
 .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c   | 3638 +++
 .../wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h   |   54 +
 .../net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c |10629 +++++++
 .../net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h |   40 +
 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c |  218 +
 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h |  164 +
 drivers/net/wireless/brcm80211/brcmsmac/pmu.c      |  474 +
 drivers/net/wireless/brcm80211/brcmsmac/pmu.h      |   52 +
 drivers/net/wireless/brcm80211/brcmsmac/pub.h      |  665 +
 drivers/net/wireless/brcm80211/brcmsmac/rate.c     |  498 +
 drivers/net/wireless/brcm80211/brcmsmac/rate.h     |  173 +
 drivers/net/wireless/brcm80211/brcmsmac/scb.h      |   85 +
 drivers/net/wireless/brcm80211/brcmsmac/srom.c     | 1237 +
 drivers/net/wireless/brcm80211/brcmsmac/srom.h     |   34 +
 drivers/net/wireless/brcm80211/brcmsmac/stf.c      |  477 +
 drivers/net/wireless/brcm80211/brcmsmac/stf.h      |   42 +
 drivers/net/wireless/brcm80211/brcmsmac/types.h    |  398 +
 .../net/wireless/brcm80211/brcmsmac/ucode_loader.c |  115 +
 .../net/wireless/brcm80211/brcmsmac/ucode_loader.h |   52 +
 drivers/net/wireless/brcm80211/brcmutil/Makefile   |   29 +
 drivers/net/wireless/brcm80211/brcmutil/utils.c    |  787 +
 drivers/net/wireless/brcm80211/brcmutil/wifi.c     |  131 +
 .../net/wireless/brcm80211/include/brcm_hw_ids.h   |   59 +
 .../net/wireless/brcm80211/include/brcmu_utils.h   |  301 +
 .../net/wireless/brcm80211/include/brcmu_wifi.h    |  243 +
 .../net/wireless/brcm80211/include/chipcommon.h    |  281 +
 drivers/net/wireless/brcm80211/include/defs.h      |  112 +
 drivers/net/wireless/brcm80211/include/soc.h       |   95 +
 drivers/staging/Kconfig                            |    2 +-
 drivers/staging/Makefile                           |    4 +-
 83 files changed, 105433 insertions(+), 3 deletions(-)
 create mode 100644 drivers/net/wireless/brcm80211/Kconfig
 create mode 100644 drivers/net/wireless/brcm80211/Makefile
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/Makefile
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/Makefile
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/alloc.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/alloc.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/antsel.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/antsel.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/bmac.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/bmac.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/channel.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/channel.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/d11.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/dma.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/dma.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/main.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/main.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/nicpci.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/nicpci.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/otp.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/otp.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_cmn.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_hal.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_int.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_lcn.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_n.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_qmath.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phy_radio.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phyreg_n.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_lcn.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy/phytbl_n.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/phy_shim.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pmu.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pmu.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/pub.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/rate.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/rate.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/scb.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/srom.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/srom.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/stf.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/stf.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/types.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmsmac/ucode_loader.h
 create mode 100644 drivers/net/wireless/brcm80211/brcmutil/Makefile
 create mode 100644 drivers/net/wireless/brcm80211/brcmutil/utils.c
 create mode 100644 drivers/net/wireless/brcm80211/brcmutil/wifi.c
 create mode 100644 drivers/net/wireless/brcm80211/include/brcm_hw_ids.h
 create mode 100644 drivers/net/wireless/brcm80211/include/brcmu_utils.h
 create mode 100644 drivers/net/wireless/brcm80211/include/brcmu_wifi.h
 create mode 100644 drivers/net/wireless/brcm80211/include/chipcommon.h
 create mode 100644 drivers/net/wireless/brcm80211/include/defs.h
 create mode 100644 drivers/net/wireless/brcm80211/include/soc.h

diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f354bd4..0348ab6 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -287,3 +287,4 @@ source "drivers/net/wireless/zd1211rw/Kconfig"
 source "drivers/net/wireless/mwifiex/Kconfig"
 
 endif # WLAN
+source "drivers/net/wireless/brcm80211/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 7bba6a8..dd71dc9 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -58,3 +58,6 @@ obj-$(CONFIG_WL12XX_PLATFORM_DATA)	+= wl12xx/
 obj-$(CONFIG_IWM)	+= iwmc3200wifi/
 
 obj-$(CONFIG_MWIFIEX)	+= mwifiex/
+obj-$(CONFIG_BRCMFMAC) += brcm80211/
+obj-$(CONFIG_BRCMUMAC) += brcm80211/
+obj-$(CONFIG_BRCMSMAC) += brcm80211/
diff --git a/drivers/net/wireless/brcm80211/Kconfig b/drivers/net/wireless/brcm80211/Kconfig
new file mode 100644
index 0000000..379cf16
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/Kconfig
@@ -0,0 +1,40 @@
+config BRCMUTIL
+	tristate
+	default n
+
+config BRCMSMAC
+	tristate "Broadcom IEEE802.11n PCIe SoftMAC WLAN driver"
+	default n
+	depends on PCI
+	depends on WLAN && MAC80211
+	depends on X86 || MIPS
+	select BRCMUTIL
+	select FW_LOADER
+	select CRC_CCITT
+	---help---
+	  This module adds support for PCIe wireless adapters based on Broadcom
+	  IEEE802.11n SoftMAC chipsets.  If you choose to build a module, it'll
+	  be called brcmsmac.ko.
+
+config BRCMFMAC
+	tristate "Broadcom IEEE802.11n embedded FullMAC WLAN driver"
+	default n
+	depends on MMC
+	depends on WLAN && CFG80211
+	depends on X86 || MIPS
+	select BRCMUTIL
+	select FW_LOADER
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	---help---
+	  This module adds support for embedded wireless adapters based on
+	  Broadcom IEEE802.11n FullMAC chipsets.  This driver uses the kernel's
+	  wireless extensions subsystem.  If you choose to build a module,
+	  it'll be called brcmfmac.ko.
+
+config BRCMDBG
+	bool "Broadcom driver debug functions"
+	default n
+	depends on BRCMSMAC || BRCMFMAC
+	---help---
+	  Selecting this enables additional code for debug purposes.
diff --git a/drivers/net/wireless/brcm80211/Makefile b/drivers/net/wireless/brcm80211/Makefile
new file mode 100644
index 0000000..8b01f5e
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/Makefile
@@ -0,0 +1,24 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# common flags
+subdir-ccflags-y					:= -DBCMDMA32
+subdir-ccflags-$(CONFIG_BRCMDBG)	+= -DBCMDBG
+
+obj-$(CONFIG_BRCMUTIL)	+= brcmutil/
+obj-$(CONFIG_BRCMFMAC)	+= brcmfmac/
+obj-$(CONFIG_BRCMSMAC)	+= brcmsmac/
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/Makefile b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
new file mode 100644
index 0000000..6b836e3
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/Makefile
@@ -0,0 +1,39 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y :=			\
+	-DBRCMF_FIRSTREAD=64	\
+	-DBRCMF_SDALIGN=64	\
+	-DMAX_HDR_READ=64
+
+ccflags-$(CONFIG_BRCMDBG)	+= -DSHOW_EVENTS
+
+ccflags-y += \
+	-Idrivers/net/wireless/brcm80211/brcmfmac	\
+	-Idrivers/net/wireless/brcm80211/include
+
+DHDOFILES = \
+	wl_cfg80211.o \
+	dhd_cdc.o \
+	dhd_common.o \
+	dhd_sdio.o	\
+	dhd_linux.o \
+	bcmsdh.o \
+	bcmsdh_sdmmc.o
+
+obj-$(CONFIG_BRCMFMAC) += brcmfmac.o
+brcmfmac-objs += $(DHDOFILES)
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h
new file mode 100644
index 0000000..d7d3afd
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmchip.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _bcmchip_h_
+#define _bcmchip_h_
+
+/* bcm4329 */
+/* SDIO device core, ID 0x829 */
+#define BCM4329_CORE_BUS_BASE		0x18011000
+/* internal memory core, ID 0x80e */
+#define BCM4329_CORE_SOCRAM_BASE	0x18003000
+/* ARM Cortex M3 core, ID 0x82a */
+#define BCM4329_CORE_ARM_BASE		0x18002000
+#define BCM4329_RAMSIZE			0x48000
+/* firmware name */
+#define BCM4329_FW_NAME			"brcm/bcm4329-fullmac-4.bin"
+#define BCM4329_NV_NAME			"brcm/bcm4329-fullmac-4.txt"
+
+#endif				/* _bcmchip_h_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
new file mode 100644
index 0000000..f4e72ed
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/* ****************** SDIO CARD Interface Functions **************************/
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/pci.h>
+#include <linux/pci_ids.h>
+#include <linux/sched.h>
+#include <linux/completion.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include <soc.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "sdio_host.h"
+
+#define SDIOH_API_ACCESS_RETRY_LIMIT	2
+
+#define BRCMF_SD_ERROR_VAL	0x0001	/* Error */
+#define BRCMF_SD_INFO_VAL		0x0002	/* Info */
+
+
+#ifdef BCMDBG
+#define BRCMF_SD_ERROR(x) \
+	do { \
+		if ((brcmf_sdio_msglevel & BRCMF_SD_ERROR_VAL) && \
+		    net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define BRCMF_SD_INFO(x)	\
+	do { \
+		if ((brcmf_sdio_msglevel & BRCMF_SD_INFO_VAL) && \
+		    net_ratelimit()) \
+			printk x; \
+	} while (0)
+#else				/* BCMDBG */
+#define BRCMF_SD_ERROR(x)
+#define BRCMF_SD_INFO(x)
+#endif				/* BCMDBG */
+
+/* debugging macros */
+#define SDLX_MSG(x)
+
+#define SDIOH_CMD_TYPE_NORMAL   0	/* Normal command */
+#define SDIOH_CMD_TYPE_APPEND   1	/* Append command */
+#define SDIOH_CMD_TYPE_CUTTHRU  2	/* Cut-through command */
+
+#define SDIOH_DATA_PIO          0	/* PIO mode */
+#define SDIOH_DATA_DMA          1	/* DMA mode */
+
+struct brcmf_sdio_card {
+	bool init_success;	/* underlying driver successfully attached */
+	void *sdioh;		/* handler for sdioh */
+	u32 vendevid;	/* Target Vendor and Device ID on SD bus */
+	bool regfail;		/* Save status of last
+				 reg_read/reg_write call */
+	u32 sbwad;		/* Save backplane window address */
+};
+
+/**
+ * SDIO Host Controller info
+ */
+struct sdio_hc {
+	struct sdio_hc *next;
+	struct device *dev;	/* platform device handle */
+	void *regs;		/* SDIO Host Controller address */
+	struct brcmf_sdio_card *card;
+	void *ch;
+	unsigned int oob_irq;
+	unsigned long oob_flags;	/* OOB Host specifiction
+					as edge and etc */
+	bool oob_irq_registered;
+};
+
+/* local copy of bcm sd handler */
+static struct brcmf_sdio_card *l_card;
+
+const uint brcmf_sdio_msglevel = BRCMF_SD_ERROR_VAL;
+
+static struct sdio_hc *sdhcinfo;
+
+/* driver info, initialized when brcmf_sdio_register is called */
+static struct brcmf_sdioh_driver drvinfo = { NULL, NULL };
+
+/* Module parameters specific to each host-controller driver */
+
+module_param(sd_msglevel, uint, 0);
+
+extern uint sd_f2_blocksize;
+module_param(sd_f2_blocksize, int, 0);
+
+/* forward declarations */
+int brcmf_sdio_probe(struct device *dev);
+EXPORT_SYMBOL(brcmf_sdio_probe);
+
+int brcmf_sdio_remove(struct device *dev);
+EXPORT_SYMBOL(brcmf_sdio_remove);
+
+struct brcmf_sdio_card*
+brcmf_sdcard_attach(void *cfghdl, u32 *regsva, uint irq)
+{
+	struct brcmf_sdio_card *card;
+
+	card = kzalloc(sizeof(struct brcmf_sdio_card), GFP_ATOMIC);
+	if (card == NULL) {
+		BRCMF_SD_ERROR(("sdcard_attach: out of memory"));
+		return NULL;
+	}
+
+	/* save the handler locally */
+	l_card = card;
+
+	card->sdioh = brcmf_sdioh_attach(cfghdl, irq);
+	if (!card->sdioh) {
+		brcmf_sdcard_detach(card);
+		return NULL;
+	}
+
+	card->init_success = true;
+
+	*regsva = SI_ENUM_BASE;
+
+	/* Report the BAR, to fix if needed */
+	card->sbwad = SI_ENUM_BASE;
+	return card;
+}
+
+int brcmf_sdcard_detach(struct brcmf_sdio_card *card)
+{
+	if (card != NULL) {
+		if (card->sdioh) {
+			brcmf_sdioh_detach(card->sdioh);
+			card->sdioh = NULL;
+		}
+		kfree(card);
+	}
+
+	l_card = NULL;
+	return 0;
+}
+
+int
+brcmf_sdcard_iovar_op(struct brcmf_sdio_card *card, const char *name,
+		void *params, int plen, void *arg, int len, bool set)
+{
+	return brcmf_sdioh_iovar_op(card->sdioh, name, params, plen, arg,
+				    len, set);
+}
+
+int brcmf_sdcard_intr_enable(struct brcmf_sdio_card *card)
+{
+	return brcmf_sdioh_interrupt_set(card->sdioh, true);
+}
+
+int brcmf_sdcard_intr_disable(struct brcmf_sdio_card *card)
+{
+	return brcmf_sdioh_interrupt_set(card->sdioh, false);
+}
+
+int brcmf_sdcard_intr_reg(struct brcmf_sdio_card *card,
+			  void (*fn)(void *), void *argh)
+{
+	return brcmf_sdioh_interrupt_register(card->sdioh, fn, argh);
+}
+
+int brcmf_sdcard_intr_dereg(struct brcmf_sdio_card *card)
+{
+	return brcmf_sdioh_interrupt_deregister(card->sdioh);
+}
+
+u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_card *card, uint fnc_num, u32 addr,
+			 int *err)
+{
+	int status;
+	s32 retry = 0;
+	u8 data = 0;
+
+	if (!card)
+		card = l_card;
+
+	do {
+		if (retry)	/* wait for 1 ms till bus get settled down */
+			udelay(1000);
+		status =
+		    brcmf_sdioh_cfg_read(card->sdioh, fnc_num, addr,
+				   (u8 *) &data);
+	} while (status != 0
+		 && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+	if (err)
+		*err = status;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, u8data = 0x%x\n",
+		     __func__, fnc_num, addr, data));
+
+	return data;
+}
+
+void
+brcmf_sdcard_cfg_write(struct brcmf_sdio_card *card, uint fnc_num, u32 addr,
+		       u8 data, int *err)
+{
+	int status;
+	s32 retry = 0;
+
+	if (!card)
+		card = l_card;
+
+	do {
+		if (retry)	/* wait for 1 ms till bus get settled down */
+			udelay(1000);
+		status =
+		    brcmf_sdioh_cfg_write(card->sdioh, fnc_num, addr,
+				    (u8 *) &data);
+	} while (status != 0
+		 && (retry++ < SDIOH_API_ACCESS_RETRY_LIMIT));
+	if (err)
+		*err = status;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, u8data = 0x%x\n",
+		     __func__, fnc_num, addr, data));
+}
+
+u32 brcmf_sdcard_cfg_read_word(struct brcmf_sdio_card *card, uint fnc_num,
+			       u32 addr, int *err)
+{
+	int status;
+	u32 data = 0;
+
+	if (!card)
+		card = l_card;
+
+	status = brcmf_sdioh_request_word(card->sdioh, SDIOH_CMD_TYPE_NORMAL,
+		SDIOH_READ, fnc_num, addr, &data, 4);
+
+	if (err)
+		*err = status;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, u32data = 0x%x\n",
+		     __func__, fnc_num, addr, data));
+
+	return data;
+}
+
+void
+brcmf_sdcard_cfg_write_word(struct brcmf_sdio_card *card, uint fnc_num,
+			    u32 addr, u32 data, int *err)
+{
+	int status;
+
+	if (!card)
+		card = l_card;
+
+	status =
+	    brcmf_sdioh_request_word(card->sdioh, SDIOH_CMD_TYPE_NORMAL,
+			       SDIOH_WRITE, fnc_num, addr, &data, 4);
+
+	if (err)
+		*err = status;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, u32data = 0x%x\n",
+		     __func__, fnc_num, addr, data));
+}
+
+int brcmf_sdcard_cis_read(struct brcmf_sdio_card *card, uint func, u8 * cis,
+			  uint length)
+{
+	int status;
+
+	u8 *tmp_buf, *tmp_ptr;
+	u8 *ptr;
+	bool ascii = func & ~0xf;
+	func &= 0x7;
+
+	if (!card)
+		card = l_card;
+
+	status = brcmf_sdioh_cis_read(card->sdioh, func, cis, length);
+
+	if (ascii) {
+		/* Move binary bits to tmp and format them
+			 into the provided buffer. */
+		tmp_buf = kmalloc(length, GFP_ATOMIC);
+		if (tmp_buf == NULL) {
+			BRCMF_SD_ERROR(("%s: out of memory\n", __func__));
+			return -ENOMEM;
+		}
+		memcpy(tmp_buf, cis, length);
+		for (tmp_ptr = tmp_buf, ptr = cis; ptr < (cis + length - 4);
+		     tmp_ptr++) {
+			ptr += sprintf((char *)ptr, "%.2x ", *tmp_ptr & 0xff);
+			if ((((tmp_ptr - tmp_buf) + 1) & 0xf) == 0)
+				ptr += sprintf((char *)ptr, "\n");
+		}
+		kfree(tmp_buf);
+	}
+
+	return status;
+}
+
+static int
+brcmf_sdcard_set_sbaddr_window(struct brcmf_sdio_card *card, u32 address)
+{
+	int err = 0;
+	brcmf_sdcard_cfg_write(card, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+			 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+	if (!err)
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_SBADDRMID,
+				       (address >> 16) & SBSDIO_SBADDRMID_MASK,
+				       &err);
+	if (!err)
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_SBADDRHIGH,
+				       (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
+				       &err);
+
+	return err;
+}
+
+u32 brcmf_sdcard_reg_read(struct brcmf_sdio_card *card, u32 addr, uint size)
+{
+	int status;
+	u32 word = 0;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+
+	BRCMF_SD_INFO(("%s:fun = 1, addr = 0x%x, ", __func__, addr));
+
+	if (!card)
+		card = l_card;
+
+	if (bar0 != card->sbwad) {
+		if (brcmf_sdcard_set_sbaddr_window(card, bar0))
+			return 0xFFFFFFFF;
+
+		card->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	if (size == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = brcmf_sdioh_request_word(card->sdioh, SDIOH_CMD_TYPE_NORMAL,
+				    SDIOH_READ, SDIO_FUNC_1, addr, &word, size);
+
+	card->regfail = (status != 0);
+
+	BRCMF_SD_INFO(("u32data = 0x%x\n", word));
+
+	/* if ok, return appropriately masked word */
+	if (status == 0) {
+		switch (size) {
+		case sizeof(u8):
+			return word & 0xff;
+		case sizeof(u16):
+			return word & 0xffff;
+		case sizeof(u32):
+			return word;
+		default:
+			card->regfail = true;
+
+		}
+	}
+
+	/* otherwise, bad sdio access or invalid size */
+	BRCMF_SD_ERROR(("%s: error reading addr 0x%04x size %d\n", __func__,
+		      addr, size));
+	return 0xFFFFFFFF;
+}
+
+u32 brcmf_sdcard_reg_write(struct brcmf_sdio_card *card, u32 addr, uint size,
+			   u32 data)
+{
+	int status;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	BRCMF_SD_INFO(("%s:fun = 1, addr = 0x%x, uint%ddata = 0x%x\n",
+		     __func__, addr, size * 8, data));
+
+	if (!card)
+		card = l_card;
+
+	if (bar0 != card->sbwad) {
+		err = brcmf_sdcard_set_sbaddr_window(card, bar0);
+		if (err)
+			return err;
+
+		card->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	if (size == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+	status =
+	    brcmf_sdioh_request_word(card->sdioh, SDIOH_CMD_TYPE_NORMAL,
+			       SDIOH_WRITE, SDIO_FUNC_1, addr, &data, size);
+	card->regfail = (status != 0);
+
+	if (status == 0)
+		return 0;
+
+	BRCMF_SD_ERROR(("%s: error writing 0x%08x to addr 0x%04x size %d\n",
+		      __func__, data, addr, size));
+	return 0xFFFFFFFF;
+}
+
+bool brcmf_sdcard_regfail(struct brcmf_sdio_card *card)
+{
+	return card->regfail;
+}
+
+int
+brcmf_sdcard_recv_buf(struct brcmf_sdio_card *card, u32 addr, uint fn,
+		      uint flags,
+		      u8 *buf, uint nbytes, struct sk_buff *pkt,
+		      void (*complete)(void *handle, int status,
+				       bool sync_waiting),
+		      void *handle)
+{
+	int status;
+	uint incr_fix;
+	uint width;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+		     __func__, fn, addr, nbytes));
+
+	/* Async not implemented yet */
+	if (flags & SDIO_REQ_ASYNC)
+		return -ENOTSUPP;
+
+	if (bar0 != card->sbwad) {
+		err = brcmf_sdcard_set_sbaddr_window(card, bar0);
+		if (err)
+			return err;
+
+		card->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+	if (width == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	status = brcmf_sdioh_request_buffer(card->sdioh, SDIOH_DATA_PIO,
+		incr_fix, SDIOH_READ, fn, addr, width, nbytes, buf, pkt);
+
+	return status;
+}
+
+int
+brcmf_sdcard_send_buf(struct brcmf_sdio_card *card, u32 addr, uint fn,
+		      uint flags, u8 *buf, uint nbytes, void *pkt,
+		      void (*complete)(void *handle, int status,
+				       bool sync_waiting),
+		      void *handle)
+{
+	uint incr_fix;
+	uint width;
+	uint bar0 = addr & ~SBSDIO_SB_OFT_ADDR_MASK;
+	int err = 0;
+
+	BRCMF_SD_INFO(("%s:fun = %d, addr = 0x%x, size = %d\n",
+		     __func__, fn, addr, nbytes));
+
+	/* Async not implemented yet */
+	if (flags & SDIO_REQ_ASYNC)
+		return -ENOTSUPP;
+
+	if (bar0 != card->sbwad) {
+		err = brcmf_sdcard_set_sbaddr_window(card, bar0);
+		if (err)
+			return err;
+
+		card->sbwad = bar0;
+	}
+
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+
+	incr_fix = (flags & SDIO_REQ_FIXED) ? SDIOH_DATA_FIX : SDIOH_DATA_INC;
+	width = (flags & SDIO_REQ_4BYTE) ? 4 : 2;
+	if (width == 4)
+		addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	return brcmf_sdioh_request_buffer(card->sdioh, SDIOH_DATA_PIO,
+		incr_fix, SDIOH_WRITE, fn, addr, width, nbytes, buf, pkt);
+}
+
+int brcmf_sdcard_rwdata(struct brcmf_sdio_card *card, uint rw, u32 addr,
+			u8 *buf, uint nbytes)
+{
+	addr &= SBSDIO_SB_OFT_ADDR_MASK;
+	addr |= SBSDIO_SB_ACCESS_2_4B_FLAG;
+
+	return brcmf_sdioh_request_buffer(card->sdioh, SDIOH_DATA_PIO,
+		SDIOH_DATA_INC, (rw ? SDIOH_WRITE : SDIOH_READ), SDIO_FUNC_1,
+		addr, 4, nbytes, buf, NULL);
+}
+
+int brcmf_sdcard_abort(struct brcmf_sdio_card *card, uint fn)
+{
+	return brcmf_sdioh_abort(card->sdioh, fn);
+}
+
+int brcmf_sdcard_query_device(struct brcmf_sdio_card *card)
+{
+	card->vendevid = (PCI_VENDOR_ID_BROADCOM << 16) | 0;
+	return card->vendevid;
+}
+
+u32 brcmf_sdcard_cur_sbwad(struct brcmf_sdio_card *card)
+{
+	if (!card)
+		card = l_card;
+
+	return card->sbwad;
+}
+
+int brcmf_sdio_probe(struct device *dev)
+{
+	struct sdio_hc *sdhc = NULL;
+	u32 regs = 0;
+	struct brcmf_sdio_card *card = NULL;
+	int irq = 0;
+	u32 vendevid;
+	unsigned long irq_flags = 0;
+
+	/* allocate SDIO Host Controller state info */
+	sdhc = kzalloc(sizeof(struct sdio_hc), GFP_ATOMIC);
+	if (!sdhc) {
+		SDLX_MSG(("%s: out of memory\n", __func__));
+		goto err;
+	}
+	sdhc->dev = (void *)dev;
+
+	card = brcmf_sdcard_attach((void *)0, &regs, irq);
+	if (!card) {
+		SDLX_MSG(("%s: attach failed\n", __func__));
+		goto err;
+	}
+
+	sdhc->card = card;
+	sdhc->oob_irq = irq;
+	sdhc->oob_flags = irq_flags;
+	sdhc->oob_irq_registered = false;	/* to make sure.. */
+
+	/* chain SDIO Host Controller info together */
+	sdhc->next = sdhcinfo;
+	sdhcinfo = sdhc;
+	/* Read the vendor/device ID from the CIS */
+	vendevid = brcmf_sdcard_query_device(card);
+
+	/* try to attach to the target device */
+	sdhc->ch = drvinfo.attach((vendevid >> 16), (vendevid & 0xFFFF),
+				  0, 0, 0, 0, regs, card);
+	if (!sdhc->ch) {
+		SDLX_MSG(("%s: device attach failed\n", __func__));
+		goto err;
+	}
+
+	return 0;
+
+	/* error handling */
+err:
+	if (sdhc) {
+		if (sdhc->card)
+			brcmf_sdcard_detach(sdhc->card);
+		kfree(sdhc);
+	}
+
+	return -ENODEV;
+}
+
+int brcmf_sdio_remove(struct device *dev)
+{
+	struct sdio_hc *sdhc, *prev;
+
+	sdhc = sdhcinfo;
+	drvinfo.detach(sdhc->ch);
+	brcmf_sdcard_detach(sdhc->card);
+	/* find the SDIO Host Controller state for this pdev
+		 and take it out from the list */
+	for (sdhc = sdhcinfo, prev = NULL; sdhc; sdhc = sdhc->next) {
+		if (sdhc->dev == (void *)dev) {
+			if (prev)
+				prev->next = sdhc->next;
+			else
+				sdhcinfo = NULL;
+			break;
+		}
+		prev = sdhc;
+	}
+	if (!sdhc) {
+		SDLX_MSG(("%s: failed\n", __func__));
+		return 0;
+	}
+
+	/* release SDIO Host Controller info */
+	kfree(sdhc);
+	return 0;
+}
+
+int brcmf_sdio_register(struct brcmf_sdioh_driver *driver)
+{
+	drvinfo = *driver;
+
+	SDLX_MSG(("Linux Kernel SDIO/MMC Driver\n"));
+	return brcmf_sdio_function_init();
+}
+
+void brcmf_sdio_unregister(void)
+{
+	brcmf_sdio_function_cleanup();
+}
+
+void brcmf_sdio_wdtmr_enable(bool enable)
+{
+	if (enable)
+		brcmf_sdbrcm_wd_timer(sdhcinfo->ch, brcmf_watchdog_ms);
+	else
+		brcmf_sdbrcm_wd_timer(sdhcinfo->ch, 0);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
new file mode 100644
index 0000000..38bd9ba
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/bcmsdh_sdmmc.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/core.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/mmc/sdio_ids.h>
+#include <linux/mmc/card.h>
+#include <linux/suspend.h>
+#include <linux/errno.h>
+#include <linux/sched.h>	/* request_irq() */
+#include <net/cfg80211.h>
+
+#include <defs.h>
+#include <brcm_hw_ids.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+#include "sdio_host.h"
+#include "dhd.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+
+#define BLOCK_SIZE_64 64
+#define BLOCK_SIZE_512 512
+#define BLOCK_SIZE_4318 64
+#define BLOCK_SIZE_4328 512
+
+/* private bus modes */
+#define SDIOH_MODE_SD4		2
+
+#define CLIENT_INTR		0x100	/* Get rid of this! */
+
+#if !defined(SDIO_VENDOR_ID_BROADCOM)
+#define SDIO_VENDOR_ID_BROADCOM		0x02d0
+#endif				/* !defined(SDIO_VENDOR_ID_BROADCOM) */
+
+#define SDIO_DEVICE_ID_BROADCOM_DEFAULT	0x0000
+
+#define DMA_ALIGN_MASK	0x03
+
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)
+#define SDIO_DEVICE_ID_BROADCOM_4325_SDGWB	0x0492	/* BCM94325SDGWB */
+#endif		/* !defined(SDIO_DEVICE_ID_BROADCOM_4325_SDGWB) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4325)
+#define SDIO_DEVICE_ID_BROADCOM_4325	0x0493
+#endif		/* !defined(SDIO_DEVICE_ID_BROADCOM_4325) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4329)
+#define SDIO_DEVICE_ID_BROADCOM_4329	0x4329
+#endif		/* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+#if !defined(SDIO_DEVICE_ID_BROADCOM_4319)
+#define SDIO_DEVICE_ID_BROADCOM_4319	0x4319
+#endif		/* !defined(SDIO_DEVICE_ID_BROADCOM_4329) */
+
+/* Common msglevel constants */
+#define SDH_ERROR_VAL		0x0001	/* Error */
+#define SDH_TRACE_VAL		0x0002	/* Trace */
+#define SDH_INFO_VAL		0x0004	/* Info */
+#define SDH_DEBUG_VAL		0x0008	/* Debug */
+#define SDH_DATA_VAL		0x0010	/* Data */
+#define SDH_CTRL_VAL		0x0020	/* Control Regs */
+#define SDH_LOG_VAL		0x0040	/* Enable bcmlog */
+#define SDH_DMA_VAL		0x0080	/* DMA */
+
+#ifdef BCMDBG
+#define sd_err(x)	\
+	do { \
+		if ((sd_msglevel & SDH_ERROR_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define sd_trace(x)	\
+	do { \
+		if ((sd_msglevel & SDH_TRACE_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define sd_info(x)	\
+	do { \
+		if ((sd_msglevel & SDH_INFO_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define sd_debug(x)	\
+	do { \
+		if ((sd_msglevel & SDH_DEBUG_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define sd_data(x)	\
+	do { \
+		if ((sd_msglevel & SDH_DATA_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#define sd_ctrl(x)	\
+	do { \
+		if ((sd_msglevel & SDH_CTRL_VAL) && net_ratelimit()) \
+			printk x; \
+	} while (0)
+#else
+#define sd_err(x)
+#define sd_trace(x)
+#define sd_info(x)
+#define sd_debug(x)
+#define sd_data(x)
+#define sd_ctrl(x)
+#endif
+
+struct sdos_info {
+	struct sdioh_info *sd;
+	spinlock_t lock;
+};
+
+static void brcmf_sdioh_irqhandler(struct sdio_func *func);
+static void brcmf_sdioh_irqhandler_f2(struct sdio_func *func);
+static int brcmf_sdioh_get_cisaddr(struct sdioh_info *sd, u32 regaddr);
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+				const struct sdio_device_id *id);
+static void brcmf_ops_sdio_remove(struct sdio_func *func);
+
+#ifdef CONFIG_PM
+static int brcmf_sdio_suspend(struct device *dev);
+static int brcmf_sdio_resume(struct device *dev);
+#endif /* CONFIG_PM */
+
+uint sd_f2_blocksize = 512;	/* Default blocksize */
+
+uint sd_msglevel = 0x01;
+
+/* module param defaults */
+static int clockoverride;
+
+module_param(clockoverride, int, 0644);
+MODULE_PARM_DESC(clockoverride, "SDIO card clock override");
+
+struct brcmf_sdmmc_instance *gInstance;
+
+struct device sdmmc_dev;
+
+/* devices we support, null terminated */
+static const struct sdio_device_id brcmf_sdmmc_ids[] = {
+	{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_DEFAULT)},
+	{SDIO_DEVICE
+	 (SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325_SDGWB)},
+	{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4325)},
+	{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4329)},
+	{SDIO_DEVICE(SDIO_VENDOR_ID_BROADCOM, SDIO_DEVICE_ID_BROADCOM_4319)},
+	{ /* end: all zeroes */ },
+};
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops brcmf_sdio_pm_ops = {
+	.suspend	= brcmf_sdio_suspend,
+	.resume		= brcmf_sdio_resume,
+};
+#endif	/* CONFIG_PM */
+
+static struct sdio_driver brcmf_sdmmc_driver = {
+	.probe = brcmf_ops_sdio_probe,
+	.remove = brcmf_ops_sdio_remove,
+	.name = "brcmfmac",
+	.id_table = brcmf_sdmmc_ids,
+#ifdef CONFIG_PM
+	.drv = {
+		.pm = &brcmf_sdio_pm_ops,
+	},
+#endif	/* CONFIG_PM */
+};
+
+MODULE_DEVICE_TABLE(sdio, brcmf_sdmmc_ids);
+
+BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_byte_wait);
+BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_word_wait);
+BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_packet_wait);
+BRCMF_PM_RESUME_WAIT_INIT(sdioh_request_buffer_wait);
+
+static int
+brcmf_sdioh_card_regread(struct sdioh_info *sd, int func, u32 regaddr,
+			 int regsize, u32 *data);
+
+static int brcmf_sdioh_enablefuncs(struct sdioh_info *sd)
+{
+	int err_ret;
+	u32 fbraddr;
+	u8 func;
+
+	sd_trace(("%s\n", __func__));
+
+	/* Get the Card's common CIS address */
+	sd->com_cis_ptr = brcmf_sdioh_get_cisaddr(sd, SDIO_CCCR_CIS);
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __func__,
+		 sd->com_cis_ptr));
+
+	/* Get the Card's function CIS (for each function) */
+	for (fbraddr = SDIO_FBR_BASE(1), func = 1;
+	     func <= sd->num_funcs; func++, fbraddr += SDIOD_FBR_SIZE) {
+		sd->func_cis_ptr[func] =
+		    brcmf_sdioh_get_cisaddr(sd, SDIO_FBR_CIS + fbraddr);
+		sd_info(("%s: Function %d CIS Ptr = 0x%x\n", __func__, func,
+			 sd->func_cis_ptr[func]));
+	}
+
+	sd->func_cis_ptr[0] = sd->com_cis_ptr;
+	sd_info(("%s: Card's Common CIS Ptr = 0x%x\n", __func__,
+		 sd->com_cis_ptr));
+
+	/* Enable Function 1 */
+	sdio_claim_host(gInstance->func[1]);
+	err_ret = sdio_enable_func(gInstance->func[1]);
+	sdio_release_host(gInstance->func[1]);
+	if (err_ret) {
+		sd_err(("brcmf_sdioh_enablefuncs: Failed to enable F1 "
+			"Err: 0x%08x\n", err_ret));
+	}
+
+	return false;
+}
+
+/*
+ *	Public entry points & extern's
+ */
+struct sdioh_info *brcmf_sdioh_attach(void *bar0, uint irq)
+{
+	struct sdioh_info *sd;
+	int err_ret;
+
+	sd_trace(("%s\n", __func__));
+
+	if (gInstance == NULL) {
+		sd_err(("%s: SDIO Device not present\n", __func__));
+		return NULL;
+	}
+
+	sd = kzalloc(sizeof(struct sdioh_info), GFP_ATOMIC);
+	if (sd == NULL) {
+		sd_err(("sdioh_attach: out of memory\n"));
+		return NULL;
+	}
+	if (brcmf_sdioh_osinit(sd) != 0) {
+		sd_err(("%s:sdioh_sdmmc_osinit() failed\n", __func__));
+		kfree(sd);
+		return NULL;
+	}
+
+	sd->num_funcs = 2;
+	sd->use_client_ints = true;
+	sd->client_block_size[0] = 64;
+
+	gInstance->sd = sd;
+
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[1]);
+
+	sd->client_block_size[1] = 64;
+	err_ret = sdio_set_block_size(gInstance->func[1], 64);
+	if (err_ret)
+		sd_err(("brcmf_sdioh_attach: Failed to set F1 blocksize\n"));
+
+	/* Release host controller F1 */
+	sdio_release_host(gInstance->func[1]);
+
+	if (gInstance->func[2]) {
+		/* Claim host controller F2 */
+		sdio_claim_host(gInstance->func[2]);
+
+		sd->client_block_size[2] = sd_f2_blocksize;
+		err_ret =
+		    sdio_set_block_size(gInstance->func[2], sd_f2_blocksize);
+		if (err_ret)
+			sd_err(("brcmf_sdioh_attach: Failed to set F2 blocksize"
+				" to %d\n", sd_f2_blocksize));
+
+		/* Release host controller F2 */
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	brcmf_sdioh_enablefuncs(sd);
+
+	sd_trace(("%s: Done\n", __func__));
+	return sd;
+}
+
+extern int brcmf_sdioh_detach(struct sdioh_info *sd)
+{
+	sd_trace(("%s\n", __func__));
+
+	if (sd) {
+
+		/* Disable Function 2 */
+		sdio_claim_host(gInstance->func[2]);
+		sdio_disable_func(gInstance->func[2]);
+		sdio_release_host(gInstance->func[2]);
+
+		/* Disable Function 1 */
+		sdio_claim_host(gInstance->func[1]);
+		sdio_disable_func(gInstance->func[1]);
+		sdio_release_host(gInstance->func[1]);
+
+		/* deregister irq */
+		brcmf_sdioh_osfree(sd);
+
+		kfree(sd);
+	}
+	return 0;
+}
+
+/* Configure callback to client when we receive client interrupt */
+extern int
+brcmf_sdioh_interrupt_register(struct sdioh_info *sd, void (*fn)(void *),
+			       void *argh)
+{
+	sd_trace(("%s: Entering\n", __func__));
+	if (fn == NULL) {
+		sd_err(("%s: interrupt handler is NULL, not registering\n",
+			__func__));
+		return -EINVAL;
+	}
+
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = true;
+
+	/* register and unmask irq */
+	if (gInstance->func[2]) {
+		sdio_claim_host(gInstance->func[2]);
+		sdio_claim_irq(gInstance->func[2], brcmf_sdioh_irqhandler_f2);
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	if (gInstance->func[1]) {
+		sdio_claim_host(gInstance->func[1]);
+		sdio_claim_irq(gInstance->func[1], brcmf_sdioh_irqhandler);
+		sdio_release_host(gInstance->func[1]);
+	}
+
+	return 0;
+}
+
+extern int brcmf_sdioh_interrupt_deregister(struct sdioh_info *sd)
+{
+	sd_trace(("%s: Entering\n", __func__));
+
+	if (gInstance->func[1]) {
+		/* register and unmask irq */
+		sdio_claim_host(gInstance->func[1]);
+		sdio_release_irq(gInstance->func[1]);
+		sdio_release_host(gInstance->func[1]);
+	}
+
+	if (gInstance->func[2]) {
+		/* Claim host controller F2 */
+		sdio_claim_host(gInstance->func[2]);
+		sdio_release_irq(gInstance->func[2]);
+		/* Release host controller F2 */
+		sdio_release_host(gInstance->func[2]);
+	}
+
+	sd->intr_handler_valid = false;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+
+	return 0;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKSIZE,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_DEVREG,
+	IOV_HCIREGS,
+	IOV_RXCHAIN
+};
+
+const struct brcmu_iovar sdioh_iovars[] = {
+	{"sd_msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0},
+	{"sd_blocksize", IOV_BLOCKSIZE, 0, IOVT_UINT32, 0},/* ((fn << 16) |
+								 size) */
+	{"sd_ints", IOV_USEINTS, 0, IOVT_BOOL, 0},
+	{"sd_numints", IOV_NUMINTS, 0, IOVT_UINT32, 0},
+	{"sd_devreg", IOV_DEVREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
+	,
+	{"sd_rxchain", IOV_RXCHAIN, 0, IOVT_BOOL, 0}
+	,
+	{NULL, 0, 0, 0, 0}
+};
+
+int
+brcmf_sdioh_iovar_op(struct sdioh_info *si, const char *name,
+		     void *params, int plen, void *arg, int len, bool set)
+{
+	const struct brcmu_iovar *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	s32 int_val = 0;
+	bool bool_val;
+	u32 actionid;
+
+	if (name == NULL || len <= 0)
+		return -EINVAL;
+
+	/* Set does not take qualifiers */
+	if (set && (params || plen))
+		return -EINVAL;
+
+	/* Get must have return space;*/
+	if (!set && !(arg && len))
+		return -EINVAL;
+
+	sd_trace(("%s: Enter (%s %s)\n", __func__, (set ? "set" : "get"),
+		  name));
+
+	vi = brcmu_iovar_lookup(sdioh_iovars, name);
+	if (vi == NULL) {
+		bcmerror = -ENOTSUPP;
+		goto exit;
+	}
+
+	bcmerror = brcmu_iovar_lencheck(vi, arg, len, set);
+	if (bcmerror != 0)
+		goto exit;
+
+	/* Set up params so get and set can share the convenience variables */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		val_size = sizeof(int);
+
+	if (plen >= (int)sizeof(int_val))
+		memcpy(&int_val, params, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? true : false;
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	switch (actionid) {
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (s32) sd_msglevel;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((u32) int_val > si->num_funcs) {
+			bcmerror = -EINVAL;
+			break;
+		}
+		int_val = (s32) si->client_block_size[int_val];
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_BLOCKSIZE):
+		{
+			uint func = ((u32) int_val >> 16);
+			uint blksize = (u16) int_val;
+			uint maxsize;
+
+			if (func > si->num_funcs) {
+				bcmerror = -EINVAL;
+				break;
+			}
+
+			switch (func) {
+			case 0:
+				maxsize = 32;
+				break;
+			case 1:
+				maxsize = BLOCK_SIZE_4318;
+				break;
+			case 2:
+				maxsize = BLOCK_SIZE_4328;
+				break;
+			default:
+				maxsize = 0;
+			}
+			if (blksize > maxsize) {
+				bcmerror = -EINVAL;
+				break;
+			}
+			if (!blksize)
+				blksize = maxsize;
+
+			/* Now set it */
+			si->client_block_size[func] = blksize;
+
+			break;
+		}
+
+	case IOV_GVAL(IOV_RXCHAIN):
+		int_val = false;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (s32) si->use_client_ints;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		si->use_client_ints = (bool) int_val;
+		if (si->use_client_ints)
+			si->intmask |= CLIENT_INTR;
+		else
+			si->intmask &= ~CLIENT_INTR;
+
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (s32) si->intrcount;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_GVAL(IOV_DEVREG):
+		{
+			struct brcmf_sdreg *sd_ptr =
+					(struct brcmf_sdreg *) params;
+			u8 data = 0;
+
+			if (brcmf_sdioh_cfg_read
+			    (si, sd_ptr->func, sd_ptr->offset, &data)) {
+				bcmerror = -EIO;
+				break;
+			}
+
+			int_val = (int)data;
+			memcpy(arg, &int_val, sizeof(int_val));
+			break;
+		}
+
+	case IOV_SVAL(IOV_DEVREG):
+		{
+			struct brcmf_sdreg *sd_ptr =
+					(struct brcmf_sdreg *) params;
+			u8 data = (u8) sd_ptr->value;
+
+			if (brcmf_sdioh_cfg_write
+			    (si, sd_ptr->func, sd_ptr->offset, &data)) {
+				bcmerror = -EIO;
+				break;
+			}
+			break;
+		}
+
+	default:
+		bcmerror = -ENOTSUPP;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+extern int
+brcmf_sdioh_cfg_read(struct sdioh_info *sd, uint fnc_num, u32 addr, u8 *data)
+{
+	int status;
+	/* No lock needed since brcmf_sdioh_request_byte does locking */
+	status = brcmf_sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern int
+brcmf_sdioh_cfg_write(struct sdioh_info *sd, uint fnc_num, u32 addr, u8 *data)
+{
+	/* No lock needed since brcmf_sdioh_request_byte does locking */
+	int status;
+	status = brcmf_sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+static int brcmf_sdioh_get_cisaddr(struct sdioh_info *sd, u32 regaddr)
+{
+	/* read 24 bits and return valid 17 bit addr */
+	int i;
+	u32 scratch, regdata;
+	u8 *ptr = (u8 *)&scratch;
+	for (i = 0; i < 3; i++) {
+		if ((brcmf_sdioh_card_regread(sd, 0, regaddr, 1, &regdata)) !=
+		    SUCCESS)
+			sd_err(("%s: Can't read!\n", __func__));
+
+		*ptr++ = (u8) regdata;
+		regaddr++;
+	}
+
+	/* Only the lower 17-bits are valid */
+	scratch = le32_to_cpu(scratch);
+	scratch &= 0x0001FFFF;
+	return scratch;
+}
+
+extern int
+brcmf_sdioh_cis_read(struct sdioh_info *sd, uint func, u8 *cisd, u32 length)
+{
+	u32 count;
+	int offset;
+	u32 foo;
+	u8 *cis = cisd;
+
+	sd_trace(("%s: Func = %d\n", __func__, func));
+
+	if (!sd->func_cis_ptr[func]) {
+		memset(cis, 0, length);
+		sd_err(("%s: no func_cis_ptr[%d]\n", __func__, func));
+		return -ENOTSUPP;
+	}
+
+	sd_err(("%s: func_cis_ptr[%d]=0x%04x\n", __func__, func,
+		sd->func_cis_ptr[func]));
+
+	for (count = 0; count < length; count++) {
+		offset = sd->func_cis_ptr[func] + count;
+		if (brcmf_sdioh_card_regread(sd, 0, offset, 1, &foo) < 0) {
+			sd_err(("%s: regread failed: Can't read CIS\n",
+				__func__));
+			return -EIO;
+		}
+
+		*cis = (u8) (foo & 0xff);
+		cis++;
+	}
+
+	return 0;
+}
+
+extern int
+brcmf_sdioh_request_byte(struct sdioh_info *sd, uint rw, uint func,
+			 uint regaddr, u8 *byte)
+{
+	int err_ret;
+
+	sd_info(("%s: rw=%d, func=%d, addr=0x%05x\n", __func__, rw, func,
+		 regaddr));
+
+	BRCMF_PM_RESUME_WAIT(sdioh_request_byte_wait);
+	BRCMF_PM_RESUME_RETURN_ERROR(-EIO);
+	if (rw) {		/* CMD52 Write */
+		if (func == 0) {
+			/* Can only directly write to some F0 registers.
+			 * Handle F2 enable
+			 * as a special case.
+			 */
+			if (regaddr == SDIO_CCCR_IOEx) {
+				if (gInstance->func[2]) {
+					sdio_claim_host(gInstance->func[2]);
+					if (*byte & SDIO_FUNC_ENABLE_2) {
+						/* Enable Function 2 */
+						err_ret =
+						    sdio_enable_func
+						    (gInstance->func[2]);
+						if (err_ret)
+							sd_err(("request_byte: "
+								"enable F2 "
+								"failed:%d\n",
+								 err_ret));
+					} else {
+						/* Disable Function 2 */
+						err_ret =
+						    sdio_disable_func
+						    (gInstance->func[2]);
+						if (err_ret)
+							sd_err(("request_byte: "
+								"Disab F2 "
+								"failed:%d\n",
+								 err_ret));
+					}
+					sdio_release_host(gInstance->func[2]);
+				}
+			}
+			/* to allow abort command through F1 */
+			else if (regaddr == SDIO_CCCR_ABORT) {
+				sdio_claim_host(gInstance->func[func]);
+				/*
+				 * this sdio_f0_writeb() can be replaced
+				 * with another api
+				 * depending upon MMC driver change.
+				 * As of this time, this is temporaray one
+				 */
+				sdio_writeb(gInstance->func[func], *byte,
+					    regaddr, &err_ret);
+				sdio_release_host(gInstance->func[func]);
+			} else if (regaddr < 0xF0) {
+				sd_err(("brcmf: F0 Wr:0x%02x: write "
+					"disallowed\n", regaddr));
+			} else {
+				/* Claim host controller, perform F0 write,
+				 and release */
+				sdio_claim_host(gInstance->func[func]);
+				sdio_f0_writeb(gInstance->func[func], *byte,
+					       regaddr, &err_ret);
+				sdio_release_host(gInstance->func[func]);
+			}
+		} else {
+			/* Claim host controller, perform Fn write,
+			 and release */
+			sdio_claim_host(gInstance->func[func]);
+			sdio_writeb(gInstance->func[func], *byte, regaddr,
+				    &err_ret);
+			sdio_release_host(gInstance->func[func]);
+		}
+	} else {		/* CMD52 Read */
+		/* Claim host controller, perform Fn read, and release */
+		sdio_claim_host(gInstance->func[func]);
+
+		if (func == 0) {
+			*byte =
+			    sdio_f0_readb(gInstance->func[func], regaddr,
+					  &err_ret);
+		} else {
+			*byte =
+			    sdio_readb(gInstance->func[func], regaddr,
+				       &err_ret);
+		}
+
+		sdio_release_host(gInstance->func[func]);
+	}
+
+	if (err_ret)
+		sd_err(("brcmf: Failed to %s byte F%d:@0x%05x=%02x, "
+			"Err: %d\n", rw ? "Write" : "Read", func, regaddr,
+			*byte, err_ret));
+
+	return err_ret;
+}
+
+extern int
+brcmf_sdioh_request_word(struct sdioh_info *sd, uint cmd_type, uint rw,
+			 uint func, uint addr, u32 *word, uint nbytes)
+{
+	int err_ret = -EIO;
+
+	if (func == 0) {
+		sd_err(("%s: Only CMD52 allowed to F0.\n", __func__));
+		return -EINVAL;
+	}
+
+	sd_info(("%s: cmd_type=%d, rw=%d, func=%d, addr=0x%05x, nbytes=%d\n",
+		 __func__, cmd_type, rw, func, addr, nbytes));
+
+	BRCMF_PM_RESUME_WAIT(sdioh_request_word_wait);
+	BRCMF_PM_RESUME_RETURN_ERROR(-EIO);
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[func]);
+
+	if (rw) {		/* CMD52 Write */
+		if (nbytes == 4) {
+			sdio_writel(gInstance->func[func], *word, addr,
+				    &err_ret);
+		} else if (nbytes == 2) {
+			sdio_writew(gInstance->func[func], (*word & 0xFFFF),
+				    addr, &err_ret);
+		} else {
+			sd_err(("%s: Invalid nbytes: %d\n", __func__, nbytes));
+		}
+	} else {		/* CMD52 Read */
+		if (nbytes == 4) {
+			*word =
+			    sdio_readl(gInstance->func[func], addr, &err_ret);
+		} else if (nbytes == 2) {
+			*word =
+			    sdio_readw(gInstance->func[func], addr,
+				       &err_ret) & 0xFFFF;
+		} else {
+			sd_err(("%s: Invalid nbytes: %d\n", __func__, nbytes));
+		}
+	}
+
+	/* Release host controller */
+	sdio_release_host(gInstance->func[func]);
+
+	if (err_ret) {
+		sd_err(("brcmf: Failed to %s word, Err: 0x%08x\n",
+			rw ? "Write" : "Read", err_ret));
+	}
+
+	return err_ret;
+}
+
+static int
+brcmf_sdioh_request_packet(struct sdioh_info *sd, uint fix_inc, uint write,
+			   uint func, uint addr, struct sk_buff *pkt)
+{
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+	u32 SGCount = 0;
+	int err_ret = 0;
+
+	struct sk_buff *pnext;
+
+	sd_trace(("%s: Enter\n", __func__));
+
+	BRCMF_PM_RESUME_WAIT(sdioh_request_packet_wait);
+	BRCMF_PM_RESUME_RETURN_ERROR(-EIO);
+
+	/* Claim host controller */
+	sdio_claim_host(gInstance->func[func]);
+	for (pnext = pkt; pnext; pnext = pnext->next) {
+		uint pkt_len = pnext->len;
+		pkt_len += 3;
+		pkt_len &= 0xFFFFFFFC;
+
+		if ((write) && (!fifo)) {
+			err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+						   ((u8 *) (pnext->data)),
+						   pkt_len);
+		} else if (write) {
+			err_ret = sdio_memcpy_toio(gInstance->func[func], addr,
+						   ((u8 *) (pnext->data)),
+						   pkt_len);
+		} else if (fifo) {
+			err_ret = sdio_readsb(gInstance->func[func],
+					      ((u8 *) (pnext->data)),
+					      addr, pkt_len);
+		} else {
+			err_ret = sdio_memcpy_fromio(gInstance->func[func],
+						     ((u8 *) (pnext->data)),
+						     addr, pkt_len);
+		}
+
+		if (err_ret) {
+			sd_err(("%s: %s FAILED %p[%d], addr=0x%05x, pkt_len=%d,"
+				 "ERR=0x%08x\n", __func__,
+				 (write) ? "TX" : "RX",
+				 pnext, SGCount, addr, pkt_len, err_ret));
+		} else {
+			sd_trace(("%s: %s xfr'd %p[%d], addr=0x%05x, len=%d\n",
+				  __func__,
+				  (write) ? "TX" : "RX",
+				  pnext, SGCount, addr, pkt_len));
+		}
+
+		if (!fifo)
+			addr += pkt_len;
+		SGCount++;
+
+	}
+
+	/* Release host controller */
+	sdio_release_host(gInstance->func[func]);
+
+	sd_trace(("%s: Exit\n", __func__));
+	return err_ret;
+}
+
+/*
+ * This function takes a buffer or packet, and fixes everything up
+ * so that in the end, a DMA-able packet is created.
+ *
+ * A buffer does not have an associated packet pointer,
+ * and may or may not be aligned.
+ * A packet may consist of a single packet, or a packet chain.
+ * If it is a packet chain, then all the packets in the chain
+ * must be properly aligned.
+ *
+ * If the packet data is not aligned, then there may only be
+ * one packet, and in this case,  it is copied to a new
+ * aligned packet.
+ *
+ */
+extern int
+brcmf_sdioh_request_buffer(struct sdioh_info *sd, uint pio_dma, uint fix_inc,
+			   uint write, uint func, uint addr, uint reg_width,
+			   uint buflen_u, u8 *buffer, struct sk_buff *pkt)
+{
+	int Status;
+	struct sk_buff *mypkt = NULL;
+
+	sd_trace(("%s: Enter\n", __func__));
+
+	BRCMF_PM_RESUME_WAIT(sdioh_request_buffer_wait);
+	BRCMF_PM_RESUME_RETURN_ERROR(-EIO);
+	/* Case 1: we don't have a packet. */
+	if (pkt == NULL) {
+		sd_data(("%s: Creating new %s Packet, len=%d\n",
+			 __func__, write ? "TX" : "RX", buflen_u));
+		mypkt = brcmu_pkt_buf_get_skb(buflen_u);
+		if (!mypkt) {
+			sd_err(("%s: brcmu_pkt_buf_get_skb failed: len %d\n",
+				__func__, buflen_u));
+			return -EIO;
+		}
+
+		/* For a write, copy the buffer data into the packet. */
+		if (write)
+			memcpy(mypkt->data, buffer, buflen_u);
+
+		Status = brcmf_sdioh_request_packet(sd, fix_inc, write, func,
+						    addr, mypkt);
+
+		/* For a read, copy the packet data back to the buffer. */
+		if (!write)
+			memcpy(buffer, mypkt->data, buflen_u);
+
+		brcmu_pkt_buf_free_skb(mypkt);
+	} else if (((ulong) (pkt->data) & DMA_ALIGN_MASK) != 0) {
+		/*
+		 * Case 2: We have a packet, but it is unaligned.
+		 * In this case, we cannot have a chain (pkt->next == NULL)
+		 */
+		sd_data(("%s: Creating aligned %s Packet, len=%d\n",
+			 __func__, write ? "TX" : "RX", pkt->len));
+		mypkt = brcmu_pkt_buf_get_skb(pkt->len);
+		if (!mypkt) {
+			sd_err(("%s: brcmu_pkt_buf_get_skb failed: len %d\n",
+				__func__, pkt->len));
+			return -EIO;
+		}
+
+		/* For a write, copy the buffer data into the packet. */
+		if (write)
+			memcpy(mypkt->data, pkt->data, pkt->len);
+
+		Status = brcmf_sdioh_request_packet(sd, fix_inc, write, func,
+						    addr, mypkt);
+
+		/* For a read, copy the packet data back to the buffer. */
+		if (!write)
+			memcpy(pkt->data, mypkt->data, mypkt->len);
+
+		brcmu_pkt_buf_free_skb(mypkt);
+	} else {		/* case 3: We have a packet and
+				 it is aligned. */
+		sd_data(("%s: Aligned %s Packet, direct DMA\n",
+			 __func__, write ? "Tx" : "Rx"));
+		Status = brcmf_sdioh_request_packet(sd, fix_inc, write, func,
+						    addr, pkt);
+	}
+
+	return Status;
+}
+
+/* this function performs "abort" for both of host & device */
+extern int brcmf_sdioh_abort(struct sdioh_info *sd, uint func)
+{
+	char t_func = (char)func;
+	sd_trace(("%s: Enter\n", __func__));
+
+	/* issue abort cmd52 command through F0 */
+	brcmf_sdioh_request_byte(sd, SDIOH_WRITE, SDIO_FUNC_0, SDIO_CCCR_ABORT,
+			   &t_func);
+
+	sd_trace(("%s: Exit\n", __func__));
+	return 0;
+}
+
+/* Disable device interrupt */
+void brcmf_sdioh_dev_intr_off(struct sdioh_info *sd)
+{
+	sd_trace(("%s: %d\n", __func__, sd->use_client_ints));
+	sd->intmask &= ~CLIENT_INTR;
+}
+
+/* Enable device interrupt */
+void brcmf_sdioh_dev_intr_on(struct sdioh_info *sd)
+{
+	sd_trace(("%s: %d\n", __func__, sd->use_client_ints));
+	sd->intmask |= CLIENT_INTR;
+}
+
+/* Read client card reg */
+int
+brcmf_sdioh_card_regread(struct sdioh_info *sd, int func, u32 regaddr,
+			 int regsize, u32 *data)
+{
+
+	if ((func == 0) || (regsize == 1)) {
+		u8 temp = 0;
+
+		brcmf_sdioh_request_byte(sd, SDIOH_READ, func, regaddr, &temp);
+		*data = temp;
+		*data &= 0xff;
+		sd_data(("%s: byte read data=0x%02x\n", __func__, *data));
+	} else {
+		brcmf_sdioh_request_word(sd, 0, SDIOH_READ, func, regaddr, data,
+				   regsize);
+		if (regsize == 2)
+			*data &= 0xffff;
+
+		sd_data(("%s: word read data=0x%08x\n", __func__, *data));
+	}
+
+	return SUCCESS;
+}
+
+static void brcmf_sdioh_irqhandler(struct sdio_func *func)
+{
+	struct sdioh_info *sd;
+
+	sd_trace(("brcmf: ***IRQHandler\n"));
+	sd = gInstance->sd;
+
+	sdio_release_host(gInstance->func[0]);
+
+	if (sd->use_client_ints) {
+		sd->intrcount++;
+		(sd->intr_handler) (sd->intr_handler_arg);
+	} else {
+		sd_err(("brcmf: ***IRQHandler\n"));
+
+		sd_err(("%s: Not ready for intr: enabled %d, handler %p\n",
+			__func__, sd->client_intr_enabled, sd->intr_handler));
+	}
+
+	sdio_claim_host(gInstance->func[0]);
+}
+
+/* interrupt handler for F2 (dummy handler) */
+static void brcmf_sdioh_irqhandler_f2(struct sdio_func *func)
+{
+	struct sdioh_info *sd;
+
+	sd_trace(("brcmf: ***IRQHandlerF2\n"));
+
+	sd = gInstance->sd;
+}
+
+static int brcmf_ops_sdio_probe(struct sdio_func *func,
+			      const struct sdio_device_id *id)
+{
+	int ret = 0;
+	static struct sdio_func sdio_func_0;
+	sd_trace(("sdio_probe: %s Enter\n", __func__));
+	sd_trace(("sdio_probe: func->class=%x\n", func->class));
+	sd_trace(("sdio_vendor: 0x%04x\n", func->vendor));
+	sd_trace(("sdio_device: 0x%04x\n", func->device));
+	sd_trace(("Function#: 0x%04x\n", func->num));
+
+	if (func->num == 1) {
+		sdio_func_0.num = 0;
+		sdio_func_0.card = func->card;
+		gInstance->func[0] = &sdio_func_0;
+		if (func->device == 0x4) {	/* 4318 */
+			gInstance->func[2] = NULL;
+			sd_trace(("NIC found, calling brcmf_sdio_probe...\n"));
+			ret = brcmf_sdio_probe(&sdmmc_dev);
+		}
+	}
+
+	gInstance->func[func->num] = func;
+
+	if (func->num == 2) {
+		brcmf_cfg80211_sdio_func(func);
+		sd_trace(("F2 found, calling brcmf_sdio_probe...\n"));
+		ret = brcmf_sdio_probe(&sdmmc_dev);
+	}
+
+	return ret;
+}
+
+static void brcmf_ops_sdio_remove(struct sdio_func *func)
+{
+	sd_trace(("%s Enter\n", __func__));
+	sd_info(("func->class=%x\n", func->class));
+	sd_info(("sdio_vendor: 0x%04x\n", func->vendor));
+	sd_info(("sdio_device: 0x%04x\n", func->device));
+	sd_info(("Function#: 0x%04x\n", func->num));
+
+	if (func->num == 2) {
+		sd_trace(("F2 found, calling brcmf_sdio_remove...\n"));
+		brcmf_sdio_remove(&sdmmc_dev);
+	}
+}
+
+
+#ifdef CONFIG_PM
+static int brcmf_sdio_suspend(struct device *dev)
+{
+	mmc_pm_flag_t sdio_flags;
+	int ret = 0;
+
+	sd_trace(("%s\n", __func__));
+
+	sdio_flags = sdio_get_host_pm_caps(gInstance->func[1]);
+	if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
+		sd_err(("Host can't keep power while suspended\n"));
+		return -EINVAL;
+	}
+
+	ret = sdio_set_host_pm_flags(gInstance->func[1], MMC_PM_KEEP_POWER);
+	if (ret) {
+		sd_err(("Failed to set pm_flags\n"));
+		return ret;
+	}
+
+	brcmf_sdio_wdtmr_enable(false);
+
+	return ret;
+}
+
+static int brcmf_sdio_resume(struct device *dev)
+{
+	brcmf_sdio_wdtmr_enable(true);
+	return 0;
+}
+#endif		/* CONFIG_PM */
+
+int brcmf_sdioh_osinit(struct sdioh_info *sd)
+{
+	struct sdos_info *sdos;
+
+	sdos = kmalloc(sizeof(struct sdos_info), GFP_ATOMIC);
+	sd->sdos_info = (void *)sdos;
+	if (sdos == NULL)
+		return -ENOMEM;
+
+	sdos->sd = sd;
+	spin_lock_init(&sdos->lock);
+	return 0;
+}
+
+void brcmf_sdioh_osfree(struct sdioh_info *sd)
+{
+	struct sdos_info *sdos;
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+	kfree(sdos);
+}
+
+/* Interrupt enable/disable */
+int brcmf_sdioh_interrupt_set(struct sdioh_info *sd, bool enable)
+{
+	unsigned long flags;
+	struct sdos_info *sdos;
+
+	sd_trace(("%s: %s\n", __func__, enable ? "Enabling" : "Disabling"));
+
+	sdos = (struct sdos_info *)sd->sdos_info;
+
+	if (enable && !(sd->intr_handler && sd->intr_handler_arg)) {
+		sd_err(("%s: no handler registered, will not enable\n",
+			__func__));
+		return -EINVAL;
+	}
+
+	/* Ensure atomicity for enable/disable calls */
+	spin_lock_irqsave(&sdos->lock, flags);
+
+	sd->client_intr_enabled = enable;
+	if (enable)
+		brcmf_sdioh_dev_intr_on(sd);
+	else
+		brcmf_sdioh_dev_intr_off(sd);
+
+	spin_unlock_irqrestore(&sdos->lock, flags);
+
+	return 0;
+}
+
+/*
+ * module init
+*/
+int brcmf_sdio_function_init(void)
+{
+	int error = 0;
+	sd_trace(("brcmf_sdio_function_init: %s Enter\n", __func__));
+
+	gInstance = kzalloc(sizeof(struct brcmf_sdmmc_instance), GFP_KERNEL);
+	if (!gInstance)
+		return -ENOMEM;
+
+	memset(&sdmmc_dev, 0, sizeof(sdmmc_dev));
+	error = sdio_register_driver(&brcmf_sdmmc_driver);
+
+	return error;
+}
+
+/*
+ * module cleanup
+*/
+void brcmf_sdio_function_cleanup(void)
+{
+	sd_trace(("%s Enter\n", __func__));
+
+	sdio_unregister_driver(&brcmf_sdmmc_driver);
+
+	kfree(gInstance);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
new file mode 100644
index 0000000..82bf04d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd.h
@@ -0,0 +1,904 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/****************
+ * Common types *
+ */
+
+#ifndef _BRCMF_H_
+#define _BRCMF_H_
+
+#define BRCMF_VERSION_STR		"4.218.248.5"
+
+#define	BRCMF_C_IOCTL_SMLEN	256	/* "small" ioctl buffer required */
+#define BRCMF_C_IOCTL_MEDLEN	1536	/* "med" ioctl buffer required */
+#define	BRCMF_C_IOCTL_MAXLEN	8192
+
+#define BRCMF_C_UP				2
+#define BRCMF_C_SET_PROMISC			10
+#define BRCMF_C_GET_RATE			12
+#define BRCMF_C_GET_INFRA			19
+#define BRCMF_C_SET_INFRA			20
+#define BRCMF_C_GET_AUTH			21
+#define BRCMF_C_SET_AUTH			22
+#define BRCMF_C_GET_BSSID			23
+#define BRCMF_C_GET_SSID			25
+#define BRCMF_C_SET_SSID			26
+#define BRCMF_C_GET_CHANNEL			29
+#define BRCMF_C_GET_SRL				31
+#define BRCMF_C_GET_LRL				33
+#define BRCMF_C_GET_RADIO			37
+#define BRCMF_C_SET_RADIO			38
+#define BRCMF_C_GET_PHYTYPE			39
+#define BRCMF_C_SET_KEY				45
+#define BRCMF_C_SET_PASSIVE_SCAN		49
+#define BRCMF_C_SCAN				50
+#define BRCMF_C_SCAN_RESULTS			51
+#define BRCMF_C_DISASSOC			52
+#define BRCMF_C_REASSOC				53
+#define BRCMF_C_SET_ROAM_TRIGGER		55
+#define BRCMF_C_SET_ROAM_DELTA			57
+#define BRCMF_C_GET_DTIMPRD			77
+#define BRCMF_C_SET_COUNTRY			84
+#define BRCMF_C_GET_PM				85
+#define BRCMF_C_SET_PM				86
+#define BRCMF_C_GET_AP				117
+#define BRCMF_C_SET_AP				118
+#define BRCMF_C_GET_RSSI			127
+#define BRCMF_C_GET_WSEC			133
+#define BRCMF_C_SET_WSEC			134
+#define BRCMF_C_GET_PHY_NOISE			135
+#define BRCMF_C_GET_BSS_INFO			136
+#define BRCMF_C_SET_SCAN_CHANNEL_TIME		185
+#define BRCMF_C_SET_SCAN_UNASSOC_TIME		187
+#define BRCMF_C_SCB_DEAUTHENTICATE_FOR_REASON	201
+#define BRCMF_C_GET_VALID_CHANNELS		217
+#define BRCMF_C_GET_KEY_PRIMARY			235
+#define BRCMF_C_SET_KEY_PRIMARY			236
+#define BRCMF_C_SET_SCAN_PASSIVE_TIME		258
+#define BRCMF_C_GET_VAR				262
+#define BRCMF_C_SET_VAR				263
+
+/* phy types (returned by WLC_GET_PHYTPE) */
+#define	WLC_PHY_TYPE_A		0
+#define	WLC_PHY_TYPE_B		1
+#define	WLC_PHY_TYPE_G		2
+#define	WLC_PHY_TYPE_N		4
+#define	WLC_PHY_TYPE_LP		5
+#define	WLC_PHY_TYPE_SSN	6
+#define	WLC_PHY_TYPE_HT		7
+#define	WLC_PHY_TYPE_LCN	8
+#define	WLC_PHY_TYPE_NULL	0xf
+
+#define BRCMF_PKT_FILTER_FIXED_LEN	offsetof(struct brcmf_pkt_filter, u)
+#define BRCMF_PKT_FILTER_PATTERN_FIXED_LEN	\
+	offsetof(struct brcmf_pkt_filter_pattern, mask_and_pattern)
+
+#define BRCMF_EVENTING_MASK_LEN	16
+
+#define TOE_TX_CSUM_OL		0x00000001
+#define TOE_RX_CSUM_OL		0x00000002
+
+/* maximum channels returned by the get valid channels iovar */
+#define WL_NUMCHANNELS		64
+
+#define	BRCMF_BSS_INFO_VERSION	108 /* current ver of brcmf_bss_info struct */
+
+/* size of brcmf_scan_params not including variable length array */
+#define BRCMF_SCAN_PARAMS_FIXED_SIZE 64
+
+/* masks for channel and ssid count */
+#define BRCMF_SCAN_PARAMS_COUNT_MASK 0x0000ffff
+#define BRCMF_SCAN_PARAMS_NSSID_SHIFT 16
+
+#define BRCMF_SCAN_ACTION_START      1
+#define BRCMF_SCAN_ACTION_CONTINUE   2
+#define WL_SCAN_ACTION_ABORT      3
+
+#define BRCMF_ISCAN_REQ_VERSION 1
+
+/* brcmf_iscan_results status values */
+#define BRCMF_SCAN_RESULTS_SUCCESS	0
+#define BRCMF_SCAN_RESULTS_PARTIAL	1
+#define BRCMF_SCAN_RESULTS_PENDING	2
+#define BRCMF_SCAN_RESULTS_ABORTED	3
+#define BRCMF_SCAN_RESULTS_NO_MEM	4
+
+#define WL_SOFT_KEY	(1 << 0)	/* Indicates this key is using soft encrypt */
+#define BRCMF_PRIMARY_KEY	(1 << 1)	/* primary (ie tx) key */
+#define WL_KF_RES_4	(1 << 4)	/* Reserved for backward compat */
+#define WL_KF_RES_5	(1 << 5)	/* Reserved for backward compat */
+#define WL_IBSS_PEER_GROUP_KEY	(1 << 6)	/* Indicates a group key for a IBSS PEER */
+
+/* For supporting multiple interfaces */
+#define BRCMF_MAX_IFS	16
+#define BRCMF_DEL_IF	-0xe
+#define BRCMF_BAD_IF	-0xf
+
+#define DOT11_BSSTYPE_ANY			2
+#define DOT11_MAX_DEFAULT_KEYS	4
+
+#define BRCMF_EVENT_MSG_LINK		0x01
+#define BRCMF_EVENT_MSG_FLUSHTXQ	0x02
+#define BRCMF_EVENT_MSG_GROUP		0x04
+
+struct brcmf_event_msg {
+	u16 version;
+	u16 flags;
+	u32 event_type;
+	u32 status;
+	u32 reason;
+	u32 auth_type;
+	u32 datalen;
+	u8 addr[ETH_ALEN];
+	char ifname[IFNAMSIZ];
+} __packed;
+
+struct brcm_ethhdr {
+	u16 subtype;
+	u16 length;
+	u8 version;
+	u8 oui[3];
+	u16 usr_subtype;
+} __packed;
+
+struct brcmf_event {
+	struct ethhdr eth;
+	struct brcm_ethhdr hdr;
+	struct brcmf_event_msg msg;
+} __packed;
+
+struct dngl_stats {
+	unsigned long rx_packets;	/* total packets received */
+	unsigned long tx_packets;	/* total packets transmitted */
+	unsigned long rx_bytes;	/* total bytes received */
+	unsigned long tx_bytes;	/* total bytes transmitted */
+	unsigned long rx_errors;	/* bad packets received */
+	unsigned long tx_errors;	/* packet transmit problems */
+	unsigned long rx_dropped;	/* packets dropped by dongle */
+	unsigned long tx_dropped;	/* packets dropped by dongle */
+	unsigned long multicast;	/* multicast packets received */
+};
+
+#define BRCMF_E_SET_SSID			0
+#define BRCMF_E_JOIN				1
+#define BRCMF_E_START				2
+#define BRCMF_E_AUTH				3
+#define BRCMF_E_AUTH_IND			4
+#define BRCMF_E_DEAUTH				5
+#define BRCMF_E_DEAUTH_IND			6
+#define BRCMF_E_ASSOC				7
+#define BRCMF_E_ASSOC_IND			8
+#define BRCMF_E_REASSOC				9
+#define BRCMF_E_REASSOC_IND			10
+#define BRCMF_E_DISASSOC			11
+#define BRCMF_E_DISASSOC_IND			12
+#define BRCMF_E_QUIET_START			13
+#define BRCMF_E_QUIET_END			14
+#define BRCMF_E_BEACON_RX			15
+#define BRCMF_E_LINK				16
+#define BRCMF_E_MIC_ERROR			17
+#define BRCMF_E_NDIS_LINK			18
+#define BRCMF_E_ROAM				19
+#define BRCMF_E_TXFAIL				20
+#define BRCMF_E_PMKID_CACHE			21
+#define BRCMF_E_RETROGRADE_TSF			22
+#define BRCMF_E_PRUNE				23
+#define BRCMF_E_AUTOAUTH			24
+#define BRCMF_E_EAPOL_MSG			25
+#define BRCMF_E_SCAN_COMPLETE			26
+#define BRCMF_E_ADDTS_IND			27
+#define BRCMF_E_DELTS_IND			28
+#define BRCMF_E_BCNSENT_IND			29
+#define BRCMF_E_BCNRX_MSG			30
+#define BRCMF_E_BCNLOST_MSG			31
+#define BRCMF_E_ROAM_PREP			32
+#define BRCMF_E_PFN_NET_FOUND			33
+#define BRCMF_E_PFN_NET_LOST			34
+#define BRCMF_E_RESET_COMPLETE			35
+#define BRCMF_E_JOIN_START			36
+#define BRCMF_E_ROAM_START			37
+#define BRCMF_E_ASSOC_START			38
+#define BRCMF_E_IBSS_ASSOC			39
+#define BRCMF_E_RADIO				40
+#define BRCMF_E_PSM_WATCHDOG			41
+#define BRCMF_E_PROBREQ_MSG			44
+#define BRCMF_E_SCAN_CONFIRM_IND		45
+#define BRCMF_E_PSK_SUP				46
+#define BRCMF_E_COUNTRY_CODE_CHANGED		47
+#define	BRCMF_E_EXCEEDED_MEDIUM_TIME		48
+#define BRCMF_E_ICV_ERROR			49
+#define BRCMF_E_UNICAST_DECODE_ERROR		50
+#define BRCMF_E_MULTICAST_DECODE_ERROR		51
+#define BRCMF_E_TRACE				52
+#define BRCMF_E_IF				54
+#define BRCMF_E_RSSI				56
+#define BRCMF_E_PFN_SCAN_COMPLETE		57
+#define BRCMF_E_EXTLOG_MSG			58
+#define BRCMF_E_ACTION_FRAME			59
+#define BRCMF_E_ACTION_FRAME_COMPLETE		60
+#define BRCMF_E_PRE_ASSOC_IND			61
+#define BRCMF_E_PRE_REASSOC_IND			62
+#define BRCMF_E_CHANNEL_ADOPTED			63
+#define BRCMF_E_AP_STARTED			64
+#define BRCMF_E_DFS_AP_STOP			65
+#define BRCMF_E_DFS_AP_RESUME			66
+#define BRCMF_E_RESERVED1			67
+#define BRCMF_E_RESERVED2			68
+#define BRCMF_E_ESCAN_RESULT			69
+#define BRCMF_E_ACTION_FRAME_OFF_CHAN_COMPLETE	70
+#define BRCMF_E_DCS_REQUEST			73
+
+#define BRCMF_E_FIFO_CREDIT_MAP			74
+
+#define BRCMF_E_LAST				75
+
+#define BRCMF_E_STATUS_SUCCESS			0
+#define BRCMF_E_STATUS_FAIL			1
+#define BRCMF_E_STATUS_TIMEOUT			2
+#define BRCMF_E_STATUS_NO_NETWORKS		3
+#define BRCMF_E_STATUS_ABORT			4
+#define BRCMF_E_STATUS_NO_ACK			5
+#define BRCMF_E_STATUS_UNSOLICITED		6
+#define BRCMF_E_STATUS_ATTEMPT			7
+#define BRCMF_E_STATUS_PARTIAL			8
+#define BRCMF_E_STATUS_NEWSCAN			9
+#define BRCMF_E_STATUS_NEWASSOC			10
+#define BRCMF_E_STATUS_11HQUIET			11
+#define BRCMF_E_STATUS_SUPPRESS			12
+#define BRCMF_E_STATUS_NOCHANS			13
+#define BRCMF_E_STATUS_CS_ABORT			15
+#define BRCMF_E_STATUS_ERROR			16
+
+#define BRCMF_E_REASON_INITIAL_ASSOC		0
+#define BRCMF_E_REASON_LOW_RSSI			1
+#define BRCMF_E_REASON_DEAUTH			2
+#define BRCMF_E_REASON_DISASSOC			3
+#define BRCMF_E_REASON_BCNS_LOST		4
+#define BRCMF_E_REASON_MINTXRATE		9
+#define BRCMF_E_REASON_TXFAIL			10
+
+#define BRCMF_E_REASON_FAST_ROAM_FAILED		5
+#define BRCMF_E_REASON_DIRECTED_ROAM		6
+#define BRCMF_E_REASON_TSPEC_REJECTED		7
+#define BRCMF_E_REASON_BETTER_AP		8
+
+#define BRCMF_E_PRUNE_ENCR_MISMATCH		1
+#define BRCMF_E_PRUNE_BCAST_BSSID		2
+#define BRCMF_E_PRUNE_MAC_DENY			3
+#define BRCMF_E_PRUNE_MAC_NA			4
+#define BRCMF_E_PRUNE_REG_PASSV			5
+#define BRCMF_E_PRUNE_SPCT_MGMT			6
+#define BRCMF_E_PRUNE_RADAR			7
+#define BRCMF_E_RSN_MISMATCH			8
+#define BRCMF_E_PRUNE_NO_COMMON_RATES		9
+#define BRCMF_E_PRUNE_BASIC_RATES		10
+#define BRCMF_E_PRUNE_CIPHER_NA			12
+#define BRCMF_E_PRUNE_KNOWN_STA			13
+#define BRCMF_E_PRUNE_WDS_PEER			15
+#define BRCMF_E_PRUNE_QBSS_LOAD			16
+#define BRCMF_E_PRUNE_HOME_AP			17
+
+#define BRCMF_E_SUP_OTHER			0
+#define BRCMF_E_SUP_DECRYPT_KEY_DATA		1
+#define BRCMF_E_SUP_BAD_UCAST_WEP128		2
+#define BRCMF_E_SUP_BAD_UCAST_WEP40		3
+#define BRCMF_E_SUP_UNSUP_KEY_LEN		4
+#define BRCMF_E_SUP_PW_KEY_CIPHER		5
+#define BRCMF_E_SUP_MSG3_TOO_MANY_IE		6
+#define BRCMF_E_SUP_MSG3_IE_MISMATCH		7
+#define BRCMF_E_SUP_NO_INSTALL_FLAG		8
+#define BRCMF_E_SUP_MSG3_NO_GTK			9
+#define BRCMF_E_SUP_GRP_KEY_CIPHER		10
+#define BRCMF_E_SUP_GRP_MSG1_NO_GTK		11
+#define BRCMF_E_SUP_GTK_DECRYPT_FAIL		12
+#define BRCMF_E_SUP_SEND_FAIL			13
+#define BRCMF_E_SUP_DEAUTH			14
+
+#define BRCMF_E_IF_ADD				1
+#define BRCMF_E_IF_DEL				2
+#define BRCMF_E_IF_CHANGE			3
+
+#define BRCMF_E_IF_ROLE_STA			0
+#define BRCMF_E_IF_ROLE_AP			1
+#define BRCMF_E_IF_ROLE_WDS			2
+
+#define BRCMF_E_LINK_BCN_LOSS			1
+#define BRCMF_E_LINK_DISASSOC			2
+#define BRCMF_E_LINK_ASSOC_REC			3
+#define BRCMF_E_LINK_BSSCFG_DIS			4
+
+/* The level of bus communication with the dongle */
+enum brcmf_bus_state {
+	BRCMF_BUS_DOWN,		/* Not ready for frame transfers */
+	BRCMF_BUS_LOAD,		/* Download access only (CPU reset) */
+	BRCMF_BUS_DATA		/* Ready for frame transfers */
+};
+
+/* Pattern matching filter. Specifies an offset within received packets to
+ * start matching, the pattern to match, the size of the pattern, and a bitmask
+ * that indicates which bits within the pattern should be matched.
+ */
+struct brcmf_pkt_filter_pattern {
+	u32 offset;		/* Offset within received packet to start pattern matching.
+				 * Offset '0' is the first byte of the ethernet header.
+				 */
+	u32 size_bytes;	/* Size of the pattern.  Bitmask must be the same size. */
+	u8 mask_and_pattern[1];	/* Variable length mask and pattern data.  mask starts
+					 * at offset 0.  Pattern immediately follows mask.
+					 */
+};
+
+/* IOVAR "pkt_filter_add" parameter. Used to install packet filters. */
+struct brcmf_pkt_filter {
+	u32 id;		/* Unique filter id, specified by app. */
+	u32 type;		/* Filter type (WL_PKT_FILTER_TYPE_xxx). */
+	u32 negate_match;	/* Negate the result of filter matches */
+	union {			/* Filter definitions */
+		struct brcmf_pkt_filter_pattern pattern; /* Filter pattern */
+	} u;
+};
+
+/* IOVAR "pkt_filter_enable" parameter. */
+struct brcmf_pkt_filter_enable {
+	u32 id;		/* Unique filter id */
+	u32 enable;		/* Enable/disable bool */
+};
+
+/* BSS info structure
+ * Applications MUST CHECK ie_offset field and length field to access IEs and
+ * next bss_info structure in a vector (in struct brcmf_scan_results)
+ */
+struct brcmf_bss_info {
+	u32 version;		/* version field */
+	u32 length;		/* byte length of data in this record,
+				 * starting at version and including IEs
+				 */
+	u8 BSSID[ETH_ALEN];
+	u16 beacon_period;	/* units are Kusec */
+	u16 capability;	/* Capability information */
+	u8 SSID_len;
+	u8 SSID[32];
+	struct {
+		uint count;	/* # rates in this set */
+		u8 rates[16];	/* rates in 500kbps units w/hi bit set if basic */
+	} rateset;		/* supported rates */
+	chanspec_t chanspec;	/* chanspec for bss */
+	u16 atim_window;	/* units are Kusec */
+	u8 dtim_period;	/* DTIM period */
+	s16 RSSI;		/* receive signal strength (in dBm) */
+	s8 phy_noise;		/* noise (in dBm) */
+
+	u8 n_cap;		/* BSS is 802.11N Capable */
+	u32 nbss_cap;	/* 802.11N BSS Capabilities (based on HT_CAP_*) */
+	u8 ctl_ch;		/* 802.11N BSS control channel number */
+	u32 reserved32[1];	/* Reserved for expansion of BSS properties */
+	u8 flags;		/* flags */
+	u8 reserved[3];	/* Reserved for expansion of BSS properties */
+	u8 basic_mcs[MCSSET_LEN];	/* 802.11N BSS required MCS set */
+
+	u16 ie_offset;	/* offset at which IEs start, from beginning */
+	u32 ie_length;	/* byte length of Information Elements */
+	s16 SNR;		/* average SNR of during frame reception */
+	/* Add new fields here */
+	/* variable length Information Elements */
+};
+
+struct brcmf_ssid {
+	u32 SSID_len;
+	unsigned char SSID[32];
+};
+
+struct brcmf_scan_params {
+	struct brcmf_ssid ssid;	/* default: {0, ""} */
+	u8 bssid[ETH_ALEN];	/* default: bcast */
+	s8 bss_type;		/* default: any,
+				 * DOT11_BSSTYPE_ANY/INFRASTRUCTURE/INDEPENDENT
+				 */
+	u8 scan_type;	/* flags, 0 use default */
+	s32 nprobes;		/* -1 use default, number of probes per channel */
+	s32 active_time;	/* -1 use default, dwell time per channel for
+				 * active scanning
+				 */
+	s32 passive_time;	/* -1 use default, dwell time per channel
+				 * for passive scanning
+				 */
+	s32 home_time;	/* -1 use default, dwell time for the home channel
+				 * between channel scans
+				 */
+	s32 channel_num;	/* count of channels and ssids that follow
+				 *
+				 * low half is count of channels in
+				 * channel_list, 0 means default (use all
+				 * available channels)
+				 *
+				 * high half is entries in struct brcmf_ssid
+				 * array that follows channel_list, aligned for
+				 * s32 (4 bytes) meaning an odd channel count
+				 * implies a 2-byte pad between end of
+				 * channel_list and first ssid
+				 *
+				 * if ssid count is zero, single ssid in the
+				 * fixed parameter portion is assumed, otherwise
+				 * ssid in the fixed portion is ignored
+				 */
+	u16 channel_list[1];	/* list of chanspecs */
+};
+
+/* incremental scan struct */
+struct brcmf_iscan_params {
+	u32 version;
+	u16 action;
+	u16 scan_duration;
+	struct brcmf_scan_params params;
+};
+
+/* 3 fields + size of brcmf_scan_params, not including variable length array */
+#define BRCMF_ISCAN_PARAMS_FIXED_SIZE \
+	(offsetof(struct brcmf_iscan_params, params) + \
+	 sizeof(struct brcmf_ssid))
+
+struct brcmf_scan_results {
+	u32 buflen;
+	u32 version;
+	u32 count;
+	struct brcmf_bss_info bss_info[1];
+};
+
+/* used for association with a specific BSSID and chanspec list */
+struct brcmf_assoc_params {
+	u8 bssid[ETH_ALEN];	/* 00:00:00:00:00:00: broadcast scan */
+	s32 chanspec_num;	/* 0: all available channels,
+				 * otherwise count of chanspecs in chanspec_list
+				 */
+	chanspec_t chanspec_list[1];	/* list of chanspecs */
+};
+#define BRCMF_ASSOC_PARAMS_FIXED_SIZE \
+	(sizeof(struct brcmf_assoc_params) - sizeof(chanspec_t))
+
+/* used for join with or without a specific bssid and channel list */
+struct brcmf_join_params {
+	struct brcmf_ssid ssid;
+	struct brcmf_assoc_params params;
+};
+
+/* size of brcmf_scan_results not including variable length array */
+#define BRCMF_SCAN_RESULTS_FIXED_SIZE \
+	(sizeof(struct brcmf_scan_results) - sizeof(struct brcmf_bss_info))
+
+/* incremental scan results struct */
+struct brcmf_iscan_results {
+	u32 status;
+	struct brcmf_scan_results results;
+};
+
+/* size of brcmf_iscan_results not including variable length array */
+#define BRCMF_ISCAN_RESULTS_FIXED_SIZE \
+	(BRCMF_SCAN_RESULTS_FIXED_SIZE + \
+	 offsetof(struct brcmf_iscan_results, results))
+
+struct brcmf_wsec_key {
+	u32 index;		/* key index */
+	u32 len;		/* key length */
+	u8 data[WLAN_MAX_KEY_LEN];	/* key data */
+	u32 pad_1[18];
+	u32 algo;		/* CRYPTO_ALGO_AES_CCM, CRYPTO_ALGO_WEP128, etc */
+	u32 flags;		/* misc flags */
+	u32 pad_2[2];
+	int pad_3;
+	int iv_initialized;	/* has IV been initialized already? */
+	int pad_4;
+	/* Rx IV */
+	struct {
+		u32 hi;	/* upper 32 bits of IV */
+		u16 lo;	/* lower 16 bits of IV */
+	} rxiv;
+	u32 pad_5[2];
+	u8 ea[ETH_ALEN];	/* per station */
+};
+
+/* Used to get specific STA parameters */
+struct brcmf_scb_val {
+	u32 val;
+	u8 ea[ETH_ALEN];
+};
+
+/* channel encoding */
+struct brcmf_channel_info {
+	int hw_channel;
+	int target_channel;
+	int scan_channel;
+};
+
+/* Linux network driver ioctl encoding */
+struct brcmf_ioctl {
+	uint cmd;		/* common ioctl definition */
+	void *buf;		/* pointer to user buffer */
+	uint len;		/* length of user buffer */
+	u8 set;		/* get or set request (optional) */
+	uint used;		/* bytes read or written (optional) */
+	uint needed;		/* bytes needed (optional) */
+};
+
+/* Forward decls for struct brcmf_pub (see below) */
+struct brcmf_bus;		/* device bus info */
+struct brcmf_proto;	/* device communication protocol info */
+struct brcmf_info;	/* device driver info */
+
+/* Common structure for module and instance linkage */
+struct brcmf_pub {
+	/* Linkage ponters */
+	struct brcmf_bus *bus;
+	struct brcmf_proto *prot;
+	struct brcmf_info *info;
+
+	/* Internal brcmf items */
+	bool up;		/* Driver up/down (to OS) */
+	bool txoff;		/* Transmit flow-controlled */
+	bool dongle_reset;	/* true = DEVRESET put dongle into reset */
+	enum brcmf_bus_state busstate;
+	uint hdrlen;		/* Total BRCMF header length (proto + bus) */
+	uint maxctl;		/* Max size rxctl request from proto to bus */
+	uint rxsz;		/* Rx buffer size bus module should use */
+	u8 wme_dp;		/* wme discard priority */
+
+	/* Dongle media info */
+	bool iswl;		/* Dongle-resident driver is wl */
+	unsigned long drv_version;	/* Version of dongle-resident driver */
+	u8 mac[ETH_ALEN];			/* MAC address obtained from dongle */
+	struct dngl_stats dstats;	/* Stats for dongle-based data */
+
+	/* Additional stats for the bus level */
+	unsigned long tx_packets;	/* Data packets sent to dongle */
+	unsigned long tx_multicast;	/* Multicast data packets sent to dongle */
+	unsigned long tx_errors;	/* Errors in sending data to dongle */
+	unsigned long tx_ctlpkts;	/* Control packets sent to dongle */
+	unsigned long tx_ctlerrs;	/* Errors sending control frames to dongle */
+	unsigned long rx_packets;	/* Packets sent up the network interface */
+	unsigned long rx_multicast;	/* Multicast packets sent up the network
+					 interface */
+	unsigned long rx_errors;	/* Errors processing rx data packets */
+	unsigned long rx_ctlpkts;	/* Control frames processed from dongle */
+	unsigned long rx_ctlerrs;	/* Errors in processing rx control frames */
+	unsigned long rx_dropped;	/* Packets dropped locally (no memory) */
+	unsigned long rx_flushed;	/* Packets flushed due to
+				unscheduled sendup thread */
+	unsigned long wd_dpc_sched;	/* Number of times dpc scheduled by
+					 watchdog timer */
+
+	unsigned long rx_readahead_cnt;	/* Number of packets where header read-ahead
+					 was used. */
+	unsigned long tx_realloc;	/* Number of tx packets we had to realloc for
+					 headroom */
+	unsigned long fc_packets;	/* Number of flow control pkts recvd */
+
+	/* Last error return */
+	int bcmerror;
+	uint tickcnt;
+
+	/* Last error from dongle */
+	int dongle_error;
+
+	/* Suspend disable flag  flag */
+	int suspend_disable_flag;	/* "1" to disable all extra powersaving
+					 during suspend */
+	int in_suspend;		/* flag set to 1 when early suspend called */
+	int dtim_skip;		/* dtim skip , default 0 means wake each dtim */
+
+	/* Pkt filter defination */
+	char *pktfilter[100];
+	int pktfilter_count;
+
+	u8 country_code[BRCM_CNTRY_BUF_SZ];
+	char eventmask[BRCMF_EVENTING_MASK_LEN];
+
+};
+
+struct brcmf_if_event {
+	u8 ifidx;
+	u8 action;
+	u8 flags;
+	u8 bssidx;
+};
+
+struct brcmf_timeout {
+	u32 limit;		/* Expiration time (usec) */
+	u32 increment;	/* Current expiration increment (usec) */
+	u32 elapsed;		/* Current elapsed time (usec) */
+	u32 tick;		/* O/S tick time (usec) */
+};
+
+struct bcmevent_name {
+	uint event;
+	const char *name;
+};
+
+#if defined(CONFIG_PM_SLEEP)
+extern atomic_t brcmf_mmc_suspend;
+#define BRCMF_PM_RESUME_WAIT_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+#define _BRCMF_PM_RESUME_WAIT(a, b) do { \
+		int retry = 0; \
+		while (atomic_read(&brcmf_mmc_suspend) && retry++ != b) { \
+			wait_event_timeout(a, false, HZ/100); \
+		} \
+	}	while (0)
+#define BRCMF_PM_RESUME_WAIT(a)	_BRCMF_PM_RESUME_WAIT(a, 30)
+#define BRCMF_PM_RESUME_RETURN_ERROR(a)	\
+	do { if (atomic_read(&brcmf_mmc_suspend)) return a; } while (0)
+
+#define BRCMF_SPINWAIT_SLEEP_INIT(a) DECLARE_WAIT_QUEUE_HEAD(a);
+#define BRCMF_SPINWAIT_SLEEP(a, exp, us) do { \
+		uint countdown = (us) + 9999; \
+		while ((exp) && (countdown >= 10000)) { \
+			wait_event_timeout(a, false, HZ/100); \
+			countdown -= 10000; \
+		} \
+	} while (0)
+
+#else
+
+#define BRCMF_PM_RESUME_WAIT_INIT(a)
+#define BRCMF_PM_RESUME_WAIT(a)
+#define BRCMF_PM_RESUME_RETURN_ERROR(a)
+
+#define BRCMF_SPINWAIT_SLEEP_INIT(a)
+#define BRCMF_SPINWAIT_SLEEP(a, exp, us)  do { \
+		uint countdown = (us) + 9; \
+		while ((exp) && (countdown >= 10)) { \
+			udelay(10);  \
+			countdown -= 10;  \
+		} \
+	} while (0)
+
+#endif	/* defined(CONFIG_PM_SLEEP) */
+
+/*
+ * Insmod parameters for debug/test
+ */
+
+/* Use interrupts */
+extern uint brcmf_intr;
+
+/* Use polling */
+extern uint brcmf_poll;
+
+/* ARP offload agent mode */
+extern uint brcmf_arp_mode;
+
+/* ARP offload enable */
+extern uint brcmf_arp_enable;
+
+/* Pkt filte enable control */
+extern uint brcmf_pkt_filter_enable;
+
+/*  Pkt filter init setup */
+extern uint brcmf_pkt_filter_init;
+
+/* Pkt filter mode control */
+extern uint brcmf_master_mode;
+
+/* Roaming mode control */
+extern uint brcmf_roam;
+
+/* Roaming mode control */
+extern uint brcmf_radio_up;
+
+/* Initial idletime ticks (may be -1 for immediate idle, 0 for no idle) */
+extern int brcmf_idletime;
+#define BRCMF_IDLETIME_TICKS 1
+
+/* SDIO Drive Strength */
+extern uint brcmf_sdiod_drive_strength;
+
+/* Override to force tx queueing all the time */
+extern uint brcmf_force_tx_queueing;
+
+#ifdef SDTEST
+/* Echo packet generator (SDIO), pkts/s */
+extern uint brcmf_pktgen;
+
+/* Echo packet len (0 => sawtooth, max 1800) */
+extern uint brcmf_pktgen_len;
+#define BRCMF_MAX_PKTGEN_LEN 1800
+#endif
+
+extern const struct bcmevent_name bcmevent_names[];
+extern const int bcmevent_names_size;
+
+
+static inline void MUTEX_LOCK_INIT(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_LOCK(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_UNLOCK(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_LOCK_SOFTAP_SET_INIT(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_LOCK_SOFTAP_SET(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_UNLOCK_SOFTAP_SET(struct brcmf_pub *drvr)
+{
+}
+
+static inline void MUTEX_LOCK_WL_SCAN_SET_INIT(void)
+{
+}
+
+static inline void MUTEX_LOCK_WL_SCAN_SET(void)
+{
+}
+
+static inline void MUTEX_UNLOCK_WL_SCAN_SET(void)
+{
+}
+
+/* Indication from bus module regarding presence/insertion of dongle.
+ * Return struct brcmf_pub pointer, used as handle to OS module in later calls.
+ * Returned structure should have bus and prot pointers filled in.
+ * bus_hdrlen specifies required headroom for bus module header.
+ */
+extern struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus,
+				      uint bus_hdrlen);
+extern int brcmf_net_attach(struct brcmf_pub *drvr, int idx);
+extern int brcmf_netdev_wait_pend8021x(struct net_device *dev);
+
+/* Indication from bus module regarding removal/absence of dongle */
+extern void brcmf_detach(struct brcmf_pub *drvr);
+
+/* Indication from bus module to change flow-control state */
+extern void brcmf_txflowcontrol(struct brcmf_pub *drvr, int ifidx, bool on);
+
+extern bool brcmf_c_prec_enq(struct brcmf_pub *drvr, struct pktq *q,
+			 struct sk_buff *pkt, int prec);
+
+/* Receive frame for delivery to OS.  Callee disposes of rxp. */
+extern void brcmf_rx_frame(struct brcmf_pub *drvr, int ifidx,
+			 struct sk_buff *rxp, int numpkt);
+
+/* Return pointer to interface name */
+extern char *brcmf_ifname(struct brcmf_pub *drvr, int idx);
+
+/* Notify tx completion */
+extern void brcmf_txcomplete(struct brcmf_pub *drvr, struct sk_buff *txp,
+			     bool success);
+
+/* Query ioctl */
+extern int brcmf_proto_cdc_query_ioctl(struct brcmf_pub *drvr, int ifidx,
+				       uint cmd, void *buf, uint len);
+
+/* OS independent layer functions */
+extern int brcmf_os_proto_block(struct brcmf_pub *drvr);
+extern int brcmf_os_proto_unblock(struct brcmf_pub *drvr);
+extern int brcmf_os_ioctl_resp_wait(struct brcmf_pub *drvr, uint *condition,
+				  bool *pending);
+extern int brcmf_os_ioctl_resp_wake(struct brcmf_pub *drvr);
+extern unsigned int brcmf_os_get_ioctl_resp_timeout(void);
+extern void brcmf_os_set_ioctl_resp_timeout(unsigned int timeout_msec);
+#ifdef BCMDBG
+extern int brcmf_write_to_file(struct brcmf_pub *drvr, u8 *buf, int size);
+#endif				/* BCMDBG */
+
+extern void brcmf_timeout_start(struct brcmf_timeout *tmo, uint usec);
+extern int brcmf_timeout_expired(struct brcmf_timeout *tmo);
+
+extern int brcmf_ifname2idx(struct brcmf_info *drvr_priv, char *name);
+extern int brcmf_c_host_event(struct brcmf_info *drvr_priv, int *idx,
+			      void *pktdata, struct brcmf_event_msg *,
+			      void **data_ptr);
+
+extern void brcmf_c_init(void);
+
+extern int brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, void *handle,
+		      char *name, u8 *mac_addr, u32 flags, u8 bssidx);
+extern void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx);
+
+/* Send packet to dongle via data channel */
+extern int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx,\
+			 struct sk_buff *pkt);
+
+extern int brcmf_bus_devreset(struct brcmf_pub *drvr, u8 flag);
+extern int brcmf_bus_start(struct brcmf_pub *drvr);
+
+extern void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg);
+extern void brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg,
+					     int enable, int master_mode);
+
+/* Linux network driver ioctl encoding */
+struct brcmf_c_ioctl {
+	uint cmd;		/* common ioctl definition */
+	void *buf;		/* pointer to user buffer */
+	uint len;		/* length of user buffer */
+	bool set;		/* get or set request (optional) */
+	uint used;		/* bytes read or written (optional) */
+	uint needed;		/* bytes needed (optional) */
+	uint driver;		/* to identify target driver */
+};
+
+/* per-driver magic numbers */
+#define BRCMF_IOCTL_MAGIC		0x00444944
+
+/* bump this number if you change the ioctl interface */
+#define BRCMF_IOCTL_VERSION	1
+#define	BRCMF_IOCTL_MAXLEN	8192	/* max length ioctl buffer required */
+
+/* common ioctl definitions */
+#define BRCMF_GET_MAGIC				0
+#define BRCMF_GET_VERSION				1
+#define BRCMF_GET_VAR				2
+#define BRCMF_SET_VAR				3
+
+/* message levels */
+#define BRCMF_ERROR_VAL	0x0001
+#define BRCMF_TRACE_VAL	0x0002
+#define BRCMF_INFO_VAL	0x0004
+#define BRCMF_DATA_VAL	0x0008
+#define BRCMF_CTL_VAL	0x0010
+#define BRCMF_TIMER_VAL	0x0020
+#define BRCMF_HDRS_VAL	0x0040
+#define BRCMF_BYTES_VAL	0x0080
+#define BRCMF_INTR_VAL	0x0100
+#define BRCMF_GLOM_VAL	0x0400
+#define BRCMF_EVENT_VAL	0x0800
+#define BRCMF_BTA_VAL	0x1000
+#define BRCMF_ISCAN_VAL 0x2000
+
+#ifdef SDTEST
+/* For pktgen iovar */
+struct brcmf_pktgen {
+	uint version;		/* To allow structure change tracking */
+	uint freq;		/* Max ticks between tx/rx attempts */
+	uint count;		/* Test packets to send/rcv each attempt */
+	uint print;		/* Print counts every <print> attempts */
+	uint total;		/* Total packets (or bursts) */
+	uint minlen;		/* Minimum length of packets to send */
+	uint maxlen;		/* Maximum length of packets to send */
+	uint numsent;		/* Count of test packets sent */
+	uint numrcvd;		/* Count of test packets received */
+	uint numfail;		/* Count of test send failures */
+	uint mode;		/* Test mode (type of test packets) */
+	uint stop;		/* Stop after this many tx failures */
+};
+
+/* Version in case structure changes */
+#define BRCMF_PKTGEN_VERSION	2
+
+/* Type of test packets to use */
+#define BRCMF_PKTGEN_ECHO	1	/* Send echo requests */
+#define BRCMF_PKTGEN_SEND	2	/* Send discard packets */
+#define BRCMF_PKTGEN_RXBURST	3	/* Request dongle send N packets */
+#define BRCMF_PKTGEN_RECV		4	/* Continuous rx from continuous
+					 tx dongle */
+#endif				/* SDTEST */
+
+/* Enter idle immediately (no timeout) */
+#define BRCMF_IDLE_IMMEDIATE	(-1)
+
+/* Values for idleclock iovar: other values are the sd_divisor to use
+	 when idle */
+#define BRCMF_IDLE_ACTIVE	0	/* Do not request any SD clock change
+				 when idle */
+
+#endif				/* _BRCMF_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
new file mode 100644
index 0000000..653cf0d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_bus.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_BUS_H_
+#define _BRCMF_BUS_H_
+
+/* Packet alignment for most efficient SDIO (can change based on platform) */
+#ifndef BRCMF_SDALIGN
+#define BRCMF_SDALIGN	32
+#endif
+#if !ISPOWEROF2(BRCMF_SDALIGN)
+#error BRCMF_SDALIGN is not a power of 2!
+#endif
+
+/*
+ * Exported from brcmf bus module (brcmf_usb, brcmf_sdio)
+ */
+
+/* dongle ram module parameter */
+extern int brcmf_dongle_memsize;
+
+/* Tx/Rx bounds module parameters */
+extern uint brcmf_txbound;
+extern uint brcmf_rxbound;
+
+/* Watchdog timer interval */
+extern uint brcmf_watchdog_ms;
+
+/* Indicate (dis)interest in finding dongles. */
+extern int brcmf_bus_register(void);
+extern void brcmf_bus_unregister(void);
+
+/* Stop bus module: clear pending frames, disable data flow */
+extern void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus, bool enforce_mutex);
+
+/* Initialize bus module: prepare for communication w/dongle */
+extern int brcmf_sdbrcm_bus_init(struct brcmf_pub *drvr, bool enforce_mutex);
+
+/* Send a data frame to the dongle.  Callee disposes of txp. */
+extern int brcmf_sdbrcm_bus_txdata(struct brcmf_bus *bus, struct sk_buff *txp);
+
+/* Send/receive a control message to/from the dongle.
+ * Expects caller to enforce a single outstanding transaction.
+ */
+extern int
+brcmf_sdbrcm_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen);
+
+extern int
+brcmf_sdbrcm_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen);
+
+/* Check for and handle local prot-specific iovar commands */
+extern int brcmf_sdbrcm_bus_iovar_op(struct brcmf_pub *drvr, const char *name,
+			    void *params, int plen, void *arg, int len,
+			    bool set);
+
+/* Add bus dump output to a buffer */
+extern void brcmf_sdbrcm_bus_dump(struct brcmf_pub *drvr,
+				  struct brcmu_strbuf *strbuf);
+
+/* Clear any bus counters */
+extern void brcmf_bus_clearcounts(struct brcmf_pub *drvr);
+
+extern void brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick);
+
+#endif				/* _BRCMF_BUS_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
new file mode 100644
index 0000000..345acab
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_cdc.c
@@ -0,0 +1,502 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <defs.h>
+
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "dhd.h"
+#include "dhd_proto.h"
+#include "dhd_bus.h"
+#include "dhd_dbg.h"
+
+struct brcmf_proto_cdc_ioctl {
+	u32 cmd;	/* ioctl command value */
+	u32 len;	/* lower 16: output buflen;
+			 * upper 16: input buflen (excludes header) */
+	u32 flags;	/* flag defns given below */
+	u32 status;	/* status code returned from the device */
+};
+
+/* Max valid buffer size that can be sent to the dongle */
+#define CDC_MAX_MSG_SIZE	(ETH_FRAME_LEN+ETH_FCS_LEN)
+
+/* CDC flag definitions */
+#define CDCF_IOC_ERROR		0x01		/* 1=ioctl cmd failed */
+#define CDCF_IOC_SET		0x02		/* 0=get, 1=set cmd */
+#define CDCF_IOC_IF_MASK	0xF000		/* I/F index */
+#define CDCF_IOC_IF_SHIFT	12
+#define CDCF_IOC_ID_MASK	0xFFFF0000	/* id an ioctl pairing */
+#define CDCF_IOC_ID_SHIFT	16		/* ID Mask shift bits */
+#define CDC_IOC_ID(flags)	\
+	(((flags) & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT)
+#define CDC_SET_IF_IDX(hdr, idx) \
+	((hdr)->flags = (((hdr)->flags & ~CDCF_IOC_IF_MASK) | \
+	((idx) << CDCF_IOC_IF_SHIFT)))
+
+/*
+ * BDC header - Broadcom specific extension of CDC.
+ * Used on data packets to convey priority across USB.
+ */
+#define	BDC_HEADER_LEN		4
+#define BDC_PROTO_VER		1	/* Protocol version */
+#define BDC_FLAG_VER_MASK	0xf0	/* Protocol version mask */
+#define BDC_FLAG_VER_SHIFT	4	/* Protocol version shift */
+#define BDC_FLAG_SUM_GOOD	0x04	/* Good RX checksums */
+#define BDC_FLAG_SUM_NEEDED	0x08	/* Dongle needs to do TX checksums */
+#define BDC_PRIORITY_MASK	0x7
+#define BDC_FLAG2_IF_MASK	0x0f	/* packet rx interface in APSTA */
+#define BDC_FLAG2_IF_SHIFT	0
+
+#define BDC_GET_IF_IDX(hdr) \
+	((int)((((hdr)->flags2) & BDC_FLAG2_IF_MASK) >> BDC_FLAG2_IF_SHIFT))
+#define BDC_SET_IF_IDX(hdr, idx) \
+	((hdr)->flags2 = (((hdr)->flags2 & ~BDC_FLAG2_IF_MASK) | \
+	((idx) << BDC_FLAG2_IF_SHIFT)))
+
+struct brcmf_proto_bdc_header {
+	u8 flags;
+	u8 priority;	/* 802.1d Priority, 4:7 flow control info for usb */
+	u8 flags2;
+	u8 rssi;
+};
+
+
+#define RETRIES 2	/* # of retries to retrieve matching ioctl response */
+#define BUS_HEADER_LEN	(16+BRCMF_SDALIGN) /* Must be atleast SDPCM_RESERVE
+					 * (amount of header tha might be added)
+					 * plus any space that might be needed
+					 * for alignment padding.
+					 */
+#define ROUND_UP_MARGIN	2048	/* Biggest SDIO block size possible for
+				 * round off at the end of buffer
+				 */
+
+struct brcmf_proto {
+	u16 reqid;
+	u8 pending;
+	u32 lastcmd;
+	u8 bus_header[BUS_HEADER_LEN];
+	struct brcmf_proto_cdc_ioctl msg;
+	unsigned char buf[BRCMF_C_IOCTL_MAXLEN + ROUND_UP_MARGIN];
+};
+
+static int brcmf_proto_cdc_msg(struct brcmf_pub *drvr)
+{
+	struct brcmf_proto *prot = drvr->prot;
+	int len = le32_to_cpu(prot->msg.len) +
+			sizeof(struct brcmf_proto_cdc_ioctl);
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* NOTE : cdc->msg.len holds the desired length of the buffer to be
+	 *        returned. Only up to CDC_MAX_MSG_SIZE of this buffer area
+	 *        is actually sent to the dongle
+	 */
+	if (len > CDC_MAX_MSG_SIZE)
+		len = CDC_MAX_MSG_SIZE;
+
+	/* Send request */
+	return brcmf_sdbrcm_bus_txctl(drvr->bus, (unsigned char *)&prot->msg,
+				      len);
+}
+
+static int brcmf_proto_cdc_cmplt(struct brcmf_pub *drvr, u32 id, u32 len)
+{
+	int ret;
+	struct brcmf_proto *prot = drvr->prot;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	do {
+		ret = brcmf_sdbrcm_bus_rxctl(drvr->bus,
+				(unsigned char *)&prot->msg,
+				len + sizeof(struct brcmf_proto_cdc_ioctl));
+		if (ret < 0)
+			break;
+	} while (CDC_IOC_ID(le32_to_cpu(prot->msg.flags)) != id);
+
+	return ret;
+}
+
+int
+brcmf_proto_cdc_query_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd,
+			    void *buf, uint len)
+{
+	struct brcmf_proto *prot = drvr->prot;
+	struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
+	void *info;
+	int ret = 0, retries = 0;
+	u32 id, flags = 0;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+	BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
+
+	/* Respond "bcmerror" and "bcmerrorstr" with local cache */
+	if (cmd == BRCMF_C_GET_VAR && buf) {
+		if (!strcmp((char *)buf, "bcmerrorstr")) {
+			strncpy((char *)buf, "bcm_error",
+				BCME_STRLEN);
+			goto done;
+		} else if (!strcmp((char *)buf, "bcmerror")) {
+			*(int *)buf = drvr->dongle_error;
+			goto done;
+		}
+	}
+
+	memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl));
+
+	msg->cmd = cpu_to_le32(cmd);
+	msg->len = cpu_to_le32(len);
+	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT);
+	CDC_SET_IF_IDX(msg, ifidx);
+	msg->flags = cpu_to_le32(msg->flags);
+
+	if (buf)
+		memcpy(prot->buf, buf, len);
+
+	ret = brcmf_proto_cdc_msg(drvr);
+	if (ret < 0) {
+		BRCMF_ERROR(("brcmf_proto_cdc_query_ioctl: brcmf_proto_cdc_msg "
+			     "failed w/status %d\n", ret));
+		goto done;
+	}
+
+retry:
+	/* wait for interrupt and get first fragment */
+	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
+	if (ret < 0)
+		goto done;
+
+	flags = le32_to_cpu(msg->flags);
+	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+	if ((id < prot->reqid) && (++retries < RETRIES))
+		goto retry;
+	if (id != prot->reqid) {
+		BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+			     brcmf_ifname(drvr, ifidx), __func__, id,
+			     prot->reqid));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Check info buffer */
+	info = (void *)&msg[1];
+
+	/* Copy info buffer */
+	if (buf) {
+		if (ret < (int)len)
+			len = ret;
+		memcpy(buf, info, len);
+	}
+
+	/* Check the ERROR flag */
+	if (flags & CDCF_IOC_ERROR) {
+		ret = le32_to_cpu(msg->status);
+		/* Cache error from dongle */
+		drvr->dongle_error = ret;
+	}
+
+done:
+	return ret;
+}
+
+int brcmf_proto_cdc_set_ioctl(struct brcmf_pub *drvr, int ifidx, uint cmd,
+			      void *buf, uint len)
+{
+	struct brcmf_proto *prot = drvr->prot;
+	struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
+	int ret = 0;
+	u32 flags, id;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+	BRCMF_CTL(("%s: cmd %d len %d\n", __func__, cmd, len));
+
+	memset(msg, 0, sizeof(struct brcmf_proto_cdc_ioctl));
+
+	msg->cmd = cpu_to_le32(cmd);
+	msg->len = cpu_to_le32(len);
+	msg->flags = (++prot->reqid << CDCF_IOC_ID_SHIFT) | CDCF_IOC_SET;
+	CDC_SET_IF_IDX(msg, ifidx);
+	msg->flags = cpu_to_le32(msg->flags);
+
+	if (buf)
+		memcpy(prot->buf, buf, len);
+
+	ret = brcmf_proto_cdc_msg(drvr);
+	if (ret < 0)
+		goto done;
+
+	ret = brcmf_proto_cdc_cmplt(drvr, prot->reqid, len);
+	if (ret < 0)
+		goto done;
+
+	flags = le32_to_cpu(msg->flags);
+	id = (flags & CDCF_IOC_ID_MASK) >> CDCF_IOC_ID_SHIFT;
+
+	if (id != prot->reqid) {
+		BRCMF_ERROR(("%s: %s: unexpected request id %d (expected %d)\n",
+			     brcmf_ifname(drvr, ifidx), __func__, id,
+			     prot->reqid));
+		ret = -EINVAL;
+		goto done;
+	}
+
+	/* Check the ERROR flag */
+	if (flags & CDCF_IOC_ERROR) {
+		ret = le32_to_cpu(msg->status);
+		/* Cache error from dongle */
+		drvr->dongle_error = ret;
+	}
+
+done:
+	return ret;
+}
+
+int
+brcmf_proto_ioctl(struct brcmf_pub *drvr, int ifidx, struct brcmf_ioctl *ioc,
+		  void *buf, int len)
+{
+	struct brcmf_proto *prot = drvr->prot;
+	int ret = -1;
+
+	if (drvr->busstate == BRCMF_BUS_DOWN) {
+		BRCMF_ERROR(("%s : bus is down. we have nothing to do\n",
+			     __func__));
+		return ret;
+	}
+	brcmf_os_proto_block(drvr);
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (len > BRCMF_C_IOCTL_MAXLEN)
+		goto done;
+
+	if (prot->pending == true) {
+		BRCMF_TRACE(("CDC packet is pending!!!! cmd=0x%x (%lu) "
+			     "lastcmd=0x%x (%lu)\n",
+			     ioc->cmd, (unsigned long)ioc->cmd, prot->lastcmd,
+			     (unsigned long)prot->lastcmd));
+		if ((ioc->cmd == BRCMF_C_SET_VAR) ||
+		    (ioc->cmd == BRCMF_C_GET_VAR))
+			BRCMF_TRACE(("iovar cmd=%s\n", (char *)buf));
+
+		goto done;
+	}
+
+	prot->pending = true;
+	prot->lastcmd = ioc->cmd;
+	if (ioc->set)
+		ret = brcmf_proto_cdc_set_ioctl(drvr, ifidx, ioc->cmd,
+						buf, len);
+	else {
+		ret = brcmf_proto_cdc_query_ioctl(drvr, ifidx, ioc->cmd,
+						  buf, len);
+		if (ret > 0)
+			ioc->used = ret - sizeof(struct brcmf_proto_cdc_ioctl);
+	}
+
+	/* Too many programs assume ioctl() returns 0 on success */
+	if (ret >= 0)
+		ret = 0;
+	else {
+		struct brcmf_proto_cdc_ioctl *msg = &prot->msg;
+		/* len == needed when set/query fails from dongle */
+		ioc->needed = le32_to_cpu(msg->len);
+	}
+
+	/* Intercept the wme_dp ioctl here */
+	if (!ret && ioc->cmd == BRCMF_C_SET_VAR &&
+	    !strcmp(buf, "wme_dp")) {
+		int slen, val = 0;
+
+		slen = strlen("wme_dp") + 1;
+		if (len >= (int)(slen + sizeof(int)))
+			memcpy(&val, (char *)buf + slen, sizeof(int));
+		drvr->wme_dp = (u8) le32_to_cpu(val);
+	}
+
+	prot->pending = false;
+
+done:
+	brcmf_os_proto_unblock(drvr);
+
+	return ret;
+}
+
+#define PKTSUMNEEDED(skb) \
+		(((struct sk_buff *)(skb))->ip_summed == CHECKSUM_PARTIAL)
+#define PKTSETSUMGOOD(skb, x) \
+		(((struct sk_buff *)(skb))->ip_summed = \
+		((x) ? CHECKSUM_UNNECESSARY : CHECKSUM_NONE))
+
+void brcmf_proto_dump(struct brcmf_pub *drvr, struct brcmu_strbuf *strbuf)
+{
+	brcmu_bprintf(strbuf, "Protocol CDC: reqid %d\n", drvr->prot->reqid);
+}
+
+void brcmf_proto_hdrpush(struct brcmf_pub *drvr, int ifidx,
+			 struct sk_buff *pktbuf)
+{
+	struct brcmf_proto_bdc_header *h;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Push BDC header used to convey priority for buses that don't */
+
+	skb_push(pktbuf, BDC_HEADER_LEN);
+
+	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
+
+	h->flags = (BDC_PROTO_VER << BDC_FLAG_VER_SHIFT);
+	if (PKTSUMNEEDED(pktbuf))
+		h->flags |= BDC_FLAG_SUM_NEEDED;
+
+	h->priority = (pktbuf->priority & BDC_PRIORITY_MASK);
+	h->flags2 = 0;
+	h->rssi = 0;
+	BDC_SET_IF_IDX(h, ifidx);
+}
+
+int brcmf_proto_hdrpull(struct brcmf_pub *drvr, int *ifidx,
+			struct sk_buff *pktbuf)
+{
+	struct brcmf_proto_bdc_header *h;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Pop BDC header used to convey priority for buses that don't */
+
+	if (pktbuf->len < BDC_HEADER_LEN) {
+		BRCMF_ERROR(("%s: rx data too short (%d < %d)\n", __func__,
+			     pktbuf->len, BDC_HEADER_LEN));
+		return -EBADE;
+	}
+
+	h = (struct brcmf_proto_bdc_header *)(pktbuf->data);
+
+	*ifidx = BDC_GET_IF_IDX(h);
+	if (*ifidx >= BRCMF_MAX_IFS) {
+		BRCMF_ERROR(("%s: rx data ifnum out of range (%d)\n",
+			     __func__, *ifidx));
+		return -EBADE;
+	}
+
+	if (((h->flags & BDC_FLAG_VER_MASK) >> BDC_FLAG_VER_SHIFT) !=
+	    BDC_PROTO_VER) {
+		BRCMF_ERROR(("%s: non-BDC packet received, flags 0x%x\n",
+			     brcmf_ifname(drvr, *ifidx), h->flags));
+		return -EBADE;
+	}
+
+	if (h->flags & BDC_FLAG_SUM_GOOD) {
+		BRCMF_INFO(("%s: BDC packet received with good rx-csum, "
+			    "flags 0x%x\n",
+			    brcmf_ifname(drvr, *ifidx), h->flags));
+		PKTSETSUMGOOD(pktbuf, true);
+	}
+
+	pktbuf->priority = h->priority & BDC_PRIORITY_MASK;
+
+	skb_pull(pktbuf, BDC_HEADER_LEN);
+
+	return 0;
+}
+
+int brcmf_proto_attach(struct brcmf_pub *drvr)
+{
+	struct brcmf_proto *cdc;
+
+	cdc = kzalloc(sizeof(struct brcmf_proto), GFP_ATOMIC);
+	if (!cdc) {
+		BRCMF_ERROR(("%s: kmalloc failed\n", __func__));
+		goto fail;
+	}
+
+	/* ensure that the msg buf directly follows the cdc msg struct */
+	if ((unsigned long)(&cdc->msg + 1) != (unsigned long)cdc->buf) {
+		BRCMF_ERROR(("struct brcmf_proto is not correctly defined\n"));
+		goto fail;
+	}
+
+	drvr->prot = cdc;
+	drvr->hdrlen += BDC_HEADER_LEN;
+	drvr->maxctl = BRCMF_C_IOCTL_MAXLEN +
+			sizeof(struct brcmf_proto_cdc_ioctl) + ROUND_UP_MARGIN;
+	return 0;
+
+fail:
+	kfree(cdc);
+	return -ENOMEM;
+}
+
+/* ~NOTE~ What if another thread is waiting on the semaphore?  Holding it? */
+void brcmf_proto_detach(struct brcmf_pub *drvr)
+{
+	kfree(drvr->prot);
+	drvr->prot = NULL;
+}
+
+void brcmf_proto_dstats(struct brcmf_pub *drvr)
+{
+	/* No stats from dongle added yet, copy bus stats */
+	drvr->dstats.tx_packets = drvr->tx_packets;
+	drvr->dstats.tx_errors = drvr->tx_errors;
+	drvr->dstats.rx_packets = drvr->rx_packets;
+	drvr->dstats.rx_errors = drvr->rx_errors;
+	drvr->dstats.rx_dropped = drvr->rx_dropped;
+	drvr->dstats.multicast = drvr->rx_multicast;
+	return;
+}
+
+int brcmf_proto_init(struct brcmf_pub *drvr)
+{
+	int ret = 0;
+	char buf[128];
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	brcmf_os_proto_block(drvr);
+
+	/* Get the device MAC address */
+	strcpy(buf, "cur_etheraddr");
+	ret = brcmf_proto_cdc_query_ioctl(drvr, 0, BRCMF_C_GET_VAR,
+					  buf, sizeof(buf));
+	if (ret < 0) {
+		brcmf_os_proto_unblock(drvr);
+		return ret;
+	}
+	memcpy(drvr->mac, buf, ETH_ALEN);
+
+	brcmf_os_proto_unblock(drvr);
+
+	ret = brcmf_c_preinit_ioctls(drvr);
+
+	/* Always assumes wl for now */
+	drvr->iswl = true;
+
+	return ret;
+}
+
+void brcmf_proto_stop(struct brcmf_pub *drvr)
+{
+	/* Nothing to do for CDC */
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
new file mode 100644
index 0000000..fdec468
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_common.c
@@ -0,0 +1,1196 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/netdevice.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+
+#define BRCM_OUI			"\x00\x10\x18"
+#define DOT11_OUI_LEN			3
+#define BCMILCP_BCM_SUBTYPE_EVENT	1
+#define PKTFILTER_BUF_SIZE		2048
+
+int brcmf_msg_level;
+
+#define MSGTRACE_VERSION	1
+
+#ifdef BCMDBG
+const char brcmf_version[] =
+"Dongle Host Driver, version " BRCMF_VERSION_STR "\nCompiled on " __DATE__
+" at " __TIME__;
+#else
+const char brcmf_version[] = "Dongle Host Driver, version " BRCMF_VERSION_STR;
+#endif
+
+/* IOVar table */
+enum {
+	IOV_VERSION = 1,
+	IOV_MSGLEVEL,
+	IOV_BCMERRORSTR,
+	IOV_BCMERROR,
+	IOV_DUMP,
+	IOV_CLEARCOUNTS,
+	IOV_LOGDUMP,
+	IOV_LOGCAL,
+	IOV_LOGSTAMP,
+	IOV_GPIOOB,
+	IOV_IOCTLTIMEOUT,
+	IOV_LAST
+};
+
+const struct brcmu_iovar brcmf_iovars[] = {
+	{"version", IOV_VERSION, 0, IOVT_BUFFER, sizeof(brcmf_version)}
+	,
+#ifdef BCMDBG
+	{"msglevel", IOV_MSGLEVEL, 0, IOVT_UINT32, 0}
+	,
+#endif				/* BCMDBG */
+	{"bcmerrorstr", IOV_BCMERRORSTR, 0, IOVT_BUFFER, BCME_STRLEN}
+	,
+	{"bcmerror", IOV_BCMERROR, 0, IOVT_INT8, 0}
+	,
+	{"dump", IOV_DUMP, 0, IOVT_BUFFER, BRCMF_IOCTL_MAXLEN}
+	,
+	{"clearcounts", IOV_CLEARCOUNTS, 0, IOVT_VOID, 0}
+	,
+	{"gpioob", IOV_GPIOOB, 0, IOVT_UINT32, 0}
+	,
+	{"ioctl_timeout", IOV_IOCTLTIMEOUT, 0, IOVT_UINT32, 0}
+	,
+	{NULL, 0, 0, 0, 0}
+};
+
+/* Message trace header */
+struct msgtrace_hdr {
+	u8 version;
+	u8 spare;
+	u16 len;		/* Len of the trace */
+	u32 seqnum;		/* Sequence number of message. Useful
+				 * if the messsage has been lost
+				 * because of DMA error or a bus reset
+				 * (ex: SDIO Func2)
+				 */
+	u32 discarded_bytes;	/* Number of discarded bytes because of
+				 trace overflow  */
+	u32 discarded_printf;	/* Number of discarded printf
+				 because of trace overflow */
+} __packed;
+
+void brcmf_c_init(void)
+{
+	/* Init global variables at run-time, not as part of the declaration.
+	 * This is required to support init/de-init of the driver.
+	 * Initialization
+	 * of globals as part of the declaration results in non-deterministic
+	 * behaviour since the value of the globals may be different on the
+	 * first time that the driver is initialized vs subsequent
+	 * initializations.
+	 */
+	brcmf_msg_level = BRCMF_ERROR_VAL;
+}
+
+static int brcmf_c_dump(struct brcmf_pub *drvr, char *buf, int buflen)
+{
+	struct brcmu_strbuf b;
+	struct brcmu_strbuf *strbuf = &b;
+
+	brcmu_binit(strbuf, buf, buflen);
+
+	/* Base info */
+	brcmu_bprintf(strbuf, "%s\n", brcmf_version);
+	brcmu_bprintf(strbuf, "\n");
+	brcmu_bprintf(strbuf, "pub.up %d pub.txoff %d pub.busstate %d\n",
+		    drvr->up, drvr->txoff, drvr->busstate);
+	brcmu_bprintf(strbuf, "pub.hdrlen %d pub.maxctl %d pub.rxsz %d\n",
+		    drvr->hdrlen, drvr->maxctl, drvr->rxsz);
+	brcmu_bprintf(strbuf, "pub.iswl %d pub.drv_version %ld pub.mac %pM\n",
+		    drvr->iswl, drvr->drv_version, &drvr->mac);
+	brcmu_bprintf(strbuf, "pub.bcmerror %d tickcnt %d\n", drvr->bcmerror,
+		    drvr->tickcnt);
+
+	brcmu_bprintf(strbuf, "dongle stats:\n");
+	brcmu_bprintf(strbuf,
+		    "tx_packets %ld tx_bytes %ld tx_errors %ld tx_dropped %ld\n",
+		    drvr->dstats.tx_packets, drvr->dstats.tx_bytes,
+		    drvr->dstats.tx_errors, drvr->dstats.tx_dropped);
+	brcmu_bprintf(strbuf,
+		    "rx_packets %ld rx_bytes %ld rx_errors %ld rx_dropped %ld\n",
+		    drvr->dstats.rx_packets, drvr->dstats.rx_bytes,
+		    drvr->dstats.rx_errors, drvr->dstats.rx_dropped);
+	brcmu_bprintf(strbuf, "multicast %ld\n", drvr->dstats.multicast);
+
+	brcmu_bprintf(strbuf, "bus stats:\n");
+	brcmu_bprintf(strbuf, "tx_packets %ld tx_multicast %ld tx_errors %ld\n",
+		    drvr->tx_packets, drvr->tx_multicast, drvr->tx_errors);
+	brcmu_bprintf(strbuf, "tx_ctlpkts %ld tx_ctlerrs %ld\n",
+		    drvr->tx_ctlpkts, drvr->tx_ctlerrs);
+	brcmu_bprintf(strbuf, "rx_packets %ld rx_multicast %ld rx_errors %ld\n",
+		    drvr->rx_packets, drvr->rx_multicast, drvr->rx_errors);
+	brcmu_bprintf(strbuf,
+		    "rx_ctlpkts %ld rx_ctlerrs %ld rx_dropped %ld rx_flushed %ld\n",
+		    drvr->rx_ctlpkts, drvr->rx_ctlerrs, drvr->rx_dropped,
+		    drvr->rx_flushed);
+	brcmu_bprintf(strbuf,
+		    "rx_readahead_cnt %ld tx_realloc %ld fc_packets %ld\n",
+		    drvr->rx_readahead_cnt, drvr->tx_realloc, drvr->fc_packets);
+	brcmu_bprintf(strbuf, "wd_dpc_sched %ld\n", drvr->wd_dpc_sched);
+	brcmu_bprintf(strbuf, "\n");
+
+	/* Add any prot info */
+	brcmf_proto_dump(drvr, strbuf);
+	brcmu_bprintf(strbuf, "\n");
+
+	/* Add any bus info */
+	brcmf_sdbrcm_bus_dump(drvr, strbuf);
+
+	return !strbuf->size ? -EOVERFLOW : 0;
+}
+
+static int
+brcmf_c_doiovar(struct brcmf_pub *drvr, const struct brcmu_iovar *vi,
+		u32 actionid, const char *name, void *params, int plen,
+		void *arg, int len, int val_size)
+{
+	int bcmerror = 0;
+	s32 int_val = 0;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	bcmerror = brcmu_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
+	if (bcmerror != 0)
+		goto exit;
+
+	if (plen >= (int)sizeof(int_val))
+		memcpy(&int_val, params, sizeof(int_val));
+
+	switch (actionid) {
+	case IOV_GVAL(IOV_VERSION):
+		/* Need to have checked buffer length */
+		strncpy((char *)arg, brcmf_version, len);
+		break;
+
+	case IOV_GVAL(IOV_MSGLEVEL):
+		int_val = (s32) brcmf_msg_level;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		brcmf_msg_level = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BCMERRORSTR):
+		strncpy((char *)arg, "bcm_error",
+			BCME_STRLEN);
+		((char *)arg)[BCME_STRLEN - 1] = 0x00;
+		break;
+
+	case IOV_GVAL(IOV_BCMERROR):
+		int_val = (s32) drvr->bcmerror;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_GVAL(IOV_DUMP):
+		bcmerror = brcmf_c_dump(drvr, arg, len);
+		break;
+
+	case IOV_SVAL(IOV_CLEARCOUNTS):
+		drvr->tx_packets = drvr->rx_packets = 0;
+		drvr->tx_errors = drvr->rx_errors = 0;
+		drvr->tx_ctlpkts = drvr->rx_ctlpkts = 0;
+		drvr->tx_ctlerrs = drvr->rx_ctlerrs = 0;
+		drvr->rx_dropped = 0;
+		drvr->rx_readahead_cnt = 0;
+		drvr->tx_realloc = 0;
+		drvr->wd_dpc_sched = 0;
+		memset(&drvr->dstats, 0, sizeof(drvr->dstats));
+		brcmf_bus_clearcounts(drvr);
+		break;
+
+	case IOV_GVAL(IOV_IOCTLTIMEOUT):{
+			int_val = (s32) brcmf_os_get_ioctl_resp_timeout();
+			memcpy(arg, &int_val, sizeof(int_val));
+			break;
+		}
+
+	case IOV_SVAL(IOV_IOCTLTIMEOUT):{
+			if (int_val <= 0)
+				bcmerror = -EINVAL;
+			else
+				brcmf_os_set_ioctl_resp_timeout((unsigned int)
+							      int_val);
+			break;
+		}
+
+	default:
+		bcmerror = -ENOTSUPP;
+		break;
+	}
+
+exit:
+	return bcmerror;
+}
+
+bool brcmf_c_prec_enq(struct brcmf_pub *drvr, struct pktq *q,
+		      struct sk_buff *pkt, int prec)
+{
+	struct sk_buff *p;
+	int eprec = -1;		/* precedence to evict from */
+	bool discard_oldest;
+
+	/* Fast case, precedence queue is not full and we are also not
+	 * exceeding total queue length
+	 */
+	if (!pktq_pfull(q, prec) && !pktq_full(q)) {
+		brcmu_pktq_penq(q, prec, pkt);
+		return true;
+	}
+
+	/* Determine precedence from which to evict packet, if any */
+	if (pktq_pfull(q, prec))
+		eprec = prec;
+	else if (pktq_full(q)) {
+		p = brcmu_pktq_peek_tail(q, &eprec);
+		if (eprec > prec)
+			return false;
+	}
+
+	/* Evict if needed */
+	if (eprec >= 0) {
+		/* Detect queueing to unconfigured precedence */
+		discard_oldest = AC_BITMAP_TST(drvr->wme_dp, eprec);
+		if (eprec == prec && !discard_oldest)
+			return false;	/* refuse newer (incoming) packet */
+		/* Evict packet according to discard policy */
+		p = discard_oldest ? brcmu_pktq_pdeq(q, eprec) :
+			brcmu_pktq_pdeq_tail(q, eprec);
+		if (p == NULL) {
+			BRCMF_ERROR(("%s: brcmu_pktq_penq() failed, oldest %d.",
+				     __func__, discard_oldest));
+		}
+		brcmu_pkt_buf_free_skb(p);
+	}
+
+	/* Enqueue */
+	p = brcmu_pktq_penq(q, prec, pkt);
+	if (p == NULL) {
+		BRCMF_ERROR(("%s: brcmu_pktq_penq() failed.", __func__));
+	}
+
+	return p != NULL;
+}
+
+static int
+brcmf_c_iovar_op(struct brcmf_pub *drvr, const char *name,
+	     void *params, int plen, void *arg, int len, bool set)
+{
+	int bcmerror = 0;
+	int val_size;
+	const struct brcmu_iovar *vi = NULL;
+	u32 actionid;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (name == NULL || len <= 0)
+		return -EINVAL;
+
+	/* Set does not take qualifiers */
+	if (set && (params || plen))
+		return -EINVAL;
+
+	/* Get must have return space;*/
+	if (!set && !(arg && len))
+		return -EINVAL;
+
+	vi = brcmu_iovar_lookup(brcmf_iovars, name);
+	if (vi == NULL) {
+		bcmerror = -ENOTSUPP;
+		goto exit;
+	}
+
+	BRCMF_CTL(("%s: %s %s, len %d plen %d\n", __func__,
+		   name, (set ? "set" : "get"), len, plen));
+
+	/* set up 'params' pointer in case this is a set command so that
+	 * the convenience int and bool code can be common to set and get
+	 */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		/* all other types are integer sized */
+		val_size = sizeof(int);
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	bcmerror =
+	    brcmf_c_doiovar(drvr, vi, actionid, name, params, plen, arg, len,
+			val_size);
+
+exit:
+	return bcmerror;
+}
+
+int brcmf_c_ioctl(struct brcmf_pub *drvr, struct brcmf_c_ioctl *ioc, void *buf,
+		  uint buflen)
+{
+	int bcmerror = 0;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (!buf)
+		return -EINVAL;
+
+	switch (ioc->cmd) {
+	case BRCMF_GET_MAGIC:
+		if (buflen < sizeof(int))
+			bcmerror = -EOVERFLOW;
+		else
+			*(int *)buf = BRCMF_IOCTL_MAGIC;
+		break;
+
+	case BRCMF_GET_VERSION:
+		if (buflen < sizeof(int))
+			bcmerror = -EOVERFLOW;
+		else
+			*(int *)buf = BRCMF_IOCTL_VERSION;
+		break;
+
+	case BRCMF_GET_VAR:
+	case BRCMF_SET_VAR:{
+			char *arg;
+			uint arglen;
+
+			/* scan past the name to any arguments */
+			for (arg = buf, arglen = buflen; *arg && arglen;
+			     arg++, arglen--)
+				;
+
+			if (*arg) {
+				bcmerror = -EOVERFLOW;
+				break;
+			}
+
+			/* account for the NUL terminator */
+			arg++, arglen--;
+
+			/* call with the appropriate arguments */
+			if (ioc->cmd == BRCMF_GET_VAR)
+				bcmerror = brcmf_c_iovar_op(drvr, buf, arg,
+						arglen, buf, buflen, IOV_GET);
+			else
+				bcmerror =
+				    brcmf_c_iovar_op(drvr, buf, NULL, 0, arg,
+						     arglen, IOV_SET);
+			if (bcmerror != -ENOTSUPP)
+				break;
+
+			/* if still not found, try bus module */
+			if (ioc->cmd == BRCMF_GET_VAR)
+				bcmerror = brcmf_sdbrcm_bus_iovar_op(drvr,
+						buf, arg, arglen, buf, buflen,
+						IOV_GET);
+			else
+				bcmerror = brcmf_sdbrcm_bus_iovar_op(drvr,
+						buf, NULL, 0, arg, arglen,
+						IOV_SET);
+
+			break;
+		}
+
+	default:
+		bcmerror = -ENOTSUPP;
+	}
+
+	return bcmerror;
+}
+
+#ifdef SHOW_EVENTS
+static void
+brcmf_c_show_host_event(struct brcmf_event_msg *event, void *event_data)
+{
+	uint i, status, reason;
+	bool group = false, flush_txq = false, link = false;
+	char *auth_str, *event_name;
+	unsigned char *buf;
+	char err_msg[256], eabuf[ETHER_ADDR_STR_LEN];
+	static struct {
+		uint event;
+		char *event_name;
+	} event_names[] = {
+		{
+		BRCMF_E_SET_SSID, "SET_SSID"}, {
+		BRCMF_E_JOIN, "JOIN"}, {
+		BRCMF_E_START, "START"}, {
+		BRCMF_E_AUTH, "AUTH"}, {
+		BRCMF_E_AUTH_IND, "AUTH_IND"}, {
+		BRCMF_E_DEAUTH, "DEAUTH"}, {
+		BRCMF_E_DEAUTH_IND, "DEAUTH_IND"}, {
+		BRCMF_E_ASSOC, "ASSOC"}, {
+		BRCMF_E_ASSOC_IND, "ASSOC_IND"}, {
+		BRCMF_E_REASSOC, "REASSOC"}, {
+		BRCMF_E_REASSOC_IND, "REASSOC_IND"}, {
+		BRCMF_E_DISASSOC, "DISASSOC"}, {
+		BRCMF_E_DISASSOC_IND, "DISASSOC_IND"}, {
+		BRCMF_E_QUIET_START, "START_QUIET"}, {
+		BRCMF_E_QUIET_END, "END_QUIET"}, {
+		BRCMF_E_BEACON_RX, "BEACON_RX"}, {
+		BRCMF_E_LINK, "LINK"}, {
+		BRCMF_E_MIC_ERROR, "MIC_ERROR"}, {
+		BRCMF_E_NDIS_LINK, "NDIS_LINK"}, {
+		BRCMF_E_ROAM, "ROAM"}, {
+		BRCMF_E_TXFAIL, "TXFAIL"}, {
+		BRCMF_E_PMKID_CACHE, "PMKID_CACHE"}, {
+		BRCMF_E_RETROGRADE_TSF, "RETROGRADE_TSF"}, {
+		BRCMF_E_PRUNE, "PRUNE"}, {
+		BRCMF_E_AUTOAUTH, "AUTOAUTH"}, {
+		BRCMF_E_EAPOL_MSG, "EAPOL_MSG"}, {
+		BRCMF_E_SCAN_COMPLETE, "SCAN_COMPLETE"}, {
+		BRCMF_E_ADDTS_IND, "ADDTS_IND"}, {
+		BRCMF_E_DELTS_IND, "DELTS_IND"}, {
+		BRCMF_E_BCNSENT_IND, "BCNSENT_IND"}, {
+		BRCMF_E_BCNRX_MSG, "BCNRX_MSG"}, {
+		BRCMF_E_BCNLOST_MSG, "BCNLOST_MSG"}, {
+		BRCMF_E_ROAM_PREP, "ROAM_PREP"}, {
+		BRCMF_E_PFN_NET_FOUND, "PNO_NET_FOUND"}, {
+		BRCMF_E_PFN_NET_LOST, "PNO_NET_LOST"}, {
+		BRCMF_E_RESET_COMPLETE, "RESET_COMPLETE"}, {
+		BRCMF_E_JOIN_START, "JOIN_START"}, {
+		BRCMF_E_ROAM_START, "ROAM_START"}, {
+		BRCMF_E_ASSOC_START, "ASSOC_START"}, {
+		BRCMF_E_IBSS_ASSOC, "IBSS_ASSOC"}, {
+		BRCMF_E_RADIO, "RADIO"}, {
+		BRCMF_E_PSM_WATCHDOG, "PSM_WATCHDOG"}, {
+		BRCMF_E_PROBREQ_MSG, "PROBREQ_MSG"}, {
+		BRCMF_E_SCAN_CONFIRM_IND, "SCAN_CONFIRM_IND"}, {
+		BRCMF_E_PSK_SUP, "PSK_SUP"}, {
+		BRCMF_E_COUNTRY_CODE_CHANGED, "COUNTRY_CODE_CHANGED"}, {
+		BRCMF_E_EXCEEDED_MEDIUM_TIME, "EXCEEDED_MEDIUM_TIME"}, {
+		BRCMF_E_ICV_ERROR, "ICV_ERROR"}, {
+		BRCMF_E_UNICAST_DECODE_ERROR, "UNICAST_DECODE_ERROR"}, {
+		BRCMF_E_MULTICAST_DECODE_ERROR, "MULTICAST_DECODE_ERROR"}, {
+		BRCMF_E_TRACE, "TRACE"}, {
+		BRCMF_E_ACTION_FRAME, "ACTION FRAME"}, {
+		BRCMF_E_ACTION_FRAME_COMPLETE, "ACTION FRAME TX COMPLETE"}, {
+		BRCMF_E_IF, "IF"}, {
+		BRCMF_E_RSSI, "RSSI"}, {
+		BRCMF_E_PFN_SCAN_COMPLETE, "SCAN_COMPLETE"}
+	};
+	uint event_type, flags, auth_type, datalen;
+	event_type = be32_to_cpu(event->event_type);
+	flags = be16_to_cpu(event->flags);
+	status = be32_to_cpu(event->status);
+	reason = be32_to_cpu(event->reason);
+	auth_type = be32_to_cpu(event->auth_type);
+	datalen = be32_to_cpu(event->datalen);
+	/* debug dump of event messages */
+	sprintf(eabuf, "%pM", event->addr);
+
+	event_name = "UNKNOWN";
+	for (i = 0; i < ARRAY_SIZE(event_names); i++) {
+		if (event_names[i].event == event_type)
+			event_name = event_names[i].event_name;
+	}
+
+	BRCMF_EVENT(("EVENT: %s, event ID = %d\n", event_name, event_type));
+	BRCMF_EVENT(("flags 0x%04x, status %d, reason %d, auth_type %d"
+		     " MAC %s\n", flags, status, reason, auth_type, eabuf));
+
+	if (flags & BRCMF_EVENT_MSG_LINK)
+		link = true;
+	if (flags & BRCMF_EVENT_MSG_GROUP)
+		group = true;
+	if (flags & BRCMF_EVENT_MSG_FLUSHTXQ)
+		flush_txq = true;
+
+	switch (event_type) {
+	case BRCMF_E_START:
+	case BRCMF_E_DEAUTH:
+	case BRCMF_E_DISASSOC:
+		BRCMF_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		break;
+
+	case BRCMF_E_ASSOC_IND:
+	case BRCMF_E_REASSOC_IND:
+		BRCMF_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		break;
+
+	case BRCMF_E_ASSOC:
+	case BRCMF_E_REASSOC:
+		if (status == BRCMF_E_STATUS_SUCCESS) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, SUCCESS\n",
+				     event_name, eabuf));
+		} else if (status == BRCMF_E_STATUS_TIMEOUT) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, TIMEOUT\n",
+				     event_name, eabuf));
+		} else if (status == BRCMF_E_STATUS_FAIL) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, FAILURE,"
+				     " reason %d\n", event_name, eabuf,
+				     (int)reason));
+		} else {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, unexpected status "
+				     "%d\n", event_name, eabuf, (int)status));
+		}
+		break;
+
+	case BRCMF_E_DEAUTH_IND:
+	case BRCMF_E_DISASSOC_IND:
+		BRCMF_EVENT(("MACEVENT: %s, MAC %s, reason %d\n", event_name,
+			     eabuf, (int)reason));
+		break;
+
+	case BRCMF_E_AUTH:
+	case BRCMF_E_AUTH_IND:
+		if (auth_type == WLAN_AUTH_OPEN)
+			auth_str = "Open System";
+		else if (auth_type == WLAN_AUTH_SHARED_KEY)
+			auth_str = "Shared Key";
+		else {
+			sprintf(err_msg, "AUTH unknown: %d", (int)auth_type);
+			auth_str = err_msg;
+		}
+		if (event_type == BRCMF_E_AUTH_IND) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, %s\n", event_name,
+				     eabuf, auth_str));
+		} else if (status == BRCMF_E_STATUS_SUCCESS) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, %s, SUCCESS\n",
+				     event_name, eabuf, auth_str));
+		} else if (status == BRCMF_E_STATUS_TIMEOUT) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, %s, TIMEOUT\n",
+				     event_name, eabuf, auth_str));
+		} else if (status == BRCMF_E_STATUS_FAIL) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s, %s, FAILURE, "
+				     "reason %d\n",
+				     event_name, eabuf, auth_str, (int)reason));
+		}
+
+		break;
+
+	case BRCMF_E_JOIN:
+	case BRCMF_E_ROAM:
+	case BRCMF_E_SET_SSID:
+		if (status == BRCMF_E_STATUS_SUCCESS) {
+			BRCMF_EVENT(("MACEVENT: %s, MAC %s\n", event_name,
+				     eabuf));
+		} else if (status == BRCMF_E_STATUS_FAIL) {
+			BRCMF_EVENT(("MACEVENT: %s, failed\n", event_name));
+		} else if (status == BRCMF_E_STATUS_NO_NETWORKS) {
+			BRCMF_EVENT(("MACEVENT: %s, no networks found\n",
+				     event_name));
+		} else {
+			BRCMF_EVENT(("MACEVENT: %s, unexpected status %d\n",
+				     event_name, (int)status));
+		}
+		break;
+
+	case BRCMF_E_BEACON_RX:
+		if (status == BRCMF_E_STATUS_SUCCESS) {
+			BRCMF_EVENT(("MACEVENT: %s, SUCCESS\n", event_name));
+		} else if (status == BRCMF_E_STATUS_FAIL) {
+			BRCMF_EVENT(("MACEVENT: %s, FAIL\n", event_name));
+		} else {
+			BRCMF_EVENT(("MACEVENT: %s, status %d\n", event_name,
+				     status));
+		}
+		break;
+
+	case BRCMF_E_LINK:
+		BRCMF_EVENT(("MACEVENT: %s %s\n", event_name,
+			     link ? "UP" : "DOWN"));
+		break;
+
+	case BRCMF_E_MIC_ERROR:
+		BRCMF_EVENT(("MACEVENT: %s, MAC %s, Group %d, Flush %d\n",
+			     event_name, eabuf, group, flush_txq));
+		break;
+
+	case BRCMF_E_ICV_ERROR:
+	case BRCMF_E_UNICAST_DECODE_ERROR:
+	case BRCMF_E_MULTICAST_DECODE_ERROR:
+		BRCMF_EVENT(("MACEVENT: %s, MAC %s\n", event_name, eabuf));
+		break;
+
+	case BRCMF_E_TXFAIL:
+		BRCMF_EVENT(("MACEVENT: %s, RA %s\n", event_name, eabuf));
+		break;
+
+	case BRCMF_E_SCAN_COMPLETE:
+	case BRCMF_E_PMKID_CACHE:
+		BRCMF_EVENT(("MACEVENT: %s\n", event_name));
+		break;
+
+	case BRCMF_E_PFN_NET_FOUND:
+	case BRCMF_E_PFN_NET_LOST:
+	case BRCMF_E_PFN_SCAN_COMPLETE:
+		BRCMF_EVENT(("PNOEVENT: %s\n", event_name));
+		break;
+
+	case BRCMF_E_PSK_SUP:
+	case BRCMF_E_PRUNE:
+		BRCMF_EVENT(("MACEVENT: %s, status %d, reason %d\n",
+			   event_name, (int)status, (int)reason));
+		break;
+
+	case BRCMF_E_TRACE:
+		{
+			static u32 seqnum_prev;
+			struct msgtrace_hdr hdr;
+			u32 nblost;
+			char *s, *p;
+
+			buf = (unsigned char *) event_data;
+			memcpy(&hdr, buf, sizeof(struct msgtrace_hdr));
+
+			if (hdr.version != MSGTRACE_VERSION) {
+				BRCMF_ERROR(
+				    ("\nMACEVENT: %s [unsupported version --> "
+				     "brcmf version:%d dongle version:%d]\n",
+				     event_name, MSGTRACE_VERSION, hdr.version)
+				);
+				/* Reset datalen to avoid display below */
+				datalen = 0;
+				break;
+			}
+
+			/* There are 2 bytes available at the end of data */
+			*(buf + sizeof(struct msgtrace_hdr)
+				 + be16_to_cpu(hdr.len)) = '\0';
+
+			if (be32_to_cpu(hdr.discarded_bytes)
+			    || be32_to_cpu(hdr.discarded_printf)) {
+				BRCMF_ERROR(
+				    ("\nWLC_E_TRACE: [Discarded traces in dongle -->"
+				     "discarded_bytes %d discarded_printf %d]\n",
+				     be32_to_cpu(hdr.discarded_bytes),
+				     be32_to_cpu(hdr.discarded_printf)));
+			}
+
+			nblost = be32_to_cpu(hdr.seqnum) - seqnum_prev - 1;
+			if (nblost > 0) {
+				BRCMF_ERROR(
+				    ("\nWLC_E_TRACE: [Event lost --> seqnum %d nblost %d\n",
+				     be32_to_cpu(hdr.seqnum), nblost));
+			}
+			seqnum_prev = be32_to_cpu(hdr.seqnum);
+
+			/* Display the trace buffer. Advance from \n to \n to
+			 * avoid display big
+			 * printf (issue with Linux printk )
+			 */
+			p = (char *)&buf[sizeof(struct msgtrace_hdr)];
+			while ((s = strstr(p, "\n")) != NULL) {
+				*s = '\0';
+				printk(KERN_DEBUG"%s\n", p);
+				p = s + 1;
+			}
+			printk(KERN_DEBUG "%s\n", p);
+
+			/* Reset datalen to avoid display below */
+			datalen = 0;
+		}
+		break;
+
+	case BRCMF_E_RSSI:
+		BRCMF_EVENT(("MACEVENT: %s %d\n", event_name,
+			     be32_to_cpu(*((int *)event_data))));
+		break;
+
+	default:
+		BRCMF_EVENT(("MACEVENT: %s %d, MAC %s, status %d, reason %d, "
+			     "auth %d\n", event_name, event_type, eabuf,
+			     (int)status, (int)reason, (int)auth_type));
+		break;
+	}
+
+	/* show any appended data */
+	if (datalen) {
+		buf = (unsigned char *) event_data;
+		BRCMF_EVENT((" data (%d) : ", datalen));
+		for (i = 0; i < datalen; i++)
+			BRCMF_EVENT((" 0x%02x ", *buf++));
+		BRCMF_EVENT(("\n"));
+	}
+}
+#endif				/* SHOW_EVENTS */
+
+int
+brcmf_c_host_event(struct brcmf_info *drvr_priv, int *ifidx, void *pktdata,
+		   struct brcmf_event_msg *event, void **data_ptr)
+{
+	/* check whether packet is a BRCM event pkt */
+	struct brcmf_event *pvt_data = (struct brcmf_event *) pktdata;
+	char *event_data;
+	u32 type, status;
+	u16 flags;
+	int evlen;
+
+	if (memcmp(BRCM_OUI, &pvt_data->hdr.oui[0], DOT11_OUI_LEN)) {
+		BRCMF_ERROR(("%s: mismatched OUI, bailing\n", __func__));
+		return -EBADE;
+	}
+
+	/* BRCM event pkt may be unaligned - use xxx_ua to load user_subtype. */
+	if (get_unaligned_be16(&pvt_data->hdr.usr_subtype) !=
+	    BCMILCP_BCM_SUBTYPE_EVENT) {
+		BRCMF_ERROR(("%s: mismatched subtype, bailing\n", __func__));
+		return -EBADE;
+	}
+
+	*data_ptr = &pvt_data[1];
+	event_data = *data_ptr;
+
+	/* memcpy since BRCM event pkt may be unaligned. */
+	memcpy(event, &pvt_data->msg, sizeof(struct brcmf_event_msg));
+
+	type = get_unaligned_be32(&event->event_type);
+	flags = get_unaligned_be16(&event->flags);
+	status = get_unaligned_be32(&event->status);
+	evlen = get_unaligned_be32(&event->datalen) +
+		sizeof(struct brcmf_event);
+
+	switch (type) {
+	case BRCMF_E_IF:
+		{
+			struct brcmf_if_event *ifevent =
+					(struct brcmf_if_event *) event_data;
+			BRCMF_TRACE(("%s: if event\n", __func__));
+
+			if (ifevent->ifidx > 0 &&
+				 ifevent->ifidx < BRCMF_MAX_IFS) {
+				if (ifevent->action == BRCMF_E_IF_ADD)
+					brcmf_add_if(drvr_priv, ifevent->ifidx,
+						   NULL, event->ifname,
+						   pvt_data->eth.h_dest,
+						   ifevent->flags,
+						   ifevent->bssidx);
+				else
+					brcmf_del_if(drvr_priv, ifevent->ifidx);
+			} else {
+				BRCMF_ERROR(("%s: Invalid ifidx %d for %s\n",
+					     __func__, ifevent->ifidx,
+					     event->ifname));
+			}
+		}
+		/* send up the if event: btamp user needs it */
+		*ifidx = brcmf_ifname2idx(drvr_priv, event->ifname);
+		break;
+
+		/* These are what external supplicant/authenticator wants */
+	case BRCMF_E_LINK:
+	case BRCMF_E_ASSOC_IND:
+	case BRCMF_E_REASSOC_IND:
+	case BRCMF_E_DISASSOC_IND:
+	case BRCMF_E_MIC_ERROR:
+	default:
+		/* Fall through: this should get _everything_  */
+
+		*ifidx = brcmf_ifname2idx(drvr_priv, event->ifname);
+		BRCMF_TRACE(("%s: MAC event %d, flags %x, status %x\n",
+			     __func__, type, flags, status));
+
+		/* put it back to BRCMF_E_NDIS_LINK */
+		if (type == BRCMF_E_NDIS_LINK) {
+			u32 temp;
+
+			temp = get_unaligned_be32(&event->event_type);
+			BRCMF_TRACE(("Converted to WLC_E_LINK type %d\n",
+				     temp));
+
+			temp = be32_to_cpu(BRCMF_E_NDIS_LINK);
+			memcpy((void *)(&pvt_data->msg.event_type), &temp,
+			       sizeof(pvt_data->msg.event_type));
+		}
+		break;
+	}
+
+#ifdef SHOW_EVENTS
+	brcmf_c_show_host_event(event, event_data);
+#endif				/* SHOW_EVENTS */
+
+	return 0;
+}
+
+/* Convert user's input in hex pattern to byte-size mask */
+static int brcmf_c_pattern_atoh(char *src, char *dst)
+{
+	int i;
+	if (strncmp(src, "0x", 2) != 0 && strncmp(src, "0X", 2) != 0) {
+		BRCMF_ERROR(("Mask invalid format. Needs to start with 0x\n"));
+		return -1;
+	}
+	src = src + 2;		/* Skip past 0x */
+	if (strlen(src) % 2 != 0) {
+		BRCMF_ERROR(("Mask invalid format. Length must be even.\n"));
+		return -1;
+	}
+	for (i = 0; *src != '\0'; i++) {
+		char num[3];
+		strncpy(num, src, 2);
+		num[2] = '\0';
+		dst[i] = (u8) simple_strtoul(num, NULL, 16);
+		src += 2;
+	}
+	return i;
+}
+
+void
+brcmf_c_pktfilter_offload_enable(struct brcmf_pub *drvr, char *arg, int enable,
+			     int master_mode)
+{
+	char *argv[8];
+	int i = 0;
+	const char *str;
+	int buf_len;
+	int str_len;
+	char *arg_save = 0, *arg_org = 0;
+	int rc;
+	char buf[128];
+	struct brcmf_pkt_filter_enable enable_parm;
+	struct brcmf_pkt_filter_enable *pkt_filterp;
+
+	arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC);
+	if (!arg_save) {
+		BRCMF_ERROR(("%s: kmalloc failed\n", __func__));
+		goto fail;
+	}
+	arg_org = arg_save;
+	memcpy(arg_save, arg, strlen(arg) + 1);
+
+	argv[i] = strsep(&arg_save, " ");
+
+	i = 0;
+	if (NULL == argv[i]) {
+		BRCMF_ERROR(("No args provided\n"));
+		goto fail;
+	}
+
+	str = "pkt_filter_enable";
+	str_len = strlen(str);
+	strncpy(buf, str, str_len);
+	buf[str_len] = '\0';
+	buf_len = str_len + 1;
+
+	pkt_filterp = (struct brcmf_pkt_filter_enable *) (buf + str_len + 1);
+
+	/* Parse packet filter id. */
+	enable_parm.id = simple_strtoul(argv[i], NULL, 0);
+
+	/* Parse enable/disable value. */
+	enable_parm.enable = enable;
+
+	buf_len += sizeof(enable_parm);
+	memcpy((char *)pkt_filterp, &enable_parm, sizeof(enable_parm));
+
+	/* Enable/disable the specified filter. */
+	rc = brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len);
+	rc = rc >= 0 ? 0 : rc;
+	if (rc)
+		BRCMF_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+			     __func__, arg, rc));
+	else
+		BRCMF_TRACE(("%s: successfully added pktfilter %s\n",
+			     __func__, arg));
+
+	/* Contorl the master mode */
+	brcmu_mkiovar("pkt_filter_mode", (char *)&master_mode, 4, buf,
+		    sizeof(buf));
+	rc = brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, buf,
+				       sizeof(buf));
+	rc = rc >= 0 ? 0 : rc;
+	if (rc)
+		BRCMF_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+			     __func__, arg, rc));
+
+fail:
+	kfree(arg_org);
+}
+
+void brcmf_c_pktfilter_offload_set(struct brcmf_pub *drvr, char *arg)
+{
+	const char *str;
+	struct brcmf_pkt_filter pkt_filter;
+	struct brcmf_pkt_filter *pkt_filterp;
+	int buf_len;
+	int str_len;
+	int rc;
+	u32 mask_size;
+	u32 pattern_size;
+	char *argv[8], *buf = 0;
+	int i = 0;
+	char *arg_save = 0, *arg_org = 0;
+
+	arg_save = kmalloc(strlen(arg) + 1, GFP_ATOMIC);
+	if (!arg_save) {
+		BRCMF_ERROR(("%s: kmalloc failed\n", __func__));
+		goto fail;
+	}
+
+	arg_org = arg_save;
+
+	buf = kmalloc(PKTFILTER_BUF_SIZE, GFP_ATOMIC);
+	if (!buf) {
+		BRCMF_ERROR(("%s: kmalloc failed\n", __func__));
+		goto fail;
+	}
+
+	strcpy(arg_save, arg);
+
+	argv[i] = strsep(&arg_save, " ");
+	while (argv[i++])
+		argv[i] = strsep(&arg_save, " ");
+
+	i = 0;
+	if (NULL == argv[i]) {
+		BRCMF_ERROR(("No args provided\n"));
+		goto fail;
+	}
+
+	str = "pkt_filter_add";
+	strcpy(buf, str);
+	str_len = strlen(str);
+	buf_len = str_len + 1;
+
+	pkt_filterp = (struct brcmf_pkt_filter *) (buf + str_len + 1);
+
+	/* Parse packet filter id. */
+	pkt_filter.id = simple_strtoul(argv[i], NULL, 0);
+
+	if (NULL == argv[++i]) {
+		BRCMF_ERROR(("Polarity not provided\n"));
+		goto fail;
+	}
+
+	/* Parse filter polarity. */
+	pkt_filter.negate_match = simple_strtoul(argv[i], NULL, 0);
+
+	if (NULL == argv[++i]) {
+		BRCMF_ERROR(("Filter type not provided\n"));
+		goto fail;
+	}
+
+	/* Parse filter type. */
+	pkt_filter.type = simple_strtoul(argv[i], NULL, 0);
+
+	if (NULL == argv[++i]) {
+		BRCMF_ERROR(("Offset not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter offset. */
+	pkt_filter.u.pattern.offset = simple_strtoul(argv[i], NULL, 0);
+
+	if (NULL == argv[++i]) {
+		BRCMF_ERROR(("Bitmask not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter mask. */
+	mask_size =
+	    brcmf_c_pattern_atoh
+		   (argv[i], (char *)pkt_filterp->u.pattern.mask_and_pattern);
+
+	if (NULL == argv[++i]) {
+		BRCMF_ERROR(("Pattern not provided\n"));
+		goto fail;
+	}
+
+	/* Parse pattern filter pattern. */
+	pattern_size =
+	    brcmf_c_pattern_atoh(argv[i],
+				   (char *)&pkt_filterp->u.pattern.
+				   mask_and_pattern[mask_size]);
+
+	if (mask_size != pattern_size) {
+		BRCMF_ERROR(("Mask and pattern not the same size\n"));
+		goto fail;
+	}
+
+	pkt_filter.u.pattern.size_bytes = mask_size;
+	buf_len += BRCMF_PKT_FILTER_FIXED_LEN;
+	buf_len += (BRCMF_PKT_FILTER_PATTERN_FIXED_LEN + 2 * mask_size);
+
+	/* Keep-alive attributes are set in local
+	 * variable (keep_alive_pkt), and
+	 ** then memcpy'ed into buffer (keep_alive_pktp) since there is no
+	 ** guarantee that the buffer is properly aligned.
+	 */
+	memcpy((char *)pkt_filterp,
+	       &pkt_filter,
+	       BRCMF_PKT_FILTER_FIXED_LEN + BRCMF_PKT_FILTER_PATTERN_FIXED_LEN);
+
+	rc = brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, buf, buf_len);
+	rc = rc >= 0 ? 0 : rc;
+
+	if (rc)
+		BRCMF_TRACE(("%s: failed to add pktfilter %s, retcode = %d\n",
+			     __func__, arg, rc));
+	else
+		BRCMF_TRACE(("%s: successfully added pktfilter %s\n",
+			     __func__, arg));
+
+fail:
+	kfree(arg_org);
+
+	kfree(buf);
+}
+
+void brcmf_c_arp_offload_set(struct brcmf_pub *drvr, int arp_mode)
+{
+	char iovbuf[32];
+	int retcode;
+
+	brcmu_mkiovar("arp_ol", (char *)&arp_mode, 4, iovbuf, sizeof(iovbuf));
+	retcode = brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR,
+				   iovbuf, sizeof(iovbuf));
+	retcode = retcode >= 0 ? 0 : retcode;
+	if (retcode)
+		BRCMF_TRACE(("%s: failed to set ARP offload mode to 0x%x, "
+			     "retcode = %d\n", __func__, arp_mode, retcode));
+	else
+		BRCMF_TRACE(("%s: successfully set ARP offload mode to 0x%x\n",
+			     __func__, arp_mode));
+}
+
+void brcmf_c_arp_offload_enable(struct brcmf_pub *drvr, int arp_enable)
+{
+	char iovbuf[32];
+	int retcode;
+
+	brcmu_mkiovar("arpoe", (char *)&arp_enable, 4, iovbuf, sizeof(iovbuf));
+	retcode = brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR,
+				   iovbuf, sizeof(iovbuf));
+	retcode = retcode >= 0 ? 0 : retcode;
+	if (retcode)
+		BRCMF_TRACE(("%s: failed to enabe ARP offload to %d, "
+			     "retcode = %d\n", __func__, arp_enable, retcode));
+	else
+		BRCMF_TRACE(("%s: successfully enabed ARP offload to %d\n",
+			     __func__, arp_enable));
+}
+
+int brcmf_c_preinit_ioctls(struct brcmf_pub *drvr)
+{
+	char iovbuf[BRCMF_EVENTING_MASK_LEN + 12];	/*  Room for
+				 "event_msgs" + '\0' + bitvec  */
+	uint up = 0;
+	char buf[128], *ptr;
+	uint power_mode = PM_FAST;
+	u32 dongle_align = BRCMF_SDALIGN;
+	u32 glom = 0;
+	uint bcn_timeout = 3;
+	int scan_assoc_time = 40;
+	int scan_unassoc_time = 40;
+	int i;
+
+	brcmf_os_proto_block(drvr);
+
+	/* Set Country code */
+	if (drvr->country_code[0] != 0) {
+		if (brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_COUNTRY,
+				     drvr->country_code,
+				     sizeof(drvr->country_code)) < 0) {
+			BRCMF_ERROR(("%s: country code setting failed\n",
+				     __func__));
+		}
+	}
+
+	/* query for 'ver' to get version info from firmware */
+	memset(buf, 0, sizeof(buf));
+	ptr = buf;
+	brcmu_mkiovar("ver", 0, 0, buf, sizeof(buf));
+	brcmf_proto_cdc_query_ioctl(drvr, 0, BRCMF_C_GET_VAR, buf, sizeof(buf));
+	strsep(&ptr, "\n");
+	/* Print fw version info */
+	BRCMF_ERROR(("Firmware version = %s\n", buf));
+
+	/* Set PowerSave mode */
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_PM, (char *)&power_mode,
+			 sizeof(power_mode));
+
+	/* Match Host and Dongle rx alignment */
+	brcmu_mkiovar("bus:txglomalign", (char *)&dongle_align, 4, iovbuf,
+		    sizeof(iovbuf));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+				  sizeof(iovbuf));
+
+	/* disable glom option per default */
+	brcmu_mkiovar("bus:txglom", (char *)&glom, 4, iovbuf, sizeof(iovbuf));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+				  sizeof(iovbuf));
+
+	/* Setup timeout if Beacons are lost and roam is off to report
+		 link down */
+	brcmu_mkiovar("bcn_timeout", (char *)&bcn_timeout, 4, iovbuf,
+		    sizeof(iovbuf));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+				  sizeof(iovbuf));
+
+	/* Enable/Disable build-in roaming to allowed ext supplicant to take
+		 of romaing */
+	brcmu_mkiovar("roam_off", (char *)&brcmf_roam, 4,
+		      iovbuf, sizeof(iovbuf));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+				  sizeof(iovbuf));
+
+	/* Force STA UP */
+	if (brcmf_radio_up)
+		brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_UP, (char *)&up,
+					  sizeof(up));
+
+	/* Setup event_msgs */
+	brcmu_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN,
+		      iovbuf, sizeof(iovbuf));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_VAR, iovbuf,
+				  sizeof(iovbuf));
+
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+			 (char *)&scan_assoc_time, sizeof(scan_assoc_time));
+	brcmf_proto_cdc_set_ioctl(drvr, 0, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+			 (char *)&scan_unassoc_time, sizeof(scan_unassoc_time));
+
+	/* Set and enable ARP offload feature */
+	if (brcmf_arp_enable)
+		brcmf_c_arp_offload_set(drvr, brcmf_arp_mode);
+	brcmf_c_arp_offload_enable(drvr, brcmf_arp_enable);
+
+	/* Set up pkt filter */
+	if (brcmf_pkt_filter_enable) {
+		for (i = 0; i < drvr->pktfilter_count; i++) {
+			brcmf_c_pktfilter_offload_set(drvr,
+						  drvr->pktfilter[i]);
+			brcmf_c_pktfilter_offload_enable(drvr,
+			     drvr->pktfilter[i],
+			     brcmf_pkt_filter_init,
+			     brcmf_master_mode);
+		}
+	}
+
+	brcmf_os_proto_unblock(drvr);
+
+	return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
new file mode 100644
index 0000000..5be4d7a
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_dbg.h
@@ -0,0 +1,70 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_DBG_H_
+#define _BRCMF_DBG_H_
+
+#if defined(BCMDBG)
+
+#define BRCMF_ERROR(args) \
+	do {if ((brcmf_msg_level & BRCMF_ERROR_VAL) && (net_ratelimit())) \
+		printk args; } while (0)
+#define BRCMF_TRACE(args)	do {if (brcmf_msg_level & BRCMF_TRACE_VAL) \
+					printk args; } while (0)
+#define BRCMF_INFO(args)	do {if (brcmf_msg_level & BRCMF_INFO_VAL) \
+					printk args; } while (0)
+#define BRCMF_DATA(args)	do {if (brcmf_msg_level & BRCMF_DATA_VAL) \
+					printk args; } while (0)
+#define BRCMF_CTL(args)		do {if (brcmf_msg_level & BRCMF_CTL_VAL) \
+					printk args; } while (0)
+#define BRCMF_TIMER(args)	do {if (brcmf_msg_level & BRCMF_TIMER_VAL) \
+					printk args; } while (0)
+#define BRCMF_INTR(args)	do {if (brcmf_msg_level & BRCMF_INTR_VAL) \
+					printk args; } while (0)
+#define BRCMF_GLOM(args)	do {if (brcmf_msg_level & BRCMF_GLOM_VAL) \
+					printk args; } while (0)
+#define BRCMF_EVENT(args)	do {if (brcmf_msg_level & BRCMF_EVENT_VAL) \
+					printk args; } while (0)
+
+#define BRCMF_DATA_ON()		(brcmf_msg_level & BRCMF_DATA_VAL)
+#define BRCMF_CTL_ON()		(brcmf_msg_level & BRCMF_CTL_VAL)
+#define BRCMF_HDRS_ON()		(brcmf_msg_level & BRCMF_HDRS_VAL)
+#define BRCMF_BYTES_ON()	(brcmf_msg_level & BRCMF_BYTES_VAL)
+#define BRCMF_GLOM_ON()		(brcmf_msg_level & BRCMF_GLOM_VAL)
+
+#else	/* (defined BCMDBG) || (defined BCMDBG) */
+
+#define BRCMF_ERROR(args)  do {if (net_ratelimit()) printk args; } while (0)
+#define BRCMF_TRACE(args)
+#define BRCMF_INFO(args)
+#define BRCMF_DATA(args)
+#define BRCMF_CTL(args)
+#define BRCMF_TIMER(args)
+#define BRCMF_INTR(args)
+#define BRCMF_GLOM(args)
+#define BRCMF_EVENT(args)
+
+#define BRCMF_DATA_ON()		0
+#define BRCMF_CTL_ON()		0
+#define BRCMF_HDRS_ON()		0
+#define BRCMF_BYTES_ON()		0
+#define BRCMF_GLOM_ON()		0
+
+#endif				/* defined(BCMDBG) */
+
+extern int brcmf_msg_level;
+
+#endif				/* _BRCMF_DBG_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
new file mode 100644
index 0000000..380447f
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_linux.c
@@ -0,0 +1,1734 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/random.h>
+#include <linux/spinlock.h>
+#include <linux/ethtool.h>
+#include <linux/fcntl.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <net/cfg80211.h>
+#include <defs.h>
+#include <brcmu_utils.h>
+#include <brcmu_wifi.h>
+
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+#include "wl_cfg80211.h"
+#include "bcmchip.h"
+
+#if defined(CONFIG_PM_SLEEP)
+#include <linux/suspend.h>
+atomic_t brcmf_mmc_suspend;
+#endif	/*  defined(CONFIG_PM_SLEEP) */
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN fullmac driver.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN fullmac cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+
+/* Interface control information */
+struct brcmf_if {
+	struct brcmf_info *info;	/* back pointer to brcmf_info */
+	/* OS/stack specifics */
+	struct net_device *net;
+	struct net_device_stats stats;
+	int idx;		/* iface idx in dongle */
+	int state;		/* interface state */
+	uint subunit;		/* subunit */
+	u8 mac_addr[ETH_ALEN];	/* assigned MAC address */
+	bool attached;		/* Delayed attachment when unset */
+	bool txflowcontrol;	/* Per interface flow control indicator */
+	char name[IFNAMSIZ];	/* linux interface name */
+};
+
+/* Local private structure (extension of pub) */
+struct brcmf_info {
+	struct brcmf_pub pub;
+
+	/* OS/stack specifics */
+	struct brcmf_if *iflist[BRCMF_MAX_IFS];
+
+	struct semaphore proto_sem;
+	wait_queue_head_t ioctl_resp_wait;
+
+	/* Thread to issue ioctl for multicast */
+	struct task_struct *sysioc_tsk;
+	struct semaphore sysioc_sem;
+	bool set_multicast;
+	bool set_macaddress;
+	u8 macvalue[ETH_ALEN];
+	atomic_t pend_8021x_cnt;
+};
+
+/* Error bits */
+module_param(brcmf_msg_level, int, 0);
+
+/* Spawn a thread for system ioctls (set mac, set mcast) */
+uint brcmf_sysioc = true;
+module_param(brcmf_sysioc, uint, 0);
+
+/* ARP offload agent mode : Enable ARP Host Auto-Reply
+and ARP Peer Auto-Reply */
+uint brcmf_arp_mode = 0xb;
+module_param(brcmf_arp_mode, uint, 0);
+
+/* ARP offload enable */
+uint brcmf_arp_enable = true;
+module_param(brcmf_arp_enable, uint, 0);
+
+/* Global Pkt filter enable control */
+uint brcmf_pkt_filter_enable = true;
+module_param(brcmf_pkt_filter_enable, uint, 0);
+
+/*  Pkt filter init setup */
+uint brcmf_pkt_filter_init;
+module_param(brcmf_pkt_filter_init, uint, 0);
+
+/* Pkt filter mode control */
+uint brcmf_master_mode = true;
+module_param(brcmf_master_mode, uint, 1);
+
+module_param(brcmf_dongle_memsize, int, 0);
+
+/* Contorl fw roaming */
+uint brcmf_roam = 1;
+
+/* Control radio state */
+uint brcmf_radio_up = 1;
+
+/* Network inteface name */
+char iface_name[IFNAMSIZ] = "wlan";
+module_param_string(iface_name, iface_name, IFNAMSIZ, 0);
+
+/* The following are specific to the SDIO dongle */
+
+/* IOCTL response timeout */
+int brcmf_ioctl_timeout_msec = IOCTL_RESP_TIMEOUT;
+
+/* Idle timeout for backplane clock */
+int brcmf_idletime = BRCMF_IDLETIME_TICKS;
+module_param(brcmf_idletime, int, 0);
+
+/* Use polling */
+uint brcmf_poll;
+module_param(brcmf_poll, uint, 0);
+
+/* Use interrupts */
+uint brcmf_intr = true;
+module_param(brcmf_intr, uint, 0);
+
+/* SDIO Drive Strength (in milliamps) */
+uint brcmf_sdiod_drive_strength = 6;
+module_param(brcmf_sdiod_drive_strength, uint, 0);
+
+/* Tx/Rx bounds */
+module_param(brcmf_txbound, uint, 0);
+module_param(brcmf_rxbound, uint, 0);
+
+#ifdef SDTEST
+/* Echo packet generator (pkts/s) */
+uint brcmf_pktgen;
+module_param(brcmf_pktgen, uint, 0);
+
+/* Echo packet len (0 => sawtooth, max 2040) */
+uint brcmf_pktgen_len;
+module_param(brcmf_pktgen_len, uint, 0);
+#endif
+
+static int brcmf_toe_get(struct brcmf_info *drvr_priv, int idx, u32 *toe_ol);
+static int brcmf_toe_set(struct brcmf_info *drvr_priv, int idx, u32 toe_ol);
+static int brcmf_host_event(struct brcmf_info *drvr_priv, int *ifidx, void *pktdata,
+			    struct brcmf_event_msg *event_ptr,
+			    void **data_ptr);
+
+/*
+ * Generalized timeout mechanism.  Uses spin sleep with exponential
+ * back-off until
+ * the sleep time reaches one jiffy, then switches over to task delay.  Usage:
+ *
+ *      brcmf_timeout_start(&tmo, usec);
+ *      while (!brcmf_timeout_expired(&tmo))
+ *              if (poll_something())
+ *                      break;
+ *      if (brcmf_timeout_expired(&tmo))
+ *              fatal();
+ */
+
+void brcmf_timeout_start(struct brcmf_timeout *tmo, uint usec)
+{
+	tmo->limit = usec;
+	tmo->increment = 0;
+	tmo->elapsed = 0;
+	tmo->tick = 1000000 / HZ;
+}
+
+int brcmf_timeout_expired(struct brcmf_timeout *tmo)
+{
+	/* Does nothing the first call */
+	if (tmo->increment == 0) {
+		tmo->increment = 1;
+		return 0;
+	}
+
+	if (tmo->elapsed >= tmo->limit)
+		return 1;
+
+	/* Add the delay that's about to take place */
+	tmo->elapsed += tmo->increment;
+
+	if (tmo->increment < tmo->tick) {
+		udelay(tmo->increment);
+		tmo->increment *= 2;
+		if (tmo->increment > tmo->tick)
+			tmo->increment = tmo->tick;
+	} else {
+		wait_queue_head_t delay_wait;
+		DECLARE_WAITQUEUE(wait, current);
+		int pending;
+		init_waitqueue_head(&delay_wait);
+		add_wait_queue(&delay_wait, &wait);
+		set_current_state(TASK_INTERRUPTIBLE);
+		schedule_timeout(1);
+		pending = signal_pending(current);
+		remove_wait_queue(&delay_wait, &wait);
+		set_current_state(TASK_RUNNING);
+		if (pending)
+			return 1;	/* Interrupted */
+	}
+
+	return 0;
+}
+
+static int brcmf_net2idx(struct brcmf_info *drvr_priv, struct net_device *net)
+{
+	int i = 0;
+
+	while (i < BRCMF_MAX_IFS) {
+		if (drvr_priv->iflist[i] && (drvr_priv->iflist[i]->net == net))
+			return i;
+		i++;
+	}
+
+	return BRCMF_BAD_IF;
+}
+
+int brcmf_ifname2idx(struct brcmf_info *drvr_priv, char *name)
+{
+	int i = BRCMF_MAX_IFS;
+
+	if (name == NULL || *name == '\0')
+		return 0;
+
+	while (--i > 0)
+		if (drvr_priv->iflist[i]
+		    && !strncmp(drvr_priv->iflist[i]->name, name, IFNAMSIZ))
+			break;
+
+	BRCMF_TRACE(("%s: return idx %d for \"%s\"\n", __func__, i, name));
+
+	return i;		/* default - the primary interface */
+}
+
+char *brcmf_ifname(struct brcmf_pub *drvr, int ifidx)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	if (ifidx < 0 || ifidx >= BRCMF_MAX_IFS) {
+		BRCMF_ERROR(("%s: ifidx %d out of range\n", __func__, ifidx));
+		return "<if_bad>";
+	}
+
+	if (drvr_priv->iflist[ifidx] == NULL) {
+		BRCMF_ERROR(("%s: null i/f %d\n", __func__, ifidx));
+		return "<if_null>";
+	}
+
+	if (drvr_priv->iflist[ifidx]->net)
+		return drvr_priv->iflist[ifidx]->net->name;
+
+	return "<if_none>";
+}
+
+static void _brcmf_set_multicast_list(struct brcmf_info *drvr_priv, int ifidx)
+{
+	struct net_device *dev;
+	struct netdev_hw_addr *ha;
+	u32 allmulti, cnt;
+
+	struct brcmf_ioctl ioc;
+	char *buf, *bufp;
+	uint buflen;
+	int ret;
+
+	dev = drvr_priv->iflist[ifidx]->net;
+	cnt = netdev_mc_count(dev);
+
+	/* Determine initial value of allmulti flag */
+	allmulti = (dev->flags & IFF_ALLMULTI) ? true : false;
+
+	/* Send down the multicast list first. */
+
+	buflen = sizeof("mcast_list") + sizeof(cnt) + (cnt * ETH_ALEN);
+	bufp = buf = kmalloc(buflen, GFP_ATOMIC);
+	if (!bufp) {
+		BRCMF_ERROR(("%s: out of memory for mcast_list, cnt %d\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx), cnt));
+		return;
+	}
+
+	strcpy(bufp, "mcast_list");
+	bufp += strlen("mcast_list") + 1;
+
+	cnt = cpu_to_le32(cnt);
+	memcpy(bufp, &cnt, sizeof(cnt));
+	bufp += sizeof(cnt);
+
+	netdev_for_each_mc_addr(ha, dev) {
+		if (!cnt)
+			break;
+		memcpy(bufp, ha->addr, ETH_ALEN);
+		bufp += ETH_ALEN;
+		cnt--;
+	}
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = BRCMF_C_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = buflen;
+	ioc.set = true;
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: set mcast_list failed, cnt %d\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx), cnt));
+		allmulti = cnt ? true : allmulti;
+	}
+
+	kfree(buf);
+
+	/* Now send the allmulti setting.  This is based on the setting in the
+	 * net_device flags, but might be modified above to be turned on if we
+	 * were trying to set some addresses and dongle rejected it...
+	 */
+
+	buflen = sizeof("allmulti") + sizeof(allmulti);
+	buf = kmalloc(buflen, GFP_ATOMIC);
+	if (!buf) {
+		BRCMF_ERROR(("%s: out of memory for allmulti\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx)));
+		return;
+	}
+	allmulti = cpu_to_le32(allmulti);
+
+	if (!brcmu_mkiovar
+	    ("allmulti", (void *)&allmulti, sizeof(allmulti), buf, buflen)) {
+		BRCMF_ERROR(("%s: mkiovar failed for allmulti, datalen %d "
+			     "buflen %u\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx),
+			     (int)sizeof(allmulti), buflen));
+		kfree(buf);
+		return;
+	}
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = BRCMF_C_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = buflen;
+	ioc.set = true;
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: set allmulti %d failed\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx),
+			     le32_to_cpu(allmulti)));
+	}
+
+	kfree(buf);
+
+	/* Finally, pick up the PROMISC flag as well, like the NIC
+		 driver does */
+
+	allmulti = (dev->flags & IFF_PROMISC) ? true : false;
+	allmulti = cpu_to_le32(allmulti);
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = BRCMF_C_SET_PROMISC;
+	ioc.buf = &allmulti;
+	ioc.len = sizeof(allmulti);
+	ioc.set = true;
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: set promisc %d failed\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx),
+			     le32_to_cpu(allmulti)));
+	}
+}
+
+static int _brcmf_set_mac_address(struct brcmf_info *drvr_priv, int ifidx, u8 *addr)
+{
+	char buf[32];
+	struct brcmf_ioctl ioc;
+	int ret;
+
+	BRCMF_TRACE(("%s enter\n", __func__));
+	if (!brcmu_mkiovar
+	    ("cur_etheraddr", (char *)addr, ETH_ALEN, buf, 32)) {
+		BRCMF_ERROR(("%s: mkiovar failed for cur_etheraddr\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx)));
+		return -1;
+	}
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = BRCMF_C_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = 32;
+	ioc.set = true;
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: set cur_etheraddr failed\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx)));
+	} else {
+		memcpy(drvr_priv->iflist[ifidx]->net->dev_addr, addr, ETH_ALEN);
+	}
+
+	return ret;
+}
+
+#ifdef SOFTAP
+extern struct net_device *ap_net_dev;
+#endif
+
+/* Virtual interfaces only ((ifp && ifp->info && ifp->idx == true) */
+static void brcmf_op_if(struct brcmf_if *ifp)
+{
+	struct brcmf_info *drvr_priv;
+	int ret = 0, err = 0;
+
+	drvr_priv = ifp->info;
+
+	BRCMF_TRACE(("%s: idx %d, state %d\n", __func__, ifp->idx, ifp->state));
+
+	switch (ifp->state) {
+	case BRCMF_E_IF_ADD:
+		/*
+		 * Delete the existing interface before overwriting it
+		 * in case we missed the BRCMF_E_IF_DEL event.
+		 */
+		if (ifp->net != NULL) {
+			BRCMF_ERROR(("%s: ERROR: netdev:%s already exists, "
+				     "try free & unregister\n",
+				     __func__, ifp->net->name));
+			netif_stop_queue(ifp->net);
+			unregister_netdev(ifp->net);
+			free_netdev(ifp->net);
+		}
+		/* Allocate etherdev, including space for private structure */
+		ifp->net = alloc_etherdev(sizeof(drvr_priv));
+		if (!ifp->net) {
+			BRCMF_ERROR(("%s: OOM - alloc_etherdev\n", __func__));
+			ret = -ENOMEM;
+		}
+		if (ret == 0) {
+			strcpy(ifp->net->name, ifp->name);
+			memcpy(netdev_priv(ifp->net), &drvr_priv, sizeof(drvr_priv));
+			err = brcmf_net_attach(&drvr_priv->pub, ifp->idx);
+			if (err != 0) {
+				BRCMF_ERROR(("%s: brcmf_net_attach failed, "
+					     "err %d\n",
+					     __func__, err));
+				ret = -EOPNOTSUPP;
+			} else {
+#ifdef SOFTAP
+				/* semaphore that the soft AP CODE
+					 waits on */
+				extern struct semaphore ap_eth_sema;
+
+				/* save ptr to wl0.1 netdev for use
+					 in wl_iw.c  */
+				ap_net_dev = ifp->net;
+				/* signal to the SOFTAP 'sleeper' thread,
+					 wl0.1 is ready */
+				up(&ap_eth_sema);
+#endif
+				BRCMF_TRACE(("\n ==== pid:%x, net_device for "
+					     "if:%s created ===\n\n",
+					     current->pid, ifp->net->name));
+				ifp->state = 0;
+			}
+		}
+		break;
+	case BRCMF_E_IF_DEL:
+		if (ifp->net != NULL) {
+			BRCMF_TRACE(("\n%s: got 'WLC_E_IF_DEL' state\n",
+				     __func__));
+			netif_stop_queue(ifp->net);
+			unregister_netdev(ifp->net);
+			ret = BRCMF_DEL_IF;	/* Make sure the free_netdev()
+							 is called */
+		}
+		break;
+	default:
+		BRCMF_ERROR(("%s: bad op %d\n", __func__, ifp->state));
+		break;
+	}
+
+	if (ret < 0) {
+		if (ifp->net)
+			free_netdev(ifp->net);
+
+		drvr_priv->iflist[ifp->idx] = NULL;
+		kfree(ifp);
+#ifdef SOFTAP
+		if (ifp->net == ap_net_dev)
+			ap_net_dev = NULL;	/*  NULL  SOFTAP global
+							 wl0.1 as well */
+#endif				/*  SOFTAP */
+	}
+}
+
+static int _brcmf_sysioc_thread(void *data)
+{
+	struct brcmf_info *drvr_priv = (struct brcmf_info *) data;
+	int i;
+#ifdef SOFTAP
+	bool in_ap = false;
+#endif
+
+	allow_signal(SIGTERM);
+
+	while (down_interruptible(&drvr_priv->sysioc_sem) == 0) {
+		if (kthread_should_stop())
+			break;
+		for (i = 0; i < BRCMF_MAX_IFS; i++) {
+			struct brcmf_if *ifentry = drvr_priv->iflist[i];
+			if (ifentry) {
+#ifdef SOFTAP
+				in_ap = (ap_net_dev != NULL);
+#endif				/* SOFTAP */
+				if (ifentry->state)
+					brcmf_op_if(ifentry);
+#ifdef SOFTAP
+				if (drvr_priv->iflist[i] == NULL) {
+					BRCMF_TRACE(("\n\n %s: interface %d "
+						     "removed!\n", __func__,
+						     i));
+					continue;
+				}
+
+				if (in_ap && drvr_priv->set_macaddress) {
+					BRCMF_TRACE(("attempt to set MAC for"
+						     " %s in AP Mode,"
+						     " blocked.\n",
+						     ifentry->net->name));
+					drvr_priv->set_macaddress = false;
+					continue;
+				}
+
+				if (in_ap && drvr_priv->set_multicast) {
+					BRCMF_TRACE(("attempt to set MULTICAST "
+						     "list for %s in AP Mode, "
+						     "blocked.\n",
+						     ifentry->net->name));
+					drvr_priv->set_multicast = false;
+					continue;
+				}
+#endif				/* SOFTAP */
+				if (drvr_priv->set_multicast) {
+					drvr_priv->set_multicast = false;
+					_brcmf_set_multicast_list(drvr_priv, i);
+				}
+				if (drvr_priv->set_macaddress) {
+					drvr_priv->set_macaddress = false;
+					_brcmf_set_mac_address(drvr_priv, i,
+						drvr_priv->macvalue);
+				}
+			}
+		}
+	}
+	return 0;
+}
+
+static int brcmf_netdev_set_mac_address(struct net_device *dev, void *addr)
+{
+	int ret = 0;
+
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(dev);
+	struct sockaddr *sa = (struct sockaddr *)addr;
+	int ifidx;
+
+	ifidx = brcmf_net2idx(drvr_priv, dev);
+	if (ifidx == BRCMF_BAD_IF)
+		return -1;
+
+	memcpy(&drvr_priv->macvalue, sa->sa_data, ETH_ALEN);
+	drvr_priv->set_macaddress = true;
+	up(&drvr_priv->sysioc_sem);
+
+	return ret;
+}
+
+static void brcmf_netdev_set_multicast_list(struct net_device *dev)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(dev);
+	int ifidx;
+
+	ifidx = brcmf_net2idx(drvr_priv, dev);
+	if (ifidx == BRCMF_BAD_IF)
+		return;
+
+	drvr_priv->set_multicast = true;
+	up(&drvr_priv->sysioc_sem);
+}
+
+int brcmf_sendpkt(struct brcmf_pub *drvr, int ifidx, struct sk_buff *pktbuf)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	/* Reject if down */
+	if (!drvr->up || (drvr->busstate == BRCMF_BUS_DOWN))
+		return -ENODEV;
+
+	/* Update multicast statistic */
+	if (pktbuf->len >= ETH_ALEN) {
+		u8 *pktdata = (u8 *) (pktbuf->data);
+		struct ethhdr *eh = (struct ethhdr *)pktdata;
+
+		if (is_multicast_ether_addr(eh->h_dest))
+			drvr->tx_multicast++;
+		if (ntohs(eh->h_proto) == ETH_P_PAE)
+			atomic_inc(&drvr_priv->pend_8021x_cnt);
+	}
+
+	/* If the protocol uses a data header, apply it */
+	brcmf_proto_hdrpush(drvr, ifidx, pktbuf);
+
+	/* Use bus module to send data frame */
+	return brcmf_sdbrcm_bus_txdata(drvr->bus, pktbuf);
+}
+
+static int brcmf_netdev_start_xmit(struct sk_buff *skb, struct net_device *net)
+{
+	int ret;
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+	int ifidx;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Reject if down */
+	if (!drvr_priv->pub.up || (drvr_priv->pub.busstate == BRCMF_BUS_DOWN)) {
+		BRCMF_ERROR(("%s: xmit rejected pub.up=%d busstate=%d\n",
+			     __func__, drvr_priv->pub.up,
+			     drvr_priv->pub.busstate));
+		netif_stop_queue(net);
+		return -ENODEV;
+	}
+
+	ifidx = brcmf_net2idx(drvr_priv, net);
+	if (ifidx == BRCMF_BAD_IF) {
+		BRCMF_ERROR(("%s: bad ifidx %d\n", __func__, ifidx));
+		netif_stop_queue(net);
+		return -ENODEV;
+	}
+
+	/* Make sure there's enough room for any header */
+	if (skb_headroom(skb) < drvr_priv->pub.hdrlen) {
+		struct sk_buff *skb2;
+
+		BRCMF_INFO(("%s: insufficient headroom\n",
+			    brcmf_ifname(&drvr_priv->pub, ifidx)));
+		drvr_priv->pub.tx_realloc++;
+		skb2 = skb_realloc_headroom(skb, drvr_priv->pub.hdrlen);
+		dev_kfree_skb(skb);
+		skb = skb2;
+		if (skb == NULL) {
+			BRCMF_ERROR(("%s: skb_realloc_headroom failed\n",
+				     brcmf_ifname(&drvr_priv->pub, ifidx)));
+			ret = -ENOMEM;
+			goto done;
+		}
+	}
+
+	ret = brcmf_sendpkt(&drvr_priv->pub, ifidx, skb);
+
+done:
+	if (ret)
+		drvr_priv->pub.dstats.tx_dropped++;
+	else
+		drvr_priv->pub.tx_packets++;
+
+	/* Return ok: we always eat the packet */
+	return 0;
+}
+
+void brcmf_txflowcontrol(struct brcmf_pub *drvr, int ifidx, bool state)
+{
+	struct net_device *net;
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	drvr->txoff = state;
+	net = drvr_priv->iflist[ifidx]->net;
+	if (state == ON)
+		netif_stop_queue(net);
+	else
+		netif_wake_queue(net);
+}
+
+void brcmf_rx_frame(struct brcmf_pub *drvr, int ifidx, struct sk_buff *skb,
+		  int numpkt)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+	unsigned char *eth;
+	uint len;
+	void *data;
+	struct sk_buff *pnext, *save_pktbuf;
+	int i;
+	struct brcmf_if *ifp;
+	struct brcmf_event_msg event;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	save_pktbuf = skb;
+
+	for (i = 0; skb && i < numpkt; i++, skb = pnext) {
+
+		pnext = skb->next;
+		skb->next = NULL;
+
+		/* Get the protocol, maintain skb around eth_type_trans()
+		 * The main reason for this hack is for the limitation of
+		 * Linux 2.4 where 'eth_type_trans' uses the
+		 * 'net->hard_header_len'
+		 * to perform skb_pull inside vs ETH_HLEN. Since to avoid
+		 * coping of the packet coming from the network stack to add
+		 * BDC, Hardware header etc, during network interface
+		 * registration
+		 * we set the 'net->hard_header_len' to ETH_HLEN + extra space
+		 * required
+		 * for BDC, Hardware header etc. and not just the ETH_HLEN
+		 */
+		eth = skb->data;
+		len = skb->len;
+
+		ifp = drvr_priv->iflist[ifidx];
+		if (ifp == NULL)
+			ifp = drvr_priv->iflist[0];
+
+		skb->dev = ifp->net;
+		skb->protocol = eth_type_trans(skb, skb->dev);
+
+		if (skb->pkt_type == PACKET_MULTICAST)
+			drvr_priv->pub.rx_multicast++;
+
+		skb->data = eth;
+		skb->len = len;
+
+		/* Strip header, count, deliver upward */
+		skb_pull(skb, ETH_HLEN);
+
+		/* Process special event packets and then discard them */
+		if (ntohs(skb->protocol) == ETH_P_LINK_CTL)
+			brcmf_host_event(drvr_priv, &ifidx,
+					  skb_mac_header(skb),
+					  &event, &data);
+
+		if (drvr_priv->iflist[ifidx] &&
+		    !drvr_priv->iflist[ifidx]->state)
+			ifp = drvr_priv->iflist[ifidx];
+
+		if (ifp->net)
+			ifp->net->last_rx = jiffies;
+
+		drvr->dstats.rx_bytes += skb->len;
+		drvr->rx_packets++;	/* Local count */
+
+		if (in_interrupt()) {
+			netif_rx(skb);
+		} else {
+			/* If the receive is not processed inside an ISR,
+			 * the softirqd must be woken explicitly to service
+			 * the NET_RX_SOFTIRQ.  In 2.6 kernels, this is handled
+			 * by netif_rx_ni(), but in earlier kernels, we need
+			 * to do it manually.
+			 */
+			netif_rx_ni(skb);
+		}
+	}
+}
+
+void brcmf_txcomplete(struct brcmf_pub *drvr, struct sk_buff *txp, bool success)
+{
+	uint ifidx;
+	struct brcmf_info *drvr_priv = drvr->info;
+	struct ethhdr *eh;
+	u16 type;
+
+	brcmf_proto_hdrpull(drvr, &ifidx, txp);
+
+	eh = (struct ethhdr *)(txp->data);
+	type = ntohs(eh->h_proto);
+
+	if (type == ETH_P_PAE)
+		atomic_dec(&drvr_priv->pend_8021x_cnt);
+
+}
+
+static struct net_device_stats *brcmf_netdev_get_stats(struct net_device *net)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+	struct brcmf_if *ifp;
+	int ifidx;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	ifidx = brcmf_net2idx(drvr_priv, net);
+	if (ifidx == BRCMF_BAD_IF)
+		return NULL;
+
+	ifp = drvr_priv->iflist[ifidx];
+
+	if (drvr_priv->pub.up) {
+		/* Use the protocol to get dongle stats */
+		brcmf_proto_dstats(&drvr_priv->pub);
+	}
+
+	/* Copy dongle stats to net device stats */
+	ifp->stats.rx_packets = drvr_priv->pub.dstats.rx_packets;
+	ifp->stats.tx_packets = drvr_priv->pub.dstats.tx_packets;
+	ifp->stats.rx_bytes = drvr_priv->pub.dstats.rx_bytes;
+	ifp->stats.tx_bytes = drvr_priv->pub.dstats.tx_bytes;
+	ifp->stats.rx_errors = drvr_priv->pub.dstats.rx_errors;
+	ifp->stats.tx_errors = drvr_priv->pub.dstats.tx_errors;
+	ifp->stats.rx_dropped = drvr_priv->pub.dstats.rx_dropped;
+	ifp->stats.tx_dropped = drvr_priv->pub.dstats.tx_dropped;
+	ifp->stats.multicast = drvr_priv->pub.dstats.multicast;
+
+	return &ifp->stats;
+}
+
+/* Retrieve current toe component enables, which are kept
+	 as a bitmap in toe_ol iovar */
+static int brcmf_toe_get(struct brcmf_info *drvr_priv, int ifidx, u32 *toe_ol)
+{
+	struct brcmf_ioctl ioc;
+	char buf[32];
+	int ret;
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	ioc.cmd = BRCMF_C_GET_VAR;
+	ioc.buf = buf;
+	ioc.len = (uint) sizeof(buf);
+	ioc.set = false;
+
+	strcpy(buf, "toe_ol");
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		/* Check for older dongle image that doesn't support toe_ol */
+		if (ret == -EIO) {
+			BRCMF_ERROR(("%s: toe not supported by device\n",
+				     brcmf_ifname(&drvr_priv->pub, ifidx)));
+			return -EOPNOTSUPP;
+		}
+
+		BRCMF_INFO(("%s: could not get toe_ol: ret=%d\n",
+			    brcmf_ifname(&drvr_priv->pub, ifidx), ret));
+		return ret;
+	}
+
+	memcpy(toe_ol, buf, sizeof(u32));
+	return 0;
+}
+
+/* Set current toe component enables in toe_ol iovar,
+	 and set toe global enable iovar */
+static int brcmf_toe_set(struct brcmf_info *drvr_priv, int ifidx, u32 toe_ol)
+{
+	struct brcmf_ioctl ioc;
+	char buf[32];
+	int toe, ret;
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	ioc.cmd = BRCMF_C_SET_VAR;
+	ioc.buf = buf;
+	ioc.len = (uint) sizeof(buf);
+	ioc.set = true;
+
+	/* Set toe_ol as requested */
+
+	strcpy(buf, "toe_ol");
+	memcpy(&buf[sizeof("toe_ol")], &toe_ol, sizeof(u32));
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: could not set toe_ol: ret=%d\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx), ret));
+		return ret;
+	}
+
+	/* Enable toe globally only if any components are enabled. */
+
+	toe = (toe_ol != 0);
+
+	strcpy(buf, "toe");
+	memcpy(&buf[sizeof("toe")], &toe, sizeof(u32));
+
+	ret = brcmf_proto_ioctl(&drvr_priv->pub, ifidx, &ioc, ioc.buf, ioc.len);
+	if (ret < 0) {
+		BRCMF_ERROR(("%s: could not set toe: ret=%d\n",
+			     brcmf_ifname(&drvr_priv->pub, ifidx), ret));
+		return ret;
+	}
+
+	return 0;
+}
+
+static void brcmf_ethtool_get_drvinfo(struct net_device *net,
+				    struct ethtool_drvinfo *info)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+
+	sprintf(info->driver, KBUILD_MODNAME);
+	sprintf(info->version, "%lu", drvr_priv->pub.drv_version);
+	sprintf(info->fw_version, "%s", BCM4329_FW_NAME);
+	sprintf(info->bus_info, "%s",
+		dev_name(&brcmf_cfg80211_get_sdio_func()->dev));
+}
+
+struct ethtool_ops brcmf_ethtool_ops = {
+	.get_drvinfo = brcmf_ethtool_get_drvinfo
+};
+
+static int brcmf_ethtool(struct brcmf_info *drvr_priv, void *uaddr)
+{
+	struct ethtool_drvinfo info;
+	char drvname[sizeof(info.driver)];
+	u32 cmd;
+	struct ethtool_value edata;
+	u32 toe_cmpnt, csum_dir;
+	int ret;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* all ethtool calls start with a cmd word */
+	if (copy_from_user(&cmd, uaddr, sizeof(u32)))
+		return -EFAULT;
+
+	switch (cmd) {
+	case ETHTOOL_GDRVINFO:
+		/* Copy out any request driver name */
+		if (copy_from_user(&info, uaddr, sizeof(info)))
+			return -EFAULT;
+		strncpy(drvname, info.driver, sizeof(info.driver));
+		drvname[sizeof(info.driver) - 1] = '\0';
+
+		/* clear struct for return */
+		memset(&info, 0, sizeof(info));
+		info.cmd = cmd;
+
+		/* if requested, identify ourselves */
+		if (strcmp(drvname, "?dhd") == 0) {
+			sprintf(info.driver, "dhd");
+			strcpy(info.version, BRCMF_VERSION_STR);
+		}
+
+		/* otherwise, require dongle to be up */
+		else if (!drvr_priv->pub.up) {
+			BRCMF_ERROR(("%s: dongle is not up\n", __func__));
+			return -ENODEV;
+		}
+
+		/* finally, report dongle driver type */
+		else if (drvr_priv->pub.iswl)
+			sprintf(info.driver, "wl");
+		else
+			sprintf(info.driver, "xx");
+
+		sprintf(info.version, "%lu", drvr_priv->pub.drv_version);
+		if (copy_to_user(uaddr, &info, sizeof(info)))
+			return -EFAULT;
+		BRCMF_CTL(("%s: given %*s, returning %s\n", __func__,
+			   (int)sizeof(drvname), drvname, info.driver));
+		break;
+
+		/* Get toe offload components from dongle */
+	case ETHTOOL_GRXCSUM:
+	case ETHTOOL_GTXCSUM:
+		ret = brcmf_toe_get(drvr_priv, 0, &toe_cmpnt);
+		if (ret < 0)
+			return ret;
+
+		csum_dir =
+		    (cmd == ETHTOOL_GTXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+		edata.cmd = cmd;
+		edata.data = (toe_cmpnt & csum_dir) ? 1 : 0;
+
+		if (copy_to_user(uaddr, &edata, sizeof(edata)))
+			return -EFAULT;
+		break;
+
+		/* Set toe offload components in dongle */
+	case ETHTOOL_SRXCSUM:
+	case ETHTOOL_STXCSUM:
+		if (copy_from_user(&edata, uaddr, sizeof(edata)))
+			return -EFAULT;
+
+		/* Read the current settings, update and write back */
+		ret = brcmf_toe_get(drvr_priv, 0, &toe_cmpnt);
+		if (ret < 0)
+			return ret;
+
+		csum_dir =
+		    (cmd == ETHTOOL_STXCSUM) ? TOE_TX_CSUM_OL : TOE_RX_CSUM_OL;
+
+		if (edata.data != 0)
+			toe_cmpnt |= csum_dir;
+		else
+			toe_cmpnt &= ~csum_dir;
+
+		ret = brcmf_toe_set(drvr_priv, 0, toe_cmpnt);
+		if (ret < 0)
+			return ret;
+
+		/* If setting TX checksum mode, tell Linux the new mode */
+		if (cmd == ETHTOOL_STXCSUM) {
+			if (edata.data)
+				drvr_priv->iflist[0]->net->features |=
+				    NETIF_F_IP_CSUM;
+			else
+				drvr_priv->iflist[0]->net->features &=
+				    ~NETIF_F_IP_CSUM;
+		}
+
+		break;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+
+	return 0;
+}
+
+static int brcmf_netdev_ioctl_entry(struct net_device *net, struct ifreq *ifr,
+				    int cmd)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+	struct brcmf_c_ioctl ioc;
+	int bcmerror = 0;
+	int buflen = 0;
+	void *buf = NULL;
+	uint driver = 0;
+	int ifidx;
+	bool is_set_key_cmd;
+
+	ifidx = brcmf_net2idx(drvr_priv, net);
+	BRCMF_TRACE(("%s: ifidx %d, cmd 0x%04x\n", __func__, ifidx, cmd));
+
+	if (ifidx == BRCMF_BAD_IF)
+		return -1;
+
+	if (cmd == SIOCETHTOOL)
+		return brcmf_ethtool(drvr_priv, (void *)ifr->ifr_data);
+
+	if (cmd != SIOCDEVPRIVATE)
+		return -EOPNOTSUPP;
+
+	memset(&ioc, 0, sizeof(ioc));
+
+	/* Copy the ioc control structure part of ioctl request */
+	if (copy_from_user(&ioc, ifr->ifr_data, sizeof(struct brcmf_ioctl))) {
+		bcmerror = -EINVAL;
+		goto done;
+	}
+
+	/* Copy out any buffer passed */
+	if (ioc.buf) {
+		buflen = min_t(int, ioc.len, BRCMF_IOCTL_MAXLEN);
+		/* optimization for direct ioctl calls from kernel */
+		/*
+		   if (segment_eq(get_fs(), KERNEL_DS)) {
+		   buf = ioc.buf;
+		   } else {
+		 */
+		{
+			buf = kmalloc(buflen, GFP_ATOMIC);
+			if (!buf) {
+				bcmerror = -ENOMEM;
+				goto done;
+			}
+			if (copy_from_user(buf, ioc.buf, buflen)) {
+				bcmerror = -EINVAL;
+				goto done;
+			}
+		}
+	}
+
+	/* To differentiate read 4 more byes */
+	if ((copy_from_user(&driver, (char *)ifr->ifr_data +
+			    sizeof(struct brcmf_ioctl), sizeof(uint)) != 0)) {
+		bcmerror = -EINVAL;
+		goto done;
+	}
+
+	if (!capable(CAP_NET_ADMIN)) {
+		bcmerror = -EPERM;
+		goto done;
+	}
+
+	/* check for local brcmf ioctl and handle it */
+	if (driver == BRCMF_IOCTL_MAGIC) {
+		bcmerror = brcmf_c_ioctl((void *)&drvr_priv->pub, &ioc, buf, buflen);
+		if (bcmerror)
+			drvr_priv->pub.bcmerror = bcmerror;
+		goto done;
+	}
+
+	/* send to dongle (must be up, and wl) */
+	if ((drvr_priv->pub.busstate != BRCMF_BUS_DATA)) {
+		BRCMF_ERROR(("%s DONGLE_DOWN,__func__\n", __func__));
+		bcmerror = -EIO;
+		goto done;
+	}
+
+	if (!drvr_priv->pub.iswl) {
+		bcmerror = -EIO;
+		goto done;
+	}
+
+	/*
+	 * Intercept BRCMF_C_SET_KEY IOCTL - serialize M4 send and
+	 * set key IOCTL to prevent M4 encryption.
+	 */
+	is_set_key_cmd = ((ioc.cmd == BRCMF_C_SET_KEY) ||
+			  ((ioc.cmd == BRCMF_C_SET_VAR) &&
+			   !(strncmp("wsec_key", ioc.buf, 9))) ||
+			  ((ioc.cmd == BRCMF_C_SET_VAR) &&
+			   !(strncmp("bsscfg:wsec_key", ioc.buf, 15))));
+	if (is_set_key_cmd)
+		brcmf_netdev_wait_pend8021x(net);
+
+	bcmerror =
+	    brcmf_proto_ioctl(&drvr_priv->pub, ifidx, (struct brcmf_ioctl *)&ioc,
+			      buf, buflen);
+
+done:
+	if (!bcmerror && buf && ioc.buf) {
+		if (copy_to_user(ioc.buf, buf, buflen))
+			bcmerror = -EFAULT;
+	}
+
+	kfree(buf);
+
+	if (bcmerror > 0)
+		bcmerror = 0;
+
+	return bcmerror;
+}
+
+static int brcmf_netdev_stop(struct net_device *net)
+{
+#if !defined(IGNORE_ETH0_DOWN)
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+	brcmf_cfg80211_down();
+	if (drvr_priv->pub.up == 0)
+		return 0;
+
+	/* Set state and stop OS transmissions */
+	drvr_priv->pub.up = 0;
+	netif_stop_queue(net);
+#else
+	BRCMF_ERROR(("BYPASS %s:due to BRCM compilation: under investigation\n",
+		     __func__));
+#endif				/* !defined(IGNORE_ETH0_DOWN) */
+
+	return 0;
+}
+
+static int brcmf_netdev_open(struct net_device *net)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **) netdev_priv(net);
+	u32 toe_ol;
+	int ifidx = brcmf_net2idx(drvr_priv, net);
+	s32 ret = 0;
+
+	BRCMF_TRACE(("%s: ifidx %d\n", __func__, ifidx));
+
+	if (ifidx == 0) {	/* do it only for primary eth0 */
+
+		/* try to bring up bus */
+		ret = brcmf_bus_start(&drvr_priv->pub);
+		if (ret != 0) {
+			BRCMF_ERROR(("%s: failed with code %d\n",
+				     __func__, ret));
+			return -1;
+		}
+		atomic_set(&drvr_priv->pend_8021x_cnt, 0);
+
+		memcpy(net->dev_addr, drvr_priv->pub.mac, ETH_ALEN);
+
+		/* Get current TOE mode from dongle */
+		if (brcmf_toe_get(drvr_priv, ifidx, &toe_ol) >= 0
+		    && (toe_ol & TOE_TX_CSUM_OL) != 0)
+			drvr_priv->iflist[ifidx]->net->features |=
+				NETIF_F_IP_CSUM;
+		else
+			drvr_priv->iflist[ifidx]->net->features &=
+				~NETIF_F_IP_CSUM;
+	}
+	/* Allow transmit calls */
+	netif_start_queue(net);
+	drvr_priv->pub.up = 1;
+	if (unlikely(brcmf_cfg80211_up())) {
+		BRCMF_ERROR(("%s: failed to bring up cfg80211\n",
+			     __func__));
+		return -1;
+	}
+
+	return ret;
+}
+
+int
+brcmf_add_if(struct brcmf_info *drvr_priv, int ifidx, void *handle, char *name,
+	   u8 *mac_addr, u32 flags, u8 bssidx)
+{
+	struct brcmf_if *ifp;
+
+	BRCMF_TRACE(("%s: idx %d, handle->%p\n", __func__, ifidx, handle));
+
+	ifp = drvr_priv->iflist[ifidx];
+	if (!ifp) {
+		ifp = kmalloc(sizeof(struct brcmf_if), GFP_ATOMIC);
+		if (!ifp) {
+			BRCMF_ERROR(("%s: OOM - struct brcmf_if\n", __func__));
+			return -ENOMEM;
+		}
+	}
+
+	memset(ifp, 0, sizeof(struct brcmf_if));
+	ifp->info = drvr_priv;
+	drvr_priv->iflist[ifidx] = ifp;
+	strlcpy(ifp->name, name, IFNAMSIZ);
+	if (mac_addr != NULL)
+		memcpy(&ifp->mac_addr, mac_addr, ETH_ALEN);
+
+	if (handle == NULL) {
+		ifp->state = BRCMF_E_IF_ADD;
+		ifp->idx = ifidx;
+		up(&drvr_priv->sysioc_sem);
+	} else
+		ifp->net = (struct net_device *)handle;
+
+	return 0;
+}
+
+void brcmf_del_if(struct brcmf_info *drvr_priv, int ifidx)
+{
+	struct brcmf_if *ifp;
+
+	BRCMF_TRACE(("%s: idx %d\n", __func__, ifidx));
+
+	ifp = drvr_priv->iflist[ifidx];
+	if (!ifp) {
+		BRCMF_ERROR(("%s: Null interface\n", __func__));
+		return;
+	}
+
+	ifp->state = BRCMF_E_IF_DEL;
+	ifp->idx = ifidx;
+	up(&drvr_priv->sysioc_sem);
+}
+
+struct brcmf_pub *brcmf_attach(struct brcmf_bus *bus, uint bus_hdrlen)
+{
+	struct brcmf_info *drvr_priv = NULL;
+	struct net_device *net;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Allocate etherdev, including space for private structure */
+	net = alloc_etherdev(sizeof(drvr_priv));
+	if (!net) {
+		BRCMF_ERROR(("%s: OOM - alloc_etherdev\n", __func__));
+		goto fail;
+	}
+
+	/* Allocate primary brcmf_info */
+	drvr_priv = kzalloc(sizeof(struct brcmf_info), GFP_ATOMIC);
+	if (!drvr_priv) {
+		BRCMF_ERROR(("%s: OOM - alloc brcmf_info\n", __func__));
+		goto fail;
+	}
+
+	/*
+	 * Save the brcmf_info into the priv
+	 */
+	memcpy(netdev_priv(net), &drvr_priv, sizeof(drvr_priv));
+
+	/* Set network interface name if it was provided as module parameter */
+	if (iface_name[0]) {
+		int len;
+		char ch;
+		strncpy(net->name, iface_name, IFNAMSIZ);
+		net->name[IFNAMSIZ - 1] = 0;
+		len = strlen(net->name);
+		ch = net->name[len - 1];
+		if ((ch > '9' || ch < '0') && (len < IFNAMSIZ - 2))
+			strcat(net->name, "%d");
+	}
+
+	if (brcmf_add_if(drvr_priv, 0, (void *)net, net->name, NULL, 0, 0) ==
+	    BRCMF_BAD_IF)
+		goto fail;
+
+	net->netdev_ops = NULL;
+	sema_init(&drvr_priv->proto_sem, 1);
+	/* Initialize other structure content */
+	init_waitqueue_head(&drvr_priv->ioctl_resp_wait);
+
+	/* Link to info module */
+	drvr_priv->pub.info = drvr_priv;
+
+	/* Link to bus module */
+	drvr_priv->pub.bus = bus;
+	drvr_priv->pub.hdrlen = bus_hdrlen;
+
+	/* Attach and link in the protocol */
+	if (brcmf_proto_attach(&drvr_priv->pub) != 0) {
+		BRCMF_ERROR(("brcmf_prot_attach failed\n"));
+		goto fail;
+	}
+
+	/* Attach and link in the cfg80211 */
+	if (unlikely(brcmf_cfg80211_attach(net, &drvr_priv->pub))) {
+		BRCMF_ERROR(("wl_cfg80211_attach failed\n"));
+		goto fail;
+	}
+
+	if (brcmf_sysioc) {
+		sema_init(&drvr_priv->sysioc_sem, 0);
+		drvr_priv->sysioc_tsk = kthread_run(_brcmf_sysioc_thread, drvr_priv,
+						"_brcmf_sysioc");
+		if (IS_ERR(drvr_priv->sysioc_tsk)) {
+			printk(KERN_WARNING
+				"_brcmf_sysioc thread failed to start\n");
+			drvr_priv->sysioc_tsk = NULL;
+		}
+	} else
+		drvr_priv->sysioc_tsk = NULL;
+
+	/*
+	 * Save the brcmf_info into the priv
+	 */
+	memcpy(netdev_priv(net), &drvr_priv, sizeof(drvr_priv));
+
+#if defined(CONFIG_PM_SLEEP)
+	atomic_set(&brcmf_mmc_suspend, false);
+#endif	/* defined(CONFIG_PM_SLEEP) */
+	return &drvr_priv->pub;
+
+fail:
+	if (net)
+		free_netdev(net);
+	if (drvr_priv)
+		brcmf_detach(&drvr_priv->pub);
+
+	return NULL;
+}
+
+int brcmf_bus_start(struct brcmf_pub *drvr)
+{
+	int ret = -1;
+	struct brcmf_info *drvr_priv = drvr->info;
+	/* Room for "event_msgs" + '\0' + bitvec */
+	char iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
+
+	BRCMF_TRACE(("%s:\n", __func__));
+
+	/* Bring up the bus */
+	ret = brcmf_sdbrcm_bus_init(&drvr_priv->pub, true);
+	if (ret != 0) {
+		BRCMF_ERROR(("%s, brcmf_sdbrcm_bus_init failed %d\n", __func__,
+			     ret));
+		return ret;
+	}
+
+	/* If bus is not ready, can't come up */
+	if (drvr_priv->pub.busstate != BRCMF_BUS_DATA) {
+		BRCMF_ERROR(("%s failed bus is not ready\n", __func__));
+		return -ENODEV;
+	}
+
+	brcmu_mkiovar("event_msgs", drvr->eventmask, BRCMF_EVENTING_MASK_LEN,
+		      iovbuf, sizeof(iovbuf));
+	brcmf_proto_cdc_query_ioctl(drvr, 0, BRCMF_C_GET_VAR, iovbuf,
+				    sizeof(iovbuf));
+	memcpy(drvr->eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
+
+	setbit(drvr->eventmask, BRCMF_E_SET_SSID);
+	setbit(drvr->eventmask, BRCMF_E_PRUNE);
+	setbit(drvr->eventmask, BRCMF_E_AUTH);
+	setbit(drvr->eventmask, BRCMF_E_REASSOC);
+	setbit(drvr->eventmask, BRCMF_E_REASSOC_IND);
+	setbit(drvr->eventmask, BRCMF_E_DEAUTH_IND);
+	setbit(drvr->eventmask, BRCMF_E_DISASSOC_IND);
+	setbit(drvr->eventmask, BRCMF_E_DISASSOC);
+	setbit(drvr->eventmask, BRCMF_E_JOIN);
+	setbit(drvr->eventmask, BRCMF_E_ASSOC_IND);
+	setbit(drvr->eventmask, BRCMF_E_PSK_SUP);
+	setbit(drvr->eventmask, BRCMF_E_LINK);
+	setbit(drvr->eventmask, BRCMF_E_NDIS_LINK);
+	setbit(drvr->eventmask, BRCMF_E_MIC_ERROR);
+	setbit(drvr->eventmask, BRCMF_E_PMKID_CACHE);
+	setbit(drvr->eventmask, BRCMF_E_TXFAIL);
+	setbit(drvr->eventmask, BRCMF_E_JOIN_START);
+	setbit(drvr->eventmask, BRCMF_E_SCAN_COMPLETE);
+
+/* enable dongle roaming event */
+
+	drvr->pktfilter_count = 1;
+	/* Setup filter to allow only unicast */
+	drvr->pktfilter[0] = "100 0 0 0 0x01 0x00";
+
+	/* Bus is ready, do any protocol initialization */
+	ret = brcmf_proto_init(&drvr_priv->pub);
+	if (ret < 0)
+		return ret;
+
+	return 0;
+}
+
+static struct net_device_ops brcmf_netdev_ops_pri = {
+	.ndo_open = brcmf_netdev_open,
+	.ndo_stop = brcmf_netdev_stop,
+	.ndo_get_stats = brcmf_netdev_get_stats,
+	.ndo_do_ioctl = brcmf_netdev_ioctl_entry,
+	.ndo_start_xmit = brcmf_netdev_start_xmit,
+	.ndo_set_mac_address = brcmf_netdev_set_mac_address,
+	.ndo_set_multicast_list = brcmf_netdev_set_multicast_list
+};
+
+int brcmf_net_attach(struct brcmf_pub *drvr, int ifidx)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+	struct net_device *net;
+	u8 temp_addr[ETH_ALEN] = {
+		0x00, 0x90, 0x4c, 0x11, 0x22, 0x33};
+
+	BRCMF_TRACE(("%s: ifidx %d\n", __func__, ifidx));
+
+	net = drvr_priv->iflist[ifidx]->net;
+	net->netdev_ops = &brcmf_netdev_ops_pri;
+
+	/*
+	 * We have to use the primary MAC for virtual interfaces
+	 */
+	if (ifidx != 0) {
+		/* for virtual interfaces use the primary MAC  */
+		memcpy(temp_addr, drvr_priv->pub.mac, ETH_ALEN);
+
+	}
+
+	if (ifidx == 1) {
+		BRCMF_TRACE(("%s ACCESS POINT MAC:\n", __func__));
+		/*  ACCESSPOINT INTERFACE CASE */
+		temp_addr[0] |= 0X02;	/* set bit 2 ,
+			 - Locally Administered address  */
+
+	}
+	net->hard_header_len = ETH_HLEN + drvr_priv->pub.hdrlen;
+	net->ethtool_ops = &brcmf_ethtool_ops;
+
+	drvr_priv->pub.rxsz = net->mtu + net->hard_header_len +
+				drvr_priv->pub.hdrlen;
+
+	memcpy(net->dev_addr, temp_addr, ETH_ALEN);
+
+	if (register_netdev(net) != 0) {
+		BRCMF_ERROR(("%s: couldn't register the net device\n",
+			     __func__));
+		goto fail;
+	}
+
+	BRCMF_INFO(("%s: Broadcom Dongle Host Driver\n", net->name));
+
+	return 0;
+
+fail:
+	net->netdev_ops = NULL;
+	return -EBADE;
+}
+
+static void brcmf_bus_detach(struct brcmf_pub *drvr)
+{
+	struct brcmf_info *drvr_priv;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (drvr) {
+		drvr_priv = drvr->info;
+		if (drvr_priv) {
+			/* Stop the protocol module */
+			brcmf_proto_stop(&drvr_priv->pub);
+
+			/* Stop the bus module */
+			brcmf_sdbrcm_bus_stop(drvr_priv->pub.bus, true);
+		}
+	}
+}
+
+void brcmf_detach(struct brcmf_pub *drvr)
+{
+	struct brcmf_info *drvr_priv;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (drvr) {
+		drvr_priv = drvr->info;
+		if (drvr_priv) {
+			struct brcmf_if *ifp;
+			int i;
+
+			for (i = 1; i < BRCMF_MAX_IFS; i++)
+				if (drvr_priv->iflist[i])
+					brcmf_del_if(drvr_priv, i);
+
+			ifp = drvr_priv->iflist[0];
+			if (ifp->net->netdev_ops == &brcmf_netdev_ops_pri) {
+				brcmf_netdev_stop(ifp->net);
+				unregister_netdev(ifp->net);
+			}
+
+			if (drvr_priv->sysioc_tsk) {
+				send_sig(SIGTERM, drvr_priv->sysioc_tsk, 1);
+				kthread_stop(drvr_priv->sysioc_tsk);
+				drvr_priv->sysioc_tsk = NULL;
+			}
+
+			brcmf_bus_detach(drvr);
+
+			if (drvr->prot)
+				brcmf_proto_detach(drvr);
+
+			brcmf_cfg80211_detach();
+
+			free_netdev(ifp->net);
+			kfree(ifp);
+			kfree(drvr_priv);
+		}
+	}
+}
+
+static void __exit brcmf_module_cleanup(void)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	brcmf_bus_unregister();
+}
+
+static int __init brcmf_module_init(void)
+{
+	int error;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	error = brcmf_bus_register();
+
+	if (error) {
+		BRCMF_ERROR(("%s: brcmf_bus_register failed\n", __func__));
+		goto failed;
+	}
+	return 0;
+
+failed:
+	return -EINVAL;
+}
+
+module_init(brcmf_module_init);
+module_exit(brcmf_module_cleanup);
+
+int brcmf_os_proto_block(struct brcmf_pub *drvr)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	if (drvr_priv) {
+		down(&drvr_priv->proto_sem);
+		return 1;
+	}
+	return 0;
+}
+
+int brcmf_os_proto_unblock(struct brcmf_pub *drvr)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	if (drvr_priv) {
+		up(&drvr_priv->proto_sem);
+		return 1;
+	}
+
+	return 0;
+}
+
+unsigned int brcmf_os_get_ioctl_resp_timeout(void)
+{
+	return (unsigned int)brcmf_ioctl_timeout_msec;
+}
+
+void brcmf_os_set_ioctl_resp_timeout(unsigned int timeout_msec)
+{
+	brcmf_ioctl_timeout_msec = (int)timeout_msec;
+}
+
+int brcmf_os_ioctl_resp_wait(struct brcmf_pub *drvr, uint *condition,
+			     bool *pending)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+	DECLARE_WAITQUEUE(wait, current);
+	int timeout = brcmf_ioctl_timeout_msec;
+
+	/* Convert timeout in millsecond to jiffies */
+	timeout = timeout * HZ / 1000;
+
+	/* Wait until control frame is available */
+	add_wait_queue(&drvr_priv->ioctl_resp_wait, &wait);
+	set_current_state(TASK_INTERRUPTIBLE);
+
+	while (!(*condition) && (!signal_pending(current) && timeout))
+		timeout = schedule_timeout(timeout);
+
+	if (signal_pending(current))
+		*pending = true;
+
+	set_current_state(TASK_RUNNING);
+	remove_wait_queue(&drvr_priv->ioctl_resp_wait, &wait);
+
+	return timeout;
+}
+
+int brcmf_os_ioctl_resp_wake(struct brcmf_pub *drvr)
+{
+	struct brcmf_info *drvr_priv = drvr->info;
+
+	if (waitqueue_active(&drvr_priv->ioctl_resp_wait))
+		wake_up_interruptible(&drvr_priv->ioctl_resp_wait);
+
+	return 0;
+}
+
+static int brcmf_host_event(struct brcmf_info *drvr_priv, int *ifidx, void *pktdata,
+			    struct brcmf_event_msg *event, void **data)
+{
+	int bcmerror = 0;
+
+	bcmerror = brcmf_c_host_event(drvr_priv, ifidx, pktdata, event, data);
+	if (bcmerror != 0)
+		return bcmerror;
+
+	if (drvr_priv->iflist[*ifidx]->net)
+		brcmf_cfg80211_event(drvr_priv->iflist[*ifidx]->net,
+				     event, *data);
+
+	return bcmerror;
+}
+
+int brcmf_netdev_reset(struct net_device *dev, u8 flag)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **)netdev_priv(dev);
+
+	brcmf_bus_devreset(&drvr_priv->pub, flag);
+
+	return 1;
+}
+
+static int brcmf_get_pend_8021x_cnt(struct brcmf_info *drvr_priv)
+{
+	return atomic_read(&drvr_priv->pend_8021x_cnt);
+}
+
+#define MAX_WAIT_FOR_8021X_TX	10
+
+int brcmf_netdev_wait_pend8021x(struct net_device *dev)
+{
+	struct brcmf_info *drvr_priv = *(struct brcmf_info **)netdev_priv(dev);
+	int timeout = 10 * HZ / 1000;
+	int ntimes = MAX_WAIT_FOR_8021X_TX;
+	int pend = brcmf_get_pend_8021x_cnt(drvr_priv);
+
+	while (ntimes && pend) {
+		if (pend) {
+			set_current_state(TASK_INTERRUPTIBLE);
+			schedule_timeout(timeout);
+			set_current_state(TASK_RUNNING);
+			ntimes--;
+		}
+		pend = brcmf_get_pend_8021x_cnt(drvr_priv);
+	}
+	return pend;
+}
+
+#ifdef BCMDBG
+int brcmf_write_to_file(struct brcmf_pub *drvr, u8 *buf, int size)
+{
+	int ret = 0;
+	struct file *fp;
+	mm_segment_t old_fs;
+	loff_t pos = 0;
+
+	/* change to KERNEL_DS address limit */
+	old_fs = get_fs();
+	set_fs(KERNEL_DS);
+
+	/* open file to write */
+	fp = filp_open("/tmp/mem_dump", O_WRONLY | O_CREAT, 0640);
+	if (!fp) {
+		BRCMF_ERROR(("%s: open file error\n", __func__));
+		ret = -1;
+		goto exit;
+	}
+
+	/* Write buf to file */
+	fp->f_op->write(fp, buf, size, &pos);
+
+exit:
+	/* free buf before return */
+	kfree(buf);
+	/* close file before return */
+	if (fp)
+		filp_close(fp, current->files);
+	/* restore previous address limit */
+	set_fs(old_fs);
+
+	return ret;
+}
+#endif				/* BCMDBG */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
new file mode 100644
index 0000000..ff788b3
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_proto.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCMF_PROTO_H_
+#define _BRCMF_PROTO_H_
+
+#ifndef IOCTL_RESP_TIMEOUT
+#define IOCTL_RESP_TIMEOUT  2000	/* In milli second */
+#endif
+
+#ifndef IOCTL_CHIP_ACTIVE_TIMEOUT
+#define IOCTL_CHIP_ACTIVE_TIMEOUT  10	/* In milli second */
+#endif
+
+/*
+ * Exported from the brcmf protocol module (brcmf_cdc)
+ */
+
+/* Linkage, sets prot link and updates hdrlen in pub */
+extern int brcmf_proto_attach(struct brcmf_pub *drvr);
+
+/* Unlink, frees allocated protocol memory (including brcmf_proto) */
+extern void brcmf_proto_detach(struct brcmf_pub *drvr);
+
+/* Initialize protocol: sync w/dongle state.
+ * Sets dongle media info (iswl, drv_version, mac address).
+ */
+extern int brcmf_proto_init(struct brcmf_pub *drvr);
+
+/* Stop protocol: sync w/dongle state. */
+extern void brcmf_proto_stop(struct brcmf_pub *drvr);
+
+/* Add any protocol-specific data header.
+ * Caller must reserve prot_hdrlen prepend space.
+ */
+extern void brcmf_proto_hdrpush(struct brcmf_pub *, int ifidx,
+				struct sk_buff *txp);
+
+/* Remove any protocol-specific data header. */
+extern int brcmf_proto_hdrpull(struct brcmf_pub *, int *ifidx,
+			       struct sk_buff *rxp);
+
+/* Use protocol to issue ioctl to dongle */
+extern int brcmf_proto_ioctl(struct brcmf_pub *drvr, int ifidx,
+			     struct brcmf_ioctl *ioc, void *buf, int len);
+
+/* Add prot dump output to a buffer */
+extern void brcmf_proto_dump(struct brcmf_pub *drvr,
+			     struct brcmu_strbuf *strbuf);
+
+/* Update local copy of dongle statistics */
+extern void brcmf_proto_dstats(struct brcmf_pub *drvr);
+
+extern int brcmf_c_ioctl(struct brcmf_pub *drvr, struct brcmf_c_ioctl *ioc,
+			 void *buf, uint buflen);
+
+extern int brcmf_c_preinit_ioctls(struct brcmf_pub *drvr);
+
+extern int brcmf_proto_cdc_set_ioctl(struct brcmf_pub *drvr, int ifidx,
+				     uint cmd, void *buf, uint len);
+
+#endif				/* _BRCMF_PROTO_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
new file mode 100644
index 0000000..722d4c7
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c
@@ -0,0 +1,6771 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/printk.h>
+#include <linux/pci_ids.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/semaphore.h>
+#include <linux/firmware.h>
+#include <asm/unaligned.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include <soc.h>
+#include "sdio_host.h"
+
+/* register access macros */
+#ifndef __BIG_ENDIAN
+#ifndef __mips__
+#define R_REG(r, typ) \
+	brcmf_sdcard_reg_read(NULL, (r), sizeof(typ))
+#else				/* __mips__ */
+#define R_REG(r, typ) \
+	({ \
+		__typeof(*(r)) __osl_v; \
+		__asm__ __volatile__("sync"); \
+		__osl_v = brcmf_sdcard_reg_read(NULL, (r),\
+					  sizeof(typ)); \
+		__asm__ __volatile__("sync"); \
+		__osl_v; \
+	})
+#endif				/* __mips__ */
+
+#else				/* __BIG_ENDIAN */
+#define R_REG(r, typ) \
+	brcmf_sdcard_reg_read(NULL, (r), sizeof(typ))
+#endif				/* __BIG_ENDIAN */
+
+#define OR_REG(r, v, typ) \
+	brcmf_sdcard_reg_write(NULL, (r), sizeof(typ), R_REG(r, typ) | (v))
+
+#ifdef BCMDBG
+
+/* ARM trap handling */
+
+/* Trap types defined by ARM (see arminc.h) */
+
+#if defined(__ARM_ARCH_4T__)
+#define	MAX_TRAP_TYPE	(TR_FIQ + 1)
+#elif defined(__ARM_ARCH_7M__)
+#define	MAX_TRAP_TYPE	(TR_ISR + ARMCM3_NUMINTS)
+#endif				/* __ARM_ARCH_7M__ */
+
+/* The trap structure is defined here as offsets for assembly */
+#define	TR_TYPE		0x00
+#define	TR_EPC		0x04
+#define	TR_CPSR		0x08
+#define	TR_SPSR		0x0c
+#define	TR_REGS		0x10
+#define	TR_REG(n)	(TR_REGS + (n) * 4)
+#define	TR_SP		TR_REG(13)
+#define	TR_LR		TR_REG(14)
+#define	TR_PC		TR_REG(15)
+
+#define	TRAP_T_SIZE	80
+
+struct brcmf_trap {
+	u32 type;
+	u32 epc;
+	u32 cpsr;
+	u32 spsr;
+	u32 r0;
+	u32 r1;
+	u32 r2;
+	u32 r3;
+	u32 r4;
+	u32 r5;
+	u32 r6;
+	u32 r7;
+	u32 r8;
+	u32 r9;
+	u32 r10;
+	u32 r11;
+	u32 r12;
+	u32 r13;
+	u32 r14;
+	u32 pc;
+};
+
+#define CBUF_LEN	(128)
+
+struct rte_log {
+	u32 buf;		/* Can't be pointer on (64-bit) hosts */
+	uint buf_size;
+	uint idx;
+	char *_buf_compat;	/* Redundant pointer for backward compat. */
+};
+
+struct rte_console {
+	/* Virtual UART
+	 * When there is no UART (e.g. Quickturn),
+	 * the host should write a complete
+	 * input line directly into cbuf and then write
+	 * the length into vcons_in.
+	 * This may also be used when there is a real UART
+	 * (at risk of conflicting with
+	 * the real UART).  vcons_out is currently unused.
+	 */
+	volatile uint vcons_in;
+	volatile uint vcons_out;
+
+	/* Output (logging) buffer
+	 * Console output is written to a ring buffer log_buf at index log_idx.
+	 * The host may read the output when it sees log_idx advance.
+	 * Output will be lost if the output wraps around faster than the host
+	 * polls.
+	 */
+	struct rte_log log;
+
+	/* Console input line buffer
+	 * Characters are read one at a time into cbuf
+	 * until <CR> is received, then
+	 * the buffer is processed as a command line.
+	 * Also used for virtual UART.
+	 */
+	uint cbuf_idx;
+	char cbuf[CBUF_LEN];
+};
+
+#endif				/* BCMDBG */
+#include <chipcommon.h>
+
+#include "dhd.h"
+#include "dhd_bus.h"
+#include "dhd_proto.h"
+#include "dhd_dbg.h"
+#include <bcmchip.h>
+
+#define TXQLEN		2048	/* bulk tx queue length */
+#define TXHI		(TXQLEN - 256)	/* turn on flow control above TXHI */
+#define TXLOW		(TXHI - 256)	/* turn off flow control below TXLOW */
+#define PRIOMASK	7
+
+#define TXRETRIES	2	/* # of retries for tx frames */
+
+#define BRCMF_RXBOUND	50	/* Default for max rx frames in
+				 one scheduling */
+
+#define BRCMF_TXBOUND	20	/* Default for max tx frames in
+				 one scheduling */
+
+#define BRCMF_TXMINMAX	1	/* Max tx frames if rx still pending */
+
+#define MEMBLOCK	2048	/* Block size used for downloading
+				 of dongle image */
+#define MAX_DATA_BUF	(32 * 1024)	/* Must be large enough to hold
+				 biggest possible glom */
+
+#ifndef BRCMF_FIRSTREAD
+#define BRCMF_FIRSTREAD	32
+#endif
+
+#if !ISPOWEROF2(BRCMF_FIRSTREAD)
+#error BRCMF_FIRSTREAD is not a power of 2!
+#endif
+
+/* SBSDIO_DEVICE_CTL */
+#define SBSDIO_DEVCTL_SETBUSY		0x01	/* 1: device will assert busy signal when
+						 * receiving CMD53
+						 */
+#define SBSDIO_DEVCTL_SPI_INTR_SYNC	0x02	/* 1: assertion of sdio interrupt is
+						 * synchronous to the sdio clock
+						 */
+#define SBSDIO_DEVCTL_CA_INT_ONLY	0x04	/* 1: mask all interrupts to host
+						 * except the chipActive (rev 8)
+						 */
+#define SBSDIO_DEVCTL_PADS_ISO		0x08	/* 1: isolate internal sdio signals, put
+						 * external pads in tri-state; requires
+						 * sdio bus power cycle to clear (rev 9)
+						 */
+#define SBSDIO_DEVCTL_SB_RST_CTL	0x30	/* Force SD->SB reset mapping (rev 11) */
+#define SBSDIO_DEVCTL_RST_CORECTL	0x00	/*   Determined by CoreControl bit */
+#define SBSDIO_DEVCTL_RST_BPRESET	0x10	/*   Force backplane reset */
+#define SBSDIO_DEVCTL_RST_NOBPRESET	0x20	/*   Force no backplane reset */
+
+/* SBSDIO_FUNC1_CHIPCLKCSR */
+#define SBSDIO_FORCE_ALP		0x01	/* Force ALP request to backplane */
+#define SBSDIO_FORCE_HT			0x02	/* Force HT request to backplane */
+#define SBSDIO_FORCE_ILP		0x04	/* Force ILP request to backplane */
+#define SBSDIO_ALP_AVAIL_REQ		0x08	/* Make ALP ready (power up xtal) */
+#define SBSDIO_HT_AVAIL_REQ		0x10	/* Make HT ready (power up PLL) */
+#define SBSDIO_FORCE_HW_CLKREQ_OFF	0x20	/* Squelch clock requests from HW */
+#define SBSDIO_ALP_AVAIL		0x40	/* Status: ALP is ready */
+#define SBSDIO_HT_AVAIL			0x80	/* Status: HT is ready */
+
+#define SBSDIO_AVBITS			(SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL)
+#define SBSDIO_ALPAV(regval)		((regval) & SBSDIO_AVBITS)
+#define SBSDIO_HTAV(regval)		(((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS)
+#define SBSDIO_ALPONLY(regval)		(SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval))
+#define SBSDIO_CLKAV(regval, alponly)	(SBSDIO_ALPAV(regval) && \
+					(alponly ? 1 : SBSDIO_HTAV(regval)))
+/* direct(mapped) cis space */
+#define SBSDIO_CIS_BASE_COMMON		0x1000	/* MAPPED common CIS address */
+#define SBSDIO_CIS_SIZE_LIMIT		0x200	/* maximum bytes in one CIS */
+#define SBSDIO_CIS_OFT_ADDR_MASK	0x1FFFF	/* cis offset addr is < 17 bits */
+
+#define SBSDIO_CIS_MANFID_TUPLE_LEN	6	/* manfid tuple length, include tuple,
+						 * link bytes
+						 */
+
+/* intstatus */
+#define I_SMB_SW0	(1 << 0)	/* To SB Mail S/W interrupt 0 */
+#define I_SMB_SW1	(1 << 1)	/* To SB Mail S/W interrupt 1 */
+#define I_SMB_SW2	(1 << 2)	/* To SB Mail S/W interrupt 2 */
+#define I_SMB_SW3	(1 << 3)	/* To SB Mail S/W interrupt 3 */
+#define I_SMB_SW_MASK	0x0000000f	/* To SB Mail S/W interrupts mask */
+#define I_SMB_SW_SHIFT	0	/* To SB Mail S/W interrupts shift */
+#define I_HMB_SW0	(1 << 4)	/* To Host Mail S/W interrupt 0 */
+#define I_HMB_SW1	(1 << 5)	/* To Host Mail S/W interrupt 1 */
+#define I_HMB_SW2	(1 << 6)	/* To Host Mail S/W interrupt 2 */
+#define I_HMB_SW3	(1 << 7)	/* To Host Mail S/W interrupt 3 */
+#define I_HMB_SW_MASK	0x000000f0	/* To Host Mail S/W interrupts mask */
+#define I_HMB_SW_SHIFT	4	/* To Host Mail S/W interrupts shift */
+#define I_WR_OOSYNC	(1 << 8)	/* Write Frame Out Of Sync */
+#define I_RD_OOSYNC	(1 << 9)	/* Read Frame Out Of Sync */
+#define	I_PC		(1 << 10)	/* descriptor error */
+#define	I_PD		(1 << 11)	/* data error */
+#define	I_DE		(1 << 12)	/* Descriptor protocol Error */
+#define	I_RU		(1 << 13)	/* Receive descriptor Underflow */
+#define	I_RO		(1 << 14)	/* Receive fifo Overflow */
+#define	I_XU		(1 << 15)	/* Transmit fifo Underflow */
+#define	I_RI		(1 << 16)	/* Receive Interrupt */
+#define I_BUSPWR	(1 << 17)	/* SDIO Bus Power Change (rev 9) */
+#define I_XMTDATA_AVAIL (1 << 23)	/* bits in fifo */
+#define	I_XI		(1 << 24)	/* Transmit Interrupt */
+#define I_RF_TERM	(1 << 25)	/* Read Frame Terminate */
+#define I_WF_TERM	(1 << 26)	/* Write Frame Terminate */
+#define I_PCMCIA_XU	(1 << 27)	/* PCMCIA Transmit FIFO Underflow */
+#define I_SBINT		(1 << 28)	/* sbintstatus Interrupt */
+#define I_CHIPACTIVE	(1 << 29)	/* chip from doze to active state */
+#define I_SRESET	(1 << 30)	/* CCCR RES interrupt */
+#define I_IOE2		(1U << 31)	/* CCCR IOE2 Bit Changed */
+#define	I_ERRORS	(I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
+#define I_DMA		(I_RI | I_XI | I_ERRORS)
+
+/* corecontrol */
+#define CC_CISRDY		(1 << 0)	/* CIS Ready */
+#define CC_BPRESEN		(1 << 1)	/* CCCR RES signal */
+#define CC_F2RDY		(1 << 2)	/* set CCCR IOR2 bit */
+#define CC_CLRPADSISO		(1 << 3)	/* clear SDIO pads isolation */
+#define CC_XMTDATAAVAIL_MODE	(1 << 4)
+#define CC_XMTDATAAVAIL_CTRL	(1 << 5)
+
+/* SDA_FRAMECTRL */
+#define SFC_RF_TERM	(1 << 0)	/* Read Frame Terminate */
+#define SFC_WF_TERM	(1 << 1)	/* Write Frame Terminate */
+#define SFC_CRC4WOOS	(1 << 2)	/* CRC error for write out of sync */
+#define SFC_ABORTALL	(1 << 3)	/* Abort all in-progress frames */
+
+/* HW frame tag */
+#define SDPCM_FRAMETAG_LEN	4	/* 2 bytes len, 2 bytes check val */
+
+/* Total length of frame header for dongle protocol */
+#define SDPCM_HDRLEN	(SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
+#ifdef SDTEST
+#define SDPCM_RESERVE	(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + BRCMF_SDALIGN)
+#else
+#define SDPCM_RESERVE	(SDPCM_HDRLEN + BRCMF_SDALIGN)
+#endif
+
+/*
+ * Software allocation of To SB Mailbox resources
+ */
+
+/* tosbmailbox bits corresponding to intstatus bits */
+#define SMB_NAK		(1 << 0)	/* Frame NAK */
+#define SMB_INT_ACK	(1 << 1)	/* Host Interrupt ACK */
+#define SMB_USE_OOB	(1 << 2)	/* Use OOB Wakeup */
+#define SMB_DEV_INT	(1 << 3)	/* Miscellaneous Interrupt */
+
+/* tosbmailboxdata */
+#define SMB_DATA_VERSION_SHIFT	16	/* host protocol version */
+
+/*
+ * Software allocation of To Host Mailbox resources
+ */
+
+/* intstatus bits */
+#define I_HMB_FC_STATE	I_HMB_SW0	/* Flow Control State */
+#define I_HMB_FC_CHANGE	I_HMB_SW1	/* Flow Control State Changed */
+#define I_HMB_FRAME_IND	I_HMB_SW2	/* Frame Indication */
+#define I_HMB_HOST_INT	I_HMB_SW3	/* Miscellaneous Interrupt */
+
+/* tohostmailboxdata */
+#define HMB_DATA_NAKHANDLED	1	/* retransmit NAK'd frame */
+#define HMB_DATA_DEVREADY	2	/* talk to host after enable */
+#define HMB_DATA_FC		4	/* per prio flowcontrol update flag */
+#define HMB_DATA_FWREADY	8	/* fw ready for protocol activity */
+
+#define HMB_DATA_FCDATA_MASK	0xff000000
+#define HMB_DATA_FCDATA_SHIFT	24
+
+#define HMB_DATA_VERSION_MASK	0x00ff0000
+#define HMB_DATA_VERSION_SHIFT	16
+
+/*
+ * Software-defined protocol header
+ */
+
+/* Current protocol version */
+#define SDPCM_PROT_VERSION	4
+
+/* SW frame header */
+#define SDPCM_PACKET_SEQUENCE(p)	(((u8 *)p)[0] & 0xff)
+
+#define SDPCM_CHANNEL_MASK		0x00000f00
+#define SDPCM_CHANNEL_SHIFT		8
+#define SDPCM_PACKET_CHANNEL(p)		(((u8 *)p)[1] & 0x0f)
+
+#define SDPCM_NEXTLEN_OFFSET		2
+
+/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
+#define SDPCM_DOFFSET_OFFSET		3	/* Data Offset */
+#define SDPCM_DOFFSET_VALUE(p)		(((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
+#define SDPCM_DOFFSET_MASK		0xff000000
+#define SDPCM_DOFFSET_SHIFT		24
+#define SDPCM_FCMASK_OFFSET		4	/* Flow control */
+#define SDPCM_FCMASK_VALUE(p)		(((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
+#define SDPCM_WINDOW_OFFSET		5	/* Credit based fc */
+#define SDPCM_WINDOW_VALUE(p)		(((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
+
+#define SDPCM_SWHEADER_LEN	8	/* SW header is 64 bits */
+
+/* logical channel numbers */
+#define SDPCM_CONTROL_CHANNEL	0	/* Control channel Id */
+#define SDPCM_EVENT_CHANNEL	1	/* Asyc Event Indication Channel Id */
+#define SDPCM_DATA_CHANNEL	2	/* Data Xmit/Recv Channel Id */
+#define SDPCM_GLOM_CHANNEL	3	/* For coalesced packets */
+#define SDPCM_TEST_CHANNEL	15	/* Reserved for test/debug packets */
+
+#define SDPCM_SEQUENCE_WRAP	256	/* wrap-around val for 8bit frame seq */
+
+#define SDPCM_GLOMDESC(p)	(((u8 *)p)[1] & 0x80)
+
+/* For TEST_CHANNEL packets, define another 4-byte header */
+#define SDPCM_TEST_HDRLEN	4	/*
+					 * Generally: Cmd(1), Ext(1), Len(2);
+					 * Semantics of Ext byte depend on
+					 * command. Len is current or requested
+					 * frame length, not including test
+					 * header; sent little-endian.
+					 */
+#define SDPCM_TEST_DISCARD	0x01	/* Receiver discards. Ext:pattern id. */
+#define SDPCM_TEST_ECHOREQ	0x02	/* Echo request. Ext:pattern id. */
+#define SDPCM_TEST_ECHORSP	0x03	/* Echo response. Ext:pattern id. */
+#define SDPCM_TEST_BURST	0x04	/*
+					 * Receiver to send a burst.
+					 * Ext is a frame count
+					 */
+#define SDPCM_TEST_SEND		0x05	/*
+					 * Receiver sets send mode.
+					 * Ext is boolean on/off
+					 */
+
+/* Handy macro for filling in datagen packets with a pattern */
+#define SDPCM_TEST_FILL(byteno, id)	((u8)(id + byteno))
+
+/*
+ * Shared structure between dongle and the host.
+ * The structure contains pointers to trap or assert information.
+ */
+#define SDPCM_SHARED_VERSION       0x0002
+#define SDPCM_SHARED_VERSION_MASK  0x00FF
+#define SDPCM_SHARED_ASSERT_BUILT  0x0100
+#define SDPCM_SHARED_ASSERT        0x0200
+#define SDPCM_SHARED_TRAP          0x0400
+
+
+/* Space for header read, limit for data packets */
+#ifndef MAX_HDR_READ
+#define MAX_HDR_READ	32
+#endif
+#if !ISPOWEROF2(MAX_HDR_READ)
+#error MAX_HDR_READ is not a power of 2!
+#endif
+
+#define MAX_RX_DATASZ	2048
+
+/* Maximum milliseconds to wait for F2 to come up */
+#define BRCMF_WAIT_F2RDY	3000
+
+/* Bump up limit on waiting for HT to account for first startup;
+ * if the image is doing a CRC calculation before programming the PMU
+ * for HT availability, it could take a couple hundred ms more, so
+ * max out at a 1 second (1000000us).
+ */
+#if (PMU_MAX_TRANSITION_DLY <= 1000000)
+#undef PMU_MAX_TRANSITION_DLY
+#define PMU_MAX_TRANSITION_DLY 1000000
+#endif
+
+/* Value for ChipClockCSR during initial setup */
+#define BRCMF_INIT_CLKCTL1	(SBSDIO_FORCE_HW_CLKREQ_OFF |	\
+					SBSDIO_ALP_AVAIL_REQ)
+
+/* Flags for SDH calls */
+#define F2SYNC	(SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
+
+/* sbimstate */
+#define	SBIM_IBE		0x20000	/* inbanderror */
+#define	SBIM_TO			0x40000	/* timeout */
+#define	SBIM_BY			0x01800000	/* busy (sonics >= 2.3) */
+#define	SBIM_RJ			0x02000000	/* reject (sonics >= 2.3) */
+
+/* sbtmstatelow */
+#define	SBTML_RESET		0x0001	/* reset */
+#define	SBTML_REJ_MASK		0x0006	/* reject field */
+#define	SBTML_REJ		0x0002	/* reject */
+#define	SBTML_TMPREJ		0x0004	/* temporary reject, for error recovery */
+
+#define	SBTML_SICF_SHIFT	16	/* Shift to locate the SI control flags in sbtml */
+
+/* sbtmstatehigh */
+#define	SBTMH_SERR		0x0001	/* serror */
+#define	SBTMH_INT		0x0002	/* interrupt */
+#define	SBTMH_BUSY		0x0004	/* busy */
+#define	SBTMH_TO		0x0020	/* timeout (sonics >= 2.3) */
+
+#define	SBTMH_SISF_SHIFT	16	/* Shift to locate the SI status flags in sbtmh */
+
+/* sbidlow */
+#define	SBIDL_INIT		0x80	/* initiator */
+
+/* sbidhigh */
+#define	SBIDH_RC_MASK		0x000f	/* revision code */
+#define	SBIDH_RCE_MASK		0x7000	/* revision code extension field */
+#define	SBIDH_RCE_SHIFT		8
+#define	SBCOREREV(sbidh) \
+	((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK))
+#define	SBIDH_CC_MASK		0x8ff0	/* core code */
+#define	SBIDH_CC_SHIFT		4
+#define	SBIDH_VC_MASK		0xffff0000	/* vendor code */
+#define	SBIDH_VC_SHIFT		16
+
+/*
+ * Conversion of 802.1D priority to precedence level
+ */
+#define PRIO2PREC(prio) \
+	(((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? \
+	((prio^2)) : (prio))
+
+BRCMF_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
+
+/*
+ * Core reg address translation.
+ * Both macro's returns a 32 bits byte address on the backplane bus.
+ */
+#define CORE_CC_REG(base, field)	(base + offsetof(chipcregs_t, field))
+#define CORE_BUS_REG(base, field) \
+		(base + offsetof(struct sdpcmd_regs, field))
+#define CORE_SB(base, field) \
+		(base + SBCONFIGOFF + offsetof(struct sbconfig, field))
+
+/* core registers */
+struct sdpcmd_regs {
+	u32 corecontrol;		/* 0x00, rev8 */
+	u32 corestatus;			/* rev8 */
+	u32 PAD[1];
+	u32 biststatus;			/* rev8 */
+
+	/* PCMCIA access */
+	u16 pcmciamesportaladdr;	/* 0x010, rev8 */
+	u16 PAD[1];
+	u16 pcmciamesportalmask;	/* rev8 */
+	u16 PAD[1];
+	u16 pcmciawrframebc;		/* rev8 */
+	u16 PAD[1];
+	u16 pcmciaunderflowtimer;	/* rev8 */
+	u16 PAD[1];
+
+	/* interrupt */
+	u32 intstatus;			/* 0x020, rev8 */
+	u32 hostintmask;		/* rev8 */
+	u32 intmask;			/* rev8 */
+	u32 sbintstatus;		/* rev8 */
+	u32 sbintmask;			/* rev8 */
+	u32 funcintmask;		/* rev4 */
+	u32 PAD[2];
+	u32 tosbmailbox;		/* 0x040, rev8 */
+	u32 tohostmailbox;		/* rev8 */
+	u32 tosbmailboxdata;		/* rev8 */
+	u32 tohostmailboxdata;		/* rev8 */
+
+	/* synchronized access to registers in SDIO clock domain */
+	u32 sdioaccess;			/* 0x050, rev8 */
+	u32 PAD[3];
+
+	/* PCMCIA frame control */
+	u8 pcmciaframectrl;		/* 0x060, rev8 */
+	u8 PAD[3];
+	u8 pcmciawatermark;		/* rev8 */
+	u8 PAD[155];
+
+	/* interrupt batching control */
+	u32 intrcvlazy;			/* 0x100, rev8 */
+	u32 PAD[3];
+
+	/* counters */
+	u32 cmd52rd;			/* 0x110, rev8 */
+	u32 cmd52wr;			/* rev8 */
+	u32 cmd53rd;			/* rev8 */
+	u32 cmd53wr;			/* rev8 */
+	u32 abort;			/* rev8 */
+	u32 datacrcerror;		/* rev8 */
+	u32 rdoutofsync;		/* rev8 */
+	u32 wroutofsync;		/* rev8 */
+	u32 writebusy;			/* rev8 */
+	u32 readwait;			/* rev8 */
+	u32 readterm;			/* rev8 */
+	u32 writeterm;			/* rev8 */
+	u32 PAD[40];
+	u32 clockctlstatus;		/* rev8 */
+	u32 PAD[7];
+
+	u32 PAD[128];			/* DMA engines */
+
+	/* SDIO/PCMCIA CIS region */
+	char cis[512];			/* 0x400-0x5ff, rev6 */
+
+	/* PCMCIA function control registers */
+	char pcmciafcr[256];		/* 0x600-6ff, rev6 */
+	u16 PAD[55];
+
+	/* PCMCIA backplane access */
+	u16 backplanecsr;		/* 0x76E, rev6 */
+	u16 backplaneaddr0;		/* rev6 */
+	u16 backplaneaddr1;		/* rev6 */
+	u16 backplaneaddr2;		/* rev6 */
+	u16 backplaneaddr3;		/* rev6 */
+	u16 backplanedata0;		/* rev6 */
+	u16 backplanedata1;		/* rev6 */
+	u16 backplanedata2;		/* rev6 */
+	u16 backplanedata3;		/* rev6 */
+	u16 PAD[31];
+
+	/* sprom "size" & "blank" info */
+	u16 spromstatus;		/* 0x7BE, rev2 */
+	u32 PAD[464];
+
+	u16 PAD[0x80];
+};
+
+#ifdef BCMDBG
+/* Device console log buffer state */
+struct brcmf_console {
+	uint count;		/* Poll interval msec counter */
+	uint log_addr;		/* Log struct address (fixed) */
+	struct rte_log log;	/* Log struct (host copy) */
+	uint bufsize;		/* Size of log buffer */
+	u8 *buf;		/* Log buffer (host copy) */
+	uint last;		/* Last buffer read index */
+};
+#endif				/* BCMDBG */
+
+struct sdpcm_shared {
+	u32 flags;
+	u32 trap_addr;
+	u32 assert_exp_addr;
+	u32 assert_file_addr;
+	u32 assert_line;
+	u32 console_addr;	/* Address of struct rte_console */
+	u32 msgtrace_addr;
+	u8 tag[32];
+};
+
+
+/* misc chip info needed by some of the routines */
+struct chip_info {
+	u32 chip;
+	u32 chiprev;
+	u32 cccorebase;
+	u32 ccrev;
+	u32 cccaps;
+	u32 buscorebase; /* 32 bits backplane bus address */
+	u32 buscorerev;
+	u32 buscoretype;
+	u32 ramcorebase;
+	u32 armcorebase;
+	u32 pmurev;
+	u32 ramsize;
+};
+
+/* Private data for SDIO bus interaction */
+struct brcmf_bus {
+	struct brcmf_pub *drvr;
+
+	struct brcmf_sdio_card *card;	/* Handle for sdio card calls */
+	struct chip_info *ci;	/* Chip info struct */
+	char *vars;		/* Variables (from CIS and/or other) */
+	uint varsz;		/* Size of variables buffer */
+
+	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */
+	u32 orig_ramsize;	/* Size of RAM in SOCRAM (bytes) */
+
+	u32 bus;		/* gSPI or SDIO bus */
+	u32 hostintmask;	/* Copy of Host Interrupt Mask */
+	u32 intstatus;	/* Intstatus bits (events) pending */
+	bool dpc_sched;		/* Indicates DPC schedule (intrpt rcvd) */
+	bool fcstate;		/* State of dongle flow-control */
+
+	u16 cl_devid;	/* cached devid for brcmf_sdio_probe_attach() */
+
+	uint blocksize;		/* Block size of SDIO transfers */
+	uint roundup;		/* Max roundup limit */
+
+	struct pktq txq;	/* Queue length used for flow-control */
+	u8 flowcontrol;	/* per prio flow control bitmask */
+	u8 tx_seq;		/* Transmit sequence number (next) */
+	u8 tx_max;		/* Maximum transmit sequence allowed */
+
+	u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
+	u8 *rxhdr;		/* Header of current rx frame (in hdrbuf) */
+	u16 nextlen;		/* Next Read Len from last header */
+	u8 rx_seq;		/* Receive sequence number (expected) */
+	bool rxskip;		/* Skip receive (awaiting NAK ACK) */
+
+	struct sk_buff *glomd;	/* Packet containing glomming descriptor */
+	struct sk_buff *glom;	/* Packet chain for glommed superframe */
+	uint glomerr;		/* Glom packet read errors */
+
+	u8 *rxbuf;		/* Buffer for receiving control packets */
+	uint rxblen;		/* Allocated length of rxbuf */
+	u8 *rxctl;		/* Aligned pointer into rxbuf */
+	u8 *databuf;		/* Buffer for receiving big glom packet */
+	u8 *dataptr;		/* Aligned pointer into databuf */
+	uint rxlen;		/* Length of valid data in buffer */
+
+	u8 sdpcm_ver;	/* Bus protocol reported by dongle */
+
+	bool intr;		/* Use interrupts */
+	bool poll;		/* Use polling */
+	bool ipend;		/* Device interrupt is pending */
+	bool intdis;		/* Interrupts disabled by isr */
+	uint intrcount;		/* Count of device interrupt callbacks */
+	uint lastintrs;		/* Count as of last watchdog timer */
+	uint spurious;		/* Count of spurious interrupts */
+	uint pollrate;		/* Ticks between device polls */
+	uint polltick;		/* Tick counter */
+	uint pollcnt;		/* Count of active polls */
+
+#ifdef BCMDBG
+	struct brcmf_console console;	/* Console output polling support */
+	uint console_addr;	/* Console address from shared struct */
+#endif				/* BCMDBG */
+
+	uint regfails;		/* Count of R_REG failures */
+
+	uint clkstate;		/* State of sd and backplane clock(s) */
+	bool activity;		/* Activity flag for clock down */
+	s32 idletime;		/* Control for activity timeout */
+	s32 idlecount;	/* Activity timeout counter */
+	s32 idleclock;	/* How to set bus driver when idle */
+	s32 sd_rxchain;
+	bool use_rxchain;	/* If brcmf should use PKT chains */
+	bool sleeping;		/* Is SDIO bus sleeping? */
+	bool rxflow_mode;	/* Rx flow control mode */
+	bool rxflow;		/* Is rx flow control on */
+	bool alp_only;		/* Don't use HT clock (ALP only) */
+/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
+	bool usebufpool;
+
+#ifdef SDTEST
+	/* external loopback */
+	bool ext_loop;
+	u8 loopid;
+
+	/* pktgen configuration */
+	uint pktgen_freq;	/* Ticks between bursts */
+	uint pktgen_count;	/* Packets to send each burst */
+	uint pktgen_print;	/* Bursts between count displays */
+	uint pktgen_total;	/* Stop after this many */
+	uint pktgen_minlen;	/* Minimum packet data len */
+	uint pktgen_maxlen;	/* Maximum packet data len */
+	uint pktgen_mode;	/* Configured mode: tx, rx, or echo */
+	uint pktgen_stop;	/* Number of tx failures causing stop */
+
+	/* active pktgen fields */
+	uint pktgen_tick;	/* Tick counter for bursts */
+	uint pktgen_ptick;	/* Burst counter for printing */
+	uint pktgen_sent;	/* Number of test packets generated */
+	uint pktgen_rcvd;	/* Number of test packets received */
+	uint pktgen_fail;	/* Number of failed send attempts */
+	u16 pktgen_len;	/* Length of next packet to send */
+#endif				/* SDTEST */
+
+	/* Some additional counters */
+	uint tx_sderrs;		/* Count of tx attempts with sd errors */
+	uint fcqueued;		/* Tx packets that got queued */
+	uint rxrtx;		/* Count of rtx requests (NAK to dongle) */
+	uint rx_toolong;	/* Receive frames too long to receive */
+	uint rxc_errors;	/* SDIO errors when reading control frames */
+	uint rx_hdrfail;	/* SDIO errors on header reads */
+	uint rx_badhdr;		/* Bad received headers (roosync?) */
+	uint rx_badseq;		/* Mismatched rx sequence number */
+	uint fc_rcvd;		/* Number of flow-control events received */
+	uint fc_xoff;		/* Number which turned on flow-control */
+	uint fc_xon;		/* Number which turned off flow-control */
+	uint rxglomfail;	/* Failed deglom attempts */
+	uint rxglomframes;	/* Number of glom frames (superframes) */
+	uint rxglompkts;	/* Number of packets from glom frames */
+	uint f2rxhdrs;		/* Number of header reads */
+	uint f2rxdata;		/* Number of frame data reads */
+	uint f2txdata;		/* Number of f2 frame writes */
+	uint f1regdata;		/* Number of f1 register accesses */
+
+	u8 *ctrl_frame_buf;
+	u32 ctrl_frame_len;
+	bool ctrl_frame_stat;
+
+	spinlock_t txqlock;
+	wait_queue_head_t ctrl_wait;
+
+	struct timer_list timer;
+	struct completion watchdog_wait;
+	struct task_struct *watchdog_tsk;
+	bool wd_timer_valid;
+
+	struct tasklet_struct tasklet;
+	struct task_struct *dpc_tsk;
+	struct completion dpc_wait;
+
+	bool threads_only;
+	struct semaphore sdsem;
+	spinlock_t sdlock;
+
+	const char *fw_name;
+	const struct firmware *firmware;
+	const char *nv_name;
+	u32 fw_ptr;
+};
+
+struct sbconfig {
+	u32 PAD[2];
+	u32 sbipsflag;	/* initiator port ocp slave flag */
+	u32 PAD[3];
+	u32 sbtpsflag;	/* target port ocp slave flag */
+	u32 PAD[11];
+	u32 sbtmerrloga;	/* (sonics >= 2.3) */
+	u32 PAD;
+	u32 sbtmerrlog;	/* (sonics >= 2.3) */
+	u32 PAD[3];
+	u32 sbadmatch3;	/* address match3 */
+	u32 PAD;
+	u32 sbadmatch2;	/* address match2 */
+	u32 PAD;
+	u32 sbadmatch1;	/* address match1 */
+	u32 PAD[7];
+	u32 sbimstate;	/* initiator agent state */
+	u32 sbintvec;	/* interrupt mask */
+	u32 sbtmstatelow;	/* target state */
+	u32 sbtmstatehigh;	/* target state */
+	u32 sbbwa0;		/* bandwidth allocation table0 */
+	u32 PAD;
+	u32 sbimconfiglow;	/* initiator configuration */
+	u32 sbimconfighigh;	/* initiator configuration */
+	u32 sbadmatch0;	/* address match0 */
+	u32 PAD;
+	u32 sbtmconfiglow;	/* target configuration */
+	u32 sbtmconfighigh;	/* target configuration */
+	u32 sbbconfig;	/* broadcast configuration */
+	u32 PAD;
+	u32 sbbstate;	/* broadcast state */
+	u32 PAD[3];
+	u32 sbactcnfg;	/* activate configuration */
+	u32 PAD[3];
+	u32 sbflagst;	/* current sbflags */
+	u32 PAD[3];
+	u32 sbidlow;		/* identification */
+	u32 sbidhigh;	/* identification */
+};
+
+/* clkstate */
+#define CLK_NONE	0
+#define CLK_SDONLY	1
+#define CLK_PENDING	2	/* Not used yet */
+#define CLK_AVAIL	3
+
+#define BRCMF_NOPMU(brcmf)	(false)
+
+#ifdef BCMDBG
+static int qcount[NUMPRIO];
+static int tx_packets[NUMPRIO];
+#endif				/* BCMDBG */
+
+/* Deferred transmit */
+uint brcmf_deferred_tx = 1;
+module_param(brcmf_deferred_tx, uint, 0);
+
+/* Watchdog thread priority, -1 to use kernel timer */
+int brcmf_watchdog_prio = 97;
+module_param(brcmf_watchdog_prio, int, 0);
+
+/* Watchdog interval */
+uint brcmf_watchdog_ms = 10;
+module_param(brcmf_watchdog_ms, uint, 0);
+
+/* DPC thread priority, -1 to use tasklet */
+int brcmf_dpc_prio = 98;
+module_param(brcmf_dpc_prio, int, 0);
+
+#ifdef BCMDBG
+/* Console poll interval */
+uint brcmf_console_ms;
+module_param(brcmf_console_ms, uint, 0);
+#endif		/* BCMDBG */
+
+/* Tx/Rx bounds */
+uint brcmf_txbound;
+uint brcmf_rxbound;
+uint brcmf_txminmax;
+
+/* override the RAM size if possible */
+#define DONGLE_MIN_MEMSIZE (128 * 1024)
+int brcmf_dongle_memsize;
+
+static bool brcmf_alignctl;
+
+static bool sd1idle;
+
+static bool retrydata;
+#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
+
+static const uint watermark = 8;
+static const uint firstread = BRCMF_FIRSTREAD;
+
+/* Retry count for register access failures */
+static const uint retry_limit = 2;
+
+/* Force even SD lengths (some host controllers mess up on odd bytes) */
+static bool forcealign;
+
+#define ALIGNMENT  4
+
+#define PKTALIGN(_p, _len, _align)				\
+	do {								\
+		uint datalign;						\
+		datalign = (unsigned long)((_p)->data);			\
+		datalign = roundup(datalign, (_align)) - datalign;	\
+		if (datalign)						\
+			skb_pull((_p), datalign);			\
+		__skb_trim((_p), (_len));				\
+	} while (0)
+
+/* Limit on rounding up frames */
+static const uint max_roundup = 512;
+
+/* Try doing readahead */
+static bool brcmf_readahead;
+
+/* To check if there's window offered */
+#define DATAOK(bus) \
+	(((u8)(bus->tx_max - bus->tx_seq) != 0) && \
+	(((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
+
+/*
+ * Reads a register in the SDIO hardware block. This block occupies a series of
+ * adresses on the 32 bit backplane bus.
+ */
+static void
+r_sdreg32(struct brcmf_bus *bus, u32 *regvar, u32 reg_offset, u32 *retryvar)
+{
+	*retryvar = 0;
+	do {
+		*regvar = R_REG(bus->ci->buscorebase + reg_offset, u32);
+	} while (brcmf_sdcard_regfail(bus->card) &&
+		 (++(*retryvar) <= retry_limit));
+	if (*retryvar) {
+		bus->regfails += (*retryvar-1);
+		if (*retryvar > retry_limit) {
+			BRCMF_ERROR(("FAILED READ %Xh\n", reg_offset));
+			*regvar = 0;
+		}
+	}
+}
+
+static void
+w_sdreg32(struct brcmf_bus *bus, u32 regval, u32 reg_offset, u32 *retryvar)
+{
+	*retryvar = 0;
+	do {
+		brcmf_sdcard_reg_write(NULL, bus->ci->buscorebase + reg_offset,
+				       sizeof(u32), regval);
+	} while (brcmf_sdcard_regfail(bus->card) &&
+		 (++(*retryvar) <= retry_limit));
+	if (*retryvar) {
+		bus->regfails += (*retryvar-1);
+		if (*retryvar > retry_limit)
+			BRCMF_ERROR(("FAILED REGISTER WRITE"
+				     " %Xh\n", reg_offset));
+	}
+}
+
+#define BRCMF_BUS			SDIO_BUS
+
+#define PKT_AVAILABLE()		(intstatus & I_HMB_FRAME_IND)
+
+#define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)
+
+#ifdef SDTEST
+static void brcmf_sdbrcm_checkdied(struct brcmf_bus *bus, void *pkt, uint seq);
+static void brcmf_sdbrcm_sdtest_set(struct brcmf_bus *bus, bool start);
+#endif
+
+#ifdef BCMDBG
+static int brcmf_sdbrcm_bus_console_in(struct brcmf_pub *drvr,
+				       unsigned char *msg, uint msglen);
+static int brcmf_sdbrcm_checkdied(struct brcmf_bus *bus, u8 *data, uint size);
+static int brcmf_sdbrcm_mem_dump(struct brcmf_bus *bus);
+#endif				/* BCMDBG  */
+static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter);
+
+static void brcmf_sdbrcm_release(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_release_malloc(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_disconnect(void *ptr);
+static bool brcmf_sdbrcm_chipmatch(u16 chipid);
+static bool brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, void *card,
+				      u32 regsva, u16 devid);
+static bool brcmf_sdbrcm_probe_malloc(struct brcmf_bus *bus, void *card);
+static bool brcmf_sdbrcm_probe_init(struct brcmf_bus *bus, void *card);
+static void brcmf_sdbrcm_release_dongle(struct brcmf_bus *bus);
+
+static uint brcmf_process_nvram_vars(char *varbuf, uint len);
+
+static void brcmf_sdbrcm_setmemsize(struct brcmf_bus *bus, int mem_size);
+static int brcmf_sdbrcm_send_buf(struct brcmf_bus *bus, u32 addr, uint fn,
+			       uint flags, u8 *buf, uint nbytes,
+			       struct sk_buff *pkt,
+			       void (*complete)(void *handle, int status,
+						      bool sync_waiting),
+			       void *handle);
+
+static bool brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus, void *card);
+static int  _brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus);
+
+static int brcmf_sdbrcm_download_code_file(struct brcmf_bus *bus);
+static int brcmf_sdbrcm_download_nvram(struct brcmf_bus *bus);
+
+static void
+brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio_card *card, u32 corebase);
+
+static int brcmf_sdbrcm_chip_attach(struct brcmf_bus *bus, u32 regs);
+
+static void
+brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio_card *card, u32 corebase);
+
+static void brcmf_sdbrcm_sdiod_drive_strength_init(struct brcmf_bus *bus,
+					u32 drivestrength);
+static void brcmf_sdbrcm_chip_detach(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_wait_for_event(struct brcmf_bus *bus, bool *lockvar);
+static void brcmf_sdbrcm_wait_event_wakeup(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_watchdog(unsigned long data);
+static int brcmf_sdbrcm_watchdog_thread(void *data);
+static int brcmf_sdbrcm_dpc_thread(void *data);
+static void brcmf_sdbrcm_dpc_tasklet(unsigned long data);
+static void brcmf_sdbrcm_sched_dpc(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_sdlock(struct brcmf_bus *bus);
+static void brcmf_sdbrcm_sdunlock(struct brcmf_bus *bus);
+static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_bus *bus);
+
+/* Packet free applicable unconditionally for sdio and sdspi.
+ * Conditional if bufpool was present for gspi bus.
+ */
+static void brcmf_sdbrcm_pktfree2(struct brcmf_bus *bus, struct sk_buff *pkt)
+{
+	if ((bus->bus != SPI_BUS) || bus->usebufpool)
+		brcmu_pkt_buf_free_skb(pkt);
+}
+
+static void brcmf_sdbrcm_setmemsize(struct brcmf_bus *bus, int mem_size)
+{
+	s32 min_size = DONGLE_MIN_MEMSIZE;
+	/* Restrict the memsize to user specified limit */
+	BRCMF_ERROR(("user: Restrict the dongle ram size to %d, min %d\n",
+		     brcmf_dongle_memsize, min_size));
+	if ((brcmf_dongle_memsize > min_size) &&
+	    (brcmf_dongle_memsize < (s32) bus->orig_ramsize))
+		bus->ramsize = brcmf_dongle_memsize;
+}
+
+static int brcmf_sdbrcm_set_siaddr_window(struct brcmf_bus *bus, u32 address)
+{
+	int err = 0;
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
+			 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
+	if (!err)
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1,
+				 SBSDIO_FUNC1_SBADDRMID,
+				 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
+	if (!err)
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_SBADDRHIGH,
+				       (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
+				       &err);
+	return err;
+}
+
+/* Turn backplane clock on or off */
+static int brcmf_sdbrcm_htclk(struct brcmf_bus *bus, bool on, bool pendok)
+{
+	int err;
+	u8 clkctl, clkreq, devctl;
+	struct brcmf_sdio_card *card;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	clkctl = 0;
+	card = bus->card;
+
+	if (on) {
+		/* Request HT Avail */
+		clkreq =
+		    bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
+
+		if ((bus->ci->chip == BCM4329_CHIP_ID)
+		    && (bus->ci->chiprev == 0))
+			clkreq |= SBSDIO_FORCE_ALP;
+
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+		if (err) {
+			BRCMF_ERROR(("%s: HT Avail request error: %d\n",
+				     __func__, err));
+			return -EBADE;
+		}
+
+		if (pendok && ((bus->ci->buscoretype == PCMCIA_CORE_ID)
+			       && (bus->ci->buscorerev == 9))) {
+			u32 dummy, retries;
+			r_sdreg32(bus, &dummy,
+				  offsetof(struct sdpcmd_regs, clockctlstatus),
+				  &retries);
+		}
+
+		/* Check current status */
+		clkctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					       SBSDIO_FUNC1_CHIPCLKCSR, &err);
+		if (err) {
+			BRCMF_ERROR(("%s: HT Avail read error: %d\n",
+				     __func__, err));
+			return -EBADE;
+		}
+
+		/* Go to pending and await interrupt if appropriate */
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
+			/* Allow only clock-available interrupt */
+			devctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					SBSDIO_DEVICE_CTL, &err);
+			if (err) {
+				BRCMF_ERROR(("%s: Devctl error setting CA:"
+					     " %d\n", __func__, err));
+				return -EBADE;
+			}
+
+			devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+					       SBSDIO_DEVICE_CTL, devctl, &err);
+			BRCMF_INFO(("CLKCTL: set PENDING\n"));
+			bus->clkstate = CLK_PENDING;
+
+			return 0;
+		} else if (bus->clkstate == CLK_PENDING) {
+			/* Cancel CA-only interrupt filter */
+			devctl =
+			    brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						  SBSDIO_DEVICE_CTL, &err);
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				SBSDIO_DEVICE_CTL, devctl, &err);
+		}
+
+		/* Otherwise, wait here (polling) for HT Avail */
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+			BRCMF_SPINWAIT_SLEEP(sdioh_spinwait_sleep,
+			       ((clkctl =
+				 brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					 SBSDIO_FUNC1_CHIPCLKCSR,
+						 &err)),
+				!SBSDIO_CLKAV(clkctl, bus->alp_only)),
+			       PMU_MAX_TRANSITION_DLY);
+		}
+		if (err) {
+			BRCMF_ERROR(("%s: HT Avail request error: %d\n",
+				     __func__, err));
+			return -EBADE;
+		}
+		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
+			BRCMF_ERROR(("%s: HT Avail timeout (%d): "
+				     "clkctl 0x%02x\n", __func__,
+				     PMU_MAX_TRANSITION_DLY, clkctl));
+			return -EBADE;
+		}
+
+		/* Mark clock available */
+		bus->clkstate = CLK_AVAIL;
+		BRCMF_INFO(("CLKCTL: turned ON\n"));
+
+#if defined(BCMDBG)
+		if (bus->alp_only != true) {
+			if (SBSDIO_ALPONLY(clkctl)) {
+				BRCMF_ERROR(("%s: HT Clock should be on.\n",
+					     __func__));
+			}
+		}
+#endif				/* defined (BCMDBG) */
+
+		bus->activity = true;
+	} else {
+		clkreq = 0;
+
+		if (bus->clkstate == CLK_PENDING) {
+			/* Cancel CA-only interrupt filter */
+			devctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					SBSDIO_DEVICE_CTL, &err);
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				SBSDIO_DEVICE_CTL, devctl, &err);
+		}
+
+		bus->clkstate = CLK_SDONLY;
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+			SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
+		BRCMF_INFO(("CLKCTL: turned OFF\n"));
+		if (err) {
+			BRCMF_ERROR(("%s: Failed access turning clock off:"
+				     " %d\n", __func__, err));
+			return -EBADE;
+		}
+	}
+	return 0;
+}
+
+/* Change idle/active SD state */
+static int brcmf_sdbrcm_sdclk(struct brcmf_bus *bus, bool on)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (on)
+		bus->clkstate = CLK_SDONLY;
+	else
+		bus->clkstate = CLK_NONE;
+
+	return 0;
+}
+
+/* Transition SD and backplane clock readiness */
+static int brcmf_sdbrcm_clkctl(struct brcmf_bus *bus, uint target, bool pendok)
+{
+#ifdef BCMDBG
+	uint oldstate = bus->clkstate;
+#endif				/* BCMDBG */
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Early exit if we're already there */
+	if (bus->clkstate == target) {
+		if (target == CLK_AVAIL) {
+			brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+			bus->activity = true;
+		}
+		return 0;
+	}
+
+	switch (target) {
+	case CLK_AVAIL:
+		/* Make sure SD clock is available */
+		if (bus->clkstate == CLK_NONE)
+			brcmf_sdbrcm_sdclk(bus, true);
+		/* Now request HT Avail on the backplane */
+		brcmf_sdbrcm_htclk(bus, true, pendok);
+		brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+		bus->activity = true;
+		break;
+
+	case CLK_SDONLY:
+		/* Remove HT request, or bring up SD clock */
+		if (bus->clkstate == CLK_NONE)
+			brcmf_sdbrcm_sdclk(bus, true);
+		else if (bus->clkstate == CLK_AVAIL)
+			brcmf_sdbrcm_htclk(bus, false, false);
+		else
+			BRCMF_ERROR(("brcmf_sdbrcm_clkctl: request for %d -> %d"
+				     "\n", bus->clkstate, target));
+		brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+		break;
+
+	case CLK_NONE:
+		/* Make sure to remove HT request */
+		if (bus->clkstate == CLK_AVAIL)
+			brcmf_sdbrcm_htclk(bus, false, false);
+		/* Now remove the SD clock */
+		brcmf_sdbrcm_sdclk(bus, false);
+		brcmf_sdbrcm_wd_timer(bus, 0);
+		break;
+	}
+#ifdef BCMDBG
+	BRCMF_INFO(("brcmf_sdbrcm_clkctl: %d -> %d\n",
+		    oldstate, bus->clkstate));
+#endif				/* BCMDBG */
+
+	return 0;
+}
+
+int brcmf_sdbrcm_bussleep(struct brcmf_bus *bus, bool sleep)
+{
+	struct brcmf_sdio_card *card = bus->card;
+	uint retries = 0;
+
+	BRCMF_INFO(("brcmf_sdbrcm_bussleep: request %s (currently %s)\n",
+		    (sleep ? "SLEEP" : "WAKE"),
+		    (bus->sleeping ? "SLEEP" : "WAKE")));
+
+	/* Done if we're already in the requested state */
+	if (sleep == bus->sleeping)
+		return 0;
+
+	/* Going to sleep: set the alarm and turn off the lights... */
+	if (sleep) {
+		/* Don't sleep if something is pending */
+		if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
+			return -EBUSY;
+
+		/* Disable SDIO interrupts (no longer interested) */
+		brcmf_sdcard_intr_disable(bus->card);
+
+		/* Make sure the controller has the bus up */
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+		/* Tell device to start using OOB wakeup */
+		w_sdreg32(bus, SMB_USE_OOB,
+			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+		if (retries > retry_limit)
+			BRCMF_ERROR(("CANNOT SIGNAL CHIP, "
+				     "WILL NOT WAKE UP!!\n"));
+
+		/* Turn off our contribution to the HT clock request */
+		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+			SBSDIO_FUNC1_CHIPCLKCSR,
+			SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
+
+		/* Isolate the bus */
+		if (bus->ci->chip != BCM4329_CHIP_ID
+		    && bus->ci->chip != BCM4319_CHIP_ID) {
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				SBSDIO_DEVICE_CTL,
+				SBSDIO_DEVCTL_PADS_ISO, NULL);
+		}
+
+		/* Change state */
+		bus->sleeping = true;
+
+	} else {
+		/* Waking up: bus power up is ok, set local state */
+
+		brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+			SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
+
+		/* Force pad isolation off if possible
+			 (in case power never toggled) */
+		if ((bus->ci->buscoretype == PCMCIA_CORE_ID)
+		    && (bus->ci->buscorerev >= 10))
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				SBSDIO_DEVICE_CTL, 0, NULL);
+
+		/* Make sure the controller has the bus up */
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+		/* Send misc interrupt to indicate OOB not needed */
+		w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, tosbmailboxdata),
+			  &retries);
+		if (retries <= retry_limit)
+			w_sdreg32(bus, SMB_DEV_INT,
+				  offsetof(struct sdpcmd_regs, tosbmailbox),
+				  &retries);
+
+		if (retries > retry_limit)
+			BRCMF_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
+
+		/* Make sure we have SD bus access */
+		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+		/* Change state */
+		bus->sleeping = false;
+
+		/* Enable interrupts again */
+		if (bus->intr && (bus->drvr->busstate == BRCMF_BUS_DATA)) {
+			bus->intdis = false;
+			brcmf_sdcard_intr_enable(bus->card);
+		}
+	}
+
+	return 0;
+}
+
+#define BUS_WAKE(bus) \
+	do { \
+		if ((bus)->sleeping) \
+			brcmf_sdbrcm_bussleep((bus), false); \
+	} while (0);
+
+/* Writes a HW/SW header into the packet and sends it. */
+/* Assumes: (a) header space already there, (b) caller holds lock */
+static int brcmf_sdbrcm_txpkt(struct brcmf_bus *bus, struct sk_buff *pkt, uint chan,
+			 bool free_pkt)
+{
+	int ret;
+	u8 *frame;
+	u16 len, pad = 0;
+	u32 swheader;
+	uint retries = 0;
+	struct brcmf_sdio_card *card;
+	struct sk_buff *new;
+	int i;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	card = bus->card;
+
+	if (bus->drvr->dongle_reset) {
+		ret = -EPERM;
+		goto done;
+	}
+
+	frame = (u8 *) (pkt->data);
+
+	/* Add alignment padding, allocate new packet if needed */
+	pad = ((unsigned long)frame % BRCMF_SDALIGN);
+	if (pad) {
+		if (skb_headroom(pkt) < pad) {
+			BRCMF_INFO(("%s: insufficient headroom %d for %d pad\n",
+				    __func__, skb_headroom(pkt), pad));
+			bus->drvr->tx_realloc++;
+			new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
+			if (!new) {
+				BRCMF_ERROR(("%s: couldn't allocate new "
+					     "%d-byte packet\n", __func__,
+					     pkt->len + BRCMF_SDALIGN));
+				ret = -ENOMEM;
+				goto done;
+			}
+
+			PKTALIGN(new, pkt->len, BRCMF_SDALIGN);
+			memcpy(new->data, pkt->data, pkt->len);
+			if (free_pkt)
+				brcmu_pkt_buf_free_skb(pkt);
+			/* free the pkt if canned one is not used */
+			free_pkt = true;
+			pkt = new;
+			frame = (u8 *) (pkt->data);
+			/* precondition: (frame % BRCMF_SDALIGN) == 0) */
+			pad = 0;
+		} else {
+			skb_push(pkt, pad);
+			frame = (u8 *) (pkt->data);
+			/* precondition: pad + SDPCM_HDRLEN <= pkt->len */
+			memset(frame, 0, pad + SDPCM_HDRLEN);
+		}
+	}
+	/* precondition: pad < BRCMF_SDALIGN */
+
+	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+	len = (u16) (pkt->len);
+	*(u16 *) frame = cpu_to_le16(len);
+	*(((u16 *) frame) + 1) = cpu_to_le16(~len);
+
+	/* Software tag: channel, sequence number, data offset */
+	swheader =
+	    ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
+	    (((pad +
+	       SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
+
+	put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
+	put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+#ifdef BCMDBG
+	tx_packets[pkt->priority]++;
+	if (BRCMF_BYTES_ON() &&
+	    (((BRCMF_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
+	      (BRCMF_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
+		printk(KERN_DEBUG "Tx Frame:\n");
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, frame, len);
+	} else if (BRCMF_HDRS_ON()) {
+		printk(KERN_DEBUG "TxHdr:\n");
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+				     frame, min_t(u16, len, 16));
+	}
+#endif
+
+	/* Raise len to next SDIO block to eliminate tail command */
+	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+		u16 pad = bus->blocksize - (len % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize))
+				len += pad;
+	} else if (len % BRCMF_SDALIGN) {
+		len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
+	}
+
+	/* Some controllers have trouble with odd bytes -- round to even */
+	if (forcealign && (len & (ALIGNMENT - 1))) {
+			len = roundup(len, ALIGNMENT);
+	}
+
+	do {
+		ret = brcmf_sdbrcm_send_buf(bus, brcmf_sdcard_cur_sbwad(card),
+			SDIO_FUNC_2, F2SYNC, frame, len, pkt, NULL, NULL);
+		bus->f2txdata++;
+
+		if (ret < 0) {
+			/* On failure, abort the command
+			 and terminate the frame */
+			BRCMF_INFO(("%s: sdio error %d, abort command and "
+				    "terminate frame.\n", __func__, ret));
+			bus->tx_sderrs++;
+
+			brcmf_sdcard_abort(card, SDIO_FUNC_2);
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+					 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
+					 NULL);
+			bus->f1regdata++;
+
+			for (i = 0; i < 3; i++) {
+				u8 hi, lo;
+				hi = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						     SBSDIO_FUNC1_WFRAMEBCHI,
+						     NULL);
+				lo = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						     SBSDIO_FUNC1_WFRAMEBCLO,
+						     NULL);
+				bus->f1regdata += 2;
+				if ((hi == 0) && (lo == 0))
+					break;
+			}
+
+		}
+		if (ret == 0)
+			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+
+	} while ((ret < 0) && retrydata && retries++ < TXRETRIES);
+
+done:
+	/* restore pkt buffer pointer before calling tx complete routine */
+	skb_pull(pkt, SDPCM_HDRLEN + pad);
+	brcmf_sdbrcm_sdunlock(bus);
+	brcmf_txcomplete(bus->drvr, pkt, ret != 0);
+	brcmf_sdbrcm_sdlock(bus);
+
+	if (free_pkt)
+		brcmu_pkt_buf_free_skb(pkt);
+
+	return ret;
+}
+
+int brcmf_sdbrcm_bus_txdata(struct brcmf_bus *bus, struct sk_buff *pkt)
+{
+	int ret = -EBADE;
+	uint datalen, prec;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	datalen = pkt->len;
+
+#ifdef SDTEST
+	/* Push the test header if doing loopback */
+	if (bus->ext_loop) {
+		u8 *data;
+		skb_push(pkt, SDPCM_TEST_HDRLEN);
+		data = pkt->data;
+		*data++ = SDPCM_TEST_ECHOREQ;
+		*data++ = (u8) bus->loopid++;
+		*data++ = (datalen >> 0);
+		*data++ = (datalen >> 8);
+		datalen += SDPCM_TEST_HDRLEN;
+	}
+#endif				/* SDTEST */
+
+	/* Add space for the header */
+	skb_push(pkt, SDPCM_HDRLEN);
+	/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
+
+	prec = PRIO2PREC((pkt->priority & PRIOMASK));
+
+	/* Check for existing queue, current flow-control,
+			 pending event, or pending clock */
+	if (brcmf_deferred_tx || bus->fcstate || pktq_len(&bus->txq)
+	    || bus->dpc_sched || (!DATAOK(bus))
+	    || (bus->flowcontrol & NBITVAL(prec))
+	    || (bus->clkstate != CLK_AVAIL)) {
+		BRCMF_TRACE(("%s: deferring pktq len %d\n", __func__,
+			     pktq_len(&bus->txq)));
+		bus->fcqueued++;
+
+		/* Priority based enq */
+		spin_lock_bh(&bus->txqlock);
+		if (brcmf_c_prec_enq(bus->drvr, &bus->txq, pkt, prec) == false) {
+			skb_pull(pkt, SDPCM_HDRLEN);
+			brcmf_txcomplete(bus->drvr, pkt, false);
+			brcmu_pkt_buf_free_skb(pkt);
+			BRCMF_ERROR(("%s: out of bus->txq !!!\n", __func__));
+			ret = -ENOSR;
+		} else {
+			ret = 0;
+		}
+		spin_unlock_bh(&bus->txqlock);
+
+		if (pktq_len(&bus->txq) >= TXHI)
+			brcmf_txflowcontrol(bus->drvr, 0, ON);
+
+#ifdef BCMDBG
+		if (pktq_plen(&bus->txq, prec) > qcount[prec])
+			qcount[prec] = pktq_plen(&bus->txq, prec);
+#endif
+		/* Schedule DPC if needed to send queued packet(s) */
+		if (brcmf_deferred_tx && !bus->dpc_sched) {
+			bus->dpc_sched = true;
+			brcmf_sdbrcm_sched_dpc(bus);
+		}
+	} else {
+		/* Lock: we're about to use shared data/code (and SDIO) */
+		brcmf_sdbrcm_sdlock(bus);
+
+		/* Otherwise, send it now */
+		BUS_WAKE(bus);
+		/* Make sure back plane ht clk is on, no pending allowed */
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
+
+#ifndef SDTEST
+		BRCMF_TRACE(("%s: calling txpkt\n", __func__));
+		ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
+#else
+		ret = brcmf_sdbrcm_txpkt(bus, pkt,
+				    (bus->ext_loop ? SDPCM_TEST_CHANNEL :
+				     SDPCM_DATA_CHANNEL), true);
+#endif
+		if (ret)
+			bus->drvr->tx_errors++;
+		else
+			bus->drvr->dstats.tx_bytes += datalen;
+
+		if (bus->idletime == BRCMF_IDLE_IMMEDIATE &&
+		    !bus->dpc_sched) {
+			bus->activity = false;
+			brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+		}
+
+		brcmf_sdbrcm_sdunlock(bus);
+	}
+
+	return ret;
+}
+
+static uint brcmf_sdbrcm_sendfromq(struct brcmf_bus *bus, uint maxframes)
+{
+	struct sk_buff *pkt;
+	u32 intstatus = 0;
+	uint retries = 0;
+	int ret = 0, prec_out;
+	uint cnt = 0;
+	uint datalen;
+	u8 tx_prec_map;
+
+	struct brcmf_pub *drvr = bus->drvr;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	tx_prec_map = ~bus->flowcontrol;
+
+	/* Send frames until the limit or some other event */
+	for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
+		spin_lock_bh(&bus->txqlock);
+		pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
+		if (pkt == NULL) {
+			spin_unlock_bh(&bus->txqlock);
+			break;
+		}
+		spin_unlock_bh(&bus->txqlock);
+		datalen = pkt->len - SDPCM_HDRLEN;
+
+#ifndef SDTEST
+		ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
+#else
+		ret = brcmf_sdbrcm_txpkt(bus, pkt,
+				    (bus->ext_loop ? SDPCM_TEST_CHANNEL :
+				     SDPCM_DATA_CHANNEL), true);
+#endif
+		if (ret)
+			bus->drvr->tx_errors++;
+		else
+			bus->drvr->dstats.tx_bytes += datalen;
+
+		/* In poll mode, need to check for other events */
+		if (!bus->intr && cnt) {
+			/* Check device status, signal pending interrupt */
+			r_sdreg32(bus, &intstatus,
+				  offsetof(struct sdpcmd_regs, intstatus),
+				  &retries);
+			bus->f2txdata++;
+			if (brcmf_sdcard_regfail(bus->card))
+				break;
+			if (intstatus & bus->hostintmask)
+				bus->ipend = true;
+		}
+	}
+
+	/* Deflow-control stack if needed */
+	if (drvr->up && (drvr->busstate == BRCMF_BUS_DATA) &&
+	    drvr->txoff && (pktq_len(&bus->txq) < TXLOW))
+		brcmf_txflowcontrol(drvr, 0, OFF);
+
+	return cnt;
+}
+
+int
+brcmf_sdbrcm_bus_txctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen)
+{
+	u8 *frame;
+	u16 len;
+	u32 swheader;
+	uint retries = 0;
+	struct brcmf_sdio_card *card = bus->card;
+	u8 doff = 0;
+	int ret = -1;
+	int i;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus->drvr->dongle_reset)
+		return -EIO;
+
+	/* Back the pointer to make a room for bus header */
+	frame = msg - SDPCM_HDRLEN;
+	len = (msglen += SDPCM_HDRLEN);
+
+	/* Add alignment padding (optional for ctl frames) */
+	if (brcmf_alignctl) {
+		doff = ((unsigned long)frame % BRCMF_SDALIGN);
+		if (doff) {
+			frame -= doff;
+			len += doff;
+			msglen += doff;
+			memset(frame, 0, doff + SDPCM_HDRLEN);
+		}
+		/* precondition: doff < BRCMF_SDALIGN */
+	}
+	doff += SDPCM_HDRLEN;
+
+	/* Round send length to next SDIO block */
+	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
+		u16 pad = bus->blocksize - (len % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize))
+			len += pad;
+	} else if (len % BRCMF_SDALIGN) {
+		len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
+	}
+
+	/* Satisfy length-alignment requirements */
+	if (forcealign && (len & (ALIGNMENT - 1)))
+		len = roundup(len, ALIGNMENT);
+
+	/* precondition: IS_ALIGNED((unsigned long)frame, 2) */
+
+	/* Need to lock here to protect txseq and SDIO tx calls */
+	brcmf_sdbrcm_sdlock(bus);
+
+	BUS_WAKE(bus);
+
+	/* Make sure backplane clock is on */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
+	*(u16 *) frame = cpu_to_le16((u16) msglen);
+	*(((u16 *) frame) + 1) = cpu_to_le16(~msglen);
+
+	/* Software tag: channel, sequence number, data offset */
+	swheader =
+	    ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
+	     SDPCM_CHANNEL_MASK)
+	    | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
+			     SDPCM_DOFFSET_MASK);
+	put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
+	put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
+
+	if (!DATAOK(bus)) {
+		BRCMF_INFO(("%s: No bus credit bus->tx_max %d,"
+			    " bus->tx_seq %d\n", __func__,
+			    bus->tx_max, bus->tx_seq));
+		bus->ctrl_frame_stat = true;
+		/* Send from dpc */
+		bus->ctrl_frame_buf = frame;
+		bus->ctrl_frame_len = len;
+
+		brcmf_sdbrcm_wait_for_event(bus, &bus->ctrl_frame_stat);
+
+		if (bus->ctrl_frame_stat == false) {
+			BRCMF_INFO(("%s: ctrl_frame_stat == false\n",
+				    __func__));
+			ret = 0;
+		} else {
+			BRCMF_INFO(("%s: ctrl_frame_stat == true\n", __func__));
+			ret = -1;
+		}
+	}
+
+	if (ret == -1) {
+#ifdef BCMDBG
+		if (BRCMF_BYTES_ON() && BRCMF_CTL_ON()) {
+			printk(KERN_DEBUG "Tx Frame:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+					     frame, len);
+		} else if (BRCMF_HDRS_ON()) {
+			printk(KERN_DEBUG "TxHdr:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+					     frame, min_t(u16, len, 16));
+		}
+#endif
+
+		do {
+			bus->ctrl_frame_stat = false;
+			ret = brcmf_sdbrcm_send_buf(bus,
+				brcmf_sdcard_cur_sbwad(card), SDIO_FUNC_2,
+				F2SYNC, frame, len, NULL, NULL, NULL);
+
+			if (ret < 0) {
+				/* On failure, abort the command and
+				 terminate the frame */
+				BRCMF_INFO(("%s: sdio error %d, abort command "
+					    "and terminate frame.\n",
+					    __func__, ret));
+				bus->tx_sderrs++;
+
+				brcmf_sdcard_abort(card, SDIO_FUNC_2);
+
+				brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+						 SBSDIO_FUNC1_FRAMECTRL,
+						 SFC_WF_TERM, NULL);
+				bus->f1regdata++;
+
+				for (i = 0; i < 3; i++) {
+					u8 hi, lo;
+					hi = brcmf_sdcard_cfg_read(card,
+					     SDIO_FUNC_1,
+					     SBSDIO_FUNC1_WFRAMEBCHI,
+					     NULL);
+					lo = brcmf_sdcard_cfg_read(card,
+					     SDIO_FUNC_1,
+					     SBSDIO_FUNC1_WFRAMEBCLO,
+					     NULL);
+					bus->f1regdata += 2;
+					if ((hi == 0) && (lo == 0))
+						break;
+				}
+
+			}
+			if (ret == 0) {
+				bus->tx_seq =
+				    (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+			}
+		} while ((ret < 0) && retries++ < TXRETRIES);
+	}
+
+	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = false;
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+	}
+
+	brcmf_sdbrcm_sdunlock(bus);
+
+	if (ret)
+		bus->drvr->tx_ctlerrs++;
+	else
+		bus->drvr->tx_ctlpkts++;
+
+	return ret ? -EIO : 0;
+}
+
+int brcmf_sdbrcm_bus_rxctl(struct brcmf_bus *bus, unsigned char *msg, uint msglen)
+{
+	int timeleft;
+	uint rxlen = 0;
+	bool pending;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus->drvr->dongle_reset)
+		return -EIO;
+
+	/* Wait until control frame is available */
+	timeleft = brcmf_os_ioctl_resp_wait(bus->drvr, &bus->rxlen, &pending);
+
+	brcmf_sdbrcm_sdlock(bus);
+	rxlen = bus->rxlen;
+	memcpy(msg, bus->rxctl, min(msglen, rxlen));
+	bus->rxlen = 0;
+	brcmf_sdbrcm_sdunlock(bus);
+
+	if (rxlen) {
+		BRCMF_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
+			   __func__, rxlen, msglen));
+	} else if (timeleft == 0) {
+		BRCMF_ERROR(("%s: resumed on timeout\n", __func__));
+#ifdef BCMDBG
+		brcmf_sdbrcm_sdlock(bus);
+		brcmf_sdbrcm_checkdied(bus, NULL, 0);
+		brcmf_sdbrcm_sdunlock(bus);
+#endif				/* BCMDBG */
+	} else if (pending == true) {
+		BRCMF_CTL(("%s: cancelled\n", __func__));
+		return -ERESTARTSYS;
+	} else {
+		BRCMF_CTL(("%s: resumed for unknown reason?\n", __func__));
+#ifdef BCMDBG
+		brcmf_sdbrcm_sdlock(bus);
+		brcmf_sdbrcm_checkdied(bus, NULL, 0);
+		brcmf_sdbrcm_sdunlock(bus);
+#endif				/* BCMDBG */
+	}
+
+	if (rxlen)
+		bus->drvr->rx_ctlpkts++;
+	else
+		bus->drvr->rx_ctlerrs++;
+
+	return rxlen ? (int)rxlen : -ETIMEDOUT;
+}
+
+/* IOVar table */
+enum {
+	IOV_INTR = 1,
+	IOV_POLLRATE,
+	IOV_SDREG,
+	IOV_SBREG,
+	IOV_SDCIS,
+	IOV_MEMBYTES,
+	IOV_MEMSIZE,
+#ifdef BCMDBG
+	IOV_CHECKDIED,
+	IOV_CONS,
+	IOV_DCONSOLE_POLL,
+#endif
+	IOV_DOWNLOAD,
+	IOV_FORCEEVEN,
+	IOV_SDIOD_DRIVE,
+	IOV_READAHEAD,
+	IOV_SDRXCHAIN,
+	IOV_ALIGNCTL,
+	IOV_SDALIGN,
+	IOV_DEVRESET,
+	IOV_CPU,
+#ifdef SDTEST
+	IOV_PKTGEN,
+	IOV_EXTLOOP,
+#endif				/* SDTEST */
+	IOV_SPROM,
+	IOV_TXBOUND,
+	IOV_RXBOUND,
+	IOV_TXMINMAX,
+	IOV_IDLETIME,
+	IOV_IDLECLOCK,
+	IOV_SD1IDLE,
+	IOV_SLEEP,
+	IOV_WDTICK,
+	IOV_VARS
+};
+
+const struct brcmu_iovar brcmf_sdio_iovars[] = {
+	{"intr", IOV_INTR, 0, IOVT_BOOL, 0},
+	{"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0},
+	{"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0},
+	{"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0},
+	{"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0},
+	{"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0},
+	{"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int)},
+	{"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0},
+	{"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0},
+	{"vars", IOV_VARS, 0, IOVT_BUFFER, 0},
+	{"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0},
+	{"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0},
+	{"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0},
+	{"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0},
+	{"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0},
+	{"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0},
+	{"wdtick", IOV_WDTICK, 0, IOVT_UINT32, 0},
+#ifdef BCMDBG
+	{"cons", IOV_CONS, 0, IOVT_BUFFER, 0}
+	,
+	{"dconpoll", IOV_DCONSOLE_POLL, 0, IOVT_UINT32, 0}
+	,
+	{"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
+	,
+	{"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
+	,
+	{"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, BRCMF_IOCTL_MAXLEN}
+	,
+	{"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0}
+	,
+	{"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0}
+	,
+	{"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0}
+	,
+	{"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0}
+	,
+	{"cpu", IOV_CPU, 0, IOVT_BOOL, 0}
+	,
+	{"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0}
+	,
+#endif				/* BCMDBG */
+#ifdef SDTEST
+	{"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0}
+	,
+	{"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(struct brcmf_pktgen)}
+	,
+#endif				/* SDTEST */
+
+	{NULL, 0, 0, 0, 0}
+};
+
+static void
+brcmf_dump_pct(struct brcmu_strbuf *strbuf, char *desc, uint num, uint div)
+{
+	uint q1, q2;
+
+	if (!div) {
+		brcmu_bprintf(strbuf, "%s N/A", desc);
+	} else {
+		q1 = num / div;
+		q2 = (100 * (num - (q1 * div))) / div;
+		brcmu_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
+	}
+}
+
+void brcmf_sdbrcm_bus_dump(struct brcmf_pub *drvr, struct brcmu_strbuf *strbuf)
+{
+	struct brcmf_bus *bus = drvr->bus;
+
+	brcmu_bprintf(strbuf, "Bus SDIO structure:\n");
+	brcmu_bprintf(strbuf,
+		    "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
+		    bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
+	brcmu_bprintf(strbuf,
+		    "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
+		    bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max,
+		    bus->rxskip, bus->rxlen, bus->rx_seq);
+	brcmu_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
+		    bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
+	brcmu_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
+		    bus->pollrate, bus->pollcnt, bus->regfails);
+
+	brcmu_bprintf(strbuf, "\nAdditional counters:\n");
+	brcmu_bprintf(strbuf,
+		    "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
+		    bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
+		    bus->rxc_errors);
+	brcmu_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
+		    bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
+	brcmu_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
+		      bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
+	brcmu_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
+		    bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
+	brcmu_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs"
+		      " %d\n",
+		      (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs,
+		      bus->f2rxdata, bus->f2txdata, bus->f1regdata);
+	{
+		brcmf_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->drvr->rx_packets,
+			     (bus->f2rxhdrs + bus->f2rxdata));
+		brcmf_dump_pct(strbuf, ", pkts/f1sd", bus->drvr->rx_packets,
+			     bus->f1regdata);
+		brcmf_dump_pct(strbuf, ", pkts/sd", bus->drvr->rx_packets,
+			     (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
+		brcmf_dump_pct(strbuf, ", pkts/int", bus->drvr->rx_packets,
+			     bus->intrcount);
+		brcmu_bprintf(strbuf, "\n");
+
+		brcmf_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
+			     bus->drvr->rx_packets);
+		brcmf_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts,
+			     bus->rxglomframes);
+		brcmu_bprintf(strbuf, "\n");
+
+		brcmf_dump_pct(strbuf, "Tx: pkts/f2wr", bus->drvr->tx_packets,
+			     bus->f2txdata);
+		brcmf_dump_pct(strbuf, ", pkts/f1sd", bus->drvr->tx_packets,
+			     bus->f1regdata);
+		brcmf_dump_pct(strbuf, ", pkts/sd", bus->drvr->tx_packets,
+			     (bus->f2txdata + bus->f1regdata));
+		brcmf_dump_pct(strbuf, ", pkts/int", bus->drvr->tx_packets,
+			     bus->intrcount);
+		brcmu_bprintf(strbuf, "\n");
+
+		brcmf_dump_pct(strbuf, "Total: pkts/f2rw",
+			     (bus->drvr->tx_packets + bus->drvr->rx_packets),
+			     (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
+		brcmf_dump_pct(strbuf, ", pkts/f1sd",
+			     (bus->drvr->tx_packets + bus->drvr->rx_packets),
+			     bus->f1regdata);
+		brcmf_dump_pct(strbuf, ", pkts/sd",
+			     (bus->drvr->tx_packets + bus->drvr->rx_packets),
+			     (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata +
+			      bus->f1regdata));
+		brcmf_dump_pct(strbuf, ", pkts/int",
+			     (bus->drvr->tx_packets + bus->drvr->rx_packets),
+			     bus->intrcount);
+		brcmu_bprintf(strbuf, "\n\n");
+	}
+
+#ifdef SDTEST
+	if (bus->pktgen_count) {
+		brcmu_bprintf(strbuf, "pktgen config and count:\n");
+		brcmu_bprintf(strbuf,
+			    "freq %d count %d print %d total %d min %d len %d\n",
+			    bus->pktgen_freq, bus->pktgen_count,
+			    bus->pktgen_print, bus->pktgen_total,
+			    bus->pktgen_minlen, bus->pktgen_maxlen);
+		brcmu_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
+			    bus->pktgen_sent, bus->pktgen_rcvd,
+			    bus->pktgen_fail);
+	}
+#endif				/* SDTEST */
+#ifdef BCMDBG
+	brcmu_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
+		      bus->dpc_sched, " not ");
+	brcmu_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize,
+		    bus->roundup);
+#endif				/* BCMDBG */
+	brcmu_bprintf(strbuf,
+		    "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
+		    bus->clkstate, bus->activity, bus->idletime, bus->idlecount,
+		    bus->sleeping);
+}
+
+void brcmf_bus_clearcounts(struct brcmf_pub *drvr)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *) drvr->bus;
+
+	bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
+	bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
+	bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
+	bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
+	bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
+	bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
+}
+
+#ifdef SDTEST
+static int brcmf_sdbrcm_pktgen_get(struct brcmf_bus *bus, u8 *arg)
+{
+	struct brcmf_pktgen pktgen;
+
+	pktgen.version = BRCMF_PKTGEN_VERSION;
+	pktgen.freq = bus->pktgen_freq;
+	pktgen.count = bus->pktgen_count;
+	pktgen.print = bus->pktgen_print;
+	pktgen.total = bus->pktgen_total;
+	pktgen.minlen = bus->pktgen_minlen;
+	pktgen.maxlen = bus->pktgen_maxlen;
+	pktgen.numsent = bus->pktgen_sent;
+	pktgen.numrcvd = bus->pktgen_rcvd;
+	pktgen.numfail = bus->pktgen_fail;
+	pktgen.mode = bus->pktgen_mode;
+	pktgen.stop = bus->pktgen_stop;
+
+	memcpy(arg, &pktgen, sizeof(pktgen));
+
+	return 0;
+}
+
+static int brcmf_sdbrcm_pktgen_set(struct brcmf_bus *bus, u8 *arg)
+{
+	struct brcmf_pktgen pktgen;
+	uint oldcnt, oldmode;
+
+	memcpy(&pktgen, arg, sizeof(pktgen));
+	if (pktgen.version != BRCMF_PKTGEN_VERSION)
+		return -EINVAL;
+
+	oldcnt = bus->pktgen_count;
+	oldmode = bus->pktgen_mode;
+
+	bus->pktgen_freq = pktgen.freq;
+	bus->pktgen_count = pktgen.count;
+	bus->pktgen_print = pktgen.print;
+	bus->pktgen_total = pktgen.total;
+	bus->pktgen_minlen = pktgen.minlen;
+	bus->pktgen_maxlen = pktgen.maxlen;
+	bus->pktgen_mode = pktgen.mode;
+	bus->pktgen_stop = pktgen.stop;
+
+	bus->pktgen_tick = bus->pktgen_ptick = 0;
+	bus->pktgen_len = max(bus->pktgen_len, bus->pktgen_minlen);
+	bus->pktgen_len = min(bus->pktgen_len, bus->pktgen_maxlen);
+
+	/* Clear counts for a new pktgen (mode change, or was stopped) */
+	if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
+		bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
+
+	return 0;
+}
+#endif				/* SDTEST */
+
+static int
+brcmf_sdbrcm_membytes(struct brcmf_bus *bus, bool write, u32 address, u8 *data,
+		 uint size)
+{
+	int bcmerror = 0;
+	u32 sdaddr;
+	uint dsize;
+
+	/* Determine initial transfer parameters */
+	sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
+	if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
+		dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
+	else
+		dsize = size;
+
+	/* Set the backplane window to include the start address */
+	bcmerror = brcmf_sdbrcm_set_siaddr_window(bus, address);
+	if (bcmerror) {
+		BRCMF_ERROR(("%s: window change failed\n", __func__));
+		goto xfer_done;
+	}
+
+	/* Do the transfer(s) */
+	while (size) {
+		BRCMF_INFO(("%s: %s %d bytes at offset 0x%08x in window"
+			    " 0x%08x\n", __func__, (write ? "write" : "read"),
+			    dsize, sdaddr, (address & SBSDIO_SBWINDOW_MASK)));
+		bcmerror =
+		     brcmf_sdcard_rwdata(bus->card, write, sdaddr, data, dsize);
+		if (bcmerror) {
+			BRCMF_ERROR(("%s: membytes transfer failed\n",
+				     __func__));
+			break;
+		}
+
+		/* Adjust for next transfer (if any) */
+		size -= dsize;
+		if (size) {
+			data += dsize;
+			address += dsize;
+			bcmerror = brcmf_sdbrcm_set_siaddr_window(bus, address);
+			if (bcmerror) {
+				BRCMF_ERROR(("%s: window change failed\n",
+					     __func__));
+				break;
+			}
+			sdaddr = 0;
+			dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
+		}
+	}
+
+xfer_done:
+	/* Return the window to backplane enumeration space for core access */
+	if (brcmf_sdbrcm_set_siaddr_window(bus,
+					   brcmf_sdcard_cur_sbwad(bus->card))) {
+		BRCMF_ERROR(("%s: FAILED to set window back to 0x%x\n",
+			     __func__, brcmf_sdcard_cur_sbwad(bus->card)));
+	}
+
+	return bcmerror;
+}
+
+#ifdef BCMDBG
+static int brcmf_sdbrcm_readshared(struct brcmf_bus *bus, struct sdpcm_shared *sh)
+{
+	u32 addr;
+	int rv;
+
+	/* Read last word in memory to determine address of
+			 sdpcm_shared structure */
+	rv = brcmf_sdbrcm_membytes(bus, false, bus->ramsize - 4, (u8 *)&addr,
+				   4);
+	if (rv < 0)
+		return rv;
+
+	addr = le32_to_cpu(addr);
+
+	BRCMF_INFO(("sdpcm_shared address 0x%08X\n", addr));
+
+	/*
+	 * Check if addr is valid.
+	 * NVRAM length at the end of memory should have been overwritten.
+	 */
+	if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
+		BRCMF_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n",
+			     __func__, addr));
+		return -EBADE;
+	}
+
+	/* Read rte_shared structure */
+	rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *) sh,
+			      sizeof(struct sdpcm_shared));
+	if (rv < 0)
+		return rv;
+
+	/* Endianness */
+	sh->flags = le32_to_cpu(sh->flags);
+	sh->trap_addr = le32_to_cpu(sh->trap_addr);
+	sh->assert_exp_addr = le32_to_cpu(sh->assert_exp_addr);
+	sh->assert_file_addr = le32_to_cpu(sh->assert_file_addr);
+	sh->assert_line = le32_to_cpu(sh->assert_line);
+	sh->console_addr = le32_to_cpu(sh->console_addr);
+	sh->msgtrace_addr = le32_to_cpu(sh->msgtrace_addr);
+
+	if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
+		BRCMF_ERROR(("%s: sdpcm_shared version %d in brcmf "
+			     "is different than sdpcm_shared version %d in dongle\n",
+			     __func__, SDPCM_SHARED_VERSION,
+			     sh->flags & SDPCM_SHARED_VERSION_MASK));
+		return -EBADE;
+	}
+
+	return 0;
+}
+
+static int brcmf_sdbrcm_checkdied(struct brcmf_bus *bus, u8 *data, uint size)
+{
+	int bcmerror = 0;
+	uint msize = 512;
+	char *mbuffer = NULL;
+	uint maxstrlen = 256;
+	char *str = NULL;
+	struct brcmf_trap tr;
+	struct sdpcm_shared sdpcm_shared;
+	struct brcmu_strbuf strbuf;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (data == NULL) {
+		/*
+		 * Called after a rx ctrl timeout. "data" is NULL.
+		 * allocate memory to trace the trap or assert.
+		 */
+		size = msize;
+		mbuffer = data = kmalloc(msize, GFP_ATOMIC);
+		if (mbuffer == NULL) {
+			BRCMF_ERROR(("%s: kmalloc(%d) failed\n", __func__,
+				     msize));
+			bcmerror = -ENOMEM;
+			goto done;
+		}
+	}
+
+	str = kmalloc(maxstrlen, GFP_ATOMIC);
+	if (str == NULL) {
+		BRCMF_ERROR(("%s: kmalloc(%d) failed\n", __func__, maxstrlen));
+		bcmerror = -ENOMEM;
+		goto done;
+	}
+
+	bcmerror = brcmf_sdbrcm_readshared(bus, &sdpcm_shared);
+	if (bcmerror < 0)
+		goto done;
+
+	brcmu_binit(&strbuf, data, size);
+
+	brcmu_bprintf(&strbuf,
+		    "msgtrace address : 0x%08X\nconsole address  : 0x%08X\n",
+		    sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
+
+	if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
+		/* NOTE: Misspelled assert is intentional - DO NOT FIX.
+		 * (Avoids conflict with real asserts for programmatic
+		 * parsing of output.)
+		 */
+		brcmu_bprintf(&strbuf, "Assrt not built in dongle\n");
+	}
+
+	if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) ==
+	    0) {
+		/* NOTE: Misspelled assert is intentional - DO NOT FIX.
+		 * (Avoids conflict with real asserts for programmatic
+		 * parsing of output.)
+		 */
+		brcmu_bprintf(&strbuf, "No trap%s in dongle",
+			    (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
+			    ? "/assrt" : "");
+	} else {
+		if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
+			/* Download assert */
+			brcmu_bprintf(&strbuf, "Dongle assert");
+			if (sdpcm_shared.assert_exp_addr != 0) {
+				str[0] = '\0';
+				bcmerror = brcmf_sdbrcm_membytes(bus, false,
+						sdpcm_shared.assert_exp_addr,
+						(u8 *) str, maxstrlen);
+				if (bcmerror < 0)
+					goto done;
+
+				str[maxstrlen - 1] = '\0';
+				brcmu_bprintf(&strbuf, " expr \"%s\"", str);
+			}
+
+			if (sdpcm_shared.assert_file_addr != 0) {
+				str[0] = '\0';
+				bcmerror = brcmf_sdbrcm_membytes(bus, false,
+						sdpcm_shared.assert_file_addr,
+						(u8 *) str, maxstrlen);
+				if (bcmerror < 0)
+					goto done;
+
+				str[maxstrlen - 1] = '\0';
+				brcmu_bprintf(&strbuf, " file \"%s\"", str);
+			}
+
+			brcmu_bprintf(&strbuf, " line %d ",
+				    sdpcm_shared.assert_line);
+		}
+
+		if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+			bcmerror = brcmf_sdbrcm_membytes(bus, false,
+					sdpcm_shared.trap_addr, (u8 *)&tr,
+					sizeof(struct brcmf_trap));
+			if (bcmerror < 0)
+				goto done;
+
+			brcmu_bprintf(&strbuf,
+				    "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
+				    "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
+				    "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n",
+				    tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13,
+				    tr.r14, tr.pc, sdpcm_shared.trap_addr,
+				    tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5,
+				    tr.r6, tr.r7);
+		}
+	}
+
+	if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP))
+		BRCMF_ERROR(("%s: %s\n", __func__, strbuf.origbuf));
+
+#ifdef BCMDBG
+	if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
+		/* Mem dump to a file on device */
+		brcmf_sdbrcm_mem_dump(bus);
+	}
+#endif				/* BCMDBG */
+
+done:
+	kfree(mbuffer);
+	kfree(str);
+
+	return bcmerror;
+}
+
+static int brcmf_sdbrcm_mem_dump(struct brcmf_bus *bus)
+{
+	int ret = 0;
+	int size;		/* Full mem size */
+	int start = 0;		/* Start address */
+	int read_size = 0;	/* Read size of each iteration */
+	u8 *buf = NULL, *databuf = NULL;
+
+	/* Get full mem size */
+	size = bus->ramsize;
+	buf = kmalloc(size, GFP_ATOMIC);
+	if (!buf) {
+		BRCMF_ERROR(("%s: Out of memory (%d bytes)\n", __func__, size));
+		return -1;
+	}
+
+	/* Read mem content */
+	printk(KERN_DEBUG "Dump dongle memory");
+	databuf = buf;
+	while (size) {
+		read_size = min(MEMBLOCK, size);
+		ret = brcmf_sdbrcm_membytes(bus, false, start, databuf,
+					  read_size);
+		if (ret) {
+			BRCMF_ERROR(("%s: Error membytes %d\n", __func__, ret));
+			kfree(buf);
+			return -1;
+		}
+		printk(".");
+
+		/* Decrement size and increment start address */
+		size -= read_size;
+		start += read_size;
+		databuf += read_size;
+	}
+	printk(KERN_DEBUG "Done\n");
+
+	/* free buf before return !!! */
+	if (brcmf_write_to_file(bus->drvr, buf, bus->ramsize)) {
+		BRCMF_ERROR(("%s: Error writing to files\n", __func__));
+		return -1;
+	}
+
+	/* buf free handled in brcmf_write_to_file, not here */
+	return 0;
+}
+
+#define CONSOLE_LINE_MAX	192
+
+static int brcmf_sdbrcm_readconsole(struct brcmf_bus *bus)
+{
+	struct brcmf_console *c = &bus->console;
+	u8 line[CONSOLE_LINE_MAX], ch;
+	u32 n, idx, addr;
+	int rv;
+
+	/* Don't do anything until FWREADY updates console address */
+	if (bus->console_addr == 0)
+		return 0;
+
+	/* Read console log struct */
+	addr = bus->console_addr + offsetof(struct rte_console, log);
+	rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log,
+				sizeof(c->log));
+	if (rv < 0)
+		return rv;
+
+	/* Allocate console buffer (one time only) */
+	if (c->buf == NULL) {
+		c->bufsize = le32_to_cpu(c->log.buf_size);
+		c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
+		if (c->buf == NULL)
+			return -ENOMEM;
+	}
+
+	idx = le32_to_cpu(c->log.idx);
+
+	/* Protect against corrupt value */
+	if (idx > c->bufsize)
+		return -EBADE;
+
+	/* Skip reading the console buffer if the index pointer
+	 has not moved */
+	if (idx == c->last)
+		return 0;
+
+	/* Read the console buffer */
+	addr = le32_to_cpu(c->log.buf);
+	rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize);
+	if (rv < 0)
+		return rv;
+
+	while (c->last != idx) {
+		for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
+			if (c->last == idx) {
+				/* This would output a partial line.
+				 * Instead, back up
+				 * the buffer pointer and output this
+				 * line next time around.
+				 */
+				if (c->last >= n)
+					c->last -= n;
+				else
+					c->last = c->bufsize - n;
+				goto break2;
+			}
+			ch = c->buf[c->last];
+			c->last = (c->last + 1) % c->bufsize;
+			if (ch == '\n')
+				break;
+			line[n] = ch;
+		}
+
+		if (n > 0) {
+			if (line[n - 1] == '\r')
+				n--;
+			line[n] = 0;
+			printk(KERN_DEBUG "CONSOLE: %s\n", line);
+		}
+	}
+break2:
+
+	return 0;
+}
+#endif				/* BCMDBG */
+
+int brcmf_sdbrcm_downloadvars(struct brcmf_bus *bus, void *arg, int len)
+{
+	int bcmerror = 0;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Basic sanity checks */
+	if (bus->drvr->up) {
+		bcmerror = -EISCONN;
+		goto err;
+	}
+	if (!len) {
+		bcmerror = -EOVERFLOW;
+		goto err;
+	}
+
+	/* Free the old ones and replace with passed variables */
+	kfree(bus->vars);
+
+	bus->vars = kmalloc(len, GFP_ATOMIC);
+	bus->varsz = bus->vars ? len : 0;
+	if (bus->vars == NULL) {
+		bcmerror = -ENOMEM;
+		goto err;
+	}
+
+	/* Copy the passed variables, which should include the
+		 terminating double-null */
+	memcpy(bus->vars, arg, bus->varsz);
+err:
+	return bcmerror;
+}
+
+static int
+brcmf_sdbrcm_doiovar(struct brcmf_bus *bus, const struct brcmu_iovar *vi, u32 actionid,
+		const char *name, void *params, int plen, void *arg, int len,
+		int val_size)
+{
+	int bcmerror = 0;
+	s32 int_val = 0;
+	bool bool_val = 0;
+
+	BRCMF_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p "
+		     "len %d val_size %d\n", __func__, actionid, name, params,
+		     plen, arg, len, val_size));
+
+	bcmerror = brcmu_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
+	if (bcmerror != 0)
+		goto exit;
+
+	if (plen >= (int)sizeof(int_val))
+		memcpy(&int_val, params, sizeof(int_val));
+
+	bool_val = (int_val != 0) ? true : false;
+
+	/* Some ioctls use the bus */
+	brcmf_sdbrcm_sdlock(bus);
+
+	/* Check if dongle is in reset. If so, only allow DEVRESET iovars */
+	if (bus->drvr->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
+					actionid == IOV_GVAL(IOV_DEVRESET))) {
+		bcmerror = -EPERM;
+		goto exit;
+	}
+
+	/* Handle sleep stuff before any clock mucking */
+	if (vi->varid == IOV_SLEEP) {
+		if (IOV_ISSET(actionid)) {
+			bcmerror = brcmf_sdbrcm_bussleep(bus, bool_val);
+		} else {
+			int_val = (s32) bus->sleeping;
+			memcpy(arg, &int_val, val_size);
+		}
+		goto exit;
+	}
+
+	/* Request clock to allow SDIO accesses */
+	if (!bus->drvr->dongle_reset) {
+		BUS_WAKE(bus);
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+	}
+
+	switch (actionid) {
+	case IOV_GVAL(IOV_INTR):
+		int_val = (s32) bus->intr;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_INTR):
+		bus->intr = bool_val;
+		bus->intdis = false;
+		if (bus->drvr->up) {
+			BRCMF_INTR(("%s: %s SDIO interrupts\n", __func__,
+				    bus->intr ? "enable" : "disable"));
+			if (bus->intr) {
+				brcmf_sdcard_intr_enable(bus->card);
+			} else {
+				brcmf_sdcard_intr_disable(bus->card);
+			}
+		}
+		break;
+
+	case IOV_GVAL(IOV_POLLRATE):
+		int_val = (s32) bus->pollrate;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POLLRATE):
+		bus->pollrate = (uint) int_val;
+		bus->poll = (bus->pollrate != 0);
+		break;
+
+	case IOV_GVAL(IOV_IDLETIME):
+		int_val = bus->idletime;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_IDLETIME):
+		if ((int_val < 0) && (int_val != BRCMF_IDLE_IMMEDIATE))
+			bcmerror = -EINVAL;
+		else
+			bus->idletime = int_val;
+		break;
+
+	case IOV_GVAL(IOV_IDLECLOCK):
+		int_val = (s32) bus->idleclock;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_IDLECLOCK):
+		bus->idleclock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SD1IDLE):
+		int_val = (s32) sd1idle;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SD1IDLE):
+		sd1idle = bool_val;
+		break;
+
+	case IOV_SVAL(IOV_MEMBYTES):
+	case IOV_GVAL(IOV_MEMBYTES):
+		{
+			u32 address;
+			uint size, dsize;
+			u8 *data;
+
+			bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
+
+			address = (u32) int_val;
+			memcpy(&int_val, (char *)params + sizeof(int_val),
+			       sizeof(int_val));
+			size = (uint) int_val;
+
+			/* Do some validation */
+			dsize = set ? plen - (2 * sizeof(int)) : len;
+			if (dsize < size) {
+				BRCMF_ERROR(("%s: error on %s membytes, addr "
+					     "0x%08x size %d dsize %d\n",
+					     __func__, (set ? "set" : "get"),
+					     address, size, dsize));
+				bcmerror = -EINVAL;
+				break;
+			}
+
+			BRCMF_INFO(("%s: Request to %s %d bytes at address "
+				    "0x%08x\n", __func__,
+				    (set ? "write" : "read"), size, address));
+
+			/* If we know about SOCRAM, check for a fit */
+			if ((bus->orig_ramsize) &&
+			    ((address > bus->orig_ramsize)
+			     || (address + size > bus->orig_ramsize))) {
+				BRCMF_ERROR(("%s: ramsize 0x%08x doesn't have"
+					     " %d bytes at 0x%08x\n", __func__,
+					     bus->orig_ramsize, size, address));
+				bcmerror = -EINVAL;
+				break;
+			}
+
+			/* Generate the actual data pointer */
+			data =
+			    set ? (u8 *) params +
+			    2 * sizeof(int) : (u8 *) arg;
+
+			/* Call to do the transfer */
+			bcmerror = brcmf_sdbrcm_membytes(bus, set, address,
+							 data, size);
+
+			break;
+		}
+
+	case IOV_GVAL(IOV_MEMSIZE):
+		int_val = (s32) bus->ramsize;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_GVAL(IOV_SDIOD_DRIVE):
+		int_val = (s32) brcmf_sdiod_drive_strength;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDIOD_DRIVE):
+		brcmf_sdiod_drive_strength = int_val;
+		brcmf_sdbrcm_sdiod_drive_strength_init(bus,
+					     brcmf_sdiod_drive_strength);
+		break;
+
+	case IOV_SVAL(IOV_DOWNLOAD):
+		bcmerror = brcmf_sdbrcm_download_state(bus, bool_val);
+		break;
+
+	case IOV_SVAL(IOV_VARS):
+		bcmerror = brcmf_sdbrcm_downloadvars(bus, arg, len);
+		break;
+
+	case IOV_GVAL(IOV_READAHEAD):
+		int_val = (s32) brcmf_readahead;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_READAHEAD):
+		if (bool_val && !brcmf_readahead)
+			bus->nextlen = 0;
+		brcmf_readahead = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_SDRXCHAIN):
+		int_val = (s32) bus->use_rxchain;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDRXCHAIN):
+		if (bool_val && !bus->sd_rxchain)
+			bcmerror = -ENOTSUPP;
+		else
+			bus->use_rxchain = bool_val;
+		break;
+	case IOV_GVAL(IOV_ALIGNCTL):
+		int_val = (s32) brcmf_alignctl;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_ALIGNCTL):
+		brcmf_alignctl = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_SDALIGN):
+		int_val = BRCMF_SDALIGN;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+#ifdef BCMDBG
+	case IOV_GVAL(IOV_VARS):
+		if (bus->varsz < (uint) len)
+			memcpy(arg, bus->vars, bus->varsz);
+		else
+			bcmerror = -EOVERFLOW;
+		break;
+#endif				/* BCMDBG */
+
+#ifdef BCMDBG
+	case IOV_GVAL(IOV_DCONSOLE_POLL):
+		int_val = (s32) brcmf_console_ms;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DCONSOLE_POLL):
+		brcmf_console_ms = (uint) int_val;
+		break;
+
+	case IOV_SVAL(IOV_CONS):
+		if (len > 0)
+			bcmerror = brcmf_sdbrcm_bus_console_in(bus->drvr,
+							       arg, len - 1);
+		break;
+
+	case IOV_GVAL(IOV_SDREG):
+		{
+			struct brcmf_sdreg *sd_ptr;
+			u32 addr, size;
+
+			sd_ptr = (struct brcmf_sdreg *) params;
+
+			addr = bus->ci->buscorebase + sd_ptr->offset;
+			size = sd_ptr->func;
+			int_val = (s32) brcmf_sdcard_reg_read(bus->card, addr,
+							      size);
+			if (brcmf_sdcard_regfail(bus->card))
+				bcmerror = -EIO;
+			memcpy(arg, &int_val, sizeof(s32));
+			break;
+		}
+
+	case IOV_SVAL(IOV_SDREG):
+		{
+			struct brcmf_sdreg *sd_ptr;
+			u32 addr, size;
+
+			sd_ptr = (struct brcmf_sdreg *) params;
+
+			addr = bus->ci->buscorebase + sd_ptr->offset;
+			size = sd_ptr->func;
+			brcmf_sdcard_reg_write(bus->card, addr, size,
+					       sd_ptr->value);
+			if (brcmf_sdcard_regfail(bus->card))
+				bcmerror = -EIO;
+			break;
+		}
+
+		/* Same as above, but offset is not backplane
+		 (not SDIO core) */
+	case IOV_GVAL(IOV_SBREG):
+		{
+			struct brcmf_sdreg sdreg;
+			u32 addr, size;
+
+			memcpy(&sdreg, params, sizeof(sdreg));
+
+			addr = SI_ENUM_BASE + sdreg.offset;
+			size = sdreg.func;
+			int_val = (s32) brcmf_sdcard_reg_read(bus->card, addr,
+							      size);
+			if (brcmf_sdcard_regfail(bus->card))
+				bcmerror = -EIO;
+			memcpy(arg, &int_val, sizeof(s32));
+			break;
+		}
+
+	case IOV_SVAL(IOV_SBREG):
+		{
+			struct brcmf_sdreg sdreg;
+			u32 addr, size;
+
+			memcpy(&sdreg, params, sizeof(sdreg));
+
+			addr = SI_ENUM_BASE + sdreg.offset;
+			size = sdreg.func;
+			brcmf_sdcard_reg_write(bus->card, addr, size,
+					       sdreg.value);
+			if (brcmf_sdcard_regfail(bus->card))
+				bcmerror = -EIO;
+			break;
+		}
+
+	case IOV_GVAL(IOV_SDCIS):
+		{
+			*(char *)arg = 0;
+
+			strcat(arg, "\nFunc 0\n");
+			brcmf_sdcard_cis_read(bus->card, 0x10,
+					(u8 *) arg + strlen(arg),
+					SBSDIO_CIS_SIZE_LIMIT);
+			strcat(arg, "\nFunc 1\n");
+			brcmf_sdcard_cis_read(bus->card, 0x11,
+					(u8 *) arg + strlen(arg),
+					SBSDIO_CIS_SIZE_LIMIT);
+			strcat(arg, "\nFunc 2\n");
+			brcmf_sdcard_cis_read(bus->card, 0x12,
+					(u8 *) arg + strlen(arg),
+					SBSDIO_CIS_SIZE_LIMIT);
+			break;
+		}
+
+	case IOV_GVAL(IOV_FORCEEVEN):
+		int_val = (s32) forcealign;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_FORCEEVEN):
+		forcealign = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_TXBOUND):
+		int_val = (s32) brcmf_txbound;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_TXBOUND):
+		brcmf_txbound = (uint) int_val;
+		break;
+
+	case IOV_GVAL(IOV_RXBOUND):
+		int_val = (s32) brcmf_rxbound;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_RXBOUND):
+		brcmf_rxbound = (uint) int_val;
+		break;
+
+	case IOV_GVAL(IOV_TXMINMAX):
+		int_val = (s32) brcmf_txminmax;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_TXMINMAX):
+		brcmf_txminmax = (uint) int_val;
+		break;
+#endif				/* BCMDBG */
+
+#ifdef SDTEST
+	case IOV_GVAL(IOV_EXTLOOP):
+		int_val = (s32) bus->ext_loop;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_EXTLOOP):
+		bus->ext_loop = bool_val;
+		break;
+
+	case IOV_GVAL(IOV_PKTGEN):
+		bcmerror = brcmf_sdbrcm_pktgen_get(bus, arg);
+		break;
+
+	case IOV_SVAL(IOV_PKTGEN):
+		bcmerror = brcmf_sdbrcm_pktgen_set(bus, arg);
+		break;
+#endif				/* SDTEST */
+
+	case IOV_SVAL(IOV_DEVRESET):
+		BRCMF_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d "
+			     "busstate=%d\n",
+			     __func__, bool_val, bus->drvr->dongle_reset,
+			     bus->drvr->busstate));
+
+		brcmf_bus_devreset(bus->drvr, (u8) bool_val);
+
+		break;
+
+	case IOV_GVAL(IOV_DEVRESET):
+		BRCMF_TRACE(("%s: Called get IOV_DEVRESET\n", __func__));
+
+		/* Get its status */
+		int_val = (bool) bus->drvr->dongle_reset;
+		memcpy(arg, &int_val, val_size);
+
+		break;
+
+	case IOV_GVAL(IOV_WDTICK):
+		int_val = (s32) brcmf_watchdog_ms;
+		memcpy(arg, &int_val, val_size);
+		break;
+
+	case IOV_SVAL(IOV_WDTICK):
+		if (!bus->drvr->up) {
+			bcmerror = -ENOLINK;
+			break;
+		}
+		brcmf_sdbrcm_wd_timer(bus, (uint) int_val);
+		break;
+
+	default:
+		bcmerror = -ENOTSUPP;
+		break;
+	}
+
+exit:
+	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = false;
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+	}
+
+	brcmf_sdbrcm_sdunlock(bus);
+
+	if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == false)
+		brcmf_c_preinit_ioctls(bus->drvr);
+
+	return bcmerror;
+}
+
+static int brcmf_sdbrcm_write_vars(struct brcmf_bus *bus)
+{
+	int bcmerror = 0;
+	u32 varsize;
+	u32 varaddr;
+	u8 *vbuffer;
+	u32 varsizew;
+#ifdef BCMDBG
+	char *nvram_ularray;
+#endif				/* BCMDBG */
+
+	/* Even if there are no vars are to be written, we still
+		 need to set the ramsize. */
+	varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
+	varaddr = (bus->ramsize - 4) - varsize;
+
+	if (bus->vars) {
+		vbuffer = kzalloc(varsize, GFP_ATOMIC);
+		if (!vbuffer)
+			return -ENOMEM;
+
+		memcpy(vbuffer, bus->vars, bus->varsz);
+
+		/* Write the vars list */
+		bcmerror =
+		    brcmf_sdbrcm_membytes(bus, true, varaddr, vbuffer, varsize);
+#ifdef BCMDBG
+		/* Verify NVRAM bytes */
+		BRCMF_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
+		nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
+		if (!nvram_ularray)
+			return -ENOMEM;
+
+		/* Upload image to verify downloaded contents. */
+		memset(nvram_ularray, 0xaa, varsize);
+
+		/* Read the vars list to temp buffer for comparison */
+		bcmerror =
+		    brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray,
+				     varsize);
+		if (bcmerror) {
+			BRCMF_ERROR(("%s: error %d on reading %d nvram bytes"
+				     " at 0x%08x\n", __func__, bcmerror,
+				     varsize, varaddr));
+		}
+		/* Compare the org NVRAM with the one read from RAM */
+		if (memcmp(vbuffer, nvram_ularray, varsize)) {
+			BRCMF_ERROR(("%s: Downloaded NVRAM image is "
+				     "corrupted.\n", __func__));
+		} else
+			BRCMF_ERROR(("%s: Download/Upload/Compare of"
+				     " NVRAM ok.\n", __func__));
+
+		kfree(nvram_ularray);
+#endif				/* BCMDBG */
+
+		kfree(vbuffer);
+	}
+
+	/* adjust to the user specified RAM */
+	BRCMF_INFO(("Physical memory size: %d, usable memory size: %d\n",
+		    bus->orig_ramsize, bus->ramsize));
+	BRCMF_INFO(("Vars are at %d, orig varsize is %d\n", varaddr, varsize));
+	varsize = ((bus->orig_ramsize - 4) - varaddr);
+
+	/*
+	 * Determine the length token:
+	 * Varsize, converted to words, in lower 16-bits, checksum
+	 * in upper 16-bits.
+	 */
+	if (bcmerror) {
+		varsizew = 0;
+	} else {
+		varsizew = varsize / 4;
+		varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
+		varsizew = cpu_to_le32(varsizew);
+	}
+
+	BRCMF_INFO(("New varsize is %d, length token=0x%08x\n", varsize,
+		    varsizew));
+
+	/* Write the length token to the last word */
+	bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->orig_ramsize - 4),
+				    (u8 *)&varsizew, 4);
+
+	return bcmerror;
+}
+
+static int brcmf_sdbrcm_download_state(struct brcmf_bus *bus, bool enter)
+{
+	uint retries;
+	u32 regdata;
+	int bcmerror = 0;
+
+	/* To enter download state, disable ARM and reset SOCRAM.
+	 * To exit download state, simply reset ARM (default is RAM boot).
+	 */
+	if (enter) {
+		bus->alp_only = true;
+
+		brcmf_sdbrcm_chip_disablecore(bus->card, bus->ci->armcorebase);
+
+		brcmf_sdbrcm_chip_resetcore(bus->card, bus->ci->ramcorebase);
+
+		/* Clear the top bit of memory */
+		if (bus->ramsize) {
+			u32 zeros = 0;
+			brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4,
+					 (u8 *)&zeros, 4);
+		}
+	} else {
+		regdata = brcmf_sdcard_reg_read(bus->card,
+			CORE_SB(bus->ci->ramcorebase, sbtmstatelow), 4);
+		regdata &= (SBTML_RESET | SBTML_REJ_MASK |
+			(SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+		if ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) != regdata) {
+			BRCMF_ERROR(("%s: SOCRAM core is down after reset?\n",
+				     __func__));
+			bcmerror = -EBADE;
+			goto fail;
+		}
+
+		bcmerror = brcmf_sdbrcm_write_vars(bus);
+		if (bcmerror) {
+			BRCMF_ERROR(("%s: no vars written to RAM\n", __func__));
+			bcmerror = 0;
+		}
+
+		w_sdreg32(bus, 0xFFFFFFFF,
+			  offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+		brcmf_sdbrcm_chip_resetcore(bus->card, bus->ci->armcorebase);
+
+		/* Allow HT Clock now that the ARM is running. */
+		bus->alp_only = false;
+
+		bus->drvr->busstate = BRCMF_BUS_LOAD;
+	}
+fail:
+	return bcmerror;
+}
+
+int
+brcmf_sdbrcm_bus_iovar_op(struct brcmf_pub *drvr, const char *name,
+			  void *params, int plen, void *arg, int len, bool set)
+{
+	struct brcmf_bus *bus = drvr->bus;
+	const struct brcmu_iovar *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	u32 actionid;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (name == NULL || len <= 0)
+		return -EINVAL;
+
+	/* Set does not take qualifiers */
+	if (set && (params || plen))
+		return -EINVAL;
+
+	/* Get must have return space;*/
+	if (!set && !(arg && len))
+		return -EINVAL;
+
+	/* Look up var locally; if not found pass to host driver */
+	vi = brcmu_iovar_lookup(brcmf_sdio_iovars, name);
+	if (vi == NULL) {
+		brcmf_sdbrcm_sdlock(bus);
+
+		BUS_WAKE(bus);
+
+		/* Turn on clock in case SD command needs backplane */
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+		bcmerror = brcmf_sdcard_iovar_op(bus->card, name, params, plen,
+						 arg, len, set);
+
+		/* Similar check for blocksize change */
+		if (set && strcmp(name, "sd_blocksize") == 0) {
+			s32 fnum = 2;
+			if (brcmf_sdcard_iovar_op
+			    (bus->card, "sd_blocksize", &fnum, sizeof(s32),
+			     &bus->blocksize, sizeof(s32),
+			     false) != 0) {
+				bus->blocksize = 0;
+				BRCMF_ERROR(("%s: fail on %s get\n", __func__,
+					     "sd_blocksize"));
+			} else {
+				BRCMF_INFO(("%s: noted sd_blocksize update,"
+					    " value now %d\n", __func__,
+					    bus->blocksize));
+			}
+		}
+		bus->roundup = min(max_roundup, bus->blocksize);
+
+		if (bus->idletime == BRCMF_IDLE_IMMEDIATE &&
+		    !bus->dpc_sched) {
+			bus->activity = false;
+			brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+		}
+
+		brcmf_sdbrcm_sdunlock(bus);
+		goto exit;
+	}
+
+	BRCMF_CTL(("%s: %s %s, len %d plen %d\n", __func__,
+		   name, (set ? "set" : "get"), len, plen));
+
+	/* set up 'params' pointer in case this is a set command so that
+	 * the convenience int and bool code can be common to set and get
+	 */
+	if (params == NULL) {
+		params = arg;
+		plen = len;
+	}
+
+	if (vi->type == IOVT_VOID)
+		val_size = 0;
+	else if (vi->type == IOVT_BUFFER)
+		val_size = len;
+	else
+		/* all other types are integer sized */
+		val_size = sizeof(int);
+
+	actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
+	bcmerror = brcmf_sdbrcm_doiovar(bus, vi, actionid, name, params, plen,
+					arg, len, val_size);
+
+exit:
+	return bcmerror;
+}
+
+void brcmf_sdbrcm_bus_stop(struct brcmf_bus *bus, bool enforce_mutex)
+{
+	u32 local_hostintmask;
+	u8 saveclk;
+	uint retries;
+	int err;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (enforce_mutex)
+		brcmf_sdbrcm_sdlock(bus);
+
+	BUS_WAKE(bus);
+
+	/* Enable clock for device interrupts */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+	if (bus->watchdog_tsk) {
+		send_sig(SIGTERM, bus->watchdog_tsk, 1);
+		kthread_stop(bus->watchdog_tsk);
+		bus->watchdog_tsk = NULL;
+	}
+
+	if (bus->dpc_tsk) {
+		send_sig(SIGTERM, bus->dpc_tsk, 1);
+		kthread_stop(bus->dpc_tsk);
+		bus->dpc_tsk = NULL;
+	} else
+		tasklet_kill(&bus->tasklet);
+
+	/* Disable and clear interrupts at the chip level also */
+	w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask), &retries);
+	local_hostintmask = bus->hostintmask;
+	bus->hostintmask = 0;
+
+	/* Change our idea of bus state */
+	bus->drvr->busstate = BRCMF_BUS_DOWN;
+
+	/* Force clocks on backplane to be sure F2 interrupt propagates */
+	saveclk = brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_1,
+					SBSDIO_FUNC1_CHIPCLKCSR, &err);
+	if (!err) {
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_CHIPCLKCSR,
+				       (saveclk | SBSDIO_FORCE_HT), &err);
+	}
+	if (err) {
+		BRCMF_ERROR(("%s: Failed to force clock for F2: err %d\n",
+			     __func__, err));
+	}
+
+	/* Turn off the bus (F2), free any pending packets */
+	BRCMF_INTR(("%s: disable SDIO interrupts\n", __func__));
+	brcmf_sdcard_intr_disable(bus->card);
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+			 SDIO_FUNC_ENABLE_1, NULL);
+
+	/* Clear any pending interrupts now that F2 is disabled */
+	w_sdreg32(bus, local_hostintmask,
+		  offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+	/* Turn off the backplane clock (only) */
+	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+	/* Clear the data packet queues */
+	brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
+
+	/* Clear any held glomming stuff */
+	if (bus->glomd)
+		brcmu_pkt_buf_free_skb(bus->glomd);
+
+	if (bus->glom)
+		brcmu_pkt_buf_free_skb(bus->glom);
+
+	bus->glom = bus->glomd = NULL;
+
+	/* Clear rx control and wake any waiters */
+	bus->rxlen = 0;
+	brcmf_os_ioctl_resp_wake(bus->drvr);
+
+	/* Reset some F2 state stuff */
+	bus->rxskip = false;
+	bus->tx_seq = bus->rx_seq = 0;
+
+	if (enforce_mutex)
+		brcmf_sdbrcm_sdunlock(bus);
+}
+
+int brcmf_sdbrcm_bus_init(struct brcmf_pub *drvr, bool enforce_mutex)
+{
+	struct brcmf_bus *bus = drvr->bus;
+	struct brcmf_timeout tmo;
+	uint retries = 0;
+	u8 ready, enable;
+	int err, ret = 0;
+	u8 saveclk;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* try to download image and nvram to the dongle */
+	if (drvr->busstate == BRCMF_BUS_DOWN) {
+		if (!(brcmf_sdbrcm_download_firmware(bus, bus->card)))
+			return -1;
+	}
+
+	if (!bus->drvr)
+		return 0;
+
+	/* Start the watchdog timer */
+	bus->drvr->tickcnt = 0;
+	brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+
+	if (enforce_mutex)
+		brcmf_sdbrcm_sdlock(bus);
+
+	/* Make sure backplane clock is on, needed to generate F2 interrupt */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+	if (bus->clkstate != CLK_AVAIL)
+		goto exit;
+
+	/* Force clocks on backplane to be sure F2 interrupt propagates */
+	saveclk =
+	    brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_1,
+				  SBSDIO_FUNC1_CHIPCLKCSR, &err);
+	if (!err) {
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1,
+				       SBSDIO_FUNC1_CHIPCLKCSR,
+				       (saveclk | SBSDIO_FORCE_HT), &err);
+	}
+	if (err) {
+		BRCMF_ERROR(("%s: Failed to force clock for F2: err %d\n",
+			     __func__, err));
+		goto exit;
+	}
+
+	/* Enable function 2 (frame transfers) */
+	w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
+		  offsetof(struct sdpcmd_regs, tosbmailboxdata), &retries);
+	enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
+
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_0, SDIO_CCCR_IOEx, enable,
+			       NULL);
+
+	/* Give the dongle some time to do its thing and set IOR2 */
+	brcmf_timeout_start(&tmo, BRCMF_WAIT_F2RDY * 1000);
+
+	ready = 0;
+	while (ready != enable && !brcmf_timeout_expired(&tmo))
+		ready = brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_0,
+					      SDIO_CCCR_IORx, NULL);
+
+	BRCMF_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
+		    __func__, enable, ready, tmo.elapsed));
+
+	/* If F2 successfully enabled, set core and enable interrupts */
+	if (ready == enable) {
+		/* Set up the interrupt mask and enable interrupts */
+		bus->hostintmask = HOSTINTMASK;
+		w_sdreg32(bus, bus->hostintmask,
+			  offsetof(struct sdpcmd_regs, hostintmask), &retries);
+
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_WATERMARK,
+				 (u8) watermark, &err);
+
+		/* Set bus state according to enable result */
+		drvr->busstate = BRCMF_BUS_DATA;
+
+		bus->intdis = false;
+		if (bus->intr) {
+			BRCMF_INTR(("%s: enable SDIO device interrupts\n",
+				    __func__));
+			brcmf_sdcard_intr_enable(bus->card);
+		} else {
+			BRCMF_INTR(("%s: disable SDIO interrupts\n", __func__));
+			brcmf_sdcard_intr_disable(bus->card);
+		}
+
+	}
+
+	else {
+		/* Disable F2 again */
+		enable = SDIO_FUNC_ENABLE_1;
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+				       enable, NULL);
+	}
+
+	/* Restore previous clock setting */
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+			 saveclk, &err);
+
+#if defined(OOB_INTR_ONLY)
+	/* Host registration for OOB interrupt */
+	if (brcmf_sdio_register_oob_intr(bus->dhd)) {
+		brcmf_sdbrcm_wd_timer(bus, 0);
+		BRCMF_ERROR(("%s Host failed to resgister for OOB\n",
+			     __func__));
+		ret = -ENODEV;
+		goto exit;
+	}
+
+	/* Enable oob at firmware */
+	brcmf_sdbrcm_enable_oob_intr(bus, true);
+#endif		/* defined(OOB_INTR_ONLY) */
+
+	/* If we didn't come up, turn off backplane clock */
+	if (drvr->busstate != BRCMF_BUS_DATA)
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+
+exit:
+	if (enforce_mutex)
+		brcmf_sdbrcm_sdunlock(bus);
+
+	return ret;
+}
+
+static void brcmf_sdbrcm_rxfail(struct brcmf_bus *bus, bool abort, bool rtx)
+{
+	struct brcmf_sdio_card *card = bus->card;
+	uint retries = 0;
+	u16 lastrbc;
+	u8 hi, lo;
+	int err;
+
+	BRCMF_ERROR(("%s: %sterminate frame%s\n", __func__,
+		     (abort ? "abort command, " : ""),
+		     (rtx ? ", send NAK" : "")));
+
+	if (abort)
+		brcmf_sdcard_abort(card, SDIO_FUNC_2);
+
+	brcmf_sdcard_cfg_write(card, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
+			       SFC_RF_TERM, &err);
+	bus->f1regdata++;
+
+	/* Wait until the packet has been flushed (device/FIFO stable) */
+	for (lastrbc = retries = 0xffff; retries > 0; retries--) {
+		hi = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					   SBSDIO_FUNC1_RFRAMEBCHI, NULL);
+		lo = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					   SBSDIO_FUNC1_RFRAMEBCLO, NULL);
+		bus->f1regdata += 2;
+
+		if ((hi == 0) && (lo == 0))
+			break;
+
+		if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
+			BRCMF_ERROR(("%s: count growing: last 0x%04x now "
+				     "0x%04x\n",
+				     __func__, lastrbc, ((hi << 8) + lo)));
+		}
+		lastrbc = (hi << 8) + lo;
+	}
+
+	if (!retries) {
+		BRCMF_ERROR(("%s: count never zeroed: last 0x%04x\n",
+			     __func__, lastrbc));
+	} else {
+		BRCMF_INFO(("%s: flush took %d iterations\n", __func__,
+			    (0xffff - retries)));
+	}
+
+	if (rtx) {
+		bus->rxrtx++;
+		w_sdreg32(bus, SMB_NAK,
+			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+
+		bus->f1regdata++;
+		if (retries <= retry_limit)
+			bus->rxskip = true;
+	}
+
+	/* Clear partial in any case */
+	bus->nextlen = 0;
+
+	/* If we can't reach the device, signal failure */
+	if (err || brcmf_sdcard_regfail(card))
+		bus->drvr->busstate = BRCMF_BUS_DOWN;
+}
+
+static void
+brcmf_sdbrcm_read_control(struct brcmf_bus *bus, u8 *hdr, uint len, uint doff)
+{
+	struct brcmf_sdio_card *card = bus->card;
+	uint rdlen, pad;
+
+	int sdret;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Control data already received in aligned rxctl */
+	if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
+		goto gotpkt;
+
+	/* Set rxctl for frame (w/optional alignment) */
+	bus->rxctl = bus->rxbuf;
+	if (brcmf_alignctl) {
+		bus->rxctl += firstread;
+		pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN);
+		if (pad)
+			bus->rxctl += (BRCMF_SDALIGN - pad);
+		bus->rxctl -= firstread;
+	}
+
+	/* Copy the already-read portion over */
+	memcpy(bus->rxctl, hdr, firstread);
+	if (len <= firstread)
+		goto gotpkt;
+
+	/* Copy the full data pkt in gSPI case and process ioctl. */
+	if (bus->bus == SPI_BUS) {
+		memcpy(bus->rxctl, hdr, len);
+		goto gotpkt;
+	}
+
+	/* Raise rdlen to next SDIO block to avoid tail command */
+	rdlen = len - firstread;
+	if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
+		pad = bus->blocksize - (rdlen % bus->blocksize);
+		if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+		    ((len + pad) < bus->drvr->maxctl))
+			rdlen += pad;
+	} else if (rdlen % BRCMF_SDALIGN) {
+		rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
+	}
+
+	/* Satisfy length-alignment requirements */
+	if (forcealign && (rdlen & (ALIGNMENT - 1)))
+		rdlen = roundup(rdlen, ALIGNMENT);
+
+	/* Drop if the read is too big or it exceeds our maximum */
+	if ((rdlen + firstread) > bus->drvr->maxctl) {
+		BRCMF_ERROR(("%s: %d-byte control read exceeds %d-byte"
+			     " buffer\n", __func__, rdlen, bus->drvr->maxctl));
+		bus->drvr->rx_errors++;
+		brcmf_sdbrcm_rxfail(bus, false, false);
+		goto done;
+	}
+
+	if ((len - doff) > bus->drvr->maxctl) {
+		BRCMF_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds "
+			     "%d-byte limit\n",
+			     __func__, len, (len - doff), bus->drvr->maxctl));
+		bus->drvr->rx_errors++;
+		bus->rx_toolong++;
+		brcmf_sdbrcm_rxfail(bus, false, false);
+		goto done;
+	}
+
+	/* Read remainder of frame body into the rxctl buffer */
+	sdret = brcmf_sdcard_recv_buf(card, brcmf_sdcard_cur_sbwad(card),
+				SDIO_FUNC_2,
+				F2SYNC, (bus->rxctl + firstread), rdlen,
+				NULL, NULL, NULL);
+	bus->f2rxdata++;
+
+	/* Control frame failures need retransmission */
+	if (sdret < 0) {
+		BRCMF_ERROR(("%s: read %d control bytes failed: %d\n",
+			     __func__, rdlen, sdret));
+		bus->rxc_errors++;
+		brcmf_sdbrcm_rxfail(bus, true, true);
+		goto done;
+	}
+
+gotpkt:
+
+#ifdef BCMDBG
+	if (BRCMF_BYTES_ON() && BRCMF_CTL_ON()) {
+		printk(KERN_DEBUG "RxCtrl:\n");
+		print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, bus->rxctl, len);
+	}
+#endif
+
+	/* Point to valid data and indicate its length */
+	bus->rxctl += doff;
+	bus->rxlen = len - doff;
+
+done:
+	/* Awake any waiters */
+	brcmf_os_ioctl_resp_wake(bus->drvr);
+}
+
+static u8 brcmf_sdbrcm_rxglom(struct brcmf_bus *bus, u8 rxseq)
+{
+	u16 dlen, totlen;
+	u8 *dptr, num = 0;
+
+	u16 sublen, check;
+	struct sk_buff *pfirst, *plast, *pnext, *save_pfirst;
+
+	int errcode;
+	u8 chan, seq, doff, sfdoff;
+	u8 txmax;
+
+	int ifidx = 0;
+	bool usechain = bus->use_rxchain;
+
+	/* If packets, issue read(s) and send up packet chain */
+	/* Return sequence numbers consumed? */
+
+	BRCMF_TRACE(("brcmf_sdbrcm_rxglom: start: glomd %p glom %p\n",
+		     bus->glomd, bus->glom));
+
+	/* If there's a descriptor, generate the packet chain */
+	if (bus->glomd) {
+		pfirst = plast = pnext = NULL;
+		dlen = (u16) (bus->glomd->len);
+		dptr = bus->glomd->data;
+		if (!dlen || (dlen & 1)) {
+			BRCMF_ERROR(("%s: bad glomd len(%d),"
+				     " ignore descriptor\n",
+				     __func__, dlen));
+			dlen = 0;
+		}
+
+		for (totlen = num = 0; dlen; num++) {
+			/* Get (and move past) next length */
+			sublen = get_unaligned_le16(dptr);
+			dlen -= sizeof(u16);
+			dptr += sizeof(u16);
+			if ((sublen < SDPCM_HDRLEN) ||
+			    ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
+				BRCMF_ERROR(("%s: descriptor len %d bad: %d\n",
+					     __func__, num, sublen));
+				pnext = NULL;
+				break;
+			}
+			if (sublen % BRCMF_SDALIGN) {
+				BRCMF_ERROR(("%s: sublen %d not multiple of"
+					     " %d\n", __func__, sublen,
+					     BRCMF_SDALIGN));
+				usechain = false;
+			}
+			totlen += sublen;
+
+			/* For last frame, adjust read len so total
+				 is a block multiple */
+			if (!dlen) {
+				sublen +=
+				    (roundup(totlen, bus->blocksize) - totlen);
+				totlen = roundup(totlen, bus->blocksize);
+			}
+
+			/* Allocate/chain packet for next subframe */
+			pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
+			if (pnext == NULL) {
+				BRCMF_ERROR(("%s: bcm_pkt_buf_get_skb failed, "
+					     "num %d len %d\n", __func__,
+					     num, sublen));
+				break;
+			}
+			if (!pfirst) {
+				pfirst = plast = pnext;
+			} else {
+				plast->next = pnext;
+				plast = pnext;
+			}
+
+			/* Adhere to start alignment requirements */
+			PKTALIGN(pnext, sublen, BRCMF_SDALIGN);
+		}
+
+		/* If all allocations succeeded, save packet chain
+			 in bus structure */
+		if (pnext) {
+			BRCMF_GLOM(("%s: allocated %d-byte packet chain for %d "
+				    "subframes\n", __func__, totlen, num));
+			if (BRCMF_GLOM_ON() && bus->nextlen) {
+				if (totlen != bus->nextlen) {
+					BRCMF_GLOM(("%s: glomdesc mismatch: "
+						    "nextlen %d glomdesc %d "
+						    "rxseq %d\n", __func__,
+						    bus->nextlen,
+						    totlen, rxseq));
+				}
+			}
+			bus->glom = pfirst;
+			pfirst = pnext = NULL;
+		} else {
+			if (pfirst)
+				brcmu_pkt_buf_free_skb(pfirst);
+			bus->glom = NULL;
+			num = 0;
+		}
+
+		/* Done with descriptor packet */
+		brcmu_pkt_buf_free_skb(bus->glomd);
+		bus->glomd = NULL;
+		bus->nextlen = 0;
+	}
+
+	/* Ok -- either we just generated a packet chain,
+		 or had one from before */
+	if (bus->glom) {
+		if (BRCMF_GLOM_ON()) {
+			BRCMF_GLOM(("%s: try superframe read, packet chain:\n",
+				    __func__));
+			for (pnext = bus->glom; pnext; pnext = pnext->next) {
+				BRCMF_GLOM(("    %p: %p len 0x%04x (%d)\n",
+					    pnext, (u8 *) (pnext->data),
+					    pnext->len, pnext->len));
+			}
+		}
+
+		pfirst = bus->glom;
+		dlen = (u16) brcmu_pkttotlen(pfirst);
+
+		/* Do an SDIO read for the superframe.  Configurable iovar to
+		 * read directly into the chained packet, or allocate a large
+		 * packet and and copy into the chain.
+		 */
+		if (usechain) {
+			errcode = brcmf_sdcard_recv_buf(bus->card,
+					brcmf_sdcard_cur_sbwad(bus->card),
+					SDIO_FUNC_2,
+					F2SYNC, (u8 *) pfirst->data, dlen,
+					pfirst, NULL, NULL);
+		} else if (bus->dataptr) {
+			errcode = brcmf_sdcard_recv_buf(bus->card,
+					brcmf_sdcard_cur_sbwad(bus->card),
+					SDIO_FUNC_2,
+					F2SYNC, bus->dataptr, dlen,
+					NULL, NULL, NULL);
+			sublen = (u16) brcmu_pktfrombuf(pfirst, 0, dlen,
+						bus->dataptr);
+			if (sublen != dlen) {
+				BRCMF_ERROR(("%s: FAILED TO COPY, dlen %d "
+					     "sublen %d\n",
+					     __func__, dlen, sublen));
+				errcode = -1;
+			}
+			pnext = NULL;
+		} else {
+			BRCMF_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, "
+				     "FORCE FAILURE\n", dlen));
+			errcode = -1;
+		}
+		bus->f2rxdata++;
+
+		/* On failure, kill the superframe, allow a couple retries */
+		if (errcode < 0) {
+			BRCMF_ERROR(("%s: glom read of %d bytes failed: %d\n",
+				     __func__, dlen, errcode));
+			bus->drvr->rx_errors++;
+
+			if (bus->glomerr++ < 3) {
+				brcmf_sdbrcm_rxfail(bus, true, true);
+			} else {
+				bus->glomerr = 0;
+				brcmf_sdbrcm_rxfail(bus, true, false);
+				brcmu_pkt_buf_free_skb(bus->glom);
+				bus->rxglomfail++;
+				bus->glom = NULL;
+			}
+			return 0;
+		}
+#ifdef BCMDBG
+		if (BRCMF_GLOM_ON()) {
+			printk(KERN_DEBUG "SUPERFRAME:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+				pfirst->data, min_t(int, pfirst->len, 48));
+		}
+#endif
+
+		/* Validate the superframe header */
+		dptr = (u8 *) (pfirst->data);
+		sublen = get_unaligned_le16(dptr);
+		check = get_unaligned_le16(dptr + sizeof(u16));
+
+		chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+		seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+		bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+			BRCMF_INFO(("%s: nextlen too large (%d) seq %d\n",
+				    __func__, bus->nextlen, seq));
+			bus->nextlen = 0;
+		}
+		doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+		txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+		errcode = 0;
+		if ((u16)~(sublen ^ check)) {
+			BRCMF_ERROR(("%s (superframe): HW hdr error: len/check "
+				     "0x%04x/0x%04x\n", __func__, sublen,
+				     check));
+			errcode = -1;
+		} else if (roundup(sublen, bus->blocksize) != dlen) {
+			BRCMF_ERROR(("%s (superframe): len 0x%04x, rounded "
+				     "0x%04x, expect 0x%04x\n",
+				     __func__, sublen,
+				     roundup(sublen, bus->blocksize), dlen));
+			errcode = -1;
+		} else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
+			   SDPCM_GLOM_CHANNEL) {
+			BRCMF_ERROR(("%s (superframe): bad channel %d\n",
+				   __func__,
+				   SDPCM_PACKET_CHANNEL(&dptr
+							[SDPCM_FRAMETAG_LEN])));
+			errcode = -1;
+		} else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
+			BRCMF_ERROR(("%s (superframe): got 2nd descriptor?\n",
+				     __func__));
+			errcode = -1;
+		} else if ((doff < SDPCM_HDRLEN) ||
+			   (doff > (pfirst->len - SDPCM_HDRLEN))) {
+			BRCMF_ERROR(("%s (superframe): Bad data offset %d: "
+				     "HW %d pkt %d min %d\n",
+				     __func__, doff, sublen,
+				     pfirst->len, SDPCM_HDRLEN));
+			errcode = -1;
+		}
+
+		/* Check sequence number of superframe SW header */
+		if (rxseq != seq) {
+			BRCMF_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
+				    __func__, seq, rxseq));
+			bus->rx_badseq++;
+			rxseq = seq;
+		}
+
+		/* Check window for sanity */
+		if ((u8) (txmax - bus->tx_seq) > 0x40) {
+			BRCMF_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
+				     __func__, txmax, bus->tx_seq));
+			txmax = bus->tx_seq + 2;
+		}
+		bus->tx_max = txmax;
+
+		/* Remove superframe header, remember offset */
+		skb_pull(pfirst, doff);
+		sfdoff = doff;
+
+		/* Validate all the subframe headers */
+		for (num = 0, pnext = pfirst; pnext && !errcode;
+		     num++, pnext = pnext->next) {
+			dptr = (u8 *) (pnext->data);
+			dlen = (u16) (pnext->len);
+			sublen = get_unaligned_le16(dptr);
+			check = get_unaligned_le16(dptr + sizeof(u16));
+			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+#ifdef BCMDBG
+			if (BRCMF_GLOM_ON()) {
+				printk(KERN_DEBUG "subframe:\n");
+				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						     dptr, 32);
+			}
+#endif
+
+			if ((u16)~(sublen ^ check)) {
+				BRCMF_ERROR(("%s (subframe %d): HW hdr error: "
+					     "len/check 0x%04x/0x%04x\n",
+					     __func__, num, sublen, check));
+				errcode = -1;
+			} else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
+				BRCMF_ERROR(("%s (subframe %d): length mismatch"
+					     ": len 0x%04x, expect 0x%04x\n",
+					     __func__, num, sublen, dlen));
+				errcode = -1;
+			} else if ((chan != SDPCM_DATA_CHANNEL) &&
+				   (chan != SDPCM_EVENT_CHANNEL)) {
+				BRCMF_ERROR(("%s (subframe %d): bad channel"
+					     " %d\n", __func__, num, chan));
+				errcode = -1;
+			} else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
+				BRCMF_ERROR(("%s (subframe %d): Bad data offset"
+					     " %d: HW %d min %d\n",
+					     __func__, num, doff, sublen,
+					     SDPCM_HDRLEN));
+				errcode = -1;
+			}
+		}
+
+		if (errcode) {
+			/* Terminate frame on error, request
+				 a couple retries */
+			if (bus->glomerr++ < 3) {
+				/* Restore superframe header space */
+				skb_push(pfirst, sfdoff);
+				brcmf_sdbrcm_rxfail(bus, true, true);
+			} else {
+				bus->glomerr = 0;
+				brcmf_sdbrcm_rxfail(bus, true, false);
+				brcmu_pkt_buf_free_skb(bus->glom);
+				bus->rxglomfail++;
+				bus->glom = NULL;
+			}
+			bus->nextlen = 0;
+			return 0;
+		}
+
+		/* Basic SD framing looks ok - process each packet (header) */
+		save_pfirst = pfirst;
+		bus->glom = NULL;
+		plast = NULL;
+
+		for (num = 0; pfirst; rxseq++, pfirst = pnext) {
+			pnext = pfirst->next;
+			pfirst->next = NULL;
+
+			dptr = (u8 *) (pfirst->data);
+			sublen = get_unaligned_le16(dptr);
+			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
+			seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
+
+			BRCMF_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d "
+				    "chan %d seq %d\n",
+				    __func__, num, pfirst, pfirst->data,
+				    pfirst->len, sublen, chan, seq));
+
+			/* precondition: chan == SDPCM_DATA_CHANNEL ||
+					 chan == SDPCM_EVENT_CHANNEL */
+
+			if (rxseq != seq) {
+				BRCMF_GLOM(("%s: rx_seq %d, expected %d\n",
+					    __func__, seq, rxseq));
+				bus->rx_badseq++;
+				rxseq = seq;
+			}
+#ifdef BCMDBG
+			if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+				printk(KERN_DEBUG "Rx Subframe Data:\n");
+				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						     dptr, dlen);
+			}
+#endif
+
+			__skb_trim(pfirst, sublen);
+			skb_pull(pfirst, doff);
+
+			if (pfirst->len == 0) {
+				brcmu_pkt_buf_free_skb(pfirst);
+				if (plast) {
+					plast->next = pnext;
+				} else {
+					save_pfirst = pnext;
+				}
+				continue;
+			} else if (brcmf_proto_hdrpull(bus->drvr, &ifidx, pfirst)
+					!= 0) {
+				BRCMF_ERROR(("%s: rx protocol error\n",
+					     __func__));
+				bus->drvr->rx_errors++;
+				brcmu_pkt_buf_free_skb(pfirst);
+				if (plast) {
+					plast->next = pnext;
+				} else {
+					save_pfirst = pnext;
+				}
+				continue;
+			}
+
+			/* this packet will go up, link back into
+				 chain and count it */
+			pfirst->next = pnext;
+			plast = pfirst;
+			num++;
+
+#ifdef BCMDBG
+			if (BRCMF_GLOM_ON()) {
+				BRCMF_GLOM(("%s subframe %d to stack, %p"
+					    "(%p/%d) nxt/lnk %p/%p\n",
+					    __func__, num, pfirst, pfirst->data,
+					    pfirst->len, pfirst->next,
+					    pfirst->prev));
+				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						pfirst->data,
+						min_t(int, pfirst->len, 32));
+			}
+#endif				/* BCMDBG */
+		}
+		if (num) {
+			brcmf_sdbrcm_sdunlock(bus);
+			brcmf_rx_frame(bus->drvr, ifidx, save_pfirst, num);
+			brcmf_sdbrcm_sdlock(bus);
+		}
+
+		bus->rxglomframes++;
+		bus->rxglompkts += num;
+	}
+	return num;
+}
+
+/* Return true if there may be more frames to read */
+static uint
+brcmf_sdbrcm_readframes(struct brcmf_bus *bus, uint maxframes, bool *finished)
+{
+	struct brcmf_sdio_card *card = bus->card;
+
+	u16 len, check;	/* Extracted hardware header fields */
+	u8 chan, seq, doff;	/* Extracted software header fields */
+	u8 fcbits;		/* Extracted fcbits from software header */
+
+	struct sk_buff *pkt;		/* Packet for event or data frames */
+	u16 pad;		/* Number of pad bytes to read */
+	u16 rdlen;		/* Total number of bytes to read */
+	u8 rxseq;		/* Next sequence number to expect */
+	uint rxleft = 0;	/* Remaining number of frames allowed */
+	int sdret;		/* Return code from calls */
+	u8 txmax;		/* Maximum tx sequence offered */
+	bool len_consistent;	/* Result of comparing readahead len and
+					 len from hw-hdr */
+	u8 *rxbuf;
+	int ifidx = 0;
+	uint rxcount = 0;	/* Total frames read */
+
+#if defined(BCMDBG) || defined(SDTEST)
+	bool sdtest = false;	/* To limit message spew from test mode */
+#endif
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+#ifdef SDTEST
+	/* Allow pktgen to override maxframes */
+	if (bus->pktgen_count && (bus->pktgen_mode == BRCMF_PKTGEN_RECV)) {
+		maxframes = bus->pktgen_count;
+		sdtest = true;
+	}
+#endif
+
+	/* Not finished unless we encounter no more frames indication */
+	*finished = false;
+
+	for (rxseq = bus->rx_seq, rxleft = maxframes;
+	     !bus->rxskip && rxleft && bus->drvr->busstate != BRCMF_BUS_DOWN;
+	     rxseq++, rxleft--) {
+
+		/* Handle glomming separately */
+		if (bus->glom || bus->glomd) {
+			u8 cnt;
+			BRCMF_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
+				    __func__, bus->glomd, bus->glom));
+			cnt = brcmf_sdbrcm_rxglom(bus, rxseq);
+			BRCMF_GLOM(("%s: rxglom returned %d\n", __func__, cnt));
+			rxseq += cnt - 1;
+			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
+			continue;
+		}
+
+		/* Try doing single read if we can */
+		if (brcmf_readahead && bus->nextlen) {
+			u16 nextlen = bus->nextlen;
+			bus->nextlen = 0;
+
+			if (bus->bus == SPI_BUS) {
+				rdlen = len = nextlen;
+			} else {
+				rdlen = len = nextlen << 4;
+
+				/* Pad read to blocksize for efficiency */
+				if (bus->roundup && bus->blocksize
+				    && (rdlen > bus->blocksize)) {
+					pad =
+					    bus->blocksize -
+					    (rdlen % bus->blocksize);
+					if ((pad <= bus->roundup)
+					    && (pad < bus->blocksize)
+					    && ((rdlen + pad + firstread) <
+						MAX_RX_DATASZ))
+						rdlen += pad;
+				} else if (rdlen % BRCMF_SDALIGN) {
+					rdlen +=
+					    BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
+				}
+			}
+
+			/* We use bus->rxctl buffer in WinXP for initial
+			 * control pkt receives.
+			 * Later we use buffer-poll for data as well
+			 * as control packets.
+			 * This is required because dhd receives full
+			 * frame in gSPI unlike SDIO.
+			 * After the frame is received we have to
+			 * distinguish whether it is data
+			 * or non-data frame.
+			 */
+			/* Allocate a packet buffer */
+			pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN);
+			if (!pkt) {
+				if (bus->bus == SPI_BUS) {
+					bus->usebufpool = false;
+					bus->rxctl = bus->rxbuf;
+					if (brcmf_alignctl) {
+						bus->rxctl += firstread;
+						pad = ((unsigned long)bus->rxctl %
+						      BRCMF_SDALIGN);
+						if (pad)
+							bus->rxctl +=
+							    (BRCMF_SDALIGN - pad);
+						bus->rxctl -= firstread;
+					}
+					rxbuf = bus->rxctl;
+					/* Read the entire frame */
+					sdret = brcmf_sdcard_recv_buf(card,
+						   brcmf_sdcard_cur_sbwad(card),
+						   SDIO_FUNC_2, F2SYNC,
+						   rxbuf, rdlen,
+						   NULL, NULL, NULL);
+					bus->f2rxdata++;
+
+					/* Control frame failures need
+					 retransmission */
+					if (sdret < 0) {
+						BRCMF_ERROR(("%s: read %d "
+							     "control bytes "
+							     "failed: %d\n",
+							     __func__,
+							     rdlen, sdret));
+						/* dhd.rx_ctlerrs is higher */
+						bus->rxc_errors++;
+						brcmf_sdbrcm_rxfail(bus, true,
+						       (bus->bus ==
+							SPI_BUS) ? false
+						       : true);
+						continue;
+					}
+				} else {
+					/* Give up on data,
+					request rtx of events */
+					BRCMF_ERROR(("%s (nextlen): "
+						     "brcmu_pkt_buf_get_skb "
+						     "failed:"
+						     " len %d rdlen %d expected"
+						     " rxseq %d\n", __func__,
+						     len, rdlen, rxseq));
+					continue;
+				}
+			} else {
+				if (bus->bus == SPI_BUS)
+					bus->usebufpool = true;
+
+				PKTALIGN(pkt, rdlen, BRCMF_SDALIGN);
+				rxbuf = (u8 *) (pkt->data);
+				/* Read the entire frame */
+				sdret = brcmf_sdcard_recv_buf(card,
+						brcmf_sdcard_cur_sbwad(card),
+						SDIO_FUNC_2, F2SYNC,
+						rxbuf, rdlen,
+						pkt, NULL, NULL);
+				bus->f2rxdata++;
+
+				if (sdret < 0) {
+					BRCMF_ERROR(("%s (nextlen): read %d"
+						     " bytes failed: %d\n",
+						     __func__, rdlen, sdret));
+					brcmu_pkt_buf_free_skb(pkt);
+					bus->drvr->rx_errors++;
+					/* Force retry w/normal header read.
+					 * Don't attempt NAK for
+					 * gSPI
+					 */
+					brcmf_sdbrcm_rxfail(bus, true,
+						       (bus->bus ==
+							SPI_BUS) ? false :
+						       true);
+					continue;
+				}
+			}
+
+			/* Now check the header */
+			memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
+
+			/* Extract hardware header fields */
+			len = get_unaligned_le16(bus->rxhdr);
+			check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
+
+			/* All zeros means readahead info was bad */
+			if (!(len | check)) {
+				BRCMF_INFO(("%s (nextlen): read zeros in HW "
+					    "header???\n", __func__));
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			/* Validate check bytes */
+			if ((u16)~(len ^ check)) {
+				BRCMF_ERROR(("%s (nextlen): HW hdr error:"
+					     " nextlen/len/check"
+					     " 0x%04x/0x%04x/0x%04x\n",
+					     __func__, nextlen, len, check));
+				bus->rx_badhdr++;
+				brcmf_sdbrcm_rxfail(bus, false, false);
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			/* Validate frame length */
+			if (len < SDPCM_HDRLEN) {
+				BRCMF_ERROR(("%s (nextlen): HW hdr length "
+					     "invalid: %d\n", __func__, len));
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			/* Check for consistency withreadahead info */
+			len_consistent = (nextlen != (roundup(len, 16) >> 4));
+			if (len_consistent) {
+				/* Mismatch, force retry w/normal
+					header (may be >4K) */
+				BRCMF_ERROR(("%s (nextlen): mismatch, "
+					     "nextlen %d len %d rnd %d; "
+					     "expected rxseq %d\n",
+					     __func__, nextlen,
+					     len, roundup(len, 16), rxseq));
+				brcmf_sdbrcm_rxfail(bus, true,
+						  bus->bus != SPI_BUS);
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			/* Extract software header fields */
+			chan = SDPCM_PACKET_CHANNEL(
+					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			seq = SDPCM_PACKET_SEQUENCE(
+					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			doff = SDPCM_DOFFSET_VALUE(
+					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+			txmax = SDPCM_WINDOW_VALUE(
+					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+			bus->nextlen =
+			    bus->rxhdr[SDPCM_FRAMETAG_LEN +
+				       SDPCM_NEXTLEN_OFFSET];
+			if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+				BRCMF_INFO(("%s (nextlen): got frame w/nextlen"
+					    " too large (%d), seq %d\n",
+					    __func__, bus->nextlen, seq));
+				bus->nextlen = 0;
+			}
+
+			bus->drvr->rx_readahead_cnt++;
+
+			/* Handle Flow Control */
+			fcbits = SDPCM_FCMASK_VALUE(
+					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+			if (bus->flowcontrol != fcbits) {
+				if (~bus->flowcontrol & fcbits)
+					bus->fc_xoff++;
+
+				if (bus->flowcontrol & ~fcbits)
+					bus->fc_xon++;
+
+				bus->fc_rcvd++;
+				bus->flowcontrol = fcbits;
+			}
+
+			/* Check and update sequence number */
+			if (rxseq != seq) {
+				BRCMF_INFO(("%s (nextlen): rx_seq %d, expected "
+					    "%d\n", __func__, seq, rxseq));
+				bus->rx_badseq++;
+				rxseq = seq;
+			}
+
+			/* Check window for sanity */
+			if ((u8) (txmax - bus->tx_seq) > 0x40) {
+				BRCMF_ERROR(("%s: got unlikely tx max %d with "
+					     "tx_seq %d\n",
+					     __func__, txmax, bus->tx_seq));
+				txmax = bus->tx_seq + 2;
+			}
+			bus->tx_max = txmax;
+
+#ifdef BCMDBG
+			if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+				printk(KERN_DEBUG "Rx Data:\n");
+				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						     rxbuf, len);
+			} else if (BRCMF_HDRS_ON()) {
+				printk(KERN_DEBUG "RxHdr:\n");
+				print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+						     bus->rxhdr, SDPCM_HDRLEN);
+			}
+#endif
+
+			if (chan == SDPCM_CONTROL_CHANNEL) {
+				if (bus->bus == SPI_BUS) {
+					brcmf_sdbrcm_read_control(bus, rxbuf,
+								  len, doff);
+				} else {
+					BRCMF_ERROR(("%s (nextlen): readahead"
+						     " on control packet %d?\n",
+						     __func__, seq));
+					/* Force retry w/normal header read */
+					bus->nextlen = 0;
+					brcmf_sdbrcm_rxfail(bus, false, true);
+				}
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
+				BRCMF_ERROR(("Received %d bytes on %d channel."
+					     " Running out of " "rx pktbuf's or"
+					     " not yet malloced.\n",
+					     len, chan));
+				continue;
+			}
+
+			/* Validate data offset */
+			if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+				BRCMF_ERROR(("%s (nextlen): bad data offset %d:"
+					     " HW len %d min %d\n", __func__,
+					     doff, len, SDPCM_HDRLEN));
+				brcmf_sdbrcm_rxfail(bus, false, false);
+				brcmf_sdbrcm_pktfree2(bus, pkt);
+				continue;
+			}
+
+			/* All done with this one -- now deliver the packet */
+			goto deliver;
+		}
+		/* gSPI frames should not be handled in fractions */
+		if (bus->bus == SPI_BUS)
+			break;
+
+		/* Read frame header (hardware and software) */
+		sdret = brcmf_sdcard_recv_buf(card,
+				brcmf_sdcard_cur_sbwad(card),
+				SDIO_FUNC_2, F2SYNC, bus->rxhdr, firstread,
+				NULL, NULL, NULL);
+		bus->f2rxhdrs++;
+
+		if (sdret < 0) {
+			BRCMF_ERROR(("%s: RXHEADER FAILED: %d\n", __func__,
+				     sdret));
+			bus->rx_hdrfail++;
+			brcmf_sdbrcm_rxfail(bus, true, true);
+			continue;
+		}
+#ifdef BCMDBG
+		if (BRCMF_BYTES_ON() || BRCMF_HDRS_ON()) {
+			printk(KERN_DEBUG "RxHdr:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+					     bus->rxhdr, SDPCM_HDRLEN);
+		}
+#endif
+
+		/* Extract hardware header fields */
+		len = get_unaligned_le16(bus->rxhdr);
+		check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
+
+		/* All zeros means no more frames */
+		if (!(len | check)) {
+			*finished = true;
+			break;
+		}
+
+		/* Validate check bytes */
+		if ((u16) ~(len ^ check)) {
+			BRCMF_ERROR(("%s: HW hdr err: len/check "
+				     "0x%04x/0x%04x\n", __func__, len, check));
+			bus->rx_badhdr++;
+			brcmf_sdbrcm_rxfail(bus, false, false);
+			continue;
+		}
+
+		/* Validate frame length */
+		if (len < SDPCM_HDRLEN) {
+			BRCMF_ERROR(("%s: HW hdr length invalid: %d\n",
+				     __func__, len));
+			continue;
+		}
+
+		/* Extract software header fields */
+		chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+		txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+		/* Validate data offset */
+		if ((doff < SDPCM_HDRLEN) || (doff > len)) {
+			BRCMF_ERROR(("%s: Bad data offset %d: HW len %d,"
+				     " min %d seq %d\n", __func__, doff,
+				     len, SDPCM_HDRLEN, seq));
+			bus->rx_badhdr++;
+			brcmf_sdbrcm_rxfail(bus, false, false);
+			continue;
+		}
+
+		/* Save the readahead length if there is one */
+		bus->nextlen =
+		    bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
+		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
+			BRCMF_INFO(("%s (nextlen): got frame w/nextlen too"
+				    " large (%d), seq %d\n",
+				    __func__, bus->nextlen, seq));
+			bus->nextlen = 0;
+		}
+
+		/* Handle Flow Control */
+		fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
+
+		if (bus->flowcontrol != fcbits) {
+			if (~bus->flowcontrol & fcbits)
+				bus->fc_xoff++;
+
+			if (bus->flowcontrol & ~fcbits)
+				bus->fc_xon++;
+
+			bus->fc_rcvd++;
+			bus->flowcontrol = fcbits;
+		}
+
+		/* Check and update sequence number */
+		if (rxseq != seq) {
+			BRCMF_INFO(("%s: rx_seq %d, expected %d\n", __func__,
+				    seq, rxseq));
+			bus->rx_badseq++;
+			rxseq = seq;
+		}
+
+		/* Check window for sanity */
+		if ((u8) (txmax - bus->tx_seq) > 0x40) {
+			BRCMF_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
+				     __func__, txmax, bus->tx_seq));
+			txmax = bus->tx_seq + 2;
+		}
+		bus->tx_max = txmax;
+
+		/* Call a separate function for control frames */
+		if (chan == SDPCM_CONTROL_CHANNEL) {
+			brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff);
+			continue;
+		}
+
+		/* precondition: chan is either SDPCM_DATA_CHANNEL,
+		   SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or
+		   SDPCM_GLOM_CHANNEL */
+
+		/* Length to read */
+		rdlen = (len > firstread) ? (len - firstread) : 0;
+
+		/* May pad read to blocksize for efficiency */
+		if (bus->roundup && bus->blocksize &&
+			(rdlen > bus->blocksize)) {
+			pad = bus->blocksize - (rdlen % bus->blocksize);
+			if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
+			    ((rdlen + pad + firstread) < MAX_RX_DATASZ))
+				rdlen += pad;
+		} else if (rdlen % BRCMF_SDALIGN) {
+			rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
+		}
+
+		/* Satisfy length-alignment requirements */
+		if (forcealign && (rdlen & (ALIGNMENT - 1)))
+			rdlen = roundup(rdlen, ALIGNMENT);
+
+		if ((rdlen + firstread) > MAX_RX_DATASZ) {
+			/* Too long -- skip this frame */
+			BRCMF_ERROR(("%s: too long: len %d rdlen %d\n",
+				     __func__, len, rdlen));
+			bus->drvr->rx_errors++;
+			bus->rx_toolong++;
+			brcmf_sdbrcm_rxfail(bus, false, false);
+			continue;
+		}
+
+		pkt = brcmu_pkt_buf_get_skb(rdlen + firstread + BRCMF_SDALIGN);
+		if (!pkt) {
+			/* Give up on data, request rtx of events */
+			BRCMF_ERROR(("%s: brcmu_pkt_buf_get_skb failed:"
+				     " rdlen %d chan %d\n", __func__, rdlen,
+				     chan));
+			bus->drvr->rx_dropped++;
+			brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan));
+			continue;
+		}
+
+		/* Leave room for what we already read, and align remainder */
+		skb_pull(pkt, firstread);
+		PKTALIGN(pkt, rdlen, BRCMF_SDALIGN);
+
+		/* Read the remaining frame data */
+		sdret = brcmf_sdcard_recv_buf(card,
+				brcmf_sdcard_cur_sbwad(card),
+				SDIO_FUNC_2, F2SYNC, ((u8 *) (pkt->data)),
+				rdlen, pkt, NULL, NULL);
+		bus->f2rxdata++;
+
+		if (sdret < 0) {
+			BRCMF_ERROR(("%s: read %d %s bytes failed: %d\n",
+				     __func__, rdlen,
+				     ((chan == SDPCM_EVENT_CHANNEL) ? "event"
+				     : ((chan == SDPCM_DATA_CHANNEL) ? "data"
+				     : "test")), sdret));
+			brcmu_pkt_buf_free_skb(pkt);
+			bus->drvr->rx_errors++;
+			brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan));
+			continue;
+		}
+
+		/* Copy the already-read portion */
+		skb_push(pkt, firstread);
+		memcpy(pkt->data, bus->rxhdr, firstread);
+
+#ifdef BCMDBG
+		if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+			printk(KERN_DEBUG "Rx Data:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
+					     pkt->data, len);
+		}
+#endif
+
+deliver:
+		/* Save superframe descriptor and allocate packet frame */
+		if (chan == SDPCM_GLOM_CHANNEL) {
+			if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
+				BRCMF_GLOM(("%s: glom descriptor, %d bytes:\n",
+					    __func__, len));
+#ifdef BCMDBG
+				if (BRCMF_GLOM_ON()) {
+					printk(KERN_DEBUG "Glom Data:\n");
+					print_hex_dump_bytes("",
+							     DUMP_PREFIX_OFFSET,
+							     pkt->data, len);
+				}
+#endif
+				__skb_trim(pkt, len);
+				skb_pull(pkt, SDPCM_HDRLEN);
+				bus->glomd = pkt;
+			} else {
+				BRCMF_ERROR(("%s: glom superframe w/o "
+					     "descriptor!\n", __func__));
+				brcmf_sdbrcm_rxfail(bus, false, false);
+			}
+			continue;
+		}
+
+		/* Fill in packet len and prio, deliver upward */
+		__skb_trim(pkt, len);
+		skb_pull(pkt, doff);
+
+#ifdef SDTEST
+		/* Test channel packets are processed separately */
+		if (chan == SDPCM_TEST_CHANNEL) {
+			brcmf_sdbrcm_checkdied(bus, pkt, seq);
+			continue;
+		}
+#endif				/* SDTEST */
+
+		if (pkt->len == 0) {
+			brcmu_pkt_buf_free_skb(pkt);
+			continue;
+		} else if (brcmf_proto_hdrpull(bus->drvr, &ifidx, pkt) != 0) {
+			BRCMF_ERROR(("%s: rx protocol error\n", __func__));
+			brcmu_pkt_buf_free_skb(pkt);
+			bus->drvr->rx_errors++;
+			continue;
+		}
+
+		/* Unlock during rx call */
+		brcmf_sdbrcm_sdunlock(bus);
+		brcmf_rx_frame(bus->drvr, ifidx, pkt, 1);
+		brcmf_sdbrcm_sdlock(bus);
+	}
+	rxcount = maxframes - rxleft;
+#ifdef BCMDBG
+	/* Message if we hit the limit */
+	if (!rxleft && !sdtest)
+		BRCMF_DATA(("%s: hit rx limit of %d frames\n", __func__,
+			    maxframes));
+	else
+#endif				/* BCMDBG */
+		BRCMF_DATA(("%s: processed %d frames\n", __func__, rxcount));
+	/* Back off rxseq if awaiting rtx, update rx_seq */
+	if (bus->rxskip)
+		rxseq--;
+	bus->rx_seq = rxseq;
+
+	return rxcount;
+}
+
+static u32 brcmf_sdbrcm_hostmail(struct brcmf_bus *bus)
+{
+	u32 intstatus = 0;
+	u32 hmb_data;
+	u8 fcbits;
+	uint retries = 0;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Read mailbox data and ack that we did so */
+	r_sdreg32(bus, &hmb_data,
+		  offsetof(struct sdpcmd_regs, tohostmailboxdata), &retries);
+
+	if (retries <= retry_limit)
+		w_sdreg32(bus, SMB_INT_ACK,
+			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
+	bus->f1regdata += 2;
+
+	/* Dongle recomposed rx frames, accept them again */
+	if (hmb_data & HMB_DATA_NAKHANDLED) {
+		BRCMF_INFO(("Dongle reports NAK handled, expect rtx of %d\n",
+			    bus->rx_seq));
+		if (!bus->rxskip)
+			BRCMF_ERROR(("%s: unexpected NAKHANDLED!\n", __func__));
+
+		bus->rxskip = false;
+		intstatus |= I_HMB_FRAME_IND;
+	}
+
+	/*
+	 * DEVREADY does not occur with gSPI.
+	 */
+	if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
+		bus->sdpcm_ver =
+		    (hmb_data & HMB_DATA_VERSION_MASK) >>
+		    HMB_DATA_VERSION_SHIFT;
+		if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
+			BRCMF_ERROR(("Version mismatch, dongle reports %d, "
+				     "expecting %d\n",
+				     bus->sdpcm_ver, SDPCM_PROT_VERSION));
+		else
+			BRCMF_INFO(("Dongle ready, protocol version %d\n",
+				    bus->sdpcm_ver));
+	}
+
+	/*
+	 * Flow Control has been moved into the RX headers and this out of band
+	 * method isn't used any more.
+	 * remaining backward compatible with older dongles.
+	 */
+	if (hmb_data & HMB_DATA_FC) {
+		fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
+							HMB_DATA_FCDATA_SHIFT;
+
+		if (fcbits & ~bus->flowcontrol)
+			bus->fc_xoff++;
+
+		if (bus->flowcontrol & ~fcbits)
+			bus->fc_xon++;
+
+		bus->fc_rcvd++;
+		bus->flowcontrol = fcbits;
+	}
+
+	/* Shouldn't be any others */
+	if (hmb_data & ~(HMB_DATA_DEVREADY |
+			 HMB_DATA_NAKHANDLED |
+			 HMB_DATA_FC |
+			 HMB_DATA_FWREADY |
+			 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) {
+		BRCMF_ERROR(("Unknown mailbox data content: 0x%02x\n",
+			     hmb_data));
+	}
+
+	return intstatus;
+}
+
+static bool brcmf_sdbrcm_dpc(struct brcmf_bus *bus)
+{
+	struct brcmf_sdio_card *card = bus->card;
+	u32 intstatus, newstatus = 0;
+	uint retries = 0;
+	uint rxlimit = brcmf_rxbound;	/* Rx frames to read before resched */
+	uint txlimit = brcmf_txbound;	/* Tx frames to send before resched */
+	uint framecnt = 0;	/* Temporary counter of tx/rx frames */
+	bool rxdone = true;	/* Flag for no more read data */
+	bool resched = false;	/* Flag indicating resched wanted */
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Start with leftover status bits */
+	intstatus = bus->intstatus;
+
+	brcmf_sdbrcm_sdlock(bus);
+
+	/* If waiting for HTAVAIL, check status */
+	if (bus->clkstate == CLK_PENDING) {
+		int err;
+		u8 clkctl, devctl = 0;
+
+#ifdef BCMDBG
+		/* Check for inconsistent device control */
+		devctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					       SBSDIO_DEVICE_CTL, &err);
+		if (err) {
+			BRCMF_ERROR(("%s: error reading DEVCTL: %d\n",
+				     __func__, err));
+			bus->drvr->busstate = BRCMF_BUS_DOWN;
+		}
+#endif				/* BCMDBG */
+
+		/* Read CSR, if clock on switch to AVAIL, else ignore */
+		clkctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					       SBSDIO_FUNC1_CHIPCLKCSR, &err);
+		if (err) {
+			BRCMF_ERROR(("%s: error reading CSR: %d\n", __func__,
+				     err));
+			bus->drvr->busstate = BRCMF_BUS_DOWN;
+		}
+
+		BRCMF_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
+			    devctl, clkctl));
+
+		if (SBSDIO_HTAV(clkctl)) {
+			devctl = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						       SBSDIO_DEVICE_CTL, &err);
+			if (err) {
+				BRCMF_ERROR(("%s: error reading DEVCTL: %d\n",
+					     __func__, err));
+				bus->drvr->busstate = BRCMF_BUS_DOWN;
+			}
+			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+				SBSDIO_DEVICE_CTL, devctl, &err);
+			if (err) {
+				BRCMF_ERROR(("%s: error writing DEVCTL: %d\n",
+					     __func__, err));
+				bus->drvr->busstate = BRCMF_BUS_DOWN;
+			}
+			bus->clkstate = CLK_AVAIL;
+		} else {
+			goto clkwait;
+		}
+	}
+
+	BUS_WAKE(bus);
+
+	/* Make sure backplane clock is on */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
+	if (bus->clkstate == CLK_PENDING)
+		goto clkwait;
+
+	/* Pending interrupt indicates new device status */
+	if (bus->ipend) {
+		bus->ipend = false;
+		r_sdreg32(bus, &newstatus,
+			  offsetof(struct sdpcmd_regs, intstatus), &retries);
+		bus->f1regdata++;
+		if (brcmf_sdcard_regfail(bus->card))
+			newstatus = 0;
+		newstatus &= bus->hostintmask;
+		bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
+		if (newstatus) {
+			w_sdreg32(bus, newstatus,
+				  offsetof(struct sdpcmd_regs, intstatus),
+				  &retries);
+			bus->f1regdata++;
+		}
+	}
+
+	/* Merge new bits with previous */
+	intstatus |= newstatus;
+	bus->intstatus = 0;
+
+	/* Handle flow-control change: read new state in case our ack
+	 * crossed another change interrupt.  If change still set, assume
+	 * FC ON for safety, let next loop through do the debounce.
+	 */
+	if (intstatus & I_HMB_FC_CHANGE) {
+		intstatus &= ~I_HMB_FC_CHANGE;
+		w_sdreg32(bus, I_HMB_FC_CHANGE,
+			  offsetof(struct sdpcmd_regs, intstatus), &retries);
+
+		r_sdreg32(bus, &newstatus,
+			  offsetof(struct sdpcmd_regs, intstatus), &retries);
+		bus->f1regdata += 2;
+		bus->fcstate =
+		    !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
+		intstatus |= (newstatus & bus->hostintmask);
+	}
+
+	/* Handle host mailbox indication */
+	if (intstatus & I_HMB_HOST_INT) {
+		intstatus &= ~I_HMB_HOST_INT;
+		intstatus |= brcmf_sdbrcm_hostmail(bus);
+	}
+
+	/* Generally don't ask for these, can get CRC errors... */
+	if (intstatus & I_WR_OOSYNC) {
+		BRCMF_ERROR(("Dongle reports WR_OOSYNC\n"));
+		intstatus &= ~I_WR_OOSYNC;
+	}
+
+	if (intstatus & I_RD_OOSYNC) {
+		BRCMF_ERROR(("Dongle reports RD_OOSYNC\n"));
+		intstatus &= ~I_RD_OOSYNC;
+	}
+
+	if (intstatus & I_SBINT) {
+		BRCMF_ERROR(("Dongle reports SBINT\n"));
+		intstatus &= ~I_SBINT;
+	}
+
+	/* Would be active due to wake-wlan in gSPI */
+	if (intstatus & I_CHIPACTIVE) {
+		BRCMF_INFO(("Dongle reports CHIPACTIVE\n"));
+		intstatus &= ~I_CHIPACTIVE;
+	}
+
+	/* Ignore frame indications if rxskip is set */
+	if (bus->rxskip)
+		intstatus &= ~I_HMB_FRAME_IND;
+
+	/* On frame indication, read available frames */
+	if (PKT_AVAILABLE()) {
+		framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone);
+		if (rxdone || bus->rxskip)
+			intstatus &= ~I_HMB_FRAME_IND;
+		rxlimit -= min(framecnt, rxlimit);
+	}
+
+	/* Keep still-pending events for next scheduling */
+	bus->intstatus = intstatus;
+
+clkwait:
+	/* Re-enable interrupts to detect new device events (mailbox, rx frame)
+	 * or clock availability.  (Allows tx loop to check ipend if desired.)
+	 * (Unless register access seems hosed, as we may not be able to ACK...)
+	 */
+	if (bus->intr && bus->intdis && !brcmf_sdcard_regfail(card)) {
+		BRCMF_INTR(("%s: enable SDIO interrupts, rxdone %d"
+			    " framecnt %d\n", __func__, rxdone, framecnt));
+		bus->intdis = false;
+		brcmf_sdcard_intr_enable(card);
+	}
+
+	if (DATAOK(bus) && bus->ctrl_frame_stat &&
+		(bus->clkstate == CLK_AVAIL)) {
+		int ret, i;
+
+		ret = brcmf_sdbrcm_send_buf(bus, brcmf_sdcard_cur_sbwad(card),
+			SDIO_FUNC_2, F2SYNC, (u8 *) bus->ctrl_frame_buf,
+			(u32) bus->ctrl_frame_len, NULL, NULL, NULL);
+
+		if (ret < 0) {
+			/* On failure, abort the command and
+				terminate the frame */
+			BRCMF_INFO(("%s: sdio error %d, abort command and "
+				    "terminate frame.\n", __func__, ret));
+			bus->tx_sderrs++;
+
+			brcmf_sdcard_abort(card, SDIO_FUNC_2);
+
+			brcmf_sdcard_cfg_write(card, SDIO_FUNC_1,
+					 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
+					 NULL);
+			bus->f1regdata++;
+
+			for (i = 0; i < 3; i++) {
+				u8 hi, lo;
+				hi = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						     SBSDIO_FUNC1_WFRAMEBCHI,
+						     NULL);
+				lo = brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+						     SBSDIO_FUNC1_WFRAMEBCLO,
+						     NULL);
+				bus->f1regdata += 2;
+				if ((hi == 0) && (lo == 0))
+					break;
+			}
+
+		}
+		if (ret == 0)
+			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
+
+		BRCMF_INFO(("Return_dpc value is : %d\n", ret));
+		bus->ctrl_frame_stat = false;
+		brcmf_sdbrcm_wait_event_wakeup(bus);
+	}
+	/* Send queued frames (limit 1 if rx may still be pending) */
+	else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
+		 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
+		 && DATAOK(bus)) {
+		framecnt = rxdone ? txlimit : min(txlimit, brcmf_txminmax);
+		framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
+		txlimit -= framecnt;
+	}
+
+	/* Resched if events or tx frames are pending,
+		 else await next interrupt */
+	/* On failed register access, all bets are off:
+		 no resched or interrupts */
+	if ((bus->drvr->busstate == BRCMF_BUS_DOWN) ||
+	    brcmf_sdcard_regfail(card)) {
+		BRCMF_ERROR(("%s: failed backplane access over SDIO, halting "
+			     "operation %d\n", __func__,
+			     brcmf_sdcard_regfail(card)));
+		bus->drvr->busstate = BRCMF_BUS_DOWN;
+		bus->intstatus = 0;
+	} else if (bus->clkstate == CLK_PENDING) {
+		BRCMF_INFO(("%s: rescheduled due to CLK_PENDING awaiting "
+			    "I_CHIPACTIVE interrupt\n", __func__));
+		resched = true;
+	} else if (bus->intstatus || bus->ipend ||
+		(!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
+		 && DATAOK(bus)) || PKT_AVAILABLE()) {
+		resched = true;
+	}
+
+	bus->dpc_sched = resched;
+
+	/* If we're done for now, turn off clock request. */
+	if ((bus->clkstate != CLK_PENDING)
+	    && bus->idletime == BRCMF_IDLE_IMMEDIATE) {
+		bus->activity = false;
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+	}
+
+	brcmf_sdbrcm_sdunlock(bus);
+
+	return resched;
+}
+
+void brcmf_sdbrcm_isr(void *arg)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *) arg;
+	struct brcmf_sdio_card *card;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (!bus) {
+		BRCMF_ERROR(("%s : bus is null pointer , exit\n", __func__));
+		return;
+	}
+	card = bus->card;
+
+	if (bus->drvr->busstate == BRCMF_BUS_DOWN) {
+		BRCMF_ERROR(("%s : bus is down. we have nothing to do\n",
+			   __func__));
+		return;
+	}
+	/* Count the interrupt call */
+	bus->intrcount++;
+	bus->ipend = true;
+
+	/* Shouldn't get this interrupt if we're sleeping? */
+	if (bus->sleeping) {
+		BRCMF_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
+		return;
+	}
+
+	/* Disable additional interrupts (is this needed now)? */
+	if (bus->intr)
+		BRCMF_INTR(("%s: disable SDIO interrupts\n", __func__));
+	else
+		BRCMF_ERROR(("brcmf_sdbrcm_isr() w/o interrupt configured!\n"));
+
+	brcmf_sdcard_intr_disable(card);
+	bus->intdis = true;
+
+#if defined(SDIO_ISR_THREAD)
+	BRCMF_TRACE(("Calling brcmf_sdbrcm_dpc() from %s\n", __func__));
+	while (brcmf_sdbrcm_dpc(bus))
+		;
+#else
+	bus->dpc_sched = true;
+	brcmf_sdbrcm_sched_dpc(bus);
+#endif
+
+}
+
+#ifdef SDTEST
+static void brcmf_sdbrcm_pktgen_init(struct brcmf_bus *bus)
+{
+	/* Default to specified length, or full range */
+	if (brcmf_pktgen_len) {
+		bus->pktgen_maxlen = min(brcmf_pktgen_len,
+					 BRCMF_MAX_PKTGEN_LEN);
+		bus->pktgen_minlen = bus->pktgen_maxlen;
+	} else {
+		bus->pktgen_maxlen = BRCMF_MAX_PKTGEN_LEN;
+		bus->pktgen_minlen = 0;
+	}
+	bus->pktgen_len = (u16) bus->pktgen_minlen;
+
+	/* Default to per-watchdog burst with 10s print time */
+	bus->pktgen_freq = 1;
+	bus->pktgen_print = 10000 / brcmf_watchdog_ms;
+	bus->pktgen_count = (brcmf_pktgen * brcmf_watchdog_ms + 999) / 1000;
+
+	/* Default to echo mode */
+	bus->pktgen_mode = BRCMF_PKTGEN_ECHO;
+	bus->pktgen_stop = 1;
+}
+
+static void brcmf_sdbrcm_pktgen(struct brcmf_bus *bus)
+{
+	struct sk_buff *pkt;
+	u8 *data;
+	uint pktcount;
+	uint fillbyte;
+	u16 len;
+
+	/* Display current count if appropriate */
+	if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
+		bus->pktgen_ptick = 0;
+		printk(KERN_DEBUG "%s: send attempts %d rcvd %d\n",
+		       __func__, bus->pktgen_sent, bus->pktgen_rcvd);
+	}
+
+	/* For recv mode, just make sure dongle has started sending */
+	if (bus->pktgen_mode == BRCMF_PKTGEN_RECV) {
+		if (!bus->pktgen_rcvd)
+			brcmf_sdbrcm_sdtest_set(bus, true);
+		return;
+	}
+
+	/* Otherwise, generate or request the specified number of packets */
+	for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
+		/* Stop if total has been reached */
+		if (bus->pktgen_total
+		    && (bus->pktgen_sent >= bus->pktgen_total)) {
+			bus->pktgen_count = 0;
+			break;
+		}
+
+		/* Allocate an appropriate-sized packet */
+		len = bus->pktgen_len;
+		pkt = brcmu_pkt_buf_get_skb(
+			len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + BRCMF_SDALIGN,
+			true);
+		if (!pkt) {
+			BRCMF_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n",
+				     __func__));
+			break;
+		}
+		PKTALIGN(pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN),
+			 BRCMF_SDALIGN);
+		data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
+
+		/* Write test header cmd and extra based on mode */
+		switch (bus->pktgen_mode) {
+		case BRCMF_PKTGEN_ECHO:
+			*data++ = SDPCM_TEST_ECHOREQ;
+			*data++ = (u8) bus->pktgen_sent;
+			break;
+
+		case BRCMF_PKTGEN_SEND:
+			*data++ = SDPCM_TEST_DISCARD;
+			*data++ = (u8) bus->pktgen_sent;
+			break;
+
+		case BRCMF_PKTGEN_RXBURST:
+			*data++ = SDPCM_TEST_BURST;
+			*data++ = (u8) bus->pktgen_count;
+			break;
+
+		default:
+			BRCMF_ERROR(("Unrecognized pktgen mode %d\n",
+				     bus->pktgen_mode));
+			brcmu_pkt_buf_free_skb(pkt, true);
+			bus->pktgen_count = 0;
+			return;
+		}
+
+		/* Write test header length field */
+		*data++ = (len >> 0);
+		*data++ = (len >> 8);
+
+		/* Then fill in the remainder -- N/A for burst,
+			 but who cares... */
+		for (fillbyte = 0; fillbyte < len; fillbyte++)
+			*data++ =
+			    SDPCM_TEST_FILL(fillbyte, (u8) bus->pktgen_sent);
+
+#ifdef BCMDBG
+		if (BRCMF_BYTES_ON() && BRCMF_DATA_ON()) {
+			data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
+			printk(KERN_DEBUG "brcmf_sdbrcm_pktgen: Tx Data:\n");
+			print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data,
+					     pkt->len - SDPCM_HDRLEN);
+		}
+#endif
+
+		/* Send it */
+		if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true)) {
+			bus->pktgen_fail++;
+			if (bus->pktgen_stop
+			    && bus->pktgen_stop == bus->pktgen_fail)
+				bus->pktgen_count = 0;
+		}
+		bus->pktgen_sent++;
+
+		/* Bump length if not fixed, wrap at max */
+		if (++bus->pktgen_len > bus->pktgen_maxlen)
+			bus->pktgen_len = (u16) bus->pktgen_minlen;
+
+		/* Special case for burst mode: just send one request! */
+		if (bus->pktgen_mode == BRCMF_PKTGEN_RXBURST)
+			break;
+	}
+}
+
+static void brcmf_sdbrcm_sdtest_set(struct brcmf_bus *bus, bool start)
+{
+	struct sk_buff *pkt;
+	u8 *data;
+
+	/* Allocate the packet */
+	pkt = brcmu_pkt_buf_get_skb(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN +
+		BRCMF_SDALIGN, true);
+	if (!pkt) {
+		BRCMF_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n", __func__));
+		return;
+	}
+	PKTALIGN(pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), BRCMF_SDALIGN);
+	data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
+
+	/* Fill in the test header */
+	*data++ = SDPCM_TEST_SEND;
+	*data++ = start;
+	*data++ = (bus->pktgen_maxlen >> 0);
+	*data++ = (bus->pktgen_maxlen >> 8);
+
+	/* Send it */
+	if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true))
+		bus->pktgen_fail++;
+}
+
+static void
+brcmf_sdbrcm_checkdied(struct brcmf_bus *bus, struct sk_buff *pkt, uint seq)
+{
+	u8 *data;
+	uint pktlen;
+
+	u8 cmd;
+	u8 extra;
+	u16 len;
+	u16 offset;
+
+	/* Check for min length */
+	pktlen = pkt->len;
+	if (pktlen < SDPCM_TEST_HDRLEN) {
+		BRCMF_ERROR(("brcmf_sdbrcm_checkdied: toss runt frame, pktlen "
+			     "%d\n", pktlen));
+		brcmu_pkt_buf_free_skb(pkt, false);
+		return;
+	}
+
+	/* Extract header fields */
+	data = pkt->data;
+	cmd = *data++;
+	extra = *data++;
+	len = *data++;
+	len += *data++ << 8;
+
+	/* Check length for relevant commands */
+	if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ
+	    || cmd == SDPCM_TEST_ECHORSP) {
+		if (pktlen != len + SDPCM_TEST_HDRLEN) {
+			BRCMF_ERROR(("brcmf_sdbrcm_checkdied: frame length "
+				     "mismatch, pktlen %d seq %d"
+				     " cmd %d extra %d len %d\n",
+				     pktlen, seq, cmd, extra, len));
+			brcmu_pkt_buf_free_skb(pkt, false);
+			return;
+		}
+	}
+
+	/* Process as per command */
+	switch (cmd) {
+	case SDPCM_TEST_ECHOREQ:
+		/* Rx->Tx turnaround ok (even on NDIS w/current
+			 implementation) */
+		*(u8 *) (pkt->data) = SDPCM_TEST_ECHORSP;
+		if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true) == 0)
+			bus->pktgen_sent++;
+		else {
+			bus->pktgen_fail++;
+			brcmu_pkt_buf_free_skb(pkt, false);
+		}
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_ECHORSP:
+		if (bus->ext_loop) {
+			brcmu_pkt_buf_free_skb(pkt, false);
+			bus->pktgen_rcvd++;
+			break;
+		}
+
+		for (offset = 0; offset < len; offset++, data++) {
+			if (*data != SDPCM_TEST_FILL(offset, extra)) {
+				BRCMF_ERROR(("brcmf_sdbrcm_checkdied: echo"
+					     " data mismatch: "
+					     "offset %d (len %d) "
+					     "expect 0x%02x rcvd 0x%02x\n",
+					     offset, len,
+					     SDPCM_TEST_FILL(offset, extra),
+					     *data));
+				break;
+			}
+		}
+		brcmu_pkt_buf_free_skb(pkt, false);
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_DISCARD:
+		brcmu_pkt_buf_free_skb(pkt, false);
+		bus->pktgen_rcvd++;
+		break;
+
+	case SDPCM_TEST_BURST:
+	case SDPCM_TEST_SEND:
+	default:
+		BRCMF_INFO(("brcmf_sdbrcm_checkdied: unsupported or unknown "
+			    "command, pktlen %d seq %d" " cmd %d extra %d"
+			    " len %d\n", pktlen, seq, cmd, extra, len));
+		brcmu_pkt_buf_free_skb(pkt, false);
+		break;
+	}
+
+	/* For recv mode, stop at limie (and tell dongle to stop sending) */
+	if (bus->pktgen_mode == BRCMF_PKTGEN_RECV) {
+		if (bus->pktgen_total
+		    && (bus->pktgen_rcvd >= bus->pktgen_total)) {
+			bus->pktgen_count = 0;
+			brcmf_sdbrcm_sdtest_set(bus, false);
+		}
+	}
+}
+#endif				/* SDTEST */
+
+extern bool brcmf_sdbrcm_bus_watchdog(struct brcmf_pub *drvr)
+{
+	struct brcmf_bus *bus;
+
+	BRCMF_TIMER(("%s: Enter\n", __func__));
+
+	bus = drvr->bus;
+
+	if (bus->drvr->dongle_reset)
+		return false;
+
+	/* Ignore the timer if simulating bus down */
+	if (bus->sleeping)
+		return false;
+
+	brcmf_sdbrcm_sdlock(bus);
+
+	/* Poll period: check device if appropriate. */
+	if (bus->poll && (++bus->polltick >= bus->pollrate)) {
+		u32 intstatus = 0;
+
+		/* Reset poll tick */
+		bus->polltick = 0;
+
+		/* Check device if no interrupts */
+		if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
+
+			if (!bus->dpc_sched) {
+				u8 devpend;
+				devpend = brcmf_sdcard_cfg_read(bus->card,
+						SDIO_FUNC_0, SDIO_CCCR_INTx,
+						NULL);
+				intstatus =
+				    devpend & (INTR_STATUS_FUNC1 |
+					       INTR_STATUS_FUNC2);
+			}
+
+			/* If there is something, make like the ISR and
+				 schedule the DPC */
+			if (intstatus) {
+				bus->pollcnt++;
+				bus->ipend = true;
+				if (bus->intr)
+					brcmf_sdcard_intr_disable(bus->card);
+
+				bus->dpc_sched = true;
+				brcmf_sdbrcm_sched_dpc(bus);
+
+			}
+		}
+
+		/* Update interrupt tracking */
+		bus->lastintrs = bus->intrcount;
+	}
+#ifdef BCMDBG
+	/* Poll for console output periodically */
+	if (drvr->busstate == BRCMF_BUS_DATA && brcmf_console_ms != 0) {
+		bus->console.count += brcmf_watchdog_ms;
+		if (bus->console.count >= brcmf_console_ms) {
+			bus->console.count -= brcmf_console_ms;
+			/* Make sure backplane clock is on */
+			brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+			if (brcmf_sdbrcm_readconsole(bus) < 0)
+				brcmf_console_ms = 0;	/* On error,
+							 stop trying */
+		}
+	}
+#endif				/* BCMDBG */
+
+#ifdef SDTEST
+	/* Generate packets if configured */
+	if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
+		/* Make sure backplane clock is on */
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+		bus->pktgen_tick = 0;
+		brcmf_sdbrcm_pktgen(bus);
+	}
+#endif
+
+	/* On idle timeout clear activity flag and/or turn off clock */
+	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
+		if (++bus->idlecount >= bus->idletime) {
+			bus->idlecount = 0;
+			if (bus->activity) {
+				bus->activity = false;
+				brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+			} else {
+				brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+			}
+		}
+	}
+
+	brcmf_sdbrcm_sdunlock(bus);
+
+	return bus->ipend;
+}
+
+#ifdef BCMDBG
+static int brcmf_sdbrcm_bus_console_in(struct brcmf_pub *drvr,
+				       unsigned char *msg, uint msglen)
+{
+	struct brcmf_bus *bus = drvr->bus;
+	u32 addr, val;
+	int rv;
+	struct sk_buff *pkt;
+
+	/* Address could be zero if CONSOLE := 0 in dongle Makefile */
+	if (bus->console_addr == 0)
+		return -ENOTSUPP;
+
+	/* Exclusive bus access */
+	brcmf_sdbrcm_sdlock(bus);
+
+	/* Don't allow input if dongle is in reset */
+	if (bus->drvr->dongle_reset) {
+		brcmf_sdbrcm_sdunlock(bus);
+		return -EPERM;
+	}
+
+	/* Request clock to allow SDIO accesses */
+	BUS_WAKE(bus);
+	/* No pend allowed since txpkt is called later, ht clk has to be on */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+	/* Zero cbuf_index */
+	addr = bus->console_addr + offsetof(struct rte_console, cbuf_idx);
+	val = cpu_to_le32(0);
+	rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
+	if (rv < 0)
+		goto done;
+
+	/* Write message into cbuf */
+	addr = bus->console_addr + offsetof(struct rte_console, cbuf);
+	rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)msg, msglen);
+	if (rv < 0)
+		goto done;
+
+	/* Write length into vcons_in */
+	addr = bus->console_addr + offsetof(struct rte_console, vcons_in);
+	val = cpu_to_le32(msglen);
+	rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
+	if (rv < 0)
+		goto done;
+
+	/* Bump dongle by sending an empty event pkt.
+	 * sdpcm_sendup (RX) checks for virtual console input.
+	 */
+	pkt = brcmu_pkt_buf_get_skb(4 + SDPCM_RESERVE);
+	if ((pkt != NULL) && bus->clkstate == CLK_AVAIL)
+		brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, true);
+
+done:
+	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
+		bus->activity = false;
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
+	}
+
+	brcmf_sdbrcm_sdunlock(bus);
+
+	return rv;
+}
+#endif				/* BCMDBG */
+
+static bool brcmf_sdbrcm_chipmatch(u16 chipid)
+{
+	if (chipid == BCM4325_CHIP_ID)
+		return true;
+	if (chipid == BCM4329_CHIP_ID)
+		return true;
+	if (chipid == BCM4319_CHIP_ID)
+		return true;
+	return false;
+}
+
+static void *brcmf_sdbrcm_probe(u16 venid, u16 devid, u16 bus_no,
+			   u16 slot, u16 func, uint bustype, u32 regsva,
+			   void *card)
+{
+	int ret;
+	struct brcmf_bus *bus;
+
+	/* Init global variables at run-time, not as part of the declaration.
+	 * This is required to support init/de-init of the driver.
+	 * Initialization
+	 * of globals as part of the declaration results in non-deterministic
+	 * behavior since the value of the globals may be different on the
+	 * first time that the driver is initialized vs subsequent
+	 * initializations.
+	 */
+	brcmf_txbound = BRCMF_TXBOUND;
+	brcmf_rxbound = BRCMF_RXBOUND;
+	brcmf_alignctl = true;
+	sd1idle = true;
+	brcmf_readahead = true;
+	retrydata = false;
+	brcmf_dongle_memsize = 0;
+	brcmf_txminmax = BRCMF_TXMINMAX;
+
+	forcealign = true;
+
+	brcmf_c_init();
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+	BRCMF_INFO(("%s: venid 0x%04x devid 0x%04x\n", __func__, venid, devid));
+
+	/* We make an assumption about address window mappings:
+	 * regsva == SI_ENUM_BASE*/
+
+	/* SDIO car passes venid and devid based on CIS parsing -- but
+	 * low-power start
+	 * means early parse could fail, so here we should get either an ID
+	 * we recognize OR (-1) indicating we must request power first.
+	 */
+	/* Check the Vendor ID */
+	switch (venid) {
+	case 0x0000:
+	case PCI_VENDOR_ID_BROADCOM:
+		break;
+	default:
+		BRCMF_ERROR(("%s: unknown vendor: 0x%04x\n", __func__, venid));
+		return NULL;
+	}
+
+	/* Check the Device ID and make sure it's one that we support */
+	switch (devid) {
+	case BCM4325_D11DUAL_ID:	/* 4325 802.11a/g id */
+	case BCM4325_D11G_ID:	/* 4325 802.11g 2.4Ghz band id */
+	case BCM4325_D11A_ID:	/* 4325 802.11a 5Ghz band id */
+		BRCMF_INFO(("%s: found 4325 Dongle\n", __func__));
+		break;
+	case BCM4329_D11NDUAL_ID:	/* 4329 802.11n dualband device */
+	case BCM4329_D11N2G_ID:	/* 4329 802.11n 2.4G device */
+	case BCM4329_D11N5G_ID:	/* 4329 802.11n 5G device */
+	case 0x4329:
+		BRCMF_INFO(("%s: found 4329 Dongle\n", __func__));
+		break;
+	case BCM4319_D11N_ID:	/* 4319 802.11n id */
+	case BCM4319_D11N2G_ID:	/* 4319 802.11n2g id */
+	case BCM4319_D11N5G_ID:	/* 4319 802.11n5g id */
+		BRCMF_INFO(("%s: found 4319 Dongle\n", __func__));
+		break;
+	case 0:
+		BRCMF_INFO(("%s: allow device id 0, will check chip"
+			    " internals\n", __func__));
+		break;
+
+	default:
+		BRCMF_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
+			     __func__, venid, devid));
+		return NULL;
+	}
+
+	/* Allocate private bus interface state */
+	bus = kzalloc(sizeof(struct brcmf_bus), GFP_ATOMIC);
+	if (!bus) {
+		BRCMF_ERROR(("%s: kmalloc of struct dhd_bus failed\n",
+			     __func__));
+		goto fail;
+	}
+	bus->card = card;
+	bus->cl_devid = (u16) devid;
+	bus->bus = BRCMF_BUS;
+	bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
+	bus->usebufpool = false;	/* Use bufpool if allocated,
+					 else use locally malloced rxbuf */
+
+	/* attempt to attach to the dongle */
+	if (!(brcmf_sdbrcm_probe_attach(bus, card, regsva, devid))) {
+		BRCMF_ERROR(("%s: brcmf_sdbrcm_probe_attach failed\n",
+			     __func__));
+		goto fail;
+	}
+
+	spin_lock_init(&bus->txqlock);
+	init_waitqueue_head(&bus->ctrl_wait);
+
+	/* Set up the watchdog timer */
+	init_timer(&bus->timer);
+	bus->timer.data = (unsigned long)bus;
+	bus->timer.function = brcmf_sdbrcm_watchdog;
+
+	/* Initialize thread based operation and lock */
+	if ((brcmf_watchdog_prio >= 0) && (brcmf_dpc_prio >= 0)) {
+		bus->threads_only = true;
+		sema_init(&bus->sdsem, 1);
+	} else {
+		bus->threads_only = false;
+		spin_lock_init(&bus->sdlock);
+	}
+
+	if (brcmf_dpc_prio >= 0) {
+		/* Initialize watchdog thread */
+		init_completion(&bus->watchdog_wait);
+		bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread,
+						bus, "brcmf_watchdog");
+		if (IS_ERR(bus->watchdog_tsk)) {
+			printk(KERN_WARNING
+			       "brcmf_watchdog thread failed to start\n");
+			bus->watchdog_tsk = NULL;
+		}
+	} else
+		bus->watchdog_tsk = NULL;
+
+	/* Set up the bottom half handler */
+	if (brcmf_dpc_prio >= 0) {
+		/* Initialize DPC thread */
+		init_completion(&bus->dpc_wait);
+		bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread,
+					   bus, "brcmf_dpc");
+		if (IS_ERR(bus->dpc_tsk)) {
+			printk(KERN_WARNING
+			       "brcmf_dpc thread failed to start\n");
+			bus->dpc_tsk = NULL;
+		}
+	} else {
+		tasklet_init(&bus->tasklet, brcmf_sdbrcm_dpc_tasklet,
+			     (unsigned long)bus);
+		bus->dpc_tsk = NULL;
+	}
+
+	/* Attach to the brcmf/OS/network interface */
+	bus->drvr = brcmf_attach(bus, SDPCM_RESERVE);
+	if (!bus->drvr) {
+		BRCMF_ERROR(("%s: brcmf_attach failed\n", __func__));
+		goto fail;
+	}
+
+	/* Allocate buffers */
+	if (!(brcmf_sdbrcm_probe_malloc(bus, card))) {
+		BRCMF_ERROR(("%s: brcmf_sdbrcm_probe_malloc failed\n",
+			     __func__));
+		goto fail;
+	}
+
+	if (!(brcmf_sdbrcm_probe_init(bus, card))) {
+		BRCMF_ERROR(("%s: brcmf_sdbrcm_probe_init failed\n", __func__));
+		goto fail;
+	}
+
+	/* Register interrupt callback, but mask it (not operational yet). */
+	BRCMF_INTR(("%s: disable SDIO interrupts (not interested yet)\n",
+		    __func__));
+	brcmf_sdcard_intr_disable(card);
+	ret = brcmf_sdcard_intr_reg(card, brcmf_sdbrcm_isr, bus);
+	if (ret != 0) {
+		BRCMF_ERROR(("%s: FAILED: sdcard_intr_reg returned %d\n",
+			     __func__, ret));
+		goto fail;
+	}
+	BRCMF_INTR(("%s: registered SDIO interrupt function ok\n", __func__));
+
+	BRCMF_INFO(("%s: completed!!\n", __func__));
+
+	/* if firmware path present try to download and bring up bus */
+	ret = brcmf_bus_start(bus->drvr);
+	if (ret != 0) {
+		if (ret == -ENOLINK) {
+			BRCMF_ERROR(("%s: dongle is not responding\n",
+				     __func__));
+			goto fail;
+		}
+	}
+	/* Ok, have the per-port tell the stack we're open for business */
+	if (brcmf_net_attach(bus->drvr, 0) != 0) {
+		BRCMF_ERROR(("%s: Net attach failed!!\n", __func__));
+		goto fail;
+	}
+
+	return bus;
+
+fail:
+	brcmf_sdbrcm_release(bus);
+	return NULL;
+}
+
+static bool
+brcmf_sdbrcm_probe_attach(struct brcmf_bus *bus, void *card, u32 regsva,
+			  u16 devid)
+{
+	u8 clkctl = 0;
+	int err = 0;
+
+	bus->alp_only = true;
+
+	/* Return the window to backplane enumeration space for core access */
+	if (brcmf_sdbrcm_set_siaddr_window(bus, SI_ENUM_BASE))
+		BRCMF_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n",
+			     __func__));
+
+#ifdef BCMDBG
+	printk(KERN_DEBUG "F1 signature read @0x18000000=0x%4x\n",
+	       brcmf_sdcard_reg_read(bus->card, SI_ENUM_BASE, 4));
+
+#endif				/* BCMDBG */
+
+	/*
+	 * Force PLL off until brcmf_sdbrcm_chip_attach()
+	 * programs PLL control regs
+	 */
+
+	brcmf_sdcard_cfg_write(card, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+			 BRCMF_INIT_CLKCTL1, &err);
+	if (!err)
+		clkctl =
+		    brcmf_sdcard_cfg_read(card, SDIO_FUNC_1,
+					  SBSDIO_FUNC1_CHIPCLKCSR, &err);
+
+	if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
+		BRCMF_ERROR(("brcmf_sdbrcm_probe: ChipClkCSR access: err %d"
+			     " wrote 0x%02x read 0x%02x\n",
+			     err, BRCMF_INIT_CLKCTL1, clkctl));
+		goto fail;
+	}
+
+	if (brcmf_sdbrcm_chip_attach(bus, regsva)) {
+		BRCMF_ERROR(("%s: brcmf_sdbrcm_chip_attach failed!\n",
+			     __func__));
+		goto fail;
+	}
+
+	if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
+		BRCMF_ERROR(("%s: unsupported chip: 0x%04x\n",
+			     __func__, bus->ci->chip));
+		goto fail;
+	}
+
+	brcmf_sdbrcm_sdiod_drive_strength_init(bus, brcmf_sdiod_drive_strength);
+
+	/* Get info on the ARM and SOCRAM cores... */
+	if (!BRCMF_NOPMU(bus)) {
+		brcmf_sdcard_reg_read(bus->card,
+			  CORE_SB(bus->ci->armcorebase, sbidhigh), 4);
+		bus->orig_ramsize = bus->ci->ramsize;
+		if (!(bus->orig_ramsize)) {
+			BRCMF_ERROR(("%s: failed to find SOCRAM memory!\n",
+				     __func__));
+			goto fail;
+		}
+		bus->ramsize = bus->orig_ramsize;
+		if (brcmf_dongle_memsize)
+			brcmf_sdbrcm_setmemsize(bus, brcmf_dongle_memsize);
+
+		BRCMF_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
+			     bus->ramsize, bus->orig_ramsize));
+	}
+
+	/* Set core control so an SDIO reset does a backplane reset */
+	OR_REG(bus->ci->buscorebase + offsetof(struct sdpcmd_regs,
+						       corecontrol),
+	       CC_BPRESEN, u32);
+
+	brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
+
+	/* Locate an appropriately-aligned portion of hdrbuf */
+	bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
+				    BRCMF_SDALIGN);
+
+	/* Set the poll and/or interrupt flags */
+	bus->intr = (bool) brcmf_intr;
+	bus->poll = (bool) brcmf_poll;
+	if (bus->poll)
+		bus->pollrate = 1;
+
+	return true;
+
+fail:
+	return false;
+}
+
+static bool brcmf_sdbrcm_probe_malloc(struct brcmf_bus *bus, void *card)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus->drvr->maxctl) {
+		bus->rxblen =
+		    roundup((bus->drvr->maxctl + SDPCM_HDRLEN),
+			    ALIGNMENT) + BRCMF_SDALIGN;
+		bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
+		if (!(bus->rxbuf)) {
+			BRCMF_ERROR(("%s: kmalloc of %d-byte rxbuf failed\n",
+				     __func__, bus->rxblen));
+			goto fail;
+		}
+	}
+
+	/* Allocate buffer to receive glomed packet */
+	bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
+	if (!(bus->databuf)) {
+		BRCMF_ERROR(("%s: kmalloc of %d-byte databuf failed\n",
+			     __func__, MAX_DATA_BUF));
+		/* release rxbuf which was already located as above */
+		if (!bus->rxblen)
+			kfree(bus->rxbuf);
+		goto fail;
+	}
+
+	/* Align the buffer */
+	if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
+		bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
+			       ((unsigned long)bus->databuf % BRCMF_SDALIGN));
+	else
+		bus->dataptr = bus->databuf;
+
+	return true;
+
+fail:
+	return false;
+}
+
+static bool brcmf_sdbrcm_probe_init(struct brcmf_bus *bus, void *card)
+{
+	s32 fnum;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+#ifdef SDTEST
+	brcmf_sdbrcm_pktgen_init(bus);
+#endif				/* SDTEST */
+
+	/* Disable F2 to clear any intermediate frame state on the dongle */
+	brcmf_sdcard_cfg_write(card, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+			       SDIO_FUNC_ENABLE_1, NULL);
+
+	bus->drvr->busstate = BRCMF_BUS_DOWN;
+	bus->sleeping = false;
+	bus->rxflow = false;
+
+	/* Done with backplane-dependent accesses, can drop clock... */
+	brcmf_sdcard_cfg_write(card, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0,
+			       NULL);
+
+	/* ...and initialize clock/power states */
+	bus->clkstate = CLK_SDONLY;
+	bus->idletime = (s32) brcmf_idletime;
+	bus->idleclock = BRCMF_IDLE_ACTIVE;
+
+	/* Query the F2 block size, set roundup accordingly */
+	fnum = 2;
+	if (brcmf_sdcard_iovar_op(card, "sd_blocksize", &fnum, sizeof(s32),
+			    &bus->blocksize, sizeof(s32), false) != 0) {
+		bus->blocksize = 0;
+		BRCMF_ERROR(("%s: fail on %s get\n", __func__, "sd_blocksize"));
+	} else {
+		BRCMF_INFO(("%s: Initial value for %s is %d\n",
+			    __func__, "sd_blocksize", bus->blocksize));
+	}
+	bus->roundup = min(max_roundup, bus->blocksize);
+
+	/* Query if bus module supports packet chaining,
+		 default to use if supported */
+	if (brcmf_sdcard_iovar_op(card, "sd_rxchain", NULL, 0,
+			    &bus->sd_rxchain, sizeof(s32),
+			    false) != 0) {
+		bus->sd_rxchain = false;
+	} else {
+		BRCMF_INFO(("%s: bus module (through sdiocard API) %s"
+			    " chaining\n", __func__, bus->sd_rxchain
+			    ? "supports" : "does not support"));
+	}
+	bus->use_rxchain = (bool) bus->sd_rxchain;
+
+	return true;
+}
+
+static bool
+brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus, void *card)
+{
+	bool ret;
+
+	/* Download the firmware */
+	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+
+	ret = _brcmf_sdbrcm_download_firmware(bus) == 0;
+
+	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
+
+	return ret;
+}
+
+/* Detach and free everything */
+static void brcmf_sdbrcm_release(struct brcmf_bus *bus)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus) {
+		/* De-register interrupt handler */
+		brcmf_sdcard_intr_disable(bus->card);
+		brcmf_sdcard_intr_dereg(bus->card);
+
+		if (bus->drvr) {
+			brcmf_detach(bus->drvr);
+			brcmf_sdbrcm_release_dongle(bus);
+			bus->drvr = NULL;
+		}
+
+		brcmf_sdbrcm_release_malloc(bus);
+
+		kfree(bus);
+	}
+
+	BRCMF_TRACE(("%s: Disconnected\n", __func__));
+}
+
+static void brcmf_sdbrcm_release_malloc(struct brcmf_bus *bus)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus->drvr && bus->drvr->dongle_reset)
+		return;
+
+	kfree(bus->rxbuf);
+	bus->rxctl = bus->rxbuf = NULL;
+	bus->rxlen = 0;
+
+	kfree(bus->databuf);
+	bus->databuf = NULL;
+}
+
+static void brcmf_sdbrcm_release_dongle(struct brcmf_bus *bus)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus->drvr && bus->drvr->dongle_reset)
+		return;
+
+	if (bus->ci) {
+		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
+		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
+		brcmf_sdbrcm_chip_detach(bus);
+		if (bus->vars && bus->varsz)
+			kfree(bus->vars);
+		bus->vars = NULL;
+	}
+
+	BRCMF_TRACE(("%s: Disconnected\n", __func__));
+}
+
+static void brcmf_sdbrcm_disconnect(void *ptr)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *)ptr;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	if (bus) {
+		brcmf_sdbrcm_release(bus);
+	}
+
+	BRCMF_TRACE(("%s: Disconnected\n", __func__));
+}
+
+/* Register/Unregister functions are called by the main DHD entry
+ * point (e.g. module insertion) to link with the bus driver, in
+ * order to look for or await the device.
+ */
+
+static struct brcmf_sdioh_driver brcmf_sdio = {
+	brcmf_sdbrcm_probe,
+	brcmf_sdbrcm_disconnect
+};
+
+int brcmf_bus_register(void)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* Sanity check on the module parameters */
+	do {
+		/* Both watchdog and DPC as tasklets are ok */
+		if ((brcmf_watchdog_prio < 0) && (brcmf_dpc_prio < 0))
+			break;
+
+		/* If both watchdog and DPC are threads, TX must be deferred */
+		if ((brcmf_watchdog_prio >= 0) && (brcmf_dpc_prio >= 0)
+		    && brcmf_deferred_tx)
+			break;
+
+		BRCMF_ERROR(("Invalid module parameters.\n"));
+		return -EINVAL;
+	} while (0);
+
+	return brcmf_sdio_register(&brcmf_sdio);
+}
+
+void brcmf_bus_unregister(void)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	brcmf_sdio_unregister();
+}
+
+static int brcmf_sdbrcm_download_code_file(struct brcmf_bus *bus)
+{
+	int offset = 0;
+	uint len;
+	u8 *memblock = NULL, *memptr;
+	int ret;
+
+	BRCMF_INFO(("%s: Enter\n", __func__));
+
+	bus->fw_name = BCM4329_FW_NAME;
+	ret = request_firmware(&bus->firmware, bus->fw_name,
+			       &gInstance->func[2]->dev);
+	if (ret) {
+		BRCMF_ERROR(("%s: Fail to request firmware %d\n",
+			     __func__, ret));
+		return ret;
+	}
+	bus->fw_ptr = 0;
+
+	memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC);
+	if (memblock == NULL) {
+		BRCMF_ERROR(("%s: Failed to allocate memory %d bytes\n",
+			     __func__, MEMBLOCK));
+		ret = -ENOMEM;
+		goto err;
+	}
+	if ((u32)(unsigned long)memblock % BRCMF_SDALIGN)
+		memptr += (BRCMF_SDALIGN -
+			   ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
+
+	/* Download image */
+	while ((len =
+		brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) {
+		ret = brcmf_sdbrcm_membytes(bus, true, offset, memptr, len);
+		if (ret) {
+			BRCMF_ERROR(("%s: error %d on writing %d membytes at "
+				     "0x%08x\n", __func__, ret, MEMBLOCK,
+				     offset));
+			goto err;
+		}
+
+		offset += MEMBLOCK;
+	}
+
+err:
+	kfree(memblock);
+
+	release_firmware(bus->firmware);
+	bus->fw_ptr = 0;
+
+	return ret;
+}
+
+/*
+ * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
+ * and ending in a NUL.
+ * Removes carriage returns, empty lines, comment lines, and converts
+ * newlines to NULs.
+ * Shortens buffer as needed and pads with NULs.  End of buffer is marked
+ * by two NULs.
+*/
+
+static uint brcmf_process_nvram_vars(char *varbuf, uint len)
+{
+	char *dp;
+	bool findNewline;
+	int column;
+	uint buf_len, n;
+
+	dp = varbuf;
+
+	findNewline = false;
+	column = 0;
+
+	for (n = 0; n < len; n++) {
+		if (varbuf[n] == 0)
+			break;
+		if (varbuf[n] == '\r')
+			continue;
+		if (findNewline && varbuf[n] != '\n')
+			continue;
+		findNewline = false;
+		if (varbuf[n] == '#') {
+			findNewline = true;
+			continue;
+		}
+		if (varbuf[n] == '\n') {
+			if (column == 0)
+				continue;
+			*dp++ = 0;
+			column = 0;
+			continue;
+		}
+		*dp++ = varbuf[n];
+		column++;
+	}
+	buf_len = dp - varbuf;
+
+	while (dp < varbuf + n)
+		*dp++ = 0;
+
+	return buf_len;
+}
+
+static int brcmf_sdbrcm_download_nvram(struct brcmf_bus *bus)
+{
+	uint len;
+	char *memblock = NULL;
+	char *bufp;
+	int ret;
+
+	bus->nv_name = BCM4329_NV_NAME;
+	ret = request_firmware(&bus->firmware, bus->nv_name,
+			       &gInstance->func[2]->dev);
+	if (ret) {
+		BRCMF_ERROR(("%s: Fail to request nvram %d\n", __func__, ret));
+		return ret;
+	}
+	bus->fw_ptr = 0;
+
+	memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
+	if (memblock == NULL) {
+		BRCMF_ERROR(("%s: Failed to allocate memory %d bytes\n",
+			     __func__, MEMBLOCK));
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	len = brcmf_sdbrcm_get_image(memblock, MEMBLOCK, bus);
+
+	if (len > 0 && len < MEMBLOCK) {
+		bufp = (char *)memblock;
+		bufp[len] = 0;
+		len = brcmf_process_nvram_vars(bufp, len);
+		bufp += len;
+		*bufp++ = 0;
+		if (len)
+			ret = brcmf_sdbrcm_downloadvars(bus, memblock, len + 1);
+		if (ret)
+			BRCMF_ERROR(("%s: error downloading vars: %d\n",
+				     __func__, ret));
+	} else {
+		BRCMF_ERROR(("%s: error reading nvram file: %d\n",
+			     __func__, len));
+		ret = -EIO;
+	}
+
+err:
+	kfree(memblock);
+
+	release_firmware(bus->firmware);
+	bus->fw_ptr = 0;
+
+	return ret;
+}
+
+static int _brcmf_sdbrcm_download_firmware(struct brcmf_bus *bus)
+{
+	int bcmerror = -1;
+
+	/* Keep arm in reset */
+	if (brcmf_sdbrcm_download_state(bus, true)) {
+		BRCMF_ERROR(("%s: error placing ARM core in reset\n",
+			     __func__));
+		goto err;
+	}
+
+	/* External image takes precedence if specified */
+	if (brcmf_sdbrcm_download_code_file(bus)) {
+		BRCMF_ERROR(("%s: dongle image file download failed\n",
+			     __func__));
+		goto err;
+	}
+
+	/* External nvram takes precedence if specified */
+	if (brcmf_sdbrcm_download_nvram(bus)) {
+		BRCMF_ERROR(("%s: dongle nvram file download failed\n",
+			     __func__));
+	}
+
+	/* Take arm out of reset */
+	if (brcmf_sdbrcm_download_state(bus, false)) {
+		BRCMF_ERROR(("%s: error getting out of ARM core reset\n",
+			     __func__));
+		goto err;
+	}
+
+	bcmerror = 0;
+
+err:
+	return bcmerror;
+}
+
+
+static int
+brcmf_sdbrcm_send_buf(struct brcmf_bus *bus, u32 addr, uint fn, uint flags,
+		    u8 *buf, uint nbytes, struct sk_buff *pkt,
+		    void (*complete)(void *handle, int status,
+				     bool sync_waiting),
+		    void *handle)
+{
+	return brcmf_sdcard_send_buf
+		(bus->card, addr, fn, flags, buf, nbytes, pkt, complete,
+		 handle);
+}
+
+int brcmf_bus_devreset(struct brcmf_pub *drvr, u8 flag)
+{
+	int bcmerror = 0;
+	struct brcmf_bus *bus;
+
+	bus = drvr->bus;
+
+	if (flag == true) {
+		brcmf_sdbrcm_wd_timer(bus, 0);
+		if (!bus->drvr->dongle_reset) {
+			/* Expect app to have torn down any
+			 connection before calling */
+			/* Stop the bus, disable F2 */
+			brcmf_sdbrcm_bus_stop(bus, false);
+
+			/* Clean tx/rx buffer pointers,
+			 detach from the dongle */
+			brcmf_sdbrcm_release_dongle(bus);
+
+			bus->drvr->dongle_reset = true;
+			bus->drvr->up = false;
+
+			BRCMF_TRACE(("%s:  WLAN OFF DONE\n", __func__));
+			/* App can now remove power from device */
+		} else
+			bcmerror = -EIO;
+	} else {
+		/* App must have restored power to device before calling */
+
+		BRCMF_TRACE(("\n\n%s: == WLAN ON ==\n", __func__));
+
+		if (bus->drvr->dongle_reset) {
+			/* Turn on WLAN */
+
+			/* Attempt to re-attach & download */
+			if (brcmf_sdbrcm_probe_attach(bus, bus->card,
+						      SI_ENUM_BASE,
+						      bus->cl_devid)) {
+				/* Attempt to download binary to the dongle */
+				if (brcmf_sdbrcm_probe_init(bus, bus->card)) {
+					/* Re-init bus, enable F2 transfer */
+					brcmf_sdbrcm_bus_init(bus->drvr, false);
+
+					bus->drvr->dongle_reset = false;
+					bus->drvr->up = true;
+
+					BRCMF_TRACE(("%s: WLAN ON DONE\n",
+						     __func__));
+				} else
+					bcmerror = -EIO;
+			} else
+				bcmerror = -EIO;
+		} else {
+			bcmerror = -EISCONN;
+			BRCMF_ERROR(("%s: Set DEVRESET=false invoked when"
+				     " device is on\n", __func__));
+			bcmerror = -EIO;
+		}
+		brcmf_sdbrcm_wd_timer(bus, brcmf_watchdog_ms);
+	}
+	return bcmerror;
+}
+
+static int
+brcmf_sdbrcm_chip_recognition(struct brcmf_sdio_card *card,
+			      struct chip_info *ci, u32 regs)
+{
+	u32 regdata;
+
+	/*
+	 * Get CC core rev
+	 * Chipid is assume to be at offset 0 from regs arg
+	 * For different chiptypes or old sdio hosts w/o chipcommon,
+	 * other ways of recognition should be added here.
+	 */
+	ci->cccorebase = regs;
+	regdata = brcmf_sdcard_reg_read(card,
+				CORE_CC_REG(ci->cccorebase, chipid), 4);
+	ci->chip = regdata & CID_ID_MASK;
+	ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
+
+	BRCMF_INFO(("%s: chipid=0x%x chiprev=%d\n",
+		    __func__, ci->chip, ci->chiprev));
+
+	/* Address of cores for new chips should be added here */
+	switch (ci->chip) {
+	case BCM4329_CHIP_ID:
+		ci->buscorebase = BCM4329_CORE_BUS_BASE;
+		ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE;
+		ci->armcorebase	= BCM4329_CORE_ARM_BASE;
+		ci->ramsize = BCM4329_RAMSIZE;
+		break;
+	default:
+		BRCMF_ERROR(("%s: chipid 0x%x is not supported\n",
+			     __func__, ci->chip));
+		return -ENODEV;
+	}
+
+	regdata = brcmf_sdcard_reg_read(card,
+		CORE_SB(ci->cccorebase, sbidhigh), 4);
+	ci->ccrev = SBCOREREV(regdata);
+
+	regdata = brcmf_sdcard_reg_read(card,
+		CORE_CC_REG(ci->cccorebase, pmucapabilities), 4);
+	ci->pmurev = regdata & PCAP_REV_MASK;
+
+	regdata = brcmf_sdcard_reg_read(card,
+					CORE_SB(ci->buscorebase, sbidhigh), 4);
+	ci->buscorerev = SBCOREREV(regdata);
+	ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
+
+	BRCMF_INFO(("%s: ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
+		    __func__, ci->ccrev, ci->pmurev,
+		    ci->buscorerev, ci->buscoretype));
+
+	/* get chipcommon capabilites */
+	ci->cccaps = brcmf_sdcard_reg_read(card,
+		CORE_CC_REG(ci->cccorebase, capabilities), 4);
+
+	return 0;
+}
+
+static void
+brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio_card *card, u32 corebase)
+{
+	u32 regdata;
+
+	regdata = brcmf_sdcard_reg_read(card,
+		CORE_SB(corebase, sbtmstatelow), 4);
+	if (regdata & SBTML_RESET)
+		return;
+
+	regdata = brcmf_sdcard_reg_read(card,
+		CORE_SB(corebase, sbtmstatelow), 4);
+	if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) {
+		/*
+		 * set target reject and spin until busy is clear
+		 * (preserve core-specific bits)
+		 */
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbtmstatelow), 4);
+		brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatelow), 4,
+			regdata | SBTML_REJ);
+
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbtmstatelow), 4);
+		udelay(1);
+		SPINWAIT((brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbtmstatehigh), 4) &
+			SBTMH_BUSY), 100000);
+
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbtmstatehigh), 4);
+		if (regdata & SBTMH_BUSY)
+			BRCMF_ERROR(("%s: ARM core still busy\n", __func__));
+
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbidlow), 4);
+		if (regdata & SBIDL_INIT) {
+			regdata = brcmf_sdcard_reg_read(card,
+				CORE_SB(corebase, sbimstate), 4) |
+				SBIM_RJ;
+			brcmf_sdcard_reg_write(card,
+				CORE_SB(corebase, sbimstate), 4,
+				regdata);
+			regdata = brcmf_sdcard_reg_read(card,
+				CORE_SB(corebase, sbimstate), 4);
+			udelay(1);
+			SPINWAIT((brcmf_sdcard_reg_read(card,
+				CORE_SB(corebase, sbimstate), 4) &
+				SBIM_BY), 100000);
+		}
+
+		/* set reset and reject while enabling the clocks */
+		brcmf_sdcard_reg_write(card,
+			CORE_SB(corebase, sbtmstatelow), 4,
+			(((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+			SBTML_REJ | SBTML_RESET));
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbtmstatelow), 4);
+		udelay(10);
+
+		/* clear the initiator reject bit */
+		regdata = brcmf_sdcard_reg_read(card,
+			CORE_SB(corebase, sbidlow), 4);
+		if (regdata & SBIDL_INIT) {
+			regdata = brcmf_sdcard_reg_read(card,
+				CORE_SB(corebase, sbimstate), 4) &
+				~SBIM_RJ;
+			brcmf_sdcard_reg_write(card,
+				CORE_SB(corebase, sbimstate), 4,
+				regdata);
+		}
+	}
+
+	/* leave reset and reject asserted */
+	brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatelow), 4,
+		(SBTML_REJ | SBTML_RESET));
+	udelay(1);
+}
+
+static int
+brcmf_sdbrcm_chip_attach(struct brcmf_bus *bus, u32 regs)
+{
+	struct chip_info *ci;
+	int err;
+	u8 clkval, clkset;
+
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	/* alloc chip_info_t */
+	ci = kmalloc(sizeof(struct chip_info), GFP_ATOMIC);
+	if (NULL == ci) {
+		BRCMF_ERROR(("%s: malloc failed!\n", __func__));
+		return -ENOMEM;
+	}
+
+	memset((unsigned char *)ci, 0, sizeof(struct chip_info));
+
+	/* bus/core/clk setup for register access */
+	/* Try forcing SDIO core to do ALPAvail request only */
+	clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+			clkset, &err);
+	if (err) {
+		BRCMF_ERROR(("%s: error writing for HT off\n", __func__));
+		goto fail;
+	}
+
+	/* If register supported, wait for ALPAvail and then force ALP */
+	/* This may take up to 15 milliseconds */
+	clkval = brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_1,
+			SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+	if ((clkval & ~SBSDIO_AVBITS) == clkset) {
+		SPINWAIT(((clkval =
+				brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_1,
+						SBSDIO_FUNC1_CHIPCLKCSR,
+						NULL)),
+				!SBSDIO_ALPAV(clkval)),
+				PMU_MAX_TRANSITION_DLY);
+		if (!SBSDIO_ALPAV(clkval)) {
+			BRCMF_ERROR(("%s: timeout on ALPAV wait,"
+				     " clkval 0x%02x\n", __func__, clkval));
+			err = -EBUSY;
+			goto fail;
+		}
+		clkset = SBSDIO_FORCE_HW_CLKREQ_OFF |
+				SBSDIO_FORCE_ALP;
+		brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1,
+				SBSDIO_FUNC1_CHIPCLKCSR,
+				clkset, &err);
+		udelay(65);
+	} else {
+		BRCMF_ERROR(("%s: ChipClkCSR access: wrote 0x%02x"
+			     " read 0x%02x\n", __func__, clkset, clkval));
+		err = -EACCES;
+		goto fail;
+	}
+
+	/* Also, disable the extra SDIO pull-ups */
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP,
+			       0, NULL);
+
+	err = brcmf_sdbrcm_chip_recognition(bus->card, ci, regs);
+	if (err)
+		goto fail;
+
+	/*
+	 * Make sure any on-chip ARM is off (in case strapping is wrong),
+	 * or downloaded code was already running.
+	 */
+	brcmf_sdbrcm_chip_disablecore(bus->card, ci->armcorebase);
+
+	brcmf_sdcard_reg_write(bus->card,
+		CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0);
+	brcmf_sdcard_reg_write(bus->card,
+		CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0);
+
+	/* Disable F2 to clear any intermediate frame state on the dongle */
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_0, SDIO_CCCR_IOEx,
+		SDIO_FUNC_ENABLE_1, NULL);
+
+	/* WAR: cmd52 backplane read so core HW will drop ALPReq */
+	clkval = brcmf_sdcard_cfg_read(bus->card, SDIO_FUNC_1,
+			0, NULL);
+
+	/* Done with backplane-dependent accesses, can drop clock... */
+	brcmf_sdcard_cfg_write(bus->card, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+			       0, NULL);
+
+	bus->ci = ci;
+	return 0;
+fail:
+	bus->ci = NULL;
+	kfree(ci);
+	return err;
+}
+
+static void
+brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio_card *card, u32 corebase)
+{
+	u32 regdata;
+
+	/*
+	 * Must do the disable sequence first to work for
+	 * arbitrary current core state.
+	 */
+	brcmf_sdbrcm_chip_disablecore(card, corebase);
+
+	/*
+	 * Now do the initialization sequence.
+	 * set reset while enabling the clock and
+	 * forcing them on throughout the core
+	 */
+	brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatelow), 4,
+		((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
+		SBTML_RESET);
+	udelay(1);
+
+	regdata = brcmf_sdcard_reg_read(card, CORE_SB(corebase, sbtmstatehigh),
+					4);
+	if (regdata & SBTMH_SERR)
+		brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatehigh),
+				       4, 0);
+
+	regdata = brcmf_sdcard_reg_read(card, CORE_SB(corebase, sbimstate), 4);
+	if (regdata & (SBIM_IBE | SBIM_TO))
+		brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbimstate), 4,
+			regdata & ~(SBIM_IBE | SBIM_TO));
+
+	/* clear reset and allow it to propagate throughout the core */
+	brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatelow), 4,
+		(SICF_FGC << SBTML_SICF_SHIFT) |
+		(SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+	udelay(1);
+
+	/* leave clock enabled */
+	brcmf_sdcard_reg_write(card, CORE_SB(corebase, sbtmstatelow), 4,
+		(SICF_CLOCK_EN << SBTML_SICF_SHIFT));
+	udelay(1);
+}
+
+/* SDIO Pad drive strength to select value mappings */
+struct sdiod_drive_str {
+	u8 strength;	/* Pad Drive Strength in mA */
+	u8 sel;		/* Chip-specific select value */
+};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 1 */
+static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = {
+	{
+	4, 0x2}, {
+	2, 0x3}, {
+	1, 0x0}, {
+	0, 0x0}
+	};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
+static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = {
+	{
+	12, 0x7}, {
+	10, 0x6}, {
+	8, 0x5}, {
+	6, 0x4}, {
+	4, 0x2}, {
+	2, 0x1}, {
+	0, 0x0}
+	};
+
+/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */
+static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = {
+	{
+	32, 0x7}, {
+	26, 0x6}, {
+	22, 0x5}, {
+	16, 0x4}, {
+	12, 0x3}, {
+	8, 0x2}, {
+	4, 0x1}, {
+	0, 0x0}
+	};
+
+#define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu))
+
+static void
+brcmf_sdbrcm_sdiod_drive_strength_init(struct brcmf_bus *bus, u32 drivestrength) {
+	struct sdiod_drive_str *str_tab = NULL;
+	u32 str_mask = 0;
+	u32 str_shift = 0;
+	char chn[8];
+
+	if (!(bus->ci->cccaps & CC_CAP_PMU))
+		return;
+
+	switch (SDIOD_DRVSTR_KEY(bus->ci->chip, bus->ci->pmurev)) {
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
+		str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1;
+		str_mask = 0x30000000;
+		str_shift = 28;
+		break;
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
+	case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
+		str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2;
+		str_mask = 0x00003800;
+		str_shift = 11;
+		break;
+	case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8):
+		str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3;
+		str_mask = 0x00003800;
+		str_shift = 11;
+		break;
+	default:
+		BRCMF_ERROR(("No SDIO Drive strength init"
+			     "done for chip %s rev %d pmurev %d\n",
+			     brcmu_chipname(bus->ci->chip, chn, 8),
+			     bus->ci->chiprev, bus->ci->pmurev));
+		break;
+	}
+
+	if (str_tab != NULL) {
+		u32 drivestrength_sel = 0;
+		u32 cc_data_temp;
+		int i;
+
+		for (i = 0; str_tab[i].strength != 0; i++) {
+			if (drivestrength >= str_tab[i].strength) {
+				drivestrength_sel = str_tab[i].sel;
+				break;
+			}
+		}
+
+		brcmf_sdcard_reg_write(bus->card,
+			CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
+			4, 1);
+		cc_data_temp = brcmf_sdcard_reg_read(bus->card,
+			CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), 4);
+		cc_data_temp &= ~str_mask;
+		drivestrength_sel <<= str_shift;
+		cc_data_temp |= drivestrength_sel;
+		brcmf_sdcard_reg_write(bus->card,
+			CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
+			4, cc_data_temp);
+
+		BRCMF_INFO(("SDIO: %dmA drive strength selected, "
+			    "set to 0x%08x\n", drivestrength, cc_data_temp));
+	}
+}
+
+static void
+brcmf_sdbrcm_chip_detach(struct brcmf_bus *bus)
+{
+	BRCMF_TRACE(("%s: Enter\n", __func__));
+
+	kfree(bus->ci);
+	bus->ci = NULL;
+}
+
+static void
+brcmf_sdbrcm_wait_for_event(struct brcmf_bus *bus, bool *lockvar)
+{
+	brcmf_sdbrcm_sdunlock(bus);
+	wait_event_interruptible_timeout(bus->ctrl_wait,
+					 (*lockvar == false), HZ * 2);
+	brcmf_sdbrcm_sdlock(bus);
+	return;
+}
+
+static void
+brcmf_sdbrcm_wait_event_wakeup(struct brcmf_bus *bus)
+{
+	if (waitqueue_active(&bus->ctrl_wait))
+		wake_up_interruptible(&bus->ctrl_wait);
+	return;
+}
+
+static int
+brcmf_sdbrcm_watchdog_thread(void *data)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *)data;
+
+	/* This thread doesn't need any user-level access,
+	* so get rid of all our resources
+	*/
+	if (brcmf_watchdog_prio > 0) {
+		struct sched_param param;
+		param.sched_priority = (brcmf_watchdog_prio < MAX_RT_PRIO) ?
+				       brcmf_watchdog_prio : (MAX_RT_PRIO - 1);
+		sched_setscheduler(current, SCHED_FIFO, &param);
+	}
+
+	allow_signal(SIGTERM);
+	/* Run until signal received */
+	while (1) {
+		if (kthread_should_stop())
+			break;
+		if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
+			if (bus->drvr->dongle_reset == false)
+				brcmf_sdbrcm_bus_watchdog(bus->drvr);
+			/* Count the tick for reference */
+			bus->drvr->tickcnt++;
+		} else
+			break;
+	}
+	return 0;
+}
+
+static void
+brcmf_sdbrcm_watchdog(unsigned long data)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *)data;
+
+	if (brcmf_watchdog_prio >= 0) {
+		if (bus->watchdog_tsk)
+			complete(&bus->watchdog_wait);
+		else
+			return;
+	} else {
+		brcmf_sdbrcm_bus_watchdog(bus->drvr);
+
+		/* Count the tick for reference */
+		bus->drvr->tickcnt++;
+	}
+
+	/* Reschedule the watchdog */
+	if (bus->wd_timer_valid)
+		mod_timer(&bus->timer, jiffies + brcmf_watchdog_ms * HZ / 1000);
+}
+
+void
+brcmf_sdbrcm_wd_timer(struct brcmf_bus *bus, uint wdtick)
+{
+	static uint save_ms;
+
+	/* don't start the wd until fw is loaded */
+	if (bus->drvr->busstate == BRCMF_BUS_DOWN)
+		return;
+
+	/* Totally stop the timer */
+	if (!wdtick && bus->wd_timer_valid == true) {
+		del_timer_sync(&bus->timer);
+		bus->wd_timer_valid = false;
+		save_ms = wdtick;
+		return;
+	}
+
+	if (wdtick) {
+		brcmf_watchdog_ms = (uint) wdtick;
+
+		if (save_ms != brcmf_watchdog_ms) {
+			if (bus->wd_timer_valid == true)
+				/* Stop timer and restart at new value */
+				del_timer_sync(&bus->timer);
+
+			/* Create timer again when watchdog period is
+			   dynamically changed or in the first instance
+			 */
+			bus->timer.expires =
+				jiffies + brcmf_watchdog_ms * HZ / 1000;
+			add_timer(&bus->timer);
+
+		} else {
+			/* Re arm the timer, at last watchdog period */
+			mod_timer(&bus->timer,
+				jiffies + brcmf_watchdog_ms * HZ / 1000);
+		}
+
+		bus->wd_timer_valid = true;
+		save_ms = wdtick;
+	}
+}
+
+static int brcmf_sdbrcm_dpc_thread(void *data)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *) data;
+
+	/* This thread doesn't need any user-level access,
+	 * so get rid of all our resources
+	 */
+	if (brcmf_dpc_prio > 0) {
+		struct sched_param param;
+		param.sched_priority = (brcmf_dpc_prio < MAX_RT_PRIO) ?
+				       brcmf_dpc_prio : (MAX_RT_PRIO - 1);
+		sched_setscheduler(current, SCHED_FIFO, &param);
+	}
+
+	allow_signal(SIGTERM);
+	/* Run until signal received */
+	while (1) {
+		if (kthread_should_stop())
+			break;
+		if (!wait_for_completion_interruptible(&bus->dpc_wait)) {
+			/* Call bus dpc unless it indicated down
+			(then clean stop) */
+			if (bus->drvr->busstate != BRCMF_BUS_DOWN) {
+				if (brcmf_sdbrcm_dpc(bus))
+					complete(&bus->dpc_wait);
+			} else {
+				brcmf_sdbrcm_bus_stop(bus, true);
+			}
+		} else
+			break;
+	}
+	return 0;
+}
+
+static void brcmf_sdbrcm_dpc_tasklet(unsigned long data)
+{
+	struct brcmf_bus *bus = (struct brcmf_bus *) data;
+
+	/* Call bus dpc unless it indicated down (then clean stop) */
+	if (bus->drvr->busstate != BRCMF_BUS_DOWN) {
+		if (brcmf_sdbrcm_dpc(bus))
+			tasklet_schedule(&bus->tasklet);
+	} else
+		brcmf_sdbrcm_bus_stop(bus, true);
+}
+
+static void brcmf_sdbrcm_sched_dpc(struct brcmf_bus *bus)
+{
+	if (bus->dpc_tsk) {
+		complete(&bus->dpc_wait);
+		return;
+	}
+
+	tasklet_schedule(&bus->tasklet);
+}
+
+static void brcmf_sdbrcm_sdlock(struct brcmf_bus *bus)
+{
+	if (bus->threads_only)
+		down(&bus->sdsem);
+	else
+		spin_lock_bh(&bus->sdlock);
+}
+
+static void brcmf_sdbrcm_sdunlock(struct brcmf_bus *bus)
+{
+	if (bus->threads_only)
+		up(&bus->sdsem);
+	else
+		spin_unlock_bh(&bus->sdlock);
+}
+
+static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_bus *bus)
+{
+	if (bus->firmware->size < bus->fw_ptr + len)
+		len = bus->firmware->size - bus->fw_ptr;
+
+	memcpy(buf, &bus->firmware->data[bus->fw_ptr], len);
+	bus->fw_ptr += len;
+	return len;
+}
+
+MODULE_FIRMWARE(BCM4329_FW_NAME);
+MODULE_FIRMWARE(BCM4329_NV_NAME);
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
new file mode 100644
index 0000000..d345472
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/sdio_host.h
@@ -0,0 +1,347 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef	_BRCM_SDH_H_
+#define	_BRCM_SDH_H_
+
+#include <linux/skbuff.h>
+extern const uint brcmf_sdio_msglevel;
+
+#define SDIO_FUNC_0		0
+#define SDIO_FUNC_1		1
+#define SDIO_FUNC_2		2
+
+#define SDIOD_FBR_SIZE		0x100
+
+/* io_en */
+#define SDIO_FUNC_ENABLE_1	0x02
+#define SDIO_FUNC_ENABLE_2	0x04
+
+/* io_rdys */
+#define SDIO_FUNC_READY_1	0x02
+#define SDIO_FUNC_READY_2	0x04
+
+/* intr_status */
+#define INTR_STATUS_FUNC1	0x2
+#define INTR_STATUS_FUNC2	0x4
+
+/* Maximum number of I/O funcs */
+#define SDIOD_MAX_IOFUNCS	7
+
+#define SBSDIO_NUM_FUNCTION		3	/* as of sdiod rev 0, supports 3 functions */
+
+/* function 1 miscellaneous registers */
+#define SBSDIO_SPROM_CS			0x10000	/* sprom command and status */
+#define SBSDIO_SPROM_INFO		0x10001	/* sprom info register */
+#define SBSDIO_SPROM_DATA_LOW		0x10002	/* sprom indirect access data byte 0 */
+#define SBSDIO_SPROM_DATA_HIGH		0x10003	/* sprom indirect access data byte 1 */
+#define SBSDIO_SPROM_ADDR_LOW		0x10004	/* sprom indirect access addr byte 0 */
+#define SBSDIO_SPROM_ADDR_HIGH		0x10005	/* sprom indirect access addr byte 0 */
+#define SBSDIO_CHIP_CTRL_DATA		0x10006	/* xtal_pu (gpio) output */
+#define SBSDIO_CHIP_CTRL_EN		0x10007	/* xtal_pu (gpio) enable */
+#define SBSDIO_WATERMARK		0x10008	/* rev < 7, watermark for sdio device */
+#define SBSDIO_DEVICE_CTL		0x10009	/* control busy signal generation */
+
+/* registers introduced in rev 8, some content (mask/bits) defs in sbsdpcmdev.h */
+#define SBSDIO_FUNC1_SBADDRLOW		0x1000A	/* SB Address Window Low (b15) */
+#define SBSDIO_FUNC1_SBADDRMID		0x1000B	/* SB Address Window Mid (b23:b16) */
+#define SBSDIO_FUNC1_SBADDRHIGH		0x1000C	/* SB Address Window High (b31:b24)    */
+#define SBSDIO_FUNC1_FRAMECTRL		0x1000D	/* Frame Control (frame term/abort) */
+#define SBSDIO_FUNC1_CHIPCLKCSR		0x1000E	/* ChipClockCSR (ALP/HT ctl/status) */
+#define SBSDIO_FUNC1_SDIOPULLUP		0x1000F	/* SdioPullUp (on cmd, d0-d2) */
+#define SBSDIO_FUNC1_WFRAMEBCLO		0x10019	/* Write Frame Byte Count Low */
+#define SBSDIO_FUNC1_WFRAMEBCHI		0x1001A	/* Write Frame Byte Count High */
+#define SBSDIO_FUNC1_RFRAMEBCLO		0x1001B	/* Read Frame Byte Count Low */
+#define SBSDIO_FUNC1_RFRAMEBCHI		0x1001C	/* Read Frame Byte Count High */
+
+#define SBSDIO_FUNC1_MISC_REG_START	0x10000	/* f1 misc register start */
+#define SBSDIO_FUNC1_MISC_REG_LIMIT	0x1001C	/* f1 misc register end */
+
+/* function 1 OCP space */
+#define SBSDIO_SB_OFT_ADDR_MASK		0x07FFF	/* sb offset addr is <= 15 bits, 32k */
+#define SBSDIO_SB_OFT_ADDR_LIMIT	0x08000
+#define SBSDIO_SB_ACCESS_2_4B_FLAG	0x08000	/* with b15, maps to 32-bit SB access */
+
+/* some duplication with sbsdpcmdev.h here */
+/* valid bits in SBSDIO_FUNC1_SBADDRxxx regs */
+#define SBSDIO_SBADDRLOW_MASK		0x80	/* Valid bits in SBADDRLOW */
+#define SBSDIO_SBADDRMID_MASK		0xff	/* Valid bits in SBADDRMID */
+#define SBSDIO_SBADDRHIGH_MASK		0xffU	/* Valid bits in SBADDRHIGH */
+#define SBSDIO_SBWINDOW_MASK		0xffff8000	/* Address bits from SBADDR regs */
+
+#define SDIOH_READ              0	/* Read request */
+#define SDIOH_WRITE             1	/* Write request */
+
+#define SDIOH_DATA_FIX          0	/* Fixed addressing */
+#define SDIOH_DATA_INC          1	/* Incremental addressing */
+
+/* internal return code */
+#define SUCCESS	0
+#define ERROR	1
+
+/* forward declarations */
+struct brcmf_sdio_card;
+
+struct brcmf_sdreg {
+	int func;
+	int offset;
+	int value;
+};
+
+struct sdioh_info {
+	struct osl_info *osh;		/* osh handler */
+	bool client_intr_enabled;	/* interrupt connnected flag */
+	bool intr_handler_valid;	/* client driver interrupt handler valid */
+	void (*intr_handler)(void *);	/* registered interrupt handler */
+	void *intr_handler_arg;	/* argument to call interrupt handler */
+	u16 intmask;		/* Current active interrupts */
+	void *sdos_info;	/* Pointer to per-OS private data */
+
+	uint irq;		/* Client irq */
+	int intrcount;		/* Client interrupts */
+	bool sd_blockmode;	/* sd_blockmode == false => 64 Byte Cmd 53s. */
+	/*  Must be on for sd_multiblock to be effective */
+	bool use_client_ints;	/* If this is false, make sure to restore */
+	int client_block_size[SDIOD_MAX_IOFUNCS];	/* Blocksize */
+	u8 num_funcs;	/* Supported funcs on client */
+	u32 com_cis_ptr;
+	u32 func_cis_ptr[SDIOD_MAX_IOFUNCS];
+	uint max_dma_len;
+	uint max_dma_descriptors;	/* DMA Descriptors supported by this controller. */
+	/*	SDDMA_DESCRIPTOR	SGList[32]; *//* Scatter/Gather DMA List */
+};
+
+struct brcmf_sdmmc_instance {
+	struct sdioh_info *sd;
+	struct sdio_func *func[SDIOD_MAX_IOFUNCS];
+	u32 host_claimed;
+};
+
+/* Attach and build an interface to the underlying SD host driver.
+ *  - Allocates resources (structs, arrays, mem, OS handles, etc) needed by
+ *    brcmf_sdcard.
+ *  - Returns the sdio card handle and virtual address base for register access.
+ *    The returned handle should be used in all subsequent calls, but the bcmsh
+ *    implementation may maintain a single "default" handle (e.g. the first or
+ *    most recent one) to enable single-instance implementations to pass NULL.
+ */
+extern struct brcmf_sdio_card*
+brcmf_sdcard_attach(void *cfghdl, u32 *regsva, uint irq);
+
+/* Detach - freeup resources allocated in attach */
+extern int brcmf_sdcard_detach(struct brcmf_sdio_card *card);
+
+/* Enable/disable SD interrupt */
+extern int brcmf_sdcard_intr_enable(struct brcmf_sdio_card *card);
+extern int brcmf_sdcard_intr_disable(struct brcmf_sdio_card *card);
+
+/* Register/deregister device interrupt handler. */
+extern int
+brcmf_sdcard_intr_reg(struct brcmf_sdio_card *card,
+		      void (*fn)(void *), void *argh);
+
+extern int brcmf_sdcard_intr_dereg(struct brcmf_sdio_card *card);
+
+/* Access SDIO address space (e.g. CCCR) using CMD52 (single-byte interface).
+ *   fn:   function number
+ *   addr: unmodified SDIO-space address
+ *   data: data byte to write
+ *   err:  pointer to error code (or NULL)
+ */
+extern u8 brcmf_sdcard_cfg_read(struct brcmf_sdio_card *card, uint func,
+				u32 addr, int *err);
+extern void brcmf_sdcard_cfg_write(struct brcmf_sdio_card *card, uint func,
+				   u32 addr, u8 data, int *err);
+
+/* Read/Write 4bytes from/to cfg space */
+extern u32
+brcmf_sdcard_cfg_read_word(struct brcmf_sdio_card *card, uint fnc_num,
+			   u32 addr, int *err);
+
+extern void brcmf_sdcard_cfg_write_word(struct brcmf_sdio_card *card,
+					uint fnc_num, u32 addr,
+					u32 data, int *err);
+
+/* Read CIS content for specified function.
+ *   fn:     function whose CIS is being requested (0 is common CIS)
+ *   cis:    pointer to memory location to place results
+ *   length: number of bytes to read
+ * Internally, this routine uses the values from the cis base regs (0x9-0xB)
+ * to form an SDIO-space address to read the data from.
+ */
+extern int brcmf_sdcard_cis_read(struct brcmf_sdio_card *card, uint func,
+				 u8 *cis, uint length);
+
+/* Synchronous access to device (client) core registers via CMD53 to F1.
+ *   addr: backplane address (i.e. >= regsva from attach)
+ *   size: register width in bytes (2 or 4)
+ *   data: data for register write
+ */
+extern u32
+brcmf_sdcard_reg_read(struct brcmf_sdio_card *card, u32 addr, uint size);
+
+extern u32
+brcmf_sdcard_reg_write(struct brcmf_sdio_card *card, u32 addr, uint size,
+		       u32 data);
+
+/* Indicate if last reg read/write failed */
+extern bool brcmf_sdcard_regfail(struct brcmf_sdio_card *card);
+
+/* Buffer transfer to/from device (client) core via cmd53.
+ *   fn:       function number
+ *   addr:     backplane address (i.e. >= regsva from attach)
+ *   flags:    backplane width, address increment, sync/async
+ *   buf:      pointer to memory data buffer
+ *   nbytes:   number of bytes to transfer to/from buf
+ *   pkt:      pointer to packet associated with buf (if any)
+ *   complete: callback function for command completion (async only)
+ *   handle:   handle for completion callback (first arg in callback)
+ * Returns 0 or error code.
+ * NOTE: Async operation is not currently supported.
+ */
+extern int
+brcmf_sdcard_send_buf(struct brcmf_sdio_card *card, u32 addr, uint fn,
+		      uint flags, u8 *buf, uint nbytes, void *pkt,
+		      void (*complete)(void *handle, int status,
+				       bool sync_waiting),
+		      void *handle);
+extern int
+brcmf_sdcard_recv_buf(struct brcmf_sdio_card *card, u32 addr, uint fn,
+		      uint flags, u8 *buf, uint nbytes, struct sk_buff *pkt,
+		      void (*complete)(void *handle, int status,
+				       bool sync_waiting),
+		      void *handle);
+
+/* Flags bits */
+#define SDIO_REQ_4BYTE	0x1	/* Four-byte target (backplane) width (vs. two-byte) */
+#define SDIO_REQ_FIXED	0x2	/* Fixed address (FIFO) (vs. incrementing address) */
+#define SDIO_REQ_ASYNC	0x4	/* Async request (vs. sync request) */
+
+/* Pending (non-error) return code */
+#define BCME_PENDING	1
+
+/* Read/write to memory block (F1, no FIFO) via CMD53 (sync only).
+ *   rw:       read or write (0/1)
+ *   addr:     direct SDIO address
+ *   buf:      pointer to memory data buffer
+ *   nbytes:   number of bytes to transfer to/from buf
+ * Returns 0 or error code.
+ */
+extern int brcmf_sdcard_rwdata(struct brcmf_sdio_card *card, uint rw, u32 addr,
+			       u8 *buf, uint nbytes);
+
+/* Issue an abort to the specified function */
+extern int brcmf_sdcard_abort(struct brcmf_sdio_card *card, uint fn);
+
+/* Returns the "Device ID" of target device on the SDIO bus. */
+extern int brcmf_sdcard_query_device(struct brcmf_sdio_card *card);
+
+/* Miscellaneous knob tweaker. */
+extern int brcmf_sdcard_iovar_op(struct brcmf_sdio_card *card, const char *name,
+				 void *params, int plen, void *arg, int len,
+				 bool set);
+
+/* helper functions */
+
+/* callback functions */
+struct brcmf_sdioh_driver {
+	/* attach to device */
+	void *(*attach) (u16 vend_id, u16 dev_id, u16 bus, u16 slot,
+			 u16 func, uint bustype, u32 regsva, void *param);
+	/* detach from device */
+	void (*detach) (void *ch);
+};
+
+struct sdioh_info;
+
+/* platform specific/high level functions */
+extern int brcmf_sdio_function_init(void);
+extern int brcmf_sdio_register(struct brcmf_sdioh_driver *driver);
+extern void brcmf_sdio_unregister(void);
+extern void brcmf_sdio_function_cleanup(void);
+extern int brcmf_sdio_probe(struct device *dev);
+extern int brcmf_sdio_remove(struct device *dev);
+
+/* Function to return current window addr */
+extern u32 brcmf_sdcard_cur_sbwad(struct brcmf_sdio_card *card);
+
+/* Allocate/init/free per-OS private data */
+extern int  brcmf_sdioh_osinit(struct sdioh_info *sd);
+extern void brcmf_sdioh_osfree(struct sdioh_info *sd);
+
+/* Core interrupt enable/disable of device interrupts */
+extern void brcmf_sdioh_dev_intr_on(struct sdioh_info *sd);
+extern void brcmf_sdioh_dev_intr_off(struct sdioh_info *sd);
+
+/* attach, return handler on success, NULL if failed.
+ *  The handler shall be provided by all subsequent calls. No local cache
+ *  cfghdl points to the starting address of pci device mapped memory
+ */
+extern struct sdioh_info *brcmf_sdioh_attach(void *cfghdl, uint irq);
+extern int brcmf_sdioh_detach(struct sdioh_info *si);
+
+extern int
+brcmf_sdioh_interrupt_register(struct sdioh_info *si,
+			       void (*sdioh_cb_fn)(void *), void *argh);
+
+extern int brcmf_sdioh_interrupt_deregister(struct sdioh_info *si);
+
+/* enable or disable SD interrupt */
+extern int
+brcmf_sdioh_interrupt_set(struct sdioh_info *si, bool enable_disable);
+
+/* read or write one byte using cmd52 */
+extern int
+brcmf_sdioh_request_byte(struct sdioh_info *si, uint rw, uint fnc, uint addr,
+			 u8 *byte);
+
+/* read or write 2/4 bytes using cmd53 */
+extern int
+brcmf_sdioh_request_word(struct sdioh_info *si, uint cmd_type,
+			 uint rw, uint fnc, uint addr,
+			 u32 *word, uint nbyte);
+
+/* read or write any buffer using cmd53 */
+extern int
+brcmf_sdioh_request_buffer(struct sdioh_info *si, uint pio_dma,
+			   uint fix_inc, uint rw, uint fnc_num,
+			   u32 addr, uint regwidth,
+			   u32 buflen, u8 *buffer, struct sk_buff *pkt);
+
+/* get cis data */
+extern int
+brcmf_sdioh_cis_read(struct sdioh_info *si, uint fuc, u8 *cis, u32 length);
+
+extern int
+brcmf_sdioh_cfg_read(struct sdioh_info *si, uint fuc, u32 addr, u8 *data);
+extern int
+brcmf_sdioh_cfg_write(struct sdioh_info *si, uint fuc, u32 addr, u8 *data);
+
+/* handle iovars */
+extern int brcmf_sdioh_iovar_op(struct sdioh_info *si, const char *name,
+			  void *params, int plen, void *arg, int len, bool set);
+
+/* Issue abort to the specified function and clear controller as needed */
+extern int brcmf_sdioh_abort(struct sdioh_info *si, uint fnc);
+
+/* Watchdog timer interface for pm ops */
+extern void brcmf_sdio_wdtmr_enable(bool enable);
+
+extern uint sd_msglevel;	/* Debug message level */
+
+extern struct brcmf_sdmmc_instance *gInstance;
+
+#endif				/* _BRCM_SDH_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
new file mode 100644
index 0000000..821206d
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.c
@@ -0,0 +1,4152 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/kernel.h>
+#include <linux/if_arp.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/netdevice.h>
+#include <linux/sched.h>
+#include <linux/etherdevice.h>
+#include <linux/wireless.h>
+#include <linux/ieee80211.h>
+#include <linux/mmc/sdio_func.h>
+#include <linux/uaccess.h>
+#include <net/cfg80211.h>
+#include <net/rtnetlink.h>
+
+#include <brcmu_utils.h>
+#include <defs.h>
+#include <brcmu_wifi.h>
+#include "dhd.h"
+#include "wl_cfg80211.h"
+
+static struct sdio_func *cfg80211_sdio_func;
+static struct brcmf_cfg80211_dev *cfg80211_dev;
+static const u8 ether_bcast[ETH_ALEN] = {255, 255, 255, 255, 255, 255};
+
+u32 brcmf_dbg_level = WL_DBG_ERR;
+
+/*
+** cfg80211_ops api/callback list
+*/
+static s32 brcmf_cfg80211_change_iface(struct wiphy *wiphy,
+				       struct net_device *ndev,
+				       enum nl80211_iftype type, u32 *flags,
+				       struct vif_params *params);
+static s32 __brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+				 struct cfg80211_scan_request *request,
+				 struct cfg80211_ssid *this_ssid);
+static s32 brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+			       struct cfg80211_scan_request *request);
+static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed);
+static s32 brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+				    struct cfg80211_ibss_params *params);
+static s32 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy,
+				     struct net_device *dev);
+static s32 brcmf_cfg80211_get_station(struct wiphy *wiphy,
+				      struct net_device *dev, u8 *mac,
+				      struct station_info *sinfo);
+static s32 brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy,
+					 struct net_device *dev, bool enabled,
+					 s32 timeout);
+static s32 brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy,
+					   struct net_device *dev,
+					   const u8 *addr,
+					   const struct cfg80211_bitrate_mask
+					   *mask);
+static int brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+				  struct cfg80211_connect_params *sme);
+static s32 brcmf_cfg80211_disconnect(struct wiphy *wiphy,
+				     struct net_device *dev,
+				     u16 reason_code);
+static s32 brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
+				      enum nl80211_tx_power_setting type,
+				      s32 dbm);
+static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm);
+static s32 brcmf_cfg80211_config_default_key(struct wiphy *wiphy,
+					  struct net_device *dev, u8 key_idx,
+					  bool unicast, bool multicast);
+static s32 brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+				 u8 key_idx, bool pairwise, const u8 *mac_addr,
+				 struct key_params *params);
+static s32 brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+				 u8 key_idx, bool pairwise, const u8 *mac_addr);
+static s32 brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+				 u8 key_idx, bool pairwise, const u8 *mac_addr,
+				 void *cookie, void (*callback) (void *cookie,
+								 struct
+								 key_params *
+								 params));
+static s32 brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+						 struct net_device *dev,
+						 u8 key_idx);
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy);
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+				 struct cfg80211_wowlan *wow);
+static s32 brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
+				   struct cfg80211_pmksa *pmksa);
+static s32 brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
+				   struct cfg80211_pmksa *pmksa);
+static s32 brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy,
+				     struct net_device *dev);
+/*
+** event & event Q handlers for cfg80211 interfaces
+*/
+static s32 brcmf_create_event_handler(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_destroy_event_handler(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_event_handler(void *data);
+static void brcmf_init_eq(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_flush_eq(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_lock_eq(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_unlock_eq(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_init_eq_lock(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el);
+static struct brcmf_cfg80211_event_q *
+brcmf_deq_event(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_enq_event(struct brcmf_cfg80211_priv *cfg_priv, u32 type,
+			  const struct brcmf_event_msg *msg, void *data);
+static void brcmf_put_event(struct brcmf_cfg80211_event_q *e);
+static void brcmf_wakeup_event(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_notify_connect_status(struct brcmf_cfg80211_priv *cfg_priv,
+				       struct net_device *ndev,
+				       const struct brcmf_event_msg *e,
+				       void *data);
+static s32 brcmf_notify_roaming_status(struct brcmf_cfg80211_priv *cfg_priv,
+				       struct net_device *ndev,
+				       const struct brcmf_event_msg *e,
+				       void *data);
+static s32 brcmf_notify_scan_status(struct brcmf_cfg80211_priv *cfg_priv,
+				    struct net_device *ndev,
+				    const struct brcmf_event_msg *e,
+				    void *data);
+static s32 brcmf_bss_connect_done(struct brcmf_cfg80211_priv *cfg_priv,
+				  struct net_device *ndev,
+				  const struct brcmf_event_msg *e, void *data,
+				  bool completed);
+static s32 brcmf_bss_roaming_done(struct brcmf_cfg80211_priv *cfg_priv,
+				  struct net_device *ndev,
+				  const struct brcmf_event_msg *e, void *data);
+static s32 brcmf_notify_mic_status(struct brcmf_cfg80211_priv *cfg_priv,
+				   struct net_device *ndev,
+				   const struct brcmf_event_msg *e, void *data);
+
+/*
+** register/deregister sdio function
+*/
+static void brcmf_clear_sdio_func(void);
+
+/*
+** ioctl utilites
+*/
+static s32 brcmf_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf,
+			       s32 buf_len);
+static __used s32 brcmf_dev_bufvar_set(struct net_device *dev, s8 *name,
+				      s8 *buf, s32 len);
+static s32 brcmf_dev_intvar_set(struct net_device *dev, s8 *name, s32 val);
+static s32 brcmf_dev_intvar_get(struct net_device *dev, s8 *name,
+			       s32 *retval);
+static s32 brcmf_dev_ioctl(struct net_device *dev, u32 cmd, void *arg,
+			  u32 len);
+
+/*
+** cfg80211 set_wiphy_params utilities
+*/
+static s32 brcmf_set_frag(struct net_device *dev, u32 frag_threshold);
+static s32 brcmf_set_rts(struct net_device *dev, u32 frag_threshold);
+static s32 brcmf_set_retry(struct net_device *dev, u32 retry, bool l);
+
+/*
+** wl profile utilities
+*/
+static s32 brcmf_update_prof(struct brcmf_cfg80211_priv *cfg_priv,
+			     const struct brcmf_event_msg *e,
+			     void *data, s32 item);
+static void *brcmf_read_prof(struct brcmf_cfg80211_priv *cfg_priv, s32 item);
+static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof);
+
+/*
+** cfg80211 connect utilites
+*/
+static s32 brcmf_set_wpa_version(struct net_device *dev,
+			struct cfg80211_connect_params *sme);
+static s32 brcmf_set_auth_type(struct net_device *dev,
+			struct cfg80211_connect_params *sme);
+static s32 brcmf_set_set_cipher(struct net_device *dev,
+			struct cfg80211_connect_params *sme);
+static s32 brcmf_set_key_mgmt(struct net_device *dev,
+			struct cfg80211_connect_params *sme);
+static s32 brcmf_set_set_sharedkey(struct net_device *dev,
+			struct cfg80211_connect_params *sme);
+static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_ch_to_chanspec(int ch,
+	struct brcmf_join_params *join_params, size_t *join_params_size);
+
+/*
+** information element utilities
+*/
+static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv,
+			       u8 t, u8 l, u8 *v);
+static s32 brcmf_mode_to_nl80211_iftype(s32 mode);
+static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
+			struct device *dev);
+static void brcmf_free_wdev(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv,
+				   struct brcmf_bss_info *bi);
+static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_add_keyext(struct wiphy *wiphy, struct net_device *dev,
+			u8 key_idx, const u8 *mac_addr,
+			struct key_params *params);
+
+/*
+** key indianess swap utilities
+*/
+static void swap_key_from_BE(struct brcmf_wsec_key *key);
+static void swap_key_to_BE(struct brcmf_wsec_key *key);
+
+/*
+** brcmf_cfg80211_priv memory init/deinit utilities
+*/
+static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv);
+
+static void brcmf_delay(u32 ms);
+
+/*
+** store/restore cfg80211 instance data
+*/
+static void brcmf_set_drvdata(struct brcmf_cfg80211_dev *dev, void *data);
+static void *brcmf_get_drvdata(struct brcmf_cfg80211_dev *dev);
+
+/*
+** ibss mode utilities
+*/
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_priv *cfg_priv);
+
+/*
+** dongle up/down , default configuration utilities
+*/
+static bool brcmf_is_linkdown(struct brcmf_cfg80211_priv *cfg_priv,
+			      const struct brcmf_event_msg *e);
+static bool brcmf_is_linkup(struct brcmf_cfg80211_priv *cfg_priv,
+			    const struct brcmf_event_msg *e);
+static bool brcmf_is_nonetwork(struct brcmf_cfg80211_priv *cfg_priv,
+			       const struct brcmf_event_msg *e);
+static void brcmf_link_down(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype);
+static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf);
+
+/*
+** dongle configuration utilities
+*/
+static s32 brcmf_dongle_eventmsg(struct net_device *ndev);
+static s32 brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
+				 s32 scan_unassoc_time, s32 scan_passive_time);
+static s32 brcmf_config_dongle(struct brcmf_cfg80211_priv *cfg_priv,
+			       bool need_lock);
+static s32 brcmf_dongle_roam(struct net_device *ndev, u32 roamvar,
+			    u32 bcn_timeout);
+
+/*
+** iscan handler
+*/
+static void brcmf_iscan_timer(unsigned long data);
+static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_iscan_thread(void *data);
+static s32 brcmf_dev_iovar_setbuf(struct net_device *dev, s8 *iovar,
+				 void *param, s32 paramlen, void *bufptr,
+				 s32 buflen);
+static s32 brcmf_dev_iovar_getbuf(struct net_device *dev, s8 *iovar,
+				 void *param, s32 paramlen, void *bufptr,
+				 s32 buflen);
+static s32 brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
+			   struct brcmf_ssid *ssid, u16 action);
+static s32 brcmf_do_iscan(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan);
+static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan,
+				   u32 *status,
+				   struct brcmf_scan_results **bss_list);
+static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
+					bool aborted);
+static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el);
+static s32 brcmf_iscan_done(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_iscan_pending(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_priv *cfg_priv);
+static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_priv *cfg_priv);
+
+/*
+* find most significant bit set
+*/
+static __used u32 brcmf_find_msb(u16 bit16);
+
+/*
+* update pmklist to dongle
+*/
+static __used s32 brcmf_update_pmklist(struct net_device *dev,
+				       struct brcmf_cfg80211_pmk_list *pmk_list,
+				       s32 err);
+
+static void brcmf_set_mpc(struct net_device *ndev, int mpc);
+
+/*
+* debufs support
+*/
+static int
+brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_priv *cfg_priv);
+static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_priv *cfg_priv);
+
+#define WL_PRIV_GET()							\
+	({								\
+	struct brcmf_cfg80211_iface *ci = brcmf_get_drvdata(cfg80211_dev); \
+	if (unlikely(!ci)) {						\
+		WL_ERR("wl_cfg80211_dev is unavailable\n");		\
+		BUG();							\
+	}								\
+	ci->cfg_priv;							\
+})
+
+#define CHECK_SYS_UP()							\
+do {									\
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);	\
+	if (unlikely(!test_bit(WL_STATUS_READY, &cfg_priv->status))) {	\
+		WL_INFO("device is not ready : status (%d)\n",		\
+			(int)cfg_priv->status);				\
+		return -EIO;						\
+	}								\
+} while (0)
+
+#define CHAN2G(_channel, _freq, _flags) {			\
+	.band			= IEEE80211_BAND_2GHZ,		\
+	.center_freq		= (_freq),			\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+#define CHAN5G(_channel, _flags) {				\
+	.band			= IEEE80211_BAND_5GHZ,		\
+	.center_freq		= 5000 + (5 * (_channel)),	\
+	.hw_value		= (_channel),			\
+	.flags			= (_flags),			\
+	.max_antenna_gain	= 0,				\
+	.max_power		= 30,				\
+}
+
+#define RATE_TO_BASE100KBPS(rate)   (((rate) * 10) / 2)
+#define RATETAB_ENT(_rateid, _flags) \
+	{                                                               \
+		.bitrate        = RATE_TO_BASE100KBPS(_rateid),     \
+		.hw_value       = (_rateid),                            \
+		.flags          = (_flags),                             \
+	}
+
+static struct ieee80211_rate __wl_rates[] = {
+	RATETAB_ENT(BRCM_RATE_1M, 0),
+	RATETAB_ENT(BRCM_RATE_2M, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATETAB_ENT(BRCM_RATE_5M5, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATETAB_ENT(BRCM_RATE_11M, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATETAB_ENT(BRCM_RATE_6M, 0),
+	RATETAB_ENT(BRCM_RATE_9M, 0),
+	RATETAB_ENT(BRCM_RATE_12M, 0),
+	RATETAB_ENT(BRCM_RATE_18M, 0),
+	RATETAB_ENT(BRCM_RATE_24M, 0),
+	RATETAB_ENT(BRCM_RATE_36M, 0),
+	RATETAB_ENT(BRCM_RATE_48M, 0),
+	RATETAB_ENT(BRCM_RATE_54M, 0),
+};
+
+#define wl_a_rates		(__wl_rates + 4)
+#define wl_a_rates_size	8
+#define wl_g_rates		(__wl_rates + 0)
+#define wl_g_rates_size	12
+
+static struct ieee80211_channel __wl_2ghz_channels[] = {
+	CHAN2G(1, 2412, 0),
+	CHAN2G(2, 2417, 0),
+	CHAN2G(3, 2422, 0),
+	CHAN2G(4, 2427, 0),
+	CHAN2G(5, 2432, 0),
+	CHAN2G(6, 2437, 0),
+	CHAN2G(7, 2442, 0),
+	CHAN2G(8, 2447, 0),
+	CHAN2G(9, 2452, 0),
+	CHAN2G(10, 2457, 0),
+	CHAN2G(11, 2462, 0),
+	CHAN2G(12, 2467, 0),
+	CHAN2G(13, 2472, 0),
+	CHAN2G(14, 2484, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_a_channels[] = {
+	CHAN5G(34, 0), CHAN5G(36, 0),
+	CHAN5G(38, 0), CHAN5G(40, 0),
+	CHAN5G(42, 0), CHAN5G(44, 0),
+	CHAN5G(46, 0), CHAN5G(48, 0),
+	CHAN5G(52, 0), CHAN5G(56, 0),
+	CHAN5G(60, 0), CHAN5G(64, 0),
+	CHAN5G(100, 0), CHAN5G(104, 0),
+	CHAN5G(108, 0), CHAN5G(112, 0),
+	CHAN5G(116, 0), CHAN5G(120, 0),
+	CHAN5G(124, 0), CHAN5G(128, 0),
+	CHAN5G(132, 0), CHAN5G(136, 0),
+	CHAN5G(140, 0), CHAN5G(149, 0),
+	CHAN5G(153, 0), CHAN5G(157, 0),
+	CHAN5G(161, 0), CHAN5G(165, 0),
+	CHAN5G(184, 0), CHAN5G(188, 0),
+	CHAN5G(192, 0), CHAN5G(196, 0),
+	CHAN5G(200, 0), CHAN5G(204, 0),
+	CHAN5G(208, 0), CHAN5G(212, 0),
+	CHAN5G(216, 0),
+};
+
+static struct ieee80211_channel __wl_5ghz_n_channels[] = {
+	CHAN5G(32, 0), CHAN5G(34, 0),
+	CHAN5G(36, 0), CHAN5G(38, 0),
+	CHAN5G(40, 0), CHAN5G(42, 0),
+	CHAN5G(44, 0), CHAN5G(46, 0),
+	CHAN5G(48, 0), CHAN5G(50, 0),
+	CHAN5G(52, 0), CHAN5G(54, 0),
+	CHAN5G(56, 0), CHAN5G(58, 0),
+	CHAN5G(60, 0), CHAN5G(62, 0),
+	CHAN5G(64, 0), CHAN5G(66, 0),
+	CHAN5G(68, 0), CHAN5G(70, 0),
+	CHAN5G(72, 0), CHAN5G(74, 0),
+	CHAN5G(76, 0), CHAN5G(78, 0),
+	CHAN5G(80, 0), CHAN5G(82, 0),
+	CHAN5G(84, 0), CHAN5G(86, 0),
+	CHAN5G(88, 0), CHAN5G(90, 0),
+	CHAN5G(92, 0), CHAN5G(94, 0),
+	CHAN5G(96, 0), CHAN5G(98, 0),
+	CHAN5G(100, 0), CHAN5G(102, 0),
+	CHAN5G(104, 0), CHAN5G(106, 0),
+	CHAN5G(108, 0), CHAN5G(110, 0),
+	CHAN5G(112, 0), CHAN5G(114, 0),
+	CHAN5G(116, 0), CHAN5G(118, 0),
+	CHAN5G(120, 0), CHAN5G(122, 0),
+	CHAN5G(124, 0), CHAN5G(126, 0),
+	CHAN5G(128, 0), CHAN5G(130, 0),
+	CHAN5G(132, 0), CHAN5G(134, 0),
+	CHAN5G(136, 0), CHAN5G(138, 0),
+	CHAN5G(140, 0), CHAN5G(142, 0),
+	CHAN5G(144, 0), CHAN5G(145, 0),
+	CHAN5G(146, 0), CHAN5G(147, 0),
+	CHAN5G(148, 0), CHAN5G(149, 0),
+	CHAN5G(150, 0), CHAN5G(151, 0),
+	CHAN5G(152, 0), CHAN5G(153, 0),
+	CHAN5G(154, 0), CHAN5G(155, 0),
+	CHAN5G(156, 0), CHAN5G(157, 0),
+	CHAN5G(158, 0), CHAN5G(159, 0),
+	CHAN5G(160, 0), CHAN5G(161, 0),
+	CHAN5G(162, 0), CHAN5G(163, 0),
+	CHAN5G(164, 0), CHAN5G(165, 0),
+	CHAN5G(166, 0), CHAN5G(168, 0),
+	CHAN5G(170, 0), CHAN5G(172, 0),
+	CHAN5G(174, 0), CHAN5G(176, 0),
+	CHAN5G(178, 0), CHAN5G(180, 0),
+	CHAN5G(182, 0), CHAN5G(184, 0),
+	CHAN5G(186, 0), CHAN5G(188, 0),
+	CHAN5G(190, 0), CHAN5G(192, 0),
+	CHAN5G(194, 0), CHAN5G(196, 0),
+	CHAN5G(198, 0), CHAN5G(200, 0),
+	CHAN5G(202, 0), CHAN5G(204, 0),
+	CHAN5G(206, 0), CHAN5G(208, 0),
+	CHAN5G(210, 0), CHAN5G(212, 0),
+	CHAN5G(214, 0), CHAN5G(216, 0),
+	CHAN5G(218, 0), CHAN5G(220, 0),
+	CHAN5G(222, 0), CHAN5G(224, 0),
+	CHAN5G(226, 0), CHAN5G(228, 0),
+};
+
+static struct ieee80211_supported_band __wl_band_2ghz = {
+	.band = IEEE80211_BAND_2GHZ,
+	.channels = __wl_2ghz_channels,
+	.n_channels = ARRAY_SIZE(__wl_2ghz_channels),
+	.bitrates = wl_g_rates,
+	.n_bitrates = wl_g_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_a = {
+	.band = IEEE80211_BAND_5GHZ,
+	.channels = __wl_5ghz_a_channels,
+	.n_channels = ARRAY_SIZE(__wl_5ghz_a_channels),
+	.bitrates = wl_a_rates,
+	.n_bitrates = wl_a_rates_size,
+};
+
+static struct ieee80211_supported_band __wl_band_5ghz_n = {
+	.band = IEEE80211_BAND_5GHZ,
+	.channels = __wl_5ghz_n_channels,
+	.n_channels = ARRAY_SIZE(__wl_5ghz_n_channels),
+	.bitrates = wl_a_rates,
+	.n_bitrates = wl_a_rates_size,
+};
+
+static const u32 __wl_cipher_suites[] = {
+	WLAN_CIPHER_SUITE_WEP40,
+	WLAN_CIPHER_SUITE_WEP104,
+	WLAN_CIPHER_SUITE_TKIP,
+	WLAN_CIPHER_SUITE_CCMP,
+	WLAN_CIPHER_SUITE_AES_CMAC,
+};
+
+static void swap_key_from_BE(struct brcmf_wsec_key *key)
+{
+	key->index = cpu_to_le32(key->index);
+	key->len = cpu_to_le32(key->len);
+	key->algo = cpu_to_le32(key->algo);
+	key->flags = cpu_to_le32(key->flags);
+	key->rxiv.hi = cpu_to_le32(key->rxiv.hi);
+	key->rxiv.lo = cpu_to_le16(key->rxiv.lo);
+	key->iv_initialized = cpu_to_le32(key->iv_initialized);
+}
+
+static void swap_key_to_BE(struct brcmf_wsec_key *key)
+{
+	key->index = le32_to_cpu(key->index);
+	key->len = le32_to_cpu(key->len);
+	key->algo = le32_to_cpu(key->algo);
+	key->flags = le32_to_cpu(key->flags);
+	key->rxiv.hi = le32_to_cpu(key->rxiv.hi);
+	key->rxiv.lo = le16_to_cpu(key->rxiv.lo);
+	key->iv_initialized = le32_to_cpu(key->iv_initialized);
+}
+
+static s32
+brcmf_dev_ioctl(struct net_device *dev, u32 cmd, void *arg, u32 len)
+{
+	struct ifreq ifr;
+	struct brcmf_ioctl ioc;
+	mm_segment_t fs;
+	s32 err = 0;
+
+	memset(&ioc, 0, sizeof(ioc));
+	ioc.cmd = cmd;
+	ioc.buf = arg;
+	ioc.len = len;
+	strcpy(ifr.ifr_name, dev->name);
+	ifr.ifr_data = (caddr_t)&ioc;
+
+	fs = get_fs();
+	set_fs(get_ds());
+	err = dev->netdev_ops->ndo_do_ioctl(dev, &ifr, SIOCDEVPRIVATE);
+	set_fs(fs);
+
+	return err;
+}
+
+static s32
+brcmf_cfg80211_change_iface(struct wiphy *wiphy, struct net_device *ndev,
+			 enum nl80211_iftype type, u32 *flags,
+			 struct vif_params *params)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct wireless_dev *wdev;
+	s32 infra = 0;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	switch (type) {
+	case NL80211_IFTYPE_MONITOR:
+	case NL80211_IFTYPE_WDS:
+		WL_ERR("type (%d) : currently we do not support this type\n",
+		       type);
+		return -EOPNOTSUPP;
+	case NL80211_IFTYPE_ADHOC:
+		cfg_priv->conf->mode = WL_MODE_IBSS;
+		infra = 0;
+		break;
+	case NL80211_IFTYPE_STATION:
+		cfg_priv->conf->mode = WL_MODE_BSS;
+		infra = 1;
+		break;
+	default:
+		err = -EINVAL;
+		goto done;
+	}
+
+	infra = cpu_to_le32(infra);
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_INFRA, &infra, sizeof(infra));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_INFRA error (%d)\n", err);
+		err = -EAGAIN;
+	} else {
+		wdev = ndev->ieee80211_ptr;
+		wdev->iftype = type;
+	}
+
+	WL_INFO("IF Type = %s\n",
+		(cfg_priv->conf->mode == WL_MODE_IBSS) ? "Adhoc" : "Infra");
+
+done:
+	WL_TRACE("Exit\n");
+
+	return err;
+}
+
+static void wl_iscan_prep(struct brcmf_scan_params *params,
+			  struct brcmf_ssid *ssid)
+{
+	memcpy(params->bssid, ether_bcast, ETH_ALEN);
+	params->bss_type = DOT11_BSSTYPE_ANY;
+	params->scan_type = 0;
+	params->nprobes = -1;
+	params->active_time = -1;
+	params->passive_time = -1;
+	params->home_time = -1;
+	params->channel_num = 0;
+
+	params->nprobes = cpu_to_le32(params->nprobes);
+	params->active_time = cpu_to_le32(params->active_time);
+	params->passive_time = cpu_to_le32(params->passive_time);
+	params->home_time = cpu_to_le32(params->home_time);
+	if (ssid && ssid->SSID_len)
+		memcpy(&params->ssid, ssid, sizeof(struct brcmf_ssid));
+
+}
+
+static s32
+brcmf_dev_iovar_setbuf(struct net_device *dev, s8 * iovar, void *param,
+		    s32 paramlen, void *bufptr, s32 buflen)
+{
+	s32 iolen;
+
+	iolen = brcmu_mkiovar(iovar, param, paramlen, bufptr, buflen);
+	BUG_ON(!iolen);
+
+	return brcmf_dev_ioctl(dev, BRCMF_C_SET_VAR, bufptr, iolen);
+}
+
+static s32
+brcmf_dev_iovar_getbuf(struct net_device *dev, s8 * iovar, void *param,
+		    s32 paramlen, void *bufptr, s32 buflen)
+{
+	s32 iolen;
+
+	iolen = brcmu_mkiovar(iovar, param, paramlen, bufptr, buflen);
+	BUG_ON(!iolen);
+
+	return brcmf_dev_ioctl(dev, BRCMF_C_GET_VAR, bufptr, buflen);
+}
+
+static s32
+brcmf_run_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan,
+		struct brcmf_ssid *ssid, u16 action)
+{
+	s32 params_size = (BRCMF_SCAN_PARAMS_FIXED_SIZE +
+				offsetof(struct brcmf_iscan_params, params));
+	struct brcmf_iscan_params *params;
+	s32 err = 0;
+
+	if (ssid && ssid->SSID_len)
+		params_size += sizeof(struct brcmf_ssid);
+	params = kzalloc(params_size, GFP_KERNEL);
+	if (unlikely(!params))
+		return -ENOMEM;
+	BUG_ON(params_size >= BRCMF_C_IOCTL_SMLEN);
+
+	wl_iscan_prep(&params->params, ssid);
+
+	params->version = cpu_to_le32(BRCMF_ISCAN_REQ_VERSION);
+	params->action = cpu_to_le16(action);
+	params->scan_duration = cpu_to_le16(0);
+
+	/* params_size += offsetof(struct brcmf_iscan_params, params); */
+	err = brcmf_dev_iovar_setbuf(iscan->dev, "iscan", params, params_size,
+				iscan->ioctl_buf, BRCMF_C_IOCTL_SMLEN);
+	if (unlikely(err)) {
+		if (err == -EBUSY) {
+			WL_INFO("system busy : iscan canceled\n");
+		} else {
+			WL_ERR("error (%d)\n", err);
+		}
+	}
+	kfree(params);
+	return err;
+}
+
+static s32 brcmf_do_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+	struct brcmf_ssid ssid;
+	s32 passive_scan;
+	s32 err = 0;
+
+	/* Broadcast scan by default */
+	memset(&ssid, 0, sizeof(ssid));
+
+	iscan->state = WL_ISCAN_STATE_SCANING;
+
+	passive_scan = cfg_priv->active_scan ? 0 : 1;
+	err = brcmf_dev_ioctl(cfg_to_ndev(cfg_priv), BRCMF_C_SET_PASSIVE_SCAN,
+			&passive_scan, sizeof(passive_scan));
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		return err;
+	}
+	brcmf_set_mpc(ndev, 0);
+	cfg_priv->iscan_kickstart = true;
+	brcmf_run_iscan(iscan, &ssid, BRCMF_SCAN_ACTION_START);
+	mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+	iscan->timer_on = 1;
+
+	return err;
+}
+
+static s32
+__brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+		   struct cfg80211_scan_request *request,
+		   struct cfg80211_ssid *this_ssid)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+	struct cfg80211_ssid *ssids;
+	struct brcmf_cfg80211_scan_req *sr = cfg_priv->scan_req_int;
+	s32 passive_scan;
+	bool iscan_req;
+	bool spec_scan;
+	s32 err = 0;
+
+	if (unlikely(test_bit(WL_STATUS_SCANNING, &cfg_priv->status))) {
+		WL_ERR("Scanning already : status (%lu)\n", cfg_priv->status);
+		return -EAGAIN;
+	}
+	if (unlikely(test_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status))) {
+		WL_ERR("Scanning being aborted : status (%lu)\n",
+		       cfg_priv->status);
+		return -EAGAIN;
+	}
+	if (test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
+		WL_ERR("Connecting : status (%lu)\n",
+		       cfg_priv->status);
+		return -EAGAIN;
+	}
+
+	iscan_req = false;
+	spec_scan = false;
+	if (request) {
+		/* scan bss */
+		ssids = request->ssids;
+		if (cfg_priv->iscan_on && (!ssids || !ssids->ssid_len))
+			iscan_req = true;
+	} else {
+		/* scan in ibss */
+		/* we don't do iscan in ibss */
+		ssids = this_ssid;
+	}
+
+	cfg_priv->scan_request = request;
+	set_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+	if (iscan_req) {
+		err = brcmf_do_iscan(cfg_priv);
+		if (likely(!err))
+			return err;
+		else
+			goto scan_out;
+	} else {
+		WL_SCAN("ssid \"%s\", ssid_len (%d)\n",
+		       ssids->ssid, ssids->ssid_len);
+		memset(&sr->ssid, 0, sizeof(sr->ssid));
+		sr->ssid.SSID_len =
+			    min_t(u8, sizeof(sr->ssid.SSID), ssids->ssid_len);
+		if (sr->ssid.SSID_len) {
+			memcpy(sr->ssid.SSID, ssids->ssid, sr->ssid.SSID_len);
+			sr->ssid.SSID_len = cpu_to_le32(sr->ssid.SSID_len);
+			spec_scan = true;
+		} else {
+			WL_SCAN("Broadcast scan\n");
+		}
+
+		passive_scan = cfg_priv->active_scan ? 0 : 1;
+		err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_PASSIVE_SCAN,
+				&passive_scan, sizeof(passive_scan));
+		if (unlikely(err)) {
+			WL_ERR("WLC_SET_PASSIVE_SCAN error (%d)\n", err);
+			goto scan_out;
+		}
+		brcmf_set_mpc(ndev, 0);
+		err = brcmf_dev_ioctl(ndev, BRCMF_C_SCAN, &sr->ssid,
+				sizeof(sr->ssid));
+		if (err) {
+			if (err == -EBUSY) {
+				WL_INFO("system busy : scan for \"%s\" canceled\n",
+					sr->ssid.SSID);
+			} else {
+				WL_ERR("WLC_SCAN error (%d)\n", err);
+			}
+			brcmf_set_mpc(ndev, 1);
+			goto scan_out;
+		}
+	}
+
+	return 0;
+
+scan_out:
+	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+	cfg_priv->scan_request = NULL;
+	return err;
+}
+
+static s32
+brcmf_cfg80211_scan(struct wiphy *wiphy, struct net_device *ndev,
+		 struct cfg80211_scan_request *request)
+{
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+
+	CHECK_SYS_UP();
+
+	err = __brcmf_cfg80211_scan(wiphy, ndev, request, NULL);
+	if (unlikely(err))
+		WL_ERR("scan error (%d)\n", err);
+
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32 brcmf_dev_intvar_set(struct net_device *dev, s8 *name, s32 val)
+{
+	s8 buf[BRCMF_C_IOCTL_SMLEN];
+	u32 len;
+	s32 err = 0;
+
+	val = cpu_to_le32(val);
+	len = brcmu_mkiovar(name, (char *)(&val), sizeof(val), buf,
+			    sizeof(buf));
+	BUG_ON(!len);
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_VAR, buf, len);
+	if (unlikely(err))
+		WL_ERR("error (%d)\n", err);
+
+	return err;
+}
+
+static s32
+brcmf_dev_intvar_get(struct net_device *dev, s8 *name, s32 *retval)
+{
+	union {
+		s8 buf[BRCMF_C_IOCTL_SMLEN];
+		s32 val;
+	} var;
+	u32 len;
+	u32 data_null;
+	s32 err = 0;
+
+	len =
+	    brcmu_mkiovar(name, (char *)(&data_null), 0, (char *)(&var),
+			sizeof(var.buf));
+	BUG_ON(!len);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_VAR, &var, len);
+	if (unlikely(err))
+		WL_ERR("error (%d)\n", err);
+
+	*retval = le32_to_cpu(var.val);
+
+	return err;
+}
+
+static s32 brcmf_set_rts(struct net_device *dev, u32 rts_threshold)
+{
+	s32 err = 0;
+
+	err = brcmf_dev_intvar_set(dev, "rtsthresh", rts_threshold);
+	if (unlikely(err))
+		WL_ERR("Error (%d)\n", err);
+
+	return err;
+}
+
+static s32 brcmf_set_frag(struct net_device *dev, u32 frag_threshold)
+{
+	s32 err = 0;
+
+	err = brcmf_dev_intvar_set(dev, "fragthresh", frag_threshold);
+	if (unlikely(err))
+		WL_ERR("Error (%d)\n", err);
+
+	return err;
+}
+
+static s32 brcmf_set_retry(struct net_device *dev, u32 retry, bool l)
+{
+	s32 err = 0;
+	u32 cmd = (l ? BRCM_SET_LRL : BRCM_SET_SRL);
+
+	retry = cpu_to_le32(retry);
+	err = brcmf_dev_ioctl(dev, cmd, &retry, sizeof(retry));
+	if (unlikely(err)) {
+		WL_ERR("cmd (%d) , error (%d)\n", cmd, err);
+		return err;
+	}
+	return err;
+}
+
+static s32 brcmf_cfg80211_set_wiphy_params(struct wiphy *wiphy, u32 changed)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	if (changed & WIPHY_PARAM_RTS_THRESHOLD &&
+	    (cfg_priv->conf->rts_threshold != wiphy->rts_threshold)) {
+		cfg_priv->conf->rts_threshold = wiphy->rts_threshold;
+		err = brcmf_set_rts(ndev, cfg_priv->conf->rts_threshold);
+		if (!err)
+			goto done;
+	}
+	if (changed & WIPHY_PARAM_FRAG_THRESHOLD &&
+	    (cfg_priv->conf->frag_threshold != wiphy->frag_threshold)) {
+		cfg_priv->conf->frag_threshold = wiphy->frag_threshold;
+		err = brcmf_set_frag(ndev, cfg_priv->conf->frag_threshold);
+		if (!err)
+			goto done;
+	}
+	if (changed & WIPHY_PARAM_RETRY_LONG
+	    && (cfg_priv->conf->retry_long != wiphy->retry_long)) {
+		cfg_priv->conf->retry_long = wiphy->retry_long;
+		err = brcmf_set_retry(ndev, cfg_priv->conf->retry_long, true);
+		if (!err)
+			goto done;
+	}
+	if (changed & WIPHY_PARAM_RETRY_SHORT
+	    && (cfg_priv->conf->retry_short != wiphy->retry_short)) {
+		cfg_priv->conf->retry_short = wiphy->retry_short;
+		err = brcmf_set_retry(ndev, cfg_priv->conf->retry_short, false);
+		if (!err)
+			goto done;
+	}
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_join_ibss(struct wiphy *wiphy, struct net_device *dev,
+		      struct cfg80211_ibss_params *params)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct brcmf_join_params join_params;
+	size_t join_params_size = 0;
+	s32 err = 0;
+	s32 wsec = 0;
+	s32 bcnprd;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	if (params->ssid)
+		WL_CONN("SSID: %s\n", params->ssid);
+	else {
+		WL_CONN("SSID: NULL, Not supported\n");
+		return -EOPNOTSUPP;
+	}
+
+	set_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+
+	if (params->bssid)
+		WL_CONN("BSSID: %02X %02X %02X %02X %02X %02X\n",
+		params->bssid[0], params->bssid[1], params->bssid[2],
+		params->bssid[3], params->bssid[4], params->bssid[5]);
+	else
+		WL_CONN("No BSSID specified\n");
+
+	if (params->channel)
+		WL_CONN("channel: %d\n", params->channel->center_freq);
+	else
+		WL_CONN("no channel specified\n");
+
+	if (params->channel_fixed)
+		WL_CONN("fixed channel required\n");
+	else
+		WL_CONN("no fixed channel required\n");
+
+	if (params->ie && params->ie_len)
+		WL_CONN("ie len: %d\n", params->ie_len);
+	else
+		WL_CONN("no ie specified\n");
+
+	if (params->beacon_interval)
+		WL_CONN("beacon interval: %d\n", params->beacon_interval);
+	else
+		WL_CONN("no beacon interval specified\n");
+
+	if (params->basic_rates)
+		WL_CONN("basic rates: %08X\n", params->basic_rates);
+	else
+		WL_CONN("no basic rates specified\n");
+
+	if (params->privacy)
+		WL_CONN("privacy required\n");
+	else
+		WL_CONN("no privacy required\n");
+
+	/* Configure Privacy for starter */
+	if (params->privacy)
+		wsec |= WEP_ENABLED;
+
+	err = brcmf_dev_intvar_set(dev, "wsec", wsec);
+	if (unlikely(err)) {
+		WL_ERR("wsec failed (%d)\n", err);
+		goto done;
+	}
+
+	/* Configure Beacon Interval for starter */
+	if (params->beacon_interval)
+		bcnprd = cpu_to_le32(params->beacon_interval);
+	else
+		bcnprd = cpu_to_le32(100);
+
+	err = brcmf_dev_ioctl(dev, BRCM_SET_BCNPRD, &bcnprd, sizeof(bcnprd));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_BCNPRD failed (%d)\n", err);
+		goto done;
+	}
+
+	/* Configure required join parameter */
+	memset(&join_params, 0, sizeof(struct brcmf_join_params));
+
+	/* SSID */
+	join_params.ssid.SSID_len =
+			(params->ssid_len > 32) ? 32 : params->ssid_len;
+	memcpy(join_params.ssid.SSID, params->ssid, join_params.ssid.SSID_len);
+	join_params.ssid.SSID_len = cpu_to_le32(join_params.ssid.SSID_len);
+	join_params_size = sizeof(join_params.ssid);
+	brcmf_update_prof(cfg_priv, NULL, &join_params.ssid, WL_PROF_SSID);
+
+	/* BSSID */
+	if (params->bssid) {
+		memcpy(join_params.params.bssid, params->bssid, ETH_ALEN);
+		join_params_size = sizeof(join_params.ssid) +
+					BRCMF_ASSOC_PARAMS_FIXED_SIZE;
+	} else {
+		memcpy(join_params.params.bssid, ether_bcast, ETH_ALEN);
+	}
+	brcmf_update_prof(cfg_priv, NULL,
+			  &join_params.params.bssid, WL_PROF_BSSID);
+
+	/* Channel */
+	if (params->channel) {
+		u32 target_channel;
+
+		cfg_priv->channel =
+			ieee80211_frequency_to_channel(
+				params->channel->center_freq);
+		if (params->channel_fixed) {
+			/* adding chanspec */
+			brcmf_ch_to_chanspec(cfg_priv->channel,
+				&join_params, &join_params_size);
+		}
+
+		/* set channel for starter */
+		target_channel = cpu_to_le32(cfg_priv->channel);
+		err = brcmf_dev_ioctl(dev, BRCM_SET_CHANNEL,
+			&target_channel, sizeof(target_channel));
+		if (unlikely(err)) {
+			WL_ERR("WLC_SET_CHANNEL failed (%d)\n", err);
+			goto done;
+		}
+	} else
+		cfg_priv->channel = 0;
+
+	cfg_priv->ibss_starter = false;
+
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_SSID,
+			   &join_params, join_params_size);
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_SSID failed (%d)\n", err);
+		goto done;
+	}
+
+done:
+	if (err)
+		clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32 brcmf_cfg80211_leave_ibss(struct wiphy *wiphy, struct net_device *dev)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	brcmf_link_down(cfg_priv);
+
+	WL_TRACE("Exit\n");
+
+	return err;
+}
+
+static s32
+brcmf_set_wpa_version(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	struct brcmf_cfg80211_security *sec;
+	s32 val = 0;
+	s32 err = 0;
+
+	if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_1)
+		val = WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED;
+	else if (sme->crypto.wpa_versions & NL80211_WPA_VERSION_2)
+		val = WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED;
+	else
+		val = WPA_AUTH_DISABLED;
+	WL_CONN("setting wpa_auth to 0x%0x\n", val);
+	err = brcmf_dev_intvar_set(dev, "wpa_auth", val);
+	if (unlikely(err)) {
+		WL_ERR("set wpa_auth failed (%d)\n", err);
+		return err;
+	}
+	sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+	sec->wpa_versions = sme->crypto.wpa_versions;
+	return err;
+}
+
+static s32
+brcmf_set_auth_type(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	struct brcmf_cfg80211_security *sec;
+	s32 val = 0;
+	s32 err = 0;
+
+	switch (sme->auth_type) {
+	case NL80211_AUTHTYPE_OPEN_SYSTEM:
+		val = 0;
+		WL_CONN("open system\n");
+		break;
+	case NL80211_AUTHTYPE_SHARED_KEY:
+		val = 1;
+		WL_CONN("shared key\n");
+		break;
+	case NL80211_AUTHTYPE_AUTOMATIC:
+		val = 2;
+		WL_CONN("automatic\n");
+		break;
+	case NL80211_AUTHTYPE_NETWORK_EAP:
+		WL_CONN("network eap\n");
+	default:
+		val = 2;
+		WL_ERR("invalid auth type (%d)\n", sme->auth_type);
+		break;
+	}
+
+	err = brcmf_dev_intvar_set(dev, "auth", val);
+	if (unlikely(err)) {
+		WL_ERR("set auth failed (%d)\n", err);
+		return err;
+	}
+	sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+	sec->auth_type = sme->auth_type;
+	return err;
+}
+
+static s32
+brcmf_set_set_cipher(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	struct brcmf_cfg80211_security *sec;
+	s32 pval = 0;
+	s32 gval = 0;
+	s32 err = 0;
+
+	if (sme->crypto.n_ciphers_pairwise) {
+		switch (sme->crypto.ciphers_pairwise[0]) {
+		case WLAN_CIPHER_SUITE_WEP40:
+		case WLAN_CIPHER_SUITE_WEP104:
+			pval = WEP_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			pval = TKIP_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			pval = AES_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			pval = AES_ENABLED;
+			break;
+		default:
+			WL_ERR("invalid cipher pairwise (%d)\n",
+			       sme->crypto.ciphers_pairwise[0]);
+			return -EINVAL;
+		}
+	}
+	if (sme->crypto.cipher_group) {
+		switch (sme->crypto.cipher_group) {
+		case WLAN_CIPHER_SUITE_WEP40:
+		case WLAN_CIPHER_SUITE_WEP104:
+			gval = WEP_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			gval = TKIP_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			gval = AES_ENABLED;
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			gval = AES_ENABLED;
+			break;
+		default:
+			WL_ERR("invalid cipher group (%d)\n",
+			       sme->crypto.cipher_group);
+			return -EINVAL;
+		}
+	}
+
+	WL_CONN("pval (%d) gval (%d)\n", pval, gval);
+	err = brcmf_dev_intvar_set(dev, "wsec", pval | gval);
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		return err;
+	}
+
+	sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+	sec->cipher_pairwise = sme->crypto.ciphers_pairwise[0];
+	sec->cipher_group = sme->crypto.cipher_group;
+
+	return err;
+}
+
+static s32
+brcmf_set_key_mgmt(struct net_device *dev, struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	struct brcmf_cfg80211_security *sec;
+	s32 val = 0;
+	s32 err = 0;
+
+	if (sme->crypto.n_akm_suites) {
+		err = brcmf_dev_intvar_get(dev, "wpa_auth", &val);
+		if (unlikely(err)) {
+			WL_ERR("could not get wpa_auth (%d)\n", err);
+			return err;
+		}
+		if (val & (WPA_AUTH_PSK | WPA_AUTH_UNSPECIFIED)) {
+			switch (sme->crypto.akm_suites[0]) {
+			case WLAN_AKM_SUITE_8021X:
+				val = WPA_AUTH_UNSPECIFIED;
+				break;
+			case WLAN_AKM_SUITE_PSK:
+				val = WPA_AUTH_PSK;
+				break;
+			default:
+				WL_ERR("invalid cipher group (%d)\n",
+				       sme->crypto.cipher_group);
+				return -EINVAL;
+			}
+		} else if (val & (WPA2_AUTH_PSK | WPA2_AUTH_UNSPECIFIED)) {
+			switch (sme->crypto.akm_suites[0]) {
+			case WLAN_AKM_SUITE_8021X:
+				val = WPA2_AUTH_UNSPECIFIED;
+				break;
+			case WLAN_AKM_SUITE_PSK:
+				val = WPA2_AUTH_PSK;
+				break;
+			default:
+				WL_ERR("invalid cipher group (%d)\n",
+				       sme->crypto.cipher_group);
+				return -EINVAL;
+			}
+		}
+
+		WL_CONN("setting wpa_auth to %d\n", val);
+		err = brcmf_dev_intvar_set(dev, "wpa_auth", val);
+		if (unlikely(err)) {
+			WL_ERR("could not set wpa_auth (%d)\n", err);
+			return err;
+		}
+	}
+	sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+	sec->wpa_auth = sme->crypto.akm_suites[0];
+
+	return err;
+}
+
+static s32
+brcmf_set_set_sharedkey(struct net_device *dev,
+		     struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	struct brcmf_cfg80211_security *sec;
+	struct brcmf_wsec_key key;
+	s32 val;
+	s32 err = 0;
+
+	WL_CONN("key len (%d)\n", sme->key_len);
+	if (sme->key_len) {
+		sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+		WL_CONN("wpa_versions 0x%x cipher_pairwise 0x%x\n",
+		       sec->wpa_versions, sec->cipher_pairwise);
+		if (!
+		    (sec->wpa_versions & (NL80211_WPA_VERSION_1 |
+					  NL80211_WPA_VERSION_2))
+&& (sec->cipher_pairwise & (WLAN_CIPHER_SUITE_WEP40 |
+			    WLAN_CIPHER_SUITE_WEP104))) {
+			memset(&key, 0, sizeof(key));
+			key.len = (u32) sme->key_len;
+			key.index = (u32) sme->key_idx;
+			if (unlikely(key.len > sizeof(key.data))) {
+				WL_ERR("Too long key length (%u)\n", key.len);
+				return -EINVAL;
+			}
+			memcpy(key.data, sme->key, key.len);
+			key.flags = BRCMF_PRIMARY_KEY;
+			switch (sec->cipher_pairwise) {
+			case WLAN_CIPHER_SUITE_WEP40:
+				key.algo = CRYPTO_ALGO_WEP1;
+				break;
+			case WLAN_CIPHER_SUITE_WEP104:
+				key.algo = CRYPTO_ALGO_WEP128;
+				break;
+			default:
+				WL_ERR("Invalid algorithm (%d)\n",
+				       sme->crypto.ciphers_pairwise[0]);
+				return -EINVAL;
+			}
+			/* Set the new key/index */
+			WL_CONN("key length (%d) key index (%d) algo (%d)\n",
+			       key.len, key.index, key.algo);
+			WL_CONN("key \"%s\"\n", key.data);
+			swap_key_from_BE(&key);
+			err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY, &key,
+					sizeof(key));
+			if (unlikely(err)) {
+				WL_ERR("WLC_SET_KEY error (%d)\n", err);
+				return err;
+			}
+			if (sec->auth_type == NL80211_AUTHTYPE_OPEN_SYSTEM) {
+				WL_CONN("set auth_type to shared key\n");
+				val = 1;	/* shared key */
+				err = brcmf_dev_intvar_set(dev, "auth", val);
+				if (unlikely(err)) {
+					WL_ERR("set auth failed (%d)\n", err);
+					return err;
+				}
+			}
+		}
+	}
+	return err;
+}
+
+static s32
+brcmf_cfg80211_connect(struct wiphy *wiphy, struct net_device *dev,
+		    struct cfg80211_connect_params *sme)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct ieee80211_channel *chan = sme->channel;
+	struct brcmf_join_params join_params;
+	size_t join_params_size;
+
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	if (unlikely(!sme->ssid)) {
+		WL_ERR("Invalid ssid\n");
+		return -EOPNOTSUPP;
+	}
+
+	set_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+
+	if (chan) {
+		cfg_priv->channel =
+			ieee80211_frequency_to_channel(chan->center_freq);
+		WL_CONN("channel (%d), center_req (%d)\n",
+				cfg_priv->channel, chan->center_freq);
+	} else
+		cfg_priv->channel = 0;
+
+	WL_INFO("ie (%p), ie_len (%zd)\n", sme->ie, sme->ie_len);
+
+	err = brcmf_set_wpa_version(dev, sme);
+	if (err) {
+		WL_ERR("wl_set_wpa_version failed (%d)\n", err);
+		goto done;
+	}
+
+	err = brcmf_set_auth_type(dev, sme);
+	if (err) {
+		WL_ERR("wl_set_auth_type failed (%d)\n", err);
+		goto done;
+	}
+
+	err = brcmf_set_set_cipher(dev, sme);
+	if (err) {
+		WL_ERR("wl_set_set_cipher failed (%d)\n", err);
+		goto done;
+	}
+
+	err = brcmf_set_key_mgmt(dev, sme);
+	if (err) {
+		WL_ERR("wl_set_key_mgmt failed (%d)\n", err);
+		goto done;
+	}
+
+	err = brcmf_set_set_sharedkey(dev, sme);
+	if (err) {
+		WL_ERR("wl_set_set_sharedkey failed (%d)\n", err);
+		goto done;
+	}
+
+	brcmf_update_prof(cfg_priv, NULL, sme->bssid, WL_PROF_BSSID);
+	/*
+	 **  Join with specific BSSID and cached SSID
+	 **  If SSID is zero join based on BSSID only
+	 */
+	memset(&join_params, 0, sizeof(join_params));
+	join_params_size = sizeof(join_params.ssid);
+
+	join_params.ssid.SSID_len = min(sizeof(join_params.ssid.SSID), sme->ssid_len);
+	memcpy(&join_params.ssid.SSID, sme->ssid, join_params.ssid.SSID_len);
+	join_params.ssid.SSID_len = cpu_to_le32(join_params.ssid.SSID_len);
+	brcmf_update_prof(cfg_priv, NULL, &join_params.ssid, WL_PROF_SSID);
+
+	if (sme->bssid)
+		memcpy(join_params.params.bssid, sme->bssid, ETH_ALEN);
+	else
+		memcpy(join_params.params.bssid, ether_bcast, ETH_ALEN);
+
+	if (join_params.ssid.SSID_len < IEEE80211_MAX_SSID_LEN) {
+		WL_CONN("ssid \"%s\", len (%d)\n",
+		       join_params.ssid.SSID, join_params.ssid.SSID_len);
+	}
+
+	brcmf_ch_to_chanspec(cfg_priv->channel,
+			     &join_params, &join_params_size);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_SSID,
+			   &join_params, join_params_size);
+	if (err)
+		WL_ERR("WLC_SET_SSID failed (%d)\n", err);
+
+done:
+	if (err)
+		clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_disconnect(struct wiphy *wiphy, struct net_device *dev,
+		       u16 reason_code)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct brcmf_scb_val scbval;
+	s32 err = 0;
+
+	WL_TRACE("Enter. Reason code = %d\n", reason_code);
+	CHECK_SYS_UP();
+
+	clear_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+
+	scbval.val = reason_code;
+	memcpy(&scbval.ea, brcmf_read_prof(cfg_priv, WL_PROF_BSSID), ETH_ALEN);
+	scbval.val = cpu_to_le32(scbval.val);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_DISASSOC, &scbval,
+			sizeof(struct brcmf_scb_val));
+	if (unlikely(err))
+		WL_ERR("error (%d)\n", err);
+
+	cfg_priv->link_up = false;
+
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_set_tx_power(struct wiphy *wiphy,
+			 enum nl80211_tx_power_setting type, s32 dbm)
+{
+
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+	u16 txpwrmw;
+	s32 err = 0;
+	s32 disable = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	switch (type) {
+	case NL80211_TX_POWER_AUTOMATIC:
+		break;
+	case NL80211_TX_POWER_LIMITED:
+		if (dbm < 0) {
+			WL_ERR("TX_POWER_LIMITED - dbm is negative\n");
+			err = -EINVAL;
+			goto done;
+		}
+		break;
+	case NL80211_TX_POWER_FIXED:
+		if (dbm < 0) {
+			WL_ERR("TX_POWER_FIXED - dbm is negative\n");
+			err = -EINVAL;
+			goto done;
+		}
+		break;
+	}
+	/* Make sure radio is off or on as far as software is concerned */
+	disable = WL_RADIO_SW_DISABLE << 16;
+	disable = cpu_to_le32(disable);
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_RADIO, &disable, sizeof(disable));
+	if (unlikely(err))
+		WL_ERR("WLC_SET_RADIO error (%d)\n", err);
+
+	if (dbm > 0xffff)
+		txpwrmw = 0xffff;
+	else
+		txpwrmw = (u16) dbm;
+	err = brcmf_dev_intvar_set(ndev, "qtxpower",
+			(s32) (brcmu_mw_to_qdbm(txpwrmw)));
+	if (unlikely(err))
+		WL_ERR("qtxpower error (%d)\n", err);
+	cfg_priv->conf->tx_power = dbm;
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32 brcmf_cfg80211_get_tx_power(struct wiphy *wiphy, s32 *dbm)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+	s32 txpwrdbm;
+	u8 result;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	err = brcmf_dev_intvar_get(ndev, "qtxpower", &txpwrdbm);
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		goto done;
+	}
+
+	result = (u8) (txpwrdbm & ~WL_TXPWR_OVERRIDE);
+	*dbm = (s32) brcmu_qdbm_to_mw(result);
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_key(struct wiphy *wiphy, struct net_device *dev,
+			       u8 key_idx, bool unicast, bool multicast)
+{
+	u32 index;
+	s32 wsec;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	WL_CONN("key index (%d)\n", key_idx);
+	CHECK_SYS_UP();
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_WSEC, &wsec, sizeof(wsec));
+	if (unlikely(err)) {
+		WL_ERR("WLC_GET_WSEC error (%d)\n", err);
+		goto done;
+	}
+
+	wsec = le32_to_cpu(wsec);
+	if (wsec & WEP_ENABLED) {
+		/* Just select a new current key */
+		index = (u32) key_idx;
+		index = cpu_to_le32(index);
+		err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY_PRIMARY, &index,
+				sizeof(index));
+		if (unlikely(err))
+			WL_ERR("error (%d)\n", err);
+	}
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_add_keyext(struct wiphy *wiphy, struct net_device *dev,
+	      u8 key_idx, const u8 *mac_addr, struct key_params *params)
+{
+	struct brcmf_wsec_key key;
+	s32 err = 0;
+
+	memset(&key, 0, sizeof(key));
+	key.index = (u32) key_idx;
+	/* Instead of bcast for ea address for default wep keys,
+		 driver needs it to be Null */
+	if (!is_multicast_ether_addr(mac_addr))
+		memcpy((char *)&key.ea, (void *)mac_addr, ETH_ALEN);
+	key.len = (u32) params->key_len;
+	/* check for key index change */
+	if (key.len == 0) {
+		/* key delete */
+		swap_key_from_BE(&key);
+		err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY, &key, sizeof(key));
+		if (unlikely(err)) {
+			WL_ERR("key delete error (%d)\n", err);
+			return err;
+		}
+	} else {
+		if (key.len > sizeof(key.data)) {
+			WL_ERR("Invalid key length (%d)\n", key.len);
+			return -EINVAL;
+		}
+
+		WL_CONN("Setting the key index %d\n", key.index);
+		memcpy(key.data, params->key, key.len);
+
+		if (params->cipher == WLAN_CIPHER_SUITE_TKIP) {
+			u8 keybuf[8];
+			memcpy(keybuf, &key.data[24], sizeof(keybuf));
+			memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+			memcpy(&key.data[16], keybuf, sizeof(keybuf));
+		}
+
+		/* if IW_ENCODE_EXT_RX_SEQ_VALID set */
+		if (params->seq && params->seq_len == 6) {
+			/* rx iv */
+			u8 *ivptr;
+			ivptr = (u8 *) params->seq;
+			key.rxiv.hi = (ivptr[5] << 24) | (ivptr[4] << 16) |
+			    (ivptr[3] << 8) | ivptr[2];
+			key.rxiv.lo = (ivptr[1] << 8) | ivptr[0];
+			key.iv_initialized = true;
+		}
+
+		switch (params->cipher) {
+		case WLAN_CIPHER_SUITE_WEP40:
+			key.algo = CRYPTO_ALGO_WEP1;
+			WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+			break;
+		case WLAN_CIPHER_SUITE_WEP104:
+			key.algo = CRYPTO_ALGO_WEP128;
+			WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+			break;
+		case WLAN_CIPHER_SUITE_TKIP:
+			key.algo = CRYPTO_ALGO_TKIP;
+			WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+			break;
+		case WLAN_CIPHER_SUITE_AES_CMAC:
+			key.algo = CRYPTO_ALGO_AES_CCM;
+			WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+			break;
+		case WLAN_CIPHER_SUITE_CCMP:
+			key.algo = CRYPTO_ALGO_AES_CCM;
+			WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
+			break;
+		default:
+			WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
+			return -EINVAL;
+		}
+		swap_key_from_BE(&key);
+
+		brcmf_netdev_wait_pend8021x(dev);
+		err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY, &key, sizeof(key));
+		if (unlikely(err)) {
+			WL_ERR("WLC_SET_KEY error (%d)\n", err);
+			return err;
+		}
+	}
+	return err;
+}
+
+static s32
+brcmf_cfg80211_add_key(struct wiphy *wiphy, struct net_device *dev,
+		    u8 key_idx, bool pairwise, const u8 *mac_addr,
+		    struct key_params *params)
+{
+	struct brcmf_wsec_key key;
+	s32 val;
+	s32 wsec;
+	s32 err = 0;
+	u8 keybuf[8];
+
+	WL_TRACE("Enter\n");
+	WL_CONN("key index (%d)\n", key_idx);
+	CHECK_SYS_UP();
+
+	if (mac_addr) {
+		WL_TRACE("Exit");
+		return brcmf_add_keyext(wiphy, dev, key_idx, mac_addr, params);
+	}
+	memset(&key, 0, sizeof(key));
+
+	key.len = (u32) params->key_len;
+	key.index = (u32) key_idx;
+
+	if (unlikely(key.len > sizeof(key.data))) {
+		WL_ERR("Too long key length (%u)\n", key.len);
+		err = -EINVAL;
+		goto done;
+	}
+	memcpy(key.data, params->key, key.len);
+
+	key.flags = BRCMF_PRIMARY_KEY;
+	switch (params->cipher) {
+	case WLAN_CIPHER_SUITE_WEP40:
+		key.algo = CRYPTO_ALGO_WEP1;
+		WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+		break;
+	case WLAN_CIPHER_SUITE_WEP104:
+		key.algo = CRYPTO_ALGO_WEP128;
+		WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+		break;
+	case WLAN_CIPHER_SUITE_TKIP:
+		memcpy(keybuf, &key.data[24], sizeof(keybuf));
+		memcpy(&key.data[24], &key.data[16], sizeof(keybuf));
+		memcpy(&key.data[16], keybuf, sizeof(keybuf));
+		key.algo = CRYPTO_ALGO_TKIP;
+		WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+		break;
+	case WLAN_CIPHER_SUITE_AES_CMAC:
+		key.algo = CRYPTO_ALGO_AES_CCM;
+		WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+		break;
+	case WLAN_CIPHER_SUITE_CCMP:
+		key.algo = CRYPTO_ALGO_AES_CCM;
+		WL_CONN("WLAN_CIPHER_SUITE_CCMP\n");
+		break;
+	default:
+		WL_ERR("Invalid cipher (0x%x)\n", params->cipher);
+		err = -EINVAL;
+		goto done;
+	}
+
+	/* Set the new key/index */
+	swap_key_from_BE(&key);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY, &key, sizeof(key));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_KEY error (%d)\n", err);
+		goto done;
+	}
+
+	val = WEP_ENABLED;
+	err = brcmf_dev_intvar_get(dev, "wsec", &wsec);
+	if (unlikely(err)) {
+		WL_ERR("get wsec error (%d)\n", err);
+		goto done;
+	}
+	wsec &= ~(WEP_ENABLED);
+	wsec |= val;
+	err = brcmf_dev_intvar_set(dev, "wsec", wsec);
+	if (unlikely(err)) {
+		WL_ERR("set wsec error (%d)\n", err);
+		goto done;
+	}
+
+	val = 1;		/* assume shared key. otherwise 0 */
+	val = cpu_to_le32(val);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_AUTH, &val, sizeof(val));
+	if (unlikely(err))
+		WL_ERR("WLC_SET_AUTH error (%d)\n", err);
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_del_key(struct wiphy *wiphy, struct net_device *dev,
+		    u8 key_idx, bool pairwise, const u8 *mac_addr)
+{
+	struct brcmf_wsec_key key;
+	s32 err = 0;
+	s32 val;
+	s32 wsec;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+	memset(&key, 0, sizeof(key));
+
+	key.index = (u32) key_idx;
+	key.flags = BRCMF_PRIMARY_KEY;
+	key.algo = CRYPTO_ALGO_OFF;
+
+	WL_CONN("key index (%d)\n", key_idx);
+	/* Set the new key/index */
+	swap_key_from_BE(&key);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_KEY, &key, sizeof(key));
+	if (unlikely(err)) {
+		if (err == -EINVAL) {
+			if (key.index >= DOT11_MAX_DEFAULT_KEYS)
+				/* we ignore this key index in this case */
+				WL_ERR("invalid key index (%d)\n", key_idx);
+		} else
+			WL_ERR("WLC_SET_KEY error (%d)\n", err);
+
+		/* Ignore this error, may happen during DISASSOC */
+		err = -EAGAIN;
+		goto done;
+	}
+
+	val = 0;
+	err = brcmf_dev_intvar_get(dev, "wsec", &wsec);
+	if (unlikely(err)) {
+		WL_ERR("get wsec error (%d)\n", err);
+		/* Ignore this error, may happen during DISASSOC */
+		err = -EAGAIN;
+		goto done;
+	}
+	wsec &= ~(WEP_ENABLED);
+	wsec |= val;
+	err = brcmf_dev_intvar_set(dev, "wsec", wsec);
+	if (unlikely(err)) {
+		WL_ERR("set wsec error (%d)\n", err);
+		/* Ignore this error, may happen during DISASSOC */
+		err = -EAGAIN;
+		goto done;
+	}
+
+	val = 0;		/* assume open key. otherwise 1 */
+	val = cpu_to_le32(val);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_AUTH, &val, sizeof(val));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_AUTH error (%d)\n", err);
+		/* Ignore this error, may happen during DISASSOC */
+		err = -EAGAIN;
+	}
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_get_key(struct wiphy *wiphy, struct net_device *dev,
+		    u8 key_idx, bool pairwise, const u8 *mac_addr, void *cookie,
+		    void (*callback) (void *cookie, struct key_params * params))
+{
+	struct key_params params;
+	struct brcmf_wsec_key key;
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct brcmf_cfg80211_security *sec;
+	s32 wsec;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	WL_CONN("key index (%d)\n", key_idx);
+	CHECK_SYS_UP();
+
+	memset(&key, 0, sizeof(key));
+	key.index = key_idx;
+	swap_key_to_BE(&key);
+	memset(&params, 0, sizeof(params));
+	params.key_len = (u8) min_t(u8, WLAN_MAX_KEY_LEN, key.len);
+	memcpy(params.key, key.data, params.key_len);
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_WSEC, &wsec, sizeof(wsec));
+	if (unlikely(err)) {
+		WL_ERR("WLC_GET_WSEC error (%d)\n", err);
+		/* Ignore this error, may happen during DISASSOC */
+		err = -EAGAIN;
+		goto done;
+	}
+	wsec = le32_to_cpu(wsec);
+	switch (wsec) {
+	case WEP_ENABLED:
+		sec = brcmf_read_prof(cfg_priv, WL_PROF_SEC);
+		if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP40) {
+			params.cipher = WLAN_CIPHER_SUITE_WEP40;
+			WL_CONN("WLAN_CIPHER_SUITE_WEP40\n");
+		} else if (sec->cipher_pairwise & WLAN_CIPHER_SUITE_WEP104) {
+			params.cipher = WLAN_CIPHER_SUITE_WEP104;
+			WL_CONN("WLAN_CIPHER_SUITE_WEP104\n");
+		}
+		break;
+	case TKIP_ENABLED:
+		params.cipher = WLAN_CIPHER_SUITE_TKIP;
+		WL_CONN("WLAN_CIPHER_SUITE_TKIP\n");
+		break;
+	case AES_ENABLED:
+		params.cipher = WLAN_CIPHER_SUITE_AES_CMAC;
+		WL_CONN("WLAN_CIPHER_SUITE_AES_CMAC\n");
+		break;
+	default:
+		WL_ERR("Invalid algo (0x%x)\n", wsec);
+		err = -EINVAL;
+		goto done;
+	}
+	callback(cookie, &params);
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_config_default_mgmt_key(struct wiphy *wiphy,
+				    struct net_device *dev, u8 key_idx)
+{
+	WL_INFO("Not supported\n");
+
+	CHECK_SYS_UP();
+	return -EOPNOTSUPP;
+}
+
+static s32
+brcmf_cfg80211_get_station(struct wiphy *wiphy, struct net_device *dev,
+			u8 *mac, struct station_info *sinfo)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct brcmf_scb_val scb_val;
+	int rssi;
+	s32 rate;
+	s32 err = 0;
+	u8 *bssid = brcmf_read_prof(cfg_priv, WL_PROF_BSSID);
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	if (unlikely
+	    (memcmp(mac, bssid, ETH_ALEN))) {
+		WL_ERR("Wrong Mac address cfg_mac-%X:%X:%X:%X:%X:%X"
+			"wl_bssid-%X:%X:%X:%X:%X:%X\n",
+			mac[0], mac[1], mac[2], mac[3], mac[4], mac[5],
+			bssid[0], bssid[1], bssid[2], bssid[3],
+			bssid[4], bssid[5]);
+		err = -ENOENT;
+		goto done;
+	}
+
+	/* Report the current tx rate */
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_RATE, &rate, sizeof(rate));
+	if (err) {
+		WL_ERR("Could not get rate (%d)\n", err);
+	} else {
+		rate = le32_to_cpu(rate);
+		sinfo->filled |= STATION_INFO_TX_BITRATE;
+		sinfo->txrate.legacy = rate * 5;
+		WL_CONN("Rate %d Mbps\n", rate / 2);
+	}
+
+	if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status)) {
+		scb_val.val = 0;
+		err = brcmf_dev_ioctl(dev, BRCMF_C_GET_RSSI, &scb_val,
+				sizeof(struct brcmf_scb_val));
+		if (unlikely(err)) {
+			WL_ERR("Could not get rssi (%d)\n", err);
+		}
+		rssi = le32_to_cpu(scb_val.val);
+		sinfo->filled |= STATION_INFO_SIGNAL;
+		sinfo->signal = rssi;
+		WL_CONN("RSSI %d dBm\n", rssi);
+	}
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_set_power_mgmt(struct wiphy *wiphy, struct net_device *dev,
+			   bool enabled, s32 timeout)
+{
+	s32 pm;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	pm = enabled ? PM_FAST : PM_OFF;
+	pm = cpu_to_le32(pm);
+	WL_INFO("power save %s\n", (pm ? "enabled" : "disabled"));
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_SET_PM, &pm, sizeof(pm));
+	if (unlikely(err)) {
+		if (err == -ENODEV)
+			WL_ERR("net_device is not ready yet\n");
+		else
+			WL_ERR("error (%d)\n", err);
+	}
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static __used u32 brcmf_find_msb(u16 bit16)
+{
+	u32 ret = 0;
+
+	if (bit16 & 0xff00) {
+		ret += 8;
+		bit16 >>= 8;
+	}
+
+	if (bit16 & 0xf0) {
+		ret += 4;
+		bit16 >>= 4;
+	}
+
+	if (bit16 & 0xc) {
+		ret += 2;
+		bit16 >>= 2;
+	}
+
+	if (bit16 & 2)
+		ret += bit16 & 2;
+	else if (bit16)
+		ret += bit16;
+
+	return ret;
+}
+
+static s32
+brcmf_cfg80211_set_bitrate_mask(struct wiphy *wiphy, struct net_device *dev,
+			     const u8 *addr,
+			     const struct cfg80211_bitrate_mask *mask)
+{
+	struct wl_rateset rateset;
+	s32 rate;
+	s32 val;
+	s32 err_bg;
+	s32 err_a;
+	u32 legacy;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	/* addr param is always NULL. ignore it */
+	/* Get current rateset */
+	err = brcmf_dev_ioctl(dev, BRCM_GET_CURR_RATESET, &rateset,
+			sizeof(rateset));
+	if (unlikely(err)) {
+		WL_ERR("could not get current rateset (%d)\n", err);
+		goto done;
+	}
+
+	rateset.count = le32_to_cpu(rateset.count);
+
+	legacy = brcmf_find_msb(mask->control[IEEE80211_BAND_2GHZ].legacy);
+	if (!legacy)
+		legacy = brcmf_find_msb(mask->control[IEEE80211_BAND_5GHZ].legacy);
+
+	val = wl_g_rates[legacy - 1].bitrate * 100000;
+
+	if (val < rateset.count)
+		/* Select rate by rateset index */
+		rate = rateset.rates[val] & 0x7f;
+	else
+		/* Specified rate in bps */
+		rate = val / 500000;
+
+	WL_CONN("rate %d mbps\n", rate / 2);
+
+	/*
+	 *
+	 *      Set rate override,
+	 *      Since the is a/b/g-blind, both a/bg_rate are enforced.
+	 */
+	err_bg = brcmf_dev_intvar_set(dev, "bg_rate", rate);
+	err_a = brcmf_dev_intvar_set(dev, "a_rate", rate);
+	if (unlikely(err_bg && err_a)) {
+		WL_ERR("could not set fixed rate (%d) (%d)\n", err_bg, err_a);
+		err = err_bg | err_a;
+	}
+
+done:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32 brcmf_cfg80211_resume(struct wiphy *wiphy)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+
+	/*
+	 * Check for WL_STATUS_READY before any function call which
+	 * could result is bus access. Don't block the resume for
+	 * any driver error conditions
+	 */
+	WL_TRACE("Enter\n");
+
+#if defined(CONFIG_PM_SLEEP)
+	atomic_set(&brcmf_mmc_suspend, false);
+#endif	/*  defined(CONFIG_PM_SLEEP) */
+
+	if (test_bit(WL_STATUS_READY, &cfg_priv->status))
+		brcmf_invoke_iscan(wiphy_to_cfg(wiphy));
+
+	WL_TRACE("Exit\n");
+	return 0;
+}
+
+static s32 brcmf_cfg80211_suspend(struct wiphy *wiphy,
+				  struct cfg80211_wowlan *wow)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+
+	WL_TRACE("Enter\n");
+
+	/*
+	 * Check for WL_STATUS_READY before any function call which
+	 * could result is bus access. Don't block the suspend for
+	 * any driver error conditions
+	 */
+
+	/*
+	 * While going to suspend if associated with AP disassociate
+	 * from AP to save power while system is in suspended state
+	 */
+	if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) ||
+	     test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) &&
+	     test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+		WL_INFO("Disassociating from AP"
+			" while entering suspend state\n");
+		brcmf_link_down(cfg_priv);
+
+		/*
+		 * Make sure WPA_Supplicant receives all the event
+		 * generated due to DISASSOC call to the fw to keep
+		 * the state fw and WPA_Supplicant state consistent
+		 */
+		rtnl_unlock();
+		brcmf_delay(500);
+		rtnl_lock();
+	}
+
+	set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+	if (test_bit(WL_STATUS_READY, &cfg_priv->status))
+		brcmf_term_iscan(cfg_priv);
+
+	if (cfg_priv->scan_request) {
+		/* Indidate scan abort to cfg80211 layer */
+		WL_INFO("Terminating scan in progress\n");
+		cfg80211_scan_done(cfg_priv->scan_request, true);
+		cfg_priv->scan_request = NULL;
+	}
+	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+	clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+
+	/* Turn off watchdog timer */
+	if (test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+		WL_INFO("Enable MPC\n");
+		brcmf_set_mpc(ndev, 1);
+	}
+
+#if defined(CONFIG_PM_SLEEP)
+	atomic_set(&brcmf_mmc_suspend, true);
+#endif	/*  defined(CONFIG_PM_SLEEP) */
+
+	WL_TRACE("Exit\n");
+
+	return 0;
+}
+
+static __used s32
+brcmf_update_pmklist(struct net_device *dev,
+		     struct brcmf_cfg80211_pmk_list *pmk_list, s32 err)
+{
+	int i, j;
+
+	WL_CONN("No of elements %d\n", pmk_list->pmkids.npmkid);
+	for (i = 0; i < pmk_list->pmkids.npmkid; i++) {
+		WL_CONN("PMKID[%d]: %pM =\n", i,
+			&pmk_list->pmkids.pmkid[i].BSSID);
+		for (j = 0; j < WLAN_PMKID_LEN; j++)
+			WL_CONN("%02x\n", pmk_list->pmkids.pmkid[i].PMKID[j]);
+	}
+
+	if (likely(!err))
+		brcmf_dev_bufvar_set(dev, "pmkid_info", (char *)pmk_list,
+					sizeof(*pmk_list));
+
+	return err;
+}
+
+static s32
+brcmf_cfg80211_set_pmksa(struct wiphy *wiphy, struct net_device *dev,
+			 struct cfg80211_pmksa *pmksa)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct _pmkid_list *pmkids = &cfg_priv->pmk_list->pmkids;
+	s32 err = 0;
+	int i;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	for (i = 0; i < pmkids->npmkid; i++)
+		if (!memcmp(pmksa->bssid, pmkids->pmkid[i].BSSID, ETH_ALEN))
+			break;
+	if (i < WL_NUM_PMKIDS_MAX) {
+		memcpy(pmkids->pmkid[i].BSSID, pmksa->bssid, ETH_ALEN);
+		memcpy(pmkids->pmkid[i].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
+		if (i == pmkids->npmkid)
+			pmkids->npmkid++;
+	} else
+		err = -EINVAL;
+
+	WL_CONN("set_pmksa,IW_PMKSA_ADD - PMKID: %pM =\n",
+		pmkids->pmkid[pmkids->npmkid].BSSID);
+	for (i = 0; i < WLAN_PMKID_LEN; i++)
+		WL_CONN("%02x\n", pmkids->pmkid[pmkids->npmkid].PMKID[i]);
+
+	err = brcmf_update_pmklist(dev, cfg_priv->pmk_list, err);
+
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_cfg80211_del_pmksa(struct wiphy *wiphy, struct net_device *dev,
+		      struct cfg80211_pmksa *pmksa)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	struct _pmkid_list pmkid;
+	s32 err = 0;
+	int i;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+	memcpy(&pmkid.pmkid[0].BSSID, pmksa->bssid, ETH_ALEN);
+	memcpy(&pmkid.pmkid[0].PMKID, pmksa->pmkid, WLAN_PMKID_LEN);
+
+	WL_CONN("del_pmksa,IW_PMKSA_REMOVE - PMKID: %pM =\n",
+	       &pmkid.pmkid[0].BSSID);
+	for (i = 0; i < WLAN_PMKID_LEN; i++)
+		WL_CONN("%02x\n", pmkid.pmkid[0].PMKID[i]);
+
+	for (i = 0; i < cfg_priv->pmk_list->pmkids.npmkid; i++)
+		if (!memcmp
+		    (pmksa->bssid, &cfg_priv->pmk_list->pmkids.pmkid[i].BSSID,
+		     ETH_ALEN))
+			break;
+
+	if ((cfg_priv->pmk_list->pmkids.npmkid > 0)
+	    && (i < cfg_priv->pmk_list->pmkids.npmkid)) {
+		memset(&cfg_priv->pmk_list->pmkids.pmkid[i], 0,
+		       sizeof(pmkid_t));
+		for (; i < (cfg_priv->pmk_list->pmkids.npmkid - 1); i++) {
+			memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].BSSID,
+			       &cfg_priv->pmk_list->pmkids.pmkid[i + 1].BSSID,
+			       ETH_ALEN);
+			memcpy(&cfg_priv->pmk_list->pmkids.pmkid[i].PMKID,
+			       &cfg_priv->pmk_list->pmkids.pmkid[i + 1].PMKID,
+			       WLAN_PMKID_LEN);
+		}
+		cfg_priv->pmk_list->pmkids.npmkid--;
+	} else
+		err = -EINVAL;
+
+	err = brcmf_update_pmklist(dev, cfg_priv->pmk_list, err);
+
+	WL_TRACE("Exit\n");
+	return err;
+
+}
+
+static s32
+brcmf_cfg80211_flush_pmksa(struct wiphy *wiphy, struct net_device *dev)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = wiphy_to_cfg(wiphy);
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	CHECK_SYS_UP();
+
+	memset(cfg_priv->pmk_list, 0, sizeof(*cfg_priv->pmk_list));
+	err = brcmf_update_pmklist(dev, cfg_priv->pmk_list, err);
+
+	WL_TRACE("Exit\n");
+	return err;
+
+}
+
+static struct cfg80211_ops wl_cfg80211_ops = {
+	.change_virtual_intf = brcmf_cfg80211_change_iface,
+	.scan = brcmf_cfg80211_scan,
+	.set_wiphy_params = brcmf_cfg80211_set_wiphy_params,
+	.join_ibss = brcmf_cfg80211_join_ibss,
+	.leave_ibss = brcmf_cfg80211_leave_ibss,
+	.get_station = brcmf_cfg80211_get_station,
+	.set_tx_power = brcmf_cfg80211_set_tx_power,
+	.get_tx_power = brcmf_cfg80211_get_tx_power,
+	.add_key = brcmf_cfg80211_add_key,
+	.del_key = brcmf_cfg80211_del_key,
+	.get_key = brcmf_cfg80211_get_key,
+	.set_default_key = brcmf_cfg80211_config_default_key,
+	.set_default_mgmt_key = brcmf_cfg80211_config_default_mgmt_key,
+	.set_power_mgmt = brcmf_cfg80211_set_power_mgmt,
+	.set_bitrate_mask = brcmf_cfg80211_set_bitrate_mask,
+	.connect = brcmf_cfg80211_connect,
+	.disconnect = brcmf_cfg80211_disconnect,
+	.suspend = brcmf_cfg80211_suspend,
+	.resume = brcmf_cfg80211_resume,
+	.set_pmksa = brcmf_cfg80211_set_pmksa,
+	.del_pmksa = brcmf_cfg80211_del_pmksa,
+	.flush_pmksa = brcmf_cfg80211_flush_pmksa
+};
+
+static s32 brcmf_mode_to_nl80211_iftype(s32 mode)
+{
+	s32 err = 0;
+
+	switch (mode) {
+	case WL_MODE_BSS:
+		return NL80211_IFTYPE_STATION;
+	case WL_MODE_IBSS:
+		return NL80211_IFTYPE_ADHOC;
+	default:
+		return NL80211_IFTYPE_UNSPECIFIED;
+	}
+
+	return err;
+}
+
+static struct wireless_dev *brcmf_alloc_wdev(s32 sizeof_iface,
+					  struct device *dev)
+{
+	struct wireless_dev *wdev;
+	s32 err = 0;
+
+	wdev = kzalloc(sizeof(*wdev), GFP_KERNEL);
+	if (unlikely(!wdev)) {
+		WL_ERR("Could not allocate wireless device\n");
+		return ERR_PTR(-ENOMEM);
+	}
+	wdev->wiphy =
+	    wiphy_new(&wl_cfg80211_ops,
+		      sizeof(struct brcmf_cfg80211_priv) + sizeof_iface);
+	if (unlikely(!wdev->wiphy)) {
+		WL_ERR("Couldn not allocate wiphy device\n");
+		err = -ENOMEM;
+		goto wiphy_new_out;
+	}
+	set_wiphy_dev(wdev->wiphy, dev);
+	wdev->wiphy->max_scan_ssids = WL_NUM_SCAN_MAX;
+	wdev->wiphy->max_num_pmkids = WL_NUM_PMKIDS_MAX;
+	wdev->wiphy->interface_modes =
+	    BIT(NL80211_IFTYPE_STATION) | BIT(NL80211_IFTYPE_ADHOC);
+	wdev->wiphy->bands[IEEE80211_BAND_2GHZ] = &__wl_band_2ghz;
+	wdev->wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_a;	/* Set
+						* it as 11a by default.
+						* This will be updated with
+						* 11n phy tables in
+						* "ifconfig up"
+						* if phy has 11n capability
+						*/
+	wdev->wiphy->signal_type = CFG80211_SIGNAL_TYPE_MBM;
+	wdev->wiphy->cipher_suites = __wl_cipher_suites;
+	wdev->wiphy->n_cipher_suites = ARRAY_SIZE(__wl_cipher_suites);
+#ifndef WL_POWERSAVE_DISABLED
+	wdev->wiphy->flags |= WIPHY_FLAG_PS_ON_BY_DEFAULT;	/* enable power
+								 * save mode
+								 * by default
+								 */
+#else
+	wdev->wiphy->flags &= ~WIPHY_FLAG_PS_ON_BY_DEFAULT;
+#endif				/* !WL_POWERSAVE_DISABLED */
+	err = wiphy_register(wdev->wiphy);
+	if (unlikely(err < 0)) {
+		WL_ERR("Couldn not register wiphy device (%d)\n", err);
+		goto wiphy_register_out;
+	}
+	return wdev;
+
+wiphy_register_out:
+	wiphy_free(wdev->wiphy);
+
+wiphy_new_out:
+	kfree(wdev);
+
+	return ERR_PTR(err);
+}
+
+static void brcmf_free_wdev(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct wireless_dev *wdev = cfg_to_wdev(cfg_priv);
+
+	if (unlikely(!wdev)) {
+		WL_ERR("wdev is invalid\n");
+		return;
+	}
+	wiphy_unregister(wdev->wiphy);
+	wiphy_free(wdev->wiphy);
+	kfree(wdev);
+	cfg_to_wdev(cfg_priv) = NULL;
+}
+
+static s32 brcmf_inform_bss(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_scan_results *bss_list;
+	struct brcmf_bss_info *bi = NULL;	/* must be initialized */
+	s32 err = 0;
+	int i;
+
+	bss_list = cfg_priv->bss_list;
+	if (unlikely(bss_list->version != BRCMF_BSS_INFO_VERSION)) {
+		WL_ERR("Version %d != WL_BSS_INFO_VERSION\n",
+		       bss_list->version);
+		return -EOPNOTSUPP;
+	}
+	WL_SCAN("scanned AP count (%d)\n", bss_list->count);
+	bi = next_bss(bss_list, bi);
+	for_each_bss(bss_list, bi, i) {
+		err = brcmf_inform_single_bss(cfg_priv, bi);
+		if (unlikely(err))
+			break;
+	}
+	return err;
+}
+
+
+static s32 brcmf_inform_single_bss(struct brcmf_cfg80211_priv *cfg_priv,
+				   struct brcmf_bss_info *bi)
+{
+	struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+	struct ieee80211_channel *notify_channel;
+	struct cfg80211_bss *bss;
+	struct ieee80211_supported_band *band;
+	s32 err = 0;
+	u16 channel;
+	u32 freq;
+	u64 notify_timestamp;
+	u16 notify_capability;
+	u16 notify_interval;
+	u8 *notify_ie;
+	size_t notify_ielen;
+	s32 notify_signal;
+
+	if (unlikely(le32_to_cpu(bi->length) > WL_BSS_INFO_MAX)) {
+		WL_ERR("Bss info is larger than buffer. Discarding\n");
+		return 0;
+	}
+
+	channel = bi->ctl_ch ? bi->ctl_ch :
+				CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+
+	if (channel <= CH_MAX_2G_CHANNEL)
+		band = wiphy->bands[IEEE80211_BAND_2GHZ];
+	else
+		band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	freq = ieee80211_channel_to_frequency(channel, band->band);
+	notify_channel = ieee80211_get_channel(wiphy, freq);
+
+	notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */
+	notify_capability = le16_to_cpu(bi->capability);
+	notify_interval = le16_to_cpu(bi->beacon_period);
+	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+	notify_ielen = le16_to_cpu(bi->ie_length);
+	notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+	WL_CONN("bssid: %2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X\n",
+			bi->BSSID[0], bi->BSSID[1], bi->BSSID[2],
+			bi->BSSID[3], bi->BSSID[4], bi->BSSID[5]);
+	WL_CONN("Channel: %d(%d)\n", channel, freq);
+	WL_CONN("Capability: %X\n", notify_capability);
+	WL_CONN("Beacon interval: %d\n", notify_interval);
+	WL_CONN("Signal: %d\n", notify_signal);
+	WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp);
+
+	bss = cfg80211_inform_bss(wiphy, notify_channel, (const u8 *)bi->BSSID,
+		notify_timestamp, notify_capability, notify_interval, notify_ie,
+		notify_ielen, notify_signal, GFP_KERNEL);
+
+	if (unlikely(!bss)) {
+		WL_ERR("cfg80211_inform_bss_frame error\n");
+		return -EINVAL;
+	}
+
+	return err;
+}
+
+static s32 wl_inform_ibss(struct brcmf_cfg80211_priv *cfg_priv,
+			  struct net_device *dev, const u8 *bssid)
+{
+	struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+	struct ieee80211_channel *notify_channel;
+	struct brcmf_bss_info *bi = NULL;
+	struct ieee80211_supported_band *band;
+	u8 *buf = NULL;
+	s32 err = 0;
+	u16 channel;
+	u32 freq;
+	u64 notify_timestamp;
+	u16 notify_capability;
+	u16 notify_interval;
+	u8 *notify_ie;
+	size_t notify_ielen;
+	s32 notify_signal;
+
+	WL_TRACE("Enter\n");
+
+	buf = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+	if (buf == NULL) {
+		WL_ERR("kzalloc() failed\n");
+		err = -ENOMEM;
+		goto CleanUp;
+	}
+
+	*(u32 *)buf = cpu_to_le32(WL_BSS_INFO_MAX);
+
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_BSS_INFO, buf, WL_BSS_INFO_MAX);
+	if (unlikely(err)) {
+		WL_ERR("WLC_GET_BSS_INFO failed: %d\n", err);
+		goto CleanUp;
+	}
+
+	bi = (struct brcmf_bss_info *)(buf + 4);
+
+	channel = bi->ctl_ch ? bi->ctl_ch :
+				CHSPEC_CHANNEL(le16_to_cpu(bi->chanspec));
+
+	if (channel <= CH_MAX_2G_CHANNEL)
+		band = wiphy->bands[IEEE80211_BAND_2GHZ];
+	else
+		band = wiphy->bands[IEEE80211_BAND_5GHZ];
+
+	freq = ieee80211_channel_to_frequency(channel, band->band);
+	notify_channel = ieee80211_get_channel(wiphy, freq);
+
+	notify_timestamp = jiffies_to_msecs(jiffies)*1000; /* uSec */
+	notify_capability = le16_to_cpu(bi->capability);
+	notify_interval = le16_to_cpu(bi->beacon_period);
+	notify_ie = (u8 *)bi + le16_to_cpu(bi->ie_offset);
+	notify_ielen = le16_to_cpu(bi->ie_length);
+	notify_signal = (s16)le16_to_cpu(bi->RSSI) * 100;
+
+	WL_CONN("channel: %d(%d)\n", channel, freq);
+	WL_CONN("capability: %X\n", notify_capability);
+	WL_CONN("beacon interval: %d\n", notify_interval);
+	WL_CONN("signal: %d\n", notify_signal);
+	WL_CONN("notify_timestamp: %#018llx\n", notify_timestamp);
+
+	cfg80211_inform_bss(wiphy, notify_channel, bssid,
+		notify_timestamp, notify_capability, notify_interval,
+		notify_ie, notify_ielen, notify_signal, GFP_KERNEL);
+
+CleanUp:
+
+	kfree(buf);
+
+	WL_TRACE("Exit\n");
+
+	return err;
+}
+
+static bool brcmf_is_linkup(struct brcmf_cfg80211_priv *cfg_priv,
+			    const struct brcmf_event_msg *e)
+{
+	u32 event = be32_to_cpu(e->event_type);
+	u32 status = be32_to_cpu(e->status);
+
+	if (event == BRCMF_E_SET_SSID && status == BRCMF_E_STATUS_SUCCESS) {
+		WL_CONN("Processing set ssid\n");
+		cfg_priv->link_up = true;
+		return true;
+	}
+
+	return false;
+}
+
+static bool brcmf_is_linkdown(struct brcmf_cfg80211_priv *cfg_priv,
+			      const struct brcmf_event_msg *e)
+{
+	u32 event = be32_to_cpu(e->event_type);
+	u16 flags = be16_to_cpu(e->flags);
+
+	if (event == BRCMF_E_LINK && (!(flags & BRCMF_EVENT_MSG_LINK))) {
+		WL_CONN("Processing link down\n");
+		return true;
+	}
+	return false;
+}
+
+static bool brcmf_is_nonetwork(struct brcmf_cfg80211_priv *cfg_priv,
+			       const struct brcmf_event_msg *e)
+{
+	u32 event = be32_to_cpu(e->event_type);
+	u32 status = be32_to_cpu(e->status);
+
+	if (event == BRCMF_E_LINK && status == BRCMF_E_STATUS_NO_NETWORKS) {
+		WL_CONN("Processing Link %s & no network found\n",
+				be16_to_cpu(e->flags) & BRCMF_EVENT_MSG_LINK ?
+				"up" : "down");
+		return true;
+	}
+
+	if (event == BRCMF_E_SET_SSID && status != BRCMF_E_STATUS_SUCCESS) {
+		WL_CONN("Processing connecting & no network found\n");
+		return true;
+	}
+
+	return false;
+}
+
+static s32
+brcmf_notify_connect_status(struct brcmf_cfg80211_priv *cfg_priv,
+			    struct net_device *ndev,
+			    const struct brcmf_event_msg *e, void *data)
+{
+	s32 err = 0;
+
+	if (brcmf_is_linkup(cfg_priv, e)) {
+		WL_CONN("Linkup\n");
+		if (brcmf_is_ibssmode(cfg_priv)) {
+			brcmf_update_prof(cfg_priv, NULL, (void *)e->addr,
+				WL_PROF_BSSID);
+			wl_inform_ibss(cfg_priv, ndev, e->addr);
+			cfg80211_ibss_joined(ndev, e->addr, GFP_KERNEL);
+			clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+			set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+		} else
+			brcmf_bss_connect_done(cfg_priv, ndev, e, data, true);
+	} else if (brcmf_is_linkdown(cfg_priv, e)) {
+		WL_CONN("Linkdown\n");
+		if (brcmf_is_ibssmode(cfg_priv)) {
+			clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+			if (test_and_clear_bit(WL_STATUS_CONNECTED,
+				&cfg_priv->status))
+				brcmf_link_down(cfg_priv);
+		} else {
+			brcmf_bss_connect_done(cfg_priv, ndev, e, data, false);
+			if (test_and_clear_bit(WL_STATUS_CONNECTED,
+				&cfg_priv->status)) {
+				cfg80211_disconnected(ndev, 0, NULL, 0,
+					GFP_KERNEL);
+				brcmf_link_down(cfg_priv);
+			}
+		}
+		brcmf_init_prof(cfg_priv->profile);
+	} else if (brcmf_is_nonetwork(cfg_priv, e)) {
+		if (brcmf_is_ibssmode(cfg_priv))
+			clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status);
+		else
+			brcmf_bss_connect_done(cfg_priv, ndev, e, data, false);
+	}
+
+	return err;
+}
+
+static s32
+brcmf_notify_roaming_status(struct brcmf_cfg80211_priv *cfg_priv,
+			    struct net_device *ndev,
+			    const struct brcmf_event_msg *e, void *data)
+{
+	s32 err = 0;
+	u32 event = be32_to_cpu(e->event_type);
+	u32 status = be32_to_cpu(e->status);
+
+	if (event == BRCMF_E_ROAM && status == BRCMF_E_STATUS_SUCCESS) {
+		if (test_bit(WL_STATUS_CONNECTED, &cfg_priv->status))
+			brcmf_bss_roaming_done(cfg_priv, ndev, e, data);
+		else
+			brcmf_bss_connect_done(cfg_priv, ndev, e, data, true);
+	}
+
+	return err;
+}
+
+static __used s32
+brcmf_dev_bufvar_set(struct net_device *dev, s8 *name, s8 *buf, s32 len)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	u32 buflen;
+
+	buflen = brcmu_mkiovar(name, buf, len, cfg_priv->ioctl_buf,
+			       WL_IOCTL_LEN_MAX);
+	BUG_ON(!buflen);
+
+	return brcmf_dev_ioctl(dev, BRCMF_C_SET_VAR, cfg_priv->ioctl_buf,
+			       buflen);
+}
+
+static s32
+brcmf_dev_bufvar_get(struct net_device *dev, s8 *name, s8 *buf,
+		  s32 buf_len)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(dev);
+	u32 len;
+	s32 err = 0;
+
+	len = brcmu_mkiovar(name, NULL, 0, cfg_priv->ioctl_buf,
+			    WL_IOCTL_LEN_MAX);
+	BUG_ON(!len);
+	err = brcmf_dev_ioctl(dev, BRCMF_C_GET_VAR, (void *)cfg_priv->ioctl_buf,
+			WL_IOCTL_LEN_MAX);
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		return err;
+	}
+	memcpy(buf, cfg_priv->ioctl_buf, buf_len);
+
+	return err;
+}
+
+static s32 brcmf_get_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+	struct brcmf_cfg80211_assoc_ielen *assoc_info;
+	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+	u32 req_len;
+	u32 resp_len;
+	s32 err = 0;
+
+	brcmf_clear_assoc_ies(cfg_priv);
+
+	err = brcmf_dev_bufvar_get(ndev, "assoc_info", cfg_priv->extra_buf,
+				WL_ASSOC_INFO_MAX);
+	if (unlikely(err)) {
+		WL_ERR("could not get assoc info (%d)\n", err);
+		return err;
+	}
+	assoc_info = (struct brcmf_cfg80211_assoc_ielen *)cfg_priv->extra_buf;
+	req_len = assoc_info->req_len;
+	resp_len = assoc_info->resp_len;
+	if (req_len) {
+		err = brcmf_dev_bufvar_get(ndev, "assoc_req_ies",
+					   cfg_priv->extra_buf,
+					   WL_ASSOC_INFO_MAX);
+		if (unlikely(err)) {
+			WL_ERR("could not get assoc req (%d)\n", err);
+			return err;
+		}
+		conn_info->req_ie_len = req_len;
+		conn_info->req_ie =
+		    kmemdup(cfg_priv->extra_buf, conn_info->req_ie_len,
+			    GFP_KERNEL);
+	} else {
+		conn_info->req_ie_len = 0;
+		conn_info->req_ie = NULL;
+	}
+	if (resp_len) {
+		err = brcmf_dev_bufvar_get(ndev, "assoc_resp_ies",
+					   cfg_priv->extra_buf,
+					   WL_ASSOC_INFO_MAX);
+		if (unlikely(err)) {
+			WL_ERR("could not get assoc resp (%d)\n", err);
+			return err;
+		}
+		conn_info->resp_ie_len = resp_len;
+		conn_info->resp_ie =
+		    kmemdup(cfg_priv->extra_buf, conn_info->resp_ie_len,
+			    GFP_KERNEL);
+	} else {
+		conn_info->resp_ie_len = 0;
+		conn_info->resp_ie = NULL;
+	}
+	WL_CONN("req len (%d) resp len (%d)\n",
+	       conn_info->req_ie_len, conn_info->resp_ie_len);
+
+	return err;
+}
+
+static void brcmf_clear_assoc_ies(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+
+	kfree(conn_info->req_ie);
+	conn_info->req_ie = NULL;
+	conn_info->req_ie_len = 0;
+	kfree(conn_info->resp_ie);
+	conn_info->resp_ie = NULL;
+	conn_info->resp_ie_len = 0;
+}
+
+
+static void brcmf_ch_to_chanspec(int ch, struct brcmf_join_params *join_params,
+	size_t *join_params_size)
+{
+	chanspec_t chanspec = 0;
+
+	if (ch != 0) {
+		join_params->params.chanspec_num = 1;
+		join_params->params.chanspec_list[0] = ch;
+
+		if (join_params->params.chanspec_list[0] <= CH_MAX_2G_CHANNEL)
+			chanspec |= WL_CHANSPEC_BAND_2G;
+		else
+			chanspec |= WL_CHANSPEC_BAND_5G;
+
+		chanspec |= WL_CHANSPEC_BW_20;
+		chanspec |= WL_CHANSPEC_CTL_SB_NONE;
+
+		*join_params_size += BRCMF_ASSOC_PARAMS_FIXED_SIZE +
+			join_params->params.chanspec_num * sizeof(chanspec_t);
+
+		join_params->params.chanspec_list[0] &= WL_CHANSPEC_CHAN_MASK;
+		join_params->params.chanspec_list[0] |= chanspec;
+		join_params->params.chanspec_list[0] =
+		cpu_to_le16(join_params->params.chanspec_list[0]);
+
+		join_params->params.chanspec_num =
+			cpu_to_le32(join_params->params.chanspec_num);
+
+		WL_CONN("join_params->params.chanspec_list[0]= %#X,"
+			"channel %d, chanspec %#X\n",
+		       join_params->params.chanspec_list[0], ch, chanspec);
+	}
+}
+
+static s32 brcmf_update_bss_info(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_bss_info *bi;
+	struct brcmf_ssid *ssid;
+	struct brcmu_tlv *tim;
+	u16 beacon_interval;
+	u8 dtim_period;
+	size_t ie_len;
+	u8 *ie;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+	if (brcmf_is_ibssmode(cfg_priv))
+		return err;
+
+	ssid = (struct brcmf_ssid *)brcmf_read_prof(cfg_priv, WL_PROF_SSID);
+
+	*(u32 *)cfg_priv->extra_buf = cpu_to_le32(WL_EXTRA_BUF_MAX);
+	err = brcmf_dev_ioctl(cfg_to_ndev(cfg_priv), BRCMF_C_GET_BSS_INFO,
+			cfg_priv->extra_buf, WL_EXTRA_BUF_MAX);
+	if (unlikely(err)) {
+		WL_ERR("Could not get bss info %d\n", err);
+		goto update_bss_info_out;
+	}
+
+	bi = (struct brcmf_bss_info *)(cfg_priv->extra_buf + 4);
+	err = brcmf_inform_single_bss(cfg_priv, bi);
+	if (unlikely(err))
+		goto update_bss_info_out;
+
+	ie = ((u8 *)bi) + bi->ie_offset;
+	ie_len = bi->ie_length;
+	beacon_interval = cpu_to_le16(bi->beacon_period);
+
+	tim = brcmu_parse_tlvs(ie, ie_len, WLAN_EID_TIM);
+	if (tim)
+		dtim_period = tim->data[1];
+	else {
+		/*
+		* active scan was done so we could not get dtim
+		* information out of probe response.
+		* so we speficially query dtim information to dongle.
+		*/
+		u32 var;
+		err = brcmf_dev_intvar_get(cfg_to_ndev(cfg_priv),
+					   "dtim_assoc", &var);
+		if (unlikely(err)) {
+			WL_ERR("wl dtim_assoc failed (%d)\n", err);
+			goto update_bss_info_out;
+		}
+		dtim_period = (u8)var;
+	}
+
+	brcmf_update_prof(cfg_priv, NULL, &beacon_interval, WL_PROF_BEACONINT);
+	brcmf_update_prof(cfg_priv, NULL, &dtim_period, WL_PROF_DTIMPERIOD);
+
+update_bss_info_out:
+	WL_TRACE("Exit");
+	return err;
+}
+
+static s32
+brcmf_bss_roaming_done(struct brcmf_cfg80211_priv *cfg_priv,
+		       struct net_device *ndev,
+		       const struct brcmf_event_msg *e, void *data)
+{
+	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+
+	brcmf_get_assoc_ies(cfg_priv);
+	brcmf_update_prof(cfg_priv, NULL, &e->addr, WL_PROF_BSSID);
+	brcmf_update_bss_info(cfg_priv);
+
+	cfg80211_roamed(ndev, NULL,
+			(u8 *)brcmf_read_prof(cfg_priv, WL_PROF_BSSID),
+			conn_info->req_ie, conn_info->req_ie_len,
+			conn_info->resp_ie, conn_info->resp_ie_len, GFP_KERNEL);
+	WL_CONN("Report roaming result\n");
+
+	set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_bss_connect_done(struct brcmf_cfg80211_priv *cfg_priv,
+		       struct net_device *ndev, const struct brcmf_event_msg *e,
+		       void *data, bool completed)
+{
+	struct brcmf_cfg80211_connect_info *conn_info = cfg_to_conn(cfg_priv);
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+
+	if (test_and_clear_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) {
+		if (completed) {
+			brcmf_get_assoc_ies(cfg_priv);
+			brcmf_update_prof(cfg_priv, NULL, &e->addr,
+					  WL_PROF_BSSID);
+			brcmf_update_bss_info(cfg_priv);
+		}
+		cfg80211_connect_result(ndev,
+					(u8 *)brcmf_read_prof(cfg_priv,
+							      WL_PROF_BSSID),
+					conn_info->req_ie,
+					conn_info->req_ie_len,
+					conn_info->resp_ie,
+					conn_info->resp_ie_len,
+					completed ? WLAN_STATUS_SUCCESS :
+						    WLAN_STATUS_AUTH_TIMEOUT,
+					GFP_KERNEL);
+		if (completed)
+			set_bit(WL_STATUS_CONNECTED, &cfg_priv->status);
+		WL_CONN("Report connect result - connection %s\n",
+				completed ? "succeeded" : "failed");
+	}
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_notify_mic_status(struct brcmf_cfg80211_priv *cfg_priv,
+			struct net_device *ndev,
+			const struct brcmf_event_msg *e, void *data)
+{
+	u16 flags = be16_to_cpu(e->flags);
+	enum nl80211_key_type key_type;
+
+	rtnl_lock();
+	if (flags & BRCMF_EVENT_MSG_GROUP)
+		key_type = NL80211_KEYTYPE_GROUP;
+	else
+		key_type = NL80211_KEYTYPE_PAIRWISE;
+
+	cfg80211_michael_mic_failure(ndev, (u8 *)&e->addr, key_type, -1,
+				     NULL, GFP_KERNEL);
+	rtnl_unlock();
+
+	return 0;
+}
+
+static s32
+brcmf_notify_scan_status(struct brcmf_cfg80211_priv *cfg_priv,
+			 struct net_device *ndev,
+			 const struct brcmf_event_msg *e, void *data)
+{
+	struct brcmf_channel_info channel_inform;
+	struct brcmf_scan_results *bss_list;
+	u32 len = WL_SCAN_BUF_MAX;
+	s32 err = 0;
+	bool scan_abort = false;
+
+	WL_TRACE("Enter\n");
+
+	if (cfg_priv->iscan_on && cfg_priv->iscan_kickstart) {
+		WL_TRACE("Exit\n");
+		return brcmf_wakeup_iscan(cfg_to_iscan(cfg_priv));
+	}
+
+	if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING,
+					 &cfg_priv->status))) {
+		WL_ERR("Scan complete while device not scanning\n");
+		scan_abort = true;
+		err = -EINVAL;
+		goto scan_done_out;
+	}
+
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_GET_CHANNEL, &channel_inform,
+			sizeof(channel_inform));
+	if (unlikely(err)) {
+		WL_ERR("scan busy (%d)\n", err);
+		scan_abort = true;
+		goto scan_done_out;
+	}
+	channel_inform.scan_channel = le32_to_cpu(channel_inform.scan_channel);
+	if (unlikely(channel_inform.scan_channel)) {
+
+		WL_CONN("channel_inform.scan_channel (%d)\n",
+		       channel_inform.scan_channel);
+	}
+	cfg_priv->bss_list = cfg_priv->scan_results;
+	bss_list = cfg_priv->bss_list;
+	memset(bss_list, 0, len);
+	bss_list->buflen = cpu_to_le32(len);
+
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SCAN_RESULTS, bss_list, len);
+	if (unlikely(err)) {
+		WL_ERR("%s Scan_results error (%d)\n", ndev->name, err);
+		err = -EINVAL;
+		scan_abort = true;
+		goto scan_done_out;
+	}
+	bss_list->buflen = le32_to_cpu(bss_list->buflen);
+	bss_list->version = le32_to_cpu(bss_list->version);
+	bss_list->count = le32_to_cpu(bss_list->count);
+
+	err = brcmf_inform_bss(cfg_priv);
+	if (err) {
+		scan_abort = true;
+		goto scan_done_out;
+	}
+
+scan_done_out:
+	if (cfg_priv->scan_request) {
+		WL_SCAN("calling cfg80211_scan_done\n");
+		cfg80211_scan_done(cfg_priv->scan_request, scan_abort);
+		brcmf_set_mpc(ndev, 1);
+		cfg_priv->scan_request = NULL;
+	}
+
+	WL_TRACE("Exit\n");
+
+	return err;
+}
+
+static void brcmf_init_conf(struct brcmf_cfg80211_conf *conf)
+{
+	conf->mode = (u32)-1;
+	conf->frag_threshold = (u32)-1;
+	conf->rts_threshold = (u32)-1;
+	conf->retry_short = (u32)-1;
+	conf->retry_long = (u32)-1;
+	conf->tx_power = -1;
+}
+
+static void brcmf_init_prof(struct brcmf_cfg80211_profile *prof)
+{
+	memset(prof, 0, sizeof(*prof));
+}
+
+static void brcmf_init_eloop_handler(struct brcmf_cfg80211_event_loop *el)
+{
+	memset(el, 0, sizeof(*el));
+	el->handler[BRCMF_E_SCAN_COMPLETE] = brcmf_notify_scan_status;
+	el->handler[BRCMF_E_LINK] = brcmf_notify_connect_status;
+	el->handler[BRCMF_E_ROAM] = brcmf_notify_roaming_status;
+	el->handler[BRCMF_E_MIC_ERROR] = brcmf_notify_mic_status;
+	el->handler[BRCMF_E_SET_SSID] = brcmf_notify_connect_status;
+}
+
+static s32 brcmf_init_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	cfg_priv->scan_results = kzalloc(WL_SCAN_BUF_MAX, GFP_KERNEL);
+	if (unlikely(!cfg_priv->scan_results)) {
+		WL_ERR("Scan results alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->conf = kzalloc(sizeof(*cfg_priv->conf), GFP_KERNEL);
+	if (unlikely(!cfg_priv->conf)) {
+		WL_ERR("wl_conf alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->profile = kzalloc(sizeof(*cfg_priv->profile), GFP_KERNEL);
+	if (unlikely(!cfg_priv->profile)) {
+		WL_ERR("wl_profile alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->bss_info = kzalloc(WL_BSS_INFO_MAX, GFP_KERNEL);
+	if (unlikely(!cfg_priv->bss_info)) {
+		WL_ERR("Bss information alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->scan_req_int = kzalloc(sizeof(*cfg_priv->scan_req_int),
+					 GFP_KERNEL);
+	if (unlikely(!cfg_priv->scan_req_int)) {
+		WL_ERR("Scan req alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->ioctl_buf = kzalloc(WL_IOCTL_LEN_MAX, GFP_KERNEL);
+	if (unlikely(!cfg_priv->ioctl_buf)) {
+		WL_ERR("Ioctl buf alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->extra_buf = kzalloc(WL_EXTRA_BUF_MAX, GFP_KERNEL);
+	if (unlikely(!cfg_priv->extra_buf)) {
+		WL_ERR("Extra buf alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->iscan = kzalloc(sizeof(*cfg_priv->iscan), GFP_KERNEL);
+	if (unlikely(!cfg_priv->iscan)) {
+		WL_ERR("Iscan buf alloc failed\n");
+		goto init_priv_mem_out;
+	}
+	cfg_priv->pmk_list = kzalloc(sizeof(*cfg_priv->pmk_list), GFP_KERNEL);
+	if (unlikely(!cfg_priv->pmk_list)) {
+		WL_ERR("pmk list alloc failed\n");
+		goto init_priv_mem_out;
+	}
+
+	return 0;
+
+init_priv_mem_out:
+	brcmf_deinit_priv_mem(cfg_priv);
+
+	return -ENOMEM;
+}
+
+static void brcmf_deinit_priv_mem(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	kfree(cfg_priv->scan_results);
+	cfg_priv->scan_results = NULL;
+	kfree(cfg_priv->bss_info);
+	cfg_priv->bss_info = NULL;
+	kfree(cfg_priv->conf);
+	cfg_priv->conf = NULL;
+	kfree(cfg_priv->profile);
+	cfg_priv->profile = NULL;
+	kfree(cfg_priv->scan_req_int);
+	cfg_priv->scan_req_int = NULL;
+	kfree(cfg_priv->ioctl_buf);
+	cfg_priv->ioctl_buf = NULL;
+	kfree(cfg_priv->extra_buf);
+	cfg_priv->extra_buf = NULL;
+	kfree(cfg_priv->iscan);
+	cfg_priv->iscan = NULL;
+	kfree(cfg_priv->pmk_list);
+	cfg_priv->pmk_list = NULL;
+}
+
+static s32 brcmf_create_event_handler(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	sema_init(&cfg_priv->event_sync, 0);
+	cfg_priv->event_tsk = kthread_run(brcmf_event_handler, cfg_priv,
+					  "wl_event_handler");
+	if (IS_ERR(cfg_priv->event_tsk)) {
+		cfg_priv->event_tsk = NULL;
+		WL_ERR("failed to create event thread\n");
+		return -ENOMEM;
+	}
+	return 0;
+}
+
+static void brcmf_destroy_event_handler(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	if (cfg_priv->event_tsk) {
+		send_sig(SIGTERM, cfg_priv->event_tsk, 1);
+		kthread_stop(cfg_priv->event_tsk);
+		cfg_priv->event_tsk = NULL;
+	}
+}
+
+static void brcmf_term_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+
+	if (cfg_priv->iscan_on && iscan->tsk) {
+		iscan->state = WL_ISCAN_STATE_IDLE;
+		send_sig(SIGTERM, iscan->tsk, 1);
+		kthread_stop(iscan->tsk);
+		iscan->tsk = NULL;
+	}
+}
+
+static void brcmf_notify_iscan_complete(struct brcmf_cfg80211_iscan_ctrl *iscan,
+					bool aborted)
+{
+	struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan);
+	struct net_device *ndev = cfg_to_ndev(cfg_priv);
+
+	if (unlikely(!test_and_clear_bit(WL_STATUS_SCANNING,
+					 &cfg_priv->status))) {
+		WL_ERR("Scan complete while device not scanning\n");
+		return;
+	}
+	if (likely(cfg_priv->scan_request)) {
+		WL_SCAN("ISCAN Completed scan: %s\n",
+				aborted ? "Aborted" : "Done");
+		cfg80211_scan_done(cfg_priv->scan_request, aborted);
+		brcmf_set_mpc(ndev, 1);
+		cfg_priv->scan_request = NULL;
+	}
+	cfg_priv->iscan_kickstart = false;
+}
+
+static s32 brcmf_wakeup_iscan(struct brcmf_cfg80211_iscan_ctrl *iscan)
+{
+	if (likely(iscan->state != WL_ISCAN_STATE_IDLE)) {
+		WL_SCAN("wake up iscan\n");
+		up(&iscan->sync);
+		return 0;
+	}
+
+	return -EIO;
+}
+
+static s32
+brcmf_get_iscan_results(struct brcmf_cfg80211_iscan_ctrl *iscan, u32 *status,
+		     struct brcmf_scan_results **bss_list)
+{
+	struct brcmf_iscan_results list;
+	struct brcmf_scan_results *results;
+	struct brcmf_iscan_results *list_buf;
+	s32 err = 0;
+
+	memset(iscan->scan_buf, 0, WL_ISCAN_BUF_MAX);
+	list_buf = (struct brcmf_iscan_results *)iscan->scan_buf;
+	results = &list_buf->results;
+	results->buflen = BRCMF_ISCAN_RESULTS_FIXED_SIZE;
+	results->version = 0;
+	results->count = 0;
+
+	memset(&list, 0, sizeof(list));
+	list.results.buflen = cpu_to_le32(WL_ISCAN_BUF_MAX);
+	err = brcmf_dev_iovar_getbuf(iscan->dev, "iscanresults", &list,
+				BRCMF_ISCAN_RESULTS_FIXED_SIZE, iscan->scan_buf,
+				WL_ISCAN_BUF_MAX);
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		return err;
+	}
+	results->buflen = le32_to_cpu(results->buflen);
+	results->version = le32_to_cpu(results->version);
+	results->count = le32_to_cpu(results->count);
+	WL_SCAN("results->count = %d\n", results->count);
+	WL_SCAN("results->buflen = %d\n", results->buflen);
+	*status = le32_to_cpu(list_buf->status);
+	*bss_list = results;
+
+	return err;
+}
+
+static s32 brcmf_iscan_done(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+	s32 err = 0;
+
+	iscan->state = WL_ISCAN_STATE_IDLE;
+	rtnl_lock();
+	brcmf_inform_bss(cfg_priv);
+	brcmf_notify_iscan_complete(iscan, false);
+	rtnl_unlock();
+
+	return err;
+}
+
+static s32 brcmf_iscan_pending(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+	s32 err = 0;
+
+	/* Reschedule the timer */
+	mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+	iscan->timer_on = 1;
+
+	return err;
+}
+
+static s32 brcmf_iscan_inprogress(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+	s32 err = 0;
+
+	rtnl_lock();
+	brcmf_inform_bss(cfg_priv);
+	brcmf_run_iscan(iscan, NULL, BRCMF_SCAN_ACTION_CONTINUE);
+	rtnl_unlock();
+	/* Reschedule the timer */
+	mod_timer(&iscan->timer, jiffies + iscan->timer_ms * HZ / 1000);
+	iscan->timer_on = 1;
+
+	return err;
+}
+
+static s32 brcmf_iscan_aborted(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_priv->iscan;
+	s32 err = 0;
+
+	iscan->state = WL_ISCAN_STATE_IDLE;
+	rtnl_lock();
+	brcmf_notify_iscan_complete(iscan, true);
+	rtnl_unlock();
+
+	return err;
+}
+
+static s32 brcmf_iscan_thread(void *data)
+{
+	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 };
+	struct brcmf_cfg80211_iscan_ctrl *iscan =
+			(struct brcmf_cfg80211_iscan_ctrl *)data;
+	struct brcmf_cfg80211_priv *cfg_priv = iscan_to_cfg(iscan);
+	struct brcmf_cfg80211_iscan_eloop *el = &iscan->el;
+	u32 status;
+	int err = 0;
+
+	sched_setscheduler(current, SCHED_FIFO, &param);
+	allow_signal(SIGTERM);
+	status = BRCMF_SCAN_RESULTS_PARTIAL;
+	while (likely(!down_interruptible(&iscan->sync))) {
+		if (kthread_should_stop())
+			break;
+		if (iscan->timer_on) {
+			del_timer_sync(&iscan->timer);
+			iscan->timer_on = 0;
+		}
+		rtnl_lock();
+		err = brcmf_get_iscan_results(iscan, &status,
+					      &cfg_priv->bss_list);
+		if (unlikely(err)) {
+			status = BRCMF_SCAN_RESULTS_ABORTED;
+			WL_ERR("Abort iscan\n");
+		}
+		rtnl_unlock();
+		el->handler[status](cfg_priv);
+	}
+	if (iscan->timer_on) {
+		del_timer_sync(&iscan->timer);
+		iscan->timer_on = 0;
+	}
+	WL_SCAN("ISCAN thread terminated\n");
+
+	return 0;
+}
+
+static void brcmf_iscan_timer(unsigned long data)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan =
+			(struct brcmf_cfg80211_iscan_ctrl *)data;
+
+	if (iscan) {
+		iscan->timer_on = 0;
+		WL_SCAN("timer expired\n");
+		brcmf_wakeup_iscan(iscan);
+	}
+}
+
+static s32 brcmf_invoke_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+	int err = 0;
+
+	if (cfg_priv->iscan_on && !iscan->tsk) {
+		iscan->state = WL_ISCAN_STATE_IDLE;
+		sema_init(&iscan->sync, 0);
+		iscan->tsk = kthread_run(brcmf_iscan_thread, iscan, "wl_iscan");
+		if (IS_ERR(iscan->tsk)) {
+			WL_ERR("Could not create iscan thread\n");
+			iscan->tsk = NULL;
+			return -ENOMEM;
+		}
+	}
+
+	return err;
+}
+
+static void brcmf_init_iscan_eloop(struct brcmf_cfg80211_iscan_eloop *el)
+{
+	memset(el, 0, sizeof(*el));
+	el->handler[BRCMF_SCAN_RESULTS_SUCCESS] = brcmf_iscan_done;
+	el->handler[BRCMF_SCAN_RESULTS_PARTIAL] = brcmf_iscan_inprogress;
+	el->handler[BRCMF_SCAN_RESULTS_PENDING] = brcmf_iscan_pending;
+	el->handler[BRCMF_SCAN_RESULTS_ABORTED] = brcmf_iscan_aborted;
+	el->handler[BRCMF_SCAN_RESULTS_NO_MEM] = brcmf_iscan_aborted;
+}
+
+static s32 brcmf_init_iscan(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_iscan_ctrl *iscan = cfg_to_iscan(cfg_priv);
+	int err = 0;
+
+	if (cfg_priv->iscan_on) {
+		iscan->dev = cfg_to_ndev(cfg_priv);
+		iscan->state = WL_ISCAN_STATE_IDLE;
+		brcmf_init_iscan_eloop(&iscan->el);
+		iscan->timer_ms = WL_ISCAN_TIMER_INTERVAL_MS;
+		init_timer(&iscan->timer);
+		iscan->timer.data = (unsigned long) iscan;
+		iscan->timer.function = brcmf_iscan_timer;
+		sema_init(&iscan->sync, 0);
+		iscan->tsk = kthread_run(brcmf_iscan_thread, iscan, "wl_iscan");
+		if (IS_ERR(iscan->tsk)) {
+			WL_ERR("Could not create iscan thread\n");
+			iscan->tsk = NULL;
+			return -ENOMEM;
+		}
+		iscan->data = cfg_priv;
+	}
+
+	return err;
+}
+
+static s32 wl_init_priv(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct wiphy *wiphy = cfg_to_wiphy(cfg_priv);
+	s32 err = 0;
+
+	cfg_priv->scan_request = NULL;
+	cfg_priv->pwr_save = !!(wiphy->flags & WIPHY_FLAG_PS_ON_BY_DEFAULT);
+	cfg_priv->iscan_on = true;	/* iscan on & off switch.
+				 we enable iscan per default */
+	cfg_priv->roam_on = false;	/* roam on & off switch.
+				 we enable roam per default */
+
+	cfg_priv->iscan_kickstart = false;
+	cfg_priv->active_scan = true;	/* we do active scan for
+				 specific scan per default */
+	cfg_priv->dongle_up = false;	/* dongle is not up yet */
+	brcmf_init_eq(cfg_priv);
+	err = brcmf_init_priv_mem(cfg_priv);
+	if (unlikely(err))
+		return err;
+	if (unlikely(brcmf_create_event_handler(cfg_priv)))
+		return -ENOMEM;
+	brcmf_init_eloop_handler(&cfg_priv->el);
+	mutex_init(&cfg_priv->usr_sync);
+	err = brcmf_init_iscan(cfg_priv);
+	if (unlikely(err))
+		return err;
+	brcmf_init_conf(cfg_priv->conf);
+	brcmf_init_prof(cfg_priv->profile);
+	brcmf_link_down(cfg_priv);
+
+	return err;
+}
+
+static void wl_deinit_priv(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	brcmf_destroy_event_handler(cfg_priv);
+	cfg_priv->dongle_up = false;	/* dongle down */
+	brcmf_flush_eq(cfg_priv);
+	brcmf_link_down(cfg_priv);
+	brcmf_term_iscan(cfg_priv);
+	brcmf_deinit_priv_mem(cfg_priv);
+}
+
+s32 brcmf_cfg80211_attach(struct net_device *ndev, void *data)
+{
+	struct wireless_dev *wdev;
+	struct brcmf_cfg80211_priv *cfg_priv;
+	struct brcmf_cfg80211_iface *ci;
+	s32 err = 0;
+
+	if (unlikely(!ndev)) {
+		WL_ERR("ndev is invalid\n");
+		return -ENODEV;
+	}
+	cfg80211_dev = kzalloc(sizeof(struct brcmf_cfg80211_dev), GFP_KERNEL);
+	if (unlikely(!cfg80211_dev)) {
+		WL_ERR("wl_cfg80211_dev is invalid\n");
+		return -ENOMEM;
+	}
+	WL_INFO("func %p\n", brcmf_cfg80211_get_sdio_func());
+	wdev = brcmf_alloc_wdev(sizeof(struct brcmf_cfg80211_iface),
+				&brcmf_cfg80211_get_sdio_func()->dev);
+	if (IS_ERR(wdev))
+		return -ENOMEM;
+
+	wdev->iftype = brcmf_mode_to_nl80211_iftype(WL_MODE_BSS);
+	cfg_priv = wdev_to_cfg(wdev);
+	cfg_priv->wdev = wdev;
+	cfg_priv->pub = data;
+	ci = (struct brcmf_cfg80211_iface *)&cfg_priv->ci;
+	ci->cfg_priv = cfg_priv;
+	ndev->ieee80211_ptr = wdev;
+	SET_NETDEV_DEV(ndev, wiphy_dev(wdev->wiphy));
+	wdev->netdev = ndev;
+	err = wl_init_priv(cfg_priv);
+	if (unlikely(err)) {
+		WL_ERR("Failed to init iwm_priv (%d)\n", err);
+		goto cfg80211_attach_out;
+	}
+	brcmf_set_drvdata(cfg80211_dev, ci);
+
+	return err;
+
+cfg80211_attach_out:
+	brcmf_free_wdev(cfg_priv);
+	return err;
+}
+
+void brcmf_cfg80211_detach(void)
+{
+	struct brcmf_cfg80211_priv *cfg_priv;
+
+	cfg_priv = WL_PRIV_GET();
+
+	wl_deinit_priv(cfg_priv);
+	brcmf_free_wdev(cfg_priv);
+	brcmf_set_drvdata(cfg80211_dev, NULL);
+	kfree(cfg80211_dev);
+	cfg80211_dev = NULL;
+	brcmf_clear_sdio_func();
+}
+
+static void brcmf_wakeup_event(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	up(&cfg_priv->event_sync);
+}
+
+static s32 brcmf_event_handler(void *data)
+{
+	struct brcmf_cfg80211_priv *cfg_priv =
+			(struct brcmf_cfg80211_priv *)data;
+	struct sched_param param = {.sched_priority = MAX_RT_PRIO - 1 };
+	struct brcmf_cfg80211_event_q *e;
+
+	sched_setscheduler(current, SCHED_FIFO, &param);
+	allow_signal(SIGTERM);
+	while (likely(!down_interruptible(&cfg_priv->event_sync))) {
+		if (kthread_should_stop())
+			break;
+		e = brcmf_deq_event(cfg_priv);
+		if (unlikely(!e)) {
+			WL_ERR("event queue empty...\n");
+			BUG();
+		}
+		WL_INFO("event type (%d)\n", e->etype);
+		if (cfg_priv->el.handler[e->etype]) {
+			cfg_priv->el.handler[e->etype](cfg_priv,
+						       cfg_to_ndev(cfg_priv),
+						       &e->emsg, e->edata);
+		} else {
+			WL_INFO("Unknown Event (%d): ignoring\n", e->etype);
+		}
+		brcmf_put_event(e);
+	}
+	WL_INFO("was terminated\n");
+	return 0;
+}
+
+void
+brcmf_cfg80211_event(struct net_device *ndev,
+		  const struct brcmf_event_msg *e, void *data)
+{
+	u32 event_type = be32_to_cpu(e->event_type);
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+
+	if (likely(!brcmf_enq_event(cfg_priv, event_type, e, data)))
+		brcmf_wakeup_event(cfg_priv);
+}
+
+static void brcmf_init_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	brcmf_init_eq_lock(cfg_priv);
+	INIT_LIST_HEAD(&cfg_priv->eq_list);
+}
+
+static void brcmf_flush_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_event_q *e;
+
+	brcmf_lock_eq(cfg_priv);
+	while (!list_empty(&cfg_priv->eq_list)) {
+		e = list_first_entry(&cfg_priv->eq_list,
+				     struct brcmf_cfg80211_event_q, eq_list);
+		list_del(&e->eq_list);
+		kfree(e);
+	}
+	brcmf_unlock_eq(cfg_priv);
+}
+
+/*
+* retrieve first queued event from head
+*/
+
+static struct brcmf_cfg80211_event_q *brcmf_deq_event(
+	struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct brcmf_cfg80211_event_q *e = NULL;
+
+	brcmf_lock_eq(cfg_priv);
+	if (likely(!list_empty(&cfg_priv->eq_list))) {
+		e = list_first_entry(&cfg_priv->eq_list,
+				     struct brcmf_cfg80211_event_q, eq_list);
+		list_del(&e->eq_list);
+	}
+	brcmf_unlock_eq(cfg_priv);
+
+	return e;
+}
+
+/*
+** push event to tail of the queue
+*/
+
+static s32
+brcmf_enq_event(struct brcmf_cfg80211_priv *cfg_priv, u32 event,
+		const struct brcmf_event_msg *msg, void *data)
+{
+	struct brcmf_cfg80211_event_q *e;
+	s32 err = 0;
+
+	e = kzalloc(sizeof(struct brcmf_cfg80211_event_q), GFP_KERNEL);
+	if (unlikely(!e)) {
+		WL_ERR("event alloc failed\n");
+		return -ENOMEM;
+	}
+
+	e->etype = event;
+	memcpy(&e->emsg, msg, sizeof(struct brcmf_event_msg));
+
+	brcmf_lock_eq(cfg_priv);
+	list_add_tail(&e->eq_list, &cfg_priv->eq_list);
+	brcmf_unlock_eq(cfg_priv);
+
+	return err;
+}
+
+static void brcmf_put_event(struct brcmf_cfg80211_event_q *e)
+{
+	kfree(e);
+}
+
+void brcmf_cfg80211_sdio_func(void *func)
+{
+	cfg80211_sdio_func = (struct sdio_func *)func;
+}
+
+static void brcmf_clear_sdio_func(void)
+{
+	cfg80211_sdio_func = NULL;
+}
+
+struct sdio_func *brcmf_cfg80211_get_sdio_func(void)
+{
+	return cfg80211_sdio_func;
+}
+
+static s32 brcmf_dongle_mode(struct net_device *ndev, s32 iftype)
+{
+	s32 infra = 0;
+	s32 err = 0;
+
+	switch (iftype) {
+	case NL80211_IFTYPE_MONITOR:
+	case NL80211_IFTYPE_WDS:
+		WL_ERR("type (%d) : currently we do not support this mode\n",
+		       iftype);
+		err = -EINVAL;
+		return err;
+	case NL80211_IFTYPE_ADHOC:
+		infra = 0;
+		break;
+	case NL80211_IFTYPE_STATION:
+		infra = 1;
+		break;
+	default:
+		err = -EINVAL;
+		WL_ERR("invalid type (%d)\n", iftype);
+		return err;
+	}
+	infra = cpu_to_le32(infra);
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_INFRA, &infra, sizeof(infra));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_INFRA error (%d)\n", err);
+		return err;
+	}
+
+	return 0;
+}
+
+static s32 brcmf_dongle_eventmsg(struct net_device *ndev)
+{
+	/* Room for "event_msgs" + '\0' + bitvec */
+	s8 iovbuf[BRCMF_EVENTING_MASK_LEN + 12];
+	s8 eventmask[BRCMF_EVENTING_MASK_LEN];
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+
+	/* Setup event_msgs */
+	brcmu_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf,
+		    sizeof(iovbuf));
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_GET_VAR, iovbuf, sizeof(iovbuf));
+	if (unlikely(err)) {
+		WL_ERR("Get event_msgs error (%d)\n", err);
+		goto dongle_eventmsg_out;
+	}
+	memcpy(eventmask, iovbuf, BRCMF_EVENTING_MASK_LEN);
+
+	setbit(eventmask, BRCMF_E_SET_SSID);
+	setbit(eventmask, BRCMF_E_ROAM);
+	setbit(eventmask, BRCMF_E_PRUNE);
+	setbit(eventmask, BRCMF_E_AUTH);
+	setbit(eventmask, BRCMF_E_REASSOC);
+	setbit(eventmask, BRCMF_E_REASSOC_IND);
+	setbit(eventmask, BRCMF_E_DEAUTH_IND);
+	setbit(eventmask, BRCMF_E_DISASSOC_IND);
+	setbit(eventmask, BRCMF_E_DISASSOC);
+	setbit(eventmask, BRCMF_E_JOIN);
+	setbit(eventmask, BRCMF_E_ASSOC_IND);
+	setbit(eventmask, BRCMF_E_PSK_SUP);
+	setbit(eventmask, BRCMF_E_LINK);
+	setbit(eventmask, BRCMF_E_NDIS_LINK);
+	setbit(eventmask, BRCMF_E_MIC_ERROR);
+	setbit(eventmask, BRCMF_E_PMKID_CACHE);
+	setbit(eventmask, BRCMF_E_TXFAIL);
+	setbit(eventmask, BRCMF_E_JOIN_START);
+	setbit(eventmask, BRCMF_E_SCAN_COMPLETE);
+
+	brcmu_mkiovar("event_msgs", eventmask, BRCMF_EVENTING_MASK_LEN, iovbuf,
+		    sizeof(iovbuf));
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
+	if (unlikely(err)) {
+		WL_ERR("Set event_msgs error (%d)\n", err);
+		goto dongle_eventmsg_out;
+	}
+
+dongle_eventmsg_out:
+	WL_TRACE("Exit\n");
+	return err;
+}
+
+static s32
+brcmf_dongle_roam(struct net_device *ndev, u32 roamvar, u32 bcn_timeout)
+{
+	s8 iovbuf[32];
+	s32 roamtrigger[2];
+	s32 roam_delta[2];
+	s32 err = 0;
+
+	/*
+	 * Setup timeout if Beacons are lost and roam is
+	 * off to report link down
+	 */
+	if (roamvar) {
+		brcmu_mkiovar("bcn_timeout", (char *)&bcn_timeout,
+			sizeof(bcn_timeout), iovbuf, sizeof(iovbuf));
+		err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_VAR,
+				   iovbuf, sizeof(iovbuf));
+		if (unlikely(err)) {
+			WL_ERR("bcn_timeout error (%d)\n", err);
+			goto dongle_rom_out;
+		}
+	}
+
+	/*
+	 * Enable/Disable built-in roaming to allow supplicant
+	 * to take care of roaming
+	 */
+	WL_INFO("Internal Roaming = %s\n", roamvar ? "Off" : "On");
+	brcmu_mkiovar("roam_off", (char *)&roamvar,
+				sizeof(roamvar), iovbuf, sizeof(iovbuf));
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_VAR, iovbuf, sizeof(iovbuf));
+	if (unlikely(err)) {
+		WL_ERR("roam_off error (%d)\n", err);
+		goto dongle_rom_out;
+	}
+
+	roamtrigger[0] = WL_ROAM_TRIGGER_LEVEL;
+	roamtrigger[1] = BRCM_BAND_ALL;
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_ROAM_TRIGGER,
+			(void *)roamtrigger, sizeof(roamtrigger));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_ROAM_TRIGGER error (%d)\n", err);
+		goto dongle_rom_out;
+	}
+
+	roam_delta[0] = WL_ROAM_DELTA;
+	roam_delta[1] = BRCM_BAND_ALL;
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_ROAM_DELTA,
+				(void *)roam_delta, sizeof(roam_delta));
+	if (unlikely(err)) {
+		WL_ERR("WLC_SET_ROAM_DELTA error (%d)\n", err);
+		goto dongle_rom_out;
+	}
+
+dongle_rom_out:
+	return err;
+}
+
+static s32
+brcmf_dongle_scantime(struct net_device *ndev, s32 scan_assoc_time,
+		s32 scan_unassoc_time, s32 scan_passive_time)
+{
+	s32 err = 0;
+
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_SCAN_CHANNEL_TIME,
+			   &scan_assoc_time, sizeof(scan_assoc_time));
+	if (err) {
+		if (err == -EOPNOTSUPP)
+			WL_INFO("Scan assoc time is not supported\n");
+		else
+			WL_ERR("Scan assoc time error (%d)\n", err);
+		goto dongle_scantime_out;
+	}
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_SCAN_UNASSOC_TIME,
+			   &scan_unassoc_time, sizeof(scan_unassoc_time));
+	if (err) {
+		if (err == -EOPNOTSUPP)
+			WL_INFO("Scan unassoc time is not supported\n");
+		else
+			WL_ERR("Scan unassoc time error (%d)\n", err);
+		goto dongle_scantime_out;
+	}
+
+	err = brcmf_dev_ioctl(ndev, BRCMF_C_SET_SCAN_PASSIVE_TIME,
+			   &scan_passive_time, sizeof(scan_passive_time));
+	if (err) {
+		if (err == -EOPNOTSUPP)
+			WL_INFO("Scan passive time is not supported\n");
+		else
+			WL_ERR("Scan passive time error (%d)\n", err);
+		goto dongle_scantime_out;
+	}
+
+dongle_scantime_out:
+	return err;
+}
+
+s32 brcmf_config_dongle(struct brcmf_cfg80211_priv *cfg_priv, bool need_lock)
+{
+	struct net_device *ndev;
+	struct wireless_dev *wdev;
+	s32 err = 0;
+
+	if (cfg_priv->dongle_up)
+		return err;
+
+	ndev = cfg_to_ndev(cfg_priv);
+	wdev = ndev->ieee80211_ptr;
+	if (need_lock)
+		rtnl_lock();
+
+	brcmf_dongle_scantime(ndev, WL_SCAN_CHANNEL_TIME,
+			WL_SCAN_UNASSOC_TIME, WL_SCAN_PASSIVE_TIME);
+
+	err = brcmf_dongle_eventmsg(ndev);
+	if (unlikely(err))
+		goto default_conf_out;
+	err = brcmf_dongle_roam(ndev, (cfg_priv->roam_on ? 0 : 1),
+				WL_BEACON_TIMEOUT);
+	if (unlikely(err))
+		goto default_conf_out;
+	err = brcmf_dongle_mode(ndev, wdev->iftype);
+	if (unlikely(err && err != -EINPROGRESS))
+		goto default_conf_out;
+	err = brcmf_dongle_probecap(cfg_priv);
+	if (unlikely(err))
+		goto default_conf_out;
+
+	/* -EINPROGRESS: Call commit handler */
+
+default_conf_out:
+	if (need_lock)
+		rtnl_unlock();
+
+	cfg_priv->dongle_up = true;
+
+	return err;
+
+}
+
+static s32 wl_update_wiphybands(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct wiphy *wiphy;
+	s32 phy_list;
+	s8 phy;
+	s32 err = 0;
+
+	err = brcmf_dev_ioctl(cfg_to_ndev(cfg_priv), BRCM_GET_PHYLIST,
+			      &phy_list, sizeof(phy_list));
+	if (unlikely(err)) {
+		WL_ERR("error (%d)\n", err);
+		return err;
+	}
+
+	phy = ((char *)&phy_list)[1];
+	WL_INFO("%c phy\n", phy);
+	if (phy == 'n' || phy == 'a') {
+		wiphy = cfg_to_wiphy(cfg_priv);
+		wiphy->bands[IEEE80211_BAND_5GHZ] = &__wl_band_5ghz_n;
+	}
+
+	return err;
+}
+
+static s32 __brcmf_cfg80211_up(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	s32 err = 0;
+
+	set_bit(WL_STATUS_READY, &cfg_priv->status);
+
+	brcmf_debugfs_add_netdev_params(cfg_priv);
+
+	err = brcmf_config_dongle(cfg_priv, false);
+	if (unlikely(err))
+		return err;
+
+	brcmf_invoke_iscan(cfg_priv);
+
+	return err;
+}
+
+static s32 __brcmf_cfg80211_down(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	/*
+	 * While going down, if associated with AP disassociate
+	 * from AP to save power
+	 */
+	if ((test_bit(WL_STATUS_CONNECTED, &cfg_priv->status) ||
+	     test_bit(WL_STATUS_CONNECTING, &cfg_priv->status)) &&
+	     test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+		WL_INFO("Disassociating from AP");
+		brcmf_link_down(cfg_priv);
+
+		/* Make sure WPA_Supplicant receives all the event
+		   generated due to DISASSOC call to the fw to keep
+		   the state fw and WPA_Supplicant state consistent
+		 */
+		rtnl_unlock();
+		brcmf_delay(500);
+		rtnl_lock();
+	}
+
+	set_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+	brcmf_term_iscan(cfg_priv);
+	if (cfg_priv->scan_request) {
+		cfg80211_scan_done(cfg_priv->scan_request, true);
+		/* May need to perform this to cover rmmod */
+		/* wl_set_mpc(cfg_to_ndev(wl), 1); */
+		cfg_priv->scan_request = NULL;
+	}
+	clear_bit(WL_STATUS_READY, &cfg_priv->status);
+	clear_bit(WL_STATUS_SCANNING, &cfg_priv->status);
+	clear_bit(WL_STATUS_SCAN_ABORTING, &cfg_priv->status);
+
+	brcmf_debugfs_remove_netdev(cfg_priv);
+
+	return 0;
+}
+
+s32 brcmf_cfg80211_up(void)
+{
+	struct brcmf_cfg80211_priv *cfg_priv;
+	s32 err = 0;
+
+	cfg_priv = WL_PRIV_GET();
+	mutex_lock(&cfg_priv->usr_sync);
+	err = __brcmf_cfg80211_up(cfg_priv);
+	mutex_unlock(&cfg_priv->usr_sync);
+
+	return err;
+}
+
+s32 brcmf_cfg80211_down(void)
+{
+	struct brcmf_cfg80211_priv *cfg_priv;
+	s32 err = 0;
+
+	cfg_priv = WL_PRIV_GET();
+	mutex_lock(&cfg_priv->usr_sync);
+	err = __brcmf_cfg80211_down(cfg_priv);
+	mutex_unlock(&cfg_priv->usr_sync);
+
+	return err;
+}
+
+static s32 brcmf_dongle_probecap(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	return wl_update_wiphybands(cfg_priv);
+}
+
+static void *brcmf_read_prof(struct brcmf_cfg80211_priv *cfg_priv, s32 item)
+{
+	switch (item) {
+	case WL_PROF_SEC:
+		return &cfg_priv->profile->sec;
+	case WL_PROF_BSSID:
+		return &cfg_priv->profile->bssid;
+	case WL_PROF_SSID:
+		return &cfg_priv->profile->ssid;
+	}
+	WL_ERR("invalid item (%d)\n", item);
+	return NULL;
+}
+
+static s32
+brcmf_update_prof(struct brcmf_cfg80211_priv *cfg_priv,
+		  const struct brcmf_event_msg *e, void *data, s32 item)
+{
+	s32 err = 0;
+	struct brcmf_ssid *ssid;
+
+	switch (item) {
+	case WL_PROF_SSID:
+		ssid = (struct brcmf_ssid *) data;
+		memset(cfg_priv->profile->ssid.SSID, 0,
+		       sizeof(cfg_priv->profile->ssid.SSID));
+		memcpy(cfg_priv->profile->ssid.SSID,
+		       ssid->SSID, ssid->SSID_len);
+		cfg_priv->profile->ssid.SSID_len = ssid->SSID_len;
+		break;
+	case WL_PROF_BSSID:
+		if (data)
+			memcpy(cfg_priv->profile->bssid, data, ETH_ALEN);
+		else
+			memset(cfg_priv->profile->bssid, 0, ETH_ALEN);
+		break;
+	case WL_PROF_SEC:
+		memcpy(&cfg_priv->profile->sec, data,
+		       sizeof(cfg_priv->profile->sec));
+		break;
+	case WL_PROF_BEACONINT:
+		cfg_priv->profile->beacon_interval = *(u16 *)data;
+		break;
+	case WL_PROF_DTIMPERIOD:
+		cfg_priv->profile->dtim_period = *(u8 *)data;
+		break;
+	default:
+		WL_ERR("unsupported item (%d)\n", item);
+		err = -EOPNOTSUPP;
+		break;
+	}
+
+	return err;
+}
+
+static bool brcmf_is_ibssmode(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	return cfg_priv->conf->mode == WL_MODE_IBSS;
+}
+
+static __used s32 brcmf_add_ie(struct brcmf_cfg80211_priv *cfg_priv,
+			       u8 t, u8 l, u8 *v)
+{
+	struct brcmf_cfg80211_ie *ie = &cfg_priv->ie;
+	s32 err = 0;
+
+	if (unlikely(ie->offset + l + 2 > WL_TLV_INFO_MAX)) {
+		WL_ERR("ei crosses buffer boundary\n");
+		return -ENOSPC;
+	}
+	ie->buf[ie->offset] = t;
+	ie->buf[ie->offset + 1] = l;
+	memcpy(&ie->buf[ie->offset + 2], v, l);
+	ie->offset += l + 2;
+
+	return err;
+}
+
+static void brcmf_link_down(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	struct net_device *dev = NULL;
+	s32 err = 0;
+
+	WL_TRACE("Enter\n");
+
+	if (cfg_priv->link_up) {
+		dev = cfg_to_ndev(cfg_priv);
+		WL_INFO("Call WLC_DISASSOC to stop excess roaming\n ");
+		err = brcmf_dev_ioctl(dev, BRCMF_C_DISASSOC, NULL, 0);
+		if (unlikely(err))
+			WL_ERR("WLC_DISASSOC failed (%d)\n", err);
+		cfg_priv->link_up = false;
+	}
+	WL_TRACE("Exit\n");
+}
+
+static void brcmf_lock_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	spin_lock_irq(&cfg_priv->eq_lock);
+}
+
+static void brcmf_unlock_eq(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	spin_unlock_irq(&cfg_priv->eq_lock);
+}
+
+static void brcmf_init_eq_lock(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	spin_lock_init(&cfg_priv->eq_lock);
+}
+
+static void brcmf_delay(u32 ms)
+{
+	if (ms < 1000 / HZ) {
+		cond_resched();
+		mdelay(ms);
+	} else {
+		msleep(ms);
+	}
+}
+
+static void brcmf_set_drvdata(struct brcmf_cfg80211_dev *dev, void *data)
+{
+	dev->driver_data = data;
+}
+
+static void *brcmf_get_drvdata(struct brcmf_cfg80211_dev *dev)
+{
+	void *data = NULL;
+
+	if (dev)
+		data = dev->driver_data;
+	return data;
+}
+
+static void brcmf_set_mpc(struct net_device *ndev, int mpc)
+{
+	s32 err = 0;
+	struct brcmf_cfg80211_priv *cfg_priv = ndev_to_cfg(ndev);
+
+	if (test_bit(WL_STATUS_READY, &cfg_priv->status)) {
+		err = brcmf_dev_intvar_set(ndev, "mpc", mpc);
+		if (unlikely(err)) {
+			WL_ERR("fail to set mpc\n");
+			return;
+		}
+		WL_INFO("MPC : %d\n", mpc);
+	}
+}
+
+static int brcmf_debugfs_add_netdev_params(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	char buf[10+IFNAMSIZ];
+	struct dentry *fd;
+	s32 err = 0;
+
+	sprintf(buf, "netdev:%s", cfg_to_ndev(cfg_priv)->name);
+	cfg_priv->debugfsdir = debugfs_create_dir(buf,
+					cfg_to_wiphy(cfg_priv)->debugfsdir);
+
+	fd = debugfs_create_u16("beacon_int", S_IRUGO, cfg_priv->debugfsdir,
+		(u16 *)&cfg_priv->profile->beacon_interval);
+	if (!fd) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+	fd = debugfs_create_u8("dtim_period", S_IRUGO, cfg_priv->debugfsdir,
+		(u8 *)&cfg_priv->profile->dtim_period);
+	if (!fd) {
+		err = -ENOMEM;
+		goto err_out;
+	}
+
+err_out:
+	return err;
+}
+
+static void brcmf_debugfs_remove_netdev(struct brcmf_cfg80211_priv *cfg_priv)
+{
+	debugfs_remove_recursive(cfg_priv->debugfsdir);
+	cfg_priv->debugfsdir = NULL;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
new file mode 100644
index 0000000..f26d087
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmfmac/wl_cfg80211.h
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _wl_cfg80211_h_
+#define _wl_cfg80211_h_
+
+struct brcmf_cfg80211_conf;
+struct brcmf_cfg80211_iface;
+struct brcmf_cfg80211_priv;
+struct brcmf_cfg80211_security;
+struct brcmf_cfg80211_ibss;
+
+#define WL_DBG_NONE		0
+#define WL_DBG_CONN		(1 << 5)
+#define WL_DBG_SCAN		(1 << 4)
+#define WL_DBG_TRACE		(1 << 3)
+#define WL_DBG_INFO		(1 << 1)
+#define WL_DBG_ERR		(1 << 0)
+#define WL_DBG_MASK		((WL_DBG_INFO | WL_DBG_ERR | WL_DBG_TRACE) | \
+				(WL_DBG_SCAN) | (WL_DBG_CONN))
+
+#define	WL_ERR(fmt, args...)					\
+do {								\
+	if (brcmf_dbg_level & WL_DBG_ERR) {			\
+		if (net_ratelimit()) {				\
+			printk(KERN_ERR "ERROR @%s : " fmt,	\
+				__func__, ##args);		\
+		}						\
+	}							\
+} while (0)
+
+#if (defined BCMDBG)
+#define	WL_INFO(fmt, args...)					\
+do {								\
+	if (brcmf_dbg_level & WL_DBG_INFO) {			\
+		if (net_ratelimit()) {				\
+			printk(KERN_ERR "INFO @%s : " fmt,	\
+				__func__, ##args);		\
+		}						\
+	}							\
+} while (0)
+
+#define	WL_TRACE(fmt, args...)					\
+do {								\
+	if (brcmf_dbg_level & WL_DBG_TRACE) {			\
+		if (net_ratelimit()) {				\
+			printk(KERN_ERR "TRACE @%s : " fmt,	\
+				__func__, ##args);		\
+		}						\
+	}							\
+} while (0)
+
+#define	WL_SCAN(fmt, args...)					\
+do {								\
+	if (brcmf_dbg_level & WL_DBG_SCAN) {			\
+		if (net_ratelimit()) {				\
+			printk(KERN_ERR "SCAN @%s : " fmt,	\
+				__func__, ##args);		\
+		}						\
+	}							\
+} while (0)
+
+#define	WL_CONN(fmt, args...)					\
+do {								\
+	if (brcmf_dbg_level & WL_DBG_CONN) {			\
+		if (net_ratelimit()) {				\
+			printk(KERN_ERR "CONN @%s : " fmt,	\
+				__func__, ##args);		\
+		}						\
+	}							\
+} while (0)
+
+#else /* (defined BCMDBG) */
+#define	WL_INFO(fmt, args...)
+#define	WL_TRACE(fmt, args...)
+#define	WL_SCAN(fmt, args...)
+#define	WL_CONN(fmt, args...)
+#endif /* (defined BCMDBG) */
+
+#define WL_NUM_SCAN_MAX		1
+#define WL_NUM_PMKIDS_MAX	MAXPMKID	/* will be used
+						 * for 2.6.33 kernel
+						 * or later
+						 */
+#define WL_SCAN_BUF_MAX			(1024 * 8)
+#define WL_TLV_INFO_MAX			1024
+#define WL_BSS_INFO_MAX			2048
+#define WL_ASSOC_INFO_MAX	512	/*
+				 * needs to grab assoc info from dongle to
+				 * report it to cfg80211 through "connect"
+				 * event
+				 */
+#define WL_IOCTL_LEN_MAX	1024
+#define WL_EXTRA_BUF_MAX	2048
+#define WL_ISCAN_BUF_MAX	2048	/*
+				 * the buf length can be BRCMF_C_IOCTL_MAXLEN
+				 * to reduce iteration
+				 */
+#define WL_ISCAN_TIMER_INTERVAL_MS	3000
+#define WL_SCAN_ERSULTS_LAST	(BRCMF_SCAN_RESULTS_NO_MEM+1)
+#define WL_AP_MAX	256	/* virtually unlimitted as long
+				 * as kernel memory allows
+				 */
+
+#define WL_ROAM_TRIGGER_LEVEL		-75
+#define WL_ROAM_DELTA			20
+#define WL_BEACON_TIMEOUT		3
+
+#define WL_SCAN_CHANNEL_TIME		40
+#define WL_SCAN_UNASSOC_TIME		40
+#define WL_SCAN_PASSIVE_TIME		120
+
+/* dongle status */
+enum wl_status {
+	WL_STATUS_READY,
+	WL_STATUS_SCANNING,
+	WL_STATUS_SCAN_ABORTING,
+	WL_STATUS_CONNECTING,
+	WL_STATUS_CONNECTED
+};
+
+/* wi-fi mode */
+enum wl_mode {
+	WL_MODE_BSS,
+	WL_MODE_IBSS,
+	WL_MODE_AP
+};
+
+/* dongle profile list */
+enum wl_prof_list {
+	WL_PROF_MODE,
+	WL_PROF_SSID,
+	WL_PROF_SEC,
+	WL_PROF_IBSS,
+	WL_PROF_BAND,
+	WL_PROF_BSSID,
+	WL_PROF_ACT,
+	WL_PROF_BEACONINT,
+	WL_PROF_DTIMPERIOD
+};
+
+/* dongle iscan state */
+enum wl_iscan_state {
+	WL_ISCAN_STATE_IDLE,
+	WL_ISCAN_STATE_SCANING
+};
+
+/* dongle configuration */
+struct brcmf_cfg80211_conf {
+	u32 mode;		/* adhoc , infrastructure or ap */
+	u32 frag_threshold;
+	u32 rts_threshold;
+	u32 retry_short;
+	u32 retry_long;
+	s32 tx_power;
+	struct ieee80211_channel channel;
+};
+
+/* cfg80211 main event loop */
+struct brcmf_cfg80211_event_loop {
+	s32(*handler[BRCMF_E_LAST]) (struct brcmf_cfg80211_priv *cfg_priv,
+				     struct net_device *ndev,
+				     const struct brcmf_event_msg *e,
+				     void *data);
+};
+
+/* representing interface of cfg80211 plane */
+struct brcmf_cfg80211_iface {
+	struct brcmf_cfg80211_priv *cfg_priv;
+};
+
+struct brcmf_cfg80211_dev {
+	void *driver_data;	/* to store cfg80211 object information */
+};
+
+/* basic structure of scan request */
+struct brcmf_cfg80211_scan_req {
+	struct brcmf_ssid ssid;
+};
+
+/* basic structure of information element */
+struct brcmf_cfg80211_ie {
+	u16 offset;
+	u8 buf[WL_TLV_INFO_MAX];
+};
+
+/* event queue for cfg80211 main event */
+struct brcmf_cfg80211_event_q {
+	struct list_head eq_list;
+	u32 etype;
+	struct brcmf_event_msg emsg;
+	s8 edata[1];
+};
+
+/* security information with currently associated ap */
+struct brcmf_cfg80211_security {
+	u32 wpa_versions;
+	u32 auth_type;
+	u32 cipher_pairwise;
+	u32 cipher_group;
+	u32 wpa_auth;
+};
+
+/* ibss information for currently joined ibss network */
+struct brcmf_cfg80211_ibss {
+	u8 beacon_interval;	/* in millisecond */
+	u8 atim;		/* in millisecond */
+	s8 join_only;
+	u8 band;
+	u8 channel;
+};
+
+/* dongle profile */
+struct brcmf_cfg80211_profile {
+	u32 mode;
+	struct brcmf_ssid ssid;
+	u8 bssid[ETH_ALEN];
+	u16 beacon_interval;
+	u8 dtim_period;
+	struct brcmf_cfg80211_security sec;
+	struct brcmf_cfg80211_ibss ibss;
+	s32 band;
+};
+
+/* dongle iscan event loop */
+struct brcmf_cfg80211_iscan_eloop {
+	s32 (*handler[WL_SCAN_ERSULTS_LAST])
+		(struct brcmf_cfg80211_priv *cfg_priv);
+};
+
+/* dongle iscan controller */
+struct brcmf_cfg80211_iscan_ctrl {
+	struct net_device *dev;
+	struct timer_list timer;
+	u32 timer_ms;
+	u32 timer_on;
+	s32 state;
+	struct task_struct *tsk;
+	struct semaphore sync;
+	struct brcmf_cfg80211_iscan_eloop el;
+	void *data;
+	s8 ioctl_buf[BRCMF_C_IOCTL_SMLEN];
+	s8 scan_buf[WL_ISCAN_BUF_MAX];
+};
+
+/* association inform */
+struct brcmf_cfg80211_connect_info {
+	u8 *req_ie;
+	s32 req_ie_len;
+	u8 *resp_ie;
+	s32 resp_ie_len;
+};
+
+/* assoc ie length */
+struct brcmf_cfg80211_assoc_ielen {
+	u32 req_len;
+	u32 resp_len;
+};
+
+/* wpa2 pmk list */
+struct brcmf_cfg80211_pmk_list {
+	pmkid_list_t pmkids;
+	pmkid_t foo[MAXPMKID - 1];
+};
+
+/* dongle private data of cfg80211 interface */
+struct brcmf_cfg80211_priv {
+	struct wireless_dev *wdev;	/* representing wl cfg80211 device */
+	struct brcmf_cfg80211_conf *conf;	/* dongle configuration */
+	struct cfg80211_scan_request *scan_request;	/* scan request
+							 object */
+	struct brcmf_cfg80211_event_loop el;	/* main event loop */
+	struct list_head eq_list;	/* used for event queue */
+	spinlock_t eq_lock;	/* for event queue synchronization */
+	struct mutex usr_sync;	/* maily for dongle up/down synchronization */
+	struct brcmf_scan_results *bss_list;	/* bss_list holding scanned
+						 ap information */
+	struct brcmf_scan_results *scan_results;
+	struct brcmf_cfg80211_scan_req *scan_req_int;	/* scan request object
+						 for internal purpose */
+	struct wl_cfg80211_bss_info *bss_info;	/* bss information for
+						 cfg80211 layer */
+	struct brcmf_cfg80211_ie ie;	/* information element object for
+					 internal purpose */
+	struct semaphore event_sync;	/* for synchronization of main event
+					 thread */
+	struct brcmf_cfg80211_profile *profile;	/* holding dongle profile */
+	struct brcmf_cfg80211_iscan_ctrl *iscan;	/* iscan controller */
+	struct brcmf_cfg80211_connect_info conn_info; /* association info */
+	struct brcmf_cfg80211_pmk_list *pmk_list;	/* wpa2 pmk list */
+	struct task_struct *event_tsk;	/* task of main event handler thread */
+	unsigned long status;		/* current dongle status */
+	void *pub;
+	u32 channel;		/* current channel */
+	bool iscan_on;		/* iscan on/off switch */
+	bool iscan_kickstart;	/* indicate iscan already started */
+	bool active_scan;	/* current scan mode */
+	bool ibss_starter;	/* indicates this sta is ibss starter */
+	bool link_up;		/* link/connection up flag */
+	bool pwr_save;		/* indicate whether dongle to support
+					 power save mode */
+	bool dongle_up;		/* indicate whether dongle up or not */
+	bool roam_on;		/* on/off switch for dongle self-roaming */
+	bool scan_tried;	/* indicates if first scan attempted */
+	u8 *ioctl_buf;	/* ioctl buffer */
+	u8 *extra_buf;	/* maily to grab assoc information */
+	struct dentry *debugfsdir;
+	u8 ci[0] __attribute__ ((__aligned__(NETDEV_ALIGN)));
+};
+
+#define cfg_to_wiphy(w) (w->wdev->wiphy)
+#define wiphy_to_cfg(w) ((struct brcmf_cfg80211_priv *)(wiphy_priv(w)))
+#define cfg_to_wdev(w) (w->wdev)
+#define wdev_to_cfg(w) ((struct brcmf_cfg80211_priv *)(wdev_priv(w)))
+#define cfg_to_ndev(w) (w->wdev->netdev)
+#define ndev_to_cfg(n) (wdev_to_cfg(n->ieee80211_ptr))
+#define iscan_to_cfg(i) ((struct brcmf_cfg80211_priv *)(i->data))
+#define cfg_to_iscan(w) (w->iscan)
+#define cfg_to_conn(w) (&w->conn_info)
+
+static inline struct brcmf_bss_info *next_bss(struct brcmf_scan_results *list,
+					   struct brcmf_bss_info *bss)
+{
+	return bss = bss ?
+		(struct brcmf_bss_info *)((unsigned long)bss +
+				       le32_to_cpu(bss->length)) :
+		list->bss_info;
+}
+
+#define for_each_bss(list, bss, __i)	\
+	for (__i = 0; __i < list->count && __i < WL_AP_MAX; __i++, bss = next_bss(list, bss))
+
+extern s32 brcmf_cfg80211_attach(struct net_device *ndev, void *data);
+extern void brcmf_cfg80211_detach(void);
+/* event handler from dongle */
+extern void brcmf_cfg80211_event(struct net_device *ndev,
+				 const struct brcmf_event_msg *e, void *data);
+extern void brcmf_cfg80211_sdio_func(void *func); /* set sdio function info */
+extern struct sdio_func *brcmf_cfg80211_get_sdio_func(void);
+extern s32 brcmf_cfg80211_up(void);	/* dongle up */
+extern s32 brcmf_cfg80211_down(void);	/* dongle down */
+
+#endif				/* _wl_cfg80211_h_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/Makefile b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
new file mode 100644
index 0000000..90be4e3
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/Makefile
@@ -0,0 +1,58 @@
+#
+# Makefile fragment for Broadcom 802.11n Networking Device Driver
+#
+# Copyright (c) 2010 Broadcom Corporation
+#
+# Permission to use, copy, modify, and/or distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+# SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+# OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+# CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+ccflags-y :=				\
+	-DWLC_HIGH				\
+	-DWLC_LOW				\
+	-DSTA					\
+	-DWME					\
+	-DWL11N					\
+	-DDBAND					\
+	-DBCMNVRAMR				\
+	-Idrivers/net/wireless/brcm80211/brcmsmac \
+	-Idrivers/net/wireless/brcm80211/brcmsmac/phy \
+	-Idrivers/net/wireless/brcm80211/include
+
+BRCMSMAC_OFILES := \
+	mac80211_if.o \
+	ucode_loader.o \
+	alloc.o \
+	ampdu.o \
+	antsel.o \
+	bmac.o \
+	channel.o \
+	main.o \
+	phy_shim.o \
+	pmu.o \
+	rate.o \
+	stf.o \
+	aiutils.o \
+	phy/phy_cmn.o \
+	phy/phy_lcn.o \
+	phy/phy_n.o \
+	phy/phytbl_lcn.o \
+	phy/phytbl_n.o \
+	phy/phy_qmath.o \
+	otp.o \
+	srom.o \
+	dma.o \
+	nicpci.o
+
+MODULEPFX := brcmsmac
+
+obj-$(CONFIG_BRCMSMAC)	+= $(MODULEPFX).o
+$(MODULEPFX)-objs	= $(BRCMSMAC_OFILES)
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
new file mode 100644
index 0000000..a25901e
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.c
@@ -0,0 +1,2279 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#include <defs.h>
+#include <chipcommon.h>
+#include <brcmu_utils.h>
+#include <brcm_hw_ids.h>
+#include "types.h"
+#include "pub.h"
+#include "pmu.h"
+#include "srom.h"
+#include "nicpci.h"
+#include "aiutils.h"
+
+/* slow_clk_ctl */
+#define SCC_SS_MASK		0x00000007	/* slow clock source mask */
+#define	SCC_SS_LPO		0x00000000	/* source of slow clock is LPO */
+#define	SCC_SS_XTAL		0x00000001	/* source of slow clock is crystal */
+#define	SCC_SS_PCI		0x00000002	/* source of slow clock is PCI */
+#define SCC_LF			0x00000200	/* LPOFreqSel, 1: 160Khz, 0: 32KHz */
+#define SCC_LP			0x00000400	/* LPOPowerDown, 1: LPO is disabled,
+						 * 0: LPO is enabled
+						 */
+#define SCC_FS			0x00000800	/* ForceSlowClk, 1: sb/cores running on slow clock,
+						 * 0: power logic control
+						 */
+#define SCC_IP			0x00001000	/* IgnorePllOffReq, 1/0: power logic ignores/honors
+						 * PLL clock disable requests from core
+						 */
+#define SCC_XC			0x00002000	/* XtalControlEn, 1/0: power logic does/doesn't
+						 * disable crystal when appropriate
+						 */
+#define SCC_XP			0x00004000	/* XtalPU (RO), 1/0: crystal running/disabled */
+#define SCC_CD_MASK		0xffff0000	/* ClockDivider (SlowClk = 1/(4+divisor)) */
+#define SCC_CD_SHIFT		16
+
+/* system_clk_ctl */
+#define	SYCC_IE			0x00000001	/* ILPen: Enable Idle Low Power */
+#define	SYCC_AE			0x00000002	/* ALPen: Enable Active Low Power */
+#define	SYCC_FP			0x00000004	/* ForcePLLOn */
+#define	SYCC_AR			0x00000008	/* Force ALP (or HT if ALPen is not set */
+#define	SYCC_HR			0x00000010	/* Force HT */
+#define SYCC_CD_MASK		0xffff0000	/* ClkDiv  (ILP = 1/(4 * (divisor + 1)) */
+#define SYCC_CD_SHIFT		16
+
+#define CST4329_SPROM_OTP_SEL_MASK	0x00000003
+#define CST4329_DEFCIS_SEL		0	/* OTP is powered up, use def. CIS, no SPROM */
+#define CST4329_SPROM_SEL		1	/* OTP is powered up, SPROM is present */
+#define CST4329_OTP_SEL			2	/* OTP is powered up, no SPROM */
+#define CST4329_OTP_PWRDN		3	/* OTP is powered down, SPROM is present */
+#define CST4329_SPI_SDIO_MODE_MASK	0x00000004
+#define CST4329_SPI_SDIO_MODE_SHIFT	2
+
+/* 43224 chip-specific ChipControl register bits */
+#define CCTRL43224_GPIO_TOGGLE          0x8000
+#define CCTRL_43224A0_12MA_LED_DRIVE    0x00F000F0	/* 12 mA drive strength */
+#define CCTRL_43224B0_12MA_LED_DRIVE    0xF0	/* 12 mA drive strength for later 43224s */
+
+/* 43236 Chip specific ChipStatus register bits */
+#define CST43236_SFLASH_MASK		0x00000040
+#define CST43236_OTP_MASK		0x00000080
+#define CST43236_HSIC_MASK		0x00000100	/* USB/HSIC */
+#define CST43236_BP_CLK			0x00000200	/* 120/96Mbps */
+#define CST43236_BOOT_MASK		0x00001800
+#define CST43236_BOOT_SHIFT		11
+#define CST43236_BOOT_FROM_SRAM		0	/* boot from SRAM, ARM in reset */
+#define CST43236_BOOT_FROM_ROM		1	/* boot from ROM */
+#define CST43236_BOOT_FROM_FLASH	2	/* boot from FLASH */
+#define CST43236_BOOT_FROM_INVALID	3
+
+/* 4331 chip-specific ChipControl register bits */
+#define CCTRL4331_BT_COEXIST		(1<<0)	/* 0 disable */
+#define CCTRL4331_SECI			(1<<1)	/* 0 SECI is disabled (JATG functional) */
+#define CCTRL4331_EXT_LNA		(1<<2)	/* 0 disable */
+#define CCTRL4331_SPROM_GPIO13_15       (1<<3)	/* sprom/gpio13-15 mux */
+#define CCTRL4331_EXTPA_EN		(1<<4)	/* 0 ext pa disable, 1 ext pa enabled */
+#define CCTRL4331_GPIOCLK_ON_SPROMCS	(1<<5)	/* set drive out GPIO_CLK on sprom_cs pin */
+#define CCTRL4331_PCIE_MDIO_ON_SPROMCS	(1<<6)	/* use sprom_cs pin as PCIE mdio interface */
+#define CCTRL4331_EXTPA_ON_GPIO2_5	(1<<7)	/* aband extpa will be at gpio2/5 and sprom_dout */
+#define CCTRL4331_OVR_PIPEAUXCLKEN	(1<<8)	/* override core control on pipe_AuxClkEnable */
+#define CCTRL4331_OVR_PIPEAUXPWRDOWN	(1<<9)	/* override core control on pipe_AuxPowerDown */
+#define CCTRL4331_PCIE_AUXCLKEN		(1<<10)	/* pcie_auxclkenable */
+#define CCTRL4331_PCIE_PIPE_PLLDOWN	(1<<11)	/* pcie_pipe_pllpowerdown */
+#define CCTRL4331_BT_SHD0_ON_GPIO4	(1<<16)	/* enable bt_shd0 at gpio4 */
+#define CCTRL4331_BT_SHD1_ON_GPIO5	(1<<17)	/* enable bt_shd1 at gpio5 */
+
+/* 4331 Chip specific ChipStatus register bits */
+#define	CST4331_XTAL_FREQ		0x00000001	/* crystal frequency 20/40Mhz */
+#define	CST4331_SPROM_PRESENT		0x00000002
+#define	CST4331_OTP_PRESENT		0x00000004
+#define	CST4331_LDO_RF			0x00000008
+#define	CST4331_LDO_PAR			0x00000010
+
+/* 4319 chip-specific ChipStatus register bits */
+#define	CST4319_SPI_CPULESSUSB		0x00000001
+#define	CST4319_SPI_CLK_POL		0x00000002
+#define	CST4319_SPI_CLK_PH		0x00000008
+#define	CST4319_SPROM_OTP_SEL_MASK	0x000000c0	/* gpio [7:6], SDIO CIS selection */
+#define	CST4319_SPROM_OTP_SEL_SHIFT	6
+#define	CST4319_DEFCIS_SEL		0x00000000	/* use default CIS, OTP is powered up */
+#define	CST4319_SPROM_SEL		0x00000040	/* use SPROM, OTP is powered up */
+#define	CST4319_OTP_SEL			0x00000080	/* use OTP, OTP is powered up */
+#define	CST4319_OTP_PWRDN		0x000000c0	/* use SPROM, OTP is powered down */
+#define	CST4319_SDIO_USB_MODE		0x00000100	/* gpio [8], sdio/usb mode */
+#define	CST4319_REMAP_SEL_MASK		0x00000600
+#define	CST4319_ILPDIV_EN		0x00000800
+#define	CST4319_XTAL_PD_POL		0x00001000
+#define	CST4319_LPO_SEL			0x00002000
+#define	CST4319_RES_INIT_MODE		0x0000c000
+#define	CST4319_PALDO_EXTPNP		0x00010000	/* PALDO is configured with external PNP */
+#define	CST4319_CBUCK_MODE_MASK		0x00060000
+#define CST4319_CBUCK_MODE_BURST	0x00020000
+#define CST4319_CBUCK_MODE_LPBURST	0x00060000
+#define	CST4319_RCAL_VALID		0x01000000
+#define	CST4319_RCAL_VALUE_MASK		0x3e000000
+#define	CST4319_RCAL_VALUE_SHIFT	25
+
+/* 4336 chip-specific ChipStatus register bits */
+#define	CST4336_SPI_MODE_MASK		0x00000001
+#define	CST4336_SPROM_PRESENT		0x00000002
+#define	CST4336_OTP_PRESENT		0x00000004
+#define	CST4336_ARMREMAP_0		0x00000008
+#define	CST4336_ILPDIV_EN_MASK		0x00000010
+#define	CST4336_ILPDIV_EN_SHIFT		4
+#define	CST4336_XTAL_PD_POL_MASK	0x00000020
+#define	CST4336_XTAL_PD_POL_SHIFT	5
+#define	CST4336_LPO_SEL_MASK		0x00000040
+#define	CST4336_LPO_SEL_SHIFT		6
+#define	CST4336_RES_INIT_MODE_MASK	0x00000180
+#define	CST4336_RES_INIT_MODE_SHIFT	7
+#define	CST4336_CBUCK_MODE_MASK		0x00000600
+#define	CST4336_CBUCK_MODE_SHIFT	9
+
+/* 4313 chip-specific ChipStatus register bits */
+#define	CST4313_SPROM_PRESENT			1
+#define	CST4313_OTP_PRESENT			2
+#define	CST4313_SPROM_OTP_SEL_MASK		0x00000002
+#define	CST4313_SPROM_OTP_SEL_SHIFT		0
+
+/* 4313 Chip specific ChipControl register bits */
+#define CCTRL_4313_12MA_LED_DRIVE    0x00000007	/* 12 mA drive strengh for later 4313 */
+
+#define BCM47162_DMP() ((sih->chip == BCM47162_CHIP_ID) && \
+		(sih->chiprev == 0) && \
+		(sii->coreid[sii->curidx] == MIPS74K_CORE_ID))
+
+/* Manufacturer Ids */
+#define	MFGID_ARM		0x43b
+#define	MFGID_BRCM		0x4bf
+#define	MFGID_MIPS		0x4a7
+
+/* Enumeration ROM registers */
+#define	ER_EROMENTRY		0x000
+#define	ER_REMAPCONTROL		0xe00
+#define	ER_REMAPSELECT		0xe04
+#define	ER_MASTERSELECT		0xe10
+#define	ER_ITCR			0xf00
+#define	ER_ITIP			0xf04
+
+/* Erom entries */
+#define	ER_TAG			0xe
+#define	ER_TAG1			0x6
+#define	ER_VALID		1
+#define	ER_CI			0
+#define	ER_MP			2
+#define	ER_ADD			4
+#define	ER_END			0xe
+#define	ER_BAD			0xffffffff
+
+/* EROM CompIdentA */
+#define	CIA_MFG_MASK		0xfff00000
+#define	CIA_MFG_SHIFT		20
+#define	CIA_CID_MASK		0x000fff00
+#define	CIA_CID_SHIFT		8
+#define	CIA_CCL_MASK		0x000000f0
+#define	CIA_CCL_SHIFT		4
+
+/* EROM CompIdentB */
+#define	CIB_REV_MASK		0xff000000
+#define	CIB_REV_SHIFT		24
+#define	CIB_NSW_MASK		0x00f80000
+#define	CIB_NSW_SHIFT		19
+#define	CIB_NMW_MASK		0x0007c000
+#define	CIB_NMW_SHIFT		14
+#define	CIB_NSP_MASK		0x00003e00
+#define	CIB_NSP_SHIFT		9
+#define	CIB_NMP_MASK		0x000001f0
+#define	CIB_NMP_SHIFT		4
+
+/* EROM AddrDesc */
+#define	AD_ADDR_MASK		0xfffff000
+#define	AD_SP_MASK		0x00000f00
+#define	AD_SP_SHIFT		8
+#define	AD_ST_MASK		0x000000c0
+#define	AD_ST_SHIFT		6
+#define	AD_ST_SLAVE		0x00000000
+#define	AD_ST_BRIDGE		0x00000040
+#define	AD_ST_SWRAP		0x00000080
+#define	AD_ST_MWRAP		0x000000c0
+#define	AD_SZ_MASK		0x00000030
+#define	AD_SZ_SHIFT		4
+#define	AD_SZ_4K		0x00000000
+#define	AD_SZ_8K		0x00000010
+#define	AD_SZ_16K		0x00000020
+#define	AD_SZ_SZD		0x00000030
+#define	AD_AG32			0x00000008
+#define	AD_ADDR_ALIGN		0x00000fff
+#define	AD_SZ_BASE		0x00001000	/* 4KB */
+
+/* EROM SizeDesc */
+#define	SD_SZ_MASK		0xfffff000
+#define	SD_SG32			0x00000008
+#define	SD_SZ_ALIGN		0x00000fff
+
+#define	PCI_CFG_GPIO_SCS	0x10	/* PCI config space bit 4 for 4306c0 slow clock source */
+#define PCI_CFG_GPIO_XTAL	0x40	/* PCI config space GPIO 14 for Xtal power-up */
+#define PCI_CFG_GPIO_PLL	0x80	/* PCI config space GPIO 15 for PLL power-down */
+
+/* power control defines */
+#define PLL_DELAY		150	/* us pll on delay */
+#define FREF_DELAY		200	/* us fref change delay */
+#define	XTAL_ON_DELAY		1000	/* us crystal power-on delay */
+
+/* resetctrl */
+#define	AIRC_RESET		1
+
+struct aidmp {
+	u32 oobselina30;	/* 0x000 */
+	u32 oobselina74;	/* 0x004 */
+	u32 PAD[6];
+	u32 oobselinb30;	/* 0x020 */
+	u32 oobselinb74;	/* 0x024 */
+	u32 PAD[6];
+	u32 oobselinc30;	/* 0x040 */
+	u32 oobselinc74;	/* 0x044 */
+	u32 PAD[6];
+	u32 oobselind30;	/* 0x060 */
+	u32 oobselind74;	/* 0x064 */
+	u32 PAD[38];
+	u32 oobselouta30;	/* 0x100 */
+	u32 oobselouta74;	/* 0x104 */
+	u32 PAD[6];
+	u32 oobseloutb30;	/* 0x120 */
+	u32 oobseloutb74;	/* 0x124 */
+	u32 PAD[6];
+	u32 oobseloutc30;	/* 0x140 */
+	u32 oobseloutc74;	/* 0x144 */
+	u32 PAD[6];
+	u32 oobseloutd30;	/* 0x160 */
+	u32 oobseloutd74;	/* 0x164 */
+	u32 PAD[38];
+	u32 oobsynca;	/* 0x200 */
+	u32 oobseloutaen;	/* 0x204 */
+	u32 PAD[6];
+	u32 oobsyncb;	/* 0x220 */
+	u32 oobseloutben;	/* 0x224 */
+	u32 PAD[6];
+	u32 oobsyncc;	/* 0x240 */
+	u32 oobseloutcen;	/* 0x244 */
+	u32 PAD[6];
+	u32 oobsyncd;	/* 0x260 */
+	u32 oobseloutden;	/* 0x264 */
+	u32 PAD[38];
+	u32 oobaextwidth;	/* 0x300 */
+	u32 oobainwidth;	/* 0x304 */
+	u32 oobaoutwidth;	/* 0x308 */
+	u32 PAD[5];
+	u32 oobbextwidth;	/* 0x320 */
+	u32 oobbinwidth;	/* 0x324 */
+	u32 oobboutwidth;	/* 0x328 */
+	u32 PAD[5];
+	u32 oobcextwidth;	/* 0x340 */
+	u32 oobcinwidth;	/* 0x344 */
+	u32 oobcoutwidth;	/* 0x348 */
+	u32 PAD[5];
+	u32 oobdextwidth;	/* 0x360 */
+	u32 oobdinwidth;	/* 0x364 */
+	u32 oobdoutwidth;	/* 0x368 */
+	u32 PAD[37];
+	u32 ioctrlset;	/* 0x400 */
+	u32 ioctrlclear;	/* 0x404 */
+	u32 ioctrl;		/* 0x408 */
+	u32 PAD[61];
+	u32 iostatus;	/* 0x500 */
+	u32 PAD[127];
+	u32 ioctrlwidth;	/* 0x700 */
+	u32 iostatuswidth;	/* 0x704 */
+	u32 PAD[62];
+	u32 resetctrl;	/* 0x800 */
+	u32 resetstatus;	/* 0x804 */
+	u32 resetreadid;	/* 0x808 */
+	u32 resetwriteid;	/* 0x80c */
+	u32 PAD[60];
+	u32 errlogctrl;	/* 0x900 */
+	u32 errlogdone;	/* 0x904 */
+	u32 errlogstatus;	/* 0x908 */
+	u32 errlogaddrlo;	/* 0x90c */
+	u32 errlogaddrhi;	/* 0x910 */
+	u32 errlogid;	/* 0x914 */
+	u32 errloguser;	/* 0x918 */
+	u32 errlogflags;	/* 0x91c */
+	u32 PAD[56];
+	u32 intstatus;	/* 0xa00 */
+	u32 PAD[127];
+	u32 config;		/* 0xe00 */
+	u32 PAD[63];
+	u32 itcr;		/* 0xf00 */
+	u32 PAD[3];
+	u32 itipooba;	/* 0xf10 */
+	u32 itipoobb;	/* 0xf14 */
+	u32 itipoobc;	/* 0xf18 */
+	u32 itipoobd;	/* 0xf1c */
+	u32 PAD[4];
+	u32 itipoobaout;	/* 0xf30 */
+	u32 itipoobbout;	/* 0xf34 */
+	u32 itipoobcout;	/* 0xf38 */
+	u32 itipoobdout;	/* 0xf3c */
+	u32 PAD[4];
+	u32 itopooba;	/* 0xf50 */
+	u32 itopoobb;	/* 0xf54 */
+	u32 itopoobc;	/* 0xf58 */
+	u32 itopoobd;	/* 0xf5c */
+	u32 PAD[4];
+	u32 itopoobain;	/* 0xf70 */
+	u32 itopoobbin;	/* 0xf74 */
+	u32 itopoobcin;	/* 0xf78 */
+	u32 itopoobdin;	/* 0xf7c */
+	u32 PAD[4];
+	u32 itopreset;	/* 0xf90 */
+	u32 PAD[15];
+	u32 peripherialid4;	/* 0xfd0 */
+	u32 peripherialid5;	/* 0xfd4 */
+	u32 peripherialid6;	/* 0xfd8 */
+	u32 peripherialid7;	/* 0xfdc */
+	u32 peripherialid0;	/* 0xfe0 */
+	u32 peripherialid1;	/* 0xfe4 */
+	u32 peripherialid2;	/* 0xfe8 */
+	u32 peripherialid3;	/* 0xfec */
+	u32 componentid0;	/* 0xff0 */
+	u32 componentid1;	/* 0xff4 */
+	u32 componentid2;	/* 0xff8 */
+	u32 componentid3;	/* 0xffc */
+};
+
+/* EROM parsing */
+
+static u32
+get_erom_ent(struct si_pub *sih, u32 **eromptr, u32 mask, u32 match)
+{
+	u32 ent;
+	uint inv = 0, nom = 0;
+
+	while (true) {
+		ent = R_REG(*eromptr);
+		(*eromptr)++;
+
+		if (mask == 0)
+			break;
+
+		if ((ent & ER_VALID) == 0) {
+			inv++;
+			continue;
+		}
+
+		if (ent == (ER_END | ER_VALID))
+			break;
+
+		if ((ent & mask) == match)
+			break;
+
+		nom++;
+	}
+
+	SI_VMSG(("%s: Returning ent 0x%08x\n", __func__, ent));
+	if (inv + nom) {
+		SI_VMSG(("  after %d invalid and %d non-matching entries\n",
+			 inv, nom));
+	}
+	return ent;
+}
+
+static u32
+get_asd(struct si_pub *sih, u32 **eromptr, uint sp, uint ad, uint st,
+	u32 *addrl, u32 *addrh, u32 *sizel, u32 *sizeh)
+{
+	u32 asd, sz, szd;
+
+	asd = get_erom_ent(sih, eromptr, ER_VALID, ER_VALID);
+	if (((asd & ER_TAG1) != ER_ADD) ||
+	    (((asd & AD_SP_MASK) >> AD_SP_SHIFT) != sp) ||
+	    ((asd & AD_ST_MASK) != st)) {
+		/* This is not what we want, "push" it back */
+		(*eromptr)--;
+		return 0;
+	}
+	*addrl = asd & AD_ADDR_MASK;
+	if (asd & AD_AG32)
+		*addrh = get_erom_ent(sih, eromptr, 0, 0);
+	else
+		*addrh = 0;
+	*sizeh = 0;
+	sz = asd & AD_SZ_MASK;
+	if (sz == AD_SZ_SZD) {
+		szd = get_erom_ent(sih, eromptr, 0, 0);
+		*sizel = szd & SD_SZ_MASK;
+		if (szd & SD_SG32)
+			*sizeh = get_erom_ent(sih, eromptr, 0, 0);
+	} else
+		*sizel = AD_SZ_BASE << (sz >> AD_SZ_SHIFT);
+
+	SI_VMSG(("  SP %d, ad %d: st = %d, 0x%08x_0x%08x @ 0x%08x_0x%08x\n",
+		 sp, ad, st, *sizeh, *sizel, *addrh, *addrl));
+
+	return asd;
+}
+
+static void ai_hwfixup(struct si_info *sii)
+{
+}
+
+/* parse the enumeration rom to identify all cores */
+void ai_scan(struct si_pub *sih, void *regs)
+{
+	struct si_info *sii = SI_INFO(sih);
+	chipcregs_t *cc = (chipcregs_t *) regs;
+	u32 erombase, *eromptr, *eromlim;
+
+	erombase = R_REG(&cc->eromptr);
+
+	switch (sih->bustype) {
+	case SI_BUS:
+		eromptr = (u32 *) REG_MAP(erombase, SI_CORE_SIZE);
+		break;
+
+	case PCI_BUS:
+		/* Set wrappers address */
+		sii->curwrap = (void *)((unsigned long)regs + SI_CORE_SIZE);
+
+		/* Now point the window at the erom */
+		pci_write_config_dword(sii->pbus, PCI_BAR0_WIN, erombase);
+		eromptr = regs;
+		break;
+
+	case SPI_BUS:
+	case SDIO_BUS:
+		eromptr = (u32 *)(unsigned long)erombase;
+		break;
+
+	default:
+		SI_ERROR(("Don't know how to do AXI enumertion on bus %d\n",
+			  sih->bustype));
+		return;
+	}
+	eromlim = eromptr + (ER_REMAPCONTROL / sizeof(u32));
+
+	SI_VMSG(("ai_scan: regs = 0x%p, erombase = 0x%08x, eromptr = 0x%p, eromlim = 0x%p\n", regs, erombase, eromptr, eromlim));
+	while (eromptr < eromlim) {
+		u32 cia, cib, cid, mfg, crev, nmw, nsw, nmp, nsp;
+		u32 mpd, asd, addrl, addrh, sizel, sizeh;
+		u32 *base;
+		uint i, j, idx;
+		bool br;
+
+		br = false;
+
+		/* Grok a component */
+		cia = get_erom_ent(sih, &eromptr, ER_TAG, ER_CI);
+		if (cia == (ER_END | ER_VALID)) {
+			SI_VMSG(("Found END of erom after %d cores\n",
+				 sii->numcores));
+			ai_hwfixup(sii);
+			return;
+		}
+		base = eromptr - 1;
+		cib = get_erom_ent(sih, &eromptr, 0, 0);
+
+		if ((cib & ER_TAG) != ER_CI) {
+			SI_ERROR(("CIA not followed by CIB\n"));
+			goto error;
+		}
+
+		cid = (cia & CIA_CID_MASK) >> CIA_CID_SHIFT;
+		mfg = (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+		crev = (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+		nmw = (cib & CIB_NMW_MASK) >> CIB_NMW_SHIFT;
+		nsw = (cib & CIB_NSW_MASK) >> CIB_NSW_SHIFT;
+		nmp = (cib & CIB_NMP_MASK) >> CIB_NMP_SHIFT;
+		nsp = (cib & CIB_NSP_MASK) >> CIB_NSP_SHIFT;
+
+		SI_VMSG(("Found component 0x%04x/0x%04x rev %d at erom addr 0x%p, with nmw = %d, " "nsw = %d, nmp = %d & nsp = %d\n", mfg, cid, crev, base, nmw, nsw, nmp, nsp));
+
+		if (((mfg == MFGID_ARM) && (cid == DEF_AI_COMP)) || (nsp == 0))
+			continue;
+		if ((nmw + nsw == 0)) {
+			/* A component which is not a core */
+			if (cid == OOB_ROUTER_CORE_ID) {
+				asd = get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE,
+					      &addrl, &addrh, &sizel, &sizeh);
+				if (asd != 0) {
+					sii->oob_router = addrl;
+				}
+			}
+			continue;
+		}
+
+		idx = sii->numcores;
+/*		sii->eromptr[idx] = base; */
+		sii->cia[idx] = cia;
+		sii->cib[idx] = cib;
+		sii->coreid[idx] = cid;
+
+		for (i = 0; i < nmp; i++) {
+			mpd = get_erom_ent(sih, &eromptr, ER_VALID, ER_VALID);
+			if ((mpd & ER_TAG) != ER_MP) {
+				SI_ERROR(("Not enough MP entries for component 0x%x\n", cid));
+				goto error;
+			}
+			SI_VMSG(("  Master port %d, mp: %d id: %d\n", i,
+				 (mpd & MPD_MP_MASK) >> MPD_MP_SHIFT,
+				 (mpd & MPD_MUI_MASK) >> MPD_MUI_SHIFT));
+		}
+
+		/* First Slave Address Descriptor should be port 0:
+		 * the main register space for the core
+		 */
+		asd =
+		    get_asd(sih, &eromptr, 0, 0, AD_ST_SLAVE, &addrl, &addrh,
+			    &sizel, &sizeh);
+		if (asd == 0) {
+			/* Try again to see if it is a bridge */
+			asd =
+			    get_asd(sih, &eromptr, 0, 0, AD_ST_BRIDGE, &addrl,
+				    &addrh, &sizel, &sizeh);
+			if (asd != 0)
+				br = true;
+			else if ((addrh != 0) || (sizeh != 0)
+				 || (sizel != SI_CORE_SIZE)) {
+				SI_ERROR(("First Slave ASD for core 0x%04x malformed " "(0x%08x)\n", cid, asd));
+				goto error;
+			}
+		}
+		sii->coresba[idx] = addrl;
+		sii->coresba_size[idx] = sizel;
+		/* Get any more ASDs in port 0 */
+		j = 1;
+		do {
+			asd =
+			    get_asd(sih, &eromptr, 0, j, AD_ST_SLAVE, &addrl,
+				    &addrh, &sizel, &sizeh);
+			if ((asd != 0) && (j == 1) && (sizel == SI_CORE_SIZE)) {
+				sii->coresba2[idx] = addrl;
+				sii->coresba2_size[idx] = sizel;
+			}
+			j++;
+		} while (asd != 0);
+
+		/* Go through the ASDs for other slave ports */
+		for (i = 1; i < nsp; i++) {
+			j = 0;
+			do {
+				asd =
+				    get_asd(sih, &eromptr, i, j++, AD_ST_SLAVE,
+					    &addrl, &addrh, &sizel, &sizeh);
+			} while (asd != 0);
+			if (j == 0) {
+				SI_ERROR((" SP %d has no address descriptors\n",
+					  i));
+				goto error;
+			}
+		}
+
+		/* Now get master wrappers */
+		for (i = 0; i < nmw; i++) {
+			asd =
+			    get_asd(sih, &eromptr, i, 0, AD_ST_MWRAP, &addrl,
+				    &addrh, &sizel, &sizeh);
+			if (asd == 0) {
+				SI_ERROR(("Missing descriptor for MW %d\n", i));
+				goto error;
+			}
+			if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+				SI_ERROR(("Master wrapper %d is not 4KB\n", i));
+				goto error;
+			}
+			if (i == 0)
+				sii->wrapba[idx] = addrl;
+		}
+
+		/* And finally slave wrappers */
+		for (i = 0; i < nsw; i++) {
+			uint fwp = (nsp == 1) ? 0 : 1;
+			asd =
+			    get_asd(sih, &eromptr, fwp + i, 0, AD_ST_SWRAP,
+				    &addrl, &addrh, &sizel, &sizeh);
+			if (asd == 0) {
+				SI_ERROR(("Missing descriptor for SW %d\n", i));
+				goto error;
+			}
+			if ((sizeh != 0) || (sizel != SI_CORE_SIZE)) {
+				SI_ERROR(("Slave wrapper %d is not 4KB\n", i));
+				goto error;
+			}
+			if ((nmw == 0) && (i == 0))
+				sii->wrapba[idx] = addrl;
+		}
+
+		/* Don't record bridges */
+		if (br)
+			continue;
+
+		/* Done with core */
+		sii->numcores++;
+	}
+
+	SI_ERROR(("Reached end of erom without finding END"));
+
+ error:
+	sii->numcores = 0;
+	return;
+}
+
+/* This function changes the logical "focus" to the indicated core.
+ * Return the current core's virtual address.
+ */
+void *ai_setcoreidx(struct si_pub *sih, uint coreidx)
+{
+	struct si_info *sii = SI_INFO(sih);
+	u32 addr = sii->coresba[coreidx];
+	u32 wrap = sii->wrapba[coreidx];
+	void *regs;
+
+	if (coreidx >= sii->numcores)
+		return NULL;
+
+	switch (sih->bustype) {
+	case SI_BUS:
+		/* map new one */
+		if (!sii->regs[coreidx]) {
+			sii->regs[coreidx] = REG_MAP(addr, SI_CORE_SIZE);
+		}
+		sii->curmap = regs = sii->regs[coreidx];
+		if (!sii->wrappers[coreidx]) {
+			sii->wrappers[coreidx] = REG_MAP(wrap, SI_CORE_SIZE);
+		}
+		sii->curwrap = sii->wrappers[coreidx];
+		break;
+
+	case PCI_BUS:
+		/* point bar0 window */
+		pci_write_config_dword(sii->pbus, PCI_BAR0_WIN, addr);
+		regs = sii->curmap;
+		/* point bar0 2nd 4KB window */
+		pci_write_config_dword(sii->pbus, PCI_BAR0_WIN2, wrap);
+		break;
+
+	case SPI_BUS:
+	case SDIO_BUS:
+		sii->curmap = regs = (void *)(unsigned long)addr;
+		sii->curwrap = (void *)(unsigned long)wrap;
+		break;
+
+	default:
+		regs = NULL;
+		break;
+	}
+
+	sii->curmap = regs;
+	sii->curidx = coreidx;
+
+	return regs;
+}
+
+/* Return the number of address spaces in current core */
+int ai_numaddrspaces(struct si_pub *sih)
+{
+	return 2;
+}
+
+/* Return the address of the nth address space in the current core */
+u32 ai_addrspace(struct si_pub *sih, uint asidx)
+{
+	struct si_info *sii;
+	uint cidx;
+
+	sii = SI_INFO(sih);
+	cidx = sii->curidx;
+
+	if (asidx == 0)
+		return sii->coresba[cidx];
+	else if (asidx == 1)
+		return sii->coresba2[cidx];
+	else {
+		SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", __func__, asidx));
+		return 0;
+	}
+}
+
+/* Return the size of the nth address space in the current core */
+u32 ai_addrspacesize(struct si_pub *sih, uint asidx)
+{
+	struct si_info *sii;
+	uint cidx;
+
+	sii = SI_INFO(sih);
+	cidx = sii->curidx;
+
+	if (asidx == 0)
+		return sii->coresba_size[cidx];
+	else if (asidx == 1)
+		return sii->coresba2_size[cidx];
+	else {
+		SI_ERROR(("%s: Need to parse the erom again to find addr space %d\n", __func__, asidx));
+		return 0;
+	}
+}
+
+uint ai_flag(struct si_pub *sih)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+
+	sii = SI_INFO(sih);
+	if (BCM47162_DMP()) {
+		SI_ERROR(("%s: Attempting to read MIPS DMP registers on 47162a0", __func__));
+		return sii->curidx;
+	}
+	ai = sii->curwrap;
+
+	return R_REG(&ai->oobselouta30) & 0x1f;
+}
+
+void ai_setint(struct si_pub *sih, int siflag)
+{
+}
+
+uint ai_corevendor(struct si_pub *sih)
+{
+	struct si_info *sii;
+	u32 cia;
+
+	sii = SI_INFO(sih);
+	cia = sii->cia[sii->curidx];
+	return (cia & CIA_MFG_MASK) >> CIA_MFG_SHIFT;
+}
+
+uint ai_corerev(struct si_pub *sih)
+{
+	struct si_info *sii;
+	u32 cib;
+
+	sii = SI_INFO(sih);
+	cib = sii->cib[sii->curidx];
+	return (cib & CIB_REV_MASK) >> CIB_REV_SHIFT;
+}
+
+bool ai_iscoreup(struct si_pub *sih)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+
+	sii = SI_INFO(sih);
+	ai = sii->curwrap;
+
+	return (((R_REG(&ai->ioctrl) & (SICF_FGC | SICF_CLOCK_EN)) ==
+		 SICF_CLOCK_EN)
+		&& ((R_REG(&ai->resetctrl) & AIRC_RESET) == 0));
+}
+
+void ai_core_cflags_wo(struct si_pub *sih, u32 mask, u32 val)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+	u32 w;
+
+	sii = SI_INFO(sih);
+
+	if (BCM47162_DMP()) {
+		SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0",
+			  __func__));
+		return;
+	}
+
+	ai = sii->curwrap;
+
+	if (mask || val) {
+		w = ((R_REG(&ai->ioctrl) & ~mask) | val);
+		W_REG(&ai->ioctrl, w);
+	}
+}
+
+u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+	u32 w;
+
+	sii = SI_INFO(sih);
+	if (BCM47162_DMP()) {
+		SI_ERROR(("%s: Accessing MIPS DMP register (ioctrl) on 47162a0",
+			  __func__));
+		return 0;
+	}
+
+	ai = sii->curwrap;
+
+	if (mask || val) {
+		w = ((R_REG(&ai->ioctrl) & ~mask) | val);
+		W_REG(&ai->ioctrl, w);
+	}
+
+	return R_REG(&ai->ioctrl);
+}
+
+u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+	u32 w;
+
+	sii = SI_INFO(sih);
+	if (BCM47162_DMP()) {
+		SI_ERROR(("%s: Accessing MIPS DMP register (iostatus) on 47162a0", __func__));
+		return 0;
+	}
+
+	ai = sii->curwrap;
+
+	if (mask || val) {
+		w = ((R_REG(&ai->iostatus) & ~mask) | val);
+		W_REG(&ai->iostatus, w);
+	}
+
+	return R_REG(&ai->iostatus);
+}
+
+/* *************** from siutils.c ************** */
+/* local prototypes */
+static struct si_info *ai_doattach(struct si_info *sii, void *regs,
+			      uint bustype, void *sdh, char **vars,
+			      uint *varsz);
+static bool ai_buscore_prep(struct si_info *sii, uint bustype);
+static bool ai_buscore_setup(struct si_info *sii, chipcregs_t *cc, uint bustype,
+			     u32 savewin, uint *origidx, void *regs);
+static void ai_nvram_process(struct si_info *sii, char *pvars);
+
+/* dev path concatenation util */
+static char *ai_devpathvar(struct si_pub *sih, char *var, int len,
+			   const char *name);
+static bool _ai_clkctl_cc(struct si_info *sii, uint mode);
+static bool ai_ispcie(struct si_info *sii);
+
+/* global variable to indicate reservation/release of gpio's */
+static u32 ai_gpioreservation;
+
+/*
+ * Allocate a si handle.
+ * devid - pci device id (used to determine chip#)
+ * osh - opaque OS handle
+ * regs - virtual address of initial core registers
+ * bustype - pci/sb/sdio/etc
+ * vars - pointer to a pointer area for "environment" variables
+ * varsz - pointer to int to return the size of the vars
+ */
+struct si_pub *ai_attach(void *regs, uint bustype,
+		void *sdh, char **vars, uint *varsz)
+{
+	struct si_info *sii;
+
+	/* alloc struct si_info */
+	sii = kmalloc(sizeof(struct si_info), GFP_ATOMIC);
+	if (sii == NULL) {
+		SI_ERROR(("si_attach: malloc failed!\n"));
+		return NULL;
+	}
+
+	if (ai_doattach(sii, regs, bustype, sdh, vars, varsz) ==
+	    NULL) {
+		kfree(sii);
+		return NULL;
+	}
+	sii->vars = vars ? *vars : NULL;
+	sii->varsz = varsz ? *varsz : 0;
+
+	return (struct si_pub *) sii;
+}
+
+/* global kernel resource */
+static struct si_info ksii;
+
+static bool ai_buscore_prep(struct si_info *sii, uint bustype)
+{
+	/* kludge to enable the clock on the 4306 which lacks a slowclock */
+	if (bustype == PCI_BUS && !ai_ispcie(sii))
+		ai_clkctl_xtal(&sii->pub, XTAL | PLL, ON);
+	return true;
+}
+
+static bool ai_buscore_setup(struct si_info *sii, chipcregs_t *cc, uint bustype,
+			     u32 savewin, uint *origidx, void *regs)
+{
+	bool pci, pcie;
+	uint i;
+	uint pciidx, pcieidx, pcirev, pcierev;
+
+	cc = ai_setcoreidx(&sii->pub, SI_CC_IDX);
+
+	/* get chipcommon rev */
+	sii->pub.ccrev = (int)ai_corerev(&sii->pub);
+
+	/* get chipcommon chipstatus */
+	if (sii->pub.ccrev >= 11)
+		sii->pub.chipst = R_REG(&cc->chipstatus);
+
+	/* get chipcommon capabilites */
+	sii->pub.cccaps = R_REG(&cc->capabilities);
+	/* get chipcommon extended capabilities */
+
+	if (sii->pub.ccrev >= 35)
+		sii->pub.cccaps_ext = R_REG(&cc->capabilities_ext);
+
+	/* get pmu rev and caps */
+	if (sii->pub.cccaps & CC_CAP_PMU) {
+		sii->pub.pmucaps = R_REG(&cc->pmucapabilities);
+		sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK;
+	}
+
+	/* figure out bus/orignal core idx */
+	sii->pub.buscoretype = NODEV_CORE_ID;
+	sii->pub.buscorerev = NOREV;
+	sii->pub.buscoreidx = BADIDX;
+
+	pci = pcie = false;
+	pcirev = pcierev = NOREV;
+	pciidx = pcieidx = BADIDX;
+
+	for (i = 0; i < sii->numcores; i++) {
+		uint cid, crev;
+
+		ai_setcoreidx(&sii->pub, i);
+		cid = ai_coreid(&sii->pub);
+		crev = ai_corerev(&sii->pub);
+
+		/* Display cores found */
+		SI_VMSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n",
+			 i, cid, crev, sii->coresba[i], sii->regs[i]));
+
+		if (bustype == PCI_BUS) {
+			if (cid == PCI_CORE_ID) {
+				pciidx = i;
+				pcirev = crev;
+				pci = true;
+			} else if (cid == PCIE_CORE_ID) {
+				pcieidx = i;
+				pcierev = crev;
+				pcie = true;
+			}
+		}
+
+		/* find the core idx before entering this func. */
+		if ((savewin && (savewin == sii->coresba[i])) ||
+		    (regs == sii->regs[i]))
+			*origidx = i;
+	}
+
+	if (pci && pcie) {
+		if (ai_ispcie(sii))
+			pci = false;
+		else
+			pcie = false;
+	}
+	if (pci) {
+		sii->pub.buscoretype = PCI_CORE_ID;
+		sii->pub.buscorerev = pcirev;
+		sii->pub.buscoreidx = pciidx;
+	} else if (pcie) {
+		sii->pub.buscoretype = PCIE_CORE_ID;
+		sii->pub.buscorerev = pcierev;
+		sii->pub.buscoreidx = pcieidx;
+	}
+
+	SI_VMSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx,
+		 sii->pub.buscoretype, sii->pub.buscorerev));
+
+	/* fixup necessary chip/core configurations */
+	if (sii->pub.bustype == PCI_BUS) {
+		if (SI_FAST(sii)) {
+			if (!sii->pch) {
+				sii->pch = (void *)pcicore_init(
+					&sii->pub, sii->pbus,
+					(void *)PCIEREGS(sii));
+				if (sii->pch == NULL)
+					return false;
+			}
+		}
+		if (ai_pci_fixcfg(&sii->pub)) {
+			SI_ERROR(("si_doattach: si_pci_fixcfg failed\n"));
+			return false;
+		}
+	}
+
+	/* return to the original core */
+	ai_setcoreidx(&sii->pub, *origidx);
+
+	return true;
+}
+
+static __used void ai_nvram_process(struct si_info *sii, char *pvars)
+{
+	uint w = 0;
+
+	/* get boardtype and boardrev */
+	switch (sii->pub.bustype) {
+	case PCI_BUS:
+		/* do a pci config read to get subsystem id and subvendor id */
+		pci_read_config_dword(sii->pbus, PCI_SUBSYSTEM_VENDOR_ID, &w);
+		/* Let nvram variables override subsystem Vend/ID */
+		sii->pub.boardvendor = (u16)ai_getdevpathintvar(&sii->pub,
+			"boardvendor");
+		if (sii->pub.boardvendor == 0)
+			sii->pub.boardvendor = w & 0xffff;
+		else
+			SI_ERROR(("Overriding boardvendor: 0x%x instead of "
+				  "0x%x\n", sii->pub.boardvendor, w & 0xffff));
+		sii->pub.boardtype = (u16)ai_getdevpathintvar(&sii->pub,
+			"boardtype");
+		if (sii->pub.boardtype == 0)
+			sii->pub.boardtype = (w >> 16) & 0xffff;
+		else
+			SI_ERROR(("Overriding boardtype: 0x%x instead of 0x%x\n"
+				  , sii->pub.boardtype, (w >> 16) & 0xffff));
+		break;
+
+		sii->pub.boardvendor = getintvar(pvars, "manfid");
+		sii->pub.boardtype = getintvar(pvars, "prodid");
+		break;
+
+	case SI_BUS:
+	case JTAG_BUS:
+		sii->pub.boardvendor = PCI_VENDOR_ID_BROADCOM;
+		sii->pub.boardtype = getintvar(pvars, "prodid");
+		if (pvars == NULL || (sii->pub.boardtype == 0)) {
+			sii->pub.boardtype = getintvar(NULL, "boardtype");
+			if (sii->pub.boardtype == 0)
+				sii->pub.boardtype = 0xffff;
+		}
+		break;
+	}
+
+	if (sii->pub.boardtype == 0) {
+		SI_ERROR(("si_doattach: unknown board type\n"));
+	}
+
+	sii->pub.boardflags = getintvar(pvars, "boardflags");
+}
+
+static struct si_info *ai_doattach(struct si_info *sii,
+			      void *regs, uint bustype, void *pbus,
+			      char **vars, uint *varsz)
+{
+	struct si_pub *sih = &sii->pub;
+	u32 w, savewin;
+	chipcregs_t *cc;
+	char *pvars = NULL;
+	uint socitype;
+	uint origidx;
+
+	memset((unsigned char *) sii, 0, sizeof(struct si_info));
+
+	savewin = 0;
+
+	sih->buscoreidx = BADIDX;
+
+	sii->curmap = regs;
+	sii->pbus = pbus;
+
+	/* check to see if we are a si core mimic'ing a pci core */
+	if (bustype == PCI_BUS) {
+		pci_read_config_dword(sii->pbus, PCI_SPROM_CONTROL,  &w);
+		if (w == 0xffffffff) {
+			SI_ERROR(("%s: incoming bus is PCI but it's a lie, "
+				" switching to SI devid:0x%x\n",
+				__func__, devid));
+			bustype = SI_BUS;
+		}
+	}
+
+	/* find Chipcommon address */
+	if (bustype == PCI_BUS) {
+		pci_read_config_dword(sii->pbus, PCI_BAR0_WIN, &savewin);
+		if (!GOODCOREADDR(savewin, SI_ENUM_BASE))
+			savewin = SI_ENUM_BASE;
+		pci_write_config_dword(sii->pbus, PCI_BAR0_WIN,
+				       SI_ENUM_BASE);
+		cc = (chipcregs_t *) regs;
+	} else {
+		cc = (chipcregs_t *) REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+	}
+
+	sih->bustype = bustype;
+
+	/* bus/core/clk setup for register access */
+	if (!ai_buscore_prep(sii, bustype)) {
+		SI_ERROR(("si_doattach: si_core_clk_prep failed %d\n",
+			  bustype));
+		return NULL;
+	}
+
+	/*
+	 * ChipID recognition.
+	 *   We assume we can read chipid at offset 0 from the regs arg.
+	 *   If we add other chiptypes (or if we need to support old sdio
+	 *   hosts w/o chipcommon), some way of recognizing them needs to
+	 *   be added here.
+	 */
+	w = R_REG(&cc->chipid);
+	socitype = (w & CID_TYPE_MASK) >> CID_TYPE_SHIFT;
+	/* Might as wll fill in chip id rev & pkg */
+	sih->chip = w & CID_ID_MASK;
+	sih->chiprev = (w & CID_REV_MASK) >> CID_REV_SHIFT;
+	sih->chippkg = (w & CID_PKG_MASK) >> CID_PKG_SHIFT;
+
+	sih->issim = IS_SIM(sih->chippkg);
+
+	/* scan for cores */
+	if (socitype == SOCI_AI) {
+		SI_MSG(("Found chip type AI (0x%08x)\n", w));
+		/* pass chipc address instead of original core base */
+		ai_scan(&sii->pub, (void *)cc);
+	} else {
+		SI_ERROR(("Found chip of unknown type (0x%08x)\n", w));
+		return NULL;
+	}
+	/* no cores found, bail out */
+	if (sii->numcores == 0) {
+		SI_ERROR(("si_doattach: could not find any cores\n"));
+		return NULL;
+	}
+	/* bus/core/clk setup */
+	origidx = SI_CC_IDX;
+	if (!ai_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) {
+		SI_ERROR(("si_doattach: si_buscore_setup failed\n"));
+		goto exit;
+	}
+
+	/* Init nvram from sprom/otp if they exist */
+	if (srom_var_init
+	    (&sii->pub, bustype, regs, vars, varsz)) {
+		SI_ERROR(("si_doattach: srom_var_init failed: bad srom\n"));
+		goto exit;
+	}
+	pvars = vars ? *vars : NULL;
+	ai_nvram_process(sii, pvars);
+
+	/* === NVRAM, clock is ready === */
+	cc = (chipcregs_t *) ai_setcore(sih, CC_CORE_ID, 0);
+	W_REG(&cc->gpiopullup, 0);
+	W_REG(&cc->gpiopulldown, 0);
+	ai_setcoreidx(sih, origidx);
+
+	/* PMU specific initializations */
+	if (PMUCTL_ENAB(sih)) {
+		u32 xtalfreq;
+		si_pmu_init(sih);
+		si_pmu_chip_init(sih);
+		xtalfreq = getintvar(pvars, "xtalfreq");
+		/* If xtalfreq var not available, try to measure it */
+		if (xtalfreq == 0)
+			xtalfreq = si_pmu_measure_alpclk(sih);
+		si_pmu_pll_init(sih, xtalfreq);
+		si_pmu_res_init(sih);
+		si_pmu_swreg_init(sih);
+	}
+
+	/* setup the GPIO based LED powersave register */
+	w = getintvar(pvars, "leddc");
+	if (w == 0)
+		w = DEFAULT_GPIOTIMERVAL;
+	ai_corereg(sih, SI_CC_IDX, offsetof(chipcregs_t, gpiotimerval), ~0, w);
+
+	if (PCIE(sii)) {
+		pcicore_attach(sii->pch, pvars, SI_DOATTACH);
+	}
+
+	if (sih->chip == BCM43224_CHIP_ID) {
+		/*
+		 * enable 12 mA drive strenth for 43224 and
+		 * set chipControl register bit 15
+		 */
+		if (sih->chiprev == 0) {
+			SI_MSG(("Applying 43224A0 WARs\n"));
+			ai_corereg(sih, SI_CC_IDX,
+				   offsetof(chipcregs_t, chipcontrol),
+				   CCTRL43224_GPIO_TOGGLE,
+				   CCTRL43224_GPIO_TOGGLE);
+			si_pmu_chipcontrol(sih, 0, CCTRL_43224A0_12MA_LED_DRIVE,
+					   CCTRL_43224A0_12MA_LED_DRIVE);
+		}
+		if (sih->chiprev >= 1) {
+			SI_MSG(("Applying 43224B0+ WARs\n"));
+			si_pmu_chipcontrol(sih, 0, CCTRL_43224B0_12MA_LED_DRIVE,
+					   CCTRL_43224B0_12MA_LED_DRIVE);
+		}
+	}
+
+	if (sih->chip == BCM4313_CHIP_ID) {
+		/*
+		 * enable 12 mA drive strenth for 4313 and
+		 * set chipControl register bit 1
+		 */
+		SI_MSG(("Applying 4313 WARs\n"));
+		si_pmu_chipcontrol(sih, 0, CCTRL_4313_12MA_LED_DRIVE,
+				   CCTRL_4313_12MA_LED_DRIVE);
+	}
+
+	return sii;
+ exit:
+	if (sih->bustype == PCI_BUS) {
+		if (sii->pch)
+			pcicore_deinit(sii->pch);
+		sii->pch = NULL;
+	}
+
+	return NULL;
+}
+
+/* may be called with core in reset */
+void ai_detach(struct si_pub *sih)
+{
+	struct si_info *sii;
+	uint idx;
+
+	struct si_pub *si_local = NULL;
+	memcpy(&si_local, &sih, sizeof(struct si_pub **));
+
+	sii = SI_INFO(sih);
+
+	if (sii == NULL)
+		return;
+
+	if (sih->bustype == SI_BUS)
+		for (idx = 0; idx < SI_MAXCORES; idx++)
+			if (sii->regs[idx]) {
+				iounmap(sii->regs[idx]);
+				sii->regs[idx] = NULL;
+			}
+
+	if (sih->bustype == PCI_BUS) {
+		if (sii->pch)
+			pcicore_deinit(sii->pch);
+		sii->pch = NULL;
+	}
+
+	if (sii != &ksii)
+		kfree(sii);
+}
+
+/* register driver interrupt disabling and restoring callback functions */
+void
+ai_register_intr_callback(struct si_pub *sih, void *intrsoff_fn,
+			  void *intrsrestore_fn,
+			  void *intrsenabled_fn, void *intr_arg)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+	sii->intr_arg = intr_arg;
+	sii->intrsoff_fn = (si_intrsoff_t) intrsoff_fn;
+	sii->intrsrestore_fn = (si_intrsrestore_t) intrsrestore_fn;
+	sii->intrsenabled_fn = (si_intrsenabled_t) intrsenabled_fn;
+	/* save current core id.  when this function called, the current core
+	 * must be the core which provides driver functions(il, et, wl, etc.)
+	 */
+	sii->dev_coreid = sii->coreid[sii->curidx];
+}
+
+void ai_deregister_intr_callback(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+	sii->intrsoff_fn = NULL;
+}
+
+uint ai_coreid(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+	return sii->coreid[sii->curidx];
+}
+
+uint ai_coreidx(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+	return sii->curidx;
+}
+
+bool ai_backplane64(struct si_pub *sih)
+{
+	return (sih->cccaps & CC_CAP_BKPLN64) != 0;
+}
+
+/* return index of coreid or BADIDX if not found */
+uint ai_findcoreidx(struct si_pub *sih, uint coreid, uint coreunit)
+{
+	struct si_info *sii;
+	uint found;
+	uint i;
+
+	sii = SI_INFO(sih);
+
+	found = 0;
+
+	for (i = 0; i < sii->numcores; i++)
+		if (sii->coreid[i] == coreid) {
+			if (found == coreunit)
+				return i;
+			found++;
+		}
+
+	return BADIDX;
+}
+
+/*
+ * This function changes logical "focus" to the indicated core;
+ * must be called with interrupts off.
+ * Moreover, callers should keep interrupts off during switching
+ * out of and back to d11 core.
+ */
+void *ai_setcore(struct si_pub *sih, uint coreid, uint coreunit)
+{
+	uint idx;
+
+	idx = ai_findcoreidx(sih, coreid, coreunit);
+	if (!GOODIDX(idx))
+		return NULL;
+
+	return ai_setcoreidx(sih, idx);
+}
+
+/* Turn off interrupt as required by ai_setcore, before switch core */
+void *ai_switch_core(struct si_pub *sih, uint coreid, uint *origidx,
+		     uint *intr_val)
+{
+	void *cc;
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	if (SI_FAST(sii)) {
+		/* Overloading the origidx variable to remember the coreid,
+		 * this works because the core ids cannot be confused with
+		 * core indices.
+		 */
+		*origidx = coreid;
+		if (coreid == CC_CORE_ID)
+			return (void *)CCREGS_FAST(sii);
+		else if (coreid == sih->buscoretype)
+			return (void *)PCIEREGS(sii);
+	}
+	INTR_OFF(sii, *intr_val);
+	*origidx = sii->curidx;
+	cc = ai_setcore(sih, coreid, 0);
+	return cc;
+}
+
+/* restore coreidx and restore interrupt */
+void ai_restore_core(struct si_pub *sih, uint coreid, uint intr_val)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+	if (SI_FAST(sii)
+	    && ((coreid == CC_CORE_ID) || (coreid == sih->buscoretype)))
+		return;
+
+	ai_setcoreidx(sih, coreid);
+	INTR_RESTORE(sii, intr_val);
+}
+
+void ai_write_wrapperreg(struct si_pub *sih, u32 offset, u32 val)
+{
+	struct si_info *sii = SI_INFO(sih);
+	u32 *w = (u32 *) sii->curwrap;
+	W_REG(w + (offset / 4), val);
+	return;
+}
+
+/*
+ * Switch to 'coreidx', issue a single arbitrary 32bit register mask&set
+ * operation, switch back to the original core, and return the new value.
+ *
+ * When using the silicon backplane, no fiddling with interrupts or core
+ * switches is needed.
+ *
+ * Also, when using pci/pcie, we can optimize away the core switching for pci
+ * registers and (on newer pci cores) chipcommon registers.
+ */
+uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
+		uint val)
+{
+	uint origidx = 0;
+	u32 *r = NULL;
+	uint w;
+	uint intr_val = 0;
+	bool fast = false;
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	if (coreidx >= SI_MAXCORES)
+		return 0;
+
+	if (sih->bustype == SI_BUS) {
+		/* If internal bus, we can always get at everything */
+		fast = true;
+		/* map if does not exist */
+		if (!sii->regs[coreidx]) {
+			sii->regs[coreidx] = REG_MAP(sii->coresba[coreidx],
+						     SI_CORE_SIZE);
+		}
+		r = (u32 *) ((unsigned char *) sii->regs[coreidx] + regoff);
+	} else if (sih->bustype == PCI_BUS) {
+		/*
+		 * If pci/pcie, we can get at pci/pcie regs
+		 * and on newer cores to chipc
+		 */
+		if ((sii->coreid[coreidx] == CC_CORE_ID) && SI_FAST(sii)) {
+			/* Chipc registers are mapped at 12KB */
+
+			fast = true;
+			r = (u32 *) ((char *)sii->curmap +
+					PCI_16KB0_CCREGS_OFFSET + regoff);
+		} else if (sii->pub.buscoreidx == coreidx) {
+			/*
+			 * pci registers are at either in the last 2KB of
+			 * an 8KB window or, in pcie and pci rev 13 at 8KB
+			 */
+			fast = true;
+			if (SI_FAST(sii))
+				r = (u32 *) ((char *)sii->curmap +
+						PCI_16KB0_PCIREGS_OFFSET +
+						regoff);
+			else
+				r = (u32 *) ((char *)sii->curmap +
+						((regoff >= SBCONFIGOFF) ?
+						 PCI_BAR0_PCISBR_OFFSET :
+						 PCI_BAR0_PCIREGS_OFFSET) +
+						regoff);
+		}
+	}
+
+	if (!fast) {
+		INTR_OFF(sii, intr_val);
+
+		/* save current core index */
+		origidx = ai_coreidx(&sii->pub);
+
+		/* switch core */
+		r = (u32 *) ((unsigned char *) ai_setcoreidx(&sii->pub, coreidx)
+				+ regoff);
+	}
+
+	/* mask and set */
+	if (mask || val) {
+		w = (R_REG(r) & ~mask) | val;
+		W_REG(r, w);
+	}
+
+	/* readback */
+	w = R_REG(r);
+
+	if (!fast) {
+		/* restore core index */
+		if (origidx != coreidx)
+			ai_setcoreidx(&sii->pub, origidx);
+
+		INTR_RESTORE(sii, intr_val);
+	}
+
+	return w;
+}
+
+void ai_core_disable(struct si_pub *sih, u32 bits)
+{
+	struct si_info *sii;
+	u32 dummy;
+	struct aidmp *ai;
+
+	sii = SI_INFO(sih);
+
+	ai = sii->curwrap;
+
+	/* if core is already in reset, just return */
+	if (R_REG(&ai->resetctrl) & AIRC_RESET)
+		return;
+
+	W_REG(&ai->ioctrl, bits);
+	dummy = R_REG(&ai->ioctrl);
+	udelay(10);
+
+	W_REG(&ai->resetctrl, AIRC_RESET);
+	udelay(1);
+}
+
+/* reset and re-enable a core
+ * inputs:
+ * bits - core specific bits that are set during and after reset sequence
+ * resetbits - core specific bits that are set only during reset sequence
+ */
+void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits)
+{
+	struct si_info *sii;
+	struct aidmp *ai;
+	u32 dummy;
+
+	sii = SI_INFO(sih);
+	ai = sii->curwrap;
+
+	/*
+	 * Must do the disable sequence first to work
+	 * for arbitrary current core state.
+	 */
+	ai_core_disable(sih, (bits | resetbits));
+
+	/*
+	 * Now do the initialization sequence.
+	 */
+	W_REG(&ai->ioctrl, (bits | SICF_FGC | SICF_CLOCK_EN));
+	dummy = R_REG(&ai->ioctrl);
+	W_REG(&ai->resetctrl, 0);
+	udelay(1);
+
+	W_REG(&ai->ioctrl, (bits | SICF_CLOCK_EN));
+	dummy = R_REG(&ai->ioctrl);
+	udelay(1);
+}
+
+/* return the slow clock source - LPO, XTAL, or PCI */
+static uint ai_slowclk_src(struct si_info *sii)
+{
+	chipcregs_t *cc;
+	u32 val;
+
+	if (sii->pub.ccrev < 6) {
+		if (sii->pub.bustype == PCI_BUS) {
+			pci_read_config_dword(sii->pbus, PCI_GPIO_OUT,
+					      &val);
+			if (val & PCI_CFG_GPIO_SCS)
+				return SCC_SS_PCI;
+		}
+		return SCC_SS_XTAL;
+	} else if (sii->pub.ccrev < 10) {
+		cc = (chipcregs_t *) ai_setcoreidx(&sii->pub, sii->curidx);
+		return R_REG(&cc->slow_clk_ctl) & SCC_SS_MASK;
+	} else			/* Insta-clock */
+		return SCC_SS_XTAL;
+}
+
+/*
+* return the ILP (slowclock) min or max frequency
+* precondition: we've established the chip has dynamic clk control
+*/
+static uint ai_slowclk_freq(struct si_info *sii, bool max_freq, chipcregs_t *cc)
+{
+	u32 slowclk;
+	uint div;
+
+	slowclk = ai_slowclk_src(sii);
+	if (sii->pub.ccrev < 6) {
+		if (slowclk == SCC_SS_PCI)
+			return max_freq ? (PCIMAXFREQ / 64)
+				: (PCIMINFREQ / 64);
+		else
+			return max_freq ? (XTALMAXFREQ / 32)
+				: (XTALMINFREQ / 32);
+	} else if (sii->pub.ccrev < 10) {
+		div = 4 *
+		    (((R_REG(&cc->slow_clk_ctl) & SCC_CD_MASK) >>
+		      SCC_CD_SHIFT) + 1);
+		if (slowclk == SCC_SS_LPO)
+			return max_freq ? LPOMAXFREQ : LPOMINFREQ;
+		else if (slowclk == SCC_SS_XTAL)
+			return max_freq ? (XTALMAXFREQ / div)
+				: (XTALMINFREQ / div);
+		else if (slowclk == SCC_SS_PCI)
+			return max_freq ? (PCIMAXFREQ / div)
+				: (PCIMINFREQ / div);
+	} else {
+		/* Chipc rev 10 is InstaClock */
+		div = R_REG(&cc->system_clk_ctl) >> SYCC_CD_SHIFT;
+		div = 4 * (div + 1);
+		return max_freq ? XTALMAXFREQ : (XTALMINFREQ / div);
+	}
+	return 0;
+}
+
+static void ai_clkctl_setdelay(struct si_info *sii, void *chipcregs)
+{
+	chipcregs_t *cc = (chipcregs_t *) chipcregs;
+	uint slowmaxfreq, pll_delay, slowclk;
+	uint pll_on_delay, fref_sel_delay;
+
+	pll_delay = PLL_DELAY;
+
+	/*
+	 * If the slow clock is not sourced by the xtal then
+	 * add the xtal_on_delay since the xtal will also be
+	 * powered down by dynamic clk control logic.
+	 */
+
+	slowclk = ai_slowclk_src(sii);
+	if (slowclk != SCC_SS_XTAL)
+		pll_delay += XTAL_ON_DELAY;
+
+	/* Starting with 4318 it is ILP that is used for the delays */
+	slowmaxfreq =
+	    ai_slowclk_freq(sii, (sii->pub.ccrev >= 10) ? false : true, cc);
+
+	pll_on_delay = ((slowmaxfreq * pll_delay) + 999999) / 1000000;
+	fref_sel_delay = ((slowmaxfreq * FREF_DELAY) + 999999) / 1000000;
+
+	W_REG(&cc->pll_on_delay, pll_on_delay);
+	W_REG(&cc->fref_sel_delay, fref_sel_delay);
+}
+
+/* initialize power control delay registers */
+void ai_clkctl_init(struct si_pub *sih)
+{
+	struct si_info *sii;
+	uint origidx = 0;
+	chipcregs_t *cc;
+	bool fast;
+
+	if (!CCCTL_ENAB(sih))
+		return;
+
+	sii = SI_INFO(sih);
+	fast = SI_FAST(sii);
+	if (!fast) {
+		origidx = sii->curidx;
+		cc = (chipcregs_t *) ai_setcore(sih, CC_CORE_ID, 0);
+		if (cc == NULL)
+			return;
+	} else {
+		cc = (chipcregs_t *) CCREGS_FAST(sii);
+		if (cc == NULL)
+			return;
+	}
+
+	/* set all Instaclk chip ILP to 1 MHz */
+	if (sih->ccrev >= 10)
+		SET_REG(&cc->system_clk_ctl, SYCC_CD_MASK,
+			(ILP_DIV_1MHZ << SYCC_CD_SHIFT));
+
+	ai_clkctl_setdelay(sii, (void *)cc);
+
+	if (!fast)
+		ai_setcoreidx(sih, origidx);
+}
+
+/*
+ * return the value suitable for writing to the
+ * dot11 core FAST_PWRUP_DELAY register
+ */
+u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih)
+{
+	struct si_info *sii;
+	uint origidx = 0;
+	chipcregs_t *cc;
+	uint slowminfreq;
+	u16 fpdelay;
+	uint intr_val = 0;
+	bool fast;
+
+	sii = SI_INFO(sih);
+	if (PMUCTL_ENAB(sih)) {
+		INTR_OFF(sii, intr_val);
+		fpdelay = si_pmu_fast_pwrup_delay(sih);
+		INTR_RESTORE(sii, intr_val);
+		return fpdelay;
+	}
+
+	if (!CCCTL_ENAB(sih))
+		return 0;
+
+	fast = SI_FAST(sii);
+	fpdelay = 0;
+	if (!fast) {
+		origidx = sii->curidx;
+		INTR_OFF(sii, intr_val);
+		cc = (chipcregs_t *) ai_setcore(sih, CC_CORE_ID, 0);
+		if (cc == NULL)
+			goto done;
+	} else {
+		cc = (chipcregs_t *) CCREGS_FAST(sii);
+		if (cc == NULL)
+			goto done;
+	}
+
+	slowminfreq = ai_slowclk_freq(sii, false, cc);
+	fpdelay = (((R_REG(&cc->pll_on_delay) + 2) * 1000000) +
+		   (slowminfreq - 1)) / slowminfreq;
+
+ done:
+	if (!fast) {
+		ai_setcoreidx(sih, origidx);
+		INTR_RESTORE(sii, intr_val);
+	}
+	return fpdelay;
+}
+
+/* turn primary xtal and/or pll off/on */
+int ai_clkctl_xtal(struct si_pub *sih, uint what, bool on)
+{
+	struct si_info *sii;
+	u32 in, out, outen;
+
+	sii = SI_INFO(sih);
+
+	switch (sih->bustype) {
+
+	case PCI_BUS:
+		/* pcie core doesn't have any mapping to control the xtal pu */
+		if (PCIE(sii))
+			return -1;
+
+		pci_read_config_dword(sii->pbus, PCI_GPIO_IN, &in);
+		pci_read_config_dword(sii->pbus, PCI_GPIO_OUT, &out);
+		pci_read_config_dword(sii->pbus, PCI_GPIO_OUTEN, &outen);
+
+		/*
+		 * Avoid glitching the clock if GPRS is already using it.
+		 * We can't actually read the state of the PLLPD so we infer it
+		 * by the value of XTAL_PU which *is* readable via gpioin.
+		 */
+		if (on && (in & PCI_CFG_GPIO_XTAL))
+			return 0;
+
+		if (what & XTAL)
+			outen |= PCI_CFG_GPIO_XTAL;
+		if (what & PLL)
+			outen |= PCI_CFG_GPIO_PLL;
+
+		if (on) {
+			/* turn primary xtal on */
+			if (what & XTAL) {
+				out |= PCI_CFG_GPIO_XTAL;
+				if (what & PLL)
+					out |= PCI_CFG_GPIO_PLL;
+				pci_write_config_dword(sii->pbus,
+						       PCI_GPIO_OUT, out);
+				pci_write_config_dword(sii->pbus,
+						       PCI_GPIO_OUTEN, outen);
+				udelay(XTAL_ON_DELAY);
+			}
+
+			/* turn pll on */
+			if (what & PLL) {
+				out &= ~PCI_CFG_GPIO_PLL;
+				pci_write_config_dword(sii->pbus,
+						       PCI_GPIO_OUT, out);
+				mdelay(2);
+			}
+		} else {
+			if (what & XTAL)
+				out &= ~PCI_CFG_GPIO_XTAL;
+			if (what & PLL)
+				out |= PCI_CFG_GPIO_PLL;
+			pci_write_config_dword(sii->pbus,
+					       PCI_GPIO_OUT, out);
+			pci_write_config_dword(sii->pbus,
+					       PCI_GPIO_OUTEN, outen);
+		}
+
+	default:
+		return -1;
+	}
+
+	return 0;
+}
+
+/*
+ *  clock control policy function throught chipcommon
+ *
+ *    set dynamic clk control mode (forceslow, forcefast, dynamic)
+ *    returns true if we are forcing fast clock
+ *    this is a wrapper over the next internal function
+ *      to allow flexible policy settings for outside caller
+ */
+bool ai_clkctl_cc(struct si_pub *sih, uint mode)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	/* chipcommon cores prior to rev6 don't support dynamic clock control */
+	if (sih->ccrev < 6)
+		return false;
+
+	if (PCI_FORCEHT(sii))
+		return mode == CLK_FAST;
+
+	return _ai_clkctl_cc(sii, mode);
+}
+
+/* clk control mechanism through chipcommon, no policy checking */
+static bool _ai_clkctl_cc(struct si_info *sii, uint mode)
+{
+	uint origidx = 0;
+	chipcregs_t *cc;
+	u32 scc;
+	uint intr_val = 0;
+	bool fast = SI_FAST(sii);
+
+	/* chipcommon cores prior to rev6 don't support dynamic clock control */
+	if (sii->pub.ccrev < 6)
+		return false;
+
+	if (!fast) {
+		INTR_OFF(sii, intr_val);
+		origidx = sii->curidx;
+
+		if ((sii->pub.bustype == SI_BUS) &&
+		    ai_setcore(&sii->pub, MIPS33_CORE_ID, 0) &&
+		    (ai_corerev(&sii->pub) <= 7) && (sii->pub.ccrev >= 10))
+			goto done;
+
+		cc = (chipcregs_t *) ai_setcore(&sii->pub, CC_CORE_ID, 0);
+	} else {
+		cc = (chipcregs_t *) CCREGS_FAST(sii);
+		if (cc == NULL)
+			goto done;
+	}
+
+	if (!CCCTL_ENAB(&sii->pub) && (sii->pub.ccrev < 20))
+		goto done;
+
+	switch (mode) {
+	case CLK_FAST:		/* FORCEHT, fast (pll) clock */
+		if (sii->pub.ccrev < 10) {
+			/*
+			 * don't forget to force xtal back
+			 * on before we clear SCC_DYN_XTAL..
+			 */
+			ai_clkctl_xtal(&sii->pub, XTAL, ON);
+			SET_REG(&cc->slow_clk_ctl,
+				(SCC_XC | SCC_FS | SCC_IP), SCC_IP);
+		} else if (sii->pub.ccrev < 20) {
+			OR_REG(&cc->system_clk_ctl, SYCC_HR);
+		} else {
+			OR_REG(&cc->clk_ctl_st, CCS_FORCEHT);
+		}
+
+		/* wait for the PLL */
+		if (PMUCTL_ENAB(&sii->pub)) {
+			u32 htavail = CCS_HTAVAIL;
+			SPINWAIT(((R_REG(&cc->clk_ctl_st) & htavail)
+				  == 0), PMU_MAX_TRANSITION_DLY);
+		} else {
+			udelay(PLL_DELAY);
+		}
+		break;
+
+	case CLK_DYNAMIC:	/* enable dynamic clock control */
+		if (sii->pub.ccrev < 10) {
+			scc = R_REG(&cc->slow_clk_ctl);
+			scc &= ~(SCC_FS | SCC_IP | SCC_XC);
+			if ((scc & SCC_SS_MASK) != SCC_SS_XTAL)
+				scc |= SCC_XC;
+			W_REG(&cc->slow_clk_ctl, scc);
+
+			/*
+			 * for dynamic control, we have to
+			 * release our xtal_pu "force on"
+			 */
+			if (scc & SCC_XC)
+				ai_clkctl_xtal(&sii->pub, XTAL, OFF);
+		} else if (sii->pub.ccrev < 20) {
+			/* Instaclock */
+			AND_REG(&cc->system_clk_ctl, ~SYCC_HR);
+		} else {
+			AND_REG(&cc->clk_ctl_st, ~CCS_FORCEHT);
+		}
+		break;
+
+	default:
+		break;
+	}
+
+ done:
+	if (!fast) {
+		ai_setcoreidx(&sii->pub, origidx);
+		INTR_RESTORE(sii, intr_val);
+	}
+	return mode == CLK_FAST;
+}
+
+/* Build device path. Support SI, PCI, and JTAG for now. */
+int ai_devpath(struct si_pub *sih, char *path, int size)
+{
+	int slen;
+
+	if (!path || size <= 0)
+		return -1;
+
+	switch (sih->bustype) {
+	case SI_BUS:
+	case JTAG_BUS:
+		slen = snprintf(path, (size_t) size, "sb/%u/", ai_coreidx(sih));
+		break;
+	case PCI_BUS:
+		slen = snprintf(path, (size_t) size, "pci/%u/%u/",
+			((struct pci_dev *)((SI_INFO(sih))->pbus))->bus->number,
+			PCI_SLOT(
+			    ((struct pci_dev *)((SI_INFO(sih))->pbus))->devfn));
+		break;
+
+	default:
+		slen = -1;
+		break;
+	}
+
+	if (slen < 0 || slen >= size) {
+		path[0] = '\0';
+		return -1;
+	}
+
+	return 0;
+}
+
+/* Get a variable, but only if it has a devpath prefix */
+char *ai_getdevpathvar(struct si_pub *sih, const char *name)
+{
+	char varname[SI_DEVPATH_BUFSZ + 32];
+
+	ai_devpathvar(sih, varname, sizeof(varname), name);
+
+	return getvar(NULL, varname);
+}
+
+/* Get a variable, but only if it has a devpath prefix */
+int ai_getdevpathintvar(struct si_pub *sih, const char *name)
+{
+#if defined(BCMBUSTYPE) && (BCMBUSTYPE == SI_BUS)
+	return getintvar(NULL, name);
+#else
+	char varname[SI_DEVPATH_BUFSZ + 32];
+
+	ai_devpathvar(sih, varname, sizeof(varname), name);
+
+	return getintvar(NULL, varname);
+#endif
+}
+
+char *ai_getnvramflvar(struct si_pub *sih, const char *name)
+{
+	return getvar(NULL, name);
+}
+
+/* Concatenate the dev path with a varname into the given 'var' buffer
+ * and return the 'var' pointer. Nothing is done to the arguments if
+ * len == 0 or var is NULL, var is still returned. On overflow, the
+ * first char will be set to '\0'.
+ */
+static char *ai_devpathvar(struct si_pub *sih, char *var, int len,
+			   const char *name)
+{
+	uint path_len;
+
+	if (!var || len <= 0)
+		return var;
+
+	if (ai_devpath(sih, var, len) == 0) {
+		path_len = strlen(var);
+
+		if (strlen(name) + 1 > (uint) (len - path_len))
+			var[0] = '\0';
+		else
+			strncpy(var + path_len, name, len - path_len - 1);
+	}
+
+	return var;
+}
+
+/* return true if PCIE capability exists in the pci config space */
+static bool ai_ispcie(struct si_info *sii)
+{
+	u8 cap_ptr;
+
+	if (sii->pub.bustype != PCI_BUS)
+		return false;
+
+	cap_ptr =
+	    pcicore_find_pci_capability(sii->pbus, PCI_CAP_ID_EXP, NULL,
+					NULL);
+	if (!cap_ptr)
+		return false;
+
+	return true;
+}
+
+bool ai_pci_war16165(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	return PCI(sii) && (sih->buscorerev <= 10);
+}
+
+void ai_pci_up(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	/* if not pci bus, we're done */
+	if (sih->bustype != PCI_BUS)
+		return;
+
+	if (PCI_FORCEHT(sii))
+		_ai_clkctl_cc(sii, CLK_FAST);
+
+	if (PCIE(sii))
+		pcicore_up(sii->pch, SI_PCIUP);
+
+}
+
+/* Unconfigure and/or apply various WARs when system is going to sleep mode */
+void ai_pci_sleep(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	pcicore_sleep(sii->pch);
+}
+
+/* Unconfigure and/or apply various WARs when going down */
+void ai_pci_down(struct si_pub *sih)
+{
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	/* if not pci bus, we're done */
+	if (sih->bustype != PCI_BUS)
+		return;
+
+	/* release FORCEHT since chip is going to "down" state */
+	if (PCI_FORCEHT(sii))
+		_ai_clkctl_cc(sii, CLK_DYNAMIC);
+
+	pcicore_down(sii->pch, SI_PCIDOWN);
+}
+
+/*
+ * Configure the pci core for pci client (NIC) action
+ * coremask is the bitvec of cores by index to be enabled.
+ */
+void ai_pci_setup(struct si_pub *sih, uint coremask)
+{
+	struct si_info *sii;
+	void *regs = NULL;
+	u32 siflag = 0, w;
+	uint idx = 0;
+
+	sii = SI_INFO(sih);
+
+	if (sii->pub.bustype != PCI_BUS)
+		return;
+
+	if (PCI(sii)) {
+		/* get current core index */
+		idx = sii->curidx;
+
+		/* we interrupt on this backplane flag number */
+		siflag = ai_flag(sih);
+
+		/* switch over to pci core */
+		regs = ai_setcoreidx(sih, sii->pub.buscoreidx);
+	}
+
+	/*
+	 * Enable sb->pci interrupts.  Assume
+	 * PCI rev 2.3 support was added in pci core rev 6 and things changed..
+	 */
+	if (PCIE(sii) || (PCI(sii) && ((sii->pub.buscorerev) >= 6))) {
+		/* pci config write to set this core bit in PCIIntMask */
+		pci_read_config_dword(sii->pbus, PCI_INT_MASK, &w);
+		w |= (coremask << PCI_SBIM_SHIFT);
+		pci_write_config_dword(sii->pbus, PCI_INT_MASK, w);
+	} else {
+		/* set sbintvec bit for our flag number */
+		ai_setint(sih, siflag);
+	}
+
+	if (PCI(sii)) {
+		pcicore_pci_setup(sii->pch, regs);
+
+		/* switch back to previous core */
+		ai_setcoreidx(sih, idx);
+	}
+}
+
+/*
+ * Fixup SROMless PCI device's configuration.
+ * The current core may be changed upon return.
+ */
+int ai_pci_fixcfg(struct si_pub *sih)
+{
+	uint origidx;
+	void *regs = NULL;
+
+	struct si_info *sii = SI_INFO(sih);
+
+	/* Fixup PI in SROM shadow area to enable the correct PCI core access */
+	/* save the current index */
+	origidx = ai_coreidx(&sii->pub);
+
+	/* check 'pi' is correct and fix it if not */
+	regs = ai_setcore(&sii->pub, sii->pub.buscoretype, 0);
+	pcicore_fixcfg(sii->pch, regs);
+
+	/* restore the original index */
+	ai_setcoreidx(&sii->pub, origidx);
+
+	pcicore_hwup(sii->pch);
+	return 0;
+}
+
+/* mask&set gpiocontrol bits */
+u32 ai_gpiocontrol(struct si_pub *sih, u32 mask, u32 val, u8 priority)
+{
+	uint regoff;
+
+	regoff = 0;
+
+	/* gpios could be shared on router platforms
+	 * ignore reservation if it's high priority (e.g., test apps)
+	 */
+	if ((priority != GPIO_HI_PRIORITY) &&
+	    (sih->bustype == SI_BUS) && (val || mask)) {
+		mask = priority ? (ai_gpioreservation & mask) :
+		    ((ai_gpioreservation | mask) & ~(ai_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = offsetof(chipcregs_t, gpiocontrol);
+	return ai_corereg(sih, SI_CC_IDX, regoff, mask, val);
+}
+
+void ai_chipcontrl_epa4331(struct si_pub *sih, bool on)
+{
+	struct si_info *sii;
+	chipcregs_t *cc;
+	uint origidx;
+	u32 val;
+
+	sii = SI_INFO(sih);
+	origidx = ai_coreidx(sih);
+
+	cc = (chipcregs_t *) ai_setcore(sih, CC_CORE_ID, 0);
+
+	val = R_REG(&cc->chipcontrol);
+
+	if (on) {
+		if (sih->chippkg == 9 || sih->chippkg == 0xb) {
+			/* Ext PA Controls for 4331 12x9 Package */
+			W_REG(&cc->chipcontrol, val |
+			      (CCTRL4331_EXTPA_EN |
+			       CCTRL4331_EXTPA_ON_GPIO2_5));
+		} else {
+			/* Ext PA Controls for 4331 12x12 Package */
+			W_REG(&cc->chipcontrol,
+			      val | (CCTRL4331_EXTPA_EN));
+		}
+	} else {
+		val &= ~(CCTRL4331_EXTPA_EN | CCTRL4331_EXTPA_ON_GPIO2_5);
+		W_REG(&cc->chipcontrol, val);
+	}
+
+	ai_setcoreidx(sih, origidx);
+}
+
+/* Enable BT-COEX & Ex-PA for 4313 */
+void ai_epa_4313war(struct si_pub *sih)
+{
+	struct si_info *sii;
+	chipcregs_t *cc;
+	uint origidx;
+
+	sii = SI_INFO(sih);
+	origidx = ai_coreidx(sih);
+
+	cc = (chipcregs_t *) ai_setcore(sih, CC_CORE_ID, 0);
+
+	/* EPA Fix */
+	W_REG(&cc->gpiocontrol,
+	      R_REG(&cc->gpiocontrol) | GPIO_CTRL_EPA_EN_MASK);
+
+	ai_setcoreidx(sih, origidx);
+}
+
+/* check if the device is removed */
+bool ai_deviceremoved(struct si_pub *sih)
+{
+	u32 w;
+	struct si_info *sii;
+
+	sii = SI_INFO(sih);
+
+	switch (sih->bustype) {
+	case PCI_BUS:
+		pci_read_config_dword(sii->pbus, PCI_VENDOR_ID, &w);
+		if ((w & 0xFFFF) != PCI_VENDOR_ID_BROADCOM)
+			return true;
+		break;
+	}
+	return false;
+}
+
+bool ai_is_sprom_available(struct si_pub *sih)
+{
+	if (sih->ccrev >= 31) {
+		struct si_info *sii;
+		uint origidx;
+		chipcregs_t *cc;
+		u32 sromctrl;
+
+		if ((sih->cccaps & CC_CAP_SROM) == 0)
+			return false;
+
+		sii = SI_INFO(sih);
+		origidx = sii->curidx;
+		cc = ai_setcoreidx(sih, SI_CC_IDX);
+		sromctrl = R_REG(&cc->sromcontrol);
+		ai_setcoreidx(sih, origidx);
+		return sromctrl & SRC_PRESENT;
+	}
+
+	switch (sih->chip) {
+	case BCM4313_CHIP_ID:
+		return (sih->chipst & CST4313_SPROM_PRESENT) != 0;
+	default:
+		return true;
+	}
+}
+
+bool ai_is_otp_disabled(struct si_pub *sih)
+{
+	switch (sih->chip) {
+	case BCM4313_CHIP_ID:
+		return (sih->chipst & CST4313_OTP_PRESENT) == 0;
+		/* These chips always have their OTP on */
+	case BCM43224_CHIP_ID:
+	case BCM43225_CHIP_ID:
+	default:
+		return false;
+	}
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
new file mode 100644
index 0000000..e245c27
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/aiutils.h
@@ -0,0 +1,584 @@
+/*
+ * Copyright (c) 2011 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef	_BRCM_AIUTILS_H_
+#define	_BRCM_AIUTILS_H_
+
+#include "types.h"
+
+/*
+ * SOC Interconnect Address Map.
+ * All regions may not exist on all chips.
+ */
+/* Physical SDRAM */
+#define SI_SDRAM_BASE		0x00000000
+/* Host Mode sb2pcitranslation0 (64 MB) */
+#define SI_PCI_MEM		0x08000000
+#define SI_PCI_MEM_SZ		(64 * 1024 * 1024)
+/* Host Mode sb2pcitranslation1 (64 MB) */
+#define SI_PCI_CFG		0x0c000000
+/* Byteswapped Physical SDRAM */
+#define	SI_SDRAM_SWAPPED	0x10000000
+/* Region 2 for sdram (512 MB) */
+#define SI_SDRAM_R2		0x80000000
+
+#ifdef SI_ENUM_BASE_VARIABLE
+#define SI_ENUM_BASE		(sii->pub.si_enum_base)
+#else
+#define SI_ENUM_BASE		0x18000000	/* Enumeration space base */
+#endif				/* SI_ENUM_BASE_VARIABLE */
+
+/* Wrapper space base */
+#define SI_WRAP_BASE		0x18100000
+/* each core gets 4Kbytes for registers */
+#define SI_CORE_SIZE		0x1000
+/*
+ * Max cores (this is arbitrary, for software
+ * convenience and could be changed if we
+ * make any larger chips
+ */
+#define	SI_MAXCORES		16
+
+/* On-chip RAM on chips that also have DDR */
+#define	SI_FASTRAM		0x19000000
+#define	SI_FASTRAM_SWAPPED	0x19800000
+
+/* Flash Region 2 (region 1 shadowed here) */
+#define	SI_FLASH2		0x1c000000
+/* Size of Flash Region 2 */
+#define	SI_FLASH2_SZ		0x02000000
+/* ARM Cortex-M3 ROM */
+#define	SI_ARMCM3_ROM		0x1e000000
+/* MIPS Flash Region 1 */
+#define	SI_FLASH1		0x1fc00000
+/* MIPS Size of Flash Region 1 */
+#define	SI_FLASH1_SZ		0x00400000
+/* ARM7TDMI-S ROM */
+#define	SI_ARM7S_ROM		0x20000000
+/* ARM Cortex-M3 SRAM Region 2 */
+#define	SI_ARMCM3_SRAM2		0x60000000
+/* ARM7TDMI-S SRAM Region 2 */
+#define	SI_ARM7S_SRAM2		0x80000000
+/* ARM Flash Region 1 */
+#define	SI_ARM_FLASH1		0xffff0000
+/* ARM Size of Flash Region 1 */
+#define	SI_ARM_FLASH1_SZ	0x00010000
+
+/* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA		0x40000000
+/* Client Mode sb2pcitranslation2 (1 GB) */
+#define SI_PCI_DMA2		0x80000000
+/* Client Mode sb2pcitranslation2 size in bytes */
+#define SI_PCI_DMA_SZ		0x40000000
+/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), low 32 bits */
+#define SI_PCIE_DMA_L32		0x00000000
+/* PCIE Client Mode sb2pcitranslation2 (2 ZettaBytes), high 32 bits */
+#define SI_PCIE_DMA_H32		0x80000000
+
+/* core codes */
+#define	NODEV_CORE_ID		0x700	/* Invalid coreid */
+#define	CC_CORE_ID		0x800	/* chipcommon core */
+#define	ILINE20_CORE_ID		0x801	/* iline20 core */
+#define	SRAM_CORE_ID		0x802	/* sram core */
+#define	SDRAM_CORE_ID		0x803	/* sdram core */
+#define	PCI_CORE_ID		0x804	/* pci core */
+#define	MIPS_CORE_ID		0x805	/* mips core */
+#define	ENET_CORE_ID		0x806	/* enet mac core */
+#define	CODEC_CORE_ID		0x807	/* v90 codec core */
+#define	USB_CORE_ID		0x808	/* usb 1.1 host/device core */
+#define	ADSL_CORE_ID		0x809	/* ADSL core */
+#define	ILINE100_CORE_ID	0x80a	/* iline100 core */
+#define	IPSEC_CORE_ID		0x80b	/* ipsec core */
+#define	UTOPIA_CORE_ID		0x80c	/* utopia core */
+#define	PCMCIA_CORE_ID		0x80d	/* pcmcia core */
+#define	SOCRAM_CORE_ID		0x80e	/* internal memory core */
+#define	MEMC_CORE_ID		0x80f	/* memc sdram core */
+#define	OFDM_CORE_ID		0x810	/* OFDM phy core */
+#define	EXTIF_CORE_ID		0x811	/* external interface core */
+#define	D11_CORE_ID		0x812	/* 802.11 MAC core */
+#define	APHY_CORE_ID		0x813	/* 802.11a phy core */
+#define	BPHY_CORE_ID		0x814	/* 802.11b phy core */
+#define	GPHY_CORE_ID		0x815	/* 802.11g phy core */
+#define	MIPS33_CORE_ID		0x816	/* mips3302 core */
+#define	USB11H_CORE_ID		0x817	/* usb 1.1 host core */
+#define	USB11D_CORE_ID		0x818	/* usb 1.1 device core */
+#define	USB20H_CORE_ID		0x819	/* usb 2.0 host core */
+#define	USB20D_CORE_ID		0x81a	/* usb 2.0 device core */
+#define	SDIOH_CORE_ID		0x81b	/* sdio host core */
+#define	ROBO_CORE_ID		0x81c	/* roboswitch core */
+#define	ATA100_CORE_ID		0x81d	/* parallel ATA core */
+#define	SATAXOR_CORE_ID		0x81e	/* serial ATA & XOR DMA core */
+#define	GIGETH_CORE_ID		0x81f	/* gigabit ethernet core */
+#define	PCIE_CORE_ID		0x820	/* pci express core */
+#define	NPHY_CORE_ID		0x821	/* 802.11n 2x2 phy core */
+#define	SRAMC_CORE_ID		0x822	/* SRAM controller core */
+#define	MINIMAC_CORE_ID		0x823	/* MINI MAC/phy core */
+#define	ARM11_CORE_ID		0x824	/* ARM 1176 core */
+#define	ARM7S_CORE_ID		0x825	/* ARM7tdmi-s core */
+#define	LPPHY_CORE_ID		0x826	/* 802.11a/b/g phy core */
+#define	PMU_CORE_ID		0x827	/* PMU core */
+#define	SSNPHY_CORE_ID		0x828	/* 802.11n single-stream phy core */
+#define	SDIOD_CORE_ID		0x829	/* SDIO device core */
+#define	ARMCM3_CORE_ID		0x82a	/* ARM Cortex M3 core */
+#define	HTPHY_CORE_ID		0x82b	/* 802.11n 4x4 phy core */
+#define	MIPS74K_CORE_ID		0x82c	/* mips 74k core */
+#define	GMAC_CORE_ID		0x82d	/* Gigabit MAC core */
+#define	DMEMC_CORE_ID		0x82e	/* DDR1/2 memory controller core */
+#define	PCIERC_CORE_ID		0x82f	/* PCIE Root Complex core */
+#define	OCP_CORE_ID		0x830	/* OCP2OCP bridge core */
+#define	SC_CORE_ID		0x831	/* shared common core */
+#define	AHB_CORE_ID		0x832	/* OCP2AHB bridge core */
+#define	SPIH_CORE_ID		0x833	/* SPI host core */
+#define	I2S_CORE_ID		0x834	/* I2S core */
+#define	DMEMS_CORE_ID		0x835	/* SDR/DDR1 memory controller core */
+#define	DEF_SHIM_COMP		0x837	/* SHIM component in ubus/6362 */
+#define OOB_ROUTER_CORE_ID	0x367	/* OOB router core ID */
+#define	DEF_AI_COMP		0xfff	/* Default component, in ai chips it
+					 * maps all unused address ranges
+					 */
+
+/* chipcommon being the first core: */
+#define	SI_CC_IDX		0
+
+/* SOC Interconnect types (aka chip types) */
+#define	SOCI_AI			1
+
+/* Common core control flags */
+#define	SICF_BIST_EN		0x8000
+#define	SICF_PME_EN		0x4000
+#define	SICF_CORE_BITS		0x3ffc
+#define	SICF_FGC		0x0002
+#define	SICF_CLOCK_EN		0x0001
+
+/* Common core status flags */
+#define	SISF_BIST_DONE		0x8000
+#define	SISF_BIST_ERROR		0x4000
+#define	SISF_GATED_CLK		0x2000
+#define	SISF_DMA64		0x1000
+#define	SISF_CORE_BITS		0x0fff
+
+/* A register that is common to all cores to
+ * communicate w/PMU regarding clock control.
+ */
+#define SI_CLK_CTL_ST		0x1e0	/* clock control and status */
+
+/* clk_ctl_st register */
+#define	CCS_FORCEALP		0x00000001	/* force ALP request */
+#define	CCS_FORCEHT		0x00000002	/* force HT request */
+#define	CCS_FORCEILP		0x00000004	/* force ILP request */
+#define	CCS_ALPAREQ		0x00000008	/* ALP Avail Request */
+#define	CCS_HTAREQ		0x00000010	/* HT Avail Request */
+#define	CCS_FORCEHWREQOFF	0x00000020	/* Force HW Clock Request Off */
+#define CCS_ERSRC_REQ_MASK	0x00000700	/* external resource requests */
+#define CCS_ERSRC_REQ_SHIFT	8
+#define	CCS_ALPAVAIL		0x00010000	/* ALP is available */
+#define	CCS_HTAVAIL		0x00020000	/* HT is available */
+#define CCS_BP_ON_APL		0x00040000	/* RO: running on ALP clock */
+#define CCS_BP_ON_HT		0x00080000	/* RO: running on HT clock */
+#define CCS_ERSRC_STS_MASK	0x07000000	/* external resource status */
+#define CCS_ERSRC_STS_SHIFT	24
+
+/* HT avail in chipc and pcmcia on 4328a0 */
+#define	CCS0_HTAVAIL		0x00010000
+/* ALP avail in chipc and pcmcia on 4328a0 */
+#define	CCS0_ALPAVAIL		0x00020000
+
+/* Not really related to SOC Interconnect, but a couple of software
+ * conventions for the use the flash space:
+ */
+
+/* Minumum amount of flash we support */
+#define FLASH_MIN		0x00020000	/* Minimum flash size */
+
+/* A boot/binary may have an embedded block that describes its size  */
+#define	BISZ_OFFSET		0x3e0	/* At this offset into the binary */
+#define	BISZ_MAGIC		0x4249535a	/* Marked with value: 'BISZ' */
+#define	BISZ_MAGIC_IDX		0	/* Word 0: magic */
+#define	BISZ_TXTST_IDX		1	/*      1: text start */
+#define	BISZ_TXTEND_IDX		2	/*      2: text end */
+#define	BISZ_DATAST_IDX		3	/*      3: data start */
+#define	BISZ_DATAEND_IDX	4	/*      4: data end */
+#define	BISZ_BSSST_IDX		5	/*      5: bss start */
+#define	BISZ_BSSEND_IDX		6	/*      6: bss end */
+#define BISZ_SIZE		7	/* descriptor size in 32-bit integers */
+
+#define	CC_SROM_OTP		0x800	/* SROM/OTP address space */
+
+/* gpiotimerval */
+#define GPIO_ONTIME_SHIFT	16
+
+/* Fields in clkdiv */
+#define	CLKD_OTP		0x000f0000
+#define	CLKD_OTP_SHIFT		16
+
+/* When Srom support present, fields in sromcontrol */
+#define	SRC_START		0x80000000
+#define	SRC_BUSY		0x80000000
+#define	SRC_OPCODE		0x60000000
+#define	SRC_OP_READ		0x00000000
+#define	SRC_OP_WRITE		0x20000000
+#define	SRC_OP_WRDIS		0x40000000
+#define	SRC_OP_WREN		0x60000000
+#define	SRC_OTPSEL		0x00000010
+#define	SRC_LOCK		0x00000008
+#define	SRC_SIZE_MASK		0x00000006
+#define	SRC_SIZE_1K		0x00000000
+#define	SRC_SIZE_4K		0x00000002
+#define	SRC_SIZE_16K		0x00000004
+#define	SRC_SIZE_SHIFT		1
+#define	SRC_PRESENT		0x00000001
+
+/* 4330 chip-specific ChipStatus register bits */
+#define CST4330_CHIPMODE_SDIOD(cs)	(((cs) & 0x7) < 6)	/* SDIO || gSPI */
+#define CST4330_CHIPMODE_USB20D(cs)	(((cs) & 0x7) >= 6)	/* USB || USBDA */
+#define CST4330_CHIPMODE_SDIO(cs)	(((cs) & 0x4) == 0)	/* SDIO */
+#define CST4330_CHIPMODE_GSPI(cs)	(((cs) & 0x6) == 4)	/* gSPI */
+#define CST4330_CHIPMODE_USB(cs)	(((cs) & 0x7) == 6)	/* USB packet-oriented */
+#define CST4330_CHIPMODE_USBDA(cs)	(((cs) & 0x7) == 7)	/* USB Direct Access */
+#define	CST4330_OTP_PRESENT		0x00000010
+#define	CST4330_LPO_AUTODET_EN		0x00000020
+#define	CST4330_ARMREMAP_0		0x00000040
+#define	CST4330_SPROM_PRESENT		0x00000080	/* takes priority over OTP if both set */
+#define	CST4330_ILPDIV_EN		0x00000100
+#define	CST4330_LPO_SEL			0x00000200
+#define	CST4330_RES_INIT_MODE_SHIFT	10
+#define	CST4330_RES_INIT_MODE_MASK	0x00000c00
+#define CST4330_CBUCK_MODE_SHIFT	12
+#define CST4330_CBUCK_MODE_MASK		0x00003000
+#define	CST4330_CBUCK_POWER_OK		0x00004000
+#define	CST4330_BB_PLL_LOCKED		0x00008000
+
+/* Package IDs */
+#define BCM4329_289PIN_PKG_ID	0	/* 4329 289-pin package id */
+#define BCM4329_182PIN_PKG_ID	1	/* 4329N 182-pin package id */
+#define	BCM4717_PKG_ID		9	/* 4717 package id */
+#define	BCM4718_PKG_ID		10	/* 4718 package id */
+#define HDLSIM_PKG_ID		14	/* HDL simulator package id */
+#define HWSIM_PKG_ID		15	/* Hardware simulator package id */
+#define BCM43224_FAB_SMIC	0xa	/* the chip is manufactured by SMIC */
+
+/* these are router chips */
+#define	BCM4716_CHIP_ID		0x4716	/* 4716 chipcommon chipid */
+#define	BCM47162_CHIP_ID	47162	/* 47162 chipcommon chipid */
+#define	BCM4748_CHIP_ID		0x4748	/* 4716 chipcommon chipid (OTP, RBBU) */
+#define	BCM5356_CHIP_ID		0x5356	/* 5356 chipcommon chipid */
+#define	BCM5357_CHIP_ID		0x5357	/* 5357 chipcommon chipid */
+
+
+#define	SI_INFO(sih)	((struct si_info *)sih)
+
+#define	GOODCOREADDR(x, b) \
+	(((x) >= (b)) && ((x) < ((b) + SI_MAXCORES * SI_CORE_SIZE)) && \
+		IS_ALIGNED((x), SI_CORE_SIZE))
+#define	GOODREGS(regs) \
+	((regs) != NULL && IS_ALIGNED((unsigned long)(regs), SI_CORE_SIZE))
+#define BADCOREADDR	0
+#define	GOODIDX(idx)	(((uint)idx) < SI_MAXCORES)
+#define	NOREV		-1	/* Invalid rev */
+
+/* Newer chips can access PCI/PCIE and CC core without requiring to change
+ * PCI BAR0 WIN
+ */
+#define SI_FAST(si) (((si)->pub.buscoretype == PCIE_CORE_ID) ||	\
+		     (((si)->pub.buscoretype == PCI_CORE_ID) && \
+		      (si)->pub.buscorerev >= 13))
+
+#define PCIEREGS(si) (((char *)((si)->curmap) + PCI_16KB0_PCIREGS_OFFSET))
+#define CCREGS_FAST(si) (((char *)((si)->curmap) + PCI_16KB0_CCREGS_OFFSET))
+
+/*
+ * Macros to disable/restore function core(D11, ENET, ILINE20, etc) interrupts
+ * before after core switching to avoid invalid register accesss inside ISR.
+ */
+#define INTR_OFF(si, intr_val) \
+	if ((si)->intrsoff_fn && \
+	    (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
+		intr_val = (*(si)->intrsoff_fn)((si)->intr_arg)
+#define INTR_RESTORE(si, intr_val) \
+	if ((si)->intrsrestore_fn && \
+	    (si)->coreid[(si)->curidx] == (si)->dev_coreid) \
+		(*(si)->intrsrestore_fn)((si)->intr_arg, intr_val)
+
+/* dynamic clock control defines */
+#define	LPOMINFREQ		25000	/* low power oscillator min */
+#define	LPOMAXFREQ		43000	/* low power oscillator max */
+#define	XTALMINFREQ		19800000	/* 20 MHz - 1% */
+#define	XTALMAXFREQ		20200000	/* 20 MHz + 1% */
+#define	PCIMINFREQ		25000000	/* 25 MHz */
+#define	PCIMAXFREQ		34000000	/* 33 MHz + fudge */
+
+#define	ILP_DIV_5MHZ		0	/* ILP = 5 MHz */
+#define	ILP_DIV_1MHZ		4	/* ILP = 1 MHz */
+
+#define PCI(si)		(((si)->pub.bustype == PCI_BUS) &&	\
+			 ((si)->pub.buscoretype == PCI_CORE_ID))
+#define PCIE(si)	(((si)->pub.bustype == PCI_BUS) &&	\
+			 ((si)->pub.buscoretype == PCIE_CORE_ID))
+#define PCI_FORCEHT(si)	\
+	(PCIE(si) && (si->pub.chip == BCM4716_CHIP_ID))
+
+/* GPIO Based LED powersave defines */
+#define DEFAULT_GPIO_ONTIME	10	/* Default: 10% on */
+#define DEFAULT_GPIO_OFFTIME	90	/* Default: 10% on */
+
+#ifndef DEFAULT_GPIOTIMERVAL
+#define DEFAULT_GPIOTIMERVAL \
+	((DEFAULT_GPIO_ONTIME << GPIO_ONTIME_SHIFT) | DEFAULT_GPIO_OFFTIME)
+#endif
+
+/*
+ * Data structure to export all chip specific common variables
+ *   public (read-only) portion of aiutils handle returned by si_attach()
+ */
+struct si_pub {
+	uint bustype;		/* SI_BUS, PCI_BUS */
+	uint buscoretype;	/* PCI_CORE_ID, PCIE_CORE_ID, PCMCIA_CORE_ID */
+	uint buscorerev;	/* buscore rev */
+	uint buscoreidx;	/* buscore index */
+	int ccrev;		/* chip common core rev */
+	u32 cccaps;		/* chip common capabilities */
+	u32 cccaps_ext;	/* chip common capabilities extension */
+	int pmurev;		/* pmu core rev */
+	u32 pmucaps;		/* pmu capabilities */
+	uint boardtype;		/* board type */
+	uint boardvendor;	/* board vendor */
+	uint boardflags;	/* board flags */
+	uint boardflags2;	/* board flags2 */
+	uint chip;		/* chip number */
+	uint chiprev;		/* chip revision */
+	uint chippkg;		/* chip package option */
+	u32 chipst;		/* chip status */
+	bool issim;		/* chip is in simulation or emulation */
+	uint socirev;		/* SOC interconnect rev */
+	bool pci_pr32414;
+
+};
+
+/*
+ * Many of the routines below take an 'sih' handle as their first arg.
+ * Allocate this by calling si_attach().  Free it by calling si_detach().
+ * At any one time, the sih is logically focused on one particular si core
+ * (the "current core").
+ * Use si_setcore() or si_setcoreidx() to change the association to another core
+ */
+
+#define	BADIDX		(SI_MAXCORES + 1)
+
+/* clkctl xtal what flags */
+#define	XTAL			0x1	/* primary crystal oscillator (2050) */
+#define	PLL			0x2	/* main chip pll */
+
+/* clkctl clk mode */
+#define	CLK_FAST		0	/* force fast (pll) clock */
+#define	CLK_DYNAMIC		2	/* enable dynamic clock control */
+
+/* GPIO usage priorities */
+#define GPIO_DRV_PRIORITY	0	/* Driver */
+#define GPIO_APP_PRIORITY	1	/* Application */
+#define GPIO_HI_PRIORITY	2	/* Highest priority. Ignore GPIO
+					 * reservation
+					 */
+
+/* GPIO pull up/down */
+#define GPIO_PULLUP		0
+#define GPIO_PULLDN		1
+
+/* GPIO event regtype */
+#define GPIO_REGEVT		0	/* GPIO register event */
+#define GPIO_REGEVT_INTMSK	1	/* GPIO register event int mask */
+#define GPIO_REGEVT_INTPOL	2	/* GPIO register event int polarity */
+
+/* device path */
+#define SI_DEVPATH_BUFSZ	16	/* min buffer size in bytes */
+
+/* SI routine enumeration: to be used by update function with multiple hooks */
+#define	SI_DOATTACH	1
+#define SI_PCIDOWN	2
+#define SI_PCIUP	3
+
+/* PMU clock/power control */
+#if defined(BCMPMUCTL)
+#define PMUCTL_ENAB(sih)	(BCMPMUCTL)
+#else
+#define PMUCTL_ENAB(sih)	((sih)->cccaps & CC_CAP_PMU)
+#endif
+
+/* chipcommon clock/power control (exclusive with PMU's) */
+#if defined(BCMPMUCTL) && BCMPMUCTL
+#define CCCTL_ENAB(sih)		(0)
+#define CCPLL_ENAB(sih)		(0)
+#else
+#define CCCTL_ENAB(sih)		((sih)->cccaps & CC_CAP_PWR_CTL)
+#define CCPLL_ENAB(sih)		((sih)->cccaps & CC_CAP_PLL_MASK)
+#endif
+
+typedef void (*gpio_handler_t) (u32 stat, void *arg);
+
+/* External PA enable mask */
+#define GPIO_CTRL_EPA_EN_MASK 0x40
+
+#define	SI_ERROR(args)
+
+#ifdef BCMDBG
+#define	SI_MSG(args)	printk args
+#else
+#define	SI_MSG(args)
+#endif				/* BCMDBG */
+
+/* Define SI_VMSG to printf for verbose debugging, but don't check it in */
+#define	SI_VMSG(args)
+
+#define	IS_SIM(chippkg)	\
+	((chippkg == HDLSIM_PKG_ID) || (chippkg == HWSIM_PKG_ID))
+
+typedef u32(*si_intrsoff_t) (void *intr_arg);
+typedef void (*si_intrsrestore_t) (void *intr_arg, u32 arg);
+typedef bool(*si_intrsenabled_t) (void *intr_arg);
+
+struct gpioh_item {
+	void *arg;
+	bool level;
+	gpio_handler_t handler;
+	u32 event;
+	struct gpioh_item *next;
+};
+
+/* misc si info needed by some of the routines */
+struct si_info {
+	struct si_pub pub;	/* back plane public state (must be first) */
+	void *pbus;		/* handle to bus (pci/sdio/..) */
+	uint dev_coreid;	/* the core provides driver functions */
+	void *intr_arg;		/* interrupt callback function arg */
+	si_intrsoff_t intrsoff_fn;	/* turns chip interrupts off */
+	si_intrsrestore_t intrsrestore_fn; /* restore chip interrupts */
+	si_intrsenabled_t intrsenabled_fn; /* check if interrupts are enabled */
+
+	void *pch;		/* PCI/E core handle */
+
+	char *vars;
+	uint varsz;
+
+	void *curmap;		/* current regs va */
+	void *regs[SI_MAXCORES];	/* other regs va */
+
+	uint curidx;		/* current core index */
+	uint numcores;		/* # discovered cores */
+	uint coreid[SI_MAXCORES]; /* id of each core */
+	u32 coresba[SI_MAXCORES]; /* backplane address of each core */
+	void *regs2[SI_MAXCORES]; /* 2nd virtual address per core (usbh20) */
+	u32 coresba2[SI_MAXCORES]; /* 2nd phys address per core (usbh20) */
+	u32 coresba_size[SI_MAXCORES]; /* backplane address space size */
+	u32 coresba2_size[SI_MAXCORES];	/* second address space size */
+
+	void *curwrap;		/* current wrapper va */
+	void *wrappers[SI_MAXCORES];	/* other cores wrapper va */
+	u32 wrapba[SI_MAXCORES];	/* address of controlling wrapper */
+
+	u32 cia[SI_MAXCORES];	/* erom cia entry for each core */
+	u32 cib[SI_MAXCORES];	/* erom cia entry for each core */
+	u32 oob_router;	/* oob router registers for axi */
+};
+
+/* AMBA Interconnect exported externs */
+extern void ai_scan(struct si_pub *sih, void *regs);
+
+extern uint ai_flag(struct si_pub *sih);
+extern void ai_setint(struct si_pub *sih, int siflag);
+extern uint ai_coreidx(struct si_pub *sih);
+extern uint ai_corevendor(struct si_pub *sih);
+extern uint ai_corerev(struct si_pub *sih);
+extern bool ai_iscoreup(struct si_pub *sih);
+extern void *ai_setcoreidx(struct si_pub *sih, uint coreidx);
+extern u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val);
+extern void ai_core_cflags_wo(struct si_pub *sih, u32 mask, u32 val);
+extern u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val);
+extern uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
+		       uint val);
+extern void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits);
+extern void ai_core_disable(struct si_pub *sih, u32 bits);
+extern int ai_numaddrspaces(struct si_pub *sih);
+extern u32 ai_addrspace(struct si_pub *sih, uint asidx);
+extern u32 ai_addrspacesize(struct si_pub *sih, uint asidx);
+extern void ai_write_wrap_reg(struct si_pub *sih, u32 offset, u32 val);
+
+/* === exported functions === */
+extern struct si_pub *ai_attach(void *regs, uint bustype,
+		       void *sdh, char **vars, uint *varsz);
+
+extern void ai_detach(struct si_pub *sih);
+extern bool ai_pci_war16165(struct si_pub *sih);
+
+extern uint ai_coreid(struct si_pub *sih);
+extern uint ai_corerev(struct si_pub *sih);
+extern uint ai_corereg(struct si_pub *sih, uint coreidx, uint regoff, uint mask,
+		uint val);
+extern void ai_write_wrapperreg(struct si_pub *sih, u32 offset, u32 val);
+extern u32 ai_core_cflags(struct si_pub *sih, u32 mask, u32 val);
+extern u32 ai_core_sflags(struct si_pub *sih, u32 mask, u32 val);
+extern bool ai_iscoreup(struct si_pub *sih);
+extern uint ai_findcoreidx(struct si_pub *sih, uint coreid, uint coreunit);
+extern void *ai_setcoreidx(struct si_pub *sih, uint coreidx);
+extern void *ai_setcore(struct si_pub *sih, uint coreid, uint coreunit);
+extern void *ai_switch_core(struct si_pub *sih, uint coreid, uint *origidx,
+			    uint *intr_val);
+extern void ai_restore_core(struct si_pub *sih, uint coreid, uint intr_val);
+extern void ai_core_reset(struct si_pub *sih, u32 bits, u32 resetbits);
+extern void ai_core_disable(struct si_pub *sih, u32 bits);
+extern u32 ai_alp_clock(struct si_pub *sih);
+extern u32 ai_ilp_clock(struct si_pub *sih);
+extern void ai_pci_setup(struct si_pub *sih, uint coremask);
+extern void ai_setint(struct si_pub *sih, int siflag);
+extern bool ai_backplane64(struct si_pub *sih);
+extern void ai_register_intr_callback(struct si_pub *sih, void *intrsoff_fn,
+				      void *intrsrestore_fn,
+				      void *intrsenabled_fn, void *intr_arg);
+extern void ai_deregister_intr_callback(struct si_pub *sih);
+extern void ai_clkctl_init(struct si_pub *sih);
+extern u16 ai_clkctl_fast_pwrup_delay(struct si_pub *sih);
+extern bool ai_clkctl_cc(struct si_pub *sih, uint mode);
+extern int ai_clkctl_xtal(struct si_pub *sih, uint what, bool on);
+extern bool ai_deviceremoved(struct si_pub *sih);
+extern u32 ai_gpiocontrol(struct si_pub *sih, u32 mask, u32 val,
+			     u8 priority);
+
+/* OTP status */
+extern bool ai_is_otp_disabled(struct si_pub *sih);
+
+/* SPROM availability */
+extern bool ai_is_sprom_available(struct si_pub *sih);
+
+/*
+ * Build device path. Path size must be >= SI_DEVPATH_BUFSZ.
+ * The returned path is NULL terminated and has trailing '/'.
+ * Return 0 on success, nonzero otherwise.
+ */
+extern int ai_devpath(struct si_pub *sih, char *path, int size);
+/* Read variable with prepending the devpath to the name */
+extern char *ai_getdevpathvar(struct si_pub *sih, const char *name);
+extern int ai_getdevpathintvar(struct si_pub *sih, const char *name);
+
+extern void ai_pci_sleep(struct si_pub *sih);
+extern void ai_pci_down(struct si_pub *sih);
+extern void ai_pci_up(struct si_pub *sih);
+extern int ai_pci_fixcfg(struct si_pub *sih);
+
+extern void ai_chipcontrl_epa4331(struct si_pub *sih, bool on);
+/* Enable Ex-PA for 4313 */
+extern void ai_epa_4313war(struct si_pub *sih);
+
+char *ai_getnvramflvar(struct si_pub *sih, const char *name);
+
+#endif				/* _BRCM_AIUTILS_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/alloc.c b/drivers/net/wireless/brcm80211/brcmsmac/alloc.c
new file mode 100644
index 0000000..7f8dd7b
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/alloc.c
@@ -0,0 +1,275 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <brcmu_utils.h>
+#include "types.h"
+#include "pub.h"
+#include "main.h"
+#include "alloc.h"
+
+static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit);
+static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg);
+static struct brcms_pub *brcms_c_pub_malloc(uint unit,
+				      uint *err, uint devid);
+static void brcms_c_pub_mfree(struct brcms_pub *pub);
+static void brcms_c_tunables_init(struct brcms_tunables *tunables, uint devid);
+
+static void brcms_c_tunables_init(struct brcms_tunables *tunables, uint devid)
+{
+	tunables->ntxd = NTXD;
+	tunables->nrxd = NRXD;
+	tunables->rxbufsz = RXBUFSZ;
+	tunables->nrxbufpost = NRXBUFPOST;
+	tunables->maxscb = MAXSCB;
+	tunables->ampdunummpdu = AMPDU_NUM_MPDU;
+	tunables->maxpktcb = MAXPKTCB;
+	tunables->maxucodebss = BRCMS_MAX_UCODE_BSS;
+	tunables->maxucodebss4 = BRCMS_MAX_UCODE_BSS4;
+	tunables->maxbss = MAXBSS;
+	tunables->datahiwat = BRCMS_DATAHIWAT;
+	tunables->ampdudatahiwat = BRCMS_AMPDUDATAHIWAT;
+	tunables->rxbnd = RXBND;
+	tunables->txsbnd = TXSBND;
+}
+
+static struct brcms_pub *brcms_c_pub_malloc(uint unit, uint *err, uint devid)
+{
+	struct brcms_pub *pub;
+
+	pub = kzalloc(sizeof(struct brcms_pub), GFP_ATOMIC);
+	if (pub == NULL) {
+		*err = 1001;
+		goto fail;
+	}
+
+	pub->tunables = kzalloc(sizeof(struct brcms_tunables), GFP_ATOMIC);
+	if (pub->tunables == NULL) {
+		*err = 1028;
+		goto fail;
+	}
+
+	/* need to init the tunables now */
+	brcms_c_tunables_init(pub->tunables, devid);
+
+	pub->multicast = kzalloc(ETH_ALEN * MAXMULTILIST, GFP_ATOMIC);
+	if (pub->multicast == NULL) {
+		*err = 1003;
+		goto fail;
+	}
+
+	return pub;
+
+ fail:
+	brcms_c_pub_mfree(pub);
+	return NULL;
+}
+
+static void brcms_c_pub_mfree(struct brcms_pub *pub)
+{
+	if (pub == NULL)
+		return;
+
+	kfree(pub->multicast);
+	kfree(pub->tunables);
+	kfree(pub);
+}
+
+static struct brcms_bss_cfg *brcms_c_bsscfg_malloc(uint unit)
+{
+	struct brcms_bss_cfg *cfg;
+
+	cfg = kzalloc(sizeof(struct brcms_bss_cfg), GFP_ATOMIC);
+	if (cfg == NULL)
+		goto fail;
+
+	cfg->current_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC);
+	if (cfg->current_bss == NULL)
+		goto fail;
+
+	return cfg;
+
+ fail:
+	brcms_c_bsscfg_mfree(cfg);
+	return NULL;
+}
+
+static void brcms_c_bsscfg_mfree(struct brcms_bss_cfg *cfg)
+{
+	if (cfg == NULL)
+		return;
+
+	kfree(cfg->maclist);
+	kfree(cfg->current_bss);
+	kfree(cfg);
+}
+
+static void brcms_c_bsscfg_ID_assign(struct brcms_c_info *wlc,
+				 struct brcms_bss_cfg *bsscfg)
+{
+	bsscfg->ID = wlc->next_bsscfg_ID;
+	wlc->next_bsscfg_ID++;
+}
+
+/*
+ * The common driver entry routine. Error codes should be unique
+ */
+struct brcms_c_info *brcms_c_attach_malloc(uint unit, uint *err, uint devid)
+{
+	struct brcms_c_info *wlc;
+
+	wlc = kzalloc(sizeof(struct brcms_c_info), GFP_ATOMIC);
+	if (wlc == NULL) {
+		*err = 1002;
+		goto fail;
+	}
+
+	/* allocate struct brcms_c_pub state structure */
+	wlc->pub = brcms_c_pub_malloc(unit, err, devid);
+	if (wlc->pub == NULL) {
+		*err = 1003;
+		goto fail;
+	}
+	wlc->pub->wlc = wlc;
+
+	/* allocate struct brcms_hardware state structure */
+
+	wlc->hw = kzalloc(sizeof(struct brcms_hardware), GFP_ATOMIC);
+	if (wlc->hw == NULL) {
+		*err = 1005;
+		goto fail;
+	}
+	wlc->hw->wlc = wlc;
+
+	wlc->hw->bandstate[0] =
+		kzalloc(sizeof(struct brcms_hw_band) * MAXBANDS, GFP_ATOMIC);
+	if (wlc->hw->bandstate[0] == NULL) {
+		*err = 1006;
+		goto fail;
+	} else {
+		int i;
+
+		for (i = 1; i < MAXBANDS; i++) {
+			wlc->hw->bandstate[i] = (struct brcms_hw_band *)
+			    ((unsigned long)wlc->hw->bandstate[0] +
+			     (sizeof(struct brcms_hw_band) * i));
+		}
+	}
+
+	wlc->modulecb =
+		kzalloc(sizeof(struct modulecb) * BRCMS_MAXMODULES, GFP_ATOMIC);
+	if (wlc->modulecb == NULL) {
+		*err = 1009;
+		goto fail;
+	}
+
+	wlc->default_bss = kzalloc(sizeof(struct brcms_bss_info), GFP_ATOMIC);
+	if (wlc->default_bss == NULL) {
+		*err = 1010;
+		goto fail;
+	}
+
+	wlc->cfg = brcms_c_bsscfg_malloc(unit);
+	if (wlc->cfg == NULL) {
+		*err = 1011;
+		goto fail;
+	}
+	brcms_c_bsscfg_ID_assign(wlc, wlc->cfg);
+
+	wlc->wsec_def_keys[0] =
+		kzalloc(sizeof(struct wsec_key) * BRCMS_DEFAULT_KEYS,
+			GFP_ATOMIC);
+	if (wlc->wsec_def_keys[0] == NULL) {
+		*err = 1015;
+		goto fail;
+	} else {
+		int i;
+		for (i = 1; i < BRCMS_DEFAULT_KEYS; i++) {
+			wlc->wsec_def_keys[i] = (struct wsec_key *)
+			    ((unsigned long)wlc->wsec_def_keys[0] +
+			     (sizeof(struct wsec_key) * i));
+		}
+	}
+
+	wlc->protection = kzalloc(sizeof(struct brcms_protection),
+				  GFP_ATOMIC);
+	if (wlc->protection == NULL) {
+		*err = 1016;
+		goto fail;
+	}
+
+	wlc->stf = kzalloc(sizeof(struct brcms_stf), GFP_ATOMIC);
+	if (wlc->stf == NULL) {
+		*err = 1017;
+		goto fail;
+	}
+
+	wlc->bandstate[0] =
+		kzalloc(sizeof(struct brcms_band)*MAXBANDS, GFP_ATOMIC);
+	if (wlc->bandstate[0] == NULL) {
+		*err = 1025;
+		goto fail;
+	} else {
+		int i;
+
+		for (i = 1; i < MAXBANDS; i++) {
+			wlc->bandstate[i] = (struct brcms_band *)
+				((unsigned long)wlc->bandstate[0]
+				+ (sizeof(struct brcms_band)*i));
+		}
+	}
+
+	wlc->corestate = kzalloc(sizeof(struct brcms_core), GFP_ATOMIC);
+	if (wlc->corestate == NULL) {
+		*err = 1026;
+		goto fail;
+	}
+
+	wlc->corestate->macstat_snapshot =
+		kzalloc(sizeof(struct macstat), GFP_ATOMIC);
+	if (wlc->corestate->macstat_snapshot == NULL) {
+		*err = 1027;
+		goto fail;
+	}
+
+	return wlc;
+
+ fail:
+	brcms_c_detach_mfree(wlc);
+	return NULL;
+}
+
+void brcms_c_detach_mfree(struct brcms_c_info *wlc)
+{
+	if (wlc == NULL)
+		return;
+
+	brcms_c_bsscfg_mfree(wlc->cfg);
+	brcms_c_pub_mfree(wlc->pub);
+	kfree(wlc->modulecb);
+	kfree(wlc->default_bss);
+	kfree(wlc->wsec_def_keys[0]);
+	kfree(wlc->protection);
+	kfree(wlc->stf);
+	kfree(wlc->bandstate[0]);
+	kfree(wlc->corestate->macstat_snapshot);
+	kfree(wlc->corestate);
+	kfree(wlc->hw->bandstate[0]);
+	kfree(wlc->hw);
+
+	/* free the wlc */
+	kfree(wlc);
+	wlc = NULL;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/alloc.h b/drivers/net/wireless/brcm80211/brcmsmac/alloc.h
new file mode 100644
index 0000000..f465d30
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/alloc.h
@@ -0,0 +1,19 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+extern struct brcms_c_info *brcms_c_attach_malloc(uint unit, uint *err,
+						  uint devid);
+extern void brcms_c_detach_mfree(struct brcms_c_info *wlc);
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
new file mode 100644
index 0000000..fcaf61e
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.c
@@ -0,0 +1,1219 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <net/mac80211.h>
+
+#include "rate.h"
+#include "scb.h"
+#include "phy/phy_hal.h"
+#include "antsel.h"
+#include "main.h"
+#include "ampdu.h"
+
+#define AMPDU_MAX_MPDU		32	/* max number of mpdus in an ampdu */
+#define AMPDU_NUM_MPDU_LEGACY	16	/* max number of mpdus in an ampdu to a legacy */
+#define AMPDU_TX_BA_MAX_WSIZE	64	/* max Tx ba window size (in pdu) */
+#define AMPDU_TX_BA_DEF_WSIZE	64	/* default Tx ba window size (in pdu) */
+#define AMPDU_RX_BA_DEF_WSIZE   64	/* max Rx ba window size (in pdu) */
+#define AMPDU_RX_BA_MAX_WSIZE   64	/* default Rx ba window size (in pdu) */
+#define	AMPDU_MAX_DUR		5	/* max dur of tx ampdu (in msec) */
+#define AMPDU_DEF_RETRY_LIMIT	5	/* default tx retry limit */
+#define AMPDU_DEF_RR_RETRY_LIMIT	2	/* default tx retry limit at reg rate */
+#define AMPDU_DEF_TXPKT_WEIGHT	2	/* default weight of ampdu in txfifo */
+#define AMPDU_DEF_FFPLD_RSVD	2048	/* default ffpld reserved bytes */
+#define AMPDU_INI_FREE		10	/* # of inis to be freed on detach */
+#define	AMPDU_SCB_MAX_RELEASE	20	/* max # of mpdus released at a time */
+
+#define NUM_FFPLD_FIFO 4	/* number of fifo concerned by pre-loading */
+#define FFPLD_TX_MAX_UNFL   200	/* default value of the average number of ampdu
+				 * without underflows
+				 */
+#define FFPLD_MPDU_SIZE 1800	/* estimate of maximum mpdu size */
+#define FFPLD_MAX_MCS 23	/* we don't deal with mcs 32 */
+#define FFPLD_PLD_INCR 1000	/* increments in bytes */
+#define FFPLD_MAX_AMPDU_CNT 5000	/* maximum number of ampdu we
+					 * accumulate between resets.
+					 */
+
+#define TX_SEQ_TO_INDEX(seq) ((seq) % AMPDU_TX_BA_MAX_WSIZE)
+
+/* max possible overhead per mpdu in the ampdu; 3 is for roundup if needed */
+#define AMPDU_MAX_MPDU_OVERHEAD (FCS_LEN + DOT11_ICV_AES_LEN +\
+	AMPDU_DELIMITER_LEN + 3\
+	+ DOT11_A4_HDR_LEN + DOT11_QOS_LEN + DOT11_IV_MAX_LEN)
+
+/* structure to hold tx fifo information and pre-loading state
+ * counters specific to tx underflows of ampdus
+ * some counters might be redundant with the ones in wlc or ampdu structures.
+ * This allows to maintain a specific state independently of
+ * how often and/or when the wlc counters are updated.
+ */
+struct brcms_fifo_info {
+	u16 ampdu_pld_size;	/* number of bytes to be pre-loaded */
+	u8 mcs2ampdu_table[FFPLD_MAX_MCS + 1];	/* per-mcs max # of mpdus in an ampdu */
+	u16 prev_txfunfl;	/* num of underflows last read from the HW macstats counter */
+	u32 accum_txfunfl;	/* num of underflows since we modified pld params */
+	u32 accum_txampdu;	/* num of tx ampdu since we modified pld params  */
+	u32 prev_txampdu;	/* previous reading of tx ampdu */
+	u32 dmaxferrate;	/* estimated dma avg xfer rate in kbits/sec */
+};
+
+/* AMPDU module specific state */
+struct ampdu_info {
+	struct brcms_c_info *wlc;	/* pointer to main wlc structure */
+	int scb_handle;		/* scb cubby handle to retrieve data from scb */
+	u8 ini_enable[AMPDU_MAX_SCB_TID];	/* per-tid initiator enable/disable of ampdu */
+	u8 ba_tx_wsize;	/* Tx ba window size (in pdu) */
+	u8 ba_rx_wsize;	/* Rx ba window size (in pdu) */
+	u8 retry_limit;	/* mpdu transmit retry limit */
+	u8 rr_retry_limit;	/* mpdu transmit retry limit at regular rate */
+	u8 retry_limit_tid[AMPDU_MAX_SCB_TID];	/* per-tid mpdu transmit retry limit */
+	/* per-tid mpdu transmit retry limit at regular rate */
+	u8 rr_retry_limit_tid[AMPDU_MAX_SCB_TID];
+	u8 mpdu_density;	/* min mpdu spacing (0-7) ==> 2^(x-1)/8 usec */
+	s8 max_pdu;		/* max pdus allowed in ampdu */
+	u8 dur;		/* max duration of an ampdu (in msec) */
+	u8 txpkt_weight;	/* weight of ampdu in txfifo; reduces rate lag */
+	u8 rx_factor;	/* maximum rx ampdu factor (0-3) ==> 2^(13+x) bytes */
+	u32 ffpld_rsvd;	/* number of bytes to reserve for preload */
+	u32 max_txlen[MCS_TABLE_SIZE][2][2];	/* max size of ampdu per mcs, bw and sgi */
+	void *ini_free[AMPDU_INI_FREE];	/* array of ini's to be freed on detach */
+	bool mfbr;		/* enable multiple fallback rate */
+	u32 tx_max_funl;	/* underflows should be kept such that
+				 * (tx_max_funfl*underflows) < tx frames
+				 */
+	/* table of fifo infos */
+	struct brcms_fifo_info fifo_tb[NUM_FFPLD_FIFO];
+
+};
+
+/* used for flushing ampdu packets */
+struct cb_del_ampdu_pars {
+	struct ieee80211_sta *sta;
+	u16 tid;
+};
+
+#define AMPDU_CLEANUPFLAG_RX   (0x1)
+#define AMPDU_CLEANUPFLAG_TX   (0x2)
+
+#define SCB_AMPDU_CUBBY(ampdu, scb) (&(scb->scb_ampdu))
+#define SCB_AMPDU_INI(scb_ampdu, tid) (&(scb_ampdu->ini[tid]))
+
+static void brcms_c_ffpld_init(struct ampdu_info *ampdu);
+static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int f);
+static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f);
+
+static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu,
+					       u8 dur);
+static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu,
+					    struct scb *scb);
+static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu);
+
+#define brcms_c_ampdu_txflowcontrol(a, b, c)	do {} while (0)
+
+static void
+brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu,
+				  struct scb *scb,
+				  struct sk_buff *p, struct tx_status *txs,
+				  u32 frmtxstatus, u32 frmtxstatus2);
+
+static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu);
+static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on);
+
+struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc)
+{
+	struct ampdu_info *ampdu;
+	int i;
+
+	ampdu = kzalloc(sizeof(struct ampdu_info), GFP_ATOMIC);
+	if (!ampdu) {
+		wiphy_err(wlc->wiphy, "wl%d: brcms_c_ampdu_attach: out of mem"
+			  "\n", wlc->pub->unit);
+		return NULL;
+	}
+	ampdu->wlc = wlc;
+
+	for (i = 0; i < AMPDU_MAX_SCB_TID; i++)
+		ampdu->ini_enable[i] = true;
+	/* Disable ampdu for VO by default */
+	ampdu->ini_enable[PRIO_8021D_VO] = false;
+	ampdu->ini_enable[PRIO_8021D_NC] = false;
+
+	/* Disable ampdu for BK by default since not enough fifo space */
+	ampdu->ini_enable[PRIO_8021D_NONE] = false;
+	ampdu->ini_enable[PRIO_8021D_BK] = false;
+
+	ampdu->ba_tx_wsize = AMPDU_TX_BA_DEF_WSIZE;
+	ampdu->ba_rx_wsize = AMPDU_RX_BA_DEF_WSIZE;
+	ampdu->mpdu_density = AMPDU_DEF_MPDU_DENSITY;
+	ampdu->max_pdu = AUTO;
+	ampdu->dur = AMPDU_MAX_DUR;
+	ampdu->txpkt_weight = AMPDU_DEF_TXPKT_WEIGHT;
+
+	ampdu->ffpld_rsvd = AMPDU_DEF_FFPLD_RSVD;
+	/* bump max ampdu rcv size to 64k for all 11n devices except 4321A0 and 4321A1 */
+	if (BRCMS_ISNPHY(wlc->band) && NREV_LT(wlc->band->phyrev, 2))
+		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_32K;
+	else
+		ampdu->rx_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	ampdu->retry_limit = AMPDU_DEF_RETRY_LIMIT;
+	ampdu->rr_retry_limit = AMPDU_DEF_RR_RETRY_LIMIT;
+
+	for (i = 0; i < AMPDU_MAX_SCB_TID; i++) {
+		ampdu->retry_limit_tid[i] = ampdu->retry_limit;
+		ampdu->rr_retry_limit_tid[i] = ampdu->rr_retry_limit;
+	}
+
+	brcms_c_scb_ampdu_update_max_txlen(ampdu, ampdu->dur);
+	ampdu->mfbr = false;
+	/* try to set ampdu to the default value */
+	brcms_c_ampdu_set(ampdu, wlc->pub->_ampdu);
+
+	ampdu->tx_max_funl = FFPLD_TX_MAX_UNFL;
+	brcms_c_ffpld_init(ampdu);
+
+	return ampdu;
+}
+
+void brcms_c_ampdu_detach(struct ampdu_info *ampdu)
+{
+	int i;
+
+	if (!ampdu)
+		return;
+
+	/* free all ini's which were to be freed on callbacks which were never called */
+	for (i = 0; i < AMPDU_INI_FREE; i++) {
+		kfree(ampdu->ini_free[i]);
+	}
+
+	brcms_c_module_unregister(ampdu->wlc->pub, "ampdu", ampdu);
+	kfree(ampdu);
+}
+
+static void brcms_c_scb_ampdu_update_config(struct ampdu_info *ampdu,
+					    struct scb *scb)
+{
+	struct scb_ampdu *scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+	int i;
+
+	scb_ampdu->max_pdu = (u8) ampdu->wlc->pub->tunables->ampdunummpdu;
+
+	/* go back to legacy size if some preloading is occurring */
+	for (i = 0; i < NUM_FFPLD_FIFO; i++) {
+		if (ampdu->fifo_tb[i].ampdu_pld_size > FFPLD_PLD_INCR)
+			scb_ampdu->max_pdu = AMPDU_NUM_MPDU_LEGACY;
+	}
+
+	/* apply user override */
+	if (ampdu->max_pdu != AUTO)
+		scb_ampdu->max_pdu = (u8) ampdu->max_pdu;
+
+	scb_ampdu->release = min_t(u8, scb_ampdu->max_pdu, AMPDU_SCB_MAX_RELEASE);
+
+	if (scb_ampdu->max_rx_ampdu_bytes)
+		scb_ampdu->release = min_t(u8, scb_ampdu->release,
+			scb_ampdu->max_rx_ampdu_bytes / 1600);
+
+	scb_ampdu->release = min(scb_ampdu->release,
+				 ampdu->fifo_tb[TX_AC_BE_FIFO].
+				 mcs2ampdu_table[FFPLD_MAX_MCS]);
+}
+
+static void brcms_c_scb_ampdu_update_config_all(struct ampdu_info *ampdu)
+{
+	brcms_c_scb_ampdu_update_config(ampdu, ampdu->wlc->pub->global_scb);
+}
+
+static void brcms_c_ffpld_init(struct ampdu_info *ampdu)
+{
+	int i, j;
+	struct brcms_fifo_info *fifo;
+
+	for (j = 0; j < NUM_FFPLD_FIFO; j++) {
+		fifo = (ampdu->fifo_tb + j);
+		fifo->ampdu_pld_size = 0;
+		for (i = 0; i <= FFPLD_MAX_MCS; i++)
+			fifo->mcs2ampdu_table[i] = 255;
+		fifo->dmaxferrate = 0;
+		fifo->accum_txampdu = 0;
+		fifo->prev_txfunfl = 0;
+		fifo->accum_txfunfl = 0;
+
+	}
+}
+
+/* evaluate the dma transfer rate using the tx underflows as feedback.
+ * If necessary, increase tx fifo preloading. If not enough,
+ * decrease maximum ampdu size for each mcs till underflows stop
+ * Return 1 if pre-loading not active, -1 if not an underflow event,
+ * 0 if pre-loading module took care of the event.
+ */
+static int brcms_c_ffpld_check_txfunfl(struct brcms_c_info *wlc, int fid)
+{
+	struct ampdu_info *ampdu = wlc->ampdu;
+	u32 phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
+	u32 txunfl_ratio;
+	u8 max_mpdu;
+	u32 current_ampdu_cnt = 0;
+	u16 max_pld_size;
+	u32 new_txunfl;
+	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + fid);
+	uint xmtfifo_sz;
+	u16 cur_txunfl;
+
+	/* return if we got here for a different reason than underflows */
+	cur_txunfl = brcms_c_read_shm(wlc,
+				      M_UCODE_MACSTAT +
+				      offsetof(struct macstat, txfunfl[fid]));
+	new_txunfl = (u16) (cur_txunfl - fifo->prev_txfunfl);
+	if (new_txunfl == 0) {
+		BCMMSG(wlc->wiphy, "TX status FRAG set but no tx underflows\n");
+		return -1;
+	}
+	fifo->prev_txfunfl = cur_txunfl;
+
+	if (!ampdu->tx_max_funl)
+		return 1;
+
+	/* check if fifo is big enough */
+	if (brcms_c_xmtfifo_sz_get(wlc, fid, &xmtfifo_sz))
+		return -1;
+
+	if ((TXFIFO_SIZE_UNIT * (u32) xmtfifo_sz) <= ampdu->ffpld_rsvd)
+		return 1;
+
+	max_pld_size = TXFIFO_SIZE_UNIT * xmtfifo_sz - ampdu->ffpld_rsvd;
+	fifo->accum_txfunfl += new_txunfl;
+
+	/* we need to wait for at least 10 underflows */
+	if (fifo->accum_txfunfl < 10)
+		return 0;
+
+	BCMMSG(wlc->wiphy, "ampdu_count %d  tx_underflows %d\n",
+		current_ampdu_cnt, fifo->accum_txfunfl);
+
+	/*
+	   compute the current ratio of tx unfl per ampdu.
+	   When the current ampdu count becomes too
+	   big while the ratio remains small, we reset
+	   the current count in order to not
+	   introduce too big of a latency in detecting a
+	   large amount of tx underflows later.
+	 */
+
+	txunfl_ratio = current_ampdu_cnt / fifo->accum_txfunfl;
+
+	if (txunfl_ratio > ampdu->tx_max_funl) {
+		if (current_ampdu_cnt >= FFPLD_MAX_AMPDU_CNT) {
+			fifo->accum_txfunfl = 0;
+		}
+		return 0;
+	}
+	max_mpdu =
+	    min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
+
+	/* In case max value max_pdu is already lower than
+	   the fifo depth, there is nothing more we can do.
+	 */
+
+	if (fifo->ampdu_pld_size >= max_mpdu * FFPLD_MPDU_SIZE) {
+		fifo->accum_txfunfl = 0;
+		return 0;
+	}
+
+	if (fifo->ampdu_pld_size < max_pld_size) {
+
+		/* increment by TX_FIFO_PLD_INC bytes */
+		fifo->ampdu_pld_size += FFPLD_PLD_INCR;
+		if (fifo->ampdu_pld_size > max_pld_size)
+			fifo->ampdu_pld_size = max_pld_size;
+
+		/* update scb release size */
+		brcms_c_scb_ampdu_update_config_all(ampdu);
+
+		/*
+		   compute a new dma xfer rate for max_mpdu @ max mcs.
+		   This is the minimum dma rate that
+		   can achieve no underflow condition for the current mpdu size.
+		 */
+		/* note : we divide/multiply by 100 to avoid integer overflows */
+		fifo->dmaxferrate =
+		    (((phy_rate / 100) *
+		      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
+		     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
+
+		BCMMSG(wlc->wiphy, "DMA estimated transfer rate %d; "
+			"pre-load size %d\n",
+			fifo->dmaxferrate, fifo->ampdu_pld_size);
+	} else {
+
+		/* decrease ampdu size */
+		if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] > 1) {
+			if (fifo->mcs2ampdu_table[FFPLD_MAX_MCS] == 255)
+				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] =
+				    AMPDU_NUM_MPDU_LEGACY - 1;
+			else
+				fifo->mcs2ampdu_table[FFPLD_MAX_MCS] -= 1;
+
+			/* recompute the table */
+			brcms_c_ffpld_calc_mcs2ampdu_table(ampdu, fid);
+
+			/* update scb release size */
+			brcms_c_scb_ampdu_update_config_all(ampdu);
+		}
+	}
+	fifo->accum_txfunfl = 0;
+	return 0;
+}
+
+static void brcms_c_ffpld_calc_mcs2ampdu_table(struct ampdu_info *ampdu, int f)
+{
+	int i;
+	u32 phy_rate, dma_rate, tmp;
+	u8 max_mpdu;
+	struct brcms_fifo_info *fifo = (ampdu->fifo_tb + f);
+
+	/* recompute the dma rate */
+	/* note : we divide/multiply by 100 to avoid integer overflows */
+	max_mpdu =
+	    min_t(u8, fifo->mcs2ampdu_table[FFPLD_MAX_MCS], AMPDU_NUM_MPDU_LEGACY);
+	phy_rate = MCS_RATE(FFPLD_MAX_MCS, true, false);
+	dma_rate =
+	    (((phy_rate / 100) *
+	      (max_mpdu * FFPLD_MPDU_SIZE - fifo->ampdu_pld_size))
+	     / (max_mpdu * FFPLD_MPDU_SIZE)) * 100;
+	fifo->dmaxferrate = dma_rate;
+
+	/* fill up the mcs2ampdu table; do not recalc the last mcs */
+	dma_rate = dma_rate >> 7;
+	for (i = 0; i < FFPLD_MAX_MCS; i++) {
+		/* shifting to keep it within integer range */
+		phy_rate = MCS_RATE(i, true, false) >> 7;
+		if (phy_rate > dma_rate) {
+			tmp = ((fifo->ampdu_pld_size * phy_rate) /
+			       ((phy_rate - dma_rate) * FFPLD_MPDU_SIZE)) + 1;
+			tmp = min_t(u32, tmp, 255);
+			fifo->mcs2ampdu_table[i] = (u8) tmp;
+		}
+	}
+}
+
+void
+brcms_c_ampdu_tx_operational(struct brcms_c_info *wlc, u8 tid,
+	u8 ba_wsize,		/* negotiated ba window size (in pdu) */
+	uint max_rx_ampdu_bytes) /* from ht_cap in beacon */
+{
+	struct scb_ampdu *scb_ampdu;
+	struct scb_ampdu_tid_ini *ini;
+	struct ampdu_info *ampdu = wlc->ampdu;
+	struct scb *scb = wlc->pub->global_scb;
+	scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+
+	if (!ampdu->ini_enable[tid]) {
+		wiphy_err(ampdu->wlc->wiphy, "%s: Rejecting tid %d\n",
+			  __func__, tid);
+		return;
+	}
+
+	ini = SCB_AMPDU_INI(scb_ampdu, tid);
+	ini->tid = tid;
+	ini->scb = scb_ampdu->scb;
+	ini->ba_wsize = ba_wsize;
+	scb_ampdu->max_rx_ampdu_bytes = max_rx_ampdu_bytes;
+}
+
+int
+brcms_c_sendampdu(struct ampdu_info *ampdu, struct brcms_txq_info *qi,
+	      struct sk_buff **pdu, int prec)
+{
+	struct brcms_c_info *wlc;
+	struct sk_buff *p, *pkt[AMPDU_MAX_MPDU];
+	u8 tid, ndelim;
+	int err = 0;
+	u8 preamble_type = BRCMS_GF_PREAMBLE;
+	u8 fbr_preamble_type = BRCMS_GF_PREAMBLE;
+	u8 rts_preamble_type = BRCMS_LONG_PREAMBLE;
+	u8 rts_fbr_preamble_type = BRCMS_LONG_PREAMBLE;
+
+	bool rr = true, fbr = false;
+	uint i, count = 0, fifo, seg_cnt = 0;
+	u16 plen, len, seq = 0, mcl, mch, index, frameid, dma_len = 0;
+	u32 ampdu_len, max_ampdu_bytes = 0;
+	struct d11txh *txh = NULL;
+	u8 *plcp;
+	struct ieee80211_hdr *h;
+	struct scb *scb;
+	struct scb_ampdu *scb_ampdu;
+	struct scb_ampdu_tid_ini *ini;
+	u8 mcs = 0;
+	bool use_rts = false, use_cts = false;
+	ratespec_t rspec = 0, rspec_fallback = 0;
+	ratespec_t rts_rspec = 0, rts_rspec_fallback = 0;
+	u16 mimo_ctlchbw = PHY_TXC1_BW_20MHZ;
+	struct ieee80211_rts *rts;
+	u8 rr_retry_limit;
+	struct brcms_fifo_info *f;
+	bool fbr_iscck;
+	struct ieee80211_tx_info *tx_info;
+	u16 qlen;
+	struct wiphy *wiphy;
+
+	wlc = ampdu->wlc;
+	wiphy = wlc->wiphy;
+	p = *pdu;
+
+	tid = (u8) (p->priority);
+
+	f = ampdu->fifo_tb + prio2fifo[tid];
+
+	scb = wlc->pub->global_scb;
+	scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+	ini = &scb_ampdu->ini[tid];
+
+	/* Let pressure continue to build ... */
+	qlen = pktq_plen(&qi->q, prec);
+	if (ini->tx_in_transit > 0 &&
+	    qlen < min(scb_ampdu->max_pdu, ini->ba_wsize)) {
+		/* Collect multiple MPDU's to be sent in the next AMPDU */
+		return -EBUSY;
+	}
+
+	/* at this point we intend to transmit an AMPDU */
+	rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
+	ampdu_len = 0;
+	dma_len = 0;
+	while (p) {
+		struct ieee80211_tx_rate *txrate;
+
+		tx_info = IEEE80211_SKB_CB(p);
+		txrate = tx_info->status.rates;
+
+		if (tx_info->flags & IEEE80211_TX_CTL_AMPDU) {
+			err = brcms_c_prep_pdu(wlc, p, &fifo);
+		} else {
+			wiphy_err(wiphy, "%s: AMPDU flag is off!\n", __func__);
+			*pdu = NULL;
+			err = 0;
+			break;
+		}
+
+		if (err) {
+			if (err == -EBUSY) {
+				wiphy_err(wiphy, "wl%d: sendampdu: "
+					  "prep_xdu retry; seq 0x%x\n",
+					  wlc->pub->unit, seq);
+				*pdu = p;
+				break;
+			}
+
+			/* error in the packet; reject it */
+			wiphy_err(wiphy, "wl%d: sendampdu: prep_xdu "
+				  "rejected; seq 0x%x\n", wlc->pub->unit, seq);
+			*pdu = NULL;
+			break;
+		}
+
+		/* pkt is good to be aggregated */
+		txh = (struct d11txh *) p->data;
+		plcp = (u8 *) (txh + 1);
+		h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
+		seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
+		index = TX_SEQ_TO_INDEX(seq);
+
+		/* check mcl fields and test whether it can be agg'd */
+		mcl = le16_to_cpu(txh->MacTxControlLow);
+		mcl &= ~TXC_AMPDU_MASK;
+		fbr_iscck = !(le16_to_cpu(txh->XtraFrameTypes) & 0x3);
+		txh->PreloadSize = 0;	/* always default to 0 */
+
+		/*  Handle retry limits */
+		if (txrate[0].count <= rr_retry_limit) {
+			txrate[0].count++;
+			rr = true;
+			fbr = false;
+		} else {
+			fbr = true;
+			rr = false;
+			txrate[1].count++;
+		}
+
+		/* extract the length info */
+		len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+		    : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+
+		/* retrieve null delimiter count */
+		ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
+		seg_cnt += 1;
+
+		BCMMSG(wlc->wiphy, "wl%d: mpdu %d plcp_len %d\n",
+			wlc->pub->unit, count, len);
+
+		/*
+		 * aggregateable mpdu. For ucode/hw agg,
+		 * test whether need to break or change the epoch
+		 */
+		if (count == 0) {
+			mcl |= (TXC_AMPDU_FIRST << TXC_AMPDU_SHIFT);
+			/* refill the bits since might be a retx mpdu */
+			mcl |= TXC_STARTMSDU;
+			rts = (struct ieee80211_rts *)&txh->rts_frame;
+
+			if (ieee80211_is_rts(rts->frame_control)) {
+				mcl |= TXC_SENDRTS;
+				use_rts = true;
+			}
+			if (ieee80211_is_cts(rts->frame_control)) {
+				mcl |= TXC_SENDCTS;
+				use_cts = true;
+			}
+		} else {
+			mcl |= (TXC_AMPDU_MIDDLE << TXC_AMPDU_SHIFT);
+			mcl &= ~(TXC_STARTMSDU | TXC_SENDRTS | TXC_SENDCTS);
+		}
+
+		len = roundup(len, 4);
+		ampdu_len += (len + (ndelim + 1) * AMPDU_DELIMITER_LEN);
+
+		dma_len += (u16) brcmu_pkttotlen(p);
+
+		BCMMSG(wlc->wiphy, "wl%d: ampdu_len %d"
+			" seg_cnt %d null delim %d\n",
+			wlc->pub->unit, ampdu_len, seg_cnt, ndelim);
+
+		txh->MacTxControlLow = cpu_to_le16(mcl);
+
+		/* this packet is added */
+		pkt[count++] = p;
+
+		/* patch the first MPDU */
+		if (count == 1) {
+			u8 plcp0, plcp3, is40, sgi;
+			struct ieee80211_sta *sta;
+
+			sta = tx_info->control.sta;
+
+			if (rr) {
+				plcp0 = plcp[0];
+				plcp3 = plcp[3];
+			} else {
+				plcp0 = txh->FragPLCPFallback[0];
+				plcp3 = txh->FragPLCPFallback[3];
+
+			}
+			is40 = (plcp0 & MIMO_PLCP_40MHZ) ? 1 : 0;
+			sgi = PLCP3_ISSGI(plcp3) ? 1 : 0;
+			mcs = plcp0 & ~MIMO_PLCP_40MHZ;
+			max_ampdu_bytes =
+			    min(scb_ampdu->max_rx_ampdu_bytes,
+				ampdu->max_txlen[mcs][is40][sgi]);
+
+			if (is40)
+				mimo_ctlchbw =
+				   CHSPEC_SB_UPPER(BRCMS_BAND_PI_RADIO_CHANSPEC)
+				   ? PHY_TXC1_BW_20MHZ_UP : PHY_TXC1_BW_20MHZ;
+
+			/* rebuild the rspec and rspec_fallback */
+			rspec = RSPEC_MIMORATE;
+			rspec |= plcp[0] & ~MIMO_PLCP_40MHZ;
+			if (plcp[0] & MIMO_PLCP_40MHZ)
+				rspec |= (PHY_TXC1_BW_40MHZ << RSPEC_BW_SHIFT);
+
+			if (fbr_iscck)	/* CCK */
+				rspec_fallback =
+				    CCK_RSPEC(CCK_PHY2MAC_RATE
+					      (txh->FragPLCPFallback[0]));
+			else {	/* MIMO */
+				rspec_fallback = RSPEC_MIMORATE;
+				rspec_fallback |=
+				    txh->FragPLCPFallback[0] & ~MIMO_PLCP_40MHZ;
+				if (txh->FragPLCPFallback[0] & MIMO_PLCP_40MHZ)
+					rspec_fallback |=
+					    (PHY_TXC1_BW_40MHZ <<
+					     RSPEC_BW_SHIFT);
+			}
+
+			if (use_rts || use_cts) {
+				rts_rspec =
+				    brcms_c_rspec_to_rts_rspec(wlc,
+					rspec, false, mimo_ctlchbw);
+				rts_rspec_fallback =
+				    brcms_c_rspec_to_rts_rspec(wlc,
+					rspec_fallback, false, mimo_ctlchbw);
+			}
+		}
+
+		/* if (first mpdu for host agg) */
+		/* test whether to add more */
+		if ((MCS_RATE(mcs, true, false) >= f->dmaxferrate) &&
+		    (count == f->mcs2ampdu_table[mcs])) {
+			BCMMSG(wlc->wiphy, "wl%d: PR 37644: stopping"
+				" ampdu at %d for mcs %d\n",
+				wlc->pub->unit, count, mcs);
+			break;
+		}
+
+		if (count == scb_ampdu->max_pdu) {
+			break;
+		}
+
+		/* check to see if the next pkt is a candidate for aggregation */
+		p = pktq_ppeek(&qi->q, prec);
+		tx_info = IEEE80211_SKB_CB(p);	/* tx_info must be checked with current p */
+
+		if (p) {
+			if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
+			    ((u8) (p->priority) == tid)) {
+
+				plen = brcmu_pkttotlen(p) +
+				       AMPDU_MAX_MPDU_OVERHEAD;
+				plen = max(scb_ampdu->min_len, plen);
+
+				if ((plen + ampdu_len) > max_ampdu_bytes) {
+					p = NULL;
+					continue;
+				}
+
+				/* check if there are enough descriptors available */
+				if (TXAVAIL(wlc, fifo) <= (seg_cnt + 1)) {
+					wiphy_err(wiphy, "%s: No fifo space  "
+						  "!!\n", __func__);
+					p = NULL;
+					continue;
+				}
+				p = brcmu_pktq_pdeq(&qi->q, prec);
+			} else {
+				p = NULL;
+			}
+		}
+	}			/* end while(p) */
+
+	ini->tx_in_transit += count;
+
+	if (count) {
+		/* patch up the last txh */
+		txh = (struct d11txh *) pkt[count - 1]->data;
+		mcl = le16_to_cpu(txh->MacTxControlLow);
+		mcl &= ~TXC_AMPDU_MASK;
+		mcl |= (TXC_AMPDU_LAST << TXC_AMPDU_SHIFT);
+		txh->MacTxControlLow = cpu_to_le16(mcl);
+
+		/* remove the null delimiter after last mpdu */
+		ndelim = txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM];
+		txh->RTSPLCPFallback[AMPDU_FBR_NULL_DELIM] = 0;
+		ampdu_len -= ndelim * AMPDU_DELIMITER_LEN;
+
+		/* remove the pad len from last mpdu */
+		fbr_iscck = ((le16_to_cpu(txh->XtraFrameTypes) & 0x3) == 0);
+		len = fbr_iscck ? BRCMS_GET_CCK_PLCP_LEN(txh->FragPLCPFallback)
+		    : BRCMS_GET_MIMO_PLCP_LEN(txh->FragPLCPFallback);
+		ampdu_len -= roundup(len, 4) - len;
+
+		/* patch up the first txh & plcp */
+		txh = (struct d11txh *) pkt[0]->data;
+		plcp = (u8 *) (txh + 1);
+
+		BRCMS_SET_MIMO_PLCP_LEN(plcp, ampdu_len);
+		/* mark plcp to indicate ampdu */
+		BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
+
+		/* reset the mixed mode header durations */
+		if (txh->MModeLen) {
+			u16 mmodelen =
+			    brcms_c_calc_lsig_len(wlc, rspec, ampdu_len);
+			txh->MModeLen = cpu_to_le16(mmodelen);
+			preamble_type = BRCMS_MM_PREAMBLE;
+		}
+		if (txh->MModeFbrLen) {
+			u16 mmfbrlen =
+			    brcms_c_calc_lsig_len(wlc, rspec_fallback,
+						  ampdu_len);
+			txh->MModeFbrLen = cpu_to_le16(mmfbrlen);
+			fbr_preamble_type = BRCMS_MM_PREAMBLE;
+		}
+
+		/* set the preload length */
+		if (MCS_RATE(mcs, true, false) >= f->dmaxferrate) {
+			dma_len = min(dma_len, f->ampdu_pld_size);
+			txh->PreloadSize = cpu_to_le16(dma_len);
+		} else
+			txh->PreloadSize = 0;
+
+		mch = le16_to_cpu(txh->MacTxControlHigh);
+
+		/* update RTS dur fields */
+		if (use_rts || use_cts) {
+			u16 durid;
+			rts = (struct ieee80211_rts *)&txh->rts_frame;
+			if ((mch & TXC_PREAMBLE_RTS_MAIN_SHORT) ==
+			    TXC_PREAMBLE_RTS_MAIN_SHORT)
+				rts_preamble_type = BRCMS_SHORT_PREAMBLE;
+
+			if ((mch & TXC_PREAMBLE_RTS_FB_SHORT) ==
+			    TXC_PREAMBLE_RTS_FB_SHORT)
+				rts_fbr_preamble_type = BRCMS_SHORT_PREAMBLE;
+
+			durid =
+			    brcms_c_compute_rtscts_dur(wlc, use_cts, rts_rspec,
+						   rspec, rts_preamble_type,
+						   preamble_type, ampdu_len,
+						   true);
+			rts->duration = cpu_to_le16(durid);
+			durid = brcms_c_compute_rtscts_dur(wlc, use_cts,
+						       rts_rspec_fallback,
+						       rspec_fallback,
+						       rts_fbr_preamble_type,
+						       fbr_preamble_type,
+						       ampdu_len, true);
+			txh->RTSDurFallback = cpu_to_le16(durid);
+			/* set TxFesTimeNormal */
+			txh->TxFesTimeNormal = rts->duration;
+			/* set fallback rate version of TxFesTimeNormal */
+			txh->TxFesTimeFallback = txh->RTSDurFallback;
+		}
+
+		/* set flag and plcp for fallback rate */
+		if (fbr) {
+			mch |= TXC_AMPDU_FBR;
+			txh->MacTxControlHigh = cpu_to_le16(mch);
+			BRCMS_SET_MIMO_PLCP_AMPDU(plcp);
+			BRCMS_SET_MIMO_PLCP_AMPDU(txh->FragPLCPFallback);
+		}
+
+		BCMMSG(wlc->wiphy, "wl%d: count %d ampdu_len %d\n",
+			wlc->pub->unit, count, ampdu_len);
+
+		/* inform rate_sel if it this is a rate probe pkt */
+		frameid = le16_to_cpu(txh->TxFrameID);
+		if (frameid & TXFID_RATE_PROBE_MASK) {
+			wiphy_err(wiphy, "%s: XXX what to do with "
+				  "TXFID_RATE_PROBE_MASK!?\n", __func__);
+		}
+		for (i = 0; i < count; i++)
+			brcms_c_txfifo(wlc, fifo, pkt[i], i == (count - 1),
+				   ampdu->txpkt_weight);
+
+	}
+	/* endif (count) */
+	return err;
+}
+
+void
+brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+		     struct sk_buff *p, struct tx_status *txs)
+{
+	struct scb_ampdu *scb_ampdu;
+	struct brcms_c_info *wlc = ampdu->wlc;
+	struct scb_ampdu_tid_ini *ini;
+	u32 s1 = 0, s2 = 0;
+	struct ieee80211_tx_info *tx_info;
+
+	tx_info = IEEE80211_SKB_CB(p);
+
+	/* BMAC_NOTE: For the split driver, second level txstatus comes later
+	 * So if the ACK was received then wait for the second level else just
+	 * call the first one
+	 */
+	if (txs->status & TX_STATUS_ACK_RCV) {
+		u8 status_delay = 0;
+
+		/* wait till the next 8 bytes of txstatus is available */
+		while (((s1 = R_REG(&wlc->regs->frmtxstatus)) & TXS_V) == 0) {
+			udelay(1);
+			status_delay++;
+			if (status_delay > 10) {
+				return; /* error condition */
+			}
+		}
+
+		s2 = R_REG(&wlc->regs->frmtxstatus2);
+	}
+
+	if (likely(scb)) {
+		scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+		ini = SCB_AMPDU_INI(scb_ampdu, p->priority);
+		brcms_c_ampdu_dotxstatus_complete(ampdu, scb, p, txs, s1, s2);
+	} else {
+		/* loop through all pkts and free */
+		u8 queue = txs->frameid & TXFID_QUEUE_MASK;
+		struct d11txh *txh;
+		u16 mcl;
+		while (p) {
+			tx_info = IEEE80211_SKB_CB(p);
+			txh = (struct d11txh *) p->data;
+			mcl = le16_to_cpu(txh->MacTxControlLow);
+			brcmu_pkt_buf_free_skb(p);
+			/* break out if last packet of ampdu */
+			if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
+			    TXC_AMPDU_LAST)
+				break;
+			p = GETNEXTTXP(wlc, queue);
+		}
+		brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
+	}
+	brcms_c_ampdu_txflowcontrol(wlc, scb_ampdu, ini);
+}
+
+static void
+brcms_c_ampdu_rate_status(struct brcms_c_info *wlc,
+			  struct ieee80211_tx_info *tx_info,
+			  struct tx_status *txs, u8 mcs)
+{
+	struct ieee80211_tx_rate *txrate = tx_info->status.rates;
+	int i;
+
+	/* clear the rest of the rates */
+	for (i = 2; i < IEEE80211_TX_MAX_RATES; i++) {
+		txrate[i].idx = -1;
+		txrate[i].count = 0;
+	}
+}
+
+#define SHORTNAME "AMPDU status"
+
+static void
+brcms_c_ampdu_dotxstatus_complete(struct ampdu_info *ampdu, struct scb *scb,
+			      struct sk_buff *p, struct tx_status *txs,
+			      u32 s1, u32 s2)
+{
+	struct scb_ampdu *scb_ampdu;
+	struct brcms_c_info *wlc = ampdu->wlc;
+	struct scb_ampdu_tid_ini *ini;
+	u8 bitmap[8], queue, tid;
+	struct d11txh *txh;
+	u8 *plcp;
+	struct ieee80211_hdr *h;
+	u16 seq, start_seq = 0, bindex, index, mcl;
+	u8 mcs = 0;
+	bool ba_recd = false, ack_recd = false;
+	u8 suc_mpdu = 0, tot_mpdu = 0;
+	uint supr_status;
+	bool update_rate = true, retry = true, tx_error = false;
+	u16 mimoantsel = 0;
+	u8 antselid = 0;
+	u8 retry_limit, rr_retry_limit;
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(p);
+	struct wiphy *wiphy = wlc->wiphy;
+
+#ifdef BCMDBG
+	u8 hole[AMPDU_MAX_MPDU];
+	memset(hole, 0, sizeof(hole));
+#endif
+
+	scb_ampdu = SCB_AMPDU_CUBBY(ampdu, scb);
+	tid = (u8) (p->priority);
+
+	ini = SCB_AMPDU_INI(scb_ampdu, tid);
+	retry_limit = ampdu->retry_limit_tid[tid];
+	rr_retry_limit = ampdu->rr_retry_limit_tid[tid];
+	memset(bitmap, 0, sizeof(bitmap));
+	queue = txs->frameid & TXFID_QUEUE_MASK;
+	supr_status = txs->status & TX_STATUS_SUPR_MASK;
+
+	if (txs->status & TX_STATUS_ACK_RCV) {
+		if (TX_STATUS_SUPR_UF == supr_status) {
+			update_rate = false;
+		}
+
+		WARN_ON(!(txs->status & TX_STATUS_INTERMEDIATE));
+		start_seq = txs->sequence >> SEQNUM_SHIFT;
+		bitmap[0] = (txs->status & TX_STATUS_BA_BMAP03_MASK) >>
+		    TX_STATUS_BA_BMAP03_SHIFT;
+
+		WARN_ON(s1 & TX_STATUS_INTERMEDIATE);
+		WARN_ON(!(s1 & TX_STATUS_AMPDU));
+
+		bitmap[0] |=
+		    (s1 & TX_STATUS_BA_BMAP47_MASK) <<
+		    TX_STATUS_BA_BMAP47_SHIFT;
+		bitmap[1] = (s1 >> 8) & 0xff;
+		bitmap[2] = (s1 >> 16) & 0xff;
+		bitmap[3] = (s1 >> 24) & 0xff;
+
+		bitmap[4] = s2 & 0xff;
+		bitmap[5] = (s2 >> 8) & 0xff;
+		bitmap[6] = (s2 >> 16) & 0xff;
+		bitmap[7] = (s2 >> 24) & 0xff;
+
+		ba_recd = true;
+	} else {
+		if (supr_status) {
+			update_rate = false;
+			if (supr_status == TX_STATUS_SUPR_BADCH) {
+				wiphy_err(wiphy, "%s: Pkt tx suppressed, "
+					  "illegal channel possibly %d\n",
+					  __func__, CHSPEC_CHANNEL(
+					  wlc->default_bss->chanspec));
+			} else {
+				if (supr_status != TX_STATUS_SUPR_FRAG)
+					wiphy_err(wiphy, "%s:"
+						  "supr_status 0x%x\n",
+						  __func__, supr_status);
+			}
+			/* no need to retry for badch; will fail again */
+			if (supr_status == TX_STATUS_SUPR_BADCH ||
+			    supr_status == TX_STATUS_SUPR_EXPTIME) {
+				retry = false;
+			} else if (supr_status == TX_STATUS_SUPR_EXPTIME) {
+				/* TX underflow : try tuning pre-loading or ampdu size */
+			} else if (supr_status == TX_STATUS_SUPR_FRAG) {
+				/* if there were underflows, but pre-loading is not active,
+				   notify rate adaptation.
+				 */
+				if (brcms_c_ffpld_check_txfunfl(wlc,
+							prio2fifo[tid]) > 0) {
+					tx_error = true;
+				}
+			}
+		} else if (txs->phyerr) {
+			update_rate = false;
+			wiphy_err(wiphy, "wl%d: ampdu tx phy "
+				  "error (0x%x)\n", wlc->pub->unit,
+				  txs->phyerr);
+
+			if (WL_ERROR_ON()) {
+				brcmu_prpkt("txpkt (AMPDU)", p);
+				brcms_c_print_txdesc((struct d11txh *) p->data);
+			}
+			brcms_c_print_txstatus(txs);
+		}
+	}
+
+	/* loop through all pkts and retry if not acked */
+	while (p) {
+		tx_info = IEEE80211_SKB_CB(p);
+		txh = (struct d11txh *) p->data;
+		mcl = le16_to_cpu(txh->MacTxControlLow);
+		plcp = (u8 *) (txh + 1);
+		h = (struct ieee80211_hdr *)(plcp + D11_PHY_HDR_LEN);
+		seq = le16_to_cpu(h->seq_ctrl) >> SEQNUM_SHIFT;
+
+		if (tot_mpdu == 0) {
+			mcs = plcp[0] & MIMO_PLCP_MCS_MASK;
+			mimoantsel = le16_to_cpu(txh->ABI_MimoAntSel);
+		}
+
+		index = TX_SEQ_TO_INDEX(seq);
+		ack_recd = false;
+		if (ba_recd) {
+			bindex = MODSUB_POW2(seq, start_seq, SEQNUM_MAX);
+			BCMMSG(wlc->wiphy, "tid %d seq %d,"
+				" start_seq %d, bindex %d set %d, index %d\n",
+				tid, seq, start_seq, bindex,
+				isset(bitmap, bindex), index);
+			/* if acked then clear bit and free packet */
+			if ((bindex < AMPDU_TX_BA_MAX_WSIZE)
+			    && isset(bitmap, bindex)) {
+				ini->tx_in_transit--;
+				ini->txretry[index] = 0;
+
+				/* ampdu_ack_len: number of acked aggregated frames */
+				/* ampdu_len: number of aggregated frames */
+				brcms_c_ampdu_rate_status(wlc, tx_info, txs,
+							  mcs);
+				tx_info->flags |= IEEE80211_TX_STAT_ACK;
+				tx_info->flags |= IEEE80211_TX_STAT_AMPDU;
+				tx_info->status.ampdu_ack_len =
+					tx_info->status.ampdu_len = 1;
+
+				skb_pull(p, D11_PHY_HDR_LEN);
+				skb_pull(p, D11_TXH_LEN);
+
+				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
+							    p);
+				ack_recd = true;
+				suc_mpdu++;
+			}
+		}
+		/* either retransmit or send bar if ack not recd */
+		if (!ack_recd) {
+			struct ieee80211_tx_rate *txrate =
+			    tx_info->status.rates;
+			if (retry && (txrate[0].count < (int)retry_limit)) {
+				ini->txretry[index]++;
+				ini->tx_in_transit--;
+				/* Use high prededence for retransmit to give some punch */
+				/* brcms_c_txq_enq(wlc, scb, p,
+				 * BRCMS_PRIO_TO_PREC(tid)); */
+				brcms_c_txq_enq(wlc, scb, p,
+					    BRCMS_PRIO_TO_HI_PREC(tid));
+			} else {
+				/* Retry timeout */
+				ini->tx_in_transit--;
+				ieee80211_tx_info_clear_status(tx_info);
+				tx_info->status.ampdu_ack_len = 0;
+				tx_info->status.ampdu_len = 1;
+				tx_info->flags |=
+				    IEEE80211_TX_STAT_AMPDU_NO_BACK;
+				skb_pull(p, D11_PHY_HDR_LEN);
+				skb_pull(p, D11_TXH_LEN);
+				wiphy_err(wiphy, "%s: BA Timeout, seq %d, in_"
+					"transit %d\n", SHORTNAME, seq,
+					ini->tx_in_transit);
+				ieee80211_tx_status_irqsafe(wlc->pub->ieee_hw,
+							    p);
+			}
+		}
+		tot_mpdu++;
+
+		/* break out if last packet of ampdu */
+		if (((mcl & TXC_AMPDU_MASK) >> TXC_AMPDU_SHIFT) ==
+		    TXC_AMPDU_LAST)
+			break;
+
+		p = GETNEXTTXP(wlc, queue);
+	}
+	brcms_c_send_q(wlc);
+
+	/* update rate state */
+	antselid = brcms_c_antsel_antsel2id(wlc->asi, mimoantsel);
+
+	brcms_c_txfifo_complete(wlc, queue, ampdu->txpkt_weight);
+}
+
+static int brcms_c_ampdu_set(struct ampdu_info *ampdu, bool on)
+{
+	struct brcms_c_info *wlc = ampdu->wlc;
+
+	wlc->pub->_ampdu = false;
+
+	if (on) {
+		if (!N_ENAB(wlc->pub)) {
+			wiphy_err(ampdu->wlc->wiphy, "wl%d: driver not "
+				"nmode enabled\n", wlc->pub->unit);
+			return -ENOTSUPP;
+		}
+		if (!brcms_c_ampdu_cap(ampdu)) {
+			wiphy_err(ampdu->wlc->wiphy, "wl%d: device not "
+				"ampdu capable\n", wlc->pub->unit);
+			return -ENOTSUPP;
+		}
+		wlc->pub->_ampdu = on;
+	}
+
+	return 0;
+}
+
+static bool brcms_c_ampdu_cap(struct ampdu_info *ampdu)
+{
+	if (BRCMS_PHY_11N_CAP(ampdu->wlc->band))
+		return true;
+	else
+		return false;
+}
+
+static void brcms_c_scb_ampdu_update_max_txlen(struct ampdu_info *ampdu, u8 dur)
+{
+	u32 rate, mcs;
+
+	for (mcs = 0; mcs < MCS_TABLE_SIZE; mcs++) {
+		/* rate is in Kbps; dur is in msec ==> len = (rate * dur) / 8 */
+		/* 20MHz, No SGI */
+		rate = MCS_RATE(mcs, false, false);
+		ampdu->max_txlen[mcs][0][0] = (rate * dur) >> 3;
+		/* 40 MHz, No SGI */
+		rate = MCS_RATE(mcs, true, false);
+		ampdu->max_txlen[mcs][1][0] = (rate * dur) >> 3;
+		/* 20MHz, SGI */
+		rate = MCS_RATE(mcs, false, true);
+		ampdu->max_txlen[mcs][0][1] = (rate * dur) >> 3;
+		/* 40 MHz, SGI */
+		rate = MCS_RATE(mcs, true, true);
+		ampdu->max_txlen[mcs][1][1] = (rate * dur) >> 3;
+	}
+}
+
+void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc)
+{
+	char template[T_RAM_ACCESS_SZ * 2];
+
+	/* driver needs to write the ta in the template; ta is at offset 16 */
+	memset(template, 0, sizeof(template));
+	memcpy(template, wlc->pub->cur_etheraddr, ETH_ALEN);
+	brcms_c_write_template_ram(wlc, (T_BA_TPL_BASE + 16),
+				  (T_RAM_ACCESS_SZ * 2),
+				  template);
+}
+
+bool brcms_c_aggregatable(struct brcms_c_info *wlc, u8 tid)
+{
+	return wlc->ampdu->ini_enable[tid];
+}
+
+void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu)
+{
+	struct brcms_c_info *wlc = ampdu->wlc;
+
+	/* Extend ucode internal watchdog timer to match larger received frames */
+	if ((ampdu->rx_factor & IEEE80211_HT_AMPDU_PARM_FACTOR) ==
+	    IEEE80211_HT_MAX_AMPDU_64K) {
+		brcms_c_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_MAX);
+		brcms_c_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_MAX);
+	} else {
+		brcms_c_write_shm(wlc, M_MIMO_MAXSYM, MIMO_MAXSYM_DEF);
+		brcms_c_write_shm(wlc, M_WATCHDOG_8TU, WATCHDOG_8TU_DEF);
+	}
+}
+
+/*
+ * callback function that helps flushing ampdu packets from a priority queue
+ */
+static bool cb_del_ampdu_pkt(struct sk_buff *mpdu, void *arg_a)
+{
+	struct ieee80211_tx_info *tx_info = IEEE80211_SKB_CB(mpdu);
+	struct cb_del_ampdu_pars *ampdu_pars =
+				 (struct cb_del_ampdu_pars *)arg_a;
+	bool rc;
+
+	rc = tx_info->flags & IEEE80211_TX_CTL_AMPDU ? true : false;
+	rc = rc && (tx_info->control.sta == NULL || ampdu_pars->sta == NULL ||
+		    tx_info->control.sta == ampdu_pars->sta);
+	rc = rc && ((u8)(mpdu->priority) == ampdu_pars->tid);
+	return rc;
+}
+
+/*
+ * callback function that helps invalidating ampdu packets in a DMA queue
+ */
+static void dma_cb_fn_ampdu(void *txi, void *arg_a)
+{
+	struct ieee80211_sta *sta = arg_a;
+	struct ieee80211_tx_info *tx_info = (struct ieee80211_tx_info *)txi;
+
+	if ((tx_info->flags & IEEE80211_TX_CTL_AMPDU) &&
+	    (tx_info->control.sta == sta || sta == NULL))
+		tx_info->control.sta = NULL;
+}
+
+/*
+ * When a remote party is no longer available for ampdu communication, any
+ * pending tx ampdu packets in the driver have to be flushed.
+ */
+void brcms_c_ampdu_flush(struct brcms_c_info *wlc,
+		     struct ieee80211_sta *sta, u16 tid)
+{
+	struct brcms_txq_info *qi = wlc->pkt_queue;
+	struct pktq *pq = &qi->q;
+	int prec;
+	struct cb_del_ampdu_pars ampdu_pars;
+
+	ampdu_pars.sta = sta;
+	ampdu_pars.tid = tid;
+	for (prec = 0; prec < pq->num_prec; prec++) {
+		brcmu_pktq_pflush(pq, prec, true, cb_del_ampdu_pkt,
+			    (void *)&ampdu_pars);
+	}
+	brcms_c_inval_dma_pkts(wlc->hw, sta, dma_cb_fn_ampdu);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
new file mode 100644
index 0000000..421f4ba
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/ampdu.h
@@ -0,0 +1,30 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_AMPDU_H_
+#define _BRCM_AMPDU_H_
+
+extern struct ampdu_info *brcms_c_ampdu_attach(struct brcms_c_info *wlc);
+extern void brcms_c_ampdu_detach(struct ampdu_info *ampdu);
+extern int brcms_c_sendampdu(struct ampdu_info *ampdu,
+			     struct brcms_txq_info *qi,
+			     struct sk_buff **aggp, int prec);
+extern void brcms_c_ampdu_dotxstatus(struct ampdu_info *ampdu, struct scb *scb,
+				 struct sk_buff *p, struct tx_status *txs);
+extern void brcms_c_ampdu_macaddr_upd(struct brcms_c_info *wlc);
+extern void brcms_c_ampdu_shm_upd(struct ampdu_info *ampdu);
+
+#endif				/* _BRCM_AMPDU_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.c b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
new file mode 100644
index 0000000..c4e76c0
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.c
@@ -0,0 +1,311 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/slab.h>
+#include <net/mac80211.h>
+
+#include "types.h"
+#include "bmac.h"
+#include "main.h"
+#include "phy_shim.h"
+#include "antsel.h"
+
+#define ANT_SELCFG_AUTO		0x80	/* bit indicates antenna sel AUTO */
+#define ANT_SELCFG_MASK		0x33	/* antenna configuration mask */
+#define ANT_SELCFG_TX_UNICAST	0	/* unicast tx antenna configuration */
+#define ANT_SELCFG_RX_UNICAST	1	/* unicast rx antenna configuration */
+#define ANT_SELCFG_TX_DEF	2	/* default tx antenna configuration */
+#define ANT_SELCFG_RX_DEF	3	/* default rx antenna configuration */
+
+/* useful macros */
+#define BRCMS_ANTSEL_11N_0(ant)	((((ant) & ANT_SELCFG_MASK) >> 4) & 0xf)
+#define BRCMS_ANTSEL_11N_1(ant)	(((ant) & ANT_SELCFG_MASK) & 0xf)
+#define BRCMS_ANTIDX_11N(ant)	(((BRCMS_ANTSEL_11N_0(ant)) << 2) +\
+				(BRCMS_ANTSEL_11N_1(ant)))
+#define BRCMS_ANT_ISAUTO_11N(ant) (((ant) & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO)
+#define BRCMS_ANTSEL_11N(ant)	((ant) & ANT_SELCFG_MASK)
+
+/* antenna switch */
+/* defines for no boardlevel antenna diversity */
+#define ANT_SELCFG_DEF_2x2	0x01	/* default antenna configuration */
+
+/* 2x3 antdiv defines and tables for GPIO communication */
+#define ANT_SELCFG_NUM_2x3	3
+#define ANT_SELCFG_DEF_2x3	0x01	/* default antenna configuration */
+
+/* 2x4 antdiv rev4 defines and tables for GPIO communication */
+#define ANT_SELCFG_NUM_2x4	4
+#define ANT_SELCFG_DEF_2x4	0x02	/* default antenna configuration */
+
+/* static functions */
+static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
+				 struct brcms_antselcfg *antsel);
+static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id);
+static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg);
+static void brcms_c_antsel_init_cfg(struct antsel_info *asi,
+				struct brcms_antselcfg *antsel,
+				bool auto_sel);
+
+const u16 mimo_2x4_div_antselpat_tbl[] = {
+	0, 0, 0x9, 0xa,		/* ant0: 0 ant1: 2,3 */
+	0, 0, 0x5, 0x6,		/* ant0: 1 ant1: 2,3 */
+	0, 0, 0, 0,		/* n.a.              */
+	0, 0, 0, 0		/* n.a.              */
+};
+
+const u8 mimo_2x4_div_antselid_tbl[16] = {
+	0, 0, 0, 0, 0, 2, 3, 0,
+	0, 0, 1, 0, 0, 0, 0, 0	/* pat to antselid */
+};
+
+const u16 mimo_2x3_div_antselpat_tbl[] = {
+	16, 0, 1, 16,		/* ant0: 0 ant1: 1,2 */
+	16, 16, 16, 16,		/* n.a.              */
+	16, 2, 16, 16,		/* ant0: 2 ant1: 1   */
+	16, 16, 16, 16		/* n.a.              */
+};
+
+const u8 mimo_2x3_div_antselid_tbl[16] = {
+	0, 1, 2, 0, 0, 0, 0, 0,
+	0, 0, 0, 0, 0, 0, 0, 0	/* pat to antselid */
+};
+
+struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc)
+{
+	struct antsel_info *asi;
+
+	asi = kzalloc(sizeof(struct antsel_info), GFP_ATOMIC);
+	if (!asi) {
+		wiphy_err(wlc->wiphy, "wl%d: brcms_c_antsel_attach: out of "
+			  "mem\n", wlc->pub->unit);
+		return NULL;
+	}
+
+	asi->wlc = wlc;
+	asi->pub = wlc->pub;
+	asi->antsel_type = ANTSEL_NA;
+	asi->antsel_avail = false;
+	asi->antsel_antswitch = (u8) getintvar(asi->pub->vars, "antswitch");
+
+	if ((asi->pub->sromrev >= 4) && (asi->antsel_antswitch != 0)) {
+		switch (asi->antsel_antswitch) {
+		case ANTSWITCH_TYPE_1:
+		case ANTSWITCH_TYPE_2:
+		case ANTSWITCH_TYPE_3:
+			/* 4321/2 board with 2x3 switch logic */
+			asi->antsel_type = ANTSEL_2x3;
+			/* Antenna selection availability */
+			if (((u16) getintvar(asi->pub->vars, "aa2g") == 7) ||
+			    ((u16) getintvar(asi->pub->vars, "aa5g") == 7)) {
+				asi->antsel_avail = true;
+			} else
+			    if (((u16) getintvar(asi->pub->vars, "aa2g") ==
+				 3)
+				|| ((u16) getintvar(asi->pub->vars, "aa5g")
+				    == 3)) {
+				asi->antsel_avail = false;
+			} else {
+				asi->antsel_avail = false;
+				wiphy_err(wlc->wiphy, "antsel_attach: 2o3 "
+					  "board cfg invalid\n");
+			}
+			break;
+		default:
+			break;
+		}
+	} else if ((asi->pub->sromrev == 4) &&
+		   ((u16) getintvar(asi->pub->vars, "aa2g") == 7) &&
+		   ((u16) getintvar(asi->pub->vars, "aa5g") == 0)) {
+		/* hack to match old 4321CB2 cards with 2of3 antenna switch */
+		asi->antsel_type = ANTSEL_2x3;
+		asi->antsel_avail = true;
+	} else if (asi->pub->boardflags2 & BFL2_2X4_DIV) {
+		asi->antsel_type = ANTSEL_2x4;
+		asi->antsel_avail = true;
+	}
+
+	/* Set the antenna selection type for the low driver */
+	brcms_b_antsel_type_set(wlc->hw, asi->antsel_type);
+
+	/* Init (auto/manual) antenna selection */
+	brcms_c_antsel_init_cfg(asi, &asi->antcfg_11n, true);
+	brcms_c_antsel_init_cfg(asi, &asi->antcfg_cur, true);
+
+	return asi;
+}
+
+void brcms_c_antsel_detach(struct antsel_info *asi)
+{
+	kfree(asi);
+}
+
+void brcms_c_antsel_init(struct antsel_info *asi)
+{
+	if ((asi->antsel_type == ANTSEL_2x3) ||
+	    (asi->antsel_type == ANTSEL_2x4))
+		brcms_c_antsel_cfgupd(asi, &asi->antcfg_11n);
+}
+
+/* boardlevel antenna selection: init antenna selection structure */
+static void
+brcms_c_antsel_init_cfg(struct antsel_info *asi, struct brcms_antselcfg *antsel,
+		    bool auto_sel)
+{
+	if (asi->antsel_type == ANTSEL_2x3) {
+		u8 antcfg_def = ANT_SELCFG_DEF_2x3 |
+		    ((asi->antsel_avail && auto_sel) ? ANT_SELCFG_AUTO : 0);
+		antsel->ant_config[ANT_SELCFG_TX_DEF] = antcfg_def;
+		antsel->ant_config[ANT_SELCFG_TX_UNICAST] = antcfg_def;
+		antsel->ant_config[ANT_SELCFG_RX_DEF] = antcfg_def;
+		antsel->ant_config[ANT_SELCFG_RX_UNICAST] = antcfg_def;
+		antsel->num_antcfg = ANT_SELCFG_NUM_2x3;
+
+	} else if (asi->antsel_type == ANTSEL_2x4) {
+
+		antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x4;
+		antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x4;
+		antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x4;
+		antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x4;
+		antsel->num_antcfg = ANT_SELCFG_NUM_2x4;
+
+	} else {		/* no antenna selection available */
+
+		antsel->ant_config[ANT_SELCFG_TX_DEF] = ANT_SELCFG_DEF_2x2;
+		antsel->ant_config[ANT_SELCFG_TX_UNICAST] = ANT_SELCFG_DEF_2x2;
+		antsel->ant_config[ANT_SELCFG_RX_DEF] = ANT_SELCFG_DEF_2x2;
+		antsel->ant_config[ANT_SELCFG_RX_UNICAST] = ANT_SELCFG_DEF_2x2;
+		antsel->num_antcfg = 0;
+	}
+}
+
+void
+brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef, bool sel,
+		      u8 antselid, u8 fbantselid, u8 *antcfg,
+		      u8 *fbantcfg)
+{
+	u8 ant;
+
+	/* if use default, assign it and return */
+	if (usedef) {
+		*antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_DEF];
+		*fbantcfg = *antcfg;
+		return;
+	}
+
+	if (!sel) {
+		*antcfg = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+		*fbantcfg = *antcfg;
+
+	} else {
+		ant = asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+		if ((ant & ANT_SELCFG_AUTO) == ANT_SELCFG_AUTO) {
+			*antcfg = brcms_c_antsel_id2antcfg(asi, antselid);
+			*fbantcfg = brcms_c_antsel_id2antcfg(asi, fbantselid);
+		} else {
+			*antcfg =
+			    asi->antcfg_11n.ant_config[ANT_SELCFG_TX_UNICAST];
+			*fbantcfg = *antcfg;
+		}
+	}
+	return;
+}
+
+/* boardlevel antenna selection: convert mimo_antsel (ucode interface) to id */
+u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel)
+{
+	u8 antselid = 0;
+
+	if (asi->antsel_type == ANTSEL_2x4) {
+		/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+		antselid = mimo_2x4_div_antselid_tbl[(antsel & 0xf)];
+		return antselid;
+
+	} else if (asi->antsel_type == ANTSEL_2x3) {
+		/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+		antselid = mimo_2x3_div_antselid_tbl[(antsel & 0xf)];
+		return antselid;
+	}
+
+	return antselid;
+}
+
+/* boardlevel antenna selection: convert id to ant_cfg */
+static u8 brcms_c_antsel_id2antcfg(struct antsel_info *asi, u8 id)
+{
+	u8 antcfg = ANT_SELCFG_DEF_2x2;
+
+	if (asi->antsel_type == ANTSEL_2x4) {
+		/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+		antcfg = (((id & 0x2) << 3) | ((id & 0x1) + 2));
+		return antcfg;
+
+	} else if (asi->antsel_type == ANTSEL_2x3) {
+		/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+		antcfg = (((id & 0x02) << 4) | ((id & 0x1) + 1));
+		return antcfg;
+	}
+
+	return antcfg;
+}
+
+/* boardlevel antenna selection: convert ant_cfg to mimo_antsel (ucode interface) */
+static u16 brcms_c_antsel_antcfg2antsel(struct antsel_info *asi, u8 ant_cfg)
+{
+	u8 idx = BRCMS_ANTIDX_11N(BRCMS_ANTSEL_11N(ant_cfg));
+	u16 mimo_antsel = 0;
+
+	if (asi->antsel_type == ANTSEL_2x4) {
+		/* 2x4 antenna diversity board, 4 cfgs: 0-2 0-3 1-2 1-3 */
+		mimo_antsel = (mimo_2x4_div_antselpat_tbl[idx] & 0xf);
+		return mimo_antsel;
+
+	} else if (asi->antsel_type == ANTSEL_2x3) {
+		/* 2x3 antenna selection, 3 cfgs: 0-1 0-2 2-1 */
+		mimo_antsel = (mimo_2x3_div_antselpat_tbl[idx] & 0xf);
+		return mimo_antsel;
+	}
+
+	return mimo_antsel;
+}
+
+/* boardlevel antenna selection: ucode interface control */
+static int brcms_c_antsel_cfgupd(struct antsel_info *asi,
+				 struct brcms_antselcfg *antsel)
+{
+	struct brcms_c_info *wlc = asi->wlc;
+	u8 ant_cfg;
+	u16 mimo_antsel;
+
+	/* 1) Update TX antconfig for all frames that are not unicast data
+	 *    (aka default TX)
+	 */
+	ant_cfg = antsel->ant_config[ANT_SELCFG_TX_DEF];
+	mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
+	brcms_c_write_shm(wlc, M_MIMO_ANTSEL_TXDFLT, mimo_antsel);
+	/* Update driver stats for currently selected default tx/rx antenna config */
+	asi->antcfg_cur.ant_config[ANT_SELCFG_TX_DEF] = ant_cfg;
+
+	/* 2) Update RX antconfig for all frames that are not unicast data
+	 *    (aka default RX)
+	 */
+	ant_cfg = antsel->ant_config[ANT_SELCFG_RX_DEF];
+	mimo_antsel = brcms_c_antsel_antcfg2antsel(asi, ant_cfg);
+	brcms_c_write_shm(wlc, M_MIMO_ANTSEL_RXDFLT, mimo_antsel);
+	/* Update driver stats for currently selected default tx/rx antenna config */
+	asi->antcfg_cur.ant_config[ANT_SELCFG_RX_DEF] = ant_cfg;
+
+	return 0;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/antsel.h b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
new file mode 100644
index 0000000..97ea388
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/antsel.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_ANTSEL_H_
+#define _BRCM_ANTSEL_H_
+
+extern struct antsel_info *brcms_c_antsel_attach(struct brcms_c_info *wlc);
+extern void brcms_c_antsel_detach(struct antsel_info *asi);
+extern void brcms_c_antsel_init(struct antsel_info *asi);
+extern void brcms_c_antsel_antcfg_get(struct antsel_info *asi, bool usedef,
+				  bool sel,
+				  u8 id, u8 fbid, u8 *antcfg,
+				  u8 *fbantcfg);
+extern u8 brcms_c_antsel_antsel2id(struct antsel_info *asi, u16 antsel);
+
+#endif /* _BRCM_ANTSEL_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/bmac.c b/drivers/net/wireless/brcm80211/brcmsmac/bmac.c
new file mode 100644
index 0000000..b25c517
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/bmac.c
@@ -0,0 +1,3593 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/pci.h>
+#include <net/mac80211.h>
+
+#include <brcm_hw_ids.h>
+#include <aiutils.h>
+#include <chipcommon.h>
+#include "types.h"
+#include "rate.h"
+#include "phy/phy_hal.h"
+#include "channel.h"
+#include "main.h"
+#include "ucode_loader.h"
+#include "mac80211_if.h"
+#include "bmac.h"
+
+#define	TIMER_INTERVAL_WATCHDOG_BMAC	1000	/* watchdog timer, in unit of ms */
+
+#define	SYNTHPU_DLY_APHY_US	3700	/* a phy synthpu_dly time in us */
+#define	SYNTHPU_DLY_BPHY_US	1050	/* b/g phy synthpu_dly time in us, default */
+#define	SYNTHPU_DLY_NPHY_US	2048	/* n phy REV3 synthpu_dly time in us, default */
+#define	SYNTHPU_DLY_LPPHY_US	300	/* lpphy synthpu_dly time in us */
+
+#define	SYNTHPU_DLY_PHY_US_QT	100	/* QT synthpu_dly time in us */
+
+#ifndef BMAC_DUP_TO_REMOVE
+
+#define	ANTCNT			10	/* vanilla M_MAX_ANTCNT value */
+
+#endif				/* BMAC_DUP_TO_REMOVE */
+
+#define DMAREG(wlc_hw, direction, fifonum) \
+	((direction == DMA_TX) ? \
+		(void *)&(wlc_hw->regs->fifo64regs[fifonum].dmaxmt) : \
+		(void *)&(wlc_hw->regs->fifo64regs[fifonum].dmarcv))
+
+#define APHY_SLOT_TIME		9
+#define BPHY_SLOT_TIME		20
+
+/*
+ * The following table lists the buffer memory allocated to xmt fifos in HW.
+ * the size is in units of 256bytes(one block), total size is HW dependent
+ * ucode has default fifo partition, sw can overwrite if necessary
+ *
+ * This is documented in twiki under the topic UcodeTxFifo. Please ensure
+ * the twiki is updated before making changes.
+ */
+
+#define XMTFIFOTBL_STARTREV	20	/* Starting corerev for the fifo size table */
+
+static u16 xmtfifo_sz[][NFIFO] = {
+	{20, 192, 192, 21, 17, 5},	/* corerev 20: 5120, 49152, 49152, 5376, 4352, 1280 */
+	{9, 58, 22, 14, 14, 5},	/* corerev 21: 2304, 14848, 5632, 3584, 3584, 1280 */
+	{20, 192, 192, 21, 17, 5},	/* corerev 22: 5120, 49152, 49152, 5376, 4352, 1280 */
+	{20, 192, 192, 21, 17, 5},	/* corerev 23: 5120, 49152, 49152, 5376, 4352, 1280 */
+	{9, 58, 22, 14, 14, 5},	/* corerev 24: 2304, 14848, 5632, 3584, 3584, 1280 */
+};
+
+static void brcms_b_clkctl_clk(struct brcms_hardware *wlc, uint mode);
+static void brcms_b_coreinit(struct brcms_c_info *wlc);
+
+/* used by wlc_wakeucode_init() */
+static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
+			    const struct d11init *inits);
+static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const u32 ucode[],
+			    const uint nbytes);
+static void brcms_ucode_download(struct brcms_hardware *wlc);
+static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw);
+
+/* used by brcms_c_dpc() */
+static bool brcms_b_dotxstatus(struct brcms_hardware *wlc,
+			       struct tx_status *txs, u32 s2);
+static bool brcms_b_txstatus(struct brcms_hardware *wlc, bool bound,
+			     bool *fatal);
+static bool brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound);
+
+/* used by brcms_c_down() */
+static void brcms_c_flushqueues(struct brcms_c_info *wlc);
+
+static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs);
+static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw);
+static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw);
+static bool brcms_b_tx_fifo_suspended(struct brcms_hardware *wlc_hw,
+				       uint tx_fifo);
+static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw,
+				    uint tx_fifo);
+static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
+				   uint tx_fifo);
+
+/* Low Level Prototypes */
+static int brcms_b_bandtype(struct brcms_hardware *wlc_hw);
+static void brcms_b_info_init(struct brcms_hardware *wlc_hw);
+static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want);
+static u16 brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset,
+				   u32 sel);
+static void brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset,
+				  u16 v, u32 sel);
+static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk);
+static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme);
+static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw);
+static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw);
+static bool brcms_c_validboardtype(struct brcms_hardware *wlc);
+static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw);
+static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw);
+static char *brcms_c_get_macaddr(struct brcms_hardware *wlc_hw);
+static void brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init);
+static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw);
+static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool want,
+			 mbool flags);
+static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw);
+static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw);
+static u32 brcms_c_wlintrsoff(struct brcms_c_info *wlc);
+static void brcms_c_wlintrsrestore(struct brcms_c_info *wlc, u32 macintmask);
+static void brcms_c_gpio_init(struct brcms_c_info *wlc);
+static void brcms_c_write_hw_bcntemplate0(struct brcms_hardware *wlc_hw,
+					  void *bcn, int len);
+static void brcms_c_write_hw_bcntemplate1(struct brcms_hardware *wlc_hw,
+					  void *bcn, int len);
+static void brcms_b_bsinit(struct brcms_c_info *wlc, chanspec_t chanspec);
+static u32 brcms_c_setband_inact(struct brcms_c_info *wlc, uint bandunit);
+static void brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit,
+			     chanspec_t chanspec);
+static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw,
+					bool shortslot);
+static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw);
+static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
+					     u8 rate);
+
+/* === Low Level functions === */
+
+void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw, bool shortslot)
+{
+	wlc_hw->shortslot = shortslot;
+
+	if (BAND_2G(brcms_b_bandtype(wlc_hw)) && wlc_hw->up) {
+		brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+		brcms_b_update_slot_timing(wlc_hw, shortslot);
+		brcms_c_enable_mac(wlc_hw->wlc);
+	}
+}
+
+/*
+ * Update the slot timing for standard 11b/g (20us slots)
+ * or shortslot 11g (9us slots)
+ * The PSM needs to be suspended for this call.
+ */
+static void brcms_b_update_slot_timing(struct brcms_hardware *wlc_hw,
+					bool shortslot)
+{
+	d11regs_t *regs;
+
+	regs = wlc_hw->regs;
+
+	if (shortslot) {
+		/* 11g short slot: 11a timing */
+		W_REG(&regs->ifs_slot, 0x0207);	/* APHY_SLOT_TIME */
+		brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, APHY_SLOT_TIME);
+	} else {
+		/* 11g long slot: 11b timing */
+		W_REG(&regs->ifs_slot, 0x0212);	/* BPHY_SLOT_TIME */
+		brcms_b_write_shm(wlc_hw, M_DOT11_SLOT, BPHY_SLOT_TIME);
+	}
+}
+
+static void brcms_c_ucode_bsinit(struct brcms_hardware *wlc_hw)
+{
+	struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+
+	/* init microcode host flags */
+	brcms_c_write_mhf(wlc_hw, wlc_hw->band->mhfs);
+
+	/* do band-specific ucode IHR, SHM, and SCR inits */
+	if (D11REV_IS(wlc_hw->corerev, 23)) {
+		if (BRCMS_ISNPHY(wlc_hw->band)) {
+			brcms_c_write_inits(wlc_hw, d11n0bsinitvals16);
+		} else {
+			wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+				  " %d\n", __func__, wlc_hw->unit,
+				  wlc_hw->corerev);
+		}
+	} else {
+		if (D11REV_IS(wlc_hw->corerev, 24)) {
+			if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+				brcms_c_write_inits(wlc_hw,
+						    d11lcn0bsinitvals24);
+			} else
+				wiphy_err(wiphy, "%s: wl%d: unsupported phy in"
+					  " core rev %d\n", __func__,
+					  wlc_hw->unit, wlc_hw->corerev);
+		} else {
+			wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n",
+				__func__, wlc_hw->unit, wlc_hw->corerev);
+		}
+	}
+}
+
+/* switch to new band but leave it inactive */
+static u32 brcms_c_setband_inact(struct brcms_c_info *wlc,
+					    uint bandunit)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	u32 macintmask;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	WARN_ON((R_REG(&wlc_hw->regs->maccontrol) & MCTL_EN_MAC) != 0);
+
+	/* disable interrupts */
+	macintmask = brcms_intrsoff(wlc->wl);
+
+	/* radio off */
+	wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+	brcms_b_core_phy_clk(wlc_hw, OFF);
+
+	brcms_c_setxband(wlc_hw, bandunit);
+
+	return macintmask;
+}
+
+/* Process received frames */
+/*
+ * Return true if more frames need to be processed. false otherwise.
+ * Param 'bound' indicates max. # frames to process before break out.
+ */
+static bool
+brcms_b_recv(struct brcms_hardware *wlc_hw, uint fifo, bool bound)
+{
+	struct sk_buff *p;
+	struct sk_buff *head = NULL;
+	struct sk_buff *tail = NULL;
+	uint n = 0;
+	uint bound_limit = bound ? wlc_hw->wlc->pub->tunables->rxbnd : -1;
+	struct brcms_d11rxhdr *wlc_rxhdr = NULL;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+	/* gather received frames */
+	while ((p = dma_rx(wlc_hw->di[fifo]))) {
+
+		if (!tail)
+			head = tail = p;
+		else {
+			tail->prev = p;
+			tail = p;
+		}
+
+		/* !give others some time to run! */
+		if (++n >= bound_limit)
+			break;
+	}
+
+	/* post more rbufs */
+	dma_rxfill(wlc_hw->di[fifo]);
+
+	/* process each frame */
+	while ((p = head) != NULL) {
+		head = head->prev;
+		p->prev = NULL;
+
+		wlc_rxhdr = (struct brcms_d11rxhdr *) p->data;
+
+		/* compute the RSSI from d11rxhdr and record it in wlc_rxd11hr */
+		wlc_phy_rssi_compute(wlc_hw->band->pi, wlc_rxhdr);
+
+		brcms_c_recv(wlc_hw->wlc, p);
+	}
+
+	return n >= bound_limit;
+}
+
+/* second-level interrupt processing
+ *   Return true if another dpc needs to be re-scheduled. false otherwise.
+ *   Param 'bounded' indicates if applicable loops should be bounded.
+ */
+bool brcms_c_dpc(struct brcms_c_info *wlc, bool bounded)
+{
+	u32 macintstatus;
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs = wlc_hw->regs;
+	bool fatal = false;
+	struct wiphy *wiphy = wlc->wiphy;
+
+	if (DEVICEREMOVED(wlc)) {
+		wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+			  __func__);
+		brcms_down(wlc->wl);
+		return false;
+	}
+
+	/* grab and clear the saved software intstatus bits */
+	macintstatus = wlc->macintstatus;
+	wlc->macintstatus = 0;
+
+	BCMMSG(wlc->wiphy, "wl%d: macintstatus 0x%x\n",
+	       wlc_hw->unit, macintstatus);
+
+	WARN_ON(macintstatus & MI_PRQ); /* PRQ Interrupt in non-MBSS */
+
+	/* BCN template is available */
+	/* ZZZ: Use AP_ACTIVE ? */
+	if (AP_ENAB(wlc->pub) && (!APSTA_ENAB(wlc->pub))
+	    && (macintstatus & MI_BCNTPL)) {
+		brcms_c_update_beacon(wlc);
+	}
+
+	/* tx status */
+	if (macintstatus & MI_TFS) {
+		if (brcms_b_txstatus(wlc->hw, bounded, &fatal))
+			wlc->macintstatus |= MI_TFS;
+		if (fatal) {
+			wiphy_err(wiphy, "MI_TFS: fatal\n");
+			goto fatal;
+		}
+	}
+
+	if (macintstatus & (MI_TBTT | MI_DTIM_TBTT))
+		brcms_c_tbtt(wlc);
+
+	/* ATIM window end */
+	if (macintstatus & MI_ATIMWINEND) {
+		BCMMSG(wlc->wiphy, "end of ATIM window\n");
+		OR_REG(&regs->maccommand, wlc->qvalid);
+		wlc->qvalid = 0;
+	}
+
+	/* received data or control frame, MI_DMAINT is indication of RX_FIFO interrupt */
+	if (macintstatus & MI_DMAINT)
+		if (brcms_b_recv(wlc_hw, RX_FIFO, bounded))
+			wlc->macintstatus |= MI_DMAINT;
+
+	/* TX FIFO suspend/flush completion */
+	if (macintstatus & MI_TXSTOP)
+		brcms_b_tx_fifo_suspended(wlc_hw, TX_DATA_FIFO);
+
+	/* noise sample collected */
+	if (macintstatus & MI_BG_NOISE) {
+		wlc_phy_noise_sample_intr(wlc_hw->band->pi);
+	}
+
+	if (macintstatus & MI_GP0) {
+		wiphy_err(wiphy, "wl%d: PSM microcode watchdog fired at %d "
+			"(seconds). Resetting.\n", wlc_hw->unit, wlc_hw->now);
+
+		printk_once("%s : PSM Watchdog, chipid 0x%x, chiprev 0x%x\n",
+					__func__, wlc_hw->sih->chip,
+					wlc_hw->sih->chiprev);
+		/* big hammer */
+		brcms_init(wlc->wl);
+	}
+
+	/* gptimer timeout */
+	if (macintstatus & MI_TO) {
+		W_REG(&regs->gptimer, 0);
+	}
+
+	if (macintstatus & MI_RFDISABLE) {
+		BCMMSG(wlc->wiphy, "wl%d: BMAC Detected a change on the"
+		       " RF Disable Input\n", wlc_hw->unit);
+		brcms_rfkill_set_hw_state(wlc->wl);
+	}
+
+	/* send any enq'd tx packets. Just makes sure to jump start tx */
+	if (!pktq_empty(&wlc->pkt_queue->q))
+		brcms_c_send_q(wlc);
+
+	/* it isn't done and needs to be resched if macintstatus is non-zero */
+	return wlc->macintstatus != 0;
+
+ fatal:
+	brcms_init(wlc->wl);
+	return wlc->macintstatus != 0;
+}
+
+/* common low-level watchdog code */
+void brcms_b_watchdog(void *arg)
+{
+	struct brcms_c_info *wlc = (struct brcms_c_info *) arg;
+	struct brcms_hardware *wlc_hw = wlc->hw;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	if (!wlc_hw->up)
+		return;
+
+	/* increment second count */
+	wlc_hw->now++;
+
+	/* Check for FIFO error interrupts */
+	brcms_b_fifoerrors(wlc_hw);
+
+	/* make sure RX dma has buffers */
+	dma_rxfill(wlc->hw->di[RX_FIFO]);
+
+	wlc_phy_watchdog(wlc_hw->band->pi);
+}
+
+void
+brcms_b_set_chanspec(struct brcms_hardware *wlc_hw, chanspec_t chanspec,
+		      bool mute, struct txpwr_limits *txpwr)
+{
+	uint bandunit;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d: 0x%x\n", wlc_hw->unit, chanspec);
+
+	wlc_hw->chanspec = chanspec;
+
+	/* Switch bands if necessary */
+	if (NBANDS_HW(wlc_hw) > 1) {
+		bandunit = CHSPEC_BANDUNIT(chanspec);
+		if (wlc_hw->band->bandunit != bandunit) {
+			/* brcms_b_setband disables other bandunit,
+			 *  use light band switch if not up yet
+			 */
+			if (wlc_hw->up) {
+				wlc_phy_chanspec_radio_set(wlc_hw->
+							   bandstate[bandunit]->
+							   pi, chanspec);
+				brcms_b_setband(wlc_hw, bandunit, chanspec);
+			} else {
+				brcms_c_setxband(wlc_hw, bandunit);
+			}
+		}
+	}
+
+	wlc_phy_initcal_enable(wlc_hw->band->pi, !mute);
+
+	if (!wlc_hw->up) {
+		if (wlc_hw->clk)
+			wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr,
+						  chanspec);
+		wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+	} else {
+		wlc_phy_chanspec_set(wlc_hw->band->pi, chanspec);
+		wlc_phy_txpower_limit_set(wlc_hw->band->pi, txpwr, chanspec);
+
+		/* Update muting of the channel */
+		brcms_b_mute(wlc_hw, mute, 0);
+	}
+}
+
+int brcms_b_state_get(struct brcms_hardware *wlc_hw,
+		      struct brcms_b_state *state)
+{
+	state->machwcap = wlc_hw->machwcap;
+
+	return 0;
+}
+
+static bool brcms_b_attach_dmapio(struct brcms_c_info *wlc, uint j, bool wme)
+{
+	uint i;
+	char name[8];
+	/* ucode host flag 2 needed for pio mode, independent of band and fifo */
+	u16 pio_mhf2 = 0;
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	uint unit = wlc_hw->unit;
+	struct brcms_tunables *tune = wlc->pub->tunables;
+	struct wiphy *wiphy = wlc->wiphy;
+
+	/* name and offsets for dma_attach */
+	snprintf(name, sizeof(name), "wl%d", unit);
+
+	if (wlc_hw->di[0] == 0) {	/* Init FIFOs */
+		uint addrwidth;
+		int dma_attach_err = 0;
+		/* Find out the DMA addressing capability and let OS know
+		 * All the channels within one DMA core have 'common-minimum' same
+		 * capability
+		 */
+		addrwidth =
+		    dma_addrwidth(wlc_hw->sih, DMAREG(wlc_hw, DMA_TX, 0));
+
+		if (!wl_alloc_dma_resources(wlc_hw->wlc->wl, addrwidth)) {
+			wiphy_err(wiphy, "wl%d: wlc_attach: alloc_dma_"
+				  "resources failed\n", unit);
+			return false;
+		}
+
+		/*
+		 * FIFO 0
+		 * TX: TX_AC_BK_FIFO (TX AC Background data packets)
+		 * RX: RX_FIFO (RX data packets)
+		 */
+		wlc_hw->di[0] = dma_attach(name, wlc_hw->sih,
+					   (wme ? DMAREG(wlc_hw, DMA_TX, 0) :
+					    NULL), DMAREG(wlc_hw, DMA_RX, 0),
+					   (wme ? tune->ntxd : 0), tune->nrxd,
+					   tune->rxbufsz, -1, tune->nrxbufpost,
+					   BRCMS_HWRXOFF, &brcm_msg_level);
+		dma_attach_err |= (NULL == wlc_hw->di[0]);
+
+		/*
+		 * FIFO 1
+		 * TX: TX_AC_BE_FIFO (TX AC Best-Effort data packets)
+		 *   (legacy) TX_DATA_FIFO (TX data packets)
+		 * RX: UNUSED
+		 */
+		wlc_hw->di[1] = dma_attach(name, wlc_hw->sih,
+					   DMAREG(wlc_hw, DMA_TX, 1), NULL,
+					   tune->ntxd, 0, 0, -1, 0, 0,
+					   &brcm_msg_level);
+		dma_attach_err |= (NULL == wlc_hw->di[1]);
+
+		/*
+		 * FIFO 2
+		 * TX: TX_AC_VI_FIFO (TX AC Video data packets)
+		 * RX: UNUSED
+		 */
+		wlc_hw->di[2] = dma_attach(name, wlc_hw->sih,
+					   DMAREG(wlc_hw, DMA_TX, 2), NULL,
+					   tune->ntxd, 0, 0, -1, 0, 0,
+					   &brcm_msg_level);
+		dma_attach_err |= (NULL == wlc_hw->di[2]);
+		/*
+		 * FIFO 3
+		 * TX: TX_AC_VO_FIFO (TX AC Voice data packets)
+		 *   (legacy) TX_CTL_FIFO (TX control & mgmt packets)
+		 */
+		wlc_hw->di[3] = dma_attach(name, wlc_hw->sih,
+					   DMAREG(wlc_hw, DMA_TX, 3),
+					   NULL, tune->ntxd, 0, 0, -1,
+					   0, 0, &brcm_msg_level);
+		dma_attach_err |= (NULL == wlc_hw->di[3]);
+/* Cleaner to leave this as if with AP defined */
+
+		if (dma_attach_err) {
+			wiphy_err(wiphy, "wl%d: wlc_attach: dma_attach failed"
+				  "\n", unit);
+			return false;
+		}
+
+		/* get pointer to dma engine tx flow control variable */
+		for (i = 0; i < NFIFO; i++)
+			if (wlc_hw->di[i])
+				wlc_hw->txavail[i] =
+				    (uint *) dma_getvar(wlc_hw->di[i],
+							"&txavail");
+	}
+
+	/* initial ucode host flags */
+	brcms_c_mhfdef(wlc, wlc_hw->band->mhfs, pio_mhf2);
+
+	return true;
+}
+
+static void brcms_b_detach_dmapio(struct brcms_hardware *wlc_hw)
+{
+	uint j;
+
+	for (j = 0; j < NFIFO; j++) {
+		if (wlc_hw->di[j]) {
+			dma_detach(wlc_hw->di[j]);
+			wlc_hw->di[j] = NULL;
+		}
+	}
+}
+
+/* low level attach
+ *    run backplane attach, init nvram
+ *    run phy attach
+ *    initialize software state for each core and band
+ *    put the whole chip in reset(driver down state), no clock
+ */
+int brcms_b_attach(struct brcms_c_info *wlc, u16 vendor, u16 device, uint unit,
+		    bool piomode, void *regsva, uint bustype, void *btparam)
+{
+	struct brcms_hardware *wlc_hw;
+	d11regs_t *regs;
+	char *macaddr = NULL;
+	char *vars;
+	uint err = 0;
+	uint j;
+	bool wme = false;
+	struct shared_phy_params sha_params;
+	struct wiphy *wiphy = wlc->wiphy;
+
+	BCMMSG(wlc->wiphy, "wl%d: vendor 0x%x device 0x%x\n", unit, vendor,
+		device);
+
+	wme = true;
+
+	wlc_hw = wlc->hw;
+	wlc_hw->wlc = wlc;
+	wlc_hw->unit = unit;
+	wlc_hw->band = wlc_hw->bandstate[0];
+	wlc_hw->_piomode = piomode;
+
+	/* populate struct brcms_hardware with default values  */
+	brcms_b_info_init(wlc_hw);
+
+	/*
+	 * Do the hardware portion of the attach.
+	 * Also initialize software state that depends on the particular hardware
+	 * we are running.
+	 */
+	wlc_hw->sih = ai_attach(regsva, bustype, btparam,
+				&wlc_hw->vars, &wlc_hw->vars_size);
+	if (wlc_hw->sih == NULL) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: si_attach failed\n",
+			  unit);
+		err = 11;
+		goto fail;
+	}
+	vars = wlc_hw->vars;
+
+	/*
+	 * Get vendid/devid nvram overwrites, which could be different
+	 * than those the BIOS recognizes for devices on PCMCIA_BUS,
+	 * SDIO_BUS, and SROMless devices on PCI_BUS.
+	 */
+#ifdef BCMBUSTYPE
+	bustype = BCMBUSTYPE;
+#endif
+	if (bustype != SI_BUS) {
+		char *var;
+
+		var = getvar(vars, "vendid");
+		if (var) {
+			vendor = (u16) simple_strtoul(var, NULL, 0);
+			wiphy_err(wiphy, "Overriding vendor id = 0x%x\n",
+				  vendor);
+		}
+		var = getvar(vars, "devid");
+		if (var) {
+			u16 devid = (u16) simple_strtoul(var, NULL, 0);
+			if (devid != 0xffff) {
+				device = devid;
+				wiphy_err(wiphy, "Overriding device id = 0x%x"
+					  "\n", device);
+			}
+		}
+
+		/* verify again the device is supported */
+		if (!brcms_c_chipmatch(vendor, device)) {
+			wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported "
+				"vendor/device (0x%x/0x%x)\n",
+				 unit, vendor, device);
+			err = 12;
+			goto fail;
+		}
+	}
+
+	wlc_hw->vendorid = vendor;
+	wlc_hw->deviceid = device;
+
+	/* set bar0 window to point at D11 core */
+	wlc_hw->regs = (d11regs_t *) ai_setcore(wlc_hw->sih, D11_CORE_ID, 0);
+	wlc_hw->corerev = ai_corerev(wlc_hw->sih);
+
+	regs = wlc_hw->regs;
+
+	wlc->regs = wlc_hw->regs;
+
+	/* validate chip, chiprev and corerev */
+	if (!brcms_c_isgoodchip(wlc_hw)) {
+		err = 13;
+		goto fail;
+	}
+
+	/* initialize power control registers */
+	ai_clkctl_init(wlc_hw->sih);
+
+	/* request fastclock and force fastclock for the rest of attach
+	 * bring the d11 core out of reset.
+	 *   For PMU chips, the first wlc_clkctl_clk is no-op since core-clk is still false;
+	 *   But it will be called again inside wlc_corereset, after d11 is out of reset.
+	 */
+	brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+	brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+	if (!brcms_b_validate_chip_access(wlc_hw)) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: validate_chip_access "
+			"failed\n", unit);
+		err = 14;
+		goto fail;
+	}
+
+	/* get the board rev, used just below */
+	j = getintvar(vars, "boardrev");
+	/* promote srom boardrev of 0xFF to 1 */
+	if (j == BOARDREV_PROMOTABLE)
+		j = BOARDREV_PROMOTED;
+	wlc_hw->boardrev = (u16) j;
+	if (!brcms_c_validboardtype(wlc_hw)) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: Unsupported Broadcom "
+			"board type (0x%x)" " or revision level (0x%x)\n",
+			 unit, wlc_hw->sih->boardtype, wlc_hw->boardrev);
+		err = 15;
+		goto fail;
+	}
+	wlc_hw->sromrev = (u8) getintvar(vars, "sromrev");
+	wlc_hw->boardflags = (u32) getintvar(vars, "boardflags");
+	wlc_hw->boardflags2 = (u32) getintvar(vars, "boardflags2");
+
+	if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+		brcms_b_pllreq(wlc_hw, true, BRCMS_PLLREQ_SHARED);
+
+	if ((wlc_hw->sih->bustype == PCI_BUS)
+	    && (ai_pci_war16165(wlc_hw->sih)))
+		wlc->war16165 = true;
+
+	/* check device id(srom, nvram etc.) to set bands */
+	if (wlc_hw->deviceid == BCM43224_D11N_ID ||
+	    wlc_hw->deviceid == BCM43224_D11N_ID_VEN1) {
+		/* Dualband boards */
+		wlc_hw->_nbands = 2;
+	} else
+		wlc_hw->_nbands = 1;
+
+	if ((wlc_hw->sih->chip == BCM43225_CHIP_ID))
+		wlc_hw->_nbands = 1;
+
+	/* BMAC_NOTE: remove init of pub values when brcms_c_attach()
+	 * unconditionally does the init of these values
+	 */
+	wlc->vendorid = wlc_hw->vendorid;
+	wlc->deviceid = wlc_hw->deviceid;
+	wlc->pub->sih = wlc_hw->sih;
+	wlc->pub->corerev = wlc_hw->corerev;
+	wlc->pub->sromrev = wlc_hw->sromrev;
+	wlc->pub->boardrev = wlc_hw->boardrev;
+	wlc->pub->boardflags = wlc_hw->boardflags;
+	wlc->pub->boardflags2 = wlc_hw->boardflags2;
+	wlc->pub->_nbands = wlc_hw->_nbands;
+
+	wlc_hw->physhim = wlc_phy_shim_attach(wlc_hw, wlc->wl, wlc);
+
+	if (wlc_hw->physhim == NULL) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_shim_attach "
+			"failed\n", unit);
+		err = 25;
+		goto fail;
+	}
+
+	/* pass all the parameters to wlc_phy_shared_attach in one struct */
+	sha_params.sih = wlc_hw->sih;
+	sha_params.physhim = wlc_hw->physhim;
+	sha_params.unit = unit;
+	sha_params.corerev = wlc_hw->corerev;
+	sha_params.vars = vars;
+	sha_params.vid = wlc_hw->vendorid;
+	sha_params.did = wlc_hw->deviceid;
+	sha_params.chip = wlc_hw->sih->chip;
+	sha_params.chiprev = wlc_hw->sih->chiprev;
+	sha_params.chippkg = wlc_hw->sih->chippkg;
+	sha_params.sromrev = wlc_hw->sromrev;
+	sha_params.boardtype = wlc_hw->sih->boardtype;
+	sha_params.boardrev = wlc_hw->boardrev;
+	sha_params.boardvendor = wlc_hw->sih->boardvendor;
+	sha_params.boardflags = wlc_hw->boardflags;
+	sha_params.boardflags2 = wlc_hw->boardflags2;
+	sha_params.bustype = wlc_hw->sih->bustype;
+	sha_params.buscorerev = wlc_hw->sih->buscorerev;
+
+	/* alloc and save pointer to shared phy state area */
+	wlc_hw->phy_sh = wlc_phy_shared_attach(&sha_params);
+	if (!wlc_hw->phy_sh) {
+		err = 16;
+		goto fail;
+	}
+
+	/* initialize software state for each core and band */
+	for (j = 0; j < NBANDS_HW(wlc_hw); j++) {
+		/*
+		 * band0 is always 2.4Ghz
+		 * band1, if present, is 5Ghz
+		 */
+
+		/* So if this is a single band 11a card, use band 1 */
+		if (IS_SINGLEBAND_5G(wlc_hw->deviceid))
+			j = BAND_5G_INDEX;
+
+		brcms_c_setxband(wlc_hw, j);
+
+		wlc_hw->band->bandunit = j;
+		wlc_hw->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G;
+		wlc->band->bandunit = j;
+		wlc->band->bandtype = j ? BRCM_BAND_5G : BRCM_BAND_2G;
+		wlc->core->coreidx = ai_coreidx(wlc_hw->sih);
+
+		wlc_hw->machwcap = R_REG(&regs->machwcap);
+		wlc_hw->machwcap_backup = wlc_hw->machwcap;
+
+		/* init tx fifo size */
+		wlc_hw->xmtfifo_sz =
+		    xmtfifo_sz[(wlc_hw->corerev - XMTFIFOTBL_STARTREV)];
+
+		/* Get a phy for this band */
+		wlc_hw->band->pi = wlc_phy_attach(wlc_hw->phy_sh,
+			(void *)regs, brcms_b_bandtype(wlc_hw), vars,
+			wlc->wiphy);
+		if (wlc_hw->band->pi == NULL) {
+			wiphy_err(wiphy, "wl%d: brcms_b_attach: wlc_phy_"
+				  "attach failed\n", unit);
+			err = 17;
+			goto fail;
+		}
+
+		wlc_phy_machwcap_set(wlc_hw->band->pi, wlc_hw->machwcap);
+
+		wlc_phy_get_phyversion(wlc_hw->band->pi, &wlc_hw->band->phytype,
+				       &wlc_hw->band->phyrev,
+				       &wlc_hw->band->radioid,
+				       &wlc_hw->band->radiorev);
+		wlc_hw->band->abgphy_encore =
+		    wlc_phy_get_encore(wlc_hw->band->pi);
+		wlc->band->abgphy_encore = wlc_phy_get_encore(wlc_hw->band->pi);
+		wlc_hw->band->core_flags =
+		    wlc_phy_get_coreflags(wlc_hw->band->pi);
+
+		/* verify good phy_type & supported phy revision */
+		if (BRCMS_ISNPHY(wlc_hw->band)) {
+			if (NCONF_HAS(wlc_hw->band->phyrev))
+				goto good_phy;
+			else
+				goto bad_phy;
+		} else if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+			if (LCNCONF_HAS(wlc_hw->band->phyrev))
+				goto good_phy;
+			else
+				goto bad_phy;
+		} else {
+ bad_phy:
+			wiphy_err(wiphy, "wl%d: brcms_b_attach: unsupported "
+				  "phy type/rev (%d/%d)\n", unit,
+				  wlc_hw->band->phytype, wlc_hw->band->phyrev);
+			err = 18;
+			goto fail;
+		}
+
+ good_phy:
+		/* BMAC_NOTE: wlc->band->pi should not be set below and should be done in the
+		 * high level attach. However we can not make that change until all low level access
+		 * is changed to wlc_hw->band->pi. Instead do the wlc->band->pi init below, keeping
+		 * wlc_hw->band->pi as well for incremental update of low level fns, and cut over
+		 * low only init when all fns updated.
+		 */
+		wlc->band->pi = wlc_hw->band->pi;
+		wlc->band->phytype = wlc_hw->band->phytype;
+		wlc->band->phyrev = wlc_hw->band->phyrev;
+		wlc->band->radioid = wlc_hw->band->radioid;
+		wlc->band->radiorev = wlc_hw->band->radiorev;
+
+		/* default contention windows size limits */
+		wlc_hw->band->CWmin = APHY_CWMIN;
+		wlc_hw->band->CWmax = PHY_CWMAX;
+
+		if (!brcms_b_attach_dmapio(wlc, j, wme)) {
+			err = 19;
+			goto fail;
+		}
+	}
+
+	/* disable core to match driver "down" state */
+	brcms_c_coredisable(wlc_hw);
+
+	/* Match driver "down" state */
+	if (wlc_hw->sih->bustype == PCI_BUS)
+		ai_pci_down(wlc_hw->sih);
+
+	/* register sb interrupt callback functions */
+	ai_register_intr_callback(wlc_hw->sih, (void *)brcms_c_wlintrsoff,
+				  (void *)brcms_c_wlintrsrestore, NULL, wlc);
+
+	/* turn off pll and xtal to match driver "down" state */
+	brcms_b_xtal(wlc_hw, OFF);
+
+	/* *********************************************************************
+	 * The hardware is in the DOWN state at this point. D11 core
+	 * or cores are in reset with clocks off, and the board PLLs
+	 * are off if possible.
+	 *
+	 * Beyond this point, wlc->sbclk == false and chip registers
+	 * should not be touched.
+	 *********************************************************************
+	 */
+
+	/* init etheraddr state variables */
+	macaddr = brcms_c_get_macaddr(wlc_hw);
+	if (macaddr == NULL) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: macaddr not found\n",
+			  unit);
+		err = 21;
+		goto fail;
+	}
+	brcmu_ether_atoe(macaddr, wlc_hw->etheraddr);
+	if (is_broadcast_ether_addr(wlc_hw->etheraddr) ||
+	    is_zero_ether_addr(wlc_hw->etheraddr)) {
+		wiphy_err(wiphy, "wl%d: brcms_b_attach: bad macaddr %s\n",
+			  unit, macaddr);
+		err = 22;
+		goto fail;
+	}
+
+	BCMMSG(wlc->wiphy,
+		 "deviceid 0x%x nbands %d board 0x%x macaddr: %s\n",
+		 wlc_hw->deviceid, wlc_hw->_nbands,
+		 wlc_hw->sih->boardtype, macaddr);
+
+	return err;
+
+ fail:
+	wiphy_err(wiphy, "wl%d: brcms_b_attach: failed with err %d\n", unit,
+		  err);
+	return err;
+}
+
+/*
+ * Initialize brcms_c_info default values ...
+ * may get overrides later in this function
+ *  BMAC_NOTES, move low out and resolve the dangling ones
+ */
+static void brcms_b_info_init(struct brcms_hardware *wlc_hw)
+{
+	struct brcms_c_info *wlc = wlc_hw->wlc;
+
+	/* set default sw macintmask value */
+	wlc->defmacintmask = DEF_MACINTMASK;
+
+	/* various 802.11g modes */
+	wlc_hw->shortslot = false;
+
+	wlc_hw->SFBL = RETRY_SHORT_FB;
+	wlc_hw->LFBL = RETRY_LONG_FB;
+
+	/* default mac retry limits */
+	wlc_hw->SRL = RETRY_SHORT_DEF;
+	wlc_hw->LRL = RETRY_LONG_DEF;
+	wlc_hw->chanspec = CH20MHZ_CHSPEC(1);
+}
+
+/*
+ * low level detach
+ */
+int brcms_b_detach(struct brcms_c_info *wlc)
+{
+	uint i;
+	struct brcms_hw_band *band;
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	int callbacks;
+
+	callbacks = 0;
+
+	if (wlc_hw->sih) {
+		/* detach interrupt sync mechanism since interrupt is disabled and per-port
+		 * interrupt object may has been freed. this must be done before sb core switch
+		 */
+		ai_deregister_intr_callback(wlc_hw->sih);
+
+		if (wlc_hw->sih->bustype == PCI_BUS)
+			ai_pci_sleep(wlc_hw->sih);
+	}
+
+	brcms_b_detach_dmapio(wlc_hw);
+
+	band = wlc_hw->band;
+	for (i = 0; i < NBANDS_HW(wlc_hw); i++) {
+		if (band->pi) {
+			/* Detach this band's phy */
+			wlc_phy_detach(band->pi);
+			band->pi = NULL;
+		}
+		band = wlc_hw->bandstate[OTHERBANDUNIT(wlc)];
+	}
+
+	/* Free shared phy state */
+	kfree(wlc_hw->phy_sh);
+
+	wlc_phy_shim_detach(wlc_hw->physhim);
+
+	/* free vars */
+	kfree(wlc_hw->vars);
+	wlc_hw->vars = NULL;
+
+	if (wlc_hw->sih) {
+		ai_detach(wlc_hw->sih);
+		wlc_hw->sih = NULL;
+	}
+
+	return callbacks;
+
+}
+
+void brcms_b_reset(struct brcms_hardware *wlc_hw)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	/* reset the core */
+	if (!DEVICEREMOVED(wlc_hw->wlc))
+		brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+	/* purge the dma rings */
+	brcms_c_flushqueues(wlc_hw->wlc);
+
+	brcms_c_reset_bmac_done(wlc_hw->wlc);
+}
+
+void
+brcms_b_init(struct brcms_hardware *wlc_hw, chanspec_t chanspec,
+			  bool mute) {
+	u32 macintmask;
+	bool fastclk;
+	struct brcms_c_info *wlc = wlc_hw->wlc;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	/* request FAST clock if not on */
+	fastclk = wlc_hw->forcefastclk;
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	/* disable interrupts */
+	macintmask = brcms_intrsoff(wlc->wl);
+
+	/* set up the specified band and chanspec */
+	brcms_c_setxband(wlc_hw, CHSPEC_BANDUNIT(chanspec));
+	wlc_phy_chanspec_radio_set(wlc_hw->band->pi, chanspec);
+
+	/* do one-time phy inits and calibration */
+	wlc_phy_cal_init(wlc_hw->band->pi);
+
+	/* core-specific initialization */
+	brcms_b_coreinit(wlc);
+
+	/* suspend the tx fifos and mute the phy for preism cac time */
+	if (mute)
+		brcms_b_mute(wlc_hw, ON, PHY_MUTE_FOR_PREISM);
+
+	/* band-specific inits */
+	brcms_b_bsinit(wlc, chanspec);
+
+	/* restore macintmask */
+	brcms_intrsrestore(wlc->wl, macintmask);
+
+	/* seed wake_override with BRCMS_WAKE_OVERRIDE_MACSUSPEND since the mac
+	 * is suspended and brcms_c_enable_mac() will clear this override bit.
+	 */
+	mboolset(wlc_hw->wake_override, BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+
+	/*
+	 * initialize mac_suspend_depth to 1 to match ucode initial suspended state
+	 */
+	wlc_hw->mac_suspend_depth = 1;
+
+	/* restore the clk */
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+int brcms_b_up_prep(struct brcms_hardware *wlc_hw)
+{
+	uint coremask;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	/*
+	 * Enable pll and xtal, initialize the power control registers,
+	 * and force fastclock for the remainder of brcms_c_up().
+	 */
+	brcms_b_xtal(wlc_hw, ON);
+	ai_clkctl_init(wlc_hw->sih);
+	brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	/*
+	 * Configure pci/pcmcia here instead of in brcms_c_attach()
+	 * to allow mfg hotswap:  down, hotswap (chip power cycle), up.
+	 */
+	coremask = (1 << wlc_hw->wlc->core->coreidx);
+
+	if (wlc_hw->sih->bustype == PCI_BUS)
+		ai_pci_setup(wlc_hw->sih, coremask);
+
+	/*
+	 * Need to read the hwradio status here to cover the case where the system
+	 * is loaded with the hw radio disabled. We do not want to bring the driver up in this case.
+	 */
+	if (brcms_b_radio_read_hwdisabled(wlc_hw)) {
+		/* put SB PCI in down state again */
+		if (wlc_hw->sih->bustype == PCI_BUS)
+			ai_pci_down(wlc_hw->sih);
+		brcms_b_xtal(wlc_hw, OFF);
+		return -ENOMEDIUM;
+	}
+
+	if (wlc_hw->sih->bustype == PCI_BUS)
+		ai_pci_up(wlc_hw->sih);
+
+	/* reset the d11 core */
+	brcms_b_corereset(wlc_hw, BRCMS_USE_COREFLAGS);
+
+	return 0;
+}
+
+int brcms_b_up_finish(struct brcms_hardware *wlc_hw)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	wlc_hw->up = true;
+	wlc_phy_hw_state_upd(wlc_hw->band->pi, true);
+
+	/* FULLY enable dynamic power control and d11 core interrupt */
+	brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+	brcms_intrson(wlc_hw->wlc->wl);
+	return 0;
+}
+
+int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw)
+{
+	bool dev_gone;
+	uint callbacks = 0;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	if (!wlc_hw->up)
+		return callbacks;
+
+	dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+	/* disable interrupts */
+	if (dev_gone)
+		wlc_hw->wlc->macintmask = 0;
+	else {
+		/* now disable interrupts */
+		brcms_intrsoff(wlc_hw->wlc->wl);
+
+		/* ensure we're running on the pll clock again */
+		brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+	}
+	/* down phy at the last of this stage */
+	callbacks += wlc_phy_down(wlc_hw->band->pi);
+
+	return callbacks;
+}
+
+int brcms_b_down_finish(struct brcms_hardware *wlc_hw)
+{
+	uint callbacks = 0;
+	bool dev_gone;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	if (!wlc_hw->up)
+		return callbacks;
+
+	wlc_hw->up = false;
+	wlc_phy_hw_state_upd(wlc_hw->band->pi, false);
+
+	dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+	if (dev_gone) {
+		wlc_hw->sbclk = false;
+		wlc_hw->clk = false;
+		wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+
+		/* reclaim any posted packets */
+		brcms_c_flushqueues(wlc_hw->wlc);
+	} else {
+
+		/* Reset and disable the core */
+		if (ai_iscoreup(wlc_hw->sih)) {
+			if (R_REG(&wlc_hw->regs->maccontrol) &
+			    MCTL_EN_MAC)
+				brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+			callbacks += brcms_reset(wlc_hw->wlc->wl);
+			brcms_c_coredisable(wlc_hw);
+		}
+
+		/* turn off primary xtal and pll */
+		if (!wlc_hw->noreset) {
+			if (wlc_hw->sih->bustype == PCI_BUS)
+				ai_pci_down(wlc_hw->sih);
+			brcms_b_xtal(wlc_hw, OFF);
+		}
+	}
+
+	return callbacks;
+}
+
+void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw)
+{
+	/* delay before first read of ucode state */
+	udelay(40);
+
+	/* wait until ucode is no longer asleep */
+	SPINWAIT((brcms_b_read_shm(wlc_hw, M_UCODE_DBGST) ==
+		  DBGST_ASLEEP), wlc_hw->wlc->fastpwrup_dly);
+}
+
+void brcms_b_hw_etheraddr(struct brcms_hardware *wlc_hw, u8 *ea)
+{
+	memcpy(ea, wlc_hw->etheraddr, ETH_ALEN);
+}
+
+static int brcms_b_bandtype(struct brcms_hardware *wlc_hw)
+{
+	return wlc_hw->band->bandtype;
+}
+
+/* control chip clock to save power, enable dynamic clock or force fast clock */
+static void brcms_b_clkctl_clk(struct brcms_hardware *wlc_hw, uint mode)
+{
+	if (PMUCTL_ENAB(wlc_hw->sih)) {
+		/* new chips with PMU, CCS_FORCEHT will distribute the HT clock on backplane,
+		 *  but mac core will still run on ALP(not HT) when it enters powersave mode,
+		 *      which means the FCA bit may not be set.
+		 *      should wakeup mac if driver wants it to run on HT.
+		 */
+
+		if (wlc_hw->clk) {
+			if (mode == CLK_FAST) {
+				OR_REG(&wlc_hw->regs->clk_ctl_st,
+				       CCS_FORCEHT);
+
+				udelay(64);
+
+				SPINWAIT(((R_REG
+					   (&wlc_hw->regs->
+					    clk_ctl_st) & CCS_HTAVAIL) == 0),
+					 PMU_MAX_TRANSITION_DLY);
+				WARN_ON(!(R_REG
+					  (&wlc_hw->regs->
+					   clk_ctl_st) & CCS_HTAVAIL));
+			} else {
+				if ((wlc_hw->sih->pmurev == 0) &&
+				    (R_REG
+				     (&wlc_hw->regs->
+				      clk_ctl_st) & (CCS_FORCEHT | CCS_HTAREQ)))
+					SPINWAIT(((R_REG
+						   (&wlc_hw->regs->
+						    clk_ctl_st) & CCS_HTAVAIL)
+						  == 0),
+						 PMU_MAX_TRANSITION_DLY);
+				AND_REG(&wlc_hw->regs->clk_ctl_st,
+					~CCS_FORCEHT);
+			}
+		}
+		wlc_hw->forcefastclk = (mode == CLK_FAST);
+	} else {
+
+		/* old chips w/o PMU, force HT through cc,
+		 * then use FCA to verify mac is running fast clock
+		 */
+
+		wlc_hw->forcefastclk = ai_clkctl_cc(wlc_hw->sih, mode);
+
+		/* check fast clock is available (if core is not in reset) */
+		if (wlc_hw->forcefastclk && wlc_hw->clk)
+			WARN_ON(!(ai_core_sflags(wlc_hw->sih, 0, 0) &
+				  SISF_FCLKA));
+
+		/* keep the ucode wake bit on if forcefastclk is on
+		 * since we do not want ucode to put us back to slow clock
+		 * when it dozes for PM mode.
+		 * Code below matches the wake override bit with current forcefastclk state
+		 * Only setting bit in wake_override instead of waking ucode immediately
+		 * since old code (wlc.c 1.4499) had this behavior. Older code set
+		 * wlc->forcefastclk but only had the wake happen if the wakup_ucode work
+		 * (protected by an up check) was executed just below.
+		 */
+		if (wlc_hw->forcefastclk)
+			mboolset(wlc_hw->wake_override,
+				 BRCMS_WAKE_OVERRIDE_FORCEFAST);
+		else
+			mboolclr(wlc_hw->wake_override,
+				 BRCMS_WAKE_OVERRIDE_FORCEFAST);
+	}
+}
+
+/* set initial host flags value */
+static void
+brcms_c_mhfdef(struct brcms_c_info *wlc, u16 *mhfs, u16 mhf2_init)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+
+	memset(mhfs, 0, MHFMAX * sizeof(u16));
+
+	mhfs[MHF2] |= mhf2_init;
+
+	/* prohibit use of slowclock on multifunction boards */
+	if (wlc_hw->boardflags & BFL_NOPLLDOWN)
+		mhfs[MHF1] |= MHF1_FORCEFASTCLK;
+
+	if (BRCMS_ISNPHY(wlc_hw->band) && NREV_LT(wlc_hw->band->phyrev, 2)) {
+		mhfs[MHF2] |= MHF2_NPHY40MHZ_WAR;
+		mhfs[MHF1] |= MHF1_IQSWAP_WAR;
+	}
+}
+
+/* set or clear ucode host flag bits
+ * it has an optimization for no-change write
+ * it only writes through shared memory when the core has clock;
+ * pre-CLK changes should use wlc_write_mhf to get around the optimization
+ *
+ *
+ * bands values are: BRCM_BAND_AUTO <--- Current band only
+ *                   BRCM_BAND_5G   <--- 5G band only
+ *                   BRCM_BAND_2G   <--- 2G band only
+ *                   BRCM_BAND_ALL  <--- All bands
+ */
+void
+brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask, u16 val,
+	     int bands)
+{
+	u16 save;
+	u16 addr[MHFMAX] = {
+		M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+		M_HOST_FLAGS5
+	};
+	struct brcms_hw_band *band;
+
+	if ((val & ~mask) || idx >= MHFMAX)
+		return; /* error condition */
+
+	switch (bands) {
+		/* Current band only or all bands,
+		 * then set the band to current band
+		 */
+	case BRCM_BAND_AUTO:
+	case BRCM_BAND_ALL:
+		band = wlc_hw->band;
+		break;
+	case BRCM_BAND_5G:
+		band = wlc_hw->bandstate[BAND_5G_INDEX];
+		break;
+	case BRCM_BAND_2G:
+		band = wlc_hw->bandstate[BAND_2G_INDEX];
+		break;
+	default:
+		band = NULL;	/* error condition */
+	}
+
+	if (band) {
+		save = band->mhfs[idx];
+		band->mhfs[idx] = (band->mhfs[idx] & ~mask) | val;
+
+		/* optimization: only write through if changed, and
+		 * changed band is the current band
+		 */
+		if (wlc_hw->clk && (band->mhfs[idx] != save)
+		    && (band == wlc_hw->band))
+			brcms_b_write_shm(wlc_hw, addr[idx],
+					   (u16) band->mhfs[idx]);
+	}
+
+	if (bands == BRCM_BAND_ALL) {
+		wlc_hw->bandstate[0]->mhfs[idx] =
+		    (wlc_hw->bandstate[0]->mhfs[idx] & ~mask) | val;
+		wlc_hw->bandstate[1]->mhfs[idx] =
+		    (wlc_hw->bandstate[1]->mhfs[idx] & ~mask) | val;
+	}
+}
+
+u16 brcms_b_mhf_get(struct brcms_hardware *wlc_hw, u8 idx, int bands)
+{
+	struct brcms_hw_band *band;
+
+	if (idx >= MHFMAX)
+		return 0; /* error condition */
+	switch (bands) {
+	case BRCM_BAND_AUTO:
+		band = wlc_hw->band;
+		break;
+	case BRCM_BAND_5G:
+		band = wlc_hw->bandstate[BAND_5G_INDEX];
+		break;
+	case BRCM_BAND_2G:
+		band = wlc_hw->bandstate[BAND_2G_INDEX];
+		break;
+	default:
+		band = NULL;		/* error condition */
+	}
+
+	if (!band)
+		return 0;
+
+	return band->mhfs[idx];
+}
+
+static void brcms_c_write_mhf(struct brcms_hardware *wlc_hw, u16 *mhfs)
+{
+	u8 idx;
+	u16 addr[] = {
+		M_HOST_FLAGS1, M_HOST_FLAGS2, M_HOST_FLAGS3, M_HOST_FLAGS4,
+		M_HOST_FLAGS5
+	};
+
+	for (idx = 0; idx < MHFMAX; idx++) {
+		brcms_b_write_shm(wlc_hw, addr[idx], mhfs[idx]);
+	}
+}
+
+/* set the maccontrol register to desired reset state and
+ * initialize the sw cache of the register
+ */
+static void brcms_c_mctrl_reset(struct brcms_hardware *wlc_hw)
+{
+	/* IHR accesses are always enabled, PSM disabled, HPS off and WAKE on */
+	wlc_hw->maccontrol = 0;
+	wlc_hw->suspended_fifos = 0;
+	wlc_hw->wake_override = 0;
+	wlc_hw->mute_override = 0;
+	brcms_b_mctrl(wlc_hw, ~0, MCTL_IHR_EN | MCTL_WAKE);
+}
+
+/* set or clear maccontrol bits */
+void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val)
+{
+	u32 maccontrol;
+	u32 new_maccontrol;
+
+	if (val & ~mask)
+		return; /* error condition */
+	maccontrol = wlc_hw->maccontrol;
+	new_maccontrol = (maccontrol & ~mask) | val;
+
+	/* if the new maccontrol value is the same as the old, nothing to do */
+	if (new_maccontrol == maccontrol)
+		return;
+
+	/* something changed, cache the new value */
+	wlc_hw->maccontrol = new_maccontrol;
+
+	/* write the new values with overrides applied */
+	brcms_c_mctrl_write(wlc_hw);
+}
+
+/* write the software state of maccontrol and overrides to the maccontrol register */
+static void brcms_c_mctrl_write(struct brcms_hardware *wlc_hw)
+{
+	u32 maccontrol = wlc_hw->maccontrol;
+
+	/* OR in the wake bit if overridden */
+	if (wlc_hw->wake_override)
+		maccontrol |= MCTL_WAKE;
+
+	/* set AP and INFRA bits for mute if needed */
+	if (wlc_hw->mute_override) {
+		maccontrol &= ~(MCTL_AP);
+		maccontrol |= MCTL_INFRA;
+	}
+
+	W_REG(&wlc_hw->regs->maccontrol, maccontrol);
+}
+
+void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
+				 u32 override_bit)
+{
+	if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE)) {
+		mboolset(wlc_hw->wake_override, override_bit);
+		return;
+	}
+
+	mboolset(wlc_hw->wake_override, override_bit);
+
+	brcms_c_mctrl_write(wlc_hw);
+	brcms_b_wait_for_wake(wlc_hw);
+
+	return;
+}
+
+void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
+				   u32 override_bit)
+{
+	mboolclr(wlc_hw->wake_override, override_bit);
+
+	if (wlc_hw->wake_override || (wlc_hw->maccontrol & MCTL_WAKE))
+		return;
+
+	brcms_c_mctrl_write(wlc_hw);
+
+	return;
+}
+
+/* When driver needs ucode to stop beaconing, it has to make sure that
+ * MCTL_AP is clear and MCTL_INFRA is set
+ * Mode           MCTL_AP        MCTL_INFRA
+ * AP                1              1
+ * STA               0              1 <--- This will ensure no beacons
+ * IBSS              0              0
+ */
+static void brcms_c_ucode_mute_override_set(struct brcms_hardware *wlc_hw)
+{
+	wlc_hw->mute_override = 1;
+
+	/* if maccontrol already has AP == 0 and INFRA == 1 without this
+	 * override, then there is no change to write
+	 */
+	if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+		return;
+
+	brcms_c_mctrl_write(wlc_hw);
+
+	return;
+}
+
+/* Clear the override on AP and INFRA bits */
+static void brcms_c_ucode_mute_override_clear(struct brcms_hardware *wlc_hw)
+{
+	if (wlc_hw->mute_override == 0)
+		return;
+
+	wlc_hw->mute_override = 0;
+
+	/* if maccontrol already has AP == 0 and INFRA == 1 without this
+	 * override, then there is no change to write
+	 */
+	if ((wlc_hw->maccontrol & (MCTL_AP | MCTL_INFRA)) == MCTL_INFRA)
+		return;
+
+	brcms_c_mctrl_write(wlc_hw);
+}
+
+/*
+ * Write a MAC address to the given match reg offset in the RXE match engine.
+ */
+void
+brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw, int match_reg_offset,
+		       const u8 *addr)
+{
+	d11regs_t *regs;
+	u16 mac_l;
+	u16 mac_m;
+	u16 mac_h;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d: brcms_b_set_addrmatch\n",
+		 wlc_hw->unit);
+
+	regs = wlc_hw->regs;
+	mac_l = addr[0] | (addr[1] << 8);
+	mac_m = addr[2] | (addr[3] << 8);
+	mac_h = addr[4] | (addr[5] << 8);
+
+	/* enter the MAC addr into the RXE match registers */
+	W_REG(&regs->rcm_ctl, RCM_INC_DATA | match_reg_offset);
+	W_REG(&regs->rcm_mat_data, mac_l);
+	W_REG(&regs->rcm_mat_data, mac_m);
+	W_REG(&regs->rcm_mat_data, mac_h);
+
+}
+
+void
+brcms_b_write_template_ram(struct brcms_hardware *wlc_hw, int offset, int len,
+			    void *buf)
+{
+	d11regs_t *regs;
+	u32 word;
+	bool be_bit;
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	regs = wlc_hw->regs;
+	W_REG(&regs->tplatewrptr, offset);
+
+	/* if MCTL_BIGEND bit set in mac control register,
+	 * the chip swaps data in fifo, as well as data in
+	 * template ram
+	 */
+	be_bit = (R_REG(&regs->maccontrol) & MCTL_BIGEND) != 0;
+
+	while (len > 0) {
+		memcpy(&word, buf, sizeof(u32));
+
+		if (be_bit)
+			word = cpu_to_be32(word);
+		else
+			word = cpu_to_le32(word);
+
+		W_REG(&regs->tplatewrdata, word);
+
+		buf = (u8 *) buf + sizeof(u32);
+		len -= sizeof(u32);
+	}
+}
+
+void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin)
+{
+	wlc_hw->band->CWmin = newmin;
+
+	W_REG(&wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMIN);
+	(void)R_REG(&wlc_hw->regs->objaddr);
+	W_REG(&wlc_hw->regs->objdata, newmin);
+}
+
+void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax)
+{
+	wlc_hw->band->CWmax = newmax;
+
+	W_REG(&wlc_hw->regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_CWMAX);
+	(void)R_REG(&wlc_hw->regs->objaddr);
+	W_REG(&wlc_hw->regs->objdata, newmax);
+}
+
+void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw)
+{
+	bool fastclk;
+
+	/* request FAST clock if not on */
+	fastclk = wlc_hw->forcefastclk;
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	wlc_phy_bw_state_set(wlc_hw->band->pi, bw);
+
+	brcms_b_phy_reset(wlc_hw);
+	wlc_phy_init(wlc_hw->band->pi, wlc_phy_chanspec_get(wlc_hw->band->pi));
+
+	/* restore the clk */
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+static void
+brcms_c_write_hw_bcntemplate0(struct brcms_hardware *wlc_hw, void *bcn,
+			      int len)
+{
+	d11regs_t *regs = wlc_hw->regs;
+
+	brcms_b_write_template_ram(wlc_hw, T_BCN0_TPL_BASE, (len + 3) & ~3,
+				    bcn);
+	/* write beacon length to SCR */
+	brcms_b_write_shm(wlc_hw, M_BCN0_FRM_BYTESZ, (u16) len);
+	/* mark beacon0 valid */
+	OR_REG(&regs->maccommand, MCMD_BCN0VLD);
+}
+
+static void
+brcms_c_write_hw_bcntemplate1(struct brcms_hardware *wlc_hw, void *bcn,
+			      int len)
+{
+	d11regs_t *regs = wlc_hw->regs;
+
+	brcms_b_write_template_ram(wlc_hw, T_BCN1_TPL_BASE, (len + 3) & ~3,
+				    bcn);
+	/* write beacon length to SCR */
+	brcms_b_write_shm(wlc_hw, M_BCN1_FRM_BYTESZ, (u16) len);
+	/* mark beacon1 valid */
+	OR_REG(&regs->maccommand, MCMD_BCN1VLD);
+}
+
+/* mac is assumed to be suspended at this point */
+void
+brcms_b_write_hw_bcntemplates(struct brcms_hardware *wlc_hw, void *bcn,
+			      int len, bool both)
+{
+	d11regs_t *regs = wlc_hw->regs;
+
+	if (both) {
+		brcms_c_write_hw_bcntemplate0(wlc_hw, bcn, len);
+		brcms_c_write_hw_bcntemplate1(wlc_hw, bcn, len);
+	} else {
+		/* bcn 0 */
+		if (!(R_REG(&regs->maccommand) & MCMD_BCN0VLD))
+			brcms_c_write_hw_bcntemplate0(wlc_hw, bcn, len);
+		/* bcn 1 */
+		else if (!
+			 (R_REG(&regs->maccommand) & MCMD_BCN1VLD))
+			brcms_c_write_hw_bcntemplate1(wlc_hw, bcn, len);
+	}
+}
+
+static void brcms_b_upd_synthpu(struct brcms_hardware *wlc_hw)
+{
+	u16 v;
+	struct brcms_c_info *wlc = wlc_hw->wlc;
+	/* update SYNTHPU_DLY */
+
+	if (BRCMS_ISLCNPHY(wlc->band)) {
+		v = SYNTHPU_DLY_LPPHY_US;
+	} else if (BRCMS_ISNPHY(wlc->band) && (NREV_GE(wlc->band->phyrev, 3))) {
+		v = SYNTHPU_DLY_NPHY_US;
+	} else {
+		v = SYNTHPU_DLY_BPHY_US;
+	}
+
+	brcms_b_write_shm(wlc_hw, M_SYNTHPU_DLY, v);
+}
+
+/* band-specific init */
+static void
+brcms_b_bsinit(struct brcms_c_info *wlc, chanspec_t chanspec)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+
+	BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+		wlc_hw->band->bandunit);
+
+	brcms_c_ucode_bsinit(wlc_hw);
+
+	wlc_phy_init(wlc_hw->band->pi, chanspec);
+
+	brcms_c_ucode_txant_set(wlc_hw);
+
+	/* cwmin is band-specific, update hardware with value for current band */
+	brcms_b_set_cwmin(wlc_hw, wlc_hw->band->CWmin);
+	brcms_b_set_cwmax(wlc_hw, wlc_hw->band->CWmax);
+
+	brcms_b_update_slot_timing(wlc_hw,
+				    BAND_5G(wlc_hw->band->
+					    bandtype) ? true : wlc_hw->
+				    shortslot);
+
+	/* write phytype and phyvers */
+	brcms_b_write_shm(wlc_hw, M_PHYTYPE, (u16) wlc_hw->band->phytype);
+	brcms_b_write_shm(wlc_hw, M_PHYVER, (u16) wlc_hw->band->phyrev);
+
+	/* initialize the txphyctl1 rate table since shmem is shared between bands */
+	brcms_upd_ofdm_pctl1_table(wlc_hw);
+
+	brcms_b_upd_synthpu(wlc_hw);
+}
+
+static void brcms_b_core_phy_clk(struct brcms_hardware *wlc_hw, bool clk)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d: clk %d\n", wlc_hw->unit, clk);
+
+	wlc_hw->phyclk = clk;
+
+	if (OFF == clk) {	/* clear gmode bit, put phy into reset */
+
+		ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC | SICF_GMODE),
+			       (SICF_PRST | SICF_FGC));
+		udelay(1);
+		ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_PRST);
+		udelay(1);
+
+	} else {		/* take phy out of reset */
+
+		ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_FGC), SICF_FGC);
+		udelay(1);
+		ai_core_cflags(wlc_hw->sih, (SICF_FGC), 0);
+		udelay(1);
+
+	}
+}
+
+/* Perform a soft reset of the PHY PLL */
+void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	ai_corereg(wlc_hw->sih, SI_CC_IDX,
+		   offsetof(chipcregs_t, chipcontrol_addr), ~0, 0);
+	udelay(1);
+	ai_corereg(wlc_hw->sih, SI_CC_IDX,
+		   offsetof(chipcregs_t, chipcontrol_data), 0x4, 0);
+	udelay(1);
+	ai_corereg(wlc_hw->sih, SI_CC_IDX,
+		   offsetof(chipcregs_t, chipcontrol_data), 0x4, 4);
+	udelay(1);
+	ai_corereg(wlc_hw->sih, SI_CC_IDX,
+		   offsetof(chipcregs_t, chipcontrol_data), 0x4, 0);
+	udelay(1);
+}
+
+/* light way to turn on phy clock without reset for NPHY only
+ *  refer to brcms_b_core_phy_clk for full version
+ */
+void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk)
+{
+	/* support(necessary for NPHY and HYPHY) only */
+	if (!BRCMS_ISNPHY(wlc_hw->band))
+		return;
+
+	if (ON == clk)
+		ai_core_cflags(wlc_hw->sih, SICF_FGC, SICF_FGC);
+	else
+		ai_core_cflags(wlc_hw->sih, SICF_FGC, 0);
+
+}
+
+void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk)
+{
+	if (ON == clk)
+		ai_core_cflags(wlc_hw->sih, SICF_MPCLKE, SICF_MPCLKE);
+	else
+		ai_core_cflags(wlc_hw->sih, SICF_MPCLKE, 0);
+}
+
+void brcms_b_phy_reset(struct brcms_hardware *wlc_hw)
+{
+	struct brcms_phy_pub *pih = wlc_hw->band->pi;
+	u32 phy_bw_clkbits;
+	bool phy_in_reset = false;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	if (pih == NULL)
+		return;
+
+	phy_bw_clkbits = wlc_phy_clk_bwbits(wlc_hw->band->pi);
+
+	/* Specific reset sequence required for NPHY rev 3 and 4 */
+	if (BRCMS_ISNPHY(wlc_hw->band) && NREV_GE(wlc_hw->band->phyrev, 3) &&
+	    NREV_LE(wlc_hw->band->phyrev, 4)) {
+		/* Set the PHY bandwidth */
+		ai_core_cflags(wlc_hw->sih, SICF_BWMASK, phy_bw_clkbits);
+
+		udelay(1);
+
+		/* Perform a soft reset of the PHY PLL */
+		brcms_b_core_phypll_reset(wlc_hw);
+
+		/* reset the PHY */
+		ai_core_cflags(wlc_hw->sih, (SICF_PRST | SICF_PCLKE),
+			       (SICF_PRST | SICF_PCLKE));
+		phy_in_reset = true;
+	} else {
+
+		ai_core_cflags(wlc_hw->sih,
+			       (SICF_PRST | SICF_PCLKE | SICF_BWMASK),
+			       (SICF_PRST | SICF_PCLKE | phy_bw_clkbits));
+	}
+
+	udelay(2);
+	brcms_b_core_phy_clk(wlc_hw, ON);
+
+	if (pih)
+		wlc_phy_anacore(pih, ON);
+}
+
+/* switch to and initialize new band */
+static void
+brcms_b_setband(struct brcms_hardware *wlc_hw, uint bandunit,
+				chanspec_t chanspec) {
+	struct brcms_c_info *wlc = wlc_hw->wlc;
+	u32 macintmask;
+
+	/* Enable the d11 core before accessing it */
+	if (!ai_iscoreup(wlc_hw->sih)) {
+		ai_core_reset(wlc_hw->sih, 0, 0);
+		brcms_c_mctrl_reset(wlc_hw);
+	}
+
+	macintmask = brcms_c_setband_inact(wlc, bandunit);
+
+	if (!wlc_hw->up)
+		return;
+
+	brcms_b_core_phy_clk(wlc_hw, ON);
+
+	/* band-specific initializations */
+	brcms_b_bsinit(wlc, chanspec);
+
+	/*
+	 * If there are any pending software interrupt bits,
+	 * then replace these with a harmless nonzero value
+	 * so brcms_c_dpc() will re-enable interrupts when done.
+	 */
+	if (wlc->macintstatus)
+		wlc->macintstatus = MI_DMAINT;
+
+	/* restore macintmask */
+	brcms_intrsrestore(wlc->wl, macintmask);
+
+	/* ucode should still be suspended.. */
+	WARN_ON((R_REG(&wlc_hw->regs->maccontrol) & MCTL_EN_MAC) != 0);
+}
+
+/* low-level band switch utility routine */
+void brcms_c_setxband(struct brcms_hardware *wlc_hw,
+				     uint bandunit)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+		bandunit);
+
+	wlc_hw->band = wlc_hw->bandstate[bandunit];
+
+	/* BMAC_NOTE: until we eliminate need for wlc->band refs in low level code */
+	wlc_hw->wlc->band = wlc_hw->wlc->bandstate[bandunit];
+
+	/* set gmode core flag */
+	if (wlc_hw->sbclk && !wlc_hw->noreset) {
+		ai_core_cflags(wlc_hw->sih, SICF_GMODE,
+			       ((bandunit == 0) ? SICF_GMODE : 0));
+	}
+}
+
+static bool brcms_c_isgoodchip(struct brcms_hardware *wlc_hw)
+{
+
+	/* reject unsupported corerev */
+	if (!VALID_COREREV(wlc_hw->corerev)) {
+		wiphy_err(wlc_hw->wlc->wiphy, "unsupported core rev %d\n",
+			  wlc_hw->corerev);
+		return false;
+	}
+
+	return true;
+}
+
+/* Validate some board info parameters */
+static bool brcms_c_validboardtype(struct brcms_hardware *wlc_hw)
+{
+	uint boardrev = wlc_hw->boardrev;
+
+	/* 4 bits each for board type, major, minor, and tiny version */
+	uint brt = (boardrev & 0xf000) >> 12;
+	uint b0 = (boardrev & 0xf00) >> 8;
+	uint b1 = (boardrev & 0xf0) >> 4;
+	uint b2 = boardrev & 0xf;
+
+	/* voards from other vendors are always considered valid */
+	if (wlc_hw->sih->boardvendor != PCI_VENDOR_ID_BROADCOM)
+		return true;
+
+	/* do some boardrev sanity checks when boardvendor is Broadcom */
+	if (boardrev == 0)
+		return false;
+
+	if (boardrev <= 0xff)
+		return true;
+
+	if ((brt > 2) || (brt == 0) || (b0 > 9) || (b0 == 0) || (b1 > 9)
+		|| (b2 > 9))
+		return false;
+
+	return true;
+}
+
+static char *brcms_c_get_macaddr(struct brcms_hardware *wlc_hw)
+{
+	const char *varname = "macaddr";
+	char *macaddr;
+
+	/* If macaddr exists, use it (Sromrev4, CIS, ...). */
+	macaddr = getvar(wlc_hw->vars, varname);
+	if (macaddr != NULL)
+		return macaddr;
+
+	if (NBANDS_HW(wlc_hw) > 1)
+		varname = "et1macaddr";
+	else
+		varname = "il0macaddr";
+
+	macaddr = getvar(wlc_hw->vars, varname);
+	if (macaddr == NULL) {
+		wiphy_err(wlc_hw->wlc->wiphy, "wl%d: wlc_get_macaddr: macaddr "
+			  "getvar(%s) not found\n", wlc_hw->unit, varname);
+	}
+
+	return macaddr;
+}
+
+/*
+ * Return true if radio is disabled, otherwise false.
+ * hw radio disable signal is an external pin, users activate it asynchronously
+ * this function could be called when driver is down and w/o clock
+ * it operates on different registers depending on corerev and boardflag.
+ */
+bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw)
+{
+	bool v, clk, xtal;
+	u32 resetbits = 0, flags = 0;
+
+	xtal = wlc_hw->sbclk;
+	if (!xtal)
+		brcms_b_xtal(wlc_hw, ON);
+
+	/* may need to take core out of reset first */
+	clk = wlc_hw->clk;
+	if (!clk) {
+		/*
+		 * mac no longer enables phyclk automatically when driver
+		 * accesses phyreg throughput mac. This can be skipped since
+		 * only mac reg is accessed below
+		 */
+		flags |= SICF_PCLKE;
+
+		/* AI chip doesn't restore bar0win2 on hibernation/resume, need sw fixup */
+		if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+		    (wlc_hw->sih->chip == BCM43225_CHIP_ID))
+			wlc_hw->regs =
+			    (d11regs_t *) ai_setcore(wlc_hw->sih, D11_CORE_ID,
+						     0);
+		ai_core_reset(wlc_hw->sih, flags, resetbits);
+		brcms_c_mctrl_reset(wlc_hw);
+	}
+
+	v = ((R_REG(&wlc_hw->regs->phydebug) & PDBG_RFD) != 0);
+
+	/* put core back into reset */
+	if (!clk)
+		ai_core_disable(wlc_hw->sih, 0);
+
+	if (!xtal)
+		brcms_b_xtal(wlc_hw, OFF);
+
+	return v;
+}
+
+/* Initialize just the hardware when coming out of POR or S3/S5 system states */
+void brcms_b_hw_up(struct brcms_hardware *wlc_hw)
+{
+	if (wlc_hw->wlc->pub->hw_up)
+		return;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	/*
+	 * Enable pll and xtal, initialize the power control registers,
+	 * and force fastclock for the remainder of brcms_c_up().
+	 */
+	brcms_b_xtal(wlc_hw, ON);
+	ai_clkctl_init(wlc_hw->sih);
+	brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	if (wlc_hw->sih->bustype == PCI_BUS) {
+		ai_pci_fixcfg(wlc_hw->sih);
+
+		/* AI chip doesn't restore bar0win2 on hibernation/resume, need sw fixup */
+		if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+		    (wlc_hw->sih->chip == BCM43225_CHIP_ID))
+			wlc_hw->regs =
+			    (d11regs_t *) ai_setcore(wlc_hw->sih, D11_CORE_ID,
+						     0);
+	}
+
+	/* Inform phy that a POR reset has occurred so it does a complete phy init */
+	wlc_phy_por_inform(wlc_hw->band->pi);
+
+	wlc_hw->ucode_loaded = false;
+	wlc_hw->wlc->pub->hw_up = true;
+
+	if ((wlc_hw->boardflags & BFL_FEM)
+	    && (wlc_hw->sih->chip == BCM4313_CHIP_ID)) {
+		if (!
+		    (wlc_hw->boardrev >= 0x1250
+		     && (wlc_hw->boardflags & BFL_FEM_BT)))
+			ai_epa_4313war(wlc_hw->sih);
+	}
+}
+
+static bool wlc_dma_rxreset(struct brcms_hardware *wlc_hw, uint fifo)
+{
+	struct dma_pub *di = wlc_hw->di[fifo];
+	return dma_rxreset(di);
+}
+
+/* d11 core reset
+ *   ensure fask clock during reset
+ *   reset dma
+ *   reset d11(out of reset)
+ *   reset phy(out of reset)
+ *   clear software macintstatus for fresh new start
+ * one testing hack wlc_hw->noreset will bypass the d11/phy reset
+ */
+void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags)
+{
+	d11regs_t *regs;
+	uint i;
+	bool fastclk;
+	u32 resetbits = 0;
+
+	if (flags == BRCMS_USE_COREFLAGS)
+		flags = (wlc_hw->band->pi ? wlc_hw->band->core_flags : 0);
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	regs = wlc_hw->regs;
+
+	/* request FAST clock if not on  */
+	fastclk = wlc_hw->forcefastclk;
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	/* reset the dma engines except first time thru */
+	if (ai_iscoreup(wlc_hw->sih)) {
+		for (i = 0; i < NFIFO; i++)
+			if ((wlc_hw->di[i]) && (!dma_txreset(wlc_hw->di[i]))) {
+				wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: "
+					  "dma_txreset[%d]: cannot stop dma\n",
+					   wlc_hw->unit, __func__, i);
+			}
+
+		if ((wlc_hw->di[RX_FIFO])
+		    && (!wlc_dma_rxreset(wlc_hw, RX_FIFO))) {
+			wiphy_err(wlc_hw->wlc->wiphy, "wl%d: %s: dma_rxreset"
+				  "[%d]: cannot stop dma\n",
+				  wlc_hw->unit, __func__, RX_FIFO);
+		}
+	}
+	/* if noreset, just stop the psm and return */
+	if (wlc_hw->noreset) {
+		wlc_hw->wlc->macintstatus = 0;	/* skip wl_dpc after down */
+		brcms_b_mctrl(wlc_hw, MCTL_PSM_RUN | MCTL_EN_MAC, 0);
+		return;
+	}
+
+	/*
+	 * mac no longer enables phyclk automatically when driver accesses
+	 * phyreg throughput mac, AND phy_reset is skipped at early stage when
+	 * band->pi is invalid. need to enable PHY CLK
+	 */
+	flags |= SICF_PCLKE;
+
+	/* reset the core
+	 * In chips with PMU, the fastclk request goes through d11 core reg 0x1e0, which
+	 *  is cleared by the core_reset. have to re-request it.
+	 *  This adds some delay and we can optimize it by also requesting fastclk through
+	 *  chipcommon during this period if necessary. But that has to work coordinate
+	 *  with other driver like mips/arm since they may touch chipcommon as well.
+	 */
+	wlc_hw->clk = false;
+	ai_core_reset(wlc_hw->sih, flags, resetbits);
+	wlc_hw->clk = true;
+	if (wlc_hw->band && wlc_hw->band->pi)
+		wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, true);
+
+	brcms_c_mctrl_reset(wlc_hw);
+
+	if (PMUCTL_ENAB(wlc_hw->sih))
+		brcms_b_clkctl_clk(wlc_hw, CLK_FAST);
+
+	brcms_b_phy_reset(wlc_hw);
+
+	/* turn on PHY_PLL */
+	brcms_b_core_phypll_ctl(wlc_hw, true);
+
+	/* clear sw intstatus */
+	wlc_hw->wlc->macintstatus = 0;
+
+	/* restore the clk setting */
+	if (!fastclk)
+		brcms_b_clkctl_clk(wlc_hw, CLK_DYNAMIC);
+}
+
+/* txfifo sizes needs to be modified(increased) since the newer cores
+ * have more memory.
+ */
+static void brcms_b_corerev_fifofixup(struct brcms_hardware *wlc_hw)
+{
+	d11regs_t *regs = wlc_hw->regs;
+	u16 fifo_nu;
+	u16 txfifo_startblk = TXFIFO_START_BLK, txfifo_endblk;
+	u16 txfifo_def, txfifo_def1;
+	u16 txfifo_cmd;
+
+	/* tx fifos start at TXFIFO_START_BLK from the Base address */
+	txfifo_startblk = TXFIFO_START_BLK;
+
+	/* sequence of operations:  reset fifo, set fifo size, reset fifo */
+	for (fifo_nu = 0; fifo_nu < NFIFO; fifo_nu++) {
+
+		txfifo_endblk = txfifo_startblk + wlc_hw->xmtfifo_sz[fifo_nu];
+		txfifo_def = (txfifo_startblk & 0xff) |
+		    (((txfifo_endblk - 1) & 0xff) << TXFIFO_FIFOTOP_SHIFT);
+		txfifo_def1 = ((txfifo_startblk >> 8) & 0x1) |
+		    ((((txfifo_endblk -
+			1) >> 8) & 0x1) << TXFIFO_FIFOTOP_SHIFT);
+		txfifo_cmd =
+		    TXFIFOCMD_RESET_MASK | (fifo_nu << TXFIFOCMD_FIFOSEL_SHIFT);
+
+		W_REG(&regs->xmtfifocmd, txfifo_cmd);
+		W_REG(&regs->xmtfifodef, txfifo_def);
+		W_REG(&regs->xmtfifodef1, txfifo_def1);
+
+		W_REG(&regs->xmtfifocmd, txfifo_cmd);
+
+		txfifo_startblk += wlc_hw->xmtfifo_sz[fifo_nu];
+	}
+	/*
+	 * need to propagate to shm location to be in sync since ucode/hw won't
+	 * do this
+	 */
+	brcms_b_write_shm(wlc_hw, M_FIFOSIZE0,
+			   wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]);
+	brcms_b_write_shm(wlc_hw, M_FIFOSIZE1,
+			   wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]);
+	brcms_b_write_shm(wlc_hw, M_FIFOSIZE2,
+			   ((wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO] << 8) | wlc_hw->
+			    xmtfifo_sz[TX_AC_BK_FIFO]));
+	brcms_b_write_shm(wlc_hw, M_FIFOSIZE3,
+			   ((wlc_hw->xmtfifo_sz[TX_ATIM_FIFO] << 8) | wlc_hw->
+			    xmtfifo_sz[TX_BCMC_FIFO]));
+}
+
+/* d11 core init
+ *   reset PSM
+ *   download ucode/PCM
+ *   let ucode run to suspended
+ *   download ucode inits
+ *   config other core registers
+ *   init dma
+ */
+static void brcms_b_coreinit(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs;
+	u32 sflags;
+	uint bcnint_us;
+	uint i = 0;
+	bool fifosz_fixup = false;
+	int err = 0;
+	u16 buf[NFIFO];
+	struct wiphy *wiphy = wlc->wiphy;
+
+	regs = wlc_hw->regs;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	/* reset PSM */
+	brcms_b_mctrl(wlc_hw, ~0, (MCTL_IHR_EN | MCTL_PSM_JMP_0 | MCTL_WAKE));
+
+	brcms_ucode_download(wlc_hw);
+	/*
+	 * FIFOSZ fixup. driver wants to controls the fifo allocation.
+	 */
+	fifosz_fixup = true;
+
+	/* let the PSM run to the suspended state, set mode to BSS STA */
+	W_REG(&regs->macintstatus, -1);
+	brcms_b_mctrl(wlc_hw, ~0,
+		       (MCTL_IHR_EN | MCTL_INFRA | MCTL_PSM_RUN | MCTL_WAKE));
+
+	/* wait for ucode to self-suspend after auto-init */
+	SPINWAIT(((R_REG(&regs->macintstatus) & MI_MACSSPNDD) == 0),
+		 1000 * 1000);
+	if ((R_REG(&regs->macintstatus) & MI_MACSSPNDD) == 0)
+		wiphy_err(wiphy, "wl%d: wlc_coreinit: ucode did not self-"
+			  "suspend!\n", wlc_hw->unit);
+
+	brcms_c_gpio_init(wlc);
+
+	sflags = ai_core_sflags(wlc_hw->sih, 0, 0);
+
+	if (D11REV_IS(wlc_hw->corerev, 23)) {
+		if (BRCMS_ISNPHY(wlc_hw->band))
+			brcms_c_write_inits(wlc_hw, d11n0initvals16);
+		else
+			wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+				  " %d\n", __func__, wlc_hw->unit,
+				  wlc_hw->corerev);
+	} else if (D11REV_IS(wlc_hw->corerev, 24)) {
+		if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+			brcms_c_write_inits(wlc_hw, d11lcn0initvals24);
+		} else {
+			wiphy_err(wiphy, "%s: wl%d: unsupported phy in corerev"
+				  " %d\n", __func__, wlc_hw->unit,
+				  wlc_hw->corerev);
+		}
+	} else {
+		wiphy_err(wiphy, "%s: wl%d: unsupported corerev %d\n",
+			  __func__, wlc_hw->unit, wlc_hw->corerev);
+	}
+
+	/* For old ucode, txfifo sizes needs to be modified(increased) */
+	if (fifosz_fixup == true) {
+		brcms_b_corerev_fifofixup(wlc_hw);
+	}
+
+	/* check txfifo allocations match between ucode and driver */
+	buf[TX_AC_BE_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE0);
+	if (buf[TX_AC_BE_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BE_FIFO]) {
+		i = TX_AC_BE_FIFO;
+		err = -1;
+	}
+	buf[TX_AC_VI_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE1);
+	if (buf[TX_AC_VI_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VI_FIFO]) {
+		i = TX_AC_VI_FIFO;
+		err = -1;
+	}
+	buf[TX_AC_BK_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE2);
+	buf[TX_AC_VO_FIFO] = (buf[TX_AC_BK_FIFO] >> 8) & 0xff;
+	buf[TX_AC_BK_FIFO] &= 0xff;
+	if (buf[TX_AC_BK_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_BK_FIFO]) {
+		i = TX_AC_BK_FIFO;
+		err = -1;
+	}
+	if (buf[TX_AC_VO_FIFO] != wlc_hw->xmtfifo_sz[TX_AC_VO_FIFO]) {
+		i = TX_AC_VO_FIFO;
+		err = -1;
+	}
+	buf[TX_BCMC_FIFO] = brcms_b_read_shm(wlc_hw, M_FIFOSIZE3);
+	buf[TX_ATIM_FIFO] = (buf[TX_BCMC_FIFO] >> 8) & 0xff;
+	buf[TX_BCMC_FIFO] &= 0xff;
+	if (buf[TX_BCMC_FIFO] != wlc_hw->xmtfifo_sz[TX_BCMC_FIFO]) {
+		i = TX_BCMC_FIFO;
+		err = -1;
+	}
+	if (buf[TX_ATIM_FIFO] != wlc_hw->xmtfifo_sz[TX_ATIM_FIFO]) {
+		i = TX_ATIM_FIFO;
+		err = -1;
+	}
+	if (err != 0) {
+		wiphy_err(wiphy, "wlc_coreinit: txfifo mismatch: ucode size %d"
+			  " driver size %d index %d\n", buf[i],
+			  wlc_hw->xmtfifo_sz[i], i);
+	}
+
+	/* make sure we can still talk to the mac */
+	WARN_ON(R_REG(&regs->maccontrol) == 0xffffffff);
+
+	/* band-specific inits done by wlc_bsinit() */
+
+	/* Set up frame burst size and antenna swap threshold init values */
+	brcms_b_write_shm(wlc_hw, M_MBURST_SIZE, MAXTXFRAMEBURST);
+	brcms_b_write_shm(wlc_hw, M_MAX_ANTCNT, ANTCNT);
+
+	/* enable one rx interrupt per received frame */
+	W_REG(&regs->intrcvlazy[0], (1 << IRL_FC_SHIFT));
+
+	/* set the station mode (BSS STA) */
+	brcms_b_mctrl(wlc_hw,
+		       (MCTL_INFRA | MCTL_DISCARD_PMQ | MCTL_AP),
+		       (MCTL_INFRA | MCTL_DISCARD_PMQ));
+
+	/* set up Beacon interval */
+	bcnint_us = 0x8000 << 10;
+	W_REG(&regs->tsf_cfprep, (bcnint_us << CFPREP_CBI_SHIFT));
+	W_REG(&regs->tsf_cfpstart, bcnint_us);
+	W_REG(&regs->macintstatus, MI_GP1);
+
+	/* write interrupt mask */
+	W_REG(&regs->intctrlregs[RX_FIFO].intmask, DEF_RXINTMASK);
+
+	/* allow the MAC to control the PHY clock (dynamic on/off) */
+	brcms_b_macphyclk_set(wlc_hw, ON);
+
+	/* program dynamic clock control fast powerup delay register */
+	wlc->fastpwrup_dly = ai_clkctl_fast_pwrup_delay(wlc_hw->sih);
+	W_REG(&regs->scc_fastpwrup_dly, wlc->fastpwrup_dly);
+
+	/* tell the ucode the corerev */
+	brcms_b_write_shm(wlc_hw, M_MACHW_VER, (u16) wlc_hw->corerev);
+
+	/* tell the ucode MAC capabilities */
+	brcms_b_write_shm(wlc_hw, M_MACHW_CAP_L,
+			   (u16) (wlc_hw->machwcap & 0xffff));
+	brcms_b_write_shm(wlc_hw, M_MACHW_CAP_H,
+			   (u16) ((wlc_hw->
+				      machwcap >> 16) & 0xffff));
+
+	/* write retry limits to SCR, this done after PSM init */
+	W_REG(&regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+	(void)R_REG(&regs->objaddr);
+	W_REG(&regs->objdata, wlc_hw->SRL);
+	W_REG(&regs->objaddr, OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+	(void)R_REG(&regs->objaddr);
+	W_REG(&regs->objdata, wlc_hw->LRL);
+
+	/* write rate fallback retry limits */
+	brcms_b_write_shm(wlc_hw, M_SFRMTXCNTFBRTHSD, wlc_hw->SFBL);
+	brcms_b_write_shm(wlc_hw, M_LFRMTXCNTFBRTHSD, wlc_hw->LFBL);
+
+	AND_REG(&regs->ifs_ctl, 0x0FFF);
+	W_REG(&regs->ifs_aifsn, EDCF_AIFSN_MIN);
+
+	/* dma initializations */
+	wlc->txpend16165war = 0;
+
+	/* init the tx dma engines */
+	for (i = 0; i < NFIFO; i++) {
+		if (wlc_hw->di[i])
+			dma_txinit(wlc_hw->di[i]);
+	}
+
+	/* init the rx dma engine(s) and post receive buffers */
+	dma_rxinit(wlc_hw->di[RX_FIFO]);
+	dma_rxfill(wlc_hw->di[RX_FIFO]);
+}
+
+/* This function is used for changing the tsf frac register
+ * If spur avoidance mode is off, the mac freq will be 80/120/160Mhz
+ * If spur avoidance mode is on1, the mac freq will be 82/123/164Mhz
+ * If spur avoidance mode is on2, the mac freq will be 84/126/168Mhz
+ * HTPHY Formula is 2^26/freq(MHz) e.g.
+ * For spuron2 - 126MHz -> 2^26/126 = 532610.0
+ *  - 532610 = 0x82082 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x2082
+ * For spuron: 123MHz -> 2^26/123    = 545600.5
+ *  - 545601 = 0x85341 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x5341
+ * For spur off: 120MHz -> 2^26/120    = 559240.5
+ *  - 559241 = 0x88889 => tsf_clk_frac_h = 0x8, tsf_clk_frac_l = 0x8889
+ */
+
+void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode)
+{
+	d11regs_t *regs;
+	regs = wlc_hw->regs;
+
+	if ((wlc_hw->sih->chip == BCM43224_CHIP_ID) ||
+	    (wlc_hw->sih->chip == BCM43225_CHIP_ID)) {
+		if (spurmode == WL_SPURAVOID_ON2) {	/* 126Mhz */
+			W_REG(&regs->tsf_clk_frac_l, 0x2082);
+			W_REG(&regs->tsf_clk_frac_h, 0x8);
+		} else if (spurmode == WL_SPURAVOID_ON1) {	/* 123Mhz */
+			W_REG(&regs->tsf_clk_frac_l, 0x5341);
+			W_REG(&regs->tsf_clk_frac_h, 0x8);
+		} else {	/* 120Mhz */
+			W_REG(&regs->tsf_clk_frac_l, 0x8889);
+			W_REG(&regs->tsf_clk_frac_h, 0x8);
+		}
+	} else if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+		if (spurmode == WL_SPURAVOID_ON1) {	/* 82Mhz */
+			W_REG(&regs->tsf_clk_frac_l, 0x7CE0);
+			W_REG(&regs->tsf_clk_frac_h, 0xC);
+		} else {	/* 80Mhz */
+			W_REG(&regs->tsf_clk_frac_l, 0xCCCD);
+			W_REG(&regs->tsf_clk_frac_h, 0xC);
+		}
+	}
+}
+
+/* Initialize GPIOs that are controlled by D11 core */
+static void brcms_c_gpio_init(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs;
+	u32 gc, gm;
+
+	regs = wlc_hw->regs;
+
+	/* use GPIO select 0 to get all gpio signals from the gpio out reg */
+	brcms_b_mctrl(wlc_hw, MCTL_GPOUT_SEL_MASK, 0);
+
+	/*
+	 * Common GPIO setup:
+	 *      G0 = LED 0 = WLAN Activity
+	 *      G1 = LED 1 = WLAN 2.4 GHz Radio State
+	 *      G2 = LED 2 = WLAN 5 GHz Radio State
+	 *      G4 = radio disable input (HI enabled, LO disabled)
+	 */
+
+	gc = gm = 0;
+
+	/* Allocate GPIOs for mimo antenna diversity feature */
+	if (wlc_hw->antsel_type == ANTSEL_2x3) {
+		/* Enable antenna diversity, use 2x3 mode */
+		brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+			     MHF3_ANTSEL_EN, BRCM_BAND_ALL);
+		brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE,
+			     MHF3_ANTSEL_MODE, BRCM_BAND_ALL);
+
+		/* init superswitch control */
+		wlc_phy_antsel_init(wlc_hw->band->pi, false);
+
+	} else if (wlc_hw->antsel_type == ANTSEL_2x4) {
+		gm |= gc |= (BOARD_GPIO_12 | BOARD_GPIO_13);
+		/*
+		 * The board itself is powered by these GPIOs
+		 * (when not sending pattern) so set them high
+		 */
+		OR_REG(&regs->psm_gpio_oe,
+		       (BOARD_GPIO_12 | BOARD_GPIO_13));
+		OR_REG(&regs->psm_gpio_out,
+		       (BOARD_GPIO_12 | BOARD_GPIO_13));
+
+		/* Enable antenna diversity, use 2x4 mode */
+		brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_EN,
+			     MHF3_ANTSEL_EN, BRCM_BAND_ALL);
+		brcms_b_mhf(wlc_hw, MHF3, MHF3_ANTSEL_MODE, 0,
+			     BRCM_BAND_ALL);
+
+		/* Configure the desired clock to be 4Mhz */
+		brcms_b_write_shm(wlc_hw, M_ANTSEL_CLKDIV,
+				   ANTSEL_CLKDIV_4MHZ);
+	}
+
+	/* gpio 9 controls the PA.  ucode is responsible for wiggling out and oe */
+	if (wlc_hw->boardflags & BFL_PACTRL)
+		gm |= gc |= BOARD_GPIO_PACTRL;
+
+	/* apply to gpiocontrol register */
+	ai_gpiocontrol(wlc_hw->sih, gm, gc, GPIO_DRV_PRIORITY);
+}
+
+static void brcms_ucode_download(struct brcms_hardware *wlc_hw)
+{
+	struct brcms_c_info *wlc;
+	wlc = wlc_hw->wlc;
+
+	if (wlc_hw->ucode_loaded)
+		return;
+
+	if (D11REV_IS(wlc_hw->corerev, 23)) {
+		if (BRCMS_ISNPHY(wlc_hw->band)) {
+			brcms_ucode_write(wlc_hw, bcm43xx_16_mimo,
+					bcm43xx_16_mimosz);
+			wlc_hw->ucode_loaded = true;
+		} else
+			wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in "
+				  "corerev %d\n",
+				  __func__, wlc_hw->unit, wlc_hw->corerev);
+	} else if (D11REV_IS(wlc_hw->corerev, 24)) {
+		if (BRCMS_ISLCNPHY(wlc_hw->band)) {
+			brcms_ucode_write(wlc_hw, bcm43xx_24_lcn,
+					bcm43xx_24_lcnsz);
+			wlc_hw->ucode_loaded = true;
+		} else {
+			wiphy_err(wlc->wiphy, "%s: wl%d: unsupported phy in "
+				  "corerev %d\n",
+				  __func__, wlc_hw->unit, wlc_hw->corerev);
+		}
+	}
+}
+
+static void brcms_ucode_write(struct brcms_hardware *wlc_hw, const u32 ucode[],
+			      const uint nbytes) {
+	d11regs_t *regs = wlc_hw->regs;
+	uint i;
+	uint count;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	count = (nbytes / sizeof(u32));
+
+	W_REG(&regs->objaddr, (OBJADDR_AUTO_INC | OBJADDR_UCM_SEL));
+	(void)R_REG(&regs->objaddr);
+	for (i = 0; i < count; i++)
+		W_REG(&regs->objdata, ucode[i]);
+}
+
+static void brcms_c_write_inits(struct brcms_hardware *wlc_hw,
+			    const struct d11init *inits)
+{
+	int i;
+	volatile u8 *base;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	base = (volatile u8 *)wlc_hw->regs;
+
+	for (i = 0; inits[i].addr != 0xffff; i++) {
+		if (inits[i].size == 2)
+			W_REG((u16 *)(base + inits[i].addr),
+			      inits[i].value);
+		else if (inits[i].size == 4)
+			W_REG((u32 *)(base + inits[i].addr),
+			      inits[i].value);
+	}
+}
+
+static void brcms_c_ucode_txant_set(struct brcms_hardware *wlc_hw)
+{
+	u16 phyctl;
+	u16 phytxant = wlc_hw->bmac_phytxant;
+	u16 mask = PHY_TXC_ANT_MASK;
+
+	/* set the Probe Response frame phy control word */
+	phyctl = brcms_b_read_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS);
+	phyctl = (phyctl & ~mask) | phytxant;
+	brcms_b_write_shm(wlc_hw, M_CTXPRS_BLK + C_CTX_PCTLWD_POS, phyctl);
+
+	/* set the Response (ACK/CTS) frame phy control word */
+	phyctl = brcms_b_read_shm(wlc_hw, M_RSP_PCTLWD);
+	phyctl = (phyctl & ~mask) | phytxant;
+	brcms_b_write_shm(wlc_hw, M_RSP_PCTLWD, phyctl);
+}
+
+void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant)
+{
+	/* update sw state */
+	wlc_hw->bmac_phytxant = phytxant;
+
+	/* push to ucode if up */
+	if (!wlc_hw->up)
+		return;
+	brcms_c_ucode_txant_set(wlc_hw);
+
+}
+
+u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw)
+{
+	return (u16) wlc_hw->wlc->stf->txant;
+}
+
+void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw, u8 antsel_type)
+{
+	wlc_hw->antsel_type = antsel_type;
+
+	/* Update the antsel type for phy module to use */
+	wlc_phy_antsel_type_set(wlc_hw->band->pi, antsel_type);
+}
+
+void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw)
+{
+	bool fatal = false;
+	uint unit;
+	uint intstatus, idx;
+	d11regs_t *regs = wlc_hw->regs;
+	struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+
+	unit = wlc_hw->unit;
+
+	for (idx = 0; idx < NFIFO; idx++) {
+		/* read intstatus register and ignore any non-error bits */
+		intstatus =
+		    R_REG(&regs->intctrlregs[idx].intstatus) & I_ERRORS;
+		if (!intstatus)
+			continue;
+
+		BCMMSG(wlc_hw->wlc->wiphy, "wl%d: intstatus%d 0x%x\n",
+			unit, idx, intstatus);
+
+		if (intstatus & I_RO) {
+			wiphy_err(wiphy, "wl%d: fifo %d: receive fifo "
+				  "overflow\n", unit, idx);
+			fatal = true;
+		}
+
+		if (intstatus & I_PC) {
+			wiphy_err(wiphy, "wl%d: fifo %d: descriptor error\n",
+				 unit, idx);
+			fatal = true;
+		}
+
+		if (intstatus & I_PD) {
+			wiphy_err(wiphy, "wl%d: fifo %d: data error\n", unit,
+				  idx);
+			fatal = true;
+		}
+
+		if (intstatus & I_DE) {
+			wiphy_err(wiphy, "wl%d: fifo %d: descriptor protocol "
+				  "error\n", unit, idx);
+			fatal = true;
+		}
+
+		if (intstatus & I_RU) {
+			wiphy_err(wiphy, "wl%d: fifo %d: receive descriptor "
+				  "underflow\n", idx, unit);
+		}
+
+		if (intstatus & I_XU) {
+			wiphy_err(wiphy, "wl%d: fifo %d: transmit fifo "
+				  "underflow\n", idx, unit);
+			fatal = true;
+		}
+
+		if (fatal) {
+			brcms_c_fatal_error(wlc_hw->wlc);	/* big hammer */
+			break;
+		} else
+			W_REG(&regs->intctrlregs[idx].intstatus,
+			      intstatus);
+	}
+}
+
+void brcms_c_intrson(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	wlc->macintmask = wlc->defmacintmask;
+	W_REG(&wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+/* callback for siutils.c, which has only wlc handler, no wl
+ * they both check up, not only because there is no need to off/restore d11 interrupt
+ *  but also because per-port code may require sync with valid interrupt.
+ */
+
+static u32 brcms_c_wlintrsoff(struct brcms_c_info *wlc)
+{
+	if (!wlc->hw->up)
+		return 0;
+
+	return brcms_intrsoff(wlc->wl);
+}
+
+static void brcms_c_wlintrsrestore(struct brcms_c_info *wlc, u32 macintmask)
+{
+	if (!wlc->hw->up)
+		return;
+
+	brcms_intrsrestore(wlc->wl, macintmask);
+}
+
+u32 brcms_c_intrsoff(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	u32 macintmask;
+
+	if (!wlc_hw->clk)
+		return 0;
+
+	macintmask = wlc->macintmask;	/* isr can still happen */
+
+	W_REG(&wlc_hw->regs->macintmask, 0);
+	(void)R_REG(&wlc_hw->regs->macintmask);	/* sync readback */
+	udelay(1);		/* ensure int line is no longer driven */
+	wlc->macintmask = 0;
+
+	/* return previous macintmask; resolve race between us and our isr */
+	return wlc->macintstatus ? 0 : macintmask;
+}
+
+void brcms_c_intrsrestore(struct brcms_c_info *wlc, u32 macintmask)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	if (!wlc_hw->clk)
+		return;
+
+	wlc->macintmask = macintmask;
+	W_REG(&wlc_hw->regs->macintmask, wlc->macintmask);
+}
+
+static void brcms_b_mute(struct brcms_hardware *wlc_hw, bool on, mbool flags)
+{
+	u8 null_ether_addr[ETH_ALEN] = {0, 0, 0, 0, 0, 0};
+
+	if (on) {
+		/* suspend tx fifos */
+		brcms_b_tx_fifo_suspend(wlc_hw, TX_DATA_FIFO);
+		brcms_b_tx_fifo_suspend(wlc_hw, TX_CTL_FIFO);
+		brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_BK_FIFO);
+		brcms_b_tx_fifo_suspend(wlc_hw, TX_AC_VI_FIFO);
+
+		/* zero the address match register so we do not send ACKs */
+		brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+				       null_ether_addr);
+	} else {
+		/* resume tx fifos */
+		if (!wlc_hw->wlc->tx_suspended) {
+			brcms_b_tx_fifo_resume(wlc_hw, TX_DATA_FIFO);
+		}
+		brcms_b_tx_fifo_resume(wlc_hw, TX_CTL_FIFO);
+		brcms_b_tx_fifo_resume(wlc_hw, TX_AC_BK_FIFO);
+		brcms_b_tx_fifo_resume(wlc_hw, TX_AC_VI_FIFO);
+
+		/* Restore address */
+		brcms_b_set_addrmatch(wlc_hw, RCM_MAC_OFFSET,
+				       wlc_hw->etheraddr);
+	}
+
+	wlc_phy_mute_upd(wlc_hw->band->pi, on, flags);
+
+	if (on)
+		brcms_c_ucode_mute_override_set(wlc_hw);
+	else
+		brcms_c_ucode_mute_override_clear(wlc_hw);
+}
+
+int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
+			   uint *blocks)
+{
+	if (fifo >= NFIFO)
+		return -EINVAL;
+
+	*blocks = wlc_hw->xmtfifo_sz[fifo];
+
+	return 0;
+}
+
+/* brcms_b_tx_fifo_suspended:
+ * Check the MAC's tx suspend status for a tx fifo.
+ *
+ * When the MAC acknowledges a tx suspend, it indicates that no more
+ * packets will be transmitted out the radio. This is independent of
+ * DMA channel suspension---the DMA may have finished suspending, or may still
+ * be pulling data into a tx fifo, by the time the MAC acks the suspend
+ * request.
+ */
+static bool brcms_b_tx_fifo_suspended(struct brcms_hardware *wlc_hw,
+				      uint tx_fifo)
+{
+	/* check that a suspend has been requested and is no longer pending */
+
+	/*
+	 * for DMA mode, the suspend request is set in xmtcontrol of the DMA engine,
+	 * and the tx fifo suspend at the lower end of the MAC is acknowledged in the
+	 * chnstatus register.
+	 * The tx fifo suspend completion is independent of the DMA suspend completion and
+	 *   may be acked before or after the DMA is suspended.
+	 */
+	if (dma_txsuspended(wlc_hw->di[tx_fifo]) &&
+	    (R_REG(&wlc_hw->regs->chnstatus) &
+	     (1 << tx_fifo)) == 0)
+		return true;
+
+	return false;
+}
+
+static void brcms_b_tx_fifo_suspend(struct brcms_hardware *wlc_hw,
+				    uint tx_fifo)
+{
+	u8 fifo = 1 << tx_fifo;
+
+	/* Two clients of this code, 11h Quiet period and scanning. */
+
+	/* only suspend if not already suspended */
+	if ((wlc_hw->suspended_fifos & fifo) == fifo)
+		return;
+
+	/* force the core awake only if not already */
+	if (wlc_hw->suspended_fifos == 0)
+		brcms_c_ucode_wake_override_set(wlc_hw,
+						BRCMS_WAKE_OVERRIDE_TXFIFO);
+
+	wlc_hw->suspended_fifos |= fifo;
+
+	if (wlc_hw->di[tx_fifo]) {
+		/* Suspending AMPDU transmissions in the middle can cause underflow
+		 * which may result in mismatch between ucode and driver
+		 * so suspend the mac before suspending the FIFO
+		 */
+		if (BRCMS_PHY_11N_CAP(wlc_hw->band))
+			brcms_c_suspend_mac_and_wait(wlc_hw->wlc);
+
+		dma_txsuspend(wlc_hw->di[tx_fifo]);
+
+		if (BRCMS_PHY_11N_CAP(wlc_hw->band))
+			brcms_c_enable_mac(wlc_hw->wlc);
+	}
+}
+
+static void brcms_b_tx_fifo_resume(struct brcms_hardware *wlc_hw,
+				   uint tx_fifo)
+{
+	/* BMAC_NOTE: BRCMS_TX_FIFO_ENAB is done in brcms_c_dpc() for DMA case
+	 * but need to be done here for PIO otherwise the watchdog will catch
+	 * the inconsistency and fire
+	 */
+	/* Two clients of this code, 11h Quiet period and scanning. */
+	if (wlc_hw->di[tx_fifo])
+		dma_txresume(wlc_hw->di[tx_fifo]);
+
+	/* allow core to sleep again */
+	if (wlc_hw->suspended_fifos == 0)
+		return;
+	else {
+		wlc_hw->suspended_fifos &= ~(1 << tx_fifo);
+		if (wlc_hw->suspended_fifos == 0)
+			brcms_c_ucode_wake_override_clear(wlc_hw,
+						BRCMS_WAKE_OVERRIDE_TXFIFO);
+	}
+}
+
+/*
+ * Read and clear macintmask and macintstatus and intstatus registers.
+ * This routine should be called with interrupts off
+ * Return:
+ *   -1 if DEVICEREMOVED(wlc) evaluates to true;
+ *   0 if the interrupt is not for us, or we are in some special cases;
+ *   device interrupt status bits otherwise.
+ */
+static inline u32 wlc_intstatus(struct brcms_c_info *wlc, bool in_isr)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs = wlc_hw->regs;
+	u32 macintstatus;
+
+	/* macintstatus includes a DMA interrupt summary bit */
+	macintstatus = R_REG(&regs->macintstatus);
+
+	BCMMSG(wlc->wiphy, "wl%d: macintstatus: 0x%x\n", wlc_hw->unit,
+		 macintstatus);
+
+	/* detect cardbus removed, in power down(suspend) and in reset */
+	if (DEVICEREMOVED(wlc))
+		return -1;
+
+	/* DEVICEREMOVED succeeds even when the core is still resetting,
+	 * handle that case here.
+	 */
+	if (macintstatus == 0xffffffff)
+		return 0;
+
+	/* defer unsolicited interrupts */
+	macintstatus &= (in_isr ? wlc->macintmask : wlc->defmacintmask);
+
+	/* if not for us */
+	if (macintstatus == 0)
+		return 0;
+
+	/* interrupts are already turned off for CFE build
+	 * Caution: For CFE Turning off the interrupts again has some undesired
+	 * consequences
+	 */
+	/* turn off the interrupts */
+	W_REG(&regs->macintmask, 0);
+	(void)R_REG(&regs->macintmask);	/* sync readback */
+	wlc->macintmask = 0;
+
+	/* clear device interrupts */
+	W_REG(&regs->macintstatus, macintstatus);
+
+	/* MI_DMAINT is indication of non-zero intstatus */
+	if (macintstatus & MI_DMAINT) {
+		/*
+		 * only fifo interrupt enabled is I_RI in
+		 * RX_FIFO. If MI_DMAINT is set, assume it
+		 * is set and clear the interrupt.
+		 */
+		W_REG(&regs->intctrlregs[RX_FIFO].intstatus,
+		      DEF_RXINTMASK);
+	}
+
+	return macintstatus;
+}
+
+/* Update wlc->macintstatus and wlc->intstatus[]. */
+/* Return true if they are updated successfully. false otherwise */
+bool brcms_c_intrsupd(struct brcms_c_info *wlc)
+{
+	u32 macintstatus;
+
+	/* read and clear macintstatus and intstatus registers */
+	macintstatus = wlc_intstatus(wlc, false);
+
+	/* device is removed */
+	if (macintstatus == 0xffffffff)
+		return false;
+
+	/* update interrupt status in software */
+	wlc->macintstatus |= macintstatus;
+
+	return true;
+}
+
+/*
+ * First-level interrupt processing.
+ * Return true if this was our interrupt, false otherwise.
+ * *wantdpc will be set to true if further brcms_c_dpc() processing is required,
+ * false otherwise.
+ */
+bool brcms_c_isr(struct brcms_c_info *wlc, bool *wantdpc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	u32 macintstatus;
+
+	*wantdpc = false;
+
+	if (!wlc_hw->up || !wlc->macintmask)
+		return false;
+
+	/* read and clear macintstatus and intstatus registers */
+	macintstatus = wlc_intstatus(wlc, true);
+
+	if (macintstatus == 0xffffffff)
+		wiphy_err(wlc->wiphy, "DEVICEREMOVED detected in the ISR code"
+			  " path\n");
+
+	/* it is not for us */
+	if (macintstatus == 0)
+		return false;
+
+	*wantdpc = true;
+
+	/* save interrupt status bits */
+	wlc->macintstatus = macintstatus;
+
+	return true;
+
+}
+
+static bool
+brcms_b_dotxstatus(struct brcms_hardware *wlc_hw, struct tx_status *txs,
+		   u32 s2)
+{
+	/* discard intermediate indications for ucode with one legitimate case:
+	 *   e.g. if "useRTS" is set. ucode did a successful rts/cts exchange, but the subsequent
+	 *   tx of DATA failed. so it will start rts/cts from the beginning (resetting the rts
+	 *   transmission count)
+	 */
+	if (!(txs->status & TX_STATUS_AMPDU)
+	    && (txs->status & TX_STATUS_INTERMEDIATE)) {
+		return false;
+	}
+
+	return brcms_c_dotxstatus(wlc_hw->wlc, txs, s2);
+}
+
+/* process tx completion events in BMAC
+ * Return true if more tx status need to be processed. false otherwise.
+ */
+static bool
+brcms_b_txstatus(struct brcms_hardware *wlc_hw, bool bound, bool *fatal)
+{
+	bool morepending = false;
+	struct brcms_c_info *wlc = wlc_hw->wlc;
+	d11regs_t *regs;
+	struct tx_status txstatus, *txs;
+	u32 s1, s2;
+	uint n = 0;
+	/*
+	 * Param 'max_tx_num' indicates max. # tx status to process before
+	 * break out.
+	 */
+	uint max_tx_num = bound ? wlc->pub->tunables->txsbnd : -1;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	txs = &txstatus;
+	regs = wlc_hw->regs;
+	while (!(*fatal)
+	       && (s1 = R_REG(&regs->frmtxstatus)) & TXS_V) {
+
+		if (s1 == 0xffffffff) {
+			wiphy_err(wlc->wiphy, "wl%d: %s: dead chip\n",
+				wlc_hw->unit, __func__);
+			return morepending;
+		}
+
+			s2 = R_REG(&regs->frmtxstatus2);
+
+		txs->status = s1 & TXS_STATUS_MASK;
+		txs->frameid = (s1 & TXS_FID_MASK) >> TXS_FID_SHIFT;
+		txs->sequence = s2 & TXS_SEQ_MASK;
+		txs->phyerr = (s2 & TXS_PTX_MASK) >> TXS_PTX_SHIFT;
+		txs->lasttxtime = 0;
+
+		*fatal = brcms_b_dotxstatus(wlc_hw, txs, s2);
+
+		/* !give others some time to run! */
+		if (++n >= max_tx_num)
+			break;
+	}
+
+	if (*fatal)
+		return 0;
+
+	if (n >= max_tx_num)
+		morepending = true;
+
+	if (!pktq_empty(&wlc->pkt_queue->q))
+		brcms_c_send_q(wlc);
+
+	return morepending;
+}
+
+void brcms_c_suspend_mac_and_wait(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs = wlc_hw->regs;
+	u32 mc, mi;
+	struct wiphy *wiphy = wlc->wiphy;
+
+	BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+		wlc_hw->band->bandunit);
+
+	/*
+	 * Track overlapping suspend requests
+	 */
+	wlc_hw->mac_suspend_depth++;
+	if (wlc_hw->mac_suspend_depth > 1)
+		return;
+
+	/* force the core awake */
+	brcms_c_ucode_wake_override_set(wlc_hw, BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+
+	mc = R_REG(&regs->maccontrol);
+
+	if (mc == 0xffffffff) {
+		wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+			  __func__);
+		brcms_down(wlc->wl);
+		return;
+	}
+	WARN_ON(mc & MCTL_PSM_JMP_0);
+	WARN_ON(!(mc & MCTL_PSM_RUN));
+	WARN_ON(!(mc & MCTL_EN_MAC));
+
+	mi = R_REG(&regs->macintstatus);
+	if (mi == 0xffffffff) {
+		wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+			  __func__);
+		brcms_down(wlc->wl);
+		return;
+	}
+	WARN_ON(mi & MI_MACSSPNDD);
+
+	brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, 0);
+
+	SPINWAIT(!(R_REG(&regs->macintstatus) & MI_MACSSPNDD),
+		 BRCMS_MAX_MAC_SUSPEND);
+
+	if (!(R_REG(&regs->macintstatus) & MI_MACSSPNDD)) {
+		wiphy_err(wiphy, "wl%d: wlc_suspend_mac_and_wait: waited %d uS"
+			  " and MI_MACSSPNDD is still not on.\n",
+			  wlc_hw->unit, BRCMS_MAX_MAC_SUSPEND);
+		wiphy_err(wiphy, "wl%d: psmdebug 0x%08x, phydebug 0x%08x, "
+			  "psm_brc 0x%04x\n", wlc_hw->unit,
+			  R_REG(&regs->psmdebug),
+			  R_REG(&regs->phydebug),
+			  R_REG(&regs->psm_brc));
+	}
+
+	mc = R_REG(&regs->maccontrol);
+	if (mc == 0xffffffff) {
+		wiphy_err(wiphy, "wl%d: %s: dead chip\n", wlc_hw->unit,
+			  __func__);
+		brcms_down(wlc->wl);
+		return;
+	}
+	WARN_ON(mc & MCTL_PSM_JMP_0);
+	WARN_ON(!(mc & MCTL_PSM_RUN));
+	WARN_ON(mc & MCTL_EN_MAC);
+}
+
+void brcms_c_enable_mac(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	d11regs_t *regs = wlc_hw->regs;
+	u32 mc, mi;
+
+	BCMMSG(wlc->wiphy, "wl%d: bandunit %d\n", wlc_hw->unit,
+		wlc->band->bandunit);
+
+	/*
+	 * Track overlapping suspend requests
+	 */
+	wlc_hw->mac_suspend_depth--;
+	if (wlc_hw->mac_suspend_depth > 0)
+		return;
+
+	mc = R_REG(&regs->maccontrol);
+	WARN_ON(mc & MCTL_PSM_JMP_0);
+	WARN_ON(mc & MCTL_EN_MAC);
+	WARN_ON(!(mc & MCTL_PSM_RUN));
+
+	brcms_b_mctrl(wlc_hw, MCTL_EN_MAC, MCTL_EN_MAC);
+	W_REG(&regs->macintstatus, MI_MACSSPNDD);
+
+	mc = R_REG(&regs->maccontrol);
+	WARN_ON(mc & MCTL_PSM_JMP_0);
+	WARN_ON(!(mc & MCTL_EN_MAC));
+	WARN_ON(!(mc & MCTL_PSM_RUN));
+
+	mi = R_REG(&regs->macintstatus);
+	WARN_ON(mi & MI_MACSSPNDD);
+
+	brcms_c_ucode_wake_override_clear(wlc_hw,
+					  BRCMS_WAKE_OVERRIDE_MACSUSPEND);
+}
+
+static void brcms_upd_ofdm_pctl1_table(struct brcms_hardware *wlc_hw)
+{
+	u8 rate;
+	u8 rates[8] = {
+		BRCM_RATE_6M, BRCM_RATE_9M, BRCM_RATE_12M, BRCM_RATE_18M,
+		BRCM_RATE_24M, BRCM_RATE_36M, BRCM_RATE_48M, BRCM_RATE_54M
+	};
+	u16 entry_ptr;
+	u16 pctl1;
+	uint i;
+
+	if (!BRCMS_PHY_11N_CAP(wlc_hw->band))
+		return;
+
+	/* walk the phy rate table and update the entries */
+	for (i = 0; i < ARRAY_SIZE(rates); i++) {
+		rate = rates[i];
+
+		entry_ptr = brcms_b_ofdm_ratetable_offset(wlc_hw, rate);
+
+		/* read the SHM Rate Table entry OFDM PCTL1 values */
+		pctl1 =
+		    brcms_b_read_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS);
+
+		/* modify the value */
+		pctl1 &= ~PHY_TXC1_MODE_MASK;
+		pctl1 |= (wlc_hw->hw_stf_ss_opmode << PHY_TXC1_MODE_SHIFT);
+
+		/* Update the SHM Rate Table entry OFDM PCTL1 values */
+		brcms_b_write_shm(wlc_hw, entry_ptr + M_RT_OFDM_PCTL1_POS,
+				   pctl1);
+	}
+}
+
+static u16 brcms_b_ofdm_ratetable_offset(struct brcms_hardware *wlc_hw,
+					 u8 rate)
+{
+	uint i;
+	u8 plcp_rate = 0;
+	struct plcp_signal_rate_lookup {
+		u8 rate;
+		u8 signal_rate;
+	};
+	/* OFDM RATE sub-field of PLCP SIGNAL field, per 802.11 sec 17.3.4.1 */
+	const struct plcp_signal_rate_lookup rate_lookup[] = {
+		{BRCM_RATE_6M, 0xB},
+		{BRCM_RATE_9M, 0xF},
+		{BRCM_RATE_12M, 0xA},
+		{BRCM_RATE_18M, 0xE},
+		{BRCM_RATE_24M, 0x9},
+		{BRCM_RATE_36M, 0xD},
+		{BRCM_RATE_48M, 0x8},
+		{BRCM_RATE_54M, 0xC}
+	};
+
+	for (i = 0; i < ARRAY_SIZE(rate_lookup); i++) {
+		if (rate == rate_lookup[i].rate) {
+			plcp_rate = rate_lookup[i].signal_rate;
+			break;
+		}
+	}
+
+	/* Find the SHM pointer to the rate table entry by looking in the
+	 * Direct-map Table
+	 */
+	return 2 * brcms_b_read_shm(wlc_hw, M_RT_DIRMAP_A + (plcp_rate * 2));
+}
+
+void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw, u8 stf_mode)
+{
+	wlc_hw->hw_stf_ss_opmode = stf_mode;
+
+	if (wlc_hw->clk)
+		brcms_upd_ofdm_pctl1_table(wlc_hw);
+}
+
+void
+brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr,
+		  u32 *tsf_h_ptr)
+{
+	d11regs_t *regs = wlc_hw->regs;
+
+	/* read the tsf timer low, then high to get an atomic read */
+	*tsf_l_ptr = R_REG(&regs->tsf_timerlow);
+	*tsf_h_ptr = R_REG(&regs->tsf_timerhigh);
+
+	return;
+}
+
+static bool brcms_b_validate_chip_access(struct brcms_hardware *wlc_hw)
+{
+	d11regs_t *regs;
+	u32 w, val;
+	struct wiphy *wiphy = wlc_hw->wlc->wiphy;
+
+	BCMMSG(wiphy, "wl%d\n", wlc_hw->unit);
+
+	regs = wlc_hw->regs;
+
+	/* Validate dchip register access */
+
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	w = R_REG(&regs->objdata);
+
+	/* Can we write and read back a 32bit register? */
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	W_REG(&regs->objdata, (u32) 0xaa5555aa);
+
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	val = R_REG(&regs->objdata);
+	if (val != (u32) 0xaa5555aa) {
+		wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, "
+			  "expected 0xaa5555aa\n", wlc_hw->unit, val);
+		return false;
+	}
+
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	W_REG(&regs->objdata, (u32) 0x55aaaa55);
+
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	val = R_REG(&regs->objdata);
+	if (val != (u32) 0x55aaaa55) {
+		wiphy_err(wiphy, "wl%d: validate_chip_access: SHM = 0x%x, "
+			  "expected 0x55aaaa55\n", wlc_hw->unit, val);
+		return false;
+	}
+
+	W_REG(&regs->objaddr, OBJADDR_SHM_SEL | 0);
+	(void)R_REG(&regs->objaddr);
+	W_REG(&regs->objdata, w);
+
+	/* clear CFPStart */
+	W_REG(&regs->tsf_cfpstart, 0);
+
+	w = R_REG(&regs->maccontrol);
+	if ((w != (MCTL_IHR_EN | MCTL_WAKE)) &&
+	    (w != (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE))) {
+		wiphy_err(wiphy, "wl%d: validate_chip_access: maccontrol = "
+			  "0x%x, expected 0x%x or 0x%x\n", wlc_hw->unit, w,
+			  (MCTL_IHR_EN | MCTL_WAKE),
+			  (MCTL_IHR_EN | MCTL_GMODE | MCTL_WAKE));
+		return false;
+	}
+
+	return true;
+}
+
+#define PHYPLL_WAIT_US	100000
+
+void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on)
+{
+	d11regs_t *regs;
+	u32 tmp;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	tmp = 0;
+	regs = wlc_hw->regs;
+
+	if (on) {
+		if ((wlc_hw->sih->chip == BCM4313_CHIP_ID)) {
+			OR_REG(&regs->clk_ctl_st,
+			       (CCS_ERSRC_REQ_HT | CCS_ERSRC_REQ_D11PLL |
+				CCS_ERSRC_REQ_PHYPLL));
+			SPINWAIT((R_REG(&regs->clk_ctl_st) &
+				  (CCS_ERSRC_AVAIL_HT)) != (CCS_ERSRC_AVAIL_HT),
+				 PHYPLL_WAIT_US);
+
+			tmp = R_REG(&regs->clk_ctl_st);
+			if ((tmp & (CCS_ERSRC_AVAIL_HT)) !=
+			    (CCS_ERSRC_AVAIL_HT)) {
+				wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on PHY"
+					  " PLL failed\n", __func__);
+			}
+		} else {
+			OR_REG(&regs->clk_ctl_st,
+			       (CCS_ERSRC_REQ_D11PLL | CCS_ERSRC_REQ_PHYPLL));
+			SPINWAIT((R_REG(&regs->clk_ctl_st) &
+				  (CCS_ERSRC_AVAIL_D11PLL |
+				   CCS_ERSRC_AVAIL_PHYPLL)) !=
+				 (CCS_ERSRC_AVAIL_D11PLL |
+				  CCS_ERSRC_AVAIL_PHYPLL), PHYPLL_WAIT_US);
+
+			tmp = R_REG(&regs->clk_ctl_st);
+			if ((tmp &
+			     (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL))
+			    !=
+			    (CCS_ERSRC_AVAIL_D11PLL | CCS_ERSRC_AVAIL_PHYPLL)) {
+				wiphy_err(wlc_hw->wlc->wiphy, "%s: turn on "
+					  "PHY PLL failed\n", __func__);
+			}
+		}
+	} else {
+		/* Since the PLL may be shared, other cores can still be requesting it;
+		 * so we'll deassert the request but not wait for status to comply.
+		 */
+		AND_REG(&regs->clk_ctl_st, ~CCS_ERSRC_REQ_PHYPLL);
+		tmp = R_REG(&regs->clk_ctl_st);
+	}
+}
+
+void brcms_c_coredisable(struct brcms_hardware *wlc_hw)
+{
+	bool dev_gone;
+
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d\n", wlc_hw->unit);
+
+	dev_gone = DEVICEREMOVED(wlc_hw->wlc);
+
+	if (dev_gone)
+		return;
+
+	if (wlc_hw->noreset)
+		return;
+
+	/* radio off */
+	wlc_phy_switch_radio(wlc_hw->band->pi, OFF);
+
+	/* turn off analog core */
+	wlc_phy_anacore(wlc_hw->band->pi, OFF);
+
+	/* turn off PHYPLL to save power */
+	brcms_b_core_phypll_ctl(wlc_hw, false);
+
+	/* No need to set wlc->pub->radio_active = OFF
+	 * because this function needs down capability and
+	 * radio_active is designed for BCMNODOWN.
+	 */
+
+	/* remove gpio controls */
+	if (wlc_hw->ucode_dbgsel)
+		ai_gpiocontrol(wlc_hw->sih, ~0, 0, GPIO_DRV_PRIORITY);
+
+	wlc_hw->clk = false;
+	ai_core_disable(wlc_hw->sih, 0);
+	wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+}
+
+/* power both the pll and external oscillator on/off */
+static void brcms_b_xtal(struct brcms_hardware *wlc_hw, bool want)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "wl%d: want %d\n", wlc_hw->unit, want);
+
+	/* dont power down if plldown is false or we must poll hw radio disable */
+	if (!want && wlc_hw->pllreq)
+		return;
+
+	if (wlc_hw->sih)
+		ai_clkctl_xtal(wlc_hw->sih, XTAL | PLL, want);
+
+	wlc_hw->sbclk = want;
+	if (!wlc_hw->sbclk) {
+		wlc_hw->clk = false;
+		if (wlc_hw->band && wlc_hw->band->pi)
+			wlc_phy_hw_clk_state_upd(wlc_hw->band->pi, false);
+	}
+}
+
+static void brcms_c_flushqueues(struct brcms_c_info *wlc)
+{
+	struct brcms_hardware *wlc_hw = wlc->hw;
+	uint i;
+
+	wlc->txpend16165war = 0;
+
+	/* free any posted tx packets */
+	for (i = 0; i < NFIFO; i++)
+		if (wlc_hw->di[i]) {
+			dma_txreclaim(wlc_hw->di[i], DMA_RANGE_ALL);
+			TXPKTPENDCLR(wlc, i);
+			BCMMSG(wlc->wiphy, "pktpend fifo %d clrd\n", i);
+		}
+
+	/* free any posted rx packets */
+	dma_rxreclaim(wlc_hw->di[RX_FIFO]);
+}
+
+u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset)
+{
+	return brcms_b_read_objmem(wlc_hw, offset, OBJADDR_SHM_SEL);
+}
+
+void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset, u16 v)
+{
+	brcms_b_write_objmem(wlc_hw, offset, v, OBJADDR_SHM_SEL);
+}
+
+static u16
+brcms_b_read_objmem(struct brcms_hardware *wlc_hw, uint offset, u32 sel)
+{
+	d11regs_t *regs = wlc_hw->regs;
+	volatile u16 *objdata_lo = (volatile u16 *)&regs->objdata;
+	volatile u16 *objdata_hi = objdata_lo + 1;
+	u16 v;
+
+	W_REG(&regs->objaddr, sel | (offset >> 2));
+	(void)R_REG(&regs->objaddr);
+	if (offset & 2) {
+		v = R_REG(objdata_hi);
+	} else {
+		v = R_REG(objdata_lo);
+	}
+
+	return v;
+}
+
+static void
+brcms_b_write_objmem(struct brcms_hardware *wlc_hw, uint offset, u16 v,
+		     u32 sel)
+{
+	d11regs_t *regs = wlc_hw->regs;
+	volatile u16 *objdata_lo = (volatile u16 *)&regs->objdata;
+	volatile u16 *objdata_hi = objdata_lo + 1;
+
+	W_REG(&regs->objaddr, sel | (offset >> 2));
+	(void)R_REG(&regs->objaddr);
+	if (offset & 2) {
+		W_REG(objdata_hi, v);
+	} else {
+		W_REG(objdata_lo, v);
+	}
+}
+
+/* Copy a buffer to shared memory of specified type .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw, uint offset,
+		      const void *buf, int len, u32 sel)
+{
+	u16 v;
+	const u8 *p = (const u8 *)buf;
+	int i;
+
+	if (len <= 0 || (offset & 1) || (len & 1))
+		return;
+
+	for (i = 0; i < len; i += 2) {
+		v = p[i] | (p[i + 1] << 8);
+		brcms_b_write_objmem(wlc_hw, offset + i, v, sel);
+	}
+}
+
+/* Copy a piece of shared memory of specified type to a buffer .
+ * SHM 'offset' needs to be an even address and
+ * Buffer length 'len' must be an even number of bytes
+ * 'sel' selects the type of memory
+ */
+void
+brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset, void *buf,
+			 int len, u32 sel)
+{
+	u16 v;
+	u8 *p = (u8 *) buf;
+	int i;
+
+	if (len <= 0 || (offset & 1) || (len & 1))
+		return;
+
+	for (i = 0; i < len; i += 2) {
+		v = brcms_b_read_objmem(wlc_hw, offset + i, sel);
+		p[i] = v & 0xFF;
+		p[i + 1] = (v >> 8) & 0xFF;
+	}
+}
+
+void brcms_b_copyfrom_vars(struct brcms_hardware *wlc_hw, char **buf,
+			   uint *len)
+{
+	BCMMSG(wlc_hw->wlc->wiphy, "nvram vars totlen=%d\n",
+		wlc_hw->vars_size);
+
+	*buf = wlc_hw->vars;
+	*len = wlc_hw->vars_size;
+}
+
+void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, u16 SRL, u16 LRL)
+{
+	wlc_hw->SRL = SRL;
+	wlc_hw->LRL = LRL;
+
+	/* write retry limit to SCR, shouldn't need to suspend */
+	if (wlc_hw->up) {
+		W_REG(&wlc_hw->regs->objaddr,
+		      OBJADDR_SCR_SEL | S_DOT11_SRC_LMT);
+		(void)R_REG(&wlc_hw->regs->objaddr);
+		W_REG(&wlc_hw->regs->objdata, wlc_hw->SRL);
+		W_REG(&wlc_hw->regs->objaddr,
+		      OBJADDR_SCR_SEL | S_DOT11_LRC_LMT);
+		(void)R_REG(&wlc_hw->regs->objaddr);
+		W_REG(&wlc_hw->regs->objdata, wlc_hw->LRL);
+	}
+}
+
+void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set, mbool req_bit)
+{
+	if (set) {
+		if (mboolisset(wlc_hw->pllreq, req_bit))
+			return;
+
+		mboolset(wlc_hw->pllreq, req_bit);
+
+		if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) {
+			if (!wlc_hw->sbclk) {
+				brcms_b_xtal(wlc_hw, ON);
+			}
+		}
+	} else {
+		if (!mboolisset(wlc_hw->pllreq, req_bit))
+			return;
+
+		mboolclr(wlc_hw->pllreq, req_bit);
+
+		if (mboolisset(wlc_hw->pllreq, BRCMS_PLLREQ_FLIP)) {
+			if (wlc_hw->sbclk) {
+				brcms_b_xtal(wlc_hw, OFF);
+			}
+		}
+	}
+
+	return;
+}
+
+u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate)
+{
+	u16 table_ptr;
+	u8 phy_rate, index;
+
+	/* get the phy specific rate encoding for the PLCP SIGNAL field */
+	if (IS_OFDM(rate))
+		table_ptr = M_RT_DIRMAP_A;
+	else
+		table_ptr = M_RT_DIRMAP_B;
+
+	/* for a given rate, the LS-nibble of the PLCP SIGNAL field is
+	 * the index into the rate table.
+	 */
+	phy_rate = rate_info[rate] & BRCMS_RATE_MASK;
+	index = phy_rate & 0xf;
+
+	/* Find the SHM pointer to the rate table entry by looking in the
+	 * Direct-map Table
+	 */
+	return 2 * brcms_b_read_shm(wlc_hw, table_ptr + (index * 2));
+}
+
+void brcms_b_antsel_set(struct brcms_hardware *wlc_hw, u32 antsel_avail)
+{
+	wlc_hw->antsel_avail = antsel_avail;
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/bmac.h b/drivers/net/wireless/brcm80211/brcmsmac/bmac.h
new file mode 100644
index 0000000..3c9ad4f
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/bmac.h
@@ -0,0 +1,174 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#ifndef _BRCM_BOTTOM_MAC_H_
+#define _BRCM_BOTTOM_MAC_H_
+
+#include <brcmu_wifi.h>
+#include "types.h"
+
+/* dup state between BMAC(struct brcms_hardware) and HIGH(struct brcms_c_info)
+   driver */
+struct brcms_b_state {
+	u32 machwcap;	/* mac hw capibility */
+	u32 preamble_ovr;	/* preamble override */
+};
+
+enum {
+	IOV_BMAC_DIAG,
+	IOV_BMAC_SBGPIOTIMERVAL,
+	IOV_BMAC_SBGPIOOUT,
+	IOV_BMAC_CCGPIOCTRL,	/* CC GPIOCTRL REG */
+	IOV_BMAC_CCGPIOOUT,	/* CC GPIOOUT REG */
+	IOV_BMAC_CCGPIOOUTEN,	/* CC GPIOOUTEN REG */
+	IOV_BMAC_CCGPIOIN,	/* CC GPIOIN REG */
+	IOV_BMAC_WPSGPIO,	/* WPS push button GPIO pin */
+	IOV_BMAC_OTPDUMP,
+	IOV_BMAC_OTPSTAT,
+	IOV_BMAC_PCIEASPM,	/* obfuscation clkreq/aspm control */
+	IOV_BMAC_PCIEADVCORRMASK,	/* advanced correctable error mask */
+	IOV_BMAC_PCIECLKREQ,	/* PCIE 1.1 clockreq enab support */
+	IOV_BMAC_PCIELCREG,	/* PCIE LCREG */
+	IOV_BMAC_SBGPIOTIMERMASK,
+	IOV_BMAC_RFDISABLEDLY,
+	IOV_BMAC_PCIEREG,	/* PCIE REG */
+	IOV_BMAC_PCICFGREG,	/* PCI Config register */
+	IOV_BMAC_PCIESERDESREG,	/* PCIE SERDES REG (dev, 0}offset) */
+	IOV_BMAC_PCIEGPIOOUT,	/* PCIEOUT REG */
+	IOV_BMAC_PCIEGPIOOUTEN,	/* PCIEOUTEN REG */
+	IOV_BMAC_PCIECLKREQENCTRL,	/* clkreqenctrl REG (PCIE REV > 6.0 */
+	IOV_BMAC_DMALPBK,
+	IOV_BMAC_CCREG,
+	IOV_BMAC_COREREG,
+	IOV_BMAC_SDCIS,
+	IOV_BMAC_SDIO_DRIVE,
+	IOV_BMAC_OTPW,
+	IOV_BMAC_NVOTPW,
+	IOV_BMAC_SROM,
+	IOV_BMAC_SRCRC,
+	IOV_BMAC_CIS_SOURCE,
+	IOV_BMAC_CISVAR,
+	IOV_BMAC_OTPLOCK,
+	IOV_BMAC_OTP_CHIPID,
+	IOV_BMAC_CUSTOMVAR1,
+	IOV_BMAC_BOARDFLAGS,
+	IOV_BMAC_BOARDFLAGS2,
+	IOV_BMAC_WPSLED,
+	IOV_BMAC_NVRAM_SOURCE,
+	IOV_BMAC_OTP_RAW_READ,
+	IOV_BMAC_LAST
+};
+
+extern int brcms_b_attach(struct brcms_c_info *wlc, u16 vendor, u16 device,
+			   uint unit, bool piomode, void *regsva, uint bustype,
+			   void *btparam);
+extern int brcms_b_detach(struct brcms_c_info *wlc);
+extern void brcms_b_watchdog(void *arg);
+
+/* up/down, reset, clk */
+extern void brcms_b_copyto_objmem(struct brcms_hardware *wlc_hw,
+				   uint offset, const void *buf, int len,
+				   u32 sel);
+extern void brcms_b_copyfrom_objmem(struct brcms_hardware *wlc_hw, uint offset,
+				     void *buf, int len, u32 sel);
+#define brcms_b_copyfrom_shm(wlc_hw, offset, buf, len)                 \
+	brcms_b_copyfrom_objmem(wlc_hw, offset, buf, len, OBJADDR_SHM_SEL)
+#define brcms_b_copyto_shm(wlc_hw, offset, buf, len)                   \
+	brcms_b_copyto_objmem(wlc_hw, offset, buf, len, OBJADDR_SHM_SEL)
+
+extern void brcms_b_core_phypll_reset(struct brcms_hardware *wlc_hw);
+extern void brcms_b_core_phypll_ctl(struct brcms_hardware *wlc_hw, bool on);
+extern void brcms_b_phyclk_fgc(struct brcms_hardware *wlc_hw, bool clk);
+extern void brcms_b_macphyclk_set(struct brcms_hardware *wlc_hw, bool clk);
+extern void brcms_b_phy_reset(struct brcms_hardware *wlc_hw);
+extern void brcms_b_corereset(struct brcms_hardware *wlc_hw, u32 flags);
+extern void brcms_b_reset(struct brcms_hardware *wlc_hw);
+extern void brcms_b_init(struct brcms_hardware *wlc_hw, chanspec_t chanspec,
+			  bool mute);
+extern int brcms_b_up_prep(struct brcms_hardware *wlc_hw);
+extern int brcms_b_up_finish(struct brcms_hardware *wlc_hw);
+extern int brcms_b_bmac_down_prep(struct brcms_hardware *wlc_hw);
+extern int brcms_b_down_finish(struct brcms_hardware *wlc_hw);
+extern void brcms_b_switch_macfreq(struct brcms_hardware *wlc_hw, u8 spurmode);
+
+/* chanspec, ucode interface */
+extern void brcms_b_set_chanspec(struct brcms_hardware *wlc_hw,
+				  chanspec_t chanspec,
+				  bool mute, struct txpwr_limits *txpwr);
+
+extern int brcms_b_xmtfifo_sz_get(struct brcms_hardware *wlc_hw, uint fifo,
+				   uint *blocks);
+extern void brcms_b_mhf(struct brcms_hardware *wlc_hw, u8 idx, u16 mask,
+			 u16 val, int bands);
+extern void brcms_b_mctrl(struct brcms_hardware *wlc_hw, u32 mask, u32 val);
+extern u16 brcms_b_mhf_get(struct brcms_hardware *wlc_hw, u8 idx, int bands);
+extern void brcms_b_txant_set(struct brcms_hardware *wlc_hw, u16 phytxant);
+extern u16 brcms_b_get_txant(struct brcms_hardware *wlc_hw);
+extern void brcms_b_antsel_type_set(struct brcms_hardware *wlc_hw,
+				     u8 antsel_type);
+extern int brcms_b_state_get(struct brcms_hardware *wlc_hw,
+			      struct brcms_b_state *state);
+extern void brcms_b_write_shm(struct brcms_hardware *wlc_hw, uint offset,
+			      u16 v);
+extern u16 brcms_b_read_shm(struct brcms_hardware *wlc_hw, uint offset);
+extern void brcms_b_write_template_ram(struct brcms_hardware *wlc_hw,
+				       int offset, int len, void *buf);
+extern void brcms_b_copyfrom_vars(struct brcms_hardware *wlc_hw, char **buf,
+				   uint *len);
+
+extern void brcms_b_hw_etheraddr(struct brcms_hardware *wlc_hw,
+				  u8 *ea);
+
+extern bool brcms_b_radio_read_hwdisabled(struct brcms_hardware *wlc_hw);
+extern void brcms_b_set_shortslot(struct brcms_hardware *wlc_hw,
+				  bool shortslot);
+extern void brcms_b_band_stf_ss_set(struct brcms_hardware *wlc_hw,
+				    u8 stf_mode);
+
+extern void brcms_b_wait_for_wake(struct brcms_hardware *wlc_hw);
+
+extern void brcms_c_ucode_wake_override_set(struct brcms_hardware *wlc_hw,
+					u32 override_bit);
+extern void brcms_c_ucode_wake_override_clear(struct brcms_hardware *wlc_hw,
+					  u32 override_bit);
+
+extern void brcms_b_set_addrmatch(struct brcms_hardware *wlc_hw,
+				   int match_reg_offset,
+				   const u8 *addr);
+extern void brcms_b_write_hw_bcntemplates(struct brcms_hardware *wlc_hw,
+					   void *bcn, int len, bool both);
+
+extern void brcms_b_read_tsf(struct brcms_hardware *wlc_hw, u32 *tsf_l_ptr,
+			      u32 *tsf_h_ptr);
+extern void brcms_b_set_cwmin(struct brcms_hardware *wlc_hw, u16 newmin);
+extern void brcms_b_set_cwmax(struct brcms_hardware *wlc_hw, u16 newmax);
+
+extern void brcms_b_retrylimit_upd(struct brcms_hardware *wlc_hw, u16 SRL,
+				    u16 LRL);
+
+extern void brcms_b_fifoerrors(struct brcms_hardware *wlc_hw);
+
+
+/* API for BMAC driver (e.g. wlc_phy.c etc) */
+
+extern void brcms_b_bw_set(struct brcms_hardware *wlc_hw, u16 bw);
+extern void brcms_b_pllreq(struct brcms_hardware *wlc_hw, bool set,
+			    mbool req_bit);
+extern void brcms_b_hw_up(struct brcms_hardware *wlc_hw);
+extern u16 brcms_b_rate_shm_offset(struct brcms_hardware *wlc_hw, u8 rate);
+extern void brcms_b_antsel_set(struct brcms_hardware *wlc_hw,
+			       u32 antsel_avail);
+
+#endif /* _BRCM_BOTTOM_MAC_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.c b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
new file mode 100644
index 0000000..f59693e
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.c
@@ -0,0 +1,1559 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/types.h>
+#include <net/mac80211.h>
+
+#include <defs.h>
+#include "pub.h"
+#include "phy/phy_hal.h"
+#include "bmac.h"
+#include "main.h"
+#include "stf.h"
+#include "channel.h"
+
+#define	VALID_CHANNEL20_DB(wlc, val) brcms_c_valid_channel20_db((wlc)->cmi, val)
+#define	VALID_CHANNEL20_IN_BAND(wlc, bandunit, val) \
+	brcms_c_valid_channel20_in_band((wlc)->cmi, bandunit, val)
+#define	VALID_CHANNEL20(wlc, val) brcms_c_valid_channel20((wlc)->cmi, val)
+
+struct brcms_cm_band {
+	u8 locale_flags;	/* struct locale_info flags */
+	chanvec_t valid_channels;	/* List of valid channels in the country */
+	const chanvec_t *restricted_channels;	/* List of restricted use channels */
+	const chanvec_t *radar_channels;	/* List of radar sensitive channels */
+	u8 PAD[8];
+};
+
+struct brcms_cm_info {
+	struct brcms_pub *pub;
+	struct brcms_c_info *wlc;
+	char srom_ccode[BRCM_CNTRY_BUF_SZ];	/* Country Code in SROM */
+	uint srom_regrev;	/* Regulatory Rev for the SROM ccode */
+	const struct country_info *country;	/* current country def */
+	char ccode[BRCM_CNTRY_BUF_SZ];	/* current internal Country Code */
+	uint regrev;		/* current Regulatory Revision */
+	char country_abbrev[BRCM_CNTRY_BUF_SZ];	/* current advertised ccode */
+	/* per-band state (one per phy/radio) */
+	struct brcms_cm_band bandstate[MAXBANDS];
+	/* quiet channels currently for radar sensitivity or 11h support */
+	chanvec_t quiet_channels;	/* channels on which we cannot transmit */
+};
+
+static int brcms_c_channels_init(struct brcms_cm_info *wlc_cm,
+			     const struct country_info *country);
+static void brcms_c_set_country_common(struct brcms_cm_info *wlc_cm,
+				   const char *country_abbrev,
+				   const char *ccode, uint regrev,
+				   const struct country_info *country);
+static int brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm,
+				   const char *ccode);
+static int brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm,
+				   const char *country_abbrev,
+				   const char *ccode, int regrev);
+static int brcms_c_country_aggregate_map(struct brcms_cm_info *wlc_cm,
+				const char *ccode,
+				char *mapped_ccode, uint *mapped_regrev);
+
+static const struct country_info *
+brcms_c_country_lookup_direct(const char *ccode, uint regrev);
+
+static const struct country_info *
+brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm,
+			const char *ccode, char *mapped_ccode,
+			uint *mapped_regrev);
+
+static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm);
+static void brcms_c_quiet_channels_reset(struct brcms_cm_info *wlc_cm);
+static bool brcms_c_quiet_chanspec(struct brcms_cm_info *wlc_cm,
+				   chanspec_t chspec);
+static bool brcms_c_valid_channel20_db(struct brcms_cm_info *wlc_cm, uint val);
+static bool brcms_c_valid_channel20_in_band(struct brcms_cm_info *wlc_cm,
+					    uint bandunit, uint val);
+static bool brcms_c_valid_channel20(struct brcms_cm_info *wlc_cm, uint val);
+
+static const struct country_info *
+brcms_c_country_lookup(struct brcms_c_info *wlc, const char *ccode);
+
+static void brcms_c_locale_get_channels(const struct locale_info *locale,
+				    chanvec_t *valid_channels);
+static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx);
+static const struct locale_info *brcms_c_get_locale_5g(u8 locale_idx);
+static bool brcms_c_japan(struct brcms_c_info *wlc);
+static bool brcms_c_japan_ccode(const char *ccode);
+static void brcms_c_channel_min_txpower_limits_with_local_constraint(
+	struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
+	u8 local_constraint_qdbm);
+static void brcms_c_locale_add_channels(chanvec_t *target,
+				    const chanvec_t *channels);
+static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx);
+static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx);
+
+/* QDB() macro takes a dB value and converts to a quarter dB value */
+#ifdef QDB
+#undef QDB
+#endif
+#define QDB(n) ((n) * BRCMS_TXPWR_DB_FACTOR)
+
+/* Regulatory Matrix Spreadsheet (CLM) MIMO v3.7.9 */
+
+/*
+ * Some common channel sets
+ */
+
+/* No channels */
+static const chanvec_t chanvec_none = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* All 2.4 GHz HW channels */
+const chanvec_t chanvec_all_2G = {
+	{0xfe, 0x7f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* All 5 GHz HW channels */
+const chanvec_t chanvec_all_5G = {
+	{0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x11, 0x11,
+	 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11,
+	 0x11, 0x11, 0x20, 0x22, 0x22, 0x00, 0x00, 0x11,
+	 0x11, 0x11, 0x11, 0x01}
+};
+
+/*
+ * Radar channel sets
+ */
+
+/* No radar */
+#define radar_set_none chanvec_none
+
+static const chanvec_t radar_set1 = {	/* Channels 52 - 64, 100 - 140 */
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,	/* 52 - 60 */
+	 0x01, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x11,	/* 64, 100 - 124 */
+	 0x11, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,	/* 128 - 140 */
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/*
+ * Restricted channel sets
+ */
+
+#define restricted_set_none chanvec_none
+
+/* Channels 34, 38, 42, 46 */
+static const chanvec_t restricted_set_japan_legacy = {
+	{0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 12, 13 */
+static const chanvec_t restricted_set_2g_short = {
+	{0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channel 165 */
+static const chanvec_t restricted_chan_165 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 36 - 48 & 149 - 165 */
+static const chanvec_t restricted_low_hi = {
+	{0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x20, 0x22, 0x22, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+/* Channels 12 - 14 */
+static const chanvec_t restricted_set_12_13_14 = {
+	{0x00, 0x70, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+#define  LOCALE_CHAN_01_11	 (1<<0)
+#define  LOCALE_CHAN_12_13	 (1<<1)
+#define  LOCALE_CHAN_14		 (1<<2)
+#define  LOCALE_SET_5G_LOW_JP1   (1<<3)	/* 34-48, step 2 */
+#define  LOCALE_SET_5G_LOW_JP2   (1<<4)	/* 34-46, step 4 */
+#define  LOCALE_SET_5G_LOW1      (1<<5)	/* 36-48, step 4 */
+#define  LOCALE_SET_5G_LOW2      (1<<6)	/* 52 */
+#define  LOCALE_SET_5G_LOW3      (1<<7)	/* 56-64, step 4 */
+#define  LOCALE_SET_5G_MID1      (1<<8)	/* 100-116, step 4 */
+#define  LOCALE_SET_5G_MID2	 (1<<9)	/* 120-124, step 4 */
+#define  LOCALE_SET_5G_MID3      (1<<10)	/* 128 */
+#define  LOCALE_SET_5G_HIGH1     (1<<11)	/* 132-140, step 4 */
+#define  LOCALE_SET_5G_HIGH2     (1<<12)	/* 149-161, step 4 */
+#define  LOCALE_SET_5G_HIGH3     (1<<13)	/* 165 */
+#define  LOCALE_CHAN_52_140_ALL  (1<<14)
+#define  LOCALE_SET_5G_HIGH4     (1<<15)	/* 184-216 */
+
+#define  LOCALE_CHAN_36_64       (LOCALE_SET_5G_LOW1 | LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
+#define  LOCALE_CHAN_52_64       (LOCALE_SET_5G_LOW2 | LOCALE_SET_5G_LOW3)
+#define  LOCALE_CHAN_100_124	 (LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2)
+#define  LOCALE_CHAN_100_140     \
+	(LOCALE_SET_5G_MID1 | LOCALE_SET_5G_MID2 | LOCALE_SET_5G_MID3 | LOCALE_SET_5G_HIGH1)
+#define  LOCALE_CHAN_149_165     (LOCALE_SET_5G_HIGH2 | LOCALE_SET_5G_HIGH3)
+#define  LOCALE_CHAN_184_216     LOCALE_SET_5G_HIGH4
+
+#define  LOCALE_CHAN_01_14	(LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13 | LOCALE_CHAN_14)
+
+#define  LOCALE_RADAR_SET_NONE		  0
+#define  LOCALE_RADAR_SET_1		  1
+
+#define  LOCALE_RESTRICTED_NONE		  0
+#define  LOCALE_RESTRICTED_SET_2G_SHORT   1
+#define  LOCALE_RESTRICTED_CHAN_165       2
+#define  LOCALE_CHAN_ALL_5G		  3
+#define  LOCALE_RESTRICTED_JAPAN_LEGACY   4
+#define  LOCALE_RESTRICTED_11D_2G	  5
+#define  LOCALE_RESTRICTED_11D_5G	  6
+#define  LOCALE_RESTRICTED_LOW_HI	  7
+#define  LOCALE_RESTRICTED_12_13_14	  8
+
+/* global memory to provide working buffer for expanded locale */
+
+static const chanvec_t *g_table_radar_set[] = {
+	&chanvec_none,
+	&radar_set1
+};
+
+static const chanvec_t *g_table_restricted_chan[] = {
+	&chanvec_none,		/* restricted_set_none */
+	&restricted_set_2g_short,
+	&restricted_chan_165,
+	&chanvec_all_5G,
+	&restricted_set_japan_legacy,
+	&chanvec_all_2G,	/* restricted_set_11d_2G */
+	&chanvec_all_5G,	/* restricted_set_11d_5G */
+	&restricted_low_hi,
+	&restricted_set_12_13_14
+};
+
+static const chanvec_t locale_2g_01_11 = {
+	{0xfe, 0x0f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_2g_12_13 = {
+	{0x00, 0x30, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_2g_14 = {
+	{0x00, 0x40, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW_JP1 = {
+	{0x00, 0x00, 0x00, 0x00, 0x54, 0x55, 0x01, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW_JP2 = {
+	{0x00, 0x00, 0x00, 0x00, 0x44, 0x44, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW1 = {
+	{0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x01, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW2 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_LOW3 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+	 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID1 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x10, 0x11, 0x11, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID2 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_MID3 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH1 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x10, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH2 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x20, 0x22, 0x02, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH3 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_52_140_ALL = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x11,
+	 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11, 0x11,
+	 0x11, 0x11, 0x00, 0x00, 0x20, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00}
+};
+
+static const chanvec_t locale_5g_HIGH4 = {
+	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+	 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x11,
+	 0x11, 0x11, 0x11, 0x11}
+};
+
+static const chanvec_t *g_table_locale_base[] = {
+	&locale_2g_01_11,
+	&locale_2g_12_13,
+	&locale_2g_14,
+	&locale_5g_LOW_JP1,
+	&locale_5g_LOW_JP2,
+	&locale_5g_LOW1,
+	&locale_5g_LOW2,
+	&locale_5g_LOW3,
+	&locale_5g_MID1,
+	&locale_5g_MID2,
+	&locale_5g_MID3,
+	&locale_5g_HIGH1,
+	&locale_5g_HIGH2,
+	&locale_5g_HIGH3,
+	&locale_5g_52_140_ALL,
+	&locale_5g_HIGH4
+};
+
+static void brcms_c_locale_add_channels(chanvec_t *target,
+				    const chanvec_t *channels)
+{
+	u8 i;
+	for (i = 0; i < sizeof(chanvec_t); i++) {
+		target->vec[i] |= channels->vec[i];
+	}
+}
+
+static void brcms_c_locale_get_channels(const struct locale_info *locale,
+				    chanvec_t *channels)
+{
+	u8 i;
+
+	memset(channels, 0, sizeof(chanvec_t));
+
+	for (i = 0; i < ARRAY_SIZE(g_table_locale_base); i++) {
+		if (locale->valid_channels & (1 << i)) {
+			brcms_c_locale_add_channels(channels,
+						g_table_locale_base[i]);
+		}
+	}
+}
+
+/*
+ * Locale Definitions - 2.4 GHz
+ */
+static const struct locale_info locale_i = {	/* locale i. channel 1 - 13 */
+	LOCALE_CHAN_01_11 | LOCALE_CHAN_12_13,
+	LOCALE_RADAR_SET_NONE,
+	LOCALE_RESTRICTED_SET_2G_SHORT,
+	{QDB(19), QDB(19), QDB(19),
+	 QDB(19), QDB(19), QDB(19)},
+	{20, 20, 20, 0},
+	BRCMS_EIRP
+};
+
+/*
+ * Locale Definitions - 5 GHz
+ */
+static const struct locale_info locale_11 = {
+	/* locale 11. channel 36 - 48, 52 - 64, 100 - 140, 149 - 165 */
+	LOCALE_CHAN_36_64 | LOCALE_CHAN_100_140 | LOCALE_CHAN_149_165,
+	LOCALE_RADAR_SET_1,
+	LOCALE_RESTRICTED_NONE,
+	{QDB(21), QDB(21), QDB(21), QDB(21), QDB(21)},
+	{23, 23, 23, 30, 30},
+	BRCMS_EIRP | BRCMS_DFS_EU
+};
+
+#define LOCALE_2G_IDX_i			0
+static const struct locale_info *g_locale_2g_table[] = {
+	&locale_i
+};
+
+#define LOCALE_5G_IDX_11	0
+static const struct locale_info *g_locale_5g_table[] = {
+	&locale_11
+};
+
+/*
+ * MIMO Locale Definitions - 2.4 GHz
+ */
+static const struct locale_mimo_info locale_bn = {
+	{QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+	 QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+	 QDB(13), QDB(13), QDB(13)},
+	{0, 0, QDB(13), QDB(13), QDB(13),
+	 QDB(13), QDB(13), QDB(13), QDB(13), QDB(13),
+	 QDB(13), 0, 0},
+	0
+};
+
+/* locale mimo 2g indexes */
+#define LOCALE_MIMO_IDX_bn			0
+
+static const struct locale_mimo_info *g_mimo_2g_table[] = {
+	&locale_bn
+};
+
+/*
+ * MIMO Locale Definitions - 5 GHz
+ */
+static const struct locale_mimo_info locale_11n = {
+	{ /* 12.5 dBm */ 50, 50, 50, QDB(15), QDB(15)},
+	{QDB(14), QDB(15), QDB(15), QDB(15), QDB(15)},
+	0
+};
+
+#define LOCALE_MIMO_IDX_11n			0
+static const struct locale_mimo_info *g_mimo_5g_table[] = {
+	&locale_11n
+};
+
+#ifdef LC
+#undef LC
+#endif
+#define LC(id)	LOCALE_MIMO_IDX_ ## id
+
+#ifdef LC_2G
+#undef LC_2G
+#endif
+#define LC_2G(id)	LOCALE_2G_IDX_ ## id
+
+#ifdef LC_5G
+#undef LC_5G
+#endif
+#define LC_5G(id)	LOCALE_5G_IDX_ ## id
+
+#define LOCALES(band2, band5, mimo2, mimo5)     {LC_2G(band2), LC_5G(band5), LC(mimo2), LC(mimo5)}
+
+static const struct {
+	char abbrev[BRCM_CNTRY_BUF_SZ];	/* country abbreviation */
+	struct country_info country;
+} cntry_locales[] = {
+	{
+	"X2", LOCALES(i, 11, bn, 11n)},	/* Worldwide RoW 2 */
+};
+
+#ifdef SUPPORT_40MHZ
+/* 20MHz channel info for 40MHz pairing support */
+struct chan20_info {
+	u8 sb;
+	u8 adj_sbs;
+};
+
+/* indicates adjacent channels that are allowed for a 40 Mhz channel and
+ * those that permitted by the HT
+ */
+struct chan20_info chan20_info[] = {
+	/* 11b/11g */
+/* 0 */ {1, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 1 */ {2, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 2 */ {3, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 3 */ {4, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 4 */ {5, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 5 */ {6, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 6 */ {7, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 7 */ {8, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 8 */ {9, (CH_UPPER_SB | CH_LOWER_SB | CH_EWA_VALID)},
+/* 9 */ {10, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 10 */ {11, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 11 */ {12, (CH_LOWER_SB)},
+/* 12 */ {13, (CH_LOWER_SB)},
+/* 13 */ {14, (CH_LOWER_SB)},
+
+/* 11a japan high */
+/* 14 */ {34, (CH_UPPER_SB)},
+/* 15 */ {38, (CH_LOWER_SB)},
+/* 16 */ {42, (CH_LOWER_SB)},
+/* 17 */ {46, (CH_LOWER_SB)},
+
+/* 11a usa low */
+/* 18 */ {36, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 19 */ {40, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 20 */ {44, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 21 */ {48, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 22 */ {52, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 23 */ {56, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 24 */ {60, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 25 */ {64, (CH_LOWER_SB | CH_EWA_VALID)},
+
+/* 11a Europe */
+/* 26 */ {100, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 27 */ {104, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 28 */ {108, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 29 */ {112, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 30 */ {116, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 31 */ {120, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 32 */ {124, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 33 */ {128, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 34 */ {132, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 35 */ {136, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 36 */ {140, (CH_LOWER_SB)},
+
+/* 11a usa high, ref5 only */
+/* The 0x80 bit in pdiv means these are REF5, other entries are REF20 */
+/* 37 */ {149, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 38 */ {153, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 39 */ {157, (CH_UPPER_SB | CH_EWA_VALID)},
+/* 40 */ {161, (CH_LOWER_SB | CH_EWA_VALID)},
+/* 41 */ {165, (CH_LOWER_SB)},
+
+/* 11a japan */
+/* 42 */ {184, (CH_UPPER_SB)},
+/* 43 */ {188, (CH_LOWER_SB)},
+/* 44 */ {192, (CH_UPPER_SB)},
+/* 45 */ {196, (CH_LOWER_SB)},
+/* 46 */ {200, (CH_UPPER_SB)},
+/* 47 */ {204, (CH_LOWER_SB)},
+/* 48 */ {208, (CH_UPPER_SB)},
+/* 49 */ {212, (CH_LOWER_SB)},
+/* 50 */ {216, (CH_LOWER_SB)}
+};
+#endif				/* SUPPORT_40MHZ */
+
+static const struct locale_info *brcms_c_get_locale_2g(u8 locale_idx)
+{
+	if (locale_idx >= ARRAY_SIZE(g_locale_2g_table)) {
+		return NULL; /* error condition */
+	}
+	return g_locale_2g_table[locale_idx];
+}
+
+static const struct locale_info *brcms_c_get_locale_5g(u8 locale_idx)
+{
+	if (locale_idx >= ARRAY_SIZE(g_locale_5g_table)) {
+		return NULL; /* error condition */
+	}
+	return g_locale_5g_table[locale_idx];
+}
+
+static const struct locale_mimo_info *brcms_c_get_mimo_2g(u8 locale_idx)
+{
+	if (locale_idx >= ARRAY_SIZE(g_mimo_2g_table)) {
+		return NULL;
+	}
+	return g_mimo_2g_table[locale_idx];
+}
+
+static const struct locale_mimo_info *brcms_c_get_mimo_5g(u8 locale_idx)
+{
+	if (locale_idx >= ARRAY_SIZE(g_mimo_5g_table)) {
+		return NULL;
+	}
+	return g_mimo_5g_table[locale_idx];
+}
+
+struct brcms_cm_info *brcms_c_channel_mgr_attach(struct brcms_c_info *wlc)
+{
+	struct brcms_cm_info *wlc_cm;
+	char country_abbrev[BRCM_CNTRY_BUF_SZ];
+	const struct country_info *country;
+	struct brcms_pub *pub = wlc->pub;
+	char *ccode;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+	wlc_cm = kzalloc(sizeof(struct brcms_cm_info), GFP_ATOMIC);
+	if (wlc_cm == NULL) {
+		wiphy_err(wlc->wiphy, "wl%d: %s: out of memory", pub->unit,
+			  __func__);
+		return NULL;
+	}
+	wlc_cm->pub = pub;
+	wlc_cm->wlc = wlc;
+	wlc->cmi = wlc_cm;
+
+	/* store the country code for passing up as a regulatory hint */
+	ccode = getvar(wlc->pub->vars, "ccode");
+	if (ccode) {
+		strncpy(wlc->pub->srom_ccode, ccode, BRCM_CNTRY_BUF_SZ - 1);
+	}
+
+	/* internal country information which must match regulatory constraints in firmware */
+	memset(country_abbrev, 0, BRCM_CNTRY_BUF_SZ);
+	strncpy(country_abbrev, "X2", sizeof(country_abbrev) - 1);
+	country = brcms_c_country_lookup(wlc, country_abbrev);
+
+	/* save default country for exiting 11d regulatory mode */
+	strncpy(wlc->country_default, country_abbrev, BRCM_CNTRY_BUF_SZ - 1);
+
+	/* initialize autocountry_default to driver default */
+	strncpy(wlc->autocountry_default, "X2", BRCM_CNTRY_BUF_SZ - 1);
+
+	brcms_c_set_countrycode(wlc_cm, country_abbrev);
+
+	return wlc_cm;
+}
+
+void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm)
+{
+	kfree(wlc_cm);
+}
+
+u8
+brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm,
+				     uint bandunit)
+{
+	return wlc_cm->bandstate[bandunit].locale_flags;
+}
+
+/* set the driver's current country and regulatory information using a country code
+ * as the source. Lookup built in country information found with the country code.
+ */
+static int
+brcms_c_set_countrycode(struct brcms_cm_info *wlc_cm, const char *ccode)
+{
+	char country_abbrev[BRCM_CNTRY_BUF_SZ];
+	strncpy(country_abbrev, ccode, BRCM_CNTRY_BUF_SZ);
+	return brcms_c_set_countrycode_rev(wlc_cm, country_abbrev, ccode, -1);
+}
+
+static int
+brcms_c_set_countrycode_rev(struct brcms_cm_info *wlc_cm,
+			const char *country_abbrev,
+			const char *ccode, int regrev)
+{
+	const struct country_info *country;
+	char mapped_ccode[BRCM_CNTRY_BUF_SZ];
+	uint mapped_regrev;
+
+	/* if regrev is -1, lookup the mapped country code,
+	 * otherwise use the ccode and regrev directly
+	 */
+	if (regrev == -1) {
+		/* map the country code to a built-in country code, regrev, and country_info */
+		country =
+		    brcms_c_countrycode_map(wlc_cm, ccode, mapped_ccode,
+					&mapped_regrev);
+	} else {
+		/* find the matching built-in country definition */
+		country = brcms_c_country_lookup_direct(ccode, regrev);
+		strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ);
+		mapped_regrev = regrev;
+	}
+
+	if (country == NULL)
+		return -EINVAL;
+
+	/* set the driver state for the country */
+	brcms_c_set_country_common(wlc_cm, country_abbrev, mapped_ccode,
+			       mapped_regrev, country);
+
+	return 0;
+}
+
+/* set the driver's current country and regulatory information using a country code
+ * as the source. Look up built in country information found with the country code.
+ */
+static void
+brcms_c_set_country_common(struct brcms_cm_info *wlc_cm,
+		       const char *country_abbrev,
+		       const char *ccode, uint regrev,
+		       const struct country_info *country)
+{
+	const struct locale_mimo_info *li_mimo;
+	const struct locale_info *locale;
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	char prev_country_abbrev[BRCM_CNTRY_BUF_SZ];
+
+	/* save current country state */
+	wlc_cm->country = country;
+
+	memset(&prev_country_abbrev, 0, BRCM_CNTRY_BUF_SZ);
+	strncpy(prev_country_abbrev, wlc_cm->country_abbrev,
+		BRCM_CNTRY_BUF_SZ - 1);
+
+	strncpy(wlc_cm->country_abbrev, country_abbrev, BRCM_CNTRY_BUF_SZ - 1);
+	strncpy(wlc_cm->ccode, ccode, BRCM_CNTRY_BUF_SZ - 1);
+	wlc_cm->regrev = regrev;
+
+	/* disable/restore nmode based on country regulations */
+	li_mimo = brcms_c_get_mimo_2g(country->locale_mimo_2G);
+	if (li_mimo && (li_mimo->flags & BRCMS_NO_MIMO)) {
+		brcms_c_set_nmode(wlc, OFF);
+		wlc->stf->no_cddstbc = true;
+	} else {
+		wlc->stf->no_cddstbc = false;
+		if (N_ENAB(wlc->pub) != wlc->protection->nmode_user)
+			brcms_c_set_nmode(wlc, wlc->protection->nmode_user);
+	}
+
+	brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_2G_INDEX]);
+	brcms_c_stf_ss_update(wlc, wlc->bandstate[BAND_5G_INDEX]);
+	/* set or restore gmode as required by regulatory */
+	locale = brcms_c_get_locale_2g(country->locale_2G);
+	if (locale && (locale->flags & BRCMS_NO_OFDM)) {
+		brcms_c_set_gmode(wlc, GMODE_LEGACY_B, false);
+	} else {
+		brcms_c_set_gmode(wlc, wlc->protection->gmode_user, false);
+	}
+
+	brcms_c_channels_init(wlc_cm, country);
+
+	return;
+}
+
+/* Lookup a country info structure from a null terminated country code
+ * The lookup is case sensitive.
+ */
+static const struct country_info *
+brcms_c_country_lookup(struct brcms_c_info *wlc, const char *ccode)
+{
+	const struct country_info *country;
+	char mapped_ccode[BRCM_CNTRY_BUF_SZ];
+	uint mapped_regrev;
+
+	/* map the country code to a built-in country code, regrev, and country_info struct */
+	country = brcms_c_countrycode_map(wlc->cmi, ccode, mapped_ccode,
+					  &mapped_regrev);
+
+	return country;
+}
+
+static const struct country_info *
+brcms_c_countrycode_map(struct brcms_cm_info *wlc_cm, const char *ccode,
+			char *mapped_ccode, uint *mapped_regrev)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	const struct country_info *country;
+	uint srom_regrev = wlc_cm->srom_regrev;
+	const char *srom_ccode = wlc_cm->srom_ccode;
+	int mapped;
+
+	/* check for currently supported ccode size */
+	if (strlen(ccode) > (BRCM_CNTRY_BUF_SZ - 1)) {
+		wiphy_err(wlc->wiphy, "wl%d: %s: ccode \"%s\" too long for "
+			  "match\n", wlc->pub->unit, __func__, ccode);
+		return NULL;
+	}
+
+	/* default mapping is the given ccode and regrev 0 */
+	strncpy(mapped_ccode, ccode, BRCM_CNTRY_BUF_SZ);
+	*mapped_regrev = 0;
+
+	/* If the desired country code matches the srom country code,
+	 * then the mapped country is the srom regulatory rev.
+	 * Otherwise look for an aggregate mapping.
+	 */
+	if (!strcmp(srom_ccode, ccode)) {
+		*mapped_regrev = srom_regrev;
+		mapped = 0;
+		wiphy_err(wlc->wiphy, "srom_code == ccode %s\n", __func__);
+	} else {
+		mapped =
+		    brcms_c_country_aggregate_map(wlc_cm, ccode, mapped_ccode,
+					      mapped_regrev);
+	}
+
+	/* find the matching built-in country definition */
+	country = brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev);
+
+	/* if there is not an exact rev match, default to rev zero */
+	if (country == NULL && *mapped_regrev != 0) {
+		*mapped_regrev = 0;
+		country =
+		    brcms_c_country_lookup_direct(mapped_ccode, *mapped_regrev);
+	}
+
+	return country;
+}
+
+static int
+brcms_c_country_aggregate_map(struct brcms_cm_info *wlc_cm, const char *ccode,
+			  char *mapped_ccode, uint *mapped_regrev)
+{
+	return false;
+}
+
+/* Lookup a country info structure from a null terminated country
+ * abbreviation and regrev directly with no translation.
+ */
+static const struct country_info *
+brcms_c_country_lookup_direct(const char *ccode, uint regrev)
+{
+	uint size, i;
+
+	/* Should just return 0 for single locale driver. */
+	/* Keep it this way in case we add more locales. (for now anyway) */
+
+	/* all other country def arrays are for regrev == 0, so if regrev is non-zero, fail */
+	if (regrev > 0)
+		return NULL;
+
+	/* find matched table entry from country code */
+	size = ARRAY_SIZE(cntry_locales);
+	for (i = 0; i < size; i++) {
+		if (strcmp(ccode, cntry_locales[i].abbrev) == 0) {
+			return &cntry_locales[i].country;
+		}
+	}
+	return NULL;
+}
+
+static int
+brcms_c_channels_init(struct brcms_cm_info *wlc_cm,
+		      const struct country_info *country)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	uint i, j;
+	struct brcms_band *band;
+	const struct locale_info *li;
+	chanvec_t sup_chan;
+	const struct locale_mimo_info *li_mimo;
+
+	band = wlc->band;
+	for (i = 0; i < NBANDS(wlc);
+	     i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
+
+		li = BAND_5G(band->bandtype) ?
+		    brcms_c_get_locale_5g(country->locale_5G) :
+		    brcms_c_get_locale_2g(country->locale_2G);
+		wlc_cm->bandstate[band->bandunit].locale_flags = li->flags;
+		li_mimo = BAND_5G(band->bandtype) ?
+		    brcms_c_get_mimo_5g(country->locale_mimo_5G) :
+		    brcms_c_get_mimo_2g(country->locale_mimo_2G);
+
+		/* merge the mimo non-mimo locale flags */
+		wlc_cm->bandstate[band->bandunit].locale_flags |=
+		    li_mimo->flags;
+
+		wlc_cm->bandstate[band->bandunit].restricted_channels =
+		    g_table_restricted_chan[li->restricted_channels];
+		wlc_cm->bandstate[band->bandunit].radar_channels =
+		    g_table_radar_set[li->radar_channels];
+
+		/* set the channel availability,
+		 * masking out the channels that may not be supported on this phy
+		 */
+		wlc_phy_chanspec_band_validch(band->pi, band->bandtype,
+					      &sup_chan);
+		brcms_c_locale_get_channels(li,
+					&wlc_cm->bandstate[band->bandunit].
+					valid_channels);
+		for (j = 0; j < sizeof(chanvec_t); j++)
+			wlc_cm->bandstate[band->bandunit].valid_channels.
+			    vec[j] &= sup_chan.vec[j];
+	}
+
+	brcms_c_quiet_channels_reset(wlc_cm);
+	brcms_c_channels_commit(wlc_cm);
+
+	return 0;
+}
+
+/* Update the radio state (enable/disable) and tx power targets
+ * based on a new set of channel/regulatory information
+ */
+static void brcms_c_channels_commit(struct brcms_cm_info *wlc_cm)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	uint chan;
+	struct txpwr_limits txpwr;
+
+	/* search for the existence of any valid channel */
+	for (chan = 0; chan < MAXCHANNEL; chan++) {
+		if (VALID_CHANNEL20_DB(wlc, chan)) {
+			break;
+		}
+	}
+	if (chan == MAXCHANNEL)
+		chan = INVCHANNEL;
+
+	/* based on the channel search above, set or clear WL_RADIO_COUNTRY_DISABLE */
+	if (chan == INVCHANNEL) {
+		/* country/locale with no valid channels, set the radio disable bit */
+		mboolset(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+		wiphy_err(wlc->wiphy, "wl%d: %s: no valid channel for \"%s\" "
+			  "nbands %d bandlocked %d\n", wlc->pub->unit,
+			  __func__, wlc_cm->country_abbrev, NBANDS(wlc),
+			  wlc->bandlocked);
+	} else
+	    if (mboolisset(wlc->pub->radio_disabled,
+		WL_RADIO_COUNTRY_DISABLE)) {
+		/* country/locale with valid channel, clear the radio disable bit */
+		mboolclr(wlc->pub->radio_disabled, WL_RADIO_COUNTRY_DISABLE);
+	}
+
+	/* Now that the country abbreviation is set, if the radio supports 2G, then
+	 * set channel 14 restrictions based on the new locale.
+	 */
+	if (NBANDS(wlc) > 1 || BAND_2G(wlc->band->bandtype)) {
+		wlc_phy_chanspec_ch14_widefilter_set(wlc->band->pi,
+						     brcms_c_japan(wlc) ? true :
+						     false);
+	}
+
+	if (wlc->pub->up && chan != INVCHANNEL) {
+		brcms_c_channel_reg_limits(wlc_cm, wlc->chanspec, &txpwr);
+		brcms_c_channel_min_txpower_limits_with_local_constraint(wlc_cm,
+			&txpwr, BRCMS_TXPWR_MAX);
+		wlc_phy_txpower_limit_set(wlc->band->pi, &txpwr, wlc->chanspec);
+	}
+}
+
+/* reset the quiet channels vector to the union of the restricted and radar channel sets */
+static void brcms_c_quiet_channels_reset(struct brcms_cm_info *wlc_cm)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	uint i, j;
+	struct brcms_band *band;
+	const chanvec_t *chanvec;
+
+	memset(&wlc_cm->quiet_channels, 0, sizeof(chanvec_t));
+
+	band = wlc->band;
+	for (i = 0; i < NBANDS(wlc);
+	     i++, band = wlc->bandstate[OTHERBANDUNIT(wlc)]) {
+
+		/* initialize quiet channels for restricted channels */
+		chanvec = wlc_cm->bandstate[band->bandunit].restricted_channels;
+		for (j = 0; j < sizeof(chanvec_t); j++)
+			wlc_cm->quiet_channels.vec[j] |= chanvec->vec[j];
+
+	}
+}
+
+static bool
+brcms_c_quiet_chanspec(struct brcms_cm_info *wlc_cm, chanspec_t chspec)
+{
+	return N_ENAB(wlc_cm->wlc->pub) && CHSPEC_IS40(chspec) ?
+		(isset
+		 (wlc_cm->quiet_channels.vec,
+		  LOWER_20_SB(CHSPEC_CHANNEL(chspec)))
+		 || isset(wlc_cm->quiet_channels.vec,
+			  UPPER_20_SB(CHSPEC_CHANNEL(chspec)))) : isset(wlc_cm->
+									quiet_channels.
+									vec,
+									CHSPEC_CHANNEL
+									(chspec));
+}
+
+/* Is the channel valid for the current locale? (but don't consider channels not
+ *   available due to bandlocking)
+ */
+static bool brcms_c_valid_channel20_db(struct brcms_cm_info *wlc_cm, uint val)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+
+	return VALID_CHANNEL20(wlc, val) ||
+		(!wlc->bandlocked
+		 && VALID_CHANNEL20_IN_BAND(wlc, OTHERBANDUNIT(wlc), val));
+}
+
+/* Is the channel valid for the current locale and specified band? */
+static bool brcms_c_valid_channel20_in_band(struct brcms_cm_info *wlc_cm,
+					    uint bandunit, uint val)
+{
+	return ((val < MAXCHANNEL)
+		&& isset(wlc_cm->bandstate[bandunit].valid_channels.vec, val));
+}
+
+/* Is the channel valid for the current locale and current band? */
+static bool brcms_c_valid_channel20(struct brcms_cm_info *wlc_cm, uint val)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+
+	return ((val < MAXCHANNEL) &&
+		isset(wlc_cm->bandstate[wlc->band->bandunit].valid_channels.vec,
+		      val));
+}
+
+static void
+brcms_c_channel_min_txpower_limits_with_local_constraint(
+		struct brcms_cm_info *wlc_cm, struct txpwr_limits *txpwr,
+		u8 local_constraint_qdbm)
+{
+	int j;
+
+	/* CCK Rates */
+	for (j = 0; j < WL_TX_POWER_CCK_NUM; j++) {
+		txpwr->cck[j] = min(txpwr->cck[j], local_constraint_qdbm);
+	}
+
+	/* 20 MHz Legacy OFDM SISO */
+	for (j = 0; j < WL_TX_POWER_OFDM_NUM; j++) {
+		txpwr->ofdm[j] = min(txpwr->ofdm[j], local_constraint_qdbm);
+	}
+
+	/* 20 MHz Legacy OFDM CDD */
+	for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) {
+		txpwr->ofdm_cdd[j] =
+		    min(txpwr->ofdm_cdd[j], local_constraint_qdbm);
+	}
+
+	/* 40 MHz Legacy OFDM SISO */
+	for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) {
+		txpwr->ofdm_40_siso[j] =
+		    min(txpwr->ofdm_40_siso[j], local_constraint_qdbm);
+	}
+
+	/* 40 MHz Legacy OFDM CDD */
+	for (j = 0; j < BRCMS_NUM_RATES_OFDM; j++) {
+		txpwr->ofdm_40_cdd[j] =
+		    min(txpwr->ofdm_40_cdd[j], local_constraint_qdbm);
+	}
+
+	/* 20MHz MCS 0-7 SISO */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_20_siso[j] =
+		    min(txpwr->mcs_20_siso[j], local_constraint_qdbm);
+	}
+
+	/* 20MHz MCS 0-7 CDD */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_20_cdd[j] =
+		    min(txpwr->mcs_20_cdd[j], local_constraint_qdbm);
+	}
+
+	/* 20MHz MCS 0-7 STBC */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_20_stbc[j] =
+		    min(txpwr->mcs_20_stbc[j], local_constraint_qdbm);
+	}
+
+	/* 20MHz MCS 8-15 MIMO */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
+		txpwr->mcs_20_mimo[j] =
+		    min(txpwr->mcs_20_mimo[j], local_constraint_qdbm);
+
+	/* 40MHz MCS 0-7 SISO */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_40_siso[j] =
+		    min(txpwr->mcs_40_siso[j], local_constraint_qdbm);
+	}
+
+	/* 40MHz MCS 0-7 CDD */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_40_cdd[j] =
+		    min(txpwr->mcs_40_cdd[j], local_constraint_qdbm);
+	}
+
+	/* 40MHz MCS 0-7 STBC */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_1_STREAM; j++) {
+		txpwr->mcs_40_stbc[j] =
+		    min(txpwr->mcs_40_stbc[j], local_constraint_qdbm);
+	}
+
+	/* 40MHz MCS 8-15 MIMO */
+	for (j = 0; j < BRCMS_NUM_RATES_MCS_2_STREAM; j++)
+		txpwr->mcs_40_mimo[j] =
+		    min(txpwr->mcs_40_mimo[j], local_constraint_qdbm);
+
+	/* 40MHz MCS 32 */
+	txpwr->mcs32 = min(txpwr->mcs32, local_constraint_qdbm);
+
+}
+
+void
+brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm, chanspec_t chanspec,
+			 u8 local_constraint_qdbm)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	struct txpwr_limits txpwr;
+
+	brcms_c_channel_reg_limits(wlc_cm, chanspec, &txpwr);
+
+	brcms_c_channel_min_txpower_limits_with_local_constraint(wlc_cm, &txpwr,
+							     local_constraint_qdbm);
+
+	brcms_b_set_chanspec(wlc->hw, chanspec,
+			      (brcms_c_quiet_chanspec(wlc_cm, chanspec) != 0),
+			      &txpwr);
+}
+
+#ifdef POWER_DBG
+static void wlc_phy_txpower_limits_dump(struct txpwr_limits *txpwr)
+{
+	int i;
+	char buf[80];
+	char fraction[4][4] = { "   ", ".25", ".5 ", ".75" };
+
+	sprintf(buf, "CCK                ");
+	for (i = 0; i < BRCMS_NUM_RATES_CCK; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->cck[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->cck[i] % BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz OFDM SISO   ");
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->ofdm[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->ofdm[i] % BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz OFDM CDD    ");
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->ofdm_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->ofdm_cdd[i] % BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz OFDM SISO   ");
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->ofdm_40_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->ofdm_40_siso[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz OFDM CDD    ");
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->ofdm_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->ofdm_40_cdd[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz MCS0-7 SISO ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_20_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_20_siso[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz MCS0-7 CDD  ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_20_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_20_cdd[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz MCS0-7 STBC ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_20_stbc[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_20_stbc[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "20 MHz MCS8-15 SDM ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_20_mimo[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_20_mimo[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz MCS0-7 SISO ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_40_siso[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_40_siso[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz MCS0-7 CDD  ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_40_cdd[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_40_cdd[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz MCS0-7 STBC ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_40_stbc[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_40_stbc[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	sprintf(buf, "40 MHz MCS8-15 SDM ");
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
+		sprintf(buf[strlen(buf)], " %2d%s",
+			txpwr->mcs_40_mimo[i] / BRCMS_TXPWR_DB_FACTOR,
+			fraction[txpwr->mcs_40_mimo[i] %
+							BRCMS_TXPWR_DB_FACTOR]);
+	}
+	printk(KERN_DEBUG "%s\n", buf);
+
+	printk(KERN_DEBUG "MCS32               %2d%s\n",
+	       txpwr->mcs32 / BRCMS_TXPWR_DB_FACTOR,
+	       fraction[txpwr->mcs32 % BRCMS_TXPWR_DB_FACTOR]);
+}
+#endif				/* POWER_DBG */
+
+void
+brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm, chanspec_t chanspec,
+		       struct txpwr_limits *txpwr)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	uint i;
+	uint chan;
+	int maxpwr;
+	int delta;
+	const struct country_info *country;
+	struct brcms_band *band;
+	const struct locale_info *li;
+	int conducted_max;
+	int conducted_ofdm_max;
+	const struct locale_mimo_info *li_mimo;
+	int maxpwr20, maxpwr40;
+	int maxpwr_idx;
+	uint j;
+
+	memset(txpwr, 0, sizeof(struct txpwr_limits));
+
+	if (!brcms_c_valid_chanspec_db(wlc_cm, chanspec)) {
+		country = brcms_c_country_lookup(wlc, wlc->autocountry_default);
+		if (country == NULL)
+			return;
+	} else {
+		country = wlc_cm->country;
+	}
+
+	chan = CHSPEC_CHANNEL(chanspec);
+	band = wlc->bandstate[CHSPEC_BANDUNIT(chanspec)];
+	li = BAND_5G(band->bandtype) ?
+	    brcms_c_get_locale_5g(country->locale_5G) :
+	    brcms_c_get_locale_2g(country->locale_2G);
+
+	li_mimo = BAND_5G(band->bandtype) ?
+	    brcms_c_get_mimo_5g(country->locale_mimo_5G) :
+	    brcms_c_get_mimo_2g(country->locale_mimo_2G);
+
+	if (li->flags & BRCMS_EIRP) {
+		delta = band->antgain;
+	} else {
+		delta = 0;
+		if (band->antgain > QDB(6))
+			delta = band->antgain - QDB(6);	/* Excess over 6 dB */
+	}
+
+	if (li == &locale_i) {
+		conducted_max = QDB(22);
+		conducted_ofdm_max = QDB(22);
+	}
+
+	/* CCK txpwr limits for 2.4G band */
+	if (BAND_2G(band->bandtype)) {
+		maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_CCK(chan)];
+
+		maxpwr = maxpwr - delta;
+		maxpwr = max(maxpwr, 0);
+		maxpwr = min(maxpwr, conducted_max);
+
+		for (i = 0; i < BRCMS_NUM_RATES_CCK; i++)
+			txpwr->cck[i] = (u8) maxpwr;
+	}
+
+	/* OFDM txpwr limits for 2.4G or 5G bands */
+	if (BAND_2G(band->bandtype)) {
+		maxpwr = li->maxpwr[CHANNEL_POWER_IDX_2G_OFDM(chan)];
+
+	} else {
+		maxpwr = li->maxpwr[CHANNEL_POWER_IDX_5G(chan)];
+	}
+
+	maxpwr = maxpwr - delta;
+	maxpwr = max(maxpwr, 0);
+	maxpwr = min(maxpwr, conducted_ofdm_max);
+
+	/* Keep OFDM lmit below CCK limit */
+	if (BAND_2G(band->bandtype))
+		maxpwr = min_t(int, maxpwr, txpwr->cck[0]);
+
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++)
+		txpwr->ofdm[i] = (u8) maxpwr;
+
+	for (i = 0; i < BRCMS_NUM_RATES_OFDM; i++) {
+		/* OFDM 40 MHz SISO has the same power as the corresponding MCS0-7 rate unless
+		 * overriden by the locale specific code. We set this value to 0 as a
+		 * flag (presumably 0 dBm isn't a possibility) and then copy the MCS0-7 value
+		 * to the 40 MHz value if it wasn't explicitly set.
+		 */
+		txpwr->ofdm_40_siso[i] = 0;
+
+		txpwr->ofdm_cdd[i] = (u8) maxpwr;
+
+		txpwr->ofdm_40_cdd[i] = 0;
+	}
+
+	/* MIMO/HT specific limits */
+	if (li_mimo->flags & BRCMS_EIRP) {
+		delta = band->antgain;
+	} else {
+		delta = 0;
+		if (band->antgain > QDB(6))
+			delta = band->antgain - QDB(6);	/* Excess over 6 dB */
+	}
+
+	if (BAND_2G(band->bandtype))
+		maxpwr_idx = (chan - 1);
+	else
+		maxpwr_idx = CHANNEL_POWER_IDX_5G(chan);
+
+	maxpwr20 = li_mimo->maxpwr20[maxpwr_idx];
+	maxpwr40 = li_mimo->maxpwr40[maxpwr_idx];
+
+	maxpwr20 = maxpwr20 - delta;
+	maxpwr20 = max(maxpwr20, 0);
+	maxpwr40 = maxpwr40 - delta;
+	maxpwr40 = max(maxpwr40, 0);
+
+	/* Fill in the MCS 0-7 (SISO) rates */
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+
+		/* 20 MHz has the same power as the corresponding OFDM rate unless
+		 * overriden by the locale specific code.
+		 */
+		txpwr->mcs_20_siso[i] = txpwr->ofdm[i];
+		txpwr->mcs_40_siso[i] = 0;
+	}
+
+	/* Fill in the MCS 0-7 CDD rates */
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		txpwr->mcs_20_cdd[i] = (u8) maxpwr20;
+		txpwr->mcs_40_cdd[i] = (u8) maxpwr40;
+	}
+
+	/* These locales have SISO expressed in the table and override CDD later */
+	if (li_mimo == &locale_bn) {
+		if (li_mimo == &locale_bn) {
+			maxpwr20 = QDB(16);
+			maxpwr40 = 0;
+
+			if (chan >= 3 && chan <= 11) {
+				maxpwr40 = QDB(16);
+			}
+		}
+
+		for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+			txpwr->mcs_20_siso[i] = (u8) maxpwr20;
+			txpwr->mcs_40_siso[i] = (u8) maxpwr40;
+		}
+	}
+
+	/* Fill in the MCS 0-7 STBC rates */
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		txpwr->mcs_20_stbc[i] = 0;
+		txpwr->mcs_40_stbc[i] = 0;
+	}
+
+	/* Fill in the MCS 8-15 SDM rates */
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_2_STREAM; i++) {
+		txpwr->mcs_20_mimo[i] = (u8) maxpwr20;
+		txpwr->mcs_40_mimo[i] = (u8) maxpwr40;
+	}
+
+	/* Fill in MCS32 */
+	txpwr->mcs32 = (u8) maxpwr40;
+
+	for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
+		if (txpwr->ofdm_40_cdd[i] == 0)
+			txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+		if (i == 0) {
+			i = i + 1;
+			if (txpwr->ofdm_40_cdd[i] == 0)
+				txpwr->ofdm_40_cdd[i] = txpwr->mcs_40_cdd[j];
+		}
+	}
+
+	/* Copy the 40 MHZ MCS 0-7 CDD value to the 40 MHZ MCS 0-7 SISO value if it wasn't
+	 * provided explicitly.
+	 */
+
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		if (txpwr->mcs_40_siso[i] == 0)
+			txpwr->mcs_40_siso[i] = txpwr->mcs_40_cdd[i];
+	}
+
+	for (i = 0, j = 0; i < BRCMS_NUM_RATES_OFDM; i++, j++) {
+		if (txpwr->ofdm_40_siso[i] == 0)
+			txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+		if (i == 0) {
+			i = i + 1;
+			if (txpwr->ofdm_40_siso[i] == 0)
+				txpwr->ofdm_40_siso[i] = txpwr->mcs_40_siso[j];
+		}
+	}
+
+	/* Copy the 20 and 40 MHz MCS0-7 CDD values to the corresponding STBC values if they weren't
+	 * provided explicitly.
+	 */
+	for (i = 0; i < BRCMS_NUM_RATES_MCS_1_STREAM; i++) {
+		if (txpwr->mcs_20_stbc[i] == 0)
+			txpwr->mcs_20_stbc[i] = txpwr->mcs_20_cdd[i];
+
+		if (txpwr->mcs_40_stbc[i] == 0)
+			txpwr->mcs_40_stbc[i] = txpwr->mcs_40_cdd[i];
+	}
+
+#ifdef POWER_DBG
+	wlc_phy_txpower_limits_dump(txpwr);
+#endif
+	return;
+}
+
+/* Returns true if currently set country is Japan or variant */
+static bool brcms_c_japan(struct brcms_c_info *wlc)
+{
+	return brcms_c_japan_ccode(wlc->cmi->country_abbrev);
+}
+
+/* JP, J1 - J10 are Japan ccodes */
+static bool brcms_c_japan_ccode(const char *ccode)
+{
+	return (ccode[0] == 'J' &&
+		(ccode[1] == 'P' || (ccode[1] >= '1' && ccode[1] <= '9')));
+}
+
+/*
+ * Validate the chanspec for this locale, for 40MHZ we need to also check that the sidebands
+ * are valid 20MZH channels in this locale and they are also a legal HT combination
+ */
+static bool
+brcms_c_valid_chanspec_ext(struct brcms_cm_info *wlc_cm, chanspec_t chspec,
+			   bool dualband)
+{
+	struct brcms_c_info *wlc = wlc_cm->wlc;
+	u8 channel = CHSPEC_CHANNEL(chspec);
+
+	/* check the chanspec */
+	if (brcmu_chspec_malformed(chspec)) {
+		wiphy_err(wlc->wiphy, "wl%d: malformed chanspec 0x%x\n",
+			wlc->pub->unit, chspec);
+		return false;
+	}
+
+	if (CHANNEL_BANDUNIT(wlc_cm->wlc, channel) !=
+	    CHSPEC_BANDUNIT(chspec))
+		return false;
+
+	/* Check a 20Mhz channel */
+	if (CHSPEC_IS20(chspec)) {
+		if (dualband)
+			return VALID_CHANNEL20_DB(wlc_cm->wlc, channel);
+		else
+			return VALID_CHANNEL20(wlc_cm->wlc, channel);
+	}
+#ifdef SUPPORT_40MHZ
+	/* We know we are now checking a 40MHZ channel, so we should only be here
+	 * for NPHYS
+	 */
+	if (BRCMS_ISNPHY(wlc->band) || BRCMS_ISSSLPNPHY(wlc->band)) {
+		u8 upper_sideband = 0, idx;
+		u8 num_ch20_entries =
+		    sizeof(chan20_info) / sizeof(struct chan20_info);
+
+		if (!VALID_40CHANSPEC_IN_BAND(wlc, CHSPEC_BANDUNIT(chspec)))
+			return false;
+
+		if (dualband) {
+			if (!VALID_CHANNEL20_DB(wlc, LOWER_20_SB(channel)) ||
+			    !VALID_CHANNEL20_DB(wlc, UPPER_20_SB(channel)))
+				return false;
+		} else {
+			if (!VALID_CHANNEL20(wlc, LOWER_20_SB(channel)) ||
+			    !VALID_CHANNEL20(wlc, UPPER_20_SB(channel)))
+				return false;
+		}
+
+		/* find the lower sideband info in the sideband array */
+		for (idx = 0; idx < num_ch20_entries; idx++) {
+			if (chan20_info[idx].sb == LOWER_20_SB(channel))
+				upper_sideband = chan20_info[idx].adj_sbs;
+		}
+		/* check that the lower sideband allows an upper sideband */
+		if ((upper_sideband & (CH_UPPER_SB | CH_EWA_VALID)) ==
+		    (CH_UPPER_SB | CH_EWA_VALID))
+			return true;
+		return false;
+	}
+#endif				/* 40 MHZ */
+
+	return false;
+}
+
+bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm, chanspec_t chspec)
+{
+	return brcms_c_valid_chanspec_ext(wlc_cm, chspec, true);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/channel.h b/drivers/net/wireless/brcm80211/brcmsmac/channel.h
new file mode 100644
index 0000000..d22f2f5
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/channel.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_CHANNEL_H_
+#define _BRCM_CHANNEL_H_
+
+/* conversion for phy txpwr calculations that use .25 dB units */
+#define BRCMS_TXPWR_DB_FACTOR 4
+
+
+/* maxpwr mapping to 5GHz band channels:
+ * maxpwr[0] - channels [34-48]
+ * maxpwr[1] - channels [52-60]
+ * maxpwr[2] - channels [62-64]
+ * maxpwr[3] - channels [100-140]
+ * maxpwr[4] - channels [149-165]
+ */
+#define BAND_5G_PWR_LVLS	5	/* 5 power levels for 5G */
+
+/* power level in group of 2.4GHz band channels:
+ * maxpwr[0] - CCK  channels [1]
+ * maxpwr[1] - CCK  channels [2-10]
+ * maxpwr[2] - CCK  channels [11-14]
+ * maxpwr[3] - OFDM channels [1]
+ * maxpwr[4] - OFDM channels [2-10]
+ * maxpwr[5] - OFDM channels [11-14]
+ */
+
+/* macro to get 2.4 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_2G_CCK(c) (((c) < 2) ? 0 : (((c) < 11) ? 1 : 2))	/* cck index */
+#define CHANNEL_POWER_IDX_2G_OFDM(c) (((c) < 2) ? 3 : (((c) < 11) ? 4 : 5))	/* ofdm index */
+
+/* macro to get 5 GHz channel group index for tx power */
+#define CHANNEL_POWER_IDX_5G(c) \
+	(((c) < 52) ? 0 : (((c) < 62) ? 1 : (((c) < 100) ? 2 : (((c) < 149) ? 3 : 4))))
+
+/* max of BAND_5G_PWR_LVLS and 6 for 2.4 GHz */
+#define BRCMS_MAXPWR_TBL_SIZE		6
+/* max of BAND_5G_PWR_LVLS and 14 for 2.4 GHz */
+#define BRCMS_MAXPWR_MIMO_TBL_SIZE	14
+
+#define NBANDS(wlc) ((wlc)->pub->_nbands)
+#define NBANDS_PUB(pub) ((pub)->_nbands)
+#define NBANDS_HW(hw) ((hw)->_nbands)
+
+#define IS_SINGLEBAND_5G(device)	0
+
+/* locale channel and power info. */
+struct locale_info {
+	u32 valid_channels;
+	/* List of radar sensitive channels */
+	u8 radar_channels;
+	/* List of channels used only if APs are detected */
+	u8 restricted_channels;
+	/* Max tx pwr in qdBm for each sub-band */
+	s8 maxpwr[BRCMS_MAXPWR_TBL_SIZE];
+	s8 pub_maxpwr[BAND_5G_PWR_LVLS];	/* Country IE advertised max tx pwr in dBm
+						 * per sub-band
+						 */
+	u8 flags;
+};
+
+/* bits for locale_info flags */
+#define BRCMS_PEAK_CONDUCTED	0x00	/* Peak for locals */
+#define BRCMS_EIRP		0x01	/* Flag for EIRP */
+#define BRCMS_DFS_TPC		0x02	/* Flag for DFS TPC */
+#define BRCMS_NO_OFDM		0x04	/* Flag for No OFDM */
+#define BRCMS_NO_40MHZ		0x08	/* Flag for No MIMO 40MHz */
+#define BRCMS_NO_MIMO		0x10	/* Flag for No MIMO, 20 or 40 MHz */
+#define BRCMS_RADAR_TYPE_EU       0x20	/* Flag for EU */
+#define BRCMS_DFS_FCC             BRCMS_DFS_TPC	/* Flag for DFS FCC */
+#define BRCMS_DFS_EU (BRCMS_DFS_TPC | BRCMS_RADAR_TYPE_EU) /* Flag for DFS EU */
+
+#define ISDFS_EU(fl)		(((fl) & BRCMS_DFS_EU) == BRCMS_DFS_EU)
+
+/* locale per-channel tx power limits for MIMO frames
+ * maxpwr arrays are index by channel for 2.4 GHz limits, and
+ * by sub-band for 5 GHz limits using CHANNEL_POWER_IDX_5G(channel)
+ */
+struct locale_mimo_info {
+	/* tx 20 MHz power limits, qdBm units */
+	s8 maxpwr20[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+	/* tx 40 MHz power limits, qdBm units */
+	s8 maxpwr40[BRCMS_MAXPWR_MIMO_TBL_SIZE];
+	u8 flags;
+};
+
+extern const chanvec_t chanvec_all_2G;
+extern const chanvec_t chanvec_all_5G;
+
+/*
+ * Country names and abbreviations with locale defined from ISO 3166
+ */
+struct country_info {
+	const u8 locale_2G;	/* 2.4G band locale */
+	const u8 locale_5G;	/* 5G band locale */
+	const u8 locale_mimo_2G;	/* 2.4G mimo info */
+	const u8 locale_mimo_5G;	/* 5G mimo info */
+};
+
+extern struct brcms_cm_info *
+brcms_c_channel_mgr_attach(struct brcms_c_info *wlc);
+
+extern void brcms_c_channel_mgr_detach(struct brcms_cm_info *wlc_cm);
+
+extern u8 brcms_c_channel_locale_flags_in_band(struct brcms_cm_info *wlc_cm,
+					   uint bandunit);
+
+extern bool brcms_c_valid_chanspec_db(struct brcms_cm_info *wlc_cm,
+				      chanspec_t chspec);
+
+extern void brcms_c_channel_reg_limits(struct brcms_cm_info *wlc_cm,
+				   chanspec_t chanspec,
+				   struct txpwr_limits *txpwr);
+extern void brcms_c_channel_set_chanspec(struct brcms_cm_info *wlc_cm,
+				     chanspec_t chanspec,
+				     u8 local_constraint_qdbm);
+
+#endif				/* _WLC_CHANNEL_H */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/d11.h b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
new file mode 100644
index 0000000..e7ff0e6
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/d11.h
@@ -0,0 +1,1775 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef	_BRCM_D11_H_
+#define	_BRCM_D11_H_
+
+#include <linux/ieee80211.h>
+
+#include <defs.h>
+#include "pub.h"
+#include "dma.h"
+
+#define	BCN_TMPL_LEN		512	/* length of the BCN template area */
+
+/* RX FIFO numbers */
+#define	RX_FIFO			0	/* data and ctl frames */
+#define	RX_TXSTATUS_FIFO	3	/* RX fifo for tx status packages */
+
+/* TX FIFO numbers using WME Access Classes */
+#define	TX_AC_BK_FIFO		0	/* Access Category Background TX FIFO */
+#define	TX_AC_BE_FIFO		1	/* Access Category Best-Effort TX FIFO */
+#define	TX_AC_VI_FIFO		2	/* Access Class Video TX FIFO */
+#define	TX_AC_VO_FIFO		3	/* Access Class Voice TX FIFO */
+#define	TX_BCMC_FIFO		4	/* Broadcast/Multicast TX FIFO */
+#define	TX_ATIM_FIFO		5	/* TX fifo for ATIM window info */
+
+/* Addr is byte address used by SW; offset is word offset used by uCode */
+
+/* Per AC TX limit settings */
+#define M_AC_TXLMT_BASE_ADDR         (0x180 * 2)
+#define M_AC_TXLMT_ADDR(_ac)         (M_AC_TXLMT_BASE_ADDR + (2 * (_ac)))
+
+/* Legacy TX FIFO numbers */
+#define	TX_DATA_FIFO		TX_AC_BE_FIFO
+#define	TX_CTL_FIFO		TX_AC_VO_FIFO
+
+#ifndef WL_RSSI_ANT_MAX
+#define WL_RSSI_ANT_MAX		4	/* max possible rx antennas */
+#elif WL_RSSI_ANT_MAX != 4
+#error "WL_RSSI_ANT_MAX does not match"
+#endif
+
+struct intctrlregs {
+	u32 intstatus;
+	u32 intmask;
+};
+
+/* PIO structure,
+ *  support two PIO format: 2 bytes access and 4 bytes access
+ *  basic FIFO register set is per channel(transmit or receive)
+ *  a pair of channels is defined for convenience
+ */
+/* 2byte-wide pio register set per channel(xmt or rcv) */
+struct pio2regs {
+	u16 fifocontrol;
+	u16 fifodata;
+	u16 fifofree;	/* only valid in xmt channel, not in rcv channel */
+	u16 PAD;
+};
+
+/* a pair of pio channels(tx and rx) */
+struct pio2regp {
+	pio2regs_t tx;
+	pio2regs_t rx;
+};
+
+/* 4byte-wide pio register set per channel(xmt or rcv) */
+struct pio4regs {
+	u32 fifocontrol;
+	u32 fifodata;
+};
+
+/* a pair of pio channels(tx and rx) */
+struct pio4regp {
+	pio4regs_t tx;
+	pio4regs_t rx;
+};
+
+/* read: 32-bit register that can be read as 32-bit or as 2 16-bit
+ * write: only low 16b-it half can be written
+ */
+union pmqreg {
+	u32 pmqhostdata;	/* read only! */
+	struct {
+		u16 pmqctrlstatus;	/* read/write */
+		u16 PAD;
+	} w;
+};
+
+struct fifo64 {
+	dma64regs_t dmaxmt;	/* dma tx */
+	pio4regs_t piotx;	/* pio tx */
+	dma64regs_t dmarcv;	/* dma rx */
+	pio4regs_t piorx;	/* pio rx */
+};
+
+/*
+ * Host Interface Registers
+ */
+struct d11regs {
+	/* Device Control ("semi-standard host registers") */
+	u32 PAD[3];		/* 0x0 - 0x8 */
+	u32 biststatus;	/* 0xC */
+	u32 biststatus2;	/* 0x10 */
+	u32 PAD;		/* 0x14 */
+	u32 gptimer;		/* 0x18 */
+	u32 usectimer;	/* 0x1c *//* for corerev >= 26 */
+
+	/* Interrupt Control *//* 0x20 */
+	intctrlregs_t intctrlregs[8];
+
+	u32 PAD[40];		/* 0x60 - 0xFC */
+
+	u32 intrcvlazy[4];	/* 0x100 - 0x10C */
+
+	u32 PAD[4];		/* 0x110 - 0x11c */
+
+	u32 maccontrol;	/* 0x120 */
+	u32 maccommand;	/* 0x124 */
+	u32 macintstatus;	/* 0x128 */
+	u32 macintmask;	/* 0x12C */
+
+	/* Transmit Template Access */
+	u32 tplatewrptr;	/* 0x130 */
+	u32 tplatewrdata;	/* 0x134 */
+	u32 PAD[2];		/* 0x138 - 0x13C */
+
+	/* PMQ registers */
+	pmqreg_t pmqreg;	/* 0x140 */
+	u32 pmqpatl;		/* 0x144 */
+	u32 pmqpath;		/* 0x148 */
+	u32 PAD;		/* 0x14C */
+
+	u32 chnstatus;	/* 0x150 */
+	u32 psmdebug;	/* 0x154 */
+	u32 phydebug;	/* 0x158 */
+	u32 machwcap;	/* 0x15C */
+
+	/* Extended Internal Objects */
+	u32 objaddr;		/* 0x160 */
+	u32 objdata;		/* 0x164 */
+	u32 PAD[2];		/* 0x168 - 0x16c */
+
+	u32 frmtxstatus;	/* 0x170 */
+	u32 frmtxstatus2;	/* 0x174 */
+	u32 PAD[2];		/* 0x178 - 0x17c */
+
+	/* TSF host access */
+	u32 tsf_timerlow;	/* 0x180 */
+	u32 tsf_timerhigh;	/* 0x184 */
+	u32 tsf_cfprep;	/* 0x188 */
+	u32 tsf_cfpstart;	/* 0x18c */
+	u32 tsf_cfpmaxdur32;	/* 0x190 */
+	u32 PAD[3];		/* 0x194 - 0x19c */
+
+	u32 maccontrol1;	/* 0x1a0 */
+	u32 machwcap1;	/* 0x1a4 */
+	u32 PAD[14];		/* 0x1a8 - 0x1dc */
+
+	/* Clock control and hardware workarounds*/
+	u32 clk_ctl_st;	/* 0x1e0 */
+	u32 hw_war;
+	u32 d11_phypllctl;	/* the phypll request/avail bits are
+				 * moved to clk_ctl_st
+				 */
+	u32 PAD[5];		/* 0x1ec - 0x1fc */
+
+	/* 0x200-0x37F dma/pio registers */
+	fifo64_t fifo64regs[6];
+
+	/* FIFO diagnostic port access */
+	dma32diag_t dmafifo;	/* 0x380 - 0x38C */
+
+	u32 aggfifocnt;	/* 0x390 */
+	u32 aggfifodata;	/* 0x394 */
+	u32 PAD[16];		/* 0x398 - 0x3d4 */
+	u16 radioregaddr;	/* 0x3d8 */
+	u16 radioregdata;	/* 0x3da */
+
+	/*
+	 * time delay between the change on rf disable input and
+	 * radio shutdown
+	 */
+	u32 rfdisabledly;	/* 0x3DC */
+
+	/* PHY register access */
+	u16 phyversion;	/* 0x3e0 - 0x0 */
+	u16 phybbconfig;	/* 0x3e2 - 0x1 */
+	u16 phyadcbias;	/* 0x3e4 - 0x2  Bphy only */
+	u16 phyanacore;	/* 0x3e6 - 0x3  pwwrdwn on aphy */
+	u16 phyrxstatus0;	/* 0x3e8 - 0x4 */
+	u16 phyrxstatus1;	/* 0x3ea - 0x5 */
+	u16 phycrsth;	/* 0x3ec - 0x6 */
+	u16 phytxerror;	/* 0x3ee - 0x7 */
+	u16 phychannel;	/* 0x3f0 - 0x8 */
+	u16 PAD[1];		/* 0x3f2 - 0x9 */
+	u16 phytest;		/* 0x3f4 - 0xa */
+	u16 phy4waddr;	/* 0x3f6 - 0xb */
+	u16 phy4wdatahi;	/* 0x3f8 - 0xc */
+	u16 phy4wdatalo;	/* 0x3fa - 0xd */
+	u16 phyregaddr;	/* 0x3fc - 0xe */
+	u16 phyregdata;	/* 0x3fe - 0xf */
+
+	/* IHR *//* 0x400 - 0x7FE */
+
+	/* RXE Block */
+	u16 PAD[3];		/* 0x400 - 0x406 */
+	u16 rcv_fifo_ctl;	/* 0x406 */
+	u16 PAD;		/* 0x408 - 0x40a */
+	u16 rcv_frm_cnt;	/* 0x40a */
+	u16 PAD[4];		/* 0x40a - 0x414 */
+	u16 rssi;		/* 0x414 */
+	u16 PAD[5];		/* 0x414 - 0x420 */
+	u16 rcm_ctl;		/* 0x420 */
+	u16 rcm_mat_data;	/* 0x422 */
+	u16 rcm_mat_mask;	/* 0x424 */
+	u16 rcm_mat_dly;	/* 0x426 */
+	u16 rcm_cond_mask_l;	/* 0x428 */
+	u16 rcm_cond_mask_h;	/* 0x42A */
+	u16 rcm_cond_dly;	/* 0x42C */
+	u16 PAD[1];		/* 0x42E */
+	u16 ext_ihr_addr;	/* 0x430 */
+	u16 ext_ihr_data;	/* 0x432 */
+	u16 rxe_phyrs_2;	/* 0x434 */
+	u16 rxe_phyrs_3;	/* 0x436 */
+	u16 phy_mode;	/* 0x438 */
+	u16 rcmta_ctl;	/* 0x43a */
+	u16 rcmta_size;	/* 0x43c */
+	u16 rcmta_addr0;	/* 0x43e */
+	u16 rcmta_addr1;	/* 0x440 */
+	u16 rcmta_addr2;	/* 0x442 */
+	u16 PAD[30];		/* 0x444 - 0x480 */
+
+	/* PSM Block *//* 0x480 - 0x500 */
+
+	u16 PAD;		/* 0x480 */
+	u16 psm_maccontrol_h;	/* 0x482 */
+	u16 psm_macintstatus_l;	/* 0x484 */
+	u16 psm_macintstatus_h;	/* 0x486 */
+	u16 psm_macintmask_l;	/* 0x488 */
+	u16 psm_macintmask_h;	/* 0x48A */
+	u16 PAD;		/* 0x48C */
+	u16 psm_maccommand;	/* 0x48E */
+	u16 psm_brc;		/* 0x490 */
+	u16 psm_phy_hdr_param;	/* 0x492 */
+	u16 psm_postcard;	/* 0x494 */
+	u16 psm_pcard_loc_l;	/* 0x496 */
+	u16 psm_pcard_loc_h;	/* 0x498 */
+	u16 psm_gpio_in;	/* 0x49A */
+	u16 psm_gpio_out;	/* 0x49C */
+	u16 psm_gpio_oe;	/* 0x49E */
+
+	u16 psm_bred_0;	/* 0x4A0 */
+	u16 psm_bred_1;	/* 0x4A2 */
+	u16 psm_bred_2;	/* 0x4A4 */
+	u16 psm_bred_3;	/* 0x4A6 */
+	u16 psm_brcl_0;	/* 0x4A8 */
+	u16 psm_brcl_1;	/* 0x4AA */
+	u16 psm_brcl_2;	/* 0x4AC */
+	u16 psm_brcl_3;	/* 0x4AE */
+	u16 psm_brpo_0;	/* 0x4B0 */
+	u16 psm_brpo_1;	/* 0x4B2 */
+	u16 psm_brpo_2;	/* 0x4B4 */
+	u16 psm_brpo_3;	/* 0x4B6 */
+	u16 psm_brwk_0;	/* 0x4B8 */
+	u16 psm_brwk_1;	/* 0x4BA */
+	u16 psm_brwk_2;	/* 0x4BC */
+	u16 psm_brwk_3;	/* 0x4BE */
+
+	u16 psm_base_0;	/* 0x4C0 */
+	u16 psm_base_1;	/* 0x4C2 */
+	u16 psm_base_2;	/* 0x4C4 */
+	u16 psm_base_3;	/* 0x4C6 */
+	u16 psm_base_4;	/* 0x4C8 */
+	u16 psm_base_5;	/* 0x4CA */
+	u16 psm_base_6;	/* 0x4CC */
+	u16 psm_pc_reg_0;	/* 0x4CE */
+	u16 psm_pc_reg_1;	/* 0x4D0 */
+	u16 psm_pc_reg_2;	/* 0x4D2 */
+	u16 psm_pc_reg_3;	/* 0x4D4 */
+	u16 PAD[0xD];	/* 0x4D6 - 0x4DE */
+	u16 psm_corectlsts;	/* 0x4f0 *//* Corerev >= 13 */
+	u16 PAD[0x7];	/* 0x4f2 - 0x4fE */
+
+	/* TXE0 Block *//* 0x500 - 0x580 */
+	u16 txe_ctl;		/* 0x500 */
+	u16 txe_aux;		/* 0x502 */
+	u16 txe_ts_loc;	/* 0x504 */
+	u16 txe_time_out;	/* 0x506 */
+	u16 txe_wm_0;	/* 0x508 */
+	u16 txe_wm_1;	/* 0x50A */
+	u16 txe_phyctl;	/* 0x50C */
+	u16 txe_status;	/* 0x50E */
+	u16 txe_mmplcp0;	/* 0x510 */
+	u16 txe_mmplcp1;	/* 0x512 */
+	u16 txe_phyctl1;	/* 0x514 */
+
+	u16 PAD[0x05];	/* 0x510 - 0x51E */
+
+	/* Transmit control */
+	u16 xmtfifodef;	/* 0x520 */
+	u16 xmtfifo_frame_cnt;	/* 0x522 *//* Corerev >= 16 */
+	u16 xmtfifo_byte_cnt;	/* 0x524 *//* Corerev >= 16 */
+	u16 xmtfifo_head;	/* 0x526 *//* Corerev >= 16 */
+	u16 xmtfifo_rd_ptr;	/* 0x528 *//* Corerev >= 16 */
+	u16 xmtfifo_wr_ptr;	/* 0x52A *//* Corerev >= 16 */
+	u16 xmtfifodef1;	/* 0x52C *//* Corerev >= 16 */
+
+	u16 PAD[0x09];	/* 0x52E - 0x53E */
+
+	u16 xmtfifocmd;	/* 0x540 */
+	u16 xmtfifoflush;	/* 0x542 */
+	u16 xmtfifothresh;	/* 0x544 */
+	u16 xmtfifordy;	/* 0x546 */
+	u16 xmtfifoprirdy;	/* 0x548 */
+	u16 xmtfiforqpri;	/* 0x54A */
+	u16 xmttplatetxptr;	/* 0x54C */
+	u16 PAD;		/* 0x54E */
+	u16 xmttplateptr;	/* 0x550 */
+	u16 smpl_clct_strptr;	/* 0x552 *//* Corerev >= 22 */
+	u16 smpl_clct_stpptr;	/* 0x554 *//* Corerev >= 22 */
+	u16 smpl_clct_curptr;	/* 0x556 *//* Corerev >= 22 */
+	u16 PAD[0x04];	/* 0x558 - 0x55E */
+	u16 xmttplatedatalo;	/* 0x560 */
+	u16 xmttplatedatahi;	/* 0x562 */
+
+	u16 PAD[2];		/* 0x564 - 0x566 */
+
+	u16 xmtsel;		/* 0x568 */
+	u16 xmttxcnt;	/* 0x56A */
+	u16 xmttxshmaddr;	/* 0x56C */
+
+	u16 PAD[0x09];	/* 0x56E - 0x57E */
+
+	/* TXE1 Block */
+	u16 PAD[0x40];	/* 0x580 - 0x5FE */
+
+	/* TSF Block */
+	u16 PAD[0X02];	/* 0x600 - 0x602 */
+	u16 tsf_cfpstrt_l;	/* 0x604 */
+	u16 tsf_cfpstrt_h;	/* 0x606 */
+	u16 PAD[0X05];	/* 0x608 - 0x610 */
+	u16 tsf_cfppretbtt;	/* 0x612 */
+	u16 PAD[0XD];	/* 0x614 - 0x62C */
+	u16 tsf_clk_frac_l;	/* 0x62E */
+	u16 tsf_clk_frac_h;	/* 0x630 */
+	u16 PAD[0X14];	/* 0x632 - 0x658 */
+	u16 tsf_random;	/* 0x65A */
+	u16 PAD[0x05];	/* 0x65C - 0x664 */
+	/* GPTimer 2 registers */
+	u16 tsf_gpt2_stat;	/* 0x666 */
+	u16 tsf_gpt2_ctr_l;	/* 0x668 */
+	u16 tsf_gpt2_ctr_h;	/* 0x66A */
+	u16 tsf_gpt2_val_l;	/* 0x66C */
+	u16 tsf_gpt2_val_h;	/* 0x66E */
+	u16 tsf_gptall_stat;	/* 0x670 */
+	u16 PAD[0x07];	/* 0x672 - 0x67E */
+
+	/* IFS Block */
+	u16 ifs_sifs_rx_tx_tx;	/* 0x680 */
+	u16 ifs_sifs_nav_tx;	/* 0x682 */
+	u16 ifs_slot;	/* 0x684 */
+	u16 PAD;		/* 0x686 */
+	u16 ifs_ctl;		/* 0x688 */
+	u16 PAD[0x3];	/* 0x68a - 0x68F */
+	u16 ifsstat;		/* 0x690 */
+	u16 ifsmedbusyctl;	/* 0x692 */
+	u16 iftxdur;		/* 0x694 */
+	u16 PAD[0x3];	/* 0x696 - 0x69b */
+	/* EDCF support in dot11macs */
+	u16 ifs_aifsn;	/* 0x69c */
+	u16 ifs_ctl1;	/* 0x69e */
+
+	/* slow clock registers */
+	u16 scc_ctl;		/* 0x6a0 */
+	u16 scc_timer_l;	/* 0x6a2 */
+	u16 scc_timer_h;	/* 0x6a4 */
+	u16 scc_frac;	/* 0x6a6 */
+	u16 scc_fastpwrup_dly;	/* 0x6a8 */
+	u16 scc_per;		/* 0x6aa */
+	u16 scc_per_frac;	/* 0x6ac */
+	u16 scc_cal_timer_l;	/* 0x6ae */
+	u16 scc_cal_timer_h;	/* 0x6b0 */
+	u16 PAD;		/* 0x6b2 */
+
+	u16 PAD[0x26];
+
+	/* NAV Block */
+	u16 nav_ctl;		/* 0x700 */
+	u16 navstat;		/* 0x702 */
+	u16 PAD[0x3e];	/* 0x702 - 0x77E */
+
+	/* WEP/PMQ Block *//* 0x780 - 0x7FE */
+	u16 PAD[0x20];	/* 0x780 - 0x7BE */
+
+	u16 wepctl;		/* 0x7C0 */
+	u16 wepivloc;	/* 0x7C2 */
+	u16 wepivkey;	/* 0x7C4 */
+	u16 wepwkey;		/* 0x7C6 */
+
+	u16 PAD[4];		/* 0x7C8 - 0x7CE */
+	u16 pcmctl;		/* 0X7D0 */
+	u16 pcmstat;		/* 0X7D2 */
+	u16 PAD[6];		/* 0x7D4 - 0x7DE */
+
+	u16 pmqctl;		/* 0x7E0 */
+	u16 pmqstatus;	/* 0x7E2 */
+	u16 pmqpat0;		/* 0x7E4 */
+	u16 pmqpat1;		/* 0x7E6 */
+	u16 pmqpat2;		/* 0x7E8 */
+
+	u16 pmqdat;		/* 0x7EA */
+	u16 pmqdator;	/* 0x7EC */
+	u16 pmqhst;		/* 0x7EE */
+	u16 pmqpath0;	/* 0x7F0 */
+	u16 pmqpath1;	/* 0x7F2 */
+	u16 pmqpath2;	/* 0x7F4 */
+	u16 pmqdath;		/* 0x7F6 */
+
+	u16 PAD[0x04];	/* 0x7F8 - 0x7FE */
+
+	/* SHM *//* 0x800 - 0xEFE */
+	u16 PAD[0x380];	/* 0x800 - 0xEFE */
+};
+
+#define	PIHR_BASE	0x0400	/* byte address of packed IHR region */
+
+/* biststatus */
+#define	BT_DONE		(1U << 31)	/* bist done */
+#define	BT_B2S		(1 << 30)	/* bist2 ram summary bit */
+
+/* intstatus and intmask */
+#define	I_PC		(1 << 10)	/* pci descriptor error */
+#define	I_PD		(1 << 11)	/* pci data error */
+#define	I_DE		(1 << 12)	/* descriptor protocol error */
+#define	I_RU		(1 << 13)	/* receive descriptor underflow */
+#define	I_RO		(1 << 14)	/* receive fifo overflow */
+#define	I_XU		(1 << 15)	/* transmit fifo underflow */
+#define	I_RI		(1 << 16)	/* receive interrupt */
+#define	I_XI		(1 << 24)	/* transmit interrupt */
+
+/* interrupt receive lazy */
+#define	IRL_TO_MASK		0x00ffffff	/* timeout */
+#define	IRL_FC_MASK		0xff000000	/* frame count */
+#define	IRL_FC_SHIFT		24	/* frame count */
+
+/* maccontrol register */
+#define	MCTL_GMODE		(1U << 31)
+#define	MCTL_DISCARD_PMQ	(1 << 30)
+#define	MCTL_WAKE		(1 << 26)
+#define	MCTL_HPS		(1 << 25)
+#define	MCTL_PROMISC		(1 << 24)
+#define	MCTL_KEEPBADFCS		(1 << 23)
+#define	MCTL_KEEPCONTROL	(1 << 22)
+#define	MCTL_PHYLOCK		(1 << 21)
+#define	MCTL_BCNS_PROMISC	(1 << 20)
+#define	MCTL_LOCK_RADIO		(1 << 19)
+#define	MCTL_AP			(1 << 18)
+#define	MCTL_INFRA		(1 << 17)
+#define	MCTL_BIGEND		(1 << 16)
+#define	MCTL_GPOUT_SEL_MASK	(3 << 14)
+#define	MCTL_GPOUT_SEL_SHIFT	14
+#define	MCTL_EN_PSMDBG		(1 << 13)
+#define	MCTL_IHR_EN		(1 << 10)
+#define	MCTL_SHM_UPPER		(1 <<  9)
+#define	MCTL_SHM_EN		(1 <<  8)
+#define	MCTL_PSM_JMP_0		(1 <<  2)
+#define	MCTL_PSM_RUN		(1 <<  1)
+#define	MCTL_EN_MAC		(1 <<  0)
+
+/* maccommand register */
+#define	MCMD_BCN0VLD		(1 <<  0)
+#define	MCMD_BCN1VLD		(1 <<  1)
+#define	MCMD_DIRFRMQVAL		(1 <<  2)
+#define	MCMD_CCA		(1 <<  3)
+#define	MCMD_BG_NOISE		(1 <<  4)
+#define	MCMD_SKIP_SHMINIT	(1 <<  5)	/* only used for simulation */
+#define MCMD_SAMPLECOLL		MCMD_SKIP_SHMINIT	/* reuse for sample collect */
+
+/* macintstatus/macintmask */
+#define	MI_MACSSPNDD		(1 <<  0)	/* MAC has gracefully suspended */
+#define	MI_BCNTPL		(1 <<  1)	/* beacon template available */
+#define	MI_TBTT			(1 <<  2)	/* TBTT indication */
+#define	MI_BCNSUCCESS		(1 <<  3)	/* beacon successfully tx'd */
+#define	MI_BCNCANCLD		(1 <<  4)	/* beacon canceled (IBSS) */
+#define	MI_ATIMWINEND		(1 <<  5)	/* end of ATIM-window (IBSS) */
+#define	MI_PMQ			(1 <<  6)	/* PMQ entries available */
+#define	MI_NSPECGEN_0		(1 <<  7)	/* non-specific gen-stat bits that are set by PSM */
+#define	MI_NSPECGEN_1		(1 <<  8)	/* non-specific gen-stat bits that are set by PSM */
+#define	MI_MACTXERR		(1 <<  9)	/* MAC level Tx error */
+#define	MI_NSPECGEN_3		(1 << 10)	/* non-specific gen-stat bits that are set by PSM */
+#define	MI_PHYTXERR		(1 << 11)	/* PHY Tx error */
+#define	MI_PME			(1 << 12)	/* Power Management Event */
+#define	MI_GP0			(1 << 13)	/* General-purpose timer0 */
+#define	MI_GP1			(1 << 14)	/* General-purpose timer1 */
+#define	MI_DMAINT		(1 << 15)	/* (ORed) DMA-interrupts */
+#define	MI_TXSTOP		(1 << 16)	/* MAC has completed a TX FIFO Suspend/Flush */
+#define	MI_CCA			(1 << 17)	/* MAC has completed a CCA measurement */
+#define	MI_BG_NOISE		(1 << 18)	/* MAC has collected background noise samples */
+#define	MI_DTIM_TBTT		(1 << 19)	/* MBSS DTIM TBTT indication */
+#define MI_PRQ			(1 << 20)	/* Probe response queue needs attention */
+#define	MI_PWRUP		(1 << 21)	/* Radio/PHY has been powered back up. */
+#define	MI_RESERVED3		(1 << 22)
+#define	MI_RESERVED2		(1 << 23)
+#define MI_RESERVED1		(1 << 25)
+/* MAC detected change on RF Disable input*/
+#define MI_RFDISABLE		(1 << 28)
+#define	MI_TFS			(1 << 29)	/* MAC has completed a TX */
+#define	MI_PHYCHANGED		(1 << 30)	/* A phy status change wrt G mode */
+#define	MI_TO			(1U << 31)	/* general purpose timeout */
+
+/* Mac capabilities registers */
+/* machwcap */
+#define	MCAP_TKIPMIC		0x80000000	/* TKIP MIC hardware present */
+
+/* pmqhost data */
+#define	PMQH_DATA_MASK		0xffff0000	/* data entry of head pmq entry */
+#define	PMQH_BSSCFG		0x00100000	/* PM entry for BSS config */
+#define	PMQH_PMOFF		0x00010000	/* PM Mode OFF: power save off */
+#define	PMQH_PMON		0x00020000	/* PM Mode ON: power save on */
+#define	PMQH_DASAT		0x00040000	/* Dis-associated or De-authenticated */
+#define	PMQH_ATIMFAIL		0x00080000	/* ATIM not acknowledged */
+#define	PMQH_DEL_ENTRY		0x00000001	/* delete head entry */
+#define	PMQH_DEL_MULT		0x00000002	/* delete head entry to cur read pointer -1 */
+#define	PMQH_OFLO		0x00000004	/* pmq overflow indication */
+#define	PMQH_NOT_EMPTY		0x00000008	/* entries are present in pmq */
+
+/* phydebug */
+#define	PDBG_CRS		(1 << 0)	/* phy is asserting carrier sense */
+#define	PDBG_TXA		(1 << 1)	/* phy is taking xmit byte from mac this cycle */
+#define	PDBG_TXF		(1 << 2)	/* mac is instructing the phy to transmit a frame */
+#define	PDBG_TXE		(1 << 3)	/* phy is signalling a transmit Error to the mac */
+#define	PDBG_RXF		(1 << 4)	/* phy detected the end of a valid frame preamble */
+#define	PDBG_RXS		(1 << 5)	/* phy detected the end of a valid PLCP header */
+#define	PDBG_RXFRG		(1 << 6)	/* rx start not asserted */
+#define	PDBG_RXV		(1 << 7)	/* mac is taking receive byte from phy this cycle */
+#define	PDBG_RFD		(1 << 16)	/* RF portion of the radio is disabled */
+
+/* objaddr register */
+#define	OBJADDR_SEL_MASK	0x000F0000
+#define	OBJADDR_UCM_SEL		0x00000000
+#define	OBJADDR_SHM_SEL		0x00010000
+#define	OBJADDR_SCR_SEL		0x00020000
+#define	OBJADDR_IHR_SEL		0x00030000
+#define	OBJADDR_RCMTA_SEL	0x00040000
+#define	OBJADDR_SRCHM_SEL	0x00060000
+#define	OBJADDR_WINC		0x01000000
+#define	OBJADDR_RINC		0x02000000
+#define	OBJADDR_AUTO_INC	0x03000000
+
+#define	WEP_PCMADDR		0x07d4
+#define	WEP_PCMDATA		0x07d6
+
+/* frmtxstatus */
+#define	TXS_V			(1 << 0)	/* valid bit */
+#define	TXS_STATUS_MASK		0xffff
+#define	TXS_FID_MASK		0xffff0000
+#define	TXS_FID_SHIFT		16
+
+/* frmtxstatus2 */
+#define	TXS_SEQ_MASK		0xffff
+#define	TXS_PTX_MASK		0xff0000
+#define	TXS_PTX_SHIFT		16
+#define	TXS_MU_MASK		0x01000000
+#define	TXS_MU_SHIFT		24
+
+/* clk_ctl_st */
+#define CCS_ERSRC_REQ_D11PLL	0x00000100	/* d11 core pll request */
+#define CCS_ERSRC_REQ_PHYPLL	0x00000200	/* PHY pll request */
+#define CCS_ERSRC_AVAIL_D11PLL	0x01000000	/* d11 core pll available */
+#define CCS_ERSRC_AVAIL_PHYPLL	0x02000000	/* PHY pll available */
+
+/* HT Cloclk Ctrl and Clock Avail for 4313 */
+#define CCS_ERSRC_REQ_HT    0x00000010	/* HT avail request */
+#define CCS_ERSRC_AVAIL_HT  0x00020000	/* HT clock available */
+
+/* tsf_cfprep register */
+#define	CFPREP_CBI_MASK		0xffffffc0
+#define	CFPREP_CBI_SHIFT	6
+#define	CFPREP_CFPP		0x00000001
+
+/* tx fifo sizes values are in terms of 256 byte blocks */
+#define TXFIFOCMD_RESET_MASK	(1 << 15)	/* reset */
+#define TXFIFOCMD_FIFOSEL_SHIFT	8	/* fifo */
+#define TXFIFO_FIFOTOP_SHIFT	8	/* fifo start */
+
+#define TXFIFO_START_BLK16	 65	/* Base address + 32 * 512 B/P */
+#define TXFIFO_START_BLK	 6	/* Base address + 6 * 256 B */
+#define TXFIFO_SIZE_UNIT	256	/* one unit corresponds to 256 bytes */
+#define MBSS16_TEMPLMEM_MINBLKS	65	/* one unit corresponds to 256 bytes */
+
+/* phy versions, PhyVersion:Revision field */
+#define	PV_AV_MASK		0xf000	/* analog block version */
+#define	PV_AV_SHIFT		12	/* analog block version bitfield offset */
+#define	PV_PT_MASK		0x0f00	/* phy type */
+#define	PV_PT_SHIFT		8	/* phy type bitfield offset */
+#define	PV_PV_MASK		0x000f	/* phy version */
+#define	PHY_TYPE(v)		((v & PV_PT_MASK) >> PV_PT_SHIFT)
+
+/* phy types, PhyVersion:PhyType field */
+#define	PHY_TYPE_N		4	/* N-Phy value */
+#define	PHY_TYPE_SSN		6	/* SSLPN-Phy value */
+#define	PHY_TYPE_LCN		8	/* LCN-Phy value */
+#define	PHY_TYPE_LCNXN		9	/* LCNXN-Phy value */
+#define	PHY_TYPE_NULL		0xf	/* Invalid Phy value */
+
+/* analog types, PhyVersion:AnalogType field */
+#define	ANA_11N_013		5
+
+/* 802.11a PLCP header def */
+struct ofdm_phy_hdr {
+	u8 rlpt[3];		/* rate, length, parity, tail */
+	u16 service;
+	u8 pad;
+} __packed;
+
+#define	D11A_PHY_HDR_GRATE(phdr)	((phdr)->rlpt[0] & 0x0f)
+#define	D11A_PHY_HDR_GRES(phdr)		(((phdr)->rlpt[0] >> 4) & 0x01)
+#define	D11A_PHY_HDR_GLENGTH(phdr)	(((u32 *)((phdr)->rlpt) >> 5) & 0x0fff)
+#define	D11A_PHY_HDR_GPARITY(phdr)	(((phdr)->rlpt[3] >> 1) & 0x01)
+#define	D11A_PHY_HDR_GTAIL(phdr)	(((phdr)->rlpt[3] >> 2) & 0x3f)
+
+/* rate encoded per 802.11a-1999 sec 17.3.4.1 */
+#define	D11A_PHY_HDR_SRATE(phdr, rate)		\
+	((phdr)->rlpt[0] = ((phdr)->rlpt[0] & 0xf0) | ((rate) & 0xf))
+/* set reserved field to zero */
+#define	D11A_PHY_HDR_SRES(phdr)		((phdr)->rlpt[0] &= 0xef)
+/* length is number of octets in PSDU */
+#define	D11A_PHY_HDR_SLENGTH(phdr, length)	\
+	(*(u32 *)((phdr)->rlpt) = *(u32 *)((phdr)->rlpt) | \
+	(((length) & 0x0fff) << 5))
+/* set the tail to all zeros */
+#define	D11A_PHY_HDR_STAIL(phdr)	((phdr)->rlpt[3] &= 0x03)
+
+#define	D11A_PHY_HDR_LEN_L	3	/* low-rate part of PLCP header */
+#define	D11A_PHY_HDR_LEN_R	2	/* high-rate part of PLCP header */
+
+#define	D11A_PHY_TX_DELAY	(2)	/* 2.1 usec */
+
+#define	D11A_PHY_HDR_TIME	(4)	/* low-rate part of PLCP header */
+#define	D11A_PHY_PRE_TIME	(16)
+#define	D11A_PHY_PREHDR_TIME	(D11A_PHY_PRE_TIME + D11A_PHY_HDR_TIME)
+
+/* 802.11b PLCP header def */
+struct cck_phy_hdr {
+	u8 signal;
+	u8 service;
+	u16 length;
+	u16 crc;
+} __packed;
+
+#define	D11B_PHY_HDR_LEN	6
+
+#define	D11B_PHY_TX_DELAY	(3)	/* 3.4 usec */
+
+#define	D11B_PHY_LHDR_TIME	(D11B_PHY_HDR_LEN << 3)
+#define	D11B_PHY_LPRE_TIME	(144)
+#define	D11B_PHY_LPREHDR_TIME	(D11B_PHY_LPRE_TIME + D11B_PHY_LHDR_TIME)
+
+#define	D11B_PHY_SHDR_TIME	(D11B_PHY_LHDR_TIME >> 1)
+#define	D11B_PHY_SPRE_TIME	(D11B_PHY_LPRE_TIME >> 1)
+#define	D11B_PHY_SPREHDR_TIME	(D11B_PHY_SPRE_TIME + D11B_PHY_SHDR_TIME)
+
+#define	D11B_PLCP_SIGNAL_LOCKED	(1 << 2)
+#define	D11B_PLCP_SIGNAL_LE	(1 << 7)
+
+#define MIMO_PLCP_MCS_MASK	0x7f	/* mcs index */
+#define MIMO_PLCP_40MHZ		0x80	/* 40 Hz frame */
+#define MIMO_PLCP_AMPDU		0x08	/* ampdu */
+
+#define BRCMS_GET_CCK_PLCP_LEN(plcp) (plcp[4] + (plcp[5] << 8))
+#define BRCMS_GET_MIMO_PLCP_LEN(plcp) (plcp[1] + (plcp[2] << 8))
+#define BRCMS_SET_MIMO_PLCP_LEN(plcp, len) \
+	do { \
+		plcp[1] = len & 0xff; \
+		plcp[2] = ((len >> 8) & 0xff); \
+	} while (0);
+
+#define BRCMS_SET_MIMO_PLCP_AMPDU(plcp) (plcp[3] |= MIMO_PLCP_AMPDU)
+#define BRCMS_CLR_MIMO_PLCP_AMPDU(plcp) (plcp[3] &= ~MIMO_PLCP_AMPDU)
+#define BRCMS_IS_MIMO_PLCP_AMPDU(plcp) (plcp[3] & MIMO_PLCP_AMPDU)
+
+/* The dot11a PLCP header is 5 bytes.  To simplify the software (so that we
+ * don't need e.g. different tx DMA headers for 11a and 11b), the PLCP header has
+ * padding added in the ucode.
+ */
+#define	D11_PHY_HDR_LEN	6
+
+/* TX DMA buffer header */
+struct d11txh {
+	u16 MacTxControlLow;	/* 0x0 */
+	u16 MacTxControlHigh;	/* 0x1 */
+	u16 MacFrameControl;	/* 0x2 */
+	u16 TxFesTimeNormal;	/* 0x3 */
+	u16 PhyTxControlWord;	/* 0x4 */
+	u16 PhyTxControlWord_1;	/* 0x5 */
+	u16 PhyTxControlWord_1_Fbr;	/* 0x6 */
+	u16 PhyTxControlWord_1_Rts;	/* 0x7 */
+	u16 PhyTxControlWord_1_FbrRts;	/* 0x8 */
+	u16 MainRates;	/* 0x9 */
+	u16 XtraFrameTypes;	/* 0xa */
+	u8 IV[16];		/* 0x0b - 0x12 */
+	u8 TxFrameRA[6];	/* 0x13 - 0x15 */
+	u16 TxFesTimeFallback;	/* 0x16 */
+	u8 RTSPLCPFallback[6];	/* 0x17 - 0x19 */
+	u16 RTSDurFallback;	/* 0x1a */
+	u8 FragPLCPFallback[6];	/* 0x1b - 1d */
+	u16 FragDurFallback;	/* 0x1e */
+	u16 MModeLen;	/* 0x1f */
+	u16 MModeFbrLen;	/* 0x20 */
+	u16 TstampLow;	/* 0x21 */
+	u16 TstampHigh;	/* 0x22 */
+	u16 ABI_MimoAntSel;	/* 0x23 */
+	u16 PreloadSize;	/* 0x24 */
+	u16 AmpduSeqCtl;	/* 0x25 */
+	u16 TxFrameID;	/* 0x26 */
+	u16 TxStatus;	/* 0x27 */
+	u16 MaxNMpdus;	/* 0x28 */
+	u16 MaxABytes_MRT;	/* 0x29 */
+	u16 MaxABytes_FBR;	/* 0x2a */
+	u16 MinMBytes;	/* 0x2b */
+	u8 RTSPhyHeader[D11_PHY_HDR_LEN];	/* 0x2c - 0x2e */
+	struct ieee80211_rts rts_frame;	/* 0x2f - 0x36 */
+	u16 PAD;		/* 0x37 */
+} __packed;
+
+#define	D11_TXH_LEN		112	/* bytes */
+
+/* Frame Types */
+#define FT_CCK	0
+#define FT_OFDM	1
+#define FT_HT	2
+#define FT_N	3
+
+/* Position of MPDU inside A-MPDU; indicated with bits 10:9 of MacTxControlLow */
+#define TXC_AMPDU_SHIFT		9	/* shift for ampdu settings */
+#define TXC_AMPDU_NONE		0	/* Regular MPDU, not an A-MPDU */
+#define TXC_AMPDU_FIRST		1	/* first MPDU of an A-MPDU */
+#define TXC_AMPDU_MIDDLE	2	/* intermediate MPDU of an A-MPDU */
+#define TXC_AMPDU_LAST		3	/* last (or single) MPDU of an A-MPDU */
+
+/* MacTxControlLow */
+#define TXC_AMIC		0x8000
+#define	TXC_SENDCTS		0x0800
+#define TXC_AMPDU_MASK		0x0600
+#define TXC_BW_40		0x0100
+#define TXC_FREQBAND_5G		0x0080
+#define	TXC_DFCS		0x0040
+#define	TXC_IGNOREPMQ		0x0020
+#define	TXC_HWSEQ		0x0010
+#define	TXC_STARTMSDU		0x0008
+#define	TXC_SENDRTS		0x0004
+#define	TXC_LONGFRAME		0x0002
+#define	TXC_IMMEDACK		0x0001
+
+/* MacTxControlHigh */
+#define TXC_PREAMBLE_RTS_FB_SHORT	0x8000	/* RTS fallback preamble type 1 = SHORT 0 = LONG */
+#define TXC_PREAMBLE_RTS_MAIN_SHORT	0x4000	/* RTS main rate preamble type 1 = SHORT 0 = LONG */
+#define TXC_PREAMBLE_DATA_FB_SHORT	0x2000	/* Main fallback rate preamble type
+						 * 1 = SHORT for OFDM/GF for MIMO
+						 * 0 = LONG for CCK/MM for MIMO
+						 */
+/* TXC_PREAMBLE_DATA_MAIN is in PhyTxControl bit 5 */
+#define	TXC_AMPDU_FBR		0x1000	/* use fallback rate for this AMPDU */
+#define	TXC_SECKEY_MASK		0x0FF0
+#define	TXC_SECKEY_SHIFT	4
+#define	TXC_ALT_TXPWR		0x0008	/* Use alternate txpwr defined at loc. M_ALT_TXPWR_IDX */
+#define	TXC_SECTYPE_MASK	0x0007
+#define	TXC_SECTYPE_SHIFT	0
+
+/* Null delimiter for Fallback rate */
+#define AMPDU_FBR_NULL_DELIM  5	/* Location of Null delimiter count for AMPDU */
+
+/* PhyTxControl for Mimophy */
+#define	PHY_TXC_PWR_MASK	0xFC00
+#define	PHY_TXC_PWR_SHIFT	10
+#define	PHY_TXC_ANT_MASK	0x03C0	/* bit 6, 7, 8, 9 */
+#define	PHY_TXC_ANT_SHIFT	6
+#define	PHY_TXC_ANT_0_1		0x00C0	/* auto, last rx */
+#define	PHY_TXC_LCNPHY_ANT_LAST	0x0000
+#define	PHY_TXC_ANT_3		0x0200	/* virtual antenna 3 */
+#define	PHY_TXC_ANT_2		0x0100	/* virtual antenna 2 */
+#define	PHY_TXC_ANT_1		0x0080	/* virtual antenna 1 */
+#define	PHY_TXC_ANT_0		0x0040	/* virtual antenna 0 */
+#define	PHY_TXC_SHORT_HDR	0x0010
+
+#define	PHY_TXC_OLD_ANT_0	0x0000
+#define	PHY_TXC_OLD_ANT_1	0x0100
+#define	PHY_TXC_OLD_ANT_LAST	0x0300
+
+/* PhyTxControl_1 for Mimophy */
+#define PHY_TXC1_BW_MASK		0x0007
+#define PHY_TXC1_BW_10MHZ		0
+#define PHY_TXC1_BW_10MHZ_UP		1
+#define PHY_TXC1_BW_20MHZ		2
+#define PHY_TXC1_BW_20MHZ_UP		3
+#define PHY_TXC1_BW_40MHZ		4
+#define PHY_TXC1_BW_40MHZ_DUP		5
+#define PHY_TXC1_MODE_SHIFT		3
+#define PHY_TXC1_MODE_MASK		0x0038
+#define PHY_TXC1_MODE_SISO		0
+#define PHY_TXC1_MODE_CDD		1
+#define PHY_TXC1_MODE_STBC		2
+#define PHY_TXC1_MODE_SDM		3
+
+/* PhyTxControl for HTphy that are different from Mimophy */
+#define	PHY_TXC_HTANT_MASK		0x3fC0	/* bit 6, 7, 8, 9, 10, 11, 12, 13 */
+
+/* XtraFrameTypes */
+#define XFTS_RTS_FT_SHIFT	2
+#define XFTS_FBRRTS_FT_SHIFT	4
+#define XFTS_CHANNEL_SHIFT	8
+
+/* Antenna diversity bit in ant_wr_settle */
+#define	PHY_AWS_ANTDIV		0x2000
+
+/* IFS ctl */
+#define IFS_USEEDCF	(1 << 2)
+
+/* IFS ctl1 */
+#define IFS_CTL1_EDCRS	(1 << 3)
+#define IFS_CTL1_EDCRS_20L (1 << 4)
+#define IFS_CTL1_EDCRS_40 (1 << 5)
+
+/* ABI_MimoAntSel */
+#define ABI_MAS_ADDR_BMP_IDX_MASK	0x0f00
+#define ABI_MAS_ADDR_BMP_IDX_SHIFT	8
+#define ABI_MAS_FBR_ANT_PTN_MASK	0x00f0
+#define ABI_MAS_FBR_ANT_PTN_SHIFT	4
+#define ABI_MAS_MRT_ANT_PTN_MASK	0x000f
+
+/* tx status packet */
+struct tx_status {
+	u16 framelen;
+	u16 PAD;
+	u16 frameid;
+	u16 status;
+	u16 lasttxtime;
+	u16 sequence;
+	u16 phyerr;
+	u16 ackphyrxsh;
+} __packed;
+
+#define	TXSTATUS_LEN	16
+
+/* status field bit definitions */
+#define	TX_STATUS_FRM_RTX_MASK	0xF000
+#define	TX_STATUS_FRM_RTX_SHIFT	12
+#define	TX_STATUS_RTS_RTX_MASK	0x0F00
+#define	TX_STATUS_RTS_RTX_SHIFT	8
+#define TX_STATUS_MASK		0x00FE
+#define	TX_STATUS_PMINDCTD	(1 << 7)	/* PM mode indicated to AP */
+#define	TX_STATUS_INTERMEDIATE	(1 << 6)	/* intermediate or 1st ampdu pkg */
+#define	TX_STATUS_AMPDU		(1 << 5)	/* AMPDU status */
+#define TX_STATUS_SUPR_MASK	0x1C	/* suppress status bits (4:2) */
+#define TX_STATUS_SUPR_SHIFT	2
+#define	TX_STATUS_ACK_RCV	(1 << 1)	/* ACK received */
+#define	TX_STATUS_VALID		(1 << 0)	/* Tx status valid */
+#define	TX_STATUS_NO_ACK	0
+
+/* suppress status reason codes */
+#define	TX_STATUS_SUPR_PMQ	(1 << 2)	/* PMQ entry */
+#define	TX_STATUS_SUPR_FLUSH	(2 << 2)	/* flush request */
+#define	TX_STATUS_SUPR_FRAG	(3 << 2)	/* previous frag failure */
+#define	TX_STATUS_SUPR_TBTT	(3 << 2)	/* SHARED: Probe response supr for TBTT */
+#define	TX_STATUS_SUPR_BADCH	(4 << 2)	/* channel mismatch */
+#define	TX_STATUS_SUPR_EXPTIME	(5 << 2)	/* lifetime expiry */
+#define	TX_STATUS_SUPR_UF	(6 << 2)	/* underflow */
+
+/* Unexpected tx status for rate update */
+#define TX_STATUS_UNEXP(status) \
+	((((status) & TX_STATUS_INTERMEDIATE) != 0) && \
+	 TX_STATUS_UNEXP_AMPDU(status))
+
+/* Unexpected tx status for A-MPDU rate update */
+#define TX_STATUS_UNEXP_AMPDU(status) \
+	((((status) & TX_STATUS_SUPR_MASK) != 0) && \
+	 (((status) & TX_STATUS_SUPR_MASK) != TX_STATUS_SUPR_EXPTIME))
+
+#define TX_STATUS_BA_BMAP03_MASK	0xF000	/* ba bitmap 0:3 in 1st pkg */
+#define TX_STATUS_BA_BMAP03_SHIFT	12	/* ba bitmap 0:3 in 1st pkg */
+#define TX_STATUS_BA_BMAP47_MASK	0x001E	/* ba bitmap 4:7 in 2nd pkg */
+#define TX_STATUS_BA_BMAP47_SHIFT	3	/* ba bitmap 4:7 in 2nd pkg */
+
+/* RXE (Receive Engine) */
+
+/* RCM_CTL */
+#define	RCM_INC_MASK_H		0x0080
+#define	RCM_INC_MASK_L		0x0040
+#define	RCM_INC_DATA		0x0020
+#define	RCM_INDEX_MASK		0x001F
+#define	RCM_SIZE		15
+
+#define	RCM_MAC_OFFSET		0	/* current MAC address */
+#define	RCM_BSSID_OFFSET	3	/* current BSSID address */
+#define	RCM_F_BSSID_0_OFFSET	6	/* foreign BSS CFP tracking */
+#define	RCM_F_BSSID_1_OFFSET	9	/* foreign BSS CFP tracking */
+#define	RCM_F_BSSID_2_OFFSET	12	/* foreign BSS CFP tracking */
+
+#define RCM_WEP_TA0_OFFSET	16
+#define RCM_WEP_TA1_OFFSET	19
+#define RCM_WEP_TA2_OFFSET	22
+#define RCM_WEP_TA3_OFFSET	25
+
+/* PSM Block */
+
+/* psm_phy_hdr_param bits */
+#define MAC_PHY_RESET		1
+#define MAC_PHY_CLOCK_EN	2
+#define MAC_PHY_FORCE_CLK	4
+
+/* WEP Block */
+
+/* WEP_WKEY */
+#define	WKEY_START		(1 << 8)
+#define	WKEY_SEL_MASK		0x1F
+
+/* WEP data formats */
+
+/* the number of RCMTA entries */
+#define RCMTA_SIZE 50
+
+#define M_ADDR_BMP_BLK		(0x37e * 2)
+#define M_ADDR_BMP_BLK_SZ	12
+
+#define ADDR_BMP_RA		(1 << 0)	/* Receiver Address (RA) */
+#define ADDR_BMP_TA		(1 << 1)	/* Transmitter Address (TA) */
+#define ADDR_BMP_BSSID		(1 << 2)	/* BSSID */
+#define ADDR_BMP_AP		(1 << 3)	/* Infra-BSS Access Point (AP) */
+#define ADDR_BMP_STA		(1 << 4)	/* Infra-BSS Station (STA) */
+#define ADDR_BMP_RESERVED1	(1 << 5)
+#define ADDR_BMP_RESERVED2	(1 << 6)
+#define ADDR_BMP_RESERVED3	(1 << 7)
+#define ADDR_BMP_BSS_IDX_MASK	(3 << 8)	/* BSS control block index */
+#define ADDR_BMP_BSS_IDX_SHIFT	8
+
+#define	WSEC_MAX_RCMTA_KEYS	54
+
+/* max keys in M_TKMICKEYS_BLK */
+#define	WSEC_MAX_TKMIC_ENGINE_KEYS		12	/* 8 + 4 default */
+
+/* max RXE match registers */
+#define WSEC_MAX_RXE_KEYS	4
+
+/* SECKINDXALGO (Security Key Index & Algorithm Block) word format */
+/* SKL (Security Key Lookup) */
+#define	SKL_ALGO_MASK		0x0007
+#define	SKL_ALGO_SHIFT		0
+#define	SKL_KEYID_MASK		0x0008
+#define	SKL_KEYID_SHIFT		3
+#define	SKL_INDEX_MASK		0x03F0
+#define	SKL_INDEX_SHIFT		4
+#define	SKL_GRP_ALGO_MASK	0x1c00
+#define	SKL_GRP_ALGO_SHIFT	10
+
+/* additional bits defined for IBSS group key support */
+#define	SKL_IBSS_INDEX_MASK	0x01F0
+#define	SKL_IBSS_INDEX_SHIFT	4
+#define	SKL_IBSS_KEYID1_MASK	0x0600
+#define	SKL_IBSS_KEYID1_SHIFT	9
+#define	SKL_IBSS_KEYID2_MASK	0x1800
+#define	SKL_IBSS_KEYID2_SHIFT	11
+#define	SKL_IBSS_KEYALGO_MASK	0xE000
+#define	SKL_IBSS_KEYALGO_SHIFT	13
+
+#define	WSEC_MODE_OFF		0
+#define	WSEC_MODE_HW		1
+#define	WSEC_MODE_SW		2
+
+#define	WSEC_ALGO_OFF		0
+#define	WSEC_ALGO_WEP1		1
+#define	WSEC_ALGO_TKIP		2
+#define	WSEC_ALGO_AES		3
+#define	WSEC_ALGO_WEP128	4
+#define	WSEC_ALGO_AES_LEGACY	5
+#define	WSEC_ALGO_NALG		6
+
+#define	AES_MODE_NONE		0
+#define	AES_MODE_CCM		1
+
+/* WEP_CTL (Rev 0) */
+#define	WECR0_KEYREG_SHIFT	0
+#define	WECR0_KEYREG_MASK	0x7
+#define	WECR0_DECRYPT		(1 << 3)
+#define	WECR0_IVINLINE		(1 << 4)
+#define	WECR0_WEPALG_SHIFT	5
+#define	WECR0_WEPALG_MASK	(0x7 << 5)
+#define	WECR0_WKEYSEL_SHIFT	8
+#define	WECR0_WKEYSEL_MASK	(0x7 << 8)
+#define	WECR0_WKEYSTART		(1 << 11)
+#define	WECR0_WEPINIT		(1 << 14)
+#define	WECR0_ICVERR		(1 << 15)
+
+/* Frame template map byte offsets */
+#define	T_ACTS_TPL_BASE		(0)
+#define	T_NULL_TPL_BASE		(0xc * 2)
+#define	T_QNULL_TPL_BASE	(0x1c * 2)
+#define	T_RR_TPL_BASE		(0x2c * 2)
+#define	T_BCN0_TPL_BASE		(0x34 * 2)
+#define	T_PRS_TPL_BASE		(0x134 * 2)
+#define	T_BCN1_TPL_BASE		(0x234 * 2)
+#define T_TX_FIFO_TXRAM_BASE	(T_ACTS_TPL_BASE + (TXFIFO_START_BLK * TXFIFO_SIZE_UNIT))
+
+#define T_BA_TPL_BASE		T_QNULL_TPL_BASE	/* template area for BA */
+
+#define T_RAM_ACCESS_SZ		4	/* template ram is 4 byte access only */
+
+/* Shared Mem byte offsets */
+
+/* Location where the ucode expects the corerev */
+#define	M_MACHW_VER		(0x00b * 2)
+
+/* Location where the ucode expects the MAC capabilities */
+#define	M_MACHW_CAP_L		(0x060 * 2)
+#define	M_MACHW_CAP_H	(0x061 * 2)
+
+/* WME shared memory */
+#define M_EDCF_STATUS_OFF	(0x007 * 2)
+#define M_TXF_CUR_INDEX		(0x018 * 2)
+#define M_EDCF_QINFO		(0x120 * 2)
+
+/* PS-mode related parameters */
+#define	M_DOT11_SLOT		(0x008 * 2)
+#define	M_DOT11_DTIMPERIOD	(0x009 * 2)
+#define	M_NOSLPZNATDTIM		(0x026 * 2)
+
+/* Beacon-related parameters */
+#define	M_BCN0_FRM_BYTESZ	(0x00c * 2)	/* Bcn 0 template length */
+#define	M_BCN1_FRM_BYTESZ	(0x00d * 2)	/* Bcn 1 template length */
+#define	M_BCN_TXTSF_OFFSET	(0x00e * 2)
+#define	M_TIMBPOS_INBEACON	(0x00f * 2)
+#define	M_SFRMTXCNTFBRTHSD	(0x022 * 2)
+#define	M_LFRMTXCNTFBRTHSD	(0x023 * 2)
+#define	M_BCN_PCTLWD		(0x02a * 2)
+#define M_BCN_LI		(0x05b * 2)	/* beacon listen interval */
+
+/* MAX Rx Frame len */
+#define M_MAXRXFRM_LEN		(0x010 * 2)
+
+/* ACK/CTS related params */
+#define	M_RSP_PCTLWD		(0x011 * 2)
+
+/* Hardware Power Control */
+#define M_TXPWR_N		(0x012 * 2)
+#define M_TXPWR_TARGET		(0x013 * 2)
+#define M_TXPWR_MAX		(0x014 * 2)
+#define M_TXPWR_CUR		(0x019 * 2)
+
+/* Rx-related parameters */
+#define	M_RX_PAD_DATA_OFFSET	(0x01a * 2)
+
+/* WEP Shared mem data */
+#define	M_SEC_DEFIVLOC		(0x01e * 2)
+#define	M_SEC_VALNUMSOFTMCHTA	(0x01f * 2)
+#define	M_PHYVER		(0x028 * 2)
+#define	M_PHYTYPE		(0x029 * 2)
+#define	M_SECRXKEYS_PTR		(0x02b * 2)
+#define	M_TKMICKEYS_PTR		(0x059 * 2)
+#define	M_SECKINDXALGO_BLK	(0x2ea * 2)
+#define M_SECKINDXALGO_BLK_SZ	54
+#define	M_SECPSMRXTAMCH_BLK	(0x2fa * 2)
+#define	M_TKIP_TSC_TTAK		(0x18c * 2)
+#define	D11_MAX_KEY_SIZE	16
+
+#define	M_MAX_ANTCNT		(0x02e * 2)	/* antenna swap threshold */
+
+/* Probe response related parameters */
+#define	M_SSIDLEN		(0x024 * 2)
+#define	M_PRB_RESP_FRM_LEN	(0x025 * 2)
+#define	M_PRS_MAXTIME		(0x03a * 2)
+#define	M_SSID			(0xb0 * 2)
+#define	M_CTXPRS_BLK		(0xc0 * 2)
+#define	C_CTX_PCTLWD_POS	(0x4 * 2)
+
+/* Delta between OFDM and CCK power in CCK power boost mode */
+#define M_OFDM_OFFSET		(0x027 * 2)
+
+/* TSSI for last 4 11b/g CCK packets transmitted */
+#define	M_B_TSSI_0		(0x02c * 2)
+#define	M_B_TSSI_1		(0x02d * 2)
+
+/* Host flags to turn on ucode options */
+#define	M_HOST_FLAGS1		(0x02f * 2)
+#define	M_HOST_FLAGS2		(0x030 * 2)
+#define	M_HOST_FLAGS3		(0x031 * 2)
+#define	M_HOST_FLAGS4		(0x03c * 2)
+#define	M_HOST_FLAGS5		(0x06a * 2)
+#define	M_HOST_FLAGS_SZ		16
+
+#define M_RADAR_REG		(0x033 * 2)
+
+/* TSSI for last 4 11a OFDM packets transmitted */
+#define	M_A_TSSI_0		(0x034 * 2)
+#define	M_A_TSSI_1		(0x035 * 2)
+
+/* noise interference measurement */
+#define M_NOISE_IF_COUNT	(0x034 * 2)
+#define M_NOISE_IF_TIMEOUT	(0x035 * 2)
+
+#define	M_RF_RX_SP_REG1		(0x036 * 2)
+
+/* TSSI for last 4 11g OFDM packets transmitted */
+#define	M_G_TSSI_0		(0x038 * 2)
+#define	M_G_TSSI_1		(0x039 * 2)
+
+/* Background noise measure */
+#define	M_JSSI_0		(0x44 * 2)
+#define	M_JSSI_1		(0x45 * 2)
+#define	M_JSSI_AUX		(0x46 * 2)
+
+#define	M_CUR_2050_RADIOCODE	(0x47 * 2)
+
+/* TX fifo sizes */
+#define M_FIFOSIZE0		(0x4c * 2)
+#define M_FIFOSIZE1		(0x4d * 2)
+#define M_FIFOSIZE2		(0x4e * 2)
+#define M_FIFOSIZE3		(0x4f * 2)
+#define D11_MAX_TX_FRMS		32	/* max frames allowed in tx fifo */
+
+/* Current channel number plus upper bits */
+#define M_CURCHANNEL		(0x50 * 2)
+#define D11_CURCHANNEL_5G	0x0100;
+#define D11_CURCHANNEL_40	0x0200;
+#define D11_CURCHANNEL_MAX	0x00FF;
+
+/* last posted frameid on the bcmc fifo */
+#define M_BCMC_FID		(0x54 * 2)
+#define INVALIDFID		0xffff
+
+/* extended beacon phyctl bytes for 11N */
+#define	M_BCN_PCTL1WD		(0x058 * 2)
+
+/* idle busy ratio to duty_cycle requirement  */
+#define M_TX_IDLE_BUSY_RATIO_X_16_CCK  (0x52 * 2)
+#define M_TX_IDLE_BUSY_RATIO_X_16_OFDM (0x5A * 2)
+
+/* CW RSSI for LCNPHY */
+#define M_LCN_RSSI_0		0x1332
+#define M_LCN_RSSI_1		0x1338
+#define M_LCN_RSSI_2		0x133e
+#define M_LCN_RSSI_3		0x1344
+
+/* SNR for LCNPHY */
+#define M_LCN_SNR_A_0	0x1334
+#define M_LCN_SNR_B_0	0x1336
+
+#define M_LCN_SNR_A_1	0x133a
+#define M_LCN_SNR_B_1	0x133c
+
+#define M_LCN_SNR_A_2	0x1340
+#define M_LCN_SNR_B_2	0x1342
+
+#define M_LCN_SNR_A_3	0x1346
+#define M_LCN_SNR_B_3	0x1348
+
+#define M_LCN_LAST_RESET	(81*2)
+#define M_LCN_LAST_LOC	(63*2)
+#define M_LCNPHY_RESET_STATUS (4902)
+#define M_LCNPHY_DSC_TIME	(0x98d*2)
+#define M_LCNPHY_RESET_CNT_DSC (0x98b*2)
+#define M_LCNPHY_RESET_CNT	(0x98c*2)
+
+/* Rate table offsets */
+#define	M_RT_DIRMAP_A		(0xe0 * 2)
+#define	M_RT_BBRSMAP_A		(0xf0 * 2)
+#define	M_RT_DIRMAP_B		(0x100 * 2)
+#define	M_RT_BBRSMAP_B		(0x110 * 2)
+
+/* Rate table entry offsets */
+#define	M_RT_PRS_PLCP_POS	10
+#define	M_RT_PRS_DUR_POS	16
+#define	M_RT_OFDM_PCTL1_POS	18
+
+#define M_20IN40_IQ			(0x380 * 2)
+
+/* SHM locations where ucode stores the current power index */
+#define M_CURR_IDX1		(0x384 * 2)
+#define M_CURR_IDX2		(0x387 * 2)
+
+#define M_BSCALE_ANT0	(0x5e * 2)
+#define M_BSCALE_ANT1	(0x5f * 2)
+
+/* Antenna Diversity Testing */
+#define M_MIMO_ANTSEL_RXDFLT	(0x63 * 2)
+#define M_ANTSEL_CLKDIV	(0x61 * 2)
+#define M_MIMO_ANTSEL_TXDFLT	(0x64 * 2)
+
+#define M_MIMO_MAXSYM	(0x5d * 2)
+#define MIMO_MAXSYM_DEF		0x8000	/* 32k */
+#define MIMO_MAXSYM_MAX		0xffff	/* 64k */
+
+#define M_WATCHDOG_8TU		(0x1e * 2)
+#define WATCHDOG_8TU_DEF	5
+#define WATCHDOG_8TU_MAX	10
+
+/* Manufacturing Test Variables */
+#define M_PKTENG_CTRL		(0x6c * 2)	/* PER test mode */
+#define M_PKTENG_IFS		(0x6d * 2)	/* IFS for TX mode */
+#define M_PKTENG_FRMCNT_LO		(0x6e * 2)	/* Lower word of tx frmcnt/rx lostcnt */
+#define M_PKTENG_FRMCNT_HI		(0x6f * 2)	/* Upper word of tx frmcnt/rx lostcnt */
+
+/* Index variation in vbat ripple */
+#define M_LCN_PWR_IDX_MAX	(0x67 * 2)	/* highest index read by ucode */
+#define M_LCN_PWR_IDX_MIN	(0x66 * 2)	/* lowest index read by ucode */
+
+/* M_PKTENG_CTRL bit definitions */
+#define M_PKTENG_MODE_TX		0x0001
+#define M_PKTENG_MODE_TX_RIFS	        0x0004
+#define M_PKTENG_MODE_TX_CTS            0x0008
+#define M_PKTENG_MODE_RX		0x0002
+#define M_PKTENG_MODE_RX_WITH_ACK	0x0402
+#define M_PKTENG_MODE_MASK		0x0003
+#define M_PKTENG_FRMCNT_VLD		0x0100	/* TX frames indicated in the frmcnt reg */
+
+/* Sample Collect parameters (bitmap and type) */
+#define M_SMPL_COL_BMP		(0x37d * 2)	/* Trigger bitmap for sample collect */
+#define M_SMPL_COL_CTL		(0x3b2 * 2)	/* Sample collect type */
+
+#define ANTSEL_CLKDIV_4MHZ	6
+#define MIMO_ANTSEL_BUSY	0x4000	/* bit 14 (busy) */
+#define MIMO_ANTSEL_SEL		0x8000	/* bit 15 write the value */
+#define MIMO_ANTSEL_WAIT	50	/* 50us wait */
+#define MIMO_ANTSEL_OVERRIDE	0x8000	/* flag */
+
+struct shm_acparams {
+	u16 txop;
+	u16 cwmin;
+	u16 cwmax;
+	u16 cwcur;
+	u16 aifs;
+	u16 bslots;
+	u16 reggap;
+	u16 status;
+	u16 rsvd[8];
+} __packed;
+#define M_EDCF_QLEN	(16 * 2)
+
+#define WME_STATUS_NEWAC	(1 << 8)
+
+/* M_HOST_FLAGS */
+#define MHFMAX		5	/* Number of valid hostflag half-word (u16) */
+#define MHF1		0	/* Hostflag 1 index */
+#define MHF2		1	/* Hostflag 2 index */
+#define MHF3		2	/* Hostflag 3 index */
+#define MHF4		3	/* Hostflag 4 index */
+#define MHF5		4	/* Hostflag 5 index */
+
+/* Flags in M_HOST_FLAGS */
+#define	MHF1_ANTDIV		0x0001	/* Enable ucode antenna diversity help */
+#define	MHF1_EDCF		0x0100	/* Enable EDCF access control */
+#define MHF1_IQSWAP_WAR		0x0200
+#define	MHF1_FORCEFASTCLK	0x0400	/* Disable Slow clock request, for corerev < 11 */
+
+/* Flags in M_HOST_FLAGS2 */
+#define MHF2_PCISLOWCLKWAR	0x0008	/* PR16165WAR : Enable ucode PCI slow clock WAR */
+#define MHF2_TXBCMC_NOW		0x0040	/* Flush BCMC FIFO immediately */
+#define MHF2_HWPWRCTL		0x0080	/* Enable ucode/hw power control */
+#define MHF2_NPHY40MHZ_WAR	0x0800
+
+/* Flags in M_HOST_FLAGS3 */
+#define MHF3_ANTSEL_EN		0x0001	/* enabled mimo antenna selection */
+#define MHF3_ANTSEL_MODE	0x0002	/* antenna selection mode: 0: 2x3, 1: 2x4 */
+#define MHF3_RESERVED1		0x0004
+#define MHF3_RESERVED2		0x0008
+#define MHF3_NPHY_MLADV_WAR	0x0010
+
+/* Flags in M_HOST_FLAGS4 */
+#define MHF4_BPHY_TXCORE0	0x0080	/* force bphy Tx on core 0 (board level WAR) */
+#define MHF4_EXTPA_ENABLE	0x4000	/* for 4313A0 FEM boards */
+
+/* Flags in M_HOST_FLAGS5 */
+#define MHF5_4313_GPIOCTRL	0x0001
+#define MHF5_RESERVED1		0x0002
+#define MHF5_RESERVED2		0x0004
+/* Radio power setting for ucode */
+#define	M_RADIO_PWR		(0x32 * 2)
+
+/* phy noise recorded by ucode right after tx */
+#define	M_PHY_NOISE		(0x037 * 2)
+#define	PHY_NOISE_MASK		0x00ff
+
+/* Receive Frame Data Header for 802.11b DCF-only frames */
+struct d11rxhdr {
+	u16 RxFrameSize;	/* Actual byte length of the frame data received */
+	u16 PAD;
+	u16 PhyRxStatus_0;	/* PhyRxStatus 15:0 */
+	u16 PhyRxStatus_1;	/* PhyRxStatus 31:16 */
+	u16 PhyRxStatus_2;	/* PhyRxStatus 47:32 */
+	u16 PhyRxStatus_3;	/* PhyRxStatus 63:48 */
+	u16 PhyRxStatus_4;	/* PhyRxStatus 79:64 */
+	u16 PhyRxStatus_5;	/* PhyRxStatus 95:80 */
+	u16 RxStatus1;	/* MAC Rx Status */
+	u16 RxStatus2;	/* extended MAC Rx status */
+	u16 RxTSFTime;	/* RxTSFTime time of first MAC symbol + M_PHY_PLCPRX_DLY */
+	u16 RxChan;		/* gain code, channel radio code, and phy type */
+} __packed;
+
+#define	RXHDR_LEN		24	/* sizeof struct d11rxhdr */
+#define	FRAMELEN(h)		((h)->RxFrameSize)
+
+struct brcms_d11rxhdr {
+	struct d11rxhdr rxhdr;
+	u32 tsf_l;		/* TSF_L reading */
+	s8 rssi;		/* computed instanteneous rssi in BMAC */
+	s8 rxpwr0;		/* obsoleted, place holder for legacy ROM code. use rxpwr[] */
+	s8 rxpwr1;		/* obsoleted, place holder for legacy ROM code. use rxpwr[] */
+	s8 do_rssi_ma;	/* do per-pkt sampling for per-antenna ma in HIGH */
+	s8 rxpwr[WL_RSSI_ANT_MAX];	/* rssi for supported antennas */
+} __packed;
+
+/* PhyRxStatus_0: */
+#define	PRXS0_FT_MASK		0x0003	/* NPHY only: CCK, OFDM, preN, N */
+#define	PRXS0_CLIP_MASK		0x000C	/* NPHY only: clip count adjustment steps by AGC */
+#define	PRXS0_CLIP_SHIFT	2
+#define	PRXS0_UNSRATE		0x0010	/* PHY received a frame with unsupported rate */
+#define	PRXS0_RXANT_UPSUBBAND	0x0020	/* GPHY: rx ant, NPHY: upper sideband */
+#define	PRXS0_LCRS		0x0040	/* CCK frame only: lost crs during cck frame reception */
+#define	PRXS0_SHORTH		0x0080	/* Short Preamble */
+#define	PRXS0_PLCPFV		0x0100	/* PLCP violation */
+#define	PRXS0_PLCPHCF		0x0200	/* PLCP header integrity check failed */
+#define	PRXS0_GAIN_CTL		0x4000	/* legacy PHY gain control */
+#define PRXS0_ANTSEL_MASK	0xF000	/* NPHY: Antennas used for received frame, bitmask */
+#define PRXS0_ANTSEL_SHIFT	0x12
+
+/* subfield PRXS0_FT_MASK */
+#define	PRXS0_CCK		0x0000
+#define	PRXS0_OFDM		0x0001	/* valid only for G phy, use rxh->RxChan for A phy */
+#define	PRXS0_PREN		0x0002
+#define	PRXS0_STDN		0x0003
+
+/* subfield PRXS0_ANTSEL_MASK */
+#define PRXS0_ANTSEL_0		0x0	/* antenna 0 is used */
+#define PRXS0_ANTSEL_1		0x2	/* antenna 1 is used */
+#define PRXS0_ANTSEL_2		0x4	/* antenna 2 is used */
+#define PRXS0_ANTSEL_3		0x8	/* antenna 3 is used */
+
+/* PhyRxStatus_1: */
+#define	PRXS1_JSSI_MASK		0x00FF
+#define	PRXS1_JSSI_SHIFT	0
+#define	PRXS1_SQ_MASK		0xFF00
+#define	PRXS1_SQ_SHIFT		8
+
+/* nphy PhyRxStatus_1: */
+#define PRXS1_nphy_PWR0_MASK	0x00FF
+#define PRXS1_nphy_PWR1_MASK	0xFF00
+
+/* HTPHY Rx Status defines */
+/* htphy PhyRxStatus_0: those bit are overlapped with PhyRxStatus_0 */
+#define PRXS0_BAND	        0x0400	/* 0 = 2.4G, 1 = 5G */
+#define PRXS0_RSVD	        0x0800	/* reserved; set to 0 */
+#define PRXS0_UNUSED	        0xF000	/* unused and not defined; set to 0 */
+
+/* htphy PhyRxStatus_1: */
+#define PRXS1_HTPHY_CORE_MASK	0x000F	/* core enables for {3..0}, 0=disabled, 1=enabled */
+#define PRXS1_HTPHY_ANTCFG_MASK	0x00F0	/* antenna configation */
+#define PRXS1_HTPHY_MMPLCPLenL_MASK	0xFF00	/* Mixmode PLCP Length low byte mask */
+
+/* htphy PhyRxStatus_2: */
+#define PRXS2_HTPHY_MMPLCPLenH_MASK	0x000F	/* Mixmode PLCP Length high byte maskw */
+#define PRXS2_HTPHY_MMPLCH_RATE_MASK	0x00F0	/* Mixmode PLCP rate mask */
+#define PRXS2_HTPHY_RXPWR_ANT0	0xFF00	/* Rx power on core 0 */
+
+/* htphy PhyRxStatus_3: */
+#define PRXS3_HTPHY_RXPWR_ANT1	0x00FF	/* Rx power on core 1 */
+#define PRXS3_HTPHY_RXPWR_ANT2	0xFF00	/* Rx power on core 2 */
+
+/* htphy PhyRxStatus_4: */
+#define PRXS4_HTPHY_RXPWR_ANT3	0x00FF	/* Rx power on core 3 */
+#define PRXS4_HTPHY_CFO		0xFF00	/* Coarse frequency offset */
+
+/* htphy PhyRxStatus_5: */
+#define PRXS5_HTPHY_FFO	        0x00FF	/* Fine frequency offset */
+#define PRXS5_HTPHY_AR	        0xFF00	/* Advance Retard */
+
+#define HTPHY_MMPLCPLen(rxs)	((((rxs)->PhyRxStatus_1 & PRXS1_HTPHY_MMPLCPLenL_MASK) >> 8) | \
+	(((rxs)->PhyRxStatus_2 & PRXS2_HTPHY_MMPLCPLenH_MASK) << 8))
+/* Get Rx power on core 0 */
+#define HTPHY_RXPWR_ANT0(rxs)	((((rxs)->PhyRxStatus_2) & PRXS2_HTPHY_RXPWR_ANT0) >> 8)
+/* Get Rx power on core 1 */
+#define HTPHY_RXPWR_ANT1(rxs)	(((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT1)
+/* Get Rx power on core 2 */
+#define HTPHY_RXPWR_ANT2(rxs)	((((rxs)->PhyRxStatus_3) & PRXS3_HTPHY_RXPWR_ANT2) >> 8)
+
+/* ucode RxStatus1: */
+#define	RXS_BCNSENT		0x8000
+#define	RXS_SECKINDX_MASK	0x07e0
+#define	RXS_SECKINDX_SHIFT	5
+#define	RXS_DECERR		(1 << 4)
+#define	RXS_DECATMPT		(1 << 3)
+#define	RXS_PBPRES		(1 << 2)	/* PAD bytes to make IP data 4 bytes aligned */
+#define	RXS_RESPFRAMETX		(1 << 1)
+#define	RXS_FCSERR		(1 << 0)
+
+/* ucode RxStatus2: */
+#define RXS_AMSDU_MASK		1
+#define	RXS_AGGTYPE_MASK	0x6
+#define	RXS_AGGTYPE_SHIFT	1
+#define	RXS_PHYRXST_VALID	(1 << 8)
+#define RXS_RXANT_MASK		0x3
+#define RXS_RXANT_SHIFT		12
+
+/* RxChan */
+#define RXS_CHAN_40		0x1000
+#define RXS_CHAN_5G		0x0800
+#define	RXS_CHAN_ID_MASK	0x07f8
+#define	RXS_CHAN_ID_SHIFT	3
+#define	RXS_CHAN_PHYTYPE_MASK	0x0007
+#define	RXS_CHAN_PHYTYPE_SHIFT	0
+
+/* Index of attenuations used during ucode power control. */
+#define M_PWRIND_BLKS	(0x184 * 2)
+#define M_PWRIND_MAP0	(M_PWRIND_BLKS + 0x0)
+#define M_PWRIND_MAP1	(M_PWRIND_BLKS + 0x2)
+#define M_PWRIND_MAP2	(M_PWRIND_BLKS + 0x4)
+#define M_PWRIND_MAP3	(M_PWRIND_BLKS + 0x6)
+/* M_PWRIND_MAP(core) macro */
+#define M_PWRIND_MAP(core)  (M_PWRIND_BLKS + ((core)<<1))
+
+/* PSM SHM variable offsets */
+#define	M_PSM_SOFT_REGS	0x0
+#define	M_BOM_REV_MAJOR	(M_PSM_SOFT_REGS + 0x0)
+#define	M_BOM_REV_MINOR	(M_PSM_SOFT_REGS + 0x2)
+#define	M_UCODE_DBGST	(M_PSM_SOFT_REGS + 0x40)	/* ucode debug status code */
+#define	M_UCODE_MACSTAT	(M_PSM_SOFT_REGS + 0xE0)	/* macstat counters */
+
+#define M_AGING_THRSH	(0x3e * 2)	/* max time waiting for medium before tx */
+#define	M_MBURST_SIZE	(0x40 * 2)	/* max frames in a frameburst */
+#define	M_MBURST_TXOP	(0x41 * 2)	/* max frameburst TXOP in unit of us */
+#define M_SYNTHPU_DLY	(0x4a * 2)	/* pre-wakeup for synthpu, default: 500 */
+#define	M_PRETBTT	(0x4b * 2)
+
+#define M_ALT_TXPWR_IDX		(M_PSM_SOFT_REGS + (0x3b * 2))	/* offset to the target txpwr */
+#define M_PHY_TX_FLT_PTR	(M_PSM_SOFT_REGS + (0x3d * 2))
+#define M_CTS_DURATION		(M_PSM_SOFT_REGS + (0x5c * 2))
+#define M_LP_RCCAL_OVR		(M_PSM_SOFT_REGS + (0x6b * 2))
+
+/* PKTENG Rx Stats Block */
+#define M_RXSTATS_BLK_PTR	(M_PSM_SOFT_REGS + (0x65 * 2))
+
+/* ucode debug status codes */
+#define	DBGST_INACTIVE		0	/* not valid really */
+#define	DBGST_INIT		1	/* after zeroing SHM, before suspending at init */
+#define	DBGST_ACTIVE		2	/* "normal" state */
+#define	DBGST_SUSPENDED		3	/* suspended */
+#define	DBGST_ASLEEP		4	/* asleep (PS mode) */
+
+/* Scratch Reg defs */
+enum _ePsmScratchPadRegDefinitions {
+	S_RSV0 = 0,
+	S_RSV1,
+	S_RSV2,
+
+	/* scratch registers for Dot11-contants */
+	S_DOT11_CWMIN,		/* CW-minimum                                   0x03 */
+	S_DOT11_CWMAX,		/* CW-maximum                                   0x04 */
+	S_DOT11_CWCUR,		/* CW-current                                   0x05 */
+	S_DOT11_SRC_LMT,	/* short retry count limit                      0x06 */
+	S_DOT11_LRC_LMT,	/* long retry count limit                       0x07 */
+	S_DOT11_DTIMCOUNT,	/* DTIM-count                                   0x08 */
+
+	/* Tx-side scratch registers */
+	S_SEQ_NUM,		/* hardware sequence number reg                 0x09 */
+	S_SEQ_NUM_FRAG,		/* seq-num for frags (Set at the start os MSDU  0x0A */
+	S_FRMRETX_CNT,		/* frame retx count                             0x0B */
+	S_SSRC,			/* Station short retry count                    0x0C */
+	S_SLRC,			/* Station long retry count                     0x0D */
+	S_EXP_RSP,		/* Expected response frame                      0x0E */
+	S_OLD_BREM,		/* Remaining backoff ctr                        0x0F */
+	S_OLD_CWWIN,		/* saved-off CW-cur                             0x10 */
+	S_TXECTL,		/* TXE-Ctl word constructed in scr-pad          0x11 */
+	S_CTXTST,		/* frm type-subtype as read from Tx-descr       0x12 */
+
+	/* Rx-side scratch registers */
+	S_RXTST,		/* Type and subtype in Rxframe                  0x13 */
+
+	/* Global state register */
+	S_STREG,		/* state storage actual bit maps below          0x14 */
+
+	S_TXPWR_SUM,		/* Tx power control: accumulator                0x15 */
+	S_TXPWR_ITER,		/* Tx power control: iteration                  0x16 */
+	S_RX_FRMTYPE,		/* Rate and PHY type for frames                 0x17 */
+	S_THIS_AGG,		/* Size of this AGG (A-MSDU)                    0x18 */
+
+	S_KEYINDX,		/*                                              0x19 */
+	S_RXFRMLEN,		/* Receive MPDU length in bytes                 0x1A */
+
+	/* Receive TSF time stored in SCR */
+	S_RXTSFTMRVAL_WD3,	/* TSF value at the start of rx                 0x1B */
+	S_RXTSFTMRVAL_WD2,	/* TSF value at the start of rx                 0x1C */
+	S_RXTSFTMRVAL_WD1,	/* TSF value at the start of rx                 0x1D */
+	S_RXTSFTMRVAL_WD0,	/* TSF value at the start of rx                 0x1E */
+	S_RXSSN,		/* Received start seq number for A-MPDU BA      0x1F */
+	S_RXQOSFLD,		/* Rx-QoS field (if present)                    0x20 */
+
+	/* Scratch pad regs used in microcode as temp storage */
+	S_TMP0,			/* stmp0                                        0x21 */
+	S_TMP1,			/* stmp1                                        0x22 */
+	S_TMP2,			/* stmp2                                        0x23 */
+	S_TMP3,			/* stmp3                                        0x24 */
+	S_TMP4,			/* stmp4                                        0x25 */
+	S_TMP5,			/* stmp5                                        0x26 */
+	S_PRQPENALTY_CTR,	/* Probe response queue penalty counter         0x27 */
+	S_ANTCNT,		/* unsuccessful attempts on current ant.        0x28 */
+	S_SYMBOL,		/* flag for possible symbol ctl frames          0x29 */
+	S_RXTP,			/* rx frame type                                0x2A */
+	S_STREG2,		/* extra state storage                          0x2B */
+	S_STREG3,		/* even more extra state storage                0x2C */
+	S_STREG4,		/* ...                                          0x2D */
+	S_STREG5,		/* remember to initialize it to zero            0x2E */
+
+	S_ADJPWR_IDX,
+	S_CUR_PTR,		/* Temp pointer for A-MPDU re-Tx SHM table      0x32 */
+	S_REVID4,		/* 0x33 */
+	S_INDX,			/* 0x34 */
+	S_ADDR0,		/* 0x35 */
+	S_ADDR1,		/* 0x36 */
+	S_ADDR2,		/* 0x37 */
+	S_ADDR3,		/* 0x38 */
+	S_ADDR4,		/* 0x39 */
+	S_ADDR5,		/* 0x3A */
+	S_TMP6,			/* 0x3B */
+	S_KEYINDX_BU,		/* Backup for Key index                         0x3C */
+	S_MFGTEST_TMP0,		/* Temp register used for RX test calculations  0x3D */
+	S_RXESN,		/* Received end sequence number for A-MPDU BA   0x3E */
+	S_STREG6,		/* 0x3F */
+};
+
+#define S_BEACON_INDX	S_OLD_BREM
+#define S_PRS_INDX	S_OLD_CWWIN
+#define S_PHYTYPE	S_SSRC
+#define S_PHYVER	S_SLRC
+
+/* IHR SLOW_CTRL values */
+#define SLOW_CTRL_PDE		(1 << 0)
+#define SLOW_CTRL_FD		(1 << 8)
+
+/* ucode mac statistic counters in shared memory */
+struct macstat {
+	u16 txallfrm;	/* 0x80 */
+	u16 txrtsfrm;	/* 0x82 */
+	u16 txctsfrm;	/* 0x84 */
+	u16 txackfrm;	/* 0x86 */
+	u16 txdnlfrm;	/* 0x88 */
+	u16 txbcnfrm;	/* 0x8a */
+	u16 txfunfl[8];	/* 0x8c - 0x9b */
+	u16 txtplunfl;	/* 0x9c */
+	u16 txphyerr;	/* 0x9e */
+	u16 pktengrxducast;	/* 0xa0 */
+	u16 pktengrxdmcast;	/* 0xa2 */
+	u16 rxfrmtoolong;	/* 0xa4 */
+	u16 rxfrmtooshrt;	/* 0xa6 */
+	u16 rxinvmachdr;	/* 0xa8 */
+	u16 rxbadfcs;	/* 0xaa */
+	u16 rxbadplcp;	/* 0xac */
+	u16 rxcrsglitch;	/* 0xae */
+	u16 rxstrt;		/* 0xb0 */
+	u16 rxdfrmucastmbss;	/* 0xb2 */
+	u16 rxmfrmucastmbss;	/* 0xb4 */
+	u16 rxcfrmucast;	/* 0xb6 */
+	u16 rxrtsucast;	/* 0xb8 */
+	u16 rxctsucast;	/* 0xba */
+	u16 rxackucast;	/* 0xbc */
+	u16 rxdfrmocast;	/* 0xbe */
+	u16 rxmfrmocast;	/* 0xc0 */
+	u16 rxcfrmocast;	/* 0xc2 */
+	u16 rxrtsocast;	/* 0xc4 */
+	u16 rxctsocast;	/* 0xc6 */
+	u16 rxdfrmmcast;	/* 0xc8 */
+	u16 rxmfrmmcast;	/* 0xca */
+	u16 rxcfrmmcast;	/* 0xcc */
+	u16 rxbeaconmbss;	/* 0xce */
+	u16 rxdfrmucastobss;	/* 0xd0 */
+	u16 rxbeaconobss;	/* 0xd2 */
+	u16 rxrsptmout;	/* 0xd4 */
+	u16 bcntxcancl;	/* 0xd6 */
+	u16 PAD;
+	u16 rxf0ovfl;	/* 0xda */
+	u16 rxf1ovfl;	/* 0xdc */
+	u16 rxf2ovfl;	/* 0xde */
+	u16 txsfovfl;	/* 0xe0 */
+	u16 pmqovfl;		/* 0xe2 */
+	u16 rxcgprqfrm;	/* 0xe4 */
+	u16 rxcgprsqovfl;	/* 0xe6 */
+	u16 txcgprsfail;	/* 0xe8 */
+	u16 txcgprssuc;	/* 0xea */
+	u16 prs_timeout;	/* 0xec */
+	u16 rxnack;
+	u16 frmscons;
+	u16 txnack;
+	u16 txglitch_nack;
+	u16 txburst;		/* 0xf6 # tx bursts */
+	u16 bphy_rxcrsglitch;	/* bphy rx crs glitch */
+	u16 phywatchdog;	/* 0xfa # of phy watchdog events */
+	u16 PAD;
+	u16 bphy_badplcp;	/* bphy bad plcp */
+};
+
+/* dot11 core-specific control flags */
+#define	SICF_PCLKE		0x0004	/* PHY clock enable */
+#define	SICF_PRST		0x0008	/* PHY reset */
+#define	SICF_MPCLKE		0x0010	/* MAC PHY clockcontrol enable */
+#define	SICF_FREF		0x0020	/* PLL FreqRefSelect */
+/* NOTE: the following bw bits only apply when the core is attached
+ * to a NPHY
+ */
+#define	SICF_BWMASK		0x00c0	/* phy clock mask (b6 & b7) */
+#define	SICF_BW40		0x0080	/* 40MHz BW (160MHz phyclk) */
+#define	SICF_BW20		0x0040	/* 20MHz BW (80MHz phyclk) */
+#define	SICF_BW10		0x0000	/* 10MHz BW (40MHz phyclk) */
+#define	SICF_GMODE		0x2000	/* gmode enable */
+
+/* dot11 core-specific status flags */
+#define	SISF_2G_PHY		0x0001	/* 2.4G capable phy */
+#define	SISF_5G_PHY		0x0002	/* 5G capable phy */
+#define	SISF_FCLKA		0x0004	/* FastClkAvailable */
+#define	SISF_DB_PHY		0x0008	/* Dualband phy */
+
+/* === End of MAC reg, Beginning of PHY(b/a/g/n) reg, radio and LPPHY regs are separated === */
+
+#define	BPHY_REG_OFT_BASE	0x0
+/* offsets for indirect access to bphy registers */
+#define	BPHY_BB_CONFIG		0x01
+#define	BPHY_ADCBIAS		0x02
+#define	BPHY_ANACORE		0x03
+#define	BPHY_PHYCRSTH		0x06
+#define	BPHY_TEST		0x0a
+#define	BPHY_PA_TX_TO		0x10
+#define	BPHY_SYNTH_DC_TO	0x11
+#define	BPHY_PA_TX_TIME_UP	0x12
+#define	BPHY_RX_FLTR_TIME_UP	0x13
+#define	BPHY_TX_POWER_OVERRIDE	0x14
+#define	BPHY_RF_OVERRIDE	0x15
+#define	BPHY_RF_TR_LOOKUP1	0x16
+#define	BPHY_RF_TR_LOOKUP2	0x17
+#define	BPHY_COEFFS		0x18
+#define	BPHY_PLL_OUT		0x19
+#define	BPHY_REFRESH_MAIN	0x1a
+#define	BPHY_REFRESH_TO0	0x1b
+#define	BPHY_REFRESH_TO1	0x1c
+#define	BPHY_RSSI_TRESH		0x20
+#define	BPHY_IQ_TRESH_HH	0x21
+#define	BPHY_IQ_TRESH_H		0x22
+#define	BPHY_IQ_TRESH_L		0x23
+#define	BPHY_IQ_TRESH_LL	0x24
+#define	BPHY_GAIN		0x25
+#define	BPHY_LNA_GAIN_RANGE	0x26
+#define	BPHY_JSSI		0x27
+#define	BPHY_TSSI_CTL		0x28
+#define	BPHY_TSSI		0x29
+#define	BPHY_TR_LOSS_CTL	0x2a
+#define	BPHY_LO_LEAKAGE		0x2b
+#define	BPHY_LO_RSSI_ACC	0x2c
+#define	BPHY_LO_IQMAG_ACC	0x2d
+#define	BPHY_TX_DC_OFF1		0x2e
+#define	BPHY_TX_DC_OFF2		0x2f
+#define	BPHY_PEAK_CNT_THRESH	0x30
+#define	BPHY_FREQ_OFFSET	0x31
+#define	BPHY_DIVERSITY_CTL	0x32
+#define	BPHY_PEAK_ENERGY_LO	0x33
+#define	BPHY_PEAK_ENERGY_HI	0x34
+#define	BPHY_SYNC_CTL		0x35
+#define	BPHY_TX_PWR_CTRL	0x36
+#define BPHY_TX_EST_PWR		0x37
+#define	BPHY_STEP		0x38
+#define	BPHY_WARMUP		0x39
+#define	BPHY_LMS_CFF_READ	0x3a
+#define	BPHY_LMS_COEFF_I	0x3b
+#define	BPHY_LMS_COEFF_Q	0x3c
+#define	BPHY_SIG_POW		0x3d
+#define	BPHY_RFDC_CANCEL_CTL	0x3e
+#define	BPHY_HDR_TYPE		0x40
+#define	BPHY_SFD_TO		0x41
+#define	BPHY_SFD_CTL		0x42
+#define	BPHY_DEBUG		0x43
+#define	BPHY_RX_DELAY_COMP	0x44
+#define	BPHY_CRS_DROP_TO	0x45
+#define	BPHY_SHORT_SFD_NZEROS	0x46
+#define	BPHY_DSSS_COEFF1	0x48
+#define	BPHY_DSSS_COEFF2	0x49
+#define	BPHY_CCK_COEFF1		0x4a
+#define	BPHY_CCK_COEFF2		0x4b
+#define	BPHY_TR_CORR		0x4c
+#define	BPHY_ANGLE_SCALE	0x4d
+#define	BPHY_TX_PWR_BASE_IDX	0x4e
+#define	BPHY_OPTIONAL_MODES2	0x4f
+#define	BPHY_CCK_LMS_STEP	0x50
+#define	BPHY_BYPASS		0x51
+#define	BPHY_CCK_DELAY_LONG	0x52
+#define	BPHY_CCK_DELAY_SHORT	0x53
+#define	BPHY_PPROC_CHAN_DELAY	0x54
+#define	BPHY_DDFS_ENABLE	0x58
+#define	BPHY_PHASE_SCALE	0x59
+#define	BPHY_FREQ_CONTROL	0x5a
+#define	BPHY_LNA_GAIN_RANGE_10	0x5b
+#define	BPHY_LNA_GAIN_RANGE_32	0x5c
+#define	BPHY_OPTIONAL_MODES	0x5d
+#define	BPHY_RX_STATUS2		0x5e
+#define	BPHY_RX_STATUS3		0x5f
+#define	BPHY_DAC_CONTROL	0x60
+#define	BPHY_ANA11G_FILT_CTRL	0x62
+#define	BPHY_REFRESH_CTRL	0x64
+#define	BPHY_RF_OVERRIDE2	0x65
+#define	BPHY_SPUR_CANCEL_CTRL	0x66
+#define	BPHY_FINE_DIGIGAIN_CTRL	0x67
+#define	BPHY_RSSI_LUT		0x88
+#define	BPHY_RSSI_LUT_END	0xa7
+#define	BPHY_TSSI_LUT		0xa8
+#define	BPHY_TSSI_LUT_END	0xc7
+#define	BPHY_TSSI2PWR_LUT	0x380
+#define	BPHY_TSSI2PWR_LUT_END	0x39f
+#define	BPHY_LOCOMP_LUT		0x3a0
+#define	BPHY_LOCOMP_LUT_END	0x3bf
+#define	BPHY_TXGAIN_LUT		0x3c0
+#define	BPHY_TXGAIN_LUT_END	0x3ff
+
+/* Bits in BB_CONFIG: */
+#define	PHY_BBC_ANT_MASK	0x0180
+#define	PHY_BBC_ANT_SHIFT	7
+#define	BB_DARWIN		0x1000
+#define BBCFG_RESETCCA		0x4000
+#define BBCFG_RESETRX		0x8000
+
+/* Bits in phytest(0x0a): */
+#define	TST_DDFS		0x2000
+#define	TST_TXFILT1		0x0800
+#define	TST_UNSCRAM		0x0400
+#define	TST_CARR_SUPP		0x0200
+#define	TST_DC_COMP_LOOP	0x0100
+#define	TST_LOOPBACK		0x0080
+#define	TST_TXFILT0		0x0040
+#define	TST_TXTEST_ENABLE	0x0020
+#define	TST_TXTEST_RATE		0x0018
+#define	TST_TXTEST_PHASE	0x0007
+
+/* phytest txTestRate values */
+#define	TST_TXTEST_RATE_1MBPS	0
+#define	TST_TXTEST_RATE_2MBPS	1
+#define	TST_TXTEST_RATE_5_5MBPS	2
+#define	TST_TXTEST_RATE_11MBPS	3
+#define	TST_TXTEST_RATE_SHIFT	3
+
+#define SHM_BYT_CNT	0x2	/* IHR location */
+#define MAX_BYT_CNT	0x600	/* Maximum frame len */
+
+struct d11cnt {
+	u32 txfrag;
+	u32 txmulti;
+	u32 txfail;
+	u32 txretry;
+	u32 txretrie;
+	u32 rxdup;
+	u32 txrts;
+	u32 txnocts;
+	u32 txnoack;
+	u32 rxfrag;
+	u32 rxmulti;
+	u32 rxcrc;
+	u32 txfrmsnt;
+	u32 rxundec;
+};
+
+#endif				/* _BRCM_D11_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.c b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
new file mode 100644
index 0000000..ea17671
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.c
@@ -0,0 +1,1917 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+#include <linux/slab.h>
+#include <linux/skbuff.h>
+#include <linux/delay.h>
+#include <linux/pci.h>
+
+#if defined(__mips__)
+#include <asm/addrspace.h>
+#endif
+
+#include <brcmu_utils.h>
+#include <aiutils.h>
+#include "types.h"
+#include "dma.h"
+
+/*
+ * Each descriptor ring must be 8kB aligned, and fit within a contiguous 8kB physical address.
+ */
+#define D64RINGALIGN_BITS	13
+#define	D64MAXRINGSZ		(1 << D64RINGALIGN_BITS)
+#define	D64RINGALIGN		(1 << D64RINGALIGN_BITS)
+
+#define	D64MAXDD	(D64MAXRINGSZ / sizeof(struct dma64desc))
+
+/* transmit channel control */
+#define	D64_XC_XE		0x00000001	/* transmit enable */
+#define	D64_XC_SE		0x00000002	/* transmit suspend request */
+#define	D64_XC_LE		0x00000004	/* loopback enable */
+#define	D64_XC_FL		0x00000010	/* flush request */
+#define	D64_XC_PD		0x00000800	/* parity check disable */
+#define	D64_XC_AE		0x00030000	/* address extension bits */
+#define	D64_XC_AE_SHIFT		16
+
+/* transmit descriptor table pointer */
+#define	D64_XP_LD_MASK		0x00000fff	/* last valid descriptor */
+
+/* transmit channel status */
+#define	D64_XS0_CD_MASK		0x00001fff	/* current descriptor pointer */
+#define	D64_XS0_XS_MASK		0xf0000000	/* transmit state */
+#define	D64_XS0_XS_SHIFT		28
+#define	D64_XS0_XS_DISABLED	0x00000000	/* disabled */
+#define	D64_XS0_XS_ACTIVE	0x10000000	/* active */
+#define	D64_XS0_XS_IDLE		0x20000000	/* idle wait */
+#define	D64_XS0_XS_STOPPED	0x30000000	/* stopped */
+#define	D64_XS0_XS_SUSP		0x40000000	/* suspend pending */
+
+#define	D64_XS1_AD_MASK		0x00001fff	/* active descriptor */
+#define	D64_XS1_XE_MASK		0xf0000000	/* transmit errors */
+#define	D64_XS1_XE_SHIFT		28
+#define	D64_XS1_XE_NOERR	0x00000000	/* no error */
+#define	D64_XS1_XE_DPE		0x10000000	/* descriptor protocol error */
+#define	D64_XS1_XE_DFU		0x20000000	/* data fifo underrun */
+#define	D64_XS1_XE_DTE		0x30000000	/* data transfer error */
+#define	D64_XS1_XE_DESRE	0x40000000	/* descriptor read error */
+#define	D64_XS1_XE_COREE	0x50000000	/* core error */
+
+/* receive channel control */
+#define	D64_RC_RE		0x00000001	/* receive enable */
+#define	D64_RC_RO_MASK		0x000000fe	/* receive frame offset */
+#define	D64_RC_RO_SHIFT		1
+#define	D64_RC_FM		0x00000100	/* direct fifo receive (pio) mode */
+#define	D64_RC_SH		0x00000200	/* separate rx header descriptor enable */
+#define	D64_RC_OC		0x00000400	/* overflow continue */
+#define	D64_RC_PD		0x00000800	/* parity check disable */
+#define	D64_RC_AE		0x00030000	/* address extension bits */
+#define	D64_RC_AE_SHIFT		16
+
+/* flags for dma controller */
+#define DMA_CTRL_PEN		(1 << 0)	/* partity enable */
+#define DMA_CTRL_ROC		(1 << 1)	/* rx overflow continue */
+#define DMA_CTRL_RXMULTI	(1 << 2)	/* allow rx scatter to multiple descriptors */
+#define DMA_CTRL_UNFRAMED	(1 << 3)	/* Unframed Rx/Tx data */
+
+/* receive descriptor table pointer */
+#define	D64_RP_LD_MASK		0x00000fff	/* last valid descriptor */
+
+/* receive channel status */
+#define	D64_RS0_CD_MASK		0x00001fff	/* current descriptor pointer */
+#define	D64_RS0_RS_MASK		0xf0000000	/* receive state */
+#define	D64_RS0_RS_SHIFT		28
+#define	D64_RS0_RS_DISABLED	0x00000000	/* disabled */
+#define	D64_RS0_RS_ACTIVE	0x10000000	/* active */
+#define	D64_RS0_RS_IDLE		0x20000000	/* idle wait */
+#define	D64_RS0_RS_STOPPED	0x30000000	/* stopped */
+#define	D64_RS0_RS_SUSP		0x40000000	/* suspend pending */
+
+#define	D64_RS1_AD_MASK		0x0001ffff	/* active descriptor */
+#define	D64_RS1_RE_MASK		0xf0000000	/* receive errors */
+#define	D64_RS1_RE_SHIFT		28
+#define	D64_RS1_RE_NOERR	0x00000000	/* no error */
+#define	D64_RS1_RE_DPO		0x10000000	/* descriptor protocol error */
+#define	D64_RS1_RE_DFU		0x20000000	/* data fifo overflow */
+#define	D64_RS1_RE_DTE		0x30000000	/* data transfer error */
+#define	D64_RS1_RE_DESRE	0x40000000	/* descriptor read error */
+#define	D64_RS1_RE_COREE	0x50000000	/* core error */
+
+/* fifoaddr */
+#define	D64_FA_OFF_MASK		0xffff	/* offset */
+#define	D64_FA_SEL_MASK		0xf0000	/* select */
+#define	D64_FA_SEL_SHIFT	16
+#define	D64_FA_SEL_XDD		0x00000	/* transmit dma data */
+#define	D64_FA_SEL_XDP		0x10000	/* transmit dma pointers */
+#define	D64_FA_SEL_RDD		0x40000	/* receive dma data */
+#define	D64_FA_SEL_RDP		0x50000	/* receive dma pointers */
+#define	D64_FA_SEL_XFD		0x80000	/* transmit fifo data */
+#define	D64_FA_SEL_XFP		0x90000	/* transmit fifo pointers */
+#define	D64_FA_SEL_RFD		0xc0000	/* receive fifo data */
+#define	D64_FA_SEL_RFP		0xd0000	/* receive fifo pointers */
+#define	D64_FA_SEL_RSD		0xe0000	/* receive frame status data */
+#define	D64_FA_SEL_RSP		0xf0000	/* receive frame status pointers */
+
+/* descriptor control flags 1 */
+#define D64_CTRL_COREFLAGS	0x0ff00000	/* core specific flags */
+#define	D64_CTRL1_EOT		((u32)1 << 28)	/* end of descriptor table */
+#define	D64_CTRL1_IOC		((u32)1 << 29)	/* interrupt on completion */
+#define	D64_CTRL1_EOF		((u32)1 << 30)	/* end of frame */
+#define	D64_CTRL1_SOF		((u32)1 << 31)	/* start of frame */
+
+/* descriptor control flags 2 */
+#define	D64_CTRL2_BC_MASK	0x00007fff	/* buffer byte count. real data len must <= 16KB */
+#define	D64_CTRL2_AE		0x00030000	/* address extension bits */
+#define	D64_CTRL2_AE_SHIFT	16
+#define D64_CTRL2_PARITY	0x00040000	/* parity bit */
+
+/* control flags in the range [27:20] are core-specific and not defined here */
+#define	D64_CTRL_CORE_MASK	0x0ff00000
+
+#define D64_RX_FRM_STS_LEN	0x0000ffff	/* frame length mask */
+#define D64_RX_FRM_STS_OVFL	0x00800000	/* RxOverFlow */
+#define D64_RX_FRM_STS_DSCRCNT	0x0f000000  /* no. of descriptors used - 1 */
+#define D64_RX_FRM_STS_DATATYPE	0xf0000000	/* core-dependent data type */
+
+#define	DMADDRWIDTH_30  30	/* 30-bit addressing capability */
+#define	DMADDRWIDTH_32  32	/* 32-bit addressing capability */
+#define	DMADDRWIDTH_63  63	/* 64-bit addressing capability */
+#define	DMADDRWIDTH_64  64	/* 64-bit addressing capability */
+
+/* packet headroom necessary to accommodate the largest header in the system, (i.e TXOFF).
+ * By doing, we avoid the need  to allocate an extra buffer for the header when bridging to WL.
+ * There is a compile time check in wlc.c which ensure that this value is at least as big
+ * as TXOFF. This value is used in dma_rxfill (dma.c).
+ */
+
+#define BCMEXTRAHDROOM 172
+
+/* debug/trace */
+#ifdef BCMDBG
+#define	DMA_ERROR(args) \
+	do { \
+		if (!(*di->msg_level & 1)) \
+			; \
+		else \
+			printk args; \
+	} while (0)
+#define	DMA_TRACE(args) \
+	do { \
+		if (!(*di->msg_level & 2)) \
+			; \
+		else \
+			printk args; \
+	} while (0)
+#else
+#define	DMA_ERROR(args)
+#define	DMA_TRACE(args)
+#endif				/* BCMDBG */
+
+#define	DMA_NONE(args)
+
+typedef unsigned long dmaaddr_t;
+#define PHYSADDRHI(_pa) (0)
+#define PHYSADDRHISET(_pa, _val)
+#define PHYSADDRLO(_pa) ((_pa))
+#define PHYSADDRLOSET(_pa, _val) \
+	do { \
+		(_pa) = (_val);			\
+	} while (0)
+
+#define d64txregs	dregs.d64_u.txregs_64
+#define d64rxregs	dregs.d64_u.rxregs_64
+#define txd64		dregs.d64_u.txd_64
+#define rxd64		dregs.d64_u.rxd_64
+
+/* default dma message level (if input msg_level pointer is null in dma_attach()) */
+static uint dma_msg_level;
+
+#define	MAXNAMEL	8	/* 8 char names */
+
+#define	DI_INFO(dmah)	((dma_info_t *)dmah)
+
+#define R_SM(r)		(*(r))
+#define W_SM(r, v)	(*(r) = (v))
+
+/* One physical DMA segment */
+struct dma_seg {
+	dmaaddr_t addr;
+	u32 length;
+};
+
+struct dma_seg_map {
+	void *oshdmah;		/* Opaque handle for OSL to store its information */
+	uint origsize;		/* Size of the virtual packet */
+	uint nsegs;
+	struct dma_seg segs[MAX_DMA_SEGS];
+};
+
+/*
+ * DMA Descriptor
+ * Descriptors are only read by the hardware, never written back.
+ */
+struct dma64desc {
+	u32 ctrl1;		/* misc control bits & bufcount */
+	u32 ctrl2;		/* buffer count and address extension */
+	u32 addrlow;		/* memory address of the date buffer, bits 31:0 */
+	u32 addrhigh;	/* memory address of the date buffer, bits 63:32 */
+};
+
+/* dma engine software state */
+struct dma_info {
+	struct dma_pub dma; /* exported structure */
+	uint *msg_level;	/* message level pointer */
+	char name[MAXNAMEL];	/* callers name for diag msgs */
+
+	void *pbus;		/* bus handle */
+
+	bool dma64;		/* this dma engine is operating in 64-bit mode */
+	bool addrext;		/* this dma engine supports DmaExtendedAddrChanges */
+
+	union {
+		struct {
+			dma64regs_t *txregs_64;	/* 64-bit dma tx engine registers */
+			dma64regs_t *rxregs_64;	/* 64-bit dma rx engine registers */
+			/* pointer to dma64 tx descriptor ring */
+			struct dma64desc *txd_64;
+			/* pointer to dma64 rx descriptor ring */
+			struct dma64desc *rxd_64;
+		} d64_u;
+	} dregs;
+
+	u16 dmadesc_align;	/* alignment requirement for dma descriptors */
+
+	u16 ntxd;		/* # tx descriptors tunable */
+	u16 txin;		/* index of next descriptor to reclaim */
+	u16 txout;		/* index of next descriptor to post */
+	void **txp;		/* pointer to parallel array of pointers to packets */
+	struct dma_seg_map *txp_dmah;	/* DMA MAP meta-data handle */
+	dmaaddr_t txdpa;	/* Aligned physical address of descriptor ring */
+	dmaaddr_t txdpaorig;	/* Original physical address of descriptor ring */
+	u16 txdalign;	/* #bytes added to alloc'd mem to align txd */
+	u32 txdalloc;	/* #bytes allocated for the ring */
+	u32 xmtptrbase;	/* When using unaligned descriptors, the ptr register
+				 * is not just an index, it needs all 13 bits to be
+				 * an offset from the addr register.
+				 */
+
+	u16 nrxd;		/* # rx descriptors tunable */
+	u16 rxin;		/* index of next descriptor to reclaim */
+	u16 rxout;		/* index of next descriptor to post */
+	void **rxp;		/* pointer to parallel array of pointers to packets */
+	struct dma_seg_map *rxp_dmah;	/* DMA MAP meta-data handle */
+	dmaaddr_t rxdpa;	/* Aligned physical address of descriptor ring */
+	dmaaddr_t rxdpaorig;	/* Original physical address of descriptor ring */
+	u16 rxdalign;	/* #bytes added to alloc'd mem to align rxd */
+	u32 rxdalloc;	/* #bytes allocated for the ring */
+	u32 rcvptrbase;	/* Base for ptr reg when using unaligned descriptors */
+
+	/* tunables */
+	unsigned int rxbufsize;	/* rx buffer size in bytes,
+				 * not including the extra headroom
+				 */
+	uint rxextrahdrroom;	/* extra rx headroom, reverseved to assist upper stack
+				 *  e.g. some rx pkt buffers will be bridged to tx side
+				 *  without byte copying. The extra headroom needs to be
+				 *  large enough to fit txheader needs.
+				 *  Some dongle driver may not need it.
+				 */
+	uint nrxpost;		/* # rx buffers to keep posted */
+	unsigned int rxoffset;	/* rxcontrol offset */
+	uint ddoffsetlow;	/* add to get dma address of descriptor ring, low 32 bits */
+	uint ddoffsethigh;	/*   high 32 bits */
+	uint dataoffsetlow;	/* add to get dma address of data buffer, low 32 bits */
+	uint dataoffsethigh;	/*   high 32 bits */
+	bool aligndesc_4k;	/* descriptor base need to be aligned or not */
+};
+
+/* DMA Scatter-gather list is supported. Note this is limited to TX direction only */
+#ifdef BCMDMASGLISTOSL
+#define DMASGLIST_ENAB true
+#else
+#define DMASGLIST_ENAB false
+#endif				/* BCMDMASGLISTOSL */
+
+/* descriptor bumping macros */
+#define	XXD(x, n)	((x) & ((n) - 1))	/* faster than %, but n must be power of 2 */
+#define	TXD(x)		XXD((x), di->ntxd)
+#define	RXD(x)		XXD((x), di->nrxd)
+#define	NEXTTXD(i)	TXD((i) + 1)
+#define	PREVTXD(i)	TXD((i) - 1)
+#define	NEXTRXD(i)	RXD((i) + 1)
+#define	PREVRXD(i)	RXD((i) - 1)
+
+#define	NTXDACTIVE(h, t)	TXD((t) - (h))
+#define	NRXDACTIVE(h, t)	RXD((t) - (h))
+
+/* macros to convert between byte offsets and indexes */
+#define	B2I(bytes, type)	((bytes) / sizeof(type))
+#define	I2B(index, type)	((index) * sizeof(type))
+
+#define	PCI32ADDR_HIGH		0xc0000000	/* address[31:30] */
+#define	PCI32ADDR_HIGH_SHIFT	30	/* address[31:30] */
+
+#define	PCI64ADDR_HIGH		0x80000000	/* address[63] */
+#define	PCI64ADDR_HIGH_SHIFT	31	/* address[63] */
+
+/* Common prototypes */
+static bool _dma_isaddrext(struct dma_info *di);
+static bool _dma_descriptor_align(struct dma_info *di);
+static bool _dma_alloc(struct dma_info *di, uint direction);
+static void _dma_detach(struct dma_info *di);
+static void _dma_ddtable_init(struct dma_info *di, uint direction,
+			      dmaaddr_t pa);
+static void _dma_rxinit(struct dma_info *di);
+static void *_dma_rx(struct dma_info *di);
+static bool _dma_rxfill(struct dma_info *di);
+static void _dma_rxreclaim(struct dma_info *di);
+static void _dma_rxenable(struct dma_info *di);
+static void *_dma_getnextrxp(struct dma_info *di, bool forceall);
+static void _dma_rx_param_get(struct dma_info *di, u16 *rxoffset,
+			      u16 *rxbufsize);
+
+static void _dma_txblock(struct dma_info *di);
+static void _dma_txunblock(struct dma_info *di);
+static uint _dma_txactive(struct dma_info *di);
+static uint _dma_rxactive(struct dma_info *di);
+static uint _dma_txpending(struct dma_info *di);
+static uint _dma_txcommitted(struct dma_info *di);
+
+static void *_dma_peeknexttxp(struct dma_info *di);
+static void *_dma_peeknextrxp(struct dma_info *di);
+static unsigned long _dma_getvar(struct dma_info *di, const char *name);
+static void _dma_counterreset(struct dma_info *di);
+static void _dma_fifoloopbackenable(struct dma_info *di);
+static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags);
+static u8 dma_align_sizetobits(uint size);
+static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size,
+			   u16 *alignbits, uint *alloced,
+			   dmaaddr_t *descpa);
+
+/* Prototypes for 64-bit routines */
+static bool dma64_alloc(struct dma_info *di, uint direction);
+static bool dma64_txreset(struct dma_info *di);
+static bool dma64_rxreset(struct dma_info *di);
+static bool dma64_txsuspendedidle(struct dma_info *di);
+static int dma64_txfast(struct dma_info *di, struct sk_buff *p0, bool commit);
+static int dma64_txunframed(struct dma_info *di, void *p0, uint len,
+			    bool commit);
+static void *dma64_getpos(struct dma_info *di, bool direction);
+static void *dma64_getnexttxp(struct dma_info *di, enum txd_range range);
+static void *dma64_getnextrxp(struct dma_info *di, bool forceall);
+static void dma64_txrotate(struct dma_info *di);
+
+static bool dma64_rxidle(struct dma_info *di);
+static void dma64_txinit(struct dma_info *di);
+static bool dma64_txenabled(struct dma_info *di);
+static void dma64_txsuspend(struct dma_info *di);
+static void dma64_txresume(struct dma_info *di);
+static bool dma64_txsuspended(struct dma_info *di);
+static void dma64_txreclaim(struct dma_info *di, enum txd_range range);
+static bool dma64_txstopped(struct dma_info *di);
+static bool dma64_rxstopped(struct dma_info *di);
+static bool dma64_rxenabled(struct dma_info *di);
+static bool _dma64_addrext(dma64regs_t *dma64regs);
+
+static inline u32 parity32(u32 data);
+
+const struct di_fcn_s dma64proc = {
+	(di_detach_t) _dma_detach,
+	(di_txinit_t) dma64_txinit,
+	(di_txreset_t) dma64_txreset,
+	(di_txenabled_t) dma64_txenabled,
+	(di_txsuspend_t) dma64_txsuspend,
+	(di_txresume_t) dma64_txresume,
+	(di_txsuspended_t) dma64_txsuspended,
+	(di_txsuspendedidle_t) dma64_txsuspendedidle,
+	(di_txfast_t) dma64_txfast,
+	(di_txunframed_t) dma64_txunframed,
+	(di_getpos_t) dma64_getpos,
+	(di_txstopped_t) dma64_txstopped,
+	(di_txreclaim_t) dma64_txreclaim,
+	(di_getnexttxp_t) dma64_getnexttxp,
+	(di_peeknexttxp_t) _dma_peeknexttxp,
+	(di_txblock_t) _dma_txblock,
+	(di_txunblock_t) _dma_txunblock,
+	(di_txactive_t) _dma_txactive,
+	(di_txrotate_t) dma64_txrotate,
+
+	(di_rxinit_t) _dma_rxinit,
+	(di_rxreset_t) dma64_rxreset,
+	(di_rxidle_t) dma64_rxidle,
+	(di_rxstopped_t) dma64_rxstopped,
+	(di_rxenable_t) _dma_rxenable,
+	(di_rxenabled_t) dma64_rxenabled,
+	(di_rx_t) _dma_rx,
+	(di_rxfill_t) _dma_rxfill,
+	(di_rxreclaim_t) _dma_rxreclaim,
+	(di_getnextrxp_t) _dma_getnextrxp,
+	(di_peeknextrxp_t) _dma_peeknextrxp,
+	(di_rxparam_get_t) _dma_rx_param_get,
+
+	(di_fifoloopbackenable_t) _dma_fifoloopbackenable,
+	(di_getvar_t) _dma_getvar,
+	(di_counterreset_t) _dma_counterreset,
+	(di_ctrlflags_t) _dma_ctrlflags,
+	NULL,
+	NULL,
+	NULL,
+	(di_rxactive_t) _dma_rxactive,
+	(di_txpending_t) _dma_txpending,
+	(di_txcommitted_t) _dma_txcommitted,
+	39
+};
+
+struct dma_pub *dma_attach(char *name, struct si_pub *sih,
+		     void *dmaregstx, void *dmaregsrx, uint ntxd,
+		     uint nrxd, uint rxbufsize, int rxextheadroom,
+		     uint nrxpost, uint rxoffset, uint *msg_level)
+{
+	struct dma_info *di;
+	uint size;
+
+	/* allocate private info structure */
+	di = kzalloc(sizeof(struct dma_info), GFP_ATOMIC);
+	if (di == NULL) {
+#ifdef BCMDBG
+		printk(KERN_ERR "dma_attach: out of memory\n");
+#endif
+		return NULL;
+	}
+
+	di->msg_level = msg_level ? msg_level : &dma_msg_level;
+
+
+	di->dma64 = ((ai_core_sflags(sih, 0, 0) & SISF_DMA64) == SISF_DMA64);
+
+	/* init dma reg pointer */
+	di->d64txregs = (dma64regs_t *) dmaregstx;
+	di->d64rxregs = (dma64regs_t *) dmaregsrx;
+	di->dma.di_fn = (const struct di_fcn_s *)&dma64proc;
+
+	/* Default flags (which can be changed by the driver calling dma_ctrlflags
+	 * before enable): For backwards compatibility both Rx Overflow Continue
+	 * and Parity are DISABLED.
+	 * supports it.
+	 */
+	di->dma.di_fn->ctrlflags(&di->dma, DMA_CTRL_ROC | DMA_CTRL_PEN,
+				 0);
+
+	DMA_TRACE(("%s: dma_attach: %s flags 0x%x ntxd %d nrxd %d "
+		   "rxbufsize %d rxextheadroom %d nrxpost %d rxoffset %d "
+		   "dmaregstx %p dmaregsrx %p\n", name, "DMA64",
+		   di->dma.dmactrlflags, ntxd, nrxd, rxbufsize,
+		   rxextheadroom, nrxpost, rxoffset, dmaregstx, dmaregsrx));
+
+	/* make a private copy of our callers name */
+	strncpy(di->name, name, MAXNAMEL);
+	di->name[MAXNAMEL - 1] = '\0';
+
+	di->pbus = ((struct si_info *)sih)->pbus;
+
+	/* save tunables */
+	di->ntxd = (u16) ntxd;
+	di->nrxd = (u16) nrxd;
+
+	/* the actual dma size doesn't include the extra headroom */
+	di->rxextrahdrroom =
+	    (rxextheadroom == -1) ? BCMEXTRAHDROOM : rxextheadroom;
+	if (rxbufsize > BCMEXTRAHDROOM)
+		di->rxbufsize = (u16) (rxbufsize - di->rxextrahdrroom);
+	else
+		di->rxbufsize = (u16) rxbufsize;
+
+	di->nrxpost = (u16) nrxpost;
+	di->rxoffset = (u8) rxoffset;
+
+	/*
+	 * figure out the DMA physical address offset for dd and data
+	 *     PCI/PCIE: they map silicon backplace address to zero based memory, need offset
+	 *     Other bus: use zero
+	 *     SI_BUS BIGENDIAN kludge: use sdram swapped region for data buffer, not descriptor
+	 */
+	di->ddoffsetlow = 0;
+	di->dataoffsetlow = 0;
+	/* for pci bus, add offset */
+	if (sih->bustype == PCI_BUS) {
+		/* pcie with DMA64 */
+		di->ddoffsetlow = 0;
+		di->ddoffsethigh = SI_PCIE_DMA_H32;
+		di->dataoffsetlow = di->ddoffsetlow;
+		di->dataoffsethigh = di->ddoffsethigh;
+	}
+#if defined(__mips__) && defined(IL_BIGENDIAN)
+	di->dataoffsetlow = di->dataoffsetlow + SI_SDRAM_SWAPPED;
+#endif				/* defined(__mips__) && defined(IL_BIGENDIAN) */
+	/* WAR64450 : DMACtl.Addr ext fields are not supported in SDIOD core. */
+	if ((ai_coreid(sih) == SDIOD_CORE_ID)
+	    && ((ai_corerev(sih) > 0) && (ai_corerev(sih) <= 2)))
+		di->addrext = 0;
+	else if ((ai_coreid(sih) == I2S_CORE_ID) &&
+		 ((ai_corerev(sih) == 0) || (ai_corerev(sih) == 1)))
+		di->addrext = 0;
+	else
+		di->addrext = _dma_isaddrext(di);
+
+	/* does the descriptors need to be aligned and if yes, on 4K/8K or not */
+	di->aligndesc_4k = _dma_descriptor_align(di);
+	if (di->aligndesc_4k) {
+		di->dmadesc_align = D64RINGALIGN_BITS;
+		if ((ntxd < D64MAXDD / 2) && (nrxd < D64MAXDD / 2)) {
+			/* for smaller dd table, HW relax alignment reqmnt */
+			di->dmadesc_align = D64RINGALIGN_BITS - 1;
+		}
+	} else
+		di->dmadesc_align = 4;	/* 16 byte alignment */
+
+	DMA_NONE(("DMA descriptor align_needed %d, align %d\n",
+		  di->aligndesc_4k, di->dmadesc_align));
+
+	/* allocate tx packet pointer vector */
+	if (ntxd) {
+		size = ntxd * sizeof(void *);
+		di->txp = kzalloc(size, GFP_ATOMIC);
+		if (di->txp == NULL) {
+			DMA_ERROR(("%s: dma_attach: out of tx memory\n", di->name));
+			goto fail;
+		}
+	}
+
+	/* allocate rx packet pointer vector */
+	if (nrxd) {
+		size = nrxd * sizeof(void *);
+		di->rxp = kzalloc(size, GFP_ATOMIC);
+		if (di->rxp == NULL) {
+			DMA_ERROR(("%s: dma_attach: out of rx memory\n", di->name));
+			goto fail;
+		}
+	}
+
+	/* allocate transmit descriptor ring, only need ntxd descriptors but it must be aligned */
+	if (ntxd) {
+		if (!_dma_alloc(di, DMA_TX))
+			goto fail;
+	}
+
+	/* allocate receive descriptor ring, only need nrxd descriptors but it must be aligned */
+	if (nrxd) {
+		if (!_dma_alloc(di, DMA_RX))
+			goto fail;
+	}
+
+	if ((di->ddoffsetlow != 0) && !di->addrext) {
+		if (PHYSADDRLO(di->txdpa) > SI_PCI_DMA_SZ) {
+			DMA_ERROR(("%s: dma_attach: txdpa 0x%x: addrext not supported\n", di->name, (u32) PHYSADDRLO(di->txdpa)));
+			goto fail;
+		}
+		if (PHYSADDRLO(di->rxdpa) > SI_PCI_DMA_SZ) {
+			DMA_ERROR(("%s: dma_attach: rxdpa 0x%x: addrext not supported\n", di->name, (u32) PHYSADDRLO(di->rxdpa)));
+			goto fail;
+		}
+	}
+
+	DMA_TRACE(("ddoffsetlow 0x%x ddoffsethigh 0x%x dataoffsetlow 0x%x dataoffsethigh " "0x%x addrext %d\n", di->ddoffsetlow, di->ddoffsethigh, di->dataoffsetlow, di->dataoffsethigh, di->addrext));
+
+	/* allocate DMA mapping vectors */
+	if (DMASGLIST_ENAB) {
+		if (ntxd) {
+			size = ntxd * sizeof(struct dma_seg_map);
+			di->txp_dmah = kzalloc(size, GFP_ATOMIC);
+			if (di->txp_dmah == NULL)
+				goto fail;
+		}
+
+		if (nrxd) {
+			size = nrxd * sizeof(struct dma_seg_map);
+			di->rxp_dmah = kzalloc(size, GFP_ATOMIC);
+			if (di->rxp_dmah == NULL)
+				goto fail;
+		}
+	}
+
+	return (struct dma_pub *) di;
+
+ fail:
+	_dma_detach(di);
+	return NULL;
+}
+
+/* Check for odd number of 1's */
+static inline u32 parity32(u32 data)
+{
+	data ^= data >> 16;
+	data ^= data >> 8;
+	data ^= data >> 4;
+	data ^= data >> 2;
+	data ^= data >> 1;
+
+	return data & 1;
+}
+
+#define DMA64_DD_PARITY(dd)  parity32((dd)->addrlow ^ (dd)->addrhigh ^ (dd)->ctrl1 ^ (dd)->ctrl2)
+
+static inline void
+dma64_dd_upd(struct dma_info *di, struct dma64desc *ddring,
+	     dmaaddr_t pa, uint outidx, u32 *flags, u32 bufcount)
+{
+	u32 ctrl2 = bufcount & D64_CTRL2_BC_MASK;
+
+	/* PCI bus with big(>1G) physical address, use address extension */
+#if defined(__mips__) && defined(IL_BIGENDIAN)
+	if ((di->dataoffsetlow == SI_SDRAM_SWAPPED)
+	    || !(PHYSADDRLO(pa) & PCI32ADDR_HIGH)) {
+#else
+	if ((di->dataoffsetlow == 0) || !(PHYSADDRLO(pa) & PCI32ADDR_HIGH)) {
+#endif				/* defined(__mips__) && defined(IL_BIGENDIAN) */
+
+		W_SM(&ddring[outidx].addrlow,
+		     BUS_SWAP32(PHYSADDRLO(pa) + di->dataoffsetlow));
+		W_SM(&ddring[outidx].addrhigh,
+		     BUS_SWAP32(PHYSADDRHI(pa) + di->dataoffsethigh));
+		W_SM(&ddring[outidx].ctrl1, BUS_SWAP32(*flags));
+		W_SM(&ddring[outidx].ctrl2, BUS_SWAP32(ctrl2));
+	} else {
+		/* address extension for 32-bit PCI */
+		u32 ae;
+
+		ae = (PHYSADDRLO(pa) & PCI32ADDR_HIGH) >> PCI32ADDR_HIGH_SHIFT;
+		PHYSADDRLO(pa) &= ~PCI32ADDR_HIGH;
+
+		ctrl2 |= (ae << D64_CTRL2_AE_SHIFT) & D64_CTRL2_AE;
+		W_SM(&ddring[outidx].addrlow,
+		     BUS_SWAP32(PHYSADDRLO(pa) + di->dataoffsetlow));
+		W_SM(&ddring[outidx].addrhigh,
+		     BUS_SWAP32(0 + di->dataoffsethigh));
+		W_SM(&ddring[outidx].ctrl1, BUS_SWAP32(*flags));
+		W_SM(&ddring[outidx].ctrl2, BUS_SWAP32(ctrl2));
+	}
+	if (di->dma.dmactrlflags & DMA_CTRL_PEN) {
+		if (DMA64_DD_PARITY(&ddring[outidx])) {
+			W_SM(&ddring[outidx].ctrl2,
+			     BUS_SWAP32(ctrl2 | D64_CTRL2_PARITY));
+		}
+	}
+}
+
+static bool _dma_alloc(struct dma_info *di, uint direction)
+{
+	return dma64_alloc(di, direction);
+}
+
+void *dma_alloc_consistent(struct pci_dev *pdev, uint size, u16 align_bits,
+			       uint *alloced, unsigned long *pap)
+{
+	if (align_bits) {
+		u16 align = (1 << align_bits);
+		if (!IS_ALIGNED(PAGE_SIZE, align))
+			size += align;
+		*alloced = size;
+	}
+	return pci_alloc_consistent(pdev, size, (dma_addr_t *) pap);
+}
+
+/* !! may be called with core in reset */
+static void _dma_detach(struct dma_info *di)
+{
+
+	DMA_TRACE(("%s: dma_detach\n", di->name));
+
+	/* free dma descriptor rings */
+	if (di->txd64)
+		pci_free_consistent(di->pbus, di->txdalloc,
+				    ((s8 *)di->txd64 - di->txdalign),
+				    (di->txdpaorig));
+	if (di->rxd64)
+		pci_free_consistent(di->pbus, di->rxdalloc,
+				    ((s8 *)di->rxd64 - di->rxdalign),
+				    (di->rxdpaorig));
+
+	/* free packet pointer vectors */
+	kfree(di->txp);
+	kfree(di->rxp);
+
+	/* free tx packet DMA handles */
+	kfree(di->txp_dmah);
+
+	/* free rx packet DMA handles */
+	kfree(di->rxp_dmah);
+
+	/* free our private info structure */
+	kfree(di);
+
+}
+
+static bool _dma_descriptor_align(struct dma_info *di)
+{
+	u32 addrl;
+
+	/* Check to see if the descriptors need to be aligned on 4K/8K or not */
+	if (di->d64txregs != NULL) {
+		W_REG(&di->d64txregs->addrlow, 0xff0);
+		addrl = R_REG(&di->d64txregs->addrlow);
+		if (addrl != 0)
+			return false;
+	} else if (di->d64rxregs != NULL) {
+		W_REG(&di->d64rxregs->addrlow, 0xff0);
+		addrl = R_REG(&di->d64rxregs->addrlow);
+		if (addrl != 0)
+			return false;
+	}
+	return true;
+}
+
+/* return true if this dma engine supports DmaExtendedAddrChanges, otherwise false */
+static bool _dma_isaddrext(struct dma_info *di)
+{
+	/* DMA64 supports full 32- or 64-bit operation. AE is always valid */
+
+	/* not all tx or rx channel are available */
+	if (di->d64txregs != NULL) {
+		if (!_dma64_addrext(di->d64txregs)) {
+			DMA_ERROR(("%s: _dma_isaddrext: DMA64 tx doesn't have "
+				   "AE set\n", di->name));
+		}
+		return true;
+	} else if (di->d64rxregs != NULL) {
+		if (!_dma64_addrext(di->d64rxregs)) {
+			DMA_ERROR(("%s: _dma_isaddrext: DMA64 rx doesn't have "
+				   "AE set\n", di->name));
+		}
+		return true;
+	}
+	return false;
+}
+
+/* initialize descriptor table base address */
+static void _dma_ddtable_init(struct dma_info *di, uint direction, dmaaddr_t pa)
+{
+	if (!di->aligndesc_4k) {
+		if (direction == DMA_TX)
+			di->xmtptrbase = PHYSADDRLO(pa);
+		else
+			di->rcvptrbase = PHYSADDRLO(pa);
+	}
+
+	if ((di->ddoffsetlow == 0)
+	    || !(PHYSADDRLO(pa) & PCI32ADDR_HIGH)) {
+		if (direction == DMA_TX) {
+			W_REG(&di->d64txregs->addrlow,
+			      (PHYSADDRLO(pa) + di->ddoffsetlow));
+			W_REG(&di->d64txregs->addrhigh,
+			      (PHYSADDRHI(pa) + di->ddoffsethigh));
+		} else {
+			W_REG(&di->d64rxregs->addrlow,
+			      (PHYSADDRLO(pa) + di->ddoffsetlow));
+			W_REG(&di->d64rxregs->addrhigh,
+				(PHYSADDRHI(pa) + di->ddoffsethigh));
+		}
+	} else {
+		/* DMA64 32bits address extension */
+		u32 ae;
+
+		/* shift the high bit(s) from pa to ae */
+		ae = (PHYSADDRLO(pa) & PCI32ADDR_HIGH) >>
+		    PCI32ADDR_HIGH_SHIFT;
+		PHYSADDRLO(pa) &= ~PCI32ADDR_HIGH;
+
+		if (direction == DMA_TX) {
+			W_REG(&di->d64txregs->addrlow,
+			      (PHYSADDRLO(pa) + di->ddoffsetlow));
+			W_REG(&di->d64txregs->addrhigh,
+			      di->ddoffsethigh);
+			SET_REG(&di->d64txregs->control,
+				D64_XC_AE, (ae << D64_XC_AE_SHIFT));
+		} else {
+			W_REG(&di->d64rxregs->addrlow,
+			      (PHYSADDRLO(pa) + di->ddoffsetlow));
+			W_REG(&di->d64rxregs->addrhigh,
+			      di->ddoffsethigh);
+			SET_REG(&di->d64rxregs->control,
+				D64_RC_AE, (ae << D64_RC_AE_SHIFT));
+		}
+	}
+}
+
+static void _dma_fifoloopbackenable(struct dma_info *di)
+{
+	DMA_TRACE(("%s: dma_fifoloopbackenable\n", di->name));
+
+	OR_REG(&di->d64txregs->control, D64_XC_LE);
+}
+
+static void _dma_rxinit(struct dma_info *di)
+{
+	DMA_TRACE(("%s: dma_rxinit\n", di->name));
+
+	if (di->nrxd == 0)
+		return;
+
+	di->rxin = di->rxout = 0;
+
+	/* clear rx descriptor ring */
+	memset((void *)di->rxd64, '\0',
+		(di->nrxd * sizeof(struct dma64desc)));
+
+	/* DMA engine with out alignment requirement requires table to be inited
+	 * before enabling the engine
+	 */
+	if (!di->aligndesc_4k)
+		_dma_ddtable_init(di, DMA_RX, di->rxdpa);
+
+	_dma_rxenable(di);
+
+	if (di->aligndesc_4k)
+		_dma_ddtable_init(di, DMA_RX, di->rxdpa);
+}
+
+static void _dma_rxenable(struct dma_info *di)
+{
+	uint dmactrlflags = di->dma.dmactrlflags;
+	u32 control;
+
+	DMA_TRACE(("%s: dma_rxenable\n", di->name));
+
+	control =
+	    (R_REG(&di->d64rxregs->control) & D64_RC_AE) |
+	    D64_RC_RE;
+
+	if ((dmactrlflags & DMA_CTRL_PEN) == 0)
+		control |= D64_RC_PD;
+
+	if (dmactrlflags & DMA_CTRL_ROC)
+		control |= D64_RC_OC;
+
+	W_REG(&di->d64rxregs->control,
+		((di->rxoffset << D64_RC_RO_SHIFT) | control));
+}
+
+static void
+_dma_rx_param_get(struct dma_info *di, u16 *rxoffset, u16 *rxbufsize)
+{
+	/* the normal values fit into 16 bits */
+	*rxoffset = (u16) di->rxoffset;
+	*rxbufsize = (u16) di->rxbufsize;
+}
+
+/* !! rx entry routine
+ * returns a pointer to the next frame received, or NULL if there are no more
+ *   if DMA_CTRL_RXMULTI is defined, DMA scattering(multiple buffers) is supported
+ *      with pkts chain
+ *   otherwise, it's treated as giant pkt and will be tossed.
+ *   The DMA scattering starts with normal DMA header, followed by first buffer data.
+ *   After it reaches the max size of buffer, the data continues in next DMA descriptor
+ *   buffer WITHOUT DMA header
+ */
+static void *_dma_rx(struct dma_info *di)
+{
+	struct sk_buff *p, *head, *tail;
+	uint len;
+	uint pkt_len;
+	int resid = 0;
+
+ next_frame:
+	head = _dma_getnextrxp(di, false);
+	if (head == NULL)
+		return NULL;
+
+	len = le16_to_cpu(*(u16 *) (head->data));
+	DMA_TRACE(("%s: dma_rx len %d\n", di->name, len));
+	dma_spin_for_len(len, head);
+
+	/* set actual length */
+	pkt_len = min((di->rxoffset + len), di->rxbufsize);
+	__skb_trim(head, pkt_len);
+	resid = len - (di->rxbufsize - di->rxoffset);
+
+	/* check for single or multi-buffer rx */
+	if (resid > 0) {
+		tail = head;
+		while ((resid > 0) && (p = _dma_getnextrxp(di, false))) {
+			tail->next = p;
+			pkt_len = min(resid, (int)di->rxbufsize);
+			__skb_trim(p, pkt_len);
+
+			tail = p;
+			resid -= di->rxbufsize;
+		}
+
+#ifdef BCMDBG
+		if (resid > 0) {
+			uint cur;
+			cur =
+			    B2I(((R_REG(&di->d64rxregs->status0) &
+				  D64_RS0_CD_MASK) -
+				 di->rcvptrbase) & D64_RS0_CD_MASK,
+				struct dma64desc);
+			DMA_ERROR(("_dma_rx, rxin %d rxout %d, hw_curr %d\n",
+				   di->rxin, di->rxout, cur));
+		}
+#endif				/* BCMDBG */
+
+		if ((di->dma.dmactrlflags & DMA_CTRL_RXMULTI) == 0) {
+			DMA_ERROR(("%s: dma_rx: bad frame length (%d)\n",
+				   di->name, len));
+			brcmu_pkt_buf_free_skb(head);
+			di->dma.rxgiants++;
+			goto next_frame;
+		}
+	}
+
+	return head;
+}
+
+/* post receive buffers
+ *  return false is refill failed completely and ring is empty
+ *  this will stall the rx dma and user might want to call rxfill again asap
+ *  This unlikely happens on memory-rich NIC, but often on memory-constrained dongle
+ */
+static bool _dma_rxfill(struct dma_info *di)
+{
+	struct sk_buff *p;
+	u16 rxin, rxout;
+	u32 flags = 0;
+	uint n;
+	uint i;
+	dmaaddr_t pa;
+	uint extra_offset = 0;
+	bool ring_empty;
+
+	ring_empty = false;
+
+	/*
+	 * Determine how many receive buffers we're lacking
+	 * from the full complement, allocate, initialize,
+	 * and post them, then update the chip rx lastdscr.
+	 */
+
+	rxin = di->rxin;
+	rxout = di->rxout;
+
+	n = di->nrxpost - NRXDACTIVE(rxin, rxout);
+
+	DMA_TRACE(("%s: dma_rxfill: post %d\n", di->name, n));
+
+	if (di->rxbufsize > BCMEXTRAHDROOM)
+		extra_offset = di->rxextrahdrroom;
+
+	for (i = 0; i < n; i++) {
+		/* the di->rxbufsize doesn't include the extra headroom, we need to add it to the
+		   size to be allocated
+		 */
+
+		p = brcmu_pkt_buf_get_skb(di->rxbufsize + extra_offset);
+
+		if (p == NULL) {
+			DMA_ERROR(("%s: dma_rxfill: out of rxbufs\n",
+				   di->name));
+			if (i == 0 && dma64_rxidle(di)) {
+				DMA_ERROR(("%s: rxfill64: ring is empty !\n",
+					   di->name));
+				ring_empty = true;
+			}
+			di->dma.rxnobuf++;
+			break;
+		}
+		/* reserve an extra headroom, if applicable */
+		if (extra_offset)
+			skb_pull(p, extra_offset);
+
+		/* Do a cached write instead of uncached write since DMA_MAP
+		 * will flush the cache.
+		 */
+		*(u32 *) (p->data) = 0;
+
+		if (DMASGLIST_ENAB)
+			memset(&di->rxp_dmah[rxout], 0,
+				sizeof(struct dma_seg_map));
+
+		pa = pci_map_single(di->pbus, p->data,
+			di->rxbufsize, PCI_DMA_FROMDEVICE);
+
+		/* save the free packet pointer */
+		di->rxp[rxout] = p;
+
+		/* reset flags for each descriptor */
+		flags = 0;
+		if (rxout == (di->nrxd - 1))
+			flags = D64_CTRL1_EOT;
+
+		dma64_dd_upd(di, di->rxd64, pa, rxout, &flags,
+			     di->rxbufsize);
+		rxout = NEXTRXD(rxout);
+	}
+
+	di->rxout = rxout;
+
+	/* update the chip lastdscr pointer */
+	W_REG(&di->d64rxregs->ptr,
+	      di->rcvptrbase + I2B(rxout, struct dma64desc));
+
+	return ring_empty;
+}
+
+/* like getnexttxp but no reclaim */
+static void *_dma_peeknexttxp(struct dma_info *di)
+{
+	uint end, i;
+
+	if (di->ntxd == 0)
+		return NULL;
+
+	end =
+	    B2I(((R_REG(&di->d64txregs->status0) &
+		  D64_XS0_CD_MASK) - di->xmtptrbase) & D64_XS0_CD_MASK,
+		  struct dma64desc);
+
+	for (i = di->txin; i != end; i = NEXTTXD(i))
+		if (di->txp[i])
+			return di->txp[i];
+
+	return NULL;
+}
+
+/* like getnextrxp but not take off the ring */
+static void *_dma_peeknextrxp(struct dma_info *di)
+{
+	uint end, i;
+
+	if (di->nrxd == 0)
+		return NULL;
+
+	end =
+	    B2I(((R_REG(&di->d64rxregs->status0) &
+		  D64_RS0_CD_MASK) - di->rcvptrbase) & D64_RS0_CD_MASK,
+		  struct dma64desc);
+
+	for (i = di->rxin; i != end; i = NEXTRXD(i))
+		if (di->rxp[i])
+			return di->rxp[i];
+
+	return NULL;
+}
+
+static void _dma_rxreclaim(struct dma_info *di)
+{
+	void *p;
+
+	DMA_TRACE(("%s: dma_rxreclaim\n", di->name));
+
+	while ((p = _dma_getnextrxp(di, true)))
+		brcmu_pkt_buf_free_skb(p);
+}
+
+static void *_dma_getnextrxp(struct dma_info *di, bool forceall)
+{
+	if (di->nrxd == 0)
+		return NULL;
+
+	return dma64_getnextrxp(di, forceall);
+}
+
+static void _dma_txblock(struct dma_info *di)
+{
+	di->dma.txavail = 0;
+}
+
+static void _dma_txunblock(struct dma_info *di)
+{
+	di->dma.txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
+}
+
+static uint _dma_txactive(struct dma_info *di)
+{
+	return NTXDACTIVE(di->txin, di->txout);
+}
+
+static uint _dma_txpending(struct dma_info *di)
+{
+	uint curr;
+
+	curr =
+	    B2I(((R_REG(&di->d64txregs->status0) &
+		  D64_XS0_CD_MASK) - di->xmtptrbase) & D64_XS0_CD_MASK,
+		  struct dma64desc);
+
+	return NTXDACTIVE(curr, di->txout);
+}
+
+static uint _dma_txcommitted(struct dma_info *di)
+{
+	uint ptr;
+	uint txin = di->txin;
+
+	if (txin == di->txout)
+		return 0;
+
+	ptr = B2I(R_REG(&di->d64txregs->ptr), struct dma64desc);
+
+	return NTXDACTIVE(di->txin, ptr);
+}
+
+static uint _dma_rxactive(struct dma_info *di)
+{
+	return NRXDACTIVE(di->rxin, di->rxout);
+}
+
+static void _dma_counterreset(struct dma_info *di)
+{
+	/* reset all software counter */
+	di->dma.rxgiants = 0;
+	di->dma.rxnobuf = 0;
+	di->dma.txnobuf = 0;
+}
+
+static uint _dma_ctrlflags(struct dma_info *di, uint mask, uint flags)
+{
+	uint dmactrlflags = di->dma.dmactrlflags;
+
+	if (di == NULL) {
+		DMA_ERROR(("%s: _dma_ctrlflags: NULL dma handle\n", di->name));
+		return 0;
+	}
+
+	dmactrlflags &= ~mask;
+	dmactrlflags |= flags;
+
+	/* If trying to enable parity, check if parity is actually supported */
+	if (dmactrlflags & DMA_CTRL_PEN) {
+		u32 control;
+
+		control = R_REG(&di->d64txregs->control);
+		W_REG(&di->d64txregs->control,
+		      control | D64_XC_PD);
+		if (R_REG(&di->d64txregs->control) & D64_XC_PD) {
+			/* We *can* disable it so it is supported,
+			 * restore control register
+			 */
+			W_REG(&di->d64txregs->control,
+			control);
+		} else {
+			/* Not supported, don't allow it to be enabled */
+			dmactrlflags &= ~DMA_CTRL_PEN;
+		}
+	}
+
+	di->dma.dmactrlflags = dmactrlflags;
+
+	return dmactrlflags;
+}
+
+/* get the address of the var in order to change later */
+static unsigned long _dma_getvar(struct dma_info *di, const char *name)
+{
+	if (!strcmp(name, "&txavail"))
+		return (unsigned long)&(di->dma.txavail);
+	return 0;
+}
+
+static
+u8 dma_align_sizetobits(uint size)
+{
+	u8 bitpos = 0;
+	while (size >>= 1) {
+		bitpos++;
+	}
+	return bitpos;
+}
+
+/* This function ensures that the DMA descriptor ring will not get allocated
+ * across Page boundary. If the allocation is done across the page boundary
+ * at the first time, then it is freed and the allocation is done at
+ * descriptor ring size aligned location. This will ensure that the ring will
+ * not cross page boundary
+ */
+static void *dma_ringalloc(struct dma_info *di, u32 boundary, uint size,
+			   u16 *alignbits, uint *alloced,
+			   dmaaddr_t *descpa)
+{
+	void *va;
+	u32 desc_strtaddr;
+	u32 alignbytes = 1 << *alignbits;
+
+	va = dma_alloc_consistent(di->pbus, size, *alignbits, alloced, descpa);
+
+	if (NULL == va)
+		return NULL;
+
+	desc_strtaddr = (u32) roundup((unsigned long)va, alignbytes);
+	if (((desc_strtaddr + size - 1) & boundary) != (desc_strtaddr
+							& boundary)) {
+		*alignbits = dma_align_sizetobits(size);
+		pci_free_consistent(di->pbus, size, va, *descpa);
+		va = dma_alloc_consistent(di->pbus, size, *alignbits,
+			alloced, descpa);
+	}
+	return va;
+}
+
+/* 64-bit DMA functions */
+
+static void dma64_txinit(struct dma_info *di)
+{
+	u32 control = D64_XC_XE;
+
+	DMA_TRACE(("%s: dma_txinit\n", di->name));
+
+	if (di->ntxd == 0)
+		return;
+
+	di->txin = di->txout = 0;
+	di->dma.txavail = di->ntxd - 1;
+
+	/* clear tx descriptor ring */
+	memset((void *)di->txd64, '\0', (di->ntxd * sizeof(struct dma64desc)));
+
+	/* DMA engine with out alignment requirement requires table to be inited
+	 * before enabling the engine
+	 */
+	if (!di->aligndesc_4k)
+		_dma_ddtable_init(di, DMA_TX, di->txdpa);
+
+	if ((di->dma.dmactrlflags & DMA_CTRL_PEN) == 0)
+		control |= D64_XC_PD;
+	OR_REG(&di->d64txregs->control, control);
+
+	/* DMA engine with alignment requirement requires table to be inited
+	 * before enabling the engine
+	 */
+	if (di->aligndesc_4k)
+		_dma_ddtable_init(di, DMA_TX, di->txdpa);
+}
+
+static bool dma64_txenabled(struct dma_info *di)
+{
+	u32 xc;
+
+	/* If the chip is dead, it is not enabled :-) */
+	xc = R_REG(&di->d64txregs->control);
+	return (xc != 0xffffffff) && (xc & D64_XC_XE);
+}
+
+static void dma64_txsuspend(struct dma_info *di)
+{
+	DMA_TRACE(("%s: dma_txsuspend\n", di->name));
+
+	if (di->ntxd == 0)
+		return;
+
+	OR_REG(&di->d64txregs->control, D64_XC_SE);
+}
+
+static void dma64_txresume(struct dma_info *di)
+{
+	DMA_TRACE(("%s: dma_txresume\n", di->name));
+
+	if (di->ntxd == 0)
+		return;
+
+	AND_REG(&di->d64txregs->control, ~D64_XC_SE);
+}
+
+static bool dma64_txsuspended(struct dma_info *di)
+{
+	return (di->ntxd == 0) ||
+	    ((R_REG(&di->d64txregs->control) & D64_XC_SE) ==
+	     D64_XC_SE);
+}
+
+static void dma64_txreclaim(struct dma_info *di, enum txd_range range)
+{
+	void *p;
+
+	DMA_TRACE(("%s: dma_txreclaim %s\n", di->name,
+		   (range == DMA_RANGE_ALL) ? "all" :
+		   ((range ==
+		     DMA_RANGE_TRANSMITTED) ? "transmitted" :
+		    "transferred")));
+
+	if (di->txin == di->txout)
+		return;
+
+	while ((p = dma64_getnexttxp(di, range))) {
+		/* For unframed data, we don't have any packets to free */
+		if (!(di->dma.dmactrlflags & DMA_CTRL_UNFRAMED))
+			brcmu_pkt_buf_free_skb(p);
+	}
+}
+
+static bool dma64_txstopped(struct dma_info *di)
+{
+	return ((R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK) ==
+		D64_XS0_XS_STOPPED);
+}
+
+static bool dma64_rxstopped(struct dma_info *di)
+{
+	return ((R_REG(&di->d64rxregs->status0) & D64_RS0_RS_MASK) ==
+		D64_RS0_RS_STOPPED);
+}
+
+static bool dma64_alloc(struct dma_info *di, uint direction)
+{
+	u16 size;
+	uint ddlen;
+	void *va;
+	uint alloced = 0;
+	u16 align;
+	u16 align_bits;
+
+	ddlen = sizeof(struct dma64desc);
+
+	size = (direction == DMA_TX) ? (di->ntxd * ddlen) : (di->nrxd * ddlen);
+	align_bits = di->dmadesc_align;
+	align = (1 << align_bits);
+
+	if (direction == DMA_TX) {
+		va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits,
+			&alloced, &di->txdpaorig);
+		if (va == NULL) {
+			DMA_ERROR(("%s: dma64_alloc: DMA_ALLOC_CONSISTENT(ntxd) failed\n", di->name));
+			return false;
+		}
+		align = (1 << align_bits);
+		di->txd64 = (struct dma64desc *)
+					roundup((unsigned long)va, align);
+		di->txdalign = (uint) ((s8 *)di->txd64 - (s8 *) va);
+		PHYSADDRLOSET(di->txdpa,
+			      PHYSADDRLO(di->txdpaorig) + di->txdalign);
+		PHYSADDRHISET(di->txdpa, PHYSADDRHI(di->txdpaorig));
+		di->txdalloc = alloced;
+	} else {
+		va = dma_ringalloc(di, D64RINGALIGN, size, &align_bits,
+			&alloced, &di->rxdpaorig);
+		if (va == NULL) {
+			DMA_ERROR(("%s: dma64_alloc: DMA_ALLOC_CONSISTENT(nrxd) failed\n", di->name));
+			return false;
+		}
+		align = (1 << align_bits);
+		di->rxd64 = (struct dma64desc *)
+					roundup((unsigned long)va, align);
+		di->rxdalign = (uint) ((s8 *)di->rxd64 - (s8 *) va);
+		PHYSADDRLOSET(di->rxdpa,
+			      PHYSADDRLO(di->rxdpaorig) + di->rxdalign);
+		PHYSADDRHISET(di->rxdpa, PHYSADDRHI(di->rxdpaorig));
+		di->rxdalloc = alloced;
+	}
+
+	return true;
+}
+
+static bool dma64_txreset(struct dma_info *di)
+{
+	u32 status;
+
+	if (di->ntxd == 0)
+		return true;
+
+	/* suspend tx DMA first */
+	W_REG(&di->d64txregs->control, D64_XC_SE);
+	SPINWAIT(((status =
+		   (R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK))
+		  != D64_XS0_XS_DISABLED) && (status != D64_XS0_XS_IDLE)
+		 && (status != D64_XS0_XS_STOPPED), 10000);
+
+	W_REG(&di->d64txregs->control, 0);
+	SPINWAIT(((status =
+		   (R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK))
+		  != D64_XS0_XS_DISABLED), 10000);
+
+	/* wait for the last transaction to complete */
+	udelay(300);
+
+	return status == D64_XS0_XS_DISABLED;
+}
+
+static bool dma64_rxidle(struct dma_info *di)
+{
+	DMA_TRACE(("%s: dma_rxidle\n", di->name));
+
+	if (di->nrxd == 0)
+		return true;
+
+	return ((R_REG(&di->d64rxregs->status0) & D64_RS0_CD_MASK) ==
+		(R_REG(&di->d64rxregs->ptr) & D64_RS0_CD_MASK));
+}
+
+static bool dma64_rxreset(struct dma_info *di)
+{
+	u32 status;
+
+	if (di->nrxd == 0)
+		return true;
+
+	W_REG(&di->d64rxregs->control, 0);
+	SPINWAIT(((status =
+		   (R_REG(&di->d64rxregs->status0) & D64_RS0_RS_MASK))
+		  != D64_RS0_RS_DISABLED), 10000);
+
+	return status == D64_RS0_RS_DISABLED;
+}
+
+static bool dma64_rxenabled(struct dma_info *di)
+{
+	u32 rc;
+
+	rc = R_REG(&di->d64rxregs->control);
+	return (rc != 0xffffffff) && (rc & D64_RC_RE);
+}
+
+static bool dma64_txsuspendedidle(struct dma_info *di)
+{
+
+	if (di->ntxd == 0)
+		return true;
+
+	if (!(R_REG(&di->d64txregs->control) & D64_XC_SE))
+		return 0;
+
+	if ((R_REG(&di->d64txregs->status0) & D64_XS0_XS_MASK) ==
+	    D64_XS0_XS_IDLE)
+		return 1;
+
+	return 0;
+}
+
+/* Useful when sending unframed data.  This allows us to get a progress report from the DMA.
+ * We return a pointer to the beginning of the DATA buffer of the current descriptor.
+ * If DMA is idle, we return NULL.
+ */
+static void *dma64_getpos(struct dma_info *di, bool direction)
+{
+	void *va;
+	bool idle;
+	u32 cd_offset;
+
+	if (direction == DMA_TX) {
+		cd_offset =
+		    R_REG(&di->d64txregs->status0) & D64_XS0_CD_MASK;
+		idle = !NTXDACTIVE(di->txin, di->txout);
+		va = di->txp[B2I(cd_offset, struct dma64desc)];
+	} else {
+		cd_offset =
+		    R_REG(&di->d64rxregs->status0) & D64_XS0_CD_MASK;
+		idle = !NRXDACTIVE(di->rxin, di->rxout);
+		va = di->rxp[B2I(cd_offset, struct dma64desc)];
+	}
+
+	/* If DMA is IDLE, return NULL */
+	if (idle) {
+		DMA_TRACE(("%s: DMA idle, return NULL\n", __func__));
+		va = NULL;
+	}
+
+	return va;
+}
+
+/* TX of unframed data
+ *
+ * Adds a DMA ring descriptor for the data pointed to by "buf".
+ * This is for DMA of a buffer of data and is unlike other dma TX functions
+ * that take a pointer to a "packet"
+ * Each call to this is results in a single descriptor being added for "len" bytes of
+ * data starting at "buf", it doesn't handle chained buffers.
+ */
+static int
+dma64_txunframed(struct dma_info *di, void *buf, uint len, bool commit)
+{
+	u16 txout;
+	u32 flags = 0;
+	dmaaddr_t pa;		/* phys addr */
+
+	txout = di->txout;
+
+	/* return nonzero if out of tx descriptors */
+	if (NEXTTXD(txout) == di->txin)
+		goto outoftxd;
+
+	if (len == 0)
+		return 0;
+
+	pa = pci_map_single(di->pbus, buf, len, PCI_DMA_TODEVICE);
+
+	flags = (D64_CTRL1_SOF | D64_CTRL1_IOC | D64_CTRL1_EOF);
+
+	if (txout == (di->ntxd - 1))
+		flags |= D64_CTRL1_EOT;
+
+	dma64_dd_upd(di, di->txd64, pa, txout, &flags, len);
+
+	/* save the buffer pointer - used by dma_getpos */
+	di->txp[txout] = buf;
+
+	txout = NEXTTXD(txout);
+	/* bump the tx descriptor index */
+	di->txout = txout;
+
+	/* kick the chip */
+	if (commit) {
+		W_REG(&di->d64txregs->ptr,
+		      di->xmtptrbase + I2B(txout, struct dma64desc));
+	}
+
+	/* tx flow control */
+	di->dma.txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
+
+	return 0;
+
+ outoftxd:
+	DMA_ERROR(("%s: %s: out of txds !!!\n", di->name, __func__));
+	di->dma.txavail = 0;
+	di->dma.txnobuf++;
+	return -1;
+}
+
+/* !! tx entry routine
+ * WARNING: call must check the return value for error.
+ *   the error(toss frames) could be fatal and cause many subsequent hard to debug problems
+ */
+static int dma64_txfast(struct dma_info *di, struct sk_buff *p0,
+				    bool commit)
+{
+	struct sk_buff *p, *next;
+	unsigned char *data;
+	uint len;
+	u16 txout;
+	u32 flags = 0;
+	dmaaddr_t pa;
+
+	DMA_TRACE(("%s: dma_txfast\n", di->name));
+
+	txout = di->txout;
+
+	/*
+	 * Walk the chain of packet buffers
+	 * allocating and initializing transmit descriptor entries.
+	 */
+	for (p = p0; p; p = next) {
+		uint nsegs, j;
+		struct dma_seg_map *map;
+
+		data = p->data;
+		len = p->len;
+		next = p->next;
+
+		/* return nonzero if out of tx descriptors */
+		if (NEXTTXD(txout) == di->txin)
+			goto outoftxd;
+
+		if (len == 0)
+			continue;
+
+		/* get physical address of buffer start */
+		if (DMASGLIST_ENAB)
+			memset(&di->txp_dmah[txout], 0,
+				sizeof(struct dma_seg_map));
+
+		pa = pci_map_single(di->pbus, data, len, PCI_DMA_TODEVICE);
+
+		if (DMASGLIST_ENAB) {
+			map = &di->txp_dmah[txout];
+
+			/* See if all the segments can be accounted for */
+			if (map->nsegs >
+			    (uint) (di->ntxd - NTXDACTIVE(di->txin, di->txout) -
+				    1))
+				goto outoftxd;
+
+			nsegs = map->nsegs;
+		} else
+			nsegs = 1;
+
+		for (j = 1; j <= nsegs; j++) {
+			flags = 0;
+			if (p == p0 && j == 1)
+				flags |= D64_CTRL1_SOF;
+
+			/* With a DMA segment list, Descriptor table is filled
+			 * using the segment list instead of looping over
+			 * buffers in multi-chain DMA. Therefore, EOF for SGLIST is when
+			 * end of segment list is reached.
+			 */
+			if ((!DMASGLIST_ENAB && next == NULL) ||
+			    (DMASGLIST_ENAB && j == nsegs))
+				flags |= (D64_CTRL1_IOC | D64_CTRL1_EOF);
+			if (txout == (di->ntxd - 1))
+				flags |= D64_CTRL1_EOT;
+
+			if (DMASGLIST_ENAB) {
+				len = map->segs[j - 1].length;
+				pa = map->segs[j - 1].addr;
+			}
+			dma64_dd_upd(di, di->txd64, pa, txout, &flags, len);
+
+			txout = NEXTTXD(txout);
+		}
+
+		/* See above. No need to loop over individual buffers */
+		if (DMASGLIST_ENAB)
+			break;
+	}
+
+	/* if last txd eof not set, fix it */
+	if (!(flags & D64_CTRL1_EOF))
+		W_SM(&di->txd64[PREVTXD(txout)].ctrl1,
+		     BUS_SWAP32(flags | D64_CTRL1_IOC | D64_CTRL1_EOF));
+
+	/* save the packet */
+	di->txp[PREVTXD(txout)] = p0;
+
+	/* bump the tx descriptor index */
+	di->txout = txout;
+
+	/* kick the chip */
+	if (commit)
+		W_REG(&di->d64txregs->ptr,
+		      di->xmtptrbase + I2B(txout, struct dma64desc));
+
+	/* tx flow control */
+	di->dma.txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
+
+	return 0;
+
+ outoftxd:
+	DMA_ERROR(("%s: dma_txfast: out of txds !!!\n", di->name));
+	brcmu_pkt_buf_free_skb(p0);
+	di->dma.txavail = 0;
+	di->dma.txnobuf++;
+	return -1;
+}
+
+/*
+ * Reclaim next completed txd (txds if using chained buffers) in the range
+ * specified and return associated packet.
+ * If range is DMA_RANGE_TRANSMITTED, reclaim descriptors that have be
+ * transmitted as noted by the hardware "CurrDescr" pointer.
+ * If range is DMA_RANGE_TRANSFERED, reclaim descriptors that have be
+ * transferred by the DMA as noted by the hardware "ActiveDescr" pointer.
+ * If range is DMA_RANGE_ALL, reclaim all txd(s) posted to the ring and
+ * return associated packet regardless of the value of hardware pointers.
+ */
+static void *dma64_getnexttxp(struct dma_info *di, enum txd_range range)
+{
+	u16 start, end, i;
+	u16 active_desc;
+	void *txp;
+
+	DMA_TRACE(("%s: dma_getnexttxp %s\n", di->name,
+		   (range == DMA_RANGE_ALL) ? "all" :
+		   ((range ==
+		     DMA_RANGE_TRANSMITTED) ? "transmitted" :
+		    "transferred")));
+
+	if (di->ntxd == 0)
+		return NULL;
+
+	txp = NULL;
+
+	start = di->txin;
+	if (range == DMA_RANGE_ALL)
+		end = di->txout;
+	else {
+		dma64regs_t *dregs = di->d64txregs;
+
+		end = (u16) (B2I(((R_REG(&dregs->status0) &
+				 D64_XS0_CD_MASK) -
+				 di->xmtptrbase) & D64_XS0_CD_MASK,
+				 struct dma64desc));
+
+		if (range == DMA_RANGE_TRANSFERED) {
+			active_desc =
+			    (u16) (R_REG(&dregs->status1) &
+				      D64_XS1_AD_MASK);
+			active_desc =
+			    (active_desc - di->xmtptrbase) & D64_XS0_CD_MASK;
+			active_desc = B2I(active_desc, struct dma64desc);
+			if (end != active_desc)
+				end = PREVTXD(active_desc);
+		}
+	}
+
+	if ((start == 0) && (end > di->txout))
+		goto bogus;
+
+	for (i = start; i != end && !txp; i = NEXTTXD(i)) {
+		dmaaddr_t pa;
+		struct dma_seg_map *map = NULL;
+		uint size, j, nsegs;
+
+		PHYSADDRLOSET(pa,
+			      (BUS_SWAP32(R_SM(&di->txd64[i].addrlow)) -
+			       di->dataoffsetlow));
+		PHYSADDRHISET(pa,
+			      (BUS_SWAP32(R_SM(&di->txd64[i].addrhigh)) -
+			       di->dataoffsethigh));
+
+		if (DMASGLIST_ENAB) {
+			map = &di->txp_dmah[i];
+			size = map->origsize;
+			nsegs = map->nsegs;
+		} else {
+			size =
+			    (BUS_SWAP32(R_SM(&di->txd64[i].ctrl2)) &
+			     D64_CTRL2_BC_MASK);
+			nsegs = 1;
+		}
+
+		for (j = nsegs; j > 0; j--) {
+			W_SM(&di->txd64[i].addrlow, 0xdeadbeef);
+			W_SM(&di->txd64[i].addrhigh, 0xdeadbeef);
+
+			txp = di->txp[i];
+			di->txp[i] = NULL;
+			if (j > 1)
+				i = NEXTTXD(i);
+		}
+
+		pci_unmap_single(di->pbus, pa, size, PCI_DMA_TODEVICE);
+	}
+
+	di->txin = i;
+
+	/* tx flow control */
+	di->dma.txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
+
+	return txp;
+
+ bogus:
+	DMA_NONE(("dma_getnexttxp: bogus curr: start %d end %d txout %d force %d\n", start, end, di->txout, forceall));
+	return NULL;
+}
+
+static void *dma64_getnextrxp(struct dma_info *di, bool forceall)
+{
+	uint i, curr;
+	void *rxp;
+	dmaaddr_t pa;
+
+	i = di->rxin;
+
+	/* return if no packets posted */
+	if (i == di->rxout)
+		return NULL;
+
+	curr =
+	    B2I(((R_REG(&di->d64rxregs->status0) & D64_RS0_CD_MASK) -
+		 di->rcvptrbase) & D64_RS0_CD_MASK, struct dma64desc);
+
+	/* ignore curr if forceall */
+	if (!forceall && (i == curr))
+		return NULL;
+
+	/* get the packet pointer that corresponds to the rx descriptor */
+	rxp = di->rxp[i];
+	di->rxp[i] = NULL;
+
+	PHYSADDRLOSET(pa,
+		      (BUS_SWAP32(R_SM(&di->rxd64[i].addrlow)) -
+		       di->dataoffsetlow));
+	PHYSADDRHISET(pa,
+		      (BUS_SWAP32(R_SM(&di->rxd64[i].addrhigh)) -
+		       di->dataoffsethigh));
+
+	/* clear this packet from the descriptor ring */
+	pci_unmap_single(di->pbus, pa, di->rxbufsize, PCI_DMA_FROMDEVICE);
+
+	W_SM(&di->rxd64[i].addrlow, 0xdeadbeef);
+	W_SM(&di->rxd64[i].addrhigh, 0xdeadbeef);
+
+	di->rxin = NEXTRXD(i);
+
+	return rxp;
+}
+
+static bool _dma64_addrext(dma64regs_t *dma64regs)
+{
+	u32 w;
+	OR_REG(&dma64regs->control, D64_XC_AE);
+	w = R_REG(&dma64regs->control);
+	AND_REG(&dma64regs->control, ~D64_XC_AE);
+	return (w & D64_XC_AE) == D64_XC_AE;
+}
+
+/*
+ * Rotate all active tx dma ring entries "forward" by (ActiveDescriptor - txin).
+ */
+static void dma64_txrotate(struct dma_info *di)
+{
+	u16 ad;
+	uint nactive;
+	uint rot;
+	u16 old, new;
+	u32 w;
+	u16 first, last;
+
+	nactive = _dma_txactive(di);
+	ad = (u16) (B2I((((R_REG(&di->d64txregs->status1) &
+			   D64_XS1_AD_MASK) - di->xmtptrbase) &
+			   D64_XS1_AD_MASK), struct dma64desc));
+	rot = TXD(ad - di->txin);
+
+	/* full-ring case is a lot harder - don't worry about this */
+	if (rot >= (di->ntxd - nactive)) {
+		DMA_ERROR(("%s: dma_txrotate: ring full - punt\n", di->name));
+		return;
+	}
+
+	first = di->txin;
+	last = PREVTXD(di->txout);
+
+	/* move entries starting at last and moving backwards to first */
+	for (old = last; old != PREVTXD(first); old = PREVTXD(old)) {
+		new = TXD(old + rot);
+
+		/*
+		 * Move the tx dma descriptor.
+		 * EOT is set only in the last entry in the ring.
+		 */
+		w = BUS_SWAP32(R_SM(&di->txd64[old].ctrl1)) & ~D64_CTRL1_EOT;
+		if (new == (di->ntxd - 1))
+			w |= D64_CTRL1_EOT;
+		W_SM(&di->txd64[new].ctrl1, BUS_SWAP32(w));
+
+		w = BUS_SWAP32(R_SM(&di->txd64[old].ctrl2));
+		W_SM(&di->txd64[new].ctrl2, BUS_SWAP32(w));
+
+		W_SM(&di->txd64[new].addrlow, R_SM(&di->txd64[old].addrlow));
+		W_SM(&di->txd64[new].addrhigh, R_SM(&di->txd64[old].addrhigh));
+
+		/* zap the old tx dma descriptor address field */
+		W_SM(&di->txd64[old].addrlow, BUS_SWAP32(0xdeadbeef));
+		W_SM(&di->txd64[old].addrhigh, BUS_SWAP32(0xdeadbeef));
+
+		/* move the corresponding txp[] entry */
+		di->txp[new] = di->txp[old];
+
+		/* Move the map */
+		if (DMASGLIST_ENAB) {
+			memcpy(&di->txp_dmah[new], &di->txp_dmah[old],
+			       sizeof(struct dma_seg_map));
+			memset(&di->txp_dmah[old], 0,
+			       sizeof(struct dma_seg_map));
+		}
+
+		di->txp[old] = NULL;
+	}
+
+	/* update txin and txout */
+	di->txin = ad;
+	di->txout = TXD(di->txout + rot);
+	di->dma.txavail = di->ntxd - NTXDACTIVE(di->txin, di->txout) - 1;
+
+	/* kick the chip */
+	W_REG(&di->d64txregs->ptr,
+	      di->xmtptrbase + I2B(di->txout, struct dma64desc));
+}
+
+uint dma_addrwidth(struct si_pub *sih, void *dmaregs)
+{
+	/* Perform 64-bit checks only if we want to advertise 64-bit (> 32bit) capability) */
+	/* DMA engine is 64-bit capable */
+	if ((ai_core_sflags(sih, 0, 0) & SISF_DMA64) == SISF_DMA64) {
+		/* backplane are 64-bit capable */
+		if (ai_backplane64(sih))
+			/* If bus is System Backplane or PCIE then we can access 64-bits */
+			if ((sih->bustype == SI_BUS) ||
+			    ((sih->bustype == PCI_BUS) &&
+			     (sih->buscoretype == PCIE_CORE_ID)))
+				return DMADDRWIDTH_64;
+	}
+	/* DMA hardware not supported by this driver*/
+	return DMADDRWIDTH_64;
+}
+
+/*
+ * Mac80211 initiated actions sometimes require packets in the DMA queue to be
+ * modified. The modified portion of the packet is not under control of the DMA
+ * engine. This function calls a caller-supplied function for each packet in
+ * the caller specified dma chain.
+ */
+void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc)
+		      (void *pkt, void *arg_a), void *arg_a)
+{
+	struct dma_info *di = (struct dma_info *) dmah;
+	uint i =   di->txin;
+	uint end = di->txout;
+	struct sk_buff *skb;
+	struct ieee80211_tx_info *tx_info;
+
+	while (i != end) {
+		skb = (struct sk_buff *)di->txp[i];
+		if (skb != NULL) {
+			tx_info = (struct ieee80211_tx_info *)skb->cb;
+			(callback_fnc)(tx_info, arg_a);
+		}
+		i = NEXTTXD(i);
+	}
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/dma.h b/drivers/net/wireless/brcm80211/brcmsmac/dma.h
new file mode 100644
index 0000000..9c8b9a6
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/dma.h
@@ -0,0 +1,250 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef	_BRCM_DMA_H_
+#define	_BRCM_DMA_H_
+
+#include "types.h"		/* forward structure declarations */
+
+/* DMA structure:
+ *  support two DMA engines: 32 bits address or 64 bit addressing
+ *  basic DMA register set is per channel(transmit or receive)
+ *  a pair of channels is defined for convenience
+ */
+
+/* 32 bits addressing */
+
+struct dma32diag {	/* diag access */
+	u32 fifoaddr;	/* diag address */
+	u32 fifodatalow;	/* low 32bits of data */
+	u32 fifodatahigh;	/* high 32bits of data */
+	u32 pad;		/* reserved */
+};
+
+/* 64 bits addressing */
+
+/* dma registers per channel(xmt or rcv) */
+struct dma64regs {
+	u32 control;		/* enable, et al */
+	u32 ptr;		/* last descriptor posted to chip */
+	u32 addrlow;		/* descriptor ring base address low 32-bits (8K aligned) */
+	u32 addrhigh;	/* descriptor ring base address bits 63:32 (8K aligned) */
+	u32 status0;		/* current descriptor, xmt state */
+	u32 status1;		/* active descriptor, xmt error */
+};
+
+/* map/unmap direction */
+#define	DMA_TX	1		/* TX direction for DMA */
+#define	DMA_RX	2		/* RX direction for DMA */
+#define BUS_SWAP32(v)		(v)
+
+/* range param for dma_getnexttxp() and dma_txreclaim */
+enum txd_range {
+	DMA_RANGE_ALL = 1,
+	DMA_RANGE_TRANSMITTED,
+	DMA_RANGE_TRANSFERED
+};
+
+/* dma function type */
+typedef void (*di_detach_t) (struct dma_pub *dmah);
+typedef bool(*di_txreset_t) (struct dma_pub *dmah);
+typedef bool(*di_rxreset_t) (struct dma_pub *dmah);
+typedef bool(*di_rxidle_t) (struct dma_pub *dmah);
+typedef void (*di_txinit_t) (struct dma_pub *dmah);
+typedef bool(*di_txenabled_t) (struct dma_pub *dmah);
+typedef void (*di_rxinit_t) (struct dma_pub *dmah);
+typedef void (*di_txsuspend_t) (struct dma_pub *dmah);
+typedef void (*di_txresume_t) (struct dma_pub *dmah);
+typedef bool(*di_txsuspended_t) (struct dma_pub *dmah);
+typedef bool(*di_txsuspendedidle_t) (struct dma_pub *dmah);
+typedef int (*di_txfast_t) (struct dma_pub *dmah, struct sk_buff *p,
+			    bool commit);
+typedef int (*di_txunframed_t) (struct dma_pub *dmah, void *p, uint len,
+				bool commit);
+typedef void *(*di_getpos_t) (struct dma_pub *di, bool direction);
+typedef void (*di_fifoloopbackenable_t) (struct dma_pub *dmah);
+typedef bool(*di_txstopped_t) (struct dma_pub *dmah);
+typedef bool(*di_rxstopped_t) (struct dma_pub *dmah);
+typedef bool(*di_rxenable_t) (struct dma_pub *dmah);
+typedef bool(*di_rxenabled_t) (struct dma_pub *dmah);
+typedef void *(*di_rx_t) (struct dma_pub *dmah);
+typedef bool(*di_rxfill_t) (struct dma_pub *dmah);
+typedef void (*di_txreclaim_t) (struct dma_pub *dmah, enum txd_range range);
+typedef void (*di_rxreclaim_t) (struct dma_pub *dmah);
+typedef unsigned long (*di_getvar_t) (struct dma_pub *dmah,
+				      const char *name);
+typedef void *(*di_getnexttxp_t) (struct dma_pub *dmah, enum txd_range range);
+typedef void *(*di_getnextrxp_t) (struct dma_pub *dmah, bool forceall);
+typedef void *(*di_peeknexttxp_t) (struct dma_pub *dmah);
+typedef void *(*di_peeknextrxp_t) (struct dma_pub *dmah);
+typedef void (*di_rxparam_get_t) (struct dma_pub *dmah, u16 *rxoffset,
+				  u16 *rxbufsize);
+typedef void (*di_txblock_t) (struct dma_pub *dmah);
+typedef void (*di_txunblock_t) (struct dma_pub *dmah);
+typedef uint(*di_txactive_t) (struct dma_pub *dmah);
+typedef void (*di_txrotate_t) (struct dma_pub *dmah);
+typedef void (*di_counterreset_t) (struct dma_pub *dmah);
+typedef uint(*di_ctrlflags_t) (struct dma_pub *dmah, uint mask, uint flags);
+typedef char *(*di_dump_t) (struct dma_pub *dmah, struct brcmu_strbuf *b,
+			    bool dumpring);
+typedef char *(*di_dumptx_t) (struct dma_pub *dmah, struct brcmu_strbuf *b,
+			      bool dumpring);
+typedef char *(*di_dumprx_t) (struct dma_pub *dmah, struct brcmu_strbuf *b,
+			      bool dumpring);
+typedef uint(*di_rxactive_t) (struct dma_pub *dmah);
+typedef uint(*di_txpending_t) (struct dma_pub *dmah);
+typedef uint(*di_txcommitted_t) (struct dma_pub *dmah);
+
+/* dma opsvec */
+struct di_fcn_s {
+	di_detach_t detach;
+	di_txinit_t txinit;
+	di_txreset_t txreset;
+	di_txenabled_t txenabled;
+	di_txsuspend_t txsuspend;
+	di_txresume_t txresume;
+	di_txsuspended_t txsuspended;
+	di_txsuspendedidle_t txsuspendedidle;
+	di_txfast_t txfast;
+	di_txunframed_t txunframed;
+	di_getpos_t getpos;
+	di_txstopped_t txstopped;
+	di_txreclaim_t txreclaim;
+	di_getnexttxp_t getnexttxp;
+	di_peeknexttxp_t peeknexttxp;
+	di_txblock_t txblock;
+	di_txunblock_t txunblock;
+	di_txactive_t txactive;
+	di_txrotate_t txrotate;
+
+	di_rxinit_t rxinit;
+	di_rxreset_t rxreset;
+	di_rxidle_t rxidle;
+	di_rxstopped_t rxstopped;
+	di_rxenable_t rxenable;
+	di_rxenabled_t rxenabled;
+	di_rx_t rx;
+	di_rxfill_t rxfill;
+	di_rxreclaim_t rxreclaim;
+	di_getnextrxp_t getnextrxp;
+	di_peeknextrxp_t peeknextrxp;
+	di_rxparam_get_t rxparam_get;
+
+	di_fifoloopbackenable_t fifoloopbackenable;
+	di_getvar_t d_getvar;
+	di_counterreset_t counterreset;
+	di_ctrlflags_t ctrlflags;
+	di_dump_t dump;
+	di_dumptx_t dumptx;
+	di_dumprx_t dumprx;
+	di_rxactive_t rxactive;
+	di_txpending_t txpending;
+	di_txcommitted_t txcommitted;
+	uint endnum;
+};
+
+/*
+ * Exported data structure (read-only)
+ */
+/* export structure */
+struct dma_pub {
+	const struct di_fcn_s *di_fn;	/* DMA function pointers */
+	uint txavail;		/* # free tx descriptors */
+	uint dmactrlflags;	/* dma control flags */
+
+	/* rx error counters */
+	uint rxgiants;		/* rx giant frames */
+	uint rxnobuf;		/* rx out of dma descriptors */
+	/* tx error counters */
+	uint txnobuf;		/* tx out of dma descriptors */
+};
+
+extern struct dma_pub *dma_attach(char *name, struct si_pub *sih,
+			    void *dmaregstx, void *dmaregsrx, uint ntxd,
+			    uint nrxd, uint rxbufsize, int rxextheadroom,
+			    uint nrxpost, uint rxoffset, uint *msg_level);
+
+extern const struct di_fcn_s dma64proc;
+
+#define dma_detach(di)			(dma64proc.detach(di))
+#define dma_txreset(di)			(dma64proc.txreset(di))
+#define dma_rxreset(di)			(dma64proc.rxreset(di))
+#define dma_rxidle(di)			(dma64proc.rxidle(di))
+#define dma_txinit(di)                  (dma64proc.txinit(di))
+#define dma_txenabled(di)               (dma64proc.txenabled(di))
+#define dma_rxinit(di)                  (dma64proc.rxinit(di))
+#define dma_txsuspend(di)               (dma64proc.txsuspend(di))
+#define dma_txresume(di)                (dma64proc.txresume(di))
+#define dma_txsuspended(di)             (dma64proc.txsuspended(di))
+#define dma_txsuspendedidle(di)         (dma64proc.txsuspendedidle(di))
+#define dma_txfast(di, p, commit)	(dma64proc.txfast(di, p, commit))
+#define dma_txunframed(di, p, l, commit)(dma64proc.txunframed(di, p, l, commit))
+#define dma_getpos(di, dir)		(dma64proc.getpos(di, dir))
+#define dma_fifoloopbackenable(di)      (dma64proc.fifoloopbackenable(di))
+#define dma_txstopped(di)               (dma64proc.txstopped(di))
+#define dma_rxstopped(di)               (dma64proc.rxstopped(di))
+#define dma_rxenable(di)                (dma64proc.rxenable(di))
+#define dma_rxenabled(di)               (dma64proc.rxenabled(di))
+#define dma_rx(di)                      (dma64proc.rx(di))
+#define dma_rxfill(di)                  (dma64proc.rxfill(di))
+#define dma_txreclaim(di, range)	(dma64proc.txreclaim(di, range))
+#define dma_rxreclaim(di)               (dma64proc.rxreclaim(di))
+#define dma_getvar(di, name)		(dma64proc.d_getvar(di, name))
+#define dma_getnexttxp(di, range)	(dma64proc.getnexttxp(di, range))
+#define dma_getnextrxp(di, forceall)    (dma64proc.getnextrxp(di, forceall))
+#define dma_peeknexttxp(di)             (dma64proc.peeknexttxp(di))
+#define dma_peeknextrxp(di)             (dma64proc.peeknextrxp(di))
+#define dma_rxparam_get(di, off, bufs)	(dma64proc.rxparam_get(di, off, bufs))
+
+#define dma_txblock(di)                 (dma64proc.txblock(di))
+#define dma_txunblock(di)               (dma64proc.txunblock(di))
+#define dma_txactive(di)                (dma64proc.txactive(di))
+#define dma_rxactive(di)                (dma64proc.rxactive(di))
+#define dma_txrotate(di)                (dma64proc.txrotate(di))
+#define dma_counterreset(di)            (dma64proc.counterreset(di))
+#define dma_ctrlflags(di, mask, flags)  (dma64proc.ctrlflags((di), (mask), (flags)))
+#define dma_txpending(di)		(dma64proc.txpending(di))
+#define dma_txcommitted(di)		(dma64proc.txcommitted(di))
+
+
+/* return addresswidth allowed
+ * This needs to be done after SB attach but before dma attach.
+ * SB attach provides ability to probe backplane and dma core capabilities
+ * This info is needed by DMA_ALLOC_CONSISTENT in dma attach
+ */
+extern uint dma_addrwidth(struct si_pub *sih, void *dmaregs);
+void dma_walk_packets(struct dma_pub *dmah, void (*callback_fnc)
+		      (void *pkt, void *arg_a), void *arg_a);
+
+/*
+ * DMA(Bug) on some chips seems to declare that the packet is ready, but the
+ * packet length is not updated yet (by DMA) on the expected time.
+ * Workaround is to hold processor till DMA updates the length, and stay off
+ * the bus to allow DMA update the length in buffer
+ */
+static inline void dma_spin_for_len(uint len, struct sk_buff *head)
+{
+#if defined(__mips__)
+	if (!len) {
+		while (!(len = *(u16 *) KSEG1ADDR(head->data)))
+			udelay(1);
+
+		*(u16 *) (head->data) = cpu_to_le16((u16) len);
+	}
+#endif				/* defined(__mips__) */
+}
+
+#endif				/* _BRCM_DMA_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
new file mode 100644
index 0000000..84245b9
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.c
@@ -0,0 +1,1933 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#define __UNDEF_NO_VERSION__
+
+#include <linux/etherdevice.h>
+#include <linux/pci.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <net/mac80211.h>
+#include <defs.h>
+#include "nicpci.h"
+#include "phy/phy_int.h"
+#include "d11.h"
+#include "channel.h"
+#include "scb.h"
+#include "pub.h"
+#include "ucode_loader.h"
+#include "mac80211_if.h"
+
+#define N_TX_QUEUES	4 /* #tx queues on mac80211<->driver interface */
+
+#define LOCK(wl)	spin_lock_bh(&(wl)->lock)
+#define UNLOCK(wl)	spin_unlock_bh(&(wl)->lock)
+
+/* locking from inside brcms_isr */
+#define ISR_LOCK(wl, flags)\
+	do {\
+		spin_lock(&(wl)->isr_lock);\
+		(void)(flags); } \
+	while (0)
+
+#define ISR_UNLOCK(wl, flags)\
+	do {\
+		spin_unlock(&(wl)->isr_lock);\
+		(void)(flags); } \
+	while (0)
+
+/* locking under LOCK() to synchronize with brcms_isr */
+#define INT_LOCK(wl, flags)	spin_lock_irqsave(&(wl)->isr_lock, flags)
+#define INT_UNLOCK(wl, flags)	spin_unlock_irqrestore(&(wl)->isr_lock, flags)
+
+static void brcms_timer(unsigned long data);
+static void _brcms_timer(struct brcms_timer *t);
+
+
+static int ieee_hw_init(struct ieee80211_hw *hw);
+static int ieee_hw_rate_init(struct ieee80211_hw *hw);
+
+static int wl_linux_watchdog(void *ctx);
+
+/* Flags we support */
+#define MAC_FILTERS (FIF_PROMISC_IN_BSS | \
+	FIF_ALLMULTI | \
+	FIF_FCSFAIL | \
+	FIF_PLCPFAIL | \
+	FIF_CONTROL | \
+	FIF_OTHER_BSS | \
+	FIF_BCN_PRBRESP_PROMISC)
+
+static int n_adapters_found;
+
+static int brcms_request_fw(struct brcms_info *wl, struct pci_dev *pdev);
+static void brcms_release_fw(struct brcms_info *wl);
+
+/* local prototypes */
+static void brcms_dpc(unsigned long data);
+static irqreturn_t brcms_isr(int irq, void *dev_id);
+
+static int __devinit brcms_pci_probe(struct pci_dev *pdev,
+				  const struct pci_device_id *ent);
+static void brcms_remove(struct pci_dev *pdev);
+static void brcms_free(struct brcms_info *wl);
+static void brcms_set_basic_rate(struct wl_rateset *rs, u16 rate, bool is_br);
+
+MODULE_AUTHOR("Broadcom Corporation");
+MODULE_DESCRIPTION("Broadcom 802.11n wireless LAN driver.");
+MODULE_SUPPORTED_DEVICE("Broadcom 802.11n WLAN cards");
+MODULE_LICENSE("Dual BSD/GPL");
+
+/* recognized PCI IDs */
+static DEFINE_PCI_DEVICE_TABLE(brcms_pci_id_table) = {
+	{PCI_VENDOR_ID_BROADCOM, 0x4357, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},	/* 43225 2G */
+	{PCI_VENDOR_ID_BROADCOM, 0x4353, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},	/* 43224 DUAL */
+	{PCI_VENDOR_ID_BROADCOM, 0x4727, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0},	/* 4313 DUAL */
+	/* 43224 Ven */
+	{PCI_VENDOR_ID_BROADCOM, 0x0576, PCI_ANY_ID, PCI_ANY_ID, 0, 0, 0 },
+	{0}
+};
+
+MODULE_DEVICE_TABLE(pci, brcms_pci_id_table);
+
+#ifdef BCMDBG
+static int msglevel = 0xdeadbeef;
+module_param(msglevel, int, 0);
+static int phymsglevel = 0xdeadbeef;
+module_param(phymsglevel, int, 0);
+#endif				/* BCMDBG */
+
+#define HW_TO_WL(hw)	 (hw->priv)
+#define WL_TO_HW(wl)	  (wl->pub->ieee_hw)
+
+/* MAC80211 callback functions */
+static int brcms_ops_start(struct ieee80211_hw *hw);
+static void brcms_ops_stop(struct ieee80211_hw *hw);
+static int brcms_ops_add_interface(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif);
+static void brcms_ops_remove_interface(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif);
+static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed);
+static void brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
+				    struct ieee80211_vif *vif,
+				    struct ieee80211_bss_conf *info,
+				    u32 changed);
+static void brcms_ops_configure_filter(struct ieee80211_hw *hw,
+				    unsigned int changed_flags,
+				    unsigned int *total_flags, u64 multicast);
+static int brcms_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta,
+			  bool set);
+static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw);
+static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw);
+static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf);
+static int brcms_ops_get_stats(struct ieee80211_hw *hw,
+			    struct ieee80211_low_level_stats *stats);
+static void brcms_ops_sta_notify(struct ieee80211_hw *hw,
+			      struct ieee80211_vif *vif,
+			      enum sta_notify_cmd cmd,
+			      struct ieee80211_sta *sta);
+static int brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
+			  const struct ieee80211_tx_queue_params *params);
+static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw);
+static int brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		      struct ieee80211_sta *sta);
+static int brcms_ops_sta_remove(struct ieee80211_hw *hw,
+				struct ieee80211_vif *vif,
+				struct ieee80211_sta *sta);
+static int brcms_ops_ampdu_action(struct ieee80211_hw *hw,
+			       struct ieee80211_vif *vif,
+			       enum ieee80211_ampdu_mlme_action action,
+			       struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+			       u8 buf_size);
+static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw);
+static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop);
+
+static void brcms_ops_tx(struct ieee80211_hw *hw, struct sk_buff *skb)
+{
+	struct brcms_info *wl = hw->priv;
+
+	LOCK(wl);
+	if (!wl->pub->up) {
+		wiphy_err(wl->wiphy, "ops->tx called while down\n");
+		kfree_skb(skb);
+		goto done;
+	}
+	brcms_c_sendpkt_mac80211(wl->wlc, skb, hw);
+ done:
+	UNLOCK(wl);
+}
+
+static int brcms_ops_start(struct ieee80211_hw *hw)
+{
+	struct brcms_info *wl = hw->priv;
+	bool blocked;
+	/*
+	  struct ieee80211_channel *curchan = hw->conf.channel;
+	*/
+
+	ieee80211_wake_queues(hw);
+	LOCK(wl);
+	blocked = brcms_rfkill_set_hw_state(wl);
+	UNLOCK(wl);
+	if (!blocked)
+		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
+
+	return 0;
+}
+
+static void brcms_ops_stop(struct ieee80211_hw *hw)
+{
+	ieee80211_stop_queues(hw);
+}
+
+static int
+brcms_ops_add_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct brcms_info *wl;
+	int err;
+
+	/* Just STA for now */
+	if (vif->type != NL80211_IFTYPE_AP &&
+	    vif->type != NL80211_IFTYPE_MESH_POINT &&
+	    vif->type != NL80211_IFTYPE_STATION &&
+	    vif->type != NL80211_IFTYPE_WDS &&
+	    vif->type != NL80211_IFTYPE_ADHOC) {
+		wiphy_err(hw->wiphy, "%s: Attempt to add type %d, only"
+			  " STA for now\n", __func__, vif->type);
+		return -EOPNOTSUPP;
+	}
+
+	wl = HW_TO_WL(hw);
+	LOCK(wl);
+	err = brcms_up(wl);
+	UNLOCK(wl);
+
+	if (err != 0) {
+		wiphy_err(hw->wiphy, "%s: brcms_up() returned %d\n", __func__,
+			  err);
+	}
+	return err;
+}
+
+static void
+brcms_ops_remove_interface(struct ieee80211_hw *hw, struct ieee80211_vif *vif)
+{
+	struct brcms_info *wl;
+
+	wl = HW_TO_WL(hw);
+
+	/* put driver in down state */
+	LOCK(wl);
+	brcms_down(wl);
+	UNLOCK(wl);
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+static int
+ieee_set_channel(struct ieee80211_hw *hw, struct ieee80211_channel *chan,
+		 enum nl80211_channel_type type)
+{
+	struct brcms_info *wl = HW_TO_WL(hw);
+	int err = 0;
+
+	switch (type) {
+	case NL80211_CHAN_HT20:
+	case NL80211_CHAN_NO_HT:
+		err = brcms_c_set(wl->wlc, BRCM_SET_CHANNEL, chan->hw_value);
+		break;
+	case NL80211_CHAN_HT40MINUS:
+	case NL80211_CHAN_HT40PLUS:
+		wiphy_err(hw->wiphy,
+			  "%s: Need to implement 40 Mhz Channels!\n", __func__);
+		err = 1;
+		break;
+	}
+
+	if (err)
+		return -EIO;
+	return err;
+}
+
+static int brcms_ops_config(struct ieee80211_hw *hw, u32 changed)
+{
+	struct ieee80211_conf *conf = &hw->conf;
+	struct brcms_info *wl = HW_TO_WL(hw);
+	int err = 0;
+	int new_int;
+	struct wiphy *wiphy = hw->wiphy;
+
+	LOCK(wl);
+	if (changed & IEEE80211_CONF_CHANGE_LISTEN_INTERVAL) {
+		if (brcms_c_set_par(wl->wlc, IOV_BCN_LI_BCN,
+				    conf->listen_interval) < 0) {
+			wiphy_err(wiphy, "%s: Error setting listen_interval\n",
+				  __func__);
+			err = -EIO;
+			goto config_out;
+		}
+		brcms_c_get_par(wl->wlc, IOV_BCN_LI_BCN, &new_int);
+	}
+	if (changed & IEEE80211_CONF_CHANGE_MONITOR)
+		wiphy_err(wiphy, "%s: change monitor mode: %s (implement)\n",
+			  __func__, conf->flags & IEEE80211_CONF_MONITOR ?
+			  "true" : "false");
+	if (changed & IEEE80211_CONF_CHANGE_PS)
+		wiphy_err(wiphy, "%s: change power-save mode: %s (implement)\n",
+			  __func__, conf->flags & IEEE80211_CONF_PS ?
+			  "true" : "false");
+
+	if (changed & IEEE80211_CONF_CHANGE_POWER) {
+		if (brcms_c_set_par(wl->wlc, IOV_QTXPOWER,
+				    conf->power_level * 4) < 0) {
+			wiphy_err(wiphy, "%s: Error setting power_level\n",
+				  __func__);
+			err = -EIO;
+			goto config_out;
+		}
+		brcms_c_get_par(wl->wlc, IOV_QTXPOWER, &new_int);
+		if (new_int != (conf->power_level * 4))
+			wiphy_err(wiphy, "%s: Power level req != actual, %d %d"
+				  "\n", __func__, conf->power_level * 4,
+				  new_int);
+	}
+	if (changed & IEEE80211_CONF_CHANGE_CHANNEL) {
+		err = ieee_set_channel(hw, conf->channel, conf->channel_type);
+	}
+	if (changed & IEEE80211_CONF_CHANGE_RETRY_LIMITS) {
+		if (brcms_c_set
+		    (wl->wlc, BRCM_SET_SRL,
+		     conf->short_frame_max_tx_count) < 0) {
+			wiphy_err(wiphy, "%s: Error setting srl\n", __func__);
+			err = -EIO;
+			goto config_out;
+		}
+		if (brcms_c_set(wl->wlc, BRCM_SET_LRL,
+				conf->long_frame_max_tx_count) < 0) {
+			wiphy_err(wiphy, "%s: Error setting lrl\n", __func__);
+			err = -EIO;
+			goto config_out;
+		}
+	}
+
+ config_out:
+	UNLOCK(wl);
+	return err;
+}
+
+static void
+brcms_ops_bss_info_changed(struct ieee80211_hw *hw,
+			struct ieee80211_vif *vif,
+			struct ieee80211_bss_conf *info, u32 changed)
+{
+	struct brcms_info *wl = HW_TO_WL(hw);
+	struct wiphy *wiphy = hw->wiphy;
+	int val;
+
+	if (changed & BSS_CHANGED_ASSOC) {
+		/* association status changed (associated/disassociated)
+		 * also implies a change in the AID.
+		 */
+		wiphy_err(wiphy, "%s: %s: %sassociated\n", KBUILD_MODNAME,
+			  __func__, info->assoc ? "" : "dis");
+		LOCK(wl);
+		brcms_c_associate_upd(wl->wlc, info->assoc);
+		UNLOCK(wl);
+	}
+	if (changed & BSS_CHANGED_ERP_SLOT) {
+		/* slot timing changed */
+		if (info->use_short_slot)
+			val = 1;
+		else
+			val = 0;
+		LOCK(wl);
+		brcms_c_set(wl->wlc, BRCMS_SET_SHORTSLOT_OVERRIDE, val);
+		UNLOCK(wl);
+	}
+
+	if (changed & BSS_CHANGED_HT) {
+		/* 802.11n parameters changed */
+		u16 mode = info->ht_operation_mode;
+
+		LOCK(wl);
+		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_CFG,
+			mode & IEEE80211_HT_OP_MODE_PROTECTION);
+		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_NONGF,
+			mode & IEEE80211_HT_OP_MODE_NON_GF_STA_PRSNT);
+		brcms_c_protection_upd(wl->wlc, BRCMS_PROT_N_OBSS,
+			mode & IEEE80211_HT_OP_MODE_NON_HT_STA_PRSNT);
+		UNLOCK(wl);
+	}
+	if (changed & BSS_CHANGED_BASIC_RATES) {
+		struct ieee80211_supported_band *bi;
+		u32 br_mask, i;
+		u16 rate;
+		struct wl_rateset rs;
+		int error;
+
+		/* retrieve the current rates */
+		LOCK(wl);
+		error = brcms_c_ioctl(wl->wlc, BRCM_GET_CURR_RATESET,
+				  &rs, sizeof(rs), NULL);
+		UNLOCK(wl);
+		if (error) {
+			wiphy_err(wiphy, "%s: retrieve rateset failed: %d\n",
+				  __func__, error);
+			return;
+		}
+		br_mask = info->basic_rates;
+		bi = hw->wiphy->bands[brcms_c_get_curband(wl->wlc)];
+		for (i = 0; i < bi->n_bitrates; i++) {
+			/* convert to internal rate value */
+			rate = (bi->bitrates[i].bitrate << 1) / 10;
+
+			/* set/clear basic rate flag */
+			brcms_set_basic_rate(&rs, rate, br_mask & 1);
+			br_mask >>= 1;
+		}
+
+		/* update the rate set */
+		LOCK(wl);
+		brcms_c_ioctl(wl->wlc, BRCM_SET_RATESET, &rs, sizeof(rs), NULL);
+		UNLOCK(wl);
+	}
+	if (changed & BSS_CHANGED_BEACON_INT) {
+		/* Beacon interval changed */
+		LOCK(wl);
+		brcms_c_set(wl->wlc, BRCM_SET_BCNPRD, info->beacon_int);
+		UNLOCK(wl);
+	}
+	if (changed & BSS_CHANGED_BSSID) {
+		/* BSSID changed, for whatever reason (IBSS and managed mode) */
+		LOCK(wl);
+		brcms_c_set_addrmatch(wl->wlc, RCM_BSSID_OFFSET,
+				  info->bssid);
+		UNLOCK(wl);
+	}
+	if (changed & BSS_CHANGED_BEACON) {
+		/* Beacon data changed, retrieve new beacon (beaconing modes) */
+		wiphy_err(wiphy, "%s: beacon changed\n", __func__);
+	}
+	if (changed & BSS_CHANGED_BEACON_ENABLED) {
+		/* Beaconing should be enabled/disabled (beaconing modes) */
+		wiphy_err(wiphy, "%s: Beacon enabled: %s\n", __func__,
+			  info->enable_beacon ? "true" : "false");
+	}
+	if (changed & BSS_CHANGED_CQM) {
+		/* Connection quality monitor config changed */
+		wiphy_err(wiphy, "%s: cqm change: threshold %d, hys %d "
+			  " (implement)\n", __func__, info->cqm_rssi_thold,
+			  info->cqm_rssi_hyst);
+	}
+	if (changed & BSS_CHANGED_IBSS) {
+		/* IBSS join status changed */
+		wiphy_err(wiphy, "%s: IBSS joined: %s (implement)\n", __func__,
+			  info->ibss_joined ? "true" : "false");
+	}
+	if (changed & BSS_CHANGED_ARP_FILTER) {
+		/* Hardware ARP filter address list or state changed */
+		wiphy_err(wiphy, "%s: arp filtering: enabled %s, count %d"
+			  " (implement)\n", __func__, info->arp_filter_enabled ?
+			  "true" : "false", info->arp_addr_cnt);
+	}
+	if (changed & BSS_CHANGED_QOS) {
+		/*
+		 * QoS for this association was enabled/disabled.
+		 * Note that it is only ever disabled for station mode.
+		 */
+		wiphy_err(wiphy, "%s: qos enabled: %s (implement)\n", __func__,
+			  info->qos ? "true" : "false");
+	}
+	return;
+}
+
+static void
+brcms_ops_configure_filter(struct ieee80211_hw *hw,
+			unsigned int changed_flags,
+			unsigned int *total_flags, u64 multicast)
+{
+	struct brcms_info *wl = hw->priv;
+	struct wiphy *wiphy = hw->wiphy;
+
+	changed_flags &= MAC_FILTERS;
+	*total_flags &= MAC_FILTERS;
+	if (changed_flags & FIF_PROMISC_IN_BSS)
+		wiphy_err(wiphy, "FIF_PROMISC_IN_BSS\n");
+	if (changed_flags & FIF_ALLMULTI)
+		wiphy_err(wiphy, "FIF_ALLMULTI\n");
+	if (changed_flags & FIF_FCSFAIL)
+		wiphy_err(wiphy, "FIF_FCSFAIL\n");
+	if (changed_flags & FIF_PLCPFAIL)
+		wiphy_err(wiphy, "FIF_PLCPFAIL\n");
+	if (changed_flags & FIF_CONTROL)
+		wiphy_err(wiphy, "FIF_CONTROL\n");
+	if (changed_flags & FIF_OTHER_BSS)
+		wiphy_err(wiphy, "FIF_OTHER_BSS\n");
+	if (changed_flags & FIF_BCN_PRBRESP_PROMISC) {
+		LOCK(wl);
+		if (*total_flags & FIF_BCN_PRBRESP_PROMISC) {
+			wl->pub->mac80211_state |= MAC80211_PROMISC_BCNS;
+			brcms_c_mac_bcn_promisc_change(wl->wlc, 1);
+		} else {
+			brcms_c_mac_bcn_promisc_change(wl->wlc, 0);
+			wl->pub->mac80211_state &= ~MAC80211_PROMISC_BCNS;
+		}
+		UNLOCK(wl);
+	}
+	return;
+}
+
+static int
+brcms_ops_set_tim(struct ieee80211_hw *hw, struct ieee80211_sta *sta, bool set)
+{
+	return 0;
+}
+
+static void brcms_ops_sw_scan_start(struct ieee80211_hw *hw)
+{
+	struct brcms_info *wl = hw->priv;
+	LOCK(wl);
+	brcms_c_scan_start(wl->wlc);
+	UNLOCK(wl);
+	return;
+}
+
+static void brcms_ops_sw_scan_complete(struct ieee80211_hw *hw)
+{
+	struct brcms_info *wl = hw->priv;
+	LOCK(wl);
+	brcms_c_scan_stop(wl->wlc);
+	UNLOCK(wl);
+	return;
+}
+
+static void brcms_ops_set_tsf(struct ieee80211_hw *hw, u64 tsf)
+{
+	wiphy_err(hw->wiphy, "%s: Enter\n", __func__);
+	return;
+}
+
+static int
+brcms_ops_get_stats(struct ieee80211_hw *hw,
+		 struct ieee80211_low_level_stats *stats)
+{
+	struct brcms_info *wl = hw->priv;
+	struct wl_cnt *cnt;
+
+	LOCK(wl);
+	cnt = wl->pub->_cnt;
+	stats->dot11ACKFailureCount = 0;
+	stats->dot11RTSFailureCount = 0;
+	stats->dot11FCSErrorCount = 0;
+	stats->dot11RTSSuccessCount = 0;
+	UNLOCK(wl);
+	return 0;
+}
+
+static void
+brcms_ops_sta_notify(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  enum sta_notify_cmd cmd, struct ieee80211_sta *sta)
+{
+	switch (cmd) {
+	default:
+		wiphy_err(hw->wiphy, "%s: Unknown cmd = %d\n", __func__,
+			  cmd);
+		break;
+	}
+	return;
+}
+
+static int
+brcms_ops_conf_tx(struct ieee80211_hw *hw, u16 queue,
+	       const struct ieee80211_tx_queue_params *params)
+{
+	struct brcms_info *wl = hw->priv;
+
+	LOCK(wl);
+	brcms_c_wme_setparams(wl->wlc, queue, params, true);
+	UNLOCK(wl);
+
+	return 0;
+}
+
+static u64 brcms_ops_get_tsf(struct ieee80211_hw *hw)
+{
+	wiphy_err(hw->wiphy, "%s: Enter\n", __func__);
+	return 0;
+}
+
+static int
+brcms_ops_sta_add(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+	       struct ieee80211_sta *sta)
+{
+	struct scb *scb;
+
+	int i;
+	struct brcms_info *wl = hw->priv;
+
+	/* Init the scb */
+	scb = (struct scb *)sta->drv_priv;
+	memset(scb, 0, sizeof(struct scb));
+	for (i = 0; i < NUMPRIO; i++)
+		scb->seqctl[i] = 0xFFFF;
+	scb->seqctl_nonqos = 0xFFFF;
+	scb->magic = SCB_MAGIC;
+
+	wl->pub->global_scb = scb;
+	wl->pub->global_ampdu = &(scb->scb_ampdu);
+	wl->pub->global_ampdu->scb = scb;
+	wl->pub->global_ampdu->max_pdu = 16;
+	brcmu_pktq_init(&scb->scb_ampdu.txq, AMPDU_MAX_SCB_TID,
+		  AMPDU_MAX_SCB_TID * PKTQ_LEN_DEFAULT);
+
+	sta->ht_cap.ht_supported = true;
+	sta->ht_cap.ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K;
+	sta->ht_cap.ampdu_density = AMPDU_DEF_MPDU_DENSITY;
+	sta->ht_cap.cap = IEEE80211_HT_CAP_GRN_FLD |
+	    IEEE80211_HT_CAP_SGI_20 |
+	    IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT;
+
+	/* minstrel_ht initiates addBA on our behalf by calling ieee80211_start_tx_ba_session() */
+	return 0;
+}
+
+static int
+brcms_ops_sta_remove(struct ieee80211_hw *hw, struct ieee80211_vif *vif,
+		  struct ieee80211_sta *sta)
+{
+	return 0;
+}
+
+static int
+brcms_ops_ampdu_action(struct ieee80211_hw *hw,
+		    struct ieee80211_vif *vif,
+		    enum ieee80211_ampdu_mlme_action action,
+		    struct ieee80211_sta *sta, u16 tid, u16 *ssn,
+		    u8 buf_size)
+{
+	struct scb *scb = (struct scb *)sta->drv_priv;
+	struct brcms_info *wl = hw->priv;
+	int status;
+
+	if (WARN_ON(scb->magic != SCB_MAGIC))
+		return -EIDRM;
+	switch (action) {
+	case IEEE80211_AMPDU_RX_START:
+		break;
+	case IEEE80211_AMPDU_RX_STOP:
+		break;
+	case IEEE80211_AMPDU_TX_START:
+		LOCK(wl);
+		status = brcms_c_aggregatable(wl->wlc, tid);
+		UNLOCK(wl);
+		if (!status) {
+			wiphy_err(wl->wiphy, "START: tid %d is not agg\'able\n",
+				  tid);
+			return -EINVAL;
+		}
+		/* Future improvement: Use the starting sequence number provided ... */
+		*ssn = 0;
+		ieee80211_start_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		break;
+
+	case IEEE80211_AMPDU_TX_STOP:
+		LOCK(wl);
+		brcms_c_ampdu_flush(wl->wlc, sta, tid);
+		UNLOCK(wl);
+		ieee80211_stop_tx_ba_cb_irqsafe(vif, sta->addr, tid);
+		break;
+	case IEEE80211_AMPDU_TX_OPERATIONAL:
+		/*
+		 * BA window size from ADDBA response ('buf_size') defines how
+		 * many outstanding MPDUs are allowed for the BA stream by
+		 * recipient and traffic class. 'ampdu_factor' gives maximum
+		 * AMPDU size.
+		 */
+		LOCK(wl);
+		brcms_c_ampdu_tx_operational(wl->wlc, tid, buf_size,
+			(1 << (IEEE80211_HT_MAX_AMPDU_FACTOR +
+			 sta->ht_cap.ampdu_factor)) - 1);
+		UNLOCK(wl);
+		/* Power save wakeup */
+		break;
+	default:
+		wiphy_err(wl->wiphy, "%s: Invalid command, ignoring\n",
+			  __func__);
+	}
+
+	return 0;
+}
+
+static void brcms_ops_rfkill_poll(struct ieee80211_hw *hw)
+{
+	struct brcms_info *wl = HW_TO_WL(hw);
+	bool blocked;
+
+	LOCK(wl);
+	blocked = brcms_c_check_radio_disabled(wl->wlc);
+	UNLOCK(wl);
+
+	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+}
+
+static void brcms_ops_flush(struct ieee80211_hw *hw, bool drop)
+{
+	struct brcms_info *wl = HW_TO_WL(hw);
+
+	no_printk("%s: drop = %s\n", __func__, drop ? "true" : "false");
+
+	/* wait for packet queue and dma fifos to run empty */
+	LOCK(wl);
+	brcms_c_wait_for_tx_completion(wl->wlc, drop);
+	UNLOCK(wl);
+}
+
+static const struct ieee80211_ops brcms_ops = {
+	.tx = brcms_ops_tx,
+	.start = brcms_ops_start,
+	.stop = brcms_ops_stop,
+	.add_interface = brcms_ops_add_interface,
+	.remove_interface = brcms_ops_remove_interface,
+	.config = brcms_ops_config,
+	.bss_info_changed = brcms_ops_bss_info_changed,
+	.configure_filter = brcms_ops_configure_filter,
+	.set_tim = brcms_ops_set_tim,
+	.sw_scan_start = brcms_ops_sw_scan_start,
+	.sw_scan_complete = brcms_ops_sw_scan_complete,
+	.set_tsf = brcms_ops_set_tsf,
+	.get_stats = brcms_ops_get_stats,
+	.sta_notify = brcms_ops_sta_notify,
+	.conf_tx = brcms_ops_conf_tx,
+	.get_tsf = brcms_ops_get_tsf,
+	.sta_add = brcms_ops_sta_add,
+	.sta_remove = brcms_ops_sta_remove,
+	.ampdu_action = brcms_ops_ampdu_action,
+	.rfkill_poll = brcms_ops_rfkill_poll,
+	.flush = brcms_ops_flush,
+};
+
+/*
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static int brcms_set_hint(struct brcms_info *wl, char *abbrev)
+{
+	return regulatory_hint(wl->pub->ieee_hw->wiphy, abbrev);
+}
+
+/**
+ * attach to the WL device.
+ *
+ * Attach to the WL device identified by vendor and device parameters.
+ * regs is a host accessible memory address pointing to WL device registers.
+ *
+ * brcms_attach is not defined as static because in the case where no bus
+ * is defined, wl_attach will never be called, and thus, gcc will issue
+ * a warning that this function is defined but not used if we declare
+ * it as static.
+ *
+ *
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static struct brcms_info *brcms_attach(u16 vendor, u16 device,
+				       unsigned long regs,
+			    uint bustype, void *btparam, uint irq)
+{
+	struct brcms_info *wl = NULL;
+	int unit, err;
+	unsigned long base_addr;
+	struct ieee80211_hw *hw;
+	u8 perm[ETH_ALEN];
+
+	unit = n_adapters_found;
+	err = 0;
+
+	if (unit < 0) {
+		return NULL;
+	}
+
+	/* allocate private info */
+	hw = pci_get_drvdata(btparam);	/* btparam == pdev */
+	if (hw != NULL)
+		wl = hw->priv;
+	if (WARN_ON(hw == NULL) || WARN_ON(wl == NULL))
+		return NULL;
+	wl->wiphy = hw->wiphy;
+
+	atomic_set(&wl->callbacks, 0);
+
+	/* setup the bottom half handler */
+	tasklet_init(&wl->tasklet, brcms_dpc, (unsigned long) wl);
+
+
+
+	base_addr = regs;
+
+	if (bustype == PCI_BUS || bustype == RPC_BUS) {
+		/* Do nothing */
+	} else {
+		bustype = PCI_BUS;
+		BCMMSG(wl->wiphy, "force to PCI\n");
+	}
+	wl->bcm_bustype = bustype;
+
+	wl->regsva = ioremap_nocache(base_addr, PCI_BAR0_WINSZ);
+	if (wl->regsva == NULL) {
+		wiphy_err(wl->wiphy, "wl%d: ioremap() failed\n", unit);
+		goto fail;
+	}
+	spin_lock_init(&wl->lock);
+	spin_lock_init(&wl->isr_lock);
+
+	/* prepare ucode */
+	if (brcms_request_fw(wl, (struct pci_dev *)btparam) < 0) {
+		wiphy_err(wl->wiphy, "%s: Failed to find firmware usually in "
+			  "%s\n", KBUILD_MODNAME, "/lib/firmware/brcm");
+		brcms_release_fw(wl);
+		brcms_remove((struct pci_dev *)btparam);
+		return NULL;
+	}
+
+	/* common load-time initialization */
+	wl->wlc = brcms_c_attach((void *)wl, vendor, device, unit, false,
+			     wl->regsva, wl->bcm_bustype, btparam, &err);
+	brcms_release_fw(wl);
+	if (!wl->wlc) {
+		wiphy_err(wl->wiphy, "%s: attach() failed with code %d\n",
+			  KBUILD_MODNAME, err);
+		goto fail;
+	}
+	wl->pub = brcms_c_pub(wl->wlc);
+
+	wl->pub->ieee_hw = hw;
+
+	if (brcms_c_set_par(wl->wlc, IOV_MPC, 0) < 0) {
+		wiphy_err(wl->wiphy, "wl%d: Error setting MPC variable to 0\n",
+			  unit);
+	}
+
+	/* register our interrupt handler */
+	if (request_irq(irq, brcms_isr, IRQF_SHARED, KBUILD_MODNAME, wl)) {
+		wiphy_err(wl->wiphy, "wl%d: request_irq() failed\n", unit);
+		goto fail;
+	}
+	wl->irq = irq;
+
+	/* register module */
+	brcms_c_module_register(wl->pub, "linux", wl, wl_linux_watchdog, NULL);
+
+	if (ieee_hw_init(hw)) {
+		wiphy_err(wl->wiphy, "wl%d: %s: ieee_hw_init failed!\n", unit,
+			  __func__);
+		goto fail;
+	}
+
+	memcpy(perm, &wl->pub->cur_etheraddr, ETH_ALEN);
+	if (WARN_ON(!is_valid_ether_addr(perm)))
+		goto fail;
+	SET_IEEE80211_PERM_ADDR(hw, perm);
+
+	err = ieee80211_register_hw(hw);
+	if (err) {
+		wiphy_err(wl->wiphy, "%s: ieee80211_register_hw failed, status"
+			  "%d\n", __func__, err);
+	}
+
+	if (wl->pub->srom_ccode[0])
+		err = brcms_set_hint(wl, wl->pub->srom_ccode);
+	else
+		err = brcms_set_hint(wl, "US");
+	if (err) {
+		wiphy_err(wl->wiphy, "%s: regulatory_hint failed, status %d\n",
+			  __func__, err);
+	}
+
+	n_adapters_found++;
+	return wl;
+
+fail:
+	brcms_free(wl);
+	return NULL;
+}
+
+
+
+#define CHAN2GHZ(channel, freqency, chflags)  { \
+	.band = IEEE80211_BAND_2GHZ, \
+	.center_freq = (freqency), \
+	.hw_value = (channel), \
+	.flags = chflags, \
+	.max_antenna_gain = 0, \
+	.max_power = 19, \
+}
+
+static struct ieee80211_channel brcms_2ghz_chantable[] = {
+	CHAN2GHZ(1, 2412, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN2GHZ(2, 2417, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN2GHZ(3, 2422, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN2GHZ(4, 2427, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN2GHZ(5, 2432, 0),
+	CHAN2GHZ(6, 2437, 0),
+	CHAN2GHZ(7, 2442, 0),
+	CHAN2GHZ(8, 2447, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(9, 2452, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(10, 2457, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(11, 2462, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(12, 2467,
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(13, 2472,
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN2GHZ(14, 2484,
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+#define CHAN5GHZ(channel, chflags)  { \
+	.band = IEEE80211_BAND_5GHZ, \
+	.center_freq = 5000 + 5*(channel), \
+	.hw_value = (channel), \
+	.flags = chflags, \
+	.max_antenna_gain = 0, \
+	.max_power = 21, \
+}
+
+static struct ieee80211_channel brcms_5ghz_nphy_chantable[] = {
+	/* UNII-1 */
+	CHAN5GHZ(36, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(40, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(44, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(48, IEEE80211_CHAN_NO_HT40PLUS),
+	/* UNII-2 */
+	CHAN5GHZ(52,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(56,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(60,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(64,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	/* MID */
+	CHAN5GHZ(100,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(104,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(108,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(112,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(116,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(120,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(124,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(128,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(132,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(136,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(140,
+		 IEEE80211_CHAN_RADAR | IEEE80211_CHAN_NO_IBSS |
+		 IEEE80211_CHAN_PASSIVE_SCAN | IEEE80211_CHAN_NO_HT40PLUS |
+		 IEEE80211_CHAN_NO_HT40MINUS),
+	/* UNII-3 */
+	CHAN5GHZ(149, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(153, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(157, IEEE80211_CHAN_NO_HT40MINUS),
+	CHAN5GHZ(161, IEEE80211_CHAN_NO_HT40PLUS),
+	CHAN5GHZ(165, IEEE80211_CHAN_NO_HT40PLUS | IEEE80211_CHAN_NO_HT40MINUS)
+};
+
+#define RATE(rate100m, _flags) { \
+	.bitrate = (rate100m), \
+	.flags = (_flags), \
+	.hw_value = (rate100m / 5), \
+}
+
+static struct ieee80211_rate legacy_ratetable[] = {
+	RATE(10, 0),
+	RATE(20, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(55, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(110, IEEE80211_RATE_SHORT_PREAMBLE),
+	RATE(60, 0),
+	RATE(90, 0),
+	RATE(120, 0),
+	RATE(180, 0),
+	RATE(240, 0),
+	RATE(360, 0),
+	RATE(480, 0),
+	RATE(540, 0),
+};
+
+static struct ieee80211_supported_band brcms_band_2GHz_nphy = {
+	.band = IEEE80211_BAND_2GHZ,
+	.channels = brcms_2ghz_chantable,
+	.n_channels = ARRAY_SIZE(brcms_2ghz_chantable),
+	.bitrates = legacy_ratetable,
+	.n_bitrates = ARRAY_SIZE(legacy_ratetable),
+	.ht_cap = {
+		   /* from include/linux/ieee80211.h */
+		   .cap = IEEE80211_HT_CAP_GRN_FLD |
+		   IEEE80211_HT_CAP_SGI_20 |
+		   IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,
+		   .ht_supported = true,
+		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+		   .mcs = {
+			   /* placeholders for now */
+			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+			   .rx_highest = 500,
+			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+		   }
+};
+
+static struct ieee80211_supported_band brcms_band_5GHz_nphy = {
+	.band = IEEE80211_BAND_5GHZ,
+	.channels = brcms_5ghz_nphy_chantable,
+	.n_channels = ARRAY_SIZE(brcms_5ghz_nphy_chantable),
+	.bitrates = legacy_ratetable + 4,
+	.n_bitrates = ARRAY_SIZE(legacy_ratetable) - 4,
+	.ht_cap = {
+		   /* use IEEE80211_HT_CAP_* from include/linux/ieee80211.h */
+		   .cap = IEEE80211_HT_CAP_GRN_FLD | IEEE80211_HT_CAP_SGI_20 | IEEE80211_HT_CAP_SGI_40 | IEEE80211_HT_CAP_40MHZ_INTOLERANT,	/* No 40 mhz yet */
+		   .ht_supported = true,
+		   .ampdu_factor = IEEE80211_HT_MAX_AMPDU_64K,
+		   .ampdu_density = AMPDU_DEF_MPDU_DENSITY,
+		   .mcs = {
+			   /* placeholders for now */
+			   .rx_mask = {0xff, 0xff, 0, 0, 0, 0, 0, 0, 0, 0},
+			   .rx_highest = 500,
+			   .tx_params = IEEE80211_HT_MCS_TX_DEFINED}
+		   }
+};
+
+/*
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static int ieee_hw_rate_init(struct ieee80211_hw *hw)
+{
+	struct brcms_info *wl = HW_TO_WL(hw);
+	int has_5g;
+	char phy_list[4];
+
+	has_5g = 0;
+
+	hw->wiphy->bands[IEEE80211_BAND_2GHZ] = NULL;
+	hw->wiphy->bands[IEEE80211_BAND_5GHZ] = NULL;
+
+	if (brcms_c_get(wl->wlc, BRCM_GET_PHYLIST, (int *)&phy_list) < 0)
+		wiphy_err(hw->wiphy, "Phy list failed\n");
+
+	if (phy_list[0] == 'n' || phy_list[0] == 'c') {
+		if (phy_list[0] == 'c') {
+			/* Single stream */
+			brcms_band_2GHz_nphy.ht_cap.mcs.rx_mask[1] = 0;
+			brcms_band_2GHz_nphy.ht_cap.mcs.rx_highest = 72;
+		}
+		hw->wiphy->bands[IEEE80211_BAND_2GHZ] = &brcms_band_2GHz_nphy;
+	} else {
+		return -EPERM;
+	}
+
+	/* Assume all bands use the same phy.  True for 11n devices. */
+	if (NBANDS_PUB(wl->pub) > 1) {
+		has_5g++;
+		if (phy_list[0] == 'n' || phy_list[0] == 'c') {
+			hw->wiphy->bands[IEEE80211_BAND_5GHZ] =
+			    &brcms_band_5GHz_nphy;
+		} else {
+			return -EPERM;
+		}
+	}
+	return 0;
+}
+
+/*
+ * is called in brcms_pci_probe() context, therefore no locking required.
+ */
+static int ieee_hw_init(struct ieee80211_hw *hw)
+{
+	hw->flags = IEEE80211_HW_SIGNAL_DBM
+	    /* | IEEE80211_HW_CONNECTION_MONITOR  What is this? */
+	    | IEEE80211_HW_REPORTS_TX_ACK_STATUS
+	    | IEEE80211_HW_AMPDU_AGGREGATION;
+
+	hw->extra_tx_headroom = brcms_c_get_header_len();
+	hw->queues = N_TX_QUEUES;
+	hw->max_rates = 2;	/* Primary rate and 1 fallback rate */
+
+	hw->channel_change_time = 7 * 1000;	/* channel change time is dependent on chip and band  */
+	hw->wiphy->interface_modes = BIT(NL80211_IFTYPE_STATION);
+
+	hw->rate_control_algorithm = "minstrel_ht";
+
+	hw->sta_data_size = sizeof(struct scb);
+	return ieee_hw_rate_init(hw);
+}
+
+/**
+ * determines if a device is a WL device, and if so, attaches it.
+ *
+ * This function determines if a device pointed to by pdev is a WL device,
+ * and if so, performs a brcms_attach() on it.
+ *
+ * Perimeter lock is initialized in the course of this function.
+ */
+static int __devinit
+brcms_pci_probe(struct pci_dev *pdev, const struct pci_device_id *ent)
+{
+	int rc;
+	struct brcms_info *wl;
+	struct ieee80211_hw *hw;
+	u32 val;
+
+	dev_info(&pdev->dev, "bus %d slot %d func %d irq %d\n",
+	       pdev->bus->number, PCI_SLOT(pdev->devfn),
+	       PCI_FUNC(pdev->devfn), pdev->irq);
+
+	if ((pdev->vendor != PCI_VENDOR_ID_BROADCOM) ||
+	    ((pdev->device != 0x0576) &&
+	     ((pdev->device & 0xff00) != 0x4300) &&
+	     ((pdev->device & 0xff00) != 0x4700) &&
+	     ((pdev->device < 43000) || (pdev->device > 43999))))
+		return -ENODEV;
+
+	rc = pci_enable_device(pdev);
+	if (rc) {
+		pr_err("%s: Cannot enable device %d-%d_%d\n",
+		       __func__, pdev->bus->number, PCI_SLOT(pdev->devfn),
+		       PCI_FUNC(pdev->devfn));
+		return -ENODEV;
+	}
+	pci_set_master(pdev);
+
+	pci_read_config_dword(pdev, 0x40, &val);
+	if ((val & 0x0000ff00) != 0)
+		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+	hw = ieee80211_alloc_hw(sizeof(struct brcms_info), &brcms_ops);
+	if (!hw) {
+		pr_err("%s: ieee80211_alloc_hw failed\n", __func__);
+		return -ENOMEM;
+	}
+
+	SET_IEEE80211_DEV(hw, &pdev->dev);
+
+	pci_set_drvdata(pdev, hw);
+
+	memset(hw->priv, 0, sizeof(*wl));
+
+	wl = brcms_attach(pdev->vendor, pdev->device,
+			  pci_resource_start(pdev, 0), PCI_BUS, pdev,
+			  pdev->irq);
+
+	if (!wl) {
+		pr_err("%s: %s: brcms_attach failed!\n", KBUILD_MODNAME,
+		       __func__);
+		return -ENODEV;
+	}
+	return 0;
+}
+
+static int brcms_suspend(struct pci_dev *pdev, pm_message_t state)
+{
+	struct brcms_info *wl;
+	struct ieee80211_hw *hw;
+
+	hw = pci_get_drvdata(pdev);
+	wl = HW_TO_WL(hw);
+	if (!wl) {
+		wiphy_err(wl->wiphy,
+			  "brcms_suspend: pci_get_drvdata failed\n");
+		return -ENODEV;
+	}
+
+	/* only need to flag hw is down for proper resume */
+	LOCK(wl);
+	wl->pub->hw_up = false;
+	UNLOCK(wl);
+
+	pci_save_state(pdev);
+	pci_disable_device(pdev);
+	return pci_set_power_state(pdev, PCI_D3hot);
+}
+
+static int brcms_resume(struct pci_dev *pdev)
+{
+	struct brcms_info *wl;
+	struct ieee80211_hw *hw;
+	int err = 0;
+	u32 val;
+
+	hw = pci_get_drvdata(pdev);
+	wl = HW_TO_WL(hw);
+	if (!wl) {
+		wiphy_err(wl->wiphy,
+			  "wl: brcms_resume: pci_get_drvdata failed\n");
+		return -ENODEV;
+	}
+
+	err = pci_set_power_state(pdev, PCI_D0);
+	if (err)
+		return err;
+
+	pci_restore_state(pdev);
+
+	err = pci_enable_device(pdev);
+	if (err)
+		return err;
+
+	pci_set_master(pdev);
+
+	pci_read_config_dword(pdev, 0x40, &val);
+	if ((val & 0x0000ff00) != 0)
+		pci_write_config_dword(pdev, 0x40, val & 0xffff00ff);
+
+	/*
+	*  done. driver will be put in up state
+	*  in brcms_ops_add_interface() call.
+	*/
+	return err;
+}
+
+/*
+* called from both kernel as from this kernel module.
+* precondition: perimeter lock is not acquired.
+*/
+static void brcms_remove(struct pci_dev *pdev)
+{
+	struct brcms_info *wl;
+	struct ieee80211_hw *hw;
+	int status;
+
+	hw = pci_get_drvdata(pdev);
+	wl = HW_TO_WL(hw);
+	if (!wl) {
+		pr_err("wl: brcms_remove: pci_get_drvdata failed\n");
+		return;
+	}
+
+	LOCK(wl);
+	status = brcms_c_chipmatch(pdev->vendor, pdev->device);
+	UNLOCK(wl);
+	if (!status) {
+		wiphy_err(wl->wiphy, "wl: brcms_remove: chipmatch "
+				     "failed\n");
+		return;
+	}
+	if (wl->wlc) {
+		wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, false);
+		wiphy_rfkill_stop_polling(wl->pub->ieee_hw->wiphy);
+		ieee80211_unregister_hw(hw);
+		LOCK(wl);
+		brcms_down(wl);
+		UNLOCK(wl);
+	}
+	pci_disable_device(pdev);
+
+	brcms_free(wl);
+
+	pci_set_drvdata(pdev, NULL);
+	ieee80211_free_hw(hw);
+}
+
+static struct pci_driver brcms_pci_driver = {
+	.name     = KBUILD_MODNAME,
+	.probe    = brcms_pci_probe,
+	.suspend  = brcms_suspend,
+	.resume   = brcms_resume,
+	.remove   = __devexit_p(brcms_remove),
+	.id_table = brcms_pci_id_table,
+};
+
+/**
+ * This is the main entry point for the WL driver.
+ *
+ * This function determines if a device pointed to by pdev is a WL device,
+ * and if so, performs a brcms_attach() on it.
+ *
+ */
+static int __init brcms_module_init(void)
+{
+	int error = -ENODEV;
+
+#ifdef BCMDBG
+	if (msglevel != 0xdeadbeef)
+		brcm_msg_level = msglevel;
+	if (phymsglevel != 0xdeadbeef)
+		phyhal_msg_level = phymsglevel;
+#endif				/* BCMDBG */
+
+	error = pci_register_driver(&brcms_pci_driver);
+	if (!error)
+		return 0;
+
+
+
+	return error;
+}
+
+/**
+ * This function unloads the WL driver from the system.
+ *
+ * This function unconditionally unloads the WL driver module from the
+ * system.
+ *
+ */
+static void __exit brcms_module_exit(void)
+{
+	pci_unregister_driver(&brcms_pci_driver);
+
+}
+
+module_init(brcms_module_init);
+module_exit(brcms_module_exit);
+
+/**
+ * This function frees the WL per-device resources.
+ *
+ * This function frees resources owned by the WL device pointed to
+ * by the wl parameter.
+ *
+ * precondition: can both be called locked and unlocked
+ *
+ */
+static void brcms_free(struct brcms_info *wl)
+{
+	struct brcms_timer *t, *next;
+
+	/* free ucode data */
+	if (wl->fw.fw_cnt)
+		brcms_ucode_data_free();
+	if (wl->irq)
+		free_irq(wl->irq, wl);
+
+	/* kill dpc */
+	tasklet_kill(&wl->tasklet);
+
+	if (wl->pub) {
+		brcms_c_module_unregister(wl->pub, "linux", wl);
+	}
+
+	/* free common resources */
+	if (wl->wlc) {
+		brcms_c_detach(wl->wlc);
+		wl->wlc = NULL;
+		wl->pub = NULL;
+	}
+
+	/* virtual interface deletion is deferred so we cannot spinwait */
+
+	/* wait for all pending callbacks to complete */
+	while (atomic_read(&wl->callbacks) > 0)
+		schedule();
+
+	/* free timers */
+	for (t = wl->timers; t; t = next) {
+		next = t->next;
+#ifdef BCMDBG
+		kfree(t->name);
+#endif
+		kfree(t);
+	}
+
+	/*
+	 * unregister_netdev() calls get_stats() which may read chip registers
+	 * so we cannot unmap the chip registers until after calling unregister_netdev() .
+	 */
+	if (wl->regsva && wl->bcm_bustype != SDIO_BUS &&
+	    wl->bcm_bustype != JTAG_BUS) {
+		iounmap((void *)wl->regsva);
+	}
+	wl->regsva = NULL;
+}
+
+/* flags the given rate in rateset as requested */
+static void brcms_set_basic_rate(struct wl_rateset *rs, u16 rate, bool is_br)
+{
+	u32 i;
+
+	for (i = 0; i < rs->count; i++) {
+		if (rate != (rs->rates[i] & 0x7f))
+			continue;
+
+		if (is_br)
+			rs->rates[i] |= BRCMS_RATE_FLAG;
+		else
+			rs->rates[i] &= BRCMS_RATE_MASK;
+		return;
+	}
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
+			 bool state, int prio)
+{
+	wiphy_err(wl->wiphy, "Shouldn't be here %s\n", __func__);
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_init(struct brcms_info *wl)
+{
+	BCMMSG(WL_TO_HW(wl)->wiphy, "wl%d\n", wl->pub->unit);
+	brcms_reset(wl);
+
+	brcms_c_init(wl->wlc);
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+uint brcms_reset(struct brcms_info *wl)
+{
+	BCMMSG(WL_TO_HW(wl)->wiphy, "wl%d\n", wl->pub->unit);
+	brcms_c_reset(wl->wlc);
+
+	/* dpc will not be rescheduled */
+	wl->resched = 0;
+
+	return 0;
+}
+
+/*
+ * These are interrupt on/off entry points. Disable interrupts
+ * during interrupt state transition.
+ */
+void brcms_intrson(struct brcms_info *wl)
+{
+	unsigned long flags;
+
+	INT_LOCK(wl, flags);
+	brcms_c_intrson(wl->wlc);
+	INT_UNLOCK(wl, flags);
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+bool wl_alloc_dma_resources(struct brcms_info *wl, uint addrwidth)
+{
+	return true;
+}
+
+u32 brcms_intrsoff(struct brcms_info *wl)
+{
+	unsigned long flags;
+	u32 status;
+
+	INT_LOCK(wl, flags);
+	status = brcms_c_intrsoff(wl->wlc);
+	INT_UNLOCK(wl, flags);
+	return status;
+}
+
+void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask)
+{
+	unsigned long flags;
+
+	INT_LOCK(wl, flags);
+	brcms_c_intrsrestore(wl->wlc, macintmask);
+	INT_UNLOCK(wl, flags);
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+int brcms_up(struct brcms_info *wl)
+{
+	int error = 0;
+
+	if (wl->pub->up)
+		return 0;
+
+	error = brcms_c_up(wl->wlc);
+
+	return error;
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_down(struct brcms_info *wl)
+{
+	uint callbacks, ret_val = 0;
+
+	/* call common down function */
+	ret_val = brcms_c_down(wl->wlc);
+	callbacks = atomic_read(&wl->callbacks) - ret_val;
+
+	/* wait for down callbacks to complete */
+	UNLOCK(wl);
+
+	/* For HIGH_only driver, it's important to actually schedule other work,
+	 * not just spin wait since everything runs at schedule level
+	 */
+	SPINWAIT((atomic_read(&wl->callbacks) > callbacks), 100 * 1000);
+
+	LOCK(wl);
+}
+
+static irqreturn_t brcms_isr(int irq, void *dev_id)
+{
+	struct brcms_info *wl;
+	bool ours, wantdpc;
+	unsigned long flags;
+
+	wl = (struct brcms_info *) dev_id;
+
+	ISR_LOCK(wl, flags);
+
+	/* call common first level interrupt handler */
+	ours = brcms_c_isr(wl->wlc, &wantdpc);
+	if (ours) {
+		/* if more to do... */
+		if (wantdpc) {
+
+			/* ...and call the second level interrupt handler */
+			/* schedule dpc */
+			tasklet_schedule(&wl->tasklet);
+		}
+	}
+
+	ISR_UNLOCK(wl, flags);
+
+	return IRQ_RETVAL(ours);
+}
+
+static void brcms_dpc(unsigned long data)
+{
+	struct brcms_info *wl;
+
+	wl = (struct brcms_info *) data;
+
+	LOCK(wl);
+
+	/* call the common second level interrupt handler */
+	if (wl->pub->up) {
+		if (wl->resched) {
+			unsigned long flags;
+
+			INT_LOCK(wl, flags);
+			brcms_c_intrsupd(wl->wlc);
+			INT_UNLOCK(wl, flags);
+		}
+
+		wl->resched = brcms_c_dpc(wl->wlc, true);
+	}
+
+	/* brcms_c_dpc() may bring the driver down */
+	if (!wl->pub->up)
+		goto done;
+
+	/* re-schedule dpc */
+	if (wl->resched)
+		tasklet_schedule(&wl->tasklet);
+	else {
+		/* re-enable interrupts */
+		brcms_intrson(wl);
+	}
+
+ done:
+	UNLOCK(wl);
+}
+
+/*
+ * is called by the kernel from software irq context
+ */
+static void brcms_timer(unsigned long data)
+{
+	_brcms_timer((struct brcms_timer *) data);
+}
+
+/*
+* precondition: perimeter lock is not acquired
+ */
+static void _brcms_timer(struct brcms_timer *t)
+{
+	LOCK(t->wl);
+
+	if (t->set) {
+		if (t->periodic) {
+			t->timer.expires = jiffies + t->ms * HZ / 1000;
+			atomic_inc(&t->wl->callbacks);
+			add_timer(&t->timer);
+			t->set = true;
+		} else
+			t->set = false;
+
+		t->fn(t->arg);
+	}
+
+	atomic_dec(&t->wl->callbacks);
+
+	UNLOCK(t->wl);
+}
+
+/*
+ * Adds a timer to the list. Caller supplies a timer function.
+ * Is called from wlc.
+ *
+ * precondition: perimeter lock has been acquired
+ */
+struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
+				     void (*fn) (void *arg),
+				     void *arg, const char *name)
+{
+	struct brcms_timer *t;
+
+	t = kzalloc(sizeof(struct brcms_timer), GFP_ATOMIC);
+	if (!t) {
+		wiphy_err(wl->wiphy, "wl%d: brcms_init_timer: out of memory\n",
+			  wl->pub->unit);
+		return 0;
+	}
+
+	init_timer(&t->timer);
+	t->timer.data = (unsigned long) t;
+	t->timer.function = brcms_timer;
+	t->wl = wl;
+	t->fn = fn;
+	t->arg = arg;
+	t->next = wl->timers;
+	wl->timers = t;
+
+#ifdef BCMDBG
+	t->name = kmalloc(strlen(name) + 1, GFP_ATOMIC);
+	if (t->name)
+		strcpy(t->name, name);
+#endif
+
+	return t;
+}
+
+/* BMAC_NOTE: Add timer adds only the kernel timer since it's going to be more accurate
+ * as well as it's easier to make it periodic
+ *
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_add_timer(struct brcms_info *wl, struct brcms_timer *t, uint ms,
+		     int periodic)
+{
+#ifdef BCMDBG
+	if (t->set) {
+		wiphy_err(wl->wiphy, "%s: Already set. Name: %s, per %d\n",
+			  __func__, t->name, periodic);
+	}
+#endif
+	t->ms = ms;
+	t->periodic = (bool) periodic;
+	t->set = true;
+	t->timer.expires = jiffies + ms * HZ / 1000;
+
+	atomic_inc(&wl->callbacks);
+	add_timer(&t->timer);
+}
+
+/*
+ * return true if timer successfully deleted, false if still pending
+ *
+ * precondition: perimeter lock has been acquired
+ */
+bool brcms_del_timer(struct brcms_info *wl, struct brcms_timer *t)
+{
+	if (t->set) {
+		t->set = false;
+		if (!del_timer(&t->timer)) {
+			return false;
+		}
+		atomic_dec(&wl->callbacks);
+	}
+
+	return true;
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_free_timer(struct brcms_info *wl, struct brcms_timer *t)
+{
+	struct brcms_timer *tmp;
+
+	/* delete the timer in case it is active */
+	brcms_del_timer(wl, t);
+
+	if (wl->timers == t) {
+		wl->timers = wl->timers->next;
+#ifdef BCMDBG
+		kfree(t->name);
+#endif
+		kfree(t);
+		return;
+
+	}
+
+	tmp = wl->timers;
+	while (tmp) {
+		if (tmp->next == t) {
+			tmp->next = t->next;
+#ifdef BCMDBG
+			kfree(t->name);
+#endif
+			kfree(t);
+			return;
+		}
+		tmp = tmp->next;
+	}
+
+}
+
+/*
+ * runs in software irq context
+ *
+ * precondition: perimeter lock is not acquired
+ */
+static int wl_linux_watchdog(void *ctx)
+{
+	return 0;
+}
+
+struct firmware_hdr {
+	u32 offset;
+	u32 len;
+	u32 idx;
+};
+
+char *brcms_firmwares[MAX_FW_IMAGES] = {
+	"brcm/bcm43xx",
+	NULL
+};
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+int brcms_ucode_init_buf(struct brcms_info *wl, void **pbuf, u32 idx)
+{
+	int i, entry;
+	const u8 *pdata;
+	struct firmware_hdr *hdr;
+	for (i = 0; i < wl->fw.fw_cnt; i++) {
+		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
+		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
+		     entry++, hdr++) {
+			if (hdr->idx == idx) {
+				pdata = wl->fw.fw_bin[i]->data + hdr->offset;
+				*pbuf = kmalloc(hdr->len, GFP_ATOMIC);
+				if (*pbuf == NULL) {
+					wiphy_err(wl->wiphy, "fail to alloc %d"
+						  " bytes\n", hdr->len);
+					goto fail;
+				}
+				memcpy(*pbuf, pdata, hdr->len);
+				return 0;
+			}
+		}
+	}
+	wiphy_err(wl->wiphy, "ERROR: ucode buf tag:%d can not be found!\n",
+		  idx);
+	*pbuf = NULL;
+fail:
+	return -ENODATA;
+}
+
+/*
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+int brcms_ucode_init_uint(struct brcms_info *wl, u32 *data, u32 idx)
+{
+	int i, entry;
+	const u8 *pdata;
+	struct firmware_hdr *hdr;
+	for (i = 0; i < wl->fw.fw_cnt; i++) {
+		hdr = (struct firmware_hdr *)wl->fw.fw_hdr[i]->data;
+		for (entry = 0; entry < wl->fw.hdr_num_entries[i];
+		     entry++, hdr++) {
+			if (hdr->idx == idx) {
+				pdata = wl->fw.fw_bin[i]->data + hdr->offset;
+				if (hdr->len != 4) {
+					wiphy_err(wl->wiphy,
+						  "ERROR: fw hdr len\n");
+					return -ENOMSG;
+				}
+				*data = *((u32 *) pdata);
+				return 0;
+			}
+		}
+	}
+	wiphy_err(wl->wiphy, "ERROR: ucode tag:%d can not be found!\n", idx);
+	return -ENOMSG;
+}
+
+/*
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+static int brcms_request_fw(struct brcms_info *wl, struct pci_dev *pdev)
+{
+	int status;
+	struct device *device = &pdev->dev;
+	char fw_name[100];
+	int i;
+
+	memset((void *)&wl->fw, 0, sizeof(struct brcms_firmware));
+	for (i = 0; i < MAX_FW_IMAGES; i++) {
+		if (brcms_firmwares[i] == NULL)
+			break;
+		sprintf(fw_name, "%s-%d.fw", brcms_firmwares[i],
+			UCODE_LOADER_API_VER);
+		status = request_firmware(&wl->fw.fw_bin[i], fw_name, device);
+		if (status) {
+			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
+				  KBUILD_MODNAME, fw_name);
+			return status;
+		}
+		sprintf(fw_name, "%s_hdr-%d.fw", brcms_firmwares[i],
+			UCODE_LOADER_API_VER);
+		status = request_firmware(&wl->fw.fw_hdr[i], fw_name, device);
+		if (status) {
+			wiphy_err(wl->wiphy, "%s: fail to load firmware %s\n",
+				  KBUILD_MODNAME, fw_name);
+			return status;
+		}
+		wl->fw.hdr_num_entries[i] =
+		    wl->fw.fw_hdr[i]->size / (sizeof(struct firmware_hdr));
+	}
+	wl->fw.fw_cnt = i;
+	return brcms_ucode_data_init(wl);
+}
+
+/*
+ * precondition: can both be called locked and unlocked
+ */
+void brcms_ucode_free_buf(void *p)
+{
+	kfree(p);
+}
+
+/*
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+static void brcms_release_fw(struct brcms_info *wl)
+{
+	int i;
+	for (i = 0; i < MAX_FW_IMAGES; i++) {
+		release_firmware(wl->fw.fw_bin[i]);
+		release_firmware(wl->fw.fw_hdr[i]);
+	}
+}
+
+
+/*
+ * checks validity of all firmware images loaded from user space
+ *
+ * Precondition: Since this function is called in brcms_pci_probe() context,
+ * no locking is required.
+ */
+int brcms_check_firmwares(struct brcms_info *wl)
+{
+	int i;
+	int entry;
+	int rc = 0;
+	const struct firmware *fw;
+	const struct firmware *fw_hdr;
+	struct firmware_hdr *ucode_hdr;
+	for (i = 0; i < MAX_FW_IMAGES && rc == 0; i++) {
+		fw =  wl->fw.fw_bin[i];
+		fw_hdr = wl->fw.fw_hdr[i];
+		if (fw == NULL && fw_hdr == NULL) {
+			break;
+		} else if (fw == NULL || fw_hdr == NULL) {
+			wiphy_err(wl->wiphy, "%s: invalid bin/hdr fw\n",
+				  __func__);
+			rc = -EBADF;
+		} else if (fw_hdr->size % sizeof(struct firmware_hdr)) {
+			wiphy_err(wl->wiphy, "%s: non integral fw hdr file "
+				"size %zu/%zu\n", __func__, fw_hdr->size,
+				sizeof(struct firmware_hdr));
+			rc = -EBADF;
+		} else if (fw->size < MIN_FW_SIZE || fw->size > MAX_FW_SIZE) {
+			wiphy_err(wl->wiphy, "%s: out of bounds fw file size "
+				  "%zu\n", __func__, fw->size);
+			rc = -EBADF;
+		} else {
+			/* check if ucode section overruns firmware image */
+			ucode_hdr = (struct firmware_hdr *)fw_hdr->data;
+			for (entry = 0; entry < wl->fw.hdr_num_entries[i] &&
+			     !rc; entry++, ucode_hdr++) {
+				if (ucode_hdr->offset + ucode_hdr->len >
+				    fw->size) {
+					wiphy_err(wl->wiphy,
+						  "%s: conflicting bin/hdr\n",
+						  __func__);
+					rc = -EBADF;
+				}
+			}
+		}
+	}
+	if (rc == 0 && wl->fw.fw_cnt != i) {
+		wiphy_err(wl->wiphy, "%s: invalid fw_cnt=%d\n", __func__,
+			wl->fw.fw_cnt);
+		rc = -EBADF;
+	}
+	return rc;
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+bool brcms_rfkill_set_hw_state(struct brcms_info *wl)
+{
+	bool blocked = brcms_c_check_radio_disabled(wl->wlc);
+
+	UNLOCK(wl);
+	wiphy_rfkill_set_hw_state(wl->pub->ieee_hw->wiphy, blocked);
+	if (blocked)
+		wiphy_rfkill_start_polling(wl->pub->ieee_hw->wiphy);
+	LOCK(wl);
+	return blocked;
+}
+
+/*
+ * precondition: perimeter lock has been acquired
+ */
+void brcms_msleep(struct brcms_info *wl, uint ms)
+{
+	UNLOCK(wl);
+	msleep(ms);
+	LOCK(wl);
+}
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
new file mode 100644
index 0000000..40e3d37
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/mac80211_if.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#ifndef _BRCM_MAC80211_IF_H_
+#define _BRCM_MAC80211_IF_H_
+
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+
+/* softmac ioctl definitions */
+#define BRCMS_SET_SHORTSLOT_OVERRIDE		146
+
+
+/* BMAC Note: High-only driver is no longer working in softirq context as it needs to block and
+ * sleep so perimeter lock has to be a semaphore instead of spinlock. This requires timers to be
+ * submitted to workqueue instead of being on kernel timer
+ */
+struct brcms_timer {
+	struct timer_list timer;
+	struct brcms_info *wl;
+	void (*fn) (void *);
+	void *arg;		/* argument to fn */
+	uint ms;
+	bool periodic;
+	bool set;
+	struct brcms_timer *next;
+#ifdef BCMDBG
+	char *name;		/* Description of the timer */
+#endif
+};
+
+struct brcms_if {
+	uint subunit;		/* WDS/BSS unit */
+	struct pci_dev *pci_dev;
+};
+
+#define MAX_FW_IMAGES		4
+struct brcms_firmware {
+	u32 fw_cnt;
+	const struct firmware *fw_bin[MAX_FW_IMAGES];
+	const struct firmware *fw_hdr[MAX_FW_IMAGES];
+	u32 hdr_num_entries[MAX_FW_IMAGES];
+};
+
+struct brcms_info {
+	struct brcms_pub *pub;		/* pointer to public wlc state */
+	void *wlc;		/* pointer to private common os-independent data */
+	u32 magic;
+
+	int irq;
+
+	spinlock_t lock;	/* per-device perimeter lock */
+	spinlock_t isr_lock;	/* per-device ISR synchronization lock */
+
+	/* bus type and regsva for unmap in brcms_free() */
+	uint bcm_bustype;	/* bus type */
+	void *regsva;		/* opaque chip registers virtual address */
+
+	/* timer related fields */
+	atomic_t callbacks;	/* # outstanding callback functions */
+	struct brcms_timer *timers;	/* timer cleanup queue */
+
+	struct tasklet_struct tasklet;	/* dpc tasklet */
+	bool resched;		/* dpc needs to be and is rescheduled */
+#ifdef LINUXSTA_PS
+	u32 pci_psstate[16];	/* pci ps-state save/restore */
+#endif
+	struct brcms_firmware fw;
+	struct wiphy *wiphy;
+};
+
+/* misc callbacks */
+extern void brcms_init(struct brcms_info *wl);
+extern uint brcms_reset(struct brcms_info *wl);
+extern void brcms_intrson(struct brcms_info *wl);
+extern u32 brcms_intrsoff(struct brcms_info *wl);
+extern void brcms_intrsrestore(struct brcms_info *wl, u32 macintmask);
+extern int brcms_up(struct brcms_info *wl);
+extern void brcms_down(struct brcms_info *wl);
+extern void brcms_txflowcontrol(struct brcms_info *wl, struct brcms_if *wlif,
+				bool state, int prio);
+extern bool wl_alloc_dma_resources(struct brcms_info *wl, uint dmaddrwidth);
+extern bool brcms_rfkill_set_hw_state(struct brcms_info *wl);
+
+/* timer functions */
+extern struct brcms_timer *brcms_init_timer(struct brcms_info *wl,
+				      void (*fn) (void *arg), void *arg,
+				      const char *name);
+extern void brcms_free_timer(struct brcms_info *wl, struct brcms_timer *timer);
+extern void brcms_add_timer(struct brcms_info *wl, struct brcms_timer *timer,
+			    uint ms, int periodic);
+extern bool brcms_del_timer(struct brcms_info *wl, struct brcms_timer *timer);
+extern void brcms_msleep(struct brcms_info *wl, uint ms);
+
+#endif				/* _BRCM_MAC80211_IF_H_ */
diff --git a/drivers/net/wireless/brcm80211/brcmsmac/main.c b/drivers/net/wireless/brcm80211/brcmsmac/main.c
new file mode 100644
index 0000000..1763c45
--- /dev/null
+++ b/drivers/net/wireless/brcm80211/brcmsmac/main.c
@@ -0,0 +1,6102 @@
+/*
+ * Copyright (c) 2010 Broadcom Corporation
+ *
+ * Permission to use, copy, modify, and/or distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
+ * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
+ * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
+ * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <linux/pci_ids.h>
+#include <net/mac80211.h>
+
+#include <brcm_hw_ids.h>
+#include <aiutils.h>
+#include "rate.h"
+#include "scb.h"
+#include "phy/phy_hal.h"
+#include "channel.h"
+#include "bmac.h"
+#include "antsel.h"
+#include "stf.h"
+#include "ampdu.h"
+#include "alloc.h"
+#include "mac80211_if.h"
+#include "main.h"
+
+/*
+ * WPA(2) definitions
+ */
+#define RSN_CAP_4_REPLAY_CNTRS		2
+#define RSN_CAP_16_REPLAY_CNTRS		3
+
+#define WPA_CAP_4_REPLAY_CNTRS		RSN_CAP_4_REPLAY_CNTRS
+#define WPA_CAP_16_REPLAY_CNTRS		RSN_CAP_16_REPLAY_CNTRS
+
+/*
+ * Indication for txflowcontrol that all priority bits in
+ * TXQ_STOP_FOR_PRIOFC_MASK are to be considered.
+ */
+#define ALLPRIO		-1
+
+/*
+ * 32 SSID chars, max of 4 chars for each SSID char "\xFF", plus NULL.
+ */
+#define SSID_FMT_BUF_LEN	((4 * IEEE80211_MAX_SSID_LEN) + 1)
+
+#define	TIMER_INTERVAL_WATCHDOG	1000	/* watchdog timer, in unit of ms */
+#define	TIMER_INTERVAL_RADIOCHK	800	/* radio monitor timer, in unit of ms */
+
+/* Max MPC timeout, in unit of watchdog */
+#ifndef BRCMS_MPC_MAX_DELAYCNT
+#define	BRCMS_MPC_MAX_DELAYCNT	10
+#endif
+
+/* Min MPC timeout, in unit of watchdog */
+#define	BRCMS_MPC_MIN_DELAYCNT	1
+#define	BRCMS_MPC_THRESHOLD	3	/* MPC count threshold level */
+
+#define	BEACON_INTERVAL_DEFAULT	100	/* beacon interval, in unit of 1024TU */
+#define	DTIM_INTERVAL_DEFAULT	3	/* DTIM interval, in unit of beacon interval */
+
+/* Scale down delays to accommodate QT slow speed */
+#define	BEACON_INTERVAL_DEF_QT	20	/* beacon interval, in unit of 1024TU */
+#define	DTIM_INTERVAL_DEF_QT	1	/* DTIM interval, in unit of beacon interval */
+
+#define	TBTT_ALIGN_LEEWAY_US	100	/* min leeway before first TBTT in us */
+
+/* Software feature flag defines used by wlfeatureflag */
+#define WL_SWFL_NOHWRADIO	0x0004
+#define WL_SWFL_FLOWCONTROL     0x0008	/* Enable backpressure to OS stack */
+#define WL_SWFL_WLBSSSORT	0x0010	/* Per-port supports sorting of BSS */
+
+/* n-mode support capability */
+/* 2x2 includes both 1x1 & 2x2 devices
+ * reserved #define 2 for future when we want to separate 1x1 & 2x2 and
+ * control it independently
+ */
+#define WL_11N_2x2			1
+#define WL_11N_3x3			3
+#define WL_11N_4x4			4
+
+/* define 11n feature disable flags */
+#define WLFEATURE_DISABLE_11N		0x00000001
+#define WLFEATURE_DISABLE_11N_STBC_TX	0x00000002
+#define WLFEATURE_DISABLE_11N_STBC_RX	0x00000004
+#define WLFEATURE_DISABLE_11N_SGI_TX	0x00000008
+#define WLFEATURE_DISABLE_11N_SGI_RX	0x00000010
+#define WLFEATURE_DISABLE_11N_AMPDU_TX	0x00000020
+#define WLFEATURE_DISABLE_11N_AMPDU_RX	0x00000040
+#define WLFEATURE_DISABLE_11N_GF	0x00000080
+
+#define EDCF_ACI_MASK                0x60
+#define EDCF_ACI_SHIFT               5
+#define EDCF_ECWMIN_MASK             0x0f
+#define EDCF_ECWMAX_SHIFT            4
+#define EDCF_AIFSN_MASK              0x0f
+#define EDCF_AIFSN_MAX               15
+#define EDCF_ECWMAX_MASK             0xf0
+
+#define EDCF_AC_BE_TXOP_STA          0x0000
+#define EDCF_AC_BK_TXOP_STA          0x0000
+#define EDCF_AC_VO_ACI_STA           0x62
+#define EDCF_AC_VO_ECW_STA           0x32
+#define EDCF_AC_VI_ACI_STA           0x42
+#define EDCF_AC_VI_ECW_STA           0x43
+#define EDCF_AC_BK_ECW_STA           0xA4
+#define EDCF_AC_VI_TXOP_STA          0x005e
+#define EDCF_AC_VO_TXOP_STA          0x002f
+#define EDCF_AC_BE_ACI_STA           0x03
+#define EDCF_AC_BE_ECW_STA           0xA4
+#define EDCF_AC_BK_ACI_STA           0x27
+#define EDCF_AC_VO_TXOP_AP           0x002f
+
+#define EDCF_TXOP2USEC(txop)         ((txop) << 5)
+#define EDCF_ECW2CW(exp)             ((1 << (exp)) - 1)
+
+#define APHY_SYMBOL_TIME	4
+#define APHY_PREAMBLE_TIME	16
+#define APHY_SIGNAL_TIME	4
+#define APHY_SIFS_TIME		16
+#define APHY_SERVICE_NBITS	16
+#define APHY_TAIL_NBITS		6
+#define BPHY_SIFS_TIME		10
+#define BPHY_PLCP_SHORT_TIME	96
+
+#define PREN_PREAMBLE		24
+#define PREN_MM_EXT		12
+#define PREN_PREAMBLE_EXT	4
+
+#define DOT11_MAC_HDR_LEN		24
+#define	DOT11_ACK_LEN		10
+#define DOT11_BA_LEN		4
+#define DOT11_OFDM_SIGNAL_EXTENSION	6
+#define DOT11_MIN_FRAG_LEN		256
+#define	DOT11_RTS_LEN		16
+#define	DOT11_CTS_LEN		10
+#define DOT11_BA_BITMAP_LEN		128
+#define DOT11_MIN_BEACON_PERIOD		1
+#define DOT11_MAX_BEACON_PERIOD		0xFFFF
+#define	DOT11_MAXNUMFRAGS	16
+#define DOT11_MAX_FRAG_LEN		2346
+
+#define BPHY_PLCP_TIME		192
+#define RIFS_11N_TIME		2
+
+#define WME_VER			1
+#define WME_SUBTYPE_PARAM_IE	1
+#define WME_TYPE		2
+#define WME_OUI			"\x00\x50\xf2"
+
+#define AC_BE			0
+#define AC_BK			1
+#define AC_VI			2
+#define AC_VO			3
+
+/*
+ * driver maintains internal 'tick'(wlc->pub->now) which increments in 1s OS timer(soft
+ * watchdog) it is not a wall clock and won't increment when driver is in "down" state
+ * this low resolution driver tick can be used for maintenance tasks such as phy
+ * calibration and scb update
+ */
+
+/* To inform the ucode of the last mcast frame posted so that it can clear moredata bit */
+#define BCMCFID(wlc, fid) brcms_b_write_shm((wlc)->hw, M_BCMC_FID, (fid))
+
+#define BRCMS_WAR16165(wlc) (wlc->pub->sih->bustype == PCI_BUS && \
+				(!AP_ENAB(wlc->pub)) && (wlc->war16165))
+
+/* debug/trace */
+uint brcm_msg_level =
+#if defined(BCMDBG)
+	LOG_ERROR_VAL;
+#else
+	0;
+#endif				/* BCMDBG */
+
+/* Find basic rate for a given rate */
+#define BRCMS_BASIC_RATE(wlc, rspec)	(IS_MCS(rspec) ? \
+			(wlc)->band->basic_rate[mcs_table[rspec & RSPEC_RATE_MASK].leg_ofdm] : \
+			(wlc)->band->basic_rate[rspec & RSPEC_RATE_MASK])
+
+#define FRAMETYPE(r, mimoframe)	(IS_MCS(r) ? mimoframe	: (IS_CCK(r) ? FT_CCK : FT_OFDM))
+
+#define RFDISABLE_DEFAULT	10000000	/* rfdisable delay timer 500 ms, runs of ALP clock */
+
+#define BRCMS_TEMPSENSE_PERIOD		10	/* 10 second timeout */
+
+#define SCAN_IN_PROGRESS(x)	0
+
+#define EPI_VERSION_NUM		0x054b0b00
+
+#ifdef BCMDBG
+/* pointer to most recently allocated wl/wlc */
+static struct brcms_c_info *wlc_info_dbg = (struct brcms_c_info *) (NULL);
+#endif
+
+const u8 prio2fifo[NUMPRIO] = {
+	TX_AC_BE_FIFO,		/* 0    BE      AC_BE   Best Effort */
+	TX_AC_BK_FIFO,		/* 1    BK      AC_BK   Background */
+	TX_AC_BK_FIFO,		/* 2    --      AC_BK   Background */
+	TX_AC_BE_FIFO,		/* 3    EE      AC_BE   Best Effort */
+	TX_AC_VI_FIFO,		/* 4    CL      AC_VI   Video */
+	TX_AC_VI_FIFO,		/* 5    VI      AC_VI   Video */
+	TX_AC_VO_FIFO,		/* 6    VO      AC_VO   Voice */
+	TX_AC_VO_FIFO		/* 7    NC      AC_VO   Voice */
+};
+
+/* precedences numbers for wlc queues. These are twice as may levels as
+ * 802.1D priorities.
+ * Odd numbers are used for HI priority traffic at same precedence levels
+ * These constants are used ONLY by wlc_prio2prec_map.  Do not use them elsewhere.
+ */
+#define	_BRCMS_PREC_NONE		0	/* None = - */
+#define	_BRCMS_PREC_BK		2	/* BK - Background */
+#define	_BRCMS_PREC_BE		4	/* BE - Best-effort */
+#define	_BRCMS_PREC_EE		6	/* EE - Excellent-effort */
+#define	_BRCMS_PREC_CL		8	/* CL - Controlled Load */
+#define	_BRCMS_PREC_VI		10	/* Vi - Video */
+#define	_BRCMS_PREC_VO		12	/* Vo - Voice */
+#define	_BRCMS_PREC_NC		14	/* NC - Network Control */
+
+#define MAXMACLIST		64	/* max # source MAC matches */
+#define BCN_TEMPLATE_COUNT	2
+
+/* The BSS is generating beacons in HW */
+#define BRCMS_BSSCFG_HW_BCN	0x20
+
+#define HWBCN_ENAB(cfg)		(((cfg)->flags & BRCMS_BSSCFG_HW_BCN) != 0)
+
+#define MBSS_BCN_ENAB(cfg)       0
+#define MBSS_PRB_ENAB(cfg)       0
+#define SOFTBCN_ENAB(pub)    (0)
+
+/* 802.1D Priority to precedence queue mapping */
+const u8 wlc_prio2prec_map[] = {
+	_BRCMS_PREC_BE,		/* 0 BE - Best-effort */
+	_BRCMS_PREC_BK,		/* 1 BK - Background */
+	_BRCMS_PREC_NONE,		/* 2 None = - */
+	_BRCMS_PREC_EE,		/* 3 EE - Excellent-effort */
+	_BRCMS_PREC_CL,		/* 4 CL - Controlled Load */
+	_BRCMS_PREC_VI,		/* 5 Vi - Video */
+	_BRCMS_PREC_VO,		/* 6 Vo - Voice */
+	_BRCMS_PREC_NC,		/* 7 NC - Network Control */
+};
+
+/* Check if a particular BSS config is AP or STA */
+#define BSSCFG_AP(cfg)		(0)
+#define BSSCFG_STA(cfg)		(1)
+#define BSSCFG_IBSS(cfg)	(!(cfg)->BSS)
+
+/* As above for all non-NULL BSS configs */
+#define FOREACH_BSS(wlc, idx, cfg) \
+	for (idx = 0; (int) idx < BRCMS_MAXBSSCFG; idx++) \
+		if ((cfg = (wlc)->bsscfg[idx]))
+
+/* TX FIFO number to WME/802.1E Access Category */
+const u8 wme_fifo2ac[] = { AC_BK, AC_BE, AC_VI, AC_VO, AC_BE, AC_BE };
+
+/* WME/802.1E Access Category to TX FIFO number */
+static const u8 wme_ac2fifo[] = { 1, 0, 2, 3 };
+
+static bool in_send_q;
+
+/* Shared memory location index for various AC params */
+#define wme_shmemacindex(ac)	wme_ac2fifo[ac]
+
+#ifdef BCMDBG
+static const char * const fifo_names[] = {
+	"AC_BK", "AC_BE", "AC_VI", "AC_VO", "BCMC", "ATIM" };
+#else
+static const char fifo_names[6][0];
+#endif
+
+static const u8 acbitmap2maxprio[] = {
+	PRIO_8021D_BE, PRIO_8021D_BE, PRIO_8021D_BK, PRIO_8021D_BK,
+	PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI, PRIO_8021D_VI,
+	PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO,
+	PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO, PRIO_8021D_VO
+};
+
+/* currently the best mechanism for determining SIFS is the band in use */
+#define SIFS(band) ((band)->bandtype == BRCM_BAND_5G ? APHY_SIFS_TIME : \
+						       BPHY_SIFS_TIME);
+
+/* local prototypes */
+static u16 brcms_c_d11hdrs_mac80211(struct brcms_c_info *wlc,
+					       struct ieee80211_hw *hw,
+					       struct sk_buff *p,
+					       struct scb *scb, uint frag,
+					       uint nfrags, uint queue,
+					       uint next_frag_len,
+					       struct wsec_key *key,
+					       ratespec_t rspec_override);
+static void brcms_c_bss_default_init(struct brcms_c_info *wlc);
+static void brcms_c_ucode_mac_upd(struct brcms_c_info *wlc);
+static ratespec_t mac80211_wlc_set_nrate(struct brcms_c_info *wlc,
+					 struct brcms_band *cur_band,
+					 u32 int_val);
+static void brcms_c_tx_prec_map_init(struct brcms_c_info *wlc);
+static void brcms_c_watchdog(void *arg);
+static void brcms_c_watchdog_by_timer(void *arg);
+static u16 brcms_c_rate_shm_offset(struct brcms_c_info *wlc, u8 rate);
+static int brcms_c_set_rateset(struct brcms_c_info *wlc, wlc_rateset_t *rs_arg);
+static u8 brcms_c_local_constraint_qdbm(struct brcms_c_info *wlc);
+
+/* send and receive */
+static struct brcms_txq_info *brcms_c_txq_alloc(struct brcms_c_info *wlc);
+static void brcms_c_txq_free(struct brcms_c_info *wlc,
+			 struct brcms_txq_info *qi);
+static void brcms_c_txflowcontrol_signal(struct brcms_c_info *wlc,
+				     struct brcms_txq_info *qi,
+				     bool on, int prio);
+static void brcms_c_txflowcontrol_reset(struct brcms_c_info *wlc);
+static void brcms_c_compute_cck_plcp(struct brcms_c_info *wlc, ratespec_t rate,
+				 uint length, u8 *plcp);
+static void brcms_c_compute_ofdm_plcp(ratespec_t rate, uint length, u8 *plcp);
+static void brcms_c_compute_mimo_plcp(ratespec_t rate, uint length, u8 *plcp);
+static u16 brcms_c_compute_frame_dur(struct brcms_c_info *wlc, ratespec_t rate,
+				    u8 preamble_type, uint next_frag_len);
+static u64 brcms_c_recover_tsf64(struct brcms_c_info *wlc,
+			     struct brcms_d11rxhdr *rxh);
+static void brcms_c_recvctl(struct brcms_c_info *wlc,
+			struct d11rxhdr *rxh, struct sk_buff *p);
+static uint brcms_c_calc_frame_len(struct brcms_c_info *wlc, ratespec_t rate,
+			       u8 preamble_type, uint dur);
+static uint brcms_c_calc_ack_time(struct brcms_c_info *wlc, ratespec_t rate,
+			      u8 preamble_type);
+static uint brcms_c_calc_cts_time(struct brcms_c_info *wlc, ratespec_t rate,
+			      u8 preamble_type);
+/* interrupt, up/down, band */
+static void brcms_c_setband(struct brcms_c_info *wlc, uint bandunit);
+static chanspec_t brcms_c_init_chanspec(struct brcms_c_info *wlc);
+static void brcms_c_bandinit_ordered(struct brcms_c_info *wlc,
+				     chanspec_t chanspec);
+static void brcms_c_bsinit(struct brcms_c_info *wlc);
+static int brcms_c_duty_cycle_set(struct brcms_c_info *wlc, int duty_cycle,
+			      bool isOFDM, bool writeToShm);
+static void brcms_c_radio_hwdisable_upd(struct brcms_c_info *wlc);
+static bool brcms_c_radio_monitor_start(struct brcms_c_info *wlc);
+static void brcms_c_radio_timer(void *arg);
+static void brcms_c_radio_enable(struct brcms_c_info *wlc);
+static void brcms_c_radio_upd(struct brcms_c_info *wlc);
+
+/* scan, association, BSS */
+static uint brcms_c_calc_ba_time(struct brcms_c_info *wlc, ratespec_t rate,
+			     u8 preamble_type);
+static void brcms_c_update_mimo_band_bwcap(struct brcms_c_info *wlc, u8 bwcap);
+static void brcms_c_ht_update_sgi_rx(struct brcms_c_info *wlc, int val);
+static void brcms_c_ht_update_ldpc(struct brcms_c_info *wlc, s8 val);
+static void brcms_c_war16165(struct brcms_c_info *wlc, bool tx);
+
+static void brcms_c_wme_retries_write(struct brcms_c_info *wlc);
+static bool brcms_c_attach_stf_ant_init(struct brcms_c_info *wlc);
+static uint brcms_c_attach_module(struct brcms_c_info *wlc);
+static void brcms_c_detach_module(struct brcms_c_info *wlc);
+static void brcms_c_timers_deinit(struct brcms_c_info *wlc);
+static void brcms_c_down_led_upd(struct brcms_c_info *wlc);
+static uint brcms_c_down_del_timer(struct brcms_c_info *wlc);
+static void brcms_c_ofdm_rateset_war(struct brcms_c_info *wlc);
+static int _brcms_c_ioctl(struct brcms_c_info *wlc, int cmd, void *arg, int len,
+		      struct brcms_c_if *wlcif);
+
+/* conditions under which the PM bit should be set in outgoing frames and STAY_AWAKE is meaningful
+ */
+bool brcms_c_ps_allowed(struct brcms_c_info *wlc)
+{
+	int idx;
+	struct brcms_bss_cfg *cfg;
+
+	/* disallow PS when one of the following global conditions meets */
+	if (!wlc->pub->associated)
+		return false;
+
+	/* disallow PS when one of these meets when not scanning */
+	if (AP_ACTIVE(wlc) || wlc->monitor)
+		return false;
+
+	for (idx = 0; idx < BRCMS_MAXBSSCFG; idx++) {
+		cfg = wlc->bsscfg[idx];
+		if (cfg && BSSCFG_STA(cfg) && cfg->associated) {
+			/*
+			 * disallow PS when one of the following
+			 * bsscfg specific conditions meets
+			 */
+			if (!cfg->BSS || !BRCMS_PORTOPEN(cfg))
+				return false;
+
+			if (!cfg->dtim_programmed)
+				return false;
+		}
+	}
+
+	return true;
+}
+
+void brcms_c_reset(struct brcms_c_info *wlc)
+{
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+	/* slurp up hw mac counters before core reset */
+	brcms_c_statsupd(wlc);
+
+	/* reset our snapshot of macstat counters */
+	memset((char *)wlc->core->macstat_snapshot, 0,
+		sizeof(struct macstat));
+
+	brcms_b_reset(wlc->hw);
+}
+
+void brcms_c_fatal_error(struct brcms_c_info *wlc)
+{
+	wiphy_err(wlc->wiphy, "wl%d: fatal error, reinitializing\n",
+		  wlc->pub->unit);
+	brcms_init(wlc->wl);
+}
+
+/* Return the channel the driver should initialize during brcms_c_init.
+ * the channel may have to be changed from the currently configured channel
+ * if other configurations are in conflict (bandlocked, 11n mode disabled,
+ * invalid channel for current country, etc.)
+ */
+static chanspec_t brcms_c_init_chanspec(struct brcms_c_info *wlc)
+{
+	chanspec_t chanspec =
+	    1 | WL_CHANSPEC_BW_20 | WL_CHANSPEC_CTL_SB_NONE |
+	    WL_CHANSPEC_BAND_2G;
+
+	return chanspec;
+}
+
+struct scb global_scb;
+
+static void brcms_c_init_scb(struct brcms_c_info *wlc, struct scb *scb)
+{
+	int i;
+	scb->flags = SCB_WMECAP | SCB_HTCAP;
+	for (i = 0; i < NUMPRIO; i++)
+		scb->seqnum[i] = 0;
+}
+
+void brcms_c_init(struct brcms_c_info *wlc)
+{
+	d11regs_t *regs;
+	chanspec_t chanspec;
+	int i;
+	struct brcms_bss_cfg *bsscfg;
+	bool mute = false;
+
+	BCMMSG(wlc->wiphy, "wl%d\n", wlc->pub->unit);
+
+	regs = wlc->regs;
+
+	/* This will happen if a big-hammer was executed. In that case, we want to go back
+	 * to the channel that we were on and not new channel
+	 */
+	if (wlc->pub->associated)
+		chanspec = wlc->home_chanspec;
+	else
+		chanspec = brcms_c_init_chanspec(wlc);
+
+	brcms_b_init(wlc->hw, chanspec, mute);
+
+	/* update beacon listen interval */
+	brcms_c_bcn_li_upd(wlc);
+
+	/* the world is new again, so is our reported rate */
+	brcms_c_reprate_init(wlc);
+
+	/* write ethernet address to core */
+	FOREACH_BSS(wlc, i, bsscfg) {
+		brcms_c_set_mac(bsscfg);
+		brcms_c_set_bssid(bsscfg);
+	}
+
+	/* Update tsf_cfprep if associated and up */
+	if (wlc->pub->associated) {
+		FOREACH_BSS(wlc, i, bsscfg) {
+			if (bsscfg->up) {
+				u32 bi;
+
+				/* get beacon period and convert to uS */
+				bi = bsscfg->current_bss->beacon_period << 10;
+				/*
+				 * update since init path would reset
+				 * to default value
+				 */
+				W_REG(&regs->tsf_cfprep,
+				      (bi << CFPREP_CBI_SHIFT));
+
+				/* Update maccontrol PM related bits */
+				brcms_c_set_ps_ctrl(wlc);
+
+				break;
+			}
+		}
+	}
+
+	brcms_c_bandinit_ordered(wlc, chanspec);
+
+	brcms_c_init_scb(wlc, &global_scb);
+
+	/* init probe response timeout */
+	brcms_c_write_shm(wlc, M_PRS_MAXTIME, wlc->prb_resp_timeout);
+
+	/* init max burst txop (framebursting) */
+	brcms_c_write_shm(wlc, M_MBURST_TXOP,
+		      (wlc->
+		       _rifs ? (EDCF_AC_VO_TXOP_AP << 5) : MAXFRAMEBURST_TXOP));
+
+	/* initialize maximum allowed duty cycle */
+	brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_ofdm, true, true);
+	brcms_c_duty_cycle_set(wlc, wlc->tx_duty_cycle_cck, false, true);
+
+	/* Update some shared memory locations related to max AMPDU size allowed to received */
+	brcms_c_ampdu_shm_upd(wlc->ampdu);
+
+	/* band-specific inits */
+	brcms_c_bsinit(wlc);
+
+	/* Enable EDCF mode (while the MAC is suspended) */
+	if (EDCF_ENAB(wlc->pub)) {
+		OR_REG(&regs->ifs_ctl, IFS_USEEDCF);
+		brcms_c_edcf_setparams(wlc, false);
+	}
+
+	/* Init precedence maps for empty FIFOs */
+	brcms_c_tx_prec_map_init(wlc);
+
+	/* read the ucode version if we have not yet done so */
+	if (wlc->ucode_rev == 0) {
+		wlc->ucode_rev =
+		    brcms_c_read_shm(wlc, M_BOM_REV_MAJOR) << NBITS(u16);
+		wlc->ucode_rev |= brcms_c_read_shm(wlc, M_BOM_REV_MINOR);
+	}
+
+	/* ..now really unleash hell (allow the MAC out of suspend) */
+	brcms_c_enable_mac(wlc);
+
+	/* clear tx flow control */
+	brcms_c_txflowcontrol_reset(wlc);
+
+	/* clear tx data fifo suspends */
+	wlc->tx_suspended = false;
+
+	/* enable the RF Disable Delay timer */
+	W_REG(&wlc->regs->rfdisabledly, RFDISABLE_DEFAULT);
+
+	/* initialize mpc delay */
+	wlc->mpc_delay_off = wlc->mpc_dlycnt = BRCMS_MPC_MIN_DELAYCNT;
+
+	/*
+	 * Initialize WME parameters; if they haven't been set by some other
+	 * mechanism (IOVar, etc) then read them from the hardware.
+	 */
+	if (BRCMS_WME_RETRY_SHORT_GET(wlc, 0) == 0) {
+		/* Uninitialized; read from HW */
+		int ac;
+
+		for (ac = 0; ac < AC_COUNT; ac++) {
+			wlc->wme_retries[ac] =
+			    brcms_c_read_shm(wlc, M_AC_TXLMT_ADDR(ac));
+		}
+	}
+}
+
+void brcms_c_mac_bcn_promisc_change(struct brcms_c_info *wlc, bool promisc)
+{
+	wlc->bcnmisc_monitor = promisc;
+	brcms_c_mac_bcn_promisc(wlc);
+}
+
+void brcms_c_mac_bcn_promisc(struct brcms_c_info *wlc)
+{
+	if ((AP_ENAB(wlc->pub) && (N_ENAB(wlc->pub) || wlc->band->gmode)) ||
+	    wlc->bcnmisc_ibss || wlc->bcnmisc_scan || wlc->bcnmisc_monitor)
+		brcms_c_mctrl(wlc, MCTL_BCNS_PROMISC, MCTL_BCNS_PROMISC);
+	else
+		brcms_c_mctrl(wlc, MCTL_BCNS_PROMISC, 0);
+}
+
+/* set or clear maccontrol bits MCTL_PROMISC and MCTL_KEEPCONTROL */
+void brcms_c_mac_promisc(struct brcms_c_info *wlc)
+{
+	u32 promisc_bits = 0;
+
+	/* promiscuous mode just sets MCTL_PROMISC
+	 * Note: APs get all BSS traffic without the need to set the MCTL_PROMISC bit
+	 * since all BSS data traffic is directed at the AP
+	 */
+	if (PROMISC_ENAB(wlc->pub) && !AP_ENAB(wlc->pub))
+		promisc_bits |= MCTL_PROMISC;
+
+	/* monitor mode needs both MCTL_PROMISC and MCTL_KEEPCONTROL
+	 * Note: monitor mode also needs MCTL_BCNS_PROMISC, but that is
+	 * handled in brcms_c_mac_bcn_promisc()
+	 */
+	if (MONITOR_ENAB(wlc))
+		promisc_bits |= MCTL_PROMISC | MCTL_KEEPCONTROL;
+
+	brcms_c_mctrl(wlc, MCTL_PROMISC | MCTL_KEEPCONTROL, promisc_bits);
+}
+
+/* push sw hps and wake state through hardware */
+void brcms_c_set_ps_ctrl(struct brcms_c_info *wlc)
+{
+	u32 v1, v2;
+	bool hps;
+	bool awake_before;
+
+	hps = PS_ALLOWED(wlc);
+
+	BCMMSG(wlc->wiphy, "wl%d: hps %d\n", wlc->pub->unit, hps);
+
+	v1 = R_REG(&wlc->regs->maccontrol);
+	v2 = MCTL_WAKE;
+	if (hps)
+		v2 |= MCTL_HPS;
+
+	brcms_c_mctrl(wlc, MCTL_WAKE | MCTL_HPS, v2);
+
+	awake_before = ((v1 & MCTL_WAKE) || ((v1 & MCTL_HPS) == 0));
+
+	if (!awake_before)
+		brcms_b_wait_for_wake(wlc->hw);
+
+}
+
+/*
+ * Write this BSS config's MAC address to core.
+ * Updates RXE match engine.
+ */
+int brcms_c_set_mac(struct brcms_bss_cfg *cfg)
+{
+	int err = 0;
+	struct brcms_c_info *wlc = cfg->wlc;
+
+	if (cfg == wlc->cfg) {
+		/* enter the MAC addr into the RXE match registers */
+		brcms_c_set_addrmatch(wlc, RCM_MAC_OFFSET, cfg->cur_etheraddr);
+	}
+
+	brcms_c_ampdu_macaddr_upd(wlc);
+
+	return err;
+}
+
+/* Write the BSS config's BSSID address to core (set_bssid in d11procs.tcl).
+ * Updates RXE match engine.
+ */
+void brcms_c_set_bssid(struct brcms_bss_cfg *cfg)
+{
+	struct brcms_c_info *wlc = cfg->wlc;
+
+	/* if primary config, we need to update BSSID in RXE match registers */
+	if (cfg == wlc->cfg) {
+		brcms_c_set_addrmatch(wlc, RCM_BSSID_OFFSET, cfg->BSSID);
+	}
+#ifdef SUPPORT_HWKEYS
+	else if (BSSCFG_STA(cfg) && cfg->BSS) {
+		brcms_c_rcmta_add_bssid(wlc, cfg);
+	}
+#endif
+}
+
+/*
+ * Suspend the the MAC and update the slot timing
+ * for standard 11b/g (20us slots) or shortslot 11g (9us slots).
+ */
+void brcms_c_switch_shortslot(struct brcms_c_info *wlc, bool shortslot)
+{
+	int idx;
+	struct brcms_bss_cfg *cfg;
+
+	/* use the override if it is set */
+	if (wlc->shortslot_override != BRCMS_SHORTSLOT_AUTO)
+		shortslot = (wlc->shortslot_override == BRCMS_SHORTSLOT_ON);
+
+	if (wlc->shortslot == shortslot)
+		return;
+
+	wlc->shortslot = shortslot;
+
+	/* update the capability based on current shortslot mode */
+	FOREACH_BSS(wlc, idx, cfg) {
+		if (!cfg->associated)
+			continue;
+		cfg->current_bss->capability &=
+					~WLAN_CAPABILITY_SHORT_SLOT_TIME;
+		if (wlc->shortslot)
+			cfg->current_bss->capability |=
+					WLAN_CAPABILITY_SHORT_SLOT_TIME;
+	}
+
+	brcms_b_set_shortslot(wlc->hw, shortslot);
+}
+
+static u8 brcms_c_local_constraint_qdbm(struct brcms_c_info *wlc)
+{
+	u8 local;
+	s16 local_max;
+
+	local = BRCMS_TXPWR_MAX;
+	if (wlc->pub->associated &&
+	    (brcmu_chspec_ctlchan(wlc->chanspec) ==
+	     brcmu_chspec_ctlchan(wlc->home_chanspec))) {
+
+		/* get the local power constraint if we are on the AP's
+		 * channel [802.11h, 7.3.2.13]
+		 */
+		/* Clamp the value between 0 and BRCMS_TXPWR_MAX w/o
+		 * overflowing the target */
+		local_max =
+		    (wlc->txpwr_local_max -
+		     wlc->txpwr_local_constraint) * BRCMS_TXPWR_DB_FACTOR;
+		if (local_max > 0 && local_max < BRCMS_TXPWR_MAX)
+			return (u8) local_max;
+		if (local_max < 0)
+			return 0;
+	}
+
+	return local;
+}
+
+/* propagate home chanspec to all bsscfgs in case bsscfg->current_bss->chanspec is referenced */
+void brcms_c_set_home_chanspec(struct brcms_c_info *wlc, chanspec_t chanspec)
+{
+	if (wlc->home_chanspec != chanspec) {
+		int idx;
+		struct brcms_bss_cfg *cfg;
+
+		wlc->home_chanspec = chanspec;
+
+		FOREACH_BSS(wlc, idx, cfg) {
+			if (!cfg->associated)
+				continue;
+
+			cfg->current_bss->chanspec = chanspec;
+		}
+
+	}
+}
+
+static void brcms_c_set_phy_chanspec(struct brcms_c_info *wlc,
+				     chanspec_t chanspec)
+{
+	/* Save our copy of the chanspec */
+	wlc->chanspec = chanspec;
+
+	/* Set the chanspec and power limits for this locale after computing
+	 * any 11h local tx power constraints.
+	 */
+	brcms_c_channel_set_chanspec(wlc->cmi, chanspec,
+				 brcms_c_local_constraint_qdbm(wlc));
+
+	if (wlc->stf->ss_algosel_auto)
+		brcms_c_stf_ss_algo_channel_get(wlc, &wlc->stf->ss_algo_channel,
+					    chanspec);
+
+	brcms_c_stf_ss_update(wlc, wlc->band);
+
+}
+
+void brcms_c_set_chanspec(struct brcms_c_info *wlc, chanspec_t chanspec)
+{
+	uint bandunit;
+	bool switchband = false;
+	chanspec_t old_chanspec = wlc->chanspec;
+
+	if (!brcms_c_valid_chanspec_db(wlc->cmi, chanspec)) {
+		wiphy_err(wlc->wiphy, "wl%d: %s: Bad channel %d\n",
+			  wlc->pub->unit, __func__, CHSPEC_CHANNEL(chanspec));
+		return;
+	}
+
+	/* Switch bands if necessary */
+	if (NBANDS(wlc) > 1) {
+		bandunit = CHSPEC_BANDUNIT(chanspec);
+		if (wlc->band->bandunit != bandunit || wlc->bandinit_pending) {
+			switchband = true;
+			if (wlc->bandlocked) {
+				wiphy_err(wlc->wiphy, "wl%d: %s: chspec %d "
+					  "band is locked!\n",
+					  wlc->pub->unit, __func__,
+					  CHSPEC_CHANNEL(chanspec));
+				return;
+			}
+			/*
+			 * should the setband call come after the
+			 * brcms_b_chanspec() ? if the se