diff options
| author | Dan Carpenter <[email protected]> | 2026-03-11 22:41:25 +0300 |
|---|---|---|
| committer | Peng Fan <[email protected]> | 2026-03-23 10:58:20 +0800 |
| commit | 0cb160f1b62905c701569850d3a4d1b46b3dc100 (patch) | |
| tree | 20f7635e46eb837d07d12fbeb81ca2c472095d1f /drivers/firmware | |
| parent | 33dbe00fbb21cd2494e374ead8ab5dc8a8dca8b6 (diff) | |
scmi: pinctrl: add pinctrl driver for SCMI
This driver adds the base support of pinctrl over SCMI. The driver
does two main things. First, it allows you to configure the initial
pin states. Secondly, it's used a base to build a GPIO driver on
top of it.
To configure the states then add a pinmux config to the scmi_pinctrl
section:
scmi_pinctrl: protocol@19 {
reg = <0x19>;
pinmux1: pinmux_test {
pinmux = <0 1 0xFFFFFFFF 18 1
0 2 0xFFFFFFFF 18 1
0 3 0xFFFFFFFF 18 1>;
function = "f_gpio1";
groups = "grp_1", "grp_3";
};
};
Under linux the pinctrl subsystem will parse the function and group
properties and use that to handle muxing. However, under u-boot the
pin muxing is done using the "pinmux" property, which feeds raw SCMI
pinctrl PINCTRL_SETTINGS_CONFIGURE commands to the server. The
numbers are: selector, identifier, function_id, config_type, and
config_value. In the example above, it sets pins 1, 2, and 3 to 1.
The linux-kernel ignores this pinmux property.
Signed-off-by: Dan Carpenter <[email protected]>
Reviewed-by: Peng Fan <[email protected]>
Signed-off-by: Peng Fan <[email protected]>
Diffstat (limited to 'drivers/firmware')
| -rw-r--r-- | drivers/firmware/scmi/Makefile | 1 | ||||
| -rw-r--r-- | drivers/firmware/scmi/pinctrl.c | 365 | ||||
| -rw-r--r-- | drivers/firmware/scmi/scmi_agent-uclass.c | 4 |
3 files changed, 368 insertions, 2 deletions
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 <dm.h> +#include <dm/device_compat.h> +#include <dm/devres.h> +#include <dm/pinctrl.h> +#include <linux/bitfield.h> +#include <linux/compat.h> +#include <scmi_agent.h> +#include <scmi_agent-uclass.h> +#include <scmi_protocols.h> + +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; |
