[PATCH v9 6/7] pinctrl: s32cc: implement GPIO functionality
Bartosz Golaszewski
brgl at kernel.org
Mon May 4 07:49:26 PDT 2026
On Mon, 4 May 2026 15:11:47 +0200, Khristine Andreea Barbulescu
<khristineandreea.barbulescu at oss.nxp.com> said:
> From: Andrei Stefanescu <andrei.stefanescu at oss.nxp.com>
>
> Add basic GPIO functionality (request, free, get, set) for
> the existing pinctrl SIUL2 driver since the hardware for
> pinctrl&GPIO is tightly coupled.
>
> 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.
>
> 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
>
> Support both SIUL2 DT layouts:
> - legacy pinctrl-only binding
> - extended pinctrl/GPIO/irqchip binding
>
> Also, remove pinmux_ops which are no longer needed.
>
> Signed-off-by: Andrei Stefanescu <andrei.stefanescu at oss.nxp.com>
> Signed-off-by: Khristine Andreea Barbulescu <khristineandreea.barbulescu at oss.nxp.com>
> ---
> drivers/pinctrl/nxp/pinctrl-s32.h | 15 +-
> drivers/pinctrl/nxp/pinctrl-s32cc.c | 527 ++++++++++++++++++++++++----
> drivers/pinctrl/nxp/pinctrl-s32g2.c | 25 +-
> 3 files changed, 499 insertions(+), 68 deletions(-)
>
> diff --git a/drivers/pinctrl/nxp/pinctrl-s32.h b/drivers/pinctrl/nxp/pinctrl-s32.h
> index 8715befd5f05..d33f4d631dd6 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,23 @@ struct s32_pin_range {
> unsigned int end;
> };
>
> +/**
> + * struct s32_gpio_range - contiguous GPIO pin range within a SIUL2 module
> + * @gpio_base: first pinctrl pin number in the GPIO range
> + * @gpio_num: number of consecutive GPIO pins in the range
> + */
> +struct s32_gpio_range {
> + unsigned int gpio_base;
> + unsigned int gpio_num;
> +};
> +
> 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;
> };
>
> struct s32_pinctrl_soc_info {
> @@ -53,6 +65,7 @@ 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(_base, _num) { .gpio_base = _base, .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 27757f2c5570..2d9b14792cf4 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.
> */
> @@ -39,6 +39,14 @@
> #define S32_MSCR_ODE BIT(20)
> #define S32_MSCR_OBE BIT(21)
>
> +/* PGPDOs are 16bit registers that come in big endian
Use:
/*
* text
*/
style for comments please.
> + * 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 +80,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
> @@ -96,8 +116,11 @@ struct s32_pinctrl_context {
> * struct s32_pinctrl - private driver data
> * @dev: a pointer back to containing device
> * @pctl: a pointer to the pinctrl device structure
> + * @gc: a pointer to the gpio_chip
> * @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_configs: saved configurations for GPIO pins
> * @gpio_configs_lock: lock for the `gpio_configs` list
> * @saved_context: configuration saved over system sleep
> @@ -105,8 +128,11 @@ struct s32_pinctrl_context {
> struct s32_pinctrl {
> struct device *dev;
> struct pinctrl_dev *pctl;
> + struct gpio_chip gc;
> struct s32_pinctrl_mem_region *regions;
> struct s32_pinctrl_soc_info *info;
> + struct s32_gpio_regmaps *gpio_regmaps;
> + unsigned int num_gpio_regmaps;
> struct list_head gpio_configs;
> spinlock_t gpio_configs_lock;
> #ifdef CONFIG_PM_SLEEP
> @@ -379,67 +405,6 @@ static int s32_pmx_get_groups(struct pinctrl_dev *pctldev,
> return 0;
> }
>
> -static int s32_pmx_gpio_request_enable(struct pinctrl_dev *pctldev,
> - struct pinctrl_gpio_range *range,
> - unsigned int offset)
> -{
> - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> - struct gpio_pin_config *gpio_pin;
> - unsigned int config;
> - unsigned long flags;
> - int ret;
> -
> - ret = s32_regmap_read(pctldev, offset, &config);
These are all mmio regmaps, right? You probably don't need to check the return
values of regmap ops in this driver.
> - if (ret)
> - return ret;
> -
> - /* Save current configuration */
> - gpio_pin = kmalloc_obj(*gpio_pin);
> - if (!gpio_pin)
> - return -ENOMEM;
> -
> - gpio_pin->pin_id = offset;
> - 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;
> -
> - return s32_regmap_write(pctldev, offset, config);
> -}
> -
> -static void s32_pmx_gpio_disable_free(struct pinctrl_dev *pctldev,
> - struct pinctrl_gpio_range *range,
> - unsigned int offset)
> -{
> - struct s32_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev);
> - struct gpio_pin_config *gpio_pin, *tmp;
> - 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_del(&gpio_pin->list);
> - kfree(gpio_pin);
> - break;
> - }
> - }
> -
> -unlock:
> - spin_unlock_irqrestore(&ipctl->gpio_configs_lock, flags);
> -}
> -
> static int s32_pmx_gpio_set_direction(struct pinctrl_dev *pctldev,
> struct pinctrl_gpio_range *range,
> unsigned int offset,
> @@ -463,8 +428,6 @@ static const struct pinmux_ops s32_pmx_ops = {
> .get_function_name = s32_pmx_get_func_name,
> .get_function_groups = s32_pmx_get_groups,
> .set_mux = s32_pmx_set,
> - .gpio_request_enable = s32_pmx_gpio_request_enable,
> - .gpio_disable_free = s32_pmx_gpio_disable_free,
> .gpio_set_direction = s32_pmx_gpio_set_direction,
> };
>
> @@ -683,6 +646,397 @@ static const struct pinconf_ops s32_pinconf_ops = {
> .pin_config_group_dbg_show = s32_pinconf_group_dbg_show,
> };
>
> +static struct s32_pinctrl *to_s32_pinctrl(struct gpio_chip *chip)
> +{
> + return container_of(chip, struct s32_pinctrl, gc);
> +}
> +
> +static struct regmap *s32_gpio_get_pgpd_regmap(struct gpio_chip *chip,
> + unsigned int gpio,
> + bool output,
> + unsigned int *relative_pin)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> + const struct s32_gpio_range *range;
> + int i;
> +
> + for (i = 0; i < ipctl->num_gpio_regmaps; i++) {
> + range = ipctl->gpio_regmaps[i].range;
> + if (gpio >= range->gpio_base &&
> + gpio < range->gpio_base + range->gpio_num) {
> + if (relative_pin)
> + *relative_pin = gpio - range->gpio_base;
> + return output ? ipctl->gpio_regmaps[i].pgpdo :
> + ipctl->gpio_regmaps[i].pgpdi;
> + }
> + }
> +
> + return NULL;
> +}
> +
> +static int s32_gpio_request(struct gpio_chip *gc, unsigned int gpio)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(gc);
> + struct pinctrl_dev *pctldev = ipctl->pctl;
> + struct gpio_pin_config *gpio_pin;
> + unsigned int config;
> + int ret;
> +
> + ret = s32_regmap_read(pctldev, gpio, &config);
> + if (ret)
> + return ret;
> +
> + /* Save current configuration */
> + gpio_pin = kmalloc(sizeof(*gpio_pin), GFP_KERNEL);
> + if (!gpio_pin)
> + return -ENOMEM;
> +
> + gpio_pin->pin_id = gpio;
> + gpio_pin->config = config;
> + INIT_LIST_HEAD(&gpio_pin->list);
This is only needed on the list *head*, not on nodes you add.
> +
> + /* GPIO pin means SSS = 0 */
> + config &= ~S32_MSCR_SSS_MASK;
> +
> + ret = s32_regmap_write(pctldev, gpio, config);
> + if (ret) {
> + kfree(gpio_pin);
Maybe __free(kfree) and no_free_ptr()?
> + return ret;
> + }
> +
> + scoped_guard(spinlock_irqsave, &ipctl->gpio_configs_lock)
> + list_add(&gpio_pin->list, &ipctl->gpio_configs);
> +
> + return 0;
> +}
> +
> +static void s32_gpio_free(struct gpio_chip *gc, unsigned int gpio)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(gc);
> + struct pinctrl_dev *pctldev = ipctl->pctl;
> + struct gpio_pin_config *gpio_pin, *tmp;
> + int ret;
> +
> + guard(spinlock_irqsave)(&ipctl->gpio_configs_lock);
> +
> + list_for_each_entry_safe(gpio_pin, tmp, &ipctl->gpio_configs, list) {
> + if (gpio_pin->pin_id == gpio) {
> + list_del(&gpio_pin->list);
> + ret = s32_regmap_write(pctldev, gpio_pin->pin_id,
> + gpio_pin->config);
> + if (ret)
> + dev_warn(gc->parent, "Failed to restore config for pin %u\n", gpio);
> + kfree(gpio_pin);
> + return;
> + }
> + }
> +}
> +
> +static int s32_gpio_get_dir(struct gpio_chip *chip, unsigned int gpio)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> + unsigned int reg_value;
> + int ret;
> +
> + ret = s32_regmap_read(ipctl->pctl, gpio, ®_value);
> + if (ret)
> + return ret;
> +
> + if (!(reg_value & S32_MSCR_IBE))
> + return -EIO;
> +
> + return reg_value & S32_MSCR_OBE ? GPIO_LINE_DIRECTION_OUT :
> + GPIO_LINE_DIRECTION_IN;
> +}
> +
> +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 struct regmap *s32_gpio_get_regmap_offset_mask(struct gpio_chip *chip,
> + unsigned int gpio,
> + unsigned int *reg_offset,
> + u16 *mask,
> + bool output)
> +{
> + struct regmap *regmap;
> + unsigned int pad, relative_pin;
> +
> + regmap = s32_gpio_get_pgpd_regmap(chip, gpio, output, &relative_pin);
> + if (!regmap)
> + return NULL;
> +
> + *mask = s32_pin2mask(relative_pin);
> + pad = s32_pin2pad(relative_pin);
> +
> + *reg_offset = S32_PGPD(pad);
> +
> + return regmap;
> +}
> +
> +static int s32_gpio_set_val(struct gpio_chip *chip, unsigned int gpio,
> + int value)
> +{
> + unsigned int reg_offset;
> + struct regmap *regmap;
> + u16 mask;
> +
> + regmap = s32_gpio_get_regmap_offset_mask(chip, gpio, ®_offset,
> + &mask, true);
> + if (!regmap)
> + return -ENODEV;
> +
> + value = value ? mask : 0;
> +
> + return regmap_update_bits(regmap, reg_offset, mask, value);
> +}
> +
> +static int s32_gpio_set(struct gpio_chip *chip, unsigned int gpio,
> + int value)
> +{
> + return s32_gpio_set_val(chip, gpio, value);
> +}
> +
> +static int s32_gpio_get(struct gpio_chip *chip, unsigned int gpio)
> +{
> + unsigned int reg_offset, value;
> + struct regmap *regmap;
> + u16 mask;
> + int ret;
> +
> + regmap = s32_gpio_get_regmap_offset_mask(chip, gpio, ®_offset,
> + &mask, false);
> + if (!regmap)
> + return -EINVAL;
> +
> + ret = regmap_read(regmap, reg_offset, &value);
> + if (ret)
> + return ret;
> +
> + return !!(value & mask);
> +}
> +
> +static int s32_gpio_dir_out(struct gpio_chip *chip, unsigned int gpio,
> + int val)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> + int ret;
> +
> + ret = s32_gpio_set_val(chip, gpio, val);
> + if (ret)
> + return ret;
> +
> + return s32_pmx_gpio_set_direction(ipctl->pctl, NULL, gpio, false);
> +}
> +
> +static int s32_gpio_dir_in(struct gpio_chip *chip, unsigned int gpio)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> +
> + return s32_pmx_gpio_set_direction(ipctl->pctl, NULL, gpio, true);
> +}
> +
> +static bool s32_gpio_is_valid(struct gpio_chip *chip, unsigned int gpio)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> + const struct s32_pinctrl_soc_data *soc_data;
> + const struct pinctrl_pin_desc *pins;
> + int i;
> +
> + soc_data = ipctl->info->soc_data;
> + pins = ipctl->info->soc_data->pins;
> + for (i = 0; i < soc_data->npins && pins[i].number <= gpio; i++)
> + if (pins[i].number == gpio)
> + return true;
> +
> + return false;
> +}
> +
> +static int s32_init_valid_mask(struct gpio_chip *chip, unsigned long *mask,
> + unsigned int ngpios)
> +{
> + struct s32_pinctrl *ipctl = to_s32_pinctrl(chip);
> + const struct s32_pinctrl_soc_data *soc_data;
> + const struct pinctrl_pin_desc *pins;
> + int i;
> +
> + bitmap_zero(mask, ngpios);
> +
> + soc_data = ipctl->info->soc_data;
> + pins = soc_data->pins;
> +
> + for (i = 0; i < soc_data->npins; i++)
> + if (pins[i].number < ngpios)
> + bitmap_set(mask, pins[i].number, 1);
> +
> + return 0;
> +}
> +
> +static int s32_gpio_gen_names(struct device *dev, struct gpio_chip *gc,
> + unsigned int cnt, char **names, char *ch_index,
> + unsigned int *num_index)
> +{
> + unsigned int gpio;
> + unsigned int i;
> +
> + /*
> + * GPIO names follow the format P<port>_<pin>, for example:
> + * PA_00 .. PA_15, PB_00 .. PB_15, ..
> + *
> + * @num_index tracks the absolute GPIO index. The port letter is
> + * advanced whenever the index crosses a 16-pin boundary.
> + */
> + for (i = 0; i < cnt; i++) {
> + gpio = *num_index;
> + if (i != 0 && (gpio % 16) == 0)
> + (*ch_index)++;
> +
> + if (s32_gpio_is_valid(gc, gpio)) {
> + names[i] = devm_kasprintf(dev, GFP_KERNEL, "P%c_%02d",
> + *ch_index, gpio & 0xF);
> + if (!names[i])
> + return -ENOMEM;
> + }
> +
> + (*num_index)++;
> + }
> +
> + return 0;
> +}
> +
> +static int s32_gpio_populate_names(struct device *dev,
> + struct s32_pinctrl *ipctl)
> +{
> + const struct s32_pinctrl_soc_data *soc_data = ipctl->info->soc_data;
> + const struct s32_gpio_range *range;
> + unsigned int num_index = 0;
> + char ch_index = 'A';
> + char **names;
> + int i, ret;
> +
> + names = devm_kcalloc(dev, ipctl->gc.ngpio, sizeof(*names),
> + GFP_KERNEL);
> + if (!names)
> + return -ENOMEM;
> +
> + for (i = 0; i < soc_data->num_gpio_ranges; i++) {
> + range = &soc_data->gpio_ranges[i];
> +
> + if (range->gpio_base % 16 == 0)
> + num_index = 0;
> +
> + ret = s32_gpio_gen_names(dev, &ipctl->gc, range->gpio_num,
> + names + range->gpio_base,
> + &ch_index, &num_index);
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "Error setting SIUL2_%d names\n",
> + i);
> +
> + ch_index++;
> + }
> +
> + ipctl->gc.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)
> @@ -928,9 +1282,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
> #ifdef CONFIG_PM_SLEEP
> struct s32_pinctrl_context *saved_context;
> #endif
> + const struct s32_gpio_range *last_range;
> struct pinctrl_desc *s32_pinctrl_desc;
> struct s32_pinctrl_soc_info *info;
> struct s32_pinctrl *ipctl;
> + struct gpio_chip *gc;
> int ret;
>
> if (!soc_data || !soc_data->pins || !soc_data->npins)
> @@ -974,6 +1330,11 @@ int s32_pinctrl_probe(struct platform_device *pdev,
> return dev_err_probe(&pdev->dev, ret,
> "Fail to probe dt properties\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)
> @@ -997,5 +1358,43 @@ int s32_pinctrl_probe(struct platform_device *pdev,
>
> 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;
> +
> + gc = &ipctl->gc;
> + gc->parent = &pdev->dev;
> + gc->label = dev_name(&pdev->dev);
> + gc->base = -1;
> + /*
> + * In some cases, there is a gap between the SIUL GPIOs.
> + *
> + * gpio_ranges[] is expected to be sorted by increasing gpio_base.
> + * ngpio is derived from the last range and must cover the highest
> + * GPIO offset, even when gaps exist in the numbering.
> + */
> + last_range = &soc_data->gpio_ranges[soc_data->num_gpio_ranges - 1];
> + gc->ngpio = last_range->gpio_base + last_range->gpio_num;
> + ret = s32_gpio_populate_names(&pdev->dev, ipctl);
> + if (ret)
> + return ret;
> +
> + gc->set = s32_gpio_set;
> + gc->get = s32_gpio_get;
> + gc->set_config = gpiochip_generic_config;
> + gc->request = s32_gpio_request;
> + gc->free = s32_gpio_free;
> + gc->direction_output = s32_gpio_dir_out;
> + gc->direction_input = s32_gpio_dir_in;
> + gc->get_direction = s32_gpio_get_dir;
> + gc->init_valid_mask = s32_init_valid_mask;
> +
> + ret = devm_gpiochip_add_data(&pdev->dev, gc, ipctl);
> + if (ret)
> + return dev_err_probe(&pdev->dev, ret,
> + "Unable to add gpiochip\n");
> +
> + dev_info(&pdev->dev, "Initialized S32 GPIO functionality\n");
Don't do this, there's no reason for a driver to emit logs on success.
> +
> return 0;
> }
> diff --git a/drivers/pinctrl/nxp/pinctrl-s32g2.c b/drivers/pinctrl/nxp/pinctrl-s32g2.c
> index c49d28793b69..b1bf328371e2 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,36 @@ 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, 102),
> + S32_GPIO_RANGE(112, 79),
> +};
> +
> +/* 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),
> };
>
> static const struct of_device_id s32_pinctrl_of_match[] = {
> + {
> + .compatible = "nxp,s32g2-siul2-pinctrl-gpio",
> + .data = &s32g_pinctrl_data,
> + },
> {
> .compatible = "nxp,s32g2-siul2-pinctrl",
> - .data = &s32_pinctrl_data,
> + .data = &legacy_s32g_pinctrl_data,
> },
> { /* sentinel */ }
> };
> --
> 2.34.1
>
>
Bart
More information about the linux-arm-kernel
mailing list