summaryrefslogtreecommitdiff
path: root/sys/dev/sdmmc
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2016-05-01 16:04:40 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2016-05-01 16:04:40 +0000
commit6bd1ebc4f6372213e2d48e527b38f1d9c3c14f0e (patch)
tree80ea831bd0078578a4716851e31069b80e26e830 /sys/dev/sdmmc
parent9350001d745387d778c568a6f38cc76cb9f0beb8 (diff)
Add support for changing the bus width to the sdmmc subsystem and the sdhc(4)
controller. Use this to switch SD cards to a 4-bit bus if they support it. ok deraadt@, jsg@
Diffstat (limited to 'sys/dev/sdmmc')
-rw-r--r--sys/dev/sdmmc/sdhc.c35
-rw-r--r--sys/dev/sdmmc/sdmmc.c34
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c123
-rw-r--r--sys/dev/sdmmc/sdmmcchip.h5
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h26
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h8
6 files changed, 192 insertions, 39 deletions
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c
index a85044b8475..dc0350a729b 100644
--- a/sys/dev/sdmmc/sdhc.c
+++ b/sys/dev/sdmmc/sdhc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdhc.c,v 1.45 2016/04/30 13:33:35 kettenis Exp $ */
+/* $OpenBSD: sdhc.c,v 1.46 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -87,6 +87,7 @@ int sdhc_host_maxblklen(sdmmc_chipset_handle_t);
int sdhc_card_detect(sdmmc_chipset_handle_t);
int sdhc_bus_power(sdmmc_chipset_handle_t, u_int32_t);
int sdhc_bus_clock(sdmmc_chipset_handle_t, int);
+int sdhc_bus_width(sdmmc_chipset_handle_t, int);
void sdhc_card_intr_mask(sdmmc_chipset_handle_t, int);
void sdhc_card_intr_ack(sdmmc_chipset_handle_t);
void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
@@ -117,6 +118,7 @@ struct sdmmc_chip_functions sdhc_functions = {
/* bus power and clock frequency */
sdhc_bus_power,
sdhc_bus_clock,
+ sdhc_bus_width,
/* command execution */
sdhc_exec_command,
/* card interrupt */
@@ -289,6 +291,7 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
saa.saa_busname = "sdmmc";
saa.sct = &sdhc_functions;
saa.sch = hp;
+ saa.caps = SMC_CAPS_4BIT_MODE;
saa.dmat = sc->sc_dmat;
if (ISSET(hp->flags, SHF_USE_DMA))
saa.caps |= SMC_CAPS_DMA;
@@ -599,6 +602,36 @@ ret:
return error;
}
+int
+sdhc_bus_width(sdmmc_chipset_handle_t sch, int width)
+{
+ struct sdhc_host *hp = (struct sdhc_host *)sch;
+ int reg;
+ int s;
+
+ if (width != 1 && width != 4 && width != 8)
+ return 1;
+
+ s = splsdmmc();
+
+ reg = HREAD1(hp, SDHC_HOST_CTL);
+ reg &= ~SDHC_4BIT_MODE;
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ reg &= ~SDHC_8BIT_MODE;
+ }
+ if (width == 4) {
+ reg |= SDHC_4BIT_MODE;
+ } else if (width == 8) {
+ KASSERT(SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3);
+ reg |= SDHC_8BIT_MODE;
+ }
+ HWRITE1(hp, SDHC_HOST_CTL, reg);
+
+ splx(s);
+
+ return 0;
+}
+
void
sdhc_card_intr_mask(sdmmc_chipset_handle_t sch, int enable)
{
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
index 4ba2b65e1e3..72b793e1d7a 100644
--- a/sys/dev/sdmmc/sdmmc.c
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc.c,v 1.40 2016/04/30 11:32:23 kettenis Exp $ */
+/* $OpenBSD: sdmmc.c,v 1.41 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -60,7 +60,6 @@ int sdmmc_enable(struct sdmmc_softc *);
void sdmmc_disable(struct sdmmc_softc *);
int sdmmc_scan(struct sdmmc_softc *);
int sdmmc_init(struct sdmmc_softc *);
-int sdmmc_set_bus_width(struct sdmmc_function *);
#ifdef SDMMC_IOCTL
int sdmmc_ioctl(struct device *, u_long, caddr_t);
#endif
@@ -700,37 +699,6 @@ sdmmc_set_relative_addr(struct sdmmc_softc *sc,
return 0;
}
-/*
- * Switch card and host to the maximum supported bus width.
- */
-int
-sdmmc_set_bus_width(struct sdmmc_function *sf)
-{
- struct sdmmc_softc *sc = sf->sc;
- struct sdmmc_command cmd;
- int error;
-
- rw_enter_write(&sc->sc_lock);
-
- if (!ISSET(sc->sc_flags, SMF_SD_MODE)) {
- rw_exit(&sc->sc_lock);
- return EOPNOTSUPP;
- }
-
- if ((error = sdmmc_select_card(sc, sf)) != 0) {
- rw_exit(&sc->sc_lock);
- return error;
- }
-
- bzero(&cmd, sizeof cmd);
- cmd.c_opcode = SD_APP_SET_BUS_WIDTH;
- cmd.c_arg = SD_ARG_BUS_WIDTH_4;
- cmd.c_flags = SCF_CMD_AC | SCF_RSP_R1;
- error = sdmmc_app_command(sc, &cmd);
- rw_exit(&sc->sc_lock);
- return error;
-}
-
int
sdmmc_select_card(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
index 188d3392d5b..9344e3b8ec8 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.23 2016/04/30 11:32:23 kettenis Exp $ */
+/* $OpenBSD: sdmmc_mem.c,v 1.24 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -37,7 +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_scr(struct sdmmc_softc *, uint32_t *);
+int sdmmc_mem_decode_scr(struct sdmmc_softc *, uint32_t *,
+ struct sdmmc_function *);
+
int sdmmc_mem_send_cxd_data(struct sdmmc_softc *, int, void *, size_t);
+int sdmmc_set_bus_width(struct sdmmc_function *, int);
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 *);
@@ -339,6 +344,70 @@ sdmmc_print_cid(struct sdmmc_cid *cid)
#endif
int
+sdmmc_mem_send_scr(struct sdmmc_softc *sc, uint32_t *scr)
+{
+ struct sdmmc_command cmd;
+ void *ptr = NULL;
+ int datalen = 8;
+ int error = 0;
+
+ 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;
+ cmd.c_opcode = SD_APP_SEND_SCR;
+
+ error = sdmmc_app_command(sc, &cmd);
+ if (error == 0) {
+ memcpy(scr, ptr, datalen);
+ }
+
+out:
+ if (ptr != NULL)
+ free(ptr, M_DEVBUF, datalen);
+
+ return error;
+}
+
+int
+sdmmc_mem_decode_scr(struct sdmmc_softc *sc, uint32_t *raw_scr,
+ struct sdmmc_function *sf)
+{
+ sdmmc_response resp;
+ int ver;
+
+ memset(resp, 0, sizeof(resp));
+ /*
+ * Change the raw SCR to a response.
+ */
+ resp[0] = be32toh(raw_scr[1]) >> 8; // LSW
+ resp[1] = be32toh(raw_scr[0]); // MSW
+ resp[0] |= (resp[1] & 0xff) << 24;
+ resp[1] >>= 8;
+
+ ver = SCR_STRUCTURE(resp);
+ sf->scr.sd_spec = SCR_SD_SPEC(resp);
+ sf->scr.bus_width = SCR_SD_BUS_WIDTHS(resp);
+
+ DPRINTF(("%s: %s: %08x%08x ver=%d, spec=%d, bus width=%d\n",
+ DEVNAME(sc), __func__, resp[1], resp[0],
+ ver, sf->scr.sd_spec, sf->scr.bus_width));
+
+ if (ver != 0) {
+ DPRINTF(("%s: unknown SCR structure version: %d\n",
+ DEVNAME(sc), ver));
+ return EINVAL;
+ }
+ return 0;
+}
+
+int
sdmmc_mem_send_cxd_data(struct sdmmc_softc *sc, int opcode, void *data,
size_t datalen)
{
@@ -376,6 +445,36 @@ out:
}
int
+sdmmc_set_bus_width(struct sdmmc_function *sf, int width)
+{
+ struct sdmmc_softc *sc = sf->sc;
+ struct sdmmc_command cmd;
+ int error;
+
+ 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, &cmd);
+ if (error == 0)
+ error = sdmmc_chip_bus_width(sc->sct, sc->sch, width);
+ return error;
+}
+
+int
sdmmc_mem_mmc_switch(struct sdmmc_function *sf, uint8_t set, uint8_t index,
uint8_t value)
{
@@ -416,7 +515,27 @@ sdmmc_mem_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
int
sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
- /* XXX */
+ uint32_t raw_scr[2];
+ int error;
+
+ error = sdmmc_mem_send_scr(sc, raw_scr);
+ if (error) {
+ printf("%s: SD_SEND_SCR send failed\n", DEVNAME(sc));
+ return error;
+ }
+ error = sdmmc_mem_decode_scr(sc, raw_scr, sf);
+ if (error)
+ return error;
+
+ if (ISSET(sc->sc_caps, SMC_CAPS_4BIT_MODE) &&
+ ISSET(sf->scr.bus_width, SCR_SD_BUS_WIDTHS_4BIT)) {
+ DPRINTF(("%s: change bus width\n", DEVNAME(sc)));
+ error = sdmmc_set_bus_width(sf, 4);
+ if (error) {
+ printf("%s: can't change bus width\n", DEVNAME(sc));
+ return error;
+ }
+ }
return 0;
}
diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h
index 4b7eedd5296..20d5e3aa59b 100644
--- a/sys/dev/sdmmc/sdmmcchip.h
+++ b/sys/dev/sdmmc/sdmmcchip.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcchip.h,v 1.6 2016/04/30 11:32:23 kettenis Exp $ */
+/* $OpenBSD: sdmmcchip.h,v 1.7 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -37,6 +37,7 @@ struct sdmmc_chip_functions {
/* bus power and clock frequency */
int (*bus_power)(sdmmc_chipset_handle_t, u_int32_t);
int (*bus_clock)(sdmmc_chipset_handle_t, int);
+ int (*bus_width)(sdmmc_chipset_handle_t, int);
/* command execution */
void (*exec_command)(sdmmc_chipset_handle_t,
struct sdmmc_command *);
@@ -61,6 +62,8 @@ struct sdmmc_chip_functions {
((tag)->bus_power((handle), (ocr)))
#define sdmmc_chip_bus_clock(tag, handle, freq) \
((tag)->bus_clock((handle), (freq)))
+#define sdmmc_chip_bus_width(tag, handle, width) \
+ ((tag)->bus_width((handle), (width)))
/* command execution */
#define sdmmc_chip_exec_command(tag, handle, cmdp) \
((tag)->exec_command((handle), (cmdp)))
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
index 57f61b125d9..7036217cc69 100644
--- a/sys/dev/sdmmc/sdmmcreg.h
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcreg.h,v 1.8 2016/01/10 14:11:43 kettenis Exp $ */
+/* $OpenBSD: sdmmcreg.h,v 1.9 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -45,6 +45,7 @@
/* SD application commands */ /* response type */
#define SD_APP_SET_BUS_WIDTH 6 /* R1 */
#define SD_APP_OP_COND 41 /* R3 */
+#define SD_APP_SEND_SCR 51 /* R1 */
/* OCR bits */
#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */
@@ -236,6 +237,29 @@
#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32)
#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12)
+/* SCR (SD Configuration Register) */
+#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 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 */
+#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */
+#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */
+#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4)
+#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */
+#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */
+#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1)
+#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4)
+#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1)
+#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8)
+#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1)
+#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1)
+#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32)
+
/* 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
diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h
index d2d929ed6da..e2867aa23f2 100644
--- a/sys/dev/sdmmc/sdmmcvar.h
+++ b/sys/dev/sdmmc/sdmmcvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcvar.h,v 1.23 2016/04/30 11:32:23 kettenis Exp $ */
+/* $OpenBSD: sdmmcvar.h,v 1.24 2016/05/01 16:04:39 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -48,6 +48,11 @@ struct sdmmc_cid {
int mdt; /* manufacturing date */
};
+struct sdmmc_scr {
+ int sd_spec;
+ int bus_width;
+};
+
typedef u_int32_t sdmmc_response[4];
struct sdmmc_softc;
@@ -149,6 +154,7 @@ struct sdmmc_function {
struct sdmmc_csd csd; /* decoded CSD value */
struct sdmmc_cid cid; /* decoded CID value */
sdmmc_response raw_cid; /* temp. storage for decoding */
+ struct sdmmc_scr scr; /* decoded SCR value */
};
/*