All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 0/4] dmaengine: Add support Oxford Semiconductor OXNAS DMA Engine
@ 2022-01-04 14:52 ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This serie adds support for the DMA engine found in Oxford Semiconductor SoCs,
notably in the OX810SE where it's heavily used for SATA transfers.

The driver was on my pipe since 2016 and a courageous person managed to get
the SATA driver work up mainline kernel with this driver, so I cleaned it up
in order to be upstreamed.

I plan to push the last patch through arm-soc when bindings is applied.

Neil Armstrong (4):
  dt-bindings: dma: Add bindings for ox810se dma engine
  dmaengine: Add Oxford Semiconductor OXNAS DMA Controller
  MAINTAINERS: add OX810SE DMA driver files under Oxnas entry
  ARM: dts: ox810se: Add DMA Support

 .../bindings/dma/oxsemi,ox810se-dma.yaml      |   97 ++
 MAINTAINERS                                   |    2 +
 arch/arm/boot/dts/ox810se-wd-mbwe.dts         |    4 +
 arch/arm/boot/dts/ox810se.dtsi                |   21 +
 drivers/dma/Kconfig                           |    8 +
 drivers/dma/Makefile                          |    1 +
 drivers/dma/oxnas_adma.c                      | 1045 +++++++++++++++++
 7 files changed, 1178 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
 create mode 100644 drivers/dma/oxnas_adma.c


base-commit: fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf
-- 
2.25.1


^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 0/4] dmaengine: Add support Oxford Semiconductor OXNAS DMA Engine
@ 2022-01-04 14:52 ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This serie adds support for the DMA engine found in Oxford Semiconductor SoCs,
notably in the OX810SE where it's heavily used for SATA transfers.

The driver was on my pipe since 2016 and a courageous person managed to get
the SATA driver work up mainline kernel with this driver, so I cleaned it up
in order to be upstreamed.

I plan to push the last patch through arm-soc when bindings is applied.

Neil Armstrong (4):
  dt-bindings: dma: Add bindings for ox810se dma engine
  dmaengine: Add Oxford Semiconductor OXNAS DMA Controller
  MAINTAINERS: add OX810SE DMA driver files under Oxnas entry
  ARM: dts: ox810se: Add DMA Support

 .../bindings/dma/oxsemi,ox810se-dma.yaml      |   97 ++
 MAINTAINERS                                   |    2 +
 arch/arm/boot/dts/ox810se-wd-mbwe.dts         |    4 +
 arch/arm/boot/dts/ox810se.dtsi                |   21 +
 drivers/dma/Kconfig                           |    8 +
 drivers/dma/Makefile                          |    1 +
 drivers/dma/oxnas_adma.c                      | 1045 +++++++++++++++++
 7 files changed, 1178 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
 create mode 100644 drivers/dma/oxnas_adma.c


base-commit: fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 13+ messages in thread

* [PATCH 1/4] dt-bindings: dma: Add bindings for ox810se dma engine
  2022-01-04 14:52 ` Neil Armstrong
@ 2022-01-04 14:52   ` Neil Armstrong
  -1 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul, devicetree
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This adds the YAML dt-bindings for the DMA engine found in the
Oxford Semiconductor OX810SE SoC.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 .../bindings/dma/oxsemi,ox810se-dma.yaml      | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml

diff --git a/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
new file mode 100644
index 000000000000..6efa28e8b124
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/oxsemi,ox810se-dma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Oxford Semiconductor DMA Controller Device Tree Bindings
+
+maintainers:
+  - Neil Armstrong <narmstrong@baylibre.com>
+
+allOf:
+  - $ref: "dma-controller.yaml#"
+
+properties:
+  "#dma-cells":
+    const: 1
+
+  compatible:
+    const: oxsemi,ox810se-dma
+
+  reg:
+    maxItems: 2
+
+  reg-names:
+    items:
+      - const: dma
+      - const: sgdma
+
+  interrupts:
+    maxItems: 5
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 2
+
+  reset-names:
+    items:
+      - const: dma
+      - const: sgdma
+
+  dma-channels: true
+
+  oxsemi,targets-types:
+    description:
+      Table with allowed memory ranges and memory type associated.
+    $ref: "/schemas/types.yaml#/definitions/uint32-matrix"
+    minItems: 4
+    items:
+      items:
+        - description:
+            The first cell defines the memory range start address
+        - description:
+            The first cell defines the memory range end address
+        - description:
+            The third cell represents memory type, 0 for SATA,
+            1 for DPE RX, 2 for DPE TX, 5 for AUDIO TX, 6 for AUDIO RX,
+            15 for DRAM MEMORY.
+          enum: [ 0, 1, 2, 5, 6, 15 ]
+
+required:
+  - "#dma-cells"
+  - dma-channels
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - resets
+  - reset-names
+  - oxsemi,targets-types
+
+additionalProperties: false
+
+examples:
+  - |
+    dma: dma-controller@600000 {
+        compatible = "oxsemi,ox810se-dma";
+        reg = <0x600000 0x100000>, <0xc00000 0x100000>;
+        reg-names = "dma", "sgdma";
+        interrupts = <13>, <14>, <15>, <16>, <20>;
+        clocks = <&stdclk 1>;
+        resets = <&reset 8>, <&reset 24>;
+        reset-names = "dma", "sgdma";
+
+        /* Encodes the authorized memory types */
+        oxsemi,targets-types =
+            <0x45900000 0x45a00000 0>,  /* SATA */
+            <0x42000000 0x43000000 0>,  /* SATA DATA */
+            <0x48000000 0x58000000 15>, /* DDR */
+            <0x58000000 0x58020000 15>; /* SRAM */
+
+        #dma-cells = <1>;
+        dma-channels = <5>;
+    };
+...
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 1/4] dt-bindings: dma: Add bindings for ox810se dma engine
@ 2022-01-04 14:52   ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul, devicetree
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This adds the YAML dt-bindings for the DMA engine found in the
Oxford Semiconductor OX810SE SoC.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 .../bindings/dma/oxsemi,ox810se-dma.yaml      | 97 +++++++++++++++++++
 1 file changed, 97 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml

diff --git a/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
new file mode 100644
index 000000000000..6efa28e8b124
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
@@ -0,0 +1,97 @@
+# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/dma/oxsemi,ox810se-dma.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Oxford Semiconductor DMA Controller Device Tree Bindings
+
+maintainers:
+  - Neil Armstrong <narmstrong@baylibre.com>
+
+allOf:
+  - $ref: "dma-controller.yaml#"
+
+properties:
+  "#dma-cells":
+    const: 1
+
+  compatible:
+    const: oxsemi,ox810se-dma
+
+  reg:
+    maxItems: 2
+
+  reg-names:
+    items:
+      - const: dma
+      - const: sgdma
+
+  interrupts:
+    maxItems: 5
+
+  clocks:
+    maxItems: 1
+
+  resets:
+    maxItems: 2
+
+  reset-names:
+    items:
+      - const: dma
+      - const: sgdma
+
+  dma-channels: true
+
+  oxsemi,targets-types:
+    description:
+      Table with allowed memory ranges and memory type associated.
+    $ref: "/schemas/types.yaml#/definitions/uint32-matrix"
+    minItems: 4
+    items:
+      items:
+        - description:
+            The first cell defines the memory range start address
+        - description:
+            The first cell defines the memory range end address
+        - description:
+            The third cell represents memory type, 0 for SATA,
+            1 for DPE RX, 2 for DPE TX, 5 for AUDIO TX, 6 for AUDIO RX,
+            15 for DRAM MEMORY.
+          enum: [ 0, 1, 2, 5, 6, 15 ]
+
+required:
+  - "#dma-cells"
+  - dma-channels
+  - compatible
+  - reg
+  - interrupts
+  - clocks
+  - resets
+  - reset-names
+  - oxsemi,targets-types
+
+additionalProperties: false
+
+examples:
+  - |
+    dma: dma-controller@600000 {
+        compatible = "oxsemi,ox810se-dma";
+        reg = <0x600000 0x100000>, <0xc00000 0x100000>;
+        reg-names = "dma", "sgdma";
+        interrupts = <13>, <14>, <15>, <16>, <20>;
+        clocks = <&stdclk 1>;
+        resets = <&reset 8>, <&reset 24>;
+        reset-names = "dma", "sgdma";
+
+        /* Encodes the authorized memory types */
+        oxsemi,targets-types =
+            <0x45900000 0x45a00000 0>,  /* SATA */
+            <0x42000000 0x43000000 0>,  /* SATA DATA */
+            <0x48000000 0x58000000 15>, /* DDR */
+            <0x58000000 0x58020000 15>; /* SRAM */
+
+        #dma-cells = <1>;
+        dma-channels = <5>;
+    };
+...
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/4] dmaengine: Add Oxford Semiconductor OXNAS DMA Controller
  2022-01-04 14:52 ` Neil Armstrong
@ 2022-01-04 14:52   ` Neil Armstrong
  -1 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

The Oxford Semiconductor OX810SE contains a DMA engine mainly used
for the SATA controller which doesn't have an embedded DMA engine.

This DMA engine support single and scatter-gather memory-to-memory
transfers and device-to-memory/memory-to-device transfers.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/dma/Kconfig      |    8 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/oxnas_adma.c | 1045 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1054 insertions(+)
 create mode 100644 drivers/dma/oxnas_adma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 6bcdb4e6a0d1..8925ffd85ac2 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -505,6 +505,14 @@ config OWL_DMA
 	help
 	  Enable support for the Actions Semi Owl SoCs DMA controller.
 
+config OXNAS_DMA
+	bool "Oxford Semiconductor OXNAS SoC Family DMA support"
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	depends on ARCH_OXNAS || COMPILE_TEST
+	help
+	  Support for Oxford Semiconductor OXNAS SoC Family DMA engine
+
 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 616d926cf2a5..ba1111785bb8 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -58,6 +58,7 @@ 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_OXNAS_DMA) += oxnas_adma.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PLX_DMA) += plx_dma.o
