Hi Thierry, On Mon, 19 May 2025 at 23:57, Thierry Bultel <thierry.bultel.yh@xxxxxxxxxxxxxx> wrote: > Add basic support, pinmode is not supported yet. > > Signed-off-by: Thierry Bultel <thierry.bultel.yh@xxxxxxxxxxxxxx> Thanks for your patch! > --- a/drivers/pinctrl/renesas/Kconfig > +++ b/drivers/pinctrl/renesas/Kconfig > @@ -44,6 +44,7 @@ config PINCTRL_RENESAS > select PINCTRL_RZG2L if ARCH_R9A09G047 > select PINCTRL_RZG2L if ARCH_R9A09G056 > select PINCTRL_RZG2L if ARCH_R9A09G057 > + select PINCTRL_RZT2H if ARCH_R9A09G077 > select PINCTRL_PFC_SH7203 if CPU_SUBTYPE_SH7203 > select PINCTRL_PFC_SH7264 if CPU_SUBTYPE_SH7264 > select PINCTRL_PFC_SH7269 if CPU_SUBTYPE_SH7269 > @@ -261,6 +262,18 @@ config PINCTRL_RZV2M > This selects GPIO and pinctrl driver for Renesas RZ/V2M > platforms. > > +config PINCTRL_RZT2H Please preserve sort order. > + bool "pin control support for RZ/T2H" > + depends on OF > + depends on ARCH_R9A09G077 || COMPILE_TEST > + select GPIOLIB > + select GENERIC_PINCTRL_GROUPS > + select GENERIC_PINMUX_FUNCTIONS > + select GENERIC_PINCONF > + help > + This selects GPIO and pinctrl driver for Renesas RZ/T2H > + platforms. > + > config PINCTRL_PFC_SH7203 > bool "pin control support for SH7203" if COMPILE_TEST > select PINCTRL_SH_FUNC_GPIO > diff --git a/drivers/pinctrl/renesas/Makefile b/drivers/pinctrl/renesas/Makefile > index 2ba623e04bf8..ef877c516225 100644 > --- a/drivers/pinctrl/renesas/Makefile > +++ b/drivers/pinctrl/renesas/Makefile > @@ -49,6 +49,7 @@ obj-$(CONFIG_PINCTRL_PFC_SHX3) += pfc-shx3.o > obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o > obj-$(CONFIG_PINCTRL_RZA2) += pinctrl-rza2.o > obj-$(CONFIG_PINCTRL_RZG2L) += pinctrl-rzg2l.o > +obj-$(CONFIG_PINCTRL_RZT2H) += pinctrl-rzt2h.o Please preserve sort order. > obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o > obj-$(CONFIG_PINCTRL_RZV2M) += pinctrl-rzv2m.o > > diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c > new file mode 100644 > index 000000000000..dd2772672716 > --- /dev/null > +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c > @@ -0,0 +1,783 @@ > +// SPDX-License-Identifier: GPL-2.0 > +/* > + * Renesas RZ/T2H Pin Control and GPIO driver core > + * > + * Copyright (C) 2025 Renesas Electronics Corporation. > + */ > + > +#include <linux/bitops.h> > +#include <linux/clk.h> > +#include <linux/gpio/driver.h> > +#include <linux/io.h> > +#include <linux/module.h> > +#include <linux/of_device.h> > +#include <linux/pinctrl/consumer.h> > +#include <linux/pinctrl/pinctrl.h> > +#include <linux/pinctrl/pinmux.h> > +#include <linux/platform_device.h> > +#include <linux/spinlock.h> > +#include <linux/mutex.h> > + > +#include <dt-bindings/pinctrl/rzt2h-pinctrl.h> > + > +#include "../core.h" > +#include "../pinconf.h" > +#include "../pinmux.h" > + > +#define DRV_NAME "pinctrl-rzt2h" > + > +/* > + * Use 16 lower bits [15:0] for pin identifier > + * Use 16 higher bits [31:16] for pin mux function > + */ > +#define MUX_PIN_ID_MASK GENMASK(15, 0) > +#define MUX_FUNC_MASK GENMASK(31, 16) > + > +/* > + * n indicates number of pins in the port, a is the register index > + * and f is pin configuration capabilities supported. > + */ > +#define RZT2H_GPIO_PORT_PACK(n, a, f) (((n) << 28) | ((a) << 20) | (f)) Unused... > + > +#define RZT2H_GPIO_PORT_GET_PINCNT(x) FIELD_GET(GENMASK(30, 28), (x)) > +#define RZT2H_GPIO_PORT_GET_INDEX(x) FIELD_GET(GENMASK(26, 20), (x)) > +#define RZT2H_GPIO_PORT_GET_CFGS(x) FIELD_GET(GENMASK(19, 0), (x)) > + > +/* > + * BIT(31) indicates dedicated pin, p is the register index while > + * referencing to SR/IEN/IOLH/FILxx registers, b is the register bits RZ/T2H does not have SR/IEN/IOLH/FILxx registers. > + * (b * 8) and f is the pin configuration capabilities supported. > + */ > +#define RZT2H_SINGLE_PIN BIT(31) > +#define RZT2H_SINGLE_PIN_PACK(p, b, f) (RZT2H_SINGLE_PIN | \ > + ((p) << 24) | ((b) << 20) | (f)) > + > +#define RZT2H_SINGLE_PIN_GET_PORT_OFFSET(x) FIELD_GET(GENMASK(30, 24), (x)) > +#define RZT2H_SINGLE_PIN_GET_BIT(x) FIELD_GET(GENMASK(22, 20), (x)) > +#define RZT2H_SINGLE_PIN_GET_CFGS(x) FIELD_GET(GENMASK(19, 0), (x)) ... until here. > + > +#define P(n) (0x001 * (n)) This does not seem to conflict with a definition in a public header file yet, surprisingly ;-) > +#define PM(n) (0x200 + 0x002 * (n)) > +#define PMC(n) (0x400 + 0x001 * (n)) > +#define PFC(n) (0x600 + 0x008 * (n)) > +#define PIN(n) (0x800 + 0x001 * (n)) Perhaps use m instead of n, to match the documentation? I would use decimal values for the (small) multipliers. > +#define DRCTL(n) (0xA00 + 0x008 * (n)) Unused > + > +#define RSELPSR 0x1F04 Unused > + > +#define PM_MASK 0x03 GENMASK(1, 0) > +#define PFC_MASK 0x3FULL GENMASK_ULL(5, 0) > +#define PM_INPUT 0x1 BIT(0) > +#define PM_OUTPUT 0x2 BIT(1) > +#define SR_MASK 0x01 Incorrect, not really a mask, and part of the DRCTL register, so #define DRCTL_SR BIT(5) > +#define SCHMITT_MASK 0x01 Incorrect, not really a mask, and part of the DRCTL register, so #define DRCTL_SMT BIT(4) > +#define IOLH_MASK 0x03 Do you mean DRV_MASK? #define DRCTL_DRV_MASK GENMASK(1, 0) > +#define PUPD_MASK 0x03 #define DRCTL_PUPD_MASK GENMASK(3, 2) Anyway, all four are unused (for now, I assume). May be easier to read if you interleave the register offset definitions and the corresponding register bit definitions. > + > +#define RZT2H_PIN_ID_TO_PORT(id) ((id) / RZT2H_PINS_PER_PORT) > +#define RZT2H_PIN_ID_TO_PORT_OFFSET(id) (RZT2H_PIN_ID_TO_PORT(id) + 0x10) Unused > +#define RZT2H_PIN_ID_TO_PIN(id) ((id) % RZT2H_PINS_PER_PORT) > + > +struct rzt2h_dedicated_configs { > + const char *name; > + u32 config; > +}; Unused > + > +struct rzt2h_pinctrl_data { > + const char * const *port_pins; > + unsigned int n_port_pins; > +}; > + > +struct rzt2h_pinctrl { > + struct pinctrl_dev *pctl; > + struct pinctrl_desc desc; > + struct pinctrl_pin_desc *pins; > + const struct rzt2h_pinctrl_data *data; > + void __iomem *base0, *base1; > + struct device *dev; > + struct clk *clk; > + struct gpio_chip gpio_chip; > + struct pinctrl_gpio_range gpio_range; This seems to be set only. > + int safety_region; Unused > + spinlock_t lock; > + struct mutex mutex; Please add comments to these two, to clarify what they are protecting against. > +}; > + > +#define RZT2H_PORT_SAFETY_LAST 12 > + > +#define RZT2H_PINCTRL_REG_ACCESS(size, type) \ > +static void rzt2h_pinctrl_write##size(struct rzt2h_pinctrl *pctrl, u8 port, type val, u16 offset) \ unsigned int offset > +{ \ > + if (port > RZT2H_PORT_SAFETY_LAST) \ > + write##size(val, pctrl->base0 + offset); \ > + else \ > + write##size(val, pctrl->base1 + offset); \ > +} \ > +\ > +static type rzt2h_pinctrl_read##size(struct rzt2h_pinctrl *pctrl, u8 port, u16 offset) \ unsigned int offset > +{ \ > + if (port > RZT2H_PORT_SAFETY_LAST) \ > + return read##size(pctrl->base0 + offset); \ > + else \ > + return read##size(pctrl->base1 + offset); \ > +} > + > +RZT2H_PINCTRL_REG_ACCESS(b, u8) > +RZT2H_PINCTRL_REG_ACCESS(w, u16) > +RZT2H_PINCTRL_REG_ACCESS(q, u64) > + > +static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl, > + u8 port, u8 pin, u64 func) > +{ > + u64 reg_pfc; > + u32 reg; In other functions, you use "u16 reg16" and "u8 reg8". Please be consistent and pick one style. > + > + guard(spinlock_irqsave)(&pctrl->lock); > + > + /* Set pin to 'Non-use (Hi-Z input protection)' */ > + reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); > + reg &= ~(PM_MASK << (pin * 2)); > + rzt2h_pinctrl_writew(pctrl, port, reg, PM(port)); > + > + /* Temporarily switch to GPIO mode with PMC register */ > + reg = rzt2h_pinctrl_readb(pctrl, port, PMC(port)); > + rzt2h_pinctrl_writeb(pctrl, port, reg & ~BIT(pin), PMC(port)); > + > + /* Select Pin function mode with PFC register */ > + reg_pfc = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); > + reg_pfc &= ~(PFC_MASK << (pin * 8)); > + rzt2h_pinctrl_writeq(pctrl, port, reg_pfc | (func << (pin * 8)), PFC(port)); > + > + /* Switch to Peripheral pin function with PMC register */ > + reg = rzt2h_pinctrl_readb(pctrl, port, PMC(port)); > + rzt2h_pinctrl_writeb(pctrl, port, reg | BIT(pin), PMC(port)); > +}; > + > +static int rzt2h_pinctrl_set_mux(struct pinctrl_dev *pctldev, > + unsigned int func_selector, > + unsigned int group_selector) > +{ > + struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev); > + struct function_desc *func; > + unsigned int i, *psel_val; > + struct group_desc *group; > + const unsigned int *pins; > + > + func = pinmux_generic_get_function(pctldev, func_selector); > + if (!func) > + return -EINVAL; > + group = pinctrl_generic_get_group(pctldev, group_selector); > + if (!group) > + return -EINVAL; > + > + psel_val = func->data; > + pins = group->grp.pins; > + > + for (i = 0; i < group->grp.npins; i++) { > + dev_dbg(pctrl->dev, "port:%u pin: %u PSEL:%u\n", Please use consistent spacing around the colons. > + RZT2H_PIN_ID_TO_PORT(pins[i]), RZT2H_PIN_ID_TO_PIN(pins[i]), > + psel_val[i]); > + rzt2h_pinctrl_set_pfc_mode(pctrl, RZT2H_PIN_ID_TO_PORT(pins[i]), > + RZT2H_PIN_ID_TO_PIN(pins[i]), psel_val[i]); > + } > + > + return 0; > +}; > +static void rzt2h_gpio_set_direction(struct rzt2h_pinctrl *pctrl, u32 port, > + u8 bit, bool output) > +{ > + u16 reg16; > + > + guard(spinlock_irqsave)(&pctrl->lock); > + > + reg16 = rzt2h_pinctrl_readw(pctrl, port, PM(port)); > + reg16 &= ~(PM_MASK << (bit * 2)); > + > + reg16 |= (output ? PM_OUTPUT : PM_INPUT) << (bit * 2); > + rzt2h_pinctrl_writew(pctrl, port, reg16, PM(port)); > +} > + > +static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) > +{ > + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); > + u32 port = RZT2H_PIN_ID_TO_PORT(offset); > + u8 bit = RZT2H_PIN_ID_TO_PIN(offset); > + > + if (!(rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit))) { Invert the logic and return early, to reduce indentation? > + u16 reg16; > + > + reg16 = rzt2h_pinctrl_readw(pctrl, port, PM(port)); > + reg16 = (reg16 >> (bit * 2)) & PM_MASK; > + if (reg16 == PM_OUTPUT) The hardware supports enabling both input and output, so I think you better check for "(reg16 >> (bit * 2)) & PM_OUTPUT". > + return GPIO_LINE_DIRECTION_OUT; > + } > + > + return GPIO_LINE_DIRECTION_IN; > +} > + > +static int rzt2h_gpio_direction_input(struct gpio_chip *chip, > + unsigned int offset) > +{ > + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); > + u32 port = RZT2H_PIN_ID_TO_PORT(offset); > + u8 bit = RZT2H_PIN_ID_TO_PIN(offset); > + > + rzt2h_gpio_set_direction(pctrl, port, bit, false); > + > + return 0; > +} > + > +static void rzt2h_gpio_set(struct gpio_chip *chip, unsigned int offset, > + int value) > +{ > + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); > + u32 port = RZT2H_PIN_ID_TO_PORT(offset); > + u8 bit = RZT2H_PIN_ID_TO_PIN(offset); > + u8 reg8; > + > + guard(spinlock_irqsave)(&pctrl->lock); > + > + reg8 = rzt2h_pinctrl_readb(pctrl, port, P(port)); > + > + if (value) > + rzt2h_pinctrl_writeb(pctrl, port, reg8 | BIT(bit), P(port)); > + else > + rzt2h_pinctrl_writeb(pctrl, port, reg8 & ~BIT(bit), P(port)); > +} > + > +static int rzt2h_gpio_direction_output(struct gpio_chip *chip, > + unsigned int offset, int value) Please move this function up, just below rzt2h_gpio_direction_input(). > +{ > + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); > + u32 port = RZT2H_PIN_ID_TO_PORT(offset); > + u8 bit = RZT2H_PIN_ID_TO_PIN(offset); > + > + rzt2h_gpio_set(chip, offset, value); > + rzt2h_gpio_set_direction(pctrl, port, bit, true); > + > + return 0; > +} > + > +static int rzt2h_gpio_get(struct gpio_chip *chip, unsigned int offset) > +{ > + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); > + u32 port = RZT2H_PIN_ID_TO_PORT(offset); > + u8 bit = RZT2H_PIN_ID_TO_PIN(offset); > + u16 reg16; > + > + reg16 = rzt2h_pinctrl_readw(pctrl, port, PM(port)); > + reg16 = (reg16 >> (bit * 2)) & PM_MASK; > + > + if (reg16 == PM_INPUT) "if (reg16 & PM_INPUT)", to handle both PM_INPUT and PM_OUTPUT set? > + return !!(rzt2h_pinctrl_readb(pctrl, port, PIN(port)) & BIT(bit)); > + else if (reg16 == PM_OUTPUT) > + return !!(rzt2h_pinctrl_readb(pctrl, port, P(port)) & BIT(bit)); > + else > + return -EINVAL; > +} > +static int rzt2h_pinctrl_probe(struct platform_device *pdev) > +{ > + struct rzt2h_pinctrl *pctrl; > + int ret; > + > + pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); > + if (!pctrl) > + return -ENOMEM; > + > + pctrl->dev = &pdev->dev; > + > + pctrl->data = of_device_get_match_data(&pdev->dev); > + if (!pctrl->data) > + return -EINVAL; > + > + pctrl->base0 = devm_platform_ioremap_resource(pdev, 0); > + if (IS_ERR(pctrl->base0)) > + return PTR_ERR(pctrl->base0); > + > + pctrl->base1 = devm_platform_ioremap_resource(pdev, 1); > + if (IS_ERR(pctrl->base1)) > + return PTR_ERR(pctrl->base1); > + > + pctrl->clk = devm_clk_get_optional(pctrl->dev, NULL); Why optional? > + if (IS_ERR(pctrl->clk)) { > + ret = PTR_ERR(pctrl->clk); > + return dev_err_probe(pctrl->dev, ret, "failed to get GPIO clk\n"); > + } > + > + spin_lock_init(&pctrl->lock); > + mutex_init(&pctrl->mutex); > + platform_set_drvdata(pdev, pctrl); > + > + if (pctrl->clk) { > + ret = clk_prepare_enable(pctrl->clk); > + if (ret) > + return dev_err_probe(pctrl->dev, ret, > + "failed to enable GPIO clk\n"); > + ret = devm_add_action_or_reset(&pdev->dev, rzt2h_pinctrl_clk_disable, > + pctrl->clk); > + if (ret) > + return dev_err_probe(pctrl->dev, ret, > + "failed to register GPIO clk disable action\n"); > + } > + > + return rzt2h_pinctrl_register(pctrl); > +} > +core_initcall(rzt2h_pinctrl_init); MODULE_DESCRIPTION/AUTHOR? Gr{oetje,eeting}s, Geert -- Geert Uytterhoeven -- There's lots of Linux beyond ia32 -- geert@xxxxxxxxxxxxxx In personal conversations with technical people, I call myself a hacker. But when I'm talking to journalists I just say "programmer" or something like that. -- Linus Torvalds