1 /* $NetBSD: w83l518d_sdmmc.c,v 1.3 2010/10/07 12:06:09 kiyohara Exp $ */ 2 3 /* 4 * Copyright (c) 2009 Jared D. McNeill <jmcneill@invisible.ca> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 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. 14 * 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 25 * SUCH DAMAGE. 26 */ 27 28 #include <sys/cdefs.h> 29 __KERNEL_RCSID(0, "$NetBSD: w83l518d_sdmmc.c,v 1.3 2010/10/07 12:06:09 kiyohara Exp $"); 30 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> 38 #include <sys/proc.h> 39 40 #include <sys/bus.h> 41 42 #include <dev/sdmmc/sdmmcvar.h> 43 #include <dev/sdmmc/sdmmcchip.h> 44 #include <dev/sdmmc/sdmmc_ioreg.h> 45 46 #include <dev/isa/isavar.h> 47 #include <dev/isa/isadmavar.h> 48 49 #include <dev/ic/w83l518dreg.h> 50 #include <dev/ic/w83l518dvar.h> 51 #include <dev/ic/w83l518d_sdmmc.h> 52 53 /* #define WB_SDMMC_DEBUG */ 54 55 #ifdef WB_SDMMC_DEBUG 56 static int wb_sdmmc_debug = 1; 57 #else 58 static int wb_sdmmc_debug = 0; 59 #endif 60 61 #if defined(__NetBSD__) && __NetBSD_Version__ < 599000600 62 #define snprintb(b, l, f, v) bitmask_snprintf((v), (f), (b), (l)) 63 #endif 64 65 #define REPORT(_wb, ...) \ 66 if (wb_sdmmc_debug > 0) \ 67 aprint_normal_dev(((struct wb_softc *)(_wb))->wb_dev, \ 68 __VA_ARGS__) 69 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 int wb_sdmmc_bus_rod(sdmmc_chipset_handle_t, int); 79 static void wb_sdmmc_exec_command(sdmmc_chipset_handle_t, 80 struct sdmmc_command *); 81 static void wb_sdmmc_card_enable_intr(sdmmc_chipset_handle_t, int); 82 static void wb_sdmmc_card_intr_ack(sdmmc_chipset_handle_t); 83 84 static struct sdmmc_chip_functions wb_sdmmc_chip_functions = { 85 .host_reset = wb_sdmmc_host_reset, 86 .host_ocr = wb_sdmmc_host_ocr, 87 .host_maxblklen = wb_sdmmc_host_maxblklen, 88 .card_detect = wb_sdmmc_card_detect, 89 .write_protect = wb_sdmmc_write_protect, 90 .bus_power = wb_sdmmc_bus_power, 91 .bus_clock = wb_sdmmc_bus_clock, 92 .bus_width = wb_sdmmc_bus_width, 93 .bus_rod = wb_sdmmc_bus_rod, 94 .exec_command = wb_sdmmc_exec_command, 95 .card_enable_intr = wb_sdmmc_card_enable_intr, 96 .card_intr_ack = wb_sdmmc_card_intr_ack, 97 }; 98 99 static void 100 wb_sdmmc_read_data(struct wb_softc *wb, uint8_t *data, int len) 101 { 102 bus_space_read_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 103 } 104 105 static void 106 wb_sdmmc_write_data(struct wb_softc *wb, uint8_t *data, int len) 107 { 108 bus_space_write_multi_1(wb->wb_iot, wb->wb_ioh, WB_SD_FIFO, data, len); 109 } 110 111 static void 112 wb_sdmmc_discover(void *opaque) 113 { 114 struct wb_softc *wb = opaque; 115 116 REPORT(wb, "TRACE: discover(wb)\n"); 117 118 sdmmc_needs_discover(wb->wb_sdmmc_dev); 119 } 120 121 static bool 122 wb_sdmmc_enable(struct wb_softc *wb) 123 { 124 int i = 5000; 125 126 REPORT(wb, "TRACE: enable(wb)\n"); 127 128 /* put the device in a known state */ 129 wb_idx_write(wb, WB_INDEX_SETUP, WB_SETUP_SOFT_RST); 130 while (--i > 0 && wb_idx_read(wb, WB_INDEX_SETUP) & WB_SETUP_SOFT_RST) 131 delay(100); 132 if (i == 0) { 133 aprint_error_dev(wb->wb_dev, "timeout resetting device\n"); 134 return false; 135 } 136 wb_idx_write(wb, WB_INDEX_CLK, wb->wb_sdmmc_clk); 137 wb_idx_write(wb, WB_INDEX_FIFOEN, 0); 138 wb_idx_write(wb, WB_INDEX_DMA, 0); 139 wb_idx_write(wb, WB_INDEX_PBSMSB, 0); 140 wb_idx_write(wb, WB_INDEX_PBSLSB, 0); 141 /* drain FIFO */ 142 while ((wb_read(wb, WB_SD_FIFOSTS) & WB_FIFO_EMPTY) == 0) 143 wb_read(wb, WB_SD_FIFO); 144 145 wb_write(wb, WB_SD_CSR, 0); 146 147 wb_write(wb, WB_SD_INTCTL, WB_INT_DEFAULT); 148 149 wb_sdmmc_card_detect(wb); 150 151 return true; 152 } 153 154 static bool 155 wb_sdmmc_disable(struct wb_softc *wb) 156 { 157 uint8_t val; 158 159 REPORT(wb, "TRACE: disable(wb)\n"); 160 161 val = wb_read(wb, WB_SD_CSR); 162 val |= WB_CSR_POWER_N; 163 wb_write(wb, WB_SD_CSR, val); 164 165 return true; 166 } 167 168 void 169 wb_sdmmc_attach(struct wb_softc *wb) 170 { 171 struct sdmmcbus_attach_args saa; 172 173 callout_init(&wb->wb_sdmmc_callout, 0); 174 callout_setfunc(&wb->wb_sdmmc_callout, wb_sdmmc_discover, wb); 175 176 wb->wb_sdmmc_width = 1; 177 wb->wb_sdmmc_clk = WB_CLK_375K; 178 179 if (wb_sdmmc_enable(wb) == false) 180 return; 181 182 memset(&saa, 0, sizeof(saa)); 183 saa.saa_busname = "sdmmc"; 184 saa.saa_sct = &wb_sdmmc_chip_functions; 185 saa.saa_sch = wb; 186 saa.saa_clkmin = 375; 187 saa.saa_clkmax = 24000; 188 saa.saa_caps = SMC_CAPS_4BIT_MODE; 189 190 wb->wb_sdmmc_dev = config_found(wb->wb_dev, &saa, NULL); 191 } 192 193 int 194 wb_sdmmc_detach(struct wb_softc *wb, int flags) 195 { 196 int rv; 197 198 if (wb->wb_sdmmc_dev) { 199 rv = config_detach(wb->wb_sdmmc_dev, flags); 200 if (rv) 201 return rv; 202 } 203 wb_sdmmc_disable(wb); 204 205 callout_halt(&wb->wb_sdmmc_callout, NULL); 206 callout_destroy(&wb->wb_sdmmc_callout); 207 208 return 0; 209 } 210 211 /* 212 * SD/MMC interface 213 */ 214 static int 215 wb_sdmmc_host_reset(sdmmc_chipset_handle_t sch) 216 { 217 REPORT(sch, "TRACE: sdmmc/host_reset(wb)\n"); 218 219 return 0; 220 } 221 222 static uint32_t 223 wb_sdmmc_host_ocr(sdmmc_chipset_handle_t sch) 224 { 225 REPORT(sch, "TRACE: sdmmc/host_ocr(wb)\n"); 226 227 return MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V; 228 } 229 230 static int 231 wb_sdmmc_host_maxblklen(sdmmc_chipset_handle_t sch) 232 { 233 REPORT(sch, "TRACE: sdmmc/host_maxblklen(wb)\n"); 234 235 return 512; /* XXX */ 236 } 237 238 static int 239 wb_sdmmc_card_detect(sdmmc_chipset_handle_t sch) 240 { 241 struct wb_softc *wb = sch; 242 int rv; 243 244 wb_led(wb, true); 245 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_CARD_PRESENT) ? 1 : 0; 246 wb_led(wb, false); 247 248 REPORT(wb, "TRACE: sdmmc/card_detect(wb) -> %d\n", rv); 249 250 return rv; 251 } 252 253 static int 254 wb_sdmmc_write_protect(sdmmc_chipset_handle_t sch) 255 { 256 struct wb_softc *wb = sch; 257 int rv; 258 259 wb_led(wb, true); 260 rv = (wb_read(wb, WB_SD_CSR) & WB_CSR_WRITE_PROTECT) ? 1 : 0; 261 wb_led(wb, false); 262 263 REPORT(wb, "TRACE: sdmmc/write_protect(wb) -> %d\n", rv); 264 265 return rv; 266 } 267 268 static int 269 wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) 270 { 271 REPORT(sch, "TRACE: sdmmc/bus_power(wb, ocr=%d)\n", ocr); 272 273 return 0; 274 } 275 276 static int 277 wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq) 278 { 279 struct wb_softc *wb = sch; 280 uint8_t clk; 281 282 REPORT(wb, "TRACE: sdmmc/bus_clock(wb, freq=%d)\n", freq); 283 284 if (freq >= 24000) 285 clk = WB_CLK_24M; 286 else if (freq >= 16000) 287 clk = WB_CLK_16M; 288 else if (freq >= 12000) 289 clk = WB_CLK_12M; 290 else 291 clk = WB_CLK_375K; 292 293 wb->wb_sdmmc_clk = clk; 294 295 if (wb_idx_read(wb, WB_INDEX_CLK) != clk) 296 wb_idx_write(wb, WB_INDEX_CLK, clk); 297 298 return 0; 299 } 300 301 static int 302 wb_sdmmc_bus_width(sdmmc_chipset_handle_t sch, int width) 303 { 304 struct wb_softc *wb = sch; 305 306 REPORT(wb, "TRACE: sdmmc/bus_width(wb, width=%d)\n", width); 307 308 if (width != 1 && width != 4) 309 return 1; 310 311 wb->wb_sdmmc_width = width; 312 313 return 0; 314 } 315 316 static int 317 wb_sdmmc_bus_rod(sdmmc_chipset_handle_t sch, int on) 318 { 319 320 /* Not support */ 321 return -1; 322 } 323 324 325 static void 326 wb_sdmmc_rsp_read_long(struct wb_softc *wb, struct sdmmc_command *cmd) 327 { 328 uint8_t *p = (uint8_t *)cmd->c_resp; 329 int i; 330 331 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 1) { 332 cmd->c_error = ENXIO; 333 return; 334 } 335 336 for (i = 12; i >= 0; i -= 4) { 337 p[3] = wb_idx_read(wb, WB_INDEX_RESP(i + 0)); 338 p[2] = wb_idx_read(wb, WB_INDEX_RESP(i + 1)); 339 p[1] = wb_idx_read(wb, WB_INDEX_RESP(i + 2)); 340 p[0] = wb_idx_read(wb, WB_INDEX_RESP(i + 3)); 341 p += 4; 342 } 343 } 344 345 static void 346 wb_sdmmc_rsp_read_short(struct wb_softc *wb, struct sdmmc_command *cmd) 347 { 348 uint8_t *p = (uint8_t *)cmd->c_resp; 349 350 if (wb_idx_read(wb, WB_INDEX_RESPLEN) != 0) { 351 cmd->c_error = ENXIO; 352 return; 353 } 354 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 } 360 361 static 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 static 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 < __arraycount(opcodes); i++) 439 if (opcodes[i] == cmd->c_opcode) 440 break; 441 if (i == __arraycount(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 aprint_error_dev(wb->wb_dev, 451 "data is not a multiple of %u bytes\n", 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, true); 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, false); 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 static void 548 wb_sdmmc_card_enable_intr(sdmmc_chipset_handle_t sch, int enable) 549 { 550 REPORT(sch, "TRACE: sdmmc/card_enable_intr(wb, enable=%d)\n", enable); 551 } 552 553 static 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 if (wb_sdmmc_debug) { 577 char buf[64]; 578 snprintb(buf, sizeof(buf), 579 "\20\1TC\2BUSYEND\3PROGEND\4TIMEOUT" 580 "\5CRC\6FIFO\7CARD\010PENDING", 581 val); 582 REPORT(wb, "WB_SD_INTSTS = %s\n", buf); 583 } 584 585 if (val & WB_INT_CARD) 586 callout_schedule(&wb->wb_sdmmc_callout, hz / 4); 587 588 return 1; 589 } 590 591 /* 592 * pmf 593 */ 594 bool 595 wb_sdmmc_suspend(struct wb_softc *wb) 596 { 597 return wb_sdmmc_disable(wb); 598 } 599 600 bool 601 wb_sdmmc_resume(struct wb_softc *wb) 602 { 603 uint8_t val; 604 605 val = wb_read(wb, WB_SD_CSR); 606 val &= ~WB_CSR_POWER_N; 607 wb_write(wb, WB_SD_CSR, val); 608 609 if (wb_sdmmc_enable(wb) == false) 610 return false; 611 612 if (wb_idx_read(wb, WB_INDEX_CLK) != wb->wb_sdmmc_clk) 613 wb_idx_write(wb, WB_INDEX_CLK, wb->wb_sdmmc_clk); 614 615 return true; 616 } 617