Initial Contribution

msm-2.6.38: tag AU_LINUX_ANDROID_GINGERBREAD.02.03.04.00.142

Signed-off-by: Bryan Huntsman <bryanh@codeaurora.org>
diff --git a/drivers/net/wireless/Kconfig b/drivers/net/wireless/Kconfig
index f1d88c5..50dacf9 100644
--- a/drivers/net/wireless/Kconfig
+++ b/drivers/net/wireless/Kconfig
@@ -273,6 +273,26 @@
 	help
 	  Enables Power/Reset/Carddetect function abstraction
 
+config LIBRA_SDIOIF
+	tristate "Qualcomm libra wlan SDIO driver"
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	select WEXT_CORE
+	select WEXT_SPY
+	depends on MMC_MSM
+	---help---
+	  A driver for Qualcomm WLAN SDIO Libra chipset.
+
+config WCNSS_WLAN
+	tristate "Qualcomm WCNSS WLAN driver"
+	depends on ARCH_MSM8960
+	select WIRELESS_EXT
+	select WEXT_PRIV
+	select WEXT_CORE
+	select WEXT_SPY
+	---help---
+	  A driver for Qualcomm WCNSS WLAN feature
+
 source "drivers/net/wireless/ath/Kconfig"
 source "drivers/net/wireless/b43/Kconfig"
 source "drivers/net/wireless/b43legacy/Kconfig"
diff --git a/drivers/net/wireless/Makefile b/drivers/net/wireless/Makefile
index 8ceae0a..f725adf 100644
--- a/drivers/net/wireless/Makefile
+++ b/drivers/net/wireless/Makefile
@@ -61,3 +61,6 @@
 
 obj-$(CONFIG_BCM4329)	+= bcm4329/
 obj-$(CONFIG_BCMDHD)	+= bcmdhd/
+
+obj-$(CONFIG_LIBRA_SDIOIF)	+= libra/
+obj-$(CONFIG_WCNSS_WLAN)	+= wcnss/
diff --git a/drivers/net/wireless/ath/ath9k/hw.c b/drivers/net/wireless/ath/ath9k/hw.c
index 1be7c8b..82fb6ce 100644
--- a/drivers/net/wireless/ath/ath9k/hw.c
+++ b/drivers/net/wireless/ath/ath9k/hw.c
@@ -506,6 +506,17 @@
 	ah->WARegVal |= (AR_WA_D3_L1_DISABLE |
 			 AR_WA_ASPM_TIMER_BASED_DISABLE);
 
