1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * System Control and Management Interface (SCMI) Power Protocol
4  *
5  * Copyright (C) 2018-2022 ARM Ltd.
6  */
7 
8 #define pr_fmt(fmt) "SCMI Notifications POWER - " fmt
9 
10 #include <linux/module.h>
11 #include <linux/scmi_protocol.h>
12 
13 #include "protocols.h"
14 #include "notify.h"
15 
16 enum scmi_power_protocol_cmd {
17 	POWER_DOMAIN_ATTRIBUTES = 0x3,
18 	POWER_STATE_SET = 0x4,
19 	POWER_STATE_GET = 0x5,
20 	POWER_STATE_NOTIFY = 0x6,
21 	POWER_DOMAIN_NAME_GET = 0x8,
22 };
23 
24 struct scmi_msg_resp_power_attributes {
25 	__le16 num_domains;
26 	__le16 reserved;
27 	__le32 stats_addr_low;
28 	__le32 stats_addr_high;
29 	__le32 stats_size;
30 };
31 
32 struct scmi_msg_resp_power_domain_attributes {
33 	__le32 flags;
34 #define SUPPORTS_STATE_SET_NOTIFY(x)	((x) & BIT(31))
35 #define SUPPORTS_STATE_SET_ASYNC(x)	((x) & BIT(30))
36 #define SUPPORTS_STATE_SET_SYNC(x)	((x) & BIT(29))
37 #define SUPPORTS_EXTENDED_NAMES(x)	((x) & BIT(27))
38 	    u8 name[SCMI_SHORT_NAME_MAX_SIZE];
39 };
40 
41 struct scmi_power_set_state {
42 	__le32 flags;
43 #define STATE_SET_ASYNC		BIT(0)
44 	__le32 domain;
45 	__le32 state;
46 };
47 
48 struct scmi_power_state_notify {
49 	__le32 domain;
50 	__le32 notify_enable;
51 };
52 
53 struct scmi_power_state_notify_payld {
54 	__le32 agent_id;
55 	__le32 domain_id;
56 	__le32 power_state;
57 };
58 
59 struct power_dom_info {
60 	bool state_set_sync;
61 	bool state_set_async;
62 	bool state_set_notify;
63 	char name[SCMI_MAX_STR_SIZE];
64 };
65 
66 struct scmi_power_info {
67 	u32 version;
68 	int num_domains;
69 	u64 stats_addr;
70 	u32 stats_size;
71 	struct power_dom_info *dom_info;
72 };
73 
74 static int scmi_power_attributes_get(const struct scmi_protocol_handle *ph,
75 				     struct scmi_power_info *pi)
76 {
77 	int ret;
78 	struct scmi_xfer *t;
79 	struct scmi_msg_resp_power_attributes *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 	attr = t->rx.buf;
87 
88 	ret = ph->xops->do_xfer(ph, t);
89 	if (!ret) {
90 		pi->num_domains = le16_to_cpu(attr->num_domains);
91 		pi->stats_addr = le32_to_cpu(attr->stats_addr_low) |
92 				(u64)le32_to_cpu(attr->stats_addr_high) << 32;
93 		pi->stats_size = le32_to_cpu(attr->stats_size);
94 	}
95 
96 	ph->xops->xfer_put(ph, t);
97 	return ret;
98 }
99 
100 static int
101 scmi_power_domain_attributes_get(const struct scmi_protocol_handle *ph,
102 				 u32 domain, struct power_dom_info *dom_info,
103 				 u32 version)
104 {
105 	int ret;
106 	u32 flags;
107 	struct scmi_xfer *t;
108 	struct scmi_msg_resp_power_domain_attributes *attr;
109 
110 	ret = ph->xops->xfer_get_init(ph, POWER_DOMAIN_ATTRIBUTES,
111 				      sizeof(domain), sizeof(*attr), &t);
112 	if (ret)
113 		return ret;
114 
115 	put_unaligned_le32(domain, t->tx.buf);
116 	attr = t->rx.buf;
117 
118 	ret = ph->xops->do_xfer(ph, t);
119 	if (!ret) {
120 		flags = le32_to_cpu(attr->flags);
121 
122 		dom_info->state_set_notify = SUPPORTS_STATE_SET_NOTIFY(flags);
123 		dom_info->state_set_async = SUPPORTS_STATE_SET_ASYNC(flags);
124 		dom_info->state_set_sync = SUPPORTS_STATE_SET_SYNC(flags);
125 		strlcpy(dom_info->name, attr->name, SCMI_MAX_STR_SIZE);
126 	}
127 	ph->xops->xfer_put(ph, t);
128 
129 	/*
130 	 * If supported overwrite short name with the extended one;
131 	 * on error just carry on and use already provided short name.
132 	 */
133 	if (!ret && PROTOCOL_REV_MAJOR(version) >= 0x3 &&
134 	    SUPPORTS_EXTENDED_NAMES(flags)) {
135 		ph->hops->extended_name_get(ph, POWER_DOMAIN_NAME_GET,
136 					    domain, dom_info->name,
137 					    SCMI_MAX_STR_SIZE);
138 	}
139 
140 	return ret;
141 }
142 
143 static int scmi_power_state_set(const struct scmi_protocol_handle *ph,
144 				u32 domain, u32 state)
145 {
146 	int ret;
147 	struct scmi_xfer *t;
148 	struct scmi_power_set_state *st;
149 
150 	ret = ph->xops->xfer_get_init(ph, POWER_STATE_SET, sizeof(*st), 0, &t);
151 	if (ret)
152 		return ret;
153 
154 	st = t->tx.buf;
155 	st->flags = cpu_to_le32(0);
156 	st->domain = cpu_to_le32(domain);
157 	st->state = cpu_to_le32(state);
158 
159 	ret = ph->xops->do_xfer(ph, t);
160 
161 	ph->xops->xfer_put(ph, t);
162 	return ret;
163 }
164 
165 static int scmi_power_state_get(const struct scmi_protocol_handle *ph,
166 				u32 domain, u32 *state)
167 {
168 	int ret;
169 	struct scmi_xfer *t;
170 
171 	ret = ph->xops->xfer_get_init(ph, POWER_STATE_GET, sizeof(u32), sizeof(u32), &t);
172 	if (ret)
173 		return ret;
174 
175 	put_unaligned_le32(domain, t->tx.buf);
176 
177 	ret = ph->xops->do_xfer(ph, t);
178 	if (!ret)
179 		*state = get_unaligned_le32(t->rx.buf);
180 
181 	ph->xops->xfer_put(ph, t);
182 	return ret;
183 }
184 
185 static int scmi_power_num_domains_get(const struct scmi_protocol_handle *ph)
186 {
187 	struct scmi_power_info *pi = ph->get_priv(ph);
188 
189 	return pi->num_domains;
190 }
191 
192 static const char *
193 scmi_power_name_get(const struct scmi_protocol_handle *ph,
194 		    u32 domain)
195 {
196 	struct scmi_power_info *pi = ph->get_priv(ph);
197 	struct power_dom_info *dom = pi->dom_info + domain;
198 
199 	return dom->name;
200 }
201 
202 static const struct scmi_power_proto_ops power_proto_ops = {
203 	.num_domains_get = scmi_power_num_domains_get,
204 	.name_get = scmi_power_name_get,
205 	.state_set = scmi_power_state_set,
206 	.state_get = scmi_power_state_get,
207 };
208 
209 static int scmi_power_request_notify(const struct scmi_protocol_handle *ph,
210 				     u32 domain, bool enable)
211 {
212 	int ret;
213 	struct scmi_xfer *t;
214 	struct scmi_power_state_notify *notify;
215 
216 	ret = ph->xops->xfer_get_init(ph, POWER_STATE_NOTIFY,
217 				      sizeof(*notify), 0, &t);
218 	if (ret)
219 		return ret;
220 
221 	notify = t->tx.buf;
222 	notify->domain = cpu_to_le32(domain);
223 	notify->notify_enable = enable ? cpu_to_le32(BIT(0)) : 0;
224 
225 	ret = ph->xops->do_xfer(ph, t);
226 
227 	ph->xops->xfer_put(ph, t);
228 	return ret;
229 }
230 
231 static int scmi_power_set_notify_enabled(const struct scmi_protocol_handle *ph,
232 					 u8 evt_id, u32 src_id, bool enable)
233 {
234 	int ret;
235 
236 	ret = scmi_power_request_notify(ph, src_id, enable);
237 	if (ret)
238 		pr_debug("FAIL_ENABLE - evt[%X] dom[%d] - ret:%d\n",
239 			 evt_id, src_id, ret);
240 
241 	return ret;
242 }
243 
244 static void *
245 scmi_power_fill_custom_report(const struct scmi_protocol_handle *ph,
246 			      u8 evt_id, ktime_t timestamp,
247 			      const void *payld, size_t payld_sz,
248 			      void *report, u32 *src_id)
249 {
250 	const struct scmi_power_state_notify_payld *p = payld;
251 	struct scmi_power_state_changed_report *r = report;
252 
253 	if (evt_id != SCMI_EVENT_POWER_STATE_CHANGED || sizeof(*p) != payld_sz)
254 		return NULL;
255 
256 	r->timestamp = timestamp;
257 	r->agent_id = le32_to_cpu(p->agent_id);
258 	r->domain_id = le32_to_cpu(p->domain_id);
259 	r->power_state = le32_to_cpu(p->power_state);
260 	*src_id = r->domain_id;
261 
262 	return r;
263 }
264 
265 static int scmi_power_get_num_sources(const struct scmi_protocol_handle *ph)
266 {
267 	struct scmi_power_info *pinfo = ph->get_priv(ph);
268 
269 	if (!pinfo)
270 		return -EINVAL;
271 
272 	return pinfo->num_domains;
273 }
274 
275 static const struct scmi_event power_events[] = {
276 	{
277 		.id = SCMI_EVENT_POWER_STATE_CHANGED,
278 		.max_payld_sz = sizeof(struct scmi_power_state_notify_payld),
279 		.max_report_sz =
280 			sizeof(struct scmi_power_state_changed_report),
281 	},
282 };
283 
284 static const struct scmi_event_ops power_event_ops = {
285 	.get_num_sources = scmi_power_get_num_sources,
286 	.set_notify_enabled = scmi_power_set_notify_enabled,
287 	.fill_custom_report = scmi_power_fill_custom_report,
288 };
289 
290 static const struct scmi_protocol_events power_protocol_events = {
291 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
292 	.ops = &power_event_ops,
293 	.evts = power_events,
294 	.num_events = ARRAY_SIZE(power_events),
295 };
296 
297 static int scmi_power_protocol_init(const struct scmi_protocol_handle *ph)
298 {
299 	int domain, ret;
300 	u32 version;
301 	struct scmi_power_info *pinfo;
302 
303 	ret = ph->xops->version_get(ph, &version);
304 	if (ret)
305 		return ret;
306 
307 	dev_dbg(ph->dev, "Power Version %d.%d\n",
308 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
309 
310 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
311 	if (!pinfo)
312 		return -ENOMEM;
313 
314 	ret = scmi_power_attributes_get(ph, pinfo);
315 	if (ret)
316 		return ret;
317 
318 	pinfo->dom_info = devm_kcalloc(ph->dev, pinfo->num_domains,
319 				       sizeof(*pinfo->dom_info), GFP_KERNEL);
320 	if (!pinfo->dom_info)
321 		return -ENOMEM;
322 
323 	for (domain = 0; domain < pinfo->num_domains; domain++) {
324 		struct power_dom_info *dom = pinfo->dom_info + domain;
325 
326 		scmi_power_domain_attributes_get(ph, domain, dom, version);
327 	}
328 
329 	pinfo->version = version;
330 
331 	return ph->set_priv(ph, pinfo);
332 }
333 
334 static const struct scmi_protocol scmi_power = {
335 	.id = SCMI_PROTOCOL_POWER,
336 	.owner = THIS_MODULE,
337 	.instance_init = &scmi_power_protocol_init,
338 	.ops = &power_proto_ops,
339 	.events = &power_protocol_events,
340 };
341 
342 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(power, scmi_power)
343