All of lore.kernel.org
 help / color / mirror / Atom feed
* [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL
@ 2017-02-17 17:52 Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 01/11] spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined Philipp Tomsich
                   ` (10 more replies)
  0 siblings, 11 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

This provides DM drivers for CLK, RESET and PINCTRL for sunxi with
initial enablement and testing done on the sun50iw1p1 (A64). As the
sun6i, sun8i and sun9i platforms are similar enough, these should be
easily added on top of this framework.

To make switching over easier, the necessary changes in the MMC
drivers and sun8i_emac are also contained as individual changes in
this set.

At this point, I'd like to see sunxi move towards a TPL approach as
the SRAM area used by the BROM for loading the SPL is too small for
both FIT and DM... yet most Allwinner SoCs have largish SRAM areas
that could be used for a TPL (even one that could use config data to
set up the DRAM according to a specific board's requirements).  If we
could get that point, we should be able to get rid of most of the chip
and board-specific (i.e. watch for low-fying #ifdef's) pinconfig code
in board/sunxi/board.c.

This changeset was tested against the A64-uQ7 (with our DTS, which is
to date only available from our public git repositories) for the
following devices:
 * MMC0 (uSD slot) and MMC2 (on-module eMMC)
 * Ethernet (sun8i_emac)
 * SPI (SPI DM driver from the A31-uQ7 tree, will post separately)
 * GPIO

The sunxi I2C driver still needs to be converted to make use of
these changes.



Philipp Tomsich (11):
  spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined
  sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back
    into GPIO
  sun50i: dts: add gpiobank nodes to the pinctrl nodes
  sun50i: dts: update DTS to avoid warnings
  sunxi: add module reset (UCLASS_RESET) support for sunxi
  sunxi: add clock driver (UCLASS_CLK) support for sunxi
  sunxi: Scan DT tree node '/clocks' on sunxi boards
  sun8i_emac: update to work with pinctrl-sunxi, reset-sunxi and
    clk-sunxi
  sunxi_mmc: convert to a device-model driver
  dts: sun50i: update mmc pin configuration and add mmc2_8bit_pins
  sun50i: dts: add spi0 and spi1 nodes and pinconfig

 arch/arm/dts/sun50i-a64.dtsi                       | 171 +++++-
 board/sunxi/board.c                                |  20 +-
 .../pinctrl/allwinner,pinctrl.txt                  | 130 +++++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/sunxi/Makefile                         |   7 +
 drivers/clk/sunxi/clk-sunxi-gate.c                 |  92 ++++
 drivers/clk/sunxi/clk-sunxi-mod.c                  | 232 +++++++++
 drivers/clk/sunxi/clk-sunxi-pll.c                  | 120 +++++
 drivers/gpio/sunxi_gpio.c                          |  94 +++-
 drivers/mmc/sunxi_mmc.c                            | 344 +++++++++++-
 drivers/net/sun8i_emac.c                           |  45 +-
 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              | 309 +++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
 drivers/reset/Kconfig                              |   9 +
 drivers/reset/Makefile                             |   1 +
 drivers/reset/reset-sunxi.c                        | 105 ++++
 include/config_uncmd_spl.h                         |   4 +
 scripts/Makefile.uncmd_spl                         |   4 +
 23 files changed, 2632 insertions(+), 58 deletions(-)
 create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sunxi-gate.c
 create mode 100644 drivers/clk/sunxi/clk-sunxi-mod.c
 create mode 100644 drivers/clk/sunxi/clk-sunxi-pll.c
 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
 create mode 100644 drivers/reset/reset-sunxi.c

-- 
1.9.1

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

* [U-Boot] [PATCH v1 01/11] spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO Philipp Tomsich
                   ` (9 subsequent siblings)
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

CONFIG_DM_MMC, CONFIG_DM_MMC_OPS and CONFIG_BLK depend on CONFIG_DM (CONFIG_SPL_DM);
if either is enabled, but CONFIG_SPL_DM is disabled, we also need to undef them.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 include/config_uncmd_spl.h | 4 ++++
 scripts/Makefile.uncmd_spl | 4 ++++
 2 files changed, 8 insertions(+)

diff --git a/include/config_uncmd_spl.h b/include/config_uncmd_spl.h
index 3c1499c..06498a4 100644
--- a/include/config_uncmd_spl.h
+++ b/include/config_uncmd_spl.h
@@ -1,25 +1,29 @@
 /*
  * (C) Copyright 2012
  * Ilya Yanok, ilya.yanok at gmail.com
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #ifndef __CONFIG_UNCMD_SPL_H__
 #define __CONFIG_UNCMD_SPL_H__
 
 #ifdef CONFIG_SPL_BUILD
 /* SPL needs only BOOTP + TFTP so undefine other stuff to save space */
 
 #ifndef CONFIG_SPL_DM
 #undef CONFIG_DM_SERIAL
 #undef CONFIG_DM_GPIO
 #undef CONFIG_DM_I2C
 #undef CONFIG_DM_SPI
+#undef CONFIG_DM_MMC
+#undef CONFIG_DM_MMC_OPS
+/* CONFIG_BLK also depends on DM */
+#undef CONFIG_BLK
 #endif
 
 #undef CONFIG_DM_WARN
 #undef CONFIG_DM_STDIO
 
 #endif /* CONFIG_SPL_BUILD */
 #endif /* __CONFIG_UNCMD_SPL_H__ */
diff --git a/scripts/Makefile.uncmd_spl b/scripts/Makefile.uncmd_spl
index 15d0836..99b9dee 100644
--- a/scripts/Makefile.uncmd_spl
+++ b/scripts/Makefile.uncmd_spl
@@ -12,6 +12,10 @@ CONFIG_DM_GPIO=
 CONIFG_DM_I2C=
 CONFIG_DM_SPI=
 CONFIG_DM_SPI_FLASH=
+CONFIG_DM_MMC=
+CONFIG_DM_MMC_OPS=
+# CONFIG_BLK also depends on CONFIG_DM
+CONFIG_BLK=
 endif
 
 endif
-- 
1.9.1

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

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 01/11] spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-21  3:48   ` Chen-Yu Tsai
  2017-02-21 20:14   ` Maxime Ripard
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 03/11] sun50i: dts: add gpiobank nodes to the pinctrl nodes Philipp Tomsich
                   ` (8 subsequent siblings)
  10 siblings, 2 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 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
 * defines and implements a binding for sunxi-style GPIO banks (to make it
   easier to describe controllers like the A64 which may not start at 'A')
   and provide the necessary translation mechanisms:
   - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
     the device framework will try to access a UCLASS_GPIO device bound to
     the same node id as the pinctrl device to perform it's of_xlate lookup.
     For this, we provide a 'gpiobridge' driver (which needs to access the
     platdata of the pinctrl device) and which can then map accesses to an
     actual GPIO bank device.
   - For the individual GPIO banks, we use a new driver (which shares most
     of its ops with the existing sunxi_gpio driver, except probe and bind)
     and provides configuration without any platdata structure.
 * 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)

The active DM tree at runtime (with this enabled) should look similar to
the following:

     pinctrl     [ + ]    |   |-- pinctrl at 1c20800
     gpio        [ + ]    |   |   |-- gpiob at 24
     gpio        [ + ]    |   |   |-- gpioc at 48
     gpio        [ + ]    |   |   |-- gpiod at 6c
     gpio        [ + ]    |   |   |-- gpioe at 90
     gpio        [ + ]    |   |   |-- gpiof at b4
     gpio        [ + ]    |   |   |-- gpiog at d8
     gpio        [ + ]    |   |   |-- gpioh at fc
     pinconfig   [ + ]    |   |   |-- uart0_pins_a
     pinconfig   [   ]    |   |   |-- uart0_pins_b
     pinconfig   [   ]    |   |   |-- uart1_2pins
     pinconfig   [   ]    |   |   |-- uart1_4pins
     pinconfig   [   ]    |   |   |-- uart2_2pins
     pinconfig   [   ]    |   |   |-- uart2_4
     pinconfig   [   ]    |   |   |-- uart3
     pinconfig   [   ]    |   |   |-- uart3_2
     pinconfig   [   ]    |   |   |-- uart3_4
     pinconfig   [   ]    |   |   |-- uart4_2
     pinconfig   [   ]    |   |   |-- uart4_4
     pinconfig   [ + ]    |   |   |-- mmc0
     pinconfig   [ + ]    |   |   |-- mmc0_cd_pin
     pinconfig   [   ]    |   |   |-- mmc1
     pinconfig   [   ]    |   |   |-- mmc2
     pinconfig   [ + ]    |   |   |-- mmc2_8bit
     pinconfig   [   ]    |   |   |-- i2c0_pins
     pinconfig   [   ]    |   |   |-- i2c1_pins
     pinconfig   [   ]    |   |   |-- i2c2_pins
     pinconfig   [   ]    |   |   |-- rmii_pins
     pinconfig   [ + ]    |   |   |-- rgmii_pins
     pinconfig   [ + ]    |   |   |-- spi0_pins
     pinconfig   [   ]    |   |   |-- spi1_pins
     pinconfig   [ + ]    |   |   |-- led_pins_sdio
     pinconfig   [ + ]    |   |   |-- ethphy_reset_pin
     gpio        [ + ]    |   |   `-- gpiobridge
     pinctrl     [ + ]    |   |-- pinctrl at 01f02c00
     gpio        [ + ]    |   |   |-- gpiol at 0
     pinconfig   [ + ]    |   |   |-- led_pins_power
     gpio        [ + ]    |   |   `-- gpiobridge

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 .../pinctrl/allwinner,pinctrl.txt                  | 130 +++++
 drivers/gpio/sunxi_gpio.c                          |  97 +++-
 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              | 326 ++++++++++++
 drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
 9 files changed, 1553 insertions(+), 2 deletions(-)
 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/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
new file mode 100644
index 0000000..e536ea3
--- /dev/null
+++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
@@ -0,0 +1,130 @@
+* 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.
+
+The Allwinner pinctrl node contains a description of the pinctrl block
+(i.e. including GPIO and external interrupt capability, if available)
+and subnodes describing individual GPIO banks and pin-configuration.
+
+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>
+
+Properties for the gpio sub-nodes:
+ - compatible: should be "allwinner,sunxi-gpiobank"
+ - allwinner,gpiobank-name: the name of the bank (e.g. <'A'>, <'B'>, ...)
+ - reg: offsets (within the address range of the enclosing pinctrl
+        node's address space) and length of the registers, where
+	* The first entry points to the mux/gpio registers.
+	* An (optional) second entry points to the extint registers.
+	  Note, that the second entry should be provided, if the
+	  interrupt property is present.
+ - interrupt: the interrupt used for external interrupt signalling
+              (should be one of the interrupts specified in the
+	      enclosing pinctrl node)
+
+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>;
+
+		/* The A64 does not have bank A and leaves a hole in the
+		   address space where it normally would be */
+
+		gpiob: gpiob at 24 {
+			compatible = "allwinner,sunxi-gpiobank";
+			allwinner,gpiobank-name = <'B'>;
+			reg = < 0x24 0x24 >, < 0x200 0x1c >;
+			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		gpioc: gpioc at 48 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x48 0x24 >;
+			allwinner,gpiobank-name = <'C'>;
+		};
+
+		gpiod: gpiod at 6c {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x6c 0x24 >;
+			allwinner,gpiobank-name = <'D'>;
+		};
+
+		gpioe: gpioe at 90 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x90 0x24 >;
+			allwinner,gpiobank-name = <'E'>;
+		};
+
+		gpiof: gpiof at b4 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0xb4 0x24 >;
+			allwinner,gpiobank-name = <'F'>;
+		};
+
+		gpiog: gpiog at d8 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0xd8 0x24 >, < 0x220 0x1c >;
+			allwinner,gpiobank-name = <'G'>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		gpioh: gpioh at fc {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0xfc 0x24 >, < 0x220 0x1c >;
+			allwinner,gpiobank-name = <'H'>;
+			interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		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..6cf2be4 100644
--- a/drivers/gpio/sunxi_gpio.c
+++ b/drivers/gpio/sunxi_gpio.c
@@ -344,36 +344,129 @@ static const struct sunxi_gpio_soc_data soc_data_l_3 = {
 static const struct udevice_id sunxi_gpio_ids[] = {
 	ID("allwinner,sun4i-a10-pinctrl",	a_all),
 	ID("allwinner,sun5i-a10s-pinctrl",	a_all),
 	ID("allwinner,sun5i-a13-pinctrl",	a_all),
 	ID("allwinner,sun6i-a31-pinctrl",	a_all),
 	ID("allwinner,sun6i-a31s-pinctrl",	a_all),
 	ID("allwinner,sun7i-a20-pinctrl",	a_all),
 	ID("allwinner,sun8i-a23-pinctrl",	a_all),
 	ID("allwinner,sun8i-a33-pinctrl",	a_all),
 	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
 	{ }
 };
 
 U_BOOT_DRIVER(gpio_sunxi) = {
 	.name	= "gpio_sunxi",
 	.id	= UCLASS_GPIO,
 	.ops	= &gpio_sunxi_ops,
 	.of_match = sunxi_gpio_ids,
 	.bind	= gpio_sunxi_bind,
 	.probe	= gpio_sunxi_probe,
 };
+
+#if defined(CONFIG_SUNXI_PINCTRL)
+
+/* When we use the sunxi pinctrl infrastructure, we have two issues that
+ * are resolved by the gpiobank and gpiobrige drivers:
+ *
+ *  - We need to have a UCLASS_GPIO device bound to the pinctrl-nodes for
+ *    translating between gpio entries (e.g. <&pio 3 24 GPIO_ACTIVE_LOW>;)
+ *    in the DT and actual gpio banks. This is done using the gpiobridge
+ *    driver, which provides only a lookup/translation mechanism.
+ *    This mechanism lives in pinctrl-sunxi.c
+ *
+ *  - We introduce a generic gpiobank device, which resolves the need to
+ *    have distinct soc_data structure for each device and avoids having
+ *    to share data structures and config data between this file and the
+ *    sunxi pinctrl (the other option would be to have the soc_data info
+ *    visible in pinctrl-sunxi.c (or merge it into this file) and bind a
+ *    gpio_sunxi device that is set up with the appropriate soc_data) to
+ *    the same node as the pinctrl device.
+ */
+
+static int gpiobank_sunxi_probe(struct udevice *dev)
+{
+	struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
+	struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+	int bank_name;
+	int bank;
+	fdt_addr_t offset;
+	fdt_size_t size;
+	fdt_addr_t base;
+
+	debug("%s: %s", __func__, dev->name);
+
+	/* At this time we are only interested in index 0 (the PIO registers)
+	   and we ignore index 1 (the external interrupt control), even if
+	   present and an interrupt property exists... */
+	offset = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
+						    dev->of_offset,
+						    "reg", 0, &size,
+						    false);
+	if (offset == FDT_ADDR_T_NONE) {
+		error("%s: missing 'reg' for offset into parent device\n",
+		      dev->name);
+		return -EINVAL;
+	}
+
+	bank_name = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+				   "allwinner,gpiobank-name", -EINVAL);
+	if (bank_name == -EINVAL) {
+		error("%s: missing 'allwinner,gpiobank-name'\n", dev->name);
+		return -EINVAL;
+	}
+
+	base = dev_get_addr(dev->parent);
+	if (base == FDT_ADDR_T_NONE) {
+		error("%s: parent '%s' does not have a valid base address\n",
+		      dev->name, dev->parent->name);
+		return -EINVAL;
+	}
+
+	bank = bank_name - 'A';
+
+	plat->regs = (void *)(base + offset);
+	plat->gpio_count = SUNXI_GPIOS_PER_BANK;
+	plat->bank_name = gpio_bank_name(bank);
+
+	/* Tell the uclass how many GPIOs we have */
+	uc_priv->gpio_count = plat->gpio_count;
+	uc_priv->bank_name = plat->bank_name;
+
+	return 0;
+}
+
+static const struct udevice_id sunxi_gpiobank_ids[] = {
+	{ .compatible = "allwinner,sunxi-gpiobank", },
+	{}
+};
+
+U_BOOT_DRIVER(gpiobank_sunxi) = {
+	.name	= "gpiobank_sunxi",
+	.id	= UCLASS_GPIO,
+	.ops	= &gpio_sunxi_ops,
+	.of_match = sunxi_gpiobank_ids,
+	.platdata_auto_alloc_size = sizeof(struct sunxi_gpio_platdata),
+	.probe	= gpiobank_sunxi_probe,
+};
+
+#endif /* CONFIG_SUNXI_PINCTRL */
+
 #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
@@ -1,18 +1,20 @@
 #
 # SPDX-License-Identifier:	GPL-2.0+
 #
 
 obj-y					+= pinctrl-uclass.o
 obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC)	+= pinctrl-generic.o
 
 obj-$(CONFIG_PINCTRL_AT91PIO4)		+= pinctrl-at91-pio4.o
 obj-y					+= nxp/
 obj-$(CONFIG_ARCH_ATH79) += ath79/
 obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
 obj-$(CONFIG_PINCTRL_SANDBOX)	+= pinctrl-sandbox.o
 
 obj-$(CONFIG_PINCTRL_UNIPHIER)	+= uniphier/
 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..36579b1
--- /dev/null
+++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
@@ -0,0 +1,326 @@
+/*
+ * (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 <dm/lists.h>
+#include <dm/pinctrl.h>
+#include <dt-bindings/pinctrl/sun4i-a10.h>
+#include <dt-bindings/gpio/gpio.h>
+#include "pinctrl-sunxi.h"
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_pctrl_priv {
+	void *base;
+};
+
+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 int sunxi_pctrl_probe(struct udevice *dev)
+{
+	struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
+#if CONFIG_DM_GPIO
+	struct udevice *gpiobridge;
+#endif
+	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;
+
+#if CONFIG_DM_GPIO
+	device_bind_driver_to_node(dev, "sunxi_pctrl_gpiobridge",
+				   "gpiobridge", dev->of_offset, &gpiobridge);
+
+	/* The new device will have been created with no driver data,
+	   so we need to set it here, as it contains the pin_base of
+	   the gpio-group.  */
+	if (gpiobridge)
+		gpiobridge->driver_data = dev_get_driver_data(dev);
+#endif
+
+	return 0;
+}
+
+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,
+};
+
+
+#if defined(CONFIG_DM_GPIO)
+/* The gpiobridge exists to translate <&pio 3 24 GPIO_ACTIVE_LOW> into the
+   underlying GPIO bank.  It needs to access/understand the driver-data for
+   the pinctrl device, so it lives here instead of in sunxi_gpio.c ... */
+
+static int sunxi_gpiobridge_xlate(struct udevice *dev, struct gpio_desc *desc,
+				  struct fdtdec_phandle_args *args)
+{
+	const struct sunxi_pinctrl_desc *data =
+		(struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
+	struct udevice *gpiobank;
+	char name[3] = {'P', 'A', '\0' };
+
+	if (!data) {
+		debug("%s: no driver_data\n", dev->name);
+		return -ENOENT;
+	}
+
+	/* Naming on each pinctrl may start with an offset (e.g. R_PIO
+	   usually starts at 'PL'). */
+	name[1] += data->pin_base / PINS_PER_BANK;
+	/* Now add the bank-number within this pinctrl */
+	name[1] += args->args[0];
+
+	for (uclass_first_device(UCLASS_GPIO, &gpiobank);
+	     gpiobank;
+	     uclass_next_device(&gpiobank)) {
+		int count;
+		const char *bank_name = gpio_get_bank_info(gpiobank, &count);
+
+		if (bank_name && !strcmp(bank_name, name)) {
+			desc->dev = gpiobank;
+			desc->offset = args->args[1];
+			desc->flags = args->args[2] & (GPIO_ACTIVE_LOW ?
+						       GPIOD_ACTIVE_LOW : 0);
+			return 0;
+		}
+	}
+
+	return -ENOENT;
+}
+
+static const struct dm_gpio_ops gpiobridge_sunxi_ops = {
+	.xlate		= sunxi_gpiobridge_xlate,
+};
+
+U_BOOT_DRIVER(gpiobridge_sunxi) = {
+	.name		= "sunxi_pctrl_gpiobridge",
+	.id		= UCLASS_GPIO,
+	.ops            = &gpiobridge_sunxi_ops,
+};
+#endif /* DM_GPIO */
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 v1 03/11] sun50i: dts: add gpiobank nodes to the pinctrl nodes
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 01/11] spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
                   ` (7 subsequent siblings)
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

To sync up with use of a pinctrl-driver and to have individual gpiobank
nodes (to easier model controllers that have gaps in the use of bank
numbering), this updates the DTS.

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

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index 24406d0..efed838 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -1,697 +1,762 @@
 /*
  * Copyright (C) 2016 ARM Ltd.
  * based on the Allwinner H3 dtsi:
  *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
  *
  * This file is dual-licensed: you can use it either under the terms
  * of the GPL or the X11 license, at your option. Note that this dual
  * licensing only applies to this file, and not this project as a
  * whole.
  *
  *  a) This file is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License as
  *     published by the Free Software Foundation; either version 2 of the
  *     License, or (at your option) any later version.
  *
  *     This file is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     GNU General Public License for more details.
  *
  * Or, alternatively,
  *
  *  b) Permission is hereby granted, free of charge, to any person
  *     obtaining a copy of this software and associated documentation
  *     files (the "Software"), to deal in the Software without
  *     restriction, including without limitation the rights to use,
  *     copy, modify, merge, publish, distribute, sublicense, and/or
  *     sell copies of the Software, and to permit persons to whom the
  *     Software is furnished to do so, subject to the following
  *     conditions:
  *
  *     The above copyright notice and this permission notice shall be
  *     included in all copies or substantial portions of the Software.
  *
  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  *     OTHER DEALINGS IN THE SOFTWARE.
  */
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
 
 / {
 	interrupt-parent = <&gic>;
 	#address-cells = <1>;
 	#size-cells = <1>;
 
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
 
 		cpu at 0 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <0>;
 			enable-method = "psci";
 		};
 
 		cpu at 1 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <1>;
 			enable-method = "psci";
 		};
 
 		cpu at 2 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <2>;
 			enable-method = "psci";
 		};
 
 		cpu at 3 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <3>;
 			enable-method = "psci";
 		};
 	};
 
 	psci {
 		compatible = "arm,psci-0.2";
 		method = "smc";
 	};
 
 	memory {
 		device_type = "memory";
 		reg = <0x40000000 0>;
 	};
 
 	gic: interrupt-controller at 1c81000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
 		#interrupt-cells = <3>;
 		#address-cells = <0>;
 
 		reg = <0x01c81000 0x1000>,
 		      <0x01c82000 0x2000>,
 		      <0x01c84000 0x2000>,
 		      <0x01c86000 0x2000>;
 		interrupts = <GIC_PPI 9
 		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupts = <GIC_PPI 13
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 14
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 11
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 10
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	clocks {
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		osc24M: osc24M_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <24000000>;
 			clock-output-names = "osc24M";
 		};
 
 		osc32k: osc32k_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <32768>;
 			clock-output-names = "osc32k";
 		};
 
 		pll1: pll1_clk at 1c20000 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-a23-pll1-clk";
 			reg = <0x01c20000 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll1";
 		};
 
 		pll6: pll6_clk at 1c20028 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c20028 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll6", "pll6x2";
 		};
 
 		pll6d2: pll6d2_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-factor-clock";
 			clock-div = <2>;
 			clock-mult = <1>;
 			clocks = <&pll6 0>;
 			clock-output-names = "pll6d2";
 		};
 
 		pll7: pll7_clk at 1c2002c {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c2002c 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll7", "pll7x2";
 		};
 
 		cpu: cpu_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-cpu-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
 			clock-output-names = "cpu";
 			critical-clocks = <0>;
 		};
 
 		axi: axi_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-axi-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&cpu>;
 			clock-output-names = "axi";
 		};
 
 		ahb1: ahb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun6i-a31-ahb1-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
 			clock-output-names = "ahb1";
 		};
 
 		ahb2: ahb2_clk at 1c2005c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-h3-ahb2-clk";
 			reg = <0x01c2005c 0x4>;
 			clocks = <&ahb1>, <&pll6d2>;
 			clock-output-names = "ahb2";
 		};
 
 		apb1: apb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb0-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&ahb1>;
 			clock-output-names = "apb1";
 		};
 
 		apb2: apb2_clk at 1c20058 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb1-clk";
 			reg = <0x01c20058 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>;
 			clock-output-names = "apb2";
 		};
 
 		bus_gates: bus_gates_clk at 1c20060 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun50i-a64-bus-gates-clk",
 				     "allwinner,sunxi-multi-bus-gates-clk";
 			reg = <0x01c20060 0x14>;
 			ahb1_parent {
 				clocks = <&ahb1>;
 				clock-indices = <1>, <5>,
 						<6>, <8>,
 						<9>, <10>,
 						<13>, <14>,
 						<18>, <19>,
 						<20>, <21>,
 						<23>, <24>,
 						<25>, <28>,
 						<32>, <35>,
 						<36>, <37>,
 						<40>, <43>,
 						<44>, <52>,
 						<53>, <54>,
 						<135>;
 				clock-output-names = "bus_mipidsi", "bus_ce",
 						"bus_dma", "bus_mmc0",
 						"bus_mmc1", "bus_mmc2",
 						"bus_nand", "bus_sdram",
 						"bus_ts", "bus_hstimer",
 						"bus_spi0", "bus_spi1",
 						"bus_otg", "bus_otg_ehci0",
 						"bus_ehci0", "bus_otg_ohci0",
 						"bus_ve", "bus_lcd0",
 						"bus_lcd1", "bus_deint",
 						"bus_csi", "bus_hdmi",
 						"bus_de", "bus_gpu",
 						"bus_msgbox", "bus_spinlock",
 						"bus_dbg";
 			};
 			ahb2_parent {
 				clocks = <&ahb2>;
 				clock-indices = <17>, <29>;
 				clock-output-names = "bus_gmac", "bus_ohci0";
 			};
 			apb1_parent {
 				clocks = <&apb1>;
 				clock-indices = <64>, <65>,
 						<69>, <72>,
 						<76>, <77>,
 						<78>;
 				clock-output-names = "bus_codec", "bus_spdif",
 						"bus_pio", "bus_ths",
 						"bus_i2s0", "bus_i2s1",
 						"bus_i2s2";
 			};
 			abp2_parent {
 				clocks = <&apb2>;
 				clock-indices = <96>, <97>,
 						<98>, <101>,
 						<112>, <113>,
 						<114>, <115>,
 						<116>;
 				clock-output-names = "bus_i2c0", "bus_i2c1",
 						"bus_i2c2", "bus_scr",
 						"bus_uart0", "bus_uart1",
 						"bus_uart2", "bus_uart3",
 						"bus_uart4";
 			};
 		};
 
 		mmc0_clk: mmc0_clk at 1c20088 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20088 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc0";
                 };
 
 		mmc1_clk: mmc1_clk at 1c2008c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c2008c 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc1";
 		};
 
 		mmc2_clk: mmc2_clk at 1c20090 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20090 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc2";
 		};
 	};
 
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		mmc0: mmc at 1c0f000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c0f000 0x1000>;
 			clocks = <&bus_gates 8>, <&mmc0_clk>,
 				 <&mmc0_clk>, <&mmc0_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 8>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc1: mmc at 1c10000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c10000 0x1000>;
 			clocks = <&bus_gates 9>, <&mmc1_clk>,
 				 <&mmc1_clk>, <&mmc1_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 9>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc2: mmc at 1c11000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c11000 0x1000>;
 			clocks = <&bus_gates 10>, <&mmc2_clk>,
 				 <&mmc2_clk>, <&mmc2_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 10>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		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>;
 
-			uart0_pins_a: uart0 at 0 {
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			/* The A64 does not have bank A and leaves a hole in the
+			   address space where it normally would be */
+
+			gpiob: gpiob at 24 {
+				compatible = "allwinner,sunxi-gpiobank";
+				allwinner,gpiobank-name = <'B'>;
+				reg = < 0x24 0x24 >, < 0x200 0x1c >;
+				interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+			};
+
+			gpioc: gpioc at 48 {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0x48 0x24 >;
+				allwinner,gpiobank-name = <'C'>;
+			};
+
+			gpiod: gpiod at 6c {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0x6c 0x24 >;
+				allwinner,gpiobank-name = <'D'>;
+			};
+
+			gpioe: gpioe at 90 {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0x90 0x24 >;
+				allwinner,gpiobank-name = <'E'>;
+			};
+
+			gpiof: gpiof at b4 {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0xb4 0x24 >;
+				allwinner,gpiobank-name = <'F'>;
+			};
+
+			gpiog: gpiog at d8 {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0xd8 0x24 >, < 0x220 0x1c >;
+				allwinner,gpiobank-name = <'G'>;
+				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+			};
+
+			gpioh: gpioh at fc {
+				compatible = "allwinner,sunxi-gpiobank";
+				reg = < 0xfc 0x24 >, < 0x220 0x1c >;
+				allwinner,gpiobank-name = <'H'>;
+				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
+			};
+
+			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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				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 {
 				allwinner,pins = "PF0", "PF1", "PF2", "PF3",
 						 "PF4", "PF5";
 				allwinner,function = "mmc0";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			mmc0_default_cd_pin: mmc0_cd_pin at 0 {
 				allwinner,pins = "PF6";
 				allwinner,function = "gpio_in";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_PULL_UP>;
 			};
 
 			mmc1_pins: mmc1 at 0 {
 				allwinner,pins = "PG0", "PG1", "PG2", "PG3",
 						 "PG4", "PG5";
 				allwinner,function = "mmc1";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			mmc2_pins: mmc2 at 0 {
 				allwinner,pins = "PC1", "PC5", "PC6", "PC8",
 						 "PC9", "PC10";
 				allwinner,function = "mmc2";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PH2", "PH3";
 				allwinner,function = "i2c1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PD10", "PD11", "PD13", "PD14",
 						 "PD17", "PD18", "PD19", "PD20",
 						 "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			rgmii_pins: rgmii_pins {
 				allwinner,pins = "PD8", "PD9", "PD10", "PD11",
 						 "PD12", "PD13", "PD15",
 						 "PD16", "PD17", "PD18", "PD19",
 						 "PD20", "PD21", "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 		};
 
 		r_pio: pinctrl at 01f02c00 {
 			compatible = "allwinner,sun50i-a64-r-pinctrl";
 			reg = <0x01f02c00 0x400>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+
 			gpio-controller;
-			interrupt-controller;
-			#interrupt-cells = <3>;
-			#size-cells = <0>;
 			#gpio-cells = <3>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+
+			#address-cells = <1>;
+			#size-cells = <1>;
+
+			gpiol: gpiol at 0 {
+				compatible = "allwinner,sunxi-gpiobank";
+				allwinner,gpiobank-name = <'L'>;
+				reg = < 0x0 0x24 >, < 0x200 0x1c >;
+				interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
+			};
 		};
 
 		ahb_rst: reset at 1c202c0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202c0 0xc>;
 		};
 
 		apb1_rst: reset at 1c202d0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d0 0x4>;
 		};
 
 		apb2_rst: reset at 1c202d8 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d8 0x4>;
 		};
 
 		uart0: serial at 1c28000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28000 0x400>;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 112>;
 			resets = <&apb2_rst 16>;
 			status = "disabled";
 		};
 
 		uart1: serial at 1c28400 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28400 0x400>;
 			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 113>;
 			resets = <&apb2_rst 17>;
 			status = "disabled";
 		};
 
 		uart2: serial at 1c28800 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28800 0x400>;
 			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 114>;
 			resets = <&apb2_rst 18>;
 			status = "disabled";
 		};
 
 		uart3: serial at 1c28c00 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28c00 0x400>;
 			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 115>;
 			resets = <&apb2_rst 19>;
 			status = "disabled";
 		};
 
 		uart4: serial at 1c29000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c29000 0x400>;
 			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 116>;
 			resets = <&apb2_rst 20>;
 			status = "disabled";
 		};
 
 		rtc: rtc at 1f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
 			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
 		i2c0: i2c at 1c2ac00 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2ac00 0x400>;
 			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 96>;
 			resets = <&apb2_rst 0>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c1: i2c at 1c2b000 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b000 0x400>;
 			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 97>;
 			resets = <&apb2_rst 1>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c2: i2c at 1c2b400 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b400 0x400>;
 			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 98>;
 			resets = <&apb2_rst 2>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		emac: ethernet at 01c30000 {
 			compatible = "allwinner,sun50i-a64-emac";
 			reg = <0x01c30000 0x2000>, <0x01c00030 0x4>;
 			reg-names = "emac", "syscon";
 			interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 			resets = <&ahb_rst 17>;
 			reset-names = "ahb";
 			clocks = <&bus_gates 17>;
 			clock-names = "ahb";
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		usbphy: phy at 1c1b810 {
 			compatible = "allwinner,sun50i-a64-usb-phy",
 				     "allwinner,sun8i-a33-usb-phy";
 			reg = <0x01c1b810 0x14>, <0x01c1b800 0x4>;
 			reg-names = "phy_ctrl", "pmu1";
 			status = "disabled";
 			#phy-cells = <1>;
 		};
 
 		ehci1: usb at 01c1b000 {
 			compatible = "allwinner,sun50i-a64-ehci",
 				     "generic-ehci";
 			reg = <0x01c1b000 0x100>;
 			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "disabled";
 		};
 
 		ohci1: usb at 01c1b400 {
 			compatible = "allwinner,sun50i-a64-ohci",
 				     "generic-ohci";
 			reg = <0x01c1b400 0x100>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "enabled";
 		};
 	};
 };
-- 
1.9.1

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

* [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (2 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 03/11] sun50i: dts: add gpiobank nodes to the pinctrl nodes Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-21 20:15   ` Maxime Ripard
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi Philipp Tomsich
                   ` (6 subsequent siblings)
  10 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 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 | 30 +++++++++++++++---------------
 1 file changed, 15 insertions(+), 15 deletions(-)

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index efed838..d592bf2 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -1,762 +1,762 @@
 /*
  * Copyright (C) 2016 ARM Ltd.
  * based on the Allwinner H3 dtsi:
  *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
  *
  * This file is dual-licensed: you can use it either under the terms
  * of the GPL or the X11 license, at your option. Note that this dual
  * licensing only applies to this file, and not this project as a
  * whole.
  *
  *  a) This file is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License as
  *     published by the Free Software Foundation; either version 2 of the
  *     License, or (at your option) any later version.
  *
  *     This file is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     GNU General Public License for more details.
  *
  * Or, alternatively,
  *
  *  b) Permission is hereby granted, free of charge, to any person
  *     obtaining a copy of this software and associated documentation
  *     files (the "Software"), to deal in the Software without
  *     restriction, including without limitation the rights to use,
  *     copy, modify, merge, publish, distribute, sublicense, and/or
  *     sell copies of the Software, and to permit persons to whom the
  *     Software is furnished to do so, subject to the following
  *     conditions:
  *
  *     The above copyright notice and this permission notice shall be
  *     included in all copies or substantial portions of the Software.
  *
  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  *     OTHER DEALINGS IN THE SOFTWARE.
  */
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
 
 / {
 	interrupt-parent = <&gic>;
 	#address-cells = <1>;
 	#size-cells = <1>;
 
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
 
 		cpu at 0 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <0>;
 			enable-method = "psci";
 		};
 
 		cpu at 1 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <1>;
 			enable-method = "psci";
 		};
 
 		cpu at 2 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <2>;
 			enable-method = "psci";
 		};
 
 		cpu at 3 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <3>;
 			enable-method = "psci";
 		};
 	};
 
 	psci {
 		compatible = "arm,psci-0.2";
 		method = "smc";
 	};
 
-	memory {
+	memory: memory at 40000000 {
 		device_type = "memory";
 		reg = <0x40000000 0>;
 	};
 
 	gic: interrupt-controller at 1c81000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
 		#interrupt-cells = <3>;
 		#address-cells = <0>;
 
 		reg = <0x01c81000 0x1000>,
 		      <0x01c82000 0x2000>,
 		      <0x01c84000 0x2000>,
 		      <0x01c86000 0x2000>;
 		interrupts = <GIC_PPI 9
 		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupts = <GIC_PPI 13
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 14
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 11
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 10
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	clocks {
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		osc24M: osc24M_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <24000000>;
 			clock-output-names = "osc24M";
 		};
 
 		osc32k: osc32k_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <32768>;
 			clock-output-names = "osc32k";
 		};
 
 		pll1: pll1_clk at 1c20000 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-a23-pll1-clk";
 			reg = <0x01c20000 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll1";
 		};
 
 		pll6: pll6_clk at 1c20028 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c20028 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll6", "pll6x2";
 		};
 
 		pll6d2: pll6d2_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-factor-clock";
 			clock-div = <2>;
 			clock-mult = <1>;
 			clocks = <&pll6 0>;
 			clock-output-names = "pll6d2";
 		};
 
 		pll7: pll7_clk at 1c2002c {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c2002c 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll7", "pll7x2";
 		};
 
 		cpu: cpu_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-cpu-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
 			clock-output-names = "cpu";
 			critical-clocks = <0>;
 		};
 
 		axi: axi_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-axi-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&cpu>;
 			clock-output-names = "axi";
 		};
 
 		ahb1: ahb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun6i-a31-ahb1-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
 			clock-output-names = "ahb1";
 		};
 
 		ahb2: ahb2_clk at 1c2005c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-h3-ahb2-clk";
 			reg = <0x01c2005c 0x4>;
 			clocks = <&ahb1>, <&pll6d2>;
 			clock-output-names = "ahb2";
 		};
 
 		apb1: apb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb0-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&ahb1>;
 			clock-output-names = "apb1";
 		};
 
 		apb2: apb2_clk at 1c20058 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb1-clk";
 			reg = <0x01c20058 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>;
 			clock-output-names = "apb2";
 		};
 
 		bus_gates: bus_gates_clk at 1c20060 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun50i-a64-bus-gates-clk",
 				     "allwinner,sunxi-multi-bus-gates-clk";
 			reg = <0x01c20060 0x14>;
 			ahb1_parent {
 				clocks = <&ahb1>;
 				clock-indices = <1>, <5>,
 						<6>, <8>,
 						<9>, <10>,
 						<13>, <14>,
 						<18>, <19>,
 						<20>, <21>,
 						<23>, <24>,
 						<25>, <28>,
 						<32>, <35>,
 						<36>, <37>,
 						<40>, <43>,
 						<44>, <52>,
 						<53>, <54>,
 						<135>;
 				clock-output-names = "bus_mipidsi", "bus_ce",
 						"bus_dma", "bus_mmc0",
 						"bus_mmc1", "bus_mmc2",
 						"bus_nand", "bus_sdram",
 						"bus_ts", "bus_hstimer",
 						"bus_spi0", "bus_spi1",
 						"bus_otg", "bus_otg_ehci0",
 						"bus_ehci0", "bus_otg_ohci0",
 						"bus_ve", "bus_lcd0",
 						"bus_lcd1", "bus_deint",
 						"bus_csi", "bus_hdmi",
 						"bus_de", "bus_gpu",
 						"bus_msgbox", "bus_spinlock",
 						"bus_dbg";
 			};
 			ahb2_parent {
 				clocks = <&ahb2>;
 				clock-indices = <17>, <29>;
 				clock-output-names = "bus_gmac", "bus_ohci0";
 			};
 			apb1_parent {
 				clocks = <&apb1>;
 				clock-indices = <64>, <65>,
 						<69>, <72>,
 						<76>, <77>,
 						<78>;
 				clock-output-names = "bus_codec", "bus_spdif",
 						"bus_pio", "bus_ths",
 						"bus_i2s0", "bus_i2s1",
 						"bus_i2s2";
 			};
 			abp2_parent {
 				clocks = <&apb2>;
 				clock-indices = <96>, <97>,
 						<98>, <101>,
 						<112>, <113>,
 						<114>, <115>,
 						<116>;
 				clock-output-names = "bus_i2c0", "bus_i2c1",
 						"bus_i2c2", "bus_scr",
 						"bus_uart0", "bus_uart1",
 						"bus_uart2", "bus_uart3",
 						"bus_uart4";
 			};
 		};
 
 		mmc0_clk: mmc0_clk at 1c20088 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20088 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc0";
                 };
 
 		mmc1_clk: mmc1_clk at 1c2008c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c2008c 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc1";
 		};
 
 		mmc2_clk: mmc2_clk at 1c20090 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20090 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc2";
 		};
 	};
 
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		mmc0: mmc at 1c0f000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c0f000 0x1000>;
 			clocks = <&bus_gates 8>, <&mmc0_clk>,
 				 <&mmc0_clk>, <&mmc0_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 8>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc1: mmc at 1c10000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c10000 0x1000>;
 			clocks = <&bus_gates 9>, <&mmc1_clk>,
 				 <&mmc1_clk>, <&mmc1_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 9>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc2: mmc at 1c11000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c11000 0x1000>;
 			clocks = <&bus_gates 10>, <&mmc2_clk>,
 				 <&mmc2_clk>, <&mmc2_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 10>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		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>;
 
 			/* The A64 does not have bank A and leaves a hole in the
 			   address space where it normally would be */
 
 			gpiob: gpiob at 24 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'B'>;
 				reg = < 0x24 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioc: gpioc at 48 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x48 0x24 >;
 				allwinner,gpiobank-name = <'C'>;
 			};
 
 			gpiod: gpiod at 6c {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x6c 0x24 >;
 				allwinner,gpiobank-name = <'D'>;
 			};
 
 			gpioe: gpioe at 90 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x90 0x24 >;
 				allwinner,gpiobank-name = <'E'>;
 			};
 
 			gpiof: gpiof at b4 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xb4 0x24 >;
 				allwinner,gpiobank-name = <'F'>;
 			};
 
 			gpiog: gpiog at d8 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xd8 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'G'>;
 				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioh: gpioh at fc {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xfc 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'H'>;
 				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			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";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				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";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				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";
 				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PH2", "PH3";
 				allwinner,function = "i2c1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PD10", "PD11", "PD13", "PD14",
 						 "PD17", "PD18", "PD19", "PD20",
 						 "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			rgmii_pins: rgmii_pins {
 				allwinner,pins = "PD8", "PD9", "PD10", "PD11",
 						 "PD12", "PD13", "PD15",
 						 "PD16", "PD17", "PD18", "PD19",
 						 "PD20", "PD21", "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 		};
 
 		r_pio: pinctrl at 01f02c00 {
 			compatible = "allwinner,sun50i-a64-r-pinctrl";
 			reg = <0x01f02c00 0x400>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 
 			gpio-controller;
 			#gpio-cells = <3>;
 
 			interrupt-controller;
 			#interrupt-cells = <2>;
 
 			#address-cells = <1>;
 			#size-cells = <1>;
 
 			gpiol: gpiol at 0 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'L'>;
 				reg = < 0x0 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 			};
 		};
 
 		ahb_rst: reset at 1c202c0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202c0 0xc>;
 		};
 
 		apb1_rst: reset at 1c202d0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d0 0x4>;
 		};
 
 		apb2_rst: reset at 1c202d8 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d8 0x4>;
 		};
 
 		uart0: serial at 1c28000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28000 0x400>;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 112>;
 			resets = <&apb2_rst 16>;
 			status = "disabled";
 		};
 
 		uart1: serial at 1c28400 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28400 0x400>;
 			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 113>;
 			resets = <&apb2_rst 17>;
 			status = "disabled";
 		};
 
 		uart2: serial at 1c28800 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28800 0x400>;
 			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 114>;
 			resets = <&apb2_rst 18>;
 			status = "disabled";
 		};
 
 		uart3: serial at 1c28c00 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28c00 0x400>;
 			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 115>;
 			resets = <&apb2_rst 19>;
 			status = "disabled";
 		};
 
 		uart4: serial at 1c29000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c29000 0x400>;
 			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 116>;
 			resets = <&apb2_rst 20>;
 			status = "disabled";
 		};
 
 		rtc: rtc at 1f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
 			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
 		i2c0: i2c at 1c2ac00 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2ac00 0x400>;
 			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 96>;
 			resets = <&apb2_rst 0>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c1: i2c at 1c2b000 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b000 0x400>;
 			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 97>;
 			resets = <&apb2_rst 1>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c2: i2c at 1c2b400 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b400 0x400>;
 			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 98>;
 			resets = <&apb2_rst 2>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		emac: ethernet at 01c30000 {
 			compatible = "allwinner,sun50i-a64-emac";
 			reg = <0x01c30000 0x2000>, <0x01c00030 0x4>;
 			reg-names = "emac", "syscon";
 			interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 			resets = <&ahb_rst 17>;
 			reset-names = "ahb";
 			clocks = <&bus_gates 17>;
 			clock-names = "ahb";
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		usbphy: phy at 1c1b810 {
 			compatible = "allwinner,sun50i-a64-usb-phy",
 				     "allwinner,sun8i-a33-usb-phy";
 			reg = <0x01c1b810 0x14>, <0x01c1b800 0x4>;
 			reg-names = "phy_ctrl", "pmu1";
 			status = "disabled";
 			#phy-cells = <1>;
 		};
 
 		ehci1: usb at 01c1b000 {
 			compatible = "allwinner,sun50i-a64-ehci",
 				     "generic-ehci";
 			reg = <0x01c1b000 0x100>;
 			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "disabled";
 		};
 
 		ohci1: usb at 01c1b400 {
 			compatible = "allwinner,sun50i-a64-ohci",
 				     "generic-ohci";
 			reg = <0x01c1b400 0x100>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "enabled";
 		};
 	};
 };
-- 
1.9.1

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

* [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (3 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-21 20:16   ` Maxime Ripard
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 06/11] sunxi: add clock driver (UCLASS_CLK) " Philipp Tomsich
                   ` (5 subsequent siblings)
  10 siblings, 1 reply; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

In order to have the device model describe the module reset bits
on sunxi (well, at least for anything newer than sun6i), we need
a (rather simple) driver for 'allwinner,sun6i-a31-clock-reset'
nodes.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/reset/Kconfig       |   9 ++++
 drivers/reset/Makefile      |   1 +
 drivers/reset/reset-sunxi.c | 107 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 117 insertions(+)
 create mode 100644 drivers/reset/reset-sunxi.c

diff --git a/drivers/reset/Kconfig b/drivers/reset/Kconfig
index c42b0bc..8db25fc 100644
--- a/drivers/reset/Kconfig
+++ b/drivers/reset/Kconfig
@@ -43,4 +43,13 @@ config RESET_UNIPHIER
 	  Say Y if you want to control reset signals provided by System Control
 	  block, Media I/O block, Peripheral Block.
 
+config RESET_SUNXI
+        bool "Reset controller driver for Allwiner SoCs"
+	depends on DM_RESET && ARCH_SUNXI
+	default y
+	help
+	  Support for reset controllers on Allwinner SoCs.
+	  Say Y if you want to control reset signals provided by CCU (e.g. sun50i)
+	  or PRCM (e.g. sun6i, sun9i) blocks.
+
 endmenu
diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 5c4305c..0086da9 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -1,10 +1,11 @@
 # Copyright (c) 2016, NVIDIA CORPORATION.
 #
 # SPDX-License-Identifier: GPL-2.0
 
 obj-$(CONFIG_DM_RESET) += reset-uclass.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset.o
 obj-$(CONFIG_SANDBOX_MBOX) += sandbox-reset-test.o
 obj-$(CONFIG_TEGRA_CAR_RESET) += tegra-car-reset.o
 obj-$(CONFIG_TEGRA186_RESET) += tegra186-reset.o
 obj-$(CONFIG_RESET_UNIPHIER) += reset-uniphier.o
+obj-$(CONFIG_RESET_SUNXI) += reset-sunxi.o
diff --git a/drivers/reset/reset-sunxi.c b/drivers/reset/reset-sunxi.c
new file mode 100644
index 0000000..b667ca1
--- /dev/null
+++ b/drivers/reset/reset-sunxi.c
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <common.h>
+#include <reset-uclass.h>
+#include <dm/device.h>
+#include <linux/bitops.h>
+#include <linux/io.h>
+#include <linux/sizes.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_reset_priv {
+	void __iomem *base;
+	size_t  size;
+};
+
+static int sunxi_reset_request(struct reset_ctl *reset_ctl)
+{
+	debug("%s (%s): id %ld\n",
+	      reset_ctl->dev->name, __func__, reset_ctl->id);
+	return 0;
+}
+
+static int sunxi_reset_free(struct reset_ctl *reset_ctl)
+{
+	debug("%s (%s): id %ld\n",
+	      reset_ctl->dev->name, __func__, reset_ctl->id);
+	return 0;
+}
+
+static int sunxi_reset_update(struct reset_ctl *reset_ctl, bool assert)
+{
+	struct sunxi_reset_priv *priv = dev_get_priv(reset_ctl->dev);
+	unsigned long id = reset_ctl->id;
+	unsigned long offset = id / 32; /* TODO: symbolic name */
+	unsigned int bit = id % 32;
+
+	debug("%s (%s): id %ld base %p offset %lx bit %d assert %d size %ld\n",
+	      reset_ctl->dev->name, __func__, id, priv->base, offset,
+	      bit, assert, priv->size);
+
+	if (offset >= priv->size)
+		return -EINVAL;
+
+	if (assert)
+		clrbits_le32(priv->base + offset, BIT(bit));
+	else
+		setbits_le32(priv->base + offset, BIT(bit));
+
+	return 0;
+}
+
+static int sunxi_reset_assert(struct reset_ctl *reset_ctl)
+{
+	return sunxi_reset_update(reset_ctl, true);
+}
+
+static int sunxi_reset_deassert(struct reset_ctl *reset_ctl)
+{
+	return sunxi_reset_update(reset_ctl, false);
+}
+
+static const struct reset_ops sunxi_reset_ops = {
+	.request = sunxi_reset_request,
+	.free = sunxi_reset_free,
+	.rst_assert = sunxi_reset_assert,
+	.rst_deassert = sunxi_reset_deassert,
+};
+
+static int sunxi_reset_probe(struct udevice *dev)
+{
+	struct sunxi_reset_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+						  "reg", 0, &size, false);
+	if (addr == FDT_ADDR_T_NONE) {
+		debug("%s: failed to find base address ('reg')\n", dev->name);
+		return -ENODEV;
+	}
+	priv->base = (void *)addr;
+	priv->size = size;
+
+	if (!priv->base)
+		return -ENOMEM;
+
+	return 0;
+}
+
+static const struct udevice_id sunxi_reset_match[] = {
+	{ .compatible = "allwinner,sun6i-a31-clock-reset" },
+	{ }
+};
+
+U_BOOT_DRIVER(sunxi_reset) = {
+	.name = "sunxi-reset",
+	.id = UCLASS_RESET,
+	.of_match = sunxi_reset_match,
+	.ops = &sunxi_reset_ops,
+	.priv_auto_alloc_size = sizeof(struct sunxi_reset_priv),
+	.probe = sunxi_reset_probe,
+};
-- 
1.9.1

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

