15b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
25b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Copyright (c) 2010 Broadcom Corporation
35b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
45b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Permission to use, copy, modify, and/or distribute this software for any
55b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * purpose with or without fee is hereby granted, provided that the above
65b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * copyright notice and this permission notice appear in all copies.
75b435de0d786869c95d1962121af0d7df2542009Arend van Spriel *
85b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
95b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1702f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
1802f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches
195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/types.h>
205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/kernel.h>
215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/kthread.h>
225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/printk.h>
235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/pci_ids.h>
245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/netdevice.h>
255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/interrupt.h>
265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/sched.h>
275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/mmc/sdio.h>
285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/mmc/sdio_func.h>
295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/mmc/card.h>
305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/semaphore.h>
315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <linux/firmware.h>
32b7a57e762ecf5d9971549ab3f9eb66b559840e72Stephen Rothwell#include <linux/module.h>
3399ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin#include <linux/bcma/bcma.h>
345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <asm/unaligned.h>
355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <defs.h>
365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_wifi.h>
375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcmu_utils.h>
385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <brcm_hw_ids.h>
395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <soc.h>
405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "sdio_host.h"
41a83369b6e1e7285edd5217601a0618b9a43bdc4bFranky Lin#include "sdio_chip.h"
425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define DCMD_RESP_TIMEOUT  2000	/* In milli second */
445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
458ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_TRAP_INFO_SIZE	80
485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CBUF_LEN	(128)
505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
515b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct rte_log_le {
525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 buf;		/* Can't be pointer on (64-bit) hosts */
535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 buf_size;
545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 idx;
555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *_buf_compat;	/* Redundant pointer for backward compat. */
565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
585b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct rte_console {
595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Virtual UART
605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * When there is no UART (e.g. Quickturn),
615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * the host should write a complete
625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * input line directly into cbuf and then write
635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * the length into vcons_in.
645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * This may also be used when there is a real UART
655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * (at risk of conflicting with
665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * the real UART).  vcons_out is currently unused.
675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint vcons_in;
695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint vcons_out;
705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Output (logging) buffer
725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Console output is written to a ring buffer log_buf at index log_idx.
735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * The host may read the output when it sees log_idx advance.
745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Output will be lost if the output wraps around faster than the host
755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * polls.
765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct rte_log_le log_le;
785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Console input line buffer
805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Characters are read one at a time into cbuf
815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * until <CR> is received, then
825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * the buffer is processed as a command line.
835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Also used for virtual UART.
845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint cbuf_idx;
865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char cbuf[CBUF_LEN];
875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
898ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include <chipcommon.h>
915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_bus.h"
935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#include "dhd_dbg.h"
945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define TXQLEN		2048	/* bulk tx queue length */
965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define TXHI		(TXQLEN - 256)	/* turn on flow control above TXHI */
975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define TXLOW		(TXHI - 256)	/* turn off flow control below TXLOW */
985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define PRIOMASK	7
995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define TXRETRIES	2	/* # of retries for tx frames */
1015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_RXBOUND	50	/* Default for max rx frames in
1035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 one scheduling */
1045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_TXBOUND	20	/* Default for max tx frames in
1065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 one scheduling */
1075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_TXMINMAX	1	/* Max tx frames if rx still pending */
1095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define MEMBLOCK	2048	/* Block size used for downloading
1115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 of dongle image */
1125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define MAX_DATA_BUF	(32 * 1024)	/* Must be large enough to hold
1135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 biggest possible glom */
1145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_FIRSTREAD	(1 << 6)
1165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* SBSDIO_DEVICE_CTL */
1195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 1: device will assert busy signal when receiving CMD53 */
1215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_SETBUSY		0x01
1225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 1: assertion of sdio interrupt is synchronous to the sdio clock */
1235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_SPI_INTR_SYNC	0x02
1245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 1: mask all interrupts to host except the chipActive (rev 8) */
1255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_CA_INT_ONLY	0x04
1265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* 1: isolate internal sdio signals, put external pads in tri-state; requires
1275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * sdio bus power cycle to clear (rev 9) */
1285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_PADS_ISO		0x08
1295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Force SD->SB reset mapping (rev 11) */
1305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_SB_RST_CTL	0x30
1315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*   Determined by CoreControl bit */
1325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_RST_CORECTL	0x00
1335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*   Force backplane reset */
1345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_RST_BPRESET	0x10
1355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*   Force no backplane reset */
1365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_DEVCTL_RST_NOBPRESET	0x20
1375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* direct(mapped) cis space */
1395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* MAPPED common CIS address */
1415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_CIS_BASE_COMMON		0x1000
1425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* maximum bytes in one CIS */
1435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_CIS_SIZE_LIMIT		0x200
1445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* cis offset addr is < 17 bits */
1455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_CIS_OFT_ADDR_MASK	0x1FFFF
1465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* manfid tuple length, include tuple, link bytes */
1485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SBSDIO_CIS_MANFID_TUPLE_LEN	6
1495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* intstatus */
1515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW0	(1 << 0)	/* To SB Mail S/W interrupt 0 */
1525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW1	(1 << 1)	/* To SB Mail S/W interrupt 1 */
1535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW2	(1 << 2)	/* To SB Mail S/W interrupt 2 */
1545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW3	(1 << 3)	/* To SB Mail S/W interrupt 3 */
1555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW_MASK	0x0000000f	/* To SB Mail S/W interrupts mask */
1565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SMB_SW_SHIFT	0	/* To SB Mail S/W interrupts shift */
1575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW0	(1 << 4)	/* To Host Mail S/W interrupt 0 */
1585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW1	(1 << 5)	/* To Host Mail S/W interrupt 1 */
1595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW2	(1 << 6)	/* To Host Mail S/W interrupt 2 */
1605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW3	(1 << 7)	/* To Host Mail S/W interrupt 3 */
1615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW_MASK	0x000000f0	/* To Host Mail S/W interrupts mask */
1625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_SW_SHIFT	4	/* To Host Mail S/W interrupts shift */
1635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_WR_OOSYNC	(1 << 8)	/* Write Frame Out Of Sync */
1645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_RD_OOSYNC	(1 << 9)	/* Read Frame Out Of Sync */
1655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_PC		(1 << 10)	/* descriptor error */
1665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_PD		(1 << 11)	/* data error */
1675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_DE		(1 << 12)	/* Descriptor protocol Error */
1685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_RU		(1 << 13)	/* Receive descriptor Underflow */
1695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_RO		(1 << 14)	/* Receive fifo Overflow */
1705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_XU		(1 << 15)	/* Transmit fifo Underflow */
1715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_RI		(1 << 16)	/* Receive Interrupt */
1725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_BUSPWR	(1 << 17)	/* SDIO Bus Power Change (rev 9) */
1735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_XMTDATA_AVAIL (1 << 23)	/* bits in fifo */
1745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_XI		(1 << 24)	/* Transmit Interrupt */
1755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_RF_TERM	(1 << 25)	/* Read Frame Terminate */
1765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_WF_TERM	(1 << 26)	/* Write Frame Terminate */
1775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_PCMCIA_XU	(1 << 27)	/* PCMCIA Transmit FIFO Underflow */
1785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SBINT		(1 << 28)	/* sbintstatus Interrupt */
1795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_CHIPACTIVE	(1 << 29)	/* chip from doze to active state */
1805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_SRESET	(1 << 30)	/* CCCR RES interrupt */
1815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_IOE2		(1U << 31)	/* CCCR IOE2 Bit Changed */
1825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define	I_ERRORS	(I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
1835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_DMA		(I_RI | I_XI | I_ERRORS)
1845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* corecontrol */
1865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_CISRDY		(1 << 0)	/* CIS Ready */
1875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_BPRESEN		(1 << 1)	/* CCCR RES signal */
1885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_F2RDY		(1 << 2)	/* set CCCR IOR2 bit */
1895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_CLRPADSISO		(1 << 3)	/* clear SDIO pads isolation */
1905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_XMTDATAAVAIL_MODE	(1 << 4)
1915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CC_XMTDATAAVAIL_CTRL	(1 << 5)
1925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* SDA_FRAMECTRL */
1945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SFC_RF_TERM	(1 << 0)	/* Read Frame Terminate */
1955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SFC_WF_TERM	(1 << 1)	/* Write Frame Terminate */
1965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SFC_CRC4WOOS	(1 << 2)	/* CRC error for write out of sync */
1975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SFC_ABORTALL	(1 << 3)	/* Abort all in-progress frames */
1985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* HW frame tag */
2005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_FRAMETAG_LEN	4	/* 2 bytes len, 2 bytes check val */
2015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Total length of frame header for dongle protocol */
2035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_HDRLEN	(SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
2045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_RESERVE	(SDPCM_HDRLEN + BRCMF_SDALIGN)
2055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
2075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Software allocation of To SB Mailbox resources
2085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
2095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* tosbmailbox bits corresponding to intstatus bits */
2115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SMB_NAK		(1 << 0)	/* Frame NAK */
2125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SMB_INT_ACK	(1 << 1)	/* Host Interrupt ACK */
2135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SMB_USE_OOB	(1 << 2)	/* Use OOB Wakeup */
2145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SMB_DEV_INT	(1 << 3)	/* Miscellaneous Interrupt */
2155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* tosbmailboxdata */
2175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SMB_DATA_VERSION_SHIFT	16	/* host protocol version */
2185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
2205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Software allocation of To Host Mailbox resources
2215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
2225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* intstatus bits */
2245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_FC_STATE	I_HMB_SW0	/* Flow Control State */
2255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_FC_CHANGE	I_HMB_SW1	/* Flow Control State Changed */
2265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_FRAME_IND	I_HMB_SW2	/* Frame Indication */
2275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define I_HMB_HOST_INT	I_HMB_SW3	/* Miscellaneous Interrupt */
2285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* tohostmailboxdata */
2305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_NAKHANDLED	1	/* retransmit NAK'd frame */
2315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_DEVREADY	2	/* talk to host after enable */
2325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_FC		4	/* per prio flowcontrol update flag */
2335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_FWREADY	8	/* fw ready for protocol activity */
2345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_FCDATA_MASK	0xff000000
2365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_FCDATA_SHIFT	24
2375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_VERSION_MASK	0x00ff0000
2395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HMB_DATA_VERSION_SHIFT	16
2405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
2425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Software-defined protocol header
2435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
2445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Current protocol version */
2465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_PROT_VERSION	4
2475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* SW frame header */
2495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_PACKET_SEQUENCE(p)	(((u8 *)p)[0] & 0xff)
2505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_CHANNEL_MASK		0x00000f00
2525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_CHANNEL_SHIFT		8
2535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_PACKET_CHANNEL(p)		(((u8 *)p)[1] & 0x0f)
2545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_NEXTLEN_OFFSET		2
2565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
2585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_DOFFSET_OFFSET		3	/* Data Offset */
2595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_DOFFSET_VALUE(p)		(((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
2605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_DOFFSET_MASK		0xff000000
2615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_DOFFSET_SHIFT		24
2625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_FCMASK_OFFSET		4	/* Flow control */
2635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_FCMASK_VALUE(p)		(((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
2645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_WINDOW_OFFSET		5	/* Credit based fc */
2655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_WINDOW_VALUE(p)		(((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
2665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SWHEADER_LEN	8	/* SW header is 64 bits */
2685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* logical channel numbers */
2705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_CONTROL_CHANNEL	0	/* Control channel Id */
2715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_EVENT_CHANNEL	1	/* Asyc Event Indication Channel Id */
2725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_DATA_CHANNEL	2	/* Data Xmit/Recv Channel Id */
2735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_GLOM_CHANNEL	3	/* For coalesced packets */
2745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_TEST_CHANNEL	15	/* Reserved for test/debug packets */
2755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SEQUENCE_WRAP	256	/* wrap-around val for 8bit frame seq */
2775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_GLOMDESC(p)	(((u8 *)p)[1] & 0x80)
2795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
2815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Shared structure between dongle and the host.
2825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * The structure contains pointers to trap or assert information.
2835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
2845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SHARED_VERSION       0x0002
2855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SHARED_VERSION_MASK  0x00FF
2865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SHARED_ASSERT_BUILT  0x0100
2875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SHARED_ASSERT        0x0200
2885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDPCM_SHARED_TRAP          0x0400
2895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Space for header read, limit for data packets */
2915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define MAX_HDR_READ	(1 << 6)
2925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define MAX_RX_DATASZ	2048
2935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Maximum milliseconds to wait for F2 to come up */
2955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_WAIT_F2RDY	3000
2965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Bump up limit on waiting for HT to account for first startup;
2985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * if the image is doing a CRC calculation before programming the PMU
2995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * for HT availability, it could take a couple hundred ms more, so
3005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * max out at a 1 second (1000000us).
3015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
3025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#undef PMU_MAX_TRANSITION_DLY
3035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define PMU_MAX_TRANSITION_DLY 1000000
3045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Value for ChipClockCSR during initial setup */
3065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define BRCMF_INIT_CLKCTL1	(SBSDIO_FORCE_HW_CLKREQ_OFF |	\
3075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					SBSDIO_ALP_AVAIL_REQ)
3085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Flags for SDH calls */
3105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define F2SYNC	(SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
3115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31252e1409f72d629644b496db80c119e04ebabad90Arend van Spriel#define BRCMF_SDIO_FW_NAME	"brcm/brcmfmac-sdio.bin"
31352e1409f72d629644b496db80c119e04ebabad90Arend van Spriel#define BRCMF_SDIO_NV_NAME	"brcm/brcmfmac-sdio.txt"
31452e1409f72d629644b496db80c119e04ebabad90Arend van SprielMODULE_FIRMWARE(BRCMF_SDIO_FW_NAME);
31552e1409f72d629644b496db80c119e04ebabad90Arend van SprielMODULE_FIRMWARE(BRCMF_SDIO_NV_NAME);
3168dd939cade92647a7c87db5ae895a6e120258320Franky Lin
317382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin#define BRCMF_IDLE_IMMEDIATE	(-1)	/* Enter idle immediately */
318382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin#define BRCMF_IDLE_ACTIVE	0	/* Do not request any SD clock change
319382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin					 * when idle
320382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin					 */
321382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin#define BRCMF_IDLE_INTERVAL	1
322382a9e0f31235e4efd63977646da5c84e73febf7Franky Lin
3235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
3245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Conversion of 802.1D priority to precedence level
3255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
3265b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic uint prio2prec(u32 prio)
3275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ?
3295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	       (prio^2) : prio;
3305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
3315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* core registers */
3335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct sdpcmd_regs {
3345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 corecontrol;		/* 0x00, rev8 */
3355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 corestatus;			/* rev8 */
3365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[1];
3375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 biststatus;			/* rev8 */
3385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* PCMCIA access */
3405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 pcmciamesportaladdr;	/* 0x010, rev8 */
3415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[1];
3425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 pcmciamesportalmask;	/* rev8 */
3435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[1];
3445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 pcmciawrframebc;		/* rev8 */
3455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[1];
3465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 pcmciaunderflowtimer;	/* rev8 */
3475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[1];
3485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* interrupt */
3505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intstatus;			/* 0x020, rev8 */
3515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 hostintmask;		/* rev8 */
3525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intmask;			/* rev8 */
3535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 sbintstatus;		/* rev8 */
3545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 sbintmask;			/* rev8 */
3555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 funcintmask;		/* rev4 */
3565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[2];
3575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 tosbmailbox;		/* 0x040, rev8 */
3585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 tohostmailbox;		/* rev8 */
3595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 tosbmailboxdata;		/* rev8 */
3605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 tohostmailboxdata;		/* rev8 */
3615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* synchronized access to registers in SDIO clock domain */
3635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 sdioaccess;			/* 0x050, rev8 */
3645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[3];
3655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* PCMCIA frame control */
3675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 pcmciaframectrl;		/* 0x060, rev8 */
3685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 PAD[3];
3695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 pcmciawatermark;		/* rev8 */
3705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 PAD[155];
3715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* interrupt batching control */
3735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intrcvlazy;			/* 0x100, rev8 */
3745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[3];
3755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* counters */
3775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 cmd52rd;			/* 0x110, rev8 */
3785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 cmd52wr;			/* rev8 */
3795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 cmd53rd;			/* rev8 */
3805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 cmd53wr;			/* rev8 */
3815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 abort;			/* rev8 */
3825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 datacrcerror;		/* rev8 */
3835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 rdoutofsync;		/* rev8 */
3845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 wroutofsync;		/* rev8 */
3855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 writebusy;			/* rev8 */
3865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 readwait;			/* rev8 */
3875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 readterm;			/* rev8 */
3885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 writeterm;			/* rev8 */
3895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[40];
3905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 clockctlstatus;		/* rev8 */
3915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[7];
3925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[128];			/* DMA engines */
3945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* SDIO/PCMCIA CIS region */
3965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char cis[512];			/* 0x400-0x5ff, rev6 */
3975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* PCMCIA function control registers */
3995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char pcmciafcr[256];		/* 0x600-6ff, rev6 */
4005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[55];
4015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* PCMCIA backplane access */
4035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplanecsr;		/* 0x76E, rev6 */
4045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplaneaddr0;		/* rev6 */
4055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplaneaddr1;		/* rev6 */
4065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplaneaddr2;		/* rev6 */
4075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplaneaddr3;		/* rev6 */
4085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplanedata0;		/* rev6 */
4095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplanedata1;		/* rev6 */
4105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplanedata2;		/* rev6 */
4115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 backplanedata3;		/* rev6 */
4125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[31];
4135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* sprom "size" & "blank" info */
4155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 spromstatus;		/* 0x7BE, rev2 */
4165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 PAD[464];
4175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 PAD[0x80];
4195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
4205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4218ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
4225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Device console log buffer state */
4235b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct brcmf_console {
4245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint count;		/* Poll interval msec counter */
4255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint log_addr;		/* Log struct address (fixed) */
4265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct rte_log_le log_le;	/* Log struct (host copy) */
4275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint bufsize;		/* Size of log buffer */
4285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *buf;		/* Log buffer (host copy) */
4295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint last;		/* Last buffer read index */
4305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
4318ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
4325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct sdpcm_shared {
4345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 flags;
4355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 trap_addr;
4365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 assert_exp_addr;
4375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 assert_file_addr;
4385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 assert_line;
4395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 console_addr;	/* Address of struct rte_console */
4405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 msgtrace_addr;
4415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 tag[32];
4425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
4435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4445b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstruct sdpcm_shared_le {
4455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 flags;
4465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 trap_addr;
4475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 assert_exp_addr;
4485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 assert_file_addr;
4495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 assert_line;
4505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 console_addr;	/* Address of struct rte_console */
4515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 msgtrace_addr;
4525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 tag[32];
4535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
4545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* misc chip info needed by some of the routines */
4575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Private data for SDIO bus interaction */
458e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstruct brcmf_sdio {
4595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_sdio_dev *sdiodev;	/* sdio device handler */
4605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct chip_info *ci;	/* Chip info struct */
4615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *vars;		/* Variables (from CIS and/or other) */
4625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint varsz;		/* Size of variables buffer */
4635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */
4655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 hostintmask;	/* Copy of Host Interrupt Mask */
4675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intstatus;	/* Intstatus bits (events) pending */
4685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool dpc_sched;		/* Indicates DPC schedule (intrpt rcvd) */
4695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool fcstate;		/* State of dongle flow-control */
4705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint blocksize;		/* Block size of SDIO transfers */
4725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint roundup;		/* Max roundup limit */
4735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct pktq txq;	/* Queue length used for flow-control */
4755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 flowcontrol;	/* per prio flow control bitmask */
4765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 tx_seq;		/* Transmit sequence number (next) */
4775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 tx_max;		/* Maximum transmit sequence allowed */
4785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
4805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *rxhdr;		/* Header of current rx frame (in hdrbuf) */
4815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 nextlen;		/* Next Read Len from last header */
4825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 rx_seq;		/* Receive sequence number (expected) */
4835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool rxskip;		/* Skip receive (awaiting NAK ACK) */
4845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxbound;		/* Rx frames to read before resched */
4865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint txbound;		/* Tx frames to send before resched */
4875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint txminmax;
4885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct sk_buff *glomd;	/* Packet containing glomming descriptor */
490b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel	struct sk_buff_head glom; /* Packet list for glommed superframe */
4915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint glomerr;		/* Glom packet read errors */
4925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *rxbuf;		/* Buffer for receiving control packets */
4945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxblen;		/* Allocated length of rxbuf */
4955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *rxctl;		/* Aligned pointer into rxbuf */
4965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *databuf;		/* Buffer for receiving big glom packet */
4975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *dataptr;		/* Aligned pointer into databuf */
4985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxlen;		/* Length of valid data in buffer */
4995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 sdpcm_ver;	/* Bus protocol reported by dongle */
5015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool intr;		/* Use interrupts */
5035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool poll;		/* Use polling */
5045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool ipend;		/* Device interrupt is pending */
5055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint intrcount;		/* Count of device interrupt callbacks */
5065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint lastintrs;		/* Count as of last watchdog timer */
5075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint spurious;		/* Count of spurious interrupts */
5085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint pollrate;		/* Ticks between device polls */
5095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint polltick;		/* Tick counter */
5105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint pollcnt;		/* Count of active polls */
5115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5128ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
5135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint console_interval;
5145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_console console;	/* Console output polling support */
5155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint console_addr;	/* Console address from shared struct */
5168ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
5175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint regfails;		/* Count of R_REG failures */
5195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint clkstate;		/* State of sd and backplane clock(s) */
5215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool activity;		/* Activity flag for clock down */
5225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	s32 idletime;		/* Control for activity timeout */
5235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	s32 idlecount;	/* Activity timeout counter */
5245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	s32 idleclock;	/* How to set bus driver when idle */
5255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	s32 sd_rxchain;
5265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool use_rxchain;	/* If brcmf should use PKT chains */
5275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool sleeping;		/* Is SDIO bus sleeping? */
5285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool rxflow_mode;	/* Rx flow control mode */
5295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool rxflow;		/* Is rx flow control on */
5305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool alp_only;		/* Don't use HT clock (ALP only) */
5315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
5325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool usebufpool;
5335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Some additional counters */
5355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint tx_sderrs;		/* Count of tx attempts with sd errors */
5365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint fcqueued;		/* Tx packets that got queued */
5375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxrtx;		/* Count of rtx requests (NAK to dongle) */
5385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rx_toolong;	/* Receive frames too long to receive */
5395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxc_errors;	/* SDIO errors when reading control frames */
5405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rx_hdrfail;	/* SDIO errors on header reads */
5415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rx_badhdr;		/* Bad received headers (roosync?) */
5425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rx_badseq;		/* Mismatched rx sequence number */
5435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint fc_rcvd;		/* Number of flow-control events received */
5445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint fc_xoff;		/* Number which turned on flow-control */
5455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint fc_xon;		/* Number which turned off flow-control */
5465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxglomfail;	/* Failed deglom attempts */
5475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxglomframes;	/* Number of glom frames (superframes) */
5485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxglompkts;	/* Number of packets from glom frames */
5495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint f2rxhdrs;		/* Number of header reads */
5505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint f2rxdata;		/* Number of frame data reads */
5515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint f2txdata;		/* Number of f2 frame writes */
5525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint f1regdata;		/* Number of f1 register accesses */
55328a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	uint tickcnt;		/* Number of watchdog been schedule */
55428a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	unsigned long tx_ctlerrs;	/* Err of sending ctrl frames */
55528a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	unsigned long tx_ctlpkts;	/* Ctrl frames sent to dongle */
55628a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	unsigned long rx_ctlerrs;	/* Err of processing rx ctrl frames */
55728a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	unsigned long rx_ctlpkts;	/* Ctrl frames processed from dongle */
55828a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	unsigned long rx_readahead_cnt;	/* Number of packets where header
55928a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin					 * read-ahead was used. */
5605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *ctrl_frame_buf;
5625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 ctrl_frame_len;
5635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool ctrl_frame_stat;
5645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spinlock_t txqlock;
5665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wait_queue_head_t ctrl_wait;
5675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	wait_queue_head_t dcmd_resp_wait;
5685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct timer_list timer;
5705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct completion watchdog_wait;
5715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct task_struct *watchdog_tsk;
5725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool wd_timer_valid;
5735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint save_ms;
5745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct task_struct *dpc_tsk;
5765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct completion dpc_wait;
577b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	struct list_head dpc_tsklst;
578b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	spinlock_t dpc_tl_lock;
5795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct semaphore sdsem;
5815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	const struct firmware *firmware;
5835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 fw_ptr;
584c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin
585c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin	bool txoff;		/* Transmit flow-controlled */
5865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel};
5875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* clkstate */
5895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CLK_NONE	0
5905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CLK_SDONLY	1
5915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CLK_PENDING	2	/* Not used yet */
5925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CLK_AVAIL	3
5935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5948ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
5955b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int qcount[NUMPRIO];
5965b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int tx_packets[NUMPRIO];
5978ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
5985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
5995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define SDIO_DRIVE_STRENGTH	6	/* in milliamps */
6005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
6025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Retry count for register access failures */
6045b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const uint retry_limit = 2;
6055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Limit on rounding up frames */
6075b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic const uint max_roundup = 512;
6085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define ALIGNMENT  4
6105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6115b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void pkt_align(struct sk_buff *p, int len, int align)
6125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint datalign;
6145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	datalign = (unsigned long)(p->data);
6155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	datalign = roundup(datalign, (align)) - datalign;
6165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (datalign)
6175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_pull(p, datalign);
6185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__skb_trim(p, len);
6195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* To check if there's window offered */
622e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic bool data_ok(struct brcmf_sdio *bus)
6235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return (u8)(bus->tx_max - bus->tx_seq) != 0 &&
6255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	       ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0;
6265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
6295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Reads a register in the SDIO hardware block. This block occupies a series of
6305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * adresses on the 32 bit backplane bus.
6315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
6325b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
633e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linr_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 reg_offset, u32 *retryvar)
6345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
63599ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
6365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*retryvar = 0;
6375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	do {
6385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*regvar = brcmf_sdcard_reg_read(bus->sdiodev,
63999ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin				bus->ci->c_inf[idx].base + reg_offset,
64099ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin				sizeof(u32));
6415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} while (brcmf_sdcard_regfail(bus->sdiodev) &&
6425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 (++(*retryvar) <= retry_limit));
6435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*retryvar) {
6445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->regfails += (*retryvar-1);
6455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (*retryvar > retry_limit) {
6465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "FAILED READ %Xh\n", reg_offset);
6475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			*regvar = 0;
6485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
6495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
6505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6525b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
653e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linw_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset, u32 *retryvar)
6545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
65599ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
6565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*retryvar = 0;
6575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	do {
6585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_reg_write(bus->sdiodev,
65999ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin				       bus->ci->c_inf[idx].base + reg_offset,
6605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       sizeof(u32), regval);
6615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} while (brcmf_sdcard_regfail(bus->sdiodev) &&
6625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 (++(*retryvar) <= retry_limit));
6635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*retryvar) {
6645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->regfails += (*retryvar-1);
6655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (*retryvar > retry_limit)
6665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "FAILED REGISTER WRITE %Xh\n",
6675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  reg_offset);
6685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
6695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define PKT_AVAILABLE()		(intstatus & I_HMB_FRAME_IND)
6725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)
6745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Packet free applicable unconditionally for sdio and sdspi.
6765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Conditional if bufpool was present for gspi bus.
6775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel */
678e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_pktfree2(struct brcmf_sdio *bus, struct sk_buff *pkt)
6795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->usebufpool)
6815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmu_pkt_buf_free_skb(pkt);
6825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
6835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Turn backplane clock on or off */
685e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok)
6865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
6875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int err;
6885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 clkctl, clkreq, devctl;
6895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned long timeout;
6905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
6925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	clkctl = 0;
6945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
6955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (on) {
6965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Request HT Avail */
6975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		clkreq =
6985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
6995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
7015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
7025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
7035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HT Avail request error: %d\n", err);
7045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBADE;
7055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check current status */
7085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		clkctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
7095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       SBSDIO_FUNC1_CHIPCLKCSR, &err);
7105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
7115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HT Avail read error: %d\n", err);
7125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBADE;
7135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Go to pending and await interrupt if appropriate */
7165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
7175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Allow only clock-available interrupt */
7185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
7195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					SDIO_FUNC_1,
7205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					SBSDIO_DEVICE_CTL, &err);
7215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (err) {
7225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "Devctl error setting CA: %d\n",
7235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  err);
7245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				return -EBADE;
7255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
7265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
7285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
7295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       SBSDIO_DEVICE_CTL, devctl, &err);
7305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "CLKCTL: set PENDING\n");
7315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->clkstate = CLK_PENDING;
7325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return 0;
7345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (bus->clkstate == CLK_PENDING) {
7355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Cancel CA-only interrupt filter */
7365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl =
7375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
7385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						  SBSDIO_DEVICE_CTL, &err);
7395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
7405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
7415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				SBSDIO_DEVICE_CTL, devctl, &err);
7425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Otherwise, wait here (polling) for HT Avail */
7455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		timeout = jiffies +
7465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000);
7475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
7485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			clkctl = brcmf_sdcard_cfg_read(bus->sdiodev,
7495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						       SDIO_FUNC_1,
7505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						       SBSDIO_FUNC1_CHIPCLKCSR,
7515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						       &err);
7525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (time_after(jiffies, timeout))
7535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
7545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			else
7555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				usleep_range(5000, 10000);
7565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
7585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HT Avail request error: %d\n", err);
7595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBADE;
7605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
7625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HT Avail timeout (%d): clkctl 0x%02x\n",
7635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  PMU_MAX_TRANSITION_DLY, clkctl);
7645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBADE;
7655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Mark clock available */
7685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->clkstate = CLK_AVAIL;
7695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "CLKCTL: turned ON\n");
7705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7718ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#if defined(DEBUG)
77223677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches		if (!bus->alp_only) {
7735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (SBSDIO_ALPONLY(clkctl))
7745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "HT Clock should be on\n");
7755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7768ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* defined (DEBUG) */
7775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->activity = true;
7795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
7805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		clkreq = 0;
7815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->clkstate == CLK_PENDING) {
7835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Cancel CA-only interrupt filter */
7845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
7855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					SDIO_FUNC_1,
7865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					SBSDIO_DEVICE_CTL, &err);
7875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
7885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
7895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				SBSDIO_DEVICE_CTL, devctl, &err);
7905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
7915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
7925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->clkstate = CLK_SDONLY;
7935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
7945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
7955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "CLKCTL: turned OFF\n");
7965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
7975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "Failed access turning clock off: %d\n",
7985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  err);
7995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBADE;
8005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
8015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
8035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Change idle/active SD state */
806e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on)
8075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
8095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (on)
8115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->clkstate = CLK_SDONLY;
8125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
8135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->clkstate = CLK_NONE;
8145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
8165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Transition SD and backplane clock readiness */
819e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok)
8205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8218ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
8225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint oldstate = bus->clkstate;
8238ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
8245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
8265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Early exit if we're already there */
8285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->clkstate == target) {
8295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (target == CLK_AVAIL) {
8305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
8315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->activity = true;
8325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
8335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
8345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	switch (target) {
8375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case CLK_AVAIL:
8385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Make sure SD clock is available */
8395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->clkstate == CLK_NONE)
8405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_sdclk(bus, true);
8415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Now request HT Avail on the backplane */
8425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_htclk(bus, true, pendok);
8435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
8445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->activity = true;
8455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
8465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case CLK_SDONLY:
8485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Remove HT request, or bring up SD clock */
8495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->clkstate == CLK_NONE)
8505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_sdclk(bus, true);
8515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else if (bus->clkstate == CLK_AVAIL)
8525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_htclk(bus, false, false);
8535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
8545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "request for %d -> %d\n",
8555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bus->clkstate, target);
8565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
8575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
8585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	case CLK_NONE:
8605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Make sure to remove HT request */
8615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->clkstate == CLK_AVAIL)
8625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_htclk(bus, false, false);
8635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Now remove the SD clock */
8645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_sdclk(bus, false);
8655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_wd_timer(bus, 0);
8665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		break;
8675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
8688ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
8695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "%d -> %d\n", oldstate, bus->clkstate);
8708ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
8715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
8735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
8745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
875e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_bussleep(struct brcmf_sdio *bus, bool sleep)
8765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
8775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
8785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "request %s (currently %s)\n",
8805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  sleep ? "SLEEP" : "WAKE",
8815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  bus->sleeping ? "SLEEP" : "WAKE");
8825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Done if we're already in the requested state */
8845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (sleep == bus->sleeping)
8855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
8865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Going to sleep: set the alarm and turn off the lights... */
8885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (sleep) {
8895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Don't sleep if something is pending */
8905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
8915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -EBUSY;
8925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Make sure the controller has the bus up */
8945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
8955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
8965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Tell device to start using OOB wakeup */
8975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, SMB_USE_OOB,
8985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
8995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (retries > retry_limit)
9005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n");
9015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Turn off our contribution to the HT clock request */
9035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
9045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
9065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			SBSDIO_FUNC1_CHIPCLKCSR,
9075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
9085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Isolate the bus */
910718897eb3f90a47a56acb504762d521388a3231cFranky Lin		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
911718897eb3f90a47a56acb504762d521388a3231cFranky Lin			SBSDIO_DEVICE_CTL,
912718897eb3f90a47a56acb504762d521388a3231cFranky Lin			SBSDIO_DEVCTL_PADS_ISO, NULL);
9135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Change state */
9155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->sleeping = true;
9165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
9185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Waking up: bus power up is ok, set local state */
9195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
9215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
9225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Make sure the controller has the bus up */
9245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
9255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Send misc interrupt to indicate OOB not needed */
9275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, tosbmailboxdata),
9285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  &retries);
9295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (retries <= retry_limit)
9305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			w_sdreg32(bus, SMB_DEV_INT,
9315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  offsetof(struct sdpcmd_regs, tosbmailbox),
9325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  &retries);
9335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (retries > retry_limit)
9355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "CANNOT SIGNAL CHIP TO CLEAR OOB!!\n");
9365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Make sure we have SD bus access */
9385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
9395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Change state */
9415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->sleeping = false;
9425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
9455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
9465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
947e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void bus_wake(struct brcmf_sdio *bus)
9485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
9495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->sleeping)
9505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_bussleep(bus, false);
9515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
9525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
953e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus)
9545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
9555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intstatus = 0;
9565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 hmb_data;
9575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 fcbits;
9585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
9595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
9615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Read mailbox data and ack that we did so */
9635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	r_sdreg32(bus, &hmb_data,
9645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  offsetof(struct sdpcmd_regs, tohostmailboxdata), &retries);
9655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (retries <= retry_limit)
9675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, SMB_INT_ACK,
9685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
9695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->f1regdata += 2;
9705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Dongle recomposed rx frames, accept them again */
9725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (hmb_data & HMB_DATA_NAKHANDLED) {
9735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "Dongle reports NAK handled, expect rtx of %d\n",
9745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  bus->rx_seq);
9755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!bus->rxskip)
9765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "unexpected NAKHANDLED!\n");
9775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxskip = false;
9795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus |= I_HMB_FRAME_IND;
9805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/*
9835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * DEVREADY does not occur with gSPI.
9845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
9855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
9865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->sdpcm_ver =
9875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    (hmb_data & HMB_DATA_VERSION_MASK) >>
9885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    HMB_DATA_VERSION_SHIFT;
9895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
9905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "Version mismatch, dongle reports %d, "
9915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  "expecting %d\n",
9925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bus->sdpcm_ver, SDPCM_PROT_VERSION);
9935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
9945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "Dongle ready, protocol version %d\n",
9955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bus->sdpcm_ver);
9965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
9975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
9985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/*
9995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Flow Control has been moved into the RX headers and this out of band
10005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * method isn't used any more.
10015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * remaining backward compatible with older dongles.
10025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
10035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (hmb_data & HMB_DATA_FC) {
10045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
10055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel							HMB_DATA_FCDATA_SHIFT;
10065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (fcbits & ~bus->flowcontrol)
10085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->fc_xoff++;
10095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->flowcontrol & ~fcbits)
10115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->fc_xon++;
10125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->fc_rcvd++;
10145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->flowcontrol = fcbits;
10155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Shouldn't be any others */
10185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (hmb_data & ~(HMB_DATA_DEVREADY |
10195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 HMB_DATA_NAKHANDLED |
10205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 HMB_DATA_FC |
10215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 HMB_DATA_FWREADY |
10225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK))
10235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Unknown mailbox data content: 0x%02x\n",
10245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  hmb_data);
10255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return intstatus;
10275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
10285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1029e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx)
10305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
10315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
10325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 lastrbc;
10335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 hi, lo;
10345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int err;
10355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(ERROR, "%sterminate frame%s\n",
10375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  abort ? "abort command, " : "",
10385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  rtx ? ", send NAK" : "");
10395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (abort)
10415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
10425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
10445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SBSDIO_FUNC1_FRAMECTRL,
10455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SFC_RF_TERM, &err);
10465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->f1regdata++;
10475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Wait until the packet has been flushed (device/FIFO stable) */
10495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (lastrbc = retries = 0xffff; retries > 0; retries--) {
10505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		hi = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
10515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					   SBSDIO_FUNC1_RFRAMEBCHI, NULL);
10525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		lo = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
10535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					   SBSDIO_FUNC1_RFRAMEBCLO, NULL);
10545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata += 2;
10555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((hi == 0) && (lo == 0))
10575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
10585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
10605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "count growing: last 0x%04x now 0x%04x\n",
10615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  lastrbc, (hi << 8) + lo);
10625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
10635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		lastrbc = (hi << 8) + lo;
10645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!retries)
10675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "count never zeroed: last 0x%04x\n", lastrbc);
10685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
10695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "flush took %d iterations\n", 0xffff - retries);
10705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rtx) {
10725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxrtx++;
10735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, SMB_NAK,
10745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, tosbmailbox), &retries);
10755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata++;
10775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (retries <= retry_limit)
10785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rxskip = true;
10795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
10805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Clear partial in any case */
10825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->nextlen = 0;
10835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
10845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If we can't reach the device, signal failure */
10855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (err || brcmf_sdcard_regfail(bus->sdiodev))
1086712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
10875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
10885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
108920e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel/* copy a buffer into a pkt buffer chain */
1090e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic uint brcmf_sdbrcm_glom_from_buf(struct brcmf_sdio *bus, uint len)
109120e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel{
109220e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	uint n, ret = 0;
109320e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	struct sk_buff *p;
109420e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	u8 *buf;
109520e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel
109620e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	buf = bus->dataptr;
109720e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel
109820e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	/* copy the data */
1099b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel	skb_queue_walk(&bus->glom, p) {
110020e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel		n = min_t(uint, p->len, len);
110120e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel		memcpy(p->data, buf, n);
110220e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel		buf += n;
110320e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel		len -= n;
110420e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel		ret += n;
1105b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel		if (!len)
1106b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel			break;
110720e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	}
110820e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel
110920e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel	return ret;
111020e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel}
111120e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel
11129a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel/* return total length of buffer chain */
1113e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus)
11149a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel{
11159a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel	struct sk_buff *p;
11169a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel	uint total;
11179a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel
11189a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel	total = 0;
11199a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel	skb_queue_walk(&bus->glom, p)
11209a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel		total += p->len;
11219a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel	return total;
11229a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel}
11239a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel
1124e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus)
1125046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel{
1126046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel	struct sk_buff *cur, *next;
1127046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel
1128046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel	skb_queue_walk_safe(&bus->glom, cur, next) {
1129046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel		skb_unlink(cur, &bus->glom);
1130046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel		brcmu_pkt_buf_free_skb(cur);
1131046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel	}
1132046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel}
1133046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel
1134e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq)
11355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
11365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 dlen, totlen;
11375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *dptr, num = 0;
11385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 sublen, check;
11400b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel	struct sk_buff *pfirst, *pnext;
11415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int errcode;
11435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 chan, seq, doff, sfdoff;
11445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 txmax;
11455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ifidx = 0;
11475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool usechain = bus->use_rxchain;
11485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If packets, issue read(s) and send up packet chain */
11505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Return sequence numbers consumed? */
11515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1152b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel	brcmf_dbg(TRACE, "start: glomd %p glom %p\n",
1153b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel		  bus->glomd, skb_peek(&bus->glom));
11545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If there's a descriptor, generate the packet chain */
11565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->glomd) {
11570b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		pfirst = pnext = NULL;
11585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		dlen = (u16) (bus->glomd->len);
11595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		dptr = bus->glomd->data;
11605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!dlen || (dlen & 1)) {
11615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "bad glomd len(%d), ignore descriptor\n",
11625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  dlen);
11635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dlen = 0;
11645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
11655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (totlen = num = 0; dlen; num++) {
11675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Get (and move past) next length */
11685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sublen = get_unaligned_le16(dptr);
11695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dlen -= sizeof(u16);
11705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dptr += sizeof(u16);
11715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((sublen < SDPCM_HDRLEN) ||
11725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
11735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "descriptor len %d bad: %d\n",
11745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, sublen);
11755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				pnext = NULL;
11765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
11775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
11785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (sublen % BRCMF_SDALIGN) {
11795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "sublen %d not multiple of %d\n",
11805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  sublen, BRCMF_SDALIGN);
11815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				usechain = false;
11825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
11835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			totlen += sublen;
11845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* For last frame, adjust read len so total
11865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 is a block multiple */
11875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (!dlen) {
11885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				sublen +=
11895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				    (roundup(totlen, bus->blocksize) - totlen);
11905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				totlen = roundup(totlen, bus->blocksize);
11915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
11925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
11935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Allocate/chain packet for next subframe */
11945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
11955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (pnext == NULL) {
11965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "bcm_pkt_buf_get_skb failed, num %d len %d\n",
11975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, sublen);
11985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
11995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
1200b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel			skb_queue_tail(&bus->glom, pnext);
12015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Adhere to start alignment requirements */
12035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pkt_align(pnext, sublen, BRCMF_SDALIGN);
12045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
12055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* If all allocations succeeded, save packet chain
12075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 in bus structure */
12085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (pnext) {
12095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n",
12105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  totlen, num);
12115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (BRCMF_GLOM_ON() && bus->nextlen &&
12125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    totlen != bus->nextlen) {
12135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n",
12145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  bus->nextlen, totlen, rxseq);
12155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
12165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pfirst = pnext = NULL;
12175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
1218046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel			brcmf_sdbrcm_free_glom(bus);
12195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			num = 0;
12205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
12215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Done with descriptor packet */
12235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmu_pkt_buf_free_skb(bus->glomd);
12245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->glomd = NULL;
12255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->nextlen = 0;
12265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
12275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Ok -- either we just generated a packet chain,
12295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 or had one from before */
1230b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel	if (!skb_queue_empty(&bus->glom)) {
12315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (BRCMF_GLOM_ON()) {
12325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(GLOM, "try superframe read, packet chain:\n");
1233b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel			skb_queue_walk(&bus->glom, pnext) {
12345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(GLOM, "    %p: %p len 0x%04x (%d)\n",
12355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  pnext, (u8 *) (pnext->data),
12365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  pnext->len, pnext->len);
12375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
12385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
12395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1240b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel		pfirst = skb_peek(&bus->glom);
12419a95e60e0610bb8ec39c74d2c8546514a76428dfArend van Spriel		dlen = (u16) brcmf_sdbrcm_glom_len(bus);
12425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Do an SDIO read for the superframe.  Configurable iovar to
12445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * read directly into the chained packet, or allocate a large
12455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * packet and and copy into the chain.
12465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 */
12475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (usechain) {
12485adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel			errcode = brcmf_sdcard_recv_chain(bus->sdiodev,
12495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bus->sdiodev->sbwad,
12505adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel					SDIO_FUNC_2, F2SYNC, &bus->glom);
12515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (bus->dataptr) {
12525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = brcmf_sdcard_recv_buf(bus->sdiodev,
12535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bus->sdiodev->sbwad,
12545adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel					SDIO_FUNC_2, F2SYNC,
12555adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel					bus->dataptr, dlen);
125620e5ca16397648811a9e1ad531360c843e005a57Arend van Spriel			sublen = (u16) brcmf_sdbrcm_glom_from_buf(bus, dlen);
12575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (sublen != dlen) {
12585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "FAILED TO COPY, dlen %d sublen %d\n",
12595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  dlen, sublen);
12605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				errcode = -1;
12615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
12625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pnext = NULL;
12635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
12645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
12655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  dlen);
12665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
12675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
12685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f2rxdata++;
12695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* On failure, kill the superframe, allow a couple retries */
12715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (errcode < 0) {
12725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "glom read of %d bytes failed: %d\n",
12735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  dlen, errcode);
1274719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.rx_errors++;
12755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bus->glomerr++ < 3) {
12775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, true, true);
12785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else {
12795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->glomerr = 0;
12805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, true, false);
12815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->rxglomfail++;
1282046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel				brcmf_sdbrcm_free_glom(bus);
12835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
12845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return 0;
12855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
12861e02382979dd422e3b1bdb45545a0699497e3692Joe Perches
12871e02382979dd422e3b1bdb45545a0699497e3692Joe Perches		brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
12881e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   pfirst->data, min_t(int, pfirst->len, 48),
12891e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   "SUPERFRAME:\n");
12905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Validate the superframe header */
12925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		dptr = (u8 *) (pfirst->data);
12935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		sublen = get_unaligned_le16(dptr);
12945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		check = get_unaligned_le16(dptr + sizeof(u16));
12955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
12965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
12975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
12985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
12995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
13005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "nextlen too large (%d) seq %d\n",
13015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bus->nextlen, seq);
13025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->nextlen = 0;
13035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
13045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
13055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
13065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		errcode = 0;
13085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((u16)~(sublen ^ check)) {
13095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "(superframe): HW hdr error: len/check 0x%04x/0x%04x\n",
13105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  sublen, check);
13115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
13125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (roundup(sublen, bus->blocksize) != dlen) {
13135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "(superframe): len 0x%04x, rounded 0x%04x, expect 0x%04x\n",
13145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  sublen, roundup(sublen, bus->blocksize),
13155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  dlen);
13165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
13175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
13185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   SDPCM_GLOM_CHANNEL) {
13195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "(superframe): bad channel %d\n",
13205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  SDPCM_PACKET_CHANNEL(
13215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  &dptr[SDPCM_FRAMETAG_LEN]));
13225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
13235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
13245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "(superframe): got 2nd descriptor?\n");
13255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
13265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if ((doff < SDPCM_HDRLEN) ||
13275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   (doff > (pfirst->len - SDPCM_HDRLEN))) {
13285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "(superframe): Bad data offset %d: HW %d pkt %d min %d\n",
13295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  doff, sublen, pfirst->len, SDPCM_HDRLEN);
13305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			errcode = -1;
13315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
13325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check sequence number of superframe SW header */
13345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (rxseq != seq) {
13355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "(superframe) rx_seq %d, expected %d\n",
13365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  seq, rxseq);
13375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_badseq++;
13385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rxseq = seq;
13395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
13405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check window for sanity */
13425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((u8) (txmax - bus->tx_seq) > 0x40) {
13435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
13445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  txmax, bus->tx_seq);
13455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			txmax = bus->tx_seq + 2;
13465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
13475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->tx_max = txmax;
13485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Remove superframe header, remember offset */
13505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_pull(pfirst, doff);
13515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		sfdoff = doff;
13520b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		num = 0;
13535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Validate all the subframe headers */
13550b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		skb_queue_walk(&bus->glom, pnext) {
13560b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel			/* leave when invalid subframe is found */
13570b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel			if (errcode)
13580b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel				break;
13590b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel
13605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dptr = (u8 *) (pnext->data);
13615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dlen = (u16) (pnext->len);
13625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sublen = get_unaligned_le16(dptr);
13635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			check = get_unaligned_le16(dptr + sizeof(u16));
13645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
13655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
13661e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
13671e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   dptr, 32, "subframe:\n");
13685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((u16)~(sublen ^ check)) {
13705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(subframe %d): HW hdr error: len/check 0x%04x/0x%04x\n",
13715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, sublen, check);
13725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				errcode = -1;
13735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
13745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(subframe %d): length mismatch: len 0x%04x, expect 0x%04x\n",
13755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, sublen, dlen);
13765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				errcode = -1;
13775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else if ((chan != SDPCM_DATA_CHANNEL) &&
13785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				   (chan != SDPCM_EVENT_CHANNEL)) {
13795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(subframe %d): bad channel %d\n",
13805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, chan);
13815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				errcode = -1;
13825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
13835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(subframe %d): Bad data offset %d: HW %d min %d\n",
13845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  num, doff, sublen, SDPCM_HDRLEN);
13855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				errcode = -1;
13865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
13870b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel			/* increase the subframe count */
13880b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel			num++;
13895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
13905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
13915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (errcode) {
13925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Terminate frame on error, request
13935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 a couple retries */
13945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bus->glomerr++ < 3) {
13955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* Restore superframe header space */
13965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				skb_push(pfirst, sfdoff);
13975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, true, true);
13985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else {
13995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->glomerr = 0;
14005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, true, false);
14015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->rxglomfail++;
1402046808daf9f6b8c5275861330d4f8c2e6cfe3c31Arend van Spriel				brcmf_sdbrcm_free_glom(bus);
14035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
14045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->nextlen = 0;
14055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return 0;
14065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
14075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Basic SD framing looks ok - process each packet (header) */
14095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14100b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		skb_queue_walk_safe(&bus->glom, pfirst, pnext) {
14115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dptr = (u8 *) (pfirst->data);
14125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sublen = get_unaligned_le16(dptr);
14135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
14145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
14155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
14165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(GLOM, "Get subframe %d, %p(%p/%d), sublen %d chan %d seq %d\n",
14185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  num, pfirst, pfirst->data,
14195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  pfirst->len, sublen, chan, seq);
14205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* precondition: chan == SDPCM_DATA_CHANNEL ||
14225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 chan == SDPCM_EVENT_CHANNEL */
14235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (rxseq != seq) {
14255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(GLOM, "rx_seq %d, expected %d\n",
14265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  seq, rxseq);
14275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->rx_badseq++;
14285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				rxseq = seq;
14295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
14300b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel			rxseq++;
14310b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel
14321e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
14331e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   dptr, dlen, "Rx Subframe Data:\n");
14345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			__skb_trim(pfirst, sublen);
14365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			skb_pull(pfirst, doff);
14375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (pfirst->len == 0) {
14390b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel				skb_unlink(pfirst, &bus->glom);
14405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmu_pkt_buf_free_skb(pfirst);
14415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
1442d5625ee66f82162acb7189c1974e688ebc178cf3Franky Lin			} else if (brcmf_proto_hdrpull(bus->sdiodev->dev,
1443d5625ee66f82162acb7189c1974e688ebc178cf3Franky Lin						       &ifidx, pfirst) != 0) {
14445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "rx protocol error\n");
1445719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin				bus->sdiodev->bus_if->dstats.rx_errors++;
14460b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel				skb_unlink(pfirst, &bus->glom);
14475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmu_pkt_buf_free_skb(pfirst);
14485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
14495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
14505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14511e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
14521e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   pfirst->data,
14531e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   min_t(int, pfirst->len, 32),
14541e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n",
14551e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   bus->glom.qlen, pfirst, pfirst->data,
14561e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   pfirst->len, pfirst->next,
14571e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   pfirst->prev);
14585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
14590b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		/* sent any remaining packets up */
14600b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		if (bus->glom.qlen) {
14615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			up(&bus->sdsem);
1462228bb43d5d82549c43eee29e12d7632d4df4e4dfFranky Lin			brcmf_rx_frame(bus->sdiodev->dev, ifidx, &bus->glom);
14635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			down(&bus->sdsem);
14645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
14655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxglomframes++;
14670b45bf74f91e781b74bfadb8bb08ef341b6befd3Arend van Spriel		bus->rxglompkts += bus->glom.qlen;
14685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
14695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return num;
14705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1472e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition,
14735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bool *pending)
14745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
14755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	DECLARE_WAITQUEUE(wait, current);
14765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT);
14775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Wait until control frame is available */
14795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	add_wait_queue(&bus->dcmd_resp_wait, &wait);
14805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	set_current_state(TASK_INTERRUPTIBLE);
14815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (!(*condition) && (!signal_pending(current) && timeout))
14835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		timeout = schedule_timeout(timeout);
14845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (signal_pending(current))
14865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*pending = true;
14875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	set_current_state(TASK_RUNNING);
14895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	remove_wait_queue(&bus->dcmd_resp_wait, &wait);
14905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return timeout;
14925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
14935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1494e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus)
14955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
14965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (waitqueue_active(&bus->dcmd_resp_wait))
14975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wake_up_interruptible(&bus->dcmd_resp_wait);
14985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
14995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
15005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15015b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
1502e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff)
15035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rdlen, pad;
15055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int sdret;
15075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
15095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Set rxctl for frame (w/optional alignment) */
15115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxctl = bus->rxbuf;
15125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxctl += BRCMF_FIRSTREAD;
15135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN);
15145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pad)
15155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxctl += (BRCMF_SDALIGN - pad);
15165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxctl -= BRCMF_FIRSTREAD;
15175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Copy the already-read portion over */
15195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(bus->rxctl, hdr, BRCMF_FIRSTREAD);
15205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len <= BRCMF_FIRSTREAD)
15215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto gotpkt;
15225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Raise rdlen to next SDIO block to avoid tail command */
15245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	rdlen = len - BRCMF_FIRSTREAD;
15255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
15265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pad = bus->blocksize - (rdlen % bus->blocksize);
15275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
1528b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin		    ((len + pad) < bus->sdiodev->bus_if->maxctl))
15295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rdlen += pad;
15305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (rdlen % BRCMF_SDALIGN) {
15315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
15325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Satisfy length-alignment requirements */
15355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rdlen & (ALIGNMENT - 1))
15365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rdlen = roundup(rdlen, ALIGNMENT);
15375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Drop if the read is too big or it exceeds our maximum */
1539b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) {
15405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "%d-byte control read exceeds %d-byte buffer\n",
1541b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin			  rdlen, bus->sdiodev->bus_if->maxctl);
1542719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin		bus->sdiodev->bus_if->dstats.rx_errors++;
15435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, false, false);
15445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
15455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
1547b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	if ((len - doff) > bus->sdiodev->bus_if->maxctl) {
15485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n",
1549b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin			  len, len - doff, bus->sdiodev->bus_if->maxctl);
1550719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin		bus->sdiodev->bus_if->dstats.rx_errors++;
15515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rx_toolong++;
15525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, false, false);
15535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
15545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Read remainder of frame body into the rxctl buffer */
15575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	sdret = brcmf_sdcard_recv_buf(bus->sdiodev,
15585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->sdiodev->sbwad,
15595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				SDIO_FUNC_2,
15605adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel				F2SYNC, (bus->rxctl + BRCMF_FIRSTREAD), rdlen);
15615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->f2rxdata++;
15625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Control frame failures need retransmission */
15645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (sdret < 0) {
15655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "read %d control bytes failed: %d\n",
15665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  rdlen, sdret);
15675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxc_errors++;
15685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, true, true);
15695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto done;
15705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15725b435de0d786869c95d1962121af0d7df2542009Arend van Sprielgotpkt:
15735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15741e02382979dd422e3b1bdb45545a0699497e3692Joe Perches	brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
15751e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			   bus->rxctl, len, "RxCtrl:\n");
15765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Point to valid data and indicate its length */
15785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxctl += doff;
15795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxlen = len - doff;
15805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15815b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
15825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Awake any waiters */
15835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_dcmd_resp_wake(bus);
15845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Pad read to blocksize for efficiency */
1587e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen)
15885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
15895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) {
15905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*pad = bus->blocksize - (*rdlen % bus->blocksize);
15915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (*pad <= bus->roundup && *pad < bus->blocksize &&
15925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ)
15935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			*rdlen += *pad;
15945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (*rdlen % BRCMF_SDALIGN) {
15955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*rdlen += BRCMF_SDALIGN - (*rdlen % BRCMF_SDALIGN);
15965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
15975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
15985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
15995b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
1600e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_alloc_pkt_and_read(struct brcmf_sdio *bus, u16 rdlen,
16015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 struct sk_buff **pkt, u8 **rxbuf)
16025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
16035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int sdret;		/* Return code from calls */
16045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN);
16065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*pkt == NULL)
16075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
16085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	pkt_align(*pkt, rdlen, BRCMF_SDALIGN);
16105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*rxbuf = (u8 *) ((*pkt)->data);
16115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Read the entire frame */
16125adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel	sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
16135adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel				      SDIO_FUNC_2, F2SYNC, *pkt);
16145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->f2rxdata++;
16155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (sdret < 0) {
16175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "(nextlen): read %d bytes failed: %d\n",
16185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  rdlen, sdret);
16195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmu_pkt_buf_free_skb(*pkt);
1620719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin		bus->sdiodev->bus_if->dstats.rx_errors++;
16215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Force retry w/normal header read.
16225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * Don't attempt NAK for
16235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 * gSPI
16245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 */
16255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, true, true);
16265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*pkt = NULL;
16275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
16285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
16295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Checks the header */
16315b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
1632e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_check_rxbuf(struct brcmf_sdio *bus, struct sk_buff *pkt, u8 *rxbuf,
16335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  u8 rxseq, u16 nextlen, u16 *len)
16345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
16355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 check;
16365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool len_consistent;	/* Result of comparing readahead len and
16375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				   len from hw-hdr */
16385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
16405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Extract hardware header fields */
16425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*len = get_unaligned_le16(bus->rxhdr);
16435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
16445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* All zeros means readahead info was bad */
16465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(*len | check)) {
16475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "(nextlen): read zeros in HW header???\n");
16485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
16495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
16505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Validate check bytes */
16525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((u16)~(*len ^ check)) {
16535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "(nextlen): HW hdr error: nextlen/len/check 0x%04x/0x%04x/0x%04x\n",
16545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  nextlen, *len, check);
16555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rx_badhdr++;
16565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, false, false);
16575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
16585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
16595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Validate frame length */
16615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (*len < SDPCM_HDRLEN) {
16625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "(nextlen): HW hdr length invalid: %d\n",
16635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  *len);
16645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
16655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
16665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check for consistency with readahead info */
16685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	len_consistent = (nextlen != (roundup(*len, 16) >> 4));
16695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len_consistent) {
16705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Mismatch, force retry w/normal
16715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			header (may be >4K) */
16725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "(nextlen): mismatch, nextlen %d len %d rnd %d; expected rxseq %d\n",
16735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  nextlen, *len, roundup(*len, 16),
16745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  rxseq);
16755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_rxfail(bus, true, true);
16765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
16775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
16785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
16805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16815b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
16825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_pktfree2(bus, pkt);
16835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return -EINVAL;
16845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
16855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Return true if there may be more frames to read */
16875b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic uint
1688e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_readframes(struct brcmf_sdio *bus, uint maxframes, bool *finished)
16895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
16905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 len, check;	/* Extracted hardware header fields */
16915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 chan, seq, doff;	/* Extracted software header fields */
16925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 fcbits;		/* Extracted fcbits from software header */
16935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
16945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct sk_buff *pkt;		/* Packet for event or data frames */
16955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 pad;		/* Number of pad bytes to read */
16965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 rdlen;		/* Total number of bytes to read */
16975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 rxseq;		/* Next sequence number to expect */
16985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxleft = 0;	/* Remaining number of frames allowed */
16995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int sdret;		/* Return code from calls */
17005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 txmax;		/* Maximum tx sequence offered */
17015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *rxbuf;
17025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ifidx = 0;
17035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxcount = 0;	/* Total frames read */
17045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
17065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Not finished unless we encounter no more frames indication */
17085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*finished = false;
17095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (rxseq = bus->rx_seq, rxleft = maxframes;
17118d169aa00d0356f915e84dbdf6c9be381cce34a4Franky Lin	     !bus->rxskip && rxleft &&
1712712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	     bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN;
17135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	     rxseq++, rxleft--) {
17145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Handle glomming separately */
1716b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel		if (bus->glomd || !skb_queue_empty(&bus->glom)) {
17175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u8 cnt;
17185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n",
1719b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel				  bus->glomd, skb_peek(&bus->glom));
17205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			cnt = brcmf_sdbrcm_rxglom(bus, rxseq);
17215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(GLOM, "rxglom returned %d\n", cnt);
17225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rxseq += cnt - 1;
17235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
17245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
17255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
17265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Try doing single read if we can */
17285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->nextlen) {
17295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u16 nextlen = bus->nextlen;
17305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->nextlen = 0;
17315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rdlen = len = nextlen << 4;
17335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_pad(bus, &pad, &rdlen);
17345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/*
17365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * After the frame is received we have to
17375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * distinguish whether it is data
17385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 * or non-data frame.
17395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 */
17405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_alloc_pkt_and_read(bus, rdlen, &pkt, &rxbuf);
17415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (pkt == NULL) {
17425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* Give up on data, request rtx of events */
17435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(nextlen): brcmf_alloc_pkt_and_read failed: len %d rdlen %d expected rxseq %d\n",
17445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  len, rdlen, rxseq);
17455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
17465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
17475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (brcmf_check_rxbuf(bus, pkt, rxbuf, rxseq, nextlen,
17495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					      &len) < 0)
17505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
17515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Extract software header fields */
17535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			chan = SDPCM_PACKET_CHANNEL(
17545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
17555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			seq = SDPCM_PACKET_SEQUENCE(
17565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
17575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			doff = SDPCM_DOFFSET_VALUE(
17585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
17595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			txmax = SDPCM_WINDOW_VALUE(
17605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
17615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->nextlen =
17635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    bus->rxhdr[SDPCM_FRAMETAG_LEN +
17645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SDPCM_NEXTLEN_OFFSET];
17655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
17665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
17675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  bus->nextlen, seq);
17685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->nextlen = 0;
17695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
17705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
177128a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin			bus->rx_readahead_cnt++;
17725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Handle Flow Control */
17745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			fcbits = SDPCM_FCMASK_VALUE(
17755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
17765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bus->flowcontrol != fcbits) {
17785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (~bus->flowcontrol & fcbits)
17795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bus->fc_xoff++;
17805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (bus->flowcontrol & ~fcbits)
17825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bus->fc_xon++;
17835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->fc_rcvd++;
17855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->flowcontrol = fcbits;
17865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
17875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Check and update sequence number */
17895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (rxseq != seq) {
17905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(INFO, "(nextlen): rx_seq %d, expected %d\n",
17915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  seq, rxseq);
17925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->rx_badseq++;
17935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				rxseq = seq;
17945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
17955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
17965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Check window for sanity */
17975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((u8) (txmax - bus->tx_seq) > 0x40) {
17985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "got unlikely tx max %d with tx_seq %d\n",
17995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  txmax, bus->tx_seq);
18005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				txmax = bus->tx_seq + 2;
18015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
18025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->tx_max = txmax;
18035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18041e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
18051e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   rxbuf, len, "Rx Data:\n");
18061e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
18071e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					     BRCMF_DATA_ON()) &&
18081e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   BRCMF_HDRS_ON(),
18091e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   bus->rxhdr, SDPCM_HDRLEN,
18101e02382979dd422e3b1bdb45545a0699497e3692Joe Perches					   "RxHdr:\n");
18115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (chan == SDPCM_CONTROL_CHANNEL) {
18135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(nextlen): readahead on control packet %d?\n",
18145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  seq);
18155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* Force retry w/normal header read */
18165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->nextlen = 0;
18175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, false, true);
18185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_pktfree2(bus, pkt);
18195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
18205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
18215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Validate data offset */
18235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((doff < SDPCM_HDRLEN) || (doff > len)) {
18245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "(nextlen): bad data offset %d: HW len %d min %d\n",
18255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  doff, len, SDPCM_HDRLEN);
18265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, false, false);
18275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_pktfree2(bus, pkt);
18285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
18295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
18305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* All done with this one -- now deliver the packet */
18325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto deliver;
18335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Read frame header (hardware and software) */
18365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		sdret = brcmf_sdcard_recv_buf(bus->sdiodev, bus->sdiodev->sbwad,
18375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					      SDIO_FUNC_2, F2SYNC, bus->rxhdr,
18385adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel					      BRCMF_FIRSTREAD);
18395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f2rxhdrs++;
18405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (sdret < 0) {
18425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "RXHEADER FAILED: %d\n", sdret);
18435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_hdrfail++;
18445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, true, true);
18455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
18465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18471e02382979dd422e3b1bdb45545a0699497e3692Joe Perches		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(),
18481e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   bus->rxhdr, SDPCM_HDRLEN, "RxHdr:\n");
18491e02382979dd422e3b1bdb45545a0699497e3692Joe Perches
18505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Extract hardware header fields */
18525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = get_unaligned_le16(bus->rxhdr);
18535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
18545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* All zeros means no more frames */
18565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!(len | check)) {
18575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			*finished = true;
18585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
18595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Validate check bytes */
18625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((u16) ~(len ^ check)) {
18635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HW hdr err: len/check 0x%04x/0x%04x\n",
18645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  len, check);
18655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_badhdr++;
18665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, false, false);
18675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
18685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Validate frame length */
18715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (len < SDPCM_HDRLEN) {
18725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "HW hdr length invalid: %d\n", len);
18735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
18745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Extract software header fields */
18775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
18785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
18795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
18805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
18815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Validate data offset */
18835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((doff < SDPCM_HDRLEN) || (doff > len)) {
18845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "Bad data offset %d: HW len %d, min %d seq %d\n",
18855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  doff, len, SDPCM_HDRLEN, seq);
18865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_badhdr++;
18875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, false, false);
18885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
18895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
18915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Save the readahead length if there is one */
18925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->nextlen =
18935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
18945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
18955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "(nextlen): got frame w/nextlen too large (%d), seq %d\n",
18965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bus->nextlen, seq);
18975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->nextlen = 0;
18985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
18995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Handle Flow Control */
19015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
19025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->flowcontrol != fcbits) {
19045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (~bus->flowcontrol & fcbits)
19055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->fc_xoff++;
19065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bus->flowcontrol & ~fcbits)
19085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->fc_xon++;
19095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->fc_rcvd++;
19115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->flowcontrol = fcbits;
19125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check and update sequence number */
19155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (rxseq != seq) {
19165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "rx_seq %d, expected %d\n", seq, rxseq);
19175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_badseq++;
19185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rxseq = seq;
19195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check window for sanity */
19225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((u8) (txmax - bus->tx_seq) > 0x40) {
19235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "unlikely tx max %d with tx_seq %d\n",
19245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  txmax, bus->tx_seq);
19255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			txmax = bus->tx_seq + 2;
19265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->tx_max = txmax;
19285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Call a separate function for control frames */
19305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (chan == SDPCM_CONTROL_CHANNEL) {
19315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff);
19325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
19335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* precondition: chan is either SDPCM_DATA_CHANNEL,
19365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   SDPCM_EVENT_CHANNEL, SDPCM_TEST_CHANNEL or
19375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   SDPCM_GLOM_CHANNEL */
19385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Length to read */
19405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rdlen = (len > BRCMF_FIRSTREAD) ? (len - BRCMF_FIRSTREAD) : 0;
19415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* May pad read to blocksize for efficiency */
19435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->roundup && bus->blocksize &&
19445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			(rdlen > bus->blocksize)) {
19455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pad = bus->blocksize - (rdlen % bus->blocksize);
19465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
19475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    ((rdlen + pad + BRCMF_FIRSTREAD) < MAX_RX_DATASZ))
19485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				rdlen += pad;
19495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else if (rdlen % BRCMF_SDALIGN) {
19505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
19515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Satisfy length-alignment requirements */
19545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (rdlen & (ALIGNMENT - 1))
19555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			rdlen = roundup(rdlen, ALIGNMENT);
19565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((rdlen + BRCMF_FIRSTREAD) > MAX_RX_DATASZ) {
19585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Too long -- skip this frame */
19595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "too long: len %d rdlen %d\n",
19605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  len, rdlen);
1961719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.rx_errors++;
19625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->rx_toolong++;
19635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, false, false);
19645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
19655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pkt = brcmu_pkt_buf_get_skb(rdlen +
19685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					    BRCMF_FIRSTREAD + BRCMF_SDALIGN);
19695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!pkt) {
19705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Give up on data, request rtx of events */
19715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "brcmu_pkt_buf_get_skb failed: rdlen %d chan %d\n",
19725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  rdlen, chan);
1973719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.rx_dropped++;
19745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan));
19755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
19765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Leave room for what we already read, and align remainder */
19795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_pull(pkt, BRCMF_FIRSTREAD);
19805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pkt_align(pkt, rdlen, BRCMF_SDALIGN);
19815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Read the remaining frame data */
19835adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel		sdret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad,
19845adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel					      SDIO_FUNC_2, F2SYNC, pkt);
19855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f2rxdata++;
19865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (sdret < 0) {
19885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "read %d %s bytes failed: %d\n", rdlen,
19895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  ((chan == SDPCM_EVENT_CHANNEL) ? "event"
19905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				   : ((chan == SDPCM_DATA_CHANNEL) ? "data"
19915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				      : "test")), sdret);
19925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmu_pkt_buf_free_skb(pkt);
1993719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.rx_errors++;
19945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan));
19955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
19965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
19975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
19985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Copy the already-read portion */
19995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_push(pkt, BRCMF_FIRSTREAD);
20005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(pkt->data, bus->rxhdr, BRCMF_FIRSTREAD);
20015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20021e02382979dd422e3b1bdb45545a0699497e3692Joe Perches		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(),
20031e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   pkt->data, len, "Rx Data:\n");
20045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20055b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldeliver:
20065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Save superframe descriptor and allocate packet frame */
20075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (chan == SDPCM_GLOM_CHANNEL) {
20085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
20095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n",
20105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  len);
20111e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				brcmf_dbg_hex_dump(BRCMF_GLOM_ON(),
20121e02382979dd422e3b1bdb45545a0699497e3692Joe Perches						   pkt->data, len,
20131e02382979dd422e3b1bdb45545a0699497e3692Joe Perches						   "Glom Data:\n");
20145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				__skb_trim(pkt, len);
20155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				skb_pull(pkt, SDPCM_HDRLEN);
20165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->glomd = pkt;
20175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else {
20185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "%s: glom superframe w/o "
20195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  "descriptor!\n", __func__);
20205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_rxfail(bus, false, false);
20215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
20225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
20235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
20245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Fill in packet len and prio, deliver upward */
20265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		__skb_trim(pkt, len);
20275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_pull(pkt, doff);
20285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (pkt->len == 0) {
20305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmu_pkt_buf_free_skb(pkt);
20315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
2032d5625ee66f82162acb7189c1974e688ebc178cf3Franky Lin		} else if (brcmf_proto_hdrpull(bus->sdiodev->dev, &ifidx,
2033d5625ee66f82162acb7189c1974e688ebc178cf3Franky Lin			   pkt) != 0) {
20345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "rx protocol error\n");
20355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmu_pkt_buf_free_skb(pkt);
2036719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.rx_errors++;
20375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
20385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
20395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Unlock during rx call */
20415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		up(&bus->sdsem);
2042228bb43d5d82549c43eee29e12d7632d4df4e4dfFranky Lin		brcmf_rx_packet(bus->sdiodev->dev, ifidx, pkt);
20435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		down(&bus->sdsem);
20445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
20455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	rxcount = maxframes - rxleft;
20465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Message if we hit the limit */
20475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!rxleft)
20485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(DATA, "hit rx limit of %d frames\n",
20495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  maxframes);
20505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
20515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(DATA, "processed %d frames\n", rxcount);
20525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Back off rxseq if awaiting rtx, update rx_seq */
20535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->rxskip)
20545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rxseq--;
20555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rx_seq = rxseq;
20565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return rxcount;
20585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
20595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20605b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
2061e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_wait_for_event(struct brcmf_sdio *bus, bool *lockvar)
20625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
20635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
206423677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches	wait_event_interruptible_timeout(bus->ctrl_wait, !*lockvar, HZ * 2);
20655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
20665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
20675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
20685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20695b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
2070e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus)
20715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
20725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (waitqueue_active(&bus->ctrl_wait))
20735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		wake_up_interruptible(&bus->ctrl_wait);
20745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return;
20755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
20765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Writes a HW/SW header into the packet and sends it. */
20785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Assumes: (a) header space already there, (b) caller holds lock */
2079e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff *pkt,
20805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			      uint chan, bool free_pkt)
20815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
20825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
20835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *frame;
20845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 len, pad = 0;
20855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 swheader;
20865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct sk_buff *new;
20875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i;
20885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
20905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	frame = (u8 *) (pkt->data);
20925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
20935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Add alignment padding, allocate new packet if needed */
20945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	pad = ((unsigned long)frame % BRCMF_SDALIGN);
20955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pad) {
20965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (skb_headroom(pkt) < pad) {
20975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "insufficient headroom %d for %d pad\n",
20985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  skb_headroom(pkt), pad);
20999c1a043ae688151444578f15208233143526bb88Franky Lin			bus->sdiodev->bus_if->tx_realloc++;
21005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
21015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (!new) {
21025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "couldn't allocate new %d-byte packet\n",
21035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  pkt->len + BRCMF_SDALIGN);
21045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				ret = -ENOMEM;
21055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				goto done;
21065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
21075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pkt_align(new, pkt->len, BRCMF_SDALIGN);
21095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			memcpy(new->data, pkt->data, pkt->len);
21105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (free_pkt)
21115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmu_pkt_buf_free_skb(pkt);
21125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* free the pkt if canned one is not used */
21135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			free_pkt = true;
21145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pkt = new;
21155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			frame = (u8 *) (pkt->data);
21165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* precondition: (frame % BRCMF_SDALIGN) == 0) */
21175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			pad = 0;
21185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
21195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			skb_push(pkt, pad);
21205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			frame = (u8 *) (pkt->data);
21215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* precondition: pad + SDPCM_HDRLEN <= pkt->len */
21225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			memset(frame, 0, pad + SDPCM_HDRLEN);
21235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
21245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
21255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* precondition: pad < BRCMF_SDALIGN */
21265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
21285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	len = (u16) (pkt->len);
21295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*(__le16 *) frame = cpu_to_le16(len);
21305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*(((__le16 *) frame) + 1) = cpu_to_le16(~len);
21315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Software tag: channel, sequence number, data offset */
21335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	swheader =
21345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
21355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    (((pad +
21365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	       SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
21375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
21395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
21405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21418ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
21425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	tx_packets[pkt->priority]++;
214318aad4f8e11530fd7e41d0faea0752c3a0ce799cJoe Perches#endif
21441e02382979dd422e3b1bdb45545a0699497e3692Joe Perches
21451e02382979dd422e3b1bdb45545a0699497e3692Joe Perches	brcmf_dbg_hex_dump(BRCMF_BYTES_ON() &&
21461e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			   ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) ||
21471e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			    (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL)),
21481e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			   frame, len, "Tx Frame:\n");
21491e02382979dd422e3b1bdb45545a0699497e3692Joe Perches	brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() &&
21501e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			     ((BRCMF_CTL_ON() &&
21511e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			       chan == SDPCM_CONTROL_CHANNEL) ||
21521e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			      (BRCMF_DATA_ON() &&
21531e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			       chan != SDPCM_CONTROL_CHANNEL))) &&
21541e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			   BRCMF_HDRS_ON(),
21551e02382979dd422e3b1bdb45545a0699497e3692Joe Perches			   frame, min_t(u16, len, 16), "TxHdr:\n");
21565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Raise len to next SDIO block to eliminate tail command */
21585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
21595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u16 pad = bus->blocksize - (len % bus->blocksize);
21605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((pad <= bus->roundup) && (pad < bus->blocksize))
21615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				len += pad;
21625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (len % BRCMF_SDALIGN) {
21635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
21645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
21655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Some controllers have trouble with odd bytes -- round to even */
21675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len & (ALIGNMENT - 1))
21685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			len = roundup(len, ALIGNMENT);
21695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21705adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel	ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad,
21715adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel				    SDIO_FUNC_2, F2SYNC, pkt);
21725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->f2txdata++;
21735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0) {
21755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* On failure, abort the command and terminate the frame */
21765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
21775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  ret);
21785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->tx_sderrs++;
21795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
21815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
21825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
21835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 NULL);
21845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata++;
21855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
21865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (i = 0; i < 3; i++) {
21875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u8 hi, lo;
21885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			hi = brcmf_sdcard_cfg_read(bus->sdiodev,
21895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     SDIO_FUNC_1,
21905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     SBSDIO_FUNC1_WFRAMEBCHI,
21915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     NULL);
21925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			lo = brcmf_sdcard_cfg_read(bus->sdiodev,
21935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     SDIO_FUNC_1,
21945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     SBSDIO_FUNC1_WFRAMEBCLO,
21955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					     NULL);
21965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->f1regdata += 2;
21975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if ((hi == 0) && (lo == 0))
21985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
21995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
22005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
22025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret == 0)
22035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
22045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22055b435de0d786869c95d1962121af0d7df2542009Arend van Sprieldone:
22065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* restore pkt buffer pointer before calling tx complete routine */
22075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_pull(pkt, SDPCM_HDRLEN + pad);
22085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
2209c995788f4761f175f811cbeabb2f88ab8565ec1eFranky Lin	brcmf_txcomplete(bus->sdiodev->dev, pkt, ret != 0);
22105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
22115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (free_pkt)
22135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmu_pkt_buf_free_skb(pkt);
22145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
22165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
22175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2218e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes)
22195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
22205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct sk_buff *pkt;
22215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intstatus = 0;
22225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
22235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = 0, prec_out;
22245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint cnt = 0;
22255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint datalen;
22265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 tx_prec_map;
22275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
22295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	tx_prec_map = ~bus->flowcontrol;
22315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Send frames until the limit or some other event */
22335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (cnt = 0; (cnt < maxframes) && data_ok(bus); cnt++) {
22345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_lock_bh(&bus->txqlock);
22355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
22365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (pkt == NULL) {
22375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			spin_unlock_bh(&bus->txqlock);
22385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
22395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
22405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		spin_unlock_bh(&bus->txqlock);
22415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		datalen = pkt->len - SDPCM_HDRLEN;
22425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
22445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret)
2245719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.tx_errors++;
22465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
2247719f2733baa1e6a6a782c5109bfe054431db4259Franky Lin			bus->sdiodev->bus_if->dstats.tx_bytes += datalen;
22485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* In poll mode, need to check for other events */
22505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!bus->intr && cnt) {
22515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Check device status, signal pending interrupt */
22525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			r_sdreg32(bus, &intstatus,
22535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  offsetof(struct sdpcmd_regs, intstatus),
22545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  &retries);
22555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->f2txdata++;
22565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (brcmf_sdcard_regfail(bus->sdiodev))
22575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
22585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (intstatus & bus->hostintmask)
22595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->ipend = true;
22605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
22615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
22625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Deflow-control stack if needed */
2264712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if (bus->sdiodev->bus_if->drvr_up &&
2265712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	    (bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) &&
2266c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin	    bus->txoff && (pktq_len(&bus->txq) < TXLOW)) {
2267c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin		bus->txoff = OFF;
22682b4590569ead15fb88a83c1d8a07e3ca5507f4c6Franky Lin		brcmf_txflowcontrol(bus->sdiodev->dev, 0, OFF);
2269c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin	}
22705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
22715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return cnt;
22725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
22735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2274a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Linstatic void brcmf_sdbrcm_bus_stop(struct device *dev)
2275a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin{
2276a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	u32 local_hostintmask;
2277a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	u8 saveclk;
2278a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	uint retries;
2279a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	int err;
2280a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
22810a332e4678e4e4f0030f10827980c72d32300274Arend van Spriel	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
2282a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	struct brcmf_sdio *bus = sdiodev->bus;
2283a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2284a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_dbg(TRACE, "Enter\n");
2285a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2286a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	if (bus->watchdog_tsk) {
2287a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		send_sig(SIGTERM, bus->watchdog_tsk, 1);
2288a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		kthread_stop(bus->watchdog_tsk);
2289a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		bus->watchdog_tsk = NULL;
2290a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	}
2291a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2292a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	if (bus->dpc_tsk && bus->dpc_tsk != current) {
2293a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		send_sig(SIGTERM, bus->dpc_tsk, 1);
2294a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		kthread_stop(bus->dpc_tsk);
2295a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		bus->dpc_tsk = NULL;
2296a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	}
2297a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2298a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	down(&bus->sdsem);
2299a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2300a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus_wake(bus);
2301a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2302a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Enable clock for device interrupts */
2303a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
2304a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2305a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Disable and clear interrupts at the chip level also */
2306a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask), &retries);
2307a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	local_hostintmask = bus->hostintmask;
2308a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->hostintmask = 0;
2309a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2310a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Change our idea of bus state */
2311a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
2312a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2313a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Force clocks on backplane to be sure F2 interrupt propagates */
2314a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	saveclk = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
2315a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin					SBSDIO_FUNC1_CHIPCLKCSR, &err);
2316a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	if (!err) {
2317a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
2318a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin				       SBSDIO_FUNC1_CHIPCLKCSR,
2319a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin				       (saveclk | SBSDIO_FORCE_HT), &err);
2320a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	}
2321a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	if (err)
2322a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err);
2323a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2324a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Turn off the bus (F2), free any pending packets */
2325a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_dbg(INTR, "disable SDIO interrupts\n");
2326a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
2327a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin			 SDIO_FUNC_ENABLE_1, NULL);
2328a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2329a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Clear any pending interrupts now that F2 is disabled */
2330a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	w_sdreg32(bus, local_hostintmask,
2331a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		  offsetof(struct sdpcmd_regs, intstatus), &retries);
2332a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2333a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Turn off the backplane clock (only) */
2334a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
2335a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2336a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Clear the data packet queues */
2337a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
2338a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2339a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Clear any held glomming stuff */
2340a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	if (bus->glomd)
2341a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin		brcmu_pkt_buf_free_skb(bus->glomd);
2342a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_sdbrcm_free_glom(bus);
2343a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2344a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Clear rx control and wake any waiters */
2345a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->rxlen = 0;
2346a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	brcmf_sdbrcm_dcmd_resp_wake(bus);
2347a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2348a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Reset some F2 state stuff */
2349a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->rxskip = false;
2350a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->tx_seq = bus->rx_seq = 0;
2351a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2352a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	up(&bus->sdsem);
2353a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin}
2354a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin
2355e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic bool brcmf_sdbrcm_dpc(struct brcmf_sdio *bus)
23565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
23575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 intstatus, newstatus = 0;
23585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
23595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxlimit = bus->rxbound;	/* Rx frames to read before resched */
23605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint txlimit = bus->txbound;	/* Tx frames to send before resched */
23615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint framecnt = 0;	/* Temporary counter of tx/rx frames */
23625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool rxdone = true;	/* Flag for no more read data */
23635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool resched = false;	/* Flag indicating resched wanted */
23645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
23665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Start with leftover status bits */
23685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	intstatus = bus->intstatus;
23695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
23715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If waiting for HTAVAIL, check status */
23735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->clkstate == CLK_PENDING) {
23745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		int err;
23755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u8 clkctl, devctl = 0;
23765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23778ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
23785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check for inconsistent device control */
23795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		devctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
23805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       SBSDIO_DEVICE_CTL, &err);
23815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
23825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "error reading DEVCTL: %d\n", err);
2383712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
23845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
23858ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
23865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Read CSR, if clock on switch to AVAIL, else ignore */
23885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		clkctl = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
23895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       SBSDIO_FUNC1_CHIPCLKCSR, &err);
23905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (err) {
23915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "error reading CSR: %d\n",
23925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  err);
2393712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
23945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
23955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n",
23975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  devctl, clkctl);
23985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
23995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (SBSDIO_HTAV(clkctl)) {
24005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl = brcmf_sdcard_cfg_read(bus->sdiodev,
24015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						       SDIO_FUNC_1,
24025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						       SBSDIO_DEVICE_CTL, &err);
24035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (err) {
24045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "error reading DEVCTL: %d\n",
24055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  err);
2406712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
24075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
24085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
24095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
24105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				SBSDIO_DEVICE_CTL, devctl, &err);
24115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (err) {
24125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "error writing DEVCTL: %d\n",
24135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  err);
2414712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
24155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
24165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->clkstate = CLK_AVAIL;
24175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
24185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto clkwait;
24195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
24205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus_wake(bus);
24235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Make sure backplane clock is on */
24255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
24265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->clkstate == CLK_PENDING)
24275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto clkwait;
24285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Pending interrupt indicates new device status */
24305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->ipend) {
24315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->ipend = false;
24325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		r_sdreg32(bus, &newstatus,
24335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, intstatus), &retries);
24345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata++;
24355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (brcmf_sdcard_regfail(bus->sdiodev))
24365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			newstatus = 0;
24375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		newstatus &= bus->hostintmask;
24385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
24395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (newstatus) {
24405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			w_sdreg32(bus, newstatus,
24415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  offsetof(struct sdpcmd_regs, intstatus),
24425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  &retries);
24435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->f1regdata++;
24445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
24455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Merge new bits with previous */
24485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	intstatus |= newstatus;
24495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->intstatus = 0;
24505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Handle flow-control change: read new state in case our ack
24525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * crossed another change interrupt.  If change still set, assume
24535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * FC ON for safety, let next loop through do the debounce.
24545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
24555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_HMB_FC_CHANGE) {
24565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_HMB_FC_CHANGE;
24575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, I_HMB_FC_CHANGE,
24585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, intstatus), &retries);
24595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		r_sdreg32(bus, &newstatus,
24615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, intstatus), &retries);
24625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata += 2;
24635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->fcstate =
24645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
24655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus |= (newstatus & bus->hostintmask);
24665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Handle host mailbox indication */
24695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_HMB_HOST_INT) {
24705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_HMB_HOST_INT;
24715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus |= brcmf_sdbrcm_hostmail(bus);
24725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Generally don't ask for these, can get CRC errors... */
24755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_WR_OOSYNC) {
24765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Dongle reports WR_OOSYNC\n");
24775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_WR_OOSYNC;
24785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_RD_OOSYNC) {
24815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Dongle reports RD_OOSYNC\n");
24825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_RD_OOSYNC;
24835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_SBINT) {
24865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Dongle reports SBINT\n");
24875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_SBINT;
24885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Would be active due to wake-wlan in gSPI */
24915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (intstatus & I_CHIPACTIVE) {
24925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n");
24935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_CHIPACTIVE;
24945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
24955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
24965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Ignore frame indications if rxskip is set */
24975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->rxskip)
24985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		intstatus &= ~I_HMB_FRAME_IND;
24995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* On frame indication, read available frames */
25015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (PKT_AVAILABLE()) {
25025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone);
25035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (rxdone || bus->rxskip)
25045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			intstatus &= ~I_HMB_FRAME_IND;
25055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		rxlimit -= min(framecnt, rxlimit);
25065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
25075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Keep still-pending events for next scheduling */
25095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->intstatus = intstatus;
25105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25115b435de0d786869c95d1962121af0d7df2542009Arend van Sprielclkwait:
25125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (data_ok(bus) && bus->ctrl_frame_stat &&
25135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(bus->clkstate == CLK_AVAIL)) {
25145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		int ret, i;
25155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25165adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel		ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
25175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			SDIO_FUNC_2, F2SYNC, (u8 *) bus->ctrl_frame_buf,
25185adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel			(u32) bus->ctrl_frame_len);
25195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret < 0) {
25215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* On failure, abort the command and
25225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				terminate the frame */
25235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
25245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  ret);
25255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->tx_sderrs++;
25265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
25285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
25305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
25315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 NULL);
25325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->f1regdata++;
25335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			for (i = 0; i < 3; i++) {
25355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				u8 hi, lo;
25365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				hi = brcmf_sdcard_cfg_read(bus->sdiodev,
25375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     SDIO_FUNC_1,
25385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     SBSDIO_FUNC1_WFRAMEBCHI,
25395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     NULL);
25405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				lo = brcmf_sdcard_cfg_read(bus->sdiodev,
25415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     SDIO_FUNC_1,
25425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     SBSDIO_FUNC1_WFRAMEBCLO,
25435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						     NULL);
25445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->f1regdata += 2;
25455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if ((hi == 0) && (lo == 0))
25465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					break;
25475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
25485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
25505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret == 0)
25515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
25525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "Return_dpc value is : %d\n", ret);
25545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->ctrl_frame_stat = false;
25555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_wait_event_wakeup(bus);
25565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
25575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Send queued frames (limit 1 if rx may still be pending) */
25585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
25595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
25605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 && data_ok(bus)) {
25615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		framecnt = rxdone ? txlimit : min(txlimit, bus->txminmax);
25625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
25635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		txlimit -= framecnt;
25645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
25655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Resched if events or tx frames are pending,
25675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 else await next interrupt */
25685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* On failed register access, all bets are off:
25695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 no resched or interrupts */
2570712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) ||
25715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    brcmf_sdcard_regfail(bus->sdiodev)) {
25725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "failed backplane access over SDIO, halting operation %d\n",
25735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  brcmf_sdcard_regfail(bus->sdiodev));
2574712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
25755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->intstatus = 0;
25765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (bus->clkstate == CLK_PENDING) {
25775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "rescheduled due to CLK_PENDING awaiting I_CHIPACTIVE interrupt\n");
25785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		resched = true;
25795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (bus->intstatus || bus->ipend ||
25805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		(!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
25815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 && data_ok(bus)) || PKT_AVAILABLE()) {
25825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		resched = true;
25835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
25845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->dpc_sched = resched;
25865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If we're done for now, turn off clock request. */
25885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((bus->clkstate != CLK_PENDING)
25895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    && bus->idletime == BRCMF_IDLE_IMMEDIATE) {
25905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->activity = false;
25915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
25925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
25935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
25955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
25965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return resched;
25975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
25985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2599b948a85c1f26d48395de8c6c7e392f008f1be666Franky Linstatic inline void brcmf_sdbrcm_adddpctsk(struct brcmf_sdio *bus)
2600b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin{
2601b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	struct list_head *new_hd;
2602b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	unsigned long flags;
2603b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2604b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	if (in_interrupt())
2605b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		new_hd = kzalloc(sizeof(struct list_head), GFP_ATOMIC);
2606b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	else
2607b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		new_hd = kzalloc(sizeof(struct list_head), GFP_KERNEL);
2608b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	if (new_hd == NULL)
2609b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		return;
2610b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2611b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	spin_lock_irqsave(&bus->dpc_tl_lock, flags);
2612b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	list_add_tail(new_hd, &bus->dpc_tsklst);
2613b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
2614b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin}
2615b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
26165b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int brcmf_sdbrcm_dpc_thread(void *data)
26175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
2618e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus = (struct brcmf_sdio *) data;
2619b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	struct list_head *cur_hd, *tmp_hd;
2620b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	unsigned long flags;
26215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	allow_signal(SIGTERM);
26235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Run until signal received */
26245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (1) {
26255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (kthread_should_stop())
26265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
2627b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2628b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		if (list_empty(&bus->dpc_tsklst))
2629b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			if (wait_for_completion_interruptible(&bus->dpc_wait))
2630b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin				break;
2631b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2632b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		spin_lock_irqsave(&bus->dpc_tl_lock, flags);
2633b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		list_for_each_safe(cur_hd, tmp_hd, &bus->dpc_tsklst) {
2634b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
2635b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2636b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
26375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* after stopping the bus, exit thread */
263894c2fb82bd7c9055bec8e410c387befce33d1299Franky Lin				brcmf_sdbrcm_bus_stop(bus->sdiodev->dev);
26395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->dpc_tsk = NULL;
2640cf04317227d0c53d931a0b963e7ac6f7f0125e8aFranky Lin				spin_lock_irqsave(&bus->dpc_tl_lock, flags);
26415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
26425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
2643b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2644b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			if (brcmf_sdbrcm_dpc(bus))
2645b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin				brcmf_sdbrcm_adddpctsk(bus);
2646b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin
2647b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			spin_lock_irqsave(&bus->dpc_tl_lock, flags);
2648b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			list_del(cur_hd);
2649b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			kfree(cur_hd);
2650b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		}
2651b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		spin_unlock_irqrestore(&bus->dpc_tl_lock, flags);
26525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
26535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
26545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
26555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2656b9692d17e842fadb8c18faf24f550db80886763eFranky Linstatic int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt)
26575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
26585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = -EBADE;
26595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint datalen, prec;
2660bf347bb9768ab0da028a0d9f92142df738211debFranky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
26610a332e4678e4e4f0030f10827980c72d32300274Arend van Spriel	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
2662bf347bb9768ab0da028a0d9f92142df738211debFranky Lin	struct brcmf_sdio *bus = sdiodev->bus;
26635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
26655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	datalen = pkt->len;
26675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Add space for the header */
26695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	skb_push(pkt, SDPCM_HDRLEN);
26705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */
26715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	prec = prio2prec((pkt->priority & PRIOMASK));
26735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Check for existing queue, current flow-control,
26755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 pending event, or pending clock */
26765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq));
26775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fcqueued++;
26785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Priority based enq */
26805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_bh(&bus->txqlock);
268123677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches	if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) {
26825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		skb_pull(pkt, SDPCM_HDRLEN);
2683c995788f4761f175f811cbeabb2f88ab8565ec1eFranky Lin		brcmf_txcomplete(bus->sdiodev->dev, pkt, false);
26845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmu_pkt_buf_free_skb(pkt);
26855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "out of bus->txq !!!\n");
26865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -ENOSR;
26875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
26885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = 0;
26895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
26905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_unlock_bh(&bus->txqlock);
26915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2692c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin	if (pktq_len(&bus->txq) >= TXHI) {
2693c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin		bus->txoff = ON;
26942b4590569ead15fb88a83c1d8a07e3ca5507f4c6Franky Lin		brcmf_txflowcontrol(bus->sdiodev->dev, 0, ON);
2695c8bf34849f92c5894a3d7e12573d3789d7851f23Franky Lin	}
26965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
26978ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
26985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (pktq_plen(&bus->txq, prec) > qcount[prec])
26995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		qcount[prec] = pktq_plen(&bus->txq, prec);
27005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#endif
27015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Schedule DPC if needed to send queued packet(s) */
27025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!bus->dpc_sched) {
27035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->dpc_sched = true;
2704b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		if (bus->dpc_tsk) {
2705b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin			brcmf_sdbrcm_adddpctsk(bus);
27065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			complete(&bus->dpc_wait);
2707b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		}
27085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
27095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
27115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
27125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27135b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
2714e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_membytes(struct brcmf_sdio *bus, bool write, u32 address, u8 *data,
27155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 uint size)
27165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
27175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int bcmerror = 0;
27185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 sdaddr;
27195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint dsize;
27205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Determine initial transfer parameters */
27225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
27235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
27245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
27255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
27265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		dsize = size;
27275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Set the backplane window to include the start address */
27295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev, address);
27305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bcmerror) {
27315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "window change failed\n");
27325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto xfer_done;
27335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
27345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Do the transfer(s) */
27365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (size) {
27375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "%s %d bytes at offset 0x%08x in window 0x%08x\n",
27385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  write ? "write" : "read", dsize,
27395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  sdaddr, address & SBSDIO_SBWINDOW_MASK);
27405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror = brcmf_sdcard_rwdata(bus->sdiodev, write,
27415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       sdaddr, data, dsize);
27425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bcmerror) {
27435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "membytes transfer failed\n");
27445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
27455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
27465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Adjust for next transfer (if any) */
27485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		size -= dsize;
27495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (size) {
27505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			data += dsize;
27515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			address += dsize;
27525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bcmerror = brcmf_sdcard_set_sbaddr_window(bus->sdiodev,
27535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel								  address);
27545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bcmerror) {
27555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_dbg(ERROR, "window change failed\n");
27565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
27575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
27585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			sdaddr = 0;
27595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
27605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
27615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
27625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27635b435de0d786869c95d1962121af0d7df2542009Arend van Sprielxfer_done:
27645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Return the window to backplane enumeration space for core access */
27655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, bus->sdiodev->sbwad))
27665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "FAILED to set window back to 0x%x\n",
27675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  bus->sdiodev->sbwad);
27685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bcmerror;
27705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
27715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27728ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
27735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel#define CONSOLE_LINE_MAX	192
27745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2775e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus)
27765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
27775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	struct brcmf_console *c = &bus->console;
27785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 line[CONSOLE_LINE_MAX], ch;
27795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 n, idx, addr;
27805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int rv;
27815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Don't do anything until FWREADY updates console address */
27835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->console_addr == 0)
27845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
27855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Read console log struct */
27875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	addr = bus->console_addr + offsetof(struct rte_console, log_le);
27885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log_le,
27895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				   sizeof(c->log_le));
27905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rv < 0)
27915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return rv;
27925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
27935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Allocate console buffer (one time only) */
27945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (c->buf == NULL) {
27955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		c->bufsize = le32_to_cpu(c->log_le.buf_size);
27965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
27975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (c->buf == NULL)
27985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -ENOMEM;
27995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
28005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	idx = le32_to_cpu(c->log_le.idx);
28025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Protect against corrupt value */
28045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (idx > c->bufsize)
28055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -EBADE;
28065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Skip reading the console buffer if the index pointer
28085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 has not moved */
28095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (idx == c->last)
28105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
28115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Read the console buffer */
28135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	addr = le32_to_cpu(c->log_le.buf);
28145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize);
28155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rv < 0)
28165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return rv;
28175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (c->last != idx) {
28195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
28205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (c->last == idx) {
28215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* This would output a partial line.
28225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 * Instead, back up
28235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 * the buffer pointer and output this
28245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 * line next time around.
28255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 */
28265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				if (c->last >= n)
28275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					c->last -= n;
28285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				else
28295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					c->last = c->bufsize - n;
28305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				goto break2;
28315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
28325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ch = c->buf[c->last];
28335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			c->last = (c->last + 1) % c->bufsize;
28345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (ch == '\n')
28355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
28365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			line[n] = ch;
28375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
28385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (n > 0) {
28405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (line[n - 1] == '\r')
28415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				n--;
28425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			line[n] = 0;
284318aad4f8e11530fd7e41d0faea0752c3a0ce799cJoe Perches			pr_debug("CONSOLE: %s\n", line);
28445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
28455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
28465b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbreak2:
28475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
28495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
28508ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
28515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2852e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len)
28535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
28545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int i;
28555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
28565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->ctrl_frame_stat = false;
28585adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel	ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad,
28595adfeb632c52a6c15e98fa6465bdade6fff915d9Arend van Spriel				    SDIO_FUNC_2, F2SYNC, frame, len);
28605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret < 0) {
28625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* On failure, abort the command and terminate the frame */
28635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n",
28645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  ret);
28655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->tx_sderrs++;
28665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2);
28685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
28705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SBSDIO_FUNC1_FRAMECTRL,
28715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SFC_WF_TERM, NULL);
28725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->f1regdata++;
28735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		for (i = 0; i < 3; i++) {
28755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u8 hi, lo;
28765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			hi = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
28775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						   SBSDIO_FUNC1_WFRAMEBCHI,
28785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						   NULL);
28795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			lo = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
28805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						   SBSDIO_FUNC1_WFRAMEBCLO,
28815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						   NULL);
28825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->f1regdata += 2;
28835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (hi == 0 && lo == 0)
28845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				break;
28855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
28865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return ret;
28875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
28885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
28905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
28915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
28925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
28935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
2894fcf094f414f9e9c088f6c2aa9e19a59f7b41e1f5Franky Linstatic int
289547a1ce78d544b9fb3b776a62de3c084cf0020fdaFranky Linbrcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen)
28965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
28975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *frame;
28985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u16 len;
28995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 swheader;
29005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
29015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 doff = 0;
29025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret = -1;
290347a1ce78d544b9fb3b776a62de3c084cf0020fdaFranky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
29040a332e4678e4e4f0030f10827980c72d32300274Arend van Spriel	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
290547a1ce78d544b9fb3b776a62de3c084cf0020fdaFranky Lin	struct brcmf_sdio *bus = sdiodev->bus;
29065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
29085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Back the pointer to make a room for bus header */
29105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	frame = msg - SDPCM_HDRLEN;
29115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	len = (msglen += SDPCM_HDRLEN);
29125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Add alignment padding (optional for ctl frames) */
29145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	doff = ((unsigned long)frame % BRCMF_SDALIGN);
29155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (doff) {
29165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		frame -= doff;
29175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len += doff;
29185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		msglen += doff;
29195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memset(frame, 0, doff + SDPCM_HDRLEN);
29205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
29215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* precondition: doff < BRCMF_SDALIGN */
29225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	doff += SDPCM_HDRLEN;
29235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Round send length to next SDIO block */
29255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
29265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u16 pad = bus->blocksize - (len % bus->blocksize);
29275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if ((pad <= bus->roundup) && (pad < bus->blocksize))
29285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			len += pad;
29295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (len % BRCMF_SDALIGN) {
29305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
29315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
29325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Satisfy length-alignment requirements */
29345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len & (ALIGNMENT - 1))
29355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = roundup(len, ALIGNMENT);
29365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* precondition: IS_ALIGNED((unsigned long)frame, 2) */
29385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Need to lock here to protect txseq and SDIO tx calls */
29405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
29415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus_wake(bus);
29435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Make sure backplane clock is on */
29455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
29465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
29485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*(__le16 *) frame = cpu_to_le16((u16) msglen);
29495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	*(((__le16 *) frame) + 1) = cpu_to_le16(~msglen);
29505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Software tag: channel, sequence number, data offset */
29525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	swheader =
29535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
29545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	     SDPCM_CHANNEL_MASK)
29555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
29565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			     SDPCM_DOFFSET_MASK);
29575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
29585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
29595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!data_ok(bus)) {
29615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n",
29625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  bus->tx_max, bus->tx_seq);
29635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->ctrl_frame_stat = true;
29645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Send from dpc */
29655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->ctrl_frame_buf = frame;
29665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->ctrl_frame_len = len;
29675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_wait_for_event(bus, &bus->ctrl_frame_stat);
29695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
297023677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches		if (!bus->ctrl_frame_stat) {
29715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "ctrl_frame_stat == false\n");
29725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ret = 0;
29735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
29745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(INFO, "ctrl_frame_stat == true\n");
29755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ret = -1;
29765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
29775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
29785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret == -1) {
29801e02382979dd422e3b1bdb45545a0699497e3692Joe Perches		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(),
29811e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   frame, len, "Tx Frame:\n");
29821e02382979dd422e3b1bdb45545a0699497e3692Joe Perches		brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) &&
29831e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   BRCMF_HDRS_ON(),
29841e02382979dd422e3b1bdb45545a0699497e3692Joe Perches				   frame, min_t(u16, len, 16), "TxHdr:\n");
29855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		do {
29875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ret = brcmf_tx_frame(bus, frame, len);
29885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} while (ret < 0 && retries++ < TXRETRIES);
29895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
29905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && !bus->dpc_sched) {
29925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->activity = false;
29935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
29945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
29955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
29975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
29985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret)
299928a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin		bus->tx_ctlerrs++;
30005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
300128a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin		bus->tx_ctlpkts++;
30025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret ? -EIO : 0;
30045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
30055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3006fcf094f414f9e9c088f6c2aa9e19a59f7b41e1f5Franky Linstatic int
3007532cdd3b99b7a89fdc128c2b58abea780f3bbb4dFranky Linbrcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen)
30085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
30095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int timeleft;
30105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint rxlen = 0;
30115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool pending;
3012532cdd3b99b7a89fdc128c2b58abea780f3bbb4dFranky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
30130a332e4678e4e4f0030f10827980c72d32300274Arend van Spriel	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
3014532cdd3b99b7a89fdc128c2b58abea780f3bbb4dFranky Lin	struct brcmf_sdio *bus = sdiodev->bus;
30155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
30175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Wait until control frame is available */
30195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending);
30205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
30225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	rxlen = bus->rxlen;
30235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(msg, bus->rxctl, min(msglen, rxlen));
30245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxlen = 0;
30255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
30265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rxlen) {
30285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n",
30295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  rxlen, msglen);
30305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else if (timeleft == 0) {
30315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "resumed on timeout\n");
303223677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches	} else if (pending) {
30335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(CTL, "cancelled\n");
30345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return -ERESTARTSYS;
30355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
30365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(CTL, "resumed for unknown reason?\n");
30375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
30385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (rxlen)
304028a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin		bus->rx_ctlpkts++;
30415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
304228a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin		bus->rx_ctlerrs++;
30435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return rxlen ? (int)rxlen : -ETIMEDOUT;
30455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
30465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3047e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_downloadvars(struct brcmf_sdio *bus, void *arg, int len)
30485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
30495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int bcmerror = 0;
30505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
30525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Basic sanity checks */
30543fb1d8d2dad35f5094350c175b778b78df894284Franky Lin	if (bus->sdiodev->bus_if->drvr_up) {
30555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror = -EISCONN;
30565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
30575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
30585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!len) {
30595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror = -EOVERFLOW;
30605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
30615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
30625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Free the old ones and replace with passed variables */
30645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(bus->vars);
30655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->vars = kmalloc(len, GFP_ATOMIC);
30675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->varsz = bus->vars ? len : 0;
30685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->vars == NULL) {
30695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror = -ENOMEM;
30705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
30715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
30725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Copy the passed variables, which should include the
30745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 terminating double-null */
30755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(bus->vars, arg, bus->varsz);
30765b435de0d786869c95d1962121af0d7df2542009Arend van Sprielerr:
30775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bcmerror;
30785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
30795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3080e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_write_vars(struct brcmf_sdio *bus)
30815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
30825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int bcmerror = 0;
30835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 varsize;
30845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 varaddr;
30855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *vbuffer;
30865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 varsizew;
30875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	__le32 varsizew_le;
30888ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
30895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *nvram_ularray;
30908ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
30915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Even if there are no vars are to be written, we still
30935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		 need to set the ramsize. */
30945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
30955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	varaddr = (bus->ramsize - 4) - varsize;
30965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
30975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->vars) {
30985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		vbuffer = kzalloc(varsize, GFP_ATOMIC);
30995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!vbuffer)
31005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -ENOMEM;
31015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memcpy(vbuffer, bus->vars, bus->varsz);
31035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Write the vars list */
31055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror =
31065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    brcmf_sdbrcm_membytes(bus, true, varaddr, vbuffer, varsize);
31078ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
31085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Verify NVRAM bytes */
31095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(INFO, "Compare NVRAM dl & ul; varsize=%d\n", varsize);
31105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
3111c40701eacbaebcfe3fb6fac8ef3da653021c212cJesper Juhl		if (!nvram_ularray) {
3112c40701eacbaebcfe3fb6fac8ef3da653021c212cJesper Juhl			kfree(vbuffer);
31135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -ENOMEM;
3114c40701eacbaebcfe3fb6fac8ef3da653021c212cJesper Juhl		}
31155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Upload image to verify downloaded contents. */
31175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memset(nvram_ularray, 0xaa, varsize);
31185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Read the vars list to temp buffer for comparison */
31205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror =
31215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray,
31225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				     varsize);
31235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bcmerror) {
31245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "error %d on reading %d nvram bytes at 0x%08x\n",
31255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  bcmerror, varsize, varaddr);
31265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
31275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Compare the org NVRAM with the one read from RAM */
31285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (memcmp(vbuffer, nvram_ularray, varsize))
31295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "Downloaded NVRAM image is corrupted\n");
31305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else
31315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "Download/Upload/Compare of NVRAM ok\n");
31325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(nvram_ularray);
31348ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
31355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(vbuffer);
31375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
31385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* adjust to the user specified RAM */
31405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "Physical memory size: %d\n", bus->ramsize);
31415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "Vars are at %d, orig varsize is %d\n",
31425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  varaddr, varsize);
31435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	varsize = ((bus->ramsize - 4) - varaddr);
31445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/*
31465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Determine the length token:
31475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * Varsize, converted to words, in lower 16-bits, checksum
31485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * in upper 16-bits.
31495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
31505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bcmerror) {
31515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		varsizew = 0;
31525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		varsizew_le = cpu_to_le32(0);
31535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
31545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		varsizew = varsize / 4;
31555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
31565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		varsizew_le = cpu_to_le32(varsizew);
31575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
31585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "New varsize is %d, length token=0x%08x\n",
31605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  varsize, varsizew);
31615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Write the length token to the last word */
31635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->ramsize - 4),
31645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 (u8 *)&varsizew_le, 4);
31655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bcmerror;
31675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
31685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3169e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter)
31705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
31715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries;
31725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int bcmerror = 0;
317399ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	struct chip_info *ci = bus->ci;
31745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* To enter download state, disable ARM and reset SOCRAM.
31765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * To exit download state, simply reset ARM (default is RAM boot).
31775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
31785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (enter) {
31795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->alp_only = true;
31805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3181086a2e0a63eef367dab9b4499ba0cfe3a309ec94Franky Lin		ci->coredisable(bus->sdiodev, ci, BCMA_CORE_ARM_CM3);
31825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3183d77e70ff5ace175f19447a1965691a794c27de24Franky Lin		ci->resetcore(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM);
31845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Clear the top bit of memory */
31865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->ramsize) {
31875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			u32 zeros = 0;
31885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4,
31895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 (u8 *)&zeros, 4);
31905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
31915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
31926ca687d9461b25ce2339ba1809ec13ef459d4661Franky Lin		if (!ci->iscoreup(bus->sdiodev, ci, BCMA_CORE_INTERNAL_MEM)) {
31935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "SOCRAM core is down after reset?\n");
31945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bcmerror = -EBADE;
31955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto fail;
31965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
31975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
31985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bcmerror = brcmf_sdbrcm_write_vars(bus);
31995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bcmerror) {
32005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "no vars written to RAM\n");
32015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bcmerror = 0;
32025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
32035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, 0xFFFFFFFF,
32055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, intstatus), &retries);
32065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3207d77e70ff5ace175f19447a1965691a794c27de24Franky Lin		ci->resetcore(bus->sdiodev, ci, BCMA_CORE_ARM_CM3);
32085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Allow HT Clock now that the ARM is running. */
32105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->alp_only = false;
32115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3212712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin		bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD;
32135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
32145b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
32155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bcmerror;
32165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
32175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3218e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_get_image(char *buf, int len, struct brcmf_sdio *bus)
32195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
32205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->firmware->size < bus->fw_ptr + len)
32215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = bus->firmware->size - bus->fw_ptr;
32225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memcpy(buf, &bus->firmware->data[bus->fw_ptr], len);
32245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fw_ptr += len;
32255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return len;
32265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
32275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3228e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus)
32295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
32305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int offset = 0;
32315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint len;
32325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 *memblock = NULL, *memptr;
32335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
32345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "Enter\n");
32365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
323752e1409f72d629644b496db80c119e04ebabad90Arend van Spriel	ret = request_firmware(&bus->firmware, BRCMF_SDIO_FW_NAME,
32385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       &bus->sdiodev->func[2]->dev);
32395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret) {
32405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Fail to request firmware %d\n", ret);
32415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return ret;
32425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
32435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fw_ptr = 0;
32445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC);
32465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (memblock == NULL) {
32475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -ENOMEM;
32485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
32495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
32505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((u32)(unsigned long)memblock % BRCMF_SDALIGN)
32515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		memptr += (BRCMF_SDALIGN -
32525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
32535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Download image */
32555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while ((len =
32565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_get_image((char *)memptr, MEMBLOCK, bus))) {
32575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = brcmf_sdbrcm_membytes(bus, true, offset, memptr, len);
32585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret) {
32595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "error %d on writing %d membytes at 0x%08x\n",
32605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  ret, MEMBLOCK, offset);
32615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto err;
32625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
32635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		offset += MEMBLOCK;
32655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
32665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32675b435de0d786869c95d1962121af0d7df2542009Arend van Sprielerr:
32685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(memblock);
32695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	release_firmware(bus->firmware);
32715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fw_ptr = 0;
32725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
32745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
32755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/*
32775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
32785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * and ending in a NUL.
32795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Removes carriage returns, empty lines, comment lines, and converts
32805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * newlines to NULs.
32815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * Shortens buffer as needed and pads with NULs.  End of buffer is marked
32825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel * by two NULs.
32835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel*/
32845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32855b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic uint brcmf_process_nvram_vars(char *varbuf, uint len)
32865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
32875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *dp;
32885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool findNewline;
32895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int column;
32905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint buf_len, n;
32915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	dp = varbuf;
32935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	findNewline = false;
32955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	column = 0;
32965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
32975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	for (n = 0; n < len; n++) {
32985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (varbuf[n] == 0)
32995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
33005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (varbuf[n] == '\r')
33015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
33025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (findNewline && varbuf[n] != '\n')
33035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
33045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		findNewline = false;
33055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (varbuf[n] == '#') {
33065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			findNewline = true;
33075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
33085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
33095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (varbuf[n] == '\n') {
33105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (column == 0)
33115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				continue;
33125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			*dp++ = 0;
33135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			column = 0;
33145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			continue;
33155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
33165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*dp++ = varbuf[n];
33175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		column++;
33185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	buf_len = dp - varbuf;
33205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (dp < varbuf + n)
33225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*dp++ = 0;
33235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return buf_len;
33255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
33265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3327e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus)
33285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
33295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint len;
33305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *memblock = NULL;
33315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	char *bufp;
33325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
33335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
333452e1409f72d629644b496db80c119e04ebabad90Arend van Spriel	ret = request_firmware(&bus->firmware, BRCMF_SDIO_NV_NAME,
33355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       &bus->sdiodev->func[2]->dev);
33365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret) {
33375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Fail to request nvram %d\n", ret);
33385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return ret;
33395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fw_ptr = 0;
33415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
33435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (memblock == NULL) {
33445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -ENOMEM;
33455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
33465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	len = brcmf_sdbrcm_get_image(memblock, MEMBLOCK, bus);
33495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (len > 0 && len < MEMBLOCK) {
33515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bufp = (char *)memblock;
33525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bufp[len] = 0;
33535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		len = brcmf_process_nvram_vars(bufp, len);
33545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bufp += len;
33555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		*bufp++ = 0;
33565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (len)
33575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			ret = brcmf_sdbrcm_downloadvars(bus, memblock, len + 1);
33585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret)
33595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "error downloading vars: %d\n", ret);
33605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	} else {
33615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "error reading nvram file: %d\n", len);
33625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ret = -EIO;
33635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33655b435de0d786869c95d1962121af0d7df2542009Arend van Sprielerr:
33665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(memblock);
33675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	release_firmware(bus->firmware);
33695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->fw_ptr = 0;
33705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
33725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
33735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3374e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
33755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
33765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int bcmerror = -1;
33775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Keep arm in reset */
33795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdbrcm_download_state(bus, true)) {
33805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "error placing ARM core in reset\n");
33815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
33825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* External image takes precedence if specified */
33855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdbrcm_download_code_file(bus)) {
33865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "dongle image file download failed\n");
33875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
33885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* External nvram takes precedence if specified */
33915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdbrcm_download_nvram(bus))
33925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "dongle nvram file download failed\n");
33935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
33945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Take arm out of reset */
33955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdbrcm_download_state(bus, false)) {
33965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "error getting out of ARM core reset\n");
33975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto err;
33985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
33995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bcmerror = 0;
34015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34025b435de0d786869c95d1962121af0d7df2542009Arend van Sprielerr:
34035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bcmerror;
34045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
34055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34065b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool
3407e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus)
34085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
34095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bool ret;
34105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Download the firmware */
34125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
34135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = _brcmf_sdbrcm_download_firmware(bus) == 0;
34155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
34175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
34195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
34205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
342199a0b8ff9105d9b78e7e4e6aaa077264707e4e1cFranky Linstatic int brcmf_sdbrcm_bus_init(struct device *dev)
34225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3423fa20b91143c616d402f1ed61f27693477279d0c6Franky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(dev);
34240a332e4678e4e4f0030f10827980c72d32300274Arend van Spriel	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio;
3425fa20b91143c616d402f1ed61f27693477279d0c6Franky Lin	struct brcmf_sdio *bus = sdiodev->bus;
34265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	unsigned long timeout;
34275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	uint retries = 0;
34285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 ready, enable;
34295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int err, ret = 0;
34305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 saveclk;
34315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
34335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* try to download image and nvram to the dongle */
3435fa20b91143c616d402f1ed61f27693477279d0c6Franky Lin	if (bus_if->state == BRCMF_BUS_DOWN) {
34365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!(brcmf_sdbrcm_download_firmware(bus)))
34375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			return -1;
34385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
34395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3440712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if (!bus->sdiodev->bus_if->drvr)
34415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return 0;
34425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Start the watchdog timer */
344428a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin	bus->tickcnt = 0;
34455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
34465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
34485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Make sure backplane clock is on, needed to generate F2 interrupt */
34505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
34515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->clkstate != CLK_AVAIL)
34525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto exit;
34535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Force clocks on backplane to be sure F2 interrupt propagates */
34555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	saveclk =
34565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	    brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
34575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  SBSDIO_FUNC1_CHIPCLKCSR, &err);
34585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!err) {
34595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
34605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SBSDIO_FUNC1_CHIPCLKCSR,
34615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       (saveclk | SBSDIO_FORCE_HT), &err);
34625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
34635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (err) {
34645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "Failed to force clock for F2: err %d\n", err);
34655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto exit;
34665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
34675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Enable function 2 (frame transfers) */
34695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT,
34705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		  offsetof(struct sdpcmd_regs, tosbmailboxdata), &retries);
34715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
34725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
34745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       enable, NULL);
34755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	timeout = jiffies + msecs_to_jiffies(BRCMF_WAIT_F2RDY);
34775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ready = 0;
34785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (enable != ready) {
34795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		ready = brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_0,
34805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					      SDIO_CCCR_IORx, NULL);
34815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (time_after(jiffies, timeout))
34825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
34835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		else if (time_after(jiffies, timeout - BRCMF_WAIT_F2RDY + 50))
34845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* prevent busy waiting if it takes too long */
34855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			msleep_interruptible(20);
34865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
34875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "enable 0x%02x, ready 0x%02x\n", enable, ready);
34895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If F2 successfully enabled, set core and enable interrupts */
34915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ready == enable) {
34925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Set up the interrupt mask and enable interrupts */
34935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->hostintmask = HOSTINTMASK;
34945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		w_sdreg32(bus, bus->hostintmask,
34955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  offsetof(struct sdpcmd_regs, hostintmask), &retries);
34965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
34975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
34985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SBSDIO_WATERMARK, 8, &err);
3499c0e89f084b5089acdfdda1cb45c88896fa0f5139Arend van Spriel	} else {
35005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Disable F2 again */
35015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		enable = SDIO_FUNC_ENABLE_1;
35025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0,
35035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				       SDIO_CCCR_IOEx, enable, NULL);
3504c0e89f084b5089acdfdda1cb45c88896fa0f5139Arend van Spriel		ret = -ENODEV;
35055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
35065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Restore previous clock setting */
35085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
35095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SBSDIO_FUNC1_CHIPCLKCSR, saveclk, &err);
35105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* If we didn't come up, turn off backplane clock */
3512c0e89f084b5089acdfdda1cb45c88896fa0f5139Arend van Spriel	if (!ret)
35135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
35145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35155b435de0d786869c95d1962121af0d7df2542009Arend van Sprielexit:
35165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
35175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return ret;
35195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
35205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35215b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_sdbrcm_isr(void *arg)
35225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3523e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus = (struct brcmf_sdio *) arg;
35245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
35265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!bus) {
35285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "bus is null pointer, exiting\n");
35295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
35305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
35315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3532712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) {
35335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "bus is down. we have nothing to do\n");
35345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
35355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
35365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Count the interrupt call */
35375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->intrcount++;
35385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->ipend = true;
35395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Shouldn't get this interrupt if we're sleeping? */
35415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->sleeping) {
35425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "INTERRUPT WHILE SLEEPING??\n");
35435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
35445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
35455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Disable additional interrupts (is this needed now)? */
35475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!bus->intr)
35485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "isr w/o interrupt configured!\n");
35495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->dpc_sched = true;
3551b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	if (bus->dpc_tsk) {
3552b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin		brcmf_sdbrcm_adddpctsk(bus);
35535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		complete(&bus->dpc_wait);
3554b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	}
35555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
35565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3557cad2b26b1010d0694d2f08d408486451b9f919d2Franky Linstatic bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus)
35585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
35598ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
3560cad2b26b1010d0694d2f08d408486451b9f919d2Franky Lin	struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev);
35618ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif	/* DEBUG */
35625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TIMER, "Enter\n");
35645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Ignore the timer if simulating bus down */
35665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->sleeping)
35675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return false;
35685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	down(&bus->sdsem);
35705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Poll period: check device if appropriate. */
35725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->poll && (++bus->polltick >= bus->pollrate)) {
35735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		u32 intstatus = 0;
35745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Reset poll tick */
35765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->polltick = 0;
35775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Check device if no interrupts */
35795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
35805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (!bus->dpc_sched) {
35825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				u8 devpend;
35835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				devpend = brcmf_sdcard_cfg_read(bus->sdiodev,
35845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						SDIO_FUNC_0, SDIO_CCCR_INTx,
35855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel						NULL);
35865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				intstatus =
35875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				    devpend & (INTR_STATUS_FUNC1 |
35885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					       INTR_STATUS_FUNC2);
35895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
35905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* If there is something, make like the ISR and
35925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				 schedule the DPC */
35935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (intstatus) {
35945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->pollcnt++;
35955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->ipend = true;
35965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
35975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->dpc_sched = true;
3598b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin				if (bus->dpc_tsk) {
3599b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin					brcmf_sdbrcm_adddpctsk(bus);
36005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					complete(&bus->dpc_wait);
3601b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin				}
36025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
36035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
36045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Update interrupt tracking */
36065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->lastintrs = bus->intrcount;
36075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
36088ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#ifdef DEBUG
36095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Poll for console output periodically */
3610cad2b26b1010d0694d2f08d408486451b9f919d2Franky Lin	if (bus_if->state == BRCMF_BUS_DATA &&
36118d169aa00d0356f915e84dbdf6c9be381cce34a4Franky Lin	    bus->console_interval != 0) {
36125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->console.count += BRCMF_WD_POLL_MS;
36135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->console.count >= bus->console_interval) {
36145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->console.count -= bus->console_interval;
36155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Make sure backplane clock is on */
36165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
36175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (brcmf_sdbrcm_readconsole(bus) < 0)
36185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* stop on error */
36195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->console_interval = 0;
36205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
36215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
36228ae746543c8370fd04c28aaf8f185c1687b0e694Joe Perches#endif				/* DEBUG */
36235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* On idle timeout clear activity flag and/or turn off clock */
36255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
36265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (++bus->idlecount >= bus->idletime) {
36275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->idlecount = 0;
36285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			if (bus->activity) {
36295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				bus->activity = false;
36305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS);
36315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			} else {
36325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
36335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			}
36345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
36355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
36365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	up(&bus->sdsem);
36385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bus->ipend;
36405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
36415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36425b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool brcmf_sdbrcm_chipmatch(u16 chipid)
36435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
36445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (chipid == BCM4329_CHIP_ID)
36455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return true;
3646ce2d7d7e8fd88191f5d1c92a8b33aeb0cb12ea34Franky Lin	if (chipid == BCM4330_CHIP_ID)
3647ce2d7d7e8fd88191f5d1c92a8b33aeb0cb12ea34Franky Lin		return true;
36485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return false;
36495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
36505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3651e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus)
36525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
36535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
36545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(bus->rxbuf);
36565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxctl = bus->rxbuf = NULL;
36575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxlen = 0;
36585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	kfree(bus->databuf);
36605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->databuf = NULL;
36615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
36625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3663e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus)
36645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
36655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
36665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3667b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin	if (bus->sdiodev->bus_if->maxctl) {
36685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxblen =
3669b01a6b3ca714e2bb86ee387aee487c7360363c93Franky Lin		    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN),
36705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			    ALIGNMENT) + BRCMF_SDALIGN;
36715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
36725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!(bus->rxbuf))
36735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto fail;
36745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
36755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Allocate buffer to receive glomed packet */
36775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
36785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(bus->databuf)) {
36795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* release rxbuf which was already located as above */
36805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!bus->rxblen)
36815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			kfree(bus->rxbuf);
36825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
36835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
36845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Align the buffer */
36865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
36875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->dataptr = bus->databuf + (BRCMF_SDALIGN -
36885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       ((unsigned long)bus->databuf % BRCMF_SDALIGN));
36895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	else
36905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->dataptr = bus->databuf;
36915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return true;
36935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36945b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
36955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return false;
36965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
36975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
36985b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic bool
3699e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva)
37005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
37015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u8 clkctl = 0;
37025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int err = 0;
37035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int reg_addr;
37045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	u32 reg_val;
370599ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	u8 idx;
37065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->alp_only = true;
37085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Return the window to backplane enumeration space for core access */
37105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (brcmf_sdcard_set_sbaddr_window(bus->sdiodev, SI_ENUM_BASE))
37115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "FAILED to return to SI_ENUM_BASE\n");
37125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
371318aad4f8e11530fd7e41d0faea0752c3a0ce799cJoe Perches	pr_debug("F1 signature read @0x18000000=0x%4x\n",
371418aad4f8e11530fd7e41d0faea0752c3a0ce799cJoe Perches		 brcmf_sdcard_reg_read(bus->sdiodev, SI_ENUM_BASE, 4));
37155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/*
3717a97e4fc5ae4b00187b25a8216a61b2105efa9c60Franky Lin	 * Force PLL off until brcmf_sdio_chip_attach()
37185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * programs PLL control regs
37195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 */
37205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
37225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SBSDIO_FUNC1_CHIPCLKCSR,
37235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       BRCMF_INIT_CLKCTL1, &err);
37245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!err)
37255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		clkctl =
37265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		    brcmf_sdcard_cfg_read(bus->sdiodev, SDIO_FUNC_1,
37275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					  SBSDIO_FUNC1_CHIPCLKCSR, &err);
37285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) {
37305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n",
37315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			  err, BRCMF_INIT_CLKCTL1, clkctl);
37325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
37335b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
37345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3735a97e4fc5ae4b00187b25a8216a61b2105efa9c60Franky Lin	if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) {
3736a97e4fc5ae4b00187b25a8216a61b2105efa9c60Franky Lin		brcmf_dbg(ERROR, "brcmf_sdio_chip_attach failed!\n");
37375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
37385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
37395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
37415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "unsupported chip: 0x%04x\n", bus->ci->chip);
37425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
37435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
37445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3745e12afb6c5d13ebff64d4a2feb97cce0c2d7e1128Franky Lin	brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci,
3746e12afb6c5d13ebff64d4a2feb97cce0c2d7e1128Franky Lin					  SDIO_DRIVE_STRENGTH);
37475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3748454d2a8816d6bc6594d3d475392290623af63656Franky Lin	/* Get info on the SOCRAM cores... */
37495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->ramsize = bus->ci->ramsize;
37505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(bus->ramsize)) {
37515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "failed to find SOCRAM memory!\n");
37525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
37535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
37545b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Set core control so an SDIO reset does a backplane reset */
375699ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV);
375799ba15cd75ed22e4ae86804ca2982a724e8102c2Franky Lin	reg_addr = bus->ci->c_inf[idx].base +
37585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		   offsetof(struct sdpcmd_regs, corecontrol);
37595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	reg_val = brcmf_sdcard_reg_read(bus->sdiodev, reg_addr, sizeof(u32));
37605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_reg_write(bus->sdiodev, reg_addr, sizeof(u32),
37615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       reg_val | CC_BPRESEN);
37625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
37645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Locate an appropriately-aligned portion of hdrbuf */
37665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0],
37675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				    BRCMF_SDALIGN);
37685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Set the poll and/or interrupt flags */
37705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->intr = true;
37715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->poll = false;
37725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->poll)
37735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->pollrate = 1;
37745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return true;
37765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37775b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
37785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return false;
37795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
37805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3781e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus)
37825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
37835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
37845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Disable F2 to clear any intermediate frame state on the dongle */
37865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_0, SDIO_CCCR_IOEx,
37875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SDIO_FUNC_ENABLE_1, NULL);
37885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3789712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN;
37905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->sleeping = false;
37915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxflow = false;
37925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Done with backplane-dependent accesses, can drop clock... */
37945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdcard_cfg_write(bus->sdiodev, SDIO_FUNC_1,
37955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			       SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
37965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
37975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* ...and initialize clock/power states */
37985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->clkstate = CLK_SDONLY;
37995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->idletime = BRCMF_IDLE_INTERVAL;
38005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->idleclock = BRCMF_IDLE_ACTIVE;
38015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Query the F2 block size, set roundup accordingly */
38035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->blocksize = bus->sdiodev->func[2]->cur_blksize;
38045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->roundup = min(max_roundup, bus->blocksize);
38055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* bus module does not support packet chaining */
38075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->use_rxchain = false;
38085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->sd_rxchain = false;
38095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return true;
38115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
38125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38135b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic int
38145b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_sdbrcm_watchdog_thread(void *data)
38155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3816e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
38175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	allow_signal(SIGTERM);
38195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Run until signal received */
38205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	while (1) {
38215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (kthread_should_stop())
38225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
38235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (!wait_for_completion_interruptible(&bus->watchdog_wait)) {
3824cad2b26b1010d0694d2f08d408486451b9f919d2Franky Lin			brcmf_sdbrcm_bus_watchdog(bus);
38255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Count the tick for reference */
382628a1a3bdaf4cce5ee8e473c332e2f371888341bbFranky Lin			bus->tickcnt++;
38275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else
38285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			break;
38295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
38305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return 0;
38315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
38325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38335b435de0d786869c95d1962121af0d7df2542009Arend van Sprielstatic void
38345b435de0d786869c95d1962121af0d7df2542009Arend van Sprielbrcmf_sdbrcm_watchdog(unsigned long data)
38355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
3836e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus = (struct brcmf_sdio *)data;
38375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->watchdog_tsk) {
38395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		complete(&bus->watchdog_wait);
38405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* Reschedule the watchdog */
38415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->wd_timer_valid)
38425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			mod_timer(&bus->timer,
38435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				  jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
38445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
38455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
38465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3847e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus)
38485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
38495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
38505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus->ci) {
38525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
38535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
3854a8a6c04586233e12551552c292797cb56b31dadeFranky Lin		brcmf_sdio_chip_detach(&bus->ci);
38555b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->vars && bus->varsz)
38565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			kfree(bus->vars);
38575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->vars = NULL;
38585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
38595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Disconnected\n");
38615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
38625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel/* Detach and free everything */
3864e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linstatic void brcmf_sdbrcm_release(struct brcmf_sdio *bus)
38655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
38665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
38675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus) {
38695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		/* De-register interrupt handler */
38705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdcard_intr_dereg(bus->sdiodev);
38715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38725f947ad942a72e7f96942da97d719dd62037dbc2Franky Lin		if (bus->sdiodev->bus_if->drvr) {
38735f947ad942a72e7f96942da97d719dd62037dbc2Franky Lin			brcmf_detach(bus->sdiodev->dev);
38745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_sdbrcm_release_dongle(bus);
38755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
38765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_release_malloc(bus);
38785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		kfree(bus);
38805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
38815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Disconnected\n");
38835b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
38845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38854175b88bd22022a60de175f94fdb303bed087eb9Franky Linvoid *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev)
38865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
38875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	int ret;
3888e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus;
38895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38905b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
38915b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38925b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* We make an assumption about address window mappings:
38935b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	 * regsva == SI_ENUM_BASE*/
38945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
38955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Allocate private bus interface state */
3896e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC);
38975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!bus)
38985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
38995b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->sdiodev = sdiodev;
39015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	sdiodev->bus = bus;
3902b83db862ffb871e3131e5d2160c741b288eea9aaArend van Spriel	skb_queue_head_init(&bus->glom);
39035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->txbound = BRCMF_TXBOUND;
39045b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->rxbound = BRCMF_RXBOUND;
39055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->txminmax = BRCMF_TXMINMAX;
39065b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
39075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->usebufpool = false;	/* Use bufpool if allocated,
39085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					 else use locally malloced rxbuf */
39095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* attempt to attach to the dongle */
39115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) {
39125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_attach failed\n");
39135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39165b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	spin_lock_init(&bus->txqlock);
39175b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	init_waitqueue_head(&bus->ctrl_wait);
39185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	init_waitqueue_head(&bus->dcmd_resp_wait);
39195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39205b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Set up the watchdog timer */
39215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	init_timer(&bus->timer);
39225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->timer.data = (unsigned long)bus;
39235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->timer.function = brcmf_sdbrcm_watchdog;
39245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Initialize thread based operation and lock */
39265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	sema_init(&bus->sdsem, 1);
39275b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39285b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Initialize watchdog thread */
39295b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	init_completion(&bus->watchdog_wait);
39305b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread,
39315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel					bus, "brcmf_watchdog");
39325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (IS_ERR(bus->watchdog_tsk)) {
393302f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches		pr_warn("brcmf_watchdog thread failed to start\n");
39345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->watchdog_tsk = NULL;
39355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Initialize DPC thread */
39375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	init_completion(&bus->dpc_wait);
3938b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	INIT_LIST_HEAD(&bus->dpc_tsklst);
3939b948a85c1f26d48395de8c6c7e392f008f1be666Franky Lin	spin_lock_init(&bus->dpc_tl_lock);
39405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	bus->dpc_tsk = kthread_run(brcmf_sdbrcm_dpc_thread,
39415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				   bus, "brcmf_dpc");
39425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (IS_ERR(bus->dpc_tsk)) {
394302f77195db6ce252d5488b6d48d8edc1c5e2aa30Joe Perches		pr_warn("brcmf_dpc thread failed to start\n");
39445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->dpc_tsk = NULL;
39455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
3947a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	/* Assign bus interface call back */
3948a9ffda88be7416b8336f644806c2b3ed3ce08b26Franky Lin	bus->sdiodev->bus_if->brcmf_bus_stop = brcmf_sdbrcm_bus_stop;
394999a0b8ff9105d9b78e7e4e6aaa077264707e4e1cFranky Lin	bus->sdiodev->bus_if->brcmf_bus_init = brcmf_sdbrcm_bus_init;
3950b9692d17e842fadb8c18faf24f550db80886763eFranky Lin	bus->sdiodev->bus_if->brcmf_bus_txdata = brcmf_sdbrcm_bus_txdata;
3951fcf094f414f9e9c088f6c2aa9e19a59f7b41e1f5Franky Lin	bus->sdiodev->bus_if->brcmf_bus_txctl = brcmf_sdbrcm_bus_txctl;
3952fcf094f414f9e9c088f6c2aa9e19a59f7b41e1f5Franky Lin	bus->sdiodev->bus_if->brcmf_bus_rxctl = brcmf_sdbrcm_bus_rxctl;
39535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Attach to the brcmf/OS/network interface */
39542447ffb0bdf89d14c9a9503e33b32b73d3040feeFranky Lin	ret = brcmf_attach(SDPCM_RESERVE, bus->sdiodev->dev);
3955712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if (ret != 0) {
39565b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "brcmf_attach failed\n");
39575b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39585b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39595b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39605b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Allocate buffers */
39615b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(brcmf_sdbrcm_probe_malloc(bus))) {
39625b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_malloc failed\n");
39635b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39645b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39655b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39665b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (!(brcmf_sdbrcm_probe_init(bus))) {
39675b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "brcmf_sdbrcm_probe_init failed\n");
39685b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39695b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39705b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39715b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Register interrupt callback, but mask it (not operational yet). */
39725b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INTR, "disable SDIO interrupts (not interested yet)\n");
39735b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	ret = brcmf_sdcard_intr_reg(bus->sdiodev);
39745b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret != 0) {
39755b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_dbg(ERROR, "FAILED: sdcard_intr_reg returned %d\n", ret);
39765b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39775b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39785b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INTR, "registered SDIO interrupt function ok\n");
39795b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39805b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(INFO, "completed!!\n");
39815b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39825b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* if firmware path present try to download and bring up bus */
3983ed683c986f6fff6b9d9fe2adc8b11e0b0be7c085Franky Lin	ret = brcmf_bus_start(bus->sdiodev->dev);
39845b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (ret != 0) {
39855b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (ret == -ENOLINK) {
39865b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			brcmf_dbg(ERROR, "dongle is not responding\n");
39875b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			goto fail;
39885b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
39895b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
399015d45b6fbd01ecebc5a77b1e06ae7ebffad8018aFranky Lin
399115d45b6fbd01ecebc5a77b1e06ae7ebffad8018aFranky Lin	/* add interface and open for business */
399255a63bcc4cdeabf76f7e42a76d0c59dbe37d0d64Franky Lin	if (brcmf_add_if(bus->sdiodev->dev, 0, "wlan%d", NULL)) {
399315d45b6fbd01ecebc5a77b1e06ae7ebffad8018aFranky Lin		brcmf_dbg(ERROR, "Add primary net device interface failed!!\n");
39945b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		goto fail;
39955b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
39965b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39975b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return bus;
39985b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
39995b435de0d786869c95d1962121af0d7df2542009Arend van Sprielfail:
40005b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_sdbrcm_release(bus);
40015b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	return NULL;
40025b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
40035b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40045b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid brcmf_sdbrcm_disconnect(void *ptr)
40055b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
4006e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Lin	struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr;
40075b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40085b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Enter\n");
40095b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40105b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (bus)
40115b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		brcmf_sdbrcm_release(bus);
40125b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40135b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	brcmf_dbg(TRACE, "Disconnected\n");
40145b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
40155b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40165b435de0d786869c95d1962121af0d7df2542009Arend van Sprielvoid
4017e92eedf4e080fc0bd98e892cb9d31d2163ae8b29Franky Linbrcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick)
40185b435de0d786869c95d1962121af0d7df2542009Arend van Spriel{
40195b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	/* Totally stop the timer */
402023677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches	if (!wdtick && bus->wd_timer_valid) {
40215b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		del_timer_sync(&bus->timer);
40225b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->wd_timer_valid = false;
40235b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->save_ms = wdtick;
40245b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		return;
40255b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
40265b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
4027ece960eae81c604aa14a1bf431eda34f4fe71c0cFranky Lin	/* don't start the wd until fw is loaded */
4028712ac5b37a3348cac4e040c551a6ca6186d33682Franky Lin	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN)
4029ece960eae81c604aa14a1bf431eda34f4fe71c0cFranky Lin		return;
4030ece960eae81c604aa14a1bf431eda34f4fe71c0cFranky Lin
40315b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	if (wdtick) {
40325b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		if (bus->save_ms != BRCMF_WD_POLL_MS) {
403323677ce3172fcb93522a1df077d21019e73ee1e3Joe Perches			if (bus->wd_timer_valid)
40345b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				/* Stop timer and restart at new value */
40355b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				del_timer_sync(&bus->timer);
40365b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40375b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Create timer again when watchdog period is
40385b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			   dynamically changed or in the first instance
40395b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			 */
40405b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			bus->timer.expires =
40415b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				jiffies + BRCMF_WD_POLL_MS * HZ / 1000;
40425b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			add_timer(&bus->timer);
40435b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40445b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		} else {
40455b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			/* Re arm the timer, at last watchdog period */
40465b435de0d786869c95d1962121af0d7df2542009Arend van Spriel			mod_timer(&bus->timer,
40475b435de0d786869c95d1962121af0d7df2542009Arend van Spriel				jiffies + BRCMF_WD_POLL_MS * HZ / 1000);
40485b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		}
40495b435de0d786869c95d1962121af0d7df2542009Arend van Spriel
40505b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->wd_timer_valid = true;
40515b435de0d786869c95d1962121af0d7df2542009Arend van Spriel		bus->save_ms = wdtick;
40525b435de0d786869c95d1962121af0d7df2542009Arend van Spriel	}
40535b435de0d786869c95d1962121af0d7df2542009Arend van Spriel}
4054