1 /* $OpenBSD: w83l518d_sdmmc.c,v 1.5 2020/01/22 03:26:02 cheloha Exp $ */ 2 /* $NetBSD: w83l518d_sdmmc.c,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */ 3 4 /* 5 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 6 * All rights reserved. 7 * 8 * Redistribution and use in source and binary forms, with or without 9 * modification, are permitted provided that the following conditions 10 * are met: 11 * 1. Redistributions of source code must retain the above copyright 12 * notice, this list of conditions and the following disclaimer. 13 * 2. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR 17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. 19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, 20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, 21 * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 22 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED 23 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 24 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 #include <sys/param.h> 30 #include <sys/kernel.h> 31 #include <sys/systm.h> 32 #include <sys/errno.h> 33 #include <sys/ioctl.h> 34 #include <sys/syslog.h> 35 #include <sys/device.h> 36 #include <sys/proc.h> 37 38 #include <machine/bus.h> 39 40 #include <dev/sdmmc/sdmmcvar.h> 41 #include <dev/sdmmc/sdmmcchip.h> 42 #include <dev/sdmmc/sdmmc_ioreg.h> 43 44 #include <dev/ic/w83l518dreg.h> 45 #include <dev/ic/w83l518dvar.h> 46 #include <dev/ic/w83l518d_sdmmc.h> 47 48 /* #define WB_SDMMC_DEBUG */ 49 50 #ifdef WB_SDMMC_DEBUG 51 static int wb_sdmmc_debug = 1; 52 #else 53 static int wb_sdmmc_debug = 0; 54 #endif 55 56 #define REPORT(_wb, ...) \ 57 if (wb_sdmmc_debug > 0) \ 58 printf(__VA_ARGS__) 59 60 int wb_sdmmc_host_reset(sdmmc_chipset_handle_t); 61 uint32_t wb_sdmmc_host_ocr(sdmmc_chipset_handle_t); 62 int wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t); 63 int wb_sdmmc_card_detect(sdmmc_chipset_handle_t); 64 #ifdef notyet 65 int wb_sdmmc_write_protect(sdmmc_chipset_handle_t); 66 #endif 67 int wb_sdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t); 68 int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t, int, int); 69 int wb_sdmmc_bus_width(sdmmc_chipset_handle_t, int); 70 void wb_sdmmc_exec_command(sdmmc_chipset_handle_t, 71 struct sdmmc_command *); 72 void wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t, int); 73 void wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t); 74 75 struct sdmmc_chip_functions wb_sdmmc_chip_functions = { 76 /* host controller reset */ 77 wb_sdmmc_host_reset, 78 /* host controlle capabilities */ 79 wb_sdmmc_host_ocr, 80 wb_sdmmc_host_maxblklen, 81 /* card detection */ 82 wb_sdmmc_card_detect, 83 #ifdef notyet 84 .write_protect = wb_sdmmc_write_protect, 85 #endif 86 /* bus power and clock frequency */ 87 wb_sdmmc_bus_power, 88 wb_sdmmc_bus_clock, 89 wb_sdmmc_bus_width, 90 /* command execution */ 91 wb_sdmmc_exec_command, 92 /* card interrupt */ 93 wb_sdmmc_card_intr_mask, 94 wb_sdmmc_card_intr_ack 95 }; 96 97 void wb_sdmmc_read_data(struct wb_softc *, uint8_t *, int); 98 void wb_sdmmc_write_data(struct wb_softc *, uint8_t *, int); 99 void wb_sdmmc_discover(void *); 100 int wb_sdmmc_enable(struct wb_softc *); 101 int wb_sdmmc_disable(struct wb_softc *); 102 int wb_sdmmc_transfer_data(struct wb_softc *, struct sdmmc_command *); 103 void wb_sdmmc_rsp_read_long(struct wb_softc *, struct sdmmc_command *); 104 void wb_sdmmc_rsp_read_short(struct wb_softc *, struct sdmmc_command *); 105 106 void 107 wb_sdmmc_read_data(struct wb_softc *wb, uint8_t *data, int len) 108 { 109 bus_space_read_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 110 } 111 112 void 113 wb_sdmmc_write_data(struct wb_softc *wb, uint8_t *data, int len) 114 { 115 bus_space_write_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 116 } 117 118 void 119 wb_sdmmc_discover(void *opaque) 120 { 121 struct wb_softc *wb = opaque; 122 123 REPORT(wb, "TRACE: discover(wb)\n"); 124 125 sdmmc_needs_discover(wb->wb_sdmmc_dev); 126 } 127 128 int 129 wb_sdmmc_enable(struct wb_softc *wb) 130 { 131 int i = 5000; 132 133 REPORT(wb, "TRACE: enable(wb)\n"); 134 135 /* put the device in a known state */ 136 wb_idx_write(wb, WB_INDEX_SETUP, WB_SETUP_SOFT_RST); 137 while (--i > 0 && wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_SOFT_RST) 138 delay(10); 139 if (i == 0) { 140 printf("%s: timeout resetting device\n", wb->wb_dev.dv_xname); 141 return 0; 142 } 143 wb_idx_write(wb, WB_INDEX_CLK, WB_CLK_375K); 144 wb_idx_write(wb, WB_INDEX_FIFOEN, 0); 145 wb_idx_write(wb, WB_INDEX_DMA, 0); 146 wb_idx_write(wb, WB_INDEX_PBSMSB, 0); 147 wb_idx_write(wb, WB_INDEX_PBSLSB, 0); 148 /* drain FIFO */ 149 while ((wb_read(wb, WB_SD_FIFOSTS) & WB_FIFO_EMPTY) == 0) 150 wb_read(wb, WB_SD_FIFO); 151 152 wb_write(wb, WB_SD_CSR, 0); 153 154 wb_write(wb, WB_SD_INTCTL, WB_INT_DEFAULT); 155 156 wb_sdmmc_card_detect(wb); 157 158 return 1; 159 } 160 161 int 162 wb_sdmmc_disable(struct wb_softc *wb) 163 { 164 uint8_t val; 165 166 REPORT(wb, "TRACE: disable(wb)\n"); 167 168 val = wb_read(wb, WB_SD_CSR); 169 val |= WB_CSR_POWER_N; 170 wb_write(wb, WB_SD_CSR, val); 171 172 return 1; 173 } 174 175 void 176 wb_sdmmc_attach(struct wb_softc *wb) 177 { 178 struct sdmmcbus_attach_args saa; 179 180 timeout_set(&wb->wb_sdmmc_to, wb_sdmmc_discover, wb); 181 182 wb->wb_sdmmc_width = 1; 183 184 if (wb_sdmmc_enable(wb) == 0) 185 return; 186 187 memset(&saa, 0, sizeof(saa)); 188 saa.saa_busname = "sdmmc"; 189 saa.sct = &wb_sdmmc_chip_functions; 190 saa.sch = wb; 191 saa.flags = SMF_STOP_AFTER_MULTIPLE; 192 193 wb->wb_sdmmc_dev = config_found(&wb->wb_dev, &saa, NULL); 194 } 195 196 int 197 wb_sdmmc_detach(struct wb_softc *wb, int flags) 198 { 199 int rv; 200 201 if (wb->wb_sdmmc_dev) { 202 rv = config_detach(wb->wb_sdmmc_dev, flags); 203 if (rv) 204 return rv; 205 } 206 wb_sdmmc_disable(wb); 207 208 timeout_del(&wb->wb_sdmmc_to); 209 210 return 0; 211 } 212 213 /* 214 * SD/MMC interface 215 */ 216 int 217 wb_sdmmc_host_reset(sdmmc_chipset_handle_t sch) 218 { 219 REPORT(sch, "TRACE: sdmmc/host_reset(wb)\n"); 220 221 return 0; 222 } 223 224 uint32_t 225 wb_sdmmc_host_ocr(sdmmc_chipset_handle_t sch) 226 { 227 REPORT(sch, "TRACE: sdmmc/host_ocr(wb)\n"); 228 229 return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; 230 } 231 232 int 233 wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t sch) 234 { 235 REPORT(sch, "TRACE: sdmmc/host_maxblklen(wb)\n"); 236 237 return 512; /* XXX */ 238 } 239 240 int 241 wb_sdmmc_card_detect(sdmmc_chipset_handle_t sch) 242 { 243 struct wb_softc *wb = sch; 244 int rv; 245 246 wb_led(wb, 1); 247 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_CARD_PRESENT) ? 1 : 0; 248 wb_led(wb, 0); 249 250 REPORT(wb, "TRACE: sdmmc/card_detect(wb) -> %d\n", rv); 251 252 return rv; 253 } 254 255 #ifdef notyet 256 int 257 wb_sdmmc_write_protect(sdmmc_chipset_handle_t sch) 258 { 259 struct wb_softc *wb = sch; 260 int rv; 261 262 wb_led(wb, 1); 263 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_WRITE_PROTECT) ? 1 : 0; 264 wb_led(wb, 0); 265 266 REPORT(wb, "TRACE: sdmmc/write_protect(wb) -> %d\n", rv); 267 268 return rv; 269 } 270 #endif 271 272 int 273 wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) 274 { 275 REPORT(sch, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr); 276 277 return 0; 278 } 279 280 int 281 wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing) 282 { 283 struct wb_softc *wb = sch; 284 uint8_t clk; 285 286 REPORT(wb, "TRACE: sdmmc/bus_clock(wb, freq=%d)\n", freq); 287 288 if (freq >= 24000) 289 clk = WB_CLK_24M; 290 else if (freq >= 16000) 291 clk = WB_CLK_16M; 292 else if (freq >= 12000) 293 clk = WB_CLK_12M; 294 else 295 clk = WB_CLK_375K; 296 297 if (wb_idx_read(wb, WB_INDEX_CLK) != clk) 298 wb_idx_write(wb, WB_INDEX_CLK, clk); 299 300 return 0; 301 } 302 303 int 304 wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) 305 { 306 struct wb_softc *wb = sch; 307 308 REPORT(wb, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width); 309 310 if (width != 1 && width != 4) 311 return 1; 312 313 wb->wb_sdmmc_width = width; 314 315 return 0; 316 } 317 318 void 319 wb_sdmmc_rsp_read_long(struct wb_softc *wb, struct sdmmc_command *cmd) 320 { 321 uint8_t *p = (uint8_t *)cmd->c_resp; 322 int i; 323 324 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 1) { 325 cmd->c_error = ENXIO; 326 return; 327 } 328 329 for (i = 12; i >= 0; i -= 4) { 330 p[0] = wb_idx_read(wb, WB_INDEX_RESP(i + 0)); 331 p[1] = wb_idx_read(wb, WB_INDEX_RESP(i + 1)); 332 p[2] = wb_idx_read(wb, WB_INDEX_RESP(i + 2)); 333 p[3] = wb_idx_read(wb, WB_INDEX_RESP(i + 3)); 334 p += 4; 335 } 336 } 337 338 void 339 wb_sdmmc_rsp_read_short(struct wb_softc *wb, struct sdmmc_command *cmd) 340 { 341 uint8_t *p = (uint8_t *)cmd->c_resp; 342 343 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 0) { 344 cmd->c_error = ENXIO; 345 return; 346 } 347 348 #if BYTE_ORDER == LITTLE_ENDIAN 349 p[3] = wb_idx_read(wb, WB_INDEX_RESP(12)); 350 p[2] = wb_idx_read(wb, WB_INDEX_RESP(13)); 351 p[1] = wb_idx_read(wb, WB_INDEX_RESP(14)); 352 p[0] = wb_idx_read(wb, WB_INDEX_RESP(15)); 353 #else 354 p[0] = wb_idx_read(wb, WB_INDEX_RESP(12)); 355 p[1] = wb_idx_read(wb, WB_INDEX_RESP(13)); 356 p[2] = wb_idx_read(wb, WB_INDEX_RESP(14)); 357 p[3] = wb_idx_read(wb, WB_INDEX_RESP(15)); 358 #endif 359 } 360 361 int 362 wb_sdmmc_transfer_data(struct wb_softc *wb, struct sdmmc_command *cmd) 363 { 364 uint8_t fifosts; 365 int datalen, retry = 5000; 366 367 if (wb->wb_sdmmc_intsts & WB_INT_CARD) 368 return EIO; 369 370 fifosts = wb_read(wb, WB_SD_FIFOSTS); 371 if (ISSET(cmd->c_flags, SCF_CMD_READ)) { 372 if (fifosts & WB_FIFO_EMPTY) { 373 while (--retry > 0) { 374 fifosts = wb_read(wb, WB_SD_FIFOSTS); 375 if ((fifosts & WB_FIFO_EMPTY) == 0) 376 break; 377 delay(100); 378 } 379 if (retry == 0) 380 return EBUSY; 381 } 382 383 if (fifosts & WB_FIFO_FULL) 384 datalen = 16; 385 else 386 datalen = fifosts & WB_FIFO_DEPTH_MASK; 387 } else { 388 if (fifosts & WB_FIFO_FULL) { 389 while (--retry > 0) { 390 fifosts = wb_read(wb, WB_SD_FIFOSTS); 391 if ((fifosts & WB_FIFO_FULL) == 0) 392 break; 393 delay(100); 394 } 395 if (retry == 0) 396 return EBUSY; 397 } 398 399 if (fifosts & WB_FIFO_EMPTY) 400 datalen = 16; 401 else 402 datalen = 16 - (fifosts & WB_FIFO_DEPTH_MASK); 403 } 404 405 datalen = MIN(datalen, cmd->c_resid); 406 if (datalen > 0) { 407 if (ISSET(cmd->c_flags, SCF_CMD_READ)) 408 wb_sdmmc_read_data(wb, cmd->c_buf, datalen); 409 else 410 wb_sdmmc_write_data(wb, cmd->c_buf, datalen); 411 412 cmd->c_buf += datalen; 413 cmd->c_resid -= datalen; 414 } 415 416 return 0; 417 } 418 419 void 420 wb_sdmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) 421 { 422 static const int opcodes[] = { 423 11, 17, 18, 20, 24, 25, 26, 27, 30, 42, 51, 56 424 }; 425 struct wb_softc *wb = sch; 426 uint8_t val; 427 int blklen; 428 int error; 429 int i, retry; 430 int s; 431 432 REPORT(wb, "TRACE: sdmmc/exec_command(wb, cmd) " 433 "opcode %d flags 0x%x data %p datalen %d\n", 434 cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen); 435 436 if (cmd->c_datalen > 0) { 437 /* controller only supports a select number of data opcodes */ 438 for (i = 0; i < nitems(opcodes); i++) 439 if (opcodes[i] == cmd->c_opcode) 440 break; 441 if (i == nitems(opcodes)) { 442 cmd->c_error = EINVAL; 443 goto done; 444 } 445 446 /* Fragment the data into proper blocks */ 447 blklen = MIN(cmd->c_datalen, cmd->c_blklen); 448 449 if (cmd->c_datalen % blklen > 0) { 450 printf("%s: data is not a multiple of %u bytes\n", 451 wb->wb_dev.dv_xname, blklen); 452 cmd->c_error = EINVAL; 453 goto done; 454 } 455 456 /* setup block size registers */ 457 blklen = blklen + 2 * wb->wb_sdmmc_width; 458 wb_idx_write(wb, WB_INDEX_PBSMSB, 459 ((blklen >> 4) & 0xf0) | (wb->wb_sdmmc_width / 4)); 460 wb_idx_write(wb, WB_INDEX_PBSLSB, blklen & 0xff); 461 462 /* clear FIFO */ 463 val = wb_idx_read(wb, WB_INDEX_SETUP); 464 val |= WB_SETUP_FIFO_RST; 465 wb_idx_write(wb, WB_INDEX_SETUP, val); 466 while (wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_FIFO_RST) 467 ; 468 469 cmd->c_resid = cmd->c_datalen; 470 cmd->c_buf = cmd->c_data; 471 472 /* setup FIFO thresholds */ 473 if (ISSET(cmd->c_flags, SCF_CMD_READ)) 474 wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_FULL | 8); 475 else { 476 wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_EMPTY | 8); 477 478 /* pre-fill the FIFO on write */ 479 error = wb_sdmmc_transfer_data(wb, cmd); 480 if (error) { 481 cmd->c_error = error; 482 goto done; 483 } 484 } 485 } 486 487 s = splsdmmc(); 488 wb->wb_sdmmc_intsts = 0; 489 wb_write(wb, WB_SD_COMMAND, cmd->c_opcode); 490 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 24) & 0xff); 491 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 16) & 0xff); 492 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 8) & 0xff); 493 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 0) & 0xff); 494 splx(s); 495 496 retry = 100000; 497 while (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { 498 if (--retry == 0) 499 break; 500 delay(1); 501 } 502 if (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { 503 REPORT(wb, 504 "command timed out, WB_INDEX_STATUS = 0x%02x\n", 505 wb_idx_read(wb, WB_INDEX_STATUS)); 506 cmd->c_error = ETIMEDOUT; 507 goto done; 508 } 509 510 if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { 511 if (wb->wb_sdmmc_intsts & WB_INT_TIMEOUT) { 512 cmd->c_error = ETIMEDOUT; 513 goto done; 514 } 515 516 if (ISSET(cmd->c_flags, SCF_RSP_136)) 517 wb_sdmmc_rsp_read_long(wb, cmd); 518 else 519 wb_sdmmc_rsp_read_short(wb, cmd); 520 } 521 522 if (cmd->c_error == 0 && cmd->c_datalen > 0) { 523 wb_led(wb, 1); 524 while (cmd->c_resid > 0) { 525 error = wb_sdmmc_transfer_data(wb, cmd); 526 if (error) { 527 cmd->c_error = error; 528 break; 529 } 530 } 531 wb_led(wb, 0); 532 } 533 534 done: 535 SET(cmd->c_flags, SCF_ITSDONE); 536 537 if (cmd->c_error) { 538 REPORT(wb, 539 "cmd error = %d, op = %d [%s] " 540 "blklen %d datalen %d resid %d\n", 541 cmd->c_error, cmd->c_opcode, 542 ISSET(cmd->c_flags, SCF_CMD_READ) ? "rd" : "wr", 543 cmd->c_blklen, cmd->c_datalen, cmd->c_resid); 544 } 545 } 546 547 void 548 wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) 549 { 550 REPORT(sch, "TRACE: sdmmc/card_enable_intr(wb, enable=%d)\n", enable); 551 } 552 553 void 554 wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t sch) 555 { 556 REPORT(sch, "TRACE: sdmmc/card_intr_ack(wb)\n"); 557 } 558 559 /* 560 * intr handler 561 */ 562 int 563 wb_sdmmc_intr(struct wb_softc *wb) 564 { 565 uint8_t val; 566 567 val = wb_read(wb, WB_SD_INTSTS); 568 if (val == 0xff || val == 0x00) 569 return 0; 570 571 if (wb->wb_sdmmc_dev == NULL) 572 return 1; 573 574 wb->wb_sdmmc_intsts |= val; 575 576 REPORT(wb, "WB_SD_INTSTS = %b\n", val, 577 "\20\1TC\2BUSYEND\3PROGEND\4TIMEOUT" 578 "\5CRC\6FIFO\7CARD\010PENDING"); 579 580 if (val & WB_INT_CARD) 581 timeout_add_msec(&wb->wb_sdmmc_to, 250); 582 583 return 1; 584 } 585