From d48a308fabd6dc7676d570f0bc971817e39d824c Mon Sep 17 00:00:00 2001 From: Biju Das Date: Thu, 23 Jul 2026 16:41:47 +0100 Subject: [PATCH 01/15] pinctrl: renesas: rzg2l: Add SD channel POC support for RZ/G3L MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add power-on control (POC) support for SD channels 1 and 2 on the RZ/G3L SoC (r9a08g046). Introduce PIN_CFG_IO_VMC_SD2 capability flag (bit 22) and SD_CH2_POC register offset (0x3024). Extend rzg2l_caps_to_pwr_reg() to return SD_CH2_POC when PIN_CFG_IO_VMC_SD2 is set. Replace RZG3L_MPXED_PIN_FUNCS() with RZG2L_MPXED_COMMON_PIN_FUNCS() for port PG and PH pins, dropping PIN_CFG_SOFT_PS which is inappropriate for SD pins, and annotate them with PIN_CFG_IO_VMC_SD1 and PIN_CFG_IO_VMC_SD2 respectively. Annotate all RZ/G3L SD0 dedicated pins (CLK, CMD, RST#, DS, DAT0–DAT7) with PIN_CFG_IO_VMC_SD0 so that power-source register lookups work correctly for those pins. Add sd_ch2 field to rzg2l_register_offsets and rzg2l_pinctrl_reg_cache to save and restore the SD_CH2_POC register across suspend/resume cycles. Signed-off-by: Biju Das Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260723154154.319674-2-biju.das.jz@bp.renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 74 +++++++++++++++++-------- 1 file changed, 50 insertions(+), 24 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index be52d47d77ae..e3e24ce917c9 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -69,6 +69,7 @@ #define PIN_CFG_PVDD1833_OTH_AWO_POC BIT(19) /* known on RZ/G3L only */ #define PIN_CFG_PVDD1833_OTH_ISO_POC BIT(20) /* known on RZ/G3L only */ #define PIN_CFG_WDTOVF_N_POC BIT(21) /* known on RZ/G3L only */ +#define PIN_CFG_IO_VMC_SD2 BIT(22) /* known on RZ/G3L only */ #define RZG2L_SINGLE_PIN BIT_ULL(63) /* Dedicated pin */ #define RZG2L_VARIABLE_CFG BIT_ULL(62) /* Variable cfg for port pins */ @@ -254,6 +255,7 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = { * struct rzg2l_register_offsets - specific register offsets * @pwpr: PWPR register offset * @sd_ch: SD_CH register offset + * @sd_ch2: SD_CH2_POC register offset * @eth_poc: ETH_POC register offset * @oen: OEN register offset * @qspi: QSPI register offset @@ -262,6 +264,7 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = { struct rzg2l_register_offsets { u16 pwpr; u16 sd_ch; + u16 sd_ch2; u16 eth_poc; u16 oen; u16 qspi; @@ -368,6 +371,7 @@ struct rzg2l_pinctrl_pin_settings { * @nod: NOD registers cache * @clone: Clone register cache * @sd_ch: SD_CH registers cache + * @sd_ch2: SD_CH2_POC registers cache * @eth_poc: ET_POC registers cache * @oen: Output Enable register cache * @other_poc: OTHER_POC register cache @@ -386,6 +390,7 @@ struct rzg2l_pinctrl_reg_cache { u32 *nod[2]; u32 clone; u8 sd_ch[2]; + u8 sd_ch2; u8 eth_poc[2]; u8 oen; u8 other_poc; @@ -474,20 +479,32 @@ static const u64 r9a08g046_variable_pin_cfg[] = { RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 5, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 6, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PE, 7, RZG3L_MPXED_ETH_PIN_FUNCS(ETH1)), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 0, RZG3L_MPXED_PIN_FUNCS(B)), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 1, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 2, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 3, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 4, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 5, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 0, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IO_VMC_SD1), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 1, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 2, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 3, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | PIN_CFG_IEN | + PIN_CFG_IO_VMC_SD1), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 4, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 5, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD1), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 6, RZG3L_MPXED_PIN_FUNCS_POC(B, ISO)), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PG, 7, RZG3L_MPXED_PIN_FUNCS_POC(B, ISO)), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 0, RZG3L_MPXED_PIN_FUNCS(B)), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 1, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 2, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 3, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 4, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), - RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 5, RZG3L_MPXED_PIN_FUNCS(B) | PIN_CFG_IEN), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 0, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IO_VMC_SD2), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 1, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD2), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 2, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD2), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 3, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD2), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 4, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD2), + RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PH, 5, RZG2L_MPXED_COMMON_PIN_FUNCS(B) | + PIN_CFG_IEN | PIN_CFG_IO_VMC_SD2), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 0, RZG3L_MPXED_PIN_FUNCS(A) | PIN_CFG_IEN), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 1, RZG3L_MPXED_PIN_FUNCS(A)), RZG2L_VARIABLE_PIN_CFG_PACK(RZG3L_PJ, 2, RZG3L_MPXED_PIN_FUNCS(A)), @@ -1053,6 +1070,8 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, return SD_CH(regs->sd_ch, 0); if (caps & PIN_CFG_IO_VMC_SD1) return SD_CH(regs->sd_ch, 1); + if (caps & PIN_CFG_IO_VMC_SD2) + return regs->sd_ch2; if (caps & PIN_CFG_IO_VMC_ETH0) return ETH_POC(regs->eth_poc, 0); if (caps & PIN_CFG_IO_VMC_ETH1) @@ -2677,28 +2696,28 @@ static const struct rzg2l_dedicated_configs rzg3l_dedicated_pins[] = { (PIN_CFG_IOLH_A | PIN_CFG_PUPD | PIN_CFG_PVDD1833_OTH_AWO_POC)) }, { "SCIF0_TXD", RZG2L_SINGLE_PIN_PACK(0x6, 1, (PIN_CFG_IOLH_A | PIN_CFG_PUPD | PIN_CFG_PVDD1833_OTH_AWO_POC)) }, - { "SD0_CLK", RZG2L_SINGLE_PIN_PACK(0x9, 0, PIN_CFG_IOLH_B) }, + { "SD0_CLK", RZG2L_SINGLE_PIN_PACK(0x9, 0, PIN_CFG_IOLH_B | PIN_CFG_IO_VMC_SD0) }, { "SD0_CMD", RZG2L_SINGLE_PIN_PACK(0x9, 1, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, - { "SD0_RST#", RZG2L_SINGLE_PIN_PACK(0x9, 2, PIN_CFG_IOLH_B) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, + { "SD0_RST#", RZG2L_SINGLE_PIN_PACK(0x9, 2, PIN_CFG_IOLH_B | PIN_CFG_IO_VMC_SD0) }, { "SD0_DS", RZG2L_SINGLE_PIN_PACK(0x9, 5, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT0", RZG2L_SINGLE_PIN_PACK(0x0a, 0, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT1", RZG2L_SINGLE_PIN_PACK(0x0a, 1, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT2", RZG2L_SINGLE_PIN_PACK(0x0a, 2, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT3", RZG2L_SINGLE_PIN_PACK(0x0a, 3, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT4", RZG2L_SINGLE_PIN_PACK(0x0a, 4, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT5", RZG2L_SINGLE_PIN_PACK(0x0a, 5, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT6", RZG2L_SINGLE_PIN_PACK(0x0a, 6, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, { "SD0_DAT7", RZG2L_SINGLE_PIN_PACK(0x0a, 7, - (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD)) }, + (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_PUPD | PIN_CFG_IO_VMC_SD0)) }, }; static const u32 r9a08g046_clone_channel_data[] = { @@ -3672,6 +3691,9 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev) cache->eth_poc[i] = readb(pctrl->base + ETH_POC(regs->eth_poc, i)); } + if (regs->sd_ch2) + cache->sd_ch2 = readb(pctrl->base + regs->sd_ch2); + if (regs->qspi) cache->qspi = readb(pctrl->base + regs->qspi); cache->oen = readb(pctrl->base + pctrl->data->hwcfg->regs.oen); @@ -3724,6 +3746,9 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev) rzg2l_oen_write_with_pwpr(pctrl, cache->oen); raw_spin_unlock_irqrestore(&pctrl->lock, flags); + if (regs->sd_ch2) + writeb(cache->sd_ch2, pctrl->base + regs->sd_ch2); + for (u8 i = 0; i < 2; i++) { if (regs->sd_ch) writeb(cache->sd_ch[i], pctrl->base + SD_CH(regs->sd_ch, i)); @@ -3791,6 +3816,7 @@ static const struct rzg2l_hwcfg rzg3l_hwcfg = { .regs = { .pwpr = 0x3000, .sd_ch = 0x3004, + .sd_ch2 = 0x3024, .eth_poc = 0x3010, .oen = 0x3018, .other_poc = OTHER_POC, From 11e4c9251295b4513a244cb73bc902b5f6a34f8e Mon Sep 17 00:00:00 2001 From: Josua Mayer Date: Fri, 4 Sep 2026 13:56:21 +0200 Subject: [PATCH 02/15] dt-bindings: pinctrl: renesas,rzg2l-pinctrl: Allow input/output-disable The list of allowed pinconf properties already includes input-enable and output-enable, but omits their -disable counterparts. Each pair controls the same bit. Where enable (set) is supported so is disable (clear). Add input-disable and output-disable to the list. Reviewed-by: Linus Walleij Reviewed-by: Geert Uytterhoeven Signed-off-by: Josua Mayer Link: https://patch.msgid.link/20260904-rzg2-sr-boards-v8-2-94c4674927e8@solid-run.com Signed-off-by: Geert Uytterhoeven --- .../devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml index fb1fe1ea759f..a551996623c0 100644 --- a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml @@ -135,7 +135,9 @@ additionalProperties: gpios: true input: true input-enable: true + input-disable: true output-enable: true + output-disable: true output-high: true output-low: true line-name: true From 342a7ce6d2f7b13037f0f993ca760779c53834d9 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 4 Sep 2026 15:54:21 +0300 Subject: [PATCH 03/15] pinctrl: renesas: rzg2l: Generalize power source code The current functions used to get/set the pin power source check the OTHER_POC register, which is specific to the RZ/G3L SoC only. To allow the code to be extended for other power source functionalities (e.g. I3C on RZ/G3S), generalize the functions used to get/set the pin power source. For this, introduce the struct rzg2l_register_masks data structure whose purpose is to store SoC specific register bit masks. The members of this structure are then used in rzg2l_caps_to_pwr_reg() to retrieve the bitmask corresponding to a SoC specific power source capability. The conversion between HW specific power source values and SW specific power source values is now handled through rzg2l_pwr_reg_val_to_ps() and rzg2l_ps_to_pwr_reg_val(). Finally, to keep the code generic, the register update in rzg2l_set_power_source() was changed to a read-modify-write approach to cover all cases. Reviewed-by: Geert Uytterhoeven Tested-by: Wolfram Sang Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260904125425.412632-2-claudiu.beznea+renesas@tuxon.dev Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 188 +++++++++++++++--------- 1 file changed, 117 insertions(+), 71 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index e3e24ce917c9..40bf8f9e8f2f 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -187,6 +187,7 @@ #define PVDD_2500 2 /* I/O domain voltage 2.5V */ #define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ #define PVDD_3300 0 /* I/O domain voltage >= 3.3V */ +#define PVDD_MASK 0x3 #define PWPR_B0WI BIT(7) /* Bit Write Disable */ #define PWPR_PFCWE BIT(6) /* PFC Register Write Enable */ @@ -271,6 +272,23 @@ struct rzg2l_register_offsets { u16 other_poc; }; +/** + * struct rzg2l_register_masks - Masks for different RZ/G2L pinctrl functionalities + * @other_poc_pvdd1833_oth_awo_poc: PVDD1833_OTH_AWO_POC mask + * @other_poc_pvdd1833_oth_iso_poc: PVDD1833_OTH_ISO_POC mask + * @other_poc_wdtovf_n_poc: WDTOVF_N_POC mask + */ +struct rzg2l_register_masks { + union { + /* RZ/G3L masks */ + struct { + u8 other_poc_pvdd1833_oth_awo_poc; + u8 other_poc_pvdd1833_oth_iso_poc; + u8 other_poc_wdtovf_n_poc; + }; + }; +}; + /** * enum rzg2l_iolh_index - starting indices in IOLH specific arrays * @RZG2L_IOLH_IDX_1V8: starting index for 1V8 power source @@ -291,6 +309,8 @@ enum rzg2l_iolh_index { /** * struct rzg2l_hwcfg - hardware configuration data structure * @regs: hardware specific register offsets + * @masks: hardware specific masks for various functionalities available in + * the registers described by regs * @iolh_groupa_ua: IOLH group A uA specific values * @iolh_groupb_ua: IOLH group B uA specific values * @iolh_groupc_ua: IOLH group C uA specific values @@ -304,6 +324,7 @@ enum rzg2l_iolh_index { */ struct rzg2l_hwcfg { const struct rzg2l_register_offsets regs; + const struct rzg2l_register_masks masks; u16 iolh_groupa_ua[RZG2L_IOLH_IDX_MAX]; u16 iolh_groupb_ua[RZG2L_IOLH_IDX_MAX]; u16 iolh_groupc_ua[RZG2L_IOLH_IDX_MAX]; @@ -1064,29 +1085,74 @@ static void rzg2l_rmw_pin_config(struct rzg2l_pinctrl *pctrl, u32 offset, } static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, - u32 caps, u8 *mask) + const struct rzg2l_register_masks *masks, + u32 caps, u16 *offset, u8 *mask) { - if (caps & PIN_CFG_IO_VMC_SD0) - return SD_CH(regs->sd_ch, 0); - if (caps & PIN_CFG_IO_VMC_SD1) - return SD_CH(regs->sd_ch, 1); - if (caps & PIN_CFG_IO_VMC_SD2) - return regs->sd_ch2; - if (caps & PIN_CFG_IO_VMC_ETH0) - return ETH_POC(regs->eth_poc, 0); - if (caps & PIN_CFG_IO_VMC_ETH1) - return ETH_POC(regs->eth_poc, 1); - if (caps & PIN_CFG_IO_VMC_QSPI) - return QSPI; - if (caps & PIN_CFG_OTHER_POC_MASK) { - if (caps & PIN_CFG_PVDD1833_OTH_AWO_POC) - *mask = BIT(0); - else if (caps & PIN_CFG_PVDD1833_OTH_ISO_POC) - *mask = BIT(1); - else - *mask = BIT(2); + *mask = PVDD_MASK; - return OTHER_POC; + if (caps & PIN_CFG_IO_VMC_SD0) { + *offset = SD_CH(regs->sd_ch, 0); + return 0; + } + if (caps & PIN_CFG_IO_VMC_SD1) { + *offset = SD_CH(regs->sd_ch, 1); + return 0; + } + if (caps & PIN_CFG_IO_VMC_SD2) { + *offset = regs->sd_ch2; + return 0; + } + if (caps & PIN_CFG_IO_VMC_ETH0) { + *offset = ETH_POC(regs->eth_poc, 0); + return 0; + } + if (caps & PIN_CFG_IO_VMC_ETH1) { + *offset = ETH_POC(regs->eth_poc, 1); + return 0; + } + if (caps & PIN_CFG_IO_VMC_QSPI) { + *offset = regs->qspi; + return 0; + } + if (caps & PIN_CFG_OTHER_POC_MASK) { + *offset = regs->other_poc; + if (caps & PIN_CFG_PVDD1833_OTH_AWO_POC) + *mask = masks->other_poc_pvdd1833_oth_awo_poc; + else if (caps & PIN_CFG_PVDD1833_OTH_ISO_POC) + *mask = masks->other_poc_pvdd1833_oth_iso_poc; + else + *mask = masks->other_poc_wdtovf_n_poc; + return 0; + } + + return -EINVAL; +} + +static int rzg2l_pwr_reg_val_to_ps(u8 val, u32 caps) +{ + switch (val) { + case PVDD_1800: + return 1800; + case PVDD_2500: + return 2500; + case PVDD_3300: + return 3300; + } + + return -EINVAL; +} + +static int rzg2l_ps_to_pwr_reg_val(u32 ps, u32 caps) +{ + switch (ps) { + case 1800: + return PVDD_1800; + case 2500: + if (!(caps & (PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1))) + return -EINVAL; + return PVDD_2500; + case 3300: + return PVDD_3300; } return -EINVAL; @@ -1096,76 +1162,51 @@ static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps { const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; - u8 val, mask; - int pwr_reg; + const struct rzg2l_register_masks *masks = &hwcfg->masks; + u8 mask, val; + u16 offset; + int ret; if (caps & PIN_CFG_SOFT_PS) return pctrl->settings[pin].power_source; - pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask); - if (pwr_reg < 0) - return pwr_reg; + ret = rzg2l_caps_to_pwr_reg(regs, masks, caps, &offset, &mask); + if (ret) + return ret; - val = readb(pctrl->base + pwr_reg); - if (pwr_reg == OTHER_POC) - val = field_get(mask, val); + val = readb(pctrl->base + offset); - switch (val) { - case PVDD_1800: - return 1800; - case PVDD_2500: - return 2500; - case PVDD_3300: - return 3300; - default: - /* Should not happen. */ - return -EINVAL; - } + return rzg2l_pwr_reg_val_to_ps(field_get(mask, val), caps); } static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps, u32 ps) { const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg; const struct rzg2l_register_offsets *regs = &hwcfg->regs; - u8 poc_val, val, mask; - int pwr_reg; + const struct rzg2l_register_masks *masks = &hwcfg->masks; + u16 offset; + u8 mask; + int ret; if (caps & PIN_CFG_SOFT_PS) { pctrl->settings[pin].power_source = ps; return 0; } - switch (ps) { - case 1800: - poc_val = PVDD_1800; - break; - case 2500: - if (!(caps & (PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1))) - return -EINVAL; - poc_val = PVDD_2500; - break; - case 3300: - poc_val = PVDD_3300; - break; - default: - return -EINVAL; - } + ret = rzg2l_caps_to_pwr_reg(regs, masks, caps, &offset, &mask); + if (ret) + return ret; - pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask); - if (pwr_reg < 0) - return pwr_reg; + ret = rzg2l_ps_to_pwr_reg_val(ps, caps); + if (ret < 0) + return ret; - if (pwr_reg == OTHER_POC) { - scoped_guard(raw_spinlock_irqsave, &pctrl->lock) { - val = readb(pctrl->base + pwr_reg); - if (poc_val) - val |= mask; - else - val &= ~mask; - writeb(val, pctrl->base + pwr_reg); - } - } else { - writeb(poc_val, pctrl->base + pwr_reg); + scoped_guard(raw_spinlock_irqsave, &pctrl->lock) { + u8 tmp = readb(pctrl->base + offset); + + tmp &= ~mask; + tmp |= field_prep(mask, ret); + writeb(tmp, pctrl->base + offset); } pctrl->settings[pin].power_source = ps; @@ -3821,6 +3862,11 @@ static const struct rzg2l_hwcfg rzg3l_hwcfg = { .oen = 0x3018, .other_poc = OTHER_POC, }, + .masks = { + .other_poc_pvdd1833_oth_awo_poc = BIT(0), + .other_poc_pvdd1833_oth_iso_poc = BIT(1), + .other_poc_wdtovf_n_poc = BIT(2), + }, .iolh_groupa_ua = { /* 1v8 power source */ [RZG2L_IOLH_IDX_1V8] = 2200, 4400, 9000, 10000, From 382ec4d1e27a95ef23080c9e3bca8b44d31736f9 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 4 Sep 2026 15:54:22 +0300 Subject: [PATCH 04/15] pinctrl: renesas: rzg2l: Drop defines present in struct rzg2l_hwcfg Drop the QSPI and OTHER_POC register defines, which are SoC specific and accessible through struct rzg2l_hwcfg::{qspi, other_poc}. While at it move the assignement of rzg2l_hwcfg->regs->qspi upper to have the initializations as sorted (by offsets) as possible. Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Tested-by: Wolfram Sang Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260904125425.412632-3-claudiu.beznea+renesas@tuxon.dev Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 40bf8f9e8f2f..c17983aafcf5 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -181,8 +181,6 @@ #define SMT(off) (0x3400 + (off) * 8) #define SD_CH(off, ch) ((off) + (ch) * 4) #define ETH_POC(off, ch) ((off) + (ch) * 4) -#define QSPI (0x3008) /* known on RZ/{G2L,G2LC,G2UL,Five} only */ -#define OTHER_POC (0x3028) /* known on RZ/G3L only */ #define PVDD_2500 2 /* I/O domain voltage 2.5V */ #define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ @@ -3840,9 +3838,9 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = { .regs = { .pwpr = 0x3014, .sd_ch = 0x3000, + .qspi = 0x3008, .eth_poc = 0x300c, .oen = 0x3018, - .qspi = QSPI, }, .iolh_groupa_ua = { /* 3v3 power source */ @@ -3860,7 +3858,7 @@ static const struct rzg2l_hwcfg rzg3l_hwcfg = { .sd_ch2 = 0x3024, .eth_poc = 0x3010, .oen = 0x3018, - .other_poc = OTHER_POC, + .other_poc = 0x3028, }, .masks = { .other_poc_pvdd1833_oth_awo_poc = BIT(0), From 5e1e9d0cd8f366cedac88b7463126da989704e35 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 4 Sep 2026 15:54:23 +0300 Subject: [PATCH 05/15] pinctrl: renesas: rzg2l: Unify power source handling The previous code handled power sources using a mixture of power source specific definitions and lookups in the available_ps[] array. Unify the power source handling by introducing struct rzg2l_pinctrl_ps_desc, whose purpose is to describe a power source through its power source value, associated register value, associated capabilities (e.g. Ethernet), and associated IOLH index. Introduce two new functions, rzg2l_ps_to_desc() and rzg2l_pwr_reg_val_to_desc(), using the RZG2L_PINCTRL_PS_DESC_MEMBER_TO_DESC_FUNC() macro, as their implementations are similar. These functions retrieve a power source descriptor based on either a power source value or a power source register value. Other functions that need to perform power source specific operations can call them to retrieve the corresponding power source descriptor. The register values for the soft power sources were kept to zero since they are not used across the driver's code. Tested-by: Wolfram Sang Signed-off-by: Claudiu Beznea Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260904125425.412632-4-claudiu.beznea+renesas@tuxon.dev Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 162 ++++++++++++++++-------- 1 file changed, 108 insertions(+), 54 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index c17983aafcf5..6696e32909c3 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -182,9 +182,6 @@ #define SD_CH(off, ch) ((off) + (ch) * 4) #define ETH_POC(off, ch) ((off) + (ch) * 4) -#define PVDD_2500 2 /* I/O domain voltage 2.5V */ -#define PVDD_1800 1 /* I/O domain voltage <= 1.8V */ -#define PVDD_3300 0 /* I/O domain voltage >= 3.3V */ #define PVDD_MASK 0x3 #define PWPR_B0WI BIT(7) /* Bit Write Disable */ @@ -289,12 +286,14 @@ struct rzg2l_register_masks { /** * enum rzg2l_iolh_index - starting indices in IOLH specific arrays + * @RZG2L_IOLH_IDX_NA: index N/A (to be used for error checking) * @RZG2L_IOLH_IDX_1V8: starting index for 1V8 power source * @RZG2L_IOLH_IDX_2V5: starting index for 2V5 power source * @RZG2L_IOLH_IDX_3V3: starting index for 3V3 power source * @RZG2L_IOLH_IDX_MAX: maximum index */ enum rzg2l_iolh_index { + RZG2L_IOLH_IDX_NA = -1, RZG2L_IOLH_IDX_1V8 = 0, RZG2L_IOLH_IDX_2V5 = 4, RZG2L_IOLH_IDX_3V3 = 8, @@ -445,7 +444,85 @@ struct rzg2l_pinctrl { u32 clone_offset; }; -static const u16 available_ps[] = { 1800, 2500, 3300 }; +/** + * struct rzg2l_pinctrl_ps_desc - RZ/G2L power source descriptor + * @caps: Capabilities that applies to the power source + * @iolh_index: IOLH index + * @ps: Power source value + * @pwr_reg_val: Power source register value + */ +struct rzg2l_pinctrl_ps_desc { + u32 caps; + enum rzg2l_iolh_index iolh_index; + u16 ps; + u16 pwr_reg_val; +}; + +#define RZG2L_PINCTRL_PS_DESC(_ps, _pwr_reg_val, _caps, _iolh_index) \ + { \ + .ps = _ps, \ + .pwr_reg_val = _pwr_reg_val, \ + .caps = _caps, \ + .iolh_index = _iolh_index, \ + } + +static const struct rzg2l_pinctrl_ps_desc available_ps[] = { + /* Ethernet I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1, + RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(2500, 2, PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1, + RZG2L_IOLH_IDX_2V5), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1, + RZG2L_IOLH_IDX_3V3), + + /* SD I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_IO_VMC_SD0 | PIN_CFG_IO_VMC_SD1 | + PIN_CFG_IO_VMC_SD2, RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_IO_VMC_SD0 | PIN_CFG_IO_VMC_SD1 | + PIN_CFG_IO_VMC_SD2, RZG2L_IOLH_IDX_3V3), + + /* QSPI I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_IO_VMC_QSPI, RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_IO_VMC_QSPI, RZG2L_IOLH_IDX_3V3), + + /* AWO I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_PVDD1833_OTH_AWO_POC, + RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_PVDD1833_OTH_AWO_POC, + RZG2L_IOLH_IDX_3V3), + + /* ISO I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_PVDD1833_OTH_ISO_POC, + RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_PVDD1833_OTH_ISO_POC, + RZG2L_IOLH_IDX_3V3), + + /* WDTOVF I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_WDTOVF_N_POC, RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_WDTOVF_N_POC, RZG2L_IOLH_IDX_3V3), + + /* Software voltage domains */ + RZG2L_PINCTRL_PS_DESC(1800, 0, PIN_CFG_SOFT_PS, RZG2L_IOLH_IDX_1V8), + RZG2L_PINCTRL_PS_DESC(2500, 0, PIN_CFG_SOFT_PS, RZG2L_IOLH_IDX_2V5), + RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_SOFT_PS, RZG2L_IOLH_IDX_3V3), +}; + +#define RZG2L_PINCTRL_PS_DESC_MEMBER_TO_DESC_FUNC(_name, _desc_member, _caps) \ +static const struct rzg2l_pinctrl_ps_desc *_name(u16 _desc_member, u32 _caps) \ +{ \ + for (unsigned int i = 0; i < ARRAY_SIZE(available_ps); i++) { \ + if (available_ps[i]._desc_member != _desc_member) \ + continue; \ + \ + if (available_ps[i].caps & _caps) \ + return &available_ps[i]; \ + } \ + \ + return NULL; \ +} + +RZG2L_PINCTRL_PS_DESC_MEMBER_TO_DESC_FUNC(rzg2l_ps_to_desc, ps, caps) +RZG2L_PINCTRL_PS_DESC_MEMBER_TO_DESC_FUNC(rzg2l_pwr_reg_val_to_desc, pwr_reg_val, caps) static u64 rzg2l_pinctrl_get_variable_pin_cfg(struct rzg2l_pinctrl *pctrl, u64 pincfg, @@ -1126,34 +1203,26 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, return -EINVAL; } -static int rzg2l_pwr_reg_val_to_ps(u8 val, u32 caps) +static int rzg2l_pwr_reg_val_to_ps(u16 pwr_reg_val, u32 caps) { - switch (val) { - case PVDD_1800: - return 1800; - case PVDD_2500: - return 2500; - case PVDD_3300: - return 3300; - } + const struct rzg2l_pinctrl_ps_desc *desc; - return -EINVAL; + desc = rzg2l_pwr_reg_val_to_desc(pwr_reg_val, caps); + if (!desc) + return -EINVAL; + + return desc->ps; } -static int rzg2l_ps_to_pwr_reg_val(u32 ps, u32 caps) +static int rzg2l_ps_to_pwr_reg_val(u16 ps, u32 caps) { - switch (ps) { - case 1800: - return PVDD_1800; - case 2500: - if (!(caps & (PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1))) - return -EINVAL; - return PVDD_2500; - case 3300: - return PVDD_3300; - } + const struct rzg2l_pinctrl_ps_desc *desc; - return -EINVAL; + desc = rzg2l_ps_to_desc(ps, caps); + if (!desc) + return -EINVAL; + + return desc->pwr_reg_val; } static int rzg2l_get_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps) @@ -1212,32 +1281,11 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps return 0; } -static bool rzg2l_ps_is_supported(u16 ps) +static enum rzg2l_iolh_index rzg2l_ps_to_iolh_idx(u16 ps, u32 caps) { - unsigned int i; + const struct rzg2l_pinctrl_ps_desc *desc = rzg2l_ps_to_desc(ps, caps); - for (i = 0; i < ARRAY_SIZE(available_ps); i++) { - if (available_ps[i] == ps) - return true; - } - - return false; -} - -static enum rzg2l_iolh_index rzg2l_ps_to_iolh_idx(u16 ps) -{ - unsigned int i; - - for (i = 0; i < ARRAY_SIZE(available_ps); i++) { - if (available_ps[i] == ps) - break; - } - - /* - * We multiply with RZG2L_IOLH_MAX_DS_ENTRIES as we have - * RZG2L_IOLH_MAX_DS_ENTRIES DS values per power source - */ - return i * RZG2L_IOLH_MAX_DS_ENTRIES; + return desc ? desc->iolh_index : RZG2L_IOLH_IDX_NA; } static u16 rzg2l_iolh_val_to_ua(const struct rzg2l_hwcfg *hwcfg, u32 caps, u8 val) @@ -1622,7 +1670,11 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev, ret = rzg2l_get_power_source(pctrl, _pin, cfg); if (ret < 0) return ret; - iolh_idx = rzg2l_ps_to_iolh_idx(ret); + + iolh_idx = rzg2l_ps_to_iolh_idx(ret, cfg); + if (iolh_idx == RZG2L_IOLH_IDX_NA) + return -EINVAL; + val = rzg2l_read_pin_config(pctrl, IOLH(off), bit, IOLH_MASK); arg = rzg2l_iolh_val_to_ua(hwcfg, cfg, iolh_idx + val); break; @@ -1818,8 +1870,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, /* Apply power source. */ if (settings.power_source != pctrl->settings[_pin].power_source) { - ret = rzg2l_ps_is_supported(settings.power_source); - if (!ret) + if (!rzg2l_ps_to_desc(settings.power_source, cfg)) return -EINVAL; /* Apply power source. */ @@ -1833,7 +1884,10 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev, enum rzg2l_iolh_index iolh_idx; int val; - iolh_idx = rzg2l_ps_to_iolh_idx(settings.power_source); + iolh_idx = rzg2l_ps_to_iolh_idx(settings.power_source, cfg); + if (iolh_idx == RZG2L_IOLH_IDX_NA) + return -EINVAL; + ret = rzg2l_ds_is_supported(pctrl, cfg, iolh_idx, settings.drive_strength_ua); if (!ret) From 16a06509a9b7754e2ebd7a66c47abd00e5fbc5a6 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 4 Sep 2026 15:54:24 +0300 Subject: [PATCH 06/15] dt-bindings: pinctrl: renesas,rzg2l-pinctrl: Document missing I3C power source option The I3C pins on the Renesas RZ/G3S SoC can be powered at either 1.2V or 1.8V. Document the missing 1.2V power source option. Acked-by: Conor Dooley Reviewed-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Tested-by: Wolfram Sang Link: https://patch.msgid.link/20260904125425.412632-5-claudiu.beznea+renesas@tuxon.dev Signed-off-by: Geert Uytterhoeven --- .../devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml index a551996623c0..ec5d6d52f07b 100644 --- a/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml +++ b/Documentation/devicetree/bindings/pinctrl/renesas,rzg2l-pinctrl.yaml @@ -129,7 +129,7 @@ additionalProperties: enum: [ 33, 50, 66, 100 ] power-source: description: I/O voltage in millivolt. - enum: [ 1800, 2500, 3300 ] + enum: [ 1200, 1800, 2500, 3300 ] slew-rate: true gpio-hog: true gpios: true From ec5202ccfa24791acf64146d5bac6b63ce234f95 Mon Sep 17 00:00:00 2001 From: Claudiu Beznea Date: Fri, 4 Sep 2026 15:54:25 +0300 Subject: [PATCH 07/15] pinctrl: renesas: rzg2l: Add RZ/G3S support for selecting I3C power source The Renesas RZ/G3S I3C pins can be powered at either 1.8V or 1.2V. The pin controller provides a register to select between these two options. Update the Renesas RZ/G2L pin controller driver to allow selecting the I3C power source on RZ/G3S SoC. Tested-by: Wolfram Sang Reviewed-by: Geert Uytterhoeven Signed-off-by: Claudiu Beznea Link: https://patch.msgid.link/20260904125425.412632-6-claudiu.beznea+renesas@tuxon.dev Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzg2l.c | 30 +++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c index 6696e32909c3..9bf3743dc1ce 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c +++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c @@ -70,6 +70,7 @@ #define PIN_CFG_PVDD1833_OTH_ISO_POC BIT(20) /* known on RZ/G3L only */ #define PIN_CFG_WDTOVF_N_POC BIT(21) /* known on RZ/G3L only */ #define PIN_CFG_IO_VMC_SD2 BIT(22) /* known on RZ/G3L only */ +#define PIN_CFG_IO_VMC_I3C BIT(23) #define RZG2L_SINGLE_PIN BIT_ULL(63) /* Dedicated pin */ #define RZG2L_VARIABLE_CFG BIT_ULL(62) /* Variable cfg for port pins */ @@ -256,6 +257,7 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = { * @oen: OEN register offset * @qspi: QSPI register offset * @other_poc: OTHER_POC register offset + * @i3c_set: I3C_SET register offset */ struct rzg2l_register_offsets { u16 pwpr; @@ -265,6 +267,7 @@ struct rzg2l_register_offsets { u16 oen; u16 qspi; u16 other_poc; + u16 i3c_set; }; /** @@ -272,6 +275,7 @@ struct rzg2l_register_offsets { * @other_poc_pvdd1833_oth_awo_poc: PVDD1833_OTH_AWO_POC mask * @other_poc_pvdd1833_oth_iso_poc: PVDD1833_OTH_ISO_POC mask * @other_poc_wdtovf_n_poc: WDTOVF_N_POC mask + * @i3c_set_poc: I3C_SET_POC mask */ struct rzg2l_register_masks { union { @@ -281,6 +285,11 @@ struct rzg2l_register_masks { u8 other_poc_pvdd1833_oth_iso_poc; u8 other_poc_wdtovf_n_poc; }; + + /* RZ/G3S masks */ + struct { + u8 i3c_set_poc; + }; }; }; @@ -394,6 +403,7 @@ struct rzg2l_pinctrl_pin_settings { * @oen: Output Enable register cache * @other_poc: OTHER_POC register cache * @qspi: QSPI registers cache + * @i3c_set: I3C_SET register cache */ struct rzg2l_pinctrl_reg_cache { u8 *p; @@ -413,6 +423,7 @@ struct rzg2l_pinctrl_reg_cache { u8 oen; u8 other_poc; u8 qspi; + u8 i3c_set; }; struct rzg2l_pinctrl { @@ -501,6 +512,10 @@ static const struct rzg2l_pinctrl_ps_desc available_ps[] = { RZG2L_PINCTRL_PS_DESC(1800, 1, PIN_CFG_WDTOVF_N_POC, RZG2L_IOLH_IDX_1V8), RZG2L_PINCTRL_PS_DESC(3300, 0, PIN_CFG_WDTOVF_N_POC, RZG2L_IOLH_IDX_3V3), + /* I3C I/O voltage domains */ + RZG2L_PINCTRL_PS_DESC(1200, 1, PIN_CFG_IO_VMC_I3C, RZG2L_IOLH_IDX_NA), + RZG2L_PINCTRL_PS_DESC(1800, 0, PIN_CFG_IO_VMC_I3C, RZG2L_IOLH_IDX_NA), + /* Software voltage domains */ RZG2L_PINCTRL_PS_DESC(1800, 0, PIN_CFG_SOFT_PS, RZG2L_IOLH_IDX_1V8), RZG2L_PINCTRL_PS_DESC(2500, 0, PIN_CFG_SOFT_PS, RZG2L_IOLH_IDX_2V5), @@ -1199,6 +1214,11 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, *mask = masks->other_poc_wdtovf_n_poc; return 0; } + if (caps & PIN_CFG_IO_VMC_I3C) { + *offset = regs->i3c_set; + *mask = masks->i3c_set_poc; + return 0; + } return -EINVAL; } @@ -2570,6 +2590,8 @@ static const struct rzg2l_dedicated_configs rzg3s_dedicated_pins[] = { { "AUDIO_CLK1", RZG2L_SINGLE_PIN_PACK(0x2, 0, PIN_CFG_IEN) }, { "AUDIO_CLK2", RZG2L_SINGLE_PIN_PACK(0x2, 1, PIN_CFG_IEN) }, { "WDTOVF_PERROUT#", RZG2L_SINGLE_PIN_PACK(0x6, 0, PIN_CFG_IOLH_A | PIN_CFG_SOFT_PS) }, + { "I3C_SDA", RZG2L_SINGLE_PIN_PACK(0x9, 0, (PIN_CFG_IEN | PIN_CFG_IO_VMC_I3C)) }, + { "I3C_SCL", RZG2L_SINGLE_PIN_PACK(0x9, 1, (PIN_CFG_IEN | PIN_CFG_IO_VMC_I3C)) }, { "SD0_CLK", RZG2L_SINGLE_PIN_PACK(0x10, 0, (PIN_CFG_IOLH_B | PIN_CFG_IO_VMC_SD0)) }, { "SD0_CMD", RZG2L_SINGLE_PIN_PACK(0x10, 1, (PIN_CFG_IOLH_B | PIN_CFG_IEN | PIN_CFG_IO_VMC_SD0)) }, @@ -3792,6 +3814,8 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev) cache->oen = readb(pctrl->base + pctrl->data->hwcfg->regs.oen); if (regs->other_poc) cache->other_poc = readb(pctrl->base + regs->other_poc); + if (regs->i3c_set) + cache->i3c_set = readb(pctrl->base + regs->i3c_set); if (pctrl->syscon) { int ret; @@ -3834,6 +3858,8 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev) writeb(cache->qspi, pctrl->base + regs->qspi); if (regs->other_poc) writeb(cache->other_poc, pctrl->base + regs->other_poc); + if (regs->i3c_set) + writeb(cache->i3c_set, pctrl->base + regs->i3c_set); raw_spin_lock_irqsave(&pctrl->lock, flags); rzg2l_oen_write_with_pwpr(pctrl, cache->oen); @@ -3950,8 +3976,12 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = { .pwpr = 0x3000, .sd_ch = 0x3004, .eth_poc = 0x3010, + .i3c_set = 0x301c, .oen = 0x3018, }, + .masks = { + .i3c_set_poc = BIT(2), + }, .iolh_groupa_ua = { /* 1v8 power source */ [RZG2L_IOLH_IDX_1V8] = 2200, 4400, 9000, 10000, From c0de88659b68402ed7e53fb998a058ef430c787b Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Mon, 17 Aug 2026 21:56:54 +0300 Subject: [PATCH 08/15] pinctrl: renesas: rzt2h: Fix used_irqs indexing in clear path struct rzt2h_pinctrl::used_irqs is supposed to be indexed based on the parent IRQ number. Do the translation using rzt2h_gpio_irq_map and clear it properly. Cc: stable@kernel.org Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts") Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260817185700.322370-2-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index eb70aee39f1a..a37f1e399446 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -1050,8 +1050,13 @@ static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v irq_hw_number_t hwirq = irqd_to_hwirq(d); u8 port = RZT2H_PIN_ID_TO_PORT(hwirq); u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq); + u8 parent_irq; - if (test_and_clear_bit(hwirq - RZT2H_INTERRUPTS_START, pctrl->used_irqs)) + parent_irq = rzt2h_gpio_irq_map[hwirq]; + if (parent_irq < RZT2H_INTERRUPTS_START) + return; + + if (test_and_clear_bit(parent_irq - RZT2H_INTERRUPTS_START, pctrl->used_irqs)) rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, false); irq_domain_free_irqs_common(domain, virq, nr_irqs); From 8d703ee95ade2d83e552025bcfb3344849311693 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Mon, 17 Aug 2026 21:56:58 +0300 Subject: [PATCH 09/15] pinctrl: renesas: rzt2h: Wire up struct gpio_chip::set_config Wire up struct gpio_chip::set_config to allow GPIO settings via pinctrl. Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Reviewed-by: Bartosz Golaszewski Link: https://patch.msgid.link/20260817185700.322370-6-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index a37f1e399446..557708368e58 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -1118,6 +1118,7 @@ static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl) chip->direction_output = rzt2h_gpio_direction_output; chip->get = rzt2h_gpio_get; chip->set = rzt2h_gpio_set; + chip->set_config = gpiochip_generic_config; chip->label = dev_name(dev); if (of_property_present(np, "interrupt-controller")) { From 3f3fe195e7d908dc15ad7ab99f38e9225225b538 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Mon, 17 Aug 2026 21:56:59 +0300 Subject: [PATCH 10/15] pinctrl: renesas: rzt2h: Remove unused struct rzt2h_pinctrl::gpio_range The struct rzt2h_pinctrl::gpio_range field is unused and seems to be a vestige of calling pinctrl_add_gpio_range(). Remove it. Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260817185700.322370-7-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 9 --------- 1 file changed, 9 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 557708368e58..d29ef107c2c1 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -94,7 +94,6 @@ struct rzt2h_pinctrl { void __iomem *base0, *base1; struct device *dev; struct gpio_chip gpio_chip; - struct pinctrl_gpio_range gpio_range; DECLARE_BITMAP(used_irqs, RZT2H_INTERRUPTS_NUM); raw_spinlock_t lock; /* lock read/write registers */ struct mutex mutex; /* serialize adding groups and functions */ @@ -1077,7 +1076,6 @@ static void rzt2h_gpio_init_irq_valid_mask(struct gpio_chip *gc, static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl) { - struct pinctrl_gpio_range *range = &pctrl->gpio_range; struct gpio_chip *chip = &pctrl->gpio_chip; struct device_node *np = pctrl->dev->of_node; struct irq_domain *parent_domain; @@ -1132,13 +1130,6 @@ static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl) girq->init_valid_mask = rzt2h_gpio_init_irq_valid_mask; } - range->id = 0; - range->pin_base = 0; - range->base = 0; - range->npins = chip->ngpio; - range->name = chip->label; - range->gc = chip; - ret = devm_gpiochip_add_data(dev, chip, pctrl); if (ret) return dev_err_probe(dev, ret, "gpiochip registration failed\n"); From 7df755632142e59a5aec5f85f2654add49220102 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 13 Sep 2026 22:56:50 +0300 Subject: [PATCH 11/15] pinctrl: renesas: rzt2h: Restore correct pin mode on IRQ free rzt2h_gpio_irq_domain_free() calls rzt2h_pinctrl_set_gpio_en() with false leaving the pin in interrupt function instead of returning it to GPIO mode. Pass true to rzt2h_pinctrl_set_gpio_en() to take the pin out of interrupt function after we're done using it as an IRQ. rzt2h_pinctrl_set_pfc_mode() switches the pin to Hi-Z, losing the previous PM value. Save the PM value before switching to Hi-Z, and restore it after the IRQ is freed. Cc: stable@kernel.org Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts") Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260913195654.3371385-2-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 43 +++++++++++++++++++++---- 1 file changed, 37 insertions(+), 6 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index d29ef107c2c1..9c1ac836ffa6 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -95,6 +95,7 @@ struct rzt2h_pinctrl { struct device *dev; struct gpio_chip gpio_chip; DECLARE_BITMAP(used_irqs, RZT2H_INTERRUPTS_NUM); + u8 saved_pm[RZT2H_INTERRUPTS_NUM]; raw_spinlock_t lock; /* lock read/write registers */ struct mutex mutex; /* serialize adding groups and functions */ bool safety_port_enabled; @@ -169,6 +170,26 @@ static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset) return (pincfg & BIT(pin)) ? 0 : -EINVAL; } +static u8 rzt2h_pin_read_pm(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin) +{ + u16 reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); + + return field_get(PM_PIN_MASK(pin), reg); +} + +static void rzt2h_pin_write_pm(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin, + unsigned int pm) +{ + u16 reg; + + guard(raw_spinlock_irqsave)(&pctrl->lock); + + reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); + reg &= ~PM_PIN_MASK(pin); + reg |= pm << (pin * 2); + rzt2h_pinctrl_writew(pctrl, port, reg, PM(port)); +} + static void rzt2h_pinctrl_set_gpio_en(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin, bool en) { @@ -1022,16 +1043,23 @@ static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc, struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc); u8 port = RZT2H_PIN_ID_TO_PORT(child); u8 pin = RZT2H_PIN_ID_TO_PIN(child); - u8 parent_irq; + u8 parent_irq, irq_idx; parent_irq = rzt2h_gpio_irq_map[child]; if (parent_irq < RZT2H_INTERRUPTS_START) return -EINVAL; - if (test_and_set_bit(parent_irq - RZT2H_INTERRUPTS_START, - pctrl->used_irqs)) + irq_idx = parent_irq - RZT2H_INTERRUPTS_START; + if (test_and_set_bit(irq_idx, pctrl->used_irqs)) return -EBUSY; + /* + * rzt2h_pinctrl_set_pfc_mode() sets PM to Hi-Z before switching to the + * interrupt function, losing the previous PM value. + * Save it so it can be restored when the IRQ is freed. + */ + pctrl->saved_pm[irq_idx] = rzt2h_pin_read_pm(pctrl, port, pin); + rzt2h_pinctrl_set_pfc_mode(pctrl, port, pin, PFC_FUNC_INTERRUPT); *parent = parent_irq; @@ -1049,14 +1077,17 @@ static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v irq_hw_number_t hwirq = irqd_to_hwirq(d); u8 port = RZT2H_PIN_ID_TO_PORT(hwirq); u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq); - u8 parent_irq; + u8 parent_irq, irq_idx; parent_irq = rzt2h_gpio_irq_map[hwirq]; if (parent_irq < RZT2H_INTERRUPTS_START) return; - if (test_and_clear_bit(parent_irq - RZT2H_INTERRUPTS_START, pctrl->used_irqs)) - rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, false); + irq_idx = parent_irq - RZT2H_INTERRUPTS_START; + if (test_and_clear_bit(irq_idx, pctrl->used_irqs)) { + rzt2h_pin_write_pm(pctrl, port, pin, pctrl->saved_pm[irq_idx]); + rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true); + } irq_domain_free_irqs_common(domain, virq, nr_irqs); } From d9980f93cef0f90a11f26296c68ed16b61a95ced Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 13 Sep 2026 22:56:51 +0300 Subject: [PATCH 12/15] pinctrl: renesas: rzt2h: Setup IRQ in .irq_{request,release}_resources() When exporting a GPIO using the deprecated sysfs GPIO API, gpio_is_visible() calls gpiod_to_irq() to determine whether a GPIO is IRQ-capable, which ends up using struct gpio_irq_chip::child_to_parent_hwirq(). When using the GPIO character device, it is possible to request a line and to dynamically switch between using it as an IRQ or as a GPIO, by updating the edge detector. The current struct gpio_irq_chip::child_to_parent_hwirq() implementation makes the assumption that it will only be called with the intention of setting up the IRQ permanently, and that it will only be released when the IRQ domain is freed. Since we now know that this is not the case, take the following steps in order to properly handle that case. Implement struct irq_chip::irq_{request,release}_resources() based on the default GPIOCHIP_IRQ_RESOURCE_HELPERS implementations. Move the IRQ setup and release logic inside them. Keep the mapping logic inside ::child_to_parent_hwirq() to be able to provide a proper HW IRQ number, but do not actually setup the IRQ. Remove rzt2h_gpio_irq_domain_free() as its only purpose was to free the acquired IRQs. irq_domain_free_irqs_common() will still be set up as struct gpio_irq_chip::child_irq_domain_ops::free by gpiochip_hierarchy_setup_domain_ops(). Cc: stable@kernel.org Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts") Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260913195654.3371385-3-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 79 +++++++++++++++---------- 1 file changed, 49 insertions(+), 30 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 9c1ac836ffa6..796120e70bc4 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -1020,32 +1020,17 @@ static int rzt2h_gpio_irq_set_wake(struct irq_data *d, unsigned int on) return 0; } -static const struct irq_chip rzt2h_gpio_irqchip = { - .name = "rzt2h-gpio", - .irq_disable = rzt2h_gpio_irq_disable, - .irq_enable = rzt2h_gpio_irq_enable, - .irq_mask = irq_chip_mask_parent, - .irq_unmask = irq_chip_unmask_parent, - .irq_set_type = irq_chip_set_type_parent, - .irq_set_wake = rzt2h_gpio_irq_set_wake, - .irq_eoi = irq_chip_eoi_parent, - .irq_set_affinity = irq_chip_set_affinity_parent, - .flags = IRQCHIP_IMMUTABLE, - GPIOCHIP_IRQ_RESOURCE_HELPERS, -}; - -static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc, - unsigned int child, - unsigned int child_type, - unsigned int *parent, - unsigned int *parent_type) +static int rzt2h_gpio_irq_request_resources(struct irq_data *d) { + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc); - u8 port = RZT2H_PIN_ID_TO_PORT(child); - u8 pin = RZT2H_PIN_ID_TO_PIN(child); + irq_hw_number_t hwirq = irqd_to_hwirq(d); + u8 port = RZT2H_PIN_ID_TO_PORT(hwirq); + u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq); u8 parent_irq, irq_idx; + int ret; - parent_irq = rzt2h_gpio_irq_map[child]; + parent_irq = rzt2h_gpio_irq_map[hwirq]; if (parent_irq < RZT2H_INTERRUPTS_START) return -EINVAL; @@ -1062,18 +1047,21 @@ static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc, rzt2h_pinctrl_set_pfc_mode(pctrl, port, pin, PFC_FUNC_INTERRUPT); - *parent = parent_irq; - *parent_type = child_type; + ret = gpiochip_lock_as_irq(gc, hwirq); + if (ret) { + clear_bit(irq_idx, pctrl->used_irqs); + rzt2h_pin_write_pm(pctrl, port, pin, pctrl->saved_pm[irq_idx]); + rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true); + return ret; + } return 0; } -static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int virq, - unsigned int nr_irqs) +static void rzt2h_gpio_irq_release_resources(struct irq_data *d) { - struct irq_data *d = irq_domain_get_irq_data(domain, virq); struct gpio_chip *gc = irq_data_get_irq_chip_data(d); - struct rzt2h_pinctrl *pctrl = container_of(gc, struct rzt2h_pinctrl, gpio_chip); + struct rzt2h_pinctrl *pctrl = gpiochip_get_data(gc); irq_hw_number_t hwirq = irqd_to_hwirq(d); u8 port = RZT2H_PIN_ID_TO_PORT(hwirq); u8 pin = RZT2H_PIN_ID_TO_PIN(hwirq); @@ -1089,7 +1077,39 @@ static void rzt2h_gpio_irq_domain_free(struct irq_domain *domain, unsigned int v rzt2h_pinctrl_set_gpio_en(pctrl, port, pin, true); } - irq_domain_free_irqs_common(domain, virq, nr_irqs); + gpiochip_unlock_as_irq(gc, hwirq); +} + +static const struct irq_chip rzt2h_gpio_irqchip = { + .name = "rzt2h-gpio", + .irq_disable = rzt2h_gpio_irq_disable, + .irq_enable = rzt2h_gpio_irq_enable, + .irq_mask = irq_chip_mask_parent, + .irq_unmask = irq_chip_unmask_parent, + .irq_set_type = irq_chip_set_type_parent, + .irq_set_wake = rzt2h_gpio_irq_set_wake, + .irq_eoi = irq_chip_eoi_parent, + .irq_request_resources = rzt2h_gpio_irq_request_resources, + .irq_release_resources = rzt2h_gpio_irq_release_resources, + .irq_set_affinity = irq_chip_set_affinity_parent, + .flags = IRQCHIP_IMMUTABLE, +}; + +static int rzt2h_gpio_child_to_parent_hwirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + u8 parent_irq = rzt2h_gpio_irq_map[child]; + + if (parent_irq < RZT2H_INTERRUPTS_START) + return -EINVAL; + + *parent = parent_irq; + *parent_type = child_type; + + return 0; } static void rzt2h_gpio_init_irq_valid_mask(struct gpio_chip *gc, @@ -1157,7 +1177,6 @@ static int rzt2h_gpio_register(struct rzt2h_pinctrl *pctrl) girq->parent_domain = parent_domain; girq->child_to_parent_hwirq = rzt2h_gpio_child_to_parent_hwirq; girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; - girq->child_irq_domain_ops.free = rzt2h_gpio_irq_domain_free; girq->init_valid_mask = rzt2h_gpio_init_irq_valid_mask; } From 55297a587c3748f320485a3b37fcccfe6f0440bb Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 13 Sep 2026 22:56:52 +0300 Subject: [PATCH 13/15] pinctrl: renesas: rzt2h: Fix reading pin value in IRQ function rzt2h_gpio_get() only reports a level for pins in input or output mode and returns -EINVAL otherwise. When a pin is requested as an interrupt it is switched to IRQ function and its I/O mode is set to Hi-Z, so its value can no longer be read. gpiolib calls gpiod_get_value_cansleep() to determine the edge's direction when a line is watched with GPIO_V2_LINE_FLAG_EDGE_BOTH. Non-zero values are translated to a rising edge, zero to a falling edge. Since gpiod_get_value_cansleep() ends up calling rzt2h_gpio_get() which returns -EINVAL when the pin is in IRQ function, every edge is reported as rising. When using the IRQ function, the input buffers are enabled and the PINm registers reflect the live state of the input. Report the input level even when the pin is used as an IRQ. Cc: stable@kernel.org Fixes: 829dde3369a9 ("pinctrl: renesas: rzt2h: Add GPIO IRQ chip to handle interrupts") Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260913195654.3371385-4-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 796120e70bc4..9d7e4cba85c9 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -170,6 +170,16 @@ static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset) return (pincfg & BIT(pin)) ? 0 : -EINVAL; } +static bool rzt2h_pin_mode_is_peripheral(struct rzt2h_pinctrl *pctrl, u8 port, u8 bit) +{ + return rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit); +} + +static bool rzt2h_pin_read_input(struct rzt2h_pinctrl *pctrl, u8 port, u8 bit) +{ + return rzt2h_pinctrl_readb(pctrl, port, PIN(port)) & BIT(bit); +} + static u8 rzt2h_pin_read_pm(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin) { u16 reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); @@ -847,6 +857,9 @@ static int rzt2h_gpio_get(struct gpio_chip *chip, unsigned int offset) u8 bit = RZT2H_PIN_ID_TO_PIN(offset); u16 reg; + if (rzt2h_pin_mode_is_peripheral(pctrl, port, bit)) + return rzt2h_pin_read_input(pctrl, port, bit); + reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); reg = (reg >> (bit * 2)) & PM_MASK; if (reg & PM_INPUT) From 20253c44c72785ad6d90d6fdc78c96e56a69f4b1 Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 13 Sep 2026 22:56:53 +0300 Subject: [PATCH 14/15] pinctrl: renesas: rzt2h: Reuse helpers The PMCm peripheral-mode test, the PINm input read, the PMm write, and the PMm read are open-coded in several places. Reuse the already existing helpers. Reviewed-by: Geert Uytterhoeven Signed-off-by: Cosmin Tanislav Link: https://patch.msgid.link/20260913195654.3371385-5-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 34 +++++++++---------------- 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 9d7e4cba85c9..80a581fdef37 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -223,7 +223,7 @@ static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl, reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); /* Check if pin is already configured to the desired function */ - if ((rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(pin)) && + if (rzt2h_pin_mode_is_peripheral(pctrl, port, pin) && field_get(PFC_PIN_MASK(pin), reg64) == func) return; @@ -777,15 +777,7 @@ static int rzt2h_gpio_request(struct gpio_chip *chip, unsigned int offset) static void rzt2h_gpio_set_direction(struct rzt2h_pinctrl *pctrl, u32 port, u8 bit, bool output) { - u16 reg; - - guard(raw_spinlock_irqsave)(&pctrl->lock); - - reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); - reg &= ~PM_PIN_MASK(bit); - - reg |= (output ? PM_OUTPUT : PM_INPUT) << (bit * 2); - rzt2h_pinctrl_writew(pctrl, port, reg, PM(port)); + rzt2h_pin_write_pm(pctrl, port, bit, output ? PM_OUTPUT : PM_INPUT); } static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) @@ -794,8 +786,8 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) u8 port = RZT2H_PIN_ID_TO_PORT(offset); u8 bit = RZT2H_PIN_ID_TO_PIN(offset); u64 reg64; - u16 reg; int ret; + u8 pm; ret = rzt2h_validate_pin(pctrl, offset); if (ret) @@ -803,7 +795,7 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) guard(raw_spinlock_irqsave)(&pctrl->lock); - if (rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit)) { + if (rzt2h_pin_mode_is_peripheral(pctrl, port, bit)) { /* * When a GPIO is being requested as an IRQ, the pinctrl * framework expects to be able to read the GPIO's direction. @@ -821,11 +813,10 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) return -EINVAL; } - reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); - reg = (reg >> (bit * 2)) & PM_MASK; - if (reg & PM_OUTPUT) + pm = rzt2h_pin_read_pm(pctrl, port, bit); + if (pm & PM_OUTPUT) return GPIO_LINE_DIRECTION_OUT; - if (reg & PM_INPUT) + if (pm & PM_INPUT) return GPIO_LINE_DIRECTION_IN; return -EINVAL; @@ -855,16 +846,15 @@ static int rzt2h_gpio_get(struct gpio_chip *chip, unsigned int offset) struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); u8 port = RZT2H_PIN_ID_TO_PORT(offset); u8 bit = RZT2H_PIN_ID_TO_PIN(offset); - u16 reg; + u8 pm; if (rzt2h_pin_mode_is_peripheral(pctrl, port, bit)) return rzt2h_pin_read_input(pctrl, port, bit); - reg = rzt2h_pinctrl_readw(pctrl, port, PM(port)); - reg = (reg >> (bit * 2)) & PM_MASK; - if (reg & PM_INPUT) - return !!(rzt2h_pinctrl_readb(pctrl, port, PIN(port)) & BIT(bit)); - if (reg & PM_OUTPUT) + pm = rzt2h_pin_read_pm(pctrl, port, bit); + if (pm & PM_INPUT) + return rzt2h_pin_read_input(pctrl, port, bit); + if (pm & PM_OUTPUT) return !!(rzt2h_pinctrl_readb(pctrl, port, P(port)) & BIT(bit)); return -EINVAL; From 0cd4a7b3a4883970df3ae8814d9aabdecf65a81e Mon Sep 17 00:00:00 2001 From: Cosmin Tanislav Date: Sun, 13 Sep 2026 22:56:54 +0300 Subject: [PATCH 15/15] pinctrl: renesas: rzt2h: Add a helper for reading PFC Introduce a rzt2h_pin_read_pfc() helper and replace the open-coded implementation inside rzt2h_gpio_get_direction() to simplify the code. While at it, use field_get() with PFC_PIN_MASK to avoid the manual shift and mask. Signed-off-by: Cosmin Tanislav Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20260913195654.3371385-6-cosmin-gabriel.tanislav.xa@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/pinctrl/renesas/pinctrl-rzt2h.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/pinctrl/renesas/pinctrl-rzt2h.c b/drivers/pinctrl/renesas/pinctrl-rzt2h.c index 80a581fdef37..4872a208e0c3 100644 --- a/drivers/pinctrl/renesas/pinctrl-rzt2h.c +++ b/drivers/pinctrl/renesas/pinctrl-rzt2h.c @@ -175,6 +175,13 @@ static bool rzt2h_pin_mode_is_peripheral(struct rzt2h_pinctrl *pctrl, u8 port, u return rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit); } +static u8 rzt2h_pin_read_pfc(struct rzt2h_pinctrl *pctrl, u8 port, u8 pin) +{ + u64 reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); + + return field_get(PFC_PIN_MASK(pin), reg64); +} + static bool rzt2h_pin_read_input(struct rzt2h_pinctrl *pctrl, u8 port, u8 bit) { return rzt2h_pinctrl_readb(pctrl, port, PIN(port)) & BIT(bit); @@ -785,7 +792,6 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip); u8 port = RZT2H_PIN_ID_TO_PORT(offset); u8 bit = RZT2H_PIN_ID_TO_PIN(offset); - u64 reg64; int ret; u8 pm; @@ -805,9 +811,7 @@ static int rzt2h_gpio_get_direction(struct gpio_chip *chip, unsigned int offset) * called to enable the IRQ function. * Default to input direction for IRQ function. */ - reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port)); - reg64 = (reg64 >> (bit * 8)) & PFC_MASK; - if (reg64 == PFC_FUNC_INTERRUPT) + if (rzt2h_pin_read_pfc(pctrl, port, bit) == PFC_FUNC_INTERRUPT) return GPIO_LINE_DIRECTION_IN; return -EINVAL;