diff options
Diffstat (limited to 'drivers/mmc')
-rw-r--r-- | drivers/mmc/Kconfig | 20 | ||||
-rw-r--r-- | drivers/mmc/arm_pl180_mmci.c | 14 | ||||
-rw-r--r-- | drivers/mmc/arm_pl180_mmci.h | 3 | ||||
-rw-r--r-- | drivers/mmc/dw_mmc.c | 38 | ||||
-rw-r--r-- | drivers/mmc/fsl_esdhc.c | 41 | ||||
-rw-r--r-- | drivers/mmc/mtk-sd.c | 23 | ||||
-rw-r--r-- | drivers/mmc/mv_sdhci.c | 67 | ||||
-rw-r--r-- | drivers/mmc/rockchip_dw_mmc.c | 4 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 188 | ||||
-rw-r--r-- | drivers/mmc/stm32_sdmmc2.c | 67 |
10 files changed, 384 insertions, 81 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c34dd5d187..c23299ea96 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -78,6 +78,12 @@ config SUPPORT_EMMC_RPMB Enable support for reading, writing and programming the key for the Replay Protection Memory Block partition in eMMC. +config SUPPORT_EMMC_BOOT + bool "Support some additional features of the eMMC boot partitions" + help + Enable support for eMMC boot partitions. This also enables + extensions within the mmc command. + config MMC_IO_VOLTAGE bool "Support IO voltage configuration" help @@ -385,6 +391,20 @@ config MMC_SDHCI_SDMA This enables support for the SDMA (Single Operation DMA) defined in the SD Host Controller Standard Specification Version 1.00 . +config MMC_SDHCI_ADMA + bool "Support SDHCI ADMA2" + depends on MMC_SDHCI + help + This enables support for the ADMA (Advanced DMA) defined + in the SD Host Controller Standard Specification Version 3.00 + +config SPL_MMC_SDHCI_ADMA + bool "Support SDHCI ADMA2 in SPL" + depends on MMC_SDHCI + help + This enables support for the ADMA (Advanced DMA) defined + in the SD Host Controller Standard Specification Version 3.00 in SPL. + config MMC_SDHCI_ATMEL bool "Atmel SDHCI controller support" depends on ARCH_AT91 diff --git a/drivers/mmc/arm_pl180_mmci.c b/drivers/mmc/arm_pl180_mmci.c index f71d79ecd6..ea8eb0d509 100644 --- a/drivers/mmc/arm_pl180_mmci.c +++ b/drivers/mmc/arm_pl180_mmci.c @@ -422,6 +422,7 @@ static int arm_pl180_mmc_probe(struct udevice *dev) struct mmc_config *cfg = &pdata->cfg; struct clk clk; u32 bus_width; + u32 periphid; int ret; ret = clk_get_by_index(dev, 0, &clk); @@ -439,7 +440,15 @@ static int arm_pl180_mmc_probe(struct udevice *dev) host->clkdiv_init = SDI_CLKCR_CLKDIV_INIT_V1 | SDI_CLKCR_CLKEN | SDI_CLKCR_HWFC_EN; host->clock_in = clk_get_rate(&clk); - host->version2 = dev_get_driver_data(dev); + + periphid = dev_read_u32_default(dev, "arm,primecell-periphid", 0); + switch (periphid) { + case STM32_MMCI_ID: /* stm32 variant */ + host->version2 = false; + break; + default: + host->version2 = true; + } cfg->name = dev->name; cfg->voltages = VOLTAGE_WINDOW_SD; @@ -526,7 +535,8 @@ static int arm_pl180_mmc_ofdata_to_platdata(struct udevice *dev) } static const struct udevice_id arm_pl180_mmc_match[] = { - { .compatible = "st,stm32f4xx-sdio", .data = VERSION1 }, + { .compatible = "arm,pl180" }, + { .compatible = "arm,primecell" }, { /* sentinel */ } }; diff --git a/drivers/mmc/arm_pl180_mmci.h b/drivers/mmc/arm_pl180_mmci.h index 36487be288..61ee96a112 100644 --- a/drivers/mmc/arm_pl180_mmci.h +++ b/drivers/mmc/arm_pl180_mmci.h @@ -141,8 +141,7 @@ #define SDI_FIFO_BURST_SIZE 8 -#define VERSION1 false -#define VERSION2 true +#define STM32_MMCI_ID 0x00880180 struct sdi_registers { u32 power; /* 0x00*/ diff --git a/drivers/mmc/dw_mmc.c b/drivers/mmc/dw_mmc.c index 93a836eac3..1992d61182 100644 --- a/drivers/mmc/dw_mmc.c +++ b/drivers/mmc/dw_mmc.c @@ -74,15 +74,15 @@ static void dwmci_prepare_data(struct dwmci_host *host, dwmci_set_idma_desc(cur_idmac, flags, cnt, (ulong)bounce_buffer + (i * PAGE_SIZE)); + cur_idmac++; if (blk_cnt <= 8) break; blk_cnt -= 8; - cur_idmac++; i++; } while(1); data_end = (ulong)cur_idmac; - flush_dcache_range(data_start, data_end + ARCH_DMA_MINALIGN); + flush_dcache_range(data_start, roundup(data_end, ARCH_DMA_MINALIGN)); ctrl = dwmci_readl(host, DWMCI_CTRL); ctrl |= DWMCI_IDMAC_EN | DWMCI_DMA_EN; @@ -114,22 +114,40 @@ static int dwmci_fifo_ready(struct dwmci_host *host, u32 bit, u32 *len) return 0; } +static unsigned int dwmci_get_timeout(struct mmc *mmc, const unsigned int size) +{ + unsigned int timeout; + + timeout = size * 8 * 1000; /* counting in bits and msec */ + timeout *= 2; /* wait twice as long */ + timeout /= mmc->clock; + timeout /= mmc->bus_width; + timeout /= mmc->ddr_mode ? 2 : 1; + timeout = (timeout < 1000) ? 1000 : timeout; + + return timeout; +} + static int dwmci_data_transfer(struct dwmci_host *host, struct mmc_data *data) { + struct mmc *mmc = host->mmc; int ret = 0; - u32 timeout = 240000; - u32 mask, size, i, len = 0; + u32 timeout, mask, size, i, len = 0; u32 *buf = NULL; ulong start = get_timer(0); u32 fifo_depth = (((host->fifoth_val & RX_WMARK_MASK) >> RX_WMARK_SHIFT) + 1) * 2; - size = data->blocksize * data->blocks / 4; + size = data->blocksize * data->blocks; if (data->flags == MMC_DATA_READ) buf = (unsigned int *)data->dest; else buf = (unsigned int *)data->src; + timeout = dwmci_get_timeout(mmc, size); + + size /= 4; + for (;;) { mask = dwmci_readl(host, DWMCI_RINTSTS); /* Error during data transfer. */ @@ -252,14 +270,20 @@ static int dwmci_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd, dwmci_wait_reset(host, DWMCI_CTRL_FIFO_RESET); } else { if (data->flags == MMC_DATA_READ) { - bounce_buffer_start(&bbstate, (void*)data->dest, + ret = bounce_buffer_start(&bbstate, + (void*)data->dest, data->blocksize * data->blocks, GEN_BB_WRITE); } else { - bounce_buffer_start(&bbstate, (void*)data->src, + ret = bounce_buffer_start(&bbstate, + (void*)data->src, data->blocksize * data->blocks, GEN_BB_READ); } + + if (ret) + return ret; + dwmci_prepare_data(host, data, cur_idmac, bbstate.bounce_buffer); } diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c index 9e34557d16..1b7de74a72 100644 --- a/drivers/mmc/fsl_esdhc.c +++ b/drivers/mmc/fsl_esdhc.c @@ -297,6 +297,13 @@ static int esdhc_setup_data(struct fsl_esdhc_priv *priv, struct mmc *mmc, printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); return -ETIMEDOUT; } + } else { +#ifdef CONFIG_DM_GPIO + if (dm_gpio_is_valid(&priv->wp_gpio) && dm_gpio_get_value(&priv->wp_gpio)) { + printf("\nThe SD card is locked. Can not write to a locked card.\n\n"); + return -ETIMEDOUT; + } +#endif } esdhc_clrsetbits32(®s->wml, WML_WR_WML_MASK, @@ -614,18 +621,31 @@ static void set_sysctl(struct fsl_esdhc_priv *priv, struct mmc *mmc, uint clock) #else int pre_div = 2; #endif - int ddr_pre_div = mmc->ddr_mode ? 2 : 1; int sdhc_clk = priv->sdhc_clk; uint clk; + /* + * For ddr mode, usdhc need to enable DDR mode first, after select + * this DDR mode, usdhc will automatically divide the usdhc clock + */ + if (mmc->ddr_mode) { + writel(readl(®s->mixctrl) | MIX_CTRL_DDREN, ®s->mixctrl); + sdhc_clk >>= 1; + } + if (clock < mmc->cfg->f_min) clock = mmc->cfg->f_min; - while (sdhc_clk / (16 * pre_div * ddr_pre_div) > clock && pre_div < 256) - pre_div *= 2; + if (sdhc_clk / 16 > clock) { + for (; pre_div < 256; pre_div *= 2) + if ((sdhc_clk / pre_div) <= (clock * 16)) + break; + } else + pre_div = 1; - while (sdhc_clk / (div * pre_div * ddr_pre_div) > clock && div < 16) - div++; + for (div = 1; div <= 16; div++) + if ((sdhc_clk / (div * pre_div)) <= clock) + break; pre_div >>= 1; div -= 1; @@ -1489,14 +1509,15 @@ static int fsl_esdhc_probe(struct udevice *dev) #endif } - priv->wp_enable = 1; - + if (dev_read_prop(dev, "fsl,wp-controller", NULL)) { + priv->wp_enable = 1; + } else { + priv->wp_enable = 0; #ifdef CONFIG_DM_GPIO - ret = gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, + gpio_request_by_name(dev, "wp-gpios", 0, &priv->wp_gpio, GPIOD_IS_IN); - if (ret) - priv->wp_enable = 0; #endif + } priv->vs18_enable = 0; diff --git a/drivers/mmc/mtk-sd.c b/drivers/mmc/mtk-sd.c index d3f0778368..e0ac3e9d69 100644 --- a/drivers/mmc/mtk-sd.c +++ b/drivers/mmc/mtk-sd.c @@ -247,6 +247,7 @@ struct msdc_host { struct msdc_compatible *dev_comp; struct clk src_clk; /* for SD/MMC bus clock */ + struct clk src_clk_cg; /* optional, MSDC source clock control gate */ struct clk h_clk; /* MSDC core clock */ u32 src_clk_freq; /* source clock */ @@ -269,7 +270,7 @@ struct msdc_host { bool builtin_cd; /* card detection / write protection GPIOs */ -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) struct gpio_desc gpio_wp; struct gpio_desc gpio_cd; #endif @@ -849,7 +850,7 @@ static int msdc_ops_get_cd(struct udevice *dev) return !(val & MSDC_PS_CDSTS); } -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) if (!host->gpio_cd.dev) return 1; @@ -861,7 +862,7 @@ static int msdc_ops_get_cd(struct udevice *dev) static int msdc_ops_get_wp(struct udevice *dev) { -#if IS_ENABLED(DM_GPIO) +#if CONFIG_IS_ENABLED(DM_GPIO) struct msdc_host *host = dev_get_priv(dev); if (!host->gpio_wp.dev) @@ -1269,6 +1270,8 @@ static void msdc_ungate_clock(struct msdc_host *host) { clk_enable(&host->src_clk); clk_enable(&host->h_clk); + if (host->src_clk_cg.dev) + clk_enable(&host->src_clk_cg); } static int msdc_drv_probe(struct udevice *dev) @@ -1332,7 +1335,9 @@ static int msdc_ofdata_to_platdata(struct udevice *dev) if (ret < 0) return ret; -#if IS_ENABLED(DM_GPIO) + clk_get_by_name(dev, "source_cg", &host->src_clk_cg); /* optional */ + +#if CONFIG_IS_ENABLED(DM_GPIO) gpio_request_by_name(dev, "wp-gpios", 0, &host->gpio_wp, GPIOD_IS_IN); gpio_request_by_name(dev, "cd-gpios", 0, &host->gpio_cd, GPIOD_IS_IN); #endif @@ -1376,8 +1381,18 @@ static const struct msdc_compatible mt7623_compat = { .enhance_rx = false }; +static const struct msdc_compatible mt8516_compat = { + .clk_div_bits = 12, + .pad_tune0 = true, + .async_fifo = true, + .data_tune = true, + .busy_check = true, + .stop_clk_fix = true, +}; + static const struct udevice_id msdc_ids[] = { { .compatible = "mediatek,mt7623-mmc", .data = (ulong)&mt7623_compat }, + { .compatible = "mediatek,mt8516-mmc", .data = (ulong)&mt8516_compat }, {} }; diff --git a/drivers/mmc/mv_sdhci.c b/drivers/mmc/mv_sdhci.c index de4ae0a0e7..bf26d2e4e2 100644 --- a/drivers/mmc/mv_sdhci.c +++ b/drivers/mmc/mv_sdhci.c @@ -4,10 +4,13 @@ */ #include <common.h> +#include <dm.h> #include <malloc.h> #include <sdhci.h> #include <linux/mbus.h> +#define MVSDH_NAME "mv_sdh" + #define SDHCI_WINDOW_CTRL(win) (0x4080 + ((win) << 4)) #define SDHCI_WINDOW_BASE(win) (0x4084 + ((win) << 4)) @@ -36,6 +39,8 @@ static void sdhci_mvebu_mbus_config(void __iomem *base) } } +#ifndef CONFIG_DM_MMC + #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS static struct sdhci_ops mv_ops; @@ -63,7 +68,6 @@ static inline void mv_sdhci_writeb(struct sdhci_host *host, u8 val, int reg) #endif /* CONFIG_SHEEVA_88SV331xV5 */ #endif /* CONFIG_MMC_SDHCI_IO_ACCESSORS */ -static char *MVSDH_NAME = "mv_sdh"; int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks) { struct sdhci_host *host = NULL; @@ -90,3 +94,64 @@ int mv_sdh_init(unsigned long regbase, u32 max_clk, u32 min_clk, u32 quirks) return add_sdhci(host, 0, min_clk); } + +#else + +DECLARE_GLOBAL_DATA_PTR; + +struct mv_sdhci_plat { + struct mmc_config cfg; + struct mmc mmc; +}; + +static int mv_sdhci_probe(struct udevice *dev) +{ + struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev); + struct mv_sdhci_plat *plat = dev_get_platdata(dev); + struct sdhci_host *host = dev_get_priv(dev); + int ret; + + host->name = MVSDH_NAME; + host->ioaddr = (void *)devfdt_get_addr(dev); + host->quirks = SDHCI_QUIRK_32BIT_DMA_ADDR | SDHCI_QUIRK_WAIT_SEND_CMD; + + ret = sdhci_setup_cfg(&plat->cfg, host, 0, 0); + if (ret) + return ret; + + if (CONFIG_IS_ENABLED(ARCH_MVEBU)) { + /* Configure SDHCI MBUS mbus bridge windows */ + sdhci_mvebu_mbus_config(host->ioaddr); + } + + host->mmc = &plat->mmc; + host->mmc->dev = dev; + host->mmc->priv = host; + upriv->mmc = host->mmc; + + return sdhci_probe(dev); +} + +static int mv_sdhci_bind(struct udevice *dev) +{ + struct mv_sdhci_plat *plat = dev_get_platdata(dev); + + return sdhci_bind(dev, &plat->mmc, &plat->cfg); +} + +static const struct udevice_id mv_sdhci_ids[] = { + { .compatible = "marvell,armada-380-sdhci" }, + { } +}; + +U_BOOT_DRIVER(mv_sdhci_drv) = { + .name = MVSDH_NAME, + .id = UCLASS_MMC, + .of_match = mv_sdhci_ids, + .bind = mv_sdhci_bind, + .probe = mv_sdhci_probe, + .ops = &sdhci_ops, + .priv_auto_alloc_size = sizeof(struct sdhci_host), + .platdata_auto_alloc_size = sizeof(struct mv_sdhci_plat), +}; +#endif /* CONFIG_DM_MMC */ diff --git a/drivers/mmc/rockchip_dw_mmc.c b/drivers/mmc/rockchip_dw_mmc.c index bf2d83a52c..b2a1201631 100644 --- a/drivers/mmc/rockchip_dw_mmc.c +++ b/drivers/mmc/rockchip_dw_mmc.c @@ -13,8 +13,8 @@ #include <pwrseq.h> #include <syscon.h> #include <asm/gpio.h> -#include <asm/arch/clock.h> -#include <asm/arch/periph.h> +#include <asm/arch-rockchip/clock.h> +#include <asm/arch-rockchip/periph.h> #include <linux/err.h> struct rockchip_mmc_plat { diff --git a/drivers/mmc/sdhci.c b/drivers/mmc/sdhci.c index cdeba914f9..e2bb90abbd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -67,17 +67,123 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) } } -static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, - unsigned int start_addr) +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) +static void sdhci_adma_desc(struct sdhci_host *host, char *buf, u16 len, + bool end) +{ + struct sdhci_adma_desc *desc; + u8 attr; + + desc = &host->adma_desc_table[host->desc_slot]; + + attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA; + if (!end) + host->desc_slot++; + else + attr |= ADMA_DESC_ATTR_END; + + desc->attr = attr; + desc->len = len; + desc->reserved = 0; + desc->addr_lo = (dma_addr_t)buf; +#ifdef CONFIG_DMA_ADDR_T_64BIT + desc->addr_hi = (u64)buf >> 32; +#endif +} + +static void sdhci_prepare_adma_table(struct sdhci_host *host, + struct mmc_data *data) +{ + uint trans_bytes = data->blocksize * data->blocks; + uint desc_count = DIV_ROUND_UP(trans_bytes, ADMA_MAX_LEN); + int i = desc_count; + char *buf; + + host->desc_slot = 0; + + if (data->flags & MMC_DATA_READ) + buf = data->dest; + else + buf = (char *)data->src; + + while (--i) { + sdhci_adma_desc(host, buf, ADMA_MAX_LEN, false); + buf += ADMA_MAX_LEN; + trans_bytes -= ADMA_MAX_LEN; + } + + sdhci_adma_desc(host, buf, trans_bytes, true); + + flush_cache((dma_addr_t)host->adma_desc_table, + ROUND(desc_count * sizeof(struct sdhci_adma_desc), + ARCH_DMA_MINALIGN)); +} +#elif defined(CONFIG_MMC_SDHCI_SDMA) +static void sdhci_prepare_adma_table(struct sdhci_host *host, + struct mmc_data *data) +{} +#endif +#if (defined(CONFIG_MMC_SDHCI_SDMA) || CONFIG_IS_ENABLED(MMC_SDHCI_ADMA)) +static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, + int *is_aligned, int trans_bytes) { - unsigned int stat, rdy, mask, timeout, block = 0; - bool transfer_done = false; -#ifdef CONFIG_MMC_SDHCI_SDMA unsigned char ctrl; + + if (data->flags == MMC_DATA_READ) + host->start_addr = (dma_addr_t)data->dest; + else + host->start_addr = (dma_addr_t)data->src; + ctrl = sdhci_readb(host, SDHCI_HOST_CONTROL); ctrl &= ~SDHCI_CTRL_DMA_MASK; + if (host->flags & USE_ADMA64) + ctrl |= SDHCI_CTRL_ADMA64; + else if (host->flags & USE_ADMA) + ctrl |= SDHCI_CTRL_ADMA32; sdhci_writeb(host, ctrl, SDHCI_HOST_CONTROL); + + if (host->flags & USE_SDMA) { + if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && + (host->start_addr & 0x7) != 0x0) { + *is_aligned = 0; + host->start_addr = (unsigned long)aligned_buffer; + if (data->flags != MMC_DATA_READ) + memcpy(aligned_buffer, data->src, trans_bytes); + } + +#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) + /* + * Always use this bounce-buffer when + * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined + */ + *is_aligned = 0; + host->start_addr = (unsigned long)aligned_buffer; + if (data->flags != MMC_DATA_READ) + memcpy(aligned_buffer, data->src, trans_bytes); +#endif + sdhci_writel(host, host->start_addr, SDHCI_DMA_ADDRESS); + + } else if (host->flags & (USE_ADMA | USE_ADMA64)) { + sdhci_prepare_adma_table(host, data); + + sdhci_writel(host, (u32)host->adma_addr, SDHCI_ADMA_ADDRESS); + if (host->flags & USE_ADMA64) + sdhci_writel(host, (u64)host->adma_addr >> 32, + SDHCI_ADMA_ADDRESS_HI); + } + + flush_cache(host->start_addr, ROUND(trans_bytes, ARCH_DMA_MINALIGN)); +} +#else +static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, + int *is_aligned, int trans_bytes) +{} #endif +static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) +{ + dma_addr_t start_addr = host->start_addr; + unsigned int stat, rdy, mask, timeout, block = 0; + bool transfer_done = false; timeout = 1000000; rdy = SDHCI_INT_SPACE_AVAIL | SDHCI_INT_DATA_AVAIL; @@ -104,14 +210,17 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data, continue; } } -#ifdef CONFIG_MMC_SDHCI_SDMA - if (!transfer_done && (stat & SDHCI_INT_DMA_END)) { + if ((host->flags & USE_DMA) && !transfer_done && + (stat & SDHCI_INT_DMA_END)) { sdhci_writel(host, SDHCI_INT_DMA_END, SDHCI_INT_STATUS); - start_addr &= ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); - start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; - sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); + if (host->flags & USE_SDMA) { + start_addr &= + ~(SDHCI_DEFAULT_BOUNDARY_SIZE - 1); + start_addr += SDHCI_DEFAULT_BOUNDARY_SIZE; + sdhci_writel(host, start_addr, + SDHCI_DMA_ADDRESS); + } } -#endif if (timeout-- > 0) udelay(10); else { @@ -149,10 +258,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, int ret = 0; int trans_bytes = 0, is_aligned = 1; u32 mask, flags, mode; - unsigned int time = 0, start_addr = 0; + unsigned int time = 0; int mmc_dev = mmc_get_blk_desc(mmc)->devnum; ulong start = get_timer(0); + host->start_addr = 0; /* Timeout unit - ms */ static unsigned int cmd_timeout = SDHCI_CMD_DEFAULT_TIMEOUT; @@ -218,33 +328,11 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; -#ifdef CONFIG_MMC_SDHCI_SDMA - if (data->flags == MMC_DATA_READ) - start_addr = (unsigned long)data->dest; - else - start_addr = (unsigned long)data->src; - if ((host->quirks & SDHCI_QUIRK_32BIT_DMA_ADDR) && - (start_addr & 0x7) != 0x0) { - is_aligned = 0; - start_addr = (unsigned long)aligned_buffer; - if (data->flags != MMC_DATA_READ) - memcpy(aligned_buffer, data->src, trans_bytes); + if (host->flags & USE_DMA) { + mode |= SDHCI_TRNS_DMA; + sdhci_prepare_dma(host, data, &is_aligned, trans_bytes); } -#if defined(CONFIG_FIXED_SDHCI_ALIGNED_BUFFER) - /* - * Always use this bounce-buffer when - * CONFIG_FIXED_SDHCI_ALIGNED_BUFFER is defined - */ - is_aligned = 0; - start_addr = (unsigned long)aligned_buffer; - if (data->flags != MMC_DATA_READ) - memcpy(aligned_buffer, data->src, trans_bytes); -#endif - - sdhci_writel(host, start_addr, SDHCI_DMA_ADDRESS); - mode |= SDHCI_TRNS_DMA; -#endif sdhci_writew(host, SDHCI_MAKE_BLKSZ(SDHCI_DEFAULT_BOUNDARY_ARG, data->blocksize), SDHCI_BLOCK_SIZE); @@ -255,12 +343,6 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, } sdhci_writel(host, cmd->cmdarg, SDHCI_ARGUMENT); -#ifdef CONFIG_MMC_SDHCI_SDMA - if (data) { - trans_bytes = ALIGN(trans_bytes, CONFIG_SYS_CACHELINE_SIZE); - flush_cache(start_addr, trans_bytes); - } -#endif sdhci_writew(host, SDHCI_MAKE_CMD(cmd->cmdidx, flags), SDHCI_COMMAND); start = get_timer(0); do { @@ -286,7 +368,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, ret = -1; if (!ret && data) - ret = sdhci_transfer_data(host, data, start_addr); + ret = sdhci_transfer_data(host, data); if (host->quirks & SDHCI_QUIRK_WAIT_SEND_CMD) udelay(1000); @@ -570,6 +652,24 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, __func__); return -EINVAL; } + + host->flags |= USE_SDMA; +#endif +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) + if (!(caps & SDHCI_CAN_DO_ADMA2)) { + printf("%s: Your controller doesn't support SDMA!!\n", + __func__); + return -EINVAL; + } + host->adma_desc_table = (struct sdhci_adma_desc *) + memalign(ARCH_DMA_MINALIGN, ADMA_TABLE_SZ); + + host->adma_addr = (dma_addr_t)host->adma_desc_table; +#ifdef CONFIG_DMA_ADDR_T_64BIT + host->flags |= USE_ADMA64; +#else + host->flags |= USE_ADMA; +#endif #endif if (host->quirks & SDHCI_QUIRK_REG32_RW) host->version = diff --git a/drivers/mmc/stm32_sdmmc2.c b/drivers/mmc/stm32_sdmmc2.c index a36612dd93..ed31ca126e 100644 --- a/drivers/mmc/stm32_sdmmc2.c +++ b/drivers/mmc/stm32_sdmmc2.c @@ -190,6 +190,7 @@ struct stm32_sdmmc2_ctx { #define SDMMC_IDMACTRL_IDMAEN BIT(0) #define SDMMC_CMD_TIMEOUT 0xFFFFFFFF +#define SDMMC_BUSYD0END_TIMEOUT_US 1000000 static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv, struct mmc_data *data, @@ -209,9 +210,6 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv, idmabase0 = (u32)data->src; } - /* Set the SDMMC Data TimeOut value */ - writel(SDMMC_CMD_TIMEOUT, priv->base + SDMMC_DTIMER); - /* Set the SDMMC DataLength value */ writel(ctx->data_length, priv->base + SDMMC_DLEN); @@ -236,8 +234,11 @@ static void stm32_sdmmc2_start_data(struct stm32_sdmmc2_priv *priv, } static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv, - struct mmc_cmd *cmd, u32 cmd_param) + struct mmc_cmd *cmd, u32 cmd_param, + struct stm32_sdmmc2_ctx *ctx) { + u32 timeout = 0; + if (readl(priv->base + SDMMC_CMD) & SDMMC_CMD_CPSMEN) writel(0, priv->base + SDMMC_CMD); @@ -251,6 +252,26 @@ static void stm32_sdmmc2_start_cmd(struct stm32_sdmmc2_priv *priv, cmd_param |= SDMMC_CMD_WAITRESP_1; } + /* + * SDMMC_DTIME must be set in two case: + * - on data transfert. + * - on busy request. + * If not done or too short, the dtimeout flag occurs and DPSM stays + * enabled/busy and waits for abort (stop transmission cmd). + * Next data command is not possible whereas DPSM is activated. + */ + if (ctx->data_length) { + timeout = SDMMC_CMD_TIMEOUT; + } else { + writel(0, priv->base + SDMMC_DCTRL); + + if (cmd->resp_type & MMC_RSP_BUSY) + timeout = SDMMC_CMD_TIMEOUT; + } + + /* Set the SDMMC Data TimeOut value */ + writel(timeout, priv->base + SDMMC_DTIMER); + /* Clear flags */ writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR); @@ -309,6 +330,31 @@ static int stm32_sdmmc2_end_cmd(struct stm32_sdmmc2_priv *priv, cmd->response[2] = readl(priv->base + SDMMC_RESP3); cmd->response[3] = readl(priv->base + SDMMC_RESP4); } + + /* Wait for BUSYD0END flag if busy status is detected */ + if (cmd->resp_type & MMC_RSP_BUSY && + status & SDMMC_STA_BUSYD0) { + mask = SDMMC_STA_DTIMEOUT | SDMMC_STA_BUSYD0END; + + /* Polling status register */ + ret = readl_poll_timeout(priv->base + SDMMC_STA, + status, status & mask, + SDMMC_BUSYD0END_TIMEOUT_US); + + if (ret < 0) { + debug("%s: timeout reading SDMMC_STA\n", + __func__); + ctx->dpsm_abort = true; + return ret; + } + + if (status & SDMMC_STA_DTIMEOUT) { + debug("%s: error SDMMC_STA_DTIMEOUT (0x%x)\n", + __func__, status); + ctx->dpsm_abort = true; + return -ETIMEDOUT; + } + } } return 0; @@ -395,7 +441,7 @@ retry_cmd: stm32_sdmmc2_start_data(priv, data, &ctx); } - stm32_sdmmc2_start_cmd(priv, cmd, cmdat); + stm32_sdmmc2_start_cmd(priv, cmd, cmdat, &ctx); debug("%s: send cmd %d data: 0x%x @ 0x%x\n", __func__, cmd->cmdidx, @@ -425,7 +471,10 @@ retry_cmd: debug("%s: send STOP command to abort dpsm treatments\n", __func__); - stm32_sdmmc2_start_cmd(priv, &stop_cmd, SDMMC_CMD_CMDSTOP); + ctx.data_length = 0; + + stm32_sdmmc2_start_cmd(priv, &stop_cmd, + SDMMC_CMD_CMDSTOP, &ctx); stm32_sdmmc2_end_cmd(priv, &stop_cmd, &ctx); writel(SDMMC_ICR_STATIC_FLAGS, priv->base + SDMMC_ICR); @@ -585,11 +634,11 @@ static int stm32_sdmmc2_probe(struct udevice *dev) if (priv->base == FDT_ADDR_T_NONE) return -EINVAL; - if (dev_read_bool(dev, "st,negedge")) + if (dev_read_bool(dev, "st,neg-edge")) priv->clk_reg_msk |= SDMMC_CLKCR_NEGEDGE; - if (dev_read_bool(dev, "st,dirpol")) + if (dev_read_bool(dev, "st,sig-dir")) priv->pwr_reg_msk |= SDMMC_POWER_DIRPOL; - if (dev_read_bool(dev, "st,pin-ckin")) + if (dev_read_bool(dev, "st,use-ckin")) priv->clk_reg_msk |= SDMMC_CLKCR_SELCLKRX_CKIN; ret = clk_get_by_index(dev, 0, &priv->clk); |