Module Name:    src
Committed By:   kiyohara
Date:           Thu Oct  7 12:24:23 UTC 2010

Modified Files:
        src/sys/dev/sdmmc: sdmmc.c sdmmc_io.c sdmmc_mem.c sdmmcreg.h sdmmcvar.h

Log Message:
Support High-Speed mode.


To generate a diff of this commit:
cvs rdiff -u -r1.4 -r1.5 src/sys/dev/sdmmc/sdmmc.c \
    src/sys/dev/sdmmc/sdmmcreg.h
cvs rdiff -u -r1.2 -r1.3 src/sys/dev/sdmmc/sdmmc_io.c
cvs rdiff -u -r1.12 -r1.13 src/sys/dev/sdmmc/sdmmc_mem.c
cvs rdiff -u -r1.7 -r1.8 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.4 src/sys/dev/sdmmc/sdmmc.c:1.5
--- src/sys/dev/sdmmc/sdmmc.c:1.4	Fri Oct  1 09:50:42 2010
+++ src/sys/dev/sdmmc/sdmmc.c	Thu Oct  7 12:24:23 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmc.c,v 1.4 2010/10/01 09:50:42 kiyohara Exp $	*/
+/*	$NetBSD: sdmmc.c,v 1.5 2010/10/07 12:24:23 kiyohara 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.4 2010/10/01 09:50:42 kiyohara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc.c,v 1.5 2010/10/07 12:24:23 kiyohara Exp $");
 
 #include <sys/param.h>
 #include <sys/device.h>
@@ -369,20 +369,6 @@
 		goto err;
 	}
 
-	/*
-	 * Set SD/MMC bus clock.
-	 */
-#ifdef SDMMC_DEBUG
-	if ((sc->sc_busclk / 1000) != 0) {
-		DPRINTF(1,("%s: bus clock: %u.%03u MHz\n", DEVNAME(sc),
-		    sc->sc_busclk / 1000, sc->sc_busclk % 1000));
-	} else {
-		DPRINTF(1,("%s: bus clock: %u KHz\n", DEVNAME(sc),
-		    sc->sc_busclk % 1000));
-	}
-#endif
-	(void)sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk);
-
 	SIMPLEQ_FOREACH(sf, &sc->sf_head, sf_list) {
 		if (ISSET(sc->sc_flags, SMF_IO_MODE) && sf->number < 1)
 			continue;
Index: src/sys/dev/sdmmc/sdmmcreg.h
diff -u src/sys/dev/sdmmc/sdmmcreg.h:1.4 src/sys/dev/sdmmc/sdmmcreg.h:1.5
--- src/sys/dev/sdmmc/sdmmcreg.h:1.4	Tue Apr  6 15:10:09 2010
+++ src/sys/dev/sdmmc/sdmmcreg.h	Thu Oct  7 12:24:23 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmcreg.h,v 1.4 2010/04/06 15:10:09 nonaka Exp $	*/
+/*	$NetBSD: sdmmcreg.h,v 1.5 2010/10/07 12:24:23 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $	*/
 
 /*
@@ -56,6 +56,7 @@
 
 /* SD commands */			/* response type */
 #define SD_SEND_RELATIVE_ADDR 	  	3	/* R6 */
+#define SD_SEND_SWITCH_FUNC		6	/* R1 */
 #define SD_SEND_IF_COND			8	/* R7 */
 
 /* SD application commands */			/* response type */
@@ -108,7 +109,11 @@
 #define SD_ARG_BUS_WIDTH_4		2
 
 /* EXT_CSD fields */
-#define EXT_CSD_BUS_WIDTH		183	/* R/W */
+#define EXT_CSD_BUS_WIDTH		183	/* WO */
+#define EXT_CSD_HS_TIMING		185	/* R/W */
+#define EXT_CSD_REV			192	/* RO */
+#define EXT_CSD_STRUCTURE		194	/* RO */
+#define EXT_CSD_CARD_TYPE		196	/* RO */
 
 /* EXT_CSD field definitions */
 #define EXT_CSD_CMD_SET_NORMAL		(1U << 0)
@@ -120,6 +125,15 @@
 #define EXT_CSD_BUS_WIDTH_4		1	/* 4 bit mode */
 #define EXT_CSD_BUS_WIDTH_8		2	/* 8 bit mode */
 
+/* EXT_CSD_STRUCTURE */
+#define EXT_CSD_STRUCTURE_VER_1_0	0	/* CSD Version No.1.0 */
+#define EXT_CSD_STRUCTURE_VER_1_1	1	/* CSD Version No.1.1 */
+#define EXT_CSD_STRUCTURE_VER_1_2	2	/* Version 4.1-4.2-4.3 */
+
+/* EXT_CSD_CARD_TYPE */
+#define EXT_CSD_CARD_TYPE_26M		(1 << 0)
+#define EXT_CSD_CARD_TYPE_52M		(1 << 1)
+
 /* 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 */
@@ -219,7 +233,15 @@
 #define  SD_CSD_SPEED_25_MHZ		0x32
 #define  SD_CSD_SPEED_50_MHZ		0x5a
 #define SD_CSD_CCC(resp)		MMC_RSP_BITS((resp), 84, 12)
-#define  SD_CSD_CCC_ALL			0x5f5
+#define  SD_CSD_CCC_BASIC		(1 << 0)	/* basic */
+#define  SD_CSD_CCC_BR			(1 << 2)	/* block read */
+#define  SD_CSD_CCC_BW			(1 << 4)	/* block write */
+#define  SD_CSD_CCC_ERACE		(1 << 5)	/* erase */
+#define  SD_CSD_CCC_WP			(1 << 6)	/* write protection */
+#define  SD_CSD_CCC_LC			(1 << 7)	/* lock card */
+#define  SD_CSD_CCC_AS			(1 << 8)	/*application specific*/
+#define  SD_CSD_CCC_IOM			(1 << 9)	/* I/O mode */
+#define  SD_CSD_CCC_SWITCH		(1 << 10)	/* switch */
 #define SD_CSD_READ_BL_LEN(resp)	MMC_RSP_BITS((resp), 80, 4)
 #define SD_CSD_READ_BL_PARTIAL(resp)	MMC_RSP_BITS((resp), 79, 1)
 #define SD_CSD_WRITE_BLK_MISALIGN(resp)	MMC_RSP_BITS((resp), 78, 1)
@@ -273,7 +295,9 @@
 #define SCR_STRUCTURE(scr)		MMC_RSP_BITS((scr), 60, 4)
 #define  SCR_STRUCTURE_VER_1_0		0 /* Version 1.0 */
 #define SCR_SD_SPEC(scr)		MMC_RSP_BITS((scr), 56, 4)
-#define  SCR_SD_SPEC_VER_1_0		0 /* Version 1.0 */
+#define  SCR_SD_SPEC_VER_1_0		0 /* Version 1.0 and 1.01 */
+#define  SCR_SD_SPEC_VER_1_10		1 /* Version 1.10 */
+#define  SCR_SD_SPEC_VER_2		2 /* Version 2.00 or Version 3.0X */
 #define SCR_DATA_STAT_AFTER_ERASE(scr)	MMC_RSP_BITS((scr), 55, 1)
 #define SCR_SD_SECURITY(scr)		MMC_RSP_BITS((scr), 52, 3)
 #define  SCR_SD_SECURITY_NONE		0 /* no security */
@@ -285,6 +309,10 @@
 #define SCR_RESERVED(scr)		MMC_RSP_BITS((scr), 32, 16)
 #define SCR_RESERVED2(scr)		MMC_RSP_BITS((scr), 0, 32)
 
+/* Status of Switch Function */
+#define SFUNC_STATUS_GROUP(status, group) \
+	be16toh(__bitfield((uint32_t *)(status), (7 - (group)) << 4, 16))
+
 /* Might be slow, but it should work on big and little endian systems. */
 #define MMC_RSP_BITS(resp, start, len)	__bitfield((resp), (start)-8, (len))
 static inline int

Index: src/sys/dev/sdmmc/sdmmc_io.c
diff -u src/sys/dev/sdmmc/sdmmc_io.c:1.2 src/sys/dev/sdmmc/sdmmc_io.c:1.3
--- src/sys/dev/sdmmc/sdmmc_io.c:1.2	Sat Dec  5 22:34:43 2009
+++ src/sys/dev/sdmmc/sdmmc_io.c	Thu Oct  7 12:24:23 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmc_io.c,v 1.2 2009/12/05 22:34:43 pooka Exp $	*/
+/*	$NetBSD: sdmmc_io.c,v 1.3 2010/10/07 12:24:23 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmc_io.c,v 1.10 2007/09/17 01:33:33 krw Exp $	*/
 
 /*
@@ -20,7 +20,7 @@
 /* Routines for SD I/O cards. */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdmmc_io.c,v 1.2 2009/12/05 22:34:43 pooka Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc_io.c,v 1.3 2010/10/07 12:24:23 kiyohara Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -158,6 +158,9 @@
 	sc->sc_fn0 = sf0;
 	SIMPLEQ_INSERT_TAIL(&sc->sf_head, sf0, sf_list);
 
+	/* Go to Data Transfer Mode, if possible. */
+	sdmmc_chip_bus_rod(sc->sc_sct, sc->sc_sch, 0);
+
 	/* Verify that the RCA has been set by selecting the card. */
 	error = sdmmc_select_card(sc, sf0);
 	if (error) {
@@ -204,6 +207,14 @@
 		if (sdmmcdebug)
 			sdmmc_print_cis(sf);
 #endif
+
+		if (sc->sc_busclk > sf->csd.tran_speed)
+			sc->sc_busclk = sf->csd.tran_speed;
+		error =
+		    sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk);
+		if (error)
+			aprint_error_dev(sc->sc_dev,
+			    "can't change bus clock\n");
 	}
 
 out:

