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
29286 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_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
432