diff --git a/drivers/dma/oxnas_adma.c b/drivers/dma/oxnas_adma.c
new file mode 100644
index 000000000000..586c23187de1
--- /dev/null
+++ b/drivers/dma/oxnas_adma.c
@@ -0,0 +1,1045 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2008 Oxford Semiconductor Ltd
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/memory.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+
+#include "dmaengine.h"
+#include "virt-dma.h"
+
+/* Normal (non-SG) registers */
+#define DMA_REGS_PER_CHANNEL 8
+
+#define DMA_CTRL_STATUS      0x00
+#define DMA_BASE_SRC_ADR     0x04
+#define DMA_BASE_DST_ADR     0x08
+#define DMA_BYTE_CNT         0x0c
+#define DMA_CURRENT_SRC_ADR  0x10
+#define DMA_CURRENT_DST_ADR  0x14
+#define DMA_CURRENT_BYTE_CNT 0x18
+#define DMA_INTR_ID          0x1c
+#define DMA_INTR_CLEAR_REG   (DMA_CURRENT_SRC_ADR)
+
+/* 8 quad-sized registers per channel arranged contiguously */
+#define DMA_CALC_REG_ADR(channel, register) (((channel) << 5) + (register))
+
+#define DMA_CTRL_STATUS_FAIR_SHARE_ARB            BIT(0)
+#define DMA_CTRL_STATUS_IN_PROGRESS               BIT(1)
+#define DMA_CTRL_STATUS_SRC_DREQ                  GENMASK(5, 2)
+#define DMA_CTRL_STATUS_DEST_DREQ                 GENMASK(10, 7)
+#define DMA_CTRL_STATUS_INTR                      BIT(10)
+#define DMA_CTRL_STATUS_NXT_FREE                  BIT(11)
+#define DMA_CTRL_STATUS_RESET                     BIT(12)
+#define DMA_CTRL_STATUS_DIR                       GENMASK(14, 13)
+#define DMA_CTRL_STATUS_SRC_ADR_MODE              BIT(15)
+#define DMA_CTRL_STATUS_DEST_ADR_MODE             BIT(16)
+#define DMA_CTRL_STATUS_TRANSFER_MODE_A           BIT(17)
+#define DMA_CTRL_STATUS_TRANSFER_MODE_B           BIT(18)
+#define DMA_CTRL_STATUS_SRC_WIDTH                 GENMASK(21, 19)
+#define DMA_CTRL_STATUS_DEST_WIDTH                GENMASK(24, 22)
+#define DMA_CTRL_STATUS_PAUSE                     BIT(25)
+#define DMA_CTRL_STATUS_INTERRUPT_ENABLE          BIT(26)
+#define DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED      BIT(27)
+#define DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED BIT(28)
+#define DMA_CTRL_STATUS_STARVE_LOW_PRIORITY       BIT(29)
+#define DMA_CTRL_STATUS_INTR_CLEAR_ENABLE         BIT(30)
+
+#define DMA_BYTE_CNT_MASK                         GENMASK(20, 0)
+#define DMA_BYTE_CNT_INC4_SET_MASK                BIT(28)
+#define DMA_BYTE_CNT_HPROT_MASK                   BIT(29)
+#define DMA_BYTE_CNT_WR_EOT_MASK                  BIT(30)
+#define DMA_BYTE_CNT_RD_EOT_MASK                  BIT(31)
+
+#define DMA_INTR_ID_GET_NUM_CHANNELS(reg_contents) (((reg_contents) >> 16) & 0xFF)
+#define DMA_INTR_ID_GET_VERSION(reg_contents)      (((reg_contents) >> 24) & 0xFF)
+#define DMA_INTR_ID_INT_MASK        GENMASK(MAX_OXNAS_DMA_CHANNELS, 0)
+
+#define DMA_HAS_V4_INTR_CLEAR(version) ((version) > 3)
+
+/* H/W scatter gather controller registers */
+#define OXNAS_DMA_NUM_SG_REGS 4
+
+#define DMA_SG_CONTROL  0x00
+#define DMA_SG_STATUS   0x04
+#define DMA_SG_REQ_PTR  0x08
+#define DMA_SG_RESETS   0x0c
+
+#define DMA_SG_CALC_REG_ADR(channel, register) (((channel) << 4) + (register))
+
+/* SG DMA controller control register field definitions */
+#define DMA_SG_CONTROL_START		BIT(0)
+#define DMA_SG_CONTROL_QUEUING_ENABLE	BIT(1)
+#define DMA_SG_CONTROL_HBURST_ENABLE	BIT(2)
+
+/* SG DMA controller status register field definitions */
+#define DMA_SG_STATUS_ERROR_CODE	GENMASK(5, 0)
+#define DMA_SG_STATUS_BUSY		BIT(7)
+
+/* SG DMA controller sub-block resets register field definitions */
+#define DMA_SG_RESETS_CONTROL		BIT(0)
+#define DMA_SG_RESETS_ARBITER		BIT(1)
+#define DMA_SG_RESETS_AHB		BIT(2)
+
+/* SG DMA controller qualifier field definitions */
+#define OXNAS_DMA_SG_QUALIFIER		GENMASK(15, 0)
+#define OXNAS_DMA_SG_DST_EOT		GENMASK(17, 16)
+#define OXNAS_DMA_SG_SRC_EOT		GENMASK(22, 20)
+#define OXNAS_DMA_SG_CHANNEL		GENMASK(31, 24)
+
+#define OXNAS_DMA_ADR_MASK		GENMASK(29, 0)
+#define OXNAS_DMA_MAX_TRANSFER_LENGTH	DMA_BYTE_CNT_MASK
+
+/* The available buses to which the DMA controller is attached */
+enum {
+	OXNAS_DMA_SIDE_A = 0,
+	OXNAS_DMA_SIDE_B
+};
+
+/* Direction of data flow between the DMA controller's pair of interfaces */
+enum {
+	OXNAS_DMA_A_TO_A = 0,
+	OXNAS_DMA_B_TO_A,
+	OXNAS_DMA_A_TO_B,
+	OXNAS_DMA_B_TO_B
+};
+
+/* The available data widths */
+enum {
+	OXNAS_DMA_TRANSFER_WIDTH_8BITS = 0,
+	OXNAS_DMA_TRANSFER_WIDTH_16BITS,
+	OXNAS_DMA_TRANSFER_WIDTH_32BITS
+};
+
+/* The mode of the DMA transfer */
+enum {
+	OXNAS_DMA_TRANSFER_MODE_SINGLE = 0,
+	OXNAS_DMA_TRANSFER_MODE_BURST
+};
+
+/* The available transfer targets */
+enum {
+	OXNAS_DMA_DREQ_PATA     = 0,
+	OXNAS_DMA_DREQ_SATA     = 0,
+	OXNAS_DMA_DREQ_DPE_RX   = 1,
+	OXNAS_DMA_DREQ_DPE_TX   = 2,
+	OXNAS_DMA_DREQ_AUDIO_TX = 5,
+	OXNAS_DMA_DREQ_AUDIO_RX = 6,
+	OXNAS_DMA_DREQ_MEMORY   = 15
+};
+
+enum {
+	OXNAS_DMA_TYPE_SIMPLE = 0,
+	OXNAS_DMA_TYPE_SG,
+};
+
+#define MAX_OXNAS_DMA_CHANNELS	5
+#define MAX_OXNAS_SG_ENTRIES	512
+
+/* Will be exchanged with SG DMA controller */
+struct oxnas_dma_sg_entry {
+	dma_addr_t                 data_addr;   /* physical address of the buffer */
+	unsigned long              data_length; /* length of the buffer */
+	dma_addr_t                 p_next_entry; /* physical address of next descriptor */
+	struct oxnas_dma_sg_entry *next_entry;   /* virtual address of the next descriptor */
+	dma_addr_t                 this_paddr;  /* physical address of this descriptor */
+	struct list_head	   entry;  /* Linked list entry */
+} __attribute((aligned(4), packed));
+
+/* Will be exchanged with SG DMA controller */
+struct oxnas_dma_sg_info {
+	unsigned long         qualifier;
+	unsigned long         control;
+	dma_addr_t            p_src_entries; /* physical address of the first source SG desc */
+	dma_addr_t            p_dst_entries; /* physical address of the first dest SG desc */
+	struct oxnas_dma_sg_entry *src_entries; /* virtual address of the first source SG desc */
+	struct oxnas_dma_sg_entry *dst_entries; /* virtual address of the first dest SG desc */
+} __attribute((aligned(4), packed));
+
+struct oxnas_dma_sg_data {
+	struct oxnas_dma_sg_entry entries[MAX_OXNAS_SG_ENTRIES];
+	struct oxnas_dma_sg_info infos[MAX_OXNAS_DMA_CHANNELS];
+} __attribute((aligned(4)));
+
+struct oxnas_dma_device;
+struct oxnas_dma_channel;
+
+struct oxnas_dma_desc {
+	struct virt_dma_desc vd;
+	struct oxnas_dma_channel *channel;
+	unsigned long ctrl;
+	unsigned long len;
+	dma_addr_t src_adr;
+	dma_addr_t dst_adr;
+	unsigned int type;
+	struct oxnas_dma_sg_info sg_info;
+	unsigned int entries;
+	struct list_head sg_entries;
+};
+
+struct oxnas_dma_channel {
+	struct virt_dma_chan vc;
+	struct list_head node;
+	struct oxnas_dma_device	*dmadev;
+	unsigned int id;
+	unsigned int irq;
+
+	struct dma_slave_config	cfg;
+
+	dma_addr_t p_sg_info; /* physical address of the array of sg_info structs */
+	struct oxnas_dma_sg_info *sg_info; /* virtual address of the array of sg_info structs */
+
+	atomic_t active;
+
+	struct oxnas_dma_desc *cur;
+};
+
+struct oxnas_dma_device {
+	struct platform_device *pdev;
+	struct dma_device common;
+	void __iomem *dma_base;
+	void __iomem *sgdma_base;
+	struct reset_control *dma_rst;
+	struct reset_control *sgdma_rst;
+	struct clk *dma_clk;
+
+	unsigned int channels_count;
+
+	struct oxnas_dma_channel channels[MAX_OXNAS_DMA_CHANNELS];
+
+	unsigned int hwversion;
+
+	/* Protects concurrent access to channels */
+	spinlock_t lock;
+	struct tasklet_struct tasklet;
+
+	struct list_head pending;
+
+	struct {
+		dma_addr_t start;
+		dma_addr_t end;
+		unsigned int type;
+	} *authorized_types;
+	unsigned int authorized_types_count;
+
+	struct list_head free_entries;
+	atomic_t free_entries_count;
+	dma_addr_t p_sg_data;
+	struct oxnas_dma_sg_data *sg_data;
+};
+
+static void oxnas_dma_start_next(struct oxnas_dma_channel *channel);
+
+static irqreturn_t oxnas_dma_interrupt(int irq, void *dev_id)
+{
+	struct oxnas_dma_channel *channel = dev_id;
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	unsigned long error_code;
+	unsigned long flags;
+
+	dev_vdbg(&dmadev->pdev->dev, "irq for channel %d\n", channel->id);
+
+	while (readl(dmadev->dma_base + DMA_CALC_REG_ADR(0, DMA_INTR_ID)) & BIT(channel->id)) {
+		dev_dbg(&dmadev->pdev->dev, "Acking interrupt for channel %u\n",
+			channel->id);
+
+		/* Write to the interrupt clear register to clear interrupt */
+		writel(0, dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_INTR_CLEAR_REG));
+	}
+
+	if (channel->cur && channel->cur->type == OXNAS_DMA_TYPE_SG) {
+		error_code = readl(dmadev->sgdma_base +
+				DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_STATUS));
+		error_code &= DMA_SG_STATUS_ERROR_CODE;
+
+		/* TOFIX report it to the core */
+		if (error_code)
+			dev_err(&dmadev->pdev->dev, "ch%d: sgdma err %x\n",
+				channel->id, (unsigned int)error_code);
+
+		writel(1, dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_STATUS));
+	}
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+
+	if (atomic_read(&channel->active)) {
+		struct oxnas_dma_desc *cur = channel->cur;
+
+		oxnas_dma_start_next(channel);
+		if (cur)
+			vchan_cookie_complete(&cur->vd);
+	} else {
+		dev_warn(&dmadev->pdev->dev, "spurious irq for channel %d\n",
+			 channel->id);
+	}
+
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void oxnas_dma_start_next(struct oxnas_dma_channel *channel)
+{
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct virt_dma_desc *vd = vchan_next_desc(&channel->vc);
+	struct oxnas_dma_desc *desc;
+	unsigned long ctrl_status;
+
+	if (!vd) {
+		channel->cur = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+	channel->cur = desc;
+
+	if (desc->type == OXNAS_DMA_TYPE_SIMPLE) {
+		/* Write the control/status value to the DMAC */
+		writel(desc->ctrl,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+
+		/* Ensure control/status word makes it to the DMAC before
+		 * we write address/length info
+		 */
+		wmb();
+
+		/* Write the source addresses to the DMAC */
+		writel(desc->src_adr & OXNAS_DMA_ADR_MASK,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BASE_SRC_ADR));
+
+		/* Write the destination addresses to the DMAC */
+		writel(desc->dst_adr & OXNAS_DMA_ADR_MASK,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BASE_DST_ADR));
+
+		/* Write the length, with EOT configuration
+		 * for the single transfer
+		 */
+		writel(desc->len,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BYTE_CNT));
+
+		/* Ensure adr/len info makes it to DMAC before later modifications to
+		 * control/status register due to starting the transfer, which happens in
+		 * oxnas_dma_start()
+		 */
+		wmb();
+
+		/* Setup channel data */
+		atomic_set(&channel->active, 1);
+
+		/* Single transfer mode, so unpause the DMA controller channel */
+		ctrl_status = readl(dmadev->dma_base +
+				    DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+		writel(ctrl_status & ~DMA_CTRL_STATUS_PAUSE,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+
+		dev_dbg(&dmadev->pdev->dev, "ch%d: started req %d from %08x to %08x, %lubytes\n",
+			channel->id, vd->tx.cookie,
+			desc->src_adr, desc->dst_adr,
+			desc->len & OXNAS_DMA_MAX_TRANSFER_LENGTH);
+	} else if (desc->type == OXNAS_DMA_TYPE_SG) {
+		/* Write to the SG-DMA channel's reset register to reset the control
+		 * in case the previous SG-DMA transfer failed in some way, thus
+		 * leaving the SG-DMA controller hung up part way through processing
+		 * its SG list. The reset bits are self-clearing
+		 */
+		writel(DMA_SG_RESETS_CONTROL,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_RESETS));
+
+		/* Copy the sg_info structure */
+		memcpy(channel->sg_info, &desc->sg_info, sizeof(struct oxnas_dma_sg_info));
+
+		/* Ensure adr/len info makes it to DMAC before later modifications to
+		 * control/status register due to starting the transfer, which happens in
+		 * oxnas_dma_start()
+		 */
+		wmb();
+
+		/* Write the pointer to the SG info struct into the Request Pointer reg */
+		writel(channel->p_sg_info,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_REQ_PTR));
+
+		/* Setup channel data */
+		atomic_set(&channel->active, 1);
+
+		/* Start the transfert */
+		writel(DMA_SG_CONTROL_START |
+		       DMA_SG_CONTROL_QUEUING_ENABLE |
+		       DMA_SG_CONTROL_HBURST_ENABLE,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_CONTROL));
+
+		dev_dbg(&dmadev->pdev->dev, "ch%d: started %d sg req with %d entries\n",
+			channel->id, vd->tx.cookie,
+			desc->entries);
+	}
+}
+
+static void oxnas_dma_sched(unsigned long data)
+{
+	struct oxnas_dma_device *dmadev = (struct oxnas_dma_device *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&dmadev->lock);
+	list_splice_tail_init(&dmadev->pending, &head);
+	spin_unlock_irq(&dmadev->lock);
+
+	while (!list_empty(&head)) {
+		struct oxnas_dma_channel *ch = list_first_entry(&head,
+								struct oxnas_dma_channel, node);
+
+		spin_lock_irq(&ch->vc.lock);
+
+		list_del_init(&ch->node);
+		oxnas_dma_start_next(ch);
+
+		spin_unlock_irq(&ch->vc.lock);
+	}
+}
+
+static int oxnas_check_address(struct oxnas_dma_device *dmadev, dma_addr_t address)
+{
+	int i;
+
+	for (i = 0 ; i <  dmadev->authorized_types_count ; ++i) {
+		if (address >= dmadev->authorized_types[i].start &&
+		    address < dmadev->authorized_types[i].end)
+			return dmadev->authorized_types[i].type;
+	}
+
+	return -1;
+}
+
+static struct dma_async_tx_descriptor *oxnas_dma_prep_slave_sg(struct dma_chan *chan,
+							       struct scatterlist *sgl,
+							       unsigned int sglen,
+							       enum dma_transfer_direction dir,
+							       unsigned long flags, void *context)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct oxnas_dma_desc *desc;
+	struct scatterlist *sgent;
+	struct oxnas_dma_sg_entry *entry_mem = NULL, *prev_entry_mem = NULL;
+	struct oxnas_dma_sg_entry *entry_dev = NULL;
+	unsigned int i;
+	int src_memory = OXNAS_DMA_DREQ_MEMORY;
+	int dst_memory = OXNAS_DMA_DREQ_MEMORY;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		src_memory = oxnas_check_address(dmadev, channel->cfg.src_addr);
+		if (src_memory == -1) {
+			dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n",
+				channel->cfg.src_addr);
+			return NULL;
+		}
+
+		if (src_memory == OXNAS_DMA_DREQ_MEMORY) {
+			dev_err(&dmadev->pdev->dev, "In DEV_TO_MEM, src cannot be memory\n");
+			return NULL;
+		}
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dst_memory = oxnas_check_address(dmadev, channel->cfg.dst_addr);
+		if (dst_memory == -1) {
+			dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n",
+				channel->cfg.dst_addr);
+			return NULL;
+		}
+
+		if (dst_memory == OXNAS_DMA_DREQ_MEMORY) {
+			dev_err(&dmadev->pdev->dev, "In MEM_TO_DEV, dst cannot be memory\n");
+			return NULL;
+		}
+	} else {
+		dev_err(&dmadev->pdev->dev, "invalid direction\n");
+		return NULL;
+	}
+
+	if (atomic_read(&dmadev->free_entries_count) < (sglen + 1)) {
+		dev_err(&dmadev->pdev->dev, "Missing sg entries...\n");
+		return NULL;
+	}
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (unlikely(!desc))
+		return NULL;
+	desc->channel = channel;
+
+	INIT_LIST_HEAD(&desc->sg_entries);
+	desc->entries = 0;
+
+	/* Device single entry */
+	entry_dev = list_first_entry_or_null(&dmadev->free_entries,
+					     struct oxnas_dma_sg_entry, entry);
+	if (!entry_dev) {
+		dev_err(&dmadev->pdev->dev, "Fatal error: Missing dev sg entry...\n");
+		goto entries_cleanup;
+	}
+
+	atomic_dec(&dmadev->free_entries_count);
+	list_move(&entry_dev->entry, &desc->sg_entries);
+	++desc->entries;
+	dev_dbg(&dmadev->pdev->dev, "got entry %p (%08x)\n", entry_dev, entry_dev->this_paddr);
+
+	entry_dev->next_entry = NULL;
+	entry_dev->p_next_entry = 0;
+	entry_dev->data_length = 0; /* Completed by mem sg entries */
+
+	if (dir == DMA_DEV_TO_MEM) {
+		entry_dev->data_addr = channel->cfg.src_addr & OXNAS_DMA_ADR_MASK;
+		desc->sg_info.src_entries = entry_dev;
+		desc->sg_info.p_src_entries = entry_dev->this_paddr;
+
+		dev_dbg(&dmadev->pdev->dev, "src set %p\n", entry_dev);
+	} else if (dir == DMA_MEM_TO_DEV) {
+		entry_dev->data_addr = channel->cfg.dst_addr & OXNAS_DMA_ADR_MASK;
+		desc->sg_info.dst_entries = entry_dev;
+		desc->sg_info.p_dst_entries = entry_dev->this_paddr;
+
+		dev_dbg(&dmadev->pdev->dev, "dst set %p\n", entry_dev);
+	}
+
+	dev_dbg(&dmadev->pdev->dev, "src = %p (%08x) dst = %p (%08x)\n",
+		desc->sg_info.src_entries, desc->sg_info.p_src_entries,
+		desc->sg_info.dst_entries, desc->sg_info.p_dst_entries);
+
+	/* Memory entries */
+	for_each_sg(sgl, sgent, sglen, i) {
+		entry_mem = list_first_entry_or_null(&dmadev->free_entries,
+						     struct oxnas_dma_sg_entry, entry);
+		if (!entry_mem) {
+			dev_err(&dmadev->pdev->dev, "Fatal error: Missing mem sg entries...\n");
+			goto entries_cleanup;
+		}
+
+		atomic_dec(&dmadev->free_entries_count);
+		list_move(&entry_mem->entry, &desc->sg_entries);
+		++desc->entries;
+		dev_dbg(&dmadev->pdev->dev, "got entry %p (%08x)\n",
+			entry_mem, entry_mem->this_paddr);
+
+		/* Fill the linked list */
+		if (prev_entry_mem) {
+			prev_entry_mem->next_entry = entry_mem;
+			prev_entry_mem->p_next_entry = entry_mem->this_paddr;
+		} else {
+			if (dir == DMA_DEV_TO_MEM) {
+				desc->sg_info.dst_entries = entry_mem;
+				desc->sg_info.p_dst_entries = entry_mem->this_paddr;
+				dev_dbg(&dmadev->pdev->dev, "src set %p\n", entry_mem);
+			} else if (dir == DMA_MEM_TO_DEV) {
+				desc->sg_info.src_entries = entry_mem;
+				desc->sg_info.p_src_entries = entry_mem->this_paddr;
+				dev_dbg(&dmadev->pdev->dev, "dst set %p\n", entry_mem);
+			}
+
+			dev_dbg(&dmadev->pdev->dev, "src = %p (%08x) dst = %p (%08x)\n",
+				desc->sg_info.src_entries, desc->sg_info.p_src_entries,
+				desc->sg_info.dst_entries, desc->sg_info.p_dst_entries);
+		}
+		prev_entry_mem = entry_mem;
+
+		/* Fill the entry from the SG */
+		entry_mem->next_entry = NULL;
+		entry_mem->p_next_entry = 0;
+
+		entry_mem->data_addr = sg_dma_address(sgent) & OXNAS_DMA_ADR_MASK;
+		entry_mem->data_length = sg_dma_len(sgent);
+		dev_dbg(&dmadev->pdev->dev, "sg = %08x len = %d\n",
+			sg_dma_address(sgent), sg_dma_len(sgent));
+
+		/* Add to dev sg length */
+		entry_dev->data_length += sg_dma_len(sgent);
+	}
+	dev_dbg(&dmadev->pdev->dev, "allocated %d sg entries\n", desc->entries);
+
+	desc->sg_info.qualifier = FIELD_PREP(OXNAS_DMA_SG_CHANNEL, channel->id) |
+				  OXNAS_DMA_SG_QUALIFIER;
+
+	if (dir == DMA_DEV_TO_MEM)
+		desc->sg_info.qualifier |= FIELD_PREP(OXNAS_DMA_SG_SRC_EOT, 2);
+	else if (dir == DMA_MEM_TO_DEV)
+		desc->sg_info.qualifier |= FIELD_PREP(OXNAS_DMA_SG_DST_EOT, 2);
+
+	desc->sg_info.control = (DMA_CTRL_STATUS_INTERRUPT_ENABLE |
+				 DMA_CTRL_STATUS_FAIR_SHARE_ARB |
+				 DMA_CTRL_STATUS_INTR_CLEAR_ENABLE);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_SRC_DREQ, src_memory);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DEST_DREQ, dst_memory);
+
+	if (dir == DMA_DEV_TO_MEM) {
+		desc->sg_info.control |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+		desc->sg_info.control |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		desc->sg_info.control |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+		desc->sg_info.control |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+	}
+	desc->sg_info.control |= DMA_CTRL_STATUS_TRANSFER_MODE_A;
+	desc->sg_info.control |= DMA_CTRL_STATUS_TRANSFER_MODE_B;
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DIR, OXNAS_DMA_A_TO_B);
+
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_SRC_WIDTH,
+					    OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DEST_WIDTH,
+					    OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->sg_info.control &= ~DMA_CTRL_STATUS_STARVE_LOW_PRIORITY;
+
+	desc->type = OXNAS_DMA_TYPE_SG;
+
+	return vchan_tx_prep(&channel->vc, &desc->vd, flags);
+
+entries_cleanup:
+	/* Put back all entries in the free entries... */
+	list_splice_tail_init(&desc->sg_entries, &dmadev->free_entries);
+	atomic_add(desc->entries, &dmadev->free_entries_count);
+	dev_dbg(&dmadev->pdev->dev, "freed %d sg entries\n", desc->entries);
+
+	kfree(desc);
+
+	return NULL;
+}
+
+/** Allocate descriptors capable of mapping the requested length of memory */
+static struct dma_async_tx_descriptor  *oxnas_dma_prep_dma_memcpy(struct dma_chan *chan,
+								  dma_addr_t dst, dma_addr_t src,
+								  size_t len, unsigned long flags)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct oxnas_dma_desc *desc;
+	int src_memory = OXNAS_DMA_DREQ_MEMORY;
+	int dst_memory = OXNAS_DMA_DREQ_MEMORY;
+
+	if (len > OXNAS_DMA_MAX_TRANSFER_LENGTH)
+		return NULL;
+
+	src_memory = oxnas_check_address(dmadev, src);
+	if (src_memory == -1) {
+		dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n", src);
+		return NULL;
+	}
+
+	dst_memory = oxnas_check_address(dmadev, dst);
+	if (dst_memory == -1) {
+		dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n", src);
+		return NULL;
+	}
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (unlikely(!desc))
+		return NULL;
+	desc->channel = channel;
+
+	dev_dbg(&dmadev->pdev->dev, "preparing memcpy from %08x to %08x, %lubytes (flags %x)\n",
+		src, dst, (unsigned long)len, (unsigned int)flags);
+
+	/* CTRL STATUS Preparation */
+
+	/* Pause while start */
+	desc->ctrl = DMA_CTRL_STATUS_PAUSE;
+
+	/* Interrupts enabled
+	 * High priority
+	 * Use new interrupt clearing register
+	 */
+	desc->ctrl |= DMA_CTRL_STATUS_INTERRUPT_ENABLE |
+		      DMA_CTRL_STATUS_FAIR_SHARE_ARB |
+		      DMA_CTRL_STATUS_INTR_CLEAR_ENABLE;
+
+	/* Type Memory */
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_SRC_DREQ, src_memory);
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DEST_DREQ, dst_memory);
+
+	/* Setup the transfer direction and burst/single mode for the two DMA busses */
+	desc->ctrl |= DMA_CTRL_STATUS_TRANSFER_MODE_A;
+	desc->ctrl |= DMA_CTRL_STATUS_TRANSFER_MODE_B;
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DIR, OXNAS_DMA_A_TO_B);
+
+	/* Incrementing addresses */
+	desc->ctrl |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+	desc->ctrl &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+	desc->ctrl |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+	desc->ctrl &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+
+	/* Set up the width of the transfers on the DMA buses */
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_SRC_WIDTH, OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DEST_WIDTH, OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+
+	/* Setup the priority arbitration scheme */
+	desc->ctrl &= ~DMA_CTRL_STATUS_STARVE_LOW_PRIORITY;
+
+	/* LENGTH and End Of Transfert Preparation */
+	desc->len = len |
+		    DMA_BYTE_CNT_INC4_SET_MASK |    /* Always enable INC4 transfers */
+		    DMA_BYTE_CNT_HPROT_MASK |       /* Always enable HPROT assertion */
+		    DMA_BYTE_CNT_RD_EOT_MASK;       /* EOT at last Read */
+
+	desc->src_adr = src;
+	desc->dst_adr = dst;
+	desc->type = OXNAS_DMA_TYPE_SIMPLE;
+
+	return vchan_tx_prep(&channel->vc, &desc->vd, flags);
+}
+
+static int oxnas_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+
+	memcpy(&channel->cfg, cfg, sizeof(channel->cfg));
+
+	return 0;
+}
+
+static void oxnas_dma_desc_free(struct virt_dma_desc *vd)
+{
+	struct oxnas_dma_desc *desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+	struct oxnas_dma_channel *channel = desc->channel;
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+
+	/* Free SG entries */
+	if (desc->type == OXNAS_DMA_TYPE_SG) {
+		list_splice_tail_init(&desc->sg_entries, &dmadev->free_entries);
+		atomic_add(desc->entries, &dmadev->free_entries_count);
+		dev_dbg(&dmadev->pdev->dev, "freed %d sg entries\n", desc->entries);
+	}
+
+	kfree(container_of(vd, struct oxnas_dma_desc, vd));
+}
+
+/** Poll for the DMA channel's active status. There can be multiple transfers
+ *  queued with the DMA channel identified by cookies, so should be checking
+ *  lists containing all pending transfers and all completed transfers that have
+ *  not yet been polled for completion
+ */
+static enum dma_status oxnas_dma_tx_status(struct dma_chan *chan,
+					   dma_cookie_t cookie,
+					   struct dma_tx_state *txstate)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct virt_dma_desc *vd;
+	enum dma_status ret;
+	unsigned long flags;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+	if (ret == DMA_COMPLETE || !txstate)
+		return ret;
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+	vd = vchan_find_desc(&channel->vc, cookie);
+	if (vd) {
+		struct oxnas_dma_desc *desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+
+		txstate->residue = desc->len & OXNAS_DMA_MAX_TRANSFER_LENGTH;
+	} else {
+		txstate->residue = 0;
+	}
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+
+	return ret;
+}
+
+/** To push outstanding transfers to h/w. This should use the list of pending
+ *  transfers identified by cookies to select the next transfer and pass this to
+ *  the hardware
+ */
+static void oxnas_dma_issue_pending(struct dma_chan *chan)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+	if (vchan_issue_pending(&channel->vc) && !channel->cur) {
+		spin_lock(&dmadev->lock);
+
+		if (list_empty(&channel->node))
+			list_add_tail(&channel->node, &dmadev->pending);
+
+		spin_unlock(&dmadev->lock);
+
+		tasklet_schedule(&dmadev->tasklet);
+	}
+
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+}
+
+static void oxnas_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+
+	vchan_free_chan_resources(&channel->vc);
+}
+
+static int oxnas_dma_probe(struct platform_device *pdev)
+{
+	struct oxnas_dma_device *dmadev;
+	struct resource *res;
+	int hwid, i, ret;
+
+	dmadev = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_dma_device), GFP_KERNEL);
+	if (!dmadev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dmadev->dma_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dmadev->dma_base))
+		return PTR_ERR(dmadev->dma_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	dmadev->sgdma_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dmadev->sgdma_base))
+		return PTR_ERR(dmadev->sgdma_base);
+
+	dmadev->dma_rst = devm_reset_control_get(&pdev->dev, "dma");
+	if (IS_ERR(dmadev->dma_rst))
+		return PTR_ERR(dmadev->dma_rst);
+
+	dmadev->sgdma_rst = devm_reset_control_get(&pdev->dev, "sgdma");
+	if (IS_ERR(dmadev->sgdma_rst))
+		return PTR_ERR(dmadev->sgdma_rst);
+
+	dmadev->dma_clk = devm_clk_get(&pdev->dev, 0);
+	if (IS_ERR(dmadev->dma_clk))
+		return PTR_ERR(dmadev->dma_clk);
+
+	ret = of_property_count_elems_of_size(pdev->dev.of_node, "oxsemi,targets-types", 4);
+	if (ret <= 0 || (ret % 3) != 0) {
+		dev_err(&pdev->dev, "malformed or missing oxsemi,targets-types\n");
+		return -EINVAL;
+	}
+
+	dmadev->authorized_types_count = ret / 3;
+	dmadev->authorized_types = devm_kzalloc(&pdev->dev,
+		sizeof(*dmadev->authorized_types) * dmadev->authorized_types_count, GFP_KERNEL);
+
+	if (!dmadev->authorized_types)
+		return -ENOMEM;
+
+	for (i = 0 ; i < dmadev->authorized_types_count ; ++i) {
+		u32 value;
+
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3), &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].start = value;
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3) + 1, &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].end = value;
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3) + 2, &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].type = value;
+	}
+
+	dev_dbg(&pdev->dev, "Authorized memory ranges :\n");
+	dev_dbg(&pdev->dev, " Start    - End      = Type\n");
+	for (i = 0 ; i <  dmadev->authorized_types_count ; ++i)
+		dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
+			dmadev->authorized_types[i].start,
+			dmadev->authorized_types[i].end,
+			dmadev->authorized_types[i].type);
+
+	dmadev->pdev = pdev;
+
+	spin_lock_init(&dmadev->lock);
+
+	tasklet_init(&dmadev->tasklet, oxnas_dma_sched, (unsigned long)dmadev);
+	INIT_LIST_HEAD(&dmadev->common.channels);
+	INIT_LIST_HEAD(&dmadev->pending);
+	INIT_LIST_HEAD(&dmadev->free_entries);
+
+	/* Enable HW & Clocks */
+	reset_control_reset(dmadev->dma_rst);
+	reset_control_reset(dmadev->sgdma_rst);
+	clk_prepare_enable(dmadev->dma_clk);
+
+	/* Discover the number of channels available */
+	hwid = readl(dmadev->dma_base + DMA_CALC_REG_ADR(0, DMA_INTR_ID));
+	dmadev->channels_count = DMA_INTR_ID_GET_NUM_CHANNELS(hwid);
+	dmadev->hwversion = DMA_INTR_ID_GET_VERSION(hwid);
+
+	dev_dbg(&pdev->dev, "OXNAS DMA v%x with %d channels\n",
+		dmadev->hwversion, dmadev->channels_count);
+
+	/* Limit channels count */
+	if (dmadev->channels_count > MAX_OXNAS_DMA_CHANNELS)
+		dmadev->channels_count = MAX_OXNAS_DMA_CHANNELS;
+
+	/* Allocate coherent memory for sg descriptors */
+	dmadev->sg_data = dma_alloc_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+					     &dmadev->p_sg_data, GFP_KERNEL);
+	if (!dmadev->sg_data) {
+		ret = -ENOMEM;
+		goto disable_clks;
+	}
+
+	/* Reset SG descritors */
+	memset(dmadev->sg_data, 0, sizeof(struct oxnas_dma_sg_data));
+	atomic_set(&dmadev->free_entries_count, 0);
+
+	/* Initialize and add all sg entries to the free list */
+	for (i = 0 ; i < MAX_OXNAS_SG_ENTRIES ; ++i) {
+		dmadev->sg_data->entries[i].this_paddr =
+			(dma_addr_t)&(((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->entries[i]);
+		INIT_LIST_HEAD(&dmadev->sg_data->entries[i].entry);
+		list_add_tail(&dmadev->sg_data->entries[i].entry,
+			      &dmadev->free_entries);
+		atomic_inc(&dmadev->free_entries_count);
+	}
+
+	/* Init all channels */
+	for (i = 0 ; i < dmadev->channels_count ; ++i) {
+		struct oxnas_dma_channel *ch = &dmadev->channels[i];
+
+		ch->dmadev = dmadev;
+		ch->id = i;
+
+		ch->irq = irq_of_parse_and_map(pdev->dev.of_node, i);
+		if (ch->irq <= 0) {
+			dev_err(&pdev->dev, "invalid irq%d from platform\n", i);
+			goto free_coherent;
+		}
+
+		ret = devm_request_irq(&pdev->dev, ch->irq,
+				       oxnas_dma_interrupt, 0,
+				       "DMA", ch);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to request irq%d\n", i);
+			goto free_coherent;
+		}
+
+		ch->p_sg_info =
+			(dma_addr_t)&((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->infos[i];
+		ch->sg_info = &dmadev->sg_data->infos[i];
+		memset(ch->sg_info, 0, sizeof(struct oxnas_dma_sg_info));
+
+		atomic_set(&ch->active, 0);
+
+		ch->vc.desc_free = oxnas_dma_desc_free;
+		vchan_init(&ch->vc, &dmadev->common);
+		INIT_LIST_HEAD(&ch->node);
+	}
+
+	platform_set_drvdata(pdev, dmadev);
+
+	dma_cap_set(DMA_MEMCPY, dmadev->common.cap_mask);
+	dmadev->common.chancnt = dmadev->channels_count;
+	dmadev->common.device_free_chan_resources = oxnas_dma_free_chan_resources;
+	dmadev->common.device_tx_status = oxnas_dma_tx_status;
+	dmadev->common.device_issue_pending = oxnas_dma_issue_pending;
+	dmadev->common.device_prep_dma_memcpy = oxnas_dma_prep_dma_memcpy;
+	dmadev->common.device_prep_slave_sg = oxnas_dma_prep_slave_sg;
+	dmadev->common.device_config = oxnas_dma_slave_config;
+	dmadev->common.copy_align = DMAENGINE_ALIGN_4_BYTES;
+	dmadev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dmadev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dmadev->common.directions = BIT(DMA_MEM_TO_MEM);
+	dmadev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+	dmadev->common.dev = &pdev->dev;
+
+	ret = dma_async_device_register(&dmadev->common);
+	if (ret)
+		goto free_coherent;
+
+	ret = of_dma_controller_register(pdev->dev.of_node,
+					 of_dma_xlate_by_chan_id,
+					 &dmadev->common);
+	if (ret) {
+		dev_warn(&pdev->dev, "Failed to register DMA Controller\n");
+		goto dma_unregister;
+	}
+
+	dev_info(&pdev->dev, "OXNAS DMA Registered\n");
+
+	return 0;
+
+dma_unregister:
+	dma_async_device_unregister(&dmadev->common);
+
+free_coherent:
+	dma_free_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+			  dmadev->sg_data, dmadev->p_sg_data);
+
+disable_clks:
+	clk_disable_unprepare(dmadev->dma_clk);
+
+	return ret;
+}
+
+static int oxnas_dma_remove(struct platform_device *pdev)
+{
+	struct oxnas_dma_device *dmadev = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&dmadev->common);
+
+	dma_free_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+			  dmadev->sg_data, dmadev->p_sg_data);
+
+	clk_disable_unprepare(dmadev->dma_clk);
+
+	return 0;
+}
+
+static const struct of_device_id oxnas_dma_of_dev_id[] = {
+	{ .compatible = "oxsemi,ox810se-dma", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, oxnas_dma_of_dev_id);
+
+static struct platform_driver oxnas_dma_driver = {
+	.probe		= oxnas_dma_probe,
+	.remove		= oxnas_dma_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "oxnas-dma",
+		.of_match_table = oxnas_dma_of_dev_id,
+	},
+};
+
+static int __init oxnas_dma_init_module(void)
+{
+	return platform_driver_register(&oxnas_dma_driver);
+}
+subsys_initcall(oxnas_dma_init_module);
+
+static void __exit oxnas_dma_exit_module(void)
+{
+	platform_driver_unregister(&oxnas_dma_driver);
+}
+module_exit(oxnas_dma_exit_module);
+
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 2/4] dmaengine: Add Oxford Semiconductor OXNAS DMA Controller
@ 2022-01-04 14:52   ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

The Oxford Semiconductor OX810SE contains a DMA engine mainly used
for the SATA controller which doesn't have an embedded DMA engine.

This DMA engine support single and scatter-gather memory-to-memory
transfers and device-to-memory/memory-to-device transfers.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 drivers/dma/Kconfig      |    8 +
 drivers/dma/Makefile     |    1 +
 drivers/dma/oxnas_adma.c | 1045 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 1054 insertions(+)
 create mode 100644 drivers/dma/oxnas_adma.c

diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index 6bcdb4e6a0d1..8925ffd85ac2 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -505,6 +505,14 @@ config OWL_DMA
 	help
 	  Enable support for the Actions Semi Owl SoCs DMA controller.
 
+config OXNAS_DMA
+	bool "Oxford Semiconductor OXNAS SoC Family DMA support"
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	depends on ARCH_OXNAS || COMPILE_TEST
+	help
+	  Support for Oxford Semiconductor OXNAS SoC Family DMA engine
+
 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 616d926cf2a5..ba1111785bb8 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -58,6 +58,7 @@ 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_OXNAS_DMA) += oxnas_adma.o
 obj-$(CONFIG_PCH_DMA) += pch_dma.o
 obj-$(CONFIG_PL330_DMA) += pl330.o
 obj-$(CONFIG_PLX_DMA) += plx_dma.o
diff --git a/drivers/dma/oxnas_adma.c b/drivers/dma/oxnas_adma.c
new file mode 100644
index 000000000000..586c23187de1
--- /dev/null
+++ b/drivers/dma/oxnas_adma.c
@@ -0,0 +1,1045 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (C) 2016 Neil Armstrong <narmstrong@baylibre.com>
+ * Copyright (C) 2008 Oxford Semiconductor Ltd
+ */
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/mutex.h>
+#include <linux/semaphore.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/memory.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/of_dma.h>
+#include <linux/reset.h>
+#include <linux/clk.h>
+#include <linux/platform_device.h>
+#include <linux/bitfield.h>
+#include <linux/dma-mapping.h>
+
+#include "dmaengine.h"
+#include "virt-dma.h"
+
+/* Normal (non-SG) registers */
+#define DMA_REGS_PER_CHANNEL 8
+
+#define DMA_CTRL_STATUS      0x00
+#define DMA_BASE_SRC_ADR     0x04
+#define DMA_BASE_DST_ADR     0x08
+#define DMA_BYTE_CNT         0x0c
+#define DMA_CURRENT_SRC_ADR  0x10
+#define DMA_CURRENT_DST_ADR  0x14
+#define DMA_CURRENT_BYTE_CNT 0x18
+#define DMA_INTR_ID          0x1c
+#define DMA_INTR_CLEAR_REG   (DMA_CURRENT_SRC_ADR)
+
+/* 8 quad-sized registers per channel arranged contiguously */
+#define DMA_CALC_REG_ADR(channel, register) (((channel) << 5) + (register))
+
+#define DMA_CTRL_STATUS_FAIR_SHARE_ARB            BIT(0)
+#define DMA_CTRL_STATUS_IN_PROGRESS               BIT(1)
+#define DMA_CTRL_STATUS_SRC_DREQ                  GENMASK(5, 2)
+#define DMA_CTRL_STATUS_DEST_DREQ                 GENMASK(10, 7)
+#define DMA_CTRL_STATUS_INTR                      BIT(10)
+#define DMA_CTRL_STATUS_NXT_FREE                  BIT(11)
+#define DMA_CTRL_STATUS_RESET                     BIT(12)
+#define DMA_CTRL_STATUS_DIR                       GENMASK(14, 13)
+#define DMA_CTRL_STATUS_SRC_ADR_MODE              BIT(15)
+#define DMA_CTRL_STATUS_DEST_ADR_MODE             BIT(16)
+#define DMA_CTRL_STATUS_TRANSFER_MODE_A           BIT(17)
+#define DMA_CTRL_STATUS_TRANSFER_MODE_B           BIT(18)
+#define DMA_CTRL_STATUS_SRC_WIDTH                 GENMASK(21, 19)
+#define DMA_CTRL_STATUS_DEST_WIDTH                GENMASK(24, 22)
+#define DMA_CTRL_STATUS_PAUSE                     BIT(25)
+#define DMA_CTRL_STATUS_INTERRUPT_ENABLE          BIT(26)
+#define DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED      BIT(27)
+#define DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED BIT(28)
+#define DMA_CTRL_STATUS_STARVE_LOW_PRIORITY       BIT(29)
+#define DMA_CTRL_STATUS_INTR_CLEAR_ENABLE         BIT(30)
+
+#define DMA_BYTE_CNT_MASK                         GENMASK(20, 0)
+#define DMA_BYTE_CNT_INC4_SET_MASK                BIT(28)
+#define DMA_BYTE_CNT_HPROT_MASK                   BIT(29)
+#define DMA_BYTE_CNT_WR_EOT_MASK                  BIT(30)
+#define DMA_BYTE_CNT_RD_EOT_MASK                  BIT(31)
+
+#define DMA_INTR_ID_GET_NUM_CHANNELS(reg_contents) (((reg_contents) >> 16) & 0xFF)
+#define DMA_INTR_ID_GET_VERSION(reg_contents)      (((reg_contents) >> 24) & 0xFF)
+#define DMA_INTR_ID_INT_MASK        GENMASK(MAX_OXNAS_DMA_CHANNELS, 0)
+
+#define DMA_HAS_V4_INTR_CLEAR(version) ((version) > 3)
+
+/* H/W scatter gather controller registers */
+#define OXNAS_DMA_NUM_SG_REGS 4
+
+#define DMA_SG_CONTROL  0x00
+#define DMA_SG_STATUS   0x04
+#define DMA_SG_REQ_PTR  0x08
+#define DMA_SG_RESETS   0x0c
+
+#define DMA_SG_CALC_REG_ADR(channel, register) (((channel) << 4) + (register))
+
+/* SG DMA controller control register field definitions */
+#define DMA_SG_CONTROL_START		BIT(0)
+#define DMA_SG_CONTROL_QUEUING_ENABLE	BIT(1)
+#define DMA_SG_CONTROL_HBURST_ENABLE	BIT(2)
+
+/* SG DMA controller status register field definitions */
+#define DMA_SG_STATUS_ERROR_CODE	GENMASK(5, 0)
+#define DMA_SG_STATUS_BUSY		BIT(7)
+
+/* SG DMA controller sub-block resets register field definitions */
+#define DMA_SG_RESETS_CONTROL		BIT(0)
+#define DMA_SG_RESETS_ARBITER		BIT(1)
+#define DMA_SG_RESETS_AHB		BIT(2)
+
+/* SG DMA controller qualifier field definitions */
+#define OXNAS_DMA_SG_QUALIFIER		GENMASK(15, 0)
+#define OXNAS_DMA_SG_DST_EOT		GENMASK(17, 16)
+#define OXNAS_DMA_SG_SRC_EOT		GENMASK(22, 20)
+#define OXNAS_DMA_SG_CHANNEL		GENMASK(31, 24)
+
+#define OXNAS_DMA_ADR_MASK		GENMASK(29, 0)
+#define OXNAS_DMA_MAX_TRANSFER_LENGTH	DMA_BYTE_CNT_MASK
+
+/* The available buses to which the DMA controller is attached */
+enum {
+	OXNAS_DMA_SIDE_A = 0,
+	OXNAS_DMA_SIDE_B
+};
+
+/* Direction of data flow between the DMA controller's pair of interfaces */
+enum {
+	OXNAS_DMA_A_TO_A = 0,
+	OXNAS_DMA_B_TO_A,
+	OXNAS_DMA_A_TO_B,
+	OXNAS_DMA_B_TO_B
+};
+
+/* The available data widths */
+enum {
+	OXNAS_DMA_TRANSFER_WIDTH_8BITS = 0,
+	OXNAS_DMA_TRANSFER_WIDTH_16BITS,
+	OXNAS_DMA_TRANSFER_WIDTH_32BITS
+};
+
+/* The mode of the DMA transfer */
+enum {
+	OXNAS_DMA_TRANSFER_MODE_SINGLE = 0,
+	OXNAS_DMA_TRANSFER_MODE_BURST
+};
+
+/* The available transfer targets */
+enum {
+	OXNAS_DMA_DREQ_PATA     = 0,
+	OXNAS_DMA_DREQ_SATA     = 0,
+	OXNAS_DMA_DREQ_DPE_RX   = 1,
+	OXNAS_DMA_DREQ_DPE_TX   = 2,
+	OXNAS_DMA_DREQ_AUDIO_TX = 5,
+	OXNAS_DMA_DREQ_AUDIO_RX = 6,
+	OXNAS_DMA_DREQ_MEMORY   = 15
+};
+
+enum {
+	OXNAS_DMA_TYPE_SIMPLE = 0,
+	OXNAS_DMA_TYPE_SG,
+};
+
+#define MAX_OXNAS_DMA_CHANNELS	5
+#define MAX_OXNAS_SG_ENTRIES	512
+
+/* Will be exchanged with SG DMA controller */
+struct oxnas_dma_sg_entry {
+	dma_addr_t                 data_addr;   /* physical address of the buffer */
+	unsigned long              data_length; /* length of the buffer */
+	dma_addr_t                 p_next_entry; /* physical address of next descriptor */
+	struct oxnas_dma_sg_entry *next_entry;   /* virtual address of the next descriptor */
+	dma_addr_t                 this_paddr;  /* physical address of this descriptor */
+	struct list_head	   entry;  /* Linked list entry */
+} __attribute((aligned(4), packed));
+
+/* Will be exchanged with SG DMA controller */
+struct oxnas_dma_sg_info {
+	unsigned long         qualifier;
+	unsigned long         control;
+	dma_addr_t            p_src_entries; /* physical address of the first source SG desc */
+	dma_addr_t            p_dst_entries; /* physical address of the first dest SG desc */
+	struct oxnas_dma_sg_entry *src_entries; /* virtual address of the first source SG desc */
+	struct oxnas_dma_sg_entry *dst_entries; /* virtual address of the first dest SG desc */
+} __attribute((aligned(4), packed));
+
+struct oxnas_dma_sg_data {
+	struct oxnas_dma_sg_entry entries[MAX_OXNAS_SG_ENTRIES];
+	struct oxnas_dma_sg_info infos[MAX_OXNAS_DMA_CHANNELS];
+} __attribute((aligned(4)));
+
+struct oxnas_dma_device;
+struct oxnas_dma_channel;
+
+struct oxnas_dma_desc {
+	struct virt_dma_desc vd;
+	struct oxnas_dma_channel *channel;
+	unsigned long ctrl;
+	unsigned long len;
+	dma_addr_t src_adr;
+	dma_addr_t dst_adr;
+	unsigned int type;
+	struct oxnas_dma_sg_info sg_info;
+	unsigned int entries;
+	struct list_head sg_entries;
+};
+
+struct oxnas_dma_channel {
+	struct virt_dma_chan vc;
+	struct list_head node;
+	struct oxnas_dma_device	*dmadev;
+	unsigned int id;
+	unsigned int irq;
+
+	struct dma_slave_config	cfg;
+
+	dma_addr_t p_sg_info; /* physical address of the array of sg_info structs */
+	struct oxnas_dma_sg_info *sg_info; /* virtual address of the array of sg_info structs */
+
+	atomic_t active;
+
+	struct oxnas_dma_desc *cur;
+};
+
+struct oxnas_dma_device {
+	struct platform_device *pdev;
+	struct dma_device common;
+	void __iomem *dma_base;
+	void __iomem *sgdma_base;
+	struct reset_control *dma_rst;
+	struct reset_control *sgdma_rst;
+	struct clk *dma_clk;
+
+	unsigned int channels_count;
+
+	struct oxnas_dma_channel channels[MAX_OXNAS_DMA_CHANNELS];
+
+	unsigned int hwversion;
+
+	/* Protects concurrent access to channels */
+	spinlock_t lock;
+	struct tasklet_struct tasklet;
+
+	struct list_head pending;
+
+	struct {
+		dma_addr_t start;
+		dma_addr_t end;
+		unsigned int type;
+	} *authorized_types;
+	unsigned int authorized_types_count;
+
+	struct list_head free_entries;
+	atomic_t free_entries_count;
+	dma_addr_t p_sg_data;
+	struct oxnas_dma_sg_data *sg_data;
+};
+
+static void oxnas_dma_start_next(struct oxnas_dma_channel *channel);
+
+static irqreturn_t oxnas_dma_interrupt(int irq, void *dev_id)
+{
+	struct oxnas_dma_channel *channel = dev_id;
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	unsigned long error_code;
+	unsigned long flags;
+
+	dev_vdbg(&dmadev->pdev->dev, "irq for channel %d\n", channel->id);
+
+	while (readl(dmadev->dma_base + DMA_CALC_REG_ADR(0, DMA_INTR_ID)) & BIT(channel->id)) {
+		dev_dbg(&dmadev->pdev->dev, "Acking interrupt for channel %u\n",
+			channel->id);
+
+		/* Write to the interrupt clear register to clear interrupt */
+		writel(0, dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_INTR_CLEAR_REG));
+	}
+
+	if (channel->cur && channel->cur->type == OXNAS_DMA_TYPE_SG) {
+		error_code = readl(dmadev->sgdma_base +
+				DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_STATUS));
+		error_code &= DMA_SG_STATUS_ERROR_CODE;
+
+		/* TOFIX report it to the core */
+		if (error_code)
+			dev_err(&dmadev->pdev->dev, "ch%d: sgdma err %x\n",
+				channel->id, (unsigned int)error_code);
+
+		writel(1, dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_STATUS));
+	}
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+
+	if (atomic_read(&channel->active)) {
+		struct oxnas_dma_desc *cur = channel->cur;
+
+		oxnas_dma_start_next(channel);
+		if (cur)
+			vchan_cookie_complete(&cur->vd);
+	} else {
+		dev_warn(&dmadev->pdev->dev, "spurious irq for channel %d\n",
+			 channel->id);
+	}
+
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+
+	return IRQ_HANDLED;
+}
+
+static void oxnas_dma_start_next(struct oxnas_dma_channel *channel)
+{
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct virt_dma_desc *vd = vchan_next_desc(&channel->vc);
+	struct oxnas_dma_desc *desc;
+	unsigned long ctrl_status;
+
+	if (!vd) {
+		channel->cur = NULL;
+		return;
+	}
+
+	list_del(&vd->node);
+
+	desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+	channel->cur = desc;
+
+	if (desc->type == OXNAS_DMA_TYPE_SIMPLE) {
+		/* Write the control/status value to the DMAC */
+		writel(desc->ctrl,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+
+		/* Ensure control/status word makes it to the DMAC before
+		 * we write address/length info
+		 */
+		wmb();
+
+		/* Write the source addresses to the DMAC */
+		writel(desc->src_adr & OXNAS_DMA_ADR_MASK,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BASE_SRC_ADR));
+
+		/* Write the destination addresses to the DMAC */
+		writel(desc->dst_adr & OXNAS_DMA_ADR_MASK,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BASE_DST_ADR));
+
+		/* Write the length, with EOT configuration
+		 * for the single transfer
+		 */
+		writel(desc->len,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_BYTE_CNT));
+
+		/* Ensure adr/len info makes it to DMAC before later modifications to
+		 * control/status register due to starting the transfer, which happens in
+		 * oxnas_dma_start()
+		 */
+		wmb();
+
+		/* Setup channel data */
+		atomic_set(&channel->active, 1);
+
+		/* Single transfer mode, so unpause the DMA controller channel */
+		ctrl_status = readl(dmadev->dma_base +
+				    DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+		writel(ctrl_status & ~DMA_CTRL_STATUS_PAUSE,
+		       dmadev->dma_base + DMA_CALC_REG_ADR(channel->id, DMA_CTRL_STATUS));
+
+		dev_dbg(&dmadev->pdev->dev, "ch%d: started req %d from %08x to %08x, %lubytes\n",
+			channel->id, vd->tx.cookie,
+			desc->src_adr, desc->dst_adr,
+			desc->len & OXNAS_DMA_MAX_TRANSFER_LENGTH);
+	} else if (desc->type == OXNAS_DMA_TYPE_SG) {
+		/* Write to the SG-DMA channel's reset register to reset the control
+		 * in case the previous SG-DMA transfer failed in some way, thus
+		 * leaving the SG-DMA controller hung up part way through processing
+		 * its SG list. The reset bits are self-clearing
+		 */
+		writel(DMA_SG_RESETS_CONTROL,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_RESETS));
+
+		/* Copy the sg_info structure */
+		memcpy(channel->sg_info, &desc->sg_info, sizeof(struct oxnas_dma_sg_info));
+
+		/* Ensure adr/len info makes it to DMAC before later modifications to
+		 * control/status register due to starting the transfer, which happens in
+		 * oxnas_dma_start()
+		 */
+		wmb();
+
+		/* Write the pointer to the SG info struct into the Request Pointer reg */
+		writel(channel->p_sg_info,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_REQ_PTR));
+
+		/* Setup channel data */
+		atomic_set(&channel->active, 1);
+
+		/* Start the transfert */
+		writel(DMA_SG_CONTROL_START |
+		       DMA_SG_CONTROL_QUEUING_ENABLE |
+		       DMA_SG_CONTROL_HBURST_ENABLE,
+		       dmadev->sgdma_base + DMA_SG_CALC_REG_ADR(channel->id, DMA_SG_CONTROL));
+
+		dev_dbg(&dmadev->pdev->dev, "ch%d: started %d sg req with %d entries\n",
+			channel->id, vd->tx.cookie,
+			desc->entries);
+	}
+}
+
+static void oxnas_dma_sched(unsigned long data)
+{
+	struct oxnas_dma_device *dmadev = (struct oxnas_dma_device *)data;
+	LIST_HEAD(head);
+
+	spin_lock_irq(&dmadev->lock);
+	list_splice_tail_init(&dmadev->pending, &head);
+	spin_unlock_irq(&dmadev->lock);
+
+	while (!list_empty(&head)) {
+		struct oxnas_dma_channel *ch = list_first_entry(&head,
+								struct oxnas_dma_channel, node);
+
+		spin_lock_irq(&ch->vc.lock);
+
+		list_del_init(&ch->node);
+		oxnas_dma_start_next(ch);
+
+		spin_unlock_irq(&ch->vc.lock);
+	}
+}
+
+static int oxnas_check_address(struct oxnas_dma_device *dmadev, dma_addr_t address)
+{
+	int i;
+
+	for (i = 0 ; i <  dmadev->authorized_types_count ; ++i) {
+		if (address >= dmadev->authorized_types[i].start &&
+		    address < dmadev->authorized_types[i].end)
+			return dmadev->authorized_types[i].type;
+	}
+
+	return -1;
+}
+
+static struct dma_async_tx_descriptor *oxnas_dma_prep_slave_sg(struct dma_chan *chan,
+							       struct scatterlist *sgl,
+							       unsigned int sglen,
+							       enum dma_transfer_direction dir,
+							       unsigned long flags, void *context)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct oxnas_dma_desc *desc;
+	struct scatterlist *sgent;
+	struct oxnas_dma_sg_entry *entry_mem = NULL, *prev_entry_mem = NULL;
+	struct oxnas_dma_sg_entry *entry_dev = NULL;
+	unsigned int i;
+	int src_memory = OXNAS_DMA_DREQ_MEMORY;
+	int dst_memory = OXNAS_DMA_DREQ_MEMORY;
+
+	if (dir == DMA_DEV_TO_MEM) {
+		src_memory = oxnas_check_address(dmadev, channel->cfg.src_addr);
+		if (src_memory == -1) {
+			dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n",
+				channel->cfg.src_addr);
+			return NULL;
+		}
+
+		if (src_memory == OXNAS_DMA_DREQ_MEMORY) {
+			dev_err(&dmadev->pdev->dev, "In DEV_TO_MEM, src cannot be memory\n");
+			return NULL;
+		}
+	} else if (dir == DMA_MEM_TO_DEV) {
+		dst_memory = oxnas_check_address(dmadev, channel->cfg.dst_addr);
+		if (dst_memory == -1) {
+			dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n",
+				channel->cfg.dst_addr);
+			return NULL;
+		}
+
+		if (dst_memory == OXNAS_DMA_DREQ_MEMORY) {
+			dev_err(&dmadev->pdev->dev, "In MEM_TO_DEV, dst cannot be memory\n");
+			return NULL;
+		}
+	} else {
+		dev_err(&dmadev->pdev->dev, "invalid direction\n");
+		return NULL;
+	}
+
+	if (atomic_read(&dmadev->free_entries_count) < (sglen + 1)) {
+		dev_err(&dmadev->pdev->dev, "Missing sg entries...\n");
+		return NULL;
+	}
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (unlikely(!desc))
+		return NULL;
+	desc->channel = channel;
+
+	INIT_LIST_HEAD(&desc->sg_entries);
+	desc->entries = 0;
+
+	/* Device single entry */
+	entry_dev = list_first_entry_or_null(&dmadev->free_entries,
+					     struct oxnas_dma_sg_entry, entry);
+	if (!entry_dev) {
+		dev_err(&dmadev->pdev->dev, "Fatal error: Missing dev sg entry...\n");
+		goto entries_cleanup;
+	}
+
+	atomic_dec(&dmadev->free_entries_count);
+	list_move(&entry_dev->entry, &desc->sg_entries);
+	++desc->entries;
+	dev_dbg(&dmadev->pdev->dev, "got entry %p (%08x)\n", entry_dev, entry_dev->this_paddr);
+
+	entry_dev->next_entry = NULL;
+	entry_dev->p_next_entry = 0;
+	entry_dev->data_length = 0; /* Completed by mem sg entries */
+
+	if (dir == DMA_DEV_TO_MEM) {
+		entry_dev->data_addr = channel->cfg.src_addr & OXNAS_DMA_ADR_MASK;
+		desc->sg_info.src_entries = entry_dev;
+		desc->sg_info.p_src_entries = entry_dev->this_paddr;
+
+		dev_dbg(&dmadev->pdev->dev, "src set %p\n", entry_dev);
+	} else if (dir == DMA_MEM_TO_DEV) {
+		entry_dev->data_addr = channel->cfg.dst_addr & OXNAS_DMA_ADR_MASK;
+		desc->sg_info.dst_entries = entry_dev;
+		desc->sg_info.p_dst_entries = entry_dev->this_paddr;
+
+		dev_dbg(&dmadev->pdev->dev, "dst set %p\n", entry_dev);
+	}
+
+	dev_dbg(&dmadev->pdev->dev, "src = %p (%08x) dst = %p (%08x)\n",
+		desc->sg_info.src_entries, desc->sg_info.p_src_entries,
+		desc->sg_info.dst_entries, desc->sg_info.p_dst_entries);
+
+	/* Memory entries */
+	for_each_sg(sgl, sgent, sglen, i) {
+		entry_mem = list_first_entry_or_null(&dmadev->free_entries,
+						     struct oxnas_dma_sg_entry, entry);
+		if (!entry_mem) {
+			dev_err(&dmadev->pdev->dev, "Fatal error: Missing mem sg entries...\n");
+			goto entries_cleanup;
+		}
+
+		atomic_dec(&dmadev->free_entries_count);
+		list_move(&entry_mem->entry, &desc->sg_entries);
+		++desc->entries;
+		dev_dbg(&dmadev->pdev->dev, "got entry %p (%08x)\n",
+			entry_mem, entry_mem->this_paddr);
+
+		/* Fill the linked list */
+		if (prev_entry_mem) {
+			prev_entry_mem->next_entry = entry_mem;
+			prev_entry_mem->p_next_entry = entry_mem->this_paddr;
+		} else {
+			if (dir == DMA_DEV_TO_MEM) {
+				desc->sg_info.dst_entries = entry_mem;
+				desc->sg_info.p_dst_entries = entry_mem->this_paddr;
+				dev_dbg(&dmadev->pdev->dev, "src set %p\n", entry_mem);
+			} else if (dir == DMA_MEM_TO_DEV) {
+				desc->sg_info.src_entries = entry_mem;
+				desc->sg_info.p_src_entries = entry_mem->this_paddr;
+				dev_dbg(&dmadev->pdev->dev, "dst set %p\n", entry_mem);
+			}
+
+			dev_dbg(&dmadev->pdev->dev, "src = %p (%08x) dst = %p (%08x)\n",
+				desc->sg_info.src_entries, desc->sg_info.p_src_entries,
+				desc->sg_info.dst_entries, desc->sg_info.p_dst_entries);
+		}
+		prev_entry_mem = entry_mem;
+
+		/* Fill the entry from the SG */
+		entry_mem->next_entry = NULL;
+		entry_mem->p_next_entry = 0;
+
+		entry_mem->data_addr = sg_dma_address(sgent) & OXNAS_DMA_ADR_MASK;
+		entry_mem->data_length = sg_dma_len(sgent);
+		dev_dbg(&dmadev->pdev->dev, "sg = %08x len = %d\n",
+			sg_dma_address(sgent), sg_dma_len(sgent));
+
+		/* Add to dev sg length */
+		entry_dev->data_length += sg_dma_len(sgent);
+	}
+	dev_dbg(&dmadev->pdev->dev, "allocated %d sg entries\n", desc->entries);
+
+	desc->sg_info.qualifier = FIELD_PREP(OXNAS_DMA_SG_CHANNEL, channel->id) |
+				  OXNAS_DMA_SG_QUALIFIER;
+
+	if (dir == DMA_DEV_TO_MEM)
+		desc->sg_info.qualifier |= FIELD_PREP(OXNAS_DMA_SG_SRC_EOT, 2);
+	else if (dir == DMA_MEM_TO_DEV)
+		desc->sg_info.qualifier |= FIELD_PREP(OXNAS_DMA_SG_DST_EOT, 2);
+
+	desc->sg_info.control = (DMA_CTRL_STATUS_INTERRUPT_ENABLE |
+				 DMA_CTRL_STATUS_FAIR_SHARE_ARB |
+				 DMA_CTRL_STATUS_INTR_CLEAR_ENABLE);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_SRC_DREQ, src_memory);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DEST_DREQ, dst_memory);
+
+	if (dir == DMA_DEV_TO_MEM) {
+		desc->sg_info.control |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+		desc->sg_info.control |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+	} else if (dir == DMA_MEM_TO_DEV) {
+		desc->sg_info.control |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+		desc->sg_info.control |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+		desc->sg_info.control &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+	}
+	desc->sg_info.control |= DMA_CTRL_STATUS_TRANSFER_MODE_A;
+	desc->sg_info.control |= DMA_CTRL_STATUS_TRANSFER_MODE_B;
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DIR, OXNAS_DMA_A_TO_B);
+
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_SRC_WIDTH,
+					    OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->sg_info.control |= FIELD_PREP(DMA_CTRL_STATUS_DEST_WIDTH,
+					    OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->sg_info.control &= ~DMA_CTRL_STATUS_STARVE_LOW_PRIORITY;
+
+	desc->type = OXNAS_DMA_TYPE_SG;
+
+	return vchan_tx_prep(&channel->vc, &desc->vd, flags);
+
+entries_cleanup:
+	/* Put back all entries in the free entries... */
+	list_splice_tail_init(&desc->sg_entries, &dmadev->free_entries);
+	atomic_add(desc->entries, &dmadev->free_entries_count);
+	dev_dbg(&dmadev->pdev->dev, "freed %d sg entries\n", desc->entries);
+
+	kfree(desc);
+
+	return NULL;
+}
+
+/** Allocate descriptors capable of mapping the requested length of memory */
+static struct dma_async_tx_descriptor  *oxnas_dma_prep_dma_memcpy(struct dma_chan *chan,
+								  dma_addr_t dst, dma_addr_t src,
+								  size_t len, unsigned long flags)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	struct oxnas_dma_desc *desc;
+	int src_memory = OXNAS_DMA_DREQ_MEMORY;
+	int dst_memory = OXNAS_DMA_DREQ_MEMORY;
+
+	if (len > OXNAS_DMA_MAX_TRANSFER_LENGTH)
+		return NULL;
+
+	src_memory = oxnas_check_address(dmadev, src);
+	if (src_memory == -1) {
+		dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n", src);
+		return NULL;
+	}
+
+	dst_memory = oxnas_check_address(dmadev, dst);
+	if (dst_memory == -1) {
+		dev_err(&dmadev->pdev->dev, "invalid memory address %08x\n", src);
+		return NULL;
+	}
+
+	desc = kzalloc(sizeof(*desc), GFP_KERNEL);
+	if (unlikely(!desc))
+		return NULL;
+	desc->channel = channel;
+
+	dev_dbg(&dmadev->pdev->dev, "preparing memcpy from %08x to %08x, %lubytes (flags %x)\n",
+		src, dst, (unsigned long)len, (unsigned int)flags);
+
+	/* CTRL STATUS Preparation */
+
+	/* Pause while start */
+	desc->ctrl = DMA_CTRL_STATUS_PAUSE;
+
+	/* Interrupts enabled
+	 * High priority
+	 * Use new interrupt clearing register
+	 */
+	desc->ctrl |= DMA_CTRL_STATUS_INTERRUPT_ENABLE |
+		      DMA_CTRL_STATUS_FAIR_SHARE_ARB |
+		      DMA_CTRL_STATUS_INTR_CLEAR_ENABLE;
+
+	/* Type Memory */
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_SRC_DREQ, src_memory);
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DEST_DREQ, dst_memory);
+
+	/* Setup the transfer direction and burst/single mode for the two DMA busses */
+	desc->ctrl |= DMA_CTRL_STATUS_TRANSFER_MODE_A;
+	desc->ctrl |= DMA_CTRL_STATUS_TRANSFER_MODE_B;
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DIR, OXNAS_DMA_A_TO_B);
+
+	/* Incrementing addresses */
+	desc->ctrl |= DMA_CTRL_STATUS_SRC_ADR_MODE;
+	desc->ctrl &= ~DMA_CTRL_STATUS_SOURCE_ADDRESS_FIXED;
+	desc->ctrl |= DMA_CTRL_STATUS_DEST_ADR_MODE;
+	desc->ctrl &= ~DMA_CTRL_STATUS_DESTINATION_ADDRESS_FIXED;
+
+	/* Set up the width of the transfers on the DMA buses */
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_SRC_WIDTH, OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+	desc->ctrl |= FIELD_PREP(DMA_CTRL_STATUS_DEST_WIDTH, OXNAS_DMA_TRANSFER_WIDTH_32BITS);
+
+	/* Setup the priority arbitration scheme */
+	desc->ctrl &= ~DMA_CTRL_STATUS_STARVE_LOW_PRIORITY;
+
+	/* LENGTH and End Of Transfert Preparation */
+	desc->len = len |
+		    DMA_BYTE_CNT_INC4_SET_MASK |    /* Always enable INC4 transfers */
+		    DMA_BYTE_CNT_HPROT_MASK |       /* Always enable HPROT assertion */
+		    DMA_BYTE_CNT_RD_EOT_MASK;       /* EOT at last Read */
+
+	desc->src_adr = src;
+	desc->dst_adr = dst;
+	desc->type = OXNAS_DMA_TYPE_SIMPLE;
+
+	return vchan_tx_prep(&channel->vc, &desc->vd, flags);
+}
+
+static int oxnas_dma_slave_config(struct dma_chan *chan, struct dma_slave_config *cfg)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+
+	memcpy(&channel->cfg, cfg, sizeof(channel->cfg));
+
+	return 0;
+}
+
+static void oxnas_dma_desc_free(struct virt_dma_desc *vd)
+{
+	struct oxnas_dma_desc *desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+	struct oxnas_dma_channel *channel = desc->channel;
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+
+	/* Free SG entries */
+	if (desc->type == OXNAS_DMA_TYPE_SG) {
+		list_splice_tail_init(&desc->sg_entries, &dmadev->free_entries);
+		atomic_add(desc->entries, &dmadev->free_entries_count);
+		dev_dbg(&dmadev->pdev->dev, "freed %d sg entries\n", desc->entries);
+	}
+
+	kfree(container_of(vd, struct oxnas_dma_desc, vd));
+}
+
+/** Poll for the DMA channel's active status. There can be multiple transfers
+ *  queued with the DMA channel identified by cookies, so should be checking
+ *  lists containing all pending transfers and all completed transfers that have
+ *  not yet been polled for completion
+ */
+static enum dma_status oxnas_dma_tx_status(struct dma_chan *chan,
+					   dma_cookie_t cookie,
+					   struct dma_tx_state *txstate)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct virt_dma_desc *vd;
+	enum dma_status ret;
+	unsigned long flags;
+
+	ret = dma_cookie_status(chan, cookie, txstate);
+	if (ret == DMA_COMPLETE || !txstate)
+		return ret;
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+	vd = vchan_find_desc(&channel->vc, cookie);
+	if (vd) {
+		struct oxnas_dma_desc *desc = container_of(&vd->tx, struct oxnas_dma_desc, vd.tx);
+
+		txstate->residue = desc->len & OXNAS_DMA_MAX_TRANSFER_LENGTH;
+	} else {
+		txstate->residue = 0;
+	}
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+
+	return ret;
+}
+
+/** To push outstanding transfers to h/w. This should use the list of pending
+ *  transfers identified by cookies to select the next transfer and pass this to
+ *  the hardware
+ */
+static void oxnas_dma_issue_pending(struct dma_chan *chan)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+	struct oxnas_dma_device *dmadev = channel->dmadev;
+	unsigned long flags;
+
+	spin_lock_irqsave(&channel->vc.lock, flags);
+	if (vchan_issue_pending(&channel->vc) && !channel->cur) {
+		spin_lock(&dmadev->lock);
+
+		if (list_empty(&channel->node))
+			list_add_tail(&channel->node, &dmadev->pending);
+
+		spin_unlock(&dmadev->lock);
+
+		tasklet_schedule(&dmadev->tasklet);
+	}
+
+	spin_unlock_irqrestore(&channel->vc.lock, flags);
+}
+
+static void oxnas_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct oxnas_dma_channel *channel = container_of(chan, struct oxnas_dma_channel, vc.chan);
+
+	vchan_free_chan_resources(&channel->vc);
+}
+
+static int oxnas_dma_probe(struct platform_device *pdev)
+{
+	struct oxnas_dma_device *dmadev;
+	struct resource *res;
+	int hwid, i, ret;
+
+	dmadev = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_dma_device), GFP_KERNEL);
+	if (!dmadev)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	dmadev->dma_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dmadev->dma_base))
+		return PTR_ERR(dmadev->dma_base);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	dmadev->sgdma_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(dmadev->sgdma_base))
+		return PTR_ERR(dmadev->sgdma_base);
+
+	dmadev->dma_rst = devm_reset_control_get(&pdev->dev, "dma");
+	if (IS_ERR(dmadev->dma_rst))
+		return PTR_ERR(dmadev->dma_rst);
+
+	dmadev->sgdma_rst = devm_reset_control_get(&pdev->dev, "sgdma");
+	if (IS_ERR(dmadev->sgdma_rst))
+		return PTR_ERR(dmadev->sgdma_rst);
+
+	dmadev->dma_clk = devm_clk_get(&pdev->dev, 0);
+	if (IS_ERR(dmadev->dma_clk))
+		return PTR_ERR(dmadev->dma_clk);
+
+	ret = of_property_count_elems_of_size(pdev->dev.of_node, "oxsemi,targets-types", 4);
+	if (ret <= 0 || (ret % 3) != 0) {
+		dev_err(&pdev->dev, "malformed or missing oxsemi,targets-types\n");
+		return -EINVAL;
+	}
+
+	dmadev->authorized_types_count = ret / 3;
+	dmadev->authorized_types = devm_kzalloc(&pdev->dev,
+		sizeof(*dmadev->authorized_types) * dmadev->authorized_types_count, GFP_KERNEL);
+
+	if (!dmadev->authorized_types)
+		return -ENOMEM;
+
+	for (i = 0 ; i < dmadev->authorized_types_count ; ++i) {
+		u32 value;
+
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3), &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].start = value;
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3) + 1, &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].end = value;
+		ret = of_property_read_u32_index(pdev->dev.of_node,
+						 "oxsemi,targets-types",
+						 (i * 3) + 2, &value);
+		if (ret < 0)
+			return ret;
+
+		dmadev->authorized_types[i].type = value;
+	}
+
+	dev_dbg(&pdev->dev, "Authorized memory ranges :\n");
+	dev_dbg(&pdev->dev, " Start    - End      = Type\n");
+	for (i = 0 ; i <  dmadev->authorized_types_count ; ++i)
+		dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
+			dmadev->authorized_types[i].start,
+			dmadev->authorized_types[i].end,
+			dmadev->authorized_types[i].type);
+
+	dmadev->pdev = pdev;
+
+	spin_lock_init(&dmadev->lock);
+
+	tasklet_init(&dmadev->tasklet, oxnas_dma_sched, (unsigned long)dmadev);
+	INIT_LIST_HEAD(&dmadev->common.channels);
+	INIT_LIST_HEAD(&dmadev->pending);
+	INIT_LIST_HEAD(&dmadev->free_entries);
+
+	/* Enable HW & Clocks */
+	reset_control_reset(dmadev->dma_rst);
+	reset_control_reset(dmadev->sgdma_rst);
+	clk_prepare_enable(dmadev->dma_clk);
+
+	/* Discover the number of channels available */
+	hwid = readl(dmadev->dma_base + DMA_CALC_REG_ADR(0, DMA_INTR_ID));
+	dmadev->channels_count = DMA_INTR_ID_GET_NUM_CHANNELS(hwid);
+	dmadev->hwversion = DMA_INTR_ID_GET_VERSION(hwid);
+
+	dev_dbg(&pdev->dev, "OXNAS DMA v%x with %d channels\n",
+		dmadev->hwversion, dmadev->channels_count);
+
+	/* Limit channels count */
+	if (dmadev->channels_count > MAX_OXNAS_DMA_CHANNELS)
+		dmadev->channels_count = MAX_OXNAS_DMA_CHANNELS;
+
+	/* Allocate coherent memory for sg descriptors */
+	dmadev->sg_data = dma_alloc_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+					     &dmadev->p_sg_data, GFP_KERNEL);
+	if (!dmadev->sg_data) {
+		ret = -ENOMEM;
+		goto disable_clks;
+	}
+
+	/* Reset SG descritors */
+	memset(dmadev->sg_data, 0, sizeof(struct oxnas_dma_sg_data));
+	atomic_set(&dmadev->free_entries_count, 0);
+
+	/* Initialize and add all sg entries to the free list */
+	for (i = 0 ; i < MAX_OXNAS_SG_ENTRIES ; ++i) {
+		dmadev->sg_data->entries[i].this_paddr =
+			(dma_addr_t)&(((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->entries[i]);
+		INIT_LIST_HEAD(&dmadev->sg_data->entries[i].entry);
+		list_add_tail(&dmadev->sg_data->entries[i].entry,
+			      &dmadev->free_entries);
+		atomic_inc(&dmadev->free_entries_count);
+	}
+
+	/* Init all channels */
+	for (i = 0 ; i < dmadev->channels_count ; ++i) {
+		struct oxnas_dma_channel *ch = &dmadev->channels[i];
+
+		ch->dmadev = dmadev;
+		ch->id = i;
+
+		ch->irq = irq_of_parse_and_map(pdev->dev.of_node, i);
+		if (ch->irq <= 0) {
+			dev_err(&pdev->dev, "invalid irq%d from platform\n", i);
+			goto free_coherent;
+		}
+
+		ret = devm_request_irq(&pdev->dev, ch->irq,
+				       oxnas_dma_interrupt, 0,
+				       "DMA", ch);
+		if (ret < 0) {
+			dev_err(&pdev->dev, "failed to request irq%d\n", i);
+			goto free_coherent;
+		}
+
+		ch->p_sg_info =
+			(dma_addr_t)&((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->infos[i];
+		ch->sg_info = &dmadev->sg_data->infos[i];
+		memset(ch->sg_info, 0, sizeof(struct oxnas_dma_sg_info));
+
+		atomic_set(&ch->active, 0);
+
+		ch->vc.desc_free = oxnas_dma_desc_free;
+		vchan_init(&ch->vc, &dmadev->common);
+		INIT_LIST_HEAD(&ch->node);
+	}
+
+	platform_set_drvdata(pdev, dmadev);
+
+	dma_cap_set(DMA_MEMCPY, dmadev->common.cap_mask);
+	dmadev->common.chancnt = dmadev->channels_count;
+	dmadev->common.device_free_chan_resources = oxnas_dma_free_chan_resources;
+	dmadev->common.device_tx_status = oxnas_dma_tx_status;
+	dmadev->common.device_issue_pending = oxnas_dma_issue_pending;
+	dmadev->common.device_prep_dma_memcpy = oxnas_dma_prep_dma_memcpy;
+	dmadev->common.device_prep_slave_sg = oxnas_dma_prep_slave_sg;
+	dmadev->common.device_config = oxnas_dma_slave_config;
+	dmadev->common.copy_align = DMAENGINE_ALIGN_4_BYTES;
+	dmadev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dmadev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
+	dmadev->common.directions = BIT(DMA_MEM_TO_MEM);
+	dmadev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
+	dmadev->common.dev = &pdev->dev;
+
+	ret = dma_async_device_register(&dmadev->common);
+	if (ret)
+		goto free_coherent;
+
+	ret = of_dma_controller_register(pdev->dev.of_node,
+					 of_dma_xlate_by_chan_id,
+					 &dmadev->common);
+	if (ret) {
+		dev_warn(&pdev->dev, "Failed to register DMA Controller\n");
+		goto dma_unregister;
+	}
+
+	dev_info(&pdev->dev, "OXNAS DMA Registered\n");
+
+	return 0;
+
+dma_unregister:
+	dma_async_device_unregister(&dmadev->common);
+
+free_coherent:
+	dma_free_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+			  dmadev->sg_data, dmadev->p_sg_data);
+
+disable_clks:
+	clk_disable_unprepare(dmadev->dma_clk);
+
+	return ret;
+}
+
+static int oxnas_dma_remove(struct platform_device *pdev)
+{
+	struct oxnas_dma_device *dmadev = platform_get_drvdata(pdev);
+
+	dma_async_device_unregister(&dmadev->common);
+
+	dma_free_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
+			  dmadev->sg_data, dmadev->p_sg_data);
+
+	clk_disable_unprepare(dmadev->dma_clk);
+
+	return 0;
+}
+
+static const struct of_device_id oxnas_dma_of_dev_id[] = {
+	{ .compatible = "oxsemi,ox810se-dma", },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, oxnas_dma_of_dev_id);
+
+static struct platform_driver oxnas_dma_driver = {
+	.probe		= oxnas_dma_probe,
+	.remove		= oxnas_dma_remove,
+	.driver		= {
+		.owner	= THIS_MODULE,
+		.name	= "oxnas-dma",
+		.of_match_table = oxnas_dma_of_dev_id,
+	},
+};
+
+static int __init oxnas_dma_init_module(void)
+{
+	return platform_driver_register(&oxnas_dma_driver);
+}
+subsys_initcall(oxnas_dma_init_module);
+
+static void __exit oxnas_dma_exit_module(void)
+{
+	platform_driver_unregister(&oxnas_dma_driver);
+}
+module_exit(oxnas_dma_exit_module);
+
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/4] MAINTAINERS: add OX810SE DMA driver files under Oxnas entry
  2022-01-04 14:52 ` Neil Armstrong
@ 2022-01-04 14:52   ` Neil Armstrong
  -1 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

