mmc: fsl_esdhc: workaround for hardware 3.3v IO reliability issue
[oweals/u-boot.git] / drivers / mmc / sh_mmcif.c
index 567e2cb61d32ba8b63f574a2ee14c4dff72b989c..2e994d0178d9ff8186294134c38cfbf221e95818 100644 (file)
@@ -1,30 +1,30 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * MMCIF driver.
  *
  * Copyright (C)  2011 Renesas Solutions Corp.
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License.
  */
 
 #include <config.h>
 #include <common.h>
+#include <log.h>
 #include <watchdog.h>
 #include <command.h>
 #include <mmc.h>
+#include <clk.h>
+#include <dm.h>
 #include <malloc.h>
-#include <asm/errno.h>
-#include <asm/io.h>
+#include <dm/device_compat.h>
+#include <linux/bitops.h>
+#include <linux/delay.h>
+#include <linux/errno.h>
+#include <linux/compat.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
 #include "sh_mmcif.h"
 
 #define DRIVER_NAME    "sh_mmcif"
 
-static void *mmc_priv(struct mmc *mmc)
-{
-       return (void *)mmc->priv;
-}
-
 static int sh_mmcif_intr(void *dev_id)
 {
        struct sh_mmcif_host *host = dev_id;
@@ -108,20 +108,18 @@ static int mmcif_wait_interrupt_flag(struct sh_mmcif_host *host)
 
 static void sh_mmcif_clock_control(struct sh_mmcif_host *host, unsigned int clk)
 {
-       int i;
-
        sh_mmcif_bitclr(CLK_ENABLE, &host->regs->ce_clk_ctrl);
        sh_mmcif_bitclr(CLK_CLEAR, &host->regs->ce_clk_ctrl);
 
        if (!clk)
                return;
-       if (clk == CLKDEV_EMMC_DATA) {
+
+       if (clk == CLKDEV_EMMC_DATA)
                sh_mmcif_bitset(CLK_PCLK, &host->regs->ce_clk_ctrl);
-       } else {
-               for (i = 1; (unsigned int)host->clk / (1 << i) >= clk; i++)
-                       ;
-               sh_mmcif_bitset((i - 1) << 16, &host->regs->ce_clk_ctrl);
-       }
+       else
+               sh_mmcif_bitset((fls(DIV_ROUND_UP(host->clk,
+                                                 clk) - 1) - 1) << 16,
+                               &host->regs->ce_clk_ctrl);
        sh_mmcif_bitset(CLK_ENABLE, &host->regs->ce_clk_ctrl);
 }
 
@@ -177,7 +175,7 @@ static int sh_mmcif_error_manage(struct sh_mmcif_host *host)
        if (state2 & STS2_CRC_ERR)
                ret = -EILSEQ;
        else if (state2 & STS2_TIMEOUT_ERR)
-               ret = TIMEOUT;
+               ret = -ETIMEDOUT;
        else
                ret = -EILSEQ;
        return ret;
@@ -492,7 +490,7 @@ static int sh_mmcif_start_cmd(struct sh_mmcif_host *host,
                case MMC_CMD_ALL_SEND_CID:
                case MMC_CMD_SELECT_CARD:
                case MMC_CMD_APP_CMD:
-                       ret = TIMEOUT;
+                       ret = -ETIMEDOUT;
                        break;
                default:
                        printf(DRIVER_NAME": Cmd(d'%d) err\n", cmd->cmdidx);
@@ -519,24 +517,23 @@ static int sh_mmcif_start_cmd(struct sh_mmcif_host *host,
        return ret;
 }
 
-static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd,
-                           struct mmc_data *data)
+static int sh_mmcif_send_cmd_common(struct sh_mmcif_host *host,
+                                   struct mmc_cmd *cmd, struct mmc_data *data)
 {
-       struct sh_mmcif_host *host = mmc_priv(mmc);
        int ret;
 
        WATCHDOG_RESET();
 
        switch (cmd->cmdidx) {
        case MMC_CMD_APP_CMD:
-               return TIMEOUT;
+               return -ETIMEDOUT;
        case MMC_CMD_SEND_EXT_CSD: /* = SD_SEND_IF_COND (8) */
                if (data)
                        /* ext_csd */
                        break;
                else
                        /* send_if_cond cmd (not support) */
-                       return TIMEOUT;
+                       return -ETIMEDOUT;
        default:
                break;
        }
@@ -548,10 +545,8 @@ static int sh_mmcif_request(struct mmc *mmc, struct mmc_cmd *cmd,
        return ret;
 }
 
-static void sh_mmcif_set_ios(struct mmc *mmc)
+static int sh_mmcif_set_ios_common(struct sh_mmcif_host *host, struct mmc *mmc)
 {
-       struct sh_mmcif_host *host = mmc_priv(mmc);
-
        if (mmc->clock)
                sh_mmcif_clock_control(host, mmc->clock);
 
@@ -563,46 +558,193 @@ static void sh_mmcif_set_ios(struct mmc *mmc)
                host->bus_width = MMC_BUS_WIDTH_1;
 
        debug("clock = %d, buswidth = %d\n", mmc->clock, mmc->bus_width);
+
+       return 0;
 }
 
-static int sh_mmcif_init(struct mmc *mmc)
+static int sh_mmcif_initialize_common(struct sh_mmcif_host *host)
 {
-       struct sh_mmcif_host *host = mmc_priv(mmc);
-
        sh_mmcif_sync_reset(host);
        sh_mmcif_write(MASK_ALL, &host->regs->ce_int_mask);
        return 0;
 }
 
+#ifndef CONFIG_DM_MMC
+static void *mmc_priv(struct mmc *mmc)
+{
+       return (void *)mmc->priv;
+}
+
+static int sh_mmcif_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
+                           struct mmc_data *data)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+
+       return sh_mmcif_send_cmd_common(host, cmd, data);
+}
+
+static int sh_mmcif_set_ios(struct mmc *mmc)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+
+       return sh_mmcif_set_ios_common(host, mmc);
+}
+
+static int sh_mmcif_initialize(struct mmc *mmc)
+{
+       struct sh_mmcif_host *host = mmc_priv(mmc);
+
+       return sh_mmcif_initialize_common(host);
+}
+
+static const struct mmc_ops sh_mmcif_ops = {
+       .send_cmd       = sh_mmcif_send_cmd,
+       .set_ios        = sh_mmcif_set_ios,
+       .init           = sh_mmcif_initialize,
+};
+
+static struct mmc_config sh_mmcif_cfg = {
+       .name           = DRIVER_NAME,
+       .ops            = &sh_mmcif_ops,
+       .host_caps      = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT |
+                         MMC_MODE_8BIT,
+       .voltages       = MMC_VDD_32_33 | MMC_VDD_33_34,
+       .b_max          = CONFIG_SYS_MMC_MAX_BLK_COUNT,
+};
+
 int mmcif_mmc_init(void)
 {
-       int ret = 0;
        struct mmc *mmc;
        struct sh_mmcif_host *host = NULL;
 
-       mmc = malloc(sizeof(struct mmc));
-       if (!mmc)
-               ret = -ENOMEM;
-       memset(mmc, 0, sizeof(*mmc));
        host = malloc(sizeof(struct sh_mmcif_host));
        if (!host)
-               ret = -ENOMEM;
+               return -ENOMEM;
        memset(host, 0, sizeof(*host));
 
-       mmc->f_min = CLKDEV_MMC_INIT;
-       mmc->f_max = CLKDEV_EMMC_DATA;
-       mmc->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
-       mmc->host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz | MMC_MODE_4BIT |
-                        MMC_MODE_8BIT;
-       memcpy(mmc->name, DRIVER_NAME, sizeof(DRIVER_NAME));
-       mmc->send_cmd = sh_mmcif_request;
-       mmc->set_ios = sh_mmcif_set_ios;
-       mmc->init = sh_mmcif_init;
        host->regs = (struct sh_mmcif_regs *)CONFIG_SH_MMCIF_ADDR;
        host->clk = CONFIG_SH_MMCIF_CLK;
-       mmc->priv = host;
 
-       mmc_register(mmc);
+       sh_mmcif_cfg.f_min = MMC_CLK_DIV_MIN(host->clk);
+       sh_mmcif_cfg.f_max = MMC_CLK_DIV_MAX(host->clk);
 
-       return ret;
+       mmc = mmc_create(&sh_mmcif_cfg, host);
+       if (mmc == NULL) {
+               free(host);
+               return -ENOMEM;
+       }
+
+       return 0;
+}
+
+#else
+struct sh_mmcif_plat {
+       struct mmc_config cfg;
+       struct mmc mmc;
+};
+
+int sh_mmcif_dm_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+                       struct mmc_data *data)
+{
+       struct sh_mmcif_host *host = dev_get_priv(dev);
+
+       return sh_mmcif_send_cmd_common(host, cmd, data);
+}
+
+int sh_mmcif_dm_set_ios(struct udevice *dev)
+{
+       struct sh_mmcif_host *host = dev_get_priv(dev);
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+
+       return sh_mmcif_set_ios_common(host, mmc);
+}
+
+static const struct dm_mmc_ops sh_mmcif_dm_ops = {
+       .send_cmd       = sh_mmcif_dm_send_cmd,
+       .set_ios        = sh_mmcif_dm_set_ios,
+};
+
+static int sh_mmcif_dm_bind(struct udevice *dev)
+{
+       struct sh_mmcif_plat *plat = dev_get_platdata(dev);
+
+       return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
+
+static int sh_mmcif_dm_probe(struct udevice *dev)
+{
+       struct sh_mmcif_plat *plat = dev_get_platdata(dev);
+       struct sh_mmcif_host *host = dev_get_priv(dev);
+       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       struct clk sh_mmcif_clk;
+       fdt_addr_t base;
+       int ret;
+
+       base = devfdt_get_addr(dev);
+       if (base == FDT_ADDR_T_NONE)
+               return -EINVAL;
+
+       host->regs = (struct sh_mmcif_regs *)devm_ioremap(dev, base, SZ_2K);
+       if (!host->regs)
+               return -ENOMEM;
+
+       ret = clk_get_by_index(dev, 0, &sh_mmcif_clk);
+       if (ret) {
+               debug("failed to get clock, ret=%d\n", ret);
+               return ret;
+       }
+
+       ret = clk_enable(&sh_mmcif_clk);
+       if (ret) {
+               debug("failed to enable clock, ret=%d\n", ret);
+               return ret;
+       }
+
+       host->clk = clk_set_rate(&sh_mmcif_clk, 97500000);
+
+       plat->cfg.name = dev->name;
+       plat->cfg.host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+       switch (fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev), "bus-width",
+                              1)) {
+       case 8:
+               plat->cfg.host_caps |= MMC_MODE_8BIT;
+               break;
+       case 4:
+               plat->cfg.host_caps |= MMC_MODE_4BIT;
+               break;
+       case 1:
+               break;
+       default:
+               dev_err(dev, "Invalid \"bus-width\" value\n");
+               return -EINVAL;
+       }
+
+       sh_mmcif_initialize_common(host);
+
+       plat->cfg.voltages = MMC_VDD_165_195 | MMC_VDD_32_33 | MMC_VDD_33_34;
+       plat->cfg.f_min = MMC_CLK_DIV_MIN(host->clk);
+       plat->cfg.f_max = MMC_CLK_DIV_MAX(host->clk);
+       plat->cfg.b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+
+       upriv->mmc = &plat->mmc;
+
+       return 0;
 }
+
+static const struct udevice_id sh_mmcif_sd_match[] = {
+       { .compatible = "renesas,sh-mmcif" },
+       { /* sentinel */ }
+};
+
+U_BOOT_DRIVER(sh_mmcif_mmc) = {
+       .name                   = "sh-mmcif",
+       .id                     = UCLASS_MMC,
+       .of_match               = sh_mmcif_sd_match,
+       .bind                   = sh_mmcif_dm_bind,
+       .probe                  = sh_mmcif_dm_probe,
+       .priv_auto_alloc_size   = sizeof(struct sh_mmcif_host),
+       .platdata_auto_alloc_size = sizeof(struct sh_mmcif_plat),
+       .ops                    = &sh_mmcif_dm_ops,
+};
+#endif