diff mbox

[01/19] mmc: mmci: regroup and define dma operations

Message ID 1528809280-31116-2-git-send-email-ludovic.Barre@st.com (mailing list archive)
State New, archived
Headers show

Commit Message

Ludovic BARRE June 12, 2018, 1:14 p.m. UTC
From: Ludovic Barre <ludovic.barre@st.com>

Prepare mmci driver to manage dma interface by new property.
This patch defines and regroups dma operations for mmci drivers.
mmci_dma_XX prototypes are added to call member of mmci_dma_ops
if not null. Based on legacy need, a mmci dma interface has been
defined with:
-mmci_dma_setup
-mmci_dma_release
-mmci_dma_pre_req
-mmci_dma_start
-mmci_dma_finalize
-mmci_dma_post_req
-mmci_dma_error
-mmci_dma_get_next_data

A dma_priv is added to host structure to contain private dma needs.

Legacy dmaengine functions have been moved with just some adaptation on:
-dma_in_progress moves to dmaengine_priv structure, called in
 dmaengine_error and dmaengine_finalize.
-centralized dma_unmap_sg into dmaengine_unmap, called by
 dmaengine_error and dmaengine_finalize.

Signed-off-by: Ludovic Barre <ludovic.barre@st.com>
---
 drivers/mmc/host/Makefile   |   2 +-
 drivers/mmc/host/mmci.c     | 437 ++----------------------------------------
 drivers/mmc/host/mmci.h     |  95 ++++++++--
 drivers/mmc/host/mmci_dma.c | 450 ++++++++++++++++++++++++++++++++++++++++++++
 drivers/mmc/host/mmci_dma.h |  31 +++
 5 files changed, 579 insertions(+), 436 deletions(-)
 create mode 100644 drivers/mmc/host/mmci_dma.c
 create mode 100644 drivers/mmc/host/mmci_dma.h

Comments

Ulf Hansson July 5, 2018, 3:17 p.m. UTC | #1
On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
> From: Ludovic Barre <ludovic.barre@st.com>
>
> Prepare mmci driver to manage dma interface by new property.
> This patch defines and regroups dma operations for mmci drivers.
> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
> if not null. Based on legacy need, a mmci dma interface has been
> defined with:
> -mmci_dma_setup
> -mmci_dma_release
> -mmci_dma_pre_req
> -mmci_dma_start
> -mmci_dma_finalize
> -mmci_dma_post_req
> -mmci_dma_error
> -mmci_dma_get_next_data

As I suggested for one of the other patches, I would rather turn core
mmci functions into library functions, which can be either invoked
from variant callbacks or assigned directly to them.

In other words, I would leave the functions that you move in this
patch to stay in mmci.c. Although some needs to be re-factored and we
also needs to make some of them available to be called from another
file, hence the functions needs to be shared via mmci.h rather than
being declared static.

Let me take a concrete example on how I would move forward, hopefully
that explains it a bit better. Please see below.

[...]

> -/*
> - * All the DMA operation mode stuff goes inside this ifdef.
> - * This assumes that you have a generic DMA device interface,
> - * no custom DMA interfaces are supported.
> - */
> -#ifdef CONFIG_DMA_ENGINE
> -static void mmci_dma_setup(struct mmci_host *host)
> -{
> -       const char *rxname, *txname;
> -       struct variant_data *variant = host->variant;
> -
> -       host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
> -       host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
> -
> -       /* initialize pre request cookie */
> -       host->next_data.cookie = 1;
> -
> -       /*
> -        * If only an RX channel is specified, the driver will
> -        * attempt to use it bidirectionally, however if it is
> -        * is specified but cannot be located, DMA will be disabled.
> -        */
> -       if (host->dma_rx_channel && !host->dma_tx_channel)
> -               host->dma_tx_channel = host->dma_rx_channel;
> -
> -       if (host->dma_rx_channel)
> -               rxname = dma_chan_name(host->dma_rx_channel);
> -       else
> -               rxname = "none";
> -
> -       if (host->dma_tx_channel)
> -               txname = dma_chan_name(host->dma_tx_channel);
> -       else
> -               txname = "none";
> -
> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
> -                rxname, txname);
> -
> -       /*
> -        * Limit the maximum segment size in any SG entry according to
> -        * the parameters of the DMA engine device.
> -        */
> -       if (host->dma_tx_channel) {
> -               struct device *dev = host->dma_tx_channel->device->dev;
> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
> -
> -               if (max_seg_size < host->mmc->max_seg_size)
> -                       host->mmc->max_seg_size = max_seg_size;
> -       }
> -       if (host->dma_rx_channel) {
> -               struct device *dev = host->dma_rx_channel->device->dev;
> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
> -
> -               if (max_seg_size < host->mmc->max_seg_size)
> -                       host->mmc->max_seg_size = max_seg_size;
> -       }

Everything above shall be left as generic library function,
mmci_dma_setup() and I would share it via mmci.h and thus change it
from being static.

> -
> -       if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
> -               if (dml_hw_init(host, host->mmc->parent->of_node))
> -                       variant->qcom_dml = false;

This piece of code, should be made specific to the qcom variant and
managed though a "mmci_host_ops" callback. The corresponding code in
that callback would then start by invoking mmci_dma_setup(), before it
continues with the qcom specific operations.

For legacy variants, the corresponding callback would be set directly
to mmci_dma_setup() and called through the callback from mmci.c when
needed. There is no need to have a separate file for DMA for the
legacy variants, I think.

[...]

Kind regards
Uffe
Ludovic BARRE July 11, 2018, 9:41 a.m. UTC | #2
On 07/05/2018 05:17 PM, Ulf Hansson wrote:
> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>> From: Ludovic Barre <ludovic.barre@st.com>
>>
>> Prepare mmci driver to manage dma interface by new property.
>> This patch defines and regroups dma operations for mmci drivers.
>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>> if not null. Based on legacy need, a mmci dma interface has been
>> defined with:
>> -mmci_dma_setup
>> -mmci_dma_release
>> -mmci_dma_pre_req
>> -mmci_dma_start
>> -mmci_dma_finalize
>> -mmci_dma_post_req
>> -mmci_dma_error
>> -mmci_dma_get_next_data
> 
> As I suggested for one of the other patches, I would rather turn core
> mmci functions into library functions, which can be either invoked
> from variant callbacks or assigned directly to them.
> 
> In other words, I would leave the functions that you move in this
> patch to stay in mmci.c. Although some needs to be re-factored and we
> also needs to make some of them available to be called from another
> file, hence the functions needs to be shared via mmci.h rather than
> being declared static.

In previous exchange mail "STM32MP1 SDMMC driver review"
we are said:

 >>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
 >>> mmci_qcom_dml.c file and integrate ST dma in.
 >>
 >> stm32 sdmmc has an internal dma, no need to use dmaengine API;
 >> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
 >> should be done with an ops or not.
 >
 >Yes.
 >
 >The Qualcomm variant is also using an internal DMA, hence I thought
 >there may be something we could re-use, or at least have some new
 >common ops for.

It's not crystal clear for me.
Do you always agree with a dma ops which allow to address different
DMA transfer:
-with dmaengine API
-sdmmc idma, without dmaengine API
-...

