xref: /linux/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c (revision 55a42f78ffd386e01a5404419f8c5ded7db70a21)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * System control and Management Interface (SCMI) NXP MISC Protocol
4  *
5  * Copyright 2024 NXP
6  */
7 
8 #define pr_fmt(fmt) "SCMI Notifications MISC - " fmt
9 
10 #include <linux/bits.h>
11 #include <linux/io.h>
12 #include <linux/module.h>
13 #include <linux/of.h>
14 #include <linux/platform_device.h>
15 #include <linux/scmi_protocol.h>
16 #include <linux/scmi_imx_protocol.h>
17 
18 #include "../../protocols.h"
19 #include "../../notify.h"
20 
21 #define SCMI_PROTOCOL_SUPPORTED_VERSION		0x10000
22 
23 #define MAX_MISC_CTRL_SOURCES			GENMASK(15, 0)
24 
25 enum scmi_imx_misc_protocol_cmd {
26 	SCMI_IMX_MISC_CTRL_SET	= 0x3,
27 	SCMI_IMX_MISC_CTRL_GET	= 0x4,
28 	SCMI_IMX_MISC_DISCOVER_BUILD_INFO = 0x6,
29 	SCMI_IMX_MISC_CTRL_NOTIFY = 0x8,
30 	SCMI_IMX_MISC_CFG_INFO_GET = 0xC,
31 	SCMI_IMX_MISC_BOARD_INFO = 0xE,
32 };
33 
34 struct scmi_imx_misc_info {
35 	u32 version;
36 	u32 nr_dev_ctrl;
37 	u32 nr_brd_ctrl;
38 	u32 nr_reason;
39 };
40 
41 struct scmi_msg_imx_misc_protocol_attributes {
42 	__le32 attributes;
43 };
44 
45 #define GET_BRD_CTRLS_NR(x)	le32_get_bits((x), GENMASK(31, 24))
46 #define GET_REASONS_NR(x)	le32_get_bits((x), GENMASK(23, 16))
47 #define GET_DEV_CTRLS_NR(x)	le32_get_bits((x), GENMASK(15, 0))
48 #define BRD_CTRL_START_ID	BIT(15)
49 
50 struct scmi_imx_misc_ctrl_set_in {
51 	__le32 id;
52 	__le32 num;
53 	__le32 value[];
54 };
55 
56 struct scmi_imx_misc_ctrl_notify_in {
57 	__le32 ctrl_id;
58 	__le32 flags;
59 };
60 
61 struct scmi_imx_misc_ctrl_notify_payld {
62 	__le32 ctrl_id;
63 	__le32 flags;
64 };
65 
66 struct scmi_imx_misc_ctrl_get_out {
67 	__le32 num;
68 	__le32 val[];
69 };
70 
71 struct scmi_imx_misc_buildinfo_out {
72 	__le32 buildnum;
73 	__le32 buildcommit;
74 #define MISC_MAX_BUILDDATE	16
75 	u8 builddate[MISC_MAX_BUILDDATE];
76 #define MISC_MAX_BUILDTIME	16
77 	u8 buildtime[MISC_MAX_BUILDTIME];
78 };
79 
80 struct scmi_imx_misc_board_info_out {
81 	__le32 attributes;
82 #define MISC_MAX_BRDNAME	16
83 	u8 brdname[MISC_MAX_BRDNAME];
84 };
85 
86 struct scmi_imx_misc_cfg_info_out {
87 	__le32 msel;
88 #define MISC_MAX_CFGNAME	16
89 	u8 cfgname[MISC_MAX_CFGNAME];
90 };
91 
92 static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
93 					struct scmi_imx_misc_info *mi)
94 {
95 	int ret;
96 	struct scmi_xfer *t;
97 	struct scmi_msg_imx_misc_protocol_attributes *attr;
98 
99 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
100 				      sizeof(*attr), &t);
101 	if (ret)
102 		return ret;
103 
104 	attr = t->rx.buf;
105 
106 	ret = ph->xops->do_xfer(ph, t);
107 	if (!ret) {
108 		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
109 		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
110 		mi->nr_reason = GET_REASONS_NR(attr->attributes);
111 		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
112 			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
113 	}
114 
115 	ph->xops->xfer_put(ph, t);
116 
117 	return ret;
118 }
119 
120 static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
121 					  u32 ctrl_id)
122 {
123 	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
124 
125 	/*
126 	 * [0,      BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related
127 	 * [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related
128 	 */
129 	if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl)
130 		return -EINVAL;
131 	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
132 		return -EINVAL;
133 
134 	return 0;
135 }
136 
137 static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
138 				     u32 ctrl_id, u32 evt_id, u32 flags)
139 {
140 	struct scmi_imx_misc_ctrl_notify_in *in;
141 	struct scmi_xfer *t;
142 	int ret;
143 
144 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
145 	if (ret)
146 		return ret;
147 
148 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
149 				      sizeof(*in), 0, &t);
150 	if (ret)
151 		return ret;
152 
153 	in = t->tx.buf;
154 	in->ctrl_id = cpu_to_le32(ctrl_id);
155 	in->flags = cpu_to_le32(flags);
156 
157 	ret = ph->xops->do_xfer(ph, t);
158 
159 	ph->xops->xfer_put(ph, t);
160 
161 	return ret;
162 }
163 
164 static int
165 scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
166 				      u8 evt_id, u32 src_id, bool enable)
167 {
168 	int ret;
169 
170 	/* misc_ctrl_req_notify is for enablement */
171 	if (enable)
172 		return 0;
173 
174 	ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0);
175 	if (ret)
176 		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
177 			evt_id, src_id, ret);
178 
179 	return ret;
180 }
181 
182 static void *
183 scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph,
184 				      u8 evt_id, ktime_t timestamp,
185 				      const void *payld, size_t payld_sz,
186 				      void *report, u32 *src_id)
187 {
188 	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
189 	struct scmi_imx_misc_ctrl_notify_report *r = report;
190 
191 	if (sizeof(*p) != payld_sz)
192 		return NULL;
193 
194 	r->timestamp = timestamp;
195 	r->ctrl_id = le32_to_cpu(p->ctrl_id);
196 	r->flags = le32_to_cpu(p->flags);
197 	if (src_id)
198 		*src_id = r->ctrl_id;
199 	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
200 		r->ctrl_id, r->flags);
201 
202 	return r;
203 }
204 
205 static const struct scmi_event_ops scmi_imx_misc_event_ops = {
206 	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
207 	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
208 };
209 
210 static const struct scmi_event scmi_imx_misc_events[] = {
211 	{
212 		.id = SCMI_EVENT_IMX_MISC_CONTROL,
213 		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
214 		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
215 	},
216 };
217 
218 static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
219 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
220 	.ops = &scmi_imx_misc_event_ops,
221 	.evts = scmi_imx_misc_events,
222 	.num_events = ARRAY_SIZE(scmi_imx_misc_events),
223 	.num_sources = MAX_MISC_CTRL_SOURCES,
224 };
225 
226 static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
227 				  u32 ctrl_id, u32 *num, u32 *val)
228 {
229 	struct scmi_imx_misc_ctrl_get_out *out;
230 	struct scmi_xfer *t;
231 	int ret, i;
232 	int max_msg_size = ph->hops->get_max_msg_size(ph);
233 	int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32);
234 
235 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
236 	if (ret)
237 		return ret;
238 
239 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
240 				      0, &t);
241 	if (ret)
242 		return ret;
243 
244 	put_unaligned_le32(ctrl_id, t->tx.buf);
245 	ret = ph->xops->do_xfer(ph, t);
246 	if (!ret) {
247 		out = t->rx.buf;
248 		*num = le32_to_cpu(out->num);
249 
250 		if (*num >= max_num ||
251 		    *num * sizeof(__le32) > t->rx.len - sizeof(__le32)) {
252 			ph->xops->xfer_put(ph, t);
253 			return -EINVAL;
254 		}
255 
256 		for (i = 0; i < *num; i++)
257 			val[i] = le32_to_cpu(out->val[i]);
258 	}
259 
260 	ph->xops->xfer_put(ph, t);
261 
262 	return ret;
263 }
264 
265 static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
266 				  u32 ctrl_id, u32 num, u32 *val)
267 {
268 	struct scmi_imx_misc_ctrl_set_in *in;
269 	struct scmi_xfer *t;
270 	int ret, i;
271 	int max_msg_size = ph->hops->get_max_msg_size(ph);
272 	int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32);
273 
274 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
275 	if (ret)
276 		return ret;
277 
278 	if (num > max_num)
279 		return -EINVAL;
280 
281 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
282 				      sizeof(*in) + num * sizeof(__le32), 0, &t);
283 	if (ret)
284 		return ret;
285 
286 	in = t->tx.buf;
287 	in->id = cpu_to_le32(ctrl_id);
288 	in->num = cpu_to_le32(num);
289 	for (i = 0; i < num; i++)
290 		in->value[i] = cpu_to_le32(val[i]);
291 
292 	ret = ph->xops->do_xfer(ph, t);
293 
294 	ph->xops->xfer_put(ph, t);
295 
296 	return ret;
297 }
298 
299 static int scmi_imx_misc_build_info_discover(const struct scmi_protocol_handle *ph)
300 {
301 	char date[MISC_MAX_BUILDDATE], time[MISC_MAX_BUILDTIME];
302 	struct scmi_imx_misc_buildinfo_out *out;
303 	struct scmi_xfer *t;
304 	int ret;
305 
306 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_DISCOVER_BUILD_INFO, 0,
307 				      sizeof(*out), &t);
308 	if (ret)
309 		return ret;
310 
311 	ret = ph->xops->do_xfer(ph, t);
312 	if (!ret) {
313 		out = t->rx.buf;
314 		strscpy(date, out->builddate, MISC_MAX_BUILDDATE);
315 		strscpy(time, out->buildtime, MISC_MAX_BUILDTIME);
316 		dev_info(ph->dev, "SM Version\t= Build %u, Commit %08x %s %s\n",
317 			 le32_to_cpu(out->buildnum), le32_to_cpu(out->buildcommit),
318 			 date, time);
319 	}
320 
321 	ph->xops->xfer_put(ph, t);
322 
323 	return ret;
324 }
325 
326 static int scmi_imx_misc_board_info(const struct scmi_protocol_handle *ph)
327 {
328 	struct scmi_imx_misc_board_info_out *out;
329 	char name[MISC_MAX_BRDNAME];
330 	struct scmi_xfer *t;
331 	int ret;
332 
333 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_BOARD_INFO, 0, sizeof(*out), &t);
334 	if (ret)
335 		return ret;
336 
337 	ret = ph->xops->do_xfer(ph, t);
338 	if (!ret) {
339 		out = t->rx.buf;
340 		strscpy(name, out->brdname, MISC_MAX_BRDNAME);
341 		dev_info(ph->dev, "Board\t\t= %s, attr=0x%08x\n",
342 			 name, le32_to_cpu(out->attributes));
343 	}
344 
345 	ph->xops->xfer_put(ph, t);
346 
347 	return ret;
348 }
349 
350 static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph)
351 {
352 	struct scmi_imx_misc_cfg_info_out *out;
353 	char name[MISC_MAX_CFGNAME];
354 	struct scmi_xfer *t;
355 	int ret;
356 
357 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CFG_INFO_GET, 0, sizeof(*out), &t);
358 	if (ret)
359 		return ret;
360 
361 	ret = ph->xops->do_xfer(ph, t);
362 	if (!ret) {
363 		out = t->rx.buf;
364 		strscpy(name, out->cfgname, MISC_MAX_CFGNAME);
365 		dev_info(ph->dev, "SM Config\t= %s, mSel = %u\n",
366 			 name, le32_to_cpu(out->msel));
367 	}
368 
369 	ph->xops->xfer_put(ph, t);
370 
371 	return ret;
372 }
373 
374 static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
375 	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
376 	.misc_ctrl_get = scmi_imx_misc_ctrl_get,
377 	.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
378 };
379 
380 static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
381 {
382 	struct scmi_imx_misc_info *minfo;
383 	u32 version;
384 	int ret;
385 
386 	ret = ph->xops->version_get(ph, &version);
387 	if (ret)
388 		return ret;
389 
390 	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
391 		 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
392 
393 	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
394 	if (!minfo)
395 		return -ENOMEM;
396 
397 	ret = scmi_imx_misc_attributes_get(ph, minfo);
398 	if (ret)
399 		return ret;
400 
401 	ret = scmi_imx_misc_build_info_discover(ph);
402 	if (ret && ret != -EOPNOTSUPP)
403 		return ret;
404 
405 	ret = scmi_imx_misc_board_info(ph);
406 	if (ret && ret != -EOPNOTSUPP)
407 		return ret;
408 
409 	ret = scmi_imx_misc_cfg_info_get(ph);
410 	if (ret && ret != -EOPNOTSUPP)
411 		return ret;
412 
413 	return ph->set_priv(ph, minfo, version);
414 }
415 
416 static const struct scmi_protocol scmi_imx_misc = {
417 	.id = SCMI_PROTOCOL_IMX_MISC,
418 	.owner = THIS_MODULE,
419 	.instance_init = &scmi_imx_misc_protocol_init,
420 	.ops = &scmi_imx_misc_proto_ops,
421 	.events = &scmi_imx_misc_protocol_events,
422 	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
423 	.vendor_id = SCMI_IMX_VENDOR,
424 	.sub_vendor_id = SCMI_IMX_SUBVENDOR,
425 };
426 module_scmi_protocol(scmi_imx_misc);
427 
428 MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_MISC) "-" SCMI_IMX_VENDOR);
429 MODULE_DESCRIPTION("i.MX SCMI MISC driver");
430 MODULE_LICENSE("GPL");
431