diff options
| author | nonaka <nonaka@NetBSD.org> | 2010-04-06 15:10:09 +0000 |
|---|---|---|
| committer | nonaka <nonaka@NetBSD.org> | 2010-04-06 15:10:09 +0000 |
| commit | 7e6a2d093f7a73569b1d22fefc688b3308eb7510 (patch) | |
| tree | 0661f699f751f14ada5d951d747baf0796030709 /sys/dev | |
| parent | e80bcec34186a1ea8375d1885b24dbc77c5c5f61 (diff) | |
- mention MMC SPI mode.
- support SD 4bit bus width mode.
Diffstat (limited to 'sys/dev')
| -rw-r--r-- | sys/dev/sdmmc/sdmmc.c | 184 | ||||
| -rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 642 | ||||
| -rw-r--r-- | sys/dev/sdmmc/sdmmcchip.h | 12 | ||||
| -rw-r--r-- | sys/dev/sdmmc/sdmmcreg.h | 43 | ||||
| -rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 69 |
5 files changed, 795 insertions, 155 deletions
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c index 581ee33e5df..1b9057177fe 100644 --- a/sys/dev/sdmmc/sdmmc.c +++ b/sys/dev/sdmmc/sdmmc.c @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */ +/* $NetBSD: sdmmc.c,v 1.2 2010/04/06 15:10:09 nonaka Exp $ */ /* $OpenBSD: sdmmc.c,v 1.18 2009/01/09 10:58:38 jsg Exp $ */ /* @@ -50,7 +50,7 @@ */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.2 2010/04/06 15:10:09 nonaka Exp $"); #include <sys/param.h> #include <sys/device.h> @@ -59,6 +59,7 @@ __KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.1 2009/04/21 03:00:30 nonaka Exp $"); #include <sys/malloc.h> #include <sys/proc.h> #include <sys/systm.h> +#include <sys/callout.h> #include <dev/sdmmc/sdmmc_ioreg.h> #include <dev/sdmmc/sdmmcchip.h> @@ -85,6 +86,7 @@ CFATTACH_DECL_NEW(sdmmc, sizeof(struct sdmmc_softc), static void sdmmc_doattach(device_t); static void sdmmc_task_thread(void *); static void sdmmc_discover_task(void *); +static void sdmmc_polling_card(void *); static void sdmmc_card_attach(struct sdmmc_softc *); static void sdmmc_card_detach(struct sdmmc_softc *, int); static int sdmmc_print(void *, const char *); @@ -115,6 +117,7 @@ sdmmc_attach(device_t parent, device_t self, void *aux) sc->sc_dev = self; sc->sc_sct = saa->saa_sct; + sc->sc_spi_sct = saa->saa_spi_sct; sc->sc_sch = saa->saa_sch; sc->sc_dmat = saa->saa_dmat; sc->sc_clkmin = saa->saa_clkmin; @@ -133,6 +136,12 @@ sdmmc_attach(device_t parent, device_t self, void *aux) } } + if (ISSET(sc->sc_caps, SMC_CAPS_POLL_CARD_DET)) { + callout_init(&sc->sc_card_detect_ch, 0); + callout_reset(&sc->sc_card_detect_ch, hz, + sdmmc_polling_card, sc); + } + SIMPLEQ_INIT(&sc->sf_head); TAILQ_INIT(&sc->sc_tskq); TAILQ_INIT(&sc->sc_intrq); @@ -291,6 +300,29 @@ sdmmc_discover_task(void *arg) } } +static void +sdmmc_polling_card(void *arg) +{ + struct sdmmc_softc *sc = (struct sdmmc_softc *)arg; + int card_detect; + int s; + + s = splsdmmc(); + card_detect = sdmmc_chip_card_detect(sc->sc_sct, sc->sc_sch); + if (card_detect) { + if (!ISSET(sc->sc_flags, SMF_CARD_PRESENT)) { + sdmmc_needs_discover(sc->sc_dev); + } + } else { + if (ISSET(sc->sc_flags, SMF_CARD_PRESENT)) { + sdmmc_needs_discover(sc->sc_dev); + } + } + splx(s); + + callout_schedule(&sc->sc_card_detect_ch, hz); +} + /* * Called from process context when a card is present. */ @@ -325,6 +357,15 @@ sdmmc_card_attach(struct sdmmc_softc *sc) } /* + * Initialize the I/O functions and memory cards. + */ + error = sdmmc_init(sc); + if (error) { + aprint_error_dev(sc->sc_dev, "init failed\n"); + goto err; + } + + /* * Set SD/MMC bus clock. */ #ifdef SDMMC_DEBUG @@ -338,15 +379,6 @@ sdmmc_card_attach(struct sdmmc_softc *sc) #endif (void)sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk); - /* - * Initialize the I/O functions and memory cards. - */ - error = sdmmc_init(sc); - if (error) { - aprint_error_dev(sc->sc_dev, "init failed\n"); - goto err; - } - SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) { if (ISSET(sc->sc_flags, SMF_IO_MODE) && sf->number < 1) continue; @@ -475,13 +507,16 @@ sdmmc_enable(struct sdmmc_softc *sc) /* XXX wait for card to power up */ sdmmc_delay(100000); - /* Initialize SD I/O card function(s). */ - error = sdmmc_io_enable(sc); - if (error) - goto out; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + /* Initialize SD I/O card function(s). */ + error = sdmmc_io_enable(sc); + if (error) + goto out; + } - /* Initialize SD/MMC memory card(s). */ - if (ISSET(sc->sc_flags, SMF_MEM_MODE)) + /* Initialize SD/MMC memory card(s). */ + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) || + ISSET(sc->sc_flags, SMF_MEM_MODE)) error = sdmmc_mem_enable(sc); out: @@ -495,8 +530,10 @@ sdmmc_disable(struct sdmmc_softc *sc) { /* XXX complete commands if card is still present. */ - /* Make sure no card is still selected. */ - (void)sdmmc_select_card(sc, NULL); + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + /* Make sure no card is still selected. */ + (void)sdmmc_select_card(sc, NULL); + } /* Turn off bus power and clock. */ (void)sdmmc_chip_bus_width(sc->sc_sct, sc->sc_sch, 1); @@ -566,9 +603,11 @@ static int sdmmc_scan(struct sdmmc_softc *sc) { - /* Scan for I/O functions. */ - if (ISSET(sc->sc_flags, SMF_IO_MODE)) - sdmmc_io_scan(sc); + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + /* Scan for I/O functions. */ + if (ISSET(sc->sc_flags, SMF_IO_MODE)) + sdmmc_io_scan(sc); + } /* Scan for memory cards on the bus. */ if (ISSET(sc->sc_flags, SMF_MEM_MODE)) @@ -593,9 +632,12 @@ sdmmc_init(struct sdmmc_softc *sc) /* Initialize all identified card functions. */ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) { - if (ISSET(sc->sc_flags, SMF_IO_MODE) && - sdmmc_io_init(sc, sf) != 0) { - aprint_error_dev(sc->sc_dev, "i/o init failed\n"); + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + if (ISSET(sc->sc_flags, SMF_IO_MODE) && + sdmmc_io_init(sc, sf) != 0) { + aprint_error_dev(sc->sc_dev, + "i/o init failed\n"); + } } if (ISSET(sc->sc_flags, SMF_MEM_MODE) && @@ -622,7 +664,7 @@ sdmmc_delay(u_int usecs) } int -sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) +sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_function *sf, struct sdmmc_command *cmd) { struct sdmmc_command acmd; int error; @@ -633,12 +675,18 @@ sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) memset(&acmd, 0, sizeof(acmd)); acmd.c_opcode = MMC_APP_CMD; - acmd.c_arg = 0; - acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; + if (sf != NULL) { + acmd.c_arg = sf->rca << 16; + acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R1; + } else { + acmd.c_arg = 0; + acmd.c_flags = SCF_CMD_BCR | SCF_RSP_R1 | SCF_RSP_SPI_R1; + } error = sdmmc_mmc_command(sc, &acmd); if (error == 0) { - if (!ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) { + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) && + !ISSET(MMC_R1(acmd.c_resp), MMC_R1_APP_CMD)) { /* Card does not support application commands. */ error = ENODEV; } else { @@ -659,13 +707,13 @@ sdmmc_mmc_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) { int error; - DPRINTF(1,("sdmmc_mmc_command: cmd=%#x, arg=%#x, flags=%#x\n", + DPRINTF(1,("sdmmc_mmc_command: cmd=%d, arg=%#x, flags=%#x\n", cmd->c_opcode, cmd->c_arg, cmd->c_flags)); /* Don't lock */ #if defined(DIAGNOSTIC) || defined(SDMMC_DEBUG) - if (cmd->c_data) { + if (cmd->c_data && !ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { if (sc->sc_card == NULL) panic("%s: deselected card\n", DEVNAME(sc)); } @@ -698,7 +746,7 @@ sdmmc_go_idle_state(struct sdmmc_softc *sc) memset(&cmd, 0, sizeof(cmd)); cmd.c_opcode = MMC_GO_IDLE_STATE; - cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0; + cmd.c_flags = SCF_CMD_BC | SCF_RSP_R0 | SCF_RSP_SPI_R1; (void)sdmmc_mmc_command(sc, &cmd); } @@ -714,6 +762,9 @@ sdmmc_set_relative_addr(struct sdmmc_softc *sc, struct sdmmc_function *sf) /* Don't lock */ + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + return EIO; + memset(&cmd, 0, sizeof(cmd)); if (ISSET(sc->sc_flags, SMF_SD_MODE)) { cmd.c_opcode = SD_SEND_RELATIVE_ADDR; @@ -741,6 +792,9 @@ sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf) /* Don't lock */ + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + return EIO; + if (sc->sc_card == sf || (sf && sc->sc_card && sc->sc_card->rca == sf->rca)) { sc->sc_card = sf; @@ -764,11 +818,9 @@ sdmmc_dump_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) { int i; - DPRINTF(1,("%s: cmd %u arg=%#x data=%p dlen=%d flags=%#x " - "proc=\"%s\" (error %d)\n", + DPRINTF(1,("%s: cmd %u arg=%#x data=%p dlen=%d flags=%#x (error %d)\n", DEVNAME(sc), cmd->c_opcode, cmd->c_arg, cmd->c_data, - cmd->c_datalen, cmd->c_flags, curproc ? curproc->p_comm : "", - cmd->c_error)); + cmd->c_datalen, cmd->c_flags, cmd->c_error)); if (cmd->c_error || sdmmcdebug < 1) return; @@ -780,6 +832,66 @@ sdmmc_dump_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd) else if (ISSET(cmd->c_flags, SCF_RSP_PRESENT)) for (i = 0; i < 4; i++) aprint_normal("%02x ", ((uint8_t *)cmd->c_resp)[i]); + else + aprint_normal("none"); aprint_normal("\n"); } + +void +sdmmc_dump_data(const char *title, void *ptr, size_t size) +{ + char buf[16]; + uint8_t *p = ptr; + int i, j; + + printf("sdmmc_dump_data: %s\n", title ? title : ""); + printf("--------+--------------------------------------------------+------------------+\n"); + printf("offset | +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +a +b +c +d +e +f | data |\n"); + printf("--------+--------------------------------------------------+------------------+\n"); + for (i = 0; i < (int)size; i++) { + if ((i % 16) == 0) { + printf("%08x| ", i); + } else if ((i % 16) == 8) { + printf(" "); + } + + printf("%02x ", p[i]); + buf[i % 16] = p[i]; + + if ((i % 16) == 15) { + printf("| "); + for (j = 0; j < 16; j++) { + if (buf[j] >= 0x20 && buf[j] <= 0x7e) { + printf("%c", buf[j]); + } else { + printf("."); + } + } + printf(" |\n"); + } + } + if ((i % 16) != 0) { + j = (i % 16); + for (; j < 16; j++) { + printf(" "); + if ((j % 16) == 8) { + printf(" "); + } + } + + printf("| "); + for (j = 0; j < (i % 16); j++) { + if (buf[j] >= 0x20 && buf[j] <= 0x7e) { + printf("%c", buf[j]); + } else { + printf("."); + } + } + for (; j < 16; j++) { + printf(" "); + } + printf(" |\n"); + } + printf("--------+--------------------------------------------------+------------------+\n"); +} #endif diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c index 09c5a3810a0..a0f6822d537 100644 --- a/sys/dev/sdmmc/sdmmc_mem.c +++ b/sys/dev/sdmmc/sdmmc_mem.c @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmc_mem.c,v 1.3 2009/11/28 10:00:24 nonaka Exp $ */ +/* $NetBSD: sdmmc_mem.c,v 1.4 2010/04/06 15:10:09 nonaka Exp $ */ /* $OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -18,7 +18,7 @@ */ /*- - * Copyright (c) 2007-2009 NONAKA Kimihiro <nonaka@netbsd.org> + * Copyright (c) 2007-2010 NONAKA Kimihiro <nonaka@netbsd.org> * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -46,7 +46,7 @@ /* Routines for SD/MMC memory cards. */ #include <sys/cdefs.h> -__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.3 2009/11/28 10:00:24 nonaka Exp $"); +__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.4 2010/04/06 15:10:09 nonaka Exp $"); #include <sys/param.h> #include <sys/kernel.h> @@ -66,13 +66,23 @@ __KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.3 2009/11/28 10:00:24 nonaka Exp $") #define DPRINTF(s) do {} while (/*CONSTCOND*/0) #endif -static int sdmmc_mem_send_op_cond(struct sdmmc_softc *, uint32_t, uint32_t *); -static int sdmmc_mem_send_if_cond(struct sdmmc_softc *, uint32_t, uint32_t *); -static int sdmmc_mem_set_blocklen(struct sdmmc_softc *, - struct sdmmc_function *); -#ifdef SDMMC_DUMP_CSD -static void sdmmc_print_csd(sdmmc_response, struct sdmmc_csd *); +static int sdmmc_mem_send_cid(struct sdmmc_softc *, sdmmc_response *); +static int sdmmc_mem_send_csd(struct sdmmc_softc *, struct sdmmc_function *, + sdmmc_response *); +static int sdmmc_mem_send_scr(struct sdmmc_softc *, struct sdmmc_function *, + uint32_t scr[2]); +static int sdmmc_mem_decode_scr(struct sdmmc_softc *, struct sdmmc_function *); +static int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t); +static int sdmmc_set_bus_width(struct sdmmc_function *, int); +#if 0 +static int sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t, + uint8_t); #endif +static int sdmmc_mem_spi_read_ocr(struct sdmmc_softc *, uint32_t, uint32_t *); +static int sdmmc_mem_single_read_block(struct sdmmc_function *, uint32_t, + u_char *, size_t); +static int sdmmc_mem_single_write_block(struct sdmmc_function *, uint32_t, + u_char *, size_t); static int sdmmc_mem_read_block_subr(struct sdmmc_function *, uint32_t, u_char *, size_t); static int sdmmc_mem_write_block_subr(struct sdmmc_function *, uint32_t, @@ -86,6 +96,7 @@ sdmmc_mem_enable(struct sdmmc_softc *sc) { uint32_t host_ocr; uint32_t card_ocr; + uint32_t ocr = 0; int error; SDMMC_LOCK(sc); @@ -93,9 +104,17 @@ sdmmc_mem_enable(struct sdmmc_softc *sc) /* Set host mode to SD "combo" card or SD memory-only. */ SET(sc->sc_flags, SMF_SD_MODE|SMF_MEM_MODE); + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + sdmmc_spi_chip_initialize(sc->sc_spi_sct, sc->sc_sch); + /* Reset memory (*must* do that before CMD55 or CMD1). */ sdmmc_go_idle_state(sc); + /* Check SD Ver.2 */ + error = sdmmc_mem_send_if_cond(sc, 0x1aa, &card_ocr); + if (error == 0 && card_ocr == 0x1aa) + SET(ocr, MMC_OCR_HCS); + /* * Read the SD/MMC memory OCR value by issuing CMD55 followed * by ACMD41 to read the OCR value from memory-only SD cards. @@ -103,7 +122,8 @@ sdmmc_mem_enable(struct sdmmc_softc *sc) * how we distinguish them from SD cards. */ mmc_mode: - error = sdmmc_mem_send_op_cond(sc, 0, &card_ocr); + error = sdmmc_mem_send_op_cond(sc, + ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) ? ocr : 0, &card_ocr); if (error) { if (ISSET(sc->sc_flags, SMF_SD_MODE) && !ISSET(sc->sc_flags, SMF_IO_MODE)) { @@ -123,6 +143,15 @@ mmc_mode: goto out; } } + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + /* get card OCR */ + error = sdmmc_mem_spi_read_ocr(sc, ocr, &card_ocr); + if (error) { + DPRINTF(("%s: couldn't read SPI memory OCR\n", + SDMMCDEVNAME(sc))); + goto out; + } + } /* Set the lowest voltage supported by the card and host. */ host_ocr = sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch); @@ -132,13 +161,8 @@ mmc_mode: SDMMCDEVNAME(sc))); goto out; } - - /* Tell the card(s) to enter the idle state (again). */ - sdmmc_go_idle_state(sc); - - error = sdmmc_mem_send_if_cond(sc, 0x1aa, &card_ocr); - if (error == 0 && card_ocr == 0x1aa) - SET(host_ocr, MMC_OCR_HCS); + host_ocr &= card_ocr; + host_ocr |= ocr; /* Send the new OCR value until all cards are ready. */ error = sdmmc_mem_send_op_cond(sc, host_ocr, NULL); @@ -160,7 +184,7 @@ out: void sdmmc_mem_scan(struct sdmmc_softc *sc) { - struct sdmmc_command cmd; + sdmmc_response resp; struct sdmmc_function *sf; uint16_t next_rca; int error; @@ -176,15 +200,13 @@ sdmmc_mem_scan(struct sdmmc_softc *sc) * card remains silent once it has been assigned a RCA. */ for (retry = 0; retry < 100; retry++) { - memset(&cmd, 0, sizeof cmd); - cmd.c_opcode = MMC_ALL_SEND_CID; - cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2; - - error = sdmmc_mmc_command(sc, &cmd); - if (error == ETIMEDOUT) { - /* No more cards there. */ - break; - } else if (error) { + error = sdmmc_mem_send_cid(sc, &resp); + if (error) { + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) && + error == ETIMEDOUT) { + /* No more cards there. */ + break; + } DPRINTF(("%s: couldn't read CID\n", SDMMCDEVNAME(sc))); break; } @@ -204,31 +226,21 @@ sdmmc_mem_scan(struct sdmmc_softc *sc) * Remember the CID returned in the CMD2 response for * later decoding. */ - memcpy(sf->raw_cid, cmd.c_resp, sizeof(sf->raw_cid)); + memcpy(sf->raw_cid, resp, sizeof(sf->raw_cid)); /* * Silence the card by assigning it a unique RCA, or * querying it for its RCA in the case of SD. */ - if (sdmmc_set_relative_addr(sc, sf) != 0) { - aprint_error_dev(sc->sc_dev, "couldn't set mem RCA\n"); - sdmmc_function_free(sf); - break; - } - -#if 0 - /* Verify that the RCA has been set by selecting the card. */ - if (sdmmc_select_card(sc, sf) != 0) { - printf("%s: can't select mem RCA %d (verify)\n", - SDMMCDEVNAME(sc), sf->rca); - sdmmc_function_free(sf); - break; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + if (sdmmc_set_relative_addr(sc, sf) != 0) { + aprint_error_dev(sc->sc_dev, + "couldn't set mem RCA\n"); + sdmmc_function_free(sf); + break; + } } - /* Deselect. */ - (void)sdmmc_select_card(sc, NULL); -#endif - /* * If this is a memory-only card, the card responding * first becomes an alias for SDIO function 0. @@ -237,6 +249,10 @@ sdmmc_mem_scan(struct sdmmc_softc *sc) sc->sc_fn0 = sf; SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf, sf_list); + + /* only one function in SPI mode */ + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + break; } /* @@ -244,17 +260,13 @@ sdmmc_mem_scan(struct sdmmc_softc *sc) * Read the CSDs and decode the raw CID for each card. */ SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) { - memset(&cmd, 0, sizeof cmd); - cmd.c_opcode = MMC_SEND_CSD; - cmd.c_arg = MMC_ARG_RCA(sf->rca); - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2; - - if (sdmmc_mmc_command(sc, &cmd) != 0) { + error = sdmmc_mem_send_csd(sc, sf, &resp); + if (error) { SET(sf->flags, SFF_ERROR); continue; } - if (sdmmc_decode_csd(sc, cmd.c_resp, sf) != 0 || + if (sdmmc_decode_csd(sc, resp, sf) != 0 || sdmmc_decode_cid(sc, sf->raw_cid, sf) != 0) { SET(sf->flags, SFF_ERROR); continue; @@ -406,7 +418,7 @@ sdmmc_print_cid(struct sdmmc_cid *cid) } #ifdef SDMMC_DUMP_CSD -static void +void sdmmc_print_csd(sdmmc_response resp, struct sdmmc_csd *csd) { @@ -430,9 +442,11 @@ sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) SDMMC_LOCK(sc); - error = sdmmc_select_card(sc, sf); - if (error) - goto out; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + error = sdmmc_select_card(sc, sf); + if (error) + goto out; + } if (!ISSET(sf->flags, SFF_SDHC)) { error = sdmmc_mem_set_blocklen(sc, sf); @@ -440,6 +454,28 @@ sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf) goto out; } + /* change bus width if supported */ + if (ISSET(sc->sc_flags, SMF_SD_MODE)) { + error = sdmmc_mem_send_scr(sc, sf, sf->raw_scr); + if (error) { + DPRINTF(("%s: SD_SEND_SCR send failed.\n", + SDMMCDEVNAME(sc))); + goto out; + } + error = sdmmc_mem_decode_scr(sc, sf); + if (error) + goto out; + + if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE) && + ISSET(sf->scr.bus_width, SCR_SD_BUS_WIDTHS_4BIT)) { + error = sdmmc_set_bus_width(sf, 4); + if (error) { + DPRINTF(("%s: can't change bus width" + " (%d bit)\n", SDMMCDEVNAME(sc), 4)); + } + } + } + out: SDMMC_UNLOCK(sc); @@ -449,7 +485,7 @@ out: /* * Get or set the card's memory OCR value (SD or MMC). */ -static int +int sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp) { struct sdmmc_command cmd; @@ -465,31 +501,42 @@ sdmmc_mem_send_op_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp) */ for (retry = 0; retry < 100; retry++) { memset(&cmd, 0, sizeof(cmd)); - cmd.c_arg = ocr; - cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R3; + cmd.c_arg = !ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE) ? + ocr : (ocr & MMC_OCR_HCS); + cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R3 | SCF_RSP_SPI_R1; if (ISSET(sc->sc_flags, SMF_SD_MODE)) { cmd.c_opcode = SD_APP_OP_COND; - error = sdmmc_app_command(sc, &cmd); + error = sdmmc_app_command(sc, NULL, &cmd); } else { cmd.c_opcode = MMC_SEND_OP_COND; error = sdmmc_mmc_command(sc, &cmd); } if (error) break; - if (ISSET(MMC_R3(cmd.c_resp), MMC_OCR_MEM_READY) || ocr == 0) - break; + + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + if (!ISSET(MMC_SPI_R1(cmd.c_resp), R1_SPI_IDLE)) + break; + } else { + if (ISSET(MMC_R3(cmd.c_resp), MMC_OCR_MEM_READY) || + ocr == 0) + break; + } error = ETIMEDOUT; sdmmc_delay(10000); } - if (error == 0 && ocrp != NULL) + if (error == 0 && + ocrp != NULL && + !ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) *ocrp = MMC_R3(cmd.c_resp); - + DPRINTF(("%s: sdmmc_mem_send_op_cond: error=%d, ocr=%#x\n", + SDMMCDEVNAME(sc), error, MMC_R3(cmd.c_resp))); return error; } -static int +int sdmmc_mem_send_if_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp) { struct sdmmc_command cmd; @@ -499,12 +546,19 @@ sdmmc_mem_send_if_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp) memset(&cmd, 0, sizeof(cmd)); cmd.c_arg = ocr; - cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R7; + cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R7 | SCF_RSP_SPI_R7; cmd.c_opcode = SD_SEND_IF_COND; error = sdmmc_mmc_command(sc, &cmd); - if (error == 0 && ocrp != NULL) - *ocrp = MMC_R7(cmd.c_resp); + if (error == 0 && ocrp != NULL) { + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + *ocrp = MMC_SPI_R7(cmd.c_resp); + } else { + *ocrp = MMC_R7(cmd.c_resp); + } + DPRINTF(("%s: sdmmc_mem_send_if_cond: error=%d, ocr=%#x\n", + SDMMCDEVNAME(sc), error, *ocrp)); + } return error; } @@ -512,7 +566,7 @@ sdmmc_mem_send_if_cond(struct sdmmc_softc *sc, uint32_t ocr, uint32_t *ocrp) * Set the read block length appropriately for this card, according to * the card CSD register value. */ -static int +int sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf) { struct sdmmc_command cmd; @@ -523,7 +577,7 @@ sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf) memset(&cmd, 0, sizeof(cmd)); cmd.c_opcode = MMC_SET_BLOCKLEN; cmd.c_arg = SDMMC_SECTOR_SIZE; - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R1; error = sdmmc_mmc_command(sc, &cmd); @@ -534,6 +588,337 @@ sdmmc_mem_set_blocklen(struct sdmmc_softc *sc, struct sdmmc_function *sf) } static int +sdmmc_mem_send_cid(struct sdmmc_softc *sc, sdmmc_response *resp) +{ + struct sdmmc_command cmd; + int error; + + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + memset(&cmd, 0, sizeof cmd); + cmd.c_opcode = MMC_ALL_SEND_CID; + cmd.c_flags = SCF_CMD_BCR | SCF_RSP_R2; + + error = sdmmc_mmc_command(sc, &cmd); + } else { + error = sdmmc_mem_send_cxd_data(sc, MMC_SEND_CID, &cmd.c_resp, + sizeof(cmd.c_resp)); + } + +#ifdef SDMMC_DEBUG + sdmmc_dump_data("CID", cmd.c_resp, sizeof(cmd.c_resp)); +#endif + if (error == 0 && resp != NULL) + memcpy(resp, &cmd.c_resp, sizeof(*resp)); + return error; +} + +static int +sdmmc_mem_send_csd(struct sdmmc_softc *sc, struct sdmmc_function *sf, + sdmmc_response *resp) +{ + struct sdmmc_command cmd; + int error; + + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + memset(&cmd, 0, sizeof cmd); + cmd.c_opcode = MMC_SEND_CSD; + cmd.c_arg = MMC_ARG_RCA(sf->rca); + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R2; + + error = sdmmc_mmc_command(sc, &cmd); + } else { + error = sdmmc_mem_send_cxd_data(sc, MMC_SEND_CSD, &cmd.c_resp, + sizeof(cmd.c_resp)); + } + +#ifdef SDMMC_DEBUG + sdmmc_dump_data("CSD", cmd.c_resp, sizeof(cmd.c_resp)); +#endif + if (error == 0 && resp != NULL) + memcpy(resp, &cmd.c_resp, sizeof(*resp)); + return error; +} + +static int +sdmmc_mem_send_scr(struct sdmmc_softc *sc, struct sdmmc_function *sf, + uint32_t scr[2]) +{ + struct sdmmc_command cmd; + bus_dma_segment_t ds[1]; + void *ptr = NULL; + int datalen = 8; + int rseg; + int error = 0; + + /* Don't lock */ + + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + error = bus_dmamem_alloc(sc->sc_dmat, datalen, PAGE_SIZE, 0, + ds, 1, &rseg, BUS_DMA_NOWAIT); + if (error) + goto out; + error = bus_dmamem_map(sc->sc_dmat, ds, 1, datalen, &ptr, + BUS_DMA_NOWAIT); + if (error) + goto dmamem_free; + error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, ptr, datalen, + NULL, BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_READ); + if (error) + goto dmamem_unmap; + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, + BUS_DMASYNC_PREREAD); + } else { + ptr = malloc(datalen, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ptr == NULL) + goto out; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = ptr; + cmd.c_datalen = datalen; + cmd.c_blklen = datalen; + cmd.c_arg = 0; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1 | SCF_RSP_SPI_R1; + cmd.c_opcode = SD_APP_SEND_SCR; + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) + cmd.c_dmamap = sc->sc_dmap; + + error = sdmmc_app_command(sc, sf, &cmd); + if (error == 0) { + memcpy(scr, ptr, datalen); + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, + BUS_DMASYNC_POSTREAD); + } + } + +out: + if (ptr != NULL) { + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap); +dmamem_unmap: + bus_dmamem_unmap(sc->sc_dmat, ptr, datalen); +dmamem_free: + bus_dmamem_free(sc->sc_dmat, ds, rseg); + } else { + free(ptr, M_DEVBUF); + } + } + DPRINTF(("%s: sdmem_mem_send_scr: error = %d\n", SDMMCDEVNAME(sc), + error)); + if (error) + return error; +#ifdef SDMMC_DEBUG + sdmmc_dump_data("SCR", scr, 8); +#endif + return error; +} + +static int +sdmmc_mem_decode_scr(struct sdmmc_softc *sc, struct sdmmc_function *sf) +{ + sdmmc_response resp; + int ver; + + memset(resp, 0, sizeof(resp)); + resp[0] = sf->raw_scr[1]; + resp[1] = sf->raw_scr[0]; + + ver = SCR_STRUCTURE(resp); + sf->scr.sd_spec = SCR_SD_SPEC(resp); + sf->scr.bus_width = SCR_SD_BUS_WIDTHS(resp); + + DPRINTF(("%s: sdmmc_mem_decode_scr: spec=%d, bus width=%d\n", + SDMMCDEVNAME(sc), sf->scr.sd_spec, sf->scr.bus_width)); + + if (ver != 0) { + DPRINTF(("%s: unknown structure version: %d\n", + SDMMCDEVNAME(sc), ver)); + return EINVAL; + } + return 0; +} + +static int +sdmmc_mem_send_cxd_data(struct sdmmc_softc *sc, int opcode, void *data, + size_t datalen) +{ + struct sdmmc_command cmd; + bus_dma_segment_t ds[1]; + void *ptr = NULL; + int rseg; + int error = 0; + + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + error = bus_dmamem_alloc(sc->sc_dmat, datalen, PAGE_SIZE, 0, ds, + 1, &rseg, BUS_DMA_NOWAIT); + if (error) + goto out; + error = bus_dmamem_map(sc->sc_dmat, ds, 1, datalen, &ptr, + BUS_DMA_NOWAIT); + if (error) + goto dmamem_free; + error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, ptr, datalen, + NULL, BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_READ); + if (error) + goto dmamem_unmap; + + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, + BUS_DMASYNC_PREREAD); + } else { + ptr = malloc(datalen, M_DEVBUF, M_NOWAIT | M_ZERO); + if (ptr == NULL) + goto out; + } + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_data = ptr; + cmd.c_datalen = datalen; + cmd.c_blklen = datalen; + cmd.c_opcode = opcode; + cmd.c_arg = 0; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_SPI_R1; + if (opcode == MMC_SEND_EXT_CSD) + SET(cmd.c_flags, SCF_RSP_R1); + else + SET(cmd.c_flags, SCF_RSP_R2); + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) + cmd.c_dmamap = sc->sc_dmap; + + error = sdmmc_mmc_command(sc, &cmd); + if (error == 0) { + memcpy(data, ptr, datalen); + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, + BUS_DMASYNC_POSTREAD); + } + } + +out: + if (ptr != NULL) { + if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) { + bus_dmamap_unload(sc->sc_dmat, sc->sc_dmap); +dmamem_unmap: + bus_dmamem_unmap(sc->sc_dmat, ptr, datalen); +dmamem_free: + bus_dmamem_free(sc->sc_dmat, ds, rseg); + } else { + free(ptr, M_DEVBUF); + } + } + return error; +} + +int +sdmmc_mem_send_extcsd(struct sdmmc_softc *sc) +{ + char buf[512]; + int error; + + error = sdmmc_mem_send_cxd_data(sc, MMC_SEND_EXT_CSD, buf, 512); + + /*XXX*/ + + return error; +} + +static int +sdmmc_set_bus_width(struct sdmmc_function *sf, int width) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_command cmd; + int error; + + if (ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + return ENODEV; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = SD_APP_SET_BUS_WIDTH; + cmd.c_flags = SCF_RSP_R1 | SCF_CMD_AC; + + switch (width) { + case 1: + cmd.c_arg = SD_ARG_BUS_WIDTH_1; + break; + + case 4: + cmd.c_arg = SD_ARG_BUS_WIDTH_4; + break; + + default: + return EINVAL; + } + + error = sdmmc_app_command(sc, sf, &cmd); + if (error == 0) + error = sdmmc_chip_bus_width(sc->sc_sct, sc->sc_sch, width); + return error; +} + +#if 0 +static int +sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index, + uint8_t value) +{ + struct sdmmc_softc *sc = sf->sc; + struct sdmmc_command cmd; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = MMC_SWITCH; + cmd.c_arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | + (index << 16) | (value << 8) | set; + cmd.c_flags = SCF_RSP_SPI_R1B | SCF_RSP_R1B | SCF_CMD_AC; + + return sdmmc_mmc_command(sc, &cmd); +} +#endif + +/* + * SPI mode function + */ +static int +sdmmc_mem_spi_read_ocr(struct sdmmc_softc *sc, uint32_t hcs, uint32_t *card_ocr) +{ + struct sdmmc_command cmd; + int error; + + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = MMC_READ_OCR; + cmd.c_arg = hcs ? MMC_OCR_HCS : 0; + cmd.c_flags = SCF_RSP_SPI_R3; + + error = sdmmc_mmc_command(sc, &cmd); + if (error == 0 && card_ocr != NULL) + *card_ocr = cmd.c_resp[1]; + DPRINTF(("%s: sdmmc_mem_spi_read_ocr: error=%d, ocr=%#x\n", + SDMMCDEVNAME(sc), error, cmd.c_resp[1])); + return error; +} + +/* + * read/write function + */ +/* read */ +static int +sdmmc_mem_single_read_block(struct sdmmc_function *sf, uint32_t blkno, + u_char *data, size_t datalen) +{ + int error = 0; + int i; + + KASSERT((datalen % SDMMC_SECTOR_SIZE) == 0); + + for (i = 0; i < datalen / SDMMC_SECTOR_SIZE; i++) { + error = sdmmc_mem_read_block_subr(sf, blkno + i, + data + i * SDMMC_SECTOR_SIZE, SDMMC_SECTOR_SIZE); + if (error) + break; + } + return error; +} + +static int sdmmc_mem_read_block_subr(struct sdmmc_function *sf, uint32_t blkno, u_char *data, size_t datalen) { @@ -541,9 +926,11 @@ sdmmc_mem_read_block_subr(struct sdmmc_function *sf, uint32_t blkno, struct sdmmc_command cmd; int error; - error = sdmmc_select_card(sc, sf); - if (error) - goto out; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + error = sdmmc_select_card(sc, sf); + if (error) + goto out; + } memset(&cmd, 0, sizeof(cmd)); cmd.c_data = data; @@ -554,7 +941,7 @@ sdmmc_mem_read_block_subr(struct sdmmc_function *sf, uint32_t blkno, cmd.c_arg = blkno; if (!ISSET(sf->flags, SFF_SDHC)) cmd.c_arg <<= SDMMC_SECTOR_SIZE_SB; - cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1; + cmd.c_flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1 | SCF_RSP_SPI_R1; if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) cmd.c_dmamap = sc->sc_dmap; @@ -567,23 +954,26 @@ sdmmc_mem_read_block_subr(struct sdmmc_function *sf, uint32_t blkno, memset(&cmd, 0, sizeof cmd); cmd.c_opcode = MMC_STOP_TRANSMISSION; cmd.c_arg = MMC_ARG_RCA(sf->rca); - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B; + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_RSP_SPI_R1B; error = sdmmc_mmc_command(sc, &cmd); if (error) goto out; } } - do { - memset(&cmd, 0, sizeof(cmd)); - cmd.c_opcode = MMC_SEND_STATUS; - cmd.c_arg = MMC_ARG_RCA(sf->rca); - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; - error = sdmmc_mmc_command(sc, &cmd); - if (error) - break; - /* XXX time out */ - } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + do { + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = MMC_SEND_STATUS; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + cmd.c_arg = MMC_ARG_RCA(sf->rca); + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R2; + error = sdmmc_mmc_command(sc, &cmd); + if (error) + break; + /* XXX time out */ + } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); + } out: return error; @@ -598,6 +988,11 @@ sdmmc_mem_read_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data, SDMMC_LOCK(sc); + if (ISSET(sc->sc_caps, SMC_CAPS_SINGLE_ONLY)) { + error = sdmmc_mem_single_read_block(sf, blkno, data, datalen); + goto out; + } + if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) { error = sdmmc_mem_read_block_subr(sf, blkno, data, datalen); goto out; @@ -605,10 +1000,18 @@ sdmmc_mem_read_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data, /* DMA transfer */ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen, NULL, - BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_READ); + BUS_DMA_NOWAIT|BUS_DMA_READ); if (error) goto out; +#ifdef SDMMC_DEBUG + for (int i = 0; i < sc->sc_dmap->dm_nsegs; i++) { + printf("seg#%d: addr=%#lx, size=%#lx\n", i, + (u_long)sc->sc_dmap->dm_segs[i].ds_addr, + (u_long)sc->sc_dmap->dm_segs[i].ds_len); + } +#endif + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, BUS_DMASYNC_PREREAD); @@ -627,6 +1030,25 @@ out: return error; } +/* write */ +static int +sdmmc_mem_single_write_block(struct sdmmc_function *sf, uint32_t blkno, + u_char *data, size_t datalen) +{ + int error = 0; + int i; + + KASSERT((datalen % SDMMC_SECTOR_SIZE) == 0); + + for (i = 0; i < datalen / SDMMC_SECTOR_SIZE; i++) { + error = sdmmc_mem_write_block_subr(sf, blkno + i, + data + i * SDMMC_SECTOR_SIZE, SDMMC_SECTOR_SIZE); + if (error) + break; + } + return error; +} + static int sdmmc_mem_write_block_subr(struct sdmmc_function *sf, uint32_t blkno, u_char *data, size_t datalen) @@ -635,9 +1057,11 @@ sdmmc_mem_write_block_subr(struct sdmmc_function *sf, uint32_t blkno, struct sdmmc_command cmd; int error; - error = sdmmc_select_card(sc, sf); - if (error) - goto out; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + error = sdmmc_select_card(sc, sf); + if (error) + goto out; + } memset(&cmd, 0, sizeof(cmd)); cmd.c_data = data; @@ -660,23 +1084,26 @@ sdmmc_mem_write_block_subr(struct sdmmc_function *sf, uint32_t blkno, if (cmd.c_opcode == MMC_WRITE_BLOCK_MULTIPLE) { memset(&cmd, 0, sizeof(cmd)); cmd.c_opcode = MMC_STOP_TRANSMISSION; - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B; + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1B | SCF_RSP_SPI_R1B; error = sdmmc_mmc_command(sc, &cmd); if (error) goto out; } } - do { - memset(&cmd, 0, sizeof(cmd)); - cmd.c_opcode = MMC_SEND_STATUS; - cmd.c_arg = MMC_ARG_RCA(sf->rca); - cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1; - error = sdmmc_mmc_command(sc, &cmd); - if (error) - break; - /* XXX time out */ - } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) { + do { + memset(&cmd, 0, sizeof(cmd)); + cmd.c_opcode = MMC_SEND_STATUS; + if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE)) + cmd.c_arg = MMC_ARG_RCA(sf->rca); + cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1 | SCF_RSP_SPI_R2; + error = sdmmc_mmc_command(sc, &cmd); + if (error) + break; + /* XXX time out */ + } while (!ISSET(MMC_R1(cmd.c_resp), MMC_R1_READY_FOR_DATA)); + } out: return error; @@ -697,6 +1124,11 @@ sdmmc_mem_write_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data, goto out; } + if (ISSET(sc->sc_caps, SMC_CAPS_SINGLE_ONLY)) { + error = sdmmc_mem_single_write_block(sf, blkno, data, datalen); + goto out; + } + if (!ISSET(sc->sc_caps, SMC_CAPS_DMA)) { error = sdmmc_mem_write_block_subr(sf, blkno, data, datalen); goto out; @@ -704,10 +1136,18 @@ sdmmc_mem_write_block(struct sdmmc_function *sf, uint32_t blkno, u_char *data, /* DMA transfer */ error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, data, datalen, NULL, - BUS_DMA_NOWAIT|BUS_DMA_STREAMING|BUS_DMA_WRITE); + BUS_DMA_NOWAIT|BUS_DMA_WRITE); if (error) goto out; +#ifdef SDMMC_DEBUG + for (int i = 0; i < sc->sc_dmap->dm_nsegs; i++) { + printf("seg#%d: addr=%#lx, size=%#lx\n", i, + (u_long)sc->sc_dmap->dm_segs[i].ds_addr, + (u_long)sc->sc_dmap->dm_segs[i].ds_len); + } +#endif + bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, datalen, BUS_DMASYNC_PREWRITE); diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h index 60fc2d920ac..866f04756e8 100644 --- a/sys/dev/sdmmc/sdmmcchip.h +++ b/sys/dev/sdmmc/sdmmcchip.h @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcchip.h,v 1.1 2009/04/21 03:00:30 nonaka Exp $ */ +/* $NetBSD: sdmmcchip.h,v 1.2 2010/04/06 15:10:09 nonaka Exp $ */ /* $OpenBSD: sdmmcchip.h,v 1.3 2007/05/31 10:09:01 uwe Exp $ */ /* @@ -27,6 +27,7 @@ struct sdmmc_command; typedef struct sdmmc_chip_functions *sdmmc_chipset_tag_t; +typedef struct sdmmc_spi_chip_functions *sdmmc_spi_chipset_tag_t; typedef void *sdmmc_chipset_handle_t; struct sdmmc_chip_functions { @@ -91,9 +92,18 @@ struct sdmmc_chip_functions { #define SDMMC_SDCLK_OFF 0 #define SDMMC_SDCLK_400K 400 +/* SPI mode */ +struct sdmmc_spi_chip_functions { + /* card initialize */ + void (*initialize)(sdmmc_chipset_handle_t); +}; +#define sdmmc_spi_chip_initialize(tag, handle) \ + ((tag)->initialize((handle))) + struct sdmmcbus_attach_args { const char *saa_busname; sdmmc_chipset_tag_t saa_sct; + sdmmc_spi_chipset_tag_t saa_spi_sct; sdmmc_chipset_handle_t saa_sch; bus_dma_tag_t saa_dmat; u_int saa_clkmin; diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h index 0da89f0e4d8..990f34fb49b 100644 --- a/sys/dev/sdmmc/sdmmcreg.h +++ b/sys/dev/sdmmc/sdmmcreg.h @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcreg.h,v 1.3 2009/04/26 13:05:55 nonaka Exp $ */ +/* $NetBSD: sdmmcreg.h,v 1.4 2010/04/06 15:10:09 nonaka Exp $ */ /* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -96,6 +96,8 @@ #define MMC_R3(resp) ((resp)[0]) #define SD_R6(resp) ((resp)[0]) #define MMC_R7(resp) ((resp)[0]) +#define MMC_SPI_R1(resp) ((resp)[0]) +#define MMC_SPI_R7(resp) ((resp)[1]) /* RCA argument and response */ #define MMC_ARG_RCA(rca) ((rca) << 16) @@ -105,6 +107,45 @@ #define SD_ARG_BUS_WIDTH_1 0 #define SD_ARG_BUS_WIDTH_4 2 +/* EXT_CSD fields */ +#define EXT_CSD_BUS_WIDTH 183 /* R/W */ + +/* EXT_CSD field definitions */ +#define EXT_CSD_CMD_SET_NORMAL (1U << 0) +#define EXT_CSD_CMD_SET_SECURE (1U << 1) +#define EXT_CSD_CMD_SET_CPSECURE (1U << 2) + +/* EXT_CSD_BUS_WIDTH */ +#define EXT_CSD_BUS_WIDTH_1 0 /* 1 bit mode */ +#define EXT_CSD_BUS_WIDTH_4 1 /* 4 bit mode */ +#define EXT_CSD_BUS_WIDTH_8 2 /* 8 bit mode */ + +/* MMC_SWITCH access mode */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* SPI mode reports R1/R2(SEND_STATUS) status. */ +#define R1_SPI_IDLE (1 << 0) +#define R1_SPI_ERASE_RESET (1 << 1) +#define R1_SPI_ILLEGAL_COMMAND (1 << 2) +#define R1_SPI_COM_CRC (1 << 3) +#define R1_SPI_ERASE_SEQ (1 << 4) +#define R1_SPI_ADDRESS (1 << 5) +#define R1_SPI_PARAMETER (1 << 6) +/* R1 bit 7 is always zero */ +#define R2_SPI_CARD_LOCKED (1 << 8) +#define R2_SPI_WP_ERASE_SKIP (1 << 9) /* or lock/unlock fail */ +#define R2_SPI_LOCK_UNLOCK_FAIL R2_SPI_WP_ERASE_SKIP +#define R2_SPI_ERROR (1 << 10) +#define R2_SPI_CC_ERROR (1 << 11) +#define R2_SPI_CARD_ECC_ERROR (1 << 12) +#define R2_SPI_WP_VIOLATION (1 << 13) +#define R2_SPI_ERASE_PARAM (1 << 14) +#define R2_SPI_OUT_OF_RANGE (1 << 15) /* or CSD overwrite */ +#define R2_SPI_CSD_OVERWRITE R2_SPI_OUT_OF_RANGE + /* MMC R2 response (CSD) */ #define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) #define MMC_CSD_CSDVER_1_0 1 diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h index 00bf07e1a57..cc6624fc1f3 100644 --- a/sys/dev/sdmmc/sdmmcvar.h +++ b/sys/dev/sdmmc/sdmmcvar.h @@ -1,4 +1,4 @@ -/* $NetBSD: sdmmcvar.h,v 1.2 2009/11/28 10:00:24 nonaka Exp $ */ +/* $NetBSD: sdmmcvar.h,v 1.3 2010/04/06 15:10:09 nonaka Exp $ */ /* $OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $ */ /* @@ -22,6 +22,7 @@ #include <sys/queue.h> #include <sys/mutex.h> +#include <sys/callout.h> #include <machine/bus.h> @@ -51,6 +52,11 @@ struct sdmmc_cid { int mdt; /* manufacturing date */ }; +struct sdmmc_scr { + int sd_spec; + int bus_width; +}; + typedef uint32_t sdmmc_response[4]; struct sdmmc_softc; @@ -83,17 +89,24 @@ struct sdmmc_command { int c_datalen; /* length of data buffer */ int c_blklen; /* block length */ int c_flags; /* see below */ -#define SCF_ITSDONE 0x0001 /* command is complete */ -#define SCF_CMD_AC 0x0000 -#define SCF_CMD_ADTC 0x0010 -#define SCF_CMD_BC 0x0020 -#define SCF_CMD_BCR 0x0030 -#define SCF_CMD_READ 0x0040 /* read command (data expected) */ -#define SCF_RSP_BSY 0x0100 -#define SCF_RSP_136 0x0200 -#define SCF_RSP_CRC 0x0400 -#define SCF_RSP_IDX 0x0800 -#define SCF_RSP_PRESENT 0x1000 +#define SCF_ITSDONE (1U << 0) /* command is complete */ +#define SCF_RSP_PRESENT (1U << 1) +#define SCF_RSP_BSY (1U << 2) +#define SCF_RSP_136 (1U << 3) +#define SCF_RSP_CRC (1U << 4) +#define SCF_RSP_IDX (1U << 5) +#define SCF_CMD_READ (1U << 6) /* read command (data expected) */ +/* non SPI */ +#define SCF_CMD_AC (0U << 8) +#define SCF_CMD_ADTC (1U << 8) +#define SCF_CMD_BC (2U << 8) +#define SCF_CMD_BCR (3U << 8) +#define SCF_CMD_MASK (3U << 8) +/* SPI */ +#define SCF_RSP_SPI_S1 (1U << 10) +#define SCF_RSP_SPI_S2 (1U << 11) +#define SCF_RSP_SPI_B4 (1U << 12) +#define SCF_RSP_SPI_BSY (1U << 13) /* response types */ #define SCF_RSP_R0 0 /* none */ #define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) @@ -105,6 +118,14 @@ struct sdmmc_command { #define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) #define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) #define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +/* SPI */ +#define SCF_RSP_SPI_R1 (SCF_RSP_SPI_S1) +#define SCF_RSP_SPI_R1B (SCF_RSP_SPI_S1|SCF_RSP_SPI_BSY) +#define SCF_RSP_SPI_R2 (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2) +#define SCF_RSP_SPI_R3 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) +#define SCF_RSP_SPI_R4 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) +#define SCF_RSP_SPI_R5 (SCF_RSP_SPI_S1|SCF_RSP_SPI_S2) +#define SCF_RSP_SPI_R7 (SCF_RSP_SPI_S1|SCF_RSP_SPI_B4) int c_error; /* errno value on completion */ /* Host controller owned fields for data xfer in progress */ @@ -152,6 +173,8 @@ struct sdmmc_function { struct sdmmc_csd csd; /* decoded CSD value */ struct sdmmc_cid cid; /* decoded CID value */ sdmmc_response raw_cid; /* temp. storage for decoding */ + uint32_t raw_scr[2]; + struct sdmmc_scr scr; /* decoded CSR value */ }; /* @@ -162,10 +185,11 @@ struct sdmmc_softc { #define SDMMCDEVNAME(sc) (device_xname(sc->sc_dev)) sdmmc_chipset_tag_t sc_sct; /* host controller chipset tag */ + sdmmc_spi_chipset_tag_t sc_spi_sct; sdmmc_chipset_handle_t sc_sch; /* host controller chipset handle */ bus_dma_tag_t sc_dmat; bus_dmamap_t sc_dmap; -#define SDMMC_MAXNSEGS 16 /* (MAXPHYS / PAGE_SIZE) */ +#define SDMMC_MAXNSEGS 17 /* (MAXPHYS / PAGE_SIZE) + 1 */ struct kmutex sc_mtx; /* lock around host controller */ int sc_dying; /* bus driver is shutting down */ @@ -182,6 +206,9 @@ struct sdmmc_softc { #define SMC_CAPS_AUTO_STOP 0x0001 /* send CMD12 automagically by host */ #define SMC_CAPS_4BIT_MODE 0x0002 /* 4-bits data bus width */ #define SMC_CAPS_DMA 0x0004 /* DMA transfer */ +#define SMC_CAPS_SPI_MODE 0x0008 /* SPI mode */ +#define SMC_CAPS_POLL_CARD_DET 0x0010 /* Polling card detect */ +#define SMC_CAPS_SINGLE_ONLY 0x0020 /* only single read/write */ /* function */ int sc_function_count; /* number of I/O functions (SDIO) */ @@ -208,6 +235,8 @@ struct sdmmc_softc { u_int sc_clkmax; /* host max bus clock */ u_int sc_busclk; /* host bus clock */ int sc_buswidth; /* host bus width */ + + callout_t sc_card_detect_ch; /* polling card insert/remove */ }; /* @@ -247,11 +276,11 @@ struct sdmmc_function *sdmmc_function_alloc(struct sdmmc_softc *); void sdmmc_function_free(struct sdmmc_function *); int sdmmc_set_bus_power(struct sdmmc_softc *, uint32_t, uint32_t); int sdmmc_mmc_command(struct sdmmc_softc *, struct sdmmc_command *); -int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_command *); +int sdmmc_app_command(struct sdmmc_softc *, struct sdmmc_function *, + struct sdmmc_command *); void sdmmc_go_idle_state(struct sdmmc_softc *); int sdmmc_select_card(struct sdmmc_softc *, struct sdmmc_function *); -int sdmmc_set_relative_addr(struct sdmmc_softc *, - struct sdmmc_function *); +int sdmmc_set_relative_addr(struct sdmmc_softc *, struct sdmmc_function *); void sdmmc_intr_enable(struct sdmmc_function *); void sdmmc_intr_disable(struct sdmmc_function *); @@ -264,6 +293,10 @@ int sdmmc_decode_csd(struct sdmmc_softc *, sdmmc_response, int sdmmc_decode_cid(struct sdmmc_softc *, sdmmc_response, struct sdmmc_function *); void sdmmc_print_cid(struct sdmmc_cid *); +#ifdef SDMMC_DUMP_CSD +void sdmmc_print_csd(sdmmc_response, struct sdmmc_csd *); +#endif +void sdmmc_dump_data(const char *, void *, size_t); int sdmmc_io_enable(struct sdmmc_softc *); void sdmmc_io_scan(struct sdmmc_softc *); @@ -286,6 +319,10 @@ void sdmmc_check_cis_quirks(struct sdmmc_function *); int sdmmc_mem_enable(struct sdmmc_softc *); void sdmmc_mem_scan(struct sdmmc_softc *); int sdmmc_mem_init(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_mem_send_op_cond(struct sdmmc_softc *, uint32_t, uint32_t *); +int sdmmc_mem_send_if_cond(struct sdmmc_softc *, uint32_t, uint32_t *); +int sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_function *); +int sdmmc_mem_send_extcsd(struct sdmmc_softc *sc); int sdmmc_mem_read_block(struct sdmmc_function *, uint32_t, u_char *, size_t); int sdmmc_mem_write_block(struct sdmmc_function *, uint32_t, u_char *, |
