1 /* $OpenBSD: w83l518d_sdmmc.c,v 1.2 2014/05/10 18:41:55 kettenis 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); 69 #if 0 70 int wb_sdmmc_bus_width(sdmmc_chipset_handle_t, int); 71 #endif 72 void wb_sdmmc_exec_command(sdmmc_chipset_handle_t, 73 struct sdmmc_command *); 74 void wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t, int); 75 void wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t); 76 77 struct sdmmc_chip_functions wb_sdmmc_chip_functions = { 78 /* host controller reset */ 79 wb_sdmmc_host_reset, 80 /* host controlle capabilities */ 81 wb_sdmmc_host_ocr, 82 wb_sdmmc_host_maxblklen, 83 /* card detection */ 84 wb_sdmmc_card_detect, 85 #ifdef notyet 86 .write_protect = wb_sdmmc_write_protect, 87 #endif 88 /* bus power and clock frequency */ 89 wb_sdmmc_bus_power, 90 wb_sdmmc_bus_clock, 91 #if 0 92 .bus_width = wb_sdmmc_bus_width, 93 #endif 94 /* command execution */ 95 wb_sdmmc_exec_command, 96 /* card interrupt */ 97 wb_sdmmc_card_intr_mask, 98 wb_sdmmc_card_intr_ack 99 }; 100 101 void wb_sdmmc_read_data(struct wb_softc *, uint8_t *, int); 102 void wb_sdmmc_write_data(struct wb_softc *, uint8_t *, int); 103 void wb_sdmmc_discover(void *); 104 int wb_sdmmc_enable(struct wb_softc *); 105 int wb_sdmmc_disable(struct wb_softc *); 106 int wb_sdmmc_transfer_data(struct wb_softc *, struct sdmmc_command *); 107 void wb_sdmmc_rsp_read_long(struct wb_softc *, struct sdmmc_command *); 108 void wb_sdmmc_rsp_read_short(struct wb_softc *, struct sdmmc_command *); 109 110 void 111 wb_sdmmc_read_data(struct wb_softc *wb, uint8_t *data, int len) 112 { 113 bus_space_read_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 114 } 115 116 void 117 wb_sdmmc_write_data(struct wb_softc *wb, uint8_t *data, int len) 118 { 119 bus_space_write_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 120 } 121 122 void 123 wb_sdmmc_discover(void *opaque) 124 { 125 struct wb_softc *wb = opaque; 126 127 REPORT(wb, "TRACE: discover(wb)\n"); 128 129 sdmmc_needs_discover(wb->wb_sdmmc_dev); 130 } 131 132 int 133 wb_sdmmc_enable(struct wb_softc *wb) 134 { 135 int i = 5000; 136 137 REPORT(wb, "TRACE: enable(wb)\n"); 138 139 /* put the device in a known state */ 140 wb_idx_write(wb, WB_INDEX_SETUP, WB_SETUP_SOFT_RST); 141 while (--i > 0 && wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_SOFT_RST) 142 delay(10); 143 if (i == 0) { 144 printf("%s: timeout resetting device\n", wb->wb_dev.dv_xname); 145 return 0; 146 } 147 wb_idx_write(wb, WB_INDEX_CLK, WB_CLK_375K); 148 wb_idx_write(wb, WB_INDEX_FIFOEN, 0); 149 wb_idx_write(wb, WB_INDEX_DMA, 0); 150 wb_idx_write(wb, WB_INDEX_PBSMSB, 0); 151 wb_idx_write(wb, WB_INDEX_PBSLSB, 0); 152 /* drain FIFO */ 153 while ((wb_read(wb, WB_SD_FIFOSTS) & WB_FIFO_EMPTY) == 0) 154 wb_read(wb, WB_SD_FIFO); 155 156 wb_write(wb, WB_SD_CSR, 0); 157 158 wb_write(wb, WB_SD_INTCTL, WB_INT_DEFAULT); 159 160 wb_sdmmc_card_detect(wb); 161 162 return 1; 163 } 164 165 int 166 wb_sdmmc_disable(struct wb_softc *wb) 167 { 168 uint8_t val; 169 170 REPORT(wb, "TRACE: disable(wb)\n"); 171 172 val = wb_read(wb, WB_SD_CSR); 173 val |= WB_CSR_POWER_N; 174 wb_write(wb, WB_SD_CSR, val); 175 176 return 1; 177 } 178 179 void 180 wb_sdmmc_attach(struct wb_softc *wb) 181 { 182 struct sdmmcbus_attach_args saa; 183 184 timeout_set(&wb->wb_sdmmc_to, wb_sdmmc_discover, wb); 185 186 wb->wb_sdmmc_width = 1; 187 188 if (wb_sdmmc_enable(wb) == 0) 189 return; 190 191 memset(&saa, 0, sizeof(saa)); 192 saa.saa_busname = "sdmmc"; 193 saa.sct = &wb_sdmmc_chip_functions; 194 saa.sch = wb; 195 saa.flags = SMF_STOP_AFTER_MULTIPLE; 196 197 wb->wb_sdmmc_dev = config_found(&wb->wb_dev, &saa, NULL); 198 } 199 200 int 201 wb_sdmmc_detach(struct wb_softc *wb, int flags) 202 { 203 int rv; 204 205 if (wb->wb_sdmmc_dev) { 206 rv = config_detach(wb->wb_sdmmc_dev, flags); 207 if (rv) 208 return rv; 209 } 210 wb_sdmmc_disable(wb); 211 212 timeout_del(&wb->wb_sdmmc_to); 213 214 return 0; 215 } 216 217 /* 218 * SD/MMC interface 219 */ 220 int 221 wb_sdmmc_host_reset(sdmmc_chipset_handle_t sch) 222 { 223 REPORT(sch, "TRACE: sdmmc/host_reset(wb)\n"); 224 225 return 0; 226 } 227 228 uint32_t 229 wb_sdmmc_host_ocr(sdmmc_chipset_handle_t sch) 230 { 231 REPORT(sch, "TRACE: sdmmc/host_ocr(wb)\n"); 232 233 return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; 234 } 235 236 int 237 wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t sch) 238 { 239 REPORT(sch, "TRACE: sdmmc/host_maxblklen(wb)\n"); 240 241 return 512; /* XXX */ 242 } 243 244 int 245 wb_sdmmc_card_detect(sdmmc_chipset_handle_t sch) 246 { 247 struct wb_softc *wb = sch; 248 int rv; 249 250 wb_led(wb, 1); 251 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_CARD_PRESENT) ? 1 : 0; 252 wb_led(wb, 0); 253 254 REPORT(wb, "TRACE: sdmmc/card_detect(wb) -> %d\n", rv); 255 256 return rv; 257 } 258 259 #ifdef notyet 260 int 261 wb_sdmmc_write_protect(sdmmc_chipset_handle_t sch) 262 { 263 struct wb_softc *wb = sch; 264 int rv; 265 266 wb_led(wb, 1); 267 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_WRITE_PROTECT) ? 1 : 0; 268 wb_led(wb, 0); 269 270 REPORT(wb, "TRACE: sdmmc/write_protect(wb) -> %d\n", rv); 271 272 return rv; 273 } 274 #endif 275 276 int 277 wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) 278 { 279 REPORT(sch, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr); 280 281 return 0; 282 } 283 284 int 285 wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) 286 { 287 struct wb_softc *wb = sch; 288 uint8_t clk; 289 290 REPORT(wb, "TRACE: sdmmc/bus_clock(wb, freq=%d)\n", freq); 291 292 if (freq >= 24000) 293 clk = WB_CLK_24M; 294 else if (freq >= 16000) 295 clk = WB_CLK_16M; 296 else if (freq >= 12000) 297 clk = WB_CLK_12M; 298 else 299 clk = WB_CLK_375K; 300 301 if (wb_idx_read(wb, WB_INDEX_CLK) != clk) 302 wb_idx_write(wb, WB_INDEX_CLK, clk); 303 304 return 0; 305 } 306 307 #if 0 308 int 309 wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) 310 { 311 struct wb_softc *wb = sch; 312 313 REPORT(wb, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width); 314 315 if (width != 1 && width != 4) 316 return 1; 317 318 wb->wb_sdmmc_width = width; 319 320 return 0; 321 } 322 #endif 323 324 void 325 wb_sdmmc_rsp_read_long(struct wb_softc *wb, struct sdmmc_command *cmd) 326 { 327 uint8_t *p = (uint8_t *)cmd->c_resp; 328 int i; 329 330 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 1) { 331 cmd->c_error = ENXIO; 332 return; 333 } 334 335 for (i = 12; i >= 0; i -= 4) { 336 p[0] = wb_idx_read(wb, WB_INDEX_RESP(i + 0)); 337 p[1] = wb_idx_read(wb, WB_INDEX_RESP(i + 1)); 338 p[2] = wb_idx_read(wb, WB_INDEX_RESP(i + 2)); 339 p[3] = wb_idx_read(wb, WB_INDEX_RESP(i + 3)); 340 p += 4; 341 } 342 } 343 344 void 345 wb_sdmmc_rsp_read_short(struct wb_softc *wb, struct sdmmc_command *cmd) 346 { 347 uint8_t *p = (uint8_t *)cmd->c_resp; 348 349 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 0) { 350 cmd->c_error = ENXIO; 351 return; 352 } 353 354 #if BYTE_ORDER == LITTLE_ENDIAN 355 p[3] = wb_idx_read(wb, WB_INDEX_RESP(12)); 356 p[2] = wb_idx_read(wb, WB_INDEX_RESP(13)); 357 p[1] = wb_idx_read(wb, WB_INDEX_RESP(14)); 358 p[0] = wb_idx_read(wb, WB_INDEX_RESP(15)); 359 #else 360 p[0] = wb_idx_read(wb, WB_INDEX_RESP(12)); 361 p[1] = wb_idx_read(wb, WB_INDEX_RESP(13)); 362 p[2] = wb_idx_read(wb, WB_INDEX_RESP(14)); 363 p[3] = wb_idx_read(wb, WB_INDEX_RESP(15)); 364 #endif 365 } 366 367 int 368 wb_sdmmc_transfer_data(struct wb_softc *wb, struct sdmmc_command *cmd) 369 { 370 uint8_t fifosts; 371 int datalen, retry = 5000; 372 373 if (wb->wb_sdmmc_intsts & WB_INT_CARD) 374 return EIO; 375 376 fifosts = wb_read(wb, WB_SD_FIFOSTS); 377 if (ISSET(cmd->c_flags, SCF_CMD_READ)) { 378 if (fifosts & WB_FIFO_EMPTY) { 379 while (--retry > 0) { 380 fifosts = wb_read(wb, WB_SD_FIFOSTS); 381 if ((fifosts & WB_FIFO_EMPTY) == 0) 382 break; 383 delay(100); 384 } 385 if (retry == 0) 386 return EBUSY; 387 } 388 389 if (fifosts & WB_FIFO_FULL) 390 datalen = 16; 391 else 392 datalen = fifosts & WB_FIFO_DEPTH_MASK; 393 } else { 394 if (fifosts & WB_FIFO_FULL) { 395 while (--retry > 0) { 396 fifosts = wb_read(wb, WB_SD_FIFOSTS); 397 if ((fifosts & WB_FIFO_FULL) == 0) 398 break; 399 delay(100); 400 } 401 if (retry == 0) 402 return EBUSY; 403 } 404 405 if (fifosts & WB_FIFO_EMPTY) 406 datalen = 16; 407 else 408 datalen = 16 - (fifosts & WB_FIFO_DEPTH_MASK); 409 } 410 411 datalen = MIN(datalen, cmd->c_resid); 412 if (datalen > 0) { 413 if (ISSET(cmd->c_flags, SCF_CMD_READ)) 414 wb_sdmmc_read_data(wb, cmd->c_buf, datalen); 415 else 416 wb_sdmmc_write_data(wb, cmd->c_buf, datalen); 417 418 cmd->c_buf += datalen; 419 cmd->c_resid -= datalen; 420 } 421 422 return 0; 423 } 424 425 void 426 wb_sdmmc_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) 427 { 428 static const int opcodes[] = { 429 11, 17, 18, 20, 24, 25, 26, 27, 30, 42, 51, 56 430 }; 431 struct wb_softc *wb = sch; 432 uint8_t val; 433 int blklen; 434 int error; 435 int i, retry; 436 int s; 437 438 REPORT(wb, "TRACE: sdmmc/exec_command(wb, cmd) " 439 "opcode %d flags 0x%x data %p datalen %d\n", 440 cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen); 441 442 if (cmd->c_datalen > 0) { 443 /* controller only supports a select number of data opcodes */ 444 for (i = 0; i < nitems(opcodes); i++) 445 if (opcodes[i] == cmd->c_opcode) 446 break; 447 if (i == nitems(opcodes)) { 448 cmd->c_error = EINVAL; 449 goto done; 450 } 451 452 /* Fragment the data into proper blocks */ 453 blklen = MIN(cmd->c_datalen, cmd->c_blklen); 454 455 if (cmd->c_datalen % blklen > 0) { 456 printf("%s: data is not a multiple of %u bytes\n", 457 wb->wb_dev.dv_xname, blklen); 458 cmd->c_error = EINVAL; 459 goto done; 460 } 461 462 /* setup block size registers */ 463 blklen = blklen + 2 * wb->wb_sdmmc_width; 464 wb_idx_write(wb, WB_INDEX_PBSMSB, 465 ((blklen >> 4) & 0xf0) | (wb->wb_sdmmc_width / 4)); 466 wb_idx_write(wb, WB_INDEX_PBSLSB, blklen & 0xff); 467 468 /* clear FIFO */ 469 val = wb_idx_read(wb, WB_INDEX_SETUP); 470 val |= WB_SETUP_FIFO_RST; 471 wb_idx_write(wb, WB_INDEX_SETUP, val); 472 while (wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_FIFO_RST) 473 ; 474 475 cmd->c_resid = cmd->c_datalen; 476 cmd->c_buf = cmd->c_data; 477 478 /* setup FIFO thresholds */ 479 if (ISSET(cmd->c_flags, SCF_CMD_READ)) 480 wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_FULL | 8); 481 else { 482 wb_idx_write(wb, WB_INDEX_FIFOEN, WB_FIFOEN_EMPTY | 8); 483 484 /* pre-fill the FIFO on write */ 485 error = wb_sdmmc_transfer_data(wb, cmd); 486 if (error) { 487 cmd->c_error = error; 488 goto done; 489 } 490 } 491 } 492 493 s = splsdmmc(); 494 wb->wb_sdmmc_intsts = 0; 495 wb_write(wb, WB_SD_COMMAND, cmd->c_opcode); 496 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 24) & 0xff); 497 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 16) & 0xff); 498 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 8) & 0xff); 499 wb_write(wb, WB_SD_COMMAND, (cmd->c_arg >> 0) & 0xff); 500 splx(s); 501 502 retry = 100000; 503 while (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { 504 if (--retry == 0) 505 break; 506 delay(1); 507 } 508 if (wb_idx_read(wb, WB_INDEX_STATUS) & WB_STATUS_CARD_TRAFFIC) { 509 REPORT(wb, 510 "command timed out, WB_INDEX_STATUS = 0x%02x\n", 511 wb_idx_read(wb, WB_INDEX_STATUS)); 512 cmd->c_error = ETIMEDOUT; 513 goto done; 514 } 515 516 if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) { 517 if (wb->wb_sdmmc_intsts & WB_INT_TIMEOUT) { 518 cmd->c_error = ETIMEDOUT; 519 goto done; 520 } 521 522 if (ISSET(cmd->c_flags, SCF_RSP_136)) 523 wb_sdmmc_rsp_read_long(wb, cmd); 524 else 525 wb_sdmmc_rsp_read_short(wb, cmd); 526 } 527 528 if (cmd->c_error == 0 && cmd->c_datalen > 0) { 529 wb_led(wb, 1); 530 while (cmd->c_resid > 0) { 531 error = wb_sdmmc_transfer_data(wb, cmd); 532 if (error) { 533 cmd->c_error = error; 534 break; 535 } 536 } 537 wb_led(wb, 0); 538 } 539 540 done: 541 SET(cmd->c_flags, SCF_ITSDONE); 542 543 if (cmd->c_error) { 544 REPORT(wb, 545 "cmd error = %d, op = %d [%s] " 546 "blklen %d datalen %d resid %d\n", 547 cmd->c_error, cmd->c_opcode, 548 ISSET(cmd->c_flags, SCF_CMD_READ) ? "rd" : "wr", 549 cmd->c_blklen, cmd->c_datalen, cmd->c_resid); 550 } 551 } 552 553 void 554 wb_sdmmc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable) 555 { 556 REPORT(sch, "TRACE: sdmmc/card_enable_intr(wb, enable=%d)\n", enable); 557 } 558 559 void 560 wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t sch) 561 { 562 REPORT(sch, "TRACE: sdmmc/card_intr_ack(wb)\n"); 563 } 564 565 /* 566 * intr handler 567 */ 568 int 569 wb_sdmmc_intr(struct wb_softc *wb) 570 { 571 uint8_t val; 572 573 val = wb_read(wb, WB_SD_INTSTS); 574 if (val == 0xff || val == 0x00) 575 return 0; 576 577 if (wb->wb_sdmmc_dev == NULL) 578 return 1; 579 580 wb->wb_sdmmc_intsts |= val; 581 582 REPORT(wb, "WB_SD_INTSTS = %b\n", val, 583 "\20\1TC\2BUSYEND\3PROGEND\4TIMEOUT" 584 "\5CRC\6FIFO\7CARD\010PENDING"); 585 586 if (val & WB_INT_CARD) 587 timeout_add(&wb->wb_sdmmc_to, hz / 4); 588 589 return 1; 590 } 591