From: Peng Fan <peng....@nxp.com>

This patch provides a driver for the SCMI pin control protocol which is
based on ARM's System Control and Management Interface (SCMI) 3.2.
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/firmware/scmi/scmi_agent-uclass.c |  11 +++
 drivers/pinctrl/nxp/Kconfig               |  13 +++
 drivers/pinctrl/nxp/Makefile              |   1 +
 drivers/pinctrl/nxp/pinctrl-imx.c         |   7 +-
 drivers/pinctrl/nxp/pinctrl-imx.h         |  11 +++
 drivers/pinctrl/nxp/pinctrl-scmi.c        | 136 ++++++++++++++++++++++++++++++
 include/scmi_agent-uclass.h               |   2 +
 include/scmi_protocols.h                  |  34 ++++++++
 8 files changed, 213 insertions(+), 2 deletions(-)

diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c 
b/drivers/firmware/scmi/scmi_agent-uclass.c
index 
8c907c3b0328095c4b35ba089ed608fcda48b567..6b9d8361c236114b6ca6c36390ab1aeab9e60e0f
 100644
--- a/drivers/firmware/scmi/scmi_agent-uclass.c
+++ b/drivers/firmware/scmi/scmi_agent-uclass.c
@@ -97,6 +97,9 @@ struct udevice *scmi_get_protocol(struct udevice *dev,
        case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
                proto = priv->voltagedom_dev;
                break;
+       case SCMI_PROTOCOL_ID_PINCTRL:
+               proto = priv->pinctrl_dev;
+               break;
        default:
                dev_err(dev, "Protocol not supported\n");
                proto = NULL;
@@ -147,6 +150,9 @@ static int scmi_add_protocol(struct udevice *dev,
        case SCMI_PROTOCOL_ID_VOLTAGE_DOMAIN:
                priv->voltagedom_dev = proto;
                break;
+       case SCMI_PROTOCOL_ID_PINCTRL:
+               priv->pinctrl_dev = proto;
+               break;
        default:
                dev_err(dev, "Protocol not supported\n");
                return -EPROTO;
@@ -436,6 +442,11 @@ static int scmi_bind_protocols(struct udevice *dev)
                                drv = DM_DRIVER_GET(scmi_voltage_domain);
                        }
                        break;
+               case SCMI_PROTOCOL_ID_PINCTRL:
+                       if (IS_ENABLED(CONFIG_PINCTRL_IMX_SCMI) &&
+                               scmi_protocol_is_supported(dev, protocol_id))
+                               drv = DM_DRIVER_GET(scmi_pinctrl_imx);
+                       break;
                default:
                        break;
                }
diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig
index 
06c26f156f6c8f63204604d6065485629cfd9b61..0086f9825837bf568bd7633a21da860e410eeeee
 100644
--- a/drivers/pinctrl/nxp/Kconfig
+++ b/drivers/pinctrl/nxp/Kconfig
@@ -1,6 +1,19 @@
 config PINCTRL_IMX
        bool
 
+config PINCTRL_IMX_SCMI
+       bool "IMX pinctrl SCMI driver"
+       depends on ARCH_IMX9 && PINCTRL_FULL
+       select PINCTRL_IMX
+       help
+         Say Y here to enable the imx pinctrl scmi driver
+
+         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.
+
 config PINCTRL_IMX_SCU
        bool
 
diff --git a/drivers/pinctrl/nxp/Makefile b/drivers/pinctrl/nxp/Makefile
index 
f10aa6ef188e37583b181bdf9d70ac191e506d75..3ec3e2a9c6fdb875da4ae9bf151df5666256883b
 100644
--- a/drivers/pinctrl/nxp/Makefile
+++ b/drivers/pinctrl/nxp/Makefile
@@ -4,6 +4,7 @@ obj-$(CONFIG_PINCTRL_IMX6)              += pinctrl-imx6.o
 obj-$(CONFIG_PINCTRL_IMX7)             += pinctrl-imx7.o
 obj-$(CONFIG_PINCTRL_IMX7ULP)          += pinctrl-imx7ulp.o
 obj-$(CONFIG_PINCTRL_IMX8ULP)          += pinctrl-imx8ulp.o