> 
> Let me take a concrete example on how I would move forward, hopefully
> that explains it a bit better. Please see below.
> 
> [...]
> 
>> -/*
>> - * All the DMA operation mode stuff goes inside this ifdef.
>> - * This assumes that you have a generic DMA device interface,
>> - * no custom DMA interfaces are supported.
>> - */
>> -#ifdef CONFIG_DMA_ENGINE
>> -static void mmci_dma_setup(struct mmci_host *host)
>> -{
>> -       const char *rxname, *txname;
>> -       struct variant_data *variant = host->variant;
>> -
>> -       host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>> -       host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>> -
>> -       /* initialize pre request cookie */
>> -       host->next_data.cookie = 1;
>> -
>> -       /*
>> -        * If only an RX channel is specified, the driver will
>> -        * attempt to use it bidirectionally, however if it is
>> -        * is specified but cannot be located, DMA will be disabled.
>> -        */
>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>> -               host->dma_tx_channel = host->dma_rx_channel;
>> -
>> -       if (host->dma_rx_channel)
>> -               rxname = dma_chan_name(host->dma_rx_channel);
>> -       else
>> -               rxname = "none";
>> -
>> -       if (host->dma_tx_channel)
>> -               txname = dma_chan_name(host->dma_tx_channel);
>> -       else
>> -               txname = "none";
>> -
>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>> -                rxname, txname);
>> -
>> -       /*
>> -        * Limit the maximum segment size in any SG entry according to
>> -        * the parameters of the DMA engine device.
>> -        */
>> -       if (host->dma_tx_channel) {
>> -               struct device *dev = host->dma_tx_channel->device->dev;
>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>> -
>> -               if (max_seg_size < host->mmc->max_seg_size)
>> -                       host->mmc->max_seg_size = max_seg_size;
>> -       }
>> -       if (host->dma_rx_channel) {
>> -               struct device *dev = host->dma_rx_channel->device->dev;
>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>> -
>> -               if (max_seg_size < host->mmc->max_seg_size)
>> -                       host->mmc->max_seg_size = max_seg_size;
>> -       }
> 
> Everything above shall be left as generic library function,
> mmci_dma_setup() and I would share it via mmci.h and thus change it
> from being static.
> 

each interfaces mmci_dma_XXX have very different needs depending
dma_ops (legacy, sdmmc idma)

>> -
>> -       if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
>> -               if (dml_hw_init(host, host->mmc->parent->of_node))
>> -                       variant->qcom_dml = false;
> 
> This piece of code, should be made specific to the qcom variant and
> managed though a "mmci_host_ops" callback. The corresponding code in
> that callback would then start by invoking mmci_dma_setup(), before it
> continues with the qcom specific operations.
> 
> For legacy variants, the corresponding callback would be set directly
> to mmci_dma_setup() and called through the callback from mmci.c when
> needed. There is no need to have a separate file for DMA for the
> legacy variants, I think.
> 
> [...]
> 
> Kind regards
> Uffe
>
Ulf Hansson July 11, 2018, 12:16 p.m. UTC | #3
On 11 July 2018 at 11:41, Ludovic BARRE <ludovic.barre@st.com> wrote:
>
>
> On 07/05/2018 05:17 PM, Ulf Hansson wrote:
>>
>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>
>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>
>>> Prepare mmci driver to manage dma interface by new property.
>>> This patch defines and regroups dma operations for mmci drivers.
>>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>>> if not null. Based on legacy need, a mmci dma interface has been
>>> defined with:
>>> -mmci_dma_setup
>>> -mmci_dma_release
>>> -mmci_dma_pre_req
>>> -mmci_dma_start
>>> -mmci_dma_finalize
>>> -mmci_dma_post_req
>>> -mmci_dma_error
>>> -mmci_dma_get_next_data
>>
>>
>> As I suggested for one of the other patches, I would rather turn core
>> mmci functions into library functions, which can be either invoked
>> from variant callbacks or assigned directly to them.
>>
>> In other words, I would leave the functions that you move in this
>> patch to stay in mmci.c. Although some needs to be re-factored and we
>> also needs to make some of them available to be called from another
>> file, hence the functions needs to be shared via mmci.h rather than
>> being declared static.
>
>
> In previous exchange mail "STM32MP1 SDMMC driver review"
> we are said:
>
>>>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
>>>> mmci_qcom_dml.c file and integrate ST dma in.

Apologize if I may have lead you in a wrong direction, that was not my intent.

However, by looking at $subject patch, your seems to be unnecessarily
shuffling code around. I would like to avoid that.

>>>
>>> stm32 sdmmc has an internal dma, no need to use dmaengine API;
>>> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
>>> should be done with an ops or not.
>>
>>Yes.
>>
>>The Qualcomm variant is also using an internal DMA, hence I thought
>>there may be something we could re-use, or at least have some new
>>common ops for.
>
> It's not crystal clear for me.
> Do you always agree with a dma ops which allow to address different
> DMA transfer:
> -with dmaengine API
> -sdmmc idma, without dmaengine API
> -...

If we can use a mmci ops callback to manage the variant differences,
that would be perfect. That combined with making the existing DMA
functions in mmci.c converted to "library" functions, which the mmci
ops callbacks can call, in order to re-use code.

When that isn't really suitable, we may need to add a "quirk" instead,
which would be specific for that particular variant. Along the lines
of what we already do for variant specifics inside mmci.c.

I think we have to decide on case by case basis, what fits best.

Hope this makes a better explanation. If not, please tell, and I can
take an initial stab and post a patch to show you with code how I mean
to move forward.

>
>
>>
>> Let me take a concrete example on how I would move forward, hopefully
>> that explains it a bit better. Please see below.
>>
>> [...]
>>
>>> -/*
>>> - * All the DMA operation mode stuff goes inside this ifdef.
>>> - * This assumes that you have a generic DMA device interface,
>>> - * no custom DMA interfaces are supported.
>>> - */
>>> -#ifdef CONFIG_DMA_ENGINE
>>> -static void mmci_dma_setup(struct mmci_host *host)
>>> -{
>>> -       const char *rxname, *txname;
>>> -       struct variant_data *variant = host->variant;
>>> -
>>> -       host->dma_rx_channel =
>>> dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>>> -       host->dma_tx_channel =
>>> dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>>> -
>>> -       /* initialize pre request cookie */
>>> -       host->next_data.cookie = 1;
>>> -
>>> -       /*
>>> -        * If only an RX channel is specified, the driver will
>>> -        * attempt to use it bidirectionally, however if it is
>>> -        * is specified but cannot be located, DMA will be disabled.
>>> -        */
>>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>>> -               host->dma_tx_channel = host->dma_rx_channel;
>>> -
>>> -       if (host->dma_rx_channel)
>>> -               rxname = dma_chan_name(host->dma_rx_channel);
>>> -       else
>>> -               rxname = "none";
>>> -
>>> -       if (host->dma_tx_channel)
>>> -               txname = dma_chan_name(host->dma_tx_channel);
>>> -       else
>>> -               txname = "none";
>>> -
>>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>>> -                rxname, txname);
>>> -
>>> -       /*
>>> -        * Limit the maximum segment size in any SG entry according to
>>> -        * the parameters of the DMA engine device.
>>> -        */
>>> -       if (host->dma_tx_channel) {
>>> -               struct device *dev = host->dma_tx_channel->device->dev;
>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>> -
>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>> -                       host->mmc->max_seg_size = max_seg_size;
>>> -       }
>>> -       if (host->dma_rx_channel) {
>>> -               struct device *dev = host->dma_rx_channel->device->dev;
>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>> -
>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>> -                       host->mmc->max_seg_size = max_seg_size;
>>> -       }
>>
>>
>> Everything above shall be left as generic library function,
>> mmci_dma_setup() and I would share it via mmci.h and thus change it
>> from being static.
>>
>
> each interfaces mmci_dma_XXX have very different needs depending
> dma_ops (legacy, sdmmc idma)

Right. This was just one example.

If I understand, what you are suggesting is to make all of them being
variant specific callbacks, so I assume that would solve the problems.
Just to be clear, I have no problem with that.

Although, that doesn't mean we can't re-use existing dma functions in
mmci.c, when that makes sense.

[...]

