summaryrefslogtreecommitdiff
path: root/drivers/mmc
diff options
context:
space:
mode:
authorMichael Walle <michael@walle.cc>2020-10-12 11:07:14 +0300
committerPeng Fan <peng.fan@nxp.com>2020-10-14 09:00:44 +0300
commit361a422b905052dcbcba61fc1e0d8d804fdca433 (patch)
tree2a6feb8bed00d12d568db596f276263ea3a854ad /drivers/mmc
parent52faec31827ec1a1837977e29c067424426634c5 (diff)
downloadu-boot-361a422b905052dcbcba61fc1e0d8d804fdca433.tar.xz
mmc: fsl_esdhc: add ADMA2 support
Newer eSDHC controllers support ADMA2 descriptor tables which support 64bit DMA addresses. One notable user of addresses in the upper memory segment is the EFI loader. If support is enabled, but the controller doesn't support ADMA2, we will fall back to SDMA (and thus 32 bit DMA addresses only). Signed-off-by: Michael Walle <michael@walle.cc>
Diffstat (limited to 'drivers/mmc')
-rw-r--r--drivers/mmc/Kconfig8
-rw-r--r--drivers/mmc/fsl_esdhc.c50
2 files changed, 53 insertions, 5 deletions
diff --git a/drivers/mmc/Kconfig b/drivers/mmc/Kconfig
index 88582db58c..14d7913986 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 15f8175660..642784e1f3 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;