blob: 6d223f345b6c97ee7b36bf9b8b43c41cc58ebefb [file] [log] [blame]
David Brazdil0f672f62019-12-10 10:32:29 +00001// SPDX-License-Identifier: GPL-2.0
2/*
3 * System Control and Management Interface (SCMI) Reset Protocol
4 *
5 * Copyright (C) 2019 ARM Ltd.
6 */
7
8#include "common.h"
9
10enum scmi_reset_protocol_cmd {
11 RESET_DOMAIN_ATTRIBUTES = 0x3,
12 RESET = 0x4,
13 RESET_NOTIFY = 0x5,
14};
15
16enum scmi_reset_protocol_notify {
17 RESET_ISSUED = 0x0,
18};
19
20#define NUM_RESET_DOMAIN_MASK 0xffff
21#define RESET_NOTIFY_ENABLE BIT(0)
22
23struct scmi_msg_resp_reset_domain_attributes {
24 __le32 attributes;
25#define SUPPORTS_ASYNC_RESET(x) ((x) & BIT(31))
26#define SUPPORTS_NOTIFY_RESET(x) ((x) & BIT(30))
27 __le32 latency;
28 u8 name[SCMI_MAX_STR_SIZE];
29};
30
31struct scmi_msg_reset_domain_reset {
32 __le32 domain_id;
33 __le32 flags;
34#define AUTONOMOUS_RESET BIT(0)
35#define EXPLICIT_RESET_ASSERT BIT(1)
36#define ASYNCHRONOUS_RESET BIT(2)
37 __le32 reset_state;
Olivier Deprez0e641232021-09-23 10:07:05 +020038#define ARCH_COLD_RESET 0
David Brazdil0f672f62019-12-10 10:32:29 +000039};
40
41struct reset_dom_info {
42 bool async_reset;
43 bool reset_notify;
44 u32 latency_us;
45 char name[SCMI_MAX_STR_SIZE];
46};
47
48struct scmi_reset_info {
49 int num_domains;
50 struct reset_dom_info *dom_info;
51};
52
53static int scmi_reset_attributes_get(const struct scmi_handle *handle,
54 struct scmi_reset_info *pi)
55{
56 int ret;
57 struct scmi_xfer *t;
58 u32 attr;
59
60 ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
61 SCMI_PROTOCOL_RESET, 0, sizeof(attr), &t);
62 if (ret)
63 return ret;
64
65 ret = scmi_do_xfer(handle, t);
66 if (!ret) {
67 attr = get_unaligned_le32(t->rx.buf);
68 pi->num_domains = attr & NUM_RESET_DOMAIN_MASK;
69 }
70
71 scmi_xfer_put(handle, t);
72 return ret;
73}
74
75static int
76scmi_reset_domain_attributes_get(const struct scmi_handle *handle, u32 domain,
77 struct reset_dom_info *dom_info)
78{
79 int ret;
80 struct scmi_xfer *t;
81 struct scmi_msg_resp_reset_domain_attributes *attr;
82
83 ret = scmi_xfer_get_init(handle, RESET_DOMAIN_ATTRIBUTES,
84 SCMI_PROTOCOL_RESET, sizeof(domain),
85 sizeof(*attr), &t);
86 if (ret)
87 return ret;
88
89 put_unaligned_le32(domain, t->tx.buf);
90 attr = t->rx.buf;
91
92 ret = scmi_do_xfer(handle, t);
93 if (!ret) {
94 u32 attributes = le32_to_cpu(attr->attributes);
95
96 dom_info->async_reset = SUPPORTS_ASYNC_RESET(attributes);
97 dom_info->reset_notify = SUPPORTS_NOTIFY_RESET(attributes);
98 dom_info->latency_us = le32_to_cpu(attr->latency);
99 if (dom_info->latency_us == U32_MAX)
100 dom_info->latency_us = 0;
101 strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
102 }
103
104 scmi_xfer_put(handle, t);
105 return ret;
106}
107
108static int scmi_reset_num_domains_get(const struct scmi_handle *handle)
109{
110 struct scmi_reset_info *pi = handle->reset_priv;
111
112 return pi->num_domains;
113}
114
115static char *scmi_reset_name_get(const struct scmi_handle *handle, u32 domain)
116{
117 struct scmi_reset_info *pi = handle->reset_priv;
118 struct reset_dom_info *dom = pi->dom_info + domain;
119
120 return dom->name;
121}
122
123static int scmi_reset_latency_get(const struct scmi_handle *handle, u32 domain)
124{
125 struct scmi_reset_info *pi = handle->reset_priv;
126 struct reset_dom_info *dom = pi->dom_info + domain;
127
128 return dom->latency_us;
129}
130
131static int scmi_domain_reset(const struct scmi_handle *handle, u32 domain,
132 u32 flags, u32 state)
133{
134 int ret;
135 struct scmi_xfer *t;
136 struct scmi_msg_reset_domain_reset *dom;
137 struct scmi_reset_info *pi = handle->reset_priv;
138 struct reset_dom_info *rdom = pi->dom_info + domain;
139
140 if (rdom->async_reset)
141 flags |= ASYNCHRONOUS_RESET;
142
143 ret = scmi_xfer_get_init(handle, RESET, SCMI_PROTOCOL_RESET,
144 sizeof(*dom), 0, &t);
145 if (ret)
146 return ret;
147
148 dom = t->tx.buf;
149 dom->domain_id = cpu_to_le32(domain);
150 dom->flags = cpu_to_le32(flags);
151 dom->reset_state = cpu_to_le32(state);
152
153 if (rdom->async_reset)
154 ret = scmi_do_xfer_with_response(handle, t);
155 else
156 ret = scmi_do_xfer(handle, t);
157
158 scmi_xfer_put(handle, t);
159 return ret;
160}
161
162static int scmi_reset_domain_reset(const struct scmi_handle *handle, u32 domain)
163{
164 return scmi_domain_reset(handle, domain, AUTONOMOUS_RESET,
165 ARCH_COLD_RESET);
166}
167
168static int
169scmi_reset_domain_assert(const struct scmi_handle *handle, u32 domain)
170{
171 return scmi_domain_reset(handle, domain, EXPLICIT_RESET_ASSERT,
172 ARCH_COLD_RESET);
173}
174
175static int
176scmi_reset_domain_deassert(const struct scmi_handle *handle, u32 domain)
177{
178 return scmi_domain_reset(handle, domain, 0, ARCH_COLD_RESET);
179}
180
181static struct scmi_reset_ops reset_ops = {
182 .num_domains_get = scmi_reset_num_domains_get,
183 .name_get = scmi_reset_name_get,
184 .latency_get = scmi_reset_latency_get,
185 .reset = scmi_reset_domain_reset,
186 .assert = scmi_reset_domain_assert,
187 .deassert = scmi_reset_domain_deassert,
188};
189
190static int scmi_reset_protocol_init(struct scmi_handle *handle)
191{
192 int domain;
193 u32 version;
194 struct scmi_reset_info *pinfo;
195
196 scmi_version_get(handle, SCMI_PROTOCOL_RESET, &version);
197
198 dev_dbg(handle->dev, "Reset Version %d.%d\n",
199 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
200
201 pinfo = devm_kzalloc(handle->dev, sizeof(*pinfo), GFP_KERNEL);
202 if (!pinfo)
203 return -ENOMEM;
204
205 scmi_reset_attributes_get(handle, pinfo);
206
207 pinfo->dom_info = devm_kcalloc(handle->dev, pinfo->num_domains,
208 sizeof(*pinfo->dom_info), GFP_KERNEL);
209 if (!pinfo->dom_info)
210 return -ENOMEM;
211
212 for (domain = 0; domain < pinfo->num_domains; domain++) {
213 struct reset_dom_info *dom = pinfo->dom_info + domain;
214
215 scmi_reset_domain_attributes_get(handle, domain, dom);
216 }
217
218 handle->reset_ops = &reset_ops;
219 handle->reset_priv = pinfo;
220
221 return 0;
222}
223
224static int __init scmi_reset_init(void)
225{
226 return scmi_protocol_register(SCMI_PROTOCOL_RESET,
227 &scmi_reset_protocol_init);
228}
229subsys_initcall(scmi_reset_init);