[U-Boot] [PATCH v1 02/11] sunxi: add pinctrl (UCLASS_PINCTRL) support for sunxi and tie back into GPIO
Chen-Yu Tsai
wens at csie.org
Tue Feb 21 03:48:34 UTC 2017
On Sat, Feb 18, 2017 at 1:52 AM, Philipp Tomsich
<philipp.tomsich at theobroma-systems.com> wrote:
> This change adds a full device-model pinctrl driver for sunxi (tested with
> sun50iw1p1) based on the support available in Linux.
>
> Details are:
> * implements a driver for pinctrl devices and assigns sun50i-a64-pinctrl
> and sun50i-a64-r-pinctrl to it
> * defines and implements a binding for sunxi-style GPIO banks (to make it
> easier to describe controllers like the A64 which may not start at 'A')
> and provide the necessary translation mechanisms:
> - As all our gpio-reference point back to either <&pio ..> or <&r_pio ..>
> the device framework will try to access a UCLASS_GPIO device bound to
> the same node id as the pinctrl device to perform it's of_xlate lookup.
> For this, we provide a 'gpiobridge' driver (which needs to access the
> platdata of the pinctrl device) and which can then map accesses to an
> actual GPIO bank device.
> - For the individual GPIO banks, we use a new driver (which shares most
> of its ops with the existing sunxi_gpio driver, except probe and bind)
> and provides configuration without any platdata structure.
Why is the new binding and driver necessary? The existing driver in U-boot is
already DM enabled and properly xlates GPIO phandles to its child GPIO banks.
I specifically fixed this in commit 4694dc56e974 ("sunxi: gpio: Add .xlate
function for gpio phandle resolution").
You are also not using the new gpiobank nodes anywhere in your dts changes.
> * lifts and reuses the pinctrl-sunxi.h and pinctrl-sun50i-a64.c files from
> Linux (thanks to Maxime and Andre) and adds a pinctrl-sun50i-a64-r.c (to
> be picked up for inclusion into Linux again)
>
> The active DM tree at runtime (with this enabled) should look similar to
> the following:
>
> pinctrl [ + ] | |-- pinctrl at 1c20800
> gpio [ + ] | | |-- gpiob at 24
> gpio [ + ] | | |-- gpioc at 48
> gpio [ + ] | | |-- gpiod at 6c
> gpio [ + ] | | |-- gpioe at 90
> gpio [ + ] | | |-- gpiof at b4
> gpio [ + ] | | |-- gpiog at d8
> gpio [ + ] | | |-- gpioh at fc
> pinconfig [ + ] | | |-- uart0_pins_a
> pinconfig [ ] | | |-- uart0_pins_b
> pinconfig [ ] | | |-- uart1_2pins
> pinconfig [ ] | | |-- uart1_4pins
> pinconfig [ ] | | |-- uart2_2pins
> pinconfig [ ] | | |-- uart2_4
> pinconfig [ ] | | |-- uart3
> pinconfig [ ] | | |-- uart3_2
> pinconfig [ ] | | |-- uart3_4
> pinconfig [ ] | | |-- uart4_2
> pinconfig [ ] | | |-- uart4_4
> pinconfig [ + ] | | |-- mmc0
> pinconfig [ + ] | | |-- mmc0_cd_pin
> pinconfig [ ] | | |-- mmc1
> pinconfig [ ] | | |-- mmc2
> pinconfig [ + ] | | |-- mmc2_8bit
> pinconfig [ ] | | |-- i2c0_pins
> pinconfig [ ] | | |-- i2c1_pins
> pinconfig [ ] | | |-- i2c2_pins
> pinconfig [ ] | | |-- rmii_pins
> pinconfig [ + ] | | |-- rgmii_pins
> pinconfig [ + ] | | |-- spi0_pins
> pinconfig [ ] | | |-- spi1_pins
> pinconfig [ + ] | | |-- led_pins_sdio
> pinconfig [ + ] | | |-- ethphy_reset_pin
> gpio [ + ] | | `-- gpiobridge
> pinctrl [ + ] | |-- pinctrl at 01f02c00
> gpio [ + ] | | |-- gpiol at 0
> pinconfig [ + ] | | |-- led_pins_power
> gpio [ + ] | | `-- gpiobridge
>
> Signed-off-by: Philipp Tomsich <philipp.tomsich at theobroma-systems.com>
> ---
> .../pinctrl/allwinner,pinctrl.txt | 130 +++++
> drivers/gpio/sunxi_gpio.c | 97 +++-
> drivers/pinctrl/Kconfig | 10 +
> drivers/pinctrl/Makefile | 2 +
> drivers/pinctrl/sunxi/Makefile | 10 +
> drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c | 92 ++++
> drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 577 +++++++++++++++++++++
> drivers/pinctrl/sunxi/pinctrl-sunxi.c | 326 ++++++++++++
> drivers/pinctrl/sunxi/pinctrl-sunxi.h | 311 +++++++++++
> 9 files changed, 1553 insertions(+), 2 deletions(-)
> create mode 100644 doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> create mode 100644 drivers/pinctrl/sunxi/Makefile
> create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> create mode 100644 drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.c
> create mode 100644 drivers/pinctrl/sunxi/pinctrl-sunxi.h
>
> diff --git a/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> new file mode 100644
> index 0000000..e536ea3
> --- /dev/null
> +++ b/doc/device-tree-bindings/pinctrl/allwinner,pinctrl.txt
> @@ -0,0 +1,130 @@
> +* Allwinner Pinmux Controller
> +
> +Allwinner integrates multiple banks (of 32 pins each) of pin-muxing,
> +GPIO functionality and (optional) external interrupt functionality
> +into a single controller.
> +
> +For each configurable pad (certain driver-cells, such as the IO from
> +integrated USB PHYs or DRAM, have a fixed function and can not be
> +configured), the muxing options (input, output or one of the several
> +functions) can be selected.
> +
> +The Allwinner pinctrl node contains a description of the pinctrl block
> +(i.e. including GPIO and external interrupt capability, if available)
> +and subnodes describing individual GPIO banks and pin-configuration.
> +
> +Properties for the pinctrl node:
> + - compatible: should be "allwinner,sun50i-pinctrl"
> + - reg: address and length of the register set for the device.
> + - interrupts: interrupt for the device
> + - clocks: A phandle to the reference clock for this device
> +
> +Properties for the pinconfig sub-nodes:
> + - allwinner,pins: a list of pins (e.g. "PH2", "PH3") to configure
> + - allwinner,function: the name of pinmux function (e.g. "mmc2")
> + - drive-strength: a drive-stength setting of 10, 20, 30 or 40 mA
> + - bias-pull-up
> + - bias-pull-down
> + - bias-disable (default)
> +
> +Deprecated properties for the pinconfig sub-nodes:
> + - allwinner,drive: one of <SUN4I_PINCTRL_10_MA>, <SUN4I_PINCTRL_20_MA>,
> + <SUN4I_PINCTRL_30_MA> or <SUN4I_PINCTRL_40_MA>
> + - allwinner,pull: one of <SUN4I_PINCTRL_NO_PULL>, <SUN4I_PINCTRL_PULL_UP>
> + or <SUN4I_PINCTRL_PULL_DOWN>
> +
> +Properties for the gpio sub-nodes:
> + - compatible: should be "allwinner,sunxi-gpiobank"
> + - allwinner,gpiobank-name: the name of the bank (e.g. <'A'>, <'B'>, ...)
> + - reg: offsets (within the address range of the enclosing pinctrl
> + node's address space) and length of the registers, where
> + * The first entry points to the mux/gpio registers.
> + * An (optional) second entry points to the extint registers.
> + Note, that the second entry should be provided, if the
> + interrupt property is present.
> + - interrupt: the interrupt used for external interrupt signalling
> + (should be one of the interrupts specified in the
> + enclosing pinctrl node)
> +
> +Example:
> +
> + pio: pinctrl at 1c20800 {
> + compatible = "allwinner,sun50i-a64-pinctrl";
> + reg = <0x01c20800 0x400>;
> +
> + interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>,
> + <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
> + clocks = <&bus_gates 69>;
> +
> + gpio-controller;
> + #gpio-cells = <3>;
> +
> + interrupt-controller;
> + #interrupt-cells = <2>;
> +
> + #address-cells = <1>;
> + #size-cells = <1>;
> +
> + /* The A64 does not have bank A and leaves a hole in the
> + address space where it normally would be */
> +
> + gpiob: gpiob at 24 {
> + compatible = "allwinner,sunxi-gpiobank";
> + allwinner,gpiobank-name = <'B'>;
> + reg = < 0x24 0x24 >, < 0x200 0x1c >;
> + interrupts = <GIC_SPI 11 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gpioc: gpioc at 48 {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0x48 0x24 >;
> + allwinner,gpiobank-name = <'C'>;
> + };
> +
> + gpiod: gpiod at 6c {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0x6c 0x24 >;
> + allwinner,gpiobank-name = <'D'>;
> + };
> +
> + gpioe: gpioe at 90 {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0x90 0x24 >;
> + allwinner,gpiobank-name = <'E'>;
> + };
> +
> + gpiof: gpiof at b4 {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0xb4 0x24 >;
> + allwinner,gpiobank-name = <'F'>;
> + };
> +
> + gpiog: gpiog at d8 {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0xd8 0x24 >, < 0x220 0x1c >;
> + allwinner,gpiobank-name = <'G'>;
> + interrupts = <GIC_SPI 17 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + gpioh: gpioh at fc {
> + compatible = "allwinner,sunxi-gpiobank";
> + reg = < 0xfc 0x24 >, < 0x220 0x1c >;
> + allwinner,gpiobank-name = <'H'>;
> + interrupts = <GIC_SPI 21 IRQ_TYPE_LEVEL_HIGH>;
> + };
> +
> + uart0_pins_a: uart0_pins_a {
> + allwinner,pins = "PB8", "PB9";
> + allwinner,function = "uart0";
> + allwinner,drive = <SUN4I_PINCTRL_10_MA>;
> + allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
> + };
> +
> + uart0_pins_b: uart0_pins_b {
> + allwinner,pins = "PF2", "PF3";
> + allwinner,function = "uart0";
> + allwinner,drive = <SUN4I_PINCTRL_10_MA>;
> + allwinner,pull = <SUN4I_PINCTRL_NO_PULL>;
> + };
> + };
> diff --git a/drivers/gpio/sunxi_gpio.c b/drivers/gpio/sunxi_gpio.c
> index 2b7bc7f..6cf2be4 100644
> --- a/drivers/gpio/sunxi_gpio.c
> +++ b/drivers/gpio/sunxi_gpio.c
> @@ -344,36 +344,129 @@ static const struct sunxi_gpio_soc_data soc_data_l_3 = {
> static const struct udevice_id sunxi_gpio_ids[] = {
> ID("allwinner,sun4i-a10-pinctrl", a_all),
> ID("allwinner,sun5i-a10s-pinctrl", a_all),
> ID("allwinner,sun5i-a13-pinctrl", a_all),
> ID("allwinner,sun6i-a31-pinctrl", a_all),
> ID("allwinner,sun6i-a31s-pinctrl", a_all),
> ID("allwinner,sun7i-a20-pinctrl", a_all),
> ID("allwinner,sun8i-a23-pinctrl", a_all),
> ID("allwinner,sun8i-a33-pinctrl", a_all),
> ID("allwinner,sun8i-a83t-pinctrl", a_all),
> ID("allwinner,sun8i-h3-pinctrl", a_all),
> ID("allwinner,sun9i-a80-pinctrl", a_all),
> +#if !defined(CONFIG_SUNXI_PINCTRL)
> /* This is not strictly correct for the A64, as it is missing
> * bank 'A'. Yet, the register layout in the pinctrl block is
> * backward compatible and any accesses to the registers that
> * normally control bank 'A' will have no adverse effect.
> */
> - ID("allwinner,sun50i-a64-pinctrl", a_all),
> + ID("allwinner,sun50i-a64-pinctrl", a_all),
> +#endif
> ID("allwinner,sun6i-a31-r-pinctrl", l_2),
> ID("allwinner,sun8i-a23-r-pinctrl", l_1),
> ID("allwinner,sun8i-a83t-r-pinctrl", l_1),
> ID("allwinner,sun8i-h3-r-pinctrl", l_1),
> ID("allwinner,sun9i-a80-r-pinctrl", l_3),
> - ID("allwinner,sun50i-a64-r-pinctrl", l_1),
> +#if !defined(CONFIG_SUNXI_PINCTRL)
> + ID("allwinner,sun50i-a64-r-pinctrl", l_1),
> +#endif
> { }
> };
>
> U_BOOT_DRIVER(gpio_sunxi) = {
> .name = "gpio_sunxi",
> .id = UCLASS_GPIO,
> .ops = &gpio_sunxi_ops,
> .of_match = sunxi_gpio_ids,
> .bind = gpio_sunxi_bind,
> .probe = gpio_sunxi_probe,
> };
> +
> +#if defined(CONFIG_SUNXI_PINCTRL)
> +
> +/* When we use the sunxi pinctrl infrastructure, we have two issues that
> + * are resolved by the gpiobank and gpiobrige drivers:
> + *
> + * - We need to have a UCLASS_GPIO device bound to the pinctrl-nodes for
> + * translating between gpio entries (e.g. <&pio 3 24 GPIO_ACTIVE_LOW>;)
> + * in the DT and actual gpio banks. This is done using the gpiobridge
> + * driver, which provides only a lookup/translation mechanism.
> + * This mechanism lives in pinctrl-sunxi.c
> + *
> + * - We introduce a generic gpiobank device, which resolves the need to
> + * have distinct soc_data structure for each device and avoids having
> + * to share data structures and config data between this file and the
> + * sunxi pinctrl (the other option would be to have the soc_data info
> + * visible in pinctrl-sunxi.c (or merge it into this file) and bind a
> + * gpio_sunxi device that is set up with the appropriate soc_data) to
> + * the same node as the pinctrl device.
Pushing hardware internals into the DT is not preferred.
Since the pinctrl and gpio drivers actually driver the same hardware block,
just in different ways, and there should be some locking between the two,
I think it would make sense to merge the two.
You can also get away with not having soc_data by just registering the full
set of GPIO banks and pins (we aren't counting extra pins anyway).
Moreover I think the gpiobank issue is just a limitation of U-boot's GPIO
implementation.
Regards
ChenYu
> + */
> +
> +static int gpiobank_sunxi_probe(struct udevice *dev)
> +{
> + struct sunxi_gpio_platdata *plat = dev_get_platdata(dev);
> + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + int bank_name;
> + int bank;
> + fdt_addr_t offset;
> + fdt_size_t size;
> + fdt_addr_t base;
> +
> + debug("%s: %s", __func__, dev->name);
> +
> + /* At this time we are only interested in index 0 (the PIO registers)
> + and we ignore index 1 (the external interrupt control), even if
> + present and an interrupt property exists... */
> + offset = fdtdec_get_addr_size_auto_noparent(gd->fdt_blob,
> + dev->of_offset,
> + "reg", 0, &size,
> + false);
> + if (offset == FDT_ADDR_T_NONE) {
> + error("%s: missing 'reg' for offset into parent device\n",
> + dev->name);
> + return -EINVAL;
> + }
> +
> + bank_name = fdtdec_get_int(gd->fdt_blob, dev->of_offset,
> + "allwinner,gpiobank-name", -EINVAL);
> + if (bank_name == -EINVAL) {
> + error("%s: missing 'allwinner,gpiobank-name'\n", dev->name);
> + return -EINVAL;
> + }
> +
> + base = dev_get_addr(dev->parent);
> + if (base == FDT_ADDR_T_NONE) {
> + error("%s: parent '%s' does not have a valid base address\n",
> + dev->name, dev->parent->name);
> + return -EINVAL;
> + }
> +
> + bank = bank_name - 'A';
> +
> + plat->regs = (void *)(base + offset);
> + plat->gpio_count = SUNXI_GPIOS_PER_BANK;
> + plat->bank_name = gpio_bank_name(bank);
> +
> + /* Tell the uclass how many GPIOs we have */
> + uc_priv->gpio_count = plat->gpio_count;
> + uc_priv->bank_name = plat->bank_name;
> +
> + return 0;
> +}
> +
> +static const struct udevice_id sunxi_gpiobank_ids[] = {
> + { .compatible = "allwinner,sunxi-gpiobank", },
> + {}
> +};
> +
> +U_BOOT_DRIVER(gpiobank_sunxi) = {
> + .name = "gpiobank_sunxi",
> + .id = UCLASS_GPIO,
> + .ops = &gpio_sunxi_ops,
> + .of_match = sunxi_gpiobank_ids,
> + .platdata_auto_alloc_size = sizeof(struct sunxi_gpio_platdata),
> + .probe = gpiobank_sunxi_probe,
> +};
> +
> +#endif /* CONFIG_SUNXI_PINCTRL */
> +
> #endif
> diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index efcb4c0..064a682 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -175,6 +175,16 @@ config PIC32_PINCTRL
> by a device tree node which contains both GPIO defintion and pin control
> functions.
>
> +config SUNXI_PINCTRL
> + bool "Allwinner Axx pin-control and pin-mux driver"
> + depends on DM && ARCH_SUNXI
> + default y
> + help
> + Supports pin multiplexing control, drive-strength and bias control on
> + Allwinner Axx SoCs. The driver is controlled by a device tree node which
> + contains both the GPIO definitions and the pin control functions for
> + each multiplex function.
> +
> endif
>
> source "drivers/pinctrl/meson/Kconfig"
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index 512112a..da27a91 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -1,18 +1,20 @@
> #
> # SPDX-License-Identifier: GPL-2.0+
> #
>
> obj-y += pinctrl-uclass.o
> obj-$(CONFIG_$(SPL_)PINCTRL_GENERIC) += pinctrl-generic.o
>
> obj-$(CONFIG_PINCTRL_AT91PIO4) += pinctrl-at91-pio4.o
> obj-y += nxp/
> obj-$(CONFIG_ARCH_ATH79) += ath79/
> obj-$(CONFIG_ARCH_ROCKCHIP) += rockchip/
> obj-$(CONFIG_PINCTRL_SANDBOX) += pinctrl-sandbox.o
>
> obj-$(CONFIG_PINCTRL_UNIPHIER) += uniphier/
> obj-$(CONFIG_PIC32_PINCTRL) += pinctrl_pic32.o
> obj-$(CONFIG_PINCTRL_EXYNOS) += exynos/
> obj-$(CONFIG_PINCTRL_MESON) += meson/
> obj-$(CONFIG_PINCTRL_MVEBU) += mvebu/
> +
> +obj-$(CONFIG_ARCH_SUNXI) += sunxi/
> \ No newline at end of file
> diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
> new file mode 100644
> index 0000000..11549ec
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Makefile
> @@ -0,0 +1,10 @@
> +#
> +# Copyright (c) 2017 Theobroma Systems Design und Consulting GmbH
> +#
> +# SPDX-License-Identifier: GPL-2.0+
> +#
> +
> +obj-$(CONFIG_SUNXI_PINCTRL) += pinctrl-sunxi.o
> +ifdef CONFIG_SUNXI_PINCTRL
> +obj-$(CONFIG_MACH_SUN50I) += pinctrl-sun50i-a64.o pinctrl-sun50i-a64-r.o
> +endif
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> new file mode 100644
> index 0000000..864d1ec
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64-r.c
> @@ -0,0 +1,92 @@
> +/*
> + * Allwinner A64 SoCs pinctrl driver.
> + *
> + * Copyright (C) 2017 Theobroma Systems Design und Consulting GmbH.
> + *
> + * Based on pinctrl-sun7i-a20.c, which is:
> + * Copyright (C) 2014 Maxime Ripard <maxime.ripard 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..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 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
>
More information about the U-Boot
mailing list