linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller
@ 2014-04-17  8:46 Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 1/8] clk: sunxi: Remove calls to clk_put Maxime Ripard
                   ` (7 more replies)
  0 siblings, 8 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Hi,

This patchset adds support for the DMA controller found in the
Allwinner A31 and A23 SoCs.

This has been tested using the newly introduced SPI driver on an A31
EVK. Support for DMA-driven SPI transfers will be the subject of
another patch serie.

Since this serie has been sent for the first time about 2 monthes ago,
and I never got any comments from the DMA maintainer, yet gathering
some Acked-by, I expect this to be merged quite quickly.

Thanks,
Maxime

Changes from v5:
  - Rebased on top of 3.15-rc1

Changes from v4:
  - Removed the packed attribute on the LLI
  - Switched to using a NULL device pointer in clk_get on PLL6 and
    AHB1 mux to make explicit that we are getting global clocks
  - Switched from spin_lock_irqsave to spin_lock in the interrupt
    handler
  - Various nitpicks from Andy Shevchenko:
    + Switched to using %p printk formats for pointers
    + Inverted some tests to lose a level of indentation
    + Dropped ifdef DEBUG protecting calls to dev_dbg

Changes from v3:
  - A few other comments made by Andy Shevchenko were fixed:
    + Used references in %pa* printk formats
    + Used is_slave_direction in prep_slave_sg to make sure we were
      actually called for something, and to avoid making assumptions
      that we were actually called with the expected directions
    + A few others minor fixes: s/pr_err/dev_err/, etc.

Changes from v2:
  - Removed the clk_put calls in the clock protection functions
  - Splitted out the sunxi machines into several files
  - Moved the clock protection code into these new machine files
  - Moved the PLL6 reparenting to the DMA driver
  - Addressed various comments from Andy Shevchenko: switched to using
    devm_kcalloc, used correct printk formats for physical and DMA
    addresses, etc.

Changes from v1:
  - Removed the clk_put call in the clocks protecting patches
  - Minor fixes here and there as suggested by Andy Shevchenko: switch
    to dmam_pool_create, switch to dev_dbg instead of pr_debug, etc.

Maxime Ripard (8):
  clk: sunxi: Remove calls to clk_put
  ARM: sunxi: Split out the various machines into separate files
  ARM: sunxi: Move the clock protection to machine hooks
  ARM: sun6i: Protect CPU clock
  ARM: sun6i: Protect SDRAM gating bit
  ARM: sun6i: Sort the NMI node by physical address
  DMA: sun6i: Add driver for the Allwinner A31 DMA controller
  ARM: sun6i: dt: Add A31 DMA controller to DTSI

 .../devicetree/bindings/dma/sun6i-dma.txt          |  45 +
 arch/arm/boot/dts/sun6i-a31.dtsi                   |  41 +-
 arch/arm/mach-sunxi/Makefile                       |   6 +-
 arch/arm/mach-sunxi/restart.c                      | 104 +++
 arch/arm/mach-sunxi/restart.h                      |  20 +
 arch/arm/mach-sunxi/sun4i.c                        |  45 +
 arch/arm/mach-sunxi/sun5i.c                        |  51 ++
 arch/arm/mach-sunxi/sun6i.c                        |  63 ++
 arch/arm/mach-sunxi/sun7i.c                        |  50 ++
 arch/arm/mach-sunxi/sunxi.c                        | 164 ----
 drivers/clk/sunxi/clk-sunxi.c                      |  28 -
 drivers/dma/Kconfig                                |   8 +
 drivers/dma/Makefile                               |   1 +
 drivers/dma/sun6i-dma.c                            | 987 +++++++++++++++++++++
 14 files changed, 1414 insertions(+), 199 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/dma/sun6i-dma.txt
 create mode 100644 arch/arm/mach-sunxi/restart.c
 create mode 100644 arch/arm/mach-sunxi/restart.h
 create mode 100644 arch/arm/mach-sunxi/sun4i.c
 create mode 100644 arch/arm/mach-sunxi/sun5i.c
 create mode 100644 arch/arm/mach-sunxi/sun6i.c
 create mode 100644 arch/arm/mach-sunxi/sun7i.c
 delete mode 100644 arch/arm/mach-sunxi/sunxi.c
 create mode 100644 drivers/dma/sun6i-dma.c

-- 
1.9.1


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

* [PATCH v6 1/8] clk: sunxi: Remove calls to clk_put
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files Maxime Ripard
                   ` (6 subsequent siblings)
  7 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Callers of clk_put must disable the clock first. This also means that as long
as the clock is enabled the driver should hold a reference to that clock.
Hence, the call to clk_put here are bogus and should be removed.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Mike Turquette <mturquette@linaro.org>
---
 drivers/clk/sunxi/clk-sunxi.c | 8 ++------
 1 file changed, 2 insertions(+), 6 deletions(-)

diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index bd7dc733c1ca..b1fde0f89bc1 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -1297,17 +1297,13 @@ static void __init sunxi_clock_protect(void)
 
 	/* memory bus clock - sun5i+ */
 	clk = clk_get(NULL, "mbus");
-	if (!IS_ERR(clk)) {
+	if (!IS_ERR(clk))
 		clk_prepare_enable(clk);
-		clk_put(clk);
-	}
 
 	/* DDR clock - sun4i+ */
 	clk = clk_get(NULL, "pll5_ddr");
-	if (!IS_ERR(clk)) {
+	if (!IS_ERR(clk))
 		clk_prepare_enable(clk);
-		clk_put(clk);
-	}
 }
 
 static void __init sunxi_init_clocks(void)
-- 
1.9.1


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

