summaryrefslogtreecommitdiff
path: root/sys/dev/sdmmc
diff options
context:
space:
mode:
authorMark Kettenis <kettenis@cvs.openbsd.org>2016-01-11 06:54:54 +0000
committerMark Kettenis <kettenis@cvs.openbsd.org>2016-01-11 06:54:54 +0000
commit67bd1cddd3db45698dfa321681211045786b7374 (patch)
treedaf2f0521c0b3565ac8f1c3343af47bd4bb502b3 /sys/dev/sdmmc
parent0a2177d95505019241dc20999f31057f910edd93 (diff)
Add (limited) support for controllers that implement version 3.0 of the
SD host controller standard. Support the larger base clock and larger clock divisors. ok jsg@
Diffstat (limited to 'sys/dev/sdmmc')
-rw-r--r--sys/dev/sdmmc/sdhc.c36
-rw-r--r--sys/dev/sdmmc/sdhcreg.h18
2 files changed, 46 insertions, 8 deletions
diff --git a/sys/dev/sdmmc/sdhc.c b/sys/dev/sdmmc/sdhc.c
index 0fbb05f7ef0..dd810c0eec3 100644
--- a/sys/dev/sdmmc/sdhc.c
+++ b/sys/dev/sdmmc/sdhc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdhc.c,v 1.40 2016/01/10 14:11:43 kettenis Exp $ */
+/* $OpenBSD: sdhc.c,v 1.41 2016/01/11 06:54:53 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -42,6 +42,7 @@ struct sdhc_host {
struct device *sdmmc; /* generic SD/MMC device */
bus_space_tag_t iot; /* host register set tag */
bus_space_handle_t ioh; /* host register set handle */
+ u_int16_t version; /* specification version */
u_int clkbase; /* base clock frequency in KHz */
int maxblklen; /* maximum block length */
int flags; /* flags for this host */
@@ -133,6 +134,7 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
struct sdmmcbus_attach_args saa;
struct sdhc_host *hp;
int error = 1;
+ int max_clock;
#ifdef SDHC_DEBUG
u_int16_t version;
@@ -159,6 +161,9 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
hp->iot = iot;
hp->ioh = ioh;
+ /* Store specification version. */
+ hp->version = bus_space_read_2(iot, ioh, SDHC_HOST_CTL_VERSION);
+
/*
* Reset the host controller and enable interrupts.
*/
@@ -175,15 +180,23 @@ sdhc_host_found(struct sdhc_softc *sc, bus_space_tag_t iot,
/*
* Determine the base clock frequency. (2.2.24)
*/
- if (SDHC_BASE_FREQ_KHZ(caps) != 0)
- hp->clkbase = SDHC_BASE_FREQ_KHZ(caps);
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3) {
+ /* SDHC 3.0 supports 10-255 MHz. */
+ max_clock = 255000;
+ if (SDHC_BASE_FREQ_KHZ_V3(caps) != 0)
+ hp->clkbase = SDHC_BASE_FREQ_KHZ_V3(caps);
+ } else {
+ /* SDHC 1.0/2.0 supports only 10-63 MHz. */
+ max_clock = 63000;
+ if (SDHC_BASE_FREQ_KHZ(caps) != 0)
+ hp->clkbase = SDHC_BASE_FREQ_KHZ(caps);
+ }
if (hp->clkbase == 0) {
/* The attachment driver must tell us. */
printf("%s: base clock frequency unknown\n",
sc->sc_dev.dv_xname);
goto err;
- } else if (hp->clkbase < 10000 || hp->clkbase > 63000) {
- /* SDHC 1.0 supports only 10-63 MHz. */
+ } else if (hp->clkbase < 10000 || hp->clkbase > max_clock) {
printf("%s: base clock frequency out of range: %u MHz\n",
sc->sc_dev.dv_xname, hp->clkbase / 1000);
goto err;
@@ -443,9 +456,13 @@ sdhc_bus_power(sdmmc_chipset_handle_t sch, u_int32_t ocr)
static int
sdhc_clock_divisor(struct sdhc_host *hp, u_int freq)
{
+ int max_div = 256;
int div;
- for (div = 1; div <= 256; div *= 2)
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3)
+ max_div = 2046;
+
+ for (div = 1; div <= max_div; div *= 2)
if ((hp->clkbase / div) <= freq)
return (div / 2);
/* No divisor found. */
@@ -462,6 +479,7 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
struct sdhc_host *hp = sch;
int s;
int div;
+ int sdclk;
int timo;
int error = 0;
@@ -489,7 +507,11 @@ sdhc_bus_clock(sdmmc_chipset_handle_t sch, int freq)
error = EINVAL;
goto ret;
}
- HWRITE2(hp, SDHC_CLOCK_CTL, div << SDHC_SDCLK_DIV_SHIFT);
+ if (SDHC_SPEC_VERSION(hp->version) >= SDHC_SPEC_V3)
+ sdclk = SDHC_SDCLK_DIV_V3(div);
+ else
+ sdclk = SDHC_SDCLK_DIV(div);
+ HWRITE2(hp, SDHC_CLOCK_CTL, sdclk);
/*
* Start internal clock. Wait 10ms for stabilization.
diff --git a/sys/dev/sdmmc/sdhcreg.h b/sys/dev/sdmmc/sdhcreg.h
index 56d72dcd24a..c7e9bc85764 100644
--- a/sys/dev/sdmmc/sdhcreg.h
+++ b/sys/dev/sdmmc/sdhcreg.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: sdhcreg.h,v 1.4 2006/07/30 17:20:40 fgsch Exp $ */
+/* $OpenBSD: sdhcreg.h,v 1.5 2016/01/11 06:54:53 kettenis Exp $ */
/*
* Copyright (c) 2006 Uwe Stuehler <uwe@openbsd.org>
@@ -95,6 +95,8 @@
#define SDHC_CLOCK_CTL 0x2c
#define SDHC_SDCLK_DIV_SHIFT 8
#define SDHC_SDCLK_DIV_MASK 0xff
+#define SDHC_SDCLK_DIV_RSHIFT_V3 2
+#define SDHC_SDCLK_DIV_MASK_V3 0x300
#define SDHC_SDCLK_ENABLE (1<<2)
#define SDHC_INTCLK_STABLE (1<<1)
#define SDHC_INTCLK_ENABLE (1<<0)
@@ -148,9 +150,11 @@
#define SDHC_MAX_BLK_LEN_MASK 0x3
#define SDHC_BASE_FREQ_SHIFT 8
#define SDHC_BASE_FREQ_MASK 0x3f
+#define SDHC_BASE_FREQ_MASK_V3 0xff
#define SDHC_TIMEOUT_FREQ_UNIT (1<<7) /* 0=KHz, 1=MHz */
#define SDHC_TIMEOUT_FREQ_SHIFT 0
#define SDHC_TIMEOUT_FREQ_MASK 0x1f
+#define SDHC_CAPABILITIES_1 0x44
#define SDHC_MAX_CAPABILITIES 0x48
#define SDHC_SLOT_INTR_STATUS 0xfc
#define SDHC_HOST_CTL_VERSION 0xfe
@@ -158,10 +162,22 @@
#define SDHC_SPEC_VERS_MASK 0xff
#define SDHC_VENDOR_VERS_SHIFT 8
#define SDHC_VENDOR_VERS_MASK 0xff
+#define SDHC_SPEC_V1 0
+#define SDHC_SPEC_V2 1
+#define SDHC_SPEC_V3 2
+
+/* SDHC_CLOCK_CTL encoding */
+#define SDHC_SDCLK_DIV(div) \
+ (((div) & SDHC_SDCLK_DIV_MASK) << SDHC_SDCLK_DIV_SHIFT)
+#define SDHC_SDCLK_DIV_V3(div) \
+ (SDHC_SDCLK_DIV(div) | \
+ (((div) & SDHC_SDCLK_DIV_MASK_V3) >> SDHC_SDCLK_DIV_RSHIFT_V3))
/* SDHC_CAPABILITIES decoding */
#define SDHC_BASE_FREQ_KHZ(cap) \
((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK) * 1000)
+#define SDHC_BASE_FREQ_KHZ_V3(cap) \
+ ((((cap) >> SDHC_BASE_FREQ_SHIFT) & SDHC_BASE_FREQ_MASK_V3) * 1000)
#define SDHC_TIMEOUT_FREQ(cap) \
(((cap) >> SDHC_TIMEOUT_FREQ_SHIFT) & SDHC_TIMEOUT_FREQ_MASK)
#define SDHC_TIMEOUT_FREQ_KHZ(cap) \