
From: rick rick@andestech.com
Add Andestech mmc DM driver for ag101p board. Do not use get_timer() to check mmc state can improve throughput performance.
Signed-off-by: rick rick@andestech.com Cc: Andes uboot@andestech.com --- drivers/mmc/Kconfig | 7 +++ drivers/mmc/Makefile | 1 + drivers/mmc/ftsdc010_mci.c | 106 ++++++++++++++++++++++++--------- drivers/mmc/nds32_mmc.c | 140 ++++++++++++++++++++++++++++++++++++++++++++ include/faraday/ftsdc010.h | 41 +++++++++++++ 5 files changed, 267 insertions(+), 28 deletions(-) create mode 100644 drivers/mmc/nds32_mmc.c
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 24f4b28..59c5e24 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -54,6 +54,13 @@ config ROCKCHIP_DWMMC SD 3.0, SDIO 3.0 and MMC 4.5 and supports common eMMC chips as well as removeable SD and micro-SD cards.
+config NDS32_MMC + bool "Andestech SD/MMC controller support" + depends on DM_MMC && OF_CONTROL + help + This enables support for the Andestech SD/MMM controller, which is + based on Faraday IP. + config SH_SDHI bool "SuperH/Renesas ARM SoCs on-chip SDHI host controller support" depends on RMOBILE diff --git a/drivers/mmc/Makefile b/drivers/mmc/Makefile index d850758..905c3e0 100644 --- a/drivers/mmc/Makefile +++ b/drivers/mmc/Makefile @@ -40,6 +40,7 @@ obj-$(CONFIG_OMAP_HSMMC) += omap_hsmmc.o obj-$(CONFIG_X86) += pci_mmc.o obj-$(CONFIG_PXA_MMC_GENERIC) += pxa_mmc_gen.o obj-$(CONFIG_ROCKCHIP_DWMMC) += rockchip_dw_mmc.o +obj-$(CONFIG_NDS32_MMC) += nds32_mmc.o obj-$(CONFIG_SUPPORT_EMMC_RPMB) += rpmb.o obj-$(CONFIG_S3C_SDI) += s3c_sdi.o obj-$(CONFIG_S5P_SDHCI) += s5p_sdhci.o diff --git a/drivers/mmc/ftsdc010_mci.c b/drivers/mmc/ftsdc010_mci.c index e88c632..9807eba 100644 --- a/drivers/mmc/ftsdc010_mci.c +++ b/drivers/mmc/ftsdc010_mci.c @@ -11,7 +11,6 @@ #include <malloc.h> #include <part.h> #include <mmc.h> - #include <asm/io.h> #include <linux/errno.h> #include <asm/byteorder.h> @@ -20,16 +19,6 @@ #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 */ -}; - static inline int ftsdc010_send_cmd(struct mmc *mmc, struct mmc_cmd *mmc_cmd) { struct ftsdc010_chip *chip = mmc->priv; @@ -127,9 +116,8 @@ static void ftsdc010_clkset(struct mmc *mmc, uint32_t rate) static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) { int ret = -ETIMEDOUT; - uint32_t st, ts; - - for (ts = get_timer(0); get_timer(ts) < CFG_CMD_TIMEOUT; ) { + uint32_t st, timeout = 10000000; + while (timeout--) { st = readl(®s->status); if (!(st & mask)) continue; @@ -137,7 +125,6 @@ static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) ret = 0; break; } - if (ret) debug("ftsdc010: wait st(0x%x) timeout\n", mask);
@@ -147,10 +134,16 @@ static int ftsdc010_wait(struct ftsdc010_mmc __iomem *regs, uint32_t mask) /* * u-boot mmc api */ - +#ifdef CONFIG_DM_MMC_OPS +static int ftsdc010_request(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { +#endif int ret = -EOPNOTSUPP; uint32_t len = 0; struct ftsdc010_chip *chip = mmc->priv; @@ -251,8 +244,14 @@ static int ftsdc010_request(struct mmc *mmc, struct mmc_cmd *cmd, return ret; }
+#ifdef CONFIG_DM_MMC_OPS +static int ftsdc010_set_ios(struct udevice *dev) +{ + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else static void ftsdc010_set_ios(struct mmc *mmc) { +#endif struct ftsdc010_chip *chip = mmc->priv; struct ftsdc010_mmc __iomem *regs = chip->regs;
@@ -270,6 +269,9 @@ static void ftsdc010_set_ios(struct mmc *mmc) setbits_le32(®s->bwr, FTSDC010_BWR_MODE_1BIT); break; } +#ifdef CONFIG_DM_MMC_OPS + return 0; +#endif }
static int ftsdc010_init(struct mmc *mmc) @@ -277,7 +279,6 @@ static int ftsdc010_init(struct mmc *mmc) struct ftsdc010_chip *chip = mmc->priv; struct ftsdc010_mmc __iomem *regs = chip->regs; uint32_t ts; - if (readl(®s->status) & FTSDC010_STATUS_CARD_DETECT) return -ENOMEDIUM;
@@ -309,11 +310,67 @@ static int ftsdc010_init(struct mmc *mmc) return 0; }
+#ifdef CONFIG_DM_MMC_OPS +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_ops = { + .send_cmd = ftsdc010_request, + .set_ios = ftsdc010_set_ios, +}; + +#else static const struct mmc_ops ftsdc010_ops = { .send_cmd = ftsdc010_request, .set_ios = ftsdc010_set_ios, .init = ftsdc010_init, }; +#endif + +void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, + uint caps, u32 max_clk, u32 min_clk) +{ + 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->host_caps |= MMC_MODE_HS | MMC_MODE_HS_52MHz; + cfg->part_type = PART_TYPE_DOS; + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; +} + +void set_bus_width(struct ftsdc010_mmc __iomem *regs, struct mmc_config *cfg) +{ + switch (readl(®s->bwr) & FTSDC010_BWR_CAPS_MASK) { + case FTSDC010_BWR_CAPS_4BIT: + cfg->host_caps |= MMC_MODE_4BIT; + cfg->host_caps &= ~MMC_MODE_8BIT; + break; + case FTSDC010_BWR_CAPS_8BIT: + cfg->host_caps |= MMC_MODE_4BIT | MMC_MODE_8BIT; + break; + default: + break; + } +} + +#ifdef CONFIG_BLK +int ftsdc010_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg) +{ + return mmc_bind(dev, mmc, cfg); +} +#else
int ftsdc010_mmc_init(int devid) { @@ -343,19 +400,11 @@ int ftsdc010_mmc_init(int devid) #endif
chip->cfg.name = "ftsdc010"; +#ifndef CONFIG_DM_MMC_OPS chip->cfg.ops = &ftsdc010_ops; +#endif chip->cfg.host_caps = MMC_MODE_HS | MMC_MODE_HS_52MHz; - switch (readl(®s->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; - } - + set_bus_width(regs , &chip->cfg); 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; @@ -371,3 +420,4 @@ int ftsdc010_mmc_init(int devid)
return 0; } +#endif diff --git a/drivers/mmc/nds32_mmc.c b/drivers/mmc/nds32_mmc.c new file mode 100644 index 0000000..f63661c --- /dev/null +++ b/drivers/mmc/nds32_mmc.c @@ -0,0 +1,140 @@ +/* + * Andestech ATFSDC010 SD/MMC driver + * + * (C) Copyright 2016 + * Rick Chen, NDS32 Software Engineering, rick@andestech.com + + * SPDX-License-Identifier: GPL-2.0+ + */ + +#include <common.h> +#include <clk.h> +#include <dm.h> +#include <dt-structs.h> +#include <faraday/ftsdc010.h> +#include <errno.h> +#include <mapmem.h> +#include <pwrseq.h> +#include <syscon.h> +#include <mmc.h> +#include <linux/err.h> + +DECLARE_GLOBAL_DATA_PTR; + +#if CONFIG_IS_ENABLED(OF_PLATDATA) +struct nds_mmc { + fdt32_t bus_width; + bool cap_mmc_highspeed; + bool cap_sd_highspeed; + fdt32_t card_detect_delay; + fdt32_t clock_freq_min_max[2]; + struct phandle_2_cell clocks[4]; + bool disable_wp; + fdt32_t fifo_depth; + fdt32_t interrupts[3]; + fdt32_t num_slots; + fdt32_t reg[2]; + fdt32_t vmmc_supply; +}; +#endif + +struct nds_mmc_plat { +#if CONFIG_IS_ENABLED(OF_PLATDATA) + struct nds_mmc 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 int nds32_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 *)dev_get_addr(dev); + chip->buswidth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "bus-width", 4); + chip->priv = dev; + /* use non-removeable as sdcard and emmc as judgement */ + if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset, "non-removable")) + chip->dev_index = 0; + else + chip->dev_index = 1; + + priv->fifo_depth = fdtdec_get_int(gd->fdt_blob, dev->of_offset, + "fifo-depth", 0); + priv->fifo_mode = fdtdec_get_bool(gd->fdt_blob, dev->of_offset, + "fifo-mode"); + if (fdtdec_get_int_array(gd->fdt_blob, dev->of_offset, + "clock-freq-min-max", priv->minmax, 2)) + return -EINVAL; +#endif + chip->sclk = priv->minmax[1]; + chip->regs = chip->ioaddr; + return 0; +} + +static int nds32_mmc_probe(struct udevice *dev) +{ + struct nds_mmc_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 nds_mmc *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 = 0; + priv->fifo_depth = dtplat->fifo_depth; + priv->fifo_mode = 0; + 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 + ftsdc_setup_cfg(&plat->cfg, dev->name, chip->buswidth, chip->caps, + priv->minmax[1] , priv->minmax[0]); + set_bus_width(chip->regs , &chip->cfg); + + chip->mmc = &plat->mmc; + chip->mmc->priv = &priv->chip; + chip->mmc->dev = dev; + upriv->mmc = chip->mmc; + return ftsdc010_probe(dev); +} + +static int nds32_mmc_bind(struct udevice *dev) +{ + struct nds_mmc_plat *plat = dev_get_platdata(dev); + return ftsdc010_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id nds32_mmc_ids[] = { + { .compatible = "andestech,atsdc010" }, + { } +}; + +U_BOOT_DRIVER(rockchip_dwmmc_drv) = { + .name = "nds32_mmc", + .id = UCLASS_MMC, + .of_match = nds32_mmc_ids, + .ofdata_to_platdata = nds32_mmc_ofdata_to_platdata, + .ops = &dm_ftsdc010_ops, + .bind = nds32_mmc_bind, + .probe = nds32_mmc_probe, + .priv_auto_alloc_size = sizeof(struct ftsdc_priv), + .platdata_auto_alloc_size = sizeof(struct nds_mmc_plat), +}; diff --git a/include/faraday/ftsdc010.h b/include/faraday/ftsdc010.h index 9bfdef9..8b30034 100644 --- a/include/faraday/ftsdc010.h +++ b/include/faraday/ftsdc010.h @@ -6,6 +6,7 @@ * * SPDX-License-Identifier: GPL-2.0+ */ +#include <mmc.h>
#ifndef __FTSDC010_H #define __FTSDC010_H @@ -243,4 +244,44 @@ int ftsdc010_mmc_init(int dev_index);
#endif /* CONFIG_FTSDC010_SDIO */
+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 */ + const char *name; + void *ioaddr; + unsigned int quirks; + unsigned int caps; + unsigned int version; + unsigned int clock; + unsigned int bus_hz; + unsigned int div; + int dev_index; + int dev_id; + int buswidth; + u32 fifoth_val; + struct mmc *mmc; + void *priv; + bool fifo_mode; +}; + + +#ifdef CONFIG_DM_MMC_OPS +/* Export the operations to drivers */ +int ftsdc010_probe(struct udevice *dev); +extern const struct dm_mmc_ops dm_ftsdc010_ops; +#endif +void ftsdc_setup_cfg(struct mmc_config *cfg, const char *name, int buswidth, + uint caps, u32 max_clk, u32 min_clk); +void set_bus_width(struct ftsdc010_mmc __iomem *regs, struct mmc_config *cfg); + +#ifdef CONFIG_BLK +int ftsdc010_bind(struct udevice *dev, struct mmc *mmc, struct mmc_config *cfg); +#endif + + #endif /* __FTSDC010_H */