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-imx.c | 7 +- drivers/pinctrl/nxp/pinctrl-imx.h | 11 +++ drivers/pinctrl/nxp/pinctrl-scmi.c | 136 +++++++++++++++++++++++++++++++++++++ include/scmi_protocols.h | 34 ++++++++++ 6 files changed, 200 insertions(+), 2 deletions(-) diff --git a/drivers/pinctrl/nxp/Kconfig b/drivers/pinctrl/nxp/Kconfig index 06c26f156f6c8f63204604d6065485629cfd9b61..ec63ab595650d0dfab7e3a7dc01d4af7814b6773 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..0026aae00afe2b0aff40c3288c231b3065ef656a 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 2025 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 | 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..f120e32d9da678d6e9ce583f0ba3c6c17d050f36 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 2025 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 +#if IS_ENABLED(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..7d2ed6542dc5166ebb07ade6e931b83b370af386 --- /dev/null +++ b/drivers/pinctrl/nxp/pinctrl-scmi.c @@ -0,0 +1,136 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright 2025 NXP + */ + +#include <asm/io.h> +#include <asm/mach-imx/iomux-v3.h> +#include <dm.h> +#include <dm/device_compat.h> +#include <errno.h> +#include <linux/bitops.h> +#include <misc.h> +#include <scmi_agent.h> +#include <scmi_protocols.h> + +#include "pinctrl-imx.h" + +#if IS_ENABLED(CONFIG_IMX93) +#define DAISY_OFFSET 0x360 +#endif +#if IS_ENABLED(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_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