Index: src/sys/dev/sdmmc/sdmmc_mem.c
diff -u src/sys/dev/sdmmc/sdmmc_mem.c:1.12 src/sys/dev/sdmmc/sdmmc_mem.c:1.13
--- src/sys/dev/sdmmc/sdmmc_mem.c:1.12	Fri Oct  1 09:50:42 2010
+++ src/sys/dev/sdmmc/sdmmc_mem.c	Thu Oct  7 12:24:23 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmc_mem.c,v 1.12 2010/10/01 09:50:42 kiyohara Exp $	*/
+/*	$NetBSD: sdmmc_mem.c,v 1.13 2010/10/07 12:24:23 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmc_mem.c,v 1.10 2009/01/09 10:55:22 jsg Exp $	*/
 
 /*
@@ -46,7 +46,7 @@
 /* Routines for SD/MMC memory cards. */
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.12 2010/10/01 09:50:42 kiyohara Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sdmmc_mem.c,v 1.13 2010/10/07 12:24:23 kiyohara Exp $");
 
 #include <sys/param.h>
 #include <sys/kernel.h>
@@ -66,6 +66,8 @@
 #define DPRINTF(s)	do {} while (/*CONSTCOND*/0)
 #endif
 
+static int sdmmc_mem_sd_init(struct sdmmc_softc *, struct sdmmc_function *);
+static int sdmmc_mem_mmc_init(struct sdmmc_softc *, struct sdmmc_function *);
 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 *);
