DMA Engine development
 help / color / mirror / Atom feed
* [v3,3/7] dt-bindings: stm32-mdma: Add DMA/MDMA chaining support bindings
From: Pierre Yves MORDRET @ 2018-10-09  8:17 UTC (permalink / raw)
  To: Vinod
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

On 10/07/2018 04:59 PM, Vinod wrote:
> On 28-09-18, 15:01, Pierre-Yves MORDRET wrote:
>> From: M'boumba Cedric Madianga <cedric.madianga@gmail.com>
>>
>> This patch adds the description of the 2 properties needed to support M2M
>> transfer triggered by STM32 DMA when his transfer is complete.
>>
>> Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
>> ---
>>   Version history:
>>     v3:
>>     v2:
>>        * rework content
>>     v1:
>>        * Initial
>> ---
>> ---
>>  Documentation/devicetree/bindings/dma/stm32-mdma.txt | 12 ++++++++----
>>  1 file changed, 8 insertions(+), 4 deletions(-)
>>
>> diff --git a/Documentation/devicetree/bindings/dma/stm32-mdma.txt b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
>> index d18772d..27c2812 100644
>> --- a/Documentation/devicetree/bindings/dma/stm32-mdma.txt
>> +++ b/Documentation/devicetree/bindings/dma/stm32-mdma.txt
>> @@ -10,7 +10,7 @@ Required properties:
>>  - interrupts: Should contain the MDMA interrupt.
>>  - clocks: Should contain the input clock of the DMA instance.
>>  - resets: Reference to a reset controller asserting the DMA controller.
>> -- #dma-cells : Must be <5>. See DMA client paragraph for more details.
>> +- #dma-cells : Must be <6>. See DMA client paragraph for more details.
> 
> can you update the example for 6 cells?

of course.

> 
> Also what happens to dts using 5 cells..

They are not managed, but it should. I will update this flaw. Thanks for
pointing this out.

> 
>>  
>>  Optional properties:
>>  - dma-channels: Number of DMA channels supported by the controller.
>> @@ -26,7 +26,7 @@ Example:
>>  		interrupts = <122>;
>>  		clocks = <&timer_clk>;
>>  		resets = <&rcc 992>;
>> -		#dma-cells = <5>;
>> +		#dma-cells = <6>;
>>  		dma-channels = <16>;
>>  		dma-requests = <32>;
>>  		st,ahb-addr-masks = <0x20000000>, <0x00000000>;
>> @@ -35,8 +35,8 @@ Example:
>>  * DMA client
>>  
>>  DMA clients connected to the STM32 MDMA controller must use the format
>> -described in the dma.txt file, using a five-cell specifier for each channel:
>> -a phandle to the MDMA controller plus the following five integer cells:
>> +described in the dma.txt file, using a six-cell specifier for each channel:
>> +a phandle to the MDMA controller plus the following six integer cells:
>>  
>>  1. The request line number
>>  2. The priority level
>> @@ -76,6 +76,10 @@ a phandle to the MDMA controller plus the following five integer cells:
>>     if no HW ack signal is used by the MDMA client
>>  5. A 32bit mask specifying the value to be written to acknowledge the request
>>     if no HW ack signal is used by the MDMA client
>> +6. A bitfield value specifying if the MDMA client wants to generate M2M
>> +   transfer with HW trigger (1) or not (0). This bitfield should be only
>> +   enabled for M2M transfer triggered by STM32 DMA client. The memory devices
>> +   involved in this kind of transfer are SRAM and DDR.
>>  
>>  Example:
>>  
>> -- 
>> 2.7.4
>

^ permalink raw reply

* [v3,4/7] dmaengine: stm32-dma: Add DMA/MDMA chaining support
From: Pierre Yves MORDRET @ 2018-10-09  8:40 UTC (permalink / raw)
  To: Vinod
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

On 10/07/2018 06:00 PM, Vinod wrote:
> On 28-09-18, 15:01, Pierre-Yves MORDRET wrote:
>> This patch adds support of DMA/MDMA chaining support.
>> It introduces an intermediate transfer between peripherals and STM32 DMA.
>> This intermediate transfer is triggered by SW for single M2D transfer and
>> by STM32 DMA IP for all other modes (sg, cyclic) and direction (D2M).
>>
>> A generic SRAM allocator is used for this intermediate buffer
>> Each DMA channel will be able to define its SRAM needs to achieve chaining
>> feature : (2 ^ order) * PAGE_SIZE.
>> For cyclic, SRAM buffer is derived from period length (rounded on
>> PAGE_SIZE).
> 
> So IIUC, you chain two dma txns together and transfer data via an SRAM?

Correct. one DMA is DMAv2 (stm32-dma) and the other is MDMA(stm32-mdma).
Intermediate transfer is between device and memory.
This intermediate transfer is using SDRAM.

> 
>>
>> Signed-off-by: Pierre-Yves MORDRET <pierre-yves.mordret@st.com>
>> ---
>>   Version history:
>>     v3:
>>        * Solve KBuild warning
>>     v2:
>>     v1:
>>        * Initial
>> ---
>> ---
>>  drivers/dma/stm32-dma.c | 879 ++++++++++++++++++++++++++++++++++++++++++------
> 
> that is a lot of change for a driver, consider splitting it up
> logically in smaller changes...
> 

This feature is rather monolithic. Difficult to split up.
All the code is required at once.

