diff mbox

[RFC,2/3] mmc: host: omap_hsmmc: Enable ADMA2

Message ID 1463561115-31798-3-git-send-email-kishon@ti.com (mailing list archive)
State New, archived
Headers show

Commit Message

Kishon Vijay Abraham I May 18, 2016, 8:45 a.m. UTC
omap hsmmc host controller has ADMA2 feature. Enable it here
for better read and write throughput. Add a new dt binding
"ti,use_adma" to enable ADMA2.

Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
---
 .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
 drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
 include/linux/platform_data/hsmmc-omap.h           |    1 +
 3 files changed, 256 insertions(+), 66 deletions(-)

Comments

Peter Ujfalusi May 18, 2016, 10:24 a.m. UTC | #1
On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
> omap hsmmc host controller has ADMA2 feature. Enable it here
> for better read and write throughput. Add a new dt binding
> "ti,use_adma" to enable ADMA2.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>  3 files changed, 256 insertions(+), 66 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> index 74166a0..eb5ceec2 100644
> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> @@ -28,6 +28,7 @@ specifier is required.
>  dma-names: List of DMA request names. These strings correspond
>  1:1 with the DMA specifiers listed in dmas. The string naming is
>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
> +ti,use_adma: enable adma2 feature

Do we have use case when you want to fall back to generic DMA instead of aDMA2?
IMHO if the driver supports aDMA2, it is going to use it instead of the
generic s/eDMA.
What I mean is:
the driver implements the aDMA2 support.
if the IP has support for aDMA2, then it is going to use it, otherwise it will
use the generic DMA.
Peter Ujfalusi May 18, 2016, 11:07 a.m. UTC | #2
On 05/18/16 11:45, Kishon Vijay Abraham I wrote:

> +static int omap_hsmmc_dma_init(struct omap_hsmmc_host *host)
> +{
> +	dma_cap_mask_t mask;
> +	unsigned int tx_req, rx_req;
> +	struct resource *res;
> +	struct platform_device *pdev = to_platform_device(host->dev);
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	if (!pdev->dev.of_node) {
> +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
> +		if (!res) {
> +			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
> +			return -ENXIO;
> +		}
> +		tx_req = res->start;
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
> +		if (!res) {
> +			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
> +			return -ENXIO;
> +		}
> +		rx_req = res->start;
> +	}
> +
> +	host->rx_chan =
> +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> +						 &rx_req, &pdev->dev, "rx");
> +
> +	if (!host->rx_chan) {
> +		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
> +		return -ENXIO;
> +	}
> +
> +	host->tx_chan =
> +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> +						 &tx_req, &pdev->dev, "tx");
> +
> +	if (!host->tx_chan) {
> +		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
> +		return -ENXIO;

upstream moved to use dma_request_chan() so this part needs to be changed.

> +	}
> +
> +	return 0;
> +}
> +
> +static void omap_hsmmc_dma_exit(struct omap_hsmmc_host *host)
> +{
> +	if (host->tx_chan)
> +		dma_release_channel(host->tx_chan);
> +	if (host->rx_chan)
> +		dma_release_channel(host->rx_chan);
> +}
> +
>  static int omap_hsmmc_probe(struct platform_device *pdev)
>  {
>  	struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
> @@ -2000,8 +2219,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  	struct resource *res;
>  	int ret, irq;
>  	const struct of_device_id *match;
> -	dma_cap_mask_t mask;
> -	unsigned tx_req, rx_req;
>  	const struct omap_mmc_of_data *data;
>  	void __iomem *base;
>  
> @@ -2114,7 +2331,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  	mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
>  	mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
>  	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
> -	mmc->max_seg_size = mmc->max_req_size;
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		mmc->max_seg_size = ADMA_MAX_LEN;
> +	else
> +		mmc->max_seg_size = mmc->max_req_size;
>  
>  	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>  		     MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
> @@ -2130,46 +2350,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  
>  	omap_hsmmc_conf_bus_power(host);
>  
> -	if (!pdev->dev.of_node) {
> -		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
> -		if (!res) {
> -			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
> -			ret = -ENXIO;
> -			goto err_irq;
> -		}
> -		tx_req = res->start;
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
> -		if (!res) {
> -			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
> -			ret = -ENXIO;
> -			goto err_irq;
> -		}
> -		rx_req = res->start;
> -	}
> -
> -	dma_cap_zero(mask);
> -	dma_cap_set(DMA_SLAVE, mask);
> -
> -	host->rx_chan =
> -		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> -						 &rx_req, &pdev->dev, "rx");
> -
> -	if (!host->rx_chan) {
> -		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
> -		ret = -ENXIO;
> -		goto err_irq;
> -	}
> -
> -	host->tx_chan =
> -		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> -						 &tx_req, &pdev->dev, "tx");
> -
> -	if (!host->tx_chan) {
> -		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
> -		ret = -ENXIO;
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		ret = omap_hsmmc_adma_init(host);
> +	else
> +		ret = omap_hsmmc_dma_init(host);
> +	if (ret)
>  		goto err_irq;
> -	}
>  
>  	/* Request IRQ for MMC operations */
>  	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
> @@ -2225,11 +2411,11 @@ err_slot_name:
>  	mmc_remove_host(mmc);
>  err_irq:
>  	device_init_wakeup(&pdev->dev, false);
> -	if (host->tx_chan)
> -		dma_release_channel(host->tx_chan);
> -	if (host->rx_chan)
> -		dma_release_channel(host->rx_chan);
>  	pm_runtime_dont_use_autosuspend(host->dev);
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		omap_hsmmc_adma_exit(host);
> +	else
> +		omap_hsmmc_dma_exit(host);
>  	pm_runtime_put_sync(host->dev);
>  	pm_runtime_disable(host->dev);
>  	if (host->dbclk)
> @@ -2248,8 +2434,10 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
>  	pm_runtime_get_sync(host->dev);
>  	mmc_remove_host(host->mmc);
>  
> -	dma_release_channel(host->tx_chan);
> -	dma_release_channel(host->rx_chan);
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		omap_hsmmc_adma_exit(host);
> +	else
> +		omap_hsmmc_dma_exit(host);
>  
>  	pm_runtime_dont_use_autosuspend(host->dev);
>  	pm_runtime_put_sync(host->dev);
> diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h
> index 8e981be..e26013d 100644
> --- a/include/linux/platform_data/hsmmc-omap.h
> +++ b/include/linux/platform_data/hsmmc-omap.h
> @@ -27,6 +27,7 @@
>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
>  #define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
> +#define OMAP_HSMMC_USE_ADMA			BIT(3)
>  
>  struct omap_hsmmc_dev_attr {
>  	u8 flags;
>
Tony Lindgren May 18, 2016, 7:30 p.m. UTC | #3
* Peter Ujfalusi <peter.ujfalusi@ti.com> [160518 03:26]:
> On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
> > omap hsmmc host controller has ADMA2 feature. Enable it here
> > for better read and write throughput. Add a new dt binding
> > "ti,use_adma" to enable ADMA2.
> > 
> > Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> > ---
> >  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
> >  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
> >  include/linux/platform_data/hsmmc-omap.h           |    1 +
> >  3 files changed, 256 insertions(+), 66 deletions(-)
> > 
> > diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> > index 74166a0..eb5ceec2 100644
> > --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> > +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> > @@ -28,6 +28,7 @@ specifier is required.
> >  dma-names: List of DMA request names. These strings correspond
> >  1:1 with the DMA specifiers listed in dmas. The string naming is
> >  to be "rx" and "tx" for RX and TX DMA requests, respectively.
> > +ti,use_adma: enable adma2 feature
> 
> Do we have use case when you want to fall back to generic DMA instead of aDMA2?

Yes my guess is that PM runtime breaks with these currently..

> IMHO if the driver supports aDMA2, it is going to use it instead of the
> generic s/eDMA.
> What I mean is:
> the driver implements the aDMA2 support.
> if the IP has support for aDMA2, then it is going to use it, otherwise it will
> use the generic DMA.

Ideally the adma support would be a separate loadable module,
similar how the cppi41dma is a child of the OTG controller.

That way the systems wanting to use adma can just specify it
in the binding, and adma PM runtime support can be added later
on.

Of course this won't work if the adma registers are sprinkled
within the MMC controller registers..

Regards,

Tony
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kishon Vijay Abraham I May 19, 2016, 6:06 a.m. UTC | #4
Hi Peter,

On Wednesday 18 May 2016 03:54 PM, Peter Ujfalusi wrote:
> On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
>> omap hsmmc host controller has ADMA2 feature. Enable it here
>> for better read and write throughput. Add a new dt binding
>> "ti,use_adma" to enable ADMA2.
>>
>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>> ---
>>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>>  3 files changed, 256 insertions(+), 66 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>> index 74166a0..eb5ceec2 100644
>> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>> @@ -28,6 +28,7 @@ specifier is required.
>>  dma-names: List of DMA request names. These strings correspond
>>  1:1 with the DMA specifiers listed in dmas. The string naming is
>>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
>> +ti,use_adma: enable adma2 feature
> 
> Do we have use case when you want to fall back to generic DMA instead of aDMA2?
> IMHO if the driver supports aDMA2, it is going to use it instead of the
> generic s/eDMA.
> What I mean is:
> the driver implements the aDMA2 support.
> if the IP has support for aDMA2, then it is going to use it, otherwise it will
> use the generic DMA.

hmm.. how will the driver know if the IP has support for aDMA2. Using dt
binding is one way. Using MMCHS_HL_HWINFO is another way but then the register
offsets in omap_hsmmc driver has to be modified for omap4+.

Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Kishon Vijay Abraham I May 19, 2016, 6:14 a.m. UTC | #5
Hi Tony,

On Thursday 19 May 2016 01:00 AM, Tony Lindgren wrote:
> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160518 03:26]:
>> On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
>>> omap hsmmc host controller has ADMA2 feature. Enable it here
>>> for better read and write throughput. Add a new dt binding
>>> "ti,use_adma" to enable ADMA2.
>>>
>>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>>> ---
>>>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>>>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>>>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>>>  3 files changed, 256 insertions(+), 66 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> index 74166a0..eb5ceec2 100644
>>> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> @@ -28,6 +28,7 @@ specifier is required.
>>>  dma-names: List of DMA request names. These strings correspond
>>>  1:1 with the DMA specifiers listed in dmas. The string naming is
>>>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
>>> +ti,use_adma: enable adma2 feature
>>
>> Do we have use case when you want to fall back to generic DMA instead of aDMA2?
> 
> Yes my guess is that PM runtime breaks with these currently..

