1 /* $NetBSD: ssumci.c,v 1.1 2010/04/06 15:54:31 nonaka Exp $ */ 2 3 /*- 4 * Copyright (c) 2010 NONAKA Kimihiro <nonaka@netbsd.org> 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. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, 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 /* 30 * driver to access MMC/SD card 31 */ 32 33 #include <sys/cdefs.h> 34 __KERNEL_RCSID(0, "$NetBSD: ssumci.c,v 1.1 2010/04/06 15:54:31 nonaka Exp $"); 35 36 #include <sys/param.h> 37 #include <sys/device.h> 38 #include <sys/systm.h> 39 #include <sys/malloc.h> 40 #include <sys/kernel.h> 41 #include <sys/proc.h> 42 #include <sys/bus.h> 43 #include <sys/intr.h> 44 45 #include <sh3/devreg.h> 46 #include <sh3/pfcreg.h> 47 #include <sh3/scireg.h> 48 49 #include <dev/sdmmc/sdmmcvar.h> 50 #include <dev/sdmmc/sdmmcchip.h> 51 52 #include <machine/autoconf.h> 53 54 #include <evbsh3/t_sh7706lan/t_sh7706lanvar.h> 55 56 #ifdef SSUMCI_DEBUG 57 int ssumci_debug = 1; 58 #define DPRINTF(n,s) do { if ((n) <= ssumci_debug) printf s; } while (0) 59 #else 60 #define DPRINTF(n,s) do {} while (0) 61 #endif 62 63 static int ssumci_host_reset(sdmmc_chipset_handle_t); 64 static uint32_t ssumci_host_ocr(sdmmc_chipset_handle_t); 65 static int ssumci_host_maxblklen(sdmmc_chipset_handle_t); 66 static int ssumci_card_detect(sdmmc_chipset_handle_t); 67 static int ssumci_write_protect(sdmmc_chipset_handle_t); 68 static int ssumci_bus_power(sdmmc_chipset_handle_t, uint32_t); 69 static int ssumci_bus_clock(sdmmc_chipset_handle_t, int); 70 static int ssumci_bus_width(sdmmc_chipset_handle_t, int); 71 static void ssumci_exec_command(sdmmc_chipset_handle_t, 72 struct sdmmc_command *); 73 74 static struct sdmmc_chip_functions ssumci_chip_functions = { 75 /* host controller reset */ 76 .host_reset = ssumci_host_reset, 77 78 /* host controller capabilities */ 79 .host_ocr = ssumci_host_ocr, 80 .host_maxblklen = ssumci_host_maxblklen, 81 82 /* card detection */ 83 .card_detect = ssumci_card_detect, 84 85 /* write protect */ 86 .write_protect = ssumci_write_protect, 87 88 /* bus power, clock frequency, width */ 89 .bus_power = ssumci_bus_power, 90 .bus_clock = ssumci_bus_clock, 91 .bus_width = ssumci_bus_width, 92 93 /* command execution */ 94 .exec_command = ssumci_exec_command, 95 96 /* card interrupt */ 97 .card_enable_intr = NULL, 98 .card_intr_ack = NULL, 99 }; 100 101 static void ssumci_spi_initialize(sdmmc_chipset_handle_t); 102 103 static struct sdmmc_spi_chip_functions ssumci_spi_chip_functions = { 104 .initialize = ssumci_spi_initialize, 105 }; 106 107 #define CSR_SET_1(reg,set,mask) \ 108 do { \ 109 uint8_t _r; \ 110 _r = _reg_read_1((reg)); \ 111 _r &= ~(mask); \ 112 _r |= (set); \ 113 _reg_write_1((reg), _r); \ 114 } while (/*CONSTCOND*/0) 115 116 #define CSR_SET_2(reg,set,mask) \ 117 do { \ 118 uint16_t _r; \ 119 _r = _reg_read_2((reg)); \ 120 _r &= ~(mask); \ 121 _r |= (set); \ 122 _reg_write_2((reg), _r); \ 123 } while (/*CONSTCOND*/0) 124 125 #define CSR_CLR_1(reg,clr) \ 126 do { \ 127 uint8_t _r; \ 128 _r = _reg_read_1((reg)); \ 129 _r &= ~(clr); \ 130 _reg_write_1((reg), _r); \ 131 } while (/*CONSTCOND*/0) 132 133 #define CSR_CLR_2(reg,clr) \ 134 do { \ 135 uint16_t _r; \ 136 _r = _reg_read_2((reg)); \ 137 _r &= ~(clr); \ 138 _reg_write_2((reg), _r); \ 139 } while (/*CONSTCOND*/0) 140 141 #define SCPCR_CLK_MASK 0x000C 142 #define SCPCR_CLK_IN 0x000C 143 #define SCPCR_CLK_OUT 0x0004 144 #define SCPDR_CLK 0x02 145 #define SCPCR_DAT_MASK 0x0003 146 #define SCPCR_DAT_IN 0x0003 147 #define SCPCR_DAT_OUT 0x0001 148 #define SCPDR_DAT 0x01 149 #define SCPCR_CMD_MASK 0x0030 150 #define SCPCR_CMD_IN 0x0030 151 #define SCPCR_CMD_OUT 0x0010 152 #define SCPDR_CMD 0x04 153 #define SCPCR_CS_MASK 0x000C 154 #define SCPCR_CS_OUT 0x0004 155 #define SCPDR_CS 0x08 156 #define SCPCR_EJECT 0x00C0 157 #define SCPDR_EJECT 0x08 158 159 #define MMC_TIME_OVER 20000 160 161 struct ssumci_softc { 162 device_t sc_dev; 163 device_t sc_sdmmc; 164 }; 165 166 static int ssumci_match(device_t, cfdata_t, void *); 167 static void ssumci_attach(device_t, device_t, void *); 168 169 CFATTACH_DECL_NEW(ssumci, sizeof(struct ssumci_softc), 170 ssumci_match, ssumci_attach, NULL, NULL); 171 172 static void ssumci_cmd_cfgread(struct ssumci_softc *, struct sdmmc_command *); 173 static void ssumci_cmd_read(struct ssumci_softc *, struct sdmmc_command *); 174 static void ssumci_cmd_write(struct ssumci_softc *, struct sdmmc_command *); 175 176 #define SSUMCI_SPIDR 0xb0008000 177 #define SSUMCI_SPISR 0xb0008002 178 #define SSUMCI_SPIBR 0xb0008004 179 180 static inline void 181 ssumci_wait(void) 182 { 183 184 while (_reg_read_1(SSUMCI_SPISR) == 0x00) 185 continue; 186 } 187 188 static inline uint8_t 189 ssumci_getc(void) 190 { 191 192 ssumci_wait(); 193 return _reg_read_1(SSUMCI_SPIBR); 194 } 195 196 static inline void 197 ssumci_putc(uint8_t v) 198 { 199 200 _reg_write_1(SSUMCI_SPIDR, v); 201 ssumci_wait(); 202 } 203 204 /*ARGSUSED*/ 205 static int 206 ssumci_match(device_t parent, cfdata_t cf, void *aux) 207 { 208 struct mainbus_attach_args *maa = (struct mainbus_attach_args *)aux; 209 210 if (strcmp(maa->ma_name, "ssumci") != 0) 211 return 0; 212 if (!IS_SH7706LSR) 213 return 0; 214 return 1; 215 } 216 217 /*ARGSUSED*/ 218 static void 219 ssumci_attach(device_t parent, device_t self, void *aux) 220 { 221 struct ssumci_softc *sc = device_private(self); 222 struct sdmmcbus_attach_args saa; 223 224 sc->sc_dev = self; 225 226 aprint_naive("\n"); 227 aprint_normal(": SPI MMC controller\n"); 228 229 /* setup */ 230 CSR_SET_2(SH7709_SCPCR, SCPCR_CS_OUT, SCPCR_CS_MASK); 231 232 /* 233 * Attach the generic SD/MMC bus driver. (The bus driver must 234 * not invoke any chipset functions before it is attached.) 235 */ 236 memset(&saa, 0, sizeof(saa)); 237 saa.saa_busname = "sdmmc"; 238 saa.saa_sct = &ssumci_chip_functions; 239 saa.saa_spi_sct = &ssumci_spi_chip_functions; 240 saa.saa_sch = sc; 241 saa.saa_clkmin = 400; 242 saa.saa_clkmax = 400; 243 saa.saa_caps = SMC_CAPS_SPI_MODE 244 | SMC_CAPS_SINGLE_ONLY 245 | SMC_CAPS_POLL_CARD_DET; 246 247 sc->sc_sdmmc = config_found(sc->sc_dev, &saa, NULL); 248 if (sc->sc_sdmmc == NULL) 249 aprint_error_dev(sc->sc_dev, "couldn't attach bus\n"); 250 } 251 252 /* 253 * Reset the host controller. Called during initialization, when 254 * cards are removed, upon resume, and during error recovery. 255 */ 256 /*ARGSUSED*/ 257 static int 258 ssumci_host_reset(sdmmc_chipset_handle_t sch) 259 { 260 261 return 0; 262 } 263 264 /*ARGSUSED*/ 265 static uint32_t 266 ssumci_host_ocr(sdmmc_chipset_handle_t sch) 267 { 268 269 return MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V; 270 } 271 272 /*ARGSUSED*/ 273 static int 274 ssumci_host_maxblklen(sdmmc_chipset_handle_t sch) 275 { 276 277 return 512; 278 } 279 280 /*ARGSUSED*/ 281 static int 282 ssumci_card_detect(sdmmc_chipset_handle_t sch) 283 { 284 uint8_t reg; 285 int s; 286 287 s = splsdmmc(); 288 CSR_SET_2(SH7709_SCPCR, SCPCR_EJECT, 0); 289 reg = _reg_read_1(SH7709_SCPDR); 290 splx(s); 291 292 return !(reg & SCPDR_EJECT); 293 } 294 295 /*ARGSUSED*/ 296 static int 297 ssumci_write_protect(sdmmc_chipset_handle_t sch) 298 { 299 300 return 0; /* non-protect */ 301 } 302 303 /* 304 * Set or change SD bus voltage and enable or disable SD bus power. 305 * Return zero on success. 306 */ 307 /*ARGSUSED*/ 308 static int 309 ssumci_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr) 310 { 311 312 if ((ocr & (MMC_OCR_3_2V_3_3V|MMC_OCR_3_3V_3_4V)) == 0) 313 return 1; 314 315 /*XXX???*/ 316 return 0; 317 } 318 319 /* 320 * Set or change MMCLK frequency or disable the MMC clock. 321 * Return zero on success. 322 */ 323 /*ARGSUSED*/ 324 static int 325 ssumci_bus_clock(sdmmc_chipset_handle_t sch, int freq) 326 { 327 328 return 0; 329 } 330 331 /*ARGSUSED*/ 332 static int 333 ssumci_bus_width(sdmmc_chipset_handle_t sch, int width) 334 { 335 336 if (width != 1) 337 return 1; 338 return 0; 339 } 340 341 /*ARGSUSED*/ 342 static void 343 ssumci_spi_initialize(sdmmc_chipset_handle_t sch) 344 { 345 int i, s; 346 347 s = splsdmmc(); 348 CSR_SET_1(SH7709_SCPDR, SCPDR_CS, 0); 349 for (i = 0; i < 10; i++) { 350 ssumci_putc(0xff); 351 } 352 CSR_CLR_1(SH7709_SCPDR, SCPDR_CS); 353 splx(s); 354 } 355 356 static void 357 ssumci_exec_command(sdmmc_chipset_handle_t sch, struct sdmmc_command *cmd) 358 { 359 struct ssumci_softc *sc = (struct ssumci_softc *)sch; 360 uint16_t resp; 361 int timo; 362 int s; 363 364 DPRINTF(1,("%s: start cmd %d arg=%#x data=%p dlen=%d flags=%#x\n", 365 device_xname(sc->sc_dev), cmd->c_opcode, cmd->c_arg, cmd->c_data, 366 cmd->c_datalen, cmd->c_flags)); 367 368 s = splsdmmc(); 369 370 ssumci_putc(0xff); 371 ssumci_putc(0x40 | (cmd->c_opcode & 0x3f)); 372 ssumci_putc((cmd->c_arg >> 24) & 0xff); 373 ssumci_putc((cmd->c_arg >> 16) & 0xff); 374 ssumci_putc((cmd->c_arg >> 8) & 0xff); 375 ssumci_putc((cmd->c_arg >> 0) & 0xff); 376 ssumci_putc((cmd->c_opcode == MMC_GO_IDLE_STATE) ? 0x95 : 377 (cmd->c_opcode == SD_SEND_IF_COND) ? 0x87 : 0); /* CRC */ 378 379 for (timo = MMC_TIME_OVER; timo > 0; timo--) { 380 resp = ssumci_getc(); 381 if (!(resp & 0x80) && timo <= (MMC_TIME_OVER - 2)) 382 break; 383 } 384 if (timo == 0) { 385 DPRINTF(1,(sc->sc_dev, "response timeout\n")); 386 cmd->c_error = ETIMEDOUT; 387 goto out; 388 } 389 390 if (ISSET(cmd->c_flags, SCF_RSP_SPI_S2)) { 391 resp |= (uint16_t) ssumci_getc() << 8; 392 } else if (ISSET(cmd->c_flags, SCF_RSP_SPI_B4)) { 393 cmd->c_resp[1] = (uint32_t) ssumci_getc() << 24; 394 cmd->c_resp[1] |= (uint32_t) ssumci_getc() << 16; 395 cmd->c_resp[1] |= (uint32_t) ssumci_getc() << 8; 396 cmd->c_resp[1] |= (uint32_t) ssumci_getc(); 397 DPRINTF(1, ("R3 resp: %#x\n", cmd->c_resp[1])); 398 } 399 cmd->c_resp[0] = resp; 400 if (resp != 0 && resp != R1_SPI_IDLE) { 401 DPRINTF(1,("response error: %#x\n", resp)); 402 cmd->c_error = EIO; 403 goto out; 404 } 405 DPRINTF(1, ("R1 resp: %#x\n", resp)); 406 407 if (cmd->c_datalen > 0) { 408 if (ISSET(cmd->c_flags, SCF_CMD_READ)) { 409 /* XXX: swap in this place? */ 410 if (cmd->c_opcode == MMC_SEND_CID || 411 cmd->c_opcode == MMC_SEND_CSD) { 412 sdmmc_response res; 413 uint32_t *p = cmd->c_data; 414 415 ssumci_cmd_cfgread(sc, cmd); 416 res[0] = be32toh(p[3]); 417 res[1] = be32toh(p[2]); 418 res[2] = be32toh(p[1]); 419 res[3] = be32toh(p[0]); 420 memcpy(p, &res, sizeof(res)); 421 } else { 422 ssumci_cmd_read(sc, cmd); 423 } 424 } else { 425 ssumci_cmd_write(sc, cmd); 426 } 427 } else { 428 ssumci_wait(); 429 } 430 431 out: 432 SET(cmd->c_flags, SCF_ITSDONE); 433 splx(s); 434 435 DPRINTF(1,("%s: cmd %d done (flags=%#x error=%d)\n", 436 device_xname(sc->sc_dev), cmd->c_opcode, cmd->c_flags, cmd->c_error)); 437 } 438 439 static void 440 ssumci_cmd_cfgread(struct ssumci_softc *sc, struct sdmmc_command *cmd) 441 { 442 u_char *data = cmd->c_data; 443 int timo; 444 int c; 445 int i; 446 447 /* wait data token */ 448 for (timo = MMC_TIME_OVER; timo > 0; timo--) { 449 c = ssumci_getc(); 450 if (c != 0xff) 451 break; 452 } 453 if (timo == 0) { 454 aprint_error_dev(sc->sc_dev, "cfg read timeout\n"); 455 cmd->c_error = ETIMEDOUT; 456 return; 457 } 458 if (c != 0xfe) { 459 aprint_error_dev(sc->sc_dev, "cfg read error (data=%#x)\n", c); 460 cmd->c_error = EIO; 461 return; 462 } 463 464 /* data read */ 465 data[0] = '\0'; /* XXXFIXME!!! */ 466 for (i = 1 /* XXXFIXME!!!*/ ; i < cmd->c_datalen; i++) { 467 data[i] = ssumci_getc(); 468 } 469 470 (void) ssumci_getc(); 471 (void) ssumci_getc(); 472 ssumci_wait(); 473 474 #ifdef SSUMCI_DEBUG 475 sdmmc_dump_data(NULL, cmd->c_data, cmd->c_datalen); 476 #endif 477 } 478 479 static void 480 ssumci_cmd_read(struct ssumci_softc *sc, struct sdmmc_command *cmd) 481 { 482 u_char *data = cmd->c_data; 483 int timo; 484 int c; 485 int i; 486 487 /* wait data token */ 488 for (timo = MMC_TIME_OVER; timo > 0; timo--) { 489 c = ssumci_getc(); 490 if (c != 0xff) 491 break; 492 } 493 if (timo == 0) { 494 aprint_error_dev(sc->sc_dev, "read timeout\n"); 495 cmd->c_error = ETIMEDOUT; 496 return; 497 } 498 if (c != 0xfe) { 499 aprint_error_dev(sc->sc_dev, "read error (data=%#x)\n", c); 500 cmd->c_error = EIO; 501 return; 502 } 503 504 /* data read */ 505 for (i = 0; i < cmd->c_datalen; i++) { 506 data[i] = ssumci_getc(); 507 } 508 509 /* ignore CRC */ 510 (void) ssumci_getc(); 511 (void) ssumci_getc(); 512 ssumci_wait(); 513 514 #ifdef SSUMCI_DEBUG 515 sdmmc_dump_data(NULL, cmd->c_data, cmd->c_datalen); 516 #endif 517 } 518 519 static void 520 ssumci_cmd_write(struct ssumci_softc *sc, struct sdmmc_command *cmd) 521 { 522 u_char *data = cmd->c_data; 523 int timo; 524 int c; 525 int i; 526 527 ssumci_wait(); 528 ssumci_putc(0xfe); 529 530 /* data write */ 531 for (i = 0; i < cmd->c_datalen; i++) { 532 ssumci_putc(data[i]); 533 } 534 535 /* dummy CRC */ 536 ssumci_putc(0); 537 ssumci_putc(0); 538 ssumci_putc(0xff); 539 if ((_reg_read_1(SSUMCI_SPIDR) & 0x0f) != 5) { 540 aprint_error_dev(sc->sc_dev, "write error\n"); 541 cmd->c_error = EIO; 542 return; 543 } 544 545 for (timo = 0x7fffffff; timo > 0; timo--) { 546 ssumci_putc(0xff); 547 c = _reg_read_1(SSUMCI_SPIDR); 548 if (c == 0xff) 549 break; 550 } 551 if (timo == 0) { 552 aprint_error_dev(sc->sc_dev, "write timeout\n"); 553 cmd->c_error = ETIMEDOUT; 554 } 555 } 556