blob: fa31b573f31ed01d90126aaf0c51df2324358ed8 [file]
// 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);