network: wireless: bcm4329: Add bcm4329 driver

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Add "HANG" event and console monitoring

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Remove unnecessary set_freezable() calls

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Update to Version 4.218.238

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Fix MAC address import

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Fix wlan card removal

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Update to Version 4.218.239
    Add reading mac address from platform data
    Add dhd_os_proto_block protection for dhd_preinit_ioctls
    Revert dhdsdio_clk() changes

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add SoftAP MAC address randomization

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Add 2.6.35 compatibility and fix memory leak in set_multicast_list

Signed-off-by: Dmitry Shmidt <dimitrysh@android.com>
Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Reduce driver loading time

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Set special OUI: 02:1A:11:FH:HH:HH for SoftAP

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Fix BUS DOWN in IOCTL

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

network: wireless: bcm4329: Add driver SETSUSPEND command

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix packet filter to only filter out non-unicast frames

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add wakelock processing in ioctl and messaging

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to 4.218.245 (combo scan)

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.246 and setband fix

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add wakelock processing to WEXT requests

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Force scan when driver is loaded

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix scan timeout for abg case

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix dhd_bus_watchdog() race conditions

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix unregister_early_suspend() in dhd_detach()

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add memory barriers to wait functions

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix "setsuspend" behavior

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Increase PMU_MAX_TRANSITION_DLY to 1 sec

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Turn OFF packet filtering during DHCP session

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Disable packet filtering

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Send "HANG" message only once

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.248

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix HW_OOB interrupt processing

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix interrupt enabling for level interrupt

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

Revert "net: wireless: bcm4329: Fix interrupt enabling for level interrupt"

This reverts commit 261d21bbfffef6261696d0d13672d2e4f9d76f05.

Revert "net: wireless: bcm4329: Fix HW_OOB interrupt processing"

This reverts commit 8bd035daa820dc5612ae311262c71dc133871046.

net: wireless: bcm4329: Fix HW_OOB interrupt processing

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix roaming failure case

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix roaming setting on resume

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add debug print for cscan failure

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Ignore error if scan results are empty

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Enable packet filtering during low power mode

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.248.6

Fix watchdog time rescheduling, fix first scan no-return

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix interrupt enabling in case of error

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix setting HT clock race conditions

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix setting HT clock race conditions in driver start

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.248-10

Add KEEP_ALIVE feature, fix 'driver rssi' processing, fix roaming processing

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Improve BT-coex for eSCO

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix race conditions for sysioc_thread

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to version 4.218.248-11

Check for 'driver start' failure, Set keep-alive feature forever

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Check for out of bounds in scan results parsing

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add check for out of bounds scan buffer

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to version 4.218.248-12

 - Fix crash during Specific Scan handling
 - Fix potential insmod crash by increase Registration Timeout to 12 sec
 - Added max time restriction to PNO scan timer
 - New IWPRIV "AP_STA_DISASSOC" to disassoc STAs in SoftAP mode
 - Add new setting to SoftAP "AP_SET_CFG" to enable Hidden SSID
 - Fixs bugs for SoftAP AP_SET_MAC_FLTR iwpriv commnd
 - Add STAs rssi filed to SoftAP "AP_GET_STA_LIST" IWPRIV commnd

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix watchdog termination after 'driver stop'

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Enable KEEP_ALIVE feature

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add FW Reload event processing

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix Softap start/stop race conditions

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Set first channel passive scan to 30 ms

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.248-15

- Increase default Listen Interval to 20 Beacons
- Add logic to adjust SKIP_DTIM if Listen Interval < DTIM * DTIM_SKIP to
  avoid data lost due to fact that packet maybe not retrieved during Listen
  Interval and AP may toss it
- Fix problem with Hidden AP setting after AP reconfigure
- Add new SoftAP optional parameter as COUNTRY to pass country code to SoftAP

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Improve way to detect BT SCO connection for BT-coex

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Use non-DFS channels only for US

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add 'setdfschannels' command

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix watchdog syncronization during start/stop

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix driver 'start' retry ability

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Fix memory leak in case of dhd_bus_init() failure

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Add CONFIG_FIRST_SCAN option

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to Version 4.218.248-17

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>

net: wireless: bcm4329: Update to version 4.218.248-18

- Improve BT-coex to differentiate HID and SCO device during DHCP session
- Enforce DTIM=1 for any SoftAP settings to optimize internal memory logic

