aboutsummaryrefslogtreecommitdiff
path: root/sys/dev/sdhci
diff options
context:
space:
mode:
authorJustin Hibbits <jhibbits@FreeBSD.org>2016-11-02 00:57:04 +0000
committerJustin Hibbits <jhibbits@FreeBSD.org>2016-11-02 00:57:04 +0000
commiteecaab5275e8502aec8016e9677bb2760133c9eb (patch)
tree8d7339ee51c6bc7d21cb7cde047ff1526fdd1212 /sys/dev/sdhci
parent2b96b955e97636d7d778d3fabb0ac527334c1fa9 (diff)
Notes
Diffstat (limited to 'sys/dev/sdhci')
-rw-r--r--sys/dev/sdhci/fsl_sdhci.c122
1 files changed, 103 insertions, 19 deletions
diff --git a/sys/dev/sdhci/fsl_sdhci.c b/sys/dev/sdhci/fsl_sdhci.c
index b332ada30d123..b691756c8b7b0 100644
--- a/sys/dev/sdhci/fsl_sdhci.c
+++ b/sys/dev/sdhci/fsl_sdhci.c
@@ -28,7 +28,7 @@
__FBSDID("$FreeBSD$");
/*
- * SDHCI driver glue for Freescale i.MX SoC family.
+ * SDHCI driver glue for Freescale i.MX SoC and QorIQ families.
*
* This supports both eSDHC (earlier SoCs) and uSDHC (more recent SoCs).
*/
@@ -38,6 +38,7 @@ __FBSDID("$FreeBSD$");
#include <sys/types.h>
#include <sys/bus.h>
#include <sys/callout.h>
+#include <sys/endian.h>
#include <sys/kernel.h>
#include <sys/libkern.h>
#include <sys/lock.h>
@@ -52,9 +53,11 @@ __FBSDID("$FreeBSD$");
#include <machine/bus.h>
#include <machine/resource.h>
+#ifdef __arm__
#include <machine/intr.h>
#include <arm/freescale/imx/imx_ccmvar.h>
+#endif
#include <dev/ofw/ofw_bus.h>
#include <dev/ofw/ofw_bus_subr.h>
@@ -146,7 +149,6 @@ struct fsl_sdhci_softc {
#define SDHC_PROT_CDSS (1 << 7)
#define SDHC_SYS_CTRL 0x2c
-#define SDHC_INT_STATUS 0x30
/*
* The clock enable bits exist in different registers for ESDHC vs USDHC, but
@@ -169,6 +171,7 @@ static struct ofw_compat_data compat_data[] = {
{"fsl,imx6sl-usdhc", HWTYPE_USDHC},
{"fsl,imx53-esdhc", HWTYPE_ESDHC},
{"fsl,imx51-esdhc", HWTYPE_ESDHC},
+ {"fsl,esdhc", HWTYPE_ESDHC},
{NULL, HWTYPE_NONE},
};
@@ -397,6 +400,11 @@ fsl_sdhci_write_1(device_t dev, struct sdhci_slot *slot, bus_size_t off, uint8_t
if (off == SDHCI_POWER_CONTROL) {
return;
}
+#ifdef __powerpc__
+ /* XXX Reset doesn't seem to work as expected. Do nothing for now. */
+ if (off == SDHCI_SOFTWARE_RESET)
+ return;
+#endif
val32 = RD4(sc, off & ~3);
val32 &= ~(0xff << (off & 3) * 8);
@@ -531,17 +539,20 @@ fsl_sdhc_get_clock(struct fsl_sdhci_softc *sc)
val |= SDHCI_CLOCK_INT_STABLE;
/*
- * On ESDHC hardware the card bus clock enable is in the usual sdhci
- * register but it's a different bit, so transcribe it (note the
+ * On i.MX ESDHC hardware the card bus clock enable is in the usual
+ * sdhci register but it's a different bit, so transcribe it (note the
* difference between standard SDHCI_ and Freescale SDHC_ prefixes
- * here). On USDHC hardware there is a force-on bit, but no force-off
- * for the card bus clock (the hardware runs the clock when transfers
- * are active no matter what), so we always say the clock is on.
+ * here). On USDHC and QorIQ ESDHC hardware there is a force-on bit, but
+ * no force-off for the card bus clock (the hardware runs the clock when
+ * transfers are active no matter what), so we always say the clock is
+ * on.
* XXX Maybe we should say it's in whatever state the sdhci driver last
* set it to.
*/
if (sc->hwtype == HWTYPE_ESDHC) {
+#ifdef __arm__
if (RD4(sc, SDHC_SYS_CTRL) & SDHC_CLK_SDCLKEN)
+#endif
val |= SDHCI_CLOCK_CARD_EN;
} else {
val |= SDHCI_CLOCK_CARD_EN;
@@ -565,15 +576,18 @@ fsl_sdhc_set_clock(struct fsl_sdhci_softc *sc, uint16_t val)
sc->sdclockreg_freq_bits = val & SDHCI_DIVIDERS_MASK;
if (sc->hwtype == HWTYPE_ESDHC) {
/*
- * The ESDHC hardware requires the driver to manually start and
- * stop the sd bus clock. If the enable bit is not set, turn
- * off the clock in hardware and we're done, otherwise decode
- * the requested frequency. ESDHC hardware is sdhci 2.0; the
- * sdhci driver will use the original 8-bit divisor field and
- * the "base / 2^N" divisor scheme.
+ * The i.MX5 ESDHC hardware requires the driver to manually
+ * start and stop the sd bus clock. If the enable bit is not
+ * set, turn off the clock in hardware and we're done, otherwise
+ * decode the requested frequency. ESDHC hardware is sdhci 2.0;
+ * the sdhci driver will use the original 8-bit divisor field
+ * and the "base / 2^N" divisor scheme.
*/
if ((val & SDHCI_CLOCK_CARD_EN) == 0) {
+#ifdef __arm__
+ /* On QorIQ, this is a reserved bit. */
WR4(sc, SDHCI_CLOCK_CONTROL, val32 & ~SDHC_CLK_SDCLKEN);
+#endif
return;
}
@@ -625,6 +639,7 @@ fsl_sdhc_set_clock(struct fsl_sdhci_softc *sc, uint16_t val)
val32 &= ~(SDHC_CLK_DIVISOR_MASK | SDHC_CLK_PRESCALE_MASK);
val32 |= divisor << SDHC_CLK_DIVISOR_SHIFT;
val32 |= prescale << SDHC_CLK_PRESCALE_SHIFT;
+ val32 |= SDHC_CLK_IPGEN;
WR4(sc, SDHCI_CLOCK_CONTROL, val32);
}
@@ -710,10 +725,10 @@ fsl_sdhci_intr(void *arg)
*/
switch (sc->r1bfix_type) {
case R1BFIX_NODATA:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_RESPONSE;
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_RESPONSE;
break;
case R1BFIX_AC12:
- intmask = RD4(sc, SDHC_INT_STATUS) & SDHCI_INT_DATA_END;
+ intmask = RD4(sc, SDHCI_INT_STATUS) & SDHCI_INT_DATA_END;
break;
default:
intmask = 0;
@@ -722,8 +737,8 @@ fsl_sdhci_intr(void *arg)
if (intmask) {
sc->r1bfix_timeout_at = getsbinuptime() + 250 * SBT_1MS;
if (!fsl_sdhci_r1bfix_is_wait_done(sc)) {
- WR4(sc, SDHC_INT_STATUS, intmask);
- bus_barrier(sc->mem_res, SDHC_INT_STATUS, 4,
+ WR4(sc, SDHCI_INT_STATUS, intmask);
+ bus_barrier(sc->mem_res, SDHCI_INT_STATUS, 4,
BUS_SPACE_BARRIER_WRITE);
}
}
@@ -735,10 +750,54 @@ fsl_sdhci_intr(void *arg)
static int
fsl_sdhci_get_ro(device_t bus, device_t child)
{
+ struct fsl_sdhci_softc *sc = device_get_softc(bus);
- return (false);
+ if (RD4(sc, SDHCI_PRESENT_STATE) & SDHC_PRES_WPSPL)
+ return (false);
+ return (true);
}
+#ifdef __powerpc__
+static uint32_t
+fsl_sdhci_get_platform_clock(device_t dev)
+{
+ device_t parent;
+ phandle_t node;
+ uint32_t clock;
+
+ node = ofw_bus_get_node(dev);
+
+ /* Get sdhci node properties */
+ if((OF_getprop(node, "clock-frequency", (void *)&clock,
+ sizeof(clock)) <= 0) || (clock == 0)) {
+
+ /*
+ * Trying to get clock from parent device (soc) if correct
+ * clock cannot be acquired from sdhci node.
+ */
+ parent = device_get_parent(dev);
+ node = ofw_bus_get_node(parent);
+
+ /* Get soc properties */
+ if ((OF_getprop(node, "bus-frequency", (void *)&clock,
+ sizeof(clock)) <= 0) || (clock == 0)) {
+ device_printf(dev,"Cannot acquire correct sdhci "
+ "frequency from DTS.\n");
+
+ return (0);
+ }
+ /* eSDHC clock is 1/2 platform clock. */
+ clock /= 2;
+ }
+
+ if (bootverbose)
+ device_printf(dev, "Acquired clock: %d from DTS\n", clock);
+
+ return (clock);
+}
+#endif
+
+
static int
fsl_sdhci_detach(device_t dev)
{
@@ -752,6 +811,7 @@ fsl_sdhci_attach(device_t dev)
struct fsl_sdhci_softc *sc = device_get_softc(dev);
int rid, err;
phandle_t node;
+ uint32_t protctl;
sc->dev = dev;
@@ -807,9 +867,23 @@ fsl_sdhci_attach(device_t dev)
*
* XXX need named constants for this stuff.
*/
- WR4(sc, SDHC_WTMK_LVL, 0x08800880);
+#ifdef __powerpc__
+ /* P1022 has the '*_BRST_LEN' fields as reserved, always reading 0x10 */
+ if ((SVR_VER(mfspr(SPR_SVR)) & 0xfff6) == SVR_P1022 )
+ WR4(sc, SDHC_WTMK_LVL, 0x10801080);
+ else
+#endif
+ WR4(sc, SDHC_WTMK_LVL, 0x08800880);
+ /*
+ * We read in native byte order in the main driver, but the register
+ * defaults to little endian.
+ */
+#ifdef __powerpc__
+ sc->baseclk_hz = fsl_sdhci_get_platform_clock(dev);
+#else
sc->baseclk_hz = imx_ccm_sdhci_hz();
+#endif
sc->slot.max_clk = sc->baseclk_hz;
/*
@@ -830,6 +904,16 @@ fsl_sdhci_attach(device_t dev)
/* XXX put real gpio hookup here. */
sc->force_card_present = true;
}
+#ifdef __powerpc__
+ /* Default to big-endian on powerpc */
+ protctl = RD4(sc, SDHC_PROT_CTRL);
+ protctl &= ~SDHC_PROT_EMODE_MASK;
+ if (OF_hasprop(node, "little-endian"))
+ protctl |= SDHC_PROT_EMODE_LITTLE;
+ else
+ protctl |= SDHC_PROT_EMODE_BIG;
+ WR4(sc, SDHC_PROT_CTRL, protctl);
+#endif
callout_init(&sc->r1bfix_callout, 1);
sdhci_init_slot(dev, &sc->slot, 0);