
From: Peng Fan peng.fan@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.Vaidyanathan@nxp.com Signed-off-by: Peng Fan peng.fan@nxp.com Signed-off-by: Alice Guo alice.guo@nxp.com Reviewed-by: Ye Li ye.li@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..5f101f4ede8b585f8c562a42886ea7be4ef09953 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 b1960c56b512cc113a810304dc3ac3f99b237a7e..3d53d0cd4b60c0685dd4c73c23e61cb712cda0ee 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@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 { /* @@ -216,7 +219,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 = ofnode_get_addr_size_index(dev_ofnode(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.freenix@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 */