]> git.sur5r.net Git - u-boot/blobdiff - drivers/mmc/ftsdc010_mci.c
dm: core: Add ofnode function to read a 64-bit int
[u-boot] / drivers / mmc / ftsdc010_mci.c
index a620678e5f9e24654bf9a2399fc2d8373145a419..9c15eb36d6462801456bcc00af60427970262079 100644 (file)
@@ -1,40 +1,70 @@
+// SPDX-License-Identifier: GPL-2.0+
 /*
  * Faraday MMC/SD Host Controller
  *
  * (C) Copyright 2010 Faraday Technology
  * Dante Su <dantesu@faraday-tech.com>
  *
- * SPDX-License-Identifier:    GPL-2.0+
+ * Copyright 2018 Andes Technology, Inc.
+ * Author: Rick Chen (rick@andestech.com)
  */
 
 #include <common.h>
+#include <clk.h>
 #include <malloc.h>
 #include <part.h>
 #include <mmc.h>
-
-#include <asm/io.h>
-#include <asm/errno.h>
+#include <linux/io.h>
+#include <linux/errno.h>
 #include <asm/byteorder.h>
 #include <faraday/ftsdc010.h>
+#include "ftsdc010_mci.h"
+#include <dm.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <mapmem.h>
+#include <pwrseq.h>
+#include <syscon.h>
+#include <linux/err.h>
+
+DECLARE_GLOBAL_DATA_PTR;
 
 #define CFG_CMD_TIMEOUT (CONFIG_SYS_HZ >> 4) /* 250 ms */
 #define CFG_RST_TIMEOUT CONFIG_SYS_HZ /* 1 sec reset timeout */
 
-struct ftsdc010_chip {
-       void __iomem *regs;
-       uint32_t wprot;   /* write protected (locked) */
-       uint32_t rate;    /* actual SD clock in Hz */
-       uint32_t sclk;    /* FTSDC010 source clock in Hz */
-       uint32_t fifo;    /* fifo depth in bytes */
-       uint32_t acmd;
-       struct mmc_config cfg;  /* mmc configuration */
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+struct ftsdc010 {
+       fdt32_t         bus_width;
+       bool            cap_mmc_highspeed;
+       bool            cap_sd_highspeed;
+       fdt32_t         clock_freq_min_max[2];
+       struct phandle_2_cell   clocks[4];
+       fdt32_t         fifo_depth;
+       fdt32_t         reg[2];
+};
+#endif
+
+struct ftsdc010_plat {
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+       struct ftsdc010 dtplat;
+#endif
+       struct mmc_config cfg;
+       struct mmc mmc;
+};
+
+struct ftsdc_priv {
+       struct clk clk;
+       struct ftsdc010_chip chip;
+       int fifo_depth;
+       bool fifo_mode;
+       u32 minmax[2];
 };
 
 static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd)
 {
        struct ftsdc010_chip *chip = mmc->priv;
        struct ftsdc010_mmc __iomem *regs = chip->regs;
-       int ret = TIMEOUT;
+       int ret = -ETIMEDOUT;
        uint32_t ts, st;
        uint32_t cmd   = FTSDC010_CMD_IDX(mmc_cmd->cmdidx);
        uint32_t arg   = mmc_cmd->cmdarg;
@@ -126,10 +156,9 @@ static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate)
 
 static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
 {
-       int ret = TIMEOUT;
-       uint32_t st, ts;
-
-       for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) {
+       int ret = -ETIMEDOUT;
+       uint32_t st, timeout = 10000000;
+       while (timeout--) {
                st = readl(&regs->status);
                if (!(st & mask))
                        continue;
@@ -138,8 +167,9 @@ static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
                break;
        }
 
-       if (ret)
+       if (ret){
                debug("ftsdc010: wait st(0x%x) timeout\n", mask);
+       }
 
        return ret;
 }
@@ -147,11 +177,11 @@ static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask)
 /*
  * u-boot mmc api
  */
-
-static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd,
+static int ftsdc010_request(struct udevice *dev, struct mmc_cmd *cmd,
        struct mmc_data *data)
 {
-       int ret = UNUSABLE_ERR;
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+       int ret = -EOPNOTSUPP;
        uint32_t len = 0;
        struct ftsdc010_chip *chip = mmc->priv;
        struct ftsdc010_mmc __iomem *regs = chip->regs;
@@ -245,14 +275,15 @@ static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd,
 
        if (!ret) {
                ret = ftsdc010_wait(regs,
-                       FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_ERROR);
+                       FTSDC010_STATUS_DATA_END | FTSDC010_STATUS_DATA_CRC_OK);
        }
 
        return ret;
 }
 
-static void ftsdc010_set_ios(struct mmc *mmc)
+static int ftsdc010_set_ios(struct udevice *dev)
 {
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
        struct ftsdc010_chip *chip = mmc->priv;
        struct ftsdc010_mmc __iomem *regs = chip->regs;
 
@@ -270,22 +301,37 @@ static void ftsdc010_set_ios(struct mmc *mmc)
                setbits_le32(&regs->bwr, FTSDC010_BWR_MODE_1BIT);
                break;
        }
+
+       return 0;
 }
 
-static int ftsdc010_init(struct mmc *mmc)
+static int ftsdc010_get_cd(struct udevice *dev)
 {
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
        struct ftsdc010_chip *chip = mmc->priv;
        struct ftsdc010_mmc __iomem *regs = chip->regs;
-       uint32_t ts;
-
-       if (readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT)
-               return NO_CARD_ERR;
+       return !(readl(&regs->status) & FTSDC010_STATUS_CARD_DETECT);
+}
 
+static int ftsdc010_get_wp(struct udevice *dev)
+{
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+       struct ftsdc010_chip *chip = mmc->priv;
+       struct ftsdc010_mmc __iomem *regs = chip->regs;
        if (readl(&regs->status) & FTSDC010_STATUS_WRITE_PROT) {
                printf("ftsdc010: write protected\n");
                chip->wprot = 1;
        }
 
+       return 0;
+}
+
+static int ftsdc010_init(struct mmc *mmc)
+{
+       struct ftsdc010_chip *chip = mmc->priv;
+       struct ftsdc010_mmc __iomem *regs = chip->regs;
+       uint32_t ts;
+
        chip->fifo = (readl(&regs->feature) & 0xff) << 2;
 
        /* 1. chip reset */
@@ -297,7 +343,7 @@ static int ftsdc010_init(struct mmc *mmc)
        }
        if (readl(&regs->cmd) & FTSDC010_CMD_SDC_RST) {
                printf("ftsdc010: reset failed\n");
-               return UNUSABLE_ERR;
+               return -EOPNOTSUPP;
        }
 
        /* 2. enter low speed mode (400k card detection) */
@@ -309,65 +355,126 @@ static int ftsdc010_init(struct mmc *mmc)
        return 0;
 }
 
-static const struct mmc_ops ftsdc010_ops = {
+static int ftsdc010_probe(struct udevice *dev)
+{
+       struct mmc *mmc = mmc_get_mmc_dev(dev);
+       return ftsdc010_init(mmc);
+}
+
+const struct dm_mmc_ops dm_ftsdc010_mmc_ops = {
        .send_cmd       = ftsdc010_request,
        .set_ios        = ftsdc010_set_ios,
-       .init           = ftsdc010_init,
+       .get_cd         = ftsdc010_get_cd,
+       .get_wp         = ftsdc010_get_wp,
 };
 
-int ftsdc010_mmc_init(int devid)
+static void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth,
+                    uint caps, u32 max_clk, u32 min_clk)
 {
-       struct mmc *mmc;
-       struct ftsdc010_chip *chip;
-       struct ftsdc010_mmc __iomem *regs;
-#ifdef CONFIG_FTSDC010_BASE_LIST
-       uint32_t base_list[] = CONFIG_FTSDC010_BASE_LIST;
-
-       if (devid < 0 || devid >= ARRAY_SIZE(base_list))
-               return -1;
-       regs = (void __iomem *)base_list[devid];
-#else
-       regs = (void __iomem *)(CONFIG_FTSDC010_BASE + (devid << 20));
-#endif
+       cfg->name = name;
+       cfg->f_min = min_clk;
+       cfg->f_max = max_clk;
+       cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34 | MMC_VDD_165_195;
+       cfg->host_caps = caps;
+       if (buswidth == 8) {
+               cfg->host_caps |= MMC_MODE_8BIT;
+               cfg->host_caps &= ~MMC_MODE_4BIT;
+       } else {
+               cfg->host_caps |= MMC_MODE_4BIT;
+               cfg->host_caps &= ~MMC_MODE_8BIT;
+       }
+       cfg->part_type = PART_TYPE_DOS;
+       cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+}
 
-       chip = malloc(sizeof(struct ftsdc010_chip));
-       if (!chip)
-               return -ENOMEM;
-       memset(chip, 0, sizeof(struct ftsdc010_chip));
+static int ftsdc010_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+#if !CONFIG_IS_ENABLED(OF_PLATDATA)
+       struct ftsdc_priv *priv = dev_get_priv(dev);
+       struct ftsdc010_chip *chip = &priv->chip;
+       chip->name = dev->name;
+       chip->ioaddr = (void *)devfdt_get_addr(dev);
+       chip->buswidth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+                                       "bus-width", 4);
+       chip->priv = dev;
+       priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+                                   "fifo-depth", 0);
+       priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
+                                         "fifo-mode");
+       if (fdtdec_get_int_array(gd->fdt_blob, dev_of_offset(dev),
+                        "clock-freq-min-max", priv->minmax, 2)) {
+               int val = fdtdec_get_int(gd->fdt_blob, dev_of_offset(dev),
+                                 "max-frequency", -EINVAL);
+               if (val < 0)
+                       return val;
+
+               priv->minmax[0] = 400000;  /* 400 kHz */
+               priv->minmax[1] = val;
+       } else {
+               debug("%s: 'clock-freq-min-max' property was deprecated.\n",
+               __func__);
+       }
+#endif
+       chip->sclk = priv->minmax[1];
+       chip->regs = chip->ioaddr;
+       return 0;
+}
 
-       chip->regs = regs;
-#ifdef CONFIG_SYS_CLK_FREQ
-       chip->sclk = CONFIG_SYS_CLK_FREQ;
-#else
-       chip->sclk = clk_get_rate("SDC");
+static int ftsdc010_mmc_probe(struct udevice *dev)
+{
+       struct ftsdc010_plat *plat = dev_get_platdata(dev);
+       struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+       struct ftsdc_priv *priv = dev_get_priv(dev);
+       struct ftsdc010_chip *chip = &priv->chip;
+       struct udevice *pwr_dev __maybe_unused;
+
+#if CONFIG_IS_ENABLED(OF_PLATDATA)
+       int ret;
+       struct ftsdc010 *dtplat = &plat->dtplat;
+       chip->name = dev->name;
+       chip->ioaddr = map_sysmem(dtplat->reg[0], dtplat->reg[1]);
+       chip->buswidth = dtplat->bus_width;
+       chip->priv = dev;
+       chip->dev_index = 1;
+       memcpy(priv->minmax, dtplat->clock_freq_min_max, sizeof(priv->minmax));
+       ret = clk_get_by_index_platdata(dev, 0, dtplat->clocks, &priv->clk);
+       if (ret < 0)
+               return ret;
 #endif
 
-       chip->cfg.name = "ftsdc010";
-       chip->cfg.ops = &ftsdc010_ops;
-       chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz;
-       switch (readl(&regs->bwr) & FTSDC010_BWR_CAPS_MASK) {
-       case FTSDC010_BWR_CAPS_4BIT:
-               chip->cfg.host_caps |= MMC_MODE_4BIT;
-               break;
-       case FTSDC010_BWR_CAPS_8BIT:
-               chip->cfg.host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT;
-               break;
-       default:
-               break;
-       }
+       if (dev_read_bool(dev, "cap-mmc-highspeed") || \
+                 dev_read_bool(dev, "cap-sd-highspeed"))
+               chip->caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz;
+
+       ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps,
+                       priv->minmax[1] , priv->minmax[0]);
+       chip->mmc = &plat->mmc;
+       chip->mmc->priv = &priv->chip;
+       chip->mmc->dev = dev;
+       upriv->mmc = chip->mmc;
+       return ftsdc010_probe(dev);
+}
 
-       chip->cfg.voltages  = MMC_VDD_32_33 | MMC_VDD_33_34;
-       chip->cfg.f_max     = chip->sclk / 2;
-       chip->cfg.f_min     = chip->sclk / 0x100;
+int ftsdc010_mmc_bind(struct udevice *dev)
+{
+       struct ftsdc010_plat *plat = dev_get_platdata(dev);
 
-       chip->cfg.part_type = PART_TYPE_DOS;
-       chip->cfg.b_max     = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+       return mmc_bind(dev, &plat->mmc, &plat->cfg);
+}
 
-       mmc = mmc_create(&chip->cfg, chip);
-       if (mmc == NULL) {
-               free(chip);
-               return -ENOMEM;
-       }
+static const struct udevice_id ftsdc010_mmc_ids[] = {
+       { .compatible = "andestech,atfsdc010" },
+       { }
+};
 
-       return 0;
-}
+U_BOOT_DRIVER(ftsdc010_mmc) = {
+       .name           = "ftsdc010_mmc",
+       .id             = UCLASS_MMC,
+       .of_match       = ftsdc010_mmc_ids,
+       .ofdata_to_platdata = ftsdc010_mmc_ofdata_to_platdata,
+       .ops            = &dm_ftsdc010_mmc_ops,
+       .bind           = ftsdc010_mmc_bind,
+       .probe          = ftsdc010_mmc_probe,
+       .priv_auto_alloc_size = sizeof(struct ftsdc_priv),
+       .platdata_auto_alloc_size = sizeof(struct ftsdc010_plat),
+};