+	/*
+	 * Read back AR_WA into a permanent copy and set bits 14 and 17.
+	 * We need to do this to avoid RMW of this register. We cannot
+	 * read the reg when chip is asleep.
+	 */
+	ah->WARegVal = REG_READ(ah, AR_WA);
+	ah->WARegVal |= (AR_WA_D3_L1_DISABLE |
+			 AR_WA_ASPM_TIMER_BASED_DISABLE);
+
+	ath9k_hw_read_revisions(ah);
+
 	if (!ath9k_hw_set_reset_reg(ah, ATH9K_RESET_POWER_ON)) {
 		ath_err(common, "Couldn't reset chip\n");
 		return -EIO;
diff --git a/drivers/net/wireless/bcm4329/bcmspibrcm.c b/drivers/net/wireless/bcm4329/bcmspibrcm.c
new file mode 100644
index 0000000..0f131a4
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmspibrcm.c
@@ -0,0 +1,1726 @@
+/*
+ * Broadcom BCMSDH to gSPI Protocol Conversion Layer
+ *
+ * Copyright (C) 2010, Broadcom Corporation
+ * All Rights Reserved.
+ * 
+ * This is UNPUBLISHED PROPRIETARY SOURCE CODE of Broadcom Corporation;
+ * the contents of this file may not be disclosed to third parties, copied
+ * or duplicated in any form, in whole or in part, without the prior
+ * written permission of Broadcom Corporation.
+ *
+ * $Id: bcmspibrcm.c,v 1.11.2.10.2.9.6.11 2009/05/21 13:21:57 Exp $
+ */
+
+#define HSMODE
+
+#include <typedefs.h>
+
+#include <bcmdevs.h>
+#include <bcmendian.h>
+#include <bcmutils.h>
+#include <osl.h>
+#include <hndsoc.h>
+#include <siutils.h>
+#include <sbchipc.h>
+#include <sbsdio.h>
+#include <spid.h>
+
+#include <bcmsdbus.h>	/* bcmsdh to/from specific controller APIs */
+#include <sdiovar.h>	/* ioctl/iovars */
+#include <sdio.h>
+
+#include <pcicfg.h>
+
+
+#include <bcmspibrcm.h>
+#include <bcmspi.h>
+
+#define F0_RESPONSE_DELAY	16
+#define F1_RESPONSE_DELAY	16
+#define F2_RESPONSE_DELAY	F0_RESPONSE_DELAY
+
+#define CMDLEN		4
+
+#define DWORDMODE_ON (sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 2) && (sd->dwordmode == TRUE)
+
+/* Globals */
+uint sd_msglevel = 0;
+
+uint sd_hiok = FALSE;		/* Use hi-speed mode if available? */
+uint sd_sdmode = SDIOH_MODE_SPI;		/* Use SD4 mode by default */
+uint sd_f2_blocksize = 64;		/* Default blocksize */
+
+
+uint sd_divisor = 2;
+uint sd_power = 1;		/* Default to SD Slot powered ON */
+uint sd_clock = 1;		/* Default to SD Clock turned ON */
+uint sd_crc = 0;		/* Default to SPI CRC Check turned OFF */
+
+uint8	spi_outbuf[SPI_MAX_PKT_LEN];
+uint8	spi_inbuf[SPI_MAX_PKT_LEN];
+
+/* 128bytes buffer is enough to clear data-not-available and program response-delay F0 bits
+ * assuming we will not exceed F0 response delay > 100 bytes at 48MHz.
+ */
+#define BUF2_PKT_LEN	128
+uint8	spi_outbuf2[BUF2_PKT_LEN];
+uint8	spi_inbuf2[BUF2_PKT_LEN];
+
+/* Prototypes */
+static bool bcmspi_test_card(sdioh_info_t *sd);
+static bool bcmspi_host_device_init_adapt(sdioh_info_t *sd);
+static int bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode);
+static int bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+                           uint32 *data, uint32 datalen);
+static int bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr,
+                              int regsize, uint32 *data);
+static int bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               int regsize, uint32 data);
+static int bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr,
+                               uint8 *data);
+static int bcmspi_driver_init(sdioh_info_t *sd);
+static int bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                          uint32 addr, int nbytes, uint32 *data);
+static int bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize,
+                                 uint32 *data);
+static void bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer);
+static int bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg);
+
+/*
+ *  Public entry points & extern's
+ */
+extern sdioh_info_t *
+sdioh_attach(osl_t *osh, void *bar0, uint irq)
+{
+	sdioh_info_t *sd;
+
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((sd = (sdioh_info_t *)MALLOC(osh, sizeof(sdioh_info_t))) == NULL) {
+		sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
+		return NULL;
+	}
+	bzero((char *)sd, sizeof(sdioh_info_t));
+	sd->osh = osh;
+	if (spi_osinit(sd) != 0) {
+		sd_err(("%s: spi_osinit() failed\n", __FUNCTION__));
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return NULL;
+	}
+
+	sd->bar0 = bar0;
+	sd->irq = irq;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	sd->intr_handler_valid = FALSE;
+
+	/* Set defaults */
+	sd->use_client_ints = TRUE;
+	sd->sd_use_dma = FALSE;	/* DMA Not supported */
+
+	/* Spi device default is 16bit mode, change to 4 when device is changed to 32bit
+	 * mode
+	 */
+	sd->wordlen = 2;
+
+	if (!spi_hw_attach(sd)) {
+		sd_err(("%s: spi_hw_attach() failed\n", __FUNCTION__));
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	if (bcmspi_driver_init(sd) != SUCCESS) {
+		sd_err(("%s: bcmspi_driver_init() failed()\n", __FUNCTION__));
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	if (spi_register_irq(sd, irq) != SUCCESS) {
+		sd_err(("%s: spi_register_irq() failed for irq = %d\n", __FUNCTION__, irq));
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+		return (NULL);
+	}
+
+	sd_trace(("%s: Done\n", __FUNCTION__));
+
+	return sd;
+}
+
+extern SDIOH_API_RC
+sdioh_detach(osl_t *osh, sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if (sd) {
+		sd_err(("%s: detaching from hardware\n", __FUNCTION__));
+		spi_free_irq(sd->irq, sd);
+		spi_hw_detach(sd);
+		spi_osfree(sd);
+		MFREE(sd->osh, sd, sizeof(sdioh_info_t));
+	}
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* Configure callback to client when we recieve client interrupt */
+extern SDIOH_API_RC
+sdioh_interrupt_register(sdioh_info_t *sd, sdioh_cb_fn_t fn, void *argh)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler = fn;
+	sd->intr_handler_arg = argh;
+	sd->intr_handler_valid = TRUE;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_deregister(sdioh_info_t *sd)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	sd->intr_handler_valid = FALSE;
+	sd->intr_handler = NULL;
+	sd->intr_handler_arg = NULL;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_interrupt_query(sdioh_info_t *sd, bool *onoff)
+{
+	sd_trace(("%s: Entering\n", __FUNCTION__));
+	*onoff = sd->client_intr_enabled;
+	return SDIOH_API_RC_SUCCESS;
+}
+
+#if defined(DHD_DEBUG)
+extern bool
+sdioh_interrupt_pending(sdioh_info_t *sd)
+{
+	return 0;
+}
+#endif
+
+extern SDIOH_API_RC
+sdioh_query_device(sdioh_info_t *sd)
+{
+	/* Return a BRCM ID appropriate to the dongle class */
+	return (sd->num_funcs > 1) ? BCM4329_D11NDUAL_ID : BCM4318_D11G_ID;
+}
+
+/* Provide dstatus bits of spi-transaction for dhd layers. */
+extern uint32
+sdioh_get_dstatus(sdioh_info_t *sd)
+{
+	return sd->card_dstatus;
+}
+
+extern void
+sdioh_chipinfo(sdioh_info_t *sd, uint32 chip, uint32 chiprev)
+{
+	sd->chip = chip;
+	sd->chiprev = chiprev;
+}
+
+extern void
+sdioh_dwordmode(sdioh_info_t *sd, bool set)
+{
+	uint8 reg = 0;
+	int status;
+
+	if ((status = sdioh_request_byte(sd, SDIOH_READ, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+	     SUCCESS) {
+		sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+		return;
+	}
+
+	if (set) {
+		reg |= DWORD_PKT_LEN_EN;
+		sd->dwordmode = TRUE;
+		sd->client_block_size[SPI_FUNC_2] = 4096; /* h2spi's limit is 4KB, we support 8KB */
+	} else {
+		reg &= ~DWORD_PKT_LEN_EN;
+		sd->dwordmode = FALSE;
+		sd->client_block_size[SPI_FUNC_2] = 2048;
+	}
+
+	if ((status = sdioh_request_byte(sd, SDIOH_WRITE, SPI_FUNC_0, SPID_STATUS_ENABLE, &reg)) !=
+	     SUCCESS) {
+		sd_err(("%s: Failed to set dwordmode in gSPI\n", __FUNCTION__));
+		return;
+	}
+}
+
+
+uint
+sdioh_query_iofnum(sdioh_info_t *sd)
+{
+	return sd->num_funcs;
+}
+
+/* IOVar table */
+enum {
+	IOV_MSGLEVEL = 1,
+	IOV_BLOCKMODE,
+	IOV_BLOCKSIZE,
+	IOV_DMA,
+	IOV_USEINTS,
+	IOV_NUMINTS,
+	IOV_NUMLOCALINTS,
+	IOV_HOSTREG,
+	IOV_DEVREG,
+	IOV_DIVISOR,
+	IOV_SDMODE,
+	IOV_HISPEED,
+	IOV_HCIREGS,
+	IOV_POWER,
+	IOV_CLOCK,
+	IOV_SPIERRSTATS,
+	IOV_RESP_DELAY_ALL
+};
+
+const bcm_iovar_t sdioh_iovars[] = {
+	{"sd_msglevel",	IOV_MSGLEVEL, 	0,	IOVT_UINT32,	0 },
+	{"sd_blocksize", IOV_BLOCKSIZE, 0,	IOVT_UINT32,	0 }, /* ((fn << 16) | size) */
+	{"sd_dma",	IOV_DMA,	0,	IOVT_BOOL,	0 },
+	{"sd_ints",	IOV_USEINTS,	0,	IOVT_BOOL,	0 },
+	{"sd_numints",	IOV_NUMINTS,	0,	IOVT_UINT32,	0 },
+	{"sd_numlocalints", IOV_NUMLOCALINTS, 0, IOVT_UINT32,	0 },
+	{"sd_hostreg",	IOV_HOSTREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t) },
+	{"sd_devreg",	IOV_DEVREG,	0,	IOVT_BUFFER,	sizeof(sdreg_t)	},
+	{"sd_divisor",	IOV_DIVISOR,	0,	IOVT_UINT32,	0 },
+	{"sd_power",	IOV_POWER,	0,	IOVT_UINT32,	0 },
+	{"sd_clock",	IOV_CLOCK,	0,	IOVT_UINT32,	0 },
+	{"sd_mode",	IOV_SDMODE,	0,	IOVT_UINT32,	100},
+	{"sd_highspeed",	IOV_HISPEED,	0,	IOVT_UINT32,	0},
+	{"spi_errstats", IOV_SPIERRSTATS, 0, IOVT_BUFFER, sizeof(struct spierrstats_t) },
+	{"spi_respdelay",	IOV_RESP_DELAY_ALL,	0,	IOVT_BOOL,	0 },
+	{NULL, 0, 0, 0, 0 }
+};
+
+int
+sdioh_iovar_op(sdioh_info_t *si, const char *name,
+               void *params, int plen, void *arg, int len, bool set)
+{
+	const bcm_iovar_t *vi = NULL;
+	int bcmerror = 0;
+	int val_size;
+	int32 int_val = 0;
+	bool bool_val;
+	uint32 actionid;
+/*
+	sdioh_regs_t *regs;
+*/
+
+	ASSERT(name);
+	ASSERT(len >= 0);
+
+	/* Get must have return space; Set does not take qualifiers */
+	ASSERT(set || (arg && len));
+	ASSERT(!set || (!params && !plen));
+
+	sd_trace(("%s: Enter (%s %s)\n", __FUNCTION__, (set ? "set" : "get"), name));
+
+	if ((vi = bcm_iovar_lookup(sdioh_iovars, name)) == NULL) {
+		bcmerror = BCME_UNSUPPORTED;
+		goto exit;
+	}
+
+	if ((bcmerror = bcm_iovar_lencheck(vi, arg, len, set)) != 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))
+		bcopy(params, &int_val, 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 = (int32)sd_msglevel;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_MSGLEVEL):
+		sd_msglevel = int_val;
+		break;
+
+	case IOV_GVAL(IOV_BLOCKSIZE):
+		if ((uint32)int_val > si->num_funcs) {
+			bcmerror = BCME_BADARG;
+			break;
+		}
+		int_val = (int32)si->client_block_size[int_val];
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_DMA):
+		int_val = (int32)si->sd_use_dma;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DMA):
+		si->sd_use_dma = (bool)int_val;
+		break;
+
+	case IOV_GVAL(IOV_USEINTS):
+		int_val = (int32)si->use_client_ints;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_USEINTS):
+		break;
+
+	case IOV_GVAL(IOV_DIVISOR):
+		int_val = (uint32)sd_divisor;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_DIVISOR):
+		sd_divisor = int_val;
+		if (!spi_start_clock(si, (uint16)sd_divisor)) {
+			sd_err(("%s: set clock failed\n", __FUNCTION__));
+			bcmerror = BCME_ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_POWER):
+		int_val = (uint32)sd_power;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_POWER):
+		sd_power = int_val;
+		break;
+
+	case IOV_GVAL(IOV_CLOCK):
+		int_val = (uint32)sd_clock;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_CLOCK):
+		sd_clock = int_val;
+		break;
+
+	case IOV_GVAL(IOV_SDMODE):
+		int_val = (uint32)sd_sdmode;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_SDMODE):
+		sd_sdmode = int_val;
+		break;
+
+	case IOV_GVAL(IOV_HISPEED):
+		int_val = (uint32)sd_hiok;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_HISPEED):
+		sd_hiok = int_val;
+
+		if (!bcmspi_set_highspeed_mode(si, (bool)sd_hiok)) {
+			sd_err(("%s: Failed changing highspeed mode to %d.\n",
+			        __FUNCTION__, sd_hiok));
+			bcmerror = BCME_ERROR;
+			return ERROR;
+		}
+		break;
+
+	case IOV_GVAL(IOV_NUMINTS):
+		int_val = (int32)si->intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_GVAL(IOV_NUMLOCALINTS):
+		int_val = (int32)si->local_intrcount;
+		bcopy(&int_val, arg, val_size);
+		break;
+	case IOV_GVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data;
+
+		if (sdioh_cfg_read(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+
+		int_val = (int)data;
+		bcopy(&int_val, arg, sizeof(int_val));
+		break;
+	}
+
+	case IOV_SVAL(IOV_DEVREG):
+	{
+		sdreg_t *sd_ptr = (sdreg_t *)params;
+		uint8 data = (uint8)sd_ptr->value;
+
+		if (sdioh_cfg_write(si, sd_ptr->func, sd_ptr->offset, &data)) {
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+	}
+
+
+	case IOV_GVAL(IOV_SPIERRSTATS):
+	{
+		bcopy(&si->spierrstats, arg, sizeof(struct spierrstats_t));
+		break;
+	}
+
+	case IOV_SVAL(IOV_SPIERRSTATS):
+	{
+		bzero(&si->spierrstats, sizeof(struct spierrstats_t));
+		break;
+	}
+
+	case IOV_GVAL(IOV_RESP_DELAY_ALL):
+		int_val = (int32)si->resp_delay_all;
+		bcopy(&int_val, arg, val_size);
+		break;
+
+	case IOV_SVAL(IOV_RESP_DELAY_ALL):
+		si->resp_delay_all = (bool)int_val;
+		int_val = STATUS_ENABLE|INTR_WITH_STATUS;
+		if (si->resp_delay_all)
+			int_val |= RESP_DELAY_ALL;
+		else {
+			if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_RESPONSE_DELAY, 1,
+			     F1_RESPONSE_DELAY) != SUCCESS) {
+				sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+				bcmerror = BCME_SDIO_ERROR;
+				break;
+			}
+		}
+
+		if (bcmspi_card_regwrite(si, SPI_FUNC_0, SPID_STATUS_ENABLE, 1, int_val)
+		     != SUCCESS) {
+			sd_err(("%s: Unable to set response delay.\n", __FUNCTION__));
+			bcmerror = BCME_SDIO_ERROR;
+			break;
+		}
+		break;
+
+	default:
+		bcmerror = BCME_UNSUPPORTED;
+		break;
+	}
+exit:
+
+	return bcmerror;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_read(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	SDIOH_API_RC status;
+	/* No lock needed since sdioh_request_byte does locking */
+	status = sdioh_request_byte(sd, SDIOH_READ, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cfg_write(sdioh_info_t *sd, uint fnc_num, uint32 addr, uint8 *data)
+{
+	/* No lock needed since sdioh_request_byte does locking */
+	SDIOH_API_RC status;
+
+	if ((fnc_num == SPI_FUNC_1) && (addr == SBSDIO_FUNC1_FRAMECTRL)) {
+		uint8 dummy_data;
+		status = sdioh_cfg_read(sd, fnc_num, addr, &dummy_data);
+		if (status) {
+			sd_err(("sdioh_cfg_read() failed.\n"));
+			return status;
+		}
+	}
+
+	status = sdioh_request_byte(sd, SDIOH_WRITE, fnc_num, addr, data);
+	return status;
+}
+
+extern SDIOH_API_RC
+sdioh_cis_read(sdioh_info_t *sd, uint func, uint8 *cisd, uint32 length)
+{
+	uint32 count;
+	int offset;
+	uint32 cis_byte;
+	uint16 *cis = (uint16 *)cisd;
+	uint bar0 = SI_ENUM_BASE;
+	int status;
+	uint8 data;
+
+	sd_trace(("%s: Func %d\n", __FUNCTION__, func));
+
+	spi_lock(sd);
+
+	/* Set sb window address to 0x18000000 */
+	data = (bar0 >> 8) & SBSDIO_SBADDRLOW_MASK;
+	status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW, &data);
+	if (status == SUCCESS) {
+		data = (bar0 >> 16) & SBSDIO_SBADDRMID_MASK;
+		status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID, &data);
+	} else {
+		sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+		spi_unlock(sd);
+		return (BCME_ERROR);
+	}
+	if (status == SUCCESS) {
+		data = (bar0 >> 24) & SBSDIO_SBADDRHIGH_MASK;
+		status = bcmspi_card_bytewrite(sd, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH, &data);
+	} else {
+		sd_err(("%s: Unable to set sb-addr-windows\n", __FUNCTION__));
+		spi_unlock(sd);
+		return (BCME_ERROR);
+	}
+
+	offset =  CC_OTP; /* OTP offset in chipcommon. */
+	for (count = 0; count < length/2; count++) {
+		if (bcmspi_card_regread (sd, SDIO_FUNC_1, offset, 2, &cis_byte) < 0) {
+			sd_err(("%s: regread failed: Can't read CIS\n", __FUNCTION__));
+			spi_unlock(sd);
+			return (BCME_ERROR);
+		}
+
+		*cis = (uint16)cis_byte;
+		cis++;
+		offset += 2;
+	}
+
+	spi_unlock(sd);
+
+	return (BCME_OK);
+}
+
+extern SDIOH_API_RC
+sdioh_request_byte(sdioh_info_t *sd, uint rw, uint func, uint regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+	uint32 data = (uint32)(*byte);
+
+	spi_lock(sd);
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, rw == SDIOH_READ ? 0 : 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, rw, func,
+	         regaddr, data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+	                              cmd_arg, &data, 1)) != SUCCESS) {
+		spi_unlock(sd);
+		return status;
+	}
+
+	if (rw == SDIOH_READ)
+		*byte = (uint8)data;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+extern SDIOH_API_RC
+sdioh_request_word(sdioh_info_t *sd, uint cmd_type, uint rw, uint func, uint addr,
+                   uint32 *word, uint nbytes)
+{
+	int status;
+
+	spi_lock(sd);
+
+	if (rw == SDIOH_READ)
+		status = bcmspi_card_regread(sd, func, addr, nbytes, word);
+	else
+		status = bcmspi_card_regwrite(sd, func, addr, nbytes, *word);
+
+	spi_unlock(sd);
+	return (status == SUCCESS ?  SDIOH_API_RC_SUCCESS : SDIOH_API_RC_FAIL);
+}
+
+extern SDIOH_API_RC
+sdioh_request_buffer(sdioh_info_t *sd, uint pio_dma, uint fix_inc, uint rw, uint func,
+                     uint addr, uint reg_width, uint buflen_u, uint8 *buffer, void *pkt)
+{
+	int len;
+	int buflen = (int)buflen_u;
+	bool fifo = (fix_inc == SDIOH_DATA_FIX);
+
+	spi_lock(sd);
+
+	ASSERT(reg_width == 4);
+	ASSERT(buflen_u < (1 << 30));
+	ASSERT(sd->client_block_size[func]);
+
+	sd_data(("%s: %c len %d r_cnt %d t_cnt %d, pkt @0x%p\n",
+	         __FUNCTION__, rw == SDIOH_READ ? 'R' : 'W',
+	         buflen_u, sd->r_cnt, sd->t_cnt, pkt));
+
+	/* Break buffer down into blocksize chunks. */
+	while (buflen > 0) {
+		len = MIN(sd->client_block_size[func], buflen);
+		if (bcmspi_card_buf(sd, rw, func, fifo, addr, len, (uint32 *)buffer) != SUCCESS) {
+			sd_err(("%s: bcmspi_card_buf %s failed\n",
+				__FUNCTION__, rw == SDIOH_READ ? "Read" : "Write"));
+			spi_unlock(sd);
+			return SDIOH_API_RC_FAIL;
+		}
+		buffer += len;
+		buflen -= len;
+		if (!fifo)
+			addr += len;
+	}
+	spi_unlock(sd);
+	return SDIOH_API_RC_SUCCESS;
+}
+
+/* This function allows write to gspi bus when another rd/wr function is deep down the call stack.
+ * Its main aim is to have simpler spi writes rather than recursive writes.
+ * e.g. When there is a need to program response delay on the fly after detecting the SPI-func
+ * this call will allow to program the response delay.
+ */
+static int
+bcmspi_card_byterewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 byte)
+{
+	uint32 cmd_arg;
+	uint32 datalen = 1;
+	uint32 hostlen;
+
+	cmd_arg = 0;
+
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, datalen);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+	datalen = ROUNDUP(datalen, sd->wordlen);
+
+	/* Start by copying command in the spi-outbuffer */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		*(uint32 *)spi_outbuf2 = bcmswap32(cmd_arg);
+		if (datalen & 0x3)
+			datalen += (4 - (datalen & 0x3));
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		*(uint16 *)spi_outbuf2 = bcmswap16(cmd_arg & 0xffff);
+		*(uint16 *)&spi_outbuf2[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+		if (datalen & 0x1)
+			datalen++;
+	} else {
+		sd_err(("%s: Host is %d bit spid, could not create SPI command.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	/* for Write, put the data into the output buffer  */
+	if (datalen != 0) {
+			if (sd->wordlen == 4) { /* 32bit spid */
+				*(uint32 *)&spi_outbuf2[CMDLEN] = bcmswap32(byte);
+			} else if (sd->wordlen == 2) { /* 16bit spid */
+				*(uint16 *)&spi_outbuf2[CMDLEN] = bcmswap16(byte & 0xffff);
+				*(uint16 *)&spi_outbuf2[CMDLEN + 2] =
+					bcmswap16((byte & 0xffff0000) >> 16);
+			}
+	}
+
+	/* +4 for cmd, +4 for dstatus */
+	hostlen = datalen + 8;
+	hostlen += (4 - (hostlen & 0x3));
+	spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, hostlen);
+
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+	} else {
+		sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	if (sd->card_dstatus)
+		sd_trace(("dstatus after byte rewrite = 0x%x\n", sd->card_dstatus));
+
+	return (BCME_OK);
+}
+
+/* Program the response delay corresponding to the spi function */
+static int
+bcmspi_prog_resp_delay(sdioh_info_t *sd, int func, uint8 resp_delay)
+{
+	if (sd->resp_delay_all == FALSE)
+		return (BCME_OK);
+
+	if (sd->prev_fun == func)
+		return (BCME_OK);
+
+	if (F0_RESPONSE_DELAY == F1_RESPONSE_DELAY)
+		return (BCME_OK);
+
+	bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_RESPONSE_DELAY, resp_delay);
+
+	/* Remember function for which to avoid reprogramming resp-delay in next iteration */
+	sd->prev_fun = func;
+
+	return (BCME_OK);
+
+}
+
+#define GSPI_RESYNC_PATTERN	0x0
+
+/* A resync pattern is a 32bit MOSI line with all zeros. Its a special command in gSPI.
+ * It resets the spi-bkplane logic so that all F1 related ping-pong buffer logic is
+ * synchronised and all queued resuests are cancelled.
+ */
+static int
+bcmspi_resync_f1(sdioh_info_t *sd)
+{
+	uint32 cmd_arg = GSPI_RESYNC_PATTERN, data = 0, datalen = 0;
+
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	ASSERT(sd->wordlen == 4 || sd->wordlen == 2);
+	datalen = ROUNDUP(datalen, sd->wordlen);
+
+	/* Start by copying command in the spi-outbuffer */
+	*(uint32 *)spi_outbuf2 = cmd_arg;
+
+	/* for Write, put the data into the output buffer  */
+	*(uint32 *)&spi_outbuf2[CMDLEN] = data;
+
+	/* +4 for cmd, +4 for dstatus */
+	spi_sendrecv(sd, spi_outbuf2, spi_inbuf2, datalen + 8);
+
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf2[datalen + CMDLEN ]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN ]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf2[datalen + CMDLEN + 2]) << 16));
+	} else {
+		sd_err(("%s: Host is %d bit machine, could not read SPI dstatus.\n",
+		        __FUNCTION__, 8 * sd->wordlen));
+		return ERROR;
+	}
+
+	if (sd->card_dstatus)
+		sd_trace(("dstatus after resync pattern write = 0x%x\n", sd->card_dstatus));
+
+	return (BCME_OK);
+}
+
+uint32 dstatus_count = 0;
+
+static int
+bcmspi_update_stats(sdioh_info_t *sd, uint32 cmd_arg)
+{
+	uint32 dstatus = sd->card_dstatus;
+	struct spierrstats_t *spierrstats = &sd->spierrstats;
+	int err = SUCCESS;
+
+	sd_trace(("cmd = 0x%x, dstatus = 0x%x\n", cmd_arg, dstatus));
+
+	/* Store dstatus of last few gSPI transactions */
+	spierrstats->dstatus[dstatus_count % NUM_PREV_TRANSACTIONS] = dstatus;
+	spierrstats->spicmd[dstatus_count % NUM_PREV_TRANSACTIONS] = cmd_arg;
+	dstatus_count++;
+
+	if (sd->card_init_done == FALSE)
+		return err;
+
+	if (dstatus & STATUS_DATA_NOT_AVAILABLE) {
+		spierrstats->dna++;
+		sd_trace(("Read data not available on F1 addr = 0x%x\n",
+		        GFIELD(cmd_arg, SPI_REG_ADDR)));
+		/* Clear dna bit */
+		bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, DATA_UNAVAILABLE);
+	}
+
+	if (dstatus & STATUS_UNDERFLOW) {
+		spierrstats->rdunderflow++;
+		sd_err(("FIFO underflow happened due to current F2 read command.\n"));
+	}
+
+	if (dstatus & STATUS_OVERFLOW) {
+		spierrstats->wroverflow++;
+		sd_err(("FIFO overflow happened due to current (F1/F2) write command.\n"));
+		if ((sd->chip == BCM4329_CHIP_ID) && (sd->chiprev == 0)) {
+			bcmspi_card_byterewrite(sd, SPI_FUNC_0, SPID_INTR_REG, F1_OVERFLOW);
+			bcmspi_resync_f1(sd);
+			sd_err(("Recovering from F1 FIFO overflow.\n"));
+		} else {
+			err = ERROR_OF;
+		}
+	}
+
+	if (dstatus & STATUS_F2_INTR) {
+		spierrstats->f2interrupt++;
+		sd_trace(("Interrupt from F2.  SW should clear corresponding IntStatus bits\n"));
+	}
+
+	if (dstatus & STATUS_F3_INTR) {
+		spierrstats->f3interrupt++;
+		sd_err(("Interrupt from F3.  SW should clear corresponding IntStatus bits\n"));
+	}
+
+	if (dstatus & STATUS_HOST_CMD_DATA_ERR) {
+		spierrstats->hostcmddataerr++;
+		sd_err(("Error in CMD or Host data, detected by CRC/Checksum (optional)\n"));
+	}
+
+	if (dstatus & STATUS_F2_PKT_AVAILABLE) {
+		spierrstats->f2pktavailable++;
+		sd_trace(("Packet is available/ready in F2 TX FIFO\n"));
+		sd_trace(("Packet length = %d\n", sd->dwordmode ?
+		         ((dstatus & STATUS_F2_PKT_LEN_MASK) >> (STATUS_F2_PKT_LEN_SHIFT - 2)) :
+		         ((dstatus & STATUS_F2_PKT_LEN_MASK) >> STATUS_F2_PKT_LEN_SHIFT)));
+	}
+
+	if (dstatus & STATUS_F3_PKT_AVAILABLE) {
+		spierrstats->f3pktavailable++;
+		sd_err(("Packet is available/ready in F3 TX FIFO\n"));
+		sd_err(("Packet length = %d\n",
+		        (dstatus & STATUS_F3_PKT_LEN_MASK) >> STATUS_F3_PKT_LEN_SHIFT));
+	}
+
+	return err;
+}
+
+extern int
+sdioh_abort(sdioh_info_t *sd, uint func)
+{
+	return 0;
+}
+
+int
+sdioh_start(sdioh_info_t *sd, int stage)
+{
+	return SUCCESS;
+}
+
+int
+sdioh_stop(sdioh_info_t *sd)
+{
+	return SUCCESS;
+}
+
+
+
+/*
+ * Private/Static work routines
+ */
+static int
+bcmspi_host_init(sdioh_info_t *sd)
+{
+
+	/* Default power on mode */
+	sd->sd_mode = SDIOH_MODE_SPI;
+	sd->polled_mode = TRUE;
+	sd->host_init_done = TRUE;
+	sd->card_init_done = FALSE;
+	sd->adapter_slot = 1;
+
+	return (SUCCESS);
+}
+
+static int
+get_client_blocksize(sdioh_info_t *sd)
+{
+	uint32 regdata[2];
+	int status;
+
+	/* Find F1/F2/F3 max packet size */
+	if ((status = bcmspi_card_regread(sd, 0, SPID_F1_INFO_REG,
+	                                 8, regdata)) != SUCCESS) {
+		return status;
+	}
+
+	sd_trace(("pkt_size regdata[0] = 0x%x, regdata[1] = 0x%x\n",
+	        regdata[0], regdata[1]));
+
+	sd->client_block_size[1] = (regdata[0] & F1_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func1 blocksize = %d\n", sd->client_block_size[1]));
+	ASSERT(sd->client_block_size[1] == BLOCK_SIZE_F1);
+
+	sd->client_block_size[2] = ((regdata[0] >> 16) & F2_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func2 blocksize = %d\n", sd->client_block_size[2]));
+	ASSERT(sd->client_block_size[2] == BLOCK_SIZE_F2);
+
+	sd->client_block_size[3] = (regdata[1] & F3_MAX_PKT_SIZE) >> 2;
+	sd_trace(("Func3 blocksize = %d\n", sd->client_block_size[3]));
+	ASSERT(sd->client_block_size[3] == BLOCK_SIZE_F3);
+
+	return 0;
+}
+
+static int
+bcmspi_client_init(sdioh_info_t *sd)
+{
+	uint32	status_en_reg = 0;
+	sd_trace(("%s: Powering up slot %d\n", __FUNCTION__, sd->adapter_slot));
+
+#ifdef HSMODE
+	if (!spi_start_clock(sd, (uint16)sd_divisor)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#else
+	/* Start at ~400KHz clock rate for initialization */
+	if (!spi_start_clock(sd, 128)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#endif /* HSMODE */
+
+	if (!bcmspi_host_device_init_adapt(sd)) {
+		sd_err(("bcmspi_host_device_init_adapt failed\n"));
+		return ERROR;
+	}
+
+	if (!bcmspi_test_card(sd)) {
+		sd_err(("bcmspi_test_card failed\n"));
+		return ERROR;
+	}
+
+	sd->num_funcs = SPI_MAX_IOFUNCS;
+
+	get_client_blocksize(sd);
+
+	/* Apply resync pattern cmd with all zeros to reset spi-bkplane F1 logic */
+	bcmspi_resync_f1(sd);
+
+	sd->dwordmode = FALSE;
+
+	bcmspi_card_regread(sd, 0, SPID_STATUS_ENABLE, 1, &status_en_reg);
+
+	sd_trace(("%s: Enabling interrupt with dstatus \n", __FUNCTION__));
+	status_en_reg |= INTR_WITH_STATUS;
+
+
+	if (bcmspi_card_regwrite(sd, SPI_FUNC_0, SPID_STATUS_ENABLE, 1,
+	    status_en_reg & 0xff) != SUCCESS) {
+		sd_err(("%s: Unable to set response delay for all fun's.\n", __FUNCTION__));
+		return ERROR;
+	}
+
+
+#ifndef HSMODE
+	/* After configuring for High-Speed mode, set the desired clock rate. */
+	if (!spi_start_clock(sd, 4)) {
+		sd_err(("spi_start_clock failed\n"));
+		return ERROR;
+	}
+#endif /* HSMODE */
+
+	sd->card_init_done = TRUE;
+
+
+	return SUCCESS;
+}
+
+static int
+bcmspi_set_highspeed_mode(sdioh_info_t *sd, bool hsmode)
+{
+	uint32 regdata;
+	int status;
+
+	if ((status = bcmspi_card_regread(sd, 0, SPID_CONFIG,
+	                                 4, &regdata)) != SUCCESS)
+		return status;
+
+	sd_trace(("In %s spih-ctrl = 0x%x \n", __FUNCTION__, regdata));
+
+
+	if (hsmode == TRUE) {
+		sd_trace(("Attempting to enable High-Speed mode.\n"));
+
+		if (regdata & HIGH_SPEED_MODE) {
+			sd_trace(("Device is already in High-Speed mode.\n"));
+			return status;
+		} else {
+			regdata |= HIGH_SPEED_MODE;
+			sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+			if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+			                                  4, regdata)) != SUCCESS) {
+				return status;
+			}
+		}
+	} else {
+		sd_trace(("Attempting to disable High-Speed mode.\n"));
+
+		if (regdata & HIGH_SPEED_MODE) {
+			regdata &= ~HIGH_SPEED_MODE;
+			sd_trace(("Writing %08x to device at %08x\n", regdata, SPID_CONFIG));
+			if ((status = bcmspi_card_regwrite(sd, 0, SPID_CONFIG,
+			                                  4, regdata)) != SUCCESS)
+				return status;
+		}
+		 else {
+			sd_trace(("Device is already in Low-Speed mode.\n"));
+			return status;
+		}
+	}
+
+	spi_controller_highspeed_mode(sd, hsmode);
+
+	return TRUE;
+}
+
+#define bcmspi_find_curr_mode(sd) { \
+	sd->wordlen = 2; \
+	status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+	regdata &= 0xff; \
+	if ((regdata == 0xad) || (regdata == 0x5b) || \
+	    (regdata == 0x5d) || (regdata == 0x5a)) \
+		break; \
+	sd->wordlen = 4; \
+	status = bcmspi_card_regread_fixedaddr(sd, 0, SPID_TEST_READ, 4, &regdata); \
+	regdata &= 0xff; \
+	if ((regdata == 0xad) || (regdata == 0x5b) || \
+	    (regdata == 0x5d) || (regdata == 0x5a)) \
+		break; \
+	sd_trace(("Silicon testability issue: regdata = 0x%x." \
+	          " Expected 0xad, 0x5a, 0x5b or 0x5d.\n", regdata));	\
+	OSL_DELAY(100000); \
+}
+
+#define INIT_ADAPT_LOOP		100
+
+/* Adapt clock-phase-speed-bitwidth between host and device */
+static bool
+bcmspi_host_device_init_adapt(sdioh_info_t *sd)
+{
+	uint32 wrregdata, regdata = 0;
+	int status;
+	int i;
+
+	/* Due to a silicon testability issue, the first command from the Host
+	 * to the device will get corrupted (first bit will be lost). So the
+	 * Host should poll the device with a safe read request. ie: The Host
+	 * should try to read F0 addr 0x14 using the Fixed address mode
+	 * (This will prevent a unintended write command to be detected by device)
+	 */
+	for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+		/* If device was not power-cycled it will stay in 32bit mode with
+		 * response-delay-all bit set.  Alternate the iteration so that
+		 * read either with or without response-delay for F0 to succeed.
+		 */
+		bcmspi_find_curr_mode(sd);
+		sd->resp_delay_all = (i & 0x1) ? TRUE : FALSE;
+
+		bcmspi_find_curr_mode(sd);
+		sd->dwordmode = TRUE;
+
+		bcmspi_find_curr_mode(sd);
+		sd->dwordmode = FALSE;
+	}
+
+	/* Bail out, device not detected */
+	if (i == INIT_ADAPT_LOOP)
+		return FALSE;
+
+	/* Softreset the spid logic */
+	if ((sd->dwordmode) || (sd->wordlen == 4)) {
+		bcmspi_card_regwrite(sd, 0, SPID_RESET_BP, 1, RESET_ON_WLAN_BP_RESET|RESET_SPI);
+		bcmspi_card_regread(sd, 0, SPID_RESET_BP, 1, &regdata);
+		sd_trace(("reset reg read = 0x%x\n", regdata));
+		sd_trace(("dwordmode = %d, wordlen = %d, resp_delay_all = %d\n", sd->dwordmode,
+		       sd->wordlen, sd->resp_delay_all));
+		/* Restore default state after softreset */
+		sd->wordlen = 2;
+		sd->dwordmode = FALSE;
+	}
+
+	if (sd->wordlen == 4) {
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) !=
+		     SUCCESS)
+				return FALSE;
+		if (regdata == TEST_RO_DATA_32BIT_LE) {
+			sd_trace(("Spid is already in 32bit LE mode. Value read = 0x%x\n",
+			          regdata));
+			sd_trace(("Spid power was left on.\n"));
+		} else {
+			sd_err(("Spid power was left on but signature read failed."
+			        " Value read = 0x%x\n", regdata));
+			return FALSE;
+		}
+	} else {
+		sd->wordlen = 2;
+
+#define CTRL_REG_DEFAULT	0x00010430 /* according to the host m/c */
+
+		wrregdata = (CTRL_REG_DEFAULT);
+		sd->resp_delay_all = TRUE;
+		if (sd->resp_delay_all == TRUE) {
+			/* Enable response delay for all */
+			wrregdata |= (RESP_DELAY_ALL << 16);
+			/* Program response delay value */
+			wrregdata &= 0xffff00ff;
+			wrregdata |= (F1_RESPONSE_DELAY << 8);
+			sd->prev_fun = SPI_FUNC_1;
+			bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+		}
+
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+			return FALSE;
+		sd_trace(("(we are still in 16bit mode) 32bit READ LE regdata = 0x%x\n", regdata));
+
+#ifndef HSMODE
+		wrregdata |= (CLOCK_PHASE | CLOCK_POLARITY);
+		wrregdata &= ~HIGH_SPEED_MODE;
+		bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+#endif /* HSMODE */
+
+		for (i = 0; i < INIT_ADAPT_LOOP; i++) {
+			if ((regdata == 0xfdda7d5b) || (regdata == 0xfdda7d5a)) {
+				sd_trace(("0xfeedbead was leftshifted by 1-bit.\n"));
+				if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4,
+				     &regdata)) != SUCCESS)
+					return FALSE;
+			}
+			OSL_DELAY(1000);
+		}
+
+
+		/* Change to host controller intr-polarity of active-low */
+		wrregdata &= ~INTR_POLARITY;
+		sd_trace(("(we are still in 16bit mode) 32bit Write LE reg-ctrl-data = 0x%x\n",
+		        wrregdata));
+		/* Change to 32bit mode */
+		wrregdata |= WORD_LENGTH_32;
+		bcmspi_card_regwrite(sd, 0, SPID_CONFIG, 4, wrregdata);
+
+		/* Change command/data packaging in 32bit LE mode */
+		sd->wordlen = 4;
+
+		if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+			return FALSE;
+
+		if (regdata == TEST_RO_DATA_32BIT_LE) {
+			sd_trace(("Read spid passed. Value read = 0x%x\n", regdata));
+			sd_trace(("Spid had power-on cycle OR spi was soft-resetted \n"));
+		} else {
+			sd_err(("Stale spid reg values read as it was kept powered. Value read ="
+			  "0x%x\n", regdata));
+			return FALSE;
+		}
+	}
+
+
+	return TRUE;
+}
+
+static bool
+bcmspi_test_card(sdioh_info_t *sd)
+{
+	uint32 regdata;
+	int status;
+
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_READ, 4, &regdata)) != SUCCESS)
+		return FALSE;
+
+	if (regdata == (TEST_RO_DATA_32BIT_LE))
+		sd_trace(("32bit LE regdata = 0x%x\n", regdata));
+	else {
+		sd_trace(("Incorrect 32bit LE regdata = 0x%x\n", regdata));
+		return FALSE;
+	}
+
+
+#define RW_PATTERN1	0xA0A1A2A3
+#define RW_PATTERN2	0x4B5B6B7B
+
+	regdata = RW_PATTERN1;
+	if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+		return FALSE;
+	regdata = 0;
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+		return FALSE;
+	if (regdata != RW_PATTERN1) {
+		sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+			RW_PATTERN1, regdata));
+		return FALSE;
+	} else
+		sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+	regdata = RW_PATTERN2;
+	if ((status = bcmspi_card_regwrite(sd, 0, SPID_TEST_RW, 4, regdata)) != SUCCESS)
+		return FALSE;
+	regdata = 0;
+	if ((status = bcmspi_card_regread(sd, 0, SPID_TEST_RW, 4, &regdata)) != SUCCESS)
+		return FALSE;
+	if (regdata != RW_PATTERN2) {
+		sd_err(("Write-Read spid failed. Value wrote = 0x%x, Value read = 0x%x\n",
+			RW_PATTERN2, regdata));
+		return FALSE;
+	} else
+		sd_trace(("R/W spid passed. Value read = 0x%x\n", regdata));
+
+	return TRUE;
+}
+
+static int
+bcmspi_driver_init(sdioh_info_t *sd)
+{
+	sd_trace(("%s\n", __FUNCTION__));
+	if ((bcmspi_host_init(sd)) != SUCCESS) {
+		return ERROR;
+	}
+
+	if (bcmspi_client_init(sd) != SUCCESS) {
+		return ERROR;
+	}
+
+	return SUCCESS;
+}
+
+/* Read device reg */
+static int
+bcmspi_card_regread(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg, dstatus;
+
+	ASSERT(regsize);
+
+	if (func == 2)
+		sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+	         regaddr, *data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+	    != SUCCESS)
+		return status;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+static int
+bcmspi_card_regread_fixedaddr(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 *data)
+{
+
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+
+	ASSERT(regsize);
+
+	if (func == 2)
+		sd_trace(("Reg access on F2 will generate error indication in dstatus bits.\n"));
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 0);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0);	/* Fixed access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, data, regsize))
+	    != SUCCESS)
+		return status;
+
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 0, func,
+	         regaddr, *data));
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	sd_trace(("dstatus =0x%x\n", dstatus));
+	return SUCCESS;
+}
+
+/* write a device register */
+static int
+bcmspi_card_regwrite(sdioh_info_t *sd, int func, uint32 regaddr, int regsize, uint32 data)
+{
+	int status;
+	uint32 cmd_arg, dstatus;
+
+	ASSERT(regsize);
+
+	cmd_arg = 0;
+
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, regsize == BLOCK_SIZE_F2 ? 0 : regsize);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: rw=%d, func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, 1, func,
+	         regaddr, data));
+
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg, &data, regsize))
+	    != SUCCESS)
+		return status;
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+/* write a device register - 1 byte */
+static int
+bcmspi_card_bytewrite(sdioh_info_t *sd, int func, uint32 regaddr, uint8 *byte)
+{
+	int status;
+	uint32 cmd_arg;
+	uint32 dstatus;
+	uint32 data = (uint32)(*byte);
+
+	cmd_arg = 0;
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);	/* Incremental access */
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, regaddr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, 1);
+	cmd_arg = SFIELD(cmd_arg, SPI_LEN, 1);
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_trace(("%s: func=%d, regaddr=0x%08x, data=0x%x\n", __FUNCTION__, func,
+	         regaddr, data));
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma,
+	                              cmd_arg, &data, 1)) != SUCCESS) {
+		return status;
+	}
+
+	bcmspi_cmd_getdstatus(sd, &dstatus);
+	if (dstatus)
+		sd_trace(("dstatus =0x%x\n", dstatus));
+
+	return SUCCESS;
+}
+
+void
+bcmspi_cmd_getdstatus(sdioh_info_t *sd, uint32 *dstatus_buffer)
+{
+	*dstatus_buffer = sd->card_dstatus;
+}
+
+/* 'data' is of type uint32 whereas other buffers are of type uint8 */
+static int
+bcmspi_cmd_issue(sdioh_info_t *sd, bool use_dma, uint32 cmd_arg,
+                uint32 *data, uint32 datalen)
+{
+	uint32	i, j;
+	uint8	resp_delay = 0;
+	int	err = SUCCESS;
+	uint32	hostlen;
+	uint32 spilen = 0;
+	uint32 dstatus_idx = 0;
+	uint16 templen, buslen, len, *ptr = NULL;
+
+	sd_trace(("spi cmd = 0x%x\n", cmd_arg));
+
+	if (DWORDMODE_ON) {
+		spilen = GFIELD(cmd_arg, SPI_LEN);
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_0) ||
+		    (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_1))
+			dstatus_idx = spilen * 3;
+
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+		    (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+			spilen = spilen << 2;
+			dstatus_idx = (spilen % 16) ? (16 - (spilen % 16)) : 0;
+			/* convert len to mod16 size */
+			spilen = ROUNDUP(spilen, 16);
+			cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+		}
+	}
+
+	/* Set up and issue the SPI command.  MSByte goes out on bus first.  Increase datalen
+	 * according to the wordlen mode(16/32bit) the device is in.
+	 */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		*(uint32 *)spi_outbuf = bcmswap32(cmd_arg);
+		if (datalen & 0x3)
+			datalen += (4 - (datalen & 0x3));
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		*(uint16 *)spi_outbuf = bcmswap16(cmd_arg & 0xffff);
+		*(uint16 *)&spi_outbuf[2] = bcmswap16((cmd_arg & 0xffff0000) >> 16);
+		if (datalen & 0x1)
+			datalen++;
+		if (datalen < 4)
+			datalen = ROUNDUP(datalen, 4);
+	} else {
+		sd_err(("Host is %d bit spid, could not create SPI command.\n",
+			8 * sd->wordlen));
+		return ERROR;
+	}
+
+	/* for Write, put the data into the output buffer */
+	if (GFIELD(cmd_arg, SPI_RW_FLAG) == 1) {
+		/* We send len field of hw-header always a mod16 size, both from host and dongle */
+		if (DWORDMODE_ON) {
+			if (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) {
+				ptr = (uint16 *)&data[0];
+				templen = *ptr;
+				/* ASSERT(*ptr == ~*(ptr + 1)); */
+				templen = ROUNDUP(templen, 16);
+				*ptr = templen;
+				sd_trace(("actual tx len = %d\n", (uint16)(~*(ptr+1))));
+			}
+		}
+
+		if (datalen != 0) {
+			for (i = 0; i < datalen/4; i++) {
+				if (sd->wordlen == 4) { /* 32bit spid */
+					*(uint32 *)&spi_outbuf[i * 4 + CMDLEN] =
+						bcmswap32(data[i]);
+				} else if (sd->wordlen == 2) { /* 16bit spid */
+					*(uint16 *)&spi_outbuf[i * 4 + CMDLEN] =
+						bcmswap16(data[i] & 0xffff);
+					*(uint16 *)&spi_outbuf[i * 4 + CMDLEN + 2] =
+						bcmswap16((data[i] & 0xffff0000) >> 16);
+				}
+			}
+		}
+	}
+
+	/* Append resp-delay number of bytes and clock them out for F0/1/2 reads. */
+	if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) {
+		int func = GFIELD(cmd_arg, SPI_FUNCTION);
+		switch (func) {
+			case 0:
+				resp_delay = sd->resp_delay_all ? F0_RESPONSE_DELAY : 0;
+				break;
+			case 1:
+				resp_delay = F1_RESPONSE_DELAY;
+				break;
+			case 2:
+				resp_delay = sd->resp_delay_all ? F2_RESPONSE_DELAY : 0;
+				break;
+			default:
+				ASSERT(0);
+				break;
+		}
+		/* Program response delay */
+	        bcmspi_prog_resp_delay(sd, func, resp_delay);
+	}
+
+	/* +4 for cmd and +4 for dstatus */
+	hostlen = datalen + 8 + resp_delay;
+	hostlen += dstatus_idx;
+	hostlen += (4 - (hostlen & 0x3));
+	spi_sendrecv(sd, spi_outbuf, spi_inbuf, hostlen);
+
+	/* for Read, get the data into the input buffer */
+	if (datalen != 0) {
+		if (GFIELD(cmd_arg, SPI_RW_FLAG) == 0) { /* if read cmd */
+			for (j = 0; j < datalen/4; j++) {
+				if (sd->wordlen == 4) { /* 32bit spid */
+					data[j] = bcmswap32(*(uint32 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay]);
+				} else if (sd->wordlen == 2) { /* 16bit spid */
+					data[j] = (bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay])) |
+					         ((bcmswap16(*(uint16 *)&spi_inbuf[j * 4 +
+					            CMDLEN + resp_delay + 2])) << 16);
+				}
+			}
+
+			if ((DWORDMODE_ON) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+				ptr = (uint16 *)&data[0];
+				templen = *ptr;
+				buslen = len = ~(*(ptr + 1));
+				buslen = ROUNDUP(buslen, 16);
+				/* populate actual len in hw-header */
+				if (templen == buslen)
+					*ptr = len;
+			}
+		}
+	}
+
+	/* Restore back the len field of the hw header */
+	if (DWORDMODE_ON) {
+		if ((GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2) &&
+		    (GFIELD(cmd_arg, SPI_RW_FLAG) == 1)) {
+			ptr = (uint16 *)&data[0];
+			*ptr = (uint16)(~*(ptr+1));
+		}
+	}
+
+	dstatus_idx += (datalen + CMDLEN + resp_delay);
+	/* Last 4bytes are dstatus.  Device is configured to return status bits. */
+	if (sd->wordlen == 4) { /* 32bit spid */
+		sd->card_dstatus = bcmswap32(*(uint32 *)&spi_inbuf[dstatus_idx]);
+	} else if (sd->wordlen == 2) { /* 16bit spid */
+		sd->card_dstatus = (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx]) |
+		                   (bcmswap16(*(uint16 *)&spi_inbuf[dstatus_idx + 2]) << 16));
+	} else {
+		sd_err(("Host is %d bit machine, could not read SPI dstatus.\n",
+			8 * sd->wordlen));
+		return ERROR;
+	}
+	if (sd->card_dstatus == 0xffffffff) {
+		sd_err(("looks like not a GSPI device or device is not powered.\n"));
+	}
+
+	err = bcmspi_update_stats(sd, cmd_arg);
+
+	return err;
+
+}
+
+static int
+bcmspi_card_buf(sdioh_info_t *sd, int rw, int func, bool fifo,
+                uint32 addr, int nbytes, uint32 *data)
+{
+	int status;
+	uint32 cmd_arg;
+	bool write = rw == SDIOH_READ ? 0 : 1;
+	uint retries = 0;
+
+	bool enable;
+	uint32	spilen;
+
+	cmd_arg = 0;
+
+	ASSERT(nbytes);
+	ASSERT(nbytes <= sd->client_block_size[func]);
+
+	if (write) sd->t_cnt++; else sd->r_cnt++;
+
+	if (func == 2) {
+		/* Frame len check limited by gSPI. */
+		if ((nbytes > 2000) && write) {
+			sd_trace((">2KB write: F2 wr of %d bytes\n", nbytes));
+		}
+		/* ASSERT(nbytes <= 2048); Fix bigger len gspi issue and uncomment. */
+		/* If F2 fifo on device is not ready to receive data, don't do F2 transfer */
+		if (write) {
+			uint32 dstatus;
+			/* check F2 ready with cached one */
+			bcmspi_cmd_getdstatus(sd, &dstatus);
+			if ((dstatus & STATUS_F2_RX_READY) == 0) {
+				retries = WAIT_F2RXFIFORDY;
+				enable = 0;
+				while (retries-- && !enable) {
+					OSL_DELAY(WAIT_F2RXFIFORDY_DELAY * 1000);
+					bcmspi_card_regread(sd, SPI_FUNC_0, SPID_STATUS_REG, 4,
+					                   &dstatus);
+					if (dstatus & STATUS_F2_RX_READY)
+						enable = TRUE;
+				}
+				if (!enable) {
+					struct spierrstats_t *spierrstats = &sd->spierrstats;
+					spierrstats->f2rxnotready++;
+					sd_err(("F2 FIFO is not ready to receive data.\n"));
+					return ERROR;
+				}
+				sd_trace(("No of retries on F2 ready %d\n",
+					(WAIT_F2RXFIFORDY - retries)));
+			}
+		}
+	}
+
+	/* F2 transfers happen on 0 addr */
+	addr = (func == 2) ? 0 : addr;
+
+	/* In pio mode buffer is read using fixed address fifo in func 1 */
+	if ((func == 1) && (fifo))
+		cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 0);
+	else
+		cmd_arg = SFIELD(cmd_arg, SPI_ACCESS, 1);
+
+	cmd_arg = SFIELD(cmd_arg, SPI_FUNCTION, func);
+	cmd_arg = SFIELD(cmd_arg, SPI_REG_ADDR, addr);
+	cmd_arg = SFIELD(cmd_arg, SPI_RW_FLAG, write);
+	spilen = sd->data_xfer_count = MIN(sd->client_block_size[func], nbytes);
+	if ((sd->dwordmode == TRUE) && (GFIELD(cmd_arg, SPI_FUNCTION) == SPI_FUNC_2)) {
+		/* convert len to mod4 size */
+		spilen = spilen + ((spilen & 0x3) ? (4 - (spilen & 0x3)): 0);
+		cmd_arg = SFIELD(cmd_arg, SPI_LEN, (spilen >> 2));
+	} else
+		cmd_arg = SFIELD(cmd_arg, SPI_LEN, spilen);
+
+	if ((func == 2) && (fifo == 1)) {
+		sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+		          __FUNCTION__, write ? "Wr" : "Rd", func, "INCR",
+		          addr, nbytes, sd->r_cnt, sd->t_cnt));
+	}
+
+	sd_trace(("%s cmd_arg = 0x%x\n", __FUNCTION__, cmd_arg));
+	sd_data(("%s: %s func %d, %s, addr 0x%x, len %d bytes, r_cnt %d t_cnt %d\n",
+	         __FUNCTION__, write ? "Wd" : "Rd", func, "INCR",
+	         addr, nbytes, sd->r_cnt, sd->t_cnt));
+
+
+	if ((status = bcmspi_cmd_issue(sd, sd->sd_use_dma, cmd_arg,
+	     data, nbytes)) != SUCCESS) {
+		sd_err(("%s: cmd_issue failed for %s\n", __FUNCTION__,
+			(write ? "write" : "read")));
+		return status;
+	}
+
+	/* gSPI expects that hw-header-len is equal to spi-command-len */
+	if ((func == 2) && (rw == SDIOH_WRITE) && (sd->dwordmode == FALSE)) {
+		ASSERT((uint16)sd->data_xfer_count == (uint16)(*data & 0xffff));
+		ASSERT((uint16)sd->data_xfer_count == (uint16)(~((*data & 0xffff0000) >> 16)));
+	}
+
+	if ((nbytes > 2000) && !write) {
+		sd_trace((">2KB read: F2 rd of %d bytes\n", nbytes));
+	}
+
+	return SUCCESS;
+}
+
+/* Reset and re-initialize the device */
+int
+sdioh_sdio_reset(sdioh_info_t *si)
+{
+	si->card_init_done = FALSE;
+	return bcmspi_client_init(si);
+}
diff --git a/drivers/net/wireless/libra/Makefile b/drivers/net/wireless/libra/Makefile
new file mode 100644
index 0000000..3c606ba
--- /dev/null
+++ b/drivers/net/wireless/libra/Makefile
@@ -0,0 +1,14 @@
+
+# Makefile for wlan sdio if driver
+
+librasdioif-objs += libra_sdioif.o
+
+ifdef CONFIG_ARCH_MSM8X60
+	librasdioif-objs += qcomwlan_pwrif.o
+endif
+
+ifdef CONFIG_ARCH_MSM7X27A
+	librasdioif-objs += qcomwlan7x27a_pwrif.o
+endif
+
+obj-$(CONFIG_LIBRA_SDIOIF) += librasdioif.o
diff --git a/drivers/net/wireless/libra/libra_sdioif.c b/drivers/net/wireless/libra/libra_sdioif.c
new file mode 100644
index 0000000..3955642
--- /dev/null
+++ b/drivers/net/wireless/libra/libra_sdioif.c
@@ -0,0 +1,481 @@
+/* Copyright (c) 2009-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/libra_sdioif.h>
+#include <linux/delay.h>
+#include <linux/mmc/sdio.h>
+#include <linux/mmc/mmc.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+
+/* Libra SDIO function device */
+static struct sdio_func *libra_sdio_func;
+static struct mmc_host *libra_mmc_host;
+static int libra_mmc_host_index;
+
+/* SDIO Card ID / Device ID */
+static unsigned short  libra_sdio_card_id;
+
+static suspend_handler_t *libra_suspend_hldr;
+static resume_handler_t *libra_resume_hldr;
+
+/**
+ * libra_sdio_configure() - Function to configure the SDIO device param
+ * @libra_sdio_rxhandler    Rx handler
+ * @func_drv_fn             Function driver function for special setup
+ * @funcdrv_timeout         Function Enable timeout
+ * @blksize                 Block size
+ *
+ * Configure SDIO device, enable function and set block size
+ */
+int libra_sdio_configure(sdio_irq_handler_t libra_sdio_rxhandler,
+	void  (*func_drv_fn)(int *status),
+	unsigned int funcdrv_timeout, unsigned int blksize)
+{
+	int err_ret = 0;
+	struct sdio_func *func = libra_sdio_func;
+
+	if (libra_sdio_func == NULL) {
+		printk(KERN_ERR "%s: Error SDIO card not detected\n", __func__);
+		goto cfg_error;
+	}
+
+	sdio_claim_host(func);
+
+	/* Currently block sizes are set here. */
+	func->max_blksize = blksize;
+	if (sdio_set_block_size(func, blksize)) {
+		printk(KERN_ERR "%s: Unable to set the block size.\n",
+				__func__);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	/* Function driver specific configuration. */
+	if (func_drv_fn) {
+		(*func_drv_fn)(&err_ret);
+		if (err_ret) {
+			printk(KERN_ERR "%s: function driver provided configure function error=%d\n",
+				__func__, err_ret);
+			sdio_release_host(func);
+			goto cfg_error;
+		}
+	}
+
+	/* We set this based on the function card. */
+	func->enable_timeout = funcdrv_timeout;
+	err_ret = sdio_enable_func(func);
+	if (err_ret != 0) {
+		printk(KERN_ERR "%s: Unable to enable function %d\n",
+				__func__, err_ret);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	if (sdio_claim_irq(func, libra_sdio_rxhandler)) {
+		sdio_disable_func(func);
+		printk(KERN_ERR "%s: Unable to claim irq.\n", __func__);
+		sdio_release_host(func);
+		goto cfg_error;
+	}
+
+	sdio_release_host(func);
+
+	return 0;
+
+cfg_error:
+	return -1;
+
+}
+EXPORT_SYMBOL(libra_sdio_configure);
+
+int libra_sdio_configure_suspend_resume(
+		suspend_handler_t *libra_sdio_suspend_hdlr,
+		resume_handler_t *libra_sdio_resume_hdlr)
+{
+	libra_suspend_hldr = libra_sdio_suspend_hdlr;
+	libra_resume_hldr = libra_sdio_resume_hdlr;
+	return 0;
+}
+EXPORT_SYMBOL(libra_sdio_configure_suspend_resume);
+
+/*
+ * libra_sdio_deconfigure() - Function to reset the SDIO device param
+ */
+void libra_sdio_deconfigure(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_claim_host(func);
+	sdio_release_irq(func);
+	sdio_disable_func(func);
+	sdio_release_host(func);
+}
+EXPORT_SYMBOL(libra_sdio_deconfigure);
+
+int libra_enable_sdio_irq(struct sdio_func *func, u8 enable)
+{
+	if (libra_mmc_host && libra_mmc_host->ops &&
+			libra_mmc_host->ops->enable_sdio_irq) {
+		libra_mmc_host->ops->enable_sdio_irq(libra_mmc_host, enable);
+		return 0;
+	}
+
+	printk(KERN_ERR "%s: Could not enable disable irq\n", __func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_enable_sdio_irq);
+
+int libra_disable_sdio_irq_capability(struct sdio_func *func, u8 disable)
+{
+	if (libra_mmc_host) {
+		if (disable)
+			libra_mmc_host->caps &= ~MMC_CAP_SDIO_IRQ;
+		else
+			libra_mmc_host->caps |= MMC_CAP_SDIO_IRQ;
+		return 0;
+	}
+	printk(KERN_ERR "%s: Could not change sdio capabilities to polling\n",
+			__func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_disable_sdio_irq_capability);
+
+/*
+ * libra_sdio_release_irq() - Function to release IRQ
+ */
+void libra_sdio_release_irq(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_release_irq(func);
+}
+EXPORT_SYMBOL(libra_sdio_release_irq);
+
+/*
+ * libra_sdio_disable_func() - Function to disable sdio func
+ */
+void libra_sdio_disable_func(struct sdio_func *func)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_disable_func(func);
+}
+EXPORT_SYMBOL(libra_sdio_disable_func);
+
+/*
+ * Return the SDIO Function device
+ */
+struct sdio_func *libra_getsdio_funcdev(void)
+{
+	return libra_sdio_func;
+}
+EXPORT_SYMBOL(libra_getsdio_funcdev);
+
+/*
+ * Set function driver as the private data for the function device
+ */
+void libra_sdio_setprivdata(struct sdio_func *sdio_func_dev,
+		void *padapter)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	sdio_set_drvdata(sdio_func_dev, padapter);
+}
+EXPORT_SYMBOL(libra_sdio_setprivdata);
+
+/*
+ * Return private data of the function device.
+ */
+void *libra_sdio_getprivdata(struct sdio_func *sdio_func_dev)
+{
+	return sdio_get_drvdata(sdio_func_dev);
+}
+EXPORT_SYMBOL(libra_sdio_getprivdata);
+
+/*
+ * Function driver claims the SDIO device
+ */
+void libra_claim_host(struct sdio_func *sdio_func_dev,
+		pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+	if (NULL == libra_sdio_func)
+		return;
+
+	if (*curr_claimed == current_pid) {
+		atomic_inc(claim_count);
+		return;
+	}
+
+	/* Go ahead and claim the host if not locked by anybody. */
+	sdio_claim_host(sdio_func_dev);
+
+	*curr_claimed = current_pid;
+	atomic_inc(claim_count);
+
+}
+EXPORT_SYMBOL(libra_claim_host);
+
+/*
+ * Function driver releases the SDIO device
+ */
+void libra_release_host(struct sdio_func *sdio_func_dev,
+		pid_t *curr_claimed, pid_t current_pid, atomic_t *claim_count)
+{
+
+	if (NULL == libra_sdio_func)
+		return;
+
+	if (*curr_claimed != current_pid) {
+		/* Dont release  */
+		return;
+	}
+
+	atomic_dec(claim_count);
+	if (atomic_read(claim_count) == 0) {
+		*curr_claimed = 0;
+		sdio_release_host(sdio_func_dev);
+	}
+}
+EXPORT_SYMBOL(libra_release_host);
+
+void libra_sdiocmd52(struct sdio_func *sdio_func_dev, unsigned int addr,
+	u8 *byte_var, int write, int *err_ret)
+{
+	if (write)
+		sdio_writeb(sdio_func_dev, byte_var[0], addr, err_ret);
+	else
+		byte_var[0] = sdio_readb(sdio_func_dev, addr, err_ret);
+}
+EXPORT_SYMBOL(libra_sdiocmd52);
+
+u8 libra_sdio_readsb(struct sdio_func *func, void *dst,
+	unsigned int addr, int count)
+{
+	return sdio_readsb(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_readsb);
+
+int libra_sdio_memcpy_fromio(struct sdio_func *func,
+		void *dst, unsigned int addr, int count)
+{
+	return sdio_memcpy_fromio(func, dst, addr, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_fromio);
+
+int libra_sdio_writesb(struct sdio_func *func,
+		unsigned int addr, void *src, int count)
+{
+	return sdio_writesb(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_writesb);
+
+int libra_sdio_memcpy_toio(struct sdio_func *func,
+	unsigned int addr, void *src, int count)
+{
+	return sdio_memcpy_toio(func, addr, src, count);
+}
+EXPORT_SYMBOL(libra_sdio_memcpy_toio);
+
+int libra_detect_card_change(void)
+{
+	if (libra_mmc_host) {
+		if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+			&& (libra_mmc_host_index == libra_mmc_host->index)) {
+			mmc_detect_change(libra_mmc_host, 0);
+			return 0;
+		}
+	}
+
+	printk(KERN_ERR "%s: Could not trigger card change\n", __func__);
+	return -EINVAL;
+}
+EXPORT_SYMBOL(libra_detect_card_change);
+
+int libra_sdio_enable_polling(void)
+{
+	if (libra_mmc_host) {
+		if (!strcmp(libra_mmc_host->class_dev.class->name, "mmc_host")
+			&& (libra_mmc_host_index == libra_mmc_host->index)) {
+			libra_mmc_host->caps |= MMC_CAP_NEEDS_POLL;
+			mmc_detect_change(libra_mmc_host, 0);
+			return 0;
+		}
+	}
+
+	printk(KERN_ERR "%s: Could not trigger SDIO scan\n", __func__);
+	return -1;
+}
+EXPORT_SYMBOL(libra_sdio_enable_polling);
+
+void libra_sdio_set_clock(struct sdio_func *func, unsigned int clk_freq)
+{
+    struct mmc_host *host = func->card->host;
+    host->ios.clock = clk_freq;
+    host->ops->set_ios(host, &host->ios);
+
+}
+EXPORT_SYMBOL(libra_sdio_set_clock);
+
+/*
+ * API to get SDIO Device Card ID
+ */
+void libra_sdio_get_card_id(struct sdio_func *func, unsigned short *card_id)
+{
+	if (card_id)
+		*card_id = libra_sdio_card_id;
+}
+EXPORT_SYMBOL(libra_sdio_get_card_id);
+
+/*
+ * SDIO Probe
+ */
+static int libra_sdio_probe(struct sdio_func *func,
+		const struct sdio_device_id *sdio_dev_id)
+{
+	libra_mmc_host = func->card->host;
+	libra_mmc_host_index = libra_mmc_host->index;
+	libra_sdio_func = func;
+	libra_sdio_card_id = sdio_dev_id->device;
+
+	printk(KERN_INFO "%s: success with block size of %d device_id=0x%x\n",
+		__func__,
+		func->cur_blksize,
+		sdio_dev_id->device);
+
+	/* Turn off SDIO polling from now on */
+	libra_mmc_host->caps &= ~MMC_CAP_NEEDS_POLL;
+	return 0;
+}
+
+static void libra_sdio_remove(struct sdio_func *func)
+{
+	libra_sdio_func = NULL;
+
+	printk(KERN_INFO "%s : Module removed.\n", __func__);
+}
+
+#ifdef CONFIG_PM
+static int libra_sdio_suspend(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+	int ret = 0;
+
+	ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
+
+	if (ret) {
+		printk(KERN_ERR "%s: Error Host doesn't support the keep power capability\n" ,
+			__func__);
+		return ret;
+	}
+	if (libra_suspend_hldr) {
+		/* Disable SDIO IRQ when driver is being suspended */
+		libra_enable_sdio_irq(func, 0);
+		ret = libra_suspend_hldr(func);
+		if (ret) {
+			printk(KERN_ERR
+			"%s: Libra driver is not able to suspend\n" , __func__);
+			/* Error - Restore SDIO IRQ */
+			libra_enable_sdio_irq(func, 1);
+			return ret;
+		}
+	}
+
+
+	return sdio_set_host_pm_flags(func, MMC_PM_WAKE_SDIO_IRQ);
+}
+
+static int libra_sdio_resume(struct device *dev)
+{
+	struct sdio_func *func = dev_to_sdio_func(dev);
+
+	if (libra_resume_hldr) {
+		libra_resume_hldr(func);
+		/* Restore SDIO IRQ */
+		libra_enable_sdio_irq(func, 1);
+	}
+
+	return 0;
+}
+#else
+#define libra_sdio_suspend 0
+#define libra_sdio_resume 0
+#endif
+
+static struct sdio_device_id libra_sdioid[] = {
+    {.class = 0, .vendor = LIBRA_MAN_ID,  .device = LIBRA_REV_1_0_CARD_ID},
+    {.class = 0, .vendor = VOLANS_MAN_ID, .device = VOLANS_REV_2_0_CARD_ID},
+    {}
+};
+
+static const struct dev_pm_ops libra_sdio_pm_ops = {
+    .suspend = libra_sdio_suspend,
+    .resume = libra_sdio_resume,
+};
+
+static struct sdio_driver libra_sdiofn_driver = {
+    .name      = "libra_sdiofn",
+    .id_table  = libra_sdioid,
+    .probe     = libra_sdio_probe,
+    .remove    = libra_sdio_remove,
+    .drv.pm    = &libra_sdio_pm_ops,
+};
+
+static int __init libra_sdioif_init(void)
+{
+	libra_sdio_func = NULL;
+	libra_mmc_host = NULL;
+	libra_mmc_host_index = -1;
+	libra_suspend_hldr = NULL;
+	libra_resume_hldr = NULL;
+
+	sdio_register_driver(&libra_sdiofn_driver);
+
+	printk(KERN_INFO "%s: Loaded Successfully\n", __func__);
+
+	return 0;
+}
+
+static void __exit libra_sdioif_exit(void)
+{
+	unsigned int attempts = 0;
+
+	if (!libra_detect_card_change()) {
+		do {
+			++attempts;
+			msleep(500);
+		} while (libra_sdio_func != NULL && attempts < 3);
+	}
+
+	if (libra_sdio_func != NULL)
+		printk(KERN_ERR "%s: Card removal not detected\n", __func__);
+
+	sdio_unregister_driver(&libra_sdiofn_driver);
+
+	libra_sdio_func = NULL;
+	libra_mmc_host = NULL;
+	libra_mmc_host_index = -1;
+
+	printk(KERN_INFO "%s: Unloaded Successfully\n", __func__);
+}
+
+module_init(libra_sdioif_init);
+module_exit(libra_sdioif_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION("1.0");
+MODULE_DESCRIPTION("WLAN SDIODriver");
diff --git a/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
new file mode 100644
index 0000000..ca2680f
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan7x27a_pwrif.c
@@ -0,0 +1,172 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/device.h>
+#include <mach/vreg.h>
+#include <linux/gpio.h>
+#include <mach/rpc_pmapp.h>
+#include <linux/err.h>
+#include <linux/qcomwlan7x27a_pwrif.h>
+
+#define WLAN_GPIO_EXT_POR_N     134
+
+static const char *id = "WLAN";
+
+enum {
+	WLAN_VREG_L17 = 0,
+	WLAN_VREG_S3,
+	WLAN_VREG_TCXO_L11,
+	WLAN_VREG_L19,
+	WLAN_VREG_L5,
+	WLAN_VREG_L6
+};
+
+struct wlan_vreg_info {
+	const char *vreg_id;
+	unsigned int vreg_level;
+	unsigned int pmapp_id;
+	unsigned int is_vreg_pin_controlled;
+	struct vreg *vreg;
+};
+
+
+static struct wlan_vreg_info vreg_info[] = {
+	{"bt", 3050, 56, 0, NULL},
+	{"msme1", 1800, 2, 0, NULL},
+	{"wlan_tcx0", 1800, 53, 0, NULL},
+	{"wlan4", 1200, 57, 0, NULL},
+	{"wlan2", 1350, 45, 0, NULL},
+	{"wlan3", 1200, 51, 0, NULL} };
+
+int chip_power_qrf6285(bool on)
+{
+	int rc = 0, index = 0;
+
+	if (on) {
+		rc = gpio_request(WLAN_GPIO_EXT_POR_N, "WLAN_DEEP_SLEEP_N");
+
+		if (rc) {
+			pr_err("WLAN reset GPIO %d request failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+			goto fail;
+		}
+		rc = gpio_direction_output(WLAN_GPIO_EXT_POR_N, 1);
+		if (rc < 0) {
+			pr_err("WLAN reset GPIO %d set direction failed %d\n",
+			WLAN_GPIO_EXT_POR_N, rc);
+			goto fail_gpio_dir_out;
+		}
+	} else {
+		gpio_set_value_cansleep(WLAN_GPIO_EXT_POR_N, 0);
+		gpio_free(WLAN_GPIO_EXT_POR_N);
+	}
+
+
+	for (index = 0; index < ARRAY_SIZE(vreg_info); index++) {
+		vreg_info[index].vreg = vreg_get(NULL,
+						vreg_info[index].vreg_id);
+		if (IS_ERR(vreg_info[index].vreg)) {
+			pr_err("%s:%s vreg get failed %ld\n",
+				__func__, vreg_info[index].vreg_id,
+				PTR_ERR(vreg_info[index].vreg));
+			rc = PTR_ERR(vreg_info[index].vreg);
+			if (on)
+				goto vreg_fail;
+			else
+				continue;
+		}
+		if (on) {
+			rc = vreg_set_level(vreg_info[index].vreg,
+					 vreg_info[index].vreg_level);
+			if (rc) {
+				pr_err("%s:%s vreg set level failed %d\n",
+					__func__, vreg_info[index].vreg_id, rc);
+				goto vreg_fail;
+			}
+			if (vreg_info[index].is_vreg_pin_controlled) {
+				rc = pmapp_vreg_pincntrl_vote(id,
+					 vreg_info[index].pmapp_id,
+					 PMAPP_CLOCK_ID_A0, 1);
+				if (rc) {
+					pr_err("%s:%s pmapp_vreg_pincntrl_vote"
+						" for enable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+					goto vreg_fail;
+				}
+			} else {
+				rc = vreg_enable(vreg_info[index].vreg);
+				if (rc) {
+					pr_err("%s:%s vreg enable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+					goto vreg_fail;
+				}
+			}
+
+			if (WLAN_VREG_TCXO_L11 == index) {
+				/*
+				 * Configure TCXO to be slave to
+				 * WLAN_CLK_PWR_REQ
+`				 */
+				rc = pmapp_clock_vote(id, PMAPP_CLOCK_ID_A0,
+						PMAPP_CLOCK_VOTE_PIN_CTRL);
+				if (rc) {
+					pr_err("%s: Configuring TCXO to Pin"
+					" controllable failed %d\n",
+							 __func__, rc);
+					goto vreg_clock_vote_fail;
+				}
+			}
+
+		} else {
+
+			if (vreg_info[index].is_vreg_pin_controlled) {
+				rc = pmapp_vreg_pincntrl_vote(id,
+						 vreg_info[index].pmapp_id,
+						 PMAPP_CLOCK_ID_A0, 0);
+				if (rc) {
+					pr_err("%s:%s pmapp_vreg_pincntrl_vote"
+						" for disable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+				}
+			} else {
+				rc = vreg_disable(vreg_info[index].vreg);
+				if (rc) {
+					pr_err("%s:%s vreg disable failed %d\n",
+						__func__,
+						vreg_info[index].vreg_id, rc);
+				}
+			}
+		}
+	}
+	return 0;
+vreg_fail:
+	index--;
+vreg_clock_vote_fail:
+	while (index > 0) {
+		rc = vreg_disable(vreg_info[index].vreg);
+		if (rc) {
+			pr_err("%s:%s vreg disable failed %d\n",
+				__func__, vreg_info[index].vreg_id, rc);
+		}
+		index--;
+	}
+	if (!on)
+		goto fail;
+fail_gpio_dir_out:
+	gpio_free(WLAN_GPIO_EXT_POR_N);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(chip_power_qrf6285);
diff --git a/drivers/net/wireless/libra/qcomwlan_pwrif.c b/drivers/net/wireless/libra/qcomwlan_pwrif.c
new file mode 100644
index 0000000..bb5e135
--- /dev/null
+++ b/drivers/net/wireless/libra/qcomwlan_pwrif.c
@@ -0,0 +1,256 @@
+/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/qcomwlan_pwrif.h>
+
+#define GPIO_WLAN_DEEP_SLEEP_N  230
+#define WLAN_RESET_OUT          1
+#define WLAN_RESET              0
+
+static const char *id = "WLAN";
+
+/**
+ * vos_chip_power_qrf8615() - WLAN Power Up Seq for WCN1314 rev 2.0 on QRF 8615
+ * @on - Turn WLAN ON/OFF (1 or 0)
+ *
+ * Power up/down WLAN by turning on/off various regs and asserting/deasserting
+ * Power-on-reset pin. Also, put XO A0 buffer as slave to wlan_clk_pwr_req while
+ * turning ON WLAN and vice-versa.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int vos_chip_power_qrf8615(int on)
+{
+	static char wlan_on;
+	static const char *vregs_qwlan_name[] = {
+		"8058_l20",
+		"8058_l8",
+		"8901_s4",
+		"8901_lvs1",
+		"8901_l0",
+		"8058_s2",
+		"8058_s1",
+	};
+	static const int vregs_qwlan_val_min[] = {
+		1800000,
+		3050000,
+		1225000,
+		0,
+		1200000,
+		1300000,
+		500000,
+	};
+	static const int vregs_qwlan_val_max[] = {
+		1800000,
+		3050000,
+		1225000,
+		0,
+		1200000,
+		1300000,
+		1250000,
+	};
+	static const bool vregs_is_pin_controlled[] = {
+		1,
+		1,
+		0,
+		0,
+		1,
+		1,
+		0,
+	};
+	static struct regulator *vregs_qwlan[ARRAY_SIZE(vregs_qwlan_name)];
+	static struct msm_xo_voter *wlan_clock;
+	int ret, i, rc = 0;
+
+	/* WLAN RESET and CLK settings */
+	if (on && !wlan_on) {
+		/*
+		 * Program U12 GPIO expander pin IO1 to de-assert (drive 0)
+		 * WLAN_EXT_POR_N to put WLAN in reset
+		 */
+		rc = gpio_request(GPIO_WLAN_DEEP_SLEEP_N, "WLAN_DEEP_SLEEP_N");
+		if (rc) {
+			pr_err("WLAN reset GPIO %d request failed\n",
+					GPIO_WLAN_DEEP_SLEEP_N);
+			goto fail;
+		}
+		rc = gpio_direction_output(GPIO_WLAN_DEEP_SLEEP_N,
+				WLAN_RESET_OUT);
+		if (rc < 0) {
+			pr_err("WLAN reset GPIO %d set output direction failed",
+					GPIO_WLAN_DEEP_SLEEP_N);
+			goto fail_gpio_dir_out;
+		}
+
+		/* Configure TCXO to be slave to WLAN_CLK_PWR_REQ */
+		if (wlan_clock == NULL) {
+			wlan_clock = msm_xo_get(MSM_XO_TCXO_A0, id);
+			if (IS_ERR(wlan_clock)) {
+				pr_err("Failed to get TCXO_A0 voter (%ld)\n",
+						PTR_ERR(wlan_clock));
+				goto fail_gpio_dir_out;
+			}
+		}
+
+		rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_PIN_CTRL);
+		if (rc < 0) {
+			pr_err("Configuring TCXO to Pin controllable failed"
+					"(%d)\n", rc);
+			goto fail_xo_mode_vote;
+		}
+	} else if (!on && wlan_on) {
+		if (wlan_clock != NULL)
+			msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF);
+		gpio_set_value_cansleep(GPIO_WLAN_DEEP_SLEEP_N, WLAN_RESET);
+		gpio_free(GPIO_WLAN_DEEP_SLEEP_N);
+	}
+
+	/* WLAN VREG settings */
+	for (i = 0; i < ARRAY_SIZE(vregs_qwlan_name); i++) {
+		if (vregs_qwlan[i] == NULL) {
+			vregs_qwlan[i] = regulator_get(NULL,
+					vregs_qwlan_name[i]);
+			if (IS_ERR(vregs_qwlan[i])) {
+				pr_err("regulator get of %s failed (%ld)\n",
+						vregs_qwlan_name[i],
+						PTR_ERR(vregs_qwlan[i]));
+				rc = PTR_ERR(vregs_qwlan[i]);
+				goto vreg_get_fail;
+			}
+			if (vregs_qwlan_val_min[i] || vregs_qwlan_val_max[i]) {
+				rc = regulator_set_voltage(vregs_qwlan[i],
+						vregs_qwlan_val_min[i],
+						vregs_qwlan_val_max[i]);
+				if (rc) {
+					pr_err("regulator_set_voltage(%s) failed\n",
+							vregs_qwlan_name[i]);
+					goto vreg_fail;
+				}
+			}
+			/* vote for pin control (if needed) */
+			if (vregs_is_pin_controlled[i]) {
+				rc = regulator_set_mode(vregs_qwlan[i],
+						REGULATOR_MODE_IDLE);
+				if (rc) {
+					pr_err("regulator_set_mode(%s) failed\n",
+							vregs_qwlan_name[i]);
+					goto vreg_fail;
+				}
+			}
+		}
+		if (on && !wlan_on) {
+			rc = regulator_enable(vregs_qwlan[i]);
+			if (rc < 0) {
+				pr_err("vreg %s enable failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+				goto vreg_fail;
+			}
+		} else if (!on && wlan_on) {
+			rc = regulator_disable(vregs_qwlan[i]);
+			if (rc < 0) {
+				pr_err("vreg %s disable failed (%d)\n",
+						vregs_qwlan_name[i], rc);
+				goto vreg_fail;
+			}
+		}
+	}
+	if (on)
+		wlan_on = true;
+	else
+		wlan_on = false;
+	return 0;
+
+vreg_fail:
+	regulator_put(vregs_qwlan[i]);
+vreg_get_fail:
+	i--;
+	while (i) {
+		ret = !on ? regulator_enable(vregs_qwlan[i]) :
+			regulator_disable(vregs_qwlan[i]);
+		if (ret < 0) {
+			pr_err("vreg %s %s failed (%d) in err path\n",
+					vregs_qwlan_name[i],
+					!on ? "enable" : "disable", ret);
+		}
+		regulator_put(vregs_qwlan[i]);
+		i--;
+	}
+	if (!on)
+		goto fail;
+fail_xo_mode_vote:
+	msm_xo_put(wlan_clock);
+fail_gpio_dir_out:
+	gpio_free(GPIO_WLAN_DEEP_SLEEP_N);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(vos_chip_power_qrf8615);
+
+/**
+ * qcomwlan_pmic_xo_core_force_enable() - Force XO Core of PMIC to be ALWAYS ON
+ * @on - Force XO Core  ON/OFF (1 or 0)
+ *
+ * The XO_CORE controls the XO feeding the TCXO buffers (A0, A1, etc.). WLAN
+ * wants to keep the XO core on even though our buffer A0 is in pin control
+ * because it can take a long time turn the XO back on and warm up the buffers.
+ * This helps in optimizing power in BMPS (power save) mode of WLAN.
+ * The WLAN driver wrapper function takes care that this API is not called
+ * consecutively.
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+int qcomwlan_pmic_xo_core_force_enable(int on)
+{
+	static struct msm_xo_voter *wlan_ps;
+	int rc = 0;
+
+	if (wlan_ps == NULL) {
+		wlan_ps = msm_xo_get(MSM_XO_CORE, id);
+		if (IS_ERR(wlan_ps)) {
+			pr_err("Failed to get XO CORE voter (%ld)\n",
+					PTR_ERR(wlan_ps));
+			goto fail;
+		}
+	}
+
+	if (on)
+		rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_ON);
+	else
+		rc = msm_xo_mode_vote(wlan_ps, MSM_XO_MODE_OFF);
+
+	if (rc < 0) {
+		pr_err("XO Core %s failed (%d)\n",
+			on ? "enable" : "disable", rc);
+		goto fail_xo_mode_vote;
+	}
+	return 0;
+fail_xo_mode_vote:
+	msm_xo_put(wlan_ps);
+fail:
+	return rc;
+}
+EXPORT_SYMBOL(qcomwlan_pmic_xo_core_force_enable);
+
+
+/**
+ * qcomwlan_freq_change_1p3v_supply() - function to change the freq for 1.3V RF supply.
+ * @freq - freq of the 1.3V Supply
+ *
+ * This function returns 0 on success or a non-zero value on failure.
+ */
+
+int qcomwlan_freq_change_1p3v_supply(enum rpm_vreg_freq freq)
+{
+	return rpm_vreg_set_frequency(RPM_VREG_ID_PM8058_S2, freq);
+}
+EXPORT_SYMBOL(qcomwlan_freq_change_1p3v_supply);
diff --git a/drivers/net/wireless/wcnss/Makefile b/drivers/net/wireless/wcnss/Makefile
new file mode 100644
index 0000000..d182b6e
--- /dev/null
+++ b/drivers/net/wireless/wcnss/Makefile
@@ -0,0 +1,6 @@
+
+# Makefile for WCNSS WLAN driver
+
+wcnsswlan-objs += wcnss_wlan.o wcnss_riva.o qcomwlan_secif.o
+
+obj-$(CONFIG_WCNSS_WLAN) += wcnsswlan.o
diff --git a/drivers/net/wireless/wcnss/qcomwlan_secif.c b/drivers/net/wireless/wcnss/qcomwlan_secif.c
new file mode 100644
index 0000000..124f387
--- /dev/null
+++ b/drivers/net/wireless/wcnss/qcomwlan_secif.c
@@ -0,0 +1,62 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/qcomwlan_secif.h>
+
+/*
+ * APIs for calling crypto routines from kernel
+ */
+struct crypto_ahash *wcnss_wlan_crypto_alloc_ahash(const char *alg_name,
+							 u32 type, u32 mask)
+{
+	return crypto_alloc_ahash(alg_name, type, mask);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ahash);
+
+int wcnss_wlan_crypto_ahash_digest(struct ahash_request *req)
+{
+	return crypto_ahash_digest(req);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_digest);
+
+void wcnss_wlan_crypto_free_ahash(struct crypto_ahash *tfm)
+{
+	crypto_free_ahash(tfm);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_free_ahash);
+
+int wcnss_wlan_crypto_ahash_setkey(struct crypto_ahash *tfm, const u8 *key,
+			unsigned int keylen)
+{
+	return crypto_ahash_setkey(tfm, key, keylen);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_ahash_setkey);
+
+struct crypto_ablkcipher *
+wcnss_wlan_crypto_alloc_ablkcipher(const char *alg_name, u32 type, u32 mask)
+{
+	return crypto_alloc_ablkcipher(alg_name, type, mask);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_alloc_ablkcipher);
+
+void wcnss_wlan_ablkcipher_request_free(struct ablkcipher_request *req)
+{
+	ablkcipher_request_free(req);
+}
+EXPORT_SYMBOL(wcnss_wlan_ablkcipher_request_free);
+
+void wcnss_wlan_crypto_free_ablkcipher(struct crypto_ablkcipher *tfm)
+{
+	crypto_free_ablkcipher(tfm);
+}
+EXPORT_SYMBOL(wcnss_wlan_crypto_free_ablkcipher);
+
diff --git a/drivers/net/wireless/wcnss/wcnss_riva.c b/drivers/net/wireless/wcnss/wcnss_riva.c
new file mode 100644
index 0000000..3617ba8
--- /dev/null
+++ b/drivers/net/wireless/wcnss/wcnss_riva.c
@@ -0,0 +1,314 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <linux/mfd/pm8xxx/pm8921.h>
+#include <linux/mfd/pm8xxx/gpio.h>
+#include <mach/msm_xo.h>
+#include <mach/msm_iomap.h>
+
+#include "wcnss_riva.h"
+
+static void __iomem *msm_riva_base;
+static struct msm_xo_voter *wlan_clock;
+static const char *id = "WLAN";
+
+#define MSM_RIVA_PHYS                     0x03204000
+#define RIVA_PMU_CFG                      (msm_riva_base + 0x28)
+#define RIVA_PMU_CFG_IRIS_XO_CFG          BIT(3)
+#define RIVA_PMU_CFG_IRIS_XO_EN           BIT(4)
+#define RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP   BIT(5)
+#define RIVA_PMU_CFG_IRIS_XO_CFG_STS      BIT(6) /* 1: in progress, 0: done */
+
+#define RIVA_PMU_CFG_IRIS_XO_MODE         0x6
+#define RIVA_PMU_CFG_IRIS_XO_MODE_48      (3 << 1)
+
+#define VREG_NULL_CONFIG            0x0000
+#define VREG_GET_REGULATOR_MASK     0x0001
+#define VREG_SET_VOLTAGE_MASK       0x0002
+#define VREG_OPTIMUM_MODE_MASK      0x0004
+#define VREG_ENABLE_MASK            0x0008
+
+struct vregs_info {
+	const char * const name;
+	int state;
+	const int nominal_min;
+	const int low_power_min;
+	const int max_voltage;
+	const int uA_load;
+	struct regulator *regulator;
+};
+
+static struct vregs_info iris_vregs[] = {
+	{"iris_vddio",  VREG_NULL_CONFIG, 0000000, 0, 0000000, 0,      NULL},
+	{"iris_vddxo",  VREG_NULL_CONFIG, 1800000, 0, 1800000, 10000,  NULL},
+	{"iris_vddrfa", VREG_NULL_CONFIG, 1300000, 0, 1300000, 100000, NULL},
+	{"iris_vddpa",  VREG_NULL_CONFIG, 2900000, 0, 2900000, 515000, NULL},
+	{"iris_vdddig", VREG_NULL_CONFIG, 0000000, 0, 0000000, 0,      NULL},
+};
+
+static struct vregs_info riva_vregs[] = {
+	{"riva_vddmx",  VREG_NULL_CONFIG, 1050000, 0, 1150000, 0,      NULL},
+	{"riva_vddcx",  VREG_NULL_CONFIG, 1050000, 0, 1150000, 0,      NULL},
+	{"riva_vddpx",  VREG_NULL_CONFIG, 1800000, 0, 1800000, 0,      NULL},
+};
+
+static int configure_iris_xo(bool use_48mhz_xo, int on)
+{
+	u32 reg = 0;
+	int rc = 0;
+
+	if (on) {
+		msm_riva_base = ioremap(MSM_RIVA_PHYS, SZ_256);
+		if (!msm_riva_base) {
+			pr_err("ioremap MSM_RIVA_PHYS failed\n");
+			goto fail;
+		}
+
+		/* Enable IRIS XO */
+		writel_relaxed(0, RIVA_PMU_CFG);
+		reg = readl_relaxed(RIVA_PMU_CFG);
+		reg |= RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
+				RIVA_PMU_CFG_IRIS_XO_EN;
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Clear XO_MODE[b2:b1] bits. Clear implies 19.2 MHz TCXO */
+		reg &= ~(RIVA_PMU_CFG_IRIS_XO_MODE);
+
+		if (use_48mhz_xo)
+			reg |= RIVA_PMU_CFG_IRIS_XO_MODE_48;
+
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Start IRIS XO configuration */
+		reg |= RIVA_PMU_CFG_IRIS_XO_CFG;
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		/* Wait for XO configuration to finish */
+		while (readl_relaxed(RIVA_PMU_CFG) &
+						RIVA_PMU_CFG_IRIS_XO_CFG_STS)
+			cpu_relax();
+
+		/* Stop IRIS XO configuration */
+		reg &= ~(RIVA_PMU_CFG_GC_BUS_MUX_SEL_TOP |
+				RIVA_PMU_CFG_IRIS_XO_CFG);
+		writel_relaxed(reg, RIVA_PMU_CFG);
+
+		if (!use_48mhz_xo) {
+			wlan_clock = msm_xo_get(MSM_XO_TCXO_A0, id);
+			if (IS_ERR(wlan_clock)) {
+				rc = PTR_ERR(wlan_clock);
+				pr_err("Failed to get MSM_XO_TCXO_A0 voter"
+							" (%d)\n", rc);
+				goto fail;
+			}
+
+			rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_ON);
+			if (rc < 0) {
+				pr_err("Configuring MSM_XO_MODE_ON failed"
+							" (%d)\n", rc);
+				goto msm_xo_vote_fail;
+			}
+		}
+	}  else {
+		if (wlan_clock != NULL && !use_48mhz_xo) {
+			rc = msm_xo_mode_vote(wlan_clock, MSM_XO_MODE_OFF);
+			if (rc < 0)
+				pr_err("Configuring MSM_XO_MODE_OFF failed"
+							" (%d)\n", rc);
+		}
+	}
+
+	/* Add some delay for XO to settle */
+	msleep(20);
+
+	return rc;
+
+msm_xo_vote_fail:
+	msm_xo_put(wlan_clock);
+
+fail:
+	return rc;
+}
+
+/* Helper routine to turn off all WCNSS vregs e.g. IRIS, Riva */
+static void wcnss_vregs_off(struct vregs_info regulators[], uint size)
+{
+	int i, rc = 0;
+
+	/* Regulators need to be turned off in the reverse order */
+	for (i = (size-1); i >= 0; i--) {
+		if (regulators[i].state == VREG_NULL_CONFIG)
+			continue;
+
+		/* Remove PWM mode */
+		if (regulators[i].state & VREG_OPTIMUM_MODE_MASK) {
+			rc = regulator_set_optimum_mode(
+					regulators[i].regulator, 0);
+			if (rc < 0)
+				pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Set voltage to lowest level */
+		if (regulators[i].state & VREG_SET_VOLTAGE_MASK) {
+			rc = regulator_set_voltage(regulators[i].regulator,
+					regulators[i].low_power_min,
+					regulators[i].max_voltage);
+			if (rc)
+				pr_err("regulator_set_voltage(%s) failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Disable regulator */
+		if (regulators[i].state & VREG_ENABLE_MASK) {
+			rc = regulator_disable(regulators[i].regulator);
+			if (rc < 0)
+				pr_err("vreg %s disable failed (%d)\n",
+						regulators[i].name, rc);
+		}
+
+		/* Free the regulator source */
+		if (regulators[i].state & VREG_GET_REGULATOR_MASK)
+			regulator_put(regulators[i].regulator);
+
+		regulators[i].state = VREG_NULL_CONFIG;
+	}
+}
+
+/* Common helper routine to turn on all WCNSS vregs e.g. IRIS, Riva */
+static int wcnss_vregs_on(struct device *dev,
+		struct vregs_info regulators[], uint size)
+{
+	int i, rc = 0;
+
+	for (i = 0; i < size; i++) {
+			/* Get regulator source */
+		regulators[i].regulator =
+			regulator_get(dev, regulators[i].name);
+		if (IS_ERR(regulators[i].regulator)) {
+			rc = PTR_ERR(regulators[i].regulator);
+				pr_err("regulator get of %s failed (%d)\n",
+					regulators[i].name, rc);
+				goto fail;
+		}
+		regulators[i].state |= VREG_GET_REGULATOR_MASK;
+
+		/* Set voltage to nominal. Exclude swtiches e.g. LVS */
+		if (regulators[i].nominal_min || regulators[i].max_voltage) {
+			rc = regulator_set_voltage(regulators[i].regulator,
+					regulators[i].nominal_min,
+					regulators[i].max_voltage);
+			if (rc) {
+				pr_err("regulator_set_voltage(%s) failed (%d)\n",
+						regulators[i].name, rc);
+				goto fail;
+			}
+			regulators[i].state |= VREG_SET_VOLTAGE_MASK;
+		}
+
+		/* Vote for PWM/PFM mode if needed */
+		if (regulators[i].uA_load) {
+			rc = regulator_set_optimum_mode(regulators[i].regulator,
+					regulators[i].uA_load);
+			if (rc < 0) {
+				pr_err("regulator_set_optimum_mode(%s) failed (%d)\n",
+						regulators[i].name, rc);
+				goto fail;
+			}
+			regulators[i].state |= VREG_OPTIMUM_MODE_MASK;
+		}
+
+		/* Enable the regulator */
+		rc = regulator_enable(regulators[i].regulator);
+		if (rc) {
+			pr_err("vreg %s enable failed (%d)\n",
+				regulators[i].name, rc);
+			goto fail;
+		}
+		regulators[i].state |= VREG_ENABLE_MASK;
+	}
+
+	return rc;
+
+fail:
+	wcnss_vregs_off(regulators, size);
+	return rc;
+
+}
+
+static void wcnss_iris_vregs_off(void)
+{
+	wcnss_vregs_off(iris_vregs, ARRAY_SIZE(iris_vregs));
+}
+
+static int wcnss_iris_vregs_on(struct device *dev)
+{
+	return wcnss_vregs_on(dev, iris_vregs, ARRAY_SIZE(iris_vregs));
+}
+
+static void wcnss_riva_vregs_off(void)
+{
+	wcnss_vregs_off(riva_vregs, ARRAY_SIZE(riva_vregs));
+}
+
+static int wcnss_riva_vregs_on(struct device *dev)
+{
+	return wcnss_vregs_on(dev, riva_vregs, ARRAY_SIZE(riva_vregs));
+}
+
+int wcnss_wlan_power(struct device *dev,
+		struct wcnss_wlan_config *cfg,
+		enum wcnss_opcode on)
+{
+	int rc = 0;
+
+	if (on) {
+		/* RIVA regulator settings */
+		rc = wcnss_riva_vregs_on(dev);
+		if (rc)
+			goto fail_riva_on;
+
+		/* IRIS regulator settings */
+		rc = wcnss_iris_vregs_on(dev);
+		if (rc)
+			goto fail_iris_on;
+
+		/* Configure IRIS XO */
+		rc = configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_ON);
+		if (rc)
+			goto fail_iris_xo;
+
+	} else {
+		configure_iris_xo(cfg->use_48mhz_xo, WCNSS_WLAN_SWITCH_OFF);
+		wcnss_iris_vregs_off();
+		wcnss_riva_vregs_off();
+	}
+
+	return rc;
+
+fail_iris_xo:
+	wcnss_iris_vregs_off();
+
+fail_iris_on:
+	wcnss_riva_vregs_off();
+
+fail_riva_on:
+	return rc;
+}
+EXPORT_SYMBOL(wcnss_wlan_power);
+
diff --git a/drivers/net/wireless/wcnss/wcnss_riva.h b/drivers/net/wireless/wcnss/wcnss_riva.h
new file mode 100644
index 0000000..e037f58
--- /dev/null
+++ b/drivers/net/wireless/wcnss/wcnss_riva.h
@@ -0,0 +1,31 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef _WCNSS_RIVA_H_
+#define _WCNSS_RIVA_H_
+
+#include <linux/device.h>
+
+enum wcnss_opcode {
+	WCNSS_WLAN_SWITCH_OFF = 0,
+	WCNSS_WLAN_SWITCH_ON,
+};
+
+struct wcnss_wlan_config {
+	int		use_48mhz_xo;
+};
+
+int wcnss_wlan_power(struct device *dev,
+		struct wcnss_wlan_config *cfg,
+		enum wcnss_opcode opcode);
+
+#endif /* _WCNSS_RIVA_H_ */
diff --git a/drivers/net/wireless/wcnss/wcnss_wlan.c b/drivers/net/wireless/wcnss/wcnss_wlan.c
new file mode 100644
index 0000000..371e58e
--- /dev/null
+++ b/drivers/net/wireless/wcnss/wcnss_wlan.c
@@ -0,0 +1,339 @@
+/* Copyright (c) 2011, Code Aurora Forum. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/parser.h>
+#include <linux/wcnss_wlan.h>
+#include <mach/peripheral-loader.h>
+#include "wcnss_riva.h"
+
+#define DEVICE "wcnss_wlan"
+#define VERSION "1.01"
+#define WCNSS_PIL_DEVICE "wcnss"
+#define WCNSS_NV_NAME "wlan/prima/WCNSS_qcom_cfg.ini"
+
+/* By default assume 48MHz XO is populated */
+#define CONFIG_USE_48MHZ_XO_DEFAULT 1
+
+static struct {
+	struct platform_device *pdev;
+	void		*pil;
+	struct resource	*mmio_res;
+	struct resource	*tx_irq_res;
+	struct resource	*rx_irq_res;
+	const struct dev_pm_ops *pm_ops;
+	int             smd_channel_ready;
+	struct wcnss_wlan_config wlan_config;
+} *penv = NULL;
+
+enum {
+	nv_none = -1,
+	nv_use_48mhz_xo,
+	nv_end,
+};
+
+static const match_table_t nv_tokens = {
+	{nv_use_48mhz_xo, "gUse48MHzXO=%d"},
+	{nv_end, "END"},
+	{nv_none, NULL}
+};
+
+static void wcnss_init_config(void)
+{
+	penv->wlan_config.use_48mhz_xo = CONFIG_USE_48MHZ_XO_DEFAULT;
+}
+
+static void wcnss_parse_nv(char *nvp)
+{
+	substring_t args[MAX_OPT_ARGS];
+	char *cur;
+	char *tok;
+	int token;
+	int intval;
+
+	cur = nvp;
+	while (cur != NULL) {
+		if ('#' == *cur) {
+			/* comment, consume remainder of line */
+			tok = strsep(&cur, "\r\n");
+			continue;
+		}
+
+		tok = strsep(&cur, " \t\r\n,");
+		if (!*tok)
+			continue;
+
+		token = match_token(tok, nv_tokens, args);
+		switch (token) {
+		case nv_use_48mhz_xo:
+			if (match_int(&args[0], &intval)) {
+				dev_err(&penv->pdev->dev,
+					"Invalid value for gUse48MHzXO: %s\n",
+					args[0].from);
+				continue;
+			}
+			if ((0 > intval) || (1 < intval)) {
+				dev_err(&penv->pdev->dev,
+					"Invalid value for gUse48MHzXO: %d\n",
+					intval);
+				continue;
+			}
+			penv->wlan_config.use_48mhz_xo = intval;
+			dev_info(&penv->pdev->dev,
+					"gUse48MHzXO set to %d\n", intval);
+			break;
+		case nv_end:
+			/* end of options so we are done */
+			return;
+		default:
+			/* silently ignore unknown settings */
+			break;
+		}
+	}
+}
+
+static int __devinit
+wcnss_wlan_ctrl_probe(struct platform_device *pdev)
+{
+	if (penv)
+		penv->smd_channel_ready = 1;
+
+	pr_info("%s: SMD ctrl channel up\n", __func__);
+
+	return 0;
+}
+
+static int __devexit
+wcnss_wlan_ctrl_remove(struct platform_device *pdev)
+{
+	if (penv)
+		penv->smd_channel_ready = 0;
+
+	pr_info("%s: SMD ctrl channel down\n", __func__);
+
+	return 0;
+}
+
+
+static struct platform_driver wcnss_wlan_ctrl_driver = {
+	.driver = {
+		.name	= "WLAN_CTRL",
+		.owner	= THIS_MODULE,
+	},
+	.probe	= wcnss_wlan_ctrl_probe,
+	.remove	= __devexit_p(wcnss_wlan_ctrl_remove),
+};
+
+struct device *wcnss_wlan_get_device(void)
+{
+	if (penv && penv->pdev && penv->smd_channel_ready)
+		return &penv->pdev->dev;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_device);
+
+struct resource *wcnss_wlan_get_memory_map(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) && penv->smd_channel_ready)
+		return penv->mmio_res;
+	return NULL;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_memory_map);
+
+int wcnss_wlan_get_dxe_tx_irq(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+				penv->tx_irq_res && penv->smd_channel_ready)
+		return penv->tx_irq_res->start;
+	return WCNSS_WLAN_IRQ_INVALID;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_dxe_tx_irq);
+
+int wcnss_wlan_get_dxe_rx_irq(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+				penv->rx_irq_res && penv->smd_channel_ready)
+		return penv->rx_irq_res->start;
+	return WCNSS_WLAN_IRQ_INVALID;
+}
+EXPORT_SYMBOL(wcnss_wlan_get_dxe_rx_irq);
+
+void wcnss_wlan_register_pm_ops(struct device *dev,
+				const struct dev_pm_ops *pm_ops)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) && pm_ops)
+		penv->pm_ops = pm_ops;
+}
+EXPORT_SYMBOL(wcnss_wlan_register_pm_ops);
+
+static int wcnss_wlan_suspend(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+	    penv->smd_channel_ready &&
+	    penv->pm_ops && penv->pm_ops->suspend)
+		return penv->pm_ops->suspend(dev);
+	return 0;
+}
+
+static int wcnss_wlan_resume(struct device *dev)
+{
+	if (penv && dev && (dev == &penv->pdev->dev) &&
+	    penv->smd_channel_ready &&
+	    penv->pm_ops && penv->pm_ops->resume)
+		return penv->pm_ops->resume(dev);
+	return 0;
+}
+
+static int __devinit
+wcnss_wlan_probe(struct platform_device *pdev)
+{
+	const struct firmware *nv;
+	char *nvp;
+	int ret;
+
+	/* verify we haven't been called more than once */
+	if (penv) {
+		dev_err(&pdev->dev, "cannot handle multiple devices.\n");
+		return -ENODEV;
+	}
+
+	/* create an environment to track the device */
+	penv = kzalloc(sizeof(*penv), GFP_KERNEL);
+	if (!penv) {
+		dev_err(&pdev->dev, "cannot allocate device memory.\n");
+		return -ENOMEM;
+	}
+	penv->pdev = pdev;
+
+	/* initialize the WCNSS default configuration */
+	wcnss_init_config();
+
+	/* update the WCNSS configuration from NV if present */
+	ret = request_firmware(&nv, WCNSS_NV_NAME, &pdev->dev);
+	if (!ret) {
+		/* firmware is read-only so make a NUL-terminated copy */
+		nvp = kmalloc(nv->size+1, GFP_KERNEL);
+		if (nvp) {
+			memcpy(nvp, nv->data, nv->size);
+			nvp[nv->size] = '\0';
+			wcnss_parse_nv(nvp);
+			kfree(nvp);
+		} else {
+			dev_err(&pdev->dev, "cannot parse NV.\n");
+		}
+		release_firmware(nv);
+	} else {
+		dev_err(&pdev->dev, "cannot read NV.\n");
+	}
+
+	/* power up the WCNSS */
+	ret = wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
+					WCNSS_WLAN_SWITCH_ON);
+	if (ret) {
+		dev_err(&pdev->dev, "WCNSS Power-up failed.\n");
+		goto fail_power;
+	}
+
+	/* trigger initialization of the WCNSS */
+	penv->pil = pil_get(WCNSS_PIL_DEVICE);
+	if (IS_ERR(penv->pil)) {
+		dev_err(&pdev->dev, "Peripheral Loader failed on WCNSS.\n");
+		ret = PTR_ERR(penv->pil);
+		penv->pil = NULL;
+		goto fail_pil;
+	}
+
+	/* allocate resources */
+	penv->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
+							"wcnss_mmio");
+	penv->tx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							"wcnss_wlantx_irq");
+	penv->rx_irq_res = platform_get_resource_byname(pdev, IORESOURCE_IRQ,
+							"wcnss_wlanrx_irq");
+
+	if (!(penv->mmio_res && penv->tx_irq_res && penv->rx_irq_res)) {
+		dev_err(&pdev->dev, "insufficient resources\n");
+		ret = -ENOENT;
+		goto fail_res;
+	}
+
+	return 0;
+
+fail_res:
+	if (penv->pil)
+		pil_put(penv->pil);
+fail_pil:
+	wcnss_wlan_power(&pdev->dev, &penv->wlan_config,
+				WCNSS_WLAN_SWITCH_OFF);
+fail_power:
+	kfree(penv);
+	penv = NULL;
+	return ret;
+}
+
+static int __devexit
+wcnss_wlan_remove(struct platform_device *pdev)
+{
+	return 0;
+}
+
+
+static const struct dev_pm_ops wcnss_wlan_pm_ops = {
+	.suspend	= wcnss_wlan_suspend,
+	.resume		= wcnss_wlan_resume,
+};
+
+static struct platform_driver wcnss_wlan_driver = {
+	.driver = {
+		.name	= DEVICE,
+		.owner	= THIS_MODULE,
+		.pm	= &wcnss_wlan_pm_ops,
+	},
+	.probe	= wcnss_wlan_probe,
+	.remove	= __devexit_p(wcnss_wlan_remove),
+};
+
+static int __init wcnss_wlan_init(void)
+{
+	platform_driver_register(&wcnss_wlan_driver);
+	platform_driver_register(&wcnss_wlan_ctrl_driver);
+
+	return 0;
+}
+
+static void __exit wcnss_wlan_exit(void)
+{
+	if (penv) {
+		if (penv->pil)
+			pil_put(penv->pil);
+
+		wcnss_wlan_power(&penv->pdev->dev, &penv->wlan_config,
+					WCNSS_WLAN_SWITCH_OFF);
+
+		kfree(penv);
+		penv = NULL;
+	}
+
+	platform_driver_unregister(&wcnss_wlan_ctrl_driver);
+	platform_driver_unregister(&wcnss_wlan_driver);
+}
+
+module_init(wcnss_wlan_init);
+module_exit(wcnss_wlan_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_VERSION(VERSION);
+MODULE_DESCRIPTION(DEVICE "Driver");