Kind regards
Uffe
Ludovic BARRE July 12, 2018, 9:09 a.m. UTC | #4
On 07/11/2018 02:16 PM, Ulf Hansson wrote:
> On 11 July 2018 at 11:41, Ludovic BARRE <ludovic.barre@st.com> wrote:
>>
>>
>> On 07/05/2018 05:17 PM, Ulf Hansson wrote:
>>>
>>> On 12 June 2018 at 15:14, Ludovic Barre <ludovic.Barre@st.com> wrote:
>>>>
>>>> From: Ludovic Barre <ludovic.barre@st.com>
>>>>
>>>> Prepare mmci driver to manage dma interface by new property.
>>>> This patch defines and regroups dma operations for mmci drivers.
>>>> mmci_dma_XX prototypes are added to call member of mmci_dma_ops
>>>> if not null. Based on legacy need, a mmci dma interface has been
>>>> defined with:
>>>> -mmci_dma_setup
>>>> -mmci_dma_release
>>>> -mmci_dma_pre_req
>>>> -mmci_dma_start
>>>> -mmci_dma_finalize
>>>> -mmci_dma_post_req
>>>> -mmci_dma_error
>>>> -mmci_dma_get_next_data
>>>
>>>
>>> As I suggested for one of the other patches, I would rather turn core
>>> mmci functions into library functions, which can be either invoked
>>> from variant callbacks or assigned directly to them.
>>>
>>> In other words, I would leave the functions that you move in this
>>> patch to stay in mmci.c. Although some needs to be re-factored and we
>>> also needs to make some of them available to be called from another
>>> file, hence the functions needs to be shared via mmci.h rather than
>>> being declared static.
>>
>>
>> In previous exchange mail "STM32MP1 SDMMC driver review"
>> we are said:
>>
>>>>> -dma variant à should fit in Qualcomm implementation, reuse (rename)
>>>>> mmci_qcom_dml.c file and integrate ST dma in.
> 
> Apologize if I may have lead you in a wrong direction, that was not my intent.
> 
> However, by looking at $subject patch, your seems to be unnecessarily
> shuffling code around. I would like to avoid that.
> 
>>>>
>>>> stm32 sdmmc has an internal dma, no need to use dmaengine API;
>>>> So some modifications in mmci (pre/post request, mmci_dma_xx). perhaps
>>>> should be done with an ops or not.
>>>
>>> Yes.
>>>
>>> The Qualcomm variant is also using an internal DMA, hence I thought
>>> there may be something we could re-use, or at least have some new
>>> common ops for.
>>
>> It's not crystal clear for me.
>> Do you always agree with a dma ops which allow to address different
>> DMA transfer:
>> -with dmaengine API
>> -sdmmc idma, without dmaengine API
>> -...
> 
> If we can use a mmci ops callback to manage the variant differences,
> that would be perfect. That combined with making the existing DMA
> functions in mmci.c converted to "library" functions, which the mmci
> ops callbacks can call, in order to re-use code.
> 
> When that isn't really suitable, we may need to add a "quirk" instead,
> which would be specific for that particular variant. Along the lines
> of what we already do for variant specifics inside mmci.c.
> 
> I think we have to decide on case by case basis, what fits best.
> 
> Hope this makes a better explanation. If not, please tell, and I can
> take an initial stab and post a patch to show you with code how I mean
> to move forward.
> 
>>
>>
>>>
>>> Let me take a concrete example on how I would move forward, hopefully
>>> that explains it a bit better. Please see below.
>>>
>>> [...]
>>>
>>>> -/*
>>>> - * All the DMA operation mode stuff goes inside this ifdef.
>>>> - * This assumes that you have a generic DMA device interface,
>>>> - * no custom DMA interfaces are supported.
>>>> - */
>>>> -#ifdef CONFIG_DMA_ENGINE
>>>> -static void mmci_dma_setup(struct mmci_host *host)
>>>> -{
>>>> -       const char *rxname, *txname;
>>>> -       struct variant_data *variant = host->variant;
>>>> -
>>>> -       host->dma_rx_channel =
>>>> dma_request_slave_channel(mmc_dev(host->mmc), "rx");
>>>> -       host->dma_tx_channel =
>>>> dma_request_slave_channel(mmc_dev(host->mmc), "tx");
>>>> -
>>>> -       /* initialize pre request cookie */
>>>> -       host->next_data.cookie = 1;
>>>> -
>>>> -       /*
>>>> -        * If only an RX channel is specified, the driver will
>>>> -        * attempt to use it bidirectionally, however if it is
>>>> -        * is specified but cannot be located, DMA will be disabled.
>>>> -        */
>>>> -       if (host->dma_rx_channel && !host->dma_tx_channel)
>>>> -               host->dma_tx_channel = host->dma_rx_channel;
>>>> -
>>>> -       if (host->dma_rx_channel)
>>>> -               rxname = dma_chan_name(host->dma_rx_channel);
>>>> -       else
>>>> -               rxname = "none";
>>>> -
>>>> -       if (host->dma_tx_channel)
>>>> -               txname = dma_chan_name(host->dma_tx_channel);
>>>> -       else
>>>> -               txname = "none";
>>>> -
>>>> -       dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
>>>> -                rxname, txname);
>>>> -
>>>> -       /*
>>>> -        * Limit the maximum segment size in any SG entry according to
>>>> -        * the parameters of the DMA engine device.
>>>> -        */
>>>> -       if (host->dma_tx_channel) {
>>>> -               struct device *dev = host->dma_tx_channel->device->dev;
>>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>>> -
>>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>>> -                       host->mmc->max_seg_size = max_seg_size;
>>>> -       }
>>>> -       if (host->dma_rx_channel) {
>>>> -               struct device *dev = host->dma_rx_channel->device->dev;
>>>> -               unsigned int max_seg_size = dma_get_max_seg_size(dev);
>>>> -
>>>> -               if (max_seg_size < host->mmc->max_seg_size)
>>>> -                       host->mmc->max_seg_size = max_seg_size;
>>>> -       }
>>>
>>>
>>> Everything above shall be left as generic library function,
>>> mmci_dma_setup() and I would share it via mmci.h and thus change it
>>> from being static.
>>>
>>
>> each interfaces mmci_dma_XXX have very different needs depending
>> dma_ops (legacy, sdmmc idma)
> 
> Right. This was just one example.
> 
> If I understand, what you are suggesting is to make all of them being
> variant specific callbacks, so I assume that would solve the problems.
> Just to be clear, I have no problem with that.
> 
> Although, that doesn't mean we can't re-use existing dma functions in
> mmci.c, when that makes sense.

Yes, when examine dmaengine_XX ops and sdmmc_idma_XX ops (in patch 01
and 17) there are few common piece of code. So I think we will have same
dma functions pointer in mmci_ops. However, the cookie management may be
shared

> 
> [...]
> 
> Kind regards
> Uffe
>
diff mbox

Patch

diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 85dc132..daecaa98 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -4,7 +4,7 @@ 
 #
 
 obj-$(CONFIG_MMC_ARMMMCI) += armmmci.o
-armmmci-y := mmci.o
+armmmci-y := mmci.o mmci_dma.o
 armmmci-$(CONFIG_MMC_QCOM_DML) += mmci_qcom_dml.o
 obj-$(CONFIG_MMC_PXA)		+= pxamci.o
 obj-$(CONFIG_MMC_MXC)		+= mxcmmc.o
diff --git a/drivers/mmc/host/mmci.c b/drivers/mmc/host/mmci.c
index f184977..8868be0 100644
--- a/drivers/mmc/host/mmci.c
+++ b/drivers/mmc/host/mmci.c
@@ -42,84 +42,13 @@ 
 #include <asm/io.h>
 
 #include "mmci.h"
+#include "mmci_dma.h"
 #include "mmci_qcom_dml.h"
 
 #define DRIVER_NAME "mmci-pl18x"
 
 static unsigned int fmax = 515633;
 