* [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 1/8] clk: sunxi: Remove calls to clk_put Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-23 12:33   ` Arnd Bergmann
  2014-04-17  8:46 ` [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks Maxime Ripard
                   ` (5 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

This will allow to add per-SoC hooks more easily.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-sunxi/Makefile  |   6 +-
 arch/arm/mach-sunxi/restart.c | 104 +++++++++++++++++++++++++++
 arch/arm/mach-sunxi/restart.h |  20 ++++++
 arch/arm/mach-sunxi/sun4i.c   |  36 ++++++++++
 arch/arm/mach-sunxi/sun5i.c   |  37 ++++++++++
 arch/arm/mach-sunxi/sun6i.c   |  49 +++++++++++++
 arch/arm/mach-sunxi/sun7i.c   |  36 ++++++++++
 arch/arm/mach-sunxi/sunxi.c   | 164 ------------------------------------------
 8 files changed, 287 insertions(+), 165 deletions(-)
 create mode 100644 arch/arm/mach-sunxi/restart.c
 create mode 100644 arch/arm/mach-sunxi/restart.h
 create mode 100644 arch/arm/mach-sunxi/sun4i.c
 create mode 100644 arch/arm/mach-sunxi/sun5i.c
 create mode 100644 arch/arm/mach-sunxi/sun6i.c
 create mode 100644 arch/arm/mach-sunxi/sun7i.c
 delete mode 100644 arch/arm/mach-sunxi/sunxi.c

diff --git a/arch/arm/mach-sunxi/Makefile b/arch/arm/mach-sunxi/Makefile
index 27b168f121a1..e0e555fededa 100644
--- a/arch/arm/mach-sunxi/Makefile
+++ b/arch/arm/mach-sunxi/Makefile
@@ -1,2 +1,6 @@
-obj-$(CONFIG_ARCH_SUNXI) += sunxi.o
+obj-$(CONFIG_ARCH_SUNXI) += restart.o
+obj-$(CONFIG_ARCH_SUNXI) += sun4i.o
+obj-$(CONFIG_ARCH_SUNXI) += sun5i.o
+obj-$(CONFIG_ARCH_SUNXI) += sun6i.o
+obj-$(CONFIG_ARCH_SUNXI) += sun7i.o
 obj-$(CONFIG_SMP) += platsmp.o
diff --git a/arch/arm/mach-sunxi/restart.c b/arch/arm/mach-sunxi/restart.c
new file mode 100644
index 000000000000..3b89727ee943
--- /dev/null
+++ b/arch/arm/mach-sunxi/restart.c
@@ -0,0 +1,104 @@
+/*
+ * Restart code for Allwinner SoCs
+ *
+ * Copyright (C) 2012-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/delay.h>
+#include <linux/io.h>
+#include <linux/mod_devicetable.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/reboot.h>
+
+#include "restart.h"
+
+#define SUN4I_WATCHDOG_CTRL_REG		0x00
+#define SUN4I_WATCHDOG_CTRL_RESTART		BIT(0)
+#define SUN4I_WATCHDOG_MODE_REG		0x04
+#define SUN4I_WATCHDOG_MODE_ENABLE		BIT(0)
+#define SUN4I_WATCHDOG_MODE_RESET_ENABLE	BIT(1)
+
+#define SUN6I_WATCHDOG1_IRQ_REG		0x00
+#define SUN6I_WATCHDOG1_CTRL_REG	0x10
+#define SUN6I_WATCHDOG1_CTRL_RESTART		BIT(0)
+#define SUN6I_WATCHDOG1_CONFIG_REG	0x14
+#define SUN6I_WATCHDOG1_CONFIG_RESTART		BIT(0)
+#define SUN6I_WATCHDOG1_CONFIG_IRQ		BIT(1)
+#define SUN6I_WATCHDOG1_MODE_REG	0x18
+#define SUN6I_WATCHDOG1_MODE_ENABLE		BIT(0)
+
+static void __iomem *wdt_base;
+
+void sun4i_restart(enum reboot_mode mode, const char *cmd)
+{
+	if (!wdt_base)
+		return;
+
+	/* Enable timer and set reset bit in the watchdog */
+	writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
+	       wdt_base + SUN4I_WATCHDOG_MODE_REG);
+
+	/*
+	 * Restart the watchdog. The default (and lowest) interval
+	 * value for the watchdog is 0.5s.
+	 */
+	writel(SUN4I_WATCHDOG_CTRL_RESTART, wdt_base + SUN4I_WATCHDOG_CTRL_REG);
+
+	while (1) {
+		mdelay(5);
+		writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
+		       wdt_base + SUN4I_WATCHDOG_MODE_REG);
+	}
+}
+
+void sun6i_restart(enum reboot_mode mode, const char *cmd)
+{
+	if (!wdt_base)
+		return;
+
+	/* Disable interrupts */
+	writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
+
+	/* We want to disable the IRQ and just reset the whole system */
+	writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
+		wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
+
+	/* Enable timer. The default and lowest interval value is 0.5s */
+	writel(SUN6I_WATCHDOG1_MODE_ENABLE,
+		wdt_base + SUN6I_WATCHDOG1_MODE_REG);
+
+	/* Restart the watchdog. */
+	writel(SUN6I_WATCHDOG1_CTRL_RESTART,
+		wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
+
+	while (1) {
+		mdelay(5);
+		writel(SUN6I_WATCHDOG1_MODE_ENABLE,
+			wdt_base + SUN6I_WATCHDOG1_MODE_REG);
+	}
+}
+
+static struct of_device_id sunxi_restart_ids[] = {
+	{ .compatible = "allwinner,sun4i-a10-wdt" },
+	{ .compatible = "allwinner,sun6i-a31-wdt" },
+	{ /*sentinel*/ }
+};
+
+void sunxi_setup_restart(void)
+{
+	struct device_node *np;
+
+	np = of_find_matching_node(NULL, sunxi_restart_ids);
+	if (WARN(!np, "unable to setup watchdog restart"))
+		return;
+
+	wdt_base = of_iomap(np, 0);
+	WARN(!wdt_base, "failed to map watchdog base address");
+}
diff --git a/arch/arm/mach-sunxi/restart.h b/arch/arm/mach-sunxi/restart.h
new file mode 100644
index 000000000000..ef039db8f544
--- /dev/null
+++ b/arch/arm/mach-sunxi/restart.h
@@ -0,0 +1,20 @@
+/*
+ * Restart code for Allwinner SoCs
+ *
+ * Copyright (C) 2012-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef _SUNXI_RESTART_H_
+#define _SUNXI_RESTART_H_
+
+void sun4i_restart(enum reboot_mode mode, const char *cmd);
+void sun6i_restart(enum reboot_mode mode, const char *cmd);
+void sunxi_setup_restart(void);
+
+#endif
diff --git a/arch/arm/mach-sunxi/sun4i.c b/arch/arm/mach-sunxi/sun4i.c
new file mode 100644
index 000000000000..fc28b89b3378
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun4i.c
@@ -0,0 +1,36 @@
+/*
+ * Device Tree support for Allwinner A10 SoCs
+ *
+ * Copyright (C) 2012-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "restart.h"
+
+static void __init sun4i_dt_init(void)
+{
+	sunxi_setup_restart();
+
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const sun4i_board_dt_compat[] = {
+	"allwinner,sun4i-a10",
+	NULL,
+};
+
+DT_MACHINE_START(SUN4I_DT, "Allwinner sun4i (A10) Family")
+	.init_machine	= sun4i_dt_init,
+	.dt_compat	= sun4i_board_dt_compat,
+	.restart	= sun4i_restart,
+MACHINE_END
diff --git a/arch/arm/mach-sunxi/sun5i.c b/arch/arm/mach-sunxi/sun5i.c
new file mode 100644
index 000000000000..623a95ad93b7
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun5i.c
@@ -0,0 +1,37 @@
+/*
+ * Device Tree support for Allwinner A20 SoCs
+ *
+ * Copyright (C) 2013-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "restart.h"
+
+static void __init sun5i_dt_init(void)
+{
+	sunxi_setup_restart();
+
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const sun5i_board_dt_compat[] = {
+	"allwinner,sun5i-a10s",
+	"allwinner,sun5i-a13",
+	NULL,
+};
+
+DT_MACHINE_START(SUN5I_DT, "Allwinner sun5i (A13/A10s) Family")
+	.init_machine	= sun5i_dt_init,
+	.dt_compat	= sun5i_board_dt_compat,
+	.restart	= sun4i_restart,
+MACHINE_END
diff --git a/arch/arm/mach-sunxi/sun6i.c b/arch/arm/mach-sunxi/sun6i.c
new file mode 100644
index 000000000000..c5dc81988ce9
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun6i.c
@@ -0,0 +1,49 @@
+/*
+ * Device Tree support for Allwinner A31 SoCs
+ *
+ * Copyright (C) 2013-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clocksource.h>
+#include <linux/init.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "common.h"
+#include "restart.h"
+
+static void __init sun6i_dt_init(void)
+{
+	sunxi_setup_restart();
+
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+extern void __init sun6i_reset_init(void);
+static void __init sun6i_timer_init(void)
+{
+	of_clk_init(NULL);
+	sun6i_reset_init();
+	clocksource_of_init();
+}
+
+static const char * const sun6i_board_dt_compat[] = {
+	"allwinner,sun6i-a31",
+	NULL,
+};
+
+DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family")
+	.init_machine	= sun6i_dt_init,
+	.init_time	= sun6i_timer_init,
+	.dt_compat	= sun6i_board_dt_compat,
+	.restart	= sun6i_restart,
+	.smp		= smp_ops(sun6i_smp_ops),
+MACHINE_END
diff --git a/arch/arm/mach-sunxi/sun7i.c b/arch/arm/mach-sunxi/sun7i.c
new file mode 100644
index 000000000000..2e6a8ee1966b
--- /dev/null
+++ b/arch/arm/mach-sunxi/sun7i.c
@@ -0,0 +1,36 @@
+/*
+ * Device Tree support for Allwinner A20 SoCs
+ *
+ * Copyright (C) 2013-2014 Maxime Ripard
+ *
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/init.h>
+#include <linux/of_platform.h>
+
+#include <asm/mach/arch.h>
+
+#include "restart.h"
+
+static void __init sun7i_dt_init(void)
+{
+	sunxi_setup_restart();
+
+	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+}
+
+static const char * const sun7i_board_dt_compat[] = {
+	"allwinner,sun7i-a20",
+	NULL,
+};
+
+DT_MACHINE_START(SUN7I_DT, "Allwinner sun7i (A20) Family")
+	.init_machine	= sun7i_dt_init,
+	.dt_compat	= sun7i_board_dt_compat,
+	.restart	= sun4i_restart,
+MACHINE_END
diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
deleted file mode 100644
index 460b5a4962ef..000000000000
--- a/arch/arm/mach-sunxi/sunxi.c
+++ /dev/null
@@ -1,164 +0,0 @@
-/*
- * Device Tree support for Allwinner A1X SoCs
- *
- * Copyright (C) 2012 Maxime Ripard
- *
- * Maxime Ripard <maxime.ripard@free-electrons.com>
- *
- * This file is licensed under the terms of the GNU General Public
- * License version 2.  This program is licensed "as is" without any
- * warranty of any kind, whether express or implied.
- */
-
-#include <linux/clk-provider.h>
-#include <linux/clocksource.h>
-#include <linux/delay.h>
-#include <linux/kernel.h>
-#include <linux/init.h>
-#include <linux/of_address.h>
-#include <linux/of_irq.h>
-#include <linux/of_platform.h>
-#include <linux/io.h>
-#include <linux/reboot.h>
-
-#include <asm/mach/arch.h>
-#include <asm/mach/map.h>
-#include <asm/system_misc.h>
-
-#include "common.h"
-
-#define SUN4I_WATCHDOG_CTRL_REG		0x00
-#define SUN4I_WATCHDOG_CTRL_RESTART		BIT(0)
-#define SUN4I_WATCHDOG_MODE_REG		0x04
-#define SUN4I_WATCHDOG_MODE_ENABLE		BIT(0)
-#define SUN4I_WATCHDOG_MODE_RESET_ENABLE	BIT(1)
-
-#define SUN6I_WATCHDOG1_IRQ_REG		0x00
-#define SUN6I_WATCHDOG1_CTRL_REG	0x10
-#define SUN6I_WATCHDOG1_CTRL_RESTART		BIT(0)
-#define SUN6I_WATCHDOG1_CONFIG_REG	0x14
-#define SUN6I_WATCHDOG1_CONFIG_RESTART		BIT(0)
-#define SUN6I_WATCHDOG1_CONFIG_IRQ		BIT(1)
-#define SUN6I_WATCHDOG1_MODE_REG	0x18
-#define SUN6I_WATCHDOG1_MODE_ENABLE		BIT(0)
-
-static void __iomem *wdt_base;
-
-static void sun4i_restart(enum reboot_mode mode, const char *cmd)
-{
-	if (!wdt_base)
-		return;
-
-	/* Enable timer and set reset bit in the watchdog */
-	writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
-	       wdt_base + SUN4I_WATCHDOG_MODE_REG);
-
-	/*
-	 * Restart the watchdog. The default (and lowest) interval
-	 * value for the watchdog is 0.5s.
-	 */
-	writel(SUN4I_WATCHDOG_CTRL_RESTART, wdt_base + SUN4I_WATCHDOG_CTRL_REG);
-
-	while (1) {
-		mdelay(5);
-		writel(SUN4I_WATCHDOG_MODE_ENABLE | SUN4I_WATCHDOG_MODE_RESET_ENABLE,
-		       wdt_base + SUN4I_WATCHDOG_MODE_REG);
-	}
-}
-
-static void sun6i_restart(enum reboot_mode mode, const char *cmd)
-{
-	if (!wdt_base)
-		return;
-
-	/* Disable interrupts */
-	writel(0, wdt_base + SUN6I_WATCHDOG1_IRQ_REG);
-
-	/* We want to disable the IRQ and just reset the whole system */
-	writel(SUN6I_WATCHDOG1_CONFIG_RESTART,
-		wdt_base + SUN6I_WATCHDOG1_CONFIG_REG);
-
-	/* Enable timer. The default and lowest interval value is 0.5s */
-	writel(SUN6I_WATCHDOG1_MODE_ENABLE,
-		wdt_base + SUN6I_WATCHDOG1_MODE_REG);
-
-	/* Restart the watchdog. */
-	writel(SUN6I_WATCHDOG1_CTRL_RESTART,
-		wdt_base + SUN6I_WATCHDOG1_CTRL_REG);
-
-	while (1) {
-		mdelay(5);
-		writel(SUN6I_WATCHDOG1_MODE_ENABLE,
-			wdt_base + SUN6I_WATCHDOG1_MODE_REG);
-	}
-}
-
-static struct of_device_id sunxi_restart_ids[] = {
-	{ .compatible = "allwinner,sun4i-a10-wdt" },
-	{ .compatible = "allwinner,sun6i-a31-wdt" },
-	{ /*sentinel*/ }
-};
-
-static void sunxi_setup_restart(void)
-{
-	struct device_node *np;
-
-	np = of_find_matching_node(NULL, sunxi_restart_ids);
-	if (WARN(!np, "unable to setup watchdog restart"))
-		return;
-
-	wdt_base = of_iomap(np, 0);
-	WARN(!wdt_base, "failed to map watchdog base address");
-}
-
-static void __init sunxi_dt_init(void)
-{
-	sunxi_setup_restart();
-
-	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
-}
-
-static const char * const sunxi_board_dt_compat[] = {
-	"allwinner,sun4i-a10",
-	"allwinner,sun5i-a10s",
-	"allwinner,sun5i-a13",
-	NULL,
-};
-
-DT_MACHINE_START(SUNXI_DT, "Allwinner A1X (Device Tree)")
-	.init_machine	= sunxi_dt_init,
-	.dt_compat	= sunxi_board_dt_compat,
-	.restart	= sun4i_restart,
-MACHINE_END
-
-static const char * const sun6i_board_dt_compat[] = {
-	"allwinner,sun6i-a31",
-	NULL,
-};
-
-extern void __init sun6i_reset_init(void);
-static void __init sun6i_timer_init(void)
-{
-	of_clk_init(NULL);
-	sun6i_reset_init();
-	clocksource_of_init();
-}
-
-DT_MACHINE_START(SUN6I_DT, "Allwinner sun6i (A31) Family")
-	.init_machine	= sunxi_dt_init,
-	.init_time	= sun6i_timer_init,
-	.dt_compat	= sun6i_board_dt_compat,
-	.restart	= sun6i_restart,
-	.smp		= smp_ops(sun6i_smp_ops),
-MACHINE_END
-
-static const char * const sun7i_board_dt_compat[] = {
-	"allwinner,sun7i-a20",
-	NULL,
-};
-
-DT_MACHINE_START(SUN7I_DT, "Allwinner sun7i (A20) Family")
-	.init_machine	= sunxi_dt_init,
-	.dt_compat	= sun7i_board_dt_compat,
-	.restart	= sun4i_restart,
-MACHINE_END
-- 
1.9.1


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

* [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 1/8] clk: sunxi: Remove calls to clk_put Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-23 12:39   ` Arnd Bergmann
  2014-04-17  8:46 ` [PATCH v6 4/8] ARM: sun6i: Protect CPU clock Maxime Ripard
                   ` (4 subsequent siblings)
  7 siblings, 1 reply; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Since we start to have a lot of clocks to protect, some of them in a few boards
only, it becomes difficult to handle the clock protection without having to add
per machine exceptions.

Move these where they belong, in the machine definition code.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-sunxi/sun4i.c   |  9 +++++++++
 arch/arm/mach-sunxi/sun5i.c   | 14 ++++++++++++++
 arch/arm/mach-sunxi/sun7i.c   | 14 ++++++++++++++
 drivers/clk/sunxi/clk-sunxi.c | 24 ------------------------
 4 files changed, 37 insertions(+), 24 deletions(-)

diff --git a/arch/arm/mach-sunxi/sun4i.c b/arch/arm/mach-sunxi/sun4i.c
index fc28b89b3378..3276e63587fb 100644
--- a/arch/arm/mach-sunxi/sun4i.c
+++ b/arch/arm/mach-sunxi/sun4i.c
@@ -10,6 +10,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/init.h>
 #include <linux/of_platform.h>
 
@@ -19,9 +20,17 @@
 
 static void __init sun4i_dt_init(void)
 {
+	struct clk *clk;
+
 	sunxi_setup_restart();
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+	/* Make sure the clocks we absolutely need are enabled */
+	/* DDR clock */
+	clk = clk_get(NULL, "pll5_ddr");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
 }
 
 static const char * const sun4i_board_dt_compat[] = {
diff --git a/arch/arm/mach-sunxi/sun5i.c b/arch/arm/mach-sunxi/sun5i.c
index 623a95ad93b7..990dcfd42681 100644
--- a/arch/arm/mach-sunxi/sun5i.c
+++ b/arch/arm/mach-sunxi/sun5i.c
@@ -10,6 +10,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/init.h>
 #include <linux/of_platform.h>
 
@@ -19,9 +20,22 @@
 
 static void __init sun5i_dt_init(void)
 {
+	struct clk *clk;
+
 	sunxi_setup_restart();
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+	/* Make sure the clocks we absolutely need are enabled */
+	/* Memory bus clock */
+	clk = clk_get(NULL, "mbus");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
+
+	/* DDR clock */
+	clk = clk_get(NULL, "pll5_ddr");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
 }
 
 static const char * const sun5i_board_dt_compat[] = {
diff --git a/arch/arm/mach-sunxi/sun7i.c b/arch/arm/mach-sunxi/sun7i.c
index 2e6a8ee1966b..48a090b91a90 100644
--- a/arch/arm/mach-sunxi/sun7i.c
+++ b/arch/arm/mach-sunxi/sun7i.c
@@ -10,6 +10,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/init.h>
 #include <linux/of_platform.h>
 
@@ -19,9 +20,22 @@
 
 static void __init sun7i_dt_init(void)
 {
+	struct clk *clk;
+
 	sunxi_setup_restart();
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+	/* Make sure the clocks we absolutely need are enabled */
+	/* Memory bus clock */
+	clk = clk_get(NULL, "mbus");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
+
+	/* DDR clock */
+	clk = clk_get(NULL, "pll5_ddr");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
 }
 
 static const char * const sun7i_board_dt_compat[] = {
diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c
index b1fde0f89bc1..b06200079f06 100644
--- a/drivers/clk/sunxi/clk-sunxi.c
+++ b/drivers/clk/sunxi/clk-sunxi.c
@@ -1285,27 +1285,6 @@ static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_mat
 	}
 }
 
-/**
- * System clock protection
- *
- * By enabling these critical clocks, we prevent their accidental gating
- * by the framework
- */
-static void __init sunxi_clock_protect(void)
-{
-	struct clk *clk;
-
-	/* memory bus clock - sun5i+ */
-	clk = clk_get(NULL, "mbus");
-	if (!IS_ERR(clk))
-		clk_prepare_enable(clk);
-
-	/* DDR clock - sun4i+ */
-	clk = clk_get(NULL, "pll5_ddr");
-	if (!IS_ERR(clk))
-		clk_prepare_enable(clk);
-}
-
 static void __init sunxi_init_clocks(void)
 {
 	/* Register factor clocks */
@@ -1322,9 +1301,6 @@ static void __init sunxi_init_clocks(void)
 
 	/* Register gate clocks */
 	of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup);
-
-	/* Enable core system clocks */
-	sunxi_clock_protect();
 }
 CLK_OF_DECLARE(sun4i_a10_clk_init, "allwinner,sun4i-a10", sunxi_init_clocks);
 CLK_OF_DECLARE(sun5i_a10s_clk_init, "allwinner,sun5i-a10s", sunxi_init_clocks);
-- 
1.9.1


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

* [PATCH v6 4/8] ARM: sun6i: Protect CPU clock
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
                   ` (2 preceding siblings ...)
  2014-04-17  8:46 ` [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 5/8] ARM: sun6i: Protect SDRAM gating bit Maxime Ripard
                   ` (3 subsequent siblings)
  7 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Right now, AHB is an indirect child clock of the CPU clock. If that happens to
change, since the CPU clock has no other consumers declared in Linux, it would
be shut down, which is not really a good idea.

Prevent this by forcing it enabled.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-sunxi/sun6i.c | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/arch/arm/mach-sunxi/sun6i.c b/arch/arm/mach-sunxi/sun6i.c
index c5dc81988ce9..6b3905505fe0 100644
--- a/arch/arm/mach-sunxi/sun6i.c
+++ b/arch/arm/mach-sunxi/sun6i.c
@@ -10,6 +10,7 @@
  * warranty of any kind, whether express or implied.
  */
 
+#include <linux/clk.h>
 #include <linux/clk-provider.h>
 #include <linux/clocksource.h>
 #include <linux/init.h>
@@ -22,9 +23,17 @@
 
 static void __init sun6i_dt_init(void)
 {
+	struct clk *clk;
+
 	sunxi_setup_restart();
 
 	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+
+	/* Make sure the clocks we absolutely need are enabled */
+	/* CPU clock */
+	clk = clk_get(NULL, "cpu");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
 }
 
 extern void __init sun6i_reset_init(void);
-- 
1.9.1


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

* [PATCH v6 5/8] ARM: sun6i: Protect SDRAM gating bit
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
                   ` (3 preceding siblings ...)
  2014-04-17  8:46 ` [PATCH v6 4/8] ARM: sun6i: Protect CPU clock Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address Maxime Ripard
                   ` (2 subsequent siblings)
  7 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Prevent the SDRAM controller from being gated by force-enabling it in the
machine code.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/mach-sunxi/sun6i.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/mach-sunxi/sun6i.c b/arch/arm/mach-sunxi/sun6i.c
index 6b3905505fe0..1ccb27524a9e 100644
--- a/arch/arm/mach-sunxi/sun6i.c
+++ b/arch/arm/mach-sunxi/sun6i.c
@@ -34,6 +34,11 @@ static void __init sun6i_dt_init(void)
 	clk = clk_get(NULL, "cpu");
 	if (!IS_ERR(clk))
 		clk_prepare_enable(clk);
+
+	/* DDR gating clock */
+	clk = clk_get(NULL, "ahb1_sdram");
+	if (!IS_ERR(clk))
+		clk_prepare_enable(clk);
 }
 
 extern void __init sun6i_reset_init(void);
