* [v2,1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Manivannan Sadhasivam @ 2018-07-25 15:23 UTC (permalink / raw)
To: Vinod
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 Vinod,
On Tue, Jul 24, 2018 at 05:46:04PM +0530, Vinod wrote:
> Hi Mani,
>
> Patch title should be dmaengine: ... Please always use the apt tags and
> one can find them using git log <subsystem>
>
Did you mean, dt-bindings: dmaengine? If yes, then I will use it for the
future patches.
> 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
>
Okay.
> >
> > 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
>
Okay. Will change the property description to request signals.
Thanks,
Mani
> > +- 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
>
> --
> ~Vinod
---
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
* dmaengine: add a new helper dmam_async_device_register
From: Vinod Koul @ 2018-07-25 12:38 UTC (permalink / raw)
To: Huang Shijie
Cc: corbet, dan.j.williams, robh, linux-doc, dmaengine, linux-kernel
On 25-07-18, 13:46, Huang Shijie wrote:
> This patch adds the dmam_async_device_register for DMA code.
> Use the Devres to call the release for the DMA engine driver.
no users?
>
> Signed-off-by: Huang Shijie <sjhuang@iluvatar.ai>
> ---
> Documentation/driver-model/devres.txt | 1 +
> drivers/dma/dmaengine.c | 35 +++++++++++++++++++++++++++
> include/linux/dmaengine.h | 1 +
> 3 files changed, 37 insertions(+)
>
> diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
> index a3e8bceb5f19..26c6de621446 100644
> --- a/Documentation/driver-model/devres.txt
> +++ b/Documentation/driver-model/devres.txt
> @@ -240,6 +240,7 @@ CLOCK
> devm_of_clk_add_hw_provider()
>
> DMA
> + dmam_async_device_register()
can we name it dmaengine_async_device_register, not to confuse with dma APIs
> dmam_alloc_coherent()
> dmam_alloc_attrs()
> dmam_declare_coherent_memory()
> diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
> index 84ac38dbdb65..2477af0bdfc7 100644
> --- a/drivers/dma/dmaengine.c
> +++ b/drivers/dma/dmaengine.c
> @@ -1135,6 +1135,41 @@ void dma_async_device_unregister(struct dma_device *device)
> }
> EXPORT_SYMBOL(dma_async_device_unregister);
>
> +static void dmam_device_release(struct device *dev, void *res)
> +{
> + struct dma_device *device;
> +
> + device = *(struct dma_device **)res;
> + dma_async_device_unregister(device);
> +}
> +
> +/**
> + * dmam_async_device_register - registers DMA devices found
> + * @device: &dma_device
> + *
> + * The operation is managed and will be undone on driver detach.
> + */
> +int dmam_async_device_register(struct dma_device *device)
> +{
> + void *p;
> + int ret;
> +
> + p = devres_alloc(dmam_device_release, sizeof(void *), GFP_KERNEL);
> + if (!p)
> + return -ENOMEM;
> +
> + ret = dma_async_device_register(device);
> + if (!ret) {
> + *(struct dma_device **)p = device;
> + devres_add(device->dev, p);
> + } else {
> + devres_free(p);
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL(dmam_async_device_register);
> +
> struct dmaengine_unmap_pool {
> struct kmem_cache *cache;
> const char *name;
> diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
> index c8c3a7a93802..b98bced0b98e 100644
> --- a/include/linux/dmaengine.h
> +++ b/include/linux/dmaengine.h
> @@ -1406,6 +1406,7 @@ static inline int dmaengine_desc_free(struct dma_async_tx_descriptor *desc)
> /* --- DMA device --- */
>
> int dma_async_device_register(struct dma_device *device);
> +int dmam_async_device_register(struct dma_device *device);
> 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);
> --
> 2.17.1
^ permalink raw reply
* dmaengine: dma_request_chan_by_mask() to handle deferred probing
From: Vinod Koul @ 2018-07-25 12:34 UTC (permalink / raw)
To: Peter Ujfalusi; +Cc: dan.j.williams, dmaengine, linux-kernel
On 18-07-18, 12:29, Peter Ujfalusi wrote:
> If there are no DMA devices registered yet, return with EPROBE_DEFER
> similarly to the case when requesting a slave channel.
Applied, thanks
^ permalink raw reply
* dmaengine: pl330: fix irq race with terminate_all
From: Vinod Koul @ 2018-07-25 12:30 UTC (permalink / raw)
To: John Keeping; +Cc: dmaengine, Dan Williams, linux-kernel
On 17-07-18, 11:48, John Keeping wrote:
> In pl330_update() when checking if a channel has been aborted, the
> channel's lock is not taken, only the overall pl330_dmac lock. But in
> pl330_terminate_all() the aborted flag (req_running==-1) is set under
> the channel lock and not the pl330_dmac lock.
>
> With threaded interrupts, this leads to a potential race:
>
> pl330_terminate_all pl330_update
> ------------------- ------------
> lock channel
> entry
> lock pl330
> _stop channel
> unlock pl330
> lock pl330
> check req_running != -1
> req_running = -1
> _start channel
>
Applied, thanks
^ permalink raw reply
* [1/1] dmaengine: mv_xor_v2: use {lower,upper}_32_bits to configure HW descriptor address
From: Vinod Koul @ 2018-07-25 12:17 UTC (permalink / raw)
To: hannah
Cc: dan.j.williams, dmaengine, thomas.petazzoni, linux-kernel, nadavh,
omrii, oferh, gregory.clement
On 24-07-18, 16:40, hannah@marvell.com wrote:
> From: Hanna Hawa <hannah@marvell.com>
>
> >> drivers/dma/mv_xor_v2.c:647:36: sparse: constant 0xFFFF00000000 is so big it is long
> include/linux/device.h:678:13: sparse: undefined identifier '__builtin_mul_overflow'
> include/linux/device.h:678:13: sparse: call with no type!
>
> Use lower_32_bits and upper_32_bits to set the hw_desq address, instead of
> using constant.
Applied after adding reported-by, thanks.
You should always give credit to error reports
^ permalink raw reply
* [v7,7/7] arm: dts: ls1021a: add qdma device tree nodes
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
add the qDMA device tree nodes for LS1021A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm/boot/dts/ls1021a.dtsi | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm/boot/dts/ls1021a.dtsi b/arch/arm/boot/dts/ls1021a.dtsi
index c55d479..061c839 100644
--- a/arch/arm/boot/dts/ls1021a.dtsi
+++ b/arch/arm/boot/dts/ls1021a.dtsi
@@ -571,6 +571,21 @@
<&clockgen 4 1>;
};
+ qdma: dma-controller@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";
+ dma-channels = <8>;
+ fsl,queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
dcu: dcu@2ce0000 {
compatible = "fsl,ls1021a-dcu";
reg = <0x0 0x2ce0000 0x0 0x10000>;
^ permalink raw reply related
* [v7,6/7] arm64: dts: ls1046a: add qdma device tree nodes
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
add the qDMA device tree nodes for LS1046A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
index 136ebfa..27988e2 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1046a.dtsi
@@ -597,6 +597,21 @@
<&clockgen 4 1>;
};
+ qdma: dma-controller@8380000 {
+ compatible = "fsl,ls1021a-qdma", "fsl,ls1046a-qdma";
+ reg = <0x0 0x8380000 0x0 0x1000>, /* Controller regs */
+ <0x0 0x8390000 0x0 0x10000>, /* Status regs */
+ <0x0 0x83a0000 0x0 0x40000>; /* Block regs */
+ interrupts = <GIC_SPI 153 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
usb0: usb@2f00000 {
compatible = "snps,dwc3";
reg = <0x0 0x2f00000 0x0 0x10000>;
^ permalink raw reply related
* [v7,5/7] arm64: dts: ls1043a: add qdma device tree nodes
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
add the qDMA device tree nodes for LS1043A devices.
Signed-off-by: Wen He <wen.he_1@nxp.com>
---
arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi | 15 +++++++++++++++
1 files changed, 15 insertions(+), 0 deletions(-)
diff --git a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
index 1109f22..48f9165 100644
--- a/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
+++ b/arch/arm64/boot/dts/freescale/fsl-ls1043a.dtsi
@@ -673,6 +673,21 @@
dma-coherent;
};
+ qdma: dma-controller@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 = <GIC_SPI 152 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 39 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ fsl,queues = <2>;
+ status-sizes = <64>;
+ queue-sizes = <64 64>;
+ big-endian;
+ };
+
msi1: msi-controller1@1571000 {
compatible = "fsl,ls1043a-msi";
reg = <0x0 0x1571000 0x0 0x8>;
^ permalink raw reply related
* [v7,4/7] dt-bindings: fsl-qdma: Add NXP Layerscpae qDMA controller bindings
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
Document the devicetree bindings for NXP Layerscape qDMA controller
which could be found on NXP QorIQ Layerscape SoCs.
Signed-off-by: Wen He <wen.he_1@nxp.com>
Reviewed-by: Rob Herring <robh@kernel.org>
---
Documentation/devicetree/bindings/dma/fsl-qdma.txt | 41 ++++++++++++++++++++
1 files changed, 41 insertions(+), 0 deletions(-)
create mode 100644 Documentation/devicetree/bindings/dma/fsl-qdma.txt
diff --git a/Documentation/devicetree/bindings/dma/fsl-qdma.txt b/Documentation/devicetree/bindings/dma/fsl-qdma.txt
new file mode 100644
index 0000000..99b3d74
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/fsl-qdma.txt
@@ -0,0 +1,41 @@
+NXP Layerscape SoC qDMA Controller
+==================================
+
+This device follows the generic DMA bindings defined in dma/dma.txt.
+
+Required properties:
+
+- compatible: Must be one of
+ "fsl,ls1021a-qdma": for LS1021A Board
+ "fsl,ls1043a-qdma": for ls1043A Board
+ "fsl,ls1046a-qdma": for ls1046A Board
+- reg: Should contain the register's base address and length.
+- interrupts: Should contain a reference to the interrupt used by this
+ device.
+- interrupt-names: Should contain interrupt names:
+ "qdma-error": the error interrupt
+ "qdma-queue": the queue interrupt
+- fsl,queues: Should contain number of queues supported.
+
+Optional properties:
+
+- dma-channels: Number of DMA channels supported by the controller.
+- big-endian: If present registers and hardware scatter/gather descriptors
+ of the qDMA are implemented in big endian mode, otherwise in little
+ mode.
+
+Examples:
+
+ qdma: dma-controller@8390000 {
+ compatible = "fsl,ls1021a-qdma";
+ reg = <0x0 0x8398000 0x0 0x2000 /* Controller registers */
+ 0x0 0x839a000 0x0 0x2000>; /* Block registers */
+ interrupts = <GIC_SPI 185 IRQ_TYPE_LEVEL_HIGH>,
+ <GIC_SPI 76 IRQ_TYPE_LEVEL_HIGH>;
+ interrupt-names = "qdma-error", "qdma-queue";
+ dma-channels = <8>;
+ queues = <2>;
+ big-endian;
+ };
+
+DMA clients must use the format described in dma/dma.txt file.
^ permalink raw reply related
* [v7,3/7] dmaengine: fsl-qdma: Add qDMA controller driver for Layerscape SoCs
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
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: Wen He <wen.he_1@nxp.com>
Signed-off-by: Jiaheng Fan <jiaheng.fan@nxp.com>
---
drivers/dma/Kconfig | 13 +
drivers/dma/Makefile | 1 +
drivers/dma/fsl-qdma.c | 1105 ++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 1119 insertions(+), 0 deletions(-)
create mode 100644 drivers/dma/fsl-qdma.c
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ca1680a..2914caf 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 203a99d..0556f9d 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..1cecf9b
--- /dev/null
+++ b/drivers/dma/fsl-qdma.c
@@ -0,0 +1,1105 @@
+// SPDX-License-Identifier: GPL-2.0
+// Copyright 2018 NXP
+
+/*
+ * Driver for NXP Layerscape Queue Direct Memory Access Controller
+ *
+ * Author:
+ * Wen He <wen.he_1@nxp.com>
+ * Jiaheng Fan <jiaheng.fan@nxp.com>
+ *
+ */
+
+#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_EXT BIT(31)
+#define QDMA_SG_LEN_MASK GENMASK(29, 0)
+#define QDMA_CCDF_MASK GENMASK(28, 20)
+
+#define FSL_QDMA_DEDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_BCQIDR_CLEAR GENMASK(31, 0)
+#define FSL_QDMA_DEIER_CLEAR GENMASK(31, 0)
+
+#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_BASE_BUFFER_SIZE 96
+#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
+
+#define FSL_QDMA_E_SG_TABLE 1
+#define FSL_QDMA_E_DATA_BUFFER 0
+#define FSL_QDMA_F_LAST_ENTRY 1
+
+/* Field definition for safe loop count*/
+#define FSL_QDMA_HALT_COUNT 1500
+#define FSL_QDMA_MAX_SIZE 16385
+
+/* Field definition for Descriptor offset */
+#define QDMA_CCDF_STATUS 20
+#define QDMA_CCDF_OFFSET 20
+
+/**
+ * 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;
+
+/* qDMA status notification pre information */
+struct fsl_pre_status {
+ u64 queue;
+ u64 addr;
+};
+
+struct fsl_qdma_chan {
+ struct virt_dma_chan vchan;
+ struct virt_dma_desc vdesc;
+ enum dma_status status;
+ u32 slave_id;
+ struct fsl_qdma_engine *qdma;
+ struct fsl_qdma_queue *queue;
+ struct list_head qcomp;
+};
+
+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;
+ spinlock_t queue_lock;
+ dma_addr_t bus_addr;
+ u32 n_cq;
+ u32 id;
+ struct fsl_qdma_format *cq;
+};
+
+struct fsl_qdma_comp {
+ dma_addr_t bus_addr;
+ struct fsl_qdma_format *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;
+ bool feature;
+ struct fsl_qdma_queue *queue;
+ struct fsl_qdma_queue *status;
+ struct fsl_qdma_chan chans[];
+
+};
+
+static inline u64
+qdma_ccdf_addr_get64(const struct fsl_qdma_format *ccdf)
+{
+ return le64_to_cpu(ccdf->data) & (U64_MAX >> 24);
+}
+
+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 & U8_MAX;
+}
+
+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));
+}
+
+static inline void qdma_csgf_set_e(struct fsl_qdma_format *csgf, int len)
+{
+ csgf->cfg = cpu_to_le32(QDMA_SG_EXT | (len & QDMA_SG_LEN_MASK));
+}
+
+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);
+ 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);
+}
+
+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_format *sdf, *ddf;
+
+ ccdf = fsl_comp->virt_addr;
+ csgf_desc = fsl_comp->virt_addr + 1;
+ csgf_src = fsl_comp->virt_addr + 2;
+ csgf_dest = fsl_comp->virt_addr + 3;
+ sdf = fsl_comp->virt_addr + 4;
+ ddf = fsl_comp->virt_addr + 5;
+
+ memset(fsl_comp->virt_addr, 0, FSL_QDMA_BASE_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->bus_addr + 64);
+ /* 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->data = cpu_to_le64(
+ FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data = cpu_to_le64(
+ FSL_QDMA_CMD_RWTTYPE << FSL_QDMA_CMD_RWTTYPE_OFFSET);
+ ddf->data |= cpu_to_le64(
+ FSL_QDMA_CMD_LWC << FSL_QDMA_CMD_LWC_OFFSET);
+}
+
+/*
+ * Pre-request full command descriptor for enqueue.
+ */
+static int fsl_qdma_pre_request_enqueue_desc(struct fsl_qdma_queue *queue)
+{
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+ int i;
+
+ for (i = 0; i < queue->n_cq; i++) {
+ comp_temp = kzalloc(sizeof(*comp_temp), GFP_NOWAIT);
+ if (!comp_temp)
+ goto err;
+
+ comp_temp->virt_addr = dma_pool_alloc(queue->comp_pool,
+ GFP_NOWAIT,
+ &comp_temp->bus_addr);
+ if (!comp_temp->virt_addr)
+ goto err;
+
+ list_add_tail(&comp_temp->list, &queue->comp_free);
+ }
+ return 0;
+
+err:
+ if (i == 0 && comp_temp) {
+ kfree(comp_temp);
+ return -ENOMEM;
+ }
+
+ while (--i >= 1) {
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue->comp_free, list) {
+ dma_pool_free(queue->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ }
+ return -ENOMEM;
+}
+
+/*
+ * Request a command descriptor for enqueue.
+ */
+static struct fsl_qdma_comp *fsl_qdma_request_enqueue_desc(
+ struct fsl_qdma_chan *fsl_chan,
+ unsigned int dst_nents,
+ unsigned int src_nents)
+{
+ struct fsl_qdma_comp *comp_temp;
+ struct fsl_qdma_queue *queue = fsl_chan->queue;
+ unsigned long flags;
+
+ spin_lock_irqsave(&queue->queue_lock, flags);
+ if (list_empty(&queue->comp_free)) {
+ spin_unlock_irqrestore(&queue->queue_lock, flags);
+ comp_temp = kzalloc(sizeof(*comp_temp), GFP_KERNEL);
+ if (!comp_temp)
+ return NULL;
+
+ 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 NULL;
+ }
+
+ } else {
+ 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;
+}
+
+static struct fsl_qdma_queue *fsl_qdma_alloc_queue_resources(
+ struct platform_device *pdev,
+ unsigned int queue_num)
+{
+ struct fsl_qdma_queue *queue_head, *queue_temp;
+ int ret, i;
+ unsigned int queue_size[FSL_QDMA_QUEUE_MAX];
+
+ if (queue_num > FSL_QDMA_QUEUE_MAX)
+ queue_num = FSL_QDMA_QUEUE_MAX;
+ queue_head = devm_kcalloc(&pdev->dev, queue_num,
+ sizeof(*queue_head), 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 (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;
+ 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) {
+ devm_kfree(&pdev->dev, queue_head);
+ return NULL;
+ }
+ 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;
+
+ /*
+ * Create a comp dma pool that size
+ * is 'FSL_QDMA_BASE_BUFFER_SIZE'.
+ * The dma pool for queue command buffer.
+ */
+ queue_temp->comp_pool = dma_pool_create("comp_pool",
+ &pdev->dev,
+ FSL_QDMA_BASE_BUFFER_SIZE,
+ 16, 0);
+ if (!queue_temp->comp_pool)
+ goto err;
+
+ /*
+ * List for queue command buffer
+ */
+ INIT_LIST_HEAD(&queue_temp->comp_used);
+ INIT_LIST_HEAD(&queue_temp->comp_free);
+ spin_lock_init(&queue_temp->queue_lock);
+ }
+
+ return queue_head;
+
+err:
+ if (i == 0 && queue_temp->comp_pool)
+ dma_pool_destroy(queue_temp->comp_pool);
+ while (--i >= 1) {
+ queue_temp = queue_head + i;
+ if (i == 1 && unlikely(queue_temp->comp_pool))
+ dma_pool_destroy(queue_temp->comp_pool);
+ }
+
+ dev_err(&pdev->dev,
+ "unable to allocate channel %d descriptor pool\n",
+ queue_temp->id);
+
+ while (--i >= 0) {
+ queue_temp = queue_head + i;
+ dma_free_coherent(&pdev->dev,
+ sizeof(struct fsl_qdma_format) *
+ queue_size[i],
+ queue_temp->cq,
+ queue_temp->bus_addr);
+ }
+ devm_kfree(&pdev->dev, queue_head);
+ return NULL;
+}
+
+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) {
+ devm_kfree(&pdev->dev, status_head);
+ 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 = fsl_qdma->block_base;
+ int i, count = FSL_QDMA_HALT_COUNT;
+ 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 (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);
+ }
+
+ /* Disable status queue. */
+ qdma_writel(fsl_qdma, 0, block + FSL_QDMA_BSQMR);
+
+ /* Clear all detected events and interrupts for all queues. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ return 0;
+}
+
+static int fsl_qdma_queue_transfer_complete(struct fsl_qdma_engine *fsl_qdma)
+{
+ struct fsl_qdma_queue *fsl_queue = fsl_qdma->queue;
+ struct fsl_qdma_queue *fsl_status = fsl_qdma->status;
+ struct fsl_qdma_queue *temp_queue;
+ struct fsl_qdma_comp *fsl_comp;
+ struct fsl_qdma_format *status_addr;
+ struct fsl_qdma_format *csgf_src;
+ struct fsl_pre_status pre;
+ void __iomem *block = fsl_qdma->block_base;
+ u32 reg, i, count;
+ bool duplicate, duplicate_handle;
+
+ memset(&pre, 0, sizeof(struct fsl_pre_status));
+ count = FSL_QDMA_MAX_SIZE;
+
+ while (count--) {
+ duplicate = 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) == pre.queue &&
+ qdma_ccdf_addr_get64(status_addr) == pre.addr)
+ duplicate = 1;
+
+ i = qdma_ccdf_get_queue(status_addr);
+ pre.queue = qdma_ccdf_get_queue(status_addr);
+ pre.addr = qdma_ccdf_addr_get64(status_addr);
+ temp_queue = fsl_queue + i;
+ spin_lock(&temp_queue->queue_lock);
+ if (list_empty(&temp_queue->comp_used)) {
+ if (!duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ } else {
+ fsl_comp = list_first_entry(&temp_queue->comp_used,
+ struct fsl_qdma_comp,
+ list);
+ csgf_src = fsl_comp->virt_addr + 2;
+ if (fsl_comp->bus_addr + 16 != pre.addr) {
+ if (duplicate) {
+ spin_unlock(&temp_queue->queue_lock);
+ return -EAGAIN;
+ }
+ }
+ }
+
+ if (duplicate) {
+ 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");
+
+ /* Clear all error conditions and interrupts. */
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, 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 *block = fsl_qdma->block_base;
+ void __iomem *ctrl = fsl_qdma->ctrl_base;
+
+ 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);
+
+ 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");
+ }
+
+ /* Clear all detected events and interrupts. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ return IRQ_HANDLED;
+}
+
+static int
+fsl_qdma_irq_init(struct platform_device *pdev,
+ struct fsl_qdma_engine *fsl_qdma)
+{
+ 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;
+ }
+
+ fsl_qdma->queue_irq = platform_get_irq_byname(pdev, "qdma-queue");
+ if (fsl_qdma->queue_irq < 0) {
+ dev_err(&pdev->dev, "Can't get qdma queue irq.\n");
+ return fsl_qdma->queue_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;
+ }
+ ret = devm_request_irq(&pdev->dev, fsl_qdma->queue_irq,
+ fsl_qdma_queue_handler, 0, "qDMA queue", fsl_qdma);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't register qDMA queue IRQ.\n");
+ 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 == fsl_qdma->error_irq) {
+ devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, fsl_qdma);
+ } else {
+ devm_free_irq(&pdev->dev, fsl_qdma->queue_irq, 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 = fsl_qdma->block_base;
+ int i, 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;
+ }
+
+ /* Clear all detected events and interrupts for all queues. */
+ qdma_writel(fsl_qdma, FSL_QDMA_BCQIDR_CLEAR,
+ block + FSL_QDMA_BCQIDR(0));
+
+ for (i = 0; i < fsl_qdma->n_queues; i++) {
+ temp = fsl_queue + i;
+ /*
+ * 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->bus_addr,
+ block + FSL_QDMA_SQEPAR);
+ qdma_writel(fsl_qdma, fsl_qdma->status->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 controller interrupt register. */
+ qdma_writel(fsl_qdma, FSL_QDMA_DEDR_CLEAR, status + FSL_QDMA_DEDR);
+ qdma_writel(fsl_qdma, FSL_QDMA_DEIER_CLEAR, status + FSL_QDMA_DEIER);
+
+ /* Initialize the status queue mode. */
+ reg = FSL_QDMA_BSQMR_EN;
+ reg |= FSL_QDMA_BSQMR_CQ_SIZE(ilog2(fsl_qdma->status->n_cq)-6);
+ qdma_writel(fsl_qdma, reg, block + FSL_QDMA_BSQMR);
+
+ 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, 0, 0);
+ 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)
+{
+ void __iomem *block = fsl_chan->qdma->block_base;
+ struct fsl_qdma_queue *fsl_queue = fsl_chan->queue;
+ struct fsl_qdma_comp *fsl_comp;
+ struct virt_dma_desc *vdesc;
+ 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,
+ sizeof(struct fsl_qdma_format));
+ 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)
+{
+ enum dma_status ret;
+
+ ret = dma_cookie_status(chan, cookie, txstate);
+ if (ret == DMA_COMPLETE || !txstate)
+ return ret;
+
+ return ret;
+}
+
+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 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;
+
+ ret = of_property_read_u32(np, "dma-channels", &chans);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get dma-channels.\n");
+ return ret;
+ }
+
+ len = sizeof(*fsl_qdma) + sizeof(*fsl_chan) * chans;
+ fsl_qdma = devm_kzalloc(&pdev->dev, len, GFP_KERNEL);
+ if (!fsl_qdma)
+ return -ENOMEM;
+
+ ret = of_property_read_u32(np, "fsl,queues", &queues);
+ if (ret) {
+ dev_err(&pdev->dev, "Can't get queues.\n");
+ return ret;
+ }
+
+ fsl_qdma->queue = fsl_qdma_alloc_queue_resources(pdev, queues);
+ if (!fsl_qdma->queue)
+ return -ENOMEM;
+
+ fsl_qdma->status = fsl_qdma_prep_status_queue(pdev);
+ if (!fsl_qdma->status)
+ return -ENOMEM;
+
+ fsl_qdma->n_chans = chans;
+ fsl_qdma->n_queues = queues;
+ mutex_init(&fsl_qdma->fsl_qdma_mutex);
+
+ 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);
+
+ ret = fsl_qdma_irq_init(pdev, fsl_qdma);
+ if (ret)
+ return ret;
+
+ 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_chan->vchan.desc_free = fsl_qdma_free_desc;
+ INIT_LIST_HEAD(&fsl_chan->qcomp);
+ vchan_init(&fsl_chan->vchan, &fsl_qdma->dma_dev);
+ }
+ for (i = 0; i < fsl_qdma->n_queues; i++)
+ fsl_qdma_pre_request_enqueue_desc(fsl_qdma->queue + i);
+
+ 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_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;
+
+ 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 *queue_temp;
+ struct fsl_qdma_queue *status = fsl_qdma->status;
+ struct fsl_qdma_comp *comp_temp, *_comp_temp;
+ 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);
+
+ /* Free descriptor areas */
+ for (i = 0; i < fsl_qdma->n_queues; i++) {
+ queue_temp = fsl_qdma->queue + i;
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue_temp->comp_used, list) {
+ dma_pool_free(queue_temp->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ list_for_each_entry_safe(comp_temp, _comp_temp,
+ &queue_temp->comp_free, list) {
+ dma_pool_free(queue_temp->comp_pool,
+ comp_temp->virt_addr,
+ comp_temp->bus_addr);
+ list_del(&comp_temp->list);
+ kfree(comp_temp);
+ }
+ dma_free_coherent(&pdev->dev, sizeof(struct fsl_qdma_format) *
+ queue_temp->n_cq, queue_temp->cq,
+ queue_temp->bus_addr);
+ dma_pool_destroy(queue_temp->comp_pool);
+ }
+
+ 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
* [v7,2/7] dmaengine: fsldma: Adding macro FSL_DMA_IN/OUT implement for ARM platform
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
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
* [v7,1/7] dmaengine: fsldma: Replace DMA_IN/OUT by FSL_DMA_IN/OUT
From: Wen He @ 2018-07-25 11:29 UTC (permalink / raw)
To: vkoul, dmaengine
Cc: robh+dt, devicetree, leoyang.li, jiafei.pan, jiaheng.fan,
wen.he_1
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
* dmaengine: sh: rcar-dmac: Should not stop the DMAC by rcar_dmac_sync_tcr()
From: Yoshihiro Shimoda @ 2018-07-25 8:27 UTC (permalink / raw)
To: vinod.koul; +Cc: dmaengine, linux-renesas-soc, Yoshihiro Shimoda
rcar_dmac_chan_get_residue() should not stop the DMAC, because
the commit 538603c6026c ("dmaengine: sh: rcar-dmac: avoid to write
CHCR.TE to 1 if TCR is set to 0") had fixed unexpected re-transferring
issue. But it had caused the next issue which might stop the cyclic
mode transferring. Thus, for example R-Car sound might be stopped
suddenly.
According to the commit 73a47bd0da66 ("dmaengine: rcar-dmac: use TCRB
instead of TCR for residue"), the purpose of clearing CHCR.DE bit is
flushing buffered data to calculate the exact residue.
Such the "exact" residue had been required by sh-sci driver. sh-sci
driver is calling dmaengine_pause() to stop transferring, and get
"exact" residue. Otherwise, it might receive extra data during
getting residue without pausing.
In rx_timer_fn() of sh-sci driver:
dmaengine_tx_status(); /* For checking roughly */
dmaengine_pause();
dmaengine_tx_status(); /* For getting residue */
dmaengine_terminate_all();
But, unfortunately the rcar-dmac driver didn't support dmaengine_pause()
at that time. So, the sh-sci driver cannot get the "exact" residue
without stopping the transferring, because rcar-dmac is buffering data
inside.
Because of these backgrounds, rcar-dmac had been cleared/set CHCR.DE
bit in rcar_dmac_chan_get_residue() to synchronizing data and getting
"exact" residue.
However, rcar-dmac driver has rcar_dmac_chan_pause() now, and clearing
CHCR.DE bit in rcar_dmac_chan_get_residue() doesn't need anymore.
So, this patch removes the rcar_dmac_sync_tcr().
Fixes: 73a47bd0da66 ("dmaengine: rcar-dmac: use TCRB instead of TCR for residue")
Signed-off-by: Yoshihiro Shimoda <yoshihiro.shimoda.uh@renesas.com>
Tested-by: Hiroyuki Yokoyama <hiroyuki.yokoyama.vx@renesas.com>
---
drivers/dma/sh/rcar-dmac.c | 17 -----------------
1 file changed, 17 deletions(-)
diff --git a/drivers/dma/sh/rcar-dmac.c b/drivers/dma/sh/rcar-dmac.c
index be82d69..48ee35e 100644
--- a/drivers/dma/sh/rcar-dmac.c
+++ b/drivers/dma/sh/rcar-dmac.c
@@ -770,20 +770,6 @@ static void rcar_dmac_clear_chcr_de(struct rcar_dmac_chan *chan)
rcar_dmac_chcr_de_barrier(chan);
}
-static void rcar_dmac_sync_tcr(struct rcar_dmac_chan *chan)
-{
- u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
-
- if (!(chcr & RCAR_DMACHCR_DE))
- return;
-
- rcar_dmac_clear_chcr_de(chan);
-
- /* back DE if remain data exists */
- if (rcar_dmac_chan_read(chan, RCAR_DMATCR))
- rcar_dmac_chan_write(chan, RCAR_DMACHCR, chcr);
-}
-
static void rcar_dmac_chan_halt(struct rcar_dmac_chan *chan)
{
u32 chcr = rcar_dmac_chan_read(chan, RCAR_DMACHCR);
@@ -1367,9 +1353,6 @@ static unsigned int rcar_dmac_chan_get_residue(struct rcar_dmac_chan *chan,
residue += chunk->size;
}
- if (desc->direction == DMA_DEV_TO_MEM)
- rcar_dmac_sync_tcr(chan);
-
/* Add the residue for the current chunk. */
residue += rcar_dmac_chan_read(chan, RCAR_DMATCRB) << desc->xfer_shift;
^ permalink raw reply related
* dmaengine: add a new helper dmam_async_device_register
From: Huang Shijie @ 2018-07-25 5:46 UTC (permalink / raw)
To: vkoul
Cc: corbet, dan.j.williams, robh, linux-doc, dmaengine, linux-kernel,
Huang Shijie
This patch adds the dmam_async_device_register for DMA code.
Use the Devres to call the release for the DMA engine driver.
Signed-off-by: Huang Shijie <sjhuang@iluvatar.ai>
---
Documentation/driver-model/devres.txt | 1 +
drivers/dma/dmaengine.c | 35 +++++++++++++++++++++++++++
include/linux/dmaengine.h | 1 +
3 files changed, 37 insertions(+)
diff --git a/Documentation/driver-model/devres.txt b/Documentation/driver-model/devres.txt
index a3e8bceb5f19..26c6de621446 100644
--- a/Documentation/driver-model/devres.txt
+++ b/Documentation/driver-model/devres.txt
@@ -240,6 +240,7 @@ CLOCK
devm_of_clk_add_hw_provider()
DMA
+ dmam_async_device_register()
dmam_alloc_coherent()
dmam_alloc_attrs()
dmam_declare_coherent_memory()
diff --git a/drivers/dma/dmaengine.c b/drivers/dma/dmaengine.c
index 84ac38dbdb65..2477af0bdfc7 100644
--- a/drivers/dma/dmaengine.c
+++ b/drivers/dma/dmaengine.c
@@ -1135,6 +1135,41 @@ void dma_async_device_unregister(struct dma_device *device)
}
EXPORT_SYMBOL(dma_async_device_unregister);
+static void dmam_device_release(struct device *dev, void *res)
+{
+ struct dma_device *device;
+
+ device = *(struct dma_device **)res;
+ dma_async_device_unregister(device);
+}
+
+/**
+ * dmam_async_device_register - registers DMA devices found
+ * @device: &dma_device
+ *
+ * The operation is managed and will be undone on driver detach.
+ */
+int dmam_async_device_register(struct dma_device *device)
+{
+ void *p;
+ int ret;
+
+ p = devres_alloc(dmam_device_release, sizeof(void *), GFP_KERNEL);
+ if (!p)
+ return -ENOMEM;
+
+ ret = dma_async_device_register(device);
+ if (!ret) {
+ *(struct dma_device **)p = device;
+ devres_add(device->dev, p);
+ } else {
+ devres_free(p);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL(dmam_async_device_register);
+
struct dmaengine_unmap_pool {
struct kmem_cache *cache;
const char *name;
diff --git a/include/linux/dmaengine.h b/include/linux/dmaengine.h
index c8c3a7a93802..b98bced0b98e 100644
--- a/include/linux/dmaengine.h
+++ b/include/linux/dmaengine.h
@@ -1406,6 +1406,7 @@ static inline int dmaengine_desc_free(struct dma_async_tx_descriptor *desc)
/* --- DMA device --- */
int dma_async_device_register(struct dma_device *device);
+int dmam_async_device_register(struct dma_device *device);
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);
^ permalink raw reply related
* [v3,3/3] dmaengine: imx-sdma: allocate max 20 bds for one transfer
From: Robin Gong @ 2018-07-25 1:24 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
> -----Original Message-----
> From: Lucas Stach [mailto:l.stach@pengutronix.de]
> Sent: 2018年7月24日 17:22
> To: Robin Gong <yibin.gong@nxp.com>; vkoul@kernel.org;
> dan.j.williams@intel.com; s.hauer@pengutronix.de; linux@armlinux.org.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 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.
Please notice only allocate bds memory from dma pool one time even in multi bds.
That's different with the common use case that allocate memory from dma pool everytime
for every bd. Why do this is to make sure all bd memory is contiguous for single transfer
whatever single bd or multi-bds, since two call dma_pool_alloc() can't promise the address
is contiguous especially for multi thread case such as dmatest 'threads_per_chan = 5'. You
can change to ' norandom=true ' and ' test_buf_size = 163840' in dmatest.c to look what
issue coming without this patch.
>
> So something isn't right with this change.
I think this patch is the easy way to resolve the bd contiguous issue, but the cost is to
allocate more dma pool memory which may not used.
>
> 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;
^ permalink raw reply
* [v3,01/18] doc: dt-bindings: jz4780-dma: Update bindings to reflect driver changes
From: Rob Herring @ 2018-07-24 23:35 UTC (permalink / raw)
To: Paul Cercueil
Cc: Vinod Koul, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
On Sat, Jul 21, 2018 at 01:06:26PM +0200, Paul Cercueil wrote:
> The driver is now compatible with four SoCs: JZ4780, JZ4770, JZ4725B and
> JZ4740.
What the driver supports is irrelevant to the binding.
>
> Besides, it now expects the devicetree to supply a second memory
> resource. This resource is mandatory on the newly supported SoCs.
> For the JZ4780, new devicetree code must also provide it, although the
> driver is still compatible with older devicetree binaries.
>
> Signed-off-by: Paul Cercueil <paul@crapouillou.net>
> Tested-by: Mathieu Malaterre <malat@debian.org>
> ---
> Documentation/devicetree/bindings/dma/jz4780-dma.txt | 14 ++++++++++----
> 1 file changed, 10 insertions(+), 4 deletions(-)
>
> v2: New patch in this series; regroups the changes made to the
> jz4780-dma.txt doc file in the previous version of the patchset.
>
> v3: Updated example to comply with devicetree specification
>
> diff --git a/Documentation/devicetree/bindings/dma/jz4780-dma.txt b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
> index f25feee62b15..14f33305e194 100644
> --- a/Documentation/devicetree/bindings/dma/jz4780-dma.txt
> +++ b/Documentation/devicetree/bindings/dma/jz4780-dma.txt
> @@ -2,8 +2,13 @@
>
> Required properties:
>
> -- compatible: Should be "ingenic,jz4780-dma"
> -- reg: Should contain the DMA controller registers location and length.
> +- compatible: Should be one of:
> + * ingenic,jz4740-dma
> + * ingenic,jz4725b-dma
> + * ingenic,jz4770-dma
> + * ingenic,jz4780-dma
So none of these are compatible with each other? It should be one valid
combination per line.
> +- reg: Should contain the DMA channel registers location and length, followed
> + by the DMA controller registers location and length.
> - interrupts: Should contain the interrupt specifier of the DMA controller.
> - interrupt-parent: Should be the phandle of the interrupt controller that
> - clocks: Should contain a clock specifier for the JZ4780 PDMA clock.
> @@ -20,9 +25,10 @@ Optional properties:
>
> Example:
>
> -dma: dma@13420000 {
> +dma: dma-controller@13420000 {
> compatible = "ingenic,jz4780-dma";
> - reg = <0x13420000 0x10000>;
> + reg = <0x13420000 0x400
> + 0x13421000 0x40>;
>
> interrupt-parent = <&intc>;
> interrupts = <10>;
> --
> 2.11.0
>
---
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,07/18] dmaengine: dma-jz4780: Add support for the JZ4770 SoC
From: Vinod Koul @ 2018-07-24 16:01 UTC (permalink / raw)
To: Paul Cercueil
Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
On 24-07-18, 17:04, Paul Cercueil wrote:
> Hi Vinod,
>
> Le mar. 24 juil. 2018 à 15:32, Vinod <vkoul@kernel.org> a écrit :
> > On 21-07-18, 13:06, Paul Cercueil wrote:
> > > +static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev
> > > *jzdma,
> > > + unsigned int chn)
> >
> > right justified and aligned with preceding please. While adding new
> > code to a existing driver it is a good idea to conform to existing style
>
> OK.
>
> > > +{
> > > + if (jzdma->version == ID_JZ4770)
> > > + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
> > > +}
> > > +
> > > +static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev
> > > *jzdma,
> > > + unsigned int chn)
> > > +{
> > > + if (jzdma->version == ID_JZ4770)
> > > + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
> >
> > so if another version has this feature we would do:
> > if (jzdma->version == ID_JZ4770) ||
> > if (jzdma->version == ID_JZXXXX))
> >
> > and so on.. why not add a value, clk_enable in the description and use
> > that. For each controller it is set to true or false
>
> I agree with what you said in your other answers.
> However here I still need to check the "version", because on JZ4725B and
> JZ4770+
> the way to start/stop each DMA channel's clock is different, so I can't use
> a boolean
sure describe the behavior and use that. Versions is not a very scalable
way..
^ permalink raw reply
* [v3,07/18] dmaengine: dma-jz4780: Add support for the JZ4770 SoC
From: Paul Cercueil @ 2018-07-24 15:04 UTC (permalink / raw)
To: Vinod
Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
Hi Vinod,
Le mar. 24 juil. 2018 à 15:32, Vinod <vkoul@kernel.org> a écrit :
> On 21-07-18, 13:06, Paul Cercueil wrote:
>> +static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev
>> *jzdma,
>> + unsigned int chn)
>
> right justified and aligned with preceding please. While adding new
> code to a existing driver it is a good idea to conform to existing
> style
OK.
>> +{
>> + if (jzdma->version == ID_JZ4770)
>> + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
>> +}
>> +
>> +static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev
>> *jzdma,
>> + unsigned int chn)
>> +{
>> + if (jzdma->version == ID_JZ4770)
>> + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
>
> so if another version has this feature we would do:
> if (jzdma->version == ID_JZ4770) ||
> if (jzdma->version == ID_JZXXXX))
>
> and so on.. why not add a value, clk_enable in the description and use
> that. For each controller it is set to true or false
I agree with what you said in your other answers.
However here I still need to check the "version", because on JZ4725B
and JZ4770+
the way to start/stop each DMA channel's clock is different, so I can't
use a boolean.
> --
> ~Vinod
Thanks,
-Paul
---
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
* [1/1] dmaengine: mv_xor_v2: use {lower,upper}_32_bits to configure HW descriptor address
From: hannah @ 2018-07-24 13:40 UTC (permalink / raw)
To: dan.j.williams, vkoul, dmaengine
Cc: thomas.petazzoni, linux-kernel, nadavh, omrii, oferh,
gregory.clement, Hanna Hawa
From: Hanna Hawa <hannah@marvell.com>
>> drivers/dma/mv_xor_v2.c:647:36: sparse: constant 0xFFFF00000000 is so big it is long
include/linux/device.h:678:13: sparse: undefined identifier '__builtin_mul_overflow'
include/linux/device.h:678:13: sparse: call with no type!
Use lower_32_bits and upper_32_bits to set the hw_desq address, instead of
using constant.
Signed-off-by: Hanna Hawa <hannah@marvell.com>
---
drivers/dma/mv_xor_v2.c | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/dma/mv_xor_v2.c b/drivers/dma/mv_xor_v2.c
index d41d916..8dc0aa4 100644
--- a/drivers/dma/mv_xor_v2.c
+++ b/drivers/dma/mv_xor_v2.c
@@ -642,9 +642,9 @@ static int mv_xor_v2_descq_init(struct mv_xor_v2_device *xor_dev)
xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_SIZE_OFF);
/* write the DESQ address to the DMA enngine*/
- writel(xor_dev->hw_desq & 0xFFFFFFFF,
+ writel(lower_32_bits(xor_dev->hw_desq),
xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_BALR_OFF);
- writel((xor_dev->hw_desq & 0xFFFF00000000) >> 32,
+ writel(upper_32_bits(xor_dev->hw_desq),
xor_dev->dma_base + MV_XOR_V2_DMA_DESQ_BAHR_OFF);
/*
^ permalink raw reply related
* [v3,11/18] dmaengine: dma-jz4780: Add missing residue DTC mask
From: Vinod Koul @ 2018-07-24 13:38 UTC (permalink / raw)
To: Paul Cercueil
Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
On 21-07-18, 13:06, Paul Cercueil wrote:
> 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>
This needs your s-o-b. Please see Documentation/process/submitting-patches.rst
I think Randy did flag this one some other patch as well. All the
patches need to be signed off by sender as well
^ permalink raw reply
* [v3,07/18] dmaengine: dma-jz4780: Add support for the JZ4770 SoC
From: Vinod Koul @ 2018-07-24 13:32 UTC (permalink / raw)
To: Paul Cercueil
Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
On 21-07-18, 13:06, Paul Cercueil wrote:
> +static inline void jz4780_dma_chan_enable(struct jz4780_dma_dev *jzdma,
> + unsigned int chn)
right justified and aligned with preceding please. While adding new
code to a existing driver it is a good idea to conform to existing style
> +{
> + if (jzdma->version == ID_JZ4770)
> + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKES, BIT(chn));
> +}
> +
> +static inline void jz4780_dma_chan_disable(struct jz4780_dma_dev *jzdma,
> + unsigned int chn)
> +{
> + if (jzdma->version == ID_JZ4770)
> + jz4780_dma_ctrl_writel(jzdma, JZ_DMA_REG_DCKEC, BIT(chn));
so if another version has this feature we would do:
if (jzdma->version == ID_JZ4770) ||
if (jzdma->version == ID_JZXXXX))
and so on.. why not add a value, clk_enable in the description and use
that. For each controller it is set to true or false
^ permalink raw reply
* [v3,03/18] dmaengine: dma-jz4780: Avoid hardcoding number of channels
From: Vinod Koul @ 2018-07-24 13:22 UTC (permalink / raw)
To: Paul Cercueil
Cc: Rob Herring, Mark Rutland, Ralf Baechle, Paul Burton, James Hogan,
Zubair Lutfullah Kakakhel, Mathieu Malaterre, Daniel Silsby,
dmaengine, devicetree, linux-kernel, linux-mips
On 21-07-18, 13:06, Paul Cercueil wrote:
> +static const struct jz4780_dma_soc_data jz4780_dma_soc_data[] = {
> + [ID_JZ4780] = { .nb_channels = 32, },
why the array of structs?
> +};
> +
> +static const struct of_device_id jz4780_dma_dt_match[] = {
> + { .compatible = "ingenic,jz4780-dma", .data = (void *)ID_JZ4780 },
the data should be jz4780_dma_soc_data? as you would add more data
later.. and not the enum..
> - jzdma = devm_kzalloc(dev, sizeof(*jzdma), GFP_KERNEL);
> + version = (enum jz_version)of_id->data;
> + soc_data = &jz4780_dma_soc_data[version];
this can be simplified if we do:
soc_data = device_get_match_data(pdev);
with:
static const struct jz4780_dma_soc_data jz4780_dma_soc_data = {
.nb_channels = 32,
};
and
{ .compatible = "ingenic,jz4780-dma", .data = (void *)jz4780_dma_soc_data },
You add more parameters in future patches and store soc_data in driver
object and use as is..
> + jzdma = devm_kzalloc(dev, sizeof(*jzdma)
> + + sizeof(*jzdma->chan) * soc_data->nb_channels,
> + GFP_KERNEL);
> if (!jzdma)
> return -ENOMEM;
>
> + jzdma->soc_data = soc_data;
> + jzdma->version = version;
why do you need to store version, driver should handle values and not
versions..
^ permalink raw reply
* [v2,3/4] dma: Add Actions Semi Owl family S900 DMA driver
From: Vinod Koul @ 2018-07-24 13:09 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
somehow this got stuck so sending again...
On 24-07-18, 18:16, Vinod wrote:
> On 23-07-18, 09:47, Manivannan Sadhasivam wrote:
>
> > +#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>
>
> do you need this?
>
> > +/* 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)
>
> what are you trying to do with this? Generally we would define register
> bits using BIT and GENMASK here..
>
> > +/* Extract the bit field to new shift */
> > +#define BIT_FIELD(val, width, shift, newshift) \
> > + ((((val) >> (shift)) & ((BIT(width)) - 1)) << (newshift))
>
> why new shift? I guess you want to extract bits from a register here and
> use those, right?
>
> > +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 */
>
> i think you can skip comment here or kernel-doc style, please pick one
> and not both
>
> > +struct owl_dma_txd {
> > + struct virt_dma_desc vd;
> > + struct list_head lli_list;
>
> why do you need this list. vd has its own list as well!
>
> > +static void pchan_update(void __iomem *reg, u32 val, bool state)
>
> why does this not use pchan as arg as the name of API implies (you did
> that on the other two)
>
> > +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 */
>
> are you checking that somewhere?
>
> > +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");
>
> not sure about this. The concept of virt-chan is that you would submit a
> transaction to controller for different channels. If channel is busy the
> txn is simply queued up. You do not need a _free_ channel
>
> > +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);
> > +}
>
> mempcy and pause/resume dont make much sense, are you sure you want that
> here and not later on slave copy
>
> > +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);
> > + }
>
> braces not required here
>
> > +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);
>
> the tasklets are killed but irqs can still run and trigger the irqs :)
> --
> ~Vinod
^ permalink raw reply
* [v2,1/4] dt-bindings: dma: Add binding for Actions Semi Owl SoCs
From: Vinod Koul @ 2018-07-24 13:08 UTC (permalink / raw)
To: Andreas Färber
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
On 24-07-18, 14:19, Andreas Färber wrote:
> 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.
for the driver patch :)
^ permalink raw reply
* [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
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