From mboxrd@z Thu Jan 1 00:00:00 1970 Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.remote-ip=159.226.251.81 ARC-Seal: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; cv=none; t=1792612126; b=nKPOTDq2q/1Yrsy0VxyydwMyadCKRtOOJWnGE6DYGvMxy2wa3jRG4W3k7shZh7Zk5eIF 0a78iXWDpPAhUES3xfzamBzsM5Cj04WYup+RqbfsEIPYKmaaQcxqq8iuQ9E2NlVe+QwBZ owFviuouozwo1bv5ZRngnOdmTvYrZUl6fQoo8kFZOCHx7HmxAnUhxyRdFKQPcfv95tQSq JgjyT/ZwsBkqtClmW/RsVtgvmlNIIH+qzFx/NpcP6AdHYlsNB30HCJhwXWIy/z5LoXjnu waoXahWQ6SxOP/hIVagT0vhXcQS1EWt1ApETIxlR3PIRy9OwW2kPVcOcsgV3S7U6qWA== ARC-Message-Signature: i=1; d=subspace.kernel.org; s=arc20261015; a=rsa-sha256; c=relaxed/relaxed; t=1792612126; h=DMARC-Filter:Received:From:Date:Subject:MIME-Version:Content-Type: Content-Transfer-Encoding:Message-Id:References:In-Reply-To:To:Cc: X-Mailer:X-CM-TRANSID:X-Coremail-Antispam:X-Originating-IP: X-CM-SenderInfo; bh=8UOnSeDpYM67ICiqR7FSIQw1QnjeFGP1JGC13SUouGk=; b=O4HjRjWiL/p28yFE0w95VleeK13D0oecZQjPSG7MPxlAUHITyQRJRBXJcgyrrOgRjRFh tWGP6H3U+xq0ZPpuS7Q0xc0ltzsoItEhKDTCUap6KGyykCYlWOCdlMuHJEAM6cRgKuC0j 50Zclz8rkQFoqWX+LMlwGwhYQH+AUTL79med0qWxHKba6s09qaHmY0+algIkvPUYWcVLR Y/sQQOmkxIqJA0wNLFMUlranBNHU0wLBgmKgLUpM3nqL7FJlvnNStcqXliNsj6lMnkD4c cjCvEm6i3AVkyeImmD4PrIc9ES54RY1+0T5Z6AoUZDlSp/WHsrJ3huj6/cvrG7+NklA== ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none header.from=iscas.ac.cn; spf=pass smtp.mailfrom=iscas.ac.cn; arc=none smtp.remote-ip=159.226.251.81 Received: from cstnet.cn (smtp81.cstnet.cn [159.226.251.81]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature ECDSA (prime256v1) server-digest SHA256) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id AC490606A320; Wed, 21 Oct 2026 19:48:42 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.4.2 smtp.subspace.kernel.org AC490606A320 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=iscas.ac.cn Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=iscas.ac.cn Received: from [127.0.1.1] (unknown [223.166.93.48]) by APP-03 (Coremail) with SMTP id rQCowAB3gT_6Ftlqxu5hCw--.4480S11; Thu, 22 Oct 2026 03:48:21 +0800 (CST) From: Han Gao Date: Thu, 22 Oct 2026 03:48:17 +0800 Subject: [PATCH 09/18] pinctrl: zhihe: add A210 support X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Post: List-Help: List-Subscribe: List-Unsubscribe: List-Owner: Precedence: list MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: 7bit Message-Id: <20261022-a210-init-v1-9-09fb4d99da65@iscas.ac.cn> References: <20261022-a210-init-v1-0-09fb4d99da65@iscas.ac.cn> In-Reply-To: <20261022-a210-init-v1-0-09fb4d99da65@iscas.ac.cn> To: Rob Herring , Krzysztof Kozlowski , Conor Dooley , Paul Walmsley , Palmer Dabbelt , Albert Ou , Alexandre Ghiti , Zhiguo Zhu , Stephen Boyd , Brian Masney , Jerome Brunet , Philipp Zabel , Linus Walleij , Bartosz Golaszewski , Thomas Gleixner , Radu Rendec , Samuel Holland , Daniel Lezcano , Anup Patel , Icenowy Zheng , Drew Fustini Cc: devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-riscv@lists.infradead.org, linux-clk@vger.kernel.org, linux-gpio@vger.kernel.org, Paul Walmsley , Han Gao X-Mailer: b4 0.16.0 X-CM-TRANSID:rQCowAB3gT_6Ftlqxu5hCw--.4480S11 X-Coremail-Antispam: 1UD129KBjvAXoWDZF18Zw45uw47Jry5tFyUtrb_yoWrCFWkJo WfurnrW3y8Jw1FgF98CF4kZ3W3Za10y3W7Jr4kZr43Jr1fAFW5KF1ayr4rW3W2gryjqryU X3savrW7Aan7AF95n29KB7ZKAUJUUUU8529EdanIXcx71UUUUU7v73VFW2AGmfu7bjvjm3 AaLaJ3UjIYCTnIWjp_UUUOT7AC8VAFwI0_Wr0E3s1l1xkIjI8I6I8E6xAIw20EY4v20xva j40_Wr0E3s1l1IIY67AEw4v_Jr0_Jr4l82xGYIkIc2x26280x7IE14v26r126s0DM28Irc Ia0xkI8VCY1x0267AKxVW5JVCq3wA2ocxC64kIII0Yj41l84x0c7CEw4AK67xGY2AK021l 84ACjcxK6xIIjxv20xvE14v26r4j6ryUM28EF7xvwVC0I7IYx2IY6xkF7I0E14v26r4UJV WxJr1l84ACjcxK6I8E87Iv67AKxVW8Jr0_Cr1UM28EF7xvwVC2z280aVCY1x0267AKxVW8 Jr0_Cr1UM2AIxVAIcxkEcVAq07x20xvEncxIr21l5I8CrVACY4xI64kE6c02F40Ex7xfMc Ij6xIIjxv20xvE14v26r1Y6r17McIj6I8E87Iv67AKxVWUJVW8JwAm72CE4IkC6x0Yz7v_ Jr0_Gr1lF7xvr2IYc2Ij64vIr41lF7I21c0EjII2zVCS5cI20VAGYxC7M4IIrI8v6xkF7I 0E8cxan2IY04v7MxkF7I0En4kS14v26r4a6rW5MxAIw28IcxkI7VAKI48JMxC20s026xCa FVCjc4AY6r1j6r4UMI8I3I0E5I8CrVAFwI0_Jr0_Jr4lx2IqxVCjr7xvwVAFwI0_JrI_Jr Wlx4CE17CEb7AF67AKxVW8ZVWrXwCIc40Y0x0EwIxGrwCI42IY6xIIjxv20xvE14v26r1I 6r4UMIIF0xvE2Ix0cI8IcVCY1x0267AKxVW8Jr0_Cr1UMIIF0xvE42xK8VAvwI8IcIk0rV WUJVWUCwCI42IY6I8E87Iv67AKxVWUJVW8JwCI42IY6I8E87Iv6xkF7I0E14v26r4UJVWx JrUvcSsGvfC2KfnxnUUI43ZEXa7sRRjg43UUUUU== X-CM-SenderInfo: xjdrxt3q6l2u1dvotugofq/1tbiBwcHDGrYyeCSrQABsu From: Zhiguo Zhu Add mux and pin configuration support for the four A210 pad controllers, including the shared PERI3 SDIO mux and suspend-state restoration. Signed-off-by: Zhiguo Zhu Co-developed-by: Han Gao Signed-off-by: Han Gao --- drivers/pinctrl/Kconfig | 14 + drivers/pinctrl/Makefile | 1 + drivers/pinctrl/pinctrl-a210.c | 1206 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1221 insertions(+) diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index e1ab3a916086..50fd2732b9b8 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -37,6 +37,20 @@ config DEBUG_PINCTRL help Say Y here to add some extra checks and diagnostics to PINCTRL calls. +config PINCTRL_A210 + tristate "Pinctrl driver for the ZhiHe A210 SoC" + depends on 64BIT + depends on OF && (ARCH_ZHIHE || COMPILE_TEST) + select GENERIC_PINMUX_FUNCTIONS + select GENERIC_PINCONF + select PINMUX + help + This selects the pinctrl driver for the ZhiHe A210 RISC-V SoC. + + It provides pin multiplexing and configuration support for + the A210 pad controllers (AON, PADCTRL0-2). Say Y if you + are building a kernel for the ZhiHe A210 platform. + config PINCTRL_AMD bool "AMD GPIO pin control" depends on HAS_IOMEM diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index a35d71135abf..c79ec78229ae 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -10,6 +10,7 @@ obj-$(CONFIG_GENERIC_PINCONF) += pinconf-generic.o obj-$(CONFIG_GENERIC_PINCTRL) += pinctrl-generic.o obj-$(CONFIG_OF) += devicetree.o +obj-$(CONFIG_PINCTRL_A210) += pinctrl-a210.o obj-$(CONFIG_PINCTRL_AMD) += pinctrl-amd.o obj-$(CONFIG_PINCTRL_AMDISP) += pinctrl-amdisp.o obj-$(CONFIG_PINCTRL_APPLE_GPIO) += pinctrl-apple-gpio.o diff --git a/drivers/pinctrl/pinctrl-a210.c b/drivers/pinctrl/pinctrl-a210.c new file mode 100644 index 000000000000..753253e35c05 --- /dev/null +++ b/drivers/pinctrl/pinctrl-a210.c @@ -0,0 +1,1206 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Zhihe Computing Limited. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "core.h" +#include "pinmux.h" +#include "pinconf.h" + +#define A210_PADCFG_IE BIT(9) +#define A210_PADCFG_SL BIT(8) +#define A210_PADCFG_ST BIT(7) +#define A210_PADCFG_SPU BIT(6) +#define A210_PADCFG_PS BIT(5) +#define A210_PADCFG_PE BIT(4) +#define A210_PADCFG_BIAS (A210_PADCFG_SPU | A210_PADCFG_PS | A210_PADCFG_PE) +#define A210_PADCFG_DS GENMASK(3, 0) + +/* PERI3 has one 32-bit configuration register per SDIO pad. */ +#define A210_SDIO_PULL_UP BIT(1) +#define A210_SDIO_PULL_DOWN BIT(0) +#define A210_SDIO_BIAS (A210_SDIO_PULL_UP | A210_SDIO_PULL_DOWN) + +#define A210_PULL_DOWN_OHM 44000 /* typ. 44kOhm */ +#define A210_PULL_UP_OHM 48000 /* typ. 48kOhm */ +#define A210_PULL_STRONG_OHM 2100 /* typ. 2.1kOhm */ + +#define A210_PAD_NO_PADCFG BIT(63) +#define A210_PAD_MUXDATA GENMASK(55, 0) + +#ifdef CONFIG_PM_SLEEP +#define A210_MAX_CFG_REGS 25 +#define A210_MAX_MUX_REGS 7 + +struct a210_register_state { + u32 mask; + u32 value; +}; +#endif + +struct a210_pinctrl; + +enum a210_pinctrl_type { + A210_PADCTRL_AON, + A210_PADCTRL_0, + A210_PADCTRL_1, + A210_PADCTRL_2, +}; + +struct a210_pad_group { + const char *name; + const struct pinctrl_pin_desc *pins; + unsigned int npins; + unsigned int offset_mux; + unsigned int mask_mux; + unsigned int offset_cfg; + unsigned int mask_cfg; + enum a210_pinctrl_type type; +}; + +struct a210_pinctrl { + struct pinctrl_desc desc; + raw_spinlock_t lock; /* serialize register access */ + void __iomem *base; + struct clk *clk; + bool clk_enabled; + unsigned int offset_mux; + unsigned int mask_mux; + unsigned int offset_cfg; + unsigned int mask_cfg; + enum a210_pinctrl_type type; + struct pinctrl_dev *pctl; +#ifdef CONFIG_PM_SLEEP + struct a210_register_state cfg_state[A210_MAX_CFG_REGS]; + struct a210_register_state mux_state[A210_MAX_MUX_REGS]; +#endif +}; + +static void __iomem *a210_padcfg(struct a210_pinctrl *pp, unsigned int pin) +{ + int width = hweight32(pp->mask_cfg); + + return pp->base + pp->offset_cfg + 4 * (pin * width / 32); +} + +static unsigned int a210_padcfg_shift(struct a210_pinctrl *pp, unsigned int pin) +{ + int width = hweight32(pp->mask_cfg); + + return width * (pin & (32 / width - 1)); +} + +static void __iomem *a210_muxcfg(struct a210_pinctrl *pp, unsigned int pin) +{ + int width = hweight32(pp->mask_mux); + + return pp->base + pp->offset_mux + 4 * (pin * width / 32); +} + +static unsigned int a210_muxcfg_shift(struct a210_pinctrl *pp, unsigned int pin) +{ + int width = hweight32(pp->mask_mux); + + if (pp->type == A210_PADCTRL_2) + return 0; + + return width * (pin & (32 / width - 1)); +} + +enum a210_muxtype { + A210_MUX_____, + A210_MUX_AOUART, + A210_MUX_AOUART_IR, + A210_MUX_AOGPIO0, + A210_MUX_AOGPIO1, + A210_MUX_AOI2C0, + A210_MUX_AOI2C1, + A210_MUX_AOSPI, + A210_MUX_JTAG, + A210_MUX_RST, + A210_MUX_POR, + A210_MUX_PMIC, + A210_MUX_DVS, + A210_MUX_UART0, + A210_MUX_UART1, + A210_MUX_UART2, + A210_MUX_UART3, + A210_MUX_UART4, + A210_MUX_UART5, + A210_MUX_UART6, + A210_MUX_UART7, + A210_MUX_UART8, + A210_MUX_UART9, + A210_MUX_UART0_IR, + A210_MUX_UART1_IR, + A210_MUX_UART2_IR, + A210_MUX_UART3_IR, + A210_MUX_UART4_IR, + A210_MUX_UART5_IR, + A210_MUX_UART6_IR, + A210_MUX_UART7_IR, + A210_MUX_UART8_IR, + A210_MUX_UART9_IR, + A210_MUX_GPIO0, + A210_MUX_GPIO1, + A210_MUX_GPIO2, + A210_MUX_GPIO3, + A210_MUX_GPIO4, + A210_MUX_GMAC0, + A210_MUX_GMAC1, + A210_MUX_SPI0, + A210_MUX_SPI1, + A210_MUX_QSPI0, + A210_MUX_QSPI1, + A210_MUX_PWM0, + A210_MUX_PWM1, + A210_MUX_PWM2, + A210_MUX_CAN0, + A210_MUX_CAN1, + A210_MUX_CAN2, + A210_MUX_I2S0, + A210_MUX_I2S1, + A210_MUX_I2S2, + A210_MUX_I2S3, + A210_MUX_I2C0, + A210_MUX_I2C1, + A210_MUX_I2C2, + A210_MUX_I2C3, + A210_MUX_I2C4, + A210_MUX_I2C5, + A210_MUX_I2C6, + A210_MUX_I2C7, + A210_MUX_I2C7_SMB, + A210_MUX_SDIO, + A210_MUX_ISP0, + A210_MUX_DPTX, + A210_MUX_USB31, + A210_MUX_SATA_P0, + A210_MUX_SATA_P1, + A210_MUX_HDMI, + A210_MUX_ZMAC, + A210_MUX_BSEL, + A210_MUX_DBGM, + A210_MUX_CDBG, + A210_MUX_PDM, + A210_MUX_TDM, + A210_MUX_PCIE_X1, + A210_MUX_PCIE_X4, + A210_MUX_SEN_VCLK, + A210_MUX_MAX = 127, +}; + +static const char *const a210_muxtype_string[] = { + [A210_MUX_AOUART] = "aouart", + [A210_MUX_AOUART_IR] = "aouart_ir", + [A210_MUX_AOGPIO0] = "aogpio0", + [A210_MUX_AOGPIO1] = "aogpio1", + [A210_MUX_AOI2C0] = "aoi2c0", + [A210_MUX_AOI2C1] = "aoi2c1", + [A210_MUX_AOSPI] = "aospi", + [A210_MUX_JTAG] = "jtag", + [A210_MUX_RST] = "reset", + [A210_MUX_POR] = "por", + [A210_MUX_PMIC] = "pmic", + [A210_MUX_DVS] = "dvs", + [A210_MUX_UART0] = "uart0", + [A210_MUX_UART1] = "uart1", + [A210_MUX_UART2] = "uart2", + [A210_MUX_UART3] = "uart3", + [A210_MUX_UART4] = "uart4", + [A210_MUX_UART5] = "uart5", + [A210_MUX_UART6] = "uart6", + [A210_MUX_UART7] = "uart7", + [A210_MUX_UART8] = "uart8", + [A210_MUX_UART9] = "uart9", + [A210_MUX_UART0_IR] = "uart0_ir", + [A210_MUX_UART1_IR] = "uart1_ir", + [A210_MUX_UART2_IR] = "uart2_ir", + [A210_MUX_UART3_IR] = "uart3_ir", + [A210_MUX_UART4_IR] = "uart4_ir", + [A210_MUX_UART5_IR] = "uart5_ir", + [A210_MUX_UART6_IR] = "uart6_ir", + [A210_MUX_UART7_IR] = "uart7_ir", + [A210_MUX_UART8_IR] = "uart8_ir", + [A210_MUX_UART9_IR] = "uart9_ir", + [A210_MUX_GPIO0] = "gpio0", + [A210_MUX_GPIO1] = "gpio1", + [A210_MUX_GPIO2] = "gpio2", + [A210_MUX_GPIO3] = "gpio3", + [A210_MUX_GPIO4] = "gpio4", + [A210_MUX_GMAC0] = "gmac0", + [A210_MUX_GMAC1] = "gmac1", + [A210_MUX_SPI0] = "spi0", + [A210_MUX_SPI1] = "spi1", + [A210_MUX_QSPI0] = "qspi0", + [A210_MUX_QSPI1] = "qspi1", + [A210_MUX_PWM0] = "pwm0", + [A210_MUX_PWM1] = "pwm1", + [A210_MUX_PWM2] = "pwm2", + [A210_MUX_CAN0] = "can0", + [A210_MUX_CAN1] = "can1", + [A210_MUX_CAN2] = "can2", + [A210_MUX_I2S0] = "i2s0", + [A210_MUX_I2S1] = "i2s1", + [A210_MUX_I2S2] = "i2s2", + [A210_MUX_I2S3] = "i2s3", + [A210_MUX_I2C0] = "i2c0", + [A210_MUX_I2C1] = "i2c1", + [A210_MUX_I2C2] = "i2c2", + [A210_MUX_I2C3] = "i2c3", + [A210_MUX_I2C4] = "i2c4", + [A210_MUX_I2C5] = "i2c5", + [A210_MUX_I2C6] = "i2c6", + [A210_MUX_I2C7] = "i2c7", + [A210_MUX_I2C7_SMB] = "i2c7_smb", + [A210_MUX_SDIO] = "sdio", + [A210_MUX_ISP0] = "isp0", + [A210_MUX_DPTX] = "dptx", + [A210_MUX_USB31] = "usb31", + [A210_MUX_SATA_P0] = "sata_p0", + [A210_MUX_SATA_P1] = "sata_p1", + [A210_MUX_HDMI] = "hdmi", + [A210_MUX_ZMAC] = "zmac", + [A210_MUX_BSEL] = "bsel", + [A210_MUX_DBGM] = "dbgm", + [A210_MUX_CDBG] = "cdbg", + [A210_MUX_PDM] = "pdm", + [A210_MUX_TDM] = "tdm", + [A210_MUX_PCIE_X1] = "pcie_x1", + [A210_MUX_PCIE_X4] = "pcie_x4", + [A210_MUX_SEN_VCLK] = "sen_vclk", +}; + +#define A210_PAD(_nr, _name, m0, m1, m2, m3, m4, m5, m6, m7, _flags) \ + { .number = _nr, \ + .name = #_name, \ + .drv_data = \ + (void *)((_flags) | (A210_MUX_##m0 << 0) | (A210_MUX_##m1 << 7) | \ + (A210_MUX_##m2 << 14) | (A210_MUX_##m3 << 21) | \ + ((uintptr_t)A210_MUX_##m4 << 28) | ((uintptr_t)A210_MUX_##m5 << 35) | \ + ((uintptr_t)A210_MUX_##m6 << 42) | ((uintptr_t)A210_MUX_##m7 << 49)) } + +static const struct pinctrl_pin_desc a210_group0_pins[] = { + A210_PAD(0, OSC_CLK_IN, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG), + A210_PAD(1, OSC_CLK_OUT, ____, ____, ____, ____, ____, ____, ____, ____, + A210_PAD_NO_PADCFG), + A210_PAD(2, RST_N_IN, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG), + A210_PAD(3, RTC_CLK_IN, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG), + A210_PAD(4, RTC_CLK_OUT, ____, ____, ____, ____, ____, ____, ____, ____, + A210_PAD_NO_PADCFG), + A210_PAD(5, TEST_MODE, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG), + A210_PAD(6, POR_SEL, ____, ____, ____, ____, ____, ____, ____, ____, A210_PAD_NO_PADCFG), + A210_PAD(7, RST_N_OUT, RST, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(8, BOOT_SEL0, BSEL, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(9, BOOT_SEL1, BSEL, AOUART, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(10, DEBUG_MODE, DBGM, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(11, AOUART_TXD, AOUART, AOUART_IR, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(12, AOUART_RXD, AOUART, AOUART_IR, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(13, AOI2C0_SCL, AOI2C0, AOSPI, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(14, AOI2C0_SDA, AOI2C0, AOSPI, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(15, AOI2C1_SCL, AOI2C1, AOSPI, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(16, AOI2C1_SDA, AOI2C1, AOSPI, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(17, CPU_JTG_TCLK, JTAG, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(18, CPU_JTG_TMS, JTAG, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(19, CPU_JTG_TDI, JTAG, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(20, CPU_JTG_TDO, JTAG, POR, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(21, CPU_JTG_TRST, JTAG, ____, ____, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(22, AOGPIO0_21, AOGPIO0, AOSPI, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(23, AOGPIO0_22, AOGPIO0, AOSPI, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(24, AOGPIO0_23, BSEL, AOSPI, PMIC, AOGPIO0, ____, ____, ____, ____, 0), + A210_PAD(25, AOGPIO0_24, AOGPIO0, AOSPI, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(26, AOGPIO0_25, AOGPIO0, AOSPI, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(27, AOGPIO0_26, AOGPIO0, AOI2C1, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(28, AOGPIO0_27, AOGPIO0, AOI2C1, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(29, AOGPIO0_28, AOGPIO0, AOI2C1, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(30, AOGPIO0_29, AOGPIO0, AOI2C1, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(31, AOGPIO0_30, AOGPIO0, AOI2C1, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(32, AOGPIO0_31, AOGPIO0, AOUART, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(33, AOGPIO1_0, AOGPIO1, AOUART, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(34, AOGPIO1_1, AOGPIO1, ____, PMIC, ____, ____, ____, ____, ____, 0), + A210_PAD(35, AOGPIO1_2, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(36, AOGPIO1_3, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(37, AOGPIO1_4, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(38, AOGPIO1_5, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(39, AOGPIO1_6, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(40, AOGPIO1_7, AOGPIO1, ____, PMIC, DVS, ____, ____, ____, ____, 0), + A210_PAD(41, AOGPIO1_8, PCIE_X4, ____, PMIC, AOGPIO1, ____, ____, ____, ____, 0), + A210_PAD(42, AOGPIO1_9, AOGPIO1, ____, PMIC, ____, ____, ____, ____, ____, 0), +}; + +static const struct pinctrl_pin_desc a210_group1_pins[] = { + A210_PAD(0, GPIO0_0, GPIO0, GMAC0, ____, ____, ____, ____, ZMAC, ____, 0), + A210_PAD(1, GPIO0_1, GPIO0, GMAC0, ____, ____, ____, ____, ZMAC, ____, 0), + A210_PAD(2, GPIO0_2, GPIO0, GMAC0, ____, ____, ____, ____, ZMAC, ____, 0), + A210_PAD(3, GPIO0_3, GPIO0, GMAC0, UART3, UART3_IR, ____, SATA_P0, ZMAC, ____, 0), + A210_PAD(4, GPIO0_4, GPIO0, GMAC0, UART3, UART3_IR, ____, SATA_P1, ZMAC, ____, 0), + A210_PAD(5, GPIO0_5, GPIO0, GMAC0, UART3, ____, ____, SATA_P1, ZMAC, ____, 0), + A210_PAD(6, GPIO0_6, GPIO0, GMAC0, UART3, ____, ____, SATA_P1, ZMAC, ____, 0), + A210_PAD(7, GPIO0_7, GPIO0, GMAC0, ____, ____, ____, SATA_P1, ZMAC, ____, 0), + A210_PAD(8, GPIO0_8, GPIO0, GMAC0, I2C0, ____, ____, SATA_P1, ZMAC, ____, 0), + A210_PAD(9, GPIO0_9, GPIO0, GMAC0, I2C0, ____, ____, PCIE_X4, ZMAC, ____, 0), + A210_PAD(10, GPIO0_10, GPIO0, GMAC0, I2C1, ____, ____, PCIE_X4, ZMAC, ____, 0), + A210_PAD(11, GPIO0_11, GPIO0, GMAC0, I2C1, ____, ____, PCIE_X4, ZMAC, ____, 0), + A210_PAD(12, GPIO0_12, GPIO0, GMAC0, ____, ____, ____, PCIE_X4, ZMAC, ____, 0), + A210_PAD(13, GPIO0_13, GPIO0, GMAC0, ____, ____, ____, PWM0, ZMAC, ____, 0), + A210_PAD(14, GPIO0_14, GPIO0, UART0, UART0_IR, ____, SATA_P1, I2S0, ____, ____, 0), + A210_PAD(15, GPIO0_15, GPIO0, UART0, UART0_IR, QSPI0, SATA_P1, I2S0, ____, ____, 0), + A210_PAD(16, GPIO0_16, GPIO0, UART0, UART1, UART1_IR, SATA_P1, I2S0, ZMAC, ____, 0), + A210_PAD(17, GPIO0_17, GPIO0, UART0, UART1, UART1_IR, SATA_P1, I2S0, ZMAC, ____, 0), + A210_PAD(18, GPIO0_18, GPIO0, QSPI0, GMAC0, SDIO, SATA_P1, I2S0, ____, PCIE_X1, 0), + A210_PAD(19, GPIO0_19, GPIO0, QSPI0, GMAC1, ____, TDM, SATA_P0, ____, PCIE_X1, 0), + A210_PAD(20, GPIO0_20, GPIO0, QSPI0, UART3, UART3_IR, TDM, SATA_P0, ZMAC, PCIE_X1, 0), + A210_PAD(21, GPIO0_21, GPIO0, QSPI0, UART3, UART3_IR, TDM, SATA_P0, ZMAC, PCIE_X1, 0), + A210_PAD(22, GPIO0_22, GPIO0, QSPI0, I2C2, ____, TDM, SATA_P0, ZMAC, PWM0, 0), + A210_PAD(23, GPIO0_23, GPIO0, QSPI0, I2C2, ____, ____, SATA_P0, ZMAC, PWM0, 0), + A210_PAD(24, GPIO0_24, GPIO0, QSPI0, I2C0, CAN0, CDBG, PCIE_X1, UART0, PWM0, 0), + A210_PAD(25, GPIO0_25, GPIO0, QSPI0, I2C0, CAN0, CDBG, PCIE_X1, UART0, PWM0, 0), + A210_PAD(26, GPIO0_26, GPIO0, QSPI0, I2C1, CAN1, CDBG, PCIE_X1, UART0, UART0_IR, 0), + A210_PAD(27, GPIO0_27, GPIO0, QSPI0, I2C1, CAN1, CDBG, PCIE_X1, UART0, UART0_IR, 0), + A210_PAD(28, GPIO0_28, GPIO0, PWM0, SPI0, UART1_IR, I2S0, PCIE_X4, UART1, ____, 0), + A210_PAD(29, GPIO0_29, GPIO0, PWM0, SPI0, UART1_IR, I2S0, PCIE_X4, UART1, ____, 0), + A210_PAD(30, GPIO0_30, GPIO0, PWM0, SPI0, ____, I2S0, PCIE_X4, UART1, ____, 0), + A210_PAD(31, GPIO0_31, GPIO0, PWM0, SPI0, QSPI0, I2S0, PCIE_X4, UART1, ____, 0), + A210_PAD(32, GPIO1_0, GPIO1, PWM0, UART2, UART2_IR, I2S0, SDIO, GMAC1, SATA_P0, 0), + A210_PAD(33, GPIO1_1, GPIO1, PWM0, UART2, UART2_IR, I2C2, SATA_P1, SPI0, SDIO, 0), + A210_PAD(34, GPIO1_2, GPIO1, GMAC1, TDM, PWM0, I2C2, SATA_P1, ____, ____, 0), + A210_PAD(35, GPIO1_3, GPIO1, GMAC1, TDM, PWM0, ____, SATA_P1, ____, ____, 0), + A210_PAD(36, GPIO1_4, GPIO1, GMAC1, TDM, UART2, ____, SATA_P1, ____, ____, 0), + A210_PAD(37, GPIO1_5, GPIO1, GMAC1, ____, UART2, ____, SATA_P1, ____, ____, 0), + A210_PAD(38, GPIO1_6, GPIO1, GMAC1, SPI0, PWM0, CDBG, SATA_P0, ____, ____, 0), + A210_PAD(39, GPIO1_7, GPIO1, GMAC1, SPI0, PWM0, CDBG, SATA_P0, ____, ____, 0), + A210_PAD(40, GPIO1_8, GPIO1, GMAC1, SPI0, PDM, CDBG, SATA_P0, ____, ____, 0), + A210_PAD(41, GPIO1_9, GPIO1, GMAC1, SPI0, PWM0, ____, SATA_P0, GMAC0, ____, 0), + A210_PAD(42, GPIO1_10, GPIO1, GMAC1, SPI0, PWM0, ____, SATA_P0, ____, ____, 0), + A210_PAD(43, GPIO1_11, GPIO1, GMAC1, QSPI0, PDM, I2S0, ISP0, PWM0, ____, 0), + A210_PAD(44, GPIO1_12, GPIO1, GMAC1, QSPI0, PDM, I2S0, ISP0, ____, ____, 0), + A210_PAD(45, GPIO1_13, GPIO1, GMAC1, QSPI0, PDM, I2S0, ISP0, ____, ____, 0), + A210_PAD(46, GPIO1_14, GPIO1, GMAC1, ____, PDM, I2S0, ISP0, ____, ____, 0), + A210_PAD(47, GPIO1_15, GPIO1, GMAC1, SDIO, PDM, I2S0, ISP0, ____, ____, 0), + A210_PAD(48, GPIO1_16, GPIO1, GMAC0, ____, ____, ____, SDIO, ____, ____, 0), +}; + +static const struct pinctrl_pin_desc a210_group2_pins[] = { + A210_PAD(0, GPIO2_0, GPIO2, UART4, UART4_IR, PWM2, ____, I2C3, ____, ____, 0), + A210_PAD(1, GPIO2_1, GPIO2, UART4, UART4_IR, PWM2, ____, I2C3, ____, ____, 0), + A210_PAD(2, GPIO2_2, GPIO2, UART5, UART5_IR, PWM2, ____, I2C5, I2S1, ____, 0), + A210_PAD(3, GPIO2_3, GPIO2, UART5, UART5_IR, PWM2, ____, I2C5, I2S1, ____, 0), + A210_PAD(4, GPIO2_4, GPIO2, UART6, UART6_IR, PWM2, ____, I2C6, I2S1, ____, 0), + A210_PAD(5, GPIO2_5, GPIO2, UART6, UART6_IR, PWM2, ____, I2C6, I2S1, ____, 0), + A210_PAD(6, GPIO2_6, GPIO2, UART7, UART7_IR, PWM2, SEN_VCLK, I2C4, I2S1, ____, 0), + A210_PAD(7, GPIO2_7, GPIO2, UART7, UART7_IR, PWM2, SEN_VCLK, I2C4, ____, ____, 0), + A210_PAD(8, GPIO2_8, GPIO2, I2S2, ____, SPI1, UART6, I2C6, PWM2, ____, 0), + A210_PAD(9, GPIO2_9, GPIO2, I2S2, ____, SPI1, UART6, I2C6, PWM2, ____, 0), + A210_PAD(10, GPIO2_10, GPIO2, I2S2, PWM1, SPI1, UART9, I2C7, ____, ____, 0), + A210_PAD(11, GPIO2_11, GPIO2, I2S2, PWM1, SPI1, UART9, I2C7, ____, ____, 0), + A210_PAD(12, GPIO2_12, GPIO2, I2S2, PWM1, SPI1, UART8, I2C7, ____, ____, 0), + A210_PAD(13, GPIO2_13, GPIO2, I2S3, PWM1, ____, ____, ____, ____, ____, 0), + A210_PAD(14, GPIO2_14, GPIO2, I2S3, PWM1, ____, ____, ____, ____, ____, 0), + A210_PAD(15, GPIO2_15, GPIO2, I2S3, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(16, GPIO2_16, GPIO2, I2S3, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(17, GPIO2_17, GPIO2, I2S3, SPI1, I2C4, UART8, I2C7, ____, ____, 0), + A210_PAD(18, GPIO2_18, GPIO2, I2S3, SPI1, I2C4, PWM2, UART5, UART5_IR, ____, 0), + A210_PAD(19, GPIO2_19, GPIO2, I2S3, SPI1, ____, PWM1, UART5, UART5_IR, ____, 0), + A210_PAD(20, GPIO2_20, GPIO2, I2S3, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(21, GPIO2_21, GPIO2, I2S3, SPI1, I2C3, PWM1, UART8, ____, ____, 0), + A210_PAD(22, GPIO2_22, GPIO2, I2S3, SPI1, I2C3, PWM1, UART8, ____, ____, 0), + A210_PAD(23, GPIO2_23, GPIO2, I2S3, PWM1, ____, ____, ____, ____, ____, 0), + A210_PAD(24, GPIO2_24, GPIO2, I2C3, UART5, ____, ____, ____, ____, ____, 0), + A210_PAD(25, GPIO2_25, GPIO2, I2C3, UART5, ____, HDMI, QSPI1, PWM2, ____, 0), + A210_PAD(26, GPIO2_26, GPIO2, I2C4, UART6, ____, ____, ____, ____, ____, 0), + A210_PAD(27, GPIO2_27, GPIO2, I2C4, UART6, ____, ____, ____, ____, ____, 0), + A210_PAD(28, GPIO2_28, GPIO2, I2C5, UART7, PWM1, ____, QSPI1, ____, ____, 0), + A210_PAD(29, GPIO2_29, GPIO2, I2C5, UART7, PWM1, ____, QSPI1, ____, ____, 0), + A210_PAD(30, GPIO2_30, GPIO2, I2C6, UART8, PWM1, HDMI, QSPI1, I2S3, ____, 0), + A210_PAD(31, GPIO2_31, GPIO2, I2C6, UART8, PWM1, HDMI, QSPI1, I2S3, ____, 0), + A210_PAD(32, GPIO3_0, GPIO3, I2C7, UART9, PWM1, CAN2, QSPI1, I2S3, SEN_VCLK, 0), + A210_PAD(33, GPIO3_1, GPIO3, I2C7, UART9, PWM1, CAN2, QSPI1, I2S3, SEN_VCLK, 0), + A210_PAD(34, GPIO3_2, GPIO3, QSPI1, UART8, UART8_IR, ____, ____, I2S3, PWM1, 0), + A210_PAD(35, GPIO3_3, GPIO3, QSPI1, UART8, UART8_IR, ____, ____, I2S3, PWM1, 0), + A210_PAD(36, GPIO3_4, GPIO3, QSPI1, UART9, UART9_IR, ____, ____, I2S3, PWM2, 0), + A210_PAD(37, GPIO3_5, GPIO3, QSPI1, UART9, UART9_IR, ____, ____, I2S3, PWM2, 0), + A210_PAD(38, GPIO3_6, GPIO3, QSPI1, I2S1, I2C7_SMB, ____, DPTX, I2S3, PWM2, 0), + A210_PAD(39, GPIO3_7, GPIO3, QSPI1, I2S1, I2C7_SMB, UART7, I2C5, I2S3, PWM2, 0), + A210_PAD(40, GPIO3_8, GPIO3, QSPI1, I2S1, I2C7_SMB, UART7, I2C5, I2S3, PWM2, 0), + A210_PAD(41, GPIO3_9, GPIO3, UART4, I2S1, I2C7_SMB, UART5, DPTX, ____, PWM2, 0), + A210_PAD(42, GPIO3_10, GPIO3, UART4, I2S1, I2C7_SMB, UART5, USB31, ____, ____, 0), +}; + +static const struct pinctrl_pin_desc a210_group3_pins[] = { + A210_PAD(0, SDIO_CLK, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(1, SDIO_CMD, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(2, SDIO_DAT0, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(3, SDIO_DAT1, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(4, SDIO_DAT2, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), + A210_PAD(5, SDIO_DAT3, SDIO, GPIO4, ____, ____, ____, ____, ____, ____, 0), +}; + +static const struct a210_pad_group a210_group0 = { + /* aon */ + .name = "a210-group0", + .pins = a210_group0_pins, + .npins = ARRAY_SIZE(a210_group0_pins), + .offset_mux = 0x400, + .mask_mux = 0xf, + .offset_cfg = 0x0, + .mask_cfg = 0xffff, + .type = A210_PADCTRL_AON, +}; + +static const struct a210_pad_group a210_group1 = { + /* peri1 */ + .name = "a210-group1", + .pins = a210_group1_pins, + .npins = ARRAY_SIZE(a210_group1_pins), + .offset_mux = 0x400, + .mask_mux = 0xf, + .offset_cfg = 0x0, + .mask_cfg = 0xffff, + .type = A210_PADCTRL_0, +}; + +static const struct a210_pad_group a210_group2 = { + /* peri2 */ + .name = "a210-group2", + .pins = a210_group2_pins, + .npins = ARRAY_SIZE(a210_group2_pins), + .offset_mux = 0x400, + .mask_mux = 0xf, + .offset_cfg = 0x0, + .mask_cfg = 0xffff, + .type = A210_PADCTRL_1, +}; + +static const struct a210_pad_group a210_group3 = { + /* PERI3 has one shared mux bit for all six SDIO pads. */ + .name = "a210-group3", + .pins = a210_group3_pins, + .npins = ARRAY_SIZE(a210_group3_pins), + .offset_mux = 0x200, + .mask_mux = 0x1, + .offset_cfg = 0x0, + .mask_cfg = U32_MAX, + .type = A210_PADCTRL_2, +}; + +static int a210_pinctrl_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + + return pp->type == A210_PADCTRL_2 ? 1 : pp->desc.npins; +} + +static const char *a210_pinctrl_get_group_name(struct pinctrl_dev *pctldev, unsigned int gsel) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + + return pp->type == A210_PADCTRL_2 ? "sdio" : pp->desc.pins[gsel].name; +} + +static const unsigned int a210_sdio_pins[] = { 0, 1, 2, 3, 4, 5 }; + +static int a210_pinctrl_get_group_pins(struct pinctrl_dev *pctldev, + unsigned int gsel, + const unsigned int **pins, + unsigned int *npins) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + + if (pp->type == A210_PADCTRL_2) { + *pins = a210_sdio_pins; + *npins = ARRAY_SIZE(a210_sdio_pins); + } else { + *pins = &pp->desc.pins[gsel].number; + *npins = 1; + } + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void a210_pin_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int pin) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + void __iomem *padcfg = a210_padcfg(pp, pin); + void __iomem *muxcfg = a210_muxcfg(pp, pin); + u32 pad; + u32 mux; + + if ((uintptr_t)pin_desc_get(pctldev, pin)->drv_data & A210_PAD_NO_PADCFG) + return; + + scoped_guard(raw_spinlock_irqsave, &pp->lock) { + pad = readl_relaxed(padcfg); + mux = readl_relaxed(muxcfg); + } + + seq_printf(s, "[padcfg=0x%08x muxcfg=0x%08x]", pad, mux); +} +#else +#define a210_pin_dbg_show NULL +#endif + +static const struct pinctrl_ops a210_pinctrl_ops = { + .get_groups_count = a210_pinctrl_get_groups_count, + .get_group_name = a210_pinctrl_get_group_name, + .get_group_pins = a210_pinctrl_get_group_pins, + .pin_dbg_show = a210_pin_dbg_show, + .dt_node_to_map = pinconf_generic_dt_node_to_map_all, + .dt_free_map = pinconf_generic_dt_free_map, +}; + +static const u8 a210_drive_strength_in_ma[16] = { + 1, 2, 3, 5, 7, 8, 10, 12, 13, 15, 16, 18, 20, 21, 23, 25, +}; + +static int a210_drive_strength_from_ma(u32 arg) +{ + unsigned int i; + + for (i = 0; i < ARRAY_SIZE(a210_drive_strength_in_ma); i++) + if (arg == a210_drive_strength_in_ma[i]) + return i; + return -EINVAL; +} + +static int a210_padcfg_rmw(struct a210_pinctrl *pp, unsigned int pin, u32 mask, u32 value) +{ + void __iomem *padcfg = a210_padcfg(pp, pin); + unsigned int shift = a210_padcfg_shift(pp, pin); + u32 tmp; + + mask <<= shift; + value <<= shift; + + scoped_guard(raw_spinlock_irqsave, &pp->lock) { + tmp = readl_relaxed(padcfg); + tmp = (tmp & ~mask) | value; + writel_relaxed(tmp, padcfg); +#ifdef CONFIG_PM_SLEEP + pp->cfg_state[pin * hweight32(pp->mask_cfg) / 32].mask |= mask; +#endif + } + return 0; +} + +static int a210_sdio_pinconf_get(struct a210_pinctrl *pp, unsigned int pin, + unsigned long *config) +{ + enum pin_config_param param = pinconf_to_config_param(*config); + u32 value = readl_relaxed(a210_padcfg(pp, pin)) & A210_SDIO_BIAS; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (value) + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (value != A210_SDIO_PULL_UP) + return -EINVAL; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (value != A210_SDIO_PULL_DOWN) + return -EINVAL; + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, param != PIN_CONFIG_BIAS_DISABLE); + return 0; +} + +static int a210_sdio_pinconf_set(struct a210_pinctrl *pp, unsigned int pin, + unsigned long *configs, unsigned int num_configs) +{ + u32 value = 0; + unsigned int i; + + for (i = 0; i < num_configs; i++) { + enum pin_config_param param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + value = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg != 1) + return -EINVAL; + value = param == PIN_CONFIG_BIAS_PULL_UP ? + A210_SDIO_PULL_UP : A210_SDIO_PULL_DOWN; + break; + default: + return -ENOTSUPP; + } + } + + if (!num_configs) + return 0; + + return a210_padcfg_rmw(pp, pin, A210_SDIO_BIAS, value); +} + +static int a210_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + bool enabled; + int param; + u32 value; + u32 arg; + + if ((uintptr_t)desc->drv_data & A210_PAD_NO_PADCFG) + return -ENOTSUPP; + if (pp->type == A210_PADCTRL_2) + return a210_sdio_pinconf_get(pp, pin, config); + + value = readl_relaxed(a210_padcfg(pp, pin)); + value = (value >> a210_padcfg_shift(pp, pin)) & GENMASK(9, 0); + + param = pinconf_to_config_param(*config); + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + enabled = !(value & (A210_PADCFG_SPU | A210_PADCFG_PE)); + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + enabled = (value & A210_PADCFG_BIAS) == A210_PADCFG_PE; + arg = enabled ? A210_PULL_DOWN_OHM : 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (value & A210_PADCFG_SPU) { + enabled = true; + arg = A210_PULL_STRONG_OHM; + } else if ((value & (A210_PADCFG_PE | A210_PADCFG_PS)) == + (A210_PADCFG_PE | A210_PADCFG_PS)) { + enabled = true; + arg = A210_PULL_UP_OHM; + } else { + enabled = false; + arg = 0; + } + break; + case PIN_CONFIG_DRIVE_STRENGTH: + enabled = true; + arg = a210_drive_strength_in_ma[value & A210_PADCFG_DS]; + break; + case PIN_CONFIG_INPUT_ENABLE: + enabled = value & A210_PADCFG_IE; + arg = enabled ? 1 : 0; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + enabled = value & A210_PADCFG_ST; + arg = enabled ? 1 : 0; + break; + case PIN_CONFIG_SLEW_RATE: + enabled = true; + arg = !!(value & A210_PADCFG_SL); + break; + default: + return -ENOTSUPP; + } + + *config = pinconf_to_config_packed(param, arg); + return enabled ? 0 : -EINVAL; +} + +static int a210_pinconf_group_get(struct pinctrl_dev *pctldev, unsigned int gsel, + unsigned long *config) +{ + const unsigned int *pins; + unsigned int npins, i; + unsigned long first = *config; + int ret; + + a210_pinctrl_get_group_pins(pctldev, gsel, &pins, &npins); + for (i = 0; i < npins; i++) { + unsigned long value = *config; + + ret = a210_pinconf_get(pctldev, pins[i], &value); + if (ret) + return ret; + if (i && value != first) + return -ENOTSUPP; + first = value; + } + *config = first; + return 0; +} + +static int a210_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *configs, + unsigned int num_configs) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, pin); + unsigned int i; + u16 mask, value; + + if ((uintptr_t)desc->drv_data & A210_PAD_NO_PADCFG) + return -ENOTSUPP; + if (pp->type == A210_PADCTRL_2) + return a210_sdio_pinconf_set(pp, pin, configs, num_configs); + + mask = 0; + value = 0; + for (i = 0; i < num_configs; i++) { + int param = pinconf_to_config_param(configs[i]); + u32 arg = pinconf_to_config_argument(configs[i]); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + mask |= A210_PADCFG_BIAS; + value &= ~A210_PADCFG_BIAS; + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + if (arg != 1 && arg != A210_PULL_DOWN_OHM) + return -EINVAL; + mask |= A210_PADCFG_BIAS; + value &= ~A210_PADCFG_BIAS; + value |= A210_PADCFG_PE; + break; + case PIN_CONFIG_BIAS_PULL_UP: + if (arg != 1 && arg != A210_PULL_UP_OHM && arg != A210_PULL_STRONG_OHM) + return -EINVAL; + mask |= A210_PADCFG_BIAS; + value &= ~A210_PADCFG_BIAS; + if (arg == A210_PULL_STRONG_OHM) + value |= A210_PADCFG_SPU; + else + value |= A210_PADCFG_PE | A210_PADCFG_PS; + break; + case PIN_CONFIG_DRIVE_STRENGTH: { + int strength = a210_drive_strength_from_ma(arg); + + if (strength < 0) + return strength; + mask |= A210_PADCFG_DS; + value &= ~A210_PADCFG_DS; + value |= strength; + break; + } + case PIN_CONFIG_INPUT_ENABLE: + mask |= A210_PADCFG_IE; + if (arg) + value |= A210_PADCFG_IE; + else + value &= ~A210_PADCFG_IE; + break; + case PIN_CONFIG_INPUT_SCHMITT_ENABLE: + mask |= A210_PADCFG_ST; + if (arg) + value |= A210_PADCFG_ST; + else + value &= ~A210_PADCFG_ST; + break; + case PIN_CONFIG_SLEW_RATE: + if (arg > 1) + return -EINVAL; + mask |= A210_PADCFG_SL; + if (arg) + value |= A210_PADCFG_SL; + else + value &= ~A210_PADCFG_SL; + break; + default: + return -ENOTSUPP; + } + } + + return a210_padcfg_rmw(pp, pin, mask, value); +} + +static int a210_pinconf_group_set(struct pinctrl_dev *pctldev, unsigned int gsel, + unsigned long *configs, unsigned int num_configs) +{ + const unsigned int *pins; + unsigned int npins, i; + int ret; + + a210_pinctrl_get_group_pins(pctldev, gsel, &pins, &npins); + for (i = 0; i < npins; i++) { + ret = a210_pinconf_set(pctldev, pins[i], configs, num_configs); + if (ret) + return ret; + } + return 0; +} + +#ifdef CONFIG_DEBUG_FS +static void a210_pinconf_dbg_show(struct pinctrl_dev *pctldev, struct seq_file *s, unsigned int pin) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + u32 value; + + if ((uintptr_t)pin_desc_get(pctldev, pin)->drv_data & A210_PAD_NO_PADCFG) + return; + + value = readl_relaxed(a210_padcfg(pp, pin)); + + if (pp->type != A210_PADCTRL_2) + value = (value >> a210_padcfg_shift(pp, pin)) & GENMASK(9, 0); + + seq_printf(s, " [0x%03x]", value); +} +#else +#define a210_pinconf_dbg_show NULL +#endif + +static const struct pinconf_ops a210_pinconf_ops = { + .pin_config_get = a210_pinconf_get, + .pin_config_group_get = a210_pinconf_group_get, + .pin_config_set = a210_pinconf_set, + .pin_config_group_set = a210_pinconf_group_set, + .pin_config_dbg_show = a210_pinconf_dbg_show, + .is_generic = true, +}; + +static int a210_pinmux_set(struct a210_pinctrl *pp, unsigned int pin, unsigned long muxdata, + enum a210_muxtype muxtype) +{ + void __iomem *muxcfg = a210_muxcfg(pp, pin); + unsigned int shift = a210_muxcfg_shift(pp, pin); + u32 mask, value, tmp; + + for (value = 0; muxdata; muxdata >>= 7, value++) { + if ((muxdata & GENMASK(6, 0)) == muxtype) + break; + } + if (!muxdata) { + dev_err(pp->pctl->dev, "invalid mux %s for pin %s\n", a210_muxtype_string[muxtype], + pin_get_name(pp->pctl, pin)); + return -EINVAL; + } + + mask = pp->mask_mux << shift; + value = value << shift; + + scoped_guard(raw_spinlock_irqsave, &pp->lock) { + tmp = readl_relaxed(muxcfg); + tmp = (tmp & ~mask) | value; + writel_relaxed(tmp, muxcfg); +#ifdef CONFIG_PM_SLEEP + pp->mux_state[pin * hweight32(pp->mask_mux) / 32].mask |= mask; +#endif + } + + return 0; +} + +static int a210_pinmux_set_mux(struct pinctrl_dev *pctldev, unsigned int fsel, unsigned int gsel) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + const struct function_desc *func = pinmux_generic_get_function(pctldev, fsel); + + return a210_pinmux_set(pp, pp->desc.pins[gsel].number, + (uintptr_t)pp->desc.pins[gsel].drv_data & A210_PAD_MUXDATA, + *(enum a210_muxtype *)func->data); +} + +static bool a210_mux_is_gpio(enum a210_muxtype muxtype) +{ + switch (muxtype) { + case A210_MUX_AOGPIO0: + case A210_MUX_AOGPIO1: + case A210_MUX_GPIO0: + case A210_MUX_GPIO1: + case A210_MUX_GPIO2: + case A210_MUX_GPIO3: + case A210_MUX_GPIO4: + return true; + default: + return false; + } +} + +static int a210_gpio_request_enable(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, + unsigned int offset) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + const struct pin_desc *desc = pin_desc_get(pctldev, offset); + unsigned long muxdata = (uintptr_t)desc->drv_data & A210_PAD_MUXDATA; + + if (pp->type == A210_PADCTRL_2) + return -EOPNOTSUPP; + + for (; muxdata; muxdata >>= 7) { + enum a210_muxtype muxtype = muxdata & GENMASK(6, 0); + + if (a210_mux_is_gpio(muxtype)) + return a210_pinmux_set(pp, offset, + (uintptr_t)desc->drv_data & A210_PAD_MUXDATA, + muxtype); + } + + dev_err(pp->pctl->dev, "no GPIO mux for pin %s\n", desc->name); + + return -EINVAL; +} + +static int a210_gpio_set_direction(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range, + unsigned int offset, bool input) +{ + struct a210_pinctrl *pp = pinctrl_dev_get_drvdata(pctldev); + + if (pp->type == A210_PADCTRL_2) + return -EOPNOTSUPP; + + return a210_padcfg_rmw(pp, offset, A210_PADCFG_IE, input ? A210_PADCFG_IE : 0); +} + +static const struct pinmux_ops a210_pinmux_ops = { + .get_functions_count = pinmux_generic_get_function_count, + .get_function_name = pinmux_generic_get_function_name, + .get_function_groups = pinmux_generic_get_function_groups, + .function_is_gpio = pinmux_generic_function_is_gpio, + .set_mux = a210_pinmux_set_mux, + .gpio_request_enable = a210_gpio_request_enable, + .gpio_set_direction = a210_gpio_set_direction, + .strict = true, +}; + +struct a210_function { + struct pinfunction function; + enum a210_muxtype type; +}; + +static bool a210_pin_has_function(const struct pinctrl_pin_desc *pin, + enum a210_muxtype type) +{ + unsigned long muxdata = (uintptr_t)pin->drv_data & A210_PAD_MUXDATA; + + for (; muxdata; muxdata >>= 7) + if ((muxdata & GENMASK(6, 0)) == type) + return true; + return false; +} + +static int a210_register_functions(struct a210_pinctrl *pp) +{ + struct device *dev = pp->pctl->dev; + unsigned int ngroups = a210_pinctrl_get_groups_count(pp->pctl); + enum a210_muxtype type; + + for (type = A210_MUX_AOUART; type < ARRAY_SIZE(a210_muxtype_string); type++) { + struct a210_function *function; + const char **groups; + unsigned int i, count = 0; + int ret; + + for (i = 0; i < ngroups; i++) + count += a210_pin_has_function(&pp->desc.pins[i], type); + if (!count) + continue; + + function = devm_kzalloc(dev, sizeof(*function), GFP_KERNEL); + if (!function) + return -ENOMEM; + groups = devm_kcalloc(dev, count, sizeof(*groups), GFP_KERNEL); + if (!groups) + return -ENOMEM; + function->type = type; + function->function.name = a210_muxtype_string[type]; + function->function.groups = groups; + function->function.ngroups = count; + if (a210_mux_is_gpio(type)) + function->function.flags = PINFUNCTION_FLAG_GPIO; + count = 0; + for (i = 0; i < ngroups; i++) + if (a210_pin_has_function(&pp->desc.pins[i], type)) + groups[count++] = a210_pinctrl_get_group_name(pp->pctl, i); + ret = pinmux_generic_add_pinfunction(pp->pctl, &function->function, + &function->type); + if (ret < 0) + return ret; + } + return 0; +} + +static int a210_pinctrl_enable_clock(struct a210_pinctrl *pp) +{ + int ret; + + if (pp->clk_enabled) + return 0; + + ret = clk_prepare_enable(pp->clk); + if (!ret) + pp->clk_enabled = true; + return ret; +} + +static void a210_pinctrl_disable_clock(void *data) +{ + struct a210_pinctrl *pp = data; + + if (pp->clk_enabled) { + clk_disable_unprepare(pp->clk); + pp->clk_enabled = false; + } +} + +static int a210_pinctrl_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + const struct a210_pad_group *group; + struct a210_pinctrl *pp; + int ret; + + group = device_get_match_data(dev); + if (!group) + return dev_err_probe(dev, -EINVAL, "missing pad group data\n"); + + pp = devm_kzalloc(dev, sizeof(*pp), GFP_KERNEL); + if (!pp) + return -ENOMEM; + + pp->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(pp->base)) + return PTR_ERR(pp->base); + + pp->desc.name = group->name; + pp->desc.pins = group->pins; + pp->desc.npins = group->npins; + pp->offset_mux = group->offset_mux; + pp->mask_mux = group->mask_mux; + pp->offset_cfg = group->offset_cfg; + pp->mask_cfg = group->mask_cfg; + pp->type = group->type; + pp->desc.pctlops = &a210_pinctrl_ops; + pp->desc.pmxops = &a210_pinmux_ops; + pp->desc.confops = &a210_pinconf_ops; + pp->desc.owner = THIS_MODULE; + raw_spin_lock_init(&pp->lock); + + pp->clk = devm_clk_get(dev, "pclk"); + if (IS_ERR(pp->clk)) + return dev_err_probe(dev, PTR_ERR(pp->clk), "error getting clock\n"); + + ret = a210_pinctrl_enable_clock(pp); + if (ret) + return dev_err_probe(dev, ret, "could not enable padctrl clock\n"); + ret = devm_add_action_or_reset(dev, a210_pinctrl_disable_clock, pp); + if (ret) + return ret; + + platform_set_drvdata(pdev, pp); + ret = devm_pinctrl_register_and_init(dev, &pp->desc, pp, &pp->pctl); + if (ret) + return dev_err_probe(dev, ret, "could not register pinctrl driver\n"); + + ret = a210_register_functions(pp); + if (ret) + return ret; + + return pinctrl_enable(pp->pctl); +} + +#ifdef CONFIG_PM_SLEEP +static void a210_pinctrl_save_regs(struct a210_register_state *state, + unsigned int count, void __iomem *base) +{ + unsigned int i; + + for (i = 0; i < count; i++) + if (state[i].mask) + state[i].value = readl(base + i * sizeof(u32)) & state[i].mask; +} + +static void a210_pinctrl_restore_regs(const struct a210_register_state *state, + unsigned int count, void __iomem *base) +{ + unsigned int i; + + for (i = 0; i < count; i++) { + u32 value; + + if (!state[i].mask) + continue; + value = readl(base + i * sizeof(u32)); + value = (value & ~state[i].mask) | state[i].value; + writel(value, base + i * sizeof(u32)); + } +} + +static int a210_pinctrl_suspend(struct device *dev) +{ + struct a210_pinctrl *pp = dev_get_drvdata(dev); + + if (!pp->clk_enabled) + return 0; + + scoped_guard(raw_spinlock_irqsave, &pp->lock) { + a210_pinctrl_save_regs(pp->cfg_state, ARRAY_SIZE(pp->cfg_state), + pp->base + pp->offset_cfg); + a210_pinctrl_save_regs(pp->mux_state, ARRAY_SIZE(pp->mux_state), + pp->base + pp->offset_mux); + } + if (pp->type != A210_PADCTRL_AON) + a210_pinctrl_disable_clock(pp); + + return 0; +} + +static int a210_pinctrl_resume(struct device *dev) +{ + struct a210_pinctrl *pp = dev_get_drvdata(dev); + int ret; + + ret = a210_pinctrl_enable_clock(pp); + if (ret) + return dev_err_probe(dev, ret, "could not enable padctrl clock\n"); + + /* Leave firmware-owned and reserved fields at their current values. */ + scoped_guard(raw_spinlock_irqsave, &pp->lock) { + a210_pinctrl_restore_regs(pp->cfg_state, ARRAY_SIZE(pp->cfg_state), + pp->base + pp->offset_cfg); + a210_pinctrl_restore_regs(pp->mux_state, ARRAY_SIZE(pp->mux_state), + pp->base + pp->offset_mux); + } + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +static const struct dev_pm_ops a210_pinctrl_dev_pm_ops = { + SET_LATE_SYSTEM_SLEEP_PM_OPS(a210_pinctrl_suspend, a210_pinctrl_resume) +}; + +static const struct of_device_id a210_pinctrl_of_match[] = { + { .compatible = "zhihe,a210-aon-pinctrl", .data = &a210_group0 }, + { .compatible = "zhihe,a210-peri1-pinctrl", .data = &a210_group1 }, + { .compatible = "zhihe,a210-peri2-pinctrl", .data = &a210_group2 }, + { .compatible = "zhihe,a210-peri3-pinctrl", .data = &a210_group3 }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, a210_pinctrl_of_match); + +static struct platform_driver a210_pinctrl_driver = { + .probe = a210_pinctrl_probe, + .driver = { + .name = "pinctrl-a210", + .of_match_table = a210_pinctrl_of_match, + .pm = &a210_pinctrl_dev_pm_ops, + }, +}; +module_platform_driver(a210_pinctrl_driver); + +MODULE_DESCRIPTION("Pinctrl driver for the ZhiHe A210 SoC"); +MODULE_AUTHOR("Zhiguo Zhu "); +MODULE_LICENSE("GPL"); -- 2.55.0