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