[3/5] mmc: omap_hsmmc: Add support for DMA (ADMA2)

Message ID 1499790014-29964-4-git-send-email-jjhiblot@ti.com
State New
Headers show
Series
  • Untitled series #2783
Related show

Commit Message

Jean-Jacques Hiblot July 11, 2017, 4:20 p.m.
From: Kishon Vijay Abraham I <kishon@ti.com>

The omap hsmmc host controller can have the ADMA2 feature. It brings better
read and write throughput.
On most SOC, the capability is read from the hl_hwinfo register. On OMAP3,
DMA support is compiled out.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
---
 arch/arm/include/asm/omap_mmc.h |  12 ++-
 drivers/mmc/omap_hsmmc.c        | 186 +++++++++++++++++++++++++++++++++++++++-
 2 files changed, 195 insertions(+), 3 deletions(-)

Comments

Jaehoon Chung July 28, 2017, 12:33 p.m. | #1
Hi JJ,

On 07/12/2017 01:20 AM, Jean-Jacques Hiblot wrote:
> From: Kishon Vijay Abraham I <kishon@ti.com>
> 
> The omap hsmmc host controller can have the ADMA2 feature. It brings better
> read and write throughput.
> On most SOC, the capability is read from the hl_hwinfo register. On OMAP3,
> DMA support is compiled out.

There is a conflict...but if you're ok, i will apply after fixing it..
If you don't want it, let me know, plz.

Best Regards,
Jaehoon Chung

> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
> ---
>  arch/arm/include/asm/omap_mmc.h |  12 ++-
>  drivers/mmc/omap_hsmmc.c        | 186 +++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 195 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h
> index 2b489a4..206badb 100644
> --- a/arch/arm/include/asm/omap_mmc.h
> +++ b/arch/arm/include/asm/omap_mmc.h
> @@ -29,7 +29,10 @@
>  
>  struct hsmmc {
>  #ifndef CONFIG_OMAP34XX
> -	unsigned char res0[0x100];
> +	unsigned int hl_rev;
> +	unsigned int hl_hwinfo;
> +	unsigned int hl_sysconfig;
> +	unsigned char res0[0xf4];
>  #endif
>  	unsigned char res1[0x10];
>  	unsigned int sysconfig;		/* 0x10 */
> @@ -52,6 +55,9 @@ struct hsmmc {
>  	unsigned int ie;		/* 0x134 */
>  	unsigned char res4[0x8];
>  	unsigned int capa;		/* 0x140 */
> +	unsigned char res5[0x10];
> +	unsigned int admaes;		/* 0x154 */
> +	unsigned int admasal;		/* 0x158 */
>  };
>  
>  struct omap_hsmmc_plat {
> @@ -64,6 +70,7 @@ struct omap_hsmmc_plat {
>  /*
>   * OMAP HS MMC Bit definitions
>   */
> +#define MADMA_EN			(0x1 << 0)
>  #define MMC_SOFTRESET			(0x1 << 1)
>  #define RESETDONE			(0x1 << 0)
>  #define NOOPENDRAIN			(0x0 << 0)
> @@ -80,6 +87,7 @@ struct omap_hsmmc_plat {
>  #define WPP_ACTIVEHIGH			(0x0 << 8)
>  #define RESERVED_MASK			(0x3 << 9)
>  #define CTPL_MMC_SD			(0x0 << 11)
> +#define DMA_MASTER			(0x1 << 20)
>  #define BLEN_512BYTESLEN		(0x200 << 0)
>  #define NBLK_STPCNT			(0x0 << 16)
>  #define DE_DISABLE			(0x0 << 0)
> @@ -119,6 +127,7 @@ struct omap_hsmmc_plat {
>  #define SDBP_PWRON			(0x1 << 8)
>  #define SDVS_1V8			(0x5 << 9)
>  #define SDVS_3V0			(0x6 << 9)
> +#define DMA_SELECT			(0x2 << 3)
>  #define ICE_MASK			(0x1 << 0)
>  #define ICE_STOP			(0x0 << 0)
>  #define ICS_MASK			(0x1 << 1)
> @@ -148,6 +157,7 @@ struct omap_hsmmc_plat {
>  #define IE_DTO				(0x01 << 20)
>  #define IE_DCRC				(0x01 << 21)
>  #define IE_DEB				(0x01 << 22)
> +#define IE_ADMAE			(0x01 << 25)
>  #define IE_CERR				(0x01 << 28)
>  #define IE_BADA				(0x01 << 29)
>  
> diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
> index 2c1e429..3419dc5 100644
> --- a/drivers/mmc/omap_hsmmc.c
> +++ b/drivers/mmc/omap_hsmmc.c
> @@ -25,6 +25,7 @@
>  #include <config.h>
>  #include <common.h>
>  #include <malloc.h>
> +#include <memalign.h>
>  #include <mmc.h>
>  #include <part.h>
>  #include <i2c.h>
> @@ -71,10 +72,37 @@ struct omap_hsmmc_data {
>  	int wp_gpio;
>  #endif
>  #endif
> +	u8 controller_flags;
> +#ifndef CONFIG_OMAP34XX
> +	struct omap_hsmmc_adma_desc *adma_desc_table;
> +	uint desc_slot;
> +#endif
> +};
> +
> +#ifndef CONFIG_OMAP34XX
> +struct omap_hsmmc_adma_desc {
> +	u8 attr;
> +	u8 reserved;
> +	u16 len;
> +	u32 addr;
>  };
>  
> +#define ADMA_MAX_LEN	63488
> +
> +/* 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)
> +#endif
> +
>  /* If we fail after 1 second wait, something is really bad */
>  #define MAX_RETRY_MS	1000
> +#define OMAP_HSMMC_USE_ADMA			BIT(2)
>  
>  static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
>  static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
> @@ -242,6 +270,11 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>  			return -ETIMEDOUT;
>  		}
>  	}
> +#ifndef CONFIG_OMAP34XX
> +	reg_val = readl(&mmc_base->hl_hwinfo);
> +	if (reg_val & MADMA_EN)
> +		priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
> +#endif
>  	writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
>  	writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
>  		&mmc_base->capa);
> @@ -269,8 +302,8 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>  	writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
>  
>  	writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
> -		IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
> -		&mmc_base->ie);
> +		IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
> +		IE_CC, &mmc_base->ie);
>  
>  	mmc_init_stream(mmc_base);
>  
> @@ -322,6 +355,123 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
>  		}
>  	}
>  }
> +
> +#ifndef CONFIG_OMAP34XX
> +static int omap_hsmmc_adma_desc(struct mmc *mmc, char *buf, u16 len, bool end)
> +{
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	struct omap_hsmmc_adma_desc *desc;
> +	u8 attr;
> +
> +	desc = &priv->adma_desc_table[priv->desc_slot];
> +
> +	attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
> +	if (!end)
> +		priv->desc_slot++;
> +	else
> +		attr |= ADMA_DESC_ATTR_END;
> +
> +	desc->len = len;
> +	desc->addr = (u32)buf;
> +	desc->reserved = 0;
> +	desc->attr = attr;
> +
> +	return 0;
> +}
> +
> +static int omap_hsmmc_prepare_adma_table(struct mmc *mmc, struct mmc_data *data)
> +{
> +	uint total_len = data->blocksize * data->blocks;
> +	uint desc_count = DIV_ROUND_UP(total_len, ADMA_MAX_LEN);
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	int i = desc_count;
> +	char *buf;
> +
> +	priv->desc_slot = 0;
> +	priv->adma_desc_table = (struct omap_hsmmc_adma_desc *)
> +				memalign(ARCH_DMA_MINALIGN, desc_count *
> +				sizeof(struct omap_hsmmc_adma_desc));
> +
> +	if (data->flags & MMC_DATA_READ)
> +		buf = data->dest;
> +	else
> +		buf = (char *)data->src;
> +
> +	while (--i) {
> +		omap_hsmmc_adma_desc(mmc, buf, ADMA_MAX_LEN, false);
> +		buf += ADMA_MAX_LEN;
> +		total_len -= ADMA_MAX_LEN;
> +	}
> +
> +	omap_hsmmc_adma_desc(mmc, buf, total_len, true);
> +
> +	flush_dcache_range((long)priv->adma_desc_table,
> +			   (long)priv->adma_desc_table +
> +			   ROUND(desc_count *
> +			   sizeof(struct omap_hsmmc_adma_desc),
> +			   ARCH_DMA_MINALIGN));
> +	return 0;
> +}
> +
> +static void omap_hsmmc_prepare_data(struct mmc *mmc, struct mmc_data *data)
> +{
> +	struct hsmmc *mmc_base;
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	u32 val;
> +	char *buf;
> +
> +	mmc_base = priv->base_addr;
> +	omap_hsmmc_prepare_adma_table(mmc, data);
> +
> +	if (data->flags & MMC_DATA_READ)
> +		buf = data->dest;
> +	else
> +		buf = (char *)data->src;
> +
> +	val = readl(&mmc_base->hctl);
> +	val |= DMA_SELECT;
> +	writel(val, &mmc_base->hctl);
> +
> +	val = readl(&mmc_base->con);
> +	val |= DMA_MASTER;
> +	writel(val, &mmc_base->con);
> +
> +	writel((u32)priv->adma_desc_table, &mmc_base->admasal);
> +
> +	/* TODO: This shouldn't be required for read. However I don't seem
> +	 * to get valid data without this.
> +	 */
> +	flush_dcache_range((u32)buf,
> +			   (u32)buf +
> +			   ROUND(data->blocksize * data->blocks,
> +			   ARCH_DMA_MINALIGN));
> +}
> +
> +static void omap_hsmmc_dma_cleanup(struct mmc *mmc)
> +{
> +	struct hsmmc *mmc_base;
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	u32 val;
> +
> +	mmc_base = priv->base_addr;
> +
> +	val = readl(&mmc_base->con);
> +	val &= ~DMA_MASTER;
> +	writel(val, &mmc_base->con);
> +
> +	val = readl(&mmc_base->hctl);
> +	val &= ~DMA_SELECT;
> +	writel(val, &mmc_base->hctl);
> +
> +	kfree(priv->adma_desc_table);
> +}
> +#else
> +#define omap_hsmmc_adma_desc
> +#define omap_hsmmc_prepare_adma_table
> +#define omap_hsmmc_prepare_data
> +#define omap_hsmmc_dma_cleanup
> +#endif
> +
>  #ifndef CONFIG_DM_MMC
>  static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
>  			struct mmc_data *data)
> @@ -332,6 +482,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  			struct mmc_data *data)
>  {
>  	struct omap_hsmmc_data *priv = dev_get_priv(dev);
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct mmc *mmc = upriv->mmc;
>  #endif
>  	struct hsmmc *mmc_base;
>  	unsigned int flags, mmc_stat;
> @@ -405,6 +557,14 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  			flags |= (DP_DATA | DDIR_READ);
>  		else
>  			flags |= (DP_DATA | DDIR_WRITE);
> +
> +#ifndef CONFIG_OMAP34XX
> +		if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) &&
> +		    !mmc_is_tuning_cmd(cmd->cmdidx)) {
> +			omap_hsmmc_prepare_data(mmc, data);
> +			flags |= DE_ENABLE;
> +		}
> +#endif
>  	}
>  
>  	writel(cmd->cmdarg, &mmc_base->arg);
> @@ -441,6 +601,28 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  		}
>  	}
>  
> +#ifndef CONFIG_OMAP34XX
> +	if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) && data &&
> +	    !mmc_is_tuning_cmd(cmd->cmdidx)) {
> +		if (mmc_stat & IE_ADMAE) {
> +			omap_hsmmc_dma_cleanup(mmc);
> +			return -1;
> +		}
> +
> +		do {
> +			mmc_stat = readl(&mmc_base->stat);
> +			if (mmc_stat & TC_MASK) {
> +				writel(readl(&mmc_base->stat) | TC_MASK,
> +				       &mmc_base->stat);
> +				break;
> +			}
> +		} while (1);
> +
> +		omap_hsmmc_dma_cleanup(mmc);
> +		return 0;
> +	}
> +#endif
> +
>  	if (data && (data->flags & MMC_DATA_READ)) {
>  		mmc_read_data(mmc_base,	data->dest,
>  				data->blocksize * data->blocks);
>
Jaehoon Chung July 28, 2017, 12:46 p.m. | #2
Hi JJ

On 07/12/2017 01:20 AM, Jean-Jacques Hiblot wrote:
> From: Kishon Vijay Abraham I <kishon@ti.com>
> 
> The omap hsmmc host controller can have the ADMA2 feature. It brings better
> read and write throughput.
> On most SOC, the capability is read from the hl_hwinfo register. On OMAP3,
> DMA support is compiled out.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>

Sorry..i added the some comment at below.

> ---
>  arch/arm/include/asm/omap_mmc.h |  12 ++-
>  drivers/mmc/omap_hsmmc.c        | 186 +++++++++++++++++++++++++++++++++++++++-
>  2 files changed, 195 insertions(+), 3 deletions(-)
> 
> diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h
> index 2b489a4..206badb 100644
> --- a/arch/arm/include/asm/omap_mmc.h
> +++ b/arch/arm/include/asm/omap_mmc.h
> @@ -29,7 +29,10 @@
>  
>  struct hsmmc {
>  #ifndef CONFIG_OMAP34XX
> -	unsigned char res0[0x100];
> +	unsigned int hl_rev;
> +	unsigned int hl_hwinfo;
> +	unsigned int hl_sysconfig;
> +	unsigned char res0[0xf4];
>  #endif
>  	unsigned char res1[0x10];
>  	unsigned int sysconfig;		/* 0x10 */
> @@ -52,6 +55,9 @@ struct hsmmc {
>  	unsigned int ie;		/* 0x134 */
>  	unsigned char res4[0x8];
>  	unsigned int capa;		/* 0x140 */
> +	unsigned char res5[0x10];
> +	unsigned int admaes;		/* 0x154 */
> +	unsigned int admasal;		/* 0x158 */
>  };
>  
>  struct omap_hsmmc_plat {
> @@ -64,6 +70,7 @@ struct omap_hsmmc_plat {
>  /*
>   * OMAP HS MMC Bit definitions
>   */
> +#define MADMA_EN			(0x1 << 0)
>  #define MMC_SOFTRESET			(0x1 << 1)
>  #define RESETDONE			(0x1 << 0)
>  #define NOOPENDRAIN			(0x0 << 0)
> @@ -80,6 +87,7 @@ struct omap_hsmmc_plat {
>  #define WPP_ACTIVEHIGH			(0x0 << 8)
>  #define RESERVED_MASK			(0x3 << 9)
>  #define CTPL_MMC_SD			(0x0 << 11)
> +#define DMA_MASTER			(0x1 << 20)
>  #define BLEN_512BYTESLEN		(0x200 << 0)
>  #define NBLK_STPCNT			(0x0 << 16)
>  #define DE_DISABLE			(0x0 << 0)
> @@ -119,6 +127,7 @@ struct omap_hsmmc_plat {
>  #define SDBP_PWRON			(0x1 << 8)
>  #define SDVS_1V8			(0x5 << 9)
>  #define SDVS_3V0			(0x6 << 9)
> +#define DMA_SELECT			(0x2 << 3)
>  #define ICE_MASK			(0x1 << 0)
>  #define ICE_STOP			(0x0 << 0)
>  #define ICS_MASK			(0x1 << 1)
> @@ -148,6 +157,7 @@ struct omap_hsmmc_plat {
>  #define IE_DTO				(0x01 << 20)
>  #define IE_DCRC				(0x01 << 21)
>  #define IE_DEB				(0x01 << 22)
> +#define IE_ADMAE			(0x01 << 25)
>  #define IE_CERR				(0x01 << 28)
>  #define IE_BADA				(0x01 << 29)
>  
> diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
> index 2c1e429..3419dc5 100644
> --- a/drivers/mmc/omap_hsmmc.c
> +++ b/drivers/mmc/omap_hsmmc.c
> @@ -25,6 +25,7 @@
>  #include <config.h>
>  #include <common.h>
>  #include <malloc.h>
> +#include <memalign.h>
>  #include <mmc.h>
>  #include <part.h>
>  #include <i2c.h>
> @@ -71,10 +72,37 @@ struct omap_hsmmc_data {
>  	int wp_gpio;
>  #endif
>  #endif
> +	u8 controller_flags;
> +#ifndef CONFIG_OMAP34XX
> +	struct omap_hsmmc_adma_desc *adma_desc_table;
> +	uint desc_slot;
> +#endif
> +};
> +
> +#ifndef CONFIG_OMAP34XX
> +struct omap_hsmmc_adma_desc {
> +	u8 attr;
> +	u8 reserved;
> +	u16 len;
> +	u32 addr;
>  };
>  
> +#define ADMA_MAX_LEN	63488
> +
> +/* 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)
> +#endif
> +
>  /* If we fail after 1 second wait, something is really bad */
>  #define MAX_RETRY_MS	1000
> +#define OMAP_HSMMC_USE_ADMA			BIT(2)
>  
>  static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
>  static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
> @@ -242,6 +270,11 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>  			return -ETIMEDOUT;
>  		}
>  	}
> +#ifndef CONFIG_OMAP34XX
> +	reg_val = readl(&mmc_base->hl_hwinfo);
> +	if (reg_val & MADMA_EN)
> +		priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
> +#endif
>  	writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
>  	writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
>  		&mmc_base->capa);
> @@ -269,8 +302,8 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>  	writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
>  
>  	writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
> -		IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
> -		&mmc_base->ie);
> +		IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
> +		IE_CC, &mmc_base->ie);
>  
>  	mmc_init_stream(mmc_base);
>  
> @@ -322,6 +355,123 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
>  		}
>  	}
>  }
> +
> +#ifndef CONFIG_OMAP34XX
> +static int omap_hsmmc_adma_desc(struct mmc *mmc, char *buf, u16 len, bool end)

