summaryrefslogtreecommitdiff
path: root/sys/dev/sdmmc/sdhc.c
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 /sys/dev/sdmmc/sdhc.c
parent8f0bc08bb64f7d54716657b84582e0e73d6a1d71 (diff)
Add Dual Data Rate support for eMMC at 52MHz.
Diffstat (limited to 'sys/dev/sdmmc/sdhc.c')
-rw-r--r--sys/dev/sdmmc/sdhc.c67
1 files changed, 57 insertions, 10 deletions
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;