-- 
1.9.1


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

* [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
                   ` (4 preceding siblings ...)
  2014-04-17  8:46 ` [PATCH v6 5/8] ARM: sun6i: Protect SDRAM gating bit Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-23 12:39   ` Arnd Bergmann
  2014-04-17  8:46 ` [PATCH v6 7/8] DMA: sun6i: Add driver for the Allwinner A31 DMA controller Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 8/8] ARM: sun6i: dt: Add A31 DMA controller to DTSI Maxime Ripard
  7 siblings, 1 reply; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

The DT are supposed to be ordered by physical address. Move the NMI node where
it belongs.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 16 ++++++++--------
 1 file changed, 8 insertions(+), 8 deletions(-)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index bc46814d2ff0..5ebf963464b9 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -237,14 +237,6 @@
 		#size-cells = <1>;
 		ranges;
 
-		nmi_intc: interrupt-controller@01f00c0c {
-			compatible = "allwinner,sun6i-a31-sc-nmi";
-			interrupt-controller;
-			#interrupt-cells = <2>;
-			reg = <0x01f00c0c 0x38>;
-			interrupts = <0 32 4>;
-		};
-
 		pio: pinctrl@01c20800 {
 			compatible = "allwinner,sun6i-a31-pinctrl";
 			reg = <0x01c20800 0x400>;
@@ -479,6 +471,14 @@
 			interrupts = <1 9 0xf04>;
 		};
 