pm_runtime_get is invoked during omap_hsmmc_request and pm_runtime_put is
invoked during omap_hsmmc_request_done. Both these calls are outside DMA API's.
So it shouldn't break anything w.r.t PM runtime no?
> 
>> IMHO if the driver supports aDMA2, it is going to use it instead of the
>> generic s/eDMA.
>> What I mean is:
>> the driver implements the aDMA2 support.
>> if the IP has support for aDMA2, then it is going to use it, otherwise it will
>> use the generic DMA.
> 
> Ideally the adma support would be a separate loadable module,
> similar how the cppi41dma is a child of the OTG controller.
> 
> That way the systems wanting to use adma can just specify it
> in the binding, and adma PM runtime support can be added later
> on.
> 
> Of course this won't work if the adma registers are sprinkled
> within the MMC controller registers..

right, adma registers are within the MMC controller register space.

Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Peter Ujfalusi May 19, 2016, 8:02 a.m. UTC | #6
On 05/19/2016 09:06 AM, Kishon Vijay Abraham I wrote:
> Hi Peter,
> 
> On Wednesday 18 May 2016 03:54 PM, Peter Ujfalusi wrote:
>> On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
>>> omap hsmmc host controller has ADMA2 feature. Enable it here
>>> for better read and write throughput. Add a new dt binding
>>> "ti,use_adma" to enable ADMA2.
>>>
>>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>>> ---
>>>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>>>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>>>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>>>  3 files changed, 256 insertions(+), 66 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> index 74166a0..eb5ceec2 100644
>>> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> @@ -28,6 +28,7 @@ specifier is required.
>>>  dma-names: List of DMA request names. These strings correspond
>>>  1:1 with the DMA specifiers listed in dmas. The string naming is
>>>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
>>> +ti,use_adma: enable adma2 feature
>>
>> Do we have use case when you want to fall back to generic DMA instead of aDMA2?
>> IMHO if the driver supports aDMA2, it is going to use it instead of the
>> generic s/eDMA.
>> What I mean is:
>> the driver implements the aDMA2 support.
>> if the IP has support for aDMA2, then it is going to use it, otherwise it will
>> use the generic DMA.
> 
> hmm.. how will the driver know if the IP has support for aDMA2. Using dt
> binding is one way. Using MMCHS_HL_HWINFO is another way but then the register
> offsets in omap_hsmmc driver has to be modified for omap4+.

I remember Felipe saying that MMCHS_HL_HWINFO is not reliable for some reason?
But in any case it would be better IMHO if we query the HW for Master DMA
support and if it is available we will use it.

If it is really the case that MMCHS_HL_HWINFO is not reliable, then probably
have a flag for the MMC/SD phandle saying that ti,master_dma_supported to
indicate that it is capable has ADMA support also.
Peter Ujfalusi May 19, 2016, 8:07 a.m. UTC | #7
On 05/18/2016 10:30 PM, Tony Lindgren wrote:
> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160518 03:26]:
>> On 05/18/16 11:45, Kishon Vijay Abraham I wrote:
>>> omap hsmmc host controller has ADMA2 feature. Enable it here
>>> for better read and write throughput. Add a new dt binding
>>> "ti,use_adma" to enable ADMA2.
>>>
>>> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
>>> ---
>>>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>>>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>>>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>>>  3 files changed, 256 insertions(+), 66 deletions(-)
>>>
>>> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> index 74166a0..eb5ceec2 100644
>>> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
>>> @@ -28,6 +28,7 @@ specifier is required.
>>>  dma-names: List of DMA request names. These strings correspond
>>>  1:1 with the DMA specifiers listed in dmas. The string naming is
>>>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
>>> +ti,use_adma: enable adma2 feature
>>
>> Do we have use case when you want to fall back to generic DMA instead of aDMA2?
> 
> Yes my guess is that PM runtime breaks with these currently..
> 
>> IMHO if the driver supports aDMA2, it is going to use it instead of the
>> generic s/eDMA.
>> What I mean is:
>> the driver implements the aDMA2 support.
>> if the IP has support for aDMA2, then it is going to use it, otherwise it will
>> use the generic DMA.
> 
> Ideally the adma support would be a separate loadable module,
> similar how the cppi41dma is a child of the OTG controller.

The Master DMA is part of the hsmmc IP block. If the same ADMA module is
present on other IPs it might be beneficial to have a helper library to handle
it (allocating the descriptor pool, wrinting, updating descriptors, etc).