@@ -74,6 +76,7 @@
 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);
+static int sdmmc_mem_sd_switch(struct sdmmc_function *, int, int, int, void *);
 static int sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t,
     uint8_t);
 static int sdmmc_mem_spi_read_ocr(struct sdmmc_softc *, uint32_t, uint32_t *);
@@ -253,6 +256,10 @@
 			break;
 	}
 
+	if (!ISSET(sc->sc_caps, SMC_CAPS_SPI_MODE))
+		/* Go to Data Transfer Mode, if possible. */
+		sdmmc_chip_bus_rod(sc->sc_sct, sc->sc_sch, 0);
+
 	/*
 	 * All cards are either inactive or awaiting further commands.
 	 * Read the CSDs and decode the raw CID for each card.
@@ -313,6 +320,7 @@
 			SET(sf->flags, SFF_SDHC);
 			csd->capacity = SD_CSD_V2_CAPACITY(resp);
 			csd->read_bl_len = SD_CSD_V2_BL_LEN;
+			csd->ccc = SD_CSD_CCC(resp);
 			break;
 
 		case SD_CSD_CSDVER_1_0:
@@ -356,9 +364,6 @@
 	if ((1 << csd->read_bl_len) > SDMMC_SECTOR_SIZE)
 		csd->capacity *= (1 << csd->read_bl_len) / SDMMC_SECTOR_SIZE;
 
-	if (sc->sc_busclk > csd->tran_speed)
-		sc->sc_busclk = csd->tran_speed;
-
 #ifdef SDMMC_DUMP_CSD
 	sdmmc_print_csd(resp, csd);
 #endif
@@ -436,7 +441,7 @@
 int
 sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
 {
-	int width, value, error = 0;
+	int error = 0;
 
 	SDMMC_LOCK(sc);
 
@@ -452,58 +457,10 @@
 			goto out;
 	}
 
-        /* change bus width if supported */
-	sf->width = 1;
-	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));
-				goto out;
-			}
-			sf->width = 4;
-		}
-	} else if (sf->csd.mmcver >= MMC_CSD_MMCVER_4_0) {
-		if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) {
-			width = 8;
-			value = EXT_CSD_BUS_WIDTH_8;
-		} else if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE)) {
-			width = 4;
-			value = EXT_CSD_BUS_WIDTH_4;
-		} else {
-			width = 1;
-			value = EXT_CSD_BUS_WIDTH_1;
-		}
-
-		if (width != 1) {
-			error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
-			    EXT_CSD_BUS_WIDTH, value);
-			if (error == 0)
-				error = sdmmc_chip_bus_width(sc->sc_sct,
-				    sc->sc_sch, width);
-			else {
-				DPRINTF(("%s: can't change bus width"
-				    " (%d bit)\n", SDMMCDEVNAME(sc), width));
-				goto out;
-			}
-
-			/* XXXX: need bus test? (using by CMD14 & CMD19) */
-
-			sf->width = width;
-		}
-	}
+	if (ISSET(sc->sc_flags, SMF_SD_MODE))
+		error = sdmmc_mem_sd_init(sc, sf);
+	else
+		error = sdmmc_mem_mmc_init(sc, sf);
 
 out:
 	SDMMC_UNLOCK(sc);
