diff options
Diffstat (limited to 'drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c')
| -rw-r--r-- | drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c | 2596 | 
1 files changed, 1406 insertions, 1190 deletions
diff --git a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c index 1aa75d5951b..8fa0dbbbda7 100644 --- a/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c +++ b/drivers/net/wireless/brcm80211/brcmfmac/dhd_sdio.c @@ -23,6 +23,7 @@  #include <linux/interrupt.h>  #include <linux/sched.h>  #include <linux/mmc/sdio.h> +#include <linux/mmc/sdio_ids.h>  #include <linux/mmc/sdio_func.h>  #include <linux/mmc/card.h>  #include <linux/semaphore.h> @@ -32,6 +33,7 @@  #include <linux/debugfs.h>  #include <linux/vmalloc.h>  #include <linux/platform_data/brcmfmac-sdio.h> +#include <linux/moduleparam.h>  #include <asm/unaligned.h>  #include <defs.h>  #include <brcmu_wifi.h> @@ -39,7 +41,8 @@  #include <brcm_hw_ids.h>  #include <soc.h>  #include "sdio_host.h" -#include "sdio_chip.h" +#include "chip.h" +#include "firmware.h"  #define DCMD_RESP_TIMEOUT  2000	/* In milli second */ @@ -152,6 +155,34 @@ struct rte_console {  /* manfid tuple length, include tuple, link bytes */  #define SBSDIO_CIS_MANFID_TUPLE_LEN	6 +#define CORE_BUS_REG(base, field) \ +		(base + offsetof(struct sdpcmd_regs, field)) + +/* SDIO function 1 register CHIPCLKCSR */ +/* Force ALP request to backplane */ +#define SBSDIO_FORCE_ALP		0x01 +/* Force HT request to backplane */ +#define SBSDIO_FORCE_HT			0x02 +/* Force ILP request to backplane */ +#define SBSDIO_FORCE_ILP		0x04 +/* Make ALP ready (power up xtal) */ +#define SBSDIO_ALP_AVAIL_REQ		0x08 +/* Make HT ready (power up PLL) */ +#define SBSDIO_HT_AVAIL_REQ		0x10 +/* Squelch clock requests from HW */ +#define SBSDIO_FORCE_HW_CLKREQ_OFF	0x20 +/* Status: ALP is ready */ +#define SBSDIO_ALP_AVAIL		0x40 +/* Status: HT is ready */ +#define SBSDIO_HT_AVAIL			0x80 +#define SBSDIO_CSR_MASK			0x1F +#define SBSDIO_AVBITS		(SBSDIO_HT_AVAIL | SBSDIO_ALP_AVAIL) +#define SBSDIO_ALPAV(regval)	((regval) & SBSDIO_AVBITS) +#define SBSDIO_HTAV(regval)	(((regval) & SBSDIO_AVBITS) == SBSDIO_AVBITS) +#define SBSDIO_ALPONLY(regval)	(SBSDIO_ALPAV(regval) && !SBSDIO_HTAV(regval)) +#define SBSDIO_CLKAV(regval, alponly) \ +	(SBSDIO_ALPAV(regval) && (alponly ? 1 : SBSDIO_HTAV(regval))) +  /* intstatus */  #define I_SMB_SW0	(1 << 0)	/* To SB Mail S/W interrupt 0 */  #define I_SMB_SW1	(1 << 1)	/* To SB Mail S/W interrupt 1 */ @@ -257,9 +288,6 @@ struct rte_console {  #define MAX_HDR_READ	(1 << 6)  #define MAX_RX_DATASZ	2048 -/* Maximum milliseconds to wait for F2 to come up */ -#define BRCMF_WAIT_F2RDY	3000 -  /* Bump up limit on waiting for HT to account for first startup;   * if the image is doing a CRC calculation before programming the PMU   * for HT availability, it could take a couple hundred ms more, so @@ -275,12 +303,6 @@ struct rte_console {  /* Flags for SDH calls */  #define F2SYNC	(SDIO_REQ_4BYTE | SDIO_REQ_FIXED) -#define BRCMF_SDIO_FW_NAME	"brcm/brcmfmac-sdio.bin" -#define BRCMF_SDIO_NV_NAME	"brcm/brcmfmac-sdio.txt" -MODULE_FIRMWARE(BRCMF_SDIO_FW_NAME); -MODULE_FIRMWARE(BRCMF_SDIO_NV_NAME); - -#define BRCMF_IDLE_IMMEDIATE	(-1)	/* Enter idle immediately */  #define BRCMF_IDLE_ACTIVE	0	/* Do not request any SD clock change  					 * when idle  					 */ @@ -365,15 +387,15 @@ struct brcmf_sdio_hdrinfo {  	u16 len_left;  	u16 len_nxtfrm;  	u8 dat_offset; +	bool lastfrm; +	u16 tail_pad;  };  /* misc chip info needed by some of the routines */  /* Private data for SDIO bus interaction */  struct brcmf_sdio {  	struct brcmf_sdio_dev *sdiodev;	/* sdio device handler */ -	struct chip_info *ci;	/* Chip info struct */ -	char *vars;		/* Variables (from CIS and/or other) */ -	uint varsz;		/* Size of variables buffer */ +	struct brcmf_chip *ci;	/* Chip info struct */  	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */ @@ -389,7 +411,7 @@ struct brcmf_sdio {  	u8 tx_seq;		/* Transmit sequence number (next) */  	u8 tx_max;		/* Maximum transmit sequence allowed */ -	u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN]; +	u8 *hdrbuf;		/* buffer for handling rx frame */  	u8 *rxhdr;		/* Header of current rx frame (in hdrbuf) */  	u8 rx_seq;		/* Receive sequence number (expected) */  	struct brcmf_sdio_hdrinfo cur_read; @@ -437,10 +459,11 @@ struct brcmf_sdio {  	bool alp_only;		/* Don't use HT clock (ALP only) */  	u8 *ctrl_frame_buf; -	u32 ctrl_frame_len; +	u16 ctrl_frame_len;  	bool ctrl_frame_stat; -	spinlock_t txqlock; +	spinlock_t txq_lock;		/* protect bus->txq */ +	struct semaphore tx_seq_lock;	/* protect bus->tx_seq */  	wait_queue_head_t ctrl_wait;  	wait_queue_head_t dcmd_resp_wait; @@ -454,15 +477,15 @@ struct brcmf_sdio {  	struct work_struct datawork;  	atomic_t dpc_tskcnt; -	const struct firmware *firmware; -	u32 fw_ptr; -  	bool txoff;		/* Transmit flow-controlled */  	struct brcmf_sdio_count sdcnt;  	bool sr_enabled; /* SaveRestore enabled */  	bool sleeping; /* SDIO bus sleeping */  	u8 tx_hdrlen;		/* sdio bus header length for tx packet */ +	bool txglom;		/* host tx glomming enable flag */ +	u16 head_align;		/* buffer pointer alignment */ +	u16 sgentry_align;	/* scatter-gather buffer alignment */  };  /* clkstate */ @@ -493,6 +516,146 @@ enum brcmf_sdio_frmtype {  	BRCMF_SDIO_FT_SUB,  }; +#define SDIOD_DRVSTR_KEY(chip, pmu)     (((chip) << 16) | (pmu)) + +/* SDIO Pad drive strength to select value mappings */ +struct sdiod_drive_str { +	u8 strength;	/* Pad Drive Strength in mA */ +	u8 sel;		/* Chip-specific select value */ +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 11 (1.8V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab1_1v8[] = { +	{32, 0x6}, +	{26, 0x7}, +	{22, 0x4}, +	{16, 0x5}, +	{12, 0x2}, +	{8, 0x3}, +	{4, 0x0}, +	{0, 0x1} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 13 (1.8v) */ +static const struct sdiod_drive_str sdiod_drive_strength_tab5_1v8[] = { +	{6, 0x7}, +	{5, 0x6}, +	{4, 0x5}, +	{3, 0x4}, +	{2, 0x2}, +	{1, 0x1}, +	{0, 0x0} +}; + +/* SDIO Drive Strength to sel value table for PMU Rev 17 (1.8v) */ +static const struct sdiod_drive_str sdiod_drvstr_tab6_1v8[] = { +	{3, 0x3}, +	{2, 0x2}, +	{1, 0x1}, +	{0, 0x0} }; + +/* SDIO Drive Strength to sel value table for 43143 PMU Rev 17 (3.3V) */ +static const struct sdiod_drive_str sdiod_drvstr_tab2_3v3[] = { +	{16, 0x7}, +	{12, 0x5}, +	{8,  0x3}, +	{4,  0x1} +}; + +#define BCM43143_FIRMWARE_NAME		"brcm/brcmfmac43143-sdio.bin" +#define BCM43143_NVRAM_NAME		"brcm/brcmfmac43143-sdio.txt" +#define BCM43241B0_FIRMWARE_NAME	"brcm/brcmfmac43241b0-sdio.bin" +#define BCM43241B0_NVRAM_NAME		"brcm/brcmfmac43241b0-sdio.txt" +#define BCM43241B4_FIRMWARE_NAME	"brcm/brcmfmac43241b4-sdio.bin" +#define BCM43241B4_NVRAM_NAME		"brcm/brcmfmac43241b4-sdio.txt" +#define BCM4329_FIRMWARE_NAME		"brcm/brcmfmac4329-sdio.bin" +#define BCM4329_NVRAM_NAME		"brcm/brcmfmac4329-sdio.txt" +#define BCM4330_FIRMWARE_NAME		"brcm/brcmfmac4330-sdio.bin" +#define BCM4330_NVRAM_NAME		"brcm/brcmfmac4330-sdio.txt" +#define BCM4334_FIRMWARE_NAME		"brcm/brcmfmac4334-sdio.bin" +#define BCM4334_NVRAM_NAME		"brcm/brcmfmac4334-sdio.txt" +#define BCM4335_FIRMWARE_NAME		"brcm/brcmfmac4335-sdio.bin" +#define BCM4335_NVRAM_NAME		"brcm/brcmfmac4335-sdio.txt" +#define BCM43362_FIRMWARE_NAME		"brcm/brcmfmac43362-sdio.bin" +#define BCM43362_NVRAM_NAME		"brcm/brcmfmac43362-sdio.txt" +#define BCM4339_FIRMWARE_NAME		"brcm/brcmfmac4339-sdio.bin" +#define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt" +#define BCM4354_FIRMWARE_NAME		"brcm/brcmfmac4354-sdio.bin" +#define BCM4354_NVRAM_NAME		"brcm/brcmfmac4354-sdio.txt" + +MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43143_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); +MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); +MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4329_NVRAM_NAME); +MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4330_NVRAM_NAME); +MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4334_NVRAM_NAME); +MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4335_NVRAM_NAME); +MODULE_FIRMWARE(BCM43362_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM43362_NVRAM_NAME); +MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4339_NVRAM_NAME); +MODULE_FIRMWARE(BCM4354_FIRMWARE_NAME); +MODULE_FIRMWARE(BCM4354_NVRAM_NAME); + +struct brcmf_firmware_names { +	u32 chipid; +	u32 revmsk; +	const char *bin; +	const char *nv; +}; + +enum brcmf_firmware_type { +	BRCMF_FIRMWARE_BIN, +	BRCMF_FIRMWARE_NVRAM +}; + +#define BRCMF_FIRMWARE_NVRAM(name) \ +	name ## _FIRMWARE_NAME, name ## _NVRAM_NAME + +static const struct brcmf_firmware_names brcmf_fwname_data[] = { +	{ BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, +	{ BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, +	{ BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, +	{ BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, +	{ BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, +	{ BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, +	{ BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, +	{ BCM43362_CHIP_ID, 0xFFFFFFFE, BRCMF_FIRMWARE_NVRAM(BCM43362) }, +	{ BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) }, +	{ BCM4354_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4354) } +}; + +static const char *brcmf_sdio_get_fwname(struct brcmf_chip *ci, +					 enum brcmf_firmware_type type) +{ +	int i; + +	for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { +		if (brcmf_fwname_data[i].chipid == ci->chip && +		    brcmf_fwname_data[i].revmsk & BIT(ci->chiprev)) { +			switch (type) { +			case BRCMF_FIRMWARE_BIN: +				return brcmf_fwname_data[i].bin; +			case BRCMF_FIRMWARE_NVRAM: +				return brcmf_fwname_data[i].nv; +			default: +				brcmf_err("invalid firmware type (%d)\n", type); +				return NULL; +			} +		} +	} +	brcmf_err("Unknown chipid %d [%d]\n", +		  ci->chip, ci->chiprev); +	return NULL; +} +  static void pkt_align(struct sk_buff *p, int len, int align)  {  	uint datalign; @@ -514,48 +677,41 @@ static bool data_ok(struct brcmf_sdio *bus)   * Reads a register in the SDIO hardware block. This block occupies a series of   * adresses on the 32 bit backplane bus.   */ -static int -r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) +static int r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset)  { -	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); +	struct brcmf_core *core;  	int ret; -	*regvar = brcmf_sdio_regrl(bus->sdiodev, -				   bus->ci->c_inf[idx].base + offset, &ret); +	core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); +	*regvar = brcmf_sdiod_regrl(bus->sdiodev, core->base + offset, &ret);  	return ret;  } -static int -w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) +static int w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset)  { -	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); +	struct brcmf_core *core;  	int ret; -	brcmf_sdio_regwl(bus->sdiodev, -			 bus->ci->c_inf[idx].base + reg_offset, -			 regval, &ret); +	core = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); +	brcmf_sdiod_regwl(bus->sdiodev, core->base + reg_offset, regval, &ret);  	return ret;  }  static int -brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) +brcmf_sdio_kso_control(struct brcmf_sdio *bus, bool on)  {  	u8 wr_val = 0, rd_val, cmp_val, bmask;  	int err = 0;  	int try_cnt = 0; -	brcmf_dbg(TRACE, "Enter\n"); +	brcmf_dbg(TRACE, "Enter: on=%d\n", on);  	wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT);  	/* 1st KSO write goes to AOS wake up core if device is asleep  */ -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, -			 wr_val, &err); -	if (err) { -		brcmf_err("SDIO_AOS KSO write error: %d\n", err); -		return err; -	} +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, +			  wr_val, &err);  	if (on) {  		/* device WAKEUP through KSO: @@ -581,26 +737,30 @@ brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on)  		 * just one write attempt may fail,  		 * read it back until it matches written value  		 */ -		rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, -					  &err); +		rd_val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, +					   &err);  		if (((rd_val & bmask) == cmp_val) && !err)  			break; -		brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", -			  try_cnt, MAX_KSO_ATTEMPTS, err); +  		udelay(KSO_WAIT_US); -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, -				 wr_val, &err); +		brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, +				  wr_val, &err);  	} while (try_cnt++ < MAX_KSO_ATTEMPTS); +	if (try_cnt > 2) +		brcmf_dbg(SDIO, "try_cnt=%d rd_val=0x%x err=%d\n", try_cnt, +			  rd_val, err); + +	if (try_cnt > MAX_KSO_ATTEMPTS) +		brcmf_err("max tries: rd_val=0x%x err=%d\n", rd_val, err); +  	return err;  } -#define PKT_AVAILABLE()		(intstatus & I_HMB_FRAME_IND) -  #define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)  /* Turn backplane clock on or off */ -static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) +static int brcmf_sdio_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  {  	int err;  	u8 clkctl, clkreq, devctl; @@ -620,16 +780,16 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  		clkreq =  		    bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -				 clkreq, &err); +		brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +				  clkreq, &err);  		if (err) {  			brcmf_err("HT Avail request error: %d\n", err);  			return -EBADE;  		}  		/* Check current status */ -		clkctl = brcmf_sdio_regrb(bus->sdiodev, -					  SBSDIO_FUNC1_CHIPCLKCSR, &err); +		clkctl = brcmf_sdiod_regrb(bus->sdiodev, +					   SBSDIO_FUNC1_CHIPCLKCSR, &err);  		if (err) {  			brcmf_err("HT Avail read error: %d\n", err);  			return -EBADE; @@ -638,8 +798,8 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  		/* Go to pending and await interrupt if appropriate */  		if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {  			/* Allow only clock-available interrupt */ -			devctl = brcmf_sdio_regrb(bus->sdiodev, -						  SBSDIO_DEVICE_CTL, &err); +			devctl = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_DEVICE_CTL, &err);  			if (err) {  				brcmf_err("Devctl error setting CA: %d\n",  					  err); @@ -647,28 +807,28 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  			}  			devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; -			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, -					 devctl, &err); +			brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, +					  devctl, &err);  			brcmf_dbg(SDIO, "CLKCTL: set PENDING\n");  			bus->clkstate = CLK_PENDING;  			return 0;  		} else if (bus->clkstate == CLK_PENDING) {  			/* Cancel CA-only interrupt filter */ -			devctl = brcmf_sdio_regrb(bus->sdiodev, -						  SBSDIO_DEVICE_CTL, &err); +			devctl = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_DEVICE_CTL, &err);  			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; -			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, -					 devctl, &err); +			brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, +					  devctl, &err);  		}  		/* Otherwise, wait here (polling) for HT Avail */  		timeout = jiffies +  			  msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);  		while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { -			clkctl = brcmf_sdio_regrb(bus->sdiodev, -						  SBSDIO_FUNC1_CHIPCLKCSR, -						  &err); +			clkctl = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_FUNC1_CHIPCLKCSR, +						   &err);  			if (time_after(jiffies, timeout))  				break;  			else @@ -695,22 +855,21 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  		}  #endif				/* defined (DEBUG) */ -		bus->activity = true;  	} else {  		clkreq = 0;  		if (bus->clkstate == CLK_PENDING) {  			/* Cancel CA-only interrupt filter */ -			devctl = brcmf_sdio_regrb(bus->sdiodev, -						  SBSDIO_DEVICE_CTL, &err); +			devctl = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_DEVICE_CTL, &err);  			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; -			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, -					 devctl, &err); +			brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, +					  devctl, &err);  		}  		bus->clkstate = CLK_SDONLY; -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -				 clkreq, &err); +		brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +				  clkreq, &err);  		brcmf_dbg(SDIO, "CLKCTL: turned OFF\n");  		if (err) {  			brcmf_err("Failed access turning clock off: %d\n", @@ -722,7 +881,7 @@ static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)  }  /* Change idle/active SD state */ -static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) +static int brcmf_sdio_sdclk(struct brcmf_sdio *bus, bool on)  {  	brcmf_dbg(SDIO, "Enter\n"); @@ -735,7 +894,7 @@ static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on)  }  /* Transition SD and backplane clock readiness */ -static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) +static int brcmf_sdio_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)  {  #ifdef DEBUG  	uint oldstate = bus->clkstate; @@ -746,7 +905,7 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)  	/* Early exit if we're already there */  	if (bus->clkstate == target) {  		if (target == CLK_AVAIL) { -			brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); +			brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);  			bus->activity = true;  		}  		return 0; @@ -756,32 +915,32 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)  	case CLK_AVAIL:  		/* Make sure SD clock is available */  		if (bus->clkstate == CLK_NONE) -			brcmf_sdbrcm_sdclk(bus, true); +			brcmf_sdio_sdclk(bus, true);  		/* Now request HT Avail on the backplane */ -		brcmf_sdbrcm_htclk(bus, true, pendok); -		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); +		brcmf_sdio_htclk(bus, true, pendok); +		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);  		bus->activity = true;  		break;  	case CLK_SDONLY:  		/* Remove HT request, or bring up SD clock */  		if (bus->clkstate == CLK_NONE) -			brcmf_sdbrcm_sdclk(bus, true); +			brcmf_sdio_sdclk(bus, true);  		else if (bus->clkstate == CLK_AVAIL) -			brcmf_sdbrcm_htclk(bus, false, false); +			brcmf_sdio_htclk(bus, false, false);  		else  			brcmf_err("request for %d -> %d\n",  				  bus->clkstate, target); -		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); +		brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);  		break;  	case CLK_NONE:  		/* Make sure to remove HT request */  		if (bus->clkstate == CLK_AVAIL) -			brcmf_sdbrcm_htclk(bus, false, false); +			brcmf_sdio_htclk(bus, false, false);  		/* Now remove the SD clock */ -		brcmf_sdbrcm_sdclk(bus, false); -		brcmf_sdbrcm_wd_timer(bus, 0); +		brcmf_sdio_sdclk(bus, false); +		brcmf_sdio_wd_timer(bus, 0);  		break;  	}  #ifdef DEBUG @@ -792,11 +951,12 @@ static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)  }  static int -brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) +brcmf_sdio_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)  {  	int err = 0; -	brcmf_dbg(TRACE, "Enter\n"); -	brcmf_dbg(SDIO, "request %s currently %s\n", +	u8 clkcsr; + +	brcmf_dbg(SDIO, "Enter: request %s currently %s\n",  		  (sleep ? "SLEEP" : "WAKE"),  		  (bus->sleeping ? "SLEEP" : "WAKE")); @@ -813,15 +973,27 @@ brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)  			    atomic_read(&bus->ipend) > 0 ||  			    (!atomic_read(&bus->fcstate) &&  			    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && -			    data_ok(bus))) -				 return -EBUSY; -			err = brcmf_sdbrcm_kso_control(bus, false); +			    data_ok(bus))) { +				 err = -EBUSY; +				 goto done; +			} + +			clkcsr = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_FUNC1_CHIPCLKCSR, +						   &err); +			if ((clkcsr & SBSDIO_CSR_MASK) == 0) { +				brcmf_dbg(SDIO, "no clock, set ALP\n"); +				brcmf_sdiod_regwb(bus->sdiodev, +						  SBSDIO_FUNC1_CHIPCLKCSR, +						  SBSDIO_ALP_AVAIL_REQ, &err); +			} +			err = brcmf_sdio_kso_control(bus, false);  			/* disable watchdog */  			if (!err) -				brcmf_sdbrcm_wd_timer(bus, 0); +				brcmf_sdio_wd_timer(bus, 0);  		} else {  			bus->idlecount = 0; -			err = brcmf_sdbrcm_kso_control(bus, true); +			err = brcmf_sdio_kso_control(bus, true);  		}  		if (!err) {  			/* Change state */ @@ -831,7 +1003,7 @@ brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok)  		} else {  			brcmf_err("error while changing bus sleep state %d\n",  				  err); -			return err; +			goto done;  		}  	} @@ -839,16 +1011,97 @@ end:  	/* control clocks */  	if (sleep) {  		if (!bus->sr_enabled) -			brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok); +			brcmf_sdio_clkctl(bus, CLK_NONE, pendok);  	} else { -		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok); +		brcmf_sdio_clkctl(bus, CLK_AVAIL, pendok);  	} - +done: +	brcmf_dbg(SDIO, "Exit: err=%d\n", err);  	return err;  } -static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) +#ifdef DEBUG +static inline bool brcmf_sdio_valid_shared_address(u32 addr) +{ +	return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); +} + +static int brcmf_sdio_readshared(struct brcmf_sdio *bus, +				 struct sdpcm_shared *sh) +{ +	u32 addr; +	int rv; +	u32 shaddr = 0; +	struct sdpcm_shared_le sh_le; +	__le32 addr_le; + +	shaddr = bus->ci->rambase + bus->ramsize - 4; + +	/* +	 * Read last word in socram to determine +	 * address of sdpcm_shared structure +	 */ +	sdio_claim_host(bus->sdiodev->func[1]); +	brcmf_sdio_bus_sleep(bus, false, false); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); +	sdio_release_host(bus->sdiodev->func[1]); +	if (rv < 0) +		return rv; + +	addr = le32_to_cpu(addr_le); + +	brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); + +	/* +	 * Check if addr is valid. +	 * NVRAM length at the end of memory should have been overwritten. +	 */ +	if (!brcmf_sdio_valid_shared_address(addr)) { +			brcmf_err("invalid sdpcm_shared address 0x%08X\n", +				  addr); +			return -EINVAL; +	} + +	/* Read hndrte_shared structure */ +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, +			       sizeof(struct sdpcm_shared_le)); +	if (rv < 0) +		return rv; + +	/* Endianness */ +	sh->flags = le32_to_cpu(sh_le.flags); +	sh->trap_addr = le32_to_cpu(sh_le.trap_addr); +	sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); +	sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); +	sh->assert_line = le32_to_cpu(sh_le.assert_line); +	sh->console_addr = le32_to_cpu(sh_le.console_addr); +	sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); + +	if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { +		brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", +			  SDPCM_SHARED_VERSION, +			  sh->flags & SDPCM_SHARED_VERSION_MASK); +		return -EPROTO; +	} + +	return 0; +} + +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +	struct sdpcm_shared sh; + +	if (brcmf_sdio_readshared(bus, &sh) == 0) +		bus->console_addr = sh.console_addr; +} +#else +static void brcmf_sdio_get_console_addr(struct brcmf_sdio *bus) +{ +} +#endif /* DEBUG */ + +static u32 brcmf_sdio_hostmail(struct brcmf_sdio *bus)  {  	u32 intstatus = 0;  	u32 hmb_data; @@ -891,6 +1144,12 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)  		else  			brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n",  				  bus->sdpcm_ver); + +		/* +		 * Retrieve console state address now that firmware should have +		 * updated it. +		 */ +		brcmf_sdio_get_console_addr(bus);  	}  	/* @@ -924,7 +1183,7 @@ static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)  	return intstatus;  } -static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) +static void brcmf_sdio_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)  {  	uint retries = 0;  	u16 lastrbc; @@ -936,18 +1195,18 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)  		  rtx ? ", send NAK" : "");  	if (abort) -		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); +		brcmf_sdiod_abort(bus->sdiodev, SDIO_FUNC_2); -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, -			 SFC_RF_TERM, &err); +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, +			  SFC_RF_TERM, &err);  	bus->sdcnt.f1regdata++;  	/* Wait until the packet has been flushed (device/FIFO stable) */  	for (lastrbc = retries = 0xffff; retries > 0; retries--) { -		hi = brcmf_sdio_regrb(bus->sdiodev, -				      SBSDIO_FUNC1_RFRAMEBCHI, &err); -		lo = brcmf_sdio_regrb(bus->sdiodev, -				      SBSDIO_FUNC1_RFRAMEBCLO, &err); +		hi = brcmf_sdiod_regrb(bus->sdiodev, +				       SBSDIO_FUNC1_RFRAMEBCHI, &err); +		lo = brcmf_sdiod_regrb(bus->sdiodev, +				       SBSDIO_FUNC1_RFRAMEBCLO, &err);  		bus->sdcnt.f1regdata += 2;  		if ((hi == 0) && (lo == 0)) @@ -977,14 +1236,32 @@ static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)  	/* Clear partial in any case */  	bus->cur_read.len = 0; +} -	/* If we can't reach the device, signal failure */ -	if (err) -		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; +static void brcmf_sdio_txfail(struct brcmf_sdio *bus) +{ +	struct brcmf_sdio_dev *sdiodev = bus->sdiodev; +	u8 i, hi, lo; + +	/* On failure, abort the command and terminate the frame */ +	brcmf_err("sdio error, abort command and terminate frame\n"); +	bus->sdcnt.tx_sderrs++; + +	brcmf_sdiod_abort(sdiodev, SDIO_FUNC_2); +	brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM, NULL); +	bus->sdcnt.f1regdata++; + +	for (i = 0; i < 3; i++) { +		hi = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCHI, NULL); +		lo = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_WFRAMEBCLO, NULL); +		bus->sdcnt.f1regdata += 2; +		if ((hi == 0) && (lo == 0)) +			break; +	}  }  /* return total length of buffer chain */ -static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) +static uint brcmf_sdio_glom_len(struct brcmf_sdio *bus)  {  	struct sk_buff *p;  	uint total; @@ -995,7 +1272,7 @@ static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)  	return total;  } -static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) +static void brcmf_sdio_free_glom(struct brcmf_sdio *bus)  {  	struct sk_buff *cur, *next; @@ -1011,10 +1288,18 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)   * host and WiFi dongle which contains information needed for SDIO core and   * firmware   * - * It consists of 2 parts: hw header and software header + * It consists of 3 parts: hardware header, hardware extension header and + * software header   * hardware header (frame tag) - 4 bytes   * Byte 0~1: Frame length   * Byte 2~3: Checksum, bit-wise inverse of frame length + * hardware extension header - 8 bytes + * Tx glom mode only, N/A for Rx or normal Tx + * Byte 0~1: Packet length excluding hw frame tag + * Byte 2: Reserved + * Byte 3: Frame flags, bit 0: last frame indication + * Byte 4~5: Reserved + * Byte 6~7: Tail padding length   * software header - 8 bytes   * Byte 0: Rx/Tx sequence number   * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag @@ -1025,6 +1310,7 @@ static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)   * Byte 6~7: Reserved   */  #define SDPCM_HWHDR_LEN			4 +#define SDPCM_HWEXT_LEN			8  #define SDPCM_SWHDR_LEN			8  #define SDPCM_HDRLEN			(SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)  /* software header */ @@ -1061,6 +1347,8 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,  	u8 rx_seq, fc, tx_seq_max;  	u32 swheader; +	trace_brcmf_sdpcm_hdr(SDPCM_RX, header); +  	/* hw header */  	len = get_unaligned_le16(header);  	checksum = get_unaligned_le16(header + sizeof(u16)); @@ -1072,7 +1360,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,  	if ((u16)(~(len ^ checksum))) {  		brcmf_err("HW header checksum error\n");  		bus->sdcnt.rx_badhdr++; -		brcmf_sdbrcm_rxfail(bus, false, false); +		brcmf_sdio_rxfail(bus, false, false);  		return -EIO;  	}  	if (len < SDPCM_HDRLEN) { @@ -1104,7 +1392,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,  	    type != BRCMF_SDIO_FT_SUPER) {  		brcmf_err("HW header length too long\n");  		bus->sdcnt.rx_toolong++; -		brcmf_sdbrcm_rxfail(bus, false, false); +		brcmf_sdio_rxfail(bus, false, false);  		rd->len = 0;  		return -EPROTO;  	} @@ -1123,7 +1411,7 @@ static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header,  	if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) {  		brcmf_err("seq %d: bad data offset\n", rx_seq);  		bus->sdcnt.rx_badhdr++; -		brcmf_sdbrcm_rxfail(bus, false, false); +		brcmf_sdio_rxfail(bus, false, false);  		rd->len = 0;  		return -ENXIO;  	} @@ -1172,24 +1460,34 @@ static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length)  static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header,  			      struct brcmf_sdio_hdrinfo *hd_info)  { -	u32 sw_header; +	u32 hdrval; +	u8 hdr_offset;  	brcmf_sdio_update_hwhdr(header, hd_info->len); - -	sw_header = bus->tx_seq; -	sw_header |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & -		     SDPCM_CHANNEL_MASK; -	sw_header |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & -		     SDPCM_DOFFSET_MASK; -	*(((__le32 *)header) + 1) = cpu_to_le32(sw_header); -	*(((__le32 *)header) + 2) = 0; +	hdr_offset = SDPCM_HWHDR_LEN; + +	if (bus->txglom) { +		hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); +		*((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); +		hdrval = (u16)hd_info->tail_pad << 16; +		*(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); +		hdr_offset += SDPCM_HWEXT_LEN; +	} + +	hdrval = hd_info->seq_num; +	hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & +		  SDPCM_CHANNEL_MASK; +	hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & +		  SDPCM_DOFFSET_MASK; +	*((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); +	*(((__le32 *)(header + hdr_offset)) + 1) = 0; +	trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header);  } -static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) +static u8 brcmf_sdio_rxglom(struct brcmf_sdio *bus, u8 rxseq)  {  	u16 dlen, totlen;  	u8 *dptr, num = 0; -	u32 align = 0;  	u16 sublen;  	struct sk_buff *pfirst, *pnext; @@ -1204,11 +1502,6 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  	brcmf_dbg(SDIO, "start: glomd %p glom %p\n",  		  bus->glomd, skb_peek(&bus->glom)); -	if (bus->sdiodev->pdata) -		align = bus->sdiodev->pdata->sd_sgentry_align; -	if (align < 4) -		align = 4; -  	/* If there's a descriptor, generate the packet chain */  	if (bus->glomd) {  		pfirst = pnext = NULL; @@ -1232,9 +1525,9 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  				pnext = NULL;  				break;  			} -			if (sublen % align) { +			if (sublen % bus->sgentry_align) {  				brcmf_err("sublen %d not multiple of %d\n", -					  sublen, align); +					  sublen, bus->sgentry_align);  			}  			totlen += sublen; @@ -1247,7 +1540,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  			}  			/* Allocate/chain packet for next subframe */ -			pnext = brcmu_pkt_buf_get_skb(sublen + align); +			pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align);  			if (pnext == NULL) {  				brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n",  					  num, sublen); @@ -1256,7 +1549,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  			skb_queue_tail(&bus->glom, pnext);  			/* Adhere to start alignment requirements */ -			pkt_align(pnext, sublen, align); +			pkt_align(pnext, sublen, bus->sgentry_align);  		}  		/* If all allocations succeeded, save packet chain @@ -1271,7 +1564,7 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  			}  			pfirst = pnext = NULL;  		} else { -			brcmf_sdbrcm_free_glom(bus); +			brcmf_sdio_free_glom(bus);  			num = 0;  		} @@ -1294,16 +1587,15 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  		}  		pfirst = skb_peek(&bus->glom); -		dlen = (u16) brcmf_sdbrcm_glom_len(bus); +		dlen = (u16) brcmf_sdio_glom_len(bus);  		/* Do an SDIO read for the superframe.  Configurable iovar to  		 * read directly into the chained packet, or allocate a large  		 * packet and and copy into the chain.  		 */  		sdio_claim_host(bus->sdiodev->func[1]); -		errcode = brcmf_sdcard_recv_chain(bus->sdiodev, -				bus->sdiodev->sbwad, -				SDIO_FUNC_2, F2SYNC, &bus->glom); +		errcode = brcmf_sdiod_recv_chain(bus->sdiodev, +						 &bus->glom, dlen);  		sdio_release_host(bus->sdiodev->func[1]);  		bus->sdcnt.f2rxdata++; @@ -1314,12 +1606,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  			sdio_claim_host(bus->sdiodev->func[1]);  			if (bus->glomerr++ < 3) { -				brcmf_sdbrcm_rxfail(bus, true, true); +				brcmf_sdio_rxfail(bus, true, true);  			} else {  				bus->glomerr = 0; -				brcmf_sdbrcm_rxfail(bus, true, false); +				brcmf_sdio_rxfail(bus, true, false);  				bus->sdcnt.rxglomfail++; -				brcmf_sdbrcm_free_glom(bus); +				brcmf_sdio_free_glom(bus);  			}  			sdio_release_host(bus->sdiodev->func[1]);  			return 0; @@ -1367,12 +1659,12 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  			if (bus->glomerr++ < 3) {  				/* Restore superframe header space */  				skb_push(pfirst, sfdoff); -				brcmf_sdbrcm_rxfail(bus, true, true); +				brcmf_sdio_rxfail(bus, true, true);  			} else {  				bus->glomerr = 0; -				brcmf_sdbrcm_rxfail(bus, true, false); +				brcmf_sdio_rxfail(bus, true, false);  				bus->sdcnt.rxglomfail++; -				brcmf_sdbrcm_free_glom(bus); +				brcmf_sdio_free_glom(bus);  			}  			sdio_release_host(bus->sdiodev->func[1]);  			bus->cur_read.len = 0; @@ -1406,19 +1698,18 @@ static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)  					   bus->glom.qlen, pfirst, pfirst->data,  					   pfirst->len, pfirst->next,  					   pfirst->prev); +			skb_unlink(pfirst, &bus->glom); +			brcmf_rx_frame(bus->sdiodev->dev, pfirst); +			bus->sdcnt.rxglompkts++;  		} -		/* sent any remaining packets up */ -		if (bus->glom.qlen) -			brcmf_rx_frames(bus->sdiodev->dev, &bus->glom);  		bus->sdcnt.rxglomframes++; -		bus->sdcnt.rxglompkts += bus->glom.qlen;  	}  	return num;  } -static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, -					bool *pending) +static int brcmf_sdio_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, +				     bool *pending)  {  	DECLARE_WAITQUEUE(wait, current);  	int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); @@ -1439,7 +1730,7 @@ static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,  	return timeout;  } -static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) +static int brcmf_sdio_dcmd_resp_wake(struct brcmf_sdio *bus)  {  	if (waitqueue_active(&bus->dcmd_resp_wait))  		wake_up_interruptible(&bus->dcmd_resp_wait); @@ -1447,7 +1738,7 @@ static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus)  	return 0;  }  static void -brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) +brcmf_sdio_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)  {  	uint rdlen, pad;  	u8 *buf = NULL, *rbuf; @@ -1461,9 +1752,9 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)  		goto done;  	rbuf = bus->rxbuf; -	pad = ((unsigned long)rbuf % BRCMF_SDALIGN); +	pad = ((unsigned long)rbuf % bus->head_align);  	if (pad) -		rbuf += (BRCMF_SDALIGN - pad); +		rbuf += (bus->head_align - pad);  	/* Copy the already-read portion over */  	memcpy(buf, hdr, BRCMF_FIRSTREAD); @@ -1477,19 +1768,15 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)  		if ((pad <= bus->roundup) && (pad < bus->blocksize) &&  		    ((len + pad) < bus->sdiodev->bus_if->maxctl))  			rdlen += pad; -	} else if (rdlen % BRCMF_SDALIGN) { -		rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN); +	} else if (rdlen % bus->head_align) { +		rdlen += bus->head_align - (rdlen % bus->head_align);  	} -	/* Satisfy length-alignment requirements */ -	if (rdlen & (ALIGNMENT - 1)) -		rdlen = roundup(rdlen, ALIGNMENT); -  	/* Drop if the read is too big or it exceeds our maximum */  	if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {  		brcmf_err("%d-byte control read exceeds %d-byte buffer\n",  			  rdlen, bus->sdiodev->bus_if->maxctl); -		brcmf_sdbrcm_rxfail(bus, false, false); +		brcmf_sdio_rxfail(bus, false, false);  		goto done;  	} @@ -1497,15 +1784,12 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)  		brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",  			  len, len - doff, bus->sdiodev->bus_if->maxctl);  		bus->sdcnt.rx_toolong++; -		brcmf_sdbrcm_rxfail(bus, false, false); +		brcmf_sdio_rxfail(bus, false, false);  		goto done;  	}  	/* Read remain of frame body */ -	sdret = brcmf_sdcard_recv_buf(bus->sdiodev, -				bus->sdiodev->sbwad, -				SDIO_FUNC_2, -				F2SYNC, rbuf, rdlen); +	sdret = brcmf_sdiod_recv_buf(bus->sdiodev, rbuf, rdlen);  	bus->sdcnt.f2rxdata++;  	/* Control frame failures need retransmission */ @@ -1513,7 +1797,7 @@ brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)  		brcmf_err("read %d control bytes failed: %d\n",  			  rdlen, sdret);  		bus->sdcnt.rxc_errors++; -		brcmf_sdbrcm_rxfail(bus, true, true); +		brcmf_sdio_rxfail(bus, true, true);  		goto done;  	} else  		memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); @@ -1538,26 +1822,25 @@ gotpkt:  done:  	/* Awake any waiters */ -	brcmf_sdbrcm_dcmd_resp_wake(bus); +	brcmf_sdio_dcmd_resp_wake(bus);  }  /* Pad read to blocksize for efficiency */ -static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) +static void brcmf_sdio_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)  {  	if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {  		*pad = bus->blocksize - (*rdlen % bus->blocksize);  		if (*pad <= bus->roundup && *pad < bus->blocksize &&  		    *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)  			*rdlen += *pad; -	} else if (*rdlen % BRCMF_SDALIGN) { -		*rdlen += BRCMF_SDALIGN - (*rdlen % BRCMF_SDALIGN); +	} else if (*rdlen % bus->head_align) { +		*rdlen += bus->head_align - (*rdlen % bus->head_align);  	}  }  static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  {  	struct sk_buff *pkt;		/* Packet for event or data frames */ -	struct sk_buff_head pktlist;	/* needed for bus interface */  	u16 pad;		/* Number of pad bytes to read */  	uint rxleft = 0;	/* Remaining number of frames allowed */  	int ret;		/* Return code from calls */ @@ -1571,8 +1854,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  	bus->rxpending = true;  	for (rd->seq_num = bus->rx_seq, rxleft = maxframes; -	     !bus->rxskip && rxleft && -	     bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; +	     !bus->rxskip && rxleft && brcmf_bus_ready(bus->sdiodev->bus_if);  	     rd->seq_num++, rxleft--) {  		/* Handle glomming separately */ @@ -1580,7 +1862,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  			u8 cnt;  			brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",  				  bus->glomd, skb_peek(&bus->glom)); -			cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num); +			cnt = brcmf_sdio_rxglom(bus, rd->seq_num);  			brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);  			rd->seq_num += cnt - 1;  			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; @@ -1591,17 +1873,14 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  		/* read header first for unknow frame length */  		sdio_claim_host(bus->sdiodev->func[1]);  		if (!rd->len) { -			ret = brcmf_sdcard_recv_buf(bus->sdiodev, -						      bus->sdiodev->sbwad, -						      SDIO_FUNC_2, F2SYNC, -						      bus->rxhdr, -						      BRCMF_FIRSTREAD); +			ret = brcmf_sdiod_recv_buf(bus->sdiodev, +						   bus->rxhdr, BRCMF_FIRSTREAD);  			bus->sdcnt.f2rxhdrs++;  			if (ret < 0) {  				brcmf_err("RXHEADER FAILED: %d\n",  					  ret);  				bus->sdcnt.rx_hdrfail++; -				brcmf_sdbrcm_rxfail(bus, true, true); +				brcmf_sdio_rxfail(bus, true, true);  				sdio_release_host(bus->sdiodev->func[1]);  				continue;  			} @@ -1620,9 +1899,9 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  			}  			if (rd->channel == SDPCM_CONTROL_CHANNEL) { -				brcmf_sdbrcm_read_control(bus, bus->rxhdr, -							  rd->len, -							  rd->dat_offset); +				brcmf_sdio_read_control(bus, bus->rxhdr, +							rd->len, +							rd->dat_offset);  				/* prepare the descriptor for the next read */  				rd->len = rd->len_nxtfrm << 4;  				rd->len_nxtfrm = 0; @@ -1636,23 +1915,22 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  			head_read = BRCMF_FIRSTREAD;  		} -		brcmf_pad(bus, &pad, &rd->len_left); +		brcmf_sdio_pad(bus, &pad, &rd->len_left);  		pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + -					    BRCMF_SDALIGN); +					    bus->head_align);  		if (!pkt) {  			/* Give up on data, request rtx of events */  			brcmf_err("brcmu_pkt_buf_get_skb failed\n"); -			brcmf_sdbrcm_rxfail(bus, false, +			brcmf_sdio_rxfail(bus, false,  					    RETRYCHAN(rd->channel));  			sdio_release_host(bus->sdiodev->func[1]);  			continue;  		}  		skb_pull(pkt, head_read); -		pkt_align(pkt, rd->len_left, BRCMF_SDALIGN); +		pkt_align(pkt, rd->len_left, bus->head_align); -		ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, -					      SDIO_FUNC_2, F2SYNC, pkt); +		ret = brcmf_sdiod_recv_pkt(bus->sdiodev, pkt);  		bus->sdcnt.f2rxdata++;  		sdio_release_host(bus->sdiodev->func[1]); @@ -1661,7 +1939,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  				  rd->len, rd->channel, ret);  			brcmu_pkt_buf_free_skb(pkt);  			sdio_claim_host(bus->sdiodev->func[1]); -			brcmf_sdbrcm_rxfail(bus, true, +			brcmf_sdio_rxfail(bus, true,  					    RETRYCHAN(rd->channel));  			sdio_release_host(bus->sdiodev->func[1]);  			continue; @@ -1686,7 +1964,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  					  rd->len,  					  roundup(rd_new.len, 16) >> 4);  				rd->len = 0; -				brcmf_sdbrcm_rxfail(bus, true, true); +				brcmf_sdio_rxfail(bus, true, true);  				sdio_release_host(bus->sdiodev->func[1]);  				brcmu_pkt_buf_free_skb(pkt);  				continue; @@ -1708,7 +1986,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  				/* Force retry w/normal header read */  				rd->len = 0;  				sdio_claim_host(bus->sdiodev->func[1]); -				brcmf_sdbrcm_rxfail(bus, false, true); +				brcmf_sdio_rxfail(bus, false, true);  				sdio_release_host(bus->sdiodev->func[1]);  				brcmu_pkt_buf_free_skb(pkt);  				continue; @@ -1733,7 +2011,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  				brcmf_err("%s: glom superframe w/o "  					  "descriptor!\n", __func__);  				sdio_claim_host(bus->sdiodev->func[1]); -				brcmf_sdbrcm_rxfail(bus, false, false); +				brcmf_sdio_rxfail(bus, false, false);  				sdio_release_host(bus->sdiodev->func[1]);  			}  			/* prepare the descriptor for the next read */ @@ -1759,9 +2037,7 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  			continue;  		} -		skb_queue_head_init(&pktlist); -		skb_queue_tail(&pktlist, pkt); -		brcmf_rx_frames(bus->sdiodev->dev, &pktlist); +		brcmf_rx_frame(bus->sdiodev->dev, pkt);  	}  	rxcount = maxframes - rxleft; @@ -1779,17 +2055,101 @@ static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes)  }  static void -brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) +brcmf_sdio_wait_event_wakeup(struct brcmf_sdio *bus)  {  	if (waitqueue_active(&bus->ctrl_wait))  		wake_up_interruptible(&bus->ctrl_wait);  	return;  } +static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) +{ +	u16 head_pad; +	u8 *dat_buf; + +	dat_buf = (u8 *)(pkt->data); + +	/* Check head padding */ +	head_pad = ((unsigned long)dat_buf % bus->head_align); +	if (head_pad) { +		if (skb_headroom(pkt) < head_pad) { +			bus->sdiodev->bus_if->tx_realloc++; +			head_pad = 0; +			if (skb_cow(pkt, head_pad)) +				return -ENOMEM; +		} +		skb_push(pkt, head_pad); +		dat_buf = (u8 *)(pkt->data); +		memset(dat_buf, 0, head_pad + bus->tx_hdrlen); +	} +	return head_pad; +} + +/** + * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for + * bus layer usage. + */  /* flag marking a dummy skb added for DMA alignment requirement */ -#define DUMMY_SKB_FLAG		0x10000 +#define ALIGN_SKB_FLAG		0x8000  /* bit mask of data length chopped from the previous packet */ -#define DUMMY_SKB_CHOP_LEN_MASK	0xffff +#define ALIGN_SKB_CHOP_LEN_MASK	0x7fff + +static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, +				    struct sk_buff_head *pktq, +				    struct sk_buff *pkt, u16 total_len) +{ +	struct brcmf_sdio_dev *sdiodev; +	struct sk_buff *pkt_pad; +	u16 tail_pad, tail_chop, chain_pad; +	unsigned int blksize; +	bool lastfrm; +	int ntail, ret; + +	sdiodev = bus->sdiodev; +	blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; +	/* sg entry alignment should be a divisor of block size */ +	WARN_ON(blksize % bus->sgentry_align); + +	/* Check tail padding */ +	lastfrm = skb_queue_is_last(pktq, pkt); +	tail_pad = 0; +	tail_chop = pkt->len % bus->sgentry_align; +	if (tail_chop) +		tail_pad = bus->sgentry_align - tail_chop; +	chain_pad = (total_len + tail_pad) % blksize; +	if (lastfrm && chain_pad) +		tail_pad += blksize - chain_pad; +	if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { +		pkt_pad = brcmu_pkt_buf_get_skb(tail_pad + tail_chop + +						bus->head_align); +		if (pkt_pad == NULL) +			return -ENOMEM; +		ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); +		if (unlikely(ret < 0)) { +			kfree_skb(pkt_pad); +			return ret; +		} +		memcpy(pkt_pad->data, +		       pkt->data + pkt->len - tail_chop, +		       tail_chop); +		*(u16 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; +		skb_trim(pkt, pkt->len - tail_chop); +		skb_trim(pkt_pad, tail_pad + tail_chop); +		__skb_queue_after(pktq, pkt, pkt_pad); +	} else { +		ntail = pkt->data_len + tail_pad - +			(pkt->end - pkt->tail); +		if (skb_cloned(pkt) || ntail > 0) +			if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) +				return -ENOMEM; +		if (skb_linearize(pkt)) +			return -ENOMEM; +		__skb_put(pkt, tail_pad); +	} + +	return tail_pad; +} +  /**   * brcmf_sdio_txpkt_prep - packet preparation for transmit   * @bus: brcmf_sdio structure pointer @@ -1806,86 +2166,66 @@ static int  brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq,  		      uint chan)  { -	u16 head_pad, tail_pad, tail_chop, head_align, sg_align; -	int ntail; -	struct sk_buff *pkt_next, *pkt_new; -	u8 *dat_buf; -	unsigned blksize = bus->sdiodev->func[SDIO_FUNC_2]->cur_blksize; +	u16 head_pad, total_len; +	struct sk_buff *pkt_next; +	u8 txseq; +	int ret;  	struct brcmf_sdio_hdrinfo hd_info = {0}; -	/* SDIO ADMA requires at least 32 bit alignment */ -	head_align = 4; -	sg_align = 4; -	if (bus->sdiodev->pdata) { -		head_align = bus->sdiodev->pdata->sd_head_align > 4 ? -			     bus->sdiodev->pdata->sd_head_align : 4; -		sg_align = bus->sdiodev->pdata->sd_sgentry_align > 4 ? -			   bus->sdiodev->pdata->sd_sgentry_align : 4; -	} -	/* sg entry alignment should be a divisor of block size */ -	WARN_ON(blksize % sg_align); - -	pkt_next = pktq->next; -	dat_buf = (u8 *)(pkt_next->data); +	txseq = bus->tx_seq; +	total_len = 0; +	skb_queue_walk(pktq, pkt_next) { +		/* alignment packet inserted in previous +		 * loop cycle can be skipped as it is +		 * already properly aligned and does not +		 * need an sdpcm header. +		 */ +		if (*(u16 *)(pkt_next->cb) & ALIGN_SKB_FLAG) +			continue; -	/* Check head padding */ -	head_pad = ((unsigned long)dat_buf % head_align); -	if (head_pad) { -		if (skb_headroom(pkt_next) < head_pad) { -			bus->sdiodev->bus_if->tx_realloc++; -			head_pad = 0; -			if (skb_cow(pkt_next, head_pad)) -				return -ENOMEM; +		/* align packet data pointer */ +		ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); +		if (ret < 0) +			return ret; +		head_pad = (u16)ret; +		if (head_pad) +			memset(pkt_next->data + bus->tx_hdrlen, 0, head_pad); + +		total_len += pkt_next->len; + +		hd_info.len = pkt_next->len; +		hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); +		if (bus->txglom && pktq->qlen > 1) { +			ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, +						       pkt_next, total_len); +			if (ret < 0) +				return ret; +			hd_info.tail_pad = (u16)ret; +			total_len += (u16)ret;  		} -		skb_push(pkt_next, head_pad); -		dat_buf = (u8 *)(pkt_next->data); -		memset(dat_buf, 0, head_pad + bus->tx_hdrlen); -	} - -	/* Check tail padding */ -	pkt_new = NULL; -	tail_chop = pkt_next->len % sg_align; -	tail_pad = sg_align - tail_chop; -	tail_pad += blksize - (pkt_next->len + tail_pad) % blksize; -	if (skb_tailroom(pkt_next) < tail_pad && pkt_next->len > blksize) { -		pkt_new = brcmu_pkt_buf_get_skb(tail_pad + tail_chop); -		if (pkt_new == NULL) -			return -ENOMEM; -		memcpy(pkt_new->data, -		       pkt_next->data + pkt_next->len - tail_chop, -		       tail_chop); -		*(u32 *)(pkt_new->cb) = DUMMY_SKB_FLAG + tail_chop; -		skb_trim(pkt_next, pkt_next->len - tail_chop); -		__skb_queue_after(pktq, pkt_next, pkt_new); -	} else { -		ntail = pkt_next->data_len + tail_pad - -			(pkt_next->end - pkt_next->tail); -		if (skb_cloned(pkt_next) || ntail > 0) -			if (pskb_expand_head(pkt_next, 0, ntail, GFP_ATOMIC)) -				return -ENOMEM; -		if (skb_linearize(pkt_next)) -			return -ENOMEM; -		dat_buf = (u8 *)(pkt_next->data); -		__skb_put(pkt_next, tail_pad); -	} - -	/* Now prep the header */ -	if (pkt_new) -		hd_info.len = pkt_next->len + tail_chop; -	else -		hd_info.len = pkt_next->len - tail_pad; -	hd_info.channel = chan; -	hd_info.dat_offset = head_pad + bus->tx_hdrlen; -	brcmf_sdio_hdpack(bus, dat_buf, &hd_info); - -	if (BRCMF_BYTES_ON() && -	    ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || -	     (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) -		brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, "Tx Frame:\n"); -	else if (BRCMF_HDRS_ON()) -		brcmf_dbg_hex_dump(true, pkt_next, head_pad + bus->tx_hdrlen, -				   "Tx Header:\n"); +		hd_info.channel = chan; +		hd_info.dat_offset = head_pad + bus->tx_hdrlen; +		hd_info.seq_num = txseq++; + +		/* Now fill the header */ +		brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); + +		if (BRCMF_BYTES_ON() && +		    ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || +		     (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) +			brcmf_dbg_hex_dump(true, pkt_next->data, hd_info.len, +					   "Tx Frame:\n"); +		else if (BRCMF_HDRS_ON()) +			brcmf_dbg_hex_dump(true, pkt_next->data, +					   head_pad + bus->tx_hdrlen, +					   "Tx Header:\n"); +	} +	/* Hardware length tag of the first packet should be total +	 * length of the chain (including padding) +	 */ +	if (bus->txglom) +		brcmf_sdio_update_hwhdr(pktq->next->data, total_len);  	return 0;  } @@ -1903,113 +2243,113 @@ brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq)  {  	u8 *hdr;  	u32 dat_offset; -	u32 dummy_flags, chop_len; +	u16 tail_pad; +	u16 dummy_flags, chop_len;  	struct sk_buff *pkt_next, *tmp, *pkt_prev;  	skb_queue_walk_safe(pktq, pkt_next, tmp) { -		dummy_flags = *(u32 *)(pkt_next->cb); -		if (dummy_flags & DUMMY_SKB_FLAG) { -			chop_len = dummy_flags & DUMMY_SKB_CHOP_LEN_MASK; +		dummy_flags = *(u16 *)(pkt_next->cb); +		if (dummy_flags & ALIGN_SKB_FLAG) { +			chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK;  			if (chop_len) {  				pkt_prev = pkt_next->prev; -				memcpy(pkt_prev->data + pkt_prev->len, -				       pkt_next->data, chop_len);  				skb_put(pkt_prev, chop_len);  			}  			__skb_unlink(pkt_next, pktq);  			brcmu_pkt_buf_free_skb(pkt_next);  		} else { -			hdr = pkt_next->data + SDPCM_HWHDR_LEN; +			hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN;  			dat_offset = le32_to_cpu(*(__le32 *)hdr);  			dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >>  				     SDPCM_DOFFSET_SHIFT;  			skb_pull(pkt_next, dat_offset); +			if (bus->txglom) { +				tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); +				skb_trim(pkt_next, pkt_next->len - tail_pad); +			}  		}  	}  }  /* Writes a HW/SW header into the packet and sends it. */  /* Assumes: (a) header space already there, (b) caller holds lock */ -static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt, -			      uint chan) +static int brcmf_sdio_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, +			    uint chan)  {  	int ret; -	int i; -	struct sk_buff_head localq; +	struct sk_buff *pkt_next, *tmp;  	brcmf_dbg(TRACE, "Enter\n"); -	__skb_queue_head_init(&localq); -	__skb_queue_tail(&localq, pkt); -	ret = brcmf_sdio_txpkt_prep(bus, &localq, chan); +	ret = brcmf_sdio_txpkt_prep(bus, pktq, chan);  	if (ret)  		goto done;  	sdio_claim_host(bus->sdiodev->func[1]); -	ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, -				    SDIO_FUNC_2, F2SYNC, &localq); +	ret = brcmf_sdiod_send_pkt(bus->sdiodev, pktq);  	bus->sdcnt.f2txdata++; -	if (ret < 0) { -		/* On failure, abort the command and terminate the frame */ -		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", -			  ret); -		bus->sdcnt.tx_sderrs++; +	if (ret < 0) +		brcmf_sdio_txfail(bus); -		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, -				 SFC_WF_TERM, NULL); -		bus->sdcnt.f1regdata++; - -		for (i = 0; i < 3; i++) { -			u8 hi, lo; -			hi = brcmf_sdio_regrb(bus->sdiodev, -					      SBSDIO_FUNC1_WFRAMEBCHI, NULL); -			lo = brcmf_sdio_regrb(bus->sdiodev, -					      SBSDIO_FUNC1_WFRAMEBCLO, NULL); -			bus->sdcnt.f1regdata += 2; -			if ((hi == 0) && (lo == 0)) -				break; -		} - -	}  	sdio_release_host(bus->sdiodev->func[1]); -	if (ret == 0) -		bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP;  done: -	brcmf_sdio_txpkt_postp(bus, &localq); -	__skb_dequeue_tail(&localq); -	brcmf_txcomplete(bus->sdiodev->dev, pkt, ret == 0); +	brcmf_sdio_txpkt_postp(bus, pktq); +	if (ret == 0) +		bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; +	skb_queue_walk_safe(pktq, pkt_next, tmp) { +		__skb_unlink(pkt_next, pktq); +		brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); +	}  	return ret;  } -static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) +static uint brcmf_sdio_sendfromq(struct brcmf_sdio *bus, uint maxframes)  {  	struct sk_buff *pkt; +	struct sk_buff_head pktq;  	u32 intstatus = 0; -	int ret = 0, prec_out; +	int ret = 0, prec_out, i;  	uint cnt = 0; -	u8 tx_prec_map; +	u8 tx_prec_map, pkt_num;  	brcmf_dbg(TRACE, "Enter\n");  	tx_prec_map = ~bus->flowcontrol;  	/* Send frames until the limit or some other event */ -	for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) { -		spin_lock_bh(&bus->txqlock); -		pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out); -		if (pkt == NULL) { -			spin_unlock_bh(&bus->txqlock); +	for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { +		pkt_num = 1; +		if (down_interruptible(&bus->tx_seq_lock)) +			return cnt; +		if (bus->txglom) +			pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, +					bus->sdiodev->txglomsz); +		pkt_num = min_t(u32, pkt_num, +				brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); +		__skb_queue_head_init(&pktq); +		spin_lock_bh(&bus->txq_lock); +		for (i = 0; i < pkt_num; i++) { +			pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, +					      &prec_out); +			if (pkt == NULL) +				break; +			__skb_queue_tail(&pktq, pkt); +		} +		spin_unlock_bh(&bus->txq_lock); +		if (i == 0) { +			up(&bus->tx_seq_lock);  			break;  		} -		spin_unlock_bh(&bus->txqlock); -		ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL); +		ret = brcmf_sdio_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); +		up(&bus->tx_seq_lock); + +		cnt += i;  		/* In poll mode, need to check for other events */ -		if (!bus->intr && cnt) { +		if (!bus->intr) {  			/* Check device status, signal pending interrupt */  			sdio_claim_host(bus->sdiodev->func[1]);  			ret = r_sdreg32(bus, &intstatus, @@ -2034,7 +2374,69 @@ static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)  	return cnt;  } -static void brcmf_sdbrcm_bus_stop(struct device *dev) +static int brcmf_sdio_tx_ctrlframe(struct brcmf_sdio *bus, u8 *frame, u16 len) +{ +	u8 doff; +	u16 pad; +	uint retries = 0; +	struct brcmf_sdio_hdrinfo hd_info = {0}; +	int ret; + +	brcmf_dbg(TRACE, "Enter\n"); + +	/* Back the pointer to make room for bus header */ +	frame -= bus->tx_hdrlen; +	len += bus->tx_hdrlen; + +	/* Add alignment padding (optional for ctl frames) */ +	doff = ((unsigned long)frame % bus->head_align); +	if (doff) { +		frame -= doff; +		len += doff; +		memset(frame + bus->tx_hdrlen, 0, doff); +	} + +	/* Round send length to next SDIO block */ +	pad = 0; +	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { +		pad = bus->blocksize - (len % bus->blocksize); +		if ((pad > bus->roundup) || (pad >= bus->blocksize)) +			pad = 0; +	} else if (len % bus->head_align) { +		pad = bus->head_align - (len % bus->head_align); +	} +	len += pad; + +	hd_info.len = len - pad; +	hd_info.channel = SDPCM_CONTROL_CHANNEL; +	hd_info.dat_offset = doff + bus->tx_hdrlen; +	hd_info.seq_num = bus->tx_seq; +	hd_info.lastfrm = true; +	hd_info.tail_pad = pad; +	brcmf_sdio_hdpack(bus, frame, &hd_info); + +	if (bus->txglom) +		brcmf_sdio_update_hwhdr(frame, len); + +	brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), +			   frame, len, "Tx Frame:\n"); +	brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && +			   BRCMF_HDRS_ON(), +			   frame, min_t(u16, len, 16), "TxHdr:\n"); + +	do { +		ret = brcmf_sdiod_send_buf(bus->sdiodev, frame, len); + +		if (ret < 0) +			brcmf_sdio_txfail(bus); +		else +			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; +	} while (ret < 0 && retries++ < TXRETRIES); + +	return ret; +} + +static void brcmf_sdio_bus_stop(struct device *dev)  {  	u32 local_hostintmask;  	u8 saveclk; @@ -2051,62 +2453,57 @@ static void brcmf_sdbrcm_bus_stop(struct device *dev)  		bus->watchdog_tsk = NULL;  	} -	sdio_claim_host(bus->sdiodev->func[1]); - -	/* Enable clock for device interrupts */ -	brcmf_sdbrcm_bus_sleep(bus, false, false); +	if (bus_if->state == BRCMF_BUS_DOWN) { +		sdio_claim_host(sdiodev->func[1]); + +		/* Enable clock for device interrupts */ +		brcmf_sdio_bus_sleep(bus, false, false); + +		/* Disable and clear interrupts at the chip level also */ +		w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); +		local_hostintmask = bus->hostintmask; +		bus->hostintmask = 0; + +		/* Force backplane clocks to assure F2 interrupt propagates */ +		saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +					    &err); +		if (!err) +			brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +					  (saveclk | SBSDIO_FORCE_HT), &err); +		if (err) +			brcmf_err("Failed to force clock for F2: err %d\n", +				  err); -	/* Disable and clear interrupts at the chip level also */ -	w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); -	local_hostintmask = bus->hostintmask; -	bus->hostintmask = 0; +		/* Turn off the bus (F2), free any pending packets */ +		brcmf_dbg(INTR, "disable SDIO interrupts\n"); +		sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); -	/* Change our idea of bus state */ -	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; +		/* Clear any pending interrupts now that F2 is disabled */ +		w_sdreg32(bus, local_hostintmask, +			  offsetof(struct sdpcmd_regs, intstatus)); -	/* Force clocks on backplane to be sure F2 interrupt propagates */ -	saveclk = brcmf_sdio_regrb(bus->sdiodev, -				   SBSDIO_FUNC1_CHIPCLKCSR, &err); -	if (!err) { -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -				 (saveclk | SBSDIO_FORCE_HT), &err); +		sdio_release_host(sdiodev->func[1]);  	} -	if (err) -		brcmf_err("Failed to force clock for F2: err %d\n", err); - -	/* Turn off the bus (F2), free any pending packets */ -	brcmf_dbg(INTR, "disable SDIO interrupts\n"); -	brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1, -			 NULL); - -	/* Clear any pending interrupts now that F2 is disabled */ -	w_sdreg32(bus, local_hostintmask, -		  offsetof(struct sdpcmd_regs, intstatus)); - -	/* Turn off the backplane clock (only) */ -	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); -	sdio_release_host(bus->sdiodev->func[1]); -  	/* Clear the data packet queues */  	brcmu_pktq_flush(&bus->txq, true, NULL, NULL);  	/* Clear any held glomming stuff */  	if (bus->glomd)  		brcmu_pkt_buf_free_skb(bus->glomd); -	brcmf_sdbrcm_free_glom(bus); +	brcmf_sdio_free_glom(bus);  	/* Clear rx control and wake any waiters */  	spin_lock_bh(&bus->rxctl_lock);  	bus->rxlen = 0;  	spin_unlock_bh(&bus->rxctl_lock); -	brcmf_sdbrcm_dcmd_resp_wake(bus); +	brcmf_sdio_dcmd_resp_wake(bus);  	/* Reset some F2 state stuff */  	bus->rxskip = false;  	bus->tx_seq = bus->rx_seq = 0;  } -static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) +static inline void brcmf_sdio_clrintr(struct brcmf_sdio *bus)  {  	unsigned long flags; @@ -2120,49 +2517,50 @@ static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus)  	}  } +static void atomic_orr(int val, atomic_t *v) +{ +	int old_val; + +	old_val = atomic_read(v); +	while (atomic_cmpxchg(v, old_val, val | old_val) != old_val) +		old_val = atomic_read(v); +} +  static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus)  { -	u8 idx; +	struct brcmf_core *buscore;  	u32 addr;  	unsigned long val; -	int n, ret; +	int ret; -	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); -	addr = bus->ci->c_inf[idx].base + -	       offsetof(struct sdpcmd_regs, intstatus); +	buscore = brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV); +	addr = buscore->base + offsetof(struct sdpcmd_regs, intstatus); -	ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false); +	val = brcmf_sdiod_regrl(bus->sdiodev, addr, &ret);  	bus->sdcnt.f1regdata++;  	if (ret != 0) -		val = 0; +		return ret;  	val &= bus->hostintmask;  	atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE));  	/* Clear interrupts */  	if (val) { -		ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true); +		brcmf_sdiod_regwl(bus->sdiodev, addr, val, &ret);  		bus->sdcnt.f1regdata++; -	} - -	if (ret) { -		atomic_set(&bus->intstatus, 0); -	} else if (val) { -		for_each_set_bit(n, &val, 32) -			set_bit(n, (unsigned long *)&bus->intstatus.counter); +		atomic_orr(val, &bus->intstatus);  	}  	return ret;  } -static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) +static void brcmf_sdio_dpc(struct brcmf_sdio *bus)  {  	u32 newstatus = 0;  	unsigned long intstatus; -	uint rxlimit = bus->rxbound;	/* Rx frames to read before resched */  	uint txlimit = bus->txbound;	/* Tx frames to send before resched */ -	uint framecnt = 0;	/* Temporary counter of tx/rx frames */ -	int err = 0, n; +	uint framecnt;			/* Temporary counter of tx/rx frames */ +	int err = 0;  	brcmf_dbg(TRACE, "Enter\n"); @@ -2174,48 +2572,29 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)  #ifdef DEBUG  		/* Check for inconsistent device control */ -		devctl = brcmf_sdio_regrb(bus->sdiodev, -					  SBSDIO_DEVICE_CTL, &err); -		if (err) { -			brcmf_err("error reading DEVCTL: %d\n", err); -			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; -		} +		devctl = brcmf_sdiod_regrb(bus->sdiodev, +					   SBSDIO_DEVICE_CTL, &err);  #endif				/* DEBUG */  		/* Read CSR, if clock on switch to AVAIL, else ignore */ -		clkctl = brcmf_sdio_regrb(bus->sdiodev, -					  SBSDIO_FUNC1_CHIPCLKCSR, &err); -		if (err) { -			brcmf_err("error reading CSR: %d\n", -				  err); -			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; -		} +		clkctl = brcmf_sdiod_regrb(bus->sdiodev, +					   SBSDIO_FUNC1_CHIPCLKCSR, &err);  		brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",  			  devctl, clkctl);  		if (SBSDIO_HTAV(clkctl)) { -			devctl = brcmf_sdio_regrb(bus->sdiodev, -						  SBSDIO_DEVICE_CTL, &err); -			if (err) { -				brcmf_err("error reading DEVCTL: %d\n", -					  err); -				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; -			} +			devctl = brcmf_sdiod_regrb(bus->sdiodev, +						   SBSDIO_DEVICE_CTL, &err);  			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; -			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, -					 devctl, &err); -			if (err) { -				brcmf_err("error writing DEVCTL: %d\n", -					  err); -				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; -			} +			brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, +					  devctl, &err);  			bus->clkstate = CLK_AVAIL;  		}  	}  	/* Make sure backplane clock is on */ -	brcmf_sdbrcm_bus_sleep(bus, false, true); +	brcmf_sdio_bus_sleep(bus, false, true);  	/* Pending interrupt indicates new device status */  	if (atomic_read(&bus->ipend) > 0) { @@ -2246,7 +2625,7 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)  	/* Handle host mailbox indication */  	if (intstatus & I_HMB_HOST_INT) {  		intstatus &= ~I_HMB_HOST_INT; -		intstatus |= brcmf_sdbrcm_hostmail(bus); +		intstatus |= brcmf_sdio_hostmail(bus);  	}  	sdio_release_host(bus->sdiodev->func[1]); @@ -2278,97 +2657,53 @@ static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)  		intstatus &= ~I_HMB_FRAME_IND;  	/* On frame indication, read available frames */ -	if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { -		framecnt = brcmf_sdio_readframes(bus, rxlimit); +	if ((intstatus & I_HMB_FRAME_IND) && (bus->clkstate == CLK_AVAIL)) { +		brcmf_sdio_readframes(bus, bus->rxbound);  		if (!bus->rxpending)  			intstatus &= ~I_HMB_FRAME_IND; -		rxlimit -= min(framecnt, rxlimit);  	}  	/* Keep still-pending events for next scheduling */ -	if (intstatus) { -		for_each_set_bit(n, &intstatus, 32) -			set_bit(n, (unsigned long *)&bus->intstatus.counter); -	} - -	brcmf_sdbrcm_clrintr(bus); - -	if (data_ok(bus) && bus->ctrl_frame_stat && -		(bus->clkstate == CLK_AVAIL)) { -		int i; +	if (intstatus) +		atomic_orr(intstatus, &bus->intstatus); -		sdio_claim_host(bus->sdiodev->func[1]); -		err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, -			SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, -			(u32) bus->ctrl_frame_len); +	brcmf_sdio_clrintr(bus); -		if (err < 0) { -			/* On failure, abort the command and -				terminate the frame */ -			brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", -				  err); -			bus->sdcnt.tx_sderrs++; - -			brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - -			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, -					 SFC_WF_TERM, &err); -			bus->sdcnt.f1regdata++; - -			for (i = 0; i < 3; i++) { -				u8 hi, lo; -				hi = brcmf_sdio_regrb(bus->sdiodev, -						      SBSDIO_FUNC1_WFRAMEBCHI, -						      &err); -				lo = brcmf_sdio_regrb(bus->sdiodev, -						      SBSDIO_FUNC1_WFRAMEBCLO, -						      &err); -				bus->sdcnt.f1regdata += 2; -				if ((hi == 0) && (lo == 0)) -					break; -			} +	if (bus->ctrl_frame_stat && (bus->clkstate == CLK_AVAIL) && +	    (down_interruptible(&bus->tx_seq_lock) == 0)) { +		if (data_ok(bus)) { +			sdio_claim_host(bus->sdiodev->func[1]); +			err = brcmf_sdio_tx_ctrlframe(bus,  bus->ctrl_frame_buf, +						      bus->ctrl_frame_len); +			sdio_release_host(bus->sdiodev->func[1]); -		} else { -			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; +			bus->ctrl_frame_stat = false; +			brcmf_sdio_wait_event_wakeup(bus);  		} -		sdio_release_host(bus->sdiodev->func[1]); -		bus->ctrl_frame_stat = false; -		brcmf_sdbrcm_wait_event_wakeup(bus); +		up(&bus->tx_seq_lock);  	}  	/* Send queued frames (limit 1 if rx may still be pending) */ -	else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && -		 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit -		 && data_ok(bus)) { +	if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && +	    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit && +	    data_ok(bus)) {  		framecnt = bus->rxpending ? min(txlimit, bus->txminmax) :  					    txlimit; -		framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); -		txlimit -= framecnt; +		brcmf_sdio_sendfromq(bus, framecnt);  	} -	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { +	if (!brcmf_bus_ready(bus->sdiodev->bus_if) || (err != 0)) {  		brcmf_err("failed backplane access over SDIO, halting operation\n"); -		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;  		atomic_set(&bus->intstatus, 0);  	} else if (atomic_read(&bus->intstatus) ||  		   atomic_read(&bus->ipend) > 0 ||  		   (!atomic_read(&bus->fcstate) &&  		    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && -		    data_ok(bus)) || PKT_AVAILABLE()) { +		    data_ok(bus))) {  		atomic_inc(&bus->dpc_tskcnt);  	} - -	/* If we're done for now, turn off clock request. */ -	if ((bus->clkstate != CLK_PENDING) -	    && bus->idletime == BRCMF_IDLE_IMMEDIATE) { -		bus->activity = false; -		brcmf_dbg(SDIO, "idle state\n"); -		sdio_claim_host(bus->sdiodev->func[1]); -		brcmf_sdbrcm_bus_sleep(bus, true, false); -		sdio_release_host(bus->sdiodev->func[1]); -	}  } -static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) +static struct pktq *brcmf_sdio_bus_gettxq(struct device *dev)  {  	struct brcmf_bus *bus_if = dev_get_drvdata(dev);  	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; @@ -2377,18 +2712,15 @@ static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev)  	return &bus->txq;  } -static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) +static int brcmf_sdio_bus_txdata(struct device *dev, struct sk_buff *pkt)  {  	int ret = -EBADE; -	uint datalen, prec; +	uint prec;  	struct brcmf_bus *bus_if = dev_get_drvdata(dev);  	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;  	struct brcmf_sdio *bus = sdiodev->bus; -	ulong flags; - -	brcmf_dbg(TRACE, "Enter\n"); -	datalen = pkt->len; +	brcmf_dbg(TRACE, "Enter: pkt: data %p len %d\n", pkt->data, pkt->len);  	/* Add space for the header */  	skb_push(pkt, bus->tx_hdrlen); @@ -2402,7 +2734,9 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)  	bus->sdcnt.fcqueued++;  	/* Priority based enq */ -	spin_lock_irqsave(&bus->txqlock, flags); +	spin_lock_bh(&bus->txq_lock); +	/* reset bus_flags in packet cb */ +	*(u16 *)(pkt->cb) = 0;  	if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {  		skb_pull(pkt, bus->tx_hdrlen);  		brcmf_err("out of bus->txq !!!\n"); @@ -2415,7 +2749,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)  		bus->txoff = true;  		brcmf_txflowblock(bus->sdiodev->dev, true);  	} -	spin_unlock_irqrestore(&bus->txqlock, flags); +	spin_unlock_bh(&bus->txq_lock);  #ifdef DEBUG  	if (pktq_plen(&bus->txq, prec) > qcount[prec]) @@ -2433,7 +2767,7 @@ static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)  #ifdef DEBUG  #define CONSOLE_LINE_MAX	192 -static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) +static int brcmf_sdio_readconsole(struct brcmf_sdio *bus)  {  	struct brcmf_console *c = &bus->console;  	u8 line[CONSOLE_LINE_MAX], ch; @@ -2446,8 +2780,8 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)  	/* Read console log struct */  	addr = bus->console_addr + offsetof(struct rte_console, log_le); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, -			      sizeof(c->log_le)); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, +			       sizeof(c->log_le));  	if (rv < 0)  		return rv; @@ -2472,7 +2806,7 @@ static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)  	/* Read the console buffer */  	addr = le32_to_cpu(c->log_le.buf); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize);  	if (rv < 0)  		return rv; @@ -2510,107 +2844,27 @@ break2:  }  #endif				/* DEBUG */ -static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) -{ -	int i; -	int ret; - -	bus->ctrl_frame_stat = false; -	ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, -				    SDIO_FUNC_2, F2SYNC, frame, len); - -	if (ret < 0) { -		/* On failure, abort the command and terminate the frame */ -		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", -			  ret); -		bus->sdcnt.tx_sderrs++; - -		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); - -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, -				 SFC_WF_TERM, NULL); -		bus->sdcnt.f1regdata++; - -		for (i = 0; i < 3; i++) { -			u8 hi, lo; -			hi = brcmf_sdio_regrb(bus->sdiodev, -					      SBSDIO_FUNC1_WFRAMEBCHI, NULL); -			lo = brcmf_sdio_regrb(bus->sdiodev, -					      SBSDIO_FUNC1_WFRAMEBCLO, NULL); -			bus->sdcnt.f1regdata += 2; -			if (hi == 0 && lo == 0) -				break; -		} -		return ret; -	} - -	bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; - -	return ret; -} -  static int -brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)  { -	u8 *frame; -	u16 len; -	uint retries = 0; -	u8 doff = 0; -	int ret = -1;  	struct brcmf_bus *bus_if = dev_get_drvdata(dev);  	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;  	struct brcmf_sdio *bus = sdiodev->bus; -	struct brcmf_sdio_hdrinfo hd_info = {0}; +	int ret = -1;  	brcmf_dbg(TRACE, "Enter\n"); -	/* Back the pointer to make a room for bus header */ -	frame = msg - bus->tx_hdrlen; -	len = (msglen += bus->tx_hdrlen); - -	/* Add alignment padding (optional for ctl frames) */ -	doff = ((unsigned long)frame % BRCMF_SDALIGN); -	if (doff) { -		frame -= doff; -		len += doff; -		msglen += doff; -		memset(frame, 0, doff + bus->tx_hdrlen); -	} -	/* precondition: doff < BRCMF_SDALIGN */ -	doff += bus->tx_hdrlen; - -	/* Round send length to next SDIO block */ -	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { -		u16 pad = bus->blocksize - (len % bus->blocksize); -		if ((pad <= bus->roundup) && (pad < bus->blocksize)) -			len += pad; -	} else if (len % BRCMF_SDALIGN) { -		len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN); -	} - -	/* Satisfy length-alignment requirements */ -	if (len & (ALIGNMENT - 1)) -		len = roundup(len, ALIGNMENT); - -	/* precondition: IS_ALIGNED((unsigned long)frame, 2) */ - -	/* Make sure backplane clock is on */ -	sdio_claim_host(bus->sdiodev->func[1]); -	brcmf_sdbrcm_bus_sleep(bus, false, false); -	sdio_release_host(bus->sdiodev->func[1]); - -	hd_info.len = (u16)msglen; -	hd_info.channel = SDPCM_CONTROL_CHANNEL; -	hd_info.dat_offset = doff; -	brcmf_sdio_hdpack(bus, frame, &hd_info); +	if (down_interruptible(&bus->tx_seq_lock)) +		return -EINTR;  	if (!data_ok(bus)) {  		brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",  			  bus->tx_max, bus->tx_seq); -		bus->ctrl_frame_stat = true; +		up(&bus->tx_seq_lock);  		/* Send from dpc */ -		bus->ctrl_frame_buf = frame; -		bus->ctrl_frame_len = len; +		bus->ctrl_frame_buf = msg; +		bus->ctrl_frame_len = msglen; +		bus->ctrl_frame_stat = true;  		wait_event_interruptible_timeout(bus->ctrl_wait,  						 !bus->ctrl_frame_stat, @@ -2621,31 +2875,18 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)  			ret = 0;  		} else {  			brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); +			bus->ctrl_frame_stat = false; +			if (down_interruptible(&bus->tx_seq_lock)) +				return -EINTR;  			ret = -1;  		}  	} -  	if (ret == -1) { -		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), -				   frame, len, "Tx Frame:\n"); -		brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && -				   BRCMF_HDRS_ON(), -				   frame, min_t(u16, len, 16), "TxHdr:\n"); - -		do { -			sdio_claim_host(bus->sdiodev->func[1]); -			ret = brcmf_tx_frame(bus, frame, len); -			sdio_release_host(bus->sdiodev->func[1]); -		} while (ret < 0 && retries++ < TXRETRIES); -	} - -	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && -	    atomic_read(&bus->dpc_tskcnt) == 0) { -		bus->activity = false;  		sdio_claim_host(bus->sdiodev->func[1]); -		brcmf_dbg(INFO, "idle\n"); -		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); +		brcmf_sdio_bus_sleep(bus, false, false); +		ret = brcmf_sdio_tx_ctrlframe(bus, msg, msglen);  		sdio_release_host(bus->sdiodev->func[1]); +		up(&bus->tx_seq_lock);  	}  	if (ret) @@ -2657,72 +2898,6 @@ brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)  }  #ifdef DEBUG -static inline bool brcmf_sdio_valid_shared_address(u32 addr) -{ -	return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); -} - -static int brcmf_sdio_readshared(struct brcmf_sdio *bus, -				 struct sdpcm_shared *sh) -{ -	u32 addr; -	int rv; -	u32 shaddr = 0; -	struct sdpcm_shared_le sh_le; -	__le32 addr_le; - -	shaddr = bus->ci->rambase + bus->ramsize - 4; - -	/* -	 * Read last word in socram to determine -	 * address of sdpcm_shared structure -	 */ -	sdio_claim_host(bus->sdiodev->func[1]); -	brcmf_sdbrcm_bus_sleep(bus, false, false); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); -	sdio_release_host(bus->sdiodev->func[1]); -	if (rv < 0) -		return rv; - -	addr = le32_to_cpu(addr_le); - -	brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); - -	/* -	 * Check if addr is valid. -	 * NVRAM length at the end of memory should have been overwritten. -	 */ -	if (!brcmf_sdio_valid_shared_address(addr)) { -			brcmf_err("invalid sdpcm_shared address 0x%08X\n", -				  addr); -			return -EINVAL; -	} - -	/* Read hndrte_shared structure */ -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, -			      sizeof(struct sdpcm_shared_le)); -	if (rv < 0) -		return rv; - -	/* Endianness */ -	sh->flags = le32_to_cpu(sh_le.flags); -	sh->trap_addr = le32_to_cpu(sh_le.trap_addr); -	sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); -	sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); -	sh->assert_line = le32_to_cpu(sh_le.assert_line); -	sh->console_addr = le32_to_cpu(sh_le.console_addr); -	sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); - -	if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { -		brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", -			  SDPCM_SHARED_VERSION, -			  sh->flags & SDPCM_SHARED_VERSION_MASK); -		return -EPROTO; -	} - -	return 0; -} -  static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,  				   struct sdpcm_shared *sh, char __user *data,  				   size_t count) @@ -2736,22 +2911,22 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,  	/* obtain console information from device memory */  	addr = sh->console_addr + offsetof(struct rte_console, log_le); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, -			      (u8 *)&sh_val, sizeof(u32)); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, +			       (u8 *)&sh_val, sizeof(u32));  	if (rv < 0)  		return rv;  	console_ptr = le32_to_cpu(sh_val);  	addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, -			      (u8 *)&sh_val, sizeof(u32)); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, +			       (u8 *)&sh_val, sizeof(u32));  	if (rv < 0)  		return rv;  	console_size = le32_to_cpu(sh_val);  	addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, -			      (u8 *)&sh_val, sizeof(u32)); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, addr, +			       (u8 *)&sh_val, sizeof(u32));  	if (rv < 0)  		return rv;  	console_index = le32_to_cpu(sh_val); @@ -2765,8 +2940,8 @@ static int brcmf_sdio_dump_console(struct brcmf_sdio *bus,  	/* obtain the console data from device */  	conbuf[console_size] = '\0'; -	rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, -			      console_size); +	rv = brcmf_sdiod_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, +			       console_size);  	if (rv < 0)  		goto done; @@ -2803,8 +2978,8 @@ static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh,  		return 0;  	} -	error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, -				 sizeof(struct brcmf_trap_info)); +	error = brcmf_sdiod_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, +				  sizeof(struct brcmf_trap_info));  	if (error < 0)  		return error; @@ -2847,14 +3022,14 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,  	sdio_claim_host(bus->sdiodev->func[1]);  	if (sh->assert_file_addr != 0) { -		error = brcmf_sdio_ramrw(bus->sdiodev, false, -					 sh->assert_file_addr, (u8 *)file, 80); +		error = brcmf_sdiod_ramrw(bus->sdiodev, false, +					  sh->assert_file_addr, (u8 *)file, 80);  		if (error < 0)  			return error;  	}  	if (sh->assert_exp_addr != 0) { -		error = brcmf_sdio_ramrw(bus->sdiodev, false, -					 sh->assert_exp_addr, (u8 *)expr, 80); +		error = brcmf_sdiod_ramrw(bus->sdiodev, false, +					  sh->assert_exp_addr, (u8 *)expr, 80);  		if (error < 0)  			return error;  	} @@ -2866,7 +3041,7 @@ static int brcmf_sdio_assert_info(struct brcmf_sdio *bus,  	return simple_read_from_buffer(data, count, &pos, buf, res);  } -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)  {  	int error;  	struct sdpcm_shared sh; @@ -2887,8 +3062,8 @@ static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus)  	return 0;  } -static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, -				  size_t count, loff_t *ppos) +static int brcmf_sdio_died_dump(struct brcmf_sdio *bus, char __user *data, +				size_t count, loff_t *ppos)  {  	int error = 0;  	struct sdpcm_shared sh; @@ -2929,7 +3104,7 @@ static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data,  	struct brcmf_sdio *bus = f->private_data;  	int res; -	res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); +	res = brcmf_sdio_died_dump(bus, data, count, ppos);  	if (res > 0)  		*ppos += res;  	return (ssize_t)res; @@ -2952,9 +3127,11 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)  	debugfs_create_file("forensics", S_IRUGO, dentry, bus,  			    &brcmf_sdio_forensic_ops);  	brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); +	debugfs_create_u32("console_interval", 0644, dentry, +			   &bus->console_interval);  }  #else -static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) +static int brcmf_sdio_checkdied(struct brcmf_sdio *bus)  {  	return 0;  } @@ -2965,7 +3142,7 @@ static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus)  #endif /* DEBUG */  static int -brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) +brcmf_sdio_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)  {  	int timeleft;  	uint rxlen = 0; @@ -2978,7 +3155,7 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)  	brcmf_dbg(TRACE, "Enter\n");  	/* Wait until control frame is available */ -	timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); +	timeleft = brcmf_sdio_dcmd_resp_wait(bus, &bus->rxlen, &pending);  	spin_lock_bh(&bus->rxctl_lock);  	rxlen = bus->rxlen; @@ -2995,13 +3172,13 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)  			  rxlen, msglen);  	} else if (timeleft == 0) {  		brcmf_err("resumed on timeout\n"); -		brcmf_sdbrcm_checkdied(bus); +		brcmf_sdio_checkdied(bus);  	} else if (pending) {  		brcmf_dbg(CTL, "cancelled\n");  		return -ERESTARTSYS;  	} else {  		brcmf_dbg(CTL, "resumed for unknown reason?\n"); -		brcmf_sdbrcm_checkdied(bus); +		brcmf_sdio_checkdied(bus);  	}  	if (rxlen) @@ -3012,272 +3189,177 @@ brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)  	return rxlen ? (int)rxlen : -ETIMEDOUT;  } -static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) -{ -	struct chip_info *ci = bus->ci; - -	/* To enter download state, disable ARM and reset SOCRAM. -	 * To exit download state, simply reset ARM (default is RAM boot). -	 */ -	if (enter) { -		bus->alp_only = true; - -		brcmf_sdio_chip_enter_download(bus->sdiodev, ci); -	} else { -		if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars, -						   bus->varsz)) -			return false; - -		/* Allow HT Clock now that the ARM is running. */ -		bus->alp_only = false; - -		bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; -	} - -	return true; -} - -static int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus) -{ -	if (bus->firmware->size < bus->fw_ptr + len) -		len = bus->firmware->size - bus->fw_ptr; - -	memcpy(buf, &bus->firmware->data[bus->fw_ptr], len); -	bus->fw_ptr += len; -	return len; -} - -static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) +#ifdef DEBUG +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, +			u8 *ram_data, uint ram_sz)  { +	char *ram_cmp; +	int err; +	bool ret = true; +	int address;  	int offset; -	uint len; -	u8 *memblock = NULL, *memptr; -	int ret; -	u8 idx; - -	brcmf_dbg(INFO, "Enter\n"); - -	ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME, -			       &bus->sdiodev->func[2]->dev); -	if (ret) { -		brcmf_err("Fail to request firmware %d\n", ret); -		return ret; -	} -	bus->fw_ptr = 0; - -	memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC); -	if (memblock == NULL) { -		ret = -ENOMEM; -		goto err; -	} -	if ((u32)(unsigned long)memblock % BRCMF_SDALIGN) -		memptr += (BRCMF_SDALIGN - -			   ((u32)(unsigned long)memblock % BRCMF_SDALIGN)); - -	offset = bus->ci->rambase; - -	/* Download image */ -	len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); -	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4); -	if (BRCMF_MAX_CORENUM != idx) -		memcpy(&bus->ci->rst_vec, memptr, sizeof(bus->ci->rst_vec)); -	while (len) { -		ret = brcmf_sdio_ramrw(bus->sdiodev, true, offset, memptr, len); -		if (ret) { -			brcmf_err("error %d on writing %d membytes at 0x%08x\n", -				  ret, MEMBLOCK, offset); -			goto err; +	int len; + +	/* read back and verify */ +	brcmf_dbg(INFO, "Compare RAM dl & ul at 0x%08x; size=%d\n", ram_addr, +		  ram_sz); +	ram_cmp = kmalloc(MEMBLOCK, GFP_KERNEL); +	/* do not proceed while no memory but  */ +	if (!ram_cmp) +		return true; + +	address = ram_addr; +	offset = 0; +	while (offset < ram_sz) { +		len = ((offset + MEMBLOCK) < ram_sz) ? MEMBLOCK : +		      ram_sz - offset; +		err = brcmf_sdiod_ramrw(sdiodev, false, address, ram_cmp, len); +		if (err) { +			brcmf_err("error %d on reading %d membytes at 0x%08x\n", +				  err, len, address); +			ret = false; +			break; +		} else if (memcmp(ram_cmp, &ram_data[offset], len)) { +			brcmf_err("Downloaded RAM image is corrupted, block offset is %d, len is %d\n", +				  offset, len); +			ret = false; +			break;  		} - -		offset += MEMBLOCK; -		len = brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus); +		offset += len; +		address += len;  	} -err: -	kfree(memblock); - -	release_firmware(bus->firmware); -	bus->fw_ptr = 0; +	kfree(ram_cmp);  	return ret;  } +#else	/* DEBUG */ +static bool +brcmf_sdio_verifymemory(struct brcmf_sdio_dev *sdiodev, u32 ram_addr, +			u8 *ram_data, uint ram_sz) +{ +	return true; +} +#endif	/* DEBUG */ -/* - * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file - * and ending in a NUL. - * Removes carriage returns, empty lines, comment lines, and converts - * newlines to NULs. - * Shortens buffer as needed and pads with NULs.  End of buffer is marked - * by two NULs. -*/ - -static int brcmf_process_nvram_vars(struct brcmf_sdio *bus) -{ -	char *varbuf; -	char *dp; -	bool findNewline; -	int column; -	int ret = 0; -	uint buf_len, n, len; - -	len = bus->firmware->size; -	varbuf = vmalloc(len); -	if (!varbuf) -		return -ENOMEM; - -	memcpy(varbuf, bus->firmware->data, len); -	dp = varbuf; +static int brcmf_sdio_download_code_file(struct brcmf_sdio *bus, +					 const struct firmware *fw) +{ +	int err; -	findNewline = false; -	column = 0; +	brcmf_dbg(TRACE, "Enter\n"); -	for (n = 0; n < len; n++) { -		if (varbuf[n] == 0) -			break; -		if (varbuf[n] == '\r') -			continue; -		if (findNewline && varbuf[n] != '\n') -			continue; -		findNewline = false; -		if (varbuf[n] == '#') { -			findNewline = true; -			continue; -		} -		if (varbuf[n] == '\n') { -			if (column == 0) -				continue; -			*dp++ = 0; -			column = 0; -			continue; -		} -		*dp++ = varbuf[n]; -		column++; -	} -	buf_len = dp - varbuf; -	while (dp < varbuf + n) -		*dp++ = 0; - -	kfree(bus->vars); -	/* roundup needed for download to device */ -	bus->varsz = roundup(buf_len + 1, 4); -	bus->vars = kmalloc(bus->varsz, GFP_KERNEL); -	if (bus->vars == NULL) { -		bus->varsz = 0; -		ret = -ENOMEM; -		goto err; -	} +	err = brcmf_sdiod_ramrw(bus->sdiodev, true, bus->ci->rambase, +				(u8 *)fw->data, fw->size); +	if (err) +		brcmf_err("error %d on writing %d membytes at 0x%08x\n", +			  err, (int)fw->size, bus->ci->rambase); +	else if (!brcmf_sdio_verifymemory(bus->sdiodev, bus->ci->rambase, +					  (u8 *)fw->data, fw->size)) +		err = -EIO; -	/* copy the processed variables and add null termination */ -	memcpy(bus->vars, varbuf, buf_len); -	bus->vars[buf_len] = 0; -err: -	vfree(varbuf); -	return ret; +	return err;  } -static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) +static int brcmf_sdio_download_nvram(struct brcmf_sdio *bus, +				     void *vars, u32 varsz)  { -	int ret; - -	ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME, -			       &bus->sdiodev->func[2]->dev); -	if (ret) { -		brcmf_err("Fail to request nvram %d\n", ret); -		return ret; -	} +	int address; +	int err; -	ret = brcmf_process_nvram_vars(bus); +	brcmf_dbg(TRACE, "Enter\n"); -	release_firmware(bus->firmware); +	address = bus->ci->ramsize - varsz + bus->ci->rambase; +	err = brcmf_sdiod_ramrw(bus->sdiodev, true, address, vars, varsz); +	if (err) +		brcmf_err("error %d on writing %d nvram bytes at 0x%08x\n", +			  err, varsz, address); +	else if (!brcmf_sdio_verifymemory(bus->sdiodev, address, vars, varsz)) +		err = -EIO; -	return ret; +	return err;  } -static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) +static int brcmf_sdio_download_firmware(struct brcmf_sdio *bus, +					const struct firmware *fw, +					void *nvram, u32 nvlen)  { -	int bcmerror = -1; +	int bcmerror = -EFAULT; +	u32 rstvec; + +	sdio_claim_host(bus->sdiodev->func[1]); +	brcmf_sdio_clkctl(bus, CLK_AVAIL, false);  	/* Keep arm in reset */ -	if (!brcmf_sdbrcm_download_state(bus, true)) { -		brcmf_err("error placing ARM core in reset\n"); -		goto err; -	} +	brcmf_chip_enter_download(bus->ci); + +	rstvec = get_unaligned_le32(fw->data); +	brcmf_dbg(SDIO, "firmware rstvec: %x\n", rstvec); -	if (brcmf_sdbrcm_download_code_file(bus)) { +	bcmerror = brcmf_sdio_download_code_file(bus, fw); +	release_firmware(fw); +	if (bcmerror) {  		brcmf_err("dongle image file download failed\n"); +		brcmf_fw_nvram_free(nvram);  		goto err;  	} -	if (brcmf_sdbrcm_download_nvram(bus)) { +	bcmerror = brcmf_sdio_download_nvram(bus, nvram, nvlen); +	brcmf_fw_nvram_free(nvram); +	if (bcmerror) {  		brcmf_err("dongle nvram file download failed\n");  		goto err;  	}  	/* Take arm out of reset */ -	if (!brcmf_sdbrcm_download_state(bus, false)) { +	if (!brcmf_chip_exit_download(bus->ci, rstvec)) {  		brcmf_err("error getting out of ARM core reset\n");  		goto err;  	} +	/* Allow HT Clock now that the ARM is running. */ +	brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_LOAD);  	bcmerror = 0;  err: +	brcmf_sdio_clkctl(bus, CLK_SDONLY, false); +	sdio_release_host(bus->sdiodev->func[1]);  	return bcmerror;  } -static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus) -{ -	u32 addr, reg; - -	brcmf_dbg(TRACE, "Enter\n"); - -	/* old chips with PMU version less than 17 don't support save restore */ -	if (bus->ci->pmurev < 17) -		return false; - -	/* read PMU chipcontrol register 3*/ -	addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); -	brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL); -	addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); -	reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL); - -	return (bool)reg; -} - -static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) +static void brcmf_sdio_sr_init(struct brcmf_sdio *bus)  {  	int err = 0;  	u8 val;  	brcmf_dbg(TRACE, "Enter\n"); -	val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, -			       &err); +	val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, &err);  	if (err) {  		brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n");  		return;  	}  	val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, -			 val, &err); +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, val, &err);  	if (err) {  		brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n");  		return;  	}  	/* Add CMD14 Support */ -	brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, -			 (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | -			  SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), -			 &err); +	brcmf_sdiod_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, +			  (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | +			   SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), +			  &err);  	if (err) {  		brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n");  		return;  	} -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -			 SBSDIO_FORCE_HT, &err); +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +			  SBSDIO_FORCE_HT, &err);  	if (err) {  		brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n");  		return; @@ -3289,7 +3371,7 @@ static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus)  }  /* enable KSO bit */ -static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) +static int brcmf_sdio_kso_init(struct brcmf_sdio *bus)  {  	u8 val;  	int err = 0; @@ -3297,11 +3379,10 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)  	brcmf_dbg(TRACE, "Enter\n");  	/* KSO bit added in SDIO core rev 12 */ -	if (bus->ci->c_inf[1].rev < 12) +	if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12)  		return 0; -	val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, -			       &err); +	val = brcmf_sdiod_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, &err);  	if (err) {  		brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n");  		return err; @@ -3310,8 +3391,8 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)  	if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) {  		val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN <<  			SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, -				 val, &err); +		brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, +				  val, &err);  		if (err) {  			brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n");  			return err; @@ -3322,132 +3403,61 @@ static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus)  } -static bool -brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) -{ -	bool ret; - -	sdio_claim_host(bus->sdiodev->func[1]); - -	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); - -	ret = _brcmf_sdbrcm_download_firmware(bus) == 0; - -	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); - -	sdio_release_host(bus->sdiodev->func[1]); - -	return ret; -} - -static int brcmf_sdbrcm_bus_init(struct device *dev) +static int brcmf_sdio_bus_preinit(struct device *dev)  {  	struct brcmf_bus *bus_if = dev_get_drvdata(dev);  	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;  	struct brcmf_sdio *bus = sdiodev->bus; -	unsigned long timeout; -	u8 ready, enable; -	int err, ret = 0; -	u8 saveclk; - -	brcmf_dbg(TRACE, "Enter\n"); - -	/* try to download image and nvram to the dongle */ -	if (bus_if->state == BRCMF_BUS_DOWN) { -		if (!(brcmf_sdbrcm_download_firmware(bus))) -			return -1; -	} - -	if (!bus->sdiodev->bus_if->drvr) -		return 0; - -	/* Start the watchdog timer */ -	bus->sdcnt.tickcnt = 0; -	brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); - -	sdio_claim_host(bus->sdiodev->func[1]); - -	/* Make sure backplane clock is on, needed to generate F2 interrupt */ -	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); -	if (bus->clkstate != CLK_AVAIL) -		goto exit; - -	/* Force clocks on backplane to be sure F2 interrupt propagates */ -	saveclk = brcmf_sdio_regrb(bus->sdiodev, -				   SBSDIO_FUNC1_CHIPCLKCSR, &err); -	if (!err) { -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -				 (saveclk | SBSDIO_FORCE_HT), &err); -	} -	if (err) { -		brcmf_err("Failed to force clock for F2: err %d\n", err); -		goto exit; -	} - -	/* Enable function 2 (frame transfers) */ -	w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, -		  offsetof(struct sdpcmd_regs, tosbmailboxdata)); -	enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2); - -	brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); - -	timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY); -	ready = 0; -	while (enable != ready) { -		ready = brcmf_sdio_regrb(bus->sdiodev, -					 SDIO_CCCR_IORx, NULL); -		if (time_after(jiffies, timeout)) -			break; -		else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50)) -			/* prevent busy waiting if it takes too long */ -			msleep_interruptible(20); -	} - -	brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready); - -	/* If F2 successfully enabled, set core and enable interrupts */ -	if (ready == enable) { -		/* Set up the interrupt mask and enable interrupts */ -		bus->hostintmask = HOSTINTMASK; -		w_sdreg32(bus, bus->hostintmask, -			  offsetof(struct sdpcmd_regs, hostintmask)); +	uint pad_size; +	u32 value; +	int err; -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); +	/* the commands below use the terms tx and rx from +	 * a device perspective, ie. bus:txglom affects the +	 * bus transfers from device to host. +	 */ +	if (brcmf_chip_get_core(bus->ci, BCMA_CORE_SDIO_DEV)->rev < 12) { +		/* for sdio core rev < 12, disable txgloming */ +		value = 0; +		err = brcmf_iovar_data_set(dev, "bus:txglom", &value, +					   sizeof(u32));  	} else { -		/* Disable F2 again */ -		enable = SDIO_FUNC_ENABLE_1; -		brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, enable, NULL); -		ret = -ENODEV; +		/* otherwise, set txglomalign */ +		value = 4; +		if (sdiodev->pdata) +			value = sdiodev->pdata->sd_sgentry_align; +		/* SDIO ADMA requires at least 32 bit alignment */ +		value = max_t(u32, value, 4); +		err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, +					   sizeof(u32));  	} -	if (brcmf_sdbrcm_sr_capable(bus)) { -		brcmf_sdbrcm_sr_init(bus); -	} else { -		/* Restore previous clock setting */ -		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -				 saveclk, &err); -	} +	if (err < 0) +		goto done; -	if (ret == 0) { -		ret = brcmf_sdio_intr_register(bus->sdiodev); -		if (ret != 0) -			brcmf_err("intr register failed:%d\n", ret); +	bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; +	if (sdiodev->sg_support) { +		bus->txglom = false; +		value = 1; +		pad_size = bus->sdiodev->func[2]->cur_blksize << 1; +		err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", +					   &value, sizeof(u32)); +		if (err < 0) { +			/* bus:rxglom is allowed to fail */ +			err = 0; +		} else { +			bus->txglom = true; +			bus->tx_hdrlen += SDPCM_HWEXT_LEN; +		}  	} +	brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); -	/* If we didn't come up, turn off backplane clock */ -	if (bus_if->state != BRCMF_BUS_DATA) -		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); - -exit: -	sdio_release_host(bus->sdiodev->func[1]); - -	return ret; +done: +	return err;  } -void brcmf_sdbrcm_isr(void *arg) +void brcmf_sdio_isr(struct brcmf_sdio *bus)  { -	struct brcmf_sdio *bus = (struct brcmf_sdio *) arg; -  	brcmf_dbg(TRACE, "Enter\n");  	if (!bus) { @@ -3455,7 +3465,7 @@ void brcmf_sdbrcm_isr(void *arg)  		return;  	} -	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { +	if (!brcmf_bus_ready(bus->sdiodev->bus_if)) {  		brcmf_err("bus is down. we have nothing to do\n");  		return;  	} @@ -3466,7 +3476,6 @@ void brcmf_sdbrcm_isr(void *arg)  	else  		if (brcmf_sdio_intr_rstatus(bus)) {  			brcmf_err("failed backplane access\n"); -			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;  		}  	/* Disable additional interrupts (is this needed now)? */ @@ -3477,7 +3486,7 @@ void brcmf_sdbrcm_isr(void *arg)  	queue_work(bus->brcmf_wq, &bus->datawork);  } -static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) +static bool brcmf_sdio_bus_watchdog(struct brcmf_sdio *bus)  {  #ifdef DEBUG  	struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); @@ -3501,9 +3510,9 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)  				u8 devpend;  				sdio_claim_host(bus->sdiodev->func[1]); -				devpend = brcmf_sdio_regrb(bus->sdiodev, -							   SDIO_CCCR_INTx, -							   NULL); +				devpend = brcmf_sdiod_regrb(bus->sdiodev, +							    SDIO_CCCR_INTx, +							    NULL);  				sdio_release_host(bus->sdiodev->func[1]);  				intstatus =  				    devpend & (INTR_STATUS_FUNC1 | @@ -3533,8 +3542,8 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)  			bus->console.count -= bus->console_interval;  			sdio_claim_host(bus->sdiodev->func[1]);  			/* Make sure backplane clock is on */ -			brcmf_sdbrcm_bus_sleep(bus, false, false); -			if (brcmf_sdbrcm_readconsole(bus) < 0) +			brcmf_sdio_bus_sleep(bus, false, false); +			if (brcmf_sdio_readconsole(bus) < 0)  				/* stop on error */  				bus->console_interval = 0;  			sdio_release_host(bus->sdiodev->func[1]); @@ -3548,11 +3557,11 @@ static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)  			bus->idlecount = 0;  			if (bus->activity) {  				bus->activity = false; -				brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); +				brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS);  			} else {  				brcmf_dbg(SDIO, "idle\n");  				sdio_claim_host(bus->sdiodev->func[1]); -				brcmf_sdbrcm_bus_sleep(bus, true, false); +				brcmf_sdio_bus_sleep(bus, true, false);  				sdio_release_host(bus->sdiodev->func[1]);  			}  		} @@ -3567,38 +3576,177 @@ static void brcmf_sdio_dataworker(struct work_struct *work)  					      datawork);  	while (atomic_read(&bus->dpc_tskcnt)) { -		brcmf_sdbrcm_dpc(bus); -		atomic_dec(&bus->dpc_tskcnt); +		atomic_set(&bus->dpc_tskcnt, 0); +		brcmf_sdio_dpc(bus);  	}  } -static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) +static void +brcmf_sdio_drivestrengthinit(struct brcmf_sdio_dev *sdiodev, +			     struct brcmf_chip *ci, u32 drivestrength)  { -	brcmf_dbg(TRACE, "Enter\n"); +	const struct sdiod_drive_str *str_tab = NULL; +	u32 str_mask; +	u32 str_shift; +	u32 base; +	u32 i; +	u32 drivestrength_sel = 0; +	u32 cc_data_temp; +	u32 addr; -	kfree(bus->rxbuf); -	bus->rxctl = bus->rxbuf = NULL; -	bus->rxlen = 0; +	if (!(ci->cc_caps & CC_CAP_PMU)) +		return; + +	switch (SDIOD_DRVSTR_KEY(ci->chip, ci->pmurev)) { +	case SDIOD_DRVSTR_KEY(BCM4330_CHIP_ID, 12): +		str_tab = sdiod_drvstr_tab1_1v8; +		str_mask = 0x00003800; +		str_shift = 11; +		break; +	case SDIOD_DRVSTR_KEY(BCM4334_CHIP_ID, 17): +		str_tab = sdiod_drvstr_tab6_1v8; +		str_mask = 0x00001800; +		str_shift = 11; +		break; +	case SDIOD_DRVSTR_KEY(BCM43143_CHIP_ID, 17): +		/* note: 43143 does not support tristate */ +		i = ARRAY_SIZE(sdiod_drvstr_tab2_3v3) - 1; +		if (drivestrength >= sdiod_drvstr_tab2_3v3[i].strength) { +			str_tab = sdiod_drvstr_tab2_3v3; +			str_mask = 0x00000007; +			str_shift = 0; +		} else +			brcmf_err("Invalid SDIO Drive strength for chip %s, strength=%d\n", +				  ci->name, drivestrength); +		break; +	case SDIOD_DRVSTR_KEY(BCM43362_CHIP_ID, 13): +		str_tab = sdiod_drive_strength_tab5_1v8; +		str_mask = 0x00003800; +		str_shift = 11; +		break; +	default: +		brcmf_err("No SDIO Drive strength init done for chip %s rev %d pmurev %d\n", +			  ci->name, ci->chiprev, ci->pmurev); +		break; +	} + +	if (str_tab != NULL) { +		for (i = 0; str_tab[i].strength != 0; i++) { +			if (drivestrength >= str_tab[i].strength) { +				drivestrength_sel = str_tab[i].sel; +				break; +			} +		} +		base = brcmf_chip_get_chipcommon(ci)->base; +		addr = CORE_CC_REG(base, chipcontrol_addr); +		brcmf_sdiod_regwl(sdiodev, addr, 1, NULL); +		cc_data_temp = brcmf_sdiod_regrl(sdiodev, addr, NULL); +		cc_data_temp &= ~str_mask; +		drivestrength_sel <<= str_shift; +		cc_data_temp |= drivestrength_sel; +		brcmf_sdiod_regwl(sdiodev, addr, cc_data_temp, NULL); + +		brcmf_dbg(INFO, "SDIO: %d mA (req=%d mA) drive strength selected, set to 0x%08x\n", +			  str_tab[i].strength, drivestrength, cc_data_temp); +	}  } -static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) +static int brcmf_sdio_buscoreprep(void *ctx)  { -	brcmf_dbg(TRACE, "Enter\n"); +	struct brcmf_sdio_dev *sdiodev = ctx; +	int err = 0; +	u8 clkval, clkset; -	if (bus->sdiodev->bus_if->maxctl) { -		bus->rxblen = -		    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), -			    ALIGNMENT) + BRCMF_SDALIGN; -		bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); -		if (!(bus->rxbuf)) -			return false; +	/* Try forcing SDIO core to do ALPAvail request only */ +	clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ; +	brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); +	if (err) { +		brcmf_err("error writing for HT off\n"); +		return err;  	} -	return true; +	/* If register supported, wait for ALPAvail and then force ALP */ +	/* This may take up to 15 milliseconds */ +	clkval = brcmf_sdiod_regrb(sdiodev, +				   SBSDIO_FUNC1_CHIPCLKCSR, NULL); + +	if ((clkval & ~SBSDIO_AVBITS) != clkset) { +		brcmf_err("ChipClkCSR access: wrote 0x%02x read 0x%02x\n", +			  clkset, clkval); +		return -EACCES; +	} + +	SPINWAIT(((clkval = brcmf_sdiod_regrb(sdiodev, +					      SBSDIO_FUNC1_CHIPCLKCSR, NULL)), +			!SBSDIO_ALPAV(clkval)), +			PMU_MAX_TRANSITION_DLY); +	if (!SBSDIO_ALPAV(clkval)) { +		brcmf_err("timeout on ALPAV wait, clkval 0x%02x\n", +			  clkval); +		return -EBUSY; +	} + +	clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP; +	brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, clkset, &err); +	udelay(65); + +	/* Also, disable the extra SDIO pull-ups */ +	brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_SDIOPULLUP, 0, NULL); + +	return 0; +} + +static void brcmf_sdio_buscore_exitdl(void *ctx, struct brcmf_chip *chip, +				      u32 rstvec) +{ +	struct brcmf_sdio_dev *sdiodev = ctx; +	struct brcmf_core *core; +	u32 reg_addr; + +	/* clear all interrupts */ +	core = brcmf_chip_get_core(chip, BCMA_CORE_SDIO_DEV); +	reg_addr = core->base + offsetof(struct sdpcmd_regs, intstatus); +	brcmf_sdiod_regwl(sdiodev, reg_addr, 0xFFFFFFFF, NULL); + +	if (rstvec) +		/* Write reset vector to address 0 */ +		brcmf_sdiod_ramrw(sdiodev, true, 0, (void *)&rstvec, +				  sizeof(rstvec)); +} + +static u32 brcmf_sdio_buscore_read32(void *ctx, u32 addr) +{ +	struct brcmf_sdio_dev *sdiodev = ctx; +	u32 val, rev; + +	val = brcmf_sdiod_regrl(sdiodev, addr, NULL); +	if (sdiodev->func[0]->device == SDIO_DEVICE_ID_BROADCOM_4335_4339 && +	    addr == CORE_CC_REG(SI_ENUM_BASE, chipid)) { +		rev = (val & CID_REV_MASK) >> CID_REV_SHIFT; +		if (rev >= 2) { +			val &= ~CID_ID_MASK; +			val |= BCM4339_CHIP_ID; +		} +	} +	return val;  } +static void brcmf_sdio_buscore_write32(void *ctx, u32 addr, u32 val) +{ +	struct brcmf_sdio_dev *sdiodev = ctx; + +	brcmf_sdiod_regwl(sdiodev, addr, val, NULL); +} + +static const struct brcmf_buscore_ops brcmf_sdio_buscore_ops = { +	.prepare = brcmf_sdio_buscoreprep, +	.exit_dl = brcmf_sdio_buscore_exitdl, +	.read32 = brcmf_sdio_buscore_read32, +	.write32 = brcmf_sdio_buscore_write32, +}; +  static bool -brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) +brcmf_sdio_probe_attach(struct brcmf_sdio *bus)  {  	u8 clkctl = 0;  	int err = 0; @@ -3606,23 +3754,21 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)  	u32 reg_val;  	u32 drivestrength; -	bus->alp_only = true; -  	sdio_claim_host(bus->sdiodev->func[1]);  	pr_debug("F1 signature read @0x18000000=0x%4x\n", -		 brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); +		 brcmf_sdiod_regrl(bus->sdiodev, SI_ENUM_BASE, NULL));  	/* -	 * Force PLL off until brcmf_sdio_chip_attach() +	 * Force PLL off until brcmf_chip_attach()  	 * programs PLL control regs  	 */ -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, -			 BRCMF_INIT_CLKCTL1, &err); +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +			  BRCMF_INIT_CLKCTL1, &err);  	if (!err) -		clkctl = brcmf_sdio_regrb(bus->sdiodev, -					  SBSDIO_FUNC1_CHIPCLKCSR, &err); +		clkctl = brcmf_sdiod_regrb(bus->sdiodev, +					   SBSDIO_FUNC1_CHIPCLKCSR, &err);  	if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {  		brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", @@ -3630,12 +3776,19 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)  		goto fail;  	} -	if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { -		brcmf_err("brcmf_sdio_chip_attach failed!\n"); +	/* SDIO register access works so moving +	 * state from UNKNOWN to DOWN. +	 */ +	brcmf_bus_change_state(bus->sdiodev->bus_if, BRCMF_BUS_DOWN); + +	bus->ci = brcmf_chip_attach(bus->sdiodev, &brcmf_sdio_buscore_ops); +	if (IS_ERR(bus->ci)) { +		brcmf_err("brcmf_chip_attach failed!\n"); +		bus->ci = NULL;  		goto fail;  	} -	if (brcmf_sdbrcm_kso_init(bus)) { +	if (brcmf_sdio_kso_init(bus)) {  		brcmf_err("error enabling KSO\n");  		goto fail;  	} @@ -3644,7 +3797,7 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)  		drivestrength = bus->sdiodev->pdata->drive_strength;  	else  		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; -	brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); +	brcmf_sdio_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength);  	/* Get info on the SOCRAM cores... */  	bus->ramsize = bus->ci->ramsize; @@ -3654,44 +3807,42 @@ brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)  	}  	/* Set card control so an SDIO card reset does a WLAN backplane reset */ -	reg_val = brcmf_sdio_regrb(bus->sdiodev, -				   SDIO_CCCR_BRCM_CARDCTRL, &err); +	reg_val = brcmf_sdiod_regrb(bus->sdiodev, +				    SDIO_CCCR_BRCM_CARDCTRL, &err);  	if (err)  		goto fail;  	reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; -	brcmf_sdio_regwb(bus->sdiodev, -			 SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); +	brcmf_sdiod_regwb(bus->sdiodev, +			  SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err);  	if (err)  		goto fail;  	/* set PMUControl so a backplane reset does PMU state reload */ -	reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, +	reg_addr = CORE_CC_REG(brcmf_chip_get_chipcommon(bus->ci)->base,  			       pmucontrol); -	reg_val = brcmf_sdio_regrl(bus->sdiodev, -				   reg_addr, -				   &err); +	reg_val = brcmf_sdiod_regrl(bus->sdiodev, reg_addr, &err);  	if (err)  		goto fail;  	reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); -	brcmf_sdio_regwl(bus->sdiodev, -			 reg_addr, -			 reg_val, -			 &err); +	brcmf_sdiod_regwl(bus->sdiodev, reg_addr, reg_val, &err);  	if (err)  		goto fail; -  	sdio_release_host(bus->sdiodev->func[1]);  	brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); +	/* allocate header buffer */ +	bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); +	if (!bus->hdrbuf) +		return false;  	/* Locate an appropriately-aligned portion of hdrbuf */  	bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], -				    BRCMF_SDALIGN); +				    bus->head_align);  	/* Set the poll and/or interrupt flags */  	bus->intr = true; @@ -3706,42 +3857,8 @@ fail:  	return false;  } -static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) -{ -	brcmf_dbg(TRACE, "Enter\n"); - -	sdio_claim_host(bus->sdiodev->func[1]); - -	/* Disable F2 to clear any intermediate frame state on the dongle */ -	brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_IOEx, -			 SDIO_FUNC_ENABLE_1, NULL); - -	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; -	bus->rxflow = false; - -	/* Done with backplane-dependent accesses, can drop clock... */ -	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); - -	sdio_release_host(bus->sdiodev->func[1]); - -	/* ...and initialize clock/power states */ -	bus->clkstate = CLK_SDONLY; -	bus->idletime = BRCMF_IDLE_INTERVAL; -	bus->idleclock = BRCMF_IDLE_ACTIVE; - -	/* Query the F2 block size, set roundup accordingly */ -	bus->blocksize = bus->sdiodev->func[2]->cur_blksize; -	bus->roundup = min(max_roundup, bus->blocksize); - -	/* SR state */ -	bus->sleeping = false; -	bus->sr_enabled = false; - -	return true; -} -  static int -brcmf_sdbrcm_watchdog_thread(void *data) +brcmf_sdio_watchdog_thread(void *data)  {  	struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -3751,9 +3868,10 @@ brcmf_sdbrcm_watchdog_thread(void *data)  		if (kthread_should_stop())  			break;  		if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { -			brcmf_sdbrcm_bus_watchdog(bus); +			brcmf_sdio_bus_watchdog(bus);  			/* Count the tick for reference */  			bus->sdcnt.tickcnt++; +			reinit_completion(&bus->watchdog_wait);  		} else  			break;  	} @@ -3761,7 +3879,7 @@ brcmf_sdbrcm_watchdog_thread(void *data)  }  static void -brcmf_sdbrcm_watchdog(unsigned long data) +brcmf_sdio_watchdog(unsigned long data)  {  	struct brcmf_sdio *bus = (struct brcmf_sdio *)data; @@ -3774,73 +3892,124 @@ brcmf_sdbrcm_watchdog(unsigned long data)  	}  } -static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) +static struct brcmf_bus_ops brcmf_sdio_bus_ops = { +	.stop = brcmf_sdio_bus_stop, +	.preinit = brcmf_sdio_bus_preinit, +	.txdata = brcmf_sdio_bus_txdata, +	.txctl = brcmf_sdio_bus_txctl, +	.rxctl = brcmf_sdio_bus_rxctl, +	.gettxq = brcmf_sdio_bus_gettxq, +}; + +static void brcmf_sdio_firmware_callback(struct device *dev, +					 const struct firmware *code, +					 void *nvram, u32 nvram_len)  { -	brcmf_dbg(TRACE, "Enter\n"); +	struct brcmf_bus *bus_if = dev_get_drvdata(dev); +	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; +	struct brcmf_sdio *bus = sdiodev->bus; +	int err = 0; +	u8 saveclk; -	if (bus->ci) { -		sdio_claim_host(bus->sdiodev->func[1]); -		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); -		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); -		sdio_release_host(bus->sdiodev->func[1]); -		brcmf_sdio_chip_detach(&bus->ci); -		if (bus->vars && bus->varsz) -			kfree(bus->vars); -		bus->vars = NULL; +	brcmf_dbg(TRACE, "Enter: dev=%s\n", dev_name(dev)); + +	/* try to download image and nvram to the dongle */ +	if (bus_if->state == BRCMF_BUS_DOWN) { +		bus->alp_only = true; +		err = brcmf_sdio_download_firmware(bus, code, nvram, nvram_len); +		if (err) +			goto fail; +		bus->alp_only = false;  	} -	brcmf_dbg(TRACE, "Disconnected\n"); -} +	if (!bus_if->drvr) +		return; -/* Detach and free everything */ -static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) -{ -	brcmf_dbg(TRACE, "Enter\n"); +	/* Start the watchdog timer */ +	bus->sdcnt.tickcnt = 0; +	brcmf_sdio_wd_timer(bus, BRCMF_WD_POLL_MS); -	if (bus) { -		/* De-register interrupt handler */ -		brcmf_sdio_intr_unregister(bus->sdiodev); +	sdio_claim_host(sdiodev->func[1]); -		cancel_work_sync(&bus->datawork); -		if (bus->brcmf_wq) -			destroy_workqueue(bus->brcmf_wq); +	/* Make sure backplane clock is on, needed to generate F2 interrupt */ +	brcmf_sdio_clkctl(bus, CLK_AVAIL, false); +	if (bus->clkstate != CLK_AVAIL) +		goto release; -		if (bus->sdiodev->bus_if->drvr) { -			brcmf_detach(bus->sdiodev->dev); -			brcmf_sdbrcm_release_dongle(bus); -		} +	/* Force clocks on backplane to be sure F2 interrupt propagates */ +	saveclk = brcmf_sdiod_regrb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, &err); +	if (!err) { +		brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +				  (saveclk | SBSDIO_FORCE_HT), &err); +	} +	if (err) { +		brcmf_err("Failed to force clock for F2: err %d\n", err); +		goto release; +	} -		brcmf_sdbrcm_release_malloc(bus); +	/* Enable function 2 (frame transfers) */ +	w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, +		  offsetof(struct sdpcmd_regs, tosbmailboxdata)); +	err = sdio_enable_func(sdiodev->func[SDIO_FUNC_2]); -		kfree(bus); + +	brcmf_dbg(INFO, "enable F2: err=%d\n", err); + +	/* If F2 successfully enabled, set core and enable interrupts */ +	if (!err) { +		/* Set up the interrupt mask and enable interrupts */ +		bus->hostintmask = HOSTINTMASK; +		w_sdreg32(bus, bus->hostintmask, +			  offsetof(struct sdpcmd_regs, hostintmask)); + +		brcmf_sdiod_regwb(sdiodev, SBSDIO_WATERMARK, 8, &err); +	} else { +		/* Disable F2 again */ +		sdio_disable_func(sdiodev->func[SDIO_FUNC_2]); +		goto release;  	} -	brcmf_dbg(TRACE, "Disconnected\n"); -} +	if (brcmf_chip_sr_capable(bus->ci)) { +		brcmf_sdio_sr_init(bus); +	} else { +		/* Restore previous clock setting */ +		brcmf_sdiod_regwb(sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, +				  saveclk, &err); +	} -static struct brcmf_bus_ops brcmf_sdio_bus_ops = { -	.stop = brcmf_sdbrcm_bus_stop, -	.init = brcmf_sdbrcm_bus_init, -	.txdata = brcmf_sdbrcm_bus_txdata, -	.txctl = brcmf_sdbrcm_bus_txctl, -	.rxctl = brcmf_sdbrcm_bus_rxctl, -	.gettxq = brcmf_sdbrcm_bus_gettxq, -}; +	if (err == 0) { +		err = brcmf_sdiod_intr_register(sdiodev); +		if (err != 0) +			brcmf_err("intr register failed:%d\n", err); +	} + +	/* If we didn't come up, turn off backplane clock */ +	if (err != 0) +		brcmf_sdio_clkctl(bus, CLK_NONE, false); + +	sdio_release_host(sdiodev->func[1]); + +	err = brcmf_bus_start(dev); +	if (err != 0) { +		brcmf_err("dongle is not responding\n"); +		goto fail; +	} +	return; + +release: +	sdio_release_host(sdiodev->func[1]); +fail: +	brcmf_dbg(TRACE, "failed: dev=%s, err=%d\n", dev_name(dev), err); +	device_release_driver(dev); +} -void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) +struct brcmf_sdio *brcmf_sdio_probe(struct brcmf_sdio_dev *sdiodev)  {  	int ret;  	struct brcmf_sdio *bus; -	struct brcmf_bus_dcmd *dlst; -	u32 dngl_txglom; -	u32 txglomalign = 0; -	u8 idx;  	brcmf_dbg(TRACE, "Enter\n"); -	/* We make an assumption about address window mappings: -	 * regsva == SI_ENUM_BASE*/ -  	/* Allocate private bus interface state */  	bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);  	if (!bus) @@ -3854,6 +4023,18 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)  	bus->txminmax = BRCMF_TXMINMAX;  	bus->tx_seq = SDPCM_SEQ_WRAP - 1; +	/* platform specific configuration: +	 *   alignments must be at least 4 bytes for ADMA +         */ +	bus->head_align = ALIGNMENT; +	bus->sgentry_align = ALIGNMENT; +	if (sdiodev->pdata) { +		if (sdiodev->pdata->sd_head_align > ALIGNMENT) +			bus->head_align = sdiodev->pdata->sd_head_align; +		if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) +			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; +	} +  	INIT_WORK(&bus->datawork, brcmf_sdio_dataworker);  	bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq");  	if (bus->brcmf_wq == NULL) { @@ -3862,24 +4043,25 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)  	}  	/* attempt to attach to the dongle */ -	if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { -		brcmf_err("brcmf_sdbrcm_probe_attach failed\n"); +	if (!(brcmf_sdio_probe_attach(bus))) { +		brcmf_err("brcmf_sdio_probe_attach failed\n");  		goto fail;  	}  	spin_lock_init(&bus->rxctl_lock); -	spin_lock_init(&bus->txqlock); +	spin_lock_init(&bus->txq_lock); +	sema_init(&bus->tx_seq_lock, 1);  	init_waitqueue_head(&bus->ctrl_wait);  	init_waitqueue_head(&bus->dcmd_resp_wait);  	/* Set up the watchdog timer */  	init_timer(&bus->timer);  	bus->timer.data = (unsigned long)bus; -	bus->timer.function = brcmf_sdbrcm_watchdog; +	bus->timer.function = brcmf_sdio_watchdog;  	/* Initialize watchdog thread */  	init_completion(&bus->watchdog_wait); -	bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, +	bus->watchdog_tsk = kthread_run(brcmf_sdio_watchdog_thread,  					bus, "brcmf_watchdog");  	if (IS_ERR(bus->watchdog_tsk)) {  		pr_warn("brcmf_watchdog thread failed to start\n"); @@ -3898,78 +4080,112 @@ void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)  	bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN;  	/* Attach to the common layer, reserve hdr space */ -	ret = brcmf_attach(bus->tx_hdrlen, bus->sdiodev->dev); +	ret = brcmf_attach(bus->sdiodev->dev);  	if (ret != 0) {  		brcmf_err("brcmf_attach failed\n");  		goto fail;  	} +	/* Query the F2 block size, set roundup accordingly */ +	bus->blocksize = bus->sdiodev->func[2]->cur_blksize; +	bus->roundup = min(max_roundup, bus->blocksize); +  	/* Allocate buffers */ -	if (!(brcmf_sdbrcm_probe_malloc(bus))) { -		brcmf_err("brcmf_sdbrcm_probe_malloc failed\n"); -		goto fail; +	if (bus->sdiodev->bus_if->maxctl) { +		bus->sdiodev->bus_if->maxctl += bus->roundup; +		bus->rxblen = +		    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), +			    ALIGNMENT) + bus->head_align; +		bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); +		if (!(bus->rxbuf)) { +			brcmf_err("rxbuf allocation failed\n"); +			goto fail; +		}  	} -	if (!(brcmf_sdbrcm_probe_init(bus))) { -		brcmf_err("brcmf_sdbrcm_probe_init failed\n"); -		goto fail; -	} +	sdio_claim_host(bus->sdiodev->func[1]); + +	/* Disable F2 to clear any intermediate frame state on the dongle */ +	sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); + +	bus->rxflow = false; + +	/* Done with backplane-dependent accesses, can drop clock... */ +	brcmf_sdiod_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); + +	sdio_release_host(bus->sdiodev->func[1]); + +	/* ...and initialize clock/power states */ +	bus->clkstate = CLK_SDONLY; +	bus->idletime = BRCMF_IDLE_INTERVAL; +	bus->idleclock = BRCMF_IDLE_ACTIVE; + +	/* SR state */ +	bus->sleeping = false; +	bus->sr_enabled = false;  	brcmf_sdio_debugfs_create(bus);  	brcmf_dbg(INFO, "completed!!\n"); -	/* sdio bus core specific dcmd */ -	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); -	dlst = kzalloc(sizeof(struct brcmf_bus_dcmd), GFP_KERNEL); -	if (dlst) { -		if (bus->ci->c_inf[idx].rev < 12) { -			/* for sdio core rev < 12, disable txgloming */ -			dngl_txglom = 0; -			dlst->name = "bus:txglom"; -			dlst->param = (char *)&dngl_txglom; -			dlst->param_len = sizeof(u32); -		} else { -			/* otherwise, set txglomalign */ -			if (sdiodev->pdata) -				txglomalign = sdiodev->pdata->sd_sgentry_align; -			/* SDIO ADMA requires at least 32 bit alignment */ -			if (txglomalign < 4) -				txglomalign = 4; -			dlst->name = "bus:txglomalign"; -			dlst->param = (char *)&txglomalign; -			dlst->param_len = sizeof(u32); -		} -		list_add(&dlst->list, &bus->sdiodev->bus_if->dcmd_list); -	} - -	/* if firmware path present try to download and bring up bus */ -	ret = brcmf_bus_start(bus->sdiodev->dev); +	ret = brcmf_fw_get_firmwares(sdiodev->dev, BRCMF_FW_REQUEST_NVRAM, +				     brcmf_sdio_get_fwname(bus->ci, +							   BRCMF_FIRMWARE_BIN), +				     brcmf_sdio_get_fwname(bus->ci, +							   BRCMF_FIRMWARE_NVRAM), +				     brcmf_sdio_firmware_callback);  	if (ret != 0) { -		brcmf_err("dongle is not responding\n"); +		brcmf_err("async firmware request failed: %d\n", ret);  		goto fail;  	}  	return bus;  fail: -	brcmf_sdbrcm_release(bus); +	brcmf_sdio_remove(bus);  	return NULL;  } -void brcmf_sdbrcm_disconnect(void *ptr) +/* Detach and free everything */ +void brcmf_sdio_remove(struct brcmf_sdio *bus)  { -	struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr; -  	brcmf_dbg(TRACE, "Enter\n"); -	if (bus) -		brcmf_sdbrcm_release(bus); +	if (bus) { +		/* De-register interrupt handler */ +		brcmf_sdiod_intr_unregister(bus->sdiodev); + +		brcmf_detach(bus->sdiodev->dev); + +		cancel_work_sync(&bus->datawork); +		if (bus->brcmf_wq) +			destroy_workqueue(bus->brcmf_wq); + +		if (bus->ci) { +			if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { +				sdio_claim_host(bus->sdiodev->func[1]); +				brcmf_sdio_clkctl(bus, CLK_AVAIL, false); +				/* Leave the device in state where it is +				 * 'quiet'. This is done by putting it in +				 * download_state which essentially resets +				 * all necessary cores. +				 */ +				msleep(20); +				brcmf_chip_enter_download(bus->ci); +				brcmf_sdio_clkctl(bus, CLK_NONE, false); +				sdio_release_host(bus->sdiodev->func[1]); +			} +			brcmf_chip_detach(bus->ci); +		} + +		kfree(bus->rxbuf); +		kfree(bus->hdrbuf); +		kfree(bus); +	}  	brcmf_dbg(TRACE, "Disconnected\n");  } -void -brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) +void brcmf_sdio_wd_timer(struct brcmf_sdio *bus, uint wdtick)  {  	/* Totally stop the timer */  	if (!wdtick && bus->wd_timer_valid) { @@ -3980,7 +4196,7 @@ brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick)  	}  	/* don't start the wd until fw is loaded */ -	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) +	if (bus->sdiodev->bus_if->state != BRCMF_BUS_DATA)  		return;  	if (wdtick) {  | 