* [U-Boot] [PATCH v1 06/11] sunxi: add clock driver (UCLASS_CLK) support for sunxi
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (4 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 07/11] sunxi: Scan DT tree node '/clocks' on sunxi boards Philipp Tomsich
                   ` (4 subsequent siblings)
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

When CONFIG_CLK is defined, we now provide support for the basic
clock configuration of peripherals on sunxi:
 * clk-sunxi-mod.c implements support for module clocks, which
     performs parent selection (determined via the device-tree)
     and determines/configures a pre-divider and divider when
     setting a clock-rate
 * clk-sunxi-pll.c implements read-only (i.e. getting the rate
     is implemented, but setting the rate is not) access to
     PLLs.  At this time, the necessary platform data (number
     of bits and position for the factors P, M, N and K) for
     the peripheral PLLs ('allwinner,sun6i-a31-pll6-clk') are
     included
 * clk-sunxi-gate.c: implements an clk-gate to gate individual
     modules (i.e. 'allwinner,sunxi-multi-bus-gates-clk')

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/clk/Makefile               |   1 +
 drivers/clk/sunxi/Makefile         |   7 ++
 drivers/clk/sunxi/clk-sunxi-gate.c |  92 ++++++++++++++
 drivers/clk/sunxi/clk-sunxi-mod.c  | 241 +++++++++++++++++++++++++++++++++++++
 drivers/clk/sunxi/clk-sunxi-pll.c  | 123 +++++++++++++++++++
 5 files changed, 464 insertions(+)
 create mode 100644 drivers/clk/sunxi/Makefile
 create mode 100644 drivers/clk/sunxi/clk-sunxi-gate.c
 create mode 100644 drivers/clk/sunxi/clk-sunxi-mod.c
 create mode 100644 drivers/clk/sunxi/clk-sunxi-pll.c

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 884c21c..7ae8029 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -7,6 +7,7 @@
 
 obj-$(CONFIG_CLK) += clk-uclass.o clk_fixed_rate.o
 obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
+obj-$(CONFIG_ARCH_SUNXI) += sunxi/
 obj-$(CONFIG_SANDBOX) += clk_sandbox.o
 obj-$(CONFIG_SANDBOX) += clk_sandbox_test.o
 obj-$(CONFIG_MACH_PIC32) += clk_pic32.o
diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile
new file mode 100644
index 0000000..e37768cdd
--- /dev/null
+++ b/drivers/clk/sunxi/Makefile
@@ -0,0 +1,7 @@
+#
+# SPDX-License-Identifier:	GPL-2.0+
+#
+
+obj-y += clk-sunxi-pll.o
+obj-y += clk-sunxi-mod.o
+obj-y += clk-sunxi-gate.o
\ No newline at end of file
diff --git a/drivers/clk/sunxi/clk-sunxi-gate.c b/drivers/clk/sunxi/clk-sunxi-gate.c
new file mode 100644
index 0000000..956ba8e
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi-gate.c
@@ -0,0 +1,92 @@
+/*
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <div64.h>
+#include <wait_bit.h>
+#include <dm/lists.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_clk_priv {
+	void   *base;
+	size_t  size;
+};
+
+static int sunxi_gate_update(struct clk *clk, bool enable)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+	unsigned long id = clk->id;
+	uint32_t offset = id / 32;
+	uint32_t bit = id % 32;
+
+	debug("%s (%s): id %ld base %p offset %x bit %d enable %d\n",
+	      clk->dev->name, __func__, id, priv->base, offset,
+	      bit, enable);
+
+	if (enable)
+		setbits_le32(priv->base + offset, BIT(bit));
+	else
+		clrbits_le32(priv->base + offset, BIT(bit));
+
+	return -EINVAL;
+}
+
+static int sunxi_gate_enable(struct clk *clk)
+{
+	return sunxi_gate_update(clk, true);
+}
+
+static int sunxi_gate_disable(struct clk *clk)
+{
+	return sunxi_gate_update(clk, false);
+}
+
+static struct clk_ops sunxi_clk_gate_ops = {
+	.enable = sunxi_gate_enable,
+	.disable = sunxi_gate_disable,
+};
+
+static int sunxi_clk_gate_probe(struct udevice *dev)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+						  "reg", 0, &size, false);
+	if (addr == FDT_ADDR_T_NONE) {
+		debug("%s: could not get addr\n", dev->name);
+		return -EINVAL;
+	}
+
+	priv->base = (void *)addr;
+	priv->size = size;
+
+	return 0;
+}
+
+static const struct udevice_id sunxi_clk_gate_ids[] = {
+	{ .compatible = "allwinner,sunxi-multi-bus-gates-clk" },
+	{}
+};
+
+U_BOOT_DRIVER(sunxi_clk_gate) = {
+	.name		= "sunxi_clk_gate",
+	.id		= UCLASS_CLK,
+	.of_match	= sunxi_clk_gate_ids,
+	.ops		= &sunxi_clk_gate_ops,
+	.probe		= sunxi_clk_gate_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_clk_priv),
+};
+
+
diff --git a/drivers/clk/sunxi/clk-sunxi-mod.c b/drivers/clk/sunxi/clk-sunxi-mod.c
new file mode 100644
index 0000000..4e70cc9
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi-mod.c
@@ -0,0 +1,241 @@
+/*
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * With sun4i_a10_get_mod0_factors(...) adapted from
+ *    linux/drivers/clk/sunxi/clk-mod0.c
+ * which is
+ *    Copyright 2013 Emilio L?pez
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <div64.h>
+#include <wait_bit.h>
+#include <dm/lists.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_clk_priv {
+	void         *reg;
+	int           num_parents;
+	struct clk    parent[4];
+};
+
+#define SRCSHIFT    (24)
+#define SRCMASK     (0x3 << SRCSHIFT)
+#define SRC(n)      (n << SRCSHIFT)
+#define PREDIVMASK  (0x3 << 16)
+#define PREDIV(n)   (n << 16)
+#define DIVMASK     (0xf << 0)
+#define DIV(n)      (n)
+
+static ulong sunxi_mod_get_rate(struct clk *clk)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+	u32 active_parent;
+	ulong rate = -EINVAL;
+	u32 regval = readl(priv->reg);
+
+	/* if not enabled, return 0 */
+	if (regval & BIT(31))
+		return 0;
+
+	active_parent = (readl(priv->reg) >> 24) & 0x3;
+	if (active_parent < priv->num_parents)
+		rate = clk_get_rate(&priv->parent[active_parent]);
+
+	return rate;
+}
+
+/**
+ * sun4i_a10_get_mod0_factors()
+ *  - calculates m, n factors for MOD0-style clocks
+ *
+ * MOD0 rate is calculated as follows:
+ *    rate = (parent_rate >> p) / (m + 1);
+ */
+
+struct factors_request {
+	unsigned long rate;
+	unsigned long parent_rate;
+	u8 parent_index;
+	u8 n;
+	u8 k;
+	u8 m;
+	u8 p;
+};
+
+static void sun4i_a10_get_mod0_factors(struct factors_request *req)
+{
+	u8 div, calcm, calcp;
+
+	/* These clocks can only divide, so we will never be able to
+	 * achieve frequencies higher than the parent frequency */
+	if (req->rate >= req->parent_rate) {
+		req->rate = req->parent_rate;
+		req->m = 0;
+		req->p = 0;
+	}
+
+	div = DIV_ROUND_UP(req->parent_rate, req->rate);
+
+	if (div < 16)
+		calcp = 0;
+	else if (div / 2 < 16)
+		calcp = 1;
+	else if (div / 4 < 16)
+		calcp = 2;
+	else
+		calcp = 3;
+
+	calcm = DIV_ROUND_UP(div, 1 << calcp);
+	/* clamp calcm to 16, as that is the largest possible divider */
+	if (calcm > 16)
+		calcm = 16;
+
+	req->rate = (req->parent_rate >> calcp) / calcm;
+	req->m = calcm - 1;
+	req->p = calcp;
+}
+
+static ulong sunxi_mod_set_rate(struct clk *clk, ulong rate)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+	ulong best_rate = 0;
+	int i;
+
+	debug("%s (%s): id %ld rate %ld base %p\n",
+	      clk->dev->name, __func__, clk->id, rate, priv->reg);
+
+	/* check if the current rate is already the target rate */
+	if (sunxi_mod_get_rate(clk) == rate)
+		return rate;
+
+	/* find the parent (iterate through) which allows us to have:
+	 *     fastest rate <= rate
+	 */
+	for (i = 0; i < priv->num_parents; ++i) {
+		ulong parent_rate = clk_get_rate(&priv->parent[i]);
+		struct factors_request  req = {
+			.rate = rate,
+			.parent_rate = parent_rate,
+			.parent_index = i
+		};
+
+		debug("%s (%s): parent %d rate %ld\n",
+		      clk->dev->name, __func__, i, parent_rate);
+
+		if (parent_rate == -ENOSYS) {
+			debug("%s: parent %d does not support get_rate\n",
+			      clk->dev->name, i);
+			continue;
+		}
+
+		if (parent_rate == 0) {
+			debug("%s: parent %d seems disabled (rate == 0)\n",
+			      clk->dev->name, i);
+			continue;
+		}
+
+		/* We recalculate the dividers, even if the parent's
+		 * rate is less than the requested rate
+		 */
+		sun4i_a10_get_mod0_factors(&req);
+
+		if (req.rate > rate) {
+			debug("%s: rate %ld for parent %i exceeds rate\n",
+			      clk->dev->name, req.rate, i);
+			continue;
+		}
+
+		if (req.rate > best_rate) {
+			debug("%s: new best => parent %d P %d M %d rate %ld\n",
+			      clk->dev->name, i, req.p, req.m, req.rate);
+
+			clrsetbits_le32(priv->reg,
+					SRCMASK | PREDIVMASK | DIVMASK,
+					SRC(i) | PREDIV(req.p) | DIV(req.m));
+			best_rate = req.rate;
+
+			/* don't continue, if this is the requested rate */
+			if (best_rate == rate)
+				break;
+		}
+	}
+
+	return best_rate;
+}
+
+static int sunxi_mod_enable(struct clk *clk)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+
+	setbits_le32(priv->reg, BIT(31));
+	return 0;
+}
+
+static int sunxi_mod_disable(struct clk *clk)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+
+	clrbits_le32(priv->reg, BIT(31));
+	return 0;
+}
+
+static struct clk_ops sunxi_clk_mod_ops = {
+	.set_rate = sunxi_mod_set_rate,
+	.get_rate = sunxi_mod_get_rate,
+	.enable = sunxi_mod_enable,
+	.disable = sunxi_mod_disable,
+};
+
+static int sunxi_clk_mod_probe(struct udevice *dev)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+	int i;
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+						  "reg", 0, &size, false);
+	if (addr == FDT_ADDR_T_NONE) {
+		debug("%s: could not get addr\n", dev->name);
+		return -EINVAL;
+	}
+
+	priv->reg = (void *)addr;
+
+	for (i = 0; i < 4; ++i) {
+		int ret = clk_get_by_index(dev, i, &priv->parent[i]);
+		if (ret != 0)
+			break;
+	};
+	priv->num_parents = i;
+
+	debug("%s: reg %p num-parents %d\n",
+	      dev->name, priv->reg, priv->num_parents);
+	return 0;
+}
+
+static const struct udevice_id sunxi_clk_mod_ids[] = {
+	{ .compatible = "allwinner,sun4i-a10-mod0-clk" },
+	{}
+};
+
+U_BOOT_DRIVER(sunxi_clk_mod) = {
+	.name		= "sunxi_clk_mod",
+	.id		= UCLASS_CLK,
+	.of_match	= sunxi_clk_mod_ids,
+	.ops		= &sunxi_clk_mod_ops,
+	.probe		= sunxi_clk_mod_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_clk_priv),
+};
+
+
diff --git a/drivers/clk/sunxi/clk-sunxi-pll.c b/drivers/clk/sunxi/clk-sunxi-pll.c
new file mode 100644
index 0000000..7c82ccf
--- /dev/null
+++ b/drivers/clk/sunxi/clk-sunxi-pll.c
@@ -0,0 +1,123 @@
+/*
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ *
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <dm.h>
+#include <div64.h>
+#include <wait_bit.h>
+#include <dm/lists.h>
+#include <asm/io.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_clk_pll_data {
+	u8 nshift;
+	u8 nwidth;
+	u8 kshift;
+	u8 kwidth;
+	u8 mshift;
+	u8 mwidth;
+	u8 pshift;
+	u8 pwidth;
+	u8 n_start;
+};
+
+struct sunxi_clk_priv {
+	void *reg;
+};
+
+static inline uint32_t bitmask(uint8_t width, uint8_t shift)
+{
+	return ((1U << width) - 1) << shift;
+}
+
+static inline uint32_t factor_extract(uint32_t regval,
+				      uint8_t width,
+				      uint8_t shift)
+{
+	return ((regval & bitmask(width, shift)) >> shift) + 1;
+}
+
+static ulong sunxi_pll_get_rate(struct clk *clk)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(clk->dev);
+	struct sunxi_clk_pll_data *data =
+		(struct sunxi_clk_pll_data *)dev_get_driver_data(clk->dev);
+	uint32_t regval = readl(priv->reg);
+	int n = factor_extract(regval, data->nwidth, data->nshift);
+	int k = factor_extract(regval, data->kwidth, data->kshift);
+	int m = factor_extract(regval, data->mwidth, data->mshift);
+	ulong rate = (24000000 * n * k) / m;
+
+	debug("%s (%s): id %ld base %p\n",
+	      clk->dev->name, __func__, clk->id, priv->reg);
+
+	/* Check if the PLL is enabled... */
+	if (!(regval & BIT(31)))
+		return 0;
+
+	debug("%s: n %d k %d m %d\n", clk->dev->name, n, k, m);
+	debug("%s: rate %ld\n", clk->dev->name, rate);
+
+	if (clk->id == 1)
+		return 2 * rate;
+
+	return rate;
+}
+
+static struct clk_ops sunxi_clk_pll_ops = {
+	/* For now, we'll let the arch/board-specific code setup the
+	   PLLs through the legacy implementation (some of this will
+	   happen in SPL, which may not have device model capability)
+	   and we only read the PLL rates. */
+	.get_rate = sunxi_pll_get_rate,
+};
+
+static int sunxi_clk_pll_probe(struct udevice *dev)
+{
+	struct sunxi_clk_priv *priv = dev_get_priv(dev);
+	fdt_addr_t addr;
+	fdt_size_t size;
+
+	addr = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob, dev->of_offset,
+						  "reg", 0, &size, false);
+	if (addr == FDT_ADDR_T_NONE)
+		return -EINVAL;
+
+	priv->reg = (void *)addr;
+	if (!priv->reg)
+		return -EINVAL;
+
+	debug("%s: reg %p\n", dev->name, priv->reg);
+
+	return 0;
+}
+
+static struct sunxi_clk_pll_data pll6_data = {
+	.nwidth = 5,
+	.nshift = 8,
+	.kwidth = 2,
+	.kshift = 4,
+	.mwidth = 2,
+	.mshift = 0,
+};
+
+static const struct udevice_id sunxi_clk_pll_ids[] = {
+	{ .compatible = "allwinner,sun6i-a31-pll6-clk",
+	  .data = (uintptr_t)&pll6_data },
+	{}
+};
+
+U_BOOT_DRIVER(sunxi_clk_pll) = {
+	.name		= "sunxi_clk_pll",
+	.id		= UCLASS_CLK,
+	.of_match	= sunxi_clk_pll_ids,
+	.ops		= &sunxi_clk_pll_ops,
+	.probe		= sunxi_clk_pll_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_clk_priv),
+};
-- 
1.9.1

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

* [U-Boot] [PATCH v1 07/11] sunxi: Scan DT tree node '/clocks' on sunxi boards
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (5 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 06/11] sunxi: add clock driver (UCLASS_CLK) " Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 08/11] sun8i_emac: update to work with pinctrl-sunxi, reset-sunxi and clk-sunxi Philipp Tomsich
                   ` (3 subsequent siblings)
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

Moving to a DT-based clock framework on sunxi requires the subnodes
of /clocks to be bound on boot-up.  As /clocks does not contain a
compatible-string, the U-Boot DT parsing code ignores it (and any
subnodes).  To overcome this limitation, the sunxi board-init code
retrieves the /clocks node and explicitly starts a DM scan of the
subnodes.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 board/sunxi/board.c | 13 ++++++++++++-
 1 file changed, 12 insertions(+), 1 deletion(-)

diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 89edf2e..838e89f 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -1,39 +1,40 @@
 /*
  * (C) Copyright 2012-2013 Henrik Nordstrom <henrik@henriknordstrom.net>
  * (C) Copyright 2013 Luke Kenneth Casson Leighton <lkcl@lkcl.net>
  *
  * (C) Copyright 2007-2011
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  * Tom Cubie <tangliang@allwinnertech.com>
  *
  * Some board init for the Allwinner A10-evb board.
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #include <common.h>
 #include <mmc.h>
 #include <axp_pmic.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/display.h>
 #include <asm/arch/dram.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/mmc.h>
 #include <asm/arch/spl.h>
 #include <asm/arch/usb_phy.h>
 #ifndef CONFIG_ARM64
 #include <asm/armv7.h>
 #endif
 #include <asm/gpio.h>
 #include <asm/io.h>
 #include <crc.h>
+#include <dm/root.h>
 #include <environment.h>
 #include <libfdt.h>
 #include <fdtdec.h>
 #include <led.h>
 #include <nand.h>
 #include <net.h>
 #include <sy8106a.h>
 #include <command.h>
 
@@ -109,76 +110,87 @@ static int setup_led(void)
 int board_init(void)
 {
 	__maybe_unused int id_pfr1, ret;
+	int __maybe_unused offset;
 
 	gd->bd->bi_boot_params = (PHYS_SDRAM_0 + 0x100);
 
+#if defined(CONFIG_CLK)
+	/* Sunxi device trees have their clock definitions in a tree
+	 * below /clocks, which is a node without a compatible-string.
+	 * We need to manually locate it and scan its subnodes.
+	 */
+	offset = fdt_path_offset(gd->fdt_blob, "/clocks");
+	if (offset > 0)
+		dm_scan_fdt_node(gd->dm_root, gd->fdt_blob, offset, false);
+#endif
+
 #ifndef CONFIG_ARM64
 	asm volatile("mrc p15, 0, %0, c0, c1, 1" : "=r"(id_pfr1));
 	debug("id_pfr1: 0x%08x\n", id_pfr1);
 	/* Generic Timer Extension available? */
 	if ((id_pfr1 >> CPUID_ARM_GENTIMER_SHIFT) & 0xf) {
 		uint32_t freq;
 
 		debug("Setting CNTFRQ\n");
 
 		/*
 		 * CNTFRQ is a secure register, so we will crash if we try to
 		 * write this from the non-secure world (read is OK, though).
 		 * In case some bootcode has already set the correct value,
 		 * we avoid the risk of writing to it.
 		 */
 		asm volatile("mrc p15, 0, %0, c14, c0, 0" : "=r"(freq));
 		if (freq != CONFIG_TIMER_CLK_FREQ) {
 			debug("arch timer frequency is %d Hz, should be %d, fixing ...\n",
 			      freq, CONFIG_TIMER_CLK_FREQ);
 #ifdef CONFIG_NON_SECURE
 			printf("arch timer frequency is wrong, but cannot adjust it\n");
 #else
 			asm volatile("mcr p15, 0, %0, c14, c0, 0"
 				     : : "r"(CONFIG_TIMER_CLK_FREQ));
 #endif
 		}
 	}
 #endif /* !CONFIG_ARM64 */
 
 	sunxi_gpio_set_cfgpin(SUNXI_GPG(10), SUN6I_GPG_USB3);
 	sunxi_gpio_set_cfgpin(SUNXI_GPG(11), SUN6I_GPG_USB3);
 
 	gpio_request(SUNXI_GPC(3), "STM32 Boot0");
 	sunxi_gpio_set_cfgpin(SUNXI_GPC(3), SUNXI_GPIO_OUTPUT);
 	gpio_request(SUNXI_GPC(26), "STM32 Reset");
 	sunxi_gpio_set_cfgpin(SUNXI_GPC(26), SUNXI_GPIO_OUTPUT);
 #if !defined(CONFIG_DM_GPIO)
 	gpio_request(SUNXI_GPA(7), "PHY Reset");
 	sunxi_gpio_set_cfgpin(SUNXI_GPA(7), SUNXI_GPIO_OUTPUT);
 	gpio_direction_output(SUNXI_GPA(7), 0);
 #endif
 
 	gpio_direction_output(SUNXI_GPC(3), 0);
 	gpio_direction_output(SUNXI_GPC(26), 0);
 	mdelay(10);
 	gpio_direction_output(SUNXI_GPC(26), 1);
 #if !defined(CONFIG_DM_GPIO)
 	gpio_direction_output(SUNXI_GPA(7), 1);
 #endif
 
 	setup_led();
 
 	ret = axp_gpio_init();
 	if (ret)
 		return ret;
 
 #ifdef CONFIG_SATAPWR
 	gpio_request(CONFIG_SATAPWR, "satapwr");
 	gpio_direction_output(CONFIG_SATAPWR, 1);
 #endif
 #ifdef CONFIG_MACPWR
 	gpio_request(CONFIG_MACPWR, "macpwr");
 	gpio_direction_output(CONFIG_MACPWR, 1);
 #endif
 
 	/* Uses dm gpio code so do this here and not in i2c_init_board() */
 	return soft_i2c_board_init();
 
 }
 
