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