diff options
Diffstat (limited to 'drivers/net/wireless/bcm4329/bcmpcispi.c')
-rw-r--r-- | drivers/net/wireless/bcm4329/bcmpcispi.c | 630 |
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 | |||
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 | } | ||