+		nmi_intc: interrupt-controller@01f00c0c {
+			compatible = "allwinner,sun6i-a31-sc-nmi";
+			interrupt-controller;
+			#interrupt-cells = <2>;
+			reg = <0x01f00c0c 0x38>;
+			interrupts = <0 32 4>;
+		};
+
 		prcm@01f01400 {
 			compatible = "allwinner,sun6i-a31-prcm";
 			reg = <0x01f01400 0x200>;
-- 
1.9.1


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

* [PATCH v6 7/8] DMA: sun6i: Add driver for the Allwinner A31 DMA controller
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
                   ` (5 preceding siblings ...)
  2014-04-17  8:46 ` [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  2014-04-17  8:46 ` [PATCH v6 8/8] ARM: sun6i: dt: Add A31 DMA controller to DTSI Maxime Ripard
  7 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

The Allwinner A31 has a 16 channels DMA controller that it shares with the
newer A23. Although sharing some similarities with the DMA controller of the
older Allwinner SoCs, it's significantly different, I don't expect it to be
possible to share the driver for these two.

The A31 Controller is able to memory-to-memory or memory-to-device transfers on
the 16 channels in parallel.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
Acked-by: Arnd Bergmann <arnd@arndb.de>
---
 .../devicetree/bindings/dma/sun6i-dma.txt          |  45 +
 drivers/dma/Kconfig                                |   8 +
 drivers/dma/Makefile                               |   1 +
 drivers/dma/sun6i-dma.c                            | 987 +++++++++++++++++++++
 4 files changed, 1041 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/dma/sun6i-dma.txt
 create mode 100644 drivers/dma/sun6i-dma.c

diff --git a/Documentation/devicetree/bindings/dma/sun6i-dma.txt b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
new file mode 100644
index 000000000000..5d7c86d52665
--- /dev/null
+++ b/Documentation/devicetree/bindings/dma/sun6i-dma.txt
@@ -0,0 +1,45 @@
+Allwinner A31 DMA Controller
+
+This driver follows the generic DMA bindings defined in dma.txt.
+
+Required properties:
+
+- compatible:	Must be "allwinner,sun6i-a31-dma"
+- reg:		Should contain the registers base address and length
+- interrupts:	Should contain a reference to the interrupt used by this device
+- clocks:	Should contain a reference to the parent AHB clock
+- resets:	Should contain a reference to the reset controller asserting
+	  	this device in reset
+- #dma-cells :	Should be 1, a single cell holding a line request number
+
+Example:
+	dma: dma-controller@01c02000 {
+		compatible = "allwinner,sun6i-a31-dma";
+		reg = <0x01c02000 0x1000>;
+		interrupts = <0 50 4>;
+		clocks = <&ahb1_gates 6>;
+		resets = <&ahb1_rst 6>;
+		#dma-cells = <1>;
+	};
+
+Clients:
+
+DMA clients connected to the A31 DMA controller must use the format
+described in the dma.txt file, using a two-cell specifier for each
+channel: a phandle plus one integer cells.
+The two cells in order are:
+
+1. A phandle pointing to the DMA controller.
+2. The port ID as specified in the datasheet
+
+Example:
+spi2: spi@01c6a000 {
+	compatible = "allwinner,sun6i-a31-spi";
+	reg = <0x01c6a000 0x1000>;
+	interrupts = <0 67 4>;
+	clocks = <&ahb1_gates 22>, <&spi2_clk>;
+	clock-names = "ahb", "mod";
+	dmas = <&dma 25>, <&dma 25>;
+	dma-names = "rx", "tx";
+	resets = <&ahb1_rst 22>;
+};
diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig
index ba06d1d2f99e..2300c0635d9a 100644
--- a/drivers/dma/Kconfig
+++ b/drivers/dma/Kconfig
@@ -361,6 +361,14 @@ config FSL_EDMA
 	  multiplexing capability for DMA request sources(slot).
 	  This module can be found on Freescale Vybrid and LS-1 SoCs.
 
+config DMA_SUN6I
+	tristate "Allwinner A31 SoCs DMA support"
+	depends on ARCH_SUNXI
+	select DMA_ENGINE
+	select DMA_VIRTUAL_CHANNELS
+	help
+	  Support for the DMA engine for Allwinner A31 SoCs.
+
 config DMA_ENGINE
 	bool
 
diff --git a/drivers/dma/Makefile b/drivers/dma/Makefile
index 5150c82c9caf..136a86f20c67 100644
--- a/drivers/dma/Makefile
+++ b/drivers/dma/Makefile
@@ -46,3 +46,4 @@ obj-$(CONFIG_K3_DMA) += k3dma.o
 obj-$(CONFIG_MOXART_DMA) += moxart-dma.o
 obj-$(CONFIG_FSL_EDMA) += fsl-edma.o
 obj-$(CONFIG_QCOM_BAM_DMA) += qcom_bam_dma.o
+obj-$(CONFIG_DMA_SUN6I) += sun6i-dma.o
diff --git a/drivers/dma/sun6i-dma.c b/drivers/dma/sun6i-dma.c
new file mode 100644
index 000000000000..b9db8594565c
--- /dev/null
+++ b/drivers/dma/sun6i-dma.c
@@ -0,0 +1,987 @@
+/*
+ * Copyright (C) 2013-2014 Allwinner Tech Co., Ltd
+ * Author: Sugar <shuge@allwinnertech.com>
+ *
+ * Copyright (C) 2014 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/dmaengine.h>
+#include <linux/dmapool.h>
+#include <linux/interrupt.h>
+#include <linux/module.h>
+#include <linux/of_dma.h>
+#include <linux/platform_device.h>
+#include <linux/reset.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+
+#include "virt-dma.h"
+
+/*
+ * There's 16 physical channels that can work in parallel.
+ *
+ * However we have 30 different endpoints for our requests.
+ *
+ * Since the channels are able to handle only an unidirectional
+ * transfer, we need to allocate more virtual channels so that
+ * everyone can grab one channel.
+ *
+ * Some devices can't work in both direction (mostly because it
+ * wouldn't make sense), so we have a bit fewer virtual channels than
+ * 2 channels per endpoints.
+ */
+
+#define NR_MAX_CHANNELS		16
+#define NR_MAX_REQUESTS		30
+#define NR_MAX_VCHANS		53
+
+/*
+ * Common registers
+ */
+#define DMA_IRQ_EN(x)		((x) * 0x04)
+#define DMA_IRQ_HALF			BIT(0)
+#define DMA_IRQ_PKG			BIT(1)
+#define DMA_IRQ_QUEUE			BIT(2)
+
+#define DMA_IRQ_CHAN_NR			8
+#define DMA_IRQ_CHAN_WIDTH		4
+
+
+#define DMA_IRQ_STAT(x)		((x) * 0x04 + 0x10)
+
+#define DMA_STAT		0x30
+
+/*
+ * Channels specific registers
+ */
+#define DMA_CHAN_ENABLE		0x00
+#define DMA_CHAN_ENABLE_START		BIT(0)
+#define DMA_CHAN_ENABLE_STOP		0
+
+#define DMA_CHAN_PAUSE		0x04
+#define DMA_CHAN_PAUSE_PAUSE		BIT(1)
+#define DMA_CHAN_PAUSE_RESUME		0
+
+#define DMA_CHAN_LLI_ADDR	0x08
+
+#define DMA_CHAN_CUR_CFG	0x0c
+#define DMA_CHAN_CFG_SRC_DRQ(x)		((x) & 0x1f)
+#define DMA_CHAN_CFG_SRC_IO_MODE	BIT(5)
+#define DMA_CHAN_CFG_SRC_LINEAR_MODE	(0 << 5)
+#define DMA_CHAN_CFG_SRC_BURST(x)	(((x) & 0x3) << 7)
+#define DMA_CHAN_CFG_SRC_WIDTH(x)	(((x) & 0x3) << 9)
+
+#define DMA_CHAN_CFG_DST_DRQ(x)		(DMA_CHAN_CFG_SRC_DRQ(x) << 16)
+#define DMA_CHAN_CFG_DST_IO_MODE	(DMA_CHAN_CFG_SRC_IO_MODE << 16)
+#define DMA_CHAN_CFG_DST_LINEAR_MODE	(DMA_CHAN_CFG_SRC_LINEAR_MODE << 16)
+#define DMA_CHAN_CFG_DST_BURST(x)	(DMA_CHAN_CFG_SRC_BURST(x) << 16)
+#define DMA_CHAN_CFG_DST_WIDTH(x)	(DMA_CHAN_CFG_SRC_WIDTH(x) << 16)
+
+#define DMA_CHAN_CUR_SRC	0x10
+
+#define DMA_CHAN_CUR_DST	0x14
+
+#define DMA_CHAN_CUR_CNT	0x18
+
+#define DMA_CHAN_CUR_PARA	0x1c
+
+
+/*
+ * Various hardware related defines
+ */
+#define LLI_LAST_ITEM	0xfffff800
+#define NORMAL_WAIT	8
+#define DRQ_SDRAM	1
+
+/*
+ * Hardware representation of the LLI
+ *
+ * The hardware will be fed the physical address of this structure,
+ * and read its content in order to start the transfer.
+ */
+struct sun6i_dma_lli {
+	u32			cfg;
+	u32			src;
+	u32			dst;
+	u32			len;
+	u32			para;
+	u32			p_lli_next;
+
+	/*
+	 * This field is not used by the DMA controller, but will be
+	 * used by the CPU to go through the list (mostly for dumping
+	 * or freeing it).
+	 */
+	struct sun6i_dma_lli	*v_lli_next;
+};
+
+
+struct sun6i_desc {
+	struct virt_dma_desc	vd;
+	dma_addr_t		p_lli;
+	struct sun6i_dma_lli	*v_lli;
+};
+
+struct sun6i_pchan {
+	u32			idx;
+	void __iomem		*base;
+	struct sun6i_vchan	*vchan;
+	struct sun6i_desc	*desc;
+	struct sun6i_desc	*done;
+};
+
+struct sun6i_vchan {
+	struct virt_dma_chan	vc;
+	struct list_head	node;
+	struct dma_slave_config	cfg;
+	struct sun6i_pchan	*phy;
+	u8			port;
+};
+
+struct sun6i_dma_dev {
+	struct dma_device	slave;
+	void __iomem		*base;
+	struct clk		*clk;
+	struct reset_control	*rstc;
+	spinlock_t		lock;
+	struct tasklet_struct	task;
+	struct list_head	pending;
+	struct dma_pool		*pool;
+	struct sun6i_pchan	*pchans;
+	struct sun6i_vchan	*vchans;
+};
+
+static struct device *chan2dev(struct dma_chan *chan)
+{
+	return &chan->dev->device;
+}
+
+static inline struct sun6i_dma_dev *to_sun6i_dma_dev(struct dma_device *d)
+{
+	return container_of(d, struct sun6i_dma_dev, slave);
+}
+
+static inline struct sun6i_vchan *to_sun6i_vchan(struct dma_chan *chan)
+{
+	return container_of(chan, struct sun6i_vchan, vc.chan);
+}
+
+static inline struct sun6i_desc *
+to_sun6i_desc(struct dma_async_tx_descriptor *tx)
+{
+	return container_of(tx, struct sun6i_desc, vd.tx);
+}
+
+static inline void sun6i_dma_dump_com_regs(struct sun6i_dma_dev *sdev)
+{
+	dev_dbg(sdev->slave.dev, "Common register:\n"
+		"\tmask0(%04x): 0x%08x\n"
+		"\tmask1(%04x): 0x%08x\n"
+		"\tpend0(%04x): 0x%08x\n"
+		"\tpend1(%04x): 0x%08x\n"
+		"\tstats(%04x): 0x%08x\n",
+		DMA_IRQ_EN(0), readl(sdev->base + DMA_IRQ_EN(0)),
+		DMA_IRQ_EN(1), readl(sdev->base + DMA_IRQ_EN(1)),
+		DMA_IRQ_STAT(0), readl(sdev->base + DMA_IRQ_STAT(0)),
+		DMA_IRQ_STAT(1), readl(sdev->base + DMA_IRQ_STAT(1)),
+		DMA_STAT, readl(sdev->base + DMA_STAT));
+}
+
+static inline void sun6i_dma_dump_chan_regs(struct sun6i_dma_dev *sdev,
+					    struct sun6i_pchan *pchan)
+{
+	phys_addr_t reg = __virt_to_phys((unsigned long)pchan->base);
+
+	dev_dbg(sdev->slave.dev, "Chan %d reg: %pa\n"
+		"\t___en(%04x): \t0x%08x\n"
+		"\tpause(%04x): \t0x%08x\n"
+		"\tstart(%04x): \t0x%08x\n"
+		"\t__cfg(%04x): \t0x%08x\n"
+		"\t__src(%04x): \t0x%08x\n"
+		"\t__dst(%04x): \t0x%08x\n"
+		"\tcount(%04x): \t0x%08x\n"
+		"\t_para(%04x): \t0x%08x\n\n",
+		pchan->idx, &reg,
+		DMA_CHAN_ENABLE,
+		readl(pchan->base + DMA_CHAN_ENABLE),
+		DMA_CHAN_PAUSE,
+		readl(pchan->base + DMA_CHAN_PAUSE),
+		DMA_CHAN_LLI_ADDR,
+		readl(pchan->base + DMA_CHAN_LLI_ADDR),
+		DMA_CHAN_CUR_CFG,
+		readl(pchan->base + DMA_CHAN_CUR_CFG),
+		DMA_CHAN_CUR_SRC,
+		readl(pchan->base + DMA_CHAN_CUR_SRC),
+		DMA_CHAN_CUR_DST,
+		readl(pchan->base + DMA_CHAN_CUR_DST),
+		DMA_CHAN_CUR_CNT,
+		readl(pchan->base + DMA_CHAN_CUR_CNT),
+		DMA_CHAN_CUR_PARA,
+		readl(pchan->base + DMA_CHAN_CUR_PARA));
+}
+
+static inline u8 convert_burst(u8 maxburst)
+{
+	if (maxburst == 1 || maxburst > 16)
+		return 0;
+
+	return fls(maxburst) - 1;
+}
+
+static inline u8 convert_buswidth(enum dma_slave_buswidth addr_width)
+{
+	switch (addr_width) {
+	case DMA_SLAVE_BUSWIDTH_2_BYTES:
+		return 1;
+	case DMA_SLAVE_BUSWIDTH_4_BYTES:
+		return 2;
+	default:
+		return 0;
+	}
+}
+
+static void *sun6i_dma_lli_add(struct sun6i_dma_lli *prev,
+			       struct sun6i_dma_lli *next,
+			       dma_addr_t next_phy,
+			       struct sun6i_desc *txd)
+{
+	if ((!prev && !txd) || !next)
+		return NULL;
+
+	if (!prev) {
+		txd->p_lli = next_phy;
+		txd->v_lli = next;
+	} else {
+		prev->p_lli_next = next_phy;
+		prev->v_lli_next = next;
+	}
+
+	next->p_lli_next = LLI_LAST_ITEM;
+	next->v_lli_next = NULL;
+
+	return next;
+}
+
+static inline void sun6i_dma_cfg_lli(struct sun6i_dma_lli *lli,
+				     dma_addr_t src,
+				     dma_addr_t dst, u32 len,
+				     struct dma_slave_config *config)
+{
+	u32 src_width, dst_width, src_burst, dst_burst;
+
+	if (!config)
+		return;
+
+	src_burst = convert_burst(config->src_maxburst);
+	dst_burst = convert_burst(config->dst_maxburst);
+
+	src_width = convert_buswidth(config->src_addr_width);
+	dst_width = convert_buswidth(config->dst_addr_width);
+
+	lli->cfg = DMA_CHAN_CFG_SRC_BURST(src_burst) |
+		DMA_CHAN_CFG_SRC_WIDTH(src_width) |
+		DMA_CHAN_CFG_DST_BURST(dst_burst) |
+		DMA_CHAN_CFG_DST_WIDTH(dst_width);
+
+	lli->src = src;
+	lli->dst = dst;
+	lli->len = len;
+	lli->para = NORMAL_WAIT;
+}
+
+static inline void sun6i_dma_dump_lli(struct sun6i_vchan *vchan,
+				      struct sun6i_dma_lli *lli)
+{
+	phys_addr_t p_lli = __virt_to_phys((unsigned long)lli);
+
+	dev_dbg(chan2dev(&vchan->vc.chan),
+		"\n\tdesc:   p - %pa v - 0x%p\n"
+		"\t\tc - 0x%08x s - 0x%08x d - 0x%08x\n"
+		"\t\tl - 0x%08x p - 0x%08x n - 0x%08x\n",
+		&p_lli, lli,
+		lli->cfg, lli->src, lli->dst,
+		lli->len, lli->para, lli->p_lli_next);
+}
+
+static void sun6i_dma_free_desc(struct virt_dma_desc *vd)
+{
+	struct sun6i_desc *txd = to_sun6i_desc(&vd->tx);
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(vd->tx.chan->device);
+	struct sun6i_dma_lli *v_lli, *v_next;
+	dma_addr_t p_lli, p_next;
+
+	if (unlikely(!txd))
+		return;
+
+	p_lli = txd->p_lli;
+	v_lli = txd->v_lli;
+
+	while (v_lli) {
+		v_next = v_lli->v_lli_next;
+		p_next = v_lli->p_lli_next;
+
+		dma_pool_free(sdev->pool, v_lli, p_lli);
+
+		v_lli = v_next;
+		p_lli = p_next;
+	}
+
+	kfree(txd);
+}
+
+static int sun6i_dma_terminate_all(struct sun6i_vchan *vchan)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(vchan->vc.chan.device);
+	struct sun6i_pchan *pchan = vchan->phy;
+	unsigned long flags;
+	LIST_HEAD(head);
+
+	spin_lock(&sdev->lock);
+	list_del_init(&vchan->node);
+	spin_unlock(&sdev->lock);
+
+	spin_lock_irqsave(&vchan->vc.lock, flags);
+
+	vchan_get_all_descriptors(&vchan->vc, &head);
+
+	if (pchan) {
+		writel(DMA_CHAN_ENABLE_STOP, pchan->base + DMA_CHAN_ENABLE);
+		writel(DMA_CHAN_PAUSE_RESUME, pchan->base + DMA_CHAN_PAUSE);
+
+		vchan->phy = NULL;
+		pchan->vchan = NULL;
+		pchan->desc = NULL;
+		pchan->done = NULL;
+	}
+
+	spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+	vchan_dma_desc_free_list(&vchan->vc, &head);
+
+	return 0;
+}
+
+static int sun6i_dma_start_desc(struct sun6i_vchan *vchan)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(vchan->vc.chan.device);
+	struct virt_dma_desc *desc = vchan_next_desc(&vchan->vc);
+	struct sun6i_pchan *pchan = vchan->phy;
+	u32 irq_val, irq_reg, irq_offset;
+
+	if (!pchan)
+		return -EAGAIN;
+
+	if (!desc) {
+		pchan->desc = NULL;
+		pchan->done = NULL;
+		return -EAGAIN;
+	}
+
+	list_del(&desc->node);
+
+	pchan->desc = to_sun6i_desc(&desc->tx);
+	pchan->done = NULL;
+
+	sun6i_dma_dump_lli(vchan, pchan->desc->v_lli);
+
+	irq_reg = pchan->idx / DMA_IRQ_CHAN_NR;
+	irq_offset = pchan->idx % DMA_IRQ_CHAN_NR;
+
+	irq_val = readl(sdev->base + DMA_IRQ_EN(irq_offset));
+	irq_val |= DMA_IRQ_QUEUE << (irq_offset * DMA_IRQ_CHAN_WIDTH);
+	writel(irq_val, sdev->base + DMA_IRQ_EN(irq_offset));
+
+	writel(pchan->desc->p_lli, pchan->base + DMA_CHAN_LLI_ADDR);
+	writel(DMA_CHAN_ENABLE_START, pchan->base + DMA_CHAN_ENABLE);
+
+	sun6i_dma_dump_com_regs(sdev);
+	sun6i_dma_dump_chan_regs(sdev, pchan);
+
+	return 0;
+}
+
+static void sun6i_dma_tasklet(unsigned long data)
+{
+	struct sun6i_dma_dev *sdev = (struct sun6i_dma_dev *)data;
+	struct sun6i_vchan *vchan;
+	struct sun6i_pchan *pchan;
+	unsigned int pchan_alloc = 0;
+	unsigned int pchan_idx;
+
+	list_for_each_entry(vchan, &sdev->slave.channels, vc.chan.device_node) {
+		spin_lock_irq(&vchan->vc.lock);
+
+		pchan = vchan->phy;
+
+		if (pchan && pchan->done) {
+			if (sun6i_dma_start_desc(vchan)) {
+				/*
+				 * No current txd associated with this channel
+				 */
+				dev_dbg(sdev->slave.dev, "pchan %u: free\n",
+					pchan->idx);
+
+				/* Mark this channel free */
+				vchan->phy = NULL;
+				pchan->vchan = NULL;
+			}
+		}
+		spin_unlock_irq(&vchan->vc.lock);
+	}
+
+	spin_lock_irq(&sdev->lock);
+	for (pchan_idx = 0; pchan_idx < NR_MAX_CHANNELS; pchan_idx++) {
+		pchan = &sdev->pchans[pchan_idx];
+
+		if (pchan->vchan || list_empty(&sdev->pending))
+			continue;
+
+		vchan = list_first_entry(&sdev->pending,
+					 struct sun6i_vchan, node);
+
+		/* Remove from pending channels */
+		list_del_init(&vchan->node);
+		pchan_alloc |= BIT(pchan_idx);
+
+		/* Mark this channel allocated */
+		pchan->vchan = vchan;
+		vchan->phy = pchan;
+		dev_dbg(sdev->slave.dev, "pchan %u: alloc vchan %p\n",
+			pchan->idx, &vchan->vc);
+	}
+	spin_unlock_irq(&sdev->lock);
+
+	for (pchan_idx = 0; pchan_idx < NR_MAX_CHANNELS; pchan_idx++) {
+		if (!(pchan_alloc & BIT(pchan_idx)))
+			continue;
+
+		pchan = sdev->pchans + pchan_idx;
+		vchan = pchan->vchan;
+		if (vchan) {
+			spin_lock_irq(&vchan->vc.lock);
+			sun6i_dma_start_desc(vchan);
+			spin_unlock_irq(&vchan->vc.lock);
+		}
+	}
+}
+
+static irqreturn_t sun6i_dma_interrupt(int irq, void *dev_id)
+{
+	struct sun6i_dma_dev *sdev = dev_id;
+	struct sun6i_vchan *vchan;
+	struct sun6i_pchan *pchan;
+	int i, j, ret = IRQ_NONE;
+	u32 status;
+
+	for (i = 0; i < 2; i++) {
+		status = readl(sdev->base + DMA_IRQ_STAT(i));
+		if (!status)
+			continue;
+
+		dev_dbg(sdev->slave.dev, "DMA irq status %s: 0x%x\n",
+			i ? "high" : "low", status);
+
+		writel(status, sdev->base + DMA_IRQ_STAT(i));
+
+		for (j = 0; (j < 8) && status; j++) {
+			if (status & DMA_IRQ_QUEUE) {
+				pchan = sdev->pchans + j;
+				vchan = pchan->vchan;
+
+				if (vchan) {
+					spin_lock(&vchan->vc.lock);
+					vchan_cookie_complete(&pchan->desc->vd);
+					pchan->done = pchan->desc;
+					spin_unlock(&vchan->vc.lock);
+				}
+			}
+
+			status = status >> 4;
+		}
+
+		tasklet_schedule(&sdev->task);
+		ret = IRQ_HANDLED;
+	}
+
+	return ret;
+}
+
+static struct dma_async_tx_descriptor *sun6i_dma_prep_dma_memcpy(
+		struct dma_chan *chan, dma_addr_t dest, dma_addr_t src,
+		size_t len, unsigned long flags)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	struct dma_slave_config *sconfig = &vchan->cfg;
+	struct sun6i_dma_lli *v_lli;
+	struct sun6i_desc *txd;
+	dma_addr_t p_lli;
+
+	dev_dbg(chan2dev(chan),
+		"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
+		__func__, vchan->vc.chan.chan_id, &dest, &src, len, flags);
+
+	if (!len)
+		return NULL;
+
+	txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
+	if (!txd)
+		return NULL;
+
+	v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
+	if (!v_lli) {
+		dev_err(sdev->slave.dev, "Failed to alloc lli memory\n");
+		kfree(txd);
+		return NULL;
+	}
+
+	sun6i_dma_cfg_lli(v_lli, src, dest, len, sconfig);
+	v_lli->cfg |= DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) |
+		DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) |
+		DMA_CHAN_CFG_DST_LINEAR_MODE |
+		DMA_CHAN_CFG_SRC_LINEAR_MODE;
+
+	sun6i_dma_lli_add(NULL, v_lli, p_lli, txd);
+
+	sun6i_dma_dump_lli(vchan, v_lli);
+
+	return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+}
+
+static struct dma_async_tx_descriptor *sun6i_dma_prep_slave_sg(
+		struct dma_chan *chan, struct scatterlist *sgl,
+		unsigned int sg_len, enum dma_transfer_direction dir,
+		unsigned long flags, void *context)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	struct dma_slave_config *sconfig = &vchan->cfg;
+	struct sun6i_dma_lli *v_lli, *prev = NULL;
+	struct sun6i_desc *txd;
+	struct scatterlist *sg;
+	dma_addr_t p_lli;
+	int i;
+
+	if (!sgl)
+		return NULL;
+
+	if (!is_slave_direction(dir)) {
+		dev_err(chan2dev(chan), "Invalid DMA direction\n");
+		return NULL;
+	}
+
+	txd = kzalloc(sizeof(*txd), GFP_NOWAIT);
+	if (!txd)
+		return NULL;
+
+	for_each_sg(sgl, sg, sg_len, i) {
+		v_lli = dma_pool_alloc(sdev->pool, GFP_NOWAIT, &p_lli);
+		if (!v_lli) {
+			kfree(txd);
+			return NULL;
+		}
+
+		if (dir == DMA_MEM_TO_DEV) {
+			sun6i_dma_cfg_lli(v_lli, sg_dma_address(sg),
+					  sconfig->dst_addr, sg_dma_len(sg),
+					  sconfig);
+			v_lli->cfg |= DMA_CHAN_CFG_DST_IO_MODE |
+				DMA_CHAN_CFG_SRC_LINEAR_MODE |
+				DMA_CHAN_CFG_SRC_DRQ(DRQ_SDRAM) |
+				DMA_CHAN_CFG_DST_DRQ(vchan->port);
+
+			dev_dbg(chan2dev(chan),
+				"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
+				__func__, vchan->vc.chan.chan_id,
+				&sconfig->dst_addr, &sg_dma_address(sg),
+				sg_dma_len(sg), flags);
+
+		} else {
+			sun6i_dma_cfg_lli(v_lli, sconfig->src_addr,
+					  sg_dma_address(sg), sg_dma_len(sg),
+					  sconfig);
+			v_lli->cfg |= DMA_CHAN_CFG_DST_LINEAR_MODE |
+				DMA_CHAN_CFG_SRC_IO_MODE |
+				DMA_CHAN_CFG_DST_DRQ(DRQ_SDRAM) |
+				DMA_CHAN_CFG_SRC_DRQ(vchan->port);
+
+			dev_dbg(chan2dev(chan),
+				"%s; chan: %d, dest: %pad, src: %pad, len: %zu. flags: 0x%08lx\n",
+				__func__, vchan->vc.chan.chan_id,
+				&sg_dma_address(sg), &sconfig->src_addr,
+				sg_dma_len(sg), flags);
+		}
+
+		prev = sun6i_dma_lli_add(prev, v_lli, p_lli, txd);
+	}
+
+	dev_dbg(chan2dev(chan), "First: %pad\n", &txd->p_lli);
+	for (prev = txd->v_lli; prev; prev = prev->v_lli_next)
+		sun6i_dma_dump_lli(vchan, prev);
+
+	return vchan_tx_prep(&vchan->vc, &txd->vd, flags);
+}
+
+static int sun6i_dma_control(struct dma_chan *chan, enum dma_ctrl_cmd cmd,
+		       unsigned long arg)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	struct sun6i_pchan *pchan = vchan->phy;
+	unsigned long flags;
+	int ret = 0;
+
+	switch (cmd) {
+	case DMA_RESUME:
+		dev_dbg(chan2dev(chan), "vchan %p: resume\n", &vchan->vc);
+
+		spin_lock_irqsave(&vchan->vc.lock, flags);
+
+		if (pchan) {
+			writel(DMA_CHAN_PAUSE_RESUME,
+			       pchan->base + DMA_CHAN_PAUSE);
+		} else if (!list_empty(&vchan->vc.desc_issued)) {
+			spin_lock(&sdev->lock);
+			list_add_tail(&vchan->node, &sdev->pending);
+			spin_unlock(&sdev->lock);
+		}
+
+		spin_unlock_irqrestore(&vchan->vc.lock, flags);
+		break;
+
+	case DMA_PAUSE:
+		dev_dbg(chan2dev(chan), "vchan %p: pause\n", &vchan->vc);
+
+		if (pchan) {
+			writel(DMA_CHAN_PAUSE_PAUSE,
+			       pchan->base + DMA_CHAN_PAUSE);
+		} else {
+			spin_lock(&sdev->lock);
+			list_del_init(&vchan->node);
+			spin_unlock(&sdev->lock);
+		}
+		break;
+
+	case DMA_TERMINATE_ALL:
+		ret = sun6i_dma_terminate_all(vchan);
+		break;
+	case DMA_SLAVE_CONFIG:
+		memcpy(&vchan->cfg, (void *)arg, sizeof(struct dma_slave_config));
+		break;
+	default:
+		ret = -ENXIO;
+		break;
+	}
+	return ret;
+}
+
+static enum dma_status sun6i_dma_tx_status(struct dma_chan *chan,
+					   dma_cookie_t cookie,
+					   struct dma_tx_state *state)
+{
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	struct sun6i_pchan *pchan = vchan->phy;
+	struct sun6i_dma_lli *lli;
+	struct virt_dma_desc *vd;
+	struct sun6i_desc *txd;
+	enum dma_status ret;
+	unsigned long flags;
+	size_t bytes = 0;
+
+	ret = dma_cookie_status(chan, cookie, state);
+	if (ret == DMA_COMPLETE)
+		return ret;
+
+	spin_lock_irqsave(&vchan->vc.lock, flags);
+
+	vd = vchan_find_desc(&vchan->vc, cookie);
+	txd = to_sun6i_desc(&vd->tx);
+
+	if (vd) {
+		for (lli = txd->v_lli; lli != NULL; lli = lli->v_lli_next)
+			bytes += lli->len;
+	} else if (!pchan || !pchan->desc) {
+		bytes = 0;
+	} else {
+		bytes = readl(pchan->base + DMA_CHAN_CUR_CNT);
+	}
+
+	spin_unlock_irqrestore(&vchan->vc.lock, flags);
+
+	dma_set_residue(state, bytes);
+
+	return ret;
+}
+
+static void sun6i_dma_issue_pending(struct dma_chan *chan)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&vchan->vc.lock, flags);
+
+	if (vchan_issue_pending(&vchan->vc)) {
+		spin_lock(&sdev->lock);
+
+		if (!vchan->phy && list_empty(&vchan->node)) {
+			list_add_tail(&vchan->node, &sdev->pending);
+			tasklet_schedule(&sdev->task);
+			dev_dbg(chan2dev(chan), "vchan %p: issued\n",
+				&vchan->vc);
+		}
+
+		spin_unlock(&sdev->lock);
+	} else {
+		dev_dbg(chan2dev(chan), "vchan %p: nothing to issue\n",
+			&vchan->vc);
+	}
+
+	spin_unlock_irqrestore(&vchan->vc.lock, flags);
+}
+
+static int sun6i_dma_alloc_chan_resources(struct dma_chan *chan)
+{
+	return 0;
+}
+
+static void sun6i_dma_free_chan_resources(struct dma_chan *chan)
+{
+	struct sun6i_dma_dev *sdev = to_sun6i_dma_dev(chan->device);
+	struct sun6i_vchan *vchan = to_sun6i_vchan(chan);
+	unsigned long flags;
+
+	spin_lock_irqsave(&sdev->lock, flags);
+	list_del_init(&vchan->node);
+	spin_unlock_irqrestore(&sdev->lock, flags);
+
+	vchan_free_chan_resources(&vchan->vc);
+}
+
+static inline void sun6i_dma_free(struct sun6i_dma_dev *sdc)
+{
+	int i;
+
+	for (i = 0; i < NR_MAX_VCHANS; i++) {
+		struct sun6i_vchan *vchan = &sdc->vchans[i];
+
+		list_del(&vchan->vc.chan.device_node);
+		tasklet_kill(&vchan->vc.task);
+	}
+
+	tasklet_kill(&sdc->task);
+}
+
+static struct dma_chan *sun6i_dma_of_xlate(struct of_phandle_args *dma_spec,
+					   struct of_dma *ofdma)
+{
+	struct sun6i_dma_dev *sdev = ofdma->of_dma_data;
+	struct sun6i_vchan *vchan;
+	struct dma_chan *chan;
+	u8 port = dma_spec->args[0];
+
+	if (port > NR_MAX_REQUESTS)
+		return NULL;
+
+	chan = dma_get_any_slave_channel(&sdev->slave);
+	if (!chan)
+		return NULL;
+
+	vchan = to_sun6i_vchan(chan);
+	vchan->port = port;
+
+	return chan;
+}
+
+static int sun6i_dma_probe(struct platform_device *pdev)
+{
+	struct sun6i_dma_dev *sdc;
+	struct resource *res;
+	struct clk *mux, *pll6;
+	int irq;
+	int ret, i;
+
+	sdc = devm_kzalloc(&pdev->dev, sizeof(*sdc), GFP_KERNEL);
+	if (!sdc)
+		return -ENOMEM;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	sdc->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(sdc->base))
+		return PTR_ERR(sdc->base);
+
+	irq = platform_get_irq(pdev, 0);
+	ret = devm_request_irq(&pdev->dev, irq, sun6i_dma_interrupt, 0,
+			       dev_name(&pdev->dev), sdc);
+	if (ret) {
+		dev_err(&pdev->dev, "Cannot request IRQ\n");
+		return ret;
+	}
+
+	sdc->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(sdc->clk)) {
+		dev_err(&pdev->dev, "No clock specified\n");
+		return PTR_ERR(sdc->clk);
+	}
+
+	mux = clk_get(NULL, "ahb1_mux");
+	if (IS_ERR(mux)) {
+		dev_err(&pdev->dev, "Couldn't get AHB1 Mux\n");
+		return PTR_ERR(mux);
+	}
+
+	pll6 = clk_get(NULL, "pll6");
+	if (IS_ERR(pll6)) {
+		dev_err(&pdev->dev, "Couldn't get PLL6\n");
+		clk_put(mux);
+		return PTR_ERR(pll6);
+	}
+
+	ret = clk_set_parent(mux, pll6);
+	clk_put(pll6);
+	clk_put(mux);
+
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't reparent AHB1 on PLL6\n");
+		return ret;
+	}
+
+	sdc->rstc = devm_reset_control_get(&pdev->dev, NULL);
+	if (IS_ERR(sdc->rstc)) {
+		dev_err(&pdev->dev, "No reset controller specified\n");
+		return PTR_ERR(sdc->rstc);
+	}
+
+	sdc->pool = dmam_pool_create(dev_name(&pdev->dev), &pdev->dev,
+				     sizeof(struct sun6i_dma_lli), 4, 0);
+	if (!sdc->pool) {
+		dev_err(&pdev->dev, "No memory for descriptors dma pool\n");
+		return -ENOMEM;
+	}
+
+	platform_set_drvdata(pdev, sdc);
+	INIT_LIST_HEAD(&sdc->pending);
+	spin_lock_init(&sdc->lock);
+
+	dma_cap_set(DMA_PRIVATE, sdc->slave.cap_mask);
+	dma_cap_set(DMA_MEMCPY, sdc->slave.cap_mask);
+	dma_cap_set(DMA_SLAVE, sdc->slave.cap_mask);
+
+	INIT_LIST_HEAD(&sdc->slave.channels);
+	sdc->slave.device_alloc_chan_resources	= sun6i_dma_alloc_chan_resources;
+	sdc->slave.device_free_chan_resources	= sun6i_dma_free_chan_resources;
+	sdc->slave.device_tx_status		= sun6i_dma_tx_status;
+	sdc->slave.device_issue_pending		= sun6i_dma_issue_pending;
+	sdc->slave.device_prep_slave_sg		= sun6i_dma_prep_slave_sg;
+	sdc->slave.device_prep_dma_memcpy	= sun6i_dma_prep_dma_memcpy;
+	sdc->slave.device_control		= sun6i_dma_control;
+	sdc->slave.chancnt			= NR_MAX_VCHANS;
+
+	sdc->slave.dev = &pdev->dev;
+
+	sdc->pchans = devm_kcalloc(&pdev->dev, NR_MAX_CHANNELS,
+				   sizeof(struct sun6i_pchan), GFP_KERNEL);
+	if (!sdc->pchans)
+		return -ENOMEM;
+
+	sdc->vchans = devm_kcalloc(&pdev->dev, NR_MAX_VCHANS,
+				   sizeof(struct sun6i_vchan), GFP_KERNEL);
+	if (!sdc->vchans)
+		return -ENOMEM;
+
+	tasklet_init(&sdc->task, sun6i_dma_tasklet, (unsigned long)sdc);
+
+	for (i = 0; i < NR_MAX_CHANNELS; i++) {
+		struct sun6i_pchan *pchan = &sdc->pchans[i];
+
+		pchan->idx = i;
+		pchan->base = sdc->base + 0x100 + i * 0x40;
+	}
+
+	for (i = 0; i < NR_MAX_VCHANS; i++) {
+		struct sun6i_vchan *vchan = &sdc->vchans[i];
+
+		INIT_LIST_HEAD(&vchan->node);
+		vchan->vc.desc_free = sun6i_dma_free_desc;
+		vchan_init(&vchan->vc, &sdc->slave);
+	}
+
+	ret = reset_control_deassert(sdc->rstc);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't deassert the device from reset\n");
+		goto err_chan_free;
+	}
+
+	ret = clk_prepare_enable(sdc->clk);
+	if (ret) {
+		dev_err(&pdev->dev, "Couldn't enable the clock\n");
+		goto err_reset_assert;
+	}
+
+	ret = dma_async_device_register(&sdc->slave);
+	if (ret) {
+		dev_warn(&pdev->dev, "Failed to register DMA engine device\n");
+		goto err_clk_disable;
+	}
+
+	ret = of_dma_controller_register(pdev->dev.of_node, sun6i_dma_of_xlate,
+					 sdc);
+	if (ret) {
+		dev_err(&pdev->dev, "of_dma_controller_register failed\n");
+		goto err_dma_unregister;
+	}
+
+	return 0;
+
+err_dma_unregister:
+	dma_async_device_unregister(&sdc->slave);
+err_clk_disable:
+	clk_disable_unprepare(sdc->clk);
+err_reset_assert:
+	reset_control_assert(sdc->rstc);
+err_chan_free:
+	sun6i_dma_free(sdc);
+	return ret;
+}
+
+static int sun6i_dma_remove(struct platform_device *pdev)
+{
+	struct sun6i_dma_dev *sdc = platform_get_drvdata(pdev);
+
+	of_dma_controller_free(pdev->dev.of_node);
+	dma_async_device_unregister(&sdc->slave);
+
+	clk_disable_unprepare(sdc->clk);
+	reset_control_assert(sdc->rstc);
+
+	sun6i_dma_free(sdc);
+
+	return 0;
+}
+
+static struct of_device_id sun6i_dma_match[] = {
+	{ .compatible = "allwinner,sun6i-a31-dma" }
+};
+
+static struct platform_driver sun6i_dma_driver = {
+	.probe		= sun6i_dma_probe,
+	.remove		= sun6i_dma_remove,
+	.driver = {
+		.name		= "sun6i-dma",
+		.of_match_table	= sun6i_dma_match,
+	},
+};
+module_platform_driver(sun6i_dma_driver);
+
+MODULE_DESCRIPTION("Allwinner A31 DMA Controller Driver");
+MODULE_AUTHOR("Sugar <shuge@allwinnertech.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
-- 
1.9.1


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

* [PATCH v6 8/8] ARM: sun6i: dt: Add A31 DMA controller to DTSI
  2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
                   ` (6 preceding siblings ...)
  2014-04-17  8:46 ` [PATCH v6 7/8] DMA: sun6i: Add driver for the Allwinner A31 DMA controller Maxime Ripard
@ 2014-04-17  8:46 ` Maxime Ripard
  7 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-17  8:46 UTC (permalink / raw)
  To: Emilio Lopez, Dan Williams, Vinod Koul
  Cc: Mike Turquette, linux-arm-kernel, dmaengine, linux-kernel,
	devicetree, linux-sunxi, kevin.z.m.zh, sunny, shuge, zhuzhenhua,
	andriy.shevchenko, Arnd Bergmann, Maxime Ripard

Now that we have a DMA driver, we can add the DMA bindings in the DTSI for the
controller and the devices supported that can use DMA.

Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
---
 arch/arm/boot/dts/sun6i-a31.dtsi | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/arch/arm/boot/dts/sun6i-a31.dtsi b/arch/arm/boot/dts/sun6i-a31.dtsi
index 5ebf963464b9..b28a55fdf3ce 100644
--- a/arch/arm/boot/dts/sun6i-a31.dtsi
+++ b/arch/arm/boot/dts/sun6i-a31.dtsi
@@ -237,6 +237,15 @@
 		#size-cells = <1>;
 		ranges;
 
+		dma: dma-controller@01c02000 {
+			compatible = "allwinner,sun6i-a31-dma";
+			reg = <0x01c02000 0x1000>;
+			interrupts = <0 50 4>;
+			clocks = <&ahb1_gates 6>;
+			resets = <&ahb1_rst 6>;
+			#dma-cells = <1>;
+		};
+
 		pio: pinctrl@01c20800 {
 			compatible = "allwinner,sun6i-a31-pinctrl";
 			reg = <0x01c20800 0x400>;
@@ -322,6 +331,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 16>;
 			resets = <&apb2_rst 16>;
+			dmas = <&dma 6>, <&dma 6>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -333,6 +344,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 17>;
 			resets = <&apb2_rst 17>;
+			dmas = <&dma 7>, <&dma 7>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -344,6 +357,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 18>;
 			resets = <&apb2_rst 18>;
+			dmas = <&dma 8>, <&dma 8>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -355,6 +370,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 19>;
 			resets = <&apb2_rst 19>;
+			dmas = <&dma 9>, <&dma 9>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -366,6 +383,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 20>;
 			resets = <&apb2_rst 20>;
+			dmas = <&dma 10>, <&dma 10>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -377,6 +396,8 @@
 			reg-io-width = <4>;
 			clocks = <&apb2_gates 21>;
 			resets = <&apb2_rst 21>;
+			dmas = <&dma 22>, <&dma 22>;
+			dma-names = "rx", "tx";
 			status = "disabled";
 		};
 
@@ -426,6 +447,8 @@
 			interrupts = <0 65 4>;
 			clocks = <&ahb1_gates 20>, <&spi0_clk>;
 			clock-names = "ahb", "mod";
+			dmas = <&dma 23>, <&dma 23>;
+			dma-names = "rx", "tx";
 			resets = <&ahb1_rst 20>;
 			status = "disabled";
 		};
@@ -436,6 +459,8 @@
 			interrupts = <0 66 4>;
 			clocks = <&ahb1_gates 21>, <&spi1_clk>;
 			clock-names = "ahb", "mod";
+			dmas = <&dma 24>, <&dma 24>;
+			dma-names = "rx", "tx";
 			resets = <&ahb1_rst 21>;
 			status = "disabled";
 		};
