blob: 2b1762e0cdb47680a026f42072f49525f48cb3e4 [file] [log] [blame]
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001/*
2 * Copyright (c) 2010 Broadcom Corporation
3 *
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
7 *
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
11 * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
13 * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
14 * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070017#include <linux/types.h>
Arend van Spriel34227312011-05-10 22:25:32 +020018#include <linux/kernel.h>
19#include <linux/printk.h>
Stanislav Fomichevbe1c09f2011-03-28 01:31:36 +040020#include <linux/pci_ids.h>
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070021#include <linux/netdevice.h>
Franky Lin7c6100e2011-06-01 13:45:36 +020022#include <linux/sched.h>
Franky Lin0df46042011-06-01 13:45:40 +020023#include <linux/mmc/sdio.h>
Franky Lin7c6100e2011-06-01 13:45:36 +020024#include <asm/unaligned.h>
Roland Vossencc3cea52011-06-01 13:45:59 +020025#include <defs.h>
Roland Vossenf97e9562011-06-01 13:45:52 +020026#include <brcmu_wifi.h>
27#include <brcmu_utils.h>
Roland Vossencc3cea52011-06-01 13:45:59 +020028#include <brcm_hw_ids.h>
Roland Vossencc3cea52011-06-01 13:45:59 +020029#include <soc.h>
Roland Vossenc4daa842011-06-09 16:44:50 +020030#include "sdio_host.h"
Roland Vossen44895182011-06-01 13:45:55 +020031
32/* register access macros */
Roland Vossen44895182011-06-01 13:45:55 +020033#ifndef __BIG_ENDIAN
34#ifndef __mips__
Roland Vossenb61a4be2011-06-01 13:45:56 +020035#define R_REG(r) \
Roland Vossen54ca2962011-06-29 16:46:59 -070036 brcmf_sdcard_reg_read(NULL, (unsigned long)(r), sizeof(*(r)))
Roland Vossen44895182011-06-01 13:45:55 +020037#else /* __mips__ */
Roland Vossenb61a4be2011-06-01 13:45:56 +020038#define R_REG(r) \
39 ({ \
40 __typeof(*(r)) __osl_v; \
41 __asm__ __volatile__("sync"); \
Roland Vossen54ca2962011-06-29 16:46:59 -070042 __osl_v = brcmf_sdcard_reg_read(NULL, (unsigned long)(r),\
Roland Vossen25036342011-06-01 13:45:57 +020043 sizeof(*(r))); \
Roland Vossenb61a4be2011-06-01 13:45:56 +020044 __asm__ __volatile__("sync"); \
45 __osl_v; \
46 })
Roland Vossen44895182011-06-01 13:45:55 +020047#endif /* __mips__ */
48
49#define W_REG(r, v) do { \
Roland Vossen54ca2962011-06-29 16:46:59 -070050 brcmf_sdcard_reg_write(NULL, (unsigned long)(r), sizeof(*(r)), \
51 (v)); \
Roland Vossen44895182011-06-01 13:45:55 +020052 } while (0)
53#else /* __BIG_ENDIAN */
Roland Vossenb61a4be2011-06-01 13:45:56 +020054#define R_REG(r) \
Roland Vossen54ca2962011-06-29 16:46:59 -070055 brcmf_sdcard_reg_read(NULL, (unsigned long)(r), sizeof(*(r)))
Roland Vossen44895182011-06-01 13:45:55 +020056#define W_REG(r, v) do { \
Roland Vossen54ca2962011-06-29 16:46:59 -070057 brcmf_sdcard_reg_write(NULL, (unsigned long)(r), sizeof(*(r)), \
58 (v)); \
Roland Vossen44895182011-06-01 13:45:55 +020059 } while (0)
60#endif /* __BIG_ENDIAN */
61
Roland Vossen44895182011-06-01 13:45:55 +020062#define AND_REG(r, v) W_REG((r), R_REG(r) & (v))
63#define OR_REG(r, v) W_REG((r), R_REG(r) | (v))
64
65#define SET_REG(r, mask, val) \
66 W_REG((r), ((R_REG(r) & ~(mask)) | (val)))
67
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070068#ifdef DHD_DEBUG
Roland Vossenf1c7a082011-06-01 13:45:09 +020069
70/* ARM trap handling */
71
72/* Trap types defined by ARM (see arminc.h) */
73
74/* Trap locations in lo memory */
75#define TRAP_STRIDE 4
76#define FIRST_TRAP TR_RST
77#define LAST_TRAP (TR_FIQ * TRAP_STRIDE)
78
79#if defined(__ARM_ARCH_4T__)
80#define MAX_TRAP_TYPE (TR_FIQ + 1)
81#elif defined(__ARM_ARCH_7M__)
82#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS)
83#endif /* __ARM_ARCH_7M__ */
84
85/* The trap structure is defined here as offsets for assembly */
86#define TR_TYPE 0x00
87#define TR_EPC 0x04
88#define TR_CPSR 0x08
89#define TR_SPSR 0x0c
90#define TR_REGS 0x10
91#define TR_REG(n) (TR_REGS + (n) * 4)
92#define TR_SP TR_REG(13)
93#define TR_LR TR_REG(14)
94#define TR_PC TR_REG(15)
95
96#define TRAP_T_SIZE 80
97
Roland Vossen51851252011-06-29 16:47:24 -070098struct brcmf_trap {
Roland Vossenf1c7a082011-06-01 13:45:09 +020099 u32 type;
100 u32 epc;
101 u32 cpsr;
102 u32 spsr;
103 u32 r0;
104 u32 r1;
105 u32 r2;
106 u32 r3;
107 u32 r4;
108 u32 r5;
109 u32 r6;
110 u32 r7;
111 u32 r8;
112 u32 r9;
113 u32 r10;
114 u32 r11;
115 u32 r12;
116 u32 r13;
117 u32 r14;
118 u32 pc;
Roland Vossen51851252011-06-29 16:47:24 -0700119};
Roland Vossenf1c7a082011-06-01 13:45:09 +0200120
Roland Vossenf1c7a082011-06-01 13:45:09 +0200121#define CBUF_LEN (128)
122
123#define LOG_BUF_LEN 1024
124
Roland Vossen51851252011-06-29 16:47:24 -0700125struct rte_log {
Roland Vossenf1c7a082011-06-01 13:45:09 +0200126 u32 buf; /* Can't be pointer on (64-bit) hosts */
127 uint buf_size;
128 uint idx;
129 char *_buf_compat; /* Redundant pointer for backward compat. */
Roland Vossen51851252011-06-29 16:47:24 -0700130};
Roland Vossenf1c7a082011-06-01 13:45:09 +0200131
Roland Vossen51851252011-06-29 16:47:24 -0700132struct rte_console {
Roland Vossenf1c7a082011-06-01 13:45:09 +0200133 /* Virtual UART
134 * When there is no UART (e.g. Quickturn),
135 * the host should write a complete
136 * input line directly into cbuf and then write
137 * the length into vcons_in.
138 * This may also be used when there is a real UART
139 * (at risk of conflicting with
140 * the real UART). vcons_out is currently unused.
141 */
142 volatile uint vcons_in;
143 volatile uint vcons_out;
144
145 /* Output (logging) buffer
146 * Console output is written to a ring buffer log_buf at index log_idx.
147 * The host may read the output when it sees log_idx advance.
148 * Output will be lost if the output wraps around faster than the host
149 * polls.
150 */
Roland Vossen51851252011-06-29 16:47:24 -0700151 struct rte_log log;
Roland Vossenf1c7a082011-06-01 13:45:09 +0200152
153 /* Console input line buffer
154 * Characters are read one at a time into cbuf
155 * until <CR> is received, then
156 * the buffer is processed as a command line.
157 * Also used for virtual UART.
158 */
159 uint cbuf_idx;
160 char cbuf[CBUF_LEN];
Roland Vossen51851252011-06-29 16:47:24 -0700161};
Roland Vossenf1c7a082011-06-01 13:45:09 +0200162
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700163#endif /* DHD_DEBUG */
Roland Vossen745c9e62011-06-01 13:45:30 +0200164#include <chipcommon.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700165
Roland Vossenc4daa842011-06-09 16:44:50 +0200166#include "sbsdio.h"
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700167
Roland Vossenc4daa842011-06-09 16:44:50 +0200168#include "dngl_stats.h"
169#include "dhd.h"
170#include "dhd_bus.h"
171#include "dhd_proto.h"
172#include "dhd_dbg.h"
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700173#include <sdiovar.h>
Franky Lincb63e4c2011-04-25 15:45:08 -0700174#include <bcmchip.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700175
176#ifndef DHDSDIO_MEM_DUMP_FNAME
177#define DHDSDIO_MEM_DUMP_FNAME "mem_dump"
178#endif
179
Grant Grundler26a71a42011-03-09 10:41:25 -0800180#define TXQLEN 2048 /* bulk tx queue length */
181#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
182#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700183#define PRIOMASK 7
184
185#define TXRETRIES 2 /* # of retries for tx frames */
186
187#if defined(CONFIG_MACH_SANDGATE2G)
188#define DHD_RXBOUND 250 /* Default for max rx frames in
189 one scheduling */
190#else
191#define DHD_RXBOUND 50 /* Default for max rx frames in
192 one scheduling */
193#endif /* defined(CONFIG_MACH_SANDGATE2G) */
194
195#define DHD_TXBOUND 20 /* Default for max tx frames in
196 one scheduling */
197
198#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
199
200#define MEMBLOCK 2048 /* Block size used for downloading
201 of dongle image */
202#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
203 biggest possible glom */
204
Arend van Spriel8d825a82011-06-29 16:47:25 -0700205#ifndef BRCMF_FIRSTREAD
206#define BRCMF_FIRSTREAD 32
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700207#endif
Arend van Spriel8d825a82011-06-29 16:47:25 -0700208#if !ISPOWEROF2(BRCMF_FIRSTREAD)
209#error BRCMF_FIRSTREAD is not a power of 2!
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700210#endif
211
212/* Total length of frame header for dongle protocol */
213#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
214#ifdef SDTEST
Arend van Spriel8d825a82011-06-29 16:47:25 -0700215#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + BRCMF_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700216#else
Arend van Spriel8d825a82011-06-29 16:47:25 -0700217#define SDPCM_RESERVE (SDPCM_HDRLEN + BRCMF_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700218#endif
219
Franky Linb49b14d2011-06-01 13:45:37 +0200220/*
221 * Software allocation of To SB Mailbox resources
222 */
223
224/* tosbmailbox bits corresponding to intstatus bits */
225#define SMB_NAK (1 << 0) /* Frame NAK */
226#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
227#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
228#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
229
230/* tosbmailboxdata */
231#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
232
233/*
234 * Software allocation of To Host Mailbox resources
235 */
236
237/* intstatus bits */
238#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
239#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
240#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
241#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
242
243/* tohostmailboxdata */
244#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
245#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
246#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
247#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
248
249#define HMB_DATA_FCDATA_MASK 0xff000000
250#define HMB_DATA_FCDATA_SHIFT 24
251
252#define HMB_DATA_VERSION_MASK 0x00ff0000
253#define HMB_DATA_VERSION_SHIFT 16
254
255/*
256 * Software-defined protocol header
257 */
258
259/* Current protocol version */
260#define SDPCM_PROT_VERSION 4
261
262/* SW frame header */
263#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff)
264
265#define SDPCM_CHANNEL_MASK 0x00000f00
266#define SDPCM_CHANNEL_SHIFT 8
267#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f)
268
269#define SDPCM_NEXTLEN_OFFSET 2
270
271/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
272#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
273#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
274#define SDPCM_DOFFSET_MASK 0xff000000
275#define SDPCM_DOFFSET_SHIFT 24
276#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
277#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
278#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
279#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
280
281#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
282
283/* logical channel numbers */
284#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */
285#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
286#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
287#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */
288#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
289
290#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */
291
292#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
293
294/* For TEST_CHANNEL packets, define another 4-byte header */
295#define SDPCM_TEST_HDRLEN 4 /*
296 * Generally: Cmd(1), Ext(1), Len(2);
297 * Semantics of Ext byte depend on
298 * command. Len is current or requested
299 * frame length, not including test
300 * header; sent little-endian.
301 */
302#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext:pattern id. */
303#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext:pattern id. */
304#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext:pattern id. */
305#define SDPCM_TEST_BURST 0x04 /*
306 * Receiver to send a burst.
307 * Ext is a frame count
308 */
309#define SDPCM_TEST_SEND 0x05 /*
310 * Receiver sets send mode.
311 * Ext is boolean on/off
312 */
313
314/* Handy macro for filling in datagen packets with a pattern */
315#define SDPCM_TEST_FILL(byteno, id) ((u8)(id + byteno))
316
317/*
318 * Shared structure between dongle and the host.
319 * The structure contains pointers to trap or assert information.
320 */
321#define SDPCM_SHARED_VERSION 0x0002
322#define SDPCM_SHARED_VERSION_MASK 0x00FF
323#define SDPCM_SHARED_ASSERT_BUILT 0x0100
324#define SDPCM_SHARED_ASSERT 0x0200
325#define SDPCM_SHARED_TRAP 0x0400
326
327
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700328/* Space for header read, limit for data packets */
329#ifndef MAX_HDR_READ
330#define MAX_HDR_READ 32
331#endif
332#if !ISPOWEROF2(MAX_HDR_READ)
333#error MAX_HDR_READ is not a power of 2!
334#endif
335
336#define MAX_RX_DATASZ 2048
337
338/* Maximum milliseconds to wait for F2 to come up */
339#define DHD_WAIT_F2RDY 3000
340
341/* Bump up limit on waiting for HT to account for first startup;
342 * if the image is doing a CRC calculation before programming the PMU
343 * for HT availability, it could take a couple hundred ms more, so
344 * max out at a 1 second (1000000us).
345 */
346#if (PMU_MAX_TRANSITION_DLY <= 1000000)
347#undef PMU_MAX_TRANSITION_DLY
348#define PMU_MAX_TRANSITION_DLY 1000000
349#endif
350
351/* Value for ChipClockCSR during initial setup */
352#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
353 SBSDIO_ALP_AVAIL_REQ)
354#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
355
356/* Flags for SDH calls */
357#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
358
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200359/* sbimstate */
360#define SBIM_IBE 0x20000 /* inbanderror */
361#define SBIM_TO 0x40000 /* timeout */
362#define SBIM_BY 0x01800000 /* busy (sonics >= 2.3) */
363#define SBIM_RJ 0x02000000 /* reject (sonics >= 2.3) */
364
365/* sbtmstatelow */
366#define SBTML_RESET 0x0001 /* reset */
367#define SBTML_REJ_MASK 0x0006 /* reject field */
368#define SBTML_REJ 0x0002 /* reject */
369#define SBTML_TMPREJ 0x0004 /* temporary reject, for error recovery */
370
371#define SBTML_SICF_SHIFT 16 /* Shift to locate the SI control flags in sbtml */
372
373/* sbtmstatehigh */
374#define SBTMH_SERR 0x0001 /* serror */
375#define SBTMH_INT 0x0002 /* interrupt */
376#define SBTMH_BUSY 0x0004 /* busy */
377#define SBTMH_TO 0x0020 /* timeout (sonics >= 2.3) */
378
379#define SBTMH_SISF_SHIFT 16 /* Shift to locate the SI status flags in sbtmh */
380
381/* sbidlow */
382#define SBIDL_INIT 0x80 /* initiator */
383
384/* sbidhigh */
385#define SBIDH_RC_MASK 0x000f /* revision code */
386#define SBIDH_RCE_MASK 0x7000 /* revision code extension field */
387#define SBIDH_RCE_SHIFT 8
388#define SBCOREREV(sbidh) \
389 ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK))
390#define SBIDH_CC_MASK 0x8ff0 /* core code */
391#define SBIDH_CC_SHIFT 4
392#define SBIDH_VC_MASK 0xffff0000 /* vendor code */
393#define SBIDH_VC_SHIFT 16
394
Arend van Spriel70dfb582011-02-25 16:39:17 +0100395/*
396 * Conversion of 802.1D priority to precedence level
397 */
398#define PRIO2PREC(prio) \
399 (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? \
400 ((prio^2)) : (prio))
401
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700402DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700403
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200404/* Core reg address translation */
405#define CORE_CC_REG(base, field) (base + offsetof(chipcregs_t, field))
Franky Lin597600a2011-06-01 13:45:39 +0200406#define CORE_BUS_REG(base, field) \
407 (base + offsetof(struct sdpcmd_regs, field))
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200408#define CORE_SB(base, field) \
409 (base + SBCONFIGOFF + offsetof(sbconfig_t, field))
410
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700411#ifdef DHD_DEBUG
412/* Device console log buffer state */
Roland Vossen51851252011-06-29 16:47:24 -0700413struct dhd_console {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700414 uint count; /* Poll interval msec counter */
415 uint log_addr; /* Log struct address (fixed) */
Roland Vossen51851252011-06-29 16:47:24 -0700416 struct rte_log log; /* Log struct (host copy) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700417 uint bufsize; /* Size of log buffer */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700418 u8 *buf; /* Log buffer (host copy) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700419 uint last; /* Last buffer read index */
Roland Vossen51851252011-06-29 16:47:24 -0700420};
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700421#endif /* DHD_DEBUG */
422
Franky Linb49b14d2011-06-01 13:45:37 +0200423struct sdpcm_shared {
424 u32 flags;
425 u32 trap_addr;
426 u32 assert_exp_addr;
427 u32 assert_file_addr;
428 u32 assert_line;
Roland Vossen51851252011-06-29 16:47:24 -0700429 u32 console_addr; /* Address of struct rte_console */
Franky Linb49b14d2011-06-01 13:45:37 +0200430 u32 msgtrace_addr;
431 u8 tag[32];
432};
433
434
Franky Lincb63e4c2011-04-25 15:45:08 -0700435/* misc chip info needed by some of the routines */
436struct chip_info {
437 u32 chip;
438 u32 chiprev;
439 u32 cccorebase;
440 u32 ccrev;
441 u32 cccaps;
442 u32 buscorebase;
443 u32 buscorerev;
444 u32 buscoretype;
445 u32 ramcorebase;
446 u32 armcorebase;
447 u32 pmurev;
Franky Linc05df632011-04-25 19:34:07 -0700448 u32 ramsize;
Franky Lincb63e4c2011-04-25 15:45:08 -0700449};
450
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700451/* Private data for SDIO bus interaction */
452typedef struct dhd_bus {
453 dhd_pub_t *dhd;
454
Roland Vossen677226a2011-06-29 16:47:21 -0700455 struct brcmf_sdio *sdh; /* Handle for BCMSDH calls */
Franky Lincb63e4c2011-04-25 15:45:08 -0700456 struct chip_info *ci; /* Chip info struct */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700457 char *vars; /* Variables (from CIS and/or other) */
458 uint varsz; /* Size of variables buffer */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700459 u32 sbaddr; /* Current SB window pointer (-1, invalid) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700460
Franky Lin597600a2011-06-01 13:45:39 +0200461 struct sdpcmd_regs *regs; /* SDIO core */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700462 uint sdpcmrev; /* SDIO core revision */
463 uint armrev; /* CPU core revision */
464 uint ramrev; /* SOCRAM core revision */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700465 u32 ramsize; /* Size of RAM in SOCRAM (bytes) */
466 u32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700467
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700468 u32 bus; /* gSPI or SDIO bus */
469 u32 hostintmask; /* Copy of Host Interrupt Mask */
470 u32 intstatus; /* Intstatus bits (events) pending */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700471 bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
472 bool fcstate; /* State of dongle flow-control */
473
Roland Vossen54ca2962011-06-29 16:46:59 -0700474 u16 cl_devid; /* cached devid for brcmf_sdio_probe_attach() */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700475 char *fw_path; /* module_param: path to firmware image */
476 char *nv_path; /* module_param: path to nvram vars file */
477 const char *nvram_params; /* user specified nvram params. */
478
479 uint blocksize; /* Block size of SDIO transfers */
480 uint roundup; /* Max roundup limit */
481
482 struct pktq txq; /* Queue length used for flow-control */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700483 u8 flowcontrol; /* per prio flow control bitmask */
484 u8 tx_seq; /* Transmit sequence number (next) */
485 u8 tx_max; /* Maximum transmit sequence allowed */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700486
Arend van Spriel8d825a82011-06-29 16:47:25 -0700487 u8 hdrbuf[MAX_HDR_READ + BRCMF_SDALIGN];
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700488 u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700489 u16 nextlen; /* Next Read Len from last header */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700490 u8 rx_seq; /* Receive sequence number (expected) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700491 bool rxskip; /* Skip receive (awaiting NAK ACK) */
492
Arend van Sprielc26b1372010-11-23 14:06:23 +0100493 struct sk_buff *glomd; /* Packet containing glomming descriptor */
494 struct sk_buff *glom; /* Packet chain for glommed superframe */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700495 uint glomerr; /* Glom packet read errors */
496
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700497 u8 *rxbuf; /* Buffer for receiving control packets */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700498 uint rxblen; /* Allocated length of rxbuf */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700499 u8 *rxctl; /* Aligned pointer into rxbuf */
500 u8 *databuf; /* Buffer for receiving big glom packet */
501 u8 *dataptr; /* Aligned pointer into databuf */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700502 uint rxlen; /* Length of valid data in buffer */
503
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700504 u8 sdpcm_ver; /* Bus protocol reported by dongle */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700505
506 bool intr; /* Use interrupts */
507 bool poll; /* Use polling */
508 bool ipend; /* Device interrupt is pending */
509 bool intdis; /* Interrupts disabled by isr */
510 uint intrcount; /* Count of device interrupt callbacks */
511 uint lastintrs; /* Count as of last watchdog timer */
512 uint spurious; /* Count of spurious interrupts */
513 uint pollrate; /* Ticks between device polls */
514 uint polltick; /* Tick counter */
515 uint pollcnt; /* Count of active polls */
516
517#ifdef DHD_DEBUG
Roland Vossen51851252011-06-29 16:47:24 -0700518 struct dhd_console console; /* Console output polling support */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700519 uint console_addr; /* Console address from shared struct */
520#endif /* DHD_DEBUG */
521
522 uint regfails; /* Count of R_REG/W_REG failures */
523
524 uint clkstate; /* State of sd and backplane clock(s) */
525 bool activity; /* Activity flag for clock down */
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700526 s32 idletime; /* Control for activity timeout */
527 s32 idlecount; /* Activity timeout counter */
528 s32 idleclock; /* How to set bus driver when idle */
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700529 s32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700530 bool use_rxchain; /* If dhd should use PKT chains */
531 bool sleeping; /* Is SDIO bus sleeping? */
532 bool rxflow_mode; /* Rx flow control mode */
533 bool rxflow; /* Is rx flow control on */
534 uint prev_rxlim_hit; /* Is prev rx limit exceeded
535 (per dpc schedule) */
536 bool alp_only; /* Don't use HT clock (ALP only) */
537/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
538 bool usebufpool;
539
540#ifdef SDTEST
541 /* external loopback */
542 bool ext_loop;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700543 u8 loopid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700544
545 /* pktgen configuration */
546 uint pktgen_freq; /* Ticks between bursts */
547 uint pktgen_count; /* Packets to send each burst */
548 uint pktgen_print; /* Bursts between count displays */
549 uint pktgen_total; /* Stop after this many */
550 uint pktgen_minlen; /* Minimum packet data len */
551 uint pktgen_maxlen; /* Maximum packet data len */
552 uint pktgen_mode; /* Configured mode: tx, rx, or echo */
553 uint pktgen_stop; /* Number of tx failures causing stop */
554
555 /* active pktgen fields */
556 uint pktgen_tick; /* Tick counter for bursts */
557 uint pktgen_ptick; /* Burst counter for printing */
558 uint pktgen_sent; /* Number of test packets generated */
559 uint pktgen_rcvd; /* Number of test packets received */
560 uint pktgen_fail; /* Number of failed send attempts */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700561 u16 pktgen_len; /* Length of next packet to send */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700562#endif /* SDTEST */
563
564 /* Some additional counters */
565 uint tx_sderrs; /* Count of tx attempts with sd errors */
566 uint fcqueued; /* Tx packets that got queued */
567 uint rxrtx; /* Count of rtx requests (NAK to dongle) */
568 uint rx_toolong; /* Receive frames too long to receive */
569 uint rxc_errors; /* SDIO errors when reading control frames */
570 uint rx_hdrfail; /* SDIO errors on header reads */
571 uint rx_badhdr; /* Bad received headers (roosync?) */
572 uint rx_badseq; /* Mismatched rx sequence number */
573 uint fc_rcvd; /* Number of flow-control events received */
574 uint fc_xoff; /* Number which turned on flow-control */
575 uint fc_xon; /* Number which turned off flow-control */
576 uint rxglomfail; /* Failed deglom attempts */
577 uint rxglomframes; /* Number of glom frames (superframes) */
578 uint rxglompkts; /* Number of packets from glom frames */
579 uint f2rxhdrs; /* Number of header reads */
580 uint f2rxdata; /* Number of frame data reads */
581 uint f2txdata; /* Number of f2 frame writes */
582 uint f1regdata; /* Number of f1 register accesses */
583
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700584 u8 *ctrl_frame_buf;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700585 u32 ctrl_frame_len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700586 bool ctrl_frame_stat;
Franky Lin0f1a87c2011-06-29 16:46:32 -0700587
588 spinlock_t txqlock;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700589} dhd_bus_t;
590
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200591typedef volatile struct _sbconfig {
592 u32 PAD[2];
593 u32 sbipsflag; /* initiator port ocp slave flag */
594 u32 PAD[3];
595 u32 sbtpsflag; /* target port ocp slave flag */
596 u32 PAD[11];
597 u32 sbtmerrloga; /* (sonics >= 2.3) */
598 u32 PAD;
599 u32 sbtmerrlog; /* (sonics >= 2.3) */
600 u32 PAD[3];
601 u32 sbadmatch3; /* address match3 */
602 u32 PAD;
603 u32 sbadmatch2; /* address match2 */
604 u32 PAD;
605 u32 sbadmatch1; /* address match1 */
606 u32 PAD[7];
607 u32 sbimstate; /* initiator agent state */
608 u32 sbintvec; /* interrupt mask */
609 u32 sbtmstatelow; /* target state */
610 u32 sbtmstatehigh; /* target state */
611 u32 sbbwa0; /* bandwidth allocation table0 */
612 u32 PAD;
613 u32 sbimconfiglow; /* initiator configuration */
614 u32 sbimconfighigh; /* initiator configuration */
615 u32 sbadmatch0; /* address match0 */
616 u32 PAD;
617 u32 sbtmconfiglow; /* target configuration */
618 u32 sbtmconfighigh; /* target configuration */
619 u32 sbbconfig; /* broadcast configuration */
620 u32 PAD;
621 u32 sbbstate; /* broadcast state */
622 u32 PAD[3];
623 u32 sbactcnfg; /* activate configuration */
624 u32 PAD[3];
625 u32 sbflagst; /* current sbflags */
626 u32 PAD[3];
627 u32 sbidlow; /* identification */
628 u32 sbidhigh; /* identification */
629} sbconfig_t;
630
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700631/* clkstate */
632#define CLK_NONE 0
633#define CLK_SDONLY 1
634#define CLK_PENDING 2 /* Not used yet */
635#define CLK_AVAIL 3
636
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700637#define DHD_NOPMU(dhd) (false)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700638
639#ifdef DHD_DEBUG
640static int qcount[NUMPRIO];
641static int tx_packets[NUMPRIO];
642#endif /* DHD_DEBUG */
643
644/* Deferred transmit */
Arend van Spriel7af9d0c2011-06-29 16:47:19 -0700645const uint brcmf_deferred_tx = 1;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700646
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700647/* Tx/Rx bounds */
Arend van Spriel7af9d0c2011-06-29 16:47:19 -0700648uint brcmf_txbound;
649uint brcmf_rxbound;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700650uint dhd_txminmax;
651
652/* override the RAM size if possible */
653#define DONGLE_MIN_MEMSIZE (128 * 1024)
Arend van Spriel7af9d0c2011-06-29 16:47:19 -0700654int brcmf_dongle_memsize;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700655
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700656static bool dhd_alignctl;
657
658static bool sd1idle;
659
660static bool retrydata;
661#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
662
663static const uint watermark = 8;
Arend van Spriel8d825a82011-06-29 16:47:25 -0700664static const uint firstread = BRCMF_FIRSTREAD;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700665
666#define HDATLEN (firstread - (SDPCM_HDRLEN))
667
668/* Retry count for register access failures */
669static const uint retry_limit = 2;
670
671/* Force even SD lengths (some host controllers mess up on odd bytes) */
672static bool forcealign;
673
674#define ALIGNMENT 4
675
676#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
Roland Vossen54ca2962011-06-29 16:46:59 -0700677extern void brcmf_sdcard_enable_hw_oob_intr(void *sdh, bool enable);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700678#endif
679
680#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
681#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
682#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100683#define PKTALIGN(_p, _len, _align) \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700684 do { \
685 uint datalign; \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100686 datalign = (unsigned long)((_p)->data); \
687 datalign = roundup(datalign, (_align)) - datalign; \
688 ASSERT(datalign < (_align)); \
689 ASSERT((_p)->len >= ((_len) + datalign)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700690 if (datalign) \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100691 skb_pull((_p), datalign); \
692 __skb_trim((_p), (_len)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700693 } while (0)
694
695/* Limit on rounding up frames */
696static const uint max_roundup = 512;
697
698/* Try doing readahead */
699static bool dhd_readahead;
700
701/* To check if there's window offered */
702#define DATAOK(bus) \
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700703 (((u8)(bus->tx_max - bus->tx_seq) != 0) && \
704 (((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700705
706/* Macros to get register read/write status */
707/* NOTE: these assume a local dhdsdio_bus_t *bus! */
708#define R_SDREG(regvar, regaddr, retryvar) \
709do { \
710 retryvar = 0; \
711 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100712 regvar = R_REG(regaddr); \
Roland Vossen54ca2962011-06-29 16:46:59 -0700713 } while (brcmf_sdcard_regfail(bus->sdh) && \
714 (++retryvar <= retry_limit)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700715 if (retryvar) { \
716 bus->regfails += (retryvar-1); \
717 if (retryvar > retry_limit) { \
718 DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
719 __func__, __LINE__)); \
720 regvar = 0; \
721 } \
722 } \
723} while (0)
724
725#define W_SDREG(regval, regaddr, retryvar) \
726do { \
727 retryvar = 0; \
728 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100729 W_REG(regaddr, regval); \
Roland Vossen54ca2962011-06-29 16:46:59 -0700730 } while (brcmf_sdcard_regfail(bus->sdh) && \
731 (++retryvar <= retry_limit)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700732 if (retryvar) { \
733 bus->regfails += (retryvar-1); \
734 if (retryvar > retry_limit) \
735 DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
736 __func__, __LINE__)); \
737 } \
738} while (0)
739
740#define DHD_BUS SDIO_BUS
741
742#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
743
744#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
745
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700746#ifdef SDTEST
Roland Vossen54ca2962011-06-29 16:46:59 -0700747static void brcmf_sdbrcm_checkdied(dhd_bus_t *bus, void *pkt, uint seq);
748static void brcmf_sdbrcm_sdtest_set(dhd_bus_t *bus, bool start);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700749#endif
750
751#ifdef DHD_DEBUG
Roland Vossen54ca2962011-06-29 16:46:59 -0700752static int brcmf_sdbrcm_checkdied(dhd_bus_t *bus, u8 *data, uint size);
753static int brcmf_sdbrcm_mem_dump(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700754#endif /* DHD_DEBUG */
Roland Vossen54ca2962011-06-29 16:46:59 -0700755static int brcmf_sdbrcm_download_state(dhd_bus_t *bus, bool enter);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700756
Roland Vossen54ca2962011-06-29 16:46:59 -0700757static void brcmf_sdbrcm_release(dhd_bus_t *bus);
758static void brcmf_sdbrcm_release_malloc(dhd_bus_t *bus);
759static void brcmf_sdbrcm_disconnect(void *ptr);
760static bool brcmf_sdbrcm_chipmatch(u16 chipid);
761static bool brcmf_sdbrcm_probe_attach(dhd_bus_t *bus, void *sdh,
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100762 void *regsva, u16 devid);
Roland Vossen54ca2962011-06-29 16:46:59 -0700763static bool brcmf_sdbrcm_probe_malloc(dhd_bus_t *bus, void *sdh);
764static bool brcmf_sdbrcm_probe_init(dhd_bus_t *bus, void *sdh);
765static void brcmf_sdbrcm_release_dongle(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700766
Roland Vossen54ca2962011-06-29 16:46:59 -0700767static uint brcmf_process_nvram_vars(char *varbuf, uint len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700768
Roland Vossen54ca2962011-06-29 16:46:59 -0700769static void brcmf_sdbrcm_setmemsize(struct dhd_bus *bus, int mem_size);
770static int brcmf_sdbrcm_send_buf(dhd_bus_t *bus, u32 addr, uint fn,
Arend van Sprielc26b1372010-11-23 14:06:23 +0100771 uint flags, u8 *buf, uint nbytes,
772 struct sk_buff *pkt, bcmsdh_cmplt_fn_t complete,
773 void *handle);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700774
Roland Vossen54ca2962011-06-29 16:46:59 -0700775static bool brcmf_sdbrcm_download_firmware(struct dhd_bus *bus, void *sdh);
776static int _brcmf_sdbrcm_download_firmware(struct dhd_bus *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700777
Roland Vossen54ca2962011-06-29 16:46:59 -0700778static int
779brcmf_sdbrcm_download_code_file(struct dhd_bus *bus, char *image_path);
780static int brcmf_sdbrcm_download_nvram(struct dhd_bus *bus);
Roland Vossen677226a2011-06-29 16:47:21 -0700781static void brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio *sdh, u32 corebase);
Roland Vossen54ca2962011-06-29 16:46:59 -0700782static int brcmf_sdbrcm_chip_attach(struct dhd_bus *bus, void *regs);
Roland Vossen677226a2011-06-29 16:47:21 -0700783static void brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio *sdh, u32 corebase);
Roland Vossen54ca2962011-06-29 16:46:59 -0700784static void brcmf_sdbrcm_sdiod_drive_strength_init(struct dhd_bus *bus,
Franky Lin5d0d7a92011-04-25 19:34:05 -0700785 u32 drivestrength);
Roland Vossen54ca2962011-06-29 16:46:59 -0700786static void brcmf_sdbrcm_chip_detach(struct dhd_bus *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700787
Grant Grundler4b455e02011-05-04 09:59:47 -0700788/* Packet free applicable unconditionally for sdio and sdspi.
789 * Conditional if bufpool was present for gspi bus.
790 */
Roland Vossen54ca2962011-06-29 16:46:59 -0700791static void brcmf_sdbrcm_pktfree2(dhd_bus_t *bus, struct sk_buff *pkt)
Grant Grundler4b455e02011-05-04 09:59:47 -0700792{
Grant Grundler4b455e02011-05-04 09:59:47 -0700793 if ((bus->bus != SPI_BUS) || bus->usebufpool)
Roland Vossen67ad48b2011-06-01 13:45:51 +0200794 brcmu_pkt_buf_free_skb(pkt);
Grant Grundler4b455e02011-05-04 09:59:47 -0700795}
796
Roland Vossen54ca2962011-06-29 16:46:59 -0700797static void brcmf_sdbrcm_setmemsize(struct dhd_bus *bus, int mem_size)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700798{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700799 s32 min_size = DONGLE_MIN_MEMSIZE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700800 /* Restrict the memsize to user specified limit */
801 DHD_ERROR(("user: Restrict the dongle ram size to %d, min %d\n",
Arend van Spriel7af9d0c2011-06-29 16:47:19 -0700802 brcmf_dongle_memsize, min_size));
803 if ((brcmf_dongle_memsize > min_size) &&
804 (brcmf_dongle_memsize < (s32) bus->orig_ramsize))
805 bus->ramsize = brcmf_dongle_memsize;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700806}
807
Roland Vossen54ca2962011-06-29 16:46:59 -0700808static int brcmf_sdbrcm_set_siaddr_window(dhd_bus_t *bus, u32 address)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700809{
810 int err = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -0700811 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700812 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
813 if (!err)
Roland Vossen54ca2962011-06-29 16:46:59 -0700814 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1,
815 SBSDIO_FUNC1_SBADDRMID,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700816 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
817 if (!err)
Roland Vossen54ca2962011-06-29 16:46:59 -0700818 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1,
819 SBSDIO_FUNC1_SBADDRHIGH,
820 (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
821 &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700822 return err;
823}
824
825/* Turn backplane clock on or off */
Roland Vossen54ca2962011-06-29 16:46:59 -0700826static int brcmf_sdbrcm_htclk(dhd_bus_t *bus, bool on, bool pendok)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700827{
828 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700829 u8 clkctl, clkreq, devctl;
Roland Vossen677226a2011-06-29 16:47:21 -0700830 struct brcmf_sdio *sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700831
832 DHD_TRACE(("%s: Enter\n", __func__));
833
834#if defined(OOB_INTR_ONLY)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700835 pendok = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700836#endif
837 clkctl = 0;
838 sdh = bus->sdh;
839
840 if (on) {
841 /* Request HT Avail */
842 clkreq =
843 bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
844
Franky Linc05df632011-04-25 19:34:07 -0700845 if ((bus->ci->chip == BCM4329_CHIP_ID)
846 && (bus->ci->chiprev == 0))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700847 clkreq |= SBSDIO_FORCE_ALP;
848
Roland Vossen54ca2962011-06-29 16:46:59 -0700849 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
850 SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700851 if (err) {
852 DHD_ERROR(("%s: HT Avail request error: %d\n",
853 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200854 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700855 }
856
Franky Linc05df632011-04-25 19:34:07 -0700857 if (pendok && ((bus->ci->buscoretype == PCMCIA_CORE_ID)
858 && (bus->ci->buscorerev == 9))) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700859 u32 dummy, retries;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700860 R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
861 }
862
863 /* Check current status */
Roland Vossen54ca2962011-06-29 16:46:59 -0700864 clkctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
865 SBSDIO_FUNC1_CHIPCLKCSR, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700866 if (err) {
867 DHD_ERROR(("%s: HT Avail read error: %d\n",
868 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200869 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700870 }
871
872 /* Go to pending and await interrupt if appropriate */
873 if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
874 /* Allow only clock-available interrupt */
Roland Vossen54ca2962011-06-29 16:46:59 -0700875 devctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
876 SBSDIO_DEVICE_CTL, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700877 if (err) {
878 DHD_ERROR(("%s: Devctl error setting CA: %d\n",
879 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200880 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700881 }
882
883 devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
Roland Vossen54ca2962011-06-29 16:46:59 -0700884 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
885 SBSDIO_DEVICE_CTL, devctl, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700886 DHD_INFO(("CLKCTL: set PENDING\n"));
887 bus->clkstate = CLK_PENDING;
888
Roland Vossena1c5ad82011-04-11 15:16:24 +0200889 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700890 } else if (bus->clkstate == CLK_PENDING) {
891 /* Cancel CA-only interrupt filter */
892 devctl =
Roland Vossen54ca2962011-06-29 16:46:59 -0700893 brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
894 SBSDIO_DEVICE_CTL, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700895 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
Roland Vossen54ca2962011-06-29 16:46:59 -0700896 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
897 SBSDIO_DEVICE_CTL, devctl, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700898 }
899
900 /* Otherwise, wait here (polling) for HT Avail */
901 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
902 SPINWAIT_SLEEP(sdioh_spinwait_sleep,
903 ((clkctl =
Roland Vossen54ca2962011-06-29 16:46:59 -0700904 brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700905 SBSDIO_FUNC1_CHIPCLKCSR,
906 &err)),
907 !SBSDIO_CLKAV(clkctl, bus->alp_only)),
908 PMU_MAX_TRANSITION_DLY);
909 }
910 if (err) {
911 DHD_ERROR(("%s: HT Avail request error: %d\n",
912 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200913 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700914 }
915 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
916 DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
917 __func__, PMU_MAX_TRANSITION_DLY, clkctl));
Roland Vossenb74ac122011-05-03 11:35:20 +0200918 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700919 }
920
921 /* Mark clock available */
922 bus->clkstate = CLK_AVAIL;
923 DHD_INFO(("CLKCTL: turned ON\n"));
924
925#if defined(DHD_DEBUG)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700926 if (bus->alp_only == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700927#if !defined(BCMLXSDMMC)
928 if (!SBSDIO_ALPONLY(clkctl)) {
929 DHD_ERROR(("%s: HT Clock, when ALP Only\n",
930 __func__));
931 }
932#endif /* !defined(BCMLXSDMMC) */
933 } else {
934 if (SBSDIO_ALPONLY(clkctl)) {
935 DHD_ERROR(("%s: HT Clock should be on.\n",
936 __func__));
937 }
938 }
939#endif /* defined (DHD_DEBUG) */
940
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700941 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700942 } else {
943 clkreq = 0;
944
945 if (bus->clkstate == CLK_PENDING) {
946 /* Cancel CA-only interrupt filter */
Roland Vossen54ca2962011-06-29 16:46:59 -0700947 devctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
948 SBSDIO_DEVICE_CTL, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700949 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
Roland Vossen54ca2962011-06-29 16:46:59 -0700950 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
951 SBSDIO_DEVICE_CTL, devctl, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700952 }
953
954 bus->clkstate = CLK_SDONLY;
Roland Vossen54ca2962011-06-29 16:46:59 -0700955 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
956 SBSDIO_FUNC1_CHIPCLKCSR, clkreq, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700957 DHD_INFO(("CLKCTL: turned OFF\n"));
958 if (err) {
959 DHD_ERROR(("%s: Failed access turning clock off: %d\n",
960 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200961 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700962 }
963 }
Roland Vossena1c5ad82011-04-11 15:16:24 +0200964 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700965}
966
967/* Change idle/active SD state */
Roland Vossen54ca2962011-06-29 16:46:59 -0700968static int brcmf_sdbrcm_sdclk(dhd_bus_t *bus, bool on)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700969{
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700970 DHD_TRACE(("%s: Enter\n", __func__));
971
Franky Lin602a8ab2011-06-01 13:45:04 +0200972 if (on)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700973 bus->clkstate = CLK_SDONLY;
Franky Lin602a8ab2011-06-01 13:45:04 +0200974 else
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700975 bus->clkstate = CLK_NONE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700976
Roland Vossena1c5ad82011-04-11 15:16:24 +0200977 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700978}
979
980/* Transition SD and backplane clock readiness */
Roland Vossen54ca2962011-06-29 16:46:59 -0700981static int brcmf_sdbrcm_clkctl(dhd_bus_t *bus, uint target, bool pendok)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700982{
983#ifdef DHD_DEBUG
984 uint oldstate = bus->clkstate;
985#endif /* DHD_DEBUG */
986
987 DHD_TRACE(("%s: Enter\n", __func__));
988
989 /* Early exit if we're already there */
990 if (bus->clkstate == target) {
991 if (target == CLK_AVAIL) {
Arend van Spriela84bac42011-06-29 16:47:10 -0700992 brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700993 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700994 }
Roland Vossena1c5ad82011-04-11 15:16:24 +0200995 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700996 }
997
998 switch (target) {
999 case CLK_AVAIL:
1000 /* Make sure SD clock is available */
1001 if (bus->clkstate == CLK_NONE)
Roland Vossen54ca2962011-06-29 16:46:59 -07001002 brcmf_sdbrcm_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001003 /* Now request HT Avail on the backplane */
Roland Vossen54ca2962011-06-29 16:46:59 -07001004 brcmf_sdbrcm_htclk(bus, true, pendok);
Arend van Spriela84bac42011-06-29 16:47:10 -07001005 brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001006 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001007 break;
1008
1009 case CLK_SDONLY:
1010 /* Remove HT request, or bring up SD clock */
1011 if (bus->clkstate == CLK_NONE)
Roland Vossen54ca2962011-06-29 16:46:59 -07001012 brcmf_sdbrcm_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001013 else if (bus->clkstate == CLK_AVAIL)
Roland Vossen54ca2962011-06-29 16:46:59 -07001014 brcmf_sdbrcm_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001015 else
Roland Vossen54ca2962011-06-29 16:46:59 -07001016 DHD_ERROR(("brcmf_sdbrcm_clkctl: request for %d -> %d"
1017 "\n", bus->clkstate, target));
Arend van Spriela84bac42011-06-29 16:47:10 -07001018 brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001019 break;
1020
1021 case CLK_NONE:
1022 /* Make sure to remove HT request */
1023 if (bus->clkstate == CLK_AVAIL)
Roland Vossen54ca2962011-06-29 16:46:59 -07001024 brcmf_sdbrcm_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001025 /* Now remove the SD clock */
Roland Vossen54ca2962011-06-29 16:46:59 -07001026 brcmf_sdbrcm_sdclk(bus, false);
Arend van Spriela84bac42011-06-29 16:47:10 -07001027 brcmf_os_wd_timer(bus->dhd, 0);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001028 break;
1029 }
1030#ifdef DHD_DEBUG
Roland Vossen54ca2962011-06-29 16:46:59 -07001031 DHD_INFO(("brcmf_sdbrcm_clkctl: %d -> %d\n", oldstate, bus->clkstate));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001032#endif /* DHD_DEBUG */
1033
Roland Vossena1c5ad82011-04-11 15:16:24 +02001034 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001035}
1036
Roland Vossen54ca2962011-06-29 16:46:59 -07001037int brcmf_sdbrcm_bussleep(dhd_bus_t *bus, bool sleep)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001038{
Roland Vossen677226a2011-06-29 16:47:21 -07001039 struct brcmf_sdio *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02001040 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001041 uint retries = 0;
1042
Roland Vossen54ca2962011-06-29 16:46:59 -07001043 DHD_INFO(("brcmf_sdbrcm_bussleep: request %s (currently %s)\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001044 (sleep ? "SLEEP" : "WAKE"),
1045 (bus->sleeping ? "SLEEP" : "WAKE")));
1046
1047 /* Done if we're already in the requested state */
1048 if (sleep == bus->sleeping)
Roland Vossena1c5ad82011-04-11 15:16:24 +02001049 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001050
1051 /* Going to sleep: set the alarm and turn off the lights... */
1052 if (sleep) {
1053 /* Don't sleep if something is pending */
1054 if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
Roland Vossene10d82d2011-05-03 11:35:19 +02001055 return -EBUSY;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001056
1057 /* Disable SDIO interrupts (no longer interested) */
Roland Vossen54ca2962011-06-29 16:46:59 -07001058 brcmf_sdcard_intr_disable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001059
1060 /* Make sure the controller has the bus up */
Roland Vossen54ca2962011-06-29 16:46:59 -07001061 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001062
1063 /* Tell device to start using OOB wakeup */
1064 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
1065 if (retries > retry_limit)
1066 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
1067
1068 /* Turn off our contribution to the HT clock request */
Roland Vossen54ca2962011-06-29 16:46:59 -07001069 brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001070
Roland Vossen54ca2962011-06-29 16:46:59 -07001071 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
1072 SBSDIO_FUNC1_CHIPCLKCSR,
1073 SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001074
1075 /* Isolate the bus */
Franky Linc05df632011-04-25 19:34:07 -07001076 if (bus->ci->chip != BCM4329_CHIP_ID
1077 && bus->ci->chip != BCM4319_CHIP_ID) {
Roland Vossen54ca2962011-06-29 16:46:59 -07001078 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
1079 SBSDIO_DEVICE_CTL,
1080 SBSDIO_DEVCTL_PADS_ISO, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001081 }
1082
1083 /* Change state */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001084 bus->sleeping = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001085
1086 } else {
1087 /* Waking up: bus power up is ok, set local state */
1088
Roland Vossen54ca2962011-06-29 16:46:59 -07001089 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
1090 SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001091
1092 /* Force pad isolation off if possible
1093 (in case power never toggled) */
Franky Linc05df632011-04-25 19:34:07 -07001094 if ((bus->ci->buscoretype == PCMCIA_CORE_ID)
1095 && (bus->ci->buscorerev >= 10))
Roland Vossen54ca2962011-06-29 16:46:59 -07001096 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
1097 SBSDIO_DEVICE_CTL, 0, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001098
1099 /* Make sure the controller has the bus up */
Roland Vossen54ca2962011-06-29 16:46:59 -07001100 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001101
1102 /* Send misc interrupt to indicate OOB not needed */
1103 W_SDREG(0, &regs->tosbmailboxdata, retries);
1104 if (retries <= retry_limit)
1105 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
1106
1107 if (retries > retry_limit)
1108 DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
1109
1110 /* Make sure we have SD bus access */
Roland Vossen54ca2962011-06-29 16:46:59 -07001111 brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001112
1113 /* Change state */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001114 bus->sleeping = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001115
1116 /* Enable interrupts again */
1117 if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001118 bus->intdis = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07001119 brcmf_sdcard_intr_enable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001120 }
1121 }
1122
Roland Vossena1c5ad82011-04-11 15:16:24 +02001123 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001124}
1125
1126#if defined(OOB_INTR_ONLY)
Roland Vossen54ca2962011-06-29 16:46:59 -07001127void brcmf_sdbrcm_enable_oob_intr(struct dhd_bus *bus, bool enable)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001128{
1129#if defined(HW_OOB)
Roland Vossen54ca2962011-06-29 16:46:59 -07001130 brcmf_sdcard_enable_hw_oob_intr(bus->sdh, enable);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001131#else
1132 sdpcmd_regs_t *regs = bus->regs;
1133 uint retries = 0;
1134
Roland Vossen54ca2962011-06-29 16:46:59 -07001135 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001136 if (enable == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001137
1138 /* Tell device to start using OOB wakeup */
1139 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
1140 if (retries > retry_limit)
1141 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
1142
1143 } else {
1144 /* Send misc interrupt to indicate OOB not needed */
1145 W_SDREG(0, &regs->tosbmailboxdata, retries);
1146 if (retries <= retry_limit)
1147 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
1148 }
1149
1150 /* Turn off our contribution to the HT clock request */
Roland Vossen54ca2962011-06-29 16:46:59 -07001151 brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001152#endif /* !defined(HW_OOB) */
1153}
1154#endif /* defined(OOB_INTR_ONLY) */
1155
1156#define BUS_WAKE(bus) \
1157 do { \
1158 if ((bus)->sleeping) \
Roland Vossen54ca2962011-06-29 16:46:59 -07001159 brcmf_sdbrcm_bussleep((bus), false); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001160 } while (0);
1161
1162/* Writes a HW/SW header into the packet and sends it. */
1163/* Assumes: (a) header space already there, (b) caller holds lock */
Roland Vossen54ca2962011-06-29 16:46:59 -07001164static int brcmf_sdbrcm_txpkt(dhd_bus_t *bus, struct sk_buff *pkt, uint chan,
Arend van Sprielc26b1372010-11-23 14:06:23 +01001165 bool free_pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001166{
1167 int ret;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001168 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001169 u16 len, pad = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001170 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001171 uint retries = 0;
Roland Vossen677226a2011-06-29 16:47:21 -07001172 struct brcmf_sdio *sdh;
Arend van Sprielc26b1372010-11-23 14:06:23 +01001173 struct sk_buff *new;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001174 int i;
1175
1176 DHD_TRACE(("%s: Enter\n", __func__));
1177
1178 sdh = bus->sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001179
1180 if (bus->dhd->dongle_reset) {
Roland Vossenb74ac122011-05-03 11:35:20 +02001181 ret = -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001182 goto done;
1183 }
1184
Arend van Spriel54991ad2010-11-23 14:06:24 +01001185 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001186
1187 /* Add alignment padding, allocate new packet if needed */
Arend van Spriel8d825a82011-06-29 16:47:25 -07001188 pad = ((unsigned long)frame % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001189 if (pad) {
Arend van Spriel3be727c2010-11-23 22:20:30 +01001190 if (skb_headroom(pkt) < pad) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001191 DHD_INFO(("%s: insufficient headroom %d for %d pad\n",
Arend van Spriel3be727c2010-11-23 22:20:30 +01001192 __func__, skb_headroom(pkt), pad));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001193 bus->dhd->tx_realloc++;
Arend van Spriel8d825a82011-06-29 16:47:25 -07001194 new = brcmu_pkt_buf_get_skb(pkt->len + BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001195 if (!new) {
1196 DHD_ERROR(("%s: couldn't allocate new %d-byte "
1197 "packet\n",
Arend van Spriel8d825a82011-06-29 16:47:25 -07001198 __func__, pkt->len + BRCMF_SDALIGN));
Roland Vossene10d82d2011-05-03 11:35:19 +02001199 ret = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001200 goto done;
1201 }
1202
Arend van Spriel8d825a82011-06-29 16:47:25 -07001203 PKTALIGN(new, pkt->len, BRCMF_SDALIGN);
Stanislav Fomichev02160692011-02-15 01:05:10 +03001204 memcpy(new->data, pkt->data, pkt->len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001205 if (free_pkt)
Roland Vossen67ad48b2011-06-01 13:45:51 +02001206 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001207 /* free the pkt if canned one is not used */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001208 free_pkt = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001209 pkt = new;
Arend van Spriel54991ad2010-11-23 14:06:24 +01001210 frame = (u8 *) (pkt->data);
Arend van Spriel8d825a82011-06-29 16:47:25 -07001211 ASSERT(((unsigned long)frame % BRCMF_SDALIGN) == 0);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001212 pad = 0;
1213 } else {
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001214 skb_push(pkt, pad);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001215 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001216
Arend van Spriel54991ad2010-11-23 14:06:24 +01001217 ASSERT((pad + SDPCM_HDRLEN) <= (int)(pkt->len));
Brett Rudley9249ede2010-11-30 20:09:49 -08001218 memset(frame, 0, pad + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001219 }
1220 }
Arend van Spriel8d825a82011-06-29 16:47:25 -07001221 ASSERT(pad < BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001222
1223 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Arend van Spriel54991ad2010-11-23 14:06:24 +01001224 len = (u16) (pkt->len);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001225 *(u16 *) frame = cpu_to_le16(len);
1226 *(((u16 *) frame) + 1) = cpu_to_le16(~len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001227
1228 /* Software tag: channel, sequence number, data offset */
1229 swheader =
1230 ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
1231 (((pad +
1232 SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03001233
1234 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
1235 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001236
1237#ifdef DHD_DEBUG
Arend van Spriel54991ad2010-11-23 14:06:24 +01001238 tx_packets[pkt->priority]++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001239 if (DHD_BYTES_ON() &&
1240 (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
1241 (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
Arend van Spriel34227312011-05-10 22:25:32 +02001242 printk(KERN_DEBUG "Tx Frame:\n");
1243 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, frame, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001244 } else if (DHD_HDRS_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02001245 printk(KERN_DEBUG "TxHdr:\n");
1246 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1247 frame, min_t(u16, len, 16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001248 }
1249#endif
1250
1251 /* Raise len to next SDIO block to eliminate tail command */
1252 if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001253 u16 pad = bus->blocksize - (len % bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001254 if ((pad <= bus->roundup) && (pad < bus->blocksize))
1255#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001256 if (pad <= skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001257#endif /* NOTUSED */
1258 len += pad;
Arend van Spriel8d825a82011-06-29 16:47:25 -07001259 } else if (len % BRCMF_SDALIGN) {
1260 len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001261 }
1262
1263 /* Some controllers have trouble with odd bytes -- round to even */
1264 if (forcealign && (len & (ALIGNMENT - 1))) {
1265#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001266 if (skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001267#endif
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001268 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001269#ifdef NOTUSED
1270 else
1271 DHD_ERROR(("%s: sending unrounded %d-byte packet\n",
1272 __func__, len));
1273#endif
1274 }
1275
1276 do {
Roland Vossen54ca2962011-06-29 16:46:59 -07001277 ret = brcmf_sdbrcm_send_buf(bus, brcmf_sdcard_cur_sbwad(sdh),
1278 SDIO_FUNC_2, F2SYNC, frame, len, pkt, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001279 bus->f2txdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02001280 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001281
1282 if (ret < 0) {
1283 /* On failure, abort the command
1284 and terminate the frame */
1285 DHD_INFO(("%s: sdio error %d, abort command and "
1286 "terminate frame.\n", __func__, ret));
1287 bus->tx_sderrs++;
1288
Roland Vossen54ca2962011-06-29 16:46:59 -07001289 brcmf_sdcard_abort(sdh, SDIO_FUNC_2);
1290 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001291 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
1292 NULL);
1293 bus->f1regdata++;
1294
1295 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001296 u8 hi, lo;
Roland Vossen54ca2962011-06-29 16:46:59 -07001297 hi = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001298 SBSDIO_FUNC1_WFRAMEBCHI,
1299 NULL);
Roland Vossen54ca2962011-06-29 16:46:59 -07001300 lo = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001301 SBSDIO_FUNC1_WFRAMEBCLO,
1302 NULL);
1303 bus->f1regdata += 2;
1304 if ((hi == 0) && (lo == 0))
1305 break;
1306 }
1307
1308 }
1309 if (ret == 0)
1310 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1311
1312 } while ((ret < 0) && retrydata && retries++ < TXRETRIES);
1313
1314done:
1315 /* restore pkt buffer pointer before calling tx complete routine */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001316 skb_pull(pkt, SDPCM_HDRLEN + pad);
Arend van Spriela84bac42011-06-29 16:47:10 -07001317 brcmf_os_sdunlock(bus->dhd);
1318 brcmf_txcomplete(bus->dhd, pkt, ret != 0);
1319 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001320
1321 if (free_pkt)
Roland Vossen67ad48b2011-06-01 13:45:51 +02001322 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001323
1324 return ret;
1325}
1326
Roland Vossen54ca2962011-06-29 16:46:59 -07001327int brcmf_sdbrcm_bus_txdata(struct dhd_bus *bus, struct sk_buff *pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001328{
Roland Vossenb74ac122011-05-03 11:35:20 +02001329 int ret = -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001330 uint datalen, prec;
1331
1332 DHD_TRACE(("%s: Enter\n", __func__));
1333
Arend van Spriel54991ad2010-11-23 14:06:24 +01001334 datalen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001335
1336#ifdef SDTEST
1337 /* Push the test header if doing loopback */
1338 if (bus->ext_loop) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001339 u8 *data;
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001340 skb_push(pkt, SDPCM_TEST_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001341 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001342 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001343 *data++ = (u8) bus->loopid++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001344 *data++ = (datalen >> 0);
1345 *data++ = (datalen >> 8);
1346 datalen += SDPCM_TEST_HDRLEN;
1347 }
1348#endif /* SDTEST */
1349
1350 /* Add space for the header */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001351 skb_push(pkt, SDPCM_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001352 ASSERT(IS_ALIGNED((unsigned long)(pkt->data), 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001353
Arend van Spriel54991ad2010-11-23 14:06:24 +01001354 prec = PRIO2PREC((pkt->priority & PRIOMASK));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001355
1356 /* Check for existing queue, current flow-control,
1357 pending event, or pending clock */
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07001358 if (brcmf_deferred_tx || bus->fcstate || pktq_len(&bus->txq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001359 || bus->dpc_sched || (!DATAOK(bus))
1360 || (bus->flowcontrol & NBITVAL(prec))
1361 || (bus->clkstate != CLK_AVAIL)) {
1362 DHD_TRACE(("%s: deferring pktq len %d\n", __func__,
1363 pktq_len(&bus->txq)));
1364 bus->fcqueued++;
1365
1366 /* Priority based enq */
Franky Lin0f1a87c2011-06-29 16:46:32 -07001367 spin_lock_bh(&bus->txqlock);
Arend van Sprielaba5e0a2011-06-29 16:47:03 -07001368 if (brcmf_c_prec_enq(bus->dhd, &bus->txq, pkt, prec) == false) {
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001369 skb_pull(pkt, SDPCM_HDRLEN);
Arend van Spriela84bac42011-06-29 16:47:10 -07001370 brcmf_txcomplete(bus->dhd, pkt, false);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001371 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001372 DHD_ERROR(("%s: out of bus->txq !!!\n", __func__));
Roland Vossene10d82d2011-05-03 11:35:19 +02001373 ret = -ENOSR;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001374 } else {
Roland Vossena1c5ad82011-04-11 15:16:24 +02001375 ret = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001376 }
Franky Lin0f1a87c2011-06-29 16:46:32 -07001377 spin_unlock_bh(&bus->txqlock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001378
Grant Grundler7c316072011-03-09 15:04:15 -08001379 if (pktq_len(&bus->txq) >= TXHI)
Arend van Spriela84bac42011-06-29 16:47:10 -07001380 brcmf_txflowcontrol(bus->dhd, 0, ON);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001381
1382#ifdef DHD_DEBUG
1383 if (pktq_plen(&bus->txq, prec) > qcount[prec])
1384 qcount[prec] = pktq_plen(&bus->txq, prec);
1385#endif
1386 /* Schedule DPC if needed to send queued packet(s) */
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07001387 if (brcmf_deferred_tx && !bus->dpc_sched) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001388 bus->dpc_sched = true;
Arend van Spriela84bac42011-06-29 16:47:10 -07001389 brcmf_sched_dpc(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001390 }
1391 } else {
1392 /* Lock: we're about to use shared data/code (and SDIO) */
Arend van Spriela84bac42011-06-29 16:47:10 -07001393 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001394
1395 /* Otherwise, send it now */
1396 BUS_WAKE(bus);
1397 /* Make sure back plane ht clk is on, no pending allowed */
Roland Vossen54ca2962011-06-29 16:46:59 -07001398 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001399
1400#ifndef SDTEST
1401 DHD_TRACE(("%s: calling txpkt\n", __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07001402 ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001403#else
Roland Vossen54ca2962011-06-29 16:46:59 -07001404 ret = brcmf_sdbrcm_txpkt(bus, pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001405 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001406 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001407#endif
1408 if (ret)
1409 bus->dhd->tx_errors++;
1410 else
1411 bus->dhd->dstats.tx_bytes += datalen;
1412
1413 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001414 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07001415 brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001416 }
1417
Arend van Spriela84bac42011-06-29 16:47:10 -07001418 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001419 }
1420
1421 return ret;
1422}
1423
Roland Vossen54ca2962011-06-29 16:46:59 -07001424static uint brcmf_sdbrcm_sendfromq(dhd_bus_t *bus, uint maxframes)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001425{
Arend van Sprielc26b1372010-11-23 14:06:23 +01001426 struct sk_buff *pkt;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001427 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001428 uint retries = 0;
1429 int ret = 0, prec_out;
1430 uint cnt = 0;
1431 uint datalen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001432 u8 tx_prec_map;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001433
1434 dhd_pub_t *dhd = bus->dhd;
Franky Lin597600a2011-06-01 13:45:39 +02001435 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001436
1437 DHD_TRACE(("%s: Enter\n", __func__));
1438
1439 tx_prec_map = ~bus->flowcontrol;
1440
1441 /* Send frames until the limit or some other event */
1442 for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
Franky Lin0f1a87c2011-06-29 16:46:32 -07001443 spin_lock_bh(&bus->txqlock);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001444 pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001445 if (pkt == NULL) {
Franky Lin0f1a87c2011-06-29 16:46:32 -07001446 spin_unlock_bh(&bus->txqlock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001447 break;
1448 }
Franky Lin0f1a87c2011-06-29 16:46:32 -07001449 spin_unlock_bh(&bus->txqlock);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001450 datalen = pkt->len - SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001451
1452#ifndef SDTEST
Roland Vossen54ca2962011-06-29 16:46:59 -07001453 ret = brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001454#else
Roland Vossen54ca2962011-06-29 16:46:59 -07001455 ret = brcmf_sdbrcm_txpkt(bus, pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001456 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001457 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001458#endif
1459 if (ret)
1460 bus->dhd->tx_errors++;
1461 else
1462 bus->dhd->dstats.tx_bytes += datalen;
1463
1464 /* In poll mode, need to check for other events */
1465 if (!bus->intr && cnt) {
1466 /* Check device status, signal pending interrupt */
1467 R_SDREG(intstatus, &regs->intstatus, retries);
1468 bus->f2txdata++;
Roland Vossen54ca2962011-06-29 16:46:59 -07001469 if (brcmf_sdcard_regfail(bus->sdh))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001470 break;
1471 if (intstatus & bus->hostintmask)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001472 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001473 }
1474 }
1475
1476 /* Deflow-control stack if needed */
Grant Grundler7c316072011-03-09 15:04:15 -08001477 if (dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
Grant Grundler26a71a42011-03-09 10:41:25 -08001478 dhd->txoff && (pktq_len(&bus->txq) < TXLOW))
Arend van Spriela84bac42011-06-29 16:47:10 -07001479 brcmf_txflowcontrol(dhd, 0, OFF);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001480
1481 return cnt;
1482}
1483
Roland Vossen54ca2962011-06-29 16:46:59 -07001484int
1485brcmf_sdbrcm_bus_txctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001486{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001487 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001488 u16 len;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001489 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001490 uint retries = 0;
Roland Vossen677226a2011-06-29 16:47:21 -07001491 struct brcmf_sdio *sdh = bus->sdh;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001492 u8 doff = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001493 int ret = -1;
1494 int i;
1495
1496 DHD_TRACE(("%s: Enter\n", __func__));
1497
1498 if (bus->dhd->dongle_reset)
1499 return -EIO;
1500
1501 /* Back the pointer to make a room for bus header */
1502 frame = msg - SDPCM_HDRLEN;
1503 len = (msglen += SDPCM_HDRLEN);
1504
1505 /* Add alignment padding (optional for ctl frames) */
1506 if (dhd_alignctl) {
Arend van Spriel8d825a82011-06-29 16:47:25 -07001507 doff = ((unsigned long)frame % BRCMF_SDALIGN);
Jason Cooper9b890322010-09-30 15:15:39 -04001508 if (doff) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001509 frame -= doff;
1510 len += doff;
1511 msglen += doff;
Brett Rudley9249ede2010-11-30 20:09:49 -08001512 memset(frame, 0, doff + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001513 }
Arend van Spriel8d825a82011-06-29 16:47:25 -07001514 ASSERT(doff < BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001515 }
1516 doff += SDPCM_HDRLEN;
1517
1518 /* Round send length to next SDIO block */
1519 if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001520 u16 pad = bus->blocksize - (len % bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001521 if ((pad <= bus->roundup) && (pad < bus->blocksize))
1522 len += pad;
Arend van Spriel8d825a82011-06-29 16:47:25 -07001523 } else if (len % BRCMF_SDALIGN) {
1524 len += BRCMF_SDALIGN - (len % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001525 }
1526
1527 /* Satisfy length-alignment requirements */
1528 if (forcealign && (len & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001529 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001530
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001531 ASSERT(IS_ALIGNED((unsigned long)frame, 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001532
1533 /* Need to lock here to protect txseq and SDIO tx calls */
Arend van Spriela84bac42011-06-29 16:47:10 -07001534 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001535
1536 BUS_WAKE(bus);
1537
1538 /* Make sure backplane clock is on */
Roland Vossen54ca2962011-06-29 16:46:59 -07001539 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001540
1541 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001542 *(u16 *) frame = cpu_to_le16((u16) msglen);
1543 *(((u16 *) frame) + 1) = cpu_to_le16(~msglen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001544
1545 /* Software tag: channel, sequence number, data offset */
1546 swheader =
1547 ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
1548 SDPCM_CHANNEL_MASK)
1549 | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
1550 SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03001551 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
1552 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001553
1554 if (!DATAOK(bus)) {
1555 DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
1556 __func__, bus->tx_max, bus->tx_seq));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001557 bus->ctrl_frame_stat = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001558 /* Send from dpc */
1559 bus->ctrl_frame_buf = frame;
1560 bus->ctrl_frame_len = len;
1561
Arend van Spriela84bac42011-06-29 16:47:10 -07001562 brcmf_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001563
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001564 if (bus->ctrl_frame_stat == false) {
1565 DHD_INFO(("%s: ctrl_frame_stat == false\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001566 ret = 0;
1567 } else {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001568 DHD_INFO(("%s: ctrl_frame_stat == true\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001569 ret = -1;
1570 }
1571 }
1572
1573 if (ret == -1) {
1574#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02001575 if (DHD_BYTES_ON() && DHD_CTL_ON()) {
1576 printk(KERN_DEBUG "Tx Frame:\n");
1577 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1578 frame, len);
1579 } else if (DHD_HDRS_ON()) {
1580 printk(KERN_DEBUG "TxHdr:\n");
1581 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1582 frame, min_t(u16, len, 16));
1583 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001584#endif
1585
1586 do {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001587 bus->ctrl_frame_stat = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07001588 ret = brcmf_sdbrcm_send_buf(bus,
1589 brcmf_sdcard_cur_sbwad(sdh), SDIO_FUNC_2,
1590 F2SYNC, frame, len, NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001591
Roland Vossenb7ef2a92011-05-03 11:35:02 +02001592 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001593
1594 if (ret < 0) {
1595 /* On failure, abort the command and
1596 terminate the frame */
1597 DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
1598 __func__, ret));
1599 bus->tx_sderrs++;
1600
Roland Vossen54ca2962011-06-29 16:46:59 -07001601 brcmf_sdcard_abort(sdh, SDIO_FUNC_2);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001602
Roland Vossen54ca2962011-06-29 16:46:59 -07001603 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001604 SBSDIO_FUNC1_FRAMECTRL,
1605 SFC_WF_TERM, NULL);
1606 bus->f1regdata++;
1607
1608 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001609 u8 hi, lo;
Roland Vossen54ca2962011-06-29 16:46:59 -07001610 hi = brcmf_sdcard_cfg_read(sdh,
1611 SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001612 SBSDIO_FUNC1_WFRAMEBCHI,
1613 NULL);
Roland Vossen54ca2962011-06-29 16:46:59 -07001614 lo = brcmf_sdcard_cfg_read(sdh,
1615 SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001616 SBSDIO_FUNC1_WFRAMEBCLO,
Roland Vossen54ca2962011-06-29 16:46:59 -07001617 NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001618 bus->f1regdata += 2;
1619 if ((hi == 0) && (lo == 0))
1620 break;
1621 }
1622
1623 }
1624 if (ret == 0) {
1625 bus->tx_seq =
1626 (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1627 }
1628 } while ((ret < 0) && retries++ < TXRETRIES);
1629 }
1630
1631 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001632 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07001633 brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001634 }
1635
Arend van Spriela84bac42011-06-29 16:47:10 -07001636 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001637
1638 if (ret)
1639 bus->dhd->tx_ctlerrs++;
1640 else
1641 bus->dhd->tx_ctlpkts++;
1642
1643 return ret ? -EIO : 0;
1644}
1645
Roland Vossen54ca2962011-06-29 16:46:59 -07001646int brcmf_sdbrcm_bus_rxctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001647{
1648 int timeleft;
1649 uint rxlen = 0;
1650 bool pending;
1651
1652 DHD_TRACE(("%s: Enter\n", __func__));
1653
1654 if (bus->dhd->dongle_reset)
1655 return -EIO;
1656
1657 /* Wait until control frame is available */
Arend van Spriela84bac42011-06-29 16:47:10 -07001658 timeleft = brcmf_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001659
Arend van Spriela84bac42011-06-29 16:47:10 -07001660 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001661 rxlen = bus->rxlen;
Stanislav Fomichev02160692011-02-15 01:05:10 +03001662 memcpy(msg, bus->rxctl, min(msglen, rxlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001663 bus->rxlen = 0;
Arend van Spriela84bac42011-06-29 16:47:10 -07001664 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001665
1666 if (rxlen) {
1667 DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
1668 __func__, rxlen, msglen));
1669 } else if (timeleft == 0) {
1670 DHD_ERROR(("%s: resumed on timeout\n", __func__));
1671#ifdef DHD_DEBUG
Arend van Spriela84bac42011-06-29 16:47:10 -07001672 brcmf_os_sdlock(bus->dhd);
Roland Vossen54ca2962011-06-29 16:46:59 -07001673 brcmf_sdbrcm_checkdied(bus, NULL, 0);
Arend van Spriela84bac42011-06-29 16:47:10 -07001674 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001675#endif /* DHD_DEBUG */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001676 } else if (pending == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001677 DHD_CTL(("%s: cancelled\n", __func__));
1678 return -ERESTARTSYS;
1679 } else {
1680 DHD_CTL(("%s: resumed for unknown reason?\n", __func__));
1681#ifdef DHD_DEBUG
Arend van Spriela84bac42011-06-29 16:47:10 -07001682 brcmf_os_sdlock(bus->dhd);
Roland Vossen54ca2962011-06-29 16:46:59 -07001683 brcmf_sdbrcm_checkdied(bus, NULL, 0);
Arend van Spriela84bac42011-06-29 16:47:10 -07001684 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001685#endif /* DHD_DEBUG */
1686 }
1687
1688 if (rxlen)
1689 bus->dhd->rx_ctlpkts++;
1690 else
1691 bus->dhd->rx_ctlerrs++;
1692
Jason Coopere9887c92010-10-06 10:08:02 -04001693 return rxlen ? (int)rxlen : -ETIMEDOUT;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001694}
1695
1696/* IOVar table */
1697enum {
1698 IOV_INTR = 1,
1699 IOV_POLLRATE,
1700 IOV_SDREG,
1701 IOV_SBREG,
1702 IOV_SDCIS,
1703 IOV_MEMBYTES,
1704 IOV_MEMSIZE,
1705#ifdef DHD_DEBUG
1706 IOV_CHECKDIED,
1707#endif
1708 IOV_DOWNLOAD,
1709 IOV_FORCEEVEN,
1710 IOV_SDIOD_DRIVE,
1711 IOV_READAHEAD,
1712 IOV_SDRXCHAIN,
1713 IOV_ALIGNCTL,
1714 IOV_SDALIGN,
1715 IOV_DEVRESET,
1716 IOV_CPU,
1717#ifdef SDTEST
1718 IOV_PKTGEN,
1719 IOV_EXTLOOP,
1720#endif /* SDTEST */
1721 IOV_SPROM,
1722 IOV_TXBOUND,
1723 IOV_RXBOUND,
1724 IOV_TXMINMAX,
1725 IOV_IDLETIME,
1726 IOV_IDLECLOCK,
1727 IOV_SD1IDLE,
1728 IOV_SLEEP,
1729 IOV_VARS
1730};
1731
Roland Vossen67ad48b2011-06-01 13:45:51 +02001732const struct brcmu_iovar dhdsdio_iovars[] = {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001733 {"intr", IOV_INTR, 0, IOVT_BOOL, 0},
1734 {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0},
1735 {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0},
1736 {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0},
1737 {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0},
1738 {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0},
1739 {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int)},
1740 {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0},
1741 {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0},
1742 {"vars", IOV_VARS, 0, IOVT_BUFFER, 0},
1743 {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0},
1744 {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0},
1745 {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0},
1746 {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0},
1747 {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0},
1748 {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0},
1749#ifdef DHD_DEBUG
Roland Vossen51851252011-06-29 16:47:24 -07001750 {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001751 ,
Roland Vossen51851252011-06-29 16:47:24 -07001752 {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(struct brcmf_sdreg)}
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001753 ,
1754 {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN}
1755 ,
1756 {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0}
1757 ,
1758 {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0}
1759 ,
1760 {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0}
1761 ,
1762 {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0}
1763 ,
1764 {"cpu", IOV_CPU, 0, IOVT_BOOL, 0}
1765 ,
1766#ifdef DHD_DEBUG
1767 {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0}
1768 ,
1769#endif /* DHD_DEBUG */
1770#endif /* DHD_DEBUG */
1771#ifdef SDTEST
1772 {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0}
1773 ,
Arend van Spriel5e92aa82011-06-29 16:47:08 -07001774 {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(brcmf_pktgen_t)}
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001775 ,
1776#endif /* SDTEST */
1777
1778 {NULL, 0, 0, 0, 0}
1779};
1780
1781static void
Roland Vossen67ad48b2011-06-01 13:45:51 +02001782dhd_dump_pct(struct brcmu_strbuf *strbuf, char *desc, uint num, uint div)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001783{
1784 uint q1, q2;
1785
1786 if (!div) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02001787 brcmu_bprintf(strbuf, "%s N/A", desc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001788 } else {
1789 q1 = num / div;
1790 q2 = (100 * (num - (q1 * div))) / div;
Roland Vossen67ad48b2011-06-01 13:45:51 +02001791 brcmu_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001792 }
1793}
1794
Roland Vossen54ca2962011-06-29 16:46:59 -07001795void brcmf_sdbrcm_bus_dump(dhd_pub_t *dhdp, struct brcmu_strbuf *strbuf)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001796{
1797 dhd_bus_t *bus = dhdp->bus;
1798
Roland Vossen67ad48b2011-06-01 13:45:51 +02001799 brcmu_bprintf(strbuf, "Bus SDIO structure:\n");
1800 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001801 "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
1802 bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001803 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001804 "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
1805 bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max,
1806 bus->rxskip, bus->rxlen, bus->rx_seq);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001807 brcmu_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001808 bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001809 brcmu_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001810 bus->pollrate, bus->pollcnt, bus->regfails);
1811
Roland Vossen67ad48b2011-06-01 13:45:51 +02001812 brcmu_bprintf(strbuf, "\nAdditional counters:\n");
1813 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001814 "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
1815 bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
1816 bus->rxc_errors);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001817 brcmu_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001818 bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001819 brcmu_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
1820 bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
1821 brcmu_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001822 bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001823 brcmu_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs"
1824 " %d\n",
1825 (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs,
1826 bus->f2rxdata, bus->f2txdata, bus->f1regdata);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001827 {
1828 dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
1829 (bus->f2rxhdrs + bus->f2rxdata));
1830 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets,
1831 bus->f1regdata);
1832 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
1833 (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
1834 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets,
1835 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001836 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001837
1838 dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
1839 bus->dhd->rx_packets);
1840 dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts,
1841 bus->rxglomframes);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001842 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001843
1844 dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets,
1845 bus->f2txdata);
1846 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets,
1847 bus->f1regdata);
1848 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets,
1849 (bus->f2txdata + bus->f1regdata));
1850 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets,
1851 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001852 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001853
1854 dhd_dump_pct(strbuf, "Total: pkts/f2rw",
1855 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1856 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
1857 dhd_dump_pct(strbuf, ", pkts/f1sd",
1858 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1859 bus->f1regdata);
1860 dhd_dump_pct(strbuf, ", pkts/sd",
1861 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1862 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata +
1863 bus->f1regdata));
1864 dhd_dump_pct(strbuf, ", pkts/int",
1865 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1866 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001867 brcmu_bprintf(strbuf, "\n\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001868 }
1869
1870#ifdef SDTEST
1871 if (bus->pktgen_count) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02001872 brcmu_bprintf(strbuf, "pktgen config and count:\n");
1873 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001874 "freq %d count %d print %d total %d min %d len %d\n",
1875 bus->pktgen_freq, bus->pktgen_count,
1876 bus->pktgen_print, bus->pktgen_total,
1877 bus->pktgen_minlen, bus->pktgen_maxlen);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001878 brcmu_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001879 bus->pktgen_sent, bus->pktgen_rcvd,
1880 bus->pktgen_fail);
1881 }
1882#endif /* SDTEST */
1883#ifdef DHD_DEBUG
Roland Vossen67ad48b2011-06-01 13:45:51 +02001884 brcmu_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001885 bus->dpc_sched,
Roland Vossen54ca2962011-06-29 16:46:59 -07001886 (brcmf_sdcard_intr_pending(bus->sdh) ? " " : " not "));
Roland Vossen67ad48b2011-06-01 13:45:51 +02001887 brcmu_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001888 bus->roundup);
1889#endif /* DHD_DEBUG */
Roland Vossen67ad48b2011-06-01 13:45:51 +02001890 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001891 "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
1892 bus->clkstate, bus->activity, bus->idletime, bus->idlecount,
1893 bus->sleeping);
1894}
1895
1896void dhd_bus_clearcounts(dhd_pub_t *dhdp)
1897{
1898 dhd_bus_t *bus = (dhd_bus_t *) dhdp->bus;
1899
1900 bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
1901 bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
1902 bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
1903 bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
1904 bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
1905 bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
1906}
1907
1908#ifdef SDTEST
Roland Vossen54ca2962011-06-29 16:46:59 -07001909static int brcmf_sdbrcm_pktgen_get(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001910{
Arend van Spriel5e92aa82011-06-29 16:47:08 -07001911 brcmf_pktgen_t pktgen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001912
1913 pktgen.version = DHD_PKTGEN_VERSION;
1914 pktgen.freq = bus->pktgen_freq;
1915 pktgen.count = bus->pktgen_count;
1916 pktgen.print = bus->pktgen_print;
1917 pktgen.total = bus->pktgen_total;
1918 pktgen.minlen = bus->pktgen_minlen;
1919 pktgen.maxlen = bus->pktgen_maxlen;
1920 pktgen.numsent = bus->pktgen_sent;
1921 pktgen.numrcvd = bus->pktgen_rcvd;
1922 pktgen.numfail = bus->pktgen_fail;
1923 pktgen.mode = bus->pktgen_mode;
1924 pktgen.stop = bus->pktgen_stop;
1925
Stanislav Fomichev02160692011-02-15 01:05:10 +03001926 memcpy(arg, &pktgen, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001927
1928 return 0;
1929}
1930
Roland Vossen54ca2962011-06-29 16:46:59 -07001931static int brcmf_sdbrcm_pktgen_set(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001932{
Arend van Spriel5e92aa82011-06-29 16:47:08 -07001933 brcmf_pktgen_t pktgen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001934 uint oldcnt, oldmode;
1935
Stanislav Fomichev02160692011-02-15 01:05:10 +03001936 memcpy(&pktgen, arg, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001937 if (pktgen.version != DHD_PKTGEN_VERSION)
Roland Vossene10d82d2011-05-03 11:35:19 +02001938 return -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001939
1940 oldcnt = bus->pktgen_count;
1941 oldmode = bus->pktgen_mode;
1942
1943 bus->pktgen_freq = pktgen.freq;
1944 bus->pktgen_count = pktgen.count;
1945 bus->pktgen_print = pktgen.print;
1946 bus->pktgen_total = pktgen.total;
1947 bus->pktgen_minlen = pktgen.minlen;
1948 bus->pktgen_maxlen = pktgen.maxlen;
1949 bus->pktgen_mode = pktgen.mode;
1950 bus->pktgen_stop = pktgen.stop;
1951
1952 bus->pktgen_tick = bus->pktgen_ptick = 0;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001953 bus->pktgen_len = max(bus->pktgen_len, bus->pktgen_minlen);
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001954 bus->pktgen_len = min(bus->pktgen_len, bus->pktgen_maxlen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001955
1956 /* Clear counts for a new pktgen (mode change, or was stopped) */
1957 if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
1958 bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
1959
1960 return 0;
1961}
1962#endif /* SDTEST */
1963
1964static int
Roland Vossen54ca2962011-06-29 16:46:59 -07001965brcmf_sdbrcm_membytes(dhd_bus_t *bus, bool write, u32 address, u8 *data,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001966 uint size)
1967{
1968 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001969 u32 sdaddr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001970 uint dsize;
1971
1972 /* Determine initial transfer parameters */
1973 sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
1974 if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
1975 dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
1976 else
1977 dsize = size;
1978
1979 /* Set the backplane window to include the start address */
Roland Vossen54ca2962011-06-29 16:46:59 -07001980 bcmerror = brcmf_sdbrcm_set_siaddr_window(bus, address);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001981 if (bcmerror) {
1982 DHD_ERROR(("%s: window change failed\n", __func__));
1983 goto xfer_done;
1984 }
1985
1986 /* Do the transfer(s) */
1987 while (size) {
1988 DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n",
1989 __func__, (write ? "write" : "read"), dsize,
1990 sdaddr, (address & SBSDIO_SBWINDOW_MASK)));
1991 bcmerror =
Roland Vossen54ca2962011-06-29 16:46:59 -07001992 brcmf_sdcard_rwdata(bus->sdh, write, sdaddr, data, dsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001993 if (bcmerror) {
1994 DHD_ERROR(("%s: membytes transfer failed\n", __func__));
1995 break;
1996 }
1997
1998 /* Adjust for next transfer (if any) */
1999 size -= dsize;
2000 if (size) {
2001 data += dsize;
2002 address += dsize;
Roland Vossen54ca2962011-06-29 16:46:59 -07002003 bcmerror = brcmf_sdbrcm_set_siaddr_window(bus, address);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002004 if (bcmerror) {
2005 DHD_ERROR(("%s: window change failed\n",
2006 __func__));
2007 break;
2008 }
2009 sdaddr = 0;
Greg Kroah-Hartmanb61640d2010-10-08 12:37:39 -07002010 dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002011 }
2012 }
2013
2014xfer_done:
2015 /* Return the window to backplane enumeration space for core access */
Roland Vossen54ca2962011-06-29 16:46:59 -07002016 if (brcmf_sdbrcm_set_siaddr_window(bus,
2017 brcmf_sdcard_cur_sbwad(bus->sdh))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002018 DHD_ERROR(("%s: FAILED to set window back to 0x%x\n",
Roland Vossen54ca2962011-06-29 16:46:59 -07002019 __func__, brcmf_sdcard_cur_sbwad(bus->sdh)));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002020 }
2021
2022 return bcmerror;
2023}
2024
2025#ifdef DHD_DEBUG
Roland Vossen54ca2962011-06-29 16:46:59 -07002026static int brcmf_sdbrcm_readshared(dhd_bus_t *bus, struct sdpcm_shared *sh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002027{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002028 u32 addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002029 int rv;
2030
2031 /* Read last word in memory to determine address of
2032 sdpcm_shared structure */
Roland Vossen54ca2962011-06-29 16:46:59 -07002033 rv = brcmf_sdbrcm_membytes(bus, false, bus->ramsize - 4, (u8 *)&addr,
2034 4);
Jason Cooper9b890322010-09-30 15:15:39 -04002035 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002036 return rv;
2037
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002038 addr = le32_to_cpu(addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002039
2040 DHD_INFO(("sdpcm_shared address 0x%08X\n", addr));
2041
2042 /*
2043 * Check if addr is valid.
2044 * NVRAM length at the end of memory should have been overwritten.
2045 */
2046 if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
2047 DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n",
2048 __func__, addr));
Roland Vossenb74ac122011-05-03 11:35:20 +02002049 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002050 }
2051
Roland Vossen70963f92011-06-01 13:45:08 +02002052 /* Read rte_shared structure */
Roland Vossen54ca2962011-06-29 16:46:59 -07002053 rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *) sh,
Franky Linb49b14d2011-06-01 13:45:37 +02002054 sizeof(struct sdpcm_shared));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002055 if (rv < 0)
2056 return rv;
2057
2058 /* Endianness */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002059 sh->flags = le32_to_cpu(sh->flags);
2060 sh->trap_addr = le32_to_cpu(sh->trap_addr);
2061 sh->assert_exp_addr = le32_to_cpu(sh->assert_exp_addr);
2062 sh->assert_file_addr = le32_to_cpu(sh->assert_file_addr);
2063 sh->assert_line = le32_to_cpu(sh->assert_line);
2064 sh->console_addr = le32_to_cpu(sh->console_addr);
2065 sh->msgtrace_addr = le32_to_cpu(sh->msgtrace_addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002066
2067 if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
2068 DHD_ERROR(("%s: sdpcm_shared version %d in dhd "
2069 "is different than sdpcm_shared version %d in dongle\n",
2070 __func__, SDPCM_SHARED_VERSION,
2071 sh->flags & SDPCM_SHARED_VERSION_MASK));
Roland Vossenb74ac122011-05-03 11:35:20 +02002072 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002073 }
2074
Roland Vossena1c5ad82011-04-11 15:16:24 +02002075 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002076}
2077
Roland Vossen54ca2962011-06-29 16:46:59 -07002078static int brcmf_sdbrcm_checkdied(dhd_bus_t *bus, u8 *data, uint size)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002079{
2080 int bcmerror = 0;
2081 uint msize = 512;
2082 char *mbuffer = NULL;
2083 uint maxstrlen = 256;
2084 char *str = NULL;
Roland Vossen51851252011-06-29 16:47:24 -07002085 struct brcmf_trap tr;
Franky Linb49b14d2011-06-01 13:45:37 +02002086 struct sdpcm_shared sdpcm_shared;
Roland Vossen67ad48b2011-06-01 13:45:51 +02002087 struct brcmu_strbuf strbuf;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002088
2089 DHD_TRACE(("%s: Enter\n", __func__));
2090
2091 if (data == NULL) {
2092 /*
2093 * Called after a rx ctrl timeout. "data" is NULL.
2094 * allocate memory to trace the trap or assert.
2095 */
2096 size = msize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002097 mbuffer = data = kmalloc(msize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002098 if (mbuffer == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002099 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002100 msize));
Roland Vossene10d82d2011-05-03 11:35:19 +02002101 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002102 goto done;
2103 }
2104 }
2105
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002106 str = kmalloc(maxstrlen, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04002107 if (str == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002108 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__, maxstrlen));
Roland Vossene10d82d2011-05-03 11:35:19 +02002109 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002110 goto done;
2111 }
2112
Roland Vossen54ca2962011-06-29 16:46:59 -07002113 bcmerror = brcmf_sdbrcm_readshared(bus, &sdpcm_shared);
Jason Cooper9b890322010-09-30 15:15:39 -04002114 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002115 goto done;
2116
Roland Vossen67ad48b2011-06-01 13:45:51 +02002117 brcmu_binit(&strbuf, data, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002118
Roland Vossen67ad48b2011-06-01 13:45:51 +02002119 brcmu_bprintf(&strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002120 "msgtrace address : 0x%08X\nconsole address : 0x%08X\n",
2121 sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
2122
2123 if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
2124 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
2125 * (Avoids conflict with real asserts for programmatic
2126 * parsing of output.)
2127 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002128 brcmu_bprintf(&strbuf, "Assrt not built in dongle\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002129 }
2130
2131 if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) ==
2132 0) {
2133 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
2134 * (Avoids conflict with real asserts for programmatic
2135 * parsing of output.)
2136 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002137 brcmu_bprintf(&strbuf, "No trap%s in dongle",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002138 (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
2139 ? "/assrt" : "");
2140 } else {
2141 if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
2142 /* Download assert */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002143 brcmu_bprintf(&strbuf, "Dongle assert");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002144 if (sdpcm_shared.assert_exp_addr != 0) {
2145 str[0] = '\0';
Roland Vossen54ca2962011-06-29 16:46:59 -07002146 bcmerror = brcmf_sdbrcm_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04002147 sdpcm_shared.assert_exp_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002148 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04002149 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002150 goto done;
2151
2152 str[maxstrlen - 1] = '\0';
Roland Vossen67ad48b2011-06-01 13:45:51 +02002153 brcmu_bprintf(&strbuf, " expr \"%s\"", str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002154 }
2155
2156 if (sdpcm_shared.assert_file_addr != 0) {
2157 str[0] = '\0';
Roland Vossen54ca2962011-06-29 16:46:59 -07002158 bcmerror = brcmf_sdbrcm_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04002159 sdpcm_shared.assert_file_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002160 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04002161 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002162 goto done;
2163
2164 str[maxstrlen - 1] = '\0';
Roland Vossen67ad48b2011-06-01 13:45:51 +02002165 brcmu_bprintf(&strbuf, " file \"%s\"", str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002166 }
2167
Roland Vossen67ad48b2011-06-01 13:45:51 +02002168 brcmu_bprintf(&strbuf, " line %d ",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002169 sdpcm_shared.assert_line);
2170 }
2171
2172 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
Roland Vossen54ca2962011-06-29 16:46:59 -07002173 bcmerror = brcmf_sdbrcm_membytes(bus, false,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002174 sdpcm_shared.trap_addr, (u8 *)&tr,
Roland Vossen51851252011-06-29 16:47:24 -07002175 sizeof(struct brcmf_trap));
Jason Cooper9b890322010-09-30 15:15:39 -04002176 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002177 goto done;
2178
Roland Vossen67ad48b2011-06-01 13:45:51 +02002179 brcmu_bprintf(&strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002180 "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
2181 "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
2182 "r0 0x%x, r1 0x%x, r2 0x%x, r3 0x%x, r4 0x%x, r5 0x%x, r6 0x%x, r7 0x%x\n",
2183 tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13,
2184 tr.r14, tr.pc, sdpcm_shared.trap_addr,
2185 tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5,
2186 tr.r6, tr.r7);
2187 }
2188 }
2189
2190 if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP))
2191 DHD_ERROR(("%s: %s\n", __func__, strbuf.origbuf));
2192
2193#ifdef DHD_DEBUG
2194 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
2195 /* Mem dump to a file on device */
Roland Vossen54ca2962011-06-29 16:46:59 -07002196 brcmf_sdbrcm_mem_dump(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002197 }
2198#endif /* DHD_DEBUG */
2199
2200done:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002201 kfree(mbuffer);
2202 kfree(str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002203
2204 return bcmerror;
2205}
2206
Roland Vossen54ca2962011-06-29 16:46:59 -07002207static int brcmf_sdbrcm_mem_dump(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002208{
2209 int ret = 0;
2210 int size; /* Full mem size */
2211 int start = 0; /* Start address */
2212 int read_size = 0; /* Read size of each iteration */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002213 u8 *buf = NULL, *databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002214
2215 /* Get full mem size */
2216 size = bus->ramsize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002217 buf = kmalloc(size, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002218 if (!buf) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002219 DHD_ERROR(("%s: Out of memory (%d bytes)\n", __func__, size));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002220 return -1;
2221 }
2222
2223 /* Read mem content */
Arend van Spriel0bef7742011-02-10 12:03:44 +01002224 printk(KERN_DEBUG "Dump dongle memory");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002225 databuf = buf;
2226 while (size) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07002227 read_size = min(MEMBLOCK, size);
Roland Vossen54ca2962011-06-29 16:46:59 -07002228 ret = brcmf_sdbrcm_membytes(bus, false, start, databuf,
2229 read_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002230 if (ret) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002231 DHD_ERROR(("%s: Error membytes %d\n", __func__, ret));
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002232 kfree(buf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002233 return -1;
2234 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01002235 printk(".");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002236
2237 /* Decrement size and increment start address */
2238 size -= read_size;
2239 start += read_size;
2240 databuf += read_size;
2241 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01002242 printk(KERN_DEBUG "Done\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002243
2244 /* free buf before return !!! */
Arend van Spriela84bac42011-06-29 16:47:10 -07002245 if (brcmf_write_to_file(bus->dhd, buf, bus->ramsize)) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002246 DHD_ERROR(("%s: Error writing to files\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002247 return -1;
2248 }
2249
Arend van Spriela84bac42011-06-29 16:47:10 -07002250 /* buf free handled in brcmf_write_to_file, not here */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002251 return 0;
2252}
2253
2254#define CONSOLE_LINE_MAX 192
2255
Roland Vossen54ca2962011-06-29 16:46:59 -07002256static int brcmf_sdbrcm_readconsole(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002257{
Roland Vossen51851252011-06-29 16:47:24 -07002258 struct dhd_console *c = &bus->console;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002259 u8 line[CONSOLE_LINE_MAX], ch;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002260 u32 n, idx, addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002261 int rv;
2262
2263 /* Don't do anything until FWREADY updates console address */
2264 if (bus->console_addr == 0)
2265 return 0;
2266
2267 /* Read console log struct */
Roland Vossen51851252011-06-29 16:47:24 -07002268 addr = bus->console_addr + offsetof(struct rte_console, log);
Roland Vossen54ca2962011-06-29 16:46:59 -07002269 rv = brcmf_sdbrcm_membytes(bus, false, addr, (u8 *)&c->log,
Jason Cooper9b890322010-09-30 15:15:39 -04002270 sizeof(c->log));
2271 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002272 return rv;
2273
2274 /* Allocate console buffer (one time only) */
2275 if (c->buf == NULL) {
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002276 c->bufsize = le32_to_cpu(c->log.buf_size);
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002277 c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04002278 if (c->buf == NULL)
Roland Vossene10d82d2011-05-03 11:35:19 +02002279 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002280 }
2281
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002282 idx = le32_to_cpu(c->log.idx);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002283
2284 /* Protect against corrupt value */
2285 if (idx > c->bufsize)
Roland Vossenb74ac122011-05-03 11:35:20 +02002286 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002287
2288 /* Skip reading the console buffer if the index pointer
2289 has not moved */
2290 if (idx == c->last)
Roland Vossena1c5ad82011-04-11 15:16:24 +02002291 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002292
2293 /* Read the console buffer */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002294 addr = le32_to_cpu(c->log.buf);
Roland Vossen54ca2962011-06-29 16:46:59 -07002295 rv = brcmf_sdbrcm_membytes(bus, false, addr, c->buf, c->bufsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002296 if (rv < 0)
2297 return rv;
2298
2299 while (c->last != idx) {
2300 for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
2301 if (c->last == idx) {
2302 /* This would output a partial line.
2303 * Instead, back up
2304 * the buffer pointer and output this
2305 * line next time around.
2306 */
2307 if (c->last >= n)
2308 c->last -= n;
2309 else
2310 c->last = c->bufsize - n;
2311 goto break2;
2312 }
2313 ch = c->buf[c->last];
2314 c->last = (c->last + 1) % c->bufsize;
2315 if (ch == '\n')
2316 break;
2317 line[n] = ch;
2318 }
2319
2320 if (n > 0) {
2321 if (line[n - 1] == '\r')
2322 n--;
2323 line[n] = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01002324 printk(KERN_DEBUG "CONSOLE: %s\n", line);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002325 }
2326 }
2327break2:
2328
Roland Vossena1c5ad82011-04-11 15:16:24 +02002329 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002330}
2331#endif /* DHD_DEBUG */
2332
Roland Vossen54ca2962011-06-29 16:46:59 -07002333int brcmf_sdbrcm_downloadvars(dhd_bus_t *bus, void *arg, int len)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002334{
Roland Vossena1c5ad82011-04-11 15:16:24 +02002335 int bcmerror = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002336
2337 DHD_TRACE(("%s: Enter\n", __func__));
2338
2339 /* Basic sanity checks */
2340 if (bus->dhd->up) {
Roland Vossenb74ac122011-05-03 11:35:20 +02002341 bcmerror = -EISCONN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002342 goto err;
2343 }
2344 if (!len) {
Roland Vossene10d82d2011-05-03 11:35:19 +02002345 bcmerror = -EOVERFLOW;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002346 goto err;
2347 }
2348
2349 /* Free the old ones and replace with passed variables */
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002350 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002351
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002352 bus->vars = kmalloc(len, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002353 bus->varsz = bus->vars ? len : 0;
2354 if (bus->vars == NULL) {
Roland Vossene10d82d2011-05-03 11:35:19 +02002355 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002356 goto err;
2357 }
2358
2359 /* Copy the passed variables, which should include the
2360 terminating double-null */
Stanislav Fomichev02160692011-02-15 01:05:10 +03002361 memcpy(bus->vars, arg, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002362err:
2363 return bcmerror;
2364}
2365
2366static int
Roland Vossen54ca2962011-06-29 16:46:59 -07002367brcmf_sdbrcm_doiovar(dhd_bus_t *bus, const struct brcmu_iovar *vi, u32 actionid,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002368 const char *name, void *params, int plen, void *arg, int len,
2369 int val_size)
2370{
2371 int bcmerror = 0;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002372 s32 int_val = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002373 bool bool_val = 0;
2374
2375 DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p "
2376 "len %d val_size %d\n",
2377 __func__, actionid, name, params, plen, arg, len, val_size));
2378
Roland Vossen67ad48b2011-06-01 13:45:51 +02002379 bcmerror = brcmu_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002380 if (bcmerror != 0)
2381 goto exit;
2382
2383 if (plen >= (int)sizeof(int_val))
Stanislav Fomichev02160692011-02-15 01:05:10 +03002384 memcpy(&int_val, params, sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002385
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002386 bool_val = (int_val != 0) ? true : false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002387
2388 /* Some ioctls use the bus */
Arend van Spriela84bac42011-06-29 16:47:10 -07002389 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002390
2391 /* Check if dongle is in reset. If so, only allow DEVRESET iovars */
2392 if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
2393 actionid == IOV_GVAL(IOV_DEVRESET))) {
Roland Vossenb74ac122011-05-03 11:35:20 +02002394 bcmerror = -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002395 goto exit;
2396 }
2397
2398 /* Handle sleep stuff before any clock mucking */
2399 if (vi->varid == IOV_SLEEP) {
2400 if (IOV_ISSET(actionid)) {
Roland Vossen54ca2962011-06-29 16:46:59 -07002401 bcmerror = brcmf_sdbrcm_bussleep(bus, bool_val);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002402 } else {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002403 int_val = (s32) bus->sleeping;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002404 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002405 }
2406 goto exit;
2407 }
2408
2409 /* Request clock to allow SDIO accesses */
2410 if (!bus->dhd->dongle_reset) {
2411 BUS_WAKE(bus);
Roland Vossen54ca2962011-06-29 16:46:59 -07002412 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002413 }
2414
2415 switch (actionid) {
2416 case IOV_GVAL(IOV_INTR):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002417 int_val = (s32) bus->intr;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002418 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002419 break;
2420
2421 case IOV_SVAL(IOV_INTR):
2422 bus->intr = bool_val;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002423 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002424 if (bus->dhd->up) {
2425 if (bus->intr) {
2426 DHD_INTR(("%s: enable SDIO device interrupts\n",
2427 __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07002428 brcmf_sdcard_intr_enable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002429 } else {
2430 DHD_INTR(("%s: disable SDIO interrupts\n",
2431 __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07002432 brcmf_sdcard_intr_disable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002433 }
2434 }
2435 break;
2436
2437 case IOV_GVAL(IOV_POLLRATE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002438 int_val = (s32) bus->pollrate;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002439 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002440 break;
2441
2442 case IOV_SVAL(IOV_POLLRATE):
2443 bus->pollrate = (uint) int_val;
2444 bus->poll = (bus->pollrate != 0);
2445 break;
2446
2447 case IOV_GVAL(IOV_IDLETIME):
2448 int_val = bus->idletime;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002449 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002450 break;
2451
2452 case IOV_SVAL(IOV_IDLETIME):
2453 if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE))
Roland Vossene10d82d2011-05-03 11:35:19 +02002454 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002455 else
2456 bus->idletime = int_val;
2457 break;
2458
2459 case IOV_GVAL(IOV_IDLECLOCK):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002460 int_val = (s32) bus->idleclock;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002461 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002462 break;
2463
2464 case IOV_SVAL(IOV_IDLECLOCK):
2465 bus->idleclock = int_val;
2466 break;
2467
2468 case IOV_GVAL(IOV_SD1IDLE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002469 int_val = (s32) sd1idle;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002470 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002471 break;
2472
2473 case IOV_SVAL(IOV_SD1IDLE):
2474 sd1idle = bool_val;
2475 break;
2476
2477 case IOV_SVAL(IOV_MEMBYTES):
2478 case IOV_GVAL(IOV_MEMBYTES):
2479 {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002480 u32 address;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002481 uint size, dsize;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002482 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002483
2484 bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
2485
2486 ASSERT(plen >= 2 * sizeof(int));
2487
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002488 address = (u32) int_val;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002489 memcpy(&int_val, (char *)params + sizeof(int_val),
2490 sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002491 size = (uint) int_val;
2492
2493 /* Do some validation */
2494 dsize = set ? plen - (2 * sizeof(int)) : len;
2495 if (dsize < size) {
2496 DHD_ERROR(("%s: error on %s membytes, addr "
2497 "0x%08x size %d dsize %d\n",
2498 __func__, (set ? "set" : "get"),
2499 address, size, dsize));
Roland Vossene10d82d2011-05-03 11:35:19 +02002500 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002501 break;
2502 }
2503
2504 DHD_INFO(("%s: Request to %s %d bytes at address "
2505 "0x%08x\n",
2506 __func__, (set ? "write" : "read"), size, address));
2507
2508 /* If we know about SOCRAM, check for a fit */
2509 if ((bus->orig_ramsize) &&
2510 ((address > bus->orig_ramsize)
2511 || (address + size > bus->orig_ramsize))) {
2512 DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d "
2513 "bytes at 0x%08x\n",
2514 __func__, bus->orig_ramsize, size, address));
Roland Vossene10d82d2011-05-03 11:35:19 +02002515 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002516 break;
2517 }
2518
2519 /* Generate the actual data pointer */
2520 data =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002521 set ? (u8 *) params +
2522 2 * sizeof(int) : (u8 *) arg;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002523
2524 /* Call to do the transfer */
Roland Vossen54ca2962011-06-29 16:46:59 -07002525 bcmerror = brcmf_sdbrcm_membytes(bus, set, address,
2526 data, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002527
2528 break;
2529 }
2530
2531 case IOV_GVAL(IOV_MEMSIZE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002532 int_val = (s32) bus->ramsize;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002533 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002534 break;
2535
2536 case IOV_GVAL(IOV_SDIOD_DRIVE):
Arend van Spriel5e92aa82011-06-29 16:47:08 -07002537 int_val = (s32) brcmf_sdiod_drive_strength;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002538 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002539 break;
2540
2541 case IOV_SVAL(IOV_SDIOD_DRIVE):
Arend van Spriel5e92aa82011-06-29 16:47:08 -07002542 brcmf_sdiod_drive_strength = int_val;
Roland Vossen54ca2962011-06-29 16:46:59 -07002543 brcmf_sdbrcm_sdiod_drive_strength_init(bus,
Arend van Spriel5e92aa82011-06-29 16:47:08 -07002544 brcmf_sdiod_drive_strength);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002545 break;
2546
2547 case IOV_SVAL(IOV_DOWNLOAD):
Roland Vossen54ca2962011-06-29 16:46:59 -07002548 bcmerror = brcmf_sdbrcm_download_state(bus, bool_val);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002549 break;
2550
2551 case IOV_SVAL(IOV_VARS):
Roland Vossen54ca2962011-06-29 16:46:59 -07002552 bcmerror = brcmf_sdbrcm_downloadvars(bus, arg, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002553 break;
2554
2555 case IOV_GVAL(IOV_READAHEAD):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002556 int_val = (s32) dhd_readahead;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002557 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002558 break;
2559
2560 case IOV_SVAL(IOV_READAHEAD):
2561 if (bool_val && !dhd_readahead)
2562 bus->nextlen = 0;
2563 dhd_readahead = bool_val;
2564 break;
2565
2566 case IOV_GVAL(IOV_SDRXCHAIN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002567 int_val = (s32) bus->use_rxchain;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002568 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002569 break;
2570
2571 case IOV_SVAL(IOV_SDRXCHAIN):
2572 if (bool_val && !bus->sd_rxchain)
Roland Vossene10d82d2011-05-03 11:35:19 +02002573 bcmerror = -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002574 else
2575 bus->use_rxchain = bool_val;
2576 break;
2577 case IOV_GVAL(IOV_ALIGNCTL):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002578 int_val = (s32) dhd_alignctl;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002579 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002580 break;
2581
2582 case IOV_SVAL(IOV_ALIGNCTL):
2583 dhd_alignctl = bool_val;
2584 break;
2585
2586 case IOV_GVAL(IOV_SDALIGN):
Arend van Spriel8d825a82011-06-29 16:47:25 -07002587 int_val = BRCMF_SDALIGN;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002588 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002589 break;
2590
2591#ifdef DHD_DEBUG
2592 case IOV_GVAL(IOV_VARS):
2593 if (bus->varsz < (uint) len)
Stanislav Fomichev02160692011-02-15 01:05:10 +03002594 memcpy(arg, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002595 else
Roland Vossene10d82d2011-05-03 11:35:19 +02002596 bcmerror = -EOVERFLOW;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002597 break;
2598#endif /* DHD_DEBUG */
2599
2600#ifdef DHD_DEBUG
2601 case IOV_GVAL(IOV_SDREG):
2602 {
Roland Vossen51851252011-06-29 16:47:24 -07002603 struct brcmf_sdreg *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002604 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002605
Roland Vossen51851252011-06-29 16:47:24 -07002606 sd_ptr = (struct brcmf_sdreg *) params;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002607
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002608 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002609 size = sd_ptr->func;
Roland Vossen54ca2962011-06-29 16:46:59 -07002610 int_val = (s32) brcmf_sdcard_reg_read(bus->sdh, addr,
2611 size);
2612 if (brcmf_sdcard_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002613 bcmerror = -EIO;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002614 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002615 break;
2616 }
2617
2618 case IOV_SVAL(IOV_SDREG):
2619 {
Roland Vossen51851252011-06-29 16:47:24 -07002620 struct brcmf_sdreg *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002621 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002622
Roland Vossen51851252011-06-29 16:47:24 -07002623 sd_ptr = (struct brcmf_sdreg *) params;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002624
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002625 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002626 size = sd_ptr->func;
Roland Vossen54ca2962011-06-29 16:46:59 -07002627 brcmf_sdcard_reg_write(bus->sdh, addr, size,
2628 sd_ptr->value);
2629 if (brcmf_sdcard_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002630 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002631 break;
2632 }
2633
2634 /* Same as above, but offset is not backplane
2635 (not SDIO core) */
2636 case IOV_GVAL(IOV_SBREG):
2637 {
Roland Vossen51851252011-06-29 16:47:24 -07002638 struct brcmf_sdreg sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002639 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002640
Stanislav Fomichev02160692011-02-15 01:05:10 +03002641 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002642
2643 addr = SI_ENUM_BASE + sdreg.offset;
2644 size = sdreg.func;
Roland Vossen54ca2962011-06-29 16:46:59 -07002645 int_val = (s32) brcmf_sdcard_reg_read(bus->sdh, addr,
2646 size);
2647 if (brcmf_sdcard_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002648 bcmerror = -EIO;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002649 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002650 break;
2651 }
2652
2653 case IOV_SVAL(IOV_SBREG):
2654 {
Roland Vossen51851252011-06-29 16:47:24 -07002655 struct brcmf_sdreg sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002656 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002657
Stanislav Fomichev02160692011-02-15 01:05:10 +03002658 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002659
2660 addr = SI_ENUM_BASE + sdreg.offset;
2661 size = sdreg.func;
Roland Vossen54ca2962011-06-29 16:46:59 -07002662 brcmf_sdcard_reg_write(bus->sdh, addr, size,
2663 sdreg.value);
2664 if (brcmf_sdcard_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002665 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002666 break;
2667 }
2668
2669 case IOV_GVAL(IOV_SDCIS):
2670 {
2671 *(char *)arg = 0;
2672
nohee koea3b8a22010-10-09 10:34:38 -07002673 strcat(arg, "\nFunc 0\n");
Roland Vossen54ca2962011-06-29 16:46:59 -07002674 brcmf_sdcard_cis_read(bus->sdh, 0x10,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002675 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002676 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002677 strcat(arg, "\nFunc 1\n");
Roland Vossen54ca2962011-06-29 16:46:59 -07002678 brcmf_sdcard_cis_read(bus->sdh, 0x11,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002679 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002680 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002681 strcat(arg, "\nFunc 2\n");
Roland Vossen54ca2962011-06-29 16:46:59 -07002682 brcmf_sdcard_cis_read(bus->sdh, 0x12,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002683 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002684 SBSDIO_CIS_SIZE_LIMIT);
2685 break;
2686 }
2687
2688 case IOV_GVAL(IOV_FORCEEVEN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002689 int_val = (s32) forcealign;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002690 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002691 break;
2692
2693 case IOV_SVAL(IOV_FORCEEVEN):
2694 forcealign = bool_val;
2695 break;
2696
2697 case IOV_GVAL(IOV_TXBOUND):
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07002698 int_val = (s32) brcmf_txbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002699 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002700 break;
2701
2702 case IOV_SVAL(IOV_TXBOUND):
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07002703 brcmf_txbound = (uint) int_val;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002704 break;
2705
2706 case IOV_GVAL(IOV_RXBOUND):
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07002707 int_val = (s32) brcmf_rxbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002708 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002709 break;
2710
2711 case IOV_SVAL(IOV_RXBOUND):
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07002712 brcmf_rxbound = (uint) int_val;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002713 break;
2714
2715 case IOV_GVAL(IOV_TXMINMAX):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002716 int_val = (s32) dhd_txminmax;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002717 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002718 break;
2719
2720 case IOV_SVAL(IOV_TXMINMAX):
2721 dhd_txminmax = (uint) int_val;
2722 break;
2723#endif /* DHD_DEBUG */
2724
2725#ifdef SDTEST
2726 case IOV_GVAL(IOV_EXTLOOP):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002727 int_val = (s32) bus->ext_loop;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002728 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002729 break;
2730
2731 case IOV_SVAL(IOV_EXTLOOP):
2732 bus->ext_loop = bool_val;
2733 break;
2734
2735 case IOV_GVAL(IOV_PKTGEN):
Roland Vossen54ca2962011-06-29 16:46:59 -07002736 bcmerror = brcmf_sdbrcm_pktgen_get(bus, arg);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002737 break;
2738
2739 case IOV_SVAL(IOV_PKTGEN):
Roland Vossen54ca2962011-06-29 16:46:59 -07002740 bcmerror = brcmf_sdbrcm_pktgen_set(bus, arg);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002741 break;
2742#endif /* SDTEST */
2743
2744 case IOV_SVAL(IOV_DEVRESET):
2745 DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d "
2746 "busstate=%d\n",
2747 __func__, bool_val, bus->dhd->dongle_reset,
2748 bus->dhd->busstate));
2749
Arend van Spriela84bac42011-06-29 16:47:10 -07002750 brcmf_bus_devreset(bus->dhd, (u8) bool_val);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002751
2752 break;
2753
2754 case IOV_GVAL(IOV_DEVRESET):
2755 DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __func__));
2756
2757 /* Get its status */
2758 int_val = (bool) bus->dhd->dongle_reset;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002759 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002760
2761 break;
2762
2763 default:
Roland Vossene10d82d2011-05-03 11:35:19 +02002764 bcmerror = -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002765 break;
2766 }
2767
2768exit:
2769 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002770 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07002771 brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002772 }
2773
Arend van Spriela84bac42011-06-29 16:47:10 -07002774 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002775
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002776 if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == false)
Arend van Sprielaba5e0a2011-06-29 16:47:03 -07002777 brcmf_c_preinit_ioctls((dhd_pub_t *) bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002778
2779 return bcmerror;
2780}
2781
Roland Vossen54ca2962011-06-29 16:46:59 -07002782static int brcmf_sdbrcm_write_vars(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002783{
2784 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002785 u32 varsize;
2786 u32 varaddr;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002787 u8 *vbuffer;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002788 u32 varsizew;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002789#ifdef DHD_DEBUG
2790 char *nvram_ularray;
2791#endif /* DHD_DEBUG */
2792
2793 /* Even if there are no vars are to be written, we still
2794 need to set the ramsize. */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07002795 varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002796 varaddr = (bus->ramsize - 4) - varsize;
2797
2798 if (bus->vars) {
Alexander Beregalov12d0eb42011-03-09 03:53:35 +03002799 vbuffer = kzalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002800 if (!vbuffer)
Roland Vossene10d82d2011-05-03 11:35:19 +02002801 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002802
Stanislav Fomichev02160692011-02-15 01:05:10 +03002803 memcpy(vbuffer, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002804
2805 /* Write the vars list */
2806 bcmerror =
Roland Vossen54ca2962011-06-29 16:46:59 -07002807 brcmf_sdbrcm_membytes(bus, true, varaddr, vbuffer, varsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002808#ifdef DHD_DEBUG
2809 /* Verify NVRAM bytes */
2810 DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002811 nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002812 if (!nvram_ularray)
Roland Vossene10d82d2011-05-03 11:35:19 +02002813 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002814
2815 /* Upload image to verify downloaded contents. */
2816 memset(nvram_ularray, 0xaa, varsize);
2817
2818 /* Read the vars list to temp buffer for comparison */
2819 bcmerror =
Roland Vossen54ca2962011-06-29 16:46:59 -07002820 brcmf_sdbrcm_membytes(bus, false, varaddr, nvram_ularray,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002821 varsize);
2822 if (bcmerror) {
2823 DHD_ERROR(("%s: error %d on reading %d nvram bytes at "
2824 "0x%08x\n", __func__, bcmerror, varsize, varaddr));
2825 }
2826 /* Compare the org NVRAM with the one read from RAM */
2827 if (memcmp(vbuffer, nvram_ularray, varsize)) {
2828 DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n",
2829 __func__));
2830 } else
2831 DHD_ERROR(("%s: Download/Upload/Compare of NVRAM ok.\n",
2832 __func__));
2833
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002834 kfree(nvram_ularray);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002835#endif /* DHD_DEBUG */
2836
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002837 kfree(vbuffer);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002838 }
2839
2840 /* adjust to the user specified RAM */
2841 DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
2842 bus->orig_ramsize, bus->ramsize));
2843 DHD_INFO(("Vars are at %d, orig varsize is %d\n", varaddr, varsize));
2844 varsize = ((bus->orig_ramsize - 4) - varaddr);
2845
2846 /*
2847 * Determine the length token:
2848 * Varsize, converted to words, in lower 16-bits, checksum
2849 * in upper 16-bits.
2850 */
2851 if (bcmerror) {
2852 varsizew = 0;
2853 } else {
2854 varsizew = varsize / 4;
2855 varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002856 varsizew = cpu_to_le32(varsizew);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002857 }
2858
2859 DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize,
2860 varsizew));
2861
2862 /* Write the length token to the last word */
Roland Vossen54ca2962011-06-29 16:46:59 -07002863 bcmerror = brcmf_sdbrcm_membytes(bus, true, (bus->orig_ramsize - 4),
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002864 (u8 *)&varsizew, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002865
2866 return bcmerror;
2867}
2868
Roland Vossen54ca2962011-06-29 16:46:59 -07002869static int brcmf_sdbrcm_download_state(dhd_bus_t *bus, bool enter)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002870{
2871 uint retries;
Franky Lineb5dc512011-04-25 19:34:04 -07002872 u32 regdata;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002873 int bcmerror = 0;
2874
2875 /* To enter download state, disable ARM and reset SOCRAM.
2876 * To exit download state, simply reset ARM (default is RAM boot).
2877 */
2878 if (enter) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002879 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002880
Roland Vossen54ca2962011-06-29 16:46:59 -07002881 brcmf_sdbrcm_chip_disablecore(bus->sdh, bus->ci->armcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002882
Roland Vossen54ca2962011-06-29 16:46:59 -07002883 brcmf_sdbrcm_chip_resetcore(bus->sdh, bus->ci->ramcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002884
2885 /* Clear the top bit of memory */
2886 if (bus->ramsize) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002887 u32 zeros = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07002888 brcmf_sdbrcm_membytes(bus, true, bus->ramsize - 4,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002889 (u8 *)&zeros, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002890 }
2891 } else {
Roland Vossen54ca2962011-06-29 16:46:59 -07002892 regdata = brcmf_sdcard_reg_read(bus->sdh,
Franky Lineb5dc512011-04-25 19:34:04 -07002893 CORE_SB(bus->ci->ramcorebase, sbtmstatelow), 4);
2894 regdata &= (SBTML_RESET | SBTML_REJ_MASK |
2895 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
2896 if ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) != regdata) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002897 DHD_ERROR(("%s: SOCRAM core is down after reset?\n",
2898 __func__));
Roland Vossenb74ac122011-05-03 11:35:20 +02002899 bcmerror = -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002900 goto fail;
2901 }
2902
Roland Vossen54ca2962011-06-29 16:46:59 -07002903 bcmerror = brcmf_sdbrcm_write_vars(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002904 if (bcmerror) {
2905 DHD_ERROR(("%s: no vars written to RAM\n", __func__));
2906 bcmerror = 0;
2907 }
2908
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002909 W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries);
2910
Roland Vossen54ca2962011-06-29 16:46:59 -07002911 brcmf_sdbrcm_chip_resetcore(bus->sdh, bus->ci->armcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002912
2913 /* Allow HT Clock now that the ARM is running. */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002914 bus->alp_only = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002915
2916 bus->dhd->busstate = DHD_BUS_LOAD;
2917 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002918fail:
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002919 return bcmerror;
2920}
2921
2922int
Roland Vossen54ca2962011-06-29 16:46:59 -07002923brcmf_sdbrcm_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
2924 void *params, int plen, void *arg, int len, bool set)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002925{
2926 dhd_bus_t *bus = dhdp->bus;
Roland Vossen67ad48b2011-06-01 13:45:51 +02002927 const struct brcmu_iovar *vi = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002928 int bcmerror = 0;
2929 int val_size;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002930 u32 actionid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002931
2932 DHD_TRACE(("%s: Enter\n", __func__));
2933
2934 ASSERT(name);
2935 ASSERT(len >= 0);
2936
2937 /* Get MUST have return space */
2938 ASSERT(set || (arg && len));
2939
2940 /* Set does NOT take qualifiers */
2941 ASSERT(!set || (!params && !plen));
2942
2943 /* Look up var locally; if not found pass to host driver */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002944 vi = brcmu_iovar_lookup(dhdsdio_iovars, name);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002945 if (vi == NULL) {
Arend van Spriela84bac42011-06-29 16:47:10 -07002946 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002947
2948 BUS_WAKE(bus);
2949
2950 /* Turn on clock in case SD command needs backplane */
Roland Vossen54ca2962011-06-29 16:46:59 -07002951 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002952
Roland Vossen54ca2962011-06-29 16:46:59 -07002953 bcmerror = brcmf_sdcard_iovar_op(bus->sdh, name, params, plen,
2954 arg, len, set);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002955
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002956 /* Similar check for blocksize change */
2957 if (set && strcmp(name, "sd_blocksize") == 0) {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002958 s32 fnum = 2;
Roland Vossen54ca2962011-06-29 16:46:59 -07002959 if (brcmf_sdcard_iovar_op
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002960 (bus->sdh, "sd_blocksize", &fnum, sizeof(s32),
2961 &bus->blocksize, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02002962 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002963 bus->blocksize = 0;
2964 DHD_ERROR(("%s: fail on %s get\n", __func__,
2965 "sd_blocksize"));
2966 } else {
2967 DHD_INFO(("%s: noted %s update, value now %d\n",
2968 __func__, "sd_blocksize",
2969 bus->blocksize));
2970 }
2971 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07002972 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002973
2974 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002975 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07002976 brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002977 }
2978
Arend van Spriela84bac42011-06-29 16:47:10 -07002979 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002980 goto exit;
2981 }
2982
2983 DHD_CTL(("%s: %s %s, len %d plen %d\n", __func__,
2984 name, (set ? "set" : "get"), len, plen));
2985
2986 /* set up 'params' pointer in case this is a set command so that
2987 * the convenience int and bool code can be common to set and get
2988 */
2989 if (params == NULL) {
2990 params = arg;
2991 plen = len;
2992 }
2993
2994 if (vi->type == IOVT_VOID)
2995 val_size = 0;
2996 else if (vi->type == IOVT_BUFFER)
2997 val_size = len;
2998 else
2999 /* all other types are integer sized */
3000 val_size = sizeof(int);
3001
3002 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
Roland Vossen54ca2962011-06-29 16:46:59 -07003003 bcmerror = brcmf_sdbrcm_doiovar(bus, vi, actionid, name, params, plen,
3004 arg, len, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003005
3006exit:
3007 return bcmerror;
3008}
3009
Roland Vossen54ca2962011-06-29 16:46:59 -07003010void brcmf_sdbrcm_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003011{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07003012 u32 local_hostintmask;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003013 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003014 uint retries;
3015 int err;
3016
3017 DHD_TRACE(("%s: Enter\n", __func__));
3018
3019 if (enforce_mutex)
Arend van Spriela84bac42011-06-29 16:47:10 -07003020 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003021
3022 BUS_WAKE(bus);
3023
3024 /* Enable clock for device interrupts */
Roland Vossen54ca2962011-06-29 16:46:59 -07003025 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003026
3027 /* Disable and clear interrupts at the chip level also */
3028 W_SDREG(0, &bus->regs->hostintmask, retries);
3029 local_hostintmask = bus->hostintmask;
3030 bus->hostintmask = 0;
3031
3032 /* Change our idea of bus state */
3033 bus->dhd->busstate = DHD_BUS_DOWN;
3034
3035 /* Force clocks on backplane to be sure F2 interrupt propagates */
Roland Vossen54ca2962011-06-29 16:46:59 -07003036 saveclk = brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_1,
3037 SBSDIO_FUNC1_CHIPCLKCSR, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003038 if (!err) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003039 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1,
3040 SBSDIO_FUNC1_CHIPCLKCSR,
3041 (saveclk | SBSDIO_FORCE_HT), &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003042 }
3043 if (err) {
3044 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
3045 __func__, err));
3046 }
3047
3048 /* Turn off the bus (F2), free any pending packets */
3049 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07003050 brcmf_sdcard_intr_disable(bus->sdh);
3051 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003052 SDIO_FUNC_ENABLE_1, NULL);
3053
3054 /* Clear any pending interrupts now that F2 is disabled */
3055 W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
3056
3057 /* Turn off the backplane clock (only) */
Roland Vossen54ca2962011-06-29 16:46:59 -07003058 brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003059
3060 /* Clear the data packet queues */
Roland Vossen67ad48b2011-06-01 13:45:51 +02003061 brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003062
3063 /* Clear any held glomming stuff */
3064 if (bus->glomd)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003065 brcmu_pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003066
3067 if (bus->glom)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003068 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003069
3070 bus->glom = bus->glomd = NULL;
3071
3072 /* Clear rx control and wake any waiters */
3073 bus->rxlen = 0;
Arend van Spriela84bac42011-06-29 16:47:10 -07003074 brcmf_os_ioctl_resp_wake(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003075
3076 /* Reset some F2 state stuff */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003077 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003078 bus->tx_seq = bus->rx_seq = 0;
3079
3080 if (enforce_mutex)
Arend van Spriela84bac42011-06-29 16:47:10 -07003081 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003082}
3083
Roland Vossen54ca2962011-06-29 16:46:59 -07003084int brcmf_sdbrcm_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003085{
3086 dhd_bus_t *bus = dhdp->bus;
3087 dhd_timeout_t tmo;
3088 uint retries = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003089 u8 ready, enable;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003090 int err, ret = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003091 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003092
3093 DHD_TRACE(("%s: Enter\n", __func__));
3094
3095 ASSERT(bus->dhd);
3096 if (!bus->dhd)
3097 return 0;
3098
3099 if (enforce_mutex)
Arend van Spriela84bac42011-06-29 16:47:10 -07003100 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003101
3102 /* Make sure backplane clock is on, needed to generate F2 interrupt */
Roland Vossen54ca2962011-06-29 16:46:59 -07003103 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003104 if (bus->clkstate != CLK_AVAIL)
3105 goto exit;
3106
3107 /* Force clocks on backplane to be sure F2 interrupt propagates */
3108 saveclk =
Roland Vossen54ca2962011-06-29 16:46:59 -07003109 brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_1,
3110 SBSDIO_FUNC1_CHIPCLKCSR, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003111 if (!err) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003112 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1,
3113 SBSDIO_FUNC1_CHIPCLKCSR,
3114 (saveclk | SBSDIO_FORCE_HT), &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003115 }
3116 if (err) {
3117 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
3118 __func__, err));
3119 goto exit;
3120 }
3121
3122 /* Enable function 2 (frame transfers) */
3123 W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT),
3124 &bus->regs->tosbmailboxdata, retries);
3125 enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
3126
Roland Vossen54ca2962011-06-29 16:46:59 -07003127 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx, enable,
3128 NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003129
3130 /* Give the dongle some time to do its thing and set IOR2 */
Arend van Spriela84bac42011-06-29 16:47:10 -07003131 brcmf_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003132
3133 ready = 0;
Arend van Spriela84bac42011-06-29 16:47:10 -07003134 while (ready != enable && !brcmf_timeout_expired(&tmo))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003135 ready =
Roland Vossen54ca2962011-06-29 16:46:59 -07003136 brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IORx,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003137 NULL);
3138
3139 DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
3140 __func__, enable, ready, tmo.elapsed));
3141
3142 /* If F2 successfully enabled, set core and enable interrupts */
3143 if (ready == enable) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003144 /* Set up the interrupt mask and enable interrupts */
3145 bus->hostintmask = HOSTINTMASK;
Franky Linc05df632011-04-25 19:34:07 -07003146 W_SDREG(bus->hostintmask,
3147 (unsigned int *)CORE_BUS_REG(bus->ci->buscorebase,
3148 hostintmask), retries);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003149
Roland Vossen54ca2962011-06-29 16:46:59 -07003150 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003151 (u8) watermark, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003152
3153 /* Set bus state according to enable result */
3154 dhdp->busstate = DHD_BUS_DATA;
3155
3156 /* bcmsdh_intr_unmask(bus->sdh); */
3157
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003158 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003159 if (bus->intr) {
3160 DHD_INTR(("%s: enable SDIO device interrupts\n",
3161 __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07003162 brcmf_sdcard_intr_enable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003163 } else {
3164 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07003165 brcmf_sdcard_intr_disable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003166 }
3167
3168 }
3169
3170 else {
3171 /* Disable F2 again */
3172 enable = SDIO_FUNC_ENABLE_1;
Roland Vossen54ca2962011-06-29 16:46:59 -07003173 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
3174 enable, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003175 }
3176
3177 /* Restore previous clock setting */
Roland Vossen54ca2962011-06-29 16:46:59 -07003178 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003179 saveclk, &err);
3180
3181 /* If we didn't come up, turn off backplane clock */
3182 if (dhdp->busstate != DHD_BUS_DATA)
Roland Vossen54ca2962011-06-29 16:46:59 -07003183 brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003184
3185exit:
3186 if (enforce_mutex)
Arend van Spriela84bac42011-06-29 16:47:10 -07003187 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003188
3189 return ret;
3190}
3191
Roland Vossen54ca2962011-06-29 16:46:59 -07003192static void brcmf_sdbrcm_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003193{
Roland Vossen677226a2011-06-29 16:47:21 -07003194 struct brcmf_sdio *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02003195 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003196 uint retries = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003197 u16 lastrbc;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003198 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003199 int err;
3200
3201 DHD_ERROR(("%s: %sterminate frame%s\n", __func__,
3202 (abort ? "abort command, " : ""),
3203 (rtx ? ", send NAK" : "")));
3204
3205 if (abort)
Roland Vossen54ca2962011-06-29 16:46:59 -07003206 brcmf_sdcard_abort(sdh, SDIO_FUNC_2);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003207
Roland Vossen54ca2962011-06-29 16:46:59 -07003208 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL,
3209 SFC_RF_TERM, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003210 bus->f1regdata++;
3211
3212 /* Wait until the packet has been flushed (device/FIFO stable) */
3213 for (lastrbc = retries = 0xffff; retries > 0; retries--) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003214 hi = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
3215 SBSDIO_FUNC1_RFRAMEBCHI, NULL);
3216 lo = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
3217 SBSDIO_FUNC1_RFRAMEBCLO, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003218 bus->f1regdata += 2;
3219
3220 if ((hi == 0) && (lo == 0))
3221 break;
3222
3223 if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
3224 DHD_ERROR(("%s: count growing: last 0x%04x now "
3225 "0x%04x\n",
3226 __func__, lastrbc, ((hi << 8) + lo)));
3227 }
3228 lastrbc = (hi << 8) + lo;
3229 }
3230
3231 if (!retries) {
3232 DHD_ERROR(("%s: count never zeroed: last 0x%04x\n",
3233 __func__, lastrbc));
3234 } else {
3235 DHD_INFO(("%s: flush took %d iterations\n", __func__,
3236 (0xffff - retries)));
3237 }
3238
3239 if (rtx) {
3240 bus->rxrtx++;
3241 W_SDREG(SMB_NAK, &regs->tosbmailbox, retries);
3242 bus->f1regdata++;
3243 if (retries <= retry_limit)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003244 bus->rxskip = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003245 }
3246
3247 /* Clear partial in any case */
3248 bus->nextlen = 0;
3249
3250 /* If we can't reach the device, signal failure */
Roland Vossen54ca2962011-06-29 16:46:59 -07003251 if (err || brcmf_sdcard_regfail(sdh))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003252 bus->dhd->busstate = DHD_BUS_DOWN;
3253}
3254
3255static void
Roland Vossen54ca2962011-06-29 16:46:59 -07003256brcmf_sdbrcm_read_control(dhd_bus_t *bus, u8 *hdr, uint len, uint doff)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003257{
Roland Vossen677226a2011-06-29 16:47:21 -07003258 struct brcmf_sdio *sdh = bus->sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003259 uint rdlen, pad;
3260
3261 int sdret;
3262
3263 DHD_TRACE(("%s: Enter\n", __func__));
3264
3265 /* Control data already received in aligned rxctl */
3266 if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
3267 goto gotpkt;
3268
3269 ASSERT(bus->rxbuf);
3270 /* Set rxctl for frame (w/optional alignment) */
3271 bus->rxctl = bus->rxbuf;
3272 if (dhd_alignctl) {
3273 bus->rxctl += firstread;
Arend van Spriel8d825a82011-06-29 16:47:25 -07003274 pad = ((unsigned long)bus->rxctl % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003275 if (pad)
Arend van Spriel8d825a82011-06-29 16:47:25 -07003276 bus->rxctl += (BRCMF_SDALIGN - pad);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003277 bus->rxctl -= firstread;
3278 }
3279 ASSERT(bus->rxctl >= bus->rxbuf);
3280
3281 /* Copy the already-read portion over */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003282 memcpy(bus->rxctl, hdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003283 if (len <= firstread)
3284 goto gotpkt;
3285
3286 /* Copy the full data pkt in gSPI case and process ioctl. */
3287 if (bus->bus == SPI_BUS) {
Stanislav Fomichev02160692011-02-15 01:05:10 +03003288 memcpy(bus->rxctl, hdr, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003289 goto gotpkt;
3290 }
3291
3292 /* Raise rdlen to next SDIO block to avoid tail command */
3293 rdlen = len - firstread;
3294 if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
3295 pad = bus->blocksize - (rdlen % bus->blocksize);
3296 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
3297 ((len + pad) < bus->dhd->maxctl))
3298 rdlen += pad;
Arend van Spriel8d825a82011-06-29 16:47:25 -07003299 } else if (rdlen % BRCMF_SDALIGN) {
3300 rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003301 }
3302
3303 /* Satisfy length-alignment requirements */
3304 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003305 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003306
3307 /* Drop if the read is too big or it exceeds our maximum */
3308 if ((rdlen + firstread) > bus->dhd->maxctl) {
3309 DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n",
3310 __func__, rdlen, bus->dhd->maxctl));
3311 bus->dhd->rx_errors++;
Roland Vossen54ca2962011-06-29 16:46:59 -07003312 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003313 goto done;
3314 }
3315
3316 if ((len - doff) > bus->dhd->maxctl) {
3317 DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds "
3318 "%d-byte limit\n",
3319 __func__, len, (len - doff), bus->dhd->maxctl));
3320 bus->dhd->rx_errors++;
3321 bus->rx_toolong++;
Roland Vossen54ca2962011-06-29 16:46:59 -07003322 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003323 goto done;
3324 }
3325
3326 /* Read remainder of frame body into the rxctl buffer */
Roland Vossen54ca2962011-06-29 16:46:59 -07003327 sdret = brcmf_sdcard_recv_buf(sdh, brcmf_sdcard_cur_sbwad(sdh),
3328 SDIO_FUNC_2,
Grant Grundler4b455e02011-05-04 09:59:47 -07003329 F2SYNC, (bus->rxctl + firstread), rdlen,
3330 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003331 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003332 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003333
3334 /* Control frame failures need retransmission */
3335 if (sdret < 0) {
3336 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3337 __func__, rdlen, sdret));
3338 bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */
Roland Vossen54ca2962011-06-29 16:46:59 -07003339 brcmf_sdbrcm_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003340 goto done;
3341 }
3342
3343gotpkt:
3344
3345#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003346 if (DHD_BYTES_ON() && DHD_CTL_ON()) {
3347 printk(KERN_DEBUG "RxCtrl:\n");
3348 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, bus->rxctl, len);
3349 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003350#endif
3351
3352 /* Point to valid data and indicate its length */
3353 bus->rxctl += doff;
3354 bus->rxlen = len - doff;
3355
3356done:
3357 /* Awake any waiters */
Arend van Spriela84bac42011-06-29 16:47:10 -07003358 brcmf_os_ioctl_resp_wake(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003359}
3360
Roland Vossen54ca2962011-06-29 16:46:59 -07003361static u8 brcmf_sdbrcm_rxglom(dhd_bus_t *bus, u8 rxseq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003362{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003363 u16 dlen, totlen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003364 u8 *dptr, num = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003365
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003366 u16 sublen, check;
Arend van Sprielc26b1372010-11-23 14:06:23 +01003367 struct sk_buff *pfirst, *plast, *pnext, *save_pfirst;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003368
3369 int errcode;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003370 u8 chan, seq, doff, sfdoff;
3371 u8 txmax;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003372
3373 int ifidx = 0;
3374 bool usechain = bus->use_rxchain;
3375
3376 /* If packets, issue read(s) and send up packet chain */
3377 /* Return sequence numbers consumed? */
3378
Roland Vossen54ca2962011-06-29 16:46:59 -07003379 DHD_TRACE(("brcmf_sdbrcm_rxglom: start: glomd %p glom %p\n", bus->glomd,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003380 bus->glom));
3381
3382 /* If there's a descriptor, generate the packet chain */
3383 if (bus->glomd) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003384 pfirst = plast = pnext = NULL;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003385 dlen = (u16) (bus->glomd->len);
3386 dptr = bus->glomd->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003387 if (!dlen || (dlen & 1)) {
3388 DHD_ERROR(("%s: bad glomd len(%d), ignore descriptor\n",
3389 __func__, dlen));
3390 dlen = 0;
3391 }
3392
3393 for (totlen = num = 0; dlen; num++) {
3394 /* Get (and move past) next length */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003395 sublen = get_unaligned_le16(dptr);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003396 dlen -= sizeof(u16);
3397 dptr += sizeof(u16);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003398 if ((sublen < SDPCM_HDRLEN) ||
3399 ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
3400 DHD_ERROR(("%s: descriptor len %d bad: %d\n",
3401 __func__, num, sublen));
3402 pnext = NULL;
3403 break;
3404 }
Arend van Spriel8d825a82011-06-29 16:47:25 -07003405 if (sublen % BRCMF_SDALIGN) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003406 DHD_ERROR(("%s: sublen %d not multiple of %d\n",
Arend van Spriel8d825a82011-06-29 16:47:25 -07003407 __func__, sublen, BRCMF_SDALIGN));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003408 usechain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003409 }
3410 totlen += sublen;
3411
3412 /* For last frame, adjust read len so total
3413 is a block multiple */
3414 if (!dlen) {
3415 sublen +=
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003416 (roundup(totlen, bus->blocksize) - totlen);
3417 totlen = roundup(totlen, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003418 }
3419
3420 /* Allocate/chain packet for next subframe */
Arend van Spriel8d825a82011-06-29 16:47:25 -07003421 pnext = brcmu_pkt_buf_get_skb(sublen + BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003422 if (pnext == NULL) {
Henry Ptasinskib33f0e22011-05-10 22:25:29 +02003423 DHD_ERROR(("%s: bcm_pkt_buf_get_skb failed, "
3424 "num %d len %d\n", __func__,
3425 num, sublen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003426 break;
3427 }
Arend van Spriel54991ad2010-11-23 14:06:24 +01003428 ASSERT(!(pnext->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003429 if (!pfirst) {
3430 ASSERT(!plast);
3431 pfirst = plast = pnext;
3432 } else {
3433 ASSERT(plast);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003434 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003435 plast = pnext;
3436 }
3437
3438 /* Adhere to start alignment requirements */
Arend van Spriel8d825a82011-06-29 16:47:25 -07003439 PKTALIGN(pnext, sublen, BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003440 }
3441
3442 /* If all allocations succeeded, save packet chain
3443 in bus structure */
3444 if (pnext) {
3445 DHD_GLOM(("%s: allocated %d-byte packet chain for %d "
3446 "subframes\n", __func__, totlen, num));
3447 if (DHD_GLOM_ON() && bus->nextlen) {
3448 if (totlen != bus->nextlen) {
3449 DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " "rxseq %d\n",
3450 __func__, bus->nextlen,
3451 totlen, rxseq));
3452 }
3453 }
3454 bus->glom = pfirst;
3455 pfirst = pnext = NULL;
3456 } else {
3457 if (pfirst)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003458 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003459 bus->glom = NULL;
3460 num = 0;
3461 }
3462
3463 /* Done with descriptor packet */
Roland Vossen67ad48b2011-06-01 13:45:51 +02003464 brcmu_pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003465 bus->glomd = NULL;
3466 bus->nextlen = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003467 }
3468
3469 /* Ok -- either we just generated a packet chain,
3470 or had one from before */
3471 if (bus->glom) {
3472 if (DHD_GLOM_ON()) {
3473 DHD_GLOM(("%s: try superframe read, packet chain:\n",
3474 __func__));
Arend van Spriel54991ad2010-11-23 14:06:24 +01003475 for (pnext = bus->glom; pnext; pnext = pnext->next) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003476 DHD_GLOM((" %p: %p len 0x%04x (%d)\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003477 pnext, (u8 *) (pnext->data),
3478 pnext->len, pnext->len));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003479 }
3480 }
3481
3482 pfirst = bus->glom;
Roland Vossen67ad48b2011-06-01 13:45:51 +02003483 dlen = (u16) brcmu_pkttotlen(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003484
3485 /* Do an SDIO read for the superframe. Configurable iovar to
3486 * read directly into the chained packet, or allocate a large
3487 * packet and and copy into the chain.
3488 */
3489 if (usechain) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003490 errcode = brcmf_sdcard_recv_buf(bus->sdh,
3491 brcmf_sdcard_cur_sbwad(bus->sdh),
3492 SDIO_FUNC_2,
Grant Grundler4b455e02011-05-04 09:59:47 -07003493 F2SYNC, (u8 *) pfirst->data, dlen,
3494 pfirst, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003495 } else if (bus->dataptr) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003496 errcode = brcmf_sdcard_recv_buf(bus->sdh,
3497 brcmf_sdcard_cur_sbwad(bus->sdh),
3498 SDIO_FUNC_2,
Grant Grundler4b455e02011-05-04 09:59:47 -07003499 F2SYNC, bus->dataptr, dlen,
3500 NULL, NULL, NULL);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003501 sublen = (u16) brcmu_pktfrombuf(pfirst, 0, dlen,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003502 bus->dataptr);
3503 if (sublen != dlen) {
3504 DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n",
3505 __func__, dlen, sublen));
3506 errcode = -1;
3507 }
3508 pnext = NULL;
3509 } else {
3510 DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
3511 dlen));
3512 errcode = -1;
3513 }
3514 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003515 ASSERT(errcode != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003516
3517 /* On failure, kill the superframe, allow a couple retries */
3518 if (errcode < 0) {
3519 DHD_ERROR(("%s: glom read of %d bytes failed: %d\n",
3520 __func__, dlen, errcode));
3521 bus->dhd->rx_errors++;
3522
3523 if (bus->glomerr++ < 3) {
Roland Vossen54ca2962011-06-29 16:46:59 -07003524 brcmf_sdbrcm_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003525 } else {
3526 bus->glomerr = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07003527 brcmf_sdbrcm_rxfail(bus, true, false);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003528 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003529 bus->rxglomfail++;
3530 bus->glom = NULL;
3531 }
3532 return 0;
3533 }
3534#ifdef DHD_DEBUG
3535 if (DHD_GLOM_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02003536 printk(KERN_DEBUG "SUPERFRAME:\n");
3537 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3538 pfirst->data, min_t(int, pfirst->len, 48));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003539 }
3540#endif
3541
3542 /* Validate the superframe header */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003543 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003544 sublen = get_unaligned_le16(dptr);
3545 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003546
3547 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3548 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3549 bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
3550 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
3551 DHD_INFO(("%s: nextlen too large (%d) seq %d\n",
3552 __func__, bus->nextlen, seq));
3553 bus->nextlen = 0;
3554 }
3555 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3556 txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3557
3558 errcode = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003559 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003560 DHD_ERROR(("%s (superframe): HW hdr error: len/check "
3561 "0x%04x/0x%04x\n", __func__, sublen, check));
3562 errcode = -1;
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003563 } else if (roundup(sublen, bus->blocksize) != dlen) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003564 DHD_ERROR(("%s (superframe): len 0x%04x, rounded "
3565 "0x%04x, expect 0x%04x\n",
3566 __func__, sublen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003567 roundup(sublen, bus->blocksize), dlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003568 errcode = -1;
3569 } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
3570 SDPCM_GLOM_CHANNEL) {
3571 DHD_ERROR(("%s (superframe): bad channel %d\n",
3572 __func__,
3573 SDPCM_PACKET_CHANNEL(&dptr
3574 [SDPCM_FRAMETAG_LEN])));
3575 errcode = -1;
3576 } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
3577 DHD_ERROR(("%s (superframe): got second descriptor?\n",
3578 __func__));
3579 errcode = -1;
3580 } else if ((doff < SDPCM_HDRLEN) ||
Arend van Spriel54991ad2010-11-23 14:06:24 +01003581 (doff > (pfirst->len - SDPCM_HDRLEN))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003582 DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d "
3583 "pkt %d min %d\n",
3584 __func__, doff, sublen,
Arend van Spriel54991ad2010-11-23 14:06:24 +01003585 pfirst->len, SDPCM_HDRLEN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003586 errcode = -1;
3587 }
3588
3589 /* Check sequence number of superframe SW header */
3590 if (rxseq != seq) {
3591 DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
3592 __func__, seq, rxseq));
3593 bus->rx_badseq++;
3594 rxseq = seq;
3595 }
3596
3597 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003598 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003599 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
3600 __func__, txmax, bus->tx_seq));
3601 txmax = bus->tx_seq + 2;
3602 }
3603 bus->tx_max = txmax;
3604
3605 /* Remove superframe header, remember offset */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003606 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003607 sfdoff = doff;
3608
3609 /* Validate all the subframe headers */
3610 for (num = 0, pnext = pfirst; pnext && !errcode;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003611 num++, pnext = pnext->next) {
3612 dptr = (u8 *) (pnext->data);
3613 dlen = (u16) (pnext->len);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003614 sublen = get_unaligned_le16(dptr);
3615 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003616 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3617 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3618#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003619 if (DHD_GLOM_ON()) {
3620 printk(KERN_DEBUG "subframe:\n");
3621 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3622 dptr, 32);
3623 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003624#endif
3625
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003626 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003627 DHD_ERROR(("%s (subframe %d): HW hdr error: "
3628 "len/check 0x%04x/0x%04x\n",
3629 __func__, num, sublen, check));
3630 errcode = -1;
3631 } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
3632 DHD_ERROR(("%s (subframe %d): length mismatch: "
3633 "len 0x%04x, expect 0x%04x\n",
3634 __func__, num, sublen, dlen));
3635 errcode = -1;
3636 } else if ((chan != SDPCM_DATA_CHANNEL) &&
3637 (chan != SDPCM_EVENT_CHANNEL)) {
3638 DHD_ERROR(("%s (subframe %d): bad channel %d\n",
3639 __func__, num, chan));
3640 errcode = -1;
3641 } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
3642 DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n",
3643 __func__, num, doff, sublen,
3644 SDPCM_HDRLEN));
3645 errcode = -1;
3646 }
3647 }
3648
3649 if (errcode) {
3650 /* Terminate frame on error, request
3651 a couple retries */
3652 if (bus->glomerr++ < 3) {
3653 /* Restore superframe header space */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003654 skb_push(pfirst, sfdoff);
Roland Vossen54ca2962011-06-29 16:46:59 -07003655 brcmf_sdbrcm_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003656 } else {
3657 bus->glomerr = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07003658 brcmf_sdbrcm_rxfail(bus, true, false);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003659 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003660 bus->rxglomfail++;
3661 bus->glom = NULL;
3662 }
3663 bus->nextlen = 0;
3664 return 0;
3665 }
3666
3667 /* Basic SD framing looks ok - process each packet (header) */
3668 save_pfirst = pfirst;
3669 bus->glom = NULL;
3670 plast = NULL;
3671
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003672 for (num = 0; pfirst; rxseq++, pfirst = pnext) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003673 pnext = pfirst->next;
3674 pfirst->next = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003675
Arend van Spriel54991ad2010-11-23 14:06:24 +01003676 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003677 sublen = get_unaligned_le16(dptr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003678 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3679 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3680 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3681
3682 DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d "
3683 "chan %d seq %d\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003684 __func__, num, pfirst, pfirst->data,
3685 pfirst->len, sublen, chan, seq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003686
3687 ASSERT((chan == SDPCM_DATA_CHANNEL)
3688 || (chan == SDPCM_EVENT_CHANNEL));
3689
3690 if (rxseq != seq) {
3691 DHD_GLOM(("%s: rx_seq %d, expected %d\n",
3692 __func__, seq, rxseq));
3693 bus->rx_badseq++;
3694 rxseq = seq;
3695 }
3696#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003697 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
3698 printk(KERN_DEBUG "Rx Subframe Data:\n");
3699 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3700 dptr, dlen);
3701 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003702#endif
3703
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01003704 __skb_trim(pfirst, sublen);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003705 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003706
Arend van Spriel54991ad2010-11-23 14:06:24 +01003707 if (pfirst->len == 0) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02003708 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003709 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003710 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003711 } else {
3712 ASSERT(save_pfirst == pfirst);
3713 save_pfirst = pnext;
3714 }
3715 continue;
Arend van Spriel0aa94b82011-06-29 16:47:13 -07003716 } else if (brcmf_proto_hdrpull(bus->dhd, &ifidx, pfirst)
3717 != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003718 DHD_ERROR(("%s: rx protocol error\n",
3719 __func__));
3720 bus->dhd->rx_errors++;
Roland Vossen67ad48b2011-06-01 13:45:51 +02003721 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003722 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003723 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003724 } else {
3725 ASSERT(save_pfirst == pfirst);
3726 save_pfirst = pnext;
3727 }
3728 continue;
3729 }
3730
3731 /* this packet will go up, link back into
3732 chain and count it */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003733 pfirst->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003734 plast = pfirst;
3735 num++;
3736
3737#ifdef DHD_DEBUG
3738 if (DHD_GLOM_ON()) {
3739 DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) "
3740 "nxt/lnk %p/%p\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003741 __func__, num, pfirst, pfirst->data,
3742 pfirst->len, pfirst->next,
3743 pfirst->prev));
Arend van Spriel34227312011-05-10 22:25:32 +02003744 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3745 pfirst->data,
3746 min_t(int, pfirst->len, 32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003747 }
3748#endif /* DHD_DEBUG */
3749 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003750 if (num) {
Arend van Spriela84bac42011-06-29 16:47:10 -07003751 brcmf_os_sdunlock(bus->dhd);
3752 brcmf_rx_frame(bus->dhd, ifidx, save_pfirst, num);
3753 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003754 }
3755
3756 bus->rxglomframes++;
3757 bus->rxglompkts += num;
3758 }
3759 return num;
3760}
3761
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003762/* Return true if there may be more frames to read */
Roland Vossen54ca2962011-06-29 16:46:59 -07003763static uint
3764brcmf_sdbrcm_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003765{
Roland Vossen677226a2011-06-29 16:47:21 -07003766 struct brcmf_sdio *sdh = bus->sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003767
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003768 u16 len, check; /* Extracted hardware header fields */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003769 u8 chan, seq, doff; /* Extracted software header fields */
3770 u8 fcbits; /* Extracted fcbits from software header */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003771
Arend van Sprielc26b1372010-11-23 14:06:23 +01003772 struct sk_buff *pkt; /* Packet for event or data frames */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003773 u16 pad; /* Number of pad bytes to read */
3774 u16 rdlen; /* Total number of bytes to read */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003775 u8 rxseq; /* Next sequence number to expect */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003776 uint rxleft = 0; /* Remaining number of frames allowed */
3777 int sdret; /* Return code from bcmsdh calls */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003778 u8 txmax; /* Maximum tx sequence offered */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003779 bool len_consistent; /* Result of comparing readahead len and
3780 len from hw-hdr */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003781 u8 *rxbuf;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003782 int ifidx = 0;
3783 uint rxcount = 0; /* Total frames read */
3784
3785#if defined(DHD_DEBUG) || defined(SDTEST)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003786 bool sdtest = false; /* To limit message spew from test mode */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003787#endif
3788
3789 DHD_TRACE(("%s: Enter\n", __func__));
3790
3791 ASSERT(maxframes);
3792
3793#ifdef SDTEST
3794 /* Allow pktgen to override maxframes */
3795 if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) {
3796 maxframes = bus->pktgen_count;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003797 sdtest = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003798 }
3799#endif
3800
3801 /* Not finished unless we encounter no more frames indication */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003802 *finished = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003803
3804 for (rxseq = bus->rx_seq, rxleft = maxframes;
3805 !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN;
3806 rxseq++, rxleft--) {
3807
3808 /* Handle glomming separately */
3809 if (bus->glom || bus->glomd) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003810 u8 cnt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003811 DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
3812 __func__, bus->glomd, bus->glom));
Roland Vossen54ca2962011-06-29 16:46:59 -07003813 cnt = brcmf_sdbrcm_rxglom(bus, rxseq);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003814 DHD_GLOM(("%s: rxglom returned %d\n", __func__, cnt));
3815 rxseq += cnt - 1;
3816 rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
3817 continue;
3818 }
3819
3820 /* Try doing single read if we can */
3821 if (dhd_readahead && bus->nextlen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003822 u16 nextlen = bus->nextlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003823 bus->nextlen = 0;
3824
3825 if (bus->bus == SPI_BUS) {
3826 rdlen = len = nextlen;
3827 } else {
3828 rdlen = len = nextlen << 4;
3829
3830 /* Pad read to blocksize for efficiency */
3831 if (bus->roundup && bus->blocksize
3832 && (rdlen > bus->blocksize)) {
3833 pad =
3834 bus->blocksize -
3835 (rdlen % bus->blocksize);
3836 if ((pad <= bus->roundup)
3837 && (pad < bus->blocksize)
3838 && ((rdlen + pad + firstread) <
3839 MAX_RX_DATASZ))
3840 rdlen += pad;
Arend van Spriel8d825a82011-06-29 16:47:25 -07003841 } else if (rdlen % BRCMF_SDALIGN) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003842 rdlen +=
Arend van Spriel8d825a82011-06-29 16:47:25 -07003843 BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003844 }
3845 }
3846
3847 /* We use bus->rxctl buffer in WinXP for initial
3848 * control pkt receives.
3849 * Later we use buffer-poll for data as well
3850 * as control packets.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003851 * This is required because dhd receives full
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003852 * frame in gSPI unlike SDIO.
3853 * After the frame is received we have to
3854 * distinguish whether it is data
3855 * or non-data frame.
3856 */
3857 /* Allocate a packet buffer */
Arend van Spriel8d825a82011-06-29 16:47:25 -07003858 pkt = brcmu_pkt_buf_get_skb(rdlen + BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003859 if (!pkt) {
3860 if (bus->bus == SPI_BUS) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003861 bus->usebufpool = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003862 bus->rxctl = bus->rxbuf;
3863 if (dhd_alignctl) {
3864 bus->rxctl += firstread;
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07003865 pad = ((unsigned long)bus->rxctl %
Arend van Spriel8d825a82011-06-29 16:47:25 -07003866 BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003867 if (pad)
3868 bus->rxctl +=
Arend van Spriel8d825a82011-06-29 16:47:25 -07003869 (BRCMF_SDALIGN - pad);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003870 bus->rxctl -= firstread;
3871 }
3872 ASSERT(bus->rxctl >= bus->rxbuf);
3873 rxbuf = bus->rxctl;
3874 /* Read the entire frame */
Roland Vossen54ca2962011-06-29 16:46:59 -07003875 sdret = brcmf_sdcard_recv_buf(sdh,
3876 brcmf_sdcard_cur_sbwad(sdh),
Grant Grundler4b455e02011-05-04 09:59:47 -07003877 SDIO_FUNC_2, F2SYNC,
3878 rxbuf, rdlen,
3879 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003880 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003881 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003882
3883 /* Control frame failures need
3884 retransmission */
3885 if (sdret < 0) {
3886 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3887 __func__,
3888 rdlen, sdret));
3889 /* dhd.rx_ctlerrs is higher */
3890 bus->rxc_errors++;
Roland Vossen54ca2962011-06-29 16:46:59 -07003891 brcmf_sdbrcm_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003892 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003893 SPI_BUS) ? false
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003894 : true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003895 continue;
3896 }
3897 } else {
3898 /* Give up on data,
3899 request rtx of events */
Arend van Sprielcda64a52011-05-10 22:25:33 +02003900 DHD_ERROR(("%s (nextlen): "
Roland Vossen67ad48b2011-06-01 13:45:51 +02003901 "brcmu_pkt_buf_get_skb "
3902 "failed:"
Arend van Sprielcda64a52011-05-10 22:25:33 +02003903 " len %d rdlen %d expected"
3904 " rxseq %d\n", __func__,
3905 len, rdlen, rxseq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003906 continue;
3907 }
3908 } else {
3909 if (bus->bus == SPI_BUS)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003910 bus->usebufpool = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003911
Arend van Spriel54991ad2010-11-23 14:06:24 +01003912 ASSERT(!(pkt->prev));
Arend van Spriel8d825a82011-06-29 16:47:25 -07003913 PKTALIGN(pkt, rdlen, BRCMF_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003914 rxbuf = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003915 /* Read the entire frame */
Roland Vossen54ca2962011-06-29 16:46:59 -07003916 sdret = brcmf_sdcard_recv_buf(sdh,
3917 brcmf_sdcard_cur_sbwad(sdh),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003918 SDIO_FUNC_2, F2SYNC,
Grant Grundler4b455e02011-05-04 09:59:47 -07003919 rxbuf, rdlen,
3920 pkt, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003921 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003922 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003923
3924 if (sdret < 0) {
3925 DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n",
3926 __func__, rdlen, sdret));
Roland Vossen67ad48b2011-06-01 13:45:51 +02003927 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003928 bus->dhd->rx_errors++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003929 /* Force retry w/normal header read.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003930 * Don't attempt NAK for
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003931 * gSPI
3932 */
Roland Vossen54ca2962011-06-29 16:46:59 -07003933 brcmf_sdbrcm_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003934 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003935 SPI_BUS) ? false :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003936 true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003937 continue;
3938 }
3939 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003940
3941 /* Now check the header */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003942 memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003943
3944 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003945 len = get_unaligned_le16(bus->rxhdr);
3946 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003947
3948 /* All zeros means readahead info was bad */
3949 if (!(len | check)) {
3950 DHD_INFO(("%s (nextlen): read zeros in HW "
3951 "header???\n", __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07003952 brcmf_sdbrcm_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003953 continue;
3954 }
3955
3956 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003957 if ((u16)~(len ^ check)) {
Grant Grundler4b455e02011-05-04 09:59:47 -07003958 DHD_ERROR(("%s (nextlen): HW hdr error:"
3959 " nextlen/len/check"
3960 " 0x%04x/0x%04x/0x%04x\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003961 __func__, nextlen, len, check));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003962 bus->rx_badhdr++;
Roland Vossen54ca2962011-06-29 16:46:59 -07003963 brcmf_sdbrcm_rxfail(bus, false, false);
3964 brcmf_sdbrcm_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003965 continue;
3966 }
3967
3968 /* Validate frame length */
3969 if (len < SDPCM_HDRLEN) {
3970 DHD_ERROR(("%s (nextlen): HW hdr length "
3971 "invalid: %d\n", __func__, len));
Roland Vossen54ca2962011-06-29 16:46:59 -07003972 brcmf_sdbrcm_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003973 continue;
3974 }
3975
3976 /* Check for consistency withreadahead info */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003977 len_consistent = (nextlen != (roundup(len, 16) >> 4));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003978 if (len_consistent) {
3979 /* Mismatch, force retry w/normal
3980 header (may be >4K) */
Grant Grundler4b455e02011-05-04 09:59:47 -07003981 DHD_ERROR(("%s (nextlen): mismatch, "
3982 "nextlen %d len %d rnd %d; "
3983 "expected rxseq %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003984 __func__, nextlen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003985 len, roundup(len, 16), rxseq));
Roland Vossen54ca2962011-06-29 16:46:59 -07003986 brcmf_sdbrcm_rxfail(bus, true,
3987 bus->bus != SPI_BUS);
3988 brcmf_sdbrcm_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003989 continue;
3990 }
3991
3992 /* Extract software header fields */
Grant Grundler4b455e02011-05-04 09:59:47 -07003993 chan = SDPCM_PACKET_CHANNEL(
3994 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
3995 seq = SDPCM_PACKET_SEQUENCE(
3996 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
3997 doff = SDPCM_DOFFSET_VALUE(
3998 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
3999 txmax = SDPCM_WINDOW_VALUE(
4000 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004001
4002 bus->nextlen =
4003 bus->rxhdr[SDPCM_FRAMETAG_LEN +
4004 SDPCM_NEXTLEN_OFFSET];
4005 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
4006 DHD_INFO(("%s (nextlen): got frame w/nextlen too large" " (%d), seq %d\n",
4007 __func__, bus->nextlen, seq));
4008 bus->nextlen = 0;
4009 }
4010
4011 bus->dhd->rx_readahead_cnt++;
Grant Grundler4b455e02011-05-04 09:59:47 -07004012
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004013 /* Handle Flow Control */
Grant Grundler4b455e02011-05-04 09:59:47 -07004014 fcbits = SDPCM_FCMASK_VALUE(
4015 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004016
Grant Grundler4b455e02011-05-04 09:59:47 -07004017 if (bus->flowcontrol != fcbits) {
4018 if (~bus->flowcontrol & fcbits)
4019 bus->fc_xoff++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004020
Grant Grundler4b455e02011-05-04 09:59:47 -07004021 if (bus->flowcontrol & ~fcbits)
4022 bus->fc_xon++;
4023
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004024 bus->fc_rcvd++;
4025 bus->flowcontrol = fcbits;
4026 }
4027
4028 /* Check and update sequence number */
4029 if (rxseq != seq) {
4030 DHD_INFO(("%s (nextlen): rx_seq %d, expected "
4031 "%d\n", __func__, seq, rxseq));
4032 bus->rx_badseq++;
4033 rxseq = seq;
4034 }
4035
4036 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004037 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004038 DHD_ERROR(("%s: got unlikely tx max %d with "
4039 "tx_seq %d\n",
4040 __func__, txmax, bus->tx_seq));
4041 txmax = bus->tx_seq + 2;
4042 }
4043 bus->tx_max = txmax;
4044
4045#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004046 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
4047 printk(KERN_DEBUG "Rx Data:\n");
4048 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4049 rxbuf, len);
4050 } else if (DHD_HDRS_ON()) {
4051 printk(KERN_DEBUG "RxHdr:\n");
4052 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4053 bus->rxhdr, SDPCM_HDRLEN);
4054 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004055#endif
4056
4057 if (chan == SDPCM_CONTROL_CHANNEL) {
4058 if (bus->bus == SPI_BUS) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004059 brcmf_sdbrcm_read_control(bus, rxbuf,
4060 len, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004061 } else {
4062 DHD_ERROR(("%s (nextlen): readahead on control" " packet %d?\n",
4063 __func__, seq));
4064 /* Force retry w/normal header read */
4065 bus->nextlen = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07004066 brcmf_sdbrcm_rxfail(bus, false, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004067 }
Roland Vossen54ca2962011-06-29 16:46:59 -07004068 brcmf_sdbrcm_pktfree2(bus, pkt);
Grant Grundler4b455e02011-05-04 09:59:47 -07004069 continue;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004070 }
4071
4072 if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
4073 DHD_ERROR(("Received %d bytes on %d channel. Running out of " "rx pktbuf's or not yet malloced.\n",
4074 len, chan));
4075 continue;
4076 }
4077
4078 /* Validate data offset */
4079 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
4080 DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n",
4081 __func__, doff, len, SDPCM_HDRLEN));
Roland Vossen54ca2962011-06-29 16:46:59 -07004082 brcmf_sdbrcm_rxfail(bus, false, false);
4083 brcmf_sdbrcm_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004084 continue;
4085 }
4086
4087 /* All done with this one -- now deliver the packet */
4088 goto deliver;
4089 }
4090 /* gSPI frames should not be handled in fractions */
4091 if (bus->bus == SPI_BUS)
4092 break;
4093
4094 /* Read frame header (hardware and software) */
Roland Vossen54ca2962011-06-29 16:46:59 -07004095 sdret = brcmf_sdcard_recv_buf(sdh, brcmf_sdcard_cur_sbwad(sdh),
Grant Grundler4b455e02011-05-04 09:59:47 -07004096 SDIO_FUNC_2, F2SYNC, bus->rxhdr, firstread,
4097 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004098 bus->f2rxhdrs++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004099 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004100
4101 if (sdret < 0) {
4102 DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __func__,
4103 sdret));
4104 bus->rx_hdrfail++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004105 brcmf_sdbrcm_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004106 continue;
4107 }
4108#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004109 if (DHD_BYTES_ON() || DHD_HDRS_ON()) {
4110 printk(KERN_DEBUG "RxHdr:\n");
4111 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4112 bus->rxhdr, SDPCM_HDRLEN);
4113 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004114#endif
4115
4116 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03004117 len = get_unaligned_le16(bus->rxhdr);
4118 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004119
4120 /* All zeros means no more frames */
4121 if (!(len | check)) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004122 *finished = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004123 break;
4124 }
4125
4126 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004127 if ((u16) ~(len ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004128 DHD_ERROR(("%s: HW hdr err: len/check 0x%04x/0x%04x\n",
4129 __func__, len, check));
4130 bus->rx_badhdr++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004131 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004132 continue;
4133 }
4134
4135 /* Validate frame length */
4136 if (len < SDPCM_HDRLEN) {
4137 DHD_ERROR(("%s: HW hdr length invalid: %d\n",
4138 __func__, len));
4139 continue;
4140 }
4141
4142 /* Extract software header fields */
4143 chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4144 seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4145 doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4146 txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4147
4148 /* Validate data offset */
4149 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
4150 DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d "
4151 "seq %d\n",
4152 __func__, doff, len, SDPCM_HDRLEN, seq));
4153 bus->rx_badhdr++;
4154 ASSERT(0);
Roland Vossen54ca2962011-06-29 16:46:59 -07004155 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004156 continue;
4157 }
4158
4159 /* Save the readahead length if there is one */
4160 bus->nextlen =
4161 bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
4162 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
4163 DHD_INFO(("%s (nextlen): got frame w/nextlen too large "
4164 "(%d), seq %d\n",
4165 __func__, bus->nextlen, seq));
4166 bus->nextlen = 0;
4167 }
4168
4169 /* Handle Flow Control */
4170 fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4171
Grant Grundler4b455e02011-05-04 09:59:47 -07004172 if (bus->flowcontrol != fcbits) {
4173 if (~bus->flowcontrol & fcbits)
4174 bus->fc_xoff++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004175
Grant Grundler4b455e02011-05-04 09:59:47 -07004176 if (bus->flowcontrol & ~fcbits)
4177 bus->fc_xon++;
4178
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004179 bus->fc_rcvd++;
4180 bus->flowcontrol = fcbits;
4181 }
4182
4183 /* Check and update sequence number */
4184 if (rxseq != seq) {
4185 DHD_INFO(("%s: rx_seq %d, expected %d\n", __func__,
4186 seq, rxseq));
4187 bus->rx_badseq++;
4188 rxseq = seq;
4189 }
4190
4191 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004192 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004193 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
4194 __func__, txmax, bus->tx_seq));
4195 txmax = bus->tx_seq + 2;
4196 }
4197 bus->tx_max = txmax;
4198
4199 /* Call a separate function for control frames */
4200 if (chan == SDPCM_CONTROL_CHANNEL) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004201 brcmf_sdbrcm_read_control(bus, bus->rxhdr, len, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004202 continue;
4203 }
4204
4205 ASSERT((chan == SDPCM_DATA_CHANNEL)
4206 || (chan == SDPCM_EVENT_CHANNEL)
4207 || (chan == SDPCM_TEST_CHANNEL)
4208 || (chan == SDPCM_GLOM_CHANNEL));
4209
4210 /* Length to read */
4211 rdlen = (len > firstread) ? (len - firstread) : 0;
4212
4213 /* May pad read to blocksize for efficiency */
4214 if (bus->roundup && bus->blocksize &&
4215 (rdlen > bus->blocksize)) {
4216 pad = bus->blocksize - (rdlen % bus->blocksize);
4217 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
4218 ((rdlen + pad + firstread) < MAX_RX_DATASZ))
4219 rdlen += pad;
Arend van Spriel8d825a82011-06-29 16:47:25 -07004220 } else if (rdlen % BRCMF_SDALIGN) {
4221 rdlen += BRCMF_SDALIGN - (rdlen % BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004222 }
4223
4224 /* Satisfy length-alignment requirements */
4225 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07004226 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004227
4228 if ((rdlen + firstread) > MAX_RX_DATASZ) {
4229 /* Too long -- skip this frame */
4230 DHD_ERROR(("%s: too long: len %d rdlen %d\n",
4231 __func__, len, rdlen));
4232 bus->dhd->rx_errors++;
4233 bus->rx_toolong++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004234 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004235 continue;
4236 }
4237
Arend van Spriel8d825a82011-06-29 16:47:25 -07004238 pkt = brcmu_pkt_buf_get_skb(rdlen + firstread + BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004239 if (!pkt) {
4240 /* Give up on data, request rtx of events */
Roland Vossen67ad48b2011-06-01 13:45:51 +02004241 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed: rdlen %d"
4242 " chan %d\n", __func__, rdlen, chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004243 bus->dhd->rx_dropped++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004244 brcmf_sdbrcm_rxfail(bus, false, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004245 continue;
4246 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004247
Arend van Spriel54991ad2010-11-23 14:06:24 +01004248 ASSERT(!(pkt->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004249
4250 /* Leave room for what we already read, and align remainder */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004251 ASSERT(firstread < pkt->len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004252 skb_pull(pkt, firstread);
Arend van Spriel8d825a82011-06-29 16:47:25 -07004253 PKTALIGN(pkt, rdlen, BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004254
4255 /* Read the remaining frame data */
Roland Vossen54ca2962011-06-29 16:46:59 -07004256 sdret = brcmf_sdcard_recv_buf(sdh, brcmf_sdcard_cur_sbwad(sdh),
4257 SDIO_FUNC_2, F2SYNC, ((u8 *) (pkt->data)),
4258 rdlen, pkt, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004259 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004260 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004261
4262 if (sdret < 0) {
4263 DHD_ERROR(("%s: read %d %s bytes failed: %d\n",
4264 __func__, rdlen,
4265 ((chan ==
4266 SDPCM_EVENT_CHANNEL) ? "event" : ((chan ==
4267 SDPCM_DATA_CHANNEL)
4268 ? "data" : "test")),
4269 sdret));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004270 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004271 bus->dhd->rx_errors++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004272 brcmf_sdbrcm_rxfail(bus, true, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004273 continue;
4274 }
4275
4276 /* Copy the already-read portion */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004277 skb_push(pkt, firstread);
Stanislav Fomichev02160692011-02-15 01:05:10 +03004278 memcpy(pkt->data, bus->rxhdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004279
4280#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004281 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
4282 printk(KERN_DEBUG "Rx Data:\n");
4283 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4284 pkt->data, len);
4285 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004286#endif
4287
4288deliver:
4289 /* Save superframe descriptor and allocate packet frame */
4290 if (chan == SDPCM_GLOM_CHANNEL) {
4291 if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
4292 DHD_GLOM(("%s: glom descriptor, %d bytes:\n",
4293 __func__, len));
4294#ifdef DHD_DEBUG
4295 if (DHD_GLOM_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02004296 printk(KERN_DEBUG "Glom Data:\n");
4297 print_hex_dump_bytes("",
4298 DUMP_PREFIX_OFFSET,
4299 pkt->data, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004300 }
4301#endif
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004302 __skb_trim(pkt, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004303 ASSERT(doff == SDPCM_HDRLEN);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004304 skb_pull(pkt, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004305 bus->glomd = pkt;
4306 } else {
4307 DHD_ERROR(("%s: glom superframe w/o "
4308 "descriptor!\n", __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07004309 brcmf_sdbrcm_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004310 }
4311 continue;
4312 }
4313
4314 /* Fill in packet len and prio, deliver upward */
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004315 __skb_trim(pkt, len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004316 skb_pull(pkt, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004317
4318#ifdef SDTEST
4319 /* Test channel packets are processed separately */
4320 if (chan == SDPCM_TEST_CHANNEL) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004321 brcmf_sdbrcm_checkdied(bus, pkt, seq);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004322 continue;
4323 }
4324#endif /* SDTEST */
4325
Arend van Spriel54991ad2010-11-23 14:06:24 +01004326 if (pkt->len == 0) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004327 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004328 continue;
Arend van Spriel0aa94b82011-06-29 16:47:13 -07004329 } else if (brcmf_proto_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004330 DHD_ERROR(("%s: rx protocol error\n", __func__));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004331 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004332 bus->dhd->rx_errors++;
4333 continue;
4334 }
4335
4336 /* Unlock during rx call */
Arend van Spriela84bac42011-06-29 16:47:10 -07004337 brcmf_os_sdunlock(bus->dhd);
4338 brcmf_rx_frame(bus->dhd, ifidx, pkt, 1);
4339 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004340 }
4341 rxcount = maxframes - rxleft;
4342#ifdef DHD_DEBUG
4343 /* Message if we hit the limit */
4344 if (!rxleft && !sdtest)
4345 DHD_DATA(("%s: hit rx limit of %d frames\n", __func__,
4346 maxframes));
4347 else
4348#endif /* DHD_DEBUG */
4349 DHD_DATA(("%s: processed %d frames\n", __func__, rxcount));
4350 /* Back off rxseq if awaiting rtx, update rx_seq */
4351 if (bus->rxskip)
4352 rxseq--;
4353 bus->rx_seq = rxseq;
4354
4355 return rxcount;
4356}
4357
Roland Vossen54ca2962011-06-29 16:46:59 -07004358static u32 brcmf_sdbrcm_hostmail(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004359{
Franky Lin597600a2011-06-01 13:45:39 +02004360 struct sdpcmd_regs *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004361 u32 intstatus = 0;
4362 u32 hmb_data;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004363 u8 fcbits;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004364 uint retries = 0;
4365
4366 DHD_TRACE(("%s: Enter\n", __func__));
4367
4368 /* Read mailbox data and ack that we did so */
4369 R_SDREG(hmb_data, &regs->tohostmailboxdata, retries);
4370 if (retries <= retry_limit)
4371 W_SDREG(SMB_INT_ACK, &regs->tosbmailbox, retries);
4372 bus->f1regdata += 2;
4373
4374 /* Dongle recomposed rx frames, accept them again */
4375 if (hmb_data & HMB_DATA_NAKHANDLED) {
4376 DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n",
4377 bus->rx_seq));
4378 if (!bus->rxskip)
4379 DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __func__));
4380
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004381 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004382 intstatus |= I_HMB_FRAME_IND;
4383 }
4384
4385 /*
4386 * DEVREADY does not occur with gSPI.
4387 */
4388 if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
4389 bus->sdpcm_ver =
4390 (hmb_data & HMB_DATA_VERSION_MASK) >>
4391 HMB_DATA_VERSION_SHIFT;
4392 if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
4393 DHD_ERROR(("Version mismatch, dongle reports %d, "
4394 "expecting %d\n",
4395 bus->sdpcm_ver, SDPCM_PROT_VERSION));
4396 else
4397 DHD_INFO(("Dongle ready, protocol version %d\n",
4398 bus->sdpcm_ver));
4399 }
4400
4401 /*
4402 * Flow Control has been moved into the RX headers and this out of band
Grant Grundler4b455e02011-05-04 09:59:47 -07004403 * method isn't used any more.
4404 * remaining backward compatible with older dongles.
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004405 */
4406 if (hmb_data & HMB_DATA_FC) {
Grant Grundler4b455e02011-05-04 09:59:47 -07004407 fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
4408 HMB_DATA_FCDATA_SHIFT;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004409
4410 if (fcbits & ~bus->flowcontrol)
4411 bus->fc_xoff++;
Grant Grundler4b455e02011-05-04 09:59:47 -07004412
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004413 if (bus->flowcontrol & ~fcbits)
4414 bus->fc_xon++;
4415
4416 bus->fc_rcvd++;
4417 bus->flowcontrol = fcbits;
4418 }
4419
4420 /* Shouldn't be any others */
4421 if (hmb_data & ~(HMB_DATA_DEVREADY |
4422 HMB_DATA_NAKHANDLED |
4423 HMB_DATA_FC |
4424 HMB_DATA_FWREADY |
4425 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) {
4426 DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data));
4427 }
4428
4429 return intstatus;
4430}
4431
Roland Vossen54ca2962011-06-29 16:46:59 -07004432bool brcmf_sdbrcm_dpc(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004433{
Roland Vossen677226a2011-06-29 16:47:21 -07004434 struct brcmf_sdio *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02004435 struct sdpcmd_regs *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004436 u32 intstatus, newstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004437 uint retries = 0;
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07004438 uint rxlimit = brcmf_rxbound; /* Rx frames to read before resched */
4439 uint txlimit = brcmf_txbound; /* Tx frames to send before resched */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004440 uint framecnt = 0; /* Temporary counter of tx/rx frames */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004441 bool rxdone = true; /* Flag for no more read data */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004442 bool resched = false; /* Flag indicating resched wanted */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004443
4444 DHD_TRACE(("%s: Enter\n", __func__));
4445
4446 /* Start with leftover status bits */
4447 intstatus = bus->intstatus;
4448
Arend van Spriela84bac42011-06-29 16:47:10 -07004449 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004450
4451 /* If waiting for HTAVAIL, check status */
4452 if (bus->clkstate == CLK_PENDING) {
4453 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004454 u8 clkctl, devctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004455
4456#ifdef DHD_DEBUG
4457 /* Check for inconsistent device control */
Roland Vossen54ca2962011-06-29 16:46:59 -07004458 devctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
4459 SBSDIO_DEVICE_CTL, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004460 if (err) {
4461 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4462 __func__, err));
4463 bus->dhd->busstate = DHD_BUS_DOWN;
4464 } else {
4465 ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY);
4466 }
4467#endif /* DHD_DEBUG */
4468
4469 /* Read CSR, if clock on switch to AVAIL, else ignore */
Roland Vossen54ca2962011-06-29 16:46:59 -07004470 clkctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
4471 SBSDIO_FUNC1_CHIPCLKCSR, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004472 if (err) {
4473 DHD_ERROR(("%s: error reading CSR: %d\n", __func__,
4474 err));
4475 bus->dhd->busstate = DHD_BUS_DOWN;
4476 }
4477
4478 DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl,
4479 clkctl));
4480
4481 if (SBSDIO_HTAV(clkctl)) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004482 devctl = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
4483 SBSDIO_DEVICE_CTL, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004484 if (err) {
4485 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4486 __func__, err));
4487 bus->dhd->busstate = DHD_BUS_DOWN;
4488 }
4489 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
Roland Vossen54ca2962011-06-29 16:46:59 -07004490 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
4491 SBSDIO_DEVICE_CTL, devctl, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004492 if (err) {
4493 DHD_ERROR(("%s: error writing DEVCTL: %d\n",
4494 __func__, err));
4495 bus->dhd->busstate = DHD_BUS_DOWN;
4496 }
4497 bus->clkstate = CLK_AVAIL;
4498 } else {
4499 goto clkwait;
4500 }
4501 }
4502
4503 BUS_WAKE(bus);
4504
4505 /* Make sure backplane clock is on */
Roland Vossen54ca2962011-06-29 16:46:59 -07004506 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004507 if (bus->clkstate == CLK_PENDING)
4508 goto clkwait;
4509
4510 /* Pending interrupt indicates new device status */
4511 if (bus->ipend) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004512 bus->ipend = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004513 R_SDREG(newstatus, &regs->intstatus, retries);
4514 bus->f1regdata++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004515 if (brcmf_sdcard_regfail(bus->sdh))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004516 newstatus = 0;
4517 newstatus &= bus->hostintmask;
4518 bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
4519 if (newstatus) {
4520 W_SDREG(newstatus, &regs->intstatus, retries);
4521 bus->f1regdata++;
4522 }
4523 }
4524
4525 /* Merge new bits with previous */
4526 intstatus |= newstatus;
4527 bus->intstatus = 0;
4528
4529 /* Handle flow-control change: read new state in case our ack
4530 * crossed another change interrupt. If change still set, assume
4531 * FC ON for safety, let next loop through do the debounce.
4532 */
4533 if (intstatus & I_HMB_FC_CHANGE) {
4534 intstatus &= ~I_HMB_FC_CHANGE;
4535 W_SDREG(I_HMB_FC_CHANGE, &regs->intstatus, retries);
4536 R_SDREG(newstatus, &regs->intstatus, retries);
4537 bus->f1regdata += 2;
4538 bus->fcstate =
4539 !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
4540 intstatus |= (newstatus & bus->hostintmask);
4541 }
4542
4543 /* Handle host mailbox indication */
4544 if (intstatus & I_HMB_HOST_INT) {
4545 intstatus &= ~I_HMB_HOST_INT;
Roland Vossen54ca2962011-06-29 16:46:59 -07004546 intstatus |= brcmf_sdbrcm_hostmail(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004547 }
4548
4549 /* Generally don't ask for these, can get CRC errors... */
4550 if (intstatus & I_WR_OOSYNC) {
4551 DHD_ERROR(("Dongle reports WR_OOSYNC\n"));
4552 intstatus &= ~I_WR_OOSYNC;
4553 }
4554
4555 if (intstatus & I_RD_OOSYNC) {
4556 DHD_ERROR(("Dongle reports RD_OOSYNC\n"));
4557 intstatus &= ~I_RD_OOSYNC;
4558 }
4559
4560 if (intstatus & I_SBINT) {
4561 DHD_ERROR(("Dongle reports SBINT\n"));
4562 intstatus &= ~I_SBINT;
4563 }
4564
4565 /* Would be active due to wake-wlan in gSPI */
4566 if (intstatus & I_CHIPACTIVE) {
4567 DHD_INFO(("Dongle reports CHIPACTIVE\n"));
4568 intstatus &= ~I_CHIPACTIVE;
4569 }
4570
4571 /* Ignore frame indications if rxskip is set */
4572 if (bus->rxskip)
4573 intstatus &= ~I_HMB_FRAME_IND;
4574
4575 /* On frame indication, read available frames */
4576 if (PKT_AVAILABLE()) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004577 framecnt = brcmf_sdbrcm_readframes(bus, rxlimit, &rxdone);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004578 if (rxdone || bus->rxskip)
4579 intstatus &= ~I_HMB_FRAME_IND;
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004580 rxlimit -= min(framecnt, rxlimit);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004581 }
4582
4583 /* Keep still-pending events for next scheduling */
4584 bus->intstatus = intstatus;
4585
4586clkwait:
4587#if defined(OOB_INTR_ONLY)
Roland Vossen54ca2962011-06-29 16:46:59 -07004588 brcmf_sdio_oob_intr_set(1);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004589#endif /* (OOB_INTR_ONLY) */
4590 /* Re-enable interrupts to detect new device events (mailbox, rx frame)
4591 * or clock availability. (Allows tx loop to check ipend if desired.)
4592 * (Unless register access seems hosed, as we may not be able to ACK...)
4593 */
Roland Vossen54ca2962011-06-29 16:46:59 -07004594 if (bus->intr && bus->intdis && !brcmf_sdcard_regfail(sdh)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004595 DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n",
4596 __func__, rxdone, framecnt));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004597 bus->intdis = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07004598 brcmf_sdcard_intr_enable(sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004599 }
4600
4601 if (DATAOK(bus) && bus->ctrl_frame_stat &&
4602 (bus->clkstate == CLK_AVAIL)) {
4603 int ret, i;
4604
Roland Vossen54ca2962011-06-29 16:46:59 -07004605 ret = brcmf_sdbrcm_send_buf(bus, brcmf_sdcard_cur_sbwad(sdh),
4606 SDIO_FUNC_2, F2SYNC, (u8 *) bus->ctrl_frame_buf,
4607 (u32) bus->ctrl_frame_len, NULL, NULL, NULL);
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004608 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004609
4610 if (ret < 0) {
4611 /* On failure, abort the command and
4612 terminate the frame */
4613 DHD_INFO(("%s: sdio error %d, abort command and "
4614 "terminate frame.\n", __func__, ret));
4615 bus->tx_sderrs++;
4616
Roland Vossen54ca2962011-06-29 16:46:59 -07004617 brcmf_sdcard_abort(sdh, SDIO_FUNC_2);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004618
Roland Vossen54ca2962011-06-29 16:46:59 -07004619 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004620 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
4621 NULL);
4622 bus->f1regdata++;
4623
4624 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004625 u8 hi, lo;
Roland Vossen54ca2962011-06-29 16:46:59 -07004626 hi = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004627 SBSDIO_FUNC1_WFRAMEBCHI,
4628 NULL);
Roland Vossen54ca2962011-06-29 16:46:59 -07004629 lo = brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004630 SBSDIO_FUNC1_WFRAMEBCLO,
4631 NULL);
4632 bus->f1regdata += 2;
4633 if ((hi == 0) && (lo == 0))
4634 break;
4635 }
4636
4637 }
4638 if (ret == 0)
4639 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
4640
Arend van Spriel0bef7742011-02-10 12:03:44 +01004641 DHD_INFO(("Return_dpc value is : %d\n", ret));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004642 bus->ctrl_frame_stat = false;
Arend van Spriela84bac42011-06-29 16:47:10 -07004643 brcmf_wait_event_wakeup(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004644 }
4645 /* Send queued frames (limit 1 if rx may still be pending) */
4646 else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
Roland Vossen67ad48b2011-06-01 13:45:51 +02004647 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004648 && DATAOK(bus)) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004649 framecnt = rxdone ? txlimit : min(txlimit, dhd_txminmax);
Roland Vossen54ca2962011-06-29 16:46:59 -07004650 framecnt = brcmf_sdbrcm_sendfromq(bus, framecnt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004651 txlimit -= framecnt;
4652 }
4653
4654 /* Resched if events or tx frames are pending,
4655 else await next interrupt */
4656 /* On failed register access, all bets are off:
4657 no resched or interrupts */
Roland Vossen54ca2962011-06-29 16:46:59 -07004658 if ((bus->dhd->busstate == DHD_BUS_DOWN) || brcmf_sdcard_regfail(sdh)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004659 DHD_ERROR(("%s: failed backplane access over SDIO, halting "
Roland Vossen54ca2962011-06-29 16:46:59 -07004660 "operation %d\n", __func__, brcmf_sdcard_regfail(sdh)));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004661 bus->dhd->busstate = DHD_BUS_DOWN;
4662 bus->intstatus = 0;
4663 } else if (bus->clkstate == CLK_PENDING) {
4664 DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting "
4665 "I_CHIPACTIVE interrupt\n", __func__));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004666 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004667 } else if (bus->intstatus || bus->ipend ||
Roland Vossen67ad48b2011-06-01 13:45:51 +02004668 (!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
4669 && DATAOK(bus)) || PKT_AVAILABLE()) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004670 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004671 }
4672
4673 bus->dpc_sched = resched;
4674
4675 /* If we're done for now, turn off clock request. */
4676 if ((bus->clkstate != CLK_PENDING)
4677 && bus->idletime == DHD_IDLE_IMMEDIATE) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004678 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07004679 brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004680 }
4681
Arend van Spriela84bac42011-06-29 16:47:10 -07004682 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004683
4684 return resched;
4685}
4686
4687bool dhd_bus_dpc(struct dhd_bus *bus)
4688{
4689 bool resched;
4690
4691 /* Call the DPC directly. */
Roland Vossen54ca2962011-06-29 16:46:59 -07004692 DHD_TRACE(("Calling brcmf_sdbrcm_dpc() from %s\n", __func__));
4693 resched = brcmf_sdbrcm_dpc(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004694
4695 return resched;
4696}
4697
Roland Vossen54ca2962011-06-29 16:46:59 -07004698void brcmf_sdbrcm_isr(void *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004699{
4700 dhd_bus_t *bus = (dhd_bus_t *) arg;
Roland Vossen677226a2011-06-29 16:47:21 -07004701 struct brcmf_sdio *sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004702
4703 DHD_TRACE(("%s: Enter\n", __func__));
4704
4705 if (!bus) {
4706 DHD_ERROR(("%s : bus is null pointer , exit\n", __func__));
4707 return;
4708 }
4709 sdh = bus->sdh;
4710
4711 if (bus->dhd->busstate == DHD_BUS_DOWN) {
4712 DHD_ERROR(("%s : bus is down. we have nothing to do\n",
4713 __func__));
4714 return;
4715 }
4716 /* Count the interrupt call */
4717 bus->intrcount++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004718 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004719
4720 /* Shouldn't get this interrupt if we're sleeping? */
4721 if (bus->sleeping) {
4722 DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
4723 return;
4724 }
4725
4726 /* Disable additional interrupts (is this needed now)? */
4727 if (bus->intr)
4728 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
4729 else
Roland Vossen54ca2962011-06-29 16:46:59 -07004730 DHD_ERROR(("brcmf_sdbrcm_isr() w/o interrupt configured!\n"));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004731
Roland Vossen54ca2962011-06-29 16:46:59 -07004732 brcmf_sdcard_intr_disable(sdh);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004733 bus->intdis = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004734
4735#if defined(SDIO_ISR_THREAD)
Roland Vossen54ca2962011-06-29 16:46:59 -07004736 DHD_TRACE(("Calling brcmf_sdbrcm_dpc() from %s\n", __func__));
4737 while (brcmf_sdbrcm_dpc(bus))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004738 ;
4739#else
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004740 bus->dpc_sched = true;
Arend van Spriela84bac42011-06-29 16:47:10 -07004741 brcmf_sched_dpc(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004742#endif
4743
4744}
4745
4746#ifdef SDTEST
Roland Vossen54ca2962011-06-29 16:46:59 -07004747static void brcmf_sdbrcm_pktgen_init(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004748{
4749 /* Default to specified length, or full range */
Arend van Spriel5e92aa82011-06-29 16:47:08 -07004750 if (brcmf_pktgen_len) {
4751 bus->pktgen_maxlen = min(brcmf_pktgen_len,
4752 BRCMF_MAX_PKTGEN_LEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004753 bus->pktgen_minlen = bus->pktgen_maxlen;
4754 } else {
Arend van Spriel5e92aa82011-06-29 16:47:08 -07004755 bus->pktgen_maxlen = BRCMF_MAX_PKTGEN_LEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004756 bus->pktgen_minlen = 0;
4757 }
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004758 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004759
4760 /* Default to per-watchdog burst with 10s print time */
4761 bus->pktgen_freq = 1;
Arend van Spriel5e92aa82011-06-29 16:47:08 -07004762 bus->pktgen_print = 10000 / brcmf_watchdog_ms;
4763 bus->pktgen_count = (brcmf_pktgen * brcmf_watchdog_ms + 999) / 1000;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004764
4765 /* Default to echo mode */
4766 bus->pktgen_mode = DHD_PKTGEN_ECHO;
4767 bus->pktgen_stop = 1;
4768}
4769
Roland Vossen54ca2962011-06-29 16:46:59 -07004770static void brcmf_sdbrcm_pktgen(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004771{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004772 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004773 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004774 uint pktcount;
4775 uint fillbyte;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004776 u16 len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004777
4778 /* Display current count if appropriate */
4779 if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
4780 bus->pktgen_ptick = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01004781 printk(KERN_DEBUG "%s: send attempts %d rcvd %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004782 __func__, bus->pktgen_sent, bus->pktgen_rcvd);
4783 }
4784
4785 /* For recv mode, just make sure dongle has started sending */
4786 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
4787 if (!bus->pktgen_rcvd)
Roland Vossen54ca2962011-06-29 16:46:59 -07004788 brcmf_sdbrcm_sdtest_set(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004789 return;
4790 }
4791
4792 /* Otherwise, generate or request the specified number of packets */
4793 for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
4794 /* Stop if total has been reached */
4795 if (bus->pktgen_total
4796 && (bus->pktgen_sent >= bus->pktgen_total)) {
4797 bus->pktgen_count = 0;
4798 break;
4799 }
4800
4801 /* Allocate an appropriate-sized packet */
4802 len = bus->pktgen_len;
Roland Vossen67ad48b2011-06-01 13:45:51 +02004803 pkt = brcmu_pkt_buf_get_skb(
Arend van Spriel8d825a82011-06-29 16:47:25 -07004804 (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + BRCMF_SDALIGN),
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004805 true);
Jason Cooper9b890322010-09-30 15:15:39 -04004806 if (!pkt) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004807 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n",
4808 __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004809 break;
4810 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004811 PKTALIGN(pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN),
Arend van Spriel8d825a82011-06-29 16:47:25 -07004812 BRCMF_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004813 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004814
4815 /* Write test header cmd and extra based on mode */
4816 switch (bus->pktgen_mode) {
4817 case DHD_PKTGEN_ECHO:
4818 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004819 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004820 break;
4821
4822 case DHD_PKTGEN_SEND:
4823 *data++ = SDPCM_TEST_DISCARD;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004824 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004825 break;
4826
4827 case DHD_PKTGEN_RXBURST:
4828 *data++ = SDPCM_TEST_BURST;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004829 *data++ = (u8) bus->pktgen_count;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004830 break;
4831
4832 default:
4833 DHD_ERROR(("Unrecognized pktgen mode %d\n",
4834 bus->pktgen_mode));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004835 brcmu_pkt_buf_free_skb(pkt, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004836 bus->pktgen_count = 0;
4837 return;
4838 }
4839
4840 /* Write test header length field */
4841 *data++ = (len >> 0);
4842 *data++ = (len >> 8);
4843
4844 /* Then fill in the remainder -- N/A for burst,
4845 but who cares... */
4846 for (fillbyte = 0; fillbyte < len; fillbyte++)
4847 *data++ =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004848 SDPCM_TEST_FILL(fillbyte, (u8) bus->pktgen_sent);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004849
4850#ifdef DHD_DEBUG
4851 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01004852 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Roland Vossen54ca2962011-06-29 16:46:59 -07004853 printk(KERN_DEBUG "brcmf_sdbrcm_pktgen: Tx Data:\n");
Arend van Spriel34227312011-05-10 22:25:32 +02004854 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data,
4855 pkt->len - SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004856 }
4857#endif
4858
4859 /* Send it */
Roland Vossen54ca2962011-06-29 16:46:59 -07004860 if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004861 bus->pktgen_fail++;
4862 if (bus->pktgen_stop
4863 && bus->pktgen_stop == bus->pktgen_fail)
4864 bus->pktgen_count = 0;
4865 }
4866 bus->pktgen_sent++;
4867
4868 /* Bump length if not fixed, wrap at max */
4869 if (++bus->pktgen_len > bus->pktgen_maxlen)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004870 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004871
4872 /* Special case for burst mode: just send one request! */
4873 if (bus->pktgen_mode == DHD_PKTGEN_RXBURST)
4874 break;
4875 }
4876}
4877
Roland Vossen54ca2962011-06-29 16:46:59 -07004878static void brcmf_sdbrcm_sdtest_set(dhd_bus_t *bus, bool start)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004879{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004880 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004881 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004882
4883 /* Allocate the packet */
Roland Vossen67ad48b2011-06-01 13:45:51 +02004884 pkt = brcmu_pkt_buf_get_skb(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN +
Arend van Spriel8d825a82011-06-29 16:47:25 -07004885 BRCMF_SDALIGN, true);
Jason Cooper9b890322010-09-30 15:15:39 -04004886 if (!pkt) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004887 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004888 return;
4889 }
Arend van Spriel8d825a82011-06-29 16:47:25 -07004890 PKTALIGN(pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), BRCMF_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004891 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004892
4893 /* Fill in the test header */
4894 *data++ = SDPCM_TEST_SEND;
4895 *data++ = start;
4896 *data++ = (bus->pktgen_maxlen >> 0);
4897 *data++ = (bus->pktgen_maxlen >> 8);
4898
4899 /* Send it */
Roland Vossen54ca2962011-06-29 16:46:59 -07004900 if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004901 bus->pktgen_fail++;
4902}
4903
Roland Vossen54ca2962011-06-29 16:46:59 -07004904static void
4905brcmf_sdbrcm_checkdied(dhd_bus_t *bus, struct sk_buff *pkt, uint seq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004906{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004907 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004908 uint pktlen;
4909
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004910 u8 cmd;
4911 u8 extra;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004912 u16 len;
4913 u16 offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004914
4915 /* Check for min length */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004916 pktlen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004917 if (pktlen < SDPCM_TEST_HDRLEN) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004918 DHD_ERROR(("brcmf_sdbrcm_checkdied: toss runt frame, pktlen "
4919 "%d\n", pktlen));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004920 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004921 return;
4922 }
4923
4924 /* Extract header fields */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004925 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004926 cmd = *data++;
4927 extra = *data++;
4928 len = *data++;
4929 len += *data++ << 8;
4930
4931 /* Check length for relevant commands */
4932 if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ
4933 || cmd == SDPCM_TEST_ECHORSP) {
4934 if (pktlen != len + SDPCM_TEST_HDRLEN) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004935 DHD_ERROR(("brcmf_sdbrcm_checkdied: frame length "
4936 "mismatch, pktlen %d seq %d" " cmd %d extra %d "
4937 "len %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004938 pktlen, seq, cmd, extra, len));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004939 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004940 return;
4941 }
4942 }
4943
4944 /* Process as per command */
4945 switch (cmd) {
4946 case SDPCM_TEST_ECHOREQ:
4947 /* Rx->Tx turnaround ok (even on NDIS w/current
4948 implementation) */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004949 *(u8 *) (pkt->data) = SDPCM_TEST_ECHORSP;
Roland Vossen54ca2962011-06-29 16:46:59 -07004950 if (brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true) == 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004951 bus->pktgen_sent++;
Roland Vossen54ca2962011-06-29 16:46:59 -07004952 else {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004953 bus->pktgen_fail++;
Roland Vossen67ad48b2011-06-01 13:45:51 +02004954 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004955 }
4956 bus->pktgen_rcvd++;
4957 break;
4958
4959 case SDPCM_TEST_ECHORSP:
4960 if (bus->ext_loop) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004961 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004962 bus->pktgen_rcvd++;
4963 break;
4964 }
4965
4966 for (offset = 0; offset < len; offset++, data++) {
4967 if (*data != SDPCM_TEST_FILL(offset, extra)) {
Roland Vossen54ca2962011-06-29 16:46:59 -07004968 DHD_ERROR(("brcmf_sdbrcm_checkdied: echo data "
4969 "mismatch: " "offset %d (len %d) "
4970 "expect 0x%02x rcvd 0x%02x\n",
4971 offset, len,
4972 SDPCM_TEST_FILL(offset, extra),
4973 *data));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004974 break;
4975 }
4976 }
Roland Vossen67ad48b2011-06-01 13:45:51 +02004977 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004978 bus->pktgen_rcvd++;
4979 break;
4980
4981 case SDPCM_TEST_DISCARD:
Roland Vossen67ad48b2011-06-01 13:45:51 +02004982 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004983 bus->pktgen_rcvd++;
4984 break;
4985
4986 case SDPCM_TEST_BURST:
4987 case SDPCM_TEST_SEND:
4988 default:
Roland Vossen54ca2962011-06-29 16:46:59 -07004989 DHD_INFO(("brcmf_sdbrcm_checkdied: unsupported or unknown "
4990 "command, pktlen %d seq %d" " cmd %d extra %d len %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004991 pktlen, seq, cmd, extra, len));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004992 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004993 break;
4994 }
4995
4996 /* For recv mode, stop at limie (and tell dongle to stop sending) */
4997 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
4998 if (bus->pktgen_total
4999 && (bus->pktgen_rcvd >= bus->pktgen_total)) {
5000 bus->pktgen_count = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07005001 brcmf_sdbrcm_sdtest_set(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005002 }
5003 }
5004}
5005#endif /* SDTEST */
5006
Roland Vossen54ca2962011-06-29 16:46:59 -07005007extern bool brcmf_sdbrcm_bus_watchdog(dhd_pub_t *dhdp)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005008{
5009 dhd_bus_t *bus;
5010
5011 DHD_TIMER(("%s: Enter\n", __func__));
5012
5013 bus = dhdp->bus;
5014
5015 if (bus->dhd->dongle_reset)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005016 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005017
5018 /* Ignore the timer if simulating bus down */
5019 if (bus->sleeping)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005020 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005021
Arend van Spriela84bac42011-06-29 16:47:10 -07005022 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005023
5024 /* Poll period: check device if appropriate. */
5025 if (bus->poll && (++bus->polltick >= bus->pollrate)) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005026 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005027
5028 /* Reset poll tick */
5029 bus->polltick = 0;
5030
5031 /* Check device if no interrupts */
5032 if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
5033
5034 if (!bus->dpc_sched) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005035 u8 devpend;
Roland Vossen54ca2962011-06-29 16:46:59 -07005036 devpend = brcmf_sdcard_cfg_read(bus->sdh,
5037 SDIO_FUNC_0, SDIO_CCCR_INTx,
5038 NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005039 intstatus =
5040 devpend & (INTR_STATUS_FUNC1 |
5041 INTR_STATUS_FUNC2);
5042 }
5043
5044 /* If there is something, make like the ISR and
5045 schedule the DPC */
5046 if (intstatus) {
5047 bus->pollcnt++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005048 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005049 if (bus->intr)
Roland Vossen54ca2962011-06-29 16:46:59 -07005050 brcmf_sdcard_intr_disable(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005051
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005052 bus->dpc_sched = true;
Arend van Spriela84bac42011-06-29 16:47:10 -07005053 brcmf_sched_dpc(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005054
5055 }
5056 }
5057
5058 /* Update interrupt tracking */
5059 bus->lastintrs = bus->intrcount;
5060 }
5061#ifdef DHD_DEBUG
5062 /* Poll for console output periodically */
Arend van Spriel5e92aa82011-06-29 16:47:08 -07005063 if (dhdp->busstate == DHD_BUS_DATA && brcmf_console_ms != 0) {
5064 bus->console.count += brcmf_watchdog_ms;
5065 if (bus->console.count >= brcmf_console_ms) {
5066 bus->console.count -= brcmf_console_ms;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005067 /* Make sure backplane clock is on */
Roland Vossen54ca2962011-06-29 16:46:59 -07005068 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
5069 if (brcmf_sdbrcm_readconsole(bus) < 0)
Arend van Spriel5e92aa82011-06-29 16:47:08 -07005070 brcmf_console_ms = 0; /* On error,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005071 stop trying */
5072 }
5073 }
5074#endif /* DHD_DEBUG */
5075
5076#ifdef SDTEST
5077 /* Generate packets if configured */
5078 if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
5079 /* Make sure backplane clock is on */
Roland Vossen54ca2962011-06-29 16:46:59 -07005080 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005081 bus->pktgen_tick = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07005082 brcmf_sdbrcm_pktgen(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005083 }
5084#endif
5085
5086 /* On idle timeout clear activity flag and/or turn off clock */
5087 if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
5088 if (++bus->idlecount >= bus->idletime) {
5089 bus->idlecount = 0;
5090 if (bus->activity) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005091 bus->activity = false;
Arend van Spriela84bac42011-06-29 16:47:10 -07005092 brcmf_os_wd_timer(bus->dhd, brcmf_watchdog_ms);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005093 } else {
Roland Vossen54ca2962011-06-29 16:46:59 -07005094 brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005095 }
5096 }
5097 }
5098
Arend van Spriela84bac42011-06-29 16:47:10 -07005099 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005100
5101 return bus->ipend;
5102}
5103
5104#ifdef DHD_DEBUG
Roland Vossen54ca2962011-06-29 16:46:59 -07005105extern int brcmf_sdbrcm_bus_console_in(dhd_pub_t *dhdp, unsigned char *msg,
5106 uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005107{
5108 dhd_bus_t *bus = dhdp->bus;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005109 u32 addr, val;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005110 int rv;
Arend van Sprielc26b1372010-11-23 14:06:23 +01005111 struct sk_buff *pkt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005112
5113 /* Address could be zero if CONSOLE := 0 in dongle Makefile */
5114 if (bus->console_addr == 0)
Roland Vossene10d82d2011-05-03 11:35:19 +02005115 return -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005116
5117 /* Exclusive bus access */
Arend van Spriela84bac42011-06-29 16:47:10 -07005118 brcmf_os_sdlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005119
5120 /* Don't allow input if dongle is in reset */
5121 if (bus->dhd->dongle_reset) {
Arend van Spriela84bac42011-06-29 16:47:10 -07005122 brcmf_os_sdunlock(bus->dhd);
Roland Vossenb74ac122011-05-03 11:35:20 +02005123 return -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005124 }
5125
5126 /* Request clock to allow SDIO accesses */
5127 BUS_WAKE(bus);
5128 /* No pend allowed since txpkt is called later, ht clk has to be on */
Roland Vossen54ca2962011-06-29 16:46:59 -07005129 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005130
5131 /* Zero cbuf_index */
Roland Vossen51851252011-06-29 16:47:24 -07005132 addr = bus->console_addr + offsetof(struct rte_console, cbuf_idx);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03005133 val = cpu_to_le32(0);
Roland Vossen54ca2962011-06-29 16:46:59 -07005134 rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04005135 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005136 goto done;
5137
5138 /* Write message into cbuf */
Roland Vossen51851252011-06-29 16:47:24 -07005139 addr = bus->console_addr + offsetof(struct rte_console, cbuf);
Roland Vossen54ca2962011-06-29 16:46:59 -07005140 rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)msg, msglen);
Jason Cooper9b890322010-09-30 15:15:39 -04005141 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005142 goto done;
5143
5144 /* Write length into vcons_in */
Roland Vossen51851252011-06-29 16:47:24 -07005145 addr = bus->console_addr + offsetof(struct rte_console, vcons_in);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03005146 val = cpu_to_le32(msglen);
Roland Vossen54ca2962011-06-29 16:46:59 -07005147 rv = brcmf_sdbrcm_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04005148 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005149 goto done;
5150
5151 /* Bump dongle by sending an empty event pkt.
5152 * sdpcm_sendup (RX) checks for virtual console input.
5153 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02005154 pkt = brcmu_pkt_buf_get_skb(4 + SDPCM_RESERVE);
Jason Cooper9b890322010-09-30 15:15:39 -04005155 if ((pkt != NULL) && bus->clkstate == CLK_AVAIL)
Roland Vossen54ca2962011-06-29 16:46:59 -07005156 brcmf_sdbrcm_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005157
5158done:
5159 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005160 bus->activity = false;
Roland Vossen54ca2962011-06-29 16:46:59 -07005161 brcmf_sdbrcm_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005162 }
5163
Arend van Spriela84bac42011-06-29 16:47:10 -07005164 brcmf_os_sdunlock(bus->dhd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005165
5166 return rv;
5167}
5168#endif /* DHD_DEBUG */
5169
Roland Vossen54ca2962011-06-29 16:46:59 -07005170static bool brcmf_sdbrcm_chipmatch(u16 chipid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005171{
5172 if (chipid == BCM4325_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005173 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005174 if (chipid == BCM4329_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005175 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005176 if (chipid == BCM4319_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005177 return true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005178 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005179}
5180
Roland Vossen54ca2962011-06-29 16:46:59 -07005181static void *brcmf_sdbrcm_probe(u16 venid, u16 devid, u16 bus_no,
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005182 u16 slot, u16 func, uint bustype, void *regsva,
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005183 void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005184{
5185 int ret;
5186 dhd_bus_t *bus;
5187
5188 /* Init global variables at run-time, not as part of the declaration.
5189 * This is required to support init/de-init of the driver.
5190 * Initialization
5191 * of globals as part of the declaration results in non-deterministic
5192 * behavior since the value of the globals may be different on the
5193 * first time that the driver is initialized vs subsequent
5194 * initializations.
5195 */
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07005196 brcmf_txbound = DHD_TXBOUND;
5197 brcmf_rxbound = DHD_RXBOUND;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005198 dhd_alignctl = true;
5199 sd1idle = true;
5200 dhd_readahead = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005201 retrydata = false;
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07005202 brcmf_dongle_memsize = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005203 dhd_txminmax = DHD_TXMINMAX;
5204
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005205 forcealign = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005206
Arend van Sprielaba5e0a2011-06-29 16:47:03 -07005207 brcmf_c_init();
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005208
5209 DHD_TRACE(("%s: Enter\n", __func__));
5210 DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __func__, venid, devid));
5211
5212 /* We make assumptions about address window mappings */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005213 ASSERT((unsigned long)regsva == SI_ENUM_BASE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005214
5215 /* BCMSDH passes venid and devid based on CIS parsing -- but
5216 * low-power start
5217 * means early parse could fail, so here we should get either an ID
5218 * we recognize OR (-1) indicating we must request power first.
5219 */
5220 /* Check the Vendor ID */
5221 switch (venid) {
5222 case 0x0000:
Stanislav Fomichevbe1c09f2011-03-28 01:31:36 +04005223 case PCI_VENDOR_ID_BROADCOM:
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005224 break;
5225 default:
5226 DHD_ERROR(("%s: unknown vendor: 0x%04x\n", __func__, venid));
5227 return NULL;
5228 }
5229
5230 /* Check the Device ID and make sure it's one that we support */
5231 switch (devid) {
5232 case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */
5233 case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */
5234 case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */
5235 DHD_INFO(("%s: found 4325 Dongle\n", __func__));
5236 break;
5237 case BCM4329_D11NDUAL_ID: /* 4329 802.11n dualband device */
5238 case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */
5239 case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */
5240 case 0x4329:
5241 DHD_INFO(("%s: found 4329 Dongle\n", __func__));
5242 break;
5243 case BCM4319_D11N_ID: /* 4319 802.11n id */
5244 case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */
5245 case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */
5246 DHD_INFO(("%s: found 4319 Dongle\n", __func__));
5247 break;
5248 case 0:
5249 DHD_INFO(("%s: allow device id 0, will check chip internals\n",
5250 __func__));
5251 break;
5252
5253 default:
5254 DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
5255 __func__, venid, devid));
5256 return NULL;
5257 }
5258
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005259 /* Allocate private bus interface state */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005260 bus = kzalloc(sizeof(dhd_bus_t), GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005261 if (!bus) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005262 DHD_ERROR(("%s: kmalloc of dhd_bus_t failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005263 goto fail;
5264 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005265 bus->sdh = sdh;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005266 bus->cl_devid = (u16) devid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005267 bus->bus = DHD_BUS;
5268 bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005269 bus->usebufpool = false; /* Use bufpool if allocated,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005270 else use locally malloced rxbuf */
5271
5272 /* attempt to attach to the dongle */
Roland Vossen54ca2962011-06-29 16:46:59 -07005273 if (!(brcmf_sdbrcm_probe_attach(bus, sdh, regsva, devid))) {
5274 DHD_ERROR(("%s: brcmf_sdbrcm_probe_attach failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005275 goto fail;
5276 }
5277
Franky Lin0f1a87c2011-06-29 16:46:32 -07005278 spin_lock_init(&bus->txqlock);
5279
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005280 /* Attach to the dhd/OS/network interface */
Arend van Spriela84bac42011-06-29 16:47:10 -07005281 bus->dhd = brcmf_attach(bus, SDPCM_RESERVE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005282 if (!bus->dhd) {
5283 DHD_ERROR(("%s: dhd_attach failed\n", __func__));
5284 goto fail;
5285 }
5286
5287 /* Allocate buffers */
Roland Vossen54ca2962011-06-29 16:46:59 -07005288 if (!(brcmf_sdbrcm_probe_malloc(bus, sdh))) {
5289 DHD_ERROR(("%s: brcmf_sdbrcm_probe_malloc failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005290 goto fail;
5291 }
5292
Roland Vossen54ca2962011-06-29 16:46:59 -07005293 if (!(brcmf_sdbrcm_probe_init(bus, sdh))) {
5294 DHD_ERROR(("%s: brcmf_sdbrcm_probe_init failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005295 goto fail;
5296 }
5297
5298 /* Register interrupt callback, but mask it (not operational yet). */
5299 DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n",
5300 __func__));
Roland Vossen54ca2962011-06-29 16:46:59 -07005301 brcmf_sdcard_intr_disable(sdh);
5302 ret = brcmf_sdcard_intr_reg(sdh, brcmf_sdbrcm_isr, bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005303 if (ret != 0) {
5304 DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
5305 __func__, ret));
5306 goto fail;
5307 }
5308 DHD_INTR(("%s: registered SDIO interrupt function ok\n", __func__));
5309
5310 DHD_INFO(("%s: completed!!\n", __func__));
5311
5312 /* if firmware path present try to download and bring up bus */
Arend van Spriela84bac42011-06-29 16:47:10 -07005313 ret = brcmf_bus_start(bus->dhd);
Jason Cooper9b890322010-09-30 15:15:39 -04005314 if (ret != 0) {
Roland Vossene10d82d2011-05-03 11:35:19 +02005315 if (ret == -ENOLINK) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005316 DHD_ERROR(("%s: dongle is not responding\n", __func__));
5317 goto fail;
5318 }
5319 }
5320 /* Ok, have the per-port tell the stack we're open for business */
Arend van Spriela84bac42011-06-29 16:47:10 -07005321 if (brcmf_net_attach(bus->dhd, 0) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005322 DHD_ERROR(("%s: Net attach failed!!\n", __func__));
5323 goto fail;
5324 }
5325
5326 return bus;
5327
5328fail:
Roland Vossen54ca2962011-06-29 16:46:59 -07005329 brcmf_sdbrcm_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005330 return NULL;
5331}
5332
5333static bool
Roland Vossen54ca2962011-06-29 16:46:59 -07005334brcmf_sdbrcm_probe_attach(struct dhd_bus *bus, void *sdh, void *regsva,
5335 u16 devid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005336{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005337 u8 clkctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005338 int err = 0;
5339
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005340 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005341
5342 /* Return the window to backplane enumeration space for core access */
Roland Vossen54ca2962011-06-29 16:46:59 -07005343 if (brcmf_sdbrcm_set_siaddr_window(bus, SI_ENUM_BASE))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005344 DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __func__));
5345
5346#ifdef DHD_DEBUG
Arend van Spriel0bef7742011-02-10 12:03:44 +01005347 printk(KERN_DEBUG "F1 signature read @0x18000000=0x%4x\n",
Roland Vossen54ca2962011-06-29 16:46:59 -07005348 brcmf_sdcard_reg_read(bus->sdh, SI_ENUM_BASE, 4));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005349
5350#endif /* DHD_DEBUG */
5351
Franky Linc05df632011-04-25 19:34:07 -07005352 /*
Roland Vossen54ca2962011-06-29 16:46:59 -07005353 * Force PLL off until brcmf_sdbrcm_chip_attach()
Franky Linc05df632011-04-25 19:34:07 -07005354 * programs PLL control regs
5355 */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005356
Roland Vossen54ca2962011-06-29 16:46:59 -07005357 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005358 DHD_INIT_CLKCTL1, &err);
5359 if (!err)
5360 clkctl =
Roland Vossen54ca2962011-06-29 16:46:59 -07005361 brcmf_sdcard_cfg_read(sdh, SDIO_FUNC_1,
5362 SBSDIO_FUNC1_CHIPCLKCSR, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005363
5364 if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) {
Roland Vossen54ca2962011-06-29 16:46:59 -07005365 DHD_ERROR(("brcmf_sdbrcm_probe: ChipClkCSR access: err %d wrote"
5366 " 0x%02x read 0x%02x\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005367 err, DHD_INIT_CLKCTL1, clkctl));
5368 goto fail;
5369 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005370
Roland Vossen54ca2962011-06-29 16:46:59 -07005371 if (brcmf_sdbrcm_chip_attach(bus, regsva)) {
5372 DHD_ERROR(("%s: brcmf_sdbrcm_chip_attach failed!\n", __func__));
Franky Lincb63e4c2011-04-25 15:45:08 -07005373 goto fail;
5374 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005375
Roland Vossen54ca2962011-06-29 16:46:59 -07005376 brcmf_sdcard_chipinfo(sdh, bus->ci->chip, bus->ci->chiprev);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005377
Roland Vossen54ca2962011-06-29 16:46:59 -07005378 if (!brcmf_sdbrcm_chipmatch((u16) bus->ci->chip)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005379 DHD_ERROR(("%s: unsupported chip: 0x%04x\n",
Franky Linc05df632011-04-25 19:34:07 -07005380 __func__, bus->ci->chip));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005381 goto fail;
5382 }
5383
Arend van Spriel5e92aa82011-06-29 16:47:08 -07005384 brcmf_sdbrcm_sdiod_drive_strength_init(bus, brcmf_sdiod_drive_strength);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005385
5386 /* Get info on the ARM and SOCRAM cores... */
5387 if (!DHD_NOPMU(bus)) {
Roland Vossen54ca2962011-06-29 16:46:59 -07005388 bus->armrev = SBCOREREV(brcmf_sdcard_reg_read(bus->sdh,
Franky Linc05df632011-04-25 19:34:07 -07005389 CORE_SB(bus->ci->armcorebase, sbidhigh), 4));
5390 bus->orig_ramsize = bus->ci->ramsize;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005391 if (!(bus->orig_ramsize)) {
5392 DHD_ERROR(("%s: failed to find SOCRAM memory!\n",
5393 __func__));
5394 goto fail;
5395 }
5396 bus->ramsize = bus->orig_ramsize;
Arend van Spriel7af9d0c2011-06-29 16:47:19 -07005397 if (brcmf_dongle_memsize)
5398 brcmf_sdbrcm_setmemsize(bus, brcmf_dongle_memsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005399
5400 DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
5401 bus->ramsize, bus->orig_ramsize));
5402 }
5403
Franky Linc05df632011-04-25 19:34:07 -07005404 bus->regs = (void *)bus->ci->buscorebase;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005405
5406 /* Set core control so an SDIO reset does a backplane reset */
Arend van Sprielff31c542011-03-01 10:56:54 +01005407 OR_REG(&bus->regs->corecontrol, CC_BPRESEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005408
Roland Vossen67ad48b2011-06-01 13:45:51 +02005409 brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005410
5411 /* Locate an appropriately-aligned portion of hdrbuf */
Arend van Spriel8d825a82011-06-29 16:47:25 -07005412 bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], BRCMF_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005413
5414 /* Set the poll and/or interrupt flags */
Arend van Spriel5e92aa82011-06-29 16:47:08 -07005415 bus->intr = (bool) brcmf_intr;
5416 bus->poll = (bool) brcmf_poll;
Jason Cooper9b890322010-09-30 15:15:39 -04005417 if (bus->poll)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005418 bus->pollrate = 1;
5419
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005420 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005421
5422fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005423 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005424}
5425
Roland Vossen54ca2962011-06-29 16:46:59 -07005426static bool brcmf_sdbrcm_probe_malloc(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005427{
5428 DHD_TRACE(("%s: Enter\n", __func__));
5429
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005430 if (bus->dhd->maxctl) {
5431 bus->rxblen =
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07005432 roundup((bus->dhd->maxctl + SDPCM_HDRLEN),
Arend van Spriel8d825a82011-06-29 16:47:25 -07005433 ALIGNMENT) + BRCMF_SDALIGN;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005434 bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005435 if (!(bus->rxbuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005436 DHD_ERROR(("%s: kmalloc of %d-byte rxbuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005437 __func__, bus->rxblen));
5438 goto fail;
5439 }
5440 }
5441
5442 /* Allocate buffer to receive glomed packet */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005443 bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005444 if (!(bus->databuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005445 DHD_ERROR(("%s: kmalloc of %d-byte databuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005446 __func__, MAX_DATA_BUF));
5447 /* release rxbuf which was already located as above */
5448 if (!bus->rxblen)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005449 kfree(bus->rxbuf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005450 goto fail;
5451 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005452
5453 /* Align the buffer */
Arend van Spriel8d825a82011-06-29 16:47:25 -07005454 if ((unsigned long)bus->databuf % BRCMF_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005455 bus->dataptr =
Arend van Spriel8d825a82011-06-29 16:47:25 -07005456 bus->databuf + (BRCMF_SDALIGN -
5457 ((unsigned long)bus->databuf % BRCMF_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005458 else
5459 bus->dataptr = bus->databuf;
5460
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005461 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005462
5463fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005464 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005465}
5466
Roland Vossen54ca2962011-06-29 16:46:59 -07005467static bool brcmf_sdbrcm_probe_init(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005468{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005469 s32 fnum;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005470
5471 DHD_TRACE(("%s: Enter\n", __func__));
5472
5473#ifdef SDTEST
Roland Vossen54ca2962011-06-29 16:46:59 -07005474 brcmf_sdbrcm_pktgen_init(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005475#endif /* SDTEST */
5476
5477 /* Disable F2 to clear any intermediate frame state on the dongle */
Roland Vossen54ca2962011-06-29 16:46:59 -07005478 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
5479 SDIO_FUNC_ENABLE_1, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005480
5481 bus->dhd->busstate = DHD_BUS_DOWN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005482 bus->sleeping = false;
5483 bus->rxflow = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005484 bus->prev_rxlim_hit = 0;
5485
5486 /* Done with backplane-dependent accesses, can drop clock... */
Roland Vossen54ca2962011-06-29 16:46:59 -07005487 brcmf_sdcard_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0,
5488 NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005489
5490 /* ...and initialize clock/power states */
5491 bus->clkstate = CLK_SDONLY;
Arend van Spriel5e92aa82011-06-29 16:47:08 -07005492 bus->idletime = (s32) brcmf_idletime;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005493 bus->idleclock = DHD_IDLE_ACTIVE;
5494
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005495 /* Query the F2 block size, set roundup accordingly */
5496 fnum = 2;
Roland Vossen54ca2962011-06-29 16:46:59 -07005497 if (brcmf_sdcard_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005498 &bus->blocksize, sizeof(s32), false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005499 bus->blocksize = 0;
5500 DHD_ERROR(("%s: fail on %s get\n", __func__, "sd_blocksize"));
5501 } else {
5502 DHD_INFO(("%s: Initial value for %s is %d\n",
5503 __func__, "sd_blocksize", bus->blocksize));
5504 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07005505 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005506
5507 /* Query if bus module supports packet chaining,
5508 default to use if supported */
Roland Vossen54ca2962011-06-29 16:46:59 -07005509 if (brcmf_sdcard_iovar_op(sdh, "sd_rxchain", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005510 &bus->sd_rxchain, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005511 false) != 0) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005512 bus->sd_rxchain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005513 } else {
5514 DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n",
5515 __func__,
5516 (bus->sd_rxchain ? "supports" : "does not support")));
5517 }
5518 bus->use_rxchain = (bool) bus->sd_rxchain;
5519
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005520 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005521}
5522
5523bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005524dhd_bus_download_firmware(struct dhd_bus *bus, char *fw_path, char *nv_path)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005525{
5526 bool ret;
5527 bus->fw_path = fw_path;
5528 bus->nv_path = nv_path;
5529
Roland Vossen54ca2962011-06-29 16:46:59 -07005530 ret = brcmf_sdbrcm_download_firmware(bus, bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005531
5532 return ret;
5533}
5534
5535static bool
Roland Vossen54ca2962011-06-29 16:46:59 -07005536brcmf_sdbrcm_download_firmware(struct dhd_bus *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005537{
5538 bool ret;
5539
5540 /* Download the firmware */
Roland Vossen54ca2962011-06-29 16:46:59 -07005541 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005542
Roland Vossen54ca2962011-06-29 16:46:59 -07005543 ret = _brcmf_sdbrcm_download_firmware(bus) == 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005544
Roland Vossen54ca2962011-06-29 16:46:59 -07005545 brcmf_sdbrcm_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005546
5547 return ret;
5548}
5549
5550/* Detach and free everything */
Roland Vossen54ca2962011-06-29 16:46:59 -07005551static void brcmf_sdbrcm_release(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005552{
5553 DHD_TRACE(("%s: Enter\n", __func__));
5554
5555 if (bus) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005556 /* De-register interrupt handler */
Roland Vossen54ca2962011-06-29 16:46:59 -07005557 brcmf_sdcard_intr_disable(bus->sdh);
5558 brcmf_sdcard_intr_dereg(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005559
5560 if (bus->dhd) {
Arend van Spriela84bac42011-06-29 16:47:10 -07005561 brcmf_detach(bus->dhd);
Roland Vossen54ca2962011-06-29 16:46:59 -07005562 brcmf_sdbrcm_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005563 bus->dhd = NULL;
5564 }
5565
Roland Vossen54ca2962011-06-29 16:46:59 -07005566 brcmf_sdbrcm_release_malloc(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005567
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005568 kfree(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005569 }
5570
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005571 DHD_TRACE(("%s: Disconnected\n", __func__));
5572}
5573
Roland Vossen54ca2962011-06-29 16:46:59 -07005574static void brcmf_sdbrcm_release_malloc(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005575{
5576 DHD_TRACE(("%s: Enter\n", __func__));
5577
5578 if (bus->dhd && bus->dhd->dongle_reset)
5579 return;
5580
Henry Ptasinski521223a2011-06-29 16:46:55 -07005581 kfree(bus->rxbuf);
5582 bus->rxctl = bus->rxbuf = NULL;
5583 bus->rxlen = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005584
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005585 kfree(bus->databuf);
5586 bus->databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005587}
5588
Roland Vossen54ca2962011-06-29 16:46:59 -07005589static void brcmf_sdbrcm_release_dongle(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005590{
5591 DHD_TRACE(("%s: Enter\n", __func__));
5592
5593 if (bus->dhd && bus->dhd->dongle_reset)
5594 return;
5595
Franky Linc05df632011-04-25 19:34:07 -07005596 if (bus->ci) {
Roland Vossen54ca2962011-06-29 16:46:59 -07005597 brcmf_sdbrcm_clkctl(bus, CLK_AVAIL, false);
5598 brcmf_sdbrcm_clkctl(bus, CLK_NONE, false);
5599 brcmf_sdbrcm_chip_detach(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005600 if (bus->vars && bus->varsz)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005601 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005602 bus->vars = NULL;
5603 }
5604
5605 DHD_TRACE(("%s: Disconnected\n", __func__));
5606}
5607
Roland Vossen54ca2962011-06-29 16:46:59 -07005608static void brcmf_sdbrcm_disconnect(void *ptr)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005609{
5610 dhd_bus_t *bus = (dhd_bus_t *)ptr;
5611
5612 DHD_TRACE(("%s: Enter\n", __func__));
5613
5614 if (bus) {
5615 ASSERT(bus->dhd);
Roland Vossen54ca2962011-06-29 16:46:59 -07005616 brcmf_sdbrcm_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005617 }
5618
5619 DHD_TRACE(("%s: Disconnected\n", __func__));
5620}
5621
5622/* Register/Unregister functions are called by the main DHD entry
5623 * point (e.g. module insertion) to link with the bus driver, in
5624 * order to look for or await the device.
5625 */
5626
Roland Vossen89650042011-06-29 16:47:22 -07005627static struct brcmf_sdioh_driver dhd_sdio = {
Roland Vossen54ca2962011-06-29 16:46:59 -07005628 brcmf_sdbrcm_probe,
5629 brcmf_sdbrcm_disconnect
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005630};
5631
5632int dhd_bus_register(void)
5633{
5634 DHD_TRACE(("%s: Enter\n", __func__));
5635
Roland Vossen54ca2962011-06-29 16:46:59 -07005636 return brcmf_sdio_register(&dhd_sdio);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005637}
5638
5639void dhd_bus_unregister(void)
5640{
5641 DHD_TRACE(("%s: Enter\n", __func__));
5642
Roland Vossen54ca2962011-06-29 16:46:59 -07005643 brcmf_sdio_unregister();
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005644}
5645
Roland Vossen54ca2962011-06-29 16:46:59 -07005646static int brcmf_sdbrcm_download_code_file(struct dhd_bus *bus, char *fw_path)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005647{
5648 int bcmerror = -1;
5649 int offset = 0;
5650 uint len;
5651 void *image = NULL;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005652 u8 *memblock = NULL, *memptr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005653
Arend van Sprielaba5e0a2011-06-29 16:47:03 -07005654 DHD_INFO(("%s: download firmware %s\n", __func__, brcmf_fw_path));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005655
Arend van Spriela84bac42011-06-29 16:47:10 -07005656 image = brcmf_os_open_image(fw_path);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005657 if (image == NULL)
5658 goto err;
5659
Arend van Spriel8d825a82011-06-29 16:47:25 -07005660 memptr = memblock = kmalloc(MEMBLOCK + BRCMF_SDALIGN, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005661 if (memblock == NULL) {
5662 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5663 __func__, MEMBLOCK));
5664 goto err;
5665 }
Arend van Spriel8d825a82011-06-29 16:47:25 -07005666 if ((u32)(unsigned long)memblock % BRCMF_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005667 memptr +=
Arend van Spriel8d825a82011-06-29 16:47:25 -07005668 (BRCMF_SDALIGN - ((u32)(unsigned long)memblock % BRCMF_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005669
5670 /* Download image */
5671 while ((len =
Arend van Spriela84bac42011-06-29 16:47:10 -07005672 brcmf_os_get_image_block((char *)memptr, MEMBLOCK, image))) {
Roland Vossen54ca2962011-06-29 16:46:59 -07005673 bcmerror = brcmf_sdbrcm_membytes(bus, true, offset, memptr,
5674 len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005675 if (bcmerror) {
5676 DHD_ERROR(("%s: error %d on writing %d membytes at "
5677 "0x%08x\n", __func__, bcmerror, MEMBLOCK, offset));
5678 goto err;
5679 }
5680
5681 offset += MEMBLOCK;
5682 }
5683
5684err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005685 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005686
5687 if (image)
Arend van Spriela84bac42011-06-29 16:47:10 -07005688 brcmf_os_close_image(image);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005689
5690 return bcmerror;
5691}
5692
5693/*
5694 * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
5695 * and ending in a NUL.
5696 * Removes carriage returns, empty lines, comment lines, and converts
5697 * newlines to NULs.
5698 * Shortens buffer as needed and pads with NULs. End of buffer is marked
5699 * by two NULs.
5700*/
5701
Roland Vossen54ca2962011-06-29 16:46:59 -07005702static uint brcmf_process_nvram_vars(char *varbuf, uint len)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005703{
5704 char *dp;
5705 bool findNewline;
5706 int column;
5707 uint buf_len, n;
5708
5709 dp = varbuf;
5710
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005711 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005712 column = 0;
5713
5714 for (n = 0; n < len; n++) {
5715 if (varbuf[n] == 0)
5716 break;
5717 if (varbuf[n] == '\r')
5718 continue;
5719 if (findNewline && varbuf[n] != '\n')
5720 continue;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005721 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005722 if (varbuf[n] == '#') {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005723 findNewline = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005724 continue;
5725 }
5726 if (varbuf[n] == '\n') {
5727 if (column == 0)
5728 continue;
5729 *dp++ = 0;
5730 column = 0;
5731 continue;
5732 }
5733 *dp++ = varbuf[n];
5734 column++;
5735 }
5736 buf_len = dp - varbuf;
5737
5738 while (dp < varbuf + n)
5739 *dp++ = 0;
5740
5741 return buf_len;
5742}
5743
5744/*
5745 EXAMPLE: nvram_array
5746 nvram_arry format:
5747 name=value
5748 Use carriage return at the end of each assignment,
5749 and an empty string with
5750 carriage return at the end of array.
5751
5752 For example:
5753 unsigned char nvram_array[] = {"name1=value1\n",
5754 "name2=value2\n", "\n"};
5755 Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx.
5756
5757 Search "EXAMPLE: nvram_array" to see how the array is activated.
5758*/
5759
5760void dhd_bus_set_nvram_params(struct dhd_bus *bus, const char *nvram_params)
5761{
5762 bus->nvram_params = nvram_params;
5763}
5764
Roland Vossen54ca2962011-06-29 16:46:59 -07005765static int brcmf_sdbrcm_download_nvram(struct dhd_bus *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005766{
5767 int bcmerror = -1;
5768 uint len;
5769 void *image = NULL;
5770 char *memblock = NULL;
5771 char *bufp;
5772 char *nv_path;
5773 bool nvram_file_exists;
5774
5775 nv_path = bus->nv_path;
5776
5777 nvram_file_exists = ((nv_path != NULL) && (nv_path[0] != '\0'));
5778 if (!nvram_file_exists && (bus->nvram_params == NULL))
5779 return 0;
5780
5781 if (nvram_file_exists) {
Arend van Spriela84bac42011-06-29 16:47:10 -07005782 image = brcmf_os_open_image(nv_path);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005783 if (image == NULL)
5784 goto err;
5785 }
5786
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005787 memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005788 if (memblock == NULL) {
5789 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5790 __func__, MEMBLOCK));
5791 goto err;
5792 }
5793
5794 /* Download variables */
5795 if (nvram_file_exists) {
Arend van Spriela84bac42011-06-29 16:47:10 -07005796 len = brcmf_os_get_image_block(memblock, MEMBLOCK, image);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005797 } else {
5798 len = strlen(bus->nvram_params);
5799 ASSERT(len <= MEMBLOCK);
5800 if (len > MEMBLOCK)
5801 len = MEMBLOCK;
5802 memcpy(memblock, bus->nvram_params, len);
5803 }
5804
5805 if (len > 0 && len < MEMBLOCK) {
5806 bufp = (char *)memblock;
5807 bufp[len] = 0;
Roland Vossen54ca2962011-06-29 16:46:59 -07005808 len = brcmf_process_nvram_vars(bufp, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005809 bufp += len;
5810 *bufp++ = 0;
5811 if (len)
Roland Vossen54ca2962011-06-29 16:46:59 -07005812 bcmerror = brcmf_sdbrcm_downloadvars(bus, memblock,
5813 len + 1);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005814 if (bcmerror) {
5815 DHD_ERROR(("%s: error downloading vars: %d\n",
5816 __func__, bcmerror));
5817 }
5818 } else {
5819 DHD_ERROR(("%s: error reading nvram file: %d\n",
5820 __func__, len));
Roland Vossenb74ac122011-05-03 11:35:20 +02005821 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005822 }
5823
5824err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005825 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005826
5827 if (image)
Arend van Spriela84bac42011-06-29 16:47:10 -07005828 brcmf_os_close_image(image);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005829
5830 return bcmerror;
5831}
5832
Roland Vossen54ca2962011-06-29 16:46:59 -07005833static int _brcmf_sdbrcm_download_firmware(struct dhd_bus *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005834{
5835 int bcmerror = -1;
5836
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005837 bool embed = false; /* download embedded firmware */
5838 bool dlok = false; /* download firmware succeeded */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005839
5840 /* Out immediately if no image to download */
Franky Lina4181fb2011-06-01 13:45:34 +02005841 if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0'))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005842 return bcmerror;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005843
5844 /* Keep arm in reset */
Roland Vossen54ca2962011-06-29 16:46:59 -07005845 if (brcmf_sdbrcm_download_state(bus, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005846 DHD_ERROR(("%s: error placing ARM core in reset\n", __func__));
5847 goto err;
5848 }
5849
5850 /* External image takes precedence if specified */
5851 if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) {
Roland Vossen54ca2962011-06-29 16:46:59 -07005852 if (brcmf_sdbrcm_download_code_file(bus, bus->fw_path)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005853 DHD_ERROR(("%s: dongle image file download failed\n",
5854 __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005855 goto err;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005856 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005857 embed = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005858 dlok = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005859 }
5860 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005861 if (!dlok) {
5862 DHD_ERROR(("%s: dongle image download failed\n", __func__));
5863 goto err;
5864 }
5865
5866 /* EXAMPLE: nvram_array */
5867 /* If a valid nvram_arry is specified as above, it can be passed
5868 down to dongle */
5869 /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */
5870
5871 /* External nvram takes precedence if specified */
Roland Vossen54ca2962011-06-29 16:46:59 -07005872 if (brcmf_sdbrcm_download_nvram(bus)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005873 DHD_ERROR(("%s: dongle nvram file download failed\n",
5874 __func__));
5875 }
5876
5877 /* Take arm out of reset */
Roland Vossen54ca2962011-06-29 16:46:59 -07005878 if (brcmf_sdbrcm_download_state(bus, false)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005879 DHD_ERROR(("%s: error getting out of ARM core reset\n",
5880 __func__));
5881 goto err;
5882 }
5883
5884 bcmerror = 0;
5885
5886err:
5887 return bcmerror;
5888}
5889
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005890
5891static int
Roland Vossen54ca2962011-06-29 16:46:59 -07005892brcmf_sdbrcm_send_buf(dhd_bus_t *bus, u32 addr, uint fn, uint flags,
Arend van Sprielc26b1372010-11-23 14:06:23 +01005893 u8 *buf, uint nbytes, struct sk_buff *pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005894 bcmsdh_cmplt_fn_t complete, void *handle)
5895{
Roland Vossen54ca2962011-06-29 16:46:59 -07005896 return brcmf_sdcard_send_buf
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005897 (bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete,
5898 handle);
5899}
5900
5901uint dhd_bus_chip(struct dhd_bus *bus)
5902{
Franky Linc05df632011-04-25 19:34:07 -07005903 ASSERT(bus->ci != NULL);
5904 return bus->ci->chip;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005905}
5906
5907void *dhd_bus_pub(struct dhd_bus *bus)
5908{
5909 return bus->dhd;
5910}
5911
5912void *dhd_bus_txq(struct dhd_bus *bus)
5913{
5914 return &bus->txq;
5915}
5916
5917uint dhd_bus_hdrlen(struct dhd_bus *bus)
5918{
5919 return SDPCM_HDRLEN;
5920}
5921
Arend van Spriela84bac42011-06-29 16:47:10 -07005922int brcmf_bus_devreset(dhd_pub_t *dhdp, u8 flag)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005923{
5924 int bcmerror = 0;
5925 dhd_bus_t *bus;
5926
5927 bus = dhdp->bus;
5928
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005929 if (flag == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005930 if (!bus->dhd->dongle_reset) {
5931 /* Expect app to have torn down any
5932 connection before calling */
5933 /* Stop the bus, disable F2 */
Roland Vossen54ca2962011-06-29 16:46:59 -07005934 brcmf_sdbrcm_bus_stop(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005935
5936 /* Clean tx/rx buffer pointers,
5937 detach from the dongle */
Roland Vossen54ca2962011-06-29 16:46:59 -07005938 brcmf_sdbrcm_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005939
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005940 bus->dhd->dongle_reset = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005941 bus->dhd->up = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005942
5943 DHD_TRACE(("%s: WLAN OFF DONE\n", __func__));
5944 /* App can now remove power from device */
5945 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02005946 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005947 } else {
5948 /* App must have restored power to device before calling */
5949
5950 DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __func__));
5951
5952 if (bus->dhd->dongle_reset) {
5953 /* Turn on WLAN */
5954 /* Reset SD client */
Roland Vossen54ca2962011-06-29 16:46:59 -07005955 brcmf_sdcard_reset(bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005956
5957 /* Attempt to re-attach & download */
Roland Vossen54ca2962011-06-29 16:46:59 -07005958 if (brcmf_sdbrcm_probe_attach(bus, bus->sdh,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005959 (u32 *) SI_ENUM_BASE,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005960 bus->cl_devid)) {
5961 /* Attempt to download binary to the dongle */
Roland Vossen54ca2962011-06-29 16:46:59 -07005962 if (brcmf_sdbrcm_probe_init
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005963 (bus, bus->sdh)
Roland Vossen54ca2962011-06-29 16:46:59 -07005964 && brcmf_sdbrcm_download_firmware(bus,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005965 bus->sdh)) {
5966
5967 /* Re-init bus, enable F2 transfer */
Roland Vossen54ca2962011-06-29 16:46:59 -07005968 brcmf_sdbrcm_bus_init(
5969 (dhd_pub_t *) bus->dhd, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005970
5971#if defined(OOB_INTR_ONLY)
Roland Vossen54ca2962011-06-29 16:46:59 -07005972 brcmf_sdbrcm_enable_oob_intr(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005973#endif /* defined(OOB_INTR_ONLY) */
5974
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005975 bus->dhd->dongle_reset = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005976 bus->dhd->up = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005977
5978 DHD_TRACE(("%s: WLAN ON DONE\n",
5979 __func__));
5980 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02005981 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005982 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02005983 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005984 } else {
Roland Vossenb74ac122011-05-03 11:35:20 +02005985 bcmerror = -EISCONN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005986 DHD_ERROR(("%s: Set DEVRESET=false invoked when device "
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005987 "is on\n", __func__));
Roland Vossenb74ac122011-05-03 11:35:20 +02005988 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005989 }
5990 }
5991 return bcmerror;
5992}
Franky Lincb63e4c2011-04-25 15:45:08 -07005993
5994static int
Roland Vossen677226a2011-06-29 16:47:21 -07005995brcmf_sdbrcm_chip_recognition(struct brcmf_sdio *sdh, struct chip_info *ci,
Roland Vossen54ca2962011-06-29 16:46:59 -07005996 void *regs)
Franky Lincb63e4c2011-04-25 15:45:08 -07005997{
5998 u32 regdata;
5999
6000 /*
6001 * Get CC core rev
6002 * Chipid is assume to be at offset 0 from regs arg
6003 * For different chiptypes or old sdio hosts w/o chipcommon,
6004 * other ways of recognition should be added here.
6005 */
6006 ci->cccorebase = (u32)regs;
Roland Vossen54ca2962011-06-29 16:46:59 -07006007 regdata = brcmf_sdcard_reg_read(sdh,
6008 CORE_CC_REG(ci->cccorebase, chipid), 4);
Franky Lincb63e4c2011-04-25 15:45:08 -07006009 ci->chip = regdata & CID_ID_MASK;
6010 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
6011
6012 DHD_INFO(("%s: chipid=0x%x chiprev=%d\n",
6013 __func__, ci->chip, ci->chiprev));
6014
6015 /* Address of cores for new chips should be added here */
6016 switch (ci->chip) {
6017 case BCM4329_CHIP_ID:
6018 ci->buscorebase = BCM4329_CORE_BUS_BASE;
6019 ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE;
6020 ci->armcorebase = BCM4329_CORE_ARM_BASE;
Franky Linc05df632011-04-25 19:34:07 -07006021 ci->ramsize = BCM4329_RAMSIZE;
Franky Lincb63e4c2011-04-25 15:45:08 -07006022 break;
6023 default:
6024 DHD_ERROR(("%s: chipid 0x%x is not supported\n",
6025 __func__, ci->chip));
6026 return -ENODEV;
6027 }
6028
Roland Vossen54ca2962011-06-29 16:46:59 -07006029 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006030 CORE_SB(ci->cccorebase, sbidhigh), 4);
6031 ci->ccrev = SBCOREREV(regdata);
6032
Roland Vossen54ca2962011-06-29 16:46:59 -07006033 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006034 CORE_CC_REG(ci->cccorebase, pmucapabilities), 4);
6035 ci->pmurev = regdata & PCAP_REV_MASK;
6036
Roland Vossen54ca2962011-06-29 16:46:59 -07006037 regdata = brcmf_sdcard_reg_read(sdh,
6038 CORE_SB(ci->buscorebase, sbidhigh), 4);
Franky Lincb63e4c2011-04-25 15:45:08 -07006039 ci->buscorerev = SBCOREREV(regdata);
6040 ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
6041
6042 DHD_INFO(("%s: ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
6043 __func__, ci->ccrev, ci->pmurev,
6044 ci->buscorerev, ci->buscoretype));
6045
6046 /* get chipcommon capabilites */
Roland Vossen54ca2962011-06-29 16:46:59 -07006047 ci->cccaps = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006048 CORE_CC_REG(ci->cccorebase, capabilities), 4);
6049
6050 return 0;
6051}
6052
6053static void
Roland Vossen677226a2011-06-29 16:47:21 -07006054brcmf_sdbrcm_chip_disablecore(struct brcmf_sdio *sdh, u32 corebase)
Franky Lincb63e4c2011-04-25 15:45:08 -07006055{
6056 u32 regdata;
6057
Roland Vossen54ca2962011-06-29 16:46:59 -07006058 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006059 CORE_SB(corebase, sbtmstatelow), 4);
6060 if (regdata & SBTML_RESET)
6061 return;
6062
Roland Vossen54ca2962011-06-29 16:46:59 -07006063 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006064 CORE_SB(corebase, sbtmstatelow), 4);
6065 if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) {
6066 /*
6067 * set target reject and spin until busy is clear
6068 * (preserve core-specific bits)
6069 */
Roland Vossen54ca2962011-06-29 16:46:59 -07006070 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006071 CORE_SB(corebase, sbtmstatelow), 4);
Roland Vossen54ca2962011-06-29 16:46:59 -07006072 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
Franky Lincb63e4c2011-04-25 15:45:08 -07006073 regdata | SBTML_REJ);
6074
Roland Vossen54ca2962011-06-29 16:46:59 -07006075 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006076 CORE_SB(corebase, sbtmstatelow), 4);
6077 udelay(1);
Roland Vossen54ca2962011-06-29 16:46:59 -07006078 SPINWAIT((brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006079 CORE_SB(corebase, sbtmstatehigh), 4) &
6080 SBTMH_BUSY), 100000);
6081
Roland Vossen54ca2962011-06-29 16:46:59 -07006082 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006083 CORE_SB(corebase, sbtmstatehigh), 4);
6084 if (regdata & SBTMH_BUSY)
6085 DHD_ERROR(("%s: ARM core still busy\n", __func__));
6086
Roland Vossen54ca2962011-06-29 16:46:59 -07006087 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006088 CORE_SB(corebase, sbidlow), 4);
6089 if (regdata & SBIDL_INIT) {
Roland Vossen54ca2962011-06-29 16:46:59 -07006090 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006091 CORE_SB(corebase, sbimstate), 4) |
6092 SBIM_RJ;
Roland Vossen54ca2962011-06-29 16:46:59 -07006093 brcmf_sdcard_reg_write(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006094 CORE_SB(corebase, sbimstate), 4,
6095 regdata);
Roland Vossen54ca2962011-06-29 16:46:59 -07006096 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006097 CORE_SB(corebase, sbimstate), 4);
6098 udelay(1);
Roland Vossen54ca2962011-06-29 16:46:59 -07006099 SPINWAIT((brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006100 CORE_SB(corebase, sbimstate), 4) &
6101 SBIM_BY), 100000);
6102 }
6103
6104 /* set reset and reject while enabling the clocks */
Roland Vossen54ca2962011-06-29 16:46:59 -07006105 brcmf_sdcard_reg_write(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006106 CORE_SB(corebase, sbtmstatelow), 4,
6107 (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
6108 SBTML_REJ | SBTML_RESET));
Roland Vossen54ca2962011-06-29 16:46:59 -07006109 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006110 CORE_SB(corebase, sbtmstatelow), 4);
6111 udelay(10);
6112
6113 /* clear the initiator reject bit */
Roland Vossen54ca2962011-06-29 16:46:59 -07006114 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006115 CORE_SB(corebase, sbidlow), 4);
6116 if (regdata & SBIDL_INIT) {
Roland Vossen54ca2962011-06-29 16:46:59 -07006117 regdata = brcmf_sdcard_reg_read(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006118 CORE_SB(corebase, sbimstate), 4) &
6119 ~SBIM_RJ;
Roland Vossen54ca2962011-06-29 16:46:59 -07006120 brcmf_sdcard_reg_write(sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006121 CORE_SB(corebase, sbimstate), 4,
6122 regdata);
6123 }
6124 }
6125
6126 /* leave reset and reject asserted */
Roland Vossen54ca2962011-06-29 16:46:59 -07006127 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
Franky Lincb63e4c2011-04-25 15:45:08 -07006128 (SBTML_REJ | SBTML_RESET));
6129 udelay(1);
6130}
6131
6132static int
Roland Vossen54ca2962011-06-29 16:46:59 -07006133brcmf_sdbrcm_chip_attach(struct dhd_bus *bus, void *regs)
Franky Lincb63e4c2011-04-25 15:45:08 -07006134{
6135 struct chip_info *ci;
6136 int err;
6137 u8 clkval, clkset;
6138
6139 DHD_TRACE(("%s: Enter\n", __func__));
6140
6141 /* alloc chip_info_t */
6142 ci = kmalloc(sizeof(struct chip_info), GFP_ATOMIC);
6143 if (NULL == ci) {
6144 DHD_ERROR(("%s: malloc failed!\n", __func__));
6145 return -ENOMEM;
6146 }
6147
6148 memset((unsigned char *)ci, 0, sizeof(struct chip_info));
6149
6150 /* bus/core/clk setup for register access */
6151 /* Try forcing SDIO core to do ALPAvail request only */
6152 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
Roland Vossen54ca2962011-06-29 16:46:59 -07006153 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
Franky Lincb63e4c2011-04-25 15:45:08 -07006154 clkset, &err);
6155 if (err) {
6156 DHD_ERROR(("%s: error writing for HT off\n", __func__));
6157 goto fail;
6158 }
6159
6160 /* If register supported, wait for ALPAvail and then force ALP */
6161 /* This may take up to 15 milliseconds */
Roland Vossen54ca2962011-06-29 16:46:59 -07006162 clkval = brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_1,
Franky Lincb63e4c2011-04-25 15:45:08 -07006163 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
6164 if ((clkval & ~SBSDIO_AVBITS) == clkset) {
6165 SPINWAIT(((clkval =
Roland Vossen54ca2962011-06-29 16:46:59 -07006166 brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_1,
Franky Lincb63e4c2011-04-25 15:45:08 -07006167 SBSDIO_FUNC1_CHIPCLKCSR,
6168 NULL)),
6169 !SBSDIO_ALPAV(clkval)),
6170 PMU_MAX_TRANSITION_DLY);
6171 if (!SBSDIO_ALPAV(clkval)) {
6172 DHD_ERROR(("%s: timeout on ALPAV wait, clkval 0x%02x\n",
6173 __func__, clkval));
6174 err = -EBUSY;
6175 goto fail;
6176 }
6177 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF |
6178 SBSDIO_FORCE_ALP;
Roland Vossen54ca2962011-06-29 16:46:59 -07006179 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1,
Franky Lincb63e4c2011-04-25 15:45:08 -07006180 SBSDIO_FUNC1_CHIPCLKCSR,
6181 clkset, &err);
6182 udelay(65);
6183 } else {
6184 DHD_ERROR(("%s: ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
6185 __func__, clkset, clkval));
6186 err = -EACCES;
6187 goto fail;
6188 }
6189
6190 /* Also, disable the extra SDIO pull-ups */
Roland Vossen54ca2962011-06-29 16:46:59 -07006191 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP,
6192 0, NULL);
Franky Lincb63e4c2011-04-25 15:45:08 -07006193
Roland Vossen54ca2962011-06-29 16:46:59 -07006194 err = brcmf_sdbrcm_chip_recognition(bus->sdh, ci, regs);
Franky Lincb63e4c2011-04-25 15:45:08 -07006195 if (err)
6196 goto fail;
6197
6198 /*
6199 * Make sure any on-chip ARM is off (in case strapping is wrong),
6200 * or downloaded code was already running.
6201 */
Roland Vossen54ca2962011-06-29 16:46:59 -07006202 brcmf_sdbrcm_chip_disablecore(bus->sdh, ci->armcorebase);
Franky Lincb63e4c2011-04-25 15:45:08 -07006203
Roland Vossen54ca2962011-06-29 16:46:59 -07006204 brcmf_sdcard_reg_write(bus->sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006205 CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0);
Roland Vossen54ca2962011-06-29 16:46:59 -07006206 brcmf_sdcard_reg_write(bus->sdh,
Franky Lincb63e4c2011-04-25 15:45:08 -07006207 CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0);
6208
6209 /* Disable F2 to clear any intermediate frame state on the dongle */
Roland Vossen54ca2962011-06-29 16:46:59 -07006210 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
Franky Lincb63e4c2011-04-25 15:45:08 -07006211 SDIO_FUNC_ENABLE_1, NULL);
6212
6213 /* WAR: cmd52 backplane read so core HW will drop ALPReq */
Roland Vossen54ca2962011-06-29 16:46:59 -07006214 clkval = brcmf_sdcard_cfg_read(bus->sdh, SDIO_FUNC_1,
Franky Lincb63e4c2011-04-25 15:45:08 -07006215 0, NULL);
6216
6217 /* Done with backplane-dependent accesses, can drop clock... */
Roland Vossen54ca2962011-06-29 16:46:59 -07006218 brcmf_sdcard_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
6219 0, NULL);
Franky Lincb63e4c2011-04-25 15:45:08 -07006220
6221 bus->ci = ci;
6222 return 0;
6223fail:
6224 bus->ci = NULL;
6225 kfree(ci);
6226 return err;
6227}
Franky Lineb5dc512011-04-25 19:34:04 -07006228
6229static void
Roland Vossen677226a2011-06-29 16:47:21 -07006230brcmf_sdbrcm_chip_resetcore(struct brcmf_sdio *sdh, u32 corebase)
Franky Lineb5dc512011-04-25 19:34:04 -07006231{
6232 u32 regdata;
6233
6234 /*
6235 * Must do the disable sequence first to work for
6236 * arbitrary current core state.
6237 */
Roland Vossen54ca2962011-06-29 16:46:59 -07006238 brcmf_sdbrcm_chip_disablecore(sdh, corebase);
Franky Lineb5dc512011-04-25 19:34:04 -07006239
6240 /*
6241 * Now do the initialization sequence.
6242 * set reset while enabling the clock and
6243 * forcing them on throughout the core
6244 */
Roland Vossen54ca2962011-06-29 16:46:59 -07006245 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
Franky Lineb5dc512011-04-25 19:34:04 -07006246 ((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
6247 SBTML_RESET);
6248 udelay(1);
6249
Roland Vossen54ca2962011-06-29 16:46:59 -07006250 regdata = brcmf_sdcard_reg_read(sdh, CORE_SB(corebase, sbtmstatehigh),
6251 4);
Franky Lineb5dc512011-04-25 19:34:04 -07006252 if (regdata & SBTMH_SERR)
Roland Vossen54ca2962011-06-29 16:46:59 -07006253 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatehigh),
6254 4, 0);
Franky Lineb5dc512011-04-25 19:34:04 -07006255
Roland Vossen54ca2962011-06-29 16:46:59 -07006256 regdata = brcmf_sdcard_reg_read(sdh, CORE_SB(corebase, sbimstate), 4);
Franky Lineb5dc512011-04-25 19:34:04 -07006257 if (regdata & (SBIM_IBE | SBIM_TO))
Roland Vossen54ca2962011-06-29 16:46:59 -07006258 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbimstate), 4,
Franky Lineb5dc512011-04-25 19:34:04 -07006259 regdata & ~(SBIM_IBE | SBIM_TO));
6260
6261 /* clear reset and allow it to propagate throughout the core */
Roland Vossen54ca2962011-06-29 16:46:59 -07006262 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
Franky Lineb5dc512011-04-25 19:34:04 -07006263 (SICF_FGC << SBTML_SICF_SHIFT) |
6264 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
6265 udelay(1);
6266
6267 /* leave clock enabled */
Roland Vossen54ca2962011-06-29 16:46:59 -07006268 brcmf_sdcard_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
Franky Lineb5dc512011-04-25 19:34:04 -07006269 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
6270 udelay(1);
6271}
Franky Lin5d0d7a92011-04-25 19:34:05 -07006272
6273/* SDIO Pad drive strength to select value mappings */
6274struct sdiod_drive_str {
6275 u8 strength; /* Pad Drive Strength in mA */
6276 u8 sel; /* Chip-specific select value */
6277};
6278
6279/* SDIO Drive Strength to sel value table for PMU Rev 1 */
6280static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = {
6281 {
6282 4, 0x2}, {
6283 2, 0x3}, {
6284 1, 0x0}, {
6285 0, 0x0}
6286 };
6287
6288/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
6289static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = {
6290 {
6291 12, 0x7}, {
6292 10, 0x6}, {
6293 8, 0x5}, {
6294 6, 0x4}, {
6295 4, 0x2}, {
6296 2, 0x1}, {
6297 0, 0x0}
6298 };
6299
6300/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */
6301static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = {
6302 {
6303 32, 0x7}, {
6304 26, 0x6}, {
6305 22, 0x5}, {
6306 16, 0x4}, {
6307 12, 0x3}, {
6308 8, 0x2}, {
6309 4, 0x1}, {
6310 0, 0x0}
6311 };
6312
6313#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
6314
6315static void
Roland Vossen54ca2962011-06-29 16:46:59 -07006316brcmf_sdbrcm_sdiod_drive_strength_init(struct dhd_bus *bus, u32 drivestrength) {
Franky Lin5d0d7a92011-04-25 19:34:05 -07006317 struct sdiod_drive_str *str_tab = NULL;
6318 u32 str_mask = 0;
6319 u32 str_shift = 0;
Franky Lin5d0d7a92011-04-25 19:34:05 -07006320 char chn[8];
Franky Lin5d0d7a92011-04-25 19:34:05 -07006321
6322 if (!(bus->ci->cccaps & CC_CAP_PMU))
6323 return;
6324
6325 switch (SDIOD_DRVSTR_KEY(bus->ci->chip, bus->ci->pmurev)) {
6326 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
6327 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1;
6328 str_mask = 0x30000000;
6329 str_shift = 28;
6330 break;
6331 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
6332 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
6333 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2;
6334 str_mask = 0x00003800;
6335 str_shift = 11;
6336 break;
6337 case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8):
6338 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3;
6339 str_mask = 0x00003800;
6340 str_shift = 11;
6341 break;
6342 default:
6343 DHD_ERROR(("No SDIO Drive strength init"
6344 "done for chip %s rev %d pmurev %d\n",
Roland Vossen67ad48b2011-06-01 13:45:51 +02006345 brcmu_chipname(bus->ci->chip, chn, 8),
Franky Lin5d0d7a92011-04-25 19:34:05 -07006346 bus->ci->chiprev, bus->ci->pmurev));
6347 break;
6348 }
6349
6350 if (str_tab != NULL) {
6351 u32 drivestrength_sel = 0;
6352 u32 cc_data_temp;
6353 int i;
6354
6355 for (i = 0; str_tab[i].strength != 0; i++) {
6356 if (drivestrength >= str_tab[i].strength) {
6357 drivestrength_sel = str_tab[i].sel;
6358 break;
6359 }
6360 }
6361
Roland Vossen54ca2962011-06-29 16:46:59 -07006362 brcmf_sdcard_reg_write(bus->sdh,
Franky Lin5d0d7a92011-04-25 19:34:05 -07006363 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
6364 4, 1);
Roland Vossen54ca2962011-06-29 16:46:59 -07006365 cc_data_temp = brcmf_sdcard_reg_read(bus->sdh,
Franky Lin5d0d7a92011-04-25 19:34:05 -07006366 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), 4);
6367 cc_data_temp &= ~str_mask;
6368 drivestrength_sel <<= str_shift;
6369 cc_data_temp |= drivestrength_sel;
Roland Vossen54ca2962011-06-29 16:46:59 -07006370 brcmf_sdcard_reg_write(bus->sdh,
Franky Lin5d0d7a92011-04-25 19:34:05 -07006371 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
6372 4, cc_data_temp);
6373
6374 DHD_INFO(("SDIO: %dmA drive strength selected, set to 0x%08x\n",
6375 drivestrength, cc_data_temp));
6376 }
6377}
Franky Lincee3cf42011-04-25 19:34:06 -07006378
6379static void
Roland Vossen54ca2962011-06-29 16:46:59 -07006380brcmf_sdbrcm_chip_detach(struct dhd_bus *bus)
Franky Lincee3cf42011-04-25 19:34:06 -07006381{
6382 DHD_TRACE(("%s: Enter\n", __func__));
6383
6384 kfree(bus->ci);
6385 bus->ci = NULL;
6386}