1 // SPDX-License-Identifier: GPL-2.0
2 /*
3 * System control and Management Interface (SCMI) NXP CPU Protocol
4 *
5 * Copyright 2025 NXP
6 */
7
8 #include <linux/bits.h>
9 #include <linux/io.h>
10 #include <linux/module.h>
11 #include <linux/of.h>
12 #include <linux/platform_device.h>
13 #include <linux/scmi_protocol.h>
14 #include <linux/scmi_imx_protocol.h>
15
16 #include "../../protocols.h"
17 #include "../../notify.h"
18
19 #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x10000
20
21 enum scmi_imx_cpu_protocol_cmd {
22 SCMI_IMX_CPU_ATTRIBUTES = 0x3,
23 SCMI_IMX_CPU_START = 0x4,
24 SCMI_IMX_CPU_STOP = 0x5,
25 SCMI_IMX_CPU_RESET_VECTOR_SET = 0x6,
26 SCMI_IMX_CPU_INFO_GET = 0xC,
27 };
28
29 struct scmi_imx_cpu_info {
30 u32 nr_cpu;
31 };
32
33 #define SCMI_IMX_CPU_NR_CPU_MASK GENMASK(15, 0)
34 struct scmi_msg_imx_cpu_protocol_attributes {
35 __le32 attributes;
36 };
37
38 struct scmi_msg_imx_cpu_attributes_out {
39 __le32 attributes;
40 #define CPU_MAX_NAME 16
41 u8 name[CPU_MAX_NAME];
42 };
43
44 struct scmi_imx_cpu_reset_vector_set_in {
45 __le32 cpuid;
46 #define CPU_VEC_FLAGS_RESUME BIT(31)
47 #define CPU_VEC_FLAGS_START BIT(30)
48 #define CPU_VEC_FLAGS_BOOT BIT(29)
49 __le32 flags;
50 __le32 resetvectorlow;
51 __le32 resetvectorhigh;
52 };
53
54 struct scmi_imx_cpu_info_get_out {
55 #define CPU_RUN_MODE_START 0
56 #define CPU_RUN_MODE_HOLD 1
57 #define CPU_RUN_MODE_STOP 2
58 #define CPU_RUN_MODE_SLEEP 3
59 __le32 runmode;
60 __le32 sleepmode;
61 __le32 resetvectorlow;
62 __le32 resetvectorhigh;
63 };
64
scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle * ph,u32 cpuid)65 static int scmi_imx_cpu_validate_cpuid(const struct scmi_protocol_handle *ph,
66 u32 cpuid)
67 {
68 struct scmi_imx_cpu_info *info = ph->get_priv(ph);
69
70 if (cpuid >= info->nr_cpu)
71 return -EINVAL;
72
73 return 0;
74 }
75
scmi_imx_cpu_start(const struct scmi_protocol_handle * ph,u32 cpuid,bool start)76 static int scmi_imx_cpu_start(const struct scmi_protocol_handle *ph,
77 u32 cpuid, bool start)
78 {
79 struct scmi_xfer *t;
80 u8 msg_id;
81 int ret;
82
83 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
84 if (ret)
85 return ret;
86
87 if (start)
88 msg_id = SCMI_IMX_CPU_START;
89 else
90 msg_id = SCMI_IMX_CPU_STOP;
91
92 ret = ph->xops->xfer_get_init(ph, msg_id, sizeof(u32), 0, &t);
93 if (ret)
94 return ret;
95
96 put_unaligned_le32(cpuid, t->tx.buf);
97 ret = ph->xops->do_xfer(ph, t);
98
99 ph->xops->xfer_put(ph, t);
100
101 return ret;
102 }
103
scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle * ph,u32 cpuid,u64 vector,bool start,bool boot,bool resume)104 static int scmi_imx_cpu_reset_vector_set(const struct scmi_protocol_handle *ph,
105 u32 cpuid, u64 vector, bool start,
106 bool boot, bool resume)
107 {
108 struct scmi_imx_cpu_reset_vector_set_in *in;
109 struct scmi_xfer *t;
110 int ret;
111
112 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
113 if (ret)
114 return ret;
115
116 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_RESET_VECTOR_SET, sizeof(*in),
117 0, &t);
118 if (ret)
119 return ret;
120
121 in = t->tx.buf;
122 in->cpuid = cpu_to_le32(cpuid);
123 in->flags = cpu_to_le32(0);
124 if (start)
125 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_START);
126 if (boot)
127 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_BOOT);
128 if (resume)
129 in->flags |= le32_encode_bits(1, CPU_VEC_FLAGS_RESUME);
130 in->resetvectorlow = cpu_to_le32(lower_32_bits(vector));
131 in->resetvectorhigh = cpu_to_le32(upper_32_bits(vector));
132 ret = ph->xops->do_xfer(ph, t);
133
134 ph->xops->xfer_put(ph, t);
135
136 return ret;
137 }
138
scmi_imx_cpu_started(const struct scmi_protocol_handle * ph,u32 cpuid,bool * started)139 static int scmi_imx_cpu_started(const struct scmi_protocol_handle *ph, u32 cpuid,
140 bool *started)
141 {
142 struct scmi_imx_cpu_info_get_out *out;
143 struct scmi_xfer *t;
144 u32 mode;
145 int ret;
146
147 if (!started)
148 return -EINVAL;
149
150 *started = false;
151 ret = scmi_imx_cpu_validate_cpuid(ph, cpuid);
152 if (ret)
153 return ret;
154
155 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_INFO_GET, sizeof(u32),
156 0, &t);
157 if (ret)
158 return ret;
159
160 put_unaligned_le32(cpuid, t->tx.buf);
161 ret = ph->xops->do_xfer(ph, t);
162 if (!ret) {
163 out = t->rx.buf;
164 mode = le32_to_cpu(out->runmode);
165 if (mode == CPU_RUN_MODE_START || mode == CPU_RUN_MODE_SLEEP)
166 *started = true;
167 }
168
169 ph->xops->xfer_put(ph, t);
170
171 return ret;
172 }
173
174 static const struct scmi_imx_cpu_proto_ops scmi_imx_cpu_proto_ops = {
175 .cpu_reset_vector_set = scmi_imx_cpu_reset_vector_set,
176 .cpu_start = scmi_imx_cpu_start,
177 .cpu_started = scmi_imx_cpu_started,
178 };
179
scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle * ph,struct scmi_imx_cpu_info * info)180 static int scmi_imx_cpu_protocol_attributes_get(const struct scmi_protocol_handle *ph,
181 struct scmi_imx_cpu_info *info)
182 {
183 struct scmi_msg_imx_cpu_protocol_attributes *attr;
184 struct scmi_xfer *t;
185 int ret;
186
187 ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
188 sizeof(*attr), &t);
189 if (ret)
190 return ret;
191
192 attr = t->rx.buf;
193
194 ret = ph->xops->do_xfer(ph, t);
195 if (!ret) {
196 info->nr_cpu = le32_get_bits(attr->attributes, SCMI_IMX_CPU_NR_CPU_MASK);
197 dev_info(ph->dev, "i.MX SM CPU: %d cpus\n",
198 info->nr_cpu);
199 }
200
201 ph->xops->xfer_put(ph, t);
202
203 return ret;
204 }
205
scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle * ph,u32 cpuid)206 static int scmi_imx_cpu_attributes_get(const struct scmi_protocol_handle *ph,
207 u32 cpuid)
208 {
209 struct scmi_msg_imx_cpu_attributes_out *out;
210 char name[SCMI_SHORT_NAME_MAX_SIZE] = {'\0'};
211 struct scmi_xfer *t;
212 int ret;
213
214 ret = ph->xops->xfer_get_init(ph, SCMI_IMX_CPU_ATTRIBUTES, sizeof(u32), 0, &t);
215 if (ret)
216 return ret;
217
218 put_unaligned_le32(cpuid, t->tx.buf);
219 ret = ph->xops->do_xfer(ph, t);
220 if (!ret) {
221 out = t->rx.buf;
222 strscpy(name, out->name, SCMI_SHORT_NAME_MAX_SIZE);
223 dev_info(ph->dev, "i.MX CPU: name: %s\n", name);
224 } else {
225 dev_err(ph->dev, "i.MX cpu: Failed to get info of cpu(%u)\n", cpuid);
226 }
227
228 ph->xops->xfer_put(ph, t);
229
230 return ret;
231 }
232
scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle * ph)233 static int scmi_imx_cpu_protocol_init(const struct scmi_protocol_handle *ph)
234 {
235 struct scmi_imx_cpu_info *info;
236 u32 version;
237 int ret, i;
238
239 ret = ph->xops->version_get(ph, &version);
240 if (ret)
241 return ret;
242
243 dev_info(ph->dev, "NXP SM CPU Protocol Version %d.%d\n",
244 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
245
246 info = devm_kzalloc(ph->dev, sizeof(*info), GFP_KERNEL);
247 if (!info)
248 return -ENOMEM;
249
250 ret = scmi_imx_cpu_protocol_attributes_get(ph, info);
251 if (ret)
252 return ret;
253
254 for (i = 0; i < info->nr_cpu; i++) {
255 ret = scmi_imx_cpu_attributes_get(ph, i);
256 if (ret)
257 return ret;
258 }
259
260 return ph->set_priv(ph, info, version);
261 }
262
263 static const struct scmi_protocol scmi_imx_cpu = {
264 .id = SCMI_PROTOCOL_IMX_CPU,
265 .owner = THIS_MODULE,
266 .instance_init = &scmi_imx_cpu_protocol_init,
267 .ops = &scmi_imx_cpu_proto_ops,
268 .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
269 .vendor_id = SCMI_IMX_VENDOR,
270 .sub_vendor_id = SCMI_IMX_SUBVENDOR,
271 };
272 module_scmi_protocol(scmi_imx_cpu);
273
274 MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_CPU) "-" SCMI_IMX_VENDOR);
275 MODULE_DESCRIPTION("i.MX SCMI CPU driver");
276 MODULE_LICENSE("GPL");
277