Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F115917991
D34027.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
19 KB
Referenced Files
None
Subscribers
None
D34027.diff
View Options
diff --git a/sys/dev/sdhci/sdhci_fsl_fdt.c b/sys/dev/sdhci/sdhci_fsl_fdt.c
--- a/sys/dev/sdhci/sdhci_fsl_fdt.c
+++ b/sys/dev/sdhci/sdhci_fsl_fdt.c
@@ -91,10 +91,11 @@
#define SDHCI_FSL_WTMK_WR_512B (0 << 15)
#define SDHCI_FSL_AUTOCERR 0x3C
-#define SDHCI_FSL_AUTOCERR_UHMS_HS200 (3 << 17)
-#define SDHCI_FSL_AUTOCERR_UHMS (7 << 18)
+#define SDHCI_FSL_AUTOCERR_UHMS_HS200 (3 << 16)
+#define SDHCI_FSL_AUTOCERR_UHMS (7 << 16)
#define SDHCI_FSL_AUTOCERR_EXTN (1 << 22)
#define SDHCI_FSL_AUTOCERR_SMPCLKSEL (1 << 23)
+#define SDHCI_FSL_AUTOCERR_UHMS_SHIFT 16
#define SDHCI_FSL_HOST_VERSION 0xfc
#define SDHCI_FSL_VENDOR_V23 0x13
@@ -115,8 +116,8 @@
#define SDHCI_FSL_TBCTL_MODE_SW 3
#define SDHCI_FSL_TBPTR 0x128
-#define SDHCI_FSL_TBPTR_WND_START 0x7F00
-#define SDHCI_FSL_TBPTR_WND_END 0x7F
+#define SDHCI_FSL_TBPTR_WND_START_SHIFT 8
+#define SDHCI_FSL_TBPTR_WND_MASK 0x7F
#define SDHCI_FSL_SDCLKCTL 0x144
#define SDHCI_FSL_SDCLKCTL_CMD_CLK_CTL (1 << 15)
@@ -127,13 +128,14 @@
#define SDHCI_FSL_DLLCFG0 0x160
#define SDHCI_FSL_DLLCFG0_FREQ_SEL (1 << 27)
+#define SDHCI_FSL_DLLCFG0_RESET (1 << 30)
#define SDHCI_FSL_DLLCFG0_EN (1 << 31)
#define SDHCI_FSL_DLLCFG1 0x164
#define SDHCI_FSL_DLLCFG1_PULSE_STRETCH (1 << 31)
-#define SDHCI_FSL_DLLCFG1 0x164
-#define SDHCI_FSL_DLLCFG1_PULSE_STRETCH (1 << 31)
+#define SDHCI_FSL_DLLSTAT0 0x170
+#define SDHCI_FSL_DLLSTAT0_SLV_STS (1 << 27)
#define SDHCI_FSL_ESDHC_CTRL 0x40c
#define SDHCI_FSL_ESDHC_CTRL_SNOOP (1 << 6)
@@ -160,11 +162,14 @@
*/
#define SDHCI_FSL_HS400_LIMITED_CLK_DIV (1 << 4)
+/*
+ * HS400 tuning is done in HS200 mode, but it has to be done using
+ * the target frequency. In order to apply the errata above we need to
+ * know the target mode during tuning procedure. Use this flag for just that.
+ */
#define SDHCI_FSL_HS400_FLAG (1 << 0)
-#define SDHCI_FSL_SWITCH_TO_HS400_FLAG (1 << 1)
-#define SDHCI_FSL_HS400_DONE (1 << 2)
-#define SDHCI_FSL_MAX_RETRIES 10
+#define SDHCI_FSL_MAX_RETRIES 20000 /* DELAY(10) * this = 200ms */
struct sdhci_fsl_fdt_softc {
device_t dev;
@@ -180,9 +185,9 @@
uint32_t cmd_and_mode;
uint16_t sdclk_bits;
struct mmc_helper fdt_helper;
+ uint32_t div_ratio;
uint8_t vendor_ver;
uint32_t flags;
- enum mmc_bus_timing last_mode;
uint32_t (* read)(struct sdhci_fsl_fdt_softc *, bus_size_t);
void (* write)(struct sdhci_fsl_fdt_softc *, bus_size_t, uint32_t);
@@ -211,7 +216,7 @@
static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_ls1046a_soc_data = {
.quirks = SDHCI_QUIRK_DONT_SET_HISPD_BIT | SDHCI_QUIRK_BROKEN_AUTO_STOP,
.baseclk_div = 2,
- .errata = SDHCI_FSL_UNSUPP_1_8V,
+ .errata = SDHCI_FSL_UNSUPP_1_8V | SDHCI_FSL_TUNING_ERRATUM_TYPE2,
};
static const struct sdhci_fsl_fdt_soc_data sdhci_fsl_fdt_gen_data = {
@@ -305,13 +310,6 @@
*/
SDHCI_FSL_FDT_CLK_DIV(sc, sc->baseclk_hz, slot->clock, prescale, div);
-#ifdef DEBUG
- device_printf(sc->dev,
- "Desired SD/MMC freq: %d, actual: %d; base %d prescale %d divisor %d\n",
- slot->clock, sc->baseclk_hz / (prescale * div),
- sc->baseclk_hz, prescale, div);
-#endif
-
div_ratio = prescale * div;
/*
@@ -335,6 +333,13 @@
}
}
+ sc->div_ratio = prescale * div;
+ if (bootverbose)
+ device_printf(sc->dev,
+ "Desired SD/MMC freq: %d, actual: %d; base %d prescale %d divisor %d\n",
+ slot->clock, sc->baseclk_hz / (prescale * div),
+ sc->baseclk_hz, prescale, div);
+
prescale >>= 1;
div -= 1;
@@ -502,24 +507,13 @@
WR4(sc, SDHCI_TRANSFER_MODE, sc->cmd_and_mode);
sc->cmd_and_mode = 0;
return;
- /*
- * When switching to HS400 mode, setting these registers
- * is required for successful tuning.
- */
case SDHCI_HOST_CONTROL2:
- if ((val & SDHCI_CTRL2_MMC_HS400) &&
- (sc->flags & SDHCI_FSL_SWITCH_TO_HS400_FLAG)) {
- val32 = RD4(sc, SDHCI_FSL_TBCTL);
- val32 |= SDHCI_FSL_TBCTL_HS400_EN;
- WR4(sc, SDHCI_FSL_TBCTL, val32);
-
- val32 = RD4(sc, SDHCI_FSL_SDCLKCTL);
- val32 |= SDHCI_FSL_SDCLKCTL_CMD_CLK_CTL;
- WR4(sc, SDHCI_FSL_SDCLKCTL, val32);
-
- }
-
- val &= ~SDHCI_CTRL2_MMC_HS400;
+ /*
+ * Switching to HS400 requires a special procedure,
+ * which is done in sdhci_fsl_fdt_set_uhs_timing.
+ */
+ if ((val & SDHCI_CTRL2_UHS_MASK) == SDHCI_CTRL2_MMC_HS400)
+ val &= ~SDHCI_CTRL2_MMC_HS400;
default:
val32 = RD4(sc, off & ~3);
val32 &= ~(UINT16_MAX << (off & 3) * 8);
@@ -800,7 +794,6 @@
sc->dev = dev;
sc->slot.quirks = sc->soc_data->quirks;
sc->flags = 0;
- sc->last_mode = bus_timing_normal;
host = &sc->slot.host;
rid = 0;
@@ -1028,7 +1021,7 @@
WR4(sc, SDHCI_FSL_DLLCFG1, val);
}
- sc->flags &= ~SDHCI_FSL_HS400_DONE;
+ sc->flags = 0;
}
static void
@@ -1049,124 +1042,49 @@
WR4(sc, SDHCI_FSL_TBCTL, reg);
}
-static int
-sdhci_hw_tuning(struct sdhci_fsl_fdt_softc *sc, device_t bus, device_t child,
- bool hs400)
-{
- int error, retries;
- uint32_t reg;
-
- retries = SDHCI_FSL_MAX_RETRIES;
-
- /* Set SYSCTL2[EXTN] to start the tuning procedure. */
- reg = RD4(sc, SDHCI_FSL_AUTOCERR);
- reg |= SDHCI_FSL_AUTOCERR_EXTN;
- WR4(sc, SDHCI_FSL_AUTOCERR, reg);
-
- while (true) {
- /* Execute generic tuning to issue CMD19 and CMD21. */
- error = sdhci_generic_tune(bus, child, hs400);
- if (error != 0) {
- device_printf(bus, "Generic tuning failed.\n");
- return (error);
- }
-
- /*
- * Tuning is done after a corresponding interrupt
- * is set.
- */
- error = sdhci_fsl_poll_register(sc, SDHCI_FSL_IRQSTAT,
- SDHCI_FSL_IRQSTAT_BRR, SDHCI_FSL_IRQSTAT_BRR);
- if (error)
- device_printf(sc->dev,
- "Timeout while waiting for hardware to finish tuning.\n");
-
- /* Clear IRQSTAT[BRR] register. */
- reg = RD4(sc, SDHCI_FSL_IRQSTAT);
- reg &= ~SDHCI_FSL_IRQSTAT_BRR;
- WR4(sc, SDHCI_FSL_IRQSTAT, reg);
-
- /* Check SYSCTL2[EXTN] register. */
- if ((RD4(sc, SDHCI_FSL_AUTOCERR) &
- SDHCI_FSL_AUTOCERR_EXTN) == 0)
- break;
-
- if (!retries--) {
- error = ENXIO;
- device_printf(bus,
- "Failed to execute HW tuning.\n");
- break;
- }
-
- DELAY(10);
- }
-
- /* Check SYSCTL2[SMPCLKSEL]. */
- reg = RD4(sc, SDHCI_FSL_AUTOCERR);
- if (!(reg & SDHCI_FSL_AUTOCERR_SMPCLKSEL)) {
- error = ENXIO;
- device_printf(bus,
- "Hardware tuning failed. Turning off tuning block\n");
- sdhci_fsl_switch_tuning_block(bus, false);
- } else
- error = 0;
-
- return (error);
-}
-
static int
sdhci_fsl_sw_tuning(struct sdhci_fsl_fdt_softc *sc, device_t bus,
device_t child, bool hs400, uint32_t wnd_start, uint32_t wnd_end)
{
- uint32_t start_p, end_p, reg;
+ uint32_t reg;
int error;
- wnd_start = 5 * sc->soc_data->baseclk_div;
- wnd_end = 3 * sc->soc_data->baseclk_div;
-
- reg = RD4(sc, SDHCI_FSL_TBCTL);
- reg &= ~SDHCI_FSL_TBCTL_TB_MODE_MASK;
- reg |= SDHCI_FSL_TBCTL_MODE_SW;
- WR4(sc, SDHCI_FSL_TBCTL, reg);
-
- reg = RD4(sc, SDHCI_FSL_TBPTR);
- start_p = reg | SDHCI_FSL_TBPTR_WND_START;
- end_p = reg | SDHCI_FSL_TBPTR_WND_END;
-
- if (abs(start_p - end_p) > (4 * sc->soc_data->baseclk_div + 2)) {
- wnd_start = 8 * sc->soc_data->baseclk_div;
- wnd_end = 4 * sc->soc_data->baseclk_div;
+ if (sc->soc_data->errata & SDHCI_FSL_TUNING_ERRATUM_TYPE1 ||
+ abs(wnd_start - wnd_end) <= (4 * sc->div_ratio + 2)) {
+ wnd_start = 5 * sc->div_ratio;
+ wnd_end = 3 * sc->div_ratio;
} else {
- wnd_start = 5 * sc->soc_data->baseclk_div;
- wnd_end = 3 * sc->soc_data->baseclk_div;
+ wnd_start = 8 * sc->div_ratio;
+ wnd_end = 4 * sc->div_ratio;
}
- reg |= (wnd_start << 14);
- reg |= (wnd_end << 6);
+ reg = RD4(sc, SDHCI_FSL_TBPTR);
+ reg &= ~SDHCI_FSL_TBPTR_WND_MASK;
+ reg &= ~(SDHCI_FSL_TBPTR_WND_MASK << SDHCI_FSL_TBPTR_WND_START_SHIFT);
+ reg |= wnd_start << SDHCI_FSL_TBPTR_WND_START_SHIFT;
+ reg |= wnd_end;
WR4(sc, SDHCI_FSL_TBPTR, reg);
+ /*
+ * Normally those are supposed to be set in sdhci_execute_tuning.
+ * However in our case we need a small delay between setting the two.
+ */
reg = RD4(sc, SDHCI_FSL_AUTOCERR);
- reg |= SDHCI_FSL_AUTOCERR_EXTN | SDHCI_FSL_AUTOCERR_SMPCLKSEL;
+ reg |= SDHCI_FSL_AUTOCERR_EXTN;
WR4(sc, SDHCI_FSL_AUTOCERR, reg);
+ DELAY(10);
+ reg |= SDHCI_FSL_AUTOCERR_SMPCLKSEL;
+ WR4(sc, SDHCI_FSL_AUTOCERR, reg);
+
+ reg = RD4(sc, SDHCI_FSL_TBCTL);
+ reg &= ~SDHCI_FSL_TBCTL_TB_MODE_MASK;
+ reg |= SDHCI_FSL_TBCTL_MODE_SW;
+ WR4(sc, SDHCI_FSL_TBCTL, reg);
error = sdhci_generic_tune(bus, child, hs400);
if (error != 0) {
device_printf(bus,
"Failed to execute generic tune while performing software tuning.\n");
- return (error);
- }
-
- reg = RD4(sc, SDHCI_FSL_IRQSTAT);
- reg &= ~SDHCI_FSL_IRQSTAT_BRR;
- WR4(sc, SDHCI_FSL_IRQSTAT, reg);
-
- reg = RD4(sc, SDHCI_FSL_AUTOCERR);
- if (!(reg & SDHCI_FSL_AUTOCERR_SMPCLKSEL)) {
- /* Error occured, need to disable tuning block. */
- reg = RD4(sc, SDHCI_FSL_TBCTL);
- reg &= ~SDHCI_FSL_TBCTL_TBEN;
- WR4(sc, SDHCI_FSL_TBCTL, reg);
- error = ENXIO;
}
return (error);
@@ -1175,17 +1093,37 @@
static int
sdhci_fsl_fdt_tune(device_t bus, device_t child, bool hs400)
{
- uint32_t wnd_start, wnd_end, clk_divider, reg;
struct sdhci_fsl_fdt_softc *sc;
+ uint32_t wnd_start, wnd_end;
+ uint32_t clk_divider, reg;
struct sdhci_slot *slot;
int error;
sc = device_get_softc(bus);
slot = device_get_ivars(child);
error = 0;
- clk_divider = slot->max_clk / slot->clock;
+ clk_divider = sc->baseclk_hz / slot->clock;
+
+ switch (sc->slot.host.ios.timing) {
+ case bus_timing_mmc_hs400:
+ return (EINVAL);
+ case bus_timing_mmc_hs200:
+ case bus_timing_uhs_ddr50:
+ case bus_timing_uhs_sdr104:
+ break;
+ case bus_timing_uhs_sdr50:
+ if (slot->opt & SDHCI_SDR50_NEEDS_TUNING)
+ break;
+ default:
+ return (0);
+ }
- /* For tuning mode SD clock divider must be within 3 to 16. */
+ /*
+ * For tuning mode SD clock divider must be within 3 to 16.
+ * We also need to match the frequency to whatever mode is used.
+ * For that reason we're just bailing if the dividers don't match
+ * that requirement.
+ */
if (clk_divider < 3 || clk_divider > 16)
return (ENXIO);
@@ -1200,9 +1138,9 @@
SDHCI_FSL_PRES_SDSTB, SDHCI_FSL_PRES_SDSTB);
if (error != 0)
device_printf(bus,
- "Timeout while waiting for hardware.\n");
+ "Timeout while waiting for clock to stabilize.\n");
- /* Set ESDHCCTL[FAF] register. */
+ /* Flush async IO. */
reg = RD4(sc, SDHCI_FSL_ESDHC_CTRL);
reg |= SDHCI_FSL_ESDHC_CTRL_FAF;
WR4(sc, SDHCI_FSL_ESDHC_CTRL, reg);
@@ -1214,13 +1152,13 @@
device_printf(bus,
"Timeout while waiting for hardware.\n");
- /* Set AUTOCERR[UHSM] register. */
- reg = RD4(sc, SDHCI_FSL_AUTOCERR);
- reg &= ~SDHCI_FSL_AUTOCERR_UHMS;
- reg |= SDHCI_FSL_AUTOCERR_UHMS_HS200;
- WR4(sc, SDHCI_FSL_AUTOCERR, reg);
-
- /* Set TBCTL[TB_EN] register and program valid tuning mode. */
+ /*
+ * Set TBCTL[TB_EN] register and program valid tuning mode.
+ * According to RM MODE_3 means that:
+ * "eSDHC takes care of the re-tuning during data transfer
+ * (auto re-tuning).".
+ * Tuning mode can only be changed while the clock is disabled.
+ */
reg = RD4(sc, SDHCI_FSL_TBCTL);
reg &= ~SDHCI_FSL_TBCTL_TB_MODE_MASK;
reg |= SDHCI_FSL_TBCTL_TBEN | SDHCI_FSL_TBCTL_MODE_3;
@@ -1237,24 +1175,29 @@
"Timeout while waiting for clock to stabilize.\n");
/* Perform hardware tuning. */
- error = sdhci_hw_tuning(sc, bus, child, hs400);
+ error = sdhci_generic_tune(bus, child, hs400);
reg = RD4(sc, SDHCI_FSL_TBPTR);
- wnd_start = reg & SDHCI_FSL_TBPTR_WND_START;
- wnd_end = reg & SDHCI_FSL_TBPTR_WND_END;
-
- /* For erratum type2 affected platforms, check tuning pointer window. */
- if (sc->soc_data->errata & SDHCI_FSL_TUNING_ERRATUM_TYPE2) {
- if (abs(wnd_start - wnd_end) >
- (4 * sc->soc_data->baseclk_div + 2))
- error = ENXIO;
+ wnd_start = reg >> SDHCI_FSL_TBPTR_WND_START_SHIFT;
+ wnd_start &= SDHCI_FSL_TBPTR_WND_MASK;
+ wnd_end = reg & SDHCI_FSL_TBPTR_WND_MASK;
+
+ /*
+ * For erratum type2 affected platforms, the controller can erroneously
+ * declare that the tuning was successful. Verify the tuning window to
+ * make sure that we're fine.
+ */
+ if (error == 0 &&
+ sc->soc_data->errata & SDHCI_FSL_TUNING_ERRATUM_TYPE2 &&
+ abs(wnd_start - wnd_end) > (4 * sc->div_ratio + 2)) {
+ error = EIO;
}
+ /* If hardware tuning failed, try software tuning. */
if (error != 0 &&
(sc->soc_data->errata &
(SDHCI_FSL_TUNING_ERRATUM_TYPE1 |
SDHCI_FSL_TUNING_ERRATUM_TYPE2))) {
- /* If hardware tuning failed, try software tuning. */
error = sdhci_fsl_sw_tuning(sc, bus, child, hs400, wnd_start,
wnd_end);
if (error != 0)
@@ -1263,33 +1206,58 @@
if (error != 0) {
sdhci_fsl_switch_tuning_block(bus, false);
-
return (error);
}
-
if (hs400) {
reg = RD4(sc, SDHCI_FSL_SDTIMINGCTL);
reg |= SDHCI_FSL_SDTIMINGCTL_FLW_CTL;
WR4(sc, SDHCI_FSL_SDTIMINGCTL, reg);
-
- /*
- * Tuning block needs to be disabled now.
- * It will be enabled by set_uhs_signalling
- */
- reg = RD4(sc, SDHCI_FSL_TBCTL);
- reg &= ~SDHCI_FSL_TBCTL_TBEN;
- WR4(sc, SDHCI_FSL_TBCTL, reg);
}
return (0);
}
+static int
+sdhci_fsl_fdt_retune(device_t bus, device_t child, bool reset)
+{
+ struct sdhci_slot *slot;
+ struct sdhci_fsl_fdt_softc *sc;
+
+ slot = device_get_ivars(child);
+ sc = device_get_softc(bus);
+
+ if (!(slot->opt & SDHCI_TUNING_ENABLED))
+ return (0);
+
+ /* HS400 must be tuned in HS200 mode. */
+ if (slot->host.ios.timing == bus_timing_mmc_hs400)
+ return (EINVAL);
+
+ /*
+ * Only re-tuning with full reset is supported.
+ * The controller is normally put in "mode 3", which means that
+ * periodic re-tuning is done automatically. See comment in
+ * sdhci_fsl_fdt_tune for details.
+ * Because of that re-tuning should only be triggered as a result
+ * of a CRC error.
+ */
+ if (!reset)
+ return (ENOTSUP);
+
+ return (sdhci_fsl_fdt_tune(bus, child,
+ sc->flags & SDHCI_FSL_HS400_FLAG));
+}
static void
sdhci_fsl_disable_hs400_mode(device_t dev, struct sdhci_fsl_fdt_softc *sc)
{
uint32_t reg;
int error;
+ /* Check if HS400 is enabled right now. */
+ reg = RD4(sc, SDHCI_FSL_TBCTL);
+ if ((reg & SDHCI_FSL_TBCTL_HS400_EN) == 0)
+ return;
+
reg = RD4(sc, SDHCI_FSL_SDTIMINGCTL);
reg &= ~SDHCI_FSL_SDTIMINGCTL_FLW_CTL;
WR4(sc, SDHCI_FSL_SDTIMINGCTL, reg);
@@ -1343,24 +1311,46 @@
error = sdhci_fsl_poll_register(sc, SDHCI_FSL_PRES_STATE,
SDHCI_FSL_PRES_SDSTB, SDHCI_FSL_PRES_SDSTB);
if (error != 0)
- device_printf(dev, "Internal clock never stabilized.\n");
+ device_printf(dev,
+ "Timeout while waiting for clock to stabilize.\n");
+
+ reg = RD4(sc, SDHCI_FSL_TBCTL);
+ reg |= SDHCI_FSL_TBCTL_HS400_EN;
+ WR4(sc, SDHCI_FSL_TBCTL, reg);
+ reg = RD4(sc, SDHCI_FSL_SDCLKCTL);
+ reg |= SDHCI_FSL_SDCLKCTL_CMD_CLK_CTL;
+ WR4(sc, SDHCI_FSL_SDCLKCTL, reg);
fsl_sdhc_fdt_set_clock(sc, slot, SDHCI_CLOCK_CARD_EN |
sc->sdclk_bits);
error = sdhci_fsl_poll_register(sc, SDHCI_FSL_PRES_STATE,
SDHCI_FSL_PRES_SDSTB, SDHCI_FSL_PRES_SDSTB);
if (error != 0)
- device_printf(dev, "Internal clock never stabilized.\n");
+ device_printf(dev,
+ "Timeout while waiting for clock to stabilize.\n");
- reg = sc->read(sc, SDHCI_FSL_DLLCFG0);
- reg |= SDHCI_FSL_DLLCFG0_EN | SDHCI_FSL_DLLCFG0_FREQ_SEL;
- sc->write(sc, SDHCI_FSL_DLLCFG0, reg);
+ reg = RD4(sc, SDHCI_FSL_DLLCFG0);
+ reg |= SDHCI_FSL_DLLCFG0_EN | SDHCI_FSL_DLLCFG0_RESET |
+ SDHCI_FSL_DLLCFG0_FREQ_SEL;
+ WR4(sc, SDHCI_FSL_DLLCFG0, reg);
+
+ /*
+ * The reset bit is not a self clearing one.
+ * Give it some time and clear it manually.
+ */
+ DELAY(100);
+ reg &= ~SDHCI_FSL_DLLCFG0_RESET;
+ WR4(sc, SDHCI_FSL_DLLCFG0, reg);
- DELAY(20);
+ error = sdhci_fsl_poll_register(sc, SDHCI_FSL_DLLSTAT0,
+ SDHCI_FSL_DLLSTAT0_SLV_STS, SDHCI_FSL_DLLSTAT0_SLV_STS);
+ if (error != 0)
+ device_printf(dev,
+ "Timeout while waiting for DLL0.\n");
- reg = sc->read(sc, SDHCI_FSL_TBCTL);
+ reg = RD4(sc, SDHCI_FSL_TBCTL);
reg |= SDHCI_FSL_TBCTL_HS400_WND_ADJ;
- sc->write(sc, SDHCI_FSL_TBCTL, reg);
+ WR4(sc, SDHCI_FSL_TBCTL, reg);
fsl_sdhc_fdt_set_clock(sc, slot, sc->sdclk_bits);
@@ -1368,11 +1358,11 @@
SDHCI_FSL_PRES_SDSTB, SDHCI_FSL_PRES_SDSTB);
if (error != 0)
device_printf(dev,
- "Timeout while waiting for clock to stabilize.\n");
+ "timeout while waiting for clock to stabilize.\n");
- reg = sc->read(sc, SDHCI_FSL_ESDHC_CTRL);
+ reg = RD4(sc, SDHCI_FSL_ESDHC_CTRL);
reg |= SDHCI_FSL_ESDHC_CTRL_FAF;
- sc->write(sc, SDHCI_FSL_ESDHC_CTRL, reg);
+ WR4(sc, SDHCI_FSL_ESDHC_CTRL, reg);
error = sdhci_fsl_poll_register(sc, SDHCI_FSL_ESDHC_CTRL,
SDHCI_FSL_ESDHC_CTRL_FAF, 0);
@@ -1388,50 +1378,55 @@
if (error != 0)
device_printf(dev,
"Timeout while waiting for clock to stabilize.\n");
-
- sdhci_generic_set_uhs_timing(dev, slot);
- sc->flags = SDHCI_FSL_HS400_DONE;
}
static void
sdhci_fsl_fdt_set_uhs_timing(device_t dev, struct sdhci_slot *slot)
{
struct sdhci_fsl_fdt_softc *sc;
- uint32_t reg;
+ const struct mmc_ios *ios;
+ uint32_t mode, reg;
sc = device_get_softc(dev);
+ ios = &slot->host.ios;
+ mode = 0;
- if (sc->flags & SDHCI_FSL_HS400_DONE)
- return;
-
- reg = RD4(sc, SDHCI_FSL_TBCTL);
-
- if (slot->host.ios.timing == bus_timing_hs &&
- (!(sc->flags & SDHCI_FSL_SWITCH_TO_HS400_FLAG)) &&
- sc->last_mode >= bus_timing_mmc_hs200) {
- sdhci_fsl_switch_tuning_block(dev, false);
-
- if (reg & SDHCI_FSL_TBCTL_HS400_EN)
- sdhci_fsl_disable_hs400_mode(dev, sc);
-
- sc->flags |= SDHCI_FSL_SWITCH_TO_HS400_FLAG;
-
- return;
- }
-
- if ((slot->host.ios.timing == bus_timing_mmc_hs400) &&
- sc->flags & SDHCI_FSL_SWITCH_TO_HS400_FLAG) {
- if (sc->last_mode < bus_timing_uhs_sdr50) {
- sc->last_mode = sc->slot.host.ios.timing;
- return;
- }
-
+ /*
+ * When we switch to HS400 this function is called twice.
+ * First after the timing is set, and then after the clock
+ * is changed to the target frequency.
+ * The controller can be switched to HS400 only after the latter
+ * is done.
+ */
+ if (slot->host.ios.timing == bus_timing_mmc_hs400 &&
+ ios->clock > SD_SDR50_MAX)
sdhci_fsl_enable_hs400_mode(dev, slot, sc);
+ else if (slot->host.ios.timing < bus_timing_mmc_hs400) {
+ sdhci_fsl_disable_hs400_mode(dev, sc);
- } else
- sdhci_generic_set_uhs_timing(dev, slot);
-
- sc->last_mode = sc->slot.host.ios.timing;
+ /*
+ * Switching to HS400 requires a custom procedure executed in
+ * sdhci_fsl_enable_hs400_mode in case above.
+ * For all other modes we just need to set the corresponding flag.
+ */
+ reg = RD4(sc, SDHCI_FSL_AUTOCERR);
+ reg &= ~SDHCI_FSL_AUTOCERR_UHMS;
+ if (ios->clock > SD_SDR50_MAX)
+ mode = SDHCI_CTRL2_UHS_SDR104;
+ else if (ios->clock > SD_SDR25_MAX)
+ mode = SDHCI_CTRL2_UHS_SDR50;
+ else if (ios->clock > SD_SDR12_MAX) {
+ if (ios->timing == bus_timing_uhs_ddr50 ||
+ ios->timing == bus_timing_mmc_ddr52)
+ mode = SDHCI_CTRL2_UHS_DDR50;
+ else
+ mode = SDHCI_CTRL2_UHS_SDR25;
+ } else if (ios->clock > SD_MMC_CARD_ID_FREQUENCY)
+ mode = SDHCI_CTRL2_UHS_SDR12;
+
+ reg |= mode << SDHCI_FSL_AUTOCERR_UHMS_SHIFT;
+ WR4(sc, SDHCI_FSL_AUTOCERR, reg);
+ }
}
static const device_method_t sdhci_fsl_fdt_methods[] = {
@@ -1452,7 +1447,7 @@
DEVMETHOD(mmcbr_switch_vccq, sdhci_fsl_fdt_switch_vccq),
DEVMETHOD(mmcbr_update_ios, sdhci_fsl_fdt_update_ios),
DEVMETHOD(mmcbr_tune, sdhci_fsl_fdt_tune),
- DEVMETHOD(mmcbr_retune, sdhci_generic_retune),
+ DEVMETHOD(mmcbr_retune, sdhci_fsl_fdt_retune),
/* SDHCI accessors. */
DEVMETHOD(sdhci_read_1, sdhci_fsl_fdt_read_1),
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Thu, May 1, 9:49 AM (6 h, 45 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
17878845
Default Alt Text
D34027.diff (19 KB)
Attached To
Mode
D34027: sdhci_fsl_fdt: Fix tuning code
Attached
Detach File
Event Timeline
Log In to Comment