-/**
- * struct variant_data - MMCI variant-specific quirks
- * @clkreg: default value for MCICLOCK register
- * @clkreg_enable: enable value for MMCICLOCK register
- * @clkreg_8bit_bus_enable: enable value for 8 bit bus
- * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
- * @datalength_bits: number of bits in the MMCIDATALENGTH register
- * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
- *	      is asserted (likewise for RX)
- * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
- *		  is asserted (likewise for RX)
- * @data_cmd_enable: enable value for data commands.
- * @st_sdio: enable ST specific SDIO logic
- * @st_clkdiv: true if using a ST-specific clock divider algorithm
- * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
- * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl register
- * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
- *		     register
- * @datactrl_mask_sdio: SDIO enable mask in datactrl register
- * @pwrreg_powerup: power up value for MMCIPOWER register
- * @f_max: maximum clk frequency supported by the controller.
- * @signal_direction: input/out direction of bus signals can be indicated
- * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
- * @busy_detect: true if the variant supports busy detection on DAT0.
- * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
- * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
- *		      indicating that the card is busy
- * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
- *		      getting busy end detection interrupts
- * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
- * @explicit_mclk_control: enable explicit mclk control in driver.
- * @qcom_fifo: enables qcom specific fifo pio read logic.
- * @qcom_dml: enables qcom specific dma glue for dma transfers.
- * @reversed_irq_handling: handle data irq before cmd irq.
- * @mmcimask1: true if variant have a MMCIMASK1 register.
- * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
- *	       register.
- * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
- */
-struct variant_data {
-	unsigned int		clkreg;
-	unsigned int		clkreg_enable;
-	unsigned int		clkreg_8bit_bus_enable;
-	unsigned int		clkreg_neg_edge_enable;
-	unsigned int		datalength_bits;
-	unsigned int		fifosize;
-	unsigned int		fifohalfsize;
-	unsigned int		data_cmd_enable;
-	unsigned int		datactrl_mask_ddrmode;
-	unsigned int		datactrl_mask_sdio;
-	bool			st_sdio;
-	bool			st_clkdiv;
-	bool			blksz_datactrl16;
-	bool			blksz_datactrl4;
-	u32			pwrreg_powerup;
-	u32			f_max;
-	bool			signal_direction;
-	bool			pwrreg_clkgate;
-	bool			busy_detect;
-	u32			busy_dpsm_flag;
-	u32			busy_detect_flag;
-	u32			busy_detect_mask;
-	bool			pwrreg_nopower;
-	bool			explicit_mclk_control;
-	bool			qcom_fifo;
-	bool			qcom_dml;
-	bool			reversed_irq_handling;
-	bool			mmcimask1;
-	u32			start_err;
-	u32			opendrain;
-};
-
 static struct variant_data variant_arm = {
 	.fifosize		= 16 * 4,
 	.fifohalfsize		= 8 * 4,
@@ -130,6 +59,7 @@  static struct variant_data variant_arm = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_arm_extended_fifo = {
@@ -141,6 +71,7 @@  static struct variant_data variant_arm_extended_fifo = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_arm_extended_fifo_hwfc = {
@@ -153,6 +84,7 @@  static struct variant_data variant_arm_extended_fifo_hwfc = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_u300 = {
@@ -171,6 +103,7 @@  static struct variant_data variant_u300 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_nomadik = {
@@ -190,6 +123,7 @@  static struct variant_data variant_nomadik = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_ux500 = {
@@ -215,6 +149,7 @@  static struct variant_data variant_ux500 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_ux500v2 = {
@@ -242,6 +177,7 @@  static struct variant_data variant_ux500v2 = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_OD,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_stm32 = {
@@ -259,6 +195,7 @@  static struct variant_data variant_stm32 = {
 	.f_max			= 48000000,
 	.pwrreg_clkgate		= true,
 	.pwrreg_nopower		= true,
+	.mmci_dma		= &dmaengine,
 };
 
 static struct variant_data variant_qcom = {
@@ -280,6 +217,7 @@  static struct variant_data variant_qcom = {
 	.mmcimask1		= true,
 	.start_err		= MCI_STARTBITERR,
 	.opendrain		= MCI_ROD,
+	.mmci_dma		= &dmaengine,
 };
 
 /* Busy detection for the ST Micro variant */
@@ -355,7 +293,7 @@  static void mmci_write_pwrreg(struct mmci_host *host, u32 pwr)
 /*
  * This must be called with host->lock held
  */
-static void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
+void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl)
 {
 	/* Keep busy mode in DPSM if enabled */
 	datactrl |= host->datactrl_reg & host->variant->busy_dpsm_flag;
@@ -480,281 +418,10 @@  static void mmci_init_sg(struct mmci_host *host, struct mmc_data *data)
 	sg_miter_start(&host->sg_miter, data->sg, data->sg_len, flags);
 }
 
-/*
- * All the DMA operation mode stuff goes inside this ifdef.
- * This assumes that you have a generic DMA device interface,
- * no custom DMA interfaces are supported.
- */
-#ifdef CONFIG_DMA_ENGINE
-static void mmci_dma_setup(struct mmci_host *host)
-{
-	const char *rxname, *txname;
-	struct variant_data *variant = host->variant;
-
-	host->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "rx");
-	host->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc), "tx");
-
-	/* initialize pre request cookie */
-	host->next_data.cookie = 1;
-
-	/*
-	 * If only an RX channel is specified, the driver will
-	 * attempt to use it bidirectionally, however if it is
-	 * is specified but cannot be located, DMA will be disabled.
-	 */
-	if (host->dma_rx_channel && !host->dma_tx_channel)
-		host->dma_tx_channel = host->dma_rx_channel;
-
-	if (host->dma_rx_channel)
-		rxname = dma_chan_name(host->dma_rx_channel);
-	else
-		rxname = "none";
-
-	if (host->dma_tx_channel)
-		txname = dma_chan_name(host->dma_tx_channel);
-	else
-		txname = "none";
-
-	dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
-		 rxname, txname);
-
-	/*
-	 * Limit the maximum segment size in any SG entry according to
-	 * the parameters of the DMA engine device.
-	 */
-	if (host->dma_tx_channel) {
-		struct device *dev = host->dma_tx_channel->device->dev;
-		unsigned int max_seg_size = dma_get_max_seg_size(dev);
-
-		if (max_seg_size < host->mmc->max_seg_size)
-			host->mmc->max_seg_size = max_seg_size;
-	}
-	if (host->dma_rx_channel) {
-		struct device *dev = host->dma_rx_channel->device->dev;
-		unsigned int max_seg_size = dma_get_max_seg_size(dev);
-
-		if (max_seg_size < host->mmc->max_seg_size)
-			host->mmc->max_seg_size = max_seg_size;
-	}
-
-	if (variant->qcom_dml && host->dma_rx_channel && host->dma_tx_channel)
-		if (dml_hw_init(host, host->mmc->parent->of_node))
-			variant->qcom_dml = false;
-}
-
-/*
- * This is used in or so inline it
- * so it can be discarded.
- */
-static inline void mmci_dma_release(struct mmci_host *host)
-{
-	if (host->dma_rx_channel)
-		dma_release_channel(host->dma_rx_channel);
-	if (host->dma_tx_channel)
-		dma_release_channel(host->dma_tx_channel);
-	host->dma_rx_channel = host->dma_tx_channel = NULL;
-}
-
-static void mmci_dma_data_error(struct mmci_host *host)
-{
-	dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
-	dmaengine_terminate_all(host->dma_current);
-	host->dma_in_progress = false;
-	host->dma_current = NULL;
-	host->dma_desc_current = NULL;
-	host->data->host_cookie = 0;
-}
-
-static void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
-{
-	struct dma_chan *chan;
-
-	if (data->flags & MMC_DATA_READ)
-		chan = host->dma_rx_channel;
-	else
-		chan = host->dma_tx_channel;
-
-	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len,
-		     mmc_get_dma_dir(data));
-}
-
-static void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
-{
-	u32 status;
-	int i;
-
-	/* Wait up to 1ms for the DMA to complete */
-	for (i = 0; ; i++) {
-		status = readl(host->base + MMCISTATUS);
-		if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
-			break;
-		udelay(10);
-	}
-
-	/*
-	 * Check to see whether we still have some data left in the FIFO -
-	 * this catches DMA controllers which are unable to monitor the
-	 * DMALBREQ and DMALSREQ signals while allowing us to DMA to non-
-	 * contiguous buffers.  On TX, we'll get a FIFO underrun error.
-	 */
-	if (status & MCI_RXDATAAVLBLMASK) {
-		mmci_dma_data_error(host);
-		if (!data->error)
-			data->error = -EIO;
-	}
-
-	if (!data->host_cookie)
-		mmci_dma_unmap(host, data);
-
-	/*
-	 * Use of DMA with scatter-gather is impossible.
-	 * Give up with DMA and switch back to PIO mode.
-	 */
-	if (status & MCI_RXDATAAVLBLMASK) {
-		dev_err(mmc_dev(host->mmc), "buggy DMA detected. Taking evasive action.\n");
-		mmci_dma_release(host);
-	}
-
-	host->dma_in_progress = false;
-	host->dma_current = NULL;
-	host->dma_desc_current = NULL;
-}
-
-/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
-static int __mmci_dma_prep_data(struct mmci_host *host, struct mmc_data *data,
-				struct dma_chan **dma_chan,
-				struct dma_async_tx_descriptor **dma_desc)
-{
-	struct variant_data *variant = host->variant;
-	struct dma_slave_config conf = {
-		.src_addr = host->phybase + MMCIFIFO,
-		.dst_addr = host->phybase + MMCIFIFO,
-		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
-		.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
-		.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
-		.device_fc = false,
-	};
-	struct dma_chan *chan;
-	struct dma_device *device;
-	struct dma_async_tx_descriptor *desc;
-	int nr_sg;
-	unsigned long flags = DMA_CTRL_ACK;
-
-	if (data->flags & MMC_DATA_READ) {
-		conf.direction = DMA_DEV_TO_MEM;
-		chan = host->dma_rx_channel;
-	} else {
-		conf.direction = DMA_MEM_TO_DEV;
-		chan = host->dma_tx_channel;
-	}
-
-	/* If there's no DMA channel, fall back to PIO */
-	if (!chan)
-		return -EINVAL;
-
-	/* If less than or equal to the fifo size, don't bother with DMA */
-	if (data->blksz * data->blocks <= variant->fifosize)
-		return -EINVAL;
-
-	device = chan->device;
-	nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
-			   mmc_get_dma_dir(data));
-	if (nr_sg == 0)
-		return -EINVAL;
-
-	if (host->variant->qcom_dml)
-		flags |= DMA_PREP_INTERRUPT;
-
-	dmaengine_slave_config(chan, &conf);
-	desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
-					    conf.direction, flags);
-	if (!desc)
-		goto unmap_exit;
-
-	*dma_chan = chan;
-	*dma_desc = desc;
-
-	return 0;
-
- unmap_exit:
-	dma_unmap_sg(device->dev, data->sg, data->sg_len,
-		     mmc_get_dma_dir(data));
-	return -ENOMEM;
-}
-
-static inline int mmci_dma_prep_data(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-	/* Check if next job is already prepared. */
-	if (host->dma_current && host->dma_desc_current)
-		return 0;
-
-	/* No job were prepared thus do it now. */
-	return __mmci_dma_prep_data(host, data, &host->dma_current,
-				    &host->dma_desc_current);
-}
-
-static inline int mmci_dma_prep_next(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-	struct mmci_host_next *nd = &host->next_data;
-	return __mmci_dma_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
-}
-
-static int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
-{
-	int ret;
-	struct mmc_data *data = host->data;
-
-	ret = mmci_dma_prep_data(host, host->data);
-	if (ret)
-		return ret;
-
-	/* Okay, go for it. */
-	dev_vdbg(mmc_dev(host->mmc),
-		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
-		 data->sg_len, data->blksz, data->blocks, data->flags);
-	host->dma_in_progress = true;
-	dmaengine_submit(host->dma_desc_current);
-	dma_async_issue_pending(host->dma_current);
-
-	if (host->variant->qcom_dml)
-		dml_start_xfer(host, data);
-
-	datactrl |= MCI_DPSM_DMAENABLE;
-
-	/* Trigger the DMA transfer */
-	mmci_write_datactrlreg(host, datactrl);
-
-	/*
-	 * Let the MMCI say when the data is ended and it's time
-	 * to fire next DMA request. When that happens, MMCI will
-	 * call mmci_data_end()
-	 */
-	writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
-	       host->base + MMCIMASK0);
-	return 0;
-}
-
-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
-{
-	struct mmci_host_next *next = &host->next_data;
-
-	WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
-	WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
-
-	host->dma_desc_current = next->dma_desc;
-	host->dma_current = next->dma_chan;
-	next->dma_desc = NULL;
-	next->dma_chan = NULL;
-}
-
 static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)
 {
 	struct mmci_host *host = mmc_priv(mmc);
 	struct mmc_data *data = mrq->data;
-	struct mmci_host_next *nd = &host->next_data;
 
 	if (!data)
 		return;
@@ -764,8 +431,7 @@  static void mmci_pre_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	if (mmci_validate_data(host, data))
 		return;
 
-	if (!mmci_dma_prep_next(host, data))
-		data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
+	mmci_dma_pre_req(host, data);
 }
 
 static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
@@ -777,67 +443,9 @@  static void mmci_post_request(struct mmc_host *mmc, struct mmc_request *mrq,
 	if (!data || !data->host_cookie)
 		return;
 
-	mmci_dma_unmap(host, data);
-
-	if (err) {
-		struct mmci_host_next *next = &host->next_data;
-		struct dma_chan *chan;
-		if (data->flags & MMC_DATA_READ)
-			chan = host->dma_rx_channel;
-		else
-			chan = host->dma_tx_channel;
-		dmaengine_terminate_all(chan);
-
-		if (host->dma_desc_current == next->dma_desc)
-			host->dma_desc_current = NULL;
-
-		if (host->dma_current == next->dma_chan) {
-			host->dma_in_progress = false;
-			host->dma_current = NULL;
-		}
-
-		next->dma_desc = NULL;
-		next->dma_chan = NULL;
-		data->host_cookie = 0;
-	}
-}
-
-#else
-/* Blank functions if the DMA engine is not available */
-static void mmci_get_next_data(struct mmci_host *host, struct mmc_data *data)
-{
-}
-static inline void mmci_dma_setup(struct mmci_host *host)
-{
-}
-
-static inline void mmci_dma_release(struct mmci_host *host)
-{
-}
-
-static inline void mmci_dma_unmap(struct mmci_host *host, struct mmc_data *data)
-{
-}
-
-static inline void mmci_dma_finalize(struct mmci_host *host,
-				     struct mmc_data *data)
-{
-}
-
-static inline void mmci_dma_data_error(struct mmci_host *host)
-{
-}
-
-static inline int mmci_dma_start_data(struct mmci_host *host, unsigned int datactrl)
-{
-	return -ENOSYS;
+	mmci_dma_post_req(host, data, err);
 }
 
