diff --git a/arch/sandbox/dts/test.dts b/arch/sandbox/dts/test.dts
index dc0bfdfb6e4b..d2ddea801995 100644
--- a/arch/sandbox/dts/test.dts
+++ b/arch/sandbox/dts/test.dts
@@ -723,9 +723,124 @@
 					};
 				};
 			};
+
+			pinctrl_scmi: protocol@19 {
+				reg = <0x19>;
+
+				pinctrl-names = "default","alternate";
+				pinctrl-0 = <&scmi_pinctrl_gpios>, <&scmi_pinctrl_i2s>;
+				pinctrl-1 = <&scmi_pinctrl_spi>, <&scmi_pinctrl_i2c>;
+
+#if 0
+				scmi_pinctrl_gpio_a: scmi_gpios {
+					gpio-controller;
+					#gpio-cells = <2>;
+					gpio-bank-name = "scmi_gpios";
+					ngpios = <4>;
+					gpio-ranges = <&pinctrl_scmi 0 5 4>,
+						      <&pinctrl_scmi 4 0 0>;
+					gpio-ranges-group-names = "",
+								  "GPIO_B";
+
+					hog_input_1 {
+						gpio-hog;
+						input;
+						gpios = <1 GPIO_ACTIVE_HIGH>;
+					};
+					hog_output_3 {
+						gpio-hog;
+						output-high;
+						output-mode;
+						output-value = <1>;
+						gpios = <3 GPIO_ACTIVE_HIGH>;
+					};
+				};
+#endif
+
+				scmi_pinctrl_gpios: gpios-pins {
+					gpio0 {
+						pins = "P5";
+						function = "GPIO";
+						bias-pull-up;
+						// input-disable;
+						input-mode = <0>;
+					};
+					gpio1 {
+						pins = "P6";
+						function = "GPIO";
+						// output-high;
+						output-mode;
+						output-value = <1>;
+						drive-open-drain;
+					};
+					gpio2 {
+						pinmux = <SANDBOX_PINMUX(7, SANDBOX_PINMUX_GPIO)>;
+						bias-pull-down;
+						// input-enable;
+						input-mode;
+					};
+					gpio3 {
+						pinmux = <SANDBOX_PINMUX(8, SANDBOX_PINMUX_GPIO)>;
+						bias-disable;
+					};
+				};
+
+				scmi_pinctrl_i2c: i2c-pins {
+					groups {
+						groups = "I2C_UART";
+						function = "I2C";
+					};
+
+					pins {
+						pins = "P0", "P1";
+						drive-open-drain;
+					};
+				};
+
+				scmi_pinctrl_i2s: i2s-pins {
+					groups = "SPI_I2S";
+					function = "I2S";
+				};
+
+				scmi_pinctrl_spi: spi-pins {
+					groups = "SPI_I2S";
+					function = "SPI";
+
+					cs {
+						pinmux = <SANDBOX_PINMUX(5, SANDBOX_PINMUX_CS)>,
+							 <SANDBOX_PINMUX(6, SANDBOX_PINMUX_CS)>;
+					};
+				};
+			};
 		};
 	};
 
+#if 1
+	scmi_pinctrl_gpio_a: scmi_gpios {
+		compatible = "arm,scmi-gpio-generic";
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-bank-name = "scmi_gpios";
+		gpio-ranges = <&pinctrl_scmi 0 5 4>,
+			      <&pinctrl_scmi 4 0 0>;
+		gpio-ranges-group-names = "",
+					  "GPIO_B";
+
+		hog_input_1 {
+			gpio-hog;
+			input;
+			gpios = <1 GPIO_ACTIVE_HIGH>;
+		};
+		hog_output_3 {
+			gpio-hog;
+			output-high;
+			output-mode;
+			output-value = <1>;
+			gpios = <3 GPIO_ACTIVE_HIGH>;
+		};
+	};
+#endif
+
 	fpga {
 		compatible = "sandbox,fpga";
 	};
diff --git a/drivers/firmware/scmi/sandbox-scmi_agent.c b/drivers/firmware/scmi/sandbox-scmi_agent.c
index 27d17809be43..d5ff8a2b1c79 100644
--- a/drivers/firmware/scmi/sandbox-scmi_agent.c
+++ b/drivers/firmware/scmi/sandbox-scmi_agent.c
@@ -14,6 +14,7 @@
 #include <asm/io.h>
 #include <asm/scmi_test.h>
 #include <dm/device_compat.h>
