diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index 077b184..6ac26dd 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -371,6 +371,15 @@ config MMC_SUNXI This selects support for the SD/MMC Host Controller on Allwinner sunxi SoCs. +config GENERIC_ATMEL_MCI + bool "Atmel Multimedia Card Interface support" + depends on DM_MMC && BLK && DM_MMC_OPS && ARCH_AT91 + help + This enables support for Atmel High Speed Multimedia Card Interface + (HSMCI), which supports the MultiMedia Card (MMC) Specification V4.3, + the SD Memory Card Specification V2.0, the SDIO V2.0 specification + and CE-ATA V1.1. + endif config TEGRA124_MMC_DISABLE_EXT_LOOPBACK diff --git a/drivers/mmc/gen_atmel_mci.c b/drivers/mmc/gen_atmel_mci.c index 7dc4a5d..c25d9ed 100644 --- a/drivers/mmc/gen_atmel_mci.c +++ b/drivers/mmc/gen_atmel_mci.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include @@ -18,8 +19,11 @@ #include #include #include +#include #include "atmel_mci.h" +DECLARE_GLOBAL_DATA_PTR; + #ifndef CONFIG_SYS_MMC_CLK_OD # define CONFIG_SYS_MMC_CLK_OD 150000 #endif @@ -37,6 +41,10 @@ struct atmel_mci_priv { struct atmel_mci *mci; unsigned int initialized:1; unsigned int curr_clk; +#ifdef CONFIG_DM_MMC + struct mmc mmc; + ulong bus_clk_rate; +#endif }; /* Read Atmel MCI IP version */ @@ -58,11 +66,19 @@ static void dump_cmd(u32 cmdr, u32 arg, u32 status, const char* msg) } /* Setup for MCI Clock and Block Size */ +#ifdef CONFIG_DM_MMC +static void mci_set_mode(struct atmel_mci_priv *priv, u32 hz, u32 blklen) +{ + struct mmc *mmc = &priv->mmc; + u32 bus_hz = priv->bus_clk_rate; +#else static void mci_set_mode(struct mmc *mmc, u32 hz, u32 blklen) { struct atmel_mci_priv *priv = mmc->priv; - atmel_mci_t *mci = priv->mci; u32 bus_hz = get_mci_clk_rate(); +#endif + + atmel_mci_t *mci = priv->mci; u32 clkdiv = 255; unsigned int version = atmel_mci_get_version(mci); u32 clkodd = 0; @@ -202,10 +218,18 @@ io_fail: * Sends a command out on the bus and deals with the block data. * Takes the mmc pointer, a command pointer, and an optional data pointer. */ +#ifdef CONFIG_DM_MMC +static int atmel_mci_send_cmd(struct udevice *dev, struct mmc_cmd *cmd, + struct mmc_data *data) +{ + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else static int mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) { struct atmel_mci_priv *priv = mmc->priv; +#endif atmel_mci_t *mci = priv->mci; u32 cmdr; u32 error_flags = 0; @@ -335,17 +359,28 @@ mci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, struct mmc_data *data) return 0; } +#ifdef CONFIG_DM_MMC +static int atmel_mci_set_ios(struct udevice *dev) +{ + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct mmc *mmc = mmc_get_mmc_dev(dev); +#else /* Entered into mmc structure during driver init */ static int mci_set_ios(struct mmc *mmc) { struct atmel_mci_priv *priv = mmc->priv; +#endif atmel_mci_t *mci = priv->mci; int bus_width = mmc->bus_width; unsigned int version = atmel_mci_get_version(mci); int busw; /* Set the clock speed */ +#ifdef CONFIG_DM_MMC + mci_set_mode(priv, mmc->clock, MMC_DEFAULT_BLKLEN); +#else mci_set_mode(mmc, mmc->clock, MMC_DEFAULT_BLKLEN); +#endif /* * set the bus width and select slot for this interface @@ -374,10 +409,15 @@ static int mci_set_ios(struct mmc *mmc) return 0; } +#ifdef CONFIG_DM_MMC +static int atmel_mci_hw_init(struct atmel_mci_priv *priv) +{ +#else /* Entered into mmc structure during driver init */ static int mci_init(struct mmc *mmc) { struct atmel_mci_priv *priv = mmc->priv; +#endif atmel_mci_t *mci = priv->mci; /* Initialize controller */ @@ -392,11 +432,16 @@ static int mci_init(struct mmc *mmc) writel(~0UL, &mci->idr); /* Set default clocks and blocklen */ +#ifdef CONFIG_DM_MMC + mci_set_mode(priv, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); +#else mci_set_mode(mmc, CONFIG_SYS_MMC_CLK_OD, MMC_DEFAULT_BLKLEN); +#endif return 0; } +#ifndef CONFIG_DM_MMC static const struct mmc_ops atmel_mci_ops = { .send_cmd = mci_send_cmd, .set_ios = mci_set_ios, @@ -456,3 +501,114 @@ int atmel_mci_init(void *regs) return 0; } +#endif + +#ifdef CONFIG_DM_MMC +static const struct dm_mmc_ops atmel_mci_mmc_ops = { + .send_cmd = atmel_mci_send_cmd, + .set_ios = atmel_mci_set_ios, +}; + +static void atmel_mci_setup_cfg(struct atmel_mci_priv *priv) +{ + struct mmc_config *cfg; + u32 version; + + cfg = &priv->cfg; + cfg->name = "Atmel mci"; + cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34; + + /* + * If the version is above 3.0, the capabilities of the 8-bit + * bus width and high speed are supported. + */ + version = atmel_mci_get_version(priv->mci); + if ((version & 0xf00) >= 0x300) { + cfg->host_caps = MMC_MODE_8BIT | + MMC_MODE_HS | MMC_MODE_HS_52MHz; + } + + cfg->host_caps |= MMC_MODE_4BIT; + cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT; + cfg->f_min = priv->bus_clk_rate / (2 * 256); + cfg->f_max = priv->bus_clk_rate / 2; +} + +static int atmel_mci_enable_clk(struct udevice *dev) +{ + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct clk clk; + ulong clk_rate; + int ret = 0; + + ret = clk_get_by_index(dev, 0, &clk); + if (ret) { + ret = -EINVAL; + goto failed; + } + + ret = clk_enable(&clk); + if (ret) + goto failed; + + clk_rate = clk_get_rate(&clk); + if (!clk_rate) { + ret = -EINVAL; + goto failed; + } + + priv->bus_clk_rate = clk_rate; + +failed: + clk_free(&clk); + + return ret; +} + +static int atmel_mci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct atmel_mci_priv *priv = dev_get_priv(dev); + struct mmc *mmc; + int ret; + + ret = atmel_mci_enable_clk(dev); + if (ret) + return ret; + + priv->mci = (struct atmel_mci *)dev_get_addr_ptr(dev); + + atmel_mci_setup_cfg(priv); + + mmc = &priv->mmc; + mmc->cfg = &priv->cfg; + mmc->dev = dev; + upriv->mmc = mmc; + + atmel_mci_hw_init(priv); + + return 0; +} + +static int atmel_mci_bind(struct udevice *dev) +{ + struct atmel_mci_priv *priv = dev_get_priv(dev); + + return mmc_bind(dev, &priv->mmc, &priv->cfg); +} + +static const struct udevice_id atmel_mci_ids[] = { + { .compatible = "atmel,hsmci" }, + { } +}; + +U_BOOT_DRIVER(atmel_mci) = { + .name = "atmel-mci", + .id = UCLASS_MMC, + .of_match = atmel_mci_ids, + .bind = atmel_mci_bind, + .probe = atmel_mci_probe, + .priv_auto_alloc_size = sizeof(struct atmel_mci_priv), + .ops = &atmel_mci_mmc_ops, +}; +#endif