4 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca>
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. The name of the author may not be used to endorse or promote products
13 * derived from this software without specific prior written permission.
15 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
17 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
18 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
19 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
20 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
21 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
22 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
23 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD$");
31 #include <sys/param.h>
32 #include <sys/kernel.h>
33 #include <sys/systm.h>
34 #include <sys/errno.h>
35 #include <sys/ioctl.h>
36 #include <sys/syslog.h>
37 #include <sys/device.h>
42 #include <dev/sdmmc/sdmmcvar.h>
43 #include <dev/sdmmc/sdmmcchip.h>
44 #include <dev/sdmmc/sdmmc_ioreg.h>
46 #include <dev/isa/isavar.h>
47 #include <dev/isa/isadmavar.h>
49 #include <dev/ic/w83l518dreg.h>
50 #include <dev/ic/w83l518dvar.h>
51 #include <dev/ic/w83l518d_sdmmc.h>
53 /* #define WB_SDMMC_DEBUG */
56 static int wb_sdmmc_debug
= 1;
58 static int wb_sdmmc_debug
= 0;
61 #if defined(__NetBSD__) && __NetBSD_Version__ < 599000600
62 #define snprintb(b, l, f, v) bitmask_snprintf((v), (f), (b), (l))
65 #define REPORT(_wb, ...) \
66 if (wb_sdmmc_debug > 0) \
67 aprint_normal_dev(((struct wb_softc *)(_wb))->wb_dev, \
70 static int wb_sdmmc_host_reset(sdmmc_chipset_handle_t
);
71 static uint32_t wb_sdmmc_host_ocr(sdmmc_chipset_handle_t
);
72 static int wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t
);
73 static int wb_sdmmc_card_detect(sdmmc_chipset_handle_t
);
74 static int wb_sdmmc_write_protect(sdmmc_chipset_handle_t
);
75 static int wb_sdmmc_bus_power(sdmmc_chipset_handle_t
, uint32_t);
76 static int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t
, int);
77 static int wb_sdmmc_bus_width(sdmmc_chipset_handle_t
, int);
78 static void wb_sdmmc_exec_command(sdmmc_chipset_handle_t
,
79 struct sdmmc_command
*);
80 static void wb_sdmmc_card_enable_intr(sdmmc_chipset_handle_t
, int);
81 static void wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t
);
83 static struct sdmmc_chip_functions wb_sdmmc_chip_functions
= {
84 .host_reset
= wb_sdmmc_host_reset
,
85 .host_ocr
= wb_sdmmc_host_ocr
,
86 .host_maxblklen
= wb_sdmmc_host_maxblklen
,
87 .card_detect
= wb_sdmmc_card_detect
,
88 .write_protect
= wb_sdmmc_write_protect
,
89 .bus_power
= wb_sdmmc_bus_power
,
90 .bus_clock
= wb_sdmmc_bus_clock
,
91 .bus_width
= wb_sdmmc_bus_width
,
92 .exec_command
= wb_sdmmc_exec_command
,
93 .card_enable_intr
= wb_sdmmc_card_enable_intr
,
94 .card_intr_ack
= wb_sdmmc_card_intr_ack
,
98 wb_sdmmc_read_data(struct wb_softc
*wb
, uint8_t *data
, int len
)
100 bus_space_read_multi_1(wb
->wb_iot
, wb
->wb_ioh
, WB_SD_FIFO
, data
, len
);
104 wb_sdmmc_write_data(struct wb_softc
*wb
, uint8_t *data
, int len
)
106 bus_space_write_multi_1(wb
->wb_iot
, wb
->wb_ioh
, WB_SD_FIFO
, data
, len
);
110 wb_sdmmc_discover(void *opaque
)
112 struct wb_softc
*wb
= opaque
;
114 REPORT(wb
, "TRACE: discover(wb)\n");
116 sdmmc_needs_discover(wb
->wb_sdmmc_dev
);
120 wb_sdmmc_enable(struct wb_softc
*wb
)
124 REPORT(wb
, "TRACE: enable(wb)\n");
126 /* put the device in a known state */
127 wb_idx_write(wb
, WB_INDEX_SETUP
, WB_SETUP_SOFT_RST
);
128 while (--i
> 0 && wb_idx_read(wb
, WB_INDEX_SETUP
) & WB_SETUP_SOFT_RST
)
131 aprint_error_dev(wb
->wb_dev
, "timeout resetting device\n");
134 wb_idx_write(wb
, WB_INDEX_CLK
, WB_CLK_375K
);
135 wb_idx_write(wb
, WB_INDEX_FIFOEN
, 0);
136 wb_idx_write(wb
, WB_INDEX_DMA
, 0);
137 wb_idx_write(wb
, WB_INDEX_PBSMSB
, 0);
138 wb_idx_write(wb
, WB_INDEX_PBSLSB
, 0);
140 while ((wb_read(wb
, WB_SD_FIFOSTS
) & WB_FIFO_EMPTY
) == 0)
141 wb_read(wb
, WB_SD_FIFO
);
143 wb_write(wb
, WB_SD_CSR
, 0);
145 wb_write(wb
, WB_SD_INTCTL
, WB_INT_DEFAULT
);
147 wb_sdmmc_card_detect(wb
);
153 wb_sdmmc_disable(struct wb_softc
*wb
)
157 REPORT(wb
, "TRACE: disable(wb)\n");
159 val
= wb_read(wb
, WB_SD_CSR
);
160 val
|= WB_CSR_POWER_N
;
161 wb_write(wb
, WB_SD_CSR
, val
);
167 wb_sdmmc_attach(struct wb_softc
*wb
)
169 struct sdmmcbus_attach_args saa
;
171 callout_init(&wb
->wb_sdmmc_callout
, 0);
172 callout_setfunc(&wb
->wb_sdmmc_callout
, wb_sdmmc_discover
, wb
);
174 wb
->wb_sdmmc_width
= 1;
176 if (wb_sdmmc_enable(wb
) == false)
179 memset(&saa
, 0, sizeof(saa
));
180 saa
.saa_busname
= "sdmmc";
181 saa
.saa_sct
= &wb_sdmmc_chip_functions
;
183 saa
.saa_clkmin
= 375;
184 saa
.saa_clkmax
= 24000;
185 saa
.saa_caps
= SMC_CAPS_4BIT_MODE
;
187 wb
->wb_sdmmc_dev
= config_found(wb
->wb_dev
, &saa
, NULL
);
191 wb_sdmmc_detach(struct wb_softc
*wb
, int flags
)
195 if (wb
->wb_sdmmc_dev
) {
196 rv
= config_detach(wb
->wb_sdmmc_dev
, flags
);
200 wb_sdmmc_disable(wb
);
202 callout_halt(&wb
->wb_sdmmc_callout
, NULL
);
203 callout_destroy(&wb
->wb_sdmmc_callout
);
212 wb_sdmmc_host_reset(sdmmc_chipset_handle_t sch
)
214 REPORT(sch
, "TRACE: sdmmc/host_reset(wb)\n");
220 wb_sdmmc_host_ocr(sdmmc_chipset_handle_t sch
)
222 REPORT(sch
, "TRACE: sdmmc/host_ocr(wb)\n");
224 return MMC_OCR_3_2V_3_3V
| MMC_OCR_3_3V_3_4V
;
228 wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t sch
)
230 REPORT(sch
, "TRACE: sdmmc/host_maxblklen(wb)\n");
232 return 512; /* XXX */
236 wb_sdmmc_card_detect(sdmmc_chipset_handle_t sch
)
238 struct wb_softc
*wb
= sch
;
242 rv
= (wb_read(wb
, WB_SD_CSR
) & WB_CSR_CARD_PRESENT
) ? 1 : 0;
245 REPORT(wb
, "TRACE: sdmmc/card_detect(wb) -> %d\n", rv
);
251 wb_sdmmc_write_protect(sdmmc_chipset_handle_t sch
)
253 struct wb_softc
*wb
= sch
;
257 rv
= (wb_read(wb
, WB_SD_CSR
) & WB_CSR_WRITE_PROTECT
) ? 1 : 0;
260 REPORT(wb
, "TRACE: sdmmc/write_protect(wb) -> %d\n", rv
);
266 wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch
, uint32_t ocr
)
268 REPORT(sch
, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr
);
274 wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch
, int freq
)
276 struct wb_softc
*wb
= sch
;
279 REPORT(wb
, "TRACE: sdmmc/bus_clock(wb, freq=%d)\n", freq
);
283 else if (freq
>= 16000)
285 else if (freq
>= 12000)
290 if (wb_idx_read(wb
, WB_INDEX_CLK
) != clk
)
291 wb_idx_write(wb
, WB_INDEX_CLK
, clk
);
297 wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch
, int width
)
299 struct wb_softc
*wb
= sch
;
301 REPORT(wb
, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width
);
303 if (width
!= 1 && width
!= 4)
306 wb
->wb_sdmmc_width
= width
;
313 wb_sdmmc_rsp_read_long(struct wb_softc
*wb
, struct sdmmc_command
*cmd
)
315 uint8_t *p
= (uint8_t *)cmd
->c_resp
;
318 if (wb_idx_read(wb
, WB_INDEX_RESPLEN
) != 1) {
319 cmd
->c_error
= ENXIO
;
323 for (i
= 12; i
>= 0; i
-= 4) {
324 p
[3] = wb_idx_read(wb
, WB_INDEX_RESP(i
+ 0));
325 p
[2] = wb_idx_read(wb
, WB_INDEX_RESP(i
+ 1));
326 p
[1] = wb_idx_read(wb
, WB_INDEX_RESP(i
+ 2));
327 p
[0] = wb_idx_read(wb
, WB_INDEX_RESP(i
+ 3));
333 wb_sdmmc_rsp_read_short(struct wb_softc
*wb
, struct sdmmc_command
*cmd
)
335 uint8_t *p
= (uint8_t *)cmd
->c_resp
;
337 if (wb_idx_read(wb
, WB_INDEX_RESPLEN
) != 0) {
338 cmd
->c_error
= ENXIO
;
342 p
[3] = wb_idx_read(wb
, WB_INDEX_RESP(12));
343 p
[2] = wb_idx_read(wb
, WB_INDEX_RESP(13));
344 p
[1] = wb_idx_read(wb
, WB_INDEX_RESP(14));
345 p
[0] = wb_idx_read(wb
, WB_INDEX_RESP(15));
349 wb_sdmmc_transfer_data(struct wb_softc
*wb
, struct sdmmc_command
*cmd
)
352 int datalen
, retry
= 5000;
354 if (wb
->wb_sdmmc_intsts
& WB_INT_CARD
)
357 fifosts
= wb_read(wb
, WB_SD_FIFOSTS
);
358 if (ISSET(cmd
->c_flags
, SCF_CMD_READ
)) {
359 if (fifosts
& WB_FIFO_EMPTY
) {
360 while (--retry
> 0) {
361 fifosts
= wb_read(wb
, WB_SD_FIFOSTS
);
362 if ((fifosts
& WB_FIFO_EMPTY
) == 0)
370 if (fifosts
& WB_FIFO_FULL
)
373 datalen
= fifosts
& WB_FIFO_DEPTH_MASK
;
375 if (fifosts
& WB_FIFO_FULL
) {
376 while (--retry
> 0) {
377 fifosts
= wb_read(wb
, WB_SD_FIFOSTS
);
378 if ((fifosts
& WB_FIFO_FULL
) == 0)
386 if (fifosts
& WB_FIFO_EMPTY
)
389 datalen
= 16 - (fifosts
& WB_FIFO_DEPTH_MASK
);
392 datalen
= MIN(datalen
, cmd
->c_resid
);
394 if (ISSET(cmd
->c_flags
, SCF_CMD_READ
))
395 wb_sdmmc_read_data(wb
, cmd
->c_buf
, datalen
);
397 wb_sdmmc_write_data(wb
, cmd
->c_buf
, datalen
);
399 cmd
->c_buf
+= datalen
;
400 cmd
->c_resid
-= datalen
;
407 wb_sdmmc_exec_command(sdmmc_chipset_handle_t sch
, struct sdmmc_command
*cmd
)
409 static const int opcodes
[] = {
410 11, 17, 18, 20, 24, 25, 26, 27, 30, 42, 51, 56
412 struct wb_softc
*wb
= sch
;
419 REPORT(wb
, "TRACE: sdmmc/exec_command(wb, cmd) "
420 "opcode %d flags 0x%x data %p datalen %d\n",
421 cmd
->c_opcode
, cmd
->c_flags
, cmd
->c_data
, cmd
->c_datalen
);
423 if (cmd
->c_datalen
> 0) {
424 /* controller only supports a select number of data opcodes */
425 for (i
= 0; i
< __arraycount(opcodes
); i
++)
426 if (opcodes
[i
] == cmd
->c_opcode
)
428 if (i
== __arraycount(opcodes
)) {
429 cmd
->c_error
= EINVAL
;
433 /* Fragment the data into proper blocks */
434 blklen
= MIN(cmd
->c_datalen
, cmd
->c_blklen
);
436 if (cmd
->c_datalen
% blklen
> 0) {
437 aprint_error_dev(wb
->wb_dev
,
438 "data is not a multiple of %u bytes\n", blklen
);
439 cmd
->c_error
= EINVAL
;
443 /* setup block size registers */
444 blklen
= blklen
+ 2 * wb
->wb_sdmmc_width
;
445 wb_idx_write(wb
, WB_INDEX_PBSMSB
,
446 ((blklen
>> 4) & 0xf0) | (wb
->wb_sdmmc_width
/ 4));
447 wb_idx_write(wb
, WB_INDEX_PBSLSB
, blklen
& 0xff);
450 val
= wb_idx_read(wb
, WB_INDEX_SETUP
);
451 val
|= WB_SETUP_FIFO_RST
;
452 wb_idx_write(wb
, WB_INDEX_SETUP
, val
);
453 while (wb_idx_read(wb
, WB_INDEX_SETUP
) & WB_SETUP_FIFO_RST
)
456 cmd
->c_resid
= cmd
->c_datalen
;
457 cmd
->c_buf
= cmd
->c_data
;
459 /* setup FIFO thresholds */
460 if (ISSET(cmd
->c_flags
, SCF_CMD_READ
))
461 wb_idx_write(wb
, WB_INDEX_FIFOEN
, WB_FIFOEN_FULL
| 8);
463 wb_idx_write(wb
, WB_INDEX_FIFOEN
, WB_FIFOEN_EMPTY
| 8);
465 /* pre-fill the FIFO on write */
466 error
= wb_sdmmc_transfer_data(wb
, cmd
);
468 cmd
->c_error
= error
;
475 wb
->wb_sdmmc_intsts
= 0;
476 wb_write(wb
, WB_SD_COMMAND
, cmd
->c_opcode
);
477 wb_write(wb
, WB_SD_COMMAND
, (cmd
->c_arg
>> 24) & 0xff);
478 wb_write(wb
, WB_SD_COMMAND
, (cmd
->c_arg
>> 16) & 0xff);
479 wb_write(wb
, WB_SD_COMMAND
, (cmd
->c_arg
>> 8) & 0xff);
480 wb_write(wb
, WB_SD_COMMAND
, (cmd
->c_arg
>> 0) & 0xff);
484 while (wb_idx_read(wb
, WB_INDEX_STATUS
) & WB_STATUS_CARD_TRAFFIC
) {
489 if (wb_idx_read(wb
, WB_INDEX_STATUS
) & WB_STATUS_CARD_TRAFFIC
) {
491 "command timed out, WB_INDEX_STATUS = 0x%02x\n",
492 wb_idx_read(wb
, WB_INDEX_STATUS
));
493 cmd
->c_error
= ETIMEDOUT
;
497 if (ISSET(cmd
->c_flags
, SCF_RSP_PRESENT
)) {
498 if (wb
->wb_sdmmc_intsts
& WB_INT_TIMEOUT
) {
499 cmd
->c_error
= ETIMEDOUT
;
503 if (ISSET(cmd
->c_flags
, SCF_RSP_136
))
504 wb_sdmmc_rsp_read_long(wb
, cmd
);
506 wb_sdmmc_rsp_read_short(wb
, cmd
);
509 if (cmd
->c_error
== 0 && cmd
->c_datalen
> 0) {
511 while (cmd
->c_resid
> 0) {
512 error
= wb_sdmmc_transfer_data(wb
, cmd
);
514 cmd
->c_error
= error
;
522 SET(cmd
->c_flags
, SCF_ITSDONE
);
526 "cmd error = %d, op = %d [%s] "
527 "blklen %d datalen %d resid %d\n",
528 cmd
->c_error
, cmd
->c_opcode
,
529 ISSET(cmd
->c_flags
, SCF_CMD_READ
) ? "rd" : "wr",
530 cmd
->c_blklen
, cmd
->c_datalen
, cmd
->c_resid
);
535 wb_sdmmc_card_enable_intr(sdmmc_chipset_handle_t sch
, int enable
)
537 REPORT(sch
, "TRACE: sdmmc/card_enable_intr(wb, enable=%d)\n", enable
);
541 wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t sch
)
543 REPORT(sch
, "TRACE: sdmmc/card_intr_ack(wb)\n");
550 wb_sdmmc_intr(struct wb_softc
*wb
)
554 val
= wb_read(wb
, WB_SD_INTSTS
);
555 if (val
== 0xff || val
== 0x00)
558 if (wb
->wb_sdmmc_dev
== NULL
)
561 wb
->wb_sdmmc_intsts
|= val
;
563 if (wb_sdmmc_debug
) {
565 snprintb(buf
, sizeof(buf
),
566 "\20\1TC\2BUSYEND\3PROGEND\4TIMEOUT"
567 "\5CRC\6FIFO\7CARD\010PENDING",
569 REPORT(wb
, "WB_SD_INTSTS = %s\n", buf
);
572 if (val
& WB_INT_CARD
)
573 callout_schedule(&wb
->wb_sdmmc_callout
, hz
/ 4);