Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c
26498 views
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
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
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
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
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
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
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
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
278