Dmitry Shmidt | da65eba | 2010-05-19 18:53:11 -0700 | [diff] [blame] | 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, ®s->spih_ctrl, 0x00000010); |
| 189 | SPIPCI_WREG(osh, ®s->spih_gpio_ctrl, 0x00000000); /* Disable GPIO for CS# */ |
| 190 | SPIPCI_WREG(osh, ®s->spih_int_mask, 0x00000000); /* Clear Intmask */ |
| 191 | SPIPCI_WREG(osh, ®s->spih_hex_disp, 0x0000DEAF); |
| 192 | SPIPCI_WREG(osh, ®s->spih_int_edge, 0x00000000); |
| 193 | SPIPCI_WREG(osh, ®s->spih_int_pol, 0x00000000); |
| 194 | SPIPCI_WREG(osh, ®s->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, ®s->spih_pll_ctrl, ext_clk ? SPIH_EXT_CLK : 0); |
| 221 | |
| 222 | SPINWAIT(((SPIPCI_RREG(osh, ®s->spih_pll_status) & SPIH_PLL_LOCKED) |
| 223 | != SPIH_PLL_LOCKED), 1000); |
| 224 | if ((SPIPCI_RREG(osh, ®s->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, ®s->spih_clk_count); |
| 272 | if (clk_tick == SPIPCI_RREG(osh, ®s->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, ®s->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, ®s->spih_ctrl); |
| 352 | t &= ~3; |
| 353 | t |= espr & 3; |
| 354 | SPIPCI_WREG(osh, ®s->spih_ctrl, t); |
| 355 | |
| 356 | t = SPIPCI_RREG(osh, ®s->spih_ext); |
| 357 | t &= ~3; |
| 358 | t |= (espr >> 2) & 3; |
| 359 | SPIPCI_WREG(osh, ®s->spih_ext, t); |
| 360 | |
| 361 | SPIPCI_WREG(osh, ®s->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, ®s->spih_ctrl), |
| 376 | SPIPCI_RREG(osh, ®s->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, ®s->spih_ext, 0x10); |
| 392 | } else { |
| 393 | SPIPCI_ANDREG(osh, ®s->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, ®s->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, ®s->spih_ctrl) & 0x02) { |
| 427 | /* Ack in case one was pending but is no longer... */ |
| 428 | SPIPCI_WREG(osh, ®s->spih_int_status, SPIH_DEV_INTR); |
| 429 | } |
| 430 | sd->intmask |= SPIH_DEV_INTR; |
| 431 | /* Set device intr in Intmask */ |
| 432 | SPIPCI_WREG(osh, ®s->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, ®s->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, ®s->spih_int_status, SPIH_DEV_INTR); |
| 465 | SPIPCI_RREG(osh, ®s->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, ®s->spih_stat, 0x00000080); |
| 474 | |
| 475 | /* Ack the interrupt in the interrupt controller */ |
| 476 | SPIPCI_WREG(osh, ®s->spih_int_status, SPIH_CTLR_INTR); |
| 477 | SPIPCI_RREG(osh, ®s->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, ®s->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, ®s->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, ®s->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, ®s->spih_int_status, SPIH_WFIFO_INTR); |
| 565 | SPIPCI_RREG(osh, ®s->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, ®s->spih_int_mask, sd->intmask); |
| 571 | |
| 572 | } |
| 573 | #endif /* BCMSDYIELD */ |
| 574 | |
| 575 | /* Wait for write fifo to empty... */ |
| 576 | SPIPCI_ANDREG(osh, ®s->spih_gpio_data, ~0x00000020); /* Set GPIO 5 Low */ |
| 577 | |
| 578 | if (yield) { |
| 579 | ASSERT((SPIPCI_RREG(sd->osh, ®s->spih_stat) & SPIH_WFEMPTY) == 0); |
| 580 | } |
| 581 | |
| 582 | spi_waitbits(sd, yield); |
| 583 | SPIPCI_ORREG(osh, ®s->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, ®s->spih_data); |
| 587 | ((uint32 *)msg_in)[count] = spi_data_in; |
| 588 | } |
| 589 | |
| 590 | /* Set GPIO CS# High (de-asserted) */ |
| 591 | SPIPCI_ORREG(osh, ®s->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, ®s->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, ®s->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 | } |