* [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support
@ 2026-02-23 14:33 Dan Carpenter
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
` (4 more replies)
0 siblings, 5 replies; 13+ messages in thread
From: Dan Carpenter @ 2026-02-23 14:33 UTC (permalink / raw)
To: Peng Fan
Cc: Alice Guo, Anis Chali, Arturs Artamonovs, Eoin Dickson,
Greg Malysa, Ian Roberts, Jacky Bai, Leo Yu-Chi Liang,
Marek Vasut, Marek Vasut, Nathan Barrett-Morrison, Neil Armstrong,
Oliver Gaskell, Philip Molloy, Rasmus Villemoes, Tanmay Kathpalia,
Tom Rini, u-boot, Utsav Agarwal, Valentin Caron,
Vasileios Bimpikas, Vinh Nguyen, Yao Zi, Ye Li, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi, Linus Walleij
There is an existing SCMI pinctrl driver in drivers/pinctrl/nxp/ which
lets you configure the initial state of the pins via device tree. This
patchset is a more generic version which provides GPIO as well.
I wrote two small cleanup patches to the NXP driver, one which renamed
an enum and another which made some white space changes.
The next patch adds an pinctrl driver which lets you configure the
initial state for the pins. But the main thing is that we need one
driver to handle the SCMI pinctrl protocol so the pinctrl driver does
this.
The last patch adds GPIO support over SCMI.
I tested this code using the SCP SCMI server on OP-TEE over Qemu. I
created a custom mock pinctrl device in SCP.
Dan Carpenter (4):
scmi: pinctrl: add pinctrl message IDs
scmi: update comments for scmi_pinctrl_config_set_in()
scmi: pinctrl: add pinctrl driver for SCMI
gpio: scmi: Add gpio_scmi driver
drivers/firmware/scmi/Makefile | 1 +
drivers/firmware/scmi/pinctrl.c | 363 +++++++++++++++++++++
drivers/firmware/scmi/scmi_agent-uclass.c | 4 +-
drivers/gpio/Kconfig | 6 +
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio_scmi.c | 221 +++++++++++++
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/nxp/pinctrl-imx-scmi.c | 2 +-
drivers/pinctrl/pinctrl-scmi.c | 357 ++++++++++++++++++++
include/scmi_agent-uclass.h | 2 +-
include/scmi_protocols.h | 379 +++++++++++++++++++++-
12 files changed, 1334 insertions(+), 12 deletions(-)
create mode 100644 drivers/firmware/scmi/pinctrl.c
create mode 100644 drivers/gpio/gpio_scmi.c
create mode 100644 drivers/pinctrl/pinctrl-scmi.c
--
2.51.0
^ permalink raw reply [flat|nested] 13+ messages in thread
* [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
@ 2026-02-23 14:33 ` Dan Carpenter
2026-02-25 10:52 ` Peng Fan
2026-02-26 22:05 ` Linus Walleij
2026-02-23 14:33 ` [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in() Dan Carpenter
` (3 subsequent siblings)
4 siblings, 2 replies; 13+ messages in thread
From: Dan Carpenter @ 2026-02-23 14:33 UTC (permalink / raw)
To: Peng Fan
Cc: Tom Rini, Alice Guo, Ye Li, Jacky Bai, Marek Vasut, Vinh Nguyen,
Valentin Caron, u-boot, Vincent Guittot, Khaled Ali Ahmed,
Michal Simek, arm-scmi, Linus Walleij
Add all the pinctrl message IDs. I renamed SCMI_MSG_PINCTRL_CONFIG_SET
to SCMI_PINCTRL_SETTINGS_CONFIGURE so the naming matches the spec better.
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
drivers/pinctrl/nxp/pinctrl-imx-scmi.c | 2 +-
include/scmi_protocols.h | 9 ++++++++-
2 files changed, 9 insertions(+), 2 deletions(-)
diff --git a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
index 781835c68527..cf76b14d3731 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx-scmi.c
@@ -69,7 +69,7 @@ static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 c
in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT;
msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
- SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
+ SCMI_PINCTRL_SETTINGS_CONFIGURE, in, out);
ret = devm_scmi_process_msg(dev, &msg);
if (ret || out.status) {
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index ecab021b4728..90b1bf76d5d6 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -1088,7 +1088,14 @@ struct scmi_voltd_level_get_out {
/* SCMI Pinctrl Protocol */
enum scmi_pinctrl_message_id {
- SCMI_MSG_PINCTRL_CONFIG_SET = 0x6
+ SCMI_PINCTRL_ATTRIBUTES = 0x3,
+ SCMI_PINCTRL_LIST_ASSOCIATIONS = 0x4,
+ SCMI_PINCTRL_SETTINGS_GET = 0x5,
+ SCMI_PINCTRL_SETTINGS_CONFIGURE = 0x6,
+ SCMI_PINCTRL_REQUEST = 0x7,
+ SCMI_PINCTRL_RELEASE = 0x8,
+ SCMI_PINCTRL_NAME_GET = 0x9,
+ SCMI_PINCTRL_SET_PERMISSIONS = 0xA,
};
struct scmi_pin_config {
--
2.51.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in()
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
@ 2026-02-23 14:33 ` Dan Carpenter
2026-02-25 11:44 ` Peng Fan
2026-02-23 14:33 ` [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI Dan Carpenter
` (2 subsequent siblings)
4 siblings, 1 reply; 13+ messages in thread
From: Dan Carpenter @ 2026-02-23 14:33 UTC (permalink / raw)
To: Peng Fan
Cc: Tom Rini, Alice Guo, Ye Li, Marek Vasut, Vinh Nguyen, u-boot,
Vincent Guittot, Khaled Ali Ahmed, Michal Simek, arm-scmi,
Linus Walleij
Delete some extra space characters to make checkpatch.pl happy:
WARNING: please, no space before tabs
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
include/scmi_protocols.h | 14 +++++++-------
1 file changed, 7 insertions(+), 7 deletions(-)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index 90b1bf76d5d6..a4efadafa73c 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -1107,14 +1107,14 @@ struct scmi_pin_config {
* struct scmi_pinctrl_config_set_in - Message payload for PAD_CONFIG_SET command
* @identifier: Identifier for the pin or group.
* @function_id: Identifier for the function selected to be enabled
- * for the selected pin or group. This field is set to
- * 0xFFFFFFFF if no function should be enabled by the
- * pin or group.
+ * for the selected pin or group. This field is set to
+ * 0xFFFFFFFF if no function should be enabled by the
+ * pin or group.
* @attributes: Bits[31:11] Reserved, must be zero.
- * Bit[10] Function valid.
- * Bits[9:2] Number of configurations to set.
- * Bits[1:0] Selector: Whether the identifier field
- * refers to a pin or a group.
+ * Bit[10] Function valid.
+ * Bits[9:2] Number of configurations to set.
+ * Bits[1:0] Selector: Whether the identifier field
+ * refers to a pin or a group.
* @configs: Array of configurations.
*/
struct scmi_pinctrl_config_set_in {
--
2.51.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
2026-02-23 14:33 ` [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in() Dan Carpenter
@ 2026-02-23 14:33 ` Dan Carpenter
2026-02-25 14:03 ` Peng Fan
2026-02-23 14:33 ` [PATCH 4/4] gpio: scmi: Add gpio_scmi driver Dan Carpenter
2026-02-24 9:33 ` [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Linus Walleij
4 siblings, 1 reply; 13+ messages in thread
From: Dan Carpenter @ 2026-02-23 14:33 UTC (permalink / raw)
To: Tom Rini
Cc: Peng Fan, Alice Guo, Marek Vasut, Utsav Agarwal, Greg Malysa,
Yao Zi, Arturs Artamonovs, Nathan Barrett-Morrison, Anis Chali,
Vasileios Bimpikas, Leo Yu-Chi Liang, Ye Li, Valentin Caron,
Vinh Nguyen, u-boot, Vincent Guittot, Khaled Ali Ahmed,
Michal Simek, arm-scmi, Linus Walleij
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. And second 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 0
0 3 0xFFFFFFFF 18 1>;
};
};
The numbers are from PINCTRL_SETTINGS_CONFIGURE: selector, identifier,
function_id, config_type, and config_value.
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
drivers/firmware/scmi/Makefile | 1 +
drivers/firmware/scmi/pinctrl.c | 363 ++++++++++++++++++++++
drivers/firmware/scmi/scmi_agent-uclass.c | 4 +-
drivers/pinctrl/Kconfig | 9 +
drivers/pinctrl/Makefile | 1 +
drivers/pinctrl/pinctrl-scmi.c | 357 +++++++++++++++++++++
include/scmi_agent-uclass.h | 2 +-
include/scmi_protocols.h | 356 +++++++++++++++++++++
8 files changed, 1090 insertions(+), 3 deletions(-)
create mode 100644 drivers/firmware/scmi/pinctrl.c
create mode 100644 drivers/pinctrl/pinctrl-scmi.c
diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
index 6129726f8173..761d89a11615 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 000000000000..981bf64cd74b
--- /dev/null
+++ b/drivers/firmware/scmi/pinctrl.c
@@ -0,0 +1,363 @@
+// 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);
+
+ *num_groups = FIELD_GET(GENMASK(31, 16), out.attr_low);
+ *num_pins = FIELD_GET(GENMASK(15, 0), out.attr_low);
+ *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);
+
+ //estended_name = FIELD_GET(BIT(31), out.attr);
+ if (gpio)
+ *gpio = FIELD_GET(BIT(17), out.attr);
+ //pin_only = FIELD_GET(BIT(16), out.attr);
+ if (count)
+ *count = FIELD_GET(GENMASK(15, 0), out.attr);
+ memcpy(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) + (1 * sizeof(u32) * 2);
+ u32 num_configs;
+ int ret;
+
+ 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);
+
+ if (config_type == SCMI_PINCTRL_CONFIG_SETTINGS_ALL) {
+ /* FIXME: implement */
+ return -EIO;
+ }
+
+ 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(32) * 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 ad825d66da2d..cd458a7f4588 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 ea90713ec6ca..512c396880c4 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 33ff7b95ef22..8ab163531821 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 000000000000..32594489178a
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-scmi.c
@@ -0,0 +1,357 @@
+// 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 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);
+ const int batch_count = 20;
+ 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) {
+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
+ cnt / 2, configs);
+ 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:
+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
+ cnt / 2, configs);
+ cnt = 0;
+store:
+ prev_type = select_type;
+ prev_selector = selector;
+ configs[cnt++] = config_type;
+ configs[cnt++] = config_value;
+ offset += 5;
+ }
+
+ if (cnt)
+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
+ cnt / 2, configs);
+free:
+ kfree(configs);
+
+ 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 9b36d3ae67bb..c40b448bcba1 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 a4efadafa73c..3e862adb8035 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -1146,4 +1146,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
+#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 */
--
2.51.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* [PATCH 4/4] gpio: scmi: Add gpio_scmi driver
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
` (2 preceding siblings ...)
2026-02-23 14:33 ` [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI Dan Carpenter
@ 2026-02-23 14:33 ` Dan Carpenter
2026-02-24 16:10 ` Michal Simek
2026-02-24 9:33 ` [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Linus Walleij
4 siblings, 1 reply; 13+ messages in thread
From: Dan Carpenter @ 2026-02-23 14:33 UTC (permalink / raw)
To: Tom Rini
Cc: Peng Fan, Utsav Agarwal, Arturs Artamonovs, Greg Malysa,
Vasileios Bimpikas, Ian Roberts, Rasmus Villemoes, Philip Molloy,
Tanmay Kathpalia, Oliver Gaskell, Nathan Barrett-Morrison,
Neil Armstrong, Eoin Dickson, u-boot, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi, Linus Walleij
This provides gpio support over SCMI. The device tree entry would look
like this:
scmi_gpio1: gpio1 {
compatible = "scmi,gpio";
scmi,pinctrl = <&scmi_pinctrl 0 2>;
pinctrl-names = "default";
pinctrl-0 = <&pinmux1>;
gpio-controller;
};
The scmi,pinctrl phandle points to the function and the index within that
function. The pinctrl-0 phandle points to the initial pin configuration
if necessary.
Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
---
drivers/gpio/Kconfig | 6 ++
drivers/gpio/Makefile | 1 +
drivers/gpio/gpio_scmi.c | 221 +++++++++++++++++++++++++++++++++++++++
3 files changed, 228 insertions(+)
create mode 100644 drivers/gpio/gpio_scmi.c
diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
index 60c5c54688e6..7d22c1f792d1 100644
--- a/drivers/gpio/Kconfig
+++ b/drivers/gpio/Kconfig
@@ -723,6 +723,12 @@ config SLG7XL45106_I2C_GPO
8-bit gpo expander, all gpo lines are controlled by writing
value into data register.
+config GPIO_SCMI
+ bool "SCMI GPIO pinctrl driver"
+ depends on DM_GPIO && PINCTRL_SCMI
+ help
+ Support pinctrl GPIO over the SCMI interface.
+
config ADP5585_GPIO
bool "ADP5585 GPIO driver"
depends on DM_GPIO && DM_I2C
diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
index 910478c0c7a9..0003ed74be67 100644
--- a/drivers/gpio/Makefile
+++ b/drivers/gpio/Makefile
@@ -77,6 +77,7 @@ obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o
obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
+obj-$(CONFIG_GPIO_SCMI) += gpio_scmi.o
obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o
obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o
obj-$(CONFIG_MPFS_GPIO) += mpfs_gpio.o
diff --git a/drivers/gpio/gpio_scmi.c b/drivers/gpio/gpio_scmi.c
new file mode 100644
index 000000000000..a859ad610587
--- /dev/null
+++ b/drivers/gpio/gpio_scmi.c
@@ -0,0 +1,221 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2026 Linaro Ltd.
+ */
+
+#include <asm/gpio.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <scmi_protocols.h>
+
+struct scmi_gpio_priv {
+ struct udevice *pin_dev;
+ char *bank_name;
+ u32 num_pins;
+ u16 *pins;
+};
+
+static int scmi_gpio_request(struct udevice *dev, unsigned int offset, const char *label)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ int pin;
+ int ret;
+
+ if (offset >= priv->num_pins)
+ return -EINVAL;
+ pin = priv->pins[offset];
+
+ ret = scmi_pinctrl_request(priv->pin_dev, SCMI_PIN, pin);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+ if (ret)
+ dev_err(dev, "%s(): request failed: %d\n", __func__, ret);
+ return ret;
+}
+
+static int scmi_gpio_rfree(struct udevice *dev, unsigned int offset)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ int pin;
+ int ret;
+
+ if (offset >= priv->num_pins)
+ return -EINVAL;
+ pin = priv->pins[offset];
+
+ ret = scmi_pinctrl_release(priv->pin_dev, SCMI_PIN, pin);
+ if (ret == -EOPNOTSUPP)
+ ret = 0;
+ if (ret)
+ dev_err(dev, "%s(): release failed: %d\n", __func__, ret);
+ return ret;
+}
+
+static int scmi_gpio_set_flags(struct udevice *dev, unsigned int offset, ulong flags)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ const int MAX_FLAGS = 10;
+ u32 configs[MAX_FLAGS * 2];
+ int cnt = 0;
+ u32 pin;
+
+ if (offset >= priv->num_pins)
+ return -EINVAL;
+ pin = priv->pins[offset];
+
+ if (flags & GPIOD_IS_OUT) {
+ configs[cnt++] = SCMI_PIN_OUTPUT_MODE;
+ configs[cnt++] = 1;
+ configs[cnt++] = SCMI_PIN_OUTPUT_VALUE;
+ if (flags & GPIOD_IS_OUT_ACTIVE)
+ configs[cnt++] = 1;
+ else
+ configs[cnt++] = 0;
+ }
+ if (flags & GPIOD_IS_IN) {
+ configs[cnt++] = SCMI_PIN_INPUT_MODE;
+ configs[cnt++] = 1;
+ }
+ if (flags & GPIOD_OPEN_DRAIN) {
+ configs[cnt++] = SCMI_PIN_DRIVE_OPEN_DRAIN;
+ configs[cnt++] = 1;
+ }
+ if (flags & GPIOD_OPEN_SOURCE) {
+ configs[cnt++] = SCMI_PIN_DRIVE_OPEN_SOURCE;
+ configs[cnt++] = 1;
+ }
+ if (flags & GPIOD_PULL_UP) {
+ configs[cnt++] = SCMI_PIN_BIAS_PULL_UP;
+ configs[cnt++] = 1;
+ }
+ if (flags & GPIOD_PULL_DOWN) {
+ configs[cnt++] = SCMI_PIN_BIAS_PULL_DOWN;
+ configs[cnt++] = 1;
+ }
+ /* TODO: handle GPIOD_ACTIVE_LOW and GPIOD_IS_AF flags */
+
+ return scmi_pinctrl_settings_configure(priv->pin_dev, SCMI_PIN, pin,
+ cnt / 2, &configs[0]);
+}
+
+static int scmi_gpio_get_value(struct udevice *dev, unsigned int offset)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ u32 value;
+ int pin;
+ int ret;
+
+ if (offset >= priv->num_pins)
+ return -EINVAL;
+ pin = priv->pins[offset];
+
+ ret = scmi_pinctrl_settings_get_one(priv->pin_dev, SCMI_PIN, pin,
+ SCMI_PIN_INPUT_VALUE, &value);
+ if (ret) {
+ dev_err(dev, "settings_get_one() failed: %d\n", ret);
+ return ret;
+ }
+
+ return value;
+}
+
+static int scmi_gpio_get_function(struct udevice *dev, unsigned int offset)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ u32 value;
+ int pin;
+ int ret;
+
+ if (offset >= priv->num_pins)
+ return -EINVAL;
+ pin = priv->pins[offset];
+
+ ret = scmi_pinctrl_settings_get_one(priv->pin_dev, SCMI_PIN, pin,
+ SCMI_PIN_INPUT_MODE,
+ &value);
+ if (ret) {
+ dev_err(dev, "settings_get() failed %d\n", ret);
+ return ret;
+ }
+
+ if (value)
+ return GPIOF_INPUT;
+ return GPIOF_OUTPUT;
+}
+
+static const struct dm_gpio_ops scmi_gpio_ops = {
+ .request = scmi_gpio_request,
+ .rfree = scmi_gpio_rfree,
+ .set_flags = scmi_gpio_set_flags,
+ .get_value = scmi_gpio_get_value,
+ .get_function = scmi_gpio_get_function,
+};
+
+static int get_pins(struct udevice *dev, u32 function, u32 index, char **f_name,
+ u16 **pins, u32 *npins)
+{
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ struct pinctrl_scmi_priv *pin_info = dev_get_priv(priv->pin_dev);
+ u32 group;
+
+ if (function >= pin_info->num_functions)
+ return -EINVAL;
+ if (index >= pin_info->function_info[function].num_groups)
+ return -EINVAL;
+ group = pin_info->function_info[function].groups[index];
+ if (group >= pin_info->num_groups)
+ return -EINVAL;
+
+ *f_name = pin_info->function_info[function].name;
+ *pins = pin_info->group_info[group].pins;
+ *npins = pin_info->group_info[group].num_pins;
+
+ return 0;
+}
+
+static int scmi_gpio_probe(struct udevice *dev)
+{
+ struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct scmi_gpio_priv *priv = dev_get_priv(dev);
+ struct ofnode_phandle_args args;
+ int ret;
+
+ ret = dev_read_phandle_with_args(dev, "scmi,pinctrl", NULL, 2, 0, &args);
+ if (ret) {
+ dev_err(dev, "failed to read scmi,pinctrl phandle: %d\n", ret);
+ return ret;
+ }
+
+ ret = uclass_get_device_by_ofnode(UCLASS_PINCTRL, args.node, &priv->pin_dev);
+ if (ret) {
+ dev_err(dev, "failed to find pinctrl device: %d\n", ret);
+ return ret;
+ }
+
+ ret = get_pins(dev, args.args[0], args.args[1], &priv->bank_name,
+ &priv->pins, &priv->num_pins);
+ if (ret) {
+ dev_err(dev, "failed to get pins for %u %u\n", args.args[0], args.args[1]);
+ return ret;
+ }
+
+ uc_priv->bank_name = priv->bank_name;
+ uc_priv->gpio_count = priv->num_pins;
+
+ return 0;
+}
+
+static const struct udevice_id scmi_gpio_match[] = {
+ { .compatible = "scmi,gpio" },
+ { }
+};
+
+U_BOOT_DRIVER(scmi_pinctrl_gpio) = {
+ .name = "scmi_pinctrl_gpio",
+ .id = UCLASS_GPIO,
+ .of_match = scmi_gpio_match,
+ .probe = scmi_gpio_probe,
+ .priv_auto = sizeof(struct scmi_gpio_priv),
+ .ops = &scmi_gpio_ops,
+};
+
--
2.51.0
^ permalink raw reply related [flat|nested] 13+ messages in thread
* Re: [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
` (3 preceding siblings ...)
2026-02-23 14:33 ` [PATCH 4/4] gpio: scmi: Add gpio_scmi driver Dan Carpenter
@ 2026-02-24 9:33 ` Linus Walleij
4 siblings, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-02-24 9:33 UTC (permalink / raw)
To: Dan Carpenter
Cc: Peng Fan, Alice Guo, Anis Chali, Arturs Artamonovs, Eoin Dickson,
Greg Malysa, Ian Roberts, Jacky Bai, Leo Yu-Chi Liang,
Marek Vasut, Marek Vasut, Nathan Barrett-Morrison, Neil Armstrong,
Oliver Gaskell, Philip Molloy, Rasmus Villemoes, Tanmay Kathpalia,
Tom Rini, u-boot, Utsav Agarwal, Valentin Caron,
Vasileios Bimpikas, Vinh Nguyen, Yao Zi, Ye Li, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi
On Mon, Feb 23, 2026 at 3:33 PM Dan Carpenter <dan.carpenter@linaro.org> wrote:
> There is an existing SCMI pinctrl driver in drivers/pinctrl/nxp/ which
> lets you configure the initial state of the pins via device tree. This
> patchset is a more generic version which provides GPIO as well.
>
> I wrote two small cleanup patches to the NXP driver, one which renamed
> an enum and another which made some white space changes.
>
> The next patch adds an pinctrl driver which lets you configure the
> initial state for the pins. But the main thing is that we need one
> driver to handle the SCMI pinctrl protocol so the pinctrl driver does
> this.
>
> The last patch adds GPIO support over SCMI.
>
> I tested this code using the SCP SCMI server on OP-TEE over Qemu. I
> created a custom mock pinctrl device in SCP.
>
> Dan Carpenter (4):
> scmi: pinctrl: add pinctrl message IDs
> scmi: update comments for scmi_pinctrl_config_set_in()
> scmi: pinctrl: add pinctrl driver for SCMI
> gpio: scmi: Add gpio_scmi driver
Looks good to me!
Believe it or not but I have not even the shallowest knowledge of the
pin control implementation in U-Boot. But for advancing this I can
at least provide an ACK:
Acked-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] gpio: scmi: Add gpio_scmi driver
2026-02-23 14:33 ` [PATCH 4/4] gpio: scmi: Add gpio_scmi driver Dan Carpenter
@ 2026-02-24 16:10 ` Michal Simek
2026-03-12 7:32 ` Dan Carpenter
0 siblings, 1 reply; 13+ messages in thread
From: Michal Simek @ 2026-02-24 16:10 UTC (permalink / raw)
To: Dan Carpenter, Tom Rini
Cc: Peng Fan, Utsav Agarwal, Arturs Artamonovs, Greg Malysa,
Vasileios Bimpikas, Ian Roberts, Rasmus Villemoes, Philip Molloy,
Tanmay Kathpalia, Oliver Gaskell, Nathan Barrett-Morrison,
Neil Armstrong, Eoin Dickson, u-boot, Vincent Guittot,
Khaled Ali Ahmed, arm-scmi, Linus Walleij
On 2/23/26 15:33, Dan Carpenter wrote:
> This provides gpio support over SCMI. The device tree entry would look
> like this:
>
> scmi_gpio1: gpio1 {
> compatible = "scmi,gpio";
> scmi,pinctrl = <&scmi_pinctrl 0 2>;
> pinctrl-names = "default";
> pinctrl-0 = <&pinmux1>;
> gpio-controller;
> };
>
> The scmi,pinctrl phandle points to the function and the index within that
> function. The pinctrl-0 phandle points to the initial pin configuration
> if necessary.
>
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
> ---
> drivers/gpio/Kconfig | 6 ++
> drivers/gpio/Makefile | 1 +
> drivers/gpio/gpio_scmi.c | 221 +++++++++++++++++++++++++++++++++++++++
> 3 files changed, 228 insertions(+)
> create mode 100644 drivers/gpio/gpio_scmi.c
>
> diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig
> index 60c5c54688e6..7d22c1f792d1 100644
> --- a/drivers/gpio/Kconfig
> +++ b/drivers/gpio/Kconfig
> @@ -723,6 +723,12 @@ config SLG7XL45106_I2C_GPO
> 8-bit gpo expander, all gpo lines are controlled by writing
> value into data register.
>
> +config GPIO_SCMI
> + bool "SCMI GPIO pinctrl driver"
> + depends on DM_GPIO && PINCTRL_SCMI
> + help
> + Support pinctrl GPIO over the SCMI interface.
> +
> config ADP5585_GPIO
> bool "ADP5585 GPIO driver"
> depends on DM_GPIO && DM_I2C
> diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile
> index 910478c0c7a9..0003ed74be67 100644
> --- a/drivers/gpio/Makefile
> +++ b/drivers/gpio/Makefile
> @@ -77,6 +77,7 @@ obj-$(CONFIG_SL28CPLD_GPIO) += sl28cpld-gpio.o
> obj-$(CONFIG_ADP5588_GPIO) += adp5588_gpio.o
> obj-$(CONFIG_ZYNQMP_GPIO_MODEPIN) += zynqmp_gpio_modepin.o
> obj-$(CONFIG_SLG7XL45106_I2C_GPO) += gpio_slg7xl45106.o
> +obj-$(CONFIG_GPIO_SCMI) += gpio_scmi.o
> obj-$(CONFIG_$(PHASE_)ADP5585_GPIO) += adp5585_gpio.o
> obj-$(CONFIG_RZG2L_GPIO) += rzg2l-gpio.o
> obj-$(CONFIG_MPFS_GPIO) += mpfs_gpio.o
> diff --git a/drivers/gpio/gpio_scmi.c b/drivers/gpio/gpio_scmi.c
> new file mode 100644
> index 000000000000..a859ad610587
> --- /dev/null
> +++ b/drivers/gpio/gpio_scmi.c
> @@ -0,0 +1,221 @@
> +// SPDX-License-Identifier: GPL-2.0+
> +/*
> + * Copyright 2026 Linaro Ltd.
> + */
> +
> +#include <asm/gpio.h>
> +#include <dm.h>
> +#include <dm/device_compat.h>
> +#include <scmi_protocols.h>
> +
> +struct scmi_gpio_priv {
> + struct udevice *pin_dev;
> + char *bank_name;
> + u32 num_pins;
> + u16 *pins;
> +};
> +
> +static int scmi_gpio_request(struct udevice *dev, unsigned int offset, const char *label)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + int pin;
> + int ret;
> +
> + if (offset >= priv->num_pins)
> + return -EINVAL;
> + pin = priv->pins[offset];
> +
> + ret = scmi_pinctrl_request(priv->pin_dev, SCMI_PIN, pin);
> + if (ret == -EOPNOTSUPP)
> + ret = 0;
> + if (ret)
> + dev_err(dev, "%s(): request failed: %d\n", __func__, ret);
> + return ret;
> +}
> +
> +static int scmi_gpio_rfree(struct udevice *dev, unsigned int offset)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + int pin;
> + int ret;
> +
> + if (offset >= priv->num_pins)
> + return -EINVAL;
> + pin = priv->pins[offset];
> +
> + ret = scmi_pinctrl_release(priv->pin_dev, SCMI_PIN, pin);
> + if (ret == -EOPNOTSUPP)
> + ret = 0;
> + if (ret)
> + dev_err(dev, "%s(): release failed: %d\n", __func__, ret);
> + return ret;
> +}
> +
> +static int scmi_gpio_set_flags(struct udevice *dev, unsigned int offset, ulong flags)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + const int MAX_FLAGS = 10;
> + u32 configs[MAX_FLAGS * 2];
> + int cnt = 0;
> + u32 pin;
> +
> + if (offset >= priv->num_pins)
> + return -EINVAL;
> + pin = priv->pins[offset];
> +
> + if (flags & GPIOD_IS_OUT) {
> + configs[cnt++] = SCMI_PIN_OUTPUT_MODE;
> + configs[cnt++] = 1;
> + configs[cnt++] = SCMI_PIN_OUTPUT_VALUE;
> + if (flags & GPIOD_IS_OUT_ACTIVE)
> + configs[cnt++] = 1;
> + else
> + configs[cnt++] = 0;
> + }
> + if (flags & GPIOD_IS_IN) {
> + configs[cnt++] = SCMI_PIN_INPUT_MODE;
> + configs[cnt++] = 1;
> + }
> + if (flags & GPIOD_OPEN_DRAIN) {
> + configs[cnt++] = SCMI_PIN_DRIVE_OPEN_DRAIN;
> + configs[cnt++] = 1;
> + }
> + if (flags & GPIOD_OPEN_SOURCE) {
> + configs[cnt++] = SCMI_PIN_DRIVE_OPEN_SOURCE;
> + configs[cnt++] = 1;
> + }
> + if (flags & GPIOD_PULL_UP) {
> + configs[cnt++] = SCMI_PIN_BIAS_PULL_UP;
> + configs[cnt++] = 1;
> + }
> + if (flags & GPIOD_PULL_DOWN) {
> + configs[cnt++] = SCMI_PIN_BIAS_PULL_DOWN;
> + configs[cnt++] = 1;
> + }
> + /* TODO: handle GPIOD_ACTIVE_LOW and GPIOD_IS_AF flags */
> +
> + return scmi_pinctrl_settings_configure(priv->pin_dev, SCMI_PIN, pin,
> + cnt / 2, &configs[0]);
> +}
> +
> +static int scmi_gpio_get_value(struct udevice *dev, unsigned int offset)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + u32 value;
> + int pin;
> + int ret;
> +
> + if (offset >= priv->num_pins)
> + return -EINVAL;
> + pin = priv->pins[offset];
> +
> + ret = scmi_pinctrl_settings_get_one(priv->pin_dev, SCMI_PIN, pin,
> + SCMI_PIN_INPUT_VALUE, &value);
> + if (ret) {
> + dev_err(dev, "settings_get_one() failed: %d\n", ret);
> + return ret;
> + }
> +
> + return value;
> +}
> +
> +static int scmi_gpio_get_function(struct udevice *dev, unsigned int offset)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + u32 value;
> + int pin;
> + int ret;
> +
> + if (offset >= priv->num_pins)
> + return -EINVAL;
> + pin = priv->pins[offset];
> +
> + ret = scmi_pinctrl_settings_get_one(priv->pin_dev, SCMI_PIN, pin,
> + SCMI_PIN_INPUT_MODE,
> + &value);
> + if (ret) {
> + dev_err(dev, "settings_get() failed %d\n", ret);
> + return ret;
> + }
> +
> + if (value)
> + return GPIOF_INPUT;
> + return GPIOF_OUTPUT;
> +}
> +
> +static const struct dm_gpio_ops scmi_gpio_ops = {
> + .request = scmi_gpio_request,
> + .rfree = scmi_gpio_rfree,
> + .set_flags = scmi_gpio_set_flags,
> + .get_value = scmi_gpio_get_value,
> + .get_function = scmi_gpio_get_function,
> +};
> +
> +static int get_pins(struct udevice *dev, u32 function, u32 index, char **f_name,
> + u16 **pins, u32 *npins)
> +{
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + struct pinctrl_scmi_priv *pin_info = dev_get_priv(priv->pin_dev);
> + u32 group;
> +
> + if (function >= pin_info->num_functions)
> + return -EINVAL;
> + if (index >= pin_info->function_info[function].num_groups)
> + return -EINVAL;
> + group = pin_info->function_info[function].groups[index];
> + if (group >= pin_info->num_groups)
> + return -EINVAL;
> +
> + *f_name = pin_info->function_info[function].name;
> + *pins = pin_info->group_info[group].pins;
> + *npins = pin_info->group_info[group].num_pins;
> +
> + return 0;
> +}
> +
> +static int scmi_gpio_probe(struct udevice *dev)
> +{
> + struct gpio_dev_priv *uc_priv = dev_get_uclass_priv(dev);
> + struct scmi_gpio_priv *priv = dev_get_priv(dev);
> + struct ofnode_phandle_args args;
> + int ret;
> +
> + ret = dev_read_phandle_with_args(dev, "scmi,pinctrl", NULL, 2, 0, &args);
> + if (ret) {
> + dev_err(dev, "failed to read scmi,pinctrl phandle: %d\n", ret);
> + return ret;
> + }
> +
> + ret = uclass_get_device_by_ofnode(UCLASS_PINCTRL, args.node, &priv->pin_dev);
> + if (ret) {
> + dev_err(dev, "failed to find pinctrl device: %d\n", ret);
> + return ret;
> + }
> +
> + ret = get_pins(dev, args.args[0], args.args[1], &priv->bank_name,
> + &priv->pins, &priv->num_pins);
> + if (ret) {
> + dev_err(dev, "failed to get pins for %u %u\n", args.args[0], args.args[1]);
> + return ret;
> + }
> +
> + uc_priv->bank_name = priv->bank_name;
> + uc_priv->gpio_count = priv->num_pins;
> +
> + return 0;
> +}
> +
> +static const struct udevice_id scmi_gpio_match[] = {
> + { .compatible = "scmi,gpio" },
Can you point me the dt binding document?
I am aware about this one and grep is not returning me anything from linux-next too
https://lore.kernel.org/all/20231002021602.260100-1-takahiro.akashi@linaro.org/
Thanks,
Michal
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
@ 2026-02-25 10:52 ` Peng Fan
2026-02-26 22:05 ` Linus Walleij
1 sibling, 0 replies; 13+ messages in thread
From: Peng Fan @ 2026-02-25 10:52 UTC (permalink / raw)
To: Dan Carpenter
Cc: Peng Fan, Tom Rini, Alice Guo, Ye Li, Jacky Bai, Marek Vasut,
Vinh Nguyen, Valentin Caron, u-boot, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi, Linus Walleij
On Mon, Feb 23, 2026 at 05:33:25PM +0300, Dan Carpenter wrote:
>Add all the pinctrl message IDs. I renamed SCMI_MSG_PINCTRL_CONFIG_SET
>to SCMI_PINCTRL_SETTINGS_CONFIGURE so the naming matches the spec better.
>
>Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Peng Fan <peng.fan@nxp.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in()
2026-02-23 14:33 ` [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in() Dan Carpenter
@ 2026-02-25 11:44 ` Peng Fan
0 siblings, 0 replies; 13+ messages in thread
From: Peng Fan @ 2026-02-25 11:44 UTC (permalink / raw)
To: Dan Carpenter
Cc: Peng Fan, Tom Rini, Alice Guo, Ye Li, Marek Vasut, Vinh Nguyen,
u-boot, Vincent Guittot, Khaled Ali Ahmed, Michal Simek, arm-scmi,
Linus Walleij
On Mon, Feb 23, 2026 at 05:33:30PM +0300, Dan Carpenter wrote:
>Delete some extra space characters to make checkpatch.pl happy:
>
>WARNING: please, no space before tabs
>
>Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Acked-by: Peng Fan <peng.fan@nxp.com>
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI
2026-02-23 14:33 ` [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI Dan Carpenter
@ 2026-02-25 14:03 ` Peng Fan
2026-02-25 14:34 ` Dan Carpenter
0 siblings, 1 reply; 13+ messages in thread
From: Peng Fan @ 2026-02-25 14:03 UTC (permalink / raw)
To: Dan Carpenter
Cc: Tom Rini, Peng Fan, Alice Guo, Marek Vasut, Utsav Agarwal,
Greg Malysa, Yao Zi, Arturs Artamonovs, Nathan Barrett-Morrison,
Anis Chali, Vasileios Bimpikas, Leo Yu-Chi Liang, Ye Li,
Valentin Caron, Vinh Nguyen, u-boot, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi, Linus Walleij
Hi Dan,
Thanks for your patch!
On Mon, Feb 23, 2026 at 05:33:35PM +0300, Dan Carpenter wrote:
>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. And second 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 0
> 0 3 0xFFFFFFFF 18 1>;
> };
> };
>
>The numbers are from PINCTRL_SETTINGS_CONFIGURE: selector, identifier,
>function_id, config_type, and config_value.
>
>Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
>---
> drivers/firmware/scmi/Makefile | 1 +
> drivers/firmware/scmi/pinctrl.c | 363 ++++++++++++++++++++++
> drivers/firmware/scmi/scmi_agent-uclass.c | 4 +-
> drivers/pinctrl/Kconfig | 9 +
> drivers/pinctrl/Makefile | 1 +
> drivers/pinctrl/pinctrl-scmi.c | 357 +++++++++++++++++++++
> include/scmi_agent-uclass.h | 2 +-
> include/scmi_protocols.h | 356 +++++++++++++++++++++
> 8 files changed, 1090 insertions(+), 3 deletions(-)
> create mode 100644 drivers/firmware/scmi/pinctrl.c
> create mode 100644 drivers/pinctrl/pinctrl-scmi.c
>
>diff --git a/drivers/firmware/scmi/Makefile b/drivers/firmware/scmi/Makefile
>index 6129726f8173..761d89a11615 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 000000000000..981bf64cd74b
>--- /dev/null
>+++ b/drivers/firmware/scmi/pinctrl.c
>@@ -0,0 +1,363 @@
>+// SPDX-License-Identifier: GPL-2.0+
>+/*
>+ * Copyright 2026 Linaro Ltd.
>+ */
>+
[...]
>+
>+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);
>+
>+ *num_groups = FIELD_GET(GENMASK(31, 16), out.attr_low);
>+ *num_pins = FIELD_GET(GENMASK(15, 0), out.attr_low);
>+ *num_functions = FIELD_GET(GENMASK(15, 0), out.attr_high);
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);
>+
>+ //estended_name = FIELD_GET(BIT(31), out.attr);
Drop this?
>+ if (gpio)
>+ *gpio = FIELD_GET(BIT(17), out.attr);
>+ //pin_only = FIELD_GET(BIT(16), out.attr);
Ditto.
>+ if (count)
>+ *count = FIELD_GET(GENMASK(15, 0), out.attr);
>+ memcpy(name, out.name, sizeof(out.name));
memcpy will always copy whole the name array, so use strncpy?
>+
>+ 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
I see spec is using 2, not understand well -2u here.
When set to 2, no configuration values need to be returned.
The command only returns the function selected for the pin or the group.
>+
>+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) + (1 * sizeof(u32) * 2);
size_t out_sz = sizeof(*out) + (sizeof(u32) * 2);
>+ u32 num_configs;
>+ int ret;
Move below sanity check code here.
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);
>+
>+ if (config_type == SCMI_PINCTRL_CONFIG_SETTINGS_ALL) {
>+ /* FIXME: implement */
>+ return -EIO;
>+ }
>+
>+ 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(32) * 2);
sizeof(u32)
>+
>+ 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 ad825d66da2d..cd458a7f4588 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 ea90713ec6ca..512c396880c4 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 33ff7b95ef22..8ab163531821 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 000000000000..32594489178a
>--- /dev/null
>+++ b/drivers/pinctrl/pinctrl-scmi.c
>@@ -0,0 +1,357 @@
>+// 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 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);
>+ const int batch_count = 20;
An comment is welcomed on the batch_count set to 20.
>+ 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) {
>+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
>+ cnt / 2, configs);
Check return value.
>+ prev_type = -1u;
>+ cnt = 0;
>+ }
>+ scmi_pinctrl_set_function(dev, select_type, selector, function);
Ditto.
>+ offset += 5;
>+ continue;
>+ }
>+
>+ if (cnt == batch_count)
>+ goto set;
>+
>+ if (prev_type == -1u)
>+ goto store;
>+
>+ if (select_type == prev_type &&
>+ selector == prev_selector)
Use one line.
>+ goto store;
>+set:
>+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
>+ cnt / 2, configs);
return value check.
>+ cnt = 0;
>+store:
>+ prev_type = select_type;
>+ prev_selector = selector;
>+ configs[cnt++] = config_type;
>+ configs[cnt++] = config_value;
>+ offset += 5;
>+ }
>+
>+ if (cnt)
>+ scmi_pinctrl_settings_configure(dev, prev_type, prev_selector,
>+ cnt / 2, configs);
return value check.
>+free:
>+ kfree(configs);
>+
>+ 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 9b36d3ae67bb..c40b448bcba1 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 a4efadafa73c..3e862adb8035 100644
>--- a/include/scmi_protocols.h
>+++ b/include/scmi_protocols.h
>@@ -1146,4 +1146,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
Not understand well why -2u.
>+#define SCMI_PINCTRL_FUNCTION_NONE 0xFFFFFFFF
>+
>+/**
Regards,
Peng
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI
2026-02-25 14:03 ` Peng Fan
@ 2026-02-25 14:34 ` Dan Carpenter
0 siblings, 0 replies; 13+ messages in thread
From: Dan Carpenter @ 2026-02-25 14:34 UTC (permalink / raw)
To: Peng Fan
Cc: Tom Rini, Peng Fan, Alice Guo, Marek Vasut, Utsav Agarwal,
Greg Malysa, Yao Zi, Arturs Artamonovs, Nathan Barrett-Morrison,
Anis Chali, Vasileios Bimpikas, Leo Yu-Chi Liang, Ye Li,
Valentin Caron, Vinh Nguyen, u-boot, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi, Linus Walleij
Thanks, Peng. This is all good feedback and I'll fix it in the
next version.
On Wed, Feb 25, 2026 at 10:03:13PM +0800, Peng Fan wrote:
> >+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);
> >+
> >+ //estended_name = FIELD_GET(BIT(31), out.attr);
>
> Drop this?
>
Yeah. I don't think anyone cares about the extended name ever.
> >+ if (gpio)
> >+ *gpio = FIELD_GET(BIT(17), out.attr);
> >+ //pin_only = FIELD_GET(BIT(16), out.attr);
>
> Ditto.
>
Huh. Yeah. It's not really clear how to handle pin only GPIO pins.
[ snip ]
> >+
> >+#define SCMI_PINCTRL_CONFIG_SETTINGS_FUNCTION -2u
>
> I see spec is using 2, not understand well -2u here.
> When set to 2, no configuration values need to be returned.
> The command only returns the function selected for the pin or the group.
>
This was just a magic define I used internally.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
2026-02-25 10:52 ` Peng Fan
@ 2026-02-26 22:05 ` Linus Walleij
1 sibling, 0 replies; 13+ messages in thread
From: Linus Walleij @ 2026-02-26 22:05 UTC (permalink / raw)
To: Dan Carpenter
Cc: Peng Fan, Tom Rini, Alice Guo, Ye Li, Jacky Bai, Marek Vasut,
Vinh Nguyen, Valentin Caron, u-boot, Vincent Guittot,
Khaled Ali Ahmed, Michal Simek, arm-scmi
On Mon, Feb 23, 2026 at 3:33 PM Dan Carpenter <dan.carpenter@linaro.org> wrote:
> Add all the pinctrl message IDs. I renamed SCMI_MSG_PINCTRL_CONFIG_SET
> to SCMI_PINCTRL_SETTINGS_CONFIGURE so the naming matches the spec better.
>
> Signed-off-by: Dan Carpenter <dan.carpenter@linaro.org>
Reviewed-by: Linus Walleij <linusw@kernel.org>
Yours,
Linus Walleij
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 4/4] gpio: scmi: Add gpio_scmi driver
2026-02-24 16:10 ` Michal Simek
@ 2026-03-12 7:32 ` Dan Carpenter
0 siblings, 0 replies; 13+ messages in thread
From: Dan Carpenter @ 2026-03-12 7:32 UTC (permalink / raw)
To: Michal Simek
Cc: Tom Rini, Peng Fan, Utsav Agarwal, Arturs Artamonovs, Greg Malysa,
Vasileios Bimpikas, Ian Roberts, Rasmus Villemoes, Philip Molloy,
Tanmay Kathpalia, Oliver Gaskell, Nathan Barrett-Morrison,
Neil Armstrong, Eoin Dickson, u-boot, Vincent Guittot,
Khaled Ali Ahmed, arm-scmi, Linus Walleij
On Tue, Feb 24, 2026 at 05:10:26PM +0100, Michal Simek wrote:
>
>
> Can you point me the dt binding document?
>
> I am aware about this one and grep is not returning me anything from linux-next too
>
> https://lore.kernel.org/all/20231002021602.260100-1-takahiro.akashi@linaro.org/
>
Yeah... I should have done the more standard thing with gpio-ranges.
In v2, u-boot and linux use the same device tree format. The difference
is that in the pin control driver, u-boot handles pin muxing by sending
raw commands.
regards,
dan carpenter
^ permalink raw reply [flat|nested] 13+ messages in thread
end of thread, other threads:[~2026-03-12 7:32 UTC | newest]
Thread overview: 13+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-02-23 14:33 [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Dan Carpenter
2026-02-23 14:33 ` [PATCH 1/4] scmi: pinctrl: add pinctrl message IDs Dan Carpenter
2026-02-25 10:52 ` Peng Fan
2026-02-26 22:05 ` Linus Walleij
2026-02-23 14:33 ` [PATCH 2/4] scmi: update comments for scmi_pinctrl_config_set_in() Dan Carpenter
2026-02-25 11:44 ` Peng Fan
2026-02-23 14:33 ` [PATCH 3/4] scmi: pinctrl: add pinctrl driver for SCMI Dan Carpenter
2026-02-25 14:03 ` Peng Fan
2026-02-25 14:34 ` Dan Carpenter
2026-02-23 14:33 ` [PATCH 4/4] gpio: scmi: Add gpio_scmi driver Dan Carpenter
2026-02-24 16:10 ` Michal Simek
2026-03-12 7:32 ` Dan Carpenter
2026-02-24 9:33 ` [PATCH 0/4] u-boot: add SCMI GPIO/Pinctrl support Linus Walleij
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox