pinctrl: renesas: Updates for v6.18

- Add support for Output Enable (OEN) on RZ/G3E,
   - Add support for the RZ/T2H and RZ/N2H SoCs,
   - Miscellaneous fixes and improvements.
 -----BEGIN PGP SIGNATURE-----
 
 iHUEABYKAB0WIQQ9qaHoIs/1I4cXmEiKwlD9ZEnxcAUCaLGVCgAKCRCKwlD9ZEnx
 cM6rAQDhXz0fVF70cyn00SG5TiHrS9HC+yPvJNphmnq6O/1WiQEA8ITcGTQGsDqn
 oQnsQIkLIJgCVGkbab3jWJ7rNSIRmgo=
 =B0ai
 -----END PGP SIGNATURE-----

Merge tag 'renesas-pinctrl-for-v6.18-tag1' of git://git.kernel.org/pub/scm/linux/kernel/git/geert/renesas-drivers into devel

pinctrl: renesas: Updates for v6.18

  - Add support for Output Enable (OEN) on RZ/G3E,
  - Add support for the RZ/T2H and RZ/N2H SoCs,
  - Miscellaneous fixes and improvements.

Signed-off-by: Linus Walleij <linus.walleij@linaro.org>
This commit is contained in:
Linus Walleij 2025-08-30 00:34:49 +02:00
commit 050e711a48
8 changed files with 1113 additions and 107 deletions

View File

@ -0,0 +1,172 @@
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
%YAML 1.2
---
$id: http://devicetree.org/schemas/pinctrl/renesas,r9a09g077-pinctrl.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
title: Renesas RZ/T2H and RZ/N2H Pin and GPIO controller
maintainers:
- Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>
description:
The Renesas RZ/T2H and RZ/N2H SoCs feature a combined Pin and GPIO controller.
Pin multiplexing and GPIO configuration are performed on a per-pin basis.
Each port supports up to 8 pins, each configurable for either GPIO (port mode)
or alternate function mode. Each pin supports function mode values ranging from
0x0 to 0x2A, allowing selection from up to 43 different functions.
properties:
compatible:
enum:
- renesas,r9a09g077-pinctrl # RZ/T2H
- renesas,r9a09g087-pinctrl # RZ/N2H
reg:
minItems: 1
items:
- description: Non-safety I/O Port base
- description: Safety I/O Port safety region base
- description: Safety I/O Port Non-safety region base
reg-names:
minItems: 1
items:
- const: nsr
- const: srs
- const: srn
gpio-controller: true
'#gpio-cells':
const: 2
description:
The first cell contains the global GPIO port index, constructed using the
RZT2H_GPIO() helper macro from <dt-bindings/pinctrl/renesas,r9a09g077-pinctrl.h>
(e.g. "RZT2H_GPIO(3, 0)" for P03_0). The second cell represents the consumer
flag. Use the macros defined in include/dt-bindings/gpio/gpio.h.
gpio-ranges:
maxItems: 1
clocks:
maxItems: 1
power-domains:
maxItems: 1
definitions:
renesas-rzt2h-n2h-pins-node:
type: object
allOf:
- $ref: pincfg-node.yaml#
- $ref: pinmux-node.yaml#
properties:
pinmux:
description:
Values are constructed from I/O port number, pin number, and
alternate function configuration number using the RZT2H_PORT_PINMUX()
helper macro from <dt-bindings/pinctrl/renesas,r9a09g077-pinctrl.h>.
pins: true
phandle: true
input: true
input-enable: true
output-enable: true
oneOf:
- required: [pinmux]
- required: [pins]
additionalProperties: false
patternProperties:
# Grouping nodes: allow multiple "-pins" subnodes within a "-group"
'.*-group$':
type: object
description:
Pin controller client devices can organize pin configuration entries into
grouping nodes ending in "-group". These group nodes may contain multiple
child nodes each ending in "-pins" to configure distinct sets of pins.
additionalProperties: false
patternProperties:
'-pins$':
$ref: '#/definitions/renesas-rzt2h-n2h-pins-node'
# Standalone "-pins" nodes under client devices or groups
'-pins$':
$ref: '#/definitions/renesas-rzt2h-n2h-pins-node'
'-hog$':
type: object
description: GPIO hog node
properties:
gpio-hog: true
gpios: true
input: true
output-high: true
output-low: true
line-name: true
required:
- gpio-hog
- gpios
additionalProperties: false
allOf:
- $ref: pinctrl.yaml#
required:
- compatible
- reg
- reg-names
- gpio-controller
- '#gpio-cells'
- gpio-ranges
- clocks
- power-domains
unevaluatedProperties: false
examples:
- |
#include <dt-bindings/clock/renesas,r9a09g077-cpg-mssr.h>
#include <dt-bindings/pinctrl/renesas,r9a09g077-pinctrl.h>
pinctrl@802c0000 {
compatible = "renesas,r9a09g077-pinctrl";
reg = <0x802c0000 0x2000>,
<0x812c0000 0x2000>,
<0x802b0000 0x2000>;
reg-names = "nsr", "srs", "srn";
clocks = <&cpg CPG_CORE R9A09G077_CLK_PCLKM>;
gpio-controller;
#gpio-cells = <2>;
gpio-ranges = <&pinctrl 0 0 288>;
power-domains = <&cpg>;
serial0-pins {
pinmux = <RZT2H_PORT_PINMUX(38, 0, 1)>, /* Tx */
<RZT2H_PORT_PINMUX(38, 1, 1)>; /* Rx */
};
sd1-pwr-en-hog {
gpio-hog;
gpios = <RZT2H_GPIO(39, 2) 0>;
output-high;
line-name = "sd1_pwr_en";
};
i2c0-pins {
pins = "RIIC0_SDA", "RIIC0_SCL";
input-enable;
};
sd0-sd-group {
ctrl-pins {
pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>, /* SD0_CLK */
<RZT2H_PORT_PINMUX(12, 1, 0x29)>; /* SD0_CMD */
};
data-pins {
pinmux = <RZT2H_PORT_PINMUX(12, 0, 0x29)>, /* SD0_CLK */
<RZT2H_PORT_PINMUX(12, 1, 0x29)>; /* SD0_CMD */
};
};
};

View File

@ -44,6 +44,8 @@ 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_RZT2H if ARCH_R9A09G087
select PINCTRL_PFC_SH7203 if CPU_SUBTYPE_SH7203
select PINCTRL_PFC_SH7264 if CPU_SUBTYPE_SH7264
select PINCTRL_PFC_SH7269 if CPU_SUBTYPE_SH7269
@ -302,6 +304,17 @@ config PINCTRL_RZN1
help
This selects pinctrl driver for Renesas RZ/N1 devices.
config PINCTRL_RZT2H
bool "pin control support for RZ/N2H and RZ/T2H" if COMPILE_TEST
depends on 64BIT && OF
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_RZV2M
bool "pin control support for RZ/V2M" if COMPILE_TEST
depends on OF

