blob: acf2a55d1a0f1fe0b518f023717a5bf0ad6c0a5a [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>
Arend van Spriel34227312011-05-10 22:25:32 +020025#include <bcmdefs.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070026#include <bcmsdh.h>
27
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070028#include <bcmdefs.h>
Roland Vossenf97e9562011-06-01 13:45:52 +020029#include <brcmu_wifi.h>
30#include <brcmu_utils.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070031#include <bcmdevs.h>
32
Roland Vossen189aed02011-06-01 13:45:07 +020033#include <bcmsoc.h>
Roland Vossen44895182011-06-01 13:45:55 +020034
35/* register access macros */
36#if defined(BCMSDIO)
37#ifdef BRCM_FULLMAC
38#include <bcmsdh.h>
39#endif
40#endif
41
Roland Vossen44895182011-06-01 13:45:55 +020042/* register access macros */
43#ifndef __BIG_ENDIAN
44#ifndef __mips__
Roland Vossenb61a4be2011-06-01 13:45:56 +020045#define R_REG(r) \
46 bcmsdh_reg_read(NULL, (unsigned long)r, sizeof(*r))
Roland Vossen44895182011-06-01 13:45:55 +020047#else /* __mips__ */
Roland Vossenb61a4be2011-06-01 13:45:56 +020048#define R_REG(r) \
49 ({ \
50 __typeof(*(r)) __osl_v; \
51 __asm__ __volatile__("sync"); \
52 __osl_v = bcmsdh_reg_read(NULL, (unsigned long)r, sizeof(*r)); \
53 __asm__ __volatile__("sync"); \
54 __osl_v; \
55 })
Roland Vossen44895182011-06-01 13:45:55 +020056#endif /* __mips__ */
57
58#define W_REG(r, v) do { \
Roland Vossenb61a4be2011-06-01 13:45:56 +020059 bcmsdh_reg_write(NULL, (unsigned long)r, sizeof(*r), (v)); \
Roland Vossen44895182011-06-01 13:45:55 +020060 } while (0)
61#else /* __BIG_ENDIAN */
Roland Vossenb61a4be2011-06-01 13:45:56 +020062#define R_REG(r) \
63 bcmsdh_reg_read(NULL, (unsigned long)r, sizeof(*r))
Roland Vossen44895182011-06-01 13:45:55 +020064#define W_REG(r, v) do { \
Roland Vossenb61a4be2011-06-01 13:45:56 +020065 bcmsdh_reg_write(NULL, (unsigned long)r, sizeof(*r), v); \
Roland Vossen44895182011-06-01 13:45:55 +020066 } while (0)
67#endif /* __BIG_ENDIAN */
68
69#ifdef __mips__
70/*
71 * bcm4716 (which includes 4717 & 4718), plus 4706 on PCIe can reorder
72 * transactions. As a fix, a read after write is performed on certain places
73 * in the code. Older chips and the newer 5357 family don't require this fix.
74 */
75#define W_REG_FLUSH(r, v) ({ W_REG((r), (v)); (void)R_REG(r); })
76#else
77#define W_REG_FLUSH(r, v) W_REG((r), (v))
78#endif /* __mips__ */
79
80#define AND_REG(r, v) W_REG((r), R_REG(r) & (v))
81#define OR_REG(r, v) W_REG((r), R_REG(r) | (v))
82
83#define SET_REG(r, mask, val) \
84 W_REG((r), ((R_REG(r) & ~(mask)) | (val)))
85
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070086#ifdef DHD_DEBUG
Roland Vossenf1c7a082011-06-01 13:45:09 +020087
88/* ARM trap handling */
89
90/* Trap types defined by ARM (see arminc.h) */
91
92/* Trap locations in lo memory */
93#define TRAP_STRIDE 4
94#define FIRST_TRAP TR_RST
95#define LAST_TRAP (TR_FIQ * TRAP_STRIDE)
96
97#if defined(__ARM_ARCH_4T__)
98#define MAX_TRAP_TYPE (TR_FIQ + 1)
99#elif defined(__ARM_ARCH_7M__)
100#define MAX_TRAP_TYPE (TR_ISR + ARMCM3_NUMINTS)
101#endif /* __ARM_ARCH_7M__ */
102
103/* The trap structure is defined here as offsets for assembly */
104#define TR_TYPE 0x00
105#define TR_EPC 0x04
106#define TR_CPSR 0x08
107#define TR_SPSR 0x0c
108#define TR_REGS 0x10
109#define TR_REG(n) (TR_REGS + (n) * 4)
110#define TR_SP TR_REG(13)
111#define TR_LR TR_REG(14)
112#define TR_PC TR_REG(15)
113
114#define TRAP_T_SIZE 80
115
Roland Vossenf1c7a082011-06-01 13:45:09 +0200116typedef struct _trap_struct {
117 u32 type;
118 u32 epc;
119 u32 cpsr;
120 u32 spsr;
121 u32 r0;
122 u32 r1;
123 u32 r2;
124 u32 r3;
125 u32 r4;
126 u32 r5;
127 u32 r6;
128 u32 r7;
129 u32 r8;
130 u32 r9;
131 u32 r10;
132 u32 r11;
133 u32 r12;
134 u32 r13;
135 u32 r14;
136 u32 pc;
137} trap_t;
138
Roland Vossenf1c7a082011-06-01 13:45:09 +0200139#define CBUF_LEN (128)
140
141#define LOG_BUF_LEN 1024
142
143typedef struct {
144 u32 buf; /* Can't be pointer on (64-bit) hosts */
145 uint buf_size;
146 uint idx;
147 char *_buf_compat; /* Redundant pointer for backward compat. */
148} rte_log_t;
149
150typedef struct {
151 /* Virtual UART
152 * When there is no UART (e.g. Quickturn),
153 * the host should write a complete
154 * input line directly into cbuf and then write
155 * the length into vcons_in.
156 * This may also be used when there is a real UART
157 * (at risk of conflicting with
158 * the real UART). vcons_out is currently unused.
159 */
160 volatile uint vcons_in;
161 volatile uint vcons_out;
162
163 /* Output (logging) buffer
164 * Console output is written to a ring buffer log_buf at index log_idx.
165 * The host may read the output when it sees log_idx advance.
166 * Output will be lost if the output wraps around faster than the host
167 * polls.
168 */
169 rte_log_t log;
170
171 /* Console input line buffer
172 * Characters are read one at a time into cbuf
173 * until <CR> is received, then
174 * the buffer is processed as a command line.
175 * Also used for virtual UART.
176 */
177 uint cbuf_idx;
178 char cbuf[CBUF_LEN];
179} rte_cons_t;
180
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700181#endif /* DHD_DEBUG */
Roland Vossen745c9e62011-06-01 13:45:30 +0200182#include <chipcommon.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700183
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700184#include <sbsdio.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700185
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700186#include <dngl_stats.h>
187#include <dhd.h>
188#include <dhd_bus.h>
189#include <dhd_proto.h>
190#include <dhd_dbg.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700191#include <sdiovar.h>
Franky Lincb63e4c2011-04-25 15:45:08 -0700192#include <bcmchip.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700193
194#ifndef DHDSDIO_MEM_DUMP_FNAME
195#define DHDSDIO_MEM_DUMP_FNAME "mem_dump"
196#endif
197
Grant Grundler26a71a42011-03-09 10:41:25 -0800198#define TXQLEN 2048 /* bulk tx queue length */
199#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
200#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700201#define PRIOMASK 7
202
203#define TXRETRIES 2 /* # of retries for tx frames */
204
205#if defined(CONFIG_MACH_SANDGATE2G)
206#define DHD_RXBOUND 250 /* Default for max rx frames in
207 one scheduling */
208#else
209#define DHD_RXBOUND 50 /* Default for max rx frames in
210 one scheduling */
211#endif /* defined(CONFIG_MACH_SANDGATE2G) */
212
213#define DHD_TXBOUND 20 /* Default for max tx frames in
214 one scheduling */
215
216#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
217
218#define MEMBLOCK 2048 /* Block size used for downloading
219 of dongle image */
220#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
221 biggest possible glom */
222
223/* Packet alignment for most efficient SDIO (can change based on platform) */
224#ifndef DHD_SDALIGN
225#define DHD_SDALIGN 32
226#endif
227#if !ISPOWEROF2(DHD_SDALIGN)
228#error DHD_SDALIGN is not a power of 2!
229#endif
230
231#ifndef DHD_FIRSTREAD
232#define DHD_FIRSTREAD 32
233#endif
234#if !ISPOWEROF2(DHD_FIRSTREAD)
235#error DHD_FIRSTREAD is not a power of 2!
236#endif
237
238/* Total length of frame header for dongle protocol */
239#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
240#ifdef SDTEST
241#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
242#else
243#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN)
244#endif
245
Franky Linb49b14d2011-06-01 13:45:37 +0200246/*
247 * Software allocation of To SB Mailbox resources
248 */
249
250/* tosbmailbox bits corresponding to intstatus bits */
251#define SMB_NAK (1 << 0) /* Frame NAK */
252#define SMB_INT_ACK (1 << 1) /* Host Interrupt ACK */
253#define SMB_USE_OOB (1 << 2) /* Use OOB Wakeup */
254#define SMB_DEV_INT (1 << 3) /* Miscellaneous Interrupt */
255
256/* tosbmailboxdata */
257#define SMB_DATA_VERSION_SHIFT 16 /* host protocol version */
258
259/*
260 * Software allocation of To Host Mailbox resources
261 */
262
263/* intstatus bits */
264#define I_HMB_FC_STATE I_HMB_SW0 /* Flow Control State */
265#define I_HMB_FC_CHANGE I_HMB_SW1 /* Flow Control State Changed */
266#define I_HMB_FRAME_IND I_HMB_SW2 /* Frame Indication */
267#define I_HMB_HOST_INT I_HMB_SW3 /* Miscellaneous Interrupt */
268
269/* tohostmailboxdata */
270#define HMB_DATA_NAKHANDLED 1 /* retransmit NAK'd frame */
271#define HMB_DATA_DEVREADY 2 /* talk to host after enable */
272#define HMB_DATA_FC 4 /* per prio flowcontrol update flag */
273#define HMB_DATA_FWREADY 8 /* fw ready for protocol activity */
274
275#define HMB_DATA_FCDATA_MASK 0xff000000
276#define HMB_DATA_FCDATA_SHIFT 24
277
278#define HMB_DATA_VERSION_MASK 0x00ff0000
279#define HMB_DATA_VERSION_SHIFT 16
280
281/*
282 * Software-defined protocol header
283 */
284
285/* Current protocol version */
286#define SDPCM_PROT_VERSION 4
287
288/* SW frame header */
289#define SDPCM_PACKET_SEQUENCE(p) (((u8 *)p)[0] & 0xff)
290
291#define SDPCM_CHANNEL_MASK 0x00000f00
292#define SDPCM_CHANNEL_SHIFT 8
293#define SDPCM_PACKET_CHANNEL(p) (((u8 *)p)[1] & 0x0f)
294
295#define SDPCM_NEXTLEN_OFFSET 2
296
297/* Data Offset from SOF (HW Tag, SW Tag, Pad) */
298#define SDPCM_DOFFSET_OFFSET 3 /* Data Offset */
299#define SDPCM_DOFFSET_VALUE(p) (((u8 *)p)[SDPCM_DOFFSET_OFFSET] & 0xff)
300#define SDPCM_DOFFSET_MASK 0xff000000
301#define SDPCM_DOFFSET_SHIFT 24
302#define SDPCM_FCMASK_OFFSET 4 /* Flow control */
303#define SDPCM_FCMASK_VALUE(p) (((u8 *)p)[SDPCM_FCMASK_OFFSET] & 0xff)
304#define SDPCM_WINDOW_OFFSET 5 /* Credit based fc */
305#define SDPCM_WINDOW_VALUE(p) (((u8 *)p)[SDPCM_WINDOW_OFFSET] & 0xff)
306
307#define SDPCM_SWHEADER_LEN 8 /* SW header is 64 bits */
308
309/* logical channel numbers */
310#define SDPCM_CONTROL_CHANNEL 0 /* Control channel Id */
311#define SDPCM_EVENT_CHANNEL 1 /* Asyc Event Indication Channel Id */
312#define SDPCM_DATA_CHANNEL 2 /* Data Xmit/Recv Channel Id */
313#define SDPCM_GLOM_CHANNEL 3 /* For coalesced packets */
314#define SDPCM_TEST_CHANNEL 15 /* Reserved for test/debug packets */
315
316#define SDPCM_SEQUENCE_WRAP 256 /* wrap-around val for 8bit frame seq */
317
318#define SDPCM_GLOMDESC(p) (((u8 *)p)[1] & 0x80)
319
320/* For TEST_CHANNEL packets, define another 4-byte header */
321#define SDPCM_TEST_HDRLEN 4 /*
322 * Generally: Cmd(1), Ext(1), Len(2);
323 * Semantics of Ext byte depend on
324 * command. Len is current or requested
325 * frame length, not including test
326 * header; sent little-endian.
327 */
328#define SDPCM_TEST_DISCARD 0x01 /* Receiver discards. Ext:pattern id. */
329#define SDPCM_TEST_ECHOREQ 0x02 /* Echo request. Ext:pattern id. */
330#define SDPCM_TEST_ECHORSP 0x03 /* Echo response. Ext:pattern id. */
331#define SDPCM_TEST_BURST 0x04 /*
332 * Receiver to send a burst.
333 * Ext is a frame count
334 */
335#define SDPCM_TEST_SEND 0x05 /*
336 * Receiver sets send mode.
337 * Ext is boolean on/off
338 */
339
340/* Handy macro for filling in datagen packets with a pattern */
341#define SDPCM_TEST_FILL(byteno, id) ((u8)(id + byteno))
342
343/*
344 * Shared structure between dongle and the host.
345 * The structure contains pointers to trap or assert information.
346 */
347#define SDPCM_SHARED_VERSION 0x0002
348#define SDPCM_SHARED_VERSION_MASK 0x00FF
349#define SDPCM_SHARED_ASSERT_BUILT 0x0100
350#define SDPCM_SHARED_ASSERT 0x0200
351#define SDPCM_SHARED_TRAP 0x0400
352
353
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700354/* Space for header read, limit for data packets */
355#ifndef MAX_HDR_READ
356#define MAX_HDR_READ 32
357#endif
358#if !ISPOWEROF2(MAX_HDR_READ)
359#error MAX_HDR_READ is not a power of 2!
360#endif
361
362#define MAX_RX_DATASZ 2048
363
364/* Maximum milliseconds to wait for F2 to come up */
365#define DHD_WAIT_F2RDY 3000
366
367/* Bump up limit on waiting for HT to account for first startup;
368 * if the image is doing a CRC calculation before programming the PMU
369 * for HT availability, it could take a couple hundred ms more, so
370 * max out at a 1 second (1000000us).
371 */
372#if (PMU_MAX_TRANSITION_DLY <= 1000000)
373#undef PMU_MAX_TRANSITION_DLY
374#define PMU_MAX_TRANSITION_DLY 1000000
375#endif
376
377/* Value for ChipClockCSR during initial setup */
378#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
379 SBSDIO_ALP_AVAIL_REQ)
380#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
381
382/* Flags for SDH calls */
383#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
384
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200385/* sbimstate */
386#define SBIM_IBE 0x20000 /* inbanderror */
387#define SBIM_TO 0x40000 /* timeout */
388#define SBIM_BY 0x01800000 /* busy (sonics >= 2.3) */
389#define SBIM_RJ 0x02000000 /* reject (sonics >= 2.3) */
390
391/* sbtmstatelow */
392#define SBTML_RESET 0x0001 /* reset */
393#define SBTML_REJ_MASK 0x0006 /* reject field */
394#define SBTML_REJ 0x0002 /* reject */
395#define SBTML_TMPREJ 0x0004 /* temporary reject, for error recovery */
396
397#define SBTML_SICF_SHIFT 16 /* Shift to locate the SI control flags in sbtml */
398
399/* sbtmstatehigh */
400#define SBTMH_SERR 0x0001 /* serror */
401#define SBTMH_INT 0x0002 /* interrupt */
402#define SBTMH_BUSY 0x0004 /* busy */
403#define SBTMH_TO 0x0020 /* timeout (sonics >= 2.3) */
404
405#define SBTMH_SISF_SHIFT 16 /* Shift to locate the SI status flags in sbtmh */
406
407/* sbidlow */
408#define SBIDL_INIT 0x80 /* initiator */
409
410/* sbidhigh */
411#define SBIDH_RC_MASK 0x000f /* revision code */
412#define SBIDH_RCE_MASK 0x7000 /* revision code extension field */
413#define SBIDH_RCE_SHIFT 8
414#define SBCOREREV(sbidh) \
415 ((((sbidh) & SBIDH_RCE_MASK) >> SBIDH_RCE_SHIFT) | ((sbidh) & SBIDH_RC_MASK))
416#define SBIDH_CC_MASK 0x8ff0 /* core code */
417#define SBIDH_CC_SHIFT 4
418#define SBIDH_VC_MASK 0xffff0000 /* vendor code */
419#define SBIDH_VC_SHIFT 16
420
Arend van Spriel70dfb582011-02-25 16:39:17 +0100421/*
422 * Conversion of 802.1D priority to precedence level
423 */
424#define PRIO2PREC(prio) \
425 (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? \
426 ((prio^2)) : (prio))
427
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700428DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
429extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf,
430 uint len);
431
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200432/* Core reg address translation */
433#define CORE_CC_REG(base, field) (base + offsetof(chipcregs_t, field))
Franky Lin597600a2011-06-01 13:45:39 +0200434#define CORE_BUS_REG(base, field) \
435 (base + offsetof(struct sdpcmd_regs, field))
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200436#define CORE_SB(base, field) \
437 (base + SBCONFIGOFF + offsetof(sbconfig_t, field))
438
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700439#ifdef DHD_DEBUG
440/* Device console log buffer state */
441typedef struct dhd_console {
442 uint count; /* Poll interval msec counter */
443 uint log_addr; /* Log struct address (fixed) */
Roland Vossen70963f92011-06-01 13:45:08 +0200444 rte_log_t log; /* Log struct (host copy) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700445 uint bufsize; /* Size of log buffer */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700446 u8 *buf; /* Log buffer (host copy) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700447 uint last; /* Last buffer read index */
448} dhd_console_t;
449#endif /* DHD_DEBUG */
450
Franky Linb49b14d2011-06-01 13:45:37 +0200451struct sdpcm_shared {
452 u32 flags;
453 u32 trap_addr;
454 u32 assert_exp_addr;
455 u32 assert_file_addr;
456 u32 assert_line;
457 u32 console_addr; /* Address of rte_cons_t */
458 u32 msgtrace_addr;
459 u8 tag[32];
460};
461
462
Franky Lincb63e4c2011-04-25 15:45:08 -0700463/* misc chip info needed by some of the routines */
464struct chip_info {
465 u32 chip;
466 u32 chiprev;
467 u32 cccorebase;
468 u32 ccrev;
469 u32 cccaps;
470 u32 buscorebase;
471 u32 buscorerev;
472 u32 buscoretype;
473 u32 ramcorebase;
474 u32 armcorebase;
475 u32 pmurev;
Franky Linc05df632011-04-25 19:34:07 -0700476 u32 ramsize;
Franky Lincb63e4c2011-04-25 15:45:08 -0700477};
478
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700479/* Private data for SDIO bus interaction */
480typedef struct dhd_bus {
481 dhd_pub_t *dhd;
482
483 bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */
Franky Lincb63e4c2011-04-25 15:45:08 -0700484 struct chip_info *ci; /* Chip info struct */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700485 char *vars; /* Variables (from CIS and/or other) */
486 uint varsz; /* Size of variables buffer */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700487 u32 sbaddr; /* Current SB window pointer (-1, invalid) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700488
Franky Lin597600a2011-06-01 13:45:39 +0200489 struct sdpcmd_regs *regs; /* SDIO core */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700490 uint sdpcmrev; /* SDIO core revision */
491 uint armrev; /* CPU core revision */
492 uint ramrev; /* SOCRAM core revision */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700493 u32 ramsize; /* Size of RAM in SOCRAM (bytes) */
494 u32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700495
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700496 u32 bus; /* gSPI or SDIO bus */
497 u32 hostintmask; /* Copy of Host Interrupt Mask */
498 u32 intstatus; /* Intstatus bits (events) pending */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700499 bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
500 bool fcstate; /* State of dongle flow-control */
501
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700502 u16 cl_devid; /* cached devid for dhdsdio_probe_attach() */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700503 char *fw_path; /* module_param: path to firmware image */
504 char *nv_path; /* module_param: path to nvram vars file */
505 const char *nvram_params; /* user specified nvram params. */
506
507 uint blocksize; /* Block size of SDIO transfers */
508 uint roundup; /* Max roundup limit */
509
510 struct pktq txq; /* Queue length used for flow-control */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700511 u8 flowcontrol; /* per prio flow control bitmask */
512 u8 tx_seq; /* Transmit sequence number (next) */
513 u8 tx_max; /* Maximum transmit sequence allowed */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700514
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700515 u8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
516 u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700517 u16 nextlen; /* Next Read Len from last header */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700518 u8 rx_seq; /* Receive sequence number (expected) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700519 bool rxskip; /* Skip receive (awaiting NAK ACK) */
520
Arend van Sprielc26b1372010-11-23 14:06:23 +0100521 struct sk_buff *glomd; /* Packet containing glomming descriptor */
522 struct sk_buff *glom; /* Packet chain for glommed superframe */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700523 uint glomerr; /* Glom packet read errors */
524
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700525 u8 *rxbuf; /* Buffer for receiving control packets */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700526 uint rxblen; /* Allocated length of rxbuf */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700527 u8 *rxctl; /* Aligned pointer into rxbuf */
528 u8 *databuf; /* Buffer for receiving big glom packet */
529 u8 *dataptr; /* Aligned pointer into databuf */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700530 uint rxlen; /* Length of valid data in buffer */
531
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700532 u8 sdpcm_ver; /* Bus protocol reported by dongle */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700533
534 bool intr; /* Use interrupts */
535 bool poll; /* Use polling */
536 bool ipend; /* Device interrupt is pending */
537 bool intdis; /* Interrupts disabled by isr */
538 uint intrcount; /* Count of device interrupt callbacks */
539 uint lastintrs; /* Count as of last watchdog timer */
540 uint spurious; /* Count of spurious interrupts */
541 uint pollrate; /* Ticks between device polls */
542 uint polltick; /* Tick counter */
543 uint pollcnt; /* Count of active polls */
544
545#ifdef DHD_DEBUG
546 dhd_console_t console; /* Console output polling support */
547 uint console_addr; /* Console address from shared struct */
548#endif /* DHD_DEBUG */
549
550 uint regfails; /* Count of R_REG/W_REG failures */
551
552 uint clkstate; /* State of sd and backplane clock(s) */
553 bool activity; /* Activity flag for clock down */
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700554 s32 idletime; /* Control for activity timeout */
555 s32 idlecount; /* Activity timeout counter */
556 s32 idleclock; /* How to set bus driver when idle */
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700557 s32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700558 bool use_rxchain; /* If dhd should use PKT chains */
559 bool sleeping; /* Is SDIO bus sleeping? */
560 bool rxflow_mode; /* Rx flow control mode */
561 bool rxflow; /* Is rx flow control on */
562 uint prev_rxlim_hit; /* Is prev rx limit exceeded
563 (per dpc schedule) */
564 bool alp_only; /* Don't use HT clock (ALP only) */
565/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
566 bool usebufpool;
567
568#ifdef SDTEST
569 /* external loopback */
570 bool ext_loop;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700571 u8 loopid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700572
573 /* pktgen configuration */
574 uint pktgen_freq; /* Ticks between bursts */
575 uint pktgen_count; /* Packets to send each burst */
576 uint pktgen_print; /* Bursts between count displays */
577 uint pktgen_total; /* Stop after this many */
578 uint pktgen_minlen; /* Minimum packet data len */
579 uint pktgen_maxlen; /* Maximum packet data len */
580 uint pktgen_mode; /* Configured mode: tx, rx, or echo */
581 uint pktgen_stop; /* Number of tx failures causing stop */
582
583 /* active pktgen fields */
584 uint pktgen_tick; /* Tick counter for bursts */
585 uint pktgen_ptick; /* Burst counter for printing */
586 uint pktgen_sent; /* Number of test packets generated */
587 uint pktgen_rcvd; /* Number of test packets received */
588 uint pktgen_fail; /* Number of failed send attempts */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700589 u16 pktgen_len; /* Length of next packet to send */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700590#endif /* SDTEST */
591
592 /* Some additional counters */
593 uint tx_sderrs; /* Count of tx attempts with sd errors */
594 uint fcqueued; /* Tx packets that got queued */
595 uint rxrtx; /* Count of rtx requests (NAK to dongle) */
596 uint rx_toolong; /* Receive frames too long to receive */
597 uint rxc_errors; /* SDIO errors when reading control frames */
598 uint rx_hdrfail; /* SDIO errors on header reads */
599 uint rx_badhdr; /* Bad received headers (roosync?) */
600 uint rx_badseq; /* Mismatched rx sequence number */
601 uint fc_rcvd; /* Number of flow-control events received */
602 uint fc_xoff; /* Number which turned on flow-control */
603 uint fc_xon; /* Number which turned off flow-control */
604 uint rxglomfail; /* Failed deglom attempts */
605 uint rxglomframes; /* Number of glom frames (superframes) */
606 uint rxglompkts; /* Number of packets from glom frames */
607 uint f2rxhdrs; /* Number of header reads */
608 uint f2rxdata; /* Number of frame data reads */
609 uint f2txdata; /* Number of f2 frame writes */
610 uint f1regdata; /* Number of f1 register accesses */
611
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700612 u8 *ctrl_frame_buf;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700613 u32 ctrl_frame_len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700614 bool ctrl_frame_stat;
615} dhd_bus_t;
616
Roland Vossenb6fe70c2011-06-01 13:45:19 +0200617typedef volatile struct _sbconfig {
618 u32 PAD[2];
619 u32 sbipsflag; /* initiator port ocp slave flag */
620 u32 PAD[3];
621 u32 sbtpsflag; /* target port ocp slave flag */
622 u32 PAD[11];
623 u32 sbtmerrloga; /* (sonics >= 2.3) */
624 u32 PAD;
625 u32 sbtmerrlog; /* (sonics >= 2.3) */
626 u32 PAD[3];
627 u32 sbadmatch3; /* address match3 */
628 u32 PAD;
629 u32 sbadmatch2; /* address match2 */
630 u32 PAD;
631 u32 sbadmatch1; /* address match1 */
632 u32 PAD[7];
633 u32 sbimstate; /* initiator agent state */
634 u32 sbintvec; /* interrupt mask */
635 u32 sbtmstatelow; /* target state */
636 u32 sbtmstatehigh; /* target state */
637 u32 sbbwa0; /* bandwidth allocation table0 */
638 u32 PAD;
639 u32 sbimconfiglow; /* initiator configuration */
640 u32 sbimconfighigh; /* initiator configuration */
641 u32 sbadmatch0; /* address match0 */
642 u32 PAD;
643 u32 sbtmconfiglow; /* target configuration */
644 u32 sbtmconfighigh; /* target configuration */
645 u32 sbbconfig; /* broadcast configuration */
646 u32 PAD;
647 u32 sbbstate; /* broadcast state */
648 u32 PAD[3];
649 u32 sbactcnfg; /* activate configuration */
650 u32 PAD[3];
651 u32 sbflagst; /* current sbflags */
652 u32 PAD[3];
653 u32 sbidlow; /* identification */
654 u32 sbidhigh; /* identification */
655} sbconfig_t;
656
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700657/* clkstate */
658#define CLK_NONE 0
659#define CLK_SDONLY 1
660#define CLK_PENDING 2 /* Not used yet */
661#define CLK_AVAIL 3
662
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700663#define DHD_NOPMU(dhd) (false)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700664
665#ifdef DHD_DEBUG
666static int qcount[NUMPRIO];
667static int tx_packets[NUMPRIO];
668#endif /* DHD_DEBUG */
669
670/* Deferred transmit */
671const uint dhd_deferred_tx = 1;
672
673extern uint dhd_watchdog_ms;
674extern void dhd_os_wd_timer(void *bus, uint wdtick);
675
676/* Tx/Rx bounds */
677uint dhd_txbound;
678uint dhd_rxbound;
679uint dhd_txminmax;
680
681/* override the RAM size if possible */
682#define DONGLE_MIN_MEMSIZE (128 * 1024)
683int dhd_dongle_memsize;
684
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700685static bool dhd_alignctl;
686
687static bool sd1idle;
688
689static bool retrydata;
690#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
691
692static const uint watermark = 8;
693static const uint firstread = DHD_FIRSTREAD;
694
695#define HDATLEN (firstread - (SDPCM_HDRLEN))
696
697/* Retry count for register access failures */
698static const uint retry_limit = 2;
699
700/* Force even SD lengths (some host controllers mess up on odd bytes) */
701static bool forcealign;
702
703#define ALIGNMENT 4
704
705#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
706extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
707#endif
708
709#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
710#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
711#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100712#define PKTALIGN(_p, _len, _align) \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700713 do { \
714 uint datalign; \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100715 datalign = (unsigned long)((_p)->data); \
716 datalign = roundup(datalign, (_align)) - datalign; \
717 ASSERT(datalign < (_align)); \
718 ASSERT((_p)->len >= ((_len) + datalign)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700719 if (datalign) \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100720 skb_pull((_p), datalign); \
721 __skb_trim((_p), (_len)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700722 } while (0)
723
724/* Limit on rounding up frames */
725static const uint max_roundup = 512;
726
727/* Try doing readahead */
728static bool dhd_readahead;
729
730/* To check if there's window offered */
731#define DATAOK(bus) \
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700732 (((u8)(bus->tx_max - bus->tx_seq) != 0) && \
733 (((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700734
735/* Macros to get register read/write status */
736/* NOTE: these assume a local dhdsdio_bus_t *bus! */
737#define R_SDREG(regvar, regaddr, retryvar) \
738do { \
739 retryvar = 0; \
740 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100741 regvar = R_REG(regaddr); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700742 } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
743 if (retryvar) { \
744 bus->regfails += (retryvar-1); \
745 if (retryvar > retry_limit) { \
746 DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
747 __func__, __LINE__)); \
748 regvar = 0; \
749 } \
750 } \
751} while (0)
752
753#define W_SDREG(regval, regaddr, retryvar) \
754do { \
755 retryvar = 0; \
756 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100757 W_REG(regaddr, regval); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700758 } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
759 if (retryvar) { \
760 bus->regfails += (retryvar-1); \
761 if (retryvar > retry_limit) \
762 DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
763 __func__, __LINE__)); \
764 } \
765} while (0)
766
767#define DHD_BUS SDIO_BUS
768
769#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
770
771#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
772
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700773#ifdef SDTEST
774static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
775static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start);
776#endif
777
778#ifdef DHD_DEBUG
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700779static int dhdsdio_checkdied(dhd_bus_t *bus, u8 *data, uint size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700780static int dhdsdio_mem_dump(dhd_bus_t *bus);
781#endif /* DHD_DEBUG */
782static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
783
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100784static void dhdsdio_release(dhd_bus_t *bus);
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100785static void dhdsdio_release_malloc(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700786static void dhdsdio_disconnect(void *ptr);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700787static bool dhdsdio_chipmatch(u16 chipid);
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100788static bool dhdsdio_probe_attach(dhd_bus_t *bus, void *sdh,
789 void *regsva, u16 devid);
790static bool dhdsdio_probe_malloc(dhd_bus_t *bus, void *sdh);
791static bool dhdsdio_probe_init(dhd_bus_t *bus, void *sdh);
792static void dhdsdio_release_dongle(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700793
794static uint process_nvram_vars(char *varbuf, uint len);
795
796static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size);
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700797static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, u32 addr, uint fn,
Arend van Sprielc26b1372010-11-23 14:06:23 +0100798 uint flags, u8 *buf, uint nbytes,
799 struct sk_buff *pkt, bcmsdh_cmplt_fn_t complete,
800 void *handle);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700801
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100802static bool dhdsdio_download_firmware(struct dhd_bus *bus, void *sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700803static int _dhdsdio_download_firmware(struct dhd_bus *bus);
804
805static int dhdsdio_download_code_file(struct dhd_bus *bus, char *image_path);
806static int dhdsdio_download_nvram(struct dhd_bus *bus);
Franky Lineb5dc512011-04-25 19:34:04 -0700807static void dhdsdio_chip_disablecore(bcmsdh_info_t *sdh, u32 corebase);
Franky Lincb63e4c2011-04-25 15:45:08 -0700808static int dhdsdio_chip_attach(struct dhd_bus *bus, void *regs);
Franky Lineb5dc512011-04-25 19:34:04 -0700809static void dhdsdio_chip_resetcore(bcmsdh_info_t *sdh, u32 corebase);
Franky Lin5d0d7a92011-04-25 19:34:05 -0700810static void dhdsdio_sdiod_drive_strength_init(struct dhd_bus *bus,
811 u32 drivestrength);
Franky Lincee3cf42011-04-25 19:34:06 -0700812static void dhdsdio_chip_detach(struct dhd_bus *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700813
Grant Grundler4b455e02011-05-04 09:59:47 -0700814/* Packet free applicable unconditionally for sdio and sdspi.
815 * Conditional if bufpool was present for gspi bus.
816 */
817static void dhdsdio_pktfree2(dhd_bus_t *bus, struct sk_buff *pkt)
818{
819 dhd_os_sdlock_rxq(bus->dhd);
820 if ((bus->bus != SPI_BUS) || bus->usebufpool)
Roland Vossen67ad48b2011-06-01 13:45:51 +0200821 brcmu_pkt_buf_free_skb(pkt);
Grant Grundler4b455e02011-05-04 09:59:47 -0700822 dhd_os_sdunlock_rxq(bus->dhd);
823}
824
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700825static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size)
826{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700827 s32 min_size = DONGLE_MIN_MEMSIZE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700828 /* Restrict the memsize to user specified limit */
829 DHD_ERROR(("user: Restrict the dongle ram size to %d, min %d\n",
830 dhd_dongle_memsize, min_size));
831 if ((dhd_dongle_memsize > min_size) &&
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700832 (dhd_dongle_memsize < (s32) bus->orig_ramsize))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700833 bus->ramsize = dhd_dongle_memsize;
834}
835
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700836static int dhdsdio_set_siaddr_window(dhd_bus_t *bus, u32 address)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700837{
838 int err = 0;
839 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
840 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
841 if (!err)
842 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
843 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
844 if (!err)
845 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
846 (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
847 &err);
848 return err;
849}
850
851/* Turn backplane clock on or off */
852static int dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
853{
854 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700855 u8 clkctl, clkreq, devctl;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700856 bcmsdh_info_t *sdh;
857
858 DHD_TRACE(("%s: Enter\n", __func__));
859
860#if defined(OOB_INTR_ONLY)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700861 pendok = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700862#endif
863 clkctl = 0;
864 sdh = bus->sdh;
865
866 if (on) {
867 /* Request HT Avail */
868 clkreq =
869 bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
870
Franky Linc05df632011-04-25 19:34:07 -0700871 if ((bus->ci->chip == BCM4329_CHIP_ID)
872 && (bus->ci->chiprev == 0))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700873 clkreq |= SBSDIO_FORCE_ALP;
874
875 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
876 clkreq, &err);
877 if (err) {
878 DHD_ERROR(("%s: HT Avail request error: %d\n",
879 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200880 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700881 }
882
Franky Linc05df632011-04-25 19:34:07 -0700883 if (pendok && ((bus->ci->buscoretype == PCMCIA_CORE_ID)
884 && (bus->ci->buscorerev == 9))) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700885 u32 dummy, retries;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700886 R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
887 }
888
889 /* Check current status */
890 clkctl =
891 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
892 &err);
893 if (err) {
894 DHD_ERROR(("%s: HT Avail read error: %d\n",
895 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200896 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700897 }
898
899 /* Go to pending and await interrupt if appropriate */
900 if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
901 /* Allow only clock-available interrupt */
902 devctl =
903 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
904 &err);
905 if (err) {
906 DHD_ERROR(("%s: Devctl error setting CA: %d\n",
907 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200908 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700909 }
910
911 devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
912 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
913 devctl, &err);
914 DHD_INFO(("CLKCTL: set PENDING\n"));
915 bus->clkstate = CLK_PENDING;
916
Roland Vossena1c5ad82011-04-11 15:16:24 +0200917 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700918 } else if (bus->clkstate == CLK_PENDING) {
919 /* Cancel CA-only interrupt filter */
920 devctl =
921 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
922 &err);
923 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
924 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
925 devctl, &err);
926 }
927
928 /* Otherwise, wait here (polling) for HT Avail */
929 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
930 SPINWAIT_SLEEP(sdioh_spinwait_sleep,
931 ((clkctl =
932 bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
933 SBSDIO_FUNC1_CHIPCLKCSR,
934 &err)),
935 !SBSDIO_CLKAV(clkctl, bus->alp_only)),
936 PMU_MAX_TRANSITION_DLY);
937 }
938 if (err) {
939 DHD_ERROR(("%s: HT Avail request error: %d\n",
940 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200941 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700942 }
943 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
944 DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
945 __func__, PMU_MAX_TRANSITION_DLY, clkctl));
Roland Vossenb74ac122011-05-03 11:35:20 +0200946 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700947 }
948
949 /* Mark clock available */
950 bus->clkstate = CLK_AVAIL;
951 DHD_INFO(("CLKCTL: turned ON\n"));
952
953#if defined(DHD_DEBUG)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700954 if (bus->alp_only == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700955#if !defined(BCMLXSDMMC)
956 if (!SBSDIO_ALPONLY(clkctl)) {
957 DHD_ERROR(("%s: HT Clock, when ALP Only\n",
958 __func__));
959 }
960#endif /* !defined(BCMLXSDMMC) */
961 } else {
962 if (SBSDIO_ALPONLY(clkctl)) {
963 DHD_ERROR(("%s: HT Clock should be on.\n",
964 __func__));
965 }
966 }
967#endif /* defined (DHD_DEBUG) */
968
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700969 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700970 } else {
971 clkreq = 0;
972
973 if (bus->clkstate == CLK_PENDING) {
974 /* Cancel CA-only interrupt filter */
975 devctl =
976 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
977 &err);
978 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
979 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
980 devctl, &err);
981 }
982
983 bus->clkstate = CLK_SDONLY;
984 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
985 clkreq, &err);
986 DHD_INFO(("CLKCTL: turned OFF\n"));
987 if (err) {
988 DHD_ERROR(("%s: Failed access turning clock off: %d\n",
989 __func__, err));
Roland Vossenb74ac122011-05-03 11:35:20 +0200990 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700991 }
992 }
Roland Vossena1c5ad82011-04-11 15:16:24 +0200993 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700994}
995
996/* Change idle/active SD state */
997static int dhdsdio_sdclk(dhd_bus_t *bus, bool on)
998{
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700999 DHD_TRACE(("%s: Enter\n", __func__));
1000
Franky Lin602a8ab2011-06-01 13:45:04 +02001001 if (on)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001002 bus->clkstate = CLK_SDONLY;
Franky Lin602a8ab2011-06-01 13:45:04 +02001003 else
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001004 bus->clkstate = CLK_NONE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001005
Roland Vossena1c5ad82011-04-11 15:16:24 +02001006 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001007}
1008
1009/* Transition SD and backplane clock readiness */
1010static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
1011{
1012#ifdef DHD_DEBUG
1013 uint oldstate = bus->clkstate;
1014#endif /* DHD_DEBUG */
1015
1016 DHD_TRACE(("%s: Enter\n", __func__));
1017
1018 /* Early exit if we're already there */
1019 if (bus->clkstate == target) {
1020 if (target == CLK_AVAIL) {
1021 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001022 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001023 }
Roland Vossena1c5ad82011-04-11 15:16:24 +02001024 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001025 }
1026
1027 switch (target) {
1028 case CLK_AVAIL:
1029 /* Make sure SD clock is available */
1030 if (bus->clkstate == CLK_NONE)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001031 dhdsdio_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001032 /* Now request HT Avail on the backplane */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001033 dhdsdio_htclk(bus, true, pendok);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001034 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001035 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001036 break;
1037
1038 case CLK_SDONLY:
1039 /* Remove HT request, or bring up SD clock */
1040 if (bus->clkstate == CLK_NONE)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001041 dhdsdio_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001042 else if (bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001043 dhdsdio_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001044 else
1045 DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
1046 bus->clkstate, target));
1047 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
1048 break;
1049
1050 case CLK_NONE:
1051 /* Make sure to remove HT request */
1052 if (bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001053 dhdsdio_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001054 /* Now remove the SD clock */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001055 dhdsdio_sdclk(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001056 dhd_os_wd_timer(bus->dhd, 0);
1057 break;
1058 }
1059#ifdef DHD_DEBUG
1060 DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
1061#endif /* DHD_DEBUG */
1062
Roland Vossena1c5ad82011-04-11 15:16:24 +02001063 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001064}
1065
1066int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
1067{
1068 bcmsdh_info_t *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02001069 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001070 uint retries = 0;
1071
1072 DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n",
1073 (sleep ? "SLEEP" : "WAKE"),
1074 (bus->sleeping ? "SLEEP" : "WAKE")));
1075
1076 /* Done if we're already in the requested state */
1077 if (sleep == bus->sleeping)
Roland Vossena1c5ad82011-04-11 15:16:24 +02001078 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001079
1080 /* Going to sleep: set the alarm and turn off the lights... */
1081 if (sleep) {
1082 /* Don't sleep if something is pending */
1083 if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
Roland Vossene10d82d2011-05-03 11:35:19 +02001084 return -EBUSY;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001085
1086 /* Disable SDIO interrupts (no longer interested) */
1087 bcmsdh_intr_disable(bus->sdh);
1088
1089 /* Make sure the controller has the bus up */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001090 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001091
1092 /* Tell device to start using OOB wakeup */
1093 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
1094 if (retries > retry_limit)
1095 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
1096
1097 /* Turn off our contribution to the HT clock request */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001098 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001099
1100 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
1101 SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
1102
1103 /* Isolate the bus */
Franky Linc05df632011-04-25 19:34:07 -07001104 if (bus->ci->chip != BCM4329_CHIP_ID
1105 && bus->ci->chip != BCM4319_CHIP_ID) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001106 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
1107 SBSDIO_DEVCTL_PADS_ISO, NULL);
1108 }
1109
1110 /* Change state */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001111 bus->sleeping = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001112
1113 } else {
1114 /* Waking up: bus power up is ok, set local state */
1115
1116 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
1117 0, NULL);
1118
1119 /* Force pad isolation off if possible
1120 (in case power never toggled) */
Franky Linc05df632011-04-25 19:34:07 -07001121 if ((bus->ci->buscoretype == PCMCIA_CORE_ID)
1122 && (bus->ci->buscorerev >= 10))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001123 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0,
1124 NULL);
1125
1126 /* Make sure the controller has the bus up */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001127 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001128
1129 /* Send misc interrupt to indicate OOB not needed */
1130 W_SDREG(0, &regs->tosbmailboxdata, retries);
1131 if (retries <= retry_limit)
1132 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
1133
1134 if (retries > retry_limit)
1135 DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
1136
1137 /* Make sure we have SD bus access */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001138 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001139
1140 /* Change state */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001141 bus->sleeping = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001142
1143 /* Enable interrupts again */
1144 if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001145 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001146 bcmsdh_intr_enable(bus->sdh);
1147 }
1148 }
1149
Roland Vossena1c5ad82011-04-11 15:16:24 +02001150 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001151}
1152
1153#if defined(OOB_INTR_ONLY)
1154void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable)
1155{
1156#if defined(HW_OOB)
1157 bcmsdh_enable_hw_oob_intr(bus->sdh, enable);
1158#else
1159 sdpcmd_regs_t *regs = bus->regs;
1160 uint retries = 0;
1161
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001162 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001163 if (enable == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001164
1165 /* Tell device to start using OOB wakeup */
1166 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
1167 if (retries > retry_limit)
1168 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
1169
1170 } else {
1171 /* Send misc interrupt to indicate OOB not needed */
1172 W_SDREG(0, &regs->tosbmailboxdata, retries);
1173 if (retries <= retry_limit)
1174 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
1175 }
1176
1177 /* Turn off our contribution to the HT clock request */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001178 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001179#endif /* !defined(HW_OOB) */
1180}
1181#endif /* defined(OOB_INTR_ONLY) */
1182
1183#define BUS_WAKE(bus) \
1184 do { \
1185 if ((bus)->sleeping) \
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001186 dhdsdio_bussleep((bus), false); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001187 } while (0);
1188
1189/* Writes a HW/SW header into the packet and sends it. */
1190/* Assumes: (a) header space already there, (b) caller holds lock */
Arend van Sprielc26b1372010-11-23 14:06:23 +01001191static int dhdsdio_txpkt(dhd_bus_t *bus, struct sk_buff *pkt, uint chan,
1192 bool free_pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001193{
1194 int ret;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001195 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001196 u16 len, pad = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001197 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001198 uint retries = 0;
1199 bcmsdh_info_t *sdh;
Arend van Sprielc26b1372010-11-23 14:06:23 +01001200 struct sk_buff *new;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001201 int i;
1202
1203 DHD_TRACE(("%s: Enter\n", __func__));
1204
1205 sdh = bus->sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001206
1207 if (bus->dhd->dongle_reset) {
Roland Vossenb74ac122011-05-03 11:35:20 +02001208 ret = -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001209 goto done;
1210 }
1211
Arend van Spriel54991ad2010-11-23 14:06:24 +01001212 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001213
1214 /* Add alignment padding, allocate new packet if needed */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001215 pad = ((unsigned long)frame % DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001216 if (pad) {
Arend van Spriel3be727c2010-11-23 22:20:30 +01001217 if (skb_headroom(pkt) < pad) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001218 DHD_INFO(("%s: insufficient headroom %d for %d pad\n",
Arend van Spriel3be727c2010-11-23 22:20:30 +01001219 __func__, skb_headroom(pkt), pad));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001220 bus->dhd->tx_realloc++;
Roland Vossen67ad48b2011-06-01 13:45:51 +02001221 new = brcmu_pkt_buf_get_skb(pkt->len + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001222 if (!new) {
1223 DHD_ERROR(("%s: couldn't allocate new %d-byte "
1224 "packet\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01001225 __func__, pkt->len + DHD_SDALIGN));
Roland Vossene10d82d2011-05-03 11:35:19 +02001226 ret = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001227 goto done;
1228 }
1229
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01001230 PKTALIGN(new, pkt->len, DHD_SDALIGN);
Stanislav Fomichev02160692011-02-15 01:05:10 +03001231 memcpy(new->data, pkt->data, pkt->len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001232 if (free_pkt)
Roland Vossen67ad48b2011-06-01 13:45:51 +02001233 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001234 /* free the pkt if canned one is not used */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001235 free_pkt = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001236 pkt = new;
Arend van Spriel54991ad2010-11-23 14:06:24 +01001237 frame = (u8 *) (pkt->data);
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001238 ASSERT(((unsigned long)frame % DHD_SDALIGN) == 0);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001239 pad = 0;
1240 } else {
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001241 skb_push(pkt, pad);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001242 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001243
Arend van Spriel54991ad2010-11-23 14:06:24 +01001244 ASSERT((pad + SDPCM_HDRLEN) <= (int)(pkt->len));
Brett Rudley9249ede2010-11-30 20:09:49 -08001245 memset(frame, 0, pad + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001246 }
1247 }
1248 ASSERT(pad < DHD_SDALIGN);
1249
1250 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Arend van Spriel54991ad2010-11-23 14:06:24 +01001251 len = (u16) (pkt->len);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001252 *(u16 *) frame = cpu_to_le16(len);
1253 *(((u16 *) frame) + 1) = cpu_to_le16(~len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001254
1255 /* Software tag: channel, sequence number, data offset */
1256 swheader =
1257 ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
1258 (((pad +
1259 SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03001260
1261 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
1262 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001263
1264#ifdef DHD_DEBUG
Arend van Spriel54991ad2010-11-23 14:06:24 +01001265 tx_packets[pkt->priority]++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001266 if (DHD_BYTES_ON() &&
1267 (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
1268 (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
Arend van Spriel34227312011-05-10 22:25:32 +02001269 printk(KERN_DEBUG "Tx Frame:\n");
1270 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, frame, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001271 } else if (DHD_HDRS_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02001272 printk(KERN_DEBUG "TxHdr:\n");
1273 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1274 frame, min_t(u16, len, 16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001275 }
1276#endif
1277
1278 /* Raise len to next SDIO block to eliminate tail command */
1279 if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001280 u16 pad = bus->blocksize - (len % bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001281 if ((pad <= bus->roundup) && (pad < bus->blocksize))
1282#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001283 if (pad <= skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001284#endif /* NOTUSED */
1285 len += pad;
1286 } else if (len % DHD_SDALIGN) {
1287 len += DHD_SDALIGN - (len % DHD_SDALIGN);
1288 }
1289
1290 /* Some controllers have trouble with odd bytes -- round to even */
1291 if (forcealign && (len & (ALIGNMENT - 1))) {
1292#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001293 if (skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001294#endif
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001295 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001296#ifdef NOTUSED
1297 else
1298 DHD_ERROR(("%s: sending unrounded %d-byte packet\n",
1299 __func__, len));
1300#endif
1301 }
1302
1303 do {
1304 ret =
1305 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
1306 F2SYNC, frame, len, pkt, NULL, NULL);
1307 bus->f2txdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02001308 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001309
1310 if (ret < 0) {
1311 /* On failure, abort the command
1312 and terminate the frame */
1313 DHD_INFO(("%s: sdio error %d, abort command and "
1314 "terminate frame.\n", __func__, ret));
1315 bus->tx_sderrs++;
1316
1317 bcmsdh_abort(sdh, SDIO_FUNC_2);
1318 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
1319 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
1320 NULL);
1321 bus->f1regdata++;
1322
1323 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001324 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001325 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1326 SBSDIO_FUNC1_WFRAMEBCHI,
1327 NULL);
1328 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1329 SBSDIO_FUNC1_WFRAMEBCLO,
1330 NULL);
1331 bus->f1regdata += 2;
1332 if ((hi == 0) && (lo == 0))
1333 break;
1334 }
1335
1336 }
1337 if (ret == 0)
1338 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1339
1340 } while ((ret < 0) && retrydata && retries++ < TXRETRIES);
1341
1342done:
1343 /* restore pkt buffer pointer before calling tx complete routine */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001344 skb_pull(pkt, SDPCM_HDRLEN + pad);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001345 dhd_os_sdunlock(bus->dhd);
1346 dhd_txcomplete(bus->dhd, pkt, ret != 0);
1347 dhd_os_sdlock(bus->dhd);
1348
1349 if (free_pkt)
Roland Vossen67ad48b2011-06-01 13:45:51 +02001350 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001351
1352 return ret;
1353}
1354
Arend van Sprielc26b1372010-11-23 14:06:23 +01001355int dhd_bus_txdata(struct dhd_bus *bus, struct sk_buff *pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001356{
Roland Vossenb74ac122011-05-03 11:35:20 +02001357 int ret = -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001358 uint datalen, prec;
1359
1360 DHD_TRACE(("%s: Enter\n", __func__));
1361
Arend van Spriel54991ad2010-11-23 14:06:24 +01001362 datalen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001363
1364#ifdef SDTEST
1365 /* Push the test header if doing loopback */
1366 if (bus->ext_loop) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001367 u8 *data;
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001368 skb_push(pkt, SDPCM_TEST_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001369 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001370 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001371 *data++ = (u8) bus->loopid++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001372 *data++ = (datalen >> 0);
1373 *data++ = (datalen >> 8);
1374 datalen += SDPCM_TEST_HDRLEN;
1375 }
1376#endif /* SDTEST */
1377
1378 /* Add space for the header */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001379 skb_push(pkt, SDPCM_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001380 ASSERT(IS_ALIGNED((unsigned long)(pkt->data), 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001381
Arend van Spriel54991ad2010-11-23 14:06:24 +01001382 prec = PRIO2PREC((pkt->priority & PRIOMASK));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001383
1384 /* Check for existing queue, current flow-control,
1385 pending event, or pending clock */
1386 if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq)
1387 || bus->dpc_sched || (!DATAOK(bus))
1388 || (bus->flowcontrol & NBITVAL(prec))
1389 || (bus->clkstate != CLK_AVAIL)) {
1390 DHD_TRACE(("%s: deferring pktq len %d\n", __func__,
1391 pktq_len(&bus->txq)));
1392 bus->fcqueued++;
1393
1394 /* Priority based enq */
1395 dhd_os_sdlock_txq(bus->dhd);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001396 if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == false) {
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001397 skb_pull(pkt, SDPCM_HDRLEN);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001398 dhd_txcomplete(bus->dhd, pkt, false);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001399 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001400 DHD_ERROR(("%s: out of bus->txq !!!\n", __func__));
Roland Vossene10d82d2011-05-03 11:35:19 +02001401 ret = -ENOSR;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001402 } else {
Roland Vossena1c5ad82011-04-11 15:16:24 +02001403 ret = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001404 }
1405 dhd_os_sdunlock_txq(bus->dhd);
1406
Grant Grundler7c316072011-03-09 15:04:15 -08001407 if (pktq_len(&bus->txq) >= TXHI)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001408 dhd_txflowcontrol(bus->dhd, 0, ON);
1409
1410#ifdef DHD_DEBUG
1411 if (pktq_plen(&bus->txq, prec) > qcount[prec])
1412 qcount[prec] = pktq_plen(&bus->txq, prec);
1413#endif
1414 /* Schedule DPC if needed to send queued packet(s) */
1415 if (dhd_deferred_tx && !bus->dpc_sched) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001416 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001417 dhd_sched_dpc(bus->dhd);
1418 }
1419 } else {
1420 /* Lock: we're about to use shared data/code (and SDIO) */
1421 dhd_os_sdlock(bus->dhd);
1422
1423 /* Otherwise, send it now */
1424 BUS_WAKE(bus);
1425 /* Make sure back plane ht clk is on, no pending allowed */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001426 dhdsdio_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001427
1428#ifndef SDTEST
1429 DHD_TRACE(("%s: calling txpkt\n", __func__));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001430 ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001431#else
1432 ret = dhdsdio_txpkt(bus, pkt,
1433 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001434 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001435#endif
1436 if (ret)
1437 bus->dhd->tx_errors++;
1438 else
1439 bus->dhd->dstats.tx_bytes += datalen;
1440
1441 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001442 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001443 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001444 }
1445
1446 dhd_os_sdunlock(bus->dhd);
1447 }
1448
1449 return ret;
1450}
1451
1452static uint dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
1453{
Arend van Sprielc26b1372010-11-23 14:06:23 +01001454 struct sk_buff *pkt;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001455 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001456 uint retries = 0;
1457 int ret = 0, prec_out;
1458 uint cnt = 0;
1459 uint datalen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001460 u8 tx_prec_map;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001461
1462 dhd_pub_t *dhd = bus->dhd;
Franky Lin597600a2011-06-01 13:45:39 +02001463 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001464
1465 DHD_TRACE(("%s: Enter\n", __func__));
1466
1467 tx_prec_map = ~bus->flowcontrol;
1468
1469 /* Send frames until the limit or some other event */
1470 for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
1471 dhd_os_sdlock_txq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001472 pkt = brcmu_pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001473 if (pkt == NULL) {
1474 dhd_os_sdunlock_txq(bus->dhd);
1475 break;
1476 }
1477 dhd_os_sdunlock_txq(bus->dhd);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001478 datalen = pkt->len - SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001479
1480#ifndef SDTEST
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001481 ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001482#else
1483 ret = dhdsdio_txpkt(bus, pkt,
1484 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001485 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001486#endif
1487 if (ret)
1488 bus->dhd->tx_errors++;
1489 else
1490 bus->dhd->dstats.tx_bytes += datalen;
1491
1492 /* In poll mode, need to check for other events */
1493 if (!bus->intr && cnt) {
1494 /* Check device status, signal pending interrupt */
1495 R_SDREG(intstatus, &regs->intstatus, retries);
1496 bus->f2txdata++;
1497 if (bcmsdh_regfail(bus->sdh))
1498 break;
1499 if (intstatus & bus->hostintmask)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001500 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001501 }
1502 }
1503
1504 /* Deflow-control stack if needed */
Grant Grundler7c316072011-03-09 15:04:15 -08001505 if (dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
Grant Grundler26a71a42011-03-09 10:41:25 -08001506 dhd->txoff && (pktq_len(&bus->txq) < TXLOW))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001507 dhd_txflowcontrol(dhd, 0, OFF);
1508
1509 return cnt;
1510}
1511
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07001512int dhd_bus_txctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001513{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001514 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001515 u16 len;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001516 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001517 uint retries = 0;
1518 bcmsdh_info_t *sdh = bus->sdh;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001519 u8 doff = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001520 int ret = -1;
1521 int i;
1522
1523 DHD_TRACE(("%s: Enter\n", __func__));
1524
1525 if (bus->dhd->dongle_reset)
1526 return -EIO;
1527
1528 /* Back the pointer to make a room for bus header */
1529 frame = msg - SDPCM_HDRLEN;
1530 len = (msglen += SDPCM_HDRLEN);
1531
1532 /* Add alignment padding (optional for ctl frames) */
1533 if (dhd_alignctl) {
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001534 doff = ((unsigned long)frame % DHD_SDALIGN);
Jason Cooper9b890322010-09-30 15:15:39 -04001535 if (doff) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001536 frame -= doff;
1537 len += doff;
1538 msglen += doff;
Brett Rudley9249ede2010-11-30 20:09:49 -08001539 memset(frame, 0, doff + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001540 }
1541 ASSERT(doff < DHD_SDALIGN);
1542 }
1543 doff += SDPCM_HDRLEN;
1544
1545 /* Round send length to next SDIO block */
1546 if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001547 u16 pad = bus->blocksize - (len % bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001548 if ((pad <= bus->roundup) && (pad < bus->blocksize))
1549 len += pad;
1550 } else if (len % DHD_SDALIGN) {
1551 len += DHD_SDALIGN - (len % DHD_SDALIGN);
1552 }
1553
1554 /* Satisfy length-alignment requirements */
1555 if (forcealign && (len & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001556 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001557
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001558 ASSERT(IS_ALIGNED((unsigned long)frame, 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001559
1560 /* Need to lock here to protect txseq and SDIO tx calls */
1561 dhd_os_sdlock(bus->dhd);
1562
1563 BUS_WAKE(bus);
1564
1565 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001566 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001567
1568 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001569 *(u16 *) frame = cpu_to_le16((u16) msglen);
1570 *(((u16 *) frame) + 1) = cpu_to_le16(~msglen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001571
1572 /* Software tag: channel, sequence number, data offset */
1573 swheader =
1574 ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
1575 SDPCM_CHANNEL_MASK)
1576 | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
1577 SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03001578 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
1579 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001580
1581 if (!DATAOK(bus)) {
1582 DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
1583 __func__, bus->tx_max, bus->tx_seq));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001584 bus->ctrl_frame_stat = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001585 /* Send from dpc */
1586 bus->ctrl_frame_buf = frame;
1587 bus->ctrl_frame_len = len;
1588
1589 dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
1590
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001591 if (bus->ctrl_frame_stat == false) {
1592 DHD_INFO(("%s: ctrl_frame_stat == false\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001593 ret = 0;
1594 } else {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001595 DHD_INFO(("%s: ctrl_frame_stat == true\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001596 ret = -1;
1597 }
1598 }
1599
1600 if (ret == -1) {
1601#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02001602 if (DHD_BYTES_ON() && DHD_CTL_ON()) {
1603 printk(KERN_DEBUG "Tx Frame:\n");
1604 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1605 frame, len);
1606 } else if (DHD_HDRS_ON()) {
1607 printk(KERN_DEBUG "TxHdr:\n");
1608 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
1609 frame, min_t(u16, len, 16));
1610 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001611#endif
1612
1613 do {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001614 bus->ctrl_frame_stat = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001615 ret =
1616 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh),
1617 SDIO_FUNC_2, F2SYNC, frame, len,
1618 NULL, NULL, NULL);
1619
Roland Vossenb7ef2a92011-05-03 11:35:02 +02001620 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001621
1622 if (ret < 0) {
1623 /* On failure, abort the command and
1624 terminate the frame */
1625 DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
1626 __func__, ret));
1627 bus->tx_sderrs++;
1628
1629 bcmsdh_abort(sdh, SDIO_FUNC_2);
1630
1631 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
1632 SBSDIO_FUNC1_FRAMECTRL,
1633 SFC_WF_TERM, NULL);
1634 bus->f1regdata++;
1635
1636 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001637 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001638 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1639 SBSDIO_FUNC1_WFRAMEBCHI,
1640 NULL);
1641 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1642 SBSDIO_FUNC1_WFRAMEBCLO,
1643 NULL);
1644 bus->f1regdata += 2;
1645 if ((hi == 0) && (lo == 0))
1646 break;
1647 }
1648
1649 }
1650 if (ret == 0) {
1651 bus->tx_seq =
1652 (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1653 }
1654 } while ((ret < 0) && retries++ < TXRETRIES);
1655 }
1656
1657 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001658 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001659 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001660 }
1661
1662 dhd_os_sdunlock(bus->dhd);
1663
1664 if (ret)
1665 bus->dhd->tx_ctlerrs++;
1666 else
1667 bus->dhd->tx_ctlpkts++;
1668
1669 return ret ? -EIO : 0;
1670}
1671
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07001672int dhd_bus_rxctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001673{
1674 int timeleft;
1675 uint rxlen = 0;
1676 bool pending;
1677
1678 DHD_TRACE(("%s: Enter\n", __func__));
1679
1680 if (bus->dhd->dongle_reset)
1681 return -EIO;
1682
1683 /* Wait until control frame is available */
1684 timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
1685
1686 dhd_os_sdlock(bus->dhd);
1687 rxlen = bus->rxlen;
Stanislav Fomichev02160692011-02-15 01:05:10 +03001688 memcpy(msg, bus->rxctl, min(msglen, rxlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001689 bus->rxlen = 0;
1690 dhd_os_sdunlock(bus->dhd);
1691
1692 if (rxlen) {
1693 DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
1694 __func__, rxlen, msglen));
1695 } else if (timeleft == 0) {
1696 DHD_ERROR(("%s: resumed on timeout\n", __func__));
1697#ifdef DHD_DEBUG
1698 dhd_os_sdlock(bus->dhd);
1699 dhdsdio_checkdied(bus, NULL, 0);
1700 dhd_os_sdunlock(bus->dhd);
1701#endif /* DHD_DEBUG */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001702 } else if (pending == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001703 DHD_CTL(("%s: cancelled\n", __func__));
1704 return -ERESTARTSYS;
1705 } else {
1706 DHD_CTL(("%s: resumed for unknown reason?\n", __func__));
1707#ifdef DHD_DEBUG
1708 dhd_os_sdlock(bus->dhd);
1709 dhdsdio_checkdied(bus, NULL, 0);
1710 dhd_os_sdunlock(bus->dhd);
1711#endif /* DHD_DEBUG */
1712 }
1713
1714 if (rxlen)
1715 bus->dhd->rx_ctlpkts++;
1716 else
1717 bus->dhd->rx_ctlerrs++;
1718
Jason Coopere9887c92010-10-06 10:08:02 -04001719 return rxlen ? (int)rxlen : -ETIMEDOUT;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001720}
1721
1722/* IOVar table */
1723enum {
1724 IOV_INTR = 1,
1725 IOV_POLLRATE,
1726 IOV_SDREG,
1727 IOV_SBREG,
1728 IOV_SDCIS,
1729 IOV_MEMBYTES,
1730 IOV_MEMSIZE,
1731#ifdef DHD_DEBUG
1732 IOV_CHECKDIED,
1733#endif
1734 IOV_DOWNLOAD,
1735 IOV_FORCEEVEN,
1736 IOV_SDIOD_DRIVE,
1737 IOV_READAHEAD,
1738 IOV_SDRXCHAIN,
1739 IOV_ALIGNCTL,
1740 IOV_SDALIGN,
1741 IOV_DEVRESET,
1742 IOV_CPU,
1743#ifdef SDTEST
1744 IOV_PKTGEN,
1745 IOV_EXTLOOP,
1746#endif /* SDTEST */
1747 IOV_SPROM,
1748 IOV_TXBOUND,
1749 IOV_RXBOUND,
1750 IOV_TXMINMAX,
1751 IOV_IDLETIME,
1752 IOV_IDLECLOCK,
1753 IOV_SD1IDLE,
1754 IOV_SLEEP,
1755 IOV_VARS
1756};
1757
Roland Vossen67ad48b2011-06-01 13:45:51 +02001758const struct brcmu_iovar dhdsdio_iovars[] = {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001759 {"intr", IOV_INTR, 0, IOVT_BOOL, 0},
1760 {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0},
1761 {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0},
1762 {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0},
1763 {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0},
1764 {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0},
1765 {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int)},
1766 {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0},
1767 {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0},
1768 {"vars", IOV_VARS, 0, IOVT_BUFFER, 0},
1769 {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0},
1770 {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0},
1771 {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0},
1772 {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0},
1773 {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0},
1774 {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0},
1775#ifdef DHD_DEBUG
1776 {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t)}
1777 ,
1778 {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t)}
1779 ,
1780 {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN}
1781 ,
1782 {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0}
1783 ,
1784 {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0}
1785 ,
1786 {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0}
1787 ,
1788 {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0}
1789 ,
1790 {"cpu", IOV_CPU, 0, IOVT_BOOL, 0}
1791 ,
1792#ifdef DHD_DEBUG
1793 {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0}
1794 ,
1795#endif /* DHD_DEBUG */
1796#endif /* DHD_DEBUG */
1797#ifdef SDTEST
1798 {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0}
1799 ,
1800 {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t)}
1801 ,
1802#endif /* SDTEST */
1803
1804 {NULL, 0, 0, 0, 0}
1805};
1806
1807static void
Roland Vossen67ad48b2011-06-01 13:45:51 +02001808dhd_dump_pct(struct brcmu_strbuf *strbuf, char *desc, uint num, uint div)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001809{
1810 uint q1, q2;
1811
1812 if (!div) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02001813 brcmu_bprintf(strbuf, "%s N/A", desc);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001814 } else {
1815 q1 = num / div;
1816 q2 = (100 * (num - (q1 * div))) / div;
Roland Vossen67ad48b2011-06-01 13:45:51 +02001817 brcmu_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001818 }
1819}
1820
Roland Vossen67ad48b2011-06-01 13:45:51 +02001821void dhd_bus_dump(dhd_pub_t *dhdp, struct brcmu_strbuf *strbuf)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001822{
1823 dhd_bus_t *bus = dhdp->bus;
1824
Roland Vossen67ad48b2011-06-01 13:45:51 +02001825 brcmu_bprintf(strbuf, "Bus SDIO structure:\n");
1826 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001827 "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
1828 bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001829 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001830 "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
1831 bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max,
1832 bus->rxskip, bus->rxlen, bus->rx_seq);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001833 brcmu_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001834 bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001835 brcmu_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001836 bus->pollrate, bus->pollcnt, bus->regfails);
1837
Roland Vossen67ad48b2011-06-01 13:45:51 +02001838 brcmu_bprintf(strbuf, "\nAdditional counters:\n");
1839 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001840 "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
1841 bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
1842 bus->rxc_errors);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001843 brcmu_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001844 bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001845 brcmu_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n",
1846 bus->fc_rcvd, bus->fc_xoff, bus->fc_xon);
1847 brcmu_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001848 bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001849 brcmu_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs"
1850 " %d\n",
1851 (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs,
1852 bus->f2rxdata, bus->f2txdata, bus->f1regdata);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001853 {
1854 dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
1855 (bus->f2rxhdrs + bus->f2rxdata));
1856 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets,
1857 bus->f1regdata);
1858 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
1859 (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
1860 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets,
1861 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001862 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001863
1864 dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
1865 bus->dhd->rx_packets);
1866 dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts,
1867 bus->rxglomframes);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001868 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001869
1870 dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets,
1871 bus->f2txdata);
1872 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets,
1873 bus->f1regdata);
1874 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets,
1875 (bus->f2txdata + bus->f1regdata));
1876 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets,
1877 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001878 brcmu_bprintf(strbuf, "\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001879
1880 dhd_dump_pct(strbuf, "Total: pkts/f2rw",
1881 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1882 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
1883 dhd_dump_pct(strbuf, ", pkts/f1sd",
1884 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1885 bus->f1regdata);
1886 dhd_dump_pct(strbuf, ", pkts/sd",
1887 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1888 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata +
1889 bus->f1regdata));
1890 dhd_dump_pct(strbuf, ", pkts/int",
1891 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1892 bus->intrcount);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001893 brcmu_bprintf(strbuf, "\n\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001894 }
1895
1896#ifdef SDTEST
1897 if (bus->pktgen_count) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02001898 brcmu_bprintf(strbuf, "pktgen config and count:\n");
1899 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001900 "freq %d count %d print %d total %d min %d len %d\n",
1901 bus->pktgen_freq, bus->pktgen_count,
1902 bus->pktgen_print, bus->pktgen_total,
1903 bus->pktgen_minlen, bus->pktgen_maxlen);
Roland Vossen67ad48b2011-06-01 13:45:51 +02001904 brcmu_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001905 bus->pktgen_sent, bus->pktgen_rcvd,
1906 bus->pktgen_fail);
1907 }
1908#endif /* SDTEST */
1909#ifdef DHD_DEBUG
Roland Vossen67ad48b2011-06-01 13:45:51 +02001910 brcmu_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001911 bus->dpc_sched,
1912 (bcmsdh_intr_pending(bus->sdh) ? " " : " not "));
Roland Vossen67ad48b2011-06-01 13:45:51 +02001913 brcmu_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001914 bus->roundup);
1915#endif /* DHD_DEBUG */
Roland Vossen67ad48b2011-06-01 13:45:51 +02001916 brcmu_bprintf(strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001917 "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
1918 bus->clkstate, bus->activity, bus->idletime, bus->idlecount,
1919 bus->sleeping);
1920}
1921
1922void dhd_bus_clearcounts(dhd_pub_t *dhdp)
1923{
1924 dhd_bus_t *bus = (dhd_bus_t *) dhdp->bus;
1925
1926 bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
1927 bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
1928 bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
1929 bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
1930 bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
1931 bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
1932}
1933
1934#ifdef SDTEST
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001935static int dhdsdio_pktgen_get(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001936{
1937 dhd_pktgen_t pktgen;
1938
1939 pktgen.version = DHD_PKTGEN_VERSION;
1940 pktgen.freq = bus->pktgen_freq;
1941 pktgen.count = bus->pktgen_count;
1942 pktgen.print = bus->pktgen_print;
1943 pktgen.total = bus->pktgen_total;
1944 pktgen.minlen = bus->pktgen_minlen;
1945 pktgen.maxlen = bus->pktgen_maxlen;
1946 pktgen.numsent = bus->pktgen_sent;
1947 pktgen.numrcvd = bus->pktgen_rcvd;
1948 pktgen.numfail = bus->pktgen_fail;
1949 pktgen.mode = bus->pktgen_mode;
1950 pktgen.stop = bus->pktgen_stop;
1951
Stanislav Fomichev02160692011-02-15 01:05:10 +03001952 memcpy(arg, &pktgen, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001953
1954 return 0;
1955}
1956
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001957static int dhdsdio_pktgen_set(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001958{
1959 dhd_pktgen_t pktgen;
1960 uint oldcnt, oldmode;
1961
Stanislav Fomichev02160692011-02-15 01:05:10 +03001962 memcpy(&pktgen, arg, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001963 if (pktgen.version != DHD_PKTGEN_VERSION)
Roland Vossene10d82d2011-05-03 11:35:19 +02001964 return -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001965
1966 oldcnt = bus->pktgen_count;
1967 oldmode = bus->pktgen_mode;
1968
1969 bus->pktgen_freq = pktgen.freq;
1970 bus->pktgen_count = pktgen.count;
1971 bus->pktgen_print = pktgen.print;
1972 bus->pktgen_total = pktgen.total;
1973 bus->pktgen_minlen = pktgen.minlen;
1974 bus->pktgen_maxlen = pktgen.maxlen;
1975 bus->pktgen_mode = pktgen.mode;
1976 bus->pktgen_stop = pktgen.stop;
1977
1978 bus->pktgen_tick = bus->pktgen_ptick = 0;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001979 bus->pktgen_len = max(bus->pktgen_len, bus->pktgen_minlen);
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001980 bus->pktgen_len = min(bus->pktgen_len, bus->pktgen_maxlen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001981
1982 /* Clear counts for a new pktgen (mode change, or was stopped) */
1983 if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
1984 bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
1985
1986 return 0;
1987}
1988#endif /* SDTEST */
1989
1990static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001991dhdsdio_membytes(dhd_bus_t *bus, bool write, u32 address, u8 *data,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001992 uint size)
1993{
1994 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001995 u32 sdaddr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001996 uint dsize;
1997
1998 /* Determine initial transfer parameters */
1999 sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
2000 if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
2001 dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
2002 else
2003 dsize = size;
2004
2005 /* Set the backplane window to include the start address */
2006 bcmerror = dhdsdio_set_siaddr_window(bus, address);
2007 if (bcmerror) {
2008 DHD_ERROR(("%s: window change failed\n", __func__));
2009 goto xfer_done;
2010 }
2011
2012 /* Do the transfer(s) */
2013 while (size) {
2014 DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n",
2015 __func__, (write ? "write" : "read"), dsize,
2016 sdaddr, (address & SBSDIO_SBWINDOW_MASK)));
2017 bcmerror =
2018 bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize);
2019 if (bcmerror) {
2020 DHD_ERROR(("%s: membytes transfer failed\n", __func__));
2021 break;
2022 }
2023
2024 /* Adjust for next transfer (if any) */
2025 size -= dsize;
2026 if (size) {
2027 data += dsize;
2028 address += dsize;
2029 bcmerror = dhdsdio_set_siaddr_window(bus, address);
2030 if (bcmerror) {
2031 DHD_ERROR(("%s: window change failed\n",
2032 __func__));
2033 break;
2034 }
2035 sdaddr = 0;
Greg Kroah-Hartmanb61640d2010-10-08 12:37:39 -07002036 dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002037 }
2038 }
2039
2040xfer_done:
2041 /* Return the window to backplane enumeration space for core access */
2042 if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) {
2043 DHD_ERROR(("%s: FAILED to set window back to 0x%x\n",
2044 __func__, bcmsdh_cur_sbwad(bus->sdh)));
2045 }
2046
2047 return bcmerror;
2048}
2049
2050#ifdef DHD_DEBUG
Franky Linb49b14d2011-06-01 13:45:37 +02002051static int dhdsdio_readshared(dhd_bus_t *bus, struct sdpcm_shared *sh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002052{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002053 u32 addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002054 int rv;
2055
2056 /* Read last word in memory to determine address of
2057 sdpcm_shared structure */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002058 rv = dhdsdio_membytes(bus, false, bus->ramsize - 4, (u8 *)&addr, 4);
Jason Cooper9b890322010-09-30 15:15:39 -04002059 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002060 return rv;
2061
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002062 addr = le32_to_cpu(addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002063
2064 DHD_INFO(("sdpcm_shared address 0x%08X\n", addr));
2065
2066 /*
2067 * Check if addr is valid.
2068 * NVRAM length at the end of memory should have been overwritten.
2069 */
2070 if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
2071 DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n",
2072 __func__, addr));
Roland Vossenb74ac122011-05-03 11:35:20 +02002073 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002074 }
2075
Roland Vossen70963f92011-06-01 13:45:08 +02002076 /* Read rte_shared structure */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002077 rv = dhdsdio_membytes(bus, false, addr, (u8 *) sh,
Franky Linb49b14d2011-06-01 13:45:37 +02002078 sizeof(struct sdpcm_shared));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002079 if (rv < 0)
2080 return rv;
2081
2082 /* Endianness */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002083 sh->flags = le32_to_cpu(sh->flags);
2084 sh->trap_addr = le32_to_cpu(sh->trap_addr);
2085 sh->assert_exp_addr = le32_to_cpu(sh->assert_exp_addr);
2086 sh->assert_file_addr = le32_to_cpu(sh->assert_file_addr);
2087 sh->assert_line = le32_to_cpu(sh->assert_line);
2088 sh->console_addr = le32_to_cpu(sh->console_addr);
2089 sh->msgtrace_addr = le32_to_cpu(sh->msgtrace_addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002090
2091 if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
2092 DHD_ERROR(("%s: sdpcm_shared version %d in dhd "
2093 "is different than sdpcm_shared version %d in dongle\n",
2094 __func__, SDPCM_SHARED_VERSION,
2095 sh->flags & SDPCM_SHARED_VERSION_MASK));
Roland Vossenb74ac122011-05-03 11:35:20 +02002096 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002097 }
2098
Roland Vossena1c5ad82011-04-11 15:16:24 +02002099 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002100}
2101
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002102static int dhdsdio_checkdied(dhd_bus_t *bus, u8 *data, uint size)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002103{
2104 int bcmerror = 0;
2105 uint msize = 512;
2106 char *mbuffer = NULL;
2107 uint maxstrlen = 256;
2108 char *str = NULL;
2109 trap_t tr;
Franky Linb49b14d2011-06-01 13:45:37 +02002110 struct sdpcm_shared sdpcm_shared;
Roland Vossen67ad48b2011-06-01 13:45:51 +02002111 struct brcmu_strbuf strbuf;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002112
2113 DHD_TRACE(("%s: Enter\n", __func__));
2114
2115 if (data == NULL) {
2116 /*
2117 * Called after a rx ctrl timeout. "data" is NULL.
2118 * allocate memory to trace the trap or assert.
2119 */
2120 size = msize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002121 mbuffer = data = kmalloc(msize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002122 if (mbuffer == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002123 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002124 msize));
Roland Vossene10d82d2011-05-03 11:35:19 +02002125 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002126 goto done;
2127 }
2128 }
2129
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002130 str = kmalloc(maxstrlen, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04002131 if (str == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002132 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__, maxstrlen));
Roland Vossene10d82d2011-05-03 11:35:19 +02002133 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002134 goto done;
2135 }
2136
Jason Cooper9b890322010-09-30 15:15:39 -04002137 bcmerror = dhdsdio_readshared(bus, &sdpcm_shared);
2138 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002139 goto done;
2140
Roland Vossen67ad48b2011-06-01 13:45:51 +02002141 brcmu_binit(&strbuf, data, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002142
Roland Vossen67ad48b2011-06-01 13:45:51 +02002143 brcmu_bprintf(&strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002144 "msgtrace address : 0x%08X\nconsole address : 0x%08X\n",
2145 sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
2146
2147 if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
2148 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
2149 * (Avoids conflict with real asserts for programmatic
2150 * parsing of output.)
2151 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002152 brcmu_bprintf(&strbuf, "Assrt not built in dongle\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002153 }
2154
2155 if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) ==
2156 0) {
2157 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
2158 * (Avoids conflict with real asserts for programmatic
2159 * parsing of output.)
2160 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002161 brcmu_bprintf(&strbuf, "No trap%s in dongle",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002162 (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
2163 ? "/assrt" : "");
2164 } else {
2165 if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
2166 /* Download assert */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002167 brcmu_bprintf(&strbuf, "Dongle assert");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002168 if (sdpcm_shared.assert_exp_addr != 0) {
2169 str[0] = '\0';
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002170 bcmerror = dhdsdio_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04002171 sdpcm_shared.assert_exp_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002172 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04002173 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002174 goto done;
2175
2176 str[maxstrlen - 1] = '\0';
Roland Vossen67ad48b2011-06-01 13:45:51 +02002177 brcmu_bprintf(&strbuf, " expr \"%s\"", str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002178 }
2179
2180 if (sdpcm_shared.assert_file_addr != 0) {
2181 str[0] = '\0';
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002182 bcmerror = dhdsdio_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04002183 sdpcm_shared.assert_file_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002184 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04002185 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002186 goto done;
2187
2188 str[maxstrlen - 1] = '\0';
Roland Vossen67ad48b2011-06-01 13:45:51 +02002189 brcmu_bprintf(&strbuf, " file \"%s\"", str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002190 }
2191
Roland Vossen67ad48b2011-06-01 13:45:51 +02002192 brcmu_bprintf(&strbuf, " line %d ",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002193 sdpcm_shared.assert_line);
2194 }
2195
2196 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002197 bcmerror = dhdsdio_membytes(bus, false,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002198 sdpcm_shared.trap_addr, (u8 *)&tr,
Jason Cooper9b890322010-09-30 15:15:39 -04002199 sizeof(trap_t));
2200 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002201 goto done;
2202
Roland Vossen67ad48b2011-06-01 13:45:51 +02002203 brcmu_bprintf(&strbuf,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002204 "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
2205 "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
2206 "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",
2207 tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13,
2208 tr.r14, tr.pc, sdpcm_shared.trap_addr,
2209 tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5,
2210 tr.r6, tr.r7);
2211 }
2212 }
2213
2214 if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP))
2215 DHD_ERROR(("%s: %s\n", __func__, strbuf.origbuf));
2216
2217#ifdef DHD_DEBUG
2218 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
2219 /* Mem dump to a file on device */
2220 dhdsdio_mem_dump(bus);
2221 }
2222#endif /* DHD_DEBUG */
2223
2224done:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002225 kfree(mbuffer);
2226 kfree(str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002227
2228 return bcmerror;
2229}
2230
2231static int dhdsdio_mem_dump(dhd_bus_t *bus)
2232{
2233 int ret = 0;
2234 int size; /* Full mem size */
2235 int start = 0; /* Start address */
2236 int read_size = 0; /* Read size of each iteration */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002237 u8 *buf = NULL, *databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002238
2239 /* Get full mem size */
2240 size = bus->ramsize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002241 buf = kmalloc(size, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002242 if (!buf) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002243 DHD_ERROR(("%s: Out of memory (%d bytes)\n", __func__, size));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002244 return -1;
2245 }
2246
2247 /* Read mem content */
Arend van Spriel0bef7742011-02-10 12:03:44 +01002248 printk(KERN_DEBUG "Dump dongle memory");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002249 databuf = buf;
2250 while (size) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07002251 read_size = min(MEMBLOCK, size);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002252 ret = dhdsdio_membytes(bus, false, start, databuf, read_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002253 if (ret) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002254 DHD_ERROR(("%s: Error membytes %d\n", __func__, ret));
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002255 kfree(buf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002256 return -1;
2257 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01002258 printk(".");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002259
2260 /* Decrement size and increment start address */
2261 size -= read_size;
2262 start += read_size;
2263 databuf += read_size;
2264 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01002265 printk(KERN_DEBUG "Done\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002266
2267 /* free buf before return !!! */
2268 if (write_to_file(bus->dhd, buf, bus->ramsize)) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01002269 DHD_ERROR(("%s: Error writing to files\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002270 return -1;
2271 }
2272
2273 /* buf free handled in write_to_file, not here */
2274 return 0;
2275}
2276
2277#define CONSOLE_LINE_MAX 192
2278
2279static int dhdsdio_readconsole(dhd_bus_t *bus)
2280{
2281 dhd_console_t *c = &bus->console;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002282 u8 line[CONSOLE_LINE_MAX], ch;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002283 u32 n, idx, addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002284 int rv;
2285
2286 /* Don't do anything until FWREADY updates console address */
2287 if (bus->console_addr == 0)
2288 return 0;
2289
2290 /* Read console log struct */
Roland Vossen70963f92011-06-01 13:45:08 +02002291 addr = bus->console_addr + offsetof(rte_cons_t, log);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002292 rv = dhdsdio_membytes(bus, false, addr, (u8 *)&c->log,
Jason Cooper9b890322010-09-30 15:15:39 -04002293 sizeof(c->log));
2294 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002295 return rv;
2296
2297 /* Allocate console buffer (one time only) */
2298 if (c->buf == NULL) {
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002299 c->bufsize = le32_to_cpu(c->log.buf_size);
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002300 c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04002301 if (c->buf == NULL)
Roland Vossene10d82d2011-05-03 11:35:19 +02002302 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002303 }
2304
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002305 idx = le32_to_cpu(c->log.idx);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002306
2307 /* Protect against corrupt value */
2308 if (idx > c->bufsize)
Roland Vossenb74ac122011-05-03 11:35:20 +02002309 return -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002310
2311 /* Skip reading the console buffer if the index pointer
2312 has not moved */
2313 if (idx == c->last)
Roland Vossena1c5ad82011-04-11 15:16:24 +02002314 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002315
2316 /* Read the console buffer */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002317 addr = le32_to_cpu(c->log.buf);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002318 rv = dhdsdio_membytes(bus, false, addr, c->buf, c->bufsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002319 if (rv < 0)
2320 return rv;
2321
2322 while (c->last != idx) {
2323 for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
2324 if (c->last == idx) {
2325 /* This would output a partial line.
2326 * Instead, back up
2327 * the buffer pointer and output this
2328 * line next time around.
2329 */
2330 if (c->last >= n)
2331 c->last -= n;
2332 else
2333 c->last = c->bufsize - n;
2334 goto break2;
2335 }
2336 ch = c->buf[c->last];
2337 c->last = (c->last + 1) % c->bufsize;
2338 if (ch == '\n')
2339 break;
2340 line[n] = ch;
2341 }
2342
2343 if (n > 0) {
2344 if (line[n - 1] == '\r')
2345 n--;
2346 line[n] = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01002347 printk(KERN_DEBUG "CONSOLE: %s\n", line);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002348 }
2349 }
2350break2:
2351
Roland Vossena1c5ad82011-04-11 15:16:24 +02002352 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002353}
2354#endif /* DHD_DEBUG */
2355
2356int dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len)
2357{
Roland Vossena1c5ad82011-04-11 15:16:24 +02002358 int bcmerror = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002359
2360 DHD_TRACE(("%s: Enter\n", __func__));
2361
2362 /* Basic sanity checks */
2363 if (bus->dhd->up) {
Roland Vossenb74ac122011-05-03 11:35:20 +02002364 bcmerror = -EISCONN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002365 goto err;
2366 }
2367 if (!len) {
Roland Vossene10d82d2011-05-03 11:35:19 +02002368 bcmerror = -EOVERFLOW;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002369 goto err;
2370 }
2371
2372 /* Free the old ones and replace with passed variables */
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002373 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002374
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002375 bus->vars = kmalloc(len, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002376 bus->varsz = bus->vars ? len : 0;
2377 if (bus->vars == NULL) {
Roland Vossene10d82d2011-05-03 11:35:19 +02002378 bcmerror = -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002379 goto err;
2380 }
2381
2382 /* Copy the passed variables, which should include the
2383 terminating double-null */
Stanislav Fomichev02160692011-02-15 01:05:10 +03002384 memcpy(bus->vars, arg, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002385err:
2386 return bcmerror;
2387}
2388
2389static int
Roland Vossen67ad48b2011-06-01 13:45:51 +02002390dhdsdio_doiovar(dhd_bus_t *bus, const struct brcmu_iovar *vi, u32 actionid,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002391 const char *name, void *params, int plen, void *arg, int len,
2392 int val_size)
2393{
2394 int bcmerror = 0;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002395 s32 int_val = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002396 bool bool_val = 0;
2397
2398 DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p "
2399 "len %d val_size %d\n",
2400 __func__, actionid, name, params, plen, arg, len, val_size));
2401
Roland Vossen67ad48b2011-06-01 13:45:51 +02002402 bcmerror = brcmu_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002403 if (bcmerror != 0)
2404 goto exit;
2405
2406 if (plen >= (int)sizeof(int_val))
Stanislav Fomichev02160692011-02-15 01:05:10 +03002407 memcpy(&int_val, params, sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002408
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002409 bool_val = (int_val != 0) ? true : false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002410
2411 /* Some ioctls use the bus */
2412 dhd_os_sdlock(bus->dhd);
2413
2414 /* Check if dongle is in reset. If so, only allow DEVRESET iovars */
2415 if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
2416 actionid == IOV_GVAL(IOV_DEVRESET))) {
Roland Vossenb74ac122011-05-03 11:35:20 +02002417 bcmerror = -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002418 goto exit;
2419 }
2420
2421 /* Handle sleep stuff before any clock mucking */
2422 if (vi->varid == IOV_SLEEP) {
2423 if (IOV_ISSET(actionid)) {
2424 bcmerror = dhdsdio_bussleep(bus, bool_val);
2425 } else {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002426 int_val = (s32) bus->sleeping;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002427 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002428 }
2429 goto exit;
2430 }
2431
2432 /* Request clock to allow SDIO accesses */
2433 if (!bus->dhd->dongle_reset) {
2434 BUS_WAKE(bus);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002435 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002436 }
2437
2438 switch (actionid) {
2439 case IOV_GVAL(IOV_INTR):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002440 int_val = (s32) bus->intr;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002441 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002442 break;
2443
2444 case IOV_SVAL(IOV_INTR):
2445 bus->intr = bool_val;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002446 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002447 if (bus->dhd->up) {
2448 if (bus->intr) {
2449 DHD_INTR(("%s: enable SDIO device interrupts\n",
2450 __func__));
2451 bcmsdh_intr_enable(bus->sdh);
2452 } else {
2453 DHD_INTR(("%s: disable SDIO interrupts\n",
2454 __func__));
2455 bcmsdh_intr_disable(bus->sdh);
2456 }
2457 }
2458 break;
2459
2460 case IOV_GVAL(IOV_POLLRATE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002461 int_val = (s32) bus->pollrate;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002462 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002463 break;
2464
2465 case IOV_SVAL(IOV_POLLRATE):
2466 bus->pollrate = (uint) int_val;
2467 bus->poll = (bus->pollrate != 0);
2468 break;
2469
2470 case IOV_GVAL(IOV_IDLETIME):
2471 int_val = bus->idletime;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002472 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002473 break;
2474
2475 case IOV_SVAL(IOV_IDLETIME):
2476 if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE))
Roland Vossene10d82d2011-05-03 11:35:19 +02002477 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002478 else
2479 bus->idletime = int_val;
2480 break;
2481
2482 case IOV_GVAL(IOV_IDLECLOCK):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002483 int_val = (s32) bus->idleclock;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002484 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002485 break;
2486
2487 case IOV_SVAL(IOV_IDLECLOCK):
2488 bus->idleclock = int_val;
2489 break;
2490
2491 case IOV_GVAL(IOV_SD1IDLE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002492 int_val = (s32) sd1idle;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002493 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002494 break;
2495
2496 case IOV_SVAL(IOV_SD1IDLE):
2497 sd1idle = bool_val;
2498 break;
2499
2500 case IOV_SVAL(IOV_MEMBYTES):
2501 case IOV_GVAL(IOV_MEMBYTES):
2502 {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002503 u32 address;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002504 uint size, dsize;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002505 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002506
2507 bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
2508
2509 ASSERT(plen >= 2 * sizeof(int));
2510
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002511 address = (u32) int_val;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002512 memcpy(&int_val, (char *)params + sizeof(int_val),
2513 sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002514 size = (uint) int_val;
2515
2516 /* Do some validation */
2517 dsize = set ? plen - (2 * sizeof(int)) : len;
2518 if (dsize < size) {
2519 DHD_ERROR(("%s: error on %s membytes, addr "
2520 "0x%08x size %d dsize %d\n",
2521 __func__, (set ? "set" : "get"),
2522 address, size, dsize));
Roland Vossene10d82d2011-05-03 11:35:19 +02002523 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002524 break;
2525 }
2526
2527 DHD_INFO(("%s: Request to %s %d bytes at address "
2528 "0x%08x\n",
2529 __func__, (set ? "write" : "read"), size, address));
2530
2531 /* If we know about SOCRAM, check for a fit */
2532 if ((bus->orig_ramsize) &&
2533 ((address > bus->orig_ramsize)
2534 || (address + size > bus->orig_ramsize))) {
2535 DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d "
2536 "bytes at 0x%08x\n",
2537 __func__, bus->orig_ramsize, size, address));
Roland Vossene10d82d2011-05-03 11:35:19 +02002538 bcmerror = -EINVAL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002539 break;
2540 }
2541
2542 /* Generate the actual data pointer */
2543 data =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002544 set ? (u8 *) params +
2545 2 * sizeof(int) : (u8 *) arg;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002546
2547 /* Call to do the transfer */
2548 bcmerror =
2549 dhdsdio_membytes(bus, set, address, data, size);
2550
2551 break;
2552 }
2553
2554 case IOV_GVAL(IOV_MEMSIZE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002555 int_val = (s32) bus->ramsize;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002556 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002557 break;
2558
2559 case IOV_GVAL(IOV_SDIOD_DRIVE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002560 int_val = (s32) dhd_sdiod_drive_strength;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002561 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002562 break;
2563
2564 case IOV_SVAL(IOV_SDIOD_DRIVE):
2565 dhd_sdiod_drive_strength = int_val;
Franky Lin5d0d7a92011-04-25 19:34:05 -07002566 dhdsdio_sdiod_drive_strength_init(bus,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002567 dhd_sdiod_drive_strength);
2568 break;
2569
2570 case IOV_SVAL(IOV_DOWNLOAD):
2571 bcmerror = dhdsdio_download_state(bus, bool_val);
2572 break;
2573
2574 case IOV_SVAL(IOV_VARS):
2575 bcmerror = dhdsdio_downloadvars(bus, arg, len);
2576 break;
2577
2578 case IOV_GVAL(IOV_READAHEAD):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002579 int_val = (s32) dhd_readahead;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002580 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002581 break;
2582
2583 case IOV_SVAL(IOV_READAHEAD):
2584 if (bool_val && !dhd_readahead)
2585 bus->nextlen = 0;
2586 dhd_readahead = bool_val;
2587 break;
2588
2589 case IOV_GVAL(IOV_SDRXCHAIN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002590 int_val = (s32) bus->use_rxchain;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002591 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002592 break;
2593
2594 case IOV_SVAL(IOV_SDRXCHAIN):
2595 if (bool_val && !bus->sd_rxchain)
Roland Vossene10d82d2011-05-03 11:35:19 +02002596 bcmerror = -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002597 else
2598 bus->use_rxchain = bool_val;
2599 break;
2600 case IOV_GVAL(IOV_ALIGNCTL):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002601 int_val = (s32) dhd_alignctl;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002602 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002603 break;
2604
2605 case IOV_SVAL(IOV_ALIGNCTL):
2606 dhd_alignctl = bool_val;
2607 break;
2608
2609 case IOV_GVAL(IOV_SDALIGN):
2610 int_val = DHD_SDALIGN;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002611 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002612 break;
2613
2614#ifdef DHD_DEBUG
2615 case IOV_GVAL(IOV_VARS):
2616 if (bus->varsz < (uint) len)
Stanislav Fomichev02160692011-02-15 01:05:10 +03002617 memcpy(arg, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002618 else
Roland Vossene10d82d2011-05-03 11:35:19 +02002619 bcmerror = -EOVERFLOW;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002620 break;
2621#endif /* DHD_DEBUG */
2622
2623#ifdef DHD_DEBUG
2624 case IOV_GVAL(IOV_SDREG):
2625 {
2626 sdreg_t *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002627 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002628
2629 sd_ptr = (sdreg_t *) params;
2630
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002631 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002632 size = sd_ptr->func;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002633 int_val = (s32) bcmsdh_reg_read(bus->sdh, addr, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002634 if (bcmsdh_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002635 bcmerror = -EIO;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002636 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002637 break;
2638 }
2639
2640 case IOV_SVAL(IOV_SDREG):
2641 {
2642 sdreg_t *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002643 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002644
2645 sd_ptr = (sdreg_t *) params;
2646
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002647 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002648 size = sd_ptr->func;
2649 bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value);
2650 if (bcmsdh_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002651 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002652 break;
2653 }
2654
2655 /* Same as above, but offset is not backplane
2656 (not SDIO core) */
2657 case IOV_GVAL(IOV_SBREG):
2658 {
2659 sdreg_t sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002660 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002661
Stanislav Fomichev02160692011-02-15 01:05:10 +03002662 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002663
2664 addr = SI_ENUM_BASE + sdreg.offset;
2665 size = sdreg.func;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002666 int_val = (s32) bcmsdh_reg_read(bus->sdh, addr, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002667 if (bcmsdh_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002668 bcmerror = -EIO;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002669 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002670 break;
2671 }
2672
2673 case IOV_SVAL(IOV_SBREG):
2674 {
2675 sdreg_t sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002676 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002677
Stanislav Fomichev02160692011-02-15 01:05:10 +03002678 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002679
2680 addr = SI_ENUM_BASE + sdreg.offset;
2681 size = sdreg.func;
2682 bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value);
2683 if (bcmsdh_regfail(bus->sdh))
Roland Vossenb74ac122011-05-03 11:35:20 +02002684 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002685 break;
2686 }
2687
2688 case IOV_GVAL(IOV_SDCIS):
2689 {
2690 *(char *)arg = 0;
2691
nohee koea3b8a22010-10-09 10:34:38 -07002692 strcat(arg, "\nFunc 0\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002693 bcmsdh_cis_read(bus->sdh, 0x10,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002694 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002695 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002696 strcat(arg, "\nFunc 1\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002697 bcmsdh_cis_read(bus->sdh, 0x11,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002698 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002699 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002700 strcat(arg, "\nFunc 2\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002701 bcmsdh_cis_read(bus->sdh, 0x12,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002702 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002703 SBSDIO_CIS_SIZE_LIMIT);
2704 break;
2705 }
2706
2707 case IOV_GVAL(IOV_FORCEEVEN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002708 int_val = (s32) forcealign;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002709 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002710 break;
2711
2712 case IOV_SVAL(IOV_FORCEEVEN):
2713 forcealign = bool_val;
2714 break;
2715
2716 case IOV_GVAL(IOV_TXBOUND):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002717 int_val = (s32) dhd_txbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002718 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002719 break;
2720
2721 case IOV_SVAL(IOV_TXBOUND):
2722 dhd_txbound = (uint) int_val;
2723 break;
2724
2725 case IOV_GVAL(IOV_RXBOUND):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002726 int_val = (s32) dhd_rxbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002727 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002728 break;
2729
2730 case IOV_SVAL(IOV_RXBOUND):
2731 dhd_rxbound = (uint) int_val;
2732 break;
2733
2734 case IOV_GVAL(IOV_TXMINMAX):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002735 int_val = (s32) dhd_txminmax;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002736 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002737 break;
2738
2739 case IOV_SVAL(IOV_TXMINMAX):
2740 dhd_txminmax = (uint) int_val;
2741 break;
2742#endif /* DHD_DEBUG */
2743
2744#ifdef SDTEST
2745 case IOV_GVAL(IOV_EXTLOOP):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002746 int_val = (s32) bus->ext_loop;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002747 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002748 break;
2749
2750 case IOV_SVAL(IOV_EXTLOOP):
2751 bus->ext_loop = bool_val;
2752 break;
2753
2754 case IOV_GVAL(IOV_PKTGEN):
2755 bcmerror = dhdsdio_pktgen_get(bus, arg);
2756 break;
2757
2758 case IOV_SVAL(IOV_PKTGEN):
2759 bcmerror = dhdsdio_pktgen_set(bus, arg);
2760 break;
2761#endif /* SDTEST */
2762
2763 case IOV_SVAL(IOV_DEVRESET):
2764 DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d "
2765 "busstate=%d\n",
2766 __func__, bool_val, bus->dhd->dongle_reset,
2767 bus->dhd->busstate));
2768
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002769 dhd_bus_devreset(bus->dhd, (u8) bool_val);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002770
2771 break;
2772
2773 case IOV_GVAL(IOV_DEVRESET):
2774 DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __func__));
2775
2776 /* Get its status */
2777 int_val = (bool) bus->dhd->dongle_reset;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002778 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002779
2780 break;
2781
2782 default:
Roland Vossene10d82d2011-05-03 11:35:19 +02002783 bcmerror = -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002784 break;
2785 }
2786
2787exit:
2788 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002789 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002790 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002791 }
2792
2793 dhd_os_sdunlock(bus->dhd);
2794
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002795 if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == false)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002796 dhd_preinit_ioctls((dhd_pub_t *) bus->dhd);
2797
2798 return bcmerror;
2799}
2800
2801static int dhdsdio_write_vars(dhd_bus_t *bus)
2802{
2803 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002804 u32 varsize;
2805 u32 varaddr;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002806 u8 *vbuffer;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002807 u32 varsizew;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002808#ifdef DHD_DEBUG
2809 char *nvram_ularray;
2810#endif /* DHD_DEBUG */
2811
2812 /* Even if there are no vars are to be written, we still
2813 need to set the ramsize. */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07002814 varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002815 varaddr = (bus->ramsize - 4) - varsize;
2816
2817 if (bus->vars) {
Alexander Beregalov12d0eb42011-03-09 03:53:35 +03002818 vbuffer = kzalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002819 if (!vbuffer)
Roland Vossene10d82d2011-05-03 11:35:19 +02002820 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002821
Stanislav Fomichev02160692011-02-15 01:05:10 +03002822 memcpy(vbuffer, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002823
2824 /* Write the vars list */
2825 bcmerror =
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002826 dhdsdio_membytes(bus, true, varaddr, vbuffer, varsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002827#ifdef DHD_DEBUG
2828 /* Verify NVRAM bytes */
2829 DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002830 nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002831 if (!nvram_ularray)
Roland Vossene10d82d2011-05-03 11:35:19 +02002832 return -ENOMEM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002833
2834 /* Upload image to verify downloaded contents. */
2835 memset(nvram_ularray, 0xaa, varsize);
2836
2837 /* Read the vars list to temp buffer for comparison */
2838 bcmerror =
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002839 dhdsdio_membytes(bus, false, varaddr, nvram_ularray,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002840 varsize);
2841 if (bcmerror) {
2842 DHD_ERROR(("%s: error %d on reading %d nvram bytes at "
2843 "0x%08x\n", __func__, bcmerror, varsize, varaddr));
2844 }
2845 /* Compare the org NVRAM with the one read from RAM */
2846 if (memcmp(vbuffer, nvram_ularray, varsize)) {
2847 DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n",
2848 __func__));
2849 } else
2850 DHD_ERROR(("%s: Download/Upload/Compare of NVRAM ok.\n",
2851 __func__));
2852
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002853 kfree(nvram_ularray);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002854#endif /* DHD_DEBUG */
2855
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002856 kfree(vbuffer);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002857 }
2858
2859 /* adjust to the user specified RAM */
2860 DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
2861 bus->orig_ramsize, bus->ramsize));
2862 DHD_INFO(("Vars are at %d, orig varsize is %d\n", varaddr, varsize));
2863 varsize = ((bus->orig_ramsize - 4) - varaddr);
2864
2865 /*
2866 * Determine the length token:
2867 * Varsize, converted to words, in lower 16-bits, checksum
2868 * in upper 16-bits.
2869 */
2870 if (bcmerror) {
2871 varsizew = 0;
2872 } else {
2873 varsizew = varsize / 4;
2874 varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002875 varsizew = cpu_to_le32(varsizew);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002876 }
2877
2878 DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize,
2879 varsizew));
2880
2881 /* Write the length token to the last word */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002882 bcmerror = dhdsdio_membytes(bus, true, (bus->orig_ramsize - 4),
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002883 (u8 *)&varsizew, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002884
2885 return bcmerror;
2886}
2887
2888static int dhdsdio_download_state(dhd_bus_t *bus, bool enter)
2889{
2890 uint retries;
Franky Lineb5dc512011-04-25 19:34:04 -07002891 u32 regdata;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002892 int bcmerror = 0;
2893
2894 /* To enter download state, disable ARM and reset SOCRAM.
2895 * To exit download state, simply reset ARM (default is RAM boot).
2896 */
2897 if (enter) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002898 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002899
Franky Lineb5dc512011-04-25 19:34:04 -07002900 dhdsdio_chip_disablecore(bus->sdh, bus->ci->armcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002901
Franky Lineb5dc512011-04-25 19:34:04 -07002902 dhdsdio_chip_resetcore(bus->sdh, bus->ci->ramcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002903
2904 /* Clear the top bit of memory */
2905 if (bus->ramsize) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002906 u32 zeros = 0;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002907 dhdsdio_membytes(bus, true, bus->ramsize - 4,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002908 (u8 *)&zeros, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002909 }
2910 } else {
Franky Lineb5dc512011-04-25 19:34:04 -07002911 regdata = bcmsdh_reg_read(bus->sdh,
2912 CORE_SB(bus->ci->ramcorebase, sbtmstatelow), 4);
2913 regdata &= (SBTML_RESET | SBTML_REJ_MASK |
2914 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
2915 if ((SICF_CLOCK_EN << SBTML_SICF_SHIFT) != regdata) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002916 DHD_ERROR(("%s: SOCRAM core is down after reset?\n",
2917 __func__));
Roland Vossenb74ac122011-05-03 11:35:20 +02002918 bcmerror = -EBADE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002919 goto fail;
2920 }
2921
2922 bcmerror = dhdsdio_write_vars(bus);
2923 if (bcmerror) {
2924 DHD_ERROR(("%s: no vars written to RAM\n", __func__));
2925 bcmerror = 0;
2926 }
2927
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002928 W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries);
2929
Franky Lineb5dc512011-04-25 19:34:04 -07002930 dhdsdio_chip_resetcore(bus->sdh, bus->ci->armcorebase);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002931
2932 /* Allow HT Clock now that the ARM is running. */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002933 bus->alp_only = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002934
2935 bus->dhd->busstate = DHD_BUS_LOAD;
2936 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002937fail:
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002938 return bcmerror;
2939}
2940
2941int
2942dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
2943 void *params, int plen, void *arg, int len, bool set)
2944{
2945 dhd_bus_t *bus = dhdp->bus;
Roland Vossen67ad48b2011-06-01 13:45:51 +02002946 const struct brcmu_iovar *vi = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002947 int bcmerror = 0;
2948 int val_size;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002949 u32 actionid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002950
2951 DHD_TRACE(("%s: Enter\n", __func__));
2952
2953 ASSERT(name);
2954 ASSERT(len >= 0);
2955
2956 /* Get MUST have return space */
2957 ASSERT(set || (arg && len));
2958
2959 /* Set does NOT take qualifiers */
2960 ASSERT(!set || (!params && !plen));
2961
2962 /* Look up var locally; if not found pass to host driver */
Roland Vossen67ad48b2011-06-01 13:45:51 +02002963 vi = brcmu_iovar_lookup(dhdsdio_iovars, name);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002964 if (vi == NULL) {
2965 dhd_os_sdlock(bus->dhd);
2966
2967 BUS_WAKE(bus);
2968
2969 /* Turn on clock in case SD command needs backplane */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002970 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002971
2972 bcmerror =
2973 bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len,
2974 set);
2975
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002976 /* Similar check for blocksize change */
2977 if (set && strcmp(name, "sd_blocksize") == 0) {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002978 s32 fnum = 2;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002979 if (bcmsdh_iovar_op
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002980 (bus->sdh, "sd_blocksize", &fnum, sizeof(s32),
2981 &bus->blocksize, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02002982 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002983 bus->blocksize = 0;
2984 DHD_ERROR(("%s: fail on %s get\n", __func__,
2985 "sd_blocksize"));
2986 } else {
2987 DHD_INFO(("%s: noted %s update, value now %d\n",
2988 __func__, "sd_blocksize",
2989 bus->blocksize));
2990 }
2991 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07002992 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002993
2994 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002995 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002996 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002997 }
2998
2999 dhd_os_sdunlock(bus->dhd);
3000 goto exit;
3001 }
3002
3003 DHD_CTL(("%s: %s %s, len %d plen %d\n", __func__,
3004 name, (set ? "set" : "get"), len, plen));
3005
3006 /* set up 'params' pointer in case this is a set command so that
3007 * the convenience int and bool code can be common to set and get
3008 */
3009 if (params == NULL) {
3010 params = arg;
3011 plen = len;
3012 }
3013
3014 if (vi->type == IOVT_VOID)
3015 val_size = 0;
3016 else if (vi->type == IOVT_BUFFER)
3017 val_size = len;
3018 else
3019 /* all other types are integer sized */
3020 val_size = sizeof(int);
3021
3022 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
3023 bcmerror =
3024 dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len,
3025 val_size);
3026
3027exit:
3028 return bcmerror;
3029}
3030
3031void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
3032{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07003033 u32 local_hostintmask;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003034 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003035 uint retries;
3036 int err;
3037
3038 DHD_TRACE(("%s: Enter\n", __func__));
3039
3040 if (enforce_mutex)
3041 dhd_os_sdlock(bus->dhd);
3042
3043 BUS_WAKE(bus);
3044
3045 /* Enable clock for device interrupts */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003046 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003047
3048 /* Disable and clear interrupts at the chip level also */
3049 W_SDREG(0, &bus->regs->hostintmask, retries);
3050 local_hostintmask = bus->hostintmask;
3051 bus->hostintmask = 0;
3052
3053 /* Change our idea of bus state */
3054 bus->dhd->busstate = DHD_BUS_DOWN;
3055
3056 /* Force clocks on backplane to be sure F2 interrupt propagates */
3057 saveclk =
3058 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3059 &err);
3060 if (!err) {
3061 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3062 (saveclk | SBSDIO_FORCE_HT), &err);
3063 }
3064 if (err) {
3065 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
3066 __func__, err));
3067 }
3068
3069 /* Turn off the bus (F2), free any pending packets */
3070 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
3071 bcmsdh_intr_disable(bus->sdh);
Franky Lin0df46042011-06-01 13:45:40 +02003072 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003073 SDIO_FUNC_ENABLE_1, NULL);
3074
3075 /* Clear any pending interrupts now that F2 is disabled */
3076 W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
3077
3078 /* Turn off the backplane clock (only) */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003079 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003080
3081 /* Clear the data packet queues */
Roland Vossen67ad48b2011-06-01 13:45:51 +02003082 brcmu_pktq_flush(&bus->txq, true, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003083
3084 /* Clear any held glomming stuff */
3085 if (bus->glomd)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003086 brcmu_pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003087
3088 if (bus->glom)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003089 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003090
3091 bus->glom = bus->glomd = NULL;
3092
3093 /* Clear rx control and wake any waiters */
3094 bus->rxlen = 0;
3095 dhd_os_ioctl_resp_wake(bus->dhd);
3096
3097 /* Reset some F2 state stuff */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003098 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003099 bus->tx_seq = bus->rx_seq = 0;
3100
3101 if (enforce_mutex)
3102 dhd_os_sdunlock(bus->dhd);
3103}
3104
3105int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
3106{
3107 dhd_bus_t *bus = dhdp->bus;
3108 dhd_timeout_t tmo;
3109 uint retries = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003110 u8 ready, enable;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003111 int err, ret = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003112 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003113
3114 DHD_TRACE(("%s: Enter\n", __func__));
3115
3116 ASSERT(bus->dhd);
3117 if (!bus->dhd)
3118 return 0;
3119
3120 if (enforce_mutex)
3121 dhd_os_sdlock(bus->dhd);
3122
3123 /* Make sure backplane clock is on, needed to generate F2 interrupt */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003124 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003125 if (bus->clkstate != CLK_AVAIL)
3126 goto exit;
3127
3128 /* Force clocks on backplane to be sure F2 interrupt propagates */
3129 saveclk =
3130 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3131 &err);
3132 if (!err) {
3133 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3134 (saveclk | SBSDIO_FORCE_HT), &err);
3135 }
3136 if (err) {
3137 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
3138 __func__, err));
3139 goto exit;
3140 }
3141
3142 /* Enable function 2 (frame transfers) */
3143 W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT),
3144 &bus->regs->tosbmailboxdata, retries);
3145 enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
3146
Franky Lin0df46042011-06-01 13:45:40 +02003147 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx, enable, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003148
3149 /* Give the dongle some time to do its thing and set IOR2 */
3150 dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000);
3151
3152 ready = 0;
3153 while (ready != enable && !dhd_timeout_expired(&tmo))
3154 ready =
Franky Lin0df46042011-06-01 13:45:40 +02003155 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IORx,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003156 NULL);
3157
3158 DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
3159 __func__, enable, ready, tmo.elapsed));
3160
3161 /* If F2 successfully enabled, set core and enable interrupts */
3162 if (ready == enable) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003163 /* Set up the interrupt mask and enable interrupts */
3164 bus->hostintmask = HOSTINTMASK;
Franky Linc05df632011-04-25 19:34:07 -07003165 W_SDREG(bus->hostintmask,
3166 (unsigned int *)CORE_BUS_REG(bus->ci->buscorebase,
3167 hostintmask), retries);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003168
3169 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003170 (u8) watermark, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003171
3172 /* Set bus state according to enable result */
3173 dhdp->busstate = DHD_BUS_DATA;
3174
3175 /* bcmsdh_intr_unmask(bus->sdh); */
3176
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003177 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003178 if (bus->intr) {
3179 DHD_INTR(("%s: enable SDIO device interrupts\n",
3180 __func__));
3181 bcmsdh_intr_enable(bus->sdh);
3182 } else {
3183 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
3184 bcmsdh_intr_disable(bus->sdh);
3185 }
3186
3187 }
3188
3189 else {
3190 /* Disable F2 again */
3191 enable = SDIO_FUNC_ENABLE_1;
Franky Lin0df46042011-06-01 13:45:40 +02003192 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx, enable,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003193 NULL);
3194 }
3195
3196 /* Restore previous clock setting */
3197 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3198 saveclk, &err);
3199
3200 /* If we didn't come up, turn off backplane clock */
3201 if (dhdp->busstate != DHD_BUS_DATA)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003202 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003203
3204exit:
3205 if (enforce_mutex)
3206 dhd_os_sdunlock(bus->dhd);
3207
3208 return ret;
3209}
3210
3211static void dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
3212{
3213 bcmsdh_info_t *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02003214 struct sdpcmd_regs *regs = bus->regs;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003215 uint retries = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003216 u16 lastrbc;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003217 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003218 int err;
3219
3220 DHD_ERROR(("%s: %sterminate frame%s\n", __func__,
3221 (abort ? "abort command, " : ""),
3222 (rtx ? ", send NAK" : "")));
3223
3224 if (abort)
3225 bcmsdh_abort(sdh, SDIO_FUNC_2);
3226
3227 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM,
3228 &err);
3229 bus->f1regdata++;
3230
3231 /* Wait until the packet has been flushed (device/FIFO stable) */
3232 for (lastrbc = retries = 0xffff; retries > 0; retries--) {
3233 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI,
3234 NULL);
3235 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO,
3236 NULL);
3237 bus->f1regdata += 2;
3238
3239 if ((hi == 0) && (lo == 0))
3240 break;
3241
3242 if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
3243 DHD_ERROR(("%s: count growing: last 0x%04x now "
3244 "0x%04x\n",
3245 __func__, lastrbc, ((hi << 8) + lo)));
3246 }
3247 lastrbc = (hi << 8) + lo;
3248 }
3249
3250 if (!retries) {
3251 DHD_ERROR(("%s: count never zeroed: last 0x%04x\n",
3252 __func__, lastrbc));
3253 } else {
3254 DHD_INFO(("%s: flush took %d iterations\n", __func__,
3255 (0xffff - retries)));
3256 }
3257
3258 if (rtx) {
3259 bus->rxrtx++;
3260 W_SDREG(SMB_NAK, &regs->tosbmailbox, retries);
3261 bus->f1regdata++;
3262 if (retries <= retry_limit)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003263 bus->rxskip = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003264 }
3265
3266 /* Clear partial in any case */
3267 bus->nextlen = 0;
3268
3269 /* If we can't reach the device, signal failure */
3270 if (err || bcmsdh_regfail(sdh))
3271 bus->dhd->busstate = DHD_BUS_DOWN;
3272}
3273
3274static void
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003275dhdsdio_read_control(dhd_bus_t *bus, u8 *hdr, uint len, uint doff)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003276{
3277 bcmsdh_info_t *sdh = bus->sdh;
3278 uint rdlen, pad;
3279
3280 int sdret;
3281
3282 DHD_TRACE(("%s: Enter\n", __func__));
3283
3284 /* Control data already received in aligned rxctl */
3285 if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
3286 goto gotpkt;
3287
3288 ASSERT(bus->rxbuf);
3289 /* Set rxctl for frame (w/optional alignment) */
3290 bus->rxctl = bus->rxbuf;
3291 if (dhd_alignctl) {
3292 bus->rxctl += firstread;
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07003293 pad = ((unsigned long)bus->rxctl % DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003294 if (pad)
3295 bus->rxctl += (DHD_SDALIGN - pad);
3296 bus->rxctl -= firstread;
3297 }
3298 ASSERT(bus->rxctl >= bus->rxbuf);
3299
3300 /* Copy the already-read portion over */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003301 memcpy(bus->rxctl, hdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003302 if (len <= firstread)
3303 goto gotpkt;
3304
3305 /* Copy the full data pkt in gSPI case and process ioctl. */
3306 if (bus->bus == SPI_BUS) {
Stanislav Fomichev02160692011-02-15 01:05:10 +03003307 memcpy(bus->rxctl, hdr, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003308 goto gotpkt;
3309 }
3310
3311 /* Raise rdlen to next SDIO block to avoid tail command */
3312 rdlen = len - firstread;
3313 if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
3314 pad = bus->blocksize - (rdlen % bus->blocksize);
3315 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
3316 ((len + pad) < bus->dhd->maxctl))
3317 rdlen += pad;
3318 } else if (rdlen % DHD_SDALIGN) {
3319 rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
3320 }
3321
3322 /* Satisfy length-alignment requirements */
3323 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003324 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003325
3326 /* Drop if the read is too big or it exceeds our maximum */
3327 if ((rdlen + firstread) > bus->dhd->maxctl) {
3328 DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n",
3329 __func__, rdlen, bus->dhd->maxctl));
3330 bus->dhd->rx_errors++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003331 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003332 goto done;
3333 }
3334
3335 if ((len - doff) > bus->dhd->maxctl) {
3336 DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds "
3337 "%d-byte limit\n",
3338 __func__, len, (len - doff), bus->dhd->maxctl));
3339 bus->dhd->rx_errors++;
3340 bus->rx_toolong++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003341 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003342 goto done;
3343 }
3344
3345 /* Read remainder of frame body into the rxctl buffer */
Grant Grundler4b455e02011-05-04 09:59:47 -07003346 sdret = bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
3347 F2SYNC, (bus->rxctl + firstread), rdlen,
3348 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003349 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003350 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003351
3352 /* Control frame failures need retransmission */
3353 if (sdret < 0) {
3354 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3355 __func__, rdlen, sdret));
3356 bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003357 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003358 goto done;
3359 }
3360
3361gotpkt:
3362
3363#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003364 if (DHD_BYTES_ON() && DHD_CTL_ON()) {
3365 printk(KERN_DEBUG "RxCtrl:\n");
3366 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, bus->rxctl, len);
3367 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003368#endif
3369
3370 /* Point to valid data and indicate its length */
3371 bus->rxctl += doff;
3372 bus->rxlen = len - doff;
3373
3374done:
3375 /* Awake any waiters */
3376 dhd_os_ioctl_resp_wake(bus->dhd);
3377}
3378
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003379static u8 dhdsdio_rxglom(dhd_bus_t *bus, u8 rxseq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003380{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003381 u16 dlen, totlen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003382 u8 *dptr, num = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003383
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003384 u16 sublen, check;
Arend van Sprielc26b1372010-11-23 14:06:23 +01003385 struct sk_buff *pfirst, *plast, *pnext, *save_pfirst;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003386
3387 int errcode;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003388 u8 chan, seq, doff, sfdoff;
3389 u8 txmax;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003390
3391 int ifidx = 0;
3392 bool usechain = bus->use_rxchain;
3393
3394 /* If packets, issue read(s) and send up packet chain */
3395 /* Return sequence numbers consumed? */
3396
3397 DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd,
3398 bus->glom));
3399
3400 /* If there's a descriptor, generate the packet chain */
3401 if (bus->glomd) {
3402 dhd_os_sdlock_rxq(bus->dhd);
3403
3404 pfirst = plast = pnext = NULL;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003405 dlen = (u16) (bus->glomd->len);
3406 dptr = bus->glomd->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003407 if (!dlen || (dlen & 1)) {
3408 DHD_ERROR(("%s: bad glomd len(%d), ignore descriptor\n",
3409 __func__, dlen));
3410 dlen = 0;
3411 }
3412
3413 for (totlen = num = 0; dlen; num++) {
3414 /* Get (and move past) next length */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003415 sublen = get_unaligned_le16(dptr);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003416 dlen -= sizeof(u16);
3417 dptr += sizeof(u16);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003418 if ((sublen < SDPCM_HDRLEN) ||
3419 ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
3420 DHD_ERROR(("%s: descriptor len %d bad: %d\n",
3421 __func__, num, sublen));
3422 pnext = NULL;
3423 break;
3424 }
3425 if (sublen % DHD_SDALIGN) {
3426 DHD_ERROR(("%s: sublen %d not multiple of %d\n",
3427 __func__, sublen, DHD_SDALIGN));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003428 usechain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003429 }
3430 totlen += sublen;
3431
3432 /* For last frame, adjust read len so total
3433 is a block multiple */
3434 if (!dlen) {
3435 sublen +=
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003436 (roundup(totlen, bus->blocksize) - totlen);
3437 totlen = roundup(totlen, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003438 }
3439
3440 /* Allocate/chain packet for next subframe */
Roland Vossen67ad48b2011-06-01 13:45:51 +02003441 pnext = brcmu_pkt_buf_get_skb(sublen + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003442 if (pnext == NULL) {
Henry Ptasinskib33f0e22011-05-10 22:25:29 +02003443 DHD_ERROR(("%s: bcm_pkt_buf_get_skb failed, "
3444 "num %d len %d\n", __func__,
3445 num, sublen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003446 break;
3447 }
Arend van Spriel54991ad2010-11-23 14:06:24 +01003448 ASSERT(!(pnext->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003449 if (!pfirst) {
3450 ASSERT(!plast);
3451 pfirst = plast = pnext;
3452 } else {
3453 ASSERT(plast);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003454 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003455 plast = pnext;
3456 }
3457
3458 /* Adhere to start alignment requirements */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01003459 PKTALIGN(pnext, sublen, DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003460 }
3461
3462 /* If all allocations succeeded, save packet chain
3463 in bus structure */
3464 if (pnext) {
3465 DHD_GLOM(("%s: allocated %d-byte packet chain for %d "
3466 "subframes\n", __func__, totlen, num));
3467 if (DHD_GLOM_ON() && bus->nextlen) {
3468 if (totlen != bus->nextlen) {
3469 DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " "rxseq %d\n",
3470 __func__, bus->nextlen,
3471 totlen, rxseq));
3472 }
3473 }
3474 bus->glom = pfirst;
3475 pfirst = pnext = NULL;
3476 } else {
3477 if (pfirst)
Roland Vossen67ad48b2011-06-01 13:45:51 +02003478 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003479 bus->glom = NULL;
3480 num = 0;
3481 }
3482
3483 /* Done with descriptor packet */
Roland Vossen67ad48b2011-06-01 13:45:51 +02003484 brcmu_pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003485 bus->glomd = NULL;
3486 bus->nextlen = 0;
3487
3488 dhd_os_sdunlock_rxq(bus->dhd);
3489 }
3490
3491 /* Ok -- either we just generated a packet chain,
3492 or had one from before */
3493 if (bus->glom) {
3494 if (DHD_GLOM_ON()) {
3495 DHD_GLOM(("%s: try superframe read, packet chain:\n",
3496 __func__));
Arend van Spriel54991ad2010-11-23 14:06:24 +01003497 for (pnext = bus->glom; pnext; pnext = pnext->next) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003498 DHD_GLOM((" %p: %p len 0x%04x (%d)\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003499 pnext, (u8 *) (pnext->data),
3500 pnext->len, pnext->len));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003501 }
3502 }
3503
3504 pfirst = bus->glom;
Roland Vossen67ad48b2011-06-01 13:45:51 +02003505 dlen = (u16) brcmu_pkttotlen(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003506
3507 /* Do an SDIO read for the superframe. Configurable iovar to
3508 * read directly into the chained packet, or allocate a large
3509 * packet and and copy into the chain.
3510 */
3511 if (usechain) {
Grant Grundler4b455e02011-05-04 09:59:47 -07003512 errcode = bcmsdh_recv_buf(bus,
3513 bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
3514 F2SYNC, (u8 *) pfirst->data, dlen,
3515 pfirst, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003516 } else if (bus->dataptr) {
Grant Grundler4b455e02011-05-04 09:59:47 -07003517 errcode = bcmsdh_recv_buf(bus,
3518 bcmsdh_cur_sbwad(bus->sdh), SDIO_FUNC_2,
3519 F2SYNC, bus->dataptr, dlen,
3520 NULL, NULL, NULL);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003521 sublen = (u16) brcmu_pktfrombuf(pfirst, 0, dlen,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003522 bus->dataptr);
3523 if (sublen != dlen) {
3524 DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n",
3525 __func__, dlen, sublen));
3526 errcode = -1;
3527 }
3528 pnext = NULL;
3529 } else {
3530 DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
3531 dlen));
3532 errcode = -1;
3533 }
3534 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003535 ASSERT(errcode != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003536
3537 /* On failure, kill the superframe, allow a couple retries */
3538 if (errcode < 0) {
3539 DHD_ERROR(("%s: glom read of %d bytes failed: %d\n",
3540 __func__, dlen, errcode));
3541 bus->dhd->rx_errors++;
3542
3543 if (bus->glomerr++ < 3) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003544 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003545 } else {
3546 bus->glomerr = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003547 dhdsdio_rxfail(bus, true, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003548 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003549 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003550 dhd_os_sdunlock_rxq(bus->dhd);
3551 bus->rxglomfail++;
3552 bus->glom = NULL;
3553 }
3554 return 0;
3555 }
3556#ifdef DHD_DEBUG
3557 if (DHD_GLOM_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02003558 printk(KERN_DEBUG "SUPERFRAME:\n");
3559 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3560 pfirst->data, min_t(int, pfirst->len, 48));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003561 }
3562#endif
3563
3564 /* Validate the superframe header */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003565 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003566 sublen = get_unaligned_le16(dptr);
3567 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003568
3569 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3570 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3571 bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
3572 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
3573 DHD_INFO(("%s: nextlen too large (%d) seq %d\n",
3574 __func__, bus->nextlen, seq));
3575 bus->nextlen = 0;
3576 }
3577 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3578 txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3579
3580 errcode = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003581 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003582 DHD_ERROR(("%s (superframe): HW hdr error: len/check "
3583 "0x%04x/0x%04x\n", __func__, sublen, check));
3584 errcode = -1;
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003585 } else if (roundup(sublen, bus->blocksize) != dlen) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003586 DHD_ERROR(("%s (superframe): len 0x%04x, rounded "
3587 "0x%04x, expect 0x%04x\n",
3588 __func__, sublen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003589 roundup(sublen, bus->blocksize), dlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003590 errcode = -1;
3591 } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
3592 SDPCM_GLOM_CHANNEL) {
3593 DHD_ERROR(("%s (superframe): bad channel %d\n",
3594 __func__,
3595 SDPCM_PACKET_CHANNEL(&dptr
3596 [SDPCM_FRAMETAG_LEN])));
3597 errcode = -1;
3598 } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
3599 DHD_ERROR(("%s (superframe): got second descriptor?\n",
3600 __func__));
3601 errcode = -1;
3602 } else if ((doff < SDPCM_HDRLEN) ||
Arend van Spriel54991ad2010-11-23 14:06:24 +01003603 (doff > (pfirst->len - SDPCM_HDRLEN))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003604 DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d "
3605 "pkt %d min %d\n",
3606 __func__, doff, sublen,
Arend van Spriel54991ad2010-11-23 14:06:24 +01003607 pfirst->len, SDPCM_HDRLEN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003608 errcode = -1;
3609 }
3610
3611 /* Check sequence number of superframe SW header */
3612 if (rxseq != seq) {
3613 DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
3614 __func__, seq, rxseq));
3615 bus->rx_badseq++;
3616 rxseq = seq;
3617 }
3618
3619 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003620 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003621 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
3622 __func__, txmax, bus->tx_seq));
3623 txmax = bus->tx_seq + 2;
3624 }
3625 bus->tx_max = txmax;
3626
3627 /* Remove superframe header, remember offset */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003628 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003629 sfdoff = doff;
3630
3631 /* Validate all the subframe headers */
3632 for (num = 0, pnext = pfirst; pnext && !errcode;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003633 num++, pnext = pnext->next) {
3634 dptr = (u8 *) (pnext->data);
3635 dlen = (u16) (pnext->len);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003636 sublen = get_unaligned_le16(dptr);
3637 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003638 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3639 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3640#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003641 if (DHD_GLOM_ON()) {
3642 printk(KERN_DEBUG "subframe:\n");
3643 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3644 dptr, 32);
3645 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003646#endif
3647
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003648 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003649 DHD_ERROR(("%s (subframe %d): HW hdr error: "
3650 "len/check 0x%04x/0x%04x\n",
3651 __func__, num, sublen, check));
3652 errcode = -1;
3653 } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
3654 DHD_ERROR(("%s (subframe %d): length mismatch: "
3655 "len 0x%04x, expect 0x%04x\n",
3656 __func__, num, sublen, dlen));
3657 errcode = -1;
3658 } else if ((chan != SDPCM_DATA_CHANNEL) &&
3659 (chan != SDPCM_EVENT_CHANNEL)) {
3660 DHD_ERROR(("%s (subframe %d): bad channel %d\n",
3661 __func__, num, chan));
3662 errcode = -1;
3663 } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
3664 DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n",
3665 __func__, num, doff, sublen,
3666 SDPCM_HDRLEN));
3667 errcode = -1;
3668 }
3669 }
3670
3671 if (errcode) {
3672 /* Terminate frame on error, request
3673 a couple retries */
3674 if (bus->glomerr++ < 3) {
3675 /* Restore superframe header space */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003676 skb_push(pfirst, sfdoff);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003677 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003678 } else {
3679 bus->glomerr = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003680 dhdsdio_rxfail(bus, true, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003681 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003682 brcmu_pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003683 dhd_os_sdunlock_rxq(bus->dhd);
3684 bus->rxglomfail++;
3685 bus->glom = NULL;
3686 }
3687 bus->nextlen = 0;
3688 return 0;
3689 }
3690
3691 /* Basic SD framing looks ok - process each packet (header) */
3692 save_pfirst = pfirst;
3693 bus->glom = NULL;
3694 plast = NULL;
3695
3696 dhd_os_sdlock_rxq(bus->dhd);
3697 for (num = 0; pfirst; rxseq++, pfirst = pnext) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003698 pnext = pfirst->next;
3699 pfirst->next = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003700
Arend van Spriel54991ad2010-11-23 14:06:24 +01003701 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003702 sublen = get_unaligned_le16(dptr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003703 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3704 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3705 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3706
3707 DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d "
3708 "chan %d seq %d\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003709 __func__, num, pfirst, pfirst->data,
3710 pfirst->len, sublen, chan, seq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003711
3712 ASSERT((chan == SDPCM_DATA_CHANNEL)
3713 || (chan == SDPCM_EVENT_CHANNEL));
3714
3715 if (rxseq != seq) {
3716 DHD_GLOM(("%s: rx_seq %d, expected %d\n",
3717 __func__, seq, rxseq));
3718 bus->rx_badseq++;
3719 rxseq = seq;
3720 }
3721#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02003722 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
3723 printk(KERN_DEBUG "Rx Subframe Data:\n");
3724 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3725 dptr, dlen);
3726 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003727#endif
3728
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01003729 __skb_trim(pfirst, sublen);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003730 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003731
Arend van Spriel54991ad2010-11-23 14:06:24 +01003732 if (pfirst->len == 0) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02003733 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003734 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003735 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003736 } else {
3737 ASSERT(save_pfirst == pfirst);
3738 save_pfirst = pnext;
3739 }
3740 continue;
3741 } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) !=
3742 0) {
3743 DHD_ERROR(("%s: rx protocol error\n",
3744 __func__));
3745 bus->dhd->rx_errors++;
Roland Vossen67ad48b2011-06-01 13:45:51 +02003746 brcmu_pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003747 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003748 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003749 } else {
3750 ASSERT(save_pfirst == pfirst);
3751 save_pfirst = pnext;
3752 }
3753 continue;
3754 }
3755
3756 /* this packet will go up, link back into
3757 chain and count it */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003758 pfirst->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003759 plast = pfirst;
3760 num++;
3761
3762#ifdef DHD_DEBUG
3763 if (DHD_GLOM_ON()) {
3764 DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) "
3765 "nxt/lnk %p/%p\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003766 __func__, num, pfirst, pfirst->data,
3767 pfirst->len, pfirst->next,
3768 pfirst->prev));
Arend van Spriel34227312011-05-10 22:25:32 +02003769 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
3770 pfirst->data,
3771 min_t(int, pfirst->len, 32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003772 }
3773#endif /* DHD_DEBUG */
3774 }
3775 dhd_os_sdunlock_rxq(bus->dhd);
3776 if (num) {
3777 dhd_os_sdunlock(bus->dhd);
3778 dhd_rx_frame(bus->dhd, ifidx, save_pfirst, num);
3779 dhd_os_sdlock(bus->dhd);
3780 }
3781
3782 bus->rxglomframes++;
3783 bus->rxglompkts += num;
3784 }
3785 return num;
3786}
3787
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003788/* Return true if there may be more frames to read */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003789static uint dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
3790{
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003791 bcmsdh_info_t *sdh = bus->sdh;
3792
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003793 u16 len, check; /* Extracted hardware header fields */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003794 u8 chan, seq, doff; /* Extracted software header fields */
3795 u8 fcbits; /* Extracted fcbits from software header */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003796
Arend van Sprielc26b1372010-11-23 14:06:23 +01003797 struct sk_buff *pkt; /* Packet for event or data frames */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003798 u16 pad; /* Number of pad bytes to read */
3799 u16 rdlen; /* Total number of bytes to read */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003800 u8 rxseq; /* Next sequence number to expect */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003801 uint rxleft = 0; /* Remaining number of frames allowed */
3802 int sdret; /* Return code from bcmsdh calls */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003803 u8 txmax; /* Maximum tx sequence offered */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003804 bool len_consistent; /* Result of comparing readahead len and
3805 len from hw-hdr */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003806 u8 *rxbuf;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003807 int ifidx = 0;
3808 uint rxcount = 0; /* Total frames read */
3809
3810#if defined(DHD_DEBUG) || defined(SDTEST)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003811 bool sdtest = false; /* To limit message spew from test mode */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003812#endif
3813
3814 DHD_TRACE(("%s: Enter\n", __func__));
3815
3816 ASSERT(maxframes);
3817
3818#ifdef SDTEST
3819 /* Allow pktgen to override maxframes */
3820 if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) {
3821 maxframes = bus->pktgen_count;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003822 sdtest = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003823 }
3824#endif
3825
3826 /* Not finished unless we encounter no more frames indication */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003827 *finished = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003828
3829 for (rxseq = bus->rx_seq, rxleft = maxframes;
3830 !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN;
3831 rxseq++, rxleft--) {
3832
3833 /* Handle glomming separately */
3834 if (bus->glom || bus->glomd) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003835 u8 cnt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003836 DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
3837 __func__, bus->glomd, bus->glom));
3838 cnt = dhdsdio_rxglom(bus, rxseq);
3839 DHD_GLOM(("%s: rxglom returned %d\n", __func__, cnt));
3840 rxseq += cnt - 1;
3841 rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
3842 continue;
3843 }
3844
3845 /* Try doing single read if we can */
3846 if (dhd_readahead && bus->nextlen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003847 u16 nextlen = bus->nextlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003848 bus->nextlen = 0;
3849
3850 if (bus->bus == SPI_BUS) {
3851 rdlen = len = nextlen;
3852 } else {
3853 rdlen = len = nextlen << 4;
3854
3855 /* Pad read to blocksize for efficiency */
3856 if (bus->roundup && bus->blocksize
3857 && (rdlen > bus->blocksize)) {
3858 pad =
3859 bus->blocksize -
3860 (rdlen % bus->blocksize);
3861 if ((pad <= bus->roundup)
3862 && (pad < bus->blocksize)
3863 && ((rdlen + pad + firstread) <
3864 MAX_RX_DATASZ))
3865 rdlen += pad;
3866 } else if (rdlen % DHD_SDALIGN) {
3867 rdlen +=
3868 DHD_SDALIGN - (rdlen % DHD_SDALIGN);
3869 }
3870 }
3871
3872 /* We use bus->rxctl buffer in WinXP for initial
3873 * control pkt receives.
3874 * Later we use buffer-poll for data as well
3875 * as control packets.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003876 * This is required because dhd receives full
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003877 * frame in gSPI unlike SDIO.
3878 * After the frame is received we have to
3879 * distinguish whether it is data
3880 * or non-data frame.
3881 */
3882 /* Allocate a packet buffer */
3883 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02003884 pkt = brcmu_pkt_buf_get_skb(rdlen + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003885 if (!pkt) {
3886 if (bus->bus == SPI_BUS) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003887 bus->usebufpool = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003888 bus->rxctl = bus->rxbuf;
3889 if (dhd_alignctl) {
3890 bus->rxctl += firstread;
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07003891 pad = ((unsigned long)bus->rxctl %
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003892 DHD_SDALIGN);
3893 if (pad)
3894 bus->rxctl +=
3895 (DHD_SDALIGN - pad);
3896 bus->rxctl -= firstread;
3897 }
3898 ASSERT(bus->rxctl >= bus->rxbuf);
3899 rxbuf = bus->rxctl;
3900 /* Read the entire frame */
Grant Grundler4b455e02011-05-04 09:59:47 -07003901 sdret = bcmsdh_recv_buf(bus,
3902 bcmsdh_cur_sbwad(sdh),
3903 SDIO_FUNC_2, F2SYNC,
3904 rxbuf, rdlen,
3905 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003906 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003907 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003908
3909 /* Control frame failures need
3910 retransmission */
3911 if (sdret < 0) {
3912 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3913 __func__,
3914 rdlen, sdret));
3915 /* dhd.rx_ctlerrs is higher */
3916 bus->rxc_errors++;
3917 dhd_os_sdunlock_rxq(bus->dhd);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003918 dhdsdio_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003919 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003920 SPI_BUS) ? false
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003921 : true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003922 continue;
3923 }
3924 } else {
3925 /* Give up on data,
3926 request rtx of events */
Arend van Sprielcda64a52011-05-10 22:25:33 +02003927 DHD_ERROR(("%s (nextlen): "
Roland Vossen67ad48b2011-06-01 13:45:51 +02003928 "brcmu_pkt_buf_get_skb "
3929 "failed:"
Arend van Sprielcda64a52011-05-10 22:25:33 +02003930 " len %d rdlen %d expected"
3931 " rxseq %d\n", __func__,
3932 len, rdlen, rxseq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003933 /* Just go try again w/normal
3934 header read */
3935 dhd_os_sdunlock_rxq(bus->dhd);
3936 continue;
3937 }
3938 } else {
3939 if (bus->bus == SPI_BUS)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003940 bus->usebufpool = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003941
Arend van Spriel54991ad2010-11-23 14:06:24 +01003942 ASSERT(!(pkt->prev));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01003943 PKTALIGN(pkt, rdlen, DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003944 rxbuf = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003945 /* Read the entire frame */
Grant Grundler4b455e02011-05-04 09:59:47 -07003946 sdret = bcmsdh_recv_buf(bus,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003947 bcmsdh_cur_sbwad(sdh),
3948 SDIO_FUNC_2, F2SYNC,
Grant Grundler4b455e02011-05-04 09:59:47 -07003949 rxbuf, rdlen,
3950 pkt, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003951 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02003952 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003953
3954 if (sdret < 0) {
3955 DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n",
3956 __func__, rdlen, sdret));
Roland Vossen67ad48b2011-06-01 13:45:51 +02003957 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003958 bus->dhd->rx_errors++;
3959 dhd_os_sdunlock_rxq(bus->dhd);
3960 /* Force retry w/normal header read.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003961 * Don't attempt NAK for
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003962 * gSPI
3963 */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003964 dhdsdio_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003965 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003966 SPI_BUS) ? false :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003967 true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003968 continue;
3969 }
3970 }
3971 dhd_os_sdunlock_rxq(bus->dhd);
3972
3973 /* Now check the header */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003974 memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003975
3976 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003977 len = get_unaligned_le16(bus->rxhdr);
3978 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003979
3980 /* All zeros means readahead info was bad */
3981 if (!(len | check)) {
3982 DHD_INFO(("%s (nextlen): read zeros in HW "
3983 "header???\n", __func__));
Grant Grundler4b455e02011-05-04 09:59:47 -07003984 dhdsdio_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003985 continue;
3986 }
3987
3988 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003989 if ((u16)~(len ^ check)) {
Grant Grundler4b455e02011-05-04 09:59:47 -07003990 DHD_ERROR(("%s (nextlen): HW hdr error:"
3991 " nextlen/len/check"
3992 " 0x%04x/0x%04x/0x%04x\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003993 __func__, nextlen, len, check));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003994 bus->rx_badhdr++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003995 dhdsdio_rxfail(bus, false, false);
Grant Grundler4b455e02011-05-04 09:59:47 -07003996 dhdsdio_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003997 continue;
3998 }
3999
4000 /* Validate frame length */
4001 if (len < SDPCM_HDRLEN) {
4002 DHD_ERROR(("%s (nextlen): HW hdr length "
4003 "invalid: %d\n", __func__, len));
Grant Grundler4b455e02011-05-04 09:59:47 -07004004 dhdsdio_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004005 continue;
4006 }
4007
4008 /* Check for consistency withreadahead info */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07004009 len_consistent = (nextlen != (roundup(len, 16) >> 4));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004010 if (len_consistent) {
4011 /* Mismatch, force retry w/normal
4012 header (may be >4K) */
Grant Grundler4b455e02011-05-04 09:59:47 -07004013 DHD_ERROR(("%s (nextlen): mismatch, "
4014 "nextlen %d len %d rnd %d; "
4015 "expected rxseq %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004016 __func__, nextlen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07004017 len, roundup(len, 16), rxseq));
Grant Grundler4b455e02011-05-04 09:59:47 -07004018 dhdsdio_rxfail(bus, true, (bus->bus != SPI_BUS));
4019 dhdsdio_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004020 continue;
4021 }
4022
4023 /* Extract software header fields */
Grant Grundler4b455e02011-05-04 09:59:47 -07004024 chan = SDPCM_PACKET_CHANNEL(
4025 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4026 seq = SDPCM_PACKET_SEQUENCE(
4027 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4028 doff = SDPCM_DOFFSET_VALUE(
4029 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4030 txmax = SDPCM_WINDOW_VALUE(
4031 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004032
4033 bus->nextlen =
4034 bus->rxhdr[SDPCM_FRAMETAG_LEN +
4035 SDPCM_NEXTLEN_OFFSET];
4036 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
4037 DHD_INFO(("%s (nextlen): got frame w/nextlen too large" " (%d), seq %d\n",
4038 __func__, bus->nextlen, seq));
4039 bus->nextlen = 0;
4040 }
4041
4042 bus->dhd->rx_readahead_cnt++;
Grant Grundler4b455e02011-05-04 09:59:47 -07004043
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004044 /* Handle Flow Control */
Grant Grundler4b455e02011-05-04 09:59:47 -07004045 fcbits = SDPCM_FCMASK_VALUE(
4046 &bus->rxhdr[SDPCM_FRAMETAG_LEN]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004047
Grant Grundler4b455e02011-05-04 09:59:47 -07004048 if (bus->flowcontrol != fcbits) {
4049 if (~bus->flowcontrol & fcbits)
4050 bus->fc_xoff++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004051
Grant Grundler4b455e02011-05-04 09:59:47 -07004052 if (bus->flowcontrol & ~fcbits)
4053 bus->fc_xon++;
4054
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004055 bus->fc_rcvd++;
4056 bus->flowcontrol = fcbits;
4057 }
4058
4059 /* Check and update sequence number */
4060 if (rxseq != seq) {
4061 DHD_INFO(("%s (nextlen): rx_seq %d, expected "
4062 "%d\n", __func__, seq, rxseq));
4063 bus->rx_badseq++;
4064 rxseq = seq;
4065 }
4066
4067 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004068 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004069 DHD_ERROR(("%s: got unlikely tx max %d with "
4070 "tx_seq %d\n",
4071 __func__, txmax, bus->tx_seq));
4072 txmax = bus->tx_seq + 2;
4073 }
4074 bus->tx_max = txmax;
4075
4076#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004077 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
4078 printk(KERN_DEBUG "Rx Data:\n");
4079 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4080 rxbuf, len);
4081 } else if (DHD_HDRS_ON()) {
4082 printk(KERN_DEBUG "RxHdr:\n");
4083 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4084 bus->rxhdr, SDPCM_HDRLEN);
4085 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004086#endif
4087
4088 if (chan == SDPCM_CONTROL_CHANNEL) {
4089 if (bus->bus == SPI_BUS) {
4090 dhdsdio_read_control(bus, rxbuf, len,
4091 doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004092 } else {
4093 DHD_ERROR(("%s (nextlen): readahead on control" " packet %d?\n",
4094 __func__, seq));
4095 /* Force retry w/normal header read */
4096 bus->nextlen = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004097 dhdsdio_rxfail(bus, false, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004098 }
Grant Grundler4b455e02011-05-04 09:59:47 -07004099 dhdsdio_pktfree2(bus, pkt);
4100 continue;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004101 }
4102
4103 if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
4104 DHD_ERROR(("Received %d bytes on %d channel. Running out of " "rx pktbuf's or not yet malloced.\n",
4105 len, chan));
4106 continue;
4107 }
4108
4109 /* Validate data offset */
4110 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
4111 DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n",
4112 __func__, doff, len, SDPCM_HDRLEN));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004113 dhdsdio_rxfail(bus, false, false);
Grant Grundler4b455e02011-05-04 09:59:47 -07004114 dhdsdio_pktfree2(bus, pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004115 continue;
4116 }
4117
4118 /* All done with this one -- now deliver the packet */
4119 goto deliver;
4120 }
4121 /* gSPI frames should not be handled in fractions */
4122 if (bus->bus == SPI_BUS)
4123 break;
4124
4125 /* Read frame header (hardware and software) */
Grant Grundler4b455e02011-05-04 09:59:47 -07004126 sdret = bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh),
4127 SDIO_FUNC_2, F2SYNC, bus->rxhdr, firstread,
4128 NULL, NULL, NULL);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004129 bus->f2rxhdrs++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004130 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004131
4132 if (sdret < 0) {
4133 DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __func__,
4134 sdret));
4135 bus->rx_hdrfail++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004136 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004137 continue;
4138 }
4139#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004140 if (DHD_BYTES_ON() || DHD_HDRS_ON()) {
4141 printk(KERN_DEBUG "RxHdr:\n");
4142 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4143 bus->rxhdr, SDPCM_HDRLEN);
4144 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004145#endif
4146
4147 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03004148 len = get_unaligned_le16(bus->rxhdr);
4149 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004150
4151 /* All zeros means no more frames */
4152 if (!(len | check)) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004153 *finished = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004154 break;
4155 }
4156
4157 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004158 if ((u16) ~(len ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004159 DHD_ERROR(("%s: HW hdr err: len/check 0x%04x/0x%04x\n",
4160 __func__, len, check));
4161 bus->rx_badhdr++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004162 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004163 continue;
4164 }
4165
4166 /* Validate frame length */
4167 if (len < SDPCM_HDRLEN) {
4168 DHD_ERROR(("%s: HW hdr length invalid: %d\n",
4169 __func__, len));
4170 continue;
4171 }
4172
4173 /* Extract software header fields */
4174 chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4175 seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4176 doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4177 txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4178
4179 /* Validate data offset */
4180 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
4181 DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d "
4182 "seq %d\n",
4183 __func__, doff, len, SDPCM_HDRLEN, seq));
4184 bus->rx_badhdr++;
4185 ASSERT(0);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004186 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004187 continue;
4188 }
4189
4190 /* Save the readahead length if there is one */
4191 bus->nextlen =
4192 bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
4193 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
4194 DHD_INFO(("%s (nextlen): got frame w/nextlen too large "
4195 "(%d), seq %d\n",
4196 __func__, bus->nextlen, seq));
4197 bus->nextlen = 0;
4198 }
4199
4200 /* Handle Flow Control */
4201 fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4202
Grant Grundler4b455e02011-05-04 09:59:47 -07004203 if (bus->flowcontrol != fcbits) {
4204 if (~bus->flowcontrol & fcbits)
4205 bus->fc_xoff++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004206
Grant Grundler4b455e02011-05-04 09:59:47 -07004207 if (bus->flowcontrol & ~fcbits)
4208 bus->fc_xon++;
4209
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004210 bus->fc_rcvd++;
4211 bus->flowcontrol = fcbits;
4212 }
4213
4214 /* Check and update sequence number */
4215 if (rxseq != seq) {
4216 DHD_INFO(("%s: rx_seq %d, expected %d\n", __func__,
4217 seq, rxseq));
4218 bus->rx_badseq++;
4219 rxseq = seq;
4220 }
4221
4222 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004223 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004224 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
4225 __func__, txmax, bus->tx_seq));
4226 txmax = bus->tx_seq + 2;
4227 }
4228 bus->tx_max = txmax;
4229
4230 /* Call a separate function for control frames */
4231 if (chan == SDPCM_CONTROL_CHANNEL) {
4232 dhdsdio_read_control(bus, bus->rxhdr, len, doff);
4233 continue;
4234 }
4235
4236 ASSERT((chan == SDPCM_DATA_CHANNEL)
4237 || (chan == SDPCM_EVENT_CHANNEL)
4238 || (chan == SDPCM_TEST_CHANNEL)
4239 || (chan == SDPCM_GLOM_CHANNEL));
4240
4241 /* Length to read */
4242 rdlen = (len > firstread) ? (len - firstread) : 0;
4243
4244 /* May pad read to blocksize for efficiency */
4245 if (bus->roundup && bus->blocksize &&
4246 (rdlen > bus->blocksize)) {
4247 pad = bus->blocksize - (rdlen % bus->blocksize);
4248 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
4249 ((rdlen + pad + firstread) < MAX_RX_DATASZ))
4250 rdlen += pad;
4251 } else if (rdlen % DHD_SDALIGN) {
4252 rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
4253 }
4254
4255 /* Satisfy length-alignment requirements */
4256 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07004257 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004258
4259 if ((rdlen + firstread) > MAX_RX_DATASZ) {
4260 /* Too long -- skip this frame */
4261 DHD_ERROR(("%s: too long: len %d rdlen %d\n",
4262 __func__, len, rdlen));
4263 bus->dhd->rx_errors++;
4264 bus->rx_toolong++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004265 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004266 continue;
4267 }
4268
4269 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02004270 pkt = brcmu_pkt_buf_get_skb(rdlen + firstread + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004271 if (!pkt) {
4272 /* Give up on data, request rtx of events */
Roland Vossen67ad48b2011-06-01 13:45:51 +02004273 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed: rdlen %d"
4274 " chan %d\n", __func__, rdlen, chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004275 bus->dhd->rx_dropped++;
4276 dhd_os_sdunlock_rxq(bus->dhd);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004277 dhdsdio_rxfail(bus, false, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004278 continue;
4279 }
4280 dhd_os_sdunlock_rxq(bus->dhd);
4281
Arend van Spriel54991ad2010-11-23 14:06:24 +01004282 ASSERT(!(pkt->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004283
4284 /* Leave room for what we already read, and align remainder */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004285 ASSERT(firstread < pkt->len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004286 skb_pull(pkt, firstread);
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004287 PKTALIGN(pkt, rdlen, DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004288
4289 /* Read the remaining frame data */
Grant Grundler4b455e02011-05-04 09:59:47 -07004290 sdret = bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
Arend van Spriel54991ad2010-11-23 14:06:24 +01004291 F2SYNC, ((u8 *) (pkt->data)), rdlen,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004292 pkt, NULL, NULL);
4293 bus->f2rxdata++;
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004294 ASSERT(sdret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004295
4296 if (sdret < 0) {
4297 DHD_ERROR(("%s: read %d %s bytes failed: %d\n",
4298 __func__, rdlen,
4299 ((chan ==
4300 SDPCM_EVENT_CHANNEL) ? "event" : ((chan ==
4301 SDPCM_DATA_CHANNEL)
4302 ? "data" : "test")),
4303 sdret));
4304 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02004305 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004306 dhd_os_sdunlock_rxq(bus->dhd);
4307 bus->dhd->rx_errors++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004308 dhdsdio_rxfail(bus, true, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004309 continue;
4310 }
4311
4312 /* Copy the already-read portion */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004313 skb_push(pkt, firstread);
Stanislav Fomichev02160692011-02-15 01:05:10 +03004314 memcpy(pkt->data, bus->rxhdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004315
4316#ifdef DHD_DEBUG
Arend van Spriel34227312011-05-10 22:25:32 +02004317 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
4318 printk(KERN_DEBUG "Rx Data:\n");
4319 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET,
4320 pkt->data, len);
4321 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004322#endif
4323
4324deliver:
4325 /* Save superframe descriptor and allocate packet frame */
4326 if (chan == SDPCM_GLOM_CHANNEL) {
4327 if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
4328 DHD_GLOM(("%s: glom descriptor, %d bytes:\n",
4329 __func__, len));
4330#ifdef DHD_DEBUG
4331 if (DHD_GLOM_ON()) {
Arend van Spriel34227312011-05-10 22:25:32 +02004332 printk(KERN_DEBUG "Glom Data:\n");
4333 print_hex_dump_bytes("",
4334 DUMP_PREFIX_OFFSET,
4335 pkt->data, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004336 }
4337#endif
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004338 __skb_trim(pkt, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004339 ASSERT(doff == SDPCM_HDRLEN);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004340 skb_pull(pkt, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004341 bus->glomd = pkt;
4342 } else {
4343 DHD_ERROR(("%s: glom superframe w/o "
4344 "descriptor!\n", __func__));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004345 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004346 }
4347 continue;
4348 }
4349
4350 /* Fill in packet len and prio, deliver upward */
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004351 __skb_trim(pkt, len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004352 skb_pull(pkt, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004353
4354#ifdef SDTEST
4355 /* Test channel packets are processed separately */
4356 if (chan == SDPCM_TEST_CHANNEL) {
4357 dhdsdio_testrcv(bus, pkt, seq);
4358 continue;
4359 }
4360#endif /* SDTEST */
4361
Arend van Spriel54991ad2010-11-23 14:06:24 +01004362 if (pkt->len == 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004363 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02004364 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004365 dhd_os_sdunlock_rxq(bus->dhd);
4366 continue;
4367 } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
4368 DHD_ERROR(("%s: rx protocol error\n", __func__));
4369 dhd_os_sdlock_rxq(bus->dhd);
Roland Vossen67ad48b2011-06-01 13:45:51 +02004370 brcmu_pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004371 dhd_os_sdunlock_rxq(bus->dhd);
4372 bus->dhd->rx_errors++;
4373 continue;
4374 }
4375
4376 /* Unlock during rx call */
4377 dhd_os_sdunlock(bus->dhd);
4378 dhd_rx_frame(bus->dhd, ifidx, pkt, 1);
4379 dhd_os_sdlock(bus->dhd);
4380 }
4381 rxcount = maxframes - rxleft;
4382#ifdef DHD_DEBUG
4383 /* Message if we hit the limit */
4384 if (!rxleft && !sdtest)
4385 DHD_DATA(("%s: hit rx limit of %d frames\n", __func__,
4386 maxframes));
4387 else
4388#endif /* DHD_DEBUG */
4389 DHD_DATA(("%s: processed %d frames\n", __func__, rxcount));
4390 /* Back off rxseq if awaiting rtx, update rx_seq */
4391 if (bus->rxskip)
4392 rxseq--;
4393 bus->rx_seq = rxseq;
4394
4395 return rxcount;
4396}
4397
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004398static u32 dhdsdio_hostmail(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004399{
Franky Lin597600a2011-06-01 13:45:39 +02004400 struct sdpcmd_regs *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004401 u32 intstatus = 0;
4402 u32 hmb_data;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004403 u8 fcbits;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004404 uint retries = 0;
4405
4406 DHD_TRACE(("%s: Enter\n", __func__));
4407
4408 /* Read mailbox data and ack that we did so */
4409 R_SDREG(hmb_data, &regs->tohostmailboxdata, retries);
4410 if (retries <= retry_limit)
4411 W_SDREG(SMB_INT_ACK, &regs->tosbmailbox, retries);
4412 bus->f1regdata += 2;
4413
4414 /* Dongle recomposed rx frames, accept them again */
4415 if (hmb_data & HMB_DATA_NAKHANDLED) {
4416 DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n",
4417 bus->rx_seq));
4418 if (!bus->rxskip)
4419 DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __func__));
4420
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004421 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004422 intstatus |= I_HMB_FRAME_IND;
4423 }
4424
4425 /*
4426 * DEVREADY does not occur with gSPI.
4427 */
4428 if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
4429 bus->sdpcm_ver =
4430 (hmb_data & HMB_DATA_VERSION_MASK) >>
4431 HMB_DATA_VERSION_SHIFT;
4432 if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
4433 DHD_ERROR(("Version mismatch, dongle reports %d, "
4434 "expecting %d\n",
4435 bus->sdpcm_ver, SDPCM_PROT_VERSION));
4436 else
4437 DHD_INFO(("Dongle ready, protocol version %d\n",
4438 bus->sdpcm_ver));
4439 }
4440
4441 /*
4442 * Flow Control has been moved into the RX headers and this out of band
Grant Grundler4b455e02011-05-04 09:59:47 -07004443 * method isn't used any more.
4444 * remaining backward compatible with older dongles.
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004445 */
4446 if (hmb_data & HMB_DATA_FC) {
Grant Grundler4b455e02011-05-04 09:59:47 -07004447 fcbits = (hmb_data & HMB_DATA_FCDATA_MASK) >>
4448 HMB_DATA_FCDATA_SHIFT;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004449
4450 if (fcbits & ~bus->flowcontrol)
4451 bus->fc_xoff++;
Grant Grundler4b455e02011-05-04 09:59:47 -07004452
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004453 if (bus->flowcontrol & ~fcbits)
4454 bus->fc_xon++;
4455
4456 bus->fc_rcvd++;
4457 bus->flowcontrol = fcbits;
4458 }
4459
4460 /* Shouldn't be any others */
4461 if (hmb_data & ~(HMB_DATA_DEVREADY |
4462 HMB_DATA_NAKHANDLED |
4463 HMB_DATA_FC |
4464 HMB_DATA_FWREADY |
4465 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) {
4466 DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data));
4467 }
4468
4469 return intstatus;
4470}
4471
4472bool dhdsdio_dpc(dhd_bus_t *bus)
4473{
4474 bcmsdh_info_t *sdh = bus->sdh;
Franky Lin597600a2011-06-01 13:45:39 +02004475 struct sdpcmd_regs *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004476 u32 intstatus, newstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004477 uint retries = 0;
4478 uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */
4479 uint txlimit = dhd_txbound; /* Tx frames to send before resched */
4480 uint framecnt = 0; /* Temporary counter of tx/rx frames */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004481 bool rxdone = true; /* Flag for no more read data */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004482 bool resched = false; /* Flag indicating resched wanted */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004483
4484 DHD_TRACE(("%s: Enter\n", __func__));
4485
4486 /* Start with leftover status bits */
4487 intstatus = bus->intstatus;
4488
4489 dhd_os_sdlock(bus->dhd);
4490
4491 /* If waiting for HTAVAIL, check status */
4492 if (bus->clkstate == CLK_PENDING) {
4493 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004494 u8 clkctl, devctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004495
4496#ifdef DHD_DEBUG
4497 /* Check for inconsistent device control */
4498 devctl =
4499 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
4500 if (err) {
4501 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4502 __func__, err));
4503 bus->dhd->busstate = DHD_BUS_DOWN;
4504 } else {
4505 ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY);
4506 }
4507#endif /* DHD_DEBUG */
4508
4509 /* Read CSR, if clock on switch to AVAIL, else ignore */
4510 clkctl =
4511 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
4512 &err);
4513 if (err) {
4514 DHD_ERROR(("%s: error reading CSR: %d\n", __func__,
4515 err));
4516 bus->dhd->busstate = DHD_BUS_DOWN;
4517 }
4518
4519 DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl,
4520 clkctl));
4521
4522 if (SBSDIO_HTAV(clkctl)) {
4523 devctl =
4524 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
4525 &err);
4526 if (err) {
4527 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4528 __func__, err));
4529 bus->dhd->busstate = DHD_BUS_DOWN;
4530 }
4531 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
4532 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
4533 devctl, &err);
4534 if (err) {
4535 DHD_ERROR(("%s: error writing DEVCTL: %d\n",
4536 __func__, err));
4537 bus->dhd->busstate = DHD_BUS_DOWN;
4538 }
4539 bus->clkstate = CLK_AVAIL;
4540 } else {
4541 goto clkwait;
4542 }
4543 }
4544
4545 BUS_WAKE(bus);
4546
4547 /* Make sure backplane clock is on */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004548 dhdsdio_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004549 if (bus->clkstate == CLK_PENDING)
4550 goto clkwait;
4551
4552 /* Pending interrupt indicates new device status */
4553 if (bus->ipend) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004554 bus->ipend = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004555 R_SDREG(newstatus, &regs->intstatus, retries);
4556 bus->f1regdata++;
4557 if (bcmsdh_regfail(bus->sdh))
4558 newstatus = 0;
4559 newstatus &= bus->hostintmask;
4560 bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
4561 if (newstatus) {
4562 W_SDREG(newstatus, &regs->intstatus, retries);
4563 bus->f1regdata++;
4564 }
4565 }
4566
4567 /* Merge new bits with previous */
4568 intstatus |= newstatus;
4569 bus->intstatus = 0;
4570
4571 /* Handle flow-control change: read new state in case our ack
4572 * crossed another change interrupt. If change still set, assume
4573 * FC ON for safety, let next loop through do the debounce.
4574 */
4575 if (intstatus & I_HMB_FC_CHANGE) {
4576 intstatus &= ~I_HMB_FC_CHANGE;
4577 W_SDREG(I_HMB_FC_CHANGE, &regs->intstatus, retries);
4578 R_SDREG(newstatus, &regs->intstatus, retries);
4579 bus->f1regdata += 2;
4580 bus->fcstate =
4581 !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
4582 intstatus |= (newstatus & bus->hostintmask);
4583 }
4584
4585 /* Handle host mailbox indication */
4586 if (intstatus & I_HMB_HOST_INT) {
4587 intstatus &= ~I_HMB_HOST_INT;
4588 intstatus |= dhdsdio_hostmail(bus);
4589 }
4590
4591 /* Generally don't ask for these, can get CRC errors... */
4592 if (intstatus & I_WR_OOSYNC) {
4593 DHD_ERROR(("Dongle reports WR_OOSYNC\n"));
4594 intstatus &= ~I_WR_OOSYNC;
4595 }
4596
4597 if (intstatus & I_RD_OOSYNC) {
4598 DHD_ERROR(("Dongle reports RD_OOSYNC\n"));
4599 intstatus &= ~I_RD_OOSYNC;
4600 }
4601
4602 if (intstatus & I_SBINT) {
4603 DHD_ERROR(("Dongle reports SBINT\n"));
4604 intstatus &= ~I_SBINT;
4605 }
4606
4607 /* Would be active due to wake-wlan in gSPI */
4608 if (intstatus & I_CHIPACTIVE) {
4609 DHD_INFO(("Dongle reports CHIPACTIVE\n"));
4610 intstatus &= ~I_CHIPACTIVE;
4611 }
4612
4613 /* Ignore frame indications if rxskip is set */
4614 if (bus->rxskip)
4615 intstatus &= ~I_HMB_FRAME_IND;
4616
4617 /* On frame indication, read available frames */
4618 if (PKT_AVAILABLE()) {
4619 framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone);
4620 if (rxdone || bus->rxskip)
4621 intstatus &= ~I_HMB_FRAME_IND;
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004622 rxlimit -= min(framecnt, rxlimit);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004623 }
4624
4625 /* Keep still-pending events for next scheduling */
4626 bus->intstatus = intstatus;
4627
4628clkwait:
4629#if defined(OOB_INTR_ONLY)
4630 bcmsdh_oob_intr_set(1);
4631#endif /* (OOB_INTR_ONLY) */
4632 /* Re-enable interrupts to detect new device events (mailbox, rx frame)
4633 * or clock availability. (Allows tx loop to check ipend if desired.)
4634 * (Unless register access seems hosed, as we may not be able to ACK...)
4635 */
4636 if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) {
4637 DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n",
4638 __func__, rxdone, framecnt));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004639 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004640 bcmsdh_intr_enable(sdh);
4641 }
4642
4643 if (DATAOK(bus) && bus->ctrl_frame_stat &&
4644 (bus->clkstate == CLK_AVAIL)) {
4645 int ret, i;
4646
4647 ret =
4648 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004649 F2SYNC, (u8 *) bus->ctrl_frame_buf,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004650 (u32) bus->ctrl_frame_len, NULL,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004651 NULL, NULL);
Roland Vossenb7ef2a92011-05-03 11:35:02 +02004652 ASSERT(ret != -BCME_PENDING);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004653
4654 if (ret < 0) {
4655 /* On failure, abort the command and
4656 terminate the frame */
4657 DHD_INFO(("%s: sdio error %d, abort command and "
4658 "terminate frame.\n", __func__, ret));
4659 bus->tx_sderrs++;
4660
4661 bcmsdh_abort(sdh, SDIO_FUNC_2);
4662
4663 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
4664 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
4665 NULL);
4666 bus->f1regdata++;
4667
4668 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004669 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004670 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
4671 SBSDIO_FUNC1_WFRAMEBCHI,
4672 NULL);
4673 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
4674 SBSDIO_FUNC1_WFRAMEBCLO,
4675 NULL);
4676 bus->f1regdata += 2;
4677 if ((hi == 0) && (lo == 0))
4678 break;
4679 }
4680
4681 }
4682 if (ret == 0)
4683 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
4684
Arend van Spriel0bef7742011-02-10 12:03:44 +01004685 DHD_INFO(("Return_dpc value is : %d\n", ret));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004686 bus->ctrl_frame_stat = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004687 dhd_wait_event_wakeup(bus->dhd);
4688 }
4689 /* Send queued frames (limit 1 if rx may still be pending) */
4690 else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
Roland Vossen67ad48b2011-06-01 13:45:51 +02004691 brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004692 && DATAOK(bus)) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004693 framecnt = rxdone ? txlimit : min(txlimit, dhd_txminmax);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004694 framecnt = dhdsdio_sendfromq(bus, framecnt);
4695 txlimit -= framecnt;
4696 }
4697
4698 /* Resched if events or tx frames are pending,
4699 else await next interrupt */
4700 /* On failed register access, all bets are off:
4701 no resched or interrupts */
4702 if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) {
4703 DHD_ERROR(("%s: failed backplane access over SDIO, halting "
4704 "operation %d\n", __func__, bcmsdh_regfail(sdh)));
4705 bus->dhd->busstate = DHD_BUS_DOWN;
4706 bus->intstatus = 0;
4707 } else if (bus->clkstate == CLK_PENDING) {
4708 DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting "
4709 "I_CHIPACTIVE interrupt\n", __func__));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004710 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004711 } else if (bus->intstatus || bus->ipend ||
Roland Vossen67ad48b2011-06-01 13:45:51 +02004712 (!bus->fcstate && brcmu_pktq_mlen(&bus->txq, ~bus->flowcontrol)
4713 && DATAOK(bus)) || PKT_AVAILABLE()) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004714 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004715 }
4716
4717 bus->dpc_sched = resched;
4718
4719 /* If we're done for now, turn off clock request. */
4720 if ((bus->clkstate != CLK_PENDING)
4721 && bus->idletime == DHD_IDLE_IMMEDIATE) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004722 bus->activity = false;
4723 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004724 }
4725
4726 dhd_os_sdunlock(bus->dhd);
4727
4728 return resched;
4729}
4730
4731bool dhd_bus_dpc(struct dhd_bus *bus)
4732{
4733 bool resched;
4734
4735 /* Call the DPC directly. */
4736 DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __func__));
4737 resched = dhdsdio_dpc(bus);
4738
4739 return resched;
4740}
4741
4742void dhdsdio_isr(void *arg)
4743{
4744 dhd_bus_t *bus = (dhd_bus_t *) arg;
4745 bcmsdh_info_t *sdh;
4746
4747 DHD_TRACE(("%s: Enter\n", __func__));
4748
4749 if (!bus) {
4750 DHD_ERROR(("%s : bus is null pointer , exit\n", __func__));
4751 return;
4752 }
4753 sdh = bus->sdh;
4754
4755 if (bus->dhd->busstate == DHD_BUS_DOWN) {
4756 DHD_ERROR(("%s : bus is down. we have nothing to do\n",
4757 __func__));
4758 return;
4759 }
4760 /* Count the interrupt call */
4761 bus->intrcount++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004762 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004763
4764 /* Shouldn't get this interrupt if we're sleeping? */
4765 if (bus->sleeping) {
4766 DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
4767 return;
4768 }
4769
4770 /* Disable additional interrupts (is this needed now)? */
4771 if (bus->intr)
4772 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
4773 else
4774 DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n"));
4775
4776 bcmsdh_intr_disable(sdh);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004777 bus->intdis = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004778
4779#if defined(SDIO_ISR_THREAD)
4780 DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __func__));
4781 while (dhdsdio_dpc(bus))
4782 ;
4783#else
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004784 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004785 dhd_sched_dpc(bus->dhd);
4786#endif
4787
4788}
4789
4790#ifdef SDTEST
4791static void dhdsdio_pktgen_init(dhd_bus_t *bus)
4792{
4793 /* Default to specified length, or full range */
4794 if (dhd_pktgen_len) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004795 bus->pktgen_maxlen = min(dhd_pktgen_len, MAX_PKTGEN_LEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004796 bus->pktgen_minlen = bus->pktgen_maxlen;
4797 } else {
4798 bus->pktgen_maxlen = MAX_PKTGEN_LEN;
4799 bus->pktgen_minlen = 0;
4800 }
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004801 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004802
4803 /* Default to per-watchdog burst with 10s print time */
4804 bus->pktgen_freq = 1;
4805 bus->pktgen_print = 10000 / dhd_watchdog_ms;
4806 bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000;
4807
4808 /* Default to echo mode */
4809 bus->pktgen_mode = DHD_PKTGEN_ECHO;
4810 bus->pktgen_stop = 1;
4811}
4812
4813static void dhdsdio_pktgen(dhd_bus_t *bus)
4814{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004815 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004816 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004817 uint pktcount;
4818 uint fillbyte;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004819 u16 len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004820
4821 /* Display current count if appropriate */
4822 if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
4823 bus->pktgen_ptick = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01004824 printk(KERN_DEBUG "%s: send attempts %d rcvd %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004825 __func__, bus->pktgen_sent, bus->pktgen_rcvd);
4826 }
4827
4828 /* For recv mode, just make sure dongle has started sending */
4829 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
4830 if (!bus->pktgen_rcvd)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004831 dhdsdio_sdtest_set(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004832 return;
4833 }
4834
4835 /* Otherwise, generate or request the specified number of packets */
4836 for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
4837 /* Stop if total has been reached */
4838 if (bus->pktgen_total
4839 && (bus->pktgen_sent >= bus->pktgen_total)) {
4840 bus->pktgen_count = 0;
4841 break;
4842 }
4843
4844 /* Allocate an appropriate-sized packet */
4845 len = bus->pktgen_len;
Roland Vossen67ad48b2011-06-01 13:45:51 +02004846 pkt = brcmu_pkt_buf_get_skb(
Jason Cooper9b890322010-09-30 15:15:39 -04004847 (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN),
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004848 true);
Jason Cooper9b890322010-09-30 15:15:39 -04004849 if (!pkt) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004850 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n",
4851 __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004852 break;
4853 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004854 PKTALIGN(pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004855 DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004856 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004857
4858 /* Write test header cmd and extra based on mode */
4859 switch (bus->pktgen_mode) {
4860 case DHD_PKTGEN_ECHO:
4861 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004862 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004863 break;
4864
4865 case DHD_PKTGEN_SEND:
4866 *data++ = SDPCM_TEST_DISCARD;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004867 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004868 break;
4869
4870 case DHD_PKTGEN_RXBURST:
4871 *data++ = SDPCM_TEST_BURST;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004872 *data++ = (u8) bus->pktgen_count;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004873 break;
4874
4875 default:
4876 DHD_ERROR(("Unrecognized pktgen mode %d\n",
4877 bus->pktgen_mode));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004878 brcmu_pkt_buf_free_skb(pkt, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004879 bus->pktgen_count = 0;
4880 return;
4881 }
4882
4883 /* Write test header length field */
4884 *data++ = (len >> 0);
4885 *data++ = (len >> 8);
4886
4887 /* Then fill in the remainder -- N/A for burst,
4888 but who cares... */
4889 for (fillbyte = 0; fillbyte < len; fillbyte++)
4890 *data++ =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004891 SDPCM_TEST_FILL(fillbyte, (u8) bus->pktgen_sent);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004892
4893#ifdef DHD_DEBUG
4894 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01004895 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Arend van Spriel34227312011-05-10 22:25:32 +02004896 printk(KERN_DEBUG "dhdsdio_pktgen: Tx Data:\n");
4897 print_hex_dump_bytes("", DUMP_PREFIX_OFFSET, data,
4898 pkt->len - SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004899 }
4900#endif
4901
4902 /* Send it */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004903 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004904 bus->pktgen_fail++;
4905 if (bus->pktgen_stop
4906 && bus->pktgen_stop == bus->pktgen_fail)
4907 bus->pktgen_count = 0;
4908 }
4909 bus->pktgen_sent++;
4910
4911 /* Bump length if not fixed, wrap at max */
4912 if (++bus->pktgen_len > bus->pktgen_maxlen)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004913 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004914
4915 /* Special case for burst mode: just send one request! */
4916 if (bus->pktgen_mode == DHD_PKTGEN_RXBURST)
4917 break;
4918 }
4919}
4920
4921static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start)
4922{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004923 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004924 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004925
4926 /* Allocate the packet */
Roland Vossen67ad48b2011-06-01 13:45:51 +02004927 pkt = brcmu_pkt_buf_get_skb(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN +
Henry Ptasinskib33f0e22011-05-10 22:25:29 +02004928 DHD_SDALIGN, true);
Jason Cooper9b890322010-09-30 15:15:39 -04004929 if (!pkt) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02004930 DHD_ERROR(("%s: brcmu_pkt_buf_get_skb failed!\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004931 return;
4932 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004933 PKTALIGN(pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004934 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004935
4936 /* Fill in the test header */
4937 *data++ = SDPCM_TEST_SEND;
4938 *data++ = start;
4939 *data++ = (bus->pktgen_maxlen >> 0);
4940 *data++ = (bus->pktgen_maxlen >> 8);
4941
4942 /* Send it */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004943 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004944 bus->pktgen_fail++;
4945}
4946
Arend van Sprielc26b1372010-11-23 14:06:23 +01004947static void dhdsdio_testrcv(dhd_bus_t *bus, struct sk_buff *pkt, uint seq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004948{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004949 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004950 uint pktlen;
4951
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004952 u8 cmd;
4953 u8 extra;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004954 u16 len;
4955 u16 offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004956
4957 /* Check for min length */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004958 pktlen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004959 if (pktlen < SDPCM_TEST_HDRLEN) {
4960 DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n",
4961 pktlen));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004962 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004963 return;
4964 }
4965
4966 /* Extract header fields */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004967 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004968 cmd = *data++;
4969 extra = *data++;
4970 len = *data++;
4971 len += *data++ << 8;
4972
4973 /* Check length for relevant commands */
4974 if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ
4975 || cmd == SDPCM_TEST_ECHORSP) {
4976 if (pktlen != len + SDPCM_TEST_HDRLEN) {
4977 DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, "
4978 "pktlen %d seq %d" " cmd %d extra %d len %d\n",
4979 pktlen, seq, cmd, extra, len));
Roland Vossen67ad48b2011-06-01 13:45:51 +02004980 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004981 return;
4982 }
4983 }
4984
4985 /* Process as per command */
4986 switch (cmd) {
4987 case SDPCM_TEST_ECHOREQ:
4988 /* Rx->Tx turnaround ok (even on NDIS w/current
4989 implementation) */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004990 *(u8 *) (pkt->data) = SDPCM_TEST_ECHORSP;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004991 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true) == 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004992 bus->pktgen_sent++;
4993 } else {
4994 bus->pktgen_fail++;
Roland Vossen67ad48b2011-06-01 13:45:51 +02004995 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004996 }
4997 bus->pktgen_rcvd++;
4998 break;
4999
5000 case SDPCM_TEST_ECHORSP:
5001 if (bus->ext_loop) {
Roland Vossen67ad48b2011-06-01 13:45:51 +02005002 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005003 bus->pktgen_rcvd++;
5004 break;
5005 }
5006
5007 for (offset = 0; offset < len; offset++, data++) {
5008 if (*data != SDPCM_TEST_FILL(offset, extra)) {
5009 DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: " "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n",
5010 offset, len,
5011 SDPCM_TEST_FILL(offset, extra), *data));
5012 break;
5013 }
5014 }
Roland Vossen67ad48b2011-06-01 13:45:51 +02005015 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005016 bus->pktgen_rcvd++;
5017 break;
5018
5019 case SDPCM_TEST_DISCARD:
Roland Vossen67ad48b2011-06-01 13:45:51 +02005020 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005021 bus->pktgen_rcvd++;
5022 break;
5023
5024 case SDPCM_TEST_BURST:
5025 case SDPCM_TEST_SEND:
5026 default:
5027 DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, "
5028 "pktlen %d seq %d" " cmd %d extra %d len %d\n",
5029 pktlen, seq, cmd, extra, len));
Roland Vossen67ad48b2011-06-01 13:45:51 +02005030 brcmu_pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005031 break;
5032 }
5033
5034 /* For recv mode, stop at limie (and tell dongle to stop sending) */
5035 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
5036 if (bus->pktgen_total
5037 && (bus->pktgen_rcvd >= bus->pktgen_total)) {
5038 bus->pktgen_count = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005039 dhdsdio_sdtest_set(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005040 }
5041 }
5042}
5043#endif /* SDTEST */
5044
5045extern bool dhd_bus_watchdog(dhd_pub_t *dhdp)
5046{
5047 dhd_bus_t *bus;
5048
5049 DHD_TIMER(("%s: Enter\n", __func__));
5050
5051 bus = dhdp->bus;
5052
5053 if (bus->dhd->dongle_reset)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005054 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005055
5056 /* Ignore the timer if simulating bus down */
5057 if (bus->sleeping)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005058 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005059
5060 dhd_os_sdlock(bus->dhd);
5061
5062 /* Poll period: check device if appropriate. */
5063 if (bus->poll && (++bus->polltick >= bus->pollrate)) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005064 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005065
5066 /* Reset poll tick */
5067 bus->polltick = 0;
5068
5069 /* Check device if no interrupts */
5070 if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
5071
5072 if (!bus->dpc_sched) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005073 u8 devpend;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005074 devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0,
Franky Lin0df46042011-06-01 13:45:40 +02005075 SDIO_CCCR_INTx,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005076 NULL);
5077 intstatus =
5078 devpend & (INTR_STATUS_FUNC1 |
5079 INTR_STATUS_FUNC2);
5080 }
5081
5082 /* If there is something, make like the ISR and
5083 schedule the DPC */
5084 if (intstatus) {
5085 bus->pollcnt++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005086 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005087 if (bus->intr)
5088 bcmsdh_intr_disable(bus->sdh);
5089
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005090 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005091 dhd_sched_dpc(bus->dhd);
5092
5093 }
5094 }
5095
5096 /* Update interrupt tracking */
5097 bus->lastintrs = bus->intrcount;
5098 }
5099#ifdef DHD_DEBUG
5100 /* Poll for console output periodically */
5101 if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) {
5102 bus->console.count += dhd_watchdog_ms;
5103 if (bus->console.count >= dhd_console_ms) {
5104 bus->console.count -= dhd_console_ms;
5105 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005106 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005107 if (dhdsdio_readconsole(bus) < 0)
5108 dhd_console_ms = 0; /* On error,
5109 stop trying */
5110 }
5111 }
5112#endif /* DHD_DEBUG */
5113
5114#ifdef SDTEST
5115 /* Generate packets if configured */
5116 if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
5117 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005118 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005119 bus->pktgen_tick = 0;
5120 dhdsdio_pktgen(bus);
5121 }
5122#endif
5123
5124 /* On idle timeout clear activity flag and/or turn off clock */
5125 if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
5126 if (++bus->idlecount >= bus->idletime) {
5127 bus->idlecount = 0;
5128 if (bus->activity) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005129 bus->activity = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005130 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
5131 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005132 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005133 }
5134 }
5135 }
5136
5137 dhd_os_sdunlock(bus->dhd);
5138
5139 return bus->ipend;
5140}
5141
5142#ifdef DHD_DEBUG
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07005143extern int dhd_bus_console_in(dhd_pub_t *dhdp, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005144{
5145 dhd_bus_t *bus = dhdp->bus;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005146 u32 addr, val;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005147 int rv;
Arend van Sprielc26b1372010-11-23 14:06:23 +01005148 struct sk_buff *pkt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005149
5150 /* Address could be zero if CONSOLE := 0 in dongle Makefile */
5151 if (bus->console_addr == 0)
Roland Vossene10d82d2011-05-03 11:35:19 +02005152 return -ENOTSUPP;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005153
5154 /* Exclusive bus access */
5155 dhd_os_sdlock(bus->dhd);
5156
5157 /* Don't allow input if dongle is in reset */
5158 if (bus->dhd->dongle_reset) {
5159 dhd_os_sdunlock(bus->dhd);
Roland Vossenb74ac122011-05-03 11:35:20 +02005160 return -EPERM;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005161 }
5162
5163 /* Request clock to allow SDIO accesses */
5164 BUS_WAKE(bus);
5165 /* No pend allowed since txpkt is called later, ht clk has to be on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005166 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005167
5168 /* Zero cbuf_index */
Roland Vossen70963f92011-06-01 13:45:08 +02005169 addr = bus->console_addr + offsetof(rte_cons_t, cbuf_idx);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03005170 val = cpu_to_le32(0);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005171 rv = dhdsdio_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04005172 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005173 goto done;
5174
5175 /* Write message into cbuf */
Roland Vossen70963f92011-06-01 13:45:08 +02005176 addr = bus->console_addr + offsetof(rte_cons_t, cbuf);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005177 rv = dhdsdio_membytes(bus, true, addr, (u8 *)msg, msglen);
Jason Cooper9b890322010-09-30 15:15:39 -04005178 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005179 goto done;
5180
5181 /* Write length into vcons_in */
Roland Vossen70963f92011-06-01 13:45:08 +02005182 addr = bus->console_addr + offsetof(rte_cons_t, vcons_in);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03005183 val = cpu_to_le32(msglen);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005184 rv = dhdsdio_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04005185 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005186 goto done;
5187
5188 /* Bump dongle by sending an empty event pkt.
5189 * sdpcm_sendup (RX) checks for virtual console input.
5190 */
Roland Vossen67ad48b2011-06-01 13:45:51 +02005191 pkt = brcmu_pkt_buf_get_skb(4 + SDPCM_RESERVE);
Jason Cooper9b890322010-09-30 15:15:39 -04005192 if ((pkt != NULL) && bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005193 dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005194
5195done:
5196 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005197 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005198 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005199 }
5200
5201 dhd_os_sdunlock(bus->dhd);
5202
5203 return rv;
5204}
5205#endif /* DHD_DEBUG */
5206
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005207static bool dhdsdio_chipmatch(u16 chipid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005208{
5209 if (chipid == BCM4325_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005210 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005211 if (chipid == BCM4329_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005212 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005213 if (chipid == BCM4319_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005214 return true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005215 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005216}
5217
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005218static void *dhdsdio_probe(u16 venid, u16 devid, u16 bus_no,
5219 u16 slot, u16 func, uint bustype, void *regsva,
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005220 void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005221{
5222 int ret;
5223 dhd_bus_t *bus;
5224
5225 /* Init global variables at run-time, not as part of the declaration.
5226 * This is required to support init/de-init of the driver.
5227 * Initialization
5228 * of globals as part of the declaration results in non-deterministic
5229 * behavior since the value of the globals may be different on the
5230 * first time that the driver is initialized vs subsequent
5231 * initializations.
5232 */
5233 dhd_txbound = DHD_TXBOUND;
5234 dhd_rxbound = DHD_RXBOUND;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005235 dhd_alignctl = true;
5236 sd1idle = true;
5237 dhd_readahead = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005238 retrydata = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005239 dhd_dongle_memsize = 0;
5240 dhd_txminmax = DHD_TXMINMAX;
5241
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005242 forcealign = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005243
5244 dhd_common_init();
5245
5246 DHD_TRACE(("%s: Enter\n", __func__));
5247 DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __func__, venid, devid));
5248
5249 /* We make assumptions about address window mappings */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005250 ASSERT((unsigned long)regsva == SI_ENUM_BASE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005251
5252 /* BCMSDH passes venid and devid based on CIS parsing -- but
5253 * low-power start
5254 * means early parse could fail, so here we should get either an ID
5255 * we recognize OR (-1) indicating we must request power first.
5256 */
5257 /* Check the Vendor ID */
5258 switch (venid) {
5259 case 0x0000:
Stanislav Fomichevbe1c09f2011-03-28 01:31:36 +04005260 case PCI_VENDOR_ID_BROADCOM:
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005261 break;
5262 default:
5263 DHD_ERROR(("%s: unknown vendor: 0x%04x\n", __func__, venid));
5264 return NULL;
5265 }
5266
5267 /* Check the Device ID and make sure it's one that we support */
5268 switch (devid) {
5269 case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */
5270 case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */
5271 case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */
5272 DHD_INFO(("%s: found 4325 Dongle\n", __func__));
5273 break;
5274 case BCM4329_D11NDUAL_ID: /* 4329 802.11n dualband device */
5275 case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */
5276 case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */
5277 case 0x4329:
5278 DHD_INFO(("%s: found 4329 Dongle\n", __func__));
5279 break;
5280 case BCM4319_D11N_ID: /* 4319 802.11n id */
5281 case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */
5282 case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */
5283 DHD_INFO(("%s: found 4319 Dongle\n", __func__));
5284 break;
5285 case 0:
5286 DHD_INFO(("%s: allow device id 0, will check chip internals\n",
5287 __func__));
5288 break;
5289
5290 default:
5291 DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
5292 __func__, venid, devid));
5293 return NULL;
5294 }
5295
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005296 /* Allocate private bus interface state */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005297 bus = kzalloc(sizeof(dhd_bus_t), GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005298 if (!bus) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005299 DHD_ERROR(("%s: kmalloc of dhd_bus_t failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005300 goto fail;
5301 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005302 bus->sdh = sdh;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005303 bus->cl_devid = (u16) devid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005304 bus->bus = DHD_BUS;
5305 bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005306 bus->usebufpool = false; /* Use bufpool if allocated,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005307 else use locally malloced rxbuf */
5308
5309 /* attempt to attach to the dongle */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005310 if (!(dhdsdio_probe_attach(bus, sdh, regsva, devid))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005311 DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __func__));
5312 goto fail;
5313 }
5314
5315 /* Attach to the dhd/OS/network interface */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005316 bus->dhd = dhd_attach(bus, SDPCM_RESERVE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005317 if (!bus->dhd) {
5318 DHD_ERROR(("%s: dhd_attach failed\n", __func__));
5319 goto fail;
5320 }
5321
5322 /* Allocate buffers */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005323 if (!(dhdsdio_probe_malloc(bus, sdh))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005324 DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __func__));
5325 goto fail;
5326 }
5327
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005328 if (!(dhdsdio_probe_init(bus, sdh))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005329 DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __func__));
5330 goto fail;
5331 }
5332
5333 /* Register interrupt callback, but mask it (not operational yet). */
5334 DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n",
5335 __func__));
5336 bcmsdh_intr_disable(sdh);
5337 ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus);
5338 if (ret != 0) {
5339 DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
5340 __func__, ret));
5341 goto fail;
5342 }
5343 DHD_INTR(("%s: registered SDIO interrupt function ok\n", __func__));
5344
5345 DHD_INFO(("%s: completed!!\n", __func__));
5346
5347 /* if firmware path present try to download and bring up bus */
Jason Cooper9b890322010-09-30 15:15:39 -04005348 ret = dhd_bus_start(bus->dhd);
5349 if (ret != 0) {
Roland Vossene10d82d2011-05-03 11:35:19 +02005350 if (ret == -ENOLINK) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005351 DHD_ERROR(("%s: dongle is not responding\n", __func__));
5352 goto fail;
5353 }
5354 }
5355 /* Ok, have the per-port tell the stack we're open for business */
5356 if (dhd_net_attach(bus->dhd, 0) != 0) {
5357 DHD_ERROR(("%s: Net attach failed!!\n", __func__));
5358 goto fail;
5359 }
5360
5361 return bus;
5362
5363fail:
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005364 dhdsdio_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005365 return NULL;
5366}
5367
5368static bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005369dhdsdio_probe_attach(struct dhd_bus *bus, void *sdh, void *regsva, u16 devid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005370{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005371 u8 clkctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005372 int err = 0;
5373
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005374 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005375
5376 /* Return the window to backplane enumeration space for core access */
5377 if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE))
5378 DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __func__));
5379
5380#ifdef DHD_DEBUG
Arend van Spriel0bef7742011-02-10 12:03:44 +01005381 printk(KERN_DEBUG "F1 signature read @0x18000000=0x%4x\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005382 bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4));
5383
5384#endif /* DHD_DEBUG */
5385
Franky Linc05df632011-04-25 19:34:07 -07005386 /*
5387 * Force PLL off until dhdsdio_chip_attach()
5388 * programs PLL control regs
5389 */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005390
5391 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
5392 DHD_INIT_CLKCTL1, &err);
5393 if (!err)
5394 clkctl =
5395 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
5396 &err);
5397
5398 if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) {
5399 DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote "
5400 "0x%02x read 0x%02x\n",
5401 err, DHD_INIT_CLKCTL1, clkctl));
5402 goto fail;
5403 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005404
Franky Lincb63e4c2011-04-25 15:45:08 -07005405 if (dhdsdio_chip_attach(bus, regsva)) {
5406 DHD_ERROR(("%s: dhdsdio_chip_attach failed!\n", __func__));
5407 goto fail;
5408 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005409
Franky Linc05df632011-04-25 19:34:07 -07005410 bcmsdh_chipinfo(sdh, bus->ci->chip, bus->ci->chiprev);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005411
Franky Linc05df632011-04-25 19:34:07 -07005412 if (!dhdsdio_chipmatch((u16) bus->ci->chip)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005413 DHD_ERROR(("%s: unsupported chip: 0x%04x\n",
Franky Linc05df632011-04-25 19:34:07 -07005414 __func__, bus->ci->chip));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005415 goto fail;
5416 }
5417
Franky Lin5d0d7a92011-04-25 19:34:05 -07005418 dhdsdio_sdiod_drive_strength_init(bus, dhd_sdiod_drive_strength);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005419
5420 /* Get info on the ARM and SOCRAM cores... */
5421 if (!DHD_NOPMU(bus)) {
Franky Linc05df632011-04-25 19:34:07 -07005422 bus->armrev = SBCOREREV(bcmsdh_reg_read(bus->sdh,
5423 CORE_SB(bus->ci->armcorebase, sbidhigh), 4));
5424 bus->orig_ramsize = bus->ci->ramsize;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005425 if (!(bus->orig_ramsize)) {
5426 DHD_ERROR(("%s: failed to find SOCRAM memory!\n",
5427 __func__));
5428 goto fail;
5429 }
5430 bus->ramsize = bus->orig_ramsize;
5431 if (dhd_dongle_memsize)
5432 dhd_dongle_setmemsize(bus, dhd_dongle_memsize);
5433
5434 DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
5435 bus->ramsize, bus->orig_ramsize));
5436 }
5437
Franky Linc05df632011-04-25 19:34:07 -07005438 bus->regs = (void *)bus->ci->buscorebase;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005439
5440 /* Set core control so an SDIO reset does a backplane reset */
Arend van Sprielff31c542011-03-01 10:56:54 +01005441 OR_REG(&bus->regs->corecontrol, CC_BPRESEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005442
Roland Vossen67ad48b2011-06-01 13:45:51 +02005443 brcmu_pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005444
5445 /* Locate an appropriately-aligned portion of hdrbuf */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005446 bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005447
5448 /* Set the poll and/or interrupt flags */
5449 bus->intr = (bool) dhd_intr;
Jason Cooper9b890322010-09-30 15:15:39 -04005450 bus->poll = (bool) dhd_poll;
5451 if (bus->poll)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005452 bus->pollrate = 1;
5453
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005454 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005455
5456fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005457 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005458}
5459
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005460static bool dhdsdio_probe_malloc(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005461{
5462 DHD_TRACE(("%s: Enter\n", __func__));
5463
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005464 if (bus->dhd->maxctl) {
5465 bus->rxblen =
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07005466 roundup((bus->dhd->maxctl + SDPCM_HDRLEN),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005467 ALIGNMENT) + DHD_SDALIGN;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005468 bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005469 if (!(bus->rxbuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005470 DHD_ERROR(("%s: kmalloc of %d-byte rxbuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005471 __func__, bus->rxblen));
5472 goto fail;
5473 }
5474 }
5475
5476 /* Allocate buffer to receive glomed packet */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005477 bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005478 if (!(bus->databuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005479 DHD_ERROR(("%s: kmalloc of %d-byte databuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005480 __func__, MAX_DATA_BUF));
5481 /* release rxbuf which was already located as above */
5482 if (!bus->rxblen)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005483 kfree(bus->rxbuf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005484 goto fail;
5485 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005486
5487 /* Align the buffer */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005488 if ((unsigned long)bus->databuf % DHD_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005489 bus->dataptr =
5490 bus->databuf + (DHD_SDALIGN -
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005491 ((unsigned long)bus->databuf % DHD_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005492 else
5493 bus->dataptr = bus->databuf;
5494
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005495 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005496
5497fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005498 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005499}
5500
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005501static bool dhdsdio_probe_init(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005502{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005503 s32 fnum;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005504
5505 DHD_TRACE(("%s: Enter\n", __func__));
5506
5507#ifdef SDTEST
5508 dhdsdio_pktgen_init(bus);
5509#endif /* SDTEST */
5510
5511 /* Disable F2 to clear any intermediate frame state on the dongle */
Franky Lin0df46042011-06-01 13:45:40 +02005512 bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx, SDIO_FUNC_ENABLE_1,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005513 NULL);
5514
5515 bus->dhd->busstate = DHD_BUS_DOWN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005516 bus->sleeping = false;
5517 bus->rxflow = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005518 bus->prev_rxlim_hit = 0;
5519
5520 /* Done with backplane-dependent accesses, can drop clock... */
5521 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
5522
5523 /* ...and initialize clock/power states */
5524 bus->clkstate = CLK_SDONLY;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005525 bus->idletime = (s32) dhd_idletime;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005526 bus->idleclock = DHD_IDLE_ACTIVE;
5527
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005528 /* Query the F2 block size, set roundup accordingly */
5529 fnum = 2;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005530 if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005531 &bus->blocksize, sizeof(s32), false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005532 bus->blocksize = 0;
5533 DHD_ERROR(("%s: fail on %s get\n", __func__, "sd_blocksize"));
5534 } else {
5535 DHD_INFO(("%s: Initial value for %s is %d\n",
5536 __func__, "sd_blocksize", bus->blocksize));
5537 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07005538 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005539
5540 /* Query if bus module supports packet chaining,
5541 default to use if supported */
5542 if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005543 &bus->sd_rxchain, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005544 false) != 0) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005545 bus->sd_rxchain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005546 } else {
5547 DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n",
5548 __func__,
5549 (bus->sd_rxchain ? "supports" : "does not support")));
5550 }
5551 bus->use_rxchain = (bool) bus->sd_rxchain;
5552
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005553 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005554}
5555
5556bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005557dhd_bus_download_firmware(struct dhd_bus *bus, char *fw_path, char *nv_path)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005558{
5559 bool ret;
5560 bus->fw_path = fw_path;
5561 bus->nv_path = nv_path;
5562
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005563 ret = dhdsdio_download_firmware(bus, bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005564
5565 return ret;
5566}
5567
5568static bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005569dhdsdio_download_firmware(struct dhd_bus *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005570{
5571 bool ret;
5572
5573 /* Download the firmware */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005574 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005575
5576 ret = _dhdsdio_download_firmware(bus) == 0;
5577
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005578 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005579
5580 return ret;
5581}
5582
5583/* Detach and free everything */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005584static void dhdsdio_release(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005585{
5586 DHD_TRACE(("%s: Enter\n", __func__));
5587
5588 if (bus) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005589 /* De-register interrupt handler */
5590 bcmsdh_intr_disable(bus->sdh);
5591 bcmsdh_intr_dereg(bus->sdh);
5592
5593 if (bus->dhd) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005594 dhd_detach(bus->dhd);
Franky Lincee3cf42011-04-25 19:34:06 -07005595 dhdsdio_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005596 bus->dhd = NULL;
5597 }
5598
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005599 dhdsdio_release_malloc(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005600
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005601 kfree(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005602 }
5603
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005604 DHD_TRACE(("%s: Disconnected\n", __func__));
5605}
5606
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005607static void dhdsdio_release_malloc(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005608{
5609 DHD_TRACE(("%s: Enter\n", __func__));
5610
5611 if (bus->dhd && bus->dhd->dongle_reset)
5612 return;
5613
5614 if (bus->rxbuf) {
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005615 kfree(bus->rxbuf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005616 bus->rxctl = bus->rxbuf = NULL;
5617 bus->rxlen = 0;
5618 }
5619
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005620 kfree(bus->databuf);
5621 bus->databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005622}
5623
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005624static void dhdsdio_release_dongle(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005625{
5626 DHD_TRACE(("%s: Enter\n", __func__));
5627
5628 if (bus->dhd && bus->dhd->dongle_reset)
5629 return;
5630
Franky Linc05df632011-04-25 19:34:07 -07005631 if (bus->ci) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005632 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005633 dhdsdio_clkctl(bus, CLK_NONE, false);
Franky Lincee3cf42011-04-25 19:34:06 -07005634 dhdsdio_chip_detach(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005635 if (bus->vars && bus->varsz)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005636 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005637 bus->vars = NULL;
5638 }
5639
5640 DHD_TRACE(("%s: Disconnected\n", __func__));
5641}
5642
5643static void dhdsdio_disconnect(void *ptr)
5644{
5645 dhd_bus_t *bus = (dhd_bus_t *)ptr;
5646
5647 DHD_TRACE(("%s: Enter\n", __func__));
5648
5649 if (bus) {
5650 ASSERT(bus->dhd);
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005651 dhdsdio_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005652 }
5653
5654 DHD_TRACE(("%s: Disconnected\n", __func__));
5655}
5656
5657/* Register/Unregister functions are called by the main DHD entry
5658 * point (e.g. module insertion) to link with the bus driver, in
5659 * order to look for or await the device.
5660 */
5661
5662static bcmsdh_driver_t dhd_sdio = {
5663 dhdsdio_probe,
5664 dhdsdio_disconnect
5665};
5666
5667int dhd_bus_register(void)
5668{
5669 DHD_TRACE(("%s: Enter\n", __func__));
5670
5671 return bcmsdh_register(&dhd_sdio);
5672}
5673
5674void dhd_bus_unregister(void)
5675{
5676 DHD_TRACE(("%s: Enter\n", __func__));
5677
5678 bcmsdh_unregister();
5679}
5680
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005681static int dhdsdio_download_code_file(struct dhd_bus *bus, char *fw_path)
5682{
5683 int bcmerror = -1;
5684 int offset = 0;
5685 uint len;
5686 void *image = NULL;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005687 u8 *memblock = NULL, *memptr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005688
5689 DHD_INFO(("%s: download firmware %s\n", __func__, fw_path));
5690
5691 image = dhd_os_open_image(fw_path);
5692 if (image == NULL)
5693 goto err;
5694
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005695 memptr = memblock = kmalloc(MEMBLOCK + DHD_SDALIGN, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005696 if (memblock == NULL) {
5697 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5698 __func__, MEMBLOCK));
5699 goto err;
5700 }
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005701 if ((u32)(unsigned long)memblock % DHD_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005702 memptr +=
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005703 (DHD_SDALIGN - ((u32)(unsigned long)memblock % DHD_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005704
5705 /* Download image */
5706 while ((len =
5707 dhd_os_get_image_block((char *)memptr, MEMBLOCK, image))) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005708 bcmerror = dhdsdio_membytes(bus, true, offset, memptr, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005709 if (bcmerror) {
5710 DHD_ERROR(("%s: error %d on writing %d membytes at "
5711 "0x%08x\n", __func__, bcmerror, MEMBLOCK, offset));
5712 goto err;
5713 }
5714
5715 offset += MEMBLOCK;
5716 }
5717
5718err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005719 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005720
5721 if (image)
5722 dhd_os_close_image(image);
5723
5724 return bcmerror;
5725}
5726
5727/*
5728 * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
5729 * and ending in a NUL.
5730 * Removes carriage returns, empty lines, comment lines, and converts
5731 * newlines to NULs.
5732 * Shortens buffer as needed and pads with NULs. End of buffer is marked
5733 * by two NULs.
5734*/
5735
5736static uint process_nvram_vars(char *varbuf, uint len)
5737{
5738 char *dp;
5739 bool findNewline;
5740 int column;
5741 uint buf_len, n;
5742
5743 dp = varbuf;
5744
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005745 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005746 column = 0;
5747
5748 for (n = 0; n < len; n++) {
5749 if (varbuf[n] == 0)
5750 break;
5751 if (varbuf[n] == '\r')
5752 continue;
5753 if (findNewline && varbuf[n] != '\n')
5754 continue;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005755 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005756 if (varbuf[n] == '#') {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005757 findNewline = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005758 continue;
5759 }
5760 if (varbuf[n] == '\n') {
5761 if (column == 0)
5762 continue;
5763 *dp++ = 0;
5764 column = 0;
5765 continue;
5766 }
5767 *dp++ = varbuf[n];
5768 column++;
5769 }
5770 buf_len = dp - varbuf;
5771
5772 while (dp < varbuf + n)
5773 *dp++ = 0;
5774
5775 return buf_len;
5776}
5777
5778/*
5779 EXAMPLE: nvram_array
5780 nvram_arry format:
5781 name=value
5782 Use carriage return at the end of each assignment,
5783 and an empty string with
5784 carriage return at the end of array.
5785
5786 For example:
5787 unsigned char nvram_array[] = {"name1=value1\n",
5788 "name2=value2\n", "\n"};
5789 Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx.
5790
5791 Search "EXAMPLE: nvram_array" to see how the array is activated.
5792*/
5793
5794void dhd_bus_set_nvram_params(struct dhd_bus *bus, const char *nvram_params)
5795{
5796 bus->nvram_params = nvram_params;
5797}
5798
5799static int dhdsdio_download_nvram(struct dhd_bus *bus)
5800{
5801 int bcmerror = -1;
5802 uint len;
5803 void *image = NULL;
5804 char *memblock = NULL;
5805 char *bufp;
5806 char *nv_path;
5807 bool nvram_file_exists;
5808
5809 nv_path = bus->nv_path;
5810
5811 nvram_file_exists = ((nv_path != NULL) && (nv_path[0] != '\0'));
5812 if (!nvram_file_exists && (bus->nvram_params == NULL))
5813 return 0;
5814
5815 if (nvram_file_exists) {
5816 image = dhd_os_open_image(nv_path);
5817 if (image == NULL)
5818 goto err;
5819 }
5820
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005821 memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005822 if (memblock == NULL) {
5823 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5824 __func__, MEMBLOCK));
5825 goto err;
5826 }
5827
5828 /* Download variables */
5829 if (nvram_file_exists) {
5830 len = dhd_os_get_image_block(memblock, MEMBLOCK, image);
5831 } else {
5832 len = strlen(bus->nvram_params);
5833 ASSERT(len <= MEMBLOCK);
5834 if (len > MEMBLOCK)
5835 len = MEMBLOCK;
5836 memcpy(memblock, bus->nvram_params, len);
5837 }
5838
5839 if (len > 0 && len < MEMBLOCK) {
5840 bufp = (char *)memblock;
5841 bufp[len] = 0;
5842 len = process_nvram_vars(bufp, len);
5843 bufp += len;
5844 *bufp++ = 0;
5845 if (len)
5846 bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1);
5847 if (bcmerror) {
5848 DHD_ERROR(("%s: error downloading vars: %d\n",
5849 __func__, bcmerror));
5850 }
5851 } else {
5852 DHD_ERROR(("%s: error reading nvram file: %d\n",
5853 __func__, len));
Roland Vossenb74ac122011-05-03 11:35:20 +02005854 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005855 }
5856
5857err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005858 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005859
5860 if (image)
5861 dhd_os_close_image(image);
5862
5863 return bcmerror;
5864}
5865
5866static int _dhdsdio_download_firmware(struct dhd_bus *bus)
5867{
5868 int bcmerror = -1;
5869
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005870 bool embed = false; /* download embedded firmware */
5871 bool dlok = false; /* download firmware succeeded */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005872
5873 /* Out immediately if no image to download */
Franky Lina4181fb2011-06-01 13:45:34 +02005874 if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0'))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005875 return bcmerror;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005876
5877 /* Keep arm in reset */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005878 if (dhdsdio_download_state(bus, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005879 DHD_ERROR(("%s: error placing ARM core in reset\n", __func__));
5880 goto err;
5881 }
5882
5883 /* External image takes precedence if specified */
5884 if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) {
5885 if (dhdsdio_download_code_file(bus, bus->fw_path)) {
5886 DHD_ERROR(("%s: dongle image file download failed\n",
5887 __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005888 goto err;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005889 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005890 embed = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005891 dlok = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005892 }
5893 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005894 if (!dlok) {
5895 DHD_ERROR(("%s: dongle image download failed\n", __func__));
5896 goto err;
5897 }
5898
5899 /* EXAMPLE: nvram_array */
5900 /* If a valid nvram_arry is specified as above, it can be passed
5901 down to dongle */
5902 /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */
5903
5904 /* External nvram takes precedence if specified */
5905 if (dhdsdio_download_nvram(bus)) {
5906 DHD_ERROR(("%s: dongle nvram file download failed\n",
5907 __func__));
5908 }
5909
5910 /* Take arm out of reset */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005911 if (dhdsdio_download_state(bus, false)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005912 DHD_ERROR(("%s: error getting out of ARM core reset\n",
5913 __func__));
5914 goto err;
5915 }
5916
5917 bcmerror = 0;
5918
5919err:
5920 return bcmerror;
5921}
5922
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005923
5924static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005925dhd_bcmsdh_send_buf(dhd_bus_t *bus, u32 addr, uint fn, uint flags,
Arend van Sprielc26b1372010-11-23 14:06:23 +01005926 u8 *buf, uint nbytes, struct sk_buff *pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005927 bcmsdh_cmplt_fn_t complete, void *handle)
5928{
5929 return bcmsdh_send_buf
5930 (bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete,
5931 handle);
5932}
5933
5934uint dhd_bus_chip(struct dhd_bus *bus)
5935{
Franky Linc05df632011-04-25 19:34:07 -07005936 ASSERT(bus->ci != NULL);
5937 return bus->ci->chip;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005938}
5939
5940void *dhd_bus_pub(struct dhd_bus *bus)
5941{
5942 return bus->dhd;
5943}
5944
5945void *dhd_bus_txq(struct dhd_bus *bus)
5946{
5947 return &bus->txq;
5948}
5949
5950uint dhd_bus_hdrlen(struct dhd_bus *bus)
5951{
5952 return SDPCM_HDRLEN;
5953}
5954
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005955int dhd_bus_devreset(dhd_pub_t *dhdp, u8 flag)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005956{
5957 int bcmerror = 0;
5958 dhd_bus_t *bus;
5959
5960 bus = dhdp->bus;
5961
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005962 if (flag == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005963 if (!bus->dhd->dongle_reset) {
5964 /* Expect app to have torn down any
5965 connection before calling */
5966 /* Stop the bus, disable F2 */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005967 dhd_bus_stop(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005968
5969 /* Clean tx/rx buffer pointers,
5970 detach from the dongle */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005971 dhdsdio_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005972
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005973 bus->dhd->dongle_reset = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005974 bus->dhd->up = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005975
5976 DHD_TRACE(("%s: WLAN OFF DONE\n", __func__));
5977 /* App can now remove power from device */
5978 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02005979 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005980 } else {
5981 /* App must have restored power to device before calling */
5982
5983 DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __func__));
5984
5985 if (bus->dhd->dongle_reset) {
5986 /* Turn on WLAN */
5987 /* Reset SD client */
5988 bcmsdh_reset(bus->sdh);
5989
5990 /* Attempt to re-attach & download */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005991 if (dhdsdio_probe_attach(bus, bus->sdh,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005992 (u32 *) SI_ENUM_BASE,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005993 bus->cl_devid)) {
5994 /* Attempt to download binary to the dongle */
5995 if (dhdsdio_probe_init
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005996 (bus, bus->sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005997 && dhdsdio_download_firmware(bus,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005998 bus->sdh)) {
5999
6000 /* Re-init bus, enable F2 transfer */
6001 dhd_bus_init((dhd_pub_t *) bus->dhd,
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006002 false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006003
6004#if defined(OOB_INTR_ONLY)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006005 dhd_enable_oob_intr(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006006#endif /* defined(OOB_INTR_ONLY) */
6007
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006008 bus->dhd->dongle_reset = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006009 bus->dhd->up = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006010
6011 DHD_TRACE(("%s: WLAN ON DONE\n",
6012 __func__));
6013 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02006014 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006015 } else
Roland Vossenb74ac122011-05-03 11:35:20 +02006016 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006017 } else {
Roland Vossenb74ac122011-05-03 11:35:20 +02006018 bcmerror = -EISCONN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006019 DHD_ERROR(("%s: Set DEVRESET=false invoked when device "
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006020 "is on\n", __func__));
Roland Vossenb74ac122011-05-03 11:35:20 +02006021 bcmerror = -EIO;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006022 }
6023 }
6024 return bcmerror;
6025}
Franky Lincb63e4c2011-04-25 15:45:08 -07006026
6027static int
6028dhdsdio_chip_recognition(bcmsdh_info_t *sdh, struct chip_info *ci, void *regs)
6029{
6030 u32 regdata;
6031
6032 /*
6033 * Get CC core rev
6034 * Chipid is assume to be at offset 0 from regs arg
6035 * For different chiptypes or old sdio hosts w/o chipcommon,
6036 * other ways of recognition should be added here.
6037 */
6038 ci->cccorebase = (u32)regs;
6039 regdata = bcmsdh_reg_read(sdh, CORE_CC_REG(ci->cccorebase, chipid), 4);
6040 ci->chip = regdata & CID_ID_MASK;
6041 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
6042
6043 DHD_INFO(("%s: chipid=0x%x chiprev=%d\n",
6044 __func__, ci->chip, ci->chiprev));
6045
6046 /* Address of cores for new chips should be added here */
6047 switch (ci->chip) {
6048 case BCM4329_CHIP_ID:
6049 ci->buscorebase = BCM4329_CORE_BUS_BASE;
6050 ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE;
6051 ci->armcorebase = BCM4329_CORE_ARM_BASE;
Franky Linc05df632011-04-25 19:34:07 -07006052 ci->ramsize = BCM4329_RAMSIZE;
Franky Lincb63e4c2011-04-25 15:45:08 -07006053 break;
6054 default:
6055 DHD_ERROR(("%s: chipid 0x%x is not supported\n",
6056 __func__, ci->chip));
6057 return -ENODEV;
6058 }
6059
6060 regdata = bcmsdh_reg_read(sdh,
6061 CORE_SB(ci->cccorebase, sbidhigh), 4);
6062 ci->ccrev = SBCOREREV(regdata);
6063
6064 regdata = bcmsdh_reg_read(sdh,
6065 CORE_CC_REG(ci->cccorebase, pmucapabilities), 4);
6066 ci->pmurev = regdata & PCAP_REV_MASK;
6067
6068 regdata = bcmsdh_reg_read(sdh, CORE_SB(ci->buscorebase, sbidhigh), 4);
6069 ci->buscorerev = SBCOREREV(regdata);
6070 ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
6071
6072 DHD_INFO(("%s: ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
6073 __func__, ci->ccrev, ci->pmurev,
6074 ci->buscorerev, ci->buscoretype));
6075
6076 /* get chipcommon capabilites */
6077 ci->cccaps = bcmsdh_reg_read(sdh,
6078 CORE_CC_REG(ci->cccorebase, capabilities), 4);
6079
6080 return 0;
6081}
6082
6083static void
6084dhdsdio_chip_disablecore(bcmsdh_info_t *sdh, u32 corebase)
6085{
6086 u32 regdata;
6087
6088 regdata = bcmsdh_reg_read(sdh,
6089 CORE_SB(corebase, sbtmstatelow), 4);
6090 if (regdata & SBTML_RESET)
6091 return;
6092
6093 regdata = bcmsdh_reg_read(sdh,
6094 CORE_SB(corebase, sbtmstatelow), 4);
6095 if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) {
6096 /*
6097 * set target reject and spin until busy is clear
6098 * (preserve core-specific bits)
6099 */
6100 regdata = bcmsdh_reg_read(sdh,
6101 CORE_SB(corebase, sbtmstatelow), 4);
6102 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6103 regdata | SBTML_REJ);
6104
6105 regdata = bcmsdh_reg_read(sdh,
6106 CORE_SB(corebase, sbtmstatelow), 4);
6107 udelay(1);
6108 SPINWAIT((bcmsdh_reg_read(sdh,
6109 CORE_SB(corebase, sbtmstatehigh), 4) &
6110 SBTMH_BUSY), 100000);
6111
6112 regdata = bcmsdh_reg_read(sdh,
6113 CORE_SB(corebase, sbtmstatehigh), 4);
6114 if (regdata & SBTMH_BUSY)
6115 DHD_ERROR(("%s: ARM core still busy\n", __func__));
6116
6117 regdata = bcmsdh_reg_read(sdh,
6118 CORE_SB(corebase, sbidlow), 4);
6119 if (regdata & SBIDL_INIT) {
6120 regdata = bcmsdh_reg_read(sdh,
6121 CORE_SB(corebase, sbimstate), 4) |
6122 SBIM_RJ;
6123 bcmsdh_reg_write(sdh,
6124 CORE_SB(corebase, sbimstate), 4,
6125 regdata);
6126 regdata = bcmsdh_reg_read(sdh,
6127 CORE_SB(corebase, sbimstate), 4);
6128 udelay(1);
6129 SPINWAIT((bcmsdh_reg_read(sdh,
6130 CORE_SB(corebase, sbimstate), 4) &
6131 SBIM_BY), 100000);
6132 }
6133
6134 /* set reset and reject while enabling the clocks */
6135 bcmsdh_reg_write(sdh,
6136 CORE_SB(corebase, sbtmstatelow), 4,
6137 (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
6138 SBTML_REJ | SBTML_RESET));
6139 regdata = bcmsdh_reg_read(sdh,
6140 CORE_SB(corebase, sbtmstatelow), 4);
6141 udelay(10);
6142
6143 /* clear the initiator reject bit */
6144 regdata = bcmsdh_reg_read(sdh,
6145 CORE_SB(corebase, sbidlow), 4);
6146 if (regdata & SBIDL_INIT) {
6147 regdata = bcmsdh_reg_read(sdh,
6148 CORE_SB(corebase, sbimstate), 4) &
6149 ~SBIM_RJ;
6150 bcmsdh_reg_write(sdh,
6151 CORE_SB(corebase, sbimstate), 4,
6152 regdata);
6153 }
6154 }
6155
6156 /* leave reset and reject asserted */
6157 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6158 (SBTML_REJ | SBTML_RESET));
6159 udelay(1);
6160}
6161
6162static int
6163dhdsdio_chip_attach(struct dhd_bus *bus, void *regs)
6164{
6165 struct chip_info *ci;
6166 int err;
6167 u8 clkval, clkset;
6168
6169 DHD_TRACE(("%s: Enter\n", __func__));
6170
6171 /* alloc chip_info_t */
6172 ci = kmalloc(sizeof(struct chip_info), GFP_ATOMIC);
6173 if (NULL == ci) {
6174 DHD_ERROR(("%s: malloc failed!\n", __func__));
6175 return -ENOMEM;
6176 }
6177
6178 memset((unsigned char *)ci, 0, sizeof(struct chip_info));
6179
6180 /* bus/core/clk setup for register access */
6181 /* Try forcing SDIO core to do ALPAvail request only */
6182 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
6183 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
6184 clkset, &err);
6185 if (err) {
6186 DHD_ERROR(("%s: error writing for HT off\n", __func__));
6187 goto fail;
6188 }
6189
6190 /* If register supported, wait for ALPAvail and then force ALP */
6191 /* This may take up to 15 milliseconds */
6192 clkval = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6193 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
6194 if ((clkval & ~SBSDIO_AVBITS) == clkset) {
6195 SPINWAIT(((clkval =
6196 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6197 SBSDIO_FUNC1_CHIPCLKCSR,
6198 NULL)),
6199 !SBSDIO_ALPAV(clkval)),
6200 PMU_MAX_TRANSITION_DLY);
6201 if (!SBSDIO_ALPAV(clkval)) {
6202 DHD_ERROR(("%s: timeout on ALPAV wait, clkval 0x%02x\n",
6203 __func__, clkval));
6204 err = -EBUSY;
6205 goto fail;
6206 }
6207 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF |
6208 SBSDIO_FORCE_ALP;
6209 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
6210 SBSDIO_FUNC1_CHIPCLKCSR,
6211 clkset, &err);
6212 udelay(65);
6213 } else {
6214 DHD_ERROR(("%s: ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
6215 __func__, clkset, clkval));
6216 err = -EACCES;
6217 goto fail;
6218 }
6219
6220 /* Also, disable the extra SDIO pull-ups */
6221 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0,
6222 NULL);
6223
6224 err = dhdsdio_chip_recognition(bus->sdh, ci, regs);
6225 if (err)
6226 goto fail;
6227
6228 /*
6229 * Make sure any on-chip ARM is off (in case strapping is wrong),
6230 * or downloaded code was already running.
6231 */
6232 dhdsdio_chip_disablecore(bus->sdh, ci->armcorebase);
6233
6234 bcmsdh_reg_write(bus->sdh,
6235 CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0);
6236 bcmsdh_reg_write(bus->sdh,
6237 CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0);
6238
6239 /* Disable F2 to clear any intermediate frame state on the dongle */
Franky Lin0df46042011-06-01 13:45:40 +02006240 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIO_CCCR_IOEx,
Franky Lincb63e4c2011-04-25 15:45:08 -07006241 SDIO_FUNC_ENABLE_1, NULL);
6242
6243 /* WAR: cmd52 backplane read so core HW will drop ALPReq */
6244 clkval = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6245 0, NULL);
6246
6247 /* Done with backplane-dependent accesses, can drop clock... */
6248 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0,
6249 NULL);
6250
6251 bus->ci = ci;
6252 return 0;
6253fail:
6254 bus->ci = NULL;
6255 kfree(ci);
6256 return err;
6257}
Franky Lineb5dc512011-04-25 19:34:04 -07006258
6259static void
6260dhdsdio_chip_resetcore(bcmsdh_info_t *sdh, u32 corebase)
6261{
6262 u32 regdata;
6263
6264 /*
6265 * Must do the disable sequence first to work for
6266 * arbitrary current core state.
6267 */
6268 dhdsdio_chip_disablecore(sdh, corebase);
6269
6270 /*
6271 * Now do the initialization sequence.
6272 * set reset while enabling the clock and
6273 * forcing them on throughout the core
6274 */
6275 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6276 ((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
6277 SBTML_RESET);
6278 udelay(1);
6279
6280 regdata = bcmsdh_reg_read(sdh, CORE_SB(corebase, sbtmstatehigh), 4);
6281 if (regdata & SBTMH_SERR)
6282 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatehigh), 4, 0);
6283
6284 regdata = bcmsdh_reg_read(sdh, CORE_SB(corebase, sbimstate), 4);
6285 if (regdata & (SBIM_IBE | SBIM_TO))
6286 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbimstate), 4,
6287 regdata & ~(SBIM_IBE | SBIM_TO));
6288
6289 /* clear reset and allow it to propagate throughout the core */
6290 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6291 (SICF_FGC << SBTML_SICF_SHIFT) |
6292 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
6293 udelay(1);
6294
6295 /* leave clock enabled */
6296 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6297 (SICF_CLOCK_EN << SBTML_SICF_SHIFT));
6298 udelay(1);
6299}
Franky Lin5d0d7a92011-04-25 19:34:05 -07006300
6301/* SDIO Pad drive strength to select value mappings */
6302struct sdiod_drive_str {
6303 u8 strength; /* Pad Drive Strength in mA */
6304 u8 sel; /* Chip-specific select value */
6305};
6306
6307/* SDIO Drive Strength to sel value table for PMU Rev 1 */
6308static const struct sdiod_drive_str sdiod_drive_strength_tab1[] = {
6309 {
6310 4, 0x2}, {
6311 2, 0x3}, {
6312 1, 0x0}, {
6313 0, 0x0}
6314 };
6315
6316/* SDIO Drive Strength to sel value table for PMU Rev 2, 3 */
6317static const struct sdiod_drive_str sdiod_drive_strength_tab2[] = {
6318 {
6319 12, 0x7}, {
6320 10, 0x6}, {
6321 8, 0x5}, {
6322 6, 0x4}, {
6323 4, 0x2}, {
6324 2, 0x1}, {
6325 0, 0x0}
6326 };
6327
6328/* SDIO Drive Strength to sel value table for PMU Rev 8 (1.8V) */
6329static const struct sdiod_drive_str sdiod_drive_strength_tab3[] = {
6330 {
6331 32, 0x7}, {
6332 26, 0x6}, {
6333 22, 0x5}, {
6334 16, 0x4}, {
6335 12, 0x3}, {
6336 8, 0x2}, {
6337 4, 0x1}, {
6338 0, 0x0}
6339 };
6340
6341#define SDIOD_DRVSTR_KEY(chip, pmu) (((chip) << 16) | (pmu))
6342
6343static void
6344dhdsdio_sdiod_drive_strength_init(struct dhd_bus *bus, u32 drivestrength) {
6345 struct sdiod_drive_str *str_tab = NULL;
6346 u32 str_mask = 0;
6347 u32 str_shift = 0;
Franky Lin5d0d7a92011-04-25 19:34:05 -07006348 char chn[8];
Franky Lin5d0d7a92011-04-25 19:34:05 -07006349
6350 if (!(bus->ci->cccaps & CC_CAP_PMU))
6351 return;
6352
6353 switch (SDIOD_DRVSTR_KEY(bus->ci->chip, bus->ci->pmurev)) {
6354 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 1):
6355 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab1;
6356 str_mask = 0x30000000;
6357 str_shift = 28;
6358 break;
6359 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 2):
6360 case SDIOD_DRVSTR_KEY(BCM4325_CHIP_ID, 3):
6361 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab2;
6362 str_mask = 0x00003800;
6363 str_shift = 11;
6364 break;
6365 case SDIOD_DRVSTR_KEY(BCM4336_CHIP_ID, 8):
6366 str_tab = (struct sdiod_drive_str *)&sdiod_drive_strength_tab3;
6367 str_mask = 0x00003800;
6368 str_shift = 11;
6369 break;
6370 default:
6371 DHD_ERROR(("No SDIO Drive strength init"
6372 "done for chip %s rev %d pmurev %d\n",
Roland Vossen67ad48b2011-06-01 13:45:51 +02006373 brcmu_chipname(bus->ci->chip, chn, 8),
Franky Lin5d0d7a92011-04-25 19:34:05 -07006374 bus->ci->chiprev, bus->ci->pmurev));
6375 break;
6376 }
6377
6378 if (str_tab != NULL) {
6379 u32 drivestrength_sel = 0;
6380 u32 cc_data_temp;
6381 int i;
6382
6383 for (i = 0; str_tab[i].strength != 0; i++) {
6384 if (drivestrength >= str_tab[i].strength) {
6385 drivestrength_sel = str_tab[i].sel;
6386 break;
6387 }
6388 }
6389
6390 bcmsdh_reg_write(bus->sdh,
6391 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
6392 4, 1);
6393 cc_data_temp = bcmsdh_reg_read(bus->sdh,
6394 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr), 4);
6395 cc_data_temp &= ~str_mask;
6396 drivestrength_sel <<= str_shift;
6397 cc_data_temp |= drivestrength_sel;
6398 bcmsdh_reg_write(bus->sdh,
6399 CORE_CC_REG(bus->ci->cccorebase, chipcontrol_addr),
6400 4, cc_data_temp);
6401
6402 DHD_INFO(("SDIO: %dmA drive strength selected, set to 0x%08x\n",
6403 drivestrength, cc_data_temp));
6404 }
6405}
Franky Lincee3cf42011-04-25 19:34:06 -07006406
6407static void
6408dhdsdio_chip_detach(struct dhd_bus *bus)
6409{
6410 DHD_TRACE(("%s: Enter\n", __func__));
6411
6412 kfree(bus->ci);
6413 bus->ci = NULL;
6414}