> That way the systems wanting to use adma can just specify it
> in the binding, and adma PM runtime support can be added later
> on.
> 
> Of course this won't work if the adma registers are sprinkled
> within the MMC controller registers..
> 
> Regards,
> 
> Tony
>
Peter Ujfalusi May 19, 2016, 8:25 a.m. UTC | #8
On 05/18/2016 11:45 AM, Kishon Vijay Abraham I wrote:
> omap hsmmc host controller has ADMA2 feature. Enable it here
> for better read and write throughput. Add a new dt binding
> "ti,use_adma" to enable ADMA2.
> 
> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
> ---
>  .../devicetree/bindings/mmc/ti-omap-hsmmc.txt      |    1 +
>  drivers/mmc/host/omap_hsmmc.c                      |  320 ++++++++++++++++----
>  include/linux/platform_data/hsmmc-omap.h           |    1 +
>  3 files changed, 256 insertions(+), 66 deletions(-)
> 
> diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> index 74166a0..eb5ceec2 100644
> --- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> +++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
> @@ -28,6 +28,7 @@ specifier is required.
>  dma-names: List of DMA request names. These strings correspond
>  1:1 with the DMA specifiers listed in dmas. The string naming is
>  to be "rx" and "tx" for RX and TX DMA requests, respectively.
> +ti,use_adma: enable adma2 feature
>  
>  Examples:
>  
> diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
> index cc916d5..b4a7d18 100644
> --- a/drivers/mmc/host/omap_hsmmc.c
> +++ b/drivers/mmc/host/omap_hsmmc.c
> @@ -66,6 +66,8 @@
>  #define OMAP_HSMMC_ISE		0x0138
>  #define OMAP_HSMMC_AC12		0x013C
>  #define OMAP_HSMMC_CAPA		0x0140
> +#define OMAP_HSMMC_ADMAES	0x0154
> +#define OMAP_HSMMC_ADMASAL	0x0158
>  
>  #define VS18			(1 << 26)
>  #define VS30			(1 << 25)
> @@ -76,6 +78,7 @@
>  #define SDVS_MASK		0x00000E00
>  #define SDVSCLR			0xFFFFF1FF
>  #define SDVSDET			0x00000400
> +#define DMA_SELECT		(2 << 3)
>  #define AUTOIDLE		0x1
>  #define SDBP			(1 << 8)
>  #define DTO			0xe
> @@ -97,6 +100,7 @@
>  #define FOUR_BIT		(1 << 1)
>  #define HSPE			(1 << 2)
>  #define IWE			(1 << 24)
> +#define DMA_MASTER		(1 << 20)
>  #define DDR			(1 << 19)
>  #define CLKEXTFREE		(1 << 16)
>  #define CTPL			(1 << 11)
> @@ -127,10 +131,11 @@
>  #define DCRC_EN			(1 << 21)
>  #define DEB_EN			(1 << 22)
>  #define ACE_EN			(1 << 24)
> +#define ADMAE_EN		(1 << 24)
>  #define CERR_EN			(1 << 28)
>  #define BADA_EN			(1 << 29)
>  
> -#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
> +#define INT_EN_MASK (BADA_EN | CERR_EN | ADMAE_EN | ACE_EN | DEB_EN | DCRC_EN |\
>  		DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
>  		BRR_EN | BWR_EN | TC_EN | CC_EN)
>  
> @@ -168,6 +173,25 @@
>  #define OMAP_HSMMC_WRITE(base, reg, val) \
>  	__raw_writel((val), (base) + OMAP_HSMMC_##reg)
>  
> +struct omap_hsmmc_adma_desc {
> +	u8 attr;
> +	u8 reserved;
> +	u16 len;
> +	u32 addr;
> +} __packed;
> +
> +#define ADMA_MAX_LEN			65532
> +
> +/* 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 omap_hsmmc_next {
>  	unsigned int	dma_len;
>  	s32		cookie;
> @@ -213,6 +237,9 @@ struct omap_hsmmc_host {
>  	struct omap_hsmmc_next	next_data;
>  	struct	omap_hsmmc_platform_data	*pdata;
>  
> +	struct omap_hsmmc_adma_desc *adma_desc_table;
> +	dma_addr_t              adma_desc_table_addr;
> +
>  	/* return MMC cover switch state, can be NULL if not supported.
>  	 *
>  	 * possible return values:
> @@ -951,6 +978,19 @@ static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
>  	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
>  }
>  
> +static void omap_hsmmc_adma_cleanup(struct omap_hsmmc_host *host)
> +{
> +	u32 val;
> +
> +	val = OMAP_HSMMC_READ(host->base, HCTL);
> +	val &= ~DMA_SELECT;
> +	OMAP_HSMMC_WRITE(host->base, HCTL, val);
> +
> +	val = OMAP_HSMMC_READ(host->base, CON);
> +	val &= ~DMA_MASTER;
> +	OMAP_HSMMC_WRITE(host->base, CON, val);
> +}
> +
>  static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
>  {
>  	int dma_ch;
> @@ -963,8 +1003,11 @@ static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req
>  
>  	omap_hsmmc_disable_irq(host);
>  	/* Do not complete the request if DMA is still in progress */
> -	if (mrq->data && dma_ch != -1)
> +	if (host->pdata->controller_flags == OMAP_HSMMC_USE_ADMA)

host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA

and I would probably name the flag to OMAP_HSMMC_MASTER_DMA_SUPPORTED

> +		omap_hsmmc_adma_cleanup(host);
> +	else if (mrq->data && dma_ch != -1)
>  		return;
> +
>  	host->mrq = NULL;
>  	mmc_request_done(host->mmc, mrq);
>  	pm_runtime_mark_last_busy(host->dev);
> @@ -1052,15 +1095,22 @@ static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
>  	host->dma_ch = -1;
>  	spin_unlock_irqrestore(&host->irq_lock, flags);
>  
> -	if (dma_ch != -1) {
> -		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
> -
> -		dmaengine_terminate_all(chan);
> -		dma_unmap_sg(chan->device->dev,
> -			host->data->sg, host->data->sg_len,
> +	if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) {

it might be better to use the same order in the driver for Master and Slave
DMA cases:

if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) {
	/* Master DMA case */
} else {
	/* Slave DMA case */
}

It will make the driver easier to read.

> +		if (dma_ch != -1) {
> +			struct dma_chan *chan = omap_hsmmc_get_dma_chan(host,
> +								host->data);
> +			dmaengine_terminate_all(chan);
> +			dma_unmap_sg(chan->device->dev,
> +				     host->data->sg, host->data->sg_len,
>  			omap_hsmmc_get_dma_dir(host, host->data));
>  
> +			host->data->host_cookie = 0;
> +		}
> +	} else {
> +		dma_unmap_sg(host->dev, host->data->sg, host->data->sg_len,
> +			     omap_hsmmc_get_dma_dir(host, host->data));
>  		host->data->host_cookie = 0;
> +
>  	}
>  	host->data = NULL;
>  }
> @@ -1191,6 +1241,14 @@ static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
>  			}
>  			dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);
>  		}
> +
> +		if (status & ADMAE_EN) {
> +			u32 val;
> +
> +			val = OMAP_HSMMC_READ(host->base, ADMAES);
> +			dev_dbg(mmc_dev(host->mmc), "ADMA error status: 0x%x\n",
> +				val);
> +		}
>  	}
>  
>  	OMAP_HSMMC_WRITE(host->base, STAT, status);
> @@ -1378,6 +1436,7 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
>  				       struct dma_chan *chan)
>  {
>  	int dma_len;
> +	struct device *dev;
>  
>  	if (!next && data->host_cookie &&
>  	    data->host_cookie != host->next_data.cookie) {
> @@ -1387,9 +1446,14 @@ static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
>  		data->host_cookie = 0;
>  	}
>  
> +	if (chan)
> +		dev = chan->device->dev;
> +	else
> +		dev = mmc_dev(host->mmc);
> +
>  	/* Check if next job is already prepared */
>  	if (next || data->host_cookie != host->next_data.cookie) {
> -		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
> +		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
>  				     omap_hsmmc_get_dma_dir(host, data));
>  
>  	} else {
> @@ -1516,6 +1580,7 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
>  {
>  	struct mmc_request *req = host->mrq;
>  	struct dma_chan *chan;
> +	int val;
>  
>  	if (!req->data)
>  		return;
> @@ -1523,10 +1588,66 @@ static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
>  				| (req->data->blocks << 16));
>  	set_data_timeout(host, req->data->timeout_ns,
>  				req->data->timeout_clks);
> -	chan = omap_hsmmc_get_dma_chan(host, req->data);
> -	dma_async_issue_pending(chan);
> +
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) {
> +		val = OMAP_HSMMC_READ(host->base, HCTL);
> +		val |= DMA_SELECT;
> +		OMAP_HSMMC_WRITE(host->base, HCTL, val);
> +
> +		val = OMAP_HSMMC_READ(host->base, CON);
> +		val |= DMA_MASTER;
> +		OMAP_HSMMC_WRITE(host->base, CON, val);
> +
> +		OMAP_HSMMC_WRITE(host->base, ADMASAL,
> +				 (u32)host->adma_desc_table_addr);
> +	} else {
> +		chan = omap_hsmmc_get_dma_chan(host, req->data);
> +		dma_async_issue_pending(chan);
> +	}
> +}
> +
> +static int omap_hsmmc_write_adma_desc(struct omap_hsmmc_host *host, void *desc,
> +				      dma_addr_t addr, u16 len, u8 attr)
> +{
> +	struct omap_hsmmc_adma_desc *dma_desc = desc;
> +
> +	dma_desc->len = len;
> +	dma_desc->addr = (u32)addr;
> +	dma_desc->reserved = 0;
> +	dma_desc->attr = attr;
> +
> +	return 0;
>  }
>  
> +static int omap_hsmmc_setup_adma_transfer(struct omap_hsmmc_host *host,
> +					  struct mmc_request *req)
> +{
> +	struct mmc_data *data = req->data;
> +	struct scatterlist *sg;
> +	int i;
> +	int len;
> +	int ret;
> +	dma_addr_t addr;
> +	struct omap_hsmmc_adma_desc *dma_desc;
> +
> +	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, NULL);
> +	if (ret)
> +		return ret;
> +
> +	dma_desc = host->adma_desc_table;
> +	for_each_sg(data->sg, sg, host->dma_len, i) {
> +		addr = sg_dma_address(sg);
> +		len = sg_dma_len(sg);
> +		WARN_ON(len > ADMA_MAX_LEN);
> +		omap_hsmmc_write_adma_desc(host, dma_desc, addr, len,
> +					   ADMA_DESC_ATTR_VALID |
> +					   ADMA_DESC_TRANSFER_DATA);
> +		dma_desc++;
> +	}
> +	omap_hsmmc_write_adma_desc(host, dma_desc, 0, 0, ADMA_DESC_ATTR_END);
> +
> +	return 0;
> +}