View File

@ -50,6 +50,7 @@ obj-$(CONFIG_PINCTRL_RZA1) += pinctrl-rza1.o
obj-$(CONFIG_PINCTRL_RZA2) += pinctrl-rza2.o
obj-$(CONFIG_PINCTRL_RZG2L) += pinctrl-rzg2l.o
obj-$(CONFIG_PINCTRL_RZN1) += pinctrl-rzn1.o
obj-$(CONFIG_PINCTRL_RZT2H) += pinctrl-rzt2h.o
obj-$(CONFIG_PINCTRL_RZV2M) += pinctrl-rzv2m.o
ifeq ($(CONFIG_COMPILE_TEST),y)

View File

@ -146,8 +146,6 @@
#define SD_CH(off, ch) ((off) + (ch) * 4)
#define ETH_POC(off, ch) ((off) + (ch) * 4)
#define QSPI (0x3008)
#define ETH_MODE (0x3018)
#define PFC_OEN (0x3C40) /* known on RZ/V2H(P) only */
#define PVDD_2500 2 /* I/O domain voltage 2.5V */
#define PVDD_1800 1 /* I/O domain voltage <= 1.8V */
@ -221,11 +219,13 @@ static const struct pin_config_item renesas_rzv2h_conf_items[] = {
* @pwpr: PWPR register offset
* @sd_ch: SD_CH register offset
* @eth_poc: ETH_POC register offset
* @oen: OEN register offset
*/
struct rzg2l_register_offsets {
u16 pwpr;
u16 sd_ch;
u16 eth_poc;
u16 oen;
};
/**
@ -254,6 +254,7 @@ enum rzg2l_iolh_index {
* @iolh_groupb_oi: IOLH group B output impedance specific values
* @tint_start_index: the start index for the TINT interrupts
* @drive_strength_ua: drive strength in uA is supported (otherwise mA is supported)
* @oen_pwpr_lock: flag indicating if the OEN register is locked by PWPR
* @func_base: base number for port function (see register PFC)
* @oen_max_pin: the maximum pin number supporting output enable
* @oen_max_port: the maximum port number supporting output enable
@ -266,6 +267,7 @@ struct rzg2l_hwcfg {
u16 iolh_groupb_oi[4];
u16 tint_start_index;
bool drive_strength_ua;
bool oen_pwpr_lock;
u8 func_base;
u8 oen_max_pin;
u8 oen_max_port;
@ -295,8 +297,7 @@ struct rzg2l_pinctrl_data {
#endif
void (*pwpr_pfc_lock_unlock)(struct rzg2l_pinctrl *pctrl, bool lock);
void (*pmc_writeb)(struct rzg2l_pinctrl *pctrl, u8 val, u16 offset);
u32 (*oen_read)(struct rzg2l_pinctrl *pctrl, unsigned int _pin);
int (*oen_write)(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oen);
int (*pin_to_oen_bit)(struct rzg2l_pinctrl *pctrl, unsigned int _pin);
int (*hw_to_bias_param)(unsigned int val);
int (*bias_param_to_hw)(enum pin_config_param param);
};
@ -322,7 +323,7 @@ struct rzg2l_pinctrl_pin_settings {
* @ien: IEN registers cache
* @sd_ch: SD_CH registers cache
* @eth_poc: ET_POC registers cache
* @eth_mode: ETH_MODE register cache
* @oen: Output Enable register cache
* @qspi: QSPI registers cache
*/
struct rzg2l_pinctrl_reg_cache {
@ -335,7 +336,7 @@ struct rzg2l_pinctrl_reg_cache {
u32 *pupd[2];
u8 sd_ch[2];
u8 eth_poc[2];
u8 eth_mode;
u8 oen;
u8 qspi;
};
@ -394,6 +395,14 @@ static const u64 r9a09g047_variable_pin_cfg[] = {
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PA, 5, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PA, 6, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PA, 7, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 0, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 1, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 2, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 3, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 4, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 5, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 6, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PB, 7, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 0, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_IEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 1, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 2, RZV2H_MPXED_PIN_FUNCS),
@ -402,6 +411,14 @@ static const u64 r9a09g047_variable_pin_cfg[] = {
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 5, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 6, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PD, 7, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 0, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 1, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 2, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 3, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 4, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 5, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 6, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PE, 7, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PG, 0, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PG, 1, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_IEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PG, 2, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_IEN),
@ -421,6 +438,14 @@ static const u64 r9a09g047_variable_pin_cfg[] = {
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PJ, 2, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PJ, 3, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PJ, 4, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 0, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 1, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 2, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 3, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 4, RZV2H_MPXED_PIN_FUNCS | PIN_CFG_OEN),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 5, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 6, RZV2H_MPXED_PIN_FUNCS),
RZG2L_VARIABLE_PIN_CFG_PACK(RZG3E_PL, 7, RZV2H_MPXED_PIN_FUNCS),
};
static const u64 r9a09g057_variable_pin_cfg[] = {
@ -1065,34 +1090,48 @@ static int rzg2l_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
return -EINVAL;
}
static u32 rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
static int rzg2l_read_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
{
int bit;
bit = rzg2l_pin_to_oen_bit(pctrl, _pin);
if (bit < 0)
return 0;
if (!pctrl->data->pin_to_oen_bit)
return -EOPNOTSUPP;
return !(readb(pctrl->base + ETH_MODE) & BIT(bit));
bit = pctrl->data->pin_to_oen_bit(pctrl, _pin);
if (bit < 0)
return -EINVAL;
return !(readb(pctrl->base + pctrl->data->hwcfg->regs.oen) & BIT(bit));
}
static int rzg2l_write_oen(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oen)
{
const struct rzg2l_register_offsets *regs = &pctrl->data->hwcfg->regs;
u16 oen_offset = pctrl->data->hwcfg->regs.oen;
unsigned long flags;
u8 val, pwpr;
int bit;
u8 val;
bit = rzg2l_pin_to_oen_bit(pctrl, _pin);
if (!pctrl->data->pin_to_oen_bit)
return -EOPNOTSUPP;
bit = pctrl->data->pin_to_oen_bit(pctrl, _pin);
if (bit < 0)
return bit;
return -EINVAL;
spin_lock_irqsave(&pctrl->lock, flags);
val = readb(pctrl->base + ETH_MODE);
val = readb(pctrl->base + oen_offset);
if (oen)
val &= ~BIT(bit);
else
val |= BIT(bit);
writeb(val, pctrl->base + ETH_MODE);
if (pctrl->data->hwcfg->oen_pwpr_lock) {
pwpr = readb(pctrl->base + regs->pwpr);
writeb(pwpr | PWPR_REGWE_B, pctrl->base + regs->pwpr);
}
writeb(val, pctrl->base + oen_offset);
if (pctrl->data->hwcfg->oen_pwpr_lock)
writeb(pwpr & ~PWPR_REGWE_B, pctrl->base + regs->pwpr);
spin_unlock_irqrestore(&pctrl->lock, flags);
return 0;
@ -1118,39 +1157,6 @@ static int rzg3s_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
return bit;
}
static u32 rzg3s_oen_read(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
{
int bit;
bit = rzg3s_pin_to_oen_bit(pctrl, _pin);
if (bit < 0)
return bit;
return !(readb(pctrl->base + ETH_MODE) & BIT(bit));
}
static int rzg3s_oen_write(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oen)
{
unsigned long flags;
int bit;
u8 val;
bit = rzg3s_pin_to_oen_bit(pctrl, _pin);
if (bit < 0)
return bit;
spin_lock_irqsave(&pctrl->lock, flags);
val = readb(pctrl->base + ETH_MODE);
if (oen)
val &= ~BIT(bit);
else
val |= BIT(bit);
writeb(val, pctrl->base + ETH_MODE);
spin_unlock_irqrestore(&pctrl->lock, flags);
return 0;
}
static int rzg2l_hw_to_bias_param(unsigned int bias)
{
switch (bias) {
@ -1216,55 +1222,37 @@ static int rzv2h_bias_param_to_hw(enum pin_config_param param)
return -EINVAL;
}
static u8 rzv2h_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
static int rzg2l_pin_names_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin,
const char * const pin_names[], unsigned int count)
{
static const char * const pin_names[] = { "ET0_TXC_TXCLK", "ET1_TXC_TXCLK",
"XSPI0_RESET0N", "XSPI0_CS0N",
"XSPI0_CKN", "XSPI0_CKP" };
const struct pinctrl_pin_desc *pin_desc = &pctrl->desc.pins[_pin];
unsigned int i;
for (i = 0; i < ARRAY_SIZE(pin_names); i++) {
for (i = 0; i < count; i++) {
if (!strcmp(pin_desc->name, pin_names[i]))
return i;
}
/* Should not happen. */
return 0;
return -EINVAL;
}
static u32 rzv2h_oen_read(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
static int rzv2h_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
{
u8 bit;
static const char * const pin_names[] = {
"ET0_TXC_TXCLK", "ET1_TXC_TXCLK", "XSPI0_RESET0N",
"XSPI0_CS0N", "XSPI0_CKN", "XSPI0_CKP"
};
bit = rzv2h_pin_to_oen_bit(pctrl, _pin);
return !(readb(pctrl->base + PFC_OEN) & BIT(bit));
return rzg2l_pin_names_to_oen_bit(pctrl, _pin, pin_names, ARRAY_SIZE(pin_names));
}
static int rzv2h_oen_write(struct rzg2l_pinctrl *pctrl, unsigned int _pin, u8 oen)
static int rzg3e_pin_to_oen_bit(struct rzg2l_pinctrl *pctrl, unsigned int _pin)
{
const struct rzg2l_hwcfg *hwcfg = pctrl->data->hwcfg;
const struct rzg2l_register_offsets *regs = &hwcfg->regs;
unsigned long flags;
u8 val, bit;
u8 pwpr;
static const char * const pin_names[] = {
"PB1", "PE1", "PL4", "PL1", "PL2", "PL0"
};
bit = rzv2h_pin_to_oen_bit(pctrl, _pin);
spin_lock_irqsave(&pctrl->lock, flags);
val = readb(pctrl->base + PFC_OEN);
if (oen)
val &= ~BIT(bit);
else
val |= BIT(bit);
pwpr = readb(pctrl->base + regs->pwpr);
writeb(pwpr | PWPR_REGWE_B, pctrl->base + regs->pwpr);
writeb(val, pctrl->base + PFC_OEN);
writeb(pwpr & ~PWPR_REGWE_B, pctrl->base + regs->pwpr);
spin_unlock_irqrestore(&pctrl->lock, flags);
return 0;
return rzg2l_pin_names_to_oen_bit(pctrl, _pin, pin_names, ARRAY_SIZE(pin_names));
}
static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
@ -1308,11 +1296,10 @@ static int rzg2l_pinctrl_pinconf_get(struct pinctrl_dev *pctldev,
case PIN_CONFIG_OUTPUT_ENABLE:
if (!(cfg & PIN_CFG_OEN))
return -EINVAL;
if (!pctrl->data->oen_read)
return -EOPNOTSUPP;
arg = pctrl->data->oen_read(pctrl, _pin);
if (!arg)
return -EINVAL;
ret = rzg2l_read_oen(pctrl, _pin);
if (ret < 0)
return ret;
arg = ret;
break;
case PIN_CONFIG_POWER_SOURCE:
@ -1471,9 +1458,7 @@ static int rzg2l_pinctrl_pinconf_set(struct pinctrl_dev *pctldev,
case PIN_CONFIG_OUTPUT_ENABLE:
if (!(cfg & PIN_CFG_OEN))
return -EINVAL;
if (!pctrl->data->oen_write)
return -EOPNOTSUPP;
ret = pctrl->data->oen_write(pctrl, _pin, !!arg);
ret = rzg2l_write_oen(pctrl, _pin, !!arg);
if (ret)
return ret;
break;
@ -2058,17 +2043,17 @@ static const u64 r9a09g047_gpio_configs[] = {
RZG2L_GPIO_PORT_PACK(6, 0x28, RZV2H_MPXED_PIN_FUNCS), /* P8 */
0x0,
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2a), /* PA */
RZG2L_GPIO_PORT_PACK(8, 0x2b, RZV2H_MPXED_PIN_FUNCS), /* PB */
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2b), /* PB */
RZG2L_GPIO_PORT_PACK(3, 0x2c, RZV2H_MPXED_PIN_FUNCS), /* PC */
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2d), /* PD */
RZG2L_GPIO_PORT_PACK(8, 0x2e, RZV2H_MPXED_PIN_FUNCS), /* PE */
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x2e), /* PE */
RZG2L_GPIO_PORT_PACK(3, 0x2f, RZV2H_MPXED_PIN_FUNCS), /* PF */
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x30), /* PG */
RZG2L_GPIO_PORT_PACK_VARIABLE(6, 0x31), /* PH */
0x0,
RZG2L_GPIO_PORT_PACK_VARIABLE(5, 0x33), /* PJ */
RZG2L_GPIO_PORT_PACK(4, 0x34, RZV2H_MPXED_PIN_FUNCS), /* PK */
RZG2L_GPIO_PORT_PACK(8, 0x35, RZV2H_MPXED_PIN_FUNCS), /* PL */
RZG2L_GPIO_PORT_PACK_VARIABLE(8, 0x35), /* PL */
RZG2L_GPIO_PORT_PACK(8, 0x36, RZV2H_MPXED_PIN_FUNCS), /* PM */
0x0,
0x0,
@ -3164,7 +3149,7 @@ static int rzg2l_pinctrl_suspend_noirq(struct device *dev)
}
cache->qspi = readb(pctrl->base + QSPI);
cache->eth_mode = readb(pctrl->base + ETH_MODE);
cache->oen = readb(pctrl->base + pctrl->data->hwcfg->regs.oen);
if (!atomic_read(&pctrl->wakeup_path))
clk_disable_unprepare(pctrl->clk);
@ -3189,7 +3174,7 @@ static int rzg2l_pinctrl_resume_noirq(struct device *dev)
}
writeb(cache->qspi, pctrl->base + QSPI);
writeb(cache->eth_mode, pctrl->base + ETH_MODE);
writeb(cache->oen, pctrl->base + pctrl->data->hwcfg->regs.oen);
for (u8 i = 0; i < 2; i++) {
if (regs->sd_ch)
writeb(cache->sd_ch[i], pctrl->base + SD_CH(regs->sd_ch, i));
@ -3241,6 +3226,7 @@ static const struct rzg2l_hwcfg rzg2l_hwcfg = {
.pwpr = 0x3014,
.sd_ch = 0x3000,
.eth_poc = 0x300c,
.oen = 0x3018,
},
.iolh_groupa_ua = {
/* 3v3 power source */
@ -3256,6 +3242,7 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = {
.pwpr = 0x3000,
.sd_ch = 0x3004,
.eth_poc = 0x3010,
.oen = 0x3018,
},
.iolh_groupa_ua = {
/* 1v8 power source */
@ -3287,8 +3274,10 @@ static const struct rzg2l_hwcfg rzg3s_hwcfg = {
static const struct rzg2l_hwcfg rzv2h_hwcfg = {
.regs = {
.pwpr = 0x3c04,
.oen = 0x3c40,
},
.tint_start_index = 17,
.oen_pwpr_lock = true,
};
static struct rzg2l_pinctrl_data r9a07g043_data = {
@ -3305,8 +3294,7 @@ static struct rzg2l_pinctrl_data r9a07g043_data = {
#endif
.pwpr_pfc_lock_unlock = &rzg2l_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzg2l_pmc_writeb,
.oen_read = &rzg2l_read_oen,
.oen_write = &rzg2l_write_oen,
.pin_to_oen_bit = &rzg2l_pin_to_oen_bit,
.hw_to_bias_param = &rzg2l_hw_to_bias_param,
.bias_param_to_hw = &rzg2l_bias_param_to_hw,
};
@ -3322,8 +3310,7 @@ static struct rzg2l_pinctrl_data r9a07g044_data = {
.hwcfg = &rzg2l_hwcfg,
.pwpr_pfc_lock_unlock = &rzg2l_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzg2l_pmc_writeb,
.oen_read = &rzg2l_read_oen,
.oen_write = &rzg2l_write_oen,
.pin_to_oen_bit = &rzg2l_pin_to_oen_bit,
.hw_to_bias_param = &rzg2l_hw_to_bias_param,
.bias_param_to_hw = &rzg2l_bias_param_to_hw,
};
@ -3338,8 +3325,7 @@ static struct rzg2l_pinctrl_data r9a08g045_data = {
.hwcfg = &rzg3s_hwcfg,
.pwpr_pfc_lock_unlock = &rzg2l_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzg2l_pmc_writeb,
.oen_read = &rzg3s_oen_read,
.oen_write = &rzg3s_oen_write,
.pin_to_oen_bit = &rzg3s_pin_to_oen_bit,
.hw_to_bias_param = &rzg2l_hw_to_bias_param,
.bias_param_to_hw = &rzg2l_bias_param_to_hw,
};
@ -3361,8 +3347,7 @@ static struct rzg2l_pinctrl_data r9a09g047_data = {
#endif
.pwpr_pfc_lock_unlock = &rzv2h_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzv2h_pmc_writeb,
.oen_read = &rzv2h_oen_read,
.oen_write = &rzv2h_oen_write,
.pin_to_oen_bit = &rzg3e_pin_to_oen_bit,
.hw_to_bias_param = &rzv2h_hw_to_bias_param,
.bias_param_to_hw = &rzv2h_bias_param_to_hw,
};
@ -3384,8 +3369,7 @@ static struct rzg2l_pinctrl_data r9a09g056_data = {
#endif
.pwpr_pfc_lock_unlock = &rzv2h_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzv2h_pmc_writeb,
.oen_read = &rzv2h_oen_read,
.oen_write = &rzv2h_oen_write,
.pin_to_oen_bit = &rzv2h_pin_to_oen_bit,
.hw_to_bias_param = &rzv2h_hw_to_bias_param,
.bias_param_to_hw = &rzv2h_bias_param_to_hw,
};
@ -3408,8 +3392,7 @@ static struct rzg2l_pinctrl_data r9a09g057_data = {
#endif
.pwpr_pfc_lock_unlock = &rzv2h_pwpr_pfc_lock_unlock,
.pmc_writeb = &rzv2h_pmc_writeb,
.oen_read = &rzv2h_oen_read,
.oen_write = &rzv2h_oen_write,
.pin_to_oen_bit = &rzv2h_pin_to_oen_bit,
.hw_to_bias_param = &rzv2h_hw_to_bias_param,
.bias_param_to_hw = &rzv2h_bias_param_to_hw,
};

View File

@ -0,0 +1,813 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Renesas RZ/T2H Pin Control and GPIO driver core
*
* Based on drivers/pinctrl/renesas/pinctrl-rzg2l.c
*
* Copyright (C) 2025 Renesas Electronics Corporation.
*/
#include <linux/bitfield.h>
#include <linux/bitops.h>
#include <linux/bits.h>
#include <linux/cleanup.h>
#include <linux/clk.h>
#include <linux/gpio/driver.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/module.h>
#include <linux/mutex.h>
#include <linux/of_device.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <linux/pinctrl/consumer.h>
#include <linux/pinctrl/pinconf-generic.h>
#include <linux/pinctrl/pinconf.h>
#include <linux/pinctrl/pinctrl.h>
#include <linux/pinctrl/pinmux.h>
#include <dt-bindings/pinctrl/renesas,r9a09g077-pinctrl.h>
#include "../core.h"
#include "../pinconf.h"
#include "../pinmux.h"
#define DRV_NAME "pinctrl-rzt2h"
#define P(m) (0x001 * (m))
#define PM(m) (0x200 + 2 * (m))
#define PMC(m) (0x400 + (m))
#define PFC(m) (0x600 + 8 * (m))
#define PIN(m) (0x800 + (m))
#define RSELP(m) (0xc00 + (m))
#define PM_MASK GENMASK(1, 0)
#define PM_PIN_MASK(pin) (PM_MASK << ((pin) * 2))
#define PM_INPUT BIT(0)
#define PM_OUTPUT BIT(1)
#define PFC_MASK GENMASK_ULL(5, 0)
#define PFC_PIN_MASK(pin) (PFC_MASK << ((pin) * 8))
/*
* Use 16 lower bits [15:0] for pin identifier
* Use 8 higher bits [23:16] for pin mux function
*/
#define MUX_PIN_ID_MASK GENMASK(15, 0)
#define MUX_FUNC_MASK GENMASK(23, 16)
#define RZT2H_PIN_ID_TO_PORT(id) ((id) / RZT2H_PINS_PER_PORT)
#define RZT2H_PIN_ID_TO_PIN(id) ((id) % RZT2H_PINS_PER_PORT)
#define RZT2H_MAX_SAFETY_PORTS 12
struct rzt2h_pinctrl_data {
unsigned int n_port_pins;
const u8 *port_pin_configs;
unsigned int n_ports;
};
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 gpio_chip gpio_chip;
struct pinctrl_gpio_range gpio_range;
spinlock_t lock; /* lock read/write registers */
struct mutex mutex; /* serialize adding groups and functions */
bool safety_port_enabled;
};
#define RZT2H_GET_BASE(pctrl, port) \
((port) > RZT2H_MAX_SAFETY_PORTS ? (pctrl)->base0 : (pctrl)->base1)
#define RZT2H_PINCTRL_REG_ACCESS(size, type) \
static inline void rzt2h_pinctrl_write##size(struct rzt2h_pinctrl *pctrl, u8 port, \
type val, unsigned int offset) \
{ \
write##size(val, RZT2H_GET_BASE(pctrl, port) + offset); \
} \
static inline type rzt2h_pinctrl_read##size(struct rzt2h_pinctrl *pctrl, u8 port, \
unsigned int offset) \
{ \
return read##size(RZT2H_GET_BASE(pctrl, port) + offset); \
}
RZT2H_PINCTRL_REG_ACCESS(b, u8)
RZT2H_PINCTRL_REG_ACCESS(w, u16)
RZT2H_PINCTRL_REG_ACCESS(q, u64)
static int rzt2h_validate_pin(struct rzt2h_pinctrl *pctrl, unsigned int offset)
{
u8 port = RZT2H_PIN_ID_TO_PORT(offset);
u8 pin = RZT2H_PIN_ID_TO_PIN(offset);
u8 pincfg;
if (offset >= pctrl->data->n_port_pins || port >= pctrl->data->n_ports)
return -EINVAL;
if (!pctrl->safety_port_enabled && port <= RZT2H_MAX_SAFETY_PORTS)
return -EINVAL;
pincfg = pctrl->data->port_pin_configs[port];
return (pincfg & BIT(pin)) ? 0 : -EINVAL;
}
static void rzt2h_pinctrl_set_pfc_mode(struct rzt2h_pinctrl *pctrl,
u8 port, u8 pin, u8 func)
{
u64 reg64;
u16 reg16;
guard(spinlock_irqsave)(&pctrl->lock);
/* Set pin to 'Non-use (Hi-Z input protection)' */
reg16 = rzt2h_pinctrl_readw(pctrl, port, PM(port));
reg16 &= ~PM_PIN_MASK(pin);
rzt2h_pinctrl_writew(pctrl, port, reg16, PM(port));
/* Temporarily switch to GPIO mode with PMC register */
reg16 = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
rzt2h_pinctrl_writeb(pctrl, port, reg16 & ~BIT(pin), PMC(port));
/* Select Pin function mode with PFC register */
reg64 = rzt2h_pinctrl_readq(pctrl, port, PFC(port));
reg64 &= ~PFC_PIN_MASK(pin);
rzt2h_pinctrl_writeq(pctrl, port, reg64 | ((u64)func << (pin * 8)), PFC(port));
/* Switch to Peripheral pin function with PMC register */
reg16 = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
rzt2h_pinctrl_writeb(pctrl, port, reg16 | 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);
const struct function_desc *func;
struct group_desc *group;
const unsigned int *pins;
unsigned int i;
u8 *psel_val;
int ret;
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",
RZT2H_PIN_ID_TO_PORT(pins[i]), RZT2H_PIN_ID_TO_PIN(pins[i]),
psel_val[i]);
ret = rzt2h_validate_pin(pctrl, pins[i]);
if (ret)
return ret;
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 int rzt2h_map_add_config(struct pinctrl_map *map,
const char *group_or_pin,
enum pinctrl_map_type type,
unsigned long *configs,
unsigned int num_configs)
{
unsigned long *cfgs;
cfgs = kmemdup_array(configs, num_configs, sizeof(*cfgs), GFP_KERNEL);
if (!cfgs)
return -ENOMEM;
map->type = type;
map->data.configs.group_or_pin = group_or_pin;
map->data.configs.configs = cfgs;
map->data.configs.num_configs = num_configs;
return 0;
}
static int rzt2h_dt_subnode_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct device_node *parent,
struct pinctrl_map **map,
unsigned int *num_maps,
unsigned int *index)
{
struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
struct pinctrl_map *maps = *map;
unsigned int nmaps = *num_maps;
unsigned long *configs = NULL;
unsigned int num_pinmux = 0;
unsigned int idx = *index;
unsigned int num_pins, i;
unsigned int num_configs;
struct property *pinmux;
struct property *prop;
int ret, gsel, fsel;
const char **pin_fn;
unsigned int *pins;
const char *name;
const char *pin;
u8 *psel_val;
pinmux = of_find_property(np, "pinmux", NULL);
if (pinmux)
num_pinmux = pinmux->length / sizeof(u32);
ret = of_property_count_strings(np, "pins");
if (ret == -EINVAL) {
num_pins = 0;
} else if (ret < 0) {
dev_err(pctrl->dev, "Invalid pins list in DT\n");
return ret;
} else {
num_pins = ret;
}
if (!num_pinmux && !num_pins)
return 0;
if (num_pinmux && num_pins) {
dev_err(pctrl->dev,
"DT node must contain either a pinmux or pins and not both\n");
return -EINVAL;
}
ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, &num_configs);
if (ret < 0)
return ret;
if (num_pins && !num_configs) {
dev_err(pctrl->dev, "DT node must contain a config\n");
ret = -ENODEV;
goto done;
}
if (num_pinmux) {
nmaps += 1;
if (num_configs)
nmaps += 1;
}
if (num_pins)
nmaps += num_pins;
maps = krealloc_array(maps, nmaps, sizeof(*maps), GFP_KERNEL);
if (!maps) {
ret = -ENOMEM;
goto done;
}
*map = maps;
*num_maps = nmaps;
if (num_pins) {
of_property_for_each_string(np, "pins", prop, pin) {
ret = rzt2h_map_add_config(&maps[idx], pin,
PIN_MAP_TYPE_CONFIGS_PIN,
configs, num_configs);
if (ret < 0)
goto done;
idx++;
}
ret = 0;
goto done;
}
pins = devm_kcalloc(pctrl->dev, num_pinmux, sizeof(*pins), GFP_KERNEL);
psel_val = devm_kcalloc(pctrl->dev, num_pinmux, sizeof(*psel_val),
GFP_KERNEL);
pin_fn = devm_kzalloc(pctrl->dev, sizeof(*pin_fn), GFP_KERNEL);
if (!pins || !psel_val || !pin_fn) {
ret = -ENOMEM;
goto done;
}
/* Collect pin locations and mux settings from DT properties */
for (i = 0; i < num_pinmux; ++i) {
u32 value;
ret = of_property_read_u32_index(np, "pinmux", i, &value);
if (ret)
goto done;
pins[i] = FIELD_GET(MUX_PIN_ID_MASK, value);
psel_val[i] = FIELD_GET(MUX_FUNC_MASK, value);
}
if (parent) {
name = devm_kasprintf(pctrl->dev, GFP_KERNEL, "%pOFn.%pOFn",
parent, np);
if (!name) {
ret = -ENOMEM;
goto done;
}
} else {
name = np->name;
}
if (num_configs) {
ret = rzt2h_map_add_config(&maps[idx], name,
PIN_MAP_TYPE_CONFIGS_GROUP,
configs, num_configs);
if (ret < 0)
goto done;
idx++;
}
scoped_guard(mutex, &pctrl->mutex) {
/* Register a single pin group listing all the pins we read from DT */
gsel = pinctrl_generic_add_group(pctldev, name, pins, num_pinmux, NULL);
if (gsel < 0) {
ret = gsel;
goto done;
}
/*
* Register a single group function where the 'data' is an array PSEL
* register values read from DT.
*/
pin_fn[0] = name;
fsel = pinmux_generic_add_function(pctldev, name, pin_fn, 1, psel_val);
if (fsel < 0) {
ret = fsel;
goto remove_group;
}
}
maps[idx].type = PIN_MAP_TYPE_MUX_GROUP;
maps[idx].data.mux.group = name;
maps[idx].data.mux.function = name;
idx++;
dev_dbg(pctrl->dev, "Parsed %pOF with %d pins\n", np, num_pinmux);
ret = 0;
goto done;
remove_group:
pinctrl_generic_remove_group(pctldev, gsel);
done:
*index = idx;
kfree(configs);
return ret;
}
static void rzt2h_dt_free_map(struct pinctrl_dev *pctldev,
struct pinctrl_map *map,
unsigned int num_maps)
{
unsigned int i;
if (!map)
return;
for (i = 0; i < num_maps; ++i) {
if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP ||
map[i].type == PIN_MAP_TYPE_CONFIGS_PIN)
kfree(map[i].data.configs.configs);
}
kfree(map);
}
static int rzt2h_dt_node_to_map(struct pinctrl_dev *pctldev,
struct device_node *np,
struct pinctrl_map **map,
unsigned int *num_maps)
{
struct rzt2h_pinctrl *pctrl = pinctrl_dev_get_drvdata(pctldev);
unsigned int index;
int ret;
*map = NULL;
*num_maps = 0;
index = 0;
for_each_child_of_node_scoped(np, child) {
ret = rzt2h_dt_subnode_to_map(pctldev, child, np, map,
num_maps, &index);
if (ret < 0)
goto done;
}
if (*num_maps == 0) {
ret = rzt2h_dt_subnode_to_map(pctldev, np, NULL, map,
num_maps, &index);
if (ret < 0)
goto done;
}
if (*num_maps)
return 0;
dev_err(pctrl->dev, "no mapping found in node %pOF\n", np);
ret = -EINVAL;
done:
rzt2h_dt_free_map(pctldev, *map, *num_maps);
return ret;
}
static const struct pinctrl_ops rzt2h_pinctrl_pctlops = {
.get_groups_count = pinctrl_generic_get_group_count,
.get_group_name = pinctrl_generic_get_group_name,
.get_group_pins = pinctrl_generic_get_group_pins,
.dt_node_to_map = rzt2h_dt_node_to_map,
.dt_free_map = rzt2h_dt_free_map,
};
static const struct pinmux_ops rzt2h_pinctrl_pmxops = {
.get_functions_count = pinmux_generic_get_function_count,
.get_function_name = pinmux_generic_get_function_name,
.get_function_groups = pinmux_generic_get_function_groups,
.set_mux = rzt2h_pinctrl_set_mux,
.strict = true,
};
static int rzt2h_gpio_request(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);
int ret;
u8 reg;
ret = rzt2h_validate_pin(pctrl, offset);
if (ret)
return ret;
ret = pinctrl_gpio_request(chip, offset);
if (ret)
return ret;
guard(spinlock_irqsave)(&pctrl->lock);
/* Select GPIO mode in PMC Register */
reg = rzt2h_pinctrl_readb(pctrl, port, PMC(port));
reg &= ~BIT(bit);
rzt2h_pinctrl_writeb(pctrl, port, reg, PMC(port));
return 0;
}
static void rzt2h_gpio_set_direction(struct rzt2h_pinctrl *pctrl, u32 port,
u8 bit, bool output)
{
u16 reg;
guard(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));
}
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);
u16 reg;
int ret;
ret = rzt2h_validate_pin(pctrl, offset);
if (ret)
return ret;
if (rzt2h_pinctrl_readb(pctrl, port, PMC(port)) & BIT(bit))
return -EINVAL;
reg = rzt2h_pinctrl_readw(pctrl, port, PM(port));
reg = (reg >> (bit * 2)) & PM_MASK;
if (reg & PM_OUTPUT)
return GPIO_LINE_DIRECTION_OUT;
if (reg & PM_INPUT)
return GPIO_LINE_DIRECTION_IN;
return -EINVAL;
}
static int rzt2h_gpio_set(struct gpio_chip *chip, unsigned int offset,
int value)
{
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip);
u8 port = RZT2H_PIN_ID_TO_PORT(offset);
u8 bit = RZT2H_PIN_ID_TO_PIN(offset);
u8 reg;
guard(spinlock_irqsave)(&pctrl->lock);
reg = rzt2h_pinctrl_readb(pctrl, port, P(port));
if (value)
rzt2h_pinctrl_writeb(pctrl, port, reg | BIT(bit), P(port));
else
rzt2h_pinctrl_writeb(pctrl, port, reg & ~BIT(bit), P(port));
return 0;
}
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;
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)
return !!(rzt2h_pinctrl_readb(pctrl, port, P(port)) & BIT(bit));
return -EINVAL;
}
static int rzt2h_gpio_direction_input(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);
rzt2h_gpio_set_direction(pctrl, port, bit, false);
return 0;
}
static int rzt2h_gpio_direction_output(struct gpio_chip *chip,
unsigned int offset, int value)
{
struct rzt2h_pinctrl *pctrl = gpiochip_get_data(chip);
u8 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 void rzt2h_gpio_free(struct gpio_chip *chip, unsigned int offset)
{
pinctrl_gpio_free(chip, offset);
/*
* Set the GPIO as an input to ensure that the next GPIO request won't
* drive the GPIO pin as an output.
*/
rzt2h_gpio_direction_input(chip, offset);
}
static const char * const rzt2h_gpio_names[] = {
"P00_0", "P00_1", "P00_2", "P00_3", "P00_4", "P00_5", "P00_6", "P00_7",
"P01_0", "P01_1", "P01_2", "P01_3", "P01_4", "P01_5", "P01_6", "P01_7",
"P02_0", "P02_1", "P02_2", "P02_3", "P02_4", "P02_5", "P02_6", "P02_7",
"P03_0", "P03_1", "P03_2", "P03_3", "P03_4", "P03_5", "P03_6", "P03_7",
"P04_0", "P04_1", "P04_2", "P04_3", "P04_4", "P04_5", "P04_6", "P04_7",
"P05_0", "P05_1", "P05_2", "P05_3", "P05_4", "P05_5", "P05_6", "P05_7",
"P06_0", "P06_1", "P06_2", "P06_3", "P06_4", "P06_5", "P06_6", "P06_7",
"P07_0", "P07_1", "P07_2", "P07_3", "P07_4", "P07_5", "P07_6", "P07_7",
"P08_0", "P08_1", "P08_2", "P08_3", "P08_4", "P08_5", "P08_6", "P08_7",
"P09_0", "P09_1", "P09_2", "P09_3", "P09_4", "P09_5", "P09_6", "P09_7",
"P10_0", "P10_1", "P10_2", "P10_3", "P10_4", "P10_5", "P10_6", "P10_7",
"P11_0", "P11_1", "P11_2", "P11_3", "P11_4", "P11_5", "P11_6", "P11_7",
"P12_0", "P12_1", "P12_2", "P12_3", "P12_4", "P12_5", "P12_6", "P12_7",
"P13_0", "P13_1", "P13_2", "P13_3", "P13_4", "P13_5", "P13_6", "P13_7",
"P14_0", "P14_1", "P14_2", "P14_3", "P14_4", "P14_5", "P14_6", "P14_7",
"P15_0", "P15_1", "P15_2", "P15_3", "P15_4", "P15_5", "P15_6", "P15_7",
"P16_0", "P16_1", "P16_2", "P16_3", "P16_4", "P16_5", "P16_6", "P16_7",
"P17_0", "P17_1", "P17_2", "P17_3", "P17_4", "P17_5", "P17_6", "P17_7",
"P18_0", "P18_1", "P18_2", "P18_3", "P18_4", "P18_5", "P18_6", "P18_7",
"P19_0", "P19_1", "P19_2", "P19_3", "P19_4", "P19_5", "P19_6", "P19_7",
"P20_0", "P20_1", "P20_2", "P20_3", "P20_4", "P20_5", "P20_6", "P20_7",
"P21_0", "P21_1", "P21_2", "P21_3", "P21_4", "P21_5", "P21_6", "P21_7",
"P22_0", "P22_1", "P22_2", "P22_3", "P22_4", "P22_5", "P22_6", "P22_7",
"P23_0", "P23_1", "P23_2", "P23_3", "P23_4", "P23_5", "P23_6", "P23_7",
"P24_0", "P24_1", "P24_2", "P24_3", "P24_4", "P24_5", "P24_6", "P24_7",
"P25_0", "P25_1", "P25_2", "P25_3", "P25_4", "P25_5", "P25_6", "P25_7",
"P26_0", "P26_1", "P26_2", "P26_3", "P26_4", "P26_5", "P26_6", "P26_7",
"P27_0", "P27_1", "P27_2", "P27_3", "P27_4", "P27_5", "P27_6", "P27_7",
"P28_0", "P28_1", "P28_2", "P28_3", "P28_4", "P28_5", "P28_6", "P28_7",
"P29_0", "P29_1", "P29_2", "P29_3", "P29_4", "P29_5", "P29_6", "P29_7",
"P30_0", "P30_1", "P30_2", "P30_3", "P30_4", "P30_5", "P30_6", "P30_7",
"P31_0", "P31_1", "P31_2", "P31_3", "P31_4", "P31_5", "P31_6", "P31_7",
"P32_0", "P32_1", "P32_2", "P32_3", "P32_4", "P32_5", "P32_6", "P32_7",
"P33_0", "P33_1", "P33_2", "P33_3", "P33_4", "P33_5", "P33_6", "P33_7",
"P34_0", "P34_1", "P34_2", "P34_3", "P34_4", "P34_5", "P34_6", "P34_7",
"P35_0", "P35_1", "P35_2", "P35_3", "P35_4", "P35_5", "P35_6", "P35_7",
};
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 *dev = pctrl->dev;
struct of_phandle_args of_args;
int ret;
ret = of_parse_phandle_with_fixed_args(dev->of_node, "gpio-ranges", 3, 0, &of_args);
if (ret)
return dev_err_probe(dev, ret, "Unable to parse gpio-ranges\n");
if (of_args.args[0] != 0 || of_args.args[1] != 0 ||
of_args.args[2] != pctrl->data->n_port_pins)
return dev_err_probe(dev, -EINVAL,
"gpio-ranges does not match selected SOC\n");
chip->base = -1;
chip->parent = dev;
chip->owner = THIS_MODULE;
chip->ngpio = of_args.args[2];
chip->names = rzt2h_gpio_names;
chip->request = rzt2h_gpio_request;
chip->free = rzt2h_gpio_free;
chip->get_direction = rzt2h_gpio_get_direction;
chip->direction_input = rzt2h_gpio_direction_input;
chip->direction_output = rzt2h_gpio_direction_output;
chip->get = rzt2h_gpio_get;
chip->set = rzt2h_gpio_set;
chip->label = dev_name(dev);
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");
return ret;
}
static int rzt2h_pinctrl_register(struct rzt2h_pinctrl *pctrl)
{
struct pinctrl_desc *desc = &pctrl->desc;
struct device *dev = pctrl->dev;
struct pinctrl_pin_desc *pins;
unsigned int i, j;
int ret;
desc->name = DRV_NAME;
desc->npins = pctrl->data->n_port_pins;
desc->pctlops = &rzt2h_pinctrl_pctlops;
desc->pmxops = &rzt2h_pinctrl_pmxops;
desc->owner = THIS_MODULE;
pins = devm_kcalloc(dev, desc->npins, sizeof(*pins), GFP_KERNEL);
if (!pins)
return -ENOMEM;
pctrl->pins = pins;
desc->pins = pins;
for (i = 0, j = 0; i < pctrl->data->n_port_pins; i++) {
pins[i].number = i;
pins[i].name = rzt2h_gpio_names[i];
if (i && !(i % RZT2H_PINS_PER_PORT))
j++;
}
ret = devm_pinctrl_register_and_init(dev, desc, pctrl, &pctrl->pctl);
if (ret)
return dev_err_probe(dev, ret, "pinctrl registration failed\n");
ret = pinctrl_enable(pctrl->pctl);
if (ret)
return dev_err_probe(dev, ret, "pinctrl enable failed\n");
return rzt2h_gpio_register(pctrl);
}
static int rzt2h_pinctrl_cfg_regions(struct platform_device *pdev,
struct rzt2h_pinctrl *pctrl)
{
struct resource *res;
pctrl->base0 = devm_platform_ioremap_resource_byname(pdev, "nsr");
if (IS_ERR(pctrl->base0))
return PTR_ERR(pctrl->base0);
/*
* Open-coded instead of using devm_platform_ioremap_resource_byname()
* because the "srs" region is optional.
*/
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "srs");
if (res) {
u8 port;
pctrl->base1 = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(pctrl->base1))
return PTR_ERR(pctrl->base1);
pctrl->safety_port_enabled = true;
/* Configure to select safety region 0x812c0xxx */
for (port = 0; port <= RZT2H_MAX_SAFETY_PORTS; port++)
writeb(0x0, pctrl->base1 + RSELP(port));
}
return 0;
}
static int rzt2h_pinctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct rzt2h_pinctrl *pctrl;
int ret;
pctrl = devm_kzalloc(dev, sizeof(*pctrl), GFP_KERNEL);
if (!pctrl)
return -ENOMEM;
pctrl->dev = dev;
pctrl->data = of_device_get_match_data(dev);
ret = rzt2h_pinctrl_cfg_regions(pdev, pctrl);
if (ret)
return ret;
spin_lock_init(&pctrl->lock);
mutex_init(&pctrl->mutex);
platform_set_drvdata(pdev, pctrl);
return rzt2h_pinctrl_register(pctrl);
}
static const u8 r9a09g077_gpio_configs[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x7f,
};
static const u8 r9a09g087_gpio_configs[] = {
0x1f, 0xff, 0xff, 0x1f, 0x00, 0xfe, 0xff, 0x00, 0x7e, 0xf0, 0xff, 0x01,
0xff, 0xff, 0xff, 0x00, 0xe0, 0xff, 0xff, 0x00, 0xff, 0xff, 0xff, 0x01,
0xe0, 0xff, 0xff, 0x7f, 0x00, 0xfe, 0xff, 0x7f, 0x00, 0xfc, 0x7f,
};
static struct rzt2h_pinctrl_data r9a09g077_data = {
.n_port_pins = ARRAY_SIZE(r9a09g077_gpio_configs) * RZT2H_PINS_PER_PORT,
.port_pin_configs = r9a09g077_gpio_configs,
.n_ports = ARRAY_SIZE(r9a09g077_gpio_configs),
};
static struct rzt2h_pinctrl_data r9a09g087_data = {
.n_port_pins = ARRAY_SIZE(r9a09g087_gpio_configs) * RZT2H_PINS_PER_PORT,
.port_pin_configs = r9a09g087_gpio_configs,
.n_ports = ARRAY_SIZE(r9a09g087_gpio_configs),
};
static const struct of_device_id rzt2h_pinctrl_of_table[] = {
{
.compatible = "renesas,r9a09g077-pinctrl",
.data = &r9a09g077_data,
},
{
.compatible = "renesas,r9a09g087-pinctrl",
.data = &r9a09g087_data,
},
{ /* sentinel */ }
};
static struct platform_driver rzt2h_pinctrl_driver = {
.driver = {
.name = DRV_NAME,
.of_match_table = of_match_ptr(rzt2h_pinctrl_of_table),
.suppress_bind_attrs = true,
},
.probe = rzt2h_pinctrl_probe,
};
static int __init rzt2h_pinctrl_init(void)
{
return platform_driver_register(&rzt2h_pinctrl_driver);
}
core_initcall(rzt2h_pinctrl_init);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Thierry Bultel <thierry.bultel.yh@bp.renesas.com>");
MODULE_AUTHOR("Lad Prabhakar <prabhakar.mahadev-lad.rj@bp.renesas.com>");
MODULE_DESCRIPTION("Pin and gpio controller driver for the RZ/T2H family");

