Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/arm_scmi/reset.c
54337 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* System Control and Management Interface (SCMI) Reset Protocol
4
*
5
* Copyright (C) 2019-2022 ARM Ltd.
6
*/
7
8
#define pr_fmt(fmt) "SCMI Notifications RESET - " fmt
9
10
#include <linux/module.h>
11
#include <linux/scmi_protocol.h>
12
13
#include "protocols.h"
14
#include "notify.h"
15
16
/* Updated only after ALL the mandatory features for that version are merged */
17
#define SCMI_PROTOCOL_SUPPORTED_VERSION 0x30001
18
19
enum scmi_reset_protocol_cmd {
20
RESET_DOMAIN_ATTRIBUTES = 0x3,
21
RESET = 0x4,
22
RESET_NOTIFY = 0x5,
23
RESET_DOMAIN_NAME_GET = 0x6,
24
};
25
26
#define NUM_RESET_DOMAIN_MASK 0xffff
27
#define RESET_NOTIFY_ENABLE BIT(0)
28
29
struct scmi_msg_resp_reset_domain_attributes {
30
__le32 attributes;
31
#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
32
#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
33
#define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(29))
34
__le32 latency;
35
u8 name[SCMI_SHORT_NAME_MAX_SIZE];
36
};
37
38
struct scmi_msg_reset_domain_reset {
39
__le32 domain_id;
40
__le32 flags;
41
#define AUTONOMOUS_RESET BIT(0)
42
#define EXPLICIT_RESET_ASSERT BIT(1)
43
#define ASYNCHRONOUS_RESET BIT(2)
44
__le32 reset_state;
45
#define ARCH_COLD_RESET 0
46
};
47
48
struct scmi_msg_reset_notify {
49
__le32 id;
50
__le32 event_control;
51
#define RESET_TP_NOTIFY_ALL BIT(0)
52
};
53
54
struct scmi_reset_issued_notify_payld {
55
__le32 agent_id;
56
__le32 domain_id;
57
__le32 reset_state;
58
};
59
60
struct reset_dom_info {
61
bool async_reset;
62
bool reset_notify;
63
u32 latency_us;
64
char name[SCMI_MAX_STR_SIZE];
65
};
66
67
struct scmi_reset_info {
68
int num_domains;
69
bool notify_reset_cmd;
70
struct reset_dom_info *dom_info;
71
};
72
73
static int scmi_reset_attributes_get(const struct scmi_protocol_handle *ph,
74
struct scmi_reset_info *pi)
75
{
76
int ret;
77
struct scmi_xfer *t;
78
u32 attr;
79
80
ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES,
81
0, sizeof(attr), &t);
82
if (ret)
83
return ret;
84
85
ret = ph->xops->do_xfer(ph, t);
86
if (!ret) {
87
attr = get_unaligned_le32(t->rx.buf);
88
pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
89
}
90
91
ph->xops->xfer_put(ph, t);
92
93
if (!ret)
94
if (!ph->hops->protocol_msg_check(ph, RESET_NOTIFY, NULL))
95
pi->notify_reset_cmd = true;
96
97
return ret;
98
}
99
100
static struct reset_dom_info *
101
scmi_reset_domain_lookup(const struct scmi_protocol_handle *ph, u32 domain)
102
{
103
struct scmi_reset_info *pi = ph->get_priv(ph);
104
105
if (domain >= pi->num_domains)
106
return ERR_PTR(-EINVAL);
107
108
return pi->dom_info + domain;
109
}
110
111
static int
112
scmi_reset_domain_attributes_get(const struct scmi_protocol_handle *ph,
113
struct scmi_reset_info *pinfo, u32 domain)
114
{
115
int ret;
116
u32 attributes;
117
struct scmi_xfer *t;
118
struct scmi_msg_resp_reset_domain_attributes *attr;
119
struct reset_dom_info *dom_info = pinfo->dom_info + domain;
120
121
ret = ph->xops->xfer_get_init(ph, RESET_DOMAIN_ATTRIBUTES,
122
sizeof(domain), sizeof(*attr), &t);
123
if (ret)
124
return ret;
125
126
put_unaligned_le32(domain, t->tx.buf);
127
attr = t->rx.buf;
128
129
ret = ph->xops->do_xfer(ph, t);
130
if (!ret) {
131
attributes = le32_to_cpu(attr->attributes);
132
133
dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
134
if (pinfo->notify_reset_cmd)
135
dom_info->reset_notify =
136
SUPPORTS_NOTIFY_RESET(attributes);
137
dom_info->latency_us = le32_to_cpu(attr->latency);
138
if (dom_info->latency_us == U32_MAX)
139
dom_info->latency_us = 0;
140
strscpy(dom_info->name, attr->name, SCMI_SHORT_NAME_MAX_SIZE);
141
}
142
143
ph->xops->xfer_put(ph, t);
144
145
/*
146
* If supported overwrite short name with the extended one;
147
* on error just carry on and use already provided short name.
148
*/
149
if (!ret && PROTOCOL_REV_MAJOR(ph->version) >= 0x3 &&
150
SUPPORTS_EXTENDED_NAMES(attributes))
151
ph->hops->extended_name_get(ph, RESET_DOMAIN_NAME_GET, domain,
152
NULL, dom_info->name,
153
SCMI_MAX_STR_SIZE);
154
155
return ret;
156
}
157
158
static int scmi_reset_num_domains_get(const struct scmi_protocol_handle *ph)
159
{
160
struct scmi_reset_info *pi = ph->get_priv(ph);
161
162
return pi->num_domains;
163
}
164
165
static const char *
166
scmi_reset_name_get(const struct scmi_protocol_handle *ph, u32 domain)
167
{
168
struct reset_dom_info *dom_info;
169
170
dom_info = scmi_reset_domain_lookup(ph, domain);
171
if (IS_ERR(dom_info))
172
return "unknown";
173
174
return dom_info->name;
175
}
176
177
static int scmi_reset_latency_get(const struct scmi_protocol_handle *ph,
178
u32 domain)
179
{
180
struct reset_dom_info *dom_info;
181
182
dom_info = scmi_reset_domain_lookup(ph, domain);
183
if (IS_ERR(dom_info))
184
return PTR_ERR(dom_info);
185
186
return dom_info->latency_us;
187
}
188
189
static int scmi_domain_reset(const struct scmi_protocol_handle *ph, u32 domain,
190
u32 flags, u32 state)
191
{
192
int ret;
193
struct scmi_xfer *t;
194
struct scmi_msg_reset_domain_reset *dom;
195
struct reset_dom_info *dom_info;
196
197
dom_info = scmi_reset_domain_lookup(ph, domain);
198
if (IS_ERR(dom_info))
199
return PTR_ERR(dom_info);
200
201
if (dom_info->async_reset && flags & AUTONOMOUS_RESET)
202
flags |= ASYNCHRONOUS_RESET;
203
204
ret = ph->xops->xfer_get_init(ph, RESET, sizeof(*dom), 0, &t);
205
if (ret)
206
return ret;
207
208
dom = t->tx.buf;
209
dom->domain_id = cpu_to_le32(domain);
210
dom->flags = cpu_to_le32(flags);
211
dom->reset_state = cpu_to_le32(state);
212
213
if (flags & ASYNCHRONOUS_RESET)
214
ret = ph->xops->do_xfer_with_response(ph, t);
215
else
216
ret = ph->xops->do_xfer(ph, t);
217
218
ph->xops->xfer_put(ph, t);
219
return ret;
220
}
221
222
static int scmi_reset_domain_reset(const struct scmi_protocol_handle *ph,
223
u32 domain)
224
{
225
return scmi_domain_reset(ph, domain, AUTONOMOUS_RESET,
226
ARCH_COLD_RESET);
227
}
228
229
static int
230
scmi_reset_domain_assert(const struct scmi_protocol_handle *ph, u32 domain)
231
{
232
return scmi_domain_reset(ph, domain, EXPLICIT_RESET_ASSERT,
233
ARCH_COLD_RESET);
234
}
235
236
static int
237
scmi_reset_domain_deassert(const struct scmi_protocol_handle *ph, u32 domain)
238
{
239
return scmi_domain_reset(ph, domain, 0, ARCH_COLD_RESET);
240
}
241
242
static const struct scmi_reset_proto_ops reset_proto_ops = {
243
.num_domains_get = scmi_reset_num_domains_get,
244
.name_get = scmi_reset_name_get,
245
.latency_get = scmi_reset_latency_get,
246
.reset = scmi_reset_domain_reset,
247
.assert = scmi_reset_domain_assert,
248
.deassert = scmi_reset_domain_deassert,
249
};
250
251
static bool scmi_reset_notify_supported(const struct scmi_protocol_handle *ph,
252
u8 evt_id, u32 src_id)
253
{
254
struct reset_dom_info *dom_info;
255
256
if (evt_id != SCMI_EVENT_RESET_ISSUED)
257
return false;
258
259
dom_info = scmi_reset_domain_lookup(ph, src_id);
260
if (IS_ERR(dom_info))
261
return false;
262
263
return dom_info->reset_notify;
264
}
265
266
static int scmi_reset_notify(const struct scmi_protocol_handle *ph,
267
u32 domain_id, bool enable)
268
{
269
int ret;
270
u32 evt_cntl = enable ? RESET_TP_NOTIFY_ALL : 0;
271
struct scmi_xfer *t;
272
struct scmi_msg_reset_notify *cfg;
273
274
ret = ph->xops->xfer_get_init(ph, RESET_NOTIFY, sizeof(*cfg), 0, &t);
275
if (ret)
276
return ret;
277
278
cfg = t->tx.buf;
279
cfg->id = cpu_to_le32(domain_id);
280
cfg->event_control = cpu_to_le32(evt_cntl);
281
282
ret = ph->xops->do_xfer(ph, t);
283
284
ph->xops->xfer_put(ph, t);
285
return ret;
286
}
287
288
static int scmi_reset_set_notify_enabled(const struct scmi_protocol_handle *ph,
289
u8 evt_id, u32 src_id, bool enable)
290
{
291
int ret;
292
293
ret = scmi_reset_notify(ph, src_id, enable);
294
if (ret)
295
pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
296
evt_id, src_id, ret);
297
298
return ret;
299
}
300
301
static void *
302
scmi_reset_fill_custom_report(const struct scmi_protocol_handle *ph,
303
u8 evt_id, ktime_t timestamp,
304
const void *payld, size_t payld_sz,
305
void *report, u32 *src_id)
306
{
307
const struct scmi_reset_issued_notify_payld *p = payld;
308
struct scmi_reset_issued_report *r = report;
309
310
if (evt_id != SCMI_EVENT_RESET_ISSUED || sizeof(*p) != payld_sz)
311
return NULL;
312
313
r->timestamp = timestamp;
314
r->agent_id = le32_to_cpu(p->agent_id);
315
r->domain_id = le32_to_cpu(p->domain_id);
316
r->reset_state = le32_to_cpu(p->reset_state);
317
*src_id = r->domain_id;
318
319
return r;
320
}
321
322
static int scmi_reset_get_num_sources(const struct scmi_protocol_handle *ph)
323
{
324
struct scmi_reset_info *pinfo = ph->get_priv(ph);
325
326
if (!pinfo)
327
return -EINVAL;
328
329
return pinfo->num_domains;
330
}
331
332
static const struct scmi_event reset_events[] = {
333
{
334
.id = SCMI_EVENT_RESET_ISSUED,
335
.max_payld_sz = sizeof(struct scmi_reset_issued_notify_payld),
336
.max_report_sz = sizeof(struct scmi_reset_issued_report),
337
},
338
};
339
340
static const struct scmi_event_ops reset_event_ops = {
341
.is_notify_supported = scmi_reset_notify_supported,
342
.get_num_sources = scmi_reset_get_num_sources,
343
.set_notify_enabled = scmi_reset_set_notify_enabled,
344
.fill_custom_report = scmi_reset_fill_custom_report,
345
};
346
347
static const struct scmi_protocol_events reset_protocol_events = {
348
.queue_sz = SCMI_PROTO_QUEUE_SZ,
349
.ops = &reset_event_ops,
350
.evts = reset_events,
351
.num_events = ARRAY_SIZE(reset_events),
352
};
353
354
static int scmi_reset_protocol_init(const struct scmi_protocol_handle *ph)
355
{
356
int domain, ret;
357
struct scmi_reset_info *pinfo;
358
359
dev_dbg(ph->dev, "Reset Version %d.%d\n",
360
PROTOCOL_REV_MAJOR(ph->version), PROTOCOL_REV_MINOR(ph->version));
361
362
pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
363
if (!pinfo)
364
return -ENOMEM;
365
366
ret = scmi_reset_attributes_get(ph, pinfo);
367
if (ret)
368
return ret;
369
370
pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
371
sizeof(*pinfo->dom_info), GFP_KERNEL);
372
if (!pinfo->dom_info)
373
return -ENOMEM;
374
375
for (domain = 0; domain < pinfo->num_domains; domain++)
376
scmi_reset_domain_attributes_get(ph, pinfo, domain);
377
378
return ph->set_priv(ph, pinfo);
379
}
380
381
static const struct scmi_protocol scmi_reset = {
382
.id = SCMI_PROTOCOL_RESET,
383
.owner = THIS_MODULE,
384
.instance_init = &scmi_reset_protocol_init,
385
.ops = &reset_proto_ops,
386
.events = &reset_protocol_events,
387
.supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION,
388
};
389
390
DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(reset, scmi_reset)
391
392