Devicetree
 help / color / mirror / Atom feed
* [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver
@ 2026-06-02  8:01 Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
                   ` (5 more replies)
  0 siblings, 6 replies; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

This patch series adds support for basic GPIO
operations using gpio-regmap.

There are two SIUL2 hardware modules: SIUL2_0 and SIUL2_1.
However, this driver exports both as a single GPIO driver.
This is because the interrupt registers are located only
in SIUL2_1, even for GPIOs that are part of SIUL2_0.

There are two gaps in the GPIO ranges:
- 102-111(inclusive) are invalid
- 123-143(inclusive) are invalid

Writing and reading GPIO values is done via the PGPDO/PGPDI
registers(Parallel GPIO Pad Data Output/Input) which are
16 bit registers, each bit corresponding to a GPIO.

Note that the PGPDO order is similar to a big-endian grouping
of two registers:
PGPDO1, PGPDO0, PGPDO3, PGPDO2, PGPDO5, PGPDO4, gap, PGPDO6.

v10 -> v9:
- implement GPIO via gpio-regmap backed by a regmap for
PGPDO/PGPDI register translation
- remove the successful probe message from the driver
- switch back to a single compatible string for both the
legacy and extended binding layout
- update binding: GPIO/IRQ properties required only
when extended reg layout is used
- remove unnecessary return value checks for MMIO
regmap operations
- replace kernel-doc style comments with regular comments
- solve relevant sashiko.dev findings
- rework GPIO request handling to preserve pinctrl ownership
- use __free(kfree) and no_free_ptr() in GPIO request path cleanup

v9 -> v8
- remove the SIUL2 syscon child nodes from the
device tree and DT bindings
- remove syscon child handling from the MFD
and pinctrl drivers
- remove the MFD driver and use a single monolithic
pinctrl/gpio/irqchip driver
- add a new compatible for the pinctrl+gpio binding
while keeping the previous compatible for the legacy
pinctrl-only binding
- update bindings to include the PGPDO/PGPDI and
IRQ register regions in the DT node for the
pinctrl/gpio/irq binding
- add IRQ-related entries in the bindings to
document the intended hierarchy; IRQ support
itself will be added in a future patch series
- update DT nodes to match the new hierarchy and
compatible scheme
- fix dtb warnings
- reorder commits: bug fixes, API changes, DT bindings,
driver implementation, DTS changes
- split commits further to separate minor
style-only adjustments

v8 -> v7
- remove all ': true' lines from properties in dt bindings
- remove NVMEM MFD cell from SIUL2 in dtsi
- remove NVMEM driver and configs
- expose SoC information via syscon cells SIUL2_0
and SIUL2_1 in MFD driver
- add SIUL2_0 and SIUL2_1 syscon nodes in dtsi
- add patternProperties for "^siul2_[0-1]$" for syscon nodes
- update example to include syscon cells with proper format
- remove `reg` property from pinctrl node in dt binding
- update Kconfig help text to reflect new syscon structure
instead of NVMEM for SoC information
- squash deprecated SIUL2 pinctrl binding with new MFD binding
- dropped "nxp,s32g3-siul2" from MFD driver match table
- fixed commit messages
- fixed dtb warnings

v7 -> v6
- fixed MAINTAINERS wrong file path
- add unevaluatedProperties, change siul2 node name, remove
  jtag_pins label in the device tree schema
- change compatible definition in schema
- change node name in dtsi
- mentioned binding deprecation in commit messages
- split mfd cell conversion commit in two: one for the
  previous refactoring, one for the mfd cell conversion
- removed Acked-by: Linus Walleij from commit:
  "pinctrl: s32: convert the driver into an mfd cell"
  because of changes to that commit
- deprecate the nxp,s32g2-siul2-pinctrl binding
- add NVMEM MFD cell for SIUL2
- made the GPIO driver not export invalid pins
  (there are some gaps 102-111, 123-143)
- removed the need for gpio-reserved-ranges
- force initialized pinctrl_desc->num_custom_params to 0

v6 -> v5
- removed description for reg in the dt-bindings and added
  maxItems
- dropped label for example in the dt-bindings
- simplified the example in the dt-bindings
- changed dt-bindings filename to nxp,s32g2-siul2.yaml
- changed title in the dt-bindings
- dropped minItmes from gpio-ranges/gpio-reserved-ranges
  and added maxItems to gpio-reserved-ranges
- added required block for -grp[0-9]$ nodes
- switch to using "" as quotes
- kernel test robot: fixed frame sizes, added description
  for reg_name, fixed typo in gpio_configs_lock, removed
  uninitialized ret variable usage
- ordered includes in nxp-siul2.c, switched to dev-err-probe
  added a mention that other commits will add nvmem functionality
  to the mfd driver
- switched spin_lock_irqsave to scoped_guard statement
- switched dev_err to dev_err_probe in pinctrl-s32cc in places
  reached during the probing part

v5 -> v4
- fixed di_div error
- fixed dt-bindings error
- added Co-developed-by tags
- added new MFD driver nxp-siul2.c
- made the old pinctrl driver an MFD cell
- added the GPIO driver in the existing SIUL2 pinctrl one
- Switch from "devm_pinctrl_register" to
  "devm_pinctrl_register_and_init"

v4 -> v3
- removed useless parentheses
- added S32G3 fallback compatible
- fixed comment alignment
- fixed dt-bindings license
- fixed modpost: "__udivdi3"
- moved MAINTAINERS entry to have the new GPIO driver
  together with other files related to S32G

v3 -> v2
- fix dt-bindings schema id
- add maxItems to gpio-ranges
- removed gpio label from dt-bindings example
- added changelog for the MAINTAINERS commit and
  added separate entry for the SIUL2 GPIO driver
- added guard(raw_spinlock_irqsave) in
  'siul2_gpio_set_direction'
- updated the description for
  'devm_platform_get_and_ioremap_resource_byname'

v2 -> v1
dt-bindings:
- changed filename to match compatible
- fixed commit messages
- removed dt-bindings unnecessary properties descriptions
- added minItems for the interrupts property
driver:
- added depends on ARCH_S32 || COMPILE_TEST to Kconfig
- added select REGMAP_MMIO to Kconfig
- remove unnecessary include
- add of_node_put after `siul2_get_gpio_pinspec`
- removed inline from function definitions
- removed match data and moved the previous platdata
  definition to the top of the file to be visible
- replace bitmap_set/clear with __clear_bit/set_bit
  and devm_bitmap_zalloc with devm_kzalloc
- switched to gpiochip_generic_request/free/config
- fixed dev_err format for size_t reported by
  kernel test robot
- add platform_get_and_ioremap_resource_byname wrapper

Andrei Stefanescu (2):
  pinctrl: s32cc: change to "devm_pinctrl_register_and_init"
  pinctrl: s32cc: implement GPIO functionality

Khristine Andreea Barbulescu (4):
  pinctrl: s32cc: add/fix some comments
  pinctrl: s32cc: remove inline specifiers
  dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources
  arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl
    node

 .../pinctrl/nxp,s32g2-siul2-pinctrl.yaml      |  83 +-
 arch/arm64/boot/dts/freescale/s32g2.dtsi      |  23 +-
 arch/arm64/boot/dts/freescale/s32g3.dtsi      |  23 +-
 drivers/pinctrl/nxp/Kconfig                   |   1 +
 drivers/pinctrl/nxp/pinctrl-s32.h             |  32 +-
 drivers/pinctrl/nxp/pinctrl-s32cc.c           | 732 +++++++++++++++---
 drivers/pinctrl/nxp/pinctrl-s32g2.c           |  46 +-
 7 files changed, 836 insertions(+), 104 deletions(-)

-- 
2.34.1


^ permalink raw reply	[flat|nested] 12+ messages in thread

* [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:06   ` sashiko-bot
  2026-06-02  8:01 ` [PATCH v10 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu
                   ` (4 subsequent siblings)
  5 siblings, 1 reply; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

Add/fix some comments and print statements.

Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 drivers/pinctrl/nxp/pinctrl-s32cc.c | 20 ++++++++++++++------
 1 file changed, 14 insertions(+), 6 deletions(-)

diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
index fe7cd641fddd..4f88c24e62a2 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
@@ -60,6 +60,12 @@ static u32 get_pin_func(u32 pinmux)
 	return pinmux & GENMASK(3, 0);
 }
 
+/*
+ * struct s32_pinctrl_mem_region - memory region for a set of SIUL2 registers
+ * @map: regmap used for this range
+ * @pin_range: the pins controlled by these registers
+ * @name: name of the current range
+ */
 struct s32_pinctrl_mem_region {
 	struct regmap *map;
 	const struct s32_pin_range *pin_range;
@@ -67,7 +73,7 @@ struct s32_pinctrl_mem_region {
 };
 
 /*
- * Holds pin configuration for GPIO's.
+ * struct gpio_pin_config - holds pin configuration for GPIO's
  * @pin_id: Pin ID for this GPIO
  * @config: Pin settings
  * @list: Linked list entry for each gpio pin
@@ -79,20 +85,22 @@ struct gpio_pin_config {
 };
 
 /*
- * Pad config save/restore for power suspend/resume.
+ * struct s32_pinctrl_context - pad config save/restore for suspend/resume
+ * @pads: saved values for the pards
  */
 struct s32_pinctrl_context {
 	unsigned int *pads;
 };
 
 /*
+ * struct s32_pinctrl - private driver data
  * @dev: a pointer back to containing device
  * @pctl: a pointer to the pinctrl device structure
  * @regions: reserved memory regions with start/end pin
  * @info: structure containing information about the pin
- * @gpio_configs: Saved configurations for GPIO pins
- * @gpiop_configs_lock: lock for the `gpio_configs` list
- * @s32_pinctrl_context: Configuration saved over system sleep
+ * @gpio_configs: saved configurations for GPIO pins
+ * @gpio_configs_lock: lock for the `gpio_configs` list
+ * @saved_context: configuration saved over system sleep
  */
 struct s32_pinctrl {
 	struct device *dev;
@@ -974,7 +982,7 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 					    ipctl);
 	if (IS_ERR(ipctl->pctl))
 		return dev_err_probe(&pdev->dev, PTR_ERR(ipctl->pctl),
-				     "could not register s32 pinctrl driver\n");
+				     "Could not register s32 pinctrl driver\n");
 
 #ifdef CONFIG_PM_SLEEP
 	saved_context = &ipctl->saved_context;
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 2/6] pinctrl: s32cc: remove inline specifiers
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu
                   ` (3 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

Remove unnecessary inline specifiers from static functions.

Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 drivers/pinctrl/nxp/pinctrl-s32cc.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
index 4f88c24e62a2..2645a92df0ba 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
@@ -131,13 +131,13 @@ s32_get_region(struct pinctrl_dev *pctldev, unsigned int pin)
 	return NULL;
 }
 
-static inline int s32_check_pin(struct pinctrl_dev *pctldev,
-				unsigned int pin)
+static int s32_check_pin(struct pinctrl_dev *pctldev,
+			 unsigned int pin)
 {
 	return s32_get_region(pctldev, pin) ? 0 : -EINVAL;
 }
 
-static inline int s32_regmap_read(struct pinctrl_dev *pctldev,
+static int s32_regmap_read(struct pinctrl_dev *pctldev,
 			   unsigned int pin, unsigned int *val)
 {
 	struct s32_pinctrl_mem_region *region;
@@ -153,7 +153,7 @@ static inline int s32_regmap_read(struct pinctrl_dev *pctldev,
 	return regmap_read(region->map, offset, val);
 }
 
-static inline int s32_regmap_write(struct pinctrl_dev *pctldev,
+static int s32_regmap_write(struct pinctrl_dev *pctldev,
 			    unsigned int pin,
 			    unsigned int val)
 {
@@ -171,7 +171,7 @@ static inline int s32_regmap_write(struct pinctrl_dev *pctldev,
 
 }
 
-static inline int s32_regmap_update(struct pinctrl_dev *pctldev, unsigned int pin,
+static int s32_regmap_update(struct pinctrl_dev *pctldev, unsigned int pin,
 			     unsigned int mask, unsigned int val)
 {
 	struct s32_pinctrl_mem_region *region;
@@ -484,8 +484,8 @@ static int s32_get_slew_regval(int arg)
 	return -EINVAL;
 }
 
-static inline void s32_pin_set_pull(enum pin_config_param param,
-				   unsigned int *mask, unsigned int *config)
+static void s32_pin_set_pull(enum pin_config_param param,
+			     unsigned int *mask, unsigned int *config)
 {
 	switch (param) {
 	case PIN_CONFIG_BIAS_DISABLE:
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init"
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu
                   ` (2 subsequent siblings)
  5 siblings, 0 replies; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

From: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>

Switch from "devm_pinctrl_register" to "devm_pinctrl_register_and_init"
and "pinctrl_enable" since this is the recommended way.

Reviewed-by: Frank Li <Frank.Li@nxp.com>
Reviewed-by: Linus Walleij <linus.walleij@linaro.org>
Reviewed-by: Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>
Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 drivers/pinctrl/nxp/pinctrl-s32cc.c | 15 ++++++++++-----
 1 file changed, 10 insertions(+), 5 deletions(-)

diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
index 2645a92df0ba..89a4eb2000ee 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
@@ -978,10 +978,10 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 		return ret;
 	}
 
-	ipctl->pctl = devm_pinctrl_register(&pdev->dev, s32_pinctrl_desc,
-					    ipctl);
-	if (IS_ERR(ipctl->pctl))
-		return dev_err_probe(&pdev->dev, PTR_ERR(ipctl->pctl),
+	ret = devm_pinctrl_register_and_init(&pdev->dev, s32_pinctrl_desc,
+					     ipctl, &ipctl->pctl);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
 				     "Could not register s32 pinctrl driver\n");
 
 #ifdef CONFIG_PM_SLEEP
@@ -994,7 +994,12 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 		return -ENOMEM;
 #endif
 
