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