summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorian <ian@FreeBSD.org>2014-05-27 15:30:24 +0000
committerian <ian@FreeBSD.org>2014-05-27 15:30:24 +0000
commitffc031ceb613b18d970372b61578f988d77e3233 (patch)
tree97cc5af267bb15463dd3cdafdb76a098b3ba0b62
parentd22760cc9071ae70622e2c29141ee76e73170a16 (diff)
downloadFreeBSD-src-ffc031ceb613b18d970372b61578f988d77e3233.zip
FreeBSD-src-ffc031ceb613b18d970372b61578f988d77e3233.tar.gz
MFC r264096, r264097, r264099 r264100, r264101, r264102, r264119:
Fixes to the ti_sdhci and sdhci drivers (fix clock divisor calcs). Use the ti_sdhci driver instead of ti_mmchs for Pandaboard.
-rw-r--r--sys/arm/conf/PANDABOARD1
-rw-r--r--sys/arm/ti/omap4/files.omap43
-rw-r--r--sys/arm/ti/ti_sdhci.c77
-rw-r--r--sys/boot/fdt/dts/arm/beaglebone-black.dts1
-rw-r--r--sys/boot/fdt/dts/arm/pandaboard.dts1
-rw-r--r--sys/dev/sdhci/sdhci.c3
6 files changed, 67 insertions, 19 deletions
diff --git a/sys/arm/conf/PANDABOARD b/sys/arm/conf/PANDABOARD
index 43d94f1..2a35b48 100644
--- a/sys/arm/conf/PANDABOARD
+++ b/sys/arm/conf/PANDABOARD
@@ -76,6 +76,7 @@ options PREEMPTION
# MMC/SD/SDIO Card slot support
device mmc # mmc/sd bus
device mmcsd # mmc/sd flash cards
+device sdhci # mmc/sd host controller
# I2C support
device iicbus
diff --git a/sys/arm/ti/omap4/files.omap4 b/sys/arm/ti/omap4/files.omap4
index d9c043d..8f40095 100644
--- a/sys/arm/ti/omap4/files.omap4
+++ b/sys/arm/ti/omap4/files.omap4
@@ -6,7 +6,8 @@ arm/ti/ti_smc.S standard
arm/ti/usb/omap_ehci.c optional usb ehci
arm/ti/ti_sdma.c optional ti_sdma
-arm/ti/ti_mmchs.c optional mmc
+arm/ti/ti_sdhci.c optional sdhci
+#arm/ti/ti_mmchs.c optional mmc
arm/ti/omap4/omap4_l2cache.c optional pl310
arm/ti/omap4/omap4_prcm_clks.c standard
diff --git a/sys/arm/ti/ti_sdhci.c b/sys/arm/ti/ti_sdhci.c
index ee044f5..451a9b8 100644
--- a/sys/arm/ti/ti_sdhci.c
+++ b/sys/arm/ti/ti_sdhci.c
@@ -72,6 +72,8 @@ struct ti_sdhci_softc {
uint32_t wp_gpio_pin;
uint32_t cmd_and_mode;
uint32_t sdhci_clkdiv;
+ boolean_t disable_highspeed;
+ boolean_t force_card_present;
};
/*
@@ -105,9 +107,13 @@ static struct ofw_compat_data compat_data[] = {
#define MMCHS_SYSCONFIG 0x010
#define MMCHS_SYSCONFIG_RESET (1 << 1)
#define MMCHS_SYSSTATUS 0x014
+#define MMCHS_SYSSTATUS_RESETDONE (1 << 0)
#define MMCHS_CON 0x02C
#define MMCHS_CON_DW8 (1 << 5)
#define MMCHS_CON_DVAL_8_4MS (3 << 9)
+#define MMCHS_SYSCTL 0x12C
+#define MMCHS_SYSCTL_CLKD_MASK 0x3FF
+#define MMCHS_SYSCTL_CLKD_SHIFT 6
#define MMCHS_SD_CAPA 0x140
#define MMCHS_SD_CAPA_VS18 (1 << 26)
#define MMCHS_SD_CAPA_VS30 (1 << 25)
@@ -161,19 +167,22 @@ ti_sdhci_read_2(device_t dev, struct sdhci_slot *slot, bus_size_t off)
* but doesn't split them into low:high fields. Instead they're a
* single number in the range 0..1023 and the number is exactly the
* clock divisor (with 0 and 1 both meaning divide by 1). The SDHCI
- * driver code expects a v2.0 divisor (value N is power of two in the
- * range 0..128 and clock is divided by 2N). The shifting and masking
+ * driver code expects a v2.0 or v3.0 divisor. The shifting and masking
* here extracts the MMCHS representation from the hardware word, cleans
- * those bits out, applies the 2N adjustment, and plugs that into the
- * bit positions for the 2.0 divisor in the returned register value. The
- * ti_sdhci_write_2() routine performs the opposite transformation when
- * the SDHCI driver writes to the register.
+ * those bits out, applies the 2N adjustment, and plugs the result into
+ * the bit positions for the 2.0 or 3.0 divisor in the returned register
+ * value. The ti_sdhci_write_2() routine performs the opposite
+ * transformation when the SDHCI driver writes to the register.
*/
if (off == SDHCI_CLOCK_CONTROL) {
val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
- clkdiv = (val32 >> SDHCI_DIVIDER_HI_SHIFT) & 0xff;
- val32 &= ~(0xff << SDHCI_DIVIDER_HI_SHIFT);
- val32 |= (clkdiv / 2) << SDHCI_DIVIDER_SHIFT;
+ clkdiv = ((val32 >> MMCHS_SYSCTL_CLKD_SHIFT) &
+ MMCHS_SYSCTL_CLKD_MASK) / 2;
+ val32 &= ~(MMCHS_SYSCTL_CLKD_MASK << MMCHS_SYSCTL_CLKD_SHIFT);
+ val32 |= (clkdiv & SDHCI_DIVIDER_MASK) << SDHCI_DIVIDER_SHIFT;
+ if (slot->version >= SDHCI_SPEC_300)
+ val32 |= ((clkdiv >> SDHCI_DIVIDER_MASK_LEN) &
+ SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_HI_SHIFT;
return (val32 & 0xffff);
}
@@ -193,8 +202,24 @@ static uint32_t
ti_sdhci_read_4(device_t dev, struct sdhci_slot *slot, bus_size_t off)
{
struct ti_sdhci_softc *sc = device_get_softc(dev);
+ uint32_t val32;
+
+ val32 = RD4(sc, off);
+
+ /*
+ * If we need to disallow highspeed mode due to the OMAP4 erratum, strip
+ * that flag from the returned capabilities.
+ */
+ if (off == SDHCI_CAPABILITIES && sc->disable_highspeed)
+ val32 &= ~SDHCI_CAN_DO_HISPD;
+
+ /*
+ * Force the card-present state if necessary.
+ */
+ if (off == SDHCI_PRESENT_STATE && sc->force_card_present)
+ val32 |= SDHCI_CARD_PRESENT;
- return (RD4(sc, off));
+ return (val32);
}
static void
@@ -228,15 +253,23 @@ ti_sdhci_write_2(device_t dev, struct sdhci_slot *slot, bus_size_t off,
uint32_t clkdiv, val32;
/*
- * Translate between the hardware and SDHCI 2.0 representations of the
- * clock divisor. See the comments in ti_sdhci_read_2() for details.
+ * Translate between the hardware and SDHCI 2.0 or 3.0 representations
+ * of the clock divisor. See the comments in ti_sdhci_read_2() for
+ * details.
*/
if (off == SDHCI_CLOCK_CONTROL) {
clkdiv = (val >> SDHCI_DIVIDER_SHIFT) & SDHCI_DIVIDER_MASK;
+ if (slot->version >= SDHCI_SPEC_300)
+ clkdiv |= ((val >> SDHCI_DIVIDER_HI_SHIFT) &
+ SDHCI_DIVIDER_HI_MASK) << SDHCI_DIVIDER_MASK_LEN;
+ clkdiv *= 2;
+ if (clkdiv > MMCHS_SYSCTL_CLKD_MASK)
+ clkdiv = MMCHS_SYSCTL_CLKD_MASK;
val32 = RD4(sc, SDHCI_CLOCK_CONTROL);
val32 &= 0xffff0000;
- val32 |= val & ~(SDHCI_DIVIDER_MASK << SDHCI_DIVIDER_SHIFT);
- val32 |= (clkdiv * 2) << SDHCI_DIVIDER_HI_SHIFT;
+ val32 |= val & ~(MMCHS_SYSCTL_CLKD_MASK <<
+ MMCHS_SYSCTL_CLKD_SHIFT);
+ val32 |= clkdiv << MMCHS_SYSCTL_CLKD_SHIFT;
WR4(sc, SDHCI_CLOCK_CONTROL, val32);
return;
}
@@ -358,7 +391,7 @@ ti_sdhci_hw_init(device_t dev)
/* Issue a softreset to the controller */
ti_mmchs_write_4(sc, MMCHS_SYSCONFIG, MMCHS_SYSCONFIG_RESET);
timeout = 1000;
- while ((ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSCONFIG_RESET)) {
+ while (!(ti_mmchs_read_4(sc, MMCHS_SYSSTATUS) & MMCHS_SYSSTATUS_RESETDONE)) {
if (--timeout == 0) {
device_printf(dev, "Error: Controller reset operation timed out\n");
break;
@@ -458,12 +491,14 @@ ti_sdhci_attach(device_t dev)
/*
* Set the offset from the device's memory start to the MMCHS registers.
+ * Also for OMAP4 disable high speed mode due to erratum ID i626.
*/
if (ti_chip() == CHIP_OMAP_3)
sc->mmchs_reg_off = OMAP3_MMCHS_REG_OFFSET;
- else if (ti_chip() == CHIP_OMAP_4)
+ else if (ti_chip() == CHIP_OMAP_4) {
sc->mmchs_reg_off = OMAP4_MMCHS_REG_OFFSET;
- else if (ti_chip() == CHIP_AM335X)
+ sc->disable_highspeed = true;
+ } else if (ti_chip() == CHIP_AM335X)
sc->mmchs_reg_off = AM335X_MMCHS_REG_OFFSET;
else
panic("Unknown OMAP device\n");
@@ -560,6 +595,14 @@ ti_sdhci_attach(device_t dev)
}
}
+ /*
+ * If the slot is flagged with the non-removable property, set our flag
+ * to always force the SDHCI_CARD_PRESENT bit on.
+ */
+ node = ofw_bus_get_node(dev);
+ if (OF_hasprop(node, "non-removable"))
+ sc->force_card_present = true;
+
bus_generic_probe(dev);
bus_generic_attach(dev);
diff --git a/sys/boot/fdt/dts/arm/beaglebone-black.dts b/sys/boot/fdt/dts/arm/beaglebone-black.dts
index 1833ec5..0efae4d 100644
--- a/sys/boot/fdt/dts/arm/beaglebone-black.dts
+++ b/sys/boot/fdt/dts/arm/beaglebone-black.dts
@@ -142,6 +142,7 @@
mmchs1@481D8000 {
bus-width = <8>;
status = "okay";
+ non-removable;
};
diff --git a/sys/boot/fdt/dts/arm/pandaboard.dts b/sys/boot/fdt/dts/arm/pandaboard.dts
index 5544dc5..c89bf88 100644
--- a/sys/boot/fdt/dts/arm/pandaboard.dts
+++ b/sys/boot/fdt/dts/arm/pandaboard.dts
@@ -174,6 +174,7 @@
interrupts = <115>;
interrupt-parent = <&GIC>;
mmchs-device-id = <1>;
+ non-removable; /* XXX need real solution */
};
};
diff --git a/sys/dev/sdhci/sdhci.c b/sys/dev/sdhci/sdhci.c
index 92a9b4f..b8e03be 100644
--- a/sys/dev/sdhci/sdhci.c
+++ b/sys/dev/sdhci/sdhci.c
@@ -235,7 +235,8 @@ sdhci_set_clock(struct sdhci_slot *slot, uint32_t clock)
slot->clock = clock;
/* Turn off the clock. */
- WR2(slot, SDHCI_CLOCK_CONTROL, 0);
+ clk = RD2(slot, SDHCI_CLOCK_CONTROL);
+ WR2(slot, SDHCI_CLOCK_CONTROL, clk & ~SDHCI_CLOCK_CARD_EN);
/* If no clock requested - left it so. */
if (clock == 0)
return;
OpenPOWER on IntegriCloud