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-misc.c
54308 views
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
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
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
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
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 *
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
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
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
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
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
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
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
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
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
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
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
509