mirror of
https://github.com/torvalds/linux.git
synced 2026-05-12 16:18:45 +02:00
Merge branch 'ib-scmi-pinctrl-gpio' into devel
This commit is contained in:
commit
ede3db74c3
59
Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml
Normal file
59
Documentation/devicetree/bindings/gpio/pin-control-gpio.yaml
Normal 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>;
|
||||
};
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
101
drivers/gpio/gpio-by-pinctrl.c
Normal file
101
drivers/gpio/gpio-by-pinctrl.c
Normal 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");
|
||||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user