All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation
@ 2017-02-22 20:47 Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi Philipp Tomsich
                   ` (5 more replies)
  0 siblings, 6 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

Here's v2, which incorporates the feedback from Maxime and ChenYu
into a more complete and less intrusive version.

The most elegant solution to our problem with pinctrl & gpio, which
will also work equally well for ccu and reset, is to have the device
model bind multiple drivers to a single node: for this reason, one
patch of the series touches the device-model core.

To provide a bit more context, I've also include the change showing
how this is enabled in our defconfig (i.e. a diff against our defconfig,
even though the defconfig has not been submitted yet).

Changes in v2:
 * two variants to reuse the sunxi_gpio layer:
   - create a matching soc_data for sunxi_gpio and rebind
   - have the device-model do this for us
 * got rid of the gpio-banks and gpiobridge drivers



Philipp Tomsich (6):
  sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi
  dm: core: Allow multiple drivers to bind for a single node
  sunxi: CONFIG_DM_ALLOW_MULTIPLE_DRIVERS for gpio/pinctrl binding
  defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS
  sun50i: dts: add r_pio node and pinconfig entries into r_pio and pio
  sun50i: dts: update DTS to avoid warnings

 arch/arm/dts/sun50i-a64.dtsi                       |  62 ++-
 arch/arm/include/asm/arch-sunxi/gpio-internal.h    |  19 +
 configs/lynx_defconfig                             |   1 +
 .../pinctrl/allwinner,pinctrl.txt                  |  65 +++
 drivers/core/Kconfig                               |  14 +
 drivers/core/lists.c                               |  12 +-
 drivers/gpio/sunxi_gpio.c                          |  15 +-
 drivers/pinctrl/Kconfig                            |  10 +
 drivers/pinctrl/Makefile                           |   2 +
 drivers/pinctrl/sunxi/Makefile                     |  10 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c       |  92 ++++
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c         | 577 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 321 ++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
 14 files changed, 1479 insertions(+), 32 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-sunxi/gpio-internal.h
 create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h

-- 
1.9.1

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

* [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-02-22 23:18   ` Maxime Ripard
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node Philipp Tomsich
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

This change adds a full device-model pinctrl driver for sunxi (tested with
sun50iw1p1) based on the support available in Linux.

Details are:
 * implements a driver for pinctrl devices and assigns sun50i-a64-pinctrl
   and sun50i-a64-r-pinctrl to it
 * dynamically creates the driver_data for a sunxi_gpio (see sunxi_gpio.c)
   driver and binds it to the same device-tree node
 * lifts and reuses the pinctrl-sunxi.h and pinctrl-sun50i-a64.c files from
   Linux (thanks to Maxime and Andre) and adds a pinctrl-sun50i-a64-r.c (to
   be picked up for inclusion into Linux again)

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 arch/arm/include/asm/arch-sunxi/gpio-internal.h    |  19 +
 .../pinctrl/allwinner,pinctrl.txt                  |  65 +++
 drivers/gpio/sunxi_gpio.c                          |  15 +-
 drivers/pinctrl/Kconfig                            |  10 +
 drivers/pinctrl/Makefile                           |   2 +
 drivers/pinctrl/sunxi/Makefile                     |  10 +
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c       |  92 ++++
 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c         | 577 +++++++++++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 317 +++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
 10 files changed, 1411 insertions(+), 7 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-sunxi/gpio-internal.h
 create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
 create mode 100644 drivers/pinctrl/sunxi/Makefile
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
 create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h

diff --git a/arch/arm/include/asm/arch-sunxi/gpio-internal.h b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
new file mode 100644
index 0000000..4dcdd34
--- /dev/null
+++ b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
@@ -0,0 +1,19 @@
+/*
+ * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+
+#ifndef _SUNXI_GPIO_INTERNAL_H
+#define _SUNXI_GPIO_INTERNAL_H
+
+/* This data structure is shared between the sunxi_gpio driver and
+ * the sunxi_pinctrl driver.
+ */
+struct sunxi_gpio_soc_data {
+	int start;
+	int no_banks;
+};
+
+#endif
diff --git a/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
new file mode 100644
index 0000000..946831f
--- /dev/null
+++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
@@ -0,0 +1,65 @@
+* Allwinner Pinmux Controller
+
+Allwinner integrates multiple banks (of 32 pins each) of pin-muxing,
+GPIO functionality and (optional) external interrupt functionality
+into a single controller.
+
+For each configurable pad (certain driver-cells, such as the IO from
+integrated USB PHYs or DRAM, have a fixed function and can not be
+configured), the muxing options (input, output or one of the several
+functions) can be selected.
+
+Properties for the pinctrl node:
+ - compatible: should be "allwinner,sun50i-pinctrl"
+ - reg: address and length of the register set for the device.
+ - interrupts: interrupt for the device
+ - clocks: A phandle to the reference clock for this device
+
+Properties for the pinconfig sub-nodes:
+ - allwinner,pins: a list of pins (e.g. "PH2", "PH3") to configure
+ - allwinner,function: the name of pinmux function (e.g. "mmc2")
+ - drive-strength: a drive-stength setting of 10, 20, 30 or 40 mA
+ - bias-pull-up
+ - bias-pull-down
+ - bias-disable (default)
+
+Deprecated properties for the pinconfig sub-nodes:
+ - allwinner,drive: one of <SUN4I_PINCTRL_10_MA>, <SUN4I_PINCTRL_20_MA>,
+                    <SUN4I_PINCTRL_30_MA> or <SUN4I_PINCTRL_40_MA>
+ - allwinner,pull: one of <SUN4I_PINCTRL_NO_PULL>, <SUN4I_PINCTRL_PULL_UP>
+    		   or <SUN4I_PINCTRL_PULL_DOWN>
+
+Example:
+
+	pio: pinctrl at 1c20800 {
+		compatible = "allwinner,sun50i-a64-pinctrl";
+		reg = <0x01c20800 0x400>;
+
+		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
+			     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+		clocks = <&bus_gates 69>;
+
+		gpio-controller;
+		#gpio-cells = <3>;
+
+		interrupt-controller;
+		#interrupt-cells = <2>;
+
+		#address-cells = <1>;
+		#size-cells = <1>;
+
+		uart0_pins_a: uart0_pins_a {
+			allwinner,pins = "PB8", "PB9";
+			allwinner,function = "uart0";
+			allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+			allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+		};
+
+		uart0_pins_b: uart0_pins_b {
+			allwinner,pins = "PF2", "PF3";
+			allwinner,function = "uart0";
+			allwinner,drive = <SUN4I_PINCTRL_10_MA>;
+			allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+		};
+	};
diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index 2b7bc7f..fd0c1ac 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -16,6 +16,7 @@
 #include <fdtdec.h>
 #include <malloc.h>
 #include <asm/arch/gpio.h>
+#include <asm/arch/gpio-internal.h>
 #include <asm/io.h>
 #include <asm/gpio.h>
 #include <dm/device-internal.h>
@@ -275,11 +276,6 @@ static int gpio_sunxi_probe(struct udevice *dev)
 	return 0;
 }
 
-struct sunxi_gpio_soc_data {
-	int start;
-	int no_banks;
-};
-
 /**
  * We have a top-level GPIO device with no actual GPIOs. It has a child
  * device for each Sunxi bank.
@@ -353,18 +349,22 @@ static const struct udevice_id sunxi_gpio_ids[] = {
 	ID("allwinner,sun8i-a83t-pinctrl",	a_all),
 	ID("allwinner,sun8i-h3-pinctrl",	a_all),
 	ID("allwinner,sun9i-a80-pinctrl",	a_all),
+#if !defined(CONFIG_SUNXI_PINCTRL)
 	/* This is not strictly correct for the A64, as it is missing
 	 * bank 'A'. Yet, the register layout in the pinctrl block is
 	 * backward compatible and any accesses to the registers that
 	 * normally control bank 'A' will have no adverse effect.
 	 */
-	ID("allwinner,sun50i-a64-pinctrl",      a_all),
+	ID("allwinner,sun50i-a64-pinctrl",	a_all),
+#endif
 	ID("allwinner,sun6i-a31-r-pinctrl",	l_2),
 	ID("allwinner,sun8i-a23-r-pinctrl",	l_1),
 	ID("allwinner,sun8i-a83t-r-pinctrl",	l_1),
 	ID("allwinner,sun8i-h3-r-pinctrl",	l_1),
 	ID("allwinner,sun9i-a80-r-pinctrl",	l_3),
-	ID("allwinner,sun50i-a64-r-pinctrl",    l_1),
+#if !defined(CONFIG_SUNXI_PINCTRL)
+	ID("allwinner,sun50i-a64-r-pinctrl",	l_1),
+#endif
 	{ }
 };
 
@@ -376,4 +376,5 @@ U_BOOT_DRIVER(gpio_sunxi) = {
 	.bind	= gpio_sunxi_bind,
 	.probe	= gpio_sunxi_probe,
 };
+
 #endif
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index efcb4c0..064a682 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -175,6 +175,16 @@ config PIC32_PINCTRL
 	  by a device tree node which contains both GPIO defintion and pin control
 	  functions.
 
+config SUNXI_PINCTRL
+        bool "Allwinner Axx pin-control and pin-mux driver"
+	depends on DM && ARCH_SUNXI
+	default y
+	help
+	  Supports pin multiplexing control, drive-strength and bias control on
+	  Allwinner Axx SoCs. The driver is controlled by a device tree node which
+	  contains both the GPIO definitions and the pin control functions for
+	  each multiplex function.
+
 endif
 
 source "drivers/pinctrl/meson/Kconfig"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index 512112a..da27a91 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -16,3 +16,5 @@ obj-$(CONFIG_PIC32_PINCTRL)	+= pinctrl_pic32.o
 obj-$(CONFIG_PINCTRL_EXYNOS)	+= exynos/
 obj-$(CONFIG_PINCTRL_MESON)	+= meson/
 obj-$(CONFIG_PINCTRL_MVEBU)	+= mvebu/
+
+obj-$(CONFIG_ARCH_SUNXI)        += sunxi/
\ No newline at end of file
diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
new file mode 100644
index 0000000..11549ec
--- /dev/null
+++ b/drivers/pinctrl/sunxi/Makefile
@@ -0,0 +1,10 @@
+#
+# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-$(CONFIG_SUNXI_PINCTRL) += pinctrl-sunxi.o
+ifdef CONFIG_SUNXI_PINCTRL
+obj-$(CONFIG_MACH_SUN50I)   += pinctrl-sun50i-a64.o pinctrl-sun50i-a64-r.o
+endif
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
new file mode 100644
index 0000000..864d1ec
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
@@ -0,0 +1,92 @@
+/*
+ * Allwinner A64 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH.
+ *
+ * Based on pinctrl-sun7i-a20.c, which is:
+ * Copyright (C) 2014 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 <common.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin a64_r_pins[] = {
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_rsb"),		/* SCK */
+		  SUNXI_FUNCTION(0x3, "s_i2c"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_rsb"),		/* SDA */
+		  SUNXI_FUNCTION(0x3, "s_i2c"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_uart"),	/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_uart"),	/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* MS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* CK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* DO */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* DI */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_i2c"),         /* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_i2c"),         /* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_pwm"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "s_cir"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)),	/* EINT11 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)),	/* EINT12 */
+};
+
+const struct sunxi_pinctrl_desc a64_r_pinctrl_data = {
+	.pins = a64_r_pins,
+	.npins = ARRAY_SIZE(a64_r_pins),
+	.pin_base = PL_BASE,
+	.irq_banks = 1,
+};
diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
new file mode 100644
index 0000000..7abea03
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
@@ -0,0 +1,577 @@
+/*
+ * Allwinner A64 SoCs pinctrl driver.
+ *
+ * Copyright (C) 2016 - ARM Ltd.
+ * Author: Andre Przywara <andre.przywara@arm.com>
+ *
+ * Based on pinctrl-sun7i-a20.c, which is:
+ * Copyright (C) 2014 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 <common.h>
+
+#include "pinctrl-sunxi.h"
+
+static const struct sunxi_desc_pin a64_pins[] = {
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
+		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
+		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),	/* EINT9 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
+		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
+		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
+		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
+		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
+		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
+		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
+		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
+		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* PCK */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* CLK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* CK */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* ERR */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* HSYNC */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* SYNC */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* VSYNC */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* DVLD */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D0 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D1 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D2 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D3 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D4 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D5 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D6 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0"),		/* D7 */
+		  SUNXI_FUNCTION(0x4, "ts0")),		/* D7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "csi0")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
+		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
+		  SUNXI_FUNCTION(0x4, "uart0")),	/* RX */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
+		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out")),
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
+		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
+	/* Hole */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
+	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
+		  SUNXI_FUNCTION(0x0, "gpio_in"),
+		  SUNXI_FUNCTION(0x1, "gpio_out"),
+		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
+		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
+};
+
+const struct sunxi_pinctrl_desc a64_pinctrl_data = {
+	.pins = a64_pins,
+	.npins = ARRAY_SIZE(a64_pins),
+	.irq_banks = 3,
+};
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
new file mode 100644
index 0000000..4640cee
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,317 @@
+/*
+ * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * In parts based on linux/drivers/pinctrl/pinctrl-sunxi.c, which is
+ *   Copyright (C) 2012 Maxime Ripard
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <dm.h>
+#include <errno.h>
+#include <syscon.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/gpio.h>
+#include <asm/arch/gpio-internal.h>
+#include <dm/device-internal.h>
+#include <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/pinctrl/sun4i-a10.h>
+#include <dt-bindings/gpio/gpio.h>
+#include <linux/kernel.h>
+#include "pinctrl-sunxi.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_pctrl_priv {
+	void *base;
+#if defined(CONFIG_DM_GPIO)
+	struct sunxi_gpio_soc_data gpio_soc_data;
+	struct udevice *gpio_dev;
+#endif
+};
+
+static int sunxi_pctrl_parse_drive_prop(const void *blob, int node)
+{
+	int val;
+
+	/* Try the new style binding */
+	val = fdtdec_get_int(blob, node, "drive-strength", -EINVAL);
+	if (val >= 0) {
+		/* We can't go below 10mA ... */
+		if (val < 10)
+			return -EINVAL;
+
+		/* ... and only up to 40 mA ... */
+		if (val > 40)
+			val = 40;
+
+		/* by steps of 10 mA */
+		return rounddown(val, 10);
+	}
+
+	/* And then fall back to the old binding */
+	val = fdtdec_get_int(blob, node, "allwinner,drive", -EINVAL);
+	if (val < 0)
+		return -EINVAL;
+
+	return (val + 1) * 10;
+}
+
+static int sunxi_pctrl_parse_bias_prop(const void *blob, int node)
+{
+	/* Try the new style binding */
+	if (fdtdec_get_bool(blob, node, "bias-pull-up"))
+		return SUN4I_PINCTRL_PULL_UP;
+
+	if (fdtdec_get_bool(blob, node, "bias-pull-down"))
+		return SUN4I_PINCTRL_PULL_DOWN;
+
+	if (fdtdec_get_bool(blob, node, "bias-disable"))
+		return SUN4I_PINCTRL_NO_PULL;
+
+	/* And fall back to the old binding */
+	return fdtdec_get_int(blob, node, "allwinner,pull", -EINVAL);
+}
+
+static const struct sunxi_desc_pin *sunxi_pctrl_pin_by_name(struct udevice *dev,
+							    const char *name)
+{
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	int i;
+
+	for (i = 0; i < data->npins; ++i)
+		if (!strcmp(data->pins[i].pin.name, name))
+			return &data->pins[i];
+
+	return NULL;
+}
+
+static int sunxi_pctrl_muxval_by_name(const struct sunxi_desc_pin *pin,
+				      const char *name)
+{
+	const struct sunxi_desc_function *func;
+
+	if (!pin)
+		return -EINVAL;
+
+	for (func = pin->functions; func->name; func++)
+		if (!strcmp(func->name, name))
+			return func->muxval;
+
+	return -ENOENT;
+}
+
+static void sunxi_pctrl_set_function(struct udevice *dev,
+				     unsigned pin, int function)
+{
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	u32 val, mask;
+
+	if (function < 0)
+		return;
+
+	pin -= data->pin_base;
+	mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
+	val = function << sunxi_mux_offset(pin);
+	clrsetbits_le32(priv->base + sunxi_mux_reg(pin), mask, val);
+}
+
+static void sunxi_pctrl_set_dlevel(struct udevice *dev,
+				   unsigned pin, int dlevel)
+{
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	u32 val, mask;
+
+	if (dlevel < 0)
+		return;
+
+	pin -= data->pin_base;
+	mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin);
+	val = dlevel << sunxi_dlevel_offset(pin);
+	clrsetbits_le32(priv->base + sunxi_dlevel_reg(pin), mask, val);
+}
+
+static void sunxi_pctrl_set_bias(struct udevice *dev,
+				 unsigned pin, int bias)
+{
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	u32 val, mask;
+
+	if (bias < 0)
+		return;
+
+	pin -= data->pin_base;
+	mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
+	val = bias << sunxi_pull_offset(pin);
+	clrsetbits_le32(priv->base + sunxi_pull_reg(pin), mask, val);
+}
+
+static int sunxi_pctrl_set_state(struct udevice *dev, struct udevice *config)
+{
+	const char *pins;
+	const char *function;
+	int flen;
+	int len, curr_len;
+	int drive, bias;
+	int muxval;
+
+	debug("%s: %s %s\n", __func__, dev->name, config->name);
+
+	pins = fdt_getprop(gd->fdt_blob, config->of_offset,
+			   "allwinner,pins", &len);
+	if (!pins) {
+		debug("%s: missing allwinner,pins property in node %s\n",
+		      dev->name, config->name);
+		return -EINVAL;
+	}
+
+	function = fdt_getprop(gd->fdt_blob, config->of_offset,
+			       "allwinner,function", &flen);
+	if (!function) {
+		debug("%s: missing allwinner,function property in node %s\n",
+		      dev->name, config->name);
+		return -EINVAL;
+	}
+
+	drive = sunxi_pctrl_parse_drive_prop(gd->fdt_blob, config->of_offset);
+	bias = sunxi_pctrl_parse_bias_prop(gd->fdt_blob, config->of_offset);
+
+	debug("%s: function %s, drive %d, bias %d\n",
+	      config->name, function, drive, bias);
+
+	/* Iterate through the pins and configure each */
+	while (len && (curr_len = strnlen(pins, len))) {
+		const struct sunxi_desc_pin *pin;
+
+		if (curr_len == len) {
+			error("%s: unterminated string?", __func__);
+			break;
+		}
+
+		pin = sunxi_pctrl_pin_by_name(dev, pins);
+		if (pin) {
+			muxval = sunxi_pctrl_muxval_by_name(pin, function);
+
+			sunxi_pctrl_set_function(dev, pin->pin.number, muxval);
+			sunxi_pctrl_set_dlevel(dev, pin->pin.number, drive);
+			sunxi_pctrl_set_bias(dev, pin->pin.number, bias);
+		} else {
+			debug("%s: could not find pin %s\n", dev->name, pins);
+		}
+
+		/* advance */
+		pins += (curr_len + 1);
+		len -= (curr_len + 1);
+	}
+
+	return 0;
+}
+
+static inline void soc_data_from_desc(const struct sunxi_pinctrl_desc *data,
+				      struct sunxi_gpio_soc_data *soc_data)
+{
+	int i;
+	unsigned pinnum;
+	unsigned low = data->pin_base / PINS_PER_BANK;
+	unsigned high = data->pin_base;
+
+	for (i = 0; i < data->npins; ++i) {
+		pinnum = data->pins[i].pin.number;
+		high = max(high, pinnum);
+	}
+
+	/* convert pin-numbers to bank numbers */
+	high /= PINS_PER_BANK;
+
+	soc_data->start = low;
+	soc_data->no_banks = high - low + 1;
+}
+
+static int sunxi_pctrl_bind_gpio(struct udevice *dev)
+{
+#if defined(CONFIG_DM_GPIO)
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	struct driver *gpio_driver;
+	char name[20];
+	int ret;
+
+	/* Fill the soc_data for the gpio driver from the pinctrl_desc */
+	soc_data_from_desc(data, &priv->gpio_soc_data);
+
+	gpio_driver = lists_driver_lookup_name("gpio_sunxi");
+	if (!gpio_driver)
+		return -ENOENT;
+
+	ret = device_bind_with_driver_data(dev, gpio_driver, "sunxi_gpio",
+					   (ulong)&priv->gpio_soc_data,
+					   dev->of_offset, &priv->gpio_dev);
+
+	if (ret < 0)
+		return ret;
+
+	snprintf(name, sizeof(name), "sunxi_gpio@%x", (uint32_t)priv->base);
+	device_set_name(priv->gpio_dev, name);
+#endif
+
+	return 0;
+}
+
+static int sunxi_pctrl_probe(struct udevice *dev)
+{
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr_base;
+	fdt_size_t size;
+
+	addr_base = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
+						       dev->of_offset,
+						       "reg", 0, &size,
+						       false);
+	if (addr_base == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->base = (void *)addr_base;
+
+	return sunxi_pctrl_bind_gpio(dev);
+}
+
+static struct pinctrl_ops sunxi_pctrl_ops = {
+	.set_state	  = sunxi_pctrl_set_state,
+};
+
+#if defined(CONFIG_MACH_SUN50I)
+extern const struct sunxi_pinctrl_desc a64_pinctrl_data;
+extern const struct sunxi_pinctrl_desc a64_r_pinctrl_data;
+#endif
+
+static const struct udevice_id sunxi_pctrl_ids[] = {
+#if defined(CONFIG_MACH_SUN50I)
+	{ .compatible = "allwinner,sun50i-a64-pinctrl",
+	  .data = (ulong)&a64_pinctrl_data },
+	{ .compatible = "allwinner,sun50i-a64-r-pinctrl",
+	  .data = (ulong)&a64_r_pinctrl_data },
+#endif
+	{ }
+};
+
+U_BOOT_DRIVER(pinctrl_sunxi) = {
+	.name		= "sunxi_pctrl",
+	.id		= UCLASS_PINCTRL,
+	.of_match	= sunxi_pctrl_ids,
+	.priv_auto_alloc_size = sizeof(struct sunxi_pctrl_priv),
+	.ops		= &sunxi_pctrl_ops,
+	.bind		= dm_scan_fdt_dev,
+	.probe		= sunxi_pctrl_probe,
+};
+
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
new file mode 100644
index 0000000..8508626
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
@@ -0,0 +1,311 @@
+/*
+ * Allwinner A1X SoCs pinctrl driver.
+ *
+ * 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.
+ */
+
+#ifndef __PINCTRL_SUNXI_H
+#define __PINCTRL_SUNXI_H
+
+#define PA_BASE	0
+#define PB_BASE	32
+#define PC_BASE	64
+#define PD_BASE	96
+#define PE_BASE	128
+#define PF_BASE	160
+#define PG_BASE	192
+#define PH_BASE	224
+#define PI_BASE	256
+#define PL_BASE	352
+#define PM_BASE	384
+#define PN_BASE	416
+
+#ifdef __UBOOT__
+/* Convenience macro to define a single named or anonymous pin descriptor */
+#define PINCTRL_PIN(a, b) { .number = a, .name = b }
+
+/**
+ * struct pinctrl_pin_desc - boards/machines provide information on their
+ * pins, pads or other muxable units in this struct
+ * @number: unique pin number from the global pin number space
+ * @name: a name for this pin
+ * @drv_data: driver-defined per-pin data. pinctrl core does not touch this
+ */
+struct pinctrl_pin_desc {
+	unsigned number;
+	const char *name;
+#ifndef __UBOOT__
+	void *drv_data;
+#endif
+};
+#endif
+
+#define SUNXI_PINCTRL_PIN(bank, pin)		\
+	PINCTRL_PIN(P ## bank ## _BASE + (pin), "P" #bank #pin)
+
+#define SUNXI_PIN_NAME_MAX_LEN	5
+
+#define BANK_MEM_SIZE		0x24
+#define MUX_REGS_OFFSET		0x0
+#define DATA_REGS_OFFSET	0x10
+#define DLEVEL_REGS_OFFSET	0x14
+#define PULL_REGS_OFFSET	0x1c
+
+#define PINS_PER_BANK		32
+#define MUX_PINS_PER_REG	8
+#define MUX_PINS_BITS		4
+#define MUX_PINS_MASK		0x0f
+#define DATA_PINS_PER_REG	32
+#define DATA_PINS_BITS		1
+#define DATA_PINS_MASK		0x01
+#define DLEVEL_PINS_PER_REG	16
+#define DLEVEL_PINS_BITS	2
+#define DLEVEL_PINS_MASK	0x03
+#define PULL_PINS_PER_REG	16
+#define PULL_PINS_BITS		2
+#define PULL_PINS_MASK		0x03
+
+#define IRQ_PER_BANK		32
+
+#define IRQ_CFG_REG		0x200
+#define IRQ_CFG_IRQ_PER_REG		8
+#define IRQ_CFG_IRQ_BITS		4
+#define IRQ_CFG_IRQ_MASK		((1 << IRQ_CFG_IRQ_BITS) - 1)
+#define IRQ_CTRL_REG		0x210
+#define IRQ_CTRL_IRQ_PER_REG		32
+#define IRQ_CTRL_IRQ_BITS		1
+#define IRQ_CTRL_IRQ_MASK		((1 << IRQ_CTRL_IRQ_BITS) - 1)
+#define IRQ_STATUS_REG		0x214
+#define IRQ_STATUS_IRQ_PER_REG		32
+#define IRQ_STATUS_IRQ_BITS		1
+#define IRQ_STATUS_IRQ_MASK		((1 << IRQ_STATUS_IRQ_BITS) - 1)
+
+#define IRQ_MEM_SIZE		0x20
+
+#define IRQ_EDGE_RISING		0x00
+#define IRQ_EDGE_FALLING	0x01
+#define IRQ_LEVEL_HIGH		0x02
+#define IRQ_LEVEL_LOW		0x03
+#define IRQ_EDGE_BOTH		0x04
+
+#define SUN4I_FUNC_INPUT	0
+#define SUN4I_FUNC_IRQ		6
+
+struct sunxi_desc_function {
+	const char	*name;
+	u8		muxval;
+	u8		irqbank;
+	u8		irqnum;
+};
+
+struct sunxi_desc_pin {
+	struct pinctrl_pin_desc		pin;
+	struct sunxi_desc_function	*functions;
+};
+
+struct sunxi_pinctrl_desc {
+	const struct sunxi_desc_pin	*pins;
+	int				npins;
+	unsigned			pin_base;
+	unsigned			irq_banks;
+	unsigned			irq_bank_base;
+	bool				irq_read_needs_mux;
+};
+
+struct sunxi_pinctrl_function {
+	const char	*name;
+	const char	**groups;
+	unsigned	ngroups;
+};
+
+struct sunxi_pinctrl_group {
+	const char	*name;
+	unsigned long	config;
+	unsigned	pin;
+};
+
+#ifndef __UBOOT__
+struct sunxi_pinctrl {
+	void __iomem			*membase;
+	struct gpio_chip		*chip;
+	const struct sunxi_pinctrl_desc	*desc;
+	struct device			*dev;
+	struct irq_domain		*domain;
+	struct sunxi_pinctrl_function	*functions;
+	unsigned			nfunctions;
+	struct sunxi_pinctrl_group	*groups;
+	unsigned			ngroups;
+	int				*irq;
+	unsigned			*irq_array;
+	spinlock_t			lock;
+	struct pinctrl_dev		*pctl_dev;
+};
+#endif
+
+#define SUNXI_PIN(_pin, ...)					\
+	{							\
+		.pin = _pin,					\
+		.functions = (struct sunxi_desc_function[]){	\
+			__VA_ARGS__, { } },			\
+	}
+
+#define SUNXI_FUNCTION(_val, _name)				\
+	{							\
+		.name = _name,					\
+		.muxval = _val,					\
+	}
+
+#define SUNXI_FUNCTION_IRQ(_val, _irq)				\
+	{							\
+		.name = "irq",					\
+		.muxval = _val,					\
+		.irqnum = _irq,					\
+	}
+
+#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)		\
+	{							\
+		.name = "irq",					\
+		.muxval = _val,					\
+		.irqbank = _bank,				\
+		.irqnum = _irq,					\
+	}
+
+/*
+ * The sunXi PIO registers are organized as is:
+ * 0x00 - 0x0c	Muxing values.
+ *		8 pins per register, each pin having a 4bits value
+ * 0x10		Pin values
+ *		32 bits per register, each pin corresponding to one bit
+ * 0x14 - 0x18	Drive level
+ *		16 pins per register, each pin having a 2bits value
+ * 0x1c - 0x20	Pull-Up values
+ *		16 pins per register, each pin having a 2bits value
+ *
+ * This is for the first bank. Each bank will have the same layout,
+ * with an offset being a multiple of 0x24.
+ *
+ * The following functions calculate from the pin number the register
+ * and the bit offset that we should access.
+ */
+static inline u32 sunxi_mux_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += MUX_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_mux_offset(u16 pin)
+{
+	u32 pin_num = pin % MUX_PINS_PER_REG;
+	return pin_num * MUX_PINS_BITS;
+}
+
+static inline u32 sunxi_data_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DATA_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_data_offset(u16 pin)
+{
+	u32 pin_num = pin % DATA_PINS_PER_REG;
+	return pin_num * DATA_PINS_BITS;
+}
+
+static inline u32 sunxi_dlevel_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += DLEVEL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_dlevel_offset(u16 pin)
+{
+	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
+	return pin_num * DLEVEL_PINS_BITS;
+}
+
+static inline u32 sunxi_pull_reg(u16 pin)
+{
+	u8 bank = pin / PINS_PER_BANK;
+	u32 offset = bank * BANK_MEM_SIZE;
+	offset += PULL_REGS_OFFSET;
+	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
+	return round_down(offset, 4);
+}
+
+static inline u32 sunxi_pull_offset(u16 pin)
+{
+	u32 pin_num = pin % PULL_PINS_PER_REG;
+	return pin_num * PULL_PINS_BITS;
+}
+
+static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base)
+{
+	u8 bank = irq / IRQ_PER_BANK;
+	u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04;
+
+	return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg;
+}
+
+static inline u32 sunxi_irq_cfg_offset(u16 irq)
+{
+	u32 irq_num = irq % IRQ_CFG_IRQ_PER_REG;
+	return irq_num * IRQ_CFG_IRQ_BITS;
+}
+
+static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base)
+{
+	return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE;
+}
+
+static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base)
+{
+	u8 bank = irq / IRQ_PER_BANK;
+
+	return sunxi_irq_ctrl_reg_from_bank(bank, bank_base);
+}
+
+static inline u32 sunxi_irq_ctrl_offset(u16 irq)
+{
+	u32 irq_num = irq % IRQ_CTRL_IRQ_PER_REG;
+	return irq_num * IRQ_CTRL_IRQ_BITS;
+}
+
+static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
+{
+	return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
+}
+
+static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base)
+{
+	u8 bank = irq / IRQ_PER_BANK;
+
+	return sunxi_irq_status_reg_from_bank(bank, bank_base);
+}
+
+static inline u32 sunxi_irq_status_offset(u16 irq)
+{
+	u32 irq_num = irq % IRQ_STATUS_IRQ_PER_REG;
+	return irq_num * IRQ_STATUS_IRQ_BITS;
+}
+
+#ifndef __UBOOT__
+int sunxi_pinctrl_init(struct platform_device *pdev,
+		       const struct sunxi_pinctrl_desc *desc);
+#endif
+
+#endif /* __PINCTRL_SUNXI_H */
-- 
1.9.1

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-03-03  4:52   ` Simon Glass
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 3/6] sunxi: CONFIG_DM_ALLOW_MULTIPLE_DRIVERS for gpio/pinctrl binding Philipp Tomsich
                   ` (3 subsequent siblings)
  5 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

Currently, driver binding stops once it encounters the first
compatible driver that doesn't refuse to bind. However, there are
cases where a single node will need to be handled by multiple driver
classes. For those cases we provide a configurable option to continue
to bind after the first driver has been found.

The first use cases for this are from the DM conversion of the sunxi
(Allwinner) architecture:
 * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
   bind against a single node
 * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
   bind against a single node

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/core/Kconfig | 14 ++++++++++++++
 drivers/core/lists.c | 12 +++++++++++-
 2 files changed, 25 insertions(+), 1 deletion(-)

diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
index 8749561..913101c 100644
--- a/drivers/core/Kconfig
+++ b/drivers/core/Kconfig
@@ -31,6 +31,20 @@ config DM_WARN
 	  This will cause dm_warn() to be compiled out - it will do nothing
 	  when called.
 
+config DM_ALLOW_MULTIPLE_DRIVERS
+        bool "Allow multiple drivers to bind for one node"
+	depends on DM
+	default n
+	help
+	  The driver model in U-Boot originally did not allow multiple
+	  drivers to bind for a single device node.
+
+	  If enabled, multiple drivers can now bind for a single node
+	  by using the same compatible string for matching: lists_bind_fdt()
+	  will assume that binding multiple drivers is desirable, if the
+	  caller does not request the pointer to the udevice structure to
+	  be returned (i.e. if devp is NULL).
+
 config DM_DEVICE_REMOVE
 	bool "Support device removal"
 	depends on DM
diff --git a/drivers/core/lists.c b/drivers/core/lists.c
index 23b6ba7..52efe69 100644
--- a/drivers/core/lists.c
+++ b/drivers/core/lists.c
@@ -166,7 +166,11 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 		dm_dbg("   - attempt to match compatible string '%s'\n",
 		       compat);
 
-		for (entry = driver; entry != driver + n_ents; entry++) {
+		entry = driver;
+#if defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
+	allow_more_matches:
+#endif
+		for (; entry != driver + n_ents; entry++) {
 			ret = driver_check_compatible(entry->of_match, &id,
 						      compat);
 			if (!ret)
@@ -190,6 +194,12 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
 			found = true;
 			if (devp)
 				*devp = dev;
+#if defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
+			else {
+				entry++;
+				goto allow_more_matches;
+			}
+#endif
 		}
 		break;
 	}
-- 
1.9.1

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

* [U-Boot] [PATCH v2 3/6] sunxi: CONFIG_DM_ALLOW_MULTIPLE_DRIVERS for gpio/pinctrl binding
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS Philipp Tomsich
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

Our gpio and pinctrl driver need to be bound against the same
node. While this can be done by hand (i.e. explicitly looking up the
driver, creating the driver-data and binding the device), it is much
easier done when the new option for the binding of multiple drivers
against a single node is configured.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/gpio/sunxi_gpio.c             | 4 ++--
 drivers/pinctrl/sunxi/pinctrl-sunxi.c | 8 ++++++--
 2 files changed, 8 insertions(+), 4 deletions(-)

diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
index fd0c1ac..cbec1b9 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -349,7 +349,7 @@ static const struct udevice_id sunxi_gpio_ids[] = {
 	ID("allwinner,sun8i-a83t-pinctrl",	a_all),
 	ID("allwinner,sun8i-h3-pinctrl",	a_all),
 	ID("allwinner,sun9i-a80-pinctrl",	a_all),
-#if !defined(CONFIG_SUNXI_PINCTRL)
+#if !defined(CONFIG_SUNXI_PINCTRL) || defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 	/* This is not strictly correct for the A64, as it is missing
 	 * bank 'A'. Yet, the register layout in the pinctrl block is
 	 * backward compatible and any accesses to the registers that
@@ -362,7 +362,7 @@ static const struct udevice_id sunxi_gpio_ids[] = {
 	ID("allwinner,sun8i-a83t-r-pinctrl",	l_1),
 	ID("allwinner,sun8i-h3-r-pinctrl",	l_1),
 	ID("allwinner,sun9i-a80-r-pinctrl",	l_3),
-#if !defined(CONFIG_SUNXI_PINCTRL)
+#if !defined(CONFIG_SUNXI_PINCTRL) || defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 	ID("allwinner,sun50i-a64-r-pinctrl",	l_1),
 #endif
 	{ }
diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
index 4640cee..445707e 100644
--- a/drivers/pinctrl/sunxi/pinctrl-sunxi.c
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -14,7 +14,9 @@
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/gpio.h>
+#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 #include <asm/arch/gpio-internal.h>
+#endif
 #include <dm/device-internal.h>
 #include <dm/lists.h>
 #include <dm/pinctrl.h>
@@ -27,7 +29,7 @@ DECLARE_GLOBAL_DATA_PTR;
 
 struct sunxi_pctrl_priv {
 	void *base;
-#if defined(CONFIG_DM_GPIO)
+#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 	struct sunxi_gpio_soc_data gpio_soc_data;
 	struct udevice *gpio_dev;
 #endif
@@ -217,6 +219,7 @@ static int sunxi_pctrl_set_state(struct udevice *dev, struct udevice *config)
 	return 0;
 }
 
+#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 static inline void soc_data_from_desc(const struct sunxi_pinctrl_desc *data,
 				      struct sunxi_gpio_soc_data *soc_data)
 {
@@ -236,10 +239,11 @@ static inline void soc_data_from_desc(const struct sunxi_pinctrl_desc *data,
 	soc_data->start = low;
 	soc_data->no_banks = high - low + 1;
 }
+#endif
 
 static int sunxi_pctrl_bind_gpio(struct udevice *dev)
 {
-#if defined(CONFIG_DM_GPIO)
+#if defined(CONFIG_DM_GPIO) && !defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
 	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
 	const struct sunxi_pinctrl_desc *data =
 		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
-- 
1.9.1

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

* [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
                   ` (2 preceding siblings ...)
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 3/6] sunxi: CONFIG_DM_ALLOW_MULTIPLE_DRIVERS for gpio/pinctrl binding Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-02-22 23:19   ` Maxime Ripard
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 5/6] sun50i: dts: add r_pio node and pinconfig entries into r_pio and pio Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
  5 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

Now that we have the new functionality to allow multiple drivers to
bind for a single node, we want to enable it for the A64-uQ7.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 configs/lynx_defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/configs/lynx_defconfig b/configs/lynx_defconfig
index 7b773f1..0fba279 100644
--- a/configs/lynx_defconfig
+++ b/configs/lynx_defconfig
@@ -2,6 +2,7 @@ CONFIG_ARM=y
 CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y
 CONFIG_ARCH_SUNXI=y
 CONFIG_MACH_SUN50I=y
+CONFIG_DM_ALLOW_MULTIPLE_DRIVERS=y
 CONFIG_PINCTRL=y
 CONFIG_SUNXI_PINCTRL=y
 CONFIG_CLK=y
-- 
1.9.1

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

* [U-Boot] [PATCH v2 5/6] sun50i: dts: add r_pio node and pinconfig entries into r_pio and pio
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
                   ` (3 preceding siblings ...)
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
  5 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

To sync up with use of a pinctrl-driver, this updates the DTS.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 arch/arm/dts/sun50i-a64.dtsi | 7 +++----
 1 file changed, 3 insertions(+), 4 deletions(-)

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index 24406d0..54b68e0 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -376,9 +376,9 @@
 			gpio-controller;
 			#gpio-cells = <3>;
 			interrupt-controller;
-			#interrupt-cells = <2>;
+			#interrupt-cells = <3>;
 
-			uart0_pins_a: uart0 at 0 {
+			uart0_pins_a: uart0_pins_a {
 				allwinner,pins = "PB8", "PB9";
 				allwinner,function = "uart0";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
@@ -532,10 +532,9 @@
 			reg = <0x01f02c00 0x400>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 			gpio-controller;
+			#gpio-cells = <3>;
 			interrupt-controller;
 			#interrupt-cells = <3>;
-			#size-cells = <0>;
-			#gpio-cells = <3>;
 		};
 
 		ahb_rst: reset at 1c202c0 {
-- 
1.9.1

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

* [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings
  2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
                   ` (4 preceding siblings ...)
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 5/6] sun50i: dts: add r_pio node and pinconfig entries into r_pio and pio Philipp Tomsich
@ 2017-02-22 20:47 ` Philipp Tomsich
  2017-02-22 23:20   ` Maxime Ripard
  5 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-22 20:47 UTC (permalink / raw)
  To: u-boot

Nodes that don't contain a reg-entry should not have an @xxx name
attached.  To silence the dt-compiler warnings, we update the DTS.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 arch/arm/dts/sun50i-a64.dtsi | 57 ++++++++++++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 21 deletions(-)

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index 54b68e0..4911d6a 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -88,7 +88,7 @@
 		method = "smc";
 	};
 
-	memory {
+	memory: memory at 40000000 {
 		device_type = "memory";
 		reg = <0x40000000 0>;
 	};
@@ -378,84 +378,84 @@
 			interrupt-controller;
 			#interrupt-cells = <3>;
 
-			uart0_pins_a: uart0_pins_a {
+			uart0_pins_a: uart0-pins-a {
 				allwinner,pins = "PB8", "PB9";
 				allwinner,function = "uart0";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart0_pins_b: uart0 at 1 {
+			uart0_pins_b: uart0-pins-b {
 				allwinner,pins = "PF2", "PF3";
 				allwinner,function = "uart0";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart1_2pins: uart1_2 at 0 {
+			uart1_2pins: uart1-2pins {
 				allwinner,pins = "PG6", "PG7";
 				allwinner,function = "uart1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart1_4pins: uart1_4 at 0 {
+			uart1_4pins: uart1-4pins {
 				allwinner,pins = "PG6", "PG7", "PG8", "PG9";
 				allwinner,function = "uart1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart2_2pins: uart2_2 at 0 {
+			uart2_2pins: uart2-2pins {
 				allwinner,pins = "PB0", "PB1";
 				allwinner,function = "uart2";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart2_4pins: uart2_4 at 0 {
+			uart2_4pins: uart2-4pins {
 				allwinner,pins = "PB0", "PB1", "PB2", "PB3";
 				allwinner,function = "uart2";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart3_pins_a: uart3 at 0 {
+			uart3_pins_a: uart3-pins-a {
 				allwinner,pins = "PD0", "PD1";
 				allwinner,function = "uart3";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart3_2pins_b: uart3_2 at 1 {
+			uart3_2pins_b: uart3-2pins-b {
 				allwinner,pins = "PH4", "PH5";
 				allwinner,function = "uart3";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart3_4pins_b: uart3_4 at 1 {
+			uart3_4pins_b: uart3-4pins-b {
 				allwinner,pins = "PH4", "PH5", "PH6", "PH7";
 				allwinner,function = "uart3";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart4_2pins: uart4_2 at 0 {
+			uart4_2pins: uart4-2pins {
 				allwinner,pins = "PD2", "PD3";
 				allwinner,function = "uart4";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			uart4_4pins: uart4_4 at 0 {
+			uart4_4pins: uart4-4pins {
 				allwinner,pins = "PD2", "PD3", "PD4", "PD5";
 				allwinner,function = "uart4";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			mmc0_pins: mmc0 at 0 {
+			mmc0_pins: mmc0-pins {
 				allwinner,pins = "PF0", "PF1", "PF2", "PF3",
 						 "PF4", "PF5";
 				allwinner,function = "mmc0";
@@ -463,14 +463,14 @@
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			mmc0_default_cd_pin: mmc0_cd_pin at 0 {
+			mmc0_default_cd_pin: mmc0-cd-pin {
 				allwinner,pins = "PF6";
 				allwinner,function = "gpio_in";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
 			};
 
-			mmc1_pins: mmc1 at 0 {
+			mmc1_pins: mmc1-pins {
 				allwinner,pins = "PG0", "PG1", "PG2", "PG3",
 						 "PG4", "PG5";
 				allwinner,function = "mmc1";
@@ -478,36 +478,51 @@
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			mmc2_pins: mmc2 at 0 {
+			mmc2_pins: mmc2-pins {
 				allwinner,pins = "PC1", "PC5", "PC6", "PC8",
 						 "PC9", "PC10";
 				allwinner,function = "mmc2";
+<<<<<<< HEAD
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+=======
+				drive-strength = < 30 >;
+				bias-pull-up;
 			};
 
-			i2c0_pins: i2c0_pins {
+			mmc2_8bit_pins: mmc2-8bit {
+				allwinner,pins = "PC5", "PC6", "PC8",
+						 "PC9", "PC10", "PC11",
+						 "PC12", "PC13", "PC14",
+						 "PC15", "PC16";
+				allwinner,function = "mmc2";
+				drive-strength = < 30 >;
+				bias-pull-up;
+>>>>>>> 20221b3... [f] dts warnings
+			};
+
+			i2c0_pins: i2c0-pins {
 				allwinner,pins = "PH0", "PH1";
 				allwinner,function = "i2c0";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			i2c1_pins: i2c1_pins {
+			i2c1_pins: i2c1-pins {
 				allwinner,pins = "PH2", "PH3";
 				allwinner,function = "i2c1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			i2c2_pins: i2c2_pins {
+			i2c2_pins: i2c2-pins {
 				allwinner,pins = "PE14", "PE15";
 				allwinner,function = "i2c2";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			rmii_pins: rmii_pins {
+			rmii_pins: rmii-pins {
 				allwinner,pins = "PD10", "PD11", "PD13", "PD14",
 						 "PD17", "PD18", "PD19", "PD20",
 						 "PD22", "PD23";
@@ -516,7 +531,7 @@
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
-			rgmii_pins: rgmii_pins {
+			rgmii_pins: rgmii-pins {
 				allwinner,pins = "PD8", "PD9", "PD10", "PD11",
 						 "PD12", "PD13", "PD15",
 						 "PD16", "PD17", "PD18", "PD19",
-- 
1.9.1

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

* [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi Philipp Tomsich
@ 2017-02-22 23:18   ` Maxime Ripard
  2017-02-23  3:54     ` Chen-Yu Tsai
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2017-02-22 23:18 UTC (permalink / raw)
  To: u-boot

On Wed, Feb 22, 2017 at 09:47:27PM +0100, Philipp Tomsich wrote:
> This change adds a full device-model pinctrl driver for sunxi (tested with
> sun50iw1p1) based on the support available in Linux.
> 
> Details are:
>  * implements a driver for pinctrl devices and assigns sun50i-a64-pinctrl
>    and sun50i-a64-r-pinctrl to it
>  * dynamically creates the driver_data for a sunxi_gpio (see sunxi_gpio.c)
>    driver and binds it to the same device-tree node
>  * lifts and reuses the pinctrl-sunxi.h and pinctrl-sun50i-a64.c files from
>    Linux (thanks to Maxime and Andre) and adds a pinctrl-sun50i-a64-r.c (to
>    be picked up for inclusion into Linux again)
> 
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  arch/arm/include/asm/arch-sunxi/gpio-internal.h    |  19 +
>  .../pinctrl/allwinner,pinctrl.txt                  |  65 +++
>  drivers/gpio/sunxi_gpio.c                          |  15 +-
>  drivers/pinctrl/Kconfig                            |  10 +
>  drivers/pinctrl/Makefile                           |   2 +
>  drivers/pinctrl/sunxi/Makefile                     |  10 +
>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c       |  92 ++++
>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c         | 577 +++++++++++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 317 +++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
>  10 files changed, 1411 insertions(+), 7 deletions(-)
>  create mode 100644 arch/arm/include/asm/arch-sunxi/gpio-internal.h
>  create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
>  create mode 100644 drivers/pinctrl/sunxi/Makefile
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
> 
> diff --git a/arch/arm/include/asm/arch-sunxi/gpio-internal.h b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
> new file mode 100644
> index 0000000..4dcdd34
> --- /dev/null
> +++ b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
> @@ -0,0 +1,19 @@
> +/*
> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +
> +#ifndef _SUNXI_GPIO_INTERNAL_H
> +#define _SUNXI_GPIO_INTERNAL_H
> +
> +/* This data structure is shared between the sunxi_gpio driver and
> + * the sunxi_pinctrl driver.
> + */
> +struct sunxi_gpio_soc_data {
> +	int start;
> +	int no_banks;
> +};
> +
> +#endif
> diff --git a/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> new file mode 100644
> index 0000000..946831f
> --- /dev/null
> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> @@ -0,0 +1,65 @@
> +* Allwinner Pinmux Controller
> +
> +Allwinner integrates multiple banks (of 32 pins each) of pin-muxing,
> +GPIO functionality and (optional) external interrupt functionality
> +into a single controller.
> +
> +For each configurable pad (certain driver-cells, such as the IO from
> +integrated USB PHYs or DRAM, have a fixed function and can not be
> +configured), the muxing options (input, output or one of the several
> +functions) can be selected.
> +
> +Properties for the pinctrl node:
> + - compatible: should be "allwinner,sun50i-pinctrl"

There's a typo here, the compatible is sun50i-a64-pinctrl

> + - reg: address and length of the register set for the device.
> + - interrupts: interrupt for the device
> + - clocks: A phandle to the reference clock for this device

(and ideally, this would take three clocks: the bus gate + the two
oscillators).

> +
> +Properties for the pinconfig sub-nodes:
> + - allwinner,pins: a list of pins (e.g. "PH2", "PH3") to configure
> + - allwinner,function: the name of pinmux function (e.g. "mmc2")

allwinner,pins and allwinner,function are also deprecated in favour of
pins and function.

> + - drive-strength: a drive-stength setting of 10, 20, 30 or 40 mA
> + - bias-pull-up
> + - bias-pull-down
> + - bias-disable (default)

The default is not bias-disable, but to keep the current configuration

> +
> +Deprecated properties for the pinconfig sub-nodes:
> + - allwinner,drive: one of <SUN4I_PINCTRL_10_MA>, <SUN4I_PINCTRL_20_MA>,
> +                    <SUN4I_PINCTRL_30_MA> or <SUN4I_PINCTRL_40_MA>
> + - allwinner,pull: one of <SUN4I_PINCTRL_NO_PULL>, <SUN4I_PINCTRL_PULL_UP>
> +    		   or <SUN4I_PINCTRL_PULL_DOWN>
> +
> +Example:
> +
> +	pio: pinctrl at 1c20800 {
> +		compatible = "allwinner,sun50i-a64-pinctrl";
> +		reg = <0x01c20800 0x400>;
> +
> +		interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
> +			     <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
> +			     <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
> +		clocks = <&bus_gates 69>;
> +
> +		gpio-controller;
> +		#gpio-cells = <3>;
> +
> +		interrupt-controller;
> +		#interrupt-cells = <2>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <1>;
> +
> +		uart0_pins_a: uart0_pins_a {
> +			allwinner,pins = "PB8", "PB9";
> +			allwinner,function = "uart0";
> +			allwinner,drive = <SUN4I_PINCTRL_10_MA>;
> +			allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
> +		};
> +
> +		uart0_pins_b: uart0_pins_b {
> +			allwinner,pins = "PF2", "PF3";
> +			allwinner,function = "uart0";
> +			allwinner,drive = <SUN4I_PINCTRL_10_MA>;
> +			allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
> +		};
> +	};
> diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
> index 2b7bc7f..fd0c1ac 100644
> --- a/drivers/gpio/sunxi_gpio.c
> +++ b/drivers/gpio/sunxi_gpio.c
> @@ -16,6 +16,7 @@
>  #include <fdtdec.h>
>  #include <malloc.h>
>  #include <asm/arch/gpio.h>
> +#include <asm/arch/gpio-internal.h>
>  #include <asm/io.h>
>  #include <asm/gpio.h>
>  #include <dm/device-internal.h>
> @@ -275,11 +276,6 @@ static int gpio_sunxi_probe(struct udevice *dev)
>  	return 0;
>  }
>  
> -struct sunxi_gpio_soc_data {
> -	int start;
> -	int no_banks;
> -};
> -
>  /**
>   * We have a top-level GPIO device with no actual GPIOs. It has a child
>   * device for each Sunxi bank.
> @@ -353,18 +349,22 @@ static const struct udevice_id sunxi_gpio_ids[] = {
>  	ID("allwinner,sun8i-a83t-pinctrl",	a_all),
>  	ID("allwinner,sun8i-h3-pinctrl",	a_all),
>  	ID("allwinner,sun9i-a80-pinctrl",	a_all),
> +#if !defined(CONFIG_SUNXI_PINCTRL)
>  	/* This is not strictly correct for the A64, as it is missing
>  	 * bank 'A'. Yet, the register layout in the pinctrl block is
>  	 * backward compatible and any accesses to the registers that
>  	 * normally control bank 'A' will have no adverse effect.
>  	 */
> -	ID("allwinner,sun50i-a64-pinctrl",      a_all),
> +	ID("allwinner,sun50i-a64-pinctrl",	a_all),
> +#endif
>  	ID("allwinner,sun6i-a31-r-pinctrl",	l_2),
>  	ID("allwinner,sun8i-a23-r-pinctrl",	l_1),
>  	ID("allwinner,sun8i-a83t-r-pinctrl",	l_1),
>  	ID("allwinner,sun8i-h3-r-pinctrl",	l_1),
>  	ID("allwinner,sun9i-a80-r-pinctrl",	l_3),
> -	ID("allwinner,sun50i-a64-r-pinctrl",    l_1),
> +#if !defined(CONFIG_SUNXI_PINCTRL)
> +	ID("allwinner,sun50i-a64-r-pinctrl",	l_1),
> +#endif
>  	{ }
>  };
>  
> @@ -376,4 +376,5 @@ U_BOOT_DRIVER(gpio_sunxi) = {
>  	.bind	= gpio_sunxi_bind,
>  	.probe	= gpio_sunxi_probe,
>  };
> +
>  #endif
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index efcb4c0..064a682 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -175,6 +175,16 @@ config PIC32_PINCTRL
>  	  by a device tree node which contains both GPIO defintion and pin control
>  	  functions.
>  
> +config SUNXI_PINCTRL
> +        bool "Allwinner Axx pin-control and pin-mux driver"
> +	depends on DM && ARCH_SUNXI
> +	default y
> +	help
> +	  Supports pin multiplexing control, drive-strength and bias control on
> +	  Allwinner Axx SoCs. The driver is controlled by a device tree node which
> +	  contains both the GPIO definitions and the pin control functions for
> +	  each multiplex function.
> +
>  endif
>  
>  source "drivers/pinctrl/meson/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 512112a..da27a91 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -16,3 +16,5 @@ obj-$(CONFIG_PIC32_PINCTRL)	+= pinctrl_pic32.o
>  obj-$(CONFIG_PINCTRL_EXYNOS)	+= exynos/
>  obj-$(CONFIG_PINCTRL_MESON)	+= meson/
>  obj-$(CONFIG_PINCTRL_MVEBU)	+= mvebu/
> +
> +obj-$(CONFIG_ARCH_SUNXI)        += sunxi/
> \ No newline at end of file
> diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
> new file mode 100644
> index 0000000..11549ec
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Makefile
> @@ -0,0 +1,10 @@
> +#
> +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> +#
> +# SPDX-License-Identifier:	GPL-2.0+
> +#
> +
> +obj-$(CONFIG_SUNXI_PINCTRL) += pinctrl-sunxi.o
> +ifdef CONFIG_SUNXI_PINCTRL
> +obj-$(CONFIG_MACH_SUN50I)   += pinctrl-sun50i-a64.o pinctrl-sun50i-a64-r.o
> +endif
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> new file mode 100644
> index 0000000..864d1ec
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> @@ -0,0 +1,92 @@
> +/*
> + * Allwinner A64 SoCs pinctrl driver.
> + *
> + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH.
> + *
> + * Based on pinctrl-sun7i-a20.c, which is:
> + * Copyright (C) 2014 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 <common.h>
> +
> +#include "pinctrl-sunxi.h"
> +
> +static const struct sunxi_desc_pin a64_r_pins[] = {
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_rsb"),		/* SCK */
> +		  SUNXI_FUNCTION(0x3, "s_i2c"),		/* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_rsb"),		/* SDA */
> +		  SUNXI_FUNCTION(0x3, "s_i2c"),		/* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_uart"),	/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_uart"),	/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* MS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* CK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),	/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* DO */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_jtag"),        /* DI */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_i2c"),         /* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_i2c"),         /* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),	/* EINT9 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_pwm"),
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)),	/* EINT10 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "s_cir"),
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)),	/* EINT11 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)),	/* EINT12 */
> +};
> +
> +const struct sunxi_pinctrl_desc a64_r_pinctrl_data = {
> +	.pins = a64_r_pins,
> +	.npins = ARRAY_SIZE(a64_r_pins),
> +	.pin_base = PL_BASE,
> +	.irq_banks = 1,
> +};
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> new file mode 100644
> index 0000000..7abea03
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> @@ -0,0 +1,577 @@
> +/*
> + * Allwinner A64 SoCs pinctrl driver.
> + *
> + * Copyright (C) 2016 - ARM Ltd.
> + * Author: Andre Przywara <andre.przywara@arm.com>
> + *
> + * Based on pinctrl-sun7i-a20.c, which is:
> + * Copyright (C) 2014 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 <common.h>
> +
> +#include "pinctrl-sunxi.h"
> +
> +static const struct sunxi_desc_pin a64_pins[] = {
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* MS0 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* CK0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VCCEN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* RTS */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DO0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPEN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart2"),		/* CTS */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* MCLK */
> +		  SUNXI_FUNCTION(0x4, "jtag"),		/* DI0 */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* VPPPP */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* DATA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),		/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* RST */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif2"),		/* DIN */
> +		  SUNXI_FUNCTION(0x3, "i2s0"),		/* DIN */
> +		  SUNXI_FUNCTION(0x5, "sim"),		/* DET */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "uart0"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "uart0"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),	/* EINT9 */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NWE */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MOSI */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NALE */
> +		  SUNXI_FUNCTION(0x3, "mmc2"),		/* DS */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* MISO */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCLE */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NCE1 */
> +		  SUNXI_FUNCTION(0x4, "spi0")),		/* CS */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NCE0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRE# */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NRB0 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* CMD */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0")),	/* NRB1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ0 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ1 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ2 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ3 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ4 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ5 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ6 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQ7 */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "nand0"),		/* NDQS */
> +		  SUNXI_FUNCTION(0x3, "mmc2")),		/* RST */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D2 */
> +		  SUNXI_FUNCTION(0x3, "uart3"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D3 */
> +		  SUNXI_FUNCTION(0x3, "uart3"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* CLK */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* DE */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D4 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* TX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MOSI */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* HSYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D5 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RX */
> +		  SUNXI_FUNCTION(0x4, "spi1"),		/* MISO */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* VSYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D6 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* RTS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D7 */
> +		  SUNXI_FUNCTION(0x3, "uart4"),		/* CTS */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D10 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D11 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D12 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD3 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D13 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ERXD2 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D14 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D15 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXD0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D18 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D19 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ERXCTL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D20 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP1 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ENULL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D21 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN1 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD3 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D22 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP2 */
> +		  SUNXI_FUNCTION(0x4, "emac"),		/* ETXD2 */
> +		  SUNXI_FUNCTION(0x5, "ccir")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* D23 */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN2 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* CLK */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VPC */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXD0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* DE */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VNC */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* HSYNC */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VP3 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ETXCTL */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "lcd0"),		/* VSYNC */
> +		  SUNXI_FUNCTION(0x3, "lvds0"),		/* VN3 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* ECLKIN */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "pwm"),		/* PWM0 */
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x4, "emac")),		/* EMDIO */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* PCK */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* CLK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* CK */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* ERR */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* HSYNC */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* SYNC */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* VSYNC */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* DVLD */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D0 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D1 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D2 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D3 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D4 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D5 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D6 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0"),		/* D7 */
> +		  SUNXI_FUNCTION(0x4, "ts0")),		/* D7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "csi0")),		/* SDA */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "pll"),		/* LOCK_DBG */
> +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SCK */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x3, "i2c2")),		/* SDA */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D1 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* MSI */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D0 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DI1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CLK */
> +		  SUNXI_FUNCTION(0x3, "uart0")),	/* TX */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* CMD */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* DO1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D3 */
> +		  SUNXI_FUNCTION(0x4, "uart0")),	/* RX */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc0"),		/* D2 */
> +		  SUNXI_FUNCTION(0x3, "jtag")),		/* CK1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out")),
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* CMD */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D0 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D1 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D2 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mmc1"),		/* D3 */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),	/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* RTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart1"),		/* CTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),	/* EINT9 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* SYNC */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* SYNC */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)),	/* EINT10 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* BCLK */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* BCLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)),	/* EINT11 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DOUT */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DOUT */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)),	/* EINT12 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "aif3"),		/* DIN */
> +		  SUNXI_FUNCTION(0x3, "i2s1"),		/* DIN */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)),	/* EINT13 */
> +	/* Hole */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),	/* EINT0 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c0"),		/* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),	/* EINT1 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SCK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),	/* EINT2 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "i2c1"),		/* SDA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),	/* EINT3 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* TX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),	/* EINT4 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RX */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),	/* EINT5 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* RTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),	/* EINT6 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "uart3"),		/* CTS */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),	/* EINT7 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "spdif"),		/* OUT */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),	/* EINT8 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),	/* EINT9 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mic"),		/* CLK */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)),	/* EINT10 */
> +	SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
> +		  SUNXI_FUNCTION(0x0, "gpio_in"),
> +		  SUNXI_FUNCTION(0x1, "gpio_out"),
> +		  SUNXI_FUNCTION(0x2, "mic"),		/* DATA */
> +		  SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)),	/* EINT11 */
> +};
> +
> +const struct sunxi_pinctrl_desc a64_pinctrl_data = {
> +	.pins = a64_pins,
> +	.npins = ARRAY_SIZE(a64_pins),
> +	.irq_banks = 3,
> +};
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> new file mode 100644
> index 0000000..4640cee
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> @@ -0,0 +1,317 @@
> +/*
> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
> + *
> + * In parts based on linux/drivers/pinctrl/pinctrl-sunxi.c, which is
> + *   Copyright (C) 2012 Maxime Ripard
> + *
> + * SPDX-License-Identifier:	GPL-2.0+
> + */
> +
> +#include <common.h>
> +#include <dm.h>
> +#include <errno.h>
> +#include <syscon.h>
> +#include <asm/io.h>
> +#include <asm/arch/clock.h>
> +#include <asm/gpio.h>
> +#include <asm/arch/gpio-internal.h>
> +#include <dm/device-internal.h>
> +#include <dm/lists.h>
> +#include <dm/pinctrl.h>
> +#include <dt-bindings/pinctrl/sun4i-a10.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include <linux/kernel.h>
> +#include "pinctrl-sunxi.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct sunxi_pctrl_priv {
> +	void *base;
> +#if defined(CONFIG_DM_GPIO)
> +	struct sunxi_gpio_soc_data gpio_soc_data;
> +	struct udevice *gpio_dev;
> +#endif
> +};
> +
> +static int sunxi_pctrl_parse_drive_prop(const void *blob, int node)
> +{
> +	int val;
> +
> +	/* Try the new style binding */
> +	val = fdtdec_get_int(blob, node, "drive-strength", -EINVAL);
> +	if (val >= 0) {
> +		/* We can't go below 10mA ... */
> +		if (val < 10)
> +			return -EINVAL;
> +
> +		/* ... and only up to 40 mA ... */
> +		if (val > 40)
> +			val = 40;
> +
> +		/* by steps of 10 mA */
> +		return rounddown(val, 10);
> +	}
> +
> +	/* And then fall back to the old binding */
> +	val = fdtdec_get_int(blob, node, "allwinner,drive", -EINVAL);
> +	if (val < 0)
> +		return -EINVAL;
> +
> +	return (val + 1) * 10;
> +}
> +
> +static int sunxi_pctrl_parse_bias_prop(const void *blob, int node)
> +{
> +	/* Try the new style binding */
> +	if (fdtdec_get_bool(blob, node, "bias-pull-up"))
> +		return SUN4I_PINCTRL_PULL_UP;
> +
> +	if (fdtdec_get_bool(blob, node, "bias-pull-down"))
> +		return SUN4I_PINCTRL_PULL_DOWN;
> +
> +	if (fdtdec_get_bool(blob, node, "bias-disable"))
> +		return SUN4I_PINCTRL_NO_PULL;
> +
> +	/* And fall back to the old binding */
> +	return fdtdec_get_int(blob, node, "allwinner,pull", -EINVAL);
> +}
> +
> +static const struct sunxi_desc_pin *sunxi_pctrl_pin_by_name(struct udevice *dev,
> +							    const char *name)
> +{
> +	const struct sunxi_pinctrl_desc *data =
> +		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +	int i;
> +
> +	for (i = 0; i < data->npins; ++i)
> +		if (!strcmp(data->pins[i].pin.name, name))
> +			return &data->pins[i];
> +
> +	return NULL;
> +}
> +
> +static int sunxi_pctrl_muxval_by_name(const struct sunxi_desc_pin *pin,
> +				      const char *name)
> +{
> +	const struct sunxi_desc_function *func;
> +
> +	if (!pin)
> +		return -EINVAL;
> +
> +	for (func = pin->functions; func->name; func++)
> +		if (!strcmp(func->name, name))
> +			return func->muxval;
> +
> +	return -ENOENT;
> +}
> +
> +static void sunxi_pctrl_set_function(struct udevice *dev,
> +				     unsigned pin, int function)
> +{
> +	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +	const struct sunxi_pinctrl_desc *data =
> +		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +	u32 val, mask;
> +
> +	if (function < 0)
> +		return;
> +
> +	pin -= data->pin_base;
> +	mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
> +	val = function << sunxi_mux_offset(pin);
> +	clrsetbits_le32(priv->base + sunxi_mux_reg(pin), mask, val);
> +}
> +
> +static void sunxi_pctrl_set_dlevel(struct udevice *dev,
> +				   unsigned pin, int dlevel)
> +{
> +	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +	const struct sunxi_pinctrl_desc *data =
> +		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +	u32 val, mask;
> +
> +	if (dlevel < 0)
> +		return;
> +
> +	pin -= data->pin_base;
> +	mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin);
> +	val = dlevel << sunxi_dlevel_offset(pin);
> +	clrsetbits_le32(priv->base + sunxi_dlevel_reg(pin), mask, val);
> +}
> +
> +static void sunxi_pctrl_set_bias(struct udevice *dev,
> +				 unsigned pin, int bias)
> +{
> +	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +	const struct sunxi_pinctrl_desc *data =
> +		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +	u32 val, mask;
> +
> +	if (bias < 0)
> +		return;
> +
> +	pin -= data->pin_base;
> +	mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
> +	val = bias << sunxi_pull_offset(pin);
> +	clrsetbits_le32(priv->base + sunxi_pull_reg(pin), mask, val);
> +}
> +
> +static int sunxi_pctrl_set_state(struct udevice *dev, struct udevice *config)
> +{
> +	const char *pins;
> +	const char *function;
> +	int flen;
> +	int len, curr_len;
> +	int drive, bias;
> +	int muxval;
> +
> +	debug("%s: %s %s\n", __func__, dev->name, config->name);
> +
> +	pins = fdt_getprop(gd->fdt_blob, config->of_offset,
> +			   "allwinner,pins", &len);
> +	if (!pins) {
> +		debug("%s: missing allwinner,pins property in node %s\n",
> +		      dev->name, config->name);
> +		return -EINVAL;
> +	}
> +
> +	function = fdt_getprop(gd->fdt_blob, config->of_offset,
> +			       "allwinner,function", &flen);
> +	if (!function) {
> +		debug("%s: missing allwinner,function property in node %s\n",
> +		      dev->name, config->name);
> +		return -EINVAL;
> +	}
> +
> +	drive = sunxi_pctrl_parse_drive_prop(gd->fdt_blob, config->of_offset);
> +	bias = sunxi_pctrl_parse_bias_prop(gd->fdt_blob, config->of_offset);
> +
> +	debug("%s: function %s, drive %d, bias %d\n",
> +	      config->name, function, drive, bias);
> +
> +	/* Iterate through the pins and configure each */
> +	while (len && (curr_len = strnlen(pins, len))) {
> +		const struct sunxi_desc_pin *pin;
> +
> +		if (curr_len == len) {
> +			error("%s: unterminated string?", __func__);
> +			break;
> +		}
> +
> +		pin = sunxi_pctrl_pin_by_name(dev, pins);
> +		if (pin) {
> +			muxval = sunxi_pctrl_muxval_by_name(pin, function);
> +
> +			sunxi_pctrl_set_function(dev, pin->pin.number, muxval);
> +			sunxi_pctrl_set_dlevel(dev, pin->pin.number, drive);
> +			sunxi_pctrl_set_bias(dev, pin->pin.number, bias);
> +		} else {
> +			debug("%s: could not find pin %s\n", dev->name, pins);
> +		}
> +
> +		/* advance */
> +		pins += (curr_len + 1);
> +		len -= (curr_len + 1);
> +	}
> +
> +	return 0;
> +}
> +
> +static inline void soc_data_from_desc(const struct sunxi_pinctrl_desc *data,
> +				      struct sunxi_gpio_soc_data *soc_data)
> +{
> +	int i;
> +	unsigned pinnum;
> +	unsigned low = data->pin_base / PINS_PER_BANK;
> +	unsigned high = data->pin_base;
> +
> +	for (i = 0; i < data->npins; ++i) {
> +		pinnum = data->pins[i].pin.number;
> +		high = max(high, pinnum);
> +	}
> +
> +	/* convert pin-numbers to bank numbers */
> +	high /= PINS_PER_BANK;
> +
> +	soc_data->start = low;
> +	soc_data->no_banks = high - low + 1;
> +}
> +
> +static int sunxi_pctrl_bind_gpio(struct udevice *dev)
> +{
> +#if defined(CONFIG_DM_GPIO)
> +	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +	const struct sunxi_pinctrl_desc *data =
> +		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +	struct driver *gpio_driver;
> +	char name[20];
> +	int ret;
> +
> +	/* Fill the soc_data for the gpio driver from the pinctrl_desc */
> +	soc_data_from_desc(data, &priv->gpio_soc_data);
> +
> +	gpio_driver = lists_driver_lookup_name("gpio_sunxi");
> +	if (!gpio_driver)
> +		return -ENOENT;
> +
> +	ret = device_bind_with_driver_data(dev, gpio_driver, "sunxi_gpio",
> +					   (ulong)&priv->gpio_soc_data,
> +					   dev->of_offset, &priv->gpio_dev);
> +
> +	if (ret < 0)
> +		return ret;
> +
> +	snprintf(name, sizeof(name), "sunxi_gpio@%x", (uint32_t)priv->base);
> +	device_set_name(priv->gpio_dev, name);
> +#endif
> +
> +	return 0;
> +}
> +
> +static int sunxi_pctrl_probe(struct udevice *dev)
> +{
> +	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +	fdt_addr_t addr_base;
> +	fdt_size_t size;
> +
> +	addr_base = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
> +						       dev->of_offset,
> +						       "reg", 0, &size,
> +						       false);
> +	if (addr_base == FDT_ADDR_T_NONE)
> +		return -EINVAL;
> +
> +	priv->base = (void *)addr_base;
> +
> +	return sunxi_pctrl_bind_gpio(dev);
> +}
> +
> +static struct pinctrl_ops sunxi_pctrl_ops = {
> +	.set_state	  = sunxi_pctrl_set_state,
> +};
> +
> +#if defined(CONFIG_MACH_SUN50I)
> +extern const struct sunxi_pinctrl_desc a64_pinctrl_data;
> +extern const struct sunxi_pinctrl_desc a64_r_pinctrl_data;
> +#endif
> +
> +static const struct udevice_id sunxi_pctrl_ids[] = {
> +#if defined(CONFIG_MACH_SUN50I)
> +	{ .compatible = "allwinner,sun50i-a64-pinctrl",
> +	  .data = (ulong)&a64_pinctrl_data },
> +	{ .compatible = "allwinner,sun50i-a64-r-pinctrl",
> +	  .data = (ulong)&a64_r_pinctrl_data },
> +#endif
> +	{ }
> +};
> +
> +U_BOOT_DRIVER(pinctrl_sunxi) = {
> +	.name		= "sunxi_pctrl",
> +	.id		= UCLASS_PINCTRL,
> +	.of_match	= sunxi_pctrl_ids,
> +	.priv_auto_alloc_size = sizeof(struct sunxi_pctrl_priv),
> +	.ops		= &sunxi_pctrl_ops,
> +	.bind		= dm_scan_fdt_dev,
> +	.probe		= sunxi_pctrl_probe,
> +};
> +
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> new file mode 100644
> index 0000000..8508626
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> @@ -0,0 +1,311 @@
> +/*
> + * Allwinner A1X SoCs pinctrl driver.
> + *
> + * 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.
> + */
> +
> +#ifndef __PINCTRL_SUNXI_H
> +#define __PINCTRL_SUNXI_H
> +
> +#define PA_BASE	0
> +#define PB_BASE	32
> +#define PC_BASE	64
> +#define PD_BASE	96
> +#define PE_BASE	128
> +#define PF_BASE	160
> +#define PG_BASE	192
> +#define PH_BASE	224
> +#define PI_BASE	256
> +#define PL_BASE	352
> +#define PM_BASE	384
> +#define PN_BASE	416
> +
> +#ifdef __UBOOT__
> +/* Convenience macro to define a single named or anonymous pin descriptor */
> +#define PINCTRL_PIN(a, b) { .number = a, .name = b }
> +
> +/**
> + * struct pinctrl_pin_desc - boards/machines provide information on their
> + * pins, pads or other muxable units in this struct
> + * @number: unique pin number from the global pin number space
> + * @name: a name for this pin
> + * @drv_data: driver-defined per-pin data. pinctrl core does not touch this
> + */
> +struct pinctrl_pin_desc {
> +	unsigned number;
> +	const char *name;
> +#ifndef __UBOOT__
> +	void *drv_data;
> +#endif
> +};
> +#endif
> +
> +#define SUNXI_PINCTRL_PIN(bank, pin)		\
> +	PINCTRL_PIN(P ## bank ## _BASE + (pin), "P" #bank #pin)
> +
> +#define SUNXI_PIN_NAME_MAX_LEN	5
> +
> +#define BANK_MEM_SIZE		0x24
> +#define MUX_REGS_OFFSET		0x0
> +#define DATA_REGS_OFFSET	0x10
> +#define DLEVEL_REGS_OFFSET	0x14
> +#define PULL_REGS_OFFSET	0x1c
> +
> +#define PINS_PER_BANK		32
> +#define MUX_PINS_PER_REG	8
> +#define MUX_PINS_BITS		4
> +#define MUX_PINS_MASK		0x0f
> +#define DATA_PINS_PER_REG	32
> +#define DATA_PINS_BITS		1
> +#define DATA_PINS_MASK		0x01
> +#define DLEVEL_PINS_PER_REG	16
> +#define DLEVEL_PINS_BITS	2
> +#define DLEVEL_PINS_MASK	0x03
> +#define PULL_PINS_PER_REG	16
> +#define PULL_PINS_BITS		2
> +#define PULL_PINS_MASK		0x03
> +
> +#define IRQ_PER_BANK		32
> +
> +#define IRQ_CFG_REG		0x200
> +#define IRQ_CFG_IRQ_PER_REG		8
> +#define IRQ_CFG_IRQ_BITS		4
> +#define IRQ_CFG_IRQ_MASK		((1 << IRQ_CFG_IRQ_BITS) - 1)
> +#define IRQ_CTRL_REG		0x210
> +#define IRQ_CTRL_IRQ_PER_REG		32
> +#define IRQ_CTRL_IRQ_BITS		1
> +#define IRQ_CTRL_IRQ_MASK		((1 << IRQ_CTRL_IRQ_BITS) - 1)
> +#define IRQ_STATUS_REG		0x214
> +#define IRQ_STATUS_IRQ_PER_REG		32
> +#define IRQ_STATUS_IRQ_BITS		1
> +#define IRQ_STATUS_IRQ_MASK		((1 << IRQ_STATUS_IRQ_BITS) - 1)
> +
> +#define IRQ_MEM_SIZE		0x20
> +
> +#define IRQ_EDGE_RISING		0x00
> +#define IRQ_EDGE_FALLING	0x01
> +#define IRQ_LEVEL_HIGH		0x02
> +#define IRQ_LEVEL_LOW		0x03
> +#define IRQ_EDGE_BOTH		0x04
> +
> +#define SUN4I_FUNC_INPUT	0
> +#define SUN4I_FUNC_IRQ		6
> +
> +struct sunxi_desc_function {
> +	const char	*name;
> +	u8		muxval;
> +	u8		irqbank;
> +	u8		irqnum;
> +};
> +
> +struct sunxi_desc_pin {
> +	struct pinctrl_pin_desc		pin;
> +	struct sunxi_desc_function	*functions;
> +};
> +
> +struct sunxi_pinctrl_desc {
> +	const struct sunxi_desc_pin	*pins;
> +	int				npins;
> +	unsigned			pin_base;
> +	unsigned			irq_banks;
> +	unsigned			irq_bank_base;
> +	bool				irq_read_needs_mux;
> +};
> +
> +struct sunxi_pinctrl_function {
> +	const char	*name;
> +	const char	**groups;
> +	unsigned	ngroups;
> +};
> +
> +struct sunxi_pinctrl_group {
> +	const char	*name;
> +	unsigned long	config;
> +	unsigned	pin;
> +};
> +
> +#ifndef __UBOOT__
> +struct sunxi_pinctrl {
> +	void __iomem			*membase;
> +	struct gpio_chip		*chip;
> +	const struct sunxi_pinctrl_desc	*desc;
> +	struct device			*dev;
> +	struct irq_domain		*domain;
> +	struct sunxi_pinctrl_function	*functions;
> +	unsigned			nfunctions;
> +	struct sunxi_pinctrl_group	*groups;
> +	unsigned			ngroups;
> +	int				*irq;
> +	unsigned			*irq_array;
> +	spinlock_t			lock;
> +	struct pinctrl_dev		*pctl_dev;
> +};
> +#endif
> +
> +#define SUNXI_PIN(_pin, ...)					\
> +	{							\
> +		.pin = _pin,					\
> +		.functions = (struct sunxi_desc_function[]){	\
> +			__VA_ARGS__, { } },			\
> +	}
> +
> +#define SUNXI_FUNCTION(_val, _name)				\
> +	{							\
> +		.name = _name,					\
> +		.muxval = _val,					\
> +	}
> +
> +#define SUNXI_FUNCTION_IRQ(_val, _irq)				\
> +	{							\
> +		.name = "irq",					\
> +		.muxval = _val,					\
> +		.irqnum = _irq,					\
> +	}
> +
> +#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)		\
> +	{							\
> +		.name = "irq",					\
> +		.muxval = _val,					\
> +		.irqbank = _bank,				\
> +		.irqnum = _irq,					\
> +	}
> +
> +/*
> + * The sunXi PIO registers are organized as is:
> + * 0x00 - 0x0c	Muxing values.
> + *		8 pins per register, each pin having a 4bits value
> + * 0x10		Pin values
> + *		32 bits per register, each pin corresponding to one bit
> + * 0x14 - 0x18	Drive level
> + *		16 pins per register, each pin having a 2bits value
> + * 0x1c - 0x20	Pull-Up values
> + *		16 pins per register, each pin having a 2bits value
> + *
> + * This is for the first bank. Each bank will have the same layout,
> + * with an offset being a multiple of 0x24.
> + *
> + * The following functions calculate from the pin number the register
> + * and the bit offset that we should access.
> + */
> +static inline u32 sunxi_mux_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += MUX_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_mux_offset(u16 pin)
> +{
> +	u32 pin_num = pin % MUX_PINS_PER_REG;
> +	return pin_num * MUX_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_data_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += DATA_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_data_offset(u16 pin)
> +{
> +	u32 pin_num = pin % DATA_PINS_PER_REG;
> +	return pin_num * DATA_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_dlevel_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += DLEVEL_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_dlevel_offset(u16 pin)
> +{
> +	u32 pin_num = pin % DLEVEL_PINS_PER_REG;
> +	return pin_num * DLEVEL_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_pull_reg(u16 pin)
> +{
> +	u8 bank = pin / PINS_PER_BANK;
> +	u32 offset = bank * BANK_MEM_SIZE;
> +	offset += PULL_REGS_OFFSET;
> +	offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
> +	return round_down(offset, 4);
> +}
> +
> +static inline u32 sunxi_pull_offset(u16 pin)
> +{
> +	u32 pin_num = pin % PULL_PINS_PER_REG;
> +	return pin_num * PULL_PINS_BITS;
> +}
> +
> +static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base)
> +{
> +	u8 bank = irq / IRQ_PER_BANK;
> +	u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04;
> +
> +	return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg;
> +}
> +
> +static inline u32 sunxi_irq_cfg_offset(u16 irq)
> +{
> +	u32 irq_num = irq % IRQ_CFG_IRQ_PER_REG;
> +	return irq_num * IRQ_CFG_IRQ_BITS;
> +}
> +
> +static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base)
> +{
> +	return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE;
> +}
> +
> +static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base)
> +{
> +	u8 bank = irq / IRQ_PER_BANK;
> +
> +	return sunxi_irq_ctrl_reg_from_bank(bank, bank_base);
> +}
> +
> +static inline u32 sunxi_irq_ctrl_offset(u16 irq)
> +{
> +	u32 irq_num = irq % IRQ_CTRL_IRQ_PER_REG;
> +	return irq_num * IRQ_CTRL_IRQ_BITS;
> +}
> +
> +static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
> +{
> +	return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
> +}
> +
> +static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base)
> +{
> +	u8 bank = irq / IRQ_PER_BANK;
> +
> +	return sunxi_irq_status_reg_from_bank(bank, bank_base);
> +}
> +
> +static inline u32 sunxi_irq_status_offset(u16 irq)
> +{
> +	u32 irq_num = irq % IRQ_STATUS_IRQ_PER_REG;
> +	return irq_num * IRQ_STATUS_IRQ_BITS;
> +}
> +
> +#ifndef __UBOOT__
> +int sunxi_pinctrl_init(struct platform_device *pdev,
> +		       const struct sunxi_pinctrl_desc *desc);
> +#endif
> +
> +#endif /* __PINCTRL_SUNXI_H */
> -- 
> 1.9.1
> 

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170222/1b5a1029/attachment.sig>

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

