xref: /linux/drivers/firmware/arm_scmi/vendors/imx/imx-sm-misc.c (revision bdbddf72a2ab1cfea699959795d70df3931eefe7)
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_SYSLOG_GET = 0xD,
32 	SCMI_IMX_MISC_BOARD_INFO = 0xE,
33 };
34 
35 struct scmi_imx_misc_info {
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 struct scmi_imx_misc_syslog_in {
93 	__le32 flags;
94 	__le32 index;
95 };
96 
97 #define REMAINING(x)	le32_get_bits((x), GENMASK(31, 20))
98 #define RETURNED(x)	le32_get_bits((x), GENMASK(11, 0))
99 
100 struct scmi_imx_misc_syslog_out {
101 	__le32 numlogflags;
102 	__le32 syslog[];
103 };
104 
scmi_imx_misc_attributes_get(const struct scmi_protocol_handle * ph,struct scmi_imx_misc_info * mi)105 static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
106 					struct scmi_imx_misc_info *mi)
107 {
108 	int ret;
109 	struct scmi_xfer *t;
110 	struct scmi_msg_imx_misc_protocol_attributes *attr;
111 
112 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
113 				      sizeof(*attr), &t);
114 	if (ret)
115 		return ret;
116 
117 	attr = t->rx.buf;
118 
119 	ret = ph->xops->do_xfer(ph, t);
120 	if (!ret) {
121 		mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
122 		mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
123 		mi->nr_reason = GET_REASONS_NR(attr->attributes);
124 		dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
125 			 mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
126 	}
127 
128 	ph->xops->xfer_put(ph, t);
129 
130 	return ret;
131 }
132 
scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle * ph,u32 ctrl_id)133 static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
134 					  u32 ctrl_id)
135 {
136 	struct scmi_imx_misc_info *mi = ph->get_priv(ph);
137 
138 	/*
139 	 * [0,      BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related
140 	 * [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related
141 	 */
142 	if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl)
143 		return -EINVAL;
144 	if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
145 		return -EINVAL;
146 
147 	return 0;
148 }
149 
scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle * ph,u32 ctrl_id,u32 evt_id,u32 flags)150 static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
151 				     u32 ctrl_id, u32 evt_id, u32 flags)
152 {
153 	struct scmi_imx_misc_ctrl_notify_in *in;
154 	struct scmi_xfer *t;
155 	int ret;
156 
157 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
158 	if (ret)
159 		return ret;
160 
161 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
162 				      sizeof(*in), 0, &t);
163 	if (ret)
164 		return ret;
165 
166 	in = t->tx.buf;
167 	in->ctrl_id = cpu_to_le32(ctrl_id);
168 	in->flags = cpu_to_le32(flags);
169 
170 	ret = ph->xops->do_xfer(ph, t);
171 
172 	ph->xops->xfer_put(ph, t);
173 
174 	return ret;
175 }
176 
177 static int
scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle * ph,u8 evt_id,u32 src_id,bool enable)178 scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
179 				      u8 evt_id, u32 src_id, bool enable)
180 {
181 	int ret;
182 
183 	/* misc_ctrl_req_notify is for enablement */
184 	if (enable)
185 		return 0;
186 
187 	ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0);
188 	if (ret)
189 		dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
190 			evt_id, src_id, ret);
191 
192 	return ret;
193 }
194 
195 static void *
scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle * ph,u8 evt_id,ktime_t timestamp,const void * payld,size_t payld_sz,void * report,u32 * src_id)196 scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph,
197 				      u8 evt_id, ktime_t timestamp,
198 				      const void *payld, size_t payld_sz,
199 				      void *report, u32 *src_id)
200 {
201 	const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
202 	struct scmi_imx_misc_ctrl_notify_report *r = report;
203 
204 	if (sizeof(*p) != payld_sz)
205 		return NULL;
206 
207 	r->timestamp = timestamp;
208 	r->ctrl_id = le32_to_cpu(p->ctrl_id);
209 	r->flags = le32_to_cpu(p->flags);
210 	if (src_id)
211 		*src_id = r->ctrl_id;
212 	dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
213 		r->ctrl_id, r->flags);
214 
215 	return r;
216 }
217 
218 static const struct scmi_event_ops scmi_imx_misc_event_ops = {
219 	.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
220 	.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
221 };
222 
223 static const struct scmi_event scmi_imx_misc_events[] = {
224 	{
225 		.id = SCMI_EVENT_IMX_MISC_CONTROL,
226 		.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
227 		.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
228 	},
229 };
230 
231 static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
232 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
233 	.ops = &scmi_imx_misc_event_ops,
234 	.evts = scmi_imx_misc_events,
235 	.num_events = ARRAY_SIZE(scmi_imx_misc_events),
236 	.num_sources = MAX_MISC_CTRL_SOURCES,
237 };
238 
scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle * ph,u32 ctrl_id,u32 * num,u32 * val)239 static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
240 				  u32 ctrl_id, u32 *num, u32 *val)
241 {
242 	struct scmi_imx_misc_ctrl_get_out *out;
243 	struct scmi_xfer *t;
244 	int ret, i;
245 	int max_msg_size = ph->hops->get_max_msg_size(ph);
246 	int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32);
247 
248 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
249 	if (ret)
250 		return ret;
251 
252 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
253 				      0, &t);
254 	if (ret)
255 		return ret;
256 
257 	put_unaligned_le32(ctrl_id, t->tx.buf);
258 	ret = ph->xops->do_xfer(ph, t);
259 	if (!ret) {
260 		out = t->rx.buf;
261 		*num = le32_to_cpu(out->num);
262 
263 		if (*num >= max_num ||
264 		    *num * sizeof(__le32) > t->rx.len - sizeof(__le32)) {
265 			ph->xops->xfer_put(ph, t);
266 			return -EINVAL;
267 		}
268 
269 		for (i = 0; i < *num; i++)
270 			val[i] = le32_to_cpu(out->val[i]);
271 	}
272 
273 	ph->xops->xfer_put(ph, t);
274 
275 	return ret;
276 }
277 
scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle * ph,u32 ctrl_id,u32 num,u32 * val)278 static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
279 				  u32 ctrl_id, u32 num, u32 *val)
280 {
281 	struct scmi_imx_misc_ctrl_set_in *in;
282 	struct scmi_xfer *t;
283 	int ret, i;
284 	int max_msg_size = ph->hops->get_max_msg_size(ph);
285 	int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32);
286 
287 	ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
288 	if (ret)
289 		return ret;
290 
291 	if (num > max_num)
292 		return -EINVAL;
293 
294 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
295 				      sizeof(*in) + num * sizeof(__le32), 0, &t);
296 	if (ret)
297 		return ret;
298 
299 	in = t->tx.buf;
300 	in->id = cpu_to_le32(ctrl_id);
301 	in->num = cpu_to_le32(num);
302 	for (i = 0; i < num; i++)
303 		in->value[i] = cpu_to_le32(val[i]);
304 
305 	ret = ph->xops->do_xfer(ph, t);
306 
307 	ph->xops->xfer_put(ph, t);
308 
309 	return ret;
310 }
311 
scmi_imx_misc_build_info_discover(const struct scmi_protocol_handle * ph)312 static int scmi_imx_misc_build_info_discover(const struct scmi_protocol_handle *ph)
313 {
314 	char date[MISC_MAX_BUILDDATE], time[MISC_MAX_BUILDTIME];
315 	struct scmi_imx_misc_buildinfo_out *out;
316 	struct scmi_xfer *t;
317 	int ret;
318 
319 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_DISCOVER_BUILD_INFO, 0,
320 				      sizeof(*out), &t);
321 	if (ret)
322 		return ret;
323 
324 	ret = ph->xops->do_xfer(ph, t);
325 	if (!ret) {
326 		out = t->rx.buf;
327 		strscpy(date, out->builddate, MISC_MAX_BUILDDATE);
328 		strscpy(time, out->buildtime, MISC_MAX_BUILDTIME);
329 		dev_info(ph->dev, "SM Version\t= Build %u, Commit %08x %s %s\n",
330 			 le32_to_cpu(out->buildnum), le32_to_cpu(out->buildcommit),
331 			 date, time);
332 	}
333 
334 	ph->xops->xfer_put(ph, t);
335 
336 	return ret;
337 }
338 
scmi_imx_misc_board_info(const struct scmi_protocol_handle * ph)339 static int scmi_imx_misc_board_info(const struct scmi_protocol_handle *ph)
340 {
341 	struct scmi_imx_misc_board_info_out *out;
342 	char name[MISC_MAX_BRDNAME];
343 	struct scmi_xfer *t;
344 	int ret;
345 
346 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_BOARD_INFO, 0, sizeof(*out), &t);
347 	if (ret)
348 		return ret;
349 
350 	ret = ph->xops->do_xfer(ph, t);
351 	if (!ret) {
352 		out = t->rx.buf;
353 		strscpy(name, out->brdname, MISC_MAX_BRDNAME);
354 		dev_info(ph->dev, "Board\t\t= %s, attr=0x%08x\n",
355 			 name, le32_to_cpu(out->attributes));
356 	}
357 
358 	ph->xops->xfer_put(ph, t);
359 
360 	return ret;
361 }
362 
scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle * ph)363 static int scmi_imx_misc_cfg_info_get(const struct scmi_protocol_handle *ph)
364 {
365 	struct scmi_imx_misc_cfg_info_out *out;
366 	char name[MISC_MAX_CFGNAME];
367 	struct scmi_xfer *t;
368 	int ret;
369 
370 	ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CFG_INFO_GET, 0, sizeof(*out), &t);
371 	if (ret)
372 		return ret;
373 
374 	ret = ph->xops->do_xfer(ph, t);
375 	if (!ret) {
376 		out = t->rx.buf;
377 		strscpy(name, out->cfgname, MISC_MAX_CFGNAME);
378 		dev_info(ph->dev, "SM Config\t= %s, mSel = %u\n",
379 			 name, le32_to_cpu(out->msel));
380 	}
381 
382 	ph->xops->xfer_put(ph, t);
383 
384 	return ret;
385 }
386 
387 struct scmi_imx_misc_syslog_ipriv {
388 	u32 *array;
389 	u16 *size;
390 };
391 
iter_misc_syslog_prepare_message(void * message,u32 desc_index,const void * priv)392 static void iter_misc_syslog_prepare_message(void *message, u32 desc_index,
393 					     const void *priv)
394 {
395 	struct scmi_imx_misc_syslog_in *msg = message;
396 
397 	msg->flags = cpu_to_le32(0);
398 	msg->index = cpu_to_le32(desc_index);
399 }
400 
iter_misc_syslog_update_state(struct scmi_iterator_state * st,const void * response,void * priv)401 static int iter_misc_syslog_update_state(struct scmi_iterator_state *st,
402 					 const void *response, void *priv)
403 {
404 	const struct scmi_imx_misc_syslog_out *r = response;
405 	struct scmi_imx_misc_syslog_ipriv *p = priv;
406 
407 	st->num_returned = RETURNED(r->numlogflags);
408 	st->num_remaining = REMAINING(r->numlogflags);
409 	*p->size = st->num_returned + st->num_remaining;
410 
411 	return 0;
412 }
413 
414 static int
iter_misc_syslog_process_response(const struct scmi_protocol_handle * ph,const void * response,struct scmi_iterator_state * st,void * priv)415 iter_misc_syslog_process_response(const struct scmi_protocol_handle *ph,
416 				  const void *response,
417 				  struct scmi_iterator_state *st, void *priv)
418 {
419 	const struct scmi_imx_misc_syslog_out *r = response;
420 	struct scmi_imx_misc_syslog_ipriv *p = priv;
421 
422 	p->array[st->desc_index + st->loop_idx] =
423 		le32_to_cpu(r->syslog[st->loop_idx]);
424 
425 	return 0;
426 }
427 
scmi_imx_misc_syslog_get(const struct scmi_protocol_handle * ph,u16 * size,void * array)428 static int scmi_imx_misc_syslog_get(const struct scmi_protocol_handle *ph, u16 *size,
429 				    void *array)
430 {
431 	struct scmi_iterator_ops ops = {
432 		.prepare_message = iter_misc_syslog_prepare_message,
433 		.update_state = iter_misc_syslog_update_state,
434 		.process_response = iter_misc_syslog_process_response,
435 	};
436 	struct scmi_imx_misc_syslog_ipriv ipriv = {
437 		.array = array,
438 		.size = size,
439 	};
440 	void *iter;
441 
442 	if (!array || !size || !*size)
443 		return -EINVAL;
444 
445 	iter = ph->hops->iter_response_init(ph, &ops, *size, SCMI_IMX_MISC_SYSLOG_GET,
446 					    sizeof(struct scmi_imx_misc_syslog_in),
447 					    &ipriv);
448 	if (IS_ERR(iter))
449 		return PTR_ERR(iter);
450 
451 	/* If firmware return NOT SUPPORTED, propagate value to caller */
452 	return ph->hops->iter_response_run(iter);
453 }
454 
455 static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
456 	.misc_ctrl_set = scmi_imx_misc_ctrl_set,
457 	.misc_ctrl_get = scmi_imx_misc_ctrl_get,
458 	.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
459 	.misc_syslog = scmi_imx_misc_syslog_get,
460 };
461 
scmi_imx_misc_protocol_init(const struct scmi_protocol_handle * ph)462 static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
463 {
464 	struct scmi_imx_misc_info *minfo;
465 	int ret;
466 
467 	dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
468 		 PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
469 
470 	minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
471 	if (!minfo)
472 		return -ENOMEM;
473 
474 	ret = scmi_imx_misc_attributes_get(ph, minfo);
475 	if (ret)
476 		return ret;
477 
478 	ret = scmi_imx_misc_build_info_discover(ph);
479 	if (ret && ret != -EOPNOTSUPP)
480 		return ret;
481 
482 	ret = scmi_imx_misc_board_info(ph);
483 	if (ret && ret != -EOPNOTSUPP)
484 		return ret;
485 
486 	ret = scmi_imx_misc_cfg_info_get(ph);
487 	if (ret && ret != -EOPNOTSUPP)
488 		return ret;
489 
490 	return ph->set_priv(ph, minfo);
491 }
492 
493 static const struct scmi_protocol scmi_imx_misc = {
494 	.id = SCMI_PROTOCOL_IMX_MISC,
495 	.owner = THIS_MODULE,
496 	.instance_init = &scmi_imx_misc_protocol_init,
497 	.ops = &scmi_imx_misc_proto_ops,
498 	.events = &scmi_imx_misc_protocol_events,
499 	.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
500 	.vendor_id = SCMI_IMX_VENDOR,
501 	.sub_vendor_id = SCMI_IMX_SUBVENDOR,
502 };
503 module_scmi_protocol(scmi_imx_misc);
504 
505 MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_MISC) "-" SCMI_IMX_VENDOR);
506 MODULE_DESCRIPTION("i.MX SCMI MISC driver");
507 MODULE_LICENSE("GPL");
508