@@ -1018,24 +1030,23 @@ U_BOOT_CMD(
 int board_fit_config_name_match(const char *name)
 {
 #ifdef CONFIG_SPL_LOAD_FIT
 	const char *cmp_str;
 
 #ifdef CONFIG_DEFAULT_DEVICE_TREE
 	cmp_str = CONFIG_DEFAULT_DEVICE_TREE;
 #else
 	return 0;
 #endif
 
 	/* Differentiate the two Pine64 board DTs by their DRAM size. */
 	if (strstr(name, "-pine64") && strstr(cmp_str, "-pine64")) {
 		if ((gd->ram_size > 512 * 1024 * 1024))
 			return !strstr(name, "plus");
 		else
 			return !!strstr(name, "plus");
 	} else {
 		return strcmp(name, cmp_str);
 	}
 #endif
 }
 
-
-- 
1.9.1

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

* [U-Boot] [PATCH v1 08/11] sun8i_emac: update to work with pinctrl-sunxi, reset-sunxi and clk-sunxi
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (6 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 07/11] sunxi: Scan DT tree node '/clocks' on sunxi boards Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 09/11] sunxi_mmc: convert to a device-model driver Philipp Tomsich
                   ` (2 subsequent siblings)
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

parse_phy_pins() is parsing the pinctrl-entry of the emac-node, which
is in conflict with the new DM pinctrl driver for sun50i.  So when
building for sun50i and if the pinctrl driver is active, let the
parse_phy_pins()-function do nothing.

Use the DM reset and DM clk frameworks for getting the emac module out
of reset and enabling the clock gates.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 drivers/net/sun8i_emac.c | 45 +++++++++++++++++++++++++++++++++++++++++----
 1 file changed, 41 insertions(+), 4 deletions(-)

diff --git a/drivers/net/sun8i_emac.c b/drivers/net/sun8i_emac.c
index 664d585..9d783e1 100644
--- a/drivers/net/sun8i_emac.c
+++ b/drivers/net/sun8i_emac.c
@@ -1,101 +1,107 @@
 /*
  * (C) Copyright 2016
  * Author: Amit Singh Tomar, amittomer25 at gmail.com
  *
  * SPDX-License-Identifier:     GPL-2.0+
  *
  * Ethernet driver for H3/A64/A83T based SoC's
  *
  * It is derived from the work done by
  * LABBE Corentin & Chen-Yu Tsai for Linux, THANKS!
  *
 */
 
 #include <asm/io.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/gpio.h>
+#if defined(CONFIG_DM_GPIO)
+#include <asm-generic/gpio.h>
+#endif
+#if defined(CONFIG_CLK)
+#include <clk.h>
+#endif
 #include <common.h>
 #include <dm.h>
 #include <fdt_support.h>
 #include <linux/err.h>
 #include <malloc.h>
 #include <miiphy.h>
 #include <net.h>
-#ifdef CONFIG_DM_GPIO
-#include <asm-generic/gpio.h>
+#if defined(CONFIG_DM_RESET)
+#include <reset.h>
 #endif
 
 #define MDIO_CMD_MII_BUSY		BIT(0)
 #define MDIO_CMD_MII_WRITE		BIT(1)
 
 #define MDIO_CMD_MII_PHY_REG_ADDR_MASK	0x000001f0
 #define MDIO_CMD_MII_PHY_REG_ADDR_SHIFT	4
 #define MDIO_CMD_MII_PHY_ADDR_MASK	0x0001f000
 #define MDIO_CMD_MII_PHY_ADDR_SHIFT	12
 
 #define MDIO_CMD_MDC_DIV_RATIO_M_SHIFT  20
 #define MDIO_CMD_MDC_DIV_16             (0 << MDIO_CMD_MDC_DIV_RATIO_M_SHIFT)
 #define MDIO_CMD_MDC_DIV_32             (1 << MDIO_CMD_MDC_DIV_RATIO_M_SHIFT)
 #define MDIO_CMD_MDC_DIV_64             (2 << MDIO_CMD_MDC_DIV_RATIO_M_SHIFT)
 #define MDIO_CMD_MDC_DIV_128            (3 << MDIO_CMD_MDC_DIV_RATIO_M_SHIFT)
 
 #define CONFIG_TX_DESCR_NUM	32
 #define CONFIG_RX_DESCR_NUM	32
 #define CONFIG_ETH_BUFSIZE	2048 /* Note must be dma aligned */
 
 /*
  * The datasheet says that each descriptor can transfers up to 4096 bytes
  * But later, the register documentation reduces that value to 2048,
  * using 2048 cause strange behaviours and even BSP driver use 2047
  */
 #define CONFIG_ETH_RXSIZE	2044 /* Note must fit in ETH_BUFSIZE */
 
 #define TX_TOTAL_BUFSIZE	(CONFIG_ETH_BUFSIZE * CONFIG_TX_DESCR_NUM)
 #define RX_TOTAL_BUFSIZE	(CONFIG_ETH_BUFSIZE * CONFIG_RX_DESCR_NUM)
 
 #define H3_EPHY_DEFAULT_VALUE	0x58000
 #define H3_EPHY_DEFAULT_MASK	GENMASK(31, 15)
 #define H3_EPHY_ADDR_SHIFT	20
 #define REG_PHY_ADDR_MASK	GENMASK(4, 0)
 #define H3_EPHY_LED_POL		BIT(17)	/* 1: active low, 0: active high */
 #define H3_EPHY_SHUTDOWN	BIT(16)	/* 1: shutdown, 0: power up */
 #define H3_EPHY_SELECT		BIT(15) /* 1: internal PHY, 0: external PHY */
 
 #define SC_RMII_EN		BIT(13)
 #define SC_EPIT			BIT(2) /* 1: RGMII, 0: MII */
 #define SC_ETCS_MASK		GENMASK(1, 0)
 #define SC_ETCS_EXT_GMII	0x1
 #define SC_ETCS_INT_GMII	0x2
 
 #define CONFIG_MDIO_TIMEOUT	(3 * CONFIG_SYS_HZ)
 
 #define AHB_GATE_OFFSET_EPHY	0
 
 #if defined(CONFIG_MACH_SUN8I_H3)
 #define SUN8I_GPD8_GMAC		2
 #else
 #define SUN8I_GPD8_GMAC		4
 #endif
 
 /* H3/A64 EMAC Register's offset */
 #define EMAC_CTL0		0x00
 #define EMAC_CTL1		0x04
 #define EMAC_INT_STA		0x08
 #define EMAC_INT_EN		0x0c
 #define EMAC_TX_CTL0		0x10
 #define EMAC_TX_CTL1		0x14
 #define EMAC_TX_FLOW_CTL	0x1c
 #define EMAC_TX_DMA_DESC	0x20
 #define EMAC_RX_CTL0		0x24
 #define EMAC_RX_CTL1		0x28
 #define EMAC_RX_DMA_DESC	0x34
 #define EMAC_MII_CMD		0x48
 #define EMAC_MII_DATA		0x4c
 #define EMAC_ADDR0_HIGH		0x50
 #define EMAC_ADDR0_LOW		0x54
 #define EMAC_TX_DMA_STA		0xb0
 #define EMAC_TX_CUR_DESC	0xb4
 #define EMAC_TX_CUR_BUF		0xb8
 #define EMAC_RX_DMA_STA		0xc0
 #define EMAC_RX_CUR_DESC	0xc4
 
@@ -117,29 +123,35 @@ struct emac_dma_desc {
 struct emac_eth_dev {
 	struct emac_dma_desc rx_chain[CONFIG_TX_DESCR_NUM];
 	struct emac_dma_desc tx_chain[CONFIG_RX_DESCR_NUM];
 	char rxbuffer[RX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);
 	char txbuffer[TX_TOTAL_BUFSIZE] __aligned(ARCH_DMA_MINALIGN);
 
 	u32 interface;
 	u32 phyaddr;
 	u32 link;
 	u32 speed;
 	u32 duplex;
 	u32 phy_configured;
 	u32 tx_currdescnum;
 	u32 rx_currdescnum;
 	u32 addr;
 	u32 tx_slot;
 	bool use_internal_phy;
 
 	enum emac_variant variant;
 	void *mac_reg;
 	phys_addr_t sysctl_reg;
 	struct phy_device *phydev;
 	struct mii_dev *bus;
 #ifdef CONFIG_DM_GPIO
 	struct gpio_desc reset_gpio;
 #endif
+#ifdef CONFIG_DM_RESET
+	struct reset_ctl reset;
+#endif
+#ifdef CONFIG_CLK
+	struct clk ahb_clk_gate;
+#endif
 };
 
 
@@ -459,45 +471,47 @@ static int _sun8i_emac_eth_init(struct emac_eth_dev *priv, u8 *enetaddr)
 
 static int parse_phy_pins(struct udevice *dev)
 {
+#if !(defined(CONFIG_MACH_SUN50I) && defined(CONFIG_SUNXI_PINCTRL))
 	int offset;
 	const char *pin_name;
 	int drive, pull, i;
 
 	offset = fdtdec_lookup_phandle(gd->fdt_blob, dev_of_offset(dev),
 				       "pinctrl-0");
 	if (offset < 0) {
 		printf("WARNING: emac: cannot find pinctrl-0 node\n");
 		return offset;
 	}
 
 	drive = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0,
 					     "allwinner,drive", 4);
 	pull = fdt_getprop_u32_default_node(gd->fdt_blob, offset, 0,
 					    "allwinner,pull", 0);
 	for (i = 0; ; i++) {
 		int pin;
 
 		pin_name = fdt_stringlist_get(gd->fdt_blob, offset,
 					      "allwinner,pins", i, NULL);
 		if (!pin_name)
 			break;
 		if (pin_name[0] != 'P')
 			continue;
 		pin = (pin_name[1] - 'A') << 5;
 		if (pin >= 26 << 5)
 			continue;
 		pin += simple_strtol(&pin_name[2], NULL, 10);
 
 		sunxi_gpio_set_cfgpin(pin, SUN8I_GPD8_GMAC);
 		sunxi_gpio_set_drv(pin, drive);
 		sunxi_gpio_set_pull(pin, pull);
 	}
 
 	if (!i) {
 		printf("WARNING: emac: cannot find allwinner,pins property\n");
 		return -2;
 	}
+#endif
 
 	return 0;
 }
 
@@ -609,20 +623,28 @@ static int sun8i_eth_write_hwaddr(struct udevice *dev)
 static void sun8i_emac_board_setup(struct emac_eth_dev *priv)
 {
 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 
 	if (priv->use_internal_phy) {
 		/* Set clock gating for ephy */
 		setbits_le32(&ccm->bus_gate4, BIT(AHB_GATE_OFFSET_EPHY));
 
 		/* Deassert EPHY */
 		setbits_le32(&ccm->ahb_reset2_cfg, BIT(AHB_RESET_OFFSET_EPHY));
 	}
 
 	/* Set clock gating for emac */
+#if defined(CONFIG_CLK)
+	clk_enable(&priv->ahb_clk_gate);
+#else
 	setbits_le32(&ccm->ahb_gate0, BIT(AHB_GATE_OFFSET_GMAC));
+#endif
 
 	/* De-assert EMAC */
+#if defined(CONFIG_DM_RESET)
+	reset_deassert(&priv->reset);
+#else
 	setbits_le32(&ccm->ahb_reset0_cfg, BIT(AHB_RESET_OFFSET_GMAC));
+#endif
 }
 
 #if defined(CONFIG_DM_GPIO)
@@ -772,75 +794,90 @@ static const struct eth_ops sun8i_emac_eth_ops = {
 static int sun8i_emac_eth_ofdata_to_platdata(struct udevice *dev)
 {
 	struct sun8i_eth_pdata *sun8i_pdata = dev_get_platdata(dev);
 	struct eth_pdata *pdata = &sun8i_pdata->eth_pdata;
 	struct emac_eth_dev *priv = dev_get_priv(dev);
 	const char *phy_mode;
 	int node = dev_of_offset(dev);
 	int offset = 0;
 #ifdef CONFIG_DM_GPIO
 	int reset_flags = GPIOD_IS_OUT;
 	int ret = 0;
 #endif
 
 	pdata->iobase = dev_get_addr_name(dev, "emac");
 	priv->sysctl_reg = dev_get_addr_name(dev, "syscon");
 
 	pdata->phy_interface = -1;
 	priv->phyaddr = -1;
 	priv->use_internal_phy = false;
 
 	offset = fdtdec_lookup_phandle(gd->fdt_blob, node,
 				       "phy");
 	if (offset > 0)
 		priv->phyaddr = fdtdec_get_int(gd->fdt_blob, offset, "reg",
 					       -1);
 
 	phy_mode = fdt_getprop(gd->fdt_blob, node, "phy-mode", NULL);
 
 	if (phy_mode)
 		pdata->phy_interface = phy_get_interface_by_name(phy_mode);
 	printf("phy interface%d\n", pdata->phy_interface);
 
 	if (pdata->phy_interface == -1) {
 		debug("%s: Invalid PHY interface '%s'\n", __func__, phy_mode);
 		return -EINVAL;
 	}
 
 	priv->variant = dev_get_driver_data(dev);
 
 	if (!priv->variant) {
 		printf("%s: Missing variant '%s'\n", __func__,
 		       (char *)priv->variant);
 		return -EINVAL;
 	}
 
 	if (priv->variant == H3_EMAC) {
 		if (fdt_getprop(gd->fdt_blob, node,
 				"allwinner,use-internal-phy", NULL))
 			priv->use_internal_phy = true;
 	}
 
 	priv->interface = pdata->phy_interface;
 
 	if (!priv->use_internal_phy)
 		parse_phy_pins(dev);
 
-#ifdef CONFIG_DM_GPIO
+#if defined(CONFIG_DM_GPIO)
 	if (fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
 			    "allwinner,reset-active-low"))
 		reset_flags |= GPIOD_ACTIVE_LOW;
 
 	ret = gpio_request_by_name(dev, "allwinner,reset-gpio", 0,
 				   &priv->reset_gpio, reset_flags);
 
 	if (ret == 0) {
 		ret = fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
-					   "allwinner,reset-delays-us", sun8i_pdata->reset_delays, 3);
+					   "allwinner,reset-delays-us",
+					   sun8i_pdata->reset_delays, 3);
 	} else if (ret == -ENOENT) {
 		ret = 0;
 	}
 #endif
 
+#if defined(CONFIG_DM_RESET)
+	if (reset_get_by_name(dev, "ahb", &priv->reset)) {
+		error("%s: failed to get 'ahb' reset\n", dev->name);
+		return -EINVAL;
+	}
+#endif
+
+#if defined(CONFIG_CLK)
+	if (clk_get_by_name(dev, "ahb", &priv->ahb_clk_gate)) {
+		error("%s: failed to get 'ahb' clock\n", dev->name);
+		return -EINVAL;
+	}
+#endif
+
 	return 0;
 }
 
-- 
1.9.1

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

* [U-Boot] [PATCH v1 09/11] sunxi_mmc: convert to a device-model driver
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (7 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 08/11] sun8i_emac: update to work with pinctrl-sunxi, reset-sunxi and clk-sunxi Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 10/11] dts: sun50i: update mmc pin configuration and add mmc2_8bit_pins Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 11/11] sun50i: dts: add spi0 and spi1 nodes and pinconfig Philipp Tomsich
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

We now support the device-model for configuration of the driver
including the interface to the pinctrl, reset and clock frameworks.

Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
---
 board/sunxi/board.c     |   7 +-
 drivers/mmc/sunxi_mmc.c | 350 ++++++++++++++++++++++++++++++++++++++++++++----
 2 files changed, 331 insertions(+), 26 deletions(-)

diff --git a/board/sunxi/board.c b/board/sunxi/board.c
index 838e89f..810fbd4 100644
--- a/board/sunxi/board.c
+++ b/board/sunxi/board.c
@@ -237,14 +237,14 @@ static void nand_clock_setup(void)
 void board_nand_init(void)
 {
 	nand_pinmux_setup();
 	nand_clock_setup();
 #ifndef CONFIG_SPL_BUILD
 	sunxi_nand_init();
 #endif
 }
 #endif
 
-#ifdef CONFIG_GENERIC_MMC
+#if defined(CONFIG_GENERIC_MMC) && !(defined(CONFIG_DM_MMC) && defined(CONFIG_PINCTRL))
 static void mmc_pinmux_setup(int sdc)
 {
 	unsigned int pin;
@@ -422,75 +422,76 @@ static void mmc_pinmux_setup(int sdc)
 
 int board_mmc_init(bd_t *bis)
 {
+#if !(defined(CONFIG_DM_MMC) && defined(CONFIG_PINCTRL))
 	__maybe_unused struct mmc *mmc0, *mmc1;
 	__maybe_unused char buf[512];
 	__maybe_unused u32 val;
 
 	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT);
 	mmc0 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT);
 	if (!mmc0)
 		return -1;
 
 #if CONFIG_MMC_SUNXI_SLOT_EXTRA != -1
 	mmc_pinmux_setup(CONFIG_MMC_SUNXI_SLOT_EXTRA);
 	mmc1 = sunxi_mmc_init(CONFIG_MMC_SUNXI_SLOT_EXTRA);
 	if (!mmc1)
 		return -1;
 #endif
 
 #if !defined(CONFIG_SPL_BUILD) && CONFIG_MMC_SUNXI_SLOT_EXTRA == 2
 #if CONFIG_MACH_SUN6I
 	/*
 	 * the bootdevice is shown in VER_REG in the system controller
 	 * if U_boot is set then the ROM trys to boot from mmc0 regardless
 	 * of the BOOT_SEL pins.
 	 */
 
 #if 0
 	val = readl(0x1c00024);
 	if ((val & (1<<10))) /* check UBOOT_SEL */
 	{
 		switch((val & (3<<8))>>8) /* check BOOT_SEL pins */
 		{
 			case 0x0: /* SPI0 boot */
 				break;
 			case 0x1: /* eMMC2 boot */
 				/* Check if there is a boot loader on eMMC2
 				 * If not we want to fall back to SD card */
 				if (mmc_init(mmc1) == 0 &&
 				    mmc1->block_dev.block_read(&mmc1->block_dev, 16, 1, buf) == 1) {
 					buf[12] = 0;
 					if (strcmp(&buf[4], "eGON.BT0") == 0) {
 						/* Boot loader found, swap to make eMMC the first device */
 						mmc0->block_dev.devnum = 1;
 						mmc1->block_dev.devnum = 0;
 					}
 				}
 				break;
 			case 0x2: /* SDC2 boot */
 				break;
 			case 0x3: /* NAND Flash boot */
 				break;
 		}
 	}
 #endif
 
 #else
 	/*
 	 * On systems with an emmc (mmc2), figure out if we are booting from
 	 * the emmc and if we are make it "mmc dev 0" so that boot.scr, etc.
 	 * are searched there first. Note we only do this for u-boot proper,
 	 * not for the SPL, see spl_boot_device().
 	 */
 	if (readb(SPL_ADDR + 0x28) == SUNXI_BOOTED_FROM_MMC2) {
 		/* Booting from emmc / mmc2, swap */
 		mmc0->block_dev.devnum = 1;
 		mmc1->block_dev.devnum = 0;
 	}
 #endif
 #endif
-
+#endif
 	return 0;
 }
 #endif
 
@@ -854,43 +855,45 @@ static void setup_environment(const void *fdt)
  */
 static void setup_environment(const void *fdt)
 {
+#if !defined(CONFIG_DM_MMC)
 	uint8_t mac_addr[6];
 	char serial_string[17] = { 0 };
 	struct mmc *mmc0;
 	struct sunxi_mmc_host {
 		unsigned mmc_no;
 		uint32_t *mclkreg;
 		unsigned fatal_err;
 		struct sunxi_mmc *reg;
 		struct mmc_config cfg;
 	};
 
 	mmc0 = find_mmc_device(1);
 
 	/* lookup the real device number to get the eMMC */
 	if(((struct sunxi_mmc_host*)mmc0->priv)->mmc_no != 2)
 		mmc0 = find_mmc_device(0);
 
 	if(mmc0->has_init == 0)
 		mmc_init(mmc0);
 
 	if (!getenv("ethaddr")) {
 		/* Non OUI / registered MAC address */
 		mac_addr[0] = 0x02;
 		mac_addr[1] = (mmc0->cid[0] >> 24) & 0xff;
 		mac_addr[2] = (mmc0->cid[2] >> 0) & 0xff;
 		mac_addr[3] = (mmc0->cid[3] >> 24) & 0xff;
 		mac_addr[4] = (mmc0->cid[3] >> 16) & 0xff;
 		mac_addr[5] = (mmc0->cid[3] >> 8) & 0xff;
 
 		eth_setenv_enetaddr("ethaddr", mac_addr);
 	}
 
 	if (!getenv("serial#")) {
 		snprintf(serial_string, sizeof(serial_string), "%08x%02x%06x",
 			 mmc0->cid[0]>>24,mmc0->cid[2]&0xff,mmc0->cid[3]>>8);
 		setenv("serial#", serial_string);
 	}
+#endif
 }
 #endif
 
diff --git a/drivers/mmc/sunxi_mmc.c b/drivers/mmc/sunxi_mmc.c
index 46abe4a..8075b9c 100644
--- a/drivers/mmc/sunxi_mmc.c
+++ b/drivers/mmc/sunxi_mmc.c
@@ -1,89 +1,165 @@
 /*
  * (C) Copyright 2007-2011
  * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
  * Aaron <leafy.myeh@allwinnertech.com>
  *
+ * (C) 2017 Theobroma Systems Design und Consulting GmbH
+ *
  * MMC driver for allwinner sunxi platform.
  *
  * SPDX-License-Identifier:	GPL-2.0+
  */
 
 #include <common.h>
-#include <errno.h>
-#include <malloc.h>
-#include <mmc.h>
-#include <asm/io.h>
+#include <asm-generic/gpio.h>
 #include <asm/arch/clock.h>
 #include <asm/arch/cpu.h>
 #include <asm/arch/gpio.h>
 #include <asm/arch/mmc.h>
-#include <asm-generic/gpio.h>
+#include <asm/io.h>
+#include <clk.h>
+#include <dm/device.h>
+#include <dt-structs.h>
+#include <errno.h>
+#include <linux/iopoll.h>
+#include <malloc.h>
+#include <mmc.h>
+#include <reset.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+struct sunxi_mmc_plat {
+	struct mmc mmc;
+};
 
 struct sunxi_mmc_host {
 	unsigned mmc_no;
+#if !defined(CONFIG_DM_MMC)
 	uint32_t *mclkreg;
+#endif
 	unsigned fatal_err;
 	struct sunxi_mmc *reg;
 	struct mmc_config cfg;
+	bool cd_inverted;
+#if defined(CONFIG_DM_MMC)
+	struct mmc *mmc;
+	struct gpio_desc cd_gpio;	/* card-detect (optional) */
+	struct gpio_desc pwr_gpio;	/* power-enabled (optional) */
+	struct gpio_desc wp_gpio;	/* write-protect (optional) */
+	bool wp_inverted;
+	struct reset_ctl reset;
+	struct clk ahb_clk_gate;
+	struct clk mmc_clk;
+#else
+	int cd_pin;
+#endif
 };
 
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+static const struct dm_mmc_ops sunxi_mmc_ops;
+#else
+static const struct mmc_ops sunxi_mmc_ops;
+#endif
+
+#if !defined(CONFIG_DM_MMC)
 /* support 4 mmc hosts */
 struct sunxi_mmc_host mmc_host[4];
 
 static int sunxi_mmc_getcd_gpio(int sdc_no)
 {
 	switch (sdc_no) {
 	case 0: return sunxi_name_to_gpio(CONFIG_MMC0_CD_PIN);
 	case 1: return sunxi_name_to_gpio(CONFIG_MMC1_CD_PIN);
 	case 2: return sunxi_name_to_gpio(CONFIG_MMC2_CD_PIN);
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
 	case 3: return sunxi_name_to_gpio(CONFIG_MMC3_CD_PIN);
+#endif
 	}
 	return -EINVAL;
 }
 
 static int mmc_resource_init(int sdc_no)
 {
 	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
 	int cd_pin, ret = 0;
 
 	debug("init mmc %d resource\n", sdc_no);
 
 	switch (sdc_no) {
 	case 0:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC0_BASE;
 		mmchost->mclkreg = &ccm->sd0_clk_cfg;
 		break;
 	case 1:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC1_BASE;
 		mmchost->mclkreg = &ccm->sd1_clk_cfg;
 		break;
 	case 2:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC2_BASE;
 		mmchost->mclkreg = &ccm->sd2_clk_cfg;
 		break;
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
 	case 3:
 		mmchost->reg = (struct sunxi_mmc *)SUNXI_MMC3_BASE;
 		mmchost->mclkreg = &ccm->sd3_clk_cfg;
 		break;
+#endif
 	default:
 		printf("Wrong mmc number %d\n", sdc_no);
 		return -1;
 	}
 	mmchost->mmc_no = sdc_no;
 
 	cd_pin = sunxi_mmc_getcd_gpio(sdc_no);
 	if (cd_pin >= 0) {
 		ret = gpio_request(cd_pin, "mmc_cd");
 		if (!ret) {
 			sunxi_gpio_set_pull(cd_pin, SUNXI_GPIO_PULL_UP);
 			ret = gpio_direction_input(cd_pin);
 		}
 	}
+	mmchost->cd_pin = cd_pin;
+
+	return ret;
+}
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int mmc_resource_init_from_udev(struct udevice *dev)
+{
+	struct sunxi_mmc_host *mmchost = dev_get_priv(dev);
+	int ret = 0;
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	switch ((uintptr_t)mmchost->reg) {
+	case SUNXI_MMC0_BASE:
+		mmchost->mmc_no = 0;
+		break;
+	case SUNXI_MMC1_BASE:
+		mmchost->mmc_no = 1;
+		break;
+	case SUNXI_MMC2_BASE:
+		mmchost->mmc_no = 2;
+		break;
+#if !defined(CONFIG_ARCH_SUN50I)  /* only 3 MMC controllers on the A64 */
+	case SUNXI_MMC3_BASE:
+		mmchost->mmc_no = 3;
+		break;
+#endif
+	default:
+		debug("%s: unknown base address %p\n", __func__, mmchost->reg);
+		return -1;
+	}
+
+	debug("%s: mmc_no %d\n", dev->name, mmchost->mmc_no);
 
 	return ret;
 }
+#endif
 
+#if !defined(CONFIG_DM_MMC)
 static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
 {
 	unsigned int pll, pll_hz, div, n, oclk_dly, sclk_dly;
@@ -154,112 +230,156 @@ static int mmc_set_mod_clk(struct sunxi_mmc_host *mmchost, unsigned int hz)
 	return 0;
 }
 
-static int mmc_clk_io_on(int sdc_no)
+static int mmc_clk_io_on(struct sunxi_mmc_host *mmchost)
 {
-	struct sunxi_mmc_host *mmchost = &mmc_host[sdc_no];
 	struct sunxi_ccm_reg *ccm = (struct sunxi_ccm_reg *)SUNXI_CCM_BASE;
+	int sdc_no = mmchost->mmc_no;
 
 	debug("init mmc %d clock and io\n", sdc_no);
 
 	/* config ahb clock */
 	setbits_le32(&ccm->ahb_gate0, 1 << AHB_GATE_OFFSET_MMC(sdc_no));
 
 #ifdef CONFIG_SUNXI_GEN_SUN6I
 	/* unassert reset */
 	setbits_le32(&ccm->ahb_reset0_cfg, 1 << AHB_RESET_OFFSET_MMC(sdc_no));
 #endif
 #if defined(CONFIG_MACH_SUN9I)
 	/* sun9i has a mmc-common module, also set the gate and reset there */
 	writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
 	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
 #endif
 
 	return mmc_set_mod_clk(mmchost, 24000000);
 }
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int mmc_clk_io_on(struct sunxi_mmc_host *mmchost)
+{
+	/* Enable the AHB clock gate */
+	clk_enable(&mmchost->ahb_clk_gate);
+
+	/* Deassert the AHB module reset */
+	reset_deassert(&mmchost->reset);
+
+#if defined(CONFIG_MACH_SUN9I)
+	/* TODO --- covert this to DM */
+	/* sun9i has a mmc-common module, also set the gate and reset there */
+	writel(SUNXI_MMC_COMMON_CLK_GATE | SUNXI_MMC_COMMON_RESET,
+	       SUNXI_MMC_COMMON_BASE + 4 * sdc_no);
+#endif
+
+	clk_set_rate(&mmchost->mmc_clk, 24000000);
+	clk_enable(&mmchost->mmc_clk);
+
+	return 0;
+}
+#endif
 
 static int mmc_update_clk(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned int cmd;
 	unsigned timeout_msecs = 2000;
 	unsigned long start = get_timer(0);
 
+	debug("%s: base %p\n", __func__, mmchost->reg);
+
 	cmd = SUNXI_MMC_CMD_START |
 	      SUNXI_MMC_CMD_UPCLK_ONLY |
 	      SUNXI_MMC_CMD_WAIT_PRE_OVER;
 	writel(cmd, &mmchost->reg->cmd);
 	while (readl(&mmchost->reg->cmd) & SUNXI_MMC_CMD_START) {
 		if (get_timer(start) > timeout_msecs)
 			return -1;
 	}
 
 	/* clock update sets various irq status bits, clear these */
 	writel(readl(&mmchost->reg->rint), &mmchost->reg->rint);
 
 	return 0;
 }
 
 static int mmc_config_clock(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned rval = readl(&mmchost->reg->clkcr);
 
 	/* Disable Clock */
 	rval &= ~SUNXI_MMC_CLK_ENABLE;
 	writel(rval, &mmchost->reg->clkcr);
 	if (mmc_update_clk(mmc))
 		return -1;
 
+#if !defined(CONFIG_DM_MMC)
 	/* Set mod_clk to new rate */
 	if (mmc_set_mod_clk(mmchost, mmc->clock))
 		return -1;
+#else
+	if (clk_set_rate(&mmchost->mmc_clk, mmc->clock) == 0)
+		return -1;
+#endif
 
 	/* Clear internal divider */
 	rval &= ~SUNXI_MMC_CLK_DIVIDER_MASK;
 	writel(rval, &mmchost->reg->clkcr);
 
 	/* Re-enable Clock */
 	rval |= SUNXI_MMC_CLK_ENABLE;
 	writel(rval, &mmchost->reg->clkcr);
 	if (mmc_update_clk(mmc))
 		return -1;
 
 	return 0;
 }
 
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_set_ios(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_set_ios(struct mmc *mmc)
 {
+#endif
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 
 	debug("set ios: bus_width: %x, clock: %d\n",
 	      mmc->bus_width, mmc->clock);
 
 	/* Change clock first */
 	if (mmc->clock && mmc_config_clock(mmc) != 0) {
 		mmchost->fatal_err = 1;
 		return -EINVAL;
 	}
 
 	/* Change bus width */
 	if (mmc->bus_width == 8)
 		writel(0x2, &mmchost->reg->width);
 	else if (mmc->bus_width == 4)
 		writel(0x1, &mmchost->reg->width);
 	else
 		writel(0x0, &mmchost->reg->width);
 
 	return 0;
 }
 
 static int sunxi_mmc_core_init(struct mmc *mmc)
 {
 	struct sunxi_mmc_host *mmchost = mmc->priv;
+	uint32_t regval;
+	int ret = 0;
+
+	debug("%s: base %p", __func__, mmchost->reg);
 
 	/* Reset controller */
 	writel(SUNXI_MMC_GCTRL_RESET, &mmchost->reg->gctrl);
-	udelay(1000);
 
-	return 0;
+	/* Wait for the reset bit (auto-clearing) to deassert */
+	ret = readl_poll_timeout(&mmchost->reg->gctrl, regval,
+				 !(regval & SUNXI_MMC_GCTRL_RESET), 1000000);
+
+	return ret;
 }
 
 static int mmc_trans_data_by_cpu(struct mmc *mmc, struct mmc_data *data)
@@ -317,113 +437,120 @@ static int mmc_rint_wait(struct mmc *mmc, unsigned int timeout_msecs,
 	return 0;
 }
 
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_send_cmd(struct udevice *dev, struct mmc_cmd *cmd,
+			      struct mmc_data *data)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_send_cmd(struct mmc *mmc, struct mmc_cmd *cmd,
 			      struct mmc_data *data)
 {
+#endif
 	struct sunxi_mmc_host *mmchost = mmc->priv;
 	unsigned int cmdval = SUNXI_MMC_CMD_START;
 	unsigned int timeout_msecs;
 	int error = 0;
 	unsigned int status = 0;
 	unsigned int bytecnt = 0;
 
 	if (mmchost->fatal_err)
 		return -1;
 	if (cmd->resp_type & MMC_RSP_BUSY)
 		debug("mmc cmd %d check rsp busy\n", cmd->cmdidx);
 	if (cmd->cmdidx == 12)
 		return 0;
 
 	if (!cmd->cmdidx)
 		cmdval |= SUNXI_MMC_CMD_SEND_INIT_SEQ;
 	if (cmd->resp_type & MMC_RSP_PRESENT)
 		cmdval |= SUNXI_MMC_CMD_RESP_EXPIRE;
 	if (cmd->resp_type & MMC_RSP_136)
 		cmdval |= SUNXI_MMC_CMD_LONG_RESPONSE;
 	if (cmd->resp_type & MMC_RSP_CRC)
 		cmdval |= SUNXI_MMC_CMD_CHK_RESPONSE_CRC;
 
 	if (data) {
 		if ((u32)(long)data->dest & 0x3) {
 			error = -1;
 			goto out;
 		}
 
 		cmdval |= SUNXI_MMC_CMD_DATA_EXPIRE|SUNXI_MMC_CMD_WAIT_PRE_OVER;
 		if (data->flags & MMC_DATA_WRITE)
 			cmdval |= SUNXI_MMC_CMD_WRITE;
 		if (data->blocks > 1)
 			cmdval |= SUNXI_MMC_CMD_AUTO_STOP;
 		writel(data->blocksize, &mmchost->reg->blksz);
 		writel(data->blocks * data->blocksize, &mmchost->reg->bytecnt);
 	}
 
-	debug("mmc %d, cmd %d(0x%08x), arg 0x%08x\n", mmchost->mmc_no,
+	debug("mmc %p, cmd %d(0x%08x), arg 0x%08x\n", mmchost,
 	      cmd->cmdidx, cmdval | cmd->cmdidx, cmd->cmdarg);
 	writel(cmd->cmdarg, &mmchost->reg->arg);
 
 	if (!data)
 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
 
 	/*
 	 * transfer data and check status
 	 * STATREG[2] : FIFO empty
 	 * STATREG[3] : FIFO full
 	 */
 	if (data) {
 		int ret = 0;
 
 		bytecnt = data->blocksize * data->blocks;
 		debug("trans data %d bytes\n", bytecnt);
 		writel(cmdval | cmd->cmdidx, &mmchost->reg->cmd);
 		ret = mmc_trans_data_by_cpu(mmc, data);
 		if (ret) {
 			error = readl(&mmchost->reg->rint) & \
 				SUNXI_MMC_RINT_INTERRUPT_ERROR_BIT;
 			error = -ETIMEDOUT;
 			goto out;
 		}
 	}
 
 	error = mmc_rint_wait(mmc, 1000, SUNXI_MMC_RINT_COMMAND_DONE, "cmd");
 	if (error)
 		goto out;
 
 	if (data) {
 		timeout_msecs = 120;
 		debug("cacl timeout %x msec\n", timeout_msecs);
 		error = mmc_rint_wait(mmc, timeout_msecs,
 				      data->blocks > 1 ?
 				      SUNXI_MMC_RINT_AUTO_COMMAND_DONE :
 				      SUNXI_MMC_RINT_DATA_OVER,
 				      "data");
 		if (error)
 			goto out;
 	}
 
 	if (cmd->resp_type & MMC_RSP_BUSY) {
 		unsigned long start = get_timer(0);
 		timeout_msecs = 2000;
 
 		do {
 			status = readl(&mmchost->reg->status);
 			if (get_timer(start) > timeout_msecs) {
 				debug("busy timeout\n");
 				error = -ETIMEDOUT;
 				goto out;
 			}
 		} while (status & SUNXI_MMC_STATUS_CARD_DATA_BUSY);
 	}
 
 	if (cmd->resp_type & MMC_RSP_136) {
 		cmd->response[0] = readl(&mmchost->reg->resp3);
 		cmd->response[1] = readl(&mmchost->reg->resp2);
 		cmd->response[2] = readl(&mmchost->reg->resp1);
 		cmd->response[3] = readl(&mmchost->reg->resp0);
 		debug("mmc resp 0x%08x 0x%08x 0x%08x 0x%08x\n",
 		      cmd->response[3], cmd->response[2],
 		      cmd->response[1], cmd->response[0]);
 	} else {
 		cmd->response[0] = readl(&mmchost->reg->resp0);
 		debug("mmc resp 0x%08x\n", cmd->response[0]);
 	}
@@ -439,50 +566,225 @@ out:
 	return error;
 }
 
+static inline int cdpin_is_valid(struct sunxi_mmc_host *priv)
+{
+#if !defined(CONFIG_DM_MMC)
+	return priv->cd_pin >= 0;
+#else
+	return dm_gpio_is_valid(&priv->cd_gpio);
+#endif
+}
+
+static inline int cdpin_get_value(struct sunxi_mmc_host *priv)
+{
+#if !defined(CONFIG_DM_MMC)
+	return gpio_get_value(priv->cd_pin);
+#else
+	return dm_gpio_get_value(&priv->cd_gpio);
+#endif
+}
+
+#if defined(CONFIG_DM_MMC_OPS)
+static int sunxi_mmc_getcd(struct udevice *dev)
+{
+	struct mmc *mmc = mmc_get_mmc_dev(dev);
+#else
 static int sunxi_mmc_getcd(struct mmc *mmc)
 {
-	struct sunxi_mmc_host *mmchost = mmc->priv;
-	int cd_pin;
+#endif
+	struct sunxi_mmc_host *priv = mmc->priv;
+	int value = 1;
 
-	cd_pin = sunxi_mmc_getcd_gpio(mmchost->mmc_no);
-	if (cd_pin < 0)
-		return 1;
+	if (cdpin_is_valid(priv)) {
+		value = cdpin_get_value(priv);
 
-	return !gpio_get_value(cd_pin);
-}
+		if (priv->cd_inverted)
+			return !value;
+	}
 
-static const struct mmc_ops sunxi_mmc_ops = {
-	.send_cmd	= sunxi_mmc_send_cmd,
-	.set_ios	= sunxi_mmc_set_ios,
-	.init		= sunxi_mmc_core_init,
-	.getcd		= sunxi_mmc_getcd,
-};
+	return value;
+}
 
+#if !defined(CONFIG_DM_MMC)
 struct mmc *sunxi_mmc_init(int sdc_no)
 {
 	struct mmc_config *cfg = &mmc_host[sdc_no].cfg;
 
 	memset(&mmc_host[sdc_no], 0, sizeof(struct sunxi_mmc_host));
+	mmc_host[sdc_no].cd_inverted = true;
 
 	cfg->name = "SUNXI SD/MMC";
 	cfg->ops  = &sunxi_mmc_ops;
 
 	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
 	cfg->host_caps = MMC_MODE_4BIT;
 #if defined(CONFIG_MACH_SUN50I) || defined(CONFIG_MACH_SUN8I)
 	if (sdc_no == 2)
 		cfg->host_caps = MMC_MODE_8BIT;
 #endif
 	cfg->host_caps |= MMC_MODE_HS_52MHz | MMC_MODE_HS;
 	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
 
 	cfg->f_min = 400000;
 	cfg->f_max = 52000000;
 
 	if (mmc_resource_init(sdc_no) != 0)
 		return NULL;
 
-	mmc_clk_io_on(sdc_no);
+	mmc_clk_io_on(&mmc_host[sdc_no]);
 
 	return mmc_create(cfg, &mmc_host[sdc_no]);
 }
+#endif
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+static const struct dm_mmc_ops sunxi_mmc_ops = {
+	.send_cmd	= sunxi_mmc_send_cmd,
+	.set_ios	= sunxi_mmc_set_ios,
+	.get_cd		= sunxi_mmc_getcd,
+};
+#else
+static const struct mmc_ops sunxi_mmc_ops = {
+	.send_cmd	= sunxi_mmc_send_cmd,
+	.set_ios	= sunxi_mmc_set_ios,
+	.init		= sunxi_mmc_core_init,
+	.getcd		= sunxi_mmc_getcd,
+};
+#endif
+
+#if defined(CONFIG_DM_MMC)
+static int sunxi_mmc_ofdata_to_platdata(struct udevice *dev)
+{
+	return 0;
+}
+
+static int sunxi_mmc_probe(struct udevice *dev)
+{
+	struct mmc_uclass_priv *upriv = dev_get_uclass_priv(dev);
+#if defined(CONFIG_BLK)
+	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
+#endif
+	struct sunxi_mmc_host *priv = dev_get_priv(dev);
+	struct mmc_config *cfg = &priv->cfg;
+	int bus_width;
+	u32 f_minmax[2];
+
+	priv->reg = (void *)dev_get_addr(dev);
+	cfg->name = "SUNXI SD/MMC";
+#if !(defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS))
+	cfg->ops  = &sunxi_mmc_ops;
+#endif
+	cfg->voltages = MMC_VDD_32_33 | MMC_VDD_33_34;
+	cfg->b_max = CONFIG_SYS_MMC_MAX_BLK_COUNT;
+	cfg->host_caps = MMC_MODE_HS_52MHz | MMC_MODE_HS;
+
+	bus_width = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
+				   "bus-width", 4);
+	if (bus_width == 8)
+		cfg->host_caps |= MMC_MODE_8BIT;
+	else if (bus_width == 4)
+		cfg->host_caps |= MMC_MODE_4BIT;
+
+	debug("%s: reg %p bus_width %d\n", dev->name, priv->reg, bus_width);
+
+	if (!fdtdec_get_int_array(gd->fdt_blob, dev->of_offset,
+				  "clock-freq-min-max", f_minmax, 2)) {
+		cfg->f_min = f_minmax[0];
+		cfg->f_max = f_minmax[1];
+	} else {
+		/* use the defaults */
+		cfg->f_min = 400000;
+		cfg->f_max = 52000000;
+	}
+
+	/* Some legacy functionality in our tree still depends on the
+	 * mmchost->mmc_no... until we can get rid of this, initialise
+	 * it based on the base address of the device.
+	 */
+	if (mmc_resource_init_from_udev(dev) != 0)
+		return -EINVAL;
+
+	/* All GPIOs are optional */
+	gpio_request_by_name(dev, "cd-gpios", 0,
+			     &priv->cd_gpio, GPIOD_IS_IN);
+	priv->cd_inverted = fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
+					    "cd-inverted");
+	gpio_request_by_name(dev, "wp-gpios", 0,
+			     &priv->wp_gpio, GPIOD_IS_IN);
+	priv->wp_inverted = fdtdec_get_bool(gd->fdt_blob, dev->of_offset,
+					    "wp-inverted");
+	gpio_request_by_name(dev, "power-gpios", 0,
+			     &priv->pwr_gpio, GPIOD_IS_OUT);
+	if (dm_gpio_is_valid(&priv->pwr_gpio))
+		dm_gpio_set_value(&priv->pwr_gpio, 1);
+
+	if (reset_get_by_name(dev, "ahb", &priv->reset)) {
+		error("%s: failed to get 'ahb' reset\n", dev->name);
+		return -EINVAL;
+	}
+
+	if (clk_get_by_name(dev, "ahb", &priv->ahb_clk_gate) ||
+	    clk_get_by_name(dev, "mmc", &priv->mmc_clk)) {
+		error("%s: failed to get all required clocks ('ahb', 'mmc')\n",
+		      dev->name);
+		return -EINVAL;
+	}
+
+	mmc_clk_io_on(priv);
+
+#if defined(CONFIG_BLK)
+	priv->mmc = &plat->mmc;
+#else
+	priv->mmc = mmc_create(cfg, priv);
+	if (priv->mmc == NULL)
+		return -1;
+#endif
+	priv->mmc->priv = priv;
+	priv->mmc->dev = dev;
+	priv->mmc->cfg = cfg;
+	priv->mmc->has_init = 0;
+	upriv->mmc = priv->mmc;
+
+#if defined(CONFIG_DM_MMC) && defined(CONFIG_DM_MMC_OPS)
+	sunxi_mmc_core_init(priv->mmc);
+#endif
+	return 0;
+}
+
+#if defined(CONFIG_BLK)
+static int sunxi_mmc_bind(struct udevice *dev)
+{
+	struct sunxi_mmc_plat *plat = dev_get_platdata(dev);
+	struct sunxi_mmc_host *priv = dev_get_priv(dev);
+
+	debug("%s: %s\n", dev->name, __func__);
+
+	/* TODO: To move cfg into plat, we need to change the legacy
+	   code, which references through the arrays... */
+
+	return mmc_bind(dev, &plat->mmc, &priv->cfg);
+}
+#endif
+
+static const struct udevice_id sunxi_mmc_ids[] = {
+	{ .compatible = "allwinner,sun50i-a64-mmc" },
+	{ }
+};
+
+U_BOOT_DRIVER(sunxi_mmc_drv) = {
+	.name		= "sunxi_mmc",
+	.id		= UCLASS_MMC,
+	.of_match	= sunxi_mmc_ids,
+	.ofdata_to_platdata = sunxi_mmc_ofdata_to_platdata,
+	.probe		= sunxi_mmc_probe,
+	.priv_auto_alloc_size = sizeof(struct sunxi_mmc_host),
+	.platdata_auto_alloc_size = sizeof(struct sunxi_mmc_plat),
+#if defined(CONFIG_DM_MMC_OPS)
+	.ops		= &sunxi_mmc_ops,
+#endif
+#if defined(CONFIG_BLK)
+	.bind           = sunxi_mmc_bind,
+#endif
+};
+
+#endif
-- 
1.9.1

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

* [U-Boot] [PATCH v1 10/11] dts: sun50i: update mmc pin configuration and add mmc2_8bit_pins
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (8 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 09/11] sunxi_mmc: convert to a device-model driver Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 11/11] sun50i: dts: add spi0 and spi1 nodes and pinconfig Philipp Tomsich
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

---
 arch/arm/dts/sun50i-a64.dtsi | 22 ++++++++++++++++------
 1 file changed, 16 insertions(+), 6 deletions(-)

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index d592bf2..b65d033 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -1,762 +1,772 @@
 /*
  * Copyright (C) 2016 ARM Ltd.
  * based on the Allwinner H3 dtsi:
  *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
  *
  * This file is dual-licensed: you can use it either under the terms
  * of the GPL or the X11 license, at your option. Note that this dual
  * licensing only applies to this file, and not this project as a
  * whole.
  *
  *  a) This file is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License as
  *     published by the Free Software Foundation; either version 2 of the
  *     License, or (at your option) any later version.
  *
  *     This file is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     GNU General Public License for more details.
  *
  * Or, alternatively,
  *
  *  b) Permission is hereby granted, free of charge, to any person
  *     obtaining a copy of this software and associated documentation
  *     files (the "Software"), to deal in the Software without
  *     restriction, including without limitation the rights to use,
  *     copy, modify, merge, publish, distribute, sublicense, and/or
  *     sell copies of the Software, and to permit persons to whom the
  *     Software is furnished to do so, subject to the following
  *     conditions:
  *
  *     The above copyright notice and this permission notice shall be
  *     included in all copies or substantial portions of the Software.
  *
  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  *     OTHER DEALINGS IN THE SOFTWARE.
  */
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
 
 / {
 	interrupt-parent = <&gic>;
 	#address-cells = <1>;
 	#size-cells = <1>;
 
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
 
 		cpu at 0 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <0>;
 			enable-method = "psci";
 		};
 
 		cpu at 1 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <1>;
 			enable-method = "psci";
 		};
 
 		cpu at 2 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <2>;
 			enable-method = "psci";
 		};
 
 		cpu at 3 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <3>;
 			enable-method = "psci";
 		};
 	};
 
 	psci {
 		compatible = "arm,psci-0.2";
 		method = "smc";
 	};
 
 	memory: memory at 40000000 {
 		device_type = "memory";
 		reg = <0x40000000 0>;
 	};
 
 	gic: interrupt-controller at 1c81000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
 		#interrupt-cells = <3>;
 		#address-cells = <0>;
 
 		reg = <0x01c81000 0x1000>,
 		      <0x01c82000 0x2000>,
 		      <0x01c84000 0x2000>,
 		      <0x01c86000 0x2000>;
 		interrupts = <GIC_PPI 9
 		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupts = <GIC_PPI 13
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 14
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 11
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 10
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	clocks {
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		osc24M: osc24M_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <24000000>;
 			clock-output-names = "osc24M";
 		};
 
 		osc32k: osc32k_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <32768>;
 			clock-output-names = "osc32k";
 		};
 
 		pll1: pll1_clk at 1c20000 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-a23-pll1-clk";
 			reg = <0x01c20000 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll1";
 		};
 
 		pll6: pll6_clk at 1c20028 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c20028 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll6", "pll6x2";
 		};
 
 		pll6d2: pll6d2_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-factor-clock";
 			clock-div = <2>;
 			clock-mult = <1>;
 			clocks = <&pll6 0>;
 			clock-output-names = "pll6d2";
 		};
 
 		pll7: pll7_clk at 1c2002c {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c2002c 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll7", "pll7x2";
 		};
 
 		cpu: cpu_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-cpu-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
 			clock-output-names = "cpu";
 			critical-clocks = <0>;
 		};
 
 		axi: axi_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-axi-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&cpu>;
 			clock-output-names = "axi";
 		};
 
 		ahb1: ahb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun6i-a31-ahb1-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
 			clock-output-names = "ahb1";
 		};
 
 		ahb2: ahb2_clk at 1c2005c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-h3-ahb2-clk";
 			reg = <0x01c2005c 0x4>;
 			clocks = <&ahb1>, <&pll6d2>;
 			clock-output-names = "ahb2";
 		};
 
 		apb1: apb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb0-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&ahb1>;
 			clock-output-names = "apb1";
 		};
 
 		apb2: apb2_clk at 1c20058 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb1-clk";
 			reg = <0x01c20058 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>;
 			clock-output-names = "apb2";
 		};
 
 		bus_gates: bus_gates_clk at 1c20060 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun50i-a64-bus-gates-clk",
 				     "allwinner,sunxi-multi-bus-gates-clk";
 			reg = <0x01c20060 0x14>;
 			ahb1_parent {
 				clocks = <&ahb1>;
 				clock-indices = <1>, <5>,
 						<6>, <8>,
 						<9>, <10>,
 						<13>, <14>,
 						<18>, <19>,
 						<20>, <21>,
 						<23>, <24>,
 						<25>, <28>,
 						<32>, <35>,
 						<36>, <37>,
 						<40>, <43>,
 						<44>, <52>,
 						<53>, <54>,
 						<135>;
 				clock-output-names = "bus_mipidsi", "bus_ce",
 						"bus_dma", "bus_mmc0",
 						"bus_mmc1", "bus_mmc2",
 						"bus_nand", "bus_sdram",
 						"bus_ts", "bus_hstimer",
 						"bus_spi0", "bus_spi1",
 						"bus_otg", "bus_otg_ehci0",
 						"bus_ehci0", "bus_otg_ohci0",
 						"bus_ve", "bus_lcd0",
 						"bus_lcd1", "bus_deint",
 						"bus_csi", "bus_hdmi",
 						"bus_de", "bus_gpu",
 						"bus_msgbox", "bus_spinlock",
 						"bus_dbg";
 			};
 			ahb2_parent {
 				clocks = <&ahb2>;
 				clock-indices = <17>, <29>;
 				clock-output-names = "bus_gmac", "bus_ohci0";
 			};
 			apb1_parent {
 				clocks = <&apb1>;
 				clock-indices = <64>, <65>,
 						<69>, <72>,
 						<76>, <77>,
 						<78>;
 				clock-output-names = "bus_codec", "bus_spdif",
 						"bus_pio", "bus_ths",
 						"bus_i2s0", "bus_i2s1",
 						"bus_i2s2";
 			};
 			abp2_parent {
 				clocks = <&apb2>;
 				clock-indices = <96>, <97>,
 						<98>, <101>,
 						<112>, <113>,
 						<114>, <115>,
 						<116>;
 				clock-output-names = "bus_i2c0", "bus_i2c1",
 						"bus_i2c2", "bus_scr",
 						"bus_uart0", "bus_uart1",
 						"bus_uart2", "bus_uart3",
 						"bus_uart4";
 			};
 		};
 
 		mmc0_clk: mmc0_clk at 1c20088 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20088 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc0";
                 };
 
 		mmc1_clk: mmc1_clk at 1c2008c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c2008c 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc1";
 		};
 
 		mmc2_clk: mmc2_clk at 1c20090 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20090 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc2";
 		};
 	};
 
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		mmc0: mmc at 1c0f000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c0f000 0x1000>;
 			clocks = <&bus_gates 8>, <&mmc0_clk>,
 				 <&mmc0_clk>, <&mmc0_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 8>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc1: mmc at 1c10000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c10000 0x1000>;
 			clocks = <&bus_gates 9>, <&mmc1_clk>,
 				 <&mmc1_clk>, <&mmc1_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 9>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc2: mmc at 1c11000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c11000 0x1000>;
 			clocks = <&bus_gates 10>, <&mmc2_clk>,
 				 <&mmc2_clk>, <&mmc2_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 10>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		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>;
 
 			/* The A64 does not have bank A and leaves a hole in the
 			   address space where it normally would be */
 
 			gpiob: gpiob at 24 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'B'>;
 				reg = < 0x24 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioc: gpioc at 48 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x48 0x24 >;
 				allwinner,gpiobank-name = <'C'>;
 			};
 
 			gpiod: gpiod at 6c {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x6c 0x24 >;
 				allwinner,gpiobank-name = <'D'>;
 			};
 
 			gpioe: gpioe at 90 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x90 0x24 >;
 				allwinner,gpiobank-name = <'E'>;
 			};
 
 			gpiof: gpiof at b4 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xb4 0x24 >;
 				allwinner,gpiobank-name = <'F'>;
 			};
 
 			gpiog: gpiog at d8 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xd8 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'G'>;
 				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioh: gpioh at fc {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xfc 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'H'>;
 				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			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>;
 			};
 
 			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_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_2pins {
 				allwinner,pins = "PB0", "PB1";
 				allwinner,function = "uart2";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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_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_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_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_2pins {
 				allwinner,pins = "PD2", "PD3";
 				allwinner,function = "uart4";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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_pins {
 				allwinner,pins = "PF0", "PF1", "PF2", "PF3",
 						 "PF4", "PF5";
 				allwinner,function = "mmc0";
-				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
-				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+				drive-strength = < 30 >;
+				bias-pull-up;
 			};
 
 			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_pins {
 				allwinner,pins = "PG0", "PG1", "PG2", "PG3",
 						 "PG4", "PG5";
 				allwinner,function = "mmc1";
-				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
-				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+				drive-strength = < 30 >;
+				bias-pull-up;
 			};
 
 			mmc2_pins: mmc2_pins {
 				allwinner,pins = "PC1", "PC5", "PC6", "PC8",
 						 "PC9", "PC10";
 				allwinner,function = "mmc2";
-				allwinner,drive = <SUN4I_PINCTRL_30_MA>;
-				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
+				drive-strength = < 30 >;
+				bias-pull-up;
+			};
+
+			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;
 			};
 
 			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 {
 				allwinner,pins = "PH2", "PH3";
 				allwinner,function = "i2c1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PD10", "PD11", "PD13", "PD14",
 						 "PD17", "PD18", "PD19", "PD20",
 						 "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			rgmii_pins: rgmii_pins {
 				allwinner,pins = "PD8", "PD9", "PD10", "PD11",
 						 "PD12", "PD13", "PD15",
 						 "PD16", "PD17", "PD18", "PD19",
 						 "PD20", "PD21", "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 		};
 
 		r_pio: pinctrl at 01f02c00 {
 			compatible = "allwinner,sun50i-a64-r-pinctrl";
 			reg = <0x01f02c00 0x400>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 
 			gpio-controller;
 			#gpio-cells = <3>;
 
 			interrupt-controller;
 			#interrupt-cells = <2>;
 
 			#address-cells = <1>;
 			#size-cells = <1>;
 
 			gpiol: gpiol at 0 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'L'>;
 				reg = < 0x0 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 			};
 		};
 
 		ahb_rst: reset at 1c202c0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202c0 0xc>;
 		};
 
 		apb1_rst: reset at 1c202d0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d0 0x4>;
 		};
 
 		apb2_rst: reset at 1c202d8 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d8 0x4>;
 		};
 
 		uart0: serial at 1c28000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28000 0x400>;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 112>;
 			resets = <&apb2_rst 16>;
 			status = "disabled";
 		};
 
 		uart1: serial at 1c28400 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28400 0x400>;
 			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 113>;
 			resets = <&apb2_rst 17>;
 			status = "disabled";
 		};
 
 		uart2: serial at 1c28800 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28800 0x400>;
 			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 114>;
 			resets = <&apb2_rst 18>;
 			status = "disabled";
 		};
 
 		uart3: serial at 1c28c00 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28c00 0x400>;
 			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 115>;
 			resets = <&apb2_rst 19>;
 			status = "disabled";
 		};
 
 		uart4: serial at 1c29000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c29000 0x400>;
 			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 116>;
 			resets = <&apb2_rst 20>;
 			status = "disabled";
 		};
 
 		rtc: rtc at 1f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
 			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
 		i2c0: i2c at 1c2ac00 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2ac00 0x400>;
 			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 96>;
 			resets = <&apb2_rst 0>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c1: i2c at 1c2b000 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b000 0x400>;
 			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 97>;
 			resets = <&apb2_rst 1>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c2: i2c at 1c2b400 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b400 0x400>;
 			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 98>;
 			resets = <&apb2_rst 2>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		emac: ethernet at 01c30000 {
 			compatible = "allwinner,sun50i-a64-emac";
 			reg = <0x01c30000 0x2000>, <0x01c00030 0x4>;
 			reg-names = "emac", "syscon";
 			interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 			resets = <&ahb_rst 17>;
 			reset-names = "ahb";
 			clocks = <&bus_gates 17>;
 			clock-names = "ahb";
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		usbphy: phy at 1c1b810 {
 			compatible = "allwinner,sun50i-a64-usb-phy",
 				     "allwinner,sun8i-a33-usb-phy";
 			reg = <0x01c1b810 0x14>, <0x01c1b800 0x4>;
 			reg-names = "phy_ctrl", "pmu1";
 			status = "disabled";
 			#phy-cells = <1>;
 		};
 
 		ehci1: usb at 01c1b000 {
 			compatible = "allwinner,sun50i-a64-ehci",
 				     "generic-ehci";
 			reg = <0x01c1b000 0x100>;
 			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "disabled";
 		};
 
 		ohci1: usb at 01c1b400 {
 			compatible = "allwinner,sun50i-a64-ohci",
 				     "generic-ohci";
 			reg = <0x01c1b400 0x100>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "enabled";
 		};
 	};
 };
-- 
1.9.1

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

* [U-Boot] [PATCH v1 11/11] sun50i: dts: add spi0 and spi1 nodes and pinconfig
  2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
                   ` (9 preceding siblings ...)
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 10/11] dts: sun50i: update mmc pin configuration and add mmc2_8bit_pins Philipp Tomsich
@ 2017-02-17 17:52 ` Philipp Tomsich
  10 siblings, 0 replies; 19+ messages in thread
From: Philipp Tomsich @ 2017-02-17 17:52 UTC (permalink / raw)
  To: u-boot

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

diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
index b65d033..b540bd9 100644
--- a/arch/arm/dts/sun50i-a64.dtsi
+++ b/arch/arm/dts/sun50i-a64.dtsi
@@ -1,772 +1,818 @@
 /*
  * Copyright (C) 2016 ARM Ltd.
  * based on the Allwinner H3 dtsi:
  *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
  *
  * This file is dual-licensed: you can use it either under the terms
  * of the GPL or the X11 license, at your option. Note that this dual
  * licensing only applies to this file, and not this project as a
  * whole.
  *
  *  a) This file is free software; you can redistribute it and/or
  *     modify it under the terms of the GNU General Public License as
  *     published by the Free Software Foundation; either version 2 of the
  *     License, or (at your option) any later version.
  *
  *     This file is distributed in the hope that it will be useful,
  *     but WITHOUT ANY WARRANTY; without even the implied warranty of
  *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  *     GNU General Public License for more details.
  *
  * Or, alternatively,
  *
  *  b) Permission is hereby granted, free of charge, to any person
  *     obtaining a copy of this software and associated documentation
  *     files (the "Software"), to deal in the Software without
  *     restriction, including without limitation the rights to use,
  *     copy, modify, merge, publish, distribute, sublicense, and/or
  *     sell copies of the Software, and to permit persons to whom the
  *     Software is furnished to do so, subject to the following
  *     conditions:
  *
  *     The above copyright notice and this permission notice shall be
  *     included in all copies or substantial portions of the Software.
  *
  *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
  *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
  *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
  *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
  *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
  *     OTHER DEALINGS IN THE SOFTWARE.
  */
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
 #include <dt-bindings/pinctrl/sun4i-a10.h>
 
 / {
 	interrupt-parent = <&gic>;
 	#address-cells = <1>;
 	#size-cells = <1>;
 
 	cpus {
 		#address-cells = <1>;
 		#size-cells = <0>;
 
 		cpu at 0 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <0>;
 			enable-method = "psci";
 		};
 
 		cpu at 1 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <1>;
 			enable-method = "psci";
 		};
 
 		cpu at 2 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <2>;
 			enable-method = "psci";
 		};
 
 		cpu at 3 {
 			compatible = "arm,cortex-a53", "arm,armv8";
 			device_type = "cpu";
 			reg = <3>;
 			enable-method = "psci";
 		};
 	};
 
 	psci {
 		compatible = "arm,psci-0.2";
 		method = "smc";
 	};
 
 	memory: memory at 40000000 {
 		device_type = "memory";
 		reg = <0x40000000 0>;
 	};
 
 	gic: interrupt-controller at 1c81000 {
 		compatible = "arm,gic-400";
 		interrupt-controller;
 		#interrupt-cells = <3>;
 		#address-cells = <0>;
 
 		reg = <0x01c81000 0x1000>,
 		      <0x01c82000 0x2000>,
 		      <0x01c84000 0x2000>,
 		      <0x01c86000 0x2000>;
 		interrupts = <GIC_PPI 9
 		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	timer {
 		compatible = "arm,armv8-timer";
 		interrupts = <GIC_PPI 13
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 14
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 11
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
 			     <GIC_PPI 10
 			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
 	};
 
 	clocks {
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		osc24M: osc24M_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <24000000>;
 			clock-output-names = "osc24M";
 		};
 
 		osc32k: osc32k_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-clock";
 			clock-frequency = <32768>;
 			clock-output-names = "osc32k";
 		};
 
 		pll1: pll1_clk at 1c20000 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-a23-pll1-clk";
 			reg = <0x01c20000 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll1";
 		};
 
 		pll6: pll6_clk at 1c20028 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c20028 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll6", "pll6x2";
 		};
 
 		pll6d2: pll6d2_clk {
 			#clock-cells = <0>;
 			compatible = "fixed-factor-clock";
 			clock-div = <2>;
 			clock-mult = <1>;
 			clocks = <&pll6 0>;
 			clock-output-names = "pll6d2";
 		};
 
 		pll7: pll7_clk at 1c2002c {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun6i-a31-pll6-clk";
 			reg = <0x01c2002c 0x4>;
 			clocks = <&osc24M>;
 			clock-output-names = "pll7", "pll7x2";
 		};
 
 		cpu: cpu_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-cpu-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
 			clock-output-names = "cpu";
 			critical-clocks = <0>;
 		};
 
 		axi: axi_clk at 1c20050 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-axi-clk";
 			reg = <0x01c20050 0x4>;
 			clocks = <&cpu>;
 			clock-output-names = "axi";
 		};
 
 		ahb1: ahb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun6i-a31-ahb1-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
 			clock-output-names = "ahb1";
 		};
 
 		ahb2: ahb2_clk at 1c2005c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun8i-h3-ahb2-clk";
 			reg = <0x01c2005c 0x4>;
 			clocks = <&ahb1>, <&pll6d2>;
 			clock-output-names = "ahb2";
 		};
 
 		apb1: apb1_clk at 1c20054 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb0-clk";
 			reg = <0x01c20054 0x4>;
 			clocks = <&ahb1>;
 			clock-output-names = "apb1";
 		};
 
 		apb2: apb2_clk at 1c20058 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-apb1-clk";
 			reg = <0x01c20058 0x4>;
 			clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>;
 			clock-output-names = "apb2";
 		};
 
 		bus_gates: bus_gates_clk at 1c20060 {
 			#clock-cells = <1>;
 			compatible = "allwinner,sun50i-a64-bus-gates-clk",
 				     "allwinner,sunxi-multi-bus-gates-clk";
 			reg = <0x01c20060 0x14>;
 			ahb1_parent {
 				clocks = <&ahb1>;
 				clock-indices = <1>, <5>,
 						<6>, <8>,
 						<9>, <10>,
 						<13>, <14>,
 						<18>, <19>,
 						<20>, <21>,
 						<23>, <24>,
 						<25>, <28>,
 						<32>, <35>,
 						<36>, <37>,
 						<40>, <43>,
 						<44>, <52>,
 						<53>, <54>,
 						<135>;
 				clock-output-names = "bus_mipidsi", "bus_ce",
 						"bus_dma", "bus_mmc0",
 						"bus_mmc1", "bus_mmc2",
 						"bus_nand", "bus_sdram",
 						"bus_ts", "bus_hstimer",
 						"bus_spi0", "bus_spi1",
 						"bus_otg", "bus_otg_ehci0",
 						"bus_ehci0", "bus_otg_ohci0",
 						"bus_ve", "bus_lcd0",
 						"bus_lcd1", "bus_deint",
 						"bus_csi", "bus_hdmi",
 						"bus_de", "bus_gpu",
 						"bus_msgbox", "bus_spinlock",
 						"bus_dbg";
 			};
 			ahb2_parent {
 				clocks = <&ahb2>;
 				clock-indices = <17>, <29>;
 				clock-output-names = "bus_gmac", "bus_ohci0";
 			};
 			apb1_parent {
 				clocks = <&apb1>;
 				clock-indices = <64>, <65>,
 						<69>, <72>,
 						<76>, <77>,
 						<78>;
 				clock-output-names = "bus_codec", "bus_spdif",
 						"bus_pio", "bus_ths",
 						"bus_i2s0", "bus_i2s1",
 						"bus_i2s2";
 			};
 			abp2_parent {
 				clocks = <&apb2>;
 				clock-indices = <96>, <97>,
 						<98>, <101>,
 						<112>, <113>,
 						<114>, <115>,
 						<116>;
 				clock-output-names = "bus_i2c0", "bus_i2c1",
 						"bus_i2c2", "bus_scr",
 						"bus_uart0", "bus_uart1",
 						"bus_uart2", "bus_uart3",
 						"bus_uart4";
 			};
 		};
 
 		mmc0_clk: mmc0_clk at 1c20088 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20088 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc0";
                 };
 
 		mmc1_clk: mmc1_clk at 1c2008c {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c2008c 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc1";
 		};
 
 		mmc2_clk: mmc2_clk at 1c20090 {
 			#clock-cells = <0>;
 			compatible = "allwinner,sun4i-a10-mod0-clk";
 			reg = <0x01c20090 0x4>;
 			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
 			clock-output-names = "mmc2";
 		};
+
+		spi0_clk: spi0_clk at 01c200a0 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod0-clk";
+			reg = <0x01c200a0 0x4>;
+			clocks = <&osc24M>, <&pll6 0>, <&pll7 0>;
+			clock-output-names = "spi0";
+		};
+
+		spi1_clk: spi1_clk at 01c200a4 {
+			#clock-cells = <0>;
+			compatible = "allwinner,sun4i-a10-mod0-clk";
+			reg = <0x01c200a4 0x4>;
+			clocks = <&osc24M>, <&pll6 0>, <&pll7 0>;
+			clock-output-names = "spi1";
+		};
 	};
 
 	soc {
 		compatible = "simple-bus";
 		#address-cells = <1>;
 		#size-cells = <1>;
 		ranges;
 
 		mmc0: mmc at 1c0f000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c0f000 0x1000>;
 			clocks = <&bus_gates 8>, <&mmc0_clk>,
 				 <&mmc0_clk>, <&mmc0_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 8>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc1: mmc at 1c10000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c10000 0x1000>;
 			clocks = <&bus_gates 9>, <&mmc1_clk>,
 				 <&mmc1_clk>, <&mmc1_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 9>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		mmc2: mmc at 1c11000 {
 			compatible = "allwinner,sun50i-a64-mmc",
 				     "allwinner,sun5i-a13-mmc";
 			reg = <0x01c11000 0x1000>;
 			clocks = <&bus_gates 10>, <&mmc2_clk>,
 				 <&mmc2_clk>, <&mmc2_clk>;
 			clock-names = "ahb", "mmc",
 				      "output", "sample";
 			resets = <&ahb_rst 10>;
 			reset-names = "ahb";
 			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		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>;
 
 			/* The A64 does not have bank A and leaves a hole in the
 			   address space where it normally would be */
 
 			gpiob: gpiob at 24 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'B'>;
 				reg = < 0x24 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioc: gpioc at 48 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x48 0x24 >;
 				allwinner,gpiobank-name = <'C'>;
 			};
 
 			gpiod: gpiod at 6c {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x6c 0x24 >;
 				allwinner,gpiobank-name = <'D'>;
 			};
 
 			gpioe: gpioe at 90 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0x90 0x24 >;
 				allwinner,gpiobank-name = <'E'>;
 			};
 
 			gpiof: gpiof at b4 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xb4 0x24 >;
 				allwinner,gpiobank-name = <'F'>;
 			};
 
 			gpiog: gpiog at d8 {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xd8 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'G'>;
 				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			gpioh: gpioh at fc {
 				compatible = "allwinner,sunxi-gpiobank";
 				reg = < 0xfc 0x24 >, < 0x220 0x1c >;
 				allwinner,gpiobank-name = <'H'>;
 				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
 			};
 
 			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>;
 			};
 
 			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_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_2pins {
 				allwinner,pins = "PB0", "PB1";
 				allwinner,function = "uart2";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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_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_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_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_2pins {
 				allwinner,pins = "PD2", "PD3";
 				allwinner,function = "uart4";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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_pins {
 				allwinner,pins = "PF0", "PF1", "PF2", "PF3",
 						 "PF4", "PF5";
 				allwinner,function = "mmc0";
 				drive-strength = < 30 >;
 				bias-pull-up;
 			};
 
 			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_pins {
 				allwinner,pins = "PG0", "PG1", "PG2", "PG3",
 						 "PG4", "PG5";
 				allwinner,function = "mmc1";
 				drive-strength = < 30 >;
 				bias-pull-up;
 			};
 
 			mmc2_pins: mmc2_pins {
 				allwinner,pins = "PC1", "PC5", "PC6", "PC8",
 						 "PC9", "PC10";
 				allwinner,function = "mmc2";
 				drive-strength = < 30 >;
 				bias-pull-up;
 			};
 
 			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;
 			};
 
 			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 {
 				allwinner,pins = "PH2", "PH3";
 				allwinner,function = "i2c1";
 				allwinner,drive = <SUN4I_PINCTRL_10_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			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 {
 				allwinner,pins = "PD10", "PD11", "PD13", "PD14",
 						 "PD17", "PD18", "PD19", "PD20",
 						 "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
 
 			rgmii_pins: rgmii_pins {
 				allwinner,pins = "PD8", "PD9", "PD10", "PD11",
 						 "PD12", "PD13", "PD15",
 						 "PD16", "PD17", "PD18", "PD19",
 						 "PD20", "PD21", "PD22", "PD23";
 				allwinner,function = "emac";
 				allwinner,drive = <SUN4I_PINCTRL_40_MA>;
 				allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
 			};
+
+			spi0_pins: spi0_pins {
+				allwinner,pins = "PC0", "PC1", "PC2", "PC3";
+				allwinner,function = "spi0";
+			};
+
+			spi1_pins: spi1_pins {
+				allwinner,pins = "PD0", "PD1", "PD2", "PD3";
+				allwinner,function = "spi1";
+			};
 		};
 
 		r_pio: pinctrl at 01f02c00 {
 			compatible = "allwinner,sun50i-a64-r-pinctrl";
 			reg = <0x01f02c00 0x400>;
 			interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 
 			gpio-controller;
 			#gpio-cells = <3>;
 
 			interrupt-controller;
 			#interrupt-cells = <2>;
 
 			#address-cells = <1>;
 			#size-cells = <1>;
 
 			gpiol: gpiol at 0 {
 				compatible = "allwinner,sunxi-gpiobank";
 				allwinner,gpiobank-name = <'L'>;
 				reg = < 0x0 0x24 >, < 0x200 0x1c >;
 				interrupts = <GIC_SPI 45 IRQ_TYPE_LEVEL_HIGH>;
 			};
 		};
 
 		ahb_rst: reset at 1c202c0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202c0 0xc>;
 		};
 
 		apb1_rst: reset at 1c202d0 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d0 0x4>;
 		};
 
 		apb2_rst: reset at 1c202d8 {
 			#reset-cells = <1>;
 			compatible = "allwinner,sun6i-a31-clock-reset";
 			reg = <0x01c202d8 0x4>;
 		};
 
 		uart0: serial at 1c28000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28000 0x400>;
 			interrupts = <GIC_SPI 0 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 112>;
 			resets = <&apb2_rst 16>;
 			status = "disabled";
 		};
 
 		uart1: serial at 1c28400 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28400 0x400>;
 			interrupts = <GIC_SPI 1 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 113>;
 			resets = <&apb2_rst 17>;
 			status = "disabled";
 		};
 
 		uart2: serial at 1c28800 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28800 0x400>;
 			interrupts = <GIC_SPI 2 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 114>;
 			resets = <&apb2_rst 18>;
 			status = "disabled";
 		};
 
 		uart3: serial at 1c28c00 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c28c00 0x400>;
 			interrupts = <GIC_SPI 3 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 115>;
 			resets = <&apb2_rst 19>;
 			status = "disabled";
 		};
 
 		uart4: serial at 1c29000 {
 			compatible = "snps,dw-apb-uart";
 			reg = <0x01c29000 0x400>;
 			interrupts = <GIC_SPI 4 IRQ_TYPE_LEVEL_HIGH>;
 			reg-shift = <2>;
 			reg-io-width = <4>;
 			clocks = <&bus_gates 116>;
 			resets = <&apb2_rst 20>;
 			status = "disabled";
 		};
 
 		rtc: rtc at 1f00000 {
 			compatible = "allwinner,sun6i-a31-rtc";
 			reg = <0x01f00000 0x54>;
 			interrupts = <GIC_SPI 40 IRQ_TYPE_LEVEL_HIGH>,
 				     <GIC_SPI 41 IRQ_TYPE_LEVEL_HIGH>;
 		};
 
 		i2c0: i2c at 1c2ac00 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2ac00 0x400>;
 			interrupts = <GIC_SPI 6 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 96>;
 			resets = <&apb2_rst 0>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c1: i2c at 1c2b000 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b000 0x400>;
 			interrupts = <GIC_SPI 7 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 97>;
 			resets = <&apb2_rst 1>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		i2c2: i2c at 1c2b400 {
 			compatible = "allwinner,sun6i-a31-i2c";
 			reg = <0x01c2b400 0x400>;
 			interrupts = <GIC_SPI 8 IRQ_TYPE_LEVEL_HIGH>;
 			clocks = <&bus_gates 98>;
 			resets = <&apb2_rst 2>;
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		emac: ethernet at 01c30000 {
 			compatible = "allwinner,sun50i-a64-emac";
 			reg = <0x01c30000 0x2000>, <0x01c00030 0x4>;
 			reg-names = "emac", "syscon";
 			interrupts = <GIC_SPI 82 IRQ_TYPE_LEVEL_HIGH>;
 			resets = <&ahb_rst 17>;
 			reset-names = "ahb";
 			clocks = <&bus_gates 17>;
 			clock-names = "ahb";
 			status = "disabled";
 			#address-cells = <1>;
 			#size-cells = <0>;
 		};
 
 		usbphy: phy at 1c1b810 {
 			compatible = "allwinner,sun50i-a64-usb-phy",
 				     "allwinner,sun8i-a33-usb-phy";
 			reg = <0x01c1b810 0x14>, <0x01c1b800 0x4>;
 			reg-names = "phy_ctrl", "pmu1";
 			status = "disabled";
 			#phy-cells = <1>;
 		};
 
 		ehci1: usb at 01c1b000 {
 			compatible = "allwinner,sun50i-a64-ehci",
 				     "generic-ehci";
 			reg = <0x01c1b000 0x100>;
 			interrupts = <GIC_SPI 74 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "disabled";
 		};
 
 		ohci1: usb at 01c1b400 {
 			compatible = "allwinner,sun50i-a64-ohci",
 				     "generic-ohci";
 			reg = <0x01c1b400 0x100>;
 			interrupts = <GIC_SPI 75 IRQ_TYPE_LEVEL_HIGH>;
 			phys = <&usbphy 1>;
 			phy-names = "usb";
 			status = "enabled";
 		};
+
+		spi0: spi at 01c68000 {
+			compatible = "allwinner,sun8i-h3-spi";
+			reg = <0x01c68000 0x1000>;
+			clocks = <&bus_gates 20>, <&spi0_clk>;
+			clock-names = "ahb", "spi";
+			resets = <&ahb_rst 20>;
+			interrupts = <GIC_SPI 65 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
+
+		spi1: spi at 01c69000 {
+			compatible = "allwinner,sun8i-h3-spi";
+			reg = <0x01c69000 0x1000>;
+			clocks = <&bus_gates 21>, <&spi1_clk>;
+			clock-names = "ahb", "spi";
+			resets = <&ahb_rst 21>;
+			interrupts = <GIC_SPI 66 IRQ_TYPE_LEVEL_HIGH>;
+			status = "disabled";
+		};
 	};
 };
-- 
1.9.1

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

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO Philipp Tomsich
@ 2017-02-21  3:48   ` Chen-Yu Tsai
  2017-02-21  9:39     ` Dr. Philipp Tomsich
  2017-02-21 20:14   ` Maxime Ripard
  1 sibling, 1 reply; 19+ messages in thread
