1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * System Control and Management Interface (SCMI) Voltage Protocol |
4 | * |
5 | * Copyright (C) 2020-2022 ARM Ltd. |
6 | */ |
7 | |
8 | #include <linux/module.h> |
9 | #include <linux/scmi_protocol.h> |
10 | |
11 | #include "protocols.h" |
12 | |
13 | /* Updated only after ALL the mandatory features for that version are merged */ |
14 | #define SCMI_PROTOCOL_SUPPORTED_VERSION 0x20000 |
15 | |
16 | #define VOLTAGE_DOMS_NUM_MASK GENMASK(15, 0) |
17 | #define REMAINING_LEVELS_MASK GENMASK(31, 16) |
18 | #define RETURNED_LEVELS_MASK GENMASK(11, 0) |
19 | |
20 | enum scmi_voltage_protocol_cmd { |
21 | VOLTAGE_DOMAIN_ATTRIBUTES = 0x3, |
22 | VOLTAGE_DESCRIBE_LEVELS = 0x4, |
23 | VOLTAGE_CONFIG_SET = 0x5, |
24 | VOLTAGE_CONFIG_GET = 0x6, |
25 | VOLTAGE_LEVEL_SET = 0x7, |
26 | VOLTAGE_LEVEL_GET = 0x8, |
27 | VOLTAGE_DOMAIN_NAME_GET = 0x09, |
28 | }; |
29 | |
30 | #define NUM_VOLTAGE_DOMAINS(x) ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x)))) |
31 | |
32 | struct scmi_msg_resp_domain_attributes { |
33 | __le32 attr; |
34 | #define SUPPORTS_ASYNC_LEVEL_SET(x) ((x) & BIT(31)) |
35 | #define SUPPORTS_EXTENDED_NAMES(x) ((x) & BIT(30)) |
36 | u8 name[SCMI_SHORT_NAME_MAX_SIZE]; |
37 | }; |
38 | |
39 | struct scmi_msg_cmd_describe_levels { |
40 | __le32 domain_id; |
41 | __le32 level_index; |
42 | }; |
43 | |
44 | struct scmi_msg_resp_describe_levels { |
45 | __le32 flags; |
46 | #define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f)))) |
47 | #define NUM_RETURNED_LEVELS(f) ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f)))) |
48 | #define SUPPORTS_SEGMENTED_LEVELS(f) ((f) & BIT(12)) |
49 | __le32 voltage[]; |
50 | }; |
51 | |
52 | struct scmi_msg_cmd_config_set { |
53 | __le32 domain_id; |
54 | __le32 config; |
55 | }; |
56 | |
57 | struct scmi_msg_cmd_level_set { |
58 | __le32 domain_id; |
59 | __le32 flags; |
60 | __le32 voltage_level; |
61 | }; |
62 | |
63 | struct scmi_resp_voltage_level_set_complete { |
64 | __le32 domain_id; |
65 | __le32 voltage_level; |
66 | }; |
67 | |
68 | struct voltage_info { |
69 | unsigned int version; |
70 | unsigned int num_domains; |
71 | struct scmi_voltage_info *domains; |
72 | }; |
73 | |
74 | static int scmi_protocol_attributes_get(const struct scmi_protocol_handle *ph, |
75 | struct voltage_info *vinfo) |
76 | { |
77 | int ret; |
78 | struct scmi_xfer *t; |
79 | |
80 | ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0, |
81 | sizeof(__le32), &t); |
82 | if (ret) |
83 | return ret; |
84 | |
85 | ret = ph->xops->do_xfer(ph, t); |
86 | if (!ret) |
87 | vinfo->num_domains = |
88 | NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf)); |
89 | |
90 | ph->xops->xfer_put(ph, t); |
91 | return ret; |
92 | } |
93 | |
94 | static int scmi_init_voltage_levels(struct device *dev, |
95 | struct scmi_voltage_info *v, |
96 | u32 num_returned, u32 num_remaining, |
97 | bool segmented) |
98 | { |
99 | u32 num_levels; |
100 | |
101 | num_levels = num_returned + num_remaining; |
102 | /* |
103 | * segmented levels entries are represented by a single triplet |
104 | * returned all in one go. |
105 | */ |
106 | if (!num_levels || |
107 | (segmented && (num_remaining || num_returned != 3))) { |
108 | dev_err(dev, |
109 | "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n" , |
110 | num_levels, num_returned, num_remaining, v->id); |
111 | return -EINVAL; |
112 | } |
113 | |
114 | v->levels_uv = devm_kcalloc(dev, n: num_levels, size: sizeof(u32), GFP_KERNEL); |
115 | if (!v->levels_uv) |
116 | return -ENOMEM; |
117 | |
118 | v->num_levels = num_levels; |
119 | v->segmented = segmented; |
120 | |
121 | return 0; |
122 | } |
123 | |
124 | struct scmi_volt_ipriv { |
125 | struct device *dev; |
126 | struct scmi_voltage_info *v; |
127 | }; |
128 | |
129 | static void iter_volt_levels_prepare_message(void *message, |
130 | unsigned int desc_index, |
131 | const void *priv) |
132 | { |
133 | struct scmi_msg_cmd_describe_levels *msg = message; |
134 | const struct scmi_volt_ipriv *p = priv; |
135 | |
136 | msg->domain_id = cpu_to_le32(p->v->id); |
137 | msg->level_index = cpu_to_le32(desc_index); |
138 | } |
139 | |
140 | static int iter_volt_levels_update_state(struct scmi_iterator_state *st, |
141 | const void *response, void *priv) |
142 | { |
143 | int ret = 0; |
144 | u32 flags; |
145 | const struct scmi_msg_resp_describe_levels *r = response; |
146 | struct scmi_volt_ipriv *p = priv; |
147 | |
148 | flags = le32_to_cpu(r->flags); |
149 | st->num_returned = NUM_RETURNED_LEVELS(flags); |
150 | st->num_remaining = NUM_REMAINING_LEVELS(flags); |
151 | |
152 | /* Allocate space for num_levels if not already done */ |
153 | if (!p->v->num_levels) { |
154 | ret = scmi_init_voltage_levels(dev: p->dev, v: p->v, num_returned: st->num_returned, |
155 | num_remaining: st->num_remaining, |
156 | SUPPORTS_SEGMENTED_LEVELS(flags)); |
157 | if (!ret) |
158 | st->max_resources = p->v->num_levels; |
159 | } |
160 | |
161 | return ret; |
162 | } |
163 | |
164 | static int |
165 | iter_volt_levels_process_response(const struct scmi_protocol_handle *ph, |
166 | const void *response, |
167 | struct scmi_iterator_state *st, void *priv) |
168 | { |
169 | s32 val; |
170 | const struct scmi_msg_resp_describe_levels *r = response; |
171 | struct scmi_volt_ipriv *p = priv; |
172 | |
173 | val = (s32)le32_to_cpu(r->voltage[st->loop_idx]); |
174 | p->v->levels_uv[st->desc_index + st->loop_idx] = val; |
175 | if (val < 0) |
176 | p->v->negative_volts_allowed = true; |
177 | |
178 | return 0; |
179 | } |
180 | |
181 | static int scmi_voltage_levels_get(const struct scmi_protocol_handle *ph, |
182 | struct scmi_voltage_info *v) |
183 | { |
184 | int ret; |
185 | void *iter; |
186 | struct scmi_iterator_ops ops = { |
187 | .prepare_message = iter_volt_levels_prepare_message, |
188 | .update_state = iter_volt_levels_update_state, |
189 | .process_response = iter_volt_levels_process_response, |
190 | }; |
191 | struct scmi_volt_ipriv vpriv = { |
192 | .dev = ph->dev, |
193 | .v = v, |
194 | }; |
195 | |
196 | iter = ph->hops->iter_response_init(ph, &ops, v->num_levels, |
197 | VOLTAGE_DESCRIBE_LEVELS, |
198 | sizeof(struct scmi_msg_cmd_describe_levels), |
199 | &vpriv); |
200 | if (IS_ERR(ptr: iter)) |
201 | return PTR_ERR(ptr: iter); |
202 | |
203 | ret = ph->hops->iter_response_run(iter); |
204 | if (ret) { |
205 | v->num_levels = 0; |
206 | devm_kfree(dev: ph->dev, p: v->levels_uv); |
207 | } |
208 | |
209 | return ret; |
210 | } |
211 | |
212 | static int scmi_voltage_descriptors_get(const struct scmi_protocol_handle *ph, |
213 | struct voltage_info *vinfo) |
214 | { |
215 | int ret, dom; |
216 | struct scmi_xfer *td; |
217 | struct scmi_msg_resp_domain_attributes *resp_dom; |
218 | |
219 | ret = ph->xops->xfer_get_init(ph, VOLTAGE_DOMAIN_ATTRIBUTES, |
220 | sizeof(__le32), sizeof(*resp_dom), &td); |
221 | if (ret) |
222 | return ret; |
223 | resp_dom = td->rx.buf; |
224 | |
225 | for (dom = 0; dom < vinfo->num_domains; dom++) { |
226 | u32 attributes; |
227 | struct scmi_voltage_info *v; |
228 | |
229 | /* Retrieve domain attributes at first ... */ |
230 | put_unaligned_le32(val: dom, p: td->tx.buf); |
231 | /* Skip domain on comms error */ |
232 | if (ph->xops->do_xfer(ph, td)) |
233 | continue; |
234 | |
235 | v = vinfo->domains + dom; |
236 | v->id = dom; |
237 | attributes = le32_to_cpu(resp_dom->attr); |
238 | strscpy(v->name, resp_dom->name, SCMI_SHORT_NAME_MAX_SIZE); |
239 | |
240 | /* |
241 | * If supported overwrite short name with the extended one; |
242 | * on error just carry on and use already provided short name. |
243 | */ |
244 | if (PROTOCOL_REV_MAJOR(vinfo->version) >= 0x2) { |
245 | if (SUPPORTS_EXTENDED_NAMES(attributes)) |
246 | ph->hops->extended_name_get(ph, |
247 | VOLTAGE_DOMAIN_NAME_GET, |
248 | v->id, NULL, v->name, |
249 | SCMI_MAX_STR_SIZE); |
250 | if (SUPPORTS_ASYNC_LEVEL_SET(attributes)) |
251 | v->async_level_set = true; |
252 | } |
253 | |
254 | /* Skip invalid voltage descriptors */ |
255 | scmi_voltage_levels_get(ph, v); |
256 | } |
257 | |
258 | ph->xops->xfer_put(ph, td); |
259 | |
260 | return ret; |
261 | } |
262 | |
263 | static int __scmi_voltage_get_u32(const struct scmi_protocol_handle *ph, |
264 | u8 cmd_id, u32 domain_id, u32 *value) |
265 | { |
266 | int ret; |
267 | struct scmi_xfer *t; |
268 | struct voltage_info *vinfo = ph->get_priv(ph); |
269 | |
270 | if (domain_id >= vinfo->num_domains) |
271 | return -EINVAL; |
272 | |
273 | ret = ph->xops->xfer_get_init(ph, cmd_id, sizeof(__le32), 0, &t); |
274 | if (ret) |
275 | return ret; |
276 | |
277 | put_unaligned_le32(val: domain_id, p: t->tx.buf); |
278 | ret = ph->xops->do_xfer(ph, t); |
279 | if (!ret) |
280 | *value = get_unaligned_le32(p: t->rx.buf); |
281 | |
282 | ph->xops->xfer_put(ph, t); |
283 | return ret; |
284 | } |
285 | |
286 | static int scmi_voltage_config_set(const struct scmi_protocol_handle *ph, |
287 | u32 domain_id, u32 config) |
288 | { |
289 | int ret; |
290 | struct scmi_xfer *t; |
291 | struct voltage_info *vinfo = ph->get_priv(ph); |
292 | struct scmi_msg_cmd_config_set *cmd; |
293 | |
294 | if (domain_id >= vinfo->num_domains) |
295 | return -EINVAL; |
296 | |
297 | ret = ph->xops->xfer_get_init(ph, VOLTAGE_CONFIG_SET, |
298 | sizeof(*cmd), 0, &t); |
299 | if (ret) |
300 | return ret; |
301 | |
302 | cmd = t->tx.buf; |
303 | cmd->domain_id = cpu_to_le32(domain_id); |
304 | cmd->config = cpu_to_le32(config & GENMASK(3, 0)); |
305 | |
306 | ret = ph->xops->do_xfer(ph, t); |
307 | |
308 | ph->xops->xfer_put(ph, t); |
309 | return ret; |
310 | } |
311 | |
312 | static int scmi_voltage_config_get(const struct scmi_protocol_handle *ph, |
313 | u32 domain_id, u32 *config) |
314 | { |
315 | return __scmi_voltage_get_u32(ph, cmd_id: VOLTAGE_CONFIG_GET, |
316 | domain_id, value: config); |
317 | } |
318 | |
319 | static int scmi_voltage_level_set(const struct scmi_protocol_handle *ph, |
320 | u32 domain_id, |
321 | enum scmi_voltage_level_mode mode, |
322 | s32 volt_uV) |
323 | { |
324 | int ret; |
325 | struct scmi_xfer *t; |
326 | struct voltage_info *vinfo = ph->get_priv(ph); |
327 | struct scmi_msg_cmd_level_set *cmd; |
328 | struct scmi_voltage_info *v; |
329 | |
330 | if (domain_id >= vinfo->num_domains) |
331 | return -EINVAL; |
332 | |
333 | ret = ph->xops->xfer_get_init(ph, VOLTAGE_LEVEL_SET, |
334 | sizeof(*cmd), 0, &t); |
335 | if (ret) |
336 | return ret; |
337 | |
338 | v = vinfo->domains + domain_id; |
339 | |
340 | cmd = t->tx.buf; |
341 | cmd->domain_id = cpu_to_le32(domain_id); |
342 | cmd->voltage_level = cpu_to_le32(volt_uV); |
343 | |
344 | if (!v->async_level_set || mode != SCMI_VOLTAGE_LEVEL_SET_AUTO) { |
345 | cmd->flags = cpu_to_le32(0x0); |
346 | ret = ph->xops->do_xfer(ph, t); |
347 | } else { |
348 | cmd->flags = cpu_to_le32(0x1); |
349 | ret = ph->xops->do_xfer_with_response(ph, t); |
350 | if (!ret) { |
351 | struct scmi_resp_voltage_level_set_complete *resp; |
352 | |
353 | resp = t->rx.buf; |
354 | if (le32_to_cpu(resp->domain_id) == domain_id) |
355 | dev_dbg(ph->dev, |
356 | "Voltage domain %d set async to %d\n" , |
357 | v->id, |
358 | le32_to_cpu(resp->voltage_level)); |
359 | else |
360 | ret = -EPROTO; |
361 | } |
362 | } |
363 | |
364 | ph->xops->xfer_put(ph, t); |
365 | return ret; |
366 | } |
367 | |
368 | static int scmi_voltage_level_get(const struct scmi_protocol_handle *ph, |
369 | u32 domain_id, s32 *volt_uV) |
370 | { |
371 | return __scmi_voltage_get_u32(ph, cmd_id: VOLTAGE_LEVEL_GET, |
372 | domain_id, value: (u32 *)volt_uV); |
373 | } |
374 | |
375 | static const struct scmi_voltage_info * __must_check |
376 | scmi_voltage_info_get(const struct scmi_protocol_handle *ph, u32 domain_id) |
377 | { |
378 | struct voltage_info *vinfo = ph->get_priv(ph); |
379 | |
380 | if (domain_id >= vinfo->num_domains || |
381 | !vinfo->domains[domain_id].num_levels) |
382 | return NULL; |
383 | |
384 | return vinfo->domains + domain_id; |
385 | } |
386 | |
387 | static int scmi_voltage_domains_num_get(const struct scmi_protocol_handle *ph) |
388 | { |
389 | struct voltage_info *vinfo = ph->get_priv(ph); |
390 | |
391 | return vinfo->num_domains; |
392 | } |
393 | |
394 | static struct scmi_voltage_proto_ops voltage_proto_ops = { |
395 | .num_domains_get = scmi_voltage_domains_num_get, |
396 | .info_get = scmi_voltage_info_get, |
397 | .config_set = scmi_voltage_config_set, |
398 | .config_get = scmi_voltage_config_get, |
399 | .level_set = scmi_voltage_level_set, |
400 | .level_get = scmi_voltage_level_get, |
401 | }; |
402 | |
403 | static int scmi_voltage_protocol_init(const struct scmi_protocol_handle *ph) |
404 | { |
405 | int ret; |
406 | u32 version; |
407 | struct voltage_info *vinfo; |
408 | |
409 | ret = ph->xops->version_get(ph, &version); |
410 | if (ret) |
411 | return ret; |
412 | |
413 | dev_dbg(ph->dev, "Voltage Version %d.%d\n" , |
414 | PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version)); |
415 | |
416 | vinfo = devm_kzalloc(dev: ph->dev, size: sizeof(*vinfo), GFP_KERNEL); |
417 | if (!vinfo) |
418 | return -ENOMEM; |
419 | vinfo->version = version; |
420 | |
421 | ret = scmi_protocol_attributes_get(ph, vinfo); |
422 | if (ret) |
423 | return ret; |
424 | |
425 | if (vinfo->num_domains) { |
426 | vinfo->domains = devm_kcalloc(dev: ph->dev, n: vinfo->num_domains, |
427 | size: sizeof(*vinfo->domains), |
428 | GFP_KERNEL); |
429 | if (!vinfo->domains) |
430 | return -ENOMEM; |
431 | ret = scmi_voltage_descriptors_get(ph, vinfo); |
432 | if (ret) |
433 | return ret; |
434 | } else { |
435 | dev_warn(ph->dev, "No Voltage domains found.\n" ); |
436 | } |
437 | |
438 | return ph->set_priv(ph, vinfo, version); |
439 | } |
440 | |
441 | static const struct scmi_protocol scmi_voltage = { |
442 | .id = SCMI_PROTOCOL_VOLTAGE, |
443 | .owner = THIS_MODULE, |
444 | .instance_init = &scmi_voltage_protocol_init, |
445 | .ops = &voltage_proto_ops, |
446 | .supported_version = SCMI_PROTOCOL_SUPPORTED_VERSION, |
447 | }; |
448 | |
449 | DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(voltage, scmi_voltage) |
450 | |