Would be nice to group the ADMA functions in one section, not scattering it
around the driver.

>  /*
>   * Configure block length for MMC/SD cards and initiate the transfer.
>   */
> @@ -1547,10 +1668,18 @@ omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
>  		return 0;
>  	}
>  
> -	ret = omap_hsmmc_setup_dma_transfer(host, req);
> -	if (ret != 0) {
> -		dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
> -		return ret;
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) {
> +		ret = omap_hsmmc_setup_adma_transfer(host, req);
> +		if (ret != 0) {
> +			dev_err(mmc_dev(host->mmc), "MMC adma setup failed\n");
> +			return ret;
> +		}
> +	} else {
> +		ret = omap_hsmmc_setup_dma_transfer(host, req);
> +		if (ret != 0) {
> +			dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
> +			return ret;

the error checking can go outside

> +		}
>  	}
>  	return 0;

	if (ret != 0)
		dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
	return ret;

if compiler complains about ret not beeing initialized
int rer = 0;

>  }
> @@ -1560,11 +1689,18 @@ static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
>  {
>  	struct omap_hsmmc_host *host = mmc_priv(mmc);
>  	struct mmc_data *data = mrq->data;
> +	struct device *dev;
> +	struct dma_chan *c;
>  
>  	if (data->host_cookie) {
> -		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
> +		if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) {
> +			c = omap_hsmmc_get_dma_chan(host, mrq->data);
> +			dev = c->device->dev;
> +		} else {
> +			dev = mmc_dev(mmc);
> +		}
>  
> -		dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
> +		dma_unmap_sg(dev, data->sg, data->sg_len,
>  			     omap_hsmmc_get_dma_dir(host, data));
>  		data->host_cookie = 0;
>  	}
> @@ -1574,13 +1710,15 @@ static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
>  			       bool is_first_req)
>  {
>  	struct omap_hsmmc_host *host = mmc_priv(mmc);
> +	struct dma_chan *c = NULL;
>  
>  	if (mrq->data->host_cookie) {
>  		mrq->data->host_cookie = 0;
>  		return ;
>  	}
>  
> -	struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
> +	if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA))
> +		c = omap_hsmmc_get_dma_chan(host, mrq->data);
>  
>  	if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
>  					&host->next_data, c))
> @@ -1967,6 +2105,9 @@ static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
>  	if (of_find_property(np, "ti,dual-volt", NULL))
>  		pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
>  
> +	if (of_find_property(np, "ti,use_adma", NULL))
> +		pdata->controller_flags |= OMAP_HSMMC_USE_ADMA;
> +
>  	pdata->gpio_cd = -EINVAL;
>  	pdata->gpio_cod = -EINVAL;
>  	pdata->gpio_wp = -EINVAL;
> @@ -1992,6 +2133,84 @@ static inline struct omap_hsmmc_platform_data
>  }
>  #endif
>  
> +static int omap_hsmmc_adma_init(struct omap_hsmmc_host *host)
> +{
> +	struct mmc_host *mmc = host->mmc;
> +
> +	host->adma_desc_table = dma_alloc_coherent(host->dev, mmc->max_segs + 1,
> +						   &host->adma_desc_table_addr,
> +						   GFP_KERNEL);
> +	if (!host->adma_desc_table) {
> +		dev_err(host->dev, "failed to allocate adma desc table\n");
> +		return -ENOMEM;

Fall back to Slave DMA?

> +	}
> +
> +	return 0;
> +}
> +
> +static void omap_hsmmc_adma_exit(struct omap_hsmmc_host *host)
> +{
> +	struct mmc_host *mmc = host->mmc;
> +
> +	dma_free_coherent(host->dev, mmc->max_segs + 1,
> +			  host->adma_desc_table, host->adma_desc_table_addr);
> +}
> +
> +static int omap_hsmmc_dma_init(struct omap_hsmmc_host *host)
> +{
> +	dma_cap_mask_t mask;
> +	unsigned int tx_req, rx_req;
> +	struct resource *res;
> +	struct platform_device *pdev = to_platform_device(host->dev);
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	if (!pdev->dev.of_node) {
> +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
> +		if (!res) {
> +			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
> +			return -ENXIO;
> +		}
> +		tx_req = res->start;
> +
> +		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
> +		if (!res) {
> +			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
> +			return -ENXIO;
> +		}
> +		rx_req = res->start;
> +	}
> +
> +	host->rx_chan =
> +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> +						 &rx_req, &pdev->dev, "rx");
> +
> +	if (!host->rx_chan) {
> +		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
> +		return -ENXIO;
> +	}
> +
> +	host->tx_chan =
> +		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> +						 &tx_req, &pdev->dev, "tx");
> +
> +	if (!host->tx_chan) {
> +		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
> +		return -ENXIO;
> +	}
> +
> +	return 0;
> +}
> +
> +static void omap_hsmmc_dma_exit(struct omap_hsmmc_host *host)
> +{
> +	if (host->tx_chan)
> +		dma_release_channel(host->tx_chan);
> +	if (host->rx_chan)
> +		dma_release_channel(host->rx_chan);
> +}
> +
>  static int omap_hsmmc_probe(struct platform_device *pdev)
>  {
>  	struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
> @@ -2000,8 +2219,6 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  	struct resource *res;
>  	int ret, irq;
>  	const struct of_device_id *match;
> -	dma_cap_mask_t mask;
> -	unsigned tx_req, rx_req;
>  	const struct omap_mmc_of_data *data;
>  	void __iomem *base;
>  
> @@ -2114,7 +2331,10 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  	mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
>  	mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
>  	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
> -	mmc->max_seg_size = mmc->max_req_size;
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		mmc->max_seg_size = ADMA_MAX_LEN;
> +	else
> +		mmc->max_seg_size = mmc->max_req_size;
>  
>  	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
>  		     MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
> @@ -2130,46 +2350,12 @@ static int omap_hsmmc_probe(struct platform_device *pdev)
>  
>  	omap_hsmmc_conf_bus_power(host);
>  
> -	if (!pdev->dev.of_node) {
> -		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
> -		if (!res) {
> -			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
> -			ret = -ENXIO;
> -			goto err_irq;
> -		}
> -		tx_req = res->start;
> -
> -		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
> -		if (!res) {
> -			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
> -			ret = -ENXIO;
> -			goto err_irq;
> -		}
> -		rx_req = res->start;
> -	}
> -
> -	dma_cap_zero(mask);
> -	dma_cap_set(DMA_SLAVE, mask);
> -
> -	host->rx_chan =
> -		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> -						 &rx_req, &pdev->dev, "rx");
> -
> -	if (!host->rx_chan) {
> -		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
> -		ret = -ENXIO;
> -		goto err_irq;
> -	}
> -
> -	host->tx_chan =
> -		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
> -						 &tx_req, &pdev->dev, "tx");
> -
> -	if (!host->tx_chan) {
> -		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
> -		ret = -ENXIO;
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		ret = omap_hsmmc_adma_init(host);
> +	else
> +		ret = omap_hsmmc_dma_init(host);
> +	if (ret)
>  		goto err_irq;
> -	}
>  
>  	/* Request IRQ for MMC operations */
>  	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
> @@ -2225,11 +2411,11 @@ err_slot_name:
>  	mmc_remove_host(mmc);
>  err_irq:
>  	device_init_wakeup(&pdev->dev, false);
> -	if (host->tx_chan)
> -		dma_release_channel(host->tx_chan);
> -	if (host->rx_chan)
> -		dma_release_channel(host->rx_chan);
>  	pm_runtime_dont_use_autosuspend(host->dev);
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		omap_hsmmc_adma_exit(host);
> +	else
> +		omap_hsmmc_dma_exit(host);
>  	pm_runtime_put_sync(host->dev);
>  	pm_runtime_disable(host->dev);
>  	if (host->dbclk)
> @@ -2248,8 +2434,10 @@ static int omap_hsmmc_remove(struct platform_device *pdev)
>  	pm_runtime_get_sync(host->dev);
>  	mmc_remove_host(host->mmc);
>  
> -	dma_release_channel(host->tx_chan);
> -	dma_release_channel(host->rx_chan);
> +	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
> +		omap_hsmmc_adma_exit(host);
> +	else
> +		omap_hsmmc_dma_exit(host);
>  
>  	pm_runtime_dont_use_autosuspend(host->dev);
>  	pm_runtime_put_sync(host->dev);
> diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h
> index 8e981be..e26013d 100644
> --- a/include/linux/platform_data/hsmmc-omap.h
> +++ b/include/linux/platform_data/hsmmc-omap.h
> @@ -27,6 +27,7 @@
>  #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
>  #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
>  #define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
> +#define OMAP_HSMMC_USE_ADMA			BIT(3)
>  
>  struct omap_hsmmc_dev_attr {
>  	u8 flags;
>
Tony Lindgren May 19, 2016, 2:57 p.m. UTC | #9
* Peter Ujfalusi <peter.ujfalusi@ti.com> [160519 01:10]:
> On 05/18/2016 10:30 PM, Tony Lindgren wrote:
> > Ideally the adma support would be a separate loadable module,
> > similar how the cppi41dma is a child of the OTG controller.
> 
> The Master DMA is part of the hsmmc IP block. If the same ADMA module is
> present on other IPs it might be beneficial to have a helper library to handle
> it (allocating the descriptor pool, wrinting, updating descriptors, etc).

OK. Yeah if it's part of the MMC controller it makes no sense to
separate it. So then the conecrns are using alternate DMA
implementations and keeping PM runtime working :)