+#include <dt-bindings/pinctrl/sandbox-pinmux.h>
 #include <linux/bitfield.h>
 #include <linux/kernel.h>
 
@@ -43,8 +44,11 @@
 #define SANDBOX_SCMI_AGENT_NAME "OSPM"
 #define SANDBOX_SCMI_PLATFORM_NAME "platform"
 
+#define SANDBOX_SCMI_PIN_CONTROL_PROTOCOL_VERSION SCMI_PIN_CONTROL_PROTOCOL_VERSION
+
 static u8 protocols[] = {
 	SCMI_PROTOCOL_ID_CLOCK,
+	SCMI_PROTOCOL_ID_PIN_CONTROL,
 	SCMI_PROTOCOL_ID_RESET_DOMAIN,
 	SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN,
 };
@@ -796,6 +800,696 @@ static int sandbox_scmi_voltd_level_get(struct udevice *dev,
 	return 0;
 }
 
+/* Pin control protocol */
+
+/*
+ * This driver emulates a pin controller with the following rules:
+ * - The pinctrl config for each pin must be set individually
+ * - The first two pins (P0-P1) must be muxed as a group
+ * - The next three pins (P2-P4) must be muxed as a group
+ * - The next four pins (P5-P8) must be muxed individually
+ * - The last two pins (P9-P10) must be fixed as a GPIO group only
+
+	P0:  "UART TX",  "I2C SCL"
+	P1:  "UART RX",  "I2C SDA"
+	P2:  "SPI SCLK", "I2S SCK"
+	P3:  "SPI MOSI", "I2S SD"
+	P4:  "SPI MISO", "I2S WS"
+	P5:  "GPIO0",    "SPI CS0"
+	P6:  "GPIO1",    "SPI CS1"
+	P7:  "GPIO2",    "PWM0"
+	P8:  "GPIO3",    "PWM1"
+	P9:  "GPIO_B"
+	P10: "GPIO_B"
+
+ */
+
+static const char * const sandbox_pins[] = {
+#define PIN(x) \
+	[x] = "P" #x
+	PIN(0),
+	PIN(1),
+	PIN(2),
+	PIN(3),
+	PIN(4),
+	PIN(5),
+	PIN(6),
+	PIN(7),
+	PIN(8),
+	PIN(9),
+	PIN(10),
+#undef PIN
+};
+
+static unsigned int sandbox_pin_functions[9];
+static u32 sandbox_pin_states[9][SCMI_PINCTRL_CONFIG_RESERVED];
+
+#define SANDBOX_GROUP_I2C_UART 0
+#define SANDBOX_GROUP_SPI_I2S 1
+#define SANDBOX_GROUP_GPIO_B 2
+
+static const char * const sandbox_groups[] = {
+	/* P0-P1 */
+	[SANDBOX_GROUP_I2C_UART] = "I2C_UART",
+	/* P2-P4 */
+	[SANDBOX_GROUP_SPI_I2S] = "SPI_I2S",
+	/* P9-P10 */
+	[SANDBOX_GROUP_GPIO_B] = "GPIO_B",
+};
+
+static const char * const sandbox_functions[] = {
+#define FUNC(id) \
+	[SANDBOX_PINMUX_##id] = #id
+	FUNC(UART),
+	FUNC(I2C),
+	FUNC(SPI),
+	FUNC(I2S),
+	FUNC(GPIO),
+	FUNC(CS),
+	FUNC(PWM),
+	/* FUNC(GPIO_B) */
+#undef FUNC
+};
+
+static int sandbox_scmi_pinctrl_protocol_version(struct udevice *dev,
+						 struct scmi_msg *msg)
+{
+	struct scmi_protocol_version_out *out = NULL;
+
+	if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	out = (struct scmi_protocol_version_out *)msg->out_msg;
+	out->version = SANDBOX_SCMI_PIN_CONTROL_PROTOCOL_VERSION;
+	out->status = SCMI_SUCCESS;
+
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_protocol_attrs(struct udevice *dev,
+					       struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_protocol_attrs_out *out = NULL;
+
+	if (!msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	out = (struct scmi_pinctrl_protocol_attrs_out *)msg->out_msg;
+	out->attributes_low = (ARRAY_SIZE(sandbox_groups) << 16)
+				+ ARRAY_SIZE(sandbox_pins);
+	out->attributes_high = ARRAY_SIZE(sandbox_functions);
+	out->status = SCMI_SUCCESS;
+
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_attrs(struct udevice *dev,
+				      struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_attrs_in *in;
+	struct scmi_pinctrl_attrs_out *out;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_attrs_in *)msg->in_msg;
+	out = (struct scmi_pinctrl_attrs_out *)msg->out_msg;
+
+	/*
+	 * Currently all pins have a name with less than 16 characters
+	 * (SCMI_PINCTRL_NAME_LENGTH_MAX).
+	 */
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id < ARRAY_SIZE(sandbox_pins)) {
+			strcpy(out->name, sandbox_pins[in->id]);
+			out->attributes = 0;
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id < ARRAY_SIZE(sandbox_groups)) {
+			strcpy(out->name, sandbox_groups[in->id]);
+			out->attributes = in->id ? 3 : 2;
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_FUNCTION:
+		if (in->id < ARRAY_SIZE(sandbox_functions)) {
+			strcpy(out->name, sandbox_functions[in->id]);
+			if (in->id == 4) /* UART */
+				out->attributes = 4;
+			else if (in->id == 5 || in->id == 6) /* CS or PWM */
+				out->attributes = 2;
+			else
+				out->attributes = 1;
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		out->status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	out->status = SCMI_SUCCESS;
+
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_list_assocs(struct udevice *dev,
+					    struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_list_assocs_in *in;
+	struct scmi_pinctrl_list_assocs_out *out;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_list_assocs_in *)msg->in_msg;
+	out = (struct scmi_pinctrl_list_assocs_out *)msg->out_msg;
+
+	/*
+	 * UART -> GROUP0
+	 * I2C  -> GROUP0
+	 * SPI  -> GROUP1
+	 * I2S  -> GROUP1
+	 * GPIO -> PIN5, 6, 7, 8
+	 * CS   -> PIN5, 6
+	 * PWM  -> PIN7, 8
+	 * (GPIO_B -> GROUP2)
+	 */
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id == SANDBOX_GROUP_I2C_UART) {
+			if (in->index == 0) {
+				out->array[0] = 0;
+				out->array[1] = 1;
+				out->flags = 2;
+			} else if (in->index == 1) {
+				out->array[0] = 0;
+				out->flags = 1;
+			} else {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+		} else if (in->id == SANDBOX_GROUP_SPI_I2S) {
+			if (in->index == 0) {
+				out->array[0] = 2;
+				out->array[1] = 3;
+				out->array[2] = 4;
+				out->flags = 3;
+			} else if (in->index == 1) {
+				out->array[0] = 3;
+				out->array[1] = 4;
+				out->flags = 2;
+			} else if (in->index == 2) {
+				out->array[0] = 4;
+				out->flags = 1;
+			} else {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+		} else if (in->id == SANDBOX_GROUP_GPIO_B) {
+			if (in->index == 0) {
+				out->array[0] = 9;
+				out->array[1] = 10;
+				out->flags = 2;
+			} else if (in->index == 1) {
+				out->array[0] = 10;
+				out->flags = 1;
+			} else {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_FUNCTION:
+		if (in->id == SANDBOX_PINMUX_UART) {
+			if (in->index > 0) {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+			out->array[0] = SANDBOX_GROUP_I2C_UART;
+			out->flags = 1;
+		} else if (in->id == SANDBOX_PINMUX_I2C) {
+			if (in->index > 0) {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+			out->array[0] = SANDBOX_GROUP_I2C_UART;
+			out->flags = 1;
+		} else if (in->id == SANDBOX_PINMUX_SPI) {
+			if (in->index > 0) {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+			out->array[0] = SANDBOX_GROUP_SPI_I2S;
+			out->flags = 1;
+		} else if (in->id == SANDBOX_PINMUX_I2S) {
+			if (in->index > 0) {
+				out->status = SCMI_OUT_OF_RANGE;
+				goto err;
+			}
+			out->array[0] = SANDBOX_GROUP_SPI_I2S;
+			out->flags = 1;
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		out->status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	out->status = SCMI_SUCCESS;
+
+err:
+	return 0;
+}
+
+static void copy_config(struct scmi_pin_entry *configs, u32 selector, int skip,
+			u32 *num, u32 *remaining)
+{
+	int max_num, i;
+
+	if ((skip + SCMI_PINCTRL_CONFIG_ENTRY_MAX)
+			> SCMI_PINCTRL_CONFIG_RESERVED)
+		max_num = SCMI_PINCTRL_CONFIG_RESERVED - skip;
+	else
+		max_num = SCMI_PINCTRL_CONFIG_ENTRY_MAX;
+
+	/* TODO: eliminate disabled properties? */
+	for (i = 0; i < max_num; i++) {
+		configs[i].type = skip + i;
+		configs[i].value = sandbox_pin_states[selector][skip + i];
+	}
+
+	*num = max_num;
+	*remaining = SCMI_PINCTRL_CONFIG_RESERVED - (skip + max_num);
+}
+
+static int sandbox_scmi_pinctrl_config_get(struct udevice *dev,
+					   struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_config_get_in *in;
+	struct scmi_pinctrl_config_get_out *out;
+	u32 type, num, remaining;
+	int all_configs, skip;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_config_get_in *)msg->in_msg;
+	out = (struct scmi_pinctrl_config_get_out *)msg->out_msg;
+
+	all_configs = in->attributes & SCMI_PINCTRL_CONFIG_GET_ALL;
+	skip = SCMI_PINCTRL_CONFIG_GET_SKIP(in->attributes);
+	type = SCMI_PINCTRL_CONFIG_GET_TYPE(in->attributes);
+	if (type >= SCMI_PINCTRL_CONFIG_RESERVED) {
+		out->status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	switch (SCMI_PINCTRL_CONFIG_GET_PINCTRL_TYPE(in->attributes)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id < ARRAY_SIZE(sandbox_pins)) {
+			if (all_configs) {
+				if (skip >= SCMI_PINCTRL_CONFIG_RESERVED) {
+					out->status = SCMI_INVALID_PARAMETERS;
+					goto err;
+				}
+				num = 0; /* avoid compiler warning */
+				remaining = 0;
+				copy_config(&out->configs[0], in->id, skip,
+					    &num, &remaining);
+			} else {
+				out->configs[0].type = type;
+				out->configs[0].value =
+					sandbox_pin_states[in->id][type];
+				num = 1;
+				remaining = 0;
+			}
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		out->num_configs =
+			SCMI_PINCTRL_CONFIG_GET_NUM_CONFIGS(remaining, num);
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id < ARRAY_SIZE(sandbox_groups)) {
+			/* TODO */;
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		out->status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	out->status = SCMI_SUCCESS;
+
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_config_set(struct udevice *dev,
+					   struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_config_set_in *in;
+	u32 *status;
+	int i, num;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_config_set_in *)msg->in_msg;
+	status = (u32 *)msg->out_msg;
+
+	num = SCMI_PINCTRL_CONFIG_SET_NUM_CONFIGS(in->attributes);
+	if (num > SCMI_PINCTRL_CONFIG_ENTRY_MAX) {
+		*status = SCMI_PROTOCOL_ERROR;
+		goto err;
+	}
+
+	switch (SCMI_PINCTRL_CONFIG_SET_PINCTRL_TYPE(in->attributes)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id < ARRAY_SIZE(sandbox_pins)) {
+			/* TODO: check value range */
+			for (i = 0; i < num; i++)
+				sandbox_pin_states[in->id][in->configs[i].type]
+					= in->configs[i].value;
+		} else {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		/* TODO: check value range */
+		if (!in->id) {
+			for (i = 0; i < num; i++) {
+				sandbox_pin_states[0][in->configs[i].type] =
+					in->configs[i].value;
+				sandbox_pin_states[1][in->configs[i].type] =
+					in->configs[i].value;
+			}
+		} else if (in->id == 1) {
+			for (i = 0; i < num; i++) {
+				sandbox_pin_states[2][in->configs[i].type] =
+					in->configs[i].value;
+				sandbox_pin_states[3][in->configs[i].type] =
+					in->configs[i].value;
+				sandbox_pin_states[4][in->configs[i].type] =
+					in->configs[i].value;
+			}
+		} else if (in->id == 2) {
+			for (i = 0; i < num; i++) {
+				sandbox_pin_states[9][in->configs[i].type] =
+					in->configs[i].value;
+				sandbox_pin_states[10][in->configs[i].type] =
+					in->configs[i].value;
+			}
+		} else {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		*status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	*status = SCMI_SUCCESS;
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_function_select(struct udevice *dev,
+						struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_function_select_in *in;
+	u32 *status;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_function_select_in *)msg->in_msg;
+	status = (u32 *)msg->out_msg;
+
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id == 5 || in->id == 6) {
+			if (in->function_id == SANDBOX_PINMUX_GPIO ||
+			    in->function_id == SANDBOX_PINMUX_CS) {
+				sandbox_pin_functions[in->id] = in->function_id;
+				*status = SCMI_SUCCESS;
+			} else {
+				*status = SCMI_NOT_SUPPORTED;
+			}
+		} else if (in->id == 7 || in->id == 8) {
+			if (in->function_id == SANDBOX_PINMUX_GPIO ||
+			    in->function_id == SANDBOX_PINMUX_PWM) {
+				sandbox_pin_functions[in->id] = in->function_id;
+				*status = SCMI_SUCCESS;
+			} else {
+				*status = SCMI_NOT_SUPPORTED;
+			}
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id == SANDBOX_GROUP_I2C_UART) {
+			if (in->function_id == SANDBOX_PINMUX_UART ||
+			    in->function_id == SANDBOX_PINMUX_I2C) {
+				sandbox_pin_functions[0] = in->function_id;
+				sandbox_pin_functions[1] = in->function_id;
+				*status = SCMI_SUCCESS;
+			} else {
+				*status = SCMI_NOT_SUPPORTED;
+			}
+		} else if (in->id == SANDBOX_GROUP_SPI_I2S) {
+			if (in->function_id == SANDBOX_PINMUX_SPI ||
+			    in->function_id == SANDBOX_PINMUX_I2S) {
+				sandbox_pin_functions[2] = in->function_id;
+				sandbox_pin_functions[3] = in->function_id;
+				sandbox_pin_functions[4] = in->function_id;
+				*status = SCMI_SUCCESS;
+			} else {
+				*status = SCMI_NOT_SUPPORTED;
+			}
+		}
+		break;
+	default:
+		*status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	*status = SCMI_SUCCESS;
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_request(struct udevice *dev,
+					struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_request_in *in;
+	u32 *status;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_request_in *)msg->in_msg;
+	status = (u32 *)msg->out_msg;
+
+	/*
+	 * No other agent, so always accept the request
+	 */
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id >= ARRAY_SIZE(sandbox_pins)) {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id >= ARRAY_SIZE(sandbox_groups)) {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		*status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	*status = SCMI_SUCCESS;
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_release(struct udevice *dev,
+					struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_release_in *in;
+	u32 *status;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_release_in *)msg->in_msg;
+	status = (u32 *)msg->out_msg;
+
+	/*
+	 * No other agent, so always accept the release
+	 */
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id >= ARRAY_SIZE(sandbox_pins)) {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id >= ARRAY_SIZE(sandbox_groups)) {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		*status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	*status = SCMI_SUCCESS;
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_name_get(struct udevice *dev,
+					 struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_name_get_in *in;
+	struct scmi_pinctrl_name_get_out *out;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*out))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_name_get_in *)msg->in_msg;
+	out = (struct scmi_pinctrl_name_get_out *)msg->out_msg;
+
+	/*
+	 * Currently all pins have a name with less than 64 characters
+	 */
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id < ARRAY_SIZE(sandbox_pins)) {
+			strcpy(out->name, sandbox_pins[in->id]);
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id < ARRAY_SIZE(sandbox_groups)) {
+			strcpy(out->name, sandbox_groups[in->id]);
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_FUNCTION:
+		if (in->id < ARRAY_SIZE(sandbox_functions)) {
+			strcpy(out->name, sandbox_functions[in->id]);
+		} else {
+			out->status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		out->status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	out->flags = 0;
+	out->status = SCMI_SUCCESS;
+
+err:
+	return 0;
+}
+
+static int sandbox_scmi_pinctrl_set_permissions(struct udevice *dev,
+						struct scmi_msg *msg)
+{
+	struct scmi_pinctrl_set_permissions_in *in;
+	u32 *status;
+
+	if (!msg->in_msg || msg->in_msg_sz < sizeof(*in) ||
+	    !msg->out_msg || msg->out_msg_sz < sizeof(*status))
+		return -EINVAL;
+
+	in = (struct scmi_pinctrl_set_permissions_in *)msg->in_msg;
+	status = (u32 *)msg->out_msg;
+
+	if (in->agent_id != 1) {
+		*status = SCMI_NOT_FOUND;
+		goto err;
+	}
+
+	switch (SCMI_PINCTRL_TYPE(in->flags)) {
+	case SCMI_PINCTRL_TYPE_PIN:
+		if (in->id < ARRAY_SIZE(sandbox_pins)) {
+			if (in->flags & SCMI_PINCTRL_PERMISSION)
+				*status = SCMI_SUCCESS;
+			else
+				/* unset not allowed */
+				*status = SCMI_DENIED;
+		} else {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	case SCMI_PINCTRL_TYPE_GROUP:
+		if (in->id < ARRAY_SIZE(sandbox_groups)) {
+			if (in->flags & SCMI_PINCTRL_PERMISSION)
+				*status = SCMI_SUCCESS;
+			else
+				/* unset not allowed */
+				*status = SCMI_DENIED;
+		} else {
+			*status = SCMI_NOT_FOUND;
+			goto err;
+		}
+		break;
+	default:
+		*status = SCMI_INVALID_PARAMETERS;
+		goto err;
+	}
+
+	*status = SCMI_SUCCESS;
+err:
+	return 0;
+}
+
 static int sandbox_scmi_test_process_msg(struct udevice *dev,
 					 struct scmi_channel *channel,
 					 struct scmi_msg *msg)
@@ -847,6 +1541,34 @@ static int sandbox_scmi_test_process_msg(struct udevice *dev,
 			break;
 		}
 		break;
+	case SCMI_PROTOCOL_ID_PIN_CONTROL:
+		switch (msg->message_id) {
+		case SCMI_PROTOCOL_VERSION:
+			return sandbox_scmi_pinctrl_protocol_version(dev, msg);
+		case SCMI_PROTOCOL_ATTRIBUTES:
+			return sandbox_scmi_pinctrl_protocol_attrs(dev, msg);
+		case SCMI_PINCTRL_ATTRIBUTES:
+			return sandbox_scmi_pinctrl_attrs(dev, msg);
+		case SCMI_PINCTRL_LIST_ASSOCIATIONS:
+			return sandbox_scmi_pinctrl_list_assocs(dev, msg);
+		case SCMI_PINCTRL_CONFIG_GET:
+			return sandbox_scmi_pinctrl_config_get(dev, msg);
+		case SCMI_PINCTRL_CONFIG_SET:
+			return sandbox_scmi_pinctrl_config_set(dev, msg);
+		case SCMI_PINCTRL_FUNCTION_SELECT:
+			return sandbox_scmi_pinctrl_function_select(dev, msg);
+		case SCMI_PINCTRL_REQUEST:
+			return sandbox_scmi_pinctrl_request(dev, msg);
+		case SCMI_PINCTRL_RELEASE:
+			return sandbox_scmi_pinctrl_release(dev, msg);
+		case SCMI_PINCTRL_NAME_GET:
+			return sandbox_scmi_pinctrl_name_get(dev, msg);
+		case SCMI_PINCTRL_SET_PERMISSIONS:
+			return sandbox_scmi_pinctrl_set_permissions(dev, msg);
+		default:
+			break;
+		}
+		break;
 	case SCMI_PROTOCOL_ID_RESET_DOMAIN:
 		switch (msg->message_id) {
 		case SCMI_RESET_DOMAIN_ATTRIBUTES:
