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
26498 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_CTRL_NOTIFY = 0x8,
29
};
30
31
struct scmi_imx_misc_info {
32
u32 version;
33
u32 nr_dev_ctrl;
34
u32 nr_brd_ctrl;
35
u32 nr_reason;
36
};
37
38
struct scmi_msg_imx_misc_protocol_attributes {
39
__le32 attributes;
40
};
41
42
#define GET_BRD_CTRLS_NR(x) le32_get_bits((x), GENMASK(31, 24))
43
#define GET_REASONS_NR(x) le32_get_bits((x), GENMASK(23, 16))
44
#define GET_DEV_CTRLS_NR(x) le32_get_bits((x), GENMASK(15, 0))
45
#define BRD_CTRL_START_ID BIT(15)
46
47
struct scmi_imx_misc_ctrl_set_in {
48
__le32 id;
49
__le32 num;
50
__le32 value[];
51
};
52
53
struct scmi_imx_misc_ctrl_notify_in {
54
__le32 ctrl_id;
55
__le32 flags;
56
};
57
58
struct scmi_imx_misc_ctrl_notify_payld {
59
__le32 ctrl_id;
60
__le32 flags;
61
};
62
63
struct scmi_imx_misc_ctrl_get_out {
64
__le32 num;
65
__le32 val[];
66
};
67
68
static int scmi_imx_misc_attributes_get(const struct scmi_protocol_handle *ph,
69
struct scmi_imx_misc_info *mi)
70
{
71
int ret;
72
struct scmi_xfer *t;
73
struct scmi_msg_imx_misc_protocol_attributes *attr;
74
75
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
76
sizeof(*attr), &t);
77
if (ret)
78
return ret;
79
80
attr = t->rx.buf;
81
82
ret = ph->xops->do_xfer(ph, t);
83
if (!ret) {
84
mi->nr_dev_ctrl = GET_DEV_CTRLS_NR(attr->attributes);
85
mi->nr_brd_ctrl = GET_BRD_CTRLS_NR(attr->attributes);
86
mi->nr_reason = GET_REASONS_NR(attr->attributes);
87
dev_info(ph->dev, "i.MX MISC NUM DEV CTRL: %d, NUM BRD CTRL: %d,NUM Reason: %d\n",
88
mi->nr_dev_ctrl, mi->nr_brd_ctrl, mi->nr_reason);
89
}
90
91
ph->xops->xfer_put(ph, t);
92
93
return ret;
94
}
95
96
static int scmi_imx_misc_ctrl_validate_id(const struct scmi_protocol_handle *ph,
97
u32 ctrl_id)
98
{
99
struct scmi_imx_misc_info *mi = ph->get_priv(ph);
100
101
/*
102
* [0, BRD_CTRL_START_ID) is for Dev Ctrl which is SOC related
103
* [BRD_CTRL_START_ID, 0xffff) is for Board Ctrl which is board related
104
*/
105
if (ctrl_id < BRD_CTRL_START_ID && ctrl_id > mi->nr_dev_ctrl)
106
return -EINVAL;
107
if (ctrl_id >= BRD_CTRL_START_ID + mi->nr_brd_ctrl)
108
return -EINVAL;
109
110
return 0;
111
}
112
113
static int scmi_imx_misc_ctrl_notify(const struct scmi_protocol_handle *ph,
114
u32 ctrl_id, u32 evt_id, u32 flags)
115
{
116
struct scmi_imx_misc_ctrl_notify_in *in;
117
struct scmi_xfer *t;
118
int ret;
119
120
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
121
if (ret)
122
return ret;
123
124
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_NOTIFY,
125
sizeof(*in), 0, &t);
126
if (ret)
127
return ret;
128
129
in = t->tx.buf;
130
in->ctrl_id = cpu_to_le32(ctrl_id);
131
in->flags = cpu_to_le32(flags);
132
133
ret = ph->xops->do_xfer(ph, t);
134
135
ph->xops->xfer_put(ph, t);
136
137
return ret;
138
}
139
140
static int
141
scmi_imx_misc_ctrl_set_notify_enabled(const struct scmi_protocol_handle *ph,
142
u8 evt_id, u32 src_id, bool enable)
143
{
144
int ret;
145
146
/* misc_ctrl_req_notify is for enablement */
147
if (enable)
148
return 0;
149
150
ret = scmi_imx_misc_ctrl_notify(ph, src_id, evt_id, 0);
151
if (ret)
152
dev_err(ph->dev, "FAIL_ENABLED - evt[%X] src[%d] - ret:%d\n",
153
evt_id, src_id, ret);
154
155
return ret;
156
}
157
158
static void *
159
scmi_imx_misc_ctrl_fill_custom_report(const struct scmi_protocol_handle *ph,
160
u8 evt_id, ktime_t timestamp,
161
const void *payld, size_t payld_sz,
162
void *report, u32 *src_id)
163
{
164
const struct scmi_imx_misc_ctrl_notify_payld *p = payld;
165
struct scmi_imx_misc_ctrl_notify_report *r = report;
166
167
if (sizeof(*p) != payld_sz)
168
return NULL;
169
170
r->timestamp = timestamp;
171
r->ctrl_id = le32_to_cpu(p->ctrl_id);
172
r->flags = le32_to_cpu(p->flags);
173
if (src_id)
174
*src_id = r->ctrl_id;
175
dev_dbg(ph->dev, "%s: ctrl_id: %d flags: %d\n", __func__,
176
r->ctrl_id, r->flags);
177
178
return r;
179
}
180
181
static const struct scmi_event_ops scmi_imx_misc_event_ops = {
182
.set_notify_enabled = scmi_imx_misc_ctrl_set_notify_enabled,
183
.fill_custom_report = scmi_imx_misc_ctrl_fill_custom_report,
184
};
185
186
static const struct scmi_event scmi_imx_misc_events[] = {
187
{
188
.id = SCMI_EVENT_IMX_MISC_CONTROL,
189
.max_payld_sz = sizeof(struct scmi_imx_misc_ctrl_notify_payld),
190
.max_report_sz = sizeof(struct scmi_imx_misc_ctrl_notify_report),
191
},
192
};
193
194
static struct scmi_protocol_events scmi_imx_misc_protocol_events = {
195
.queue_sz = SCMI_PROTO_QUEUE_SZ,
196
.ops = &scmi_imx_misc_event_ops,
197
.evts = scmi_imx_misc_events,
198
.num_events = ARRAY_SIZE(scmi_imx_misc_events),
199
.num_sources = MAX_MISC_CTRL_SOURCES,
200
};
201
202
static int scmi_imx_misc_ctrl_get(const struct scmi_protocol_handle *ph,
203
u32 ctrl_id, u32 *num, u32 *val)
204
{
205
struct scmi_imx_misc_ctrl_get_out *out;
206
struct scmi_xfer *t;
207
int ret, i;
208
int max_msg_size = ph->hops->get_max_msg_size(ph);
209
int max_num = (max_msg_size - sizeof(*out)) / sizeof(__le32);
210
211
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
212
if (ret)
213
return ret;
214
215
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_GET, sizeof(u32),
216
0, &t);
217
if (ret)
218
return ret;
219
220
put_unaligned_le32(ctrl_id, t->tx.buf);
221
ret = ph->xops->do_xfer(ph, t);
222
if (!ret) {
223
out = t->rx.buf;
224
*num = le32_to_cpu(out->num);
225
226
if (*num >= max_num ||
227
*num * sizeof(__le32) > t->rx.len - sizeof(__le32)) {
228
ph->xops->xfer_put(ph, t);
229
return -EINVAL;
230
}
231
232
for (i = 0; i < *num; i++)
233
val[i] = le32_to_cpu(out->val[i]);
234
}
235
236
ph->xops->xfer_put(ph, t);
237
238
return ret;
239
}
240
241
static int scmi_imx_misc_ctrl_set(const struct scmi_protocol_handle *ph,
242
u32 ctrl_id, u32 num, u32 *val)
243
{
244
struct scmi_imx_misc_ctrl_set_in *in;
245
struct scmi_xfer *t;
246
int ret, i;
247
int max_msg_size = ph->hops->get_max_msg_size(ph);
248
int max_num = (max_msg_size - sizeof(*in)) / sizeof(__le32);
249
250
ret = scmi_imx_misc_ctrl_validate_id(ph, ctrl_id);
251
if (ret)
252
return ret;
253
254
if (num > max_num)
255
return -EINVAL;
256
257
ret = ph->xops->xfer_get_init(ph, SCMI_IMX_MISC_CTRL_SET,
258
sizeof(*in) + num * sizeof(__le32), 0, &t);
259
if (ret)
260
return ret;
261
262
in = t->tx.buf;
263
in->id = cpu_to_le32(ctrl_id);
264
in->num = cpu_to_le32(num);
265
for (i = 0; i < num; i++)
266
in->value[i] = cpu_to_le32(val[i]);
267
268
ret = ph->xops->do_xfer(ph, t);
269
270
ph->xops->xfer_put(ph, t);
271
272
return ret;
273
}
274
275
static const struct scmi_imx_misc_proto_ops scmi_imx_misc_proto_ops = {
276
.misc_ctrl_set = scmi_imx_misc_ctrl_set,
277
.misc_ctrl_get = scmi_imx_misc_ctrl_get,
278
.misc_ctrl_req_notify = scmi_imx_misc_ctrl_notify,
279
};
280
281
static int scmi_imx_misc_protocol_init(const struct scmi_protocol_handle *ph)
282
{
283
struct scmi_imx_misc_info *minfo;
284
u32 version;
285
int ret;
286
287
ret = ph->xops->version_get(ph, &version);
288
if (ret)
289
return ret;
290
291
dev_info(ph->dev, "NXP SM MISC Version %d.%d\n",
292
PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
293
294
minfo = devm_kzalloc(ph->dev, sizeof(*minfo), GFP_KERNEL);
295
if (!minfo)
296
return -ENOMEM;
297
298
ret = scmi_imx_misc_attributes_get(ph, minfo);
299
if (ret)
300
return ret;
301
302
return ph->set_priv(ph, minfo, version);
303
}
304
305
static const struct scmi_protocol scmi_imx_misc = {
306
.id = SCMI_PROTOCOL_IMX_MISC,
307
.owner = THIS_MODULE,
308
.instance_init = &scmi_imx_misc_protocol_init,
309
.ops = &scmi_imx_misc_proto_ops,
310
.events = &scmi_imx_misc_protocol_events,
311
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
312
.vendor_id = SCMI_IMX_VENDOR,
313
.sub_vendor_id = SCMI_IMX_SUBVENDOR,
314
};
315
module_scmi_protocol(scmi_imx_misc);
316
317
MODULE_ALIAS("scmi-protocol-" __stringify(SCMI_PROTOCOL_IMX_MISC) "-" SCMI_IMX_VENDOR);
318
MODULE_DESCRIPTION("i.MX SCMI MISC driver");
319
MODULE_LICENSE("GPL");
320
321