BTW, Felipe mentioned that the best thing to do in the long run would
be to set up sdhci-omap.c operating in ADMA mode.

Felipe, care to summarize what you had in mind?

Regards,

Tony

--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Felipe Balbi May 19, 2016, 6:36 p.m. UTC | #10
Hi,

Tony Lindgren <tony@atomide.com> writes:
> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160519 01:10]:
>> On 05/18/2016 10:30 PM, Tony Lindgren wrote:
>> > Ideally the adma support would be a separate loadable module,
>> > similar how the cppi41dma is a child of the OTG controller.
>> 
>> The Master DMA is part of the hsmmc IP block. If the same ADMA module is
>> present on other IPs it might be beneficial to have a helper library to handle
>> it (allocating the descriptor pool, wrinting, updating descriptors, etc).
>
> OK. Yeah if it's part of the MMC controller it makes no sense to
> separate it. So then the conecrns are using alternate DMA
> implementations and keeping PM runtime working :)
>
> BTW, Felipe mentioned that the best thing to do in the long run would
> be to set up sdhci-omap.c operating in ADMA mode.
>
> Felipe, care to summarize what you had in mind?

yeah, just write a new sdhci-omap.c to start moving away from
omap-hsmmc.c, just like it was done for 8250-omap.

At the beginning, it could be just the bare minimum to get it working
and slowly move over stuff like pm runtime, dmaengine, PIO. Move more
platforms over to that driver and, eventually, get rid of omap-hsmmc.c
altogether.

That way, development can be focussed on generic layers (SDHCI) to which
OMAP MMC controller is compliant (apart from the VERSION register
quirk).
Kishon Vijay Abraham I May 23, 2016, 6:22 a.m. UTC | #11
Hi Felipe,

On Friday 20 May 2016 12:06 AM, Felipe Balbi wrote:
> 
> Hi,
> 
> Tony Lindgren <tony@atomide.com> writes:
>> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160519 01:10]:
>>> On 05/18/2016 10:30 PM, Tony Lindgren wrote:
>>>> Ideally the adma support would be a separate loadable module,
>>>> similar how the cppi41dma is a child of the OTG controller.
>>>
>>> The Master DMA is part of the hsmmc IP block. If the same ADMA module is
>>> present on other IPs it might be beneficial to have a helper library to handle
>>> it (allocating the descriptor pool, wrinting, updating descriptors, etc).
>>
>> OK. Yeah if it's part of the MMC controller it makes no sense to
>> separate it. So then the conecrns are using alternate DMA
>> implementations and keeping PM runtime working :)
>>
>> BTW, Felipe mentioned that the best thing to do in the long run would
>> be to set up sdhci-omap.c operating in ADMA mode.
>>
>> Felipe, care to summarize what you had in mind?
> 
> yeah, just write a new sdhci-omap.c to start moving away from
> omap-hsmmc.c, just like it was done for 8250-omap.
> 
> At the beginning, it could be just the bare minimum to get it working
> and slowly move over stuff like pm runtime, dmaengine, PIO. Move more
> platforms over to that driver and, eventually, get rid of omap-hsmmc.c
> altogether.
> 
> That way, development can be focussed on generic layers (SDHCI) to which
> OMAP MMC controller is compliant (apart from the VERSION register
> quirk).

About an year back, when I tried using SDHCI for OMAP I ran into issues and was
not able to get it working. IIRC SDHCI_PRESENT_STATE (or OMAP_HSMMC_PSTATE) was
not showing the correct state for card present and is unable to raise an
interrupt when a card is inserted. I didn't debug this further.

It also kept me wondering why gpio interrupt was always used for card detect
instead of using mmci_sdcd line of the controller.

Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Felipe Balbi May 23, 2016, 7:18 a.m. UTC | #12
Hi Kishon,

Kishon Vijay Abraham I <kishon@ti.com> writes:
> Hi Felipe,
>
> On Friday 20 May 2016 12:06 AM, Felipe Balbi wrote:
>> 
>> Hi,
>> 
>> Tony Lindgren <tony@atomide.com> writes:
>>> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160519 01:10]:
>>>> On 05/18/2016 10:30 PM, Tony Lindgren wrote:
>>>>> Ideally the adma support would be a separate loadable module,
>>>>> similar how the cppi41dma is a child of the OTG controller.
>>>>
>>>> The Master DMA is part of the hsmmc IP block. If the same ADMA module is
>>>> present on other IPs it might be beneficial to have a helper library to handle
>>>> it (allocating the descriptor pool, wrinting, updating descriptors, etc).
>>>
>>> OK. Yeah if it's part of the MMC controller it makes no sense to
>>> separate it. So then the conecrns are using alternate DMA
>>> implementations and keeping PM runtime working :)
>>>
>>> BTW, Felipe mentioned that the best thing to do in the long run would
>>> be to set up sdhci-omap.c operating in ADMA mode.
>>>
>>> Felipe, care to summarize what you had in mind?
>> 
>> yeah, just write a new sdhci-omap.c to start moving away from
>> omap-hsmmc.c, just like it was done for 8250-omap.
>> 
>> At the beginning, it could be just the bare minimum to get it working
>> and slowly move over stuff like pm runtime, dmaengine, PIO. Move more
>> platforms over to that driver and, eventually, get rid of omap-hsmmc.c
>> altogether.
>> 
>> That way, development can be focussed on generic layers (SDHCI) to which
>> OMAP MMC controller is compliant (apart from the VERSION register
>> quirk).
>
> About an year back, when I tried using SDHCI for OMAP I ran into
> issues and was not able to get it working. IIRC SDHCI_PRESENT_STATE
> (or OMAP_HSMMC_PSTATE) was not showing the correct state for card
> present and is unable to raise an interrupt when a card is inserted. I
> didn't debug this further.

I'd say this is a bug in hsmmc. I remember seeing some bits in some
TI-specific register (before SDHCI address space starts) which can be
used to keep parts of SDHCI powered on exactly so normal WP and CD pins
work as expected.

In any case, adding support for GPIO-based card detect to generic SDHCI
shouldn't be too difficult :-)

> It also kept me wondering why gpio interrupt was always used for card
> detect instead of using mmci_sdcd line of the controller.

Probably a really, really old bug which nobody ever debugged properly ;-)

ps: you don't need that ADMA2 DT property, btw. There's a bit in another
register which you can check if $this controller was configured with
ADMA2 support or not. IIRC, OMAP5's TRM describes them.
Kishon Vijay Abraham I May 23, 2016, 8 a.m. UTC | #13
Hi Felipe,

On Monday 23 May 2016 12:48 PM, Felipe Balbi wrote:
> 
> Hi Kishon,
> 
> Kishon Vijay Abraham I <kishon@ti.com> writes:
>> Hi Felipe,
>>
>> On Friday 20 May 2016 12:06 AM, Felipe Balbi wrote:
>>>
>>> Hi,
>>>
>>> Tony Lindgren <tony@atomide.com> writes:
>>>> * Peter Ujfalusi <peter.ujfalusi@ti.com> [160519 01:10]:
>>>>> On 05/18/2016 10:30 PM, Tony Lindgren wrote:
>>>>>> Ideally the adma support would be a separate loadable module,
>>>>>> similar how the cppi41dma is a child of the OTG controller.
>>>>>
>>>>> The Master DMA is part of the hsmmc IP block. If the same ADMA module is
>>>>> present on other IPs it might be beneficial to have a helper library to handle
>>>>> it (allocating the descriptor pool, wrinting, updating descriptors, etc).
>>>>
>>>> OK. Yeah if it's part of the MMC controller it makes no sense to
>>>> separate it. So then the conecrns are using alternate DMA
>>>> implementations and keeping PM runtime working :)
>>>>
>>>> BTW, Felipe mentioned that the best thing to do in the long run would
>>>> be to set up sdhci-omap.c operating in ADMA mode.
>>>>
>>>> Felipe, care to summarize what you had in mind?
>>>
>>> yeah, just write a new sdhci-omap.c to start moving away from
>>> omap-hsmmc.c, just like it was done for 8250-omap.
>>>
>>> At the beginning, it could be just the bare minimum to get it working
>>> and slowly move over stuff like pm runtime, dmaengine, PIO. Move more
>>> platforms over to that driver and, eventually, get rid of omap-hsmmc.c
>>> altogether.
>>>
>>> That way, development can be focussed on generic layers (SDHCI) to which
>>> OMAP MMC controller is compliant (apart from the VERSION register
>>> quirk).
>>
>> About an year back, when I tried using SDHCI for OMAP I ran into
>> issues and was not able to get it working. IIRC SDHCI_PRESENT_STATE
>> (or OMAP_HSMMC_PSTATE) was not showing the correct state for card
>> present and is unable to raise an interrupt when a card is inserted. I
>> didn't debug this further.
> 
> I'd say this is a bug in hsmmc. I remember seeing some bits in some
> TI-specific register (before SDHCI address space starts) which can be
> used to keep parts of SDHCI powered on exactly so normal WP and CD pins
> work as expected.
> 
> In any case, adding support for GPIO-based card detect to generic SDHCI
> shouldn't be too difficult :-)
> 
>> It also kept me wondering why gpio interrupt was always used for card
>> detect instead of using mmci_sdcd line of the controller.
> 
> Probably a really, really old bug which nobody ever debugged properly ;-)
> 
> ps: you don't need that ADMA2 DT property, btw. There's a bit in another
> register which you can check if $this controller was configured with
> ADMA2 support or not. IIRC, OMAP5's TRM describes them.

hmm yeah.. Should be the MADMA_EN in MMCHS_HL_HWINFO.

Thanks
Kishon
--
To unsubscribe from this list: send the line "unsubscribe linux-omap" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
diff mbox

Patch

diff --git a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
index 74166a0..eb5ceec2 100644
--- a/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
+++ b/Documentation/devicetree/bindings/mmc/ti-omap-hsmmc.txt
@@ -28,6 +28,7 @@  specifier is required.
 dma-names: List of DMA request names. These strings correspond
 1:1 with the DMA specifiers listed in dmas. The string naming is
 to be "rx" and "tx" for RX and TX DMA requests, respectively.
+ti,use_adma: enable adma2 feature
 
 Examples:
 
diff --git a/drivers/mmc/host/omap_hsmmc.c b/drivers/mmc/host/omap_hsmmc.c
index cc916d5..b4a7d18 100644
--- a/drivers/mmc/host/omap_hsmmc.c
+++ b/drivers/mmc/host/omap_hsmmc.c
@@ -66,6 +66,8 @@ 
 #define OMAP_HSMMC_ISE		0x0138
 #define OMAP_HSMMC_AC12		0x013C
 #define OMAP_HSMMC_CAPA		0x0140
+#define OMAP_HSMMC_ADMAES	0x0154
+#define OMAP_HSMMC_ADMASAL	0x0158
 
 #define VS18			(1 << 26)
 #define VS30			(1 << 25)
@@ -76,6 +78,7 @@ 
 #define SDVS_MASK		0x00000E00
 #define SDVSCLR			0xFFFFF1FF
 #define SDVSDET			0x00000400
+#define DMA_SELECT		(2 << 3)
 #define AUTOIDLE		0x1
 #define SDBP			(1 << 8)
 #define DTO			0xe
@@ -97,6 +100,7 @@ 
 #define FOUR_BIT		(1 << 1)
 #define HSPE			(1 << 2)
 #define IWE			(1 << 24)
+#define DMA_MASTER		(1 << 20)
 #define DDR			(1 << 19)
 #define CLKEXTFREE		(1 << 16)
 #define CTPL			(1 << 11)
@@ -127,10 +131,11 @@ 
 #define DCRC_EN			(1 << 21)
 #define DEB_EN			(1 << 22)
 #define ACE_EN			(1 << 24)
+#define ADMAE_EN		(1 << 24)
 #define CERR_EN			(1 << 28)
 #define BADA_EN			(1 << 29)
 
-#define INT_EN_MASK (BADA_EN | CERR_EN | ACE_EN | DEB_EN | DCRC_EN |\
+#define INT_EN_MASK (BADA_EN | CERR_EN | ADMAE_EN | ACE_EN | DEB_EN | DCRC_EN |\
 		DTO_EN | CIE_EN | CEB_EN | CCRC_EN | CTO_EN | \
 		BRR_EN | BWR_EN | TC_EN | CC_EN)
 
