blob: 14f4703581822b8900d3ac5bfb3ef1261a46ec49 [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>
Stanislav Fomichevbe1c09f2011-03-28 01:31:36 +040018#include <linux/pci_ids.h>
Greg Kroah-Hartmana1c16ed2010-10-21 11:17:44 -070019#include <bcmdefs.h>
Brett Rudleyc6ac24e2010-10-26 11:55:23 -070020#include <linux/netdevice.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070021#include <bcmsdh.h>
22
23#ifdef BCMEMBEDIMAGE
24#include BCMEMBEDIMAGE
25#endif /* BCMEMBEDIMAGE */
26
27#include <bcmdefs.h>
28#include <bcmutils.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070029#include <bcmdevs.h>
30
31#include <siutils.h>
32#include <hndpmu.h>
33#include <hndsoc.h>
34#ifdef DHD_DEBUG
35#include <hndrte_armtrap.h>
36#include <hndrte_cons.h>
37#endif /* DHD_DEBUG */
38#include <sbchipc.h>
39#include <sbhnddma.h>
40
41#include <sdio.h>
42#include <sbsdio.h>
43#include <sbsdpcmdev.h>
44#include <bcmsdpcm.h>
45
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070046#include <proto/802.11.h>
47
48#include <dngl_stats.h>
49#include <dhd.h>
50#include <dhd_bus.h>
51#include <dhd_proto.h>
52#include <dhd_dbg.h>
53#include <dhdioctl.h>
54#include <sdiovar.h>
55#include <siutils_priv.h>
Franky Lincb63e4c2011-04-25 15:45:08 -070056#include <bcmchip.h>
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070057
58#ifndef DHDSDIO_MEM_DUMP_FNAME
59#define DHDSDIO_MEM_DUMP_FNAME "mem_dump"
60#endif
61
Grant Grundler26a71a42011-03-09 10:41:25 -080062#define TXQLEN 2048 /* bulk tx queue length */
63#define TXHI (TXQLEN - 256) /* turn on flow control above TXHI */
64#define TXLOW (TXHI - 256) /* turn off flow control below TXLOW */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -070065#define PRIOMASK 7
66
67#define TXRETRIES 2 /* # of retries for tx frames */
68
69#if defined(CONFIG_MACH_SANDGATE2G)
70#define DHD_RXBOUND 250 /* Default for max rx frames in
71 one scheduling */
72#else
73#define DHD_RXBOUND 50 /* Default for max rx frames in
74 one scheduling */
75#endif /* defined(CONFIG_MACH_SANDGATE2G) */
76
77#define DHD_TXBOUND 20 /* Default for max tx frames in
78 one scheduling */
79
80#define DHD_TXMINMAX 1 /* Max tx frames if rx still pending */
81
82#define MEMBLOCK 2048 /* Block size used for downloading
83 of dongle image */
84#define MAX_DATA_BUF (32 * 1024) /* Must be large enough to hold
85 biggest possible glom */
86
87/* Packet alignment for most efficient SDIO (can change based on platform) */
88#ifndef DHD_SDALIGN
89#define DHD_SDALIGN 32
90#endif
91#if !ISPOWEROF2(DHD_SDALIGN)
92#error DHD_SDALIGN is not a power of 2!
93#endif
94
95#ifndef DHD_FIRSTREAD
96#define DHD_FIRSTREAD 32
97#endif
98#if !ISPOWEROF2(DHD_FIRSTREAD)
99#error DHD_FIRSTREAD is not a power of 2!
100#endif
101
102/* Total length of frame header for dongle protocol */
103#define SDPCM_HDRLEN (SDPCM_FRAMETAG_LEN + SDPCM_SWHEADER_LEN)
104#ifdef SDTEST
105#define SDPCM_RESERVE (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN)
106#else
107#define SDPCM_RESERVE (SDPCM_HDRLEN + DHD_SDALIGN)
108#endif
109
110/* Space for header read, limit for data packets */
111#ifndef MAX_HDR_READ
112#define MAX_HDR_READ 32
113#endif
114#if !ISPOWEROF2(MAX_HDR_READ)
115#error MAX_HDR_READ is not a power of 2!
116#endif
117
118#define MAX_RX_DATASZ 2048
119
120/* Maximum milliseconds to wait for F2 to come up */
121#define DHD_WAIT_F2RDY 3000
122
123/* Bump up limit on waiting for HT to account for first startup;
124 * if the image is doing a CRC calculation before programming the PMU
125 * for HT availability, it could take a couple hundred ms more, so
126 * max out at a 1 second (1000000us).
127 */
128#if (PMU_MAX_TRANSITION_DLY <= 1000000)
129#undef PMU_MAX_TRANSITION_DLY
130#define PMU_MAX_TRANSITION_DLY 1000000
131#endif
132
133/* Value for ChipClockCSR during initial setup */
134#define DHD_INIT_CLKCTL1 (SBSDIO_FORCE_HW_CLKREQ_OFF | \
135 SBSDIO_ALP_AVAIL_REQ)
136#define DHD_INIT_CLKCTL2 (SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_FORCE_ALP)
137
138/* Flags for SDH calls */
139#define F2SYNC (SDIO_REQ_4BYTE | SDIO_REQ_FIXED)
140
141/* Packet free applicable unconditionally for sdio and sdspi. Conditional if
142 * bufpool was present for gspi bus.
143 */
144#define PKTFREE2() if ((bus->bus != SPI_BUS) || bus->usebufpool) \
Arend van Spriela30825a2011-03-02 21:18:46 +0100145 pkt_buf_free_skb(pkt);
Arend van Spriel70dfb582011-02-25 16:39:17 +0100146
147/*
148 * Conversion of 802.1D priority to precedence level
149 */
150#define PRIO2PREC(prio) \
151 (((prio) == PRIO_8021D_NONE || (prio) == PRIO_8021D_BE) ? \
152 ((prio^2)) : (prio))
153
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700154DHD_SPINWAIT_SLEEP_INIT(sdioh_spinwait_sleep);
155extern int dhdcdc_set_ioctl(dhd_pub_t *dhd, int ifidx, uint cmd, void *buf,
156 uint len);
157
158#ifdef DHD_DEBUG
159/* Device console log buffer state */
160typedef struct dhd_console {
161 uint count; /* Poll interval msec counter */
162 uint log_addr; /* Log struct address (fixed) */
163 hndrte_log_t log; /* Log struct (host copy) */
164 uint bufsize; /* Size of log buffer */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700165 u8 *buf; /* Log buffer (host copy) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700166 uint last; /* Last buffer read index */
167} dhd_console_t;
168#endif /* DHD_DEBUG */
169
Franky Lincb63e4c2011-04-25 15:45:08 -0700170/* misc chip info needed by some of the routines */
171struct chip_info {
172 u32 chip;
173 u32 chiprev;
174 u32 cccorebase;
175 u32 ccrev;
176 u32 cccaps;
177 u32 buscorebase;
178 u32 buscorerev;
179 u32 buscoretype;
180 u32 ramcorebase;
181 u32 armcorebase;
182 u32 pmurev;
183};
184
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700185/* Private data for SDIO bus interaction */
186typedef struct dhd_bus {
187 dhd_pub_t *dhd;
188
189 bcmsdh_info_t *sdh; /* Handle for BCMSDH calls */
190 si_t *sih; /* Handle for SI calls */
Franky Lincb63e4c2011-04-25 15:45:08 -0700191 struct chip_info *ci; /* Chip info struct */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700192 char *vars; /* Variables (from CIS and/or other) */
193 uint varsz; /* Size of variables buffer */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700194 u32 sbaddr; /* Current SB window pointer (-1, invalid) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700195
196 sdpcmd_regs_t *regs; /* Registers for SDIO core */
197 uint sdpcmrev; /* SDIO core revision */
198 uint armrev; /* CPU core revision */
199 uint ramrev; /* SOCRAM core revision */
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700200 u32 ramsize; /* Size of RAM in SOCRAM (bytes) */
201 u32 orig_ramsize; /* Size of RAM in SOCRAM (bytes) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700202
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700203 u32 bus; /* gSPI or SDIO bus */
204 u32 hostintmask; /* Copy of Host Interrupt Mask */
205 u32 intstatus; /* Intstatus bits (events) pending */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700206 bool dpc_sched; /* Indicates DPC schedule (intrpt rcvd) */
207 bool fcstate; /* State of dongle flow-control */
208
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700209 u16 cl_devid; /* cached devid for dhdsdio_probe_attach() */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700210 char *fw_path; /* module_param: path to firmware image */
211 char *nv_path; /* module_param: path to nvram vars file */
212 const char *nvram_params; /* user specified nvram params. */
213
214 uint blocksize; /* Block size of SDIO transfers */
215 uint roundup; /* Max roundup limit */
216
217 struct pktq txq; /* Queue length used for flow-control */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700218 u8 flowcontrol; /* per prio flow control bitmask */
219 u8 tx_seq; /* Transmit sequence number (next) */
220 u8 tx_max; /* Maximum transmit sequence allowed */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700221
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700222 u8 hdrbuf[MAX_HDR_READ + DHD_SDALIGN];
223 u8 *rxhdr; /* Header of current rx frame (in hdrbuf) */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700224 u16 nextlen; /* Next Read Len from last header */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700225 u8 rx_seq; /* Receive sequence number (expected) */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700226 bool rxskip; /* Skip receive (awaiting NAK ACK) */
227
Arend van Sprielc26b1372010-11-23 14:06:23 +0100228 struct sk_buff *glomd; /* Packet containing glomming descriptor */
229 struct sk_buff *glom; /* Packet chain for glommed superframe */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700230 uint glomerr; /* Glom packet read errors */
231
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700232 u8 *rxbuf; /* Buffer for receiving control packets */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700233 uint rxblen; /* Allocated length of rxbuf */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700234 u8 *rxctl; /* Aligned pointer into rxbuf */
235 u8 *databuf; /* Buffer for receiving big glom packet */
236 u8 *dataptr; /* Aligned pointer into databuf */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700237 uint rxlen; /* Length of valid data in buffer */
238
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700239 u8 sdpcm_ver; /* Bus protocol reported by dongle */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700240
241 bool intr; /* Use interrupts */
242 bool poll; /* Use polling */
243 bool ipend; /* Device interrupt is pending */
244 bool intdis; /* Interrupts disabled by isr */
245 uint intrcount; /* Count of device interrupt callbacks */
246 uint lastintrs; /* Count as of last watchdog timer */
247 uint spurious; /* Count of spurious interrupts */
248 uint pollrate; /* Ticks between device polls */
249 uint polltick; /* Tick counter */
250 uint pollcnt; /* Count of active polls */
251
252#ifdef DHD_DEBUG
253 dhd_console_t console; /* Console output polling support */
254 uint console_addr; /* Console address from shared struct */
255#endif /* DHD_DEBUG */
256
257 uint regfails; /* Count of R_REG/W_REG failures */
258
259 uint clkstate; /* State of sd and backplane clock(s) */
260 bool activity; /* Activity flag for clock down */
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700261 s32 idletime; /* Control for activity timeout */
262 s32 idlecount; /* Activity timeout counter */
263 s32 idleclock; /* How to set bus driver when idle */
264 s32 sd_divisor; /* Speed control to bus driver */
265 s32 sd_mode; /* Mode control to bus driver */
266 s32 sd_rxchain; /* If bcmsdh api accepts PKT chains */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700267 bool use_rxchain; /* If dhd should use PKT chains */
268 bool sleeping; /* Is SDIO bus sleeping? */
269 bool rxflow_mode; /* Rx flow control mode */
270 bool rxflow; /* Is rx flow control on */
271 uint prev_rxlim_hit; /* Is prev rx limit exceeded
272 (per dpc schedule) */
273 bool alp_only; /* Don't use HT clock (ALP only) */
274/* Field to decide if rx of control frames happen in rxbuf or lb-pool */
275 bool usebufpool;
276
277#ifdef SDTEST
278 /* external loopback */
279 bool ext_loop;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700280 u8 loopid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700281
282 /* pktgen configuration */
283 uint pktgen_freq; /* Ticks between bursts */
284 uint pktgen_count; /* Packets to send each burst */
285 uint pktgen_print; /* Bursts between count displays */
286 uint pktgen_total; /* Stop after this many */
287 uint pktgen_minlen; /* Minimum packet data len */
288 uint pktgen_maxlen; /* Maximum packet data len */
289 uint pktgen_mode; /* Configured mode: tx, rx, or echo */
290 uint pktgen_stop; /* Number of tx failures causing stop */
291
292 /* active pktgen fields */
293 uint pktgen_tick; /* Tick counter for bursts */
294 uint pktgen_ptick; /* Burst counter for printing */
295 uint pktgen_sent; /* Number of test packets generated */
296 uint pktgen_rcvd; /* Number of test packets received */
297 uint pktgen_fail; /* Number of failed send attempts */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700298 u16 pktgen_len; /* Length of next packet to send */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700299#endif /* SDTEST */
300
301 /* Some additional counters */
302 uint tx_sderrs; /* Count of tx attempts with sd errors */
303 uint fcqueued; /* Tx packets that got queued */
304 uint rxrtx; /* Count of rtx requests (NAK to dongle) */
305 uint rx_toolong; /* Receive frames too long to receive */
306 uint rxc_errors; /* SDIO errors when reading control frames */
307 uint rx_hdrfail; /* SDIO errors on header reads */
308 uint rx_badhdr; /* Bad received headers (roosync?) */
309 uint rx_badseq; /* Mismatched rx sequence number */
310 uint fc_rcvd; /* Number of flow-control events received */
311 uint fc_xoff; /* Number which turned on flow-control */
312 uint fc_xon; /* Number which turned off flow-control */
313 uint rxglomfail; /* Failed deglom attempts */
314 uint rxglomframes; /* Number of glom frames (superframes) */
315 uint rxglompkts; /* Number of packets from glom frames */
316 uint f2rxhdrs; /* Number of header reads */
317 uint f2rxdata; /* Number of frame data reads */
318 uint f2txdata; /* Number of f2 frame writes */
319 uint f1regdata; /* Number of f1 register accesses */
320
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700321 u8 *ctrl_frame_buf;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700322 u32 ctrl_frame_len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700323 bool ctrl_frame_stat;
324} dhd_bus_t;
325
326/* clkstate */
327#define CLK_NONE 0
328#define CLK_SDONLY 1
329#define CLK_PENDING 2 /* Not used yet */
330#define CLK_AVAIL 3
331
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700332#define DHD_NOPMU(dhd) (false)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700333
334#ifdef DHD_DEBUG
335static int qcount[NUMPRIO];
336static int tx_packets[NUMPRIO];
337#endif /* DHD_DEBUG */
338
339/* Deferred transmit */
340const uint dhd_deferred_tx = 1;
341
342extern uint dhd_watchdog_ms;
343extern void dhd_os_wd_timer(void *bus, uint wdtick);
344
345/* Tx/Rx bounds */
346uint dhd_txbound;
347uint dhd_rxbound;
348uint dhd_txminmax;
349
350/* override the RAM size if possible */
351#define DONGLE_MIN_MEMSIZE (128 * 1024)
352int dhd_dongle_memsize;
353
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700354static bool dhd_alignctl;
355
356static bool sd1idle;
357
358static bool retrydata;
359#define RETRYCHAN(chan) (((chan) == SDPCM_EVENT_CHANNEL) || retrydata)
360
361static const uint watermark = 8;
362static const uint firstread = DHD_FIRSTREAD;
363
364#define HDATLEN (firstread - (SDPCM_HDRLEN))
365
366/* Retry count for register access failures */
367static const uint retry_limit = 2;
368
369/* Force even SD lengths (some host controllers mess up on odd bytes) */
370static bool forcealign;
371
372#define ALIGNMENT 4
373
374#if defined(OOB_INTR_ONLY) && defined(HW_OOB)
375extern void bcmsdh_enable_hw_oob_intr(void *sdh, bool enable);
376#endif
377
378#if defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD)
379#error OOB_INTR_ONLY is NOT working with SDIO_ISR_THREAD
380#endif /* defined(OOB_INTR_ONLY) && defined(SDIO_ISR_THREAD) */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100381#define PKTALIGN(_p, _len, _align) \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700382 do { \
383 uint datalign; \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100384 datalign = (unsigned long)((_p)->data); \
385 datalign = roundup(datalign, (_align)) - datalign; \
386 ASSERT(datalign < (_align)); \
387 ASSERT((_p)->len >= ((_len) + datalign)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700388 if (datalign) \
Arend van Spriel54991ad2010-11-23 14:06:24 +0100389 skb_pull((_p), datalign); \
390 __skb_trim((_p), (_len)); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700391 } while (0)
392
393/* Limit on rounding up frames */
394static const uint max_roundup = 512;
395
396/* Try doing readahead */
397static bool dhd_readahead;
398
399/* To check if there's window offered */
400#define DATAOK(bus) \
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700401 (((u8)(bus->tx_max - bus->tx_seq) != 0) && \
402 (((u8)(bus->tx_max - bus->tx_seq) & 0x80) == 0))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700403
404/* Macros to get register read/write status */
405/* NOTE: these assume a local dhdsdio_bus_t *bus! */
406#define R_SDREG(regvar, regaddr, retryvar) \
407do { \
408 retryvar = 0; \
409 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100410 regvar = R_REG(regaddr); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700411 } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
412 if (retryvar) { \
413 bus->regfails += (retryvar-1); \
414 if (retryvar > retry_limit) { \
415 DHD_ERROR(("%s: FAILED" #regvar "READ, LINE %d\n", \
416 __func__, __LINE__)); \
417 regvar = 0; \
418 } \
419 } \
420} while (0)
421
422#define W_SDREG(regval, regaddr, retryvar) \
423do { \
424 retryvar = 0; \
425 do { \
Arend van Sprielff31c542011-03-01 10:56:54 +0100426 W_REG(regaddr, regval); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700427 } while (bcmsdh_regfail(bus->sdh) && (++retryvar <= retry_limit)); \
428 if (retryvar) { \
429 bus->regfails += (retryvar-1); \
430 if (retryvar > retry_limit) \
431 DHD_ERROR(("%s: FAILED REGISTER WRITE, LINE %d\n", \
432 __func__, __LINE__)); \
433 } \
434} while (0)
435
436#define DHD_BUS SDIO_BUS
437
438#define PKT_AVAILABLE() (intstatus & I_HMB_FRAME_IND)
439
440#define HOSTINTMASK (I_HMB_SW_MASK | I_CHIPACTIVE)
441
442#define GSPI_PR55150_BAILOUT
443
444#ifdef SDTEST
445static void dhdsdio_testrcv(dhd_bus_t *bus, void *pkt, uint seq);
446static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start);
447#endif
448
449#ifdef DHD_DEBUG
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700450static int dhdsdio_checkdied(dhd_bus_t *bus, u8 *data, uint size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700451static int dhdsdio_mem_dump(dhd_bus_t *bus);
452#endif /* DHD_DEBUG */
453static int dhdsdio_download_state(dhd_bus_t *bus, bool enter);
454
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100455static void dhdsdio_release(dhd_bus_t *bus);
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100456static void dhdsdio_release_malloc(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700457static void dhdsdio_disconnect(void *ptr);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700458static bool dhdsdio_chipmatch(u16 chipid);
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100459static bool dhdsdio_probe_attach(dhd_bus_t *bus, void *sdh,
460 void *regsva, u16 devid);
461static bool dhdsdio_probe_malloc(dhd_bus_t *bus, void *sdh);
462static bool dhdsdio_probe_init(dhd_bus_t *bus, void *sdh);
463static void dhdsdio_release_dongle(dhd_bus_t *bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700464
465static uint process_nvram_vars(char *varbuf, uint len);
466
467static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size);
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700468static int dhd_bcmsdh_recv_buf(dhd_bus_t *bus, u32 addr, uint fn,
Arend van Sprielc26b1372010-11-23 14:06:23 +0100469 uint flags, u8 *buf, uint nbytes,
470 struct sk_buff *pkt, bcmsdh_cmplt_fn_t complete,
471 void *handle);
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700472static int dhd_bcmsdh_send_buf(dhd_bus_t *bus, u32 addr, uint fn,
Arend van Sprielc26b1372010-11-23 14:06:23 +0100473 uint flags, u8 *buf, uint nbytes,
474 struct sk_buff *pkt, bcmsdh_cmplt_fn_t complete,
475 void *handle);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700476
Arend van Spriel8da4a3a2011-03-02 21:18:42 +0100477static bool dhdsdio_download_firmware(struct dhd_bus *bus, void *sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700478static int _dhdsdio_download_firmware(struct dhd_bus *bus);
479
480static int dhdsdio_download_code_file(struct dhd_bus *bus, char *image_path);
481static int dhdsdio_download_nvram(struct dhd_bus *bus);
482#ifdef BCMEMBEDIMAGE
483static int dhdsdio_download_code_array(struct dhd_bus *bus);
484#endif
Franky Lincb63e4c2011-04-25 15:45:08 -0700485static int dhdsdio_chip_attach(struct dhd_bus *bus, void *regs);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700486
487static void dhd_dongle_setmemsize(struct dhd_bus *bus, int mem_size)
488{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700489 s32 min_size = DONGLE_MIN_MEMSIZE;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700490 /* Restrict the memsize to user specified limit */
491 DHD_ERROR(("user: Restrict the dongle ram size to %d, min %d\n",
492 dhd_dongle_memsize, min_size));
493 if ((dhd_dongle_memsize > min_size) &&
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700494 (dhd_dongle_memsize < (s32) bus->orig_ramsize))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700495 bus->ramsize = dhd_dongle_memsize;
496}
497
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700498static int dhdsdio_set_siaddr_window(dhd_bus_t *bus, u32 address)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700499{
500 int err = 0;
501 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRLOW,
502 (address >> 8) & SBSDIO_SBADDRLOW_MASK, &err);
503 if (!err)
504 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRMID,
505 (address >> 16) & SBSDIO_SBADDRMID_MASK, &err);
506 if (!err)
507 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SBADDRHIGH,
508 (address >> 24) & SBSDIO_SBADDRHIGH_MASK,
509 &err);
510 return err;
511}
512
513/* Turn backplane clock on or off */
514static int dhdsdio_htclk(dhd_bus_t *bus, bool on, bool pendok)
515{
516 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700517 u8 clkctl, clkreq, devctl;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700518 bcmsdh_info_t *sdh;
519
520 DHD_TRACE(("%s: Enter\n", __func__));
521
522#if defined(OOB_INTR_ONLY)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700523 pendok = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700524#endif
525 clkctl = 0;
526 sdh = bus->sdh;
527
528 if (on) {
529 /* Request HT Avail */
530 clkreq =
531 bus->alp_only ? SBSDIO_ALP_AVAIL_REQ : SBSDIO_HT_AVAIL_REQ;
532
533 if ((bus->sih->chip == BCM4329_CHIP_ID)
534 && (bus->sih->chiprev == 0))
535 clkreq |= SBSDIO_FORCE_ALP;
536
537 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
538 clkreq, &err);
539 if (err) {
540 DHD_ERROR(("%s: HT Avail request error: %d\n",
541 __func__, err));
542 return BCME_ERROR;
543 }
544
545 if (pendok && ((bus->sih->buscoretype == PCMCIA_CORE_ID)
546 && (bus->sih->buscorerev == 9))) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700547 u32 dummy, retries;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700548 R_SDREG(dummy, &bus->regs->clockctlstatus, retries);
549 }
550
551 /* Check current status */
552 clkctl =
553 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
554 &err);
555 if (err) {
556 DHD_ERROR(("%s: HT Avail read error: %d\n",
557 __func__, err));
558 return BCME_ERROR;
559 }
560
561 /* Go to pending and await interrupt if appropriate */
562 if (!SBSDIO_CLKAV(clkctl, bus->alp_only) && pendok) {
563 /* Allow only clock-available interrupt */
564 devctl =
565 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
566 &err);
567 if (err) {
568 DHD_ERROR(("%s: Devctl error setting CA: %d\n",
569 __func__, err));
570 return BCME_ERROR;
571 }
572
573 devctl |= SBSDIO_DEVCTL_CA_INT_ONLY;
574 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
575 devctl, &err);
576 DHD_INFO(("CLKCTL: set PENDING\n"));
577 bus->clkstate = CLK_PENDING;
578
Roland Vossena1c5ad82011-04-11 15:16:24 +0200579 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700580 } else if (bus->clkstate == CLK_PENDING) {
581 /* Cancel CA-only interrupt filter */
582 devctl =
583 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
584 &err);
585 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
586 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
587 devctl, &err);
588 }
589
590 /* Otherwise, wait here (polling) for HT Avail */
591 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
592 SPINWAIT_SLEEP(sdioh_spinwait_sleep,
593 ((clkctl =
594 bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
595 SBSDIO_FUNC1_CHIPCLKCSR,
596 &err)),
597 !SBSDIO_CLKAV(clkctl, bus->alp_only)),
598 PMU_MAX_TRANSITION_DLY);
599 }
600 if (err) {
601 DHD_ERROR(("%s: HT Avail request error: %d\n",
602 __func__, err));
603 return BCME_ERROR;
604 }
605 if (!SBSDIO_CLKAV(clkctl, bus->alp_only)) {
606 DHD_ERROR(("%s: HT Avail timeout (%d): clkctl 0x%02x\n",
607 __func__, PMU_MAX_TRANSITION_DLY, clkctl));
608 return BCME_ERROR;
609 }
610
611 /* Mark clock available */
612 bus->clkstate = CLK_AVAIL;
613 DHD_INFO(("CLKCTL: turned ON\n"));
614
615#if defined(DHD_DEBUG)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700616 if (bus->alp_only == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700617#if !defined(BCMLXSDMMC)
618 if (!SBSDIO_ALPONLY(clkctl)) {
619 DHD_ERROR(("%s: HT Clock, when ALP Only\n",
620 __func__));
621 }
622#endif /* !defined(BCMLXSDMMC) */
623 } else {
624 if (SBSDIO_ALPONLY(clkctl)) {
625 DHD_ERROR(("%s: HT Clock should be on.\n",
626 __func__));
627 }
628 }
629#endif /* defined (DHD_DEBUG) */
630
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700631 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700632 } else {
633 clkreq = 0;
634
635 if (bus->clkstate == CLK_PENDING) {
636 /* Cancel CA-only interrupt filter */
637 devctl =
638 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
639 &err);
640 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
641 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
642 devctl, &err);
643 }
644
645 bus->clkstate = CLK_SDONLY;
646 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
647 clkreq, &err);
648 DHD_INFO(("CLKCTL: turned OFF\n"));
649 if (err) {
650 DHD_ERROR(("%s: Failed access turning clock off: %d\n",
651 __func__, err));
652 return BCME_ERROR;
653 }
654 }
Roland Vossena1c5ad82011-04-11 15:16:24 +0200655 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700656}
657
658/* Change idle/active SD state */
659static int dhdsdio_sdclk(dhd_bus_t *bus, bool on)
660{
661 int err;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -0700662 s32 iovalue;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700663
664 DHD_TRACE(("%s: Enter\n", __func__));
665
666 if (on) {
667 if (bus->idleclock == DHD_IDLE_STOP) {
668 /* Turn on clock and restore mode */
669 iovalue = 1;
670 err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700671 &iovalue, sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700672 if (err) {
673 DHD_ERROR(("%s: error enabling sd_clock: %d\n",
674 __func__, err));
675 return BCME_ERROR;
676 }
677
678 iovalue = bus->sd_mode;
679 err = bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700680 &iovalue, sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700681 if (err) {
682 DHD_ERROR(("%s: error changing sd_mode: %d\n",
683 __func__, err));
684 return BCME_ERROR;
685 }
686 } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
687 /* Restore clock speed */
688 iovalue = bus->sd_divisor;
689 err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700690 &iovalue, sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700691 if (err) {
692 DHD_ERROR(("%s: error restoring sd_divisor: %d\n",
693 __func__, err));
694 return BCME_ERROR;
695 }
696 }
697 bus->clkstate = CLK_SDONLY;
698 } else {
699 /* Stop or slow the SD clock itself */
700 if ((bus->sd_divisor == -1) || (bus->sd_mode == -1)) {
701 DHD_TRACE(("%s: can't idle clock, divisor %d mode %d\n",
702 __func__, bus->sd_divisor, bus->sd_mode));
703 return BCME_ERROR;
704 }
705 if (bus->idleclock == DHD_IDLE_STOP) {
706 if (sd1idle) {
707 /* Change to SD1 mode and turn off clock */
708 iovalue = 1;
709 err =
710 bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL,
711 0, &iovalue,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700712 sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700713 if (err) {
714 DHD_ERROR(("%s: error changing sd_clock: %d\n",
715 __func__, err));
716 return BCME_ERROR;
717 }
718 }
719
720 iovalue = 0;
721 err = bcmsdh_iovar_op(bus->sdh, "sd_clock", NULL, 0,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700722 &iovalue, sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700723 if (err) {
724 DHD_ERROR(("%s: error disabling sd_clock: %d\n",
725 __func__, err));
726 return BCME_ERROR;
727 }
728 } else if (bus->idleclock != DHD_IDLE_ACTIVE) {
729 /* Set divisor to idle value */
730 iovalue = bus->idleclock;
731 err = bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700732 &iovalue, sizeof(iovalue), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700733 if (err) {
734 DHD_ERROR(("%s: error changing sd_divisor: %d\n",
735 __func__, err));
736 return BCME_ERROR;
737 }
738 }
739 bus->clkstate = CLK_NONE;
740 }
741
Roland Vossena1c5ad82011-04-11 15:16:24 +0200742 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700743}
744
745/* Transition SD and backplane clock readiness */
746static int dhdsdio_clkctl(dhd_bus_t *bus, uint target, bool pendok)
747{
748#ifdef DHD_DEBUG
749 uint oldstate = bus->clkstate;
750#endif /* DHD_DEBUG */
751
752 DHD_TRACE(("%s: Enter\n", __func__));
753
754 /* Early exit if we're already there */
755 if (bus->clkstate == target) {
756 if (target == CLK_AVAIL) {
757 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700758 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700759 }
Roland Vossena1c5ad82011-04-11 15:16:24 +0200760 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700761 }
762
763 switch (target) {
764 case CLK_AVAIL:
765 /* Make sure SD clock is available */
766 if (bus->clkstate == CLK_NONE)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700767 dhdsdio_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700768 /* Now request HT Avail on the backplane */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700769 dhdsdio_htclk(bus, true, pendok);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700770 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700771 bus->activity = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700772 break;
773
774 case CLK_SDONLY:
775 /* Remove HT request, or bring up SD clock */
776 if (bus->clkstate == CLK_NONE)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700777 dhdsdio_sdclk(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700778 else if (bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700779 dhdsdio_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700780 else
781 DHD_ERROR(("dhdsdio_clkctl: request for %d -> %d\n",
782 bus->clkstate, target));
783 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
784 break;
785
786 case CLK_NONE:
787 /* Make sure to remove HT request */
788 if (bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700789 dhdsdio_htclk(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700790 /* Now remove the SD clock */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700791 dhdsdio_sdclk(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700792 dhd_os_wd_timer(bus->dhd, 0);
793 break;
794 }
795#ifdef DHD_DEBUG
796 DHD_INFO(("dhdsdio_clkctl: %d -> %d\n", oldstate, bus->clkstate));
797#endif /* DHD_DEBUG */
798
Roland Vossena1c5ad82011-04-11 15:16:24 +0200799 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700800}
801
802int dhdsdio_bussleep(dhd_bus_t *bus, bool sleep)
803{
804 bcmsdh_info_t *sdh = bus->sdh;
805 sdpcmd_regs_t *regs = bus->regs;
806 uint retries = 0;
807
808 DHD_INFO(("dhdsdio_bussleep: request %s (currently %s)\n",
809 (sleep ? "SLEEP" : "WAKE"),
810 (bus->sleeping ? "SLEEP" : "WAKE")));
811
812 /* Done if we're already in the requested state */
813 if (sleep == bus->sleeping)
Roland Vossena1c5ad82011-04-11 15:16:24 +0200814 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700815
816 /* Going to sleep: set the alarm and turn off the lights... */
817 if (sleep) {
818 /* Don't sleep if something is pending */
819 if (bus->dpc_sched || bus->rxskip || pktq_len(&bus->txq))
820 return BCME_BUSY;
821
822 /* Disable SDIO interrupts (no longer interested) */
823 bcmsdh_intr_disable(bus->sdh);
824
825 /* Make sure the controller has the bus up */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700826 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700827
828 /* Tell device to start using OOB wakeup */
829 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
830 if (retries > retry_limit)
831 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
832
833 /* Turn off our contribution to the HT clock request */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700834 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700835
836 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
837 SBSDIO_FORCE_HW_CLKREQ_OFF, NULL);
838
839 /* Isolate the bus */
840 if (bus->sih->chip != BCM4329_CHIP_ID
841 && bus->sih->chip != BCM4319_CHIP_ID) {
842 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
843 SBSDIO_DEVCTL_PADS_ISO, NULL);
844 }
845
846 /* Change state */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700847 bus->sleeping = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700848
849 } else {
850 /* Waking up: bus power up is ok, set local state */
851
852 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
853 0, NULL);
854
855 /* Force pad isolation off if possible
856 (in case power never toggled) */
857 if ((bus->sih->buscoretype == PCMCIA_CORE_ID)
858 && (bus->sih->buscorerev >= 10))
859 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, 0,
860 NULL);
861
862 /* Make sure the controller has the bus up */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700863 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700864
865 /* Send misc interrupt to indicate OOB not needed */
866 W_SDREG(0, &regs->tosbmailboxdata, retries);
867 if (retries <= retry_limit)
868 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
869
870 if (retries > retry_limit)
871 DHD_ERROR(("CANNOT SIGNAL CHIP TO CLEAR OOB!!\n"));
872
873 /* Make sure we have SD bus access */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700874 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700875
876 /* Change state */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700877 bus->sleeping = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700878
879 /* Enable interrupts again */
880 if (bus->intr && (bus->dhd->busstate == DHD_BUS_DATA)) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700881 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700882 bcmsdh_intr_enable(bus->sdh);
883 }
884 }
885
Roland Vossena1c5ad82011-04-11 15:16:24 +0200886 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700887}
888
889#if defined(OOB_INTR_ONLY)
890void dhd_enable_oob_intr(struct dhd_bus *bus, bool enable)
891{
892#if defined(HW_OOB)
893 bcmsdh_enable_hw_oob_intr(bus->sdh, enable);
894#else
895 sdpcmd_regs_t *regs = bus->regs;
896 uint retries = 0;
897
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700898 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700899 if (enable == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700900
901 /* Tell device to start using OOB wakeup */
902 W_SDREG(SMB_USE_OOB, &regs->tosbmailbox, retries);
903 if (retries > retry_limit)
904 DHD_ERROR(("CANNOT SIGNAL CHIP, WILL NOT WAKE UP!!\n"));
905
906 } else {
907 /* Send misc interrupt to indicate OOB not needed */
908 W_SDREG(0, &regs->tosbmailboxdata, retries);
909 if (retries <= retry_limit)
910 W_SDREG(SMB_DEV_INT, &regs->tosbmailbox, retries);
911 }
912
913 /* Turn off our contribution to the HT clock request */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700914 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700915#endif /* !defined(HW_OOB) */
916}
917#endif /* defined(OOB_INTR_ONLY) */
918
919#define BUS_WAKE(bus) \
920 do { \
921 if ((bus)->sleeping) \
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -0700922 dhdsdio_bussleep((bus), false); \
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700923 } while (0);
924
925/* Writes a HW/SW header into the packet and sends it. */
926/* Assumes: (a) header space already there, (b) caller holds lock */
Arend van Sprielc26b1372010-11-23 14:06:23 +0100927static int dhdsdio_txpkt(dhd_bus_t *bus, struct sk_buff *pkt, uint chan,
928 bool free_pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700929{
930 int ret;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -0700931 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -0700932 u16 len, pad = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -0700933 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700934 uint retries = 0;
935 bcmsdh_info_t *sdh;
Arend van Sprielc26b1372010-11-23 14:06:23 +0100936 struct sk_buff *new;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700937 int i;
938
939 DHD_TRACE(("%s: Enter\n", __func__));
940
941 sdh = bus->sdh;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700942
943 if (bus->dhd->dongle_reset) {
944 ret = BCME_NOTREADY;
945 goto done;
946 }
947
Arend van Spriel54991ad2010-11-23 14:06:24 +0100948 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700949
950 /* Add alignment padding, allocate new packet if needed */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -0700951 pad = ((unsigned long)frame % DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700952 if (pad) {
Arend van Spriel3be727c2010-11-23 22:20:30 +0100953 if (skb_headroom(pkt) < pad) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700954 DHD_INFO(("%s: insufficient headroom %d for %d pad\n",
Arend van Spriel3be727c2010-11-23 22:20:30 +0100955 __func__, skb_headroom(pkt), pad));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700956 bus->dhd->tx_realloc++;
Arend van Spriela30825a2011-03-02 21:18:46 +0100957 new = pkt_buf_get_skb(pkt->len + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700958 if (!new) {
959 DHD_ERROR(("%s: couldn't allocate new %d-byte "
960 "packet\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +0100961 __func__, pkt->len + DHD_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700962 ret = BCME_NOMEM;
963 goto done;
964 }
965
Arend van Spriel3c9d4c32011-03-02 21:18:48 +0100966 PKTALIGN(new, pkt->len, DHD_SDALIGN);
Stanislav Fomichev02160692011-02-15 01:05:10 +0300967 memcpy(new->data, pkt->data, pkt->len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700968 if (free_pkt)
Arend van Spriela30825a2011-03-02 21:18:46 +0100969 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700970 /* free the pkt if canned one is not used */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -0700971 free_pkt = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700972 pkt = new;
Arend van Spriel54991ad2010-11-23 14:06:24 +0100973 frame = (u8 *) (pkt->data);
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -0700974 ASSERT(((unsigned long)frame % DHD_SDALIGN) == 0);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700975 pad = 0;
976 } else {
Arend van Sprielc303ecb2010-11-18 20:46:43 +0100977 skb_push(pkt, pad);
Arend van Spriel54991ad2010-11-23 14:06:24 +0100978 frame = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700979
Arend van Spriel54991ad2010-11-23 14:06:24 +0100980 ASSERT((pad + SDPCM_HDRLEN) <= (int)(pkt->len));
Brett Rudley9249ede2010-11-30 20:09:49 -0800981 memset(frame, 0, pad + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700982 }
983 }
984 ASSERT(pad < DHD_SDALIGN);
985
986 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Arend van Spriel54991ad2010-11-23 14:06:24 +0100987 len = (u16) (pkt->len);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +0300988 *(u16 *) frame = cpu_to_le16(len);
989 *(((u16 *) frame) + 1) = cpu_to_le16(~len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700990
991 /* Software tag: channel, sequence number, data offset */
992 swheader =
993 ((chan << SDPCM_CHANNEL_SHIFT) & SDPCM_CHANNEL_MASK) | bus->tx_seq |
994 (((pad +
995 SDPCM_HDRLEN) << SDPCM_DOFFSET_SHIFT) & SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +0300996
997 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
998 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -0700999
1000#ifdef DHD_DEBUG
Arend van Spriel54991ad2010-11-23 14:06:24 +01001001 tx_packets[pkt->priority]++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001002 if (DHD_BYTES_ON() &&
1003 (((DHD_CTL_ON() && (chan == SDPCM_CONTROL_CHANNEL)) ||
1004 (DHD_DATA_ON() && (chan != SDPCM_CONTROL_CHANNEL))))) {
1005 prhex("Tx Frame", frame, len);
1006 } else if (DHD_HDRS_ON()) {
Greg Kroah-Hartmanb61640d2010-10-08 12:37:39 -07001007 prhex("TxHdr", frame, min_t(u16, len, 16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001008 }
1009#endif
1010
1011 /* Raise len to next SDIO block to eliminate tail command */
1012 if (bus->roundup && bus->blocksize && (len > bus->blocksize)) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001013 u16 pad = bus->blocksize - (len % bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001014 if ((pad <= bus->roundup) && (pad < bus->blocksize))
1015#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001016 if (pad <= skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001017#endif /* NOTUSED */
1018 len += pad;
1019 } else if (len % DHD_SDALIGN) {
1020 len += DHD_SDALIGN - (len % DHD_SDALIGN);
1021 }
1022
1023 /* Some controllers have trouble with odd bytes -- round to even */
1024 if (forcealign && (len & (ALIGNMENT - 1))) {
1025#ifdef NOTUSED
Arend van Spriel3be727c2010-11-23 22:20:30 +01001026 if (skb_tailroom(pkt))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001027#endif
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001028 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001029#ifdef NOTUSED
1030 else
1031 DHD_ERROR(("%s: sending unrounded %d-byte packet\n",
1032 __func__, len));
1033#endif
1034 }
1035
1036 do {
1037 ret =
1038 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
1039 F2SYNC, frame, len, pkt, NULL, NULL);
1040 bus->f2txdata++;
1041 ASSERT(ret != BCME_PENDING);
1042
1043 if (ret < 0) {
1044 /* On failure, abort the command
1045 and terminate the frame */
1046 DHD_INFO(("%s: sdio error %d, abort command and "
1047 "terminate frame.\n", __func__, ret));
1048 bus->tx_sderrs++;
1049
1050 bcmsdh_abort(sdh, SDIO_FUNC_2);
1051 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
1052 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
1053 NULL);
1054 bus->f1regdata++;
1055
1056 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001057 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001058 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1059 SBSDIO_FUNC1_WFRAMEBCHI,
1060 NULL);
1061 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1062 SBSDIO_FUNC1_WFRAMEBCLO,
1063 NULL);
1064 bus->f1regdata += 2;
1065 if ((hi == 0) && (lo == 0))
1066 break;
1067 }
1068
1069 }
1070 if (ret == 0)
1071 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1072
1073 } while ((ret < 0) && retrydata && retries++ < TXRETRIES);
1074
1075done:
1076 /* restore pkt buffer pointer before calling tx complete routine */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001077 skb_pull(pkt, SDPCM_HDRLEN + pad);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001078 dhd_os_sdunlock(bus->dhd);
1079 dhd_txcomplete(bus->dhd, pkt, ret != 0);
1080 dhd_os_sdlock(bus->dhd);
1081
1082 if (free_pkt)
Arend van Spriela30825a2011-03-02 21:18:46 +01001083 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001084
1085 return ret;
1086}
1087
Arend van Sprielc26b1372010-11-23 14:06:23 +01001088int dhd_bus_txdata(struct dhd_bus *bus, struct sk_buff *pkt)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001089{
1090 int ret = BCME_ERROR;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001091 uint datalen, prec;
1092
1093 DHD_TRACE(("%s: Enter\n", __func__));
1094
Arend van Spriel54991ad2010-11-23 14:06:24 +01001095 datalen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001096
1097#ifdef SDTEST
1098 /* Push the test header if doing loopback */
1099 if (bus->ext_loop) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001100 u8 *data;
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001101 skb_push(pkt, SDPCM_TEST_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001102 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001103 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001104 *data++ = (u8) bus->loopid++;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001105 *data++ = (datalen >> 0);
1106 *data++ = (datalen >> 8);
1107 datalen += SDPCM_TEST_HDRLEN;
1108 }
1109#endif /* SDTEST */
1110
1111 /* Add space for the header */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001112 skb_push(pkt, SDPCM_HDRLEN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001113 ASSERT(IS_ALIGNED((unsigned long)(pkt->data), 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001114
Arend van Spriel54991ad2010-11-23 14:06:24 +01001115 prec = PRIO2PREC((pkt->priority & PRIOMASK));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001116
1117 /* Check for existing queue, current flow-control,
1118 pending event, or pending clock */
1119 if (dhd_deferred_tx || bus->fcstate || pktq_len(&bus->txq)
1120 || bus->dpc_sched || (!DATAOK(bus))
1121 || (bus->flowcontrol & NBITVAL(prec))
1122 || (bus->clkstate != CLK_AVAIL)) {
1123 DHD_TRACE(("%s: deferring pktq len %d\n", __func__,
1124 pktq_len(&bus->txq)));
1125 bus->fcqueued++;
1126
1127 /* Priority based enq */
1128 dhd_os_sdlock_txq(bus->dhd);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001129 if (dhd_prec_enq(bus->dhd, &bus->txq, pkt, prec) == false) {
Arend van Sprielc303ecb2010-11-18 20:46:43 +01001130 skb_pull(pkt, SDPCM_HDRLEN);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001131 dhd_txcomplete(bus->dhd, pkt, false);
Arend van Spriela30825a2011-03-02 21:18:46 +01001132 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001133 DHD_ERROR(("%s: out of bus->txq !!!\n", __func__));
1134 ret = BCME_NORESOURCE;
1135 } else {
Roland Vossena1c5ad82011-04-11 15:16:24 +02001136 ret = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001137 }
1138 dhd_os_sdunlock_txq(bus->dhd);
1139
Grant Grundler7c316072011-03-09 15:04:15 -08001140 if (pktq_len(&bus->txq) >= TXHI)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001141 dhd_txflowcontrol(bus->dhd, 0, ON);
1142
1143#ifdef DHD_DEBUG
1144 if (pktq_plen(&bus->txq, prec) > qcount[prec])
1145 qcount[prec] = pktq_plen(&bus->txq, prec);
1146#endif
1147 /* Schedule DPC if needed to send queued packet(s) */
1148 if (dhd_deferred_tx && !bus->dpc_sched) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001149 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001150 dhd_sched_dpc(bus->dhd);
1151 }
1152 } else {
1153 /* Lock: we're about to use shared data/code (and SDIO) */
1154 dhd_os_sdlock(bus->dhd);
1155
1156 /* Otherwise, send it now */
1157 BUS_WAKE(bus);
1158 /* Make sure back plane ht clk is on, no pending allowed */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001159 dhdsdio_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001160
1161#ifndef SDTEST
1162 DHD_TRACE(("%s: calling txpkt\n", __func__));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001163 ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001164#else
1165 ret = dhdsdio_txpkt(bus, pkt,
1166 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001167 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001168#endif
1169 if (ret)
1170 bus->dhd->tx_errors++;
1171 else
1172 bus->dhd->dstats.tx_bytes += datalen;
1173
1174 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001175 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001176 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001177 }
1178
1179 dhd_os_sdunlock(bus->dhd);
1180 }
1181
1182 return ret;
1183}
1184
1185static uint dhdsdio_sendfromq(dhd_bus_t *bus, uint maxframes)
1186{
Arend van Sprielc26b1372010-11-23 14:06:23 +01001187 struct sk_buff *pkt;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001188 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001189 uint retries = 0;
1190 int ret = 0, prec_out;
1191 uint cnt = 0;
1192 uint datalen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001193 u8 tx_prec_map;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001194
1195 dhd_pub_t *dhd = bus->dhd;
1196 sdpcmd_regs_t *regs = bus->regs;
1197
1198 DHD_TRACE(("%s: Enter\n", __func__));
1199
1200 tx_prec_map = ~bus->flowcontrol;
1201
1202 /* Send frames until the limit or some other event */
1203 for (cnt = 0; (cnt < maxframes) && DATAOK(bus); cnt++) {
1204 dhd_os_sdlock_txq(bus->dhd);
1205 pkt = pktq_mdeq(&bus->txq, tx_prec_map, &prec_out);
1206 if (pkt == NULL) {
1207 dhd_os_sdunlock_txq(bus->dhd);
1208 break;
1209 }
1210 dhd_os_sdunlock_txq(bus->dhd);
Arend van Spriel54991ad2010-11-23 14:06:24 +01001211 datalen = pkt->len - SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001212
1213#ifndef SDTEST
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001214 ret = dhdsdio_txpkt(bus, pkt, SDPCM_DATA_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001215#else
1216 ret = dhdsdio_txpkt(bus, pkt,
1217 (bus->ext_loop ? SDPCM_TEST_CHANNEL :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001218 SDPCM_DATA_CHANNEL), true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001219#endif
1220 if (ret)
1221 bus->dhd->tx_errors++;
1222 else
1223 bus->dhd->dstats.tx_bytes += datalen;
1224
1225 /* In poll mode, need to check for other events */
1226 if (!bus->intr && cnt) {
1227 /* Check device status, signal pending interrupt */
1228 R_SDREG(intstatus, &regs->intstatus, retries);
1229 bus->f2txdata++;
1230 if (bcmsdh_regfail(bus->sdh))
1231 break;
1232 if (intstatus & bus->hostintmask)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001233 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001234 }
1235 }
1236
1237 /* Deflow-control stack if needed */
Grant Grundler7c316072011-03-09 15:04:15 -08001238 if (dhd->up && (dhd->busstate == DHD_BUS_DATA) &&
Grant Grundler26a71a42011-03-09 10:41:25 -08001239 dhd->txoff && (pktq_len(&bus->txq) < TXLOW))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001240 dhd_txflowcontrol(dhd, 0, OFF);
1241
1242 return cnt;
1243}
1244
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07001245int dhd_bus_txctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001246{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001247 u8 *frame;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07001248 u16 len;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001249 u32 swheader;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001250 uint retries = 0;
1251 bcmsdh_info_t *sdh = bus->sdh;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001252 u8 doff = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001253 int ret = -1;
1254 int i;
1255
1256 DHD_TRACE(("%s: Enter\n", __func__));
1257
1258 if (bus->dhd->dongle_reset)
1259 return -EIO;
1260
1261 /* Back the pointer to make a room for bus header */
1262 frame = msg - SDPCM_HDRLEN;
1263 len = (msglen += SDPCM_HDRLEN);
1264
1265 /* Add alignment padding (optional for ctl frames) */
1266 if (dhd_alignctl) {
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001267 doff = ((unsigned long)frame % DHD_SDALIGN);
Jason Cooper9b890322010-09-30 15:15:39 -04001268 if (doff) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001269 frame -= doff;
1270 len += doff;
1271 msglen += doff;
Brett Rudley9249ede2010-11-30 20:09:49 -08001272 memset(frame, 0, doff + SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001273 }
1274 ASSERT(doff < DHD_SDALIGN);
1275 }
1276 doff += SDPCM_HDRLEN;
1277
1278 /* Round send length to next SDIO block */
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 len += pad;
1283 } else if (len % DHD_SDALIGN) {
1284 len += DHD_SDALIGN - (len % DHD_SDALIGN);
1285 }
1286
1287 /* Satisfy length-alignment requirements */
1288 if (forcealign && (len & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07001289 len = roundup(len, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001290
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07001291 ASSERT(IS_ALIGNED((unsigned long)frame, 2));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001292
1293 /* Need to lock here to protect txseq and SDIO tx calls */
1294 dhd_os_sdlock(bus->dhd);
1295
1296 BUS_WAKE(bus);
1297
1298 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001299 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001300
1301 /* Hardware tag: 2 byte len followed by 2 byte ~len check (all LE) */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001302 *(u16 *) frame = cpu_to_le16((u16) msglen);
1303 *(((u16 *) frame) + 1) = cpu_to_le16(~msglen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001304
1305 /* Software tag: channel, sequence number, data offset */
1306 swheader =
1307 ((SDPCM_CONTROL_CHANNEL << SDPCM_CHANNEL_SHIFT) &
1308 SDPCM_CHANNEL_MASK)
1309 | bus->tx_seq | ((doff << SDPCM_DOFFSET_SHIFT) &
1310 SDPCM_DOFFSET_MASK);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03001311 put_unaligned_le32(swheader, frame + SDPCM_FRAMETAG_LEN);
1312 put_unaligned_le32(0, frame + SDPCM_FRAMETAG_LEN + sizeof(swheader));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001313
1314 if (!DATAOK(bus)) {
1315 DHD_INFO(("%s: No bus credit bus->tx_max %d, bus->tx_seq %d\n",
1316 __func__, bus->tx_max, bus->tx_seq));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001317 bus->ctrl_frame_stat = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001318 /* Send from dpc */
1319 bus->ctrl_frame_buf = frame;
1320 bus->ctrl_frame_len = len;
1321
1322 dhd_wait_for_event(bus->dhd, &bus->ctrl_frame_stat);
1323
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001324 if (bus->ctrl_frame_stat == false) {
1325 DHD_INFO(("%s: ctrl_frame_stat == false\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001326 ret = 0;
1327 } else {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001328 DHD_INFO(("%s: ctrl_frame_stat == true\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001329 ret = -1;
1330 }
1331 }
1332
1333 if (ret == -1) {
1334#ifdef DHD_DEBUG
1335 if (DHD_BYTES_ON() && DHD_CTL_ON())
1336 prhex("Tx Frame", frame, len);
1337 else if (DHD_HDRS_ON())
Greg Kroah-Hartmanb61640d2010-10-08 12:37:39 -07001338 prhex("TxHdr", frame, min_t(u16, len, 16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001339#endif
1340
1341 do {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001342 bus->ctrl_frame_stat = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001343 ret =
1344 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh),
1345 SDIO_FUNC_2, F2SYNC, frame, len,
1346 NULL, NULL, NULL);
1347
1348 ASSERT(ret != BCME_PENDING);
1349
1350 if (ret < 0) {
1351 /* On failure, abort the command and
1352 terminate the frame */
1353 DHD_INFO(("%s: sdio error %d, abort command and terminate frame.\n",
1354 __func__, ret));
1355 bus->tx_sderrs++;
1356
1357 bcmsdh_abort(sdh, SDIO_FUNC_2);
1358
1359 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
1360 SBSDIO_FUNC1_FRAMECTRL,
1361 SFC_WF_TERM, NULL);
1362 bus->f1regdata++;
1363
1364 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001365 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001366 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1367 SBSDIO_FUNC1_WFRAMEBCHI,
1368 NULL);
1369 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
1370 SBSDIO_FUNC1_WFRAMEBCLO,
1371 NULL);
1372 bus->f1regdata += 2;
1373 if ((hi == 0) && (lo == 0))
1374 break;
1375 }
1376
1377 }
1378 if (ret == 0) {
1379 bus->tx_seq =
1380 (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
1381 }
1382 } while ((ret < 0) && retries++ < TXRETRIES);
1383 }
1384
1385 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001386 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001387 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001388 }
1389
1390 dhd_os_sdunlock(bus->dhd);
1391
1392 if (ret)
1393 bus->dhd->tx_ctlerrs++;
1394 else
1395 bus->dhd->tx_ctlpkts++;
1396
1397 return ret ? -EIO : 0;
1398}
1399
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07001400int dhd_bus_rxctl(struct dhd_bus *bus, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001401{
1402 int timeleft;
1403 uint rxlen = 0;
1404 bool pending;
1405
1406 DHD_TRACE(("%s: Enter\n", __func__));
1407
1408 if (bus->dhd->dongle_reset)
1409 return -EIO;
1410
1411 /* Wait until control frame is available */
1412 timeleft = dhd_os_ioctl_resp_wait(bus->dhd, &bus->rxlen, &pending);
1413
1414 dhd_os_sdlock(bus->dhd);
1415 rxlen = bus->rxlen;
Stanislav Fomichev02160692011-02-15 01:05:10 +03001416 memcpy(msg, bus->rxctl, min(msglen, rxlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001417 bus->rxlen = 0;
1418 dhd_os_sdunlock(bus->dhd);
1419
1420 if (rxlen) {
1421 DHD_CTL(("%s: resumed on rxctl frame, got %d expected %d\n",
1422 __func__, rxlen, msglen));
1423 } else if (timeleft == 0) {
1424 DHD_ERROR(("%s: resumed on timeout\n", __func__));
1425#ifdef DHD_DEBUG
1426 dhd_os_sdlock(bus->dhd);
1427 dhdsdio_checkdied(bus, NULL, 0);
1428 dhd_os_sdunlock(bus->dhd);
1429#endif /* DHD_DEBUG */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07001430 } else if (pending == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001431 DHD_CTL(("%s: cancelled\n", __func__));
1432 return -ERESTARTSYS;
1433 } else {
1434 DHD_CTL(("%s: resumed for unknown reason?\n", __func__));
1435#ifdef DHD_DEBUG
1436 dhd_os_sdlock(bus->dhd);
1437 dhdsdio_checkdied(bus, NULL, 0);
1438 dhd_os_sdunlock(bus->dhd);
1439#endif /* DHD_DEBUG */
1440 }
1441
1442 if (rxlen)
1443 bus->dhd->rx_ctlpkts++;
1444 else
1445 bus->dhd->rx_ctlerrs++;
1446
Jason Coopere9887c92010-10-06 10:08:02 -04001447 return rxlen ? (int)rxlen : -ETIMEDOUT;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001448}
1449
1450/* IOVar table */
1451enum {
1452 IOV_INTR = 1,
1453 IOV_POLLRATE,
1454 IOV_SDREG,
1455 IOV_SBREG,
1456 IOV_SDCIS,
1457 IOV_MEMBYTES,
1458 IOV_MEMSIZE,
1459#ifdef DHD_DEBUG
1460 IOV_CHECKDIED,
1461#endif
1462 IOV_DOWNLOAD,
1463 IOV_FORCEEVEN,
1464 IOV_SDIOD_DRIVE,
1465 IOV_READAHEAD,
1466 IOV_SDRXCHAIN,
1467 IOV_ALIGNCTL,
1468 IOV_SDALIGN,
1469 IOV_DEVRESET,
1470 IOV_CPU,
1471#ifdef SDTEST
1472 IOV_PKTGEN,
1473 IOV_EXTLOOP,
1474#endif /* SDTEST */
1475 IOV_SPROM,
1476 IOV_TXBOUND,
1477 IOV_RXBOUND,
1478 IOV_TXMINMAX,
1479 IOV_IDLETIME,
1480 IOV_IDLECLOCK,
1481 IOV_SD1IDLE,
1482 IOV_SLEEP,
1483 IOV_VARS
1484};
1485
1486const bcm_iovar_t dhdsdio_iovars[] = {
1487 {"intr", IOV_INTR, 0, IOVT_BOOL, 0},
1488 {"sleep", IOV_SLEEP, 0, IOVT_BOOL, 0},
1489 {"pollrate", IOV_POLLRATE, 0, IOVT_UINT32, 0},
1490 {"idletime", IOV_IDLETIME, 0, IOVT_INT32, 0},
1491 {"idleclock", IOV_IDLECLOCK, 0, IOVT_INT32, 0},
1492 {"sd1idle", IOV_SD1IDLE, 0, IOVT_BOOL, 0},
1493 {"membytes", IOV_MEMBYTES, 0, IOVT_BUFFER, 2 * sizeof(int)},
1494 {"memsize", IOV_MEMSIZE, 0, IOVT_UINT32, 0},
1495 {"download", IOV_DOWNLOAD, 0, IOVT_BOOL, 0},
1496 {"vars", IOV_VARS, 0, IOVT_BUFFER, 0},
1497 {"sdiod_drive", IOV_SDIOD_DRIVE, 0, IOVT_UINT32, 0},
1498 {"readahead", IOV_READAHEAD, 0, IOVT_BOOL, 0},
1499 {"sdrxchain", IOV_SDRXCHAIN, 0, IOVT_BOOL, 0},
1500 {"alignctl", IOV_ALIGNCTL, 0, IOVT_BOOL, 0},
1501 {"sdalign", IOV_SDALIGN, 0, IOVT_BOOL, 0},
1502 {"devreset", IOV_DEVRESET, 0, IOVT_BOOL, 0},
1503#ifdef DHD_DEBUG
1504 {"sdreg", IOV_SDREG, 0, IOVT_BUFFER, sizeof(sdreg_t)}
1505 ,
1506 {"sbreg", IOV_SBREG, 0, IOVT_BUFFER, sizeof(sdreg_t)}
1507 ,
1508 {"sd_cis", IOV_SDCIS, 0, IOVT_BUFFER, DHD_IOCTL_MAXLEN}
1509 ,
1510 {"forcealign", IOV_FORCEEVEN, 0, IOVT_BOOL, 0}
1511 ,
1512 {"txbound", IOV_TXBOUND, 0, IOVT_UINT32, 0}
1513 ,
1514 {"rxbound", IOV_RXBOUND, 0, IOVT_UINT32, 0}
1515 ,
1516 {"txminmax", IOV_TXMINMAX, 0, IOVT_UINT32, 0}
1517 ,
1518 {"cpu", IOV_CPU, 0, IOVT_BOOL, 0}
1519 ,
1520#ifdef DHD_DEBUG
1521 {"checkdied", IOV_CHECKDIED, 0, IOVT_BUFFER, 0}
1522 ,
1523#endif /* DHD_DEBUG */
1524#endif /* DHD_DEBUG */
1525#ifdef SDTEST
1526 {"extloop", IOV_EXTLOOP, 0, IOVT_BOOL, 0}
1527 ,
1528 {"pktgen", IOV_PKTGEN, 0, IOVT_BUFFER, sizeof(dhd_pktgen_t)}
1529 ,
1530#endif /* SDTEST */
1531
1532 {NULL, 0, 0, 0, 0}
1533};
1534
1535static void
1536dhd_dump_pct(struct bcmstrbuf *strbuf, char *desc, uint num, uint div)
1537{
1538 uint q1, q2;
1539
1540 if (!div) {
1541 bcm_bprintf(strbuf, "%s N/A", desc);
1542 } else {
1543 q1 = num / div;
1544 q2 = (100 * (num - (q1 * div))) / div;
1545 bcm_bprintf(strbuf, "%s %d.%02d", desc, q1, q2);
1546 }
1547}
1548
1549void dhd_bus_dump(dhd_pub_t *dhdp, struct bcmstrbuf *strbuf)
1550{
1551 dhd_bus_t *bus = dhdp->bus;
1552
1553 bcm_bprintf(strbuf, "Bus SDIO structure:\n");
1554 bcm_bprintf(strbuf,
1555 "hostintmask 0x%08x intstatus 0x%08x sdpcm_ver %d\n",
1556 bus->hostintmask, bus->intstatus, bus->sdpcm_ver);
1557 bcm_bprintf(strbuf,
1558 "fcstate %d qlen %d tx_seq %d, max %d, rxskip %d rxlen %d rx_seq %d\n",
1559 bus->fcstate, pktq_len(&bus->txq), bus->tx_seq, bus->tx_max,
1560 bus->rxskip, bus->rxlen, bus->rx_seq);
1561 bcm_bprintf(strbuf, "intr %d intrcount %d lastintrs %d spurious %d\n",
1562 bus->intr, bus->intrcount, bus->lastintrs, bus->spurious);
1563 bcm_bprintf(strbuf, "pollrate %d pollcnt %d regfails %d\n",
1564 bus->pollrate, bus->pollcnt, bus->regfails);
1565
1566 bcm_bprintf(strbuf, "\nAdditional counters:\n");
1567 bcm_bprintf(strbuf,
1568 "tx_sderrs %d fcqueued %d rxrtx %d rx_toolong %d rxc_errors %d\n",
1569 bus->tx_sderrs, bus->fcqueued, bus->rxrtx, bus->rx_toolong,
1570 bus->rxc_errors);
1571 bcm_bprintf(strbuf, "rx_hdrfail %d badhdr %d badseq %d\n",
1572 bus->rx_hdrfail, bus->rx_badhdr, bus->rx_badseq);
1573 bcm_bprintf(strbuf, "fc_rcvd %d, fc_xoff %d, fc_xon %d\n", bus->fc_rcvd,
1574 bus->fc_xoff, bus->fc_xon);
1575 bcm_bprintf(strbuf, "rxglomfail %d, rxglomframes %d, rxglompkts %d\n",
1576 bus->rxglomfail, bus->rxglomframes, bus->rxglompkts);
1577 bcm_bprintf(strbuf, "f2rx (hdrs/data) %d (%d/%d), f2tx %d f1regs %d\n",
1578 (bus->f2rxhdrs + bus->f2rxdata), bus->f2rxhdrs,
1579 bus->f2rxdata, bus->f2txdata, bus->f1regdata);
1580 {
1581 dhd_dump_pct(strbuf, "\nRx: pkts/f2rd", bus->dhd->rx_packets,
1582 (bus->f2rxhdrs + bus->f2rxdata));
1583 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->rx_packets,
1584 bus->f1regdata);
1585 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->rx_packets,
1586 (bus->f2rxhdrs + bus->f2rxdata + bus->f1regdata));
1587 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->rx_packets,
1588 bus->intrcount);
1589 bcm_bprintf(strbuf, "\n");
1590
1591 dhd_dump_pct(strbuf, "Rx: glom pct", (100 * bus->rxglompkts),
1592 bus->dhd->rx_packets);
1593 dhd_dump_pct(strbuf, ", pkts/glom", bus->rxglompkts,
1594 bus->rxglomframes);
1595 bcm_bprintf(strbuf, "\n");
1596
1597 dhd_dump_pct(strbuf, "Tx: pkts/f2wr", bus->dhd->tx_packets,
1598 bus->f2txdata);
1599 dhd_dump_pct(strbuf, ", pkts/f1sd", bus->dhd->tx_packets,
1600 bus->f1regdata);
1601 dhd_dump_pct(strbuf, ", pkts/sd", bus->dhd->tx_packets,
1602 (bus->f2txdata + bus->f1regdata));
1603 dhd_dump_pct(strbuf, ", pkts/int", bus->dhd->tx_packets,
1604 bus->intrcount);
1605 bcm_bprintf(strbuf, "\n");
1606
1607 dhd_dump_pct(strbuf, "Total: pkts/f2rw",
1608 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1609 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata));
1610 dhd_dump_pct(strbuf, ", pkts/f1sd",
1611 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1612 bus->f1regdata);
1613 dhd_dump_pct(strbuf, ", pkts/sd",
1614 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1615 (bus->f2txdata + bus->f2rxhdrs + bus->f2rxdata +
1616 bus->f1regdata));
1617 dhd_dump_pct(strbuf, ", pkts/int",
1618 (bus->dhd->tx_packets + bus->dhd->rx_packets),
1619 bus->intrcount);
1620 bcm_bprintf(strbuf, "\n\n");
1621 }
1622
1623#ifdef SDTEST
1624 if (bus->pktgen_count) {
1625 bcm_bprintf(strbuf, "pktgen config and count:\n");
1626 bcm_bprintf(strbuf,
1627 "freq %d count %d print %d total %d min %d len %d\n",
1628 bus->pktgen_freq, bus->pktgen_count,
1629 bus->pktgen_print, bus->pktgen_total,
1630 bus->pktgen_minlen, bus->pktgen_maxlen);
1631 bcm_bprintf(strbuf, "send attempts %d rcvd %d fail %d\n",
1632 bus->pktgen_sent, bus->pktgen_rcvd,
1633 bus->pktgen_fail);
1634 }
1635#endif /* SDTEST */
1636#ifdef DHD_DEBUG
1637 bcm_bprintf(strbuf, "dpc_sched %d host interrupt%spending\n",
1638 bus->dpc_sched,
1639 (bcmsdh_intr_pending(bus->sdh) ? " " : " not "));
1640 bcm_bprintf(strbuf, "blocksize %d roundup %d\n", bus->blocksize,
1641 bus->roundup);
1642#endif /* DHD_DEBUG */
1643 bcm_bprintf(strbuf,
1644 "clkstate %d activity %d idletime %d idlecount %d sleeping %d\n",
1645 bus->clkstate, bus->activity, bus->idletime, bus->idlecount,
1646 bus->sleeping);
1647}
1648
1649void dhd_bus_clearcounts(dhd_pub_t *dhdp)
1650{
1651 dhd_bus_t *bus = (dhd_bus_t *) dhdp->bus;
1652
1653 bus->intrcount = bus->lastintrs = bus->spurious = bus->regfails = 0;
1654 bus->rxrtx = bus->rx_toolong = bus->rxc_errors = 0;
1655 bus->rx_hdrfail = bus->rx_badhdr = bus->rx_badseq = 0;
1656 bus->tx_sderrs = bus->fc_rcvd = bus->fc_xoff = bus->fc_xon = 0;
1657 bus->rxglomfail = bus->rxglomframes = bus->rxglompkts = 0;
1658 bus->f2rxhdrs = bus->f2rxdata = bus->f2txdata = bus->f1regdata = 0;
1659}
1660
1661#ifdef SDTEST
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001662static int dhdsdio_pktgen_get(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001663{
1664 dhd_pktgen_t pktgen;
1665
1666 pktgen.version = DHD_PKTGEN_VERSION;
1667 pktgen.freq = bus->pktgen_freq;
1668 pktgen.count = bus->pktgen_count;
1669 pktgen.print = bus->pktgen_print;
1670 pktgen.total = bus->pktgen_total;
1671 pktgen.minlen = bus->pktgen_minlen;
1672 pktgen.maxlen = bus->pktgen_maxlen;
1673 pktgen.numsent = bus->pktgen_sent;
1674 pktgen.numrcvd = bus->pktgen_rcvd;
1675 pktgen.numfail = bus->pktgen_fail;
1676 pktgen.mode = bus->pktgen_mode;
1677 pktgen.stop = bus->pktgen_stop;
1678
Stanislav Fomichev02160692011-02-15 01:05:10 +03001679 memcpy(arg, &pktgen, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001680
1681 return 0;
1682}
1683
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001684static int dhdsdio_pktgen_set(dhd_bus_t *bus, u8 *arg)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001685{
1686 dhd_pktgen_t pktgen;
1687 uint oldcnt, oldmode;
1688
Stanislav Fomichev02160692011-02-15 01:05:10 +03001689 memcpy(&pktgen, arg, sizeof(pktgen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001690 if (pktgen.version != DHD_PKTGEN_VERSION)
1691 return BCME_BADARG;
1692
1693 oldcnt = bus->pktgen_count;
1694 oldmode = bus->pktgen_mode;
1695
1696 bus->pktgen_freq = pktgen.freq;
1697 bus->pktgen_count = pktgen.count;
1698 bus->pktgen_print = pktgen.print;
1699 bus->pktgen_total = pktgen.total;
1700 bus->pktgen_minlen = pktgen.minlen;
1701 bus->pktgen_maxlen = pktgen.maxlen;
1702 bus->pktgen_mode = pktgen.mode;
1703 bus->pktgen_stop = pktgen.stop;
1704
1705 bus->pktgen_tick = bus->pktgen_ptick = 0;
Greg Kroah-Hartman3ea2f4d2010-10-08 11:39:43 -07001706 bus->pktgen_len = max(bus->pktgen_len, bus->pktgen_minlen);
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001707 bus->pktgen_len = min(bus->pktgen_len, bus->pktgen_maxlen);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001708
1709 /* Clear counts for a new pktgen (mode change, or was stopped) */
1710 if (bus->pktgen_count && (!oldcnt || oldmode != bus->pktgen_mode))
1711 bus->pktgen_sent = bus->pktgen_rcvd = bus->pktgen_fail = 0;
1712
1713 return 0;
1714}
1715#endif /* SDTEST */
1716
1717static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001718dhdsdio_membytes(dhd_bus_t *bus, bool write, u32 address, u8 *data,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001719 uint size)
1720{
1721 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001722 u32 sdaddr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001723 uint dsize;
1724
1725 /* Determine initial transfer parameters */
1726 sdaddr = address & SBSDIO_SB_OFT_ADDR_MASK;
1727 if ((sdaddr + size) & SBSDIO_SBWINDOW_MASK)
1728 dsize = (SBSDIO_SB_OFT_ADDR_LIMIT - sdaddr);
1729 else
1730 dsize = size;
1731
1732 /* Set the backplane window to include the start address */
1733 bcmerror = dhdsdio_set_siaddr_window(bus, address);
1734 if (bcmerror) {
1735 DHD_ERROR(("%s: window change failed\n", __func__));
1736 goto xfer_done;
1737 }
1738
1739 /* Do the transfer(s) */
1740 while (size) {
1741 DHD_INFO(("%s: %s %d bytes at offset 0x%08x in window 0x%08x\n",
1742 __func__, (write ? "write" : "read"), dsize,
1743 sdaddr, (address & SBSDIO_SBWINDOW_MASK)));
1744 bcmerror =
1745 bcmsdh_rwdata(bus->sdh, write, sdaddr, data, dsize);
1746 if (bcmerror) {
1747 DHD_ERROR(("%s: membytes transfer failed\n", __func__));
1748 break;
1749 }
1750
1751 /* Adjust for next transfer (if any) */
1752 size -= dsize;
1753 if (size) {
1754 data += dsize;
1755 address += dsize;
1756 bcmerror = dhdsdio_set_siaddr_window(bus, address);
1757 if (bcmerror) {
1758 DHD_ERROR(("%s: window change failed\n",
1759 __func__));
1760 break;
1761 }
1762 sdaddr = 0;
Greg Kroah-Hartmanb61640d2010-10-08 12:37:39 -07001763 dsize = min_t(uint, SBSDIO_SB_OFT_ADDR_LIMIT, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001764 }
1765 }
1766
1767xfer_done:
1768 /* Return the window to backplane enumeration space for core access */
1769 if (dhdsdio_set_siaddr_window(bus, bcmsdh_cur_sbwad(bus->sdh))) {
1770 DHD_ERROR(("%s: FAILED to set window back to 0x%x\n",
1771 __func__, bcmsdh_cur_sbwad(bus->sdh)));
1772 }
1773
1774 return bcmerror;
1775}
1776
1777#ifdef DHD_DEBUG
1778static int dhdsdio_readshared(dhd_bus_t *bus, sdpcm_shared_t *sh)
1779{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07001780 u32 addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001781 int rv;
1782
1783 /* Read last word in memory to determine address of
1784 sdpcm_shared structure */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001785 rv = dhdsdio_membytes(bus, false, bus->ramsize - 4, (u8 *)&addr, 4);
Jason Cooper9b890322010-09-30 15:15:39 -04001786 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001787 return rv;
1788
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001789 addr = le32_to_cpu(addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001790
1791 DHD_INFO(("sdpcm_shared address 0x%08X\n", addr));
1792
1793 /*
1794 * Check if addr is valid.
1795 * NVRAM length at the end of memory should have been overwritten.
1796 */
1797 if (addr == 0 || ((~addr >> 16) & 0xffff) == (addr & 0xffff)) {
1798 DHD_ERROR(("%s: address (0x%08x) of sdpcm_shared invalid\n",
1799 __func__, addr));
1800 return BCME_ERROR;
1801 }
1802
1803 /* Read hndrte_shared structure */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001804 rv = dhdsdio_membytes(bus, false, addr, (u8 *) sh,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001805 sizeof(sdpcm_shared_t));
1806 if (rv < 0)
1807 return rv;
1808
1809 /* Endianness */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03001810 sh->flags = le32_to_cpu(sh->flags);
1811 sh->trap_addr = le32_to_cpu(sh->trap_addr);
1812 sh->assert_exp_addr = le32_to_cpu(sh->assert_exp_addr);
1813 sh->assert_file_addr = le32_to_cpu(sh->assert_file_addr);
1814 sh->assert_line = le32_to_cpu(sh->assert_line);
1815 sh->console_addr = le32_to_cpu(sh->console_addr);
1816 sh->msgtrace_addr = le32_to_cpu(sh->msgtrace_addr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001817
1818 if ((sh->flags & SDPCM_SHARED_VERSION_MASK) != SDPCM_SHARED_VERSION) {
1819 DHD_ERROR(("%s: sdpcm_shared version %d in dhd "
1820 "is different than sdpcm_shared version %d in dongle\n",
1821 __func__, SDPCM_SHARED_VERSION,
1822 sh->flags & SDPCM_SHARED_VERSION_MASK));
1823 return BCME_ERROR;
1824 }
1825
Roland Vossena1c5ad82011-04-11 15:16:24 +02001826 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001827}
1828
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001829static int dhdsdio_checkdied(dhd_bus_t *bus, u8 *data, uint size)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001830{
1831 int bcmerror = 0;
1832 uint msize = 512;
1833 char *mbuffer = NULL;
1834 uint maxstrlen = 256;
1835 char *str = NULL;
1836 trap_t tr;
1837 sdpcm_shared_t sdpcm_shared;
1838 struct bcmstrbuf strbuf;
1839
1840 DHD_TRACE(("%s: Enter\n", __func__));
1841
1842 if (data == NULL) {
1843 /*
1844 * Called after a rx ctrl timeout. "data" is NULL.
1845 * allocate memory to trace the trap or assert.
1846 */
1847 size = msize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001848 mbuffer = data = kmalloc(msize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001849 if (mbuffer == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001850 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001851 msize));
1852 bcmerror = BCME_NOMEM;
1853 goto done;
1854 }
1855 }
1856
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001857 str = kmalloc(maxstrlen, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04001858 if (str == NULL) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001859 DHD_ERROR(("%s: kmalloc(%d) failed\n", __func__, maxstrlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001860 bcmerror = BCME_NOMEM;
1861 goto done;
1862 }
1863
Jason Cooper9b890322010-09-30 15:15:39 -04001864 bcmerror = dhdsdio_readshared(bus, &sdpcm_shared);
1865 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001866 goto done;
1867
1868 bcm_binit(&strbuf, data, size);
1869
1870 bcm_bprintf(&strbuf,
1871 "msgtrace address : 0x%08X\nconsole address : 0x%08X\n",
1872 sdpcm_shared.msgtrace_addr, sdpcm_shared.console_addr);
1873
1874 if ((sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT) == 0) {
1875 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
1876 * (Avoids conflict with real asserts for programmatic
1877 * parsing of output.)
1878 */
1879 bcm_bprintf(&strbuf, "Assrt not built in dongle\n");
1880 }
1881
1882 if ((sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP)) ==
1883 0) {
1884 /* NOTE: Misspelled assert is intentional - DO NOT FIX.
1885 * (Avoids conflict with real asserts for programmatic
1886 * parsing of output.)
1887 */
1888 bcm_bprintf(&strbuf, "No trap%s in dongle",
1889 (sdpcm_shared.flags & SDPCM_SHARED_ASSERT_BUILT)
1890 ? "/assrt" : "");
1891 } else {
1892 if (sdpcm_shared.flags & SDPCM_SHARED_ASSERT) {
1893 /* Download assert */
1894 bcm_bprintf(&strbuf, "Dongle assert");
1895 if (sdpcm_shared.assert_exp_addr != 0) {
1896 str[0] = '\0';
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001897 bcmerror = dhdsdio_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04001898 sdpcm_shared.assert_exp_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001899 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04001900 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001901 goto done;
1902
1903 str[maxstrlen - 1] = '\0';
1904 bcm_bprintf(&strbuf, " expr \"%s\"", str);
1905 }
1906
1907 if (sdpcm_shared.assert_file_addr != 0) {
1908 str[0] = '\0';
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001909 bcmerror = dhdsdio_membytes(bus, false,
Jason Cooper9b890322010-09-30 15:15:39 -04001910 sdpcm_shared.assert_file_addr,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001911 (u8 *) str, maxstrlen);
Jason Cooper9b890322010-09-30 15:15:39 -04001912 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001913 goto done;
1914
1915 str[maxstrlen - 1] = '\0';
1916 bcm_bprintf(&strbuf, " file \"%s\"", str);
1917 }
1918
1919 bcm_bprintf(&strbuf, " line %d ",
1920 sdpcm_shared.assert_line);
1921 }
1922
1923 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001924 bcmerror = dhdsdio_membytes(bus, false,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001925 sdpcm_shared.trap_addr, (u8 *)&tr,
Jason Cooper9b890322010-09-30 15:15:39 -04001926 sizeof(trap_t));
1927 if (bcmerror < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001928 goto done;
1929
1930 bcm_bprintf(&strbuf,
1931 "Dongle trap type 0x%x @ epc 0x%x, cpsr 0x%x, spsr 0x%x, sp 0x%x,"
1932 "lp 0x%x, rpc 0x%x Trap offset 0x%x, "
1933 "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",
1934 tr.type, tr.epc, tr.cpsr, tr.spsr, tr.r13,
1935 tr.r14, tr.pc, sdpcm_shared.trap_addr,
1936 tr.r0, tr.r1, tr.r2, tr.r3, tr.r4, tr.r5,
1937 tr.r6, tr.r7);
1938 }
1939 }
1940
1941 if (sdpcm_shared.flags & (SDPCM_SHARED_ASSERT | SDPCM_SHARED_TRAP))
1942 DHD_ERROR(("%s: %s\n", __func__, strbuf.origbuf));
1943
1944#ifdef DHD_DEBUG
1945 if (sdpcm_shared.flags & SDPCM_SHARED_TRAP) {
1946 /* Mem dump to a file on device */
1947 dhdsdio_mem_dump(bus);
1948 }
1949#endif /* DHD_DEBUG */
1950
1951done:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05001952 kfree(mbuffer);
1953 kfree(str);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001954
1955 return bcmerror;
1956}
1957
1958static int dhdsdio_mem_dump(dhd_bus_t *bus)
1959{
1960 int ret = 0;
1961 int size; /* Full mem size */
1962 int start = 0; /* Start address */
1963 int read_size = 0; /* Read size of each iteration */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07001964 u8 *buf = NULL, *databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001965
1966 /* Get full mem size */
1967 size = bus->ramsize;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02001968 buf = kmalloc(size, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001969 if (!buf) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01001970 DHD_ERROR(("%s: Out of memory (%d bytes)\n", __func__, size));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001971 return -1;
1972 }
1973
1974 /* Read mem content */
Arend van Spriel0bef7742011-02-10 12:03:44 +01001975 printk(KERN_DEBUG "Dump dongle memory");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001976 databuf = buf;
1977 while (size) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07001978 read_size = min(MEMBLOCK, size);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07001979 ret = dhdsdio_membytes(bus, false, start, databuf, read_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001980 if (ret) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01001981 DHD_ERROR(("%s: Error membytes %d\n", __func__, ret));
Ilia Mirkin46d994b2011-03-13 00:28:56 -05001982 kfree(buf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001983 return -1;
1984 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01001985 printk(".");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001986
1987 /* Decrement size and increment start address */
1988 size -= read_size;
1989 start += read_size;
1990 databuf += read_size;
1991 }
Arend van Spriel0bef7742011-02-10 12:03:44 +01001992 printk(KERN_DEBUG "Done\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001993
1994 /* free buf before return !!! */
1995 if (write_to_file(bus->dhd, buf, bus->ramsize)) {
Arend van Spriel0bef7742011-02-10 12:03:44 +01001996 DHD_ERROR(("%s: Error writing to files\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07001997 return -1;
1998 }
1999
2000 /* buf free handled in write_to_file, not here */
2001 return 0;
2002}
2003
2004#define CONSOLE_LINE_MAX 192
2005
2006static int dhdsdio_readconsole(dhd_bus_t *bus)
2007{
2008 dhd_console_t *c = &bus->console;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002009 u8 line[CONSOLE_LINE_MAX], ch;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002010 u32 n, idx, addr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002011 int rv;
2012
2013 /* Don't do anything until FWREADY updates console address */
2014 if (bus->console_addr == 0)
2015 return 0;
2016
2017 /* Read console log struct */
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -07002018 addr = bus->console_addr + offsetof(hndrte_cons_t, log);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002019 rv = dhdsdio_membytes(bus, false, addr, (u8 *)&c->log,
Jason Cooper9b890322010-09-30 15:15:39 -04002020 sizeof(c->log));
2021 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002022 return rv;
2023
2024 /* Allocate console buffer (one time only) */
2025 if (c->buf == NULL) {
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002026 c->bufsize = le32_to_cpu(c->log.buf_size);
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002027 c->buf = kmalloc(c->bufsize, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04002028 if (c->buf == NULL)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002029 return BCME_NOMEM;
2030 }
2031
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002032 idx = le32_to_cpu(c->log.idx);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002033
2034 /* Protect against corrupt value */
2035 if (idx > c->bufsize)
2036 return BCME_ERROR;
2037
2038 /* Skip reading the console buffer if the index pointer
2039 has not moved */
2040 if (idx == c->last)
Roland Vossena1c5ad82011-04-11 15:16:24 +02002041 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002042
2043 /* Read the console buffer */
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002044 addr = le32_to_cpu(c->log.buf);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002045 rv = dhdsdio_membytes(bus, false, addr, c->buf, c->bufsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002046 if (rv < 0)
2047 return rv;
2048
2049 while (c->last != idx) {
2050 for (n = 0; n < CONSOLE_LINE_MAX - 2; n++) {
2051 if (c->last == idx) {
2052 /* This would output a partial line.
2053 * Instead, back up
2054 * the buffer pointer and output this
2055 * line next time around.
2056 */
2057 if (c->last >= n)
2058 c->last -= n;
2059 else
2060 c->last = c->bufsize - n;
2061 goto break2;
2062 }
2063 ch = c->buf[c->last];
2064 c->last = (c->last + 1) % c->bufsize;
2065 if (ch == '\n')
2066 break;
2067 line[n] = ch;
2068 }
2069
2070 if (n > 0) {
2071 if (line[n - 1] == '\r')
2072 n--;
2073 line[n] = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01002074 printk(KERN_DEBUG "CONSOLE: %s\n", line);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002075 }
2076 }
2077break2:
2078
Roland Vossena1c5ad82011-04-11 15:16:24 +02002079 return 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002080}
2081#endif /* DHD_DEBUG */
2082
2083int dhdsdio_downloadvars(dhd_bus_t *bus, void *arg, int len)
2084{
Roland Vossena1c5ad82011-04-11 15:16:24 +02002085 int bcmerror = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002086
2087 DHD_TRACE(("%s: Enter\n", __func__));
2088
2089 /* Basic sanity checks */
2090 if (bus->dhd->up) {
2091 bcmerror = BCME_NOTDOWN;
2092 goto err;
2093 }
2094 if (!len) {
2095 bcmerror = BCME_BUFTOOSHORT;
2096 goto err;
2097 }
2098
2099 /* Free the old ones and replace with passed variables */
Ilia Mirkin46d994b2011-03-13 00:28:56 -05002100 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002101
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002102 bus->vars = kmalloc(len, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002103 bus->varsz = bus->vars ? len : 0;
2104 if (bus->vars == NULL) {
2105 bcmerror = BCME_NOMEM;
2106 goto err;
2107 }
2108
2109 /* Copy the passed variables, which should include the
2110 terminating double-null */
Stanislav Fomichev02160692011-02-15 01:05:10 +03002111 memcpy(bus->vars, arg, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002112err:
2113 return bcmerror;
2114}
2115
2116static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002117dhdsdio_doiovar(dhd_bus_t *bus, const bcm_iovar_t *vi, u32 actionid,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002118 const char *name, void *params, int plen, void *arg, int len,
2119 int val_size)
2120{
2121 int bcmerror = 0;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002122 s32 int_val = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002123 bool bool_val = 0;
2124
2125 DHD_TRACE(("%s: Enter, action %d name %s params %p plen %d arg %p "
2126 "len %d val_size %d\n",
2127 __func__, actionid, name, params, plen, arg, len, val_size));
2128
2129 bcmerror = bcm_iovar_lencheck(vi, arg, len, IOV_ISSET(actionid));
2130 if (bcmerror != 0)
2131 goto exit;
2132
2133 if (plen >= (int)sizeof(int_val))
Stanislav Fomichev02160692011-02-15 01:05:10 +03002134 memcpy(&int_val, params, sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002135
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002136 bool_val = (int_val != 0) ? true : false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002137
2138 /* Some ioctls use the bus */
2139 dhd_os_sdlock(bus->dhd);
2140
2141 /* Check if dongle is in reset. If so, only allow DEVRESET iovars */
2142 if (bus->dhd->dongle_reset && !(actionid == IOV_SVAL(IOV_DEVRESET) ||
2143 actionid == IOV_GVAL(IOV_DEVRESET))) {
2144 bcmerror = BCME_NOTREADY;
2145 goto exit;
2146 }
2147
2148 /* Handle sleep stuff before any clock mucking */
2149 if (vi->varid == IOV_SLEEP) {
2150 if (IOV_ISSET(actionid)) {
2151 bcmerror = dhdsdio_bussleep(bus, bool_val);
2152 } else {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002153 int_val = (s32) bus->sleeping;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002154 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002155 }
2156 goto exit;
2157 }
2158
2159 /* Request clock to allow SDIO accesses */
2160 if (!bus->dhd->dongle_reset) {
2161 BUS_WAKE(bus);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002162 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002163 }
2164
2165 switch (actionid) {
2166 case IOV_GVAL(IOV_INTR):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002167 int_val = (s32) bus->intr;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002168 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002169 break;
2170
2171 case IOV_SVAL(IOV_INTR):
2172 bus->intr = bool_val;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002173 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002174 if (bus->dhd->up) {
2175 if (bus->intr) {
2176 DHD_INTR(("%s: enable SDIO device interrupts\n",
2177 __func__));
2178 bcmsdh_intr_enable(bus->sdh);
2179 } else {
2180 DHD_INTR(("%s: disable SDIO interrupts\n",
2181 __func__));
2182 bcmsdh_intr_disable(bus->sdh);
2183 }
2184 }
2185 break;
2186
2187 case IOV_GVAL(IOV_POLLRATE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002188 int_val = (s32) bus->pollrate;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002189 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002190 break;
2191
2192 case IOV_SVAL(IOV_POLLRATE):
2193 bus->pollrate = (uint) int_val;
2194 bus->poll = (bus->pollrate != 0);
2195 break;
2196
2197 case IOV_GVAL(IOV_IDLETIME):
2198 int_val = bus->idletime;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002199 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002200 break;
2201
2202 case IOV_SVAL(IOV_IDLETIME):
2203 if ((int_val < 0) && (int_val != DHD_IDLE_IMMEDIATE))
2204 bcmerror = BCME_BADARG;
2205 else
2206 bus->idletime = int_val;
2207 break;
2208
2209 case IOV_GVAL(IOV_IDLECLOCK):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002210 int_val = (s32) bus->idleclock;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002211 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002212 break;
2213
2214 case IOV_SVAL(IOV_IDLECLOCK):
2215 bus->idleclock = int_val;
2216 break;
2217
2218 case IOV_GVAL(IOV_SD1IDLE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002219 int_val = (s32) sd1idle;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002220 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002221 break;
2222
2223 case IOV_SVAL(IOV_SD1IDLE):
2224 sd1idle = bool_val;
2225 break;
2226
2227 case IOV_SVAL(IOV_MEMBYTES):
2228 case IOV_GVAL(IOV_MEMBYTES):
2229 {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002230 u32 address;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002231 uint size, dsize;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002232 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002233
2234 bool set = (actionid == IOV_SVAL(IOV_MEMBYTES));
2235
2236 ASSERT(plen >= 2 * sizeof(int));
2237
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002238 address = (u32) int_val;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002239 memcpy(&int_val, (char *)params + sizeof(int_val),
2240 sizeof(int_val));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002241 size = (uint) int_val;
2242
2243 /* Do some validation */
2244 dsize = set ? plen - (2 * sizeof(int)) : len;
2245 if (dsize < size) {
2246 DHD_ERROR(("%s: error on %s membytes, addr "
2247 "0x%08x size %d dsize %d\n",
2248 __func__, (set ? "set" : "get"),
2249 address, size, dsize));
2250 bcmerror = BCME_BADARG;
2251 break;
2252 }
2253
2254 DHD_INFO(("%s: Request to %s %d bytes at address "
2255 "0x%08x\n",
2256 __func__, (set ? "write" : "read"), size, address));
2257
2258 /* If we know about SOCRAM, check for a fit */
2259 if ((bus->orig_ramsize) &&
2260 ((address > bus->orig_ramsize)
2261 || (address + size > bus->orig_ramsize))) {
2262 DHD_ERROR(("%s: ramsize 0x%08x doesn't have %d "
2263 "bytes at 0x%08x\n",
2264 __func__, bus->orig_ramsize, size, address));
2265 bcmerror = BCME_BADARG;
2266 break;
2267 }
2268
2269 /* Generate the actual data pointer */
2270 data =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002271 set ? (u8 *) params +
2272 2 * sizeof(int) : (u8 *) arg;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002273
2274 /* Call to do the transfer */
2275 bcmerror =
2276 dhdsdio_membytes(bus, set, address, data, size);
2277
2278 break;
2279 }
2280
2281 case IOV_GVAL(IOV_MEMSIZE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002282 int_val = (s32) bus->ramsize;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002283 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002284 break;
2285
2286 case IOV_GVAL(IOV_SDIOD_DRIVE):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002287 int_val = (s32) dhd_sdiod_drive_strength;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002288 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002289 break;
2290
2291 case IOV_SVAL(IOV_SDIOD_DRIVE):
2292 dhd_sdiod_drive_strength = int_val;
Arend van Spriel26bcc182011-03-01 10:56:55 +01002293 si_sdiod_drive_strength_init(bus->sih,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002294 dhd_sdiod_drive_strength);
2295 break;
2296
2297 case IOV_SVAL(IOV_DOWNLOAD):
2298 bcmerror = dhdsdio_download_state(bus, bool_val);
2299 break;
2300
2301 case IOV_SVAL(IOV_VARS):
2302 bcmerror = dhdsdio_downloadvars(bus, arg, len);
2303 break;
2304
2305 case IOV_GVAL(IOV_READAHEAD):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002306 int_val = (s32) dhd_readahead;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002307 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002308 break;
2309
2310 case IOV_SVAL(IOV_READAHEAD):
2311 if (bool_val && !dhd_readahead)
2312 bus->nextlen = 0;
2313 dhd_readahead = bool_val;
2314 break;
2315
2316 case IOV_GVAL(IOV_SDRXCHAIN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002317 int_val = (s32) bus->use_rxchain;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002318 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002319 break;
2320
2321 case IOV_SVAL(IOV_SDRXCHAIN):
2322 if (bool_val && !bus->sd_rxchain)
2323 bcmerror = BCME_UNSUPPORTED;
2324 else
2325 bus->use_rxchain = bool_val;
2326 break;
2327 case IOV_GVAL(IOV_ALIGNCTL):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002328 int_val = (s32) dhd_alignctl;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002329 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002330 break;
2331
2332 case IOV_SVAL(IOV_ALIGNCTL):
2333 dhd_alignctl = bool_val;
2334 break;
2335
2336 case IOV_GVAL(IOV_SDALIGN):
2337 int_val = DHD_SDALIGN;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002338 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002339 break;
2340
2341#ifdef DHD_DEBUG
2342 case IOV_GVAL(IOV_VARS):
2343 if (bus->varsz < (uint) len)
Stanislav Fomichev02160692011-02-15 01:05:10 +03002344 memcpy(arg, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002345 else
2346 bcmerror = BCME_BUFTOOSHORT;
2347 break;
2348#endif /* DHD_DEBUG */
2349
2350#ifdef DHD_DEBUG
2351 case IOV_GVAL(IOV_SDREG):
2352 {
2353 sdreg_t *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002354 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002355
2356 sd_ptr = (sdreg_t *) params;
2357
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002358 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002359 size = sd_ptr->func;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002360 int_val = (s32) bcmsdh_reg_read(bus->sdh, addr, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002361 if (bcmsdh_regfail(bus->sdh))
2362 bcmerror = BCME_SDIO_ERROR;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002363 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002364 break;
2365 }
2366
2367 case IOV_SVAL(IOV_SDREG):
2368 {
2369 sdreg_t *sd_ptr;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002370 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002371
2372 sd_ptr = (sdreg_t *) params;
2373
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07002374 addr = (unsigned long)bus->regs + sd_ptr->offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002375 size = sd_ptr->func;
2376 bcmsdh_reg_write(bus->sdh, addr, size, sd_ptr->value);
2377 if (bcmsdh_regfail(bus->sdh))
2378 bcmerror = BCME_SDIO_ERROR;
2379 break;
2380 }
2381
2382 /* Same as above, but offset is not backplane
2383 (not SDIO core) */
2384 case IOV_GVAL(IOV_SBREG):
2385 {
2386 sdreg_t sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002387 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002388
Stanislav Fomichev02160692011-02-15 01:05:10 +03002389 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002390
2391 addr = SI_ENUM_BASE + sdreg.offset;
2392 size = sdreg.func;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002393 int_val = (s32) bcmsdh_reg_read(bus->sdh, addr, size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002394 if (bcmsdh_regfail(bus->sdh))
2395 bcmerror = BCME_SDIO_ERROR;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002396 memcpy(arg, &int_val, sizeof(s32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002397 break;
2398 }
2399
2400 case IOV_SVAL(IOV_SBREG):
2401 {
2402 sdreg_t sdreg;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002403 u32 addr, size;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002404
Stanislav Fomichev02160692011-02-15 01:05:10 +03002405 memcpy(&sdreg, params, sizeof(sdreg));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002406
2407 addr = SI_ENUM_BASE + sdreg.offset;
2408 size = sdreg.func;
2409 bcmsdh_reg_write(bus->sdh, addr, size, sdreg.value);
2410 if (bcmsdh_regfail(bus->sdh))
2411 bcmerror = BCME_SDIO_ERROR;
2412 break;
2413 }
2414
2415 case IOV_GVAL(IOV_SDCIS):
2416 {
2417 *(char *)arg = 0;
2418
nohee koea3b8a22010-10-09 10:34:38 -07002419 strcat(arg, "\nFunc 0\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002420 bcmsdh_cis_read(bus->sdh, 0x10,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002421 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002422 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002423 strcat(arg, "\nFunc 1\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002424 bcmsdh_cis_read(bus->sdh, 0x11,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002425 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002426 SBSDIO_CIS_SIZE_LIMIT);
nohee koea3b8a22010-10-09 10:34:38 -07002427 strcat(arg, "\nFunc 2\n");
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002428 bcmsdh_cis_read(bus->sdh, 0x12,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002429 (u8 *) arg + strlen(arg),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002430 SBSDIO_CIS_SIZE_LIMIT);
2431 break;
2432 }
2433
2434 case IOV_GVAL(IOV_FORCEEVEN):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002435 int_val = (s32) forcealign;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002436 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002437 break;
2438
2439 case IOV_SVAL(IOV_FORCEEVEN):
2440 forcealign = bool_val;
2441 break;
2442
2443 case IOV_GVAL(IOV_TXBOUND):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002444 int_val = (s32) dhd_txbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002445 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002446 break;
2447
2448 case IOV_SVAL(IOV_TXBOUND):
2449 dhd_txbound = (uint) int_val;
2450 break;
2451
2452 case IOV_GVAL(IOV_RXBOUND):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002453 int_val = (s32) dhd_rxbound;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002454 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002455 break;
2456
2457 case IOV_SVAL(IOV_RXBOUND):
2458 dhd_rxbound = (uint) int_val;
2459 break;
2460
2461 case IOV_GVAL(IOV_TXMINMAX):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002462 int_val = (s32) dhd_txminmax;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002463 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002464 break;
2465
2466 case IOV_SVAL(IOV_TXMINMAX):
2467 dhd_txminmax = (uint) int_val;
2468 break;
2469#endif /* DHD_DEBUG */
2470
2471#ifdef SDTEST
2472 case IOV_GVAL(IOV_EXTLOOP):
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002473 int_val = (s32) bus->ext_loop;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002474 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002475 break;
2476
2477 case IOV_SVAL(IOV_EXTLOOP):
2478 bus->ext_loop = bool_val;
2479 break;
2480
2481 case IOV_GVAL(IOV_PKTGEN):
2482 bcmerror = dhdsdio_pktgen_get(bus, arg);
2483 break;
2484
2485 case IOV_SVAL(IOV_PKTGEN):
2486 bcmerror = dhdsdio_pktgen_set(bus, arg);
2487 break;
2488#endif /* SDTEST */
2489
2490 case IOV_SVAL(IOV_DEVRESET):
2491 DHD_TRACE(("%s: Called set IOV_DEVRESET=%d dongle_reset=%d "
2492 "busstate=%d\n",
2493 __func__, bool_val, bus->dhd->dongle_reset,
2494 bus->dhd->busstate));
2495
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002496 dhd_bus_devreset(bus->dhd, (u8) bool_val);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002497
2498 break;
2499
2500 case IOV_GVAL(IOV_DEVRESET):
2501 DHD_TRACE(("%s: Called get IOV_DEVRESET\n", __func__));
2502
2503 /* Get its status */
2504 int_val = (bool) bus->dhd->dongle_reset;
Stanislav Fomichev02160692011-02-15 01:05:10 +03002505 memcpy(arg, &int_val, val_size);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002506
2507 break;
2508
2509 default:
2510 bcmerror = BCME_UNSUPPORTED;
2511 break;
2512 }
2513
2514exit:
2515 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002516 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002517 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002518 }
2519
2520 dhd_os_sdunlock(bus->dhd);
2521
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002522 if (actionid == IOV_SVAL(IOV_DEVRESET) && bool_val == false)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002523 dhd_preinit_ioctls((dhd_pub_t *) bus->dhd);
2524
2525 return bcmerror;
2526}
2527
2528static int dhdsdio_write_vars(dhd_bus_t *bus)
2529{
2530 int bcmerror = 0;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002531 u32 varsize;
2532 u32 varaddr;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002533 u8 *vbuffer;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002534 u32 varsizew;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002535#ifdef DHD_DEBUG
2536 char *nvram_ularray;
2537#endif /* DHD_DEBUG */
2538
2539 /* Even if there are no vars are to be written, we still
2540 need to set the ramsize. */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07002541 varsize = bus->varsz ? roundup(bus->varsz, 4) : 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002542 varaddr = (bus->ramsize - 4) - varsize;
2543
2544 if (bus->vars) {
Alexander Beregalov12d0eb42011-03-09 03:53:35 +03002545 vbuffer = kzalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002546 if (!vbuffer)
2547 return BCME_NOMEM;
2548
Stanislav Fomichev02160692011-02-15 01:05:10 +03002549 memcpy(vbuffer, bus->vars, bus->varsz);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002550
2551 /* Write the vars list */
2552 bcmerror =
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002553 dhdsdio_membytes(bus, true, varaddr, vbuffer, varsize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002554#ifdef DHD_DEBUG
2555 /* Verify NVRAM bytes */
2556 DHD_INFO(("Compare NVRAM dl & ul; varsize=%d\n", varsize));
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02002557 nvram_ularray = kmalloc(varsize, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002558 if (!nvram_ularray)
2559 return BCME_NOMEM;
2560
2561 /* Upload image to verify downloaded contents. */
2562 memset(nvram_ularray, 0xaa, varsize);
2563
2564 /* Read the vars list to temp buffer for comparison */
2565 bcmerror =
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002566 dhdsdio_membytes(bus, false, varaddr, nvram_ularray,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002567 varsize);
2568 if (bcmerror) {
2569 DHD_ERROR(("%s: error %d on reading %d nvram bytes at "
2570 "0x%08x\n", __func__, bcmerror, varsize, varaddr));
2571 }
2572 /* Compare the org NVRAM with the one read from RAM */
2573 if (memcmp(vbuffer, nvram_ularray, varsize)) {
2574 DHD_ERROR(("%s: Downloaded NVRAM image is corrupted.\n",
2575 __func__));
2576 } else
2577 DHD_ERROR(("%s: Download/Upload/Compare of NVRAM ok.\n",
2578 __func__));
2579
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002580 kfree(nvram_ularray);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002581#endif /* DHD_DEBUG */
2582
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02002583 kfree(vbuffer);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002584 }
2585
2586 /* adjust to the user specified RAM */
2587 DHD_INFO(("Physical memory size: %d, usable memory size: %d\n",
2588 bus->orig_ramsize, bus->ramsize));
2589 DHD_INFO(("Vars are at %d, orig varsize is %d\n", varaddr, varsize));
2590 varsize = ((bus->orig_ramsize - 4) - varaddr);
2591
2592 /*
2593 * Determine the length token:
2594 * Varsize, converted to words, in lower 16-bits, checksum
2595 * in upper 16-bits.
2596 */
2597 if (bcmerror) {
2598 varsizew = 0;
2599 } else {
2600 varsizew = varsize / 4;
2601 varsizew = (~varsizew << 16) | (varsizew & 0x0000FFFF);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03002602 varsizew = cpu_to_le32(varsizew);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002603 }
2604
2605 DHD_INFO(("New varsize is %d, length token=0x%08x\n", varsize,
2606 varsizew));
2607
2608 /* Write the length token to the last word */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002609 bcmerror = dhdsdio_membytes(bus, true, (bus->orig_ramsize - 4),
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002610 (u8 *)&varsizew, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002611
2612 return bcmerror;
2613}
2614
2615static int dhdsdio_download_state(dhd_bus_t *bus, bool enter)
2616{
2617 uint retries;
2618 int bcmerror = 0;
2619
2620 /* To enter download state, disable ARM and reset SOCRAM.
2621 * To exit download state, simply reset ARM (default is RAM boot).
2622 */
2623 if (enter) {
2624
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002625 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002626
2627 if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
2628 !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
2629 DHD_ERROR(("%s: Failed to find ARM core!\n", __func__));
2630 bcmerror = BCME_ERROR;
2631 goto fail;
2632 }
2633
2634 si_core_disable(bus->sih, 0);
2635 if (bcmsdh_regfail(bus->sdh)) {
2636 bcmerror = BCME_SDIO_ERROR;
2637 goto fail;
2638 }
2639
2640 if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
2641 DHD_ERROR(("%s: Failed to find SOCRAM core!\n",
2642 __func__));
2643 bcmerror = BCME_ERROR;
2644 goto fail;
2645 }
2646
2647 si_core_reset(bus->sih, 0, 0);
2648 if (bcmsdh_regfail(bus->sdh)) {
2649 DHD_ERROR(("%s: Failure trying reset SOCRAM core?\n",
2650 __func__));
2651 bcmerror = BCME_SDIO_ERROR;
2652 goto fail;
2653 }
2654
2655 /* Clear the top bit of memory */
2656 if (bus->ramsize) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002657 u32 zeros = 0;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002658 dhdsdio_membytes(bus, true, bus->ramsize - 4,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002659 (u8 *)&zeros, 4);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002660 }
2661 } else {
2662 if (!(si_setcore(bus->sih, SOCRAM_CORE_ID, 0))) {
2663 DHD_ERROR(("%s: Failed to find SOCRAM core!\n",
2664 __func__));
2665 bcmerror = BCME_ERROR;
2666 goto fail;
2667 }
2668
2669 if (!si_iscoreup(bus->sih)) {
2670 DHD_ERROR(("%s: SOCRAM core is down after reset?\n",
2671 __func__));
2672 bcmerror = BCME_ERROR;
2673 goto fail;
2674 }
2675
2676 bcmerror = dhdsdio_write_vars(bus);
2677 if (bcmerror) {
2678 DHD_ERROR(("%s: no vars written to RAM\n", __func__));
2679 bcmerror = 0;
2680 }
2681
2682 if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0) &&
2683 !si_setcore(bus->sih, SDIOD_CORE_ID, 0)) {
2684 DHD_ERROR(("%s: Can't change back to SDIO core?\n",
2685 __func__));
2686 bcmerror = BCME_ERROR;
2687 goto fail;
2688 }
2689 W_SDREG(0xFFFFFFFF, &bus->regs->intstatus, retries);
2690
2691 if (!(si_setcore(bus->sih, ARM7S_CORE_ID, 0)) &&
2692 !(si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
2693 DHD_ERROR(("%s: Failed to find ARM core!\n", __func__));
2694 bcmerror = BCME_ERROR;
2695 goto fail;
2696 }
2697
2698 si_core_reset(bus->sih, 0, 0);
2699 if (bcmsdh_regfail(bus->sdh)) {
2700 DHD_ERROR(("%s: Failure trying to reset ARM core?\n",
2701 __func__));
2702 bcmerror = BCME_SDIO_ERROR;
2703 goto fail;
2704 }
2705
2706 /* Allow HT Clock now that the ARM is running. */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002707 bus->alp_only = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002708
2709 bus->dhd->busstate = DHD_BUS_LOAD;
2710 }
2711
2712fail:
2713 /* Always return to SDIOD core */
2714 if (!si_setcore(bus->sih, PCMCIA_CORE_ID, 0))
2715 si_setcore(bus->sih, SDIOD_CORE_ID, 0);
2716
2717 return bcmerror;
2718}
2719
2720int
2721dhd_bus_iovar_op(dhd_pub_t *dhdp, const char *name,
2722 void *params, int plen, void *arg, int len, bool set)
2723{
2724 dhd_bus_t *bus = dhdp->bus;
2725 const bcm_iovar_t *vi = NULL;
2726 int bcmerror = 0;
2727 int val_size;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002728 u32 actionid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002729
2730 DHD_TRACE(("%s: Enter\n", __func__));
2731
2732 ASSERT(name);
2733 ASSERT(len >= 0);
2734
2735 /* Get MUST have return space */
2736 ASSERT(set || (arg && len));
2737
2738 /* Set does NOT take qualifiers */
2739 ASSERT(!set || (!params && !plen));
2740
2741 /* Look up var locally; if not found pass to host driver */
2742 vi = bcm_iovar_lookup(dhdsdio_iovars, name);
2743 if (vi == NULL) {
2744 dhd_os_sdlock(bus->dhd);
2745
2746 BUS_WAKE(bus);
2747
2748 /* Turn on clock in case SD command needs backplane */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002749 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002750
2751 bcmerror =
2752 bcmsdh_iovar_op(bus->sdh, name, params, plen, arg, len,
2753 set);
2754
2755 /* Check for bus configuration changes of interest */
2756
2757 /* If it was divisor change, read the new one */
2758 if (set && strcmp(name, "sd_divisor") == 0) {
2759 if (bcmsdh_iovar_op(bus->sdh, "sd_divisor", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002760 &bus->sd_divisor, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02002761 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002762 bus->sd_divisor = -1;
2763 DHD_ERROR(("%s: fail on %s get\n", __func__,
2764 name));
2765 } else {
2766 DHD_INFO(("%s: noted %s update, value now %d\n",
2767 __func__, name, bus->sd_divisor));
2768 }
2769 }
2770 /* If it was a mode change, read the new one */
2771 if (set && strcmp(name, "sd_mode") == 0) {
2772 if (bcmsdh_iovar_op(bus->sdh, "sd_mode", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002773 &bus->sd_mode, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02002774 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002775 bus->sd_mode = -1;
2776 DHD_ERROR(("%s: fail on %s get\n", __func__,
2777 name));
2778 } else {
2779 DHD_INFO(("%s: noted %s update, value now %d\n",
2780 __func__, name, bus->sd_mode));
2781 }
2782 }
2783 /* Similar check for blocksize change */
2784 if (set && strcmp(name, "sd_blocksize") == 0) {
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002785 s32 fnum = 2;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002786 if (bcmsdh_iovar_op
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07002787 (bus->sdh, "sd_blocksize", &fnum, sizeof(s32),
2788 &bus->blocksize, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02002789 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002790 bus->blocksize = 0;
2791 DHD_ERROR(("%s: fail on %s get\n", __func__,
2792 "sd_blocksize"));
2793 } else {
2794 DHD_INFO(("%s: noted %s update, value now %d\n",
2795 __func__, "sd_blocksize",
2796 bus->blocksize));
2797 }
2798 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07002799 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002800
2801 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002802 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07002803 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002804 }
2805
2806 dhd_os_sdunlock(bus->dhd);
2807 goto exit;
2808 }
2809
2810 DHD_CTL(("%s: %s %s, len %d plen %d\n", __func__,
2811 name, (set ? "set" : "get"), len, plen));
2812
2813 /* set up 'params' pointer in case this is a set command so that
2814 * the convenience int and bool code can be common to set and get
2815 */
2816 if (params == NULL) {
2817 params = arg;
2818 plen = len;
2819 }
2820
2821 if (vi->type == IOVT_VOID)
2822 val_size = 0;
2823 else if (vi->type == IOVT_BUFFER)
2824 val_size = len;
2825 else
2826 /* all other types are integer sized */
2827 val_size = sizeof(int);
2828
2829 actionid = set ? IOV_SVAL(vi->varid) : IOV_GVAL(vi->varid);
2830 bcmerror =
2831 dhdsdio_doiovar(bus, vi, actionid, name, params, plen, arg, len,
2832 val_size);
2833
2834exit:
2835 return bcmerror;
2836}
2837
2838void dhd_bus_stop(struct dhd_bus *bus, bool enforce_mutex)
2839{
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07002840 u32 local_hostintmask;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002841 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002842 uint retries;
2843 int err;
2844
2845 DHD_TRACE(("%s: Enter\n", __func__));
2846
2847 if (enforce_mutex)
2848 dhd_os_sdlock(bus->dhd);
2849
2850 BUS_WAKE(bus);
2851
2852 /* Enable clock for device interrupts */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002853 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002854
2855 /* Disable and clear interrupts at the chip level also */
2856 W_SDREG(0, &bus->regs->hostintmask, retries);
2857 local_hostintmask = bus->hostintmask;
2858 bus->hostintmask = 0;
2859
2860 /* Change our idea of bus state */
2861 bus->dhd->busstate = DHD_BUS_DOWN;
2862
2863 /* Force clocks on backplane to be sure F2 interrupt propagates */
2864 saveclk =
2865 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
2866 &err);
2867 if (!err) {
2868 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
2869 (saveclk | SBSDIO_FORCE_HT), &err);
2870 }
2871 if (err) {
2872 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
2873 __func__, err));
2874 }
2875
2876 /* Turn off the bus (F2), free any pending packets */
2877 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
2878 bcmsdh_intr_disable(bus->sdh);
2879 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN,
2880 SDIO_FUNC_ENABLE_1, NULL);
2881
2882 /* Clear any pending interrupts now that F2 is disabled */
2883 W_SDREG(local_hostintmask, &bus->regs->intstatus, retries);
2884
2885 /* Turn off the backplane clock (only) */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002886 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002887
2888 /* Clear the data packet queues */
Arend van Spriel537ebbb2011-03-02 21:18:47 +01002889 pktq_flush(&bus->txq, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002890
2891 /* Clear any held glomming stuff */
2892 if (bus->glomd)
Arend van Spriela30825a2011-03-02 21:18:46 +01002893 pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002894
2895 if (bus->glom)
Arend van Spriela30825a2011-03-02 21:18:46 +01002896 pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002897
2898 bus->glom = bus->glomd = NULL;
2899
2900 /* Clear rx control and wake any waiters */
2901 bus->rxlen = 0;
2902 dhd_os_ioctl_resp_wake(bus->dhd);
2903
2904 /* Reset some F2 state stuff */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002905 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002906 bus->tx_seq = bus->rx_seq = 0;
2907
2908 if (enforce_mutex)
2909 dhd_os_sdunlock(bus->dhd);
2910}
2911
2912int dhd_bus_init(dhd_pub_t *dhdp, bool enforce_mutex)
2913{
2914 dhd_bus_t *bus = dhdp->bus;
2915 dhd_timeout_t tmo;
2916 uint retries = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002917 u8 ready, enable;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002918 int err, ret = 0;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002919 u8 saveclk;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002920
2921 DHD_TRACE(("%s: Enter\n", __func__));
2922
2923 ASSERT(bus->dhd);
2924 if (!bus->dhd)
2925 return 0;
2926
2927 if (enforce_mutex)
2928 dhd_os_sdlock(bus->dhd);
2929
2930 /* Make sure backplane clock is on, needed to generate F2 interrupt */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002931 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002932 if (bus->clkstate != CLK_AVAIL)
2933 goto exit;
2934
2935 /* Force clocks on backplane to be sure F2 interrupt propagates */
2936 saveclk =
2937 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
2938 &err);
2939 if (!err) {
2940 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
2941 (saveclk | SBSDIO_FORCE_HT), &err);
2942 }
2943 if (err) {
2944 DHD_ERROR(("%s: Failed to force clock for F2: err %d\n",
2945 __func__, err));
2946 goto exit;
2947 }
2948
2949 /* Enable function 2 (frame transfers) */
2950 W_SDREG((SDPCM_PROT_VERSION << SMB_DATA_VERSION_SHIFT),
2951 &bus->regs->tosbmailboxdata, retries);
2952 enable = (SDIO_FUNC_ENABLE_1 | SDIO_FUNC_ENABLE_2);
2953
2954 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable, NULL);
2955
2956 /* Give the dongle some time to do its thing and set IOR2 */
2957 dhd_timeout_start(&tmo, DHD_WAIT_F2RDY * 1000);
2958
2959 ready = 0;
2960 while (ready != enable && !dhd_timeout_expired(&tmo))
2961 ready =
2962 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IORDY,
2963 NULL);
2964
2965 DHD_INFO(("%s: enable 0x%02x, ready 0x%02x (waited %uus)\n",
2966 __func__, enable, ready, tmo.elapsed));
2967
2968 /* If F2 successfully enabled, set core and enable interrupts */
2969 if (ready == enable) {
2970 /* Make sure we're talking to the core. */
2971 bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0);
2972 if (!(bus->regs))
2973 bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0);
2974
2975 /* Set up the interrupt mask and enable interrupts */
2976 bus->hostintmask = HOSTINTMASK;
2977 W_SDREG(bus->hostintmask, &bus->regs->hostintmask, retries);
2978
2979 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_WATERMARK,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07002980 (u8) watermark, &err);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002981
2982 /* Set bus state according to enable result */
2983 dhdp->busstate = DHD_BUS_DATA;
2984
2985 /* bcmsdh_intr_unmask(bus->sdh); */
2986
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07002987 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07002988 if (bus->intr) {
2989 DHD_INTR(("%s: enable SDIO device interrupts\n",
2990 __func__));
2991 bcmsdh_intr_enable(bus->sdh);
2992 } else {
2993 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
2994 bcmsdh_intr_disable(bus->sdh);
2995 }
2996
2997 }
2998
2999 else {
3000 /* Disable F2 again */
3001 enable = SDIO_FUNC_ENABLE_1;
3002 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, enable,
3003 NULL);
3004 }
3005
3006 /* Restore previous clock setting */
3007 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
3008 saveclk, &err);
3009
3010 /* If we didn't come up, turn off backplane clock */
3011 if (dhdp->busstate != DHD_BUS_DATA)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003012 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003013
3014exit:
3015 if (enforce_mutex)
3016 dhd_os_sdunlock(bus->dhd);
3017
3018 return ret;
3019}
3020
3021static void dhdsdio_rxfail(dhd_bus_t *bus, bool abort, bool rtx)
3022{
3023 bcmsdh_info_t *sdh = bus->sdh;
3024 sdpcmd_regs_t *regs = bus->regs;
3025 uint retries = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003026 u16 lastrbc;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003027 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003028 int err;
3029
3030 DHD_ERROR(("%s: %sterminate frame%s\n", __func__,
3031 (abort ? "abort command, " : ""),
3032 (rtx ? ", send NAK" : "")));
3033
3034 if (abort)
3035 bcmsdh_abort(sdh, SDIO_FUNC_2);
3036
3037 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_FRAMECTRL, SFC_RF_TERM,
3038 &err);
3039 bus->f1regdata++;
3040
3041 /* Wait until the packet has been flushed (device/FIFO stable) */
3042 for (lastrbc = retries = 0xffff; retries > 0; retries--) {
3043 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCHI,
3044 NULL);
3045 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_RFRAMEBCLO,
3046 NULL);
3047 bus->f1regdata += 2;
3048
3049 if ((hi == 0) && (lo == 0))
3050 break;
3051
3052 if ((hi > (lastrbc >> 8)) && (lo > (lastrbc & 0x00ff))) {
3053 DHD_ERROR(("%s: count growing: last 0x%04x now "
3054 "0x%04x\n",
3055 __func__, lastrbc, ((hi << 8) + lo)));
3056 }
3057 lastrbc = (hi << 8) + lo;
3058 }
3059
3060 if (!retries) {
3061 DHD_ERROR(("%s: count never zeroed: last 0x%04x\n",
3062 __func__, lastrbc));
3063 } else {
3064 DHD_INFO(("%s: flush took %d iterations\n", __func__,
3065 (0xffff - retries)));
3066 }
3067
3068 if (rtx) {
3069 bus->rxrtx++;
3070 W_SDREG(SMB_NAK, &regs->tosbmailbox, retries);
3071 bus->f1regdata++;
3072 if (retries <= retry_limit)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003073 bus->rxskip = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003074 }
3075
3076 /* Clear partial in any case */
3077 bus->nextlen = 0;
3078
3079 /* If we can't reach the device, signal failure */
3080 if (err || bcmsdh_regfail(sdh))
3081 bus->dhd->busstate = DHD_BUS_DOWN;
3082}
3083
3084static void
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003085dhdsdio_read_control(dhd_bus_t *bus, u8 *hdr, uint len, uint doff)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003086{
3087 bcmsdh_info_t *sdh = bus->sdh;
3088 uint rdlen, pad;
3089
3090 int sdret;
3091
3092 DHD_TRACE(("%s: Enter\n", __func__));
3093
3094 /* Control data already received in aligned rxctl */
3095 if ((bus->bus == SPI_BUS) && (!bus->usebufpool))
3096 goto gotpkt;
3097
3098 ASSERT(bus->rxbuf);
3099 /* Set rxctl for frame (w/optional alignment) */
3100 bus->rxctl = bus->rxbuf;
3101 if (dhd_alignctl) {
3102 bus->rxctl += firstread;
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07003103 pad = ((unsigned long)bus->rxctl % DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003104 if (pad)
3105 bus->rxctl += (DHD_SDALIGN - pad);
3106 bus->rxctl -= firstread;
3107 }
3108 ASSERT(bus->rxctl >= bus->rxbuf);
3109
3110 /* Copy the already-read portion over */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003111 memcpy(bus->rxctl, hdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003112 if (len <= firstread)
3113 goto gotpkt;
3114
3115 /* Copy the full data pkt in gSPI case and process ioctl. */
3116 if (bus->bus == SPI_BUS) {
Stanislav Fomichev02160692011-02-15 01:05:10 +03003117 memcpy(bus->rxctl, hdr, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003118 goto gotpkt;
3119 }
3120
3121 /* Raise rdlen to next SDIO block to avoid tail command */
3122 rdlen = len - firstread;
3123 if (bus->roundup && bus->blocksize && (rdlen > bus->blocksize)) {
3124 pad = bus->blocksize - (rdlen % bus->blocksize);
3125 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
3126 ((len + pad) < bus->dhd->maxctl))
3127 rdlen += pad;
3128 } else if (rdlen % DHD_SDALIGN) {
3129 rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
3130 }
3131
3132 /* Satisfy length-alignment requirements */
3133 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003134 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003135
3136 /* Drop if the read is too big or it exceeds our maximum */
3137 if ((rdlen + firstread) > bus->dhd->maxctl) {
3138 DHD_ERROR(("%s: %d-byte control read exceeds %d-byte buffer\n",
3139 __func__, rdlen, bus->dhd->maxctl));
3140 bus->dhd->rx_errors++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003141 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003142 goto done;
3143 }
3144
3145 if ((len - doff) > bus->dhd->maxctl) {
3146 DHD_ERROR(("%s: %d-byte ctl frame (%d-byte ctl data) exceeds "
3147 "%d-byte limit\n",
3148 __func__, len, (len - doff), bus->dhd->maxctl));
3149 bus->dhd->rx_errors++;
3150 bus->rx_toolong++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003151 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003152 goto done;
3153 }
3154
3155 /* Read remainder of frame body into the rxctl buffer */
3156 sdret =
3157 dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2, F2SYNC,
3158 (bus->rxctl + firstread), rdlen, NULL, NULL,
3159 NULL);
3160 bus->f2rxdata++;
3161 ASSERT(sdret != BCME_PENDING);
3162
3163 /* Control frame failures need retransmission */
3164 if (sdret < 0) {
3165 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3166 __func__, rdlen, sdret));
3167 bus->rxc_errors++; /* dhd.rx_ctlerrs is higher level */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003168 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003169 goto done;
3170 }
3171
3172gotpkt:
3173
3174#ifdef DHD_DEBUG
3175 if (DHD_BYTES_ON() && DHD_CTL_ON())
3176 prhex("RxCtrl", bus->rxctl, len);
3177#endif
3178
3179 /* Point to valid data and indicate its length */
3180 bus->rxctl += doff;
3181 bus->rxlen = len - doff;
3182
3183done:
3184 /* Awake any waiters */
3185 dhd_os_ioctl_resp_wake(bus->dhd);
3186}
3187
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003188static u8 dhdsdio_rxglom(dhd_bus_t *bus, u8 rxseq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003189{
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003190 u16 dlen, totlen;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003191 u8 *dptr, num = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003192
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003193 u16 sublen, check;
Arend van Sprielc26b1372010-11-23 14:06:23 +01003194 struct sk_buff *pfirst, *plast, *pnext, *save_pfirst;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003195
3196 int errcode;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003197 u8 chan, seq, doff, sfdoff;
3198 u8 txmax;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003199
3200 int ifidx = 0;
3201 bool usechain = bus->use_rxchain;
3202
3203 /* If packets, issue read(s) and send up packet chain */
3204 /* Return sequence numbers consumed? */
3205
3206 DHD_TRACE(("dhdsdio_rxglom: start: glomd %p glom %p\n", bus->glomd,
3207 bus->glom));
3208
3209 /* If there's a descriptor, generate the packet chain */
3210 if (bus->glomd) {
3211 dhd_os_sdlock_rxq(bus->dhd);
3212
3213 pfirst = plast = pnext = NULL;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003214 dlen = (u16) (bus->glomd->len);
3215 dptr = bus->glomd->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003216 if (!dlen || (dlen & 1)) {
3217 DHD_ERROR(("%s: bad glomd len(%d), ignore descriptor\n",
3218 __func__, dlen));
3219 dlen = 0;
3220 }
3221
3222 for (totlen = num = 0; dlen; num++) {
3223 /* Get (and move past) next length */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003224 sublen = get_unaligned_le16(dptr);
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003225 dlen -= sizeof(u16);
3226 dptr += sizeof(u16);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003227 if ((sublen < SDPCM_HDRLEN) ||
3228 ((num == 0) && (sublen < (2 * SDPCM_HDRLEN)))) {
3229 DHD_ERROR(("%s: descriptor len %d bad: %d\n",
3230 __func__, num, sublen));
3231 pnext = NULL;
3232 break;
3233 }
3234 if (sublen % DHD_SDALIGN) {
3235 DHD_ERROR(("%s: sublen %d not multiple of %d\n",
3236 __func__, sublen, DHD_SDALIGN));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003237 usechain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003238 }
3239 totlen += sublen;
3240
3241 /* For last frame, adjust read len so total
3242 is a block multiple */
3243 if (!dlen) {
3244 sublen +=
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003245 (roundup(totlen, bus->blocksize) - totlen);
3246 totlen = roundup(totlen, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003247 }
3248
3249 /* Allocate/chain packet for next subframe */
Arend van Spriela30825a2011-03-02 21:18:46 +01003250 pnext = pkt_buf_get_skb(sublen + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003251 if (pnext == NULL) {
Arend van Sprielf09e0232010-12-04 16:35:42 +01003252 DHD_ERROR(("%s: pkt_buf_get_skb failed, num %d len %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003253 __func__, num, sublen));
3254 break;
3255 }
Arend van Spriel54991ad2010-11-23 14:06:24 +01003256 ASSERT(!(pnext->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003257 if (!pfirst) {
3258 ASSERT(!plast);
3259 pfirst = plast = pnext;
3260 } else {
3261 ASSERT(plast);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003262 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003263 plast = pnext;
3264 }
3265
3266 /* Adhere to start alignment requirements */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01003267 PKTALIGN(pnext, sublen, DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003268 }
3269
3270 /* If all allocations succeeded, save packet chain
3271 in bus structure */
3272 if (pnext) {
3273 DHD_GLOM(("%s: allocated %d-byte packet chain for %d "
3274 "subframes\n", __func__, totlen, num));
3275 if (DHD_GLOM_ON() && bus->nextlen) {
3276 if (totlen != bus->nextlen) {
3277 DHD_GLOM(("%s: glomdesc mismatch: nextlen %d glomdesc %d " "rxseq %d\n",
3278 __func__, bus->nextlen,
3279 totlen, rxseq));
3280 }
3281 }
3282 bus->glom = pfirst;
3283 pfirst = pnext = NULL;
3284 } else {
3285 if (pfirst)
Arend van Spriela30825a2011-03-02 21:18:46 +01003286 pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003287 bus->glom = NULL;
3288 num = 0;
3289 }
3290
3291 /* Done with descriptor packet */
Arend van Spriela30825a2011-03-02 21:18:46 +01003292 pkt_buf_free_skb(bus->glomd);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003293 bus->glomd = NULL;
3294 bus->nextlen = 0;
3295
3296 dhd_os_sdunlock_rxq(bus->dhd);
3297 }
3298
3299 /* Ok -- either we just generated a packet chain,
3300 or had one from before */
3301 if (bus->glom) {
3302 if (DHD_GLOM_ON()) {
3303 DHD_GLOM(("%s: try superframe read, packet chain:\n",
3304 __func__));
Arend van Spriel54991ad2010-11-23 14:06:24 +01003305 for (pnext = bus->glom; pnext; pnext = pnext->next) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003306 DHD_GLOM((" %p: %p len 0x%04x (%d)\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003307 pnext, (u8 *) (pnext->data),
3308 pnext->len, pnext->len));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003309 }
3310 }
3311
3312 pfirst = bus->glom;
Arend van Sprield6075c92011-02-25 16:39:21 +01003313 dlen = (u16) pkttotlen(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003314
3315 /* Do an SDIO read for the superframe. Configurable iovar to
3316 * read directly into the chained packet, or allocate a large
3317 * packet and and copy into the chain.
3318 */
3319 if (usechain) {
3320 errcode = dhd_bcmsdh_recv_buf(bus,
3321 bcmsdh_cur_sbwad
3322 (bus->sdh), SDIO_FUNC_2,
3323 F2SYNC,
Arend van Spriel54991ad2010-11-23 14:06:24 +01003324 (u8 *) pfirst->data,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003325 dlen, pfirst, NULL, NULL);
3326 } else if (bus->dataptr) {
3327 errcode = dhd_bcmsdh_recv_buf(bus,
3328 bcmsdh_cur_sbwad
3329 (bus->sdh), SDIO_FUNC_2,
3330 F2SYNC, bus->dataptr,
3331 dlen, NULL, NULL, NULL);
3332 sublen =
Arend van Spriel26bcc182011-03-01 10:56:55 +01003333 (u16) pktfrombuf(pfirst, 0, dlen,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003334 bus->dataptr);
3335 if (sublen != dlen) {
3336 DHD_ERROR(("%s: FAILED TO COPY, dlen %d sublen %d\n",
3337 __func__, dlen, sublen));
3338 errcode = -1;
3339 }
3340 pnext = NULL;
3341 } else {
3342 DHD_ERROR(("COULDN'T ALLOC %d-BYTE GLOM, FORCE FAILURE\n",
3343 dlen));
3344 errcode = -1;
3345 }
3346 bus->f2rxdata++;
3347 ASSERT(errcode != BCME_PENDING);
3348
3349 /* On failure, kill the superframe, allow a couple retries */
3350 if (errcode < 0) {
3351 DHD_ERROR(("%s: glom read of %d bytes failed: %d\n",
3352 __func__, dlen, errcode));
3353 bus->dhd->rx_errors++;
3354
3355 if (bus->glomerr++ < 3) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003356 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003357 } else {
3358 bus->glomerr = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003359 dhdsdio_rxfail(bus, true, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003360 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01003361 pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003362 dhd_os_sdunlock_rxq(bus->dhd);
3363 bus->rxglomfail++;
3364 bus->glom = NULL;
3365 }
3366 return 0;
3367 }
3368#ifdef DHD_DEBUG
3369 if (DHD_GLOM_ON()) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003370 prhex("SUPERFRAME", pfirst->data,
3371 min_t(int, pfirst->len, 48));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003372 }
3373#endif
3374
3375 /* Validate the superframe header */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003376 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003377 sublen = get_unaligned_le16(dptr);
3378 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003379
3380 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3381 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3382 bus->nextlen = dptr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
3383 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
3384 DHD_INFO(("%s: nextlen too large (%d) seq %d\n",
3385 __func__, bus->nextlen, seq));
3386 bus->nextlen = 0;
3387 }
3388 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3389 txmax = SDPCM_WINDOW_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3390
3391 errcode = 0;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003392 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003393 DHD_ERROR(("%s (superframe): HW hdr error: len/check "
3394 "0x%04x/0x%04x\n", __func__, sublen, check));
3395 errcode = -1;
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003396 } else if (roundup(sublen, bus->blocksize) != dlen) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003397 DHD_ERROR(("%s (superframe): len 0x%04x, rounded "
3398 "0x%04x, expect 0x%04x\n",
3399 __func__, sublen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003400 roundup(sublen, bus->blocksize), dlen));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003401 errcode = -1;
3402 } else if (SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]) !=
3403 SDPCM_GLOM_CHANNEL) {
3404 DHD_ERROR(("%s (superframe): bad channel %d\n",
3405 __func__,
3406 SDPCM_PACKET_CHANNEL(&dptr
3407 [SDPCM_FRAMETAG_LEN])));
3408 errcode = -1;
3409 } else if (SDPCM_GLOMDESC(&dptr[SDPCM_FRAMETAG_LEN])) {
3410 DHD_ERROR(("%s (superframe): got second descriptor?\n",
3411 __func__));
3412 errcode = -1;
3413 } else if ((doff < SDPCM_HDRLEN) ||
Arend van Spriel54991ad2010-11-23 14:06:24 +01003414 (doff > (pfirst->len - SDPCM_HDRLEN))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003415 DHD_ERROR(("%s (superframe): Bad data offset %d: HW %d "
3416 "pkt %d min %d\n",
3417 __func__, doff, sublen,
Arend van Spriel54991ad2010-11-23 14:06:24 +01003418 pfirst->len, SDPCM_HDRLEN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003419 errcode = -1;
3420 }
3421
3422 /* Check sequence number of superframe SW header */
3423 if (rxseq != seq) {
3424 DHD_INFO(("%s: (superframe) rx_seq %d, expected %d\n",
3425 __func__, seq, rxseq));
3426 bus->rx_badseq++;
3427 rxseq = seq;
3428 }
3429
3430 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003431 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003432 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
3433 __func__, txmax, bus->tx_seq));
3434 txmax = bus->tx_seq + 2;
3435 }
3436 bus->tx_max = txmax;
3437
3438 /* Remove superframe header, remember offset */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003439 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003440 sfdoff = doff;
3441
3442 /* Validate all the subframe headers */
3443 for (num = 0, pnext = pfirst; pnext && !errcode;
Arend van Spriel54991ad2010-11-23 14:06:24 +01003444 num++, pnext = pnext->next) {
3445 dptr = (u8 *) (pnext->data);
3446 dlen = (u16) (pnext->len);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003447 sublen = get_unaligned_le16(dptr);
3448 check = get_unaligned_le16(dptr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003449 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3450 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3451#ifdef DHD_DEBUG
3452 if (DHD_GLOM_ON())
3453 prhex("subframe", dptr, 32);
3454#endif
3455
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003456 if ((u16)~(sublen ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003457 DHD_ERROR(("%s (subframe %d): HW hdr error: "
3458 "len/check 0x%04x/0x%04x\n",
3459 __func__, num, sublen, check));
3460 errcode = -1;
3461 } else if ((sublen > dlen) || (sublen < SDPCM_HDRLEN)) {
3462 DHD_ERROR(("%s (subframe %d): length mismatch: "
3463 "len 0x%04x, expect 0x%04x\n",
3464 __func__, num, sublen, dlen));
3465 errcode = -1;
3466 } else if ((chan != SDPCM_DATA_CHANNEL) &&
3467 (chan != SDPCM_EVENT_CHANNEL)) {
3468 DHD_ERROR(("%s (subframe %d): bad channel %d\n",
3469 __func__, num, chan));
3470 errcode = -1;
3471 } else if ((doff < SDPCM_HDRLEN) || (doff > sublen)) {
3472 DHD_ERROR(("%s (subframe %d): Bad data offset %d: HW %d min %d\n",
3473 __func__, num, doff, sublen,
3474 SDPCM_HDRLEN));
3475 errcode = -1;
3476 }
3477 }
3478
3479 if (errcode) {
3480 /* Terminate frame on error, request
3481 a couple retries */
3482 if (bus->glomerr++ < 3) {
3483 /* Restore superframe header space */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003484 skb_push(pfirst, sfdoff);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003485 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003486 } else {
3487 bus->glomerr = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003488 dhdsdio_rxfail(bus, true, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003489 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01003490 pkt_buf_free_skb(bus->glom);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003491 dhd_os_sdunlock_rxq(bus->dhd);
3492 bus->rxglomfail++;
3493 bus->glom = NULL;
3494 }
3495 bus->nextlen = 0;
3496 return 0;
3497 }
3498
3499 /* Basic SD framing looks ok - process each packet (header) */
3500 save_pfirst = pfirst;
3501 bus->glom = NULL;
3502 plast = NULL;
3503
3504 dhd_os_sdlock_rxq(bus->dhd);
3505 for (num = 0; pfirst; rxseq++, pfirst = pnext) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003506 pnext = pfirst->next;
3507 pfirst->next = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003508
Arend van Spriel54991ad2010-11-23 14:06:24 +01003509 dptr = (u8 *) (pfirst->data);
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003510 sublen = get_unaligned_le16(dptr);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003511 chan = SDPCM_PACKET_CHANNEL(&dptr[SDPCM_FRAMETAG_LEN]);
3512 seq = SDPCM_PACKET_SEQUENCE(&dptr[SDPCM_FRAMETAG_LEN]);
3513 doff = SDPCM_DOFFSET_VALUE(&dptr[SDPCM_FRAMETAG_LEN]);
3514
3515 DHD_GLOM(("%s: Get subframe %d, %p(%p/%d), sublen %d "
3516 "chan %d seq %d\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003517 __func__, num, pfirst, pfirst->data,
3518 pfirst->len, sublen, chan, seq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003519
3520 ASSERT((chan == SDPCM_DATA_CHANNEL)
3521 || (chan == SDPCM_EVENT_CHANNEL));
3522
3523 if (rxseq != seq) {
3524 DHD_GLOM(("%s: rx_seq %d, expected %d\n",
3525 __func__, seq, rxseq));
3526 bus->rx_badseq++;
3527 rxseq = seq;
3528 }
3529#ifdef DHD_DEBUG
3530 if (DHD_BYTES_ON() && DHD_DATA_ON())
3531 prhex("Rx Subframe Data", dptr, dlen);
3532#endif
3533
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01003534 __skb_trim(pfirst, sublen);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01003535 skb_pull(pfirst, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003536
Arend van Spriel54991ad2010-11-23 14:06:24 +01003537 if (pfirst->len == 0) {
Arend van Spriela30825a2011-03-02 21:18:46 +01003538 pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003539 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003540 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003541 } else {
3542 ASSERT(save_pfirst == pfirst);
3543 save_pfirst = pnext;
3544 }
3545 continue;
3546 } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pfirst) !=
3547 0) {
3548 DHD_ERROR(("%s: rx protocol error\n",
3549 __func__));
3550 bus->dhd->rx_errors++;
Arend van Spriela30825a2011-03-02 21:18:46 +01003551 pkt_buf_free_skb(pfirst);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003552 if (plast) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01003553 plast->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003554 } else {
3555 ASSERT(save_pfirst == pfirst);
3556 save_pfirst = pnext;
3557 }
3558 continue;
3559 }
3560
3561 /* this packet will go up, link back into
3562 chain and count it */
Arend van Spriel54991ad2010-11-23 14:06:24 +01003563 pfirst->next = pnext;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003564 plast = pfirst;
3565 num++;
3566
3567#ifdef DHD_DEBUG
3568 if (DHD_GLOM_ON()) {
3569 DHD_GLOM(("%s subframe %d to stack, %p(%p/%d) "
3570 "nxt/lnk %p/%p\n",
Arend van Spriel54991ad2010-11-23 14:06:24 +01003571 __func__, num, pfirst, pfirst->data,
3572 pfirst->len, pfirst->next,
3573 pfirst->prev));
3574 prhex("", (u8 *) pfirst->data,
3575 min_t(int, pfirst->len, 32));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003576 }
3577#endif /* DHD_DEBUG */
3578 }
3579 dhd_os_sdunlock_rxq(bus->dhd);
3580 if (num) {
3581 dhd_os_sdunlock(bus->dhd);
3582 dhd_rx_frame(bus->dhd, ifidx, save_pfirst, num);
3583 dhd_os_sdlock(bus->dhd);
3584 }
3585
3586 bus->rxglomframes++;
3587 bus->rxglompkts += num;
3588 }
3589 return num;
3590}
3591
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003592/* Return true if there may be more frames to read */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003593static uint dhdsdio_readframes(dhd_bus_t *bus, uint maxframes, bool *finished)
3594{
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003595 bcmsdh_info_t *sdh = bus->sdh;
3596
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003597 u16 len, check; /* Extracted hardware header fields */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003598 u8 chan, seq, doff; /* Extracted software header fields */
3599 u8 fcbits; /* Extracted fcbits from software header */
3600 u8 delta;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003601
Arend van Sprielc26b1372010-11-23 14:06:23 +01003602 struct sk_buff *pkt; /* Packet for event or data frames */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003603 u16 pad; /* Number of pad bytes to read */
3604 u16 rdlen; /* Total number of bytes to read */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003605 u8 rxseq; /* Next sequence number to expect */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003606 uint rxleft = 0; /* Remaining number of frames allowed */
3607 int sdret; /* Return code from bcmsdh calls */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003608 u8 txmax; /* Maximum tx sequence offered */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003609 bool len_consistent; /* Result of comparing readahead len and
3610 len from hw-hdr */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003611 u8 *rxbuf;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003612 int ifidx = 0;
3613 uint rxcount = 0; /* Total frames read */
3614
3615#if defined(DHD_DEBUG) || defined(SDTEST)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003616 bool sdtest = false; /* To limit message spew from test mode */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003617#endif
3618
3619 DHD_TRACE(("%s: Enter\n", __func__));
3620
3621 ASSERT(maxframes);
3622
3623#ifdef SDTEST
3624 /* Allow pktgen to override maxframes */
3625 if (bus->pktgen_count && (bus->pktgen_mode == DHD_PKTGEN_RECV)) {
3626 maxframes = bus->pktgen_count;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003627 sdtest = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003628 }
3629#endif
3630
3631 /* Not finished unless we encounter no more frames indication */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003632 *finished = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003633
3634 for (rxseq = bus->rx_seq, rxleft = maxframes;
3635 !bus->rxskip && rxleft && bus->dhd->busstate != DHD_BUS_DOWN;
3636 rxseq++, rxleft--) {
3637
3638 /* Handle glomming separately */
3639 if (bus->glom || bus->glomd) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003640 u8 cnt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003641 DHD_GLOM(("%s: calling rxglom: glomd %p, glom %p\n",
3642 __func__, bus->glomd, bus->glom));
3643 cnt = dhdsdio_rxglom(bus, rxseq);
3644 DHD_GLOM(("%s: rxglom returned %d\n", __func__, cnt));
3645 rxseq += cnt - 1;
3646 rxleft = (rxleft > cnt) ? (rxleft - cnt) : 1;
3647 continue;
3648 }
3649
3650 /* Try doing single read if we can */
3651 if (dhd_readahead && bus->nextlen) {
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003652 u16 nextlen = bus->nextlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003653 bus->nextlen = 0;
3654
3655 if (bus->bus == SPI_BUS) {
3656 rdlen = len = nextlen;
3657 } else {
3658 rdlen = len = nextlen << 4;
3659
3660 /* Pad read to blocksize for efficiency */
3661 if (bus->roundup && bus->blocksize
3662 && (rdlen > bus->blocksize)) {
3663 pad =
3664 bus->blocksize -
3665 (rdlen % bus->blocksize);
3666 if ((pad <= bus->roundup)
3667 && (pad < bus->blocksize)
3668 && ((rdlen + pad + firstread) <
3669 MAX_RX_DATASZ))
3670 rdlen += pad;
3671 } else if (rdlen % DHD_SDALIGN) {
3672 rdlen +=
3673 DHD_SDALIGN - (rdlen % DHD_SDALIGN);
3674 }
3675 }
3676
3677 /* We use bus->rxctl buffer in WinXP for initial
3678 * control pkt receives.
3679 * Later we use buffer-poll for data as well
3680 * as control packets.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003681 * This is required because dhd receives full
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003682 * frame in gSPI unlike SDIO.
3683 * After the frame is received we have to
3684 * distinguish whether it is data
3685 * or non-data frame.
3686 */
3687 /* Allocate a packet buffer */
3688 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01003689 pkt = pkt_buf_get_skb(rdlen + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003690 if (!pkt) {
3691 if (bus->bus == SPI_BUS) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003692 bus->usebufpool = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003693 bus->rxctl = bus->rxbuf;
3694 if (dhd_alignctl) {
3695 bus->rxctl += firstread;
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07003696 pad = ((unsigned long)bus->rxctl %
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003697 DHD_SDALIGN);
3698 if (pad)
3699 bus->rxctl +=
3700 (DHD_SDALIGN - pad);
3701 bus->rxctl -= firstread;
3702 }
3703 ASSERT(bus->rxctl >= bus->rxbuf);
3704 rxbuf = bus->rxctl;
3705 /* Read the entire frame */
3706 sdret = dhd_bcmsdh_recv_buf(bus,
3707 bcmsdh_cur_sbwad
3708 (sdh),
3709 SDIO_FUNC_2,
3710 F2SYNC,
3711 rxbuf,
3712 rdlen, NULL,
3713 NULL, NULL);
3714 bus->f2rxdata++;
3715 ASSERT(sdret != BCME_PENDING);
3716
3717 /* Control frame failures need
3718 retransmission */
3719 if (sdret < 0) {
3720 DHD_ERROR(("%s: read %d control bytes failed: %d\n",
3721 __func__,
3722 rdlen, sdret));
3723 /* dhd.rx_ctlerrs is higher */
3724 bus->rxc_errors++;
3725 dhd_os_sdunlock_rxq(bus->dhd);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003726 dhdsdio_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003727 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003728 SPI_BUS) ? false
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003729 : true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003730 continue;
3731 }
3732 } else {
3733 /* Give up on data,
3734 request rtx of events */
Arend van Sprielf09e0232010-12-04 16:35:42 +01003735 DHD_ERROR(("%s (nextlen): pkt_buf_get_skb failed: len %d rdlen %d " "expected rxseq %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003736 __func__, len, rdlen, rxseq));
3737 /* Just go try again w/normal
3738 header read */
3739 dhd_os_sdunlock_rxq(bus->dhd);
3740 continue;
3741 }
3742 } else {
3743 if (bus->bus == SPI_BUS)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003744 bus->usebufpool = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003745
Arend van Spriel54991ad2010-11-23 14:06:24 +01003746 ASSERT(!(pkt->prev));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01003747 PKTALIGN(pkt, rdlen, DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01003748 rxbuf = (u8 *) (pkt->data);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003749 /* Read the entire frame */
3750 sdret =
3751 dhd_bcmsdh_recv_buf(bus,
3752 bcmsdh_cur_sbwad(sdh),
3753 SDIO_FUNC_2, F2SYNC,
3754 rxbuf, rdlen, pkt, NULL,
3755 NULL);
3756 bus->f2rxdata++;
3757 ASSERT(sdret != BCME_PENDING);
3758
3759 if (sdret < 0) {
3760 DHD_ERROR(("%s (nextlen): read %d bytes failed: %d\n",
3761 __func__, rdlen, sdret));
Arend van Spriela30825a2011-03-02 21:18:46 +01003762 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003763 bus->dhd->rx_errors++;
3764 dhd_os_sdunlock_rxq(bus->dhd);
3765 /* Force retry w/normal header read.
Lucas De Marchi25985ed2011-03-30 22:57:33 -03003766 * Don't attempt NAK for
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003767 * gSPI
3768 */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003769 dhdsdio_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003770 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003771 SPI_BUS) ? false :
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003772 true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003773 continue;
3774 }
3775 }
3776 dhd_os_sdunlock_rxq(bus->dhd);
3777
3778 /* Now check the header */
Stanislav Fomichev02160692011-02-15 01:05:10 +03003779 memcpy(bus->rxhdr, rxbuf, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003780
3781 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003782 len = get_unaligned_le16(bus->rxhdr);
3783 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003784
3785 /* All zeros means readahead info was bad */
3786 if (!(len | check)) {
3787 DHD_INFO(("%s (nextlen): read zeros in HW "
3788 "header???\n", __func__));
3789 dhd_os_sdlock_rxq(bus->dhd);
3790 PKTFREE2();
3791 dhd_os_sdunlock_rxq(bus->dhd);
3792 GSPI_PR55150_BAILOUT;
3793 continue;
3794 }
3795
3796 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003797 if ((u16)~(len ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003798 DHD_ERROR(("%s (nextlen): HW hdr error: nextlen/len/check" " 0x%04x/0x%04x/0x%04x\n",
3799 __func__, nextlen, len, check));
3800 dhd_os_sdlock_rxq(bus->dhd);
3801 PKTFREE2();
3802 dhd_os_sdunlock_rxq(bus->dhd);
3803 bus->rx_badhdr++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003804 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003805 GSPI_PR55150_BAILOUT;
3806 continue;
3807 }
3808
3809 /* Validate frame length */
3810 if (len < SDPCM_HDRLEN) {
3811 DHD_ERROR(("%s (nextlen): HW hdr length "
3812 "invalid: %d\n", __func__, len));
3813 dhd_os_sdlock_rxq(bus->dhd);
3814 PKTFREE2();
3815 dhd_os_sdunlock_rxq(bus->dhd);
3816 GSPI_PR55150_BAILOUT;
3817 continue;
3818 }
3819
3820 /* Check for consistency withreadahead info */
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003821 len_consistent = (nextlen != (roundup(len, 16) >> 4));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003822 if (len_consistent) {
3823 /* Mismatch, force retry w/normal
3824 header (may be >4K) */
3825 DHD_ERROR(("%s (nextlen): mismatch, nextlen %d len %d rnd %d; " "expected rxseq %d\n",
3826 __func__, nextlen,
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07003827 len, roundup(len, 16), rxseq));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003828 dhd_os_sdlock_rxq(bus->dhd);
3829 PKTFREE2();
3830 dhd_os_sdunlock_rxq(bus->dhd);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003831 dhdsdio_rxfail(bus, true,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003832 (bus->bus ==
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003833 SPI_BUS) ? false : true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003834 GSPI_PR55150_BAILOUT;
3835 continue;
3836 }
3837
3838 /* Extract software header fields */
3839 chan =
3840 SDPCM_PACKET_CHANNEL(&bus->rxhdr
3841 [SDPCM_FRAMETAG_LEN]);
3842 seq =
3843 SDPCM_PACKET_SEQUENCE(&bus->rxhdr
3844 [SDPCM_FRAMETAG_LEN]);
3845 doff =
3846 SDPCM_DOFFSET_VALUE(&bus->rxhdr
3847 [SDPCM_FRAMETAG_LEN]);
3848 txmax =
3849 SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
3850
3851 bus->nextlen =
3852 bus->rxhdr[SDPCM_FRAMETAG_LEN +
3853 SDPCM_NEXTLEN_OFFSET];
3854 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
3855 DHD_INFO(("%s (nextlen): got frame w/nextlen too large" " (%d), seq %d\n",
3856 __func__, bus->nextlen, seq));
3857 bus->nextlen = 0;
3858 }
3859
3860 bus->dhd->rx_readahead_cnt++;
3861 /* Handle Flow Control */
3862 fcbits =
3863 SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
3864
3865 delta = 0;
3866 if (~bus->flowcontrol & fcbits) {
3867 bus->fc_xoff++;
3868 delta = 1;
3869 }
3870 if (bus->flowcontrol & ~fcbits) {
3871 bus->fc_xon++;
3872 delta = 1;
3873 }
3874
3875 if (delta) {
3876 bus->fc_rcvd++;
3877 bus->flowcontrol = fcbits;
3878 }
3879
3880 /* Check and update sequence number */
3881 if (rxseq != seq) {
3882 DHD_INFO(("%s (nextlen): rx_seq %d, expected "
3883 "%d\n", __func__, seq, rxseq));
3884 bus->rx_badseq++;
3885 rxseq = seq;
3886 }
3887
3888 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07003889 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003890 DHD_ERROR(("%s: got unlikely tx max %d with "
3891 "tx_seq %d\n",
3892 __func__, txmax, bus->tx_seq));
3893 txmax = bus->tx_seq + 2;
3894 }
3895 bus->tx_max = txmax;
3896
3897#ifdef DHD_DEBUG
3898 if (DHD_BYTES_ON() && DHD_DATA_ON())
3899 prhex("Rx Data", rxbuf, len);
3900 else if (DHD_HDRS_ON())
3901 prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
3902#endif
3903
3904 if (chan == SDPCM_CONTROL_CHANNEL) {
3905 if (bus->bus == SPI_BUS) {
3906 dhdsdio_read_control(bus, rxbuf, len,
3907 doff);
3908 if (bus->usebufpool) {
3909 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01003910 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003911 dhd_os_sdunlock_rxq(bus->dhd);
3912 }
3913 continue;
3914 } else {
3915 DHD_ERROR(("%s (nextlen): readahead on control" " packet %d?\n",
3916 __func__, seq));
3917 /* Force retry w/normal header read */
3918 bus->nextlen = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003919 dhdsdio_rxfail(bus, false, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003920 dhd_os_sdlock_rxq(bus->dhd);
3921 PKTFREE2();
3922 dhd_os_sdunlock_rxq(bus->dhd);
3923 continue;
3924 }
3925 }
3926
3927 if ((bus->bus == SPI_BUS) && !bus->usebufpool) {
3928 DHD_ERROR(("Received %d bytes on %d channel. Running out of " "rx pktbuf's or not yet malloced.\n",
3929 len, chan));
3930 continue;
3931 }
3932
3933 /* Validate data offset */
3934 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
3935 DHD_ERROR(("%s (nextlen): bad data offset %d: HW len %d min %d\n",
3936 __func__, doff, len, SDPCM_HDRLEN));
3937 dhd_os_sdlock_rxq(bus->dhd);
3938 PKTFREE2();
3939 dhd_os_sdunlock_rxq(bus->dhd);
3940 ASSERT(0);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003941 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003942 continue;
3943 }
3944
3945 /* All done with this one -- now deliver the packet */
3946 goto deliver;
3947 }
3948 /* gSPI frames should not be handled in fractions */
3949 if (bus->bus == SPI_BUS)
3950 break;
3951
3952 /* Read frame header (hardware and software) */
3953 sdret =
3954 dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
3955 F2SYNC, bus->rxhdr, firstread, NULL,
3956 NULL, NULL);
3957 bus->f2rxhdrs++;
3958 ASSERT(sdret != BCME_PENDING);
3959
3960 if (sdret < 0) {
3961 DHD_ERROR(("%s: RXHEADER FAILED: %d\n", __func__,
3962 sdret));
3963 bus->rx_hdrfail++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003964 dhdsdio_rxfail(bus, true, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003965 continue;
3966 }
3967#ifdef DHD_DEBUG
3968 if (DHD_BYTES_ON() || DHD_HDRS_ON())
3969 prhex("RxHdr", bus->rxhdr, SDPCM_HDRLEN);
3970#endif
3971
3972 /* Extract hardware header fields */
Stanislav Fomichev56dfe3c2011-02-20 21:43:33 +03003973 len = get_unaligned_le16(bus->rxhdr);
3974 check = get_unaligned_le16(bus->rxhdr + sizeof(u16));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003975
3976 /* All zeros means no more frames */
3977 if (!(len | check)) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07003978 *finished = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003979 break;
3980 }
3981
3982 /* Validate check bytes */
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07003983 if ((u16) ~(len ^ check)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003984 DHD_ERROR(("%s: HW hdr err: len/check 0x%04x/0x%04x\n",
3985 __func__, len, check));
3986 bus->rx_badhdr++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07003987 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07003988 continue;
3989 }
3990
3991 /* Validate frame length */
3992 if (len < SDPCM_HDRLEN) {
3993 DHD_ERROR(("%s: HW hdr length invalid: %d\n",
3994 __func__, len));
3995 continue;
3996 }
3997
3998 /* Extract software header fields */
3999 chan = SDPCM_PACKET_CHANNEL(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4000 seq = SDPCM_PACKET_SEQUENCE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4001 doff = SDPCM_DOFFSET_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4002 txmax = SDPCM_WINDOW_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4003
4004 /* Validate data offset */
4005 if ((doff < SDPCM_HDRLEN) || (doff > len)) {
4006 DHD_ERROR(("%s: Bad data offset %d: HW len %d, min %d "
4007 "seq %d\n",
4008 __func__, doff, len, SDPCM_HDRLEN, seq));
4009 bus->rx_badhdr++;
4010 ASSERT(0);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004011 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004012 continue;
4013 }
4014
4015 /* Save the readahead length if there is one */
4016 bus->nextlen =
4017 bus->rxhdr[SDPCM_FRAMETAG_LEN + SDPCM_NEXTLEN_OFFSET];
4018 if ((bus->nextlen << 4) > MAX_RX_DATASZ) {
4019 DHD_INFO(("%s (nextlen): got frame w/nextlen too large "
4020 "(%d), seq %d\n",
4021 __func__, bus->nextlen, seq));
4022 bus->nextlen = 0;
4023 }
4024
4025 /* Handle Flow Control */
4026 fcbits = SDPCM_FCMASK_VALUE(&bus->rxhdr[SDPCM_FRAMETAG_LEN]);
4027
4028 delta = 0;
4029 if (~bus->flowcontrol & fcbits) {
4030 bus->fc_xoff++;
4031 delta = 1;
4032 }
4033 if (bus->flowcontrol & ~fcbits) {
4034 bus->fc_xon++;
4035 delta = 1;
4036 }
4037
4038 if (delta) {
4039 bus->fc_rcvd++;
4040 bus->flowcontrol = fcbits;
4041 }
4042
4043 /* Check and update sequence number */
4044 if (rxseq != seq) {
4045 DHD_INFO(("%s: rx_seq %d, expected %d\n", __func__,
4046 seq, rxseq));
4047 bus->rx_badseq++;
4048 rxseq = seq;
4049 }
4050
4051 /* Check window for sanity */
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004052 if ((u8) (txmax - bus->tx_seq) > 0x40) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004053 DHD_ERROR(("%s: unlikely tx max %d with tx_seq %d\n",
4054 __func__, txmax, bus->tx_seq));
4055 txmax = bus->tx_seq + 2;
4056 }
4057 bus->tx_max = txmax;
4058
4059 /* Call a separate function for control frames */
4060 if (chan == SDPCM_CONTROL_CHANNEL) {
4061 dhdsdio_read_control(bus, bus->rxhdr, len, doff);
4062 continue;
4063 }
4064
4065 ASSERT((chan == SDPCM_DATA_CHANNEL)
4066 || (chan == SDPCM_EVENT_CHANNEL)
4067 || (chan == SDPCM_TEST_CHANNEL)
4068 || (chan == SDPCM_GLOM_CHANNEL));
4069
4070 /* Length to read */
4071 rdlen = (len > firstread) ? (len - firstread) : 0;
4072
4073 /* May pad read to blocksize for efficiency */
4074 if (bus->roundup && bus->blocksize &&
4075 (rdlen > bus->blocksize)) {
4076 pad = bus->blocksize - (rdlen % bus->blocksize);
4077 if ((pad <= bus->roundup) && (pad < bus->blocksize) &&
4078 ((rdlen + pad + firstread) < MAX_RX_DATASZ))
4079 rdlen += pad;
4080 } else if (rdlen % DHD_SDALIGN) {
4081 rdlen += DHD_SDALIGN - (rdlen % DHD_SDALIGN);
4082 }
4083
4084 /* Satisfy length-alignment requirements */
4085 if (forcealign && (rdlen & (ALIGNMENT - 1)))
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07004086 rdlen = roundup(rdlen, ALIGNMENT);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004087
4088 if ((rdlen + firstread) > MAX_RX_DATASZ) {
4089 /* Too long -- skip this frame */
4090 DHD_ERROR(("%s: too long: len %d rdlen %d\n",
4091 __func__, len, rdlen));
4092 bus->dhd->rx_errors++;
4093 bus->rx_toolong++;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004094 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004095 continue;
4096 }
4097
4098 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01004099 pkt = pkt_buf_get_skb(rdlen + firstread + DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004100 if (!pkt) {
4101 /* Give up on data, request rtx of events */
Arend van Sprielf09e0232010-12-04 16:35:42 +01004102 DHD_ERROR(("%s: pkt_buf_get_skb failed: rdlen %d chan %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004103 __func__, rdlen, chan));
4104 bus->dhd->rx_dropped++;
4105 dhd_os_sdunlock_rxq(bus->dhd);
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004106 dhdsdio_rxfail(bus, false, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004107 continue;
4108 }
4109 dhd_os_sdunlock_rxq(bus->dhd);
4110
Arend van Spriel54991ad2010-11-23 14:06:24 +01004111 ASSERT(!(pkt->prev));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004112
4113 /* Leave room for what we already read, and align remainder */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004114 ASSERT(firstread < pkt->len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004115 skb_pull(pkt, firstread);
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004116 PKTALIGN(pkt, rdlen, DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004117
4118 /* Read the remaining frame data */
4119 sdret =
4120 dhd_bcmsdh_recv_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
Arend van Spriel54991ad2010-11-23 14:06:24 +01004121 F2SYNC, ((u8 *) (pkt->data)), rdlen,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004122 pkt, NULL, NULL);
4123 bus->f2rxdata++;
4124 ASSERT(sdret != BCME_PENDING);
4125
4126 if (sdret < 0) {
4127 DHD_ERROR(("%s: read %d %s bytes failed: %d\n",
4128 __func__, rdlen,
4129 ((chan ==
4130 SDPCM_EVENT_CHANNEL) ? "event" : ((chan ==
4131 SDPCM_DATA_CHANNEL)
4132 ? "data" : "test")),
4133 sdret));
4134 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01004135 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004136 dhd_os_sdunlock_rxq(bus->dhd);
4137 bus->dhd->rx_errors++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004138 dhdsdio_rxfail(bus, true, RETRYCHAN(chan));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004139 continue;
4140 }
4141
4142 /* Copy the already-read portion */
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004143 skb_push(pkt, firstread);
Stanislav Fomichev02160692011-02-15 01:05:10 +03004144 memcpy(pkt->data, bus->rxhdr, firstread);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004145
4146#ifdef DHD_DEBUG
4147 if (DHD_BYTES_ON() && DHD_DATA_ON())
Arend van Spriel54991ad2010-11-23 14:06:24 +01004148 prhex("Rx Data", pkt->data, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004149#endif
4150
4151deliver:
4152 /* Save superframe descriptor and allocate packet frame */
4153 if (chan == SDPCM_GLOM_CHANNEL) {
4154 if (SDPCM_GLOMDESC(&bus->rxhdr[SDPCM_FRAMETAG_LEN])) {
4155 DHD_GLOM(("%s: glom descriptor, %d bytes:\n",
4156 __func__, len));
4157#ifdef DHD_DEBUG
4158 if (DHD_GLOM_ON()) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01004159 prhex("Glom Data", pkt->data, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004160 }
4161#endif
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004162 __skb_trim(pkt, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004163 ASSERT(doff == SDPCM_HDRLEN);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004164 skb_pull(pkt, SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004165 bus->glomd = pkt;
4166 } else {
4167 DHD_ERROR(("%s: glom superframe w/o "
4168 "descriptor!\n", __func__));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004169 dhdsdio_rxfail(bus, false, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004170 }
4171 continue;
4172 }
4173
4174 /* Fill in packet len and prio, deliver upward */
Arend van Spriel2cb8ada2010-11-18 20:46:44 +01004175 __skb_trim(pkt, len);
Arend van Sprielc303ecb2010-11-18 20:46:43 +01004176 skb_pull(pkt, doff);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004177
4178#ifdef SDTEST
4179 /* Test channel packets are processed separately */
4180 if (chan == SDPCM_TEST_CHANNEL) {
4181 dhdsdio_testrcv(bus, pkt, seq);
4182 continue;
4183 }
4184#endif /* SDTEST */
4185
Arend van Spriel54991ad2010-11-23 14:06:24 +01004186 if (pkt->len == 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004187 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01004188 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004189 dhd_os_sdunlock_rxq(bus->dhd);
4190 continue;
4191 } else if (dhd_prot_hdrpull(bus->dhd, &ifidx, pkt) != 0) {
4192 DHD_ERROR(("%s: rx protocol error\n", __func__));
4193 dhd_os_sdlock_rxq(bus->dhd);
Arend van Spriela30825a2011-03-02 21:18:46 +01004194 pkt_buf_free_skb(pkt);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004195 dhd_os_sdunlock_rxq(bus->dhd);
4196 bus->dhd->rx_errors++;
4197 continue;
4198 }
4199
4200 /* Unlock during rx call */
4201 dhd_os_sdunlock(bus->dhd);
4202 dhd_rx_frame(bus->dhd, ifidx, pkt, 1);
4203 dhd_os_sdlock(bus->dhd);
4204 }
4205 rxcount = maxframes - rxleft;
4206#ifdef DHD_DEBUG
4207 /* Message if we hit the limit */
4208 if (!rxleft && !sdtest)
4209 DHD_DATA(("%s: hit rx limit of %d frames\n", __func__,
4210 maxframes));
4211 else
4212#endif /* DHD_DEBUG */
4213 DHD_DATA(("%s: processed %d frames\n", __func__, rxcount));
4214 /* Back off rxseq if awaiting rtx, update rx_seq */
4215 if (bus->rxskip)
4216 rxseq--;
4217 bus->rx_seq = rxseq;
4218
4219 return rxcount;
4220}
4221
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004222static u32 dhdsdio_hostmail(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004223{
4224 sdpcmd_regs_t *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004225 u32 intstatus = 0;
4226 u32 hmb_data;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004227 u8 fcbits;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004228 uint retries = 0;
4229
4230 DHD_TRACE(("%s: Enter\n", __func__));
4231
4232 /* Read mailbox data and ack that we did so */
4233 R_SDREG(hmb_data, &regs->tohostmailboxdata, retries);
4234 if (retries <= retry_limit)
4235 W_SDREG(SMB_INT_ACK, &regs->tosbmailbox, retries);
4236 bus->f1regdata += 2;
4237
4238 /* Dongle recomposed rx frames, accept them again */
4239 if (hmb_data & HMB_DATA_NAKHANDLED) {
4240 DHD_INFO(("Dongle reports NAK handled, expect rtx of %d\n",
4241 bus->rx_seq));
4242 if (!bus->rxskip)
4243 DHD_ERROR(("%s: unexpected NAKHANDLED!\n", __func__));
4244
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004245 bus->rxskip = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004246 intstatus |= I_HMB_FRAME_IND;
4247 }
4248
4249 /*
4250 * DEVREADY does not occur with gSPI.
4251 */
4252 if (hmb_data & (HMB_DATA_DEVREADY | HMB_DATA_FWREADY)) {
4253 bus->sdpcm_ver =
4254 (hmb_data & HMB_DATA_VERSION_MASK) >>
4255 HMB_DATA_VERSION_SHIFT;
4256 if (bus->sdpcm_ver != SDPCM_PROT_VERSION)
4257 DHD_ERROR(("Version mismatch, dongle reports %d, "
4258 "expecting %d\n",
4259 bus->sdpcm_ver, SDPCM_PROT_VERSION));
4260 else
4261 DHD_INFO(("Dongle ready, protocol version %d\n",
4262 bus->sdpcm_ver));
4263 }
4264
4265 /*
4266 * Flow Control has been moved into the RX headers and this out of band
4267 * method isn't used any more. Leae this here for possibly
4268 * remaining backward
4269 * compatible with older dongles
4270 */
4271 if (hmb_data & HMB_DATA_FC) {
4272 fcbits =
4273 (hmb_data & HMB_DATA_FCDATA_MASK) >> HMB_DATA_FCDATA_SHIFT;
4274
4275 if (fcbits & ~bus->flowcontrol)
4276 bus->fc_xoff++;
4277 if (bus->flowcontrol & ~fcbits)
4278 bus->fc_xon++;
4279
4280 bus->fc_rcvd++;
4281 bus->flowcontrol = fcbits;
4282 }
4283
4284 /* Shouldn't be any others */
4285 if (hmb_data & ~(HMB_DATA_DEVREADY |
4286 HMB_DATA_NAKHANDLED |
4287 HMB_DATA_FC |
4288 HMB_DATA_FWREADY |
4289 HMB_DATA_FCDATA_MASK | HMB_DATA_VERSION_MASK)) {
4290 DHD_ERROR(("Unknown mailbox data content: 0x%02x\n", hmb_data));
4291 }
4292
4293 return intstatus;
4294}
4295
4296bool dhdsdio_dpc(dhd_bus_t *bus)
4297{
4298 bcmsdh_info_t *sdh = bus->sdh;
4299 sdpcmd_regs_t *regs = bus->regs;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004300 u32 intstatus, newstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004301 uint retries = 0;
4302 uint rxlimit = dhd_rxbound; /* Rx frames to read before resched */
4303 uint txlimit = dhd_txbound; /* Tx frames to send before resched */
4304 uint framecnt = 0; /* Temporary counter of tx/rx frames */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004305 bool rxdone = true; /* Flag for no more read data */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004306 bool resched = false; /* Flag indicating resched wanted */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004307
4308 DHD_TRACE(("%s: Enter\n", __func__));
4309
4310 /* Start with leftover status bits */
4311 intstatus = bus->intstatus;
4312
4313 dhd_os_sdlock(bus->dhd);
4314
4315 /* If waiting for HTAVAIL, check status */
4316 if (bus->clkstate == CLK_PENDING) {
4317 int err;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004318 u8 clkctl, devctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004319
4320#ifdef DHD_DEBUG
4321 /* Check for inconsistent device control */
4322 devctl =
4323 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL, &err);
4324 if (err) {
4325 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4326 __func__, err));
4327 bus->dhd->busstate = DHD_BUS_DOWN;
4328 } else {
4329 ASSERT(devctl & SBSDIO_DEVCTL_CA_INT_ONLY);
4330 }
4331#endif /* DHD_DEBUG */
4332
4333 /* Read CSR, if clock on switch to AVAIL, else ignore */
4334 clkctl =
4335 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
4336 &err);
4337 if (err) {
4338 DHD_ERROR(("%s: error reading CSR: %d\n", __func__,
4339 err));
4340 bus->dhd->busstate = DHD_BUS_DOWN;
4341 }
4342
4343 DHD_INFO(("DPC: PENDING, devctl 0x%02x clkctl 0x%02x\n", devctl,
4344 clkctl));
4345
4346 if (SBSDIO_HTAV(clkctl)) {
4347 devctl =
4348 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
4349 &err);
4350 if (err) {
4351 DHD_ERROR(("%s: error reading DEVCTL: %d\n",
4352 __func__, err));
4353 bus->dhd->busstate = DHD_BUS_DOWN;
4354 }
4355 devctl &= ~SBSDIO_DEVCTL_CA_INT_ONLY;
4356 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_DEVICE_CTL,
4357 devctl, &err);
4358 if (err) {
4359 DHD_ERROR(("%s: error writing DEVCTL: %d\n",
4360 __func__, err));
4361 bus->dhd->busstate = DHD_BUS_DOWN;
4362 }
4363 bus->clkstate = CLK_AVAIL;
4364 } else {
4365 goto clkwait;
4366 }
4367 }
4368
4369 BUS_WAKE(bus);
4370
4371 /* Make sure backplane clock is on */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004372 dhdsdio_clkctl(bus, CLK_AVAIL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004373 if (bus->clkstate == CLK_PENDING)
4374 goto clkwait;
4375
4376 /* Pending interrupt indicates new device status */
4377 if (bus->ipend) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004378 bus->ipend = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004379 R_SDREG(newstatus, &regs->intstatus, retries);
4380 bus->f1regdata++;
4381 if (bcmsdh_regfail(bus->sdh))
4382 newstatus = 0;
4383 newstatus &= bus->hostintmask;
4384 bus->fcstate = !!(newstatus & I_HMB_FC_STATE);
4385 if (newstatus) {
4386 W_SDREG(newstatus, &regs->intstatus, retries);
4387 bus->f1regdata++;
4388 }
4389 }
4390
4391 /* Merge new bits with previous */
4392 intstatus |= newstatus;
4393 bus->intstatus = 0;
4394
4395 /* Handle flow-control change: read new state in case our ack
4396 * crossed another change interrupt. If change still set, assume
4397 * FC ON for safety, let next loop through do the debounce.
4398 */
4399 if (intstatus & I_HMB_FC_CHANGE) {
4400 intstatus &= ~I_HMB_FC_CHANGE;
4401 W_SDREG(I_HMB_FC_CHANGE, &regs->intstatus, retries);
4402 R_SDREG(newstatus, &regs->intstatus, retries);
4403 bus->f1regdata += 2;
4404 bus->fcstate =
4405 !!(newstatus & (I_HMB_FC_STATE | I_HMB_FC_CHANGE));
4406 intstatus |= (newstatus & bus->hostintmask);
4407 }
4408
4409 /* Handle host mailbox indication */
4410 if (intstatus & I_HMB_HOST_INT) {
4411 intstatus &= ~I_HMB_HOST_INT;
4412 intstatus |= dhdsdio_hostmail(bus);
4413 }
4414
4415 /* Generally don't ask for these, can get CRC errors... */
4416 if (intstatus & I_WR_OOSYNC) {
4417 DHD_ERROR(("Dongle reports WR_OOSYNC\n"));
4418 intstatus &= ~I_WR_OOSYNC;
4419 }
4420
4421 if (intstatus & I_RD_OOSYNC) {
4422 DHD_ERROR(("Dongle reports RD_OOSYNC\n"));
4423 intstatus &= ~I_RD_OOSYNC;
4424 }
4425
4426 if (intstatus & I_SBINT) {
4427 DHD_ERROR(("Dongle reports SBINT\n"));
4428 intstatus &= ~I_SBINT;
4429 }
4430
4431 /* Would be active due to wake-wlan in gSPI */
4432 if (intstatus & I_CHIPACTIVE) {
4433 DHD_INFO(("Dongle reports CHIPACTIVE\n"));
4434 intstatus &= ~I_CHIPACTIVE;
4435 }
4436
4437 /* Ignore frame indications if rxskip is set */
4438 if (bus->rxskip)
4439 intstatus &= ~I_HMB_FRAME_IND;
4440
4441 /* On frame indication, read available frames */
4442 if (PKT_AVAILABLE()) {
4443 framecnt = dhdsdio_readframes(bus, rxlimit, &rxdone);
4444 if (rxdone || bus->rxskip)
4445 intstatus &= ~I_HMB_FRAME_IND;
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004446 rxlimit -= min(framecnt, rxlimit);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004447 }
4448
4449 /* Keep still-pending events for next scheduling */
4450 bus->intstatus = intstatus;
4451
4452clkwait:
4453#if defined(OOB_INTR_ONLY)
4454 bcmsdh_oob_intr_set(1);
4455#endif /* (OOB_INTR_ONLY) */
4456 /* Re-enable interrupts to detect new device events (mailbox, rx frame)
4457 * or clock availability. (Allows tx loop to check ipend if desired.)
4458 * (Unless register access seems hosed, as we may not be able to ACK...)
4459 */
4460 if (bus->intr && bus->intdis && !bcmsdh_regfail(sdh)) {
4461 DHD_INTR(("%s: enable SDIO interrupts, rxdone %d framecnt %d\n",
4462 __func__, rxdone, framecnt));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004463 bus->intdis = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004464 bcmsdh_intr_enable(sdh);
4465 }
4466
4467 if (DATAOK(bus) && bus->ctrl_frame_stat &&
4468 (bus->clkstate == CLK_AVAIL)) {
4469 int ret, i;
4470
4471 ret =
4472 dhd_bcmsdh_send_buf(bus, bcmsdh_cur_sbwad(sdh), SDIO_FUNC_2,
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004473 F2SYNC, (u8 *) bus->ctrl_frame_buf,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004474 (u32) bus->ctrl_frame_len, NULL,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004475 NULL, NULL);
4476 ASSERT(ret != BCME_PENDING);
4477
4478 if (ret < 0) {
4479 /* On failure, abort the command and
4480 terminate the frame */
4481 DHD_INFO(("%s: sdio error %d, abort command and "
4482 "terminate frame.\n", __func__, ret));
4483 bus->tx_sderrs++;
4484
4485 bcmsdh_abort(sdh, SDIO_FUNC_2);
4486
4487 bcmsdh_cfg_write(sdh, SDIO_FUNC_1,
4488 SBSDIO_FUNC1_FRAMECTRL, SFC_WF_TERM,
4489 NULL);
4490 bus->f1regdata++;
4491
4492 for (i = 0; i < 3; i++) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004493 u8 hi, lo;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004494 hi = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
4495 SBSDIO_FUNC1_WFRAMEBCHI,
4496 NULL);
4497 lo = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
4498 SBSDIO_FUNC1_WFRAMEBCLO,
4499 NULL);
4500 bus->f1regdata += 2;
4501 if ((hi == 0) && (lo == 0))
4502 break;
4503 }
4504
4505 }
4506 if (ret == 0)
4507 bus->tx_seq = (bus->tx_seq + 1) % SDPCM_SEQUENCE_WRAP;
4508
Arend van Spriel0bef7742011-02-10 12:03:44 +01004509 DHD_INFO(("Return_dpc value is : %d\n", ret));
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004510 bus->ctrl_frame_stat = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004511 dhd_wait_event_wakeup(bus->dhd);
4512 }
4513 /* Send queued frames (limit 1 if rx may still be pending) */
4514 else if ((bus->clkstate == CLK_AVAIL) && !bus->fcstate &&
4515 pktq_mlen(&bus->txq, ~bus->flowcontrol) && txlimit
4516 && DATAOK(bus)) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004517 framecnt = rxdone ? txlimit : min(txlimit, dhd_txminmax);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004518 framecnt = dhdsdio_sendfromq(bus, framecnt);
4519 txlimit -= framecnt;
4520 }
4521
4522 /* Resched if events or tx frames are pending,
4523 else await next interrupt */
4524 /* On failed register access, all bets are off:
4525 no resched or interrupts */
4526 if ((bus->dhd->busstate == DHD_BUS_DOWN) || bcmsdh_regfail(sdh)) {
4527 DHD_ERROR(("%s: failed backplane access over SDIO, halting "
4528 "operation %d\n", __func__, bcmsdh_regfail(sdh)));
4529 bus->dhd->busstate = DHD_BUS_DOWN;
4530 bus->intstatus = 0;
4531 } else if (bus->clkstate == CLK_PENDING) {
4532 DHD_INFO(("%s: rescheduled due to CLK_PENDING awaiting "
4533 "I_CHIPACTIVE interrupt\n", __func__));
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004534 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004535 } else if (bus->intstatus || bus->ipend ||
4536 (!bus->fcstate && pktq_mlen(&bus->txq, ~bus->flowcontrol) &&
4537 DATAOK(bus)) || PKT_AVAILABLE()) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004538 resched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004539 }
4540
4541 bus->dpc_sched = resched;
4542
4543 /* If we're done for now, turn off clock request. */
4544 if ((bus->clkstate != CLK_PENDING)
4545 && bus->idletime == DHD_IDLE_IMMEDIATE) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004546 bus->activity = false;
4547 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004548 }
4549
4550 dhd_os_sdunlock(bus->dhd);
4551
4552 return resched;
4553}
4554
4555bool dhd_bus_dpc(struct dhd_bus *bus)
4556{
4557 bool resched;
4558
4559 /* Call the DPC directly. */
4560 DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __func__));
4561 resched = dhdsdio_dpc(bus);
4562
4563 return resched;
4564}
4565
4566void dhdsdio_isr(void *arg)
4567{
4568 dhd_bus_t *bus = (dhd_bus_t *) arg;
4569 bcmsdh_info_t *sdh;
4570
4571 DHD_TRACE(("%s: Enter\n", __func__));
4572
4573 if (!bus) {
4574 DHD_ERROR(("%s : bus is null pointer , exit\n", __func__));
4575 return;
4576 }
4577 sdh = bus->sdh;
4578
4579 if (bus->dhd->busstate == DHD_BUS_DOWN) {
4580 DHD_ERROR(("%s : bus is down. we have nothing to do\n",
4581 __func__));
4582 return;
4583 }
4584 /* Count the interrupt call */
4585 bus->intrcount++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004586 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004587
4588 /* Shouldn't get this interrupt if we're sleeping? */
4589 if (bus->sleeping) {
4590 DHD_ERROR(("INTERRUPT WHILE SLEEPING??\n"));
4591 return;
4592 }
4593
4594 /* Disable additional interrupts (is this needed now)? */
4595 if (bus->intr)
4596 DHD_INTR(("%s: disable SDIO interrupts\n", __func__));
4597 else
4598 DHD_ERROR(("dhdsdio_isr() w/o interrupt configured!\n"));
4599
4600 bcmsdh_intr_disable(sdh);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004601 bus->intdis = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004602
4603#if defined(SDIO_ISR_THREAD)
4604 DHD_TRACE(("Calling dhdsdio_dpc() from %s\n", __func__));
4605 while (dhdsdio_dpc(bus))
4606 ;
4607#else
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004608 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004609 dhd_sched_dpc(bus->dhd);
4610#endif
4611
4612}
4613
4614#ifdef SDTEST
4615static void dhdsdio_pktgen_init(dhd_bus_t *bus)
4616{
4617 /* Default to specified length, or full range */
4618 if (dhd_pktgen_len) {
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07004619 bus->pktgen_maxlen = min(dhd_pktgen_len, MAX_PKTGEN_LEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004620 bus->pktgen_minlen = bus->pktgen_maxlen;
4621 } else {
4622 bus->pktgen_maxlen = MAX_PKTGEN_LEN;
4623 bus->pktgen_minlen = 0;
4624 }
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004625 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004626
4627 /* Default to per-watchdog burst with 10s print time */
4628 bus->pktgen_freq = 1;
4629 bus->pktgen_print = 10000 / dhd_watchdog_ms;
4630 bus->pktgen_count = (dhd_pktgen * dhd_watchdog_ms + 999) / 1000;
4631
4632 /* Default to echo mode */
4633 bus->pktgen_mode = DHD_PKTGEN_ECHO;
4634 bus->pktgen_stop = 1;
4635}
4636
4637static void dhdsdio_pktgen(dhd_bus_t *bus)
4638{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004639 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004640 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004641 uint pktcount;
4642 uint fillbyte;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004643 u16 len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004644
4645 /* Display current count if appropriate */
4646 if (bus->pktgen_print && (++bus->pktgen_ptick >= bus->pktgen_print)) {
4647 bus->pktgen_ptick = 0;
Arend van Spriel0bef7742011-02-10 12:03:44 +01004648 printk(KERN_DEBUG "%s: send attempts %d rcvd %d\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004649 __func__, bus->pktgen_sent, bus->pktgen_rcvd);
4650 }
4651
4652 /* For recv mode, just make sure dongle has started sending */
4653 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
4654 if (!bus->pktgen_rcvd)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004655 dhdsdio_sdtest_set(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004656 return;
4657 }
4658
4659 /* Otherwise, generate or request the specified number of packets */
4660 for (pktcount = 0; pktcount < bus->pktgen_count; pktcount++) {
4661 /* Stop if total has been reached */
4662 if (bus->pktgen_total
4663 && (bus->pktgen_sent >= bus->pktgen_total)) {
4664 bus->pktgen_count = 0;
4665 break;
4666 }
4667
4668 /* Allocate an appropriate-sized packet */
4669 len = bus->pktgen_len;
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004670 pkt = pkt_buf_get_skb(
Jason Cooper9b890322010-09-30 15:15:39 -04004671 (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN),
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004672 true);
Jason Cooper9b890322010-09-30 15:15:39 -04004673 if (!pkt) {
Arend van Sprielf09e0232010-12-04 16:35:42 +01004674 DHD_ERROR(("%s: pkt_buf_get_skb failed!\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004675 break;
4676 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004677 PKTALIGN(pkt, (len + SDPCM_HDRLEN + SDPCM_TEST_HDRLEN),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004678 DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004679 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004680
4681 /* Write test header cmd and extra based on mode */
4682 switch (bus->pktgen_mode) {
4683 case DHD_PKTGEN_ECHO:
4684 *data++ = SDPCM_TEST_ECHOREQ;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004685 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004686 break;
4687
4688 case DHD_PKTGEN_SEND:
4689 *data++ = SDPCM_TEST_DISCARD;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004690 *data++ = (u8) bus->pktgen_sent;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004691 break;
4692
4693 case DHD_PKTGEN_RXBURST:
4694 *data++ = SDPCM_TEST_BURST;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004695 *data++ = (u8) bus->pktgen_count;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004696 break;
4697
4698 default:
4699 DHD_ERROR(("Unrecognized pktgen mode %d\n",
4700 bus->pktgen_mode));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004701 pkt_buf_free_skb(pkt, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004702 bus->pktgen_count = 0;
4703 return;
4704 }
4705
4706 /* Write test header length field */
4707 *data++ = (len >> 0);
4708 *data++ = (len >> 8);
4709
4710 /* Then fill in the remainder -- N/A for burst,
4711 but who cares... */
4712 for (fillbyte = 0; fillbyte < len; fillbyte++)
4713 *data++ =
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004714 SDPCM_TEST_FILL(fillbyte, (u8) bus->pktgen_sent);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004715
4716#ifdef DHD_DEBUG
4717 if (DHD_BYTES_ON() && DHD_DATA_ON()) {
Arend van Spriel54991ad2010-11-23 14:06:24 +01004718 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004719 prhex("dhdsdio_pktgen: Tx Data", data,
Arend van Spriel54991ad2010-11-23 14:06:24 +01004720 pkt->len - SDPCM_HDRLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004721 }
4722#endif
4723
4724 /* Send it */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004725 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004726 bus->pktgen_fail++;
4727 if (bus->pktgen_stop
4728 && bus->pktgen_stop == bus->pktgen_fail)
4729 bus->pktgen_count = 0;
4730 }
4731 bus->pktgen_sent++;
4732
4733 /* Bump length if not fixed, wrap at max */
4734 if (++bus->pktgen_len > bus->pktgen_maxlen)
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004735 bus->pktgen_len = (u16) bus->pktgen_minlen;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004736
4737 /* Special case for burst mode: just send one request! */
4738 if (bus->pktgen_mode == DHD_PKTGEN_RXBURST)
4739 break;
4740 }
4741}
4742
4743static void dhdsdio_sdtest_set(dhd_bus_t *bus, bool start)
4744{
Arend van Sprielc26b1372010-11-23 14:06:23 +01004745 struct sk_buff *pkt;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004746 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004747
4748 /* Allocate the packet */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004749 pkt = pkt_buf_get_skb(SDPCM_HDRLEN + SDPCM_TEST_HDRLEN + DHD_SDALIGN,
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004750 true);
Jason Cooper9b890322010-09-30 15:15:39 -04004751 if (!pkt) {
Arend van Sprielf09e0232010-12-04 16:35:42 +01004752 DHD_ERROR(("%s: pkt_buf_get_skb failed!\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004753 return;
4754 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004755 PKTALIGN(pkt, (SDPCM_HDRLEN + SDPCM_TEST_HDRLEN), DHD_SDALIGN);
Arend van Spriel54991ad2010-11-23 14:06:24 +01004756 data = (u8 *) (pkt->data) + SDPCM_HDRLEN;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004757
4758 /* Fill in the test header */
4759 *data++ = SDPCM_TEST_SEND;
4760 *data++ = start;
4761 *data++ = (bus->pktgen_maxlen >> 0);
4762 *data++ = (bus->pktgen_maxlen >> 8);
4763
4764 /* Send it */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004765 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true))
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004766 bus->pktgen_fail++;
4767}
4768
Arend van Sprielc26b1372010-11-23 14:06:23 +01004769static void dhdsdio_testrcv(dhd_bus_t *bus, struct sk_buff *pkt, uint seq)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004770{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004771 u8 *data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004772 uint pktlen;
4773
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004774 u8 cmd;
4775 u8 extra;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07004776 u16 len;
4777 u16 offset;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004778
4779 /* Check for min length */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004780 pktlen = pkt->len;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004781 if (pktlen < SDPCM_TEST_HDRLEN) {
4782 DHD_ERROR(("dhdsdio_restrcv: toss runt frame, pktlen %d\n",
4783 pktlen));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004784 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004785 return;
4786 }
4787
4788 /* Extract header fields */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004789 data = pkt->data;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004790 cmd = *data++;
4791 extra = *data++;
4792 len = *data++;
4793 len += *data++ << 8;
4794
4795 /* Check length for relevant commands */
4796 if (cmd == SDPCM_TEST_DISCARD || cmd == SDPCM_TEST_ECHOREQ
4797 || cmd == SDPCM_TEST_ECHORSP) {
4798 if (pktlen != len + SDPCM_TEST_HDRLEN) {
4799 DHD_ERROR(("dhdsdio_testrcv: frame length mismatch, "
4800 "pktlen %d seq %d" " cmd %d extra %d len %d\n",
4801 pktlen, seq, cmd, extra, len));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004802 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004803 return;
4804 }
4805 }
4806
4807 /* Process as per command */
4808 switch (cmd) {
4809 case SDPCM_TEST_ECHOREQ:
4810 /* Rx->Tx turnaround ok (even on NDIS w/current
4811 implementation) */
Arend van Spriel54991ad2010-11-23 14:06:24 +01004812 *(u8 *) (pkt->data) = SDPCM_TEST_ECHORSP;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004813 if (dhdsdio_txpkt(bus, pkt, SDPCM_TEST_CHANNEL, true) == 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004814 bus->pktgen_sent++;
4815 } else {
4816 bus->pktgen_fail++;
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004817 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004818 }
4819 bus->pktgen_rcvd++;
4820 break;
4821
4822 case SDPCM_TEST_ECHORSP:
4823 if (bus->ext_loop) {
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004824 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004825 bus->pktgen_rcvd++;
4826 break;
4827 }
4828
4829 for (offset = 0; offset < len; offset++, data++) {
4830 if (*data != SDPCM_TEST_FILL(offset, extra)) {
4831 DHD_ERROR(("dhdsdio_testrcv: echo data mismatch: " "offset %d (len %d) expect 0x%02x rcvd 0x%02x\n",
4832 offset, len,
4833 SDPCM_TEST_FILL(offset, extra), *data));
4834 break;
4835 }
4836 }
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004837 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004838 bus->pktgen_rcvd++;
4839 break;
4840
4841 case SDPCM_TEST_DISCARD:
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004842 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004843 bus->pktgen_rcvd++;
4844 break;
4845
4846 case SDPCM_TEST_BURST:
4847 case SDPCM_TEST_SEND:
4848 default:
4849 DHD_INFO(("dhdsdio_testrcv: unsupported or unknown command, "
4850 "pktlen %d seq %d" " cmd %d extra %d len %d\n",
4851 pktlen, seq, cmd, extra, len));
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01004852 pkt_buf_free_skb(pkt, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004853 break;
4854 }
4855
4856 /* For recv mode, stop at limie (and tell dongle to stop sending) */
4857 if (bus->pktgen_mode == DHD_PKTGEN_RECV) {
4858 if (bus->pktgen_total
4859 && (bus->pktgen_rcvd >= bus->pktgen_total)) {
4860 bus->pktgen_count = 0;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004861 dhdsdio_sdtest_set(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004862 }
4863 }
4864}
4865#endif /* SDTEST */
4866
4867extern bool dhd_bus_watchdog(dhd_pub_t *dhdp)
4868{
4869 dhd_bus_t *bus;
4870
4871 DHD_TIMER(("%s: Enter\n", __func__));
4872
4873 bus = dhdp->bus;
4874
4875 if (bus->dhd->dongle_reset)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004876 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004877
4878 /* Ignore the timer if simulating bus down */
4879 if (bus->sleeping)
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004880 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004881
4882 dhd_os_sdlock(bus->dhd);
4883
4884 /* Poll period: check device if appropriate. */
4885 if (bus->poll && (++bus->polltick >= bus->pollrate)) {
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004886 u32 intstatus = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004887
4888 /* Reset poll tick */
4889 bus->polltick = 0;
4890
4891 /* Check device if no interrupts */
4892 if (!bus->intr || (bus->intrcount == bus->lastintrs)) {
4893
4894 if (!bus->dpc_sched) {
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07004895 u8 devpend;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004896 devpend = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_0,
4897 SDIOD_CCCR_INTPEND,
4898 NULL);
4899 intstatus =
4900 devpend & (INTR_STATUS_FUNC1 |
4901 INTR_STATUS_FUNC2);
4902 }
4903
4904 /* If there is something, make like the ISR and
4905 schedule the DPC */
4906 if (intstatus) {
4907 bus->pollcnt++;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004908 bus->ipend = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004909 if (bus->intr)
4910 bcmsdh_intr_disable(bus->sdh);
4911
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004912 bus->dpc_sched = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004913 dhd_sched_dpc(bus->dhd);
4914
4915 }
4916 }
4917
4918 /* Update interrupt tracking */
4919 bus->lastintrs = bus->intrcount;
4920 }
4921#ifdef DHD_DEBUG
4922 /* Poll for console output periodically */
4923 if (dhdp->busstate == DHD_BUS_DATA && dhd_console_ms != 0) {
4924 bus->console.count += dhd_watchdog_ms;
4925 if (bus->console.count >= dhd_console_ms) {
4926 bus->console.count -= dhd_console_ms;
4927 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004928 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004929 if (dhdsdio_readconsole(bus) < 0)
4930 dhd_console_ms = 0; /* On error,
4931 stop trying */
4932 }
4933 }
4934#endif /* DHD_DEBUG */
4935
4936#ifdef SDTEST
4937 /* Generate packets if configured */
4938 if (bus->pktgen_count && (++bus->pktgen_tick >= bus->pktgen_freq)) {
4939 /* Make sure backplane clock is on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004940 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004941 bus->pktgen_tick = 0;
4942 dhdsdio_pktgen(bus);
4943 }
4944#endif
4945
4946 /* On idle timeout clear activity flag and/or turn off clock */
4947 if ((bus->idletime > 0) && (bus->clkstate == CLK_AVAIL)) {
4948 if (++bus->idlecount >= bus->idletime) {
4949 bus->idlecount = 0;
4950 if (bus->activity) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004951 bus->activity = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004952 dhd_os_wd_timer(bus->dhd, dhd_watchdog_ms);
4953 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004954 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004955 }
4956 }
4957 }
4958
4959 dhd_os_sdunlock(bus->dhd);
4960
4961 return bus->ipend;
4962}
4963
4964#ifdef DHD_DEBUG
Greg Kroah-Hartman580a0bd2010-10-05 11:09:48 -07004965extern int dhd_bus_console_in(dhd_pub_t *dhdp, unsigned char *msg, uint msglen)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004966{
4967 dhd_bus_t *bus = dhdp->bus;
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07004968 u32 addr, val;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004969 int rv;
Arend van Sprielc26b1372010-11-23 14:06:23 +01004970 struct sk_buff *pkt;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004971
4972 /* Address could be zero if CONSOLE := 0 in dongle Makefile */
4973 if (bus->console_addr == 0)
4974 return BCME_UNSUPPORTED;
4975
4976 /* Exclusive bus access */
4977 dhd_os_sdlock(bus->dhd);
4978
4979 /* Don't allow input if dongle is in reset */
4980 if (bus->dhd->dongle_reset) {
4981 dhd_os_sdunlock(bus->dhd);
4982 return BCME_NOTREADY;
4983 }
4984
4985 /* Request clock to allow SDIO accesses */
4986 BUS_WAKE(bus);
4987 /* No pend allowed since txpkt is called later, ht clk has to be on */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07004988 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004989
4990 /* Zero cbuf_index */
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -07004991 addr = bus->console_addr + offsetof(hndrte_cons_t, cbuf_idx);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03004992 val = cpu_to_le32(0);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004993 rv = dhdsdio_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04004994 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07004995 goto done;
4996
4997 /* Write message into cbuf */
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -07004998 addr = bus->console_addr + offsetof(hndrte_cons_t, cbuf);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07004999 rv = dhdsdio_membytes(bus, true, addr, (u8 *)msg, msglen);
Jason Cooper9b890322010-09-30 15:15:39 -04005000 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005001 goto done;
5002
5003 /* Write length into vcons_in */
Greg Kroah-Hartmance0f1b82010-10-08 11:44:45 -07005004 addr = bus->console_addr + offsetof(hndrte_cons_t, vcons_in);
Stanislav Fomichev628f10b2011-02-20 21:43:32 +03005005 val = cpu_to_le32(msglen);
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005006 rv = dhdsdio_membytes(bus, true, addr, (u8 *)&val, sizeof(val));
Jason Cooper9b890322010-09-30 15:15:39 -04005007 if (rv < 0)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005008 goto done;
5009
5010 /* Bump dongle by sending an empty event pkt.
5011 * sdpcm_sendup (RX) checks for virtual console input.
5012 */
Arend van Spriela30825a2011-03-02 21:18:46 +01005013 pkt = pkt_buf_get_skb(4 + SDPCM_RESERVE);
Jason Cooper9b890322010-09-30 15:15:39 -04005014 if ((pkt != NULL) && bus->clkstate == CLK_AVAIL)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005015 dhdsdio_txpkt(bus, pkt, SDPCM_EVENT_CHANNEL, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005016
5017done:
5018 if ((bus->idletime == DHD_IDLE_IMMEDIATE) && !bus->dpc_sched) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005019 bus->activity = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005020 dhdsdio_clkctl(bus, CLK_NONE, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005021 }
5022
5023 dhd_os_sdunlock(bus->dhd);
5024
5025 return rv;
5026}
5027#endif /* DHD_DEBUG */
5028
5029#ifdef DHD_DEBUG
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005030static void dhd_dump_cis(uint fn, u8 *cis)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005031{
5032 uint byte, tag, tdata;
5033 DHD_INFO(("Function %d CIS:\n", fn));
5034
5035 for (tdata = byte = 0; byte < SBSDIO_CIS_SIZE_LIMIT; byte++) {
5036 if ((byte % 16) == 0)
5037 DHD_INFO((" "));
5038 DHD_INFO(("%02x ", cis[byte]));
5039 if ((byte % 16) == 15)
5040 DHD_INFO(("\n"));
5041 if (!tdata--) {
5042 tag = cis[byte];
5043 if (tag == 0xff)
5044 break;
5045 else if (!tag)
5046 tdata = 0;
5047 else if ((byte + 1) < SBSDIO_CIS_SIZE_LIMIT)
5048 tdata = cis[byte + 1] + 1;
5049 else
5050 DHD_INFO(("]"));
5051 }
5052 }
5053 if ((byte % 16) != 15)
5054 DHD_INFO(("\n"));
5055}
5056#endif /* DHD_DEBUG */
5057
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005058static bool dhdsdio_chipmatch(u16 chipid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005059{
5060 if (chipid == BCM4325_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005061 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005062 if (chipid == BCM4329_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005063 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005064 if (chipid == BCM4319_CHIP_ID)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005065 return true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005066 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005067}
5068
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005069static void *dhdsdio_probe(u16 venid, u16 devid, u16 bus_no,
5070 u16 slot, u16 func, uint bustype, void *regsva,
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005071 void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005072{
5073 int ret;
5074 dhd_bus_t *bus;
5075
5076 /* Init global variables at run-time, not as part of the declaration.
5077 * This is required to support init/de-init of the driver.
5078 * Initialization
5079 * of globals as part of the declaration results in non-deterministic
5080 * behavior since the value of the globals may be different on the
5081 * first time that the driver is initialized vs subsequent
5082 * initializations.
5083 */
5084 dhd_txbound = DHD_TXBOUND;
5085 dhd_rxbound = DHD_RXBOUND;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005086 dhd_alignctl = true;
5087 sd1idle = true;
5088 dhd_readahead = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005089 retrydata = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005090 dhd_dongle_memsize = 0;
5091 dhd_txminmax = DHD_TXMINMAX;
5092
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005093 forcealign = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005094
5095 dhd_common_init();
5096
5097 DHD_TRACE(("%s: Enter\n", __func__));
5098 DHD_INFO(("%s: venid 0x%04x devid 0x%04x\n", __func__, venid, devid));
5099
5100 /* We make assumptions about address window mappings */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005101 ASSERT((unsigned long)regsva == SI_ENUM_BASE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005102
5103 /* BCMSDH passes venid and devid based on CIS parsing -- but
5104 * low-power start
5105 * means early parse could fail, so here we should get either an ID
5106 * we recognize OR (-1) indicating we must request power first.
5107 */
5108 /* Check the Vendor ID */
5109 switch (venid) {
5110 case 0x0000:
Stanislav Fomichevbe1c09f2011-03-28 01:31:36 +04005111 case PCI_VENDOR_ID_BROADCOM:
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005112 break;
5113 default:
5114 DHD_ERROR(("%s: unknown vendor: 0x%04x\n", __func__, venid));
5115 return NULL;
5116 }
5117
5118 /* Check the Device ID and make sure it's one that we support */
5119 switch (devid) {
5120 case BCM4325_D11DUAL_ID: /* 4325 802.11a/g id */
5121 case BCM4325_D11G_ID: /* 4325 802.11g 2.4Ghz band id */
5122 case BCM4325_D11A_ID: /* 4325 802.11a 5Ghz band id */
5123 DHD_INFO(("%s: found 4325 Dongle\n", __func__));
5124 break;
5125 case BCM4329_D11NDUAL_ID: /* 4329 802.11n dualband device */
5126 case BCM4329_D11N2G_ID: /* 4329 802.11n 2.4G device */
5127 case BCM4329_D11N5G_ID: /* 4329 802.11n 5G device */
5128 case 0x4329:
5129 DHD_INFO(("%s: found 4329 Dongle\n", __func__));
5130 break;
5131 case BCM4319_D11N_ID: /* 4319 802.11n id */
5132 case BCM4319_D11N2G_ID: /* 4319 802.11n2g id */
5133 case BCM4319_D11N5G_ID: /* 4319 802.11n5g id */
5134 DHD_INFO(("%s: found 4319 Dongle\n", __func__));
5135 break;
5136 case 0:
5137 DHD_INFO(("%s: allow device id 0, will check chip internals\n",
5138 __func__));
5139 break;
5140
5141 default:
5142 DHD_ERROR(("%s: skipping 0x%04x/0x%04x, not a dongle\n",
5143 __func__, venid, devid));
5144 return NULL;
5145 }
5146
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005147 /* Allocate private bus interface state */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005148 bus = kzalloc(sizeof(dhd_bus_t), GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005149 if (!bus) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005150 DHD_ERROR(("%s: kmalloc of dhd_bus_t failed\n", __func__));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005151 goto fail;
5152 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005153 bus->sdh = sdh;
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005154 bus->cl_devid = (u16) devid;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005155 bus->bus = DHD_BUS;
5156 bus->tx_seq = SDPCM_SEQUENCE_WRAP - 1;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005157 bus->usebufpool = false; /* Use bufpool if allocated,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005158 else use locally malloced rxbuf */
5159
5160 /* attempt to attach to the dongle */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005161 if (!(dhdsdio_probe_attach(bus, sdh, regsva, devid))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005162 DHD_ERROR(("%s: dhdsdio_probe_attach failed\n", __func__));
5163 goto fail;
5164 }
5165
5166 /* Attach to the dhd/OS/network interface */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005167 bus->dhd = dhd_attach(bus, SDPCM_RESERVE);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005168 if (!bus->dhd) {
5169 DHD_ERROR(("%s: dhd_attach failed\n", __func__));
5170 goto fail;
5171 }
5172
5173 /* Allocate buffers */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005174 if (!(dhdsdio_probe_malloc(bus, sdh))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005175 DHD_ERROR(("%s: dhdsdio_probe_malloc failed\n", __func__));
5176 goto fail;
5177 }
5178
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005179 if (!(dhdsdio_probe_init(bus, sdh))) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005180 DHD_ERROR(("%s: dhdsdio_probe_init failed\n", __func__));
5181 goto fail;
5182 }
5183
5184 /* Register interrupt callback, but mask it (not operational yet). */
5185 DHD_INTR(("%s: disable SDIO interrupts (not interested yet)\n",
5186 __func__));
5187 bcmsdh_intr_disable(sdh);
5188 ret = bcmsdh_intr_reg(sdh, dhdsdio_isr, bus);
5189 if (ret != 0) {
5190 DHD_ERROR(("%s: FAILED: bcmsdh_intr_reg returned %d\n",
5191 __func__, ret));
5192 goto fail;
5193 }
5194 DHD_INTR(("%s: registered SDIO interrupt function ok\n", __func__));
5195
5196 DHD_INFO(("%s: completed!!\n", __func__));
5197
5198 /* if firmware path present try to download and bring up bus */
Jason Cooper9b890322010-09-30 15:15:39 -04005199 ret = dhd_bus_start(bus->dhd);
5200 if (ret != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005201 if (ret == BCME_NOTUP) {
5202 DHD_ERROR(("%s: dongle is not responding\n", __func__));
5203 goto fail;
5204 }
5205 }
5206 /* Ok, have the per-port tell the stack we're open for business */
5207 if (dhd_net_attach(bus->dhd, 0) != 0) {
5208 DHD_ERROR(("%s: Net attach failed!!\n", __func__));
5209 goto fail;
5210 }
5211
5212 return bus;
5213
5214fail:
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005215 dhdsdio_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005216 return NULL;
5217}
5218
5219static bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005220dhdsdio_probe_attach(struct dhd_bus *bus, void *sdh, void *regsva, u16 devid)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005221{
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005222 u8 clkctl = 0;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005223 int err = 0;
5224
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005225 bus->alp_only = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005226
5227 /* Return the window to backplane enumeration space for core access */
5228 if (dhdsdio_set_siaddr_window(bus, SI_ENUM_BASE))
5229 DHD_ERROR(("%s: FAILED to return to SI_ENUM_BASE\n", __func__));
5230
5231#ifdef DHD_DEBUG
Arend van Spriel0bef7742011-02-10 12:03:44 +01005232 printk(KERN_DEBUG "F1 signature read @0x18000000=0x%4x\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005233 bcmsdh_reg_read(bus->sdh, SI_ENUM_BASE, 4));
5234
5235#endif /* DHD_DEBUG */
5236
5237 /* Force PLL off until si_attach() programs PLL control regs */
5238
5239 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
5240 DHD_INIT_CLKCTL1, &err);
5241 if (!err)
5242 clkctl =
5243 bcmsdh_cfg_read(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
5244 &err);
5245
5246 if (err || ((clkctl & ~SBSDIO_AVBITS) != DHD_INIT_CLKCTL1)) {
5247 DHD_ERROR(("dhdsdio_probe: ChipClkCSR access: err %d wrote "
5248 "0x%02x read 0x%02x\n",
5249 err, DHD_INIT_CLKCTL1, clkctl));
5250 goto fail;
5251 }
5252#ifdef DHD_DEBUG
5253 if (DHD_INFO_ON()) {
5254 uint fn, numfn;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005255 u8 *cis[SDIOD_MAX_IOFUNCS];
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005256 int err = 0;
5257
5258 numfn = bcmsdh_query_iofnum(sdh);
5259 ASSERT(numfn <= SDIOD_MAX_IOFUNCS);
5260
5261 /* Make sure ALP is available before trying to read CIS */
5262 SPINWAIT(((clkctl = bcmsdh_cfg_read(sdh, SDIO_FUNC_1,
5263 SBSDIO_FUNC1_CHIPCLKCSR,
5264 NULL)),
5265 !SBSDIO_ALPAV(clkctl)), PMU_MAX_TRANSITION_DLY);
5266
5267 /* Now request ALP be put on the bus */
5268 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
5269 DHD_INIT_CLKCTL2, &err);
mike.rapoport@gmail.com73831412010-10-13 00:09:07 +02005270 udelay(65);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005271
5272 for (fn = 0; fn <= numfn; fn++) {
Alexander Beregalov12d0eb42011-03-09 03:53:35 +03005273 cis[fn] = kzalloc(SBSDIO_CIS_SIZE_LIMIT, GFP_ATOMIC);
Jason Cooper9b890322010-09-30 15:15:39 -04005274 if (!cis[fn]) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005275 DHD_INFO(("dhdsdio_probe: fn %d cis malloc "
5276 "failed\n", fn));
5277 break;
5278 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005279
Jason Cooper9b890322010-09-30 15:15:39 -04005280 err = bcmsdh_cis_read(sdh, fn, cis[fn],
5281 SBSDIO_CIS_SIZE_LIMIT);
5282 if (err) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005283 DHD_INFO(("dhdsdio_probe: fn %d cis read "
5284 "err %d\n", fn, err));
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005285 kfree(cis[fn]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005286 break;
5287 }
5288 dhd_dump_cis(fn, cis[fn]);
5289 }
5290
5291 while (fn-- > 0) {
5292 ASSERT(cis[fn]);
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005293 kfree(cis[fn]);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005294 }
5295
5296 if (err) {
5297 DHD_ERROR(("dhdsdio_probe: error read/parsing CIS\n"));
5298 goto fail;
5299 }
5300 }
5301#endif /* DHD_DEBUG */
5302
5303 /* si_attach() will provide an SI handle and scan the backplane */
Arend van Spriel26bcc182011-03-01 10:56:55 +01005304 bus->sih = si_attach((uint) devid, regsva, DHD_BUS, sdh,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005305 &bus->vars, &bus->varsz);
5306 if (!(bus->sih)) {
5307 DHD_ERROR(("%s: si_attach failed!\n", __func__));
5308 goto fail;
5309 }
Franky Lincb63e4c2011-04-25 15:45:08 -07005310 if (dhdsdio_chip_attach(bus, regsva)) {
5311 DHD_ERROR(("%s: dhdsdio_chip_attach failed!\n", __func__));
5312 goto fail;
5313 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005314
5315 bcmsdh_chipinfo(sdh, bus->sih->chip, bus->sih->chiprev);
5316
Greg Kroah-Hartman7d4df482010-10-07 17:04:47 -07005317 if (!dhdsdio_chipmatch((u16) bus->sih->chip)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005318 DHD_ERROR(("%s: unsupported chip: 0x%04x\n",
5319 __func__, bus->sih->chip));
5320 goto fail;
5321 }
5322
Arend van Spriel26bcc182011-03-01 10:56:55 +01005323 si_sdiod_drive_strength_init(bus->sih, dhd_sdiod_drive_strength);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005324
5325 /* Get info on the ARM and SOCRAM cores... */
5326 if (!DHD_NOPMU(bus)) {
5327 if ((si_setcore(bus->sih, ARM7S_CORE_ID, 0)) ||
5328 (si_setcore(bus->sih, ARMCM3_CORE_ID, 0))) {
5329 bus->armrev = si_corerev(bus->sih);
5330 } else {
5331 DHD_ERROR(("%s: failed to find ARM core!\n", __func__));
5332 goto fail;
5333 }
5334 bus->orig_ramsize = si_socram_size(bus->sih);
5335 if (!(bus->orig_ramsize)) {
5336 DHD_ERROR(("%s: failed to find SOCRAM memory!\n",
5337 __func__));
5338 goto fail;
5339 }
5340 bus->ramsize = bus->orig_ramsize;
5341 if (dhd_dongle_memsize)
5342 dhd_dongle_setmemsize(bus, dhd_dongle_memsize);
5343
5344 DHD_ERROR(("DHD: dongle ram size is set to %d(orig %d)\n",
5345 bus->ramsize, bus->orig_ramsize));
5346 }
5347
5348 /* ...but normally deal with the SDPCMDEV core */
Jason Cooper9b890322010-09-30 15:15:39 -04005349 bus->regs = si_setcore(bus->sih, PCMCIA_CORE_ID, 0);
5350 if (!bus->regs) {
5351 bus->regs = si_setcore(bus->sih, SDIOD_CORE_ID, 0);
5352 if (!bus->regs) {
5353 DHD_ERROR(("%s: failed to find SDIODEV core!\n",
5354 __func__));
5355 goto fail;
5356 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005357 }
5358 bus->sdpcmrev = si_corerev(bus->sih);
5359
5360 /* Set core control so an SDIO reset does a backplane reset */
Arend van Sprielff31c542011-03-01 10:56:54 +01005361 OR_REG(&bus->regs->corecontrol, CC_BPRESEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005362
Grant Grundler26a71a42011-03-09 10:41:25 -08005363 pktq_init(&bus->txq, (PRIOMASK + 1), TXQLEN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005364
5365 /* Locate an appropriately-aligned portion of hdrbuf */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005366 bus->rxhdr = (u8 *) roundup((unsigned long)&bus->hdrbuf[0], DHD_SDALIGN);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005367
5368 /* Set the poll and/or interrupt flags */
5369 bus->intr = (bool) dhd_intr;
Jason Cooper9b890322010-09-30 15:15:39 -04005370 bus->poll = (bool) dhd_poll;
5371 if (bus->poll)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005372 bus->pollrate = 1;
5373
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005374 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005375
5376fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005377 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005378}
5379
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005380static bool dhdsdio_probe_malloc(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005381{
5382 DHD_TRACE(("%s: Enter\n", __func__));
5383
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005384 if (bus->dhd->maxctl) {
5385 bus->rxblen =
Greg Kroah-Hartmane18d5312010-10-08 11:59:06 -07005386 roundup((bus->dhd->maxctl + SDPCM_HDRLEN),
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005387 ALIGNMENT) + DHD_SDALIGN;
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005388 bus->rxbuf = kmalloc(bus->rxblen, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005389 if (!(bus->rxbuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005390 DHD_ERROR(("%s: kmalloc of %d-byte rxbuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005391 __func__, bus->rxblen));
5392 goto fail;
5393 }
5394 }
5395
5396 /* Allocate buffer to receive glomed packet */
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005397 bus->databuf = kmalloc(MAX_DATA_BUF, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005398 if (!(bus->databuf)) {
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005399 DHD_ERROR(("%s: kmalloc of %d-byte databuf failed\n",
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005400 __func__, MAX_DATA_BUF));
5401 /* release rxbuf which was already located as above */
5402 if (!bus->rxblen)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005403 kfree(bus->rxbuf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005404 goto fail;
5405 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005406
5407 /* Align the buffer */
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005408 if ((unsigned long)bus->databuf % DHD_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005409 bus->dataptr =
5410 bus->databuf + (DHD_SDALIGN -
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005411 ((unsigned long)bus->databuf % DHD_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005412 else
5413 bus->dataptr = bus->databuf;
5414
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005415 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005416
5417fail:
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005418 return false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005419}
5420
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005421static bool dhdsdio_probe_init(dhd_bus_t *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005422{
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005423 s32 fnum;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005424
5425 DHD_TRACE(("%s: Enter\n", __func__));
5426
5427#ifdef SDTEST
5428 dhdsdio_pktgen_init(bus);
5429#endif /* SDTEST */
5430
5431 /* Disable F2 to clear any intermediate frame state on the dongle */
5432 bcmsdh_cfg_write(sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN, SDIO_FUNC_ENABLE_1,
5433 NULL);
5434
5435 bus->dhd->busstate = DHD_BUS_DOWN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005436 bus->sleeping = false;
5437 bus->rxflow = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005438 bus->prev_rxlim_hit = 0;
5439
5440 /* Done with backplane-dependent accesses, can drop clock... */
5441 bcmsdh_cfg_write(sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0, NULL);
5442
5443 /* ...and initialize clock/power states */
5444 bus->clkstate = CLK_SDONLY;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005445 bus->idletime = (s32) dhd_idletime;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005446 bus->idleclock = DHD_IDLE_ACTIVE;
5447
5448 /* Query the SD clock speed */
5449 if (bcmsdh_iovar_op(sdh, "sd_divisor", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005450 &bus->sd_divisor, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005451 false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005452 DHD_ERROR(("%s: fail on %s get\n", __func__, "sd_divisor"));
5453 bus->sd_divisor = -1;
5454 } else {
5455 DHD_INFO(("%s: Initial value for %s is %d\n",
5456 __func__, "sd_divisor", bus->sd_divisor));
5457 }
5458
5459 /* Query the SD bus mode */
5460 if (bcmsdh_iovar_op(sdh, "sd_mode", NULL, 0,
Roland Vossena1c5ad82011-04-11 15:16:24 +02005461 &bus->sd_mode, sizeof(s32), false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005462 DHD_ERROR(("%s: fail on %s get\n", __func__, "sd_mode"));
5463 bus->sd_mode = -1;
5464 } else {
5465 DHD_INFO(("%s: Initial value for %s is %d\n",
5466 __func__, "sd_mode", bus->sd_mode));
5467 }
5468
5469 /* Query the F2 block size, set roundup accordingly */
5470 fnum = 2;
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005471 if (bcmsdh_iovar_op(sdh, "sd_blocksize", &fnum, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005472 &bus->blocksize, sizeof(s32), false) != 0) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005473 bus->blocksize = 0;
5474 DHD_ERROR(("%s: fail on %s get\n", __func__, "sd_blocksize"));
5475 } else {
5476 DHD_INFO(("%s: Initial value for %s is %d\n",
5477 __func__, "sd_blocksize", bus->blocksize));
5478 }
Greg Kroah-Hartman7068c2f2010-10-08 11:34:59 -07005479 bus->roundup = min(max_roundup, bus->blocksize);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005480
5481 /* Query if bus module supports packet chaining,
5482 default to use if supported */
5483 if (bcmsdh_iovar_op(sdh, "sd_rxchain", NULL, 0,
Greg Kroah-Hartman3e264162010-10-08 11:11:13 -07005484 &bus->sd_rxchain, sizeof(s32),
Roland Vossena1c5ad82011-04-11 15:16:24 +02005485 false) != 0) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005486 bus->sd_rxchain = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005487 } else {
5488 DHD_INFO(("%s: bus module (through bcmsdh API) %s chaining\n",
5489 __func__,
5490 (bus->sd_rxchain ? "supports" : "does not support")));
5491 }
5492 bus->use_rxchain = (bool) bus->sd_rxchain;
5493
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005494 return true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005495}
5496
5497bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005498dhd_bus_download_firmware(struct dhd_bus *bus, char *fw_path, char *nv_path)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005499{
5500 bool ret;
5501 bus->fw_path = fw_path;
5502 bus->nv_path = nv_path;
5503
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005504 ret = dhdsdio_download_firmware(bus, bus->sdh);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005505
5506 return ret;
5507}
5508
5509static bool
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005510dhdsdio_download_firmware(struct dhd_bus *bus, void *sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005511{
5512 bool ret;
5513
5514 /* Download the firmware */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005515 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005516
5517 ret = _dhdsdio_download_firmware(bus) == 0;
5518
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005519 dhdsdio_clkctl(bus, CLK_SDONLY, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005520
5521 return ret;
5522}
5523
5524/* Detach and free everything */
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005525static void dhdsdio_release(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005526{
5527 DHD_TRACE(("%s: Enter\n", __func__));
5528
5529 if (bus) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005530 /* De-register interrupt handler */
5531 bcmsdh_intr_disable(bus->sdh);
5532 bcmsdh_intr_dereg(bus->sdh);
5533
5534 if (bus->dhd) {
5535
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005536 dhdsdio_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005537
5538 dhd_detach(bus->dhd);
5539 bus->dhd = NULL;
5540 }
5541
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005542 dhdsdio_release_malloc(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005543
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005544 kfree(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005545 }
5546
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005547 DHD_TRACE(("%s: Disconnected\n", __func__));
5548}
5549
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005550static void dhdsdio_release_malloc(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005551{
5552 DHD_TRACE(("%s: Enter\n", __func__));
5553
5554 if (bus->dhd && bus->dhd->dongle_reset)
5555 return;
5556
5557 if (bus->rxbuf) {
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005558 kfree(bus->rxbuf);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005559 bus->rxctl = bus->rxbuf = NULL;
5560 bus->rxlen = 0;
5561 }
5562
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005563 kfree(bus->databuf);
5564 bus->databuf = NULL;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005565}
5566
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01005567static void dhdsdio_release_dongle(dhd_bus_t *bus)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005568{
5569 DHD_TRACE(("%s: Enter\n", __func__));
5570
5571 if (bus->dhd && bus->dhd->dongle_reset)
5572 return;
5573
5574 if (bus->sih) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005575 dhdsdio_clkctl(bus, CLK_AVAIL, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005576#if !defined(BCMLXSDMMC)
5577 si_watchdog(bus->sih, 4);
5578#endif /* !defined(BCMLXSDMMC) */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005579 dhdsdio_clkctl(bus, CLK_NONE, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005580 si_detach(bus->sih);
5581 if (bus->vars && bus->varsz)
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005582 kfree(bus->vars);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005583 bus->vars = NULL;
5584 }
5585
5586 DHD_TRACE(("%s: Disconnected\n", __func__));
5587}
5588
5589static void dhdsdio_disconnect(void *ptr)
5590{
5591 dhd_bus_t *bus = (dhd_bus_t *)ptr;
5592
5593 DHD_TRACE(("%s: Enter\n", __func__));
5594
5595 if (bus) {
5596 ASSERT(bus->dhd);
Arend van Spriel3c9d4c32011-03-02 21:18:48 +01005597 dhdsdio_release(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005598 }
5599
5600 DHD_TRACE(("%s: Disconnected\n", __func__));
5601}
5602
5603/* Register/Unregister functions are called by the main DHD entry
5604 * point (e.g. module insertion) to link with the bus driver, in
5605 * order to look for or await the device.
5606 */
5607
5608static bcmsdh_driver_t dhd_sdio = {
5609 dhdsdio_probe,
5610 dhdsdio_disconnect
5611};
5612
5613int dhd_bus_register(void)
5614{
5615 DHD_TRACE(("%s: Enter\n", __func__));
5616
5617 return bcmsdh_register(&dhd_sdio);
5618}
5619
5620void dhd_bus_unregister(void)
5621{
5622 DHD_TRACE(("%s: Enter\n", __func__));
5623
5624 bcmsdh_unregister();
5625}
5626
5627#ifdef BCMEMBEDIMAGE
5628static int dhdsdio_download_code_array(struct dhd_bus *bus)
5629{
5630 int bcmerror = -1;
5631 int offset = 0;
5632
5633 DHD_INFO(("%s: download embedded firmware...\n", __func__));
5634
5635 /* Download image */
5636 while ((offset + MEMBLOCK) < sizeof(dlarray)) {
5637 bcmerror =
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005638 dhdsdio_membytes(bus, true, offset, dlarray + offset,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005639 MEMBLOCK);
5640 if (bcmerror) {
5641 DHD_ERROR(("%s: error %d on writing %d membytes at "
5642 "0x%08x\n",
5643 __func__, bcmerror, MEMBLOCK, offset));
5644 goto err;
5645 }
5646
5647 offset += MEMBLOCK;
5648 }
5649
5650 if (offset < sizeof(dlarray)) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005651 bcmerror = dhdsdio_membytes(bus, true, offset,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005652 dlarray + offset,
5653 sizeof(dlarray) - offset);
5654 if (bcmerror) {
5655 DHD_ERROR(("%s: error %d on writing %d membytes at "
5656 "0x%08x\n", __func__, bcmerror,
5657 sizeof(dlarray) - offset, offset));
5658 goto err;
5659 }
5660 }
5661#ifdef DHD_DEBUG
5662 /* Upload and compare the downloaded code */
5663 {
5664 unsigned char *ularray;
5665
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005666 ularray = kmalloc(bus->ramsize, GFP_ATOMIC);
Alexander Beregalov570edd32011-03-13 21:58:47 +03005667 if (!ularray) {
5668 bcmerror = BCME_NOMEM;
5669 goto err;
5670 }
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005671 /* Upload image to verify downloaded contents. */
5672 offset = 0;
5673 memset(ularray, 0xaa, bus->ramsize);
5674 while ((offset + MEMBLOCK) < sizeof(dlarray)) {
5675 bcmerror =
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005676 dhdsdio_membytes(bus, false, offset,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005677 ularray + offset, MEMBLOCK);
5678 if (bcmerror) {
5679 DHD_ERROR(("%s: error %d on reading %d membytes"
5680 " at 0x%08x\n",
5681 __func__, bcmerror, MEMBLOCK, offset));
Alexander Beregalov570edd32011-03-13 21:58:47 +03005682 goto free;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005683 }
5684
5685 offset += MEMBLOCK;
5686 }
5687
5688 if (offset < sizeof(dlarray)) {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005689 bcmerror = dhdsdio_membytes(bus, false, offset,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005690 ularray + offset,
5691 sizeof(dlarray) - offset);
5692 if (bcmerror) {
5693 DHD_ERROR(("%s: error %d on reading %d membytes at 0x%08x\n",
5694 __func__, bcmerror,
5695 sizeof(dlarray) - offset, offset));
Alexander Beregalov570edd32011-03-13 21:58:47 +03005696 goto free;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005697 }
5698 }
5699
5700 if (memcmp(dlarray, ularray, sizeof(dlarray))) {
5701 DHD_ERROR(("%s: Downloaded image is corrupted.\n",
5702 __func__));
5703 ASSERT(0);
Alexander Beregalov570edd32011-03-13 21:58:47 +03005704 goto free;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005705 } else
5706 DHD_ERROR(("%s: Download/Upload/Compare succeeded.\n",
5707 __func__));
Alexander Beregalov570edd32011-03-13 21:58:47 +03005708free:
mike.rapoport@gmail.com182acb32010-10-13 00:09:12 +02005709 kfree(ularray);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005710 }
5711#endif /* DHD_DEBUG */
5712
5713err:
5714 return bcmerror;
5715}
5716#endif /* BCMEMBEDIMAGE */
5717
5718static int dhdsdio_download_code_file(struct dhd_bus *bus, char *fw_path)
5719{
5720 int bcmerror = -1;
5721 int offset = 0;
5722 uint len;
5723 void *image = NULL;
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07005724 u8 *memblock = NULL, *memptr;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005725
5726 DHD_INFO(("%s: download firmware %s\n", __func__, fw_path));
5727
5728 image = dhd_os_open_image(fw_path);
5729 if (image == NULL)
5730 goto err;
5731
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005732 memptr = memblock = kmalloc(MEMBLOCK + DHD_SDALIGN, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005733 if (memblock == NULL) {
5734 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5735 __func__, MEMBLOCK));
5736 goto err;
5737 }
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005738 if ((u32)(unsigned long)memblock % DHD_SDALIGN)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005739 memptr +=
Greg Kroah-Hartmanf024c482010-10-21 10:50:21 -07005740 (DHD_SDALIGN - ((u32)(unsigned long)memblock % DHD_SDALIGN));
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005741
5742 /* Download image */
5743 while ((len =
5744 dhd_os_get_image_block((char *)memptr, MEMBLOCK, image))) {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005745 bcmerror = dhdsdio_membytes(bus, true, offset, memptr, len);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005746 if (bcmerror) {
5747 DHD_ERROR(("%s: error %d on writing %d membytes at "
5748 "0x%08x\n", __func__, bcmerror, MEMBLOCK, offset));
5749 goto err;
5750 }
5751
5752 offset += MEMBLOCK;
5753 }
5754
5755err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005756 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005757
5758 if (image)
5759 dhd_os_close_image(image);
5760
5761 return bcmerror;
5762}
5763
5764/*
5765 * ProcessVars:Takes a buffer of "<var>=<value>\n" lines read from a file
5766 * and ending in a NUL.
5767 * Removes carriage returns, empty lines, comment lines, and converts
5768 * newlines to NULs.
5769 * Shortens buffer as needed and pads with NULs. End of buffer is marked
5770 * by two NULs.
5771*/
5772
5773static uint process_nvram_vars(char *varbuf, uint len)
5774{
5775 char *dp;
5776 bool findNewline;
5777 int column;
5778 uint buf_len, n;
5779
5780 dp = varbuf;
5781
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005782 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005783 column = 0;
5784
5785 for (n = 0; n < len; n++) {
5786 if (varbuf[n] == 0)
5787 break;
5788 if (varbuf[n] == '\r')
5789 continue;
5790 if (findNewline && varbuf[n] != '\n')
5791 continue;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005792 findNewline = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005793 if (varbuf[n] == '#') {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005794 findNewline = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005795 continue;
5796 }
5797 if (varbuf[n] == '\n') {
5798 if (column == 0)
5799 continue;
5800 *dp++ = 0;
5801 column = 0;
5802 continue;
5803 }
5804 *dp++ = varbuf[n];
5805 column++;
5806 }
5807 buf_len = dp - varbuf;
5808
5809 while (dp < varbuf + n)
5810 *dp++ = 0;
5811
5812 return buf_len;
5813}
5814
5815/*
5816 EXAMPLE: nvram_array
5817 nvram_arry format:
5818 name=value
5819 Use carriage return at the end of each assignment,
5820 and an empty string with
5821 carriage return at the end of array.
5822
5823 For example:
5824 unsigned char nvram_array[] = {"name1=value1\n",
5825 "name2=value2\n", "\n"};
5826 Hex values start with 0x, and mac addr format: xx:xx:xx:xx:xx:xx.
5827
5828 Search "EXAMPLE: nvram_array" to see how the array is activated.
5829*/
5830
5831void dhd_bus_set_nvram_params(struct dhd_bus *bus, const char *nvram_params)
5832{
5833 bus->nvram_params = nvram_params;
5834}
5835
5836static int dhdsdio_download_nvram(struct dhd_bus *bus)
5837{
5838 int bcmerror = -1;
5839 uint len;
5840 void *image = NULL;
5841 char *memblock = NULL;
5842 char *bufp;
5843 char *nv_path;
5844 bool nvram_file_exists;
5845
5846 nv_path = bus->nv_path;
5847
5848 nvram_file_exists = ((nv_path != NULL) && (nv_path[0] != '\0'));
5849 if (!nvram_file_exists && (bus->nvram_params == NULL))
5850 return 0;
5851
5852 if (nvram_file_exists) {
5853 image = dhd_os_open_image(nv_path);
5854 if (image == NULL)
5855 goto err;
5856 }
5857
mike.rapoport@gmail.com5fcc1fc2010-10-13 00:09:10 +02005858 memblock = kmalloc(MEMBLOCK, GFP_ATOMIC);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005859 if (memblock == NULL) {
5860 DHD_ERROR(("%s: Failed to allocate memory %d bytes\n",
5861 __func__, MEMBLOCK));
5862 goto err;
5863 }
5864
5865 /* Download variables */
5866 if (nvram_file_exists) {
5867 len = dhd_os_get_image_block(memblock, MEMBLOCK, image);
5868 } else {
5869 len = strlen(bus->nvram_params);
5870 ASSERT(len <= MEMBLOCK);
5871 if (len > MEMBLOCK)
5872 len = MEMBLOCK;
5873 memcpy(memblock, bus->nvram_params, len);
5874 }
5875
5876 if (len > 0 && len < MEMBLOCK) {
5877 bufp = (char *)memblock;
5878 bufp[len] = 0;
5879 len = process_nvram_vars(bufp, len);
5880 bufp += len;
5881 *bufp++ = 0;
5882 if (len)
5883 bcmerror = dhdsdio_downloadvars(bus, memblock, len + 1);
5884 if (bcmerror) {
5885 DHD_ERROR(("%s: error downloading vars: %d\n",
5886 __func__, bcmerror));
5887 }
5888 } else {
5889 DHD_ERROR(("%s: error reading nvram file: %d\n",
5890 __func__, len));
5891 bcmerror = BCME_SDIO_ERROR;
5892 }
5893
5894err:
Ilia Mirkin46d994b2011-03-13 00:28:56 -05005895 kfree(memblock);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005896
5897 if (image)
5898 dhd_os_close_image(image);
5899
5900 return bcmerror;
5901}
5902
5903static int _dhdsdio_download_firmware(struct dhd_bus *bus)
5904{
5905 int bcmerror = -1;
5906
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005907 bool embed = false; /* download embedded firmware */
5908 bool dlok = false; /* download firmware succeeded */
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005909
5910 /* Out immediately if no image to download */
5911 if ((bus->fw_path == NULL) || (bus->fw_path[0] == '\0')) {
5912#ifdef BCMEMBEDIMAGE
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005913 embed = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005914#else
5915 return bcmerror;
5916#endif
5917 }
5918
5919 /* Keep arm in reset */
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005920 if (dhdsdio_download_state(bus, true)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005921 DHD_ERROR(("%s: error placing ARM core in reset\n", __func__));
5922 goto err;
5923 }
5924
5925 /* External image takes precedence if specified */
5926 if ((bus->fw_path != NULL) && (bus->fw_path[0] != '\0')) {
5927 if (dhdsdio_download_code_file(bus, bus->fw_path)) {
5928 DHD_ERROR(("%s: dongle image file download failed\n",
5929 __func__));
5930#ifdef BCMEMBEDIMAGE
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005931 embed = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005932#else
5933 goto err;
5934#endif
5935 } else {
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005936 embed = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005937 dlok = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005938 }
5939 }
5940#ifdef BCMEMBEDIMAGE
5941 if (embed) {
5942 if (dhdsdio_download_code_array(bus)) {
5943 DHD_ERROR(("%s: dongle image array download failed\n",
5944 __func__));
5945 goto err;
5946 } else {
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07005947 dlok = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005948 }
5949 }
5950#endif
5951 if (!dlok) {
5952 DHD_ERROR(("%s: dongle image download failed\n", __func__));
5953 goto err;
5954 }
5955
5956 /* EXAMPLE: nvram_array */
5957 /* If a valid nvram_arry is specified as above, it can be passed
5958 down to dongle */
5959 /* dhd_bus_set_nvram_params(bus, (char *)&nvram_array); */
5960
5961 /* External nvram takes precedence if specified */
5962 if (dhdsdio_download_nvram(bus)) {
5963 DHD_ERROR(("%s: dongle nvram file download failed\n",
5964 __func__));
5965 }
5966
5967 /* Take arm out of reset */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07005968 if (dhdsdio_download_state(bus, false)) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005969 DHD_ERROR(("%s: error getting out of ARM core reset\n",
5970 __func__));
5971 goto err;
5972 }
5973
5974 bcmerror = 0;
5975
5976err:
5977 return bcmerror;
5978}
5979
5980static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005981dhd_bcmsdh_recv_buf(dhd_bus_t *bus, u32 addr, uint fn, uint flags,
Arend van Sprielc26b1372010-11-23 14:06:23 +01005982 u8 *buf, uint nbytes, struct sk_buff *pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005983 bcmsdh_cmplt_fn_t complete, void *handle)
5984{
5985 int status;
5986
5987 /* 4329: GSPI check */
5988 status =
5989 bcmsdh_recv_buf(bus->sdh, addr, fn, flags, buf, nbytes, pkt,
5990 complete, handle);
5991 return status;
5992}
5993
5994static int
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07005995dhd_bcmsdh_send_buf(dhd_bus_t *bus, u32 addr, uint fn, uint flags,
Arend van Sprielc26b1372010-11-23 14:06:23 +01005996 u8 *buf, uint nbytes, struct sk_buff *pkt,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07005997 bcmsdh_cmplt_fn_t complete, void *handle)
5998{
5999 return bcmsdh_send_buf
6000 (bus->sdh, addr, fn, flags, buf, nbytes, pkt, complete,
6001 handle);
6002}
6003
6004uint dhd_bus_chip(struct dhd_bus *bus)
6005{
6006 ASSERT(bus->sih != NULL);
6007 return bus->sih->chip;
6008}
6009
6010void *dhd_bus_pub(struct dhd_bus *bus)
6011{
6012 return bus->dhd;
6013}
6014
6015void *dhd_bus_txq(struct dhd_bus *bus)
6016{
6017 return &bus->txq;
6018}
6019
6020uint dhd_bus_hdrlen(struct dhd_bus *bus)
6021{
6022 return SDPCM_HDRLEN;
6023}
6024
Greg Kroah-Hartman3fd79f72010-10-05 10:11:12 -07006025int dhd_bus_devreset(dhd_pub_t *dhdp, u8 flag)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006026{
6027 int bcmerror = 0;
6028 dhd_bus_t *bus;
6029
6030 bus = dhdp->bus;
6031
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006032 if (flag == true) {
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006033 if (!bus->dhd->dongle_reset) {
6034 /* Expect app to have torn down any
6035 connection before calling */
6036 /* Stop the bus, disable F2 */
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006037 dhd_bus_stop(bus, false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006038
6039 /* Clean tx/rx buffer pointers,
6040 detach from the dongle */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01006041 dhdsdio_release_dongle(bus);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006042
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006043 bus->dhd->dongle_reset = true;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006044 bus->dhd->up = false;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006045
6046 DHD_TRACE(("%s: WLAN OFF DONE\n", __func__));
6047 /* App can now remove power from device */
6048 } else
6049 bcmerror = BCME_SDIO_ERROR;
6050 } else {
6051 /* App must have restored power to device before calling */
6052
6053 DHD_TRACE(("\n\n%s: == WLAN ON ==\n", __func__));
6054
6055 if (bus->dhd->dongle_reset) {
6056 /* Turn on WLAN */
6057 /* Reset SD client */
6058 bcmsdh_reset(bus->sdh);
6059
6060 /* Attempt to re-attach & download */
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01006061 if (dhdsdio_probe_attach(bus, bus->sdh,
Greg Kroah-Hartman66cbd3a2010-10-08 11:05:47 -07006062 (u32 *) SI_ENUM_BASE,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006063 bus->cl_devid)) {
6064 /* Attempt to download binary to the dongle */
6065 if (dhdsdio_probe_init
Arend van Spriel8da4a3a2011-03-02 21:18:42 +01006066 (bus, bus->sdh)
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006067 && dhdsdio_download_firmware(bus,
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006068 bus->sdh)) {
6069
6070 /* Re-init bus, enable F2 transfer */
6071 dhd_bus_init((dhd_pub_t *) bus->dhd,
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006072 false);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006073
6074#if defined(OOB_INTR_ONLY)
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006075 dhd_enable_oob_intr(bus, true);
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006076#endif /* defined(OOB_INTR_ONLY) */
6077
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006078 bus->dhd->dongle_reset = false;
Greg Kroah-Hartman0f0881b2010-10-12 12:15:18 -07006079 bus->dhd->up = true;
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006080
6081 DHD_TRACE(("%s: WLAN ON DONE\n",
6082 __func__));
6083 } else
6084 bcmerror = BCME_SDIO_ERROR;
6085 } else
6086 bcmerror = BCME_SDIO_ERROR;
6087 } else {
6088 bcmerror = BCME_NOTDOWN;
Greg Kroah-Hartman0965ae82010-10-12 12:50:15 -07006089 DHD_ERROR(("%s: Set DEVRESET=false invoked when device "
Henry Ptasinskicf2b4482010-09-20 22:33:12 -07006090 "is on\n", __func__));
6091 bcmerror = BCME_SDIO_ERROR;
6092 }
6093 }
6094 return bcmerror;
6095}
Franky Lincb63e4c2011-04-25 15:45:08 -07006096
6097static int
6098dhdsdio_chip_recognition(bcmsdh_info_t *sdh, struct chip_info *ci, void *regs)
6099{
6100 u32 regdata;
6101
6102 /*
6103 * Get CC core rev
6104 * Chipid is assume to be at offset 0 from regs arg
6105 * For different chiptypes or old sdio hosts w/o chipcommon,
6106 * other ways of recognition should be added here.
6107 */
6108 ci->cccorebase = (u32)regs;
6109 regdata = bcmsdh_reg_read(sdh, CORE_CC_REG(ci->cccorebase, chipid), 4);
6110 ci->chip = regdata & CID_ID_MASK;
6111 ci->chiprev = (regdata & CID_REV_MASK) >> CID_REV_SHIFT;
6112
6113 DHD_INFO(("%s: chipid=0x%x chiprev=%d\n",
6114 __func__, ci->chip, ci->chiprev));
6115
6116 /* Address of cores for new chips should be added here */
6117 switch (ci->chip) {
6118 case BCM4329_CHIP_ID:
6119 ci->buscorebase = BCM4329_CORE_BUS_BASE;
6120 ci->ramcorebase = BCM4329_CORE_SOCRAM_BASE;
6121 ci->armcorebase = BCM4329_CORE_ARM_BASE;
6122 break;
6123 default:
6124 DHD_ERROR(("%s: chipid 0x%x is not supported\n",
6125 __func__, ci->chip));
6126 return -ENODEV;
6127 }
6128
6129 regdata = bcmsdh_reg_read(sdh,
6130 CORE_SB(ci->cccorebase, sbidhigh), 4);
6131 ci->ccrev = SBCOREREV(regdata);
6132
6133 regdata = bcmsdh_reg_read(sdh,
6134 CORE_CC_REG(ci->cccorebase, pmucapabilities), 4);
6135 ci->pmurev = regdata & PCAP_REV_MASK;
6136
6137 regdata = bcmsdh_reg_read(sdh, CORE_SB(ci->buscorebase, sbidhigh), 4);
6138 ci->buscorerev = SBCOREREV(regdata);
6139 ci->buscoretype = (regdata & SBIDH_CC_MASK) >> SBIDH_CC_SHIFT;
6140
6141 DHD_INFO(("%s: ccrev=%d, pmurev=%d, buscore rev/type=%d/0x%x\n",
6142 __func__, ci->ccrev, ci->pmurev,
6143 ci->buscorerev, ci->buscoretype));
6144
6145 /* get chipcommon capabilites */
6146 ci->cccaps = bcmsdh_reg_read(sdh,
6147 CORE_CC_REG(ci->cccorebase, capabilities), 4);
6148
6149 return 0;
6150}
6151
6152static void
6153dhdsdio_chip_disablecore(bcmsdh_info_t *sdh, u32 corebase)
6154{
6155 u32 regdata;
6156
6157 regdata = bcmsdh_reg_read(sdh,
6158 CORE_SB(corebase, sbtmstatelow), 4);
6159 if (regdata & SBTML_RESET)
6160 return;
6161
6162 regdata = bcmsdh_reg_read(sdh,
6163 CORE_SB(corebase, sbtmstatelow), 4);
6164 if ((regdata & (SICF_CLOCK_EN << SBTML_SICF_SHIFT)) != 0) {
6165 /*
6166 * set target reject and spin until busy is clear
6167 * (preserve core-specific bits)
6168 */
6169 regdata = bcmsdh_reg_read(sdh,
6170 CORE_SB(corebase, sbtmstatelow), 4);
6171 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6172 regdata | SBTML_REJ);
6173
6174 regdata = bcmsdh_reg_read(sdh,
6175 CORE_SB(corebase, sbtmstatelow), 4);
6176 udelay(1);
6177 SPINWAIT((bcmsdh_reg_read(sdh,
6178 CORE_SB(corebase, sbtmstatehigh), 4) &
6179 SBTMH_BUSY), 100000);
6180
6181 regdata = bcmsdh_reg_read(sdh,
6182 CORE_SB(corebase, sbtmstatehigh), 4);
6183 if (regdata & SBTMH_BUSY)
6184 DHD_ERROR(("%s: ARM core still busy\n", __func__));
6185
6186 regdata = bcmsdh_reg_read(sdh,
6187 CORE_SB(corebase, sbidlow), 4);
6188 if (regdata & SBIDL_INIT) {
6189 regdata = bcmsdh_reg_read(sdh,
6190 CORE_SB(corebase, sbimstate), 4) |
6191 SBIM_RJ;
6192 bcmsdh_reg_write(sdh,
6193 CORE_SB(corebase, sbimstate), 4,
6194 regdata);
6195 regdata = bcmsdh_reg_read(sdh,
6196 CORE_SB(corebase, sbimstate), 4);
6197 udelay(1);
6198 SPINWAIT((bcmsdh_reg_read(sdh,
6199 CORE_SB(corebase, sbimstate), 4) &
6200 SBIM_BY), 100000);
6201 }
6202
6203 /* set reset and reject while enabling the clocks */
6204 bcmsdh_reg_write(sdh,
6205 CORE_SB(corebase, sbtmstatelow), 4,
6206 (((SICF_FGC | SICF_CLOCK_EN) << SBTML_SICF_SHIFT) |
6207 SBTML_REJ | SBTML_RESET));
6208 regdata = bcmsdh_reg_read(sdh,
6209 CORE_SB(corebase, sbtmstatelow), 4);
6210 udelay(10);
6211
6212 /* clear the initiator reject bit */
6213 regdata = bcmsdh_reg_read(sdh,
6214 CORE_SB(corebase, sbidlow), 4);
6215 if (regdata & SBIDL_INIT) {
6216 regdata = bcmsdh_reg_read(sdh,
6217 CORE_SB(corebase, sbimstate), 4) &
6218 ~SBIM_RJ;
6219 bcmsdh_reg_write(sdh,
6220 CORE_SB(corebase, sbimstate), 4,
6221 regdata);
6222 }
6223 }
6224
6225 /* leave reset and reject asserted */
6226 bcmsdh_reg_write(sdh, CORE_SB(corebase, sbtmstatelow), 4,
6227 (SBTML_REJ | SBTML_RESET));
6228 udelay(1);
6229}
6230
6231static int
6232dhdsdio_chip_attach(struct dhd_bus *bus, void *regs)
6233{
6234 struct chip_info *ci;
6235 int err;
6236 u8 clkval, clkset;
6237
6238 DHD_TRACE(("%s: Enter\n", __func__));
6239
6240 /* alloc chip_info_t */
6241 ci = kmalloc(sizeof(struct chip_info), GFP_ATOMIC);
6242 if (NULL == ci) {
6243 DHD_ERROR(("%s: malloc failed!\n", __func__));
6244 return -ENOMEM;
6245 }
6246
6247 memset((unsigned char *)ci, 0, sizeof(struct chip_info));
6248
6249 /* bus/core/clk setup for register access */
6250 /* Try forcing SDIO core to do ALPAvail request only */
6251 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF | SBSDIO_ALP_AVAIL_REQ;
6252 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR,
6253 clkset, &err);
6254 if (err) {
6255 DHD_ERROR(("%s: error writing for HT off\n", __func__));
6256 goto fail;
6257 }
6258
6259 /* If register supported, wait for ALPAvail and then force ALP */
6260 /* This may take up to 15 milliseconds */
6261 clkval = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6262 SBSDIO_FUNC1_CHIPCLKCSR, NULL);
6263 if ((clkval & ~SBSDIO_AVBITS) == clkset) {
6264 SPINWAIT(((clkval =
6265 bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6266 SBSDIO_FUNC1_CHIPCLKCSR,
6267 NULL)),
6268 !SBSDIO_ALPAV(clkval)),
6269 PMU_MAX_TRANSITION_DLY);
6270 if (!SBSDIO_ALPAV(clkval)) {
6271 DHD_ERROR(("%s: timeout on ALPAV wait, clkval 0x%02x\n",
6272 __func__, clkval));
6273 err = -EBUSY;
6274 goto fail;
6275 }
6276 clkset = SBSDIO_FORCE_HW_CLKREQ_OFF |
6277 SBSDIO_FORCE_ALP;
6278 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1,
6279 SBSDIO_FUNC1_CHIPCLKCSR,
6280 clkset, &err);
6281 udelay(65);
6282 } else {
6283 DHD_ERROR(("%s: ChipClkCSR access: wrote 0x%02x read 0x%02x\n",
6284 __func__, clkset, clkval));
6285 err = -EACCES;
6286 goto fail;
6287 }
6288
6289 /* Also, disable the extra SDIO pull-ups */
6290 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_SDIOPULLUP, 0,
6291 NULL);
6292
6293 err = dhdsdio_chip_recognition(bus->sdh, ci, regs);
6294 if (err)
6295 goto fail;
6296
6297 /*
6298 * Make sure any on-chip ARM is off (in case strapping is wrong),
6299 * or downloaded code was already running.
6300 */
6301 dhdsdio_chip_disablecore(bus->sdh, ci->armcorebase);
6302
6303 bcmsdh_reg_write(bus->sdh,
6304 CORE_CC_REG(ci->cccorebase, gpiopullup), 4, 0);
6305 bcmsdh_reg_write(bus->sdh,
6306 CORE_CC_REG(ci->cccorebase, gpiopulldown), 4, 0);
6307
6308 /* Disable F2 to clear any intermediate frame state on the dongle */
6309 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_0, SDIOD_CCCR_IOEN,
6310 SDIO_FUNC_ENABLE_1, NULL);
6311
6312 /* WAR: cmd52 backplane read so core HW will drop ALPReq */
6313 clkval = bcmsdh_cfg_read(bus->sdh, SDIO_FUNC_1,
6314 0, NULL);
6315
6316 /* Done with backplane-dependent accesses, can drop clock... */
6317 bcmsdh_cfg_write(bus->sdh, SDIO_FUNC_1, SBSDIO_FUNC1_CHIPCLKCSR, 0,
6318 NULL);
6319
6320 bus->ci = ci;
6321 return 0;
6322fail:
6323 bus->ci = NULL;
6324 kfree(ci);
6325 return err;
6326}