@@ -617,6 +574,215 @@
 }
 
 static int
+sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
+{
+	struct {
+		int v;
+		int freq;
+	} switch_group0_functions [] = {
+		/* Default/SDR12 */
+		{ MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V |
+		  MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V,	 25000 },
+
+		/* High-Speed/SDR25 */
+		{ MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V |
+		  MMC_OCR_3_2V_3_3V | MMC_OCR_3_3V_3_4V,	 50000 },
+
+		/* SDR50 */
+		{ MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V,	100000 },
+
+		/* SDR104 */
+		{ MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V,	208000 },
+
+		/* DDR50 */
+		{ MMC_OCR_1_7V_1_8V | MMC_OCR_1_8V_1_9V,	 50000 },
+	};
+	int host_ocr, support_func, best_func, error, g, i;
+	char status[64];
+
+	error = sdmmc_mem_send_scr(sc, sf, sf->raw_scr);
+	if (error) {
+		aprint_error_dev(sc->sc_dev, "SD_SEND_SCR send failed.\n");
+		return error;
+	}
+	error = sdmmc_mem_decode_scr(sc, sf);
+	if (error)
+		return error;
+
+	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) {
+			aprint_error_dev(sc->sc_dev,
+			    "can't change bus width (%d bit)\n", 4);
+			return error;
+		}
+		sf->width = 4;
+	} else
+		sf->width = 1;
+
+	if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 &&
+	    ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) {
+		error = sdmmc_mem_sd_switch(sf, 0, 1, 0, status);
+		if (error) {
+			aprint_error_dev(sc->sc_dev,
+			    "switch func mode 0 failed\n");
+			return error;
+		}
+
+		host_ocr = sdmmc_chip_host_ocr(sc->sc_sct, sc->sc_sch);
+		support_func = SFUNC_STATUS_GROUP(status, 1);
+		best_func = 0;
+		for (i = 0, g = 1;
+		    i < __arraycount(switch_group0_functions); i++, g <<= 1) {
+			if (!(switch_group0_functions[i].v & host_ocr))
+				continue;
+			if (g & support_func)
+				best_func = i;
+		}
+		if (best_func != 0) {
+			error =
+			    sdmmc_mem_sd_switch(sf, 1, 1, best_func, status);
+			if (error) {
+				aprint_error_dev(sc->sc_dev,
+				    "switch func mode 1 failed:"
+				    " group 1 function %d(0x%2x)\n",
+				    best_func, support_func);
+				return error;
+			}
+			sf->csd.tran_speed =
+			    switch_group0_functions[best_func].freq;
+
+			/* Wait 400KHz x 8 clock */
+			delay(1);
+			if (sc->sc_busclk > sf->csd.tran_speed)
+				sc->sc_busclk = sf->csd.tran_speed;
+
+			error = sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch,
+			    sc->sc_busclk);
+			if (error) {
+				aprint_error_dev(sc->sc_dev,
+				    "can't change bus clock\n");
+				return error;
+			}
+		} else
+			if (sc->sc_busclk > sf->csd.tran_speed)
+				sc->sc_busclk = sf->csd.tran_speed;
+	}
+
+	return 0;
+}
+
+static int
+sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
+{
+	int width, value, hs_timing, error;
+	char ext_csd[512];
+
+	if (sf->csd.mmcver >= MMC_CSD_MMCVER_4_0) {
+		error = sdmmc_mem_send_cxd_data(sc,
+		    MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd));
+		if (error) {
+			aprint_error_dev(sc->sc_dev, "can't read EXT_CSD\n");
+			return error;
+		}
+		if (ext_csd[EXT_CSD_STRUCTURE] > EXT_CSD_STRUCTURE_VER_1_2) {
+			aprint_error_dev(sc->sc_dev,
+			    "unrecognised future version\n");
+			return error;
+		}
+		hs_timing = 0;
+		switch (ext_csd[EXT_CSD_CARD_TYPE]) {
+		case EXT_CSD_CARD_TYPE_26M:
+			sf->csd.tran_speed = 26000;	/* 26MHz */
+			break;
+
+		case EXT_CSD_CARD_TYPE_52M | EXT_CSD_CARD_TYPE_26M:
+			sf->csd.tran_speed = 52000;	/* 52MHz */
+			hs_timing = 1;
+
+			error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
+			    EXT_CSD_HS_TIMING, hs_timing);
+			if (error) {
+				aprint_error_dev(sc->sc_dev,
+				    "can't change high speed\n");
+				return error;
+			}
+			break;
+
+		default:
+			aprint_error_dev(sc->sc_dev,
+			    "unknwon CARD_TYPE: 0x%x\n",
+			    ext_csd[EXT_CSD_CARD_TYPE]);
+			return error;
+		}
+		if (sc->sc_busclk > sf->csd.tran_speed)
+			sc->sc_busclk = sf->csd.tran_speed;
+		error =
+		    sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk);
+		if (error) {
+			aprint_error_dev(sc->sc_dev,
+			    "can't change bus clock\n");
+			return error;
+		}
+		if (hs_timing) {
+			error = sdmmc_mem_send_cxd_data(sc,
+			    MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd));
+			if (error) {
+				aprint_error_dev(sc->sc_dev,
+				    "can't re-read EXT_CSD\n");
+				return error;
+			}
+			if (ext_csd[EXT_CSD_HS_TIMING] != hs_timing) {
+				aprint_error_dev(sc->sc_dev,
+				    "HS_TIMING set failed\n");
+				return EINVAL;
+			}
+		}
+
+		if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) {
+			width = 8;
+			value = EXT_CSD_BUS_WIDTH_8;
+		} else if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE)) {
+			width = 4;
+			value = EXT_CSD_BUS_WIDTH_4;
+		} else {
+			width = 1;
+			value = EXT_CSD_BUS_WIDTH_1;
+		}
+
+		if (width != 1) {
+			error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
+			    EXT_CSD_BUS_WIDTH, value);
+			if (error == 0)
+				error = sdmmc_chip_bus_width(sc->sc_sct,
+				    sc->sc_sch, width);
+			else {
+				DPRINTF(("%s: can't change bus width"
+				    " (%d bit)\n", SDMMCDEVNAME(sc), width));
+				return error;
+			}
+
+			/* XXXX: need bus test? (using by CMD14 & CMD19) */
+		}
+		sf->width = width;
+	} else {
+		if (sc->sc_busclk > sf->csd.tran_speed)
+			sc->sc_busclk = sf->csd.tran_speed;
+		error =
+		    sdmmc_chip_bus_clock(sc->sc_sct, sc->sc_sch, sc->sc_busclk);
+		if (error) {
+			aprint_error_dev(sc->sc_dev,
+			    "can't change bus clock\n");
+			return error;
+		}
+		sf->width = 1;
+	}
+
+	return 0;
+}
+
+static int
 sdmmc_mem_send_cid(struct sdmmc_softc *sc, sdmmc_response *resp)
 {
 	struct sdmmc_command cmd;
@@ -842,19 +1008,6 @@
 	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)
 {
@@ -889,6 +1042,84 @@
 }
 
 static int
