summaryrefslogtreecommitdiff
path: root/sys/dev/sdmmc
diff options
context:
space:
mode:
authorRaphael Graf <rapha@cvs.openbsd.org>2013-09-12 11:54:05 +0000
committerRaphael Graf <rapha@cvs.openbsd.org>2013-09-12 11:54:05 +0000
commitc35ed72d8027a9e01ee567685263d88b06240bfd (patch)
treeee395fb8ecb1bf80bc29a0ba60f3bc365ec9c4e4 /sys/dev/sdmmc
parent1e389c2ada08c66f8504f0f08915546624b59f24 (diff)
Add basic support for eMMC memory.
Heavily based on netbsd. Tested by dlg@, bcallah@ (sdhc), stsp@ (rstx) and me (ommmc). ok patrick@
Diffstat (limited to 'sys/dev/sdmmc')
-rw-r--r--sys/dev/sdmmc/sdmmc.c6
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c160
-rw-r--r--sys/dev/sdmmc/sdmmcchip.h3
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h41
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h15
5 files changed, 214 insertions, 11 deletions
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
index 295bce3fab1..2129b3fbb6c 100644
--- a/sys/dev/sdmmc/sdmmc.c
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc.c,v 1.27 2013/05/31 21:28:32 deraadt Exp $ */
+/* $OpenBSD: sdmmc.c,v 1.28 2013/09/12 11:54:04 rapha Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -105,6 +105,7 @@ sdmmc_attach(struct device *parent, struct device *self, void *aux)
sc->sct = saa->sct;
sc->sch = saa->sch;
sc->sc_flags = saa->flags;
+ sc->sc_caps = saa->caps;
sc->sc_max_xfer = saa->max_xfer;
SIMPLEQ_INIT(&sc->sf_head);
@@ -571,6 +572,9 @@ sdmmc_app_command(struct sdmmc_softc *sc, struct sdmmc_command *cmd)
bzero(&acmd, sizeof acmd);
acmd.c_opcode = MMC_APP_CMD;
acmd.c_arg = 0;
+ if (sc->sc_card != NULL) {
+ acmd.c_arg = sc->sc_card->rca << 16;
+ }
acmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
error = sdmmc_mmc_command(sc, &acmd);
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
index eb87d33ea4b..39940e26158 100644
--- a/sys/dev/sdmmc/sdmmc_mem.c
+++ b/sys/dev/sdmmc/sdmmc_mem.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc_mem.c,v 1.16 2010/08/24 14:52:23 blambert Exp $ */
+/* $OpenBSD: sdmmc_mem.c,v 1.17 2013/09/12 11:54:04 rapha Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -37,6 +37,12 @@ void sdmmc_print_cid(struct sdmmc_cid *);
int sdmmc_mem_send_op_cond(struct sdmmc_softc *, u_int32_t, u_int32_t *);
int sdmmc_mem_set_blocklen(struct sdmmc_softc *, struct sdmmc_function *);
+int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
+int sdmmc_mem_mmc_switch(struct sdmmc_function *, uint8_t, uint8_t, uint8_t);
+
+int sdmmc_mem_sd_init(struct sdmmc_softc *, struct sdmmc_function *);
+int sdmmc_mem_mmc_init(struct sdmmc_softc *, struct sdmmc_function *);
+
#ifdef SDMMC_DEBUG
#define DPRINTF(s) printf s
#else
@@ -253,16 +259,17 @@ sdmmc_decode_csd(struct sdmmc_softc *sc, sdmmc_response resp,
} else {
csd->csdver = MMC_CSD_CSDVER(resp);
- if (csd->csdver != MMC_CSD_CSDVER_1_0 &&
- csd->csdver != MMC_CSD_CSDVER_2_0) {
+ if (csd->csdver == MMC_CSD_CSDVER_1_0 ||
+ csd->csdver == MMC_CSD_CSDVER_2_0 ||
+ csd->csdver == MMC_CSD_CSDVER_EXT_CSD) {
+ csd->mmcver = MMC_CSD_MMCVER(resp);
+ csd->capacity = MMC_CSD_CAPACITY(resp);
+ csd->read_bl_len = MMC_CSD_READ_BL_LEN(resp);
+ } else {
printf("%s: unknown MMC CSD structure version 0x%x\n",
DEVNAME(sc), csd->csdver);
return 1;
}
-
- csd->mmcver = MMC_CSD_MMCVER(resp);
- csd->capacity = MMC_CSD_CAPACITY(resp);
- csd->read_bl_len = MMC_CSD_READ_BL_LEN(resp);
}
csd->sector_size = MIN(1 << csd->read_bl_len,
sdmmc_chip_host_maxblklen(sc->sct, sc->sch));
@@ -323,6 +330,59 @@ sdmmc_print_cid(struct sdmmc_cid *cid)
}
#endif
+int
+sdmmc_mem_send_cxd_data(struct sdmmc_softc *sc, int opcode, void *data,
+ size_t datalen)
+{
+ struct sdmmc_command cmd;
+ void *ptr = NULL;
+ int error = 0;
+
+ ptr = malloc(datalen, M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (ptr == NULL) {
+ error = ENOMEM;
+ 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;
+ if (opcode == MMC_SEND_EXT_CSD)
+ SET(cmd.c_flags, SCF_RSP_R1);
+ else
+ SET(cmd.c_flags, SCF_RSP_R2);
+
+ error = sdmmc_mmc_command(sc, &cmd);
+ if (error == 0)
+ memcpy(data, ptr, datalen);
+
+out:
+ if (ptr != NULL)
+ free(ptr, M_DEVBUF);
+
+ return error;
+}
+
+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_R1B | SCF_CMD_AC;
+
+ return sdmmc_mmc_command(sc, &cmd);
+}
+
/*
* Initialize a SD/MMC memory card.
*/
@@ -336,6 +396,92 @@ sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
if (sdmmc_select_card(sc, sf) != 0 ||
sdmmc_mem_set_blocklen(sc, sf) != 0)
error = 1;
+
+ if (ISSET(sc->sc_flags, SMF_SD_MODE))
+ error = sdmmc_mem_sd_init(sc, sf);
+ else
+ error = sdmmc_mem_mmc_init(sc, sf);
+
+ return error;
+}
+
+int
+sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
+{
+ /* XXX */
+
+ return 0;
+}
+
+int
+sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
+{
+ int error = 0;
+ u_int8_t ext_csd[512];
+ int speed = 0;
+ int hs_timing = 0;
+
+ if (sf->csd.mmcver >= MMC_CSD_MMCVER_4_0) {
+ /* read EXT_CSD */
+ error = sdmmc_mem_send_cxd_data(sc,
+ MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd));
+ if (error != 0) {
+ SET(sf->flags, SFF_ERROR);
+ printf("%s: can't read EXT_CSD\n", DEVNAME(sc));
+ return error;
+ }
+
+ switch (ext_csd[EXT_CSD_CARD_TYPE]) {
+ case EXT_CSD_CARD_TYPE_26M:
+ speed = 26000;
+ break;
+ case EXT_CSD_CARD_TYPE_52M:
+ case EXT_CSD_CARD_TYPE_52M_V18:
+ case EXT_CSD_CARD_TYPE_52M_V12:
+ case EXT_CSD_CARD_TYPE_52M_V12_18:
+ speed = 52000;
+ hs_timing = 1;
+ break;
+ default:
+ printf("%s: unknown CARD_TYPE 0x%x\n", DEVNAME(sc),
+ ext_csd[EXT_CSD_CARD_TYPE]);
+ }
+ if (!ISSET(sc->sc_caps, SMC_CAPS_MMC_HIGHSPEED))
+ hs_timing = 0;
+
+ if (hs_timing) {
+ /* switch to high speed timing */
+ error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, hs_timing);
+ if (error != 0) {
+ printf("%s: can't change high speed\n",
+ DEVNAME(sc));
+ return error;
+ }
+ }
+
+ error =
+ sdmmc_chip_bus_clock(sc->sct, sc->sch, speed);
+ if (error != 0) {
+ printf("%s: can't change bus clock\n", DEVNAME(sc));
+ return error;
+ }
+
+ if (hs_timing) {
+ /* read EXT_CSD again */
+ error = sdmmc_mem_send_cxd_data(sc,
+ MMC_SEND_EXT_CSD, ext_csd, sizeof(ext_csd));
+ if (error != 0) {
+ printf("%s: can't re-read EXT_CSD\n", DEVNAME(sc));
+ return error;
+ }
+ if (ext_csd[EXT_CSD_HS_TIMING] != 1) {
+ printf("%s, HS_TIMING set failed\n", DEVNAME(sc));
+ return EINVAL;
+ }
+ }
+ }
+
return error;
}
diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h
index 1846b1d192a..14c772fafb1 100644
--- a/sys/dev/sdmmc/sdmmcchip.h
+++ b/sys/dev/sdmmc/sdmmcchip.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcchip.h,v 1.4 2009/02/20 19:16:35 miod Exp $ */
+/* $OpenBSD: sdmmcchip.h,v 1.5 2013/09/12 11:54:04 rapha Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -78,6 +78,7 @@ struct sdmmcbus_attach_args {
sdmmc_chipset_tag_t sct;
sdmmc_chipset_handle_t sch;
int flags;
+ int caps;
long max_xfer;
};
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
index 9b182deb5f8..99660761c7f 100644
--- a/sys/dev/sdmmc/sdmmcreg.h
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcreg.h,v 1.4 2009/01/09 10:55:22 jsg Exp $ */
+/* $OpenBSD: sdmmcreg.h,v 1.5 2013/09/12 11:54:04 rapha Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -24,7 +24,9 @@
#define MMC_SEND_OP_COND 1 /* R3 */
#define MMC_ALL_SEND_CID 2 /* R2 */
#define MMC_SET_RELATIVE_ADDR 3 /* R1 */
+#define MMC_SWITCH 6 /* R1B */
#define MMC_SELECT_CARD 7 /* R1 */
+#define MMC_SEND_EXT_CSD 8 /* R1 */
#define MMC_SEND_CSD 9 /* R2 */
#define MMC_STOP_TRANSMISSION 12 /* R1B */
#define MMC_SEND_STATUS 13 /* R1 */
@@ -87,10 +89,47 @@
#define SD_ARG_BUS_WIDTH_1 0
#define SD_ARG_BUS_WIDTH_4 2
+/* EXT_CSD fields */
+#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)
+#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
+#define EXT_CSD_BUS_WIDTH_4 1
+#define EXT_CSD_BUS_WIDTH_8 2
+
+/* EXT_CSD_CARD_TYPE */
+/* The only currently valid values for this field are 0x01, 0x03, 0x07,
+ * 0x0B and 0x0F. */
+#define EXT_CSD_CARD_TYPE_F_26M (1 << 0)
+#define EXT_CSD_CARD_TYPE_F_52M (1 << 1)
+#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2)
+#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3)
+#define EXT_CSD_CARD_TYPE_26M 0x01
+#define EXT_CSD_CARD_TYPE_52M 0x03
+#define EXT_CSD_CARD_TYPE_52M_V18 0x07
+#define EXT_CSD_CARD_TYPE_52M_V12 0x0b
+#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f
+
+/* 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 */
+
/* MMC R2 response (CSD) */
#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2)
#define MMC_CSD_CSDVER_1_0 1
#define MMC_CSD_CSDVER_2_0 2
+#define MMC_CSD_CSDVER_EXT_CSD 3
#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4)
#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */
#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */
diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h
index 8da8cc6851d..1da8de541fa 100644
--- a/sys/dev/sdmmc/sdmmcvar.h
+++ b/sys/dev/sdmmc/sdmmcvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcvar.h,v 1.21 2011/11/10 14:24:29 uwe Exp $ */
+/* $OpenBSD: sdmmcvar.h,v 1.22 2013/09/12 11:54:04 rapha Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -164,6 +164,19 @@ struct sdmmc_softc {
#define SMF_CARD_ATTACHED 0x0020 /* card driver(s) attached */
#define SMF_STOP_AFTER_MULTIPLE 0x0040 /* send a stop after a multiple cmd */
#define SMF_CONFIG_PENDING 0x0080 /* config_pending_incr() called */
+
+ uint32_t sc_caps; /* host capability */
+#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 */
+#define SMC_CAPS_8BIT_MODE 0x0040 /* 8-bits data bus width */
+#define SMC_CAPS_MULTI_SEG_DMA 0x0080 /* multiple segment DMA transfer */
+#define SMC_CAPS_SD_HIGHSPEED 0x0100 /* SD high-speed timing */
+#define SMC_CAPS_MMC_HIGHSPEED 0x0200 /* MMC high-speed timing */
+
int sc_function_count; /* number of I/O functions (SDIO) */
struct sdmmc_function *sc_card; /* selected card */
struct sdmmc_function *sc_fn0; /* function 0, the card itself */