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