xref: /openbmc/linux/drivers/firmware/arm_scmi/powercap.c (revision 4e1a53b4030e0a28e17548f8f380fa38207ec300)
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 
15b27d04d5SCristian Marussi #include <trace/events/scmi.h>
16b27d04d5SCristian 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 
316*4e1a53b4SCristian Marussi static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
317*4e1a53b4SCristian Marussi 				   const struct scmi_powercap_info *dom,
318*4e1a53b4SCristian Marussi 				   u32 *power_cap)
319855aa26eSCristian Marussi {
320855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
321855aa26eSCristian Marussi 		*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
322b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
323*4e1a53b4SCristian Marussi 				   dom->id, *power_cap, 0);
324855aa26eSCristian Marussi 		return 0;
325855aa26eSCristian Marussi 	}
326855aa26eSCristian Marussi 
327*4e1a53b4SCristian Marussi 	return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
328*4e1a53b4SCristian Marussi }
329*4e1a53b4SCristian Marussi 
330*4e1a53b4SCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
331*4e1a53b4SCristian Marussi 				 u32 domain_id, u32 *power_cap)
332*4e1a53b4SCristian Marussi {
333*4e1a53b4SCristian Marussi 	const struct scmi_powercap_info *dom;
334*4e1a53b4SCristian Marussi 
335*4e1a53b4SCristian Marussi 	if (!power_cap)
336*4e1a53b4SCristian Marussi 		return -EINVAL;
337*4e1a53b4SCristian Marussi 
338*4e1a53b4SCristian Marussi 	dom = scmi_powercap_dom_info_get(ph, domain_id);
339*4e1a53b4SCristian Marussi 	if (!dom)
340*4e1a53b4SCristian Marussi 		return -EINVAL;
341*4e1a53b4SCristian Marussi 
342*4e1a53b4SCristian Marussi 	return __scmi_powercap_cap_get(ph, dom, power_cap);
343855aa26eSCristian Marussi }
344855aa26eSCristian Marussi 
345855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
346855aa26eSCristian Marussi 				      const struct scmi_powercap_info *pc,
347855aa26eSCristian Marussi 				      u32 power_cap, bool ignore_dresp)
3480316f99cSCristian Marussi {
3490316f99cSCristian Marussi 	int ret;
3500316f99cSCristian Marussi 	struct scmi_xfer *t;
3510316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
3520316f99cSCristian Marussi 
3530316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
3540316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
3550316f99cSCristian Marussi 	if (ret)
3560316f99cSCristian Marussi 		return ret;
3570316f99cSCristian Marussi 
3580316f99cSCristian Marussi 	msg = t->tx.buf;
359855aa26eSCristian Marussi 	msg->domain = cpu_to_le32(pc->id);
3600316f99cSCristian Marussi 	msg->flags =
3610316f99cSCristian Marussi 		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
3620316f99cSCristian Marussi 			    FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
3630316f99cSCristian Marussi 	msg->value = cpu_to_le32(power_cap);
3640316f99cSCristian Marussi 
3650316f99cSCristian Marussi 	if (!pc->async_powercap_cap_set || ignore_dresp) {
3660316f99cSCristian Marussi 		ret = ph->xops->do_xfer(ph, t);
3670316f99cSCristian Marussi 	} else {
3680316f99cSCristian Marussi 		ret = ph->xops->do_xfer_with_response(ph, t);
3690316f99cSCristian Marussi 		if (!ret) {
3700316f99cSCristian Marussi 			struct scmi_msg_resp_powercap_cap_set_complete *resp;
3710316f99cSCristian Marussi 
3720316f99cSCristian Marussi 			resp = t->rx.buf;
373855aa26eSCristian Marussi 			if (le32_to_cpu(resp->domain) == pc->id)
3740316f99cSCristian Marussi 				dev_dbg(ph->dev,
3750316f99cSCristian Marussi 					"Powercap ID %d CAP set async to %u\n",
376855aa26eSCristian Marussi 					pc->id,
3770316f99cSCristian Marussi 					get_unaligned_le32(&resp->power_cap));
3780316f99cSCristian Marussi 			else
3790316f99cSCristian Marussi 				ret = -EPROTO;
3800316f99cSCristian Marussi 		}
3810316f99cSCristian Marussi 	}
3820316f99cSCristian Marussi 
3830316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3840316f99cSCristian Marussi 	return ret;
3850316f99cSCristian Marussi }
3860316f99cSCristian Marussi 
387*4e1a53b4SCristian Marussi static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
388*4e1a53b4SCristian Marussi 				   struct powercap_info *pi, u32 domain_id,
389*4e1a53b4SCristian Marussi 				   u32 power_cap, bool ignore_dresp)
390855aa26eSCristian Marussi {
391*4e1a53b4SCristian Marussi 	int ret = -EINVAL;
392855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
393855aa26eSCristian Marussi 
394855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
395*4e1a53b4SCristian Marussi 	if (!pc || !pc->powercap_cap_config)
396*4e1a53b4SCristian Marussi 		return ret;
397*4e1a53b4SCristian Marussi 
398*4e1a53b4SCristian Marussi 	if (power_cap &&
399*4e1a53b4SCristian Marussi 	    (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
400*4e1a53b4SCristian Marussi 		return ret;
401855aa26eSCristian Marussi 
402855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
403855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
404855aa26eSCristian Marussi 
405855aa26eSCristian Marussi 		iowrite32(power_cap, fci->set_addr);
406855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
407b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
408b27d04d5SCristian Marussi 				   domain_id, power_cap, 0);
409*4e1a53b4SCristian Marussi 		ret = 0;
410*4e1a53b4SCristian Marussi 	} else {
411*4e1a53b4SCristian Marussi 		ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
412*4e1a53b4SCristian Marussi 						 ignore_dresp);
413855aa26eSCristian Marussi 	}
414855aa26eSCristian Marussi 
415*4e1a53b4SCristian Marussi 	return ret;
416*4e1a53b4SCristian Marussi }
417*4e1a53b4SCristian Marussi 
418*4e1a53b4SCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
419*4e1a53b4SCristian Marussi 				 u32 domain_id, u32 power_cap,
420*4e1a53b4SCristian Marussi 				 bool ignore_dresp)
421*4e1a53b4SCristian Marussi {
422*4e1a53b4SCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
423*4e1a53b4SCristian Marussi 
424*4e1a53b4SCristian Marussi 	return __scmi_powercap_cap_set(ph, pi, domain_id,
425*4e1a53b4SCristian Marussi 				       power_cap, ignore_dresp);
426855aa26eSCristian Marussi }
427855aa26eSCristian Marussi 
428855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
4290316f99cSCristian Marussi 				      u32 domain_id, u32 *pai)
4300316f99cSCristian Marussi {
4310316f99cSCristian Marussi 	int ret;
4320316f99cSCristian Marussi 	struct scmi_xfer *t;
4330316f99cSCristian Marussi 
4340316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
4350316f99cSCristian Marussi 				      sizeof(u32), &t);
4360316f99cSCristian Marussi 	if (ret)
4370316f99cSCristian Marussi 		return ret;
4380316f99cSCristian Marussi 
4390316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
4400316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4410316f99cSCristian Marussi 	if (!ret)
4420316f99cSCristian Marussi 		*pai = get_unaligned_le32(t->rx.buf);
4430316f99cSCristian Marussi 
4440316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4450316f99cSCristian Marussi 
4460316f99cSCristian Marussi 	return ret;
4470316f99cSCristian Marussi }
4480316f99cSCristian Marussi 
449855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
450855aa26eSCristian Marussi 				 u32 domain_id, u32 *pai)
451855aa26eSCristian Marussi {
452855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
453855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
454855aa26eSCristian Marussi 
455855aa26eSCristian Marussi 	if (!pai || domain_id >= pi->num_domains)
456855aa26eSCristian Marussi 		return -EINVAL;
457855aa26eSCristian Marussi 
458855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
459855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
460855aa26eSCristian Marussi 		*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
461b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
462b27d04d5SCristian Marussi 				   domain_id, *pai, 0);
463855aa26eSCristian Marussi 		return 0;
464855aa26eSCristian Marussi 	}
465855aa26eSCristian Marussi 
466855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
467855aa26eSCristian Marussi }
468855aa26eSCristian Marussi 
469855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
4700316f99cSCristian Marussi 				      u32 domain_id, u32 pai)
4710316f99cSCristian Marussi {
4720316f99cSCristian Marussi 	int ret;
4730316f99cSCristian Marussi 	struct scmi_xfer *t;
4740316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
4750316f99cSCristian Marussi 
4760316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
4770316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
4780316f99cSCristian Marussi 	if (ret)
4790316f99cSCristian Marussi 		return ret;
4800316f99cSCristian Marussi 
4810316f99cSCristian Marussi 	msg = t->tx.buf;
4820316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
4830316f99cSCristian Marussi 	msg->flags = cpu_to_le32(0);
4840316f99cSCristian Marussi 	msg->value = cpu_to_le32(pai);
4850316f99cSCristian Marussi 
4860316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4870316f99cSCristian Marussi 
4880316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4890316f99cSCristian Marussi 	return ret;
4900316f99cSCristian Marussi }
4910316f99cSCristian Marussi 
492855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
493855aa26eSCristian Marussi 				 u32 domain_id, u32 pai)
494855aa26eSCristian Marussi {
495855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
496855aa26eSCristian Marussi 
497855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
498855aa26eSCristian Marussi 	if (!pc || !pc->powercap_pai_config || !pai ||
499855aa26eSCristian Marussi 	    pai < pc->min_pai || pai > pc->max_pai)
500855aa26eSCristian Marussi 		return -EINVAL;
501855aa26eSCristian Marussi 
502855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
503855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
504855aa26eSCristian Marussi 
505b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
506b27d04d5SCristian Marussi 				   domain_id, pai, 0);
507855aa26eSCristian Marussi 		iowrite32(pai, fci->set_addr);
508855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
509855aa26eSCristian Marussi 		return 0;
510855aa26eSCristian Marussi 	}
511855aa26eSCristian Marussi 
512855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
513855aa26eSCristian Marussi }
514855aa26eSCristian Marussi 
5150316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
5160316f99cSCristian Marussi 					  u32 domain_id, u32 *average_power,
5170316f99cSCristian Marussi 					  u32 *pai)
5180316f99cSCristian Marussi {
5190316f99cSCristian Marussi 	int ret;
5200316f99cSCristian Marussi 	struct scmi_xfer *t;
5210316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_meas_get *resp;
5220316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
5230316f99cSCristian Marussi 
5240316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
5250316f99cSCristian Marussi 	if (!pc || !pc->powercap_monitoring || !pai || !average_power)
5260316f99cSCristian Marussi 		return -EINVAL;
5270316f99cSCristian Marussi 
5280316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
5290316f99cSCristian Marussi 				      sizeof(u32), sizeof(*resp), &t);
5300316f99cSCristian Marussi 	if (ret)
5310316f99cSCristian Marussi 		return ret;
5320316f99cSCristian Marussi 
5330316f99cSCristian Marussi 	resp = t->rx.buf;
5340316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
5350316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
5360316f99cSCristian Marussi 	if (!ret) {
5370316f99cSCristian Marussi 		*average_power = le32_to_cpu(resp->power);
5380316f99cSCristian Marussi 		*pai = le32_to_cpu(resp->pai);
5390316f99cSCristian Marussi 	}
5400316f99cSCristian Marussi 
5410316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
5420316f99cSCristian Marussi 	return ret;
5430316f99cSCristian Marussi }
5440316f99cSCristian Marussi 
5450316f99cSCristian Marussi static int
5460316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
5470316f99cSCristian Marussi 					 u32 domain_id, u32 *power_thresh_low,
5480316f99cSCristian Marussi 					 u32 *power_thresh_high)
5490316f99cSCristian Marussi {
5500316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5510316f99cSCristian Marussi 
5520316f99cSCristian Marussi 	if (!power_thresh_low || !power_thresh_high ||
5530316f99cSCristian Marussi 	    domain_id >= pi->num_domains)
5540316f99cSCristian Marussi 		return -EINVAL;
5550316f99cSCristian Marussi 
5560316f99cSCristian Marussi 	*power_thresh_low =  THRESH_LOW(pi, domain_id);
5570316f99cSCristian Marussi 	*power_thresh_high = THRESH_HIGH(pi, domain_id);
5580316f99cSCristian Marussi 
5590316f99cSCristian Marussi 	return 0;
5600316f99cSCristian Marussi }
5610316f99cSCristian Marussi 
5620316f99cSCristian Marussi static int
5630316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
5640316f99cSCristian Marussi 					 u32 domain_id, u32 power_thresh_low,
5650316f99cSCristian Marussi 					 u32 power_thresh_high)
5660316f99cSCristian Marussi {
5670316f99cSCristian Marussi 	int ret = 0;
5680316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5690316f99cSCristian Marussi 
5700316f99cSCristian Marussi 	if (domain_id >= pi->num_domains ||
5710316f99cSCristian Marussi 	    power_thresh_low > power_thresh_high)
5720316f99cSCristian Marussi 		return -EINVAL;
5730316f99cSCristian Marussi 
5740316f99cSCristian Marussi 	/* Anything to do ? */
5750316f99cSCristian Marussi 	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
5760316f99cSCristian Marussi 	    THRESH_HIGH(pi, domain_id) == power_thresh_high)
5770316f99cSCristian Marussi 		return ret;
5780316f99cSCristian Marussi 
5790316f99cSCristian Marussi 	pi->states[domain_id].thresholds =
5800316f99cSCristian Marussi 		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
5810316f99cSCristian Marussi 		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
5820316f99cSCristian Marussi 
5830316f99cSCristian Marussi 	/* Update thresholds if notification already enabled */
5840316f99cSCristian Marussi 	if (pi->states[domain_id].meas_notif_enabled)
5850316f99cSCristian Marussi 		ret = scmi_powercap_notify(ph, domain_id,
5860316f99cSCristian Marussi 					   POWERCAP_MEASUREMENTS_NOTIFY,
5870316f99cSCristian Marussi 					   true);
5880316f99cSCristian Marussi 
5890316f99cSCristian Marussi 	return ret;
5900316f99cSCristian Marussi }
5910316f99cSCristian Marussi 
5920316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
5930316f99cSCristian Marussi 	.num_domains_get = scmi_powercap_num_domains_get,
5940316f99cSCristian Marussi 	.info_get = scmi_powercap_dom_info_get,
5950316f99cSCristian Marussi 	.cap_get = scmi_powercap_cap_get,
5960316f99cSCristian Marussi 	.cap_set = scmi_powercap_cap_set,
5970316f99cSCristian Marussi 	.pai_get = scmi_powercap_pai_get,
5980316f99cSCristian Marussi 	.pai_set = scmi_powercap_pai_set,
5990316f99cSCristian Marussi 	.measurements_get = scmi_powercap_measurements_get,
6000316f99cSCristian Marussi 	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
6010316f99cSCristian Marussi 	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
6020316f99cSCristian Marussi };
6030316f99cSCristian Marussi 
604855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
605855aa26eSCristian Marussi 					 u32 domain, struct scmi_fc_info **p_fc)
606855aa26eSCristian Marussi {
607855aa26eSCristian Marussi 	struct scmi_fc_info *fc;
608855aa26eSCristian Marussi 
609855aa26eSCristian Marussi 	fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
610855aa26eSCristian Marussi 	if (!fc)
611855aa26eSCristian Marussi 		return;
612855aa26eSCristian Marussi 
613855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
614855aa26eSCristian Marussi 				   POWERCAP_CAP_SET, 4, domain,
615855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_addr,
616855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_db);
617855aa26eSCristian Marussi 
618855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
619855aa26eSCristian Marussi 				   POWERCAP_CAP_GET, 4, domain,
620855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].get_addr, NULL);
621855aa26eSCristian Marussi 
622855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
623855aa26eSCristian Marussi 				   POWERCAP_PAI_SET, 4, domain,
624855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_addr,
625855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_db);
626855aa26eSCristian Marussi 
627855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
628855aa26eSCristian Marussi 				   POWERCAP_PAI_GET, 4, domain,
629855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].get_addr, NULL);
630855aa26eSCristian Marussi 
631855aa26eSCristian Marussi 	*p_fc = fc;
632855aa26eSCristian Marussi }
633855aa26eSCristian Marussi 
6340316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
6350316f99cSCristian Marussi 				u32 domain, int message_id, bool enable)
6360316f99cSCristian Marussi {
6370316f99cSCristian Marussi 	int ret;
6380316f99cSCristian Marussi 	struct scmi_xfer *t;
6390316f99cSCristian Marussi 
6400316f99cSCristian Marussi 	switch (message_id) {
6410316f99cSCristian Marussi 	case POWERCAP_CAP_NOTIFY:
6420316f99cSCristian Marussi 	{
6430316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_cap *notify;
6440316f99cSCristian Marussi 
6450316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6460316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6470316f99cSCristian Marussi 		if (ret)
6480316f99cSCristian Marussi 			return ret;
6490316f99cSCristian Marussi 
6500316f99cSCristian Marussi 		notify = t->tx.buf;
6510316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6520316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6530316f99cSCristian Marussi 		break;
6540316f99cSCristian Marussi 	}
6550316f99cSCristian Marussi 	case POWERCAP_MEASUREMENTS_NOTIFY:
6560316f99cSCristian Marussi 	{
6570316f99cSCristian Marussi 		u32 low, high;
6580316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_thresh *notify;
6590316f99cSCristian Marussi 
6600316f99cSCristian Marussi 		/*
6610316f99cSCristian Marussi 		 * Note that we have to pick the most recently configured
6620316f99cSCristian Marussi 		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
6630316f99cSCristian Marussi 		 * enable request and we fail, complaining, if no thresholds
6640316f99cSCristian Marussi 		 * were ever set, since this is an indication the API has been
6650316f99cSCristian Marussi 		 * used wrongly.
6660316f99cSCristian Marussi 		 */
6670316f99cSCristian Marussi 		ret = scmi_powercap_measurements_threshold_get(ph, domain,
6680316f99cSCristian Marussi 							       &low, &high);
6690316f99cSCristian Marussi 		if (ret)
6700316f99cSCristian Marussi 			return ret;
6710316f99cSCristian Marussi 
6720316f99cSCristian Marussi 		if (enable && !low && !high) {
6730316f99cSCristian Marussi 			dev_err(ph->dev,
6740316f99cSCristian Marussi 				"Invalid Measurements Notify thresholds: %u/%u\n",
6750316f99cSCristian Marussi 				low, high);
6760316f99cSCristian Marussi 			return -EINVAL;
6770316f99cSCristian Marussi 		}
6780316f99cSCristian Marussi 
6790316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
6800316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
6810316f99cSCristian Marussi 		if (ret)
6820316f99cSCristian Marussi 			return ret;
6830316f99cSCristian Marussi 
6840316f99cSCristian Marussi 		notify = t->tx.buf;
6850316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
6860316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
6870316f99cSCristian Marussi 		notify->power_thresh_low = cpu_to_le32(low);
6880316f99cSCristian Marussi 		notify->power_thresh_high = cpu_to_le32(high);
6890316f99cSCristian Marussi 		break;
6900316f99cSCristian Marussi 	}
6910316f99cSCristian Marussi 	default:
6920316f99cSCristian Marussi 		return -EINVAL;
6930316f99cSCristian Marussi 	}
6940316f99cSCristian Marussi 
6950316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
6960316f99cSCristian Marussi 
6970316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
6980316f99cSCristian Marussi 	return ret;
6990316f99cSCristian Marussi }
7000316f99cSCristian Marussi 
7010316f99cSCristian Marussi static int
7020316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
7030316f99cSCristian Marussi 				 u8 evt_id, u32 src_id, bool enable)
7040316f99cSCristian Marussi {
7050316f99cSCristian Marussi 	int ret, cmd_id;
7060316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
7070316f99cSCristian Marussi 
7080316f99cSCristian Marussi 	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
7090316f99cSCristian Marussi 		return -EINVAL;
7100316f99cSCristian Marussi 
7110316f99cSCristian Marussi 	cmd_id = evt_2_cmd[evt_id];
7120316f99cSCristian Marussi 	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
7130316f99cSCristian Marussi 	if (ret)
7140316f99cSCristian Marussi 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
7150316f99cSCristian Marussi 			 evt_id, src_id, ret);
7160316f99cSCristian Marussi 	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
7170316f99cSCristian Marussi 		/*
7180316f99cSCristian Marussi 		 * On success save the current notification enabled state, so
7190316f99cSCristian Marussi 		 * as to be able to properly update the notification thresholds
7200316f99cSCristian Marussi 		 * when they are modified on a domain for which measurement
7210316f99cSCristian Marussi 		 * notifications were currently enabled.
7220316f99cSCristian Marussi 		 *
7230316f99cSCristian Marussi 		 * This is needed because the SCMI Notification core machinery
7240316f99cSCristian Marussi 		 * and API does not support passing per-notification custom
7250316f99cSCristian Marussi 		 * arguments at callback registration time.
7260316f99cSCristian Marussi 		 *
7270316f99cSCristian Marussi 		 * Note that this can be done here with a simple flag since the
7280316f99cSCristian Marussi 		 * SCMI core Notifications code takes care of keeping proper
7290316f99cSCristian Marussi 		 * per-domain enables refcounting, so that this helper function
7300316f99cSCristian Marussi 		 * will be called only once (for enables) when the first user
7310316f99cSCristian Marussi 		 * registers a callback on this domain and once more (disable)
7320316f99cSCristian Marussi 		 * when the last user de-registers its callback.
7330316f99cSCristian Marussi 		 */
7340316f99cSCristian Marussi 		pi->states[src_id].meas_notif_enabled = enable;
7350316f99cSCristian Marussi 
7360316f99cSCristian Marussi 	return ret;
7370316f99cSCristian Marussi }
7380316f99cSCristian Marussi 
7390316f99cSCristian Marussi static void *
7400316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
7410316f99cSCristian Marussi 				 u8 evt_id, ktime_t timestamp,
7420316f99cSCristian Marussi 				 const void *payld, size_t payld_sz,
7430316f99cSCristian Marussi 				 void *report, u32 *src_id)
7440316f99cSCristian Marussi {
7450316f99cSCristian Marussi 	void *rep = NULL;
7460316f99cSCristian Marussi 
7470316f99cSCristian Marussi 	switch (evt_id) {
7480316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_CAP_CHANGED:
7490316f99cSCristian Marussi 	{
7500316f99cSCristian Marussi 		const struct scmi_powercap_cap_changed_notify_payld *p = payld;
7510316f99cSCristian Marussi 		struct scmi_powercap_cap_changed_report *r = report;
7520316f99cSCristian Marussi 
7530316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7540316f99cSCristian Marussi 			break;
7550316f99cSCristian Marussi 
7560316f99cSCristian Marussi 		r->timestamp = timestamp;
7570316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7580316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7590316f99cSCristian Marussi 		r->power_cap = le32_to_cpu(p->power_cap);
7600316f99cSCristian Marussi 		r->pai = le32_to_cpu(p->pai);
7610316f99cSCristian Marussi 		*src_id = r->domain_id;
7620316f99cSCristian Marussi 		rep = r;
7630316f99cSCristian Marussi 		break;
7640316f99cSCristian Marussi 	}
7650316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
7660316f99cSCristian Marussi 	{
7670316f99cSCristian Marussi 		const struct scmi_powercap_meas_changed_notify_payld *p = payld;
7680316f99cSCristian Marussi 		struct scmi_powercap_meas_changed_report *r = report;
7690316f99cSCristian Marussi 
7700316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
7710316f99cSCristian Marussi 			break;
7720316f99cSCristian Marussi 
7730316f99cSCristian Marussi 		r->timestamp = timestamp;
7740316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
7750316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
7760316f99cSCristian Marussi 		r->power = le32_to_cpu(p->power);
7770316f99cSCristian Marussi 		*src_id = r->domain_id;
7780316f99cSCristian Marussi 		rep = r;
7790316f99cSCristian Marussi 		break;
7800316f99cSCristian Marussi 	}
7810316f99cSCristian Marussi 	default:
7820316f99cSCristian Marussi 		break;
7830316f99cSCristian Marussi 	}
7840316f99cSCristian Marussi 
7850316f99cSCristian Marussi 	return rep;
7860316f99cSCristian Marussi }
7870316f99cSCristian Marussi 
7880316f99cSCristian Marussi static int
7890316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
7900316f99cSCristian Marussi {
7910316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
7920316f99cSCristian Marussi 
7930316f99cSCristian Marussi 	if (!pi)
7940316f99cSCristian Marussi 		return -EINVAL;
7950316f99cSCristian Marussi 
7960316f99cSCristian Marussi 	return pi->num_domains;
7970316f99cSCristian Marussi }
7980316f99cSCristian Marussi 
7990316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
8000316f99cSCristian Marussi 	{
8010316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
8020316f99cSCristian Marussi 		.max_payld_sz =
8030316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_notify_payld),
8040316f99cSCristian Marussi 		.max_report_sz =
8050316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_report),
8060316f99cSCristian Marussi 	},
8070316f99cSCristian Marussi 	{
8080316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
8090316f99cSCristian Marussi 		.max_payld_sz =
8100316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_notify_payld),
8110316f99cSCristian Marussi 		.max_report_sz =
8120316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_report),
8130316f99cSCristian Marussi 	},
8140316f99cSCristian Marussi };
8150316f99cSCristian Marussi 
8160316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
8170316f99cSCristian Marussi 	.get_num_sources = scmi_powercap_get_num_sources,
8180316f99cSCristian Marussi 	.set_notify_enabled = scmi_powercap_set_notify_enabled,
8190316f99cSCristian Marussi 	.fill_custom_report = scmi_powercap_fill_custom_report,
8200316f99cSCristian Marussi };
8210316f99cSCristian Marussi 
8220316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
8230316f99cSCristian Marussi 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
8240316f99cSCristian Marussi 	.ops = &powercap_event_ops,
8250316f99cSCristian Marussi 	.evts = powercap_events,
8260316f99cSCristian Marussi 	.num_events = ARRAY_SIZE(powercap_events),
8270316f99cSCristian Marussi };
8280316f99cSCristian Marussi 
8290316f99cSCristian Marussi static int
8300316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
8310316f99cSCristian Marussi {
8320316f99cSCristian Marussi 	int domain, ret;
8330316f99cSCristian Marussi 	u32 version;
8340316f99cSCristian Marussi 	struct powercap_info *pinfo;
8350316f99cSCristian Marussi 
8360316f99cSCristian Marussi 	ret = ph->xops->version_get(ph, &version);
8370316f99cSCristian Marussi 	if (ret)
8380316f99cSCristian Marussi 		return ret;
8390316f99cSCristian Marussi 
8400316f99cSCristian Marussi 	dev_dbg(ph->dev, "Powercap Version %d.%d\n",
8410316f99cSCristian Marussi 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
8420316f99cSCristian Marussi 
8430316f99cSCristian Marussi 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
8440316f99cSCristian Marussi 	if (!pinfo)
8450316f99cSCristian Marussi 		return -ENOMEM;
8460316f99cSCristian Marussi 
8470316f99cSCristian Marussi 	ret = scmi_powercap_attributes_get(ph, pinfo);
8480316f99cSCristian Marussi 	if (ret)
8490316f99cSCristian Marussi 		return ret;
8500316f99cSCristian Marussi 
8510316f99cSCristian Marussi 	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
8520316f99cSCristian Marussi 					sizeof(*pinfo->powercaps),
8530316f99cSCristian Marussi 					GFP_KERNEL);
8540316f99cSCristian Marussi 	if (!pinfo->powercaps)
8550316f99cSCristian Marussi 		return -ENOMEM;
8560316f99cSCristian Marussi 
8570316f99cSCristian Marussi 	/*
8580316f99cSCristian Marussi 	 * Note that any failure in retrieving any domain attribute leads to
8590316f99cSCristian Marussi 	 * the whole Powercap protocol initialization failure: this way the
8600316f99cSCristian Marussi 	 * reported Powercap domains are all assured, when accessed, to be well
8610316f99cSCristian Marussi 	 * formed and correlated by sane parent-child relationship (if any).
8620316f99cSCristian Marussi 	 */
8630316f99cSCristian Marussi 	for (domain = 0; domain < pinfo->num_domains; domain++) {
8640316f99cSCristian Marussi 		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
8650316f99cSCristian Marussi 		if (ret)
8660316f99cSCristian Marussi 			return ret;
867855aa26eSCristian Marussi 
868855aa26eSCristian Marussi 		if (pinfo->powercaps[domain].fastchannels)
869855aa26eSCristian Marussi 			scmi_powercap_domain_init_fc(ph, domain,
870855aa26eSCristian Marussi 						     &pinfo->powercaps[domain].fc_info);
8710316f99cSCristian Marussi 	}
8720316f99cSCristian Marussi 
8730316f99cSCristian Marussi 	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
8740316f99cSCristian Marussi 				     sizeof(*pinfo->states), GFP_KERNEL);
8750316f99cSCristian Marussi 	if (!pinfo->states)
8760316f99cSCristian Marussi 		return -ENOMEM;
8770316f99cSCristian Marussi 
8780316f99cSCristian Marussi 	pinfo->version = version;
8790316f99cSCristian Marussi 
8800316f99cSCristian Marussi 	return ph->set_priv(ph, pinfo);
8810316f99cSCristian Marussi }
8820316f99cSCristian Marussi 
8830316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
8840316f99cSCristian Marussi 	.id = SCMI_PROTOCOL_POWERCAP,
8850316f99cSCristian Marussi 	.owner = THIS_MODULE,
8860316f99cSCristian Marussi 	.instance_init = &scmi_powercap_protocol_init,
8870316f99cSCristian Marussi 	.ops = &powercap_proto_ops,
8880316f99cSCristian Marussi 	.events = &powercap_protocol_events,
8890316f99cSCristian Marussi };
8900316f99cSCristian Marussi 
8910316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
892