+sdmmc_mem_sd_switch(struct sdmmc_function *sf, int mode, int group,
+    int function, void *status)
+{
+	struct sdmmc_softc *sc = sf->sc;
+	struct sdmmc_command cmd;
+	bus_dma_segment_t ds[1];
+	void *ptr = NULL;
+	int gsft, rseg, error = 0;
+	const int statlen = 64;
+
+	if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 &&
+	    !ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH))
+		return EINVAL;
+
+	if (group <= 0 || group > 6 ||
+	    function < 0 || function > 16)
+		return EINVAL;
+
+	gsft = (group - 1) << 2;
+
+	if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+		error = bus_dmamem_alloc(sc->sc_dmat, statlen, PAGE_SIZE, 0, ds,
+		    1, &rseg, BUS_DMA_NOWAIT);
+		if (error)
+			goto out;
+		error = bus_dmamem_map(sc->sc_dmat, ds, 1, statlen, &ptr,
+		    BUS_DMA_NOWAIT);
+		if (error)
+			goto dmamem_free;
+		error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmap, ptr, statlen,
+		    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, statlen,
+		    BUS_DMASYNC_PREREAD);
+	} else {
+		ptr = malloc(statlen, M_DEVBUF, M_NOWAIT | M_ZERO);
+		if (ptr == NULL)
+			goto out;
+	}
+
+	memset(&cmd, 0, sizeof(cmd));
+	cmd.c_data = ptr;
+	cmd.c_datalen = statlen;
+	cmd.c_blklen = statlen;
+	cmd.c_opcode = SD_SEND_SWITCH_FUNC;
+	cmd.c_arg =
+	    (!!mode << 31) | (function << gsft) | (0x00ffffff & ~(0xf << gsft));
+	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;
+
+	error = sdmmc_mmc_command(sc, &cmd);
+	if (error == 0) {
+		if (ISSET(sc->sc_caps, SMC_CAPS_DMA)) {
+			bus_dmamap_sync(sc->sc_dmat, sc->sc_dmap, 0, statlen,
+			    BUS_DMASYNC_POSTREAD);
+		}
+		memcpy(status, ptr, statlen);
+	}
+
+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, statlen);
+dmamem_free:
+			bus_dmamem_free(sc->sc_dmat, ds, rseg);
+		} else {
+			free(ptr, M_DEVBUF);
+		}
+	}
+	return error;
+}
+
+static int
 sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index,
     uint8_t value)
 {

Index: src/sys/dev/sdmmc/sdmmcvar.h
diff -u src/sys/dev/sdmmc/sdmmcvar.h:1.7 src/sys/dev/sdmmc/sdmmcvar.h:1.8
--- src/sys/dev/sdmmc/sdmmcvar.h:1.7	Fri Oct  1 09:50:42 2010
+++ src/sys/dev/sdmmc/sdmmcvar.h	Thu Oct  7 12:24:23 2010
@@ -1,4 +1,4 @@
-/*	$NetBSD: sdmmcvar.h,v 1.7 2010/10/01 09:50:42 kiyohara Exp $	*/
+/*	$NetBSD: sdmmcvar.h,v 1.8 2010/10/07 12:24:23 kiyohara Exp $	*/
 /*	$OpenBSD: sdmmcvar.h,v 1.13 2009/01/09 10:55:22 jsg Exp $	*/
 
 /*
@@ -40,6 +40,7 @@
 	int	write_bl_len;	/* block length for writes */
 	int	r2w_factor;
 	int	tran_speed;	/* transfer speed (kbit/s) */
+	int	ccc;		/* Card Command Class for SD */
 	/* ... */
 };
 
@@ -163,6 +164,7 @@
 	/* common members */
 	struct sdmmc_softc *sc;		/* card slot softc */
 	uint16_t rca;			/* relative card address */
+	int interface;			/* SD/MMC:0, SDIO:standard interface */
 	int width;			/* bus width */
 	int flags;
 #define SFF_ERROR		0x0001	/* function is poo; ignore it */
@@ -178,6 +180,7 @@
 	sdmmc_response raw_cid;		/* temp. storage for decoding */
 	uint32_t raw_scr[2];
 	struct sdmmc_scr scr;		/* decoded CSR value */
+
 	void *bbuf;			/* bounce buffer */
 	bus_dmamap_t bbuf_dmap;		/* DMA map for bounce buffer */
 };
@@ -252,6 +255,7 @@
 struct sdmmc_attach_args {
 	uint16_t manufacturer;
 	uint16_t product;
+	int interface;
 	struct sdmmc_function *sf;
 };
 
@@ -329,7 +333,6 @@
 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