#include <fsl_esdhc.h>
#include <fdt_support.h>
#include <asm/io.h>
+#include <dm.h>
+#include <asm-generic/gpio.h>
DECLARE_GLOBAL_DATA_PTR;
uint fevt; /* Force event register */
uint admaes; /* ADMA error status register */
uint adsaddr; /* ADMA system address register */
- char reserved2[100]; /* reserved */
- uint vendorspec; /* Vendor Specific register */
- char reserved3[56]; /* reserved */
+ char reserved2[4];
+ uint dllctrl;
+ uint dllstat;
+ uint clktunectrlstatus;
+ char reserved3[84];
+ uint vendorspec;
+ uint mmcboot;
+ uint vendorspec2;
+ char reserved4[48];
uint hostver; /* Host controller version register */
- char reserved4[4]; /* reserved */
- uint dmaerraddr; /* DMA error address register */
char reserved5[4]; /* reserved */
- uint dmaerrattr; /* DMA error attribute register */
+ uint dmaerraddr; /* DMA error address register */
char reserved6[4]; /* reserved */
+ uint dmaerrattr; /* DMA error attribute register */
+ char reserved7[4]; /* reserved */
uint hostcapblt2; /* Host controller capabilities register 2 */
- char reserved7[8]; /* reserved */
+ char reserved8[8]; /* reserved */
uint tcr; /* Tuning control register */
- char reserved8[28]; /* reserved */
+ char reserved9[28]; /* reserved */
uint sddirctl; /* SD direction control register */
- char reserved9[712]; /* reserved */
+ char reserved10[712];/* reserved */
uint scr; /* eSDHC control register */
};
+/**
+ * struct fsl_esdhc_priv
+ *
+ * @esdhc_regs: registers of the sdhc controller
+ * @sdhc_clk: Current clk of the sdhc controller
+ * @bus_width: bus width, 1bit, 4bit or 8bit
+ * @cfg: mmc config
+ * @mmc: mmc
+ * Following is used when Driver Model is enabled for MMC
+ * @dev: pointer for the device
+ * @non_removable: 0: removable; 1: non-removable
+ * @wp_enable: 1: enable checking wp; 0: no check
+ * @cd_gpio: gpio for card detection
+ * @wp_gpio: gpio for write protection
+ */
+struct fsl_esdhc_priv {
+ struct fsl_esdhc *esdhc_regs;
+ unsigned int sdhc_clk;
+ unsigned int bus_width;
+ struct mmc_config cfg;
+ struct mmc *mmc;
+ struct udevice *dev;
+ int non_removable;
+ int wp_enable;
+ struct gpio_desc cd_gpio;
+ struct gpio_desc wp_gpio;
+};
+
/* Return the XFERTYP flags for a given command and data packet */
static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data)
{
else if (cmd->resp_type & MMC_RSP_PRESENT)
xfertyp |= XFERTYP_RSPTYP_48;
-#if defined(CONFIG_MX53) || defined(CONFIG_PPC_T4240) || \
- defined(CONFIG_LS102XA) || defined(CONFIG_LS2085A)
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
xfertyp |= XFERTYP_CMDTYP_ABORT;
-#endif
+
return XFERTYP_CMD(cmd->cmdidx) | xfertyp;
}
static void
esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data)
{
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
uint blocks;
char *buffer;
uint databuf;
static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data)
{
int timeout;
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
-#ifdef CONFIG_LS2085A
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
+#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234)
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
-#ifdef CONFIG_LS2085A
+#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234)
addr = virt_to_phys((void *)(data->dest));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
#endif
if (wml_value > WML_WR_WML_MAX)
wml_value = WML_WR_WML_MAX_VAL;
- if ((esdhc_read32(®s->prsstat) & PRSSTAT_WPSPL) == 0) {
- printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
- return TIMEOUT;
+ if (priv->wp_enable) {
+ if ((esdhc_read32(®s->prsstat) &
+ PRSSTAT_WPSPL) == 0) {
+ printf("\nThe SD card is locked. Can not write to a locked card.\n\n");
+ return TIMEOUT;
+ }
}
esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK,
wml_value << 16);
#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
-#ifdef CONFIG_LS2085A
+#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234)
addr = virt_to_phys((void *)(data->src));
if (upper_32_bits(addr))
printf("Error found for upper 32 bits\n");
* Rounding up to next power of 2
* => timeout + 13 = log2(mmc->clock/4) + 1
* => timeout + 13 = fls(mmc->clock/4)
+ *
+ * However, the MMC spec "It is strongly recommended for hosts to
+ * implement more than 500ms timeout value even if the card
+ * indicates the 250ms maximum busy length." Even the previous
+ * value of 300ms is known to be insufficient for some cards.
+ * So, we use
+ * => timeout + 13 = fls(mmc->clock/2)
*/
- timeout = fls(mmc->clock/4);
+ timeout = fls(mmc->clock/2);
timeout -= 13;
if (timeout > 14)
return 0;
}
-#ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO
static void check_and_invalidate_dcache_range
(struct mmc_cmd *cmd,
struct mmc_data *data) {
-#ifdef CONFIG_LS2085A
unsigned start = 0;
-#else
- unsigned start = (unsigned)data->dest ;
-#endif
+ unsigned end = 0;
unsigned size = roundup(ARCH_DMA_MINALIGN,
data->blocks*data->blocksize);
- unsigned end = start+size ;
-#ifdef CONFIG_LS2085A
+#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234)
dma_addr_t addr;
addr = virt_to_phys((void *)(data->dest));
printf("Error found for upper 32 bits\n");
else
start = lower_32_bits(addr);
+#else
+ start = (unsigned)data->dest;
#endif
+ end = start + size;
invalidate_dcache_range(start, end);
}
-#endif
/*
* Sends a command out on the bus. Takes the mmc pointer,
int err = 0;
uint xfertyp;
uint irqstat;
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
#ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111
if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION)
err = esdhc_setup_data(mmc, data);
if(err)
return err;
+
+ if (data->flags & MMC_DATA_READ)
+ check_and_invalidate_dcache_range(cmd, data);
}
/* Figure out the transfer arguments */
}
} while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE);
+ /*
+ * Need invalidate the dcache here again to avoid any
+ * cache-fill during the DMA operations such as the
+ * speculative pre-fetching etc.
+ */
if (data->flags & MMC_DATA_READ)
check_and_invalidate_dcache_range(cmd, data);
#endif
static void set_sysctl(struct mmc *mmc, uint clock)
{
int div, pre_div;
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- volatile struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
- int sdhc_clk = cfg->sdhc_clk;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
+ int sdhc_clk = priv->sdhc_clk;
uint clk;
if (clock < mmc->cfg->f_min)
clk = (pre_div << 8) | (div << 4);
+#ifdef CONFIG_FSL_USDHC
+ esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN);
+#else
esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN);
+#endif
esdhc_clrsetbits32(®s->sysctl, SYSCTL_CLOCK_MASK, clk);
udelay(10000);
- clk = SYSCTL_PEREN | SYSCTL_CKEN;
+#ifdef CONFIG_FSL_USDHC
+ esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | VENDORSPEC_CKEN);
+#else
+ esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN);
+#endif
- esdhc_setbits32(®s->sysctl, clk);
}
#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
static void esdhc_clock_control(struct mmc *mmc, bool enable)
{
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
u32 value;
u32 time_out;
static void esdhc_set_ios(struct mmc *mmc)
{
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
#ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK
/* Select to use peripheral clock */
static int esdhc_init(struct mmc *mmc)
{
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
int timeout = 1000;
/* Reset the entire host controller */
while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout)
udelay(1000);
+#if defined(CONFIG_FSL_USDHC)
+ /* RSTA doesn't reset MMC_BOOT register, so manually reset it */
+ esdhc_write32(®s->mmcboot, 0x0);
+ /* Reset MIX_CTRL and CLK_TUNE_CTRL_STATUS regs to 0 */
+ esdhc_write32(®s->mixctrl, 0x0);
+ esdhc_write32(®s->clktunectrlstatus, 0x0);
+
+ /* Put VEND_SPEC to default value */
+ esdhc_write32(®s->vendorspec, VENDORSPEC_INIT);
+
+ /* Disable DLL_CTRL delay line */
+ esdhc_write32(®s->dllctrl, 0x0);
+#endif
+
#ifndef ARCH_MXC
/* Enable cache snooping */
esdhc_write32(®s->scr, 0x00000040);
#endif
+#ifndef CONFIG_FSL_USDHC
esdhc_setbits32(®s->sysctl, SYSCTL_HCKEN | SYSCTL_IPGEN);
+#else
+ esdhc_setbits32(®s->vendorspec, VENDORSPEC_HCKEN | VENDORSPEC_IPGEN);
+#endif
/* Set the initial clock speed */
mmc_set_clock(mmc, 400000);
static int esdhc_getcd(struct mmc *mmc)
{
- struct fsl_esdhc_cfg *cfg = mmc->priv;
- struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ struct fsl_esdhc_priv *priv = mmc->priv;
+ struct fsl_esdhc *regs = priv->esdhc_regs;
int timeout = 1000;
#ifdef CONFIG_ESDHC_DETECT_QUIRK
if (CONFIG_ESDHC_DETECT_QUIRK)
return 1;
#endif
+
+#ifdef CONFIG_DM_MMC
+ if (priv->non_removable)
+ return 1;
+
+ if (dm_gpio_is_valid(&priv->cd_gpio))
+ return dm_gpio_get_value(&priv->cd_gpio);
+#endif
+
while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout)
udelay(1000);
.getcd = esdhc_getcd,
};
-int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
+static int fsl_esdhc_cfg_to_priv(struct fsl_esdhc_cfg *cfg,
+ struct fsl_esdhc_priv *priv)
+{
+ if (!cfg || !priv)
+ return -EINVAL;
+
+ priv->esdhc_regs = (struct fsl_esdhc *)(unsigned long)(cfg->esdhc_base);
+ priv->bus_width = cfg->max_bus_width;
+ priv->sdhc_clk = cfg->sdhc_clk;
+ priv->wp_enable = cfg->wp_enable;
+
+ return 0;
+};
+
+static int fsl_esdhc_init(struct fsl_esdhc_priv *priv)
{
struct fsl_esdhc *regs;
struct mmc *mmc;
u32 caps, voltage_caps;
- if (!cfg)
- return -1;
+ if (!priv)
+ return -EINVAL;
- regs = (struct fsl_esdhc *)cfg->esdhc_base;
+ regs = priv->esdhc_regs;
/* First reset the eSDHC controller */
esdhc_reset(regs);
+#ifndef CONFIG_FSL_USDHC
esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_HCKEN
| SYSCTL_IPGEN | SYSCTL_CKEN);
+#else
+ esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN |
+ VENDORSPEC_HCKEN | VENDORSPEC_IPGEN | VENDORSPEC_CKEN);
+#endif
writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten);
- memset(&cfg->cfg, 0, sizeof(cfg->cfg));
+ memset(&priv->cfg, 0, sizeof(priv->cfg));
voltage_caps = 0;
caps = esdhc_read32(®s->hostcapblt);
if (caps & ESDHC_HOSTCAPBLT_VS33)
voltage_caps |= MMC_VDD_32_33 | MMC_VDD_33_34;
- cfg->cfg.name = "FSL_SDHC";
- cfg->cfg.ops = &esdhc_ops;
+ priv->cfg.name = "FSL_SDHC";
+ priv->cfg.ops = &esdhc_ops;
#ifdef CONFIG_SYS_SD_VOLTAGE
- cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
+ priv->cfg.voltages = CONFIG_SYS_SD_VOLTAGE;
#else
- cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+ priv->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
#endif
- if ((cfg->cfg.voltages & voltage_caps) == 0) {
+ if ((priv->cfg.voltages & voltage_caps) == 0) {
printf("voltage not supported by controller\n");
return -1;
}
- cfg->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+ if (priv->bus_width == 8)
+ priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
+ else if (priv->bus_width == 4)
+ priv->cfg.host_caps = MMC_MODE_4BIT;
+
+ priv->cfg.host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT;
#ifdef CONFIG_SYS_FSL_ESDHC_HAS_DDR_MODE
- cfg->cfg.host_caps |= MMC_MODE_DDR_52MHz;
+ priv->cfg.host_caps |= MMC_MODE_DDR_52MHz;
#endif
- if (cfg->max_bus_width > 0) {
- if (cfg->max_bus_width < 8)
- cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
- if (cfg->max_bus_width < 4)
- cfg->cfg.host_caps &= ~MMC_MODE_4BIT;
+ if (priv->bus_width > 0) {
+ if (priv->bus_width < 8)
+ priv->cfg.host_caps &= ~MMC_MODE_8BIT;
+ if (priv->bus_width < 4)
+ priv->cfg.host_caps &= ~MMC_MODE_4BIT;
}
if (caps & ESDHC_HOSTCAPBLT_HSS)
- cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
+ priv->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
#ifdef CONFIG_ESDHC_DETECT_8_BIT_QUIRK
if (CONFIG_ESDHC_DETECT_8_BIT_QUIRK)
- cfg->cfg.host_caps &= ~MMC_MODE_8BIT;
+ priv->cfg.host_caps &= ~MMC_MODE_8BIT;
#endif
- cfg->cfg.f_min = 400000;
- cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000);
+ priv->cfg.f_min = 400000;
+ priv->cfg.f_max = min(priv->sdhc_clk, (u32)52000000);
- cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+ priv->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
- mmc = mmc_create(&cfg->cfg, cfg);
+ mmc = mmc_create(&priv->cfg, priv);
if (mmc == NULL)
return -1;
+ priv->mmc = mmc;
+
+ return 0;
+}
+
+int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg)
+{
+ struct fsl_esdhc_priv *priv;
+ int ret;
+
+ if (!cfg)
+ return -EINVAL;
+
+ priv = calloc(sizeof(struct fsl_esdhc_priv), 1);
+ if (!priv)
+ return -ENOMEM;
+
+ ret = fsl_esdhc_cfg_to_priv(cfg, priv);
+ if (ret) {
+ debug("%s xlate failure\n", __func__);
+ free(priv);
+ return ret;
+ }
+
+ ret = fsl_esdhc_init(priv);
+ if (ret) {
+ debug("%s init failure\n", __func__);
+ free(priv);
+ return ret;
+ }
+
return 0;
}
switch (card_id) {
case QIXIS_ESDHC_ADAPTER_TYPE_EMMC45:
+ value = QIXIS_READ(brdcfg[5]);
+ value |= (QIXIS_DAT4 | QIXIS_DAT5_6_7);
+ QIXIS_WRITE(brdcfg[5], value);
break;
case QIXIS_ESDHC_ADAPTER_TYPE_SDMMC_LEGACY:
+ value = QIXIS_READ(pwr_ctl[1]);
+ value |= QIXIS_EVDD_BY_SDHC_VS;
+ QIXIS_WRITE(pwr_ctl[1], value);
break;
case QIXIS_ESDHC_ADAPTER_TYPE_EMMC44:
value = QIXIS_READ(brdcfg[5]);
4 + 1, 1);
}
#endif
+
+#ifdef CONFIG_DM_MMC
+#include <asm/arch/clock.h>
+static int fsl_esdhc_probe(struct udevice *dev)
+{
+ struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+ struct fsl_esdhc_priv *priv = dev_get_priv(dev);
+ const void *fdt = gd->fdt_blob;
+ int node = dev->of_offset;
+ fdt_addr_t addr;
+ unsigned int val;
+ int ret;
+
+ addr = dev_get_addr(dev);
+ if (addr == FDT_ADDR_T_NONE)
+ return -EINVAL;
+
+ priv->esdhc_regs = (struct fsl_esdhc *)addr;
+ priv->dev = dev;
+
+ val = fdtdec_get_int(fdt, node, "bus-width", -1);
+ if (val == 8)
+ priv->bus_width = 8;
+ else if (val == 4)
+ priv->bus_width = 4;
+ else
+ priv->bus_width = 1;
+
+ if (fdt_get_property(fdt, node, "non-removable", NULL)) {
+ priv->non_removable = 1;
+ } else {
+ priv->non_removable = 0;
+ gpio_request_by_name_nodev(fdt, node, "cd-gpios", 0,
+ &priv->cd_gpio, GPIOD_IS_IN);
+ }
+
+ priv->wp_enable = 1;
+
+ ret = gpio_request_by_name_nodev(fdt, node, "wp-gpios", 0,
+ &priv->wp_gpio, GPIOD_IS_IN);
+ if (ret)
+ priv->wp_enable = 0;
+
+ /*
+ * TODO:
+ * Because lack of clk driver, if SDHC clk is not enabled,
+ * need to enable it first before this driver is invoked.
+ *
+ * we use MXC_ESDHC_CLK to get clk freq.
+ * If one would like to make this function work,
+ * the aliases should be provided in dts as this:
+ *
+ * aliases {
+ * mmc0 = &usdhc1;
+ * mmc1 = &usdhc2;
+ * mmc2 = &usdhc3;
+ * mmc3 = &usdhc4;
+ * };
+ * Then if your board only supports mmc2 and mmc3, but we can
+ * correctly get the seq as 2 and 3, then let mxc_get_clock
+ * work as expected.
+ */
+ 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);
+ if (ret) {
+ dev_err(dev, "fsl_esdhc_init failure\n");
+ return ret;
+ }
+
+ upriv->mmc = priv->mmc;
+
+ return 0;
+}
+
+static const struct udevice_id fsl_esdhc_ids[] = {
+ { .compatible = "fsl,imx6ul-usdhc", },
+ { .compatible = "fsl,imx6sx-usdhc", },
+ { .compatible = "fsl,imx6sl-usdhc", },
+ { .compatible = "fsl,imx6q-usdhc", },
+ { .compatible = "fsl,imx7d-usdhc", },
+ { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(fsl_esdhc) = {
+ .name = "fsl-esdhc-mmc",
+ .id = UCLASS_MMC,
+ .of_match = fsl_esdhc_ids,
+ .probe = fsl_esdhc_probe,
+ .priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv),
+};
+#endif