-	dev_info(&pdev->dev, "initialized s32 pinctrl driver\n");
+	ret = pinctrl_enable(ipctl->pctl);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to enable pinctrl\n");
+
+	dev_info(&pdev->dev, "Initialized S32 pinctrl driver\n");
 
 	return 0;
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
                   ` (2 preceding siblings ...)
  2026-06-02  8:01 ` [PATCH v10 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:21   ` sashiko-bot
  2026-06-02  8:01 ` [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu
  2026-06-02  8:01 ` [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu
  5 siblings, 1 reply; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

Extend the S32G2 SIUL2 pinctrl binding to describe the GPIO data and
external interrupt resources present in the same SIUL2 hardware block.

Besides the MSCR and IMCR registers used for pin multiplexing and pad
configuration, SIUL2 also contains PGPDO and PGPDI registers
for GPIO data and EIRQ registers for external interrupt control.

Add GPIO controller properties because the SIUL2 block also provides
GPIO functionality, and gpio-ranges are needed to describe the
mapping between GPIO lines and pin controller pins.

Document the interrupt controller properties. The SIUL2 block
contains EIRQ hardware as part of the same register space. IRQ support
itself will be added in a follow-up patch series.

Update the example accordingly to show the complete SIUL2 register
layout, including the GPIO data and EIRQ register windows.

Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 .../pinctrl/nxp,s32g2-siul2-pinctrl.yaml      | 83 +++++++++++++++++--
 1 file changed, 78 insertions(+), 5 deletions(-)

diff --git a/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
index a24286e4def6..e4cc1a3a795c 100644
--- a/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
@@ -1,5 +1,5 @@
 # SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
-# Copyright 2022 NXP
+# Copyright 2022, 2026 NXP
 %YAML 1.2
 ---
 $id: http://devicetree.org/schemas/pinctrl/nxp,s32g2-siul2-pinctrl.yaml#
@@ -17,8 +17,10 @@ description: |
     SIUL2_0 @ 0x4009c000
     SIUL2_1 @ 0x44010000
 
-  Every SIUL2 region has multiple register types, and here only MSCR and
-  IMCR registers need to be revealed for kernel to configure pinmux.
+  Every SIUL2 region has multiple register types. MSCR and IMCR registers
+  need to be revealed for kernel to configure pinmux. PGPDO and PGPDI
+  registers are used for GPIO output/input operations. EIRQ registers
+  are used for external interrupt configuration.
 
   Please note that some register indexes are reserved in S32G2, such as
   MSCR102-MSCR111, MSCR123-MSCR143, IMCR84-IMCR118 and IMCR398-IMCR429.
@@ -29,14 +31,22 @@ properties:
       - nxp,s32g2-siul2-pinctrl
 
   reg:
+    minItems: 6
     description: |
-      A list of MSCR/IMCR register regions to be reserved.
+      A list of MSCR/IMCR/PGPDO/PGPDI/EIRQ register regions to be reserved.
       - MSCR (Multiplexed Signal Configuration Register)
         An MSCR register can configure the associated pin as either a GPIO pin
         or a function output pin depends on the selected signal source.
       - IMCR (Input Multiplexed Signal Configuration Register)
         An IMCR register can configure the associated pin as function input
         pin depends on the selected signal source.
+      - PGPDO (Parallel GPIO Pad Data Out Register)
+        A PGPDO register is used to set the output value of a GPIO pin.
+      - PGPDI (Parallel GPIO Pad Data In Register)
+        A PGPDI register is used to read the input value of a GPIO pin.
+      - EIRQ (External Interrupt Request)
+        EIRQ registers are used to configure and manage external interrupts.
+
     items:
       - description: MSCR registers group 0 in SIUL2_0
       - description: MSCR registers group 1 in SIUL2_1
@@ -44,6 +54,28 @@ properties:
       - description: IMCR registers group 0 in SIUL2_0
       - description: IMCR registers group 1 in SIUL2_1
       - description: IMCR registers group 2 in SIUL2_1
+      - description: PGPDO registers in SIUL2_0
+      - description: PGPDI registers in SIUL2_0
+      - description: PGPDO registers in SIUL2_1
+      - description: PGPDI registers in SIUL2_1
+      - description: EIRQ registers in SIUL2_1
+
+  gpio-controller: true
+
+  "#gpio-cells":
+    const: 2
+
+  gpio-ranges:
+    minItems: 1
+    maxItems: 4
+
+  interrupt-controller: true
+
+  "#interrupt-cells":
+    const: 2
+
+  interrupts:
+    maxItems: 1
 
 patternProperties:
   '-pins$':
@@ -86,10 +118,32 @@ required:
   - compatible
   - reg
 
+oneOf:
+  - description: Legacy pinctrl-only node
+    properties:
+      reg:
+        minItems: 6
+        maxItems: 6
+
+  - description: Pinctrl node with GPIO and external interrupt support
+    required:
+      - gpio-controller
+      - "#gpio-cells"
+      - gpio-ranges
+      - interrupt-controller
+      - "#interrupt-cells"
+      - interrupts
+    properties:
+      reg:
+        minItems: 11
+        maxItems: 11
+
 additionalProperties: false
 
 examples:
   - |
+    #include <dt-bindings/interrupt-controller/arm-gic.h>
+
     pinctrl@4009c240 {
         compatible = "nxp,s32g2-siul2-pinctrl";
 
@@ -104,7 +158,26 @@ examples:
               /* IMCR119-IMCR397 registers on siul2_1 */
               <0x44010c1c 0x45c>,
               /* IMCR430-IMCR495 registers on siul2_1 */
-              <0x440110f8 0x108>;
+              <0x440110f8 0x108>,
+              /* PGPDO registers on siul2_0 */
+              <0x4009d700 0x10>,
+              /* PGPDI registers on siul2_0 */
+              <0x4009d740 0x10>,
+              /* PGPDO registers on siul2_1 */
+              <0x44011700 0x18>,
+              /* PGPDI registers on siul2_1 */
+              <0x44011740 0x18>,
+              /* EIRQ registers on siul2_1 */
+              <0x44010010 0x34>;
+
+        gpio-controller;
+        #gpio-cells = <2>;
+        gpio-ranges = <&pinctrl 0 0 102>,
+                      <&pinctrl 112 112 79>;
+
+        interrupt-controller;
+        #interrupt-cells = <2>;
+        interrupts = <GIC_SPI 210 IRQ_TYPE_LEVEL_HIGH>;
 
         llce-can0-pins {
             llce-can0-grp0 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
                   ` (3 preceding siblings ...)
  2026-06-02  8:01 ` [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:31   ` sashiko-bot
  2026-06-02  9:26   ` Enric Balletbo i Serra
  2026-06-02  8:01 ` [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu
  5 siblings, 2 replies; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

From: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>

The updated SIUL2 block groups pinctrl, GPIO data access
and interrupt control within the same hardware unit.
The SIUL2 driver is therefore structured as a monolithic
pinctrl/GPIO driver.

GPIO data access and direction handling are implemented using the
gpio-regmap library backed by a virtual regmap. The virtual regmap
translates the gpio-regmap register model to the underlying SIUL2
registers: MSCR for direction, PGPDI for input values and PGPDO for
output values.

The existing pinctrl GPIO callbacks are used for the request/free path:
they switch the pad to GPIO mode on request and restore the previous
MSCR configuration when the GPIO is released.

This change came as a result of upstream review in the
following series:
https://lore.kernel.org/linux-gpio/20260120115923.3463866-4-khristineandreea.barbulescu@oss.nxp.com/T/#m543c9edbdde74bdc68b6a2364e8b975356c33043
https://lore.kernel.org/all/20260504131148.3622697-7-khristineandreea.barbulescu@oss.nxp.com/

Support both SIUL2 DT layouts:
- legacy pinctrl-only binding
- extended pinctrl/GPIO/irqchip binding

Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 drivers/pinctrl/nxp/Kconfig         |   1 +
 drivers/pinctrl/nxp/pinctrl-s32.h   |  32 +-
 drivers/pinctrl/nxp/pinctrl-s32cc.c | 685 +++++++++++++++++++++++++---
 drivers/pinctrl/nxp/pinctrl-s32g2.c |  46 +-
 4 files changed, 686 insertions(+), 78 deletions(-)

diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index abca7ef97003..59fc6adf5b0b 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -5,6 +5,7 @@ config PINCTRL_S32CC
 	select GENERIC_PINCTRL_GROUPS
 	select GENERIC_PINMUX_FUNCTIONS
 	select GENERIC_PINCONF
+	select GPIO_REGMAP
 	select REGMAP_MMIO
 
 config PINCTRL_S32G2
diff --git a/drivers/pinctrl/nxp/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h
index 8715befd5f05..c2fc5eda7eb4 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32.h
+++ b/drivers/pinctrl/nxp/pinctrl-s32.h
@@ -2,7 +2,7 @@
  *
  * S32 pinmux core definitions
  *
- * Copyright 2016-2020, 2022 NXP
+ * Copyright 2016-2020, 2022, 2026 NXP
  * Copyright (C) 2022 SUSE LLC
  * Copyright 2015-2016 Freescale Semiconductor, Inc.
  * Copyright (C) 2012 Linaro Ltd.
@@ -34,11 +34,39 @@ struct s32_pin_range {
 	unsigned int end;
 };
 
+/**
+ * struct s32_gpio_range - contiguous GPIO pin range within a SIUL2 module
+ * @gpio_base: first GPIO line offset in the GPIO range
+ * @pin_base: first pinctrl pin number mapped by this GPIO range
+ * @gpio_num: number of consecutive GPIO pins in the range
+ */
+struct s32_gpio_range {
+	unsigned int gpio_base;
+	unsigned int pin_base;
+	unsigned int gpio_num;
+};
+
+/**
+ * struct s32_gpio_pad_map - mapping between GPIO ranges and PGPD pads
+ * @gpio_start: first GPIO line offset in the range
+ * @gpio_end: last GPIO line offset in the range
+ * @pad: PGPD pad number serving the range
+ */
+struct s32_gpio_pad_map {
+	unsigned int gpio_start;
+	unsigned int gpio_end;
+	unsigned int pad;
+};
+
 struct s32_pinctrl_soc_data {
 	const struct pinctrl_pin_desc *pins;
 	unsigned int npins;
 	const struct s32_pin_range *mem_pin_ranges;
 	unsigned int mem_regions;
+	const struct s32_gpio_range *gpio_ranges;
+	unsigned int num_gpio_ranges;
+	const struct s32_gpio_pad_map *gpio_pad_maps;
+	unsigned int num_gpio_pad_maps;
 };
 
 struct s32_pinctrl_soc_info {
@@ -53,6 +81,8 @@ struct s32_pinctrl_soc_info {
 
 #define S32_PINCTRL_PIN(pin)	PINCTRL_PIN(pin, #pin)
 #define S32_PIN_RANGE(_start, _end) { .start = _start, .end = _end }
+#define S32_GPIO_RANGE(gpio, pin, num) \
+	{ .gpio_base = gpio, .pin_base = pin, .gpio_num = num }
 
 int s32_pinctrl_probe(struct platform_device *pdev,
 		      const struct s32_pinctrl_soc_data *soc_data);
diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
index 89a4eb2000ee..8843926345ec 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
@@ -2,7 +2,7 @@
 /*
  * Core driver for the S32 CC (Common Chassis) pin controller
  *
- * Copyright 2017-2022,2024 NXP
+ * Copyright 2017-2022,2024-2026 NXP
  * Copyright (C) 2022 SUSE LLC
  * Copyright 2015-2016 Freescale Semiconductor, Inc.
  */
@@ -10,6 +10,7 @@
 #include <linux/bitops.h>
 #include <linux/err.h>
 #include <linux/gpio/driver.h>
+#include <linux/gpio/regmap.h>
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/module.h>
@@ -39,6 +40,40 @@
 #define S32_MSCR_ODE		BIT(20)
 #define S32_MSCR_OBE		BIT(21)
 
+#define S32_GPIO_OP_SHIFT	16
+#define S32_GPIO_OP_MASK	GENMASK(19, 16)
+
+#define S32_GPIO_OP_DIR		0 /* MSCR direction */
+#define S32_GPIO_OP_DAT		BIT(S32_GPIO_OP_SHIFT) /* PGPDI read */
+#define S32_GPIO_OP_SET		BIT(S32_GPIO_OP_SHIFT + 1) /* PGPDO write */
+
+/*
+ * [15:12] = GPIO bank / gpio range index
+ * [11:0]  = real register offset or pin id
+ */
+#define S32_GPIO_BANK_SHIFT    12
+#define S32_GPIO_BANK_MASK    GENMASK(15, 12)
+#define S32_GPIO_REG_MASK    GENMASK(11, 0)
+
+#define S32_GPIO_ENCODE(bank, off) \
+	((((bank) << S32_GPIO_BANK_SHIFT) & S32_GPIO_BANK_MASK) | \
+		((off) & S32_GPIO_REG_MASK))
+
+#define S32_GPIO_DECODE_BANK(reg) \
+	(((reg) & S32_GPIO_BANK_MASK) >> S32_GPIO_BANK_SHIFT)
+
+#define S32_GPIO_DECODE_OFF(reg) \
+	((reg) & S32_GPIO_REG_MASK)
+
+/*
+ * PGPDOs are 16bit registers that come in big endian
+ * order if they are grouped in pairs of two.
+ *
+ * For example, the order is PGPDO1, PGPDO0, PGPDO3, PGPDO2...
+ */
+#define S32_PGPD(N)		(((N) ^ 1) * 2)
+#define S32_PGPD_SIZE		16
+
 enum s32_write_type {
 	S32_PINCONF_UPDATE_ONLY,
 	S32_PINCONF_OVERWRITE,
@@ -72,6 +107,18 @@ struct s32_pinctrl_mem_region {
 	char name[8];
 };
 
+/*
+ * struct s32_gpio_regmaps - GPIO register maps for a SIUL2 instance
+ * @pgpdo: regmap for Parallel GPIO Pad Data Out registers
+ * @pgpdi: regmap for Parallel GPIO Pad Data In registers
+ * @range: GPIO range info
+ */
+struct s32_gpio_regmaps {
+	struct regmap *pgpdo;
+	struct regmap *pgpdi;
+	const struct s32_gpio_range *range;
+};
+
 /*
  * struct gpio_pin_config - holds pin configuration for GPIO's
  * @pin_id: Pin ID for this GPIO
@@ -98,6 +145,12 @@ struct s32_pinctrl_context {
  * @pctl: a pointer to the pinctrl device structure
  * @regions: reserved memory regions with start/end pin
  * @info: structure containing information about the pin
+ * @gpio_regmaps: PGPDO/PGPDI regmaps for each SIUL2 module
+ * @num_gpio_regmaps: number of GPIO regmap entries
+ * @gpio_regmap: regmap bridging gpio-regmap to SIUL2 registers
+ * @gpio_rgm: gpio-regmap instance registered for this controller
+ * @ngpio: total number of GPIO line offsets
+ * @gpio_names: GPIO line names array passed to gpio-regmap
  * @gpio_configs: saved configurations for GPIO pins
  * @gpio_configs_lock: lock for the `gpio_configs` list
  * @saved_context: configuration saved over system sleep
@@ -107,6 +160,12 @@ struct s32_pinctrl {
 	struct pinctrl_dev *pctl;
 	struct s32_pinctrl_mem_region *regions;
 	struct s32_pinctrl_soc_info *info;
+	struct s32_gpio_regmaps *gpio_regmaps;
+	unsigned int num_gpio_regmaps;
+	struct regmap *gpio_regmap;
+	struct gpio_regmap *gpio_rgm;
+	unsigned int ngpio;
+	const char *const *gpio_names;
 	struct list_head gpio_configs;
 	spinlock_t gpio_configs_lock;
 #ifdef CONFIG_PM_SLEEP
@@ -356,88 +415,84 @@ static int s32_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
 	return info->nfunctions;
 }
 
-static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev,
-					 unsigned int selector)
-{
-	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
-	const struct s32_pinctrl_soc_info *info = ipctl->info;
-
-	return info->functions[selector].name;
-}
-
-static int s32_pmx_get_groups(struct pinctrl_dev *pctldev,
-			      unsigned int selector,
-			      const char * const **groups,
-			      unsigned int * const num_groups)
-{
-	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
-	const struct s32_pinctrl_soc_info *info = ipctl->info;
-
-	*groups = info->functions[selector].groups;
-	*num_groups = info->functions[selector].ngroups;
-
-	return 0;
-}
-
 static int s32_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
 				       struct pinctrl_gpio_range *range,
-				       unsigned int offset)
+				       unsigned int pin)
 {
 	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
-	struct gpio_pin_config *gpio_pin;
+	struct gpio_pin_config *gpio_pin __free(kfree) = NULL;
 	unsigned int config;
-	unsigned long flags;
 	int ret;
 
-	ret = s32_regmap_read(pctldev, offset, &config);
+	ret = s32_regmap_read(pctldev, pin, &config);
 	if (ret)
 		return ret;
 
-	/* Save current configuration */
-	gpio_pin = kmalloc_obj(*gpio_pin);
+	gpio_pin = kmalloc(sizeof(*gpio_pin), GFP_KERNEL);
 	if (!gpio_pin)
 		return -ENOMEM;
 
-	gpio_pin->pin_id = offset;
+	gpio_pin->pin_id = pin;
 	gpio_pin->config = config;
-	INIT_LIST_HEAD(&gpio_pin->list);
-
-	spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
-	list_add(&gpio_pin->list, &ipctl->gpio_configs);
-	spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
 
 	/* GPIO pin means SSS = 0 */
-	config &= ~S32_MSCR_SSS_MASK;
+	ret = s32_regmap_update(pctldev, pin,
+				S32_MSCR_SSS_MASK | S32_MSCR_IBE,
+				S32_MSCR_IBE);
+	if (ret)
+		return ret;
+
+	scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock)
+		list_add(&no_free_ptr(gpio_pin)->list, &ipctl->gpio_configs);
 
-	return s32_regmap_write(pctldev, offset, config);
+	return 0;
 }
 
 static void s32_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
 				      struct pinctrl_gpio_range *range,
-				      unsigned int offset)
+				      unsigned int pin)
 {
 	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
-	struct gpio_pin_config *gpio_pin, *tmp;
+	struct gpio_pin_config *gpio_pin, *found = NULL;
 	unsigned long flags;
-	int ret;
 
 	spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
-
-	list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) {
-		if (gpio_pin->pin_id == offset) {
-			ret = s32_regmap_write(pctldev, gpio_pin->pin_id,
-						 gpio_pin->config);
-			if (ret != 0)
-				goto unlock;
-
+	list_for_each_entry(gpio_pin, &ipctl->gpio_configs, list) {
+		if (gpio_pin->pin_id == pin) {
 			list_del(&gpio_pin->list);
-			kfree(gpio_pin);
+			found = gpio_pin;
 			break;
 		}
 	}
-
-unlock:
 	spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
+
+	if (found) {
+		s32_regmap_write(pctldev, found->pin_id, found->config);
+		kfree(found);
+	}
+}
+
+static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev,
+					 unsigned int selector)
+{
+	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+	const struct s32_pinctrl_soc_info *info = ipctl->info;
+
+	return info->functions[selector].name;
+}
+
+static int s32_pmx_get_groups(struct pinctrl_dev *pctldev,
+			      unsigned int selector,
+			      const char * const **groups,
+			      unsigned int * const num_groups)
+{
+	struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
+	const struct s32_pinctrl_soc_info *info = ipctl->info;
+
+	*groups = info->functions[selector].groups;
+	*num_groups = info->functions[selector].ngroups;
+
+	return 0;
 }
 
 static int s32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
@@ -649,9 +704,9 @@ static void s32_pinconf_dbg_show(struct pinctrl_dev *pctldev,
 
 	ret = s32_regmap_read(pctldev, pin_id, &config);
 	if (ret)
-		return;
-
-	seq_printf(s, "0x%x", config);
+		seq_printf(s, "error %d", ret);
+	else
+		seq_printf(s, "0x%x", config);
 }
 
 static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
@@ -662,15 +717,13 @@ static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
 	struct s32_pin_group *grp;
 	unsigned int config;
 	const char *name;
-	int i, ret;
+	int i;
 
 	seq_puts(s, "\n");
 	grp = &info->groups[selector];
 	for (i = 0; i < grp->data.npins; i++) {
 		name = pin_get_name(pctldev, grp->data.pins[i]);
-		ret = s32_regmap_read(pctldev, grp->data.pins[i], &config);
-		if (ret)
-			return;
+		s32_regmap_read(pctldev, grp->data.pins[i], &config);
 		seq_printf(s, "%s: 0x%x\n", name, config);
 	}
 }
@@ -683,6 +736,450 @@ static const struct pinconf_ops s32_pinconf_ops = {
 	.pin_config_group_dbg_show = s32_pinconf_group_dbg_show,
 };
 
+static void s32_gpio_free_saved_configs(void *data)
+{
+	struct s32_pinctrl *ipctl = data;
+	struct gpio_pin_config *gpio_pin, *tmp;
+	unsigned long flags;
+
+	spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
+	list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) {
+		list_del(&gpio_pin->list);
+		kfree(gpio_pin);
+	}
+	spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
+}
+
+static unsigned int s32_pin2pad(unsigned int pin)
+{
+	return pin / S32_PGPD_SIZE;
+}
+
+static u16 s32_pin2mask(unsigned int pin)
+{
+	/*
+	 * From Reference manual :
+	 * PGPDOx[PPDOy] = GPDO(x × 16) + (15 - y)[PDO_(x × 16) + (15 - y)]
+	 */
+	return BIT(S32_PGPD_SIZE - 1 - pin % S32_PGPD_SIZE);
+}
+
+static int s32_gpio_get_range(struct s32_pinctrl *ipctl,
+			      unsigned int gpio,
+			      unsigned int *pin,
+			      unsigned int *bank)
+{
+	const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
+	const struct s32_gpio_range *range;
+	int i;
+
+	for (i = 0; i < soc_data->num_gpio_ranges; i++) {
+		range = &soc_data->gpio_ranges[i];
+
+		if (gpio < range->gpio_base ||
+		    gpio >= range->gpio_base + range->gpio_num)
+			continue;
+
+		if (pin)
+			*pin = range->pin_base + gpio - range->gpio_base;
+
+		if (bank)
+			*bank = i;
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int s32_gpio_pad_map_xlate(struct s32_pinctrl *ipctl,
+				  unsigned int gpio,
+				  unsigned int *reg_offset,
+				  u16 *mask)
+{
+	const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
+	const struct s32_gpio_pad_map *map;
+	unsigned int bit;
+	int i;
+
+	if (!soc_data->gpio_pad_maps || !soc_data->num_gpio_pad_maps)
+		return -EINVAL;
+
+	for (i = 0; i < soc_data->num_gpio_pad_maps; i++) {
+		map = &soc_data->gpio_pad_maps[i];
+
+		if (gpio < map->gpio_start || gpio > map->gpio_end)
+			continue;
+
+		bit = gpio - map->gpio_start;
+		*mask = BIT(S32_PGPD_SIZE - 1 - bit);
+		*reg_offset = S32_PGPD(map->pad);
+
+		return 0;
+	}
+
+	return -EINVAL;
+}
+
+static int s32_gpio_xlate_pgpd(struct s32_pinctrl *ipctl,
+			       unsigned int pin,
+			       unsigned int *reg_offset,
+			       u16 *mask)
+{
+	/*
+	 * SIUL2_1 does not expose GPIO data registers as a linear pad sequence.
+	 * Valid PGPD offsets there correspond to PGPD7, PGPD9, PGPD10, PGPD11.
+	 */
+	if (pin >= 112)
+		return s32_gpio_pad_map_xlate(ipctl, pin, reg_offset, mask);
+
+	*mask = s32_pin2mask(pin);
+	*reg_offset = S32_PGPD(s32_pin2pad(pin));
+
+	return 0;
+}
+
+static int s32_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
+				   unsigned int base, unsigned int offset,
+				   unsigned int *reg, unsigned int *mask)
+{
+	struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio);
+	unsigned int pgpd_reg, pin, bank;
+	u16 pgpd_mask;
+	int ret;
+
+	ret = s32_gpio_get_range(ipctl, offset, &pin, &bank);
+	if (ret)
+		return ret;
+
+	switch (base) {
+	case S32_GPIO_OP_DIR:
+		/*
+		 * Direction is controlled through MSCR OBE.
+		 * Encode the real pin id in the virtual register.
+		 */
+		*reg = S32_GPIO_OP_DIR | pin;
+		*mask = S32_MSCR_OBE;
+		return 0;
+
+	case S32_GPIO_OP_DAT:
+	case S32_GPIO_OP_SET:
+		ret = s32_gpio_xlate_pgpd(ipctl, pin, &pgpd_reg, &pgpd_mask);
+		if (ret)
+			return ret;
+		/*
+		 * Encode both the GPIO bank and the real PGPD register offset.
+		 */
+		*reg = base | S32_GPIO_ENCODE(bank, pgpd_reg);
+		*mask = pgpd_mask;
+		return 0;
+	default:
+		return -EINVAL;
+	}
+}
+
+static int s32_gpio_reg_read(void *context, unsigned int reg,
+			     unsigned int *val)
+{
+	struct s32_pinctrl *ipctl = context;
+	unsigned int op = reg & S32_GPIO_OP_MASK;
+	unsigned int vreg = reg & ~S32_GPIO_OP_MASK;
+	unsigned int bank;
+	unsigned int offset;
+	struct regmap *map;
+
+	switch (op) {
+	case S32_GPIO_OP_DIR:
+		/*
+		 * Lower bits contain the real MSCR pin id.
+		 */
+		offset = S32_GPIO_DECODE_OFF(vreg);
+
+		return s32_regmap_read(ipctl->pctl, offset, val);
+
+	case S32_GPIO_OP_DAT:
+		bank = S32_GPIO_DECODE_BANK(vreg);
+		offset = S32_GPIO_DECODE_OFF(vreg);
+
+		if (bank >= ipctl->num_gpio_regmaps)
+			return -EINVAL;
+
+		map = ipctl->gpio_regmaps[bank].pgpdi;
+		if (!map)
+			return -ENODEV;
+
+		return regmap_read(map, offset, val);
+
+	case S32_GPIO_OP_SET:
+		/*
+		 * gpio-regmap uses update_bits() for set, so it needs to read
+		 * the output register before writing the updated value.
+		 */
+		bank = S32_GPIO_DECODE_BANK(vreg);
+		offset = S32_GPIO_DECODE_OFF(vreg);
+
+		if (bank >= ipctl->num_gpio_regmaps)
+			return -EINVAL;
+
+		map = ipctl->gpio_regmaps[bank].pgpdo;
+		if (!map)
+			return -ENODEV;
+
+		return regmap_read(map, offset, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static int s32_gpio_reg_write(void *context, unsigned int reg,
+			      unsigned int val)
+{
+	struct s32_pinctrl *ipctl = context;
+	unsigned int op = reg & S32_GPIO_OP_MASK;
+	unsigned int vreg = reg & ~S32_GPIO_OP_MASK;
+	unsigned int bank, offset, config;
+	struct regmap *map;
+
+	switch (op) {
+	case S32_GPIO_OP_DIR:
+		/*
+		 * gpio-regmap sets S32_MSCR_OBE for output and clears it for
+		 * input. Keep IBE enabled for GPIOs in both cases.
+		 */
+		offset = S32_GPIO_DECODE_OFF(vreg);
+
+		config = S32_MSCR_IBE;
+		if (val & S32_MSCR_OBE)
+			config |= S32_MSCR_OBE;
+
+		return s32_regmap_update(ipctl->pctl, offset,
+					 S32_MSCR_OBE | S32_MSCR_IBE,
+					 config);
+
+	case S32_GPIO_OP_SET:
+		bank = S32_GPIO_DECODE_BANK(vreg);
+		offset = S32_GPIO_DECODE_OFF(vreg);
+
+		if (bank >= ipctl->num_gpio_regmaps)
+			return -EINVAL;
+
+		map = ipctl->gpio_regmaps[bank].pgpdo;
+		if (!map)
+			return -ENODEV;
+
+		return regmap_write(map, offset, val);
+
+	default:
+		return -EINVAL;
+	}
+}
+
+static const struct regmap_bus s32_gpio_regmap_bus = {
+	.reg_read = s32_gpio_reg_read,
+	.reg_write = s32_gpio_reg_write,
+};
+
+static const struct regmap_config s32_gpio_regmap_config = {
+	.name = "s32-gpio",
+	.reg_bits = 32,
+	.val_bits = 32,
+	.reg_stride = 1,
+	.max_register = S32_GPIO_OP_SET | S32_GPIO_BANK_MASK | S32_GPIO_REG_MASK,
+	.cache_type = REGCACHE_NONE,
+};
+
+static int s32_gpio_get_ngpio(const struct s32_pinctrl_soc_data *soc_data,
+			      unsigned int *ngpio)
+{
+	const struct s32_gpio_range *range;
+	unsigned int end, max = 0;
+	int i;
+
+	if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
+		return -EINVAL;
+
+	for (i = 0; i < soc_data->num_gpio_ranges; i++) {
+		range = &soc_data->gpio_ranges[i];
+
+		if (!range->gpio_num)
+			return -EINVAL;
+
+		end = range->gpio_base + range->gpio_num;
+
+		/*
+		 * gpio_ranges must be ordered by gpio_base and must not overlap.
+		 * The GPIO line space size is derived from the highest range end.
+		 */
+		if (i > 0 && range->gpio_base < max)
+			return -EINVAL;
+
+		if (end > max)
+			max = end;
+	}
+
+	*ngpio = max;
+
+	return 0;
+}
+
+static int s32_init_gpio_regmap(struct platform_device *pdev,
+				struct s32_pinctrl *ipctl)
+{
+	ipctl->gpio_regmap =
+		devm_regmap_init(&pdev->dev, &s32_gpio_regmap_bus,
+				 ipctl, &s32_gpio_regmap_config);
+	if (IS_ERR(ipctl->gpio_regmap))
+		return dev_err_probe(&pdev->dev,
+				     PTR_ERR(ipctl->gpio_regmap),
+				     "Failed to init GPIO regmap\n");
+
+	return 0;
+}
+
+static int s32_init_valid_mask(struct gpio_chip *chip, unsigned long *mask,
+			       unsigned int ngpios)
+{
+	struct gpio_regmap *gpio = gpiochip_get_data(chip);
+	struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio);
+	unsigned int gpio_num, pin, reg_offset;
+	u16 pgpd_mask;
+	int ret;
+
+	bitmap_zero(mask, ngpios);
+
+	for (gpio_num = 0; gpio_num < ngpios; gpio_num++) {
+		ret = s32_gpio_get_range(ipctl, gpio_num, &pin, NULL);
+		if (ret)
+			continue;
+
+		ret = s32_gpio_xlate_pgpd(ipctl, pin, &reg_offset, &pgpd_mask);
+		if (ret)
+			continue;
+
+		bitmap_set(mask, gpio_num, 1);
+	}
+
+	return 0;
+}
+
+static int s32_gpio_populate_names(struct s32_pinctrl *ipctl)
+{
+	char **names;
+	unsigned int gpio;
+	unsigned int pin;
+	char port;
+	int ret;
+
+	names = devm_kcalloc(ipctl->dev, ipctl->ngpio, sizeof(*names),
+			     GFP_KERNEL);
+	if (!names)
+		return -ENOMEM;
+
+	for (gpio = 0; gpio < ipctl->ngpio; gpio++) {
+		ret = s32_gpio_get_range(ipctl, gpio, &pin, NULL);
+		if (ret)
+			continue;
+
+		port = 'A' + pin / 16;
+
+		names[gpio] = devm_kasprintf(ipctl->dev, GFP_KERNEL,
+					     "P%c_%02u", port, pin & 0xf);
+		if (!names[gpio])
+			return -ENOMEM;
+	}
+
+	ipctl->gpio_names = (const char *const *)names;
+
+	return 0;
+}
+
+static int s32_pinctrl_init_gpio_regmaps(struct platform_device *pdev,
+					 struct s32_pinctrl *ipctl)
+{
+	const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
+	static const struct regmap_config pgpd_config = {
+		.reg_bits = 32,
+		.val_bits = 16,
+		.reg_stride = 2,
+	};
+	struct regmap_config cfg;
+	struct resource *res;
+	void __iomem *base;
+	unsigned int pgpdo_idx, pgpdi_idx;
+	unsigned int i;
+
+	if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
+		return 0;
+
+	ipctl->num_gpio_regmaps = soc_data->num_gpio_ranges;
+	ipctl->gpio_regmaps = devm_kcalloc(&pdev->dev, ipctl->num_gpio_regmaps,
+					   sizeof(*ipctl->gpio_regmaps),
+					   GFP_KERNEL);
+	if (!ipctl->gpio_regmaps)
+		return -ENOMEM;
+
+	for (i = 0; i < ipctl->num_gpio_regmaps; i++) {
+		ipctl->gpio_regmaps[i].range = &soc_data->gpio_ranges[i];
+
+		/*
+		 * GPIO resources are placed after the pinctrl regions
+		 */
+		pgpdo_idx = soc_data->mem_regions + i * 2;
+		pgpdi_idx = soc_data->mem_regions + i * 2 + 1;
+
+		/* PGPDO */
+		res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdo_idx);
+		if (!res)
+			return dev_err_probe(&pdev->dev, -ENOENT,
+						 "Missing PGPDO resource %u\n", i);
+
+		base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(base))
+			return PTR_ERR(base);
+
+		cfg = pgpd_config;
+		cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdo%u", i);
+		if (!cfg.name)
+			return -ENOMEM;
+
+		cfg.max_register = resource_size(res) - cfg.reg_stride;
+
+		ipctl->gpio_regmaps[i].pgpdo =
+			devm_regmap_init_mmio(&pdev->dev, base, &cfg);
+		if (IS_ERR(ipctl->gpio_regmaps[i].pgpdo))
+			return dev_err_probe(&pdev->dev,
+						 PTR_ERR(ipctl->gpio_regmaps[i].pgpdo),
+						 "Failed to init PGPDO regmap %u\n", i);
+
+		/* PGPDI */
+		res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdi_idx);
+		if (!res)
+			return dev_err_probe(&pdev->dev, -ENOENT,
+						 "Missing PGPDI resource %u\n", i);
+
+		base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(base))
+			return PTR_ERR(base);
+
+		cfg = pgpd_config;
+		cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdi%u", i);
+		if (!cfg.name)
+			return -ENOMEM;
+
+		cfg.max_register = resource_size(res) - cfg.reg_stride;
+
+		ipctl->gpio_regmaps[i].pgpdi =
+			devm_regmap_init_mmio(&pdev->dev, base, &cfg);
+		if (IS_ERR(ipctl->gpio_regmaps[i].pgpdi))
+			return dev_err_probe(&pdev->dev,
+						 PTR_ERR(ipctl->gpio_regmaps[i].pgpdi),
+						 "Failed to init PGPDI regmap %u\n", i);
+	}
+
+	return 0;
+}
+
 #ifdef CONFIG_PM_SLEEP
 static bool s32_pinctrl_should_save(struct s32_pinctrl *ipctl,
 				    unsigned int pin)