Can be void..?

> +{
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	struct omap_hsmmc_adma_desc *desc;
> +	u8 attr;
> +
> +	desc = &priv->adma_desc_table[priv->desc_slot];
> +
> +	attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
> +	if (!end)
> +		priv->desc_slot++;
> +	else
> +		attr |= ADMA_DESC_ATTR_END;
> +
> +	desc->len = len;
> +	desc->addr = (u32)buf;
> +	desc->reserved = 0;
> +	desc->attr = attr;
> +
> +	return 0;
> +}
> +
> +static int omap_hsmmc_prepare_adma_table(struct mmc *mmc, struct mmc_data *data)

Ditto.

> +{
> +	uint total_len = data->blocksize * data->blocks;
> +	uint desc_count = DIV_ROUND_UP(total_len, ADMA_MAX_LEN);
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	int i = desc_count;
> +	char *buf;
> +
> +	priv->desc_slot = 0;
> +	priv->adma_desc_table = (struct omap_hsmmc_adma_desc *)
> +				memalign(ARCH_DMA_MINALIGN, desc_count *
> +				sizeof(struct omap_hsmmc_adma_desc));
> +
> +	if (data->flags & MMC_DATA_READ)
> +		buf = data->dest;
> +	else
> +		buf = (char *)data->src;
> +
> +	while (--i) {
> +		omap_hsmmc_adma_desc(mmc, buf, ADMA_MAX_LEN, false);
> +		buf += ADMA_MAX_LEN;
> +		total_len -= ADMA_MAX_LEN;
> +	}
> +
> +	omap_hsmmc_adma_desc(mmc, buf, total_len, true);
> +
> +	flush_dcache_range((long)priv->adma_desc_table,
> +			   (long)priv->adma_desc_table +
> +			   ROUND(desc_count *
> +			   sizeof(struct omap_hsmmc_adma_desc),
> +			   ARCH_DMA_MINALIGN));
> +	return 0;
> +}
> +
> +static void omap_hsmmc_prepare_data(struct mmc *mmc, struct mmc_data *data)
> +{
> +	struct hsmmc *mmc_base;
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	u32 val;
> +	char *buf;
> +
> +	mmc_base = priv->base_addr;
> +	omap_hsmmc_prepare_adma_table(mmc, data);
> +
> +	if (data->flags & MMC_DATA_READ)
> +		buf = data->dest;
> +	else
> +		buf = (char *)data->src;
> +
> +	val = readl(&mmc_base->hctl);
> +	val |= DMA_SELECT;
> +	writel(val, &mmc_base->hctl);
> +
> +	val = readl(&mmc_base->con);
> +	val |= DMA_MASTER;
> +	writel(val, &mmc_base->con);
> +
> +	writel((u32)priv->adma_desc_table, &mmc_base->admasal);
> +
> +	/* TODO: This shouldn't be required for read. However I don't seem
> +	 * to get valid data without this.

TODO? not FIXME?

> +	 */
> +	flush_dcache_range((u32)buf,
> +			   (u32)buf +
> +			   ROUND(data->blocksize * data->blocks,
> +			   ARCH_DMA_MINALIGN));
> +}
> +
> +static void omap_hsmmc_dma_cleanup(struct mmc *mmc)
> +{
> +	struct hsmmc *mmc_base;
> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
> +	u32 val;
> +
> +	mmc_base = priv->base_addr;
> +
> +	val = readl(&mmc_base->con);
> +	val &= ~DMA_MASTER;
> +	writel(val, &mmc_base->con);
> +
> +	val = readl(&mmc_base->hctl);
> +	val &= ~DMA_SELECT;
> +	writel(val, &mmc_base->hctl);
> +
> +	kfree(priv->adma_desc_table);
> +}
> +#else
> +#define omap_hsmmc_adma_desc
> +#define omap_hsmmc_prepare_adma_table
> +#define omap_hsmmc_prepare_data
> +#define omap_hsmmc_dma_cleanup
> +#endif
> +
>  #ifndef CONFIG_DM_MMC
>  static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
>  			struct mmc_data *data)
> @@ -332,6 +482,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  			struct mmc_data *data)
>  {
>  	struct omap_hsmmc_data *priv = dev_get_priv(dev);
> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
> +	struct mmc *mmc = upriv->mmc;
>  #endif
>  	struct hsmmc *mmc_base;
>  	unsigned int flags, mmc_stat;
> @@ -405,6 +557,14 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  			flags |= (DP_DATA | DDIR_READ);
>  		else
>  			flags |= (DP_DATA | DDIR_WRITE);
> +
> +#ifndef CONFIG_OMAP34XX
> +		if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) &&
> +		    !mmc_is_tuning_cmd(cmd->cmdidx)) {
> +			omap_hsmmc_prepare_data(mmc, data);
> +			flags |= DE_ENABLE;
> +		}
> +#endif
>  	}
>  
>  	writel(cmd->cmdarg, &mmc_base->arg);
> @@ -441,6 +601,28 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>  		}
>  	}
>  
> +#ifndef CONFIG_OMAP34XX
> +	if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) && data &&
> +	    !mmc_is_tuning_cmd(cmd->cmdidx)) {
> +		if (mmc_stat & IE_ADMAE) {
> +			omap_hsmmc_dma_cleanup(mmc);
> +			return -1;

Use the valid error number.

> +		}
> +
> +		do {
> +			mmc_stat = readl(&mmc_base->stat);
> +			if (mmc_stat & TC_MASK) {
> +				writel(readl(&mmc_base->stat) | TC_MASK,
> +				       &mmc_base->stat);
> +				break;
> +			}
> +		} while (1);

Can you add the timeout? There is a potential infinte loop.

> +
> +		omap_hsmmc_dma_cleanup(mmc);
> +		return 0;
> +	}
> +#endif
> +
>  	if (data && (data->flags & MMC_DATA_READ)) {
>  		mmc_read_data(mmc_base,	data->dest,
>  				data->blocksize * data->blocks);
>
Jean-Jacques Hiblot July 28, 2017, 3:40 p.m. | #3
On 28/07/2017 14:46, Jaehoon Chung wrote:
> Hi JJ
>
> On 07/12/2017 01:20 AM, Jean-Jacques Hiblot wrote:
>> From: Kishon Vijay Abraham I <kishon@ti.com>
>>
>> The omap hsmmc host controller can have the ADMA2 feature. It brings better
>> read and write throughput.
>> On most SOC, the capability is read from the hl_hwinfo register. On OMAP3,
>> DMA support is compiled out.
>>
>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>> Signed-off-by: Jean-Jacques Hiblot <jjhiblot@ti.com>
> Sorry..i added the some comment at below.
>
>> ---
>>   arch/arm/include/asm/omap_mmc.h |  12 ++-
>>   drivers/mmc/omap_hsmmc.c        | 186 +++++++++++++++++++++++++++++++++++++++-
>>   2 files changed, 195 insertions(+), 3 deletions(-)
>>
>> diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h
>> index 2b489a4..206badb 100644
>> --- a/arch/arm/include/asm/omap_mmc.h
>> +++ b/arch/arm/include/asm/omap_mmc.h
>> @@ -29,7 +29,10 @@
>>   
>>   struct hsmmc {
>>   #ifndef CONFIG_OMAP34XX
>> -	unsigned char res0[0x100];
>> +	unsigned int hl_rev;
>> +	unsigned int hl_hwinfo;
>> +	unsigned int hl_sysconfig;
>> +	unsigned char res0[0xf4];
>>   #endif
>>   	unsigned char res1[0x10];
>>   	unsigned int sysconfig;		/* 0x10 */
>> @@ -52,6 +55,9 @@ struct hsmmc {
>>   	unsigned int ie;		/* 0x134 */
>>   	unsigned char res4[0x8];
>>   	unsigned int capa;		/* 0x140 */
>> +	unsigned char res5[0x10];
>> +	unsigned int admaes;		/* 0x154 */
>> +	unsigned int admasal;		/* 0x158 */
>>   };
>>   
>>   struct omap_hsmmc_plat {
>> @@ -64,6 +70,7 @@ struct omap_hsmmc_plat {
>>   /*
>>    * OMAP HS MMC Bit definitions
>>    */
>> +#define MADMA_EN			(0x1 << 0)
>>   #define MMC_SOFTRESET			(0x1 << 1)
>>   #define RESETDONE			(0x1 << 0)
>>   #define NOOPENDRAIN			(0x0 << 0)
>> @@ -80,6 +87,7 @@ struct omap_hsmmc_plat {
>>   #define WPP_ACTIVEHIGH			(0x0 << 8)
>>   #define RESERVED_MASK			(0x3 << 9)
>>   #define CTPL_MMC_SD			(0x0 << 11)
>> +#define DMA_MASTER			(0x1 << 20)
>>   #define BLEN_512BYTESLEN		(0x200 << 0)
>>   #define NBLK_STPCNT			(0x0 << 16)
>>   #define DE_DISABLE			(0x0 << 0)
>> @@ -119,6 +127,7 @@ struct omap_hsmmc_plat {
>>   #define SDBP_PWRON			(0x1 << 8)
>>   #define SDVS_1V8			(0x5 << 9)
>>   #define SDVS_3V0			(0x6 << 9)
>> +#define DMA_SELECT			(0x2 << 3)
>>   #define ICE_MASK			(0x1 << 0)
>>   #define ICE_STOP			(0x0 << 0)
>>   #define ICS_MASK			(0x1 << 1)
>> @@ -148,6 +157,7 @@ struct omap_hsmmc_plat {
>>   #define IE_DTO				(0x01 << 20)
>>   #define IE_DCRC				(0x01 << 21)
>>   #define IE_DEB				(0x01 << 22)
>> +#define IE_ADMAE			(0x01 << 25)
>>   #define IE_CERR				(0x01 << 28)
>>   #define IE_BADA				(0x01 << 29)
>>   
>> diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
>> index 2c1e429..3419dc5 100644
>> --- a/drivers/mmc/omap_hsmmc.c
>> +++ b/drivers/mmc/omap_hsmmc.c
>> @@ -25,6 +25,7 @@
>>   #include <config.h>
>>   #include <common.h>
>>   #include <malloc.h>
>> +#include <memalign.h>
>>   #include <mmc.h>
>>   #include <part.h>
>>   #include <i2c.h>
>> @@ -71,10 +72,37 @@ struct omap_hsmmc_data {
>>   	int wp_gpio;
>>   #endif
>>   #endif
>> +	u8 controller_flags;
>> +#ifndef CONFIG_OMAP34XX
>> +	struct omap_hsmmc_adma_desc *adma_desc_table;
>> +	uint desc_slot;
>> +#endif
>> +};
>> +
>> +#ifndef CONFIG_OMAP34XX
>> +struct omap_hsmmc_adma_desc {
>> +	u8 attr;
>> +	u8 reserved;
>> +	u16 len;
>> +	u32 addr;
>>   };
>>   
>> +#define ADMA_MAX_LEN	63488
>> +
>> +/* 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)
>> +#endif
>> +
>>   /* If we fail after 1 second wait, something is really bad */
>>   #define MAX_RETRY_MS	1000
>> +#define OMAP_HSMMC_USE_ADMA			BIT(2)
>>   
>>   static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
>>   static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
>> @@ -242,6 +270,11 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>>   			return -ETIMEDOUT;
>>   		}
>>   	}
>> +#ifndef CONFIG_OMAP34XX
>> +	reg_val = readl(&mmc_base->hl_hwinfo);
>> +	if (reg_val & MADMA_EN)
>> +		priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
>> +#endif
>>   	writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
>>   	writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
>>   		&mmc_base->capa);
>> @@ -269,8 +302,8 @@ static int omap_hsmmc_init_setup(struct mmc *mmc)
>>   	writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
>>   
>>   	writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
>> -		IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
>> -		&mmc_base->ie);
>> +		IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
>> +		IE_CC, &mmc_base->ie);
>>   
>>   	mmc_init_stream(mmc_base);
>>   
>> @@ -322,6 +355,123 @@ static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
>>   		}
>>   	}
>>   }
>> +
>> +#ifndef CONFIG_OMAP34XX
>> +static int omap_hsmmc_adma_desc(struct mmc *mmc, char *buf, u16 len, bool end)
> Can be void..?
yes indeed. I'll fix that.
>
>> +{
>> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
>> +	struct omap_hsmmc_adma_desc *desc;
>> +	u8 attr;
>> +
>> +	desc = &priv->adma_desc_table[priv->desc_slot];
>> +
>> +	attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
>> +	if (!end)
>> +		priv->desc_slot++;
>> +	else
>> +		attr |= ADMA_DESC_ATTR_END;
>> +
>> +	desc->len = len;
>> +	desc->addr = (u32)buf;
>> +	desc->reserved = 0;
>> +	desc->attr = attr;
>> +
>> +	return 0;
>> +}
>> +
>> +static int omap_hsmmc_prepare_adma_table(struct mmc *mmc, struct mmc_data *data)
> Ditto.
>
>> +{
>> +	uint total_len = data->blocksize * data->blocks;
>> +	uint desc_count = DIV_ROUND_UP(total_len, ADMA_MAX_LEN);
>> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
>> +	int i = desc_count;
>> +	char *buf;
>> +
>> +	priv->desc_slot = 0;
>> +	priv->adma_desc_table = (struct omap_hsmmc_adma_desc *)
>> +				memalign(ARCH_DMA_MINALIGN, desc_count *
>> +				sizeof(struct omap_hsmmc_adma_desc));
>> +
>> +	if (data->flags & MMC_DATA_READ)
>> +		buf = data->dest;
>> +	else
>> +		buf = (char *)data->src;
>> +
>> +	while (--i) {
>> +		omap_hsmmc_adma_desc(mmc, buf, ADMA_MAX_LEN, false);
>> +		buf += ADMA_MAX_LEN;
>> +		total_len -= ADMA_MAX_LEN;
>> +	}
>> +
>> +	omap_hsmmc_adma_desc(mmc, buf, total_len, true);
>> +
>> +	flush_dcache_range((long)priv->adma_desc_table,
>> +			   (long)priv->adma_desc_table +
>> +			   ROUND(desc_count *
>> +			   sizeof(struct omap_hsmmc_adma_desc),
>> +			   ARCH_DMA_MINALIGN));
>> +	return 0;
>> +}
>> +
>> +static void omap_hsmmc_prepare_data(struct mmc *mmc, struct mmc_data *data)
>> +{
>> +	struct hsmmc *mmc_base;
>> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
>> +	u32 val;
>> +	char *buf;
>> +
>> +	mmc_base = priv->base_addr;
>> +	omap_hsmmc_prepare_adma_table(mmc, data);
>> +
>> +	if (data->flags & MMC_DATA_READ)
>> +		buf = data->dest;
>> +	else
>> +		buf = (char *)data->src;
>> +
>> +	val = readl(&mmc_base->hctl);
>> +	val |= DMA_SELECT;
>> +	writel(val, &mmc_base->hctl);
>> +
>> +	val = readl(&mmc_base->con);
>> +	val |= DMA_MASTER;
>> +	writel(val, &mmc_base->con);
>> +
>> +	writel((u32)priv->adma_desc_table, &mmc_base->admasal);
>> +
>> +	/* TODO: This shouldn't be required for read. However I don't seem
>> +	 * to get valid data without this.
> TODO? not FIXME?
Actually the comment should be removed. It's required for read as well 
(at least the cache invalidation part is required).
I'll remove the comment in v2
>
>> +	 */
>> +	flush_dcache_range((u32)buf,
>> +			   (u32)buf +
>> +			   ROUND(data->blocksize * data->blocks,
>> +			   ARCH_DMA_MINALIGN));
>> +}
>> +
>> +static void omap_hsmmc_dma_cleanup(struct mmc *mmc)
>> +{
>> +	struct hsmmc *mmc_base;
>> +	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
>> +	u32 val;
>> +
>> +	mmc_base = priv->base_addr;
>> +
>> +	val = readl(&mmc_base->con);
>> +	val &= ~DMA_MASTER;
>> +	writel(val, &mmc_base->con);
>> +
>> +	val = readl(&mmc_base->hctl);
>> +	val &= ~DMA_SELECT;
>> +	writel(val, &mmc_base->hctl);
>> +
>> +	kfree(priv->adma_desc_table);
>> +}
>> +#else
>> +#define omap_hsmmc_adma_desc
>> +#define omap_hsmmc_prepare_adma_table
>> +#define omap_hsmmc_prepare_data
>> +#define omap_hsmmc_dma_cleanup
>> +#endif
>> +
>>   #ifndef CONFIG_DM_MMC
>>   static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
>>   			struct mmc_data *data)
>> @@ -332,6 +482,8 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>>   			struct mmc_data *data)
>>   {
>>   	struct omap_hsmmc_data *priv = dev_get_priv(dev);
>> +	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
>> +	struct mmc *mmc = upriv->mmc;
>>   #endif
>>   	struct hsmmc *mmc_base;
>>   	unsigned int flags, mmc_stat;
>> @@ -405,6 +557,14 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>>   			flags |= (DP_DATA | DDIR_READ);
>>   		else
>>   			flags |= (DP_DATA | DDIR_WRITE);
>> +
>> +#ifndef CONFIG_OMAP34XX
>> +		if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) &&
>> +		    !mmc_is_tuning_cmd(cmd->cmdidx)) {
>> +			omap_hsmmc_prepare_data(mmc, data);
>> +			flags |= DE_ENABLE;
>> +		}
>> +#endif
>>   	}
>>   
>>   	writel(cmd->cmdarg, &mmc_base->arg);
>> @@ -441,6 +601,28 @@ static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
>>   		}
>>   	}
>>   
>> +#ifndef CONFIG_OMAP34XX
>> +	if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) && data &&
>> +	    !mmc_is_tuning_cmd(cmd->cmdidx)) {
>> +		if (mmc_stat & IE_ADMAE) {
>> +			omap_hsmmc_dma_cleanup(mmc);
>> +			return -1;
> Use the valid error number.
>
>> +		}
>> +
>> +		do {
>> +			mmc_stat = readl(&mmc_base->stat);
>> +			if (mmc_stat & TC_MASK) {
>> +				writel(readl(&mmc_base->stat) | TC_MASK,
>> +				       &mmc_base->stat);
>> +				break;
>> +			}
>> +		} while (1);
> Can you add the timeout? There is a potential infinte loop.
ok

Thanks for the review

JJ
>
>> +
>> +		omap_hsmmc_dma_cleanup(mmc);
>> +		return 0;
>> +	}
>> +#endif
>> +
>>   	if (data && (data->flags & MMC_DATA_READ)) {
>>   		mmc_read_data(mmc_base,	data->dest,
>>   				data->blocksize * data->blocks);
>>
>

Patch

diff --git a/arch/arm/include/asm/omap_mmc.h b/arch/arm/include/asm/omap_mmc.h
index 2b489a4..206badb 100644
--- a/arch/arm/include/asm/omap_mmc.h
+++ b/arch/arm/include/asm/omap_mmc.h
@@ -29,7 +29,10 @@ 
 
 struct hsmmc {
 #ifndef CONFIG_OMAP34XX
-	unsigned char res0[0x100];
+	unsigned int hl_rev;
+	unsigned int hl_hwinfo;
+	unsigned int hl_sysconfig;
+	unsigned char res0[0xf4];
 #endif
 	unsigned char res1[0x10];
 	unsigned int sysconfig;		/* 0x10 */
@@ -52,6 +55,9 @@  struct hsmmc {
 	unsigned int ie;		/* 0x134 */
 	unsigned char res4[0x8];
 	unsigned int capa;		/* 0x140 */
+	unsigned char res5[0x10];
+	unsigned int admaes;		/* 0x154 */
+	unsigned int admasal;		/* 0x158 */
 };
 
 struct omap_hsmmc_plat {
@@ -64,6 +70,7 @@  struct omap_hsmmc_plat {
 /*
  * OMAP HS MMC Bit definitions
  */
+#define MADMA_EN			(0x1 << 0)
 #define MMC_SOFTRESET			(0x1 << 1)
 #define RESETDONE			(0x1 << 0)
 #define NOOPENDRAIN			(0x0 << 0)
@@ -80,6 +87,7 @@  struct omap_hsmmc_plat {
 #define WPP_ACTIVEHIGH			(0x0 << 8)
 #define RESERVED_MASK			(0x3 << 9)
 #define CTPL_MMC_SD			(0x0 << 11)
+#define DMA_MASTER			(0x1 << 20)
 #define BLEN_512BYTESLEN		(0x200 << 0)
 #define NBLK_STPCNT			(0x0 << 16)
 #define DE_DISABLE			(0x0 << 0)
@@ -119,6 +127,7 @@  struct omap_hsmmc_plat {
 #define SDBP_PWRON			(0x1 << 8)
 #define SDVS_1V8			(0x5 << 9)
 #define SDVS_3V0			(0x6 << 9)
+#define DMA_SELECT			(0x2 << 3)
 #define ICE_MASK			(0x1 << 0)
 #define ICE_STOP			(0x0 << 0)
 #define ICS_MASK			(0x1 << 1)
@@ -148,6 +157,7 @@  struct omap_hsmmc_plat {
 #define IE_DTO				(0x01 << 20)
 #define IE_DCRC				(0x01 << 21)
 #define IE_DEB				(0x01 << 22)
+#define IE_ADMAE			(0x01 << 25)
 #define IE_CERR				(0x01 << 28)
 #define IE_BADA				(0x01 << 29)
 
diff --git a/drivers/mmc/omap_hsmmc.c b/drivers/mmc/omap_hsmmc.c
index 2c1e429..3419dc5 100644
--- a/drivers/mmc/omap_hsmmc.c
+++ b/drivers/mmc/omap_hsmmc.c
@@ -25,6 +25,7 @@ 
 #include <config.h>
 #include <common.h>
 #include <malloc.h>
+#include <memalign.h>
 #include <mmc.h>
 #include <part.h>
 #include <i2c.h>
@@ -71,10 +72,37 @@  struct omap_hsmmc_data {
 	int wp_gpio;
 #endif
 #endif
+	u8 controller_flags;
+#ifndef CONFIG_OMAP34XX
+	struct omap_hsmmc_adma_desc *adma_desc_table;
+	uint desc_slot;
+#endif
+};
+
+#ifndef CONFIG_OMAP34XX
+struct omap_hsmmc_adma_desc {
+	u8 attr;
+	u8 reserved;
+	u16 len;
+	u32 addr;
 };
 
+#define ADMA_MAX_LEN	63488
+
+/* 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)
+#endif
+
 /* If we fail after 1 second wait, something is really bad */
 #define MAX_RETRY_MS	1000
+#define OMAP_HSMMC_USE_ADMA			BIT(2)
 
 static int mmc_read_data(struct hsmmc *mmc_base, char *buf, unsigned int size);
 static int mmc_write_data(struct hsmmc *mmc_base, const char *buf,
@@ -242,6 +270,11 @@  static int omap_hsmmc_init_setup(struct mmc *mmc)
 			return -ETIMEDOUT;
 		}
 	}
+#ifndef CONFIG_OMAP34XX
+	reg_val = readl(&mmc_base->hl_hwinfo);
+	if (reg_val & MADMA_EN)
+		priv->controller_flags |= OMAP_HSMMC_USE_ADMA;
+#endif
 	writel(DTW_1_BITMODE | SDBP_PWROFF | SDVS_3V0, &mmc_base->hctl);
 	writel(readl(&mmc_base->capa) | VS30_3V0SUP | VS18_1V8SUP,
 		&mmc_base->capa);
@@ -269,8 +302,8 @@  static int omap_hsmmc_init_setup(struct mmc *mmc)
 	writel(readl(&mmc_base->hctl) | SDBP_PWRON, &mmc_base->hctl);
 
 	writel(IE_BADA | IE_CERR | IE_DEB | IE_DCRC | IE_DTO | IE_CIE |
-		IE_CEB | IE_CCRC | IE_CTO | IE_BRR | IE_BWR | IE_TC | IE_CC,
-		&mmc_base->ie);
+		IE_CEB | IE_CCRC | IE_ADMAE | IE_CTO | IE_BRR | IE_BWR | IE_TC |
+		IE_CC, &mmc_base->ie);
 
 	mmc_init_stream(mmc_base);
 
@@ -322,6 +355,123 @@  static void mmc_reset_controller_fsm(struct hsmmc *mmc_base, u32 bit)
 		}
 	}
 }
+
+#ifndef CONFIG_OMAP34XX
+static int omap_hsmmc_adma_desc(struct mmc *mmc, char *buf, u16 len, bool end)
+{
+	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+	struct omap_hsmmc_adma_desc *desc;
+	u8 attr;
+
+	desc = &priv->adma_desc_table[priv->desc_slot];
+
+	attr = ADMA_DESC_ATTR_VALID | ADMA_DESC_TRANSFER_DATA;
+	if (!end)
+		priv->desc_slot++;
+	else
+		attr |= ADMA_DESC_ATTR_END;
+
+	desc->len = len;
+	desc->addr = (u32)buf;
+	desc->reserved = 0;
+	desc->attr = attr;
+
+	return 0;
+}
+
+static int omap_hsmmc_prepare_adma_table(struct mmc *mmc, struct mmc_data *data)
+{
+	uint total_len = data->blocksize * data->blocks;
+	uint desc_count = DIV_ROUND_UP(total_len, ADMA_MAX_LEN);
+	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+	int i = desc_count;
+	char *buf;
+
+	priv->desc_slot = 0;
+	priv->adma_desc_table = (struct omap_hsmmc_adma_desc *)
+				memalign(ARCH_DMA_MINALIGN, desc_count *
+				sizeof(struct omap_hsmmc_adma_desc));
+
+	if (data->flags & MMC_DATA_READ)
+		buf = data->dest;
+	else
+		buf = (char *)data->src;
+
+	while (--i) {
+		omap_hsmmc_adma_desc(mmc, buf, ADMA_MAX_LEN, false);
+		buf += ADMA_MAX_LEN;
+		total_len -= ADMA_MAX_LEN;
+	}
+
+	omap_hsmmc_adma_desc(mmc, buf, total_len, true);
+
+	flush_dcache_range((long)priv->adma_desc_table,
+			   (long)priv->adma_desc_table +
+			   ROUND(desc_count *
+			   sizeof(struct omap_hsmmc_adma_desc),
+			   ARCH_DMA_MINALIGN));
+	return 0;
+}
+
+static void omap_hsmmc_prepare_data(struct mmc *mmc, struct mmc_data *data)
+{
+	struct hsmmc *mmc_base;
+	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+	u32 val;
+	char *buf;
+
+	mmc_base = priv->base_addr;
+	omap_hsmmc_prepare_adma_table(mmc, data);
+
+	if (data->flags & MMC_DATA_READ)
+		buf = data->dest;
+	else
+		buf = (char *)data->src;
+
+	val = readl(&mmc_base->hctl);
+	val |= DMA_SELECT;
+	writel(val, &mmc_base->hctl);
+
+	val = readl(&mmc_base->con);
+	val |= DMA_MASTER;
+	writel(val, &mmc_base->con);
+
+	writel((u32)priv->adma_desc_table, &mmc_base->admasal);
+
+	/* TODO: This shouldn't be required for read. However I don't seem
+	 * to get valid data without this.
+	 */
+	flush_dcache_range((u32)buf,
+			   (u32)buf +
+			   ROUND(data->blocksize * data->blocks,
+			   ARCH_DMA_MINALIGN));
+}
+
+static void omap_hsmmc_dma_cleanup(struct mmc *mmc)
+{
+	struct hsmmc *mmc_base;
+	struct omap_hsmmc_data *priv = omap_hsmmc_get_data(mmc);
+	u32 val;
+
+	mmc_base = priv->base_addr;
+
+	val = readl(&mmc_base->con);
+	val &= ~DMA_MASTER;
+	writel(val, &mmc_base->con);
+
+	val = readl(&mmc_base->hctl);
+	val &= ~DMA_SELECT;
+	writel(val, &mmc_base->hctl);
+
+	kfree(priv->adma_desc_table);
+}
+#else
+#define omap_hsmmc_adma_desc
+#define omap_hsmmc_prepare_adma_table
+#define omap_hsmmc_prepare_data
+#define omap_hsmmc_dma_cleanup
+#endif
+
 #ifndef CONFIG_DM_MMC
 static int omap_hsmmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 			struct mmc_data *data)
@@ -332,6 +482,8 @@  static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 			struct mmc_data *data)
 {
 	struct omap_hsmmc_data *priv = dev_get_priv(dev);
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+	struct mmc *mmc = upriv->mmc;
 #endif
 	struct hsmmc *mmc_base;
 	unsigned int flags, mmc_stat;
@@ -405,6 +557,14 @@  static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 			flags |= (DP_DATA | DDIR_READ);
 		else
 			flags |= (DP_DATA | DDIR_WRITE);
+
+#ifndef CONFIG_OMAP34XX
+		if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) &&
+		    !mmc_is_tuning_cmd(cmd->cmdidx)) {
+			omap_hsmmc_prepare_data(mmc, data);
+			flags |= DE_ENABLE;
+		}
+#endif
 	}
 
 	writel(cmd->cmdarg, &mmc_base->arg);
@@ -441,6 +601,28 @@  static int omap_hsmmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
 		}
 	}
 
+#ifndef CONFIG_OMAP34XX
+	if ((priv->controller_flags & OMAP_HSMMC_USE_ADMA) && data &&
+	    !mmc_is_tuning_cmd(cmd->cmdidx)) {
+		if (mmc_stat & IE_ADMAE) {
+			omap_hsmmc_dma_cleanup(mmc);
+			return -1;
+		}
+
+		do {
+			mmc_stat = readl(&mmc_base->stat);
+			if (mmc_stat & TC_MASK) {
+				writel(readl(&mmc_base->stat) | TC_MASK,
+				       &mmc_base->stat);
+				break;
+			}
+		} while (1);
+
+		omap_hsmmc_dma_cleanup(mmc);
+		return 0;
+	}
+#endif
+
 	if (data && (data->flags & MMC_DATA_READ)) {
 		mmc_read_data(mmc_base,	data->dest,
 				data->blocksize * data->blocks);