@@ -168,6 +173,25 @@ 
 #define OMAP_HSMMC_WRITE(base, reg, val) \
 	__raw_writel((val), (base) + OMAP_HSMMC_##reg)
 
+struct omap_hsmmc_adma_desc {
+	u8 attr;
+	u8 reserved;
+	u16 len;
+	u32 addr;
+} __packed;
+
+#define ADMA_MAX_LEN			65532
+
+/* 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 omap_hsmmc_next {
 	unsigned int	dma_len;
 	s32		cookie;
@@ -213,6 +237,9 @@  struct omap_hsmmc_host {
 	struct omap_hsmmc_next	next_data;
 	struct	omap_hsmmc_platform_data	*pdata;
 
+	struct omap_hsmmc_adma_desc *adma_desc_table;
+	dma_addr_t              adma_desc_table_addr;
+
 	/* return MMC cover switch state, can be NULL if not supported.
 	 *
 	 * possible return values:
@@ -951,6 +978,19 @@  static struct dma_chan *omap_hsmmc_get_dma_chan(struct omap_hsmmc_host *host,
 	return data->flags & MMC_DATA_WRITE ? host->tx_chan : host->rx_chan;
 }
 
+static void omap_hsmmc_adma_cleanup(struct omap_hsmmc_host *host)
+{
+	u32 val;
+
+	val = OMAP_HSMMC_READ(host->base, HCTL);
+	val &= ~DMA_SELECT;
+	OMAP_HSMMC_WRITE(host->base, HCTL, val);
+
+	val = OMAP_HSMMC_READ(host->base, CON);
+	val &= ~DMA_MASTER;
+	OMAP_HSMMC_WRITE(host->base, CON, val);
+}
+
 static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_request *mrq)
 {
 	int dma_ch;
@@ -963,8 +1003,11 @@  static void omap_hsmmc_request_done(struct omap_hsmmc_host *host, struct mmc_req
 
 	omap_hsmmc_disable_irq(host);
 	/* Do not complete the request if DMA is still in progress */
-	if (mrq->data && dma_ch != -1)
+	if (host->pdata->controller_flags == OMAP_HSMMC_USE_ADMA)
+		omap_hsmmc_adma_cleanup(host);
+	else if (mrq->data && dma_ch != -1)
 		return;
+
 	host->mrq = NULL;
 	mmc_request_done(host->mmc, mrq);
 	pm_runtime_mark_last_busy(host->dev);
@@ -1052,15 +1095,22 @@  static void omap_hsmmc_dma_cleanup(struct omap_hsmmc_host *host, int errno)
 	host->dma_ch = -1;
 	spin_unlock_irqrestore(&host->irq_lock, flags);
 
-	if (dma_ch != -1) {
-		struct dma_chan *chan = omap_hsmmc_get_dma_chan(host, host->data);
-
-		dmaengine_terminate_all(chan);
-		dma_unmap_sg(chan->device->dev,
-			host->data->sg, host->data->sg_len,
+	if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) {
+		if (dma_ch != -1) {
+			struct dma_chan *chan = omap_hsmmc_get_dma_chan(host,
+								host->data);
+			dmaengine_terminate_all(chan);
+			dma_unmap_sg(chan->device->dev,
+				     host->data->sg, host->data->sg_len,
 			omap_hsmmc_get_dma_dir(host, host->data));
 
+			host->data->host_cookie = 0;
+		}
+	} else {
+		dma_unmap_sg(host->dev, host->data->sg, host->data->sg_len,
+			     omap_hsmmc_get_dma_dir(host, host->data));
 		host->data->host_cookie = 0;
+
 	}
 	host->data = NULL;
 }
@@ -1191,6 +1241,14 @@  static void omap_hsmmc_do_irq(struct omap_hsmmc_host *host, int status)
 			}
 			dev_dbg(mmc_dev(host->mmc), "AC12 err: 0x%x\n", ac12);
 		}
+
+		if (status & ADMAE_EN) {
+			u32 val;
+
+			val = OMAP_HSMMC_READ(host->base, ADMAES);
+			dev_dbg(mmc_dev(host->mmc), "ADMA error status: 0x%x\n",
+				val);
+		}
 	}
 
 	OMAP_HSMMC_WRITE(host->base, STAT, status);
@@ -1378,6 +1436,7 @@  static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 				       struct dma_chan *chan)
 {
 	int dma_len;
+	struct device *dev;
 
 	if (!next && data->host_cookie &&
 	    data->host_cookie != host->next_data.cookie) {
@@ -1387,9 +1446,14 @@  static int omap_hsmmc_pre_dma_transfer(struct omap_hsmmc_host *host,
 		data->host_cookie = 0;
 	}
 
+	if (chan)
+		dev = chan->device->dev;
+	else
+		dev = mmc_dev(host->mmc);
+
 	/* Check if next job is already prepared */
 	if (next || data->host_cookie != host->next_data.cookie) {
-		dma_len = dma_map_sg(chan->device->dev, data->sg, data->sg_len,
+		dma_len = dma_map_sg(dev, data->sg, data->sg_len,
 				     omap_hsmmc_get_dma_dir(host, data));
 
 	} else {
@@ -1516,6 +1580,7 @@  static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
 {
 	struct mmc_request *req = host->mrq;
 	struct dma_chan *chan;
+	int val;
 
 	if (!req->data)
 		return;
@@ -1523,10 +1588,66 @@  static void omap_hsmmc_start_dma_transfer(struct omap_hsmmc_host *host)
 				| (req->data->blocks << 16));
 	set_data_timeout(host, req->data->timeout_ns,
 				req->data->timeout_clks);
-	chan = omap_hsmmc_get_dma_chan(host, req->data);
-	dma_async_issue_pending(chan);
+
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) {
+		val = OMAP_HSMMC_READ(host->base, HCTL);
+		val |= DMA_SELECT;
+		OMAP_HSMMC_WRITE(host->base, HCTL, val);
+
+		val = OMAP_HSMMC_READ(host->base, CON);
+		val |= DMA_MASTER;
+		OMAP_HSMMC_WRITE(host->base, CON, val);
+
+		OMAP_HSMMC_WRITE(host->base, ADMASAL,
+				 (u32)host->adma_desc_table_addr);
+	} else {
+		chan = omap_hsmmc_get_dma_chan(host, req->data);
+		dma_async_issue_pending(chan);
+	}
+}
+
+static int omap_hsmmc_write_adma_desc(struct omap_hsmmc_host *host, void *desc,
+				      dma_addr_t addr, u16 len, u8 attr)
+{
+	struct omap_hsmmc_adma_desc *dma_desc = desc;
+
+	dma_desc->len = len;
+	dma_desc->addr = (u32)addr;
+	dma_desc->reserved = 0;
+	dma_desc->attr = attr;
+
+	return 0;
 }
 
+static int omap_hsmmc_setup_adma_transfer(struct omap_hsmmc_host *host,
+					  struct mmc_request *req)
+{
+	struct mmc_data *data = req->data;
+	struct scatterlist *sg;
+	int i;
+	int len;
+	int ret;
+	dma_addr_t addr;
+	struct omap_hsmmc_adma_desc *dma_desc;
+
+	ret = omap_hsmmc_pre_dma_transfer(host, data, NULL, NULL);
+	if (ret)
+		return ret;
+
+	dma_desc = host->adma_desc_table;
+	for_each_sg(data->sg, sg, host->dma_len, i) {
+		addr = sg_dma_address(sg);
+		len = sg_dma_len(sg);
+		WARN_ON(len > ADMA_MAX_LEN);
+		omap_hsmmc_write_adma_desc(host, dma_desc, addr, len,
+					   ADMA_DESC_ATTR_VALID |
+					   ADMA_DESC_TRANSFER_DATA);
+		dma_desc++;
+	}
+	omap_hsmmc_write_adma_desc(host, dma_desc, 0, 0, ADMA_DESC_ATTR_END);
+
+	return 0;
+}
 /*
  * Configure block length for MMC/SD cards and initiate the transfer.
  */
@@ -1547,10 +1668,18 @@  omap_hsmmc_prepare_data(struct omap_hsmmc_host *host, struct mmc_request *req)
 		return 0;
 	}
 
-	ret = omap_hsmmc_setup_dma_transfer(host, req);
-	if (ret != 0) {
-		dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
-		return ret;
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA) {
+		ret = omap_hsmmc_setup_adma_transfer(host, req);
+		if (ret != 0) {
+			dev_err(mmc_dev(host->mmc), "MMC adma setup failed\n");
+			return ret;
+		}
+	} else {
+		ret = omap_hsmmc_setup_dma_transfer(host, req);
+		if (ret != 0) {
+			dev_err(mmc_dev(host->mmc), "MMC start dma failure\n");
+			return ret;
+		}
 	}
 	return 0;
 }
@@ -1560,11 +1689,18 @@  static void omap_hsmmc_post_req(struct mmc_host *mmc, struct mmc_request *mrq,
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
+	struct device *dev;
+	struct dma_chan *c;
 
 	if (data->host_cookie) {
-		struct dma_chan *c = omap_hsmmc_get_dma_chan(host, data);
+		if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)) {
+			c = omap_hsmmc_get_dma_chan(host, mrq->data);
+			dev = c->device->dev;
+		} else {
+			dev = mmc_dev(mmc);
+		}
 
-		dma_unmap_sg(c->device->dev, data->sg, data->sg_len,
+		dma_unmap_sg(dev, data->sg, data->sg_len,
 			     omap_hsmmc_get_dma_dir(host, data));
 		data->host_cookie = 0;
 	}
