]> Git Repo - linux.git/blob - drivers/firmware/arm_scmi/voltage.c
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/dtor/input
[linux.git] / drivers / firmware / arm_scmi / voltage.c
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * System Control and Management Interface (SCMI) Voltage Protocol
4  *
5  * Copyright (C) 2020 ARM Ltd.
6  */
7
8 #include <linux/scmi_protocol.h>
9
10 #include "common.h"
11
12 #define VOLTAGE_DOMS_NUM_MASK           GENMASK(15, 0)
13 #define REMAINING_LEVELS_MASK           GENMASK(31, 16)
14 #define RETURNED_LEVELS_MASK            GENMASK(11, 0)
15
16 enum scmi_voltage_protocol_cmd {
17         VOLTAGE_DOMAIN_ATTRIBUTES = 0x3,
18         VOLTAGE_DESCRIBE_LEVELS = 0x4,
19         VOLTAGE_CONFIG_SET = 0x5,
20         VOLTAGE_CONFIG_GET = 0x6,
21         VOLTAGE_LEVEL_SET = 0x7,
22         VOLTAGE_LEVEL_GET = 0x8,
23 };
24
25 #define NUM_VOLTAGE_DOMAINS(x)  ((u16)(FIELD_GET(VOLTAGE_DOMS_NUM_MASK, (x))))
26
27 struct scmi_msg_resp_domain_attributes {
28         __le32 attr;
29         u8 name[SCMI_MAX_STR_SIZE];
30 };
31
32 struct scmi_msg_cmd_describe_levels {
33         __le32 domain_id;
34         __le32 level_index;
35 };
36
37 struct scmi_msg_resp_describe_levels {
38         __le32 flags;
39 #define NUM_REMAINING_LEVELS(f) ((u16)(FIELD_GET(REMAINING_LEVELS_MASK, (f))))
40 #define NUM_RETURNED_LEVELS(f)  ((u16)(FIELD_GET(RETURNED_LEVELS_MASK, (f))))
41 #define SUPPORTS_SEGMENTED_LEVELS(f)    ((f) & BIT(12))
42         __le32 voltage[];
43 };
44
45 struct scmi_msg_cmd_config_set {
46         __le32 domain_id;
47         __le32 config;
48 };
49
50 struct scmi_msg_cmd_level_set {
51         __le32 domain_id;
52         __le32 flags;
53         __le32 voltage_level;
54 };
55
56 struct voltage_info {
57         unsigned int version;
58         unsigned int num_domains;
59         struct scmi_voltage_info *domains;
60 };
61
62 static int scmi_protocol_attributes_get(const struct scmi_handle *handle,
63                                         struct voltage_info *vinfo)
64 {
65         int ret;
66         struct scmi_xfer *t;
67
68         ret = scmi_xfer_get_init(handle, PROTOCOL_ATTRIBUTES,
69                                  SCMI_PROTOCOL_VOLTAGE, 0, sizeof(__le32), &t);
70         if (ret)
71                 return ret;
72
73         ret = scmi_do_xfer(handle, t);
74         if (!ret)
75                 vinfo->num_domains =
76                         NUM_VOLTAGE_DOMAINS(get_unaligned_le32(t->rx.buf));
77
78         scmi_xfer_put(handle, t);
79         return ret;
80 }
81
82 static int scmi_init_voltage_levels(struct device *dev,
83                                     struct scmi_voltage_info *v,
84                                     u32 num_returned, u32 num_remaining,
85                                     bool segmented)
86 {
87         u32 num_levels;
88
89         num_levels = num_returned + num_remaining;
90         /*
91          * segmented levels entries are represented by a single triplet
92          * returned all in one go.
93          */
94         if (!num_levels ||
95             (segmented && (num_remaining || num_returned != 3))) {
96                 dev_err(dev,
97                         "Invalid level descriptor(%d/%d/%d) for voltage dom %d\n",
98                         num_levels, num_returned, num_remaining, v->id);
99                 return -EINVAL;
100         }
101
102         v->levels_uv = devm_kcalloc(dev, num_levels, sizeof(u32), GFP_KERNEL);
103         if (!v->levels_uv)
104                 return -ENOMEM;
105
106         v->num_levels = num_levels;
107         v->segmented = segmented;
108
109         return 0;
110 }
111
112 static int scmi_voltage_descriptors_get(const struct scmi_handle *handle,
113                                         struct voltage_info *vinfo)
114 {
115         int ret, dom;
116         struct scmi_xfer *td, *tl;
117         struct device *dev = handle->dev;
118         struct scmi_msg_resp_domain_attributes *resp_dom;
119         struct scmi_msg_resp_describe_levels *resp_levels;
120
121         ret = scmi_xfer_get_init(handle, VOLTAGE_DOMAIN_ATTRIBUTES,
122                                  SCMI_PROTOCOL_VOLTAGE, sizeof(__le32),
123                                  sizeof(*resp_dom), &td);
124         if (ret)
125                 return ret;
126         resp_dom = td->rx.buf;
127
128         ret = scmi_xfer_get_init(handle, VOLTAGE_DESCRIBE_LEVELS,
129                                  SCMI_PROTOCOL_VOLTAGE, sizeof(__le64), 0, &tl);
130         if (ret)
131                 goto outd;
132         resp_levels = tl->rx.buf;
133
134         for (dom = 0; dom < vinfo->num_domains; dom++) {
135                 u32 desc_index = 0;
136                 u16 num_returned = 0, num_remaining = 0;
137                 struct scmi_msg_cmd_describe_levels *cmd;
138                 struct scmi_voltage_info *v;
139
140                 /* Retrieve domain attributes at first ... */
141                 put_unaligned_le32(dom, td->tx.buf);
142                 ret = scmi_do_xfer(handle, td);
143                 /* Skip domain on comms error */
144                 if (ret)
145                         continue;
146
147                 v = vinfo->domains + dom;
148                 v->id = dom;
149                 v->attributes = le32_to_cpu(resp_dom->attr);
150                 strlcpy(v->name, resp_dom->name, SCMI_MAX_STR_SIZE);
151
152                 cmd = tl->tx.buf;
153                 /* ...then retrieve domain levels descriptions */
154                 do {
155                         u32 flags;
156                         int cnt;
157
158                         cmd->domain_id = cpu_to_le32(v->id);
159                         cmd->level_index = desc_index;
160                         ret = scmi_do_xfer(handle, tl);
161                         if (ret)
162                                 break;
163
164                         flags = le32_to_cpu(resp_levels->flags);
165                         num_returned = NUM_RETURNED_LEVELS(flags);
166                         num_remaining = NUM_REMAINING_LEVELS(flags);
167
168                         /* Allocate space for num_levels if not already done */
169                         if (!v->num_levels) {
170                                 ret = scmi_init_voltage_levels(dev, v,
171                                                                num_returned,
172                                                                num_remaining,
173                                               SUPPORTS_SEGMENTED_LEVELS(flags));
174                                 if (ret)
175                                         break;
176                         }
177
178                         if (desc_index + num_returned > v->num_levels) {
179                                 dev_err(handle->dev,
180                                         "No. of voltage levels can't exceed %d\n",
181                                         v->num_levels);
182                                 ret = -EINVAL;
183                                 break;
184                         }
185
186                         for (cnt = 0; cnt < num_returned; cnt++) {
187                                 s32 val;
188
189                                 val =
190                                     (s32)le32_to_cpu(resp_levels->voltage[cnt]);
191                                 v->levels_uv[desc_index + cnt] = val;
192                                 if (val < 0)
193                                         v->negative_volts_allowed = true;
194                         }
195
196                         desc_index += num_returned;
197
198                         scmi_reset_rx_to_maxsz(handle, tl);
199                         /* check both to avoid infinite loop due to buggy fw */
200                 } while (num_returned && num_remaining);
201
202                 if (ret) {
203                         v->num_levels = 0;
204                         devm_kfree(dev, v->levels_uv);
205                 }
206
207                 scmi_reset_rx_to_maxsz(handle, td);
208         }
209
210         scmi_xfer_put(handle, tl);
211 outd:
212         scmi_xfer_put(handle, td);
213
214         return ret;
215 }
216
217 static int __scmi_voltage_get_u32(const struct scmi_handle *handle,
218                                   u8 cmd_id, u32 domain_id, u32 *value)
219 {
220         int ret;
221         struct scmi_xfer *t;
222         struct voltage_info *vinfo = handle->voltage_priv;
223
224         if (domain_id >= vinfo->num_domains)
225                 return -EINVAL;
226
227         ret = scmi_xfer_get_init(handle, cmd_id,
228                                  SCMI_PROTOCOL_VOLTAGE,
229                                  sizeof(__le32), 0, &t);
230         if (ret)
231                 return ret;
232
233         put_unaligned_le32(domain_id, t->tx.buf);
234         ret = scmi_do_xfer(handle, t);
235         if (!ret)
236                 *value = get_unaligned_le32(t->rx.buf);
237
238         scmi_xfer_put(handle, t);
239         return ret;
240 }
241
242 static int scmi_voltage_config_set(const struct scmi_handle *handle,
243                                    u32 domain_id, u32 config)
244 {
245         int ret;
246         struct scmi_xfer *t;
247         struct voltage_info *vinfo = handle->voltage_priv;
248         struct scmi_msg_cmd_config_set *cmd;
249
250         if (domain_id >= vinfo->num_domains)
251                 return -EINVAL;
252
253         ret = scmi_xfer_get_init(handle, VOLTAGE_CONFIG_SET,
254                                  SCMI_PROTOCOL_VOLTAGE,
255                                  sizeof(*cmd), 0, &t);
256         if (ret)
257                 return ret;
258
259         cmd = t->tx.buf;
260         cmd->domain_id = cpu_to_le32(domain_id);
261         cmd->config = cpu_to_le32(config & GENMASK(3, 0));
262
263         ret = scmi_do_xfer(handle, t);
264
265         scmi_xfer_put(handle, t);
266         return ret;
267 }
268
269 static int scmi_voltage_config_get(const struct scmi_handle *handle,
270                                    u32 domain_id, u32 *config)
271 {
272         return __scmi_voltage_get_u32(handle, VOLTAGE_CONFIG_GET,
273                                       domain_id, config);
274 }
275
276 static int scmi_voltage_level_set(const struct scmi_handle *handle,
277                                   u32 domain_id, u32 flags, s32 volt_uV)
278 {
279         int ret;
280         struct scmi_xfer *t;
281         struct voltage_info *vinfo = handle->voltage_priv;
282         struct scmi_msg_cmd_level_set *cmd;
283
284         if (domain_id >= vinfo->num_domains)
285                 return -EINVAL;
286
287         ret = scmi_xfer_get_init(handle, VOLTAGE_LEVEL_SET,
288                                  SCMI_PROTOCOL_VOLTAGE,
289                                  sizeof(*cmd), 0, &t);
290         if (ret)
291                 return ret;
292
293         cmd = t->tx.buf;
294         cmd->domain_id = cpu_to_le32(domain_id);
295         cmd->flags = cpu_to_le32(flags);
296         cmd->voltage_level = cpu_to_le32(volt_uV);
297
298         ret = scmi_do_xfer(handle, t);
299
300         scmi_xfer_put(handle, t);
301         return ret;
302 }
303
304 static int scmi_voltage_level_get(const struct scmi_handle *handle,
305                                   u32 domain_id, s32 *volt_uV)
306 {
307         return __scmi_voltage_get_u32(handle, VOLTAGE_LEVEL_GET,
308                                       domain_id, (u32 *)volt_uV);
309 }
310
311 static const struct scmi_voltage_info * __must_check
312 scmi_voltage_info_get(const struct scmi_handle *handle, u32 domain_id)
313 {
314         struct voltage_info *vinfo = handle->voltage_priv;
315
316         if (domain_id >= vinfo->num_domains ||
317             !vinfo->domains[domain_id].num_levels)
318                 return NULL;
319
320         return vinfo->domains + domain_id;
321 }
322
323 static int scmi_voltage_domains_num_get(const struct scmi_handle *handle)
324 {
325         struct voltage_info *vinfo = handle->voltage_priv;
326
327         return vinfo->num_domains;
328 }
329
330 static struct scmi_voltage_ops voltage_ops = {
331         .num_domains_get = scmi_voltage_domains_num_get,
332         .info_get = scmi_voltage_info_get,
333         .config_set = scmi_voltage_config_set,
334         .config_get = scmi_voltage_config_get,
335         .level_set = scmi_voltage_level_set,
336         .level_get = scmi_voltage_level_get,
337 };
338
339 static int scmi_voltage_protocol_init(struct scmi_handle *handle)
340 {
341         int ret;
342         u32 version;
343         struct voltage_info *vinfo;
344
345         ret = scmi_version_get(handle, SCMI_PROTOCOL_VOLTAGE, &version);
346         if (ret)
347                 return ret;
348
349         dev_dbg(handle->dev, "Voltage Version %d.%d\n",
350                 PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
351
352         vinfo = devm_kzalloc(handle->dev, sizeof(*vinfo), GFP_KERNEL);
353         if (!vinfo)
354                 return -ENOMEM;
355         vinfo->version = version;
356
357         ret = scmi_protocol_attributes_get(handle, vinfo);
358         if (ret)
359                 return ret;
360
361         if (vinfo->num_domains) {
362                 vinfo->domains = devm_kcalloc(handle->dev, vinfo->num_domains,
363                                               sizeof(*vinfo->domains),
364                                               GFP_KERNEL);
365                 if (!vinfo->domains)
366                         return -ENOMEM;
367                 ret = scmi_voltage_descriptors_get(handle, vinfo);
368                 if (ret)
369                         return ret;
370         } else {
371                 dev_warn(handle->dev, "No Voltage domains found.\n");
372         }
373
374         handle->voltage_ops = &voltage_ops;
375         handle->voltage_priv = vinfo;
376
377         return 0;
378 }
379
380 DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(SCMI_PROTOCOL_VOLTAGE, voltage)
This page took 0.060429 seconds and 4 git commands to generate.