View File

@ -25,5 +25,6 @@
#define R9A09G077_CLK_PCLKM 13
#define R9A09G077_CLK_PCLKL 14
#define R9A09G077_SDHI_CLKHS 15
#define R9A09G077_USB_CLK 16
#endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G077_CPG_H__ */

View File

@ -25,5 +25,6 @@
#define R9A09G087_CLK_PCLKM 13
#define R9A09G087_CLK_PCLKL 14
#define R9A09G087_SDHI_CLKHS 15
#define R9A09G087_USB_CLK 16
#endif /* __DT_BINDINGS_CLOCK_RENESAS_R9A09G087_CPG_H__ */

View File

@ -0,0 +1,22 @@
/* SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause) */
/*
* This header provides constants for Renesas RZ/T2H family pinctrl bindings.
*
* Copyright (C) 2025 Renesas Electronics Corp.
*/
#ifndef __DT_BINDINGS_PINCTRL_RENESAS_R9A09G077_PINCTRL_H__
#define __DT_BINDINGS_PINCTRL_RENESAS_R9A09G077_PINCTRL_H__
#define RZT2H_PINS_PER_PORT 8
/*
* Create the pin index from its bank and position numbers and store in
* the upper 16 bits the alternate function identifier
*/
#define RZT2H_PORT_PINMUX(b, p, f) ((b) * RZT2H_PINS_PER_PORT + (p) | ((f) << 16))
/* Convert a port and pin label to its global pin index */
#define RZT2H_GPIO(port, pin) ((port) * RZT2H_PINS_PER_PORT + (pin))
#endif /* __DT_BINDINGS_PINCTRL_RENESAS_R9A09G077_PINCTRL_H__ */