@@ -446,6 +471,8 @@
 			interrupts = <0 67 4>;
 			clocks = <&ahb1_gates 22>, <&spi2_clk>;
 			clock-names = "ahb", "mod";
+			dmas = <&dma 25>, <&dma 25>;
+			dma-names = "rx", "tx";
 			resets = <&ahb1_rst 22>;
 			status = "disabled";
 		};
@@ -456,6 +483,8 @@
 			interrupts = <0 68 4>;
 			clocks = <&ahb1_gates 23>, <&spi3_clk>;
 			clock-names = "ahb", "mod";
+			dmas = <&dma 26>, <&dma 26>;
+			dma-names = "rx", "tx";
 			resets = <&ahb1_rst 23>;
 			status = "disabled";
 		};
-- 
1.9.1


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

* Re: [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files
  2014-04-17  8:46 ` [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files Maxime Ripard
@ 2014-04-23 12:33   ` Arnd Bergmann
  2014-04-23 13:28     ` Maxime Ripard
  0 siblings, 1 reply; 18+ messages in thread
From: Arnd Bergmann @ 2014-04-23 12:33 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Maxime Ripard, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

On Thursday 17 April 2014, Maxime Ripard wrote:
> This will allow to add per-SoC hooks more easily.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> ---
>  arch/arm/mach-sunxi/Makefile  |   6 +-
>  arch/arm/mach-sunxi/restart.c | 104 +++++++++++++++++++++++++++
>  arch/arm/mach-sunxi/restart.h |  20 ++++++
>  arch/arm/mach-sunxi/sun4i.c   |  36 ++++++++++
>  arch/arm/mach-sunxi/sun5i.c   |  37 ++++++++++
>  arch/arm/mach-sunxi/sun6i.c   |  49 +++++++++++++
>  arch/arm/mach-sunxi/sun7i.c   |  36 ++++++++++
>  arch/arm/mach-sunxi/sunxi.c   | 164 ------------------------------------------
>  8 files changed, 287 insertions(+), 165 deletions(-)

This doesn't look like a move in the right direction, and I don't see
the connection with the DMA driver.

> diff --git a/arch/arm/mach-sunxi/restart.c b/arch/arm/mach-sunxi/restart.c
> new file mode 100644
> index 000000000000..3b89727ee943
> --- /dev/null
> +++ b/arch/arm/mach-sunxi/restart.c

Splitting out the restart code is good, but please complete this by
moving it into a driver directory. My preferred location would be
within the watchdog driver, since it's really the same registers.
You can turn on the watchdog driver as built-in in the defconfig.
While it's not ideal that you can end up without a restart function
if the driver does not get loaded, I would still prefer that over
having the driver in the mach-sunxi directory.

An alternative would be to move the restart code into
drivers/power/reset.

> diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
> deleted file mode 100644
> index 460b5a4962ef..000000000000
> --- a/arch/arm/mach-sunxi/sunxi.c

Instead of splitting up this file, I think it would be better to
reduce the number of special hacks required per machine. Note that
for arm64 we require platforms not to have any code outside of
the drivers directory, and I'd like to get to the same situation
for much of arch/arm/mach-* as well.

	Arnd

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

* Re: [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks
  2014-04-17  8:46 ` [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks Maxime Ripard
@ 2014-04-23 12:39   ` Arnd Bergmann
  2014-04-23 13:17     ` Maxime Ripard
  0 siblings, 1 reply; 18+ messages in thread
From: Arnd Bergmann @ 2014-04-23 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Maxime Ripard, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

On Thursday 17 April 2014, Maxime Ripard wrote:
> Since we start to have a lot of clocks to protect, some of them in a few boards
> only, it becomes difficult to handle the clock protection without having to add
> per machine exceptions.
> 
> Move these where they belong, in the machine definition code.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

I don't like the fact that these are required to be hardcoded
anywhere in source code.

> +#include <linux/clk.h>
>  #include <linux/init.h>
>  #include <linux/of_platform.h>
>  
> @@ -19,9 +20,17 @@
>  
>  static void __init sun4i_dt_init(void)
>  {
> +       struct clk *clk;
> +
>         sunxi_setup_restart();
>  
>         of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> +
> +       /* Make sure the clocks we absolutely need are enabled */
> +       /* DDR clock */
> +       clk = clk_get(NULL, "pll5_ddr");
> +       if (!IS_ERR(clk))
> +               clk_prepare_enable(clk);
>  }

Isn't there already DT syntax to do the same? If not, should there be?

	Arnd

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

* Re: [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address
  2014-04-17  8:46 ` [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address Maxime Ripard
@ 2014-04-23 12:39   ` Arnd Bergmann
  2014-04-23 13:11     ` Maxime Ripard
  0 siblings, 1 reply; 18+ messages in thread
From: Arnd Bergmann @ 2014-04-23 12:39 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Maxime Ripard, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

On Thursday 17 April 2014, Maxime Ripard wrote:
> The DT are supposed to be ordered by physical address. Move the NMI node where
> it belongs.
> 
> Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>

Acked-by: Arnd Bergmann <arnd@arndb.de>

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

* Re: [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address
  2014-04-23 12:39   ` Arnd Bergmann
@ 2014-04-23 13:11     ` Maxime Ripard
  0 siblings, 0 replies; 18+ messages in thread
From: Maxime Ripard @ 2014-04-23 13:11 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

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

On Wed, Apr 23, 2014 at 02:39:26PM +0200, Arnd Bergmann wrote:
> On Thursday 17 April 2014, Maxime Ripard wrote:
> > The DT are supposed to be ordered by physical address. Move the NMI node where
> > it belongs.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> Acked-by: Arnd Bergmann <arnd@arndb.de>

Thanks!

Just applied.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks
  2014-04-23 12:39   ` Arnd Bergmann
@ 2014-04-23 13:17     ` Maxime Ripard
  2014-04-23 13:31       ` Arnd Bergmann
  0 siblings, 1 reply; 18+ messages in thread
From: Maxime Ripard @ 2014-04-23 13:17 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

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

On Wed, Apr 23, 2014 at 02:39:02PM +0200, Arnd Bergmann wrote:
> On Thursday 17 April 2014, Maxime Ripard wrote:
> > Since we start to have a lot of clocks to protect, some of them in a few boards
> > only, it becomes difficult to handle the clock protection without having to add
> > per machine exceptions.
> > 
> > Move these where they belong, in the machine definition code.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> 
> I don't like the fact that these are required to be hardcoded
> anywhere in source code.

I agree, but that would also mean having a driver for everything that
would need a clock: a CPU, the RAM. I'm not sure we want that either.

> > +#include <linux/clk.h>
> >  #include <linux/init.h>
> >  #include <linux/of_platform.h>
> >  
> > @@ -19,9 +20,17 @@
> >  
> >  static void __init sun4i_dt_init(void)
> >  {
> > +       struct clk *clk;
> > +
> >         sunxi_setup_restart();
> >  
> >         of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> > +
> > +       /* Make sure the clocks we absolutely need are enabled */
> > +       /* DDR clock */
> > +       clk = clk_get(NULL, "pll5_ddr");
> > +       if (!IS_ERR(clk))
> > +               clk_prepare_enable(clk);
> >  }
> 
> Isn't there already DT syntax to do the same? If not, should there be?

I don't think there is, and I gave some thought about it too. But
something a la regulator-always-on wouldn't work with clocks with
multiple outputs (like pll5), because you might need to leave only one
of the output enabled, but not the others, and I couldn't think of a
nice way to do so.

If you have one, I'd be happy to implement it.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files
  2014-04-23 12:33   ` Arnd Bergmann
@ 2014-04-23 13:28     ` Maxime Ripard
  2014-04-23 13:33       ` Arnd Bergmann
  0 siblings, 1 reply; 18+ messages in thread
From: Maxime Ripard @ 2014-04-23 13:28 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

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

On Wed, Apr 23, 2014 at 02:33:50PM +0200, Arnd Bergmann wrote:
> On Thursday 17 April 2014, Maxime Ripard wrote:
> > This will allow to add per-SoC hooks more easily.
> > 
> > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > ---
> >  arch/arm/mach-sunxi/Makefile  |   6 +-
> >  arch/arm/mach-sunxi/restart.c | 104 +++++++++++++++++++++++++++
> >  arch/arm/mach-sunxi/restart.h |  20 ++++++
> >  arch/arm/mach-sunxi/sun4i.c   |  36 ++++++++++
> >  arch/arm/mach-sunxi/sun5i.c   |  37 ++++++++++
> >  arch/arm/mach-sunxi/sun6i.c   |  49 +++++++++++++
> >  arch/arm/mach-sunxi/sun7i.c   |  36 ++++++++++
> >  arch/arm/mach-sunxi/sunxi.c   | 164 ------------------------------------------
> >  8 files changed, 287 insertions(+), 165 deletions(-)
> 
> This doesn't look like a move in the right direction, and I don't see
> the connection with the DMA driver.

This is actually not adding any code at all, just reorganizing it.

This was also something that would allow to have per-family
configuration options, and hence not select drivers like the pinctrl
one that are pretty huge if they're not needed.

> 
> > diff --git a/arch/arm/mach-sunxi/restart.c b/arch/arm/mach-sunxi/restart.c
> > new file mode 100644
> > index 000000000000..3b89727ee943
> > --- /dev/null
> > +++ b/arch/arm/mach-sunxi/restart.c
> 
> Splitting out the restart code is good, but please complete this by
> moving it into a driver directory. My preferred location would be
> within the watchdog driver, since it's really the same registers.
> You can turn on the watchdog driver as built-in in the defconfig.
> While it's not ideal that you can end up without a restart function
> if the driver does not get loaded, I would still prefer that over
> having the driver in the mach-sunxi directory.

Ok. The thing is, we don't have any watchdog driver for the A31 for
the moment, so the restart code will have to stay there. But I can
totally do that for the A10/A20.

> An alternative would be to move the restart code into
> drivers/power/reset.

That would be a good place for the A31 code then.

> > diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
> > deleted file mode 100644
> > index 460b5a4962ef..000000000000
> > --- a/arch/arm/mach-sunxi/sunxi.c
> 
> Instead of splitting up this file, I think it would be better to
> reduce the number of special hacks required per machine. Note that
> for arm64 we require platforms not to have any code outside of
> the drivers directory, and I'd like to get to the same situation
> for much of arch/arm/mach-* as well.

I think it's fair.

How would you enforce default clock parenting and/or rate then?

-- 
Maxime Ripard, Free Electrons
Embedded Linux, Kernel and Android engineering
http://free-electrons.com

[-- Attachment #2: Digital signature --]
[-- Type: application/pgp-signature, Size: 836 bytes --]

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

* Re: [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks
  2014-04-23 13:17     ` Maxime Ripard
@ 2014-04-23 13:31       ` Arnd Bergmann
  2014-05-13  1:30         ` Mike Turquette
  0 siblings, 1 reply; 18+ messages in thread
From: Arnd Bergmann @ 2014-04-23 13:31 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Maxime Ripard, devicetree, Mike Turquette, andriy.shevchenko,
	Emilio Lopez, dmaengine, linux-kernel, zhuzhenhua, Vinod Koul,
	linux-sunxi, kevin.z.m.zh, sunny, shuge, Dan Williams

On Wednesday 23 April 2014 15:17:20 Maxime Ripard wrote:
> > > +#include <linux/clk.h>
> > >  #include <linux/init.h>
> > >  #include <linux/of_platform.h>
> > >  
> > > @@ -19,9 +20,17 @@
> > >  
> > >  static void __init sun4i_dt_init(void)
> > >  {
> > > +       struct clk *clk;
> > > +
> > >         sunxi_setup_restart();
> > >  
> > >         of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> > > +
> > > +       /* Make sure the clocks we absolutely need are enabled */
> > > +       /* DDR clock */
> > > +       clk = clk_get(NULL, "pll5_ddr");
> > > +       if (!IS_ERR(clk))
> > > +               clk_prepare_enable(clk);
> > >  }
> > 
> > Isn't there already DT syntax to do the same? If not, should there be?
> 
> I don't think there is, and I gave some thought about it too. But
> something a la regulator-always-on wouldn't work with clocks with
> multiple outputs (like pll5), because you might need to leave only one
> of the output enabled, but not the others, and I couldn't think of a
> nice way to do so.
> 
> If you have one, I'd be happy to implement it.

We had a discussion a while ago about encoding default settings for clock
providers in the clock provider nodes. I don't remember the details
unfortunately.

Mike, can you explain how this should be done?

	Arnd

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

* Re: [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files
  2014-04-23 13:28     ` Maxime Ripard
@ 2014-04-23 13:33       ` Arnd Bergmann
  0 siblings, 0 replies; 18+ messages in thread
From: Arnd Bergmann @ 2014-04-23 13:33 UTC (permalink / raw)
  To: Maxime Ripard
  Cc: linux-arm-kernel, Emilio Lopez, Dan Williams, Vinod Koul,
	devicetree, Mike Turquette, andriy.shevchenko, linux-kernel,
	zhuzhenhua, shuge, linux-sunxi, kevin.z.m.zh, sunny, dmaengine

On Wednesday 23 April 2014 15:28:29 Maxime Ripard wrote:
> On Wed, Apr 23, 2014 at 02:33:50PM +0200, Arnd Bergmann wrote:
> > On Thursday 17 April 2014, Maxime Ripard wrote:
> > > This will allow to add per-SoC hooks more easily.
> > > 
> > > Signed-off-by: Maxime Ripard <maxime.ripard@free-electrons.com>
> > > ---
> > >  arch/arm/mach-sunxi/Makefile  |   6 +-
> > >  arch/arm/mach-sunxi/restart.c | 104 +++++++++++++++++++++++++++
> > >  arch/arm/mach-sunxi/restart.h |  20 ++++++
> > >  arch/arm/mach-sunxi/sun4i.c   |  36 ++++++++++
> > >  arch/arm/mach-sunxi/sun5i.c   |  37 ++++++++++
> > >  arch/arm/mach-sunxi/sun6i.c   |  49 +++++++++++++
> > >  arch/arm/mach-sunxi/sun7i.c   |  36 ++++++++++
> > >  arch/arm/mach-sunxi/sunxi.c   | 164 ------------------------------------------
> > >  8 files changed, 287 insertions(+), 165 deletions(-)
> > 
> > This doesn't look like a move in the right direction, and I don't see
> > the connection with the DMA driver.
> 
> This is actually not adding any code at all, just reorganizing it.
> 
> This was also something that would allow to have per-family
> configuration options, and hence not select drivers like the pinctrl
> one that are pretty huge if they're not needed.

I don't mind having split Kconfig entries to control which drivers
are used. It was more about the absolute growth in C source code
lines above.

> > Splitting out the restart code is good, but please complete this by
> > moving it into a driver directory. My preferred location would be
> > within the watchdog driver, since it's really the same registers.
> > You can turn on the watchdog driver as built-in in the defconfig.
> > While it's not ideal that you can end up without a restart function
> > if the driver does not get loaded, I would still prefer that over
> > having the driver in the mach-sunxi directory.
> 
> Ok. The thing is, we don't have any watchdog driver for the A31 for
> the moment, so the restart code will have to stay there. But I can
> totally do that for the A10/A20.
> 
> > An alternative would be to move the restart code into
> > drivers/power/reset.
> 
> That would be a good place for the A31 code then.

Ok, good.

> > > diff --git a/arch/arm/mach-sunxi/sunxi.c b/arch/arm/mach-sunxi/sunxi.c
> > > deleted file mode 100644
> > > index 460b5a4962ef..000000000000
> > > --- a/arch/arm/mach-sunxi/sunxi.c
> > 
> > Instead of splitting up this file, I think it would be better to
> > reduce the number of special hacks required per machine. Note that
> > for arm64 we require platforms not to have any code outside of
> > the drivers directory, and I'd like to get to the same situation
> > for much of arch/arm/mach-* as well.
> 
> I think it's fair.
> 
> How would you enforce default clock parenting and/or rate then?

See my other reply.

	Arnd

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

* Re: [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks
  2014-04-23 13:31       ` Arnd Bergmann
@ 2014-05-13  1:30         ` Mike Turquette
  0 siblings, 0 replies; 18+ messages in thread
From: Mike Turquette @ 2014-05-13  1:30 UTC (permalink / raw)
  To: Arnd Bergmann, linux-arm-kernel
  Cc: Maxime Ripard, devicetree, andriy.shevchenko, Emilio Lopez,
	dmaengine, linux-kernel, zhuzhenhua, Vinod Koul, linux-sunxi,
	kevin.z.m.zh, sunny, shuge, Dan Williams

Quoting Arnd Bergmann (2014-04-23 06:31:06)
> On Wednesday 23 April 2014 15:17:20 Maxime Ripard wrote:
> > > > +#include <linux/clk.h>
> > > >  #include <linux/init.h>
> > > >  #include <linux/of_platform.h>
> > > >  
> > > > @@ -19,9 +20,17 @@
> > > >  
> > > >  static void __init sun4i_dt_init(void)
> > > >  {
> > > > +       struct clk *clk;
> > > > +
> > > >         sunxi_setup_restart();
> > > >  
> > > >         of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
> > > > +
> > > > +       /* Make sure the clocks we absolutely need are enabled */
> > > > +       /* DDR clock */
> > > > +       clk = clk_get(NULL, "pll5_ddr");
> > > > +       if (!IS_ERR(clk))
> > > > +               clk_prepare_enable(clk);
> > > >  }
> > > 
> > > Isn't there already DT syntax to do the same? If not, should there be?
> > 
> > I don't think there is, and I gave some thought about it too. But
> > something a la regulator-always-on wouldn't work with clocks with
> > multiple outputs (like pll5), because you might need to leave only one
> > of the output enabled, but not the others, and I couldn't think of a
> > nice way to do so.
> > 
> > If you have one, I'd be happy to implement it.
> 
> We had a discussion a while ago about encoding default settings for clock
> providers in the clock provider nodes. I don't remember the details
> unfortunately.
> 
> Mike, can you explain how this should be done?

The default clock settings are still in discussion. My perspective is
that the default settings are best used by the consuming device. E.g. an
MMC controller that consumes a clock signal should specify in DT the
clock that it consumes (already does this) and the preferred rate that
it wants that clock, as well as the preferred parent if that clock is a
mux.

Of course clock providers themselves are devices and could specify such
defaults for the clocks that they create. This approach is a minor
bastardization of the default clock settings since the clock provider
device isn't really consuming the clock, but it is helpful when no such
driver or DT node exists to do the same.

It is worth pointing out that the above approach amounts to the exact
same hard coding of clk_enable requirements as is done in Maxime's
patch, but with the added problem that the default are hidden away in DT
somewhere which is a debugging nightmare for someone trying to figure
out why a clock is set at a certain rate.

Finally, to answer the question more directly, I completely oppose
having something analogous to a regulator-always-on for a clock that can
gate. Please always use the clk.h API for this in Linux kernel driver C
code:

clk_get()
clk_prepare()
clk_enable()

In Maxime's case he is doing the right thing (using the clk.h API) but
he has no memory driver to do it, so he is putting that stuff in the clk
provider driver. I'm fine with that solution, until the day that he does
have a memory driver and then it needs to migrate out to that code.

Regards,
Mike

> 
>         Arnd

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

end of thread, other threads:[~2014-05-13  1:30 UTC | newest]

Thread overview: 18+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2014-04-17  8:46 [PATCH v6 0/8] Add support for the Allwinner A31 DMA Controller Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 1/8] clk: sunxi: Remove calls to clk_put Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 2/8] ARM: sunxi: Split out the various machines into separate files Maxime Ripard
2014-04-23 12:33   ` Arnd Bergmann
2014-04-23 13:28     ` Maxime Ripard
2014-04-23 13:33       ` Arnd Bergmann
2014-04-17  8:46 ` [PATCH v6 3/8] ARM: sunxi: Move the clock protection to machine hooks Maxime Ripard
2014-04-23 12:39   ` Arnd Bergmann
2014-04-23 13:17     ` Maxime Ripard
2014-04-23 13:31       ` Arnd Bergmann
2014-05-13  1:30         ` Mike Turquette
2014-04-17  8:46 ` [PATCH v6 4/8] ARM: sun6i: Protect CPU clock Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 5/8] ARM: sun6i: Protect SDRAM gating bit Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 6/8] ARM: sun6i: Sort the NMI node by physical address Maxime Ripard
2014-04-23 12:39   ` Arnd Bergmann
2014-04-23 13:11     ` Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 7/8] DMA: sun6i: Add driver for the Allwinner A31 DMA controller Maxime Ripard
2014-04-17  8:46 ` [PATCH v6 8/8] ARM: sun6i: dt: Add A31 DMA controller to DTSI Maxime Ripard

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).