+// SPDX-License-Identifier: GPL-2.0+
/*
* Copyright 2007, 2010-2011 Freescale Semiconductor, Inc
* Andy Fleming
* Based vaguely on the pxa mmc code:
* (C) Copyright 2003
* Kyle Harris, Nexus Technologies, Inc. kharris@nexus-tech.net
- *
- * SPDX-License-Identifier: GPL-2.0+
*/
#include <config.h>
#include <common.h>
#include <command.h>
+#include <clk.h>
#include <errno.h>
#include <hwconfig.h>
#include <mmc.h>
struct fsl_esdhc_priv {
struct fsl_esdhc *esdhc_regs;
unsigned int sdhc_clk;
+ struct clk per_clk;
unsigned int clock;
unsigned int mode;
unsigned int bus_width;
int timeout;
struct fsl_esdhc *regs = priv->esdhc_regs;
#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \
- defined(CONFIG_MX8M)
+ defined(CONFIG_IMX8) || defined(CONFIG_IMX8M)
dma_addr_t addr;
#endif
uint wml_value;
esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value);
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \
- defined(CONFIG_MX8M)
+ defined(CONFIG_IMX8) || defined(CONFIG_IMX8M)
addr = virt_to_phys((void *)(data->dest));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
wml_value << 16);
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \
- defined(CONFIG_MX8M)
+ defined(CONFIG_IMX8) || defined(CONFIG_IMX8M)
addr = virt_to_phys((void *)(data->src));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
unsigned size = roundup(ARCH_DMA_MINALIGN,
data->blocks*data->blocksize);
#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) || \
- defined(CONFIG_MX8M)
+ defined(CONFIG_IMX8) || defined(CONFIG_IMX8M)
dma_addr_t addr;
addr = virt_to_phys((void *)(data->dest));
invalidate_dcache_range(start, end);
}
+#ifdef CONFIG_MCF5441x
+/*
+ * Swaps 32-bit words to little-endian byte order.
+ */
+static inline void sd_swap_dma_buff(struct mmc_data *data)
+{
+ int i, size = data->blocksize >> 2;
+ u32 *buffer = (u32 *)data->dest;
+ u32 sw;
+
+ while (data->blocks--) {
+ for (i = 0; i < size; i++) {
+ sw = __sw32(*buffer);
+ *buffer++ = sw;
+ }
+ }
+}
+#endif
+
/*
* Sends a command out on the bus. Takes the mmc pointer,
* a command pointer, and an optional data pointer.
uint irqstat;
u32 flags = IRQSTAT_CC | IRQSTAT_CTOE;
struct fsl_esdhc *regs = priv->esdhc_regs;
+ unsigned long start;
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
flags = IRQSTAT_BRR;
/* Wait for the command to complete */
- while (!(esdhc_read32(®s->irqstat) & flags))
- ;
+ start = get_timer(0);
+ while (!(esdhc_read32(®s->irqstat) & flags)) {
+ if (get_timer(start) > 1000) {
+ err = -ETIMEDOUT;
+ goto out;
+ }
+ }
irqstat = esdhc_read32(®s->irqstat);
* cache-fill during the DMA operations such as the
* speculative pre-fetching etc.
*/
- if (data->flags & MMC_DATA_READ)
+ if (data->flags & MMC_DATA_READ) {
check_and_invalidate_dcache_range(cmd, data);
+#ifdef CONFIG_MCF5441x
+ sd_swap_dma_buff(data);
+#endif
+ }
#endif
}
break;
case UHS_SDR104:
case MMC_HS_200:
+ case MMC_HS_400:
ret = pinctrl_select_state(dev, "state_200mhz");
break;
default:
}
}
+static void esdhc_set_strobe_dll(struct mmc *mmc)
+{
+ struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev);
+ struct fsl_esdhc *regs = priv->esdhc_regs;
+ u32 val;
+
+ if (priv->clock > ESDHC_STROBE_DLL_CLK_FREQ) {
+ writel(ESDHC_STROBE_DLL_CTRL_RESET, ®s->strobe_dllctrl);
+
+ /*
+ * enable strobe dll ctrl and adjust the delay target
+ * for the uSDHC loopback read clock
+ */
+ val = ESDHC_STROBE_DLL_CTRL_ENABLE |
+ (priv->strobe_dll_delay_target <<
+ ESDHC_STROBE_DLL_CTRL_SLV_DLY_TARGET_SHIFT);
+ writel(val, ®s->strobe_dllctrl);
+ /* wait 1us to make sure strobe dll status register stable */
+ mdelay(1);
+ val = readl(®s->strobe_dllstat);
+ if (!(val & ESDHC_STROBE_DLL_STS_REF_LOCK))
+ pr_warn("HS400 strobe DLL status REF not lock!\n");
+ if (!(val & ESDHC_STROBE_DLL_STS_SLV_LOCK))
+ pr_warn("HS400 strobe DLL status SLV not lock!\n");
+ }
+}
+
static int esdhc_set_timing(struct mmc *mmc)
{
struct fsl_esdhc_priv *priv = dev_get_priv(mmc->dev);
case MMC_LEGACY:
case SD_LEGACY:
esdhc_reset_tuning(mmc);
+ writel(mixctrl, ®s->mixctrl);
+ break;
+ case MMC_HS_400:
+ mixctrl |= MIX_CTRL_DDREN | MIX_CTRL_HS400_EN;
+ writel(mixctrl, ®s->mixctrl);
+ esdhc_set_strobe_dll(mmc);
break;
case MMC_HS:
case MMC_HS_52:
case MMC_SIGNAL_VOLTAGE_330:
if (priv->vs18_enable)
return -EIO;
-#ifdef CONFIG_DM_REGULATOR
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) {
ret = regulator_set_value(priv->vqmmc_dev, 3300000);
if (ret) {
return -EAGAIN;
case MMC_SIGNAL_VOLTAGE_180:
-#ifdef CONFIG_DM_REGULATOR
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
if (!IS_ERR_OR_NULL(priv->vqmmc_dev)) {
ret = regulator_set_value(priv->vqmmc_dev, 1800000);
if (ret) {
#endif
/* Set the initial clock speed */
- mmc_set_clock(mmc, 400000, false);
+ mmc_set_clock(mmc, 400000, MMC_CLK_ENABLE);
/* Disable the BRR and BWR bits in IRQSTAT */
esdhc_clrbits32(®s->irqstaten, IRQSTATEN_BRR | IRQSTATEN_BWR);
+#ifdef CONFIG_MCF5441x
+ esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD);
+#else
/* Put the PROCTL reg back to the default */
esdhc_write32(®s->proctl, PROCTL_INIT);
+#endif
/* Set timout to the maximum value */
esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16);
if (ret)
return ret;
+#ifdef CONFIG_MCF5441x
+ /* ColdFire, using SDHC_DATA[3] for card detection */
+ esdhc_write32(®s->proctl, PROCTL_INIT | PROCTL_D3CD);
+#endif
+
#ifndef CONFIG_FSL_USDHC
esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN
| SYSCTL_IPGEN | SYSCTL_CKEN);
voltage_caps = 0;
caps = esdhc_read32(®s->hostcapblt);
+#ifdef CONFIG_MCF5441x
+ /*
+ * MCF5441x RM declares in more points that sdhc clock speed must
+ * never exceed 25 Mhz. From this, the HS bit needs to be disabled
+ * from host capabilities.
+ */
+ caps &= ~ESDHC_HOSTCAPBLT_HSS;
+#endif
+
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC135
caps = caps & ~(ESDHC_HOSTCAPBLT_SRS |
ESDHC_HOSTCAPBLT_VS18 | ESDHC_HOSTCAPBLT_VS30);
int node = dev_of_offset(dev);
struct esdhc_soc_data *data =
(struct esdhc_soc_data *)dev_get_driver_data(dev);
-#ifdef CONFIG_DM_REGULATOR
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
struct udevice *vqmmc_dev;
#endif
fdt_addr_t addr;
priv->vs18_enable = 0;
-#ifdef CONFIG_DM_REGULATOR
+#if CONFIG_IS_ENABLED(DM_REGULATOR)
/*
* If emmc I/O has a fixed voltage at 1.8V, this must be provided,
* otherwise, emmc will work abnormally.
#endif
if (fdt_get_property(fdt, node, "no-1-8-v", NULL))
- priv->caps &= ~(UHS_CAPS | MMC_MODE_HS200);
+ priv->caps &= ~(UHS_CAPS | MMC_MODE_HS200 | MMC_MODE_HS400);
/*
* TODO:
init_clk_usdhc(dev->seq);
- priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
- if (priv->sdhc_clk <= 0) {
- dev_err(dev, "Unable to get clk for %s\n", dev->name);
- return -EINVAL;
+ if (IS_ENABLED(CONFIG_CLK)) {
+ /* Assigned clock already set clock */
+ ret = clk_get_by_name(dev, "per", &priv->per_clk);
+ if (ret) {
+ printf("Failed to get per_clk\n");
+ return ret;
+ }
+ ret = clk_enable(&priv->per_clk);
+ if (ret) {
+ printf("Failed to enable per_clk\n");
+ return ret;
+ }
+
+ priv->sdhc_clk = clk_get_rate(&priv->per_clk);
+ } else {
+ priv->sdhc_clk = mxc_get_clock(MXC_ESDHC_CLK + dev->seq);
+ if (priv->sdhc_clk <= 0) {
+ dev_err(dev, "Unable to get clk for %s\n", dev->name);
+ return -EINVAL;
+ }
}
ret = fsl_esdhc_init(priv, plat);
{
struct fsl_esdhc_priv *priv = dev_get_priv(dev);
- return true;
return esdhc_getcd_common(priv);
}
};
static const struct udevice_id fsl_esdhc_ids[] = {
+ { .compatible = "fsl,imx53-esdhc", },
{ .compatible = "fsl,imx6ul-usdhc", },
{ .compatible = "fsl,imx6sx-usdhc", },
{ .compatible = "fsl,imx6sl-usdhc", },