aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/net/wireless/bcm4329/bcmpcispi.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/net/wireless/bcm4329/bcmpcispi.c')
-rw-r--r--drivers/net/wireless/bcm4329/bcmpcispi.c630
1 files changed, 630 insertions, 0 deletions
diff --git a/drivers/net/wireless/bcm4329/bcmpcispi.c b/drivers/net/wireless/bcm4329/bcmpcispi.c
new file mode 100644
index 00000000000..1a8b6717f92
--- /dev/null
+++ b/drivers/net/wireless/bcm4329/bcmpcispi.c
@@ -0,0 +1,630 @@
1/*
2 * Broadcom SPI over PCI-SPI Host Controller, low-level hardware driver
3 *
4 * Copyright (C) 1999-2010, Broadcom Corporation
5 *
6 * Unless you and Broadcom execute a separate written software license
7 * agreement governing use of this software, this software is licensed to you
8 * under the terms of the GNU General Public License version 2 (the "GPL"),
9 * available at http://www.broadcom.com/licenses/GPLv2.php, with the
10 * following added to such license:
11 *
12 * As a special exception, the copyright holders of this software give you
13 * permission to link this software with independent modules, and to copy and
14 * distribute the resulting executable under terms of your choice, provided that
15 * you also meet, for each linked independent module, the terms and conditions of
16 * the license of that module. An independent module is a module which is not
17 * derived from this software. The special exception does not apply to any
18 * modifications of the software.
19 *
20 * Notwithstanding the above, under no circumstances may you combine this
21 * software in any way with any other Broadcom software provided under a license
22 * other than the GPL, without Broadcom's express prior written consent.
23 *
24 * $Id: bcmpcispi.c,v 1.22.2.4.4.5.6.1 2010/08/13 00:26:05 Exp $
25 */
26
27#include <typedefs.h>
28#include <bcmutils.h>
29
30#include <sdio.h> /* SDIO Specs */
31#include <bcmsdbus.h> /* bcmsdh to/from specific controller APIs */
32#include <sdiovar.h> /* to get msglevel bit values */
33
34#include <pcicfg.h>
35#include <bcmsdspi.h>
36#include <bcmspi.h>
37#include <bcmpcispi.h> /* BRCM PCI-SPI Host Controller Register definitions */
38
39
40/* ndis_osl.h needs to do a runtime check of the osh to map
41 * R_REG/W_REG to bus specific access similar to linux_osl.h.
42 * Until then...
43 */
44/* linux */
45
46#define SPIPCI_RREG R_REG
47#define SPIPCI_WREG W_REG
48
49
50#define SPIPCI_ANDREG(osh, r, v) SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) & (v)))
51#define SPIPCI_ORREG(osh, r, v) SPIPCI_WREG(osh, (r), (SPIPCI_RREG(osh, r) | (v)))
52
53
54int bcmpcispi_dump = 0; /* Set to dump complete trace of all SPI bus transactions */
55
56typedef struct spih_info_ {
57 uint bar0; /* BAR0 of PCI Card */
58 uint bar1; /* BAR1 of PCI Card */
59 osl_t *osh; /* osh handle */
60 spih_pciregs_t *pciregs; /* PCI Core Registers */
61 spih_regs_t *regs; /* SPI Controller Registers */
62 uint8 rev; /* PCI Card Revision ID */
63} spih_info_t;
64
65
66/* Attach to PCI-SPI Host Controller Hardware */
67bool
68spi_hw_attach(sdioh_info_t *sd)
69{
70 osl_t *osh;
71 spih_info_t *si;
72
73 sd_trace(("%s: enter\n", __FUNCTION__));
74
75 osh = sd->osh;
76
77 if ((si = (spih_info_t *)MALLOC(osh, sizeof(spih_info_t))) == NULL) {
78 sd_err(("%s: out of memory, malloced %d bytes\n", __FUNCTION__, MALLOCED(osh)));
79 return FALSE;
80 }
81
82 bzero(si, sizeof(spih_info_t));
83
84 sd->controller = si;
85
86 si->osh = sd->osh;
87 si->rev = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_REV, 4) & 0xFF;
88
89 if (si->rev < 3) {
90 sd_err(("Host controller %d not supported, please upgrade to rev >= 3\n", si->rev));
91 MFREE(osh, si, sizeof(spih_info_t));
92 return (FALSE);
93 }
94
95 sd_err(("Attaching to Generic PCI SPI Host Controller Rev %d\n", si->rev));
96
97 /* FPGA Revision < 3 not supported by driver anymore. */
98 ASSERT(si->rev >= 3);
99
100 si->bar0 = sd->bar0;
101
102 /* Rev < 10 PciSpiHost has 2 BARs:
103 * BAR0 = PCI Core Registers
104 * BAR1 = PciSpiHost Registers (all other cores on backplane)
105 *
106 * Rev 10 and up use a different PCI core which only has a single
107 * BAR0 which contains the PciSpiHost Registers.
108 */
109 if (si->rev < 10) {
110 si->pciregs = (spih_pciregs_t *)spi_reg_map(osh,
111 (uintptr)si->bar0,
112 sizeof(spih_pciregs_t));
113 sd_err(("Mapped PCI Core regs to BAR0 at %p\n", si->pciregs));
114
115 si->bar1 = OSL_PCI_READ_CONFIG(sd->osh, PCI_CFG_BAR1, 4);
116 si->regs = (spih_regs_t *)spi_reg_map(osh,
117 (uintptr)si->bar1,
118 sizeof(spih_regs_t));
119 sd_err(("Mapped SPI Controller regs to BAR1 at %p\n", si->regs));
120 } else {
121 si->regs = (spih_regs_t *)spi_reg_map(osh,
122 (uintptr)si->bar0,
123 sizeof(spih_regs_t));
124 sd_err(("Mapped SPI Controller regs to BAR0 at %p\n", si->regs));
125 si->pciregs = NULL;
126 }
127 /* Enable SPI Controller, 16.67MHz SPI Clock */
128 SPIPCI_WREG(osh, &si->regs->spih_ctrl, 0x000000d1);
129
130 /* Set extended feature register to defaults */
131 SPIPCI_WREG(osh, &si->regs->spih_ext, 0x00000000);
132
133 /* Set GPIO CS# High (de-asserted) */
134 SPIPCI_WREG(osh, &si->regs->spih_gpio_data, SPIH_CS);
135
136 /* set GPIO[0] to output for CS# */
137 /* set GPIO[1] to output for power control */
138 /* set GPIO[2] to input for card detect */
139 SPIPCI_WREG(osh, &si->regs->spih_gpio_ctrl, (SPIH_CS | SPIH_SLOT_POWER));
140
141 /* Clear out the Read FIFO in case there is any stuff left in there from a previous run. */
142 while ((SPIPCI_RREG(osh, &si->regs->spih_stat) & SPIH_RFEMPTY) == 0) {
143 SPIPCI_RREG(osh, &si->regs->spih_data);
144 }
145
146 /* Wait for power to stabilize to the SDIO Card (100msec was insufficient) */
147 OSL_DELAY(250000);
148
149 /* Check card detect on FPGA Revision >= 4 */
150 if (si->rev >= 4) {
151 if (SPIPCI_RREG(osh, &si->regs->spih_gpio_data) & SPIH_CARD_DETECT) {
152 sd_err(("%s: no card detected in SD slot\n", __FUNCTION__));
153 spi_reg_unmap(osh, (uintptr)si->regs, sizeof(spih_regs_t));
154 if (si->pciregs) {
155 spi_reg_unmap(osh, (uintptr)si->pciregs, sizeof(spih_pciregs_t));
156 }
157 MFREE(osh, si, sizeof(spih_info_t));
158 return FALSE;
159 }
160 }
161
162 /* Interrupts are level sensitive */
163 SPIPCI_WREG(osh, &si->regs->spih_int_edge, 0x80000000);
164
165 /* Interrupts are active low. */
166 SPIPCI_WREG(osh, &si->regs->spih_int_pol, 0x40000004);
167
168 /* Enable interrupts through PCI Core. */
169 if (si->pciregs) {
170 SPIPCI_WREG(osh, &si->pciregs->ICR, PCI_INT_PROP_EN);
171 }
172
173 sd_trace(("%s: exit\n", __FUNCTION__));
174 return TRUE;
175}
176
177/* Detach and return PCI-SPI Hardware to unconfigured state */
178bool
179spi_hw_detach(sdioh_info_t *sd)
180{
181 spih_info_t *si = (spih_info_t *)sd->controller;
182 osl_t *osh = si->osh;
183 spih_regs_t *regs = si->regs;
184 spih_pciregs_t *pciregs = si->pciregs;
185
186 sd_trace(("%s: enter\n", __FUNCTION__));
187
188 SPIPCI_WREG(osh, &regs->spih_ctrl, 0x00000010);
189 SPIPCI_WREG(osh, &regs->spih_gpio_ctrl, 0x00000000); /* Disable GPIO for CS# */
190 SPIPCI_WREG(osh, &regs->spih_int_mask, 0x00000000); /* Clear Intmask */
191 SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAF);
192 SPIPCI_WREG(osh, &regs->spih_int_edge, 0x00000000);
193 SPIPCI_WREG(osh, &regs->spih_int_pol, 0x00000000);
194 SPIPCI_WREG(osh, &regs->spih_hex_disp, 0x0000DEAD);
195
196 /* Disable interrupts through PCI Core. */
197 if (si->pciregs) {
198 SPIPCI_WREG(osh, &pciregs->ICR, 0x00000000);
199 spi_reg_unmap(osh, (uintptr)pciregs, sizeof(spih_pciregs_t));
200 }
201 spi_reg_unmap(osh, (uintptr)regs, sizeof(spih_regs_t));
202
203 MFREE(osh, si, sizeof(spih_info_t));
204
205 sd->controller = NULL;
206
207 sd_trace(("%s: exit\n", __FUNCTION__));
208 return TRUE;
209}
210
211/* Switch between internal (PCI) and external clock oscillator */
212static bool
213sdspi_switch_clock(sdioh_info_t *sd, bool ext_clk)
214{
215 spih_info_t *si = (spih_info_t *)sd->controller;
216 osl_t *osh = si->osh;
217 spih_regs_t *regs = si->regs;
218
219 /* Switch to desired clock, and reset the PLL. */
220 SPIPCI_WREG(osh, &regs->spih_pll_ctrl, ext_clk ? SPIH_EXT_CLK : 0);
221
222 SPINWAIT(((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED)
223 != SPIH_PLL_LOCKED), 1000);
224 if ((SPIPCI_RREG(osh, &regs->spih_pll_status) & SPIH_PLL_LOCKED) != SPIH_PLL_LOCKED) {
225 sd_err(("%s: timeout waiting for PLL to lock\n", __FUNCTION__));
226 return (FALSE);
227 }
228 return (TRUE);
229
230}
231
232/* Configure PCI-SPI Host Controller's SPI Clock rate as a divisor into the
233 * base clock rate. The base clock is either the PCI Clock (33MHz) or the
234 * external clock oscillator at U17 on the PciSpiHost.
235 */
236bool
237spi_start_clock(sdioh_info_t *sd, uint16 div)
238{
239 spih_info_t *si = (spih_info_t *)sd->controller;
240 osl_t *osh = si->osh;
241 spih_regs_t *regs = si->regs;
242 uint32 t, espr, disp;
243 uint32 disp_xtal_freq;
244 bool ext_clock = FALSE;
245 char disp_string[5];
246
247 if (div > 2048) {
248 sd_err(("%s: divisor %d too large; using max of 2048\n", __FUNCTION__, div));
249 div = 2048;
250 } else if (div & (div - 1)) { /* Not a power of 2? */
251 /* Round up to a power of 2 */
252 while ((div + 1) & div)
253 div |= div >> 1;
254 div++;
255 }
256
257 /* For FPGA Rev >= 5, the use of an external clock oscillator is supported.
258 * If the oscillator is populated, use it to provide the SPI base clock,
259 * otherwise, default to the PCI clock as the SPI base clock.
260 */
261 if (si->rev >= 5) {
262 uint32 clk_tick;
263 /* Enable the External Clock Oscillator as PLL clock source. */
264 if (!sdspi_switch_clock(sd, TRUE)) {
265 sd_err(("%s: error switching to external clock\n", __FUNCTION__));
266 }
267
268 /* Check to make sure the external clock is running. If not, then it
269 * is not populated on the card, so we will default to the PCI clock.
270 */
271 clk_tick = SPIPCI_RREG(osh, &regs->spih_clk_count);
272 if (clk_tick == SPIPCI_RREG(osh, &regs->spih_clk_count)) {
273
274 /* Switch back to the PCI clock as the clock source. */
275 if (!sdspi_switch_clock(sd, FALSE)) {
276 sd_err(("%s: error switching to external clock\n", __FUNCTION__));
277 }
278 } else {
279 ext_clock = TRUE;
280 }
281 }
282
283 /* Hack to allow hot-swapping oscillators:
284 * 1. Force PCI clock as clock source, using sd_divisor of 0.
285 * 2. Swap oscillator
286 * 3. Set desired sd_divisor (will switch to external oscillator as clock source.
287 */
288 if (div == 0) {
289 ext_clock = FALSE;
290 div = 2;
291
292 /* Select PCI clock as the clock source. */
293 if (!sdspi_switch_clock(sd, FALSE)) {
294 sd_err(("%s: error switching to external clock\n", __FUNCTION__));
295 }
296
297 sd_err(("%s: Ok to hot-swap oscillators.\n", __FUNCTION__));
298 }
299
300 /* If using the external oscillator, read the clock frequency from the controller
301 * The value read is in units of 10000Hz, and it's not a nice round number because
302 * it is calculated by the FPGA. So to make up for that, we round it off.
303 */
304 if (ext_clock == TRUE) {
305 uint32 xtal_freq;
306
307 OSL_DELAY(1000);
308 xtal_freq = SPIPCI_RREG(osh, &regs->spih_xtal_freq) * 10000;
309
310 sd_info(("%s: Oscillator is %dHz\n", __FUNCTION__, xtal_freq));
311
312
313 disp_xtal_freq = xtal_freq / 10000;
314
315 /* Round it off to a nice number. */
316 if ((disp_xtal_freq % 100) > 50) {
317 disp_xtal_freq += 100;
318 }
319
320 disp_xtal_freq = (disp_xtal_freq / 100) * 100;
321 } else {
322 sd_err(("%s: no external oscillator installed, using PCI clock.\n", __FUNCTION__));
323 disp_xtal_freq = 3333;
324 }
325
326 /* Convert the SPI Clock frequency to BCD format. */
327 sprintf(disp_string, "%04d", disp_xtal_freq / div);
328
329 disp = (disp_string[0] - '0') << 12;
330 disp |= (disp_string[1] - '0') << 8;
331 disp |= (disp_string[2] - '0') << 4;
332 disp |= (disp_string[3] - '0');
333
334 /* Select the correct ESPR register value based on the divisor. */
335 switch (div) {
336 case 1: espr = 0x0; break;
337 case 2: espr = 0x1; break;
338 case 4: espr = 0x2; break;
339 case 8: espr = 0x5; break;
340 case 16: espr = 0x3; break;
341 case 32: espr = 0x4; break;
342 case 64: espr = 0x6; break;
343 case 128: espr = 0x7; break;
344 case 256: espr = 0x8; break;
345 case 512: espr = 0x9; break;
346 case 1024: espr = 0xa; break;
347 case 2048: espr = 0xb; break;
348 default: espr = 0x0; ASSERT(0); break;
349 }
350
351 t = SPIPCI_RREG(osh, &regs->spih_ctrl);
352 t &= ~3;
353 t |= espr & 3;
354 SPIPCI_WREG(osh, &regs->spih_ctrl, t);
355
356 t = SPIPCI_RREG(osh, &regs->spih_ext);
357 t &= ~3;
358 t |= (espr >> 2) & 3;
359 SPIPCI_WREG(osh, &regs->spih_ext, t);
360
361 SPIPCI_WREG(osh, &regs->spih_hex_disp, disp);
362
363 /* For Rev 8, writing to the PLL_CTRL register resets
364 * the PLL, and it can re-acquire in 200uS. For
365 * Rev 7 and older, we use a software delay to allow
366 * the PLL to re-acquire, which takes more than 2mS.
367 */
368 if (si->rev < 8) {
369 /* Wait for clock to settle. */
370 OSL_DELAY(5000);
371 }
372
373 sd_info(("%s: SPI_CTRL=0x%08x SPI_EXT=0x%08x\n",
374 __FUNCTION__,
375 SPIPCI_RREG(osh, &regs->spih_ctrl),
376 SPIPCI_RREG(osh, &regs->spih_ext)));
377
378 return TRUE;
379}
380
381/* Configure PCI-SPI Host Controller High-Speed Clocking mode setting */
382bool
383spi_controller_highspeed_mode(sdioh_info_t *sd, bool hsmode)
384{
385 spih_info_t *si = (spih_info_t *)sd->controller;
386 osl_t *osh = si->osh;
387 spih_regs_t *regs = si->regs;
388
389 if (si->rev >= 10) {
390 if (hsmode) {
391 SPIPCI_ORREG(osh, &regs->spih_ext, 0x10);
392 } else {
393 SPIPCI_ANDREG(osh, &regs->spih_ext, ~0x10);
394 }
395 }
396
397 return TRUE;
398}
399
400/* Disable device interrupt */
401void
402spi_devintr_off(sdioh_info_t *sd)
403{
404 spih_info_t *si = (spih_info_t *)sd->controller;
405 osl_t *osh = si->osh;
406 spih_regs_t *regs = si->regs;
407
408 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
409 if (sd->use_client_ints) {
410 sd->intmask &= ~SPIH_DEV_INTR;
411 SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask); /* Clear Intmask */
412 }
413}
414
415/* Enable device interrupt */
416void
417spi_devintr_on(sdioh_info_t *sd)
418{
419 spih_info_t *si = (spih_info_t *)sd->controller;
420 osl_t *osh = si->osh;
421 spih_regs_t *regs = si->regs;
422
423 ASSERT(sd->lockcount == 0);
424 sd_trace(("%s: %d\n", __FUNCTION__, sd->use_client_ints));
425 if (sd->use_client_ints) {
426 if (SPIPCI_RREG(osh, &regs->spih_ctrl) & 0x02) {
427 /* Ack in case one was pending but is no longer... */
428 SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
429 }
430 sd->intmask |= SPIH_DEV_INTR;
431 /* Set device intr in Intmask */
432 SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
433 }
434}
435
436/* Check to see if an interrupt belongs to the PCI-SPI Host or a SPI Device */
437bool
438spi_check_client_intr(sdioh_info_t *sd, int *is_dev_intr)
439{
440 spih_info_t *si = (spih_info_t *)sd->controller;
441 osl_t *osh = si->osh;
442 spih_regs_t *regs = si->regs;
443 bool ours = FALSE;
444
445 uint32 raw_int, cur_int;
446 ASSERT(sd);
447
448 if (is_dev_intr)
449 *is_dev_intr = FALSE;
450 raw_int = SPIPCI_RREG(osh, &regs->spih_int_status);
451 cur_int = raw_int & sd->intmask;
452 if (cur_int & SPIH_DEV_INTR) {
453 if (sd->client_intr_enabled && sd->use_client_ints) {
454 sd->intrcount++;
455 ASSERT(sd->intr_handler);
456 ASSERT(sd->intr_handler_arg);
457 (sd->intr_handler)(sd->intr_handler_arg);
458 if (is_dev_intr)
459 *is_dev_intr = TRUE;
460 } else {
461 sd_trace(("%s: Not ready for intr: enabled %d, handler 0x%p\n",
462 __FUNCTION__, sd->client_intr_enabled, sd->intr_handler));
463 }
464 SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_DEV_INTR);
465 SPIPCI_RREG(osh, &regs->spih_int_status);
466 ours = TRUE;
467 } else if (cur_int & SPIH_CTLR_INTR) {
468 /* Interrupt is from SPI FIFO... just clear and ack it... */
469 sd_trace(("%s: SPI CTLR interrupt: raw_int 0x%08x cur_int 0x%08x\n",
470 __FUNCTION__, raw_int, cur_int));
471
472 /* Clear the interrupt in the SPI_STAT register */
473 SPIPCI_WREG(osh, &regs->spih_stat, 0x00000080);
474
475 /* Ack the interrupt in the interrupt controller */
476 SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_CTLR_INTR);
477 SPIPCI_RREG(osh, &regs->spih_int_status);
478
479 ours = TRUE;
480 } else if (cur_int & SPIH_WFIFO_INTR) {
481 sd_trace(("%s: SPI WR FIFO Empty interrupt: raw_int 0x%08x cur_int 0x%08x\n",
482 __FUNCTION__, raw_int, cur_int));
483
484 /* Disable the FIFO Empty Interrupt */
485 sd->intmask &= ~SPIH_WFIFO_INTR;
486 SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
487
488 sd->local_intrcount++;
489 sd->got_hcint = TRUE;
490 ours = TRUE;
491 } else {
492 /* Not an error: can share interrupts... */
493 sd_trace(("%s: Not my interrupt: raw_int 0x%08x cur_int 0x%08x\n",
494 __FUNCTION__, raw_int, cur_int));
495 ours = FALSE;
496 }
497
498 return ours;
499}
500
501static void
502hexdump(char *pfx, unsigned char *msg, int msglen)
503{
504 int i, col;
505 char buf[80];
506
507 ASSERT(strlen(pfx) + 49 <= sizeof(buf));
508
509 col = 0;
510
511 for (i = 0; i < msglen; i++, col++) {
512 if (col % 16 == 0)
513 strcpy(buf, pfx);
514 sprintf(buf + strlen(buf), "%02x", msg[i]);
515 if ((col + 1) % 16 == 0)
516 printf("%s\n", buf);
517 else
518 sprintf(buf + strlen(buf), " ");
519 }
520
521 if (col % 16 != 0)
522 printf("%s\n", buf);
523}
524
525/* Send/Receive an SPI Packet */
526void
527spi_sendrecv(sdioh_info_t *sd, uint8 *msg_out, uint8 *msg_in, int msglen)
528{
529 spih_info_t *si = (spih_info_t *)sd->controller;
530 osl_t *osh = si->osh;
531 spih_regs_t *regs = si->regs;
532 uint32 count;
533 uint32 spi_data_out;
534 uint32 spi_data_in;
535 bool yield;
536
537 sd_trace(("%s: enter\n", __FUNCTION__));
538
539 if (bcmpcispi_dump) {
540 printf("SENDRECV(len=%d)\n", msglen);
541 hexdump(" OUT: ", msg_out, msglen);
542 }
543
544#ifdef BCMSDYIELD
545 /* Only yield the CPU and wait for interrupt on Rev 8 and newer FPGA images. */
546 yield = ((msglen > 500) && (si->rev >= 8));
547#else
548 yield = FALSE;
549#endif /* BCMSDYIELD */
550
551 ASSERT(msglen % 4 == 0);
552
553
554 SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~SPIH_CS); /* Set GPIO CS# Low (asserted) */
555
556 for (count = 0; count < (uint32)msglen/4; count++) {
557 spi_data_out = ((uint32)((uint32 *)msg_out)[count]);
558 SPIPCI_WREG(osh, &regs->spih_data, spi_data_out);
559 }
560
561#ifdef BCMSDYIELD
562 if (yield) {
563 /* Ack the interrupt in the interrupt controller */
564 SPIPCI_WREG(osh, &regs->spih_int_status, SPIH_WFIFO_INTR);
565 SPIPCI_RREG(osh, &regs->spih_int_status);
566
567 /* Enable the FIFO Empty Interrupt */
568 sd->intmask |= SPIH_WFIFO_INTR;
569 sd->got_hcint = FALSE;
570 SPIPCI_WREG(osh, &regs->spih_int_mask, sd->intmask);
571
572 }
573#endif /* BCMSDYIELD */
574
575 /* Wait for write fifo to empty... */
576 SPIPCI_ANDREG(osh, &regs->spih_gpio_data, ~0x00000020); /* Set GPIO 5 Low */
577
578 if (yield) {
579 ASSERT((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0);
580 }
581
582 spi_waitbits(sd, yield);
583 SPIPCI_ORREG(osh, &regs->spih_gpio_data, 0x00000020); /* Set GPIO 5 High (de-asserted) */
584
585 for (count = 0; count < (uint32)msglen/4; count++) {
586 spi_data_in = SPIPCI_RREG(osh, &regs->spih_data);
587 ((uint32 *)msg_in)[count] = spi_data_in;
588 }
589
590 /* Set GPIO CS# High (de-asserted) */
591 SPIPCI_ORREG(osh, &regs->spih_gpio_data, SPIH_CS);
592
593 if (bcmpcispi_dump) {
594 hexdump(" IN : ", msg_in, msglen);
595 }
596}
597
598void
599spi_spinbits(sdioh_info_t *sd)
600{
601 spih_info_t *si = (spih_info_t *)sd->controller;
602 osl_t *osh = si->osh;
603 spih_regs_t *regs = si->regs;
604 uint spin_count; /* Spin loop bound check */
605
606 spin_count = 0;
607 while ((SPIPCI_RREG(sd->osh, &regs->spih_stat) & SPIH_WFEMPTY) == 0) {
608 if (spin_count > SPI_SPIN_BOUND) {
609 sd_err(("%s: SPIH_WFEMPTY spin bits out of bound %u times \n",
610 __FUNCTION__, spin_count));
611 ASSERT(FALSE);
612 }
613 spin_count++;
614 }
615
616 /* Wait for SPI Transfer state machine to return to IDLE state.
617 * The state bits are only implemented in Rev >= 5 FPGA. These
618 * bits are hardwired to 00 for Rev < 5, so this check doesn't cause
619 * any problems.
620 */
621 spin_count = 0;
622 while ((SPIPCI_RREG(osh, &regs->spih_stat) & SPIH_STATE_MASK) != 0) {
623 if (spin_count > SPI_SPIN_BOUND) {
624 sd_err(("%s: SPIH_STATE_MASK spin bits out of bound %u times \n",
625 __FUNCTION__, spin_count));
626 ASSERT(FALSE);
627 }
628 spin_count++;
629 }
630}