• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Broadcom SPI over PCI-SPI Host Controller, low-level hardware driver
3  *
4  * Copyright (C) 1999-2009, 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 2008/07/09 21:23:30 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 
54 int bcmpcispi_dump = 0;		/* Set to dump complete trace of all SPI bus transactions */
55 
56 typedef 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 */
67 bool
spi_hw_attach(sdioh_info_t * sd)68 spi_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 */
178 bool
spi_hw_detach(sdioh_info_t * sd)179 spi_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 */
212 static bool
sdspi_switch_clock(sdioh_info_t * sd,bool ext_clk)213 sdspi_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  */
236 bool
spi_start_clock(sdioh_info_t * sd,uint16 div)237 spi_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 */
382 bool
spi_controller_highspeed_mode(sdioh_info_t * sd,bool hsmode)383 spi_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 */
401 void
spi_devintr_off(sdioh_info_t * sd)402 spi_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 */
416 void
spi_devintr_on(sdioh_info_t * sd)417 spi_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 */
437 bool
spi_check_client_intr(sdioh_info_t * sd,int * is_dev_intr)438 spi_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 
501 static void
hexdump(char * pfx,unsigned char * msg,int msglen)502 hexdump(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 */
526 void
spi_sendrecv(sdioh_info_t * sd,uint8 * msg_out,uint8 * msg_in,int msglen)527 spi_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 
598 void
spi_spinbits(sdioh_info_t * sd)599 spi_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 			ASSERT(FALSE); /* Spin bound exceeded */
610 		}
611 		spin_count++;
612 	}
613 	spin_count = 0;
614 	/* Wait for SPI Transfer state machine to return to IDLE state.
615 	 * The state bits are only implemented in Rev >= 5 FPGA.  These
616 	 * bits are hardwired to 00 for Rev < 5, so this check doesn't cause
617 	 * any problems.
618 	 */
619 	while ((SPIPCI_RREG(osh, &regs->spih_stat) & SPIH_STATE_MASK) != 0) {
620 		if (spin_count > SPI_SPIN_BOUND) {
621 			ASSERT(FALSE);
622 		}
623 		spin_count++;
624 	}
625 }
626