diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile index 6129726f817..761d89a1161 100644 --- a/drivers/firmware/scmi/Makefile +++ b/drivers/firmware/scmi/Makefile @@ -5,5 +5,6 @@ obj-$(CONFIG_SCMI_AGENT_SMCCC) += smccc_agent.o obj-$(CONFIG_SCMI_AGENT_MAILBOX) += mailbox_agent.o obj-$(CONFIG_SCMI_AGENT_OPTEE) += optee_agent.o obj-$(CONFIG_SCMI_POWER_DOMAIN) += pwdom.o +obj-$(CONFIG_PINCTRL_SCMI) += pinctrl.o obj-$(CONFIG_SANDBOX) += sandbox-scmi_agent.o sandbox-scmi_devices.o obj-y += vendors/imx/ diff --git a/drivers/firmware/scmi/pinctrl.c b/drivers/firmware/scmi/pinctrl.c new file mode 100644 index 00000000000..47f7a8ad9b8 --- /dev/null +++ b/drivers/firmware/scmi/pinctrl.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Linaro Ltd. + */ + +#define LOG_CATEGORY UCLASS_PINCTRL + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int map_config_param_to_scmi(u32 config_param) +{ + switch (config_param) { + case PIN_CONFIG_BIAS_BUS_HOLD: + return SCMI_PIN_BIAS_BUS_HOLD; + case PIN_CONFIG_BIAS_DISABLE: + return SCMI_PIN_BIAS_DISABLE; + case PIN_CONFIG_BIAS_HIGH_IMPEDANCE: + return SCMI_PIN_BIAS_HIGH_IMPEDANCE; + case PIN_CONFIG_BIAS_PULL_DOWN: + return SCMI_PIN_BIAS_PULL_DOWN; + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + return SCMI_PIN_BIAS_PULL_DEFAULT; + case PIN_CONFIG_BIAS_PULL_UP: + return SCMI_PIN_BIAS_PULL_UP; + case PIN_CONFIG_DRIVE_OPEN_DRAIN: + return SCMI_PIN_DRIVE_OPEN_DRAIN; + case PIN_CONFIG_DRIVE_OPEN_SOURCE: + return SCMI_PIN_DRIVE_OPEN_SOURCE; + case PIN_CONFIG_DRIVE_PUSH_PULL: + return SCMI_PIN_DRIVE_PUSH_PULL; + case PIN_CONFIG_DRIVE_STRENGTH: + return SCMI_PIN_DRIVE_STRENGTH; + case PIN_CONFIG_INPUT_DEBOUNCE: + return SCMI_PIN_INPUT_DEBOUNCE; + case PIN_CONFIG_INPUT_ENABLE: + return SCMI_PIN_INPUT_MODE; + case PIN_CONFIG_INPUT_SCHMITT: + return SCMI_PIN_INPUT_SCHMITT; + case PIN_CONFIG_LOW_POWER_MODE: + return SCMI_PIN_LOW_POWER_MODE; + case PIN_CONFIG_OUTPUT_ENABLE: + return SCMI_PIN_OUTPUT_MODE; + case PIN_CONFIG_OUTPUT: + return SCMI_PIN_OUTPUT_VALUE; + case PIN_CONFIG_POWER_SOURCE: + return SCMI_PIN_POWER_SOURCE; + case PIN_CONFIG_SLEW_RATE: + return SCMI_PIN_SLEW_RATE; + } + + return -EINVAL; +} + +int scmi_pinctrl_protocol_attrs(struct udevice *dev, int *num_pins, + int *num_groups, int *num_functions) +{ + struct scmi_pinctrl_protocol_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PROTOCOL_ATTRIBUTES, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + if (num_groups) + *num_groups = FIELD_GET(GENMASK(31, 16), out.attr_low); + if (num_pins) + *num_pins = FIELD_GET(GENMASK(15, 0), out.attr_low); + if (num_functions) + *num_functions = FIELD_GET(GENMASK(15, 0), out.attr_high); + + return 0; +} + +int scmi_pinctrl_attrs(struct udevice *dev, enum select_type select_type, + unsigned int selector, bool *gpio, unsigned int *count, + char *name) +{ + struct scmi_pinctrl_attrs_in in; + struct scmi_pinctrl_attrs_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_ATTRIBUTES, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + in.select_type = select_type; + in.id = selector; + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + if (gpio) + *gpio = FIELD_GET(BIT(17), out.attr); + if (count) + *count = FIELD_GET(GENMASK(15, 0), out.attr); + if (name) + strncpy(name, out.name, sizeof(out.name)); + + return 0; +} + +int scmi_pinctrl_list_associations(struct udevice *dev, + enum select_type select_type, + unsigned int selector, + unsigned short *output, + unsigned short num_out) +{ + struct scmi_pinctrl_list_associations_in in; + struct scmi_pinctrl_list_associations_out *out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_LIST_ASSOCIATIONS, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + }; + size_t out_sz = sizeof(*out) + num_out * sizeof(out->array[0]); + unsigned int count; + int ret = -EINVAL; + + out = kzalloc(out_sz, GFP_KERNEL); + if (!out) + return -ENOMEM; + + msg.out_msg = (u8 *)out; + msg.out_msg_sz = out_sz; + in.select_type = select_type; + in.id = selector; + in.index = 0; + + while (num_out > 0) { + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + goto free; + if (out->status) { + ret = scmi_to_linux_errno(out->status); + goto free; + } + + count = FIELD_GET(GENMASK(11, 0), out->flags); + if (count > num_out) + return -EINVAL; + memcpy(&output[in.index], out->array, count * sizeof(u16)); + num_out -= count; + in.index += count; + } +free: + kfree(out); + return ret; +} + +#define SCMI_PINCTRL_CONFIG_SETTINGS_FUNCTION -2u + +int scmi_pinctrl_settings_get_one(struct udevice *dev, enum select_type select_type, + unsigned int selector, + u32 config_type, u32 *value) +{ + struct scmi_pinctrl_settings_get_in in; + struct scmi_pinctrl_settings_get_out *out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_SETTINGS_GET, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + }; + size_t out_sz = sizeof(*out) + (sizeof(u32) * 2); + u32 num_configs; + int ret; + + if (config_type == SCMI_PINCTRL_CONFIG_SETTINGS_ALL) { + /* FIXME: implement */ + return -EIO; + } + + out = kzalloc(out_sz, GFP_KERNEL); + if (!out) + return -ENOMEM; + + msg.out_msg = (u8 *)out; + msg.out_msg_sz = out_sz; + in.id = selector; + in.attr = 0; + if (config_type == SCMI_PINCTRL_CONFIG_SETTINGS_FUNCTION) + in.attr = FIELD_PREP(GENMASK(19, 18), 2); + in.attr |= FIELD_PREP(GENMASK(17, 16), select_type); + if (config_type != SCMI_PINCTRL_CONFIG_SETTINGS_FUNCTION) + in.attr |= FIELD_PREP(GENMASK(7, 0), config_type); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + goto free; + if (out->status) { + ret = scmi_to_linux_errno(out->status); + goto free; + } + num_configs = FIELD_GET(GENMASK(7, 0), out->num_configs); + if (out->num_configs == 0) { + *value = out->function_selected; + goto free; + } + if (num_configs != 1) { + ret = -EINVAL; + goto free; + } + + *value = out->configs[1]; +free: + kfree(out); + return ret; +} + +static int scmi_pinctrl_settings_configure_helper(struct udevice *dev, + enum select_type select_type, + unsigned int selector, + u32 function_id, + u16 num_configs, u32 *configs) +{ + struct scmi_pinctrl_settings_configure_in *in; + struct scmi_pinctrl_settings_configure_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_SETTINGS_CONFIGURE, + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + size_t in_sz = sizeof(*in) + (num_configs * sizeof(u32) * 2); + int ret; + + in = kzalloc(in_sz, GFP_KERNEL); + if (!in) + return -ENOMEM; + + msg.in_msg = (u8 *)in; + msg.in_msg_sz = in_sz; + in->id = selector; + in->function_id = function_id; + in->attr = 0; + in->attr |= FIELD_PREP(GENMASK(9, 2), num_configs); + in->attr |= FIELD_PREP(GENMASK(1, 0), select_type); + memcpy(in->configs, configs, num_configs * sizeof(u32) * 2); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + goto free; + if (out.status) { + ret = scmi_to_linux_errno(out.status); + goto free; + } +free: + kfree(in); + return ret; +} + +int scmi_pinctrl_settings_configure(struct udevice *dev, enum select_type select_type, + unsigned int selector, u16 num_configs, + u32 *configs) +{ + return scmi_pinctrl_settings_configure_helper(dev, select_type, + selector, + SCMI_PINCTRL_FUNCTION_NONE, + num_configs, configs); +} + +int scmi_pinctrl_settings_configure_one(struct udevice *dev, enum select_type select_type, + unsigned int selector, + u32 param, u32 argument) +{ + u32 config_value[2]; + int scmi_config; + + /* see stmfx_pinctrl_conf_set() */ + scmi_config = map_config_param_to_scmi(param); + if (scmi_config < 0) + return scmi_config; + + config_value[0] = scmi_config; + config_value[1] = argument; + + return scmi_pinctrl_settings_configure(dev, select_type, selector, 1, + &config_value[0]); +} + +int scmi_pinctrl_set_function(struct udevice *dev, enum select_type select_type, + unsigned int selector, u32 function_id) +{ + return scmi_pinctrl_settings_configure_helper(dev, select_type, selector, + function_id, 0, NULL); +} + +int scmi_pinctrl_request(struct udevice *dev, enum select_type select_type, + unsigned int selector) +{ + struct scmi_pinctrl_request_in in; + struct scmi_pinctrl_request_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_REQUEST, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + in.id = selector; + in.flags = FIELD_PREP(GENMASK(1, 0), select_type); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + return 0; +} + +int scmi_pinctrl_release(struct udevice *dev, enum select_type select_type, + unsigned int selector) +{ + struct scmi_pinctrl_release_in in; + struct scmi_pinctrl_release_out out; + struct scmi_msg msg = { + .protocol_id = SCMI_PROTOCOL_ID_PINCTRL, + .message_id = SCMI_PINCTRL_RELEASE, + .in_msg = (u8 *)&in, + .in_msg_sz = sizeof(in), + .out_msg = (u8 *)&out, + .out_msg_sz = sizeof(out), + }; + int ret; + + in.id = selector; + in.flags = FIELD_PREP(GENMASK(1, 0), select_type); + + ret = devm_scmi_process_msg(dev, &msg); + if (ret) + return ret; + if (out.status) + return scmi_to_linux_errno(out.status); + + return 0; +} + diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c index ad825d66da2..cd458a7f458 100644 --- a/drivers/firmware/scmi/scmi_agent-uclass.c +++ b/drivers/firmware/scmi/scmi_agent-uclass.c @@ -106,7 +106,7 @@ struct udevice *scmi_get_protocol(struct udevice *dev, proto = priv->voltagedom_dev; break; #endif -#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) +#if IS_ENABLED(CONFIG_PINCTRL_SCMI) || IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) case SCMI_PROTOCOL_ID_PINCTRL: proto = priv->pinctrl_dev; break; @@ -179,7 +179,7 @@ static int scmi_add_protocol(struct udevice *dev, priv->voltagedom_dev = proto; break; #endif -#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) +#if IS_ENABLED(CONFIG_PINCTRL_SCMI) || IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) case SCMI_PROTOCOL_ID_PINCTRL: priv->pinctrl_dev = proto; break; diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig index 73dd5ff4a79..d9d4f7ceb83 100644 --- a/drivers/pinctrl/Kconfig +++ b/drivers/pinctrl/Kconfig @@ -291,6 +291,15 @@ config PINCTRL_SANDBOX Currently, this driver actually does nothing but print debug messages when pinctrl operations are invoked. +config PINCTRL_SCMI + bool "Support SCMI pin controllers" + depends on PINCTRL_FULL && SCMI_FIRMWARE + help + This is for pinctrl over the SCMI protocol. This allows the + initial pin configuration to be set up from the device tree. The + gpio_scmi driver is built on top of this driver if GPIO is + required. + config PINCTRL_SINGLE bool "Single register pin-control and pin-multiplex driver" depends on DM diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile index 4fb6cef3113..29fb9b484d0 100644 --- a/drivers/pinctrl/Makefile +++ b/drivers/pinctrl/Makefile @@ -29,6 +29,7 @@ obj-$(CONFIG_PINCTRL_MSCC) += mscc/ obj-$(CONFIG_ARCH_MVEBU) += mvebu/ obj-$(CONFIG_ARCH_NEXELL) += nexell/ obj-$(CONFIG_PINCTRL_QE) += pinctrl-qe-io.o +obj-$(CONFIG_PINCTRL_SCMI) += pinctrl-scmi.o obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o obj-$(CONFIG_PINCTRL_STI) += pinctrl-sti.o obj-$(CONFIG_PINCTRL_STM32) += pinctrl_stm32.o diff --git a/drivers/pinctrl/pinctrl-scmi.c b/drivers/pinctrl/pinctrl-scmi.c new file mode 100644 index 00000000000..63d4f8ffeb5 --- /dev/null +++ b/drivers/pinctrl/pinctrl-scmi.c @@ -0,0 +1,365 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2026 Linaro Ltd. + */ + +#include +#include +#include +#include +#include +#include +#include + +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 bool valid_selector(struct udevice *dev, enum select_type select_type, u32 selector) +{ + struct pinctrl_scmi_priv *priv = dev_get_priv(dev); + + if (select_type == SCMI_PIN) + return selector < priv->num_pins; + if (select_type == SCMI_GROUP) + return selector < priv->num_groups; + if (select_type == SCMI_FUNCTION) + return selector < priv->num_functions; + + return false; +} + +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_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) +{ + struct pinctrl_scmi_priv *priv = dev_get_priv(dev); + /* batch the setup into 20 lines at a go (there are 5 u32s in a config) */ + const int batch_count = 20 * 5; + u32 prev_type = -1u; + u32 prev_selector; + u32 *configs; + const u32 *prop; + int offset, cnt, len; + int ret = 0; + + prop = dev_read_prop(config, "pinmux", &len); + if (!prop) + return 0; + + if (len % sizeof(u32) * 5) { + dev_err(dev, "invalid pin configuration: len=%d\n", len); + return -FDT_ERR_BADSTRUCTURE; + } + + configs = kcalloc(batch_count, sizeof(u32), GFP_KERNEL); + if (!configs) + return -ENOMEM; + + offset = 0; + cnt = 0; + while (offset + 4 < len / sizeof(u32)) { + u32 select_type = fdt32_to_cpu(prop[offset]); + u32 selector = fdt32_to_cpu(prop[offset + 1]); + u32 function = fdt32_to_cpu(prop[offset + 2]); + u32 config_type = fdt32_to_cpu(prop[offset + 3]); + u32 config_value = fdt32_to_cpu(prop[offset + 4]); + + if (select_type > SCMI_GROUP || + !valid_selector(dev, select_type, selector) || + (function != SCMI_PINCTRL_FUNCTION_NONE && + function > priv->num_functions)) { + dev_err(dev, "invalid pinctrl data (%u %u %u %u %u)\n", + select_type, selector, function, config_type, + config_value); + ret = -EINVAL; + goto free; + } + + if (function != SCMI_PINCTRL_FUNCTION_NONE) { + if (cnt) { + ret = scmi_pinctrl_settings_configure(dev, + prev_type, + prev_selector, + cnt / 2, configs); + if (ret) + goto free; + prev_type = -1u; + cnt = 0; + } + scmi_pinctrl_set_function(dev, select_type, selector, function); + offset += 5; + continue; + } + + if (cnt == batch_count) + goto set; + + if (prev_type == -1u) + goto store; + + if (select_type == prev_type && selector == prev_selector) + goto store; +set: + ret = scmi_pinctrl_settings_configure(dev, prev_type, prev_selector, + cnt / 2, configs); + if (ret) + goto free; + cnt = 0; +store: + prev_type = select_type; + prev_selector = selector; + configs[cnt++] = config_type; + configs[cnt++] = config_value; + offset += 5; + } + + if (cnt) + ret = scmi_pinctrl_settings_configure(dev, prev_type, prev_selector, + cnt / 2, configs); +free: + kfree(configs); + if (ret) + dev_err(dev, "set_state() failed: %d\n", ret); + + return ret; +} + +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); + diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h index 9b36d3ae67b..c40b448bcba 100644 --- a/include/scmi_agent-uclass.h +++ b/include/scmi_agent-uclass.h @@ -52,7 +52,7 @@ struct scmi_agent_priv { #if IS_ENABLED(CONFIG_DM_REGULATOR_SCMI) struct udevice *voltagedom_dev; #endif -#if IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) +#if IS_ENABLED(CONFIG_PINCTRL_SCMI) || IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) struct udevice *pinctrl_dev; #endif #if IS_ENABLED(CONFIG_SCMI_ID_VENDOR_80) diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h index d5175551490..a8fd0a5a729 100644 --- a/include/scmi_protocols.h +++ b/include/scmi_protocols.h @@ -1147,4 +1147,360 @@ struct scmi_perf_in { struct scmi_perf_out { s32 status; }; + +#define SCMI_PIN_NAME_LEN 16 + +struct pin_info { + char name[SCMI_PIN_NAME_LEN]; +}; + +struct group_info { + char name[SCMI_PIN_NAME_LEN]; + u16 *pins; + u32 num_pins; +}; + +struct function_info { + char name[SCMI_PIN_NAME_LEN]; + u16 *groups; + u32 num_groups; +}; + +/* This is used by both the SCMI pinctrl and gpio drivers */ +struct pinctrl_scmi_priv { + int num_pins; + struct pin_info *pin_info; + int num_groups; + struct group_info *group_info; + int num_functions; + struct function_info *function_info; +}; + +/* SCMI Pinctrl selector type */ +enum select_type { + SCMI_PIN, + SCMI_GROUP, + SCMI_FUNCTION, +}; + +/** + * struct scmi_pinctrl_protocol_attrs_out - Response to SCMI_PROTOCOL_ATTRIBUTES + * command. + * @status: SCMI command status + * @attr_low: Number of pins and groups + * @attr_high: Number of functions + */ +struct scmi_pinctrl_protocol_attrs_out { + s32 status; + u32 attr_low; + u32 attr_high; +}; + +/** + * struct scmi_pinctrl_attrs_in - Parameters for SCMI_PINCTRL_ATTRIBUTES command + * @id: Identifier for pin, group or function + * @select_type: Pin, group or function + */ +struct scmi_pinctrl_attrs_in { + u32 id; + u32 select_type; +}; + +/** + * struct scmi_pinctrl_attrs_out - Response to SCMI_PINCTRL_ATTRIBUTES command + * @status: SCMI command status + * @attr: GPIO, number of pins or groups + * @name: Name of pin, group or function + */ +struct scmi_pinctrl_attrs_out { + s32 status; + u32 attr; + u8 name[SCMI_PIN_NAME_LEN]; +}; + +/** + * struct scmi_pinctrl_list_associations_in - Parameters for + * SCMI_PINCTRL_LIST_ASSOCIATIONS command + * @id: Identifier for group or function + * @select_type: Group or function + * @index: Index within the group or function + */ +struct scmi_pinctrl_list_associations_in { + u32 id; + u32 select_type; + u32 index; +}; + +/** + * struct scmi_pinctrl_list_associations_out - Response to + * SCMI_PINCTRL_LIST_ASSOCIATIONS command + * @status: SCMI command status + * @flags: Number of items returned and number still remaining + * @array: List of groups or pins + */ +struct scmi_pinctrl_list_associations_out { + s32 status; + u32 flags; + u16 array[]; +}; + +/** + * struct scmi_pinctrl_settings_get_in - Parameters for + * SCMI_PINCTRL_SETTINGS_GET command + * @id: Identifier for pin or group + * @attr: Config flag: one setting, function or all settings + * Selector: Pin or Group + * Skip: Number of config types to skip + * Config type: Type of config to read + */ +struct scmi_pinctrl_settings_get_in { + u32 id; + u32 attr; +}; + +#define SCMI_PINCTRL_CONFIG_SETTINGS_ALL -2u /* This is an internal magic number */ +#define SCMI_PINCTRL_FUNCTION_NONE 0xFFFFFFFF + +/** + * struct scmi_pinctrl_settings_get_out - Response to SCMI_PINCTRL_SETTINGS_GET + * command + * @status: SCMI command status + * @function_selected: The function enabled by the pin or group + * @num_configs: The number of settings returned and number still remaining + * @configs: The list of config data + */ +struct scmi_pinctrl_settings_get_out { + s32 status; + u32 function_selected; + u32 num_configs; + u32 configs[]; +}; + +/** + * struct scmi_pinctrl_settings_configure_in - Parameters for + * SCMI_PINCTRL_SETTINGS_CONFIGURE command + * @id: Identifier for pin or group + * @function_id: The function to enable for this pin or group (optional) + * @attr: Function id: Set the function or not + * Number of configs to set + * Selector: pin or group + * @configs: List of config type value pairs + */ +struct scmi_pinctrl_settings_configure_in { + u32 id; + u32 function_id; + u32 attr; + u32 configs[]; +}; + +/** + * struct scmi_pinctrl_settings_configure_out - Response to + * SCMI_PINCTRL_SETTINGS_CONFIGURE command + * @status: SCMI command status + */ +struct scmi_pinctrl_settings_configure_out { + s32 status; +}; + +/** + * struct scmi_pinctrl_request_in - Parameters for SCMI_PINCTRL_REQUEST command + * @id: Identifier for pin or group + * @flags: Pin, group or function + */ +struct scmi_pinctrl_request_in { + u32 id; + u32 flags; +}; + +/** + * struct scmi_pinctrl_request_out - Response to SCMI_PINCTRL_REQUEST command + * @status: SCMI command status + */ +struct scmi_pinctrl_request_out { + s32 status; +}; + +/** + * struct scmi_pinctrl_release_in - Parameters for SCMI_PINCTRL_RELEASE command + * @id: Identifier for pin or group + * @flags: Pin, group or function + */ +struct scmi_pinctrl_release_in { + u32 id; + u32 flags; +}; + +/** + * struct scmi_pinctrl_release_out - Response to SCMI_PINCTRL_RELEASE command + * @status: SCMI command status + */ +struct scmi_pinctrl_release_out { + s32 status; +}; + +/* SCMI Pinctrl Config Types */ +enum scmi_config_type { + SCMI_PIN_DEFUALT = 0, + SCMI_PIN_BIAS_BUS_HOLD = 1, + SCMI_PIN_BIAS_DISABLE = 2, + SCMI_PIN_BIAS_HIGH_IMPEDANCE = 3, + SCMI_PIN_BIAS_PULL_UP = 4, + SCMI_PIN_BIAS_PULL_DEFAULT = 5, + SCMI_PIN_BIAS_PULL_DOWN = 6, + SCMI_PIN_DRIVE_OPEN_DRAIN = 7, + SCMI_PIN_DRIVE_OPEN_SOURCE = 8, + SCMI_PIN_DRIVE_PUSH_PULL = 9, + SCMI_PIN_DRIVE_STRENGTH = 10, + SCMI_PIN_INPUT_DEBOUNCE = 11, + SCMI_PIN_INPUT_MODE = 12, + SCMI_PIN_PULL_MODE = 13, + SCMI_PIN_INPUT_VALUE = 14, + SCMI_PIN_INPUT_SCHMITT = 15, + SCMI_PIN_LOW_POWER_MODE = 16, + SCMI_PIN_OUTPUT_MODE = 17, + SCMI_PIN_OUTPUT_VALUE = 18, + SCMI_PIN_POWER_SOURCE = 19, + SCMI_PIN_SLEW_RATE = 20, +}; + +/** + * scmi_pinctrl_protocol_attrs - get pinctrl information + * @dev: SCMI protocol device + * @num_pins: Number of pins + * @num_groups: Number of groups + * @num_functions: Number of functions + * + * Obtain the number of pins, groups and functions. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_protocol_attrs(struct udevice *dev, int *num_pins, + int *num_groups, int *num_functions); + +/** + * scmi_pinctrl_attrs - get information for a specific pin, group or function + * @dev: SCMI protocol device + * @select_type: pin, group or function + * @selector: id of pin, group or function + * @gpio: set to true if the pin or group supports gpio + * @count: number of groups in function or pins in group + * @name: name of pin, group or function + * + * Obtain information about a specific pin, group or function. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_attrs(struct udevice *dev, enum select_type select_type, + unsigned int selector, bool *gpio, unsigned int *count, + char *name); + +/** + * scmi_pinctrl_request - claim a pin or group + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * + * Claim ownership of a pin or group. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_request(struct udevice *dev, enum select_type select_type, + unsigned int selector); +/** + * scmi_pinctrl_release - release a claimed pin or group + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * + * Release a pin or group that you previously claimed. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_release(struct udevice *dev, enum select_type select_type, + unsigned int selector); + +/** + * scmi_pinctrl_list_associations - get list of pins in group or groups in function + * @dev: SCMI protocol device + * @select_type: group or function + * @selector: id of group or function + * @output: list of groups in function or pins in group + * @num_out: How many groups are in the function or pins in the group + * + * Obtain the list of groups or pins in the function or group respectively. + * We know how many items will be in the list from calling scmi_pinctrl_attrs(). + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_list_associations(struct udevice *dev, + enum select_type select_type, + unsigned int selector, + unsigned short *output, + unsigned short num_out); + +/** + * scmi_pinctrl_settings_get_one - get a configuration setting + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * @config_type: Which configuration type to read + * @value: returned configuration value + * + * This reads a single config setting. Most importantly the + * SCMI_PIN_INPUT_VALUE setting is used to read from a pin. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_settings_get_one(struct udevice *dev, enum select_type select_type, + unsigned int selector, + u32 config_type, u32 *value); + +/** + * scmi_pinctrl_settings_configure - set multiple configuration settings + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * @num_configs: number of settings to set + * @configs: Config type and value pairs + * + * Configure multiple settings at once to reduce overhead. The + * SCMI_PIN_OUTPUT_VALUE setting is used to write to a pin. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_settings_configure(struct udevice *dev, enum select_type select_type, + unsigned int selector, u16 num_configs, + u32 *configs); + +/** + * scmi_pinctrl_settings_configure_one - set a configuration setting + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * @param: The setting type to configure + * @argument: The value of the configuration + * + * Configure a single setting. The SCMI_PIN_OUTPUT_VALUE setting is used to + * write to a pin. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_settings_configure_one(struct udevice *dev, enum select_type select_type, + unsigned int selector, + u32 param, u32 argument); + +/** + * scmi_pinctrl_set_function - set the function for a group or pin + * @dev: SCMI protocol device + * @select_type: pin or group + * @selector: id of pin or group + * @function_id: id of the function + * + * Set the function for a group or pin. + * + * Return: 0 on success, error code on failure + */ +int scmi_pinctrl_set_function(struct udevice *dev, enum select_type select_type, + unsigned int selector, u32 function_id); + #endif /* _SCMI_PROTOCOLS_H */