cf3c20ae |
1 | /* $OpenBSD: ommmc.c,v 1.18 2016/05/02 07:38:34 jsg Exp $ */ |
2 | |
3 | /* |
4 | * Copyright (c) 2009 Dale Rahn <drahn@openbsd.org> |
5 | * Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org> |
6 | * |
7 | * Permission to use, copy, modify, and distribute this software for any |
8 | * purpose with or without fee is hereby granted, provided that the above |
9 | * copyright notice and this permission notice appear in all copies. |
10 | * |
11 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
12 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
13 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
14 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
15 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
16 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
17 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
18 | */ |
19 | |
20 | /* Omap SD/MMC support derived from /sys/dev/sdmmc/sdhc.c */ |
21 | |
22 | |
23 | #include <sys/param.h> |
24 | #include <sys/device.h> |
25 | #include <sys/kernel.h> |
26 | #include <sys/kthread.h> |
27 | #include <sys/malloc.h> |
28 | #include <sys/systm.h> |
29 | #include <machine/bus.h> |
30 | |
31 | #include <dev/sdmmc/sdmmcchip.h> |
32 | #include <dev/sdmmc/sdmmcvar.h> |
33 | |
34 | #include <armv7/armv7/armv7var.h> |
35 | #include <armv7/omap/prcmvar.h> |
36 | |
37 | /* |
38 | * NOTE: on OMAP4430/AM335x these registers skew by 0x100 |
39 | * this is handled by mapping at base address + 0x100 |
40 | */ |
41 | /* registers */ |
42 | #define MMCHS_SYSCONFIG 0x010 |
43 | #define MMCHS_SYSSTATUS 0x014 |
44 | #define MMCHS_CSRE 0x024 |
45 | #define MMCHS_SYSTEST 0x028 |
46 | #define MMCHS_SYSTEST_SDCD (1 << 15) |
47 | #define MMCHS_CON 0x02C |
48 | #define MMCHS_CON_INIT (1<<1) |
49 | #define MMCHS_CON_DW8 (1<<5) |
50 | #define MMCHS_CON_OD (1<<0) |
51 | #define MMCHS_PWCNT 0x030 |
52 | #define MMCHS_BLK 0x104 |
53 | #define MMCHS_BLK_NBLK_MAX 0xffff |
54 | #define MMCHS_BLK_NBLK_SHIFT 16 |
55 | #define MMCHS_BLK_NBLK_MASK (MMCHS_BLK_NBLK_MAX<<MMCHS_BLK_NBLK_SHIFT) |
56 | #define MMCHS_BLK_BLEN_MAX 0x400 |
57 | #define MMCHS_BLK_BLEN_SHIFT 0 |
58 | #define MMCHS_BLK_BLEN_MASK (MMCHS_BLK_BLEN_MAX<<MMCHS_BLK_BLEN_SHIFT) |
59 | #define MMCHS_ARG 0x108 |
60 | #define MMCHS_CMD 0x10C |
61 | #define MMCHS_CMD_INDX_SHIFT 24 |
62 | #define MMCHS_CMD_INDX_SHIFT_MASK (0x3f << MMCHS_CMD_INDX_SHIFT) |
63 | #define MMCHS_CMD_CMD_TYPE_SHIFT 22 |
64 | #define MMCHS_CMD_DP_SHIFT 21 |
65 | #define MMCHS_CMD_DP (1 << MMCHS_CMD_DP_SHIFT) |
66 | #define MMCHS_CMD_CICE_SHIFT 20 |
67 | #define MMCHS_CMD_CICE (1 << MMCHS_CMD_CICE_SHIFT) |
68 | #define MMCHS_CMD_CCCE_SHIFT 19 |
69 | #define MMCHS_CMD_CCCE (1 << MMCHS_CMD_CCCE_SHIFT) |
70 | #define MMCHS_CMD_RSP_TYPE_SHIFT 16 |
71 | #define MMCHS_CMD_RESP_NONE (0x0 << MMCHS_CMD_RSP_TYPE_SHIFT) |
72 | #define MMCHS_CMD_RESP136 (0x1 << MMCHS_CMD_RSP_TYPE_SHIFT) |
73 | #define MMCHS_CMD_RESP48 (0x2 << MMCHS_CMD_RSP_TYPE_SHIFT) |
74 | #define MMCHS_CMD_RESP48B (0x3 << MMCHS_CMD_RSP_TYPE_SHIFT) |
75 | #define MMCHS_CMD_MSBS (1 << 5) |
76 | #define MMCHS_CMD_DDIR (1 << 4) |
77 | #define MMCHS_CMD_ACEN (1 << 2) |
78 | #define MMCHS_CMD_BCE (1 << 1) |
79 | #define MMCHS_CMD_DE (1 << 0) |
80 | #define MMCHS_RSP10 0x110 |
81 | #define MMCHS_RSP32 0x114 |
82 | #define MMCHS_RSP54 0x118 |
83 | #define MMCHS_RSP76 0x11C |
84 | #define MMCHS_DATA 0x120 |
85 | #define MMCHS_PSTATE 0x124 |
86 | #define MMCHS_PSTATE_CLEV (1<<24) |
87 | #define MMCHS_PSTATE_DLEV_SH 20 |
88 | #define MMCHS_PSTATE_DLEV_M (0xf << MMCHS_PSTATE_DLEV_SH) |
89 | #define MMCHS_PSTATE_BRE (1<<11) |
90 | #define MMCHS_PSTATE_BWE (1<<10) |
91 | #define MMCHS_PSTATE_RTA (1<<9) |
92 | #define MMCHS_PSTATE_WTA (1<<8) |
93 | #define MMCHS_PSTATE_DLA (1<<2) |
94 | #define MMCHS_PSTATE_DATI (1<<1) |
95 | #define MMCHS_PSTATE_CMDI (1<<0) |
96 | #define MMCHS_PSTATE_FMT "\20" \ |
97 | "\x098_CLEV" \ |
98 | "\x08b_BRE" \ |
99 | "\x08a_BWE" \ |
100 | "\x089_RTA" \ |
101 | "\x088_WTA" \ |
102 | "\x082_DLA" \ |
103 | "\x081_DATI" \ |
104 | "\x080_CMDI" |
105 | #define MMCHS_HCTL 0x128 |
106 | #define MMCHS_HCTL_SDVS_SHIFT 9 |
107 | #define MMCHS_HCTL_SDVS_MASK (0x7<<MMCHS_HCTL_SDVS_SHIFT) |
108 | #define MMCHS_HCTL_SDVS_V18 (0x5<<MMCHS_HCTL_SDVS_SHIFT) |
109 | #define MMCHS_HCTL_SDVS_V30 (0x6<<MMCHS_HCTL_SDVS_SHIFT) |
110 | #define MMCHS_HCTL_SDVS_V33 (0x7<<MMCHS_HCTL_SDVS_SHIFT) |
111 | #define MMCHS_HCTL_SDBP (1<<8) |
112 | #define MMCHS_HCTL_HSPE (1<<2) |
113 | #define MMCHS_HCTL_DTW (1<<1) |
114 | #define MMCHS_SYSCTL 0x12C |
115 | #define MMCHS_SYSCTL_SRD (1<<26) |
116 | #define MMCHS_SYSCTL_SRC (1<<25) |
117 | #define MMCHS_SYSCTL_SRA (1<<24) |
118 | #define MMCHS_SYSCTL_DTO_SH 16 |
119 | #define MMCHS_SYSCTL_DTO_MASK 0x000f0000 |
120 | #define MMCHS_SYSCTL_CLKD_SH 6 |
121 | #define MMCHS_SYSCTL_CLKD_MASK 0x0000ffc0 |
122 | #define MMCHS_SYSCTL_CEN (1<<2) |
123 | #define MMCHS_SYSCTL_ICS (1<<1) |
124 | #define MMCHS_SYSCTL_ICE (1<<0) |
125 | #define MMCHS_STAT 0x130 |
126 | #define MMCHS_STAT_BADA (1<<29) |
127 | #define MMCHS_STAT_CERR (1<<28) |
128 | #define MMCHS_STAT_ACE (1<<24) |
129 | #define MMCHS_STAT_DEB (1<<22) |
130 | #define MMCHS_STAT_DCRC (1<<21) |
131 | #define MMCHS_STAT_DTO (1<<20) |
132 | #define MMCHS_STAT_CIE (1<<19) |
133 | #define MMCHS_STAT_CEB (1<<18) |
134 | #define MMCHS_STAT_CCRC (1<<17) |
135 | #define MMCHS_STAT_CTO (1<<16) |
136 | #define MMCHS_STAT_ERRI (1<<15) |
137 | #define MMCHS_STAT_OBI (1<<9) |
138 | #define MMCHS_STAT_CIRQ (1<<8) |
139 | #define MMCHS_STAT_BRR (1<<5) |
140 | #define MMCHS_STAT_BWR (1<<4) |
141 | #define MMCHS_STAT_BGE (1<<2) |
142 | #define MMCHS_STAT_TC (1<<1) |
143 | #define MMCHS_STAT_CC (1<<0) |
144 | #define MMCHS_STAT_FMT "\20" \ |
145 | "\x09d_BADA" \ |
146 | "\x09c_CERR" \ |
147 | "\x098_ACE" \ |
148 | "\x096_DEB" \ |
149 | "\x095_DCRC" \ |
150 | "\x094_DTO" \ |
151 | "\x093_CIE" \ |
152 | "\x092_CEB" \ |
153 | "\x091_CCRC" \ |
154 | "\x090_CTO" \ |
155 | "\x08f_ERRI" \ |
156 | "\x089_OBI" \ |
157 | "\x088_CIRQ" \ |
158 | "\x085_BRR" \ |
159 | "\x084_BWR" \ |
160 | "\x082_BGE" \ |
161 | "\x081_TC" \ |
162 | "\x080_CC" |
163 | #define MMCHS_IE 0x134 |
164 | #define MMCHS_ISE 0x138 |
165 | #define MMCHS_AC12 0x13C |
166 | #define MMCHS_CAPA 0x140 |
167 | #define MMCHS_CAPA_VS18 (1 << 26) |
168 | #define MMCHS_CAPA_VS30 (1 << 25) |
169 | #define MMCHS_CAPA_VS33 (1 << 24) |
170 | #define MMCHS_CAPA_SRS (1 << 23) |
171 | #define MMCHS_CAPA_DS (1 << 22) |
172 | #define MMCHS_CAPA_HSS (1 << 21) |
173 | #define MMCHS_CAPA_MBL_SHIFT 16 |
174 | #define MMCHS_CAPA_MBL_MASK (3 << MMCHS_CAPA_MBL_SHIFT) |
175 | #define MMCHS_CUR_CAPA 0x148 |
176 | #define MMCHS_REV 0x1fc |
177 | |
178 | #define SDHC_COMMAND_TIMEOUT hz |
179 | #define SDHC_BUFFER_TIMEOUT hz |
180 | #define SDHC_TRANSFER_TIMEOUT hz |
181 | |
182 | void ommmc_attach(struct device *parent, struct device *self, void *args); |
183 | |
184 | struct ommmc_softc { |
185 | struct device sc_dev; |
186 | bus_space_tag_t sc_iot; |
187 | bus_space_handle_t sc_ioh; |
188 | void *sc_ih; /* Interrupt handler */ |
189 | uint32_t sc_flags; |
190 | |
191 | struct device *sdmmc; /* generic SD/MMC device */ |
192 | int clockbit; /* clock control bit */ |
193 | uint32_t clkbase; /* base clock frequency in KHz */ |
194 | int maxblklen; /* maximum block length */ |
195 | int flags; /* flags for this host */ |
196 | uint32_t ocr; /* OCR value from capabilities */ |
197 | uint32_t intr_status; /* soft interrupt status */ |
198 | uint32_t intr_error_status; /* */ |
199 | }; |
200 | |
201 | |
202 | /* Host controller functions called by the attachment driver. */ |
203 | int ommmc_host_found(struct ommmc_softc *, bus_space_tag_t, |
204 | bus_space_handle_t, bus_size_t, int); |
205 | void ommmc_power(int, void *); |
206 | void ommmc_shutdown(void *); |
207 | int ommmc_intr(void *); |
208 | |
209 | /* RESET MODES */ |
210 | #define MMC_RESET_DAT 1 |
211 | #define MMC_RESET_CMD 2 |
212 | #define MMC_RESET_ALL (MMC_RESET_CMD|MMC_RESET_DAT) |
213 | |
214 | /* flag values */ |
215 | #define SHF_USE_DMA 0x0001 |
216 | |
217 | #define HREAD4(sc, reg) \ |
218 | (bus_space_read_4((sc)->sc_iot, (sc)->sc_ioh, (reg))) |
219 | #define HWRITE4(sc, reg, val) \ |
220 | bus_space_write_4((sc)->sc_iot, (sc)->sc_ioh, (reg), (val)) |
221 | #define HSET4(sc, reg, bits) \ |
222 | HWRITE4((sc), (reg), HREAD4((sc), (reg)) | (bits)) |
223 | #define HCLR4(sc, reg, bits) \ |
224 | HWRITE4((sc), (reg), HREAD4((sc), (reg)) & ~(bits)) |
225 | |
226 | int ommmc_host_reset(sdmmc_chipset_handle_t); |
227 | uint32_t ommmc_host_ocr(sdmmc_chipset_handle_t); |
228 | int ommmc_host_maxblklen(sdmmc_chipset_handle_t); |
229 | int ommmc_card_detect(sdmmc_chipset_handle_t); |
230 | int ommmc_bus_power(sdmmc_chipset_handle_t, uint32_t); |
231 | int ommmc_bus_clock(sdmmc_chipset_handle_t, int); |
232 | int ommmc_bus_width(sdmmc_chipset_handle_t, int); |
233 | void ommmc_card_intr_mask(sdmmc_chipset_handle_t, int); |
234 | void ommmc_card_intr_ack(sdmmc_chipset_handle_t); |
235 | void ommmc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *); |
236 | int ommmc_start_command(struct ommmc_softc *, struct sdmmc_command *); |
237 | int ommmc_wait_state(struct ommmc_softc *, uint32_t, uint32_t); |
238 | int ommmc_soft_reset(struct ommmc_softc *, int); |
239 | int ommmc_wait_intr(struct ommmc_softc *, int, int); |
240 | void ommmc_transfer_data(struct ommmc_softc *, struct sdmmc_command *); |
241 | void ommmc_read_data(struct ommmc_softc *, uint8_t *, int); |
242 | void ommmc_write_data(struct ommmc_softc *, uint8_t *, int); |
243 | |
244 | /* #define SDHC_DEBUG */ |
245 | #ifdef SDHC_DEBUG |
246 | int ommmcdebug = 20; |
247 | #define DPRINTF(n,s) do { if ((n) <= ommmcdebug) printf s; } while (0) |
248 | void ommmc_dump_regs(struct ommmc_softc *); |
249 | #else |
250 | #define DPRINTF(n,s) do {} while(0) |
251 | #endif |
252 | |
253 | struct sdmmc_chip_functions ommmc_functions = { |
254 | /* host controller reset */ |
255 | ommmc_host_reset, |
256 | /* host controller capabilities */ |
257 | ommmc_host_ocr, |
258 | ommmc_host_maxblklen, |
259 | /* card detection */ |
260 | ommmc_card_detect, |
261 | /* bus power and clock frequency */ |
262 | ommmc_bus_power, |
263 | ommmc_bus_clock, |
264 | ommmc_bus_width, |
265 | /* command execution */ |
266 | ommmc_exec_command, |
267 | /* card interrupt */ |
268 | ommmc_card_intr_mask, |
269 | ommmc_card_intr_ack |
270 | }; |
271 | |
272 | struct cfdriver ommmc_cd = { |
273 | NULL, "ommmc", DV_DULL |
274 | }; |
275 | |
276 | struct cfattach ommmc_ca = { |
277 | sizeof(struct ommmc_softc), NULL, ommmc_attach |
278 | }; |
279 | |
280 | void |
281 | ommmc_attach(struct device *parent, struct device *self, void *args) |
282 | { |
283 | struct ommmc_softc *sc = (struct ommmc_softc *) self; |
284 | struct armv7_attach_args *aa = args; |
285 | struct sdmmcbus_attach_args saa; |
286 | uint32_t caps; |
287 | |
288 | sc->sc_iot = aa->aa_iot; |
289 | if (bus_space_map(sc->sc_iot, aa->aa_dev->mem[0].addr, |
290 | aa->aa_dev->mem[0].size, 0, &sc->sc_ioh)) |
291 | panic("%s: bus_space_map failed!", __func__); |
292 | |
293 | printf("\n"); |
294 | |
295 | /* Enable ICLKEN, FCLKEN? */ |
296 | prcm_enablemodule(PRCM_MMC0 + aa->aa_dev->unit); |
297 | |
298 | sc->sc_ih = arm_intr_establish(aa->aa_dev->irq[0], IPL_SDMMC, |
299 | ommmc_intr, sc, DEVNAME(sc)); |
300 | if (sc->sc_ih == NULL) { |
301 | printf("%s: cannot map interrupt\n", DEVNAME(sc)); |
302 | goto err; |
303 | } |
304 | |
305 | /* Controller Voltage Capabilities Initialization */ |
306 | HSET4(sc, MMCHS_CAPA, MMCHS_CAPA_VS18 | MMCHS_CAPA_VS30); |
307 | |
308 | #ifdef SDHC_DEBUG |
309 | ommmc_dump_regs(sc); |
310 | #endif |
311 | |
312 | /* |
313 | * Reset the host controller and enable interrupts. |
314 | */ |
315 | ommmc_host_reset(sc); |
316 | |
317 | /* Determine host capabilities. */ |
318 | caps = HREAD4(sc, MMCHS_CAPA); |
319 | |
320 | #if 0 |
321 | /* we want this !! */ |
322 | /* Use DMA if the host system and the controller support it. */ |
323 | if (usedma && ISSET(caps, SDHC_DMA_SUPPORT)) |
324 | SET(sc->flags, SHF_USE_DMA); |
325 | #endif |
326 | |
327 | /* |
328 | * Determine the base clock frequency. (2.2.24) |
329 | */ |
330 | |
331 | sc->clkbase = 96 * 1000; |
332 | #if 0 |
333 | if (SDHC_BASE_FREQ_KHZ(caps) != 0) |
334 | sc->clkbase = SDHC_BASE_FREQ_KHZ(caps); |
335 | sc->clkbase = SDHC_BASE_FREQ_KHZ(caps); |
336 | #endif |
337 | if (sc->clkbase == 0) { |
338 | /* The attachment driver must tell us. */ |
339 | printf("%s: base clock frequency unknown\n", DEVNAME(sc)); |
340 | goto err; |
341 | } else if (sc->clkbase < 10000 || sc->clkbase > 96000) { |
342 | /* SDHC 1.0 supports only 10-63 MHz. */ |
343 | printf("%s: base clock frequency out of range: %u MHz\n", |
344 | DEVNAME(sc), sc->clkbase / 1000); |
345 | goto err; |
346 | } |
347 | |
348 | /* |
349 | * XXX Set the data timeout counter value according to |
350 | * capabilities. (2.2.15) |
351 | */ |
352 | |
353 | |
354 | /* |
355 | * Determine SD bus voltage levels supported by the controller. |
356 | */ |
357 | if (caps & MMCHS_CAPA_VS18) |
358 | SET(sc->ocr, MMC_OCR_1_65V_1_95V); |
359 | if (caps & MMCHS_CAPA_VS30) |
360 | SET(sc->ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V); |
361 | if (caps & MMCHS_CAPA_VS33) |
362 | SET(sc->ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V); |
363 | |
364 | /* |
365 | * Omap max block size is fixed (single buffer), could limit |
366 | * this to 512 for double buffering, but dont see the point. |
367 | */ |
368 | switch ((caps & MMCHS_CAPA_MBL_MASK) >> MMCHS_CAPA_MBL_SHIFT) { |
369 | case 0: |
370 | sc->maxblklen = 512; |
371 | break; |
372 | case 1: |
373 | sc->maxblklen = 1024; |
374 | break; |
375 | case 2: |
376 | sc->maxblklen = 2048; |
377 | break; |
378 | default: |
379 | sc->maxblklen = 512; |
380 | printf("invalid capability blocksize in capa %08x," |
381 | " trying 512\n", HREAD4(sc, MMCHS_CAPA)); |
382 | } |
383 | /* |
384 | * MMC does not support blksize > 512 yet |
385 | */ |
386 | sc->maxblklen = 512; |
387 | /* |
388 | * Attach the generic SD/MMC bus driver. (The bus driver must |
389 | * not invoke any chipset functions before it is attached.) |
390 | */ |
391 | bzero(&saa, sizeof(saa)); |
392 | saa.saa_busname = "sdmmc"; |
393 | saa.sct = &ommmc_functions; |
394 | saa.sch = sc; |
395 | saa.caps = SMC_CAPS_4BIT_MODE; |
396 | if (caps & MMCHS_CAPA_HSS) |
397 | saa.caps |= SMC_CAPS_MMC_HIGHSPEED; |
398 | |
399 | sc->sdmmc = config_found(&sc->sc_dev, &saa, NULL); |
400 | if (sc->sdmmc == NULL) { |
401 | printf("%s: can't attach sdmmc\n", DEVNAME(sc)); |
402 | goto err; |
403 | } |
404 | |
405 | return; |
406 | err: |
407 | if (sc->sc_ih != NULL) |
408 | arm_intr_disestablish(sc->sc_ih); |
409 | bus_space_unmap(sc->sc_iot, sc->sc_ioh, aa->aa_dev->mem[0].size); |
410 | } |
411 | |
412 | |
413 | /* |
414 | * Power hook established by or called from attachment driver. |
415 | */ |
416 | void |
417 | ommmc_power(int why, void *arg) |
418 | { |
419 | #if 0 |
420 | struct ommmc_softc *sc = arg; |
421 | int n, i; |
422 | #endif |
423 | |
424 | switch(why) { |
425 | case DVACT_SUSPEND: |
426 | /* XXX poll for command completion or suspend command |
427 | * in progress */ |
428 | |
429 | /* Save the host controller state. */ |
430 | #if 0 |
431 | for (i = 0; i < sizeof sc->regs; i++) |
432 | sc->regs[i] = HREAD1(sc, i); |
433 | #endif |
434 | break; |
435 | |
436 | case DVACT_RESUME: |
437 | /* Restore the host controller state. */ |
438 | #if 0 |
439 | (void)ommmc_host_reset(sc); |
440 | for (i = 0; i < sizeof sc->regs; i++) |
441 | HWRITE1(sc, i, sc->regs[i]); |
442 | #endif |
443 | break; |
444 | } |
445 | } |
446 | |
447 | /* |
448 | * Shutdown hook established by or called from attachment driver. |
449 | */ |
450 | void |
451 | ommmc_shutdown(void *arg) |
452 | { |
453 | struct ommmc_softc *sc = arg; |
454 | |
455 | /* XXX chip locks up if we don't disable it before reboot. */ |
456 | (void)ommmc_host_reset(sc); |
457 | } |
458 | |
459 | /* |
460 | * Reset the host controller. Called during initialization, when |
461 | * cards are removed, upon resume, and during error recovery. |
462 | */ |
463 | int |
464 | ommmc_host_reset(sdmmc_chipset_handle_t sch) |
465 | { |
466 | struct ommmc_softc *sc = sch; |
467 | uint32_t imask; |
468 | int error; |
469 | int s; |
470 | |
471 | s = splsdmmc(); |
472 | |
473 | /* Disable all interrupts. */ |
474 | HWRITE4(sc, MMCHS_IE, 0); |
475 | HWRITE4(sc, MMCHS_ISE, 0); |
476 | |
477 | /* |
478 | * Reset the entire host controller and wait up to 100ms for |
479 | * the controller to clear the reset bit. |
480 | */ |
481 | if ((error = ommmc_soft_reset(sc, MMCHS_SYSCTL_SRA)) != 0) { |
482 | splx(s); |
483 | return (error); |
484 | } |
485 | |
486 | #if 0 |
487 | HSET4(sc, MMCHS_CON, MMCHS_CON_INIT); |
488 | HWRITE4(sc, MMCHS_CMD, 0); |
489 | delay(100); /* should delay 1ms */ |
490 | |
491 | HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CC); |
492 | HCLR4(sc, MMCHS_CON, MMCHS_CON_INIT); |
493 | HWRITE4(sc, MMCHS_STAT, ~0); |
494 | #endif |
495 | |
496 | |
497 | /* Set data timeout counter value to max for now. */ |
498 | HSET4(sc, MMCHS_SYSCTL, 0xe << MMCHS_SYSCTL_DTO_SH); |
499 | |
500 | /* Enable interrupts. */ |
501 | imask = MMCHS_STAT_BRR | MMCHS_STAT_BWR | MMCHS_STAT_BGE | |
502 | MMCHS_STAT_TC | MMCHS_STAT_CC; |
503 | |
504 | imask |= MMCHS_STAT_BADA | MMCHS_STAT_CERR | MMCHS_STAT_DEB | |
505 | MMCHS_STAT_DCRC | MMCHS_STAT_DTO | MMCHS_STAT_CIE | |
506 | MMCHS_STAT_CEB | MMCHS_STAT_CCRC | MMCHS_STAT_CTO; |
507 | |
508 | HWRITE4(sc, MMCHS_IE, imask); |
509 | HWRITE4(sc, MMCHS_ISE, imask); |
510 | |
511 | splx(s); |
512 | return (0); |
513 | } |
514 | |
515 | uint32_t |
516 | ommmc_host_ocr(sdmmc_chipset_handle_t sch) |
517 | { |
518 | struct ommmc_softc *sc = sch; |
519 | return (sc->ocr); |
520 | } |
521 | |
522 | int |
523 | ommmc_host_maxblklen(sdmmc_chipset_handle_t sch) |
524 | { |
525 | struct ommmc_softc *sc = sch; |
526 | return (sc->maxblklen); |
527 | } |
528 | |
529 | /* |
530 | * Return non-zero if the card is currently inserted. |
531 | */ |
532 | int |
533 | ommmc_card_detect(sdmmc_chipset_handle_t sch) |
534 | { |
535 | struct ommmc_softc *sc = sch; |
536 | return !ISSET(HREAD4(sc, MMCHS_SYSTEST), MMCHS_SYSTEST_SDCD) ? |
537 | 1 : 0; |
538 | } |
539 | |
540 | /* |
541 | * Set or change SD bus voltage and enable or disable SD bus power. |
542 | * Return zero on success. |
543 | */ |
544 | int |
545 | ommmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) |
546 | { |
547 | struct ommmc_softc *sc = sch; |
548 | uint32_t vdd; |
549 | uint32_t reg; |
550 | int s; |
551 | |
552 | s = splsdmmc(); |
553 | |
554 | /* |
555 | * Disable bus power before voltage change. |
556 | */ |
557 | HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP); |
558 | |
559 | /* If power is disabled, reset the host and return now. */ |
560 | if (ocr == 0) { |
561 | splx(s); |
562 | (void)ommmc_host_reset(sc); |
563 | return (0); |
564 | } |
565 | |
566 | /* |
567 | * Select the maximum voltage according to capabilities. |
568 | */ |
569 | ocr &= sc->ocr; |
570 | |
571 | if (ISSET(ocr, MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V)) |
572 | vdd = MMCHS_HCTL_SDVS_V33; |
573 | else if (ISSET(ocr, MMC_OCR_2_9V_3_0V | MMC_OCR_3_0V_3_1V)) |
574 | vdd = MMCHS_HCTL_SDVS_V30; |
575 | else if (ISSET(ocr, MMC_OCR_1_65V_1_95V)) |
576 | vdd = MMCHS_HCTL_SDVS_V18; |
577 | else { |
578 | /* Unsupported voltage level requested. */ |
579 | splx(s); |
580 | return (EINVAL); |
581 | } |
582 | |
583 | /* |
584 | * Enable bus power. Wait at least 1 ms (or 74 clocks) plus |
585 | * voltage ramp until power rises. |
586 | */ |
587 | reg = HREAD4(sc, MMCHS_HCTL); |
588 | reg &= ~MMCHS_HCTL_SDVS_MASK; |
589 | reg |= vdd; |
590 | HWRITE4(sc, MMCHS_HCTL, reg); |
591 | |
592 | HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_SDBP); |
593 | delay(10000); /* XXX */ |
594 | |
595 | /* |
596 | * The host system may not power the bus due to battery low, |
597 | * etc. In that case, the host controller should clear the |
598 | * bus power bit. |
599 | */ |
600 | if (!ISSET(HREAD4(sc, MMCHS_HCTL), MMCHS_HCTL_SDBP)) { |
601 | splx(s); |
602 | return (ENXIO); |
603 | } |
604 | |
605 | splx(s); |
606 | return (0); |
607 | } |
608 | |
609 | /* |
610 | * Return the smallest possible base clock frequency divisor value |
611 | * for the CLOCK_CTL register to produce `freq' (KHz). |
612 | */ |
613 | static int |
614 | ommmc_clock_divisor(struct ommmc_softc *sc, uint32_t freq) |
615 | { |
616 | int div; |
617 | uint32_t maxclk = MMCHS_SYSCTL_CLKD_MASK>>MMCHS_SYSCTL_CLKD_SH; |
618 | |
619 | for (div = 1; div <= maxclk; div++) |
620 | if ((sc->clkbase / div) <= freq) { |
621 | return (div); |
622 | } |
623 | |
624 | printf("divisor failure\n"); |
625 | /* No divisor found. */ |
626 | return (-1); |
627 | } |
628 | |
629 | /* |
630 | * Set or change SDCLK frequency or disable the SD clock. |
631 | * Return zero on success. |
632 | */ |
633 | int |
634 | ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) |
635 | { |
636 | int error = 0; |
637 | struct ommmc_softc *sc = sch; |
638 | uint32_t reg; |
639 | int s; |
640 | int div; |
641 | int timo; |
642 | |
643 | s = splsdmmc(); |
644 | |
645 | /* Must not stop the clock if commands are in progress. */ |
646 | for (timo = 1000; timo > 0; timo--) { |
647 | if (!ISSET(HREAD4(sc, MMCHS_PSTATE), |
648 | MMCHS_PSTATE_CMDI|MMCHS_PSTATE_DATI)) |
649 | break; |
650 | delay(10); |
651 | } |
652 | if (timo == 0) { |
653 | error = ETIMEDOUT; |
654 | goto ret; |
655 | } |
656 | |
657 | /* |
658 | * Stop SD clock before changing the frequency. |
659 | */ |
660 | HCLR4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN); |
661 | if (freq == SDMMC_SDCLK_OFF) |
662 | goto ret; |
663 | |
664 | /* |
665 | * Set the minimum base clock frequency divisor. |
666 | */ |
667 | if ((div = ommmc_clock_divisor(sc, freq)) < 0) { |
668 | /* Invalid base clock frequency or `freq' value. */ |
669 | error = EINVAL; |
670 | goto ret; |
671 | } |
672 | reg = HREAD4(sc, MMCHS_SYSCTL); |
673 | reg &= ~MMCHS_SYSCTL_CLKD_MASK; |
674 | reg |= div << MMCHS_SYSCTL_CLKD_SH; |
675 | HWRITE4(sc, MMCHS_SYSCTL, reg); |
676 | |
677 | /* |
678 | * Start internal clock. Wait 10ms for stabilization. |
679 | */ |
680 | HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_ICE); |
681 | for (timo = 1000; timo > 0; timo--) { |
682 | if (ISSET(HREAD4(sc, MMCHS_SYSCTL), MMCHS_SYSCTL_ICS)) |
683 | break; |
684 | delay(10); |
685 | } |
686 | if (timo == 0) { |
687 | error = ETIMEDOUT; |
688 | goto ret; |
689 | } |
690 | |
691 | /* |
692 | * Enable SD clock. |
693 | */ |
694 | HSET4(sc, MMCHS_SYSCTL, MMCHS_SYSCTL_CEN); |
695 | ret: |
696 | splx(s); |
697 | return (error); |
698 | } |
699 | |
700 | int |
701 | ommmc_bus_width(sdmmc_chipset_handle_t sch, int width) |
702 | { |
703 | struct ommmc_softc *sc = sch; |
704 | int s; |
705 | |
706 | if (width != 1 && width != 4 && width != 8) |
707 | return (1); |
708 | |
709 | s = splsdmmc(); |
710 | |
711 | if (width == 8) |
712 | HSET4(sc, MMCHS_CON, MMCHS_CON_DW8); |
713 | else |
714 | HCLR4(sc, MMCHS_CON, MMCHS_CON_DW8); |
715 | |
716 | if (width == 4) |
717 | HSET4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); |
718 | else if (width == 1) |
719 | HCLR4(sc, MMCHS_HCTL, MMCHS_HCTL_DTW); |
720 | |
721 | splx(s); |
722 | |
723 | return (0); |
724 | } |
725 | |
726 | void |
727 | ommmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) |
728 | { |
729 | /* - this is SDIO card interrupt */ |
730 | struct ommmc_softc *sc = sch; |
731 | |
732 | if (enable) { |
733 | HSET4(sc, MMCHS_IE, MMCHS_STAT_CIRQ); |
734 | HSET4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ); |
735 | } else { |
736 | HCLR4(sc, MMCHS_IE, MMCHS_STAT_CIRQ); |
737 | HCLR4(sc, MMCHS_ISE, MMCHS_STAT_CIRQ); |
738 | } |
739 | } |
740 | |
741 | void |
742 | ommmc_card_intr_ack(sdmmc_chipset_handle_t sch) |
743 | { |
744 | struct ommmc_softc *sc = sch; |
745 | |
746 | HWRITE4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ); |
747 | } |
748 | |
749 | int |
750 | ommmc_wait_state(struct ommmc_softc *sc, uint32_t mask, uint32_t value) |
751 | { |
752 | uint32_t state; |
753 | int timeout; |
754 | |
755 | state = HREAD4(sc, MMCHS_PSTATE); |
756 | DPRINTF(3,("%s: wait_state %x %x %x(state=%b)\n", DEVNAME(sc), |
757 | mask, value, state, state, MMCHS_PSTATE_FMT)); |
758 | for (timeout = 1000; timeout > 0; timeout--) { |
759 | if (((state = HREAD4(sc, MMCHS_PSTATE)) & mask) == value) |
760 | return (0); |
761 | delay(10); |
762 | } |
763 | DPRINTF(0,("%s: timeout waiting for %x (state=%b)\n", DEVNAME(sc), |
764 | value, state, MMCHS_PSTATE_FMT)); |
765 | return (ETIMEDOUT); |
766 | } |
767 | |
768 | void |
769 | ommmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) |
770 | { |
771 | struct ommmc_softc *sc = sch; |
772 | int error; |
773 | |
774 | /* |
775 | * Start the MMC command, or mark `cmd' as failed and return. |
776 | */ |
777 | error = ommmc_start_command(sc, cmd); |
778 | if (error != 0) { |
779 | cmd->c_error = error; |
780 | SET(cmd->c_flags, SCF_ITSDONE); |
781 | return; |
782 | } |
783 | |
784 | /* |
785 | * Wait until the command phase is done, or until the command |
786 | * is marked done for any other reason. |
787 | */ |
788 | if (!ommmc_wait_intr(sc, MMCHS_STAT_CC, SDHC_COMMAND_TIMEOUT)) { |
789 | cmd->c_error = ETIMEDOUT; |
790 | SET(cmd->c_flags, SCF_ITSDONE); |
791 | return; |
792 | } |
793 | |
794 | /* |
795 | * The host controller removes bits [0:7] from the response |
796 | * data (CRC) and we pass the data up unchanged to the bus |
797 | * driver (without padding). |
798 | */ |
799 | if (cmd->c_error == 0 && ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { |
800 | if (ISSET(cmd->c_flags, SCF_RSP_136)) { |
801 | uint32_t v0,v1,v2,v3; |
802 | v0 = HREAD4(sc, MMCHS_RSP10); |
803 | v1 = HREAD4(sc, MMCHS_RSP32); |
804 | v2 = HREAD4(sc, MMCHS_RSP54); |
805 | v3 = HREAD4(sc, MMCHS_RSP76); |
806 | |
807 | cmd->c_resp[0] = (v0 >> 8) | ((v1 & 0xff) << 24); |
808 | cmd->c_resp[1] = (v1 >> 8) | ((v2 & 0xff) << 24); |
809 | cmd->c_resp[2] = (v2 >> 8) | ((v3 & 0xff) << 24); |
810 | cmd->c_resp[3] = v3 >> 8; |
811 | #ifdef SDHC_DEBUG |
812 | printf("resp[0] 0x%08x\nresp[1] 0x%08x\nresp[2] 0x%08x\nresp[3] 0x%08x\n", cmd->c_resp[0], cmd->c_resp[1], cmd->c_resp[2], cmd->c_resp[3]); |
813 | #endif |
814 | } else { |
815 | cmd->c_resp[0] = HREAD4(sc, MMCHS_RSP10); |
816 | #ifdef SDHC_DEBUG |
817 | printf("resp[0] 0x%08x\n", cmd->c_resp[0]); |
818 | #endif |
819 | } |
820 | } |
821 | |
822 | /* |
823 | * If the command has data to transfer in any direction, |
824 | * execute the transfer now. |
825 | */ |
826 | if (cmd->c_error == 0 && cmd->c_data != NULL) |
827 | ommmc_transfer_data(sc, cmd); |
828 | |
829 | #if 0 |
830 | /* Turn off the LED. */ |
831 | HCLR1(sc, SDHC_HOST_CTL, SDHC_LED_ON); |
832 | #endif |
833 | |
834 | DPRINTF(1,("%s: cmd %u done (flags=%#x error=%d)\n", |
835 | DEVNAME(sc), cmd->c_opcode, cmd->c_flags, cmd->c_error)); |
836 | SET(cmd->c_flags, SCF_ITSDONE); |
837 | } |
838 | |
839 | int |
840 | ommmc_start_command(struct ommmc_softc *sc, struct sdmmc_command *cmd) |
841 | { |
842 | uint32_t blksize = 0; |
843 | uint32_t blkcount = 0; |
844 | uint32_t command; |
845 | int error; |
846 | int s; |
847 | |
848 | DPRINTF(1,("%s: start cmd %u arg=%#x data=%p dlen=%d flags=%#x " |
849 | "proc=\"%s\"\n", DEVNAME(sc), cmd->c_opcode, cmd->c_arg, |
850 | cmd->c_data, cmd->c_datalen, cmd->c_flags, curproc ? |
851 | curproc->p_comm : "")); |
852 | |
853 | /* |
854 | * The maximum block length for commands should be the minimum |
855 | * of the host buffer size and the card buffer size. (1.7.2) |
856 | */ |
857 | |
858 | /* Fragment the data into proper blocks. */ |
859 | if (cmd->c_datalen > 0) { |
860 | blksize = MIN(cmd->c_datalen, cmd->c_blklen); |
861 | blkcount = cmd->c_datalen / blksize; |
862 | if (cmd->c_datalen % blksize > 0) { |
863 | /* XXX: Split this command. (1.7.4) */ |
864 | printf("%s: data not a multiple of %d bytes\n", |
865 | DEVNAME(sc), blksize); |
866 | return (EINVAL); |
867 | } |
868 | } |
869 | |
870 | /* Check limit imposed by 9-bit block count. (1.7.2) */ |
871 | if (blkcount > MMCHS_BLK_NBLK_MAX) { |
872 | printf("%s: too much data\n", DEVNAME(sc)); |
873 | return (EINVAL); |
874 | } |
875 | |
876 | /* Prepare transfer mode register value. (2.2.5) */ |
877 | command = 0; |
878 | if (ISSET(cmd->c_flags, SCF_CMD_READ)) |
879 | command |= MMCHS_CMD_DDIR; |
880 | if (blkcount > 0) { |
881 | command |= MMCHS_CMD_BCE; |
882 | if (blkcount > 1) { |
883 | command |= MMCHS_CMD_MSBS; |
884 | /* XXX only for memory commands? */ |
885 | command |= MMCHS_CMD_ACEN; |
886 | } |
887 | } |
888 | #ifdef notyet |
889 | if (ISSET(sc->flags, SHF_USE_DMA)) |
890 | command |= MMCHS_CMD_DE; |
891 | #endif |
892 | |
893 | /* |
894 | * Prepare command register value. (2.2.6) |
895 | */ |
896 | command |= (cmd->c_opcode << MMCHS_CMD_INDX_SHIFT) & |
897 | MMCHS_CMD_INDX_SHIFT_MASK; |
898 | |
899 | if (ISSET(cmd->c_flags, SCF_RSP_CRC)) |
900 | command |= MMCHS_CMD_CCCE; |
901 | if (ISSET(cmd->c_flags, SCF_RSP_IDX)) |
902 | command |= MMCHS_CMD_CICE; |
903 | if (cmd->c_data != NULL) |
904 | command |= MMCHS_CMD_DP; |
905 | |
906 | if (!ISSET(cmd->c_flags, SCF_RSP_PRESENT)) |
907 | command |= MMCHS_CMD_RESP_NONE; |
908 | else if (ISSET(cmd->c_flags, SCF_RSP_136)) |
909 | command |= MMCHS_CMD_RESP136; |
910 | else if (ISSET(cmd->c_flags, SCF_RSP_BSY)) |
911 | command |= MMCHS_CMD_RESP48B; |
912 | else |
913 | command |= MMCHS_CMD_RESP48; |
914 | |
915 | /* Wait until command and data inhibit bits are clear. (1.5) */ |
916 | if ((error = ommmc_wait_state(sc, MMCHS_PSTATE_CMDI, 0)) != 0) |
917 | return (error); |
918 | |
919 | s = splsdmmc(); |
920 | |
921 | #if 0 |
922 | /* Alert the user not to remove the card. */ |
923 | HSET1(sc, SDHC_HOST_CTL, SDHC_LED_ON); |
924 | #endif |
925 | |
926 | /* XXX: Set DMA start address if SHF_USE_DMA is set. */ |
927 | |
928 | DPRINTF(1,("%s: cmd=%#x blksize=%d blkcount=%d\n", |
929 | DEVNAME(sc), command, blksize, blkcount)); |
930 | |
931 | /* |
932 | * Start a CPU data transfer. Writing to the high order byte |
933 | * of the SDHC_COMMAND register triggers the SD command. (1.5) |
934 | */ |
935 | HWRITE4(sc, MMCHS_BLK, (blkcount << MMCHS_BLK_NBLK_SHIFT) | |
936 | (blksize << MMCHS_BLK_BLEN_SHIFT)); |
937 | HWRITE4(sc, MMCHS_ARG, cmd->c_arg); |
938 | HWRITE4(sc, MMCHS_CMD, command); |
939 | |
940 | splx(s); |
941 | return (0); |
942 | } |
943 | |
944 | void |
945 | ommmc_transfer_data(struct ommmc_softc *sc, struct sdmmc_command *cmd) |
946 | { |
947 | uint8_t *datap = cmd->c_data; |
948 | int i, datalen; |
949 | int mask; |
950 | int error; |
951 | |
952 | mask = ISSET(cmd->c_flags, SCF_CMD_READ) ? |
953 | MMCHS_PSTATE_BRE : MMCHS_PSTATE_BWE; |
954 | error = 0; |
955 | datalen = cmd->c_datalen; |
956 | |
957 | DPRINTF(1,("%s: resp=%#x datalen=%d\n", DEVNAME(sc), |
958 | MMC_R1(cmd->c_resp), datalen)); |
959 | |
960 | while (datalen > 0) { |
961 | if (!ommmc_wait_intr(sc, MMCHS_STAT_BRR| MMCHS_STAT_BWR, |
962 | SDHC_BUFFER_TIMEOUT)) { |
963 | error = ETIMEDOUT; |
964 | break; |
965 | } |
966 | |
967 | if ((error = ommmc_wait_state(sc, mask, mask)) != 0) |
968 | break; |
969 | |
970 | i = MIN(datalen, cmd->c_blklen); |
971 | if (ISSET(cmd->c_flags, SCF_CMD_READ)) |
972 | ommmc_read_data(sc, datap, i); |
973 | else |
974 | ommmc_write_data(sc, datap, i); |
975 | |
976 | datap += i; |
977 | datalen -= i; |
978 | } |
979 | |
980 | if (error == 0 && !ommmc_wait_intr(sc, MMCHS_STAT_TC, |
981 | SDHC_TRANSFER_TIMEOUT)) |
982 | error = ETIMEDOUT; |
983 | |
984 | if (error != 0) |
985 | cmd->c_error = error; |
986 | SET(cmd->c_flags, SCF_ITSDONE); |
987 | |
988 | DPRINTF(1,("%s: data transfer done (error=%d)\n", |
989 | DEVNAME(sc), cmd->c_error)); |
990 | } |
991 | |
992 | void |
993 | ommmc_read_data(struct ommmc_softc *sc, uint8_t *datap, int datalen) |
994 | { |
995 | while (datalen > 3) { |
996 | *(uint32_t *)datap = HREAD4(sc, MMCHS_DATA); |
997 | datap += 4; |
998 | datalen -= 4; |
999 | } |
1000 | if (datalen > 0) { |
1001 | uint32_t rv = HREAD4(sc, MMCHS_DATA); |
1002 | do { |
1003 | *datap++ = rv & 0xff; |
1004 | rv = rv >> 8; |
1005 | } while (--datalen > 0); |
1006 | } |
1007 | } |
1008 | |
1009 | void |
1010 | ommmc_write_data(struct ommmc_softc *sc, uint8_t *datap, int datalen) |
1011 | { |
1012 | while (datalen > 3) { |
1013 | DPRINTF(3,("%08x\n", *(uint32_t *)datap)); |
1014 | HWRITE4(sc, MMCHS_DATA, *((uint32_t *)datap)); |
1015 | datap += 4; |
1016 | datalen -= 4; |
1017 | } |
1018 | if (datalen > 0) { |
1019 | uint32_t rv = *datap++; |
1020 | if (datalen > 1) |
1021 | rv |= *datap++ << 8; |
1022 | if (datalen > 2) |
1023 | rv |= *datap++ << 16; |
1024 | DPRINTF(3,("rv %08x\n", rv)); |
1025 | HWRITE4(sc, MMCHS_DATA, rv); |
1026 | } |
1027 | } |
1028 | |
1029 | /* Prepare for another command. */ |
1030 | int |
1031 | ommmc_soft_reset(struct ommmc_softc *sc, int mask) |
1032 | { |
1033 | |
1034 | int timo; |
1035 | |
1036 | DPRINTF(1,("%s: software reset reg=%#x\n", DEVNAME(sc), mask)); |
1037 | |
1038 | HSET4(sc, MMCHS_SYSCTL, mask); |
1039 | delay(10); |
1040 | for (timo = 1000; timo > 0; timo--) { |
1041 | if (!ISSET(HREAD4(sc, MMCHS_SYSCTL), mask)) |
1042 | break; |
1043 | delay(10); |
1044 | } |
1045 | if (timo == 0) { |
1046 | DPRINTF(1,("%s: timeout reg=%#x\n", DEVNAME(sc), |
1047 | HREAD4(sc, MMCHS_SYSCTL))); |
1048 | return (ETIMEDOUT); |
1049 | } |
1050 | |
1051 | return (0); |
1052 | } |
1053 | |
1054 | int |
1055 | ommmc_wait_intr(struct ommmc_softc *sc, int mask, int timo) |
1056 | { |
1057 | int status; |
1058 | int s; |
1059 | |
1060 | mask |= MMCHS_STAT_ERRI; |
1061 | |
1062 | s = splsdmmc(); |
1063 | status = sc->intr_status & mask; |
1064 | while (status == 0) { |
1065 | if (tsleep(&sc->intr_status, PWAIT, "hcintr", timo) |
1066 | == EWOULDBLOCK) { |
1067 | status |= MMCHS_STAT_ERRI; |
1068 | break; |
1069 | } |
1070 | status = sc->intr_status & mask; |
1071 | } |
1072 | sc->intr_status &= ~status; |
1073 | |
1074 | DPRINTF(2,("%s: intr status %#x error %#x\n", DEVNAME(sc), status, |
1075 | sc->intr_error_status)); |
1076 | |
1077 | /* Command timeout has higher priority than command complete. */ |
1078 | if (ISSET(status, MMCHS_STAT_ERRI)) { |
1079 | sc->intr_error_status = 0; |
1080 | (void)ommmc_soft_reset(sc, MMCHS_SYSCTL_SRC|MMCHS_SYSCTL_SRD); |
1081 | status = 0; |
1082 | } |
1083 | |
1084 | splx(s); |
1085 | return (status); |
1086 | } |
1087 | |
1088 | /* |
1089 | * Established by attachment driver at interrupt priority IPL_SDMMC. |
1090 | */ |
1091 | int |
1092 | ommmc_intr(void *arg) |
1093 | { |
1094 | struct ommmc_softc *sc = arg; |
1095 | |
1096 | uint32_t status; |
1097 | |
1098 | /* Find out which interrupts are pending. */ |
1099 | status = HREAD4(sc, MMCHS_STAT); |
1100 | |
1101 | /* Acknowledge the interrupts we are about to handle. */ |
1102 | HWRITE4(sc, MMCHS_STAT, status); |
1103 | DPRINTF(2,("%s: interrupt status=%b\n", DEVNAME(sc), |
1104 | status, MMCHS_STAT_FMT)); |
1105 | |
1106 | /* |
1107 | * Service error interrupts. |
1108 | */ |
1109 | if (ISSET(status, MMCHS_STAT_ERRI)) { |
1110 | if (ISSET(status, MMCHS_STAT_CTO| |
1111 | MMCHS_STAT_DTO)) { |
1112 | sc->intr_status |= status; |
1113 | sc->intr_error_status |= status & 0xffff0000; |
1114 | wakeup(&sc->intr_status); |
1115 | } |
1116 | } |
1117 | |
1118 | #if 0 |
1119 | /* |
1120 | * Wake up the sdmmc event thread to scan for cards. |
1121 | */ |
1122 | if (ISSET(status, SDHC_CARD_REMOVAL|SDHC_CARD_INSERTION)) |
1123 | ommmc_needs_discover(sc->sdmmc); |
1124 | #endif |
1125 | |
1126 | /* |
1127 | * Wake up the blocking process to service command |
1128 | * related interrupt(s). |
1129 | */ |
1130 | if (ISSET(status, MMCHS_STAT_BRR| |
1131 | MMCHS_STAT_BWR|MMCHS_STAT_TC| |
1132 | MMCHS_STAT_CC)) { |
1133 | sc->intr_status |= status; |
1134 | wakeup(&sc->intr_status); |
1135 | } |
1136 | |
1137 | /* |
1138 | * Service SD card interrupts. |
1139 | */ |
1140 | if (ISSET(status, MMCHS_STAT_CIRQ)) { |
1141 | DPRINTF(0,("%s: card interrupt\n", DEVNAME(sc))); |
1142 | HCLR4(sc, MMCHS_STAT, MMCHS_STAT_CIRQ); |
1143 | sdmmc_card_intr(sc->sdmmc); |
1144 | } |
1145 | return 1; |
1146 | } |
1147 | |
1148 | #ifdef SDHC_DEBUG |
1149 | void |
1150 | ommmc_dump_regs(struct ommmc_softc *sc) |
1151 | { |
1152 | } |
1153 | #endif |