File Index
Files and directories
Sign in
FILE

imx-sm-cpu.c

File preview with syntax highlighting.

linux-6.19.14/drivers/firmware/arm_scmi/vendors/imx/imx-sm-cpu.c C
  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
 21enum 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
 29struct scmi_imx_cpu_info {
 30	u32 nr_cpu;
 31};
 32
 33#define SCMI_IMX_CPU_NR_CPU_MASK	GENMASK(15, 0)
 34struct scmi_msg_imx_cpu_protocol_attributes {
 35	__le32 attributes;
 36};
 37
 38struct scmi_msg_imx_cpu_attributes_out {
 39	__le32 attributes;
 40#define	CPU_MAX_NAME	16
 41	u8 name[CPU_MAX_NAME];
 42};
 43
 44struct 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
 54struct 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
 65static 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
 76static 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
104static 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
139static 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
174static 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
180static 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
206static 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
233static 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
263static 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};
272module_scmi_protocol(scmi_imx_cpu);
273
274MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_CPU) "-" SCMI_IMX_VENDOR);
275MODULE_DESCRIPTION("i.MX SCMI CPU driver");
276MODULE_LICENSE("GPL");
~/ Read-only file preview.