1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
|
// 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_agent-uclass.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,
};
static struct scmi_proto_match match[] = {
{ .proto_id = SCMI_PROTOCOL_ID_PINCTRL },
{ /* Sentinel */ }
};
U_BOOT_SCMI_PROTO_DRIVER(scmi_pinctrl_imx, match);
|