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

Chen-Yu Tsai wens at csie.org
Thu Feb 23 03:54:15 UTC 2017


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

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

ChenYu

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


More information about the U-Boot mailing list