xref: /openbmc/linux/drivers/firmware/arm_scmi/powercap.c (revision b27d04d5a51c28322cadb18d8d2ff5d0fb892fff)
10316f99cSCristian Marussi // SPDX-License-Identifier: GPL-2.0
20316f99cSCristian Marussi /*
30316f99cSCristian Marussi  * System Control and Management Interface (SCMI) Powercap Protocol
40316f99cSCristian Marussi  *
50316f99cSCristian Marussi  * Copyright (C) 2022 ARM Ltd.
60316f99cSCristian Marussi  */
70316f99cSCristian Marussi 
80316f99cSCristian Marussi #define pr_fmt(fmt) "SCMI Notifications POWERCAP - " fmt
90316f99cSCristian Marussi 
100316f99cSCristian Marussi #include <linux/bitfield.h>
11855aa26eSCristian Marussi #include <linux/io.h>
120316f99cSCristian Marussi #include <linux/module.h>
130316f99cSCristian Marussi #include <linux/scmi_protocol.h>
140316f99cSCristian Marussi 
15*b27d04d5SCristian Marussi #include <trace/events/scmi.h>
16*b27d04d5SCristian Marussi 
170316f99cSCristian Marussi #include "protocols.h"
180316f99cSCristian Marussi #include "notify.h"
190316f99cSCristian Marussi 
200316f99cSCristian Marussi enum scmi_powercap_protocol_cmd {
210316f99cSCristian Marussi 	POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
220316f99cSCristian Marussi 	POWERCAP_CAP_GET = 0x4,
230316f99cSCristian Marussi 	POWERCAP_CAP_SET = 0x5,
240316f99cSCristian Marussi 	POWERCAP_PAI_GET = 0x6,
250316f99cSCristian Marussi 	POWERCAP_PAI_SET = 0x7,
260316f99cSCristian Marussi 	POWERCAP_DOMAIN_NAME_GET = 0x8,
270316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_GET = 0x9,
280316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY = 0xa,
290316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
300316f99cSCristian Marussi 	POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
310316f99cSCristian Marussi };
320316f99cSCristian Marussi 
33855aa26eSCristian Marussi enum {
34855aa26eSCristian Marussi 	POWERCAP_FC_CAP,
35855aa26eSCristian Marussi 	POWERCAP_FC_PAI,
36855aa26eSCristian Marussi 	POWERCAP_FC_MAX,
37855aa26eSCristian Marussi };
38855aa26eSCristian Marussi 
390316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes {
400316f99cSCristian Marussi 	__le32 attributes;
410316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x)		((x) & BIT(31))
420316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x)	((x) & BIT(30))
430316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x)		((x) & BIT(29))
440316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x)			((x) & BIT(28))
450316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x)		((x) & BIT(27))
460316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x)			((x) & BIT(26))
470316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x)		((x) & BIT(25))
48855aa26eSCristian Marussi #define SUPPORTS_POWERCAP_FASTCHANNELS(x)		((x) & BIT(22))
490316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x)				\
500316f99cSCristian Marussi 		(FIELD_GET(GENMASK(24, 23), (x)))
510316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_MW(x)			\
520316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x2)
530316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_UW(x)			\
540316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x1)
550316f99cSCristian Marussi 	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
560316f99cSCristian Marussi 	__le32 min_pai;
570316f99cSCristian Marussi 	__le32 max_pai;
580316f99cSCristian Marussi 	__le32 pai_step;
590316f99cSCristian Marussi 	__le32 min_power_cap;
600316f99cSCristian Marussi 	__le32 max_power_cap;
610316f99cSCristian Marussi 	__le32 power_cap_step;
620316f99cSCristian Marussi 	__le32 sustainable_power;
630316f99cSCristian Marussi 	__le32 accuracy;
640316f99cSCristian Marussi 	__le32 parent_id;
650316f99cSCristian Marussi };
660316f99cSCristian Marussi 
670316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai {
680316f99cSCristian Marussi 	__le32 domain;
690316f99cSCristian Marussi 	__le32 flags;
700316f99cSCristian Marussi #define CAP_SET_ASYNC		BIT(1)
710316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP	BIT(0)
720316f99cSCristian Marussi 	__le32 value;
730316f99cSCristian Marussi };
740316f99cSCristian Marussi 
750316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete {
760316f99cSCristian Marussi 	__le32 domain;
770316f99cSCristian Marussi 	__le32 power_cap;
780316f99cSCristian Marussi };
790316f99cSCristian Marussi 
800316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get {
810316f99cSCristian Marussi 	__le32 power;
820316f99cSCristian Marussi 	__le32 pai;
830316f99cSCristian Marussi };
840316f99cSCristian Marussi 
850316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap {
860316f99cSCristian Marussi 	__le32 domain;
870316f99cSCristian Marussi 	__le32 notify_enable;
880316f99cSCristian Marussi };
890316f99cSCristian Marussi 
900316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh {
910316f99cSCristian Marussi 	__le32 domain;
920316f99cSCristian Marussi 	__le32 notify_enable;
930316f99cSCristian Marussi 	__le32 power_thresh_low;
940316f99cSCristian Marussi 	__le32 power_thresh_high;
950316f99cSCristian Marussi };
960316f99cSCristian Marussi 
970316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld {
980316f99cSCristian Marussi 	__le32 agent_id;
990316f99cSCristian Marussi 	__le32 domain_id;
1000316f99cSCristian Marussi 	__le32 power_cap;
1010316f99cSCristian Marussi 	__le32 pai;
1020316f99cSCristian Marussi };
1030316f99cSCristian Marussi 
1040316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld {
1050316f99cSCristian Marussi 	__le32 agent_id;
1060316f99cSCristian Marussi 	__le32 domain_id;
1070316f99cSCristian Marussi 	__le32 power;
1080316f99cSCristian Marussi };
1090316f99cSCristian Marussi 
1100316f99cSCristian Marussi struct scmi_powercap_state {
1110316f99cSCristian Marussi 	bool meas_notif_enabled;
1120316f99cSCristian Marussi 	u64 thresholds;
1130316f99cSCristian Marussi #define THRESH_LOW(p, id)				\
1140316f99cSCristian Marussi 	(lower_32_bits((p)->states[(id)].thresholds))
1150316f99cSCristian Marussi #define THRESH_HIGH(p, id)				\
1160316f99cSCristian Marussi 	(upper_32_bits((p)->states[(id)].thresholds))
1170316f99cSCristian Marussi };
1180316f99cSCristian Marussi 
1190316f99cSCristian Marussi struct powercap_info {
1200316f99cSCristian Marussi 	u32 version;
1210316f99cSCristian Marussi 	int num_domains;
1220316f99cSCristian Marussi 	struct scmi_powercap_state *states;
1230316f99cSCristian Marussi 	struct scmi_powercap_info *powercaps;
1240316f99cSCristian Marussi };
1250316f99cSCristian Marussi 
1260316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
1270316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY,
1280316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY,
1290316f99cSCristian Marussi };
1300316f99cSCristian Marussi 
1310316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
1320316f99cSCristian Marussi 				u32 domain, int message_id, bool enable);
1330316f99cSCristian Marussi 
1340316f99cSCristian Marussi static int
1350316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
1360316f99cSCristian Marussi 			     struct powercap_info *pi)
1370316f99cSCristian Marussi {
1380316f99cSCristian Marussi 	int ret;
1390316f99cSCristian Marussi 	struct scmi_xfer *t;
1400316f99cSCristian Marussi 
1410316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
1420316f99cSCristian Marussi 				      sizeof(u32), &t);
1430316f99cSCristian Marussi 	if (ret)
1440316f99cSCristian Marussi 		return ret;
1450316f99cSCristian Marussi 
1460316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1470316f99cSCristian Marussi 	if (!ret) {
1480316f99cSCristian Marussi 		u32 attributes;
1490316f99cSCristian Marussi 
1500316f99cSCristian Marussi 		attributes = get_unaligned_le32(t->rx.buf);
1510316f99cSCristian Marussi 		pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
1520316f99cSCristian Marussi 	}
1530316f99cSCristian Marussi 
1540316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
1550316f99cSCristian Marussi 	return ret;
1560316f99cSCristian Marussi }
1570316f99cSCristian Marussi 
1580316f99cSCristian Marussi static inline int
1590316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
1600316f99cSCristian Marussi 		       unsigned int step_val, bool configurable)
1610316f99cSCristian Marussi {
1620316f99cSCristian Marussi 	if (!min_val || !max_val)
1630316f99cSCristian Marussi 		return -EPROTO;
1640316f99cSCristian Marussi 
1650316f99cSCristian Marussi 	if ((configurable && min_val == max_val) ||
1660316f99cSCristian Marussi 	    (!configurable && min_val != max_val))
1670316f99cSCristian Marussi 		return -EPROTO;
1680316f99cSCristian Marussi 
1690316f99cSCristian Marussi 	if (min_val != max_val && !step_val)
1700316f99cSCristian Marussi 		return -EPROTO;
1710316f99cSCristian Marussi 
1720316f99cSCristian Marussi 	return 0;
1730316f99cSCristian Marussi }
1740316f99cSCristian Marussi 
1750316f99cSCristian Marussi static int
1760316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
1770316f99cSCristian Marussi 				    struct powercap_info *pinfo, u32 domain)
1780316f99cSCristian Marussi {
1790316f99cSCristian Marussi 	int ret;
1800316f99cSCristian Marussi 	u32 flags;
1810316f99cSCristian Marussi 	struct scmi_xfer *t;
1820316f99cSCristian Marussi 	struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
1830316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_domain_attributes *resp;
1840316f99cSCristian Marussi 
1850316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
1860316f99cSCristian Marussi 				      sizeof(domain), sizeof(*resp), &t);
1870316f99cSCristian Marussi 	if (ret)
1880316f99cSCristian Marussi 		return ret;
1890316f99cSCristian Marussi 
1900316f99cSCristian Marussi 	put_unaligned_le32(domain, t->tx.buf);
1910316f99cSCristian Marussi 	resp = t->rx.buf;
1920316f99cSCristian Marussi 
1930316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1940316f99cSCristian Marussi 	if (!ret) {
1950316f99cSCristian Marussi 		flags = le32_to_cpu(resp->attributes);
1960316f99cSCristian Marussi 
1970316f99cSCristian Marussi 		dom_info->id = domain;
1980316f99cSCristian Marussi 		dom_info->notify_powercap_cap_change =
1990316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
2000316f99cSCristian Marussi 		dom_info->notify_powercap_measurement_change =
2010316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
2020316f99cSCristian Marussi 		dom_info->async_powercap_cap_set =
2030316f99cSCristian Marussi 			SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
2040316f99cSCristian Marussi 		dom_info->powercap_cap_config =
2050316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
2060316f99cSCristian Marussi 		dom_info->powercap_monitoring =
2070316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MONITORING(flags);
2080316f99cSCristian Marussi 		dom_info->powercap_pai_config =
2090316f99cSCristian Marussi 			SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
2100316f99cSCristian Marussi 		dom_info->powercap_scale_mw =
2110316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_MW(flags);
2120316f99cSCristian Marussi 		dom_info->powercap_scale_uw =
2130316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_UW(flags);
214855aa26eSCristian Marussi 		dom_info->fastchannels =
215855aa26eSCristian Marussi 			SUPPORTS_POWERCAP_FASTCHANNELS(flags);
2160316f99cSCristian Marussi 
2170316f99cSCristian Marussi 		strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
2180316f99cSCristian Marussi 
2190316f99cSCristian Marussi 		dom_info->min_pai = le32_to_cpu(resp->min_pai);
2200316f99cSCristian Marussi 		dom_info->max_pai = le32_to_cpu(resp->max_pai);
2210316f99cSCristian Marussi 		dom_info->pai_step = le32_to_cpu(resp->pai_step);
2220316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_pai,
2230316f99cSCristian Marussi 					     dom_info->max_pai,
2240316f99cSCristian Marussi 					     dom_info->pai_step,
2250316f99cSCristian Marussi 					     dom_info->powercap_pai_config);
2260316f99cSCristian Marussi 		if (ret) {
2270316f99cSCristian Marussi 			dev_err(ph->dev,
2280316f99cSCristian Marussi 				"Platform reported inconsistent PAI config for domain %d - %s\n",
2290316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2300316f99cSCristian Marussi 			goto clean;
2310316f99cSCristian Marussi 		}
2320316f99cSCristian Marussi 
2330316f99cSCristian Marussi 		dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
2340316f99cSCristian Marussi 		dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
2350316f99cSCristian Marussi 		dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
2360316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_power_cap,
2370316f99cSCristian Marussi 					     dom_info->max_power_cap,
2380316f99cSCristian Marussi 					     dom_info->power_cap_step,
2390316f99cSCristian Marussi 					     dom_info->powercap_cap_config);
2400316f99cSCristian Marussi 		if (ret) {
2410316f99cSCristian Marussi 			dev_err(ph->dev,
2420316f99cSCristian Marussi 				"Platform reported inconsistent CAP config for domain %d - %s\n",
2430316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2440316f99cSCristian Marussi 			goto clean;
2450316f99cSCristian Marussi 		}
2460316f99cSCristian Marussi 
2470316f99cSCristian Marussi 		dom_info->sustainable_power =
2480316f99cSCristian Marussi 			le32_to_cpu(resp->sustainable_power);
2490316f99cSCristian Marussi 		dom_info->accuracy = le32_to_cpu(resp->accuracy);
2500316f99cSCristian Marussi 
2510316f99cSCristian Marussi 		dom_info->parent_id = le32_to_cpu(resp->parent_id);
2520316f99cSCristian Marussi 		if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
2530316f99cSCristian Marussi 		    (dom_info->parent_id >= pinfo->num_domains ||
2540316f99cSCristian Marussi 		     dom_info->parent_id == dom_info->id)) {
2550316f99cSCristian Marussi 			dev_err(ph->dev,
2560316f99cSCristian Marussi 				"Platform reported inconsistent parent ID for domain %d - %s\n",
2570316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2580316f99cSCristian Marussi 			ret = -ENODEV;
2590316f99cSCristian Marussi 		}
2600316f99cSCristian Marussi 	}
2610316f99cSCristian Marussi 
2620316f99cSCristian Marussi clean:
2630316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
2640316f99cSCristian Marussi 
2650316f99cSCristian Marussi 	/*
2660316f99cSCristian Marussi 	 * If supported overwrite short name with the extended one;
2670316f99cSCristian Marussi 	 * on error just carry on and use already provided short name.
2680316f99cSCristian Marussi 	 */
2690316f99cSCristian Marussi 	if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
2700316f99cSCristian Marussi 		ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
2710316f99cSCristian Marussi 					    domain, dom_info->name,
2720316f99cSCristian Marussi 					    SCMI_MAX_STR_SIZE);
2730316f99cSCristian Marussi 
2740316f99cSCristian Marussi 	return ret;
2750316f99cSCristian Marussi }
2760316f99cSCristian Marussi 
2770316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
2780316f99cSCristian Marussi {
2790316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2800316f99cSCristian Marussi 
2810316f99cSCristian Marussi 	return pi->num_domains;
2820316f99cSCristian Marussi }
2830316f99cSCristian Marussi 
2840316f99cSCristian Marussi static const struct scmi_powercap_info *
2850316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
2860316f99cSCristian Marussi {
2870316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2880316f99cSCristian Marussi 
2890316f99cSCristian Marussi 	if (domain_id >= pi->num_domains)
2900316f99cSCristian Marussi 		return NULL;
2910316f99cSCristian Marussi 
2920316f99cSCristian Marussi 	return pi->powercaps + domain_id;
2930316f99cSCristian Marussi }
2940316f99cSCristian Marussi 
295855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
2960316f99cSCristian Marussi 				      u32 domain_id, u32 *power_cap)
2970316f99cSCristian Marussi {
2980316f99cSCristian Marussi 	int ret;
2990316f99cSCristian Marussi 	struct scmi_xfer *t;
3000316f99cSCristian Marussi 
3010316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
3020316f99cSCristian Marussi 				      sizeof(u32), &t);
3030316f99cSCristian Marussi 	if (ret)
3040316f99cSCristian Marussi 		return ret;
3050316f99cSCristian Marussi 
3060316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
3070316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
3080316f99cSCristian Marussi 	if (!ret)
3090316f99cSCristian Marussi 		*power_cap = get_unaligned_le32(t->rx.buf);
3100316f99cSCristian Marussi 
3110316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3120316f99cSCristian Marussi 
3130316f99cSCristian Marussi 	return ret;
3140316f99cSCristian Marussi }
3150316f99cSCristian Marussi 
316855aa26eSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
317855aa26eSCristian Marussi 				 u32 domain_id, u32 *power_cap)
318855aa26eSCristian Marussi {
319855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
320855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
321855aa26eSCristian Marussi 
322855aa26eSCristian Marussi 	if (!power_cap || domain_id >= pi->num_domains)
323855aa26eSCristian Marussi 		return -EINVAL;
324855aa26eSCristian Marussi 
325855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
326855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
327855aa26eSCristian Marussi 		*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
328*b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
329*b27d04d5SCristian Marussi 				   domain_id, *power_cap, 0);
330855aa26eSCristian Marussi 		return 0;
331855aa26eSCristian Marussi 	}
332855aa26eSCristian Marussi 
333855aa26eSCristian Marussi 	return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap);
334855aa26eSCristian Marussi }
335855aa26eSCristian Marussi 
336855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
337855aa26eSCristian Marussi 				      const struct scmi_powercap_info *pc,
338855aa26eSCristian Marussi 				      u32 power_cap, bool ignore_dresp)
3390316f99cSCristian Marussi {
3400316f99cSCristian Marussi 	int ret;
3410316f99cSCristian Marussi 	struct scmi_xfer *t;
3420316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
3430316f99cSCristian Marussi 
3440316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
3450316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
3460316f99cSCristian Marussi 	if (ret)
3470316f99cSCristian Marussi 		return ret;
3480316f99cSCristian Marussi 
3490316f99cSCristian Marussi 	msg = t->tx.buf;
350855aa26eSCristian Marussi 	msg->domain = cpu_to_le32(pc->id);
3510316f99cSCristian Marussi 	msg->flags =
3520316f99cSCristian Marussi 		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
3530316f99cSCristian Marussi 			    FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
3540316f99cSCristian Marussi 	msg->value = cpu_to_le32(power_cap);
3550316f99cSCristian Marussi 
3560316f99cSCristian Marussi 	if (!pc->async_powercap_cap_set || ignore_dresp) {
3570316f99cSCristian Marussi 		ret = ph->xops->do_xfer(ph, t);
3580316f99cSCristian Marussi 	} else {
3590316f99cSCristian Marussi 		ret = ph->xops->do_xfer_with_response(ph, t);
3600316f99cSCristian Marussi 		if (!ret) {
3610316f99cSCristian Marussi 			struct scmi_msg_resp_powercap_cap_set_complete *resp;
3620316f99cSCristian Marussi 
3630316f99cSCristian Marussi 			resp = t->rx.buf;
364855aa26eSCristian Marussi 			if (le32_to_cpu(resp->domain) == pc->id)
3650316f99cSCristian Marussi 				dev_dbg(ph->dev,
3660316f99cSCristian Marussi 					"Powercap ID %d CAP set async to %u\n",
367855aa26eSCristian Marussi 					pc->id,
3680316f99cSCristian Marussi 					get_unaligned_le32(&resp->power_cap));
3690316f99cSCristian Marussi 			else
3700316f99cSCristian Marussi 				ret = -EPROTO;
3710316f99cSCristian Marussi 		}
3720316f99cSCristian Marussi 	}
3730316f99cSCristian Marussi 
3740316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3750316f99cSCristian Marussi 	return ret;
3760316f99cSCristian Marussi }
3770316f99cSCristian Marussi 
378855aa26eSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
379855aa26eSCristian Marussi 				 u32 domain_id, u32 power_cap,
380855aa26eSCristian Marussi 				 bool ignore_dresp)
381855aa26eSCristian Marussi {
382855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
383855aa26eSCristian Marussi 
384855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
385855aa26eSCristian Marussi 	if (!pc || !pc->powercap_cap_config || !power_cap ||
386855aa26eSCristian Marussi 	    power_cap < pc->min_power_cap ||
387855aa26eSCristian Marussi 	    power_cap > pc->max_power_cap)
388855aa26eSCristian Marussi 		return -EINVAL;
389855aa26eSCristian Marussi 
390855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
391855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
392855aa26eSCristian Marussi 
393855aa26eSCristian Marussi 		iowrite32(power_cap, fci->set_addr);
394855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
395*b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
396*b27d04d5SCristian Marussi 				   domain_id, power_cap, 0);
397855aa26eSCristian Marussi 		return 0;
398855aa26eSCristian Marussi 	}
399855aa26eSCristian Marussi 
400855aa26eSCristian Marussi 	return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp);
401855aa26eSCristian Marussi }
402855aa26eSCristian Marussi 
403855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
4040316f99cSCristian Marussi 				      u32 domain_id, u32 *pai)
4050316f99cSCristian Marussi {
4060316f99cSCristian Marussi 	int ret;
4070316f99cSCristian Marussi 	struct scmi_xfer *t;
4080316f99cSCristian Marussi 
4090316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
4100316f99cSCristian Marussi 				      sizeof(u32), &t);
4110316f99cSCristian Marussi 	if (ret)
4120316f99cSCristian Marussi 		return ret;
4130316f99cSCristian Marussi 
4140316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
4150316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4160316f99cSCristian Marussi 	if (!ret)
4170316f99cSCristian Marussi 		*pai = get_unaligned_le32(t->rx.buf);
4180316f99cSCristian Marussi 
4190316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4200316f99cSCristian Marussi 
4210316f99cSCristian Marussi 	return ret;
4220316f99cSCristian Marussi }
4230316f99cSCristian Marussi 
424855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
425855aa26eSCristian Marussi 				 u32 domain_id, u32 *pai)
426855aa26eSCristian Marussi {
427855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
428855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
429855aa26eSCristian Marussi 
430855aa26eSCristian Marussi 	if (!pai || domain_id >= pi->num_domains)
431855aa26eSCristian Marussi 		return -EINVAL;
432855aa26eSCristian Marussi 
433855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
434855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
435855aa26eSCristian Marussi 		*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
436*b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
437*b27d04d5SCristian Marussi 				   domain_id, *pai, 0);
438855aa26eSCristian Marussi 		return 0;
439855aa26eSCristian Marussi 	}
440855aa26eSCristian Marussi 
441855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
442855aa26eSCristian Marussi }
443855aa26eSCristian Marussi 
444855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
4450316f99cSCristian Marussi 				      u32 domain_id, u32 pai)
4460316f99cSCristian Marussi {
4470316f99cSCristian Marussi 	int ret;
4480316f99cSCristian Marussi 	struct scmi_xfer *t;
4490316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
4500316f99cSCristian Marussi 
4510316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
4520316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
4530316f99cSCristian Marussi 	if (ret)
4540316f99cSCristian Marussi 		return ret;
4550316f99cSCristian Marussi 
4560316f99cSCristian Marussi 	msg = t->tx.buf;
4570316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
4580316f99cSCristian Marussi 	msg->flags = cpu_to_le32(0);
4590316f99cSCristian Marussi 	msg->value = cpu_to_le32(pai);
4600316f99cSCristian Marussi 
4610316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4620316f99cSCristian Marussi 
4630316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4640316f99cSCristian Marussi 	return ret;
4650316f99cSCristian Marussi }
4660316f99cSCristian Marussi 
467855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
468855aa26eSCristian Marussi 				 u32 domain_id, u32 pai)
469855aa26eSCristian Marussi {
470855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
471855aa26eSCristian Marussi 
472855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
473855aa26eSCristian Marussi 	if (!pc || !pc->powercap_pai_config || !pai ||
474855aa26eSCristian Marussi 	    pai < pc->min_pai || pai > pc->max_pai)
475855aa26eSCristian Marussi 		return -EINVAL;
476855aa26eSCristian Marussi 
477855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
478855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
479855aa26eSCristian Marussi 
480*b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
481*b27d04d5SCristian Marussi 				   domain_id, pai, 0);
482855aa26eSCristian Marussi 		iowrite32(pai, fci->set_addr);
483855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
484855aa26eSCristian Marussi 		return 0;
485855aa26eSCristian Marussi 	}
486855aa26eSCristian Marussi 
487855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
488855aa26eSCristian Marussi }
489855aa26eSCristian Marussi 
4900316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
4910316f99cSCristian Marussi 					  u32 domain_id, u32 *average_power,
4920316f99cSCristian Marussi 					  u32 *pai)
4930316f99cSCristian Marussi {
4940316f99cSCristian Marussi 	int ret;
4950316f99cSCristian Marussi 	struct scmi_xfer *t;
4960316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_meas_get *resp;
4970316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
4980316f99cSCristian Marussi 
4990316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
5000316f99cSCristian Marussi 	if (!pc || !pc->powercap_monitoring || !pai || !average_power)
5010316f99cSCristian Marussi 		return -EINVAL;
5020316f99cSCristian Marussi 
5030316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
5040316f99cSCristian Marussi 				      sizeof(u32), sizeof(*resp), &t);
5050316f99cSCristian Marussi 	if (ret)
5060316f99cSCristian Marussi 		return ret;
5070316f99cSCristian Marussi 
5080316f99cSCristian Marussi 	resp = t->rx.buf;
5090316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
5100316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
5110316f99cSCristian Marussi 	if (!ret) {
5120316f99cSCristian Marussi 		*average_power = le32_to_cpu(resp->power);
5130316f99cSCristian Marussi 		*pai = le32_to_cpu(resp->pai);
5140316f99cSCristian Marussi 	}
5150316f99cSCristian Marussi 
5160316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
5170316f99cSCristian Marussi 	return ret;
5180316f99cSCristian Marussi }
5190316f99cSCristian Marussi 
5200316f99cSCristian Marussi static int
5210316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
5220316f99cSCristian Marussi 					 u32 domain_id, u32 *power_thresh_low,
5230316f99cSCristian Marussi 					 u32 *power_thresh_high)
5240316f99cSCristian Marussi {
5250316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5260316f99cSCristian Marussi 
5270316f99cSCristian Marussi 	if (!power_thresh_low || !power_thresh_high ||
5280316f99cSCristian Marussi 	    domain_id >= pi->num_domains)
5290316f99cSCristian Marussi 		return -EINVAL;
5300316f99cSCristian Marussi 
5310316f99cSCristian Marussi 	*power_thresh_low =  THRESH_LOW(pi, domain_id);
5320316f99cSCristian Marussi 	*power_thresh_high = THRESH_HIGH(pi, domain_id);
5330316f99cSCristian Marussi 
5340316f99cSCristian Marussi 	return 0;
5350316f99cSCristian Marussi }
5360316f99cSCristian Marussi 
5370316f99cSCristian Marussi static int
5380316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
5390316f99cSCristian Marussi 					 u32 domain_id, u32 power_thresh_low,
5400316f99cSCristian Marussi 					 u32 power_thresh_high)
5410316f99cSCristian Marussi {
5420316f99cSCristian Marussi 	int ret = 0;
5430316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5440316f99cSCristian Marussi 
5450316f99cSCristian Marussi 	if (domain_id >= pi->num_domains ||
5460316f99cSCristian Marussi 	    power_thresh_low > power_thresh_high)
5470316f99cSCristian Marussi 		return -EINVAL;
5480316f99cSCristian Marussi 
5490316f99cSCristian Marussi 	/* Anything to do ? */
5500316f99cSCristian Marussi 	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
5510316f99cSCristian Marussi 	    THRESH_HIGH(pi, domain_id) == power_thresh_high)
5520316f99cSCristian Marussi 		return ret;
5530316f99cSCristian Marussi 
5540316f99cSCristian Marussi 	pi->states[domain_id].thresholds =
5550316f99cSCristian Marussi 		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
5560316f99cSCristian Marussi 		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
5570316f99cSCristian Marussi 
5580316f99cSCristian Marussi 	/* Update thresholds if notification already enabled */
5590316f99cSCristian Marussi 	if (pi->states[domain_id].meas_notif_enabled)
5600316f99cSCristian Marussi 		ret = scmi_powercap_notify(ph, domain_id,
5610316f99cSCristian Marussi 					   POWERCAP_MEASUREMENTS_NOTIFY,
5620316f99cSCristian Marussi 					   true);
5630316f99cSCristian Marussi 
5640316f99cSCristian Marussi 	return ret;
5650316f99cSCristian Marussi }
5660316f99cSCristian Marussi 
5670316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
5680316f99cSCristian Marussi 	.num_domains_get = scmi_powercap_num_domains_get,
5690316f99cSCristian Marussi 	.info_get = scmi_powercap_dom_info_get,
5700316f99cSCristian Marussi 	.cap_get = scmi_powercap_cap_get,
5710316f99cSCristian Marussi 	.cap_set = scmi_powercap_cap_set,
5720316f99cSCristian Marussi 	.pai_get = scmi_powercap_pai_get,
5730316f99cSCristian Marussi 	.pai_set = scmi_powercap_pai_set,
5740316f99cSCristian Marussi 	.measurements_get = scmi_powercap_measurements_get,
5750316f99cSCristian Marussi 	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
5760316f99cSCristian Marussi 	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
5770316f99cSCristian Marussi };
5780316f99cSCristian Marussi 
579855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
580855aa26eSCristian Marussi 					 u32 domain, struct scmi_fc_info **p_fc)
581855aa26eSCristian Marussi {
582855aa26eSCristian Marussi 	struct scmi_fc_info *fc;
583855aa26eSCristian Marussi 
584855aa26eSCristian Marussi 	fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
585855aa26eSCristian Marussi 	if (!fc)
586855aa26eSCristian Marussi 		return;
587855aa26eSCristian Marussi 
588855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
589855aa26eSCristian Marussi 				   POWERCAP_CAP_SET, 4, domain,
590855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_addr,
591855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_db);
592855aa26eSCristian Marussi 
593855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
594855aa26eSCristian Marussi 				   POWERCAP_CAP_GET, 4, domain,
595855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].get_addr, NULL);
596855aa26eSCristian Marussi 
597855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
598855aa26eSCristian Marussi 				   POWERCAP_PAI_SET, 4, domain,
599855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_addr,
600855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_db);
601855aa26eSCristian Marussi 
602855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
603855aa26eSCristian Marussi 				   POWERCAP_PAI_GET, 4, domain,
604855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].get_addr, NULL);
605855aa26eSCristian Marussi 
606855aa26eSCristian Marussi 	*p_fc = fc;
607855aa26eSCristian Marussi }
608855aa26eSCristian Marussi 
6090316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
6100316f99cSCristian Marussi 				u32 domain, int message_id, bool enable)
6110316f99cSCristian Marussi {
6120316f99cSCristian Marussi 	int ret;
6130316f99cSCristian Marussi 	struct scmi_xfer *t;
6140316f99cSCristian Marussi 
6150316f99cSCristian Marussi 	switch (message_id) {
6160316f99cSCristian Marussi 	case POWERCAP_CAP_NOTIFY:
6170316f99cSCristian Marussi 	{
6180316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_cap *notify;
6190316f99cSCristian Marussi 
6200316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6210316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6220316f99cSCristian Marussi 		if (ret)
6230316f99cSCristian Marussi 			return ret;
6240316f99cSCristian Marussi 
6250316f99cSCristian Marussi 		notify = t->tx.buf;
6260316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6270316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6280316f99cSCristian Marussi 		break;
6290316f99cSCristian Marussi 	}
6300316f99cSCristian Marussi 	case POWERCAP_MEASUREMENTS_NOTIFY:
6310316f99cSCristian Marussi 	{
6320316f99cSCristian Marussi 		u32 low, high;
6330316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_thresh *notify;
6340316f99cSCristian Marussi 
6350316f99cSCristian Marussi 		/*
6360316f99cSCristian Marussi 		 * Note that we have to pick the most recently configured
6370316f99cSCristian Marussi 		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
6380316f99cSCristian Marussi 		 * enable request and we fail, complaining, if no thresholds
6390316f99cSCristian Marussi 		 * were ever set, since this is an indication the API has been
6400316f99cSCristian Marussi 		 * used wrongly.
6410316f99cSCristian Marussi 		 */
6420316f99cSCristian Marussi 		ret = scmi_powercap_measurements_threshold_get(ph, domain,
6430316f99cSCristian Marussi 							       &low, &high);
6440316f99cSCristian Marussi 		if (ret)
6450316f99cSCristian Marussi 			return ret;
6460316f99cSCristian Marussi 
6470316f99cSCristian Marussi 		if (enable && !low && !high) {
6480316f99cSCristian Marussi 			dev_err(ph->dev,
6490316f99cSCristian Marussi 				"Invalid Measurements Notify thresholds: %u/%u\n",
6500316f99cSCristian Marussi 				low, high);
6510316f99cSCristian Marussi 			return -EINVAL;
6520316f99cSCristian Marussi 		}
6530316f99cSCristian Marussi 
6540316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6550316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6560316f99cSCristian Marussi 		if (ret)
6570316f99cSCristian Marussi 			return ret;
6580316f99cSCristian Marussi 
6590316f99cSCristian Marussi 		notify = t->tx.buf;
6600316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6610316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6620316f99cSCristian Marussi 		notify->power_thresh_low = cpu_to_le32(low);
6630316f99cSCristian Marussi 		notify->power_thresh_high = cpu_to_le32(high);
6640316f99cSCristian Marussi 		break;
6650316f99cSCristian Marussi 	}
6660316f99cSCristian Marussi 	default:
6670316f99cSCristian Marussi 		return -EINVAL;
6680316f99cSCristian Marussi 	}
6690316f99cSCristian Marussi 
6700316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
6710316f99cSCristian Marussi 
6720316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
6730316f99cSCristian Marussi 	return ret;
6740316f99cSCristian Marussi }
6750316f99cSCristian Marussi 
6760316f99cSCristian Marussi static int
6770316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
6780316f99cSCristian Marussi 				 u8 evt_id, u32 src_id, bool enable)
6790316f99cSCristian Marussi {
6800316f99cSCristian Marussi 	int ret, cmd_id;
6810316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
6820316f99cSCristian Marussi 
6830316f99cSCristian Marussi 	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
6840316f99cSCristian Marussi 		return -EINVAL;
6850316f99cSCristian Marussi 
6860316f99cSCristian Marussi 	cmd_id = evt_2_cmd[evt_id];
6870316f99cSCristian Marussi 	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
6880316f99cSCristian Marussi 	if (ret)
6890316f99cSCristian Marussi 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
6900316f99cSCristian Marussi 			 evt_id, src_id, ret);
6910316f99cSCristian Marussi 	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
6920316f99cSCristian Marussi 		/*
6930316f99cSCristian Marussi 		 * On success save the current notification enabled state, so
6940316f99cSCristian Marussi 		 * as to be able to properly update the notification thresholds
6950316f99cSCristian Marussi 		 * when they are modified on a domain for which measurement
6960316f99cSCristian Marussi 		 * notifications were currently enabled.
6970316f99cSCristian Marussi 		 *
6980316f99cSCristian Marussi 		 * This is needed because the SCMI Notification core machinery
6990316f99cSCristian Marussi 		 * and API does not support passing per-notification custom
7000316f99cSCristian Marussi 		 * arguments at callback registration time.
7010316f99cSCristian Marussi 		 *
7020316f99cSCristian Marussi 		 * Note that this can be done here with a simple flag since the
7030316f99cSCristian Marussi 		 * SCMI core Notifications code takes care of keeping proper
7040316f99cSCristian Marussi 		 * per-domain enables refcounting, so that this helper function
7050316f99cSCristian Marussi 		 * will be called only once (for enables) when the first user
7060316f99cSCristian Marussi 		 * registers a callback on this domain and once more (disable)
7070316f99cSCristian Marussi 		 * when the last user de-registers its callback.
7080316f99cSCristian Marussi 		 */
7090316f99cSCristian Marussi 		pi->states[src_id].meas_notif_enabled = enable;
7100316f99cSCristian Marussi 
7110316f99cSCristian Marussi 	return ret;
7120316f99cSCristian Marussi }
7130316f99cSCristian Marussi 
7140316f99cSCristian Marussi static void *
7150316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
7160316f99cSCristian Marussi 				 u8 evt_id, ktime_t timestamp,
7170316f99cSCristian Marussi 				 const void *payld, size_t payld_sz,
7180316f99cSCristian Marussi 				 void *report, u32 *src_id)
7190316f99cSCristian Marussi {
7200316f99cSCristian Marussi 	void *rep = NULL;
7210316f99cSCristian Marussi 
7220316f99cSCristian Marussi 	switch (evt_id) {
7230316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_CAP_CHANGED:
7240316f99cSCristian Marussi 	{
7250316f99cSCristian Marussi 		const struct scmi_powercap_cap_changed_notify_payld *p = payld;
7260316f99cSCristian Marussi 		struct scmi_powercap_cap_changed_report *r = report;
7270316f99cSCristian Marussi 
7280316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7290316f99cSCristian Marussi 			break;
7300316f99cSCristian Marussi 
7310316f99cSCristian Marussi 		r->timestamp = timestamp;
7320316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7330316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7340316f99cSCristian Marussi 		r->power_cap = le32_to_cpu(p->power_cap);
7350316f99cSCristian Marussi 		r->pai = le32_to_cpu(p->pai);
7360316f99cSCristian Marussi 		*src_id = r->domain_id;
7370316f99cSCristian Marussi 		rep = r;
7380316f99cSCristian Marussi 		break;
7390316f99cSCristian Marussi 	}
7400316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
7410316f99cSCristian Marussi 	{
7420316f99cSCristian Marussi 		const struct scmi_powercap_meas_changed_notify_payld *p = payld;
7430316f99cSCristian Marussi 		struct scmi_powercap_meas_changed_report *r = report;
7440316f99cSCristian Marussi 
7450316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7460316f99cSCristian Marussi 			break;
7470316f99cSCristian Marussi 
7480316f99cSCristian Marussi 		r->timestamp = timestamp;
7490316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7500316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7510316f99cSCristian Marussi 		r->power = le32_to_cpu(p->power);
7520316f99cSCristian Marussi 		*src_id = r->domain_id;
7530316f99cSCristian Marussi 		rep = r;
7540316f99cSCristian Marussi 		break;
7550316f99cSCristian Marussi 	}
7560316f99cSCristian Marussi 	default:
7570316f99cSCristian Marussi 		break;
7580316f99cSCristian Marussi 	}
7590316f99cSCristian Marussi 
7600316f99cSCristian Marussi 	return rep;
7610316f99cSCristian Marussi }
7620316f99cSCristian Marussi 
7630316f99cSCristian Marussi static int
7640316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
7650316f99cSCristian Marussi {
7660316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
7670316f99cSCristian Marussi 
7680316f99cSCristian Marussi 	if (!pi)
7690316f99cSCristian Marussi 		return -EINVAL;
7700316f99cSCristian Marussi 
7710316f99cSCristian Marussi 	return pi->num_domains;
7720316f99cSCristian Marussi }
7730316f99cSCristian Marussi 
7740316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
7750316f99cSCristian Marussi 	{
7760316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
7770316f99cSCristian Marussi 		.max_payld_sz =
7780316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_notify_payld),
7790316f99cSCristian Marussi 		.max_report_sz =
7800316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_report),
7810316f99cSCristian Marussi 	},
7820316f99cSCristian Marussi 	{
7830316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
7840316f99cSCristian Marussi 		.max_payld_sz =
7850316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_notify_payld),
7860316f99cSCristian Marussi 		.max_report_sz =
7870316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_report),
7880316f99cSCristian Marussi 	},
7890316f99cSCristian Marussi };
7900316f99cSCristian Marussi 
7910316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
7920316f99cSCristian Marussi 	.get_num_sources = scmi_powercap_get_num_sources,
7930316f99cSCristian Marussi 	.set_notify_enabled = scmi_powercap_set_notify_enabled,
7940316f99cSCristian Marussi 	.fill_custom_report = scmi_powercap_fill_custom_report,
7950316f99cSCristian Marussi };
7960316f99cSCristian Marussi 
7970316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
7980316f99cSCristian Marussi 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
7990316f99cSCristian Marussi 	.ops = &powercap_event_ops,
8000316f99cSCristian Marussi 	.evts = powercap_events,
8010316f99cSCristian Marussi 	.num_events = ARRAY_SIZE(powercap_events),
8020316f99cSCristian Marussi };
8030316f99cSCristian Marussi 
8040316f99cSCristian Marussi static int
8050316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
8060316f99cSCristian Marussi {
8070316f99cSCristian Marussi 	int domain, ret;
8080316f99cSCristian Marussi 	u32 version;
8090316f99cSCristian Marussi 	struct powercap_info *pinfo;
8100316f99cSCristian Marussi 
8110316f99cSCristian Marussi 	ret = ph->xops->version_get(ph, &version);
8120316f99cSCristian Marussi 	if (ret)
8130316f99cSCristian Marussi 		return ret;
8140316f99cSCristian Marussi 
8150316f99cSCristian Marussi 	dev_dbg(ph->dev, "Powercap Version %d.%d\n",
8160316f99cSCristian Marussi 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
8170316f99cSCristian Marussi 
8180316f99cSCristian Marussi 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
8190316f99cSCristian Marussi 	if (!pinfo)
8200316f99cSCristian Marussi 		return -ENOMEM;
8210316f99cSCristian Marussi 
8220316f99cSCristian Marussi 	ret = scmi_powercap_attributes_get(ph, pinfo);
8230316f99cSCristian Marussi 	if (ret)
8240316f99cSCristian Marussi 		return ret;
8250316f99cSCristian Marussi 
8260316f99cSCristian Marussi 	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
8270316f99cSCristian Marussi 					sizeof(*pinfo->powercaps),
8280316f99cSCristian Marussi 					GFP_KERNEL);
8290316f99cSCristian Marussi 	if (!pinfo->powercaps)
8300316f99cSCristian Marussi 		return -ENOMEM;
8310316f99cSCristian Marussi 
8320316f99cSCristian Marussi 	/*
8330316f99cSCristian Marussi 	 * Note that any failure in retrieving any domain attribute leads to
8340316f99cSCristian Marussi 	 * the whole Powercap protocol initialization failure: this way the
8350316f99cSCristian Marussi 	 * reported Powercap domains are all assured, when accessed, to be well
8360316f99cSCristian Marussi 	 * formed and correlated by sane parent-child relationship (if any).
8370316f99cSCristian Marussi 	 */
8380316f99cSCristian Marussi 	for (domain = 0; domain < pinfo->num_domains; domain++) {
8390316f99cSCristian Marussi 		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
8400316f99cSCristian Marussi 		if (ret)
8410316f99cSCristian Marussi 			return ret;
842855aa26eSCristian Marussi 
843855aa26eSCristian Marussi 		if (pinfo->powercaps[domain].fastchannels)
844855aa26eSCristian Marussi 			scmi_powercap_domain_init_fc(ph, domain,
845855aa26eSCristian Marussi 						     &pinfo->powercaps[domain].fc_info);
8460316f99cSCristian Marussi 	}
8470316f99cSCristian Marussi 
8480316f99cSCristian Marussi 	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
8490316f99cSCristian Marussi 				     sizeof(*pinfo->states), GFP_KERNEL);
8500316f99cSCristian Marussi 	if (!pinfo->states)
8510316f99cSCristian Marussi 		return -ENOMEM;
8520316f99cSCristian Marussi 
8530316f99cSCristian Marussi 	pinfo->version = version;
8540316f99cSCristian Marussi 
8550316f99cSCristian Marussi 	return ph->set_priv(ph, pinfo);
8560316f99cSCristian Marussi }
8570316f99cSCristian Marussi 
8580316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
8590316f99cSCristian Marussi 	.id = SCMI_PROTOCOL_POWERCAP,
8600316f99cSCristian Marussi 	.owner = THIS_MODULE,
8610316f99cSCristian Marussi 	.instance_init = &scmi_powercap_protocol_init,
8620316f99cSCristian Marussi 	.ops = &powercap_proto_ops,
8630316f99cSCristian Marussi 	.events = &powercap_protocol_events,
8640316f99cSCristian Marussi };
8650316f99cSCristian Marussi 
8660316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
867