diff mbox

[U-Boot,v1,02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO

Message ID 5209f12ba10211990f861187aa9c59c1f7148ce5.1487349600.git.philipp.tomsich@theobroma-systems.com
State Changes Requested
Delegated to: Jagannadha Sutradharudu Teki
Headers show

Commit Message

Philipp Tomsich Feb. 17, 2017, 5:52 p.m. UTC
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@1c20800
     gpio        [ + ]    |   |   |-- gpiob@24
     gpio        [ + ]    |   |   |-- gpioc@48
     gpio        [ + ]    |   |   |-- gpiod@6c
     gpio        [ + ]    |   |   |-- gpioe@90
     gpio        [ + ]    |   |   |-- gpiof@b4
     gpio        [ + ]    |   |   |-- gpiog@d8
     gpio        [ + ]    |   |   |-- gpioh@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@01f02c00
     gpio        [ + ]    |   |   |-- gpiol@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

Comments

Chen-Yu Tsai Feb. 21, 2017, 3:48 a.m. UTC | #1
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@1c20800
>      gpio        [ + ]    |   |   |-- gpiob@24
>      gpio        [ + ]    |   |   |-- gpioc@48
>      gpio        [ + ]    |   |   |-- gpiod@6c
>      gpio        [ + ]    |   |   |-- gpioe@90
>      gpio        [ + ]    |   |   |-- gpiof@b4
>      gpio        [ + ]    |   |   |-- gpiog@d8
>      gpio        [ + ]    |   |   |-- gpioh@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@01f02c00
>      gpio        [ + ]    |   |   |-- gpiol@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@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@24 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       allwinner,gpiobank-name = <'B'>;
> +                       reg = < 0x24 0x24 >, < 0x200 0x1c >;
> +                       interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
> +               };
> +
> +               gpioc: gpioc@48 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x48 0x24 >;
> +                       allwinner,gpiobank-name = <'C'>;
> +               };
> +
> +               gpiod: gpiod@6c {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x6c 0x24 >;
> +                       allwinner,gpiobank-name = <'D'>;
> +               };
> +
> +               gpioe: gpioe@90 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0x90 0x24 >;
> +                       allwinner,gpiobank-name = <'E'>;
> +               };
> +
> +               gpiof: gpiof@b4 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0xb4 0x24 >;
> +                       allwinner,gpiobank-name = <'F'>;
> +               };
> +
> +               gpiog: gpiog@d8 {
> +                       compatible = "allwinner,sunxi-gpiobank";
> +                       reg = < 0xd8 0x24 >, < 0x220 0x1c >;
> +                       allwinner,gpiobank-name = <'G'>;
> +                       interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> +               };
> +
> +               gpioh: gpioh@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
>
Philipp Tomsich Feb. 21, 2017, 9:39 a.m. UTC | #2
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@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.
Maxime Ripard Feb. 21, 2017, 8:14 p.m. UTC | #3
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@1c20800
>      gpio        [ + ]    |   |   |-- gpiob@24
>      gpio        [ + ]    |   |   |-- gpioc@48
>      gpio        [ + ]    |   |   |-- gpiod@6c
>      gpio        [ + ]    |   |   |-- gpioe@90
>      gpio        [ + ]    |   |   |-- gpiof@b4
>      gpio        [ + ]    |   |   |-- gpiog@d8
>      gpio        [ + ]    |   |   |-- gpioh@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@01f02c00
>      gpio        [ + ]    |   |   |-- gpiol@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@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@24 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			allwinner,gpiobank-name = <'B'>;
> +			reg = < 0x24 0x24 >, < 0x200 0x1c >;
> +			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
> +		};
> +
> +		gpioc: gpioc@48 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x48 0x24 >;
> +			allwinner,gpiobank-name = <'C'>;
> +		};
> +
> +		gpiod: gpiod@6c {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x6c 0x24 >;
> +			allwinner,gpiobank-name = <'D'>;
> +		};
> +
> +		gpioe: gpioe@90 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0x90 0x24 >;
> +			allwinner,gpiobank-name = <'E'>;
> +		};
> +
> +		gpiof: gpiof@b4 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0xb4 0x24 >;
> +			allwinner,gpiobank-name = <'F'>;
> +		};
> +
> +		gpiog: gpiog@d8 {
> +			compatible = "allwinner,sunxi-gpiobank";
> +			reg = < 0xd8 0x24 >, < 0x220 0x1c >;
> +			allwinner,gpiobank-name = <'G'>;
> +			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> +		};
> +
> +		gpioh: gpioh@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
Chen-Yu Tsai Feb. 22, 2017, 6:07 a.m. UTC | #4
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.
Philipp Tomsich Feb. 22, 2017, 9:26 a.m. UTC | #5
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.
diff mbox

Patch

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@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@24 {
+			compatible = "allwinner,sunxi-gpiobank";
+			allwinner,gpiobank-name = <'B'>;
+			reg = < 0x24 0x24 >, < 0x200 0x1c >;
+			interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		gpioc: gpioc@48 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x48 0x24 >;
+			allwinner,gpiobank-name = <'C'>;
+		};
+
+		gpiod: gpiod@6c {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x6c 0x24 >;
+			allwinner,gpiobank-name = <'D'>;
+		};
+
+		gpioe: gpioe@90 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0x90 0x24 >;
+			allwinner,gpiobank-name = <'E'>;
+		};
+
+		gpiof: gpiof@b4 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0xb4 0x24 >;
+			allwinner,gpiobank-name = <'F'>;
+		};
+
+		gpiog: gpiog@d8 {
+			compatible = "allwinner,sunxi-gpiobank";
+			reg = < 0xd8 0x24 >, < 0x220 0x1c >;
+			allwinner,gpiobank-name = <'G'>;
+			interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
+		};
+
+		gpioh: gpioh@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 */