[PATCH v4 3/7] pinctrl: renesas: rzg2l: Add support for selecting power source for {WDT,AWO,ISO}

From: Biju

Date: Thu Apr 30 2026 - 05:37:48 EST


From: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>

The RZ/G3L SoC has support for setting power source that are not
controlled by the following voltage control registers:
- SD_CH{0,1,2}_POC, XSPI_POC, ETH{0,1}_POC, I3C_SET.POC

Add support for selecting voltages using OTHER_POC register for
setting I/O domain voltage for WDT, ISO and AWO by extending
rzg2l_caps_to_pwr_reg() with a mask output parameter so that callers
callers can identify which bit(s) within OTHER_POC correspond to the
requested domain. Update rzg2l_get_power_source() to extract the
relevant bit field via field_get() when reading OTHER_POC, and update
rzg2l_set_power_source() to perform a read-modify-write under the
spinlock when writing to OTHER_POC, since multiple domains share the
same register.

Signed-off-by: Biju Das <biju.das.jz@xxxxxxxxxxxxxx>
---
v3->v4:
* Updated commit description.
* Updated rzg2l_caps_to_pwr_reg() to return mask in addition to register
offset.
* Dropped ffs(), using field_get() instead to get PoC offset in
rzg2l_get_power_source().
* Simplified rzg2l_set_power_source() by using mask from
rzg2l_caps_to_pwr_reg().
* Added scoped_guard() for RMW operation in rzg2l_set_power_source().
v2->v3:
* No change
v1->v2:
* No change
---
drivers/pinctrl/renesas/pinctrl-rzg2l.c | 53 ++++++++++++++++++++-----
1 file changed, 44 insertions(+), 9 deletions(-)

diff --git a/drivers/pinctrl/renesas/pinctrl-rzg2l.c b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
index ca9d4a3ec737..7b1bb66d4ff6 100644
--- a/drivers/pinctrl/renesas/pinctrl-rzg2l.c
+++ b/drivers/pinctrl/renesas/pinctrl-rzg2l.c
@@ -63,10 +63,18 @@
#define PIN_CFG_SMT BIT(16) /* Schmitt-trigger input control */
#define PIN_CFG_ELC BIT(17)
#define PIN_CFG_IOLH_RZV2H BIT(18)
+#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 RZG2L_SINGLE_PIN BIT_ULL(63) /* Dedicated pin */
#define RZG2L_VARIABLE_CFG BIT_ULL(62) /* Variable cfg for port pins */

+#define PIN_CFG_OTHER_POC_MASK \
+ (PIN_CFG_PVDD1833_OTH_AWO_POC | \
+ PIN_CFG_PVDD1833_OTH_ISO_POC | \
+ PIN_CFG_WDTOVF_N_POC)
+
#define RZG2L_MPXED_COMMON_PIN_FUNCS(group) \
(PIN_CFG_IOLH_##group | \
PIN_CFG_PUPD | \
@@ -146,6 +154,7 @@
#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 */
@@ -900,7 +909,8 @@ static void rzg2l_rmw_pin_config(struct rzg2l_pinctrl *pctrl, u32 offset,
raw_spin_unlock_irqrestore(&pctrl->lock, flags);
}

-static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, u32 caps)
+static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs,
+ u32 caps, u8 *mask)
{
if (caps & PIN_CFG_IO_VMC_SD0)
return SD_CH(regs->sd_ch, 0);
@@ -912,6 +922,16 @@ static int rzg2l_caps_to_pwr_reg(const struct rzg2l_register_offsets *regs, u32
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);
+
+ return OTHER_POC;
+ }

return -EINVAL;
}
@@ -920,17 +940,20 @@ 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;
- u8 val;

if (caps & PIN_CFG_SOFT_PS)
return pctrl->settings[pin].power_source;

- pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps);
+ pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask);
if (pwr_reg < 0)
return pwr_reg;

val = readb(pctrl->base + pwr_reg);
+ if (pwr_reg == OTHER_POC)
+ val = field_get(mask, val);
+
switch (val) {
case PVDD_1800:
return 1800;
@@ -948,8 +971,8 @@ static int rzg2l_set_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 poc_val, val, mask;
int pwr_reg;
- u8 val;

if (caps & PIN_CFG_SOFT_PS) {
pctrl->settings[pin].power_source = ps;
@@ -958,25 +981,37 @@ static int rzg2l_set_power_source(struct rzg2l_pinctrl *pctrl, u32 pin, u32 caps

switch (ps) {
case 1800:
- val = PVDD_1800;
+ poc_val = PVDD_1800;
break;
case 2500:
if (!(caps & (PIN_CFG_IO_VMC_ETH0 | PIN_CFG_IO_VMC_ETH1)))
return -EINVAL;
- val = PVDD_2500;
+ poc_val = PVDD_2500;
break;
case 3300:
- val = PVDD_3300;
+ poc_val = PVDD_3300;
break;
default:
return -EINVAL;
}

- pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps);
+ pwr_reg = rzg2l_caps_to_pwr_reg(regs, caps, &mask);
if (pwr_reg < 0)
return pwr_reg;

- writeb(val, pctrl->base + pwr_reg);
+ if (pwr_reg == OTHER_POC) {
+ scoped_guard(raw_spinlock, &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);
+ }
+
pctrl->settings[pin].power_source = ps;

return 0;
--
2.43.0