-#define mmci_pre_request NULL
-#define mmci_post_request NULL
-
-#endif
-
 static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 {
 	struct variant_data *variant = host->variant;
@@ -904,7 +512,7 @@  static void mmci_start_data(struct mmci_host *host, struct mmc_data *data)
 	 * Attempt to use DMA operation mode, if this
 	 * should fail, fall back to PIO mode
 	 */
-	if (!mmci_dma_start_data(host, datactrl))
+	if (!mmci_dma_start(host, datactrl))
 		return;
 
 	/* IRQ mode, map the SG list for CPU reading/writing */
@@ -979,10 +587,7 @@  mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		u32 remain, success;
 
 		/* Terminate the DMA transfer */
-		if (dma_inprogress(host)) {
-			mmci_dma_data_error(host);
-			mmci_dma_unmap(host, data);
-		}
+		mmci_dma_error(host);
 
 		/*
 		 * Calculate how far we are into the transfer.  Note that
@@ -1020,8 +625,8 @@  mmci_data_irq(struct mmci_host *host, struct mmc_data *data,
 		dev_err(mmc_dev(host->mmc), "stray MCI_DATABLOCKEND interrupt\n");
 
 	if (status & MCI_DATAEND || data->error) {
-		if (dma_inprogress(host))
-			mmci_dma_finalize(host, data);
+		mmci_dma_finalize(host, data);
+
 		mmci_stop_data(host);
 
 		if (!data->error)
@@ -1128,10 +733,8 @@  mmci_cmd_irq(struct mmci_host *host, struct mmc_command *cmd,
 	if ((!sbc && !cmd->data) || cmd->error) {
 		if (host->data) {
 			/* Terminate the DMA transfer */
-			if (dma_inprogress(host)) {
-				mmci_dma_data_error(host);
-				mmci_dma_unmap(host, host->data);
-			}
+			mmci_dma_error(host);
+
 			mmci_stop_data(host);
 		}
 		mmci_request_end(host, host->mrq);
@@ -1399,7 +1002,7 @@  static void mmci_request(struct mmc_host *mmc, struct mmc_request *mrq)
 	host->mrq = mrq;
 
 	if (mrq->data)
-		mmci_get_next_data(host, mrq->data);
+		mmci_dma_get_next_data(host, mrq->data);
 
 	if (mrq->data && mrq->data->flags & MMC_DATA_READ)
 		mmci_start_data(host, mrq->data);
diff --git a/drivers/mmc/host/mmci.h b/drivers/mmc/host/mmci.h
index f91cdf7..a73bb98 100644
--- a/drivers/mmc/host/mmci.h
+++ b/drivers/mmc/host/mmci.h
@@ -195,13 +195,81 @@ 
 #define MMCI_PINCTRL_STATE_OPENDRAIN "opendrain"
 
 struct clk;
-struct variant_data;
 struct dma_chan;
 