@@ -710,7 +1207,6 @@ int s32_pinctrl_suspend(struct device *dev)
 	const struct s32_pinctrl_soc_info *info = ipctl->info;
 	struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
 	int i;
-	int ret;
 	unsigned int config;
 
 	for (i = 0; i < info->soc_data->npins; i++) {
@@ -719,9 +1215,7 @@ int s32_pinctrl_suspend(struct device *dev)
 		if (!s32_pinctrl_should_save(ipctl, pin->number))
 			continue;
 
-		ret = s32_regmap_read(ipctl->pctl, pin->number, &config);
-		if (ret)
-			return -EINVAL;
+		s32_regmap_read(ipctl->pctl, pin->number, &config);
 
 		saved_context->pads[i] = config;
 	}
@@ -736,7 +1230,7 @@ int s32_pinctrl_resume(struct device *dev)
 	const struct s32_pinctrl_soc_info *info = ipctl->info;
 	const struct pinctrl_pin_desc *pin;
 	struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
-	int ret, i;
+	int i;
 
 	for (i = 0; i < info->soc_data->npins; i++) {
 		pin = &info->soc_data->pins[i];
@@ -744,10 +1238,8 @@ int s32_pinctrl_resume(struct device *dev)
 		if (!s32_pinctrl_should_save(ipctl, pin->number))
 			continue;
 
-		ret = s32_regmap_write(ipctl->pctl, pin->number,
-					 saved_context->pads[i]);
-		if (ret)
-			return ret;
+		s32_regmap_write(ipctl->pctl, pin->number,
+				 saved_context->pads[i]);
 	}
 
 	return 0;
