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 *,

Reply via email to