--- /dev/null
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2025 NXP
+ */
+
+#include <asm/io.h>
+#include <asm/mach-imx/sys_proto.h>
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <dm/devres.h>
+#include <dm/pinctrl.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+
+#include "pinctrl-imx.h"
+
+#define DAISY_OFFSET_IMX95 0x408
+
+/* SCMI pin control types */
+#define PINCTRL_TYPE_MUX 192
+#define PINCTRL_TYPE_CONFIG 193
+#define PINCTRL_TYPE_DAISY_ID 194
+#define PINCTRL_TYPE_DAISY_CFG 195
+#define PINCTRL_NUM_CFGS_SHIFT 2
+
+struct imx_scmi_pinctrl_priv {
+ u16 daisy_offset;
+};
+
+static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 config_val,
+ u32 input_ofs, u32 input_val)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+ int ret, num_cfgs = 0;
+ struct scmi_msg msg;
+
+ /* Call SCMI API to set the pin mux and configuration. */
+ struct scmi_pinctrl_config_set_out out;
+ struct scmi_pinctrl_config_set_in in = {
+ .identifier = mux_ofs / 4,
+ .function_id = 0xFFFFFFFF,
+ .attributes = 0,
+ };
+
+ if (mux_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
+ in.configs[num_cfgs].val = mux;
+ num_cfgs++;
+ }
+
+ if (config_val) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
+ in.configs[num_cfgs].val = config_val;
+ num_cfgs++;
+ }
+
+ if (input_ofs) {
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_ID;
+ in.configs[num_cfgs].val = (input_ofs - priv->daisy_offset) / 4;
+ num_cfgs++;
+ in.configs[num_cfgs].type = PINCTRL_TYPE_DAISY_CFG;
+ in.configs[num_cfgs].val = input_val;
+ num_cfgs++;
+ }
+
+ /* Update the number of configs sent in this call. */
+ in.attributes = num_cfgs << PINCTRL_NUM_CFGS_SHIFT;
+
+ msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
+ SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
+
+ ret = devm_scmi_process_msg(dev, &msg);
+ if (ret || out.status) {
+ dev_err(dev, "Failed to set PAD = %d, daisy = %d, scmi_err = %d, ret = %d\n",
+ mux_ofs / 4, input_ofs / 4, out.status, ret);
+ }
+
+ return ret;
+}
+
+static int imx_pinctrl_set_state_scmi(struct udevice *dev, struct udevice *config)
+{
+ int mux_ofs, mux, config_val, input_reg, input_val;
+ u32 *pin_data;
+ int i, j = 0;
+ int npins;
+ int ret;
+
+ ret = imx_pinctrl_set_state_common(dev, config, FSL_PIN_SIZE,
+ &pin_data, &npins);
+ if (ret)
+ return ret;
+
+ /*
+ * Refer to linux documentation for details:
+ * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
+ */
+ for (i = 0; i < npins; i++) {
+ mux_ofs = pin_data[j++];
+ /* Skip config_reg */
+ j++;
+ input_reg = pin_data[j++];
+
+ mux = pin_data[j++];
+ input_val = pin_data[j++];
+ config_val = pin_data[j++];
+
+ if (config_val & IMX_PAD_SION)
+ mux |= IOMUXC_CONFIG_SION;
+
+ config_val &= ~IMX_PAD_SION;
+
+ ret = imx_pinconf_scmi_set(dev, mux_ofs, mux, config_val, input_reg, input_val);
+ if (ret && ret != -EPERM) {
+ dev_err(dev, "Set pin %d, mux %d, val %d, error\n",
+ mux_ofs, mux, config_val);
+ }
+ }
+
+ devm_kfree(dev, pin_data);
+
+ return ret;
+}
+
+static const struct pinctrl_ops imx_scmi_pinctrl_ops = {
+ .set_state = imx_pinctrl_set_state_scmi,
+};
+
+static int imx_scmi_pinctrl_probe(struct udevice *dev)
+{
+ struct imx_scmi_pinctrl_priv *priv = dev_get_priv(dev);
+
+ if (IS_ENABLED(CONFIG_IMX95))
+ priv->daisy_offset = DAISY_OFFSET_IMX95;
+ else
+ return -EINVAL;
+
+ return devm_scmi_of_get_channel(dev);
+}
+
+static int imx_scmi_pinctrl_bind(struct udevice *dev)
+{
+ if (IS_ENABLED(CONFIG_IMX95))
+ return 0;
+
+ return -ENODEV;
+}
+
+U_BOOT_DRIVER(scmi_pinctrl_imx) = {
+ .name = "scmi_pinctrl_imx",
+ .id = UCLASS_PINCTRL,
+ .bind = imx_scmi_pinctrl_bind,
+ .probe = imx_scmi_pinctrl_probe,
+ .priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
+ .ops = &imx_scmi_pinctrl_ops,
+ .flags = DM_FLAG_PRE_RELOC,
+};
SCMI_PROTOCOL_ID_SENSOR = 0x15,
SCMI_PROTOCOL_ID_RESET_DOMAIN = 0x16,
SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN = 0x17,
+ SCMI_PROTOCOL_ID_PINCTRL = 0x19,
};
enum scmi_status_code {
s32 voltage_level;
};
+/* SCMI Pinctrl Protocol */
+enum scmi_pinctrl_message_id {
+ SCMI_MSG_PINCTRL_CONFIG_SET = 0x6
+};
+
+struct scmi_pin_config {
+ u32 type;
+ u32 val;
+};
+
+/**
+ * struct scmi_pad_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.
+ * @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.
+ * @configs: Array of configurations.
+ */
+struct scmi_pinctrl_config_set_in {
+ u32 identifier;
+ u32 function_id;
+ u32 attributes;
+ struct scmi_pin_config configs[4];
+};
+
+struct scmi_pinctrl_config_set_out {
+ s32 status;
+};
+
#endif /* _SCMI_PROTOCOLS_H */