xref: /openbmc/linux/drivers/firmware/arm_scmi/powercap.c (revision 855aa26e5f56d415b71d3f8d86ef0cc51b2166a3)
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>
11*855aa26eSCristian Marussi #include <linux/io.h>
120316f99cSCristian Marussi #include <linux/module.h>
130316f99cSCristian Marussi #include <linux/scmi_protocol.h>
140316f99cSCristian Marussi 
150316f99cSCristian Marussi #include "protocols.h"
160316f99cSCristian Marussi #include "notify.h"
170316f99cSCristian Marussi 
180316f99cSCristian Marussi enum scmi_powercap_protocol_cmd {
190316f99cSCristian Marussi 	POWERCAP_DOMAIN_ATTRIBUTES = 0x3,
200316f99cSCristian Marussi 	POWERCAP_CAP_GET = 0x4,
210316f99cSCristian Marussi 	POWERCAP_CAP_SET = 0x5,
220316f99cSCristian Marussi 	POWERCAP_PAI_GET = 0x6,
230316f99cSCristian Marussi 	POWERCAP_PAI_SET = 0x7,
240316f99cSCristian Marussi 	POWERCAP_DOMAIN_NAME_GET = 0x8,
250316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_GET = 0x9,
260316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY = 0xa,
270316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY = 0xb,
280316f99cSCristian Marussi 	POWERCAP_DESCRIBE_FASTCHANNEL = 0xc,
290316f99cSCristian Marussi };
300316f99cSCristian Marussi 
31*855aa26eSCristian Marussi enum {
32*855aa26eSCristian Marussi 	POWERCAP_FC_CAP,
33*855aa26eSCristian Marussi 	POWERCAP_FC_PAI,
34*855aa26eSCristian Marussi 	POWERCAP_FC_MAX,
35*855aa26eSCristian Marussi };
36*855aa26eSCristian Marussi 
370316f99cSCristian Marussi struct scmi_msg_resp_powercap_domain_attributes {
380316f99cSCristian Marussi 	__le32 attributes;
390316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(x)		((x) & BIT(31))
400316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(x)	((x) & BIT(30))
410316f99cSCristian Marussi #define SUPPORTS_ASYNC_POWERCAP_CAP_SET(x)		((x) & BIT(29))
420316f99cSCristian Marussi #define SUPPORTS_EXTENDED_NAMES(x)			((x) & BIT(28))
430316f99cSCristian Marussi #define SUPPORTS_POWERCAP_CAP_CONFIGURATION(x)		((x) & BIT(27))
440316f99cSCristian Marussi #define SUPPORTS_POWERCAP_MONITORING(x)			((x) & BIT(26))
450316f99cSCristian Marussi #define SUPPORTS_POWERCAP_PAI_CONFIGURATION(x)		((x) & BIT(25))
46*855aa26eSCristian Marussi #define SUPPORTS_POWERCAP_FASTCHANNELS(x)		((x) & BIT(22))
470316f99cSCristian Marussi #define POWERCAP_POWER_UNIT(x)				\
480316f99cSCristian Marussi 		(FIELD_GET(GENMASK(24, 23), (x)))
490316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_MW(x)			\
500316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x2)
510316f99cSCristian Marussi #define	SUPPORTS_POWER_UNITS_UW(x)			\
520316f99cSCristian Marussi 		(POWERCAP_POWER_UNIT(x) == 0x1)
530316f99cSCristian Marussi 	u8 name[SCMI_SHORT_NAME_MAX_SIZE];
540316f99cSCristian Marussi 	__le32 min_pai;
550316f99cSCristian Marussi 	__le32 max_pai;
560316f99cSCristian Marussi 	__le32 pai_step;
570316f99cSCristian Marussi 	__le32 min_power_cap;
580316f99cSCristian Marussi 	__le32 max_power_cap;
590316f99cSCristian Marussi 	__le32 power_cap_step;
600316f99cSCristian Marussi 	__le32 sustainable_power;
610316f99cSCristian Marussi 	__le32 accuracy;
620316f99cSCristian Marussi 	__le32 parent_id;
630316f99cSCristian Marussi };
640316f99cSCristian Marussi 
650316f99cSCristian Marussi struct scmi_msg_powercap_set_cap_or_pai {
660316f99cSCristian Marussi 	__le32 domain;
670316f99cSCristian Marussi 	__le32 flags;
680316f99cSCristian Marussi #define CAP_SET_ASYNC		BIT(1)
690316f99cSCristian Marussi #define CAP_SET_IGNORE_DRESP	BIT(0)
700316f99cSCristian Marussi 	__le32 value;
710316f99cSCristian Marussi };
720316f99cSCristian Marussi 
730316f99cSCristian Marussi struct scmi_msg_resp_powercap_cap_set_complete {
740316f99cSCristian Marussi 	__le32 domain;
750316f99cSCristian Marussi 	__le32 power_cap;
760316f99cSCristian Marussi };
770316f99cSCristian Marussi 
780316f99cSCristian Marussi struct scmi_msg_resp_powercap_meas_get {
790316f99cSCristian Marussi 	__le32 power;
800316f99cSCristian Marussi 	__le32 pai;
810316f99cSCristian Marussi };
820316f99cSCristian Marussi 
830316f99cSCristian Marussi struct scmi_msg_powercap_notify_cap {
840316f99cSCristian Marussi 	__le32 domain;
850316f99cSCristian Marussi 	__le32 notify_enable;
860316f99cSCristian Marussi };
870316f99cSCristian Marussi 
880316f99cSCristian Marussi struct scmi_msg_powercap_notify_thresh {
890316f99cSCristian Marussi 	__le32 domain;
900316f99cSCristian Marussi 	__le32 notify_enable;
910316f99cSCristian Marussi 	__le32 power_thresh_low;
920316f99cSCristian Marussi 	__le32 power_thresh_high;
930316f99cSCristian Marussi };
940316f99cSCristian Marussi 
950316f99cSCristian Marussi struct scmi_powercap_cap_changed_notify_payld {
960316f99cSCristian Marussi 	__le32 agent_id;
970316f99cSCristian Marussi 	__le32 domain_id;
980316f99cSCristian Marussi 	__le32 power_cap;
990316f99cSCristian Marussi 	__le32 pai;
1000316f99cSCristian Marussi };
1010316f99cSCristian Marussi 
1020316f99cSCristian Marussi struct scmi_powercap_meas_changed_notify_payld {
1030316f99cSCristian Marussi 	__le32 agent_id;
1040316f99cSCristian Marussi 	__le32 domain_id;
1050316f99cSCristian Marussi 	__le32 power;
1060316f99cSCristian Marussi };
1070316f99cSCristian Marussi 
1080316f99cSCristian Marussi struct scmi_powercap_state {
1090316f99cSCristian Marussi 	bool meas_notif_enabled;
1100316f99cSCristian Marussi 	u64 thresholds;
1110316f99cSCristian Marussi #define THRESH_LOW(p, id)				\
1120316f99cSCristian Marussi 	(lower_32_bits((p)->states[(id)].thresholds))
1130316f99cSCristian Marussi #define THRESH_HIGH(p, id)				\
1140316f99cSCristian Marussi 	(upper_32_bits((p)->states[(id)].thresholds))
1150316f99cSCristian Marussi };
1160316f99cSCristian Marussi 
1170316f99cSCristian Marussi struct powercap_info {
1180316f99cSCristian Marussi 	u32 version;
1190316f99cSCristian Marussi 	int num_domains;
1200316f99cSCristian Marussi 	struct scmi_powercap_state *states;
1210316f99cSCristian Marussi 	struct scmi_powercap_info *powercaps;
1220316f99cSCristian Marussi };
1230316f99cSCristian Marussi 
1240316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
1250316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY,
1260316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY,
1270316f99cSCristian Marussi };
1280316f99cSCristian Marussi 
1290316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
1300316f99cSCristian Marussi 				u32 domain, int message_id, bool enable);
1310316f99cSCristian Marussi 
1320316f99cSCristian Marussi static int
1330316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
1340316f99cSCristian Marussi 			     struct powercap_info *pi)
1350316f99cSCristian Marussi {
1360316f99cSCristian Marussi 	int ret;
1370316f99cSCristian Marussi 	struct scmi_xfer *t;
1380316f99cSCristian Marussi 
1390316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
1400316f99cSCristian Marussi 				      sizeof(u32), &t);
1410316f99cSCristian Marussi 	if (ret)
1420316f99cSCristian Marussi 		return ret;
1430316f99cSCristian Marussi 
1440316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1450316f99cSCristian Marussi 	if (!ret) {
1460316f99cSCristian Marussi 		u32 attributes;
1470316f99cSCristian Marussi 
1480316f99cSCristian Marussi 		attributes = get_unaligned_le32(t->rx.buf);
1490316f99cSCristian Marussi 		pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
1500316f99cSCristian Marussi 	}
1510316f99cSCristian Marussi 
1520316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
1530316f99cSCristian Marussi 	return ret;
1540316f99cSCristian Marussi }
1550316f99cSCristian Marussi 
1560316f99cSCristian Marussi static inline int
1570316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
1580316f99cSCristian Marussi 		       unsigned int step_val, bool configurable)
1590316f99cSCristian Marussi {
1600316f99cSCristian Marussi 	if (!min_val || !max_val)
1610316f99cSCristian Marussi 		return -EPROTO;
1620316f99cSCristian Marussi 
1630316f99cSCristian Marussi 	if ((configurable && min_val == max_val) ||
1640316f99cSCristian Marussi 	    (!configurable && min_val != max_val))
1650316f99cSCristian Marussi 		return -EPROTO;
1660316f99cSCristian Marussi 
1670316f99cSCristian Marussi 	if (min_val != max_val && !step_val)
1680316f99cSCristian Marussi 		return -EPROTO;
1690316f99cSCristian Marussi 
1700316f99cSCristian Marussi 	return 0;
1710316f99cSCristian Marussi }
1720316f99cSCristian Marussi 
1730316f99cSCristian Marussi static int
1740316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
1750316f99cSCristian Marussi 				    struct powercap_info *pinfo, u32 domain)
1760316f99cSCristian Marussi {
1770316f99cSCristian Marussi 	int ret;
1780316f99cSCristian Marussi 	u32 flags;
1790316f99cSCristian Marussi 	struct scmi_xfer *t;
1800316f99cSCristian Marussi 	struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
1810316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_domain_attributes *resp;
1820316f99cSCristian Marussi 
1830316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
1840316f99cSCristian Marussi 				      sizeof(domain), sizeof(*resp), &t);
1850316f99cSCristian Marussi 	if (ret)
1860316f99cSCristian Marussi 		return ret;
1870316f99cSCristian Marussi 
1880316f99cSCristian Marussi 	put_unaligned_le32(domain, t->tx.buf);
1890316f99cSCristian Marussi 	resp = t->rx.buf;
1900316f99cSCristian Marussi 
1910316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1920316f99cSCristian Marussi 	if (!ret) {
1930316f99cSCristian Marussi 		flags = le32_to_cpu(resp->attributes);
1940316f99cSCristian Marussi 
1950316f99cSCristian Marussi 		dom_info->id = domain;
1960316f99cSCristian Marussi 		dom_info->notify_powercap_cap_change =
1970316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
1980316f99cSCristian Marussi 		dom_info->notify_powercap_measurement_change =
1990316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
2000316f99cSCristian Marussi 		dom_info->async_powercap_cap_set =
2010316f99cSCristian Marussi 			SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
2020316f99cSCristian Marussi 		dom_info->powercap_cap_config =
2030316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
2040316f99cSCristian Marussi 		dom_info->powercap_monitoring =
2050316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MONITORING(flags);
2060316f99cSCristian Marussi 		dom_info->powercap_pai_config =
2070316f99cSCristian Marussi 			SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
2080316f99cSCristian Marussi 		dom_info->powercap_scale_mw =
2090316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_MW(flags);
2100316f99cSCristian Marussi 		dom_info->powercap_scale_uw =
2110316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_UW(flags);
212*855aa26eSCristian Marussi 		dom_info->fastchannels =
213*855aa26eSCristian Marussi 			SUPPORTS_POWERCAP_FASTCHANNELS(flags);
2140316f99cSCristian Marussi 
2150316f99cSCristian Marussi 		strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
2160316f99cSCristian Marussi 
2170316f99cSCristian Marussi 		dom_info->min_pai = le32_to_cpu(resp->min_pai);
2180316f99cSCristian Marussi 		dom_info->max_pai = le32_to_cpu(resp->max_pai);
2190316f99cSCristian Marussi 		dom_info->pai_step = le32_to_cpu(resp->pai_step);
2200316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_pai,
2210316f99cSCristian Marussi 					     dom_info->max_pai,
2220316f99cSCristian Marussi 					     dom_info->pai_step,
2230316f99cSCristian Marussi 					     dom_info->powercap_pai_config);
2240316f99cSCristian Marussi 		if (ret) {
2250316f99cSCristian Marussi 			dev_err(ph->dev,
2260316f99cSCristian Marussi 				"Platform reported inconsistent PAI config for domain %d - %s\n",
2270316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2280316f99cSCristian Marussi 			goto clean;
2290316f99cSCristian Marussi 		}
2300316f99cSCristian Marussi 
2310316f99cSCristian Marussi 		dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
2320316f99cSCristian Marussi 		dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
2330316f99cSCristian Marussi 		dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
2340316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_power_cap,
2350316f99cSCristian Marussi 					     dom_info->max_power_cap,
2360316f99cSCristian Marussi 					     dom_info->power_cap_step,
2370316f99cSCristian Marussi 					     dom_info->powercap_cap_config);
2380316f99cSCristian Marussi 		if (ret) {
2390316f99cSCristian Marussi 			dev_err(ph->dev,
2400316f99cSCristian Marussi 				"Platform reported inconsistent CAP config for domain %d - %s\n",
2410316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2420316f99cSCristian Marussi 			goto clean;
2430316f99cSCristian Marussi 		}
2440316f99cSCristian Marussi 
2450316f99cSCristian Marussi 		dom_info->sustainable_power =
2460316f99cSCristian Marussi 			le32_to_cpu(resp->sustainable_power);
2470316f99cSCristian Marussi 		dom_info->accuracy = le32_to_cpu(resp->accuracy);
2480316f99cSCristian Marussi 
2490316f99cSCristian Marussi 		dom_info->parent_id = le32_to_cpu(resp->parent_id);
2500316f99cSCristian Marussi 		if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
2510316f99cSCristian Marussi 		    (dom_info->parent_id >= pinfo->num_domains ||
2520316f99cSCristian Marussi 		     dom_info->parent_id == dom_info->id)) {
2530316f99cSCristian Marussi 			dev_err(ph->dev,
2540316f99cSCristian Marussi 				"Platform reported inconsistent parent ID for domain %d - %s\n",
2550316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2560316f99cSCristian Marussi 			ret = -ENODEV;
2570316f99cSCristian Marussi 		}
2580316f99cSCristian Marussi 	}
2590316f99cSCristian Marussi 
2600316f99cSCristian Marussi clean:
2610316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
2620316f99cSCristian Marussi 
2630316f99cSCristian Marussi 	/*
2640316f99cSCristian Marussi 	 * If supported overwrite short name with the extended one;
2650316f99cSCristian Marussi 	 * on error just carry on and use already provided short name.
2660316f99cSCristian Marussi 	 */
2670316f99cSCristian Marussi 	if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
2680316f99cSCristian Marussi 		ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
2690316f99cSCristian Marussi 					    domain, dom_info->name,
2700316f99cSCristian Marussi 					    SCMI_MAX_STR_SIZE);
2710316f99cSCristian Marussi 
2720316f99cSCristian Marussi 	return ret;
2730316f99cSCristian Marussi }
2740316f99cSCristian Marussi 
2750316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
2760316f99cSCristian Marussi {
2770316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2780316f99cSCristian Marussi 
2790316f99cSCristian Marussi 	return pi->num_domains;
2800316f99cSCristian Marussi }
2810316f99cSCristian Marussi 
2820316f99cSCristian Marussi static const struct scmi_powercap_info *
2830316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
2840316f99cSCristian Marussi {
2850316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2860316f99cSCristian Marussi 
2870316f99cSCristian Marussi 	if (domain_id >= pi->num_domains)
2880316f99cSCristian Marussi 		return NULL;
2890316f99cSCristian Marussi 
2900316f99cSCristian Marussi 	return pi->powercaps + domain_id;
2910316f99cSCristian Marussi }
2920316f99cSCristian Marussi 
293*855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
2940316f99cSCristian Marussi 				      u32 domain_id, u32 *power_cap)
2950316f99cSCristian Marussi {
2960316f99cSCristian Marussi 	int ret;
2970316f99cSCristian Marussi 	struct scmi_xfer *t;
2980316f99cSCristian Marussi 
2990316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
3000316f99cSCristian Marussi 				      sizeof(u32), &t);
3010316f99cSCristian Marussi 	if (ret)
3020316f99cSCristian Marussi 		return ret;
3030316f99cSCristian Marussi 
3040316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
3050316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
3060316f99cSCristian Marussi 	if (!ret)
3070316f99cSCristian Marussi 		*power_cap = get_unaligned_le32(t->rx.buf);
3080316f99cSCristian Marussi 
3090316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3100316f99cSCristian Marussi 
3110316f99cSCristian Marussi 	return ret;
3120316f99cSCristian Marussi }
3130316f99cSCristian Marussi 
314*855aa26eSCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
315*855aa26eSCristian Marussi 				 u32 domain_id, u32 *power_cap)
316*855aa26eSCristian Marussi {
317*855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
318*855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
319*855aa26eSCristian Marussi 
320*855aa26eSCristian Marussi 	if (!power_cap || domain_id >= pi->num_domains)
321*855aa26eSCristian Marussi 		return -EINVAL;
322*855aa26eSCristian Marussi 
323*855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
324*855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
325*855aa26eSCristian Marussi 		*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
326*855aa26eSCristian Marussi 		return 0;
327*855aa26eSCristian Marussi 	}
328*855aa26eSCristian Marussi 
329*855aa26eSCristian Marussi 	return scmi_powercap_xfer_cap_get(ph, domain_id, power_cap);
330*855aa26eSCristian Marussi }
331*855aa26eSCristian Marussi 
332*855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
333*855aa26eSCristian Marussi 				      const struct scmi_powercap_info *pc,
334*855aa26eSCristian Marussi 				      u32 power_cap, bool ignore_dresp)
3350316f99cSCristian Marussi {
3360316f99cSCristian Marussi 	int ret;
3370316f99cSCristian Marussi 	struct scmi_xfer *t;
3380316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
3390316f99cSCristian Marussi 
3400316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
3410316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
3420316f99cSCristian Marussi 	if (ret)
3430316f99cSCristian Marussi 		return ret;
3440316f99cSCristian Marussi 
3450316f99cSCristian Marussi 	msg = t->tx.buf;
346*855aa26eSCristian Marussi 	msg->domain = cpu_to_le32(pc->id);
3470316f99cSCristian Marussi 	msg->flags =
3480316f99cSCristian Marussi 		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
3490316f99cSCristian Marussi 			    FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
3500316f99cSCristian Marussi 	msg->value = cpu_to_le32(power_cap);
3510316f99cSCristian Marussi 
3520316f99cSCristian Marussi 	if (!pc->async_powercap_cap_set || ignore_dresp) {
3530316f99cSCristian Marussi 		ret = ph->xops->do_xfer(ph, t);
3540316f99cSCristian Marussi 	} else {
3550316f99cSCristian Marussi 		ret = ph->xops->do_xfer_with_response(ph, t);
3560316f99cSCristian Marussi 		if (!ret) {
3570316f99cSCristian Marussi 			struct scmi_msg_resp_powercap_cap_set_complete *resp;
3580316f99cSCristian Marussi 
3590316f99cSCristian Marussi 			resp = t->rx.buf;
360*855aa26eSCristian Marussi 			if (le32_to_cpu(resp->domain) == pc->id)
3610316f99cSCristian Marussi 				dev_dbg(ph->dev,
3620316f99cSCristian Marussi 					"Powercap ID %d CAP set async to %u\n",
363*855aa26eSCristian Marussi 					pc->id,
3640316f99cSCristian Marussi 					get_unaligned_le32(&resp->power_cap));
3650316f99cSCristian Marussi 			else
3660316f99cSCristian Marussi 				ret = -EPROTO;
3670316f99cSCristian Marussi 		}
3680316f99cSCristian Marussi 	}
3690316f99cSCristian Marussi 
3700316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3710316f99cSCristian Marussi 	return ret;
3720316f99cSCristian Marussi }
3730316f99cSCristian Marussi 
374*855aa26eSCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
375*855aa26eSCristian Marussi 				 u32 domain_id, u32 power_cap,
376*855aa26eSCristian Marussi 				 bool ignore_dresp)
377*855aa26eSCristian Marussi {
378*855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
379*855aa26eSCristian Marussi 
380*855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
381*855aa26eSCristian Marussi 	if (!pc || !pc->powercap_cap_config || !power_cap ||
382*855aa26eSCristian Marussi 	    power_cap < pc->min_power_cap ||
383*855aa26eSCristian Marussi 	    power_cap > pc->max_power_cap)
384*855aa26eSCristian Marussi 		return -EINVAL;
385*855aa26eSCristian Marussi 
386*855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
387*855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
388*855aa26eSCristian Marussi 
389*855aa26eSCristian Marussi 		iowrite32(power_cap, fci->set_addr);
390*855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
391*855aa26eSCristian Marussi 		return 0;
392*855aa26eSCristian Marussi 	}
393*855aa26eSCristian Marussi 
394*855aa26eSCristian Marussi 	return scmi_powercap_xfer_cap_set(ph, pc, power_cap, ignore_dresp);
395*855aa26eSCristian Marussi }
396*855aa26eSCristian Marussi 
397*855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
3980316f99cSCristian Marussi 				      u32 domain_id, u32 *pai)
3990316f99cSCristian Marussi {
4000316f99cSCristian Marussi 	int ret;
4010316f99cSCristian Marussi 	struct scmi_xfer *t;
4020316f99cSCristian Marussi 
4030316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
4040316f99cSCristian Marussi 				      sizeof(u32), &t);
4050316f99cSCristian Marussi 	if (ret)
4060316f99cSCristian Marussi 		return ret;
4070316f99cSCristian Marussi 
4080316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
4090316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4100316f99cSCristian Marussi 	if (!ret)
4110316f99cSCristian Marussi 		*pai = get_unaligned_le32(t->rx.buf);
4120316f99cSCristian Marussi 
4130316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4140316f99cSCristian Marussi 
4150316f99cSCristian Marussi 	return ret;
4160316f99cSCristian Marussi }
4170316f99cSCristian Marussi 
418*855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
419*855aa26eSCristian Marussi 				 u32 domain_id, u32 *pai)
420*855aa26eSCristian Marussi {
421*855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
422*855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
423*855aa26eSCristian Marussi 
424*855aa26eSCristian Marussi 	if (!pai || domain_id >= pi->num_domains)
425*855aa26eSCristian Marussi 		return -EINVAL;
426*855aa26eSCristian Marussi 
427*855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
428*855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
429*855aa26eSCristian Marussi 		*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
430*855aa26eSCristian Marussi 		return 0;
431*855aa26eSCristian Marussi 	}
432*855aa26eSCristian Marussi 
433*855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
434*855aa26eSCristian Marussi }
435*855aa26eSCristian Marussi 
436*855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
4370316f99cSCristian Marussi 				      u32 domain_id, u32 pai)
4380316f99cSCristian Marussi {
4390316f99cSCristian Marussi 	int ret;
4400316f99cSCristian Marussi 	struct scmi_xfer *t;
4410316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
4420316f99cSCristian Marussi 
4430316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
4440316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
4450316f99cSCristian Marussi 	if (ret)
4460316f99cSCristian Marussi 		return ret;
4470316f99cSCristian Marussi 
4480316f99cSCristian Marussi 	msg = t->tx.buf;
4490316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
4500316f99cSCristian Marussi 	msg->flags = cpu_to_le32(0);
4510316f99cSCristian Marussi 	msg->value = cpu_to_le32(pai);
4520316f99cSCristian Marussi 
4530316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4540316f99cSCristian Marussi 
4550316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4560316f99cSCristian Marussi 	return ret;
4570316f99cSCristian Marussi }
4580316f99cSCristian Marussi 
459*855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
460*855aa26eSCristian Marussi 				 u32 domain_id, u32 pai)
461*855aa26eSCristian Marussi {
462*855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
463*855aa26eSCristian Marussi 
464*855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
465*855aa26eSCristian Marussi 	if (!pc || !pc->powercap_pai_config || !pai ||
466*855aa26eSCristian Marussi 	    pai < pc->min_pai || pai > pc->max_pai)
467*855aa26eSCristian Marussi 		return -EINVAL;
468*855aa26eSCristian Marussi 
469*855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
470*855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
471*855aa26eSCristian Marussi 
472*855aa26eSCristian Marussi 		iowrite32(pai, fci->set_addr);
473*855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
474*855aa26eSCristian Marussi 		return 0;
475*855aa26eSCristian Marussi 	}
476*855aa26eSCristian Marussi 
477*855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
478*855aa26eSCristian Marussi }
479*855aa26eSCristian Marussi 
4800316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
4810316f99cSCristian Marussi 					  u32 domain_id, u32 *average_power,
4820316f99cSCristian Marussi 					  u32 *pai)
4830316f99cSCristian Marussi {
4840316f99cSCristian Marussi 	int ret;
4850316f99cSCristian Marussi 	struct scmi_xfer *t;
4860316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_meas_get *resp;
4870316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
4880316f99cSCristian Marussi 
4890316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
4900316f99cSCristian Marussi 	if (!pc || !pc->powercap_monitoring || !pai || !average_power)
4910316f99cSCristian Marussi 		return -EINVAL;
4920316f99cSCristian Marussi 
4930316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
4940316f99cSCristian Marussi 				      sizeof(u32), sizeof(*resp), &t);
4950316f99cSCristian Marussi 	if (ret)
4960316f99cSCristian Marussi 		return ret;
4970316f99cSCristian Marussi 
4980316f99cSCristian Marussi 	resp = t->rx.buf;
4990316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
5000316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
5010316f99cSCristian Marussi 	if (!ret) {
5020316f99cSCristian Marussi 		*average_power = le32_to_cpu(resp->power);
5030316f99cSCristian Marussi 		*pai = le32_to_cpu(resp->pai);
5040316f99cSCristian Marussi 	}
5050316f99cSCristian Marussi 
5060316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
5070316f99cSCristian Marussi 	return ret;
5080316f99cSCristian Marussi }
5090316f99cSCristian Marussi 
5100316f99cSCristian Marussi static int
5110316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
5120316f99cSCristian Marussi 					 u32 domain_id, u32 *power_thresh_low,
5130316f99cSCristian Marussi 					 u32 *power_thresh_high)
5140316f99cSCristian Marussi {
5150316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5160316f99cSCristian Marussi 
5170316f99cSCristian Marussi 	if (!power_thresh_low || !power_thresh_high ||
5180316f99cSCristian Marussi 	    domain_id >= pi->num_domains)
5190316f99cSCristian Marussi 		return -EINVAL;
5200316f99cSCristian Marussi 
5210316f99cSCristian Marussi 	*power_thresh_low =  THRESH_LOW(pi, domain_id);
5220316f99cSCristian Marussi 	*power_thresh_high = THRESH_HIGH(pi, domain_id);
5230316f99cSCristian Marussi 
5240316f99cSCristian Marussi 	return 0;
5250316f99cSCristian Marussi }
5260316f99cSCristian Marussi 
5270316f99cSCristian Marussi static int
5280316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
5290316f99cSCristian Marussi 					 u32 domain_id, u32 power_thresh_low,
5300316f99cSCristian Marussi 					 u32 power_thresh_high)
5310316f99cSCristian Marussi {
5320316f99cSCristian Marussi 	int ret = 0;
5330316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5340316f99cSCristian Marussi 
5350316f99cSCristian Marussi 	if (domain_id >= pi->num_domains ||
5360316f99cSCristian Marussi 	    power_thresh_low > power_thresh_high)
5370316f99cSCristian Marussi 		return -EINVAL;
5380316f99cSCristian Marussi 
5390316f99cSCristian Marussi 	/* Anything to do ? */
5400316f99cSCristian Marussi 	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
5410316f99cSCristian Marussi 	    THRESH_HIGH(pi, domain_id) == power_thresh_high)
5420316f99cSCristian Marussi 		return ret;
5430316f99cSCristian Marussi 
5440316f99cSCristian Marussi 	pi->states[domain_id].thresholds =
5450316f99cSCristian Marussi 		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
5460316f99cSCristian Marussi 		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
5470316f99cSCristian Marussi 
5480316f99cSCristian Marussi 	/* Update thresholds if notification already enabled */
5490316f99cSCristian Marussi 	if (pi->states[domain_id].meas_notif_enabled)
5500316f99cSCristian Marussi 		ret = scmi_powercap_notify(ph, domain_id,
5510316f99cSCristian Marussi 					   POWERCAP_MEASUREMENTS_NOTIFY,
5520316f99cSCristian Marussi 					   true);
5530316f99cSCristian Marussi 
5540316f99cSCristian Marussi 	return ret;
5550316f99cSCristian Marussi }
5560316f99cSCristian Marussi 
5570316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
5580316f99cSCristian Marussi 	.num_domains_get = scmi_powercap_num_domains_get,
5590316f99cSCristian Marussi 	.info_get = scmi_powercap_dom_info_get,
5600316f99cSCristian Marussi 	.cap_get = scmi_powercap_cap_get,
5610316f99cSCristian Marussi 	.cap_set = scmi_powercap_cap_set,
5620316f99cSCristian Marussi 	.pai_get = scmi_powercap_pai_get,
5630316f99cSCristian Marussi 	.pai_set = scmi_powercap_pai_set,
5640316f99cSCristian Marussi 	.measurements_get = scmi_powercap_measurements_get,
5650316f99cSCristian Marussi 	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
5660316f99cSCristian Marussi 	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
5670316f99cSCristian Marussi };
5680316f99cSCristian Marussi 
569*855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
570*855aa26eSCristian Marussi 					 u32 domain, struct scmi_fc_info **p_fc)
571*855aa26eSCristian Marussi {
572*855aa26eSCristian Marussi 	struct scmi_fc_info *fc;
573*855aa26eSCristian Marussi 
574*855aa26eSCristian Marussi 	fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
575*855aa26eSCristian Marussi 	if (!fc)
576*855aa26eSCristian Marussi 		return;
577*855aa26eSCristian Marussi 
578*855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
579*855aa26eSCristian Marussi 				   POWERCAP_CAP_SET, 4, domain,
580*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_addr,
581*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_db);
582*855aa26eSCristian Marussi 
583*855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
584*855aa26eSCristian Marussi 				   POWERCAP_CAP_GET, 4, domain,
585*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].get_addr, NULL);
586*855aa26eSCristian Marussi 
587*855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
588*855aa26eSCristian Marussi 				   POWERCAP_PAI_SET, 4, domain,
589*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_addr,
590*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_db);
591*855aa26eSCristian Marussi 
592*855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
593*855aa26eSCristian Marussi 				   POWERCAP_PAI_GET, 4, domain,
594*855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].get_addr, NULL);
595*855aa26eSCristian Marussi 
596*855aa26eSCristian Marussi 	*p_fc = fc;
597*855aa26eSCristian Marussi }
598*855aa26eSCristian Marussi 
5990316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
6000316f99cSCristian Marussi 				u32 domain, int message_id, bool enable)
6010316f99cSCristian Marussi {
6020316f99cSCristian Marussi 	int ret;
6030316f99cSCristian Marussi 	struct scmi_xfer *t;
6040316f99cSCristian Marussi 
6050316f99cSCristian Marussi 	switch (message_id) {
6060316f99cSCristian Marussi 	case POWERCAP_CAP_NOTIFY:
6070316f99cSCristian Marussi 	{
6080316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_cap *notify;
6090316f99cSCristian Marussi 
6100316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6110316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6120316f99cSCristian Marussi 		if (ret)
6130316f99cSCristian Marussi 			return ret;
6140316f99cSCristian Marussi 
6150316f99cSCristian Marussi 		notify = t->tx.buf;
6160316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6170316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6180316f99cSCristian Marussi 		break;
6190316f99cSCristian Marussi 	}
6200316f99cSCristian Marussi 	case POWERCAP_MEASUREMENTS_NOTIFY:
6210316f99cSCristian Marussi 	{
6220316f99cSCristian Marussi 		u32 low, high;
6230316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_thresh *notify;
6240316f99cSCristian Marussi 
6250316f99cSCristian Marussi 		/*
6260316f99cSCristian Marussi 		 * Note that we have to pick the most recently configured
6270316f99cSCristian Marussi 		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
6280316f99cSCristian Marussi 		 * enable request and we fail, complaining, if no thresholds
6290316f99cSCristian Marussi 		 * were ever set, since this is an indication the API has been
6300316f99cSCristian Marussi 		 * used wrongly.
6310316f99cSCristian Marussi 		 */
6320316f99cSCristian Marussi 		ret = scmi_powercap_measurements_threshold_get(ph, domain,
6330316f99cSCristian Marussi 							       &low, &high);
6340316f99cSCristian Marussi 		if (ret)
6350316f99cSCristian Marussi 			return ret;
6360316f99cSCristian Marussi 
6370316f99cSCristian Marussi 		if (enable && !low && !high) {
6380316f99cSCristian Marussi 			dev_err(ph->dev,
6390316f99cSCristian Marussi 				"Invalid Measurements Notify thresholds: %u/%u\n",
6400316f99cSCristian Marussi 				low, high);
6410316f99cSCristian Marussi 			return -EINVAL;
6420316f99cSCristian Marussi 		}
6430316f99cSCristian Marussi 
6440316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6450316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6460316f99cSCristian Marussi 		if (ret)
6470316f99cSCristian Marussi 			return ret;
6480316f99cSCristian Marussi 
6490316f99cSCristian Marussi 		notify = t->tx.buf;
6500316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6510316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6520316f99cSCristian Marussi 		notify->power_thresh_low = cpu_to_le32(low);
6530316f99cSCristian Marussi 		notify->power_thresh_high = cpu_to_le32(high);
6540316f99cSCristian Marussi 		break;
6550316f99cSCristian Marussi 	}
6560316f99cSCristian Marussi 	default:
6570316f99cSCristian Marussi 		return -EINVAL;
6580316f99cSCristian Marussi 	}
6590316f99cSCristian Marussi 
6600316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
6610316f99cSCristian Marussi 
6620316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
6630316f99cSCristian Marussi 	return ret;
6640316f99cSCristian Marussi }
6650316f99cSCristian Marussi 
6660316f99cSCristian Marussi static int
6670316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
6680316f99cSCristian Marussi 				 u8 evt_id, u32 src_id, bool enable)
6690316f99cSCristian Marussi {
6700316f99cSCristian Marussi 	int ret, cmd_id;
6710316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
6720316f99cSCristian Marussi 
6730316f99cSCristian Marussi 	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
6740316f99cSCristian Marussi 		return -EINVAL;
6750316f99cSCristian Marussi 
6760316f99cSCristian Marussi 	cmd_id = evt_2_cmd[evt_id];
6770316f99cSCristian Marussi 	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
6780316f99cSCristian Marussi 	if (ret)
6790316f99cSCristian Marussi 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
6800316f99cSCristian Marussi 			 evt_id, src_id, ret);
6810316f99cSCristian Marussi 	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
6820316f99cSCristian Marussi 		/*
6830316f99cSCristian Marussi 		 * On success save the current notification enabled state, so
6840316f99cSCristian Marussi 		 * as to be able to properly update the notification thresholds
6850316f99cSCristian Marussi 		 * when they are modified on a domain for which measurement
6860316f99cSCristian Marussi 		 * notifications were currently enabled.
6870316f99cSCristian Marussi 		 *
6880316f99cSCristian Marussi 		 * This is needed because the SCMI Notification core machinery
6890316f99cSCristian Marussi 		 * and API does not support passing per-notification custom
6900316f99cSCristian Marussi 		 * arguments at callback registration time.
6910316f99cSCristian Marussi 		 *
6920316f99cSCristian Marussi 		 * Note that this can be done here with a simple flag since the
6930316f99cSCristian Marussi 		 * SCMI core Notifications code takes care of keeping proper
6940316f99cSCristian Marussi 		 * per-domain enables refcounting, so that this helper function
6950316f99cSCristian Marussi 		 * will be called only once (for enables) when the first user
6960316f99cSCristian Marussi 		 * registers a callback on this domain and once more (disable)
6970316f99cSCristian Marussi 		 * when the last user de-registers its callback.
6980316f99cSCristian Marussi 		 */
6990316f99cSCristian Marussi 		pi->states[src_id].meas_notif_enabled = enable;
7000316f99cSCristian Marussi 
7010316f99cSCristian Marussi 	return ret;
7020316f99cSCristian Marussi }
7030316f99cSCristian Marussi 
7040316f99cSCristian Marussi static void *
7050316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
7060316f99cSCristian Marussi 				 u8 evt_id, ktime_t timestamp,
7070316f99cSCristian Marussi 				 const void *payld, size_t payld_sz,
7080316f99cSCristian Marussi 				 void *report, u32 *src_id)
7090316f99cSCristian Marussi {
7100316f99cSCristian Marussi 	void *rep = NULL;
7110316f99cSCristian Marussi 
7120316f99cSCristian Marussi 	switch (evt_id) {
7130316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_CAP_CHANGED:
7140316f99cSCristian Marussi 	{
7150316f99cSCristian Marussi 		const struct scmi_powercap_cap_changed_notify_payld *p = payld;
7160316f99cSCristian Marussi 		struct scmi_powercap_cap_changed_report *r = report;
7170316f99cSCristian Marussi 
7180316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7190316f99cSCristian Marussi 			break;
7200316f99cSCristian Marussi 
7210316f99cSCristian Marussi 		r->timestamp = timestamp;
7220316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7230316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7240316f99cSCristian Marussi 		r->power_cap = le32_to_cpu(p->power_cap);
7250316f99cSCristian Marussi 		r->pai = le32_to_cpu(p->pai);
7260316f99cSCristian Marussi 		*src_id = r->domain_id;
7270316f99cSCristian Marussi 		rep = r;
7280316f99cSCristian Marussi 		break;
7290316f99cSCristian Marussi 	}
7300316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
7310316f99cSCristian Marussi 	{
7320316f99cSCristian Marussi 		const struct scmi_powercap_meas_changed_notify_payld *p = payld;
7330316f99cSCristian Marussi 		struct scmi_powercap_meas_changed_report *r = report;
7340316f99cSCristian Marussi 
7350316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7360316f99cSCristian Marussi 			break;
7370316f99cSCristian Marussi 
7380316f99cSCristian Marussi 		r->timestamp = timestamp;
7390316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7400316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7410316f99cSCristian Marussi 		r->power = le32_to_cpu(p->power);
7420316f99cSCristian Marussi 		*src_id = r->domain_id;
7430316f99cSCristian Marussi 		rep = r;
7440316f99cSCristian Marussi 		break;
7450316f99cSCristian Marussi 	}
7460316f99cSCristian Marussi 	default:
7470316f99cSCristian Marussi 		break;
7480316f99cSCristian Marussi 	}
7490316f99cSCristian Marussi 
7500316f99cSCristian Marussi 	return rep;
7510316f99cSCristian Marussi }
7520316f99cSCristian Marussi 
7530316f99cSCristian Marussi static int
7540316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
7550316f99cSCristian Marussi {
7560316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
7570316f99cSCristian Marussi 
7580316f99cSCristian Marussi 	if (!pi)
7590316f99cSCristian Marussi 		return -EINVAL;
7600316f99cSCristian Marussi 
7610316f99cSCristian Marussi 	return pi->num_domains;
7620316f99cSCristian Marussi }
7630316f99cSCristian Marussi 
7640316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
7650316f99cSCristian Marussi 	{
7660316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
7670316f99cSCristian Marussi 		.max_payld_sz =
7680316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_notify_payld),
7690316f99cSCristian Marussi 		.max_report_sz =
7700316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_report),
7710316f99cSCristian Marussi 	},
7720316f99cSCristian Marussi 	{
7730316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
7740316f99cSCristian Marussi 		.max_payld_sz =
7750316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_notify_payld),
7760316f99cSCristian Marussi 		.max_report_sz =
7770316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_report),
7780316f99cSCristian Marussi 	},
7790316f99cSCristian Marussi };
7800316f99cSCristian Marussi 
7810316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
7820316f99cSCristian Marussi 	.get_num_sources = scmi_powercap_get_num_sources,
7830316f99cSCristian Marussi 	.set_notify_enabled = scmi_powercap_set_notify_enabled,
7840316f99cSCristian Marussi 	.fill_custom_report = scmi_powercap_fill_custom_report,
7850316f99cSCristian Marussi };
7860316f99cSCristian Marussi 
7870316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
7880316f99cSCristian Marussi 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
7890316f99cSCristian Marussi 	.ops = &powercap_event_ops,
7900316f99cSCristian Marussi 	.evts = powercap_events,
7910316f99cSCristian Marussi 	.num_events = ARRAY_SIZE(powercap_events),
7920316f99cSCristian Marussi };
7930316f99cSCristian Marussi 
7940316f99cSCristian Marussi static int
7950316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
7960316f99cSCristian Marussi {
7970316f99cSCristian Marussi 	int domain, ret;
7980316f99cSCristian Marussi 	u32 version;
7990316f99cSCristian Marussi 	struct powercap_info *pinfo;
8000316f99cSCristian Marussi 
8010316f99cSCristian Marussi 	ret = ph->xops->version_get(ph, &version);
8020316f99cSCristian Marussi 	if (ret)
8030316f99cSCristian Marussi 		return ret;
8040316f99cSCristian Marussi 
8050316f99cSCristian Marussi 	dev_dbg(ph->dev, "Powercap Version %d.%d\n",
8060316f99cSCristian Marussi 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
8070316f99cSCristian Marussi 
8080316f99cSCristian Marussi 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
8090316f99cSCristian Marussi 	if (!pinfo)
8100316f99cSCristian Marussi 		return -ENOMEM;
8110316f99cSCristian Marussi 
8120316f99cSCristian Marussi 	ret = scmi_powercap_attributes_get(ph, pinfo);
8130316f99cSCristian Marussi 	if (ret)
8140316f99cSCristian Marussi 		return ret;
8150316f99cSCristian Marussi 
8160316f99cSCristian Marussi 	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
8170316f99cSCristian Marussi 					sizeof(*pinfo->powercaps),
8180316f99cSCristian Marussi 					GFP_KERNEL);
8190316f99cSCristian Marussi 	if (!pinfo->powercaps)
8200316f99cSCristian Marussi 		return -ENOMEM;
8210316f99cSCristian Marussi 
8220316f99cSCristian Marussi 	/*
8230316f99cSCristian Marussi 	 * Note that any failure in retrieving any domain attribute leads to
8240316f99cSCristian Marussi 	 * the whole Powercap protocol initialization failure: this way the
8250316f99cSCristian Marussi 	 * reported Powercap domains are all assured, when accessed, to be well
8260316f99cSCristian Marussi 	 * formed and correlated by sane parent-child relationship (if any).
8270316f99cSCristian Marussi 	 */
8280316f99cSCristian Marussi 	for (domain = 0; domain < pinfo->num_domains; domain++) {
8290316f99cSCristian Marussi 		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
8300316f99cSCristian Marussi 		if (ret)
8310316f99cSCristian Marussi 			return ret;
832*855aa26eSCristian Marussi 
833*855aa26eSCristian Marussi 		if (pinfo->powercaps[domain].fastchannels)
834*855aa26eSCristian Marussi 			scmi_powercap_domain_init_fc(ph, domain,
835*855aa26eSCristian Marussi 						     &pinfo->powercaps[domain].fc_info);
8360316f99cSCristian Marussi 	}
8370316f99cSCristian Marussi 
8380316f99cSCristian Marussi 	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
8390316f99cSCristian Marussi 				     sizeof(*pinfo->states), GFP_KERNEL);
8400316f99cSCristian Marussi 	if (!pinfo->states)
8410316f99cSCristian Marussi 		return -ENOMEM;
8420316f99cSCristian Marussi 
8430316f99cSCristian Marussi 	pinfo->version = version;
8440316f99cSCristian Marussi 
8450316f99cSCristian Marussi 	return ph->set_priv(ph, pinfo);
8460316f99cSCristian Marussi }
8470316f99cSCristian Marussi 
8480316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
8490316f99cSCristian Marussi 	.id = SCMI_PROTOCOL_POWERCAP,
8500316f99cSCristian Marussi 	.owner = THIS_MODULE,
8510316f99cSCristian Marussi 	.instance_init = &scmi_powercap_protocol_init,
8520316f99cSCristian Marussi 	.ops = &powercap_proto_ops,
8530316f99cSCristian Marussi 	.events = &powercap_protocol_events,
8540316f99cSCristian Marussi };
8550316f99cSCristian Marussi 
8560316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
857