diff options
-rw-r--r-- | drivers/mmc/Kconfig | 14 | ||||
-rw-r--r-- | drivers/mmc/sdhci.c | 138 | ||||
-rw-r--r-- | include/sdhci.h | 41 |
3 files changed, 170 insertions, 23 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig index c34dd5d187..69c983063c 100644 --- a/drivers/mmc/Kconfig +++ b/drivers/mmc/Kconfig @@ -385,6 +385,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/sdhci.c b/drivers/mmc/sdhci.c index c82af1e16e..e2bb90abbd 100644 --- a/drivers/mmc/sdhci.c +++ b/drivers/mmc/sdhci.c @@ -66,7 +66,64 @@ static void sdhci_transfer_pio(struct sdhci_host *host, struct mmc_data *data) sdhci_writel(host, *(u32 *)offs, SDHCI_BUFFER); } } -#ifdef CONFIG_MMC_SDHCI_SDMA + +#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) { @@ -77,29 +134,44 @@ static void sdhci_prepare_dma(struct sdhci_host *host, struct mmc_data *data, else host->start_addr = (dma_addr_t)data->src; - 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); - } - 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); + /* + * 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); + 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 @@ -138,12 +210,16 @@ static int sdhci_transfer_data(struct sdhci_host *host, struct mmc_data *data) continue; } } - if ((host->flags & USE_SDMA) && !transfer_done && + 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); + } } if (timeout-- > 0) udelay(10); @@ -252,7 +328,7 @@ static int sdhci_send_command(struct mmc *mmc, struct mmc_cmd *cmd, if (data->flags == MMC_DATA_READ) mode |= SDHCI_TRNS_READ; - if (host->flags & USE_SDMA) { + if (host->flags & USE_DMA) { mode |= SDHCI_TRNS_DMA; sdhci_prepare_dma(host, data, &is_aligned, trans_bytes); } @@ -579,6 +655,22 @@ int sdhci_setup_cfg(struct mmc_config *cfg, struct sdhci_host *host, 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 = sdhci_readl(host, SDHCI_HOST_VERSION - 2) >> 16; diff --git a/include/sdhci.h b/include/sdhci.h index c66ec6ca25..eee493ab5f 100644 --- a/include/sdhci.h +++ b/include/sdhci.h @@ -186,6 +186,7 @@ /* 55-57 reserved */ #define SDHCI_ADMA_ADDRESS 0x58 +#define SDHCI_ADMA_ADDRESS_HI 0x5c /* 60-FB reserved */ @@ -252,6 +253,38 @@ struct sdhci_ops { void (*set_delay)(struct sdhci_host *host); }; +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) +#define ADMA_MAX_LEN 65532 +#ifdef CONFIG_DMA_ADDR_T_64BIT +#define ADMA_DESC_LEN 16 +#else +#define ADMA_DESC_LEN 8 +#endif +#define ADMA_TABLE_NO_ENTRIES (CONFIG_SYS_MMC_MAX_BLK_COUNT * \ + MMC_MAX_BLOCK_LEN) / ADMA_MAX_LEN + +#define ADMA_TABLE_SZ (ADMA_TABLE_NO_ENTRIES * ADMA_DESC_LEN) + +/* Decriptor table defines */ +#define ADMA_DESC_ATTR_VALID BIT(0) +#define ADMA_DESC_ATTR_END BIT(1) +#define ADMA_DESC_ATTR_INT BIT(2) +#define ADMA_DESC_ATTR_ACT1 BIT(4) +#define ADMA_DESC_ATTR_ACT2 BIT(5) + +#define ADMA_DESC_TRANSFER_DATA ADMA_DESC_ATTR_ACT2 +#define ADMA_DESC_LINK_DESC (ADMA_DESC_ATTR_ACT1 | ADMA_DESC_ATTR_ACT2) + +struct sdhci_adma_desc { + u8 attr; + u8 reserved; + u16 len; + u32 addr_lo; +#ifdef CONFIG_DMA_ADDR_T_64BIT + u32 addr_hi; +#endif +} __packed; +#endif struct sdhci_host { const char *name; void *ioaddr; @@ -275,6 +308,14 @@ struct sdhci_host { dma_addr_t start_addr; int flags; #define USE_SDMA (0x1 << 0) +#define USE_ADMA (0x1 << 1) +#define USE_ADMA64 (0x1 << 2) +#define USE_DMA (USE_SDMA | USE_ADMA | USE_ADMA64) + dma_addr_t adma_addr; +#if CONFIG_IS_ENABLED(MMC_SDHCI_ADMA) + struct sdhci_adma_desc *adma_desc_table; + uint desc_slot; +#endif }; #ifdef CONFIG_MMC_SDHCI_IO_ACCESSORS |