xref: /openbmc/linux/drivers/firmware/arm_scmi/powercap.c (revision 0316f99c4780b0a5fd60b7f136c64cb1af8d5fc3)
1*0316f99cSCristian Marussi // SPDX-License-Identifier: GPL-2.0
2*0316f99cSCristian Marussi /*
3*0316f99cSCristian Marussi  * System Control and Management Interface (SCMI) Powercap Protocol
4*0316f99cSCristian Marussi  *
5*0316f99cSCristian Marussi  * Copyright (C) 2022 ARM Ltd.
6*0316f99cSCristian Marussi  */
7*0316f99cSCristian Marussi 
8*0316f99cSCristian Marussi #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
9*0316f99cSCristian Marussi 
10*0316f99cSCristian Marussi #include <linux/bitfield.h>
11*0316f99cSCristian Marussi #include <linux/module.h>
12*0316f99cSCristian Marussi #include <linux/scmi_protocol.h>
13*0316f99cSCristian Marussi 
14*0316f99cSCristian Marussi #include "protocols.h"
15*0316f99cSCristian Marussi #include "notify.h"
16*0316f99cSCristian Marussi 
17*0316f99cSCristian Marussi enum scmi_powercap_protocol_cmd {
18*0316f99cSCristian Marussi 	POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
19*0316f99cSCristian Marussi 	POWERCAP_CAP_GET = 0x4,
20*0316f99cSCristian Marussi 	POWERCAP_CAP_SET = 0x5,
21*0316f99cSCristian Marussi 	POWERCAP_PAI_GET = 0x6,
22*0316f99cSCristian Marussi 	POWERCAP_PAI_SET = 0x7,
23*0316f99cSCristian Marussi 	POWERCAP_DOMAIN_NAME_GET = 0x8,
24*0316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_GET = 0x9,
25*0316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY = 0xa,
26*0316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
27*0316f99cSCristian Marussi 	POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
28*0316f99cSCristian Marussi };
29*0316f99cSCristian Marussi 
30*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes {
31*0316f99cSCristian Marussi 	__le32 attributes;
32*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x)		((x) & BIT(31))
33*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x)	((x) & BIT(30))
34*0316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x)		((x) & BIT(29))
35*0316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x)			((x) & BIT(28))
36*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x)		((x) & BIT(27))
37*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x)			((x) & BIT(26))
38*0316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x)		((x) & BIT(25))
39*0316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x)				\
40*0316f99cSCristian Marussi 		(FIELD_GET(GENMASK(24, 23), (x)))
41*0316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_MW(x)			\
42*0316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x2)
43*0316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_UW(x)			\
44*0316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x1)
45*0316f99cSCristian Marussi 	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
46*0316f99cSCristian Marussi 	__le32 min_pai;
47*0316f99cSCristian Marussi 	__le32 max_pai;
48*0316f99cSCristian Marussi 	__le32 pai_step;
49*0316f99cSCristian Marussi 	__le32 min_power_cap;
50*0316f99cSCristian Marussi 	__le32 max_power_cap;
51*0316f99cSCristian Marussi 	__le32 power_cap_step;
52*0316f99cSCristian Marussi 	__le32 sustainable_power;
53*0316f99cSCristian Marussi 	__le32 accuracy;
54*0316f99cSCristian Marussi 	__le32 parent_id;
55*0316f99cSCristian Marussi };
56*0316f99cSCristian Marussi 
57*0316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai {
58*0316f99cSCristian Marussi 	__le32 domain;
59*0316f99cSCristian Marussi 	__le32 flags;
60*0316f99cSCristian Marussi #define CAP_SET_ASYNC		BIT(1)
61*0316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP	BIT(0)
62*0316f99cSCristian Marussi 	__le32 value;
63*0316f99cSCristian Marussi };
64*0316f99cSCristian Marussi 
65*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete {
66*0316f99cSCristian Marussi 	__le32 domain;
67*0316f99cSCristian Marussi 	__le32 power_cap;
68*0316f99cSCristian Marussi };
69*0316f99cSCristian Marussi 
70*0316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get {
71*0316f99cSCristian Marussi 	__le32 power;
72*0316f99cSCristian Marussi 	__le32 pai;
73*0316f99cSCristian Marussi };
74*0316f99cSCristian Marussi 
75*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap {
76*0316f99cSCristian Marussi 	__le32 domain;
77*0316f99cSCristian Marussi 	__le32 notify_enable;
78*0316f99cSCristian Marussi };
79*0316f99cSCristian Marussi 
80*0316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh {
81*0316f99cSCristian Marussi 	__le32 domain;
82*0316f99cSCristian Marussi 	__le32 notify_enable;
83*0316f99cSCristian Marussi 	__le32 power_thresh_low;
84*0316f99cSCristian Marussi 	__le32 power_thresh_high;
85*0316f99cSCristian Marussi };
86*0316f99cSCristian Marussi 
87*0316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld {
88*0316f99cSCristian Marussi 	__le32 agent_id;
89*0316f99cSCristian Marussi 	__le32 domain_id;
90*0316f99cSCristian Marussi 	__le32 power_cap;
91*0316f99cSCristian Marussi 	__le32 pai;
92*0316f99cSCristian Marussi };
93*0316f99cSCristian Marussi 
94*0316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld {
95*0316f99cSCristian Marussi 	__le32 agent_id;
96*0316f99cSCristian Marussi 	__le32 domain_id;
97*0316f99cSCristian Marussi 	__le32 power;
98*0316f99cSCristian Marussi };
99*0316f99cSCristian Marussi 
100*0316f99cSCristian Marussi struct scmi_powercap_state {
101*0316f99cSCristian Marussi 	bool meas_notif_enabled;
102*0316f99cSCristian Marussi 	u64 thresholds;
103*0316f99cSCristian Marussi #define THRESH_LOW(p, id)				\
104*0316f99cSCristian Marussi 	(lower_32_bits((p)->states[(id)].thresholds))
105*0316f99cSCristian Marussi #define THRESH_HIGH(p, id)				\
106*0316f99cSCristian Marussi 	(upper_32_bits((p)->states[(id)].thresholds))
107*0316f99cSCristian Marussi };
108*0316f99cSCristian Marussi 
109*0316f99cSCristian Marussi struct powercap_info {
110*0316f99cSCristian Marussi 	u32 version;
111*0316f99cSCristian Marussi 	int num_domains;
112*0316f99cSCristian Marussi 	struct scmi_powercap_state *states;
113*0316f99cSCristian Marussi 	struct scmi_powercap_info *powercaps;
114*0316f99cSCristian Marussi };
115*0316f99cSCristian Marussi 
116*0316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
117*0316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY,
118*0316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY,
119*0316f99cSCristian Marussi };
120*0316f99cSCristian Marussi 
121*0316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
122*0316f99cSCristian Marussi 				u32 domain, int message_id, bool enable);
123*0316f99cSCristian Marussi 
124*0316f99cSCristian Marussi static int
125*0316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
126*0316f99cSCristian Marussi 			     struct powercap_info *pi)
127*0316f99cSCristian Marussi {
128*0316f99cSCristian Marussi 	int ret;
129*0316f99cSCristian Marussi 	struct scmi_xfer *t;
130*0316f99cSCristian Marussi 
131*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
132*0316f99cSCristian Marussi 				      sizeof(u32), &t);
133*0316f99cSCristian Marussi 	if (ret)
134*0316f99cSCristian Marussi 		return ret;
135*0316f99cSCristian Marussi 
136*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
137*0316f99cSCristian Marussi 	if (!ret) {
138*0316f99cSCristian Marussi 		u32 attributes;
139*0316f99cSCristian Marussi 
140*0316f99cSCristian Marussi 		attributes = get_unaligned_le32(t->rx.buf);
141*0316f99cSCristian Marussi 		pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
142*0316f99cSCristian Marussi 	}
143*0316f99cSCristian Marussi 
144*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
145*0316f99cSCristian Marussi 	return ret;
146*0316f99cSCristian Marussi }
147*0316f99cSCristian Marussi 
148*0316f99cSCristian Marussi static inline int
149*0316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
150*0316f99cSCristian Marussi 		       unsigned int step_val, bool configurable)
151*0316f99cSCristian Marussi {
152*0316f99cSCristian Marussi 	if (!min_val || !max_val)
153*0316f99cSCristian Marussi 		return -EPROTO;
154*0316f99cSCristian Marussi 
155*0316f99cSCristian Marussi 	if ((configurable && min_val == max_val) ||
156*0316f99cSCristian Marussi 	    (!configurable && min_val != max_val))
157*0316f99cSCristian Marussi 		return -EPROTO;
158*0316f99cSCristian Marussi 
159*0316f99cSCristian Marussi 	if (min_val != max_val && !step_val)
160*0316f99cSCristian Marussi 		return -EPROTO;
161*0316f99cSCristian Marussi 
162*0316f99cSCristian Marussi 	return 0;
163*0316f99cSCristian Marussi }
164*0316f99cSCristian Marussi 
165*0316f99cSCristian Marussi static int
166*0316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
167*0316f99cSCristian Marussi 				    struct powercap_info *pinfo, u32 domain)
168*0316f99cSCristian Marussi {
169*0316f99cSCristian Marussi 	int ret;
170*0316f99cSCristian Marussi 	u32 flags;
171*0316f99cSCristian Marussi 	struct scmi_xfer *t;
172*0316f99cSCristian Marussi 	struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
173*0316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_domain_attributes *resp;
174*0316f99cSCristian Marussi 
175*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
176*0316f99cSCristian Marussi 				      sizeof(domain), sizeof(*resp), &t);
177*0316f99cSCristian Marussi 	if (ret)
178*0316f99cSCristian Marussi 		return ret;
179*0316f99cSCristian Marussi 
180*0316f99cSCristian Marussi 	put_unaligned_le32(domain, t->tx.buf);
181*0316f99cSCristian Marussi 	resp = t->rx.buf;
182*0316f99cSCristian Marussi 
183*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
184*0316f99cSCristian Marussi 	if (!ret) {
185*0316f99cSCristian Marussi 		flags = le32_to_cpu(resp->attributes);
186*0316f99cSCristian Marussi 
187*0316f99cSCristian Marussi 		dom_info->id = domain;
188*0316f99cSCristian Marussi 		dom_info->notify_powercap_cap_change =
189*0316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
190*0316f99cSCristian Marussi 		dom_info->notify_powercap_measurement_change =
191*0316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
192*0316f99cSCristian Marussi 		dom_info->async_powercap_cap_set =
193*0316f99cSCristian Marussi 			SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
194*0316f99cSCristian Marussi 		dom_info->powercap_cap_config =
195*0316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
196*0316f99cSCristian Marussi 		dom_info->powercap_monitoring =
197*0316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MONITORING(flags);
198*0316f99cSCristian Marussi 		dom_info->powercap_pai_config =
199*0316f99cSCristian Marussi 			SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
200*0316f99cSCristian Marussi 		dom_info->powercap_scale_mw =
201*0316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_MW(flags);
202*0316f99cSCristian Marussi 		dom_info->powercap_scale_uw =
203*0316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_UW(flags);
204*0316f99cSCristian Marussi 
205*0316f99cSCristian Marussi 		strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
206*0316f99cSCristian Marussi 
207*0316f99cSCristian Marussi 		dom_info->min_pai = le32_to_cpu(resp->min_pai);
208*0316f99cSCristian Marussi 		dom_info->max_pai = le32_to_cpu(resp->max_pai);
209*0316f99cSCristian Marussi 		dom_info->pai_step = le32_to_cpu(resp->pai_step);
210*0316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_pai,
211*0316f99cSCristian Marussi 					     dom_info->max_pai,
212*0316f99cSCristian Marussi 					     dom_info->pai_step,
213*0316f99cSCristian Marussi 					     dom_info->powercap_pai_config);
214*0316f99cSCristian Marussi 		if (ret) {
215*0316f99cSCristian Marussi 			dev_err(ph->dev,
216*0316f99cSCristian Marussi 				"Platform reported inconsistent PAI config for domain %d - %s\n",
217*0316f99cSCristian Marussi 				dom_info->id, dom_info->name);
218*0316f99cSCristian Marussi 			goto clean;
219*0316f99cSCristian Marussi 		}
220*0316f99cSCristian Marussi 
221*0316f99cSCristian Marussi 		dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
222*0316f99cSCristian Marussi 		dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
223*0316f99cSCristian Marussi 		dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
224*0316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_power_cap,
225*0316f99cSCristian Marussi 					     dom_info->max_power_cap,
226*0316f99cSCristian Marussi 					     dom_info->power_cap_step,
227*0316f99cSCristian Marussi 					     dom_info->powercap_cap_config);
228*0316f99cSCristian Marussi 		if (ret) {
229*0316f99cSCristian Marussi 			dev_err(ph->dev,
230*0316f99cSCristian Marussi 				"Platform reported inconsistent CAP config for domain %d - %s\n",
231*0316f99cSCristian Marussi 				dom_info->id, dom_info->name);
232*0316f99cSCristian Marussi 			goto clean;
233*0316f99cSCristian Marussi 		}
234*0316f99cSCristian Marussi 
235*0316f99cSCristian Marussi 		dom_info->sustainable_power =
236*0316f99cSCristian Marussi 			le32_to_cpu(resp->sustainable_power);
237*0316f99cSCristian Marussi 		dom_info->accuracy = le32_to_cpu(resp->accuracy);
238*0316f99cSCristian Marussi 
239*0316f99cSCristian Marussi 		dom_info->parent_id = le32_to_cpu(resp->parent_id);
240*0316f99cSCristian Marussi 		if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
241*0316f99cSCristian Marussi 		    (dom_info->parent_id >= pinfo->num_domains ||
242*0316f99cSCristian Marussi 		     dom_info->parent_id == dom_info->id)) {
243*0316f99cSCristian Marussi 			dev_err(ph->dev,
244*0316f99cSCristian Marussi 				"Platform reported inconsistent parent ID for domain %d - %s\n",
245*0316f99cSCristian Marussi 				dom_info->id, dom_info->name);
246*0316f99cSCristian Marussi 			ret = -ENODEV;
247*0316f99cSCristian Marussi 		}
248*0316f99cSCristian Marussi 	}
249*0316f99cSCristian Marussi 
250*0316f99cSCristian Marussi clean:
251*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
252*0316f99cSCristian Marussi 
253*0316f99cSCristian Marussi 	/*
254*0316f99cSCristian Marussi 	 * If supported overwrite short name with the extended one;
255*0316f99cSCristian Marussi 	 * on error just carry on and use already provided short name.
256*0316f99cSCristian Marussi 	 */
257*0316f99cSCristian Marussi 	if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
258*0316f99cSCristian Marussi 		ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
259*0316f99cSCristian Marussi 					    domain, dom_info->name,
260*0316f99cSCristian Marussi 					    SCMI_MAX_STR_SIZE);
261*0316f99cSCristian Marussi 
262*0316f99cSCristian Marussi 	return ret;
263*0316f99cSCristian Marussi }
264*0316f99cSCristian Marussi 
265*0316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
266*0316f99cSCristian Marussi {
267*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
268*0316f99cSCristian Marussi 
269*0316f99cSCristian Marussi 	return pi->num_domains;
270*0316f99cSCristian Marussi }
271*0316f99cSCristian Marussi 
272*0316f99cSCristian Marussi static const struct scmi_powercap_info *
273*0316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
274*0316f99cSCristian Marussi {
275*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
276*0316f99cSCristian Marussi 
277*0316f99cSCristian Marussi 	if (domain_id >= pi->num_domains)
278*0316f99cSCristian Marussi 		return NULL;
279*0316f99cSCristian Marussi 
280*0316f99cSCristian Marussi 	return pi->powercaps + domain_id;
281*0316f99cSCristian Marussi }
282*0316f99cSCristian Marussi 
283*0316f99cSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
284*0316f99cSCristian Marussi 				 u32 domain_id, u32 *power_cap)
285*0316f99cSCristian Marussi {
286*0316f99cSCristian Marussi 	int ret;
287*0316f99cSCristian Marussi 	struct scmi_xfer *t;
288*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
289*0316f99cSCristian Marussi 
290*0316f99cSCristian Marussi 	if (!power_cap || domain_id >= pi->num_domains)
291*0316f99cSCristian Marussi 		return -EINVAL;
292*0316f99cSCristian Marussi 
293*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
294*0316f99cSCristian Marussi 				      sizeof(u32), &t);
295*0316f99cSCristian Marussi 	if (ret)
296*0316f99cSCristian Marussi 		return ret;
297*0316f99cSCristian Marussi 
298*0316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
299*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
300*0316f99cSCristian Marussi 	if (!ret)
301*0316f99cSCristian Marussi 		*power_cap = get_unaligned_le32(t->rx.buf);
302*0316f99cSCristian Marussi 
303*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
304*0316f99cSCristian Marussi 
305*0316f99cSCristian Marussi 	return ret;
306*0316f99cSCristian Marussi }
307*0316f99cSCristian Marussi 
308*0316f99cSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
309*0316f99cSCristian Marussi 				 u32 domain_id, u32 power_cap,
310*0316f99cSCristian Marussi 				 bool ignore_dresp)
311*0316f99cSCristian Marussi {
312*0316f99cSCristian Marussi 	int ret;
313*0316f99cSCristian Marussi 	struct scmi_xfer *t;
314*0316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
315*0316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
316*0316f99cSCristian Marussi 
317*0316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
318*0316f99cSCristian Marussi 	if (!pc || !pc->powercap_cap_config || !power_cap ||
319*0316f99cSCristian Marussi 	    power_cap < pc->min_power_cap ||
320*0316f99cSCristian Marussi 	    power_cap > pc->max_power_cap)
321*0316f99cSCristian Marussi 		return -EINVAL;
322*0316f99cSCristian Marussi 
323*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
324*0316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
325*0316f99cSCristian Marussi 	if (ret)
326*0316f99cSCristian Marussi 		return ret;
327*0316f99cSCristian Marussi 
328*0316f99cSCristian Marussi 	msg = t->tx.buf;
329*0316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
330*0316f99cSCristian Marussi 	msg->flags =
331*0316f99cSCristian Marussi 		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
332*0316f99cSCristian Marussi 			    FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
333*0316f99cSCristian Marussi 	msg->value = cpu_to_le32(power_cap);
334*0316f99cSCristian Marussi 
335*0316f99cSCristian Marussi 	if (!pc->async_powercap_cap_set || ignore_dresp) {
336*0316f99cSCristian Marussi 		ret = ph->xops->do_xfer(ph, t);
337*0316f99cSCristian Marussi 	} else {
338*0316f99cSCristian Marussi 		ret = ph->xops->do_xfer_with_response(ph, t);
339*0316f99cSCristian Marussi 		if (!ret) {
340*0316f99cSCristian Marussi 			struct scmi_msg_resp_powercap_cap_set_complete *resp;
341*0316f99cSCristian Marussi 
342*0316f99cSCristian Marussi 			resp = t->rx.buf;
343*0316f99cSCristian Marussi 			if (le32_to_cpu(resp->domain) == domain_id)
344*0316f99cSCristian Marussi 				dev_dbg(ph->dev,
345*0316f99cSCristian Marussi 					"Powercap ID %d CAP set async to %u\n",
346*0316f99cSCristian Marussi 					domain_id,
347*0316f99cSCristian Marussi 					get_unaligned_le32(&resp->power_cap));
348*0316f99cSCristian Marussi 			else
349*0316f99cSCristian Marussi 				ret = -EPROTO;
350*0316f99cSCristian Marussi 		}
351*0316f99cSCristian Marussi 	}
352*0316f99cSCristian Marussi 
353*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
354*0316f99cSCristian Marussi 	return ret;
355*0316f99cSCristian Marussi }
356*0316f99cSCristian Marussi 
357*0316f99cSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
358*0316f99cSCristian Marussi 				 u32 domain_id, u32 *pai)
359*0316f99cSCristian Marussi {
360*0316f99cSCristian Marussi 	int ret;
361*0316f99cSCristian Marussi 	struct scmi_xfer *t;
362*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
363*0316f99cSCristian Marussi 
364*0316f99cSCristian Marussi 	if (!pai || domain_id >= pi->num_domains)
365*0316f99cSCristian Marussi 		return -EINVAL;
366*0316f99cSCristian Marussi 
367*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
368*0316f99cSCristian Marussi 				      sizeof(u32), &t);
369*0316f99cSCristian Marussi 	if (ret)
370*0316f99cSCristian Marussi 		return ret;
371*0316f99cSCristian Marussi 
372*0316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
373*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
374*0316f99cSCristian Marussi 	if (!ret)
375*0316f99cSCristian Marussi 		*pai = get_unaligned_le32(t->rx.buf);
376*0316f99cSCristian Marussi 
377*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
378*0316f99cSCristian Marussi 
379*0316f99cSCristian Marussi 	return ret;
380*0316f99cSCristian Marussi }
381*0316f99cSCristian Marussi 
382*0316f99cSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
383*0316f99cSCristian Marussi 				 u32 domain_id, u32 pai)
384*0316f99cSCristian Marussi {
385*0316f99cSCristian Marussi 	int ret;
386*0316f99cSCristian Marussi 	struct scmi_xfer *t;
387*0316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
388*0316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
389*0316f99cSCristian Marussi 
390*0316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
391*0316f99cSCristian Marussi 	if (!pc || !pc->powercap_pai_config || !pai ||
392*0316f99cSCristian Marussi 	    pai < pc->min_pai || pai > pc->max_pai)
393*0316f99cSCristian Marussi 		return -EINVAL;
394*0316f99cSCristian Marussi 
395*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
396*0316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
397*0316f99cSCristian Marussi 	if (ret)
398*0316f99cSCristian Marussi 		return ret;
399*0316f99cSCristian Marussi 
400*0316f99cSCristian Marussi 	msg = t->tx.buf;
401*0316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
402*0316f99cSCristian Marussi 	msg->flags = cpu_to_le32(0);
403*0316f99cSCristian Marussi 	msg->value = cpu_to_le32(pai);
404*0316f99cSCristian Marussi 
405*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
406*0316f99cSCristian Marussi 
407*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
408*0316f99cSCristian Marussi 	return ret;
409*0316f99cSCristian Marussi }
410*0316f99cSCristian Marussi 
411*0316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
412*0316f99cSCristian Marussi 					  u32 domain_id, u32 *average_power,
413*0316f99cSCristian Marussi 					  u32 *pai)
414*0316f99cSCristian Marussi {
415*0316f99cSCristian Marussi 	int ret;
416*0316f99cSCristian Marussi 	struct scmi_xfer *t;
417*0316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_meas_get *resp;
418*0316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
419*0316f99cSCristian Marussi 
420*0316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
421*0316f99cSCristian Marussi 	if (!pc || !pc->powercap_monitoring || !pai || !average_power)
422*0316f99cSCristian Marussi 		return -EINVAL;
423*0316f99cSCristian Marussi 
424*0316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
425*0316f99cSCristian Marussi 				      sizeof(u32), sizeof(*resp), &t);
426*0316f99cSCristian Marussi 	if (ret)
427*0316f99cSCristian Marussi 		return ret;
428*0316f99cSCristian Marussi 
429*0316f99cSCristian Marussi 	resp = t->rx.buf;
430*0316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
431*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
432*0316f99cSCristian Marussi 	if (!ret) {
433*0316f99cSCristian Marussi 		*average_power = le32_to_cpu(resp->power);
434*0316f99cSCristian Marussi 		*pai = le32_to_cpu(resp->pai);
435*0316f99cSCristian Marussi 	}
436*0316f99cSCristian Marussi 
437*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
438*0316f99cSCristian Marussi 	return ret;
439*0316f99cSCristian Marussi }
440*0316f99cSCristian Marussi 
441*0316f99cSCristian Marussi static int
442*0316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
443*0316f99cSCristian Marussi 					 u32 domain_id, u32 *power_thresh_low,
444*0316f99cSCristian Marussi 					 u32 *power_thresh_high)
445*0316f99cSCristian Marussi {
446*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
447*0316f99cSCristian Marussi 
448*0316f99cSCristian Marussi 	if (!power_thresh_low || !power_thresh_high ||
449*0316f99cSCristian Marussi 	    domain_id >= pi->num_domains)
450*0316f99cSCristian Marussi 		return -EINVAL;
451*0316f99cSCristian Marussi 
452*0316f99cSCristian Marussi 	*power_thresh_low =  THRESH_LOW(pi, domain_id);
453*0316f99cSCristian Marussi 	*power_thresh_high = THRESH_HIGH(pi, domain_id);
454*0316f99cSCristian Marussi 
455*0316f99cSCristian Marussi 	return 0;
456*0316f99cSCristian Marussi }
457*0316f99cSCristian Marussi 
458*0316f99cSCristian Marussi static int
459*0316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
460*0316f99cSCristian Marussi 					 u32 domain_id, u32 power_thresh_low,
461*0316f99cSCristian Marussi 					 u32 power_thresh_high)
462*0316f99cSCristian Marussi {
463*0316f99cSCristian Marussi 	int ret = 0;
464*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
465*0316f99cSCristian Marussi 
466*0316f99cSCristian Marussi 	if (domain_id >= pi->num_domains ||
467*0316f99cSCristian Marussi 	    power_thresh_low > power_thresh_high)
468*0316f99cSCristian Marussi 		return -EINVAL;
469*0316f99cSCristian Marussi 
470*0316f99cSCristian Marussi 	/* Anything to do ? */
471*0316f99cSCristian Marussi 	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
472*0316f99cSCristian Marussi 	    THRESH_HIGH(pi, domain_id) == power_thresh_high)
473*0316f99cSCristian Marussi 		return ret;
474*0316f99cSCristian Marussi 
475*0316f99cSCristian Marussi 	pi->states[domain_id].thresholds =
476*0316f99cSCristian Marussi 		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
477*0316f99cSCristian Marussi 		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
478*0316f99cSCristian Marussi 
479*0316f99cSCristian Marussi 	/* Update thresholds if notification already enabled */
480*0316f99cSCristian Marussi 	if (pi->states[domain_id].meas_notif_enabled)
481*0316f99cSCristian Marussi 		ret = scmi_powercap_notify(ph, domain_id,
482*0316f99cSCristian Marussi 					   POWERCAP_MEASUREMENTS_NOTIFY,
483*0316f99cSCristian Marussi 					   true);
484*0316f99cSCristian Marussi 
485*0316f99cSCristian Marussi 	return ret;
486*0316f99cSCristian Marussi }
487*0316f99cSCristian Marussi 
488*0316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
489*0316f99cSCristian Marussi 	.num_domains_get = scmi_powercap_num_domains_get,
490*0316f99cSCristian Marussi 	.info_get = scmi_powercap_dom_info_get,
491*0316f99cSCristian Marussi 	.cap_get = scmi_powercap_cap_get,
492*0316f99cSCristian Marussi 	.cap_set = scmi_powercap_cap_set,
493*0316f99cSCristian Marussi 	.pai_get = scmi_powercap_pai_get,
494*0316f99cSCristian Marussi 	.pai_set = scmi_powercap_pai_set,
495*0316f99cSCristian Marussi 	.measurements_get = scmi_powercap_measurements_get,
496*0316f99cSCristian Marussi 	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
497*0316f99cSCristian Marussi 	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
498*0316f99cSCristian Marussi };
499*0316f99cSCristian Marussi 
500*0316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
501*0316f99cSCristian Marussi 				u32 domain, int message_id, bool enable)
502*0316f99cSCristian Marussi {
503*0316f99cSCristian Marussi 	int ret;
504*0316f99cSCristian Marussi 	struct scmi_xfer *t;
505*0316f99cSCristian Marussi 
506*0316f99cSCristian Marussi 	switch (message_id) {
507*0316f99cSCristian Marussi 	case POWERCAP_CAP_NOTIFY:
508*0316f99cSCristian Marussi 	{
509*0316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_cap *notify;
510*0316f99cSCristian Marussi 
511*0316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
512*0316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
513*0316f99cSCristian Marussi 		if (ret)
514*0316f99cSCristian Marussi 			return ret;
515*0316f99cSCristian Marussi 
516*0316f99cSCristian Marussi 		notify = t->tx.buf;
517*0316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
518*0316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
519*0316f99cSCristian Marussi 		break;
520*0316f99cSCristian Marussi 	}
521*0316f99cSCristian Marussi 	case POWERCAP_MEASUREMENTS_NOTIFY:
522*0316f99cSCristian Marussi 	{
523*0316f99cSCristian Marussi 		u32 low, high;
524*0316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_thresh *notify;
525*0316f99cSCristian Marussi 
526*0316f99cSCristian Marussi 		/*
527*0316f99cSCristian Marussi 		 * Note that we have to pick the most recently configured
528*0316f99cSCristian Marussi 		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
529*0316f99cSCristian Marussi 		 * enable request and we fail, complaining, if no thresholds
530*0316f99cSCristian Marussi 		 * were ever set, since this is an indication the API has been
531*0316f99cSCristian Marussi 		 * used wrongly.
532*0316f99cSCristian Marussi 		 */
533*0316f99cSCristian Marussi 		ret = scmi_powercap_measurements_threshold_get(ph, domain,
534*0316f99cSCristian Marussi 							       &low, &high);
535*0316f99cSCristian Marussi 		if (ret)
536*0316f99cSCristian Marussi 			return ret;
537*0316f99cSCristian Marussi 
538*0316f99cSCristian Marussi 		if (enable && !low && !high) {
539*0316f99cSCristian Marussi 			dev_err(ph->dev,
540*0316f99cSCristian Marussi 				"Invalid Measurements Notify thresholds: %u/%u\n",
541*0316f99cSCristian Marussi 				low, high);
542*0316f99cSCristian Marussi 			return -EINVAL;
543*0316f99cSCristian Marussi 		}
544*0316f99cSCristian Marussi 
545*0316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
546*0316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
547*0316f99cSCristian Marussi 		if (ret)
548*0316f99cSCristian Marussi 			return ret;
549*0316f99cSCristian Marussi 
550*0316f99cSCristian Marussi 		notify = t->tx.buf;
551*0316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
552*0316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
553*0316f99cSCristian Marussi 		notify->power_thresh_low = cpu_to_le32(low);
554*0316f99cSCristian Marussi 		notify->power_thresh_high = cpu_to_le32(high);
555*0316f99cSCristian Marussi 		break;
556*0316f99cSCristian Marussi 	}
557*0316f99cSCristian Marussi 	default:
558*0316f99cSCristian Marussi 		return -EINVAL;
559*0316f99cSCristian Marussi 	}
560*0316f99cSCristian Marussi 
561*0316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
562*0316f99cSCristian Marussi 
563*0316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
564*0316f99cSCristian Marussi 	return ret;
565*0316f99cSCristian Marussi }
566*0316f99cSCristian Marussi 
567*0316f99cSCristian Marussi static int
568*0316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
569*0316f99cSCristian Marussi 				 u8 evt_id, u32 src_id, bool enable)
570*0316f99cSCristian Marussi {
571*0316f99cSCristian Marussi 	int ret, cmd_id;
572*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
573*0316f99cSCristian Marussi 
574*0316f99cSCristian Marussi 	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
575*0316f99cSCristian Marussi 		return -EINVAL;
576*0316f99cSCristian Marussi 
577*0316f99cSCristian Marussi 	cmd_id = evt_2_cmd[evt_id];
578*0316f99cSCristian Marussi 	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
579*0316f99cSCristian Marussi 	if (ret)
580*0316f99cSCristian Marussi 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
581*0316f99cSCristian Marussi 			 evt_id, src_id, ret);
582*0316f99cSCristian Marussi 	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
583*0316f99cSCristian Marussi 		/*
584*0316f99cSCristian Marussi 		 * On success save the current notification enabled state, so
585*0316f99cSCristian Marussi 		 * as to be able to properly update the notification thresholds
586*0316f99cSCristian Marussi 		 * when they are modified on a domain for which measurement
587*0316f99cSCristian Marussi 		 * notifications were currently enabled.
588*0316f99cSCristian Marussi 		 *
589*0316f99cSCristian Marussi 		 * This is needed because the SCMI Notification core machinery
590*0316f99cSCristian Marussi 		 * and API does not support passing per-notification custom
591*0316f99cSCristian Marussi 		 * arguments at callback registration time.
592*0316f99cSCristian Marussi 		 *
593*0316f99cSCristian Marussi 		 * Note that this can be done here with a simple flag since the
594*0316f99cSCristian Marussi 		 * SCMI core Notifications code takes care of keeping proper
595*0316f99cSCristian Marussi 		 * per-domain enables refcounting, so that this helper function
596*0316f99cSCristian Marussi 		 * will be called only once (for enables) when the first user
597*0316f99cSCristian Marussi 		 * registers a callback on this domain and once more (disable)
598*0316f99cSCristian Marussi 		 * when the last user de-registers its callback.
599*0316f99cSCristian Marussi 		 */
600*0316f99cSCristian Marussi 		pi->states[src_id].meas_notif_enabled = enable;
601*0316f99cSCristian Marussi 
602*0316f99cSCristian Marussi 	return ret;
603*0316f99cSCristian Marussi }
604*0316f99cSCristian Marussi 
605*0316f99cSCristian Marussi static void *
606*0316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
607*0316f99cSCristian Marussi 				 u8 evt_id, ktime_t timestamp,
608*0316f99cSCristian Marussi 				 const void *payld, size_t payld_sz,
609*0316f99cSCristian Marussi 				 void *report, u32 *src_id)
610*0316f99cSCristian Marussi {
611*0316f99cSCristian Marussi 	void *rep = NULL;
612*0316f99cSCristian Marussi 
613*0316f99cSCristian Marussi 	switch (evt_id) {
614*0316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_CAP_CHANGED:
615*0316f99cSCristian Marussi 	{
616*0316f99cSCristian Marussi 		const struct scmi_powercap_cap_changed_notify_payld *p = payld;
617*0316f99cSCristian Marussi 		struct scmi_powercap_cap_changed_report *r = report;
618*0316f99cSCristian Marussi 
619*0316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
620*0316f99cSCristian Marussi 			break;
621*0316f99cSCristian Marussi 
622*0316f99cSCristian Marussi 		r->timestamp = timestamp;
623*0316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
624*0316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
625*0316f99cSCristian Marussi 		r->power_cap = le32_to_cpu(p->power_cap);
626*0316f99cSCristian Marussi 		r->pai = le32_to_cpu(p->pai);
627*0316f99cSCristian Marussi 		*src_id = r->domain_id;
628*0316f99cSCristian Marussi 		rep = r;
629*0316f99cSCristian Marussi 		break;
630*0316f99cSCristian Marussi 	}
631*0316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
632*0316f99cSCristian Marussi 	{
633*0316f99cSCristian Marussi 		const struct scmi_powercap_meas_changed_notify_payld *p = payld;
634*0316f99cSCristian Marussi 		struct scmi_powercap_meas_changed_report *r = report;
635*0316f99cSCristian Marussi 
636*0316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
637*0316f99cSCristian Marussi 			break;
638*0316f99cSCristian Marussi 
639*0316f99cSCristian Marussi 		r->timestamp = timestamp;
640*0316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
641*0316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
642*0316f99cSCristian Marussi 		r->power = le32_to_cpu(p->power);
643*0316f99cSCristian Marussi 		*src_id = r->domain_id;
644*0316f99cSCristian Marussi 		rep = r;
645*0316f99cSCristian Marussi 		break;
646*0316f99cSCristian Marussi 	}
647*0316f99cSCristian Marussi 	default:
648*0316f99cSCristian Marussi 		break;
649*0316f99cSCristian Marussi 	}
650*0316f99cSCristian Marussi 
651*0316f99cSCristian Marussi 	return rep;
652*0316f99cSCristian Marussi }
653*0316f99cSCristian Marussi 
654*0316f99cSCristian Marussi static int
655*0316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
656*0316f99cSCristian Marussi {
657*0316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
658*0316f99cSCristian Marussi 
659*0316f99cSCristian Marussi 	if (!pi)
660*0316f99cSCristian Marussi 		return -EINVAL;
661*0316f99cSCristian Marussi 
662*0316f99cSCristian Marussi 	return pi->num_domains;
663*0316f99cSCristian Marussi }
664*0316f99cSCristian Marussi 
665*0316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
666*0316f99cSCristian Marussi 	{
667*0316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
668*0316f99cSCristian Marussi 		.max_payld_sz =
669*0316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_notify_payld),
670*0316f99cSCristian Marussi 		.max_report_sz =
671*0316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_report),
672*0316f99cSCristian Marussi 	},
673*0316f99cSCristian Marussi 	{
674*0316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
675*0316f99cSCristian Marussi 		.max_payld_sz =
676*0316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_notify_payld),
677*0316f99cSCristian Marussi 		.max_report_sz =
678*0316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_report),
679*0316f99cSCristian Marussi 	},
680*0316f99cSCristian Marussi };
681*0316f99cSCristian Marussi 
682*0316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
683*0316f99cSCristian Marussi 	.get_num_sources = scmi_powercap_get_num_sources,
684*0316f99cSCristian Marussi 	.set_notify_enabled = scmi_powercap_set_notify_enabled,
685*0316f99cSCristian Marussi 	.fill_custom_report = scmi_powercap_fill_custom_report,
686*0316f99cSCristian Marussi };
687*0316f99cSCristian Marussi 
688*0316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
689*0316f99cSCristian Marussi 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
690*0316f99cSCristian Marussi 	.ops = &powercap_event_ops,
691*0316f99cSCristian Marussi 	.evts = powercap_events,
692*0316f99cSCristian Marussi 	.num_events = ARRAY_SIZE(powercap_events),
693*0316f99cSCristian Marussi };
694*0316f99cSCristian Marussi 
695*0316f99cSCristian Marussi static int
696*0316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
697*0316f99cSCristian Marussi {
698*0316f99cSCristian Marussi 	int domain, ret;
699*0316f99cSCristian Marussi 	u32 version;
700*0316f99cSCristian Marussi 	struct powercap_info *pinfo;
701*0316f99cSCristian Marussi 
702*0316f99cSCristian Marussi 	ret = ph->xops->version_get(ph, &version);
703*0316f99cSCristian Marussi 	if (ret)
704*0316f99cSCristian Marussi 		return ret;
705*0316f99cSCristian Marussi 
706*0316f99cSCristian Marussi 	dev_dbg(ph->dev, "Powercap Version %d.%d\n",
707*0316f99cSCristian Marussi 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
708*0316f99cSCristian Marussi 
709*0316f99cSCristian Marussi 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
710*0316f99cSCristian Marussi 	if (!pinfo)
711*0316f99cSCristian Marussi 		return -ENOMEM;
712*0316f99cSCristian Marussi 
713*0316f99cSCristian Marussi 	ret = scmi_powercap_attributes_get(ph, pinfo);
714*0316f99cSCristian Marussi 	if (ret)
715*0316f99cSCristian Marussi 		return ret;
716*0316f99cSCristian Marussi 
717*0316f99cSCristian Marussi 	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
718*0316f99cSCristian Marussi 					sizeof(*pinfo->powercaps),
719*0316f99cSCristian Marussi 					GFP_KERNEL);
720*0316f99cSCristian Marussi 	if (!pinfo->powercaps)
721*0316f99cSCristian Marussi 		return -ENOMEM;
722*0316f99cSCristian Marussi 
723*0316f99cSCristian Marussi 	/*
724*0316f99cSCristian Marussi 	 * Note that any failure in retrieving any domain attribute leads to
725*0316f99cSCristian Marussi 	 * the whole Powercap protocol initialization failure: this way the
726*0316f99cSCristian Marussi 	 * reported Powercap domains are all assured, when accessed, to be well
727*0316f99cSCristian Marussi 	 * formed and correlated by sane parent-child relationship (if any).
728*0316f99cSCristian Marussi 	 */
729*0316f99cSCristian Marussi 	for (domain = 0; domain < pinfo->num_domains; domain++) {
730*0316f99cSCristian Marussi 		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
731*0316f99cSCristian Marussi 		if (ret)
732*0316f99cSCristian Marussi 			return ret;
733*0316f99cSCristian Marussi 	}
734*0316f99cSCristian Marussi 
735*0316f99cSCristian Marussi 	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
736*0316f99cSCristian Marussi 				     sizeof(*pinfo->states), GFP_KERNEL);
737*0316f99cSCristian Marussi 	if (!pinfo->states)
738*0316f99cSCristian Marussi 		return -ENOMEM;
739*0316f99cSCristian Marussi 
740*0316f99cSCristian Marussi 	pinfo->version = version;
741*0316f99cSCristian Marussi 
742*0316f99cSCristian Marussi 	return ph->set_priv(ph, pinfo);
743*0316f99cSCristian Marussi }
744*0316f99cSCristian Marussi 
745*0316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
746*0316f99cSCristian Marussi 	.id = SCMI_PROTOCOL_POWERCAP,
747*0316f99cSCristian Marussi 	.owner = THIS_MODULE,
748*0316f99cSCristian Marussi 	.instance_init = &scmi_powercap_protocol_init,
749*0316f99cSCristian Marussi 	.ops = &powercap_proto_ops,
750*0316f99cSCristian Marussi 	.events = &powercap_protocol_events,
751*0316f99cSCristian Marussi };
752*0316f99cSCristian Marussi 
753*0316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
754