* [v2,1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Andreas Färber @ 2018-07-24 12:19 UTC (permalink / raw)
To: Vinod
Cc: Manivannan Sadhasivam, dan.j.williams, robh+dt, dmaengine, liuwei,
96boards, devicetree, daniel.thompson, amit.kucheria,
linux-arm-kernel, linux-kernel, hzhang, bdong, manivannanece23,
thomas.liau, jeff.chen, pn, edgar.righi
Hi Vinod,
Am 24.07.2018 um 14:16 schrieb Vinod:
> Patch title should be dmaengine: ... Please always use the apt tags and
> one can find them using git log <subsystem>
Do you mean dt-bindings: dmaengine: ...? Because this is clearly a DT
binding and not a driver patch.
Regards,
Andreas
^ permalink raw reply
* [v2,1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Vinod Koul @ 2018-07-24 12:16 UTC (permalink / raw)
To: Manivannan Sadhasivam
Cc: dan.j.williams, afaerber, robh+dt, dmaengine, liuwei, 96boards,
devicetree, daniel.thompson, amit.kucheria, linux-arm-kernel,
linux-kernel, hzhang, bdong, manivannanece23, thomas.liau,
jeff.chen, pn, edgar.righi
Hi Mani,
Patch title should be dmaengine: ... Please always use the apt tags and
one can find them using git log <subsystem>
On 23-07-18, 09:47, Manivannan Sadhasivam wrote:
> Add devicetree binding for Actions Semi Owl SoCs DMA controller.
It would help for review to describe the controller here
>
> Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
> ---
> .../devicetree/bindings/dma/owl-dma.txt | 46 +++++++++++++++++++
> 1 file changed, 46 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/dma/owl-dma.txt
>
> diff --git a/Documentation/devicetree/bindings/dma/owl-dma.txt b/Documentation/devicetree/bindings/dma/owl-dma.txt
> new file mode 100644
> index 000000000000..dd6ce237b216
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/owl-dma.txt
> @@ -0,0 +1,46 @@
> +* Actions Semi Owl SoCs DMA controller
> +
> +This binding follows the generic DMA bindings defined in dma.txt.
> +
> +Required properties:
> +- compatible: Should be "actions,s900-dma".
> +- reg: Should contain DMA registers location and length.
> +- interrupts: Should contain 4 interrupts shared by all channel.
> +- #dma-cells: Must be <1>. Used to represent the number of integer
> + cells in the dmas property of client device.
> +- dma-channels: Physical channels supported.
> +- dma-requests: Virtual channels supported.
not really, virtual channel is a software concepts. On the other hand
you may have request lines in hw and you can describe that, if not skip
this one
> +- clocks: Phandle and Specifier of the clock feeding the DMA controller.
> +
> +Example:
> +
> +Controller:
> + dma: dma-controller@e0260000 {
> + compatible = "actions,s900-dma";
> + reg = <0x0 0xe0260000 0x0 0x1000>;
> + interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
> + #dma-cells = <1>;
> + dma-channels = <12>;
> + dma-requests = <46>;
> + clocks = <&clock CLK_DMAC>;
> + };
> +
> +Client:
> +
> +DMA clients connected to the Actions Semi Owl SoCs DMA controller must
> +use the format described in the dma.txt file, using a two-cell specifier
> +for each channel.
> +
> +The two cells in order are:
> +1. A phandle pointing to the DMA controller.
> +2. The channel id.
> +
> +uart5: serial@e012a000 {
> + ...
> + dma-names = "tx", "rx";
> + dmas = <&dma 26>, <&dma 27>;
> + ...
> +};
> --
> 2.17.1
^ permalink raw reply
* [09/15] dmaengine: img-mdc: Handle early status read
From: Vinod Koul @ 2018-07-24 11:36 UTC (permalink / raw)
To: Andreas Färber
Cc: linux-mips, Ralf Baechle, Paul Burton, James Hogan, linux-kernel,
Damien Horsley, Dan Williams, dmaengine
On 22-07-18, 23:20, Andreas Färber wrote:
> From: Damien Horsley <damien.horsley@imgtec.com>
>
> It is possible that mdc_tx_status may be called before the first
> node has been read from memory.
>
> In this case, the residue value stored in the register is undefined.
> Return the transfer size instead.
>
> Signed-off-by: Damien Horsley <damien.horsley@imgtec.com>
> Signed-off-by: Andreas Färber <afaerber@suse.de>
> ---
> drivers/dma/img-mdc-dma.c | 40 ++++++++++++++++++++++++----------------
> 1 file changed, 24 insertions(+), 16 deletions(-)
>
> diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
> index 25cec9c243e1..0f2f0f52d83a 100644
> --- a/drivers/dma/img-mdc-dma.c
> +++ b/drivers/dma/img-mdc-dma.c
> @@ -621,25 +621,33 @@ static enum dma_status mdc_tx_status(struct dma_chan *chan,
> (MDC_CMDS_PROCESSED_CMDS_DONE_MASK + 1);
>
> /*
> - * If the command loaded event hasn't been processed yet, then
> - * the difference above includes an extra command.
> + * If the first node has not yet been read from memory,
> + * the residue register value is undefined
so is this the case when transfer is not started ?
> */
> - if (!mdesc->cmd_loaded)
> - cmds--;
> - else
> - cmds += mdesc->list_cmds_done;
> -
> - bytes = mdesc->list_xfer_size;
> - ldesc = mdesc->list;
> - for (i = 0; i < cmds; i++) {
> - bytes -= ldesc->xfer_size + 1;
> - ldesc = ldesc->next_desc;
> - }
> - if (ldesc) {
> - if (residue != MDC_TRANSFER_SIZE_MASK)
> - bytes -= ldesc->xfer_size - residue;
> + if (!mdesc->cmd_loaded && !cmds) {
> + bytes = mdesc->list_xfer_size;
> + } else {
> + /*
> + * If the command loaded event hasn't been processed yet, then
> + * the difference above includes an extra command.
> + */
> + if (!mdesc->cmd_loaded)
> + cmds--;
> else
> + cmds += mdesc->list_cmds_done;
> +
> + bytes = mdesc->list_xfer_size;
> + ldesc = mdesc->list;
> + for (i = 0; i < cmds; i++) {
> bytes -= ldesc->xfer_size + 1;
> + ldesc = ldesc->next_desc;
> + }
> + if (ldesc) {
> + if (residue != MDC_TRANSFER_SIZE_MASK)
> + bytes -= ldesc->xfer_size - residue;
> + else
> + bytes -= ldesc->xfer_size + 1;
> + }
> }
> }
> spin_unlock_irqrestore(&mchan->vc.lock, flags);
> --
> 2.16.4
^ permalink raw reply
* [RFC] dmaengine: Add metadat_ops for dma_async_tx_descriptor
From: Vinod Koul @ 2018-07-24 11:14 UTC (permalink / raw)
To: Peter Ujfalusi
Cc: radheys, vinod.koul, lars, michal.simek, linux-kernel, dmaengine,
dan.j.williams, appanad, linux-arm-kernel
On 20-07-18, 16:42, Peter Ujfalusi wrote:
>
>
> On 2018-07-19 12:22, Vinod wrote:
> > Hi Peter,
> >
> > On 18-07-18, 13:06, Peter Ujfalusi wrote:
> >
> >>>> +struct dma_async_tx_descriptor;
> >>>> +
> >>>> +struct dma_descriptor_metadata_ops {
> >>>> + int (*attach)(struct dma_async_tx_descriptor *desc, void *data,
> >>>> + size_t len);
> >>>
> >>> How does one detach?
> >>
> >> I have not thought about detach, but clients can just attach NULL I guess.
> >
> > So what are the implication of attach and detach here, should the data
> > be deref by dmaengine driver and drop the ref.
>
> It largely depends on the DMA driver, but I think we must have clear
> definition on how clients (and thus DMA drivers) must handle the metadata.
Correct, defining these will help out get clarity and avoid abuse.
> I think the simpler rule would be that clients _must_ attach the
> metadata buffer after _prepare() and before issue_pending() and they
> must make sure that the buffer is valid (not freed up) before the
> completion callback is called for the given descriptor.
>
> About the detach: If clients detaches the metadata buffer then on
> completion it is not going to receive back any metadata and I think the
> DMA drivers should clean and disable the metadata sending as well if the
> detach happens before issue_pending().
>
> > Should anyone do refcounting?
>
> Need to think about that.
>
> >>
> >>> When should the client free up the memory, IOW when
> >>> does dma driver drop ref to data.
> >>
> >> The metadata is for the descriptor so the DMA driver might want to
> >> access to it while the descriptor is valid.
> >>
> >> Typically clients can free up their metadata storage after the dma
> >> completion callback. On DEV_TO_MEM the metadata is going to be placed in
> >> the provided buffer when the transfer is completed.
> >
> > That sounds okay to me
> >
> >>>> + void *(*get_ptr)(struct dma_async_tx_descriptor *desc,
> >>>> + size_t *payload_len, size_t *max_len);
> >>>
> >>> so what is this supposed to do..?
> >>
> >> My issue with the attach in general is that it will need additional
> >> memcpy to move the metadata from/to the client buffer to it's place.
> >>
> >> With get_ptr the client can get the pointer to the actual place where
> >> the metadata resides and modify/read it in place w/o memcpy.
> >>
> >> I know, I know... We need to trust the clients, but with high throughput
> >> peripherals the memcpy is taxing.
> >
> > Okay I am not sure I have understood fully, so with attach you set
> > a pointer (containing metdata?) so why do you need additional one..
> >
> >>
> >>>
> >>>> + int (*set_len)(struct dma_async_tx_descriptor *desc,
> >>>> + size_t payload_len);
> >>>
> >>> attach already has length, so how does this help?
> >>
> >> So, DMA drivers can implement either or both:
> >> 1. attach()
> >> 2. get_ptr() / set_len()
> >
> > Ah okay, what are the reasons for providing two methods and not a single
> > one
>
> For the HW I have it would be more efficient to grab pointer and do
> in-place modification to metadata section (the part of the CPPI5
> descriptor which is owned by the client driver).
>
> Other vendors might have the metadata scattered, or in different way
> which does not fit with the ptr mode for security or sanity point of
> view - I don't want to give the whole descriptor to the client. I don't
> trust ;)
>
> >>
> >> Clients must not mix the two way of handling the metadata.
> >> The set_len() is intended to tell the DMA driver the client provided
> >> metadata size (in MEM_TO_DEV case mostly).
> >>
> >> MEM_TO_DEV flow on client side:
> >> get_ptr()
> >> fill in the metadata to the pointer (not exceeding max_len)
> >> set_len() to tell the DMA driver the amount of valid bytes written
> >>
> >> DEV_TO_MEM flow on client side:
> >> In the completion callback, get_ptr()
> >> the metadata is payload_len bytes and can be accessed in the return pointer.
> >
> > I would think to unify this..
>
> I have tried it, but the attach mode and the pointer mode is hard to
> handle with a generic API.
> I will try to find a way to unify things in a sane way.
Hmmm, looking from the description they will be for different methods,
so lets make them orthogonal and not allow driver to register both.
>
> I have moved the metadata_ops to dma_async_tx_descriptor to emphasize
> that it is per descriptor setting:
> https://github.com/omap-audio/linux-audio/commit/02e095d1320a4bb3ae281ddb208ce82ead746f00#diff-92c0a79f414dc3be9dfc67a969c0dd71
>
>
> >> BTW: The driver which is going to need this is now accessible in public:
> >> https://git.ti.com/ti-linux-kernel/ti-linux-kernel/trees/ti-linux-4.14.y/drivers/dma/ti
> >>
> >> or in my wip tree:
> >> https://github.com/omap-audio/linux-audio/tree/peter/ti-linux-4.14.y/wip/drivers/dma/ti
> >>
> >> prefixed with k3-*
> >>
>
> - Péter
>
> Texas Instruments Finland Oy, Porkkalankatu 22, 00180 Helsinki.
> Y-tunnus/Business ID: 0615521-4. Kotipaikka/Domicile: Helsinki
^ permalink raw reply
* [v3,3/3] dmaengine: imx-sdma: allocate max 20 bds for one transfer
From: Lucas Stach @ 2018-07-24 9:22 UTC (permalink / raw)
To: Robin Gong, vkoul@kernel.org, dan.j.williams@intel.com,
s.hauer@pengutronix.de, linux@armlinux.org.uk
Cc: dmaengine@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
Am Montag, den 23.07.2018, 13:55 +0000 schrieb Robin Gong:
> > -----Original Message-----
> > From: Lucas Stach [mailto:l.stach@pengutronix.de]
> > Sent: 2018年7月23日 18:54
> > To: Robin Gong <yibin.gong@nxp.com>; vkoul@kernel.org;
> > dan.j.williams@intel.com; s.hauer@pengutronix.de; linux@armlinux.or
> > g.uk
> > Cc: dmaengine@vger.kernel.org; dl-linux-imx <linux-imx@nxp.com>;
> > kernel@pengutronix.de; linux-arm-kernel@lists.infradead.org;
> > linux-kernel@vger.kernel.org
> > Subject: Re: [PATCH v3 3/3] dmaengine: imx-sdma: allocate max 20
> > bds for one
> > transfer
> >
> > Am Dienstag, den 24.07.2018, 01:46 +0800 schrieb Robin Gong:
> > > If multi-bds used in one transfer, all bds should be consisten
> > > memory.To easily follow it, enlarge the dma pool size into 20
> > > bds, and
> > > it will report error if the number of bds is over than 20. For
> > > dmatest, the max count for single transfer is NUM_BD *
> >
> > SDMA_BD_MAX_CNT
> > > = 20 * 65535 = ~1.28MB.
> >
> > Both the commit message and the comment need a lot more care to
> > actually
> > tell what this commit is trying to achieve. Currently I don't
> > follow at all. What
> > does "consisten" mean? Do you mean BDs should be contiguous in
> > memory?
>
> Yes, BDs should be contiguous one by one in memory.
Okay, but this isn't what the code change does. By increasing the size
parameter of the dma pool you just allocate 20 times as much memory as
needed for each BD. So actually the BDs end up being very non-
contiguous in memory as there are now holes of 19 BD sizes between the
start of each BD.
So something isn't right with this change.
Regards,
Lucas
> >
> > What do you gain by over-allocating each BD by a factor of 20?
>
> I guess dma_pool_alloc will return error in such case, and then cause
> dma setup
> transfer failure.
> >
> > Regards,
> > Lucas
> >
> > > Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> > > ---
> > > drivers/dma/imx-sdma.c | 17 ++++++++++++++++-
> > > 1 file changed, 16 insertions(+), 1 deletion(-)
> > >
> > > diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
> > > index
> > > b4ec2d2..5973489 100644
> > > --- a/drivers/dma/imx-sdma.c
> > > +++ b/drivers/dma/imx-sdma.c
> > > @@ -298,6 +298,15 @@ struct sdma_context_data {
> > > > u32 scratch7;
> > >
> > > } __attribute__ ((packed));
> > >
> > > +/*
> > > + * All bds in one transfer should be consitent on SDMA. To
> > > easily
> > > +follow it,just
> > > + * set the dma pool size as the enough bds. For example, in
> > > dmatest
> > > +case, the
> > > + * max 20 bds means the max for single transfer is NUM_BD *
> > > +SDMA_BD_MAX_CNT = 20
> > > + * * 65535 = ~1.28MB. 20 bds supposed to be enough basically.If
> > > it's
> > > +still not
> > > + * enough in some specific cases, enlarge it here.Warning
> > > message
> > > +would also
> > > + * appear if the bd numbers is over than 20.
> > > + */
> > > +#define NUM_BD 20
> > >
> > > struct sdma_engine;
> > >
> > > @@ -1273,7 +1282,7 @@ static int sdma_alloc_chan_resources(struct
> > > dma_chan *chan)
> > > > goto disable_clk_ahb;
> > > > sdmac->bd_pool = dma_pool_create("bd_pool", chan-
> > > > >device->dev,
> > > > - sizeof(struct
> > > > sdma_buffer_descriptor),
> > > > + NUM_BD * sizeof(struct
> > > > sdma_buffer_descriptor),
> > > > 32, 0);
> > > > return 0;
> > >
> > > @@ -1314,6 +1323,12 @@ static struct sdma_desc
> > > *sdma_transfer_init(struct sdma_channel *sdmac,
> > > {
> > > > struct sdma_desc *desc;
> > > > + if (bds > NUM_BD) {
> > > > + dev_err(sdmac->sdma->dev, "%d bds exceed the
> > > > max %d\n",
> > > > + bds, NUM_BD);
> > > > + goto err_out;
> > > > + }
> > >
> > > +
> > > > desc = kzalloc((sizeof(*desc)), GFP_NOWAIT);
> > > > if (!desc)
> > > > goto err_out;
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v3,3/3] dmaengine: imx-sdma: allocate max 20 bds for one transfer
From: Robin Gong @ 2018-07-23 17:46 UTC (permalink / raw)
To: vkoul, dan.j.williams, s.hauer, linux
Cc: linux-arm-kernel, kernel, dmaengine, linux-kernel, linux-imx
If multi-bds used in one transfer, all bds should be consisten
memory.To easily follow it, enlarge the dma pool size into 20 bds,
and it will report error if the number of bds is over than 20. For
dmatest, the max count for single transfer is NUM_BD *
SDMA_BD_MAX_CNT = 20 * 65535 = ~1.28MB.
Signed-off-by: Robin Gong <yibin.gong@nxp.com>
---
drivers/dma/imx-sdma.c | 17 ++++++++++++++++-
1 file changed, 16 insertions(+), 1 deletion(-)
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index b4ec2d2..5973489 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -298,6 +298,15 @@ struct sdma_context_data {
u32 scratch7;
} __attribute__ ((packed));
+/*
+ * All bds in one transfer should be consitent on SDMA. To easily follow it,just
+ * set the dma pool size as the enough bds. For example, in dmatest case, the
+ * max 20 bds means the max for single transfer is NUM_BD * SDMA_BD_MAX_CNT = 20
+ * * 65535 = ~1.28MB. 20 bds supposed to be enough basically.If it's still not
+ * enough in some specific cases, enlarge it here.Warning message would also
+ * appear if the bd numbers is over than 20.
+ */
+#define NUM_BD 20
struct sdma_engine;
@@ -1273,7 +1282,7 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
goto disable_clk_ahb;
sdmac->bd_pool = dma_pool_create("bd_pool", chan->device->dev,
- sizeof(struct sdma_buffer_descriptor),
+ NUM_BD * sizeof(struct sdma_buffer_descriptor),
32, 0);
return 0;
@@ -1314,6 +1323,12 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
{
struct sdma_desc *desc;
+ if (bds > NUM_BD) {
+ dev_err(sdmac->sdma->dev, "%d bds exceed the max %d\n",
+ bds, NUM_BD);
+ goto err_out;
+ }
+
desc = kzalloc((sizeof(*desc)), GFP_NOWAIT);
if (!desc)
goto err_out;
^ permalink raw reply related
* [v3,2/3] dmaengine: imx-sdma: add memcpy interface
From: Robin Gong @ 2018-07-23 17:46 UTC (permalink / raw)
To: vkoul, dan.j.williams, s.hauer, linux
Cc: linux-arm-kernel, kernel, dmaengine, linux-kernel, linux-imx
Add MEMCPY capability for imx-sdma driver.
Signed-off-by: Robin Gong <yibin.gong@nxp.com>
---
drivers/dma/imx-sdma.c | 93 ++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 90 insertions(+), 3 deletions(-)
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index e3d5e73..b4ec2d2 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -342,6 +342,7 @@ struct sdma_desc {
* @pc_from_device: script address for those device_2_memory
* @pc_to_device: script address for those memory_2_device
* @device_to_device: script address for those device_2_device
+ * @pc_to_pc: script address for those memory_2_memory
* @flags: loop mode or not
* @per_address: peripheral source or destination address in common case
* destination address in p_2_p case
@@ -367,6 +368,7 @@ struct sdma_channel {
enum dma_slave_buswidth word_size;
unsigned int pc_from_device, pc_to_device;
unsigned int device_to_device;
+ unsigned int pc_to_pc;
unsigned long flags;
dma_addr_t per_address, per_address2;
unsigned long event_mask[2];
@@ -869,14 +871,16 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
* These are needed once we start to support transfers between
* two peripherals or memory-to-memory transfers
*/
- int per_2_per = 0;
+ int per_2_per = 0, emi_2_emi = 0;
sdmac->pc_from_device = 0;
sdmac->pc_to_device = 0;
sdmac->device_to_device = 0;
+ sdmac->pc_to_pc = 0;
switch (peripheral_type) {
case IMX_DMATYPE_MEMORY:
+ emi_2_emi = sdma->script_addrs->ap_2_ap_addr;
break;
case IMX_DMATYPE_DSP:
emi_2_per = sdma->script_addrs->bp_2_ap_addr;
@@ -949,6 +953,7 @@ static void sdma_get_pc(struct sdma_channel *sdmac,
sdmac->pc_from_device = per_2_emi;
sdmac->pc_to_device = emi_2_per;
sdmac->device_to_device = per_2_per;
+ sdmac->pc_to_pc = emi_2_emi;
}
static int sdma_load_context(struct sdma_channel *sdmac)
@@ -965,6 +970,8 @@ static int sdma_load_context(struct sdma_channel *sdmac)
load_address = sdmac->pc_from_device;
else if (sdmac->direction == DMA_DEV_TO_DEV)
load_address = sdmac->device_to_device;
+ else if (sdmac->direction == DMA_MEM_TO_MEM)
+ load_address = sdmac->pc_to_pc;
else
load_address = sdmac->pc_to_device;
@@ -1214,10 +1221,28 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
{
struct sdma_channel *sdmac = to_sdma_chan(chan);
struct imx_dma_data *data = chan->private;
+ struct imx_dma_data mem_data;
int prio, ret;
- if (!data)
- return -EINVAL;
+ /*
+ * MEMCPY may never setup chan->private by filter function such as
+ * dmatest, thus create 'struct imx_dma_data mem_data' for this case.
+ * Please note in any other slave case, you have to setup chan->private
+ * with 'struct imx_dma_data' in your own filter function if you want to
+ * request dma channel by dma_request_channel() rather than
+ * dma_request_slave_channel(). Othwise, 'MEMCPY in case?' will appear
+ * to warn you to correct your filter function.
+ */
+ if (!data) {
+ dev_dbg(sdmac->sdma->dev, "MEMCPY in case?\n");
+ mem_data.priority = 2;
+ mem_data.peripheral_type = IMX_DMATYPE_MEMORY;
+ mem_data.dma_request = 0;
+ mem_data.dma_request2 = 0;
+ data = &mem_data;
+
+ sdma_get_pc(sdmac, IMX_DMATYPE_MEMORY);
+ }
switch (data->priority) {
case DMA_PRIO_HIGH:
@@ -1307,6 +1332,10 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
if (sdma_alloc_bd(desc))
goto err_desc_out;
+ /* No slave_config called in MEMCPY case, so do here */
+ if (direction == DMA_MEM_TO_MEM)
+ sdma_config_ownership(sdmac, false, true, false);
+
if (sdma_load_context(sdmac))
goto err_desc_out;
@@ -1318,6 +1347,62 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
return NULL;
}
+static struct dma_async_tx_descriptor *sdma_prep_memcpy(
+ struct dma_chan *chan, dma_addr_t dma_dst,
+ dma_addr_t dma_src, size_t len, unsigned long flags)
+{
+ struct sdma_channel *sdmac = to_sdma_chan(chan);
+ struct sdma_engine *sdma = sdmac->sdma;
+ int channel = sdmac->channel;
+ size_t count;
+ int i = 0, param;
+ struct sdma_buffer_descriptor *bd;
+ struct sdma_desc *desc;
+
+ if (!chan || !len)
+ return NULL;
+
+ dev_dbg(sdma->dev, "memcpy: %pad->%pad, len=%zu, channel=%d.\n",
+ &dma_src, &dma_dst, len, channel);
+
+ desc = sdma_transfer_init(sdmac, DMA_MEM_TO_MEM,
+ len / SDMA_BD_MAX_CNT + 1);
+ if (!desc)
+ return NULL;
+
+ do {
+ count = min_t(size_t, len, SDMA_BD_MAX_CNT);
+ bd = &desc->bd[i];
+ bd->buffer_addr = dma_src;
+ bd->ext_buffer_addr = dma_dst;
+ bd->mode.count = count;
+ desc->chn_count += count;
+ bd->mode.command = 0;
+
+ dma_src += count;
+ dma_dst += count;
+ len -= count;
+ i++;
+
+ param = BD_DONE | BD_EXTD | BD_CONT;
+ /* last bd */
+ if (!len) {
+ param |= BD_INTR;
+ param |= BD_LAST;
+ param &= ~BD_CONT;
+ }
+
+ dev_dbg(sdma->dev, "entry %d: count: %zd dma: 0x%x %s%s\n",
+ i, count, bd->buffer_addr,
+ param & BD_WRAP ? "wrap" : "",
+ param & BD_INTR ? " intr" : "");
+
+ bd->mode.status = param;
+ } while (len);
+
+ return vchan_tx_prep(&sdmac->vc, &desc->vd, flags);
+}
+
static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
struct dma_chan *chan, struct scatterlist *sgl,
unsigned int sg_len, enum dma_transfer_direction direction,
@@ -1903,6 +1988,7 @@ static int sdma_probe(struct platform_device *pdev)
dma_cap_set(DMA_SLAVE, sdma->dma_device.cap_mask);
dma_cap_set(DMA_CYCLIC, sdma->dma_device.cap_mask);
+ dma_cap_set(DMA_MEMCPY, sdma->dma_device.cap_mask);
INIT_LIST_HEAD(&sdma->dma_device.channels);
/* Initialize channel parameters */
@@ -1969,6 +2055,7 @@ static int sdma_probe(struct platform_device *pdev)
sdma->dma_device.dst_addr_widths = SDMA_DMA_BUSWIDTHS;
sdma->dma_device.directions = SDMA_DMA_DIRECTIONS;
sdma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
+ sdma->dma_device.device_prep_dma_memcpy = sdma_prep_memcpy;
sdma->dma_device.device_issue_pending = sdma_issue_pending;
sdma->dma_device.dev->dma_parms = &sdma->dma_parms;
dma_set_max_seg_size(sdma->dma_device.dev, SDMA_BD_MAX_CNT);
^ permalink raw reply related
* [v3,1/3] dmaengine: imx-sdma: add SDMA_BD_MAX_CNT to replace '0xffff'
From: Robin Gong @ 2018-07-23 17:46 UTC (permalink / raw)
To: vkoul, dan.j.williams, s.hauer, linux
Cc: linux-arm-kernel, kernel, dmaengine, linux-kernel, linux-imx
Add macro SDMA_BD_MAX_CNT to replace '0xffff'.
Signed-off-by: Robin Gong <yibin.gong@nxp.com>
---
drivers/dma/imx-sdma.c | 11 ++++++-----
1 file changed, 6 insertions(+), 5 deletions(-)
diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
index 3b622d6..e3d5e73 100644
--- a/drivers/dma/imx-sdma.c
+++ b/drivers/dma/imx-sdma.c
@@ -185,6 +185,7 @@
* Mode/Count of data node descriptors - IPCv2
*/
struct sdma_mode_count {
+#define SDMA_BD_MAX_CNT 0xffff
u32 count : 16; /* size of the buffer pointed by this BD */
u32 status : 8; /* E,R,I,C,W,D status bits stored here */
u32 command : 8; /* command mostly used for channel 0 */
@@ -1344,9 +1345,9 @@ static struct dma_async_tx_descriptor *sdma_prep_slave_sg(
count = sg_dma_len(sg);
- if (count > 0xffff) {
+ if (count > SDMA_BD_MAX_CNT) {
dev_err(sdma->dev, "SDMA channel %d: maximum bytes for sg entry exceeded: %d > %d\n",
- channel, count, 0xffff);
+ channel, count, SDMA_BD_MAX_CNT);
goto err_bd_out;
}
@@ -1421,9 +1422,9 @@ static struct dma_async_tx_descriptor *sdma_prep_dma_cyclic(
sdmac->flags |= IMX_DMA_SG_LOOP;
- if (period_len > 0xffff) {
+ if (period_len > SDMA_BD_MAX_CNT) {
dev_err(sdma->dev, "SDMA channel %d: maximum period size exceeded: %zu > %d\n",
- channel, period_len, 0xffff);
+ channel, period_len, SDMA_BD_MAX_CNT);
goto err_bd_out;
}
@@ -1970,7 +1971,7 @@ static int sdma_probe(struct platform_device *pdev)
sdma->dma_device.residue_granularity = DMA_RESIDUE_GRANULARITY_SEGMENT;
sdma->dma_device.device_issue_pending = sdma_issue_pending;
sdma->dma_device.dev->dma_parms = &sdma->dma_parms;
- dma_set_max_seg_size(sdma->dma_device.dev, 65535);
+ dma_set_max_seg_size(sdma->dma_device.dev, SDMA_BD_MAX_CNT);
platform_set_drvdata(pdev, sdma);
^ permalink raw reply related
* [v3,3/3] dmaengine: imx-sdma: allocate max 20 bds for one transfer
From: Robin Gong @ 2018-07-23 13:55 UTC (permalink / raw)
To: Lucas Stach, vkoul@kernel.org, dan.j.williams@intel.com,
s.hauer@pengutronix.de, linux@armlinux.org.uk
Cc: dmaengine@vger.kernel.org, dl-linux-imx, kernel@pengutronix.de,
linux-arm-kernel@lists.infradead.org,
linux-kernel@vger.kernel.org
PiAtLS0tLU9yaWdpbmFsIE1lc3NhZ2UtLS0tLQ0KPiBGcm9tOiBMdWNhcyBTdGFjaCBbbWFpbHRv
Omwuc3RhY2hAcGVuZ3V0cm9uaXguZGVdDQo+IFNlbnQ6IDIwMTjlubQ35pyIMjPml6UgMTg6NTQN
Cj4gVG86IFJvYmluIEdvbmcgPHlpYmluLmdvbmdAbnhwLmNvbT47IHZrb3VsQGtlcm5lbC5vcmc7
DQo+IGRhbi5qLndpbGxpYW1zQGludGVsLmNvbTsgcy5oYXVlckBwZW5ndXRyb25peC5kZTsgbGlu
dXhAYXJtbGludXgub3JnLnVrDQo+IENjOiBkbWFlbmdpbmVAdmdlci5rZXJuZWwub3JnOyBkbC1s
aW51eC1pbXggPGxpbnV4LWlteEBueHAuY29tPjsNCj4ga2VybmVsQHBlbmd1dHJvbml4LmRlOyBs
aW51eC1hcm0ta2VybmVsQGxpc3RzLmluZnJhZGVhZC5vcmc7DQo+IGxpbnV4LWtlcm5lbEB2Z2Vy
Lmtlcm5lbC5vcmcNCj4gU3ViamVjdDogUmU6IFtQQVRDSCB2MyAzLzNdIGRtYWVuZ2luZTogaW14
LXNkbWE6IGFsbG9jYXRlIG1heCAyMCBiZHMgZm9yIG9uZQ0KPiB0cmFuc2Zlcg0KPiANCj4gQW0g
RGllbnN0YWcsIGRlbiAyNC4wNy4yMDE4LCAwMTo0NiArMDgwMCBzY2hyaWViIFJvYmluIEdvbmc6
DQo+ID4gSWYgbXVsdGktYmRzIHVzZWQgaW4gb25lIHRyYW5zZmVyLCBhbGwgYmRzIHNob3VsZCBi
ZSBjb25zaXN0ZW4NCj4gPiBtZW1vcnkuVG8gZWFzaWx5IGZvbGxvdyBpdCwgZW5sYXJnZSB0aGUg
ZG1hIHBvb2wgc2l6ZSBpbnRvIDIwIGJkcywgYW5kDQo+ID4gaXQgd2lsbCByZXBvcnQgZXJyb3Ig
aWYgdGhlIG51bWJlciBvZiBiZHMgaXMgb3ZlciB0aGFuIDIwLiBGb3INCj4gPiBkbWF0ZXN0LCB0
aGUgbWF4IGNvdW50IGZvciBzaW5nbGUgdHJhbnNmZXIgaXMgTlVNX0JEICoNCj4gU0RNQV9CRF9N
QVhfQ05UDQo+ID4gPSAyMCAqIDY1NTM1ID0gfjEuMjhNQi4NCj4gDQo+IEJvdGggdGhlIGNvbW1p
dCBtZXNzYWdlIGFuZCB0aGUgY29tbWVudCBuZWVkIGEgbG90IG1vcmUgY2FyZSB0byBhY3R1YWxs
eQ0KPiB0ZWxsIHdoYXQgdGhpcyBjb21taXQgaXMgdHJ5aW5nIHRvIGFjaGlldmUuIEN1cnJlbnRs
eSBJIGRvbid0IGZvbGxvdyBhdCBhbGwuIFdoYXQNCj4gZG9lcyAiY29uc2lzdGVuIiBtZWFuPyBE
byB5b3UgbWVhbiBCRHMgc2hvdWxkIGJlIGNvbnRpZ3VvdXMgaW4gbWVtb3J5Pw0KWWVzLCBCRHMg
c2hvdWxkIGJlIGNvbnRpZ3VvdXMgIG9uZSBieSBvbmUgaW4gbWVtb3J5Lg0KPiANCj4gV2hhdCBk
byB5b3UgZ2FpbiBieSBvdmVyLWFsbG9jYXRpbmcgZWFjaCBCRCBieSBhIGZhY3RvciBvZiAyMD8N
CkkgZ3Vlc3MgZG1hX3Bvb2xfYWxsb2Mgd2lsbCByZXR1cm4gZXJyb3IgaW4gc3VjaCBjYXNlLCBh
bmQgdGhlbiBjYXVzZSBkbWEgc2V0dXANCnRyYW5zZmVyIGZhaWx1cmUuDQo+IA0KPiBSZWdhcmRz
LA0KPiBMdWNhcw0KPiANCj4gPiBTaWduZWQtb2ZmLWJ5OiBSb2JpbiBHb25nIDx5aWJpbi5nb25n
QG54cC5jb20+DQo+ID4gLS0tDQo+ID4gwqBkcml2ZXJzL2RtYS9pbXgtc2RtYS5jIHwgMTcgKysr
KysrKysrKysrKysrKy0NCj4gPiDCoDEgZmlsZSBjaGFuZ2VkLCAxNiBpbnNlcnRpb25zKCspLCAx
IGRlbGV0aW9uKC0pDQo+ID4NCj4gPiBkaWZmIC0tZ2l0IGEvZHJpdmVycy9kbWEvaW14LXNkbWEu
YyBiL2RyaXZlcnMvZG1hL2lteC1zZG1hLmMgaW5kZXgNCj4gPiBiNGVjMmQyLi41OTczNDg5IDEw
MDY0NA0KPiA+IC0tLSBhL2RyaXZlcnMvZG1hL2lteC1zZG1hLmMNCj4gPiArKysgYi9kcml2ZXJz
L2RtYS9pbXgtc2RtYS5jDQo+ID4gQEAgLTI5OCw2ICsyOTgsMTUgQEAgc3RydWN0IHNkbWFfY29u
dGV4dF9kYXRhIHsNCj4gPiA+IMKgCXUzMsKgwqBzY3JhdGNoNzsNCj4gPiDCoH0gX19hdHRyaWJ1
dGVfXyAoKHBhY2tlZCkpOw0KPiA+DQo+ID4gKy8qDQo+ID4gKyAqIEFsbCBiZHMgaW4gb25lIHRy
YW5zZmVyIHNob3VsZCBiZSBjb25zaXRlbnQgb24gU0RNQS4gVG8gZWFzaWx5DQo+ID4gK2ZvbGxv
dyBpdCxqdXN0DQo+ID4gKyAqIHNldCB0aGUgZG1hIHBvb2wgc2l6ZSBhcyB0aGUgZW5vdWdoIGJk
cy4gRm9yIGV4YW1wbGUsIGluIGRtYXRlc3QNCj4gPiArY2FzZSwgdGhlDQo+ID4gKyAqIG1heCAy
MCBiZHMgbWVhbnMgdGhlIG1heCBmb3Igc2luZ2xlIHRyYW5zZmVyIGlzIE5VTV9CRCAqDQo+ID4g
K1NETUFfQkRfTUFYX0NOVCA9IDIwDQo+ID4gKyAqICogNjU1MzUgPSB+MS4yOE1CLiAyMCBiZHMg
c3VwcG9zZWQgdG8gYmUgZW5vdWdoIGJhc2ljYWxseS5JZiBpdCdzDQo+ID4gK3N0aWxsIG5vdA0K
PiA+ICsgKiBlbm91Z2ggaW4gc29tZSBzcGVjaWZpYyBjYXNlcywgZW5sYXJnZSBpdCBoZXJlLldh
cm5pbmcgbWVzc2FnZQ0KPiA+ICt3b3VsZCBhbHNvDQo+ID4gKyAqIGFwcGVhciBpZiB0aGUgYmQg
bnVtYmVycyBpcyBvdmVyIHRoYW4gMjAuDQo+ID4gKyAqLw0KPiA+ICsjZGVmaW5lIE5VTV9CRCAy
MA0KPiA+DQo+ID4gwqBzdHJ1Y3Qgc2RtYV9lbmdpbmU7DQo+ID4NCj4gPiBAQCAtMTI3Myw3ICsx
MjgyLDcgQEAgc3RhdGljIGludCBzZG1hX2FsbG9jX2NoYW5fcmVzb3VyY2VzKHN0cnVjdA0KPiA+
IGRtYV9jaGFuICpjaGFuKQ0KPiA+ID4gwqAJCWdvdG8gZGlzYWJsZV9jbGtfYWhiOw0KPiA+DQo+
ID4gPiDCoAlzZG1hYy0+YmRfcG9vbCA9IGRtYV9wb29sX2NyZWF0ZSgiYmRfcG9vbCIsIGNoYW4t
PmRldmljZS0+ZGV2LA0KPiA+ID4gLQkJCQlzaXplb2Yoc3RydWN0IHNkbWFfYnVmZmVyX2Rlc2Ny
aXB0b3IpLA0KPiA+ID4gKwkJCQlOVU1fQkQgKiBzaXplb2Yoc3RydWN0IHNkbWFfYnVmZmVyX2Rl
c2NyaXB0b3IpLA0KPiA+ID4gwqAJCQkJMzIsIDApOw0KPiA+DQo+ID4gPiDCoAlyZXR1cm4gMDsN
Cj4gPiBAQCAtMTMxNCw2ICsxMzIzLDEyIEBAIHN0YXRpYyBzdHJ1Y3Qgc2RtYV9kZXNjDQo+ID4g
KnNkbWFfdHJhbnNmZXJfaW5pdChzdHJ1Y3Qgc2RtYV9jaGFubmVsICpzZG1hYywNCj4gPiDCoHsN
Cj4gPiA+IMKgCXN0cnVjdCBzZG1hX2Rlc2MgKmRlc2M7DQo+ID4NCj4gPiA+ICsJaWYgKGJkcyA+
IE5VTV9CRCkgew0KPiA+ID4gKwkJZGV2X2VycihzZG1hYy0+c2RtYS0+ZGV2LCAiJWQgYmRzIGV4
Y2VlZCB0aGUgbWF4ICVkXG4iLA0KPiA+ID4gKwkJCWJkcywgTlVNX0JEKTsNCj4gPiA+ICsJCWdv
dG8gZXJyX291dDsNCj4gPiA+ICsJfQ0KPiA+ICsNCj4gPiA+IMKgCWRlc2MgPSBremFsbG9jKChz
aXplb2YoKmRlc2MpKSwgR0ZQX05PV0FJVCk7DQo+ID4gPiDCoAlpZiAoIWRlc2MpDQo+ID4gPiDC
oAkJZ290byBlcnJfb3V0Ow0K
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v3,3/3] dmaengine: imx-sdma: allocate max 20 bds for one transfer
From: Lucas Stach @ 2018-07-23 10:54 UTC (permalink / raw)
To: Robin Gong, vkoul, dan.j.williams, s.hauer, linux
Cc: dmaengine, linux-imx, kernel, linux-arm-kernel, linux-kernel
Am Dienstag, den 24.07.2018, 01:46 +0800 schrieb Robin Gong:
> If multi-bds used in one transfer, all bds should be consisten
> memory.To easily follow it, enlarge the dma pool size into 20 bds,
> and it will report error if the number of bds is over than 20. For
> dmatest, the max count for single transfer is NUM_BD *
> SDMA_BD_MAX_CNT = 20 * 65535 = ~1.28MB.
Both the commit message and the comment need a lot more care to
actually tell what this commit is trying to achieve. Currently I don't
follow at all. What does "consisten" mean? Do you mean BDs should be
contiguous in memory?
What do you gain by over-allocating each BD by a factor of 20?
Regards,
Lucas
> Signed-off-by: Robin Gong <yibin.gong@nxp.com>
> ---
> drivers/dma/imx-sdma.c | 17 ++++++++++++++++-
> 1 file changed, 16 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c
> index b4ec2d2..5973489 100644
> --- a/drivers/dma/imx-sdma.c
> +++ b/drivers/dma/imx-sdma.c
> @@ -298,6 +298,15 @@ struct sdma_context_data {
> > u32 scratch7;
> } __attribute__ ((packed));
>
> +/*
> + * All bds in one transfer should be consitent on SDMA. To easily follow it,just
> + * set the dma pool size as the enough bds. For example, in dmatest case, the
> + * max 20 bds means the max for single transfer is NUM_BD * SDMA_BD_MAX_CNT = 20
> + * * 65535 = ~1.28MB. 20 bds supposed to be enough basically.If it's still not
> + * enough in some specific cases, enlarge it here.Warning message would also
> + * appear if the bd numbers is over than 20.
> + */
> +#define NUM_BD 20
>
> struct sdma_engine;
>
> @@ -1273,7 +1282,7 @@ static int sdma_alloc_chan_resources(struct dma_chan *chan)
> > goto disable_clk_ahb;
>
> > sdmac->bd_pool = dma_pool_create("bd_pool", chan->device->dev,
> > - sizeof(struct sdma_buffer_descriptor),
> > + NUM_BD * sizeof(struct sdma_buffer_descriptor),
> > 32, 0);
>
> > return 0;
> @@ -1314,6 +1323,12 @@ static struct sdma_desc *sdma_transfer_init(struct sdma_channel *sdmac,
> {
> > struct sdma_desc *desc;
>
> > + if (bds > NUM_BD) {
> > + dev_err(sdmac->sdma->dev, "%d bds exceed the max %d\n",
> > + bds, NUM_BD);
> > + goto err_out;
> > + }
> +
> > desc = kzalloc((sizeof(*desc)), GFP_NOWAIT);
> > if (!desc)
> > goto err_out;
---
To unsubscribe from this list: send the line "unsubscribe dmaengine" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply
* [v2,4/4] MAINTAINERS: Add entry for Actions Semi Owl SoCs DMA driver
From: Manivannan Sadhasivam @ 2018-07-23 4:17 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add entry for Actions Semi Owl SoCs DMA driver under ARM/ACTIONS.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
MAINTAINERS | 2 ++
1 file changed, 2 insertions(+)
diff --git a/MAINTAINERS b/MAINTAINERS
index 09b54e9ebc6f..56d9c7715c2a 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -1145,12 +1145,14 @@ F: arch/arm/boot/dts/owl-*
F: arch/arm64/boot/dts/actions/
F: drivers/clk/actions/
F: drivers/clocksource/owl-*
+F: drivers/dma/owl-dma.c
F: drivers/pinctrl/actions/*
F: drivers/soc/actions/
F: include/dt-bindings/power/owl-*
F: include/linux/soc/actions/
F: Documentation/devicetree/bindings/arm/actions.txt
F: Documentation/devicetree/bindings/clock/actions,s900-cmu.txt
+F: Documentation/devicetree/bindings/dma/owl-dma.txt
F: Documentation/devicetree/bindings/pinctrl/actions,s900-pinctrl.txt
F: Documentation/devicetree/bindings/power/actions,owl-sps.txt
F: Documentation/devicetree/bindings/timer/actions,owl-timer.txt
^ permalink raw reply related
* [v2,3/4] dma: Add Actions Semi Owl family S900 DMA driver
From: Manivannan Sadhasivam @ 2018-07-23 4:17 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add Actions Semi Owl family S900 DMA driver.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
drivers/dma/Kconfig | 8 +
drivers/dma/Makefile | 1 +
drivers/dma/owl-dma.c | 1023 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 1032 insertions(+)
create mode 100644 drivers/dma/owl-dma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680afa20a..92a278e6618c 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -413,6 +413,14 @@ config NBPFAXI_DMA
help
Support for "Type-AXI" NBPF DMA IPs from Renesas
+config OWL_DMA
+ tristate "Actions Semi Owl SoCs DMA support"
+ depends on ARCH_ACTIONS
+ select DMA_ENGINE
+ select DMA_VIRTUAL_CHANNELS
+ help
+ Enable support for the Actions Semi Owl SoCs DMA controller.
+
config PCH_DMA
tristate "Intel EG20T PCH / LAPIS Semicon IOH(ML7213/ML7223/ML7831) DMA"
depends on PCI && (X86_32 || COMPILE_TEST)
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 203a99d68315..c91702d88b95 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -52,6 +52,7 @@ obj-$(CONFIG_MV_XOR_V2) += mv_xor_v2.o
obj-$(CONFIG_MXS_DMA) += mxs-dma.o
obj-$(CONFIG_MX3_IPU) += ipu/
obj-$(CONFIG_NBPFAXI_DMA) += nbpfaxi.o
+obj-$(CONFIG_OWL_DMA) += owl-dma.o
obj-$(CONFIG_PCH_DMA) += pch_dma.o
obj-$(CONFIG_PL330_DMA) += pl330.o
obj-$(CONFIG_PPC_BESTCOMM) += bestcomm/
diff --git a/drivers/dma/owl-dma.c b/drivers/dma/owl-dma.c
new file mode 100644
index 000000000000..05f56cc8a712
--- /dev/null
+++ b/drivers/dma/owl-dma.c
@@ -0,0 +1,1023 @@
+// SPDX-License-Identifier: GPL-2.0+
+//
+// Actions Semi Owl SoCs DMA driver
+//
+// Copyright (c) 2014 Actions Semi Inc.
+// Author: David Liu <liuwei@actions-semi.com>
+//
+// Copyright (c) 2018 Linaro Ltd.
+// Author: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
+
+#include <linux/bitops.h>
+#include <linux/clk.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/io.h>
+#include <linux/mm.h>
+#include <linux/module.h>
+#include <linux/of_device.h>
+#include <linux/of_dma.h>
+#include <linux/slab.h>
+#include "virt-dma.h"
+
+#define OWL_DMA_FRAME_MAX_LENGTH 0xfffff
+
+/* Global DMA Controller Registers */
+#define OWL_DMA_IRQ_PD0 0x00
+#define OWL_DMA_IRQ_PD1 0x04
+#define OWL_DMA_IRQ_PD2 0x08
+#define OWL_DMA_IRQ_PD3 0x0C
+#define OWL_DMA_IRQ_EN0 0x10
+#define OWL_DMA_IRQ_EN1 0x14
+#define OWL_DMA_IRQ_EN2 0x18
+#define OWL_DMA_IRQ_EN3 0x1C
+#define OWL_DMA_SECURE_ACCESS_CTL 0x20
+#define OWL_DMA_NIC_QOS 0x24
+#define OWL_DMA_DBGSEL 0x28
+#define OWL_DMA_IDLE_STAT 0x2C
+
+/* Channel Registers */
+#define OWL_DMA_CHAN_BASE(i) (0x100 + (i) * 0x100)
+#define OWL_DMAX_MODE 0x00
+#define OWL_DMAX_SOURCE 0x04
+#define OWL_DMAX_DESTINATION 0x08
+#define OWL_DMAX_FRAME_LEN 0x0C
+#define OWL_DMAX_FRAME_CNT 0x10
+#define OWL_DMAX_REMAIN_FRAME_CNT 0x14
+#define OWL_DMAX_REMAIN_CNT 0x18
+#define OWL_DMAX_SOURCE_STRIDE 0x1C
+#define OWL_DMAX_DESTINATION_STRIDE 0x20
+#define OWL_DMAX_START 0x24
+#define OWL_DMAX_PAUSE 0x28
+#define OWL_DMAX_CHAINED_CTL 0x2C
+#define OWL_DMAX_CONSTANT 0x30
+#define OWL_DMAX_LINKLIST_CTL 0x34
+#define OWL_DMAX_NEXT_DESCRIPTOR 0x38
+#define OWL_DMAX_CURRENT_DESCRIPTOR_NUM 0x3C
+#define OWL_DMAX_INT_CTL 0x40
+#define OWL_DMAX_INT_STATUS 0x44
+#define OWL_DMAX_CURRENT_SOURCE_POINTER 0x48
+#define OWL_DMAX_CURRENT_DESTINATION_POINTER 0x4C
+
+/* OWL_DMAX_MODE Bits */
+#define OWL_DMA_MODE_TS(x) (((x) & 0x3f) << 0)
+#define OWL_DMA_MODE_ST(x) (((x) & 0x3) << 8)
+#define OWL_DMA_MODE_ST_DEV OWL_DMA_MODE_ST(0)
+#define OWL_DMA_MODE_ST_DCU OWL_DMA_MODE_ST(2)
+#define OWL_DMA_MODE_ST_SRAM OWL_DMA_MODE_ST(3)
+#define OWL_DMA_MODE_DT(x) (((x) & 0x3) << 10)
+#define OWL_DMA_MODE_DT_DEV OWL_DMA_MODE_DT(0)
+#define OWL_DMA_MODE_DT_DCU OWL_DMA_MODE_DT(2)
+#define OWL_DMA_MODE_DT_SRAM OWL_DMA_MODE_DT(3)
+#define OWL_DMA_MODE_SAM(x) (((x) & 0x3) << 16)
+#define OWL_DMA_MODE_SAM_CONST OWL_DMA_MODE_SAM(0)
+#define OWL_DMA_MODE_SAM_INC OWL_DMA_MODE_SAM(1)
+#define OWL_DMA_MODE_SAM_STRIDE OWL_DMA_MODE_SAM(2)
+#define OWL_DMA_MODE_DAM(x) (((x) & 0x3) << 18)
+#define OWL_DMA_MODE_DAM_CONST OWL_DMA_MODE_DAM(0)
+#define OWL_DMA_MODE_DAM_INC OWL_DMA_MODE_DAM(1)
+#define OWL_DMA_MODE_DAM_STRIDE OWL_DMA_MODE_DAM(2)
+#define OWL_DMA_MODE_PW(x) (((x) & 0x7) << 20)
+#define OWL_DMA_MODE_CB BIT(23)
+#define OWL_DMA_MODE_NDDBW(x) (((x) & 0x1) << 28)
+#define OWL_DMA_MODE_NDDBW_32BIT OWL_DMA_MODE_NDDBW(0)
+#define OWL_DMA_MODE_NDDBW_8BIT OWL_DMA_MODE_NDDBW(1)
+#define OWL_DMA_MODE_CFE BIT(29)
+#define OWL_DMA_MODE_LME BIT(30)
+#define OWL_DMA_MODE_CME BIT(31)
+
+/* OWL_DMAX_LINKLIST_CTL Bits */
+#define OWL_DMA_LLC_SAV(x) (((x) & 0x3) << 8)
+#define OWL_DMA_LLC_SAV_INC OWL_DMA_LLC_SAV(0)
+#define OWL_DMA_LLC_SAV_LOAD_NEXT OWL_DMA_LLC_SAV(1)
+#define OWL_DMA_LLC_SAV_LOAD_PREV OWL_DMA_LLC_SAV(2)
+#define OWL_DMA_LLC_DAV(x) (((x) & 0x3) << 10)
+#define OWL_DMA_LLC_DAV_INC OWL_DMA_LLC_DAV(0)
+#define OWL_DMA_LLC_DAV_LOAD_NEXT OWL_DMA_LLC_DAV(1)
+#define OWL_DMA_LLC_DAV_LOAD_PREV OWL_DMA_LLC_DAV(2)
+#define OWL_DMA_LLC_SUSPEND BIT(16)
+
+/* OWL_DMAX_INT_CTL Bits */
+#define OWL_DMA_INTCTL_BLOCK BIT(0)
+#define OWL_DMA_INTCTL_SUPER_BLOCK BIT(1)
+#define OWL_DMA_INTCTL_FRAME BIT(2)
+#define OWL_DMA_INTCTL_HALF_FRAME BIT(3)
+#define OWL_DMA_INTCTL_LAST_FRAME BIT(4)
+
+/* OWL_DMAX_INT_STATUS Bits */
+#define OWL_DMA_INTSTAT_BLOCK BIT(0)
+#define OWL_DMA_INTSTAT_SUPER_BLOCK BIT(1)
+#define OWL_DMA_INTSTAT_FRAME BIT(2)
+#define OWL_DMA_INTSTAT_HALF_FRAME BIT(3)
+#define OWL_DMA_INTSTAT_LAST_FRAME BIT(4)
+
+/* Extract the bit field to new shift */
+#define BIT_FIELD(val, width, shift, newshift) \
+ ((((val) >> (shift)) & ((BIT(width)) - 1)) << (newshift))
+
+/**
+ * struct owl_dma_lli_hw - Hardware link list for dma transfer
+ * @next_lli: physical address of the next link list
+ * @saddr: source physical address
+ * @daddr: destination physical address
+ * @flen: frame length
+ * @fcnt: frame count
+ * @src_stride: source stride
+ * @dst_stride: destination stride
+ * @ctrla: dma_mode and linklist ctrl config
+ * @ctrlb: interrupt config
+ * @const_num: data for constant fill
+ */
+struct owl_dma_lli_hw {
+ u32 next_lli; /* physical address of the next link list */
+ u32 saddr; /* source physical address */
+ u32 daddr; /* destination physical address */
+ u32 flen:20; /* frame length */
+ u32 fcnt:12; /* frame count */
+ u32 src_stride; /* source stride */
+ u32 dst_stride; /* destination stride */
+ u32 ctrla; /* dma_mode and linklist ctrl */
+ u32 ctrlb; /* interrupt control */
+ u32 const_num; /* data for constant fill */
+};
+
+/**
+ * struct owl_dma_lli - Link list for dma transfer
+ * @hw: hardware link list
+ * @phys: physical address of hardware link list
+ * @node: node for txd's lli_list
+ */
+struct owl_dma_lli {
+ struct owl_dma_lli_hw hw;
+ dma_addr_t phys;
+ struct list_head node;
+};
+
+/**
+ * struct owl_dma_txd - Wrapper for struct dma_async_tx_descriptor
+ * @vd: virtual DMA descriptor
+ * @lli_list: link list of children sg's
+ */
+struct owl_dma_txd {
+ struct virt_dma_desc vd;
+ struct list_head lli_list;
+};
+
+/**
+ * struct owl_dma_pchan - Holder for the physical channels
+ * @id: physical index to this channel
+ * @base: virtual memory base for the dma channel
+ * @vchan: the virtual channel currently being served by this physical channel
+ * @lock: a lock to use when altering an instance of this struct
+ */
+struct owl_dma_pchan {
+ u32 id;
+ void __iomem *base;
+ struct owl_dma_vchan *vchan;
+ spinlock_t lock;
+};
+
+/**
+ * struct owl_dma_pchan - Wrapper for DMA ENGINE channel
+ * @vc: wrappped virtual channel
+ * @pchan: the physical channel utilized by this channel
+ * @txd: active transaction on this channel
+ */
+struct owl_dma_vchan {
+ struct virt_dma_chan vc;
+ struct owl_dma_pchan *pchan;
+ struct owl_dma_txd *txd;
+};
+
+/**
+ * struct owl_dma - Holder for the Owl DMA controller
+ * @dma: dma engine for this instance
+ * @base: virtual memory base for the DMA controller
+ * @clk: clock for the DMA controller
+ * @lock: a lock to use when change DMA controller global register
+ * @lli_pool: a pool for the LLI descriptors
+ * @nr_pchans: the number of physical channels
+ * @pchans: array of data for the physical channels
+ * @nr_vchans: the number of physical channels
+ * @vchans: array of data for the physical channels
+ */
+struct owl_dma {
+ struct dma_device dma;
+ void __iomem *base;
+ struct clk *clk;
+ spinlock_t lock;
+ struct dma_pool *lli_pool;
+
+ /* physical dma channels */
+ unsigned int nr_pchans;
+ struct owl_dma_pchan *pchans;
+
+ /* virtual dma channels */
+ unsigned int nr_vchans;
+ struct owl_dma_vchan *vchans;
+};
+
+static void pchan_update(void __iomem *reg, u32 val, bool state)
+{
+ u32 regval;
+
+ regval = readl(reg);
+
+ if (state)
+ regval |= val;
+ else
+ regval &= ~val;
+
+ writel(val, reg);
+}
+
+static void pchan_writel(struct owl_dma_pchan *pchan, u32 data, u32 reg)
+{
+ writel(data, pchan->base + reg);
+}
+
+static u32 pchan_readl(struct owl_dma_pchan *pchan, u32 reg)
+{
+ return readl(pchan->base + reg);
+}
+
+static void dma_update(void __iomem *reg, u32 val, bool state)
+{
+ u32 regval;
+
+ regval = readl(reg);
+
+ if (state)
+ regval |= val;
+ else
+ regval &= ~val;
+
+ writel(val, reg);
+}
+
+static void dma_writel(struct owl_dma *od, u32 data, u32 reg)
+{
+ writel(data, od->base + reg);
+}
+
+static u32 dma_readl(struct owl_dma *od, u32 reg)
+{
+ return readl(od->base + reg);
+}
+
+static inline struct owl_dma *to_owl_dma(struct dma_device *dd)
+{
+ return container_of(dd, struct owl_dma, dma);
+}
+
+static struct device *chan2dev(struct dma_chan *chan)
+{
+ return &chan->dev->device;
+}
+
+static inline struct owl_dma_vchan *to_owl_vchan(struct dma_chan *chan)
+{
+ return container_of(chan, struct owl_dma_vchan, vc.chan);
+}
+
+static inline struct owl_dma_txd *to_owl_txd(struct dma_async_tx_descriptor *tx)
+{
+ return container_of(tx, struct owl_dma_txd, vd.tx);
+}
+
+static inline u32 llc_hw_ctrla(u32 mode, u32 llc_ctl)
+{
+ u32 ctl;
+
+ ctl = BIT_FIELD(mode, 4, 28, 28) |
+ BIT_FIELD(mode, 8, 16, 20) |
+ BIT_FIELD(mode, 4, 8, 16) |
+ BIT_FIELD(mode, 6, 0, 10) |
+ BIT_FIELD(llc_ctl, 2, 10, 8) |
+ BIT_FIELD(llc_ctl, 2, 8, 6);
+
+ return ctl;
+}
+
+static inline u32 llc_hw_ctrlb(u32 int_ctl)
+{
+ u32 ctl;
+
+ ctl = BIT_FIELD(int_ctl, 7, 0, 18);
+
+ return ctl;
+}
+
+static void owl_dma_free_lli(struct owl_dma *od,
+ struct owl_dma_lli *lli)
+{
+ list_del(&lli->node);
+ dma_pool_free(od->lli_pool, lli, lli->phys);
+}
+
+static struct owl_dma_lli *owl_dma_alloc_lli(struct owl_dma *od)
+{
+ struct owl_dma_lli *lli;
+ dma_addr_t phys;
+
+ lli = dma_pool_alloc(od->lli_pool, GFP_NOWAIT, &phys);
+ if (!lli)
+ return NULL;
+
+ INIT_LIST_HEAD(&lli->node);
+ lli->phys = phys;
+
+ return lli;
+}
+
+static struct owl_dma_lli *owl_dma_add_lli(struct owl_dma_txd *txd,
+ struct owl_dma_lli *prev,
+ struct owl_dma_lli *next)
+{
+ list_add_tail(&next->node, &txd->lli_list);
+
+ if (prev) {
+ prev->hw.next_lli = next->phys;
+ prev->hw.ctrla |= llc_hw_ctrla(OWL_DMA_MODE_LME, 0);
+ }
+
+ return next;
+}
+
+static inline int owl_dma_cfg_lli(struct owl_dma_vchan *vchan,
+ struct owl_dma_lli *lli,
+ dma_addr_t src, dma_addr_t dst,
+ u32 len, enum dma_transfer_direction dir)
+{
+ struct owl_dma_lli_hw *hw = &lli->hw;
+ u32 mode;
+
+ mode = OWL_DMA_MODE_PW(0);
+
+ switch (dir) {
+ case DMA_MEM_TO_MEM:
+ mode |= OWL_DMA_MODE_TS(0) | OWL_DMA_MODE_ST_DCU |
+ OWL_DMA_MODE_DT_DCU | OWL_DMA_MODE_SAM_INC |
+ OWL_DMA_MODE_DAM_INC;
+
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ hw->next_lli = 0; /* One link list by default */
+ hw->saddr = src;
+ hw->daddr = dst;
+
+ hw->fcnt = 1; /* Frame count fixed as 1 */
+ hw->flen = len; /* Max frame length is 1MB */
+ hw->src_stride = 0;
+ hw->dst_stride = 0;
+ hw->ctrla = llc_hw_ctrla(mode,
+ OWL_DMA_LLC_SAV_LOAD_NEXT |
+ OWL_DMA_LLC_DAV_LOAD_NEXT);
+
+ hw->ctrlb = llc_hw_ctrlb(OWL_DMA_INTCTL_SUPER_BLOCK);
+
+ return 0;
+}
+
+static struct owl_dma_pchan *owl_dma_get_pchan(struct owl_dma *od,
+ struct owl_dma_vchan *vchan)
+{
+ struct owl_dma_pchan *pchan;
+ unsigned long flags;
+ int i;
+
+ for (i = 0; i < od->nr_pchans; i++) {
+ pchan = &od->pchans[i];
+
+ spin_lock_irqsave(&pchan->lock, flags);
+ if (!pchan->vchan) {
+ pchan->vchan = vchan;
+ spin_unlock_irqrestore(&pchan->lock, flags);
+ break;
+ }
+
+ spin_unlock_irqrestore(&pchan->lock, flags);
+ }
+
+ if (i == od->nr_pchans) {
+ /* No physical channel available, cope with it */
+ dev_dbg(od->dma.dev, "no physical channel available\n");
+ return NULL;
+ }
+
+ return pchan;
+}
+
+static int owl_dma_pchan_busy(struct owl_dma *od, struct owl_dma_pchan *pchan)
+{
+ unsigned int val;
+
+ val = dma_readl(od, OWL_DMA_IDLE_STAT);
+
+ return !(val & (1 << pchan->id));
+}
+
+static void owl_dma_terminate_pchan(struct owl_dma *od,
+ struct owl_dma_pchan *pchan)
+{
+ unsigned long flags;
+ u32 irq_pd;
+
+ pchan_writel(pchan, 0, OWL_DMAX_START);
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+
+ spin_lock_irqsave(&od->lock, flags);
+ dma_update(od->base + OWL_DMA_IRQ_EN0, (1 << pchan->id), false);
+
+ irq_pd = dma_readl(od, OWL_DMA_IRQ_PD0);
+ if (irq_pd & (1 << pchan->id)) {
+ dev_warn(od->dma.dev,
+ "terminating pchan %d that still has pending irq\n",
+ pchan->id);
+ dma_writel(od, (1 << pchan->id), OWL_DMA_IRQ_PD0);
+ }
+
+ pchan->vchan = NULL;
+
+ spin_unlock_irqrestore(&od->lock, flags);
+}
+
+static void owl_dma_pause_pchan(struct owl_dma_pchan *pchan)
+{
+ pchan_writel(pchan, 1, OWL_DMAX_PAUSE);
+}
+
+static void owl_dma_resume_pchan(struct owl_dma_pchan *pchan)
+{
+ pchan_writel(pchan, 0, OWL_DMAX_PAUSE);
+}
+
+static int owl_dma_start_next_txd(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma *od = to_owl_dma(vchan->vc.chan.device);
+ struct virt_dma_desc *vd = vchan_next_desc(&vchan->vc);
+ struct owl_dma_pchan *pchan = vchan->pchan;
+ struct owl_dma_txd *txd = to_owl_txd(&vd->tx);
+ struct owl_dma_lli *lli;
+ unsigned long flags;
+ u32 int_ctl;
+
+ list_del(&vd->node);
+
+ vchan->txd = txd;
+
+ /* Wait for channel inactive */
+ while (owl_dma_pchan_busy(od, pchan))
+ cpu_relax();
+
+ lli = list_first_entry(&txd->lli_list,
+ struct owl_dma_lli, node);
+
+ int_ctl = OWL_DMA_INTCTL_SUPER_BLOCK;
+
+ pchan_writel(pchan, OWL_DMA_MODE_LME, OWL_DMAX_MODE);
+ pchan_writel(pchan, OWL_DMA_LLC_SAV_LOAD_NEXT |
+ OWL_DMA_LLC_DAV_LOAD_NEXT, OWL_DMAX_LINKLIST_CTL);
+ pchan_writel(pchan, lli->phys, OWL_DMAX_NEXT_DESCRIPTOR);
+ pchan_writel(pchan, int_ctl, OWL_DMAX_INT_CTL);
+
+ /* Clear IRQ status for this pchan */
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+
+ spin_lock_irqsave(&od->lock, flags);
+
+ dma_update(od->base + OWL_DMA_IRQ_EN0, (1 << pchan->id), true);
+
+ spin_unlock_irqrestore(&od->lock, flags);
+
+ dev_dbg(chan2dev(&vchan->vc.chan), "starting pchan %d\n", pchan->id);
+
+ /* Start DMA transfer for this pchan */
+ pchan_writel(pchan, 0x1, OWL_DMAX_START);
+
+ return 0;
+}
+
+static void owl_dma_phy_free(struct owl_dma *od, struct owl_dma_vchan *vchan)
+{
+ /* Ensure that the physical channel is stopped */
+ owl_dma_terminate_pchan(od, vchan->pchan);
+
+ vchan->pchan = NULL;
+}
+
+static irqreturn_t owl_dma_interrupt(int irq, void *dev_id)
+{
+ struct owl_dma *od = dev_id;
+ struct owl_dma_vchan *vchan;
+ struct owl_dma_pchan *pchan;
+ unsigned long pending;
+ int i;
+ unsigned int global_irq_pending, chan_irq_pending;
+
+ spin_lock(&od->lock);
+
+ pending = dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ /* Clear IRQ status for each pchan */
+ for_each_set_bit(i, &pending, od->nr_pchans) {
+ pchan = &od->pchans[i];
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS, 0xff, false);
+ }
+
+ /* Clear pending IRQ */
+ dma_writel(od, pending, OWL_DMA_IRQ_PD0);
+
+ /* Check missed pending IRQ */
+ for (i = 0; i < od->nr_pchans; i++) {
+ pchan = &od->pchans[i];
+ chan_irq_pending = pchan_readl(pchan, OWL_DMAX_INT_CTL) &
+ pchan_readl(pchan, OWL_DMAX_INT_STATUS);
+
+ /* Dummy read to ensure OWL_DMA_IRQ_PD0 value is updated */
+ dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ global_irq_pending = dma_readl(od, OWL_DMA_IRQ_PD0);
+
+ if (chan_irq_pending && !(global_irq_pending & BIT(i))) {
+ dev_dbg(od->dma.dev,
+ "global and channel IRQ pending match err\n");
+
+ /* Clear IRQ status for this pchan */
+ pchan_update(pchan->base + OWL_DMAX_INT_STATUS,
+ 0xff, false);
+
+ /* Update global IRQ pending */
+ pending |= BIT(i);
+ }
+ }
+
+ spin_unlock(&od->lock);
+
+ for_each_set_bit(i, &pending, od->nr_pchans) {
+ struct owl_dma_txd *txd;
+
+ pchan = &od->pchans[i];
+
+ vchan = pchan->vchan;
+ if (!vchan) {
+ dev_warn(od->dma.dev, "no vchan attached on pchan %d\n",
+ pchan->id);
+ continue;
+ }
+
+ spin_lock(&vchan->vc.lock);
+
+ txd = vchan->txd;
+ if (txd) {
+ vchan->txd = NULL;
+
+ vchan_cookie_complete(&txd->vd);
+
+ /*
+ * Start the next descriptor (if any),
+ * otherwise free this channel.
+ */
+ if (vchan_next_desc(&vchan->vc))
+ owl_dma_start_next_txd(vchan);
+ else
+ owl_dma_phy_free(od, vchan);
+ }
+
+ spin_unlock(&vchan->vc.lock);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static void owl_dma_free_txd(struct owl_dma *od, struct owl_dma_txd *txd)
+{
+ struct owl_dma_lli *lli, *_lli;
+
+ if (unlikely(!txd))
+ return;
+
+ list_for_each_entry_safe(lli, _lli, &txd->lli_list, node) {
+ owl_dma_free_lli(od, lli);
+ }
+
+ kfree(txd);
+}
+
+static void owl_dma_desc_free(struct virt_dma_desc *vd)
+{
+ struct owl_dma *od = to_owl_dma(vd->tx.chan->device);
+ struct owl_dma_txd *txd = to_owl_txd(&vd->tx);
+
+ owl_dma_free_txd(od, txd);
+}
+
+static int owl_dma_terminate_all(struct dma_chan *chan)
+{
+ struct owl_dma *od = to_owl_dma(chan->device);
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+ LIST_HEAD(head);
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ if (vchan->pchan)
+ owl_dma_phy_free(od, vchan);
+
+ if (vchan->txd) {
+ owl_dma_desc_free(&vchan->txd->vd);
+ vchan->txd = NULL;
+ }
+
+ vchan_get_all_descriptors(&vchan->vc, &head);
+ vchan_dma_desc_free_list(&vchan->vc, &head);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static int owl_dma_pause(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ owl_dma_pause_pchan(vchan->pchan);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static int owl_dma_resume(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ if (!vchan->pchan && !vchan->txd)
+ return 0;
+
+ dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc);
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ owl_dma_resume_pchan(vchan->pchan);
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ return 0;
+}
+
+static u32 owl_dma_getbytes_chan(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma_pchan *pchan;
+ struct owl_dma_txd *txd;
+ struct owl_dma_lli *lli;
+ unsigned int next_lli_phy;
+ size_t bytes;
+
+ pchan = vchan->pchan;
+ txd = vchan->txd;
+
+ if (!pchan || !txd)
+ return 0;
+
+ /* Get remain count of current node in link list */
+ bytes = pchan_readl(pchan, OWL_DMAX_REMAIN_CNT);
+
+ /* Loop through the preceding nodes to get total remaining bytes */
+ if (pchan_readl(pchan, OWL_DMAX_MODE) & OWL_DMA_MODE_LME) {
+ next_lli_phy = pchan_readl(pchan, OWL_DMAX_NEXT_DESCRIPTOR);
+ list_for_each_entry(lli, &txd->lli_list, node) {
+ /* Start from the next active node */
+ if (lli->phys == next_lli_phy) {
+ list_for_each_entry(lli, &txd->lli_list, node)
+ bytes += lli->hw.flen;
+ break;
+ }
+ }
+ }
+
+ return bytes;
+}
+
+static enum dma_status owl_dma_tx_status(struct dma_chan *chan,
+ dma_cookie_t cookie,
+ struct dma_tx_state *state)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ struct owl_dma_lli *lli;
+ struct virt_dma_desc *vd;
+ struct owl_dma_txd *txd;
+ enum dma_status ret;
+ unsigned long flags;
+ size_t bytes = 0;
+
+ ret = dma_cookie_status(chan, cookie, state);
+ if (ret == DMA_COMPLETE || !state)
+ return ret;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+
+ vd = vchan_find_desc(&vchan->vc, cookie);
+ if (vd) {
+ txd = to_owl_txd(&vd->tx);
+ list_for_each_entry(lli, &txd->lli_list, node)
+ bytes += lli->hw.flen;
+ } else {
+ bytes = owl_dma_getbytes_chan(vchan);
+ }
+
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+ dma_set_residue(state, bytes);
+
+ return ret;
+}
+
+static void owl_dma_phy_alloc_and_start(struct owl_dma_vchan *vchan)
+{
+ struct owl_dma *od = to_owl_dma(vchan->vc.chan.device);
+ struct owl_dma_pchan *pchan;
+
+ pchan = owl_dma_get_pchan(od, vchan);
+ if (!pchan) {
+ dev_dbg(od->dma.dev, "no physical channel available\n");
+ return;
+ }
+
+ dev_dbg(od->dma.dev, "allocated pchan %d\n", pchan->id);
+
+ vchan->pchan = pchan;
+ owl_dma_start_next_txd(vchan);
+}
+
+static void owl_dma_issue_pending(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ unsigned long flags;
+
+ spin_lock_irqsave(&vchan->vc.lock, flags);
+ if (vchan_issue_pending(&vchan->vc)) {
+ if (!vchan->pchan)
+ owl_dma_phy_alloc_and_start(vchan);
+ }
+ spin_unlock_irqrestore(&vchan->vc.lock, flags);
+}
+
+static struct dma_async_tx_descriptor
+ *owl_dma_prep_memcpy(struct dma_chan *chan,
+ dma_addr_t dst, dma_addr_t src,
+ size_t len, unsigned long flags)
+{
+ struct owl_dma *od = to_owl_dma(chan->device);
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+ struct owl_dma_txd *txd;
+ struct owl_dma_lli *lli, *prev = NULL;
+ size_t offset, bytes;
+ int ret;
+
+ if (!len)
+ return NULL;
+
+ txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
+ if (!txd)
+ return NULL;
+
+ INIT_LIST_HEAD(&txd->lli_list);
+
+ /* Process the transfer as frame by frame */
+ for (offset = 0; offset < len; offset += bytes) {
+ lli = owl_dma_alloc_lli(od);
+ if (!lli) {
+ dev_warn(chan2dev(chan), "failed to allocate lli\n");
+ goto err_txd_free;
+ }
+
+ bytes = min_t(size_t, (len - offset), OWL_DMA_FRAME_MAX_LENGTH);
+
+ ret = owl_dma_cfg_lli(vchan, lli, src + offset, dst + offset,
+ bytes, DMA_MEM_TO_MEM);
+ if (ret) {
+ dev_warn(chan2dev(chan), "failed to config lli\n");
+ goto err_txd_free;
+ }
+
+ prev = owl_dma_add_lli(txd, prev, lli);
+ }
+
+ return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+
+err_txd_free:
+ owl_dma_free_txd(od, txd);
+ return NULL;
+}
+
+static void owl_dma_free_chan_resources(struct dma_chan *chan)
+{
+ struct owl_dma_vchan *vchan = to_owl_vchan(chan);
+
+ /* Ensure all queued descriptors are freed */
+ vchan_free_chan_resources(&vchan->vc);
+}
+
+static inline void owl_dma_free(struct owl_dma *od)
+{
+ struct owl_dma_vchan *vchan = NULL;
+ struct owl_dma_vchan *next;
+
+ list_for_each_entry_safe(vchan,
+ next, &od->dma.channels, vc.chan.device_node) {
+ list_del(&vchan->vc.chan.device_node);
+ tasklet_kill(&vchan->vc.task);
+ }
+}
+
+static int owl_dma_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct owl_dma *od;
+ struct resource *res;
+ int ret, i, irq, nr_channels, nr_requests;
+
+ od = devm_kzalloc(&pdev->dev, sizeof(*od), GFP_KERNEL);
+ if (!od)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -EINVAL;
+
+ od->base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(od->base))
+ return PTR_ERR(od->base);
+
+ ret = of_property_read_u32(np, "dma-channels", &nr_channels);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get dma-channels\n");
+ return ret;
+ }
+
+ ret = of_property_read_u32(np, "dma-requests", &nr_requests);
+ if (ret) {
+ dev_err(&pdev->dev, "can't get dma-requests\n");
+ return ret;
+ }
+
+ dev_info(&pdev->dev, "dma-channels %d, dma-requests %d\n",
+ nr_channels, nr_requests);
+
+ od->nr_pchans = nr_channels;
+ od->nr_vchans = nr_requests;
+
+ pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+
+ platform_set_drvdata(pdev, od);
+ spin_lock_init(&od->lock);
+
+ dma_cap_set(DMA_MEMCPY, od->dma.cap_mask);
+
+ od->dma.dev = &pdev->dev;
+ od->dma.device_free_chan_resources = owl_dma_free_chan_resources;
+ od->dma.device_tx_status = owl_dma_tx_status;
+ od->dma.device_issue_pending = owl_dma_issue_pending;
+ od->dma.device_prep_dma_memcpy = owl_dma_prep_memcpy;
+ od->dma.device_pause = owl_dma_pause;
+ od->dma.device_resume = owl_dma_resume;
+ od->dma.device_terminate_all = owl_dma_terminate_all;
+ od->dma.src_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ od->dma.dst_addr_widths = BIT(DMA_SLAVE_BUSWIDTH_4_BYTES);
+ od->dma.directions = BIT(DMA_MEM_TO_MEM);
+ od->dma.residue_granularity = DMA_RESIDUE_GRANULARITY_BURST;
+
+ INIT_LIST_HEAD(&od->dma.channels);
+
+ od->clk = devm_clk_get(&pdev->dev, NULL);
+ if (IS_ERR(od->clk)) {
+ dev_err(&pdev->dev, "unable to get clock\n");
+ return PTR_ERR(od->clk);
+ }
+
+ /*
+ * Eventhough the DMA controller is capable of generating 4
+ * IRQ's for DMA priority feature, we only use 1 IRQ for
+ * simplification.
+ */
+ irq = platform_get_irq(pdev, 0);
+ ret = devm_request_irq(&pdev->dev, irq, owl_dma_interrupt, 0,
+ dev_name(&pdev->dev), od);
+ if (ret) {
+ dev_err(&pdev->dev, "unable to request IRQ\n");
+ return ret;
+ }
+
+ /* Init physical channel */
+ od->pchans = devm_kcalloc(&pdev->dev, od->nr_pchans,
+ sizeof(struct owl_dma_pchan), GFP_KERNEL);
+ if (!od->pchans)
+ return -ENOMEM;
+
+ for (i = 0; i < od->nr_pchans; i++) {
+ struct owl_dma_pchan *pchan = &od->pchans[i];
+
+ pchan->id = i;
+ pchan->base = od->base + OWL_DMA_CHAN_BASE(i);
+ }
+
+ /* Init virtual channel */
+ od->vchans = devm_kcalloc(&pdev->dev, od->nr_vchans,
+ sizeof(struct owl_dma_vchan), GFP_KERNEL);
+ if (!od->vchans)
+ return -ENOMEM;
+
+ for (i = 0; i < od->nr_vchans; i++) {
+ struct owl_dma_vchan *vchan = &od->vchans[i];
+
+ vchan->vc.desc_free = owl_dma_desc_free;
+ vchan_init(&vchan->vc, &od->dma);
+ }
+
+ /* Create a pool of consistent memory blocks for hardware descriptors */
+ od->lli_pool = dma_pool_create(dev_name(od->dma.dev), od->dma.dev,
+ sizeof(struct owl_dma_lli),
+ __alignof__(struct owl_dma_lli),
+ 0);
+ if (!od->lli_pool) {
+ dev_err(&pdev->dev, "unable to allocate DMA descriptor pool\n");
+ return -ENOMEM;
+ }
+
+ clk_prepare_enable(od->clk);
+
+ ret = dma_async_device_register(&od->dma);
+ if (ret) {
+ dev_err(&pdev->dev, "failed to register DMA engine device\n");
+ goto err_pool_free;
+ }
+
+ return 0;
+
+err_pool_free:
+ clk_disable_unprepare(od->clk);
+ dma_pool_destroy(od->lli_pool);
+
+ return ret;
+}
+
+static int owl_dma_remove(struct platform_device *pdev)
+{
+ struct owl_dma *od = platform_get_drvdata(pdev);
+
+ of_dma_controller_free(pdev->dev.of_node);
+ dma_async_device_unregister(&od->dma);
+
+ /* Mask all interrupts for this execution environment */
+ dma_writel(od, 0x0, OWL_DMA_IRQ_EN0);
+ owl_dma_free(od);
+
+ clk_disable_unprepare(od->clk);
+
+ return 0;
+}
+
+static const struct of_device_id owl_dma_match[] = {
+ { .compatible = "actions,s900-dma", },
+ { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, owl_dma_match);
+
+static struct platform_driver owl_dma_driver = {
+ .probe = owl_dma_probe,
+ .remove = owl_dma_remove,
+ .driver = {
+ .name = "dma-owl",
+ .of_match_table = of_match_ptr(owl_dma_match),
+ },
+};
+
+static int owl_dma_init(void)
+{
+ return platform_driver_register(&owl_dma_driver);
+}
+subsys_initcall(owl_dma_init);
+
+static void __exit owl_dma_exit(void)
+{
+ platform_driver_unregister(&owl_dma_driver);
+}
+module_exit(owl_dma_exit);
+
+MODULE_AUTHOR("David Liu <liuwei@actions-semi.com>");
+MODULE_AUTHOR("Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>");
+MODULE_DESCRIPTION("Actions Semi Owl SoCs DMA driver");
+MODULE_LICENSE("GPL");
^ permalink raw reply related
* [v2,2/4] arm64: dts: actions: Add Actions Semi S900 DMA Controller
From: Manivannan Sadhasivam @ 2018-07-23 4:17 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add DMA controller node for Actions Semi S900 SoC.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
arch/arm64/boot/dts/actions/s900.dtsi | 13 +++++++++++++
1 file changed, 13 insertions(+)
diff --git a/arch/arm64/boot/dts/actions/s900.dtsi b/arch/arm64/boot/dts/actions/s900.dtsi
index 7ae8b931f000..2e8178e50832 100644
--- a/arch/arm64/boot/dts/actions/s900.dtsi
+++ b/arch/arm64/boot/dts/actions/s900.dtsi
@@ -191,6 +191,19 @@
<GIC_SPI 16 IRQ_TYPE_LEVEL_HIGH>;
};
+ dma: dma-controller@e0260000 {
+ compatible = "actions,s900-dma";
+ reg = <0x0 0xe0260000 0x0 0x1000>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ #dma-cells = <1>;
+ dma-channels = <12>;
+ dma-requests = <46>;
+ clocks = <&cmu CLK_DMAC>;
+ };
+
timer: timer@e0228000 {
compatible = "actions,s900-timer";
reg = <0x0 0xe0228000 0x0 0x8000>;
^ permalink raw reply related
* [v2,1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Manivannan Sadhasivam @ 2018-07-23 4:17 UTC (permalink / raw)
To: vkoul, dan.j.williams, afaerber, robh+dt
Cc: dmaengine, liuwei, 96boards, devicetree, daniel.thompson,
amit.kucheria, linux-arm-kernel, linux-kernel, hzhang, bdong,
manivannanece23, thomas.liau, jeff.chen, pn, edgar.righi,
Manivannan Sadhasivam
Add devicetree binding for Actions Semi Owl SoCs DMA controller.
Signed-off-by: Manivannan Sadhasivam <manivannan.sadhasivam@linaro.org>
---
.../devicetree/bindings/dma/owl-dma.txt | 46 +++++++++++++++++++
1 file changed, 46 insertions(+)
create mode 100644 Documentation/devicetree/bindings/dma/owl-dma.txt
diff --git a/Documentation/devicetree/bindings/dma/owl-dma.txt b/Documentation/devicetree/bindings/dma/owl-dma.txt
new file mode 100644
index 000000000000..dd6ce237b216
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/owl-dma.txt
@@ -0,0 +1,46 @@
+* Actions Semi Owl SoCs DMA controller
+
+This binding follows the generic DMA bindings defined in dma.txt.
+
+Required properties:
+- compatible: Should be "actions,s900-dma".
+- reg: Should contain DMA registers location and length.
+- interrupts: Should contain 4 interrupts shared by all channel.
+- #dma-cells: Must be <1>. Used to represent the number of integer
+ cells in the dmas property of client device.
+- dma-channels: Physical channels supported.
+- dma-requests: Virtual channels supported.
+- clocks: Phandle and Specifier of the clock feeding the DMA controller.
+
+Example:
+
+Controller:
+ dma: dma-controller@e0260000 {
+ compatible = "actions,s900-dma";
+ reg = <0x0 0xe0260000 0x0 0x1000>;
+ interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 58 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 59 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
+ #dma-cells = <1>;
+ dma-channels = <12>;
+ dma-requests = <46>;
+ clocks = <&clock CLK_DMAC>;
+ };
+
+Client:
+
+DMA clients connected to the Actions Semi Owl SoCs DMA controller must
+use the format described in the dma.txt file, using a two-cell specifier
+for each channel.
+
+The two cells in order are:
+1. A phandle pointing to the DMA controller.
+2. The channel id.
+
+uart5: serial@e012a000 {
+ ...
+ dma-names = "tx", "rx";
+ dmas = <&dma 26>, <&dma 27>;
+ ...
+};
^ permalink raw reply related
* [09/15] dmaengine: img-mdc: Handle early status read
From: Andreas Färber @ 2018-07-22 21:20 UTC (permalink / raw)
To: linux-mips
Cc: Ralf Baechle, Paul Burton, James Hogan, linux-kernel,
Damien Horsley, Andreas Färber, Vinod Koul, Dan Williams,
dmaengine
From: Damien Horsley <damien.horsley@imgtec.com>
It is possible that mdc_tx_status may be called before the first
node has been read from memory.
In this case, the residue value stored in the register is undefined.
Return the transfer size instead.
Signed-off-by: Damien Horsley <damien.horsley@imgtec.com>
Signed-off-by: Andreas Färber <afaerber@suse.de>
---
drivers/dma/img-mdc-dma.c | 40 ++++++++++++++++++++++++----------------
1 file changed, 24 insertions(+), 16 deletions(-)
diff --git a/drivers/dma/img-mdc-dma.c b/drivers/dma/img-mdc-dma.c
index 25cec9c243e1..0f2f0f52d83a 100644
--- a/drivers/dma/img-mdc-dma.c
+++ b/drivers/dma/img-mdc-dma.c
@@ -621,25 +621,33 @@ static enum dma_status mdc_tx_status(struct dma_chan *chan,
(MDC_CMDS_PROCESSED_CMDS_DONE_MASK + 1);
/*
- * If the command loaded event hasn't been processed yet, then
- * the difference above includes an extra command.
+ * If the first node has not yet been read from memory,
+ * the residue register value is undefined
*/
- if (!mdesc->cmd_loaded)
- cmds--;
- else
- cmds += mdesc->list_cmds_done;
-
- bytes = mdesc->list_xfer_size;
- ldesc = mdesc->list;
- for (i = 0; i < cmds; i++) {
- bytes -= ldesc->xfer_size + 1;
- ldesc = ldesc->next_desc;
- }
- if (ldesc) {
- if (residue != MDC_TRANSFER_SIZE_MASK)
- bytes -= ldesc->xfer_size - residue;
+ if (!mdesc->cmd_loaded && !cmds) {
+ bytes = mdesc->list_xfer_size;
+ } else {
+ /*
+ * If the command loaded event hasn't been processed yet, then
+ * the difference above includes an extra command.
+ */
+ if (!mdesc->cmd_loaded)
+ cmds--;
else
+ cmds += mdesc->list_cmds_done;
+
+ bytes = mdesc->list_xfer_size;
+ ldesc = mdesc->list;
+ for (i = 0; i < cmds; i++) {
bytes -= ldesc->xfer_size + 1;
+ ldesc = ldesc->next_desc;
+ }
+ if (ldesc) {
+ if (residue != MDC_TRANSFER_SIZE_MASK)
+ bytes -= ldesc->xfer_size - residue;
+ else
+ bytes -= ldesc->xfer_size + 1;
+ }
}
}
spin_unlock_irqrestore(&mchan->vc.lock, flags);
^ permalink raw reply related
* [v3,13/18] dmaengine: dma-jz4780: Set DTCn register explicitly
From: Randy Dunlap @ 2018-07-21 15:10 UTC (permalink / raw)
To: Paul Cercueil, Vinod Koul, Rob Herring, Mark Rutland,
Ralf Baechle, Paul Burton, James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, dmaengine, devicetree,
linux-kernel, linux-mips
On 07/21/2018 04:06 AM, Paul Cercueil wrote:
> From: Daniel Silsby <dansilsby@gmail.com>
>
> Normally, we wouldn't set the channel transfer count register directly
> when using descriptor-driven transfers. However, there is no harm in
> doing so, and it allows jz4780_dma_desc_residue() to report the correct
> residue of an ongoing transfer, no matter when it is called.
>
> Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
> Tested-by: Mathieu Malaterre <malat@debian.org>
> ---
> drivers/dma/dma-jz4780.c | 9 +++++++++
> 1 file changed, 9 insertions(+)
Hi,
Documentation/process/submitting-patches.rst says:
The Signed-off-by: tag indicates that the signer was involved in the
development of the patch, or that he/she was in the patch's delivery path.
That means that patches that are from Daniel but you send (delivery path)
should also be Signed-off-by: you.
^ permalink raw reply
* [v3,18/18] MIPS: JZ4740: DTS: Add DMA nodes
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
Add the devicetree nodes for the DMA core of the JZ4740 SoC, disabled
by default, as currently there are no clients for the DMA driver
(until the MMC driver and/or others get a devicetree node).
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
arch/mips/boot/dts/ingenic/jz4740.dtsi | 15 +++++++++++++++
1 file changed, 15 insertions(+)
v2: New patch in this series
v3: Modify node to comply with devicetree specification
diff --git a/arch/mips/boot/dts/ingenic/jz4740.dtsi b/arch/mips/boot/dts/ingenic/jz4740.dtsi
index 26c6b561d6f7..6fb16fd24035 100644
--- a/arch/mips/boot/dts/ingenic/jz4740.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4740.dtsi
@@ -154,6 +154,21 @@
clock-names = "baud", "module";
};
+ dmac: dma-controller@13020000 {
+ compatible = "ingenic,jz4740-dma";
+ reg = <0x13020000 0xbc
+ 0x13020300 0x14>;
+ #dma-cells = <2>;
+
+ interrupt-parent = <&intc>;
+ interrupts = <29>;
+
+ clocks = <&cgu JZ4740_CLK_DMA>;
+
+ /* Disable dmac until we have something that uses it */
+ status = "disabled";
+ };
+
uhc: uhc@13030000 {
compatible = "ingenic,jz4740-ohci", "generic-ohci";
reg = <0x13030000 0x1000>;
^ permalink raw reply related
* [v3,17/18] MIPS: JZ4770: DTS: Add DMA nodes
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
Add the two devicetree nodes for the two DMA cores of the JZ4770 SoC,
disabled by default, as currently there are no clients for the DMA
driver (until the MMC driver and/or others get a devicetree node).
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
arch/mips/boot/dts/ingenic/jz4770.dtsi | 30 ++++++++++++++++++++++++++++++
1 file changed, 30 insertions(+)
v2: No change
v3: Modify nodes to comply with devicetree specification
diff --git a/arch/mips/boot/dts/ingenic/jz4770.dtsi b/arch/mips/boot/dts/ingenic/jz4770.dtsi
index 7c2804f3f5f1..49ede6c14ff3 100644
--- a/arch/mips/boot/dts/ingenic/jz4770.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4770.dtsi
@@ -196,6 +196,36 @@
status = "disabled";
};
+ dmac0: dma-controller@13420000 {
+ compatible = "ingenic,jz4770-dma";
+ reg = <0x13420000 0xC0
+ 0x13420300 0x20>;
+
+ #dma-cells = <1>;
+
+ clocks = <&cgu JZ4770_CLK_DMA>;
+ interrupt-parent = <&intc>;
+ interrupts = <24>;
+
+ /* Disable dmac0 until we have something that uses it */
+ status = "disabled";
+ };
+
+ dmac1: dma-controller@13420100 {
+ compatible = "ingenic,jz4770-dma";
+ reg = <0x13420100 0xC0
+ 0x13420400 0x20>;
+
+ #dma-cells = <1>;
+
+ clocks = <&cgu JZ4770_CLK_DMA>;
+ interrupt-parent = <&intc>;
+ interrupts = <23>;
+
+ /* Disable dmac1 until we have something that uses it */
+ status = "disabled";
+ };
+
uhc: uhc@13430000 {
compatible = "generic-ohci";
reg = <0x13430000 0x1000>;
^ permalink raw reply related
* [v3,16/18] MIPS: JZ4780: DTS: Update DMA node to match driver changes
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
The driver now accepts two memory resources, the first one for the
channel-specific registers, the second one for the controller-specific
registers.
Note that older devicetrees, without this commit, will still work with
the jz4780-dma driver.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
arch/mips/boot/dts/ingenic/jz4780.dtsi | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
v2: Update info about devicetree ABI compatibility in commit message
v3: No change
diff --git a/arch/mips/boot/dts/ingenic/jz4780.dtsi b/arch/mips/boot/dts/ingenic/jz4780.dtsi
index aa4e8f75ff5d..ad3b1f827cf5 100644
--- a/arch/mips/boot/dts/ingenic/jz4780.dtsi
+++ b/arch/mips/boot/dts/ingenic/jz4780.dtsi
@@ -247,7 +247,8 @@
dma: dma@13420000 {
compatible = "ingenic,jz4780-dma";
- reg = <0x13420000 0x10000>;
+ reg = <0x13420000 0x400
+ 0x13421000 0x40>;
#dma-cells = <2>;
interrupt-parent = <&intc>;
^ permalink raw reply related
* [v3,15/18] dmaengine: dma-jz4780: Use dma_set_residue()
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
From: Daniel Silsby <dansilsby@gmail.com>
This is the standard method provided by dmaengine header.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 9 +++++----
1 file changed, 5 insertions(+), 4 deletions(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index b9db539a5b34..ea17886031fa 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -637,6 +637,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
struct virt_dma_desc *vdesc;
enum dma_status status;
unsigned long flags;
+ unsigned long residue = 0;
status = dma_cookie_status(chan, cookie, txstate);
if ((status == DMA_COMPLETE) || (txstate == NULL))
@@ -647,13 +648,13 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
vdesc = vchan_find_desc(&jzchan->vchan, cookie);
if (vdesc) {
/* On the issued list, so hasn't been processed yet */
- txstate->residue = jz4780_dma_desc_residue(jzchan,
+ residue = jz4780_dma_desc_residue(jzchan,
to_jz4780_dma_desc(vdesc), 0);
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
- txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
+ residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
jzchan->curr_hwdesc + 1);
- } else
- txstate->residue = 0;
+ }
+ dma_set_residue(txstate, residue);
if (vdesc && jzchan->desc && vdesc == &jzchan->desc->vdesc
&& jzchan->desc->status & (JZ_DMA_DCS_AR | JZ_DMA_DCS_HLT))
^ permalink raw reply related
* [v3,14/18] dmaengine: dma-jz4780: Further residue status fix
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
From: Daniel Silsby <dansilsby@gmail.com>
Func jz4780_dma_desc_residue() expects the index to the next hw
descriptor as its last parameter. Caller func jz4780_dma_tx_status(),
however, applied modulus before passing it. When the current hw
descriptor was last in the list, the index passed became zero.
The resulting excess of reported residue especially caused problems
with cyclic DMA transfer clients, i.e. ALSA AIC audio output, which
rely on this for determining current DMA location within buffer.
Combined with the recent and related residue-reporting fixes, spurious
ALSA audio underruns on jz4770 hardware are now fixed.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 78849131c81d..b9db539a5b34 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -651,7 +651,7 @@ static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
to_jz4780_dma_desc(vdesc), 0);
} else if (cookie == jzchan->desc->vdesc.tx.cookie) {
txstate->residue = jz4780_dma_desc_residue(jzchan, jzchan->desc,
- (jzchan->curr_hwdesc + 1) % jzchan->desc->count);
+ jzchan->curr_hwdesc + 1);
} else
txstate->residue = 0;
^ permalink raw reply related
* [v3,13/18] dmaengine: dma-jz4780: Set DTCn register explicitly
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
From: Daniel Silsby <dansilsby@gmail.com>
Normally, we wouldn't set the channel transfer count register directly
when using descriptor-driven transfers. However, there is no harm in
doing so, and it allows jz4780_dma_desc_residue() to report the correct
residue of an ongoing transfer, no matter when it is called.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 9 +++++++++
1 file changed, 9 insertions(+)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index cc2a86844db4..78849131c81d 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -530,6 +530,15 @@ static void jz4780_dma_begin(struct jz4780_dma_chan *jzchan)
jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DRT,
jzchan->transfer_type);
+ /*
+ * Set the transfer count. This is redundant for a descriptor-driven
+ * transfer. However, there can be a delay between the transfer start
+ * time and when DTCn reg contains the new transfer count. Setting
+ * it explicitly ensures residue is computed correctly at all times.
+ */
+ jz4780_dma_chn_writel(jzdma, jzchan->id, JZ_DMA_REG_DTC,
+ jzchan->desc->desc[jzchan->curr_hwdesc].dtc);
+
/* Write descriptor address and initiate descriptor fetch. */
desc_phys = jzchan->desc->desc_phys +
(jzchan->curr_hwdesc * sizeof(*jzchan->desc->desc));
^ permalink raw reply related
* [v3,12/18] dmaengine: dma-jz4780: Simplify jz4780_dma_desc_residue()
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
From: Daniel Silsby <dansilsby@gmail.com>
Simple cleanup, no changes to actual logic here.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 15 +++++----------
1 file changed, 5 insertions(+), 10 deletions(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index fa926de082ba..cc2a86844db4 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -608,22 +608,17 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
struct jz4780_dma_desc *desc, unsigned int next_sg)
{
struct jz4780_dma_dev *jzdma = jz4780_dma_chan_parent(jzchan);
- unsigned int residue, count;
+ unsigned int count = 0;
unsigned int i;
- residue = 0;
-
for (i = next_sg; i < desc->count; i++)
- residue += (desc->desc[i].dtc & 0xffffff) <<
- jzchan->transfer_shift;
+ count += desc->desc[i].dtc & 0xffffff;
- if (next_sg != 0) {
- count = jz4780_dma_chn_readl(jzdma, jzchan->id,
+ if (next_sg != 0)
+ count += jz4780_dma_chn_readl(jzdma, jzchan->id,
JZ_DMA_REG_DTC);
- residue += count << jzchan->transfer_shift;
- }
- return residue;
+ return count << jzchan->transfer_shift;
}
static enum dma_status jz4780_dma_tx_status(struct dma_chan *chan,
^ permalink raw reply related
* [v3,11/18] dmaengine: dma-jz4780: Add missing residue DTC mask
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
From: Daniel Silsby <dansilsby@gmail.com>
The 'dtc' word in jz DMA descriptors contains two fields: The
lowest 24 bits are the transfer count, and upper 8 bits are the DOA
offset to next descriptor. The upper 8 bits are now correctly masked
off when computing residue in jz4780_dma_desc_residue(). Note that
reads of the DTCn hardware reg are automatically masked this way.
Signed-off-by: Daniel Silsby <dansilsby@gmail.com>
Tested-by: Mathieu Malaterre <malat@debian.org>
---
drivers/dma/dma-jz4780.c | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 3c9d3952e23a..fa926de082ba 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -614,7 +614,8 @@ static size_t jz4780_dma_desc_residue(struct jz4780_dma_chan *jzchan,
residue = 0;
for (i = next_sg; i < desc->count; i++)
- residue += desc->desc[i].dtc << jzchan->transfer_shift;
+ residue += (desc->desc[i].dtc & 0xffffff) <<
+ jzchan->transfer_shift;
if (next_sg != 0) {
count = jz4780_dma_chn_readl(jzdma, jzchan->id,
^ permalink raw reply related
* [v3,10/18] dmaengine: dma-jz4780: Enable Fast DMA to the AIC
From: Paul Cercueil @ 2018-07-21 11:06 UTC (permalink / raw)
To: Vinod Koul, Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton,
James Hogan, Zubair Lutfullah Kakakhel
Cc: Mathieu Malaterre, Daniel Silsby, Paul Cercueil, dmaengine,
devicetree, linux-kernel, linux-mips
With the fast DMA bit set, the DMA will transfer twice as much data
per clock period to the AIC, so there is little point not to set it.
Signed-off-by: Paul Cercueil <paul@crapouillou.net>
Tested-by: Mathieu Malaterre <malat@debian.org>
Reviewed-by: PrasannaKumar Muralidharan <prasannatsmkumar@gmail.com>
---
drivers/dma/dma-jz4780.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
v2: No change
v3: No change
diff --git a/drivers/dma/dma-jz4780.c b/drivers/dma/dma-jz4780.c
index 88ce3f0157f6..3c9d3952e23a 100644
--- a/drivers/dma/dma-jz4780.c
+++ b/drivers/dma/dma-jz4780.c
@@ -52,6 +52,7 @@
#define JZ_DMA_DMAC_DMAE BIT(0)
#define JZ_DMA_DMAC_AR BIT(2)
#define JZ_DMA_DMAC_HLT BIT(3)
+#define JZ_DMA_DMAC_FAIC BIT(27)
#define JZ_DMA_DMAC_FMSC BIT(31)
#define JZ_DMA_DRT_AUTO 0x8
@@ -941,8 +942,8 @@ static int jz4780_dma_probe(struct platform_device *pdev)
* Also set the FMSC bit - it increases MSC performance, so it makes
* little sense not to enable it.
*/
- jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC,
- JZ_DMA_DMAC_DMAE | JZ_DMA_DMAC_FMSC);
+ jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMAC, JZ_DMA_DMAC_DMAE |
+ JZ_DMA_DMAC_FAIC | JZ_DMA_DMAC_FMSC);
if (jzdma->version == ID_JZ4780)
jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DMACP, 0);
^ permalink raw reply related
page: next (older) | prev (newer) | latest
- recent:[subjects (threaded)|topics (new)|topics (active)]
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox