summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2016-05-05 11:01:09 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2016-05-05 11:01:09 +0000
commit6251683a85f7efb0ecd95910fb03ebd1c20b30ac (patch)
treed031f5ff4c52f4912000d71aab493ad77bcf7bdf
parent8f0bc08bb64f7d54716657b84582e0e73d6a1d71 (diff)
Add Dual Data Rate support for eMMC at 52MHz.
-rw-r--r--sys/arch/arm/xscale/pxa2x0_mmc.c6
-rw-r--r--sys/arch/armv7/exynos/exesdhc.c6
-rw-r--r--sys/arch/armv7/imx/imxesdhc.c6
-rw-r--r--sys/arch/armv7/omap/ommmc.c6
-rw-r--r--sys/dev/ic/rtsx.c6
-rw-r--r--sys/dev/ic/w83l518d_sdmmc.c6
-rw-r--r--sys/dev/sdmmc/sdhc.c67
-rw-r--r--sys/dev/sdmmc/sdmmc.c8
-rw-r--r--sys/dev/sdmmc/sdmmc_io.c4
-rw-r--r--sys/dev/sdmmc/sdmmc_mem.c104
-rw-r--r--sys/dev/sdmmc/sdmmcchip.h21
-rw-r--r--sys/dev/sdmmc/sdmmcreg.h10
-rw-r--r--sys/dev/sdmmc/sdmmcvar.h9
13 files changed, 190 insertions, 69 deletions
diff --git a/sys/arch/arm/xscale/pxa2x0_mmc.c b/sys/arch/arm/xscale/pxa2x0_mmc.c
index 434013cb71f..26c4bdd9c02 100644
--- a/sys/arch/arm/xscale/pxa2x0_mmc.c
+++ b/sys/arch/arm/xscale/pxa2x0_mmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: pxa2x0_mmc.c,v 1.12 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: pxa2x0_mmc.c,v 1.13 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2007 Uwe Stuehler <uwe@openbsd.org>
@@ -56,7 +56,7 @@ u_int32_t pxammc_host_ocr(sdmmc_chipset_handle_t);
int pxammc_host_maxblklen(sdmmc_chipset_handle_t);
int pxammc_card_detect(sdmmc_chipset_handle_t);
int pxammc_bus_power(sdmmc_chipset_handle_t, u_int32_t);
-int pxammc_bus_clock(sdmmc_chipset_handle_t, int);
+int pxammc_bus_clock(sdmmc_chipset_handle_t, int, int);
void pxammc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
void pxammc_clock_stop(struct pxammc_softc *);
void pxammc_clock_start(struct pxammc_softc *);
@@ -278,7 +278,7 @@ pxammc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr)
}
int
-pxammc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+pxammc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct pxammc_softc *sc = sch;
int actfreq = 19500; /* KHz */
diff --git a/sys/arch/armv7/exynos/exesdhc.c b/sys/arch/armv7/exynos/exesdhc.c
index 7e03d5f5245..73ff91e2ac5 100644
--- a/sys/arch/armv7/exynos/exesdhc.c
+++ b/sys/arch/armv7/exynos/exesdhc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: exesdhc.c,v 1.6 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: exesdhc.c,v 1.7 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2009 Dale Rahn <drahn@openbsd.org>
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -197,7 +197,7 @@ uint32_t exesdhc_host_ocr(sdmmc_chipset_handle_t);
int exesdhc_host_maxblklen(sdmmc_chipset_handle_t);
int exesdhc_card_detect(sdmmc_chipset_handle_t);
int exesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t);
-int exesdhc_bus_clock(sdmmc_chipset_handle_t, int);
+int exesdhc_bus_clock(sdmmc_chipset_handle_t, int, int);
void exesdhc_card_intr_mask(sdmmc_chipset_handle_t, int);
void exesdhc_card_intr_ack(sdmmc_chipset_handle_t);
void exesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
@@ -517,7 +517,7 @@ exesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
* Return zero on success.
*/
int
-exesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+exesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct exesdhc_softc *sc = sch;
int div, pre_div, cur_freq, s;
diff --git a/sys/arch/armv7/imx/imxesdhc.c b/sys/arch/armv7/imx/imxesdhc.c
index 2a7f934bb65..43119ef05e0 100644
--- a/sys/arch/armv7/imx/imxesdhc.c
+++ b/sys/arch/armv7/imx/imxesdhc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: imxesdhc.c,v 1.14 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: imxesdhc.c,v 1.15 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2009 Dale Rahn <drahn@openbsd.org>
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -195,7 +195,7 @@ uint32_t imxesdhc_host_ocr(sdmmc_chipset_handle_t);
int imxesdhc_host_maxblklen(sdmmc_chipset_handle_t);
int imxesdhc_card_detect(sdmmc_chipset_handle_t);
int imxesdhc_bus_power(sdmmc_chipset_handle_t, uint32_t);
-int imxesdhc_bus_clock(sdmmc_chipset_handle_t, int);
+int imxesdhc_bus_clock(sdmmc_chipset_handle_t, int, int);
void imxesdhc_card_intr_mask(sdmmc_chipset_handle_t, int);
void imxesdhc_card_intr_ack(sdmmc_chipset_handle_t);
void imxesdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
@@ -555,7 +555,7 @@ imxesdhc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
* Return zero on success.
*/
int
-imxesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+imxesdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct imxesdhc_softc *sc = sch;
int div, pre_div, cur_freq, s;
diff --git a/sys/arch/armv7/omap/ommmc.c b/sys/arch/armv7/omap/ommmc.c
index 92a585bd5a5..1e7329375e5 100644
--- a/sys/arch/armv7/omap/ommmc.c
+++ b/sys/arch/armv7/omap/ommmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ommmc.c,v 1.18 2016/05/02 07:38:34 jsg Exp $ */
+/* $OpenBSD: ommmc.c,v 1.19 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2009 Dale Rahn <drahn@openbsd.org>
@@ -228,7 +228,7 @@ uint32_t ommmc_host_ocr(sdmmc_chipset_handle_t);
int ommmc_host_maxblklen(sdmmc_chipset_handle_t);
int ommmc_card_detect(sdmmc_chipset_handle_t);
int ommmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
-int ommmc_bus_clock(sdmmc_chipset_handle_t, int);
+int ommmc_bus_clock(sdmmc_chipset_handle_t, int, int);
int ommmc_bus_width(sdmmc_chipset_handle_t, int);
void ommmc_card_intr_mask(sdmmc_chipset_handle_t, int);
void ommmc_card_intr_ack(sdmmc_chipset_handle_t);
@@ -631,7 +631,7 @@ ommmc_clock_divisor(struct ommmc_softc *sc, uint32_t freq)
* Return zero on success.
*/
int
-ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+ommmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
int error = 0;
struct ommmc_softc *sc = sch;
diff --git a/sys/dev/ic/rtsx.c b/sys/dev/ic/rtsx.c
index ba734292ca6..9d1c379c130 100644
--- a/sys/dev/ic/rtsx.c
+++ b/sys/dev/ic/rtsx.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: rtsx.c,v 1.13 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: rtsx.c,v 1.14 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -95,7 +95,7 @@ u_int32_t rtsx_host_ocr(sdmmc_chipset_handle_t);
int rtsx_host_maxblklen(sdmmc_chipset_handle_t);
int rtsx_card_detect(sdmmc_chipset_handle_t);
int rtsx_bus_power(sdmmc_chipset_handle_t, u_int32_t);
-int rtsx_bus_clock(sdmmc_chipset_handle_t, int);
+int rtsx_bus_clock(sdmmc_chipset_handle_t, int, int);
void rtsx_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
int rtsx_init(struct rtsx_softc *, int);
void rtsx_soft_reset(struct rtsx_softc *);
@@ -614,7 +614,7 @@ ret:
* Return zero on success.
*/
int
-rtsx_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+rtsx_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct rtsx_softc *sc = sch;
int s;
diff --git a/sys/dev/ic/w83l518d_sdmmc.c b/sys/dev/ic/w83l518d_sdmmc.c
index 9e867ca7a12..76c7a10c8d6 100644
--- a/sys/dev/ic/w83l518d_sdmmc.c
+++ b/sys/dev/ic/w83l518d_sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: w83l518d_sdmmc.c,v 1.3 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: w83l518d_sdmmc.c,v 1.4 2016/05/05 11:01:08 kettenis Exp $ */
/* $NetBSD: w83l518d_sdmmc.c,v 1.1 2009/09/30 20:44:50 jmcneill Exp $ */
/*
@@ -65,7 +65,7 @@ int wb_sdmmc_card_detect(sdmmc_chipset_handle_t);
int wb_sdmmc_write_protect(sdmmc_chipset_handle_t);
#endif
int wb_sdmmc_bus_power(sdmmc_chipset_handle_t, uint32_t);
-int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t, int);
+int wb_sdmmc_bus_clock(sdmmc_chipset_handle_t, int, int);
int wb_sdmmc_bus_width(sdmmc_chipset_handle_t, int);
void wb_sdmmc_exec_command(sdmmc_chipset_handle_t,
struct sdmmc_command *);
@@ -278,7 +278,7 @@ wb_sdmmc_bus_power(sdmmc_chipset_handle_t sch, uint32_t ocr)
}
int
-wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+wb_sdmmc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct wb_softc *wb = sch;
uint8_t clk;
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c
index be29a5f8efa..47bcada5e87 100644
--- a/sys/dev/sdmmc/sdhc.c
+++ b/sys/dev/sdmmc/sdhc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdhc.c,v 1.50 2016/05/04 14:05:32 kettenis Exp $ */
+/* $OpenBSD: sdhc.c,v 1.51 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -86,10 +86,11 @@ u_int32_t sdhc_host_ocr(sdmmc_chipset_handle_t);
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_clock(sdmmc_chipset_handle_t, int, 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);
+int sdhc_signal_voltage(sdmmc_chipset_handle_t, int);
void sdhc_exec_command(sdmmc_chipset_handle_t, struct sdmmc_command *);
int sdhc_start_command(struct sdhc_host *, struct sdmmc_command *);
int sdhc_wait_state(struct sdhc_host *, u_int32_t, u_int32_t);
@@ -123,7 +124,9 @@ struct sdmmc_chip_functions sdhc_functions = {
sdhc_exec_command,
/* card interrupt */
sdhc_card_intr_mask,
- sdhc_card_intr_ack
+ sdhc_card_intr_ack,
+ /* UHS functions */
+ sdhc_signal_voltage
};
struct cfdriver sdhc_cd = {
@@ -305,8 +308,13 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
saa.caps |= SMC_CAPS_MMC_HIGHSPEED;
if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ uint32_t caps2 = HREAD4(hp, SDHC_CAPABILITIES2);
+
if (ISSET(caps, SDHC_8BIT_MODE_SUPP))
saa.caps |= SMC_CAPS_8BIT_MODE;
+
+ if (ISSET(caps2, SDHC_DDR50_SUPP))
+ saa.caps |= SMC_CAPS_MMC_DDR52;
}
hp->sdmmc = config_found(&sc->sc_dev, &saa, NULL);
@@ -542,7 +550,7 @@ sdhc_clock_divisor(struct sdhc_host *hp, u_int freq)
* Return zero on success.
*/
int
-sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
+sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq, int timing)
{
struct sdhc_host *hp = sch;
int s;
@@ -567,6 +575,20 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
if (freq == SDMMC_SDCLK_OFF)
goto ret;
+ if (timing == SDMMC_TIMING_LEGACY)
+ HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
+ else
+ HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
+
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ switch (timing) {
+ case SDMMC_TIMING_MMC_DDR52:
+ HCLR2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_MASK);
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_UHS_MODE_SELECT_DDR50);
+ break;
+ }
+ }
+
/*
* Set the minimum base clock frequency divisor.
*/
@@ -600,11 +622,6 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
*/
HSET2(hp, SDHC_CLOCK_CTL, SDHC_SDCLK_ENABLE);
- if (freq > 26000)
- HSET1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
- else
- HCLR1(hp, SDHC_HOST_CTL, SDHC_HIGH_SPEED);
-
ret:
splx(s);
return error;
@@ -618,7 +635,7 @@ sdhc_bus_width(sdmmc_chipset_handle_t sch, int width)
int s;
if (width != 1 && width != 4 && width != 8)
- return 1;
+ return EINVAL;
s = splsdmmc();
@@ -663,6 +680,36 @@ sdhc_card_intr_ack(sdmmc_chipset_handle_t sch)
}
int
+sdhc_signal_voltage(sdmmc_chipset_handle_t sch, int signal_voltage)
+{
+ struct sdhc_host *hp = sch;
+
+ if (SDHC_SPEC_VERSION(hp->version) < SDHC_SPEC_V3)
+ return EINVAL;
+
+ switch (signal_voltage) {
+ case SDMMC_SIGNAL_VOLTAGE_180:
+ HSET2(hp, SDHC_HOST_CTL2, SDHC_1_8V_SIGNAL_EN);
+ break;
+ case SDMMC_SIGNAL_VOLTAGE_330:
+ HCLR2(hp, SDHC_HOST_CTL2, SDHC_1_8V_SIGNAL_EN);
+ break;
+ default:
+ return EINVAL;
+ }
+
+ /* Regulator output shall be stable within 5 ms. */
+ sdmmc_delay(5000);
+
+ /* Host controller clears this bit if 1.8V signalling fails. */
+ if (signal_voltage == SDMMC_SIGNAL_VOLTAGE_180 &&
+ !ISSET(HREAD4(hp, SDHC_HOST_CTL2), SDHC_1_8V_SIGNAL_EN))
+ return EIO;
+
+ return 0;
+}
+
+int
sdhc_wait_state(struct sdhc_host *hp, u_int32_t mask, u_int32_t value)
{
u_int32_t state;
diff --git a/sys/dev/sdmmc/sdmmc.c b/sys/dev/sdmmc/sdmmc.c
index e67338aebaa..e09391698e5 100644
--- a/sys/dev/sdmmc/sdmmc.c
+++ b/sys/dev/sdmmc/sdmmc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc.c,v 1.43 2016/05/04 09:30:06 kettenis Exp $ */
+/* $OpenBSD: sdmmc.c,v 1.44 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -420,7 +420,8 @@ sdmmc_enable(struct sdmmc_softc *sc)
/*
* Select the minimum clock frequency.
*/
- error = sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_400KHZ);
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch,
+ SDMMC_SDCLK_400KHZ, SDMMC_TIMING_LEGACY);
if (error != 0) {
printf("%s: can't supply clock\n", DEVNAME(sc));
goto err;
@@ -456,7 +457,8 @@ sdmmc_disable(struct sdmmc_softc *sc)
(void)sdmmc_select_card(sc, NULL);
/* Turn off bus power and clock. */
- (void)sdmmc_chip_bus_clock(sc->sct, sc->sch, SDMMC_SDCLK_OFF);
+ (void)sdmmc_chip_bus_clock(sc->sct, sc->sch,
+ SDMMC_SDCLK_OFF, SDMMC_TIMING_LEGACY);
(void)sdmmc_chip_bus_power(sc->sct, sc->sch, 0);
}
diff --git a/sys/dev/sdmmc/sdmmc_io.c b/sys/dev/sdmmc/sdmmc_io.c
index 3c9372aa965..13c95710f4f 100644
--- a/sys/dev/sdmmc/sdmmc_io.c
+++ b/sys/dev/sdmmc/sdmmc_io.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmc_io.c,v 1.24 2016/05/04 09:30:06 kettenis Exp $ */
+/* $OpenBSD: sdmmc_io.c,v 1.25 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -185,7 +185,7 @@ sdmmc_io_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
if (sf->number == 0) {
/* XXX respect host and card capabilities */
(void)sdmmc_chip_bus_clock(sc->sct, sc->sch,
- SDMMC_SDCLK_25MHZ);
+ 25000, SDMMC_TIMING_LEGACY);
}
return 0;
diff --git a/sys/dev/sdmmc/sdmmc_mem.c b/sys/dev/sdmmc/sdmmc_mem.c
index 8b5bb829fa1..9b8a288883f 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.27 2016/05/04 09:30:06 kettenis Exp $ */
+/* $OpenBSD: sdmmc_mem.c,v 1.28 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -579,7 +579,7 @@ sdmmc_be512_to_bitfield512(sdmmc_bitfield512_t *buf) {
int
sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
- int support_func, best_func, bus_clock, error;
+ int support_func, best_func, bus_clock, timing, error;
sdmmc_bitfield512_t status; /* Switch Function Status */
uint32_t raw_scr[2];
@@ -604,6 +604,7 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
best_func = 0;
bus_clock = 25000;
+ timing = SDMMC_TIMING_LEGACY;
if (sf->scr.sd_spec >= SCR_SD_SPEC_VER_1_10 &&
ISSET(sf->csd.ccc, SD_CSD_CCC_SWITCH)) {
DPRINTF(("%s: switch func mode 0\n", DEVNAME(sc)));
@@ -630,6 +631,7 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
return error;
}
bus_clock = 50000;
+ timing = SDMMC_TIMING_HIGHSPEED;
/* Wait 400KHz x 8 clock (2.5us * 8 + slop) */
delay(25);
@@ -637,7 +639,7 @@ sdmmc_mem_sd_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
}
/* change bus clock */
- error = sdmmc_chip_bus_clock(sc->sct, sc->sch, bus_clock);
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, bus_clock, timing);
if (error) {
printf("%s: can't change bus clock\n", DEVNAME(sc));
return error;
@@ -650,10 +652,11 @@ int
sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
{
int width, value;
+ int card_type;
int error = 0;
u_int8_t ext_csd[512];
- int speed = 0;
- int hs_timing = 0;
+ int speed = 20000;
+ int timing = SDMMC_TIMING_LEGACY;
u_int32_t sectors = 0;
if (sf->csd.mmcver >= MMC_CSD_MMCVER_4_0) {
@@ -666,16 +669,56 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
return error;
}
- if (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_F_52M) {
+ card_type = ext_csd[EXT_CSD_CARD_TYPE];
+
+ if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V &&
+ ISSET(sc->sc_caps, SMC_CAPS_MMC_DDR52)) {
+ speed = 52000;
+ timing = SDMMC_TIMING_MMC_DDR52;
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_52M &&
+ ISSET(sc->sc_caps, SMC_CAPS_MMC_HIGHSPEED)) {
speed = 52000;
- hs_timing = 1;
- } else if (ext_csd[EXT_CSD_CARD_TYPE] & EXT_CSD_CARD_TYPE_F_26M) {
+ timing = SDMMC_TIMING_HIGHSPEED;
+ } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) {
speed = 26000;
} else {
printf("%s: unknown CARD_TYPE 0x%x\n", DEVNAME(sc),
ext_csd[EXT_CSD_CARD_TYPE]);
}
+ if (timing != SDMMC_TIMING_LEGACY) {
+ /* switch to high speed timing */
+ error = sdmmc_mem_mmc_switch(sf, EXT_CSD_CMD_SET_NORMAL,
+ EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS);
+ if (error != 0) {
+ printf("%s: can't change high speed\n",
+ DEVNAME(sc));
+ return error;
+ }
+
+ sdmmc_delay(10000);
+ }
+
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed, SDMMC_TIMING_HIGHSPEED);
+ if (error != 0) {
+ printf("%s: can't change bus clock\n", DEVNAME(sc));
+ return error;
+ }
+
+ if (timing != SDMMC_TIMING_LEGACY) {
+ /* 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] != EXT_CSD_HS_TIMING_HS) {
+ printf("%s, HS_TIMING set failed\n", DEVNAME(sc));
+ return EINVAL;
+ }
+ }
+
if (ISSET(sc->sc_caps, SMC_CAPS_8BIT_MODE)) {
width = 8;
value = EXT_CSD_BUS_WIDTH_8;
@@ -703,40 +746,41 @@ sdmmc_mem_mmc_init(struct sdmmc_softc *sc, struct sdmmc_function *sf)
sdmmc_delay(10000);
}
- if (!ISSET(sc->sc_caps, SMC_CAPS_MMC_HIGHSPEED))
- hs_timing = 0;
+ if (timing == SDMMC_TIMING_MMC_DDR52) {
+ switch (width) {
+ case 4:
+ value = EXT_CSD_BUS_WIDTH_4_DDR;
+ break;
+ case 8:
+ value = EXT_CSD_BUS_WIDTH_8_DDR;
+ break;
+ }
- 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",
+ EXT_CSD_BUS_WIDTH, value);
+ if (error) {
+ printf("%s: can't switch to DDR\n",
DEVNAME(sc));
return error;
}
sdmmc_delay(10000);
- }
- 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;
- }
+ error = sdmmc_chip_signal_voltage(sc->sct, sc->sch,
+ SDMMC_SIGNAL_VOLTAGE_180);
+ if (error) {
+ printf("%s: can't switch signalling voltage\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));
+ error = sdmmc_chip_bus_clock(sc->sct, sc->sch, speed, timing);
if (error != 0) {
- printf("%s: can't re-read EXT_CSD\n", DEVNAME(sc));
+ printf("%s: can't change bus clock\n", DEVNAME(sc));
return error;
}
- if (ext_csd[EXT_CSD_HS_TIMING] != hs_timing) {
- printf("%s, HS_TIMING set failed\n", DEVNAME(sc));
- return EINVAL;
- }
+
+ sdmmc_delay(10000);
}
sectors = ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 |
diff --git a/sys/dev/sdmmc/sdmmcchip.h b/sys/dev/sdmmc/sdmmcchip.h
index 20d5e3aa59b..4f4843cc741 100644
--- a/sys/dev/sdmmc/sdmmcchip.h
+++ b/sys/dev/sdmmc/sdmmcchip.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcchip.h,v 1.7 2016/05/01 16:04:39 kettenis Exp $ */
+/* $OpenBSD: sdmmcchip.h,v 1.8 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -36,7 +36,7 @@ struct sdmmc_chip_functions {
int (*card_detect)(sdmmc_chipset_handle_t);
/* 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_clock)(sdmmc_chipset_handle_t, int, int);
int (*bus_width)(sdmmc_chipset_handle_t, int);
/* command execution */
void (*exec_command)(sdmmc_chipset_handle_t,
@@ -44,6 +44,8 @@ struct sdmmc_chip_functions {
/* card interrupt */
void (*card_intr_mask)(sdmmc_chipset_handle_t, int);
void (*card_intr_ack)(sdmmc_chipset_handle_t);
+ /* UHS functions */
+ int (*signal_voltage)(sdmmc_chipset_handle_t, int);
};
/* host controller reset */
@@ -60,8 +62,8 @@ struct sdmmc_chip_functions {
/* bus power and clock frequency */
#define sdmmc_chip_bus_power(tag, handle, ocr) \
((tag)->bus_power((handle), (ocr)))
-#define sdmmc_chip_bus_clock(tag, handle, freq) \
- ((tag)->bus_clock((handle), (freq)))
+#define sdmmc_chip_bus_clock(tag, handle, freq, timing) \
+ ((tag)->bus_clock((handle), (freq), (timing)))
#define sdmmc_chip_bus_width(tag, handle, width) \
((tag)->bus_width((handle), (width)))
/* command execution */
@@ -72,12 +74,23 @@ struct sdmmc_chip_functions {
((tag)->card_intr_mask((handle), (enable)))
#define sdmmc_chip_card_intr_ack(tag, handle) \
((tag)->card_intr_ack((handle)))
+/* UHS functions */
+#define sdmmc_chip_signal_voltage(tag, handle, voltage) \
+ ((tag)->signal_voltage((handle), (voltage)))
/* clock frequencies for sdmmc_chip_bus_clock() */
#define SDMMC_SDCLK_OFF 0
#define SDMMC_SDCLK_400KHZ 400
#define SDMMC_SDCLK_25MHZ 25000
+/* voltage levels for sdmmc_chip_signal_voltage() */
+#define SDMMC_SIGNAL_VOLTAGE_330 0
+#define SDMMC_SIGNAL_VOLTAGE_180 1
+
+#define SDMMC_TIMING_LEGACY 0
+#define SDMMC_TIMING_HIGHSPEED 1
+#define SDMMC_TIMING_MMC_DDR52 2
+
struct sdmmcbus_attach_args {
const char *saa_busname;
sdmmc_chipset_tag_t sct;
diff --git a/sys/dev/sdmmc/sdmmcreg.h b/sys/dev/sdmmc/sdmmcreg.h
index c2751488b9e..c984afc9f78 100644
--- a/sys/dev/sdmmc/sdmmcreg.h
+++ b/sys/dev/sdmmc/sdmmcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcreg.h,v 1.10 2016/05/04 09:30:06 kettenis Exp $ */
+/* $OpenBSD: sdmmcreg.h,v 1.11 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -104,10 +104,18 @@
#define EXT_CSD_CMD_SET_SECURE (1U << 1)
#define EXT_CSD_CMD_SET_CPSECURE (1U << 2)
+/* EXT_CSD_HS_TIMING */
+#define EXT_CSD_HS_TIMING_BC 0
+#define EXT_CSD_HS_TIMING_HS 1
+#define EXT_CSD_HS_TIMING_HS200 2
+#define EXT_CSD_HS_TIMING_HS400 3
+
/* 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
+#define EXT_CSD_BUS_WIDTH_4_DDR 5
+#define EXT_CSD_BUS_WIDTH_8_DDR 6
/* EXT_CSD_CARD_TYPE */
/* The only currently valid values for this field are 0x01, 0x03, 0x07,
diff --git a/sys/dev/sdmmc/sdmmcvar.h b/sys/dev/sdmmc/sdmmcvar.h
index 7a66dd097c5..cc4074cd00b 100644
--- a/sys/dev/sdmmc/sdmmcvar.h
+++ b/sys/dev/sdmmc/sdmmcvar.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdmmcvar.h,v 1.25 2016/05/04 09:30:06 kettenis Exp $ */
+/* $OpenBSD: sdmmcvar.h,v 1.26 2016/05/05 11:01:08 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -191,6 +191,13 @@ struct sdmmc_softc {
#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 */
+#define SMC_CAPS_UHS_SDR50 0x0400 /* UHS SDR50 timing */
+#define SMC_CAPS_UHS_SDR104 0x0800 /* UHS SDR104 timing */
+#define SMC_CAPS_UHS_DDR50 0x1000 /* UHS DDR50 timing */
+#define SMC_CAPS_UHS_MASK 0x1c00
+#define SMC_CAPS_MMC_DDR52 0x2000 /* eMMC DDR52 timing */
+#define SMC_CAPS_MMC_HS200 0x4000 /* eMMC HS200 timing */
+#define SMC_CAPS_MMC_HS400 0x8000 /* eMMC HS400 timing */
int sc_function_count; /* number of I/O functions (SDIO) */
struct sdmmc_function *sc_card; /* selected card */