+obj-$(CONFIG_PINCTRL_IMX_SCMI)         += pinctrl-scmi.o
 obj-$(CONFIG_PINCTRL_IMX_SCU)          += pinctrl-scu.o
 obj-$(CONFIG_PINCTRL_IMX8)             += pinctrl-imx8.o
 obj-$(CONFIG_PINCTRL_IMX8M)            += pinctrl-imx8m.o
diff --git a/drivers/pinctrl/nxp/pinctrl-imx.c 
b/drivers/pinctrl/nxp/pinctrl-imx.c
index 
ff466c4910414db3ad8790cd59596a9836b29e28..cdadf7a3f10e3be940fe5660a7103143a7f517b3
 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx.c
+++ b/drivers/pinctrl/nxp/pinctrl-imx.c
@@ -1,6 +1,7 @@
 // SPDX-License-Identifier: GPL-2.0+
 /*
  * Copyright (C) 2016 Peng Fan <van.free...@gmail.com>
+ * Copyright 2024 NXP
  */
 
 #include <malloc.h>
@@ -65,7 +66,9 @@ static int imx_pinctrl_set_state(struct udevice *dev, struct 
udevice *config)
 
        npins = size / pin_size;
 
-       if (info->flags & IMX8_USE_SCU) {
+       if (info->flags & IMX_USE_SCMI) {
+               return (imx_pinctrl_scmi_conf_pins(dev, pin_data, npins));
+       } else if (info->flags & IMX8_USE_SCU) {
                imx_pinctrl_scu_conf_pins(info, pin_data, npins);
        } else {
                /*
@@ -215,7 +218,7 @@ int imx_pinctrl_probe(struct udevice *dev,
        priv->dev = dev;
        priv->info = info;
 
-       if (info->flags & IMX8_USE_SCU)
+       if ((info->flags & IMX8_USE_SCU) || (info->flags & IMX_USE_SCMI))
                return 0;
 
        addr = devfdt_get_addr_size_index(dev, 0, &size);
diff --git a/drivers/pinctrl/nxp/pinctrl-imx.h 
b/drivers/pinctrl/nxp/pinctrl-imx.h
index 
fa4c084e2fc067fcb4e92c33312d4f430081fffd..b2f8865ded0ae380fc0156baa5eaea505c12f501
 100644
--- a/drivers/pinctrl/nxp/pinctrl-imx.h
+++ b/drivers/pinctrl/nxp/pinctrl-imx.h
@@ -1,6 +1,7 @@
 /* SPDX-License-Identifier: GPL-2.0+ */
 /*
  * Copyright (C) 2016 Peng Fan <van.free...@gmail.com>
+ * Copyright 2024 NXP
  */
 
 #ifndef __DRIVERS_PINCTRL_IMX_H
@@ -47,6 +48,7 @@ extern const struct pinctrl_ops imx_pinctrl_ops;
 #define ZERO_OFFSET_VALID      0x2
 #define CFG_IBE_OBE            0x4
 #define IMX8_USE_SCU           0x8
+#define IMX_USE_SCMI           0x10
 
 #define IOMUXC_CONFIG_SION     (0x1 << 4)
 
@@ -65,4 +67,13 @@ static inline int imx_pinctrl_scu_conf_pins(struct 
imx_pinctrl_soc_info *info,
 }
 #endif
 
+#ifdef CONFIG_PINCTRL_IMX_SCMI
+int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 *pin_data, int npins);
+#else
+static inline int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 
*pin_data, int npins)
+{
+       return 0;
+}
+#endif
+
 #endif /* __DRIVERS_PINCTRL_IMX_H */
