Home | History | Annotate | Download | only in sys
      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 
     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
     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
    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
    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
    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
    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
    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
    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
    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
    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
    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
    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 			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 }
    631