>>  1 file changed, 772 insertions(+), 107 deletions(-)
>>
>> diff --git a/drivers/dma/stm32-dma.c b/drivers/dma/stm32-dma.c
>> index 379e8d5..85e81c4 100644
>> --- a/drivers/dma/stm32-dma.c
>> +++ b/drivers/dma/stm32-dma.c
>> @@ -15,11 +15,14 @@
>>  #include <linux/dmaengine.h>
>>  #include <linux/dma-mapping.h>
>>  #include <linux/err.h>
>> +#include <linux/genalloc.h>
>>  #include <linux/init.h>
>> +#include <linux/iopoll.h>
>>  #include <linux/jiffies.h>
>>  #include <linux/list.h>
>>  #include <linux/module.h>
>>  #include <linux/of.h>
>> +#include <linux/of_address.h>
>>  #include <linux/of_device.h>
>>  #include <linux/of_dma.h>
>>  #include <linux/platform_device.h>
>> @@ -118,6 +121,7 @@
>>  #define STM32_DMA_FIFO_THRESHOLD_FULL			0x03
>>  
>>  #define STM32_DMA_MAX_DATA_ITEMS	0xffff
>> +#define STM32_DMA_SRAM_GRANULARITY	PAGE_SIZE
>>  /*
>>   * Valid transfer starts from @0 to @0xFFFE leading to unaligned scatter
>>   * gather at boundary. Thus it's safer to round down this value on FIFO
>> @@ -135,6 +139,12 @@
>>  /* DMA Features */
>>  #define STM32_DMA_THRESHOLD_FTR_MASK	GENMASK(1, 0)
>>  #define STM32_DMA_THRESHOLD_FTR_GET(n)	((n) & STM32_DMA_THRESHOLD_FTR_MASK)
>> +#define STM32_DMA_MDMA_CHAIN_FTR_MASK	BIT(2)
>> +#define STM32_DMA_MDMA_CHAIN_FTR_GET(n)	(((n) & STM32_DMA_MDMA_CHAIN_FTR_MASK) \
>> +					 >> 2)
>> +#define STM32_DMA_MDMA_SRAM_SIZE_MASK	GENMASK(4, 3)
>> +#define STM32_DMA_MDMA_SRAM_SIZE_GET(n)	(((n) & STM32_DMA_MDMA_SRAM_SIZE_MASK) \
>> +					 >> 3)
>>  
>>  enum stm32_dma_width {
>>  	STM32_DMA_BYTE,
>> @@ -176,15 +186,31 @@ struct stm32_dma_chan_reg {
>>  	u32 dma_sfcr;
>>  };
>>  
>> +struct stm32_dma_mdma_desc {
>> +	struct sg_table sgt;
>> +	struct dma_async_tx_descriptor *desc;
>> +};
>> +
>> +struct stm32_dma_mdma {
>> +	struct dma_chan *chan;
>> +	enum dma_transfer_direction dir;
>> +	dma_addr_t sram_buf;
>> +	u32 sram_period;
>> +	u32 num_sgs;
>> +};
>> +
>>  struct stm32_dma_sg_req {
>> -	u32 len;
>> +	struct scatterlist stm32_sgl_req;
>>  	struct stm32_dma_chan_reg chan_reg;
>> +	struct stm32_dma_mdma_desc m_desc;
>>  };
>>  
>>  struct stm32_dma_desc {
>>  	struct virt_dma_desc vdesc;
>>  	bool cyclic;
>>  	u32 num_sgs;
>> +	dma_addr_t dma_buf;
>> +	void *dma_buf_cpu;
>>  	struct stm32_dma_sg_req sg_req[];
>>  };
>>  
>> @@ -201,6 +227,10 @@ struct stm32_dma_chan {
>>  	u32 threshold;
>>  	u32 mem_burst;
>>  	u32 mem_width;
>> +	struct stm32_dma_mdma mchan;
>> +	u32 use_mdma;
>> +	u32 sram_size;
>> +	u32 residue_after_drain;
>>  };
>>  
>>  struct stm32_dma_device {
>> @@ -210,6 +240,7 @@ struct stm32_dma_device {
>>  	struct reset_control *rst;
>>  	bool mem2mem;
>>  	struct stm32_dma_chan chan[STM32_DMA_MAX_CHANNELS];
>> +	struct gen_pool *sram_pool;
>>  };
>>  
>>  static struct stm32_dma_device *stm32_dma_get_dev(struct stm32_dma_chan *chan)
>> @@ -497,11 +528,15 @@ static void stm32_dma_stop(struct stm32_dma_chan *chan)
>>  static int stm32_dma_terminate_all(struct dma_chan *c)
>>  {
>>  	struct stm32_dma_chan *chan = to_stm32_dma_chan(c);
>> +	struct stm32_dma_mdma *mchan = &chan->mchan;
>>  	unsigned long flags;
>>  	LIST_HEAD(head);
>>  
>>  	spin_lock_irqsave(&chan->vchan.lock, flags);
>>  
>> +	if (chan->use_mdma)
>> +		dmaengine_terminate_async(mchan->chan);
>> +
>>  	if (chan->busy) {
>>  		stm32_dma_stop(chan);
>>  		chan->desc = NULL;
>> @@ -514,9 +549,96 @@ static int stm32_dma_terminate_all(struct dma_chan *c)
>>  	return 0;
>>  }
>>  
>> +static u32 stm32_dma_get_remaining_bytes(struct stm32_dma_chan *chan)
>> +{
>> +	u32 dma_scr, width, ndtr;
>> +	struct stm32_dma_device *dmadev = stm32_dma_get_dev(chan);
>> +
>> +	dma_scr = stm32_dma_read(dmadev, STM32_DMA_SCR(chan->id));
>> +	width = STM32_DMA_SCR_PSIZE_GET(dma_scr);
>> +	ndtr = stm32_dma_read(dmadev, STM32_DMA_SNDTR(chan->id));
>> +
>> +	return ndtr << width;
>> +}
>> +
>> +static int stm32_dma_mdma_drain(struct stm32_dma_chan *chan)
>> +{
>> +	struct stm32_dma_mdma *mchan = &chan->mchan;
>> +	struct stm32_dma_sg_req *sg_req;
>> +	struct dma_device *ddev = mchan->chan->device;
>> +	struct dma_async_tx_descriptor *desc = NULL;
>> +	enum dma_status status;
>> +	dma_addr_t src_buf, dst_buf;
>> +	u32 mdma_residue, mdma_wrote, dma_to_write, len;
>> +	struct dma_tx_state state;
>> +	int ret;
>> +
>> +	/* DMA/MDMA chain: drain remaining data in SRAM */
>> +
>> +	/* Get the residue on MDMA side */
>> +	status = dmaengine_tx_status(mchan->chan, mchan->chan->cookie, &state);
>> +	if (status == DMA_COMPLETE)
>> +		return status;
>> +
>> +	mdma_residue = state.residue;
>> +	sg_req = &chan->desc->sg_req[chan->next_sg - 1];
>> +	len = sg_dma_len(&sg_req->stm32_sgl_req);
>> +
>> +	/*
>> +	 * Total = mdma blocks * sram_period + rest (< sram_period)
>> +	 * so mdma blocks * sram_period = len - mdma residue - rest
>> +	 */
>> +	mdma_wrote = len - mdma_residue - (len % mchan->sram_period);
>> +
>> +	/* Remaining data stuck in SRAM */
>> +	dma_to_write = mchan->sram_period - stm32_dma_get_remaining_bytes(chan);
>> +	if (dma_to_write > 0) {
>> +		/* Stop DMA current operation */
>> +		stm32_dma_disable_chan(chan);
>> +
>> +		/* Terminate current MDMA to initiate a new one */
>> +		dmaengine_terminate_all(mchan->chan);
>> +
>> +		/* Double buffer management */
>> +		src_buf = mchan->sram_buf +
>> +			  ((mdma_wrote / mchan->sram_period) & 0x1) *
>> +			  mchan->sram_period;
>> +		dst_buf = sg_dma_address(&sg_req->stm32_sgl_req) + mdma_wrote;
>> +
>> +		desc = ddev->device_prep_dma_memcpy(mchan->chan,
>> +						    dst_buf, src_buf,
>> +						    dma_to_write,
>> +						    DMA_PREP_INTERRUPT);
> 
> why would you do that?
> 
> If at all you need to create anothe txn, I think it would be good to
> prepare a new descriptor and chain it, not call the dmaengine APIs..
> 

In this UC, DMAv2 is configured in cyclic mode because this DMA doesn't work
with hw LLI only sw. This is really for performances reason we use this cyclic mode.
This very last txn is to flush remaining bytes stick in SDRAM.
I don't believe I can chain cyclic and this last txn.

^ permalink raw reply

* [v3,1/7] dt-bindings: stm32-dma: Add DMA/MDMA chaining support bindings
From: Vinod Koul @ 2018-10-09  8:57 UTC (permalink / raw)
  To: Pierre Yves MORDRET
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

Hi Pierre,

On 09-10-18, 09:18, Pierre Yves MORDRET wrote:

> >>  * DMA client
> >> @@ -68,7 +84,16 @@ channel: a phandle to the DMA controller plus the following four integer cells:
> >>  	0x1: 1/2 full FIFO
> >>  	0x2: 3/4 full FIFO
> >>  	0x3: full FIFO
> >> -
> >> + -bit 2: Intermediate M2M transfer from/to DDR to/from SRAM throughout MDMA
> >> +	0: MDMA not used to generate an intermediate M2M transfer
> >> +	1: MDMA used to generate an intermediate M2M transfer.
> >> + -bit 3-4: indicated SRAM Buffer size in (2^order)*PAGE_SIZE.
> >> +	PAGE_SIZE is given by Linux at 4KiB: include/asm-generic/page.h.
> >> +	Order is given by those 2 bits starting at 0.
> >> +	Valid only whether Intermediate M2M transfer is set.
> > 
> > why do we need this as a property?
> 
> In some UC, we need more than 4KiB in case of chaining for better performances.
> Chaining has to be enabled by client if performance is at sacks.

Okay if that is the case why is the user not taking care of this?
Creating DMA txn and chaining them up and starting the chain? Why would
dmaengine driver need to do that?

^ permalink raw reply

* [v3] dmaengine: fsl-edma: remove dma_slave_config direction usage
From: Vinod Koul @ 2018-10-09  8:59 UTC (permalink / raw)
  To: Angelo Dureghello; +Cc: Krzysztof Kozlowski, dmaengine

On 09-10-18, 10:02, Angelo Dureghello wrote:
> On Tue, Oct 09, 2018 at 09:48:05AM +0200, Krzysztof Kozlowski wrote:
> > On Tue, 9 Oct 2018 at 09:31, Vinod Koul <vkoul@kernel.org> wrote:
> > >
> > > dma_slave_config direction was marked as deprecated quite some
> > > time back, remove the usage from this driver so that the field
> > > can be removed
> > >
> > > Signed-off-by: Vinod Koul <vkoul@kernel.org>
> > > ---
> > > CC: Angelo Dureghello <angelo@sysam.it>
> > > CC: Krzysztof Kozlowski <krzk@kernel.org>
> > >
> > >  drivers/dma/fsl-edma-common.c | 74 +++++++++++++++++++++++++------------------
> > >  drivers/dma/fsl-edma-common.h | 12 ++-----
> > >  2 files changed, 46 insertions(+), 40 deletions(-)
> > 
> > Seems to work fine on Freescale Colibri VF50:
> > Tested-by: Krzysztof Kozlowski <krzk@kernel.org>
> > 
> > Best regards,
> > Krzysztof
> 
> As already replied to v2,
> 
> Tested-by: Angelo Dureghello <angelo@sysam.it>

Thanks Krzysztof, Angelo for quick tests

^ permalink raw reply

* [RFC,1/5] dmaengine: Add support for reporting DMA cached data amount
From: Vinod Koul @ 2018-10-09 10:47 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 24-09-18, 16:00, Peter Ujfalusi wrote:
> A DMA hardware can have big cache or FIFO and the amount of data sitting in
> the DMA fabric can be an interest for the clients.
> 
> For example in audio we want to know the delay in the data flow and in case
> the DMA have significantly large FIFO/cache, it can affect the latenc/delay

this looks okay to me, but not really up for 'cached', better
alternates?

Also we need to update Documentation for this..

> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
>  drivers/dma/dmaengine.h   | 7 +++++++
>  include/linux/dmaengine.h | 2 ++
>  2 files changed, 9 insertions(+)
> 
> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
> index 501c0b063f85..80214270b70e 100644
> --- a/drivers/dma/dmaengine.h
> +++ b/drivers/dma/dmaengine.h
> @@ -77,6 +77,7 @@ static inline enum dma_status dma_cookie_status(struct dma_chan *chan,
>  		state->last = complete;
>  		state->used = used;
>  		state->residue = 0;
> +		state->cached = 0;
>  	}
>  	return dma_async_is_complete(cookie, complete, used);
>  }
> @@ -87,6 +88,12 @@ static inline void dma_set_residue(struct dma_tx_state *state, u32 residue)
>  		state->residue = residue;
>  }
>  
> +static inline void dma_set_cached(struct dma_tx_state *state, u32 cached)
> +{
> +	if (state)
> +		state->cached = cached;
> +}
> +
>  struct dmaengine_desc_callback {
>  	dma_async_tx_callback callback;
>  	dma_async_tx_callback_result callback_result;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 10ff71b13eef..16c9d021988a 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -701,11 +701,13 @@ static inline struct dma_async_tx_descriptor *txd_next(struct dma_async_tx_descr
>   * @residue: the remaining number of bytes left to transmit
>   *	on the selected transfer for states DMA_IN_PROGRESS and
>   *	DMA_PAUSED if this is implemented in the driver, else 0
> + * @cached: amount of data in bytes cached by the DMA.
>   */
>  struct dma_tx_state {
>  	dma_cookie_t last;
>  	dma_cookie_t used;
>  	u32 residue;
> +	u32 cached;
>  };
>  
>  /**
> -- 
> Peter
> 
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,2/5] dmaengine: Add function to request slave channel from a dma_device
From: Vinod Koul @ 2018-10-09 10:49 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 24-09-18, 16:00, Peter Ujfalusi wrote:
> dma_get_any_slave_channel() would skip using the filter function, which
> in some cases needed to be executed before the alloc_chan_resources
> callback to make sure that all parameters are provided for the slave
> channel.

Another request API, i though you had solved that last time around :(

> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
>  drivers/dma/dmaengine.c   | 20 ++++++++++++++++++++
>  include/linux/dmaengine.h |  2 ++
>  2 files changed, 22 insertions(+)
> 
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index 27b6d7c2d8a0..b80f1bdd8813 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -634,6 +634,26 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
>  }
>  EXPORT_SYMBOL_GPL(dma_get_any_slave_channel);
>  
> +struct dma_chan *dmadev_get_slave_channel(struct dma_device *device,
> +					  dma_filter_fn fn, void *fn_param)
> +{
> +	dma_cap_mask_t mask;
> +	struct dma_chan *chan;
> +
> +	dma_cap_zero(mask);
> +	dma_cap_set(DMA_SLAVE, mask);
> +
> +	/* lock against __dma_request_channel */
> +	mutex_lock(&dma_list_mutex);
> +
> +	chan = find_candidate(device, &mask, fn, fn_param);
> +
> +	mutex_unlock(&dma_list_mutex);
> +
> +	return IS_ERR(chan) ? NULL : chan;
> +}
> +EXPORT_SYMBOL_GPL(dmadev_get_slave_channel);
> +
>  /**
>   * __dma_request_channel - try to allocate an exclusive channel
>   * @mask: capabilities that the channel must satisfy
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index 16c9d021988a..32010c6ae13a 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -1542,6 +1542,8 @@ void dma_async_device_unregister(struct dma_device *device);
>  void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
>  struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
>  struct dma_chan *dma_get_any_slave_channel(struct dma_device *device);
> +struct dma_chan *dmadev_get_slave_channel(struct dma_device *device,
> +					  dma_filter_fn fn, void *fn_param);
>  #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
>  #define dma_request_slave_channel_compat(mask, x, y, dev, name) \
>  	__dma_request_slave_channel_compat(&(mask), x, y, dev, name)
> -- 
> Peter
> 
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,1/5] dmaengine: Add support for reporting DMA cached data amount
From: Peter Ujfalusi @ 2018-10-09 11:05 UTC (permalink / raw)
  To: Vinod; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 2018-10-09 13:47, Vinod wrote:
> On 24-09-18, 16:00, Peter Ujfalusi wrote:
>> A DMA hardware can have big cache or FIFO and the amount of data sitting in
>> the DMA fabric can be an interest for the clients.
>>
>> For example in audio we want to know the delay in the data flow and in case
>> the DMA have significantly large FIFO/cache, it can affect the latenc/delay
> 
> this looks okay to me, but not really up for 'cached', better
> alternates?

It is after all the amount of bytes cached within the DMA fabric, but I
agree it is a bit awkward.

Recently I tried to come up with something better, but did not went too
far...
dma_cached_bytes?
dma_held_bytes?
in_flight_bytes?
not_in_source_but_not_yet_in_destination_bytes :D ?

I try to come up with something for the initial series.

> Also we need to update Documentation for this..

Sure, I tend to forget that.

> 
>>
>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>> ---
>>  drivers/dma/dmaengine.h   | 7 +++++++
>>  include/linux/dmaengine.h | 2 ++
>>  2 files changed, 9 insertions(+)
>>
>> diff --git a/drivers/dma/dmaengine.h b/drivers/dma/dmaengine.h
>> index 501c0b063f85..80214270b70e 100644
>> --- a/drivers/dma/dmaengine.h
>> +++ b/drivers/dma/dmaengine.h
>> @@ -77,6 +77,7 @@ static inline enum dma_status dma_cookie_status(struct dma_chan *chan,
>>  		state->last = complete;
>>  		state->used = used;
>>  		state->residue = 0;
>> +		state->cached = 0;
>>  	}
>>  	return dma_async_is_complete(cookie, complete, used);
>>  }
>> @@ -87,6 +88,12 @@ static inline void dma_set_residue(struct dma_tx_state *state, u32 residue)
>>  		state->residue = residue;
>>  }
>>  
>> +static inline void dma_set_cached(struct dma_tx_state *state, u32 cached)
>> +{
>> +	if (state)
>> +		state->cached = cached;
>> +}
>> +
>>  struct dmaengine_desc_callback {
>>  	dma_async_tx_callback callback;
>>  	dma_async_tx_callback_result callback_result;
>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>> index 10ff71b13eef..16c9d021988a 100644
>> --- a/include/linux/dmaengine.h
>> +++ b/include/linux/dmaengine.h
>> @@ -701,11 +701,13 @@ static inline struct dma_async_tx_descriptor *txd_next(struct dma_async_tx_descr
>>   * @residue: the remaining number of bytes left to transmit
>>   *	on the selected transfer for states DMA_IN_PROGRESS and
>>   *	DMA_PAUSED if this is implemented in the driver, else 0
>> + * @cached: amount of data in bytes cached by the DMA.
>>   */
>>  struct dma_tx_state {
>>  	dma_cookie_t last;
>>  	dma_cookie_t used;
>>  	u32 residue;
>> +	u32 cached;
>>  };
>>  
>>  /**
>> -- 
>> Peter
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
> 

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,2/5] dmaengine: Add function to request slave channel from a dma_device
From: Peter Ujfalusi @ 2018-10-09 11:22 UTC (permalink / raw)
  To: Vinod; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 2018-10-09 13:49, Vinod wrote:
> On 24-09-18, 16:00, Peter Ujfalusi wrote:
>> dma_get_any_slave_channel() would skip using the filter function, which
>> in some cases needed to be executed before the alloc_chan_resources
>> callback to make sure that all parameters are provided for the slave
>> channel.
> 
> Another request API, i though you had solved that last time around :(

Yes, I thought so, but this time it is different.
I want to drop this patch myself also, but I need to figure out a way to
do what the dmadev_get_slave_channel() allows me to do.

In path 4, udma_of_xlate() and the udma_dma_filter_fn()

It might be possible that I look up a free udma_chan, do the setup via
the filter_fn() and finally call the dma_get_slave_channel().
Might work fine, but I wanted to avoid to implement my own find free
channel code when we have it already in DMAengine core.

>>
>> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
>> ---
>>  drivers/dma/dmaengine.c   | 20 ++++++++++++++++++++
>>  include/linux/dmaengine.h |  2 ++
>>  2 files changed, 22 insertions(+)
>>
>> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
>> index 27b6d7c2d8a0..b80f1bdd8813 100644
>> --- a/drivers/dma/dmaengine.c
>> +++ b/drivers/dma/dmaengine.c
>> @@ -634,6 +634,26 @@ struct dma_chan *dma_get_any_slave_channel(struct dma_device *device)
>>  }
>>  EXPORT_SYMBOL_GPL(dma_get_any_slave_channel);
>>  
>> +struct dma_chan *dmadev_get_slave_channel(struct dma_device *device,
>> +					  dma_filter_fn fn, void *fn_param)
>> +{
>> +	dma_cap_mask_t mask;
>> +	struct dma_chan *chan;
>> +
>> +	dma_cap_zero(mask);
>> +	dma_cap_set(DMA_SLAVE, mask);
>> +
>> +	/* lock against __dma_request_channel */
>> +	mutex_lock(&dma_list_mutex);
>> +
>> +	chan = find_candidate(device, &mask, fn, fn_param);
>> +
>> +	mutex_unlock(&dma_list_mutex);
>> +
>> +	return IS_ERR(chan) ? NULL : chan;
>> +}
>> +EXPORT_SYMBOL_GPL(dmadev_get_slave_channel);
>> +
>>  /**
>>   * __dma_request_channel - try to allocate an exclusive channel
>>   * @mask: capabilities that the channel must satisfy
>> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
>> index 16c9d021988a..32010c6ae13a 100644
>> --- a/include/linux/dmaengine.h
>> +++ b/include/linux/dmaengine.h
>> @@ -1542,6 +1542,8 @@ void dma_async_device_unregister(struct dma_device *device);
>>  void dma_run_dependencies(struct dma_async_tx_descriptor *tx);
>>  struct dma_chan *dma_get_slave_channel(struct dma_chan *chan);
>>  struct dma_chan *dma_get_any_slave_channel(struct dma_device *device);
>> +struct dma_chan *dmadev_get_slave_channel(struct dma_device *device,
>> +					  dma_filter_fn fn, void *fn_param);
>>  #define dma_request_channel(mask, x, y) __dma_request_channel(&(mask), x, y)
>>  #define dma_request_slave_channel_compat(mask, x, y, dev, name) \
>>  	__dma_request_slave_channel_compat(&(mask), x, y, dev, name)
>> -- 
>> Peter
>>
>> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
>> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
> 

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,1/5] dmaengine: Add support for reporting DMA cached data amount
From: Vinod Koul @ 2018-10-09 11:41 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 09-10-18, 14:05, Peter Ujfalusi wrote:
> 
> 
> On 2018-10-09 13:47, Vinod wrote:
> > On 24-09-18, 16:00, Peter Ujfalusi wrote:
> >> A DMA hardware can have big cache or FIFO and the amount of data sitting in
> >> the DMA fabric can be an interest for the clients.
> >>
> >> For example in audio we want to know the delay in the data flow and in case
> >> the DMA have significantly large FIFO/cache, it can affect the latenc/delay
> > 
> > this looks okay to me, but not really up for 'cached', better
> > alternates?
> 
> It is after all the amount of bytes cached within the DMA fabric, but I
> agree it is a bit awkward.
> 
> Recently I tried to come up with something better, but did not went too
> far...
> dma_cached_bytes?
> dma_held_bytes?
> in_flight_bytes?
> not_in_source_but_not_yet_in_destination_bytes :D ?

yeah_that_sounds_good :D, i think in_flight would be apt here..

^ permalink raw reply

* [v3,1/7] dt-bindings: stm32-dma: Add DMA/MDMA chaining support bindings
From: Pierre Yves MORDRET @ 2018-10-09 13:46 UTC (permalink / raw)
  To: Vinod
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

On 10/09/2018 10:57 AM, Vinod wrote:
> Hi Pierre,
> 
> On 09-10-18, 09:18, Pierre Yves MORDRET wrote:
> 
>>>>  * DMA client
>>>> @@ -68,7 +84,16 @@ channel: a phandle to the DMA controller plus the following four integer cells:
>>>>  	0x1: 1/2 full FIFO
>>>>  	0x2: 3/4 full FIFO
>>>>  	0x3: full FIFO
>>>> -
>>>> + -bit 2: Intermediate M2M transfer from/to DDR to/from SRAM throughout MDMA
>>>> +	0: MDMA not used to generate an intermediate M2M transfer
>>>> +	1: MDMA used to generate an intermediate M2M transfer.
>>>> + -bit 3-4: indicated SRAM Buffer size in (2^order)*PAGE_SIZE.
>>>> +	PAGE_SIZE is given by Linux at 4KiB: include/asm-generic/page.h.
>>>> +	Order is given by those 2 bits starting at 0.
>>>> +	Valid only whether Intermediate M2M transfer is set.
>>>
>>> why do we need this as a property?
>>
>> In some UC, we need more than 4KiB in case of chaining for better performances.
>> Chaining has to be enabled by client if performance is at sacks.
> 
> Okay if that is the case why is the user not taking care of this?
> Creating DMA txn and chaining them up and starting the chain? Why would
> dmaengine driver need to do that?
> 

User is using standard DMA API (single, sg or cyclic) and is agnostic on what is
behind the scene(almost). As driver I just fulfill the request to transfer what
he wants. My driver scatters transfer into SDRAM chunks defined by user.
Unfortunately all transfer are not % the SDRAM size given in DT. This very last
txn is to flush the last expected bytes.
Whatever user set for chaining(bit 2) the DMA API remains the same at its side.

^ permalink raw reply

* [RFC,4/5] dmaengine: ti: New driver for K3 UDMA
From: Vinod Koul @ 2018-10-10  4:00 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 24-09-18, 16:00, Peter Ujfalusi wrote:
> DMA driver for
> Texas Instruments K3 NAVSS Unified DMA – Peripheral Root Complex (UDMA-P)
> 
> The UDMA-P is intended to perform similar (but significantly upgraded) functions
> as the packet-oriented DMA used on previous SoC devices. The UDMA-P module
> supports the transmission and reception of various packet types. The UDMA-P is
> architected to facilitate the segmentation and reassembly of SoC DMA data
> structure compliant packets to/from smaller data blocks that are natively
> compatible with the specific requirements of each connected peripheral. Multiple
> Tx and Rx channels are provided within the DMA which allow multiple segmentation
> or reassembly operations to be ongoing. The DMA controller maintains state
> information for each of the channels which allows packet segmentation and
> reassembly operations to be time division multiplexed between channels in order
> to share the underlying DMA hardware. An external DMA scheduler is used to
> control the ordering and rate at which this multiplexing occurs for Transmit
> operations. The ordering and rate of Receive operations is indirectly controlled
> by the order in which blocks are pushed into the DMA on the Rx PSI-L interface.
> 
> The UDMA-P also supports acting as both a UTC and UDMA-C for its internal
> channels. Channels in the UDMA-P can be configured to be either Packet-Based or
> Third-Party channels on a channel by channel basis.
> 
> The initial driver supports:
> - MEM_TO_MEM (TR mode)
> - DEV_TO_MEM (Packet / TR mode)
> - MEM_TO_DEV (Packet / TR mode)
> - Cyclic (Packet / TR mode)
> - Metadata for descriptors
> 
> Signed-off-by: Peter Ujfalusi <peter.ujfalusi@ti.com>
> ---
>  drivers/dma/ti/Kconfig            |   12 +
>  drivers/dma/ti/Makefile           |    1 +
>  drivers/dma/ti/k3-udma.c          | 3623 +++++++++++++++++++++++++++++

Sorry but that's too big to review, please split it up

...

I tried to review but it is too long, can you split to possibly, struct
and io accessors, then low level function and dma callbacks at the end..

>  drivers/dma/ti/k3-udma.h          |  132 ++
>  include/dt-bindings/dma/k3-udma.h |   26 +
>  5 files changed, 3794 insertions(+)
>  create mode 100644 drivers/dma/ti/k3-udma.c
>  create mode 100644 drivers/dma/ti/k3-udma.h
>  create mode 100644 include/dt-bindings/dma/k3-udma.h
> 
> diff --git a/drivers/dma/ti/Kconfig b/drivers/dma/ti/Kconfig
> index e5e74e1361dc..db73371de206 100644
> --- a/drivers/dma/ti/Kconfig
> +++ b/drivers/dma/ti/Kconfig
> @@ -33,5 +33,17 @@ config DMA_OMAP
>  	  Enable support for the TI sDMA (System DMA or DMA4) controller. This
>  	  DMA engine is found on OMAP and DRA7xx parts.
>  
> +config TI_K3_NAVSS_UDMA
> +	tristate "Texas Instruments UDMA support"
> +	depends on ARCH_K3 || COMPILE_TEST
> +	depends on TI_SCI_PROTOCOL
> +	select DMA_ENGINE
> +	select DMA_VIRTUAL_CHANNELS
> +	select TI_K3_NAVSS_RINGACC
> +	default y
> +        help
> +	  Enable support for the TI UDMA (Unified DMA) controller. This
> +	  DMA engine is used in AM65x.
> +
>  config TI_DMA_CROSSBAR
>  	bool
> diff --git a/drivers/dma/ti/Makefile b/drivers/dma/ti/Makefile
> index 113e59ec9c32..911f7c723979 100644
> --- a/drivers/dma/ti/Makefile
> +++ b/drivers/dma/ti/Makefile
> @@ -2,4 +2,5 @@
>  obj-$(CONFIG_TI_CPPI41) += cppi41.o
>  obj-$(CONFIG_TI_EDMA) += edma.o
>  obj-$(CONFIG_DMA_OMAP) += omap-dma.o
> +obj-$(CONFIG_TI_K3_NAVSS_UDMA) += k3-udma.o
>  obj-$(CONFIG_TI_DMA_CROSSBAR) += dma-crossbar.o
> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
> new file mode 100644
> index 000000000000..2a2a2c8b1fc2
> --- /dev/null
> +++ b/drivers/dma/ti/k3-udma.c
> @@ -0,0 +1,3623 @@
> +// SPDX-License-Identifier: GPL-2.0
> +/*
> + *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
> + *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
> + */
> +
> +#include <linux/kernel.h>
> +#include <linux/delay.h>
> +#include <linux/dmaengine.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmapool.h>
> +#include <linux/err.h>
> +#include <linux/init.h>
> +#include <linux/interrupt.h>
> +#include <linux/list.h>
> +#include <linux/module.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +#include <linux/of.h>
> +#include <linux/of_dma.h>
> +#include <linux/of_device.h>
> +#include <linux/of_irq.h>

Do you really need all of_  variants?

^ permalink raw reply

* [v3,4/7] dmaengine: stm32-dma: Add DMA/MDMA chaining support
From: Vinod Koul @ 2018-10-10  4:03 UTC (permalink / raw)
  To: Pierre Yves MORDRET
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

On 09-10-18, 10:40, Pierre Yves MORDRET wrote:
> 
> 
> On 10/07/2018 06:00 PM, Vinod wrote:
> > On 28-09-18, 15:01, Pierre-Yves MORDRET wrote:
> >> This patch adds support of DMA/MDMA chaining support.
> >> It introduces an intermediate transfer between peripherals and STM32 DMA.
> >> This intermediate transfer is triggered by SW for single M2D transfer and
> >> by STM32 DMA IP for all other modes (sg, cyclic) and direction (D2M).
> >>
> >> A generic SRAM allocator is used for this intermediate buffer
> >> Each DMA channel will be able to define its SRAM needs to achieve chaining
> >> feature : (2 ^ order) * PAGE_SIZE.
> >> For cyclic, SRAM buffer is derived from period length (rounded on
> >> PAGE_SIZE).
> > 
> > So IIUC, you chain two dma txns together and transfer data via an SRAM?
> 
> Correct. one DMA is DMAv2 (stm32-dma) and the other is MDMA(stm32-mdma).
> Intermediate transfer is between device and memory.
> This intermediate transfer is using SDRAM.

Ah so you use dma calls to setup mdma xtfers? I dont think that is a
good idea. How do you know you should use mdma for subsequent transfer?


> >>  drivers/dma/stm32-dma.c | 879 ++++++++++++++++++++++++++++++++++++++++++------
> > 
> > that is a lot of change for a driver, consider splitting it up
> > logically in smaller changes...
> > 
> 
> This feature is rather monolithic. Difficult to split up.
> All the code is required at once.

It can be enabled at last but split up logically. Intrusive changes to a
driver make it hard to review..

^ permalink raw reply

* [RFC,5/5] dmaengine: ti: k3-udma: Add glue layer for non DMAengine users
From: Vinod Koul @ 2018-10-10  4:05 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 24-09-18, 16:00, Peter Ujfalusi wrote:
> From: Grygorii Strashko <grygorii.strashko@ti.com>
> 
> Certain users can not use right now the DMAengine API due to missing
> features in the core. Prime example is Networking.

What are the features you have in mind?

^ permalink raw reply

* [RFC,4/5] dmaengine: ti: New driver for K3 UDMA
From: Peter Ujfalusi @ 2018-10-10  6:53 UTC (permalink / raw)
  To: Vinod; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 2018-10-10 07:00, Vinod wrote:
>>  drivers/dma/ti/Kconfig            |   12 +
>>  drivers/dma/ti/Makefile           |    1 +
>>  drivers/dma/ti/k3-udma.c          | 3623 +++++++++++++++++++++++++++++
> 
> Sorry but that's too big to review, please split it up
> 
> ...
> 
> I tried to review but it is too long, can you split to possibly, struct
> and io accessors, then low level function and dma callbacks at the end..

Hrm, I can try that.

>>  drivers/dma/ti/k3-udma.h          |  132 ++
>>  include/dt-bindings/dma/k3-udma.h |   26 +
>>  5 files changed, 3794 insertions(+)
>>  create mode 100644 drivers/dma/ti/k3-udma.c
>>  create mode 100644 drivers/dma/ti/k3-udma.h
>>  create mode 100644 include/dt-bindings/dma/k3-udma.h
>>
>> diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c
>> new file mode 100644
>> index 000000000000..2a2a2c8b1fc2
>> --- /dev/null
>> +++ b/drivers/dma/ti/k3-udma.c
>> @@ -0,0 +1,3623 @@
>> +// SPDX-License-Identifier: GPL-2.0
>> +/*
>> + *  Copyright (C) 2018 Texas Instruments Incorporated - http://www.ti.com
>> + *  Author: Peter Ujfalusi <peter.ujfalusi@ti.com>
>> + */
>> +
>> +#include <linux/kernel.h>
>> +#include <linux/delay.h>
>> +#include <linux/dmaengine.h>
>> +#include <linux/dma-mapping.h>
>> +#include <linux/dmapool.h>
>> +#include <linux/err.h>
>> +#include <linux/init.h>
>> +#include <linux/interrupt.h>
>> +#include <linux/list.h>
>> +#include <linux/module.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/of.h>
>> +#include <linux/of_dma.h>
>> +#include <linux/of_device.h>
>> +#include <linux/of_irq.h>
> 
> Do you really need all of_  variants?

Yes, they are needed:

of_dma.h -> of_dma support
of_device.h -> of_find_device_by_node()
of_irq.h -> of_irq_find_parent()

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [v3,4/7] dmaengine: stm32-dma: Add DMA/MDMA chaining support
From: Pierre Yves MORDRET @ 2018-10-10  7:02 UTC (permalink / raw)
  To: Vinod
  Cc: Rob Herring, Mark Rutland, Alexandre Torgue, Maxime Coquelin,
	Dan Williams, devicetree, dmaengine, linux-arm-kernel,
	linux-kernel

On 10/10/2018 06:03 AM, Vinod wrote:
> On 09-10-18, 10:40, Pierre Yves MORDRET wrote:
>>
>>
>> On 10/07/2018 06:00 PM, Vinod wrote:
>>> On 28-09-18, 15:01, Pierre-Yves MORDRET wrote:
>>>> This patch adds support of DMA/MDMA chaining support.
>>>> It introduces an intermediate transfer between peripherals and STM32 DMA.
>>>> This intermediate transfer is triggered by SW for single M2D transfer and
>>>> by STM32 DMA IP for all other modes (sg, cyclic) and direction (D2M).
>>>>
>>>> A generic SRAM allocator is used for this intermediate buffer
>>>> Each DMA channel will be able to define its SRAM needs to achieve chaining
>>>> feature : (2 ^ order) * PAGE_SIZE.
>>>> For cyclic, SRAM buffer is derived from period length (rounded on
>>>> PAGE_SIZE).
>>>
>>> So IIUC, you chain two dma txns together and transfer data via an SRAM?
>>
>> Correct. one DMA is DMAv2 (stm32-dma) and the other is MDMA(stm32-mdma).
>> Intermediate transfer is between device and memory.
>> This intermediate transfer is using SDRAM.
> 
> Ah so you use dma calls to setup mdma xtfers? I dont think that is a
> good idea. How do you know you should use mdma for subsequent transfer?
> 

When user bindings told to setup chaining intermediate MDMA transfers are always
triggers.
For instance if a user requests a Dev2Mem transfer with chaining. From client
pov this is still a prep_slave_sg. Internally DMAv2 is setup in cyclic mode (in
double buffer mode indeed => 2 buffer of PAGE_SIZE/2) and destination is SDRAM.
DMAv2 will flip/flop on those 2 buffers.
At the same time DMAv2 driver prepares a MDMA SG that will fetch data from those
2 buffers in SDRAM and fills final destination memory.

> 
>>>>  drivers/dma/stm32-dma.c | 879 ++++++++++++++++++++++++++++++++++++++++++------
>>>
>>> that is a lot of change for a driver, consider splitting it up
>>> logically in smaller changes...
>>>
>>
>> This feature is rather monolithic. Difficult to split up.
>> All the code is required at once.
> 
> It can be enabled at last but split up logically. Intrusive changes to a
> driver make it hard to review..
> 
Ok. I will to think about it how to proceed.

^ permalink raw reply

* [RFC,5/5] dmaengine: ti: k3-udma: Add glue layer for non DMAengine users
From: Peter Ujfalusi @ 2018-10-10  9:44 UTC (permalink / raw)
  To: Vinod; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 2018-10-10 07:05, Vinod wrote:
> On 24-09-18, 16:00, Peter Ujfalusi wrote:
>> From: Grygorii Strashko <grygorii.strashko@ti.com>
>>
>> Certain users can not use right now the DMAengine API due to missing
>> features in the core. Prime example is Networking.
> 
> What are the features you have in mind?

The two big ones are:
1. Support for out of order completion of descriptors (cookie management)

This mostly concerns the DEV_TO_MEM.
Currently we have dma_cookie_t (s32) to track which descriptor is
completed, which one is issued and which one is pending.
It is mandatory that descriptors are completed in the same order as they
were issued.

We need to have support for out of order completion of the issued
descriptors and thus the s32 way of tracking is not working.

We basically have three use cases needed to be supported:
1a: in order completion (currently supported by DMAengine)
1b: out of order completion in a channel, descriptors can complete in
different order.
1c: out of order completion in a channel, the completion is ordered
within 'classification type' of descriptors via the channel

If we issue descriptors 1, 2, 3, 4, 5, 6, 7

We could receive back them
1a: 1, 2, 3, 4, 5, 6, 7
1b: 4, 7, 2, 1, 3, 5, 6

in case of 1c we can classify descriptors when issuing:
1(c0), 2(c0), 3(c0), 4(c1), 5(c1), 6(c1), 7(c1)

and we receive the back like:
4(c1), 5(c1), 1(c0), 2(c0), 6(c1), 3(c0), 7(c1)

The descriptors are coming back in 'random' order, however they are in
order within their classification.

The must supported one is 1b at the moment, 1c should be reasonably
simple to add support for.

2. NAPI support

Via NAPI an RX looks something like this:
- give the hardware bunch of descriptors to receive packets
- when the first packet is received get notification
 - disable the interrupts
 - schedule NAPI
- in the NAPI poll function read out the completed descriptors one by
one, process them, then give back the descriptor (with new buffer) to
the hardware.
- after poll finished (no more packets or got the number of packets NAPI
told us to read out) enable the interrupts and wait for packets to start
to flow in.

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,5/5] dmaengine: ti: k3-udma: Add glue layer for non DMAengine users
From: Vinod Koul @ 2018-10-10 10:09 UTC (permalink / raw)
  To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 10-10-18, 12:44, Peter Ujfalusi wrote:
> 
> 
> On 2018-10-10 07:05, Vinod wrote:
> > On 24-09-18, 16:00, Peter Ujfalusi wrote:
> >> From: Grygorii Strashko <grygorii.strashko@ti.com>
> >>
> >> Certain users can not use right now the DMAengine API due to missing
> >> features in the core. Prime example is Networking.
> > 
> > What are the features you have in mind?
> 
> The two big ones are:
> 1. Support for out of order completion of descriptors (cookie management)
> 
> This mostly concerns the DEV_TO_MEM.
> Currently we have dma_cookie_t (s32) to track which descriptor is
> completed, which one is issued and which one is pending.
> It is mandatory that descriptors are completed in the same order as they
> were issued.
> 
> We need to have support for out of order completion of the issued
> descriptors and thus the s32 way of tracking is not working.
> 
> We basically have three use cases needed to be supported:
> 1a: in order completion (currently supported by DMAengine)
> 1b: out of order completion in a channel, descriptors can complete in
> different order.
> 1c: out of order completion in a channel, the completion is ordered
> within 'classification type' of descriptors via the channel
> 
> If we issue descriptors 1, 2, 3, 4, 5, 6, 7
> 
> We could receive back them
> 1a: 1, 2, 3, 4, 5, 6, 7
> 1b: 4, 7, 2, 1, 3, 5, 6
> 
> in case of 1c we can classify descriptors when issuing:
> 1(c0), 2(c0), 3(c0), 4(c1), 5(c1), 6(c1), 7(c1)
> 
> and we receive the back like:
> 4(c1), 5(c1), 1(c0), 2(c0), 6(c1), 3(c0), 7(c1)
> 
> The descriptors are coming back in 'random' order, however they are in
> order within their classification.

when you say classification you mean actual HW channels right, so to
support that why should the cookie be provided from the channel where
you schedule the descriptor for?

> The must supported one is 1b at the moment, 1c should be reasonably
> simple to add support for.

so you are adding 1b in this patch?

> 
> 2. NAPI support
> 
> Via NAPI an RX looks something like this:
> - give the hardware bunch of descriptors to receive packets
> - when the first packet is received get notification
>  - disable the interrupts
>  - schedule NAPI
> - in the NAPI poll function read out the completed descriptors one by
> one, process them, then give back the descriptor (with new buffer) to
> the hardware.
> - after poll finished (no more packets or got the number of packets NAPI
> told us to read out) enable the interrupts and wait for packets to start
> to flow in.

Can that not be done by using the interrupt/ack flags?

^ permalink raw reply

* [RFC,5/5] dmaengine: ti: k3-udma: Add glue layer for non DMAengine users
From: Peter Ujfalusi @ 2018-10-10 11:25 UTC (permalink / raw)
  To: Vinod; +Cc: dan.j.williams, dmaengine, grygorii.strashko

On 2018-10-10 13:09, Vinod wrote:
> On 10-10-18, 12:44, Peter Ujfalusi wrote:
>>
>>
>> On 2018-10-10 07:05, Vinod wrote:
>>> On 24-09-18, 16:00, Peter Ujfalusi wrote:
>>>> From: Grygorii Strashko <grygorii.strashko@ti.com>
>>>>
>>>> Certain users can not use right now the DMAengine API due to missing
>>>> features in the core. Prime example is Networking.
>>>
>>> What are the features you have in mind?
>>
>> The two big ones are:
>> 1. Support for out of order completion of descriptors (cookie management)
>>
>> This mostly concerns the DEV_TO_MEM.
>> Currently we have dma_cookie_t (s32) to track which descriptor is
>> completed, which one is issued and which one is pending.
>> It is mandatory that descriptors are completed in the same order as they
>> were issued.
>>
>> We need to have support for out of order completion of the issued
>> descriptors and thus the s32 way of tracking is not working.
>>
>> We basically have three use cases needed to be supported:
>> 1a: in order completion (currently supported by DMAengine)
>> 1b: out of order completion in a channel, descriptors can complete in
>> different order.
>> 1c: out of order completion in a channel, the completion is ordered
>> within 'classification type' of descriptors via the channel
>>
>> If we issue descriptors 1, 2, 3, 4, 5, 6, 7
>>
>> We could receive back them
>> 1a: 1, 2, 3, 4, 5, 6, 7
>> 1b: 4, 7, 2, 1, 3, 5, 6
>>
>> in case of 1c we can classify descriptors when issuing:
>> 1(c0), 2(c0), 3(c0), 4(c1), 5(c1), 6(c1), 7(c1)
>>
>> and we receive the back like:
>> 4(c1), 5(c1), 1(c0), 2(c0), 6(c1), 3(c0), 7(c1)
>>
>> The descriptors are coming back in 'random' order, however they are in
>> order within their classification.
> 
> when you say classification you mean actual HW channels right, so to
> support that why should the cookie be provided from the channel where
> you schedule the descriptor for?

No, it is not actual HW channel.

There is a concept of flowID in CPPI5 (as well in CPPI4). Each flowID is
addressing (triggering) the corresponding receive flow (rflow)
configuration. FlowID == 0x3fff would use the default flow for the
channel, which has the same ID as the rchan

So,
We have a receive channel (rchan10).
We have the default rflow10 for it
We have extra FlowID range reserved for the channel (rflow180-188)

The rflow configuration describes what to do when a packet with the
matching FlowID has been received:
- from which ring the free descriptor should be used (fdq - free
descriptor queue)
- to which ring the packet should be put when the packet is completed
(rq - receive queue)
- it has more fine grained control, but let's not worry about them now.

In case of 1c each rflow have unique pair of fdq and rq. We can classify
a packet to go to fdqA, which is used by rflowA and it is going to
return the packet to rqA.

But it could be that all rflows are pointing to one rq. So packets from
fdqA/B/C/.. for rflowA/B/C/... are all going to be pushed to one rq.

The 1b case is when all rflow has been configured to use a single fdq,
but push the completed packets to different rq. In this mode it is easy
to support packet priority for example: The eth hardware will push high
priority packets with lower FlowID and the SW is first empties the rq
which is associated with the lower indexed rflow configuration.

I was thinking of representing the rflows as 'channels' but then there
will packets coming out from channels where we have not even placed an
RX - we push RX to one 'channel' but it is coming back on another in
random order.

> 
>> The must supported one is 1b at the moment, 1c should be reasonably
>> simple to add support for.
> 
> so you are adding 1b in this patch?

Well, in a way yes. This patch makes it possible to use 1b in
networking, but w/o using any DMAengine API. I want to avoid mixed use
(abuse) of DMAengine and custom API.

> 
>>
>> 2. NAPI support
>>
>> Via NAPI an RX looks something like this:
>> - give the hardware bunch of descriptors to receive packets
>> - when the first packet is received get notification
>>  - disable the interrupts
>>  - schedule NAPI
>> - in the NAPI poll function read out the completed descriptors one by
>> one, process them, then give back the descriptor (with new buffer) to
>> the hardware.
>> - after poll finished (no more packets or got the number of packets NAPI
>> told us to read out) enable the interrupts and wait for packets to start
>> to flow in.
> 
> Can that not be done by using the interrupt/ack flags?

I don't think so.
By definition the callback is called to notify the client that the
transaction is completed (the given descriptor). After this the
DMAengine considers the cookie to be completed, right?

In NAPI however the client is notified that there is something pending
for reception (via irq), the client then disable the interrupt. net core
will initiate the polling and the packets are taken out from the HW.

It is not possible to issue the first packet w/ DMA_PREP_INTERRUPT the
others with w/o it for example as we don't know how many packets are
going to come and we are not even sure if the first issued packet is
going to be the first to come back.

It is more like a new dma_transaction_type than per descriptor flag. The
channel must know that it is in this mode so when it get the interrupt
that there is something pending for reception, it would just disable the
interrupt and tell the client this. Then we need to somehow be able to
poll read the packets out. Either one-by-one read or a looping within
DMAengine/DMA driver and calling a client provided callback for each packet.

- Péter

Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki

^ permalink raw reply

* [RFC,5/5] dmaengine: ti: k3-udma: Add glue layer for non DMAengine users
From: Grygorii Strashko @ 2018-10-10 19:01 UTC (permalink / raw)
  To: Vinod, Peter Ujfalusi; +Cc: dan.j.williams, dmaengine

On 10/10/2018 05:09 AM, Vinod wrote:
> On 10-10-18, 12:44, Peter Ujfalusi wrote:
>>
>>
>> On 2018-10-10 07:05, Vinod wrote:
>>> On 24-09-18, 16:00, Peter Ujfalusi wrote:
>>>> From: Grygorii Strashko <grygorii.strashko@ti.com>
>>>>
>>>> Certain users can not use right now the DMAengine API due to missing
>>>> features in the core. Prime example is Networking.
>>>
>>> What are the features you have in mind?
>>
>> The two big ones are:
>> 1. Support for out of order completion of descriptors (cookie management)
>>
>> This mostly concerns the DEV_TO_MEM.
>> Currently we have dma_cookie_t (s32) to track which descriptor is
>> completed, which one is issued and which one is pending.
>> It is mandatory that descriptors are completed in the same order as they
>> were issued.
>>
>> We need to have support for out of order completion of the issued
>> descriptors and thus the s32 way of tracking is not working.
>>
>> We basically have three use cases needed to be supported:
>> 1a: in order completion (currently supported by DMAengine)
>> 1b: out of order completion in a channel, descriptors can complete in
>> different order.
>> 1c: out of order completion in a channel, the completion is ordered
>> within 'classification type' of descriptors via the channel
>>
>> If we issue descriptors 1, 2, 3, 4, 5, 6, 7
>>
>> We could receive back them
>> 1a: 1, 2, 3, 4, 5, 6, 7
>> 1b: 4, 7, 2, 1, 3, 5, 6
>>
>> in case of 1c we can classify descriptors when issuing:
>> 1(c0), 2(c0), 3(c0), 4(c1), 5(c1), 6(c1), 7(c1)
>>
>> and we receive the back like:
>> 4(c1), 5(c1), 1(c0), 2(c0), 6(c1), 3(c0), 7(c1)
>>
>> The descriptors are coming back in 'random' order, however they are in
>> order within their classification.
> 
> when you say classification you mean actual HW channels right, so to
> support that why should the cookie be provided from the channel where
> you schedule the descriptor for?
> 
>> The must supported one is 1b at the moment, 1c should be reasonably
>> simple to add support for.
> 
> so you are adding 1b in this patch?
> 
>>
>> 2. NAPI support
>>
>> Via NAPI an RX looks something like this:
>> - give the hardware bunch of descriptors to receive packets
>> - when the first packet is received get notification
>>   - disable the interrupts
>>   - schedule NAPI
>> - in the NAPI poll function read out the completed descriptors one by
>> one, process them, then give back the descriptor (with new buffer) to
>> the hardware.
>> - after poll finished (no more packets or got the number of packets NAPI
>> told us to read out) enable the interrupts and wait for packets to start
>> to flow in.
> 
> Can that not be done by using the interrupt/ack flags?
> 

There is actually point 3) UDMA channels are very "universal" and support pretty
high number of configuration possibilities, so each UDMA channel consumer need to configure
each channel depending on its own needs and target IP. Some of the parameters might
by dynamic - minimum for debugging purposes. The consumer driver usually knows what it 
needs to configure basing on compat string.

it seems there is no other way now to pass custom channel configuration from
consumer driver to the DMA engine driver except using DT (at least I've not found it)
- which means only static configuration can be used (plus, DT bindings might grow significantly).

Any way, this is not so critical as 1 and 2.

I'll try to provide more info on 1 also.

^ permalink raw reply

* [1/7] dmaengine: fsldma: Replace DMA_IN/OUT by FSL_DMA_IN/OUT
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Wen He

From: Wen He <wen.he_1@nxp.com>

This patch implement a standard macro call functions is
used to NXP dma drivers.

Signed-off-by: Wen He <wen.he_1@nxp.com>
---
 drivers/dma/fsldma.c |   16 ++++++++--------
 drivers/dma/fsldma.h |    4 ++--
 2 files changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/dma/fsldma.c b/drivers/dma/fsldma.c
index 1117b51..39871e0 100644
--- a/drivers/dma/fsldma.c
+++ b/drivers/dma/fsldma.c
@@ -53,42 +53,42 @@
 
 static void set_sr(struct fsldma_chan *chan, u32 val)
 {
-	DMA_OUT(chan, &chan->regs->sr, val, 32);
+	FSL_DMA_OUT(chan, &chan->regs->sr, val, 32);
 }
 
 static u32 get_sr(struct fsldma_chan *chan)
 {
-	return DMA_IN(chan, &chan->regs->sr, 32);
+	return FSL_DMA_IN(chan, &chan->regs->sr, 32);
 }
 
 static void set_mr(struct fsldma_chan *chan, u32 val)
 {
-	DMA_OUT(chan, &chan->regs->mr, val, 32);
+	FSL_DMA_OUT(chan, &chan->regs->mr, val, 32);
 }
 
 static u32 get_mr(struct fsldma_chan *chan)
 {
-	return DMA_IN(chan, &chan->regs->mr, 32);
+	return FSL_DMA_IN(chan, &chan->regs->mr, 32);
 }
 
 static void set_cdar(struct fsldma_chan *chan, dma_addr_t addr)
 {
-	DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
+	FSL_DMA_OUT(chan, &chan->regs->cdar, addr | FSL_DMA_SNEN, 64);
 }
 
 static dma_addr_t get_cdar(struct fsldma_chan *chan)
 {
-	return DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
+	return FSL_DMA_IN(chan, &chan->regs->cdar, 64) & ~FSL_DMA_SNEN;
 }
 
 static void set_bcr(struct fsldma_chan *chan, u32 val)
 {
-	DMA_OUT(chan, &chan->regs->bcr, val, 32);
+	FSL_DMA_OUT(chan, &chan->regs->bcr, val, 32);
 }
 
 static u32 get_bcr(struct fsldma_chan *chan)
 {
-	return DMA_IN(chan, &chan->regs->bcr, 32);
+	return FSL_DMA_IN(chan, &chan->regs->bcr, 32);
 }
 
 /*
diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 4787d48..982845b 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -223,10 +223,10 @@ static void out_le64(u64 __iomem *addr, u64 val)
 }
 #endif
 
-#define DMA_IN(fsl_chan, addr, width)					\
+#define FSL_DMA_IN(fsl_chan, addr, width)				\
 		(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ?		\
 			in_be##width(addr) : in_le##width(addr))
-#define DMA_OUT(fsl_chan, addr, val, width)				\
+#define FSL_DMA_OUT(fsl_chan, addr, val, width)			\
 		(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ?		\
 			out_be##width(addr, val) : out_le##width(addr, val))
 

^ permalink raw reply related

* [2/7] dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Wen He

From: Wen He <wen.he_1@nxp.com>

This patch add the macro FSL_DMA_IN/OUT implement for ARM platform.

Signed-off-by: Wen He <wen.he_1@nxp.com>
---
 drivers/dma/fsldma.h |   57 +++++++++++++++++++++++++++++++++----------------
 1 files changed, 38 insertions(+), 19 deletions(-)

diff --git a/drivers/dma/fsldma.h b/drivers/dma/fsldma.h
index 982845b..1dc64c9 100644
--- a/drivers/dma/fsldma.h
+++ b/drivers/dma/fsldma.h
@@ -196,39 +196,58 @@ struct fsldma_chan {
 #define to_fsl_desc(lh) container_of(lh, struct fsl_desc_sw, node)
 #define tx_to_fsl_desc(tx) container_of(tx, struct fsl_desc_sw, async_tx)
 
+#ifdef	CONFIG_PPC
+#define fsl_ioread32(p)		in_le32(p)
+#define fsl_ioread32be(p)	in_be32(p)
+#define fsl_iowrite32(v, p)	out_le32(p, v)
+#define fsl_iowrite32be(v, p)	out_be32(p, v)
+
 #ifndef __powerpc64__
-static u64 in_be64(const u64 __iomem *addr)
+static u64 fsl_ioread64(const u64 __iomem *addr)
 {
-	return ((u64)in_be32((u32 __iomem *)addr) << 32) |
-		(in_be32((u32 __iomem *)addr + 1));
+	return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
+		(in_le32((u32 __iomem *)addr));
 }
 
-static void out_be64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64(u64 val, u64 __iomem *addr)
 {
-	out_be32((u32 __iomem *)addr, val >> 32);
-	out_be32((u32 __iomem *)addr + 1, (u32)val);
+	out_le32((u32 __iomem *)addr + 1, val >> 32);
+	out_le32((u32 __iomem *)addr, (u32)val);
 }
 
-/* There is no asm instructions for 64 bits reverse loads and stores */
-static u64 in_le64(const u64 __iomem *addr)
+static u64 fsl_ioread64be(const u64 __iomem *addr)
 {
-	return ((u64)in_le32((u32 __iomem *)addr + 1) << 32) |
-		(in_le32((u32 __iomem *)addr));
+	return ((u64)in_be32((u32 __iomem *)addr) << 32) |
+		(in_be32((u32 __iomem *)addr + 1));
 }
 
-static void out_le64(u64 __iomem *addr, u64 val)
+static void fsl_iowrite64be(u64 val, u64 __iomem *addr)
 {
-	out_le32((u32 __iomem *)addr + 1, val >> 32);
-	out_le32((u32 __iomem *)addr, (u32)val);
+	out_be32((u32 __iomem *)addr, val >> 32);
+	out_be32((u32 __iomem *)addr + 1, (u32)val);
 }
 #endif
+#endif
 
-#define FSL_DMA_IN(fsl_chan, addr, width)				\
-		(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ?		\
-			in_be##width(addr) : in_le##width(addr))
-#define FSL_DMA_OUT(fsl_chan, addr, val, width)			\
-		(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ?		\
-			out_be##width(addr, val) : out_le##width(addr, val))
+#if defined(CONFIG_ARM64) || defined(CONFIG_ARM)
+#define fsl_ioread32(p)		ioread32(p)
+#define fsl_ioread32be(p)	ioread32be(p)
+#define fsl_iowrite32(v, p)	iowrite32(v, p)
+#define fsl_iowrite32be(v, p)	iowrite32be(v, p)
+#define fsl_ioread64(p)		ioread64(p)
+#define fsl_ioread64be(p)	ioread64be(p)
+#define fsl_iowrite64(v, p)	iowrite64(v, p)
+#define fsl_iowrite64be(v, p)	iowrite64be(v, p)
+#endif
+
+#define FSL_DMA_IN(fsl_dma, addr, width)			\
+		(((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ?	\
+			fsl_ioread##width##be(addr) : fsl_ioread##width(addr))
+
+#define FSL_DMA_OUT(fsl_dma, addr, val, width)			\
+		(((fsl_dma)->feature & FSL_DMA_BIG_ENDIAN) ?	\
+			fsl_iowrite##width##be(val, addr) : fsl_iowrite	\
+		##width(val, addr))
 
 #define DMA_TO_CPU(fsl_chan, d, width)					\
 		(((fsl_chan)->feature & FSL_DMA_BIG_ENDIAN) ?		\

^ permalink raw reply related

* [3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Peng Ma

NXP Queue DMA controller(qDMA) on Layerscape SoCs supports channel
virtuallization by allowing DMA jobs to be enqueued into different
command queues.

Signed-off-by: Peng Ma <peng.ma@nxp.com>
---
 drivers/dma/Kconfig    |   13 +
 drivers/dma/Makefile   |    1 +
 drivers/dma/fsl-qdma.c | 1282 ++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 1296 insertions(+), 0 deletions(-)
 create mode 100644 drivers/dma/fsl-qdma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index dacf3f4..50e19d7 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -218,6 +218,19 @@ config FSL_EDMA
 	  multiplexing capability for DMA request sources(slot).
 	  This module can be found on Freescale Vybrid and LS-1 SoCs.
 
+config FSL_QDMA
+       tristate "NXP Layerscape qDMA engine support"
+       depends on ARM || ARM64
+       select DMA_ENGINE
+       select DMA_VIRTUAL_CHANNELS
+       select DMA_ENGINE_RAID
+       select ASYNC_TX_ENABLE_CHANNEL_SWITCH
+       help
+         Support the NXP Layerscape qDMA engine with command queue and legacy mode.
+         Channel virtualization is supported through enqueuing of DMA jobs to,
+         or dequeuing DMA jobs from, different work queues.
+         This module can be found on NXP Layerscape SoCs.
+
 config FSL_RAID
         tristate "Freescale RAID engine Support"
         depends on FSL_SOC && !ASYNC_TX_ENABLE_CHANNEL_SWITCH
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index c91702d..2d1b586 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -32,6 +32,7 @@ obj-$(CONFIG_DW_DMAC_CORE) += dw/
 obj-$(CONFIG_EP93XX_DMA) += ep93xx_dma.o
 obj-$(CONFIG_FSL_DMA) += fsldma.o
 obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
+obj-$(CONFIG_FSL_QDMA) += fsl-qdma.o
 obj-$(CONFIG_FSL_RAID) += fsl_raid.o
 obj-$(CONFIG_HSU_DMA) += hsu/
 obj-$(CONFIG_IMG_MDC_DMA) += img-mdc-dma.o
diff --git a/drivers/dma/fsl-qdma.c b/drivers/dma/fsl-qdma.c
new file mode 100644
index 0000000..71a4f2c
--- /dev/null
+++ b/drivers/dma/fsl-qdma.c
@@ -0,0 +1,1282 @@
+/*
+ * Driver for NXP Layerscape Queue direct memory access controller (qDMA)
+ *
+ * Copyright 2018 NXP
+ *
+ * Author:
+ *  Jiaheng Fan <jiaheng.fan@nxp.com>
+ *  Wen He <wen.he_1@nxp.com>
+ *
+ * SPDX-License-Identifier: GPL-2.0+
+ */
+
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/of_irq.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/of_dma.h>
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/dmaengine.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "virt-dma.h"
+#include "fsldma.h"
+
+/* Register related definition */
+#define FSL_QDMA_DMR			0x0
+#define FSL_QDMA_DSR			0x4
+#define FSL_QDMA_DEIER			0xe00
+#define FSL_QDMA_DEDR			0xe04
+#define FSL_QDMA_DECFDW0R		0xe10
+#define FSL_QDMA_DECFDW1R		0xe14
+#define FSL_QDMA_DECFDW2R		0xe18
+#define FSL_QDMA_DECFDW3R		0xe1c
+#define FSL_QDMA_DECFQIDR		0xe30
+#define FSL_QDMA_DECBR			0xe34
+
+#define FSL_QDMA_BCQMR(x)		(0xc0 + 0x100 * (x))
+#define FSL_QDMA_BCQSR(x)		(0xc4 + 0x100 * (x))
+#define FSL_QDMA_BCQEDPA_SADDR(x)	(0xc8 + 0x100 * (x))
+#define FSL_QDMA_BCQDPA_SADDR(x)	(0xcc + 0x100 * (x))
+#define FSL_QDMA_BCQEEPA_SADDR(x)	(0xd0 + 0x100 * (x))
+#define FSL_QDMA_BCQEPA_SADDR(x)	(0xd4 + 0x100 * (x))
+#define FSL_QDMA_BCQIER(x)		(0xe0 + 0x100 * (x))
+#define FSL_QDMA_BCQIDR(x)		(0xe4 + 0x100 * (x))
+
+#define FSL_QDMA_SQDPAR			0x80c
+#define FSL_QDMA_SQEPAR			0x814
+#define FSL_QDMA_BSQMR			0x800
+#define FSL_QDMA_BSQSR			0x804
+#define FSL_QDMA_BSQICR			0x828
+#define FSL_QDMA_CQMR			0xa00
+#define FSL_QDMA_CQDSCR1		0xa08
+#define FSL_QDMA_CQDSCR2                0xa0c
+#define FSL_QDMA_CQIER			0xa10
+#define FSL_QDMA_CQEDR			0xa14
+#define FSL_QDMA_SQCCMR			0xa20
+
+/* Registers for bit and genmask */
+#define FSL_QDMA_CQIDR_SQT		BIT(15)
+#define QDMA_CCDF_FOTMAT		BIT(29)
+#define QDMA_CCDF_SER			BIT(30)
+#define QDMA_SG_FIN			BIT(30)
+#define QDMA_SG_LEN_MASK		GENMASK(29, 0)
+#define QDMA_CCDF_MASK			GENMASK(28, 20)
+
+#define FSL_QDMA_BCQIER_CQTIE		BIT(15)
+#define FSL_QDMA_BCQIER_CQPEIE		BIT(23)
+#define FSL_QDMA_BSQICR_ICEN		BIT(31)
+
+#define FSL_QDMA_BSQICR_ICST(x)		((x) << 16)
+#define FSL_QDMA_CQIER_MEIE		BIT(31)
+#define FSL_QDMA_CQIER_TEIE		BIT(0)
+#define FSL_QDMA_SQCCMR_ENTER_WM	BIT(21)
+
+#define FSL_QDMA_BCQMR_EN		BIT(31)
+#define FSL_QDMA_BCQMR_EI		BIT(30)
+#define FSL_QDMA_BCQMR_CD_THLD(x)	((x) << 20)
+#define FSL_QDMA_BCQMR_CQ_SIZE(x)	((x) << 16)
+
+#define FSL_QDMA_BCQSR_QF		BIT(16)
+#define FSL_QDMA_BCQSR_XOFF		BIT(0)
+
+#define FSL_QDMA_BSQMR_EN		BIT(31)
+#define FSL_QDMA_BSQMR_DI		BIT(30)
+#define FSL_QDMA_BSQMR_CQ_SIZE(x)	((x) << 16)
+
+#define FSL_QDMA_BSQSR_QE		BIT(17)
+
+#define FSL_QDMA_DMR_DQD		BIT(30)
+#define FSL_QDMA_DSR_DB		BIT(31)
+
+/* Size related definition */
+#define FSL_QDMA_QUEUE_MAX		8
+#define FSL_QDMA_COMMAND_BUFFER_SIZE	64
+#define FSL_QDMA_DESCRIPTOR_BUFFER_SIZE 32
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MIN	64
+#define FSL_QDMA_CIRCULAR_DESC_SIZE_MAX	16384
+#define FSL_QDMA_QUEUE_NUM_MAX		8
+
+/* Field definition for CMD */
+#define FSL_QDMA_CMD_RWTTYPE		0x4
+#define FSL_QDMA_CMD_LWC                0x2
+#define FSL_QDMA_CMD_RWTTYPE_OFFSET	28
+#define FSL_QDMA_CMD_NS_OFFSET		27
+#define FSL_QDMA_CMD_DQOS_OFFSET	24
+#define FSL_QDMA_CMD_WTHROTL_OFFSET	20
+#define FSL_QDMA_CMD_DSEN_OFFSET	19
+#define FSL_QDMA_CMD_LWC_OFFSET		16
+
+/* Field definition for Descriptor offset */
+#define QDMA_CCDF_STATUS		20
+#define QDMA_CCDF_OFFSET		20
+
+#define	COMP_TIMEOUT			1000
+#define COMMAND_QUEUE_OVERFLLOW		10
+
+#define FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma_engine, x)			\
+	(((fsl_qdma_engine)->block_offset) * (x))
+
+/* qDMA status notification pre information */
+struct fsl_pre_status {
+	u64 queue;
+	u64 addr;
+};
+
+static DEFINE_PER_CPU(struct fsl_pre_status, pre);
+
+/**
+ * struct fsl_qdma_format - This is the struct holding describing compound
+ *			    descriptor format with qDMA.
+ * @status:		    Command status and enqueue status notification.
+ * @cfg:		    Frame offset and frame format.
+ * @addr_lo:		    Holding the compound descriptor of the lower
+ *			    32-bits address in memory 40-bit address.
+ * @addr_hi:		    Same as above member, but point high 8-bits in
+ *			    memory 40-bit address.
+ * @__reserved1:	    Reserved field.
+ * @cfg8b_w1:		    Compound descriptor command queue origin produced
+ *			    by qDMA and dynamic debug field.
+ * @data		    Pointer to the memory 40-bit address, describes DMA
+ *			    source information and DMA destination information.
+ */
+struct fsl_qdma_format {
+	__le32 status;
+	__le32 cfg;
+	union {
+		struct {
+			__le32 addr_lo;
+			u8 addr_hi;
+			u8 __reserved1[2];
+			u8 cfg8b_w1;
+		} __packed;
+		__le64 data;
+	};
+} __packed;
+
+static inline u64
+qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf)
+{
+	return le64_to_cpu(ccdf->data) & 0xffffffffffLLU;
+}
+
+static inline void
+qdma_desc_addr_set64(struct fsl_qdma_format *ccdf, u64 addr)
+{
+	ccdf->addr_hi = upper_32_bits(addr);
+	ccdf->addr_lo = cpu_to_le32(lower_32_bits(addr));
+}
+
+static inline u64
+qdma_ccdf_get_queue(const struct fsl_qdma_format *ccdf)
+{
+	return ccdf->cfg8b_w1 & 0xff;
+}
+
+static inline int
+qdma_ccdf_get_offset(const struct fsl_qdma_format *ccdf)
+{
+	return (le32_to_cpu(ccdf->cfg) & QDMA_CCDF_MASK) >> QDMA_CCDF_OFFSET;
+}
+
+static inline void
+qdma_ccdf_set_format(struct fsl_qdma_format *ccdf, int offset)
+{
+	ccdf->cfg = cpu_to_le32(QDMA_CCDF_FOTMAT | offset);
+}
+
+static inline int
+qdma_ccdf_get_status(const struct fsl_qdma_format *ccdf)
+{
+	return (le32_to_cpu(ccdf->status) & QDMA_CCDF_MASK) >> QDMA_CCDF_STATUS;
+}
+
+static inline void
+qdma_ccdf_set_ser(struct fsl_qdma_format *ccdf, int status)
+{
+	ccdf->status = cpu_to_le32(QDMA_CCDF_SER | status);
+}
+
+static inline void qdma_csgf_set_len(struct fsl_qdma_format *csgf, int len)
+{
+	csgf->cfg = cpu_to_le32(len & QDMA_SG_LEN_MASK);
+}
+
+static inline void qdma_csgf_set_f(struct fsl_qdma_format *csgf, int len)
+{
+	csgf->cfg = cpu_to_le32(QDMA_SG_FIN | (len & QDMA_SG_LEN_MASK));
+}
+
+/* qDMA Source Descriptor Format */
+struct fsl_qdma_sdf {
+	__le32 rev3;
+	__le32 cfg; /* rev4, bit[0-11] - ssd, bit[12-23] sss */
+	__le32 rev5;
+	__le32 cmd;
+} __packed;
+
+/* qDMA Destination Descriptor Format */
+struct fsl_qdma_ddf {
+	__le32 rev1;
+	__le32 cfg; /* rev2, bit[0-11] - dsd, bit[12-23] - dss */
+	__le32 rev3;
+	__le32 cmd;
+} __packed;
+
+struct fsl_qdma_chan {
+	struct virt_dma_chan		vchan;
+	struct virt_dma_desc		vdesc;
+	enum dma_status			status;
+	struct fsl_qdma_engine		*qdma;
+	struct fsl_qdma_queue		*queue;
+};
+
+struct fsl_qdma_queue {
+	struct fsl_qdma_format	*virt_head;
+	struct fsl_qdma_format	*virt_tail;
+	struct list_head	comp_used;
+	struct list_head	comp_free;
+	struct dma_pool		*comp_pool;
+	struct dma_pool		*desc_pool;
+	spinlock_t		queue_lock;
+	dma_addr_t		bus_addr;
+	u32                     n_cq;
+	u32			id;
+	struct fsl_qdma_format	*cq;
+	void __iomem		*block_base;
+};
+
+struct fsl_qdma_comp {
+	dma_addr_t              bus_addr;
+	dma_addr_t              desc_bus_addr;
+	void			*virt_addr;
+	void			*desc_virt_addr;
+	struct fsl_qdma_chan	*qchan;
+	struct virt_dma_desc    vdesc;
+	struct list_head	list;
+};
+
+struct fsl_qdma_engine {
+	struct dma_device	dma_dev;
+	void __iomem		*ctrl_base;
+	void __iomem            *status_base;
+	void __iomem		*block_base;
+	u32			n_chans;
+	u32			n_queues;
+	struct mutex            fsl_qdma_mutex;
+	int			error_irq;
+	int			*queue_irq;
+	u32			feature;
+	struct fsl_qdma_queue	*queue;
+	struct fsl_qdma_queue	**status;
+	struct fsl_qdma_chan	*chans;
+	int			block_number;
+	int			block_offset;
+	int			irq_base;
+	int			desc_allocated;
+
+};
+
+static u32 qdma_readl(struct fsl_qdma_engine *qdma, void __iomem *addr)
+{
+	return FSL_DMA_IN(qdma, addr, 32);
+}
+
+static void qdma_writel(struct fsl_qdma_engine *qdma, u32 val,
+						void __iomem *addr)
+{
+	FSL_DMA_OUT(qdma, addr, val, 32);
+}
+
+static struct fsl_qdma_chan *to_fsl_qdma_chan(struct dma_chan *chan)
+{
+	return container_of(chan, struct fsl_qdma_chan, vchan.chan);
+}
+
+static struct fsl_qdma_comp *to_fsl_qdma_comp(struct virt_dma_desc *vd)
+{
+	return container_of(vd, struct fsl_qdma_comp, vdesc);
+}
+
+static void fsl_qdma_free_chan_resources(struct dma_chan *chan)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+	struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+	struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma;
+	struct fsl_qdma_comp *comp_temp, *_comp_temp;
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+	vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+
+	vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+
+	if (!fsl_queue->comp_pool && !fsl_queue->comp_pool)
+		return;
+
+	list_for_each_entry_safe(comp_temp, _comp_temp,
+				&fsl_queue->comp_used,	list) {
+		dma_pool_free(fsl_queue->comp_pool,
+					 comp_temp->virt_addr,
+					 comp_temp->bus_addr);
+		dma_pool_free(fsl_queue->desc_pool,
+				comp_temp->desc_virt_addr,
+				comp_temp->desc_bus_addr);
+		list_del(&comp_temp->list);
+		kfree(comp_temp);
+	}
+
+	list_for_each_entry_safe(comp_temp, _comp_temp,
+				&fsl_queue->comp_free, list) {
+		dma_pool_free(fsl_queue->comp_pool,
+					 comp_temp->virt_addr,
+					 comp_temp->bus_addr);
+		dma_pool_free(fsl_queue->desc_pool,
+				comp_temp->desc_virt_addr,
+				comp_temp->desc_bus_addr);
+		list_del(&comp_temp->list);
+		kfree(comp_temp);
+	}
+
+	dma_pool_destroy(fsl_queue->comp_pool);
+	dma_pool_destroy(fsl_queue->desc_pool);
+
+	fsl_qdma->desc_allocated--;
+	fsl_queue->comp_pool = NULL;
+	fsl_queue->desc_pool = NULL;
+}
+
+static void fsl_qdma_comp_fill_memcpy(struct fsl_qdma_comp *fsl_comp,
+					dma_addr_t dst, dma_addr_t src, u32 len)
+{
+	struct fsl_qdma_format *ccdf, *csgf_desc, *csgf_src, *csgf_dest;
+	struct fsl_qdma_sdf *sdf;
+	struct fsl_qdma_ddf *ddf;
+
+	ccdf = (struct fsl_qdma_format *)fsl_comp->virt_addr;
+	csgf_desc = (struct fsl_qdma_format *)fsl_comp->virt_addr + 1;
+	csgf_src = (struct fsl_qdma_format *)fsl_comp->virt_addr + 2;
+	csgf_dest = (struct fsl_qdma_format *)fsl_comp->virt_addr + 3;
+	sdf = (struct fsl_qdma_sdf *)fsl_comp->desc_virt_addr;
+	ddf = (struct fsl_qdma_ddf *)fsl_comp->desc_virt_addr + 1;
+
+	memset(fsl_comp->virt_addr, 0, FSL_QDMA_COMMAND_BUFFER_SIZE);
+	memset(fsl_comp->desc_virt_addr, 0, FSL_QDMA_DESCRIPTOR_BUFFER_SIZE);
+	/* Head Command Descriptor(Frame Descriptor) */
+	qdma_desc_addr_set64(ccdf, fsl_comp->bus_addr + 16);
+	qdma_ccdf_set_format(ccdf, qdma_ccdf_get_offset(ccdf));
+	qdma_ccdf_set_ser(ccdf, qdma_ccdf_get_status(ccdf));
+	/* Status notification is enqueued to status queue. */
+	/* Compound Command Descriptor(Frame List Table) */
+	qdma_desc_addr_set64(csgf_desc, fsl_comp->desc_bus_addr);
+	/* It must be 32 as Compound S/G Descriptor */
+	qdma_csgf_set_len(csgf_desc, 32);
+	qdma_desc_addr_set64(csgf_src, src);
+	qdma_csgf_set_len(csgf_src, len);
+	qdma_desc_addr_set64(csgf_dest, dst);
+	qdma_csgf_set_len(csgf_dest, len);
+	/* This entry is the last entry. */
+	qdma_csgf_set_f(csgf_dest, len);
+	/* Descriptor Buffer */
+	sdf->cmd = cpu_to_le32(
+			FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+	ddf->cmd = cpu_to_le32(
+			FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+	ddf->cmd |= cpu_to_le32(
+			FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET);
+}
+
+/*
+ * Pre-request command descriptor and compound S/G for enqueue.
+ */
+static int fsl_qdma_pre_request_enqueue_comp_desc(struct fsl_qdma_queue *queue)
+{
+	struct fsl_qdma_comp *comp_temp;
+	int i;
+
+	for (i = 0; i < queue->n_cq + COMMAND_QUEUE_OVERFLLOW; i++) {
+		comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL);
+		if (!comp_temp)
+			return -ENOMEM;
+		comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
+						      GFP_KERNEL,
+						      &comp_temp->bus_addr);
+
+		if (!comp_temp->virt_addr) {
+			kfree(comp_temp);
+			return -ENOMEM;
+		}
+
+		list_add_tail(&comp_temp->list, &queue->comp_free);
+	}
+
+	return 0;
+}
+
+/*
+ * Pre-request source and destination descriptor for enqueue.
+ */
+static int fsl_qdma_pre_request_enqueue_sd_desc(struct fsl_qdma_queue *queue)
+{
+	struct fsl_qdma_comp *comp_temp, *_comp_temp;
+
+	list_for_each_entry_safe(comp_temp, _comp_temp,
+				&queue->comp_free, list) {
+		comp_temp->desc_virt_addr = dma_pool_alloc(queue->desc_pool,
+						GFP_KERNEL,
+						&comp_temp->desc_bus_addr);
+		if (!comp_temp->desc_virt_addr)
+			return -ENOMEM;
+	}
+
+	return 0;
+}
+
+/*
+ * Request a command descriptor for enqueue.
+ */
+static struct fsl_qdma_comp *fsl_qdma_request_enqueue_desc(
+					struct fsl_qdma_chan *fsl_chan)
+{
+	struct fsl_qdma_comp *comp_temp;
+	struct fsl_qdma_queue *queue = fsl_chan->queue;
+	unsigned long flags;
+	int timeout = COMP_TIMEOUT;
+
+	while (timeout) {
+		spin_lock_irqsave(&queue->queue_lock, flags);
+		if (!list_empty(&queue->comp_free)) {
+			comp_temp = list_first_entry(&queue->comp_free,
+					     struct fsl_qdma_comp,
+					     list);
+			list_del(&comp_temp->list);
+
+			spin_unlock_irqrestore(&queue->queue_lock, flags);
+			comp_temp->qchan = fsl_chan;
+			return comp_temp;
+		}
+		spin_unlock_irqrestore(&queue->queue_lock, flags);
+		udelay(1);
+		timeout--;
+	}
+
+	return NULL;
+}
+
+static struct fsl_qdma_queue *fsl_qdma_alloc_queue_resources(
+					struct platform_device *pdev,
+					struct fsl_qdma_engine *fsl_qdma)
+{
+	struct fsl_qdma_queue *queue_head, *queue_temp;
+	int ret, len, i, j;
+	unsigned int queue_size[FSL_QDMA_QUEUE_MAX];
+	int queue_num;
+	int block_number;
+
+	queue_num = fsl_qdma->n_queues;
+	block_number = fsl_qdma->block_number;
+
+	if (queue_num > FSL_QDMA_QUEUE_MAX)
+		queue_num = FSL_QDMA_QUEUE_MAX;
+	len = sizeof(*queue_head) * queue_num * block_number;
+	queue_head = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!queue_head)
+		return NULL;
+
+	ret = device_property_read_u32_array(&pdev->dev, "queue-sizes",
+					queue_size, queue_num);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get queue-sizes.\n");
+		return NULL;
+	}
+	for (j = 0; j < block_number; j++) {
+		for (i = 0; i < queue_num; i++) {
+			if (queue_size[i] > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX ||
+			   queue_size[i] < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+				dev_err(&pdev->dev,
+				       "Get wrong queue-sizes.\n");
+				return NULL;
+			}
+			queue_temp = queue_head + i + (j * queue_num);
+
+			queue_temp->cq =
+			dma_alloc_coherent(&pdev->dev,
+					sizeof(struct fsl_qdma_format) *
+					queue_size[i],
+					&queue_temp->bus_addr,
+					GFP_KERNEL);
+			if (!queue_temp->cq)
+				return NULL;
+			queue_temp->block_base = fsl_qdma->block_base +
+				FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+			queue_temp->n_cq = queue_size[i];
+			queue_temp->id = i;
+			queue_temp->virt_head = queue_temp->cq;
+			queue_temp->virt_tail = queue_temp->cq;
+			/*
+			 * List for queue command buffer
+			 */
+			INIT_LIST_HEAD(&queue_temp->comp_used);
+			spin_lock_init(&queue_temp->queue_lock);
+		}
+	}
+	return queue_head;
+}
+
+static struct fsl_qdma_queue *fsl_qdma_prep_status_queue(
+						struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct fsl_qdma_queue *status_head;
+	unsigned int status_size;
+	int ret;
+
+	ret = of_property_read_u32(np, "status-sizes", &status_size);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get status-sizes.\n");
+		return NULL;
+	}
+	if (status_size > FSL_QDMA_CIRCULAR_DESC_SIZE_MAX
+			|| status_size < FSL_QDMA_CIRCULAR_DESC_SIZE_MIN) {
+		dev_err(&pdev->dev, "Get wrong status_size.\n");
+		return NULL;
+	}
+	status_head = devm_kzalloc(&pdev->dev, sizeof(*status_head),
+								GFP_KERNEL);
+	if (!status_head)
+		return NULL;
+
+	/*
+	 * Buffer for queue command
+	 */
+	status_head->cq = dma_alloc_coherent(&pdev->dev,
+						sizeof(struct fsl_qdma_format) *
+						status_size,
+						&status_head->bus_addr,
+						GFP_KERNEL);
+	if (!status_head->cq)
+		return NULL;
+	status_head->n_cq = status_size;
+	status_head->virt_head = status_head->cq;
+	status_head->virt_tail = status_head->cq;
+	status_head->comp_pool = NULL;
+
+	return status_head;
+}
+
+static int fsl_qdma_halt(struct fsl_qdma_engine *fsl_qdma)
+{
+	void __iomem *ctrl = fsl_qdma->ctrl_base;
+	void __iomem *block;
+	int i, count = 5;
+	int j;
+	u32 reg;
+
+	/* Disable the command queue and wait for idle state. */
+	reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+	reg |= FSL_QDMA_DMR_DQD;
+	qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+	for (j = 0; j < fsl_qdma->block_number; j++) {
+		block = fsl_qdma->block_base +
+			FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+		for (i = 0; i < FSL_QDMA_QUEUE_NUM_MAX; i++)
+			qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQMR(i));
+	}
+	while (1) {
+		reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DSR);
+		if (!(reg & FSL_QDMA_DSR_DB))
+			break;
+		if (count-- < 0)
+			return -EBUSY;
+		udelay(100);
+	}
+
+	for (j = 0; j < fsl_qdma->block_number; j++) {
+
+		block = fsl_qdma->block_base +
+			FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+
+		/* Disable status queue. */
+		qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR);
+
+		/*
+		 * clear the command queue interrupt detect register for
+		 * all queues.
+		 */
+		qdma_writel(fsl_qdma, 0xffffffff, block + FSL_QDMA_BCQIDR(0));
+	}
+
+	return 0;
+}
+
+static int fsl_qdma_queue_transfer_complete(
+				struct fsl_qdma_engine *fsl_qdma,
+				void *block,
+				int id)
+{
+	struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+	struct fsl_qdma_queue *fsl_status = fsl_qdma->status[id];
+	struct fsl_qdma_queue *temp_queue;
+	struct fsl_qdma_format *status_addr;
+	struct fsl_qdma_comp *fsl_comp = NULL;
+	u32 reg, i;
+	bool duplicate, duplicate_handle;
+
+	while (1) {
+		duplicate = 0;
+		duplicate_handle = 0;
+		reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQSR);
+		if (reg & FSL_QDMA_BSQSR_QE)
+			return 0;
+
+		status_addr = fsl_status->virt_head;
+
+		if (qdma_ccdf_get_queue(status_addr) ==
+		   __this_cpu_read(pre.queue) &&
+			qdma_ccdf_addr_get64(status_addr) ==
+			__this_cpu_read(pre.addr))
+			duplicate = 1;
+		i = qdma_ccdf_get_queue(status_addr) +
+			id * fsl_qdma->n_queues;
+		__this_cpu_write(pre.addr, qdma_ccdf_addr_get64(status_addr));
+		__this_cpu_write(pre.queue, qdma_ccdf_get_queue(status_addr));
+		temp_queue = fsl_queue + i;
+
+		spin_lock(&temp_queue->queue_lock);
+		if (list_empty(&temp_queue->comp_used)) {
+			if (duplicate)
+				duplicate_handle = 1;
+			else {
+				spin_unlock(&temp_queue->queue_lock);
+				return -1;
+			}
+		} else {
+			fsl_comp = list_first_entry(&temp_queue->comp_used,
+							struct fsl_qdma_comp,
+							list);
+			if (fsl_comp->bus_addr + 16 !=
+				__this_cpu_read(pre.addr)) {
+				if (duplicate)
+					duplicate_handle = 1;
+				else {
+					spin_unlock(&temp_queue->queue_lock);
+					return -1;
+				}
+			}
+
+		}
+
+		if (duplicate_handle) {
+			reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+			reg |= FSL_QDMA_BSQMR_DI;
+			qdma_desc_addr_set64(status_addr, 0x0);
+			fsl_status->virt_head++;
+			if (fsl_status->virt_head == fsl_status->cq
+						   + fsl_status->n_cq)
+				fsl_status->virt_head = fsl_status->cq;
+			qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+			spin_unlock(&temp_queue->queue_lock);
+			continue;
+		}
+		list_del(&fsl_comp->list);
+
+		reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+		reg |= FSL_QDMA_BSQMR_DI;
+		qdma_desc_addr_set64(status_addr, 0x0);
+		fsl_status->virt_head++;
+		if (fsl_status->virt_head == fsl_status->cq + fsl_status->n_cq)
+			fsl_status->virt_head = fsl_status->cq;
+		qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+		spin_unlock(&temp_queue->queue_lock);
+
+		spin_lock(&fsl_comp->qchan->vchan.lock);
+		vchan_cookie_complete(&fsl_comp->vdesc);
+		fsl_comp->qchan->status = DMA_COMPLETE;
+		spin_unlock(&fsl_comp->qchan->vchan.lock);
+	}
+	return 0;
+}
+
+static irqreturn_t fsl_qdma_error_handler(int irq, void *dev_id)
+{
+	struct fsl_qdma_engine *fsl_qdma = dev_id;
+	unsigned int intr;
+	void __iomem *status = fsl_qdma->status_base;
+
+	intr = qdma_readl(fsl_qdma, status + FSL_QDMA_DEDR);
+
+	if (intr)
+		dev_err(fsl_qdma->dma_dev.dev, "DMA transaction error!\n");
+
+	qdma_writel(fsl_qdma, 0xffffffff, status + FSL_QDMA_DEDR);
+	return IRQ_HANDLED;
+}
+
+static irqreturn_t fsl_qdma_queue_handler(int irq, void *dev_id)
+{
+	struct fsl_qdma_engine *fsl_qdma = dev_id;
+	unsigned int intr, reg;
+	void __iomem *ctrl = fsl_qdma->ctrl_base;
+	void __iomem *block;
+	int id;
+
+	id = irq - fsl_qdma->irq_base;
+	if (id < 0 && id > fsl_qdma->block_number) {
+		dev_err(fsl_qdma->dma_dev.dev,
+			"irq %d is wrong irq_base is %d\n",
+			irq, fsl_qdma->irq_base);
+	}
+
+	block = fsl_qdma->block_base +
+		FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, id);
+
+	intr = qdma_readl(fsl_qdma, block + FSL_QDMA_BCQIDR(0));
+
+	if ((intr & FSL_QDMA_CQIDR_SQT) != 0)
+		intr = fsl_qdma_queue_transfer_complete(fsl_qdma, block, id);
+
+	if (intr != 0) {
+		reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+		reg |= FSL_QDMA_DMR_DQD;
+		qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+		qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BCQIER(0));
+		dev_err(fsl_qdma->dma_dev.dev, "QDMA: status err!\n");
+	}
+
+	qdma_writel(fsl_qdma, 0xffffffff, block + FSL_QDMA_BCQIDR(0));
+
+	return IRQ_HANDLED;
+}
+
+static int
+fsl_qdma_irq_init(struct platform_device *pdev,
+		  struct fsl_qdma_engine *fsl_qdma)
+{
+	char irq_name[20];
+	int i;
+	int cpu;
+	int ret;
+
+	fsl_qdma->error_irq = platform_get_irq_byname(pdev,
+							"qdma-error");
+	if (fsl_qdma->error_irq < 0) {
+		dev_err(&pdev->dev, "Can't get qdma controller irq.\n");
+		return fsl_qdma->error_irq;
+	}
+
+	ret = devm_request_irq(&pdev->dev, fsl_qdma->error_irq,
+			fsl_qdma_error_handler, 0, "qDMA error", fsl_qdma);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't register qDMA controller IRQ.\n");
+		return  ret;
+	}
+
+	for (i = 0; i < fsl_qdma->block_number; i++) {
+		sprintf(irq_name, "qdma-queue%d", i);
+		fsl_qdma->queue_irq[i] = platform_get_irq_byname(pdev,
+								irq_name);
+
+		if (fsl_qdma->queue_irq[i] < 0) {
+			dev_err(&pdev->dev,
+				   "Can't get qdma queue %d irq.\n",
+				   i);
+			return fsl_qdma->queue_irq[i];
+		}
+
+		ret = devm_request_irq(&pdev->dev,
+				      fsl_qdma->queue_irq[i],
+				      fsl_qdma_queue_handler,
+				      0,
+				      "qDMA queue",
+				      fsl_qdma);
+		if (ret) {
+			dev_err(&pdev->dev,
+			       "Can't register qDMA queue IRQ.\n");
+			return  ret;
+		}
+
+		cpu = i % num_online_cpus();
+		ret = irq_set_affinity_hint(fsl_qdma->queue_irq[i],
+					   get_cpu_mask(cpu));
+		if (ret) {
+			dev_err(&pdev->dev,
+			       "Can't set cpu %d affinity to IRQ %d.\n",
+				cpu,
+				fsl_qdma->queue_irq[i]);
+			return  ret;
+		}
+
+	}
+
+	return 0;
+}
+
+static void fsl_qdma_irq_exit(
+		struct platform_device *pdev, struct fsl_qdma_engine *fsl_qdma)
+{
+	if (fsl_qdma->queue_irq[0] == fsl_qdma->error_irq) {
+		devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[0], fsl_qdma);
+	} else {
+		devm_free_irq(&pdev->dev, fsl_qdma->queue_irq[0], fsl_qdma);
+		devm_free_irq(&pdev->dev, fsl_qdma->error_irq, fsl_qdma);
+	}
+}
+
+static int fsl_qdma_reg_init(struct fsl_qdma_engine *fsl_qdma)
+{
+	struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+	struct fsl_qdma_queue *temp;
+	void __iomem *ctrl = fsl_qdma->ctrl_base;
+	void __iomem *status = fsl_qdma->status_base;
+	void __iomem *block;
+	int i, j, ret;
+	u32 reg;
+
+	/* Try to halt the qDMA engine first. */
+	ret = fsl_qdma_halt(fsl_qdma);
+	if (ret) {
+		dev_err(fsl_qdma->dma_dev.dev, "DMA halt failed!");
+		return ret;
+	}
+
+	for (i = 0; i < fsl_qdma->block_number; i++) {
+		/*
+		 * Clear the command queue interrupt detect register for
+		 * all queues.
+		 */
+
+		block = fsl_qdma->block_base +
+			FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, i);
+		qdma_writel(fsl_qdma, 0xffffffff, block + FSL_QDMA_BCQIDR(0));
+	}
+
+	for (j = 0; j < fsl_qdma->block_number; j++) {
+		block = fsl_qdma->block_base +
+			FSL_QDMA_BLOCK_BASE_OFFSET(fsl_qdma, j);
+		for (i = 0; i < fsl_qdma->n_queues; i++) {
+			temp = fsl_queue + i + (j * fsl_qdma->n_queues);
+			/*
+			 * Initialize Command Queue registers to
+			 * point to the first
+			 * command descriptor in memory.
+			 * Dequeue Pointer Address Registers
+			 * Enqueue Pointer Address Registers
+			 */
+
+			qdma_writel(fsl_qdma, temp->bus_addr,
+					   block + FSL_QDMA_BCQDPA_SADDR(i));
+			qdma_writel(fsl_qdma, temp->bus_addr,
+					   block + FSL_QDMA_BCQEPA_SADDR(i));
+
+			/* Initialize the queue mode. */
+			reg = FSL_QDMA_BCQMR_EN;
+			reg |= FSL_QDMA_BCQMR_CD_THLD(ilog2(temp->n_cq) - 4);
+			reg |= FSL_QDMA_BCQMR_CQ_SIZE(ilog2(temp->n_cq) - 6);
+			qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BCQMR(i));
+		}
+
+		/*
+		 * Workaround for erratum: ERR010812.
+		 * We must enable XOFF to avoid the enqueue rejection occurs.
+		 * Setting SQCCMR ENTER_WM to 0x20.
+		 */
+
+		qdma_writel(fsl_qdma, FSL_QDMA_SQCCMR_ENTER_WM,
+				   block + FSL_QDMA_SQCCMR);
+
+		/*
+		 * Initialize status queue registers to point to the first
+		 * command descriptor in memory.
+		 * Dequeue Pointer Address Registers
+		 * Enqueue Pointer Address Registers
+		 */
+
+		qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr,
+				   block + FSL_QDMA_SQEPAR);
+		qdma_writel(fsl_qdma, fsl_qdma->status[j]->bus_addr,
+				   block + FSL_QDMA_SQDPAR);
+		/* Initialize status queue interrupt. */
+		qdma_writel(fsl_qdma, FSL_QDMA_BCQIER_CQTIE,
+				   block + FSL_QDMA_BCQIER(0));
+		qdma_writel(fsl_qdma, FSL_QDMA_BSQICR_ICEN |
+				   FSL_QDMA_BSQICR_ICST(5) | 0x8000,
+				   block + FSL_QDMA_BSQICR);
+		qdma_writel(fsl_qdma, FSL_QDMA_CQIER_MEIE |
+				   FSL_QDMA_CQIER_TEIE,
+				   block + FSL_QDMA_CQIER);
+
+		/* Initialize the status queue mode. */
+		reg = FSL_QDMA_BSQMR_EN;
+		reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2(
+			fsl_qdma->status[j]->n_cq) - 6);
+
+		qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+		reg = qdma_readl(fsl_qdma, block + FSL_QDMA_BSQMR);
+
+	}
+
+	/* Initialize controller interrupt register. */
+	qdma_writel(fsl_qdma, 0xffffffff, status + FSL_QDMA_DEDR);
+	qdma_writel(fsl_qdma, 0xffffffff, status + FSL_QDMA_DEIER);
+
+	reg = qdma_readl(fsl_qdma, ctrl + FSL_QDMA_DMR);
+	reg &= ~FSL_QDMA_DMR_DQD;
+	qdma_writel(fsl_qdma, reg, ctrl + FSL_QDMA_DMR);
+
+	return 0;
+}
+
+static struct dma_async_tx_descriptor *
+fsl_qdma_prep_memcpy(struct dma_chan *chan, dma_addr_t dst,
+		dma_addr_t src, size_t len, unsigned long flags)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+	struct fsl_qdma_comp *fsl_comp;
+
+	fsl_comp = fsl_qdma_request_enqueue_desc(fsl_chan);
+
+	if (!fsl_comp)
+		return NULL;
+
+	fsl_qdma_comp_fill_memcpy(fsl_comp, dst, src, len);
+
+	return vchan_tx_prep(&fsl_chan->vchan, &fsl_comp->vdesc, flags);
+}
+
+static void fsl_qdma_enqueue_desc(struct fsl_qdma_chan *fsl_chan)
+{
+	struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+	struct fsl_qdma_comp *fsl_comp;
+	struct virt_dma_desc *vdesc;
+	void __iomem *block = fsl_queue->block_base;
+	u32 reg;
+
+	reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQSR(fsl_queue->id));
+	if (reg & (FSL_QDMA_BCQSR_QF | FSL_QDMA_BCQSR_XOFF))
+		return;
+	vdesc = vchan_next_desc(&fsl_chan->vchan);
+	if (!vdesc)
+		return;
+	list_del(&vdesc->node);
+	fsl_comp = to_fsl_qdma_comp(vdesc);
+
+	memcpy(fsl_queue->virt_head++, fsl_comp->virt_addr, 16);
+	if (fsl_queue->virt_head == fsl_queue->cq + fsl_queue->n_cq)
+		fsl_queue->virt_head = fsl_queue->cq;
+
+	list_add_tail(&fsl_comp->list, &fsl_queue->comp_used);
+	barrier();
+	reg = qdma_readl(fsl_chan->qdma, block + FSL_QDMA_BCQMR(fsl_queue->id));
+	reg |= FSL_QDMA_BCQMR_EI;
+	qdma_writel(fsl_chan->qdma, reg, block + FSL_QDMA_BCQMR(fsl_queue->id));
+	fsl_chan->status = DMA_IN_PROGRESS;
+}
+
+static enum dma_status fsl_qdma_tx_status(struct dma_chan *chan,
+		dma_cookie_t cookie, struct dma_tx_state *txstate)
+{
+	return dma_cookie_status(chan, cookie, txstate);
+}
+
+static void fsl_qdma_free_desc(struct virt_dma_desc *vdesc)
+{
+	struct fsl_qdma_comp *fsl_comp;
+	struct fsl_qdma_queue *fsl_queue;
+	unsigned long flags;
+
+	fsl_comp = to_fsl_qdma_comp(vdesc);
+	fsl_queue = fsl_comp->qchan->queue;
+
+	spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+	list_add_tail(&fsl_comp->list, &fsl_queue->comp_free);
+	spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static void fsl_qdma_issue_pending(struct dma_chan *chan)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+	struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+	unsigned long flags;
+
+	spin_lock_irqsave(&fsl_queue->queue_lock, flags);
+	spin_lock(&fsl_chan->vchan.lock);
+	if (vchan_issue_pending(&fsl_chan->vchan))
+		fsl_qdma_enqueue_desc(fsl_chan);
+	spin_unlock(&fsl_chan->vchan.lock);
+	spin_unlock_irqrestore(&fsl_queue->queue_lock, flags);
+}
+
+static void fsl_qdma_synchronize(struct dma_chan *chan)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+
+	vchan_synchronize(&fsl_chan->vchan);
+}
+
+static int fsl_qdma_terminate_all(struct dma_chan *chan)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock_irqsave(&fsl_chan->vchan.lock, flags);
+	vchan_get_all_descriptors(&fsl_chan->vchan, &head);
+	spin_unlock_irqrestore(&fsl_chan->vchan.lock, flags);
+	vchan_dma_desc_free_list(&fsl_chan->vchan, &head);
+	return 0;
+}
+
+static int fsl_qdma_alloc_chan_resources(struct dma_chan *chan)
+{
+	struct fsl_qdma_chan *fsl_chan = to_fsl_qdma_chan(chan);
+	struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+	struct fsl_qdma_engine *fsl_qdma = fsl_chan->qdma;
+	int ret;
+
+	if (fsl_queue->comp_pool && fsl_queue->desc_pool)
+		return fsl_qdma->desc_allocated;
+
+	INIT_LIST_HEAD(&fsl_queue->comp_free);
+
+	/*
+	 * The dma pool for queue command buffer
+	 */
+	fsl_queue->comp_pool =
+	dma_pool_create("comp_pool",
+		       chan->device->dev,
+		       FSL_QDMA_COMMAND_BUFFER_SIZE,
+		       64, 0);
+	if (!fsl_queue->comp_pool)
+		return -ENOMEM;
+
+	/*
+	 * The dma pool for Descriptor(SD/DD) buffer
+	 */
+	fsl_queue->desc_pool =
+	dma_pool_create("desc_pool",
+		       chan->device->dev,
+		       FSL_QDMA_DESCRIPTOR_BUFFER_SIZE,
+		       32, 0);
+	if (!fsl_queue->desc_pool)
+		goto err_desc_pool;
+
+	ret = fsl_qdma_pre_request_enqueue_comp_desc(fsl_queue);
+	if (ret) {
+		dev_err(chan->device->dev, "failed to alloc dma buffer for "
+				"comp S/G descriptor\n");
+		goto err_mem;
+	}
+
+	ret = fsl_qdma_pre_request_enqueue_sd_desc(fsl_queue);
+	if (ret) {
+		dev_err(chan->device->dev, "failed to alloc dma buffer for "
+				"S/D descriptor\n");
+		goto err_mem;
+	}
+
+	fsl_qdma->desc_allocated++;
+	return fsl_qdma->desc_allocated;
+
+err_mem:
+	dma_pool_destroy(fsl_queue->desc_pool);
+err_desc_pool:
+	dma_pool_destroy(fsl_queue->comp_pool);
+	return -ENOMEM;
+}
+
+static int fsl_qdma_probe(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct fsl_qdma_engine *fsl_qdma;
+	struct fsl_qdma_chan *fsl_chan;
+	struct resource *res;
+	unsigned int len, chans, queues;
+	int ret, i;
+	int blk_num;
+	int blk_off;
+
+	ret = of_property_read_u32(np, "channels", &chans);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get channels.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "block-offset", &blk_off);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get block-offset.\n");
+		return ret;
+	}
+
+	ret = of_property_read_u32(np, "block-number", &blk_num);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get block-number.\n");
+		return ret;
+	}
+
+	blk_num = min_t(int, blk_num, num_online_cpus());
+
+	len = sizeof(*fsl_qdma);
+	fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!fsl_qdma)
+		return -ENOMEM;
+
+	len = sizeof(*fsl_chan) * chans;
+	fsl_qdma->chans = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!fsl_qdma->chans)
+		return -ENOMEM;
+
+	len = sizeof(struct fsl_qdma_queue *) * blk_num;
+	fsl_qdma->status = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!fsl_qdma->status)
+		return -ENOMEM;
+
+	len = sizeof(int) * blk_num;
+	fsl_qdma->queue_irq = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+	if (!fsl_qdma->queue_irq)
+		return -ENOMEM;
+
+	ret = of_property_read_u32(np, "queues", &queues);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't get queues.\n");
+		return ret;
+	}
+
+	fsl_qdma->desc_allocated = 0;
+	fsl_qdma->n_chans = chans;
+	fsl_qdma->n_queues = queues;
+	fsl_qdma->block_number = blk_num;
+	fsl_qdma->block_offset = blk_off;
+
+	mutex_init(&fsl_qdma->fsl_qdma_mutex);
+
+	for (i = 0; i < fsl_qdma->block_number; i++) {
+		fsl_qdma->status[i] = fsl_qdma_prep_status_queue(pdev);
+		if (!fsl_qdma->status[i])
+			return -ENOMEM;
+	}
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	fsl_qdma->ctrl_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(fsl_qdma->ctrl_base))
+		return PTR_ERR(fsl_qdma->ctrl_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	fsl_qdma->status_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(fsl_qdma->status_base))
+		return PTR_ERR(fsl_qdma->status_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	fsl_qdma->block_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(fsl_qdma->block_base))
+		return PTR_ERR(fsl_qdma->block_base);
+	fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, fsl_qdma);
+	if (!fsl_qdma->queue)
+		return -ENOMEM;
+
+	ret = fsl_qdma_irq_init(pdev, fsl_qdma);
+	if (ret)
+		return ret;
+
+	fsl_qdma->irq_base = platform_get_irq_byname(pdev, "qdma-queue0");
+	fsl_qdma->feature = of_property_read_bool(np, "big-endian");
+	INIT_LIST_HEAD(&fsl_qdma->dma_dev.channels);
+
+	for (i = 0; i < fsl_qdma->n_chans; i++) {
+		struct fsl_qdma_chan *fsl_chan = &fsl_qdma->chans[i];
+
+		fsl_chan->qdma = fsl_qdma;
+		fsl_chan->queue = fsl_qdma->queue + i % (fsl_qdma->n_queues *
+							fsl_qdma->block_number);
+		fsl_chan->vchan.desc_free = fsl_qdma_free_desc;
+		vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev);
+	}
+
+	dma_cap_set(DMA_MEMCPY, fsl_qdma->dma_dev.cap_mask);
+
+	fsl_qdma->dma_dev.dev = &pdev->dev;
+	fsl_qdma->dma_dev.device_free_chan_resources
+		= fsl_qdma_free_chan_resources;
+	fsl_qdma->dma_dev.device_alloc_chan_resources
+		= fsl_qdma_alloc_chan_resources;
+	fsl_qdma->dma_dev.device_tx_status = fsl_qdma_tx_status;
+	fsl_qdma->dma_dev.device_prep_dma_memcpy = fsl_qdma_prep_memcpy;
+	fsl_qdma->dma_dev.device_issue_pending = fsl_qdma_issue_pending;
+	fsl_qdma->dma_dev.device_synchronize = fsl_qdma_synchronize;
+	fsl_qdma->dma_dev.device_terminate_all = fsl_qdma_terminate_all;
+
+	dma_set_mask(&pdev->dev, DMA_BIT_MASK(40));
+
+	platform_set_drvdata(pdev, fsl_qdma);
+
+	ret = dma_async_device_register(&fsl_qdma->dma_dev);
+	if (ret) {
+		dev_err(&pdev->dev,
+			"Can't register NXP Layerscape qDMA engine.\n");
+		return ret;
+	}
+
+	ret = fsl_qdma_reg_init(fsl_qdma);
+	if (ret) {
+		dev_err(&pdev->dev, "Can't Initialize the qDMA engine.\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void fsl_qdma_cleanup_vchan(struct dma_device *dmadev)
+{
+	struct fsl_qdma_chan *chan, *_chan;
+
+	list_for_each_entry_safe(chan, _chan,
+				&dmadev->channels, vchan.chan.device_node) {
+		list_del(&chan->vchan.chan.device_node);
+		tasklet_kill(&chan->vchan.task);
+	}
+}
+
+static int fsl_qdma_remove(struct platform_device *pdev)
+{
+	struct device_node *np = pdev->dev.of_node;
+	struct fsl_qdma_engine *fsl_qdma = platform_get_drvdata(pdev);
+	struct fsl_qdma_queue *status;
+	int i;
+
+	fsl_qdma_irq_exit(pdev, fsl_qdma);
+	fsl_qdma_cleanup_vchan(&fsl_qdma->dma_dev);
+	of_dma_controller_free(np);
+	dma_async_device_unregister(&fsl_qdma->dma_dev);
+
+	for (i = 0; i < fsl_qdma->block_number; i++) {
+		status = fsl_qdma->status[i];
+		dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
+				status->n_cq, status->cq, status->bus_addr);
+	}
+	return 0;
+}
+
+static const struct of_device_id fsl_qdma_dt_ids[] = {
+	{ .compatible = "fsl,ls1021a-qdma", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, fsl_qdma_dt_ids);
+
+static struct platform_driver fsl_qdma_driver = {
+	.driver		= {
+		.name	= "fsl-qdma",
+		.of_match_table = fsl_qdma_dt_ids,
+	},
+	.probe          = fsl_qdma_probe,
+	.remove		= fsl_qdma_remove,
+};
+
+module_platform_driver(fsl_qdma_driver);
+
+MODULE_ALIAS("platform:fsl-qdma");
+MODULE_DESCRIPTION("NXP Layerscape qDMA engine driver");
+MODULE_LICENSE("GPL v2");

^ permalink raw reply related

* [4/7] arm: dts: ls1021a: add qdma device tree nodes
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Peng Ma

add the qDMA device tree nodes for LS1021A devices.

Signed-off-by: Peng Ma <peng.ma@nxp.com>
---
 arch/arm/boot/dts/ls1021a.dtsi |   16 ++++++++++++++++
 1 files changed, 16 insertions(+), 0 deletions(-)

diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index f184905..abc50da 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -806,5 +806,21 @@
 			#size-cells = <1>;
 			ranges = <0x0 0x0 0x10010000 0x10000>;
 		};
+
+		qdma: qdma@8390000 {
+			compatible = "fsl,ls1021a-qdma";
+			reg = <0x0 0x8398000 0x0 0x1000>, /* Controller regs */
+			      <0x0 0x8399000 0x0 0x1000>, /* Status regs */
+			      <0x0 0x839a000 0x0 0x2000>; /* Block regs */
+			interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_HIGH>,
+				     <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+			interrupt-names = "qdma-error", "qdma-queue";
+			channels = <8>;
+			queues = <2>;
+			status-sizes = <64>;
+			queue-sizes = <64 64>;
+			big-endian;
+		};
+
 	};
 };