diff --git a/drivers/pinctrl/nxp/pinctrl-scmi.c 
b/drivers/pinctrl/nxp/pinctrl-scmi.c
new file mode 100644
index 
0000000000000000000000000000000000000000..19b1b433a5ced0bb85e52f4d19bdeab2825f275e
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-scmi.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2024 NXP
+ */
+
+#include <dm.h>
+#include <dm/device_compat.h>
+#include <errno.h>
+#include <misc.h>
+#include <scmi_agent.h>
+#include <scmi_protocols.h>
+#include <asm/io.h>
+#include <asm/mach-imx/iomux-v3.h>
+#include <linux/bitops.h>
+
+#include "pinctrl-imx.h"
+
+#if defined(CONFIG_IMX93)
+#define DAISY_OFFSET   0x360
+#endif
+#if defined(CONFIG_IMX95)
+#define DAISY_OFFSET   0x408
+#endif
+
+/* 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
+
+static int imx_pinconf_scmi_set(struct udevice *dev, u32 mux_ofs, u32 mux, u32 
config_val,
+                               u32 input_ofs, u32 input_val)
+{
+       int ret, num_cfgs = 0;
+
+       /* 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 - 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;
+
+       struct scmi_msg msg = SCMI_MSG_IN(SCMI_PROTOCOL_ID_PINCTRL,
+                                         SCMI_MSG_PINCTRL_CONFIG_SET, in, out);
+
+       ret = devm_scmi_process_msg(dev, &msg);
+       if (ret != 0 || 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;
+}
+
+int imx_pinctrl_scmi_conf_pins(struct udevice *dev, u32 *pin_data, int npins)
+{
+       int mux_ofs, mux, config_val, input_reg, input_val;
+       int i, j = 0;
+       int 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 struct imx_pinctrl_soc_info imx_pinctrl_scmi_soc_info 
__section(".data") = {
+       .flags = ZERO_OFFSET_VALID | IMX_USE_SCMI,
+};
+
+static int imx_scmi_pinctrl_probe(struct udevice *dev)
+{
+       struct imx_pinctrl_priv *priv = dev_get_priv(dev);
+       int ret;
+
+       ret = devm_scmi_of_get_channel(dev);
+       if (ret) {
+               dev_err(dev, "get channel: %d\n", ret);
+               return ret;
+       }
+
+       debug("%s %p %s\n", __func__, priv, dev->name);
+       return imx_pinctrl_probe(dev, &imx_pinctrl_scmi_soc_info);
+}
+
+U_BOOT_DRIVER(scmi_pinctrl_imx) = {
+       .name = "scmi_pinctrl_imx",
+       .id = UCLASS_PINCTRL,
+       .probe = imx_scmi_pinctrl_probe,
+       .remove = imx_pinctrl_remove,
+       .priv_auto = sizeof(struct imx_pinctrl_priv),
+       .ops = &imx_pinctrl_ops,
+       .flags = DM_FLAG_PRE_RELOC,
+};
diff --git a/include/scmi_agent-uclass.h b/include/scmi_agent-uclass.h
index 
33e0e18c30d4ae4ddaaa2cb3830c03cd7a559cc0..4beec43cb080b14a777cac1cb0f18ef53433596c
 100644
--- a/include/scmi_agent-uclass.h
+++ b/include/scmi_agent-uclass.h
@@ -27,6 +27,7 @@ struct scmi_channel;
  * @clock_dev:         SCMI clock protocol device
  * @resetdom_dev:      SCMI reset domain protocol device
  * @voltagedom_dev:    SCMI voltage domain protocol device
+ * @pinctrl_dev:       SCMI pin control protocol device
  */
 struct scmi_agent_priv {
        u32 version;
@@ -43,6 +44,7 @@ struct scmi_agent_priv {
        struct udevice *clock_dev;
        struct udevice *resetdom_dev;
        struct udevice *voltagedom_dev;
+       struct udevice *pinctrl_dev;
 };
 
 static inline u32 scmi_version(struct udevice *dev)
diff --git a/include/scmi_protocols.h b/include/scmi_protocols.h
index 
7abb2a6f36ba53223bd9103fcc02f58506fbfc8d..2367467512a6b65253317e51bea71de396aa96f9
 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,37 @@ 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.34.1

Reply via email to