Merge branch 'ib-scmi-pinctrl-gpio' into devel

This commit is contained in:
Linus Walleij 2026-03-24 13:50:44 +01:00
commit ede3db74c3
9 changed files with 255 additions and 13 deletions

View File

@ -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>;
};

View File

@ -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;

View File

@ -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

View File

@ -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

View File

@ -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");

View File

@ -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)
{

View File

@ -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)

View File

@ -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;

View File

@ -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);
@ -101,6 +103,13 @@ pinctrl_gpio_direction_output(struct gpio_chip *gc, unsigned int offset)
return 0;
}
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)