* [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver
@ 2026-06-10 13:21 Khristine Andreea Barbulescu
2026-06-10 13:21 ` [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu
` (5 more replies)
0 siblings, 6 replies; 9+ messages in thread
From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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.
v11 -> v10:
- add GPIOLIB as explicit Kconfig dependency
- mark regmap config as fast_io to avoid mutex overhead
- propagate regmap errors in debug show callback and
suspend/resume paths instead of silently discarding them
- introduce a per-range sparse flag to handle SIUL2 instances
with a non-linear PGPD layout
- dt-bindings: drop redundant minItems from legacy oneOf branch,
add gpio-controller: false and related properties to prevent
GPIO/IRQ properties without the required reg entries and
drop maxItems from the GPIO+IRQ branch
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 | 90 ++-
arch/arm64/boot/dts/freescale/s32g2.dtsi | 23 +-
arch/arm64/boot/dts/freescale/s32g3.dtsi | 23 +-
drivers/pinctrl/nxp/Kconfig | 3 +-
drivers/pinctrl/nxp/pinctrl-s32.h | 35 +-
drivers/pinctrl/nxp/pinctrl-s32cc.c | 756 ++++++++++++++++--
drivers/pinctrl/nxp/pinctrl-s32g2.c | 47 +-
7 files changed, 877 insertions(+), 100 deletions(-)
--
2.34.1
^ permalink raw reply [flat|nested] 9+ messages in thread* [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 2026-06-10 13:31 ` sashiko-bot 2026-06-10 13:21 ` [PATCH v11 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu ` (4 subsequent siblings) 5 siblings, 1 reply; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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. 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 | 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] 9+ messages in thread
* Re: [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments 2026-06-10 13:21 ` [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu @ 2026-06-10 13:31 ` sashiko-bot 0 siblings, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-06-10 13:31 UTC (permalink / raw) To: Khristine Andreea Barbulescu; +Cc: devicetree, Frank.Li, robh, imx, conor+dt Thank you for your contribution! Sashiko AI review found 3 potential issue(s) to consider: - [Low] Kernel-doc style comments use `/*` instead of `/**`. - [Low] Typo 'pards' instead of 'pads' in the `struct s32_pinctrl_context` comment. - [Low] Stylistic regression: capitalizing the first letter of a log message in `dev_err_probe`. -- commit 0e69d63aa3bf617928fdc20d2e9e07024a115811 Author: Khristine Andreea Barbulescu <khristineandreea.barbulescu@oss.nxp.com> pinctrl: s32cc: add/fix some comments This commit adds and fixes some comments and print statements in the s32cc pinctrl driver. > 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); > } > > +/* > + * struct s32_pinctrl_mem_region - memory region for a set of SIUL2 registers [Severity: Low] This isn't a bug, but since these comments are written in the kernel-doc format, should they begin with "/**" instead of "/*"? Using "/*" prevents the scripts/kernel-doc parsing tool from reading them and integrating them into the generated documentation. > + * @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; [ ... ] > @@ -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. Should "pards" be "pads"? > */ > struct s32_pinctrl_context { > unsigned int *pads; > }; > > /* > + * struct s32_pinctrl - private driver data [Severity: Low] This is another instance where a kernel-doc style comment begins with "/*" instead of "/**". Does this need to be updated to match the standard documentation style? > * @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 [ ... ] > @@ -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 the Linux kernel coding style generally prefers starting dev_err_probe messages with a lower-case letter. Is there a specific reason to capitalize "Could" here? > > #ifdef CONFIG_PM_SLEEP > saved_context = &ipctl->saved_context; -- Sashiko AI review · https://sashiko.dev/#/patchset/20260610132116.1998140-1-khristineandreea.barbulescu@oss.nxp.com?part=1 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v11 2/6] pinctrl: s32cc: remove inline specifiers 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu ` (3 subsequent siblings) 5 siblings, 0 replies; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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] 9+ messages in thread
* [PATCH v11 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu ` (2 subsequent siblings) 5 siblings, 0 replies; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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] 9+ messages in thread
* [PATCH v11 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu ` (2 preceding siblings ...) 2026-06-10 13:21 ` [PATCH v11 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu 5 siblings, 0 replies; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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 | 90 +++++++++++++++++-- 1 file changed, 84 insertions(+), 6 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..36f2393fa406 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,11 +118,38 @@ required: - compatible - reg +oneOf: + - description: Legacy pinctrl-only node + properties: + reg: + maxItems: 6 + + gpio-controller: false + "#gpio-cells": false + gpio-ranges: false + interrupt-controller: false + "#interrupt-cells": false + interrupts: false + + - 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 + additionalProperties: false examples: - | - pinctrl@4009c240 { + #include <dt-bindings/interrupt-controller/arm-gic.h> + + pinctrl: pinctrl@4009c240 { compatible = "nxp,s32g2-siul2-pinctrl"; /* MSCR0-MSCR101 registers on siul2_0 */ @@ -104,7 +163,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] 9+ messages in thread
* [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu ` (3 preceding siblings ...) 2026-06-10 13:21 ` [PATCH v11 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 2026-06-10 13:37 ` sashiko-bot 2026-06-10 13:21 ` [PATCH v11 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu 5 siblings, 1 reply; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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 | 3 +- drivers/pinctrl/nxp/pinctrl-s32.h | 35 +- drivers/pinctrl/nxp/pinctrl-s32cc.c | 709 +++++++++++++++++++++++++--- drivers/pinctrl/nxp/pinctrl-s32g2.c | 47 +- 4 files changed, 721 insertions(+), 73 deletions(-) diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index abca7ef97003..711c0fe11565 100644 --- a/drivers/pinctrl/nxp/Kconfig +++ b/drivers/pinctrl/nxp/Kconfig @@ -1,10 +1,11 @@ # SPDX-License-Identifier: GPL-2.0-only config PINCTRL_S32CC bool - depends on ARCH_S32 && OF + depends on ARCH_S32 && OF && GPIOLIB 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..028578a090e4 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,42 @@ 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 + * @sparse: true if the PGPD layout is non-linear (resolved via pad map only); + * pins not found in the pad map are invalid for this range + */ +struct s32_gpio_range { + unsigned int gpio_base; + unsigned int pin_base; + unsigned int gpio_num; + bool sparse; +}; + +/** + * 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 +84,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..b8fb0db42949 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_obj(*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, @@ -669,8 +724,11 @@ static void s32_pinconf_group_dbg_show(struct pinctrl_dev *pctldev, 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; + if (ret) { + seq_printf(s, "%s: error %d\n", name, ret); + continue; + } + seq_printf(s, "%s: 0x%x\n", name, config); } } @@ -683,6 +741,477 @@ 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 bool s32_gpio_pin_is_sparse(struct s32_pinctrl *ipctl, unsigned int pin) +{ + 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 (pin >= range->pin_base && + pin < range->pin_base + range->gpio_num) + return range->sparse; + } + + return false; +} + +static int s32_gpio_xlate_pgpd(struct s32_pinctrl *ipctl, + unsigned int pin, + unsigned int *reg_offset, + u16 *mask) +{ + int ret; + + /* + * Try the pad map first. For sparse ranges (SIUL2_1), only pins + * listed in the pad map are valid, return the error directly without + * falling back to the linear layout. + * For linear ranges (SIUL2_0), fall back to the linear pad-to-PGPD + * formula if no pad map entry matches. + */ + ret = s32_gpio_pad_map_xlate(ipctl, pin, reg_offset, mask); + if (ret != -EINVAL) + return ret; + + if (s32_gpio_pin_is_sparse(ipctl, pin)) + return -EINVAL; + + /* Linear layout fallback for non-sparse ranges. */ + *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, + .fast_io = true, +}; + +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, ®_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) @@ -709,8 +1238,7 @@ int s32_pinctrl_suspend(struct device *dev) const struct pinctrl_pin_desc *pin; const struct s32_pinctrl_soc_info *info = ipctl->info; struct s32_pinctrl_context *saved_context = &ipctl->saved_context; - int i; - int ret; + int i, ret; unsigned int config; for (i = 0; i < info->soc_data->npins; i++) { @@ -721,7 +1249,7 @@ int s32_pinctrl_suspend(struct device *dev) ret = s32_regmap_read(ipctl->pctl, pin->number, &config); if (ret) - return -EINVAL; + return ret; saved_context->pads[i] = config; } @@ -736,7 +1264,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, ret; for (i = 0; i < info->soc_data->npins; i++) { pin = &info->soc_data->pins[i]; @@ -745,7 +1273,7 @@ int s32_pinctrl_resume(struct device *dev) continue; ret = s32_regmap_write(ipctl->pctl, pin->number, - saved_context->pads[i]); + saved_context->pads[i]); if (ret) return ret; } @@ -927,13 +1455,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 +1489,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 +1513,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 +1539,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..f9546c67a269 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,48 @@ 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), + /* SIUL2_1: sparse layout, PGPD mapping required for all pins */ + { .gpio_base = 112, .pin_base = 112, .gpio_num = 79, .sparse = true }, +}; + +/* + * 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 +823,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] 9+ messages in thread
* Re: [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality 2026-06-10 13:21 ` [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu @ 2026-06-10 13:37 ` sashiko-bot 0 siblings, 0 replies; 9+ messages in thread From: sashiko-bot @ 2026-06-10 13:37 UTC (permalink / raw) To: Khristine Andreea Barbulescu; +Cc: imx, conor+dt, Frank.Li, devicetree, robh Thank you for your contribution! Sashiko AI review found 2 potential issue(s) to consider: - [High] GPIO output states (PGPDO) are lost during system suspend and resume, causing hardware glitches or data loss upon resume. - [Low] Semantic mismatch and confusing naming in `s32_gpio_pad_map_xlate()` where a pin ID is compared against properties documented as GPIO line offsets. -- commit ff45721a707b92abe04df6541806874a10e772ee Author: Andrei Stefanescu <andrei.stefanescu@oss.nxp.com> pinctrl: s32cc: implement GPIO functionality This commit introduces GPIO functionality to the S32CC pin controller using the gpio-regmap library. It adds virtual regmaps to translate GPIO operations to the underlying SIUL2 registers (MSCR, PGPDI, PGPDO) and supports both legacy and extended DT 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/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h > --- a/drivers/pinctrl/nxp/pinctrl-s32.h > +++ b/drivers/pinctrl/nxp/pinctrl-s32.h [ ... ] > +/** > + * 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; > +}; [ ... ] > diff --git a/drivers/pinctrl/nxp/pinctrl-s32cc.c b/drivers/pinctrl/nxp/pinctrl-s32cc.c > --- a/drivers/pinctrl/nxp/pinctrl-s32cc.c > +++ b/drivers/pinctrl/nxp/pinctrl-s32cc.c [ ... ] > +static int s32_gpio_pad_map_xlate(struct s32_pinctrl *ipctl, > + unsigned int gpio, > + unsigned int *reg_offset, > + u16 *mask) > +{ [ ... ] > + 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; [Severity: Low] Are gpio_start and gpio_end actually meant to be pin IDs instead of GPIO line offsets? The documentation in struct s32_gpio_pad_map says they are GPIO line offsets, but here they are compared against the incoming gpio parameter which actually receives a pin ID from s32_gpio_xlate_pgpd(). While this does not break functionality currently because gpio_base equals pin_base for all sparse ranges, is it worth updating the naming and documentation to reflect that these variables map to pin IDs? > + > + bit = gpio - map->gpio_start; > + *mask = BIT(S32_PGPD_SIZE - 1 - bit); > + *reg_offset = S32_PGPD(map->pad); [ ... ] > @@ -709,8 +1238,7 @@ int s32_pinctrl_suspend(struct device *dev) > const struct pinctrl_pin_desc *pin; > const struct s32_pinctrl_soc_info *info = ipctl->info; > struct s32_pinctrl_context *saved_context = &ipctl->saved_context; > - int i; > - int ret; > + int i, ret; > unsigned int config; > > for (i = 0; i < info->soc_data->npins; i++) { > @@ -721,7 +1249,7 @@ int s32_pinctrl_suspend(struct device *dev) > > ret = s32_regmap_read(ipctl->pctl, pin->number, &config); > if (ret) > - return -EINVAL; > + return ret; > > saved_context->pads[i] = config; > } [Severity: High] Should PGPDO output register states be saved and restored during system suspend and resume? Currently, s32_pinctrl_suspend() saves the MSCR configurations but omits saving the newly introduced PGPDO registers. > @@ -736,7 +1264,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, ret; > > for (i = 0; i < info->soc_data->npins; i++) { > pin = &info->soc_data->pins[i]; > @@ -745,7 +1273,7 @@ int s32_pinctrl_resume(struct device *dev) > continue; > > ret = s32_regmap_write(ipctl->pctl, pin->number, > - saved_context->pads[i]); > + saved_context->pads[i]); During s32_pinctrl_resume(), when the MSCR registers are restored, the Output Buffer Enable (OBE) bits are re-enabled. Because PGPDO registers lose power and reset to 0 during suspend, and they are not explicitly restored here, could active GPIO outputs immediately drive their hardware reset values instead of their pre-suspend states upon resume, potentially causing hardware glitches? > if (ret) > return ret; > } -- Sashiko AI review · https://sashiko.dev/#/patchset/20260610132116.1998140-1-khristineandreea.barbulescu@oss.nxp.com?part=5 ^ permalink raw reply [flat|nested] 9+ messages in thread
* [PATCH v11 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu ` (4 preceding siblings ...) 2026-06-10 13:21 ` [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu @ 2026-06-10 13:21 ` Khristine Andreea Barbulescu 5 siblings, 0 replies; 9+ messages in thread From: Khristine Andreea Barbulescu @ 2026-06-10 13:21 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] 9+ messages in thread
end of thread, other threads:[~2026-06-10 13:37 UTC | newest] Thread overview: 9+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2026-06-10 13:21 [PATCH v11 0/6] gpio: siul2-s32g2: add initial GPIO driver Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 1/6] pinctrl: s32cc: add/fix some comments Khristine Andreea Barbulescu 2026-06-10 13:31 ` sashiko-bot 2026-06-10 13:21 ` [PATCH v11 2/6] pinctrl: s32cc: remove inline specifiers Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 3/6] pinctrl: s32cc: change to "devm_pinctrl_register_and_init" Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 4/6] dt-bindings: pinctrl: s32g2-siul2: describe GPIO and EIRQ resources Khristine Andreea Barbulescu 2026-06-10 13:21 ` [PATCH v11 5/6] pinctrl: s32cc: implement GPIO functionality Khristine Andreea Barbulescu 2026-06-10 13:37 ` sashiko-bot 2026-06-10 13:21 ` [PATCH v11 6/6] arm64: dts: s32g: describe GPIO and EIRQ resources in SIUL2 pinctrl node Khristine Andreea Barbulescu
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox