diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index ea5f4bf6c0..3acf9e8820 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -20,6 +20,8 @@ #include #include #include +#include +#include DECLARE_GLOBAL_DATA_PTR; @@ -72,6 +74,30 @@ struct fsl_esdhc { 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 + * @cd_gpio: gpio for card detection + */ +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; + struct gpio_desc cd_gpio; +}; + /* Return the XFERTYP flags for a given command and data packet */ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) { @@ -118,8 +144,8 @@ static uint esdhc_xfertyp(struct mmc_cmd *cmd, struct mmc_data *data) 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; @@ -180,8 +206,8 @@ esdhc_pio_read_write(struct mmc *mmc, struct mmc_data *data) 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; + struct fsl_esdhc_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_FSL_LAYERSCAPE dma_addr_t addr; #endif @@ -312,8 +338,8 @@ esdhc_send_cmd(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_priv *priv = mmc->priv; + struct fsl_esdhc *regs = priv->esdhc_regs; #ifdef CONFIG_SYS_FSL_ERRATUM_ESDHC111 if (cmd->cmdidx == MMC_CMD_STOP_TRANSMISSION) @@ -482,9 +508,9 @@ out: 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) @@ -527,8 +553,8 @@ 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) { - 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; @@ -556,8 +582,8 @@ static void esdhc_clock_control(struct mmc *mmc, bool enable) 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 */ @@ -580,8 +606,8 @@ static void esdhc_set_ios(struct mmc *mmc) 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 */ @@ -621,14 +647,23 @@ static int esdhc_init(struct mmc *mmc) 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); @@ -656,16 +691,29 @@ static const struct mmc_ops esdhc_ops = { .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; + + 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); @@ -676,7 +724,7 @@ int fsl_esdhc_initialize(bd_t *bis, struct fsl_esdhc_cfg *cfg) #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); @@ -698,47 +746,83 @@ 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; + 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; } @@ -819,3 +903,92 @@ void fdt_fixup_esdhc(void *blob, bd_t *bd) 4 + 1, 1); } #endif + +#ifdef CONFIG_DM_MMC +#include +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); + } + + /* + * 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