[PATCH v1] pinctrl: nxp: add SCMI pinctrl driver
alice.guo at oss.nxp.com
alice.guo at oss.nxp.com
Sun Oct 6 11:05:40 CEST 2024
From: Peng Fan <peng.fan at nxp.com>
Add SCMI based iMX pinctrl driver to use SCMI 3.2 pinctrl protocol.
Signed-off-by: Ranjani Vaidyanathan <Ranjani.Vaidyanathan at nxp.com>
Signed-off-by: Peng Fan <peng.fan at nxp.com>
Signed-off-by: Alice Guo <alice.guo at nxp.com>
Reviewed-by: Ye Li <ye.li at 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 | 1 +
include/scmi_protocols.h | 28 +++++
8 files changed, 206 insertions(+), 2 deletions(-)
create mode 100644 drivers/pinctrl/nxp/pinctrl-scmi.c
diff --git a/drivers/firmware/scmi/scmi_agent-uclass.c b/drivers/firmware/scmi/scmi_agent-uclass.c
index 8c907c3b03..6b9d8361c2 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 06c26f156f..0086f98258 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 f10aa6ef18..3ec3e2a9c6 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 ff466c4910..496aeed219 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.freenix at gmail.com>
+ * Copyright 2023 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 fa4c084e2f..3cbd5e2607 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.freenix at gmail.com>
+ * Copyright 2023 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 0000000000..1e8a7fe6af
--- /dev/null
+++ b/drivers/pinctrl/nxp/pinctrl-scmi.c
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Copyright 2023 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 33e0e18c30..511f0063a9 100644
--- a/include/scmi_agent-uclass.h
+++ b/include/scmi_agent-uclass.h
@@ -43,6 +43,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 7abb2a6f36..e52c740cb3 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 {
@@ -780,6 +781,7 @@ struct scmi_clk_attribute_out {
struct scmi_clk_state_in {
u32 clock_id;
u32 attributes;
+ u32 oem_config_val;
};
/**
@@ -1005,4 +1007,30 @@ 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
+ * @clock_id: SCMI clock ID
+ * @parent_clk: SCMI clock ID
+ */
+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
More information about the U-Boot
mailing list