@@ -927,13 +1419,15 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev,
 int s32_pinctrl_probe(struct platform_device *pdev,
 		      const struct s32_pinctrl_soc_data *soc_data)
 {
-	struct s32_pinctrl *ipctl;
-	int ret;
-	struct pinctrl_desc *s32_pinctrl_desc;
-	struct s32_pinctrl_soc_info *info;
 #ifdef CONFIG_PM_SLEEP
 	struct s32_pinctrl_context *saved_context;
 #endif
+	struct gpio_regmap_config gpio_cfg = {};
+	struct pinctrl_desc *s32_pinctrl_desc;
+	struct s32_pinctrl_soc_info *info;
+	struct s32_pinctrl *ipctl;
+	unsigned int ngpio;
+	int ret;
 
 	if (!soc_data || !soc_data->pins || !soc_data->npins) {
 		dev_err(&pdev->dev, "wrong pinctrl info\n");
@@ -959,6 +1453,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 	INIT_LIST_HEAD(&ipctl->gpio_configs);
 	spin_lock_init(&ipctl->gpio_configs_lock);
 
+	ret = devm_add_action_or_reset(&pdev->dev,
+				       s32_gpio_free_saved_configs, ipctl);
+	if (ret)
+		return ret;
+
 	s32_pinctrl_desc =
 		devm_kzalloc(&pdev->dev, sizeof(*s32_pinctrl_desc), GFP_KERNEL);
 	if (!s32_pinctrl_desc)
@@ -978,6 +1477,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 		return ret;
 	}
 
+	ret = s32_pinctrl_init_gpio_regmaps(pdev, ipctl);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret,
+				     "Failed to init GPIO regmaps\n");
+
 	ret = devm_pinctrl_register_and_init(&pdev->dev, s32_pinctrl_desc,
 					     ipctl, &ipctl->pctl);
 	if (ret)
@@ -999,7 +1503,42 @@ int s32_pinctrl_probe(struct platform_device *pdev,
 		return dev_err_probe(&pdev->dev, ret,
 				     "Failed to enable pinctrl\n");
 
-	dev_info(&pdev->dev, "Initialized S32 pinctrl driver\n");
+	/* Setup GPIO if GPIO ranges are defined */
+	if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
+		return 0;
+
+	ret = s32_gpio_get_ngpio(soc_data, &ngpio);
+	if (ret)
+		return dev_err_probe(&pdev->dev, ret, "Invalid GPIO ranges\n");
+
+	ipctl->ngpio = ngpio;
+
+	ret = s32_gpio_populate_names(ipctl);
+	if (ret)
+		return ret;
+
+	ret = s32_init_gpio_regmap(pdev, ipctl);
+	if (ret)
+		return ret;
+
+	gpio_cfg.parent = &pdev->dev;
+	gpio_cfg.fwnode = dev_fwnode(&pdev->dev);
+	gpio_cfg.label = dev_name(&pdev->dev);
+	gpio_cfg.regmap = ipctl->gpio_regmap;
+	gpio_cfg.ngpio = ngpio;
+	gpio_cfg.names = ipctl->gpio_names;
+	gpio_cfg.reg_dir_out_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DIR);
+	gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DAT);
+	gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_SET);
+	gpio_cfg.reg_mask_xlate = s32_gpio_reg_mask_xlate;
+	gpio_cfg.init_valid_mask = s32_init_valid_mask;
+	gpio_cfg.drvdata = ipctl;
+
+	ipctl->gpio_rgm = devm_gpio_regmap_register(&pdev->dev, &gpio_cfg);
+	if (IS_ERR(ipctl->gpio_rgm))
+		return dev_err_probe(&pdev->dev,
+				     PTR_ERR(ipctl->gpio_rgm),
+				     "Unable to add gpio_regmap chip\n");
 
 	return 0;
 }
diff --git a/drivers/pinctrl/nxp/pinctrl-s32g2.c b/drivers/pinctrl/nxp/pinctrl-s32g2.c
index c49d28793b69..0bd6e6ab5ad1 100644
--- a/drivers/pinctrl/nxp/pinctrl-s32g2.c
+++ b/drivers/pinctrl/nxp/pinctrl-s32g2.c
@@ -3,7 +3,7 @@
  * NXP S32G pinctrl driver
  *
  * Copyright 2015-2016 Freescale Semiconductor, Inc.
- * Copyright 2017-2018, 2020-2022 NXP
+ * Copyright 2017-2018, 2020-2022, 2025-2026 NXP
  * Copyright (C) 2022 SUSE LLC
  */
 
@@ -773,17 +773,47 @@ static const struct s32_pin_range s32_pin_ranges_siul2[] = {
 	S32_PIN_RANGE(942, 1007),
 };
 