Add the DMA driver file and bindings in the Oxnas maintainers entry.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a2345ce8521..782332ab0d9b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2371,8 +2371,10 @@ M:	Neil Armstrong <narmstrong@baylibre.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:	linux-oxnas@groups.io (moderated for non-subscribers)
 S:	Maintained
+F:	Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
 F:	arch/arm/boot/dts/ox8*.dts*
 F:	arch/arm/mach-oxnas/
+F:	drivers/dma/oxnas_adma.c
 F:	drivers/power/reset/oxnas-restart.c
 N:	oxnas
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 3/4] MAINTAINERS: add OX810SE DMA driver files under Oxnas entry
@ 2022-01-04 14:52   ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

Add the DMA driver file and bindings in the Oxnas maintainers entry.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index 7a2345ce8521..782332ab0d9b 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -2371,8 +2371,10 @@ M:	Neil Armstrong <narmstrong@baylibre.com>
 L:	linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
 L:	linux-oxnas@groups.io (moderated for non-subscribers)
 S:	Maintained
+F:	Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
 F:	arch/arm/boot/dts/ox8*.dts*
 F:	arch/arm/mach-oxnas/
+F:	drivers/dma/oxnas_adma.c
 F:	drivers/power/reset/oxnas-restart.c
 N:	oxnas
 
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/4] ARM: dts: ox810se: Add DMA Support
  2022-01-04 14:52 ` Neil Armstrong
@ 2022-01-04 14:52   ` Neil Armstrong
  -1 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This adds the DMA engine node.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 arch/arm/boot/dts/ox810se-wd-mbwe.dts |  4 ++++
 arch/arm/boot/dts/ox810se.dtsi        | 21 +++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/arch/arm/boot/dts/ox810se-wd-mbwe.dts b/arch/arm/boot/dts/ox810se-wd-mbwe.dts
index 7e2fcb220aea..19e5d510e425 100644
--- a/arch/arm/boot/dts/ox810se-wd-mbwe.dts
+++ b/arch/arm/boot/dts/ox810se-wd-mbwe.dts
@@ -103,6 +103,10 @@ rtc0: rtc@48 {
 	};
 };
 
+&dma {
+	status = "okay";
+};
+
 &uart1 {
 	status = "okay";
 
diff --git a/arch/arm/boot/dts/ox810se.dtsi b/arch/arm/boot/dts/ox810se.dtsi
index 0755e5864c4a..79b2b49dcfbb 100644
--- a/arch/arm/boot/dts/ox810se.dtsi
+++ b/arch/arm/boot/dts/ox810se.dtsi
@@ -334,6 +334,27 @@ timer0: timer@200 {
 					interrupts = <4 5>;
 				};
 			};
+
+			dma: dma-controller@600000 {
+				compatible = "oxsemi,ox810se-dma";
+				reg = <0x600000 0x100000>,
+				      <0xc00000 0x100000>;
+				reg-names = "dma", "sgdma";
+				interrupts = <13>, <14>, <15>, <16>, <20>;
+				clocks = <&stdclk 1>;
+				resets = <&reset 8>, <&reset 24>;
+				reset-names = "dma", "sgdma";
+
+				/* Encodes the authorized memory types */
+				oxsemi,targets-types =
+					<0x45900000 0x45a00000 0>,  /* SATA */
+					<0x42000000 0x43000000 0>,  /* SATA DATA */
+					<0x48000000 0x58000000 15>, /* DDR */
+					<0x58000000 0x58020000 15>; /* SRAM */
+
+				#dma-cells = <1>;
+				dma-channels = <5>;
+			};
 		};
 	};
 };
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 13+ messages in thread

* [PATCH 4/4] ARM: dts: ox810se: Add DMA Support
@ 2022-01-04 14:52   ` Neil Armstrong
  0 siblings, 0 replies; 13+ messages in thread
From: Neil Armstrong @ 2022-01-04 14:52 UTC (permalink / raw)
  To: vkoul
  Cc: linux-oxnas, dmaengine, linux-arm-kernel, linux-kernel, Neil Armstrong

This adds the DMA engine node.

Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
---
 arch/arm/boot/dts/ox810se-wd-mbwe.dts |  4 ++++
 arch/arm/boot/dts/ox810se.dtsi        | 21 +++++++++++++++++++++
 2 files changed, 25 insertions(+)

diff --git a/arch/arm/boot/dts/ox810se-wd-mbwe.dts b/arch/arm/boot/dts/ox810se-wd-mbwe.dts
index 7e2fcb220aea..19e5d510e425 100644
--- a/arch/arm/boot/dts/ox810se-wd-mbwe.dts
+++ b/arch/arm/boot/dts/ox810se-wd-mbwe.dts
@@ -103,6 +103,10 @@ rtc0: rtc@48 {
 	};
 };
 
+&dma {
+	status = "okay";
+};
+
 &uart1 {
 	status = "okay";
 
diff --git a/arch/arm/boot/dts/ox810se.dtsi b/arch/arm/boot/dts/ox810se.dtsi
index 0755e5864c4a..79b2b49dcfbb 100644
--- a/arch/arm/boot/dts/ox810se.dtsi
+++ b/arch/arm/boot/dts/ox810se.dtsi
@@ -334,6 +334,27 @@ timer0: timer@200 {
 					interrupts = <4 5>;
 				};
 			};
+
+			dma: dma-controller@600000 {
+				compatible = "oxsemi,ox810se-dma";
+				reg = <0x600000 0x100000>,
+				      <0xc00000 0x100000>;
+				reg-names = "dma", "sgdma";
+				interrupts = <13>, <14>, <15>, <16>, <20>;
+				clocks = <&stdclk 1>;
+				resets = <&reset 8>, <&reset 24>;
+				reset-names = "dma", "sgdma";
+
+				/* Encodes the authorized memory types */
+				oxsemi,targets-types =
+					<0x45900000 0x45a00000 0>,  /* SATA */
+					<0x42000000 0x43000000 0>,  /* SATA DATA */
+					<0x48000000 0x58000000 15>, /* DDR */
+					<0x58000000 0x58020000 15>; /* SRAM */
+
+				#dma-cells = <1>;
+				dma-channels = <5>;
+			};
 		};
 	};
 };
-- 
2.25.1


_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply related	[flat|nested] 13+ messages in thread

* Re: [PATCH 2/4] dmaengine: Add Oxford Semiconductor OXNAS DMA Controller
  2022-01-04 14:52   ` Neil Armstrong
  (?)
@ 2022-01-04 17:27   ` kernel test robot
  -1 siblings, 0 replies; 13+ messages in thread
From: kernel test robot @ 2022-01-04 17:27 UTC (permalink / raw)
  To: kbuild-all

[-- Attachment #1: Type: text/plain, Size: 17681 bytes --]

Hi Neil,

I love your patch! Perhaps something to improve:

[auto build test WARNING on fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf]

url:    https://github.com/0day-ci/linux/commits/Neil-Armstrong/dmaengine-Add-support-Oxford-Semiconductor-OXNAS-DMA-Engine/20220104-225322
base:   fa55b7dcdc43c1aa1ba12bca9d2dd4318c2a0dbf
config: arc-allyesconfig (https://download.01.org/0day-ci/archive/20220105/202201050144.3TpBzQkL-lkp(a)intel.com/config)
compiler: arceb-elf-gcc (GCC) 11.2.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/0day-ci/linux/commit/55f80a9eaf56cb8c1b943a7ea35e3a97aecc628d
        git remote add linux-review https://github.com/0day-ci/linux
        git fetch --no-tags linux-review Neil-Armstrong/dmaengine-Add-support-Oxford-Semiconductor-OXNAS-DMA-Engine/20220104-225322
        git checkout 55f80a9eaf56cb8c1b943a7ea35e3a97aecc628d
        # save the config file to linux build tree
        mkdir build_dir
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-11.2.0 make.cross O=build_dir ARCH=arc SHELL=/bin/bash drivers/dma/

If you fix the issue, kindly add following tag as appropriate
Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

         |                                                               |
         |                                                               unsigned int
         |                                                            %08llx
   In file included from include/linux/printk.h:555,
                    from include/linux/kernel.h:20,
                    from include/linux/cpumask.h:10,
                    from include/linux/mm_types_task.h:14,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/dma/oxnas_adma.c:7:
   drivers/dma/oxnas_adma.c:655:37: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' {aka 'long long unsigned int'} [-Wformat=]
     655 |         dev_dbg(&dmadev->pdev->dev, "preparing memcpy from %08x to %08x, %lubytes (flags %x)\n",
         |                                     ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/dma/oxnas_adma.c:655:9: note: in expansion of macro 'dev_dbg'
     655 |         dev_dbg(&dmadev->pdev->dev, "preparing memcpy from %08x to %08x, %lubytes (flags %x)\n",
         |         ^~~~~~~
   drivers/dma/oxnas_adma.c:655:71: note: format string is defined here
     655 |         dev_dbg(&dmadev->pdev->dev, "preparing memcpy from %08x to %08x, %lubytes (flags %x)\n",
         |                                                                    ~~~^
         |                                                                       |
         |                                                                       unsigned int
         |                                                                    %08llx
   In file included from include/linux/printk.h:555,
                    from include/linux/kernel.h:20,
                    from include/linux/cpumask.h:10,
                    from include/linux/mm_types_task.h:14,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/dma/oxnas_adma.c:7:
   drivers/dma/oxnas_adma.c: In function 'oxnas_dma_probe':
   drivers/dma/oxnas_adma.c:869:37: warning: format '%x' expects argument of type 'unsigned int', but argument 4 has type 'dma_addr_t' {aka 'long long unsigned int'} [-Wformat=]
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                                     ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/dma/oxnas_adma.c:869:17: note: in expansion of macro 'dev_dbg'
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                 ^~~~~~~
   drivers/dma/oxnas_adma.c:869:43: note: format string is defined here
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                                        ~~~^
         |                                           |
         |                                           unsigned int
         |                                        %08llx
   In file included from include/linux/printk.h:555,
                    from include/linux/kernel.h:20,
                    from include/linux/cpumask.h:10,
                    from include/linux/mm_types_task.h:14,
                    from include/linux/mm_types.h:5,
                    from include/linux/buildid.h:5,
                    from include/linux/module.h:14,
                    from drivers/dma/oxnas_adma.c:7:
   drivers/dma/oxnas_adma.c:869:37: warning: format '%x' expects argument of type 'unsigned int', but argument 5 has type 'dma_addr_t' {aka 'long long unsigned int'} [-Wformat=]
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                                     ^~~~~~~~~~~~~~~~~~~~~~
   include/linux/dynamic_debug.h:134:29: note: in definition of macro '__dynamic_func_call'
     134 |                 func(&id, ##__VA_ARGS__);               \
         |                             ^~~~~~~~~~~
   include/linux/dynamic_debug.h:166:9: note: in expansion of macro '_dynamic_func_call'
     166 |         _dynamic_func_call(fmt,__dynamic_dev_dbg,               \
         |         ^~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:9: note: in expansion of macro 'dynamic_dev_dbg'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |         ^~~~~~~~~~~~~~~
   include/linux/dev_printk.h:155:30: note: in expansion of macro 'dev_fmt'
     155 |         dynamic_dev_dbg(dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                              ^~~~~~~
   drivers/dma/oxnas_adma.c:869:17: note: in expansion of macro 'dev_dbg'
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                 ^~~~~~~
   drivers/dma/oxnas_adma.c:869:50: note: format string is defined here
     869 |                 dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
         |                                               ~~~^
         |                                                  |
         |                                                  unsigned int
         |                                               %08llx
>> drivers/dma/oxnas_adma.c:915:40: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     915 |                         (dma_addr_t)&(((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->entries[i]);
         |                                        ^
>> drivers/dma/oxnas_adma.c:915:25: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     915 |                         (dma_addr_t)&(((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->entries[i]);
         |                         ^
   drivers/dma/oxnas_adma.c:944:39: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
     944 |                         (dma_addr_t)&((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->infos[i];
         |                                       ^
   drivers/dma/oxnas_adma.c:944:25: warning: cast from pointer to integer of different size [-Wpointer-to-int-cast]
     944 |                         (dma_addr_t)&((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->infos[i];
         |                         ^


vim +915 drivers/dma/oxnas_adma.c

   794	
   795	static int oxnas_dma_probe(struct platform_device *pdev)
   796	{
   797		struct oxnas_dma_device *dmadev;
   798		struct resource *res;
   799		int hwid, i, ret;
   800	
   801		dmadev = devm_kzalloc(&pdev->dev, sizeof(struct oxnas_dma_device), GFP_KERNEL);
   802		if (!dmadev)
   803			return -ENOMEM;
   804	
   805		res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
   806		dmadev->dma_base = devm_ioremap_resource(&pdev->dev, res);
   807		if (IS_ERR(dmadev->dma_base))
   808			return PTR_ERR(dmadev->dma_base);
   809	
   810		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
   811		dmadev->sgdma_base = devm_ioremap_resource(&pdev->dev, res);
   812		if (IS_ERR(dmadev->sgdma_base))
   813			return PTR_ERR(dmadev->sgdma_base);
   814	
   815		dmadev->dma_rst = devm_reset_control_get(&pdev->dev, "dma");
   816		if (IS_ERR(dmadev->dma_rst))
   817			return PTR_ERR(dmadev->dma_rst);
   818	
   819		dmadev->sgdma_rst = devm_reset_control_get(&pdev->dev, "sgdma");
   820		if (IS_ERR(dmadev->sgdma_rst))
   821			return PTR_ERR(dmadev->sgdma_rst);
   822	
   823		dmadev->dma_clk = devm_clk_get(&pdev->dev, 0);
   824		if (IS_ERR(dmadev->dma_clk))
   825			return PTR_ERR(dmadev->dma_clk);
   826	
   827		ret = of_property_count_elems_of_size(pdev->dev.of_node, "oxsemi,targets-types", 4);
   828		if (ret <= 0 || (ret % 3) != 0) {
   829			dev_err(&pdev->dev, "malformed or missing oxsemi,targets-types\n");
   830			return -EINVAL;
   831		}
   832	
   833		dmadev->authorized_types_count = ret / 3;
   834		dmadev->authorized_types = devm_kzalloc(&pdev->dev,
   835			sizeof(*dmadev->authorized_types) * dmadev->authorized_types_count, GFP_KERNEL);
   836	
   837		if (!dmadev->authorized_types)
   838			return -ENOMEM;
   839	
   840		for (i = 0 ; i < dmadev->authorized_types_count ; ++i) {
   841			u32 value;
   842	
   843			ret = of_property_read_u32_index(pdev->dev.of_node,
   844							 "oxsemi,targets-types",
   845							 (i * 3), &value);
   846			if (ret < 0)
   847				return ret;
   848	
   849			dmadev->authorized_types[i].start = value;
   850			ret = of_property_read_u32_index(pdev->dev.of_node,
   851							 "oxsemi,targets-types",
   852							 (i * 3) + 1, &value);
   853			if (ret < 0)
   854				return ret;
   855	
   856			dmadev->authorized_types[i].end = value;
   857			ret = of_property_read_u32_index(pdev->dev.of_node,
   858							 "oxsemi,targets-types",
   859							 (i * 3) + 2, &value);
   860			if (ret < 0)
   861				return ret;
   862	
   863			dmadev->authorized_types[i].type = value;
   864		}
   865	
   866		dev_dbg(&pdev->dev, "Authorized memory ranges :\n");
   867		dev_dbg(&pdev->dev, " Start    - End      = Type\n");
   868		for (i = 0 ; i <  dmadev->authorized_types_count ; ++i)
   869			dev_dbg(&pdev->dev, "0x%08x-0x%08x = %d\n",
   870				dmadev->authorized_types[i].start,
   871				dmadev->authorized_types[i].end,
   872				dmadev->authorized_types[i].type);
   873	
   874		dmadev->pdev = pdev;
   875	
   876		spin_lock_init(&dmadev->lock);
   877	
   878		tasklet_init(&dmadev->tasklet, oxnas_dma_sched, (unsigned long)dmadev);
   879		INIT_LIST_HEAD(&dmadev->common.channels);
   880		INIT_LIST_HEAD(&dmadev->pending);
   881		INIT_LIST_HEAD(&dmadev->free_entries);
   882	
   883		/* Enable HW & Clocks */
   884		reset_control_reset(dmadev->dma_rst);
   885		reset_control_reset(dmadev->sgdma_rst);
   886		clk_prepare_enable(dmadev->dma_clk);
   887	
   888		/* Discover the number of channels available */
   889		hwid = readl(dmadev->dma_base + DMA_CALC_REG_ADR(0, DMA_INTR_ID));
   890		dmadev->channels_count = DMA_INTR_ID_GET_NUM_CHANNELS(hwid);
   891		dmadev->hwversion = DMA_INTR_ID_GET_VERSION(hwid);
   892	
   893		dev_dbg(&pdev->dev, "OXNAS DMA v%x with %d channels\n",
   894			dmadev->hwversion, dmadev->channels_count);
   895	
   896		/* Limit channels count */
   897		if (dmadev->channels_count > MAX_OXNAS_DMA_CHANNELS)
   898			dmadev->channels_count = MAX_OXNAS_DMA_CHANNELS;
   899	
   900		/* Allocate coherent memory for sg descriptors */
   901		dmadev->sg_data = dma_alloc_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
   902						     &dmadev->p_sg_data, GFP_KERNEL);
   903		if (!dmadev->sg_data) {
   904			ret = -ENOMEM;
   905			goto disable_clks;
   906		}
   907	
   908		/* Reset SG descritors */
   909		memset(dmadev->sg_data, 0, sizeof(struct oxnas_dma_sg_data));
   910		atomic_set(&dmadev->free_entries_count, 0);
   911	
   912		/* Initialize and add all sg entries to the free list */
   913		for (i = 0 ; i < MAX_OXNAS_SG_ENTRIES ; ++i) {
   914			dmadev->sg_data->entries[i].this_paddr =
 > 915				(dma_addr_t)&(((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->entries[i]);
   916			INIT_LIST_HEAD(&dmadev->sg_data->entries[i].entry);
   917			list_add_tail(&dmadev->sg_data->entries[i].entry,
   918				      &dmadev->free_entries);
   919			atomic_inc(&dmadev->free_entries_count);
   920		}
   921	
   922		/* Init all channels */
   923		for (i = 0 ; i < dmadev->channels_count ; ++i) {
   924			struct oxnas_dma_channel *ch = &dmadev->channels[i];
   925	
   926			ch->dmadev = dmadev;
   927			ch->id = i;
   928	
   929			ch->irq = irq_of_parse_and_map(pdev->dev.of_node, i);
   930			if (ch->irq <= 0) {
   931				dev_err(&pdev->dev, "invalid irq%d from platform\n", i);
   932				goto free_coherent;
   933			}
   934	
   935			ret = devm_request_irq(&pdev->dev, ch->irq,
   936					       oxnas_dma_interrupt, 0,
   937					       "DMA", ch);
   938			if (ret < 0) {
   939				dev_err(&pdev->dev, "failed to request irq%d\n", i);
   940				goto free_coherent;
   941			}
   942	
   943			ch->p_sg_info =
   944				(dma_addr_t)&((struct oxnas_dma_sg_data *)dmadev->p_sg_data)->infos[i];
   945			ch->sg_info = &dmadev->sg_data->infos[i];
   946			memset(ch->sg_info, 0, sizeof(struct oxnas_dma_sg_info));
   947	
   948			atomic_set(&ch->active, 0);
   949	
   950			ch->vc.desc_free = oxnas_dma_desc_free;
   951			vchan_init(&ch->vc, &dmadev->common);
   952			INIT_LIST_HEAD(&ch->node);
   953		}
   954	
   955		platform_set_drvdata(pdev, dmadev);
   956	
   957		dma_cap_set(DMA_MEMCPY, dmadev->common.cap_mask);
   958		dmadev->common.chancnt = dmadev->channels_count;
   959		dmadev->common.device_free_chan_resources = oxnas_dma_free_chan_resources;
   960		dmadev->common.device_tx_status = oxnas_dma_tx_status;
   961		dmadev->common.device_issue_pending = oxnas_dma_issue_pending;
   962		dmadev->common.device_prep_dma_memcpy = oxnas_dma_prep_dma_memcpy;
   963		dmadev->common.device_prep_slave_sg = oxnas_dma_prep_slave_sg;
   964		dmadev->common.device_config = oxnas_dma_slave_config;
   965		dmadev->common.copy_align = DMAENGINE_ALIGN_4_BYTES;
   966		dmadev->common.src_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
   967		dmadev->common.dst_addr_widths = DMA_SLAVE_BUSWIDTH_4_BYTES;
   968		dmadev->common.directions = BIT(DMA_MEM_TO_MEM);
   969		dmadev->common.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR;
   970		dmadev->common.dev = &pdev->dev;
   971	
   972		ret = dma_async_device_register(&dmadev->common);
   973		if (ret)
   974			goto free_coherent;
   975	
   976		ret = of_dma_controller_register(pdev->dev.of_node,
   977						 of_dma_xlate_by_chan_id,
   978						 &dmadev->common);
   979		if (ret) {
   980			dev_warn(&pdev->dev, "Failed to register DMA Controller\n");
   981			goto dma_unregister;
   982		}
   983	
   984		dev_info(&pdev->dev, "OXNAS DMA Registered\n");
   985	
   986		return 0;
   987	
   988	dma_unregister:
   989		dma_async_device_unregister(&dmadev->common);
   990	
   991	free_coherent:
   992		dma_free_coherent(&pdev->dev, sizeof(struct oxnas_dma_sg_data),
   993				  dmadev->sg_data, dmadev->p_sg_data);
   994	
   995	disable_clks:
   996		clk_disable_unprepare(dmadev->dma_clk);
   997	
   998		return ret;
   999	}
  1000	

---
0-DAY CI Kernel Test Service, Intel Corporation
https://lists.01.org/hyperkitty/list/kbuild-all(a)lists.01.org

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/4] dt-bindings: dma: Add bindings for ox810se dma engine
  2022-01-04 14:52   ` Neil Armstrong
@ 2022-01-12  1:20     ` Rob Herring
  -1 siblings, 0 replies; 13+ messages in thread
From: Rob Herring @ 2022-01-12  1:20 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: vkoul, devicetree, linux-oxnas, dmaengine, linux-arm-kernel,
	linux-kernel

On Tue, Jan 04, 2022 at 03:52:03PM +0100, Neil Armstrong wrote:
> This adds the YAML dt-bindings for the DMA engine found in the
> Oxford Semiconductor OX810SE SoC.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
>  .../bindings/dma/oxsemi,ox810se-dma.yaml      | 97 +++++++++++++++++++
>  1 file changed, 97 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> 
> diff --git a/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> new file mode 100644
> index 000000000000..6efa28e8b124
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> @@ -0,0 +1,97 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/dma/oxsemi,ox810se-dma.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Oxford Semiconductor DMA Controller Device Tree Bindings
> +
> +maintainers:
> +  - Neil Armstrong <narmstrong@baylibre.com>
> +
> +allOf:
> +  - $ref: "dma-controller.yaml#"
> +
> +properties:
> +  "#dma-cells":
> +    const: 1
> +
> +  compatible:
> +    const: oxsemi,ox810se-dma
> +
> +  reg:
> +    maxItems: 2
> +
> +  reg-names:
> +    items:
> +      - const: dma
> +      - const: sgdma
> +
> +  interrupts:
> +    maxItems: 5

Need to define what each one is.

> +
> +  clocks:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 2
> +
> +  reset-names:
> +    items:
> +      - const: dma
> +      - const: sgdma
> +
> +  dma-channels: true

Constraints on number of channels?

> +
> +  oxsemi,targets-types:
> +    description:
> +      Table with allowed memory ranges and memory type associated.
> +    $ref: "/schemas/types.yaml#/definitions/uint32-matrix"
> +    minItems: 4
> +    items:
> +      items:
> +        - description:
> +            The first cell defines the memory range start address
> +        - description:
> +            The first cell defines the memory range end address
> +        - description:
> +            The third cell represents memory type, 0 for SATA,
> +            1 for DPE RX, 2 for DPE TX, 5 for AUDIO TX, 6 for AUDIO RX,
> +            15 for DRAM MEMORY.
> +          enum: [ 0, 1, 2, 5, 6, 15 ]
> +
> +required:
> +  - "#dma-cells"
> +  - dma-channels
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - resets
> +  - reset-names
> +  - oxsemi,targets-types
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dma: dma-controller@600000 {

Drop unused labels.

> +        compatible = "oxsemi,ox810se-dma";
> +        reg = <0x600000 0x100000>, <0xc00000 0x100000>;
> +        reg-names = "dma", "sgdma";
> +        interrupts = <13>, <14>, <15>, <16>, <20>;
> +        clocks = <&stdclk 1>;
> +        resets = <&reset 8>, <&reset 24>;
> +        reset-names = "dma", "sgdma";
> +
> +        /* Encodes the authorized memory types */
> +        oxsemi,targets-types =
> +            <0x45900000 0x45a00000 0>,  /* SATA */
> +            <0x42000000 0x43000000 0>,  /* SATA DATA */
> +            <0x48000000 0x58000000 15>, /* DDR */
> +            <0x58000000 0x58020000 15>; /* SRAM */
> +
> +        #dma-cells = <1>;
> +        dma-channels = <5>;
> +    };
> +...
> -- 
> 2.25.1
> 
> 

^ permalink raw reply	[flat|nested] 13+ messages in thread

* Re: [PATCH 1/4] dt-bindings: dma: Add bindings for ox810se dma engine
@ 2022-01-12  1:20     ` Rob Herring
  0 siblings, 0 replies; 13+ messages in thread
From: Rob Herring @ 2022-01-12  1:20 UTC (permalink / raw)
  To: Neil Armstrong
  Cc: vkoul, devicetree, linux-oxnas, dmaengine, linux-arm-kernel,
	linux-kernel

On Tue, Jan 04, 2022 at 03:52:03PM +0100, Neil Armstrong wrote:
> This adds the YAML dt-bindings for the DMA engine found in the
> Oxford Semiconductor OX810SE SoC.
> 
> Signed-off-by: Neil Armstrong <narmstrong@baylibre.com>
> ---
>  .../bindings/dma/oxsemi,ox810se-dma.yaml      | 97 +++++++++++++++++++
>  1 file changed, 97 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> 
> diff --git a/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> new file mode 100644
> index 000000000000..6efa28e8b124
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/dma/oxsemi,ox810se-dma.yaml
> @@ -0,0 +1,97 @@
> +# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
> +%YAML 1.2
> +---
> +$id: http://devicetree.org/schemas/dma/oxsemi,ox810se-dma.yaml#
> +$schema: http://devicetree.org/meta-schemas/core.yaml#
> +
> +title: Oxford Semiconductor DMA Controller Device Tree Bindings
> +
> +maintainers:
> +  - Neil Armstrong <narmstrong@baylibre.com>
> +
> +allOf:
> +  - $ref: "dma-controller.yaml#"
> +
> +properties:
> +  "#dma-cells":
> +    const: 1
> +
> +  compatible:
> +    const: oxsemi,ox810se-dma
> +
> +  reg:
> +    maxItems: 2
> +
> +  reg-names:
> +    items:
> +      - const: dma
> +      - const: sgdma
> +
> +  interrupts:
> +    maxItems: 5

Need to define what each one is.

> +
> +  clocks:
> +    maxItems: 1
> +
> +  resets:
> +    maxItems: 2
> +
> +  reset-names:
> +    items:
> +      - const: dma
> +      - const: sgdma
> +
> +  dma-channels: true

Constraints on number of channels?

> +
> +  oxsemi,targets-types:
> +    description:
> +      Table with allowed memory ranges and memory type associated.
> +    $ref: "/schemas/types.yaml#/definitions/uint32-matrix"
> +    minItems: 4
> +    items:
> +      items:
> +        - description:
> +            The first cell defines the memory range start address
> +        - description:
> +            The first cell defines the memory range end address
> +        - description:
> +            The third cell represents memory type, 0 for SATA,
> +            1 for DPE RX, 2 for DPE TX, 5 for AUDIO TX, 6 for AUDIO RX,
> +            15 for DRAM MEMORY.
> +          enum: [ 0, 1, 2, 5, 6, 15 ]
> +
> +required:
> +  - "#dma-cells"
> +  - dma-channels
> +  - compatible
> +  - reg
> +  - interrupts
> +  - clocks
> +  - resets
> +  - reset-names
> +  - oxsemi,targets-types
> +
> +additionalProperties: false
> +
> +examples:
> +  - |
> +    dma: dma-controller@600000 {

Drop unused labels.

> +        compatible = "oxsemi,ox810se-dma";
> +        reg = <0x600000 0x100000>, <0xc00000 0x100000>;
> +        reg-names = "dma", "sgdma";
> +        interrupts = <13>, <14>, <15>, <16>, <20>;
> +        clocks = <&stdclk 1>;
> +        resets = <&reset 8>, <&reset 24>;
> +        reset-names = "dma", "sgdma";
> +
> +        /* Encodes the authorized memory types */
> +        oxsemi,targets-types =
> +            <0x45900000 0x45a00000 0>,  /* SATA */
> +            <0x42000000 0x43000000 0>,  /* SATA DATA */
> +            <0x48000000 0x58000000 15>, /* DDR */
> +            <0x58000000 0x58020000 15>; /* SRAM */
> +
> +        #dma-cells = <1>;
> +        dma-channels = <5>;
> +    };
> +...
> -- 
> 2.25.1
> 
> 

_______________________________________________
linux-arm-kernel mailing list
linux-arm-kernel@lists.infradead.org
http://lists.infradead.org/mailman/listinfo/linux-arm-kernel

^ permalink raw reply	[flat|nested] 13+ messages in thread

end of thread, other threads:[~2022-01-12  1:22 UTC | newest]

Thread overview: 13+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-01-04 14:52 [PATCH 0/4] dmaengine: Add support Oxford Semiconductor OXNAS DMA Engine Neil Armstrong
2022-01-04 14:52 ` Neil Armstrong
2022-01-04 14:52 ` [PATCH 1/4] dt-bindings: dma: Add bindings for ox810se dma engine Neil Armstrong
2022-01-04 14:52   ` Neil Armstrong
2022-01-12  1:20   ` Rob Herring
2022-01-12  1:20     ` Rob Herring
2022-01-04 14:52 ` [PATCH 2/4] dmaengine: Add Oxford Semiconductor OXNAS DMA Controller Neil Armstrong
2022-01-04 14:52   ` Neil Armstrong
2022-01-04 17:27   ` kernel test robot
2022-01-04 14:52 ` [PATCH 3/4] MAINTAINERS: add OX810SE DMA driver files under Oxnas entry Neil Armstrong
2022-01-04 14:52   ` Neil Armstrong
2022-01-04 14:52 ` [PATCH 4/4] ARM: dts: ox810se: Add DMA Support Neil Armstrong
2022-01-04 14:52   ` Neil Armstrong

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.