pinctrl: nxp: add a pin controller driver based on SCMI pin control protocol
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.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>
This commit is contained in:
@@ -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
|
||||
|
@@ -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-imx-scmi.o
|
||||
|
157
drivers/pinctrl/nxp/pinctrl-imx-scmi.c
Normal file
157
drivers/pinctrl/nxp/pinctrl-imx-scmi.c
Normal file
@@ -0,0 +1,157 @@
|
||||
// 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/devres.h>
|
||||
#include <dm/pinctrl.h>
|
||||
#include <scmi_agent.h>
|
||||
#include <scmi_protocols.h>
|
||||
|
||||
#include "pinctrl-imx.h"
|
||||
|
||||
#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) {
|
||||
in.configs[num_cfgs].type = PINCTRL_TYPE_MUX;
|
||||
in.configs[num_cfgs].val = mux;
|
||||
num_cfgs++;
|
||||
}
|
||||
|
||||
if (config_val) {
|
||||
in.configs[num_cfgs].type = PINCTRL_TYPE_CONFIG;
|
||||
in.configs[num_cfgs].val = config_val;
|
||||
num_cfgs++;
|
||||
}
|
||||
|
||||
if (input_ofs) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
devm_kfree(dev, pin_data);
|
||||
|
||||
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);
|
||||
|
||||
if (IS_ENABLED(CONFIG_IMX95))
|
||||
priv->daisy_offset = DAISY_OFFSET_IMX95;
|
||||
else
|
||||
return -EINVAL;
|
||||
|
||||
return devm_scmi_of_get_channel(dev);
|
||||
}
|
||||
|
||||
static int imx_scmi_pinctrl_bind(struct udevice *dev)
|
||||
{
|
||||
if (IS_ENABLED(CONFIG_IMX95))
|
||||
return 0;
|
||||
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
U_BOOT_DRIVER(scmi_pinctrl_imx) = {
|
||||
.name = "scmi_pinctrl_imx",
|
||||
.id = UCLASS_PINCTRL,
|
||||
.bind = imx_scmi_pinctrl_bind,
|
||||
.probe = imx_scmi_pinctrl_probe,
|
||||
.priv_auto = sizeof(struct imx_scmi_pinctrl_priv),
|
||||
.ops = &imx_scmi_pinctrl_ops,
|
||||
.flags = DM_FLAG_PRE_RELOC,
|
||||
};
|
@@ -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 */
|
||||
|
Reference in New Issue
Block a user