-static const struct s32_pinctrl_soc_data s32_pinctrl_data = {
+static const struct s32_gpio_range s32_gpio_ranges_siul2[] = {
+	S32_GPIO_RANGE(0, 0, 102),
+	S32_GPIO_RANGE(112, 112, 79),
+};
+
+/*
+ * SIUL2_1 GPIO ranges mapped to sparse PGPD pads.
+ *
+ * SIUL2_1 does not expose GPIO data registers as a linear pad
+ * sequence. Each entry describes a contiguous GPIO offset range
+ * and the PGPD pad servicing that range.
+ */
+static const struct s32_gpio_pad_map s32g_gpio_pad_maps[] = {
+	{ 112, 122, 7  }, /* PH_00 .. PH_10 -> PGPD7  */
+	{ 144, 159, 9  }, /* PJ_00 .. PJ_15 -> PGPD9  */
+	{ 160, 175, 10 }, /* PK_00 .. PK_15 -> PGPD10 */
+	{ 176, 190, 11 }, /* PL_00 .. PL_14 -> PGPD11 */
+};
+
+/* Legacy data for old DT bindings without GPIO support */
+static const struct s32_pinctrl_soc_data legacy_s32g_pinctrl_data = {
+	.pins = s32_pinctrl_pads_siul2,
+	.npins = ARRAY_SIZE(s32_pinctrl_pads_siul2),
+	.mem_pin_ranges = s32_pin_ranges_siul2,
+	.mem_regions = ARRAY_SIZE(s32_pin_ranges_siul2),
+};
+
+static const struct s32_pinctrl_soc_data s32g_pinctrl_data = {
 	.pins = s32_pinctrl_pads_siul2,
 	.npins = ARRAY_SIZE(s32_pinctrl_pads_siul2),
 	.mem_pin_ranges = s32_pin_ranges_siul2,
 	.mem_regions = ARRAY_SIZE(s32_pin_ranges_siul2),
+	.gpio_ranges = s32_gpio_ranges_siul2,
+	.num_gpio_ranges = ARRAY_SIZE(s32_gpio_ranges_siul2),
+	.gpio_pad_maps = s32g_gpio_pad_maps,
+	.num_gpio_pad_maps = ARRAY_SIZE(s32g_gpio_pad_maps),
 };
 
 static const struct of_device_id s32_pinctrl_of_match[] = {
 	{
 		.compatible = "nxp,s32g2-siul2-pinctrl",
-		.data = &s32_pinctrl_data,
 	},
 	{ /* sentinel */ }
 };
@@ -792,8 +822,16 @@ MODULE_DEVICE_TABLE(of, s32_pinctrl_of_match);
 static int s32g_pinctrl_probe(struct platform_device *pdev)
 {
 	const struct s32_pinctrl_soc_data *soc_data;
+	struct device_node *np = pdev->dev.of_node;
 
-	soc_data = of_device_get_match_data(&pdev->dev);
+	/*
+	 * Legacy DTs only describe the pinctrl resources.
+	 * New DT changes extend the same node with GPIO resources.
+	 */
+	if (of_property_present(np, "gpio-controller"))
+		soc_data = &s32g_pinctrl_data;
+	else
+		soc_data = &legacy_s32g_pinctrl_data;
 
 	return s32_pinctrl_probe(pdev, soc_data);
 }
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node
  2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
                   ` (4 preceding siblings ...)
  2026-06-02  8:01 ` [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu
@ 2026-06-02  8:01 ` Khristine Andreea Barbulescu
  2026-06-02  8:38   ` sashiko-bot
  5 siblings, 1 reply; 12+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-02  8:01 UTC (permalink / raw)
  To: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla
  Cc: Alberto Ruiz, Christophe Lizzi, devicetree, Enric Balletbo,
	Eric Chanudet, imx, linux-arm-kernel, linux-gpio, linux-kernel,
	NXP S32 Linux Team, Pengutronix Kernel Team, Vincent Guittot

Update the SIUL2 pinctrl node to describe the additional register
ranges and DT properties used by the updated SIUL2 driver.

Besides the MSCR and IMCR ranges used for pinmux and pin
configuration, the SIUL2 block also provides PGPDO and
PGPDI registers for GPIO output and input operations,
as well as an EIRQ register window for external interrupt configuration.

The driver supports both legacy pinctrl-only DTs and
extended DTs with GPIO and IRQ.

Reflect these resources in the SIUL2 pinctrl node by adding:
  - the PGPDO and PGPDI register ranges
  - the EIRQ register range
  - gpio-controller, #gpio-cells and gpio-ranges
  - interrupt-controller, #interrupt-cells and interrupts

Keep the hardware description aligned with the updated SIUL2
driver, where pinctrl, GPIO data access and the EIRQ register
block are described under the same device node.

Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
---
 arch/arm64/boot/dts/freescale/s32g2.dtsi | 23 +++++++++++++++++++++--
 arch/arm64/boot/dts/freescale/s32g3.dtsi | 23 +++++++++++++++++++++--
 2 files changed, 42 insertions(+), 4 deletions(-)

diff --git a/arch/arm64/boot/dts/freescale/s32g2.dtsi b/arch/arm64/boot/dts/freescale/s32g2.dtsi
index 51d00dac12de..f21c8d19bcfe 100644
--- a/arch/arm64/boot/dts/freescale/s32g2.dtsi
+++ b/arch/arm64/boot/dts/freescale/s32g2.dtsi
@@ -3,7 +3,7 @@
  * NXP S32G2 SoC family
  *
  * Copyright (c) 2021 SUSE LLC
- * Copyright 2017-2021, 2024-2025 NXP
+ * Copyright 2017-2021, 2024-2026 NXP
  */
 
 #include <dt-bindings/interrupt-controller/arm-gic.h>
@@ -135,7 +135,26 @@ pinctrl: pinctrl@4009c240 {
 				/* IMCR119-IMCR397 registers on siul2_1 */
 			      <0x44010c1c 0x45c>,
 				/* IMCR430-IMCR495 registers on siul2_1 */
-			      <0x440110f8 0x108>;
+			      <0x440110f8 0x108>,
+				/* PGPDO registers on siul2_0 */
+			      <0x4009d700 0x10>,
+				/* PGPDI registers on siul2_0 */
+			      <0x4009d740 0x10>,
+				/* PGPDO registers on siul2_1 */
+			      <0x44011700 0x18>,
+				/* PGPDI registers on siul2_1 */
+			      <0x44011740 0x18>,
+				/* EIRQ window: DISR0..IFEER0 */
+			      <0x44010010 0x34>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-ranges = <&pinctrl 0 0 102>,
+					<&pinctrl 112 112 79>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+			interrupts = <GIC_SPI 210 IRQ_TYPE_LEVEL_HIGH>;
 
 			jtag_pins: jtag-pins {
 				jtag-grp0 {
diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi
index e314f3c7d61d..f5825881fd69 100644
--- a/arch/arm64/boot/dts/freescale/s32g3.dtsi
+++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi
@@ -1,6 +1,6 @@
 // SPDX-License-Identifier: (GPL-2.0+ OR BSD-3-Clause)
 /*
- * Copyright 2021-2025 NXP
+ * Copyright 2021-2026 NXP
  *
  * Authors: Ghennadi Procopciuc <ghennadi.procopciuc@nxp.com>
  *          Ciprian Costea <ciprianmarian.costea@nxp.com>
@@ -193,7 +193,26 @@ pinctrl: pinctrl@4009c240 {
 				/* IMCR119-IMCR397 registers on siul2_1 */
 			      <0x44010c1c 0x45c>,
 				/* IMCR430-IMCR495 registers on siul2_1 */
-			      <0x440110f8 0x108>;
+			      <0x440110f8 0x108>,
+				/* PGPDO registers on siul2_0 */
+			      <0x4009d700 0x10>,
+				/* PGPDI registers on siul2_0 */
+			      <0x4009d740 0x10>,
+				/* PGPDO registers on siul2_1 */
+			      <0x44011700 0x18>,
+				/* PGPDI registers on siul2_1 */
+			      <0x44011740 0x18>,
+				/* EIRQ window: DISR0..IFEER0 */
+			      <0x44010010 0x34>;
+
+			gpio-controller;
+			#gpio-cells = <2>;
+			gpio-ranges = <&pinctrl 0 0 102>,
+					<&pinctrl 112 112 79>;
+
+			interrupt-controller;
+			#interrupt-cells = <2>;
+			interrupts = <GIC_SPI 210 IRQ_TYPE_LEVEL_HIGH>;
 
 			jtag_pins: jtag-pins {
 				jtag-grp0 {
-- 
2.34.1


^ permalink raw reply related	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments
  2026-06-02  8:01 ` [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
@ 2026-06-02  8:06   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-02  8:06 UTC (permalink / raw)
  To: Khristine Andreea Barbulescu; +Cc: conor+dt, Frank.Li, imx, devicetree, robh

Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider:
- [Low] Typo in the newly added documentation for `struct s32_pinctrl_context`.
- [Low] Invalid kernel-doc comment block syntax.
- [Low] Inconsistent error message capitalization.
--

commit a7822dc8d9c7d5010c39b80aee179b5ec0735e03
Author: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>

pinctrl: s32cc: add/fix some comments

Add/fix some comments and print statements.

> diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
> index fe7cd641fddd0..4f88c24e62a21 100644
> --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
> +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
> @@ -60,6 +60,12 @@ static u32 get_pin_func(u32 pinmux)
>  	return pinmux & GENMASK(3, 0);
>  }
>  
> +/*

[Severity: Low]
This isn't a bug, but should this start with /** instead of /*?

The newly added comment for struct s32_pinctrl_mem_region follows the
kernel-doc format, but because it begins with /* rather than /**, the
kernel-doc scripts will likely ignore it.

> + * struct s32_pinctrl_mem_region - memory region for a set of SIUL2 registers
> + * @map: regmap used for this range
> + * @pin_range: the pins controlled by these registers
> + * @name: name of the current range
> + */
>  struct s32_pinctrl_mem_region {

[ ... ]

> @@ -67,7 +73,7 @@ struct s32_pinctrl_mem_region {
>  };
>  
>  /*
> - * Holds pin configuration for GPIO's.
> + * struct gpio_pin_config - holds pin configuration for GPIO's

[Severity: Low]
This isn't a bug, but this comment also follows the kernel-doc format
without starting with /**. Should it be updated to /** so it can be picked up
by the documentation tools?

>   * @pin_id: Pin ID for this GPIO
>   * @config: Pin settings
>   * @list: Linked list entry for each gpio pin

[ ... ]

> @@ -79,20 +85,22 @@ struct gpio_pin_config {
>  };
>  
>  /*
> - * Pad config save/restore for power suspend/resume.
> + * struct s32_pinctrl_context - pad config save/restore for suspend/resume
> + * @pads: saved values for the pards

[Severity: Low]
This isn't a bug, but there's a typo here (pards instead of pads).

Additionally, this comment block also lacks the /** prefix required for
kernel-doc.

>   */
>  struct s32_pinctrl_context {
>  	unsigned int *pads;
>  };
>  
>  /*
> + * struct s32_pinctrl - private driver data
>   * @dev: a pointer back to containing device

[Severity: Low]
This isn't a bug, but this comment block is also missing the /** prefix.

>   * @pctl: a pointer to the pinctrl device structure
>   * @regions: reserved memory regions with start/end pin

[ ... ]

> @@ -974,7 +982,7 @@ int s32_pinctrl_probe(struct platform_device *pdev,
>  					    ipctl);
>  	if (IS_ERR(ipctl->pctl))
>  		return dev_err_probe(&pdev->dev, PTR_ERR(ipctl->pctl),
> -				     "could not register s32 pinctrl driver\n");
> +				     "Could not register s32 pinctrl driver\n");

[Severity: Low]
This isn't a bug, but does changing "could" to "Could" break consistency
with the rest of this file?

Other error messages in s32_pinctrl_probe() start with lowercase letters
(e.g., "wrong pinctrl info", "fail to probe dt properties"). Should this be
kept lowercase to match the rest of the file and standard kernel practice?

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602080132.3256239-1-khristineandreea.barbulescu@oss.nxp.com?part=1

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources
  2026-06-02  8:01 ` [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu
@ 2026-06-02  8:21   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-02  8:21 UTC (permalink / raw)
  To: Khristine Andreea Barbulescu; +Cc: devicetree, robh, imx, Frank.Li, conor+dt

Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider:
- [Medium] The legacy fallback branch in the `oneOf` block fails to explicitly forbid newly added capability properties (`gpio-controller`, `interrupt-controller`), creating a schema validation loophole.
- [Low] The device tree example will fail `dt_binding_check` compilation due to an unresolved phandle reference to the undeclared label `&pinctrl`.
--

commit 055c2d417b67cea0bcdcafe1444e526610028d07
Author: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>

dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources

This commit extends the S32G2 SIUL2 pinctrl binding to describe GPIO data
and external interrupt resources. It adds GPIO and interrupt controller
properties and expands the reg property ranges. It also provides a oneOf
construct to support legacy and new node layouts.

> diff --git a/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
> index a24286e4def62..e4cc1a3a795c8 100644
> --- a/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
> +++ b/Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml
[ ... ]
> @@ -86,10 +118,32 @@ required:
>    - compatible
>    - reg
>  
> +oneOf:
> +  - description: Legacy pinctrl-only node
> +    properties:
> +      reg:
> +        minItems: 6
> +        maxItems: 6

[Severity: Medium]
Since gpio-controller and interrupt-controller are allowed at the top level,
does this legacy branch need to explicitly forbid them?

Without setting gpio-controller: false and interrupt-controller: false here,
could a node matching this legacy branch declare these capabilities without
providing the 11 reg items and gpio-ranges required to actually use them,
thereby bypassing schema validation?

> +
> +  - description: Pinctrl node with GPIO and external interrupt support
> +    required:
> +      - gpio-controller
> +      - "#gpio-cells"
> +      - gpio-ranges
> +      - interrupt-controller
> +      - "#interrupt-cells"
> +      - interrupts
> +    properties:
> +      reg:
> +        minItems: 11
> +        maxItems: 11
> +
>  additionalProperties: false
>  
>  examples:
>    - |
> +    #include <dt-bindings/interrupt-controller/arm-gic.h>
> +
>      pinctrl@4009c240 {
>          compatible = "nxp,s32g2-siul2-pinctrl";

[Severity: Low]
Does this node need a pinctrl: label?

The gpio-ranges property added below attempts to reference &pinctrl, which
will cause dt_binding_check to fail with a 'reference to non-existent node
or label' error.

[ ... ]
>                /* IMCR119-IMCR397 registers on siul2_1 */
>                <0x44010c1c 0x45c>,
>                /* IMCR430-IMCR495 registers on siul2_1 */
> -              <0x440110f8 0x108>;
> +              <0x440110f8 0x108>,
> +              /* PGPDO registers on siul2_0 */
> +              <0x4009d700 0x10>,
> +              /* PGPDI registers on siul2_0 */
> +              <0x4009d740 0x10>,
> +              /* PGPDO registers on siul2_1 */
> +              <0x44011700 0x18>,
> +              /* PGPDI registers on siul2_1 */
> +              <0x44011740 0x18>,
> +              /* EIRQ registers on siul2_1 */
> +              <0x44010010 0x34>;
> +
> +        gpio-controller;
> +        #gpio-cells = <2>;
> +        gpio-ranges = <&pinctrl 0 0 102>,
> +                      <&pinctrl 112 112 79>;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602080132.3256239-1-khristineandreea.barbulescu@oss.nxp.com?part=4

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality
  2026-06-02  8:01 ` [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu
@ 2026-06-02  8:31   ` sashiko-bot
  2026-06-02  9:26   ` Enric Balletbo i Serra
  1 sibling, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-02  8:31 UTC (permalink / raw)
  To: Khristine Andreea Barbulescu; +Cc: robh, conor+dt, imx, devicetree, Frank.Li

Thank you for your contribution! Sashiko AI review found 4 potential issue(s) to consider:
- [High] Removal of error checks for `s32_regmap_read()` causes uninitialized kernel stack memory to be saved during suspend and printed via debugfs.
- [High] The driver does not save and restore the PGPDO (Parallel GPIO Pad Data Out) registers across suspend and resume, causing output GPIOs to lose their state.
- [Medium] `PINCTRL_S32CC` selects `GPIO_REGMAP` without fulfilling its dependency on `GPIOLIB`, potentially causing build failures.
- [Medium] The virtual `s32-gpio` regmap forces GPIO operations to be sleepable, preventing usage from interrupt handlers.
--

commit 065b0eb13249298984a661238d8ddad54d7f31a4
Author: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
Subject: pinctrl: s32cc: implement GPIO functionality

The patch updates the S32CC SIUL2 driver to operate as a monolithic
pinctrl/GPIO driver. It adds GPIO data access and direction handling using
the gpio-regmap library backed by a virtual regmap. Support is also added
for both legacy pinctrl-only and extended bindings.

Link: https://lore.kernel.org/linux-gpio/20260120115923.3463866-4-khristineandreea.barbulescu@oss.nxp.com/T/#m543c9edbdde74bdc68b6a2364e8b975356c33043
Link: https://lore.kernel.org/all/20260504131148.3622697-7-khristineandreea.barbulescu@oss.nxp.com/

> diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
> index abca7ef97003d..59fc6adf5b0bb 100644
> --- a/drivers/pinctrl/nxp/Kconfig
> +++ b/drivers/pinctrl/nxp/Kconfig
> @@ -5,6 +5,7 @@ config PINCTRL_S32CC
>  	select GENERIC_PINCTRL_GROUPS
>  	select GENERIC_PINMUX_FUNCTIONS
>  	select GENERIC_PINCONF
> +	select GPIO_REGMAP
>  	select REGMAP_MMIO

[Severity: Medium]
Does PINCTRL_S32CC also need to select or depend on GPIOLIB here?

Since GPIO_REGMAP is enclosed in an "if GPIOLIB" block in drivers/gpio/Kconfig,
it implicitly depends on GPIOLIB. If a user disables GPIOLIB, Kconfig will
report an unmet direct dependency warning. 

Because drivers/gpio/Makefile is gated by CONFIG_GPIOLIB, gpio-regmap.c
won't compile in that case, potentially leading to linker errors for
devm_gpio_regmap_register() in the pinctrl driver.

> diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
> index 89a4eb2000ee7..8843926345ec8 100644
> --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
> +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
[ ... ]
> @@ -662,15 +717,13 @@ static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
>  	struct s32_pin_group *grp;
>  	unsigned int config;
>  	const char *name;
> -	int i, ret;
> +	int i;
>  
>  	seq_puts(s, "\n");
>  	grp = &info->groups[selector];
>  	for (i = 0; i < grp->data.npins; i++) {
>  		name = pin_get_name(pctldev, grp->data.pins[i]);
> -		ret = s32_regmap_read(pctldev, grp->data.pins[i], &config);
> -		if (ret)
> -			return;
> +		s32_regmap_read(pctldev, grp->data.pins[i], &config);
>  		seq_printf(s, "%s: 0x%x\n", name, config);
>  	}
>  }

[Severity: High]
Is it safe to remove the return value check for s32_regmap_read() here?

If s32_regmap_read() fails, it returns a negative error code and leaves
config unmodified. Because config is an uninitialized local variable, this
could leak kernel stack memory to userspace via seq_printf().

[ ... ]
> +static const struct regmap_config s32_gpio_regmap_config = {
> +	.name = "s32-gpio",
> +	.reg_bits = 32,
> +	.val_bits = 32,
> +	.reg_stride = 1,
> +	.max_register = S32_GPIO_OP_SET | S32_GPIO_BANK_MASK | S32_GPIO_REG_MASK,
> +	.cache_type = REGCACHE_NONE,
> +};

[Severity: Medium]
Should this virtual regmap configuration specify .disable_locking = true?

Without .fast_io = true or .disable_locking = true, devm_regmap_init()
protects the map with a mutex. When gpio-regmap registers this, it detects
the mutex and unconditionally sets chip->can_sleep = true.

Consumers typically expect an on-SoC MMIO GPIO controller to be fast and
non-sleeping for use in interrupt contexts. Using a mutex breaks this
assumption and might trigger might_sleep() warnings.

[ ... ]
> @@ -710,7 +1207,6 @@ int s32_pinctrl_suspend(struct device *dev)
>  	const struct s32_pinctrl_soc_info *info = ipctl->info;
>  	struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
>  	int i;
> -	int ret;
>  	unsigned int config;
>  
>  	for (i = 0; i < info->soc_data->npins; i++) {
> @@ -719,9 +1215,7 @@ int s32_pinctrl_suspend(struct device *dev)
>  		if (!s32_pinctrl_should_save(ipctl, pin->number))
>  			continue;
>  
> -		ret = s32_regmap_read(ipctl->pctl, pin->number, &config);
> -		if (ret)
> -			return -EINVAL;
> +		s32_regmap_read(ipctl->pctl, pin->number, &config);
>  
>  		saved_context->pads[i] = config;
>  	}

[Severity: High]
Can this save uninitialized stack garbage into the suspend context?

By removing the error check, if s32_regmap_read() fails, the uninitialized
config variable is stored in saved_context->pads[i] and will be written
back to the hardware during system resume.

[Severity: High]
Does this suspend/resume implementation also need to save and restore the
PGPDO (Parallel GPIO Pad Data Out) registers?

Currently, only the MSCR configuration (direction/pull state) via
mem_pin_ranges appears to be saved. If power is lost during suspend, the
PGPDO registers will reset to 0. Any GPIO configured as an output and driven
HIGH before suspend might glitch LOW upon resume since its output line
value is lost.

> @@ -736,7 +1230,7 @@ int s32_pinctrl_resume(struct device *dev)
>  	const struct s32_pinctrl_soc_info *info = ipctl->info;
>  	const struct pinctrl_pin_desc *pin;
>  	struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
> -	int ret, i;
> +	int i;
>  
>  	for (i = 0; i < info->soc_data->npins; i++) {
>  		pin = &info->soc_data->pins[i];
> @@ -744,10 +1238,8 @@ int s32_pinctrl_resume(struct device *dev)
>  		if (!s32_pinctrl_should_save(ipctl, pin->number))
>  			continue;
>  
> -		ret = s32_regmap_write(ipctl->pctl, pin->number,
> -					 saved_context->pads[i]);
> -		if (ret)
> -			return ret;
> +		s32_regmap_write(ipctl->pctl, pin->number,
> +				 saved_context->pads[i]);
>  	}
>  
>  	return 0;

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602080132.3256239-1-khristineandreea.barbulescu@oss.nxp.com?part=5

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node
  2026-06-02  8:01 ` [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu
@ 2026-06-02  8:38   ` sashiko-bot
  0 siblings, 0 replies; 12+ messages in thread
From: sashiko-bot @ 2026-06-02  8:38 UTC (permalink / raw)
  To: Khristine Andreea Barbulescu; +Cc: robh, imx, Frank.Li, conor+dt, devicetree

Thank you for your contribution! Sashiko AI review found 1 potential issue(s) to consider:

Pre-existing issues:
- [High] The `reg` property for the `swt8` watchdog device is missing the `0x` prefix for its base address.
--

commit 1403ffa829f6f3074ef410621ae401b08c310e0a
Author: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>

arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node

This commit updates the SIUL2 pinctrl nodes in the s32g2 and s32g3 device
trees to describe GPIO and EIRQ registers. It adds the PGPDO, PGPDI, and
EIRQ register windows along with the corresponding controller properties.

> diff --git a/arch/arm64/boot/dts/freescale/s32g3.dtsi b/arch/arm64/boot/dts/freescale/s32g3.dtsi
> --- a/arch/arm64/boot/dts/freescale/s32g3.dtsi
> +++ b/arch/arm64/boot/dts/freescale/s32g3.dtsi

[ ... ]

> 		swt8: watchdog@40500000 {
> 			compatible = "nxp,s32g3-swt", "nxp,s32g2-swt";
> 			reg = <40500000 0x1000>;

[Severity: High]
This isn't a bug introduced by this patch, but while reviewing the s32g3
device tree changes, I noticed this pre-existing issue in the swt8 node.

Is it intentional that the reg property uses a decimal base address instead
of a hexadecimal one?

Since there is no 0x prefix, 40500000 is evaluated as decimal, which is
0x269FEC0 in hexadecimal. This does not match the intended hardware
address of 0x40500000 specified in the unit address.

If a board enables this watchdog device, could the driver attempt to
ioremap the wrong physical memory address and trigger a synchronous
external abort?

> 			clocks = <&clks 0x3a>, <&clks 0x3b>, <&clks 0x3b>;
> 			clock-names = "counter", "module", "register";
> 			status = "disabled";
> 		};

-- 
Sashiko AI review · https://sashiko.dev/#/patchset/20260602080132.3256239-1-khristineandreea.barbulescu@oss.nxp.com?part=6

^ permalink raw reply	[flat|nested] 12+ messages in thread

* Re: [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality
  2026-06-02  8:01 ` [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu
  2026-06-02  8:31   ` sashiko-bot
@ 2026-06-02  9:26   ` Enric Balletbo i Serra
  1 sibling, 0 replies; 12+ messages in thread
From: Enric Balletbo i Serra @ 2026-06-02  9:26 UTC (permalink / raw)
  To: Khristine Andreea Barbulescu
  Cc: Linus Walleij, Bartosz Golaszewski, Rob Herring,
	Krzysztof Kozlowski, Conor Dooley, Chester Lin, Matthias Brugger,
	Ghennadi Procopciuc, Larisa Grigore, Lee Jones, Shawn Guo,
	Sascha Hauer, Fabio Estevam, Dong Aisheng, Jacky Bai,
	Greg Kroah-Hartman, Rafael J. Wysocki, Srinivas Kandagatla,
	Alberto Ruiz, Christophe Lizzi, devicetree, Eric Chanudet, imx,
	linux-arm-kernel, linux-gpio, linux-kernel, NXP S32 Linux Team,
	Pengutronix Kernel Team, Vincent Guittot

Hi Khristine,

Thank you for the patch. I got some checkpatch warnings, could you
take a look? And some minor comments below.

On Tue, Jun 2, 2026 at 10:02 AM Khristine Andreea Barbulescu
<khristineandreea.barbulescu@oss.nxp.com> wrote:
>
> From: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
>
> The updated SIUL2 block groups pinctrl, GPIO data access
> and interrupt control within the same hardware unit.
> The SIUL2 driver is therefore structured as a monolithic
> pinctrl/GPIO driver.
>
> GPIO data access and direction handling are implemented using the
> gpio-regmap library backed by a virtual regmap. The virtual regmap
> translates the gpio-regmap register model to the underlying SIUL2
> registers: MSCR for direction, PGPDI for input values and PGPDO for
> output values.
>
> The existing pinctrl GPIO callbacks are used for the request/free path:
> they switch the pad to GPIO mode on request and restore the previous
> MSCR configuration when the GPIO is released.
>
> This change came as a result of upstream review in the
> following series:
> https://lore.kernel.org/linux-gpio/20260120115923.3463866-4-khristineandreea.barbulescu@oss.nxp.com/T/#m543c9edbdde74bdc68b6a2364e8b975356c33043
> https://lore.kernel.org/all/20260504131148.3622697-7-khristineandreea.barbulescu@oss.nxp.com/
>
> Support both SIUL2 DT layouts:
> - legacy pinctrl-only binding
> - extended pinctrl/GPIO/irqchip binding
>
> Signed-off-by: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com>
> Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com>
> ---
>  drivers/pinctrl/nxp/Kconfig         |   1 +
>  drivers/pinctrl/nxp/pinctrl-s32.h   |  32 +-
>  drivers/pinctrl/nxp/pinctrl-s32cc.c | 685 +++++++++++++++++++++++++---
>  drivers/pinctrl/nxp/pinctrl-s32g2.c |  46 +-
>  4 files changed, 686 insertions(+), 78 deletions(-)
>
> diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
> index abca7ef97003..59fc6adf5b0b 100644
> --- a/drivers/pinctrl/nxp/Kconfig
> +++ b/drivers/pinctrl/nxp/Kconfig
> @@ -5,6 +5,7 @@ config PINCTRL_S32CC
>         select GENERIC_PINCTRL_GROUPS
>         select GENERIC_PINMUX_FUNCTIONS
>         select GENERIC_PINCONF
> +       select GPIO_REGMAP
>         select REGMAP_MMIO
>
>  config PINCTRL_S32G2
> diff --git a/drivers/pinctrl/nxp/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h
> index 8715befd5f05..c2fc5eda7eb4 100644
> --- a/drivers/pinctrl/nxp/pinctrl-s32.h
> +++ b/drivers/pinctrl/nxp/pinctrl-s32.h
> @@ -2,7 +2,7 @@
>   *
>   * S32 pinmux core definitions
>   *
> - * Copyright 2016-2020, 2022 NXP
> + * Copyright 2016-2020, 2022, 2026 NXP
>   * Copyright (C) 2022 SUSE LLC
>   * Copyright 2015-2016 Freescale Semiconductor, Inc.
>   * Copyright (C) 2012 Linaro Ltd.
> @@ -34,11 +34,39 @@ struct s32_pin_range {
>         unsigned int end;
>  };
>
> +/**
> + * struct s32_gpio_range - contiguous GPIO pin range within a SIUL2 module
> + * @gpio_base: first GPIO line offset in the GPIO range
> + * @pin_base: first pinctrl pin number mapped by this GPIO range
> + * @gpio_num: number of consecutive GPIO pins in the range
> + */
> +struct s32_gpio_range {
> +       unsigned int gpio_base;
> +       unsigned int pin_base;
> +       unsigned int gpio_num;
> +};
> +
> +/**
> + * struct s32_gpio_pad_map - mapping between GPIO ranges and PGPD pads
> + * @gpio_start: first GPIO line offset in the range
> + * @gpio_end: last GPIO line offset in the range
> + * @pad: PGPD pad number serving the range
> + */
> +struct s32_gpio_pad_map {
> +       unsigned int gpio_start;
> +       unsigned int gpio_end;
> +       unsigned int pad;
> +};
> +
>  struct s32_pinctrl_soc_data {
>         const struct pinctrl_pin_desc *pins;
>         unsigned int npins;
>         const struct s32_pin_range *mem_pin_ranges;
>         unsigned int mem_regions;
> +       const struct s32_gpio_range *gpio_ranges;
> +       unsigned int num_gpio_ranges;
> +       const struct s32_gpio_pad_map *gpio_pad_maps;
> +       unsigned int num_gpio_pad_maps;
>  };
>
>  struct s32_pinctrl_soc_info {
> @@ -53,6 +81,8 @@ struct s32_pinctrl_soc_info {
>
>  #define S32_PINCTRL_PIN(pin)   PINCTRL_PIN(pin, #pin)
>  #define S32_PIN_RANGE(_start, _end) { .start = _start, .end = _end }
> +#define S32_GPIO_RANGE(gpio, pin, num) \
> +       { .gpio_base = gpio, .pin_base = pin, .gpio_num = num }
>
>  int s32_pinctrl_probe(struct platform_device *pdev,
>                       const struct s32_pinctrl_soc_data *soc_data);
> diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c
> index 89a4eb2000ee..8843926345ec 100644
> --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c
> +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c
> @@ -2,7 +2,7 @@
>  /*
>   * Core driver for the S32 CC (Common Chassis) pin controller
>   *
> - * Copyright 2017-2022,2024 NXP
> + * Copyright 2017-2022,2024-2026 NXP
>   * Copyright (C) 2022 SUSE LLC
>   * Copyright 2015-2016 Freescale Semiconductor, Inc.
>   */
> @@ -10,6 +10,7 @@
>  #include <linux/bitops.h>
>  #include <linux/err.h>
>  #include <linux/gpio/driver.h>
> +#include <linux/gpio/regmap.h>
>  #include <linux/init.h>
>  #include <linux/io.h>
>  #include <linux/module.h>
> @@ -39,6 +40,40 @@
>  #define S32_MSCR_ODE           BIT(20)
>  #define S32_MSCR_OBE           BIT(21)
>
> +#define S32_GPIO_OP_SHIFT      16
> +#define S32_GPIO_OP_MASK       GENMASK(19, 16)
> +
> +#define S32_GPIO_OP_DIR                0 /* MSCR direction */
> +#define S32_GPIO_OP_DAT                BIT(S32_GPIO_OP_SHIFT) /* PGPDI read */
> +#define S32_GPIO_OP_SET                BIT(S32_GPIO_OP_SHIFT + 1) /* PGPDO write */
> +
> +/*
> + * [15:12] = GPIO bank / gpio range index
> + * [11:0]  = real register offset or pin id
> + */
> +#define S32_GPIO_BANK_SHIFT    12
> +#define S32_GPIO_BANK_MASK    GENMASK(15, 12)
> +#define S32_GPIO_REG_MASK    GENMASK(11, 0)
> +
> +#define S32_GPIO_ENCODE(bank, off) \
> +       ((((bank) << S32_GPIO_BANK_SHIFT) & S32_GPIO_BANK_MASK) | \
> +               ((off) & S32_GPIO_REG_MASK))
> +
> +#define S32_GPIO_DECODE_BANK(reg) \
> +       (((reg) & S32_GPIO_BANK_MASK) >> S32_GPIO_BANK_SHIFT)
> +
> +#define S32_GPIO_DECODE_OFF(reg) \
> +       ((reg) & S32_GPIO_REG_MASK)
> +
> +/*
> + * PGPDOs are 16bit registers that come in big endian
> + * order if they are grouped in pairs of two.
> + *
> + * For example, the order is PGPDO1, PGPDO0, PGPDO3, PGPDO2...
> + */
> +#define S32_PGPD(N)            (((N) ^ 1) * 2)
> +#define S32_PGPD_SIZE          16
> +
>  enum s32_write_type {
>         S32_PINCONF_UPDATE_ONLY,
>         S32_PINCONF_OVERWRITE,
> @@ -72,6 +107,18 @@ struct s32_pinctrl_mem_region {
>         char name[8];
>  };
>
> +/*
> + * struct s32_gpio_regmaps - GPIO register maps for a SIUL2 instance
> + * @pgpdo: regmap for Parallel GPIO Pad Data Out registers
> + * @pgpdi: regmap for Parallel GPIO Pad Data In registers
> + * @range: GPIO range info
> + */
> +struct s32_gpio_regmaps {
> +       struct regmap *pgpdo;
> +       struct regmap *pgpdi;
> +       const struct s32_gpio_range *range;
> +};
> +
>  /*
>   * struct gpio_pin_config - holds pin configuration for GPIO's
>   * @pin_id: Pin ID for this GPIO
> @@ -98,6 +145,12 @@ struct s32_pinctrl_context {
>   * @pctl: a pointer to the pinctrl device structure
>   * @regions: reserved memory regions with start/end pin
>   * @info: structure containing information about the pin
> + * @gpio_regmaps: PGPDO/PGPDI regmaps for each SIUL2 module
> + * @num_gpio_regmaps: number of GPIO regmap entries
> + * @gpio_regmap: regmap bridging gpio-regmap to SIUL2 registers
> + * @gpio_rgm: gpio-regmap instance registered for this controller
> + * @ngpio: total number of GPIO line offsets
> + * @gpio_names: GPIO line names array passed to gpio-regmap
>   * @gpio_configs: saved configurations for GPIO pins
>   * @gpio_configs_lock: lock for the `gpio_configs` list
>   * @saved_context: configuration saved over system sleep
> @@ -107,6 +160,12 @@ struct s32_pinctrl {
>         struct pinctrl_dev *pctl;
>         struct s32_pinctrl_mem_region *regions;
>         struct s32_pinctrl_soc_info *info;
> +       struct s32_gpio_regmaps *gpio_regmaps;
> +       unsigned int num_gpio_regmaps;
> +       struct regmap *gpio_regmap;
> +       struct gpio_regmap *gpio_rgm;
> +       unsigned int ngpio;
> +       const char *const *gpio_names;
>         struct list_head gpio_configs;
>         spinlock_t gpio_configs_lock;
>  #ifdef CONFIG_PM_SLEEP
> @@ -356,88 +415,84 @@ static int s32_pmx_get_funcs_count(struct pinctrl_dev *pctldev)
>         return info->nfunctions;
>  }
>
> -static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev,
> -                                        unsigned int selector)
> -{
> -       struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> -       const struct s32_pinctrl_soc_info *info = ipctl->info;
> -
> -       return info->functions[selector].name;
> -}
> -
> -static int s32_pmx_get_groups(struct pinctrl_dev *pctldev,
> -                             unsigned int selector,
> -                             const char * const **groups,
> -                             unsigned int * const num_groups)
> -{
> -       struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> -       const struct s32_pinctrl_soc_info *info = ipctl->info;
> -
> -       *groups = info->functions[selector].groups;
> -       *num_groups = info->functions[selector].ngroups;
> -
> -       return 0;
> -}
> -
>  static int s32_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
>                                        struct pinctrl_gpio_range *range,
> -                                      unsigned int offset)
> +                                      unsigned int pin)
>  {
>         struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> -       struct gpio_pin_config *gpio_pin;
> +       struct gpio_pin_config *gpio_pin __free(kfree) = NULL;
>         unsigned int config;
> -       unsigned long flags;
>         int ret;
>
> -       ret = s32_regmap_read(pctldev, offset, &config);
> +       ret = s32_regmap_read(pctldev, pin, &config);
>         if (ret)
>                 return ret;
>
> -       /* Save current configuration */
> -       gpio_pin = kmalloc_obj(*gpio_pin);
> +       gpio_pin = kmalloc(sizeof(*gpio_pin), GFP_KERNEL);

Why? Isn't kmalloc_obj safer?

>         if (!gpio_pin)
>                 return -ENOMEM;
>
> -       gpio_pin->pin_id = offset;
> +       gpio_pin->pin_id = pin;
>         gpio_pin->config = config;
> -       INIT_LIST_HEAD(&gpio_pin->list);
> -
> -       spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
> -       list_add(&gpio_pin->list, &ipctl->gpio_configs);
> -       spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
>
>         /* GPIO pin means SSS = 0 */
> -       config &= ~S32_MSCR_SSS_MASK;
> +       ret = s32_regmap_update(pctldev, pin,
> +                               S32_MSCR_SSS_MASK | S32_MSCR_IBE,
> +                               S32_MSCR_IBE);
> +       if (ret)
> +               return ret;
> +
> +       scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock)
> +               list_add(&no_free_ptr(gpio_pin)->list, &ipctl->gpio_configs);
>
> -       return s32_regmap_write(pctldev, offset, config);
> +       return 0;
>  }
>
>  static void s32_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
>                                       struct pinctrl_gpio_range *range,
> -                                     unsigned int offset)
> +                                     unsigned int pin)
>  {
>         struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> -       struct gpio_pin_config *gpio_pin, *tmp;
> +       struct gpio_pin_config *gpio_pin, *found = NULL;
>         unsigned long flags;
> -       int ret;
>
>         spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
> -
> -       list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) {
> -               if (gpio_pin->pin_id == offset) {
> -                       ret = s32_regmap_write(pctldev, gpio_pin->pin_id,
> -                                                gpio_pin->config);
> -                       if (ret != 0)
> -                               goto unlock;
> -
> +       list_for_each_entry(gpio_pin, &ipctl->gpio_configs, list) {
> +               if (gpio_pin->pin_id == pin) {
>                         list_del(&gpio_pin->list);
> -                       kfree(gpio_pin);
> +                       found = gpio_pin;
>                         break;
>                 }
>         }
> -
> -unlock:
>         spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
> +
> +       if (found) {
> +               s32_regmap_write(pctldev, found->pin_id, found->config);
> +               kfree(found);
> +       }
> +}
> +
> +static const char *s32_pmx_get_func_name(struct pinctrl_dev *pctldev,
> +                                        unsigned int selector)
> +{
> +       struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> +       const struct s32_pinctrl_soc_info *info = ipctl->info;
> +
> +       return info->functions[selector].name;
> +}
> +
> +static int s32_pmx_get_groups(struct pinctrl_dev *pctldev,
> +                             unsigned int selector,
> +                             const char * const **groups,
> +                             unsigned int * const num_groups)
> +{
> +       struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> +       const struct s32_pinctrl_soc_info *info = ipctl->info;
> +
> +       *groups = info->functions[selector].groups;
> +       *num_groups = info->functions[selector].ngroups;
> +
> +       return 0;
>  }
>
>  static int s32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> @@ -649,9 +704,9 @@ static void s32_pinconf_dbg_show(struct pinctrl_dev *pctldev,
>
>         ret = s32_regmap_read(pctldev, pin_id, &config);
>         if (ret)
> -               return;
> -
> -       seq_printf(s, "0x%x", config);
> +               seq_printf(s, "error %d", ret);
> +       else
> +               seq_printf(s, "0x%x", config);
>  }
>
>  static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
> @@ -662,15 +717,13 @@ static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev,
>         struct s32_pin_group *grp;
>         unsigned int config;
>         const char *name;
> -       int i, ret;
> +       int i;
>
>         seq_puts(s, "\n");
>         grp = &info->groups[selector];
>         for (i = 0; i < grp->data.npins; i++) {
>                 name = pin_get_name(pctldev, grp->data.pins[i]);
> -               ret = s32_regmap_read(pctldev, grp->data.pins[i], &config);
> -               if (ret)
> -                       return;
> +               s32_regmap_read(pctldev, grp->data.pins[i], &config);
>                 seq_printf(s, "%s: 0x%x\n", name, config);
>         }
>  }
> @@ -683,6 +736,450 @@ static const struct pinconf_ops s32_pinconf_ops = {
>         .pin_config_group_dbg_show = s32_pinconf_group_dbg_show,
>  };
>
> +static void s32_gpio_free_saved_configs(void *data)
> +{
> +       struct s32_pinctrl *ipctl = data;
> +       struct gpio_pin_config *gpio_pin, *tmp;
> +       unsigned long flags;
> +
> +       spin_lock_irqsave(&ipctl->gpio_configs_lock, flags);
> +       list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) {
> +               list_del(&gpio_pin->list);
> +               kfree(gpio_pin);
> +       }
> +       spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
> +}
> +
> +static unsigned int s32_pin2pad(unsigned int pin)
> +{
> +       return pin / S32_PGPD_SIZE;
> +}
> +
> +static u16 s32_pin2mask(unsigned int pin)
> +{
> +       /*
> +        * From Reference manual :
> +        * PGPDOx[PPDOy] = GPDO(x × 16) + (15 - y)[PDO_(x × 16) + (15 - y)]
> +        */
> +       return BIT(S32_PGPD_SIZE - 1 - pin % S32_PGPD_SIZE);
> +}
> +
> +static int s32_gpio_get_range(struct s32_pinctrl *ipctl,
> +                             unsigned int gpio,
> +                             unsigned int *pin,
> +                             unsigned int *bank)
> +{
> +       const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
> +       const struct s32_gpio_range *range;
> +       int i;
> +
> +       for (i = 0; i < soc_data->num_gpio_ranges; i++) {
> +               range = &soc_data->gpio_ranges[i];
> +
> +               if (gpio < range->gpio_base ||
> +                   gpio >= range->gpio_base + range->gpio_num)
> +                       continue;
> +
> +               if (pin)
> +                       *pin = range->pin_base + gpio - range->gpio_base;
> +
> +               if (bank)
> +                       *bank = i;
> +
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int s32_gpio_pad_map_xlate(struct s32_pinctrl *ipctl,
> +                                 unsigned int gpio,
> +                                 unsigned int *reg_offset,
> +                                 u16 *mask)
> +{
> +       const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
> +       const struct s32_gpio_pad_map *map;
> +       unsigned int bit;
> +       int i;
> +
> +       if (!soc_data->gpio_pad_maps || !soc_data->num_gpio_pad_maps)
> +               return -EINVAL;
> +
> +       for (i = 0; i < soc_data->num_gpio_pad_maps; i++) {
> +               map = &soc_data->gpio_pad_maps[i];
> +
> +               if (gpio < map->gpio_start || gpio > map->gpio_end)
> +                       continue;
> +
> +               bit = gpio - map->gpio_start;
> +               *mask = BIT(S32_PGPD_SIZE - 1 - bit);
> +               *reg_offset = S32_PGPD(map->pad);
> +
> +               return 0;
> +       }
> +
> +       return -EINVAL;
> +}
> +
> +static int s32_gpio_xlate_pgpd(struct s32_pinctrl *ipctl,
> +                              unsigned int pin,
> +                              unsigned int *reg_offset,
> +                              u16 *mask)
> +{
> +       /*
> +        * SIUL2_1 does not expose GPIO data registers as a linear pad sequence.
> +        * Valid PGPD offsets there correspond to PGPD7, PGPD9, PGPD10, PGPD11.
> +        */
> +       if (pin >= 112)

The magic number 112 requires better documentation or a define

Or maybe, (NOT TESTED) instead of hardcoding, check if a pad map
exists for this pin

    /* Try pad map first (needed for SIUL2_1's sparse layout) */
    ret = s32_gpio_pad_map_xlate(ipctl, pin, reg_offset, mask);
    if (ret != -EINVAL)
        return ret;

    /* Fall back to linear layout (SIUL2_0) */
    *mask = s32_pin2mask(pin);
    *reg_offset = S32_PGPD(s32_pin2pad(pin));
    return 0;

Does it make sense?

> +               return s32_gpio_pad_map_xlate(ipctl, pin, reg_offset, mask);
> +
> +       *mask = s32_pin2mask(pin);
> +       *reg_offset = S32_PGPD(s32_pin2pad(pin));
> +
> +       return 0;
> +}
> +
> +static int s32_gpio_reg_mask_xlate(struct gpio_regmap *gpio,
> +                                  unsigned int base, unsigned int offset,
> +                                  unsigned int *reg, unsigned int *mask)
> +{
> +       struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio);
> +       unsigned int pgpd_reg, pin, bank;
> +       u16 pgpd_mask;
> +       int ret;
> +
> +       ret = s32_gpio_get_range(ipctl, offset, &pin, &bank);
> +       if (ret)
> +               return ret;
> +
> +       switch (base) {
> +       case S32_GPIO_OP_DIR:
> +               /*
> +                * Direction is controlled through MSCR OBE.
> +                * Encode the real pin id in the virtual register.
> +                */
> +               *reg = S32_GPIO_OP_DIR | pin;
> +               *mask = S32_MSCR_OBE;
> +               return 0;
> +
> +       case S32_GPIO_OP_DAT:
> +       case S32_GPIO_OP_SET:
> +               ret = s32_gpio_xlate_pgpd(ipctl, pin, &pgpd_reg, &pgpd_mask);
> +               if (ret)
> +                       return ret;
> +               /*
> +                * Encode both the GPIO bank and the real PGPD register offset.
> +                */
> +               *reg = base | S32_GPIO_ENCODE(bank, pgpd_reg);
> +               *mask = pgpd_mask;
> +               return 0;
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int s32_gpio_reg_read(void *context, unsigned int reg,
> +                            unsigned int *val)
> +{
> +       struct s32_pinctrl *ipctl = context;
> +       unsigned int op = reg & S32_GPIO_OP_MASK;
> +       unsigned int vreg = reg & ~S32_GPIO_OP_MASK;
> +       unsigned int bank;
> +       unsigned int offset;
> +       struct regmap *map;
> +
> +       switch (op) {
> +       case S32_GPIO_OP_DIR:
> +               /*
> +                * Lower bits contain the real MSCR pin id.
> +                */
> +               offset = S32_GPIO_DECODE_OFF(vreg);
> +
> +               return s32_regmap_read(ipctl->pctl, offset, val);
> +
> +       case S32_GPIO_OP_DAT:
> +               bank = S32_GPIO_DECODE_BANK(vreg);
> +               offset = S32_GPIO_DECODE_OFF(vreg);
> +
> +               if (bank >= ipctl->num_gpio_regmaps)
> +                       return -EINVAL;
> +
> +               map = ipctl->gpio_regmaps[bank].pgpdi;
> +               if (!map)
> +                       return -ENODEV;
> +
> +               return regmap_read(map, offset, val);
> +
> +       case S32_GPIO_OP_SET:
> +               /*
> +                * gpio-regmap uses update_bits() for set, so it needs to read
> +                * the output register before writing the updated value.
> +                */
> +               bank = S32_GPIO_DECODE_BANK(vreg);
> +               offset = S32_GPIO_DECODE_OFF(vreg);
> +
> +               if (bank >= ipctl->num_gpio_regmaps)
> +                       return -EINVAL;
> +
> +               map = ipctl->gpio_regmaps[bank].pgpdo;
> +               if (!map)
> +                       return -ENODEV;
> +
> +               return regmap_read(map, offset, val);
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static int s32_gpio_reg_write(void *context, unsigned int reg,
> +                             unsigned int val)
> +{
> +       struct s32_pinctrl *ipctl = context;
> +       unsigned int op = reg & S32_GPIO_OP_MASK;
> +       unsigned int vreg = reg & ~S32_GPIO_OP_MASK;
> +       unsigned int bank, offset, config;
> +       struct regmap *map;
> +
> +       switch (op) {
> +       case S32_GPIO_OP_DIR:
> +               /*
> +                * gpio-regmap sets S32_MSCR_OBE for output and clears it for
> +                * input. Keep IBE enabled for GPIOs in both cases.
> +                */
> +               offset = S32_GPIO_DECODE_OFF(vreg);
> +
> +               config = S32_MSCR_IBE;
> +               if (val & S32_MSCR_OBE)
> +                       config |= S32_MSCR_OBE;
> +
> +               return s32_regmap_update(ipctl->pctl, offset,
> +                                        S32_MSCR_OBE | S32_MSCR_IBE,
> +                                        config);
> +
> +       case S32_GPIO_OP_SET:
> +               bank = S32_GPIO_DECODE_BANK(vreg);
> +               offset = S32_GPIO_DECODE_OFF(vreg);
> +
> +               if (bank >= ipctl->num_gpio_regmaps)
> +                       return -EINVAL;
> +
> +               map = ipctl->gpio_regmaps[bank].pgpdo;
> +               if (!map)
> +                       return -ENODEV;
> +
> +               return regmap_write(map, offset, val);
> +
> +       default:
> +               return -EINVAL;
> +       }
> +}
> +
> +static const struct regmap_bus s32_gpio_regmap_bus = {
> +       .reg_read = s32_gpio_reg_read,
> +       .reg_write = s32_gpio_reg_write,
> +};
> +
> +static const struct regmap_config s32_gpio_regmap_config = {
> +       .name = "s32-gpio",
> +       .reg_bits = 32,
> +       .val_bits = 32,
> +       .reg_stride = 1,
> +       .max_register = S32_GPIO_OP_SET | S32_GPIO_BANK_MASK | S32_GPIO_REG_MASK,
> +       .cache_type = REGCACHE_NONE,
> +};
> +
> +static int s32_gpio_get_ngpio(const struct s32_pinctrl_soc_data *soc_data,
> +                             unsigned int *ngpio)
> +{
> +       const struct s32_gpio_range *range;
> +       unsigned int end, max = 0;
> +       int i;
> +
> +       if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
> +               return -EINVAL;
> +
> +       for (i = 0; i < soc_data->num_gpio_ranges; i++) {
> +               range = &soc_data->gpio_ranges[i];
> +
> +               if (!range->gpio_num)
> +                       return -EINVAL;
> +
> +               end = range->gpio_base + range->gpio_num;
> +
> +               /*
> +                * gpio_ranges must be ordered by gpio_base and must not overlap.
> +                * The GPIO line space size is derived from the highest range end.
> +                */
> +               if (i > 0 && range->gpio_base < max)
> +                       return -EINVAL;
> +
> +               if (end > max)
> +                       max = end;
> +       }
> +
> +       *ngpio = max;
> +
> +       return 0;
> +}
> +
> +static int s32_init_gpio_regmap(struct platform_device *pdev,
> +                               struct s32_pinctrl *ipctl)
> +{
> +       ipctl->gpio_regmap =
> +               devm_regmap_init(&pdev->dev, &s32_gpio_regmap_bus,
> +                                ipctl, &s32_gpio_regmap_config);
> +       if (IS_ERR(ipctl->gpio_regmap))
> +               return dev_err_probe(&pdev->dev,
> +                                    PTR_ERR(ipctl->gpio_regmap),
> +                                    "Failed to init GPIO regmap\n");
> +
> +       return 0;
> +}
> +
> +static int s32_init_valid_mask(struct gpio_chip *chip, unsigned long *mask,
> +                              unsigned int ngpios)
> +{
> +       struct gpio_regmap *gpio = gpiochip_get_data(chip);
> +       struct s32_pinctrl *ipctl = gpio_regmap_get_drvdata(gpio);
> +       unsigned int gpio_num, pin, reg_offset;
> +       u16 pgpd_mask;
> +       int ret;
> +
> +       bitmap_zero(mask, ngpios);
> +
> +       for (gpio_num = 0; gpio_num < ngpios; gpio_num++) {
> +               ret = s32_gpio_get_range(ipctl, gpio_num, &pin, NULL);
> +               if (ret)
> +                       continue;
> +
> +               ret = s32_gpio_xlate_pgpd(ipctl, pin, &reg_offset, &pgpd_mask);
> +               if (ret)
> +                       continue;
> +
> +               bitmap_set(mask, gpio_num, 1);
> +       }
> +
> +       return 0;
> +}
> +
> +static int s32_gpio_populate_names(struct s32_pinctrl *ipctl)
> +{
> +       char **names;
> +       unsigned int gpio;
> +       unsigned int pin;
> +       char port;
> +       int ret;
> +
> +       names = devm_kcalloc(ipctl->dev, ipctl->ngpio, sizeof(*names),
> +                            GFP_KERNEL);
> +       if (!names)
> +               return -ENOMEM;
> +
> +       for (gpio = 0; gpio < ipctl->ngpio; gpio++) {
> +               ret = s32_gpio_get_range(ipctl, gpio, &pin, NULL);
> +               if (ret)
> +                       continue;
> +
> +               port = 'A' + pin / 16;
> +
> +               names[gpio] = devm_kasprintf(ipctl->dev, GFP_KERNEL,
> +                                            "P%c_%02u", port, pin & 0xf);
> +               if (!names[gpio])
> +                       return -ENOMEM;
> +       }
> +
> +       ipctl->gpio_names = (const char *const *)names;
> +
> +       return 0;
> +}
> +
> +static int s32_pinctrl_init_gpio_regmaps(struct platform_device *pdev,
> +                                        struct s32_pinctrl *ipctl)
> +{
> +       const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
> +       static const struct regmap_config pgpd_config = {
> +               .reg_bits = 32,
> +               .val_bits = 16,
> +               .reg_stride = 2,
> +       };
> +       struct regmap_config cfg;
> +       struct resource *res;
> +       void __iomem *base;
> +       unsigned int pgpdo_idx, pgpdi_idx;
> +       unsigned int i;
> +
> +       if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
> +               return 0;
> +
> +       ipctl->num_gpio_regmaps = soc_data->num_gpio_ranges;
> +       ipctl->gpio_regmaps = devm_kcalloc(&pdev->dev, ipctl->num_gpio_regmaps,
> +                                          sizeof(*ipctl->gpio_regmaps),
> +                                          GFP_KERNEL);
> +       if (!ipctl->gpio_regmaps)
> +               return -ENOMEM;
> +
> +       for (i = 0; i < ipctl->num_gpio_regmaps; i++) {
> +               ipctl->gpio_regmaps[i].range = &soc_data->gpio_ranges[i];
> +
> +               /*
> +                * GPIO resources are placed after the pinctrl regions
> +                */
> +               pgpdo_idx = soc_data->mem_regions + i * 2;
> +               pgpdi_idx = soc_data->mem_regions + i * 2 + 1;
> +
> +               /* PGPDO */
> +               res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdo_idx);
> +               if (!res)
> +                       return dev_err_probe(&pdev->dev, -ENOENT,
> +                                                "Missing PGPDO resource %u\n", i);
> +
> +               base = devm_ioremap_resource(&pdev->dev, res);
> +               if (IS_ERR(base))
> +                       return PTR_ERR(base);
> +
> +               cfg = pgpd_config;
> +               cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdo%u", i);
> +               if (!cfg.name)
> +                       return -ENOMEM;
> +
> +               cfg.max_register = resource_size(res) - cfg.reg_stride;
> +
> +               ipctl->gpio_regmaps[i].pgpdo =
> +                       devm_regmap_init_mmio(&pdev->dev, base, &cfg);
> +               if (IS_ERR(ipctl->gpio_regmaps[i].pgpdo))
> +                       return dev_err_probe(&pdev->dev,
> +                                                PTR_ERR(ipctl->gpio_regmaps[i].pgpdo),
> +                                                "Failed to init PGPDO regmap %u\n", i);
> +
> +               /* PGPDI */
> +               res = platform_get_resource(pdev, IORESOURCE_MEM, pgpdi_idx);
> +               if (!res)
> +                       return dev_err_probe(&pdev->dev, -ENOENT,
> +                                                "Missing PGPDI resource %u\n", i);
> +
> +               base = devm_ioremap_resource(&pdev->dev, res);
> +               if (IS_ERR(base))
> +                       return PTR_ERR(base);
> +
> +               cfg = pgpd_config;
> +               cfg.name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "pgpdi%u", i);
> +               if (!cfg.name)
> +                       return -ENOMEM;
> +
> +               cfg.max_register = resource_size(res) - cfg.reg_stride;
> +
> +               ipctl->gpio_regmaps[i].pgpdi =
> +                       devm_regmap_init_mmio(&pdev->dev, base, &cfg);
> +               if (IS_ERR(ipctl->gpio_regmaps[i].pgpdi))
> +                       return dev_err_probe(&pdev->dev,
> +                                                PTR_ERR(ipctl->gpio_regmaps[i].pgpdi),
> +                                                "Failed to init PGPDI regmap %u\n", i);
> +       }
> +
> +       return 0;
> +}
> +
>  #ifdef CONFIG_PM_SLEEP
>  static bool s32_pinctrl_should_save(struct s32_pinctrl *ipctl,
>                                     unsigned int pin)
> @@ -710,7 +1207,6 @@ int s32_pinctrl_suspend(struct device *dev)
>         const struct s32_pinctrl_soc_info *info = ipctl->info;
>         struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
>         int i;
> -       int ret;
>         unsigned int config;
>
>         for (i = 0; i < info->soc_data->npins; i++) {
> @@ -719,9 +1215,7 @@ int s32_pinctrl_suspend(struct device *dev)
>                 if (!s32_pinctrl_should_save(ipctl, pin->number))
>                         continue;
>
> -               ret = s32_regmap_read(ipctl->pctl, pin->number, &config);
> -               if (ret)
> -                       return -EINVAL;
> +               s32_regmap_read(ipctl->pctl, pin->number, &config);
>
>                 saved_context->pads[i] = config;
>         }
> @@ -736,7 +1230,7 @@ int s32_pinctrl_resume(struct device *dev)
>         const struct s32_pinctrl_soc_info *info = ipctl->info;
>         const struct pinctrl_pin_desc *pin;
>         struct s32_pinctrl_context *saved_context = &ipctl->saved_context;
> -       int ret, i;
> +       int i;
>
>         for (i = 0; i < info->soc_data->npins; i++) {
>                 pin = &info->soc_data->pins[i];
> @@ -744,10 +1238,8 @@ int s32_pinctrl_resume(struct device *dev)
>                 if (!s32_pinctrl_should_save(ipctl, pin->number))
>                         continue;
>
> -               ret = s32_regmap_write(ipctl->pctl, pin->number,
> -                                        saved_context->pads[i]);
> -               if (ret)
> -                       return ret;
> +               s32_regmap_write(ipctl->pctl, pin->number,
> +                                saved_context->pads[i]);
>         }
>
>         return 0;
> @@ -927,13 +1419,15 @@ static int s32_pinctrl_probe_dt(struct platform_device *pdev,
>  int s32_pinctrl_probe(struct platform_device *pdev,
>                       const struct s32_pinctrl_soc_data *soc_data)
>  {
> -       struct s32_pinctrl *ipctl;
> -       int ret;
> -       struct pinctrl_desc *s32_pinctrl_desc;
> -       struct s32_pinctrl_soc_info *info;
>  #ifdef CONFIG_PM_SLEEP
>         struct s32_pinctrl_context *saved_context;
>  #endif
> +       struct gpio_regmap_config gpio_cfg = {};
> +       struct pinctrl_desc *s32_pinctrl_desc;
> +       struct s32_pinctrl_soc_info *info;
> +       struct s32_pinctrl *ipctl;
> +       unsigned int ngpio;
> +       int ret;
>
>         if (!soc_data || !soc_data->pins || !soc_data->npins) {
>                 dev_err(&pdev->dev, "wrong pinctrl info\n");
> @@ -959,6 +1453,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
>         INIT_LIST_HEAD(&ipctl->gpio_configs);
>         spin_lock_init(&ipctl->gpio_configs_lock);
>
> +       ret = devm_add_action_or_reset(&pdev->dev,
> +                                      s32_gpio_free_saved_configs, ipctl);
> +       if (ret)
> +               return ret;
> +
>         s32_pinctrl_desc =
>                 devm_kzalloc(&pdev->dev, sizeof(*s32_pinctrl_desc), GFP_KERNEL);
>         if (!s32_pinctrl_desc)
> @@ -978,6 +1477,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
>                 return ret;
>         }
>
> +       ret = s32_pinctrl_init_gpio_regmaps(pdev, ipctl);
> +       if (ret)
> +               return dev_err_probe(&pdev->dev, ret,
> +                                    "Failed to init GPIO regmaps\n");
> +
>         ret = devm_pinctrl_register_and_init(&pdev->dev, s32_pinctrl_desc,
>                                              ipctl, &ipctl->pctl);
>         if (ret)
> @@ -999,7 +1503,42 @@ int s32_pinctrl_probe(struct platform_device *pdev,
>                 return dev_err_probe(&pdev->dev, ret,
>                                      "Failed to enable pinctrl\n");
>
> -       dev_info(&pdev->dev, "Initialized S32 pinctrl driver\n");
> +       /* Setup GPIO if GPIO ranges are defined */
> +       if (!soc_data->gpio_ranges || !soc_data->num_gpio_ranges)
> +               return 0;
> +
> +       ret = s32_gpio_get_ngpio(soc_data, &ngpio);
> +       if (ret)
> +               return dev_err_probe(&pdev->dev, ret, "Invalid GPIO ranges\n");
> +
> +       ipctl->ngpio = ngpio;
> +
> +       ret = s32_gpio_populate_names(ipctl);
> +       if (ret)
> +               return ret;
> +
> +       ret = s32_init_gpio_regmap(pdev, ipctl);
> +       if (ret)
> +               return ret;
> +
> +       gpio_cfg.parent = &pdev->dev;
> +       gpio_cfg.fwnode = dev_fwnode(&pdev->dev);
> +       gpio_cfg.label = dev_name(&pdev->dev);
> +       gpio_cfg.regmap = ipctl->gpio_regmap;
> +       gpio_cfg.ngpio = ngpio;
> +       gpio_cfg.names = ipctl->gpio_names;
> +       gpio_cfg.reg_dir_out_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DIR);
> +       gpio_cfg.reg_dat_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_DAT);
> +       gpio_cfg.reg_set_base = GPIO_REGMAP_ADDR(S32_GPIO_OP_SET);
> +       gpio_cfg.reg_mask_xlate = s32_gpio_reg_mask_xlate;
> +       gpio_cfg.init_valid_mask = s32_init_valid_mask;
> +       gpio_cfg.drvdata = ipctl;
> +
> +       ipctl->gpio_rgm = devm_gpio_regmap_register(&pdev->dev, &gpio_cfg);
> +       if (IS_ERR(ipctl->gpio_rgm))
> +               return dev_err_probe(&pdev->dev,
> +                                    PTR_ERR(ipctl->gpio_rgm),
> +                                    "Unable to add gpio_regmap chip\n");
>
>         return 0;
>  }
> diff --git a/drivers/pinctrl/nxp/pinctrl-s32g2.c b/drivers/pinctrl/nxp/pinctrl-s32g2.c
> index c49d28793b69..0bd6e6ab5ad1 100644
> --- a/drivers/pinctrl/nxp/pinctrl-s32g2.c
> +++ b/drivers/pinctrl/nxp/pinctrl-s32g2.c
> @@ -3,7 +3,7 @@
>   * NXP S32G pinctrl driver
>   *
>   * Copyright 2015-2016 Freescale Semiconductor, Inc.
> - * Copyright 2017-2018, 2020-2022 NXP
> + * Copyright 2017-2018, 2020-2022, 2025-2026 NXP
>   * Copyright (C) 2022 SUSE LLC
>   */
>
> @@ -773,17 +773,47 @@ static const struct s32_pin_range s32_pin_ranges_siul2[] = {
>         S32_PIN_RANGE(942, 1007),
>  };
>
> -static const struct s32_pinctrl_soc_data s32_pinctrl_data = {
> +static const struct s32_gpio_range s32_gpio_ranges_siul2[] = {
> +       S32_GPIO_RANGE(0, 0, 102),
> +       S32_GPIO_RANGE(112, 112, 79),
> +};
> +
> +/*
> + * SIUL2_1 GPIO ranges mapped to sparse PGPD pads.
> + *
> + * SIUL2_1 does not expose GPIO data registers as a linear pad
> + * sequence. Each entry describes a contiguous GPIO offset range
> + * and the PGPD pad servicing that range.
> + */
> +static const struct s32_gpio_pad_map s32g_gpio_pad_maps[] = {
> +       { 112, 122, 7  }, /* PH_00 .. PH_10 -> PGPD7  */
> +       { 144, 159, 9  }, /* PJ_00 .. PJ_15 -> PGPD9  */
> +       { 160, 175, 10 }, /* PK_00 .. PK_15 -> PGPD10 */
> +       { 176, 190, 11 }, /* PL_00 .. PL_14 -> PGPD11 */
> +};
> +
> +/* Legacy data for old DT bindings without GPIO support */
> +static const struct s32_pinctrl_soc_data legacy_s32g_pinctrl_data = {
> +       .pins = s32_pinctrl_pads_siul2,
> +       .npins = ARRAY_SIZE(s32_pinctrl_pads_siul2),
> +       .mem_pin_ranges = s32_pin_ranges_siul2,
> +       .mem_regions = ARRAY_SIZE(s32_pin_ranges_siul2),
> +};
> +
> +static const struct s32_pinctrl_soc_data s32g_pinctrl_data = {
>         .pins = s32_pinctrl_pads_siul2,
>         .npins = ARRAY_SIZE(s32_pinctrl_pads_siul2),
>         .mem_pin_ranges = s32_pin_ranges_siul2,
>         .mem_regions = ARRAY_SIZE(s32_pin_ranges_siul2),
> +       .gpio_ranges = s32_gpio_ranges_siul2,
> +       .num_gpio_ranges = ARRAY_SIZE(s32_gpio_ranges_siul2),
> +       .gpio_pad_maps = s32g_gpio_pad_maps,
> +       .num_gpio_pad_maps = ARRAY_SIZE(s32g_gpio_pad_maps),
>  };
>
>  static const struct of_device_id s32_pinctrl_of_match[] = {
>         {
>                 .compatible = "nxp,s32g2-siul2-pinctrl",
> -               .data = &s32_pinctrl_data,
>         },
>         { /* sentinel */ }
>  };
> @@ -792,8 +822,16 @@ MODULE_DEVICE_TABLE(of, s32_pinctrl_of_match);
>  static int s32g_pinctrl_probe(struct platform_device *pdev)
>  {
>         const struct s32_pinctrl_soc_data *soc_data;
> +       struct device_node *np = pdev->dev.of_node;
>
> -       soc_data = of_device_get_match_data(&pdev->dev);
> +       /*
> +        * Legacy DTs only describe the pinctrl resources.
> +        * New DT changes extend the same node with GPIO resources.
> +        */
> +       if (of_property_present(np, "gpio-controller"))
> +               soc_data = &s32g_pinctrl_data;
> +       else
> +               soc_data = &legacy_s32g_pinctrl_data;
>
>         return s32_pinctrl_probe(pdev, soc_data);
>  }
> --
> 2.34.1
>


^ permalink raw reply	[flat|nested] 12+ messages in thread

end of thread, other threads:[~2026-06-02  9:26 UTC | newest]

Thread overview: 12+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-06-02  8:01 [PATCH v10 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu
2026-06-02  8:01 ` [PATCH v10 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
2026-06-02  8:06   ` sashiko-bot
2026-06-02  8:01 ` [PATCH v10 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu
2026-06-02  8:01 ` [PATCH v10 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu
2026-06-02  8:01 ` [PATCH v10 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu
2026-06-02  8:21   ` sashiko-bot
2026-06-02  8:01 ` [PATCH v10 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu
2026-06-02  8:31   ` sashiko-bot
2026-06-02  9:26   ` Enric Balletbo i Serra
2026-06-02  8:01 ` [PATCH v10 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu
2026-06-02  8:38   ` sashiko-bot

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox