[U-Boot] [PATCH v2 1/6] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi

Maxime Ripard maxime.ripard at free-electrons.com
Wed Feb 22 23:18:22 UTC 2017


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

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

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

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

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

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

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

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

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

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


More information about the U-Boot mailing list