diff options
author | Raphael Graf <rapha@cvs.openbsd.org> | 2013-09-12 11:54:05 +0000 |
---|---|---|
committer | Raphael Graf <rapha@cvs.openbsd.org> | 2013-09-12 11:54:05 +0000 |
commit | c35ed72d8027a9e01ee567685263d88b06240bfd (patch) | |
tree | ee395fb8ecb1bf80bc29a0ba60f3bc365ec9c4e4 /sys/dev/sdmmc | |
parent | 1e389c2ada08c66f8504f0f08915546624b59f24 (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.c | 6 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmc_mem.c | 160 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcchip.h | 3 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcreg.h | 41 | ||||
-rw-r--r-- | sys/dev/sdmmc/sdmmcvar.h | 15 |
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 */ |