diff options
| author | Linus Walleij <linusw@kernel.org> | 2026-03-24 13:50:44 +0100 |
|---|---|---|
| committer | Linus Walleij <linusw@kernel.org> | 2026-03-24 13:50:44 +0100 |
| commit | ede3db74c3d07a1aa67e3eebf91c91409c09330e (patch) | |
| tree | e66830a5d99b7b440ef601a7b518becb46787e4d | |
| parent | 34006f77890d050e6d80cbee365b5d703c1140b4 (diff) | |
| parent | 7671f4949a6c9111234fdbcd577b227ace799f16 (diff) | |
Merge branch 'ib-scmi-pinctrl-gpio' into devel
| -rw-r--r-- | Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml | 59 | ||||
| -rw-r--r-- | drivers/firmware/arm_scmi/pinctrl.c | 2 | ||||
| -rw-r--r-- | drivers/gpio/Kconfig | 13 | ||||
| -rw-r--r-- | drivers/gpio/Makefile | 1 | ||||
| -rw-r--r-- | drivers/gpio/gpio-by-pinctrl.c | 101 | ||||
| -rw-r--r-- | drivers/pinctrl/core.c | 31 | ||||
| -rw-r--r-- | drivers/pinctrl/pinconf.h | 6 | ||||
| -rw-r--r-- | drivers/pinctrl/pinctrl-scmi.c | 46 | ||||
| -rw-r--r-- | include/linux/pinctrl/consumer.h | 9 |
9 files changed, 255 insertions, 13 deletions
diff --git a/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml b/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml new file mode 100644 index 000000000000..a05cd339253a --- /dev/null +++ b/Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml @@ -0,0 +1,59 @@ +# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause +%YAML 1.2 +--- +$id: http://devicetree.org/schemas/gpio/pin-control-gpio.yaml# +$schema: http://devicetree.org/meta-schemas/core.yaml# + +title: Pin control based generic GPIO controller + +description: + The pin control-based GPIO will facilitate a pin controller's ability + to drive electric lines high/low and other generic properties of a + pin controller to perform general-purpose one-bit binary I/O. + +maintainers: + - Dan Carpenter <dan.carpenter@linaro.org> + +properties: + compatible: + const: scmi-pinctrl-gpio + + gpio-controller: true + + "#gpio-cells": + const: 2 + + gpio-line-names: true + + gpio-ranges: true + + ngpios: true + +patternProperties: + "^.+-hog(-[0-9]+)?$": + type: object + + required: + - gpio-hog + +required: + - compatible + - gpio-controller + - "#gpio-cells" + - gpio-ranges + - ngpios + +additionalProperties: false + +examples: + - | + gpio { + compatible = "scmi-pinctrl-gpio"; + gpio-controller; + #gpio-cells = <2>; + ngpios = <4>; + gpio-line-names = "gpio_5_17", "gpio_5_20", "gpio_5_22", "gpio_2_1"; + gpio-ranges = <&scmi_pinctrl 0 30 4>; + pinctrl-names = "default"; + pinctrl-0 = <&keys_pins>; + }; diff --git a/drivers/firmware/arm_scmi/pinctrl.c b/drivers/firmware/arm_scmi/pinctrl.c index a020e23d7c49..42cb1aef1fe1 100644 --- a/drivers/firmware/arm_scmi/pinctrl.c +++ b/drivers/firmware/arm_scmi/pinctrl.c @@ -578,6 +578,8 @@ static int scmi_pinctrl_request_free(const struct scmi_protocol_handle *ph, tx->flags = cpu_to_le32(type); ret = ph->xops->do_xfer(ph, t); + if (ret == -EOPNOTSUPP) + ret = 0; ph->xops->xfer_put(ph, t); return ret; diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index b45fb799e36c..c631ecb01e07 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -246,6 +246,19 @@ config GPIO_BRCMSTB help Say yes here to enable GPIO support for Broadcom STB (BCM7XXX) SoCs. +config GPIO_BY_PINCTRL + tristate "GPIO support based on a pure pin control backend" + depends on GPIOLIB + help + Support for generic GPIO handling based on top of pin control. + Traditionally, firmware creates a GPIO interface or a pin + controller interface and we have a driver to support it. But + in SCMI, the pin control interface is generic and we can + create a simple GPIO device based on the pin control interface + without doing anything custom. + + This driver used to do GPIO over the ARM SCMI protocol. + config GPIO_CADENCE tristate "Cadence GPIO support" depends on OF_GPIO diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index c05f7d795c43..20d4a57afdaa 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -51,6 +51,7 @@ obj-$(CONFIG_GPIO_BD9571MWV) += gpio-bd9571mwv.o obj-$(CONFIG_GPIO_BLZP1600) += gpio-blzp1600.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o +obj-$(CONFIG_GPIO_BY_PINCTRL) += gpio-by-pinctrl.o obj-$(CONFIG_GPIO_CADENCE) += gpio-cadence.o obj-$(CONFIG_GPIO_CGBC) += gpio-cgbc.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o diff --git a/drivers/gpio/gpio-by-pinctrl.c b/drivers/gpio/gpio-by-pinctrl.c new file mode 100644 index 000000000000..ddfdc479d38a --- /dev/null +++ b/drivers/gpio/gpio-by-pinctrl.c @@ -0,0 +1,101 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// Copyright (C) 2026 Linaro Inc. +// Author: AKASHI takahiro <takahiro.akashi@linaro.org> + +#include <linux/errno.h> +#include <linux/gpio/driver.h> +#include <linux/mod_devicetable.h> +#include <linux/module.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/types.h> + +#include "gpiolib.h" + +static int pin_control_gpio_get_direction(struct gpio_chip *gc, unsigned int offset) +{ + unsigned long config; + int ret; + + config = PIN_CONFIG_OUTPUT_ENABLE; + ret = pinctrl_gpio_get_config(gc, offset, &config); + if (ret) + return ret; + if (config) + return GPIO_LINE_DIRECTION_OUT; + + return GPIO_LINE_DIRECTION_IN; +} + +static int pin_control_gpio_direction_output(struct gpio_chip *chip, + unsigned int offset, int val) +{ + return pinctrl_gpio_direction_output(chip, offset); +} + +static int pin_control_gpio_get(struct gpio_chip *chip, unsigned int offset) +{ + unsigned long config; + int ret; + + config = PIN_CONFIG_LEVEL; + ret = pinctrl_gpio_get_config(chip, offset, &config); + if (ret) + return ret; + + return !!config; +} + +static int pin_control_gpio_set(struct gpio_chip *chip, unsigned int offset, + int val) +{ + unsigned long config; + + config = pinconf_to_config_packed(PIN_CONFIG_LEVEL, val); + return pinctrl_gpio_set_config(chip, offset, config); +} + +static int pin_control_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct gpio_chip *chip; + + chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL); + if (!chip) + return -ENOMEM; + + chip->label = dev_name(dev); + chip->parent = dev; + chip->base = -1; + + chip->request = gpiochip_generic_request; + chip->free = gpiochip_generic_free; + chip->get_direction = pin_control_gpio_get_direction; + chip->direction_input = pinctrl_gpio_direction_input; + chip->direction_output = pin_control_gpio_direction_output; + chip->get = pin_control_gpio_get; + chip->set = pin_control_gpio_set; + chip->set_config = gpiochip_generic_config; + + return devm_gpiochip_add_data(dev, chip, NULL); +} + +static const struct of_device_id pin_control_gpio_match[] = { + { .compatible = "scmi-pinctrl-gpio" }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, pin_control_gpio_match); + +static struct platform_driver pin_control_gpio_driver = { + .probe = pin_control_gpio_probe, + .driver = { + .name = "pin-control-gpio", + .of_match_table = pin_control_gpio_match, + }, +}; +module_platform_driver(pin_control_gpio_driver); + +MODULE_AUTHOR("AKASHI Takahiro <takahiro.akashi@linaro.org>"); +MODULE_DESCRIPTION("Pinctrl based GPIO driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 254161d52da7..07a6b7fe1c9c 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -30,6 +30,7 @@ #include <linux/pinctrl/consumer.h> #include <linux/pinctrl/devinfo.h> #include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> #include <linux/pinctrl/pinctrl.h> #include "core.h" @@ -938,6 +939,36 @@ int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, } EXPORT_SYMBOL_GPL(pinctrl_gpio_set_config); +/** + * pinctrl_gpio_get_config() - Get the config for a given GPIO pin + * @gc: GPIO chip structure from the GPIO subsystem + * @offset: hardware offset of the GPIO relative to the controller + * @config: the configuration to query. On success it holds the result + * Return: 0 on success, negative errno otherwise + */ +int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, unsigned long *config) +{ + struct pinctrl_gpio_range *range; + struct pinctrl_dev *pctldev; + int ret, pin; + + ret = pinctrl_get_device_gpio_range(gc, offset, &pctldev, &range); + if (ret) + return ret; + + mutex_lock(&pctldev->mutex); + pin = gpio_to_pin(range, gc, offset); + ret = pin_config_get_for_pin(pctldev, pin, config); + mutex_unlock(&pctldev->mutex); + + if (ret) + return ret; + + *config = pinconf_to_config_argument(*config); + return 0; +} +EXPORT_SYMBOL_GPL(pinctrl_gpio_get_config); + static struct pinctrl_state *find_state(struct pinctrl *p, const char *name) { diff --git a/drivers/pinctrl/pinconf.h b/drivers/pinctrl/pinconf.h index 2880adef476e..659a781e2091 100644 --- a/drivers/pinctrl/pinconf.h +++ b/drivers/pinctrl/pinconf.h @@ -74,6 +74,12 @@ static inline int pinconf_set_config(struct pinctrl_dev *pctldev, unsigned int p return -ENOTSUPP; } +static inline int pin_config_get_for_pin(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + return -ENOTSUPP; +} + #endif #if defined(CONFIG_PINCONF) && defined(CONFIG_DEBUG_FS) diff --git a/drivers/pinctrl/pinctrl-scmi.c b/drivers/pinctrl/pinctrl-scmi.c index f4f296e07be5..f22be6b7b82a 100644 --- a/drivers/pinctrl/pinctrl-scmi.c +++ b/drivers/pinctrl/pinctrl-scmi.c @@ -251,15 +251,9 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, case PIN_CONFIG_MODE_LOW_POWER: *type = SCMI_PIN_LOW_POWER_MODE; break; - case PIN_CONFIG_LEVEL: - *type = SCMI_PIN_OUTPUT_VALUE; - break; case PIN_CONFIG_OUTPUT_ENABLE: *type = SCMI_PIN_OUTPUT_MODE; break; - case PIN_CONFIG_OUTPUT_IMPEDANCE_OHMS: - *type = SCMI_PIN_OUTPUT_VALUE; - break; case PIN_CONFIG_POWER_SOURCE: *type = SCMI_PIN_POWER_SOURCE; break; @@ -276,6 +270,28 @@ static int pinctrl_scmi_map_pinconf_type(enum pin_config_param param, return 0; } +static int pinctrl_scmi_map_pinconf_type_get(enum pin_config_param param, + enum scmi_pinctrl_conf_type *type) +{ + if (param == PIN_CONFIG_LEVEL) { + *type = SCMI_PIN_INPUT_VALUE; + return 0; + } + + return pinctrl_scmi_map_pinconf_type(param, type); +} + +static int pinctrl_scmi_map_pinconf_type_set(enum pin_config_param param, + enum scmi_pinctrl_conf_type *type) +{ + if (param == PIN_CONFIG_LEVEL) { + *type = SCMI_PIN_OUTPUT_VALUE; + return 0; + } + + return pinctrl_scmi_map_pinconf_type(param, type); +} + static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, unsigned long *config) { @@ -290,7 +306,7 @@ static int pinctrl_scmi_pinconf_get(struct pinctrl_dev *pctldev, config_type = pinconf_to_config_param(*config); - ret = pinctrl_scmi_map_pinconf_type(config_type, &type); + ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type); if (ret) return ret; @@ -345,7 +361,7 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, unsigned long *configs, unsigned int num_configs) { - int i, ret; + int i, cnt, ret; struct scmi_pinctrl *pmx = pinctrl_dev_get_drvdata(pctldev); enum scmi_pinctrl_conf_type config_type[SCMI_NUM_CONFIGS]; u32 config_value[SCMI_NUM_CONFIGS]; @@ -361,17 +377,21 @@ static int pinctrl_scmi_pinconf_set(struct pinctrl_dev *pctldev, if (ret) return ret; + cnt = 0; for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); - ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); + if (param == PIN_CONFIG_PERSIST_STATE) + continue; + ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[cnt]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; } - p_config_value[i] = pinconf_to_config_argument(configs[i]); + p_config_value[cnt] = pinconf_to_config_argument(configs[i]); + cnt++; } - ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, num_configs, + ret = pinctrl_ops->settings_conf(pmx->ph, pin, PIN_TYPE, cnt, p_config_type, p_config_value); if (ret) dev_err(pmx->dev, "Error parsing config %d\n", ret); @@ -405,7 +425,7 @@ static int pinctrl_scmi_pinconf_group_set(struct pinctrl_dev *pctldev, for (i = 0; i < num_configs; i++) { param = pinconf_to_config_param(configs[i]); - ret = pinctrl_scmi_map_pinconf_type(param, &p_config_type[i]); + ret = pinctrl_scmi_map_pinconf_type_set(param, &p_config_type[i]); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); goto free_config; @@ -440,7 +460,7 @@ static int pinctrl_scmi_pinconf_group_get(struct pinctrl_dev *pctldev, return -EINVAL; config_type = pinconf_to_config_param(*config); - ret = pinctrl_scmi_map_pinconf_type(config_type, &type); + ret = pinctrl_scmi_map_pinconf_type_get(config_type, &type); if (ret) { dev_err(pmx->dev, "Error map pinconf_type %d\n", ret); return ret; diff --git a/include/linux/pinctrl/consumer.h b/include/linux/pinctrl/consumer.h index 63ce16191eb9..11b8f0b8da0c 100644 --- a/include/linux/pinctrl/consumer.h +++ b/include/linux/pinctrl/consumer.h @@ -35,6 +35,8 @@ int pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset); int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config); +int pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config); struct pinctrl * __must_check pinctrl_get(struct device *dev); void pinctrl_put(struct pinctrl *p); @@ -102,6 +104,13 @@ pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset) } static inline int +pinctrl_gpio_get_config(struct gpio_chip *gc, unsigned int offset, + unsigned long *config) +{ + return 0; +} + +static inline int pinctrl_gpio_set_config(struct gpio_chip *gc, unsigned int offset, unsigned long config) { |
