mirror of
https://source.denx.de/u-boot/u-boot.git
synced 2026-05-05 12:46:14 +02:00
In the original code, I wrote a custom pin muxing parser but the
upstream device trees wouldn't accept something like that so it would
have complicated mergine the device tree files.
Use the standard device tree format with function and groups:
pinmux1: pinmux1 {
function = "f_gpio1";
groups = "grp_1", "grp_3";
};
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Acked-by: Linus Walleij <linusw@kernel.org>
Signed-off-by: Peng Fan <peng.fan@nxp.com>
323 lines
8.6 KiB
C
323 lines
8.6 KiB
C
// SPDX-License-Identifier: GPL-2.0+
|
|
/*
|
|
* Copyright 2026 Linaro Ltd.
|
|
*/
|
|
|
|
#include <dm.h>
|
|
#include <dm/device_compat.h>
|
|
#include <dm/devres.h>
|
|
#include <dm/pinctrl.h>
|
|
#include <scmi_agent.h>
|
|
#include <scmi_agent-uclass.h>
|
|
#include <scmi_protocols.h>
|
|
|
|
static const struct pinconf_param pinctrl_scmi_conf_params[] = {
|
|
{ "bias-bus-hold", PIN_CONFIG_BIAS_BUS_HOLD, 0},
|
|
{ "bias-disable", PIN_CONFIG_BIAS_DISABLE, 0 },
|
|
{ "bias-high-impedance", PIN_CONFIG_BIAS_HIGH_IMPEDANCE, 0 },
|
|
{ "bias-pull-down", PIN_CONFIG_BIAS_PULL_DOWN, 0 },
|
|
{ "bias-pull-pin-default", PIN_CONFIG_BIAS_PULL_PIN_DEFAULT, 0 },
|
|
{ "bias-pull-up", PIN_CONFIG_BIAS_PULL_UP, 0 },
|
|
{ "drive-open-drain", PIN_CONFIG_DRIVE_OPEN_DRAIN, 0 },
|
|
{ "drive-open-source", PIN_CONFIG_DRIVE_OPEN_SOURCE, 0 },
|
|
{ "drive-push-pull", PIN_CONFIG_DRIVE_PUSH_PULL, 0 },
|
|
{ "drive-strength", PIN_CONFIG_DRIVE_STRENGTH, 0 },
|
|
{ "input-debounce", PIN_CONFIG_INPUT_DEBOUNCE, 0 },
|
|
{ "input-enable", PIN_CONFIG_INPUT_ENABLE, 1 },
|
|
{ "input-schmitt", PIN_CONFIG_INPUT_SCHMITT, 0 },
|
|
{ "low-power-mode", PIN_CONFIG_LOW_POWER_MODE, 0 },
|
|
{ "output-mode", PIN_CONFIG_OUTPUT_ENABLE, 0 },
|
|
{ "output-value", PIN_CONFIG_OUTPUT, 0 },
|
|
{ "power-source", PIN_CONFIG_POWER_SOURCE, 0 },
|
|
{ "slew-rate", PIN_CONFIG_SLEW_RATE, 0 },
|
|
/* The SCMI spec also include "default", "pull-mode" and "input-value */
|
|
};
|
|
|
|
static int pinctrl_scmi_get_pins_count(struct udevice *dev)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
return priv->num_pins;
|
|
}
|
|
|
|
static int pinctrl_scmi_get_groups_count(struct udevice *dev)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
return priv->num_groups;
|
|
}
|
|
|
|
static int pinctrl_scmi_get_functions_count(struct udevice *dev)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
return priv->num_functions;
|
|
}
|
|
|
|
static const char *pinctrl_scmi_get_pin_name(struct udevice *dev, unsigned int selector)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
if (selector >= priv->num_pins)
|
|
return NULL;
|
|
|
|
return (const char *)priv->pin_info[selector].name;
|
|
}
|
|
|
|
static const char *pinctrl_scmi_get_group_name(struct udevice *dev, unsigned int selector)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
if (selector >= priv->num_groups)
|
|
return NULL;
|
|
|
|
return (const char *)priv->group_info[selector].name;
|
|
}
|
|
|
|
static const char *pinctrl_scmi_get_function_name(struct udevice *dev, unsigned int selector)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
if (selector >= priv->num_functions)
|
|
return NULL;
|
|
|
|
return (const char *)priv->function_info[selector].name;
|
|
}
|
|
|
|
static int pinctrl_scmi_get_function_id(struct udevice *dev, const char *function)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
int i;
|
|
|
|
if (!function)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < priv->num_functions; i++) {
|
|
if (strcmp(priv->function_info[i].name, function) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int pinctrl_scmi_get_group_id(struct udevice *dev, const char *group)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
int i;
|
|
|
|
if (!group)
|
|
return -EINVAL;
|
|
|
|
for (i = 0; i < priv->num_groups; i++) {
|
|
if (strcmp(priv->group_info[i].name, group) == 0)
|
|
return i;
|
|
}
|
|
|
|
return -EINVAL;
|
|
}
|
|
|
|
static int pinctrl_scmi_pinmux_set(struct udevice *dev, u32 pin, u32 function)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
if (pin >= priv->num_pins || function >= priv->num_functions)
|
|
return -EINVAL;
|
|
|
|
return scmi_pinctrl_set_function(dev, SCMI_PIN, pin, function);
|
|
}
|
|
|
|
static int pinctrl_scmi_pinmux_group_set(struct udevice *dev, u32 group, u32 function)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
|
|
if (group >= priv->num_groups || function >= priv->num_functions)
|
|
return -EINVAL;
|
|
|
|
return scmi_pinctrl_set_function(dev, SCMI_GROUP, group, function);
|
|
}
|
|
|
|
static int pinctrl_scmi_set_state(struct udevice *dev, struct udevice *config)
|
|
{
|
|
int function_id, group_id;
|
|
const char *function;
|
|
const char **groups;
|
|
int group_count;
|
|
int ret;
|
|
int i;
|
|
|
|
ret = dev_read_string_index(config, "function", 0, &function);
|
|
if (ret)
|
|
return ret;
|
|
|
|
function_id = pinctrl_scmi_get_function_id(dev, function);
|
|
if (function_id < 0)
|
|
return function_id;
|
|
|
|
group_count = dev_read_string_list(config, "groups", &groups);
|
|
if (group_count < 0)
|
|
return group_count;
|
|
|
|
for (i = 0; i < group_count; i++) {
|
|
group_id = pinctrl_scmi_get_group_id(dev, groups[i]);
|
|
if (group_id < 0)
|
|
return group_id;
|
|
ret = pinctrl_scmi_pinmux_group_set(dev, group_id, function_id);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int get_pin_muxing(struct udevice *dev, unsigned int selector,
|
|
char *buf, int size)
|
|
{
|
|
u32 value;
|
|
int ret;
|
|
|
|
ret = scmi_pinctrl_settings_get_one(dev, SCMI_PIN, selector,
|
|
SCMI_PIN_INPUT_VALUE, &value);
|
|
if (ret) {
|
|
dev_err(dev, "settings_get() failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
snprintf(buf, size, "%d", value);
|
|
return 0;
|
|
}
|
|
|
|
static int pinctrl_scmi_pinconf_set(struct udevice *dev, u32 pin, u32 param, u32 argument)
|
|
{
|
|
return scmi_pinctrl_settings_configure_one(dev, SCMI_PIN, pin, param, argument);
|
|
}
|
|
|
|
static int pinctrl_scmi_pinconf_group_set(struct udevice *dev, u32 group, u32 param, u32 argument)
|
|
{
|
|
return scmi_pinctrl_settings_configure_one(dev, SCMI_GROUP, group, param, argument);
|
|
}
|
|
|
|
static struct pinctrl_ops scmi_pinctrl_ops = {
|
|
.get_pins_count = pinctrl_scmi_get_pins_count,
|
|
.get_pin_name = pinctrl_scmi_get_pin_name,
|
|
|
|
.get_groups_count = pinctrl_scmi_get_groups_count,
|
|
.get_group_name = pinctrl_scmi_get_group_name,
|
|
|
|
.get_functions_count = pinctrl_scmi_get_functions_count,
|
|
.get_function_name = pinctrl_scmi_get_function_name,
|
|
|
|
.pinmux_set = pinctrl_scmi_pinmux_set,
|
|
.pinmux_group_set = pinctrl_scmi_pinmux_group_set,
|
|
|
|
.pinconf_num_params = ARRAY_SIZE(pinctrl_scmi_conf_params),
|
|
.pinconf_params = pinctrl_scmi_conf_params,
|
|
|
|
.pinconf_set = pinctrl_scmi_pinconf_set,
|
|
.pinconf_group_set = pinctrl_scmi_pinconf_group_set,
|
|
.set_state = pinctrl_scmi_set_state,
|
|
.get_pin_muxing = get_pin_muxing,
|
|
};
|
|
|
|
static int scmi_pinctrl_probe(struct udevice *dev)
|
|
{
|
|
struct pinctrl_scmi_priv *priv = dev_get_priv(dev);
|
|
int ret;
|
|
int i;
|
|
|
|
ret = devm_scmi_of_get_channel(dev);
|
|
if (ret) {
|
|
dev_err(dev, "get_channel() failed: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
ret = scmi_pinctrl_protocol_attrs(dev, &priv->num_pins,
|
|
&priv->num_groups,
|
|
&priv->num_functions);
|
|
if (ret) {
|
|
dev_err(dev, "failed to get protocol attributes: %d\n", ret);
|
|
return ret;
|
|
}
|
|
|
|
priv->pin_info = devm_kcalloc(dev, priv->num_pins,
|
|
sizeof(*priv->pin_info), GFP_KERNEL);
|
|
priv->group_info = devm_kcalloc(dev, priv->num_groups,
|
|
sizeof(*priv->group_info), GFP_KERNEL);
|
|
priv->function_info = devm_kcalloc(dev, priv->num_functions,
|
|
sizeof(*priv->function_info), GFP_KERNEL);
|
|
if (!priv->pin_info || !priv->group_info || !priv->function_info)
|
|
return -ENOMEM;
|
|
|
|
for (i = 0; i < priv->num_pins; i++) {
|
|
ret = scmi_pinctrl_attrs(dev, SCMI_PIN, i, NULL, NULL,
|
|
priv->pin_info[i].name);
|
|
if (ret)
|
|
return ret;
|
|
}
|
|
|
|
for (i = 0; i < priv->num_groups; i++) {
|
|
ret = scmi_pinctrl_attrs(dev, SCMI_GROUP, i, NULL,
|
|
&priv->group_info[i].num_pins,
|
|
priv->group_info[i].name);
|
|
if (ret) {
|
|
dev_err(dev, "loading group %d failed: %d\n", i, ret);
|
|
return ret;
|
|
}
|
|
priv->group_info[i].pins = devm_kcalloc(dev,
|
|
priv->group_info[i].num_pins,
|
|
sizeof(*priv->group_info[i].pins),
|
|
GFP_KERNEL);
|
|
if (!priv->group_info[i].pins)
|
|
return -ENOMEM;
|
|
|
|
ret = scmi_pinctrl_list_associations(dev, SCMI_GROUP, i,
|
|
priv->group_info[i].pins,
|
|
priv->group_info[i].num_pins);
|
|
if (ret) {
|
|
dev_err(dev, "list association %d failed for group: %d\n", i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
for (i = 0; i < priv->num_functions; i++) {
|
|
ret = scmi_pinctrl_attrs(dev, SCMI_FUNCTION, i, NULL,
|
|
&priv->function_info[i].num_groups,
|
|
priv->function_info[i].name);
|
|
if (ret) {
|
|
dev_err(dev, "loading function %d failed: %d\n", i, ret);
|
|
return ret;
|
|
}
|
|
priv->function_info[i].groups = devm_kcalloc(dev,
|
|
priv->function_info[i].num_groups,
|
|
sizeof(*priv->function_info[i].groups),
|
|
GFP_KERNEL);
|
|
if (!priv->function_info[i].groups)
|
|
return -ENOMEM;
|
|
|
|
ret = scmi_pinctrl_list_associations(dev, SCMI_FUNCTION, i,
|
|
priv->function_info[i].groups,
|
|
priv->function_info[i].num_groups);
|
|
if (ret) {
|
|
dev_err(dev, "list association %d failed for function: %d\n", i, ret);
|
|
return ret;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
U_BOOT_DRIVER(pinctrl_scmi) = {
|
|
.name = "scmi_pinctrl",
|
|
.id = UCLASS_PINCTRL,
|
|
.ops = &scmi_pinctrl_ops,
|
|
.probe = scmi_pinctrl_probe,
|
|
.priv_auto = sizeof(struct pinctrl_scmi_priv),
|
|
};
|
|
|
|
static struct scmi_proto_match match[] = {
|
|
{ .proto_id = SCMI_PROTOCOL_ID_PINCTRL },
|
|
{ /* Sentinel */ }
|
|
};
|
|
|
|
U_BOOT_SCMI_PROTO_DRIVER(pinctrl_scmi, match);
|
|
|