Module Name: src
Committed By: nonaka
Date: Tue Apr 6 15:10:09 UTC 2010
Modified Files:
src/sys/dev/sdmmc: sdmmc.c sdmmc_mem.c sdmmcchip.h sdmmcreg.h
sdmmcvar.h
Log Message:
- mention MMC SPI mode.
- support SD 4bit bus width mode.
To generate a diff of this commit:
cvs rdiff -u -r1.1 -r1.2 src/sys/dev/sdmmc/sdmmc.c \
src/sys/dev/sdmmc/sdmmcchip.h
cvs rdiff -u -r1.3 -r1.4 src/sys/dev/sdmmc/sdmmc_mem.c \
src/sys/dev/sdmmc/sdmmcreg.h
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/sdmmc/sdmmcvar.h
Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.
Modified files:
Index: src/sys/dev/sdmmc/sdmmc.c
diff -u src/sys/dev/sdmmc/sdmmc.c:1.1 src/sys/dev/sdmmc/sdmmc.c:1.2
--- src/sys/dev/sdmmc/sdmmc.c:1.1 Tue Apr 21 03:00:30 2009
+++ src/sys/dev/sdmmc/sdmmc.c Tue Apr 6 15:10:09 2010
@@ -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 @@
#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 @@
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 @@
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 @@
}
}
+ 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 @@
}
}
+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 @@
}
/*
+ * 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 @@
#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 @@
/* 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 @@
{
/* 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 @@
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 @@
/* 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 @@
}
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 @@
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 @@
{
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 @@
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 @@
/* 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 @@
/* 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 @@
{
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 @@
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
Index: src/sys/dev/sdmmc/sdmmcchip.h
diff -u src/sys/dev/sdmmc/sdmmcchip.h:1.1 src/sys/dev/sdmmc/sdmmcchip.h:1.2
--- src/sys/dev/sdmmc/sdmmcchip.h:1.1 Tue Apr 21 03:00:30 2009
+++ src/sys/dev/sdmmc/sdmmcchip.h Tue Apr 6 15:10:09 2010
@@ -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 @@
#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;
Index: src/sys/dev/sdmmc/sdmmc_mem.c
diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.3 src/sys/dev/sdmmc/sdmmc_mem.c:1.4
--- src/sys/dev/sdmmc/sdmmc_mem.c:1.3 Sat Nov 28 10:00:24 2009
+++ src/sys/dev/sdmmc/sdmmc_mem.c Tue Apr 6 15:10:09 2010
@@ -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 <[email protected]>
+ * Copyright (c) 2007-2010 NONAKA Kimihiro <[email protected]>
* 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 @@
#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 @@
{
uint32_t host_ocr;
uint32_t card_ocr;
+ uint32_t ocr = 0;
int error;
SDMMC_LOCK(sc);
@@ -93,9 +104,17 @@
/* 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 @@
* 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 @@
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 @@
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 @@
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 @@
* 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 @@
* 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 @@
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 @@
* 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 @@
}
#ifdef SDMMC_DUMP_CSD
-static void
+void
sdmmc_print_csd(sdmmc_response resp, struct sdmmc_csd *csd)
{
@@ -430,9 +442,11 @@
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 @@
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 @@
/*
* 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 @@
*/
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 @@
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 @@
* 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 @@
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 @@
}
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 @@
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 @@
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 @@
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_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 @@
/* 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 @@
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 @@
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 @@
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 @@
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 @@
/* 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);
Index: src/sys/dev/sdmmc/sdmmcreg.h
diff -u src/sys/dev/sdmmc/sdmmcreg.h:1.3 src/sys/dev/sdmmc/sdmmcreg.h:1.4
--- src/sys/dev/sdmmc/sdmmcreg.h:1.3 Sun Apr 26 13:05:55 2009
+++ src/sys/dev/sdmmc/sdmmcreg.h Tue Apr 6 15:10:09 2010
@@ -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
Index: src/sys/dev/sdmmc/sdmmcvar.h
diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.2 src/sys/dev/sdmmc/sdmmcvar.h:1.3
--- src/sys/dev/sdmmc/sdmmcvar.h:1.2 Sat Nov 28 10:00:24 2009
+++ src/sys/dev/sdmmc/sdmmcvar.h Tue Apr 6 15:10:09 2010
@@ -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 @@
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 @@
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 @@
#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_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 @@
#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 @@
#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 @@
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 @@
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_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 @@
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 *,