summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMark Brown <broonie@kernel.org>2026-07-06 15:08:52 +0100
committerMark Brown <broonie@kernel.org>2026-07-06 15:08:52 +0100
commit24d5bd9bd8539a2488ac52ec53c83c2fcd6841ce (patch)
treee7a5183f171fcdbf1fbd11e6d74f1cd239be4a08
parentc3e8e6a6abe0f480dab7c5ea1c41c015b512dd3f (diff)
parent94cb9e8f270797e489633cfa53d2d44afecb8bef (diff)
downloadlinux-next-24d5bd9bd8539a2488ac52ec53c83c2fcd6841ce.tar.gz
linux-next-24d5bd9bd8539a2488ac52ec53c83c2fcd6841ce.zip
Merge branch 'for-next' of https://git.kernel.org/pub/scm/linux/kernel/git/linusw/linux-pinctrl.git
-rw-r--r--Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml1
-rw-r--r--Documentation/devicetree/bindings/pinctrl/nxp,s32g2-siul2-pinctrl.yaml90
-rw-r--r--MAINTAINERS7
-rw-r--r--drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c6
-rw-r--r--drivers/pinctrl/bcm/pinctrl-bcm2835.c1
-rw-r--r--drivers/pinctrl/freescale/pinctrl-imx1-core.c7
-rw-r--r--drivers/pinctrl/nxp/Kconfig3
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32.h35
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32cc.c748
-rw-r--r--drivers/pinctrl/nxp/pinctrl-s32g2.c47
-rw-r--r--drivers/pinctrl/pinctrl-tb10x.c2
11 files changed, 846 insertions, 101 deletions
diff --git a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml
index 76944fd14e2c..fe7cef4fef6a 100644
--- a/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml
+++ b/Documentation/devicetree/bindings/pinctrl/aspeed,ast2700-soc1-pinctrl.yaml
@@ -356,6 +356,7 @@ patternProperties:
- I3C8
- I3C9
- JTAGM1
+ - JTAGM1TRST
- LPC0
- LPC1
- LTPI
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 {
diff --git a/MAINTAINERS b/MAINTAINERS
index 878b91dde848..52b6c2e1d6a2 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -22380,6 +22380,13 @@ L: linux-arm-msm@vger.kernel.org
S: Maintained
F: drivers/firmware/qcom/qcom_qseecom_uefisecapp.c
+QUALCOMM PINCTRL DRIVERS
+M: Bartosz Golaszewski <brgl@kernel.org>
+L: linux-arm-msm@vger.kernel.org
+S: Supported
+F: Documentation/devicetree/bindings/pinctrl/qcom,*.yaml
+F: drivers/pinctrl/qcom/
+
QUALCOMM RMNET DRIVER
M: Subash Abhinov Kasiviswanathan <subash.a.kasiviswanathan@oss.qualcomm.com>
M: Sean Tranchetti <sean.tranchetti@oss.qualcomm.com>
diff --git a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c
index 50027d69c342..f8b4066699ce 100644
--- a/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c
+++ b/drivers/pinctrl/aspeed/pinctrl-aspeed-g7-soc1.c
@@ -1018,7 +1018,8 @@ PIN_GROUP(I3C6, AA22, AB20);
PIN_GROUP(I3C7, AF18, AE19);
PIN_GROUP(I3C8, AD20, AC20);
PIN_GROUP(I3C9, AA21, AB21);
-PIN_GROUP(JTAGM1, D12, F10, E11, F11, F13);
+PIN_GROUP(JTAGM1, F10, E11, F11, F13);
+PIN_GROUP(JTAGM1TRST, D12);
PIN_GROUP(LPC0, AF26, AF25, B16, D14, B15, B14, C17, B13, E14, C15);
PIN_GROUP(LPC1, C16, C14, C11, D9, F14, D10, C12, C13, AE16, AE17);
PIN_GROUP(LTPI, U25, U26, Y26, AA24);
@@ -1263,6 +1264,7 @@ static const struct pingroup aspeed_g7_soc1_groups[] = {
GROUP(I3C8),
GROUP(I3C9),
GROUP(JTAGM1),
+ GROUP(JTAGM1TRST),
GROUP(LPC0),
GROUP(LPC1),
GROUP(LTPI),
@@ -1528,7 +1530,7 @@ static const struct aspeed_g7_soc1_function aspeed_g7_soc1_functions[] = {
FUNC(I3C7, (1), "I3C7"),
FUNC(I3C8, (1), "I3C8"),
FUNC(I3C9, (1), "I3C9"),
- FUNC(JTAGM1, (1), "JTAGM1"),
+ FUNC(JTAGM1, (1, 1), "JTAGM1", "JTAGM1TRST"),
FUNC(LPC0, (2), "LPC0"),
FUNC(LPC1, (2), "LPC1"),
FUNC(LTPI, (2), "LTPI"),
diff --git a/drivers/pinctrl/bcm/pinctrl-bcm2835.c b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
index e7b35019a5a7..725e880ae086 100644
--- a/drivers/pinctrl/bcm/pinctrl-bcm2835.c
+++ b/drivers/pinctrl/bcm/pinctrl-bcm2835.c
@@ -1350,7 +1350,6 @@ static int bcm2835_pinctrl_probe(struct platform_device *pdev)
pc->pctl_desc = *pdata->pctl_desc;
pc->pctl_dev = devm_pinctrl_register(dev, &pc->pctl_desc, pc);
if (IS_ERR(pc->pctl_dev)) {
- gpiochip_remove(&pc->gpio_chip);
return PTR_ERR(pc->pctl_dev);
}
diff --git a/drivers/pinctrl/freescale/pinctrl-imx1-core.c b/drivers/pinctrl/freescale/pinctrl-imx1-core.c
index b7bd4ef9c0db..4a6bdaefa42f 100644
--- a/drivers/pinctrl/freescale/pinctrl-imx1-core.c
+++ b/drivers/pinctrl/freescale/pinctrl-imx1-core.c
@@ -547,14 +547,11 @@ static int imx1_pinctrl_parse_functions(struct device_node *np,
*/
static bool imx1_pinctrl_dt_is_flat_functions(struct device_node *np)
{
- struct device_node *function_np;
- struct device_node *pinctrl_np;
-
- for_each_child_of_node(np, function_np) {
+ for_each_child_of_node_scoped(np, function_np) {
if (of_property_present(function_np, "fsl,pins"))
return true;
- for_each_child_of_node(function_np, pinctrl_np) {
+ for_each_child_of_node_scoped(function_np, pinctrl_np) {
if (of_property_present(pinctrl_np, "fsl,pins"))
return false;
}
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 56be6e8d624e..0025add68495 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-2025 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,
@@ -60,6 +95,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 +108,19 @@ struct s32_pinctrl_mem_region {
};
/*
- * Holds pin configuration for GPIO's.
+ * 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
* @config: Pin settings
* @list: Linked list entry for each gpio pin
@@ -79,26 +132,40 @@ 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 pads
*/
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_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
*/
struct s32_pinctrl {
struct device *dev;
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
@@ -123,13 +190,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;
@@ -145,7 +212,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)
{
@@ -163,7 +230,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;
@@ -348,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;
- return s32_regmap_write(pctldev, offset, config);
+ scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock)
+ list_add(&no_free_ptr(gpio_pin)->list, &ipctl->gpio_configs);
+
+ 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,
@@ -476,8 +539,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:
@@ -641,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,
@@ -661,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);
}
}
@@ -675,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, &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)
@@ -701,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++) {
@@ -713,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;
}
@@ -728,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];
@@ -737,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;
}
@@ -920,9 +1456,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
#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)
@@ -948,6 +1486,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)
@@ -966,11 +1509,16 @@ int s32_pinctrl_probe(struct platform_device *pdev,
return dev_err_probe(&pdev->dev, ret,
"Fail to probe dt properties\n");
- 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),
- "could not register s32 pinctrl driver\n");
+ 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)
+ return dev_err_probe(&pdev->dev, ret,
+ "Could not register s32 pinctrl driver\n");
#ifdef CONFIG_PM_SLEEP
saved_context = &ipctl->saved_context;
@@ -982,7 +1530,47 @@ 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");
+
+ /* 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);
}
diff --git a/drivers/pinctrl/pinctrl-tb10x.c b/drivers/pinctrl/pinctrl-tb10x.c
index 3f581404a9b9..b7cbd69dd877 100644
--- a/drivers/pinctrl/pinctrl-tb10x.c
+++ b/drivers/pinctrl/pinctrl-tb10x.c
@@ -479,7 +479,7 @@ struct tb10x_port {
*/
struct tb10x_pinctrl {
struct pinctrl_dev *pctl;
- void *base;
+ void __iomem *base;
const struct tb10x_pinfuncgrp *pingroups;
unsigned int pinfuncgrpcnt;
unsigned int pinfuncnt;