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