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