-struct mmci_host_next {
-	struct dma_async_tx_descriptor	*dma_desc;
-	struct dma_chan			*dma_chan;
-	s32				cookie;
+/**
+ * struct variant_data - MMCI variant-specific quirks
+ * @clkreg: default value for MCICLOCK register
+ * @clkreg_enable: enable value for MMCICLOCK register
+ * @clkreg_8bit_bus_enable: enable value for 8 bit bus
+ * @clkreg_neg_edge_enable: enable value for inverted data/cmd output
+ * @datalength_bits: number of bits in the MMCIDATALENGTH register
+ * @fifosize: number of bytes that can be written when MMCI_TXFIFOEMPTY
+ *	      is asserted (likewise for RX)
+ * @fifohalfsize: number of bytes that can be written when MCI_TXFIFOHALFEMPTY
+ *		  is asserted (likewise for RX)
+ * @data_cmd_enable: enable value for data commands.
+ * @st_sdio: enable ST specific SDIO logic
+ * @st_clkdiv: true if using a ST-specific clock divider algorithm
+ * @datactrl_mask_ddrmode: ddr mode mask in datactrl register.
+ * @blksz_datactrl16: true if Block size is at b16..b30 position in datactrl
+ *                    register
+ * @blksz_datactrl4: true if Block size is at b4..b16 position in datactrl
+ *		     register
+ * @datactrl_mask_sdio: SDIO enable mask in datactrl register
+ * @pwrreg_powerup: power up value for MMCIPOWER register
+ * @f_max: maximum clk frequency supported by the controller.
+ * @signal_direction: input/out direction of bus signals can be indicated
+ * @pwrreg_clkgate: MMCIPOWER register must be used to gate the clock
+ * @busy_detect: true if the variant supports busy detection on DAT0.
+ * @busy_dpsm_flag: bitmask enabling busy detection in the DPSM
+ * @busy_detect_flag: bitmask identifying the bit in the MMCISTATUS register
+ *		      indicating that the card is busy
+ * @busy_detect_mask: bitmask identifying the bit in the MMCIMASK0 to mask for
+ *		      getting busy end detection interrupts
+ * @pwrreg_nopower: bits in MMCIPOWER don't controls ext. power supply
+ * @explicit_mclk_control: enable explicit mclk control in driver.
+ * @qcom_fifo: enables qcom specific fifo pio read logic.
+ * @qcom_dml: enables qcom specific dma glue for dma transfers.
+ * @reversed_irq_handling: handle data irq before cmd irq.
+ * @mmcimask1: true if variant have a MMCIMASK1 register.
+ * @start_err: bitmask identifying the STARTBITERR bit inside MMCISTATUS
+ *	       register.
+ * @opendrain: bitmask identifying the OPENDRAIN bit inside MMCIPOWER register
+ * @mmci_dma: Pointer to platform-specific DMA callbacks.
+ */
+struct variant_data {
+	unsigned int		clkreg;
+	unsigned int		clkreg_enable;
+	unsigned int		clkreg_8bit_bus_enable;
+	unsigned int		clkreg_neg_edge_enable;
+	unsigned int		datalength_bits;
+	unsigned int		fifosize;
+	unsigned int		fifohalfsize;
+	unsigned int		data_cmd_enable;
+	unsigned int		datactrl_mask_ddrmode;
+	unsigned int		datactrl_mask_sdio;
+	bool			st_sdio;
+	bool			st_clkdiv;
+	bool			blksz_datactrl16;
+	bool			blksz_datactrl4;
+	u32			pwrreg_powerup;
+	u32			f_max;
+	bool			signal_direction;
+	bool			pwrreg_clkgate;
+	bool			busy_detect;
+	u32			busy_dpsm_flag;
+	u32			busy_detect_flag;
+	u32			busy_detect_mask;
+	bool			pwrreg_nopower;
+	bool			explicit_mclk_control;
+	bool			qcom_fifo;
+	bool			qcom_dml;
+	bool			reversed_irq_handling;
+	bool			mmcimask1;
+	u32			start_err;
+	u32			opendrain;
+	struct mmci_dma_ops	*mmci_dma;
 };
 
 struct mmci_host {
@@ -244,18 +312,9 @@  struct mmci_host {
 	unsigned int		size;
 	int (*get_rx_fifocnt)(struct mmci_host *h, u32 status, int remain);
 
-#ifdef CONFIG_DMA_ENGINE
-	/* DMA stuff */
-	struct dma_chan		*dma_current;
-	struct dma_chan		*dma_rx_channel;
-	struct dma_chan		*dma_tx_channel;
-	struct dma_async_tx_descriptor	*dma_desc_current;
-	struct mmci_host_next	next_data;
-	bool			dma_in_progress;
-
-#define dma_inprogress(host)	((host)->dma_in_progress)
-#else
-#define dma_inprogress(host)	(0)
-#endif
+	void			*dma_priv;
 };
 
+extern struct mmci_dma_ops dmaengine;
+
+void mmci_write_datactrlreg(struct mmci_host *host, u32 datactrl);
diff --git a/drivers/mmc/host/mmci_dma.c b/drivers/mmc/host/mmci_dma.c
new file mode 100644
index 0000000..98a542d
--- /dev/null
+++ b/drivers/mmc/host/mmci_dma.c
@@ -0,0 +1,450 @@ 
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Ludovic.barre@st.com for STMicroelectronics.
+ */
+#include <linux/delay.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/scatterlist.h>
+
+#include "mmci.h"
+#include "mmci_dma.h"
+#include "mmci_qcom_dml.h"
+
+int mmci_dma_setup(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->setup)
+		return mmci_dma->setup(host);
+
+	return 0;
+}
+
+void mmci_dma_release(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->release)
+		mmci_dma->release(host);
+}
+
+void mmci_dma_pre_req(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->pre_req)
+		mmci_dma->pre_req(host, data);
+}
+
+int mmci_dma_start(struct mmci_host *host, unsigned int datactrl)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->start)
+		return mmci_dma->start(host, datactrl);
+
+	return -EINVAL;
+}
+
+void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->finalize)
+		mmci_dma->finalize(host, data);
+}
+
+void mmci_dma_post_req(struct mmci_host *host,
+		       struct mmc_data *data, int err)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->post_req)
+		mmci_dma->post_req(host, data, err);
+}
+
+void mmci_dma_error(struct mmci_host *host)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->error)
+		mmci_dma->error(host);
+}
+
+void mmci_dma_get_next_data(struct mmci_host *host, struct mmc_data *data)
+{
+	struct mmci_dma_ops *mmci_dma = host->variant->mmci_dma;
+
+	if (mmci_dma && mmci_dma->get_next_data)
+		mmci_dma->get_next_data(host, data);
+}
+
+#ifdef CONFIG_DMA_ENGINE
+struct dmaengine_next {
+	struct dma_async_tx_descriptor *dma_desc;
+	struct dma_chan	*dma_chan;
+	s32 cookie;
+};
+
+struct dmaengine_priv {
+	struct dma_chan	*dma_current;
+	struct dma_chan	*dma_rx_channel;
+	struct dma_chan	*dma_tx_channel;
+	struct dma_async_tx_descriptor	*dma_desc_current;
+	struct dmaengine_next next_data;
+	bool dma_in_progress;
+};
+
+#define dma_inprogress(dmae) ((dmae)->dma_in_progress)
+
+static int dmaengine_setup(struct mmci_host *host)
+{
+	const char *rxname, *txname;
+	struct variant_data *variant = host->variant;
+	struct dmaengine_priv *dmae;
+
+	dmae = devm_kzalloc(mmc_dev(host->mmc), sizeof(*dmae), GFP_KERNEL);
+	if (!dmae)
+		return -ENOMEM;
+
+	host->dma_priv = dmae;
+
+	dmae->dma_rx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
+							 "rx");
+	dmae->dma_tx_channel = dma_request_slave_channel(mmc_dev(host->mmc),
+							 "tx");
+
+	/* initialize pre request cookie */
+	dmae->next_data.cookie = 1;
+
+	/*
+	 * If only an RX channel is specified, the driver will
+	 * attempt to use it bidirectionally, however if it is
+	 * is specified but cannot be located, DMA will be disabled.
+	 */
+	if (dmae->dma_rx_channel && !dmae->dma_tx_channel)
+		dmae->dma_tx_channel = dmae->dma_rx_channel;
+
+	if (dmae->dma_rx_channel)
+		rxname = dma_chan_name(dmae->dma_rx_channel);
+	else
+		rxname = "none";
+
+	if (dmae->dma_tx_channel)
+		txname = dma_chan_name(dmae->dma_tx_channel);
+	else
+		txname = "none";
+
+	dev_info(mmc_dev(host->mmc), "DMA channels RX %s, TX %s\n",
+		 rxname, txname);
+
+	/*
+	 * Limit the maximum segment size in any SG entry according to
+	 * the parameters of the DMA engine device.
+	 */
+	if (dmae->dma_tx_channel) {
+		struct device *dev = dmae->dma_tx_channel->device->dev;
+		unsigned int max_seg_size = dma_get_max_seg_size(dev);
+
+		if (max_seg_size < host->mmc->max_seg_size)
+			host->mmc->max_seg_size = max_seg_size;
+	}
+	if (dmae->dma_rx_channel) {
+		struct device *dev = dmae->dma_rx_channel->device->dev;
+		unsigned int max_seg_size = dma_get_max_seg_size(dev);
+
+		if (max_seg_size < host->mmc->max_seg_size)
+			host->mmc->max_seg_size = max_seg_size;
+	}
+
+	if (variant->qcom_dml && dmae->dma_rx_channel && dmae->dma_tx_channel)
+		if (dml_hw_init(host, host->mmc->parent->of_node))
+			variant->qcom_dml = false;
+
+	return 0;
+}
+
+static inline void dmaengine_release(struct mmci_host *host)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	if (dmae->dma_rx_channel)
+		dma_release_channel(dmae->dma_rx_channel);
+	if (dmae->dma_tx_channel)
+		dma_release_channel(dmae->dma_tx_channel);
+
+	dmae->dma_rx_channel = dmae->dma_tx_channel = NULL;
+}
+
+static void dmaengine_unmap(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dma_chan *chan;
+
+	if (data->flags & MMC_DATA_READ)
+		chan = dmae->dma_rx_channel;
+	else
+		chan = dmae->dma_tx_channel;
+
+	dma_unmap_sg(chan->device->dev, data->sg, data->sg_len,
+		     mmc_get_dma_dir(data));
+}
+
+static void dmaengine_error(struct mmci_host *host)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	if (!dma_inprogress(dmae))
+		return;
+
+	dev_err(mmc_dev(host->mmc), "error during DMA transfer!\n");
+	dmaengine_terminate_all(dmae->dma_current);
+	dmae->dma_in_progress = false;
+	dmae->dma_current = NULL;
+	dmae->dma_desc_current = NULL;
+	host->data->host_cookie = 0;
+
+	dmaengine_unmap(host, host->data);
+}
+
+static void dmaengine_finalize(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	u32 status;
+	int i;
+
+	if (!dma_inprogress(dmae))
+		return;
+
+	/* Wait up to 1ms for the DMA to complete */
+	for (i = 0; ; i++) {
+		status = readl(host->base + MMCISTATUS);
+		if (!(status & MCI_RXDATAAVLBLMASK) || i >= 100)
+			break;
+		udelay(10);
+	}
+
+	/*
+	 * Check to see whether we still have some data left in the FIFO -
+	 * this catches DMA controllers which are unable to monitor the
+	 * DMALBREQ and DMALSREQ signals while allowing us to DMA to non-
+	 * contiguous buffers.  On TX, we'll get a FIFO underrun error.
+	 */
+	if (status & MCI_RXDATAAVLBLMASK) {
+		dmaengine_error(host);
+		if (!data->error)
+			data->error = -EIO;
+	}
+
+	if (!data->host_cookie)
+		dmaengine_unmap(host, data);
+
+	/*
+	 * Use of DMA with scatter-gather is impossible.
+	 * Give up with DMA and switch back to PIO mode.
+	 */
+	if (status & MCI_RXDATAAVLBLMASK) {
+		dev_err(mmc_dev(host->mmc),
+			"buggy DMA detected. Taking evasive action.\n");
+		dmaengine_release(host);
+	}
+
+	dmae->dma_in_progress = false;
+	dmae->dma_current = NULL;
+	dmae->dma_desc_current = NULL;
+}
+
+/* prepares DMA channel and DMA descriptor, returns non-zero on failure */
+static int __dmaengine_prep_data(struct mmci_host *host, struct mmc_data *data,
+				 struct dma_chan **dma_chan,
+				 struct dma_async_tx_descriptor **dma_desc)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct variant_data *variant = host->variant;
+	struct dma_slave_config conf = {
+		.src_addr = host->phybase + MMCIFIFO,
+		.dst_addr = host->phybase + MMCIFIFO,
+		.src_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+		.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES,
+		.src_maxburst = variant->fifohalfsize >> 2, /* # of words */
+		.dst_maxburst = variant->fifohalfsize >> 2, /* # of words */
+		.device_fc = false,
+	};
+	struct dma_chan *chan;
+	struct dma_device *device;
+	struct dma_async_tx_descriptor *desc;
+	int nr_sg;
+	unsigned long flags = DMA_CTRL_ACK;
+
+	if (data->flags & MMC_DATA_READ) {
+		conf.direction = DMA_DEV_TO_MEM;
+		chan = dmae->dma_rx_channel;
+	} else {
+		conf.direction = DMA_MEM_TO_DEV;
+		chan = dmae->dma_tx_channel;
+	}
+
+	/* If there's no DMA channel, fall back to PIO */
+	if (!chan)
+		return -EINVAL;
+
+	/* If less than or equal to the fifo size, don't bother with DMA */
+	if (data->blksz * data->blocks <= variant->fifosize)
+		return -EINVAL;
+
+	device = chan->device;
+	nr_sg = dma_map_sg(device->dev, data->sg, data->sg_len,
+			   mmc_get_dma_dir(data));
+	if (nr_sg == 0)
+		return -EINVAL;
+
+	if (host->variant->qcom_dml)
+		flags |= DMA_PREP_INTERRUPT;
+
+	dmaengine_slave_config(chan, &conf);
+	desc = dmaengine_prep_slave_sg(chan, data->sg, nr_sg,
+				       conf.direction, flags);
+	if (!desc)
+		goto unmap_exit;
+
+	*dma_chan = chan;
+	*dma_desc = desc;
+
+	return 0;
+
+ unmap_exit:
+	dmaengine_unmap(host, data);
+	return -ENOMEM;
+}
+
+static inline int dmaengine_prep_data(struct mmci_host *host,
+				      struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	/* Check if next job is already prepared. */
+	if (dmae->dma_current && dmae->dma_desc_current)
+		return 0;
+
+	/* No job were prepared thus do it now. */
+	return __dmaengine_prep_data(host, data, &dmae->dma_current,
+				     &dmae->dma_desc_current);
+}
+
+static inline int dmaengine_prep_next(struct mmci_host *host,
+				      struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *nd = &dmae->next_data;
+
+	return __dmaengine_prep_data(host, data, &nd->dma_chan, &nd->dma_desc);
+}
+
+static int dmaengine_start(struct mmci_host *host, unsigned int datactrl)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct mmc_data *data = host->data;
+	int ret;
+
+	ret = dmaengine_prep_data(host, host->data);
+	if (ret)
+		return ret;
+
+	/* Okay, go for it. */
+	dev_vdbg(mmc_dev(host->mmc),
+		 "Submit MMCI DMA job, sglen %d blksz %04x blks %04x flags %08x\n",
+		 data->sg_len, data->blksz, data->blocks, data->flags);
+	dmae->dma_in_progress = true;
+	dmaengine_submit(dmae->dma_desc_current);
+	dma_async_issue_pending(dmae->dma_current);
+
+	if (host->variant->qcom_dml)
+		dml_start_xfer(host, data);
+
+	datactrl |= MCI_DPSM_DMAENABLE;
+
+	/* Trigger the DMA transfer */
+	mmci_write_datactrlreg(host, datactrl);
+
+	/*
+	 * Let the MMCI say when the data is ended and it's time
+	 * to fire next DMA request. When that happens, MMCI will
+	 * call mmci_data_end()
+	 */
+	writel(readl(host->base + MMCIMASK0) | MCI_DATAENDMASK,
+	       host->base + MMCIMASK0);
+	return 0;
+}
+
+static void dmaengine_get_next_data(struct mmci_host *host,
+				    struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *next = &dmae->next_data;
+
+	WARN_ON(data->host_cookie && data->host_cookie != next->cookie);
+	WARN_ON(!data->host_cookie && (next->dma_desc || next->dma_chan));
+
+	dmae->dma_desc_current = next->dma_desc;
+	dmae->dma_current = next->dma_chan;
+	next->dma_desc = NULL;
+	next->dma_chan = NULL;
+}
+
+static void dmaengine_pre_req(struct mmci_host *host, struct mmc_data *data)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+	struct dmaengine_next *nd = &dmae->next_data;
+
+	if (!dmaengine_prep_next(host, data))
+		data->host_cookie = ++nd->cookie < 0 ? 1 : nd->cookie;
+}
+
+static void dmaengine_post_req(struct mmci_host *host,
+			       struct mmc_data *data, int err)
+{
+	struct dmaengine_priv *dmae = host->dma_priv;
+
+	dmaengine_unmap(host, data);
+
+	if (err) {
+		struct dmaengine_next *next = &dmae->next_data;
+		struct dma_chan *chan;
+
+		if (data->flags & MMC_DATA_READ)
+			chan = dmae->dma_rx_channel;
+		else
+			chan = dmae->dma_tx_channel;
+		dmaengine_terminate_all(chan);
+
+		if (dmae->dma_desc_current == next->dma_desc)
+			dmae->dma_desc_current = NULL;
+
+		if (dmae->dma_current == next->dma_chan) {
+			dmae->dma_in_progress = false;
+			dmae->dma_current = NULL;
+		}
+
+		next->dma_desc = NULL;
+		next->dma_chan = NULL;
+		data->host_cookie = 0;
+	}
+}
+
+struct mmci_dma_ops dmaengine = {
+	.setup = dmaengine_setup,
+	.release = dmaengine_release,
+	.pre_req = dmaengine_pre_req,
+	.start = dmaengine_start,
+	.finalize = dmaengine_finalize,
+	.post_req = dmaengine_post_req,
+	.error = dmaengine_error,
+	.get_next_data = dmaengine_get_next_data,
+};
+#else
+struct mmci_dma_ops dmaengine = {};
+#endif
diff --git a/drivers/mmc/host/mmci_dma.h b/drivers/mmc/host/mmci_dma.h
new file mode 100644
index 0000000..33e4e8a
--- /dev/null
+++ b/drivers/mmc/host/mmci_dma.h
@@ -0,0 +1,31 @@ 
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (C) STMicroelectronics 2018 - All Rights Reserved
+ * Author: Ludovic.barre@st.com for STMicroelectronics.
+ */
+#ifndef __MMC_DMA_H__
+#define __MMC_DMA_H__
+
+struct mmci_dma_ops {
+	int (*setup)(struct mmci_host *host);
+	void (*release)(struct mmci_host *host);
+	void (*pre_req)(struct mmci_host *host, struct mmc_data *data);
+	int (*start)(struct mmci_host *host, unsigned int datactrl);
+	void (*finalize)(struct mmci_host *host, struct mmc_data *data);
+	void (*post_req)(struct mmci_host *host,
+			 struct mmc_data *data, int err);
+	void (*error)(struct mmci_host *host);
+	void (*get_next_data)(struct mmci_host *host, struct mmc_data *data);
+};
+
+int mmci_dma_setup(struct mmci_host *host);
+int mmci_dma_start(struct mmci_host *host, unsigned int datactrl);
+void mmci_dma_release(struct mmci_host *host);
+void mmci_dma_pre_req(struct mmci_host *host, struct mmc_data *data);
+void mmci_dma_finalize(struct mmci_host *host, struct mmc_data *data);
+void mmci_dma_post_req(struct mmci_host *host,
+		       struct mmc_data *data, int err);
+void mmci_dma_error(struct mmci_host *host);
+void mmci_dma_get_next_data(struct mmci_host *host, struct mmc_data *data);
+
+#endif /* __MMC_DMA_H__ */