aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/mmc/Kconfig8
-rw-r--r--drivers/mmc/fsl_esdhc.c50
-rw-r--r--include/fsl_esdhc.h14
3 files changed, 67 insertions, 5 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 88582db58c9..14d79139864 100644
--- a/drivers/mmc/Kconfig
+++ b/drivers/mmc/Kconfig
@@ -755,6 +755,14 @@ config FSL_ESDHC
This selects support for the eSDHC (Enhanced Secure Digital Host
Controller) found on numerous Freescale/NXP SoCs.
+config FSL_ESDHC_SUPPORT_ADMA2
+ bool "enable ADMA2 support"
+ depends on FSL_ESDHC
+ select MMC_SDHCI_ADMA_HELPERS
+ help
+ This enables support for the ADMA2 transfer mode. If supported by the
+ eSDHC it will allow 64bit DMA addresses.
+
config FSL_ESDHC_33V_IO_RELIABILITY_WORKAROUND
bool "enable eSDHC workaround for 3.3v IO reliability issue"
depends on FSL_ESDHC && DM_MMC
diff --git a/drivers/mmc/fsl_esdhc.c b/drivers/mmc/fsl_esdhc.c
index 15f8175660f..642784e1f35 100644
--- a/drivers/mmc/fsl_esdhc.c
+++ b/drivers/mmc/fsl_esdhc.c
@@ -27,6 +27,7 @@
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/dma-mapping.h>
+#include <sdhci.h>
DECLARE_GLOBAL_DATA_PTR;
@@ -52,8 +53,9 @@ struct fsl_esdhc {
char reserved1[8]; /* reserved */
uint fevt; /* Force event register */
uint admaes; /* ADMA error status register */
- uint adsaddr; /* ADMA system address register */
- char reserved2[160];
+ uint adsaddrl; /* ADMA system address low register */
+ uint adsaddrh; /* ADMA system address high register */
+ char reserved2[156];
uint hostver; /* Host controller version register */
char reserved3[4]; /* reserved */
uint dmaerraddr; /* DMA error address register */
@@ -99,6 +101,7 @@ struct fsl_esdhc_priv {
struct mmc *mmc;
#endif
struct udevice *dev;
+ struct sdhci_adma_desc *adma_desc_table;
dma_addr_t dma_addr;
};
@@ -228,6 +231,7 @@ static void esdhc_setup_dma(struct fsl_esdhc_priv *priv, struct mmc_data *data)
{
uint trans_bytes = data->blocksize * data->blocks;
struct fsl_esdhc *regs = priv->esdhc_regs;
+ phys_addr_t adma_addr;
void *buf;
if (data->flags & MMC_DATA_WRITE)
@@ -237,9 +241,29 @@ static void esdhc_setup_dma(struct fsl_esdhc_priv *priv, struct mmc_data *data)
priv->dma_addr = dma_map_single(buf, trans_bytes,
mmc_get_dma_dir(data));
- if (upper_32_bits(priv->dma_addr))
- printf("Cannot use 64 bit addresses with SDMA\n");
- esdhc_write32(&regs->dsaddr, lower_32_bits(priv->dma_addr));
+
+ if (IS_ENABLED(CONFIG_FSL_ESDHC_SUPPORT_ADMA2) &&
+ priv->adma_desc_table) {
+ debug("Using ADMA2\n");
+ /* prefer ADMA2 if it is available */
+ sdhci_prepare_adma_table(priv->adma_desc_table, data,
+ priv->dma_addr);
+
+ adma_addr = virt_to_phys(priv->adma_desc_table);
+ esdhc_write32(&regs->adsaddrl, lower_32_bits(adma_addr));
+ if (IS_ENABLED(CONFIG_DMA_ADDR_T_64BIT))
+ esdhc_write32(&regs->adsaddrh, upper_32_bits(adma_addr));
+ esdhc_clrsetbits32(&regs->proctl, PROCTL_DMAS_MASK,
+ PROCTL_DMAS_ADMA2);
+ } else {
+ debug("Using SDMA\n");
+ if (upper_32_bits(priv->dma_addr))
+ printf("Cannot use 64 bit addresses with SDMA\n");
+ esdhc_write32(&regs->dsaddr, lower_32_bits(priv->dma_addr));
+ esdhc_clrsetbits32(&regs->proctl, PROCTL_DMAS_MASK,
+ PROCTL_DMAS_SDMA);
+ }
+
esdhc_write32(&regs->blkattr, data->blocks << 16 | data->blocksize);
}
@@ -911,6 +935,7 @@ static int fsl_esdhc_probe(struct udevice *dev)
struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
struct fsl_esdhc_plat *plat = dev_get_platdata(dev);
struct fsl_esdhc_priv *priv = dev_get_priv(dev);
+ u32 caps, hostver;
fdt_addr_t addr;
struct mmc *mmc;
int ret;
@@ -925,6 +950,21 @@ static int fsl_esdhc_probe(struct udevice *dev)
#endif
priv->dev = dev;
+ if (IS_ENABLED(CONFIG_FSL_ESDHC_SUPPORT_ADMA2)) {
+ /*
+ * Only newer eSDHC controllers can do ADMA2 if the ADMA flag
+ * is set in the host capabilities register.
+ */
+ caps = esdhc_read32(&priv->esdhc_regs->hostcapblt);
+ hostver = esdhc_read32(&priv->esdhc_regs->hostver);
+ if (caps & HOSTCAPBLT_DMAS &&
+ HOSTVER_VENDOR(hostver) > VENDOR_V_22) {
+ priv->adma_desc_table = sdhci_adma_init();
+ if (!priv->adma_desc_table)
+ debug("Could not allocate ADMA tables, falling back to SDMA\n");
+ }
+ }
+
if (gd->arch.sdhc_per_clk) {
priv->sdhc_clk = gd->arch.sdhc_per_clk;
priv->is_sdhc_per_clk = true;
diff --git a/include/fsl_esdhc.h b/include/fsl_esdhc.h
index cc119668d6a..e6f1c75e27c 100644
--- a/include/fsl_esdhc.h
+++ b/include/fsl_esdhc.h
@@ -97,6 +97,10 @@
#define PROCTL_DTW_4 0x00000002
#define PROCTL_DTW_8 0x00000004
#define PROCTL_D3CD 0x00000008
+#define PROCTL_DMAS_MASK 0x00000300
+#define PROCTL_DMAS_SDMA 0x00000000
+#define PROCTL_DMAS_ADMA1 0x00000100
+#define PROCTL_DMAS_ADMA2 0x00000300
#define PROCTL_VOLT_SEL 0x00000400
#define CMDARG 0x0002e008
@@ -187,6 +191,16 @@
#define MAX_TUNING_LOOP 40
+#define HOSTVER_VENDOR(x) (((x) >> 8) & 0xff)
+#define VENDOR_V_10 0x00
+#define VENDOR_V_20 0x10
+#define VENDOR_V_21 0x11
+#define VENDOR_V_22 0x12
+#define VENDOR_V_23 0x13
+#define VENDOR_V_30 0x20
+#define VENDOR_V_31 0x21
+#define VENDOR_V_32 0x22
+
struct fsl_esdhc_cfg {
phys_addr_t esdhc_base;
u32 sdhc_clk;