From: Alice Guo <alice....@nxp.com>

This patch provides a pinctrl driver based on SCMI pin control protocol.
Currently, only the PINCTRL_CONFIG_SET command is implemented.

Signed-off-by: Ranjani Vaidyanathan <ranjani.vaidyanat...@nxp.com>
Signed-off-by: Peng Fan <peng....@nxp.com>
Signed-off-by: Alice Guo <alice....@nxp.com>
Reviewed-by: Ye Li <ye...@nxp.com>
---
 drivers/pinctrl/nxp/Kconfig        |  13 +++
 drivers/pinctrl/nxp/Makefile       |   1 +
 drivers/pinctrl/nxp/pinctrl-scmi.c | 143 +++++++++++++++++++++++++++++
 include/scmi_protocols.h           |  36 ++++++++
 4 files changed, 193 insertions(+)
 create mode 100644 drivers/pinctrl/nxp/pinctrl-scmi.c

diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index d13c5f2a6d5..84d9a3641ff 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -139,6 +139,19 @@ config PINCTRL_IMXRT
          only parses the 'fsl,pins' property and configure related
          registers.
 
+config PINCTRL_IMX_SCMI
+       bool "IMX pinctrl SCMI driver"
+       depends on ARCH_IMX9 && PINCTRL_FULL
+       select PINCTRL_IMX
+       help
+         This provides a simple pinctrl driver for i.MX SoC which supports
+         SCMI. This feature depends on device tree configuration. This driver
+         is different from the linux one, this is a simple implementation,
+         only parses the 'fsl,pins' property and configure related
+         registers.
+
+         Say Y here to enable the imx pinctrl SCMI driver
+
 config PINCTRL_VYBRID
        bool "Vybrid (vf610) pinctrl driver"
        depends on ARCH_VF610 && PINCTRL_FULL
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile
index 44e37c631e5..9ef675d18c6 100644
--- a/drivers/pinctrl/nxp/Makefile
+++ b/drivers/pinctrl/nxp/Makefile
@@ -11,3 +11,4 @@ obj-$(CONFIG_PINCTRL_IMX93)           += pinctrl-imx93.o
 obj-$(CONFIG_PINCTRL_MXS)              += pinctrl-mxs.o
 obj-$(CONFIG_PINCTRL_VYBRID)           += pinctrl-vf610.o
 obj-$(CONFIG_PINCTRL_IMXRT)            += pinctrl-imxrt.o
+obj-$(CONFIG_PINCTRL_IMX_SCMI)         += pinctrl-scmi.o
diff --git a/drivers/pinctrl/nxp/pinctrl-scmi.c 
b/drivers/pinctrl/nxp/pinctrl-scmi.c
new file mode 100644
index 00000000000..4a791b7e951
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-scmi.c
@@ -0,0 +1,143 @@
+// 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/pinctrl.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+
+#include "pinctrl-imx.h"
+
+#define DAISY_OFFSET_IMX93     0x360
+#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 != 0) {
+               in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
+               in.configs[num_cfgs].val = mux;
+               num_cfgs++;
+       }
+
+       if (config_val != 0) {
+               in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
+               in.configs[num_cfgs].val = config_val;
+               num_cfgs++;
+       }
+
+       if (input_ofs != 0) {
+               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 != 0) {
+               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);
+               }
+       }
+
+       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);
+
+       priv->daisy_offset = is_imx93() ? DAISY_OFFSET_IMX93 : 
DAISY_OFFSET_IMX95;
+
+       return devm_scmi_of_get_channel(dev);
+}
+
+U_BOOT_DRIVER(scmi_pinctrl_imx) = {
+       .name = "scmi_pinctrl_imx",
+       .id = UCLASS_PINCTRL,
+       .probe = imx_scmi_pinctrl_probe,
+       .priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
+       .ops = &imx_scmi_pinctrl_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index 7abb2a6f36b..279ebbad440 100644
--- a/include/scmi_protocols.h
+++ b/include/scmi_protocols.h
@@ -24,6 +24,7 @@ enum scmi_std_protocol {
        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 {
@@ -1005,4 +1006,39 @@ struct scmi_voltd_level_get_out {
        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 */
-- 
2.43.0

Reply via email to