| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Copyright (c) 2010 Broadcom Corporation | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Permission to use, copy, modify, and/or distribute this software for any | 
					
						
							|  |  |  |  * purpose with or without fee is hereby granted, provided that the above | 
					
						
							|  |  |  |  * copyright notice and this permission notice appear in all copies. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | 
					
						
							|  |  |  |  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | 
					
						
							|  |  |  |  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY | 
					
						
							|  |  |  |  * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | 
					
						
							|  |  |  |  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION | 
					
						
							|  |  |  |  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN | 
					
						
							|  |  |  |  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include <linux/types.h>
 | 
					
						
							|  |  |  | #include <linux/kernel.h>
 | 
					
						
							|  |  |  | #include <linux/kthread.h>
 | 
					
						
							|  |  |  | #include <linux/printk.h>
 | 
					
						
							|  |  |  | #include <linux/pci_ids.h>
 | 
					
						
							|  |  |  | #include <linux/netdevice.h>
 | 
					
						
							|  |  |  | #include <linux/interrupt.h>
 | 
					
						
							|  |  |  | #include <linux/sched.h>
 | 
					
						
							|  |  |  | #include <linux/mmc/sdio.h>
 | 
					
						
							|  |  |  | #include <linux/mmc/sdio_func.h>
 | 
					
						
							|  |  |  | #include <linux/mmc/card.h>
 | 
					
						
							|  |  |  | #include <linux/semaphore.h>
 | 
					
						
							|  |  |  | #include <linux/firmware.h>
 | 
					
						
							| 
									
										
										
										
											2011-10-12 21:35:07 +02:00
										 |  |  | #include <linux/module.h>
 | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:42 +01:00
										 |  |  | #include <linux/bcma/bcma.h>
 | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | #include <linux/debugfs.h>
 | 
					
						
							| 
									
										
										
										
											2012-06-21 11:33:32 +02:00
										 |  |  | #include <linux/vmalloc.h>
 | 
					
						
							| 
									
										
										
										
											2013-04-12 10:55:55 +02:00
										 |  |  | #include <linux/platform_data/brcmfmac-sdio.h>
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | #include <linux/moduleparam.h>
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #include <asm/unaligned.h>
 | 
					
						
							|  |  |  | #include <defs.h>
 | 
					
						
							|  |  |  | #include <brcmu_wifi.h>
 | 
					
						
							|  |  |  | #include <brcmu_utils.h>
 | 
					
						
							|  |  |  | #include <brcm_hw_ids.h>
 | 
					
						
							|  |  |  | #include <soc.h>
 | 
					
						
							|  |  |  | #include "sdio_host.h"
 | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:28 +01:00
										 |  |  | #include "sdio_chip.h"
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define DCMD_RESP_TIMEOUT  2000	/* In milli second */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define BRCMF_TRAP_INFO_SIZE	80
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define CBUF_LEN	(128)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | /* Device console log buffer state */ | 
					
						
							|  |  |  | #define CONSOLE_BUFFER_MAX	2024
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | struct rte_log_le { | 
					
						
							|  |  |  | 	__le32 buf;		/* Can't be pointer on (64-bit) hosts */ | 
					
						
							|  |  |  | 	__le32 buf_size; | 
					
						
							|  |  |  | 	__le32 idx; | 
					
						
							|  |  |  | 	char *_buf_compat;	/* Redundant pointer for backward compat. */ | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct rte_console { | 
					
						
							|  |  |  | 	/* Virtual UART
 | 
					
						
							|  |  |  | 	 * When there is no UART (e.g. Quickturn), | 
					
						
							|  |  |  | 	 * the host should write a complete | 
					
						
							|  |  |  | 	 * input line directly into cbuf and then write | 
					
						
							|  |  |  | 	 * the length into vcons_in. | 
					
						
							|  |  |  | 	 * This may also be used when there is a real UART | 
					
						
							|  |  |  | 	 * (at risk of conflicting with | 
					
						
							|  |  |  | 	 * the real UART).  vcons_out is currently unused. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	uint vcons_in; | 
					
						
							|  |  |  | 	uint vcons_out; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Output (logging) buffer
 | 
					
						
							|  |  |  | 	 * Console output is written to a ring buffer log_buf at index log_idx. | 
					
						
							|  |  |  | 	 * The host may read the output when it sees log_idx advance. | 
					
						
							|  |  |  | 	 * Output will be lost if the output wraps around faster than the host | 
					
						
							|  |  |  | 	 * polls. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	struct rte_log_le log_le; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Console input line buffer
 | 
					
						
							|  |  |  | 	 * Characters are read one at a time into cbuf | 
					
						
							|  |  |  | 	 * until <CR> is received, then | 
					
						
							|  |  |  | 	 * the buffer is processed as a command line. | 
					
						
							|  |  |  | 	 * Also used for virtual UART. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	uint cbuf_idx; | 
					
						
							|  |  |  | 	char cbuf[CBUF_LEN]; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #include <chipcommon.h>
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #include "dhd_bus.h"
 | 
					
						
							|  |  |  | #include "dhd_dbg.h"
 | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:44 +02:00
										 |  |  | #include "tracepoint.h"
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define TXQLEN		2048	/* bulk tx queue length */
 | 
					
						
							|  |  |  | #define TXHI		(TXQLEN - 256)	/* turn on flow control above TXHI */
 | 
					
						
							|  |  |  | #define TXLOW		(TXHI - 256)	/* turn off flow control below TXLOW */
 | 
					
						
							|  |  |  | #define PRIOMASK	7
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define TXRETRIES	2	/* # of retries for tx frames */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BRCMF_RXBOUND	50	/* Default for max rx frames in
 | 
					
						
							|  |  |  | 				 one scheduling */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BRCMF_TXBOUND	20	/* Default for max tx frames in
 | 
					
						
							|  |  |  | 				 one scheduling */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | #define BRCMF_DEFAULT_TXGLOM_SIZE	32  /* max tx frames in glom chain */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #define BRCMF_TXMINMAX	1	/* Max tx frames if rx still pending */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define MEMBLOCK	2048	/* Block size used for downloading
 | 
					
						
							|  |  |  | 				 of dongle image */ | 
					
						
							|  |  |  | #define MAX_DATA_BUF	(32 * 1024)	/* Must be large enough to hold
 | 
					
						
							|  |  |  | 				 biggest possible glom */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BRCMF_FIRSTREAD	(1 << 6)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SBSDIO_DEVICE_CTL */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* 1: device will assert busy signal when receiving CMD53 */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_SETBUSY		0x01
 | 
					
						
							|  |  |  | /* 1: assertion of sdio interrupt is synchronous to the sdio clock */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_SPI_INTR_SYNC	0x02
 | 
					
						
							|  |  |  | /* 1: mask all interrupts to host except the chipActive (rev 8) */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_CA_INT_ONLY	0x04
 | 
					
						
							|  |  |  | /* 1: isolate internal sdio signals, put external pads in tri-state; requires
 | 
					
						
							|  |  |  |  * sdio bus power cycle to clear (rev 9) */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_PADS_ISO		0x08
 | 
					
						
							|  |  |  | /* Force SD->SB reset mapping (rev 11) */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_SB_RST_CTL	0x30
 | 
					
						
							|  |  |  | /*   Determined by CoreControl bit */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_RST_CORECTL	0x00
 | 
					
						
							|  |  |  | /*   Force backplane reset */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_RST_BPRESET	0x10
 | 
					
						
							|  |  |  | /*   Force no backplane reset */ | 
					
						
							|  |  |  | #define SBSDIO_DEVCTL_RST_NOBPRESET	0x20
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* direct(mapped) cis space */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* MAPPED common CIS address */ | 
					
						
							|  |  |  | #define SBSDIO_CIS_BASE_COMMON		0x1000
 | 
					
						
							|  |  |  | /* maximum bytes in one CIS */ | 
					
						
							|  |  |  | #define SBSDIO_CIS_SIZE_LIMIT		0x200
 | 
					
						
							|  |  |  | /* cis offset addr is < 17 bits */ | 
					
						
							|  |  |  | #define SBSDIO_CIS_OFT_ADDR_MASK	0x1FFFF
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* manfid tuple length, include tuple, link bytes */ | 
					
						
							|  |  |  | #define SBSDIO_CIS_MANFID_TUPLE_LEN	6
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* intstatus */ | 
					
						
							|  |  |  | #define I_SMB_SW0	(1 << 0)	/* To SB Mail S/W interrupt 0 */
 | 
					
						
							|  |  |  | #define I_SMB_SW1	(1 << 1)	/* To SB Mail S/W interrupt 1 */
 | 
					
						
							|  |  |  | #define I_SMB_SW2	(1 << 2)	/* To SB Mail S/W interrupt 2 */
 | 
					
						
							|  |  |  | #define I_SMB_SW3	(1 << 3)	/* To SB Mail S/W interrupt 3 */
 | 
					
						
							|  |  |  | #define I_SMB_SW_MASK	0x0000000f	/* To SB Mail S/W interrupts mask */
 | 
					
						
							|  |  |  | #define I_SMB_SW_SHIFT	0	/* To SB Mail S/W interrupts shift */
 | 
					
						
							|  |  |  | #define I_HMB_SW0	(1 << 4)	/* To Host Mail S/W interrupt 0 */
 | 
					
						
							|  |  |  | #define I_HMB_SW1	(1 << 5)	/* To Host Mail S/W interrupt 1 */
 | 
					
						
							|  |  |  | #define I_HMB_SW2	(1 << 6)	/* To Host Mail S/W interrupt 2 */
 | 
					
						
							|  |  |  | #define I_HMB_SW3	(1 << 7)	/* To Host Mail S/W interrupt 3 */
 | 
					
						
							|  |  |  | #define I_HMB_SW_MASK	0x000000f0	/* To Host Mail S/W interrupts mask */
 | 
					
						
							|  |  |  | #define I_HMB_SW_SHIFT	4	/* To Host Mail S/W interrupts shift */
 | 
					
						
							|  |  |  | #define I_WR_OOSYNC	(1 << 8)	/* Write Frame Out Of Sync */
 | 
					
						
							|  |  |  | #define I_RD_OOSYNC	(1 << 9)	/* Read Frame Out Of Sync */
 | 
					
						
							|  |  |  | #define	I_PC		(1 << 10)	/* descriptor error */
 | 
					
						
							|  |  |  | #define	I_PD		(1 << 11)	/* data error */
 | 
					
						
							|  |  |  | #define	I_DE		(1 << 12)	/* Descriptor protocol Error */
 | 
					
						
							|  |  |  | #define	I_RU		(1 << 13)	/* Receive descriptor Underflow */
 | 
					
						
							|  |  |  | #define	I_RO		(1 << 14)	/* Receive fifo Overflow */
 | 
					
						
							|  |  |  | #define	I_XU		(1 << 15)	/* Transmit fifo Underflow */
 | 
					
						
							|  |  |  | #define	I_RI		(1 << 16)	/* Receive Interrupt */
 | 
					
						
							|  |  |  | #define I_BUSPWR	(1 << 17)	/* SDIO Bus Power Change (rev 9) */
 | 
					
						
							|  |  |  | #define I_XMTDATA_AVAIL (1 << 23)	/* bits in fifo */
 | 
					
						
							|  |  |  | #define	I_XI		(1 << 24)	/* Transmit Interrupt */
 | 
					
						
							|  |  |  | #define I_RF_TERM	(1 << 25)	/* Read Frame Terminate */
 | 
					
						
							|  |  |  | #define I_WF_TERM	(1 << 26)	/* Write Frame Terminate */
 | 
					
						
							|  |  |  | #define I_PCMCIA_XU	(1 << 27)	/* PCMCIA Transmit FIFO Underflow */
 | 
					
						
							|  |  |  | #define I_SBINT		(1 << 28)	/* sbintstatus Interrupt */
 | 
					
						
							|  |  |  | #define I_CHIPACTIVE	(1 << 29)	/* chip from doze to active state */
 | 
					
						
							|  |  |  | #define I_SRESET	(1 << 30)	/* CCCR RES interrupt */
 | 
					
						
							|  |  |  | #define I_IOE2		(1U << 31)	/* CCCR IOE2 Bit Changed */
 | 
					
						
							|  |  |  | #define	I_ERRORS	(I_PC | I_PD | I_DE | I_RU | I_RO | I_XU)
 | 
					
						
							|  |  |  | #define I_DMA		(I_RI | I_XI | I_ERRORS)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* corecontrol */ | 
					
						
							|  |  |  | #define CC_CISRDY		(1 << 0)	/* CIS Ready */
 | 
					
						
							|  |  |  | #define CC_BPRESEN		(1 << 1)	/* CCCR RES signal */
 | 
					
						
							|  |  |  | #define CC_F2RDY		(1 << 2)	/* set CCCR IOR2 bit */
 | 
					
						
							|  |  |  | #define CC_CLRPADSISO		(1 << 3)	/* clear SDIO pads isolation */
 | 
					
						
							|  |  |  | #define CC_XMTDATAAVAIL_MODE	(1 << 4)
 | 
					
						
							|  |  |  | #define CC_XMTDATAAVAIL_CTRL	(1 << 5)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* SDA_FRAMECTRL */ | 
					
						
							|  |  |  | #define SFC_RF_TERM	(1 << 0)	/* Read Frame Terminate */
 | 
					
						
							|  |  |  | #define SFC_WF_TERM	(1 << 1)	/* Write Frame Terminate */
 | 
					
						
							|  |  |  | #define SFC_CRC4WOOS	(1 << 2)	/* CRC error for write out of sync */
 | 
					
						
							|  |  |  | #define SFC_ABORTALL	(1 << 3)	/* Abort all in-progress frames */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Software allocation of To SB Mailbox resources | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* tosbmailbox bits corresponding to intstatus bits */ | 
					
						
							|  |  |  | #define SMB_NAK		(1 << 0)	/* Frame NAK */
 | 
					
						
							|  |  |  | #define SMB_INT_ACK	(1 << 1)	/* Host Interrupt ACK */
 | 
					
						
							|  |  |  | #define SMB_USE_OOB	(1 << 2)	/* Use OOB Wakeup */
 | 
					
						
							|  |  |  | #define SMB_DEV_INT	(1 << 3)	/* Miscellaneous Interrupt */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* tosbmailboxdata */ | 
					
						
							|  |  |  | #define SMB_DATA_VERSION_SHIFT	16	/* host protocol version */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Software allocation of To Host Mailbox resources | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* intstatus bits */ | 
					
						
							|  |  |  | #define I_HMB_FC_STATE	I_HMB_SW0	/* Flow Control State */
 | 
					
						
							|  |  |  | #define I_HMB_FC_CHANGE	I_HMB_SW1	/* Flow Control State Changed */
 | 
					
						
							|  |  |  | #define I_HMB_FRAME_IND	I_HMB_SW2	/* Frame Indication */
 | 
					
						
							|  |  |  | #define I_HMB_HOST_INT	I_HMB_SW3	/* Miscellaneous Interrupt */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* tohostmailboxdata */ | 
					
						
							|  |  |  | #define HMB_DATA_NAKHANDLED	1	/* retransmit NAK'd frame */
 | 
					
						
							|  |  |  | #define HMB_DATA_DEVREADY	2	/* talk to host after enable */
 | 
					
						
							|  |  |  | #define HMB_DATA_FC		4	/* per prio flowcontrol update flag */
 | 
					
						
							|  |  |  | #define HMB_DATA_FWREADY	8	/* fw ready for protocol activity */
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HMB_DATA_FCDATA_MASK	0xff000000
 | 
					
						
							|  |  |  | #define HMB_DATA_FCDATA_SHIFT	24
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HMB_DATA_VERSION_MASK	0x00ff0000
 | 
					
						
							|  |  |  | #define HMB_DATA_VERSION_SHIFT	16
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Software-defined protocol header | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Current protocol version */ | 
					
						
							|  |  |  | #define SDPCM_PROT_VERSION	4
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Shared structure between dongle and the host. | 
					
						
							|  |  |  |  * The structure contains pointers to trap or assert information. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | #define SDPCM_SHARED_VERSION       0x0003
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #define SDPCM_SHARED_VERSION_MASK  0x00FF
 | 
					
						
							|  |  |  | #define SDPCM_SHARED_ASSERT_BUILT  0x0100
 | 
					
						
							|  |  |  | #define SDPCM_SHARED_ASSERT        0x0200
 | 
					
						
							|  |  |  | #define SDPCM_SHARED_TRAP          0x0400
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Space for header read, limit for data packets */ | 
					
						
							|  |  |  | #define MAX_HDR_READ	(1 << 6)
 | 
					
						
							|  |  |  | #define MAX_RX_DATASZ	2048
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Bump up limit on waiting for HT to account for first startup;
 | 
					
						
							|  |  |  |  * if the image is doing a CRC calculation before programming the PMU | 
					
						
							|  |  |  |  * for HT availability, it could take a couple hundred ms more, so | 
					
						
							|  |  |  |  * max out at a 1 second (1000000us). | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #undef PMU_MAX_TRANSITION_DLY
 | 
					
						
							|  |  |  | #define PMU_MAX_TRANSITION_DLY 1000000
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Value for ChipClockCSR during initial setup */ | 
					
						
							|  |  |  | #define BRCMF_INIT_CLKCTL1	(SBSDIO_FORCE_HW_CLKREQ_OFF |	\
 | 
					
						
							|  |  |  | 					SBSDIO_ALP_AVAIL_REQ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Flags for SDH calls */ | 
					
						
							|  |  |  | #define F2SYNC	(SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:18 -08:00
										 |  |  | #define BRCMF_IDLE_IMMEDIATE	(-1)	/* Enter idle immediately */
 | 
					
						
							|  |  |  | #define BRCMF_IDLE_ACTIVE	0	/* Do not request any SD clock change
 | 
					
						
							|  |  |  | 					 * when idle | 
					
						
							|  |  |  | 					 */ | 
					
						
							|  |  |  | #define BRCMF_IDLE_INTERVAL	1
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | #define KSO_WAIT_US 50
 | 
					
						
							|  |  |  | #define MAX_KSO_ATTEMPTS (PMU_MAX_TRANSITION_DLY/KSO_WAIT_US)
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | /*
 | 
					
						
							|  |  |  |  * Conversion of 802.1D priority to precedence level | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static uint prio2prec(u32 prio) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return (prio == PRIO_8021D_NONE || prio == PRIO_8021D_BE) ? | 
					
						
							|  |  |  | 	       (prio^2) : prio; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | /* Device console log buffer state */ | 
					
						
							|  |  |  | struct brcmf_console { | 
					
						
							|  |  |  | 	uint count;		/* Poll interval msec counter */ | 
					
						
							|  |  |  | 	uint log_addr;		/* Log struct address (fixed) */ | 
					
						
							|  |  |  | 	struct rte_log_le log_le;	/* Log struct (host copy) */ | 
					
						
							|  |  |  | 	uint bufsize;		/* Size of log buffer */ | 
					
						
							|  |  |  | 	u8 *buf;		/* Log buffer (host copy) */ | 
					
						
							|  |  |  | 	uint last;		/* Last buffer read index */ | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct brcmf_trap_info { | 
					
						
							|  |  |  | 	__le32		type; | 
					
						
							|  |  |  | 	__le32		epc; | 
					
						
							|  |  |  | 	__le32		cpsr; | 
					
						
							|  |  |  | 	__le32		spsr; | 
					
						
							|  |  |  | 	__le32		r0;	/* a1 */ | 
					
						
							|  |  |  | 	__le32		r1;	/* a2 */ | 
					
						
							|  |  |  | 	__le32		r2;	/* a3 */ | 
					
						
							|  |  |  | 	__le32		r3;	/* a4 */ | 
					
						
							|  |  |  | 	__le32		r4;	/* v1 */ | 
					
						
							|  |  |  | 	__le32		r5;	/* v2 */ | 
					
						
							|  |  |  | 	__le32		r6;	/* v3 */ | 
					
						
							|  |  |  | 	__le32		r7;	/* v4 */ | 
					
						
							|  |  |  | 	__le32		r8;	/* v5 */ | 
					
						
							|  |  |  | 	__le32		r9;	/* sb/v6 */ | 
					
						
							|  |  |  | 	__le32		r10;	/* sl/v7 */ | 
					
						
							|  |  |  | 	__le32		r11;	/* fp/v8 */ | 
					
						
							|  |  |  | 	__le32		r12;	/* ip */ | 
					
						
							|  |  |  | 	__le32		r13;	/* sp */ | 
					
						
							|  |  |  | 	__le32		r14;	/* lr */ | 
					
						
							|  |  |  | 	__le32		pc;	/* r15 */ | 
					
						
							|  |  |  | }; | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct sdpcm_shared { | 
					
						
							|  |  |  | 	u32 flags; | 
					
						
							|  |  |  | 	u32 trap_addr; | 
					
						
							|  |  |  | 	u32 assert_exp_addr; | 
					
						
							|  |  |  | 	u32 assert_file_addr; | 
					
						
							|  |  |  | 	u32 assert_line; | 
					
						
							|  |  |  | 	u32 console_addr;	/* Address of struct rte_console */ | 
					
						
							|  |  |  | 	u32 msgtrace_addr; | 
					
						
							|  |  |  | 	u8 tag[32]; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	u32 brpt_addr; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct sdpcm_shared_le { | 
					
						
							|  |  |  | 	__le32 flags; | 
					
						
							|  |  |  | 	__le32 trap_addr; | 
					
						
							|  |  |  | 	__le32 assert_exp_addr; | 
					
						
							|  |  |  | 	__le32 assert_file_addr; | 
					
						
							|  |  |  | 	__le32 assert_line; | 
					
						
							|  |  |  | 	__le32 console_addr;	/* Address of struct rte_console */ | 
					
						
							|  |  |  | 	__le32 msgtrace_addr; | 
					
						
							|  |  |  | 	u8 tag[32]; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	__le32 brpt_addr; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | /* dongle SDIO bus specific header info */ | 
					
						
							|  |  |  | struct brcmf_sdio_hdrinfo { | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	u8 seq_num; | 
					
						
							|  |  |  | 	u8 channel; | 
					
						
							|  |  |  | 	u16 len; | 
					
						
							|  |  |  | 	u16 len_left; | 
					
						
							|  |  |  | 	u16 len_nxtfrm; | 
					
						
							|  |  |  | 	u8 dat_offset; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	bool lastfrm; | 
					
						
							|  |  |  | 	u16 tail_pad; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | }; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | /* misc chip info needed by some of the routines */ | 
					
						
							|  |  |  | /* Private data for SDIO bus interaction */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | struct brcmf_sdio { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev;	/* sdio device handler */ | 
					
						
							|  |  |  | 	struct chip_info *ci;	/* Chip info struct */ | 
					
						
							|  |  |  | 	char *vars;		/* Variables (from CIS and/or other) */ | 
					
						
							|  |  |  | 	uint varsz;		/* Size of variables buffer */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u32 ramsize;		/* Size of RAM in SOCRAM (bytes) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u32 hostintmask;	/* Copy of Host Interrupt Mask */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	atomic_t intstatus;	/* Intstatus bits (events) pending */ | 
					
						
							|  |  |  | 	atomic_t fcstate;	/* State of dongle flow-control */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	uint blocksize;		/* Block size of SDIO transfers */ | 
					
						
							|  |  |  | 	uint roundup;		/* Max roundup limit */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct pktq txq;	/* Queue length used for flow-control */ | 
					
						
							|  |  |  | 	u8 flowcontrol;	/* per prio flow control bitmask */ | 
					
						
							|  |  |  | 	u8 tx_seq;		/* Transmit sequence number (next) */ | 
					
						
							|  |  |  | 	u8 tx_max;		/* Maximum transmit sequence allowed */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	u8 *hdrbuf;		/* buffer for handling rx frame */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	u8 *rxhdr;		/* Header of current rx frame (in hdrbuf) */ | 
					
						
							|  |  |  | 	u8 rx_seq;		/* Receive sequence number (expected) */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	struct brcmf_sdio_hdrinfo cur_read; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				/* info of current read frame */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bool rxskip;		/* Skip receive (awaiting NAK ACK) */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	bool rxpending;		/* Data frame pending in dongle */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	uint rxbound;		/* Rx frames to read before resched */ | 
					
						
							|  |  |  | 	uint txbound;		/* Tx frames to send before resched */ | 
					
						
							|  |  |  | 	uint txminmax; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct sk_buff *glomd;	/* Packet containing glomming descriptor */ | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 	struct sk_buff_head glom; /* Packet list for glommed superframe */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint glomerr;		/* Glom packet read errors */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u8 *rxbuf;		/* Buffer for receiving control packets */ | 
					
						
							|  |  |  | 	uint rxblen;		/* Allocated length of rxbuf */ | 
					
						
							|  |  |  | 	u8 *rxctl;		/* Aligned pointer into rxbuf */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	u8 *rxctl_orig;		/* pointer for freeing rxctl */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint rxlen;		/* Length of valid data in buffer */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spinlock_t rxctl_lock;	/* protection lock for ctrl frame resources */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	u8 sdpcm_ver;	/* Bus protocol reported by dongle */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bool intr;		/* Use interrupts */ | 
					
						
							|  |  |  | 	bool poll;		/* Use polling */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:11:59 +02:00
										 |  |  | 	atomic_t ipend;		/* Device interrupt is pending */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint spurious;		/* Count of spurious interrupts */ | 
					
						
							|  |  |  | 	uint pollrate;		/* Ticks between device polls */ | 
					
						
							|  |  |  | 	uint polltick;		/* Tick counter */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint console_interval; | 
					
						
							|  |  |  | 	struct brcmf_console console;	/* Console output polling support */ | 
					
						
							|  |  |  | 	uint console_addr;	/* Console address from shared struct */ | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	uint clkstate;		/* State of sd and backplane clock(s) */ | 
					
						
							|  |  |  | 	bool activity;		/* Activity flag for clock down */ | 
					
						
							|  |  |  | 	s32 idletime;		/* Control for activity timeout */ | 
					
						
							|  |  |  | 	s32 idlecount;	/* Activity timeout counter */ | 
					
						
							|  |  |  | 	s32 idleclock;	/* How to set bus driver when idle */ | 
					
						
							|  |  |  | 	bool rxflow_mode;	/* Rx flow control mode */ | 
					
						
							|  |  |  | 	bool rxflow;		/* Is rx flow control on */ | 
					
						
							|  |  |  | 	bool alp_only;		/* Don't use HT clock (ALP only) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	u8 *ctrl_frame_buf; | 
					
						
							|  |  |  | 	u32 ctrl_frame_len; | 
					
						
							|  |  |  | 	bool ctrl_frame_stat; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	spinlock_t txqlock; | 
					
						
							|  |  |  | 	wait_queue_head_t ctrl_wait; | 
					
						
							|  |  |  | 	wait_queue_head_t dcmd_resp_wait; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	struct timer_list timer; | 
					
						
							|  |  |  | 	struct completion watchdog_wait; | 
					
						
							|  |  |  | 	struct task_struct *watchdog_tsk; | 
					
						
							|  |  |  | 	bool wd_timer_valid; | 
					
						
							|  |  |  | 	uint save_ms; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 	struct workqueue_struct *brcmf_wq; | 
					
						
							|  |  |  | 	struct work_struct datawork; | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	atomic_t dpc_tskcnt; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:08 -08:00
										 |  |  | 	bool txoff;		/* Transmit flow-controlled */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	struct brcmf_sdio_count sdcnt; | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	bool sr_enabled; /* SaveRestore enabled */ | 
					
						
							|  |  |  | 	bool sleeping; /* SDIO bus sleeping */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	u8 tx_hdrlen;		/* sdio bus header length for tx packet */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	bool txglom;		/* host tx glomming enable flag */ | 
					
						
							|  |  |  | 	struct sk_buff *txglom_sgpad;	/* scatter-gather padding buffer */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	u16 head_align;		/* buffer pointer alignment */ | 
					
						
							|  |  |  | 	u16 sgentry_align;	/* scatter-gather buffer alignment */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* clkstate */ | 
					
						
							|  |  |  | #define CLK_NONE	0
 | 
					
						
							|  |  |  | #define CLK_SDONLY	1
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | #define CLK_PENDING	2
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #define CLK_AVAIL	3
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | static int qcount[NUMPRIO]; | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-12 10:55:55 +02:00
										 |  |  | #define DEFAULT_SDIO_DRIVE_STRENGTH	6	/* in milliamps */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | #define RETRYCHAN(chan) ((chan) == SDPCM_EVENT_CHANNEL)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Retry count for register access failures */ | 
					
						
							|  |  |  | static const uint retry_limit = 2; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Limit on rounding up frames */ | 
					
						
							|  |  |  | static const uint max_roundup = 512; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define ALIGNMENT  4
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | static int brcmf_sdio_txglomsz = BRCMF_DEFAULT_TXGLOM_SIZE; | 
					
						
							|  |  |  | module_param_named(txglomsz, brcmf_sdio_txglomsz, int, 0); | 
					
						
							|  |  |  | MODULE_PARM_DESC(txglomsz, "maximum tx packet chain size [SDIO]"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | enum brcmf_sdio_frmtype { | 
					
						
							|  |  |  | 	BRCMF_SDIO_FT_NORMAL, | 
					
						
							|  |  |  | 	BRCMF_SDIO_FT_SUPER, | 
					
						
							|  |  |  | 	BRCMF_SDIO_FT_SUB, | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | #define BCM43143_FIRMWARE_NAME		"brcm/brcmfmac43143-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM43143_NVRAM_NAME		"brcm/brcmfmac43143-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM43241B0_FIRMWARE_NAME	"brcm/brcmfmac43241b0-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM43241B0_NVRAM_NAME		"brcm/brcmfmac43241b0-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM43241B4_FIRMWARE_NAME	"brcm/brcmfmac43241b4-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM43241B4_NVRAM_NAME		"brcm/brcmfmac43241b4-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM4329_FIRMWARE_NAME		"brcm/brcmfmac4329-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM4329_NVRAM_NAME		"brcm/brcmfmac4329-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM4330_FIRMWARE_NAME		"brcm/brcmfmac4330-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM4330_NVRAM_NAME		"brcm/brcmfmac4330-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM4334_FIRMWARE_NAME		"brcm/brcmfmac4334-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM4334_NVRAM_NAME		"brcm/brcmfmac4334-sdio.txt"
 | 
					
						
							|  |  |  | #define BCM4335_FIRMWARE_NAME		"brcm/brcmfmac4335-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM4335_NVRAM_NAME		"brcm/brcmfmac4335-sdio.txt"
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:09 +01:00
										 |  |  | #define BCM4339_FIRMWARE_NAME		"brcm/brcmfmac4339-sdio.bin"
 | 
					
						
							|  |  |  | #define BCM4339_NVRAM_NAME		"brcm/brcmfmac4339-sdio.txt"
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43143_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43143_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43241B0_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43241B0_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43241B4_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM43241B4_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4329_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4329_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4330_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4330_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4334_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4334_NVRAM_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4335_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4335_NVRAM_NAME); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:09 +01:00
										 |  |  | MODULE_FIRMWARE(BCM4339_FIRMWARE_NAME); | 
					
						
							|  |  |  | MODULE_FIRMWARE(BCM4339_NVRAM_NAME); | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | struct brcmf_firmware_names { | 
					
						
							|  |  |  | 	u32 chipid; | 
					
						
							|  |  |  | 	u32 revmsk; | 
					
						
							|  |  |  | 	const char *bin; | 
					
						
							|  |  |  | 	const char *nv; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | enum brcmf_firmware_type { | 
					
						
							|  |  |  | 	BRCMF_FIRMWARE_BIN, | 
					
						
							|  |  |  | 	BRCMF_FIRMWARE_NVRAM | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define BRCMF_FIRMWARE_NVRAM(name) \
 | 
					
						
							|  |  |  | 	name ## _FIRMWARE_NAME, name ## _NVRAM_NAME | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct brcmf_firmware_names brcmf_fwname_data[] = { | 
					
						
							|  |  |  | 	{ BCM43143_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM43143) }, | 
					
						
							|  |  |  | 	{ BCM43241_CHIP_ID, 0x0000001F, BRCMF_FIRMWARE_NVRAM(BCM43241B0) }, | 
					
						
							|  |  |  | 	{ BCM43241_CHIP_ID, 0xFFFFFFE0, BRCMF_FIRMWARE_NVRAM(BCM43241B4) }, | 
					
						
							|  |  |  | 	{ BCM4329_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4329) }, | 
					
						
							|  |  |  | 	{ BCM4330_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4330) }, | 
					
						
							|  |  |  | 	{ BCM4334_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4334) }, | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:09 +01:00
										 |  |  | 	{ BCM4335_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4335) }, | 
					
						
							|  |  |  | 	{ BCM4339_CHIP_ID, 0xFFFFFFFF, BRCMF_FIRMWARE_NVRAM(BCM4339) } | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct firmware *brcmf_sdbrcm_get_fw(struct brcmf_sdio *bus, | 
					
						
							|  |  |  | 						  enum brcmf_firmware_type type) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	const struct firmware *fw; | 
					
						
							|  |  |  | 	const char *name; | 
					
						
							|  |  |  | 	int err, i; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (i = 0; i < ARRAY_SIZE(brcmf_fwname_data); i++) { | 
					
						
							|  |  |  | 		if (brcmf_fwname_data[i].chipid == bus->ci->chip && | 
					
						
							|  |  |  | 		    brcmf_fwname_data[i].revmsk & BIT(bus->ci->chiprev)) { | 
					
						
							|  |  |  | 			switch (type) { | 
					
						
							|  |  |  | 			case BRCMF_FIRMWARE_BIN: | 
					
						
							|  |  |  | 				name = brcmf_fwname_data[i].bin; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case BRCMF_FIRMWARE_NVRAM: | 
					
						
							|  |  |  | 				name = brcmf_fwname_data[i].nv; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			default: | 
					
						
							|  |  |  | 				brcmf_err("invalid firmware type (%d)\n", type); | 
					
						
							|  |  |  | 				return NULL; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			goto found; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	brcmf_err("Unknown chipid %d [%d]\n", | 
					
						
							|  |  |  | 		  bus->ci->chip, bus->ci->chiprev); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | found: | 
					
						
							|  |  |  | 	err = request_firmware(&fw, name, &bus->sdiodev->func[2]->dev); | 
					
						
							|  |  |  | 	if ((err) || (!fw)) { | 
					
						
							|  |  |  | 		brcmf_err("fail to request firmware %s (%d)\n", name, err); | 
					
						
							|  |  |  | 		return NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return fw; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | static void pkt_align(struct sk_buff *p, int len, int align) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	uint datalign; | 
					
						
							|  |  |  | 	datalign = (unsigned long)(p->data); | 
					
						
							|  |  |  | 	datalign = roundup(datalign, (align)) - datalign; | 
					
						
							|  |  |  | 	if (datalign) | 
					
						
							|  |  |  | 		skb_pull(p, datalign); | 
					
						
							|  |  |  | 	__skb_trim(p, len); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* To check if there's window offered */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static bool data_ok(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	return (u8)(bus->tx_max - bus->tx_seq) != 0 && | 
					
						
							|  |  |  | 	       ((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * Reads a register in the SDIO hardware block. This block occupies a series of | 
					
						
							|  |  |  |  * adresses on the 32 bit backplane bus. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | static int | 
					
						
							|  |  |  | r_sdreg32(struct brcmf_sdio *bus, u32 *regvar, u32 offset) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:42 +01:00
										 |  |  | 	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:34 -07:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	*regvar = brcmf_sdio_regrl(bus->sdiodev, | 
					
						
							|  |  |  | 				   bus->ci->c_inf[idx].base + offset, &ret); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | static int | 
					
						
							|  |  |  | w_sdreg32(struct brcmf_sdio *bus, u32 regval, u32 reg_offset) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:42 +01:00
										 |  |  | 	u8 idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:35 -07:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdio_regwl(bus->sdiodev, | 
					
						
							|  |  |  | 			 bus->ci->c_inf[idx].base + reg_offset, | 
					
						
							|  |  |  | 			 regval, &ret); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | static int | 
					
						
							|  |  |  | brcmf_sdbrcm_kso_control(struct brcmf_sdio *bus, bool on) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 wr_val = 0, rd_val, cmp_val, bmask; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 	int try_cnt = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	wr_val = (on << SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); | 
					
						
							|  |  |  | 	/* 1st KSO write goes to AOS wake up core if device is asleep  */ | 
					
						
							|  |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, | 
					
						
							|  |  |  | 			 wr_val, &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("SDIO_AOS KSO write error: %d\n", err); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (on) { | 
					
						
							|  |  |  | 		/* device WAKEUP through KSO:
 | 
					
						
							|  |  |  | 		 * write bit 0 & read back until | 
					
						
							|  |  |  | 		 * both bits 0 (kso bit) & 1 (dev on status) are set | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		cmp_val = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK | | 
					
						
							|  |  |  | 			  SBSDIO_FUNC1_SLEEPCSR_DEVON_MASK; | 
					
						
							|  |  |  | 		bmask = cmp_val; | 
					
						
							|  |  |  | 		usleep_range(2000, 3000); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Put device to sleep, turn off KSO */ | 
					
						
							|  |  |  | 		cmp_val = 0; | 
					
						
							|  |  |  | 		/* only check for bit0, bit1(dev on status) may not
 | 
					
						
							|  |  |  | 		 * get cleared right away | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		bmask = SBSDIO_FUNC1_SLEEPCSR_KSO_MASK; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	do { | 
					
						
							|  |  |  | 		/* reliable KSO bit set/clr:
 | 
					
						
							|  |  |  | 		 * the sdiod sleep write access is synced to PMU 32khz clk | 
					
						
							|  |  |  | 		 * just one write attempt may fail, | 
					
						
							|  |  |  | 		 * read it back until it matches written value | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		rd_val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, | 
					
						
							|  |  |  | 					  &err); | 
					
						
							|  |  |  | 		if (((rd_val & bmask) == cmp_val) && !err) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		brcmf_dbg(SDIO, "KSO wr/rd retry:%d (max: %d) ERR:%x\n", | 
					
						
							|  |  |  | 			  try_cnt, MAX_KSO_ATTEMPTS, err); | 
					
						
							|  |  |  | 		udelay(KSO_WAIT_US); | 
					
						
							|  |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, | 
					
						
							|  |  |  | 				 wr_val, &err); | 
					
						
							|  |  |  | 	} while (try_cnt++ < MAX_KSO_ATTEMPTS); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #define PKT_AVAILABLE()		(intstatus & I_HMB_FRAME_IND)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | #define HOSTINTMASK		(I_HMB_SW_MASK | I_CHIPACTIVE)
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Turn backplane clock on or off */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_htclk(struct brcmf_sdio *bus, bool on, bool pendok) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	u8 clkctl, clkreq, devctl; | 
					
						
							|  |  |  | 	unsigned long timeout; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "Enter\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	clkctl = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	if (bus->sr_enabled) { | 
					
						
							|  |  |  | 		bus->clkstate = (on ? CLK_AVAIL : CLK_SDONLY); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (on) { | 
					
						
							|  |  |  | 		/* Request HT Avail */ | 
					
						
							|  |  |  | 		clkreq = | 
					
						
							|  |  |  | 		    bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 				 clkreq, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("HT Avail request error: %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			return -EBADE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Check current status */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		clkctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					  SBSDIO_FUNC1_CHIPCLKCSR, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("HT Avail read error: %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			return -EBADE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Go to pending and await interrupt if appropriate */ | 
					
						
							|  |  |  | 		if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) { | 
					
						
							|  |  |  | 			/* Allow only clock-available interrupt */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			devctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						  SBSDIO_DEVICE_CTL, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("Devctl error setting CA: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  err); | 
					
						
							|  |  |  | 				return -EBADE; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			devctl |= SBSDIO_DEVCTL_CA_INT_ONLY; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, | 
					
						
							|  |  |  | 					 devctl, &err); | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 			brcmf_dbg(SDIO, "CLKCTL: set PENDING\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			bus->clkstate = CLK_PENDING; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			return 0; | 
					
						
							|  |  |  | 		} else if (bus->clkstate == CLK_PENDING) { | 
					
						
							|  |  |  | 			/* Cancel CA-only interrupt filter */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			devctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 						  SBSDIO_DEVICE_CTL, &err); | 
					
						
							|  |  |  | 			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, | 
					
						
							|  |  |  | 					 devctl, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Otherwise, wait here (polling) for HT Avail */ | 
					
						
							|  |  |  | 		timeout = jiffies + | 
					
						
							|  |  |  | 			  msecs_to_jiffies(PMU_MAX_TRANSITION_DLY/1000); | 
					
						
							|  |  |  | 		while (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			clkctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						  SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 						  &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (time_after(jiffies, timeout)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			else | 
					
						
							|  |  |  | 				usleep_range(5000, 10000); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("HT Avail request error: %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			return -EBADE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("HT Avail timeout (%d): clkctl 0x%02x\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  PMU_MAX_TRANSITION_DLY, clkctl); | 
					
						
							|  |  |  | 			return -EBADE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Mark clock available */ | 
					
						
							|  |  |  | 		bus->clkstate = CLK_AVAIL; | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "CLKCTL: turned ON\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #if defined(DEBUG)
 | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 		if (!bus->alp_only) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (SBSDIO_ALPONLY(clkctl)) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("HT Clock should be on\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* defined (DEBUG) */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		bus->activity = true; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		clkreq = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (bus->clkstate == CLK_PENDING) { | 
					
						
							|  |  |  | 			/* Cancel CA-only interrupt filter */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			devctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						  SBSDIO_DEVICE_CTL, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, | 
					
						
							|  |  |  | 					 devctl, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bus->clkstate = CLK_SDONLY; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 				 clkreq, &err); | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "CLKCTL: turned OFF\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("Failed access turning clock off: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  err); | 
					
						
							|  |  |  | 			return -EBADE; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Change idle/active SD state */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_sdclk(struct brcmf_sdio *bus, bool on) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "Enter\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (on) | 
					
						
							|  |  |  | 		bus->clkstate = CLK_SDONLY; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		bus->clkstate = CLK_NONE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Transition SD and backplane clock readiness */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_clkctl(struct brcmf_sdio *bus, uint target, bool pendok) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint oldstate = bus->clkstate; | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "Enter\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Early exit if we're already there */ | 
					
						
							|  |  |  | 	if (bus->clkstate == target) { | 
					
						
							|  |  |  | 		if (target == CLK_AVAIL) { | 
					
						
							|  |  |  | 			brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); | 
					
						
							|  |  |  | 			bus->activity = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch (target) { | 
					
						
							|  |  |  | 	case CLK_AVAIL: | 
					
						
							|  |  |  | 		/* Make sure SD clock is available */ | 
					
						
							|  |  |  | 		if (bus->clkstate == CLK_NONE) | 
					
						
							|  |  |  | 			brcmf_sdbrcm_sdclk(bus, true); | 
					
						
							|  |  |  | 		/* Now request HT Avail on the backplane */ | 
					
						
							|  |  |  | 		brcmf_sdbrcm_htclk(bus, true, pendok); | 
					
						
							|  |  |  | 		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); | 
					
						
							|  |  |  | 		bus->activity = true; | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CLK_SDONLY: | 
					
						
							|  |  |  | 		/* Remove HT request, or bring up SD clock */ | 
					
						
							|  |  |  | 		if (bus->clkstate == CLK_NONE) | 
					
						
							|  |  |  | 			brcmf_sdbrcm_sdclk(bus, true); | 
					
						
							|  |  |  | 		else if (bus->clkstate == CLK_AVAIL) | 
					
						
							|  |  |  | 			brcmf_sdbrcm_htclk(bus, false, false); | 
					
						
							|  |  |  | 		else | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("request for %d -> %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  bus->clkstate, target); | 
					
						
							|  |  |  | 		brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	case CLK_NONE: | 
					
						
							|  |  |  | 		/* Make sure to remove HT request */ | 
					
						
							|  |  |  | 		if (bus->clkstate == CLK_AVAIL) | 
					
						
							|  |  |  | 			brcmf_sdbrcm_htclk(bus, false, false); | 
					
						
							|  |  |  | 		/* Now remove the SD clock */ | 
					
						
							|  |  |  | 		brcmf_sdbrcm_sdclk(bus, false); | 
					
						
							|  |  |  | 		brcmf_sdbrcm_wd_timer(bus, 0); | 
					
						
							|  |  |  | 		break; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "%d -> %d\n", oldstate, bus->clkstate); | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | static int | 
					
						
							|  |  |  | brcmf_sdbrcm_bus_sleep(struct brcmf_sdio *bus, bool sleep, bool pendok) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 	brcmf_dbg(SDIO, "request %s currently %s\n", | 
					
						
							|  |  |  | 		  (sleep ? "SLEEP" : "WAKE"), | 
					
						
							|  |  |  | 		  (bus->sleeping ? "SLEEP" : "WAKE")); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If SR is enabled control bus state with KSO */ | 
					
						
							|  |  |  | 	if (bus->sr_enabled) { | 
					
						
							|  |  |  | 		/* Done if we're already in the requested state */ | 
					
						
							|  |  |  | 		if (sleep == bus->sleeping) | 
					
						
							|  |  |  | 			goto end; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Going to sleep */ | 
					
						
							|  |  |  | 		if (sleep) { | 
					
						
							|  |  |  | 			/* Don't sleep if something is pending */ | 
					
						
							|  |  |  | 			if (atomic_read(&bus->intstatus) || | 
					
						
							|  |  |  | 			    atomic_read(&bus->ipend) > 0 || | 
					
						
							|  |  |  | 			    (!atomic_read(&bus->fcstate) && | 
					
						
							|  |  |  | 			    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && | 
					
						
							|  |  |  | 			    data_ok(bus))) | 
					
						
							|  |  |  | 				 return -EBUSY; | 
					
						
							|  |  |  | 			err = brcmf_sdbrcm_kso_control(bus, false); | 
					
						
							|  |  |  | 			/* disable watchdog */ | 
					
						
							|  |  |  | 			if (!err) | 
					
						
							|  |  |  | 				brcmf_sdbrcm_wd_timer(bus, 0); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			bus->idlecount = 0; | 
					
						
							|  |  |  | 			err = brcmf_sdbrcm_kso_control(bus, true); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (!err) { | 
					
						
							|  |  |  | 			/* Change state */ | 
					
						
							|  |  |  | 			bus->sleeping = sleep; | 
					
						
							|  |  |  | 			brcmf_dbg(SDIO, "new state %s\n", | 
					
						
							|  |  |  | 				  (sleep ? "SLEEP" : "WAKE")); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			brcmf_err("error while changing bus sleep state %d\n", | 
					
						
							|  |  |  | 				  err); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | end: | 
					
						
							|  |  |  | 	/* control clocks */ | 
					
						
							|  |  |  | 	if (sleep) { | 
					
						
							|  |  |  | 		if (!bus->sr_enabled) | 
					
						
							|  |  |  | 			brcmf_sdbrcm_clkctl(bus, CLK_NONE, pendok); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, pendok); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return err; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static u32 brcmf_sdbrcm_hostmail(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	u32 intstatus = 0; | 
					
						
							|  |  |  | 	u32 hmb_data; | 
					
						
							|  |  |  | 	u8 fcbits; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "Enter\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Read mailbox data and ack that we did so */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 	ret = r_sdreg32(bus, &hmb_data, | 
					
						
							|  |  |  | 			offsetof(struct sdpcmd_regs, tohostmailboxdata)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 	if (ret == 0) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		w_sdreg32(bus, SMB_INT_ACK, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 			  offsetof(struct sdpcmd_regs, tosbmailbox)); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Dongle recomposed rx frames, accept them again */ | 
					
						
							|  |  |  | 	if (hmb_data & HMB_DATA_NAKHANDLED) { | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "Dongle reports NAK handled, expect rtx of %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			  bus->rx_seq); | 
					
						
							|  |  |  | 		if (!bus->rxskip) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("unexpected NAKHANDLED!\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		bus->rxskip = false; | 
					
						
							|  |  |  | 		intstatus |= I_HMB_FRAME_IND; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * DEVREADY does not occur with gSPI. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) { | 
					
						
							|  |  |  | 		bus->sdpcm_ver = | 
					
						
							|  |  |  | 		    (hmb_data & HMB_DATA_VERSION_MASK) >> | 
					
						
							|  |  |  | 		    HMB_DATA_VERSION_SHIFT; | 
					
						
							|  |  |  | 		if (bus->sdpcm_ver != SDPCM_PROT_VERSION) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("Version mismatch, dongle reports %d, " | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  "expecting %d\n", | 
					
						
							|  |  |  | 				  bus->sdpcm_ver, SDPCM_PROT_VERSION); | 
					
						
							|  |  |  | 		else | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 			brcmf_dbg(SDIO, "Dongle ready, protocol version %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  bus->sdpcm_ver); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Flow Control has been moved into the RX headers and this out of band | 
					
						
							|  |  |  | 	 * method isn't used any more. | 
					
						
							|  |  |  | 	 * remaining backward compatible with older dongles. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (hmb_data & HMB_DATA_FC) { | 
					
						
							|  |  |  | 		fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >> | 
					
						
							|  |  |  | 							HMB_DATA_FCDATA_SHIFT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (fcbits & ~bus->flowcontrol) | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.fc_xoff++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (bus->flowcontrol & ~fcbits) | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.fc_xon++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.fc_rcvd++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->flowcontrol = fcbits; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Shouldn't be any others */ | 
					
						
							|  |  |  | 	if (hmb_data & ~(HMB_DATA_DEVREADY | | 
					
						
							|  |  |  | 			 HMB_DATA_NAKHANDLED | | 
					
						
							|  |  |  | 			 HMB_DATA_FC | | 
					
						
							|  |  |  | 			 HMB_DATA_FWREADY | | 
					
						
							|  |  |  | 			 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Unknown mailbox data content: 0x%02x\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			  hmb_data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return intstatus; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_sdbrcm_rxfail(struct brcmf_sdio *bus, bool abort, bool rtx) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	uint retries = 0; | 
					
						
							|  |  |  | 	u16 lastrbc; | 
					
						
							|  |  |  | 	u8 hi, lo; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 	brcmf_err("%sterminate frame%s\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		  abort ? "abort command, " : "", | 
					
						
							|  |  |  | 		  rtx ? ", send NAK" : ""); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (abort) | 
					
						
							|  |  |  | 		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, | 
					
						
							|  |  |  | 			 SFC_RF_TERM, &err); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.f1regdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Wait until the packet has been flushed (device/FIFO stable) */ | 
					
						
							|  |  |  | 	for (lastrbc = retries = 0xffff; retries > 0; retries--) { | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		hi = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 				      SBSDIO_FUNC1_RFRAMEBCHI, &err); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		lo = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 				      SBSDIO_FUNC1_RFRAMEBCLO, &err); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if ((hi == 0) && (lo == 0)) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("count growing: last 0x%04x now 0x%04x\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  lastrbc, (hi << 8) + lo); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		lastrbc = (hi << 8) + lo; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!retries) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("count never zeroed: last 0x%04x\n", lastrbc); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "flush took %d iterations\n", 0xffff - retries); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (rtx) { | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rxrtx++; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 		err = w_sdreg32(bus, SMB_NAK, | 
					
						
							|  |  |  | 				offsetof(struct sdpcmd_regs, tosbmailbox)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f1regdata++; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 		if (err == 0) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			bus->rxskip = true; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear partial in any case */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	bus->cur_read.len = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If we can't reach the device, signal failure */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 	if (err) | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:29 +01:00
										 |  |  | /* return total length of buffer chain */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static uint brcmf_sdbrcm_glom_len(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:29 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sk_buff *p; | 
					
						
							|  |  |  | 	uint total; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	total = 0; | 
					
						
							|  |  |  | 	skb_queue_walk(&bus->glom, p) | 
					
						
							|  |  |  | 		total += p->len; | 
					
						
							|  |  |  | 	return total; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_sdbrcm_free_glom(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:31 +01:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sk_buff *cur, *next; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_queue_walk_safe(&bus->glom, cur, next) { | 
					
						
							|  |  |  | 		skb_unlink(cur, &bus->glom); | 
					
						
							|  |  |  | 		brcmu_pkt_buf_free_skb(cur); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * brcmfmac sdio bus specific header | 
					
						
							|  |  |  |  * This is the lowest layer header wrapped on the packets transmitted between | 
					
						
							|  |  |  |  * host and WiFi dongle which contains information needed for SDIO core and | 
					
						
							|  |  |  |  * firmware | 
					
						
							|  |  |  |  * | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  |  * It consists of 3 parts: hardware header, hardware extension header and | 
					
						
							|  |  |  |  * software header | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  |  * hardware header (frame tag) - 4 bytes | 
					
						
							|  |  |  |  * Byte 0~1: Frame length | 
					
						
							|  |  |  |  * Byte 2~3: Checksum, bit-wise inverse of frame length | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  |  * hardware extension header - 8 bytes | 
					
						
							|  |  |  |  * Tx glom mode only, N/A for Rx or normal Tx | 
					
						
							|  |  |  |  * Byte 0~1: Packet length excluding hw frame tag | 
					
						
							|  |  |  |  * Byte 2: Reserved | 
					
						
							|  |  |  |  * Byte 3: Frame flags, bit 0: last frame indication | 
					
						
							|  |  |  |  * Byte 4~5: Reserved | 
					
						
							|  |  |  |  * Byte 6~7: Tail padding length | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  |  * software header - 8 bytes | 
					
						
							|  |  |  |  * Byte 0: Rx/Tx sequence number | 
					
						
							|  |  |  |  * Byte 1: 4 MSB Channel number, 4 LSB arbitrary flag | 
					
						
							|  |  |  |  * Byte 2: Length of next data frame, reserved for Tx | 
					
						
							|  |  |  |  * Byte 3: Data offset | 
					
						
							|  |  |  |  * Byte 4: Flow control bits, reserved for Tx | 
					
						
							|  |  |  |  * Byte 5: Maximum Sequence number allowed by firmware for Tx, N/A for Tx packet | 
					
						
							|  |  |  |  * Byte 6~7: Reserved | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | #define SDPCM_HWHDR_LEN			4
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | #define SDPCM_HWEXT_LEN			8
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | #define SDPCM_SWHDR_LEN			8
 | 
					
						
							|  |  |  | #define SDPCM_HDRLEN			(SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN)
 | 
					
						
							|  |  |  | /* software header */ | 
					
						
							|  |  |  | #define SDPCM_SEQ_MASK			0x000000ff
 | 
					
						
							|  |  |  | #define SDPCM_SEQ_WRAP			256
 | 
					
						
							|  |  |  | #define SDPCM_CHANNEL_MASK		0x00000f00
 | 
					
						
							|  |  |  | #define SDPCM_CHANNEL_SHIFT		8
 | 
					
						
							|  |  |  | #define SDPCM_CONTROL_CHANNEL		0	/* Control */
 | 
					
						
							|  |  |  | #define SDPCM_EVENT_CHANNEL		1	/* Asyc Event Indication */
 | 
					
						
							|  |  |  | #define SDPCM_DATA_CHANNEL		2	/* Data Xmit/Recv */
 | 
					
						
							|  |  |  | #define SDPCM_GLOM_CHANNEL		3	/* Coalesced packets */
 | 
					
						
							|  |  |  | #define SDPCM_TEST_CHANNEL		15	/* Test/debug packets */
 | 
					
						
							|  |  |  | #define SDPCM_GLOMDESC(p)		(((u8 *)p)[1] & 0x80)
 | 
					
						
							|  |  |  | #define SDPCM_NEXTLEN_MASK		0x00ff0000
 | 
					
						
							|  |  |  | #define SDPCM_NEXTLEN_SHIFT		16
 | 
					
						
							|  |  |  | #define SDPCM_DOFFSET_MASK		0xff000000
 | 
					
						
							|  |  |  | #define SDPCM_DOFFSET_SHIFT		24
 | 
					
						
							|  |  |  | #define SDPCM_FCMASK_MASK		0x000000ff
 | 
					
						
							|  |  |  | #define SDPCM_WINDOW_MASK		0x0000ff00
 | 
					
						
							|  |  |  | #define SDPCM_WINDOW_SHIFT		8
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static inline u8 brcmf_sdio_getdatoffset(u8 *swheader) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 hdrvalue; | 
					
						
							|  |  |  | 	hdrvalue = *(u32 *)swheader; | 
					
						
							|  |  |  | 	return (u8)((hdrvalue & SDPCM_DOFFSET_MASK) >> SDPCM_DOFFSET_SHIFT); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdio_hdparse(struct brcmf_sdio *bus, u8 *header, | 
					
						
							|  |  |  | 			      struct brcmf_sdio_hdrinfo *rd, | 
					
						
							|  |  |  | 			      enum brcmf_sdio_frmtype type) | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	u16 len, checksum; | 
					
						
							|  |  |  | 	u8 rx_seq, fc, tx_seq_max; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	u32 swheader; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:06 +01:00
										 |  |  | 	trace_brcmf_sdpcm_hdr(SDPCM_RX, header); | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:47 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	/* hw header */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	len = get_unaligned_le16(header); | 
					
						
							|  |  |  | 	checksum = get_unaligned_le16(header + sizeof(u16)); | 
					
						
							|  |  |  | 	/* All zero means no more to read */ | 
					
						
							|  |  |  | 	if (!(len | checksum)) { | 
					
						
							|  |  |  | 		bus->rxpending = false; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -ENODATA; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if ((u16)(~(len ^ checksum))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("HW header checksum error\n"); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		bus->sdcnt.rx_badhdr++; | 
					
						
							|  |  |  | 		brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EIO; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (len < SDPCM_HDRLEN) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("HW header length error\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EPROTO; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	if (type == BRCMF_SDIO_FT_SUPER && | 
					
						
							|  |  |  | 	    (roundup(len, bus->blocksize) != rd->len)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("HW superframe header length error\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EPROTO; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (type == BRCMF_SDIO_FT_SUB && len > rd->len) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("HW subframe header length error\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EPROTO; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	rd->len = len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	/* software header */ | 
					
						
							|  |  |  | 	header += SDPCM_HWHDR_LEN; | 
					
						
							|  |  |  | 	swheader = le32_to_cpu(*(__le32 *)header); | 
					
						
							|  |  |  | 	if (type == BRCMF_SDIO_FT_SUPER && SDPCM_GLOMDESC(header)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Glom descriptor found in superframe head\n"); | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	rx_seq = (u8)(swheader & SDPCM_SEQ_MASK); | 
					
						
							|  |  |  | 	rd->channel = (swheader & SDPCM_CHANNEL_MASK) >> SDPCM_CHANNEL_SHIFT; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	if (len > MAX_RX_DATASZ && rd->channel != SDPCM_CONTROL_CHANNEL && | 
					
						
							|  |  |  | 	    type != BRCMF_SDIO_FT_SUPER) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("HW header length too long\n"); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		bus->sdcnt.rx_toolong++; | 
					
						
							|  |  |  | 		brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							|  |  |  | 		rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EPROTO; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	if (type == BRCMF_SDIO_FT_SUPER && rd->channel != SDPCM_GLOM_CHANNEL) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Wrong channel for superframe\n"); | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (type == BRCMF_SDIO_FT_SUB && rd->channel != SDPCM_DATA_CHANNEL && | 
					
						
							|  |  |  | 	    rd->channel != SDPCM_EVENT_CHANNEL) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Wrong channel for subframe\n"); | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -EINVAL; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	rd->dat_offset = brcmf_sdio_getdatoffset(header); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	if (rd->dat_offset < SDPCM_HDRLEN || rd->dat_offset > rd->len) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("seq %d: bad data offset\n", rx_seq); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		bus->sdcnt.rx_badhdr++; | 
					
						
							|  |  |  | 		brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							|  |  |  | 		rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return -ENXIO; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (rd->seq_num != rx_seq) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("seq %d: sequence number error, expect %d\n", | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			  rx_seq, rd->seq_num); | 
					
						
							|  |  |  | 		bus->sdcnt.rx_badseq++; | 
					
						
							|  |  |  | 		rd->seq_num = rx_seq; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	/* no need to check the reset for subframe */ | 
					
						
							|  |  |  | 	if (type == BRCMF_SDIO_FT_SUB) | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	rd->len_nxtfrm = (swheader & SDPCM_NEXTLEN_MASK) >> SDPCM_NEXTLEN_SHIFT; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	if (rd->len_nxtfrm << 4 > MAX_RX_DATASZ) { | 
					
						
							|  |  |  | 		/* only warm for NON glom packet */ | 
					
						
							|  |  |  | 		if (rd->channel != SDPCM_GLOM_CHANNEL) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("seq %d: next length error\n", rx_seq); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		rd->len_nxtfrm = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	swheader = le32_to_cpu(*(__le32 *)(header + 4)); | 
					
						
							|  |  |  | 	fc = swheader & SDPCM_FCMASK_MASK; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	if (bus->flowcontrol != fc) { | 
					
						
							|  |  |  | 		if (~bus->flowcontrol & fc) | 
					
						
							|  |  |  | 			bus->sdcnt.fc_xoff++; | 
					
						
							|  |  |  | 		if (bus->flowcontrol & ~fc) | 
					
						
							|  |  |  | 			bus->sdcnt.fc_xon++; | 
					
						
							|  |  |  | 		bus->sdcnt.fc_rcvd++; | 
					
						
							|  |  |  | 		bus->flowcontrol = fc; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	tx_seq_max = (swheader & SDPCM_WINDOW_MASK) >> SDPCM_WINDOW_SHIFT; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	if ((u8)(tx_seq_max - bus->tx_seq) > 0x40) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("seq %d: max tx seq number error\n", rx_seq); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		tx_seq_max = bus->tx_seq + 2; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bus->tx_max = tx_seq_max; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:26 -08:00
										 |  |  | 	return 0; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | static inline void brcmf_sdio_update_hwhdr(u8 *header, u16 frm_length) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	*(__le16 *)header = cpu_to_le16(frm_length); | 
					
						
							|  |  |  | 	*(((__le16 *)header) + 1) = cpu_to_le16(~frm_length); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void brcmf_sdio_hdpack(struct brcmf_sdio *bus, u8 *header, | 
					
						
							|  |  |  | 			      struct brcmf_sdio_hdrinfo *hd_info) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u32 hdrval; | 
					
						
							|  |  |  | 	u8 hdr_offset; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdio_update_hwhdr(header, hd_info->len); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	hdr_offset = SDPCM_HWHDR_LEN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (bus->txglom) { | 
					
						
							|  |  |  | 		hdrval = (hd_info->len - hdr_offset) | (hd_info->lastfrm << 24); | 
					
						
							|  |  |  | 		*((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); | 
					
						
							|  |  |  | 		hdrval = (u16)hd_info->tail_pad << 16; | 
					
						
							|  |  |  | 		*(((__le32 *)(header + hdr_offset)) + 1) = cpu_to_le32(hdrval); | 
					
						
							|  |  |  | 		hdr_offset += SDPCM_HWEXT_LEN; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	hdrval = hd_info->seq_num; | 
					
						
							|  |  |  | 	hdrval |= (hd_info->channel << SDPCM_CHANNEL_SHIFT) & | 
					
						
							|  |  |  | 		  SDPCM_CHANNEL_MASK; | 
					
						
							|  |  |  | 	hdrval |= (hd_info->dat_offset << SDPCM_DOFFSET_SHIFT) & | 
					
						
							|  |  |  | 		  SDPCM_DOFFSET_MASK; | 
					
						
							|  |  |  | 	*((__le32 *)(header + hdr_offset)) = cpu_to_le32(hdrval); | 
					
						
							|  |  |  | 	*(((__le32 *)(header + hdr_offset)) + 1) = 0; | 
					
						
							|  |  |  | 	trace_brcmf_sdpcm_hdr(SDPCM_TX + !!(bus->txglom), header); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static u8 brcmf_sdbrcm_rxglom(struct brcmf_sdio *bus, u8 rxseq) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	u16 dlen, totlen; | 
					
						
							|  |  |  | 	u8 *dptr, num = 0; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	u16 sublen; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 	struct sk_buff *pfirst, *pnext; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	int errcode; | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 	u8 doff, sfdoff; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	struct brcmf_sdio_hdrinfo rd_new; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If packets, issue read(s) and send up packet chain */ | 
					
						
							|  |  |  | 	/* Return sequence numbers consumed? */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "start: glomd %p glom %p\n", | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 		  bus->glomd, skb_peek(&bus->glom)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If there's a descriptor, generate the packet chain */ | 
					
						
							|  |  |  | 	if (bus->glomd) { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 		pfirst = pnext = NULL; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		dlen = (u16) (bus->glomd->len); | 
					
						
							|  |  |  | 		dptr = bus->glomd->data; | 
					
						
							|  |  |  | 		if (!dlen || (dlen & 1)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("bad glomd len(%d), ignore descriptor\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  dlen); | 
					
						
							|  |  |  | 			dlen = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for (totlen = num = 0; dlen; num++) { | 
					
						
							|  |  |  | 			/* Get (and move past) next length */ | 
					
						
							|  |  |  | 			sublen = get_unaligned_le16(dptr); | 
					
						
							|  |  |  | 			dlen -= sizeof(u16); | 
					
						
							|  |  |  | 			dptr += sizeof(u16); | 
					
						
							|  |  |  | 			if ((sublen < SDPCM_HDRLEN) || | 
					
						
							|  |  |  | 			    ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("descriptor len %d bad: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  num, sublen); | 
					
						
							|  |  |  | 				pnext = NULL; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 			if (sublen % bus->sgentry_align) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("sublen %d not multiple of %d\n", | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 					  sublen, bus->sgentry_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			totlen += sublen; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* For last frame, adjust read len so total
 | 
					
						
							|  |  |  | 				 is a block multiple */ | 
					
						
							|  |  |  | 			if (!dlen) { | 
					
						
							|  |  |  | 				sublen += | 
					
						
							|  |  |  | 				    (roundup(totlen, bus->blocksize) - totlen); | 
					
						
							|  |  |  | 				totlen = roundup(totlen, bus->blocksize); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Allocate/chain packet for next subframe */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 			pnext = brcmu_pkt_buf_get_skb(sublen + bus->sgentry_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (pnext == NULL) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("bcm_pkt_buf_get_skb failed, num %d len %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  num, sublen); | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 			skb_queue_tail(&bus->glom, pnext); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			/* Adhere to start alignment requirements */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 			pkt_align(pnext, sublen, bus->sgentry_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* If all allocations succeeded, save packet chain
 | 
					
						
							|  |  |  | 			 in bus structure */ | 
					
						
							|  |  |  | 		if (pnext) { | 
					
						
							|  |  |  | 			brcmf_dbg(GLOM, "allocated %d-byte packet chain for %d subframes\n", | 
					
						
							|  |  |  | 				  totlen, num); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			if (BRCMF_GLOM_ON() && bus->cur_read.len && | 
					
						
							|  |  |  | 			    totlen != bus->cur_read.len) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				brcmf_dbg(GLOM, "glomdesc mismatch: nextlen %d glomdesc %d rxseq %d\n", | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 					  bus->cur_read.len, totlen, rxseq); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			pfirst = pnext = NULL; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:31 +01:00
										 |  |  | 			brcmf_sdbrcm_free_glom(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			num = 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Done with descriptor packet */ | 
					
						
							|  |  |  | 		brcmu_pkt_buf_free_skb(bus->glomd); | 
					
						
							|  |  |  | 		bus->glomd = NULL; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		bus->cur_read.len = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Ok -- either we just generated a packet chain,
 | 
					
						
							|  |  |  | 		 or had one from before */ | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 	if (!skb_queue_empty(&bus->glom)) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (BRCMF_GLOM_ON()) { | 
					
						
							|  |  |  | 			brcmf_dbg(GLOM, "try superframe read, packet chain:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 			skb_queue_walk(&bus->glom, pnext) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				brcmf_dbg(GLOM, "    %p: %p len 0x%04x (%d)\n", | 
					
						
							|  |  |  | 					  pnext, (u8 *) (pnext->data), | 
					
						
							|  |  |  | 					  pnext->len, pnext->len); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 		pfirst = skb_peek(&bus->glom); | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:29 +01:00
										 |  |  | 		dlen = (u16) brcmf_sdbrcm_glom_len(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Do an SDIO read for the superframe.  Configurable iovar to
 | 
					
						
							|  |  |  | 		 * read directly into the chained packet, or allocate a large | 
					
						
							|  |  |  | 		 * packet and and copy into the chain. | 
					
						
							|  |  |  | 		 */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:29 +02:00
										 |  |  | 		errcode = brcmf_sdcard_recv_chain(bus->sdiodev, | 
					
						
							|  |  |  | 				bus->sdiodev->sbwad, | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:57 +02:00
										 |  |  | 				SDIO_FUNC_2, F2SYNC, &bus->glom, dlen); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f2rxdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* On failure, kill the superframe, allow a couple retries */ | 
					
						
							|  |  |  | 		if (errcode < 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("glom read of %d bytes failed: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  dlen, errcode); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (bus->glomerr++ < 3) { | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, true); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				bus->glomerr = 0; | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, false); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 				bus->sdcnt.rxglomfail++; | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:31 +01:00
										 |  |  | 				brcmf_sdbrcm_free_glom(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), | 
					
						
							|  |  |  | 				   pfirst->data, min_t(int, pfirst->len, 48), | 
					
						
							|  |  |  | 				   "SUPERFRAME:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		rd_new.seq_num = rxseq; | 
					
						
							|  |  |  | 		rd_new.len = dlen; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 		errcode = brcmf_sdio_hdparse(bus, pfirst->data, &rd_new, | 
					
						
							|  |  |  | 					     BRCMF_SDIO_FT_SUPER); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		bus->cur_read.len = rd_new.len_nxtfrm << 4; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Remove superframe header, remember offset */ | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 		skb_pull(pfirst, rd_new.dat_offset); | 
					
						
							|  |  |  | 		sfdoff = rd_new.dat_offset; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 		num = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Validate all the subframe headers */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 		skb_queue_walk(&bus->glom, pnext) { | 
					
						
							|  |  |  | 			/* leave when invalid subframe is found */ | 
					
						
							|  |  |  | 			if (errcode) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 			rd_new.len = pnext->len; | 
					
						
							|  |  |  | 			rd_new.seq_num = rxseq++; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			errcode = brcmf_sdio_hdparse(bus, pnext->data, &rd_new, | 
					
						
							|  |  |  | 						     BRCMF_SDIO_FT_SUB); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 			brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 					   pnext->data, 32, "subframe:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 			num++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (errcode) { | 
					
						
							|  |  |  | 			/* Terminate frame on error, request
 | 
					
						
							|  |  |  | 				 a couple retries */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (bus->glomerr++ < 3) { | 
					
						
							|  |  |  | 				/* Restore superframe header space */ | 
					
						
							|  |  |  | 				skb_push(pfirst, sfdoff); | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, true); | 
					
						
							|  |  |  | 			} else { | 
					
						
							|  |  |  | 				bus->glomerr = 0; | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, false); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 				bus->sdcnt.rxglomfail++; | 
					
						
							| 
									
										
										
										
											2011-11-10 20:30:31 +01:00
										 |  |  | 				brcmf_sdbrcm_free_glom(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			bus->cur_read.len = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			return 0; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Basic SD framing looks ok - process each packet (header) */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 		skb_queue_walk_safe(&bus->glom, pfirst, pnext) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			dptr = (u8 *) (pfirst->data); | 
					
						
							|  |  |  | 			sublen = get_unaligned_le16(dptr); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			doff = brcmf_sdio_getdatoffset(&dptr[SDPCM_HWHDR_LEN]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), | 
					
						
							| 
									
										
										
										
											2012-10-22 10:36:24 -07:00
										 |  |  | 					   dptr, pfirst->len, | 
					
						
							|  |  |  | 					   "Rx Subframe Data:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			__skb_trim(pfirst, sublen); | 
					
						
							|  |  |  | 			skb_pull(pfirst, doff); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (pfirst->len == 0) { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:36 -08:00
										 |  |  | 				skb_unlink(pfirst, &bus->glom); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				brcmu_pkt_buf_free_skb(pfirst); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 			brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), | 
					
						
							|  |  |  | 					   pfirst->data, | 
					
						
							|  |  |  | 					   min_t(int, pfirst->len, 32), | 
					
						
							|  |  |  | 					   "subframe %d to stack, %p (%p/%d) nxt/lnk %p/%p\n", | 
					
						
							|  |  |  | 					   bus->glom.qlen, pfirst, pfirst->data, | 
					
						
							|  |  |  | 					   pfirst->len, pfirst->next, | 
					
						
							|  |  |  | 					   pfirst->prev); | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:46 +02:00
										 |  |  | 			skb_unlink(pfirst, &bus->glom); | 
					
						
							|  |  |  | 			brcmf_rx_frame(bus->sdiodev->dev, pfirst); | 
					
						
							|  |  |  | 			bus->sdcnt.rxglompkts++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rxglomframes++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	return num; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_dcmd_resp_wait(struct brcmf_sdio *bus, uint *condition, | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					bool *pending) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	DECLARE_WAITQUEUE(wait, current); | 
					
						
							|  |  |  | 	int timeout = msecs_to_jiffies(DCMD_RESP_TIMEOUT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Wait until control frame is available */ | 
					
						
							|  |  |  | 	add_wait_queue(&bus->dcmd_resp_wait, &wait); | 
					
						
							|  |  |  | 	set_current_state(TASK_INTERRUPTIBLE); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (!(*condition) && (!signal_pending(current) && timeout)) | 
					
						
							|  |  |  | 		timeout = schedule_timeout(timeout); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (signal_pending(current)) | 
					
						
							|  |  |  | 		*pending = true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	set_current_state(TASK_RUNNING); | 
					
						
							|  |  |  | 	remove_wait_queue(&bus->dcmd_resp_wait, &wait); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return timeout; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_dcmd_resp_wake(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (waitqueue_active(&bus->dcmd_resp_wait)) | 
					
						
							|  |  |  | 		wake_up_interruptible(&bus->dcmd_resp_wait); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | brcmf_sdbrcm_read_control(struct brcmf_sdio *bus, u8 *hdr, uint len, uint doff) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	uint rdlen, pad; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	u8 *buf = NULL, *rbuf; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	int sdret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	if (bus->rxblen) | 
					
						
							|  |  |  | 		buf = vzalloc(bus->rxblen); | 
					
						
							| 
									
										
										
										
											2013-02-07 11:46:27 +00:00
										 |  |  | 	if (!buf) | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2013-02-07 11:46:27 +00:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	rbuf = bus->rxbuf; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	pad = ((unsigned long)rbuf % bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (pad) | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 		rbuf += (bus->head_align - pad); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Copy the already-read portion over */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	memcpy(buf, hdr, BRCMF_FIRSTREAD); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (len <= BRCMF_FIRSTREAD) | 
					
						
							|  |  |  | 		goto gotpkt; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Raise rdlen to next SDIO block to avoid tail command */ | 
					
						
							|  |  |  | 	rdlen = len - BRCMF_FIRSTREAD; | 
					
						
							|  |  |  | 	if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) { | 
					
						
							|  |  |  | 		pad = bus->blocksize - (rdlen % bus->blocksize); | 
					
						
							|  |  |  | 		if ((pad <= bus->roundup) && (pad < bus->blocksize) && | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 		    ((len + pad) < bus->sdiodev->bus_if->maxctl)) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			rdlen += pad; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	} else if (rdlen % bus->head_align) { | 
					
						
							|  |  |  | 		rdlen += bus->head_align - (rdlen % bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Drop if the read is too big or it exceeds our maximum */ | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 	if ((rdlen + BRCMF_FIRSTREAD) > bus->sdiodev->bus_if->maxctl) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("%d-byte control read exceeds %d-byte buffer\n", | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 			  rdlen, bus->sdiodev->bus_if->maxctl); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 	if ((len - doff) > bus->sdiodev->bus_if->maxctl) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("%d-byte ctl frame (%d-byte ctl data) exceeds %d-byte limit\n", | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 			  len, len - doff, bus->sdiodev->bus_if->maxctl); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rx_toolong++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	/* Read remain of frame body */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	sdret = brcmf_sdcard_recv_buf(bus->sdiodev, | 
					
						
							|  |  |  | 				bus->sdiodev->sbwad, | 
					
						
							|  |  |  | 				SDIO_FUNC_2, | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 				F2SYNC, rbuf, rdlen); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.f2rxdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Control frame failures need retransmission */ | 
					
						
							|  |  |  | 	if (sdret < 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("read %d control bytes failed: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			  rdlen, sdret); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rxc_errors++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_rxfail(bus, true, true); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	} else | 
					
						
							|  |  |  | 		memcpy(buf + BRCMF_FIRSTREAD, rbuf, rdlen); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | gotpkt: | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 	brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 			   buf, len, "RxCtrl:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Point to valid data and indicate its length */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_lock_bh(&bus->rxctl_lock); | 
					
						
							|  |  |  | 	if (bus->rxctl) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("last control frame is being processed.\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 		spin_unlock_bh(&bus->rxctl_lock); | 
					
						
							|  |  |  | 		vfree(buf); | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	bus->rxctl = buf + doff; | 
					
						
							|  |  |  | 	bus->rxctl_orig = buf; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bus->rxlen = len - doff; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_unlock_bh(&bus->rxctl_lock); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  | 	/* Awake any waiters */ | 
					
						
							|  |  |  | 	brcmf_sdbrcm_dcmd_resp_wake(bus); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Pad read to blocksize for efficiency */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_pad(struct brcmf_sdio *bus, u16 *pad, u16 *rdlen) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (bus->roundup && bus->blocksize && *rdlen > bus->blocksize) { | 
					
						
							|  |  |  | 		*pad = bus->blocksize - (*rdlen % bus->blocksize); | 
					
						
							|  |  |  | 		if (*pad <= bus->roundup && *pad < bus->blocksize && | 
					
						
							|  |  |  | 		    *rdlen + *pad + BRCMF_FIRSTREAD < MAX_RX_DATASZ) | 
					
						
							|  |  |  | 			*rdlen += *pad; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	} else if (*rdlen % bus->head_align) { | 
					
						
							|  |  |  | 		*rdlen += bus->head_align - (*rdlen % bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | static uint brcmf_sdio_readframes(struct brcmf_sdio *bus, uint maxframes) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sk_buff *pkt;		/* Packet for event or data frames */ | 
					
						
							|  |  |  | 	u16 pad;		/* Number of pad bytes to read */ | 
					
						
							|  |  |  | 	uint rxleft = 0;	/* Remaining number of frames allowed */ | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 	int ret;		/* Return code from calls */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint rxcount = 0;	/* Total frames read */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	struct brcmf_sdio_hdrinfo *rd = &bus->cur_read, rd_new; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	u8 head_read = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Not finished unless we encounter no more frames indication */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	bus->rxpending = true; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	for (rd->seq_num = bus->rx_seq, rxleft = maxframes; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:52 -08:00
										 |  |  | 	     !bus->rxskip && rxleft && | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	     bus->sdiodev->bus_if->state != BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 	     rd->seq_num++, rxleft--) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Handle glomming separately */ | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 		if (bus->glomd || !skb_queue_empty(&bus->glom)) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			u8 cnt; | 
					
						
							|  |  |  | 			brcmf_dbg(GLOM, "calling rxglom: glomd %p, glom %p\n", | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 				  bus->glomd, skb_peek(&bus->glom)); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			cnt = brcmf_sdbrcm_rxglom(bus, rd->seq_num); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			brcmf_dbg(GLOM, "rxglom returned %d\n", cnt); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			rd->seq_num += cnt - 1; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		rd->len_left = rd->len; | 
					
						
							|  |  |  | 		/* read header first for unknow frame length */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		if (!rd->len) { | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 			ret = brcmf_sdcard_recv_buf(bus->sdiodev, | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 						      bus->sdiodev->sbwad, | 
					
						
							|  |  |  | 						      SDIO_FUNC_2, F2SYNC, | 
					
						
							|  |  |  | 						      bus->rxhdr, | 
					
						
							|  |  |  | 						      BRCMF_FIRSTREAD); | 
					
						
							|  |  |  | 			bus->sdcnt.f2rxhdrs++; | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 			if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("RXHEADER FAILED: %d\n", | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 					  ret); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				bus->sdcnt.rx_hdrfail++; | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, true); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			brcmf_dbg_hex_dump(BRCMF_BYTES_ON() || BRCMF_HDRS_ON(), | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 					   bus->rxhdr, SDPCM_HDRLEN, | 
					
						
							|  |  |  | 					   "RxHdr:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			if (brcmf_sdio_hdparse(bus, bus->rxhdr, rd, | 
					
						
							|  |  |  | 					       BRCMF_SDIO_FT_NORMAL)) { | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				if (!bus->rxpending) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					continue; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			if (rd->channel == SDPCM_CONTROL_CHANNEL) { | 
					
						
							|  |  |  | 				brcmf_sdbrcm_read_control(bus, bus->rxhdr, | 
					
						
							|  |  |  | 							  rd->len, | 
					
						
							|  |  |  | 							  rd->dat_offset); | 
					
						
							|  |  |  | 				/* prepare the descriptor for the next read */ | 
					
						
							|  |  |  | 				rd->len = rd->len_nxtfrm << 4; | 
					
						
							|  |  |  | 				rd->len_nxtfrm = 0; | 
					
						
							|  |  |  | 				/* treat all packet as event if we don't know */ | 
					
						
							|  |  |  | 				rd->channel = SDPCM_EVENT_CHANNEL; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			rd->len_left = rd->len > BRCMF_FIRSTREAD ? | 
					
						
							|  |  |  | 				       rd->len - BRCMF_FIRSTREAD : 0; | 
					
						
							|  |  |  | 			head_read = BRCMF_FIRSTREAD; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		brcmf_pad(bus, &pad, &rd->len_left); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		pkt = brcmu_pkt_buf_get_skb(rd->len_left + head_read + | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 					    bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (!pkt) { | 
					
						
							|  |  |  | 			/* Give up on data, request rtx of events */ | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("brcmu_pkt_buf_get_skb failed\n"); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			brcmf_sdbrcm_rxfail(bus, false, | 
					
						
							|  |  |  | 					    RETRYCHAN(rd->channel)); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		skb_pull(pkt, head_read); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 		pkt_align(pkt, rd->len_left, bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 		ret = brcmf_sdcard_recv_pkt(bus->sdiodev, bus->sdiodev->sbwad, | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:38 -08:00
										 |  |  | 					      SDIO_FUNC_2, F2SYNC, pkt); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f2rxdata++; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 		if (ret < 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("read %d bytes from channel %d failed: %d\n", | 
					
						
							| 
									
										
										
										
											2013-03-03 12:45:28 +01:00
										 |  |  | 				  rd->len, rd->channel, ret); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			brcmu_pkt_buf_free_skb(pkt); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			brcmf_sdbrcm_rxfail(bus, true, | 
					
						
							|  |  |  | 					    RETRYCHAN(rd->channel)); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		if (head_read) { | 
					
						
							|  |  |  | 			skb_push(pkt, head_read); | 
					
						
							|  |  |  | 			memcpy(pkt->data, bus->rxhdr, head_read); | 
					
						
							|  |  |  | 			head_read = 0; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			memcpy(bus->rxhdr, pkt->data, SDPCM_HDRLEN); | 
					
						
							|  |  |  | 			rd_new.seq_num = rd->seq_num; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			if (brcmf_sdio_hdparse(bus, bus->rxhdr, &rd_new, | 
					
						
							|  |  |  | 					       BRCMF_SDIO_FT_NORMAL)) { | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				rd->len = 0; | 
					
						
							|  |  |  | 				brcmu_pkt_buf_free_skb(pkt); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			bus->sdcnt.rx_readahead_cnt++; | 
					
						
							|  |  |  | 			if (rd->len != roundup(rd_new.len, 16)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("frame length mismatch:read %d, should be %d\n", | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 					  rd->len, | 
					
						
							|  |  |  | 					  roundup(rd_new.len, 16) >> 4); | 
					
						
							|  |  |  | 				rd->len = 0; | 
					
						
							|  |  |  | 				brcmf_sdbrcm_rxfail(bus, true, true); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				brcmu_pkt_buf_free_skb(pkt); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			rd->len_nxtfrm = rd_new.len_nxtfrm; | 
					
						
							|  |  |  | 			rd->channel = rd_new.channel; | 
					
						
							|  |  |  | 			rd->dat_offset = rd_new.dat_offset; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && | 
					
						
							|  |  |  | 					     BRCMF_DATA_ON()) && | 
					
						
							|  |  |  | 					   BRCMF_HDRS_ON(), | 
					
						
							|  |  |  | 					   bus->rxhdr, SDPCM_HDRLEN, | 
					
						
							|  |  |  | 					   "RxHdr:\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (rd_new.channel == SDPCM_CONTROL_CHANNEL) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("readahead on control packet %d?\n", | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 					  rd_new.seq_num); | 
					
						
							|  |  |  | 				/* Force retry w/normal header read */ | 
					
						
							|  |  |  | 				rd->len = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				brcmf_sdbrcm_rxfail(bus, false, true); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				brcmu_pkt_buf_free_skb(pkt); | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_DATA_ON(), | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				   pkt->data, rd->len, "Rx Data:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Save superframe descriptor and allocate packet frame */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		if (rd->channel == SDPCM_GLOM_CHANNEL) { | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_HWHDR_LEN])) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				brcmf_dbg(GLOM, "glom descriptor, %d bytes:\n", | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 					  rd->len); | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 				brcmf_dbg_hex_dump(BRCMF_GLOM_ON(), | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 						   pkt->data, rd->len, | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 						   "Glom Data:\n"); | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 				__skb_trim(pkt, rd->len); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				skb_pull(pkt, SDPCM_HDRLEN); | 
					
						
							|  |  |  | 				bus->glomd = pkt; | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("%s: glom superframe w/o " | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  "descriptor!\n", __func__); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				brcmf_sdbrcm_rxfail(bus, false, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 			/* prepare the descriptor for the next read */ | 
					
						
							|  |  |  | 			rd->len = rd->len_nxtfrm << 4; | 
					
						
							|  |  |  | 			rd->len_nxtfrm = 0; | 
					
						
							|  |  |  | 			/* treat all packet as event if we don't know */ | 
					
						
							|  |  |  | 			rd->channel = SDPCM_EVENT_CHANNEL; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Fill in packet len and prio, deliver upward */ | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		__skb_trim(pkt, rd->len); | 
					
						
							|  |  |  | 		skb_pull(pkt, rd->dat_offset); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* prepare the descriptor for the next read */ | 
					
						
							|  |  |  | 		rd->len = rd->len_nxtfrm << 4; | 
					
						
							|  |  |  | 		rd->len_nxtfrm = 0; | 
					
						
							|  |  |  | 		/* treat all packet as event if we don't know */ | 
					
						
							|  |  |  | 		rd->channel = SDPCM_EVENT_CHANNEL; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		if (pkt->len == 0) { | 
					
						
							|  |  |  | 			brcmu_pkt_buf_free_skb(pkt); | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:46 +02:00
										 |  |  | 		brcmf_rx_frame(bus->sdiodev->dev, pkt); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	rxcount = maxframes - rxleft; | 
					
						
							|  |  |  | 	/* Message if we hit the limit */ | 
					
						
							|  |  |  | 	if (!rxleft) | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		brcmf_dbg(DATA, "hit rx limit of %d frames\n", maxframes); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	else | 
					
						
							|  |  |  | 		brcmf_dbg(DATA, "processed %d frames\n", rxcount); | 
					
						
							|  |  |  | 	/* Back off rxseq if awaiting rtx, update rx_seq */ | 
					
						
							|  |  |  | 	if (bus->rxskip) | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		rd->seq_num--; | 
					
						
							|  |  |  | 	bus->rx_seq = rd->seq_num; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return rxcount; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | brcmf_sdbrcm_wait_event_wakeup(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	if (waitqueue_active(&bus->ctrl_wait)) | 
					
						
							|  |  |  | 		wake_up_interruptible(&bus->ctrl_wait); | 
					
						
							|  |  |  | 	return; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | static int brcmf_sdio_txpkt_hdalign(struct brcmf_sdio *bus, struct sk_buff *pkt) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	u16 head_pad; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u8 *dat_buf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	dat_buf = (u8 *)(pkt->data); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check head padding */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	head_pad = ((unsigned long)dat_buf % bus->head_align); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	if (head_pad) { | 
					
						
							|  |  |  | 		if (skb_headroom(pkt) < head_pad) { | 
					
						
							|  |  |  | 			bus->sdiodev->bus_if->tx_realloc++; | 
					
						
							|  |  |  | 			head_pad = 0; | 
					
						
							|  |  |  | 			if (skb_cow(pkt, head_pad)) | 
					
						
							|  |  |  | 				return -ENOMEM; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		skb_push(pkt, head_pad); | 
					
						
							|  |  |  | 		dat_buf = (u8 *)(pkt->data); | 
					
						
							|  |  |  | 		memset(dat_buf, 0, head_pad + bus->tx_hdrlen); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return head_pad; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:43 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * struct brcmf_skbuff_cb reserves first two bytes in sk_buff::cb for | 
					
						
							|  |  |  |  * bus layer usage. | 
					
						
							|  |  |  |  */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | /* flag marking a dummy skb added for DMA alignment requirement */ | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:43 +02:00
										 |  |  | #define ALIGN_SKB_FLAG		0x8000
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | /* bit mask of data length chopped from the previous packet */ | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:43 +02:00
										 |  |  | #define ALIGN_SKB_CHOP_LEN_MASK	0x7fff
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | static int brcmf_sdio_txpkt_prep_sg(struct brcmf_sdio *bus, | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 				    struct sk_buff_head *pktq, | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 				    struct sk_buff *pkt, u16 total_len) | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 	struct sk_buff *pkt_pad; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	u16 tail_pad, tail_chop, chain_pad; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 	unsigned int blksize; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	bool lastfrm; | 
					
						
							|  |  |  | 	int ntail, ret; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	sdiodev = bus->sdiodev; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 	blksize = sdiodev->func[SDIO_FUNC_2]->cur_blksize; | 
					
						
							|  |  |  | 	/* sg entry alignment should be a divisor of block size */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	WARN_ON(blksize % bus->sgentry_align); | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Check tail padding */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	lastfrm = skb_queue_is_last(pktq, pkt); | 
					
						
							|  |  |  | 	tail_pad = 0; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	tail_chop = pkt->len % bus->sgentry_align; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	if (tail_chop) | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 		tail_pad = bus->sgentry_align - tail_chop; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	chain_pad = (total_len + tail_pad) % blksize; | 
					
						
							|  |  |  | 	if (lastfrm && chain_pad) | 
					
						
							|  |  |  | 		tail_pad += blksize - chain_pad; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 	if (skb_tailroom(pkt) < tail_pad && pkt->len > blksize) { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		pkt_pad = bus->txglom_sgpad; | 
					
						
							|  |  |  | 		if (pkt_pad == NULL) | 
					
						
							|  |  |  | 			  brcmu_pkt_buf_get_skb(tail_pad + tail_chop); | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 		if (pkt_pad == NULL) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		ret = brcmf_sdio_txpkt_hdalign(bus, pkt_pad); | 
					
						
							|  |  |  | 		if (unlikely(ret < 0)) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 		memcpy(pkt_pad->data, | 
					
						
							|  |  |  | 		       pkt->data + pkt->len - tail_chop, | 
					
						
							|  |  |  | 		       tail_chop); | 
					
						
							|  |  |  | 		*(u32 *)(pkt_pad->cb) = ALIGN_SKB_FLAG + tail_chop; | 
					
						
							|  |  |  | 		skb_trim(pkt, pkt->len - tail_chop); | 
					
						
							|  |  |  | 		__skb_queue_after(pktq, pkt, pkt_pad); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ntail = pkt->data_len + tail_pad - | 
					
						
							|  |  |  | 			(pkt->end - pkt->tail); | 
					
						
							|  |  |  | 		if (skb_cloned(pkt) || ntail > 0) | 
					
						
							|  |  |  | 			if (pskb_expand_head(pkt, 0, ntail, GFP_ATOMIC)) | 
					
						
							|  |  |  | 				return -ENOMEM; | 
					
						
							|  |  |  | 		if (skb_linearize(pkt)) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 		__skb_put(pkt, tail_pad); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	return tail_pad; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * brcmf_sdio_txpkt_prep - packet preparation for transmit | 
					
						
							|  |  |  |  * @bus: brcmf_sdio structure pointer | 
					
						
							|  |  |  |  * @pktq: packet list pointer | 
					
						
							|  |  |  |  * @chan: virtual channel to transmit the packet | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Processes to be applied to the packet | 
					
						
							|  |  |  |  *	- Align data buffer pointer | 
					
						
							|  |  |  |  *	- Align data buffer length | 
					
						
							|  |  |  |  *	- Prepare header | 
					
						
							|  |  |  |  * Return: negative value if there is error | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | brcmf_sdio_txpkt_prep(struct brcmf_sdio *bus, struct sk_buff_head *pktq, | 
					
						
							|  |  |  | 		      uint chan) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u16 head_pad, total_len; | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 	struct sk_buff *pkt_next; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u8 txseq; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	struct brcmf_sdio_hdrinfo hd_info = {0}; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	txseq = bus->tx_seq; | 
					
						
							|  |  |  | 	total_len = 0; | 
					
						
							|  |  |  | 	skb_queue_walk(pktq, pkt_next) { | 
					
						
							|  |  |  | 		/* alignment packet inserted in previous
 | 
					
						
							|  |  |  | 		 * loop cycle can be skipped as it is | 
					
						
							|  |  |  | 		 * already properly aligned and does not | 
					
						
							|  |  |  | 		 * need an sdpcm header. | 
					
						
							|  |  |  | 		 */ | 
					
						
							|  |  |  | 		if (*(u32 *)(pkt_next->cb) & ALIGN_SKB_FLAG) | 
					
						
							|  |  |  | 			continue; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		/* align packet data pointer */ | 
					
						
							|  |  |  | 		ret = brcmf_sdio_txpkt_hdalign(bus, pkt_next); | 
					
						
							|  |  |  | 		if (ret < 0) | 
					
						
							|  |  |  | 			return ret; | 
					
						
							|  |  |  | 		head_pad = (u16)ret; | 
					
						
							|  |  |  | 		if (head_pad) | 
					
						
							|  |  |  | 			memset(pkt_next->data, 0, head_pad + bus->tx_hdrlen); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		total_len += pkt_next->len; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-10-15 15:44:56 +02:00
										 |  |  | 		hd_info.len = pkt_next->len; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		hd_info.lastfrm = skb_queue_is_last(pktq, pkt_next); | 
					
						
							|  |  |  | 		if (bus->txglom && pktq->qlen > 1) { | 
					
						
							|  |  |  | 			ret = brcmf_sdio_txpkt_prep_sg(bus, pktq, | 
					
						
							|  |  |  | 						       pkt_next, total_len); | 
					
						
							|  |  |  | 			if (ret < 0) | 
					
						
							|  |  |  | 				return ret; | 
					
						
							|  |  |  | 			hd_info.tail_pad = (u16)ret; | 
					
						
							|  |  |  | 			total_len += (u16)ret; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		hd_info.channel = chan; | 
					
						
							|  |  |  | 		hd_info.dat_offset = head_pad + bus->tx_hdrlen; | 
					
						
							|  |  |  | 		hd_info.seq_num = txseq++; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Now fill the header */ | 
					
						
							|  |  |  | 		brcmf_sdio_hdpack(bus, pkt_next->data, &hd_info); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (BRCMF_BYTES_ON() && | 
					
						
							|  |  |  | 		    ((BRCMF_CTL_ON() && chan == SDPCM_CONTROL_CHANNEL) || | 
					
						
							|  |  |  | 		     (BRCMF_DATA_ON() && chan != SDPCM_CONTROL_CHANNEL))) | 
					
						
							|  |  |  | 			brcmf_dbg_hex_dump(true, pkt_next, hd_info.len, | 
					
						
							|  |  |  | 					   "Tx Frame:\n"); | 
					
						
							|  |  |  | 		else if (BRCMF_HDRS_ON()) | 
					
						
							|  |  |  | 			brcmf_dbg_hex_dump(true, pkt_next, | 
					
						
							|  |  |  | 					   head_pad + bus->tx_hdrlen, | 
					
						
							|  |  |  | 					   "Tx Header:\n"); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Hardware length tag of the first packet should be total
 | 
					
						
							|  |  |  | 	 * length of the chain (including padding) | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (bus->txglom) | 
					
						
							|  |  |  | 		brcmf_sdio_update_hwhdr(pktq->next->data, total_len); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | /**
 | 
					
						
							|  |  |  |  * brcmf_sdio_txpkt_postp - packet post processing for transmit | 
					
						
							|  |  |  |  * @bus: brcmf_sdio structure pointer | 
					
						
							|  |  |  |  * @pktq: packet list pointer | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Processes to be applied to the packet | 
					
						
							|  |  |  |  *	- Remove head padding | 
					
						
							|  |  |  |  *	- Remove tail padding | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | brcmf_sdio_txpkt_postp(struct brcmf_sdio *bus, struct sk_buff_head *pktq) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 *hdr; | 
					
						
							|  |  |  | 	u32 dat_offset; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u16 tail_pad; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 	u32 dummy_flags, chop_len; | 
					
						
							|  |  |  | 	struct sk_buff *pkt_next, *tmp, *pkt_prev; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	skb_queue_walk_safe(pktq, pkt_next, tmp) { | 
					
						
							|  |  |  | 		dummy_flags = *(u32 *)(pkt_next->cb); | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:43 +02:00
										 |  |  | 		if (dummy_flags & ALIGN_SKB_FLAG) { | 
					
						
							|  |  |  | 			chop_len = dummy_flags & ALIGN_SKB_CHOP_LEN_MASK; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 			if (chop_len) { | 
					
						
							|  |  |  | 				pkt_prev = pkt_next->prev; | 
					
						
							|  |  |  | 				skb_put(pkt_prev, chop_len); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			__skb_unlink(pkt_next, pktq); | 
					
						
							|  |  |  | 			brcmu_pkt_buf_free_skb(pkt_next); | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 			hdr = pkt_next->data + bus->tx_hdrlen - SDPCM_SWHDR_LEN; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 			dat_offset = le32_to_cpu(*(__le32 *)hdr); | 
					
						
							|  |  |  | 			dat_offset = (dat_offset & SDPCM_DOFFSET_MASK) >> | 
					
						
							|  |  |  | 				     SDPCM_DOFFSET_SHIFT; | 
					
						
							|  |  |  | 			skb_pull(pkt_next, dat_offset); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 			if (bus->txglom) { | 
					
						
							|  |  |  | 				tail_pad = le16_to_cpu(*(__le16 *)(hdr - 2)); | 
					
						
							|  |  |  | 				skb_trim(pkt_next, pkt_next->len - tail_pad); | 
					
						
							|  |  |  | 			} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | } | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | /* Writes a HW/SW header into the packet and sends it. */ | 
					
						
							|  |  |  | /* Assumes: (a) header space already there, (b) caller holds lock */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | static int brcmf_sdbrcm_txpkt(struct brcmf_sdio *bus, struct sk_buff_head *pktq, | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 			      uint chan) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 	int i; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	struct sk_buff *pkt_next, *tmp; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	ret = brcmf_sdio_txpkt_prep(bus, pktq, chan); | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:26 +02:00
										 |  |  | 	if (ret) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:38 -08:00
										 |  |  | 	ret = brcmf_sdcard_send_pkt(bus->sdiodev, bus->sdiodev->sbwad, | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 				    SDIO_FUNC_2, F2SYNC, pktq); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.f2txdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		/* On failure, abort the command and terminate the frame */ | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", | 
					
						
							|  |  |  | 			  ret); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.tx_sderrs++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, | 
					
						
							|  |  |  | 				 SFC_WF_TERM, NULL); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f1regdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < 3; i++) { | 
					
						
							|  |  |  | 			u8 hi, lo; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			hi = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					      SBSDIO_FUNC1_WFRAMEBCHI, NULL); | 
					
						
							|  |  |  | 			lo = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					      SBSDIO_FUNC1_WFRAMEBCLO, NULL); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if ((hi == 0) && (lo == 0)) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | done: | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	brcmf_sdio_txpkt_postp(bus, pktq); | 
					
						
							|  |  |  | 	if (ret == 0) | 
					
						
							|  |  |  | 		bus->tx_seq = (bus->tx_seq + pktq->qlen) % SDPCM_SEQ_WRAP; | 
					
						
							|  |  |  | 	skb_queue_walk_safe(pktq, pkt_next, tmp) { | 
					
						
							|  |  |  | 		__skb_unlink(pkt_next, pktq); | 
					
						
							|  |  |  | 		brcmf_txcomplete(bus->sdiodev->dev, pkt_next, ret == 0); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static uint brcmf_sdbrcm_sendfromq(struct brcmf_sdio *bus, uint maxframes) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct sk_buff *pkt; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	struct sk_buff_head pktq; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	u32 intstatus = 0; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	int ret = 0, prec_out, i; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint cnt = 0; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u8 tx_prec_map, pkt_num; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	tx_prec_map = ~bus->flowcontrol; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Send frames until the limit or some other event */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	for (cnt = 0; (cnt < maxframes) && data_ok(bus);) { | 
					
						
							|  |  |  | 		pkt_num = 1; | 
					
						
							|  |  |  | 		__skb_queue_head_init(&pktq); | 
					
						
							|  |  |  | 		if (bus->txglom) | 
					
						
							|  |  |  | 			pkt_num = min_t(u8, bus->tx_max - bus->tx_seq, | 
					
						
							|  |  |  | 					brcmf_sdio_txglomsz); | 
					
						
							|  |  |  | 		pkt_num = min_t(u32, pkt_num, | 
					
						
							|  |  |  | 				brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		spin_lock_bh(&bus->txqlock); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		for (i = 0; i < pkt_num; i++) { | 
					
						
							|  |  |  | 			pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, | 
					
						
							|  |  |  | 					      &prec_out); | 
					
						
							|  |  |  | 			if (pkt == NULL) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			__skb_queue_tail(&pktq, pkt); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 		spin_unlock_bh(&bus->txqlock); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		if (i == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		ret = brcmf_sdbrcm_txpkt(bus, &pktq, SDPCM_DATA_CHANNEL); | 
					
						
							|  |  |  | 		cnt += i; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* In poll mode, need to check for other events */ | 
					
						
							|  |  |  | 		if (!bus->intr && cnt) { | 
					
						
							|  |  |  | 			/* Check device status, signal pending interrupt */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 			ret = r_sdreg32(bus, &intstatus, | 
					
						
							|  |  |  | 					offsetof(struct sdpcmd_regs, | 
					
						
							|  |  |  | 						 intstatus)); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.f2txdata++; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 			if (ret != 0) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				break; | 
					
						
							|  |  |  | 			if (intstatus & bus->hostintmask) | 
					
						
							| 
									
										
										
										
											2012-09-13 21:11:59 +02:00
										 |  |  | 				atomic_set(&bus->ipend, 1); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Deflow-control stack if needed */ | 
					
						
							| 
									
										
										
										
											2013-02-06 18:40:36 +01:00
										 |  |  | 	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DATA) && | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:08 -08:00
										 |  |  | 	    bus->txoff && (pktq_len(&bus->txq) < TXLOW)) { | 
					
						
							| 
									
										
										
										
											2012-09-11 21:18:46 +02:00
										 |  |  | 		bus->txoff = false; | 
					
						
							|  |  |  | 		brcmf_txflowblock(bus->sdiodev->dev, false); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:08 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return cnt; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | static void brcmf_sdbrcm_bus_stop(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 local_hostintmask; | 
					
						
							|  |  |  | 	u8 saveclk; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:02 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (bus->watchdog_tsk) { | 
					
						
							|  |  |  | 		send_sig(SIGTERM, bus->watchdog_tsk, 1); | 
					
						
							|  |  |  | 		kthread_stop(bus->watchdog_tsk); | 
					
						
							|  |  |  | 		bus->watchdog_tsk = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable clock for device interrupts */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	brcmf_sdbrcm_bus_sleep(bus, false, false); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable and clear interrupts at the chip level also */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 	w_sdreg32(bus, 0, offsetof(struct sdpcmd_regs, hostintmask)); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	local_hostintmask = bus->hostintmask; | 
					
						
							|  |  |  | 	bus->hostintmask = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Change our idea of bus state */ | 
					
						
							|  |  |  | 	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Force clocks on backplane to be sure F2 interrupt propagates */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 	saveclk = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 				   SBSDIO_FUNC1_CHIPCLKCSR, &err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	if (!err) { | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 				 (saveclk | SBSDIO_FORCE_HT), &err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (err) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Failed to force clock for F2: err %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Turn off the bus (F2), free any pending packets */ | 
					
						
							|  |  |  | 	brcmf_dbg(INTR, "disable SDIO interrupts\n"); | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 	sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear any pending interrupts now that F2 is disabled */ | 
					
						
							|  |  |  | 	w_sdreg32(bus, local_hostintmask, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 		  offsetof(struct sdpcmd_regs, intstatus)); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Turn off the backplane clock (only) */ | 
					
						
							|  |  |  | 	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear the data packet queues */ | 
					
						
							|  |  |  | 	brcmu_pktq_flush(&bus->txq, true, NULL, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear any held glomming stuff */ | 
					
						
							|  |  |  | 	if (bus->glomd) | 
					
						
							|  |  |  | 		brcmu_pkt_buf_free_skb(bus->glomd); | 
					
						
							|  |  |  | 	brcmf_sdbrcm_free_glom(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear rx control and wake any waiters */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_lock_bh(&bus->rxctl_lock); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	bus->rxlen = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_unlock_bh(&bus->rxctl_lock); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	brcmf_sdbrcm_dcmd_resp_wake(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Reset some F2 state stuff */ | 
					
						
							|  |  |  | 	bus->rxskip = false; | 
					
						
							|  |  |  | 	bus->tx_seq = bus->rx_seq = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:59 -07:00
										 |  |  | static inline void brcmf_sdbrcm_clrintr(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	unsigned long flags; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-12 10:55:55 +02:00
										 |  |  | 	if (bus->sdiodev->oob_irq_requested) { | 
					
						
							|  |  |  | 		spin_lock_irqsave(&bus->sdiodev->irq_en_lock, flags); | 
					
						
							|  |  |  | 		if (!bus->sdiodev->irq_en && !atomic_read(&bus->ipend)) { | 
					
						
							|  |  |  | 			enable_irq(bus->sdiodev->pdata->oob_irq_nr); | 
					
						
							|  |  |  | 			bus->sdiodev->irq_en = true; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		spin_unlock_irqrestore(&bus->sdiodev->irq_en_lock, flags); | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:59 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | static int brcmf_sdio_intr_rstatus(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 idx; | 
					
						
							|  |  |  | 	u32 addr; | 
					
						
							|  |  |  | 	unsigned long val; | 
					
						
							|  |  |  | 	int n, ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); | 
					
						
							|  |  |  | 	addr = bus->ci->c_inf[idx].base + | 
					
						
							|  |  |  | 	       offsetof(struct sdpcmd_regs, intstatus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, false); | 
					
						
							|  |  |  | 	bus->sdcnt.f1regdata++; | 
					
						
							|  |  |  | 	if (ret != 0) | 
					
						
							|  |  |  | 		val = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val &= bus->hostintmask; | 
					
						
							|  |  |  | 	atomic_set(&bus->fcstate, !!(val & I_HMB_FC_STATE)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Clear interrupts */ | 
					
						
							|  |  |  | 	if (val) { | 
					
						
							|  |  |  | 		ret = brcmf_sdio_regrw_helper(bus->sdiodev, addr, &val, true); | 
					
						
							|  |  |  | 		bus->sdcnt.f1regdata++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret) { | 
					
						
							|  |  |  | 		atomic_set(&bus->intstatus, 0); | 
					
						
							|  |  |  | 	} else if (val) { | 
					
						
							|  |  |  | 		for_each_set_bit(n, &val, 32) | 
					
						
							|  |  |  | 			set_bit(n, (unsigned long *)&bus->intstatus.counter); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | static void brcmf_sdbrcm_dpc(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	u32 newstatus = 0; | 
					
						
							|  |  |  | 	unsigned long intstatus; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint rxlimit = bus->rxbound;	/* Rx frames to read before resched */ | 
					
						
							|  |  |  | 	uint txlimit = bus->txbound;	/* Tx frames to send before resched */ | 
					
						
							|  |  |  | 	uint framecnt = 0;	/* Temporary counter of tx/rx frames */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	int err = 0, n; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If waiting for HTAVAIL, check status */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	if (!bus->sr_enabled && bus->clkstate == CLK_PENDING) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		u8 clkctl, devctl = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		/* Check for inconsistent device control */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		devctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					  SBSDIO_DEVICE_CTL, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("error reading DEVCTL: %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Read CSR, if clock on switch to AVAIL, else ignore */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		clkctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					  SBSDIO_FUNC1_CHIPCLKCSR, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("error reading CSR: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				  err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			  devctl, clkctl); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (SBSDIO_HTAV(clkctl)) { | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			devctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						  SBSDIO_DEVICE_CTL, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("error reading DEVCTL: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_DEVICE_CTL, | 
					
						
							|  |  |  | 					 devctl, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 				brcmf_err("error writing DEVCTL: %d\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  err); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 				bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 			bus->clkstate = CLK_AVAIL; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure backplane clock is on */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	brcmf_sdbrcm_bus_sleep(bus, false, true); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Pending interrupt indicates new device status */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:11:59 +02:00
										 |  |  | 	if (atomic_read(&bus->ipend) > 0) { | 
					
						
							|  |  |  | 		atomic_set(&bus->ipend, 0); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 		err = brcmf_sdio_intr_rstatus(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	/* Start with leftover status bits */ | 
					
						
							|  |  |  | 	intstatus = atomic_xchg(&bus->intstatus, 0); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Handle flow-control change: read new state in case our ack
 | 
					
						
							|  |  |  | 	 * crossed another change interrupt.  If change still set, assume | 
					
						
							|  |  |  | 	 * FC ON for safety, let next loop through do the debounce. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (intstatus & I_HMB_FC_CHANGE) { | 
					
						
							|  |  |  | 		intstatus &= ~I_HMB_FC_CHANGE; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 		err = w_sdreg32(bus, I_HMB_FC_CHANGE, | 
					
						
							|  |  |  | 				offsetof(struct sdpcmd_regs, intstatus)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 		err = r_sdreg32(bus, &newstatus, | 
					
						
							|  |  |  | 				offsetof(struct sdpcmd_regs, intstatus)); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 		atomic_set(&bus->fcstate, | 
					
						
							|  |  |  | 			   !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE))); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		intstatus |= (newstatus & bus->hostintmask); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Handle host mailbox indication */ | 
					
						
							|  |  |  | 	if (intstatus & I_HMB_HOST_INT) { | 
					
						
							|  |  |  | 		intstatus &= ~I_HMB_HOST_INT; | 
					
						
							|  |  |  | 		intstatus |= brcmf_sdbrcm_hostmail(bus); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:23 -08:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* Generally don't ask for these, can get CRC errors... */ | 
					
						
							|  |  |  | 	if (intstatus & I_WR_OOSYNC) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Dongle reports WR_OOSYNC\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		intstatus &= ~I_WR_OOSYNC; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (intstatus & I_RD_OOSYNC) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Dongle reports RD_OOSYNC\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		intstatus &= ~I_RD_OOSYNC; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (intstatus & I_SBINT) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Dongle reports SBINT\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		intstatus &= ~I_SBINT; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Would be active due to wake-wlan in gSPI */ | 
					
						
							|  |  |  | 	if (intstatus & I_CHIPACTIVE) { | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "Dongle reports CHIPACTIVE\n"); | 
					
						
							|  |  |  | 		intstatus &= ~I_CHIPACTIVE; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Ignore frame indications if rxskip is set */ | 
					
						
							|  |  |  | 	if (bus->rxskip) | 
					
						
							|  |  |  | 		intstatus &= ~I_HMB_FRAME_IND; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* On frame indication, read available frames */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 	if (PKT_AVAILABLE() && bus->clkstate == CLK_AVAIL) { | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		framecnt = brcmf_sdio_readframes(bus, rxlimit); | 
					
						
							|  |  |  | 		if (!bus->rxpending) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			intstatus &= ~I_HMB_FRAME_IND; | 
					
						
							|  |  |  | 		rxlimit -= min(framecnt, rxlimit); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Keep still-pending events for next scheduling */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	if (intstatus) { | 
					
						
							|  |  |  | 		for_each_set_bit(n, &intstatus, 32) | 
					
						
							|  |  |  | 			set_bit(n, (unsigned long *)&bus->intstatus.counter); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:59 -07:00
										 |  |  | 	brcmf_sdbrcm_clrintr(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (data_ok(bus) && bus->ctrl_frame_stat && | 
					
						
							|  |  |  | 		(bus->clkstate == CLK_AVAIL)) { | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 		int i; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 		err = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, | 
					
						
							| 
									
										
										
										
											2012-06-04 12:44:17 +00:00
										 |  |  | 			SDIO_FUNC_2, F2SYNC, bus->ctrl_frame_buf, | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:38 -08:00
										 |  |  | 			(u32) bus->ctrl_frame_len); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 		if (err < 0) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			/* On failure, abort the command and
 | 
					
						
							|  |  |  | 				terminate the frame */ | 
					
						
							|  |  |  | 			brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 				  err); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.tx_sderrs++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 			brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 					 SFC_WF_TERM, &err); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.f1regdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 			for (i = 0; i < 3; i++) { | 
					
						
							|  |  |  | 				u8 hi, lo; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 				hi = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						      SBSDIO_FUNC1_WFRAMEBCHI, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 						      &err); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 				lo = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 						      SBSDIO_FUNC1_WFRAMEBCLO, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 						      &err); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 				bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				if ((hi == 0) && (lo == 0)) | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 			bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:01 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->ctrl_frame_stat = false; | 
					
						
							|  |  |  | 		brcmf_sdbrcm_wait_event_wakeup(bus); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Send queued frames (limit 1 if rx may still be pending) */ | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	else if ((bus->clkstate == CLK_AVAIL) && !atomic_read(&bus->fcstate) && | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit | 
					
						
							|  |  |  | 		 && data_ok(bus)) { | 
					
						
							| 
									
										
										
										
											2012-09-19 22:21:11 +02:00
										 |  |  | 		framecnt = bus->rxpending ? min(txlimit, bus->txminmax) : | 
					
						
							|  |  |  | 					    txlimit; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt); | 
					
						
							|  |  |  | 		txlimit -= framecnt; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:37 -07:00
										 |  |  | 	if ((bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) || (err != 0)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("failed backplane access over SDIO, halting operation\n"); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 		bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 		atomic_set(&bus->intstatus, 0); | 
					
						
							|  |  |  | 	} else if (atomic_read(&bus->intstatus) || | 
					
						
							|  |  |  | 		   atomic_read(&bus->ipend) > 0 || | 
					
						
							|  |  |  | 		   (!atomic_read(&bus->fcstate) && | 
					
						
							|  |  |  | 		    brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && | 
					
						
							|  |  |  | 		    data_ok(bus)) || PKT_AVAILABLE()) { | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 		atomic_inc(&bus->dpc_tskcnt); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* If we're done for now, turn off clock request. */ | 
					
						
							|  |  |  | 	if ((bus->clkstate != CLK_PENDING) | 
					
						
							|  |  |  | 	    && bus->idletime == BRCMF_IDLE_IMMEDIATE) { | 
					
						
							|  |  |  | 		bus->activity = false; | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 		brcmf_dbg(SDIO, "idle state\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 		brcmf_sdbrcm_bus_sleep(bus, true, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:38 +02:00
										 |  |  | static struct pktq *brcmf_sdbrcm_bus_gettxq(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							|  |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return &bus->txq; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:15 -08:00
										 |  |  | static int brcmf_sdbrcm_bus_txdata(struct device *dev, struct sk_buff *pkt) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret = -EBADE; | 
					
						
							|  |  |  | 	uint datalen, prec; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:56 -08:00
										 |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:02 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:56 -08:00
										 |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:19 +02:00
										 |  |  | 	ulong flags; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	datalen = pkt->len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Add space for the header */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 	skb_push(pkt, bus->tx_hdrlen); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* precondition: IS_ALIGNED((unsigned long)(pkt->data), 2) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	prec = prio2prec((pkt->priority & PRIOMASK)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Check for existing queue, current flow-control,
 | 
					
						
							|  |  |  | 			 pending event, or pending clock */ | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "deferring pktq len %d\n", pktq_len(&bus->txq)); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.fcqueued++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Priority based enq */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:19 +02:00
										 |  |  | 	spin_lock_irqsave(&bus->txqlock, flags); | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 	if (!brcmf_c_prec_enq(bus->sdiodev->dev, &bus->txq, pkt, prec)) { | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 		skb_pull(pkt, bus->tx_hdrlen); | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("out of bus->txq !!!\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		ret = -ENOSR; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		ret = 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:08 -08:00
										 |  |  | 	if (pktq_len(&bus->txq) >= TXHI) { | 
					
						
							| 
									
										
										
										
											2012-09-11 21:18:46 +02:00
										 |  |  | 		bus->txoff = true; | 
					
						
							|  |  |  | 		brcmf_txflowblock(bus->sdiodev->dev, true); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:08 -08:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:19 +02:00
										 |  |  | 	spin_unlock_irqrestore(&bus->txqlock, flags); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (pktq_plen(&bus->txq, prec) > qcount[prec]) | 
					
						
							|  |  |  | 		qcount[prec] = pktq_plen(&bus->txq, prec); | 
					
						
							|  |  |  | #endif
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	if (atomic_read(&bus->dpc_tskcnt) == 0) { | 
					
						
							|  |  |  | 		atomic_inc(&bus->dpc_tskcnt); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 		queue_work(bus->brcmf_wq, &bus->datawork); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | #define CONSOLE_LINE_MAX	192
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_readconsole(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_console *c = &bus->console; | 
					
						
							|  |  |  | 	u8 line[CONSOLE_LINE_MAX], ch; | 
					
						
							|  |  |  | 	u32 n, idx, addr; | 
					
						
							|  |  |  | 	int rv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Don't do anything until FWREADY updates console address */ | 
					
						
							|  |  |  | 	if (bus->console_addr == 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read console log struct */ | 
					
						
							|  |  |  | 	addr = bus->console_addr + offsetof(struct rte_console, log_le); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&c->log_le, | 
					
						
							|  |  |  | 			      sizeof(c->log_le)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Allocate console buffer (one time only) */ | 
					
						
							|  |  |  | 	if (c->buf == NULL) { | 
					
						
							|  |  |  | 		c->bufsize = le32_to_cpu(c->log_le.buf_size); | 
					
						
							|  |  |  | 		c->buf = kmalloc(c->bufsize, GFP_ATOMIC); | 
					
						
							|  |  |  | 		if (c->buf == NULL) | 
					
						
							|  |  |  | 			return -ENOMEM; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	idx = le32_to_cpu(c->log_le.idx); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Protect against corrupt value */ | 
					
						
							|  |  |  | 	if (idx > c->bufsize) | 
					
						
							|  |  |  | 		return -EBADE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Skip reading the console buffer if the index pointer
 | 
					
						
							|  |  |  | 	 has not moved */ | 
					
						
							|  |  |  | 	if (idx == c->last) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read the console buffer */ | 
					
						
							|  |  |  | 	addr = le32_to_cpu(c->log_le.buf); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, c->buf, c->bufsize); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	while (c->last != idx) { | 
					
						
							|  |  |  | 		for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) { | 
					
						
							|  |  |  | 			if (c->last == idx) { | 
					
						
							|  |  |  | 				/* This would output a partial line.
 | 
					
						
							|  |  |  | 				 * Instead, back up | 
					
						
							|  |  |  | 				 * the buffer pointer and output this | 
					
						
							|  |  |  | 				 * line next time around. | 
					
						
							|  |  |  | 				 */ | 
					
						
							|  |  |  | 				if (c->last >= n) | 
					
						
							|  |  |  | 					c->last -= n; | 
					
						
							|  |  |  | 				else | 
					
						
							|  |  |  | 					c->last = c->bufsize - n; | 
					
						
							|  |  |  | 				goto break2; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 			ch = c->buf[c->last]; | 
					
						
							|  |  |  | 			c->last = (c->last + 1) % c->bufsize; | 
					
						
							|  |  |  | 			if (ch == '\n') | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			line[n] = ch; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (n > 0) { | 
					
						
							|  |  |  | 			if (line[n - 1] == '\r') | 
					
						
							|  |  |  | 				n--; | 
					
						
							|  |  |  | 			line[n] = 0; | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:42 -08:00
										 |  |  | 			pr_debug("CONSOLE: %s\n", line); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | break2: | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_tx_frame(struct brcmf_sdio *bus, u8 *frame, u16 len) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int i; | 
					
						
							|  |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus->ctrl_frame_stat = false; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:38 -08:00
										 |  |  | 	ret = brcmf_sdcard_send_buf(bus->sdiodev, bus->sdiodev->sbwad, | 
					
						
							|  |  |  | 				    SDIO_FUNC_2, F2SYNC, frame, len); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (ret < 0) { | 
					
						
							|  |  |  | 		/* On failure, abort the command and terminate the frame */ | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "sdio error %d, abort command and terminate frame\n", | 
					
						
							|  |  |  | 			  ret); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.tx_sderrs++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		brcmf_sdcard_abort(bus->sdiodev, SDIO_FUNC_2); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_FRAMECTRL, | 
					
						
							|  |  |  | 				 SFC_WF_TERM, NULL); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.f1regdata++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		for (i = 0; i < 3; i++) { | 
					
						
							|  |  |  | 			u8 hi, lo; | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 			hi = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					      SBSDIO_FUNC1_WFRAMEBCHI, NULL); | 
					
						
							|  |  |  | 			lo = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 					      SBSDIO_FUNC1_WFRAMEBCLO, NULL); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.f1regdata += 2; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (hi == 0 && lo == 0) | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		return ret; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQ_WRAP; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:16 -08:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:55 -08:00
										 |  |  | brcmf_sdbrcm_bus_txctl(struct device *dev, unsigned char *msg, uint msglen) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	u8 *frame; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	u16 len, pad; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	uint retries = 0; | 
					
						
							|  |  |  | 	u8 doff = 0; | 
					
						
							|  |  |  | 	int ret = -1; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:55 -08:00
										 |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:02 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:55 -08:00
										 |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	struct brcmf_sdio_hdrinfo hd_info = {0}; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Back the pointer to make a room for bus header */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 	frame = msg - bus->tx_hdrlen; | 
					
						
							|  |  |  | 	len = (msglen += bus->tx_hdrlen); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Add alignment padding (optional for ctl frames) */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	doff = ((unsigned long)frame % bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (doff) { | 
					
						
							|  |  |  | 		frame -= doff; | 
					
						
							|  |  |  | 		len += doff; | 
					
						
							|  |  |  | 		msglen += doff; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 		memset(frame, 0, doff + bus->tx_hdrlen); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	/* precondition: doff < bus->head_align */ | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 	doff += bus->tx_hdrlen; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Round send length to next SDIO block */ | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	pad = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (bus->roundup && bus->blocksize && (len > bus->blocksize)) { | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		pad = bus->blocksize - (len % bus->blocksize); | 
					
						
							|  |  |  | 		if ((pad > bus->roundup) || (pad >= bus->blocksize)) | 
					
						
							|  |  |  | 			pad = 0; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	} else if (len % bus->head_align) { | 
					
						
							|  |  |  | 		pad = bus->head_align - (len % bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	len += pad; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* precondition: IS_ALIGNED((unsigned long)frame, 2) */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure backplane clock is on */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	brcmf_sdbrcm_bus_sleep(bus, false, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	hd_info.len = (u16)msglen; | 
					
						
							|  |  |  | 	hd_info.channel = SDPCM_CONTROL_CHANNEL; | 
					
						
							|  |  |  | 	hd_info.dat_offset = doff; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	hd_info.seq_num = bus->tx_seq; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	hd_info.lastfrm = true; | 
					
						
							|  |  |  | 	hd_info.tail_pad = pad; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	brcmf_sdio_hdpack(bus, frame, &hd_info); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	if (bus->txglom) | 
					
						
							|  |  |  | 		brcmf_sdio_update_hwhdr(frame, len); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (!data_ok(bus)) { | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "No bus credit bus->tx_max %d, bus->tx_seq %d\n", | 
					
						
							|  |  |  | 			  bus->tx_max, bus->tx_seq); | 
					
						
							|  |  |  | 		bus->ctrl_frame_stat = true; | 
					
						
							|  |  |  | 		/* Send from dpc */ | 
					
						
							|  |  |  | 		bus->ctrl_frame_buf = frame; | 
					
						
							|  |  |  | 		bus->ctrl_frame_len = len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:25 -08:00
										 |  |  | 		wait_event_interruptible_timeout(bus->ctrl_wait, | 
					
						
							|  |  |  | 						 !bus->ctrl_frame_stat, | 
					
						
							|  |  |  | 						 msecs_to_jiffies(2000)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 		if (!bus->ctrl_frame_stat) { | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 			brcmf_dbg(SDIO, "ctrl_frame_stat == false\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			ret = 0; | 
					
						
							|  |  |  | 		} else { | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 			brcmf_dbg(SDIO, "ctrl_frame_stat == true\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			ret = -1; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret == -1) { | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:40 -08:00
										 |  |  | 		brcmf_dbg_hex_dump(BRCMF_BYTES_ON() && BRCMF_CTL_ON(), | 
					
						
							|  |  |  | 				   frame, len, "Tx Frame:\n"); | 
					
						
							|  |  |  | 		brcmf_dbg_hex_dump(!(BRCMF_BYTES_ON() && BRCMF_CTL_ON()) && | 
					
						
							|  |  |  | 				   BRCMF_HDRS_ON(), | 
					
						
							|  |  |  | 				   frame, min_t(u16, len, 16), "TxHdr:\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		do { | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			ret = brcmf_tx_frame(bus, frame, len); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} while (ret < 0 && retries++ < TXRETRIES); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 	if ((bus->idletime == BRCMF_IDLE_IMMEDIATE) && | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	    atomic_read(&bus->dpc_tskcnt) == 0) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->activity = false; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 		brcmf_dbg(INFO, "idle\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_clkctl(bus, CLK_NONE, true); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (ret) | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.tx_ctlerrs++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.tx_ctlpkts++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret ? -EIO : 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | static inline bool brcmf_sdio_valid_shared_address(u32 addr) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return !(addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdio_readshared(struct brcmf_sdio *bus, | 
					
						
							|  |  |  | 				 struct sdpcm_shared *sh) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 addr; | 
					
						
							|  |  |  | 	int rv; | 
					
						
							|  |  |  | 	u32 shaddr = 0; | 
					
						
							|  |  |  | 	struct sdpcm_shared_le sh_le; | 
					
						
							|  |  |  | 	__le32 addr_le; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:51 +02:00
										 |  |  | 	shaddr = bus->ci->rambase + bus->ramsize - 4; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Read last word in socram to determine | 
					
						
							|  |  |  | 	 * address of sdpcm_shared structure | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	brcmf_sdbrcm_bus_sleep(bus, false, false); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, shaddr, (u8 *)&addr_le, 4); | 
					
						
							| 
									
										
										
										
											2013-02-08 12:06:33 +01:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = le32_to_cpu(addr_le); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:44 +02:00
										 |  |  | 	brcmf_dbg(SDIO, "sdpcm_shared address 0x%08X\n", addr); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							|  |  |  | 	 * Check if addr is valid. | 
					
						
							|  |  |  | 	 * NVRAM length at the end of memory should have been overwritten. | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (!brcmf_sdio_valid_shared_address(addr)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("invalid sdpcm_shared address 0x%08X\n", | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 				  addr); | 
					
						
							|  |  |  | 			return -EINVAL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Read hndrte_shared structure */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, (u8 *)&sh_le, | 
					
						
							|  |  |  | 			      sizeof(struct sdpcm_shared_le)); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Endianness */ | 
					
						
							|  |  |  | 	sh->flags = le32_to_cpu(sh_le.flags); | 
					
						
							|  |  |  | 	sh->trap_addr = le32_to_cpu(sh_le.trap_addr); | 
					
						
							|  |  |  | 	sh->assert_exp_addr = le32_to_cpu(sh_le.assert_exp_addr); | 
					
						
							|  |  |  | 	sh->assert_file_addr = le32_to_cpu(sh_le.assert_file_addr); | 
					
						
							|  |  |  | 	sh->assert_line = le32_to_cpu(sh_le.assert_line); | 
					
						
							|  |  |  | 	sh->console_addr = le32_to_cpu(sh_le.console_addr); | 
					
						
							|  |  |  | 	sh->msgtrace_addr = le32_to_cpu(sh_le.msgtrace_addr); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:43 +02:00
										 |  |  | 	if ((sh->flags & SDPCM_SHARED_VERSION_MASK) > SDPCM_SHARED_VERSION) { | 
					
						
							|  |  |  | 		brcmf_err("sdpcm shared version unsupported: dhd %d dongle %d\n", | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 			  SDPCM_SHARED_VERSION, | 
					
						
							|  |  |  | 			  sh->flags & SDPCM_SHARED_VERSION_MASK); | 
					
						
							|  |  |  | 		return -EPROTO; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdio_dump_console(struct brcmf_sdio *bus, | 
					
						
							|  |  |  | 				   struct sdpcm_shared *sh, char __user *data, | 
					
						
							|  |  |  | 				   size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 addr, console_ptr, console_size, console_index; | 
					
						
							|  |  |  | 	char *conbuf = NULL; | 
					
						
							|  |  |  | 	__le32 sh_val; | 
					
						
							|  |  |  | 	int rv; | 
					
						
							|  |  |  | 	loff_t pos = 0; | 
					
						
							|  |  |  | 	int nbytes = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* obtain console information from device memory */ | 
					
						
							|  |  |  | 	addr = sh->console_addr + offsetof(struct rte_console, log_le); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, | 
					
						
							|  |  |  | 			      (u8 *)&sh_val, sizeof(u32)); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 	console_ptr = le32_to_cpu(sh_val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = sh->console_addr + offsetof(struct rte_console, log_le.buf_size); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, | 
					
						
							|  |  |  | 			      (u8 *)&sh_val, sizeof(u32)); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 	console_size = le32_to_cpu(sh_val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	addr = sh->console_addr + offsetof(struct rte_console, log_le.idx); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, addr, | 
					
						
							|  |  |  | 			      (u8 *)&sh_val, sizeof(u32)); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		return rv; | 
					
						
							|  |  |  | 	console_index = le32_to_cpu(sh_val); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* allocate buffer for console data */ | 
					
						
							|  |  |  | 	if (console_size <= CONSOLE_BUFFER_MAX) | 
					
						
							|  |  |  | 		conbuf = vzalloc(console_size+1); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!conbuf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* obtain the console data from device */ | 
					
						
							|  |  |  | 	conbuf[console_size] = '\0'; | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	rv = brcmf_sdio_ramrw(bus->sdiodev, false, console_ptr, (u8 *)conbuf, | 
					
						
							|  |  |  | 			      console_size); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	rv = simple_read_from_buffer(data, count, &pos, | 
					
						
							|  |  |  | 				     conbuf + console_index, | 
					
						
							|  |  |  | 				     console_size - console_index); | 
					
						
							|  |  |  | 	if (rv < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	nbytes = rv; | 
					
						
							|  |  |  | 	if (console_index > 0) { | 
					
						
							|  |  |  | 		pos = 0; | 
					
						
							|  |  |  | 		rv = simple_read_from_buffer(data+nbytes, count, &pos, | 
					
						
							|  |  |  | 					     conbuf, console_index - 1); | 
					
						
							|  |  |  | 		if (rv < 0) | 
					
						
							|  |  |  | 			goto done; | 
					
						
							|  |  |  | 		rv += nbytes; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | done: | 
					
						
							|  |  |  | 	vfree(conbuf); | 
					
						
							|  |  |  | 	return rv; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdio_trap_info(struct brcmf_sdio *bus, struct sdpcm_shared *sh, | 
					
						
							|  |  |  | 				char __user *data, size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error, res; | 
					
						
							|  |  |  | 	char buf[350]; | 
					
						
							|  |  |  | 	struct brcmf_trap_info tr; | 
					
						
							|  |  |  | 	loff_t pos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 	if ((sh->flags & SDPCM_SHARED_TRAP) == 0) { | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "no trap in firmware\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 		return 0; | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 	error = brcmf_sdio_ramrw(bus->sdiodev, false, sh->trap_addr, (u8 *)&tr, | 
					
						
							|  |  |  | 				 sizeof(struct brcmf_trap_info)); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		return error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = scnprintf(buf, sizeof(buf), | 
					
						
							|  |  |  | 			"dongle trap info: type 0x%x @ epc 0x%08x\n" | 
					
						
							|  |  |  | 			"  cpsr 0x%08x spsr 0x%08x sp 0x%08x\n" | 
					
						
							|  |  |  | 			"  lr   0x%08x pc   0x%08x offset 0x%x\n" | 
					
						
							|  |  |  | 			"  r0   0x%08x r1   0x%08x r2 0x%08x r3 0x%08x\n" | 
					
						
							|  |  |  | 			"  r4   0x%08x r5   0x%08x r6 0x%08x r7 0x%08x\n", | 
					
						
							|  |  |  | 			le32_to_cpu(tr.type), le32_to_cpu(tr.epc), | 
					
						
							|  |  |  | 			le32_to_cpu(tr.cpsr), le32_to_cpu(tr.spsr), | 
					
						
							|  |  |  | 			le32_to_cpu(tr.r13), le32_to_cpu(tr.r14), | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:40 +02:00
										 |  |  | 			le32_to_cpu(tr.pc), sh->trap_addr, | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 			le32_to_cpu(tr.r0), le32_to_cpu(tr.r1), | 
					
						
							|  |  |  | 			le32_to_cpu(tr.r2), le32_to_cpu(tr.r3), | 
					
						
							|  |  |  | 			le32_to_cpu(tr.r4), le32_to_cpu(tr.r5), | 
					
						
							|  |  |  | 			le32_to_cpu(tr.r6), le32_to_cpu(tr.r7)); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 	return simple_read_from_buffer(data, count, &pos, buf, res); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdio_assert_info(struct brcmf_sdio *bus, | 
					
						
							|  |  |  | 				  struct sdpcm_shared *sh, char __user *data, | 
					
						
							|  |  |  | 				  size_t count) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 	char buf[200]; | 
					
						
							|  |  |  | 	char file[80] = "?"; | 
					
						
							|  |  |  | 	char expr[80] = "<???>"; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 	loff_t pos = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((sh->flags & SDPCM_SHARED_ASSERT_BUILT) == 0) { | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "firmware not built with -assert\n"); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} else if ((sh->flags & SDPCM_SHARED_ASSERT) == 0) { | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "no assert in dongle\n"); | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (sh->assert_file_addr != 0) { | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 		error = brcmf_sdio_ramrw(bus->sdiodev, false, | 
					
						
							|  |  |  | 					 sh->assert_file_addr, (u8 *)file, 80); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 		if (error < 0) | 
					
						
							|  |  |  | 			return error; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	if (sh->assert_exp_addr != 0) { | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:47 +02:00
										 |  |  | 		error = brcmf_sdio_ramrw(bus->sdiodev, false, | 
					
						
							|  |  |  | 					 sh->assert_exp_addr, (u8 *)expr, 80); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 		if (error < 0) | 
					
						
							|  |  |  | 			return error; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	res = scnprintf(buf, sizeof(buf), | 
					
						
							|  |  |  | 			"dongle assert: %s:%d: assert(%s)\n", | 
					
						
							|  |  |  | 			file, sh->assert_line, expr); | 
					
						
							|  |  |  | 	return simple_read_from_buffer(data, count, &pos, buf, res); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error; | 
					
						
							|  |  |  | 	struct sdpcm_shared sh; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = brcmf_sdio_readshared(bus, &sh); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		return error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if ((sh.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) | 
					
						
							|  |  |  | 		brcmf_dbg(INFO, "firmware not built with -assert\n"); | 
					
						
							|  |  |  | 	else if (sh.flags & SDPCM_SHARED_ASSERT) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("assertion in dongle\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (sh.flags & SDPCM_SHARED_TRAP) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("firmware trap in dongle\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int brcmf_sdbrcm_died_dump(struct brcmf_sdio *bus, char __user *data, | 
					
						
							|  |  |  | 				  size_t count, loff_t *ppos) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int error = 0; | 
					
						
							|  |  |  | 	struct sdpcm_shared sh; | 
					
						
							|  |  |  | 	int nbytes = 0; | 
					
						
							|  |  |  | 	loff_t pos = *ppos; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (pos != 0) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = brcmf_sdio_readshared(bus, &sh); | 
					
						
							|  |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = brcmf_sdio_assert_info(bus, &sh, data, count); | 
					
						
							|  |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	nbytes = error; | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	error = brcmf_sdio_trap_info(bus, &sh, data+nbytes, count); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 	nbytes += error; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	error = brcmf_sdio_dump_console(bus, &sh, data+nbytes, count); | 
					
						
							|  |  |  | 	if (error < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 	nbytes += error; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-05 10:57:42 +02:00
										 |  |  | 	error = nbytes; | 
					
						
							|  |  |  | 	*ppos += nbytes; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | done: | 
					
						
							|  |  |  | 	return error; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static ssize_t brcmf_sdio_forensic_read(struct file *f, char __user *data, | 
					
						
							|  |  |  | 					size_t count, loff_t *ppos) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_sdio *bus = f->private_data; | 
					
						
							|  |  |  | 	int res; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	res = brcmf_sdbrcm_died_dump(bus, data, count, ppos); | 
					
						
							|  |  |  | 	if (res > 0) | 
					
						
							|  |  |  | 		*ppos += res; | 
					
						
							|  |  |  | 	return (ssize_t)res; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static const struct file_operations brcmf_sdio_forensic_ops = { | 
					
						
							|  |  |  | 	.owner = THIS_MODULE, | 
					
						
							|  |  |  | 	.open = simple_open, | 
					
						
							|  |  |  | 	.read = brcmf_sdio_forensic_read | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_pub *drvr = bus->sdiodev->bus_if->drvr; | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	struct dentry *dentry = brcmf_debugfs_get_devdir(drvr); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 	if (IS_ERR_OR_NULL(dentry)) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	debugfs_create_file("forensics", S_IRUGO, dentry, bus, | 
					
						
							|  |  |  | 			    &brcmf_sdio_forensic_ops); | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	brcmf_debugfs_create_sdio_count(drvr, &bus->sdcnt); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #else
 | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | static int brcmf_sdbrcm_checkdied(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | static void brcmf_sdio_debugfs_create(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | #endif /* DEBUG */
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:16 -08:00
										 |  |  | static int | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:54 -08:00
										 |  |  | brcmf_sdbrcm_bus_rxctl(struct device *dev, unsigned char *msg, uint msglen) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int timeleft; | 
					
						
							|  |  |  | 	uint rxlen = 0; | 
					
						
							|  |  |  | 	bool pending; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	u8 *buf; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:54 -08:00
										 |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:02 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:54 -08:00
										 |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Wait until control frame is available */ | 
					
						
							|  |  |  | 	timeleft = brcmf_sdbrcm_dcmd_resp_wait(bus, &bus->rxlen, &pending); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_lock_bh(&bus->rxctl_lock); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	rxlen = bus->rxlen; | 
					
						
							|  |  |  | 	memcpy(msg, bus->rxctl, min(msglen, rxlen)); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	bus->rxctl = NULL; | 
					
						
							|  |  |  | 	buf = bus->rxctl_orig; | 
					
						
							|  |  |  | 	bus->rxctl_orig = NULL; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bus->rxlen = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_unlock_bh(&bus->rxctl_lock); | 
					
						
							|  |  |  | 	vfree(buf); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (rxlen) { | 
					
						
							|  |  |  | 		brcmf_dbg(CTL, "resumed on rxctl frame, got %d expected %d\n", | 
					
						
							|  |  |  | 			  rxlen, msglen); | 
					
						
							|  |  |  | 	} else if (timeleft == 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("resumed on timeout\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 		brcmf_sdbrcm_checkdied(bus); | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 	} else if (pending) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_dbg(CTL, "cancelled\n"); | 
					
						
							|  |  |  | 		return -ERESTARTSYS; | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		brcmf_dbg(CTL, "resumed for unknown reason?\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 		brcmf_sdbrcm_checkdied(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (rxlen) | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rx_ctlpkts++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	else | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.rx_ctlerrs++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return rxlen ? (int)rxlen : -ETIMEDOUT; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | static bool brcmf_sdbrcm_download_state(struct brcmf_sdio *bus, bool enter) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:42 +01:00
										 |  |  | 	struct chip_info *ci = bus->ci; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* To enter download state, disable ARM and reset SOCRAM.
 | 
					
						
							|  |  |  | 	 * To exit download state, simply reset ARM (default is RAM boot). | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 	if (enter) { | 
					
						
							|  |  |  | 		bus->alp_only = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | 		brcmf_sdio_chip_enter_download(bus->sdiodev, ci); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | 		if (!brcmf_sdio_chip_exit_download(bus->sdiodev, ci, bus->vars, | 
					
						
							|  |  |  | 						   bus->varsz)) | 
					
						
							|  |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 		/* Allow HT Clock now that the ARM is running. */ | 
					
						
							|  |  |  | 		bus->alp_only = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 		bus->sdiodev->bus_if->state = BRCMF_BUS_LOAD; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_download_code_file(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	const struct firmware *fw; | 
					
						
							|  |  |  | 	int err; | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:51 +02:00
										 |  |  | 	int offset; | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	int address; | 
					
						
							|  |  |  | 	int len; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	fw = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_BIN); | 
					
						
							|  |  |  | 	if (fw == NULL) | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_ARM_CR4) != | 
					
						
							|  |  |  | 	    BRCMF_MAX_CORENUM) | 
					
						
							|  |  |  | 		memcpy(&bus->ci->rst_vec, fw->data, sizeof(bus->ci->rst_vec)); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	err = 0; | 
					
						
							|  |  |  | 	offset = 0; | 
					
						
							|  |  |  | 	address = bus->ci->rambase; | 
					
						
							|  |  |  | 	while (offset < fw->size) { | 
					
						
							|  |  |  | 		len = ((offset + MEMBLOCK) < fw->size) ? MEMBLOCK : | 
					
						
							|  |  |  | 		      fw->size - offset; | 
					
						
							|  |  |  | 		err = brcmf_sdio_ramrw(bus->sdiodev, true, address, | 
					
						
							|  |  |  | 				       (u8 *)&fw->data[offset], len); | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("error %d on writing %d membytes at 0x%08x\n", | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 				  err, len, address); | 
					
						
							|  |  |  | 			goto failure; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 		offset += len; | 
					
						
							|  |  |  | 		address += len; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | failure: | 
					
						
							|  |  |  | 	release_firmware(fw); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	return err; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /*
 | 
					
						
							|  |  |  |  * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file | 
					
						
							|  |  |  |  * and ending in a NUL. | 
					
						
							|  |  |  |  * Removes carriage returns, empty lines, comment lines, and converts | 
					
						
							|  |  |  |  * newlines to NULs. | 
					
						
							|  |  |  |  * Shortens buffer as needed and pads with NULs.  End of buffer is marked | 
					
						
							|  |  |  |  * by two NULs. | 
					
						
							|  |  |  | */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | static int brcmf_process_nvram_vars(struct brcmf_sdio *bus, | 
					
						
							|  |  |  | 				    const struct firmware *nv) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:37 +02:00
										 |  |  | 	char *varbuf; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	char *dp; | 
					
						
							|  |  |  | 	bool findNewline; | 
					
						
							|  |  |  | 	int column; | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:37 +02:00
										 |  |  | 	int ret = 0; | 
					
						
							|  |  |  | 	uint buf_len, n, len; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	len = nv->size; | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:37 +02:00
										 |  |  | 	varbuf = vmalloc(len); | 
					
						
							|  |  |  | 	if (!varbuf) | 
					
						
							|  |  |  | 		return -ENOMEM; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	memcpy(varbuf, nv->data, len); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	dp = varbuf; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	findNewline = false; | 
					
						
							|  |  |  | 	column = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for (n = 0; n < len; n++) { | 
					
						
							|  |  |  | 		if (varbuf[n] == 0) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (varbuf[n] == '\r') | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (findNewline && varbuf[n] != '\n') | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		findNewline = false; | 
					
						
							|  |  |  | 		if (varbuf[n] == '#') { | 
					
						
							|  |  |  | 			findNewline = true; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		if (varbuf[n] == '\n') { | 
					
						
							|  |  |  | 			if (column == 0) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 			*dp++ = 0; | 
					
						
							|  |  |  | 			column = 0; | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		*dp++ = varbuf[n]; | 
					
						
							|  |  |  | 		column++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	buf_len = dp - varbuf; | 
					
						
							|  |  |  | 	while (dp < varbuf + n) | 
					
						
							|  |  |  | 		*dp++ = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:37 +02:00
										 |  |  | 	kfree(bus->vars); | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:38 +02:00
										 |  |  | 	/* roundup needed for download to device */ | 
					
						
							|  |  |  | 	bus->varsz = roundup(buf_len + 1, 4); | 
					
						
							| 
									
										
										
										
											2012-06-26 21:26:37 +02:00
										 |  |  | 	bus->vars = kmalloc(bus->varsz, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (bus->vars == NULL) { | 
					
						
							|  |  |  | 		bus->varsz = 0; | 
					
						
							|  |  |  | 		ret = -ENOMEM; | 
					
						
							|  |  |  | 		goto err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* copy the processed variables and add null termination */ | 
					
						
							|  |  |  | 	memcpy(bus->vars, varbuf, buf_len); | 
					
						
							|  |  |  | 	bus->vars[buf_len] = 0; | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	vfree(varbuf); | 
					
						
							|  |  |  | 	return ret; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int brcmf_sdbrcm_download_nvram(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	const struct firmware *nv; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	int ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	nv = brcmf_sdbrcm_get_fw(bus, BRCMF_FIRMWARE_NVRAM); | 
					
						
							|  |  |  | 	if (nv == NULL) | 
					
						
							|  |  |  | 		return -ENOENT; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	ret = brcmf_process_nvram_vars(bus, nv); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-09-25 13:05:45 +02:00
										 |  |  | 	release_firmware(nv); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static int _brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int bcmerror = -1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Keep arm in reset */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | 	if (!brcmf_sdbrcm_download_state(bus, true)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("error placing ARM core in reset\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (brcmf_sdbrcm_download_code_file(bus)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("dongle image file download failed\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-02 21:06:20 +02:00
										 |  |  | 	if (brcmf_sdbrcm_download_nvram(bus)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("dongle nvram file download failed\n"); | 
					
						
							| 
									
										
										
										
											2013-04-02 21:06:20 +02:00
										 |  |  | 		goto err; | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Take arm out of reset */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:48 +02:00
										 |  |  | 	if (!brcmf_sdbrcm_download_state(bus, false)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("error getting out of ARM core reset\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bcmerror = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | err: | 
					
						
							|  |  |  | 	return bcmerror; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | static bool brcmf_sdbrcm_sr_capable(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u32 addr, reg; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* old chips with PMU version less than 17 don't support save restore */ | 
					
						
							|  |  |  | 	if (bus->ci->pmurev < 17) | 
					
						
							|  |  |  | 		return false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* read PMU chipcontrol register 3*/ | 
					
						
							|  |  |  | 	addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_addr); | 
					
						
							|  |  |  | 	brcmf_sdio_regwl(bus->sdiodev, addr, 3, NULL); | 
					
						
							|  |  |  | 	addr = CORE_CC_REG(bus->ci->c_inf[0].base, chipcontrol_data); | 
					
						
							|  |  |  | 	reg = brcmf_sdio_regrl(bus->sdiodev, addr, NULL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return (bool)reg; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void brcmf_sdbrcm_sr_init(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 	u8 val; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, | 
					
						
							|  |  |  | 			       &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("error reading SBSDIO_FUNC1_WAKEUPCTRL\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val |= 1 << SBSDIO_FUNC1_WCTRL_HTWAIT_SHIFT; | 
					
						
							|  |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_WAKEUPCTRL, | 
					
						
							|  |  |  | 			 val, &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("error writing SBSDIO_FUNC1_WAKEUPCTRL\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Add CMD14 Support */ | 
					
						
							|  |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SDIO_CCCR_BRCM_CARDCAP, | 
					
						
							|  |  |  | 			 (SDIO_CCCR_BRCM_CARDCAP_CMD14_SUPPORT | | 
					
						
							|  |  |  | 			  SDIO_CCCR_BRCM_CARDCAP_CMD14_EXT), | 
					
						
							|  |  |  | 			 &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("error writing SDIO_CCCR_BRCM_CARDCAP\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 			 SBSDIO_FORCE_HT, &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("error writing SBSDIO_FUNC1_CHIPCLKCSR\n"); | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set flag */ | 
					
						
							|  |  |  | 	bus->sr_enabled = true; | 
					
						
							|  |  |  | 	brcmf_dbg(INFO, "SR enabled\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* enable KSO bit */ | 
					
						
							|  |  |  | static int brcmf_sdbrcm_kso_init(struct brcmf_sdio *bus) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	u8 val; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* KSO bit added in SDIO core rev 12 */ | 
					
						
							|  |  |  | 	if (bus->ci->c_inf[1].rev < 12) | 
					
						
							|  |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	val = brcmf_sdio_regrb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, | 
					
						
							|  |  |  | 			       &err); | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							|  |  |  | 		brcmf_err("error reading SBSDIO_FUNC1_SLEEPCSR\n"); | 
					
						
							|  |  |  | 		return err; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(val & SBSDIO_FUNC1_SLEEPCSR_KSO_MASK)) { | 
					
						
							|  |  |  | 		val |= (SBSDIO_FUNC1_SLEEPCSR_KSO_EN << | 
					
						
							|  |  |  | 			SBSDIO_FUNC1_SLEEPCSR_KSO_SHIFT); | 
					
						
							|  |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_SLEEPCSR, | 
					
						
							|  |  |  | 				 val, &err); | 
					
						
							|  |  |  | 		if (err) { | 
					
						
							|  |  |  | 			brcmf_err("error writing SBSDIO_FUNC1_SLEEPCSR\n"); | 
					
						
							|  |  |  | 			return err; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | static bool | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | brcmf_sdbrcm_download_firmware(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	bool ret; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	ret = _brcmf_sdbrcm_download_firmware(bus) == 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:15 +01:00
										 |  |  | static int brcmf_sdbrcm_bus_preinit(struct device *dev) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							|  |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							|  |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	uint pad_size; | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:15 +01:00
										 |  |  | 	u32 value; | 
					
						
							|  |  |  | 	u8 idx; | 
					
						
							|  |  |  | 	int err; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 	/* the commands below use the terms tx and rx from
 | 
					
						
							|  |  |  | 	 * a device perspective, ie. bus:txglom affects the | 
					
						
							|  |  |  | 	 * bus transfers from device to host. | 
					
						
							|  |  |  | 	 */ | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:15 +01:00
										 |  |  | 	idx = brcmf_sdio_chip_getinfidx(bus->ci, BCMA_CORE_SDIO_DEV); | 
					
						
							|  |  |  | 	if (bus->ci->c_inf[idx].rev < 12) { | 
					
						
							|  |  |  | 		/* for sdio core rev < 12, disable txgloming */ | 
					
						
							|  |  |  | 		value = 0; | 
					
						
							|  |  |  | 		err = brcmf_iovar_data_set(dev, "bus:txglom", &value, | 
					
						
							|  |  |  | 					   sizeof(u32)); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* otherwise, set txglomalign */ | 
					
						
							|  |  |  | 		value = 4; | 
					
						
							|  |  |  | 		if (sdiodev->pdata) | 
					
						
							|  |  |  | 			value = sdiodev->pdata->sd_sgentry_align; | 
					
						
							|  |  |  | 		/* SDIO ADMA requires at least 32 bit alignment */ | 
					
						
							|  |  |  | 		value = max_t(u32, value, 4); | 
					
						
							|  |  |  | 		err = brcmf_iovar_data_set(dev, "bus:txglomalign", &value, | 
					
						
							|  |  |  | 					   sizeof(u32)); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (err < 0) | 
					
						
							|  |  |  | 		goto done; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; | 
					
						
							|  |  |  | 	if (sdiodev->sg_support) { | 
					
						
							|  |  |  | 		bus->txglom = false; | 
					
						
							|  |  |  | 		value = 1; | 
					
						
							|  |  |  | 		pad_size = bus->sdiodev->func[2]->cur_blksize << 1; | 
					
						
							|  |  |  | 		bus->txglom_sgpad = brcmu_pkt_buf_get_skb(pad_size); | 
					
						
							|  |  |  | 		if (!bus->txglom_sgpad) | 
					
						
							|  |  |  | 			brcmf_err("allocating txglom padding skb failed, reduced performance\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		err = brcmf_iovar_data_set(bus->sdiodev->dev, "bus:rxglom", | 
					
						
							|  |  |  | 					   &value, sizeof(u32)); | 
					
						
							|  |  |  | 		if (err < 0) { | 
					
						
							|  |  |  | 			/* bus:rxglom is allowed to fail */ | 
					
						
							|  |  |  | 			err = 0; | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			bus->txglom = true; | 
					
						
							|  |  |  | 			bus->tx_hdrlen += SDPCM_HWEXT_LEN; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	brcmf_bus_add_txhdrlen(bus->sdiodev->dev, bus->tx_hdrlen); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | done: | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:15 +01:00
										 |  |  | 	return err; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:14 -08:00
										 |  |  | static int brcmf_sdbrcm_bus_init(struct device *dev) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:57 -08:00
										 |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(dev); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:02 +01:00
										 |  |  | 	struct brcmf_sdio_dev *sdiodev = bus_if->bus_priv.sdio; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:57 -08:00
										 |  |  | 	struct brcmf_sdio *bus = sdiodev->bus; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	int err, ret = 0; | 
					
						
							|  |  |  | 	u8 saveclk; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* try to download image and nvram to the dongle */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:57 -08:00
										 |  |  | 	if (bus_if->state == BRCMF_BUS_DOWN) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (!(brcmf_sdbrcm_download_firmware(bus))) | 
					
						
							|  |  |  | 			return -1; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	if (!bus->sdiodev->bus_if->drvr) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		return 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Start the watchdog timer */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.tickcnt = 0; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Make sure backplane clock is on, needed to generate F2 interrupt */ | 
					
						
							|  |  |  | 	brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); | 
					
						
							|  |  |  | 	if (bus->clkstate != CLK_AVAIL) | 
					
						
							|  |  |  | 		goto exit; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Force clocks on backplane to be sure F2 interrupt propagates */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 	saveclk = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 				   SBSDIO_FUNC1_CHIPCLKCSR, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (!err) { | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 				 (saveclk | SBSDIO_FORCE_HT), &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	if (err) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("Failed to force clock for F2: err %d\n", err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto exit; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Enable function 2 (frame transfers) */ | 
					
						
							|  |  |  | 	w_sdreg32(bus, SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 		  offsetof(struct sdpcmd_regs, tosbmailboxdata)); | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 	err = sdio_enable_func(bus->sdiodev->func[SDIO_FUNC_2]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 	brcmf_dbg(INFO, "enable F2: err=%d\n", err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* If F2 successfully enabled, set core and enable interrupts */ | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 	if (!err) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		/* Set up the interrupt mask and enable interrupts */ | 
					
						
							|  |  |  | 		bus->hostintmask = HOSTINTMASK; | 
					
						
							|  |  |  | 		w_sdreg32(bus, bus->hostintmask, | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:36 -07:00
										 |  |  | 			  offsetof(struct sdpcmd_regs, hostintmask)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_WATERMARK, 8, &err); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:04 +01:00
										 |  |  | 	} else { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		/* Disable F2 again */ | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 		sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); | 
					
						
							| 
									
										
										
										
											2012-02-09 21:09:04 +01:00
										 |  |  | 		ret = -ENODEV; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	if (brcmf_sdbrcm_sr_capable(bus)) { | 
					
						
							|  |  |  | 		brcmf_sdbrcm_sr_init(bus); | 
					
						
							|  |  |  | 	} else { | 
					
						
							|  |  |  | 		/* Restore previous clock setting */ | 
					
						
							|  |  |  | 		brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 				 saveclk, &err); | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:58 -07:00
										 |  |  | 	if (ret == 0) { | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:59 -07:00
										 |  |  | 		ret = brcmf_sdio_intr_register(bus->sdiodev); | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:58 -07:00
										 |  |  | 		if (ret != 0) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("intr register failed:%d\n", ret); | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:58 -07:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* If we didn't come up, turn off backplane clock */ | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:57 -07:00
										 |  |  | 	if (bus_if->state != BRCMF_BUS_DATA) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | exit: | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	return ret; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void brcmf_sdbrcm_isr(void *arg) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	struct brcmf_sdio *bus = (struct brcmf_sdio *) arg; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!bus) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("bus is null pointer, exiting\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("bus is down. we have nothing to do\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Count the interrupt call */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	bus->sdcnt.intrcount++; | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 	if (in_interrupt()) | 
					
						
							|  |  |  | 		atomic_set(&bus->ipend, 1); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		if (brcmf_sdio_intr_rstatus(bus)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 			brcmf_err("failed backplane access\n"); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:03 +02:00
										 |  |  | 			bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							|  |  |  | 		} | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Disable additional interrupts (is this needed now)? */ | 
					
						
							|  |  |  | 	if (!bus->intr) | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("isr w/o interrupt configured!\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	atomic_inc(&bus->dpc_tskcnt); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 	queue_work(bus->brcmf_wq, &bus->datawork); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:53 -08:00
										 |  |  | static bool brcmf_sdbrcm_bus_watchdog(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:53 -08:00
										 |  |  | 	struct brcmf_bus *bus_if = dev_get_drvdata(bus->sdiodev->dev); | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif	/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TIMER, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Poll period: check device if appropriate. */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	if (!bus->sr_enabled && | 
					
						
							|  |  |  | 	    bus->poll && (++bus->polltick >= bus->pollrate)) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		u32 intstatus = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Reset poll tick */ | 
					
						
							|  |  |  | 		bus->polltick = 0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Check device if no interrupts */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		if (!bus->intr || | 
					
						
							|  |  |  | 		    (bus->sdcnt.intrcount == bus->sdcnt.lastintrs)) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 			if (atomic_read(&bus->dpc_tskcnt) == 0) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				u8 devpend; | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 				devpend = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 							   SDIO_CCCR_INTx, | 
					
						
							|  |  |  | 							   NULL); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				intstatus = | 
					
						
							|  |  |  | 				    devpend & (INTR_STATUS_FUNC1 | | 
					
						
							|  |  |  | 					       INTR_STATUS_FUNC2); | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* If there is something, make like the ISR and
 | 
					
						
							|  |  |  | 				 schedule the DPC */ | 
					
						
							|  |  |  | 			if (intstatus) { | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 				bus->sdcnt.pollcnt++; | 
					
						
							| 
									
										
										
										
											2012-09-13 21:11:59 +02:00
										 |  |  | 				atomic_set(&bus->ipend, 1); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 				atomic_inc(&bus->dpc_tskcnt); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 				queue_work(bus->brcmf_wq, &bus->datawork); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		/* Update interrupt tracking */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 		bus->sdcnt.lastintrs = bus->sdcnt.intrcount; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #ifdef DEBUG
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* Poll for console output periodically */ | 
					
						
							| 
									
										
										
										
											2012-11-14 18:46:19 -08:00
										 |  |  | 	if (bus_if && bus_if->state == BRCMF_BUS_DATA && | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:52 -08:00
										 |  |  | 	    bus->console_interval != 0) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->console.count += BRCMF_WD_POLL_MS; | 
					
						
							|  |  |  | 		if (bus->console.count >= bus->console_interval) { | 
					
						
							|  |  |  | 			bus->console.count -= bus->console_interval; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			/* Make sure backplane clock is on */ | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 			brcmf_sdbrcm_bus_sleep(bus, false, false); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			if (brcmf_sdbrcm_readconsole(bus) < 0) | 
					
						
							|  |  |  | 				/* stop on error */ | 
					
						
							|  |  |  | 				bus->console_interval = 0; | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 			sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:38 -08:00
										 |  |  | #endif				/* DEBUG */
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* On idle timeout clear activity flag and/or turn off clock */ | 
					
						
							|  |  |  | 	if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) { | 
					
						
							|  |  |  | 		if (++bus->idlecount >= bus->idletime) { | 
					
						
							|  |  |  | 			bus->idlecount = 0; | 
					
						
							|  |  |  | 			if (bus->activity) { | 
					
						
							|  |  |  | 				bus->activity = false; | 
					
						
							|  |  |  | 				brcmf_sdbrcm_wd_timer(bus, BRCMF_WD_POLL_MS); | 
					
						
							|  |  |  | 			} else { | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 				brcmf_dbg(SDIO, "idle\n"); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 				brcmf_sdbrcm_bus_sleep(bus, true, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 				sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:11:59 +02:00
										 |  |  | 	return (atomic_read(&bus->ipend) > 0); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | static void brcmf_sdio_dataworker(struct work_struct *work) | 
					
						
							|  |  |  | { | 
					
						
							|  |  |  | 	struct brcmf_sdio *bus = container_of(work, struct brcmf_sdio, | 
					
						
							|  |  |  | 					      datawork); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	while (atomic_read(&bus->dpc_tskcnt)) { | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 		brcmf_sdbrcm_dpc(bus); | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 		atomic_dec(&bus->dpc_tskcnt); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_sdbrcm_release_malloc(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	kfree(bus->rxbuf); | 
					
						
							|  |  |  | 	bus->rxctl = bus->rxbuf = NULL; | 
					
						
							|  |  |  | 	bus->rxlen = 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static bool brcmf_sdbrcm_probe_malloc(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 	if (bus->sdiodev->bus_if->maxctl) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->rxblen = | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:03 -08:00
										 |  |  | 		    roundup((bus->sdiodev->bus_if->maxctl + SDPCM_HDRLEN), | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 			    ALIGNMENT) + bus->head_align; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC); | 
					
						
							|  |  |  | 		if (!(bus->rxbuf)) | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:29 +02:00
										 |  |  | 			return false; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static bool | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | brcmf_sdbrcm_probe_attach(struct brcmf_sdio *bus, u32 regsva) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	u8 clkctl = 0; | 
					
						
							|  |  |  | 	int err = 0; | 
					
						
							|  |  |  | 	int reg_addr; | 
					
						
							|  |  |  | 	u32 reg_val; | 
					
						
							| 
									
										
										
										
											2013-04-12 10:55:55 +02:00
										 |  |  | 	u32 drivestrength; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bus->alp_only = true; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:42 -08:00
										 |  |  | 	pr_debug("F1 signature read @0x18000000=0x%4x\n", | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:34 -07:00
										 |  |  | 		 brcmf_sdio_regrl(bus->sdiodev, SI_ENUM_BASE, NULL)); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/*
 | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:35 +01:00
										 |  |  | 	 * Force PLL off until brcmf_sdio_chip_attach() | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	 * programs PLL control regs | 
					
						
							|  |  |  | 	 */ | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, | 
					
						
							|  |  |  | 			 BRCMF_INIT_CLKCTL1, &err); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (!err) | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:32 -07:00
										 |  |  | 		clkctl = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 					  SBSDIO_FUNC1_CHIPCLKCSR, &err); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (err || ((clkctl & ~SBSDIO_AVBITS) != BRCMF_INIT_CLKCTL1)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("ChipClkCSR access: err %d wrote 0x%02x read 0x%02x\n", | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			  err, BRCMF_INIT_CLKCTL1, clkctl); | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:35 +01:00
										 |  |  | 	if (brcmf_sdio_chip_attach(bus->sdiodev, &bus->ci, regsva)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("brcmf_sdio_chip_attach failed!\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	if (brcmf_sdbrcm_kso_init(bus)) { | 
					
						
							|  |  |  | 		brcmf_err("error enabling KSO\n"); | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-12 10:55:55 +02:00
										 |  |  | 	if ((bus->sdiodev->pdata) && (bus->sdiodev->pdata->drive_strength)) | 
					
						
							|  |  |  | 		drivestrength = bus->sdiodev->pdata->drive_strength; | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		drivestrength = DEFAULT_SDIO_DRIVE_STRENGTH; | 
					
						
							|  |  |  | 	brcmf_sdio_chip_drivestrengthinit(bus->sdiodev, bus->ci, drivestrength); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:37 +01:00
										 |  |  | 	/* Get info on the SOCRAM cores... */ | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bus->ramsize = bus->ci->ramsize; | 
					
						
							|  |  |  | 	if (!(bus->ramsize)) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("failed to find SOCRAM memory!\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:52 +02:00
										 |  |  | 	/* Set card control so an SDIO card reset does a WLAN backplane reset */ | 
					
						
							|  |  |  | 	reg_val = brcmf_sdio_regrb(bus->sdiodev, | 
					
						
							|  |  |  | 				   SDIO_CCCR_BRCM_CARDCTRL, &err); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reg_val |= SDIO_CCCR_BRCM_CARDCTRL_WLANRESET; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdio_regwb(bus->sdiodev, | 
					
						
							|  |  |  | 			 SDIO_CCCR_BRCM_CARDCTRL, reg_val, &err); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* set PMUControl so a backplane reset does PMU state reload */ | 
					
						
							|  |  |  | 	reg_addr = CORE_CC_REG(bus->ci->c_inf[0].base, | 
					
						
							|  |  |  | 			       pmucontrol); | 
					
						
							|  |  |  | 	reg_val = brcmf_sdio_regrl(bus->sdiodev, | 
					
						
							|  |  |  | 				   reg_addr, | 
					
						
							|  |  |  | 				   &err); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	reg_val |= (BCMA_CC_PMU_CTL_RES_RELOAD << BCMA_CC_PMU_CTL_RES_SHIFT); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_sdio_regwl(bus->sdiodev, | 
					
						
							|  |  |  | 			 reg_addr, | 
					
						
							|  |  |  | 			 reg_val, | 
					
						
							|  |  |  | 			 &err); | 
					
						
							|  |  |  | 	if (err) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 	/* allocate header buffer */ | 
					
						
							|  |  |  | 	bus->hdrbuf = kzalloc(MAX_HDR_READ + bus->head_align, GFP_KERNEL); | 
					
						
							|  |  |  | 	if (!bus->hdrbuf) | 
					
						
							|  |  |  | 		return false; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* Locate an appropriately-aligned portion of hdrbuf */ | 
					
						
							|  |  |  | 	bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 				    bus->head_align); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	/* Set the poll and/or interrupt flags */ | 
					
						
							|  |  |  | 	bus->intr = true; | 
					
						
							|  |  |  | 	bus->poll = false; | 
					
						
							|  |  |  | 	if (bus->poll) | 
					
						
							|  |  |  | 		bus->pollrate = 1; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | fail: | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	return false; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static bool brcmf_sdbrcm_probe_init(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* Disable F2 to clear any intermediate frame state on the dongle */ | 
					
						
							| 
									
										
										
										
											2013-12-12 11:58:53 +01:00
										 |  |  | 	sdio_disable_func(bus->sdiodev->func[SDIO_FUNC_2]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	bus->sdiodev->bus_if->state = BRCMF_BUS_DOWN; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bus->rxflow = false; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Done with backplane-dependent accesses, can drop clock... */ | 
					
						
							| 
									
										
										
										
											2012-05-04 18:27:33 -07:00
										 |  |  | 	brcmf_sdio_regwb(bus->sdiodev, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 	sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* ...and initialize clock/power states */ | 
					
						
							|  |  |  | 	bus->clkstate = CLK_SDONLY; | 
					
						
							|  |  |  | 	bus->idletime = BRCMF_IDLE_INTERVAL; | 
					
						
							|  |  |  | 	bus->idleclock = BRCMF_IDLE_ACTIVE; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Query the F2 block size, set roundup accordingly */ | 
					
						
							|  |  |  | 	bus->blocksize = bus->sdiodev->func[2]->cur_blksize; | 
					
						
							|  |  |  | 	bus->roundup = min(max_roundup, bus->blocksize); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-04-11 13:28:46 +02:00
										 |  |  | 	/* SR state */ | 
					
						
							|  |  |  | 	bus->sleeping = false; | 
					
						
							|  |  |  | 	bus->sr_enabled = false; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	return true; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static int | 
					
						
							|  |  |  | brcmf_sdbrcm_watchdog_thread(void *data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	struct brcmf_sdio *bus = (struct brcmf_sdio *)data; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	allow_signal(SIGTERM); | 
					
						
							|  |  |  | 	/* Run until signal received */ | 
					
						
							|  |  |  | 	while (1) { | 
					
						
							|  |  |  | 		if (kthread_should_stop()) | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 		if (!wait_for_completion_interruptible(&bus->watchdog_wait)) { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:53 -08:00
										 |  |  | 			brcmf_sdbrcm_bus_watchdog(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			/* Count the tick for reference */ | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 			bus->sdcnt.tickcnt++; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		} else | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return 0; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | static void | 
					
						
							|  |  |  | brcmf_sdbrcm_watchdog(unsigned long data) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	struct brcmf_sdio *bus = (struct brcmf_sdio *)data; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	if (bus->watchdog_tsk) { | 
					
						
							|  |  |  | 		complete(&bus->watchdog_wait); | 
					
						
							|  |  |  | 		/* Reschedule the watchdog */ | 
					
						
							|  |  |  | 		if (bus->wd_timer_valid) | 
					
						
							|  |  |  | 			mod_timer(&bus->timer, | 
					
						
							|  |  |  | 				  jiffies + BRCMF_WD_POLL_MS * HZ / 1000); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_sdbrcm_release_dongle(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (bus->ci) { | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_claim_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false); | 
					
						
							|  |  |  | 		brcmf_sdbrcm_clkctl(bus, CLK_NONE, false); | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:24 -08:00
										 |  |  | 		sdio_release_host(bus->sdiodev->func[1]); | 
					
						
							| 
									
										
										
										
											2011-11-04 22:23:39 +01:00
										 |  |  | 		brcmf_sdio_chip_detach(&bus->ci); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		if (bus->vars && bus->varsz) | 
					
						
							|  |  |  | 			kfree(bus->vars); | 
					
						
							|  |  |  | 		bus->vars = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Disconnected\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | /* Detach and free everything */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | static void brcmf_sdbrcm_release(struct brcmf_sdio *bus) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							| 
									
										
										
										
											2012-06-14 12:16:57 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (bus) { | 
					
						
							|  |  |  | 		/* De-register interrupt handler */ | 
					
						
							| 
									
										
										
										
											2012-04-27 18:56:59 -07:00
										 |  |  | 		brcmf_sdio_intr_unregister(bus->sdiodev); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 		cancel_work_sync(&bus->datawork); | 
					
						
							| 
									
										
										
										
											2012-11-14 18:46:18 -08:00
										 |  |  | 		if (bus->brcmf_wq) | 
					
						
							|  |  |  | 			destroy_workqueue(bus->brcmf_wq); | 
					
						
							| 
									
										
										
										
											2012-09-13 21:12:00 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:36:56 -08:00
										 |  |  | 		if (bus->sdiodev->bus_if->drvr) { | 
					
						
							|  |  |  | 			brcmf_detach(bus->sdiodev->dev); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 			brcmf_sdbrcm_release_dongle(bus); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:10 +01:00
										 |  |  | 		brcmu_pkt_buf_free_skb(bus->txglom_sgpad); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		brcmf_sdbrcm_release_malloc(bus); | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:14 +01:00
										 |  |  | 		kfree(bus->hdrbuf); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		kfree(bus); | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Disconnected\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-12-05 15:25:54 +01:00
										 |  |  | static struct brcmf_bus_ops brcmf_sdio_bus_ops = { | 
					
						
							|  |  |  | 	.stop = brcmf_sdbrcm_bus_stop, | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:15 +01:00
										 |  |  | 	.preinit = brcmf_sdbrcm_bus_preinit, | 
					
						
							| 
									
										
										
										
											2012-12-05 15:25:54 +01:00
										 |  |  | 	.init = brcmf_sdbrcm_bus_init, | 
					
						
							|  |  |  | 	.txdata = brcmf_sdbrcm_bus_txdata, | 
					
						
							|  |  |  | 	.txctl = brcmf_sdbrcm_bus_txctl, | 
					
						
							|  |  |  | 	.rxctl = brcmf_sdbrcm_bus_rxctl, | 
					
						
							| 
									
										
										
										
											2013-04-03 12:40:38 +02:00
										 |  |  | 	.gettxq = brcmf_sdbrcm_bus_gettxq, | 
					
						
							| 
									
										
										
										
											2012-12-05 15:25:54 +01:00
										 |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:49 -08:00
										 |  |  | void *brcmf_sdbrcm_probe(u32 regsva, struct brcmf_sdio_dev *sdiodev) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	int ret; | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	struct brcmf_sdio *bus; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* We make an assumption about address window mappings:
 | 
					
						
							|  |  |  | 	 * regsva == SI_ENUM_BASE*/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Allocate private bus interface state */ | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	bus = kzalloc(sizeof(struct brcmf_sdio), GFP_ATOMIC); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (!bus) | 
					
						
							|  |  |  | 		goto fail; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	bus->sdiodev = sdiodev; | 
					
						
							|  |  |  | 	sdiodev->bus = bus; | 
					
						
							| 
									
										
										
										
											2011-10-19 12:51:09 +02:00
										 |  |  | 	skb_queue_head_init(&bus->glom); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	bus->txbound = BRCMF_TXBOUND; | 
					
						
							|  |  |  | 	bus->rxbound = BRCMF_RXBOUND; | 
					
						
							|  |  |  | 	bus->txminmax = BRCMF_TXMINMAX; | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:28 +02:00
										 |  |  | 	bus->tx_seq = SDPCM_SEQ_WRAP - 1; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-11-29 12:25:13 +01:00
										 |  |  | 	/* platform specific configuration:
 | 
					
						
							|  |  |  | 	 *   alignments must be at least 4 bytes for ADMA | 
					
						
							|  |  |  |          */ | 
					
						
							|  |  |  | 	bus->head_align = ALIGNMENT; | 
					
						
							|  |  |  | 	bus->sgentry_align = ALIGNMENT; | 
					
						
							|  |  |  | 	if (sdiodev->pdata) { | 
					
						
							|  |  |  | 		if (sdiodev->pdata->sd_head_align > ALIGNMENT) | 
					
						
							|  |  |  | 			bus->head_align = sdiodev->pdata->sd_head_align; | 
					
						
							|  |  |  | 		if (sdiodev->pdata->sd_sgentry_align > ALIGNMENT) | 
					
						
							|  |  |  | 			bus->sgentry_align = sdiodev->pdata->sd_sgentry_align; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-14 18:46:18 -08:00
										 |  |  | 	INIT_WORK(&bus->datawork, brcmf_sdio_dataworker); | 
					
						
							|  |  |  | 	bus->brcmf_wq = create_singlethread_workqueue("brcmf_wq"); | 
					
						
							|  |  |  | 	if (bus->brcmf_wq == NULL) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("insufficient memory to create txworkqueue\n"); | 
					
						
							| 
									
										
										
										
											2012-11-14 18:46:18 -08:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	/* attempt to attach to the dongle */ | 
					
						
							|  |  |  | 	if (!(brcmf_sdbrcm_probe_attach(bus, regsva))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("brcmf_sdbrcm_probe_attach failed\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-11-05 16:22:22 -08:00
										 |  |  | 	spin_lock_init(&bus->rxctl_lock); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	spin_lock_init(&bus->txqlock); | 
					
						
							|  |  |  | 	init_waitqueue_head(&bus->ctrl_wait); | 
					
						
							|  |  |  | 	init_waitqueue_head(&bus->dcmd_resp_wait); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Set up the watchdog timer */ | 
					
						
							|  |  |  | 	init_timer(&bus->timer); | 
					
						
							|  |  |  | 	bus->timer.data = (unsigned long)bus; | 
					
						
							|  |  |  | 	bus->timer.function = brcmf_sdbrcm_watchdog; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Initialize watchdog thread */ | 
					
						
							|  |  |  | 	init_completion(&bus->watchdog_wait); | 
					
						
							|  |  |  | 	bus->watchdog_tsk = kthread_run(brcmf_sdbrcm_watchdog_thread, | 
					
						
							|  |  |  | 					bus, "brcmf_watchdog"); | 
					
						
							|  |  |  | 	if (IS_ERR(bus->watchdog_tsk)) { | 
					
						
							| 
									
										
										
										
											2012-01-15 00:38:44 -08:00
										 |  |  | 		pr_warn("brcmf_watchdog thread failed to start\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		bus->watchdog_tsk = NULL; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	/* Initialize DPC thread */ | 
					
						
							| 
									
										
										
										
											2013-06-18 13:29:26 +02:00
										 |  |  | 	atomic_set(&bus->dpc_tskcnt, 0); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:12 -08:00
										 |  |  | 	/* Assign bus interface call back */ | 
					
						
							| 
									
										
										
										
											2012-12-05 15:25:54 +01:00
										 |  |  | 	bus->sdiodev->bus_if->dev = bus->sdiodev->dev; | 
					
						
							|  |  |  | 	bus->sdiodev->bus_if->ops = &brcmf_sdio_bus_ops; | 
					
						
							| 
									
										
										
										
											2013-02-06 18:40:45 +01:00
										 |  |  | 	bus->sdiodev->bus_if->chip = bus->ci->chip; | 
					
						
							|  |  |  | 	bus->sdiodev->bus_if->chiprev = bus->ci->chiprev; | 
					
						
							| 
									
										
										
										
											2012-12-05 15:25:54 +01:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2013-08-10 12:27:29 +02:00
										 |  |  | 	/* default sdio bus header length for tx packet */ | 
					
						
							|  |  |  | 	bus->tx_hdrlen = SDPCM_HWHDR_LEN + SDPCM_SWHDR_LEN; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Attach to the common layer, reserve hdr space */ | 
					
						
							| 
									
										
										
										
											2013-11-29 11:48:14 +01:00
										 |  |  | 	ret = brcmf_attach(bus->sdiodev->dev); | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	if (ret != 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("brcmf_attach failed\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Allocate buffers */ | 
					
						
							|  |  |  | 	if (!(brcmf_sdbrcm_probe_malloc(bus))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("brcmf_sdbrcm_probe_malloc failed\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (!(brcmf_sdbrcm_probe_init(bus))) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("brcmf_sdbrcm_probe_init failed\n"); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		goto fail; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2012-06-09 22:51:44 +02:00
										 |  |  | 	brcmf_sdio_debugfs_create(bus); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	brcmf_dbg(INFO, "completed!!\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* if firmware path present try to download and bring up bus */ | 
					
						
							| 
									
										
										
										
											2011-12-16 18:36:55 -08:00
										 |  |  | 	ret = brcmf_bus_start(bus->sdiodev->dev); | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (ret != 0) { | 
					
						
							| 
									
										
										
										
											2012-12-07 10:49:57 +01:00
										 |  |  | 		brcmf_err("dongle is not responding\n"); | 
					
						
							| 
									
										
										
										
											2012-11-14 18:46:17 -08:00
										 |  |  | 		goto fail; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	} | 
					
						
							| 
									
										
										
										
											2011-10-21 16:16:32 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	return bus; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | fail: | 
					
						
							|  |  |  | 	brcmf_sdbrcm_release(bus); | 
					
						
							|  |  |  | 	return NULL; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void brcmf_sdbrcm_disconnect(void *ptr) | 
					
						
							|  |  |  | { | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | 	struct brcmf_sdio *bus = (struct brcmf_sdio *)ptr; | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Enter\n"); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (bus) | 
					
						
							|  |  |  | 		brcmf_sdbrcm_release(bus); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	brcmf_dbg(TRACE, "Disconnected\n"); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void | 
					
						
							| 
									
										
										
										
											2011-11-22 17:21:50 -08:00
										 |  |  | brcmf_sdbrcm_wd_timer(struct brcmf_sdio *bus, uint wdtick) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | { | 
					
						
							|  |  |  | 	/* Totally stop the timer */ | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 	if (!wdtick && bus->wd_timer_valid) { | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 		del_timer_sync(&bus->timer); | 
					
						
							|  |  |  | 		bus->wd_timer_valid = false; | 
					
						
							|  |  |  | 		bus->save_ms = wdtick; | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-21 16:16:19 +02:00
										 |  |  | 	/* don't start the wd until fw is loaded */ | 
					
						
							| 
									
										
										
										
											2011-12-16 18:37:09 -08:00
										 |  |  | 	if (bus->sdiodev->bus_if->state == BRCMF_BUS_DOWN) | 
					
						
							| 
									
										
										
										
											2011-10-21 16:16:19 +02:00
										 |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 	if (wdtick) { | 
					
						
							|  |  |  | 		if (bus->save_ms != BRCMF_WD_POLL_MS) { | 
					
						
							| 
									
										
										
										
											2012-02-09 11:17:23 +00:00
										 |  |  | 			if (bus->wd_timer_valid) | 
					
						
							| 
									
										
										
										
											2011-10-05 13:19:03 +02:00
										 |  |  | 				/* Stop timer and restart at new value */ | 
					
						
							|  |  |  | 				del_timer_sync(&bus->timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			/* Create timer again when watchdog period is
 | 
					
						
							|  |  |  | 			   dynamically changed or in the first instance | 
					
						
							|  |  |  | 			 */ | 
					
						
							|  |  |  | 			bus->timer.expires = | 
					
						
							|  |  |  | 				jiffies + BRCMF_WD_POLL_MS * HZ / 1000; | 
					
						
							|  |  |  | 			add_timer(&bus->timer); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 			/* Re arm the timer, at last watchdog period */ | 
					
						
							|  |  |  | 			mod_timer(&bus->timer, | 
					
						
							|  |  |  | 				jiffies + BRCMF_WD_POLL_MS * HZ / 1000); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		bus->wd_timer_valid = true; | 
					
						
							|  |  |  | 		bus->save_ms = wdtick; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } |