X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=drivers%2Fmmc%2Ffsl_esdhc.c;h=b49a269e4bf23a2faebd63544afa0e11ae299523;hb=5248930ebf48448b76a59069a5a96037264c3d6e;hp=1ccc576c347c6f0f669ab35e247641df252ba1eb;hpb=3861d784b1a470570a0098ac5c25d79e3073c0d9;p=u-boot diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 1ccc576c34..b49a269e4b 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -12,14 +12,17 @@ #include #include #include +#include #include #include #include +#include #include -#include #include #include #include +#include +#include DECLARE_GLOBAL_DATA_PTR; @@ -54,24 +57,68 @@ struct fsl_esdhc { 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_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +/** + * 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 + * @vs18_enable: 1: use 1.8V voltage; 0: use 3.3V + * @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; +#if !CONFIG_IS_ENABLED(BLK) + struct mmc *mmc; +#endif + struct udevice *dev; + int non_removable; + int wp_enable; + int vs18_enable; +#ifdef CONFIG_DM_GPIO + struct gpio_desc cd_gpio; + struct gpio_desc wp_gpio; +#endif +}; + /* Return the XFERTYP flags for a given command and data packet */ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) { @@ -105,12 +152,9 @@ 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_FSL_LAYERSCAPE) || \ - defined(CONFIG_PPC_T4160) if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) xfertyp |= XFERTYP_CMDTYP_ABORT; -#endif + return XFERTYP_CMD(cmd->cmdidx) | xfertyp; } @@ -118,11 +162,10 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) /* * PIO Read/Write Mode reduce the performace as DMA is not used in this mode. */ -static void -esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) +static void esdhc_pio_read_write(struct fsl_esdhc_priv *priv, + struct mmc_data *data) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc *regs = priv->esdhc_regs; uint blocks; char *buffer; uint databuf; @@ -180,12 +223,12 @@ esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) } #endif -static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) +static int esdhc_setup_data(struct fsl_esdhc_priv *priv, 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_FSL_LAYERSCAPE + struct fsl_esdhc *regs = priv->esdhc_regs; +#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) dma_addr_t addr; #endif uint wml_value; @@ -198,7 +241,7 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) esdhc_clrsetbits32(®s->wml, WML_RD_WML_MASK, wml_value); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO -#ifdef CONFIG_FSL_LAYERSCAPE +#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"); @@ -216,15 +259,18 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) #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 -ETIMEDOUT; + } } esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, wml_value << 16); #ifndef CONFIG_SYS_FSL_ESDHC_USE_PIO -#ifdef CONFIG_FSL_LAYERSCAPE +#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"); @@ -252,8 +298,15 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) * 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) @@ -278,15 +331,11 @@ static int esdhc_setup_data(struct mmc *mmc, struct mmc_data *data) static void check_and_invalidate_dcache_range (struct mmc_cmd *cmd, struct mmc_data *data) { -#ifdef CONFIG_FSL_LAYERSCAPE 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_FSL_LAYERSCAPE +#if defined(CONFIG_FSL_LAYERSCAPE) || defined(CONFIG_S32V234) dma_addr_t addr; addr = virt_to_phys((void *)(data->dest)); @@ -294,7 +343,10 @@ static void check_and_invalidate_dcache_range 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); } @@ -302,14 +354,13 @@ static void check_and_invalidate_dcache_range * Sends a command out on the bus. Takes the mmc pointer, * a command pointer, and an optional data pointer. */ -static int -esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) +static int esdhc_send_cmd_common(struct fsl_esdhc_priv *priv, struct mmc *mmc, + struct mmc_cmd *cmd, struct mmc_data *data) { 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 *regs = priv->esdhc_regs; #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) @@ -337,7 +388,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Set up for a data transfer if we have one */ if (data) { - err = esdhc_setup_data(mmc, data); + err = esdhc_setup_data(priv, mmc, data); if(err) return err; @@ -369,12 +420,12 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) irqstat = esdhc_read32(®s->irqstat); if (irqstat & CMD_ERR) { - err = COMM_ERR; + err = -ECOMM; goto out; } if (irqstat & IRQSTAT_CTOE) { - err = TIMEOUT; + err = -ETIMEDOUT; goto out; } @@ -400,7 +451,7 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) if (timeout <= 0) { printf("Timeout waiting for DAT0 to go high!\n"); - err = TIMEOUT; + err = -ETIMEDOUT; goto out; } } @@ -423,18 +474,18 @@ esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) /* Wait until all of the blocks are transferred */ if (data) { #ifdef CONFIG_SYS_FSL_ESDHC_USE_PIO - esdhc_pio_read_write(mmc, data); + esdhc_pio_read_write(priv, data); #else do { irqstat = esdhc_read32(®s->irqstat); if (irqstat & IRQSTAT_DTOE) { - err = TIMEOUT; + err = -ETIMEDOUT; goto out; } if (irqstat & DATA_ERR) { - err = COMM_ERR; + err = -ECOMM; goto out; } } while ((irqstat & DATA_COMPLETE) != DATA_COMPLETE); @@ -475,35 +526,35 @@ out: return err; } -static void set_sysctl(struct mmc *mmc, uint clock) +static void set_sysctl(struct fsl_esdhc_priv *priv, 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; + int div = 1; +#ifdef ARCH_MXC + int pre_div = 1; +#else + int pre_div = 2; +#endif + int ddr_pre_div = mmc->ddr_mode ? 2 : 1; + struct fsl_esdhc *regs = priv->esdhc_regs; + int sdhc_clk = priv->sdhc_clk; uint clk; if (clock < mmc->cfg->f_min) clock = mmc->cfg->f_min; - if (sdhc_clk / 16 > clock) { - for (pre_div = 2; pre_div < 256; pre_div *= 2) - if ((sdhc_clk / pre_div) <= (clock * 16)) - break; - } else - pre_div = 2; + while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256) + pre_div *= 2; - for (div = 1; div <= 16; div++) - if ((sdhc_clk / (div * pre_div)) <= clock) - break; + while (sdhc_clk / (div * pre_div * ddr_pre_div) > clock && div < 16) + div++; - pre_div >>= mmc->ddr_mode ? 2 : 1; + pre_div >>= 1; div -= 1; clk = (pre_div << 8) | (div << 4); #ifdef CONFIG_FSL_USDHC - esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); + esdhc_clrbits32(®s->vendorspec, VENDORSPEC_CKEN); #else esdhc_clrbits32(®s->sysctl, SYSCTL_CKEN); #endif @@ -513,7 +564,7 @@ static void set_sysctl(struct mmc *mmc, uint clock) udelay(10000); #ifdef CONFIG_FSL_USDHC - esdhc_clrbits32(®s->sysctl, SYSCTL_RSTA); + esdhc_setbits32(®s->vendorspec, VENDORSPEC_PEREN | VENDORSPEC_CKEN); #else esdhc_setbits32(®s->sysctl, SYSCTL_PEREN | SYSCTL_CKEN); #endif @@ -521,10 +572,9 @@ static void set_sysctl(struct mmc *mmc, uint clock) } #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK -static void esdhc_clock_control(struct mmc *mmc, bool enable) +static void esdhc_clock_control(struct fsl_esdhc_priv *priv, bool enable) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc *regs = priv->esdhc_regs; u32 value; u32 time_out; @@ -550,19 +600,18 @@ static void esdhc_clock_control(struct mmc *mmc, bool enable) } #endif -static void esdhc_set_ios(struct mmc *mmc) +static int esdhc_set_ios_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK /* Select to use peripheral clock */ - esdhc_clock_control(mmc, false); + esdhc_clock_control(priv, false); esdhc_setbits32(®s->scr, ESDHCCTL_PCS); - esdhc_clock_control(mmc, true); + esdhc_clock_control(priv, true); #endif /* Set the clock speed */ - set_sysctl(mmc, mmc->clock); + set_sysctl(priv, mmc, mmc->clock); /* Set the bus width */ esdhc_clrbits32(®s->proctl, PROCTL_DTW_4 | PROCTL_DTW_8); @@ -572,20 +621,37 @@ static void esdhc_set_ios(struct mmc *mmc) else if (mmc->bus_width == 8) esdhc_setbits32(®s->proctl, PROCTL_DTW_8); + return 0; } -static int esdhc_init(struct mmc *mmc) +static int esdhc_init_common(struct fsl_esdhc_priv *priv, struct mmc *mmc) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; - int timeout = 1000; + struct fsl_esdhc *regs = priv->esdhc_regs; + ulong start; /* Reset the entire host controller */ esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); /* Wait until the controller is available */ - while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) - udelay(1000); + start = get_timer(0); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + if (get_timer(start) > 1000) + return -ETIMEDOUT; + } + +#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 */ @@ -594,6 +660,8 @@ static int esdhc_init(struct mmc *mmc) #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 */ @@ -608,71 +676,128 @@ static int esdhc_init(struct mmc *mmc) /* Set timout to the maximum value */ esdhc_clrsetbits32(®s->sysctl, SYSCTL_TIMEOUT_MASK, 14 << 16); -#ifdef CONFIG_SYS_FSL_ESDHC_FORCE_VSELECT - esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); -#endif + if (priv->vs18_enable) + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); return 0; } -static int esdhc_getcd(struct mmc *mmc) +static int esdhc_getcd_common(struct fsl_esdhc_priv *priv) { - struct fsl_esdhc_cfg *cfg = mmc->priv; - struct fsl_esdhc *regs = (struct fsl_esdhc *)cfg->esdhc_base; + struct fsl_esdhc *regs = priv->esdhc_regs; int timeout = 1000; #ifdef CONFIG_ESDHC_DETECT_QUIRK if (CONFIG_ESDHC_DETECT_QUIRK) return 1; #endif + +#if CONFIG_IS_ENABLED(DM_MMC) + if (priv->non_removable) + return 1; +#ifdef CONFIG_DM_GPIO + if (dm_gpio_is_valid(&priv->cd_gpio)) + return dm_gpio_get_value(&priv->cd_gpio); +#endif +#endif + while (!(esdhc_read32(®s->prsstat) & PRSSTAT_CINS) && --timeout) udelay(1000); return timeout > 0; } -static void esdhc_reset(struct fsl_esdhc *regs) +static int esdhc_reset(struct fsl_esdhc *regs) { - unsigned long timeout = 100; /* wait max 100 ms */ + ulong start; /* reset the controller */ esdhc_setbits32(®s->sysctl, SYSCTL_RSTA); /* hardware clears the bit when it is done */ - while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA) && --timeout) - udelay(1000); - if (!timeout) - printf("MMC/SD: Reset never completed.\n"); + start = get_timer(0); + while ((esdhc_read32(®s->sysctl) & SYSCTL_RSTA)) { + if (get_timer(start) > 100) { + printf("MMC/SD: Reset never completed.\n"); + return -ETIMEDOUT; + } + } + + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_MMC_OPS) +static int esdhc_getcd(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_getcd_common(priv); +} + +static int esdhc_init(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_init_common(priv, mmc); +} + +static int esdhc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_send_cmd_common(priv, mmc, cmd, data); +} + +static int esdhc_set_ios(struct mmc *mmc) +{ + struct fsl_esdhc_priv *priv = mmc->priv; + + return esdhc_set_ios_common(priv, mmc); } static const struct mmc_ops esdhc_ops = { + .getcd = esdhc_getcd, + .init = esdhc_init, .send_cmd = esdhc_send_cmd, .set_ios = esdhc_set_ios, - .init = esdhc_init, - .getcd = esdhc_getcd, }; +#endif -int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +static int fsl_esdhc_init(struct fsl_esdhc_priv *priv, + struct fsl_esdhc_plat *plat) { + struct mmc_config *cfg; struct fsl_esdhc *regs; - struct mmc *mmc; u32 caps, voltage_caps; + int ret; - 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); + ret = esdhc_reset(regs); + if (ret) + return ret; #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 + if (priv->vs18_enable) + esdhc_setbits32(®s->vendorspec, ESDHC_VENDORSPEC_VSELECT); + writel(SDHCI_IRQ_EN_BITS, ®s->irqstaten); - memset(&cfg->cfg, 0, sizeof(cfg->cfg)); + cfg = &plat->cfg; +#ifndef CONFIG_DM_MMC + memset(cfg, '\0', sizeof(*cfg)); +#endif voltage_caps = 0; caps = esdhc_read32(®s->hostcapblt); @@ -694,46 +819,109 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) 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; + cfg->name = "FSL_SDHC"; +#if !CONFIG_IS_ENABLED(DM_MMC_OPS) + cfg->ops = &esdhc_ops; +#endif #ifdef CONFIG_SYS_SD_VOLTAGE - cfg->cfg.voltages = CONFIG_SYS_SD_VOLTAGE; + cfg->voltages = CONFIG_SYS_SD_VOLTAGE; #else - cfg->cfg.voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; #endif - if ((cfg->cfg.voltages & voltage_caps) == 0) { + if ((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) + cfg->host_caps = MMC_MODE_4BIT | MMC_MODE_8BIT; + else if (priv->bus_width == 4) + cfg->host_caps = MMC_MODE_4BIT; + + 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; + 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) + cfg->host_caps &= ~MMC_MODE_8BIT; + if (priv->bus_width < 4) + cfg->host_caps &= ~MMC_MODE_4BIT; } if (caps & ESDHC_HOSTCAPBLT_HSS) - cfg->cfg.host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS; + 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; + cfg->host_caps &= ~MMC_MODE_8BIT; #endif - cfg->cfg.f_min = 400000; - cfg->cfg.f_max = min(cfg->sdhc_clk, (u32)52000000); + cfg->f_min = 400000; + cfg->f_max = min(priv->sdhc_clk, (u32)52000000); - cfg->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; - mmc = mmc_create(&cfg->cfg, cfg); - if (mmc == NULL) - return -1; + return 0; +} + +#if !CONFIG_IS_ENABLED(DM_MMC) +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; + priv->vs18_enable = cfg->vs18_enable; + + return 0; +}; + +int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) +{ + struct fsl_esdhc_plat *plat; + struct fsl_esdhc_priv *priv; + struct mmc *mmc; + int ret; + + if (!cfg) + return -EINVAL; + + priv = calloc(sizeof(struct fsl_esdhc_priv), 1); + if (!priv) + return -ENOMEM; + plat = calloc(sizeof(struct fsl_esdhc_plat), 1); + if (!plat) { + free(priv); + return -ENOMEM; + } + + ret = fsl_esdhc_cfg_to_priv(cfg, priv); + if (ret) { + debug("%s xlate failure\n", __func__); + free(plat); + free(priv); + return ret; + } + + ret = fsl_esdhc_init(priv, plat); + if (ret) { + debug("%s init failure\n", __func__); + free(plat); + free(priv); + return ret; + } + + mmc = mmc_create(&plat->cfg, priv); + if (!mmc) + return -EIO; + + priv->mmc = mmc; return 0; } @@ -747,6 +935,7 @@ int fsl_esdhc_mmc_init(bd_t *bis) cfg->sdhc_clk = gd->arch.sdhc_clk; return fsl_esdhc_initialize(bis, cfg); } +#endif #ifdef CONFIG_FSL_ESDHC_ADAPTER_IDENT void mmc_adapter_card_type_ident(void) @@ -788,17 +977,24 @@ void mmc_adapter_card_type_ident(void) #endif #ifdef CONFIG_OF_LIBFDT -void fdt_fixup_esdhc(void *blob, bd_t *bd) +__weak int esdhc_status_fixup(void *blob, const char *compat) { - const char *compat = "fsl,esdhc"; - #ifdef CONFIG_FSL_ESDHC_PIN_MUX if (!hwconfig("esdhc")) { do_fixup_by_compat(blob, compat, "status", "disabled", - 8 + 1, 1); - return; + sizeof("disabled"), 1); + return 1; } #endif + return 0; +} + +void fdt_fixup_esdhc(void *blob, bd_t *bd) +{ + const char *compat = "fsl,esdhc"; + + if (esdhc_status_fixup(blob, compat)) + return; #ifdef CONFIG_FSL_ESDHC_USE_PERIPHERAL_CLK do_fixup_by_compat_u32(blob, compat, "peripheral-frequency", @@ -811,7 +1007,191 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) do_fixup_by_compat_u32(blob, compat, "adapter-type", (u32)(gd->arch.sdhc_adapter), 1); #endif - do_fixup_by_compat(blob, compat, "status", "okay", - 4 + 1, 1); } #endif + +#if CONFIG_IS_ENABLED(DM_MMC) +#include +__weak void init_clk_usdhc(u32 index) +{ +} + +static int fsl_esdhc_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); +#ifdef CONFIG_DM_REGULATOR + struct udevice *vqmmc_dev; +#endif + fdt_addr_t addr; + unsigned int val; + struct mmc *mmc; + int ret; + + addr = dev_read_addr(dev); + if (addr == FDT_ADDR_T_NONE) + return -EINVAL; + + priv->esdhc_regs = (struct fsl_esdhc *)addr; + priv->dev = dev; + + val = dev_read_u32_default(dev, "bus-width", -1); + if (val == 8) + priv->bus_width = 8; + else if (val == 4) + priv->bus_width = 4; + else + priv->bus_width = 1; + + if (dev_read_bool(dev, "non-removable")) { + priv->non_removable = 1; + } else { + priv->non_removable = 0; +#ifdef CONFIG_DM_GPIO + gpio_request_by_name(dev, "cd-gpios", 0, &priv->cd_gpio, + GPIOD_IS_IN); +#endif + } + + priv->wp_enable = 1; + +#ifdef CONFIG_DM_GPIO + ret = gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, + GPIOD_IS_IN); + if (ret) + priv->wp_enable = 0; +#endif + + priv->vs18_enable = 0; + +#ifdef CONFIG_DM_REGULATOR + /* + * If emmc I/O has a fixed voltage at 1.8V, this must be provided, + * otherwise, emmc will work abnormally. + */ + ret = device_get_supply_regulator(dev, "vqmmc-supply", &vqmmc_dev); + if (ret) { + dev_dbg(dev, "no vqmmc-supply\n"); + } else { + ret = regulator_set_enable(vqmmc_dev, true); + if (ret) { + dev_err(dev, "fail to enable vqmmc-supply\n"); + return ret; + } + + if (regulator_get_value(vqmmc_dev) == 1800000) + priv->vs18_enable = 1; + } +#endif + + /* + * 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. + */ + + 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; + } + + ret = fsl_esdhc_init(priv, plat); + if (ret) { + dev_err(dev, "fsl_esdhc_init failure\n"); + return ret; + } + + mmc = &plat->mmc; + mmc->cfg = &plat->cfg; + mmc->dev = dev; + upriv->mmc = mmc; + + return esdhc_init_common(priv, mmc); +} + +#if CONFIG_IS_ENABLED(DM_MMC_OPS) +static int fsl_esdhc_get_cd(struct udevice *dev) +{ + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return true; + return esdhc_getcd_common(priv); +} + +static int fsl_esdhc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_send_cmd_common(priv, &plat->mmc, cmd, data); +} + +static int fsl_esdhc_set_ios(struct udevice *dev) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + struct fsl_esdhc_priv *priv = dev_get_priv(dev); + + return esdhc_set_ios_common(priv, &plat->mmc); +} + +static const struct dm_mmc_ops fsl_esdhc_ops = { + .get_cd = fsl_esdhc_get_cd, + .send_cmd = fsl_esdhc_send_cmd, + .set_ios = fsl_esdhc_set_ios, +}; +#endif + +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", }, + { .compatible = "fsl,imx7ulp-usdhc", }, + { .compatible = "fsl,esdhc", }, + { /* sentinel */ } +}; + +#if CONFIG_IS_ENABLED(BLK) +static int fsl_esdhc_bind(struct udevice *dev) +{ + struct fsl_esdhc_plat *plat = dev_get_platdata(dev); + + return mmc_bind(dev, &plat->mmc, &plat->cfg); +} +#endif + +U_BOOT_DRIVER(fsl_esdhc) = { + .name = "fsl-esdhc-mmc", + .id = UCLASS_MMC, + .of_match = fsl_esdhc_ids, +#if CONFIG_IS_ENABLED(DM_MMC_OPS) + .ops = &fsl_esdhc_ops, +#endif +#if CONFIG_IS_ENABLED(BLK) + .bind = fsl_esdhc_bind, +#endif + .probe = fsl_esdhc_probe, + .platdata_auto_alloc_size = sizeof(struct fsl_esdhc_plat), + .priv_auto_alloc_size = sizeof(struct fsl_esdhc_priv), +}; +#endif