Signed-off-by: Dmitry Shmidt <dimitrysh@google.com>
diff --git a/drivers/net/wireless/bcm4329/siutils.c b/drivers/net/wireless/bcm4329/siutils.c
new file mode 100644
index 0000000..1814db0
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/siutils.c
@@ -0,0 +1,1527 @@
+/*
+ * Misc utility routines for accessing chip-specific features
+ * of the SiliconBackplane-based Broadcom chips.
+ *
+ * Copyright (C) 1999-2010, Broadcom Corporation
+ * 
+ *      Unless you and Broadcom execute a separate written software license
+ * agreement governing use of this software, this software is licensed to you
+ * under the terms of the GNU General Public License version 2 (the "GPL"),
+ * available at http://www.broadcom.com/licenses/GPLv2.php, with the
+ * following added to such license:
+ * 
+ *      As a special exception, the copyright holders of this software give you
+ * permission to link this software with independent modules, and to copy and
+ * distribute the resulting executable under terms of your choice, provided that
+ * you also meet, for each linked independent module, the terms and conditions of
+ * the license of that module.  An independent module is a module which is not
+ * derived from this software.  The special exception does not apply to any
+ * modifications of the software.
+ * 
+ *      Notwithstanding the above, under no circumstances may you combine this
+ * software in any way with any other Broadcom software provided under a license
+ * other than the GPL, without Broadcom's express prior written consent.
+ *
+ * $Id: siutils.c,v 1.662.4.4.4.16.4.28 2010/06/23 21:37:54 Exp $
+ */
+
+#include <typedefs.h>
+#include <bcmdefs.h>
+#include <osl.h>
+#include <bcmutils.h>
+#include <siutils.h>
+#include <bcmdevs.h>
+#include <hndsoc.h>
+#include <sbchipc.h>
+#include <pcicfg.h>
+#include <sbpcmcia.h>
+#include <sbsocram.h>
+#include <bcmsdh.h>
+#include <sdio.h>
+#include <sbsdio.h>
+#include <sbhnddma.h>
+#include <sbsdpcmdev.h>
+#include <bcmsdpcm.h>
+#include <hndpmu.h>
+
+#include "siutils_priv.h"
+
+/* local prototypes */
+static si_info_t *si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+                              uint bustype, void *sdh, char **vars, uint *varsz);
+static bool si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh);
+static bool si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+	uint *origidx, void *regs);
+
+
+/* global variable to indicate reservation/release of gpio's */
+static uint32 si_gpioreservation = 0;
+static void *common_info_alloced = NULL;
+
+/* global flag to prevent shared resources from being initialized multiple times in si_attach() */
+
+/*
+ * 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/pcmcia/sb/sdio/etc
+ * vars - pointer to a pointer area for "environment" variables
+ * varsz - pointer to int to return the size of the vars
+ */
+si_t *
+si_attach(uint devid, osl_t *osh, void *regs,
+                       uint bustype, void *sdh, char **vars, uint *varsz)
+{
+	si_info_t *sii;
+
+	/* alloc si_info_t */
+	if ((sii = MALLOC(osh, sizeof (si_info_t))) == NULL) {
+		SI_ERROR(("si_attach: malloc failed! malloced %d bytes\n", MALLOCED(osh)));
+		return (NULL);
+	}
+
+	if (si_doattach(sii, devid, osh, regs, bustype, sdh, vars, varsz) == NULL) {
+		if (NULL != sii->common_info)
+			MFREE(osh, sii->common_info, sizeof(si_common_info_t));
+		MFREE(osh, sii, sizeof(si_info_t));
+		return (NULL);
+	}
+	sii->vars = vars ? *vars : NULL;
+	sii->varsz = varsz ? *varsz : 0;
+
+	return (si_t *)sii;
+}
+
+/* global kernel resource */
+static si_info_t ksii;
+
+static uint32	wd_msticks;		/* watchdog timer ticks normalized to ms */
+
+/* generic kernel variant of si_attach() */
+si_t *
+si_kattach(osl_t *osh)
+{
+	static bool ksii_attached = FALSE;
+
+	if (!ksii_attached) {
+		void *regs = REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+
+		if (si_doattach(&ksii, BCM4710_DEVICE_ID, osh, regs,
+		                SI_BUS, NULL,
+		                osh != SI_OSH ? &ksii.vars : NULL,
+		                osh != SI_OSH ? &ksii.varsz : NULL) == NULL) {
+			if (NULL != ksii.common_info)
+				MFREE(osh, ksii.common_info, sizeof(si_common_info_t));
+			SI_ERROR(("si_kattach: si_doattach failed\n"));
+			REG_UNMAP(regs);
+			return NULL;
+		}
+		REG_UNMAP(regs);
+
+		/* save ticks normalized to ms for si_watchdog_ms() */
+		if (PMUCTL_ENAB(&ksii.pub)) {
+			/* based on 32KHz ILP clock */
+			wd_msticks = 32;
+		} else {
+			wd_msticks = ALP_CLOCK / 1000;
+		}
+
+		ksii_attached = TRUE;
+		SI_MSG(("si_kattach done. ccrev = %d, wd_msticks = %d\n",
+		        ksii.pub.ccrev, wd_msticks));
+	}
+
+	return &ksii.pub;
+}
+
+
+static bool
+si_buscore_prep(si_info_t *sii, uint bustype, uint devid, void *sdh)
+{
+	/* need to set memseg flag for CF card first before any sb registers access */
+	if (BUSTYPE(bustype) == PCMCIA_BUS)
+		sii->memseg = TRUE;
+
+
+	if (BUSTYPE(bustype) == SDIO_BUS) {
+		int err;
+		uint8 clkset;
+
+		/* Try forcing SDIO core to do ALPAvail request only */
+		clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err);
+		if (!err) {
+			uint8 clkval;
+
+			/* If register supported, wait for ALPAvail and then force ALP */
+			clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, NULL);
+			if ((clkval & ~SBSDIO_AVBITS) == clkset) {
+				SPINWAIT(((clkval = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
+					SBSDIO_FUNC1_CHIPCLKCSR, NULL)), !SBSDIO_ALPAV(clkval)),
+					PMU_MAX_TRANSITION_DLY);
+				if (!SBSDIO_ALPAV(clkval)) {
+					SI_ERROR(("timeout on ALPAV wait, clkval 0x%02x\n",
+						clkval));
+					return FALSE;
+				}
+				clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP;
+				bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
+					clkset, &err);
+				OSL_DELAY(65);
+			}
+		}
+
+		/* Also, disable the extra SDIO pull-ups */
+		bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL);
+	}
+
+
+	return TRUE;
+}
+
+static bool
+si_buscore_setup(si_info_t *sii, chipcregs_t *cc, uint bustype, uint32 savewin,
+	uint *origidx, void *regs)
+{
+	bool pci, pcie;
+	uint i;
+	uint pciidx, pcieidx, pcirev, pcierev;
+
+	cc = si_setcoreidx(&sii->pub, SI_CC_IDX);
+	ASSERT((uintptr)cc);
+
+	/* get chipcommon rev */
+	sii->pub.ccrev = (int)si_corerev(&sii->pub);
+
+	/* get chipcommon chipstatus */
+	if (sii->pub.ccrev >= 11)
+		sii->pub.chipst = R_REG(sii->osh, &cc->chipstatus);
+
+	/* get chipcommon capabilites */
+	sii->pub.cccaps = R_REG(sii->osh, &cc->capabilities);
+
+	/* get pmu rev and caps */
+	if (sii->pub.cccaps & CC_CAP_PMU) {
+		sii->pub.pmucaps = R_REG(sii->osh, &cc->pmucapabilities);
+		sii->pub.pmurev = sii->pub.pmucaps & PCAP_REV_MASK;
+	}
+
+	SI_MSG(("Chipc: rev %d, caps 0x%x, chipst 0x%x pmurev %d, pmucaps 0x%x\n",
+		sii->pub.ccrev, sii->pub.cccaps, sii->pub.chipst, sii->pub.pmurev,
+		sii->pub.pmucaps));
+
+	/* 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;
+
+		si_setcoreidx(&sii->pub, i);
+		cid = si_coreid(&sii->pub);
+		crev = si_corerev(&sii->pub);
+
+		/* Display cores found */
+		SI_MSG(("CORE[%d]: id 0x%x rev %d base 0x%x regs 0x%p\n",
+		        i, cid, crev, sii->common_info->coresba[i], sii->common_info->regs[i]));
+
+		if (BUSTYPE(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;
+			}
+		} else if ((BUSTYPE(bustype) == PCMCIA_BUS) &&
+		           (cid == PCMCIA_CORE_ID)) {
+			sii->pub.buscorerev = crev;
+			sii->pub.buscoretype = cid;
+			sii->pub.buscoreidx = i;
+		}
+		else if (((BUSTYPE(bustype) == SDIO_BUS) ||
+		          (BUSTYPE(bustype) == SPI_BUS)) &&
+		         ((cid == PCMCIA_CORE_ID) ||
+		          (cid == SDIOD_CORE_ID))) {
+			sii->pub.buscorerev = crev;
+			sii->pub.buscoretype = cid;
+			sii->pub.buscoreidx = i;
+		}
+
+		/* find the core idx before entering this func. */
+		if ((savewin && (savewin == sii->common_info->coresba[i])) ||
+		    (regs == sii->common_info->regs[i]))
+			*origidx = i;
+	}
+
+
+	SI_MSG(("Buscore id/type/rev %d/0x%x/%d\n", sii->pub.buscoreidx, sii->pub.buscoretype,
+		sii->pub.buscorerev));
+
+	if (BUSTYPE(sii->pub.bustype) == SI_BUS && (CHIPID(sii->pub.chip) == BCM4712_CHIP_ID) &&
+	    (sii->pub.chippkg != BCM4712LARGE_PKG_ID) && (sii->pub.chiprev <= 3))
+		OR_REG(sii->osh, &cc->slow_clk_ctl, SCC_SS_XTAL);
+
+
+	/* Make sure any on-chip ARM is off (in case strapping is wrong), or downloaded code was
+	 * already running.
+	 */
+	if ((BUSTYPE(bustype) == SDIO_BUS) || (BUSTYPE(bustype) == SPI_BUS)) {
+		if (si_setcore(&sii->pub, ARM7S_CORE_ID, 0) ||
+		    si_setcore(&sii->pub, ARMCM3_CORE_ID, 0))
+			si_core_disable(&sii->pub, 0);
+	}
+
+	/* return to the original core */
+	si_setcoreidx(&sii->pub, *origidx);
+
+	return TRUE;
+}
+
+
+
+static si_info_t *
+si_doattach(si_info_t *sii, uint devid, osl_t *osh, void *regs,
+                       uint bustype, void *sdh, char **vars, uint *varsz)
+{
+	struct si_pub *sih = &sii->pub;
+	uint32 w, savewin;
+	chipcregs_t *cc;
+	char *pvars = NULL;
+	uint origidx;
+
+	ASSERT(GOODREGS(regs));
+
+	bzero((uchar*)sii, sizeof(si_info_t));
+
+
+	{
+		if (NULL == (common_info_alloced = (void *)MALLOC(osh, sizeof(si_common_info_t)))) {
+			SI_ERROR(("si_doattach: malloc failed! malloced %dbytes\n", MALLOCED(osh)));
+			return (NULL);
+		}
+		bzero((uchar*)(common_info_alloced), sizeof(si_common_info_t));
+	}
+	sii->common_info = (si_common_info_t *)common_info_alloced;
+	sii->common_info->attach_count++;
+
+	savewin = 0;
+
+	sih->buscoreidx = BADIDX;
+
+	sii->curmap = regs;
+	sii->sdh = sdh;
+	sii->osh = osh;
+
+
+	/* find Chipcommon address */
+	if (bustype == PCI_BUS) {
+		savewin = OSL_PCI_READ_CONFIG(sii->osh, PCI_BAR0_WIN, sizeof(uint32));
+		if (!GOODCOREADDR(savewin, SI_ENUM_BASE))
+			savewin = SI_ENUM_BASE;
+		OSL_PCI_WRITE_CONFIG(sii->osh, PCI_BAR0_WIN, 4, SI_ENUM_BASE);
+		cc = (chipcregs_t *)regs;
+	} else
+	if ((bustype == SDIO_BUS) || (bustype == SPI_BUS)) {
+		cc = (chipcregs_t *)sii->curmap;
+	} else {
+		cc = (chipcregs_t *)REG_MAP(SI_ENUM_BASE, SI_CORE_SIZE);
+	}
+
+	sih->bustype = bustype;
+	if (bustype != BUSTYPE(bustype)) {
+		SI_ERROR(("si_doattach: bus type %d does not match configured bus type %d\n",
+			bustype, BUSTYPE(bustype)));
+		return NULL;
+	}
+
+	/* bus/core/clk setup for register access */
+	if (!si_buscore_prep(sii, bustype, devid, sdh)) {
+		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(osh, &cc->chipid);
+	sih->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;
+	if ((CHIPID(sih->chip) == BCM4329_CHIP_ID) && (sih->chippkg != BCM4329_289PIN_PKG_ID))
+		sih->chippkg = BCM4329_182PIN_PKG_ID;
+	sih->issim = IS_SIM(sih->chippkg);
+
+	/* scan for cores */
+	if (CHIPTYPE(sii->pub.socitype) == SOCI_SB) {
+		SI_MSG(("Found chip type SB (0x%08x)\n", w));
+		sb_scan(&sii->pub, regs, devid);
+	} else if (CHIPTYPE(sii->pub.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, devid);
+	} else {
+		SI_ERROR(("Found chip of unkown 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 (!si_buscore_setup(sii, cc, bustype, savewin, &origidx, regs)) {
+		SI_ERROR(("si_doattach: si_buscore_setup failed\n"));
+		return NULL;
+	}
+
+	pvars = NULL;
+
+
+
+		if (sii->pub.ccrev >= 20) {
+			cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+			W_REG(osh, &cc->gpiopullup, 0);
+			W_REG(osh, &cc->gpiopulldown, 0);
+			si_setcoreidx(sih, origidx);
+		}
+
+		/* Skip PMU initialization from the Dongle Host.
+		 * Firmware will take care of it when it comes up.
+		 */
+
+
+
+	return (sii);
+}
+
+/* may be called with core in reset */
+void
+si_detach(si_t *sih)
+{
+	si_info_t *sii;
+	uint idx;
+
+	sii = SI_INFO(sih);
+
+	if (sii == NULL)
+		return;
+
+	if (BUSTYPE(sih->bustype) == SI_BUS)
+		for (idx = 0; idx < SI_MAXCORES; idx++)
+			if (sii->common_info->regs[idx]) {
+				REG_UNMAP(sii->common_info->regs[idx]);
+				sii->common_info->regs[idx] = NULL;
+			}
+
+
+	if (1 == sii->common_info->attach_count--) {
+		MFREE(sii->osh, sii->common_info, sizeof(si_common_info_t));
+		common_info_alloced = NULL;
+	}
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+	if (sii != &ksii)
+#endif	/* !BCMBUSTYPE || (BCMBUSTYPE == SI_BUS) */
+		MFREE(sii->osh, sii, sizeof(si_info_t));
+}
+
+void *
+si_osh(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->osh;
+}
+
+void
+si_setosh(si_t *sih, osl_t *osh)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	if (sii->osh != NULL) {
+		SI_ERROR(("osh is already set....\n"));
+		ASSERT(!sii->osh);
+	}
+	sii->osh = osh;
+}
+
+/* register driver interrupt disabling and restoring callback functions */
+void
+si_register_intr_callback(si_t *sih, void *intrsoff_fn, void *intrsrestore_fn,
+                          void *intrsenabled_fn, void *intr_arg)
+{
+	si_info_t *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->common_info->coreid[sii->curidx];
+}
+
+void
+si_deregister_intr_callback(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	sii->intrsoff_fn = NULL;
+}
+
+uint
+si_intflag(si_t *sih)
+{
+	si_info_t *sii = SI_INFO(sih);
+	if (CHIPTYPE(sih->socitype) == SOCI_SB) {
+		sbconfig_t *ccsbr = (sbconfig_t *)((uintptr)((ulong)
+			    (sii->common_info->coresba[SI_CC_IDX]) + SBCONFIGOFF));
+		return R_REG(sii->osh, &ccsbr->sbflagst);
+	} else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return R_REG(sii->osh, ((uint32 *)(uintptr)
+			    (sii->common_info->oob_router + OOB_STATUSA)));
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint
+si_flag(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_flag(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_flag(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_setint(si_t *sih, int siflag)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_setint(sih, siflag);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_setint(sih, siflag);
+	else
+		ASSERT(0);
+}
+
+uint
+si_coreid(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->common_info->coreid[sii->curidx];
+}
+
+uint
+si_coreidx(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	return sii->curidx;
+}
+
+/* return the core-type instantiation # of the current core */
+uint
+si_coreunit(si_t *sih)
+{
+	si_info_t *sii;
+	uint idx;
+	uint coreid;
+	uint coreunit;
+	uint i;
+
+	sii = SI_INFO(sih);
+	coreunit = 0;
+
+	idx = sii->curidx;
+
+	ASSERT(GOODREGS(sii->curmap));
+	coreid = si_coreid(sih);
+
+	/* count the cores of our type */
+	for (i = 0; i < idx; i++)
+		if (sii->common_info->coreid[i] == coreid)
+			coreunit++;
+
+	return (coreunit);
+}
+
+uint
+si_corevendor(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corevendor(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corevendor(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+bool
+si_backplane64(si_t *sih)
+{
+	return ((sih->cccaps & CC_CAP_BKPLN64) != 0);
+}
+
+uint
+si_corerev(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corerev(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corerev(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+/* return index of coreid or BADIDX if not found */
+uint
+si_findcoreidx(si_t *sih, uint coreid, uint coreunit)
+{
+	si_info_t *sii;
+	uint found;
+	uint i;
+
+	sii = SI_INFO(sih);
+
+	found = 0;
+
+	for (i = 0; i < sii->numcores; i++)
+		if (sii->common_info->coreid[i] == coreid) {
+			if (found == coreunit)
+				return (i);
+			found++;
+		}
+
+	return (BADIDX);
+}
+
+/* return list of found cores */
+uint
+si_corelist(si_t *sih, uint coreid[])
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	bcopy((uchar*)sii->common_info->coreid, (uchar*)coreid, (sii->numcores * sizeof(uint)));
+	return (sii->numcores);
+}
+
+/* return current register mapping */
+void *
+si_coreregs(si_t *sih)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	ASSERT(GOODREGS(sii->curmap));
+
+	return (sii->curmap);
+}
+
+/*
+ * 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 *
+si_setcore(si_t *sih, uint coreid, uint coreunit)
+{
+	uint idx;
+
+	idx = si_findcoreidx(sih, coreid, coreunit);
+	if (!GOODIDX(idx))
+		return (NULL);
+
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_setcoreidx(sih, idx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_setcoreidx(sih, idx);
+	else {
+		ASSERT(0);
+		return NULL;
+	}
+}
+
+void *
+si_setcoreidx(si_t *sih, uint coreidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_setcoreidx(sih, coreidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_setcoreidx(sih, coreidx);
+	else {
+		ASSERT(0);
+		return NULL;
+	}
+}
+
+/* Turn off interrupt as required by sb_setcore, before switch core */
+void *si_switch_core(si_t *sih, uint coreid, uint *origidx, uint *intr_val)
+{
+	void *cc;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	INTR_OFF(sii, *intr_val);
+	*origidx = sii->curidx;
+	cc = si_setcore(sih, coreid, 0);
+	ASSERT(cc != NULL);
+
+	return cc;
+}
+
+/* restore coreidx and restore interrupt */
+void si_restore_core(si_t *sih, uint coreid, uint intr_val)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	si_setcoreidx(sih, coreid);
+	INTR_RESTORE(sii, intr_val);
+}
+
+int
+si_numaddrspaces(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_numaddrspaces(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_numaddrspaces(sih);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_addrspace(si_t *sih, uint asidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_addrspace(sih, asidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_addrspace(sih, asidx);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_addrspacesize(si_t *sih, uint asidx)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_addrspacesize(sih, asidx);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_addrspacesize(sih, asidx);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+uint32
+si_core_cflags(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_core_cflags(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_core_cflags(sih, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_core_cflags_wo(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_cflags_wo(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_cflags_wo(sih, mask, val);
+	else
+		ASSERT(0);
+}
+
+uint32
+si_core_sflags(si_t *sih, uint32 mask, uint32 val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_core_sflags(sih, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_core_sflags(sih, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+bool
+si_iscoreup(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_iscoreup(sih);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_iscoreup(sih);
+	else {
+		ASSERT(0);
+		return FALSE;
+	}
+}
+
+void
+si_write_wrapperreg(si_t *sih, uint32 offset, uint32 val)
+{
+	/* only for 4319, no requirement for SOCI_SB */
+	if (CHIPTYPE(sih->socitype) == SOCI_AI) {
+		ai_write_wrap_reg(sih, offset, val);
+	}
+	else
+		return;
+
+	return;
+}
+
+uint
+si_corereg(si_t *sih, uint coreidx, uint regoff, uint mask, uint val)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		return sb_corereg(sih, coreidx, regoff, mask, val);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		return ai_corereg(sih, coreidx, regoff, mask, val);
+	else {
+		ASSERT(0);
+		return 0;
+	}
+}
+
+void
+si_core_disable(si_t *sih, uint32 bits)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_disable(sih, bits);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_disable(sih, bits);
+}
+
+void
+si_core_reset(si_t *sih, uint32 bits, uint32 resetbits)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_reset(sih, bits, resetbits);
+	else if (CHIPTYPE(sih->socitype) == SOCI_AI)
+		ai_core_reset(sih, bits, resetbits);
+}
+
+void
+si_core_tofixup(si_t *sih)
+{
+	if (CHIPTYPE(sih->socitype) == SOCI_SB)
+		sb_core_tofixup(sih);
+}
+
+/* Run bist on current core. Caller needs to take care of core-specific bist hazards */
+int
+si_corebist(si_t *sih)
+{
+	uint32 cflags;
+	int result = 0;
+
+	/* Read core control flags */
+	cflags = si_core_cflags(sih, 0, 0);
+
+	/* Set bist & fgc */
+	si_core_cflags(sih, 0, (SICF_BIST_EN | SICF_FGC));
+
+	/* Wait for bist done */
+	SPINWAIT(((si_core_sflags(sih, 0, 0) & SISF_BIST_DONE) == 0), 100000);
+
+	if (si_core_sflags(sih, 0, 0) & SISF_BIST_ERROR)
+		result = BCME_ERROR;
+
+	/* Reset core control flags */
+	si_core_cflags(sih, 0xffff, cflags);
+
+	return result;
+}
+
+static uint32
+factor6(uint32 x)
+{
+	switch (x) {
+	case CC_F6_2:	return 2;
+	case CC_F6_3:	return 3;
+	case CC_F6_4:	return 4;
+	case CC_F6_5:	return 5;
+	case CC_F6_6:	return 6;
+	case CC_F6_7:	return 7;
+	default:	return 0;
+	}
+}
+
+/* calculate the speed the SI would run at given a set of clockcontrol values */
+uint32
+si_clock_rate(uint32 pll_type, uint32 n, uint32 m)
+{
+	uint32 n1, n2, clock, m1, m2, m3, mc;
+
+	n1 = n & CN_N1_MASK;
+	n2 = (n & CN_N2_MASK) >> CN_N2_SHIFT;
+
+	if (pll_type == PLL_TYPE6) {
+		if (m & CC_T6_MMASK)
+			return CC_T6_M1;
+		else
+			return CC_T6_M0;
+	} else if ((pll_type == PLL_TYPE1) ||
+	           (pll_type == PLL_TYPE3) ||
+	           (pll_type == PLL_TYPE4) ||
+	           (pll_type == PLL_TYPE7)) {
+		n1 = factor6(n1);
+		n2 += CC_F5_BIAS;
+	} else if (pll_type == PLL_TYPE2) {
+		n1 += CC_T2_BIAS;
+		n2 += CC_T2_BIAS;
+		ASSERT((n1 >= 2) && (n1 <= 7));
+		ASSERT((n2 >= 5) && (n2 <= 23));
+	} else if (pll_type == PLL_TYPE5) {
+		return (100000000);
+	} else
+		ASSERT(0);
+	/* PLL types 3 and 7 use BASE2 (25Mhz) */
+	if ((pll_type == PLL_TYPE3) ||
+	    (pll_type == PLL_TYPE7)) {
+		clock = CC_CLOCK_BASE2 * n1 * n2;
+	} else
+		clock = CC_CLOCK_BASE1 * n1 * n2;
+
+	if (clock == 0)
+		return 0;
+
+	m1 = m & CC_M1_MASK;
+	m2 = (m & CC_M2_MASK) >> CC_M2_SHIFT;
+	m3 = (m & CC_M3_MASK) >> CC_M3_SHIFT;
+	mc = (m & CC_MC_MASK) >> CC_MC_SHIFT;
+
+	if ((pll_type == PLL_TYPE1) ||
+	    (pll_type == PLL_TYPE3) ||
+	    (pll_type == PLL_TYPE4) ||
+	    (pll_type == PLL_TYPE7)) {
+		m1 = factor6(m1);
+		if ((pll_type == PLL_TYPE1) || (pll_type == PLL_TYPE3))
+			m2 += CC_F5_BIAS;
+		else
+			m2 = factor6(m2);
+		m3 = factor6(m3);
+
+		switch (mc) {
+		case CC_MC_BYPASS:	return (clock);
+		case CC_MC_M1:		return (clock / m1);
+		case CC_MC_M1M2:	return (clock / (m1 * m2));
+		case CC_MC_M1M2M3:	return (clock / (m1 * m2 * m3));
+		case CC_MC_M1M3:	return (clock / (m1 * m3));
+		default:		return (0);
+		}
+	} else {
+		ASSERT(pll_type == PLL_TYPE2);
+
+		m1 += CC_T2_BIAS;
+		m2 += CC_T2M2_BIAS;
+		m3 += CC_T2_BIAS;
+		ASSERT((m1 >= 2) && (m1 <= 7));
+		ASSERT((m2 >= 3) && (m2 <= 10));
+		ASSERT((m3 >= 2) && (m3 <= 7));
+
+		if ((mc & CC_T2MC_M1BYP) == 0)
+			clock /= m1;
+		if ((mc & CC_T2MC_M2BYP) == 0)
+			clock /= m2;
+		if ((mc & CC_T2MC_M3BYP) == 0)
+			clock /= m3;
+
+		return (clock);
+	}
+}
+
+
+/* set chip watchdog reset timer to fire in 'ticks' */
+void
+si_watchdog(si_t *sih, uint ticks)
+{
+	if (PMUCTL_ENAB(sih)) {
+
+		if ((sih->chip == BCM4319_CHIP_ID) && (sih->chiprev == 0) && (ticks != 0)) {
+			si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, clk_ctl_st), ~0, 0x2);
+			si_setcore(sih, USB20D_CORE_ID, 0);
+			si_core_disable(sih, 1);
+			si_setcore(sih, CC_CORE_ID, 0);
+		}
+
+		if (ticks == 1)
+			ticks = 2;
+		si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, pmuwatchdog), ~0, ticks);
+	} else {
+		/* instant NMI */
+		si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, watchdog), ~0, ticks);
+	}
+}
+
+#if !defined(BCMBUSTYPE) || (BCMBUSTYPE == SI_BUS)
+/* trigger watchdog reset after ms milliseconds */
+void
+si_watchdog_ms(si_t *sih, uint32 ms)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	si_watchdog(sih, wd_msticks * ms);
+}
+#endif
+
+
+
+/* initialize the sdio core */
+void
+si_sdio_init(si_t *sih)
+{
+	si_info_t *sii = SI_INFO(sih);
+
+	if (((sih->buscoretype == PCMCIA_CORE_ID) && (sih->buscorerev >= 8)) ||
+	    (sih->buscoretype == SDIOD_CORE_ID)) {
+		uint idx;
+		sdpcmd_regs_t *sdpregs;
+
+		/* get the current core index */
+		idx = sii->curidx;
+		ASSERT(idx == si_findcoreidx(sih, D11_CORE_ID, 0));
+
+		/* switch to sdio core */
+		if (!(sdpregs = (sdpcmd_regs_t *)si_setcore(sih, PCMCIA_CORE_ID, 0)))
+			sdpregs = (sdpcmd_regs_t *)si_setcore(sih, SDIOD_CORE_ID, 0);
+		ASSERT(sdpregs);
+
+		SI_MSG(("si_sdio_init: For PCMCIA/SDIO Corerev %d, enable ints from core %d "
+		        "through SD core %d (%p)\n",
+		        sih->buscorerev, idx, sii->curidx, sdpregs));
+
+		/* enable backplane error and core interrupts */
+		W_REG(sii->osh, &sdpregs->hostintmask, I_SBINT);
+		W_REG(sii->osh, &sdpregs->sbintmask, (I_SB_SERR | I_SB_RESPERR | (1 << idx)));
+
+		/* switch back to previous core */
+		si_setcoreidx(sih, idx);
+	}
+
+	/* enable interrupts */
+	bcmsdh_intr_enable(sii->sdh);
+
+}
+
+
+/* change logical "focus" to the gpio core for optimized access */
+void *
+si_gpiosetcore(si_t *sih)
+{
+	return (si_setcoreidx(sih, SI_CC_IDX));
+}
+
+/* mask&set gpiocontrol bits */
+uint32
+si_gpiocontrol(si_t *sih, uint32 mask, uint32 val, uint8 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) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiocontrol);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output enable bits */
+uint32
+si_gpioouten(si_t *sih, uint32 mask, uint32 val, uint8 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) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpioouten);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio output bits */
+uint32
+si_gpioout(si_t *sih, uint32 mask, uint32 val, uint8 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) &&
+	    (BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpioout);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* reserve one gpio */
+uint32
+si_gpioreserve(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	/* only cores on SI_BUS share GPIO's and only applcation users need to
+	 * reserve/release GPIO
+	 */
+	if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+		ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+		return -1;
+	}
+	/* make sure only one bit is set */
+	if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+		ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+		return -1;
+	}
+
+	/* already reserved */
+	if (si_gpioreservation & gpio_bitmask)
+		return -1;
+	/* set reservation */
+	si_gpioreservation |= gpio_bitmask;
+
+	return si_gpioreservation;
+}
+
+/* release one gpio */
+/*
+ * releasing the gpio doesn't change the current value on the GPIO last write value
+ * persists till some one overwrites it
+ */
+
+uint32
+si_gpiorelease(si_t *sih, uint32 gpio_bitmask, uint8 priority)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	/* only cores on SI_BUS share GPIO's and only applcation users need to
+	 * reserve/release GPIO
+	 */
+	if ((BUSTYPE(sih->bustype) != SI_BUS) || (!priority)) {
+		ASSERT((BUSTYPE(sih->bustype) == SI_BUS) && (priority));
+		return -1;
+	}
+	/* make sure only one bit is set */
+	if ((!gpio_bitmask) || ((gpio_bitmask) & (gpio_bitmask - 1))) {
+		ASSERT((gpio_bitmask) && !((gpio_bitmask) & (gpio_bitmask - 1)));
+		return -1;
+	}
+
+	/* already released */
+	if (!(si_gpioreservation & gpio_bitmask))
+		return -1;
+
+	/* clear reservation */
+	si_gpioreservation &= ~gpio_bitmask;
+
+	return si_gpioreservation;
+}
+
+/* return the current gpioin register value */
+uint32
+si_gpioin(si_t *sih)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	regoff = OFFSETOF(chipcregs_t, gpioin);
+	return (si_corereg(sih, SI_CC_IDX, regoff, 0, 0));
+}
+
+/* mask&set gpio interrupt polarity bits */
+uint32
+si_gpiointpolarity(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	/* gpios could be shared on router platforms */
+	if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiointpolarity);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* mask&set gpio interrupt mask bits */
+uint32
+si_gpiointmask(si_t *sih, uint32 mask, uint32 val, uint8 priority)
+{
+	si_info_t *sii;
+	uint regoff;
+
+	sii = SI_INFO(sih);
+	regoff = 0;
+
+	/* gpios could be shared on router platforms */
+	if ((BUSTYPE(sih->bustype) == SI_BUS) && (val || mask)) {
+		mask = priority ? (si_gpioreservation & mask) :
+			((si_gpioreservation | mask) & ~(si_gpioreservation));
+		val &= mask;
+	}
+
+	regoff = OFFSETOF(chipcregs_t, gpiointmask);
+	return (si_corereg(sih, SI_CC_IDX, regoff, mask, val));
+}
+
+/* assign the gpio to an led */
+uint32
+si_gpioled(si_t *sih, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 16)
+		return -1;
+
+	/* gpio led powersave reg */
+	return (si_corereg(sih, SI_CC_IDX, OFFSETOF(chipcregs_t, gpiotimeroutmask), mask, val));
+}
+
+/* mask&set gpio timer val */
+uint32
+si_gpiotimerval(si_t *sih, uint32 mask, uint32 gpiotimerval)
+{
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	if (sih->ccrev < 16)
+		return -1;
+
+	return (si_corereg(sih, SI_CC_IDX,
+		OFFSETOF(chipcregs_t, gpiotimerval), mask, gpiotimerval));
+}
+
+uint32
+si_gpiopull(si_t *sih, bool updown, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 20)
+		return -1;
+
+	offs = (updown ? OFFSETOF(chipcregs_t, gpiopulldown) : OFFSETOF(chipcregs_t, gpiopullup));
+	return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+uint32
+si_gpioevent(si_t *sih, uint regtype, uint32 mask, uint32 val)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return -1;
+
+	if (regtype == GPIO_REGEVT)
+		offs = OFFSETOF(chipcregs_t, gpioevent);
+	else if (regtype == GPIO_REGEVT_INTMSK)
+		offs = OFFSETOF(chipcregs_t, gpioeventintmask);
+	else if (regtype == GPIO_REGEVT_INTPOL)
+		offs = OFFSETOF(chipcregs_t, gpioeventintpolarity);
+	else
+		return -1;
+
+	return (si_corereg(sih, SI_CC_IDX, offs, mask, val));
+}
+
+void *
+si_gpio_handler_register(si_t *sih, uint32 event,
+	bool level, gpio_handler_t cb, void *arg)
+{
+	si_info_t *sii;
+	gpioh_item_t *gi;
+
+	ASSERT(event);
+	ASSERT(cb != NULL);
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return NULL;
+
+	if ((gi = MALLOC(sii->osh, sizeof(gpioh_item_t))) == NULL)
+		return NULL;
+
+	bzero(gi, sizeof(gpioh_item_t));
+	gi->event = event;
+	gi->handler = cb;
+	gi->arg = arg;
+	gi->level = level;
+
+	gi->next = sii->gpioh_head;
+	sii->gpioh_head = gi;
+
+	return (void *)(gi);
+}
+
+void
+si_gpio_handler_unregister(si_t *sih, void *gpioh)
+{
+	si_info_t *sii;
+	gpioh_item_t *p, *n;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return;
+
+	ASSERT(sii->gpioh_head != NULL);
+	if ((void*)sii->gpioh_head == gpioh) {
+		sii->gpioh_head = sii->gpioh_head->next;
+		MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+		return;
+	} else {
+		p = sii->gpioh_head;
+		n = p->next;
+		while (n) {
+			if ((void*)n == gpioh) {
+				p->next = n->next;
+				MFREE(sii->osh, gpioh, sizeof(gpioh_item_t));
+				return;
+			}
+			p = n;
+			n = n->next;
+		}
+	}
+
+	ASSERT(0); /* Not found in list */
+}
+
+void
+si_gpio_handler_process(si_t *sih)
+{
+	si_info_t *sii;
+	gpioh_item_t *h;
+	uint32 status;
+	uint32 level = si_gpioin(sih);
+	uint32 edge = si_gpioevent(sih, GPIO_REGEVT, 0, 0);
+
+	sii = SI_INFO(sih);
+	for (h = sii->gpioh_head; h != NULL; h = h->next) {
+		if (h->handler) {
+			status = (h->level ? level : edge);
+
+			if (status & h->event)
+				h->handler(status, h->arg);
+		}
+	}
+
+	si_gpioevent(sih, GPIO_REGEVT, edge, edge); /* clear edge-trigger status */
+}
+
+uint32
+si_gpio_int_enable(si_t *sih, bool enable)
+{
+	si_info_t *sii;
+	uint offs;
+
+	sii = SI_INFO(sih);
+	if (sih->ccrev < 11)
+		return -1;
+
+	offs = OFFSETOF(chipcregs_t, intmask);
+	return (si_corereg(sih, SI_CC_IDX, offs, CI_GPIO, (enable ? CI_GPIO : 0)));
+}
+
+
+/* Return the RAM size of the SOCRAM core */
+uint32
+si_socram_size(si_t *sih)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+
+	sbsocramregs_t *regs;
+	bool wasup;
+	uint corerev;
+	uint32 coreinfo;
+	uint memsize = 0;
+
+	sii = SI_INFO(sih);
+
+	/* Block ints and save current core */
+	INTR_OFF(sii, intr_val);
+	origidx = si_coreidx(sih);
+
+	/* Switch to SOCRAM core */
+	if (!(regs = si_setcore(sih, SOCRAM_CORE_ID, 0)))
+		goto done;
+
+	/* Get info for determining size */
+	if (!(wasup = si_iscoreup(sih)))
+		si_core_reset(sih, 0, 0);
+	corerev = si_corerev(sih);
+	coreinfo = R_REG(sii->osh, &regs->coreinfo);
+
+	/* Calculate size from coreinfo based on rev */
+	if (corerev == 0)
+		memsize = 1 << (16 + (coreinfo & SRCI_MS0_MASK));
+	else if (corerev < 3) {
+		memsize = 1 << (SR_BSZ_BASE + (coreinfo & SRCI_SRBSZ_MASK));
+		memsize *= (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+	} else {
+		uint nb = (coreinfo & SRCI_SRNB_MASK) >> SRCI_SRNB_SHIFT;
+		uint bsz = (coreinfo & SRCI_SRBSZ_MASK);
+		uint lss = (coreinfo & SRCI_LSS_MASK) >> SRCI_LSS_SHIFT;
+		if (lss != 0)
+			nb --;
+		memsize = nb * (1 << (bsz + SR_BSZ_BASE));
+		if (lss != 0)
+			memsize += (1 << ((lss - 1) + SR_BSZ_BASE));
+	}
+
+	/* Return to previous state and core */
+	if (!wasup)
+		si_core_disable(sih, 0);
+	si_setcoreidx(sih, origidx);
+
+done:
+	INTR_RESTORE(sii, intr_val);
+
+	return memsize;
+}
+
+
+void
+si_btcgpiowar(si_t *sih)
+{
+	si_info_t *sii;
+	uint origidx;
+	uint intr_val = 0;
+	chipcregs_t *cc;
+
+	sii = SI_INFO(sih);
+
+	/* Make sure that there is ChipCommon core present &&
+	 * UART_TX is strapped to 1
+	 */
+	if (!(sih->cccaps & CC_CAP_UARTGPIO))
+		return;
+
+	/* si_corereg cannot be used as we have to guarantee 8-bit read/writes */
+	INTR_OFF(sii, intr_val);
+
+	origidx = si_coreidx(sih);
+
+	cc = (chipcregs_t *)si_setcore(sih, CC_CORE_ID, 0);
+	ASSERT(cc != NULL);
+
+	W_REG(sii->osh, &cc->uart0mcr, R_REG(sii->osh, &cc->uart0mcr) | 0x04);
+
+	/* restore the original index */
+	si_setcoreidx(sih, origidx);
+
+	INTR_RESTORE(sii, intr_val);
+}
+
+/* check if the device is removed */
+bool
+si_deviceremoved(si_t *sih)
+{
+	uint32 w;
+	si_info_t *sii;
+
+	sii = SI_INFO(sih);
+
+	switch (BUSTYPE(sih->bustype)) {
+	case PCI_BUS:
+		ASSERT(sii->osh != NULL);
+		w = OSL_PCI_READ_CONFIG(sii->osh, PCI_CFG_VID, sizeof(uint32));
+		if ((w & 0xFFFF) != VENDOR_BROADCOM)
+			return TRUE;
+		else
+			return FALSE;
+	default:
+		return FALSE;
+	}
+	return FALSE;
+}