From: Chen-Yu Tsai @ 2017-02-21  3:48 UTC (permalink / raw)
  To: u-boot

On Sat, Feb 18, 2017 at 1:52 AM, Philipp Tomsich
<philipp.tomsich@theobroma-systems.com> 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
>  * defines and implements a binding for sunxi-style GPIO banks (to make it
>    easier to describe controllers like the A64 which may not start at 'A')
>    and provide the necessary translation mechanisms:
>    - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
>      the device framework will try to access a UCLASS_GPIO device bound to
>      the same node id as the pinctrl device to perform it's of_xlate lookup.
>      For this, we provide a 'gpiobridge' driver (which needs to access the
>      platdata of the pinctrl device) and which can then map accesses to an
>      actual GPIO bank device.
>    - For the individual GPIO banks, we use a new driver (which shares most
>      of its ops with the existing sunxi_gpio driver, except probe and bind)
>      and provides configuration without any platdata structure.

Why is the new binding and driver necessary? The existing driver in U-boot is
already DM enabled and properly xlates GPIO phandles to its child GPIO banks.
I specifically fixed this in commit 4694dc56e974 ("sunxi: gpio: Add .xlate
function for gpio phandle resolution").

You are also not using the new gpiobank nodes anywhere in your dts changes.

>  * 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)
>
> The active DM tree at runtime (with this enabled) should look similar to
> the following:
>
>      pinctrl     [ + ]    |   |-- pinctrl at 1c20800
>      gpio        [ + ]    |   |   |-- gpiob at 24
>      gpio        [ + ]    |   |   |-- gpioc at 48
>      gpio        [ + ]    |   |   |-- gpiod at 6c
>      gpio        [ + ]    |   |   |-- gpioe at 90
>      gpio        [ + ]    |   |   |-- gpiof at b4
>      gpio        [ + ]    |   |   |-- gpiog at d8
>      gpio        [ + ]    |   |   |-- gpioh at fc
>      pinconfig   [ + ]    |   |   |-- uart0_pins_a
>      pinconfig   [   ]    |   |   |-- uart0_pins_b
>      pinconfig   [   ]    |   |   |-- uart1_2pins
>      pinconfig   [   ]    |   |   |-- uart1_4pins
>      pinconfig   [   ]    |   |   |-- uart2_2pins
>      pinconfig   [   ]    |   |   |-- uart2_4
>      pinconfig   [   ]    |   |   |-- uart3
>      pinconfig   [   ]    |   |   |-- uart3_2
>      pinconfig   [   ]    |   |   |-- uart3_4
>      pinconfig   [   ]    |   |   |-- uart4_2
>      pinconfig   [   ]    |   |   |-- uart4_4
>      pinconfig   [ + ]    |   |   |-- mmc0
>      pinconfig   [ + ]    |   |   |-- mmc0_cd_pin
>      pinconfig   [   ]    |   |   |-- mmc1
>      pinconfig   [   ]    |   |   |-- mmc2
>      pinconfig   [ + ]    |   |   |-- mmc2_8bit
>      pinconfig   [   ]    |   |   |-- i2c0_pins
>      pinconfig   [   ]    |   |   |-- i2c1_pins
>      pinconfig   [   ]    |   |   |-- i2c2_pins
>      pinconfig   [   ]    |   |   |-- rmii_pins
>      pinconfig   [ + ]    |   |   |-- rgmii_pins
>      pinconfig   [ + ]    |   |   |-- spi0_pins
>      pinconfig   [   ]    |   |   |-- spi1_pins
>      pinconfig   [ + ]    |   |   |-- led_pins_sdio
>      pinconfig   [ + ]    |   |   |-- ethphy_reset_pin
>      gpio        [ + ]    |   |   `-- gpiobridge
>      pinctrl     [ + ]    |   |-- pinctrl at 01f02c00
>      gpio        [ + ]    |   |   |-- gpiol at 0
>      pinconfig   [ + ]    |   |   |-- led_pins_power
>      gpio        [ + ]    |   |   `-- gpiobridge
>
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  .../pinctrl/allwinner,pinctrl.txt                  | 130 +++++
>  drivers/gpio/sunxi_gpio.c                          |  97 +++-
>  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              | 326 ++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
>  9 files changed, 1553 insertions(+), 2 deletions(-)
>  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/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> new file mode 100644
> index 0000000..e536ea3
> --- /dev/null
> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> @@ -0,0 +1,130 @@
> +* 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.
> +
> +The Allwinner pinctrl node contains a description of the pinctrl block
> +(i.e. including GPIO and external interrupt capability, if available)
> +and subnodes describing individual GPIO banks and pin-configuration.
> +
> +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>
> +
> +Properties for the gpio sub-nodes:
> + - compatible: should be "allwinner,sunxi-gpiobank"
> + - allwinner,gpiobank-name: the name of the bank (e.g. <'A'>, <'B'>, ...)
> + - reg: offsets (within the address range of the enclosing pinctrl
> +        node's address space) and length of the registers, where
> +       * The first entry points to the mux/gpio registers.
> +       * An (optional) second entry points to the extint registers.
> +         Note, that the second entry should be provided, if the
> +         interrupt property is present.
> + - interrupt: the interrupt used for external interrupt signalling
> +              (should be one of the interrupts specified in the
> +             enclosing pinctrl node)
> +
> +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>;
> +
> +               /* The A64 does not have bank A and leaves a hole in the
> +                  address space where it normally would be */
> +
> +               gpiob: gpiob at 24 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       allwinner,gpiobank-name = <'B'>;
> +                       reg = < 0x24 0x24 >, < 0x200 0x1c >;
> +                       interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
> +               };
> +
> +               gpioc: gpioc at 48 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x48 0x24 >;
> +                       allwinner,gpiobank-name = <'C'>;
> +               };
> +
> +               gpiod: gpiod at 6c {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x6c 0x24 >;
> +                       allwinner,gpiobank-name = <'D'>;
> +               };
> +
> +               gpioe: gpioe at 90 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x90 0x24 >;
> +                       allwinner,gpiobank-name = <'E'>;
> +               };
> +
> +               gpiof: gpiof at b4 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0xb4 0x24 >;
> +                       allwinner,gpiobank-name = <'F'>;
> +               };
> +
> +               gpiog: gpiog at d8 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0xd8 0x24 >, < 0x220 0x1c >;
> +                       allwinner,gpiobank-name = <'G'>;
> +                       interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> +               };
> +
> +               gpioh: gpioh at fc {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0xfc 0x24 >, < 0x220 0x1c >;
> +                       allwinner,gpiobank-name = <'H'>;
> +                       interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
> +               };
> +
> +               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..6cf2be4 100644
> --- a/drivers/gpio/sunxi_gpio.c
> +++ b/drivers/gpio/sunxi_gpio.c
> @@ -344,36 +344,129 @@ static const struct sunxi_gpio_soc_data soc_data_l_3 = {
>  static const struct udevice_id sunxi_gpio_ids[] = {
>         ID("allwinner,sun4i-a10-pinctrl",       a_all),
>         ID("allwinner,sun5i-a10s-pinctrl",      a_all),
>         ID("allwinner,sun5i-a13-pinctrl",       a_all),
>         ID("allwinner,sun6i-a31-pinctrl",       a_all),
>         ID("allwinner,sun6i-a31s-pinctrl",      a_all),
>         ID("allwinner,sun7i-a20-pinctrl",       a_all),
>         ID("allwinner,sun8i-a23-pinctrl",       a_all),
>         ID("allwinner,sun8i-a33-pinctrl",       a_all),
>         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
>         { }
>  };
>
>  U_BOOT_DRIVER(gpio_sunxi) = {
>         .name   = "gpio_sunxi",
>         .id     = UCLASS_GPIO,
>         .ops    = &gpio_sunxi_ops,
>         .of_match = sunxi_gpio_ids,
>         .bind   = gpio_sunxi_bind,
>         .probe  = gpio_sunxi_probe,
>  };
> +
> +#if defined(CONFIG_SUNXI_PINCTRL)
> +
> +/* When we use the sunxi pinctrl infrastructure, we have two issues that
> + * are resolved by the gpiobank and gpiobrige drivers:
> + *
> + *  - We need to have a UCLASS_GPIO device bound to the pinctrl-nodes for
> + *    translating between gpio entries (e.g. <&pio 3 24 GPIO_ACTIVE_LOW>;)
> + *    in the DT and actual gpio banks. This is done using the gpiobridge
> + *    driver, which provides only a lookup/translation mechanism.
> + *    This mechanism lives in pinctrl-sunxi.c
> + *
> + *  - We introduce a generic gpiobank device, which resolves the need to
> + *    have distinct soc_data structure for each device and avoids having
> + *    to share data structures and config data between this file and the
> + *    sunxi pinctrl (the other option would be to have the soc_data info
> + *    visible in pinctrl-sunxi.c (or merge it into this file) and bind a
> + *    gpio_sunxi device that is set up with the appropriate soc_data) to
> + *    the same node as the pinctrl device.

Pushing hardware internals into the DT is not preferred.

Since the pinctrl and gpio drivers actually driver the same hardware block,
just in different ways, and there should be some locking between the two,
I think it would make sense to merge the two.

You can also get away with not having soc_data by just registering the full
set of GPIO banks and pins (we aren't counting extra pins anyway).

Moreover I think the gpiobank issue is just a limitation of U-boot's GPIO
implementation.

Regards
ChenYu

> + */
> +
> +static int gpiobank_sunxi_probe(struct udevice *dev)
> +{
> +       struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
> +       struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> +       int bank_name;
> +       int bank;
> +       fdt_addr_t offset;
> +       fdt_size_t size;
> +       fdt_addr_t base;
> +
> +       debug("%s: %s", __func__, dev->name);
> +
> +       /* At this time we are only interested in index 0 (the PIO registers)
> +          and we ignore index 1 (the external interrupt control), even if
> +          present and an interrupt property exists... */
> +       offset = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
> +                                                   dev->of_offset,
> +                                                   "reg", 0, &size,
> +                                                   false);
> +       if (offset == FDT_ADDR_T_NONE) {
> +               error("%s: missing 'reg' for offset into parent device\n",
> +                     dev->name);
> +               return -EINVAL;
> +       }
> +
> +       bank_name = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
> +                                  "allwinner,gpiobank-name", -EINVAL);
> +       if (bank_name == -EINVAL) {
> +               error("%s: missing 'allwinner,gpiobank-name'\n", dev->name);
> +               return -EINVAL;
> +       }
> +
> +       base = dev_get_addr(dev->parent);
> +       if (base == FDT_ADDR_T_NONE) {
> +               error("%s: parent '%s' does not have a valid base address\n",
> +                     dev->name, dev->parent->name);
> +               return -EINVAL;
> +       }
> +
> +       bank = bank_name - 'A';
> +
> +       plat->regs = (void *)(base + offset);
> +       plat->gpio_count = SUNXI_GPIOS_PER_BANK;
> +       plat->bank_name = gpio_bank_name(bank);
> +
> +       /* Tell the uclass how many GPIOs we have */
> +       uc_priv->gpio_count = plat->gpio_count;
> +       uc_priv->bank_name = plat->bank_name;
> +
> +       return 0;
> +}
> +
> +static const struct udevice_id sunxi_gpiobank_ids[] = {
> +       { .compatible = "allwinner,sunxi-gpiobank", },
> +       {}
> +};
> +
> +U_BOOT_DRIVER(gpiobank_sunxi) = {
> +       .name   = "gpiobank_sunxi",
> +       .id     = UCLASS_GPIO,
> +       .ops    = &gpio_sunxi_ops,
> +       .of_match = sunxi_gpiobank_ids,
> +       .platdata_auto_alloc_size = sizeof(struct sunxi_gpio_platdata),
> +       .probe  = gpiobank_sunxi_probe,
> +};
> +
> +#endif /* CONFIG_SUNXI_PINCTRL */
> +
>  #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
> @@ -1,18 +1,20 @@
>  #
>  # SPDX-License-Identifier:     GPL-2.0+
>  #
>
>  obj-y                                  += pinctrl-uclass.o
>  obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC)   += pinctrl-generic.o
>
>  obj-$(CONFIG_PINCTRL_AT91PIO4)         += pinctrl-at91-pio4.o
>  obj-y                                  += nxp/
>  obj-$(CONFIG_ARCH_ATH79) += ath79/
>  obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
>  obj-$(CONFIG_PINCTRL_SANDBOX)  += pinctrl-sandbox.o
>
>  obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
>  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..36579b1
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> @@ -0,0 +1,326 @@
> +/*
> + * (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 <dm/lists.h>
> +#include <dm/pinctrl.h>
> +#include <dt-bindings/pinctrl/sun4i-a10.h>
> +#include <dt-bindings/gpio/gpio.h>
> +#include "pinctrl-sunxi.h"
> +
> +DECLARE_GLOBAL_DATA_PTR;
> +
> +struct sunxi_pctrl_priv {
> +       void *base;
> +};
> +
> +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 int sunxi_pctrl_probe(struct udevice *dev)
> +{
> +       struct sunxi_pctrl_priv *priv = dev_get_priv(dev);
> +#if CONFIG_DM_GPIO
> +       struct udevice *gpiobridge;
> +#endif
> +       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;
> +
> +#if CONFIG_DM_GPIO
> +       device_bind_driver_to_node(dev, "sunxi_pctrl_gpiobridge",
> +                                  "gpiobridge", dev->of_offset, &gpiobridge);
> +
> +       /* The new device will have been created with no driver data,
> +          so we need to set it here, as it contains the pin_base of
> +          the gpio-group.  */
> +       if (gpiobridge)
> +               gpiobridge->driver_data = dev_get_driver_data(dev);
> +#endif
> +
> +       return 0;
> +}
> +
> +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,
> +};
> +
> +
> +#if defined(CONFIG_DM_GPIO)
> +/* The gpiobridge exists to translate <&pio 3 24 GPIO_ACTIVE_LOW> into the
> +   underlying GPIO bank.  It needs to access/understand the driver-data for
> +   the pinctrl device, so it lives here instead of in sunxi_gpio.c ... */
> +
> +static int sunxi_gpiobridge_xlate(struct udevice *dev, struct gpio_desc *desc,
> +                                 struct fdtdec_phandle_args *args)
> +{
> +       const struct sunxi_pinctrl_desc *data =
> +               (struct sunxi_pinctrl_desc *)dev_get_driver_data(dev);
> +       struct udevice *gpiobank;
> +       char name[3] = {'P', 'A', '\0' };
> +
> +       if (!data) {
> +               debug("%s: no driver_data\n", dev->name);
> +               return -ENOENT;
> +       }
> +
> +       /* Naming on each pinctrl may start with an offset (e.g. R_PIO
> +          usually starts at 'PL'). */
> +       name[1] += data->pin_base / PINS_PER_BANK;
> +       /* Now add the bank-number within this pinctrl */
> +       name[1] += args->args[0];
> +
> +       for (uclass_first_device(UCLASS_GPIO, &gpiobank);
> +            gpiobank;
> +            uclass_next_device(&gpiobank)) {
> +               int count;
> +               const char *bank_name = gpio_get_bank_info(gpiobank, &count);
> +
> +               if (bank_name && !strcmp(bank_name, name)) {
> +                       desc->dev = gpiobank;
> +                       desc->offset = args->args[1];
> +                       desc->flags = args->args[2] & (GPIO_ACTIVE_LOW ?
> +                                                      GPIOD_ACTIVE_LOW : 0);
> +                       return 0;
> +               }
> +       }
> +
> +       return -ENOENT;
> +}
> +
> +static const struct dm_gpio_ops gpiobridge_sunxi_ops = {
> +       .xlate          = sunxi_gpiobridge_xlate,
> +};
> +
> +U_BOOT_DRIVER(gpiobridge_sunxi) = {
> +       .name           = "sunxi_pctrl_gpiobridge",
> +       .id             = UCLASS_GPIO,
> +       .ops            = &gpiobridge_sunxi_ops,
> +};
> +#endif /* DM_GPIO */
> 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	[flat|nested] 19+ messages in thread

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-21  3:48   ` Chen-Yu Tsai
@ 2017-02-21  9:39     ` Dr. Philipp Tomsich
  2017-02-22  6:07       ` Chen-Yu Tsai
  0 siblings, 1 reply; 19+ messages in thread
From: Dr. Philipp Tomsich @ 2017-02-21  9:39 UTC (permalink / raw)
  To: u-boot

Chen,

> On 21 Feb 2017, at 04:48, Chen-Yu Tsai <wens@csie.org> wrote:
> 
> On Sat, Feb 18, 2017 at 1:52 AM, Philipp Tomsich
> <philipp.tomsich at theobroma-systems.com <mailto:philipp.tomsich@theobroma-systems.com>> 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
>> * defines and implements a binding for sunxi-style GPIO banks (to make it
>>   easier to describe controllers like the A64 which may not start at 'A')
>>   and provide the necessary translation mechanisms:
>>   - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
>>     the device framework will try to access a UCLASS_GPIO device bound to
>>     the same node id as the pinctrl device to perform it's of_xlate lookup.
>>     For this, we provide a 'gpiobridge' driver (which needs to access the
>>     platdata of the pinctrl device) and which can then map accesses to an
>>     actual GPIO bank device.
>>   - For the individual GPIO banks, we use a new driver (which shares most
>>     of its ops with the existing sunxi_gpio driver, except probe and bind)
>>     and provides configuration without any platdata structure.
> 
> Why is the new binding and driver necessary? The existing driver in U-boot is
> already DM enabled and properly xlates GPIO phandles to its child GPIO banks.
> I specifically fixed this in commit 4694dc56e974 ("sunxi: gpio: Add .xlate
> function for gpio phandle resolution?).

The problem is that all GPIO is referenced to the pinctrl-node.  As we need to
provide a pinctrl driver (so the configuration of pins can go through that) for
the pinconfig to work, the GPIOs are then referenced to that node.

We had considered multiple solutions
(a)	the one we?ve chosen, which avoids having to provide additional data
	outside the device-tree to track the number of banks and bank-size
(b)	binding the existing gpio driver to the same node (but as the driverdata
	needs to be matched as well, we?d need to select this based on the
	compatible string and have a tighter coupling between the drivers than
	strictly necessary)
(c)	dynamically creating gpio subnodes (just as done by the top-level
	sunxi_gpio instances) for each of the banks from within the pinctrl driver

Another reason why we felt the gpiobank to be helpful is that it allows us to
easily describe where the register sets (i.e. PIO and IRQ) are for each bank.

> You are also not using the new gpiobank nodes anywhere in your dts changes.

The DTS change is in the same patch as the the pinctrl driver:
	http://lists.denx.de/pipermail/u-boot/2017-February/281637.html <http://lists.denx.de/pipermail/u-boot/2017-February/281637.html>

I had generated this with full function context, which makes the diff in the DTS
hard to spot.

>> +#if defined(CONFIG_SUNXI_PINCTRL)
>> +
>> +/* When we use the sunxi pinctrl infrastructure, we have two issues that
>> + * are resolved by the gpiobank and gpiobrige drivers:
>> + *
>> + *  - We need to have a UCLASS_GPIO device bound to the pinctrl-nodes for
>> + *    translating between gpio entries (e.g. <&pio 3 24 GPIO_ACTIVE_LOW>;)
>> + *    in the DT and actual gpio banks. This is done using the gpiobridge
>> + *    driver, which provides only a lookup/translation mechanism.
>> + *    This mechanism lives in pinctrl-sunxi.c
>> + *
>> + *  - We introduce a generic gpiobank device, which resolves the need to
>> + *    have distinct soc_data structure for each device and avoids having
>> + *    to share data structures and config data between this file and the
>> + *    sunxi pinctrl (the other option would be to have the soc_data info
>> + *    visible in pinctrl-sunxi.c (or merge it into this file) and bind a
>> + *    gpio_sunxi device that is set up with the appropriate soc_data) to
>> + *    the same node as the pinctrl device.
> 
> Pushing hardware internals into the DT is not preferred.

That?s exactly the point: the current DT format encapsulates the internal
grouping of the hardware blocks into larger address-decoding groups. For
the case of pinctrl and GPIO, the actual structure looks as follows:
	+ ?bunch of registers?
			+ N x [pinctrl functionality for a pin-group]
			+ N x [irq-control for a pin-group]
			+ N x [GPIO functionality for a pin-group]

So if I were asked to model this from scratch, I?d use a regmap-device for
the PIO and R_PIO address spaces and then reference the pinctrl and gpio
functionality back to it (hiding the internals of how each bank assigns bits
and where).

> Since the pinctrl and gpio drivers actually driver the same hardware block,
> just in different ways, and there should be some locking between the two,
> I think it would make sense to merge the two.
> 
> You can also get away with not having soc_data by just registering the full
> set of GPIO banks and pins (we aren't counting extra pins anyway).

Merging the pinctrl and gpio drivers is going to be tricky, as a driver can only
have a single class.

The more immediate solution would be to simply derive the appropriate driver
data for the existing gpio class and manually bind a driver to the node (see 
option ?b? from above).

This will then require sharing the (internal) driver data structure from the
gpio-driver with pinctrl and populating it from the pinctrl probe? i.e. we had
this in an earlier version of the changes, but it looked messy.

Before we revert to this model, I?ll wait if a consensus emerges from the
discussion here.


Regards,
Philipp.

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

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO Philipp Tomsich
  2017-02-21  3:48   ` Chen-Yu Tsai
@ 2017-02-21 20:14   ` Maxime Ripard
  1 sibling, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2017-02-21 20:14 UTC (permalink / raw)
  To: u-boot

Hi Philipp,

On Fri, Feb 17, 2017 at 06:52:39PM +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
>  * defines and implements a binding for sunxi-style GPIO banks (to make it
>    easier to describe controllers like the A64 which may not start at 'A')
>    and provide the necessary translation mechanisms:
>    - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
>      the device framework will try to access a UCLASS_GPIO device bound to
>      the same node id as the pinctrl device to perform it's of_xlate lookup.
>      For this, we provide a 'gpiobridge' driver (which needs to access the
>      platdata of the pinctrl device) and which can then map accesses to an
>      actual GPIO bank device.
>    - For the individual GPIO banks, we use a new driver (which shares most
>      of its ops with the existing sunxi_gpio driver, except probe and bind)
>      and provides configuration without any platdata structure.
>  * 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)
> 
> The active DM tree at runtime (with this enabled) should look similar to
> the following:
> 
>      pinctrl     [ + ]    |   |-- pinctrl at 1c20800
>      gpio        [ + ]    |   |   |-- gpiob at 24
>      gpio        [ + ]    |   |   |-- gpioc at 48
>      gpio        [ + ]    |   |   |-- gpiod at 6c
>      gpio        [ + ]    |   |   |-- gpioe at 90
>      gpio        [ + ]    |   |   |-- gpiof at b4
>      gpio        [ + ]    |   |   |-- gpiog at d8
>      gpio        [ + ]    |   |   |-- gpioh at fc
>      pinconfig   [ + ]    |   |   |-- uart0_pins_a
>      pinconfig   [   ]    |   |   |-- uart0_pins_b
>      pinconfig   [   ]    |   |   |-- uart1_2pins
>      pinconfig   [   ]    |   |   |-- uart1_4pins
>      pinconfig   [   ]    |   |   |-- uart2_2pins
>      pinconfig   [   ]    |   |   |-- uart2_4
>      pinconfig   [   ]    |   |   |-- uart3
>      pinconfig   [   ]    |   |   |-- uart3_2
>      pinconfig   [   ]    |   |   |-- uart3_4
>      pinconfig   [   ]    |   |   |-- uart4_2
>      pinconfig   [   ]    |   |   |-- uart4_4
>      pinconfig   [ + ]    |   |   |-- mmc0
>      pinconfig   [ + ]    |   |   |-- mmc0_cd_pin
>      pinconfig   [   ]    |   |   |-- mmc1
>      pinconfig   [   ]    |   |   |-- mmc2
>      pinconfig   [ + ]    |   |   |-- mmc2_8bit
>      pinconfig   [   ]    |   |   |-- i2c0_pins
>      pinconfig   [   ]    |   |   |-- i2c1_pins
>      pinconfig   [   ]    |   |   |-- i2c2_pins
>      pinconfig   [   ]    |   |   |-- rmii_pins
>      pinconfig   [ + ]    |   |   |-- rgmii_pins
>      pinconfig   [ + ]    |   |   |-- spi0_pins
>      pinconfig   [   ]    |   |   |-- spi1_pins
>      pinconfig   [ + ]    |   |   |-- led_pins_sdio
>      pinconfig   [ + ]    |   |   |-- ethphy_reset_pin
>      gpio        [ + ]    |   |   `-- gpiobridge
>      pinctrl     [ + ]    |   |-- pinctrl at 01f02c00
>      gpio        [ + ]    |   |   |-- gpiol at 0
>      pinconfig   [ + ]    |   |   |-- led_pins_power
>      gpio        [ + ]    |   |   `-- gpiobridge
> 
> Signed-off-by: Philipp Tomsich <philipp.tomsich@theobroma-systems.com>
> ---
>  .../pinctrl/allwinner,pinctrl.txt                  | 130 +++++
>  drivers/gpio/sunxi_gpio.c                          |  97 +++-
>  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              | 326 ++++++++++++
>  drivers/pinctrl/sunxi/pinctrl-sunxi.h              | 311 +++++++++++
>  9 files changed, 1553 insertions(+), 2 deletions(-)
>  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/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> new file mode 100644
> index 0000000..e536ea3
> --- /dev/null
> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> @@ -0,0 +1,130 @@
> +* 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.
> +
> +The Allwinner pinctrl node contains a description of the pinctrl block
> +(i.e. including GPIO and external interrupt capability, if available)
> +and subnodes describing individual GPIO banks and pin-configuration.
> +
> +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>
> +
> +Properties for the gpio sub-nodes:
> + - compatible: should be "allwinner,sunxi-gpiobank"
> + - allwinner,gpiobank-name: the name of the bank (e.g. <'A'>, <'B'>, ...)
> + - reg: offsets (within the address range of the enclosing pinctrl
> +        node's address space) and length of the registers, where
> +	* The first entry points to the mux/gpio registers.
> +	* An (optional) second entry points to the extint registers.
> +	  Note, that the second entry should be provided, if the
> +	  interrupt property is present.
> + - interrupt: the interrupt used for external interrupt signalling
> +              (should be one of the interrupts specified in the
> +	      enclosing pinctrl node)
> +
> +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>;
> +
> +		/* The A64 does not have bank A and leaves a hole in the
> +		   address space where it normally would be */
> +
> +		gpiob: gpiob at 24 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			allwinner,gpiobank-name = <'B'>;
> +			reg = < 0x24 0x24 >, < 0x200 0x1c >;
> +			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
> +		};
> +
> +		gpioc: gpioc at 48 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x48 0x24 >;
> +			allwinner,gpiobank-name = <'C'>;
> +		};
> +
> +		gpiod: gpiod at 6c {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x6c 0x24 >;
> +			allwinner,gpiobank-name = <'D'>;
> +		};
> +
> +		gpioe: gpioe at 90 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x90 0x24 >;
> +			allwinner,gpiobank-name = <'E'>;
> +		};
> +
> +		gpiof: gpiof at b4 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0xb4 0x24 >;
> +			allwinner,gpiobank-name = <'F'>;
> +		};
> +
> +		gpiog: gpiog at d8 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0xd8 0x24 >, < 0x220 0x1c >;
> +			allwinner,gpiobank-name = <'G'>;
> +			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> +		};
> +
> +		gpioh: gpioh at fc {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0xfc 0x24 >, < 0x220 0x1c >;
> +			allwinner,gpiobank-name = <'H'>;
> +			interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
> +		};
> +
> +		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>;
> +		};
> +	};

Unfortunately, we're using the DT bindings coming from Linux, and this
is not the bindings used there. For every new bindings, this should be
submitted, reviewed and accepted first by the DT maintainers there.

In this case, there's no particular need to make that addition in the
first place, since we can use some other means to implement that.

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/20170221/a584ee29/attachment.sig>

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

* [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
@ 2017-02-21 20:15   ` Maxime Ripard
  0 siblings, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2017-02-21 20:15 UTC (permalink / raw)
  To: u-boot

Hi,

On Fri, Feb 17, 2017 at 06:52:41PM +0100, Philipp Tomsich wrote:
> 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>

Can you submit it to Linux as well?

> ---
>  arch/arm/dts/sun50i-a64.dtsi | 30 +++++++++++++++---------------
>  1 file changed, 15 insertions(+), 15 deletions(-)
> 
> diff --git a/arch/arm/dts/sun50i-a64.dtsi b/arch/arm/dts/sun50i-a64.dtsi
> index efed838..d592bf2 100644
> --- a/arch/arm/dts/sun50i-a64.dtsi
> +++ b/arch/arm/dts/sun50i-a64.dtsi
> @@ -1,762 +1,762 @@
>  /*
>   * Copyright (C) 2016 ARM Ltd.
>   * based on the Allwinner H3 dtsi:
>   *    Copyright (C) 2015 Jens Kuske <jenskuske@gmail.com>
>   *
>   * This file is dual-licensed: you can use it either under the terms
>   * of the GPL or the X11 license, at your option. Note that this dual
>   * licensing only applies to this file, and not this project as a
>   * whole.
>   *
>   *  a) This file is free software; you can redistribute it and/or
>   *     modify it under the terms of the GNU General Public License as
>   *     published by the Free Software Foundation; either version 2 of the
>   *     License, or (at your option) any later version.
>   *
>   *     This file is distributed in the hope that it will be useful,
>   *     but WITHOUT ANY WARRANTY; without even the implied warranty of
>   *     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>   *     GNU General Public License for more details.
>   *
>   * Or, alternatively,
>   *
>   *  b) Permission is hereby granted, free of charge, to any person
>   *     obtaining a copy of this software and associated documentation
>   *     files (the "Software"), to deal in the Software without
>   *     restriction, including without limitation the rights to use,
>   *     copy, modify, merge, publish, distribute, sublicense, and/or
>   *     sell copies of the Software, and to permit persons to whom the
>   *     Software is furnished to do so, subject to the following
>   *     conditions:
>   *
>   *     The above copyright notice and this permission notice shall be
>   *     included in all copies or substantial portions of the Software.
>   *
>   *     THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
>   *     EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
>   *     OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
>   *     NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
>   *     HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
>   *     WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
>   *     FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
>   *     OTHER DEALINGS IN THE SOFTWARE.
>   */
>  
>  #include <dt-bindings/interrupt-controller/arm-gic.h>
>  #include <dt-bindings/pinctrl/sun4i-a10.h>
>  
>  / {
>  	interrupt-parent = <&gic>;
>  	#address-cells = <1>;
>  	#size-cells = <1>;
>  
>  	cpus {
>  		#address-cells = <1>;
>  		#size-cells = <0>;
>  
>  		cpu at 0 {
>  			compatible = "arm,cortex-a53", "arm,armv8";
>  			device_type = "cpu";
>  			reg = <0>;
>  			enable-method = "psci";
>  		};
>  
>  		cpu at 1 {
>  			compatible = "arm,cortex-a53", "arm,armv8";
>  			device_type = "cpu";
>  			reg = <1>;
>  			enable-method = "psci";
>  		};
>  
>  		cpu at 2 {
>  			compatible = "arm,cortex-a53", "arm,armv8";
>  			device_type = "cpu";
>  			reg = <2>;
>  			enable-method = "psci";
>  		};
>  
>  		cpu at 3 {
>  			compatible = "arm,cortex-a53", "arm,armv8";
>  			device_type = "cpu";
>  			reg = <3>;
>  			enable-method = "psci";
>  		};
>  	};
>  
>  	psci {
>  		compatible = "arm,psci-0.2";
>  		method = "smc";
>  	};
>  
> -	memory {
> +	memory: memory at 40000000 {
>  		device_type = "memory";
>  		reg = <0x40000000 0>;
>  	};
>  
>  	gic: interrupt-controller at 1c81000 {
>  		compatible = "arm,gic-400";
>  		interrupt-controller;
>  		#interrupt-cells = <3>;
>  		#address-cells = <0>;
>  
>  		reg = <0x01c81000 0x1000>,
>  		      <0x01c82000 0x2000>,
>  		      <0x01c84000 0x2000>,
>  		      <0x01c86000 0x2000>;
>  		interrupts = <GIC_PPI 9
>  		      (GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
>  	};
>  
>  	timer {
>  		compatible = "arm,armv8-timer";
>  		interrupts = <GIC_PPI 13
>  			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
>  			     <GIC_PPI 14
>  			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
>  			     <GIC_PPI 11
>  			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>,
>  			     <GIC_PPI 10
>  			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
>  	};
>  
>  	clocks {
>  		#address-cells = <1>;
>  		#size-cells = <1>;
>  		ranges;
>  
>  		osc24M: osc24M_clk {
>  			#clock-cells = <0>;
>  			compatible = "fixed-clock";
>  			clock-frequency = <24000000>;
>  			clock-output-names = "osc24M";
>  		};
>  
>  		osc32k: osc32k_clk {
>  			#clock-cells = <0>;
>  			compatible = "fixed-clock";
>  			clock-frequency = <32768>;
>  			clock-output-names = "osc32k";
>  		};
>  
>  		pll1: pll1_clk at 1c20000 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun8i-a23-pll1-clk";
>  			reg = <0x01c20000 0x4>;
>  			clocks = <&osc24M>;
>  			clock-output-names = "pll1";
>  		};
>  
>  		pll6: pll6_clk at 1c20028 {
>  			#clock-cells = <1>;
>  			compatible = "allwinner,sun6i-a31-pll6-clk";
>  			reg = <0x01c20028 0x4>;
>  			clocks = <&osc24M>;
>  			clock-output-names = "pll6", "pll6x2";
>  		};
>  
>  		pll6d2: pll6d2_clk {
>  			#clock-cells = <0>;
>  			compatible = "fixed-factor-clock";
>  			clock-div = <2>;
>  			clock-mult = <1>;
>  			clocks = <&pll6 0>;
>  			clock-output-names = "pll6d2";
>  		};
>  
>  		pll7: pll7_clk at 1c2002c {
>  			#clock-cells = <1>;
>  			compatible = "allwinner,sun6i-a31-pll6-clk";
>  			reg = <0x01c2002c 0x4>;
>  			clocks = <&osc24M>;
>  			clock-output-names = "pll7", "pll7x2";
>  		};
>  
>  		cpu: cpu_clk at 1c20050 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-cpu-clk";
>  			reg = <0x01c20050 0x4>;
>  			clocks = <&osc32k>, <&osc24M>, <&pll1>, <&pll1>;
>  			clock-output-names = "cpu";
>  			critical-clocks = <0>;
>  		};
>  
>  		axi: axi_clk at 1c20050 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-axi-clk";
>  			reg = <0x01c20050 0x4>;
>  			clocks = <&cpu>;
>  			clock-output-names = "axi";
>  		};
>  
>  		ahb1: ahb1_clk at 1c20054 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun6i-a31-ahb1-clk";
>  			reg = <0x01c20054 0x4>;
>  			clocks = <&osc32k>, <&osc24M>, <&axi>, <&pll6 0>;
>  			clock-output-names = "ahb1";
>  		};
>  
>  		ahb2: ahb2_clk at 1c2005c {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun8i-h3-ahb2-clk";
>  			reg = <0x01c2005c 0x4>;
>  			clocks = <&ahb1>, <&pll6d2>;
>  			clock-output-names = "ahb2";
>  		};
>  
>  		apb1: apb1_clk at 1c20054 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-apb0-clk";
>  			reg = <0x01c20054 0x4>;
>  			clocks = <&ahb1>;
>  			clock-output-names = "apb1";
>  		};
>  
>  		apb2: apb2_clk at 1c20058 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-apb1-clk";
>  			reg = <0x01c20058 0x4>;
>  			clocks = <&osc32k>, <&osc24M>, <&pll6 1>, <&pll6 1>;
>  			clock-output-names = "apb2";
>  		};
>  
>  		bus_gates: bus_gates_clk at 1c20060 {
>  			#clock-cells = <1>;
>  			compatible = "allwinner,sun50i-a64-bus-gates-clk",
>  				     "allwinner,sunxi-multi-bus-gates-clk";
>  			reg = <0x01c20060 0x14>;
>  			ahb1_parent {
>  				clocks = <&ahb1>;
>  				clock-indices = <1>, <5>,
>  						<6>, <8>,
>  						<9>, <10>,
>  						<13>, <14>,
>  						<18>, <19>,
>  						<20>, <21>,
>  						<23>, <24>,
>  						<25>, <28>,
>  						<32>, <35>,
>  						<36>, <37>,
>  						<40>, <43>,
>  						<44>, <52>,
>  						<53>, <54>,
>  						<135>;
>  				clock-output-names = "bus_mipidsi", "bus_ce",
>  						"bus_dma", "bus_mmc0",
>  						"bus_mmc1", "bus_mmc2",
>  						"bus_nand", "bus_sdram",
>  						"bus_ts", "bus_hstimer",
>  						"bus_spi0", "bus_spi1",
>  						"bus_otg", "bus_otg_ehci0",
>  						"bus_ehci0", "bus_otg_ohci0",
>  						"bus_ve", "bus_lcd0",
>  						"bus_lcd1", "bus_deint",
>  						"bus_csi", "bus_hdmi",
>  						"bus_de", "bus_gpu",
>  						"bus_msgbox", "bus_spinlock",
>  						"bus_dbg";
>  			};
>  			ahb2_parent {
>  				clocks = <&ahb2>;
>  				clock-indices = <17>, <29>;
>  				clock-output-names = "bus_gmac", "bus_ohci0";
>  			};
>  			apb1_parent {
>  				clocks = <&apb1>;
>  				clock-indices = <64>, <65>,
>  						<69>, <72>,
>  						<76>, <77>,
>  						<78>;
>  				clock-output-names = "bus_codec", "bus_spdif",
>  						"bus_pio", "bus_ths",
>  						"bus_i2s0", "bus_i2s1",
>  						"bus_i2s2";
>  			};
>  			abp2_parent {
>  				clocks = <&apb2>;
>  				clock-indices = <96>, <97>,
>  						<98>, <101>,
>  						<112>, <113>,
>  						<114>, <115>,
>  						<116>;
>  				clock-output-names = "bus_i2c0", "bus_i2c1",
>  						"bus_i2c2", "bus_scr",
>  						"bus_uart0", "bus_uart1",
>  						"bus_uart2", "bus_uart3",
>  						"bus_uart4";
>  			};
>  		};
>  
>  		mmc0_clk: mmc0_clk at 1c20088 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-mod0-clk";
>  			reg = <0x01c20088 0x4>;
>  			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
>  			clock-output-names = "mmc0";
>                  };
>  
>  		mmc1_clk: mmc1_clk at 1c2008c {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-mod0-clk";
>  			reg = <0x01c2008c 0x4>;
>  			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
>  			clock-output-names = "mmc1";
>  		};
>  
>  		mmc2_clk: mmc2_clk at 1c20090 {
>  			#clock-cells = <0>;
>  			compatible = "allwinner,sun4i-a10-mod0-clk";
>  			reg = <0x01c20090 0x4>;
>  			clocks = <&osc24M>, <&pll6 1>, <&pll7 1>;
>  			clock-output-names = "mmc2";
>  		};
>  	};
>  
>  	soc {
>  		compatible = "simple-bus";
>  		#address-cells = <1>;
>  		#size-cells = <1>;
>  		ranges;
>  
>  		mmc0: mmc at 1c0f000 {
>  			compatible = "allwinner,sun50i-a64-mmc",
>  				     "allwinner,sun5i-a13-mmc";
>  			reg = <0x01c0f000 0x1000>;
>  			clocks = <&bus_gates 8>, <&mmc0_clk>,
>  				 <&mmc0_clk>, <&mmc0_clk>;
>  			clock-names = "ahb", "mmc",
>  				      "output", "sample";
>  			resets = <&ahb_rst 8>;
>  			reset-names = "ahb";
>  			interrupts = <GIC_SPI 60 IRQ_TYPE_LEVEL_HIGH>;
>  			status = "disabled";
>  			#address-cells = <1>;
>  			#size-cells = <0>;
>  		};
>  
>  		mmc1: mmc at 1c10000 {
>  			compatible = "allwinner,sun50i-a64-mmc",
>  				     "allwinner,sun5i-a13-mmc";
>  			reg = <0x01c10000 0x1000>;
>  			clocks = <&bus_gates 9>, <&mmc1_clk>,
>  				 <&mmc1_clk>, <&mmc1_clk>;
>  			clock-names = "ahb", "mmc",
>  				      "output", "sample";
>  			resets = <&ahb_rst 9>;
>  			reset-names = "ahb";
>  			interrupts = <GIC_SPI 61 IRQ_TYPE_LEVEL_HIGH>;
>  			status = "disabled";
>  			#address-cells = <1>;
>  			#size-cells = <0>;
>  		};
>  
>  		mmc2: mmc at 1c11000 {
>  			compatible = "allwinner,sun50i-a64-mmc",
>  				     "allwinner,sun5i-a13-mmc";
>  			reg = <0x01c11000 0x1000>;
>  			clocks = <&bus_gates 10>, <&mmc2_clk>,
>  				 <&mmc2_clk>, <&mmc2_clk>;
>  			clock-names = "ahb", "mmc",
>  				      "output", "sample";
>  			resets = <&ahb_rst 10>;
>  			reset-names = "ahb";
>  			interrupts = <GIC_SPI 62 IRQ_TYPE_LEVEL_HIGH>;
>  			status = "disabled";
>  			#address-cells = <1>;
>  			#size-cells = <0>;
>  		};
>  
>  		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>;
>  
>  			/* The A64 does not have bank A and leaves a hole in the
>  			   address space where it normally would be */
>  
>  			gpiob: gpiob at 24 {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				allwinner,gpiobank-name = <'B'>;
>  				reg = < 0x24 0x24 >, < 0x200 0x1c >;
>  				interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
>  			};
>  
>  			gpioc: gpioc at 48 {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0x48 0x24 >;
>  				allwinner,gpiobank-name = <'C'>;
>  			};
>  
>  			gpiod: gpiod at 6c {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0x6c 0x24 >;
>  				allwinner,gpiobank-name = <'D'>;
>  			};
>  
>  			gpioe: gpioe at 90 {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0x90 0x24 >;
>  				allwinner,gpiobank-name = <'E'>;
>  			};
>  
>  			gpiof: gpiof at b4 {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0xb4 0x24 >;
>  				allwinner,gpiobank-name = <'F'>;
>  			};
>  
>  			gpiog: gpiog at d8 {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0xd8 0x24 >, < 0x220 0x1c >;
>  				allwinner,gpiobank-name = <'G'>;
>  				interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
>  			};
>  
>  			gpioh: gpioh at fc {
>  				compatible = "allwinner,sunxi-gpiobank";
>  				reg = < 0xfc 0x24 >, < 0x220 0x1c >;
>  				allwinner,gpiobank-name = <'H'>;
>  				interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
>  			};
>  
>  			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 {

Unfortunately, underscores are also going to generate warnings in the
next dtc versions. Can you use dashes instead?

Thanks,
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/20170221/8f91109f/attachment.sig>

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

* [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi
  2017-02-17 17:52 ` [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi Philipp Tomsich
@ 2017-02-21 20:16   ` Maxime Ripard
  0 siblings, 0 replies; 19+ messages in thread
From: Maxime Ripard @ 2017-02-21 20:16 UTC (permalink / raw)
  To: u-boot

On Fri, Feb 17, 2017 at 06:52:42PM +0100, Philipp Tomsich wrote:
> In order to have the device model describe the module reset bits
> on sunxi (well, at least for anything newer than sun6i), we need
> a (rather simple) driver for 'allwinner,sun6i-a31-clock-reset'
> nodes.

This one (and the next one) isn't the binding that we've settled for
in the kernel. This DT was only one of the intermediate versions that
got sent.

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/20170221/ad90a981/attachment.sig>

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

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-21  9:39     ` Dr. Philipp Tomsich
@ 2017-02-22  6:07       ` Chen-Yu Tsai
  2017-02-22  9:26         ` Dr. Philipp Tomsich
  0 siblings, 1 reply; 19+ messages in thread
From: Chen-Yu Tsai @ 2017-02-22  6:07 UTC (permalink / raw)
  To: u-boot

On Tue, Feb 21, 2017 at 5:39 PM, Dr. Philipp Tomsich
<philipp.tomsich@theobroma-systems.com> wrote:
> Chen,

It's ChenYu. :)

>
> On 21 Feb 2017, at 04:48, Chen-Yu Tsai <wens@csie.org> wrote:
>
> On Sat, Feb 18, 2017 at 1:52 AM, Philipp Tomsich
> <philipp.tomsich@theobroma-systems.com> 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
> * defines and implements a binding for sunxi-style GPIO banks (to make it
>   easier to describe controllers like the A64 which may not start at 'A')
>   and provide the necessary translation mechanisms:
>   - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
>     the device framework will try to access a UCLASS_GPIO device bound to
>     the same node id as the pinctrl device to perform it's of_xlate lookup.
>     For this, we provide a 'gpiobridge' driver (which needs to access the
>     platdata of the pinctrl device) and which can then map accesses to an
>     actual GPIO bank device.
>   - For the individual GPIO banks, we use a new driver (which shares most
>     of its ops with the existing sunxi_gpio driver, except probe and bind)
>     and provides configuration without any platdata structure.
>
>
> Why is the new binding and driver necessary? The existing driver in U-boot
> is
> already DM enabled and properly xlates GPIO phandles to its child GPIO
> banks.
> I specifically fixed this in commit 4694dc56e974 ("sunxi: gpio: Add .xlate
> function for gpio phandle resolution?).
>
>
> The problem is that all GPIO is referenced to the pinctrl-node.  As we need
> to
> provide a pinctrl driver (so the configuration of pins can go through that)
> for
> the pinconfig to work, the GPIOs are then referenced to that node.
>
> We had considered multiple solutions
> (a) the one we?ve chosen, which avoids having to provide additional data
> outside the device-tree to track the number of banks and bank-size

You already have this data in the pinctrl driver.

> (b) binding the existing gpio driver to the same node (but as the driverdata
> needs to be matched as well, we?d need to select this based on the
> compatible string and have a tighter coupling between the drivers than
> strictly necessary)

They really should be the same driver. They control the same piece of hardware,
just exporting different functions to the 2 subsystems. There should be proper
locking between the 2, as Allwinner's hardware cannot simultaneously mux a pin
to some function and provide GPIO functionality. The user should not be able
to request a GPIO on a pin that is already claimed by a UART or MMC card.

> (c) dynamically creating gpio subnodes (just as done by the top-level
> sunxi_gpio instances) for each of the banks from within the pinctrl driver
>
> Another reason why we felt the gpiobank to be helpful is that it allows us
> to
> easily describe where the register sets (i.e. PIO and IRQ) are for each
> bank.

These are always at fixed multiple offsets in Allwinner's design though.
The driver can always calculate which registers it needs to access from
the pin name/number. IRQ bank relations are also encoded in the pinctrl
tables you imported. Everything needed is available in code.

>
> You are also not using the new gpiobank nodes anywhere in your dts changes.
>
>
> The DTS change is in the same patch as the the pinctrl driver:
> http://lists.denx.de/pipermail/u-boot/2017-February/281637.html
>
> I had generated this with full function context, which makes the diff in the
> DTS
> hard to spot.

What I meant was you are not actually referencing them. Since you can do
without them, what's the point of adding them, beyond making the driver
slightly easier to write?

>
> +#if defined(CONFIG_SUNXI_PINCTRL)
> +
> +/* When we use the sunxi pinctrl infrastructure, we have two issues that
> + * are resolved by the gpiobank and gpiobrige drivers:
> + *
> + *  - We need to have a UCLASS_GPIO device bound to the pinctrl-nodes for
> + *    translating between gpio entries (e.g. <&pio 3 24 GPIO_ACTIVE_LOW>;)
> + *    in the DT and actual gpio banks. This is done using the gpiobridge
> + *    driver, which provides only a lookup/translation mechanism.
> + *    This mechanism lives in pinctrl-sunxi.c
> + *
> + *  - We introduce a generic gpiobank device, which resolves the need to
> + *    have distinct soc_data structure for each device and avoids having
> + *    to share data structures and config data between this file and the
> + *    sunxi pinctrl (the other option would be to have the soc_data info
> + *    visible in pinctrl-sunxi.c (or merge it into this file) and bind a
> + *    gpio_sunxi device that is set up with the appropriate soc_data) to
> + *    the same node as the pinctrl device.
>
>
> Pushing hardware internals into the DT is not preferred.
>
>
> That?s exactly the point: the current DT format encapsulates the internal
> grouping of the hardware blocks into larger address-decoding groups. For
> the case of pinctrl and GPIO, the actual structure looks as follows:
> + ?bunch of registers?
> + N x [pinctrl functionality for a pin-group]
> + N x [irq-control for a pin-group]
> + N x [GPIO functionality for a pin-group]

Which are part of the same hardware block, which exposes N x M pins with
varying functionality.

> So if I were asked to model this from scratch, I?d use a regmap-device for
> the PIO and R_PIO address spaces and then reference the pinctrl and gpio
> functionality back to it (hiding the internals of how each bank assigns bits
> and where).

However we have an existing binding that should be followed, unless something
is seriously wrong with it, then it should be fixed. Also, AFAIK U-boot sunxi
treats the kernel as the canonical source for device tree files and bindings.

> Since the pinctrl and gpio drivers actually driver the same hardware block,
> just in different ways, and there should be some locking between the two,
> I think it would make sense to merge the two.
>
> You can also get away with not having soc_data by just registering the full
> set of GPIO banks and pins (we aren't counting extra pins anyway).
>
>
> Merging the pinctrl and gpio drivers is going to be tricky, as a driver can
> only
> have a single class.
>
> The more immediate solution would be to simply derive the appropriate driver
> data for the existing gpio class and manually bind a driver to the node (see
> option ?b? from above).
>
> This will then require sharing the (internal) driver data structure from the
> gpio-driver with pinctrl and populating it from the pinctrl probe? i.e. we
> had
> this in an earlier version of the changes, but it looked messy.

The Linux kernel driver also has pinctrl and gpio in the same driver. However
the difference is that the kernel probes device tree nodes as platform devices,
and the device driver then registers pinctrl and gpio functions.

The U-boot model is a simplified one assuming that one device only has one
function, which is not always true. You're going to run into the same issue
with the CCU, which provides clocks and reset controls.


Regards
ChenYu


> Before we revert to this model, I?ll wait if a consensus emerges from the
> discussion here.
>
>
> Regards,
> Philipp.

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

* [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
  2017-02-22  6:07       ` Chen-Yu Tsai
@ 2017-02-22  9:26         ` Dr. Philipp Tomsich
  0 siblings, 0 replies; 19+ messages in thread
From: Dr. Philipp Tomsich @ 2017-02-22  9:26 UTC (permalink / raw)
  To: u-boot

ChenYu,

> On 22 Feb 2017, at 07:07, Chen-Yu Tsai <wens@csie.org> wrote:
> 
>> (b) binding the existing gpio driver to the same node (but as the driverdata
>> needs to be matched as well, we?d need to select this based on the
>> compatible string and have a tighter coupling between the drivers than
>> strictly necessary)
> 
> They really should be the same driver. They control the same piece of hardware,
> just exporting different functions to the 2 subsystems. There should be proper
> locking between the 2, as Allwinner's hardware cannot simultaneously mux a pin
> to some function and provide GPIO functionality. The user should not be able
> to request a GPIO on a pin that is already claimed by a UART or MMC card.

This is already being revised, although (with the user being able to change the
function assignment via mw.l ? which we use a lot during bringup ourselves)
the data used by the driver might get modified hardware (as we fetch the latest
info directly from the hardware at all times).

Stay tuned for v2...

>> (c) dynamically creating gpio subnodes (just as done by the top-level
>> sunxi_gpio instances) for each of the banks from within the pinctrl driver
>> 
>> Another reason why we felt the gpiobank to be helpful is that it allows us
>> to
>> easily describe where the register sets (i.e. PIO and IRQ) are for each
>> bank.
> 
> These are always at fixed multiple offsets in Allwinner's design though.
> The driver can always calculate which registers it needs to access from
> the pin name/number. IRQ bank relations are also encoded in the pinctrl
> tables you imported. Everything needed is available in code.

I have to disagree on the ext-interrupt aspect of the pinctrl.
E.g. on the A31, we had ext-interrupt for PA at 0x200 and PB at 0x200, while
it?s now

Doesn?t really matter at this point though, as a new version with the changes
requested by you and Maxime will be coming shortly.

>> This will then require sharing the (internal) driver data structure from the
>> gpio-driver with pinctrl and populating it from the pinctrl probe? i.e. we
>> had
>> this in an earlier version of the changes, but it looked messy.
> 
> The Linux kernel driver also has pinctrl and gpio in the same driver. However
> the difference is that the kernel probes device tree nodes as platform devices,
> and the device driver then registers pinctrl and gpio functions.
> 
> The U-boot model is a simplified one assuming that one device only has one
> function, which is not always true. You're going to run into the same issue
> with the CCU, which provides clocks and reset controls.

I fully agree, but didn?t want to open a question regarding whether the device
tree parsing and driver instantiation in U-Boot should be extended in a similar
way (i.e. continue looking for drivers that match a node beyond the first match)
at this stage.

We might in fact want to propose both solutions now, as we may need to have a
discussion about this...

Regards,
Philipp.

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

end of thread, other threads:[~2017-02-22  9:26 UTC | newest]

Thread overview: 19+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-02-17 17:52 [U-Boot] [PATCH v1 00/11] sunxi: DM drivers for CLK, RESET and PINCTRL Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 01/11] spl: dm: Undefine DM_MMC, DM_MMC_OPS and BLK, if SPL_DM is not defined Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO Philipp Tomsich
2017-02-21  3:48   ` Chen-Yu Tsai
2017-02-21  9:39     ` Dr. Philipp Tomsich
2017-02-22  6:07       ` Chen-Yu Tsai
2017-02-22  9:26         ` Dr. Philipp Tomsich
2017-02-21 20:14   ` Maxime Ripard
2017-02-17 17:52 ` [U-Boot] [PATCH v1 03/11] sun50i: dts: add gpiobank nodes to the pinctrl nodes Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 04/11] sun50i: dts: update DTS to avoid warnings Philipp Tomsich
2017-02-21 20:15   ` Maxime Ripard
2017-02-17 17:52 ` [U-Boot] [PATCH v1 05/11] sunxi: add module reset (UCLASS_RESET) support for sunxi Philipp Tomsich
2017-02-21 20:16   ` Maxime Ripard
2017-02-17 17:52 ` [U-Boot] [PATCH v1 06/11] sunxi: add clock driver (UCLASS_CLK) " Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 07/11] sunxi: Scan DT tree node '/clocks' on sunxi boards Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 08/11] sun8i_emac: update to work with pinctrl-sunxi, reset-sunxi and clk-sunxi Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 09/11] sunxi_mmc: convert to a device-model driver Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 10/11] dts: sun50i: update mmc pin configuration and add mmc2_8bit_pins Philipp Tomsich
2017-02-17 17:52 ` [U-Boot] [PATCH v1 11/11] sun50i: dts: add spi0 and spi1 nodes and pinconfig 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.