@@ -1574,13 +1710,15 @@  static void omap_hsmmc_pre_req(struct mmc_host *mmc, struct mmc_request *mrq,
 			       bool is_first_req)
 {
 	struct omap_hsmmc_host *host = mmc_priv(mmc);
+	struct dma_chan *c = NULL;
 
 	if (mrq->data->host_cookie) {
 		mrq->data->host_cookie = 0;
 		return ;
 	}
 
-	struct dma_chan *c = omap_hsmmc_get_dma_chan(host, mrq->data);
+	if (!(host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA))
+		c = omap_hsmmc_get_dma_chan(host, mrq->data);
 
 	if (omap_hsmmc_pre_dma_transfer(host, mrq->data,
 					&host->next_data, c))
@@ -1967,6 +2105,9 @@  static struct omap_hsmmc_platform_data *of_get_hsmmc_pdata(struct device *dev)
 	if (of_find_property(np, "ti,dual-volt", NULL))
 		pdata->controller_flags |= OMAP_HSMMC_SUPPORTS_DUAL_VOLT;
 
+	if (of_find_property(np, "ti,use_adma", NULL))
+		pdata->controller_flags |= OMAP_HSMMC_USE_ADMA;
+
 	pdata->gpio_cd = -EINVAL;
 	pdata->gpio_cod = -EINVAL;
 	pdata->gpio_wp = -EINVAL;
@@ -1992,6 +2133,84 @@  static inline struct omap_hsmmc_platform_data
 }
 #endif
 
+static int omap_hsmmc_adma_init(struct omap_hsmmc_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	host->adma_desc_table = dma_alloc_coherent(host->dev, mmc->max_segs + 1,
+						   &host->adma_desc_table_addr,
+						   GFP_KERNEL);
+	if (!host->adma_desc_table) {
+		dev_err(host->dev, "failed to allocate adma desc table\n");
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void omap_hsmmc_adma_exit(struct omap_hsmmc_host *host)
+{
+	struct mmc_host *mmc = host->mmc;
+
+	dma_free_coherent(host->dev, mmc->max_segs + 1,
+			  host->adma_desc_table, host->adma_desc_table_addr);
+}
+
+static int omap_hsmmc_dma_init(struct omap_hsmmc_host *host)
+{
+	dma_cap_mask_t mask;
+	unsigned int tx_req, rx_req;
+	struct resource *res;
+	struct platform_device *pdev = to_platform_device(host->dev);
+
+	dma_cap_zero(mask);
+	dma_cap_set(DMA_SLAVE, mask);
+
+	if (!pdev->dev.of_node) {
+		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
+		if (!res) {
+			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
+			return -ENXIO;
+		}
+		tx_req = res->start;
+
+		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
+		if (!res) {
+			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
+			return -ENXIO;
+		}
+		rx_req = res->start;
+	}
+
+	host->rx_chan =
+		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+						 &rx_req, &pdev->dev, "rx");
+
+	if (!host->rx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
+		return -ENXIO;
+	}
+
+	host->tx_chan =
+		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
+						 &tx_req, &pdev->dev, "tx");
+
+	if (!host->tx_chan) {
+		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static void omap_hsmmc_dma_exit(struct omap_hsmmc_host *host)
+{
+	if (host->tx_chan)
+		dma_release_channel(host->tx_chan);
+	if (host->rx_chan)
+		dma_release_channel(host->rx_chan);
+}
+
 static int omap_hsmmc_probe(struct platform_device *pdev)
 {
 	struct omap_hsmmc_platform_data *pdata = pdev->dev.platform_data;
@@ -2000,8 +2219,6 @@  static int omap_hsmmc_probe(struct platform_device *pdev)
 	struct resource *res;
 	int ret, irq;
 	const struct of_device_id *match;
-	dma_cap_mask_t mask;
-	unsigned tx_req, rx_req;
 	const struct omap_mmc_of_data *data;
 	void __iomem *base;
 
@@ -2114,7 +2331,10 @@  static int omap_hsmmc_probe(struct platform_device *pdev)
 	mmc->max_blk_size = 512;       /* Block Length at max can be 1024 */
 	mmc->max_blk_count = 0xFFFF;    /* No. of Blocks is 16 bits */
 	mmc->max_req_size = mmc->max_blk_size * mmc->max_blk_count;
-	mmc->max_seg_size = mmc->max_req_size;
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
+		mmc->max_seg_size = ADMA_MAX_LEN;
+	else
+		mmc->max_seg_size = mmc->max_req_size;
 
 	mmc->caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
 		     MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE;
@@ -2130,46 +2350,12 @@  static int omap_hsmmc_probe(struct platform_device *pdev)
 
 	omap_hsmmc_conf_bus_power(host);
 
-	if (!pdev->dev.of_node) {
-		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx");
-		if (!res) {
-			dev_err(mmc_dev(host->mmc), "cannot get DMA TX channel\n");
-			ret = -ENXIO;
-			goto err_irq;
-		}
-		tx_req = res->start;
-
-		res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx");
-		if (!res) {
-			dev_err(mmc_dev(host->mmc), "cannot get DMA RX channel\n");
-			ret = -ENXIO;
-			goto err_irq;
-		}
-		rx_req = res->start;
-	}
-
-	dma_cap_zero(mask);
-	dma_cap_set(DMA_SLAVE, mask);
-
-	host->rx_chan =
-		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
-						 &rx_req, &pdev->dev, "rx");
-
-	if (!host->rx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain RX DMA engine channel\n");
-		ret = -ENXIO;
-		goto err_irq;
-	}
-
-	host->tx_chan =
-		dma_request_slave_channel_compat(mask, omap_dma_filter_fn,
-						 &tx_req, &pdev->dev, "tx");
-
-	if (!host->tx_chan) {
-		dev_err(mmc_dev(host->mmc), "unable to obtain TX DMA engine channel\n");
-		ret = -ENXIO;
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
+		ret = omap_hsmmc_adma_init(host);
+	else
+		ret = omap_hsmmc_dma_init(host);
+	if (ret)
 		goto err_irq;
-	}
 
 	/* Request IRQ for MMC operations */
 	ret = devm_request_irq(&pdev->dev, host->irq, omap_hsmmc_irq, 0,
@@ -2225,11 +2411,11 @@  err_slot_name:
 	mmc_remove_host(mmc);
 err_irq:
 	device_init_wakeup(&pdev->dev, false);
-	if (host->tx_chan)
-		dma_release_channel(host->tx_chan);
-	if (host->rx_chan)
-		dma_release_channel(host->rx_chan);
 	pm_runtime_dont_use_autosuspend(host->dev);
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
+		omap_hsmmc_adma_exit(host);
+	else
+		omap_hsmmc_dma_exit(host);
 	pm_runtime_put_sync(host->dev);
 	pm_runtime_disable(host->dev);
 	if (host->dbclk)
@@ -2248,8 +2434,10 @@  static int omap_hsmmc_remove(struct platform_device *pdev)
 	pm_runtime_get_sync(host->dev);
 	mmc_remove_host(host->mmc);
 
-	dma_release_channel(host->tx_chan);
-	dma_release_channel(host->rx_chan);
+	if (host->pdata->controller_flags & OMAP_HSMMC_USE_ADMA)
+		omap_hsmmc_adma_exit(host);
+	else
+		omap_hsmmc_dma_exit(host);
 
 	pm_runtime_dont_use_autosuspend(host->dev);
 	pm_runtime_put_sync(host->dev);
diff --git a/include/linux/platform_data/hsmmc-omap.h b/include/linux/platform_data/hsmmc-omap.h
index 8e981be..e26013d 100644
--- a/include/linux/platform_data/hsmmc-omap.h
+++ b/include/linux/platform_data/hsmmc-omap.h
@@ -27,6 +27,7 @@ 
 #define OMAP_HSMMC_SUPPORTS_DUAL_VOLT		BIT(0)
 #define OMAP_HSMMC_BROKEN_MULTIBLOCK_READ	BIT(1)
 #define OMAP_HSMMC_SWAKEUP_MISSING		BIT(2)
+#define OMAP_HSMMC_USE_ADMA			BIT(3)
 
 struct omap_hsmmc_dev_attr {
 	u8 flags;