^ permalink raw reply related

* [5/7] arm64: dts: ls1043a: add qdma device tree nodes
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Peng Ma

add the qDMA device tree nodes for LS1043A devices.

Signed-off-by: Peng Ma <peng.ma@nxp.com>
---
 arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi |   22 ++++++++++++++++++++++
 1 files changed, 22 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
index 7881e3d..49c6655 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
@@ -734,6 +734,28 @@
 					<0000 0 0 3 &gic 0 156 0x4>,
 					<0000 0 0 4 &gic 0 157 0x4>;
 		};
+
+		qdma: qdma@8380000 {
+			compatible = "fsl,ls1021a-qdma", "fsl,ls1043a-qdma";
+			reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
+			      <0x0 0x8390000 0x0 0x10000>, /* Status regs */
+			      <0x0 0x83a0000 0x0 0x40000>; /* Block regs */
+			interrupts = <0 153 0x4>,
+				     <0 39 0x4>,
+				     <0 40 0x4>,
+				     <0 41 0x4>,
+				     <0 42 0x4>;
+			interrupt-names = "qdma-error", "qdma-queue0",
+				"qdma-queue1", "qdma-queue2", "qdma-queue3";
+			channels = <8>;
+			block-number = <1>;
+			block-offset = <0x10000>;
+			queues = <2>;
+			status-sizes = <64>;
+			queue-sizes = <64 64>;
+			big-endian;
+		};
+
 	};
 
 	firmware {

^ permalink raw reply related

* [6/7] arm64: dts: ls1046a: add qdma device tree nodes
From: Peng Ma @ 2018-10-11  9:46 UTC (permalink / raw)
  To: vkoul, leoyang.li
  Cc: robh+dt, mark.rutland, shawnguo, dan.j.williams, zw, dmaengine,
	devicetree, linux-kernel, linux-arm-kernel, linuxppc-dev, Peng Ma

add the qDMA device tree nodes for LS1046A devices.

Signed-off-by: Peng Ma <peng.ma@nxp.com>
---
 arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi |   21 +++++++++++++++++++++
 1 files changed, 21 insertions(+), 0 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index ef83786..9f36293 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -704,6 +704,27 @@
 					<0000 0 0 4 &gic GIC_SPI 154 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
+		qdma: qdma@8380000 {
+			compatible = "fsl,ls1046a-qdma", "fsl,ls1021a-qdma";
+			reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
+			      <0x0 0x8390000 0x0 0x10000>, /* Status regs */
+			      <0x0 0x83a0000 0x0 0x40000>; /* Block regs */
+			interrupts = <0 153 0x4>,
+				     <0 39 0x4>,
+				     <0 40 0x4>,
+				     <0 41 0x4>,
+				     <0 42 0x4>;
+			interrupt-names = "qdma-error", "qdma-queue0",
+				"qdma-queue1", "qdma-queue2", "qdma-queue3";
+			channels = <8>;
+			block-number = <1>;
+			block-offset = <0x10000>;
+			queues = <2>;
+			status-sizes = <64>;
+			queue-sizes = <64 64>;
+			big-endian;
+		};
+
 	};
 
 	reserved-memory {

^ permalink raw reply related


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox