[RFC PATCH 07/11] pinctrl: Add sun50i-a64 pinctrl driver
Ahmad Fatoum
a.fatoum at pengutronix.de
Thu May 18 12:10:27 PDT 2023
On 11.05.23 01:37, Jules Maselbas wrote:
> sunxi pinctrl driver, adapted from Linux, is split in two parts:
> - pinctrl-sunxi.c that implement sunxi-generic gpio and function mux
> - pinctrl-sun50i-a64.c that only declare sun50i's pin functions
Could you describe why the pin functions is needed? Is this just sanity
checking?
> ---
> drivers/pinctrl/Kconfig | 2 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/sunxi/Kconfig | 15 +
> drivers/pinctrl/sunxi/Makefile | 3 +
> drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c | 593 +++++++++++++++++++++
> drivers/pinctrl/sunxi/pinctrl-sunxi.c | 371 +++++++++++++
> drivers/pinctrl/sunxi/pinctrl-sunxi.h | 224 ++++++++
> 7 files changed, 1209 insertions(+)
> create mode 100644 drivers/pinctrl/sunxi/Kconfig
> create mode 100644 drivers/pinctrl/sunxi/Makefile
> 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/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
> index 2ff99a39c8..0b3d79d1cc 100644
> --- a/drivers/pinctrl/Kconfig
> +++ b/drivers/pinctrl/Kconfig
> @@ -105,6 +105,8 @@ config PINCTRL_STM32
> default y if ARCH_STM32
> help
> Pinmux and GPIO controller found on STM32 family
> +
> +source "drivers/pinctrl/sunxi/Kconfig"
> endif
>
> endmenu
> diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
> index f1a5fa5715..3bc718d355 100644
> --- a/drivers/pinctrl/Makefile
> +++ b/drivers/pinctrl/Makefile
> @@ -16,3 +16,4 @@ obj-$(CONFIG_PINCTRL_VF610) += pinctrl-vf610.o
> obj-$(CONFIG_PINCTRL_STM32) += pinctrl-stm32.o
>
> obj-$(CONFIG_ARCH_MVEBU) += mvebu/
> +obj-$(CONFIG_ARCH_SUNXI) += sunxi/
> diff --git a/drivers/pinctrl/sunxi/Kconfig b/drivers/pinctrl/sunxi/Kconfig
> new file mode 100644
> index 0000000000..36168dc2bb
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Kconfig
> @@ -0,0 +1,15 @@
> +# SPDX-License-Identifier: GPL-2.0-only
> +if ARCH_SUNXI
> +
> +config PINCTRL_SUNXI
> + bool
> + select PINMUX
Undefined
> + select GENERIC_PINCONF
Undefined
> + select GPIOLIB
> +
> +config PINCTRL_SUN50I_A64
> + bool "Support for the Allwinner A64 PIO"
> + default ARM64 && ARCH_SUNXI
ARM64 undefined
> + select PINCTRL_SUNXI
> +
> +endif
> diff --git a/drivers/pinctrl/sunxi/Makefile b/drivers/pinctrl/sunxi/Makefile
> new file mode 100644
> index 0000000000..db0ff5b50b
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/Makefile
> @@ -0,0 +1,3 @@
> +# SPDX-License-Identifier: GPL-2.0
> +obj-$(CONFIG_ARCH_SUNXI) += pinctrl-sunxi.o
> +obj-$(CONFIG_PINCTRL_SUN50I_A64) += pinctrl-sun50i-a64.o
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> new file mode 100644
> index 0000000000..4a087802c8
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sun50i-a64.c
> @@ -0,0 +1,593 @@
> +/*
> + * 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.
SPDX?
> + */
> +
> +#include <common.h>
> +#include <init.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, "csi"), /* PCK */
> + SUNXI_FUNCTION(0x4, "ts")), /* CLK */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 1),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* CK */
> + SUNXI_FUNCTION(0x4, "ts")), /* ERR */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 2),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* HSYNC */
> + SUNXI_FUNCTION(0x4, "ts")), /* SYNC */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 3),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* VSYNC */
> + SUNXI_FUNCTION(0x4, "ts")), /* DVLD */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 4),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D0 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D0 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 5),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D1 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D1 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 6),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D2 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D2 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 7),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D3 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D3 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 8),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D4 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D4 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 9),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D5 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D5 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 10),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D6 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D6 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 11),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi"), /* D7 */
> + SUNXI_FUNCTION(0x4, "ts")), /* D7 */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 12),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi")), /* SCK */
> + SUNXI_PIN(SUNXI_PINCTRL_PIN(E, 13),
> + SUNXI_FUNCTION(0x0, "gpio_in"),
> + SUNXI_FUNCTION(0x1, "gpio_out"),
> + SUNXI_FUNCTION(0x2, "csi")), /* 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(0x3, "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 */
> +};
> +
> +static const struct sunxi_pinctrl_desc a64_pinctrl_data = {
> + .pins = a64_pins,
> + .npins = ARRAY_SIZE(a64_pins),
> +};
> +
> +static const struct of_device_id a64_pinctrl_dt_match[] = {
> + {
> + .compatible = "allwinner,sun50i-a64-pinctrl",
> + .data = &a64_pinctrl_data
> + }, {
> + /* sentinel */
> + }
> +};
> +
> +static struct driver a64_pinctrl_driver = {
> + .name = "sun50i-a64-pinctrl",
> + .probe = sunxi_pinctrl_probe,
> + .of_compatible = DRV_OF_COMPAT(a64_pinctrl_dt_match),
> +};
> +core_platform_driver(a64_pinctrl_driver);
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.c b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> new file mode 100644
> index 0000000000..e2bde96c70
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.c
> @@ -0,0 +1,371 @@
> +// SPDX-License-Identifier: GPL-2.0-only
> +
> +#define pr_fmt(fmt) "pinctrl-sunxi: " fmt
> +
> +#include <common.h>
> +#include <io.h>
> +#include <of.h>
> +#include <of_address.h>
> +#include <malloc.h>
> +#include <linux/clk.h>
> +
> +#include "pinctrl-sunxi.h"
> +
> +/* This driver assumes the gpio function mux value will not change */
> +#define FUNC_GPIO_IN 0
> +#define FUNC_GPIO_OUT 1
> +
> +static struct sunxi_pinctrl *to_sunxi_pinctrl(struct pinctrl_device *pdev)
> +{
> + return container_of(pdev, struct sunxi_pinctrl, pdev);
> +}
> +
> +static void sunxi_pinctrl_set_pull(struct sunxi_pinctrl *pinctrl,
> + u16 pin, u32 pull)
> +{
> + u32 reg = sunxi_pull_reg(pin);
> + u32 off = sunxi_pull_offset(pin);
> + u32 msk = MUX_PINS_MASK << off;
> + u32 val = readl(pinctrl->base + reg);
> +
> + val &= ~msk;
> + val |= (pull << off) & msk;
> + writel(val, pinctrl->base + reg);
> +}
> +
> +static void sunxi_pinctrl_set_dlevel(struct sunxi_pinctrl *pinctrl,
> + u16 pin, u32 lvl)
> +{
> + u32 reg = sunxi_dlevel_reg(pin);
> + u32 off = sunxi_dlevel_offset(pin);
> + u32 msk = MUX_PINS_MASK << off;
> + u32 val = readl(pinctrl->base + reg);
> +
> + val &= ~msk;
> + val |= (lvl << off) & msk;
> + writel(val, pinctrl->base + reg);
> +}
> +
> +static void sunxi_pinctrl_set_mux(struct sunxi_pinctrl *pinctrl,
> + u16 pin, u8 mux)
> +{
> + u32 reg = sunxi_mux_reg(pin);
> + u32 off = sunxi_mux_offset(pin);
> + u32 msk = MUX_PINS_MASK << off;
> + u32 val = readl(pinctrl->base + reg);
> +
> + val &= ~msk;
> + val |= (mux << off) & msk;
> + writel(val, pinctrl->base + reg);
> +}
> +
> +static u8 sunxi_pinctrl_get_mux(struct sunxi_pinctrl *pinctrl, u16 pin)
> +{
> + u32 reg = sunxi_mux_reg(pin);
> + u32 off = sunxi_mux_offset(pin);
> + u32 val = readl(pinctrl->base + reg);
> +
> + return (val >> off) & MUX_PINS_MASK;
> +}
> +
> +static void sunxi_pinctrl_set_conf(struct sunxi_pinctrl *pinctrl,
> + u16 pin, struct device_node *node)
> +{
> + u32 val;
> +
> + if (of_find_property(node, "bias-pull-up", NULL))
> + sunxi_pinctrl_set_pull(pinctrl, pin, 1);
> + if (of_find_property(node, "bias-pull-down", NULL))
> + sunxi_pinctrl_set_pull(pinctrl, pin, 2);
> + if (of_find_property(node, "bias-disable", NULL))
> + sunxi_pinctrl_set_pull(pinctrl, pin, 0);
> +
> + if (!of_property_read_u32(node, "drive-strength", &val)) {
> + val = rounddown(val, 10) / 10 - 1;
> + sunxi_pinctrl_set_dlevel(pinctrl, pin, val);
> + }
> +}
> +
> +static const char *sunxi_pinctrl_parse_function_prop(struct device_node *node)
> +{
> + const char *function;
> + int ret;
> +
> + /* Try the generic binding */
> + ret = of_property_read_string(node, "function", &function);
> + if (!ret)
> + return function;
> +
> + /* And fall back to our legacy one */
> + ret = of_property_read_string(node, "allwinner,function", &function);
> + if (!ret)
> + return function;
> +
> + return NULL;
> +}
> +
> +static struct property *sunxi_pinctrl_find_pins_prop(struct device_node *node)
> +{
> + struct property *prop;
> +
> + /* Try the generic binding */
> + prop = of_find_property(node, "pins", NULL);
> + if (prop)
> + return prop;
> +
> + /* And fall back to our legacy one */
> + prop = of_find_property(node, "allwinner,pins", NULL);
> + if (prop)
> + return prop;
> +
> + return NULL;
> +}
> +
> +#define sunxi_pinctrl_of_pins_for_each_string(np, prop, s) \
> + for (prop = sunxi_pinctrl_find_pins_prop(np), \
> + s = of_prop_next_string(prop, NULL); \
> + s; \
> + s = of_prop_next_string(prop, s))
> +
> +
> +static const struct sunxi_desc_pin *
> +sunxi_pinctrl_find_pin(struct sunxi_pinctrl *pinctrl, const char *pin_name)
> +{
> + const struct sunxi_desc_pin *pin;
> + int i;
> +
> + for (i = 0; i < pinctrl->desc->npins; i++) {
> + pin = &pinctrl->desc->pins[i];
> + if (!strcmp(pin->pin.name, pin_name))
> + return pin;
> + }
> +
> + return NULL;
> +}
> +
> +static const struct sunxi_desc_function *
> +sunxi_pinctrl_find_func(struct sunxi_pinctrl *pinctrl,
> + const char *pin_name, const char *func_name)
> +{
> + const struct sunxi_desc_pin *pin;
> + const struct sunxi_desc_function *func;
> +
> + pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> + if (!pin)
> + return NULL;
> +
> + for (func = pin->functions; func->name; func++)
> + if (!strcmp(func->name, func_name))
> + return func;
> +
> + return NULL;
> +}
> +
> +static int sunxi_pinctrl_set_func(struct sunxi_pinctrl *pinctrl,
> + struct device_node *np,
> + const char *pin_name, const char *func_name)
> +{
> + struct device *dev = pinctrl->pdev.dev;
> + const struct sunxi_desc_pin *pin;
> + const struct sunxi_desc_function *func;
> +
> + dev_dbg(dev, "setfunc %s @ %s\n", func_name, pin_name);
> +
> + pin = sunxi_pinctrl_find_pin(pinctrl, pin_name);
> + if (!pin) {
> + dev_err(dev, "pin %s not found\n", pin_name);
> + return -EINVAL;
> + }
> +
> + func = sunxi_pinctrl_find_func(pinctrl, pin_name, func_name);
> + if (!func) {
> + dev_err(dev, "func %s not found\n", func_name);
> + return -EINVAL;
> + }
> +
> + sunxi_pinctrl_set_mux(pinctrl, pin->pin.number, func->muxval);
> + sunxi_pinctrl_set_conf(pinctrl, pin->pin.number, np);
> +
> + return 0;
> +}
> +
> +static int sunxi_pinctrl_set_state(struct pinctrl_device *pdev, struct device_node *np)
> +{
> + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> + struct device *dev = pinctrl->pdev.dev;
> + struct property *prop;
> + const char *func_name;
> + const char *pin_name;
> +
> + func_name = sunxi_pinctrl_parse_function_prop(np);
> + if (!func_name) {
> + dev_err(dev, "%s: missing 'function' property\n", np->full_name);
> + return -EINVAL;
> + }
> +
> + sunxi_pinctrl_of_pins_for_each_string(np, prop, pin_name) {
> + sunxi_pinctrl_set_func(pinctrl, np, pin_name, func_name);
> + }
> +
> + return 0;
> +}
> +
> +static int sunxi_pinctrl_set_direction(struct pinctrl_device *pdev, unsigned int gpio, bool in)
> +{
> + struct sunxi_pinctrl *pinctrl = to_sunxi_pinctrl(pdev);
> + u32 func = in ? FUNC_GPIO_IN : FUNC_GPIO_OUT;
> +
> + sunxi_pinctrl_set_mux(pinctrl, gpio, func);
> +
> + return 0;
> +}
> +
> +static int sunxi_gpio_get(struct gpio_chip *chip, unsigned gpio)
> +{
> + struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> + u32 reg = sunxi_data_reg(gpio);
> + u32 bit = sunxi_data_offset(gpio);
> + u32 val = readl(pinctrl->base + reg);
> +
> + return val & BIT(bit);
> +}
> +
> +static void sunxi_gpio_set(struct gpio_chip *chip, unsigned gpio, int value)
> +{
> + struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> + u32 reg = sunxi_data_reg(gpio);
> + u32 bit = sunxi_data_offset(gpio);
> + u32 val = readl(pinctrl->base + reg);
> +
> + if (value)
> + val |= BIT(bit);
> + else
> + val &= ~BIT(bit);
> + writel(val, pinctrl->base + reg);
> +}
> +
> +static int sunxi_gpio_direction_output(struct gpio_chip *chip,
> + unsigned gpio, int value)
> +{
> + struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +
> + sunxi_gpio_set(chip, gpio, value);
> + sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_OUT);
> +
> + return 0;
> +}
> +
> +static int sunxi_gpio_direction_input(struct gpio_chip *chip,
> + unsigned gpio)
> +{
> + struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> +
> + sunxi_pinctrl_set_mux(pinctrl, gpio, FUNC_GPIO_IN);
> +
> + return 0;
> +}
> +
> +static int sunxi_gpio_get_direction(struct gpio_chip *chip, unsigned gpio)
> +{
> + struct sunxi_pinctrl *pinctrl = chip->dev->priv;
> + u32 func = sunxi_pinctrl_get_mux(pinctrl, gpio);
> +
> + if (func == FUNC_GPIO_IN)
> + return GPIOF_DIR_IN;
> + if (func == FUNC_GPIO_OUT)
> + return GPIOF_DIR_OUT;
> + return -EINVAL;
> +}
> +
> +static int sunxi_gpio_of_xlate(struct gpio_chip *chip,
> + const struct of_phandle_args *gpiospec,
> + u32 *flags)
> +{
> + int pin, base;
> +
> + if (gpiospec->args_count != 3)
> + return -EINVAL;
> +
> + base = PINS_PER_BANK * gpiospec->args[0];
> + pin = base + gpiospec->args[1];
> +
> + if (pin > chip->ngpio)
> + return -EINVAL;
> +
> + if (flags)
> + *flags = gpiospec->args[2];
> +
> + return pin;
> +}
> +
> +static struct pinctrl_ops sunxi_pinctrl_ops = {
> + .set_state = sunxi_pinctrl_set_state,
> + .set_direction = sunxi_pinctrl_set_direction,
> +};
> +
> +static struct gpio_ops sunxi_gpio_ops = {
> + .request = sunxi_gpio_direction_input, /* switch to input function */
> + .direction_input = sunxi_gpio_direction_input,
> + .direction_output = sunxi_gpio_direction_output,
> + .get_direction = sunxi_gpio_get_direction,
> + .get = sunxi_gpio_get,
> + .set = sunxi_gpio_set,
> + .of_xlate = sunxi_gpio_of_xlate,
> +};
> +
> +int sunxi_pinctrl_probe(struct device *dev)
> +{
> + const struct sunxi_pinctrl_desc *desc;
> + struct sunxi_pinctrl *pinctrl;
> + struct resource *iores;
> + int ret;
> +
> + if (!IS_ENABLED(CONFIG_PINCTRL))
> + return 0;
> +
> + desc = device_get_match_data(dev);
> + if (!desc)
> + return -EINVAL;
> +
> + iores = dev_request_mem_resource(dev, 0);
> + if (IS_ERR(iores))
> + return PTR_ERR(iores);
> +
> + pinctrl = xzalloc(sizeof(*pinctrl));
> + dev->priv = pinctrl;
> + pinctrl->base = IOMEM(iores->start);
> +
> + pinctrl->desc = desc;
> + pinctrl->pdev.dev = dev;
> + pinctrl->pdev.ops = &sunxi_pinctrl_ops;
> +
> + ret = pinctrl_register(&pinctrl->pdev);
> + if (ret) {
> + dev_err(dev, "couldn't register %s driver\n", "pinctrl");
> + goto err;
> + }
> + dev_dbg(dev, "sunxi %s registered\n", "pinctrl");
> +
> + pinctrl->chip.dev = dev;
> + pinctrl->chip.ops = &sunxi_gpio_ops;
> + /* only the first 8 bank are supported */
> + pinctrl->chip.base = 0;
> + pinctrl->chip.ngpio = 8 * PINS_PER_BANK;
> +
> + if (of_property_read_bool(dev->of_node, "gpio-controller")) {
> + ret = gpiochip_add(&pinctrl->chip);
> + if (ret) {
> + dev_err(dev, "couldn't register %s driver\n", "gpio-chip");
> + goto pinctrl_unregister;
> + }
> + dev_dbg(dev, "sunxi %s registered\n", "gpio-chip");
> + }
> + return 0;
> +
> +pinctrl_unregister:
> + pinctrl_unregister(&pinctrl->pdev);
> +err:
> + release_region(iores);
> + free(pinctrl);
> + return ret;
> +}
> diff --git a/drivers/pinctrl/sunxi/pinctrl-sunxi.h b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> new file mode 100644
> index 0000000000..630f1ef98e
> --- /dev/null
> +++ b/drivers/pinctrl/sunxi/pinctrl-sunxi.h
> @@ -0,0 +1,224 @@
> +/* SPDX-License-Identifier: GPL-2.0-only */
> +/*
> + * 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
> +
> +#include <pinctrl.h>
> +#include <gpio.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
> +
> +#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 GRP_CFG_REG 0x300
> +
> +#define IO_BIAS_MASK GENMASK(3, 0)
> +
> +#define SUN4I_FUNC_INPUT 0
> +#define SUN4I_FUNC_IRQ 6
> +
> +#define PINCTRL_SUN5I_A10S BIT(1)
> +#define PINCTRL_SUN5I_A13 BIT(2)
> +#define PINCTRL_SUN5I_GR8 BIT(3)
> +#define PINCTRL_SUN6I_A31 BIT(4)
> +#define PINCTRL_SUN6I_A31S BIT(5)
> +#define PINCTRL_SUN4I_A10 BIT(6)
> +#define PINCTRL_SUN7I_A20 BIT(7)
> +#define PINCTRL_SUN8I_R40 BIT(8)
> +#define PINCTRL_SUN8I_V3 BIT(9)
> +#define PINCTRL_SUN8I_V3S BIT(10)
> +
> +#define PIO_POW_MOD_SEL_REG 0x340
> +
> +enum sunxi_desc_bias_voltage {
> + BIAS_VOLTAGE_NONE,
> + /*
> + * Bias voltage configuration is done through
> + * Pn_GRP_CONFIG registers, as seen on A80 SoC.
> + */
> + BIAS_VOLTAGE_GRP_CONFIG,
> + /*
> + * Bias voltage is set through PIO_POW_MOD_SEL_REG
> + * register, as seen on H6 SoC, for example.
> + */
> + BIAS_VOLTAGE_PIO_POW_MODE_SEL,
> +};
> +
> +struct sunxi_desc_function {
> + const char *name;
> + u8 muxval;
> +};
> +
> +struct sunxi_desc_pin {
> + struct {
> + const char name[6];
> + u16 number;
> + } pin;
> + const struct sunxi_desc_function *functions;
> +};
> +
> +struct sunxi_pinctrl_desc {
> + const struct sunxi_desc_pin *pins;
> + size_t npins;
> + unsigned pin_base;
> + bool disable_strict_mode;
> + enum sunxi_desc_bias_voltage io_bias_cfg_variant;
> +};
> +
> +struct sunxi_pinctrl {
> + void __iomem *base;
> + struct gpio_chip chip;
> + struct pinctrl_device pdev;
> + const struct sunxi_pinctrl_desc *desc;
> +};
> +
> +#define SUNXI_PIN(_pin, ...) \
> + { \
> + .pin = _pin, \
> + .functions = (struct sunxi_desc_function[]){ \
> + __VA_ARGS__, { } }, \
> + }
> +
> +#define SUNXI_PINCTRL_PIN(bank, pin) \
> + { \
> + .name = "P" #bank #pin, \
> + .number = P ## bank ## _BASE + (pin) \
> + }
> +
> +#define SUNXI_FUNCTION(_val, _name) \
> + { \
> + .name = _name, \
> + .muxval = _val, \
> + }
> +
> +#define SUNXI_FUNCTION_IRQ_BANK(...) {}
> +
> +/*
> + * 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_grp_config_reg(u16 pin)
> +{
> + u8 bank = pin / PINS_PER_BANK;
> +
> + return GRP_CFG_REG + bank * 0x4;
> +}
> +
> +int sunxi_pinctrl_probe(struct device *dev);
> +
> +#endif /* __PINCTRL_SUNXI_H */
--
Pengutronix e.K. | |
Steuerwalder Str. 21 | http://www.pengutronix.de/ |
31137 Hildesheim, Germany | Phone: +49-5121-206917-0 |
Amtsgericht Hildesheim, HRA 2686 | Fax: +49-5121-206917-5555 |
More information about the barebox
mailing list