* [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS Philipp Tomsich
@ 2017-02-22 23:19   ` Maxime Ripard
  0 siblings, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2017-02-22 23:19 UTC (permalink / raw)
  To: u-boot

On Wed, Feb 22, 2017 at 09:47:30PM +0100, Philipp Tomsich wrote:
> Now that we have the new functionality to allow multiple drivers to
> bind for a single node, we want to enable it for the A64-uQ7.
> 
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  configs/lynx_defconfig | 1 +
>  1 file changed, 1 insertion(+)
> 
> diff --git a/configs/lynx_defconfig b/configs/lynx_defconfig
> index 7b773f1..0fba279 100644
> --- a/configs/lynx_defconfig
> +++ b/configs/lynx_defconfig
> @@ -2,6 +2,7 @@ CONFIG_ARM=y
>  CONFIG_RESERVE_ALLWINNER_BOOT0_HEADER=y
>  CONFIG_ARCH_SUNXI=y
>  CONFIG_MACH_SUN50I=y
> +CONFIG_DM_ALLOW_MULTIPLE_DRIVERS=y

I guess it would be better to just put the default value in Kconfig as
MACH_SUN50I or even ARCH_SUNXI to enable it for all the boards

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170222/120eafaa/attachment.sig>

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

* [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
@ 2017-02-22 23:20   ` Maxime Ripard
  2017-02-22 23:24     ` Dr. Philipp Tomsich
  0 siblings, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2017-02-22 23:20 UTC (permalink / raw)
  To: u-boot

On Wed, Feb 22, 2017 at 09:47:32PM +0100, Philipp Tomsich wrote:
> +<<<<<<< HEAD
>  				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
>  				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
> +=======
> +				drive-strength = < 30 >;
> +				bias-pull-up;
>  			};
>  
> -			i2c0_pins: i2c0_pins {
> +			mmc2_8bit_pins: mmc2-8bit {
> +				allwinner,pins = "PC5", "PC6", "PC8",
> +						 "PC9", "PC10", "PC11",
> +						 "PC12", "PC13", "PC14",
> +						 "PC15", "PC16";
> +				allwinner,function = "mmc2";
> +				drive-strength = < 30 >;
> +				bias-pull-up;
> +>>>>>>> 20221b3... [f] dts warnings

Hmmm, are you sure about those ? :)

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170222/18160e36/attachment.sig>

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

* [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings
  2017-02-22 23:20   ` Maxime Ripard
@ 2017-02-22 23:24     ` Dr. Philipp Tomsich
  0 siblings, 0 replies; 19+ messages in thread
From: Dr. Philipp Tomsich @ 2017-02-22 23:24 UTC (permalink / raw)
  To: u-boot


> On 23 Feb 2017, at 00:20, Maxime Ripard <maxime.ripard@free-electrons.com> wrote:
> 
> On Wed, Feb 22, 2017 at 09:47:32PM +0100, Philipp Tomsich wrote:
>> +<<<<<<< HEAD
>> 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
>> 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> +=======
>> +				drive-strength = < 30 >;
>> +				bias-pull-up;
>> 			};
>> 
>> -			i2c0_pins: i2c0_pins {
>> +			mmc2_8bit_pins: mmc2-8bit {
>> +				allwinner,pins = "PC5", "PC6", "PC8",
>> +						 "PC9", "PC10", "PC11",
>> +						 "PC12", "PC13", "PC14",
>> +						 "PC15", "PC16";
>> +				allwinner,function = "mmc2";
>> +				drive-strength = < 30 >;
>> +				bias-pull-up;
>> +>>>>>>> 20221b3... [f] dts warnings
> 
> Hmmm, are you sure about those ? :)

I can?t believe this slipped through. I think I need more sleep.
At least the patch-format wasn?t such a mess this time around?

Cheers,
Philipp.

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

* [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi
  2017-02-22 23:18   ` Maxime Ripard
@ 2017-02-23  3:54     ` Chen-Yu Tsai
  2017-02-23 18:10       ` Maxime Ripard
  0 siblings, 1 reply; 19+ messages in thread
From: Chen-Yu Tsai @ 2017-02-23  3:54 UTC (permalink / raw)
  To: u-boot

On Thu, Feb 23, 2017 at 7:18 AM, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Wed, Feb 22, 2017 at 09:47:27PM +0100, Philipp Tomsich wrote:
>> This change adds a full device-model pinctrl driver for sunxi (tested with
>> sun50iw1p1) based on the support available in Linux.
>>
>> Details are:
>>  * implements a driver for pinctrl devices and assigns sun50i-a64-pinctrl
>>    and sun50i-a64-r-pinctrl to it
>>  * dynamically creates the driver_data for a sunxi_gpio (see sunxi_gpio.c)
>>    driver and binds it to the same device-tree node
>>  * lifts and reuses the pinctrl-sunxi.h and pinctrl-sun50i-a64.c files from
>>    Linux (thanks to Maxime and Andre) and adds a pinctrl-sun50i-a64-r.c (to
>>    be picked up for inclusion into Linux again)
>>
>> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
>> ---
>>  arch/arm/include/asm/arch-sunxi/gpio-internal.h    |  19 +
>>  .../pinctrl/allwinner,pinctrl.txt                  |  65 +++
>>  drivers/gpio/sunxi_gpio.c                          |  15 +-
>>  drivers/pinctrl/Kconfig                            |  10 +
>>  drivers/pinctrl/Makefile                           |   2 +
>>  drivers/pinctrl/sunxi/Makefile                     |  10 +
>>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c       |  92 ++++
>>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c         | 577 +++++++++++++++++++++
>>  drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 317 +++++++++++
>>  drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
>>  10 files changed, 1411 insertions(+), 7 deletions(-)
>>  create mode 100644 arch/arm/include/asm/arch-sunxi/gpio-internal.h
>>  create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
>>  create mode 100644 drivers/pinctrl/sunxi/Makefile
>>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
>>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
>>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
>>
>> diff --git a/arch/arm/include/asm/arch-sunxi/gpio-internal.h b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
>> new file mode 100644
>> index 0000000..4dcdd34
>> --- /dev/null
>> +++ b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
>> @@ -0,0 +1,19 @@
>> +/*
>> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
>> + *
>> + * SPDX-License-Identifier:  GPL-2.0+
>> + */
>> +
>> +
>> +#ifndef _SUNXI_GPIO_INTERNAL_H
>> +#define _SUNXI_GPIO_INTERNAL_H
>> +
>> +/* This data structure is shared between the sunxi_gpio driver and
>> + * the sunxi_pinctrl driver.
>> + */
>> +struct sunxi_gpio_soc_data {
>> +     int start;
>> +     int no_banks;
>> +};
>> +
>> +#endif
>> diff --git a/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
>> new file mode 100644
>> index 0000000..946831f
>> --- /dev/null
>> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
>> @@ -0,0 +1,65 @@
>> +* Allwinner Pinmux Controller
>> +
>> +Allwinner integrates multiple banks (of 32 pins each) of pin-muxing,
>> +GPIO functionality and (optional) external interrupt functionality
>> +into a single controller.
>> +
>> +For each configurable pad (certain driver-cells, such as the IO from
>> +integrated USB PHYs or DRAM, have a fixed function and can not be
>> +configured), the muxing options (input, output or one of the several
>> +functions) can be selected.
>> +
>> +Properties for the pinctrl node:
>> + - compatible: should be "allwinner,sun50i-pinctrl"
>
> There's a typo here, the compatible is sun50i-a64-pinctrl
>
>> + - reg: address and length of the register set for the device.
>> + - interrupts: interrupt for the device
>> + - clocks: A phandle to the reference clock for this device
>
> (and ideally, this would take three clocks: the bus gate + the two
> oscillators).
>
>> +
>> +Properties for the pinconfig sub-nodes:
>> + - allwinner,pins: a list of pins (e.g. "PH2", "PH3") to configure
>> + - allwinner,function: the name of pinmux function (e.g. "mmc2")
>
> allwinner,pins and allwinner,function are also deprecated in favour of
> pins and function.
>
>> + - drive-strength: a drive-stength setting of 10, 20, 30 or 40 mA
>> + - bias-pull-up
>> + - bias-pull-down
>> + - bias-disable (default)
>
> The default is not bias-disable, but to keep the current configuration

Shouldn't we just copy the binding docs from the Linux kernel?
Afterall they are supposed to be the same.

ChenYu

>> +
>> +Deprecated properties for the pinconfig sub-nodes:
>> + - allwinner,drive: one of <SUN4I_PINCTRL_10_MA>, <SUN4I_PINCTRL_20_MA>,
>> +                    <SUN4I_PINCTRL_30_MA> or <SUN4I_PINCTRL_40_MA>
>> + - allwinner,pull: one of <SUN4I_PINCTRL_NO_PULL>, <SUN4I_PINCTRL_PULL_UP>
>> +                or <SUN4I_PINCTRL_PULL_DOWN>
>> +
>> +Example:
>> +
>> +     pio: pinctrl at 1c20800 {
>> +             compatible = "allwinner,sun50i-a64-pinctrl";
>> +             reg = <0x01c20800 0x400>;
>> +
>> +             interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
>> +                          <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
>> +                          <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
>> +             clocks = <&bus_gates 69>;
>> +
>> +             gpio-controller;
>> +             #gpio-cells = <3>;
>> +
>> +             interrupt-controller;
>> +             #interrupt-cells = <2>;
>> +
>> +             #address-cells = <1>;
>> +             #size-cells = <1>;
>> +
>> +             uart0_pins_a: uart0_pins_a {
>> +                     allwinner,pins = "PB8", "PB9";
>> +                     allwinner,function = "uart0";
>> +                     allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> +                     allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> +             };
>> +
>> +             uart0_pins_b: uart0_pins_b {
>> +                     allwinner,pins = "PF2", "PF3";
>> +                     allwinner,function = "uart0";
>> +                     allwinner,drive = <SUN4I_PINCTRL_10_MA>;
>> +                     allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
>> +             };
>> +     };
>> diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
>> index 2b7bc7f..fd0c1ac 100644
>> --- a/drivers/gpio/sunxi_gpio.c
>> +++ b/drivers/gpio/sunxi_gpio.c
>> @@ -16,6 +16,7 @@
>>  #include <fdtdec.h>
>>  #include <malloc.h>
>>  #include <asm/arch/gpio.h>
>> +#include <asm/arch/gpio-internal.h>
>>  #include <asm/io.h>
>>  #include <asm/gpio.h>
>>  #include <dm/device-internal.h>
>> @@ -275,11 +276,6 @@ static int gpio_sunxi_probe(struct udevice *dev)
>>       return 0;
>>  }
>>
>> -struct sunxi_gpio_soc_data {
>> -     int start;
>> -     int no_banks;
>> -};
>> -
>>  /**
>>   * We have a top-level GPIO device with no actual GPIOs. It has a child
>>   * device for each Sunxi bank.
>> @@ -353,18 +349,22 @@ static const struct udevice_id sunxi_gpio_ids[] = {
>>       ID("allwinner,sun8i-a83t-pinctrl",      a_all),
>>       ID("allwinner,sun8i-h3-pinctrl",        a_all),
>>       ID("allwinner,sun9i-a80-pinctrl",       a_all),
>> +#if !defined(CONFIG_SUNXI_PINCTRL)
>>       /* This is not strictly correct for the A64, as it is missing
>>        * bank 'A'. Yet, the register layout in the pinctrl block is
>>        * backward compatible and any accesses to the registers that
>>        * normally control bank 'A' will have no adverse effect.
>>        */
>> -     ID("allwinner,sun50i-a64-pinctrl",      a_all),
>> +     ID("allwinner,sun50i-a64-pinctrl",      a_all),
>> +#endif
>>       ID("allwinner,sun6i-a31-r-pinctrl",     l_2),
>>       ID("allwinner,sun8i-a23-r-pinctrl",     l_1),
>>       ID("allwinner,sun8i-a83t-r-pinctrl",    l_1),
>>       ID("allwinner,sun8i-h3-r-pinctrl",      l_1),
>>       ID("allwinner,sun9i-a80-r-pinctrl",     l_3),
>> -     ID("allwinner,sun50i-a64-r-pinctrl",    l_1),
>> +#if !defined(CONFIG_SUNXI_PINCTRL)
>> +     ID("allwinner,sun50i-a64-r-pinctrl",    l_1),
>> +#endif
>>       { }
>>  };
>>
>> @@ -376,4 +376,5 @@ U_BOOT_DRIVER(gpio_sunxi) = {
>>       .bind   = gpio_sunxi_bind,
>>       .probe  = gpio_sunxi_probe,
>>  };
>> +
>>  #endif
>> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
>> index efcb4c0..064a682 100644
>> --- a/drivers/pinctrl/Kconfig
>> +++ b/drivers/pinctrl/Kconfig
>> @@ -175,6 +175,16 @@ config PIC32_PINCTRL
>>         by a device tree node which contains both GPIO defintion and pin control
>>         functions.
>>
>> +config SUNXI_PINCTRL
>> +        bool "Allwinner Axx pin-control and pin-mux driver"
>> +     depends on DM && ARCH_SUNXI
>> +     default y
>> +     help
>> +       Supports pin multiplexing control, drive-strength and bias control on
>> +       Allwinner Axx SoCs. The driver is controlled by a device tree node which
>> +       contains both the GPIO definitions and the pin control functions for
>> +       each multiplex function.
>> +
>>  endif
>>
>>  source "drivers/pinctrl/meson/Kconfig"
>> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
>> index 512112a..da27a91 100644
>> --- a/drivers/pinctrl/Makefile
>> +++ b/drivers/pinctrl/Makefile
>> @@ -16,3 +16,5 @@ obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o
>>  obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/
>>  obj-$(CONFIG_PINCTRL_MESON)  += meson/
>>  obj-$(CONFIG_PINCTRL_MVEBU)  += mvebu/
>> +
>> +obj-$(CONFIG_ARCH_SUNXI)        += sunxi/
>> \ No newline at end of file
>> diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
>> new file mode 100644
>> index 0000000..11549ec
>> --- /dev/null
>> +++ b/drivers/pinctrl/sunxi/Makefile
>> @@ -0,0 +1,10 @@
>> +#
>> +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
>> +#
>> +# SPDX-License-Identifier:   GPL-2.0+
>> +#
>> +
>> +obj-$(CONFIG_SUNXI_PINCTRL) += pinctrl-sunxi.o
>> +ifdef CONFIG_SUNXI_PINCTRL
>> +obj-$(CONFIG_MACH_SUN50I)   += pinctrl-sun50i-a64.o pinctrl-sun50i-a64-r.o
>> +endif
>> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
>> new file mode 100644
>> index 0000000..864d1ec
>> --- /dev/null
>> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
>> @@ -0,0 +1,92 @@
>> +/*
>> + * Allwinner A64 SoCs pinctrl driver.
>> + *
>> + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH.
>> + *
>> + * Based on pinctrl-sun7i-a20.c, which is:
>> + * Copyright (C) 2014 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 <common.h>
>> +
>> +#include "pinctrl-sunxi.h"
>> +
>> +static const struct sunxi_desc_pin a64_r_pins[] = {
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_rsb"),         /* SCK */
>> +               SUNXI_FUNCTION(0x3, "s_i2c"),         /* SCK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),  /* EINT0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_rsb"),         /* SDA */
>> +               SUNXI_FUNCTION(0x3, "s_i2c"),         /* SDA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),  /* EINT1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_uart"),        /* TX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),  /* EINT2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_uart"),        /* RX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),  /* EINT3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_jtag"),        /* MS */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),  /* EINT4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_jtag"),        /* CK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),  /* EINT5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_jtag"),        /* DO */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),  /* EINT6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_jtag"),        /* DI */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),  /* EINT7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_i2c"),         /* SCK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),  /* EINT8 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_i2c"),         /* SDA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),  /* EINT9 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_pwm"),
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 10)), /* EINT10 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "s_cir"),
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 11)), /* EINT11 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(L, 12),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 12)), /* EINT12 */
>> +};
>> +
>> +const struct sunxi_pinctrl_desc a64_r_pinctrl_data = {
>> +     .pins = a64_r_pins,
>> +     .npins = ARRAY_SIZE(a64_r_pins),
>> +     .pin_base = PL_BASE,
>> +     .irq_banks = 1,
>> +};
>> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>> new file mode 100644
>> index 0000000..7abea03
>> --- /dev/null
>> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
>> @@ -0,0 +1,577 @@
>> +/*
>> + * Allwinner A64 SoCs pinctrl driver.
>> + *
>> + * Copyright (C) 2016 - ARM Ltd.
>> + * Author: Andre Przywara <andre.przywara@arm.com>
>> + *
>> + * Based on pinctrl-sun7i-a20.c, which is:
>> + * Copyright (C) 2014 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 <common.h>
>> +
>> +#include "pinctrl-sunxi.h"
>> +
>> +static const struct sunxi_desc_pin a64_pins[] = {
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart2"),         /* TX */
>> +               SUNXI_FUNCTION(0x4, "jtag"),          /* MS0 */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 0)),  /* EINT0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart2"),         /* RX */
>> +               SUNXI_FUNCTION(0x4, "jtag"),          /* CK0 */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* VCCEN */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 1)),  /* EINT1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart2"),         /* RTS */
>> +               SUNXI_FUNCTION(0x4, "jtag"),          /* DO0 */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* VPPEN */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 2)),  /* EINT2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart2"),         /* CTS */
>> +               SUNXI_FUNCTION(0x3, "i2s0"),          /* MCLK */
>> +               SUNXI_FUNCTION(0x4, "jtag"),          /* DI0 */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* VPPPP */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 3)),  /* EINT3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif2"),          /* SYNC */
>> +               SUNXI_FUNCTION(0x3, "i2s0"),          /* SYNC */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* CLK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 4)),  /* EINT4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif2"),          /* BCLK */
>> +               SUNXI_FUNCTION(0x3, "i2s0"),          /* BCLK */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* DATA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 5)),          /* EINT5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif2"),          /* DOUT */
>> +               SUNXI_FUNCTION(0x3, "i2s0"),          /* DOUT */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* RST */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 6)),  /* EINT6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif2"),          /* DIN */
>> +               SUNXI_FUNCTION(0x3, "i2s0"),          /* DIN */
>> +               SUNXI_FUNCTION(0x5, "sim"),           /* DET */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 7)),  /* EINT7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x4, "uart0"),         /* TX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 8)),  /* EINT8 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(B, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x4, "uart0"),         /* RX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 0, 9)),  /* EINT9 */
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NWE */
>> +               SUNXI_FUNCTION(0x4, "spi0")),         /* MOSI */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NALE */
>> +               SUNXI_FUNCTION(0x3, "mmc2"),          /* DS */
>> +               SUNXI_FUNCTION(0x4, "spi0")),         /* MISO */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NCLE */
>> +               SUNXI_FUNCTION(0x4, "spi0")),         /* SCK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NCE1 */
>> +               SUNXI_FUNCTION(0x4, "spi0")),         /* CS */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0")),        /* NCE0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NRE# */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* CLK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NRB0 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* CMD */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0")),        /* NRB1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ0 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ1 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ2 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ3 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 12),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ4 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 13),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ5 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 14),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ6 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 15),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQ7 */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* D7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(C, 16),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "nand0"),         /* NDQS */
>> +               SUNXI_FUNCTION(0x3, "mmc2")),         /* RST */
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D2 */
>> +               SUNXI_FUNCTION(0x3, "uart3"),         /* TX */
>> +               SUNXI_FUNCTION(0x4, "spi1"),          /* CS */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* CLK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D3 */
>> +               SUNXI_FUNCTION(0x3, "uart3"),         /* RX */
>> +               SUNXI_FUNCTION(0x4, "spi1"),          /* CLK */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* DE */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D4 */
>> +               SUNXI_FUNCTION(0x3, "uart4"),         /* TX */
>> +               SUNXI_FUNCTION(0x4, "spi1"),          /* MOSI */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* HSYNC */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D5 */
>> +               SUNXI_FUNCTION(0x3, "uart4"),         /* RX */
>> +               SUNXI_FUNCTION(0x4, "spi1"),          /* MISO */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* VSYNC */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D6 */
>> +               SUNXI_FUNCTION(0x3, "uart4"),         /* RTS */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D7 */
>> +               SUNXI_FUNCTION(0x3, "uart4"),         /* CTS */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D10 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D11 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D12 */
>> +               SUNXI_FUNCTION(0x4, "emac"),          /* ERXD3 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D13 */
>> +               SUNXI_FUNCTION(0x4, "emac"),          /* ERXD2 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D14 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ERXD1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D15 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ERXD0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 12),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D18 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VP0 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ERXCK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 13),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D19 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VN0 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ERXCTL */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 14),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D20 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VP1 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ENULL */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 15),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D21 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VN1 */
>> +               SUNXI_FUNCTION(0x4, "emac"),          /* ETXD3 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 16),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D22 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VP2 */
>> +               SUNXI_FUNCTION(0x4, "emac"),          /* ETXD2 */
>> +               SUNXI_FUNCTION(0x5, "ccir")),         /* D7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 17),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* D23 */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VN2 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ETXD1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 18),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* CLK */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VPC */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ETXD0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 19),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* DE */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VNC */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ETXCK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 20),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* HSYNC */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VP3 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ETXCTL */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 21),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "lcd0"),          /* VSYNC */
>> +               SUNXI_FUNCTION(0x3, "lvds0"),         /* VN3 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* ECLKIN */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 22),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "pwm"),           /* PWM0 */
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* EMDC */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 23),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x4, "emac")),         /* EMDIO */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(D, 24),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out")),
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* PCK */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* CLK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* CK */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* ERR */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* HSYNC */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* SYNC */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* VSYNC */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* DVLD */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D0 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D1 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D2 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D3 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D4 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D5 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D6 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0"),          /* D7 */
>> +               SUNXI_FUNCTION(0x4, "ts0")),          /* D7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0")),         /* SCK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "csi0")),         /* SDA */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 14),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "pll"),           /* LOCK_DBG */
>> +               SUNXI_FUNCTION(0x3, "i2c2")),         /* SCK */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 15),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x3, "i2c2")),         /* SDA */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 16),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out")),
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 17),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out")),
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* D1 */
>> +               SUNXI_FUNCTION(0x3, "jtag")),         /* MSI */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* D0 */
>> +               SUNXI_FUNCTION(0x3, "jtag")),         /* DI1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* CLK */
>> +               SUNXI_FUNCTION(0x3, "uart0")),        /* TX */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* CMD */
>> +               SUNXI_FUNCTION(0x3, "jtag")),         /* DO1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* D3 */
>> +               SUNXI_FUNCTION(0x4, "uart0")),        /* RX */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc0"),          /* D2 */
>> +               SUNXI_FUNCTION(0x3, "jtag")),         /* CK1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(F, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out")),
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* CLK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 0)),  /* EINT0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* CMD */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 1)),  /* EINT1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* D0 */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 2)),  /* EINT2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* D1 */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 3)),  /* EINT3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* D2 */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 4)),  /* EINT4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mmc1"),          /* D3 */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 5)),  /* EINT5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart1"),         /* TX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 6)),  /* EINT6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart1"),         /* RX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 7)),  /* EINT7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart1"),         /* RTS */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 8)),  /* EINT8 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart1"),         /* CTS */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 9)),  /* EINT9 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif3"),          /* SYNC */
>> +               SUNXI_FUNCTION(0x3, "i2s1"),          /* SYNC */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 10)), /* EINT10 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif3"),          /* BCLK */
>> +               SUNXI_FUNCTION(0x3, "i2s1"),          /* BCLK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 11)), /* EINT11 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 12),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif3"),          /* DOUT */
>> +               SUNXI_FUNCTION(0x3, "i2s1"),          /* DOUT */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 12)), /* EINT12 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(G, 13),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "aif3"),          /* DIN */
>> +               SUNXI_FUNCTION(0x3, "i2s1"),          /* DIN */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 1, 13)), /* EINT13 */
>> +     /* Hole */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 0),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "i2c0"),          /* SCK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 0)),  /* EINT0 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 1),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "i2c0"),          /* SDA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 1)),  /* EINT1 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 2),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "i2c1"),          /* SCK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 2)),  /* EINT2 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 3),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "i2c1"),          /* SDA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 3)),  /* EINT3 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 4),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart3"),         /* TX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 4)),  /* EINT4 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 5),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart3"),         /* RX */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 5)),  /* EINT5 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 6),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart3"),         /* RTS */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 6)),  /* EINT6 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 7),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "uart3"),         /* CTS */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 7)),  /* EINT7 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 8),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "spdif"),         /* OUT */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 8)),  /* EINT8 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 9),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 9)),  /* EINT9 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 10),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mic"),           /* CLK */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 10)), /* EINT10 */
>> +     SUNXI_PIN(SUNXI_PINCTRL_PIN(H, 11),
>> +               SUNXI_FUNCTION(0x0, "gpio_in"),
>> +               SUNXI_FUNCTION(0x1, "gpio_out"),
>> +               SUNXI_FUNCTION(0x2, "mic"),           /* DATA */
>> +               SUNXI_FUNCTION_IRQ_BANK(0x6, 2, 11)), /* EINT11 */
>> +};
>> +
>> +const struct sunxi_pinctrl_desc a64_pinctrl_data = {
>> +     .pins = a64_pins,
>> +     .npins = ARRAY_SIZE(a64_pins),
>> +     .irq_banks = 3,
>> +};
>> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
>> new file mode 100644
>> index 0000000..4640cee
>> --- /dev/null
>> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
>> @@ -0,0 +1,317 @@
>> +/*
>> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
>> + *
>> + * In parts based on linux/drivers/pinctrl/pinctrl-sunxi.c, which is
>> + *   Copyright (C) 2012 Maxime Ripard
>> + *
>> + * SPDX-License-Identifier:  GPL-2.0+
>> + */
>> +
>> +#include <common.h>
>> +#include <dm.h>
>> +#include <errno.h>
>> +#include <syscon.h>
>> +#include <asm/io.h>
>> +#include <asm/arch/clock.h>
>> +#include <asm/gpio.h>
>> +#include <asm/arch/gpio-internal.h>
>> +#include <dm/device-internal.h>
>> +#include <dm/lists.h>
>> +#include <dm/pinctrl.h>
>> +#include <dt-bindings/pinctrl/sun4i-a10.h>
>> +#include <dt-bindings/gpio/gpio.h>
>> +#include <linux/kernel.h>
>> +#include "pinctrl-sunxi.h"
>> +
>> +DECLARE_GLOBAL_DATA_PTR;
>> +
>> +struct sunxi_pctrl_priv {
>> +     void *base;
>> +#if defined(CONFIG_DM_GPIO)
>> +     struct sunxi_gpio_soc_data gpio_soc_data;
>> +     struct udevice *gpio_dev;
>> +#endif
>> +};
>> +
>> +static int sunxi_pctrl_parse_drive_prop(const void *blob, int node)
>> +{
>> +     int val;
>> +
>> +     /* Try the new style binding */
>> +     val = fdtdec_get_int(blob, node, "drive-strength", -EINVAL);
>> +     if (val >= 0) {
>> +             /* We can't go below 10mA ... */
>> +             if (val < 10)
>> +                     return -EINVAL;
>> +
>> +             /* ... and only up to 40 mA ... */
>> +             if (val > 40)
>> +                     val = 40;
>> +
>> +             /* by steps of 10 mA */
>> +             return rounddown(val, 10);
>> +     }
>> +
>> +     /* And then fall back to the old binding */
>> +     val = fdtdec_get_int(blob, node, "allwinner,drive", -EINVAL);
>> +     if (val < 0)
>> +             return -EINVAL;
>> +
>> +     return (val + 1) * 10;
>> +}
>> +
>> +static int sunxi_pctrl_parse_bias_prop(const void *blob, int node)
>> +{
>> +     /* Try the new style binding */
>> +     if (fdtdec_get_bool(blob, node, "bias-pull-up"))
>> +             return SUN4I_PINCTRL_PULL_UP;
>> +
>> +     if (fdtdec_get_bool(blob, node, "bias-pull-down"))
>> +             return SUN4I_PINCTRL_PULL_DOWN;
>> +
>> +     if (fdtdec_get_bool(blob, node, "bias-disable"))
>> +             return SUN4I_PINCTRL_NO_PULL;
>> +
>> +     /* And fall back to the old binding */
>> +     return fdtdec_get_int(blob, node, "allwinner,pull", -EINVAL);
>> +}
>> +
>> +static const struct sunxi_desc_pin *sunxi_pctrl_pin_by_name(struct udevice *dev,
>> +                                                         const char *name)
>> +{
>> +     const struct sunxi_pinctrl_desc *data =
>> +             (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
>> +     int i;
>> +
>> +     for (i = 0; i < data->npins; ++i)
>> +             if (!strcmp(data->pins[i].pin.name, name))
>> +                     return &data->pins[i];
>> +
>> +     return NULL;
>> +}
>> +
>> +static int sunxi_pctrl_muxval_by_name(const struct sunxi_desc_pin *pin,
>> +                                   const char *name)
>> +{
>> +     const struct sunxi_desc_function *func;
>> +
>> +     if (!pin)
>> +             return -EINVAL;
>> +
>> +     for (func = pin->functions; func->name; func++)
>> +             if (!strcmp(func->name, name))
>> +                     return func->muxval;
>> +
>> +     return -ENOENT;
>> +}
>> +
>> +static void sunxi_pctrl_set_function(struct udevice *dev,
>> +                                  unsigned pin, int function)
>> +{
>> +     struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
>> +     const struct sunxi_pinctrl_desc *data =
>> +             (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
>> +     u32 val, mask;
>> +
>> +     if (function < 0)
>> +             return;
>> +
>> +     pin -= data->pin_base;
>> +     mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
>> +     val = function << sunxi_mux_offset(pin);
>> +     clrsetbits_le32(priv->base + sunxi_mux_reg(pin), mask, val);
>> +}
>> +
>> +static void sunxi_pctrl_set_dlevel(struct udevice *dev,
>> +                                unsigned pin, int dlevel)
>> +{
>> +     struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
>> +     const struct sunxi_pinctrl_desc *data =
>> +             (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
>> +     u32 val, mask;
>> +
>> +     if (dlevel < 0)
>> +             return;
>> +
>> +     pin -= data->pin_base;
>> +     mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(pin);
>> +     val = dlevel << sunxi_dlevel_offset(pin);
>> +     clrsetbits_le32(priv->base + sunxi_dlevel_reg(pin), mask, val);
>> +}
>> +
>> +static void sunxi_pctrl_set_bias(struct udevice *dev,
>> +                              unsigned pin, int bias)
>> +{
>> +     struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
>> +     const struct sunxi_pinctrl_desc *data =
>> +             (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
>> +     u32 val, mask;
>> +
>> +     if (bias < 0)
>> +             return;
>> +
>> +     pin -= data->pin_base;
>> +     mask = PULL_PINS_MASK << sunxi_pull_offset(pin);
>> +     val = bias << sunxi_pull_offset(pin);
>> +     clrsetbits_le32(priv->base + sunxi_pull_reg(pin), mask, val);
>> +}
>> +
>> +static int sunxi_pctrl_set_state(struct udevice *dev, struct udevice *config)
>> +{
>> +     const char *pins;
>> +     const char *function;
>> +     int flen;
>> +     int len, curr_len;
>> +     int drive, bias;
>> +     int muxval;
>> +
>> +     debug("%s: %s %s\n", __func__, dev->name, config->name);
>> +
>> +     pins = fdt_getprop(gd->fdt_blob, config->of_offset,
>> +                        "allwinner,pins", &len);
>> +     if (!pins) {
>> +             debug("%s: missing allwinner,pins property in node %s\n",
>> +                   dev->name, config->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     function = fdt_getprop(gd->fdt_blob, config->of_offset,
>> +                            "allwinner,function", &flen);
>> +     if (!function) {
>> +             debug("%s: missing allwinner,function property in node %s\n",
>> +                   dev->name, config->name);
>> +             return -EINVAL;
>> +     }
>> +
>> +     drive = sunxi_pctrl_parse_drive_prop(gd->fdt_blob, config->of_offset);
>> +     bias = sunxi_pctrl_parse_bias_prop(gd->fdt_blob, config->of_offset);
>> +
>> +     debug("%s: function %s, drive %d, bias %d\n",
>> +           config->name, function, drive, bias);
>> +
>> +     /* Iterate through the pins and configure each */
>> +     while (len && (curr_len = strnlen(pins, len))) {
>> +             const struct sunxi_desc_pin *pin;
>> +
>> +             if (curr_len == len) {
>> +                     error("%s: unterminated string?", __func__);
>> +                     break;
>> +             }
>> +
>> +             pin = sunxi_pctrl_pin_by_name(dev, pins);
>> +             if (pin) {
>> +                     muxval = sunxi_pctrl_muxval_by_name(pin, function);
>> +
>> +                     sunxi_pctrl_set_function(dev, pin->pin.number, muxval);
>> +                     sunxi_pctrl_set_dlevel(dev, pin->pin.number, drive);
>> +                     sunxi_pctrl_set_bias(dev, pin->pin.number, bias);
>> +             } else {
>> +                     debug("%s: could not find pin %s\n", dev->name, pins);
>> +             }
>> +
>> +             /* advance */
>> +             pins += (curr_len + 1);
>> +             len -= (curr_len + 1);
>> +     }
>> +
>> +     return 0;
>> +}
>> +
>> +static inline void soc_data_from_desc(const struct sunxi_pinctrl_desc *data,
>> +                                   struct sunxi_gpio_soc_data *soc_data)
>> +{
>> +     int i;
>> +     unsigned pinnum;
>> +     unsigned low = data->pin_base / PINS_PER_BANK;
>> +     unsigned high = data->pin_base;
>> +
>> +     for (i = 0; i < data->npins; ++i) {
>> +             pinnum = data->pins[i].pin.number;
>> +             high = max(high, pinnum);
>> +     }
>> +
>> +     /* convert pin-numbers to bank numbers */
>> +     high /= PINS_PER_BANK;
>> +
>> +     soc_data->start = low;
>> +     soc_data->no_banks = high - low + 1;
>> +}
>> +
>> +static int sunxi_pctrl_bind_gpio(struct udevice *dev)
>> +{
>> +#if defined(CONFIG_DM_GPIO)
>> +     struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
>> +     const struct sunxi_pinctrl_desc *data =
>> +             (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
>> +     struct driver *gpio_driver;
>> +     char name[20];
>> +     int ret;
>> +
>> +     /* Fill the soc_data for the gpio driver from the pinctrl_desc */
>> +     soc_data_from_desc(data, &priv->gpio_soc_data);
>> +
>> +     gpio_driver = lists_driver_lookup_name("gpio_sunxi");
>> +     if (!gpio_driver)
>> +             return -ENOENT;
>> +
>> +     ret = device_bind_with_driver_data(dev, gpio_driver, "sunxi_gpio",
>> +                                        (ulong)&priv->gpio_soc_data,
>> +                                        dev->of_offset, &priv->gpio_dev);
>> +
>> +     if (ret < 0)
>> +             return ret;
>> +
>> +     snprintf(name, sizeof(name), "sunxi_gpio@%x", (uint32_t)priv->base);
>> +     device_set_name(priv->gpio_dev, name);
>> +#endif
>> +
>> +     return 0;
>> +}
>> +
>> +static int sunxi_pctrl_probe(struct udevice *dev)
>> +{
>> +     struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
>> +     fdt_addr_t addr_base;
>> +     fdt_size_t size;
>> +
>> +     addr_base = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
>> +                                                    dev->of_offset,
>> +                                                    "reg", 0, &size,
>> +                                                    false);
>> +     if (addr_base == FDT_ADDR_T_NONE)
>> +             return -EINVAL;
>> +
>> +     priv->base = (void *)addr_base;
>> +
>> +     return sunxi_pctrl_bind_gpio(dev);
>> +}
>> +
>> +static struct pinctrl_ops sunxi_pctrl_ops = {
>> +     .set_state        = sunxi_pctrl_set_state,
>> +};
>> +
>> +#if defined(CONFIG_MACH_SUN50I)
>> +extern const struct sunxi_pinctrl_desc a64_pinctrl_data;
>> +extern const struct sunxi_pinctrl_desc a64_r_pinctrl_data;
>> +#endif
>> +
>> +static const struct udevice_id sunxi_pctrl_ids[] = {
>> +#if defined(CONFIG_MACH_SUN50I)
>> +     { .compatible = "allwinner,sun50i-a64-pinctrl",
>> +       .data = (ulong)&a64_pinctrl_data },
>> +     { .compatible = "allwinner,sun50i-a64-r-pinctrl",
>> +       .data = (ulong)&a64_r_pinctrl_data },
>> +#endif
>> +     { }
>> +};
>> +
>> +U_BOOT_DRIVER(pinctrl_sunxi) = {
>> +     .name           = "sunxi_pctrl",
>> +     .id             = UCLASS_PINCTRL,
>> +     .of_match       = sunxi_pctrl_ids,
>> +     .priv_auto_alloc_size = sizeof(struct sunxi_pctrl_priv),
>> +     .ops            = &sunxi_pctrl_ops,
>> +     .bind           = dm_scan_fdt_dev,
>> +     .probe          = sunxi_pctrl_probe,
>> +};
>> +
>> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
>> new file mode 100644
>> index 0000000..8508626
>> --- /dev/null
>> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
>> @@ -0,0 +1,311 @@
>> +/*
>> + * Allwinner A1X SoCs pinctrl driver.
>> + *
>> + * 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.
>> + */
>> +
>> +#ifndef __PINCTRL_SUNXI_H
>> +#define __PINCTRL_SUNXI_H
>> +
>> +#define PA_BASE      0
>> +#define PB_BASE      32
>> +#define PC_BASE      64
>> +#define PD_BASE      96
>> +#define PE_BASE      128
>> +#define PF_BASE      160
>> +#define PG_BASE      192
>> +#define PH_BASE      224
>> +#define PI_BASE      256
>> +#define PL_BASE      352
>> +#define PM_BASE      384
>> +#define PN_BASE      416
>> +
>> +#ifdef __UBOOT__
>> +/* Convenience macro to define a single named or anonymous pin descriptor */
>> +#define PINCTRL_PIN(a, b) { .number = a, .name = b }
>> +
>> +/**
>> + * struct pinctrl_pin_desc - boards/machines provide information on their
>> + * pins, pads or other muxable units in this struct
>> + * @number: unique pin number from the global pin number space
>> + * @name: a name for this pin
>> + * @drv_data: driver-defined per-pin data. pinctrl core does not touch this
>> + */
>> +struct pinctrl_pin_desc {
>> +     unsigned number;
>> +     const char *name;
>> +#ifndef __UBOOT__
>> +     void *drv_data;
>> +#endif
>> +};
>> +#endif
>> +
>> +#define SUNXI_PINCTRL_PIN(bank, pin)         \
>> +     PINCTRL_PIN(P ## bank ## _BASE + (pin), "P" #bank #pin)
>> +
>> +#define SUNXI_PIN_NAME_MAX_LEN       5
>> +
>> +#define BANK_MEM_SIZE                0x24
>> +#define MUX_REGS_OFFSET              0x0
>> +#define DATA_REGS_OFFSET     0x10
>> +#define DLEVEL_REGS_OFFSET   0x14
>> +#define PULL_REGS_OFFSET     0x1c
>> +
>> +#define PINS_PER_BANK                32
>> +#define MUX_PINS_PER_REG     8
>> +#define MUX_PINS_BITS                4
>> +#define MUX_PINS_MASK                0x0f
>> +#define DATA_PINS_PER_REG    32
>> +#define DATA_PINS_BITS               1
>> +#define DATA_PINS_MASK               0x01
>> +#define DLEVEL_PINS_PER_REG  16
>> +#define DLEVEL_PINS_BITS     2
>> +#define DLEVEL_PINS_MASK     0x03
>> +#define PULL_PINS_PER_REG    16
>> +#define PULL_PINS_BITS               2
>> +#define PULL_PINS_MASK               0x03
>> +
>> +#define IRQ_PER_BANK         32
>> +
>> +#define IRQ_CFG_REG          0x200
>> +#define IRQ_CFG_IRQ_PER_REG          8
>> +#define IRQ_CFG_IRQ_BITS             4
>> +#define IRQ_CFG_IRQ_MASK             ((1 << IRQ_CFG_IRQ_BITS) - 1)
>> +#define IRQ_CTRL_REG         0x210
>> +#define IRQ_CTRL_IRQ_PER_REG         32
>> +#define IRQ_CTRL_IRQ_BITS            1
>> +#define IRQ_CTRL_IRQ_MASK            ((1 << IRQ_CTRL_IRQ_BITS) - 1)
>> +#define IRQ_STATUS_REG               0x214
>> +#define IRQ_STATUS_IRQ_PER_REG               32
>> +#define IRQ_STATUS_IRQ_BITS          1
>> +#define IRQ_STATUS_IRQ_MASK          ((1 << IRQ_STATUS_IRQ_BITS) - 1)
>> +
>> +#define IRQ_MEM_SIZE         0x20
>> +
>> +#define IRQ_EDGE_RISING              0x00
>> +#define IRQ_EDGE_FALLING     0x01
>> +#define IRQ_LEVEL_HIGH               0x02
>> +#define IRQ_LEVEL_LOW                0x03
>> +#define IRQ_EDGE_BOTH                0x04
>> +
>> +#define SUN4I_FUNC_INPUT     0
>> +#define SUN4I_FUNC_IRQ               6
>> +
>> +struct sunxi_desc_function {
>> +     const char      *name;
>> +     u8              muxval;
>> +     u8              irqbank;
>> +     u8              irqnum;
>> +};
>> +
>> +struct sunxi_desc_pin {
>> +     struct pinctrl_pin_desc         pin;
>> +     struct sunxi_desc_function      *functions;
>> +};
>> +
>> +struct sunxi_pinctrl_desc {
>> +     const struct sunxi_desc_pin     *pins;
>> +     int                             npins;
>> +     unsigned                        pin_base;
>> +     unsigned                        irq_banks;
>> +     unsigned                        irq_bank_base;
>> +     bool                            irq_read_needs_mux;
>> +};
>> +
>> +struct sunxi_pinctrl_function {
>> +     const char      *name;
>> +     const char      **groups;
>> +     unsigned        ngroups;
>> +};
>> +
>> +struct sunxi_pinctrl_group {
>> +     const char      *name;
>> +     unsigned long   config;
>> +     unsigned        pin;
>> +};
>> +
>> +#ifndef __UBOOT__
>> +struct sunxi_pinctrl {
>> +     void __iomem                    *membase;
>> +     struct gpio_chip                *chip;
>> +     const struct sunxi_pinctrl_desc *desc;
>> +     struct device                   *dev;
>> +     struct irq_domain               *domain;
>> +     struct sunxi_pinctrl_function   *functions;
>> +     unsigned                        nfunctions;
>> +     struct sunxi_pinctrl_group      *groups;
>> +     unsigned                        ngroups;
>> +     int                             *irq;
>> +     unsigned                        *irq_array;
>> +     spinlock_t                      lock;
>> +     struct pinctrl_dev              *pctl_dev;
>> +};
>> +#endif
>> +
>> +#define SUNXI_PIN(_pin, ...)                                 \
>> +     {                                                       \
>> +             .pin = _pin,                                    \
>> +             .functions = (struct sunxi_desc_function[]){    \
>> +                     __VA_ARGS__, { } },                     \
>> +     }
>> +
>> +#define SUNXI_FUNCTION(_val, _name)                          \
>> +     {                                                       \
>> +             .name = _name,                                  \
>> +             .muxval = _val,                                 \
>> +     }
>> +
>> +#define SUNXI_FUNCTION_IRQ(_val, _irq)                               \
>> +     {                                                       \
>> +             .name = "irq",                                  \
>> +             .muxval = _val,                                 \
>> +             .irqnum = _irq,                                 \
>> +     }
>> +
>> +#define SUNXI_FUNCTION_IRQ_BANK(_val, _bank, _irq)           \
>> +     {                                                       \
>> +             .name = "irq",                                  \
>> +             .muxval = _val,                                 \
>> +             .irqbank = _bank,                               \
>> +             .irqnum = _irq,                                 \
>> +     }
>> +
>> +/*
>> + * The sunXi PIO registers are organized as is:
>> + * 0x00 - 0x0c       Muxing values.
>> + *           8 pins per register, each pin having a 4bits value
>> + * 0x10              Pin values
>> + *           32 bits per register, each pin corresponding to one bit
>> + * 0x14 - 0x18       Drive level
>> + *           16 pins per register, each pin having a 2bits value
>> + * 0x1c - 0x20       Pull-Up values
>> + *           16 pins per register, each pin having a 2bits value
>> + *
>> + * This is for the first bank. Each bank will have the same layout,
>> + * with an offset being a multiple of 0x24.
>> + *
>> + * The following functions calculate from the pin number the register
>> + * and the bit offset that we should access.
>> + */
>> +static inline u32 sunxi_mux_reg(u16 pin)
>> +{
>> +     u8 bank = pin / PINS_PER_BANK;
>> +     u32 offset = bank * BANK_MEM_SIZE;
>> +     offset += MUX_REGS_OFFSET;
>> +     offset += pin % PINS_PER_BANK / MUX_PINS_PER_REG * 0x04;
>> +     return round_down(offset, 4);
>> +}
>> +
>> +static inline u32 sunxi_mux_offset(u16 pin)
>> +{
>> +     u32 pin_num = pin % MUX_PINS_PER_REG;
>> +     return pin_num * MUX_PINS_BITS;
>> +}
>> +
>> +static inline u32 sunxi_data_reg(u16 pin)
>> +{
>> +     u8 bank = pin / PINS_PER_BANK;
>> +     u32 offset = bank * BANK_MEM_SIZE;
>> +     offset += DATA_REGS_OFFSET;
>> +     offset += pin % PINS_PER_BANK / DATA_PINS_PER_REG * 0x04;
>> +     return round_down(offset, 4);
>> +}
>> +
>> +static inline u32 sunxi_data_offset(u16 pin)
>> +{
>> +     u32 pin_num = pin % DATA_PINS_PER_REG;
>> +     return pin_num * DATA_PINS_BITS;
>> +}
>> +
>> +static inline u32 sunxi_dlevel_reg(u16 pin)
>> +{
>> +     u8 bank = pin / PINS_PER_BANK;
>> +     u32 offset = bank * BANK_MEM_SIZE;
>> +     offset += DLEVEL_REGS_OFFSET;
>> +     offset += pin % PINS_PER_BANK / DLEVEL_PINS_PER_REG * 0x04;
>> +     return round_down(offset, 4);
>> +}
>> +
>> +static inline u32 sunxi_dlevel_offset(u16 pin)
>> +{
>> +     u32 pin_num = pin % DLEVEL_PINS_PER_REG;
>> +     return pin_num * DLEVEL_PINS_BITS;
>> +}
>> +
>> +static inline u32 sunxi_pull_reg(u16 pin)
>> +{
>> +     u8 bank = pin / PINS_PER_BANK;
>> +     u32 offset = bank * BANK_MEM_SIZE;
>> +     offset += PULL_REGS_OFFSET;
>> +     offset += pin % PINS_PER_BANK / PULL_PINS_PER_REG * 0x04;
>> +     return round_down(offset, 4);
>> +}
>> +
>> +static inline u32 sunxi_pull_offset(u16 pin)
>> +{
>> +     u32 pin_num = pin % PULL_PINS_PER_REG;
>> +     return pin_num * PULL_PINS_BITS;
>> +}
>> +
>> +static inline u32 sunxi_irq_cfg_reg(u16 irq, unsigned bank_base)
>> +{
>> +     u8 bank = irq / IRQ_PER_BANK;
>> +     u8 reg = (irq % IRQ_PER_BANK) / IRQ_CFG_IRQ_PER_REG * 0x04;
>> +
>> +     return IRQ_CFG_REG + (bank_base + bank) * IRQ_MEM_SIZE + reg;
>> +}
>> +
>> +static inline u32 sunxi_irq_cfg_offset(u16 irq)
>> +{
>> +     u32 irq_num = irq % IRQ_CFG_IRQ_PER_REG;
>> +     return irq_num * IRQ_CFG_IRQ_BITS;
>> +}
>> +
>> +static inline u32 sunxi_irq_ctrl_reg_from_bank(u8 bank, unsigned bank_base)
>> +{
>> +     return IRQ_CTRL_REG + (bank_base + bank) * IRQ_MEM_SIZE;
>> +}
>> +
>> +static inline u32 sunxi_irq_ctrl_reg(u16 irq, unsigned bank_base)
>> +{
>> +     u8 bank = irq / IRQ_PER_BANK;
>> +
>> +     return sunxi_irq_ctrl_reg_from_bank(bank, bank_base);
>> +}
>> +
>> +static inline u32 sunxi_irq_ctrl_offset(u16 irq)
>> +{
>> +     u32 irq_num = irq % IRQ_CTRL_IRQ_PER_REG;
>> +     return irq_num * IRQ_CTRL_IRQ_BITS;
>> +}
>> +
>> +static inline u32 sunxi_irq_status_reg_from_bank(u8 bank, unsigned bank_base)
>> +{
>> +     return IRQ_STATUS_REG + (bank_base + bank) * IRQ_MEM_SIZE;
>> +}
>> +
>> +static inline u32 sunxi_irq_status_reg(u16 irq, unsigned bank_base)
>> +{
>> +     u8 bank = irq / IRQ_PER_BANK;
>> +
>> +     return sunxi_irq_status_reg_from_bank(bank, bank_base);
>> +}
>> +
>> +static inline u32 sunxi_irq_status_offset(u16 irq)
>> +{
>> +     u32 irq_num = irq % IRQ_STATUS_IRQ_PER_REG;
>> +     return irq_num * IRQ_STATUS_IRQ_BITS;
>> +}
>> +
>> +#ifndef __UBOOT__
>> +int sunxi_pinctrl_init(struct platform_device *pdev,
>> +                    const struct sunxi_pinctrl_desc *desc);
>> +#endif
>> +
>> +#endif /* __PINCTRL_SUNXI_H */
>> --
>> 1.9.1
>>
>
> --
> Maxime Ripard, Free Electrons
> Embedded Linux and Kernel engineering
> http://free-electrons.com

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

* [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi
  2017-02-23  3:54     ` Chen-Yu Tsai
@ 2017-02-23 18:10       ` Maxime Ripard
  0 siblings, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2017-02-23 18:10 UTC (permalink / raw)
  To: u-boot

On Thu, Feb 23, 2017 at 11:54:15AM +0800, Chen-Yu Tsai wrote:
> On Thu, Feb 23, 2017 at 7:18 AM, Maxime Ripard
> <maxime.ripard@free-electrons.com> wrote:
> > On Wed, Feb 22, 2017 at 09:47:27PM +0100, Philipp Tomsich wrote:
> >> This change adds a full device-model pinctrl driver for sunxi (tested with
> >> sun50iw1p1) based on the support available in Linux.
> >>
> >> Details are:
> >>  * implements a driver for pinctrl devices and assigns sun50i-a64-pinctrl
> >>    and sun50i-a64-r-pinctrl to it
> >>  * dynamically creates the driver_data for a sunxi_gpio (see sunxi_gpio.c)
> >>    driver and binds it to the same device-tree node
> >>  * lifts and reuses the pinctrl-sunxi.h and pinctrl-sun50i-a64.c files from
> >>    Linux (thanks to Maxime and Andre) and adds a pinctrl-sun50i-a64-r.c (to
> >>    be picked up for inclusion into Linux again)
> >>
> >> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> >> ---
> >>  arch/arm/include/asm/arch-sunxi/gpio-internal.h    |  19 +
> >>  .../pinctrl/allwinner,pinctrl.txt                  |  65 +++
> >>  drivers/gpio/sunxi_gpio.c                          |  15 +-
> >>  drivers/pinctrl/Kconfig                            |  10 +
> >>  drivers/pinctrl/Makefile                           |   2 +
> >>  drivers/pinctrl/sunxi/Makefile                     |  10 +
> >>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c       |  92 ++++
> >>  drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c         | 577 +++++++++++++++++++++
> >>  drivers/pinctrl/sunxi/pinctrl-sunxi.c              | 317 +++++++++++
> >>  drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
> >>  10 files changed, 1411 insertions(+), 7 deletions(-)
> >>  create mode 100644 arch/arm/include/asm/arch-sunxi/gpio-internal.h
> >>  create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> >>  create mode 100644 drivers/pinctrl/sunxi/Makefile
> >>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> >>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> >>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
> >>  create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
> >>
> >> diff --git a/arch/arm/include/asm/arch-sunxi/gpio-internal.h b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
> >> new file mode 100644
> >> index 0000000..4dcdd34
> >> --- /dev/null
> >> +++ b/arch/arm/include/asm/arch-sunxi/gpio-internal.h
> >> @@ -0,0 +1,19 @@
> >> +/*
> >> + * (C) Copyright 2017 Theobroma Systems Design und Consulting GmbH
> >> + *
> >> + * SPDX-License-Identifier:  GPL-2.0+
> >> + */
> >> +
> >> +
> >> +#ifndef _SUNXI_GPIO_INTERNAL_H
> >> +#define _SUNXI_GPIO_INTERNAL_H
> >> +
> >> +/* This data structure is shared between the sunxi_gpio driver and
> >> + * the sunxi_pinctrl driver.
> >> + */
> >> +struct sunxi_gpio_soc_data {
> >> +     int start;
> >> +     int no_banks;
> >> +};
> >> +
> >> +#endif
> >> diff --git a/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> >> new file mode 100644
> >> index 0000000..946831f
> >> --- /dev/null
> >> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> >> @@ -0,0 +1,65 @@
> >> +* Allwinner Pinmux Controller
> >> +
> >> +Allwinner integrates multiple banks (of 32 pins each) of pin-muxing,
> >> +GPIO functionality and (optional) external interrupt functionality
> >> +into a single controller.
> >> +
> >> +For each configurable pad (certain driver-cells, such as the IO from
> >> +integrated USB PHYs or DRAM, have a fixed function and can not be
> >> +configured), the muxing options (input, output or one of the several
> >> +functions) can be selected.
> >> +
> >> +Properties for the pinctrl node:
> >> + - compatible: should be "allwinner,sun50i-pinctrl"
> >
> > There's a typo here, the compatible is sun50i-a64-pinctrl
> >
> >> + - reg: address and length of the register set for the device.
> >> + - interrupts: interrupt for the device
> >> + - clocks: A phandle to the reference clock for this device
> >
> > (and ideally, this would take three clocks: the bus gate + the two
> > oscillators).
> >
> >> +
> >> +Properties for the pinconfig sub-nodes:
> >> + - allwinner,pins: a list of pins (e.g. "PH2", "PH3") to configure
> >> + - allwinner,function: the name of pinmux function (e.g. "mmc2")
> >
> > allwinner,pins and allwinner,function are also deprecated in favour of
> > pins and function.
> >
> >> + - drive-strength: a drive-stength setting of 10, 20, 30 or 40 mA
> >> + - bias-pull-up
> >> + - bias-pull-down
> >> + - bias-disable (default)
> >
> > The default is not bias-disable, but to keep the current configuration
> 
> Shouldn't we just copy the binding docs from the Linux kernel?
> Afterall they are supposed to be the same.

Yep, that would probably be the easiest.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170223/ccf5b85f/attachment-0001.sig>

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-02-22 20:47 ` [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node Philipp Tomsich
@ 2017-03-03  4:52   ` Simon Glass
  2017-03-03 10:52     ` Dr. Philipp Tomsich
  0 siblings, 1 reply; 19+ messages in thread
From: Simon Glass @ 2017-03-03  4:52 UTC (permalink / raw)
  To: u-boot

Hi Philipp,

On 22 February 2017 at 13:47, Philipp Tomsich
<philipp.tomsich@theobroma-systems.com> wrote:
> Currently, driver binding stops once it encounters the first
> compatible driver that doesn't refuse to bind. However, there are
> cases where a single node will need to be handled by multiple driver
> classes. For those cases we provide a configurable option to continue
> to bind after the first driver has been found.
>
> The first use cases for this are from the DM conversion of the sunxi
> (Allwinner) architecture:
>  * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
>    bind against a single node
>  * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
>    bind against a single node

Does linux work this way? Another approach would be to have a separate
MISC driver with two children, one pinctrl, one clk.

>
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  drivers/core/Kconfig | 14 ++++++++++++++
>  drivers/core/lists.c | 12 +++++++++++-
>  2 files changed, 25 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/core/Kconfig b/drivers/core/Kconfig
> index 8749561..913101c 100644
> --- a/drivers/core/Kconfig
> +++ b/drivers/core/Kconfig
> @@ -31,6 +31,20 @@ config DM_WARN
>           This will cause dm_warn() to be compiled out - it will do nothing
>           when called.
>
> +config DM_ALLOW_MULTIPLE_DRIVERS
> +        bool "Allow multiple drivers to bind for one node"
> +       depends on DM
> +       default n

You should be able to drop this line.

> +       help
> +         The driver model in U-Boot originally did not allow multiple
> +         drivers to bind for a single device node.
> +
> +         If enabled, multiple drivers can now bind for a single node
> +         by using the same compatible string for matching: lists_bind_fdt()
> +         will assume that binding multiple drivers is desirable, if the
> +         caller does not request the pointer to the udevice structure to
> +         be returned (i.e. if devp is NULL).

Please update the function documentation in the header file.

> +
>  config DM_DEVICE_REMOVE
>         bool "Support device removal"
>         depends on DM
> diff --git a/drivers/core/lists.c b/drivers/core/lists.c
> index 23b6ba7..52efe69 100644
> --- a/drivers/core/lists.c
> +++ b/drivers/core/lists.c
> @@ -166,7 +166,11 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
>                 dm_dbg("   - attempt to match compatible string '%s'\n",
>                        compat);
>
> -               for (entry = driver; entry != driver + n_ents; entry++) {
> +               entry = driver;
> +#if defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)
> +       allow_more_matches:
> +#endif
> +               for (; entry != driver + n_ents; entry++) {
>                         ret = driver_check_compatible(entry->of_match, &id,
>                                                       compat);
>                         if (!ret)
> @@ -190,6 +194,12 @@ int lists_bind_fdt(struct udevice *parent, const void *blob, int offset,
>                         found = true;
>                         if (devp)
>                                 *devp = dev;
> +#if defined(CONFIG_DM_ALLOW_MULTIPLE_DRIVERS)

Can you make this a variable, e.g. with allow_multiple =
IS_ENABLED(DM_ALLOW_MULTIPLE_DRIVERS)? I'd prefer not to add #ifdefs
in this file.

> +                       else {
> +                               entry++;
> +                               goto allow_more_matches;

Is it possible to loop without using goto?

> +                       }
> +#endif
>                 }
>                 break;
>         }
> --
> 1.9.1
>

Regards,
Simon

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-03-03  4:52   ` Simon Glass
@ 2017-03-03 10:52     ` Dr. Philipp Tomsich
  2017-03-12 20:21       ` Simon Glass
  0 siblings, 1 reply; 19+ messages in thread
From: Dr. Philipp Tomsich @ 2017-03-03 10:52 UTC (permalink / raw)
  To: u-boot

Hi Simon,

> On 03 Mar 2017, at 05:52, Simon Glass <sjg@chromium.org> wrote:
> 
> Hi Philipp,
> 
> On 22 February 2017 at 13:47, Philipp Tomsich
> <philipp.tomsich at theobroma-systems.com <mailto:philipp.tomsich@theobroma-systems.com>> wrote:
>> Currently, driver binding stops once it encounters the first
>> compatible driver that doesn't refuse to bind. However, there are
>> cases where a single node will need to be handled by multiple driver
>> classes. For those cases we provide a configurable option to continue
>> to bind after the first driver has been found.
>> 
>> The first use cases for this are from the DM conversion of the sunxi
>> (Allwinner) architecture:
>> * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
>>   bind against a single node
>> * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
>>   bind against a single node
> 
> Does linux work this way? Another approach would be to have a separate
> MISC driver with two children, one pinctrl, one clk.

The linux CLK driver creates and registers a reset-controller; the PINCTRL driver
does the same with the gpio-controller. Similar code to do this is easily possible in
U-Boot … see sunxi_pctrl_bind_gpio(…) in [PATCH v2 1/6] of this series.

However, binding multiple times makes for much simpler code and allows to keep
driver data in separate drivers.

Regards,
Philipp.

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-03-03 10:52     ` Dr. Philipp Tomsich
@ 2017-03-12 20:21       ` Simon Glass
  2017-03-13  8:40         ` Heiko Stübner
  2017-03-20  7:08         ` Maxime Ripard
  0 siblings, 2 replies; 19+ messages in thread
From: Simon Glass @ 2017-03-12 20:21 UTC (permalink / raw)
  To: u-boot

Hi,

On 3 March 2017 at 03:52, Dr. Philipp Tomsich
<philipp.tomsich@theobroma-systems.com> wrote:
> Hi Simon,
>
> On 03 Mar 2017, at 05:52, Simon Glass <sjg@chromium.org> wrote:
>
> Hi Philipp,
>
> On 22 February 2017 at 13:47, Philipp Tomsich
> <philipp.tomsich@theobroma-systems.com> wrote:
>
> Currently, driver binding stops once it encounters the first
> compatible driver that doesn't refuse to bind. However, there are
> cases where a single node will need to be handled by multiple driver
> classes. For those cases we provide a configurable option to continue
> to bind after the first driver has been found.
>
> The first use cases for this are from the DM conversion of the sunxi
> (Allwinner) architecture:
> * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
>   bind against a single node
> * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
>   bind against a single node
>
>
> Does linux work this way? Another approach would be to have a separate
> MISC driver with two children, one pinctrl, one clk.
>
>
> The linux CLK driver creates and registers a reset-controller; the PINCTRL
> driver
> does the same with the gpio-controller. Similar code to do this is easily
> possible in
> U-Boot … see sunxi_pctrl_bind_gpio(…) in [PATCH v2 1/6] of this series.
>
> However, binding multiple times makes for much simpler code and allows to
> keep
> driver data in separate drivers.

My question was more whether Linux registers multiple drivers with one
device node. It's just not something I expected.

I'm not really convinced on this. It will break the current one-to-one
relationship, and functions like device_get_child_by_of_offset(). I
think it would be better to have a MISC driver with two children.

Regards,
Simon

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-03-12 20:21       ` Simon Glass
@ 2017-03-13  8:40         ` Heiko Stübner
  2017-03-20  7:08         ` Maxime Ripard
  1 sibling, 0 replies; 19+ messages in thread
From: Heiko Stübner @ 2017-03-13  8:40 UTC (permalink / raw)
  To: u-boot

Hi Simon,

Am Sonntag, 12. März 2017, 14:21:35 CET schrieb Simon Glass:
> On 3 March 2017 at 03:52, Dr. Philipp Tomsich
> <philipp.tomsich@theobroma-systems.com> wrote:
> > On 03 Mar 2017, at 05:52, Simon Glass <sjg@chromium.org> wrote:
> > On 22 February 2017 at 13:47, Philipp Tomsich
> > <philipp.tomsich@theobroma-systems.com> wrote:
> > 
> > Currently, driver binding stops once it encounters the first
> > compatible driver that doesn't refuse to bind. However, there are
> > cases where a single node will need to be handled by multiple driver
> > classes. For those cases we provide a configurable option to continue
> > to bind after the first driver has been found.
> > 
> > The first use cases for this are from the DM conversion of the sunxi
> > (Allwinner) architecture:
> > * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
> > 
> >   bind against a single node
> > 
> > * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
> > 
> >   bind against a single node
> > 
> > Does linux work this way? Another approach would be to have a separate
> > MISC driver with two children, one pinctrl, one clk.
> > 
> > 
> > The linux CLK driver creates and registers a reset-controller; the PINCTRL
> > driver
> > does the same with the gpio-controller. Similar code to do this is easily
> > possible in
> > U-Boot … see sunxi_pctrl_bind_gpio(…) in [PATCH v2 1/6] of this series.
> > 
> > However, binding multiple times makes for much simpler code and allows to
> > keep
> > driver data in separate drivers.
> 
> My question was more whether Linux registers multiple drivers with one
> device node. It's just not something I expected.
> 
> I'm not really convinced on this. It will break the current one-to-one
> relationship, and functions like device_get_child_by_of_offset(). I
> think it would be better to have a MISC driver with two children.

In the regular device model the Linux kernel also generally has a one-to-one 
model. There are special cases, like clock and timer init using special 
handling, or things like "syscon" which acts like more of a flag and can live 
next to a regular device.

Therefore things like the Rockchip clock controller create the reset 
controller included in the CRU block. A behaviour the uboot clk driver mimics.

Also doesn't allowing multiple drivers to sit on top of a node create 
contention on who gets access to registers? At least in the kernel multiple 
mappings of registers are generally not favoured.


Heiko

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-03-12 20:21       ` Simon Glass
  2017-03-13  8:40         ` Heiko Stübner
@ 2017-03-20  7:08         ` Maxime Ripard
  2017-03-22 13:05           ` Simon Glass
  1 sibling, 1 reply; 19+ messages in thread
From: Maxime Ripard @ 2017-03-20  7:08 UTC (permalink / raw)
  To: u-boot

On Sun, Mar 12, 2017 at 02:21:35PM -0600, Simon Glass wrote:
> Hi,
> 
> On 3 March 2017 at 03:52, Dr. Philipp Tomsich
> <philipp.tomsich@theobroma-systems.com> wrote:
> > Hi Simon,
> >
> > On 03 Mar 2017, at 05:52, Simon Glass <sjg@chromium.org> wrote:
> >
> > Hi Philipp,
> >
> > On 22 February 2017 at 13:47, Philipp Tomsich
> > <philipp.tomsich@theobroma-systems.com> wrote:
> >
> > Currently, driver binding stops once it encounters the first
> > compatible driver that doesn't refuse to bind. However, there are
> > cases where a single node will need to be handled by multiple driver
> > classes. For those cases we provide a configurable option to continue
> > to bind after the first driver has been found.
> >
> > The first use cases for this are from the DM conversion of the sunxi
> > (Allwinner) architecture:
> > * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
> >   bind against a single node
> > * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
> >   bind against a single node
> >
> >
> > Does linux work this way? Another approach would be to have a separate
> > MISC driver with two children, one pinctrl, one clk.
> >
> >
> > The linux CLK driver creates and registers a reset-controller; the PINCTRL
> > driver
> > does the same with the gpio-controller. Similar code to do this is easily
> > possible in
> > U-Boot … see sunxi_pctrl_bind_gpio(…) in [PATCH v2 1/6] of this series.
> >
> > However, binding multiple times makes for much simpler code and allows to
> > keep
> > driver data in separate drivers.
> 
> My question was more whether Linux registers multiple drivers with one
> device node. It's just not something I expected.

It does, but it's not really what we're doing in the linux driver. It
has one driver, with one device, but registering into multiple
frameworks.

Maxime

-- 
Maxime Ripard, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.denx.de/pipermail/u-boot/attachments/20170320/13f2e446/attachment.sig>

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

* [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node
  2017-03-20  7:08         ` Maxime Ripard
@ 2017-03-22 13:05           ` Simon Glass
  0 siblings, 0 replies; 19+ messages in thread
From: Simon Glass @ 2017-03-22 13:05 UTC (permalink / raw)
  To: u-boot

Hi,

On 20 March 2017 at 01:08, Maxime Ripard
<maxime.ripard@free-electrons.com> wrote:
> On Sun, Mar 12, 2017 at 02:21:35PM -0600, Simon Glass wrote:
>> Hi,
>>
>> On 3 March 2017 at 03:52, Dr. Philipp Tomsich
>> <philipp.tomsich@theobroma-systems.com> wrote:
>> > Hi Simon,
>> >
>> > On 03 Mar 2017, at 05:52, Simon Glass <sjg@chromium.org> wrote:
>> >
>> > Hi Philipp,
>> >
>> > On 22 February 2017 at 13:47, Philipp Tomsich
>> > <philipp.tomsich@theobroma-systems.com> wrote:
>> >
>> > Currently, driver binding stops once it encounters the first
>> > compatible driver that doesn't refuse to bind. However, there are
>> > cases where a single node will need to be handled by multiple driver
>> > classes. For those cases we provide a configurable option to continue
>> > to bind after the first driver has been found.
>> >
>> > The first use cases for this are from the DM conversion of the sunxi
>> > (Allwinner) architecture:
>> > * pinctrl (UCLASS_PINCTRL) and gpio (UCLASS_GPIO) drivers need to
>> >   bind against a single node
>> > * clock (UCLASS_CLK) and reset (UCLASS_RESET) drivers also need to
>> >   bind against a single node
>> >
>> >
>> > Does linux work this way? Another approach would be to have a separate
>> > MISC driver with two children, one pinctrl, one clk.
>> >
>> >
>> > The linux CLK driver creates and registers a reset-controller; the PINCTRL
>> > driver
>> > does the same with the gpio-controller. Similar code to do this is easily
>> > possible in
>> > U-Boot … see sunxi_pctrl_bind_gpio(…) in [PATCH v2 1/6] of this series.
>> >
>> > However, binding multiple times makes for much simpler code and allows to
>> > keep
>> > driver data in separate drivers.
>>
>> My question was more whether Linux registers multiple drivers with one
>> device node. It's just not something I expected.
>
> It does, but it's not really what we're doing in the linux driver. It
> has one driver, with one device, but registering into multiple
> frameworks.

I believe that the U-Boot equivalent of this is to have a parent
device with several children each in its own uclass.

Regards,
Simon

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

end of thread, other threads:[~2017-03-22 13:05 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-22 20:47 [U-Boot] [PATCH v2 0/6] sunxi: DM pinctrl implementation Philipp Tomsich
2017-02-22 20:47 ` [U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi Philipp Tomsich
2017-02-22 23:18   ` Maxime Ripard
2017-02-23  3:54     ` Chen-Yu Tsai
2017-02-23 18:10       ` Maxime Ripard
2017-02-22 20:47 ` [U-Boot] [PATCH v2 2/6] dm: core: Allow multiple drivers to bind for a single node Philipp Tomsich
2017-03-03  4:52   ` Simon Glass
2017-03-03 10:52     ` Dr. Philipp Tomsich
2017-03-12 20:21       ` Simon Glass
2017-03-13  8:40         ` Heiko Stübner
2017-03-20  7:08         ` Maxime Ripard
2017-03-22 13:05           ` Simon Glass
2017-02-22 20:47 ` [U-Boot] [PATCH v2 3/6] sunxi: CONFIG_DM_ALLOW_MULTIPLE_DRIVERS for gpio/pinctrl binding Philipp Tomsich
2017-02-22 20:47 ` [U-Boot] [PATCH v2 4/6] defconfig: lynx: enable CONFIG_DM_ALLOW_MULTIPLE_DRIVERS Philipp Tomsich
2017-02-22 23:19   ` Maxime Ripard
2017-02-22 20:47 ` [U-Boot] [PATCH v2 5/6] sun50i: dts: add r_pio node and pinconfig entries into r_pio and pio Philipp Tomsich
2017-02-22 20:47 ` [U-Boot] [PATCH v2 6/6] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
2017-02-22 23:20   ` Maxime Ripard
2017-02-22 23:24     ` Dr. Philipp Tomsich

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.