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 {
111*758cd5fcSCristian Marussi 	bool enabled;
112*758cd5fcSCristian Marussi 	u32 last_pcap;
1130316f99cSCristian Marussi 	bool meas_notif_enabled;
1140316f99cSCristian Marussi 	u64 thresholds;
1150316f99cSCristian Marussi #define THRESH_LOW(p, id)				\
1160316f99cSCristian Marussi 	(lower_32_bits((p)->states[(id)].thresholds))
1170316f99cSCristian Marussi #define THRESH_HIGH(p, id)				\
1180316f99cSCristian Marussi 	(upper_32_bits((p)->states[(id)].thresholds))
1190316f99cSCristian Marussi };
1200316f99cSCristian Marussi 
1210316f99cSCristian Marussi struct powercap_info {
1220316f99cSCristian Marussi 	u32 version;
1230316f99cSCristian Marussi 	int num_domains;
1240316f99cSCristian Marussi 	struct scmi_powercap_state *states;
1250316f99cSCristian Marussi 	struct scmi_powercap_info *powercaps;
1260316f99cSCristian Marussi };
1270316f99cSCristian Marussi 
1280316f99cSCristian Marussi static enum scmi_powercap_protocol_cmd evt_2_cmd[] = {
1290316f99cSCristian Marussi 	POWERCAP_CAP_NOTIFY,
1300316f99cSCristian Marussi 	POWERCAP_MEASUREMENTS_NOTIFY,
1310316f99cSCristian Marussi };
1320316f99cSCristian Marussi 
1330316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
1340316f99cSCristian Marussi 				u32 domain, int message_id, bool enable);
1350316f99cSCristian Marussi 
1360316f99cSCristian Marussi static int
scmi_powercap_attributes_get(const struct scmi_protocol_handle * ph,struct powercap_info * pi)1370316f99cSCristian Marussi scmi_powercap_attributes_get(const struct scmi_protocol_handle *ph,
1380316f99cSCristian Marussi 			     struct powercap_info *pi)
1390316f99cSCristian Marussi {
1400316f99cSCristian Marussi 	int ret;
1410316f99cSCristian Marussi 	struct scmi_xfer *t;
1420316f99cSCristian Marussi 
1430316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, PROTOCOL_ATTRIBUTES, 0,
1440316f99cSCristian Marussi 				      sizeof(u32), &t);
1450316f99cSCristian Marussi 	if (ret)
1460316f99cSCristian Marussi 		return ret;
1470316f99cSCristian Marussi 
1480316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1490316f99cSCristian Marussi 	if (!ret) {
1500316f99cSCristian Marussi 		u32 attributes;
1510316f99cSCristian Marussi 
1520316f99cSCristian Marussi 		attributes = get_unaligned_le32(t->rx.buf);
1530316f99cSCristian Marussi 		pi->num_domains = FIELD_GET(GENMASK(15, 0), attributes);
1540316f99cSCristian Marussi 	}
1550316f99cSCristian Marussi 
1560316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
1570316f99cSCristian Marussi 	return ret;
1580316f99cSCristian Marussi }
1590316f99cSCristian Marussi 
1600316f99cSCristian Marussi static inline int
scmi_powercap_validate(unsigned int min_val,unsigned int max_val,unsigned int step_val,bool configurable)1610316f99cSCristian Marussi scmi_powercap_validate(unsigned int min_val, unsigned int max_val,
1620316f99cSCristian Marussi 		       unsigned int step_val, bool configurable)
1630316f99cSCristian Marussi {
1640316f99cSCristian Marussi 	if (!min_val || !max_val)
1650316f99cSCristian Marussi 		return -EPROTO;
1660316f99cSCristian Marussi 
1670316f99cSCristian Marussi 	if ((configurable && min_val == max_val) ||
1680316f99cSCristian Marussi 	    (!configurable && min_val != max_val))
1690316f99cSCristian Marussi 		return -EPROTO;
1700316f99cSCristian Marussi 
1710316f99cSCristian Marussi 	if (min_val != max_val && !step_val)
1720316f99cSCristian Marussi 		return -EPROTO;
1730316f99cSCristian Marussi 
1740316f99cSCristian Marussi 	return 0;
1750316f99cSCristian Marussi }
1760316f99cSCristian Marussi 
1770316f99cSCristian Marussi static int
scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle * ph,struct powercap_info * pinfo,u32 domain)1780316f99cSCristian Marussi scmi_powercap_domain_attributes_get(const struct scmi_protocol_handle *ph,
1790316f99cSCristian Marussi 				    struct powercap_info *pinfo, u32 domain)
1800316f99cSCristian Marussi {
1810316f99cSCristian Marussi 	int ret;
1820316f99cSCristian Marussi 	u32 flags;
1830316f99cSCristian Marussi 	struct scmi_xfer *t;
1840316f99cSCristian Marussi 	struct scmi_powercap_info *dom_info = pinfo->powercaps + domain;
1850316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_domain_attributes *resp;
1860316f99cSCristian Marussi 
1870316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_DOMAIN_ATTRIBUTES,
1880316f99cSCristian Marussi 				      sizeof(domain), sizeof(*resp), &t);
1890316f99cSCristian Marussi 	if (ret)
1900316f99cSCristian Marussi 		return ret;
1910316f99cSCristian Marussi 
1920316f99cSCristian Marussi 	put_unaligned_le32(domain, t->tx.buf);
1930316f99cSCristian Marussi 	resp = t->rx.buf;
1940316f99cSCristian Marussi 
1950316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
1960316f99cSCristian Marussi 	if (!ret) {
1970316f99cSCristian Marussi 		flags = le32_to_cpu(resp->attributes);
1980316f99cSCristian Marussi 
1990316f99cSCristian Marussi 		dom_info->id = domain;
2000316f99cSCristian Marussi 		dom_info->notify_powercap_cap_change =
2010316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CHANGE_NOTIFY(flags);
2020316f99cSCristian Marussi 		dom_info->notify_powercap_measurement_change =
2030316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MEASUREMENTS_CHANGE_NOTIFY(flags);
2040316f99cSCristian Marussi 		dom_info->async_powercap_cap_set =
2050316f99cSCristian Marussi 			SUPPORTS_ASYNC_POWERCAP_CAP_SET(flags);
2060316f99cSCristian Marussi 		dom_info->powercap_cap_config =
2070316f99cSCristian Marussi 			SUPPORTS_POWERCAP_CAP_CONFIGURATION(flags);
2080316f99cSCristian Marussi 		dom_info->powercap_monitoring =
2090316f99cSCristian Marussi 			SUPPORTS_POWERCAP_MONITORING(flags);
2100316f99cSCristian Marussi 		dom_info->powercap_pai_config =
2110316f99cSCristian Marussi 			SUPPORTS_POWERCAP_PAI_CONFIGURATION(flags);
2120316f99cSCristian Marussi 		dom_info->powercap_scale_mw =
2130316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_MW(flags);
2140316f99cSCristian Marussi 		dom_info->powercap_scale_uw =
2150316f99cSCristian Marussi 			SUPPORTS_POWER_UNITS_UW(flags);
216855aa26eSCristian Marussi 		dom_info->fastchannels =
217855aa26eSCristian Marussi 			SUPPORTS_POWERCAP_FASTCHANNELS(flags);
2180316f99cSCristian Marussi 
2190316f99cSCristian Marussi 		strscpy(dom_info->name, resp->name, SCMI_SHORT_NAME_MAX_SIZE);
2200316f99cSCristian Marussi 
2210316f99cSCristian Marussi 		dom_info->min_pai = le32_to_cpu(resp->min_pai);
2220316f99cSCristian Marussi 		dom_info->max_pai = le32_to_cpu(resp->max_pai);
2230316f99cSCristian Marussi 		dom_info->pai_step = le32_to_cpu(resp->pai_step);
2240316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_pai,
2250316f99cSCristian Marussi 					     dom_info->max_pai,
2260316f99cSCristian Marussi 					     dom_info->pai_step,
2270316f99cSCristian Marussi 					     dom_info->powercap_pai_config);
2280316f99cSCristian Marussi 		if (ret) {
2290316f99cSCristian Marussi 			dev_err(ph->dev,
2300316f99cSCristian Marussi 				"Platform reported inconsistent PAI config for domain %d - %s\n",
2310316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2320316f99cSCristian Marussi 			goto clean;
2330316f99cSCristian Marussi 		}
2340316f99cSCristian Marussi 
2350316f99cSCristian Marussi 		dom_info->min_power_cap = le32_to_cpu(resp->min_power_cap);
2360316f99cSCristian Marussi 		dom_info->max_power_cap = le32_to_cpu(resp->max_power_cap);
2370316f99cSCristian Marussi 		dom_info->power_cap_step = le32_to_cpu(resp->power_cap_step);
2380316f99cSCristian Marussi 		ret = scmi_powercap_validate(dom_info->min_power_cap,
2390316f99cSCristian Marussi 					     dom_info->max_power_cap,
2400316f99cSCristian Marussi 					     dom_info->power_cap_step,
2410316f99cSCristian Marussi 					     dom_info->powercap_cap_config);
2420316f99cSCristian Marussi 		if (ret) {
2430316f99cSCristian Marussi 			dev_err(ph->dev,
2440316f99cSCristian Marussi 				"Platform reported inconsistent CAP config for domain %d - %s\n",
2450316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2460316f99cSCristian Marussi 			goto clean;
2470316f99cSCristian Marussi 		}
2480316f99cSCristian Marussi 
2490316f99cSCristian Marussi 		dom_info->sustainable_power =
2500316f99cSCristian Marussi 			le32_to_cpu(resp->sustainable_power);
2510316f99cSCristian Marussi 		dom_info->accuracy = le32_to_cpu(resp->accuracy);
2520316f99cSCristian Marussi 
2530316f99cSCristian Marussi 		dom_info->parent_id = le32_to_cpu(resp->parent_id);
2540316f99cSCristian Marussi 		if (dom_info->parent_id != SCMI_POWERCAP_ROOT_ZONE_ID &&
2550316f99cSCristian Marussi 		    (dom_info->parent_id >= pinfo->num_domains ||
2560316f99cSCristian Marussi 		     dom_info->parent_id == dom_info->id)) {
2570316f99cSCristian Marussi 			dev_err(ph->dev,
2580316f99cSCristian Marussi 				"Platform reported inconsistent parent ID for domain %d - %s\n",
2590316f99cSCristian Marussi 				dom_info->id, dom_info->name);
2600316f99cSCristian Marussi 			ret = -ENODEV;
2610316f99cSCristian Marussi 		}
2620316f99cSCristian Marussi 	}
2630316f99cSCristian Marussi 
2640316f99cSCristian Marussi clean:
2650316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
2660316f99cSCristian Marussi 
2670316f99cSCristian Marussi 	/*
2680316f99cSCristian Marussi 	 * If supported overwrite short name with the extended one;
2690316f99cSCristian Marussi 	 * on error just carry on and use already provided short name.
2700316f99cSCristian Marussi 	 */
2710316f99cSCristian Marussi 	if (!ret && SUPPORTS_EXTENDED_NAMES(flags))
2720316f99cSCristian Marussi 		ph->hops->extended_name_get(ph, POWERCAP_DOMAIN_NAME_GET,
2730316f99cSCristian Marussi 					    domain, dom_info->name,
2740316f99cSCristian Marussi 					    SCMI_MAX_STR_SIZE);
2750316f99cSCristian Marussi 
2760316f99cSCristian Marussi 	return ret;
2770316f99cSCristian Marussi }
2780316f99cSCristian Marussi 
scmi_powercap_num_domains_get(const struct scmi_protocol_handle * ph)2790316f99cSCristian Marussi static int scmi_powercap_num_domains_get(const struct scmi_protocol_handle *ph)
2800316f99cSCristian Marussi {
2810316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2820316f99cSCristian Marussi 
2830316f99cSCristian Marussi 	return pi->num_domains;
2840316f99cSCristian Marussi }
2850316f99cSCristian Marussi 
2860316f99cSCristian Marussi static const struct scmi_powercap_info *
scmi_powercap_dom_info_get(const struct scmi_protocol_handle * ph,u32 domain_id)2870316f99cSCristian Marussi scmi_powercap_dom_info_get(const struct scmi_protocol_handle *ph, u32 domain_id)
2880316f99cSCristian Marussi {
2890316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
2900316f99cSCristian Marussi 
2910316f99cSCristian Marussi 	if (domain_id >= pi->num_domains)
2920316f99cSCristian Marussi 		return NULL;
2930316f99cSCristian Marussi 
2940316f99cSCristian Marussi 	return pi->powercaps + domain_id;
2950316f99cSCristian Marussi }
2960316f99cSCristian Marussi 
scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_cap)297855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_get(const struct scmi_protocol_handle *ph,
2980316f99cSCristian Marussi 				      u32 domain_id, u32 *power_cap)
2990316f99cSCristian Marussi {
3000316f99cSCristian Marussi 	int ret;
3010316f99cSCristian Marussi 	struct scmi_xfer *t;
3020316f99cSCristian Marussi 
3030316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_GET, sizeof(u32),
3040316f99cSCristian Marussi 				      sizeof(u32), &t);
3050316f99cSCristian Marussi 	if (ret)
3060316f99cSCristian Marussi 		return ret;
3070316f99cSCristian Marussi 
3080316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
3090316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
3100316f99cSCristian Marussi 	if (!ret)
3110316f99cSCristian Marussi 		*power_cap = get_unaligned_le32(t->rx.buf);
3120316f99cSCristian Marussi 
3130316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3140316f99cSCristian Marussi 
3150316f99cSCristian Marussi 	return ret;
3160316f99cSCristian Marussi }
3170316f99cSCristian Marussi 
__scmi_powercap_cap_get(const struct scmi_protocol_handle * ph,const struct scmi_powercap_info * dom,u32 * power_cap)3184e1a53b4SCristian Marussi static int __scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
3194e1a53b4SCristian Marussi 				   const struct scmi_powercap_info *dom,
3204e1a53b4SCristian Marussi 				   u32 *power_cap)
321855aa26eSCristian Marussi {
322855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_CAP].get_addr) {
323855aa26eSCristian Marussi 		*power_cap = ioread32(dom->fc_info[POWERCAP_FC_CAP].get_addr);
324b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_GET,
3254e1a53b4SCristian Marussi 				   dom->id, *power_cap, 0);
326855aa26eSCristian Marussi 		return 0;
327855aa26eSCristian Marussi 	}
328855aa26eSCristian Marussi 
3294e1a53b4SCristian Marussi 	return scmi_powercap_xfer_cap_get(ph, dom->id, power_cap);
3304e1a53b4SCristian Marussi }
3314e1a53b4SCristian Marussi 
scmi_powercap_cap_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_cap)3324e1a53b4SCristian Marussi static int scmi_powercap_cap_get(const struct scmi_protocol_handle *ph,
3334e1a53b4SCristian Marussi 				 u32 domain_id, u32 *power_cap)
3344e1a53b4SCristian Marussi {
3354e1a53b4SCristian Marussi 	const struct scmi_powercap_info *dom;
3364e1a53b4SCristian Marussi 
3374e1a53b4SCristian Marussi 	if (!power_cap)
3384e1a53b4SCristian Marussi 		return -EINVAL;
3394e1a53b4SCristian Marussi 
3404e1a53b4SCristian Marussi 	dom = scmi_powercap_dom_info_get(ph, domain_id);
3414e1a53b4SCristian Marussi 	if (!dom)
3424e1a53b4SCristian Marussi 		return -EINVAL;
3434e1a53b4SCristian Marussi 
3444e1a53b4SCristian Marussi 	return __scmi_powercap_cap_get(ph, dom, power_cap);
345855aa26eSCristian Marussi }
346855aa26eSCristian Marussi 
scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle * ph,const struct scmi_powercap_info * pc,u32 power_cap,bool ignore_dresp)347855aa26eSCristian Marussi static int scmi_powercap_xfer_cap_set(const struct scmi_protocol_handle *ph,
348855aa26eSCristian Marussi 				      const struct scmi_powercap_info *pc,
349855aa26eSCristian Marussi 				      u32 power_cap, bool ignore_dresp)
3500316f99cSCristian Marussi {
3510316f99cSCristian Marussi 	int ret;
3520316f99cSCristian Marussi 	struct scmi_xfer *t;
3530316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
3540316f99cSCristian Marussi 
3550316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_CAP_SET,
3560316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
3570316f99cSCristian Marussi 	if (ret)
3580316f99cSCristian Marussi 		return ret;
3590316f99cSCristian Marussi 
3600316f99cSCristian Marussi 	msg = t->tx.buf;
361855aa26eSCristian Marussi 	msg->domain = cpu_to_le32(pc->id);
3620316f99cSCristian Marussi 	msg->flags =
3630316f99cSCristian Marussi 		cpu_to_le32(FIELD_PREP(CAP_SET_ASYNC, !!pc->async_powercap_cap_set) |
3640316f99cSCristian Marussi 			    FIELD_PREP(CAP_SET_IGNORE_DRESP, !!ignore_dresp));
3650316f99cSCristian Marussi 	msg->value = cpu_to_le32(power_cap);
3660316f99cSCristian Marussi 
3670316f99cSCristian Marussi 	if (!pc->async_powercap_cap_set || ignore_dresp) {
3680316f99cSCristian Marussi 		ret = ph->xops->do_xfer(ph, t);
3690316f99cSCristian Marussi 	} else {
3700316f99cSCristian Marussi 		ret = ph->xops->do_xfer_with_response(ph, t);
3710316f99cSCristian Marussi 		if (!ret) {
3720316f99cSCristian Marussi 			struct scmi_msg_resp_powercap_cap_set_complete *resp;
3730316f99cSCristian Marussi 
3740316f99cSCristian Marussi 			resp = t->rx.buf;
375855aa26eSCristian Marussi 			if (le32_to_cpu(resp->domain) == pc->id)
3760316f99cSCristian Marussi 				dev_dbg(ph->dev,
3770316f99cSCristian Marussi 					"Powercap ID %d CAP set async to %u\n",
378855aa26eSCristian Marussi 					pc->id,
3790316f99cSCristian Marussi 					get_unaligned_le32(&resp->power_cap));
3800316f99cSCristian Marussi 			else
3810316f99cSCristian Marussi 				ret = -EPROTO;
3820316f99cSCristian Marussi 		}
3830316f99cSCristian Marussi 	}
3840316f99cSCristian Marussi 
3850316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
3860316f99cSCristian Marussi 	return ret;
3870316f99cSCristian Marussi }
3880316f99cSCristian Marussi 
__scmi_powercap_cap_set(const struct scmi_protocol_handle * ph,struct powercap_info * pi,u32 domain_id,u32 power_cap,bool ignore_dresp)3894e1a53b4SCristian Marussi static int __scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
3904e1a53b4SCristian Marussi 				   struct powercap_info *pi, u32 domain_id,
3914e1a53b4SCristian Marussi 				   u32 power_cap, bool ignore_dresp)
392855aa26eSCristian Marussi {
3934e1a53b4SCristian Marussi 	int ret = -EINVAL;
394855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
395855aa26eSCristian Marussi 
396855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
3974e1a53b4SCristian Marussi 	if (!pc || !pc->powercap_cap_config)
3984e1a53b4SCristian Marussi 		return ret;
3994e1a53b4SCristian Marussi 
4004e1a53b4SCristian Marussi 	if (power_cap &&
4014e1a53b4SCristian Marussi 	    (power_cap < pc->min_power_cap || power_cap > pc->max_power_cap))
4024e1a53b4SCristian Marussi 		return ret;
403855aa26eSCristian Marussi 
404855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_CAP].set_addr) {
405855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_CAP];
406855aa26eSCristian Marussi 
407855aa26eSCristian Marussi 		iowrite32(power_cap, fci->set_addr);
408855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
409b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_CAP_SET,
410b27d04d5SCristian Marussi 				   domain_id, power_cap, 0);
4114e1a53b4SCristian Marussi 		ret = 0;
4124e1a53b4SCristian Marussi 	} else {
4134e1a53b4SCristian Marussi 		ret = scmi_powercap_xfer_cap_set(ph, pc, power_cap,
4144e1a53b4SCristian Marussi 						 ignore_dresp);
415855aa26eSCristian Marussi 	}
416855aa26eSCristian Marussi 
417*758cd5fcSCristian Marussi 	/* Save the last explicitly set non-zero powercap value */
418*758cd5fcSCristian Marussi 	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 && !ret && power_cap)
419*758cd5fcSCristian Marussi 		pi->states[domain_id].last_pcap = power_cap;
420*758cd5fcSCristian Marussi 
4214e1a53b4SCristian Marussi 	return ret;
4224e1a53b4SCristian Marussi }
4234e1a53b4SCristian Marussi 
scmi_powercap_cap_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 power_cap,bool ignore_dresp)4244e1a53b4SCristian Marussi static int scmi_powercap_cap_set(const struct scmi_protocol_handle *ph,
4254e1a53b4SCristian Marussi 				 u32 domain_id, u32 power_cap,
4264e1a53b4SCristian Marussi 				 bool ignore_dresp)
4274e1a53b4SCristian Marussi {
4284e1a53b4SCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
4294e1a53b4SCristian Marussi 
430*758cd5fcSCristian Marussi 	/*
431*758cd5fcSCristian Marussi 	 * Disallow zero as a possible explicitly requested powercap:
432*758cd5fcSCristian Marussi 	 * there are enable/disable operations for this.
433*758cd5fcSCristian Marussi 	 */
434*758cd5fcSCristian Marussi 	if (!power_cap)
435*758cd5fcSCristian Marussi 		return -EINVAL;
436*758cd5fcSCristian Marussi 
437*758cd5fcSCristian Marussi 	/* Just log the last set request if acting on a disabled domain */
438*758cd5fcSCristian Marussi 	if (PROTOCOL_REV_MAJOR(pi->version) >= 0x2 &&
439*758cd5fcSCristian Marussi 	    !pi->states[domain_id].enabled) {
440*758cd5fcSCristian Marussi 		pi->states[domain_id].last_pcap = power_cap;
441*758cd5fcSCristian Marussi 		return 0;
442*758cd5fcSCristian Marussi 	}
443*758cd5fcSCristian Marussi 
4444e1a53b4SCristian Marussi 	return __scmi_powercap_cap_set(ph, pi, domain_id,
4454e1a53b4SCristian Marussi 				       power_cap, ignore_dresp);
446855aa26eSCristian Marussi }
447855aa26eSCristian Marussi 
scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * pai)448855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_get(const struct scmi_protocol_handle *ph,
4490316f99cSCristian Marussi 				      u32 domain_id, u32 *pai)
4500316f99cSCristian Marussi {
4510316f99cSCristian Marussi 	int ret;
4520316f99cSCristian Marussi 	struct scmi_xfer *t;
4530316f99cSCristian Marussi 
4540316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_GET, sizeof(u32),
4550316f99cSCristian Marussi 				      sizeof(u32), &t);
4560316f99cSCristian Marussi 	if (ret)
4570316f99cSCristian Marussi 		return ret;
4580316f99cSCristian Marussi 
4590316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
4600316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
4610316f99cSCristian Marussi 	if (!ret)
4620316f99cSCristian Marussi 		*pai = get_unaligned_le32(t->rx.buf);
4630316f99cSCristian Marussi 
4640316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
4650316f99cSCristian Marussi 
4660316f99cSCristian Marussi 	return ret;
4670316f99cSCristian Marussi }
4680316f99cSCristian Marussi 
scmi_powercap_pai_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * pai)469855aa26eSCristian Marussi static int scmi_powercap_pai_get(const struct scmi_protocol_handle *ph,
470855aa26eSCristian Marussi 				 u32 domain_id, u32 *pai)
471855aa26eSCristian Marussi {
472855aa26eSCristian Marussi 	struct scmi_powercap_info *dom;
473855aa26eSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
474855aa26eSCristian Marussi 
475855aa26eSCristian Marussi 	if (!pai || domain_id >= pi->num_domains)
476855aa26eSCristian Marussi 		return -EINVAL;
477855aa26eSCristian Marussi 
478855aa26eSCristian Marussi 	dom = pi->powercaps + domain_id;
479855aa26eSCristian Marussi 	if (dom->fc_info && dom->fc_info[POWERCAP_FC_PAI].get_addr) {
480855aa26eSCristian Marussi 		*pai = ioread32(dom->fc_info[POWERCAP_FC_PAI].get_addr);
481b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_GET,
482b27d04d5SCristian Marussi 				   domain_id, *pai, 0);
483855aa26eSCristian Marussi 		return 0;
484855aa26eSCristian Marussi 	}
485855aa26eSCristian Marussi 
486855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_get(ph, domain_id, pai);
487855aa26eSCristian Marussi }
488855aa26eSCristian Marussi 
scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 pai)489855aa26eSCristian Marussi static int scmi_powercap_xfer_pai_set(const struct scmi_protocol_handle *ph,
4900316f99cSCristian Marussi 				      u32 domain_id, u32 pai)
4910316f99cSCristian Marussi {
4920316f99cSCristian Marussi 	int ret;
4930316f99cSCristian Marussi 	struct scmi_xfer *t;
4940316f99cSCristian Marussi 	struct scmi_msg_powercap_set_cap_or_pai *msg;
4950316f99cSCristian Marussi 
4960316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_PAI_SET,
4970316f99cSCristian Marussi 				      sizeof(*msg), 0, &t);
4980316f99cSCristian Marussi 	if (ret)
4990316f99cSCristian Marussi 		return ret;
5000316f99cSCristian Marussi 
5010316f99cSCristian Marussi 	msg = t->tx.buf;
5020316f99cSCristian Marussi 	msg->domain = cpu_to_le32(domain_id);
5030316f99cSCristian Marussi 	msg->flags = cpu_to_le32(0);
5040316f99cSCristian Marussi 	msg->value = cpu_to_le32(pai);
5050316f99cSCristian Marussi 
5060316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
5070316f99cSCristian Marussi 
5080316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
5090316f99cSCristian Marussi 	return ret;
5100316f99cSCristian Marussi }
5110316f99cSCristian Marussi 
scmi_powercap_pai_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 pai)512855aa26eSCristian Marussi static int scmi_powercap_pai_set(const struct scmi_protocol_handle *ph,
513855aa26eSCristian Marussi 				 u32 domain_id, u32 pai)
514855aa26eSCristian Marussi {
515855aa26eSCristian Marussi 	const struct scmi_powercap_info *pc;
516855aa26eSCristian Marussi 
517855aa26eSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
518855aa26eSCristian Marussi 	if (!pc || !pc->powercap_pai_config || !pai ||
519855aa26eSCristian Marussi 	    pai < pc->min_pai || pai > pc->max_pai)
520855aa26eSCristian Marussi 		return -EINVAL;
521855aa26eSCristian Marussi 
522855aa26eSCristian Marussi 	if (pc->fc_info && pc->fc_info[POWERCAP_FC_PAI].set_addr) {
523855aa26eSCristian Marussi 		struct scmi_fc_info *fci = &pc->fc_info[POWERCAP_FC_PAI];
524855aa26eSCristian Marussi 
525b27d04d5SCristian Marussi 		trace_scmi_fc_call(SCMI_PROTOCOL_POWERCAP, POWERCAP_PAI_SET,
526b27d04d5SCristian Marussi 				   domain_id, pai, 0);
527855aa26eSCristian Marussi 		iowrite32(pai, fci->set_addr);
528855aa26eSCristian Marussi 		ph->hops->fastchannel_db_ring(fci->set_db);
529855aa26eSCristian Marussi 		return 0;
530855aa26eSCristian Marussi 	}
531855aa26eSCristian Marussi 
532855aa26eSCristian Marussi 	return scmi_powercap_xfer_pai_set(ph, domain_id, pai);
533855aa26eSCristian Marussi }
534855aa26eSCristian Marussi 
scmi_powercap_measurements_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * average_power,u32 * pai)5350316f99cSCristian Marussi static int scmi_powercap_measurements_get(const struct scmi_protocol_handle *ph,
5360316f99cSCristian Marussi 					  u32 domain_id, u32 *average_power,
5370316f99cSCristian Marussi 					  u32 *pai)
5380316f99cSCristian Marussi {
5390316f99cSCristian Marussi 	int ret;
5400316f99cSCristian Marussi 	struct scmi_xfer *t;
5410316f99cSCristian Marussi 	struct scmi_msg_resp_powercap_meas_get *resp;
5420316f99cSCristian Marussi 	const struct scmi_powercap_info *pc;
5430316f99cSCristian Marussi 
5440316f99cSCristian Marussi 	pc = scmi_powercap_dom_info_get(ph, domain_id);
5450316f99cSCristian Marussi 	if (!pc || !pc->powercap_monitoring || !pai || !average_power)
5460316f99cSCristian Marussi 		return -EINVAL;
5470316f99cSCristian Marussi 
5480316f99cSCristian Marussi 	ret = ph->xops->xfer_get_init(ph, POWERCAP_MEASUREMENTS_GET,
5490316f99cSCristian Marussi 				      sizeof(u32), sizeof(*resp), &t);
5500316f99cSCristian Marussi 	if (ret)
5510316f99cSCristian Marussi 		return ret;
5520316f99cSCristian Marussi 
5530316f99cSCristian Marussi 	resp = t->rx.buf;
5540316f99cSCristian Marussi 	put_unaligned_le32(domain_id, t->tx.buf);
5550316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
5560316f99cSCristian Marussi 	if (!ret) {
5570316f99cSCristian Marussi 		*average_power = le32_to_cpu(resp->power);
5580316f99cSCristian Marussi 		*pai = le32_to_cpu(resp->pai);
5590316f99cSCristian Marussi 	}
5600316f99cSCristian Marussi 
5610316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
5620316f99cSCristian Marussi 	return ret;
5630316f99cSCristian Marussi }
5640316f99cSCristian Marussi 
5650316f99cSCristian Marussi static int
scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle * ph,u32 domain_id,u32 * power_thresh_low,u32 * power_thresh_high)5660316f99cSCristian Marussi scmi_powercap_measurements_threshold_get(const struct scmi_protocol_handle *ph,
5670316f99cSCristian Marussi 					 u32 domain_id, u32 *power_thresh_low,
5680316f99cSCristian Marussi 					 u32 *power_thresh_high)
5690316f99cSCristian Marussi {
5700316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5710316f99cSCristian Marussi 
5720316f99cSCristian Marussi 	if (!power_thresh_low || !power_thresh_high ||
5730316f99cSCristian Marussi 	    domain_id >= pi->num_domains)
5740316f99cSCristian Marussi 		return -EINVAL;
5750316f99cSCristian Marussi 
5760316f99cSCristian Marussi 	*power_thresh_low =  THRESH_LOW(pi, domain_id);
5770316f99cSCristian Marussi 	*power_thresh_high = THRESH_HIGH(pi, domain_id);
5780316f99cSCristian Marussi 
5790316f99cSCristian Marussi 	return 0;
5800316f99cSCristian Marussi }
5810316f99cSCristian Marussi 
5820316f99cSCristian Marussi static int
scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle * ph,u32 domain_id,u32 power_thresh_low,u32 power_thresh_high)5830316f99cSCristian Marussi scmi_powercap_measurements_threshold_set(const struct scmi_protocol_handle *ph,
5840316f99cSCristian Marussi 					 u32 domain_id, u32 power_thresh_low,
5850316f99cSCristian Marussi 					 u32 power_thresh_high)
5860316f99cSCristian Marussi {
5870316f99cSCristian Marussi 	int ret = 0;
5880316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
5890316f99cSCristian Marussi 
5900316f99cSCristian Marussi 	if (domain_id >= pi->num_domains ||
5910316f99cSCristian Marussi 	    power_thresh_low > power_thresh_high)
5920316f99cSCristian Marussi 		return -EINVAL;
5930316f99cSCristian Marussi 
5940316f99cSCristian Marussi 	/* Anything to do ? */
5950316f99cSCristian Marussi 	if (THRESH_LOW(pi, domain_id) == power_thresh_low &&
5960316f99cSCristian Marussi 	    THRESH_HIGH(pi, domain_id) == power_thresh_high)
5970316f99cSCristian Marussi 		return ret;
5980316f99cSCristian Marussi 
5990316f99cSCristian Marussi 	pi->states[domain_id].thresholds =
6000316f99cSCristian Marussi 		(FIELD_PREP(GENMASK_ULL(31, 0), power_thresh_low) |
6010316f99cSCristian Marussi 		 FIELD_PREP(GENMASK_ULL(63, 32), power_thresh_high));
6020316f99cSCristian Marussi 
6030316f99cSCristian Marussi 	/* Update thresholds if notification already enabled */
6040316f99cSCristian Marussi 	if (pi->states[domain_id].meas_notif_enabled)
6050316f99cSCristian Marussi 		ret = scmi_powercap_notify(ph, domain_id,
6060316f99cSCristian Marussi 					   POWERCAP_MEASUREMENTS_NOTIFY,
6070316f99cSCristian Marussi 					   true);
6080316f99cSCristian Marussi 
6090316f99cSCristian Marussi 	return ret;
6100316f99cSCristian Marussi }
6110316f99cSCristian Marussi 
scmi_powercap_cap_enable_set(const struct scmi_protocol_handle * ph,u32 domain_id,bool enable)612*758cd5fcSCristian Marussi static int scmi_powercap_cap_enable_set(const struct scmi_protocol_handle *ph,
613*758cd5fcSCristian Marussi 					u32 domain_id, bool enable)
614*758cd5fcSCristian Marussi {
615*758cd5fcSCristian Marussi 	int ret;
616*758cd5fcSCristian Marussi 	u32 power_cap;
617*758cd5fcSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
618*758cd5fcSCristian Marussi 
619*758cd5fcSCristian Marussi 	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
620*758cd5fcSCristian Marussi 		return -EINVAL;
621*758cd5fcSCristian Marussi 
622*758cd5fcSCristian Marussi 	if (enable == pi->states[domain_id].enabled)
623*758cd5fcSCristian Marussi 		return 0;
624*758cd5fcSCristian Marussi 
625*758cd5fcSCristian Marussi 	if (enable) {
626*758cd5fcSCristian Marussi 		/* Cannot enable with a zero powercap. */
627*758cd5fcSCristian Marussi 		if (!pi->states[domain_id].last_pcap)
628*758cd5fcSCristian Marussi 			return -EINVAL;
629*758cd5fcSCristian Marussi 
630*758cd5fcSCristian Marussi 		ret = __scmi_powercap_cap_set(ph, pi, domain_id,
631*758cd5fcSCristian Marussi 					      pi->states[domain_id].last_pcap,
632*758cd5fcSCristian Marussi 					      true);
633*758cd5fcSCristian Marussi 	} else {
634*758cd5fcSCristian Marussi 		ret = __scmi_powercap_cap_set(ph, pi, domain_id, 0, true);
635*758cd5fcSCristian Marussi 	}
636*758cd5fcSCristian Marussi 
637*758cd5fcSCristian Marussi 	if (ret)
638*758cd5fcSCristian Marussi 		return ret;
639*758cd5fcSCristian Marussi 
640*758cd5fcSCristian Marussi 	/*
641*758cd5fcSCristian Marussi 	 * Update our internal state to reflect final platform state: the SCMI
642*758cd5fcSCristian Marussi 	 * server could have ignored a disable request and kept enforcing some
643*758cd5fcSCristian Marussi 	 * powercap limit requested by other agents.
644*758cd5fcSCristian Marussi 	 */
645*758cd5fcSCristian Marussi 	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
646*758cd5fcSCristian Marussi 	if (!ret)
647*758cd5fcSCristian Marussi 		pi->states[domain_id].enabled = !!power_cap;
648*758cd5fcSCristian Marussi 
649*758cd5fcSCristian Marussi 	return ret;
650*758cd5fcSCristian Marussi }
651*758cd5fcSCristian Marussi 
scmi_powercap_cap_enable_get(const struct scmi_protocol_handle * ph,u32 domain_id,bool * enable)652*758cd5fcSCristian Marussi static int scmi_powercap_cap_enable_get(const struct scmi_protocol_handle *ph,
653*758cd5fcSCristian Marussi 					u32 domain_id, bool *enable)
654*758cd5fcSCristian Marussi {
655*758cd5fcSCristian Marussi 	int ret;
656*758cd5fcSCristian Marussi 	u32 power_cap;
657*758cd5fcSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
658*758cd5fcSCristian Marussi 
659*758cd5fcSCristian Marussi 	*enable = true;
660*758cd5fcSCristian Marussi 	if (PROTOCOL_REV_MAJOR(pi->version) < 0x2)
661*758cd5fcSCristian Marussi 		return 0;
662*758cd5fcSCristian Marussi 
663*758cd5fcSCristian Marussi 	/*
664*758cd5fcSCristian Marussi 	 * Report always real platform state; platform could have ignored
665*758cd5fcSCristian Marussi 	 * a previous disable request. Default true on any error.
666*758cd5fcSCristian Marussi 	 */
667*758cd5fcSCristian Marussi 	ret = scmi_powercap_cap_get(ph, domain_id, &power_cap);
668*758cd5fcSCristian Marussi 	if (!ret)
669*758cd5fcSCristian Marussi 		*enable = !!power_cap;
670*758cd5fcSCristian Marussi 
671*758cd5fcSCristian Marussi 	/* Update internal state with current real platform state */
672*758cd5fcSCristian Marussi 	pi->states[domain_id].enabled = *enable;
673*758cd5fcSCristian Marussi 
674*758cd5fcSCristian Marussi 	return 0;
675*758cd5fcSCristian Marussi }
676*758cd5fcSCristian Marussi 
6770316f99cSCristian Marussi static const struct scmi_powercap_proto_ops powercap_proto_ops = {
6780316f99cSCristian Marussi 	.num_domains_get = scmi_powercap_num_domains_get,
6790316f99cSCristian Marussi 	.info_get = scmi_powercap_dom_info_get,
6800316f99cSCristian Marussi 	.cap_get = scmi_powercap_cap_get,
6810316f99cSCristian Marussi 	.cap_set = scmi_powercap_cap_set,
682*758cd5fcSCristian Marussi 	.cap_enable_set = scmi_powercap_cap_enable_set,
683*758cd5fcSCristian Marussi 	.cap_enable_get = scmi_powercap_cap_enable_get,
6840316f99cSCristian Marussi 	.pai_get = scmi_powercap_pai_get,
6850316f99cSCristian Marussi 	.pai_set = scmi_powercap_pai_set,
6860316f99cSCristian Marussi 	.measurements_get = scmi_powercap_measurements_get,
6870316f99cSCristian Marussi 	.measurements_threshold_set = scmi_powercap_measurements_threshold_set,
6880316f99cSCristian Marussi 	.measurements_threshold_get = scmi_powercap_measurements_threshold_get,
6890316f99cSCristian Marussi };
6900316f99cSCristian Marussi 
scmi_powercap_domain_init_fc(const struct scmi_protocol_handle * ph,u32 domain,struct scmi_fc_info ** p_fc)691855aa26eSCristian Marussi static void scmi_powercap_domain_init_fc(const struct scmi_protocol_handle *ph,
692855aa26eSCristian Marussi 					 u32 domain, struct scmi_fc_info **p_fc)
693855aa26eSCristian Marussi {
694855aa26eSCristian Marussi 	struct scmi_fc_info *fc;
695855aa26eSCristian Marussi 
696855aa26eSCristian Marussi 	fc = devm_kcalloc(ph->dev, POWERCAP_FC_MAX, sizeof(*fc), GFP_KERNEL);
697855aa26eSCristian Marussi 	if (!fc)
698855aa26eSCristian Marussi 		return;
699855aa26eSCristian Marussi 
700855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
701855aa26eSCristian Marussi 				   POWERCAP_CAP_SET, 4, domain,
702855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_addr,
703855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].set_db);
704855aa26eSCristian Marussi 
705855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
706855aa26eSCristian Marussi 				   POWERCAP_CAP_GET, 4, domain,
707855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_CAP].get_addr, NULL);
708855aa26eSCristian Marussi 
709855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
710855aa26eSCristian Marussi 				   POWERCAP_PAI_SET, 4, domain,
711855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_addr,
712855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].set_db);
713855aa26eSCristian Marussi 
714855aa26eSCristian Marussi 	ph->hops->fastchannel_init(ph, POWERCAP_DESCRIBE_FASTCHANNEL,
715855aa26eSCristian Marussi 				   POWERCAP_PAI_GET, 4, domain,
716855aa26eSCristian Marussi 				   &fc[POWERCAP_FC_PAI].get_addr, NULL);
717855aa26eSCristian Marussi 
718855aa26eSCristian Marussi 	*p_fc = fc;
719855aa26eSCristian Marussi }
720855aa26eSCristian Marussi 
scmi_powercap_notify(const struct scmi_protocol_handle * ph,u32 domain,int message_id,bool enable)7210316f99cSCristian Marussi static int scmi_powercap_notify(const struct scmi_protocol_handle *ph,
7220316f99cSCristian Marussi 				u32 domain, int message_id, bool enable)
7230316f99cSCristian Marussi {
7240316f99cSCristian Marussi 	int ret;
7250316f99cSCristian Marussi 	struct scmi_xfer *t;
7260316f99cSCristian Marussi 
7270316f99cSCristian Marussi 	switch (message_id) {
7280316f99cSCristian Marussi 	case POWERCAP_CAP_NOTIFY:
7290316f99cSCristian Marussi 	{
7300316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_cap *notify;
7310316f99cSCristian Marussi 
7320316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
7330316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
7340316f99cSCristian Marussi 		if (ret)
7350316f99cSCristian Marussi 			return ret;
7360316f99cSCristian Marussi 
7370316f99cSCristian Marussi 		notify = t->tx.buf;
7380316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
7390316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
7400316f99cSCristian Marussi 		break;
7410316f99cSCristian Marussi 	}
7420316f99cSCristian Marussi 	case POWERCAP_MEASUREMENTS_NOTIFY:
7430316f99cSCristian Marussi 	{
7440316f99cSCristian Marussi 		u32 low, high;
7450316f99cSCristian Marussi 		struct scmi_msg_powercap_notify_thresh *notify;
7460316f99cSCristian Marussi 
7470316f99cSCristian Marussi 		/*
7480316f99cSCristian Marussi 		 * Note that we have to pick the most recently configured
7490316f99cSCristian Marussi 		 * thresholds to build a proper POWERCAP_MEASUREMENTS_NOTIFY
7500316f99cSCristian Marussi 		 * enable request and we fail, complaining, if no thresholds
7510316f99cSCristian Marussi 		 * were ever set, since this is an indication the API has been
7520316f99cSCristian Marussi 		 * used wrongly.
7530316f99cSCristian Marussi 		 */
7540316f99cSCristian Marussi 		ret = scmi_powercap_measurements_threshold_get(ph, domain,
7550316f99cSCristian Marussi 							       &low, &high);
7560316f99cSCristian Marussi 		if (ret)
7570316f99cSCristian Marussi 			return ret;
7580316f99cSCristian Marussi 
7590316f99cSCristian Marussi 		if (enable && !low && !high) {
7600316f99cSCristian Marussi 			dev_err(ph->dev,
7610316f99cSCristian Marussi 				"Invalid Measurements Notify thresholds: %u/%u\n",
7620316f99cSCristian Marussi 				low, high);
7630316f99cSCristian Marussi 			return -EINVAL;
7640316f99cSCristian Marussi 		}
7650316f99cSCristian Marussi 
7660316f99cSCristian Marussi 		ret = ph->xops->xfer_get_init(ph, message_id,
7670316f99cSCristian Marussi 					      sizeof(*notify), 0, &t);
7680316f99cSCristian Marussi 		if (ret)
7690316f99cSCristian Marussi 			return ret;
7700316f99cSCristian Marussi 
7710316f99cSCristian Marussi 		notify = t->tx.buf;
7720316f99cSCristian Marussi 		notify->domain = cpu_to_le32(domain);
7730316f99cSCristian Marussi 		notify->notify_enable = cpu_to_le32(enable ? BIT(0) : 0);
7740316f99cSCristian Marussi 		notify->power_thresh_low = cpu_to_le32(low);
7750316f99cSCristian Marussi 		notify->power_thresh_high = cpu_to_le32(high);
7760316f99cSCristian Marussi 		break;
7770316f99cSCristian Marussi 	}
7780316f99cSCristian Marussi 	default:
7790316f99cSCristian Marussi 		return -EINVAL;
7800316f99cSCristian Marussi 	}
7810316f99cSCristian Marussi 
7820316f99cSCristian Marussi 	ret = ph->xops->do_xfer(ph, t);
7830316f99cSCristian Marussi 
7840316f99cSCristian Marussi 	ph->xops->xfer_put(ph, t);
7850316f99cSCristian Marussi 	return ret;
7860316f99cSCristian Marussi }
7870316f99cSCristian Marussi 
7880316f99cSCristian Marussi static int
scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle * ph,u8 evt_id,u32 src_id,bool enable)7890316f99cSCristian Marussi scmi_powercap_set_notify_enabled(const struct scmi_protocol_handle *ph,
7900316f99cSCristian Marussi 				 u8 evt_id, u32 src_id, bool enable)
7910316f99cSCristian Marussi {
7920316f99cSCristian Marussi 	int ret, cmd_id;
7930316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
7940316f99cSCristian Marussi 
7950316f99cSCristian Marussi 	if (evt_id >= ARRAY_SIZE(evt_2_cmd) || src_id >= pi->num_domains)
7960316f99cSCristian Marussi 		return -EINVAL;
7970316f99cSCristian Marussi 
7980316f99cSCristian Marussi 	cmd_id = evt_2_cmd[evt_id];
7990316f99cSCristian Marussi 	ret = scmi_powercap_notify(ph, src_id, cmd_id, enable);
8000316f99cSCristian Marussi 	if (ret)
8010316f99cSCristian Marussi 		pr_debug("FAIL_ENABLED - evt[%X] dom[%d] - ret:%d\n",
8020316f99cSCristian Marussi 			 evt_id, src_id, ret);
8030316f99cSCristian Marussi 	else if (cmd_id == POWERCAP_MEASUREMENTS_NOTIFY)
8040316f99cSCristian Marussi 		/*
8050316f99cSCristian Marussi 		 * On success save the current notification enabled state, so
8060316f99cSCristian Marussi 		 * as to be able to properly update the notification thresholds
8070316f99cSCristian Marussi 		 * when they are modified on a domain for which measurement
8080316f99cSCristian Marussi 		 * notifications were currently enabled.
8090316f99cSCristian Marussi 		 *
8100316f99cSCristian Marussi 		 * This is needed because the SCMI Notification core machinery
8110316f99cSCristian Marussi 		 * and API does not support passing per-notification custom
8120316f99cSCristian Marussi 		 * arguments at callback registration time.
8130316f99cSCristian Marussi 		 *
8140316f99cSCristian Marussi 		 * Note that this can be done here with a simple flag since the
8150316f99cSCristian Marussi 		 * SCMI core Notifications code takes care of keeping proper
8160316f99cSCristian Marussi 		 * per-domain enables refcounting, so that this helper function
8170316f99cSCristian Marussi 		 * will be called only once (for enables) when the first user
8180316f99cSCristian Marussi 		 * registers a callback on this domain and once more (disable)
8190316f99cSCristian Marussi 		 * when the last user de-registers its callback.
8200316f99cSCristian Marussi 		 */
8210316f99cSCristian Marussi 		pi->states[src_id].meas_notif_enabled = enable;
8220316f99cSCristian Marussi 
8230316f99cSCristian Marussi 	return ret;
8240316f99cSCristian Marussi }
8250316f99cSCristian Marussi 
8260316f99cSCristian Marussi static void *
scmi_powercap_fill_custom_report(const struct scmi_protocol_handle * ph,u8 evt_id,ktime_t timestamp,const void * payld,size_t payld_sz,void * report,u32 * src_id)8270316f99cSCristian Marussi scmi_powercap_fill_custom_report(const struct scmi_protocol_handle *ph,
8280316f99cSCristian Marussi 				 u8 evt_id, ktime_t timestamp,
8290316f99cSCristian Marussi 				 const void *payld, size_t payld_sz,
8300316f99cSCristian Marussi 				 void *report, u32 *src_id)
8310316f99cSCristian Marussi {
8320316f99cSCristian Marussi 	void *rep = NULL;
8330316f99cSCristian Marussi 
8340316f99cSCristian Marussi 	switch (evt_id) {
8350316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_CAP_CHANGED:
8360316f99cSCristian Marussi 	{
8370316f99cSCristian Marussi 		const struct scmi_powercap_cap_changed_notify_payld *p = payld;
8380316f99cSCristian Marussi 		struct scmi_powercap_cap_changed_report *r = report;
8390316f99cSCristian Marussi 
8400316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
8410316f99cSCristian Marussi 			break;
8420316f99cSCristian Marussi 
8430316f99cSCristian Marussi 		r->timestamp = timestamp;
8440316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
8450316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
8460316f99cSCristian Marussi 		r->power_cap = le32_to_cpu(p->power_cap);
8470316f99cSCristian Marussi 		r->pai = le32_to_cpu(p->pai);
8480316f99cSCristian Marussi 		*src_id = r->domain_id;
8490316f99cSCristian Marussi 		rep = r;
8500316f99cSCristian Marussi 		break;
8510316f99cSCristian Marussi 	}
8520316f99cSCristian Marussi 	case SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED:
8530316f99cSCristian Marussi 	{
8540316f99cSCristian Marussi 		const struct scmi_powercap_meas_changed_notify_payld *p = payld;
8550316f99cSCristian Marussi 		struct scmi_powercap_meas_changed_report *r = report;
8560316f99cSCristian Marussi 
8570316f99cSCristian Marussi 		if (sizeof(*p) != payld_sz)
8580316f99cSCristian Marussi 			break;
8590316f99cSCristian Marussi 
8600316f99cSCristian Marussi 		r->timestamp = timestamp;
8610316f99cSCristian Marussi 		r->agent_id = le32_to_cpu(p->agent_id);
8620316f99cSCristian Marussi 		r->domain_id = le32_to_cpu(p->domain_id);
8630316f99cSCristian Marussi 		r->power = le32_to_cpu(p->power);
8640316f99cSCristian Marussi 		*src_id = r->domain_id;
8650316f99cSCristian Marussi 		rep = r;
8660316f99cSCristian Marussi 		break;
8670316f99cSCristian Marussi 	}
8680316f99cSCristian Marussi 	default:
8690316f99cSCristian Marussi 		break;
8700316f99cSCristian Marussi 	}
8710316f99cSCristian Marussi 
8720316f99cSCristian Marussi 	return rep;
8730316f99cSCristian Marussi }
8740316f99cSCristian Marussi 
8750316f99cSCristian Marussi static int
scmi_powercap_get_num_sources(const struct scmi_protocol_handle * ph)8760316f99cSCristian Marussi scmi_powercap_get_num_sources(const struct scmi_protocol_handle *ph)
8770316f99cSCristian Marussi {
8780316f99cSCristian Marussi 	struct powercap_info *pi = ph->get_priv(ph);
8790316f99cSCristian Marussi 
8800316f99cSCristian Marussi 	if (!pi)
8810316f99cSCristian Marussi 		return -EINVAL;
8820316f99cSCristian Marussi 
8830316f99cSCristian Marussi 	return pi->num_domains;
8840316f99cSCristian Marussi }
8850316f99cSCristian Marussi 
8860316f99cSCristian Marussi static const struct scmi_event powercap_events[] = {
8870316f99cSCristian Marussi 	{
8880316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_CAP_CHANGED,
8890316f99cSCristian Marussi 		.max_payld_sz =
8900316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_notify_payld),
8910316f99cSCristian Marussi 		.max_report_sz =
8920316f99cSCristian Marussi 			sizeof(struct scmi_powercap_cap_changed_report),
8930316f99cSCristian Marussi 	},
8940316f99cSCristian Marussi 	{
8950316f99cSCristian Marussi 		.id = SCMI_EVENT_POWERCAP_MEASUREMENTS_CHANGED,
8960316f99cSCristian Marussi 		.max_payld_sz =
8970316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_notify_payld),
8980316f99cSCristian Marussi 		.max_report_sz =
8990316f99cSCristian Marussi 			sizeof(struct scmi_powercap_meas_changed_report),
9000316f99cSCristian Marussi 	},
9010316f99cSCristian Marussi };
9020316f99cSCristian Marussi 
9030316f99cSCristian Marussi static const struct scmi_event_ops powercap_event_ops = {
9040316f99cSCristian Marussi 	.get_num_sources = scmi_powercap_get_num_sources,
9050316f99cSCristian Marussi 	.set_notify_enabled = scmi_powercap_set_notify_enabled,
9060316f99cSCristian Marussi 	.fill_custom_report = scmi_powercap_fill_custom_report,
9070316f99cSCristian Marussi };
9080316f99cSCristian Marussi 
9090316f99cSCristian Marussi static const struct scmi_protocol_events powercap_protocol_events = {
9100316f99cSCristian Marussi 	.queue_sz = SCMI_PROTO_QUEUE_SZ,
9110316f99cSCristian Marussi 	.ops = &powercap_event_ops,
9120316f99cSCristian Marussi 	.evts = powercap_events,
9130316f99cSCristian Marussi 	.num_events = ARRAY_SIZE(powercap_events),
9140316f99cSCristian Marussi };
9150316f99cSCristian Marussi 
9160316f99cSCristian Marussi static int
scmi_powercap_protocol_init(const struct scmi_protocol_handle * ph)9170316f99cSCristian Marussi scmi_powercap_protocol_init(const struct scmi_protocol_handle *ph)
9180316f99cSCristian Marussi {
9190316f99cSCristian Marussi 	int domain, ret;
9200316f99cSCristian Marussi 	u32 version;
9210316f99cSCristian Marussi 	struct powercap_info *pinfo;
9220316f99cSCristian Marussi 
9230316f99cSCristian Marussi 	ret = ph->xops->version_get(ph, &version);
9240316f99cSCristian Marussi 	if (ret)
9250316f99cSCristian Marussi 		return ret;
9260316f99cSCristian Marussi 
9270316f99cSCristian Marussi 	dev_dbg(ph->dev, "Powercap Version %d.%d\n",
9280316f99cSCristian Marussi 		PROTOCOL_REV_MAJOR(version), PROTOCOL_REV_MINOR(version));
9290316f99cSCristian Marussi 
9300316f99cSCristian Marussi 	pinfo = devm_kzalloc(ph->dev, sizeof(*pinfo), GFP_KERNEL);
9310316f99cSCristian Marussi 	if (!pinfo)
9320316f99cSCristian Marussi 		return -ENOMEM;
9330316f99cSCristian Marussi 
9340316f99cSCristian Marussi 	ret = scmi_powercap_attributes_get(ph, pinfo);
9350316f99cSCristian Marussi 	if (ret)
9360316f99cSCristian Marussi 		return ret;
9370316f99cSCristian Marussi 
9380316f99cSCristian Marussi 	pinfo->powercaps = devm_kcalloc(ph->dev, pinfo->num_domains,
9390316f99cSCristian Marussi 					sizeof(*pinfo->powercaps),
9400316f99cSCristian Marussi 					GFP_KERNEL);
9410316f99cSCristian Marussi 	if (!pinfo->powercaps)
9420316f99cSCristian Marussi 		return -ENOMEM;
9430316f99cSCristian Marussi 
944*758cd5fcSCristian Marussi 	pinfo->states = devm_kcalloc(ph->dev, pinfo->num_domains,
945*758cd5fcSCristian Marussi 				     sizeof(*pinfo->states), GFP_KERNEL);
946*758cd5fcSCristian Marussi 	if (!pinfo->states)
947*758cd5fcSCristian Marussi 		return -ENOMEM;
948*758cd5fcSCristian Marussi 
9490316f99cSCristian Marussi 	/*
9500316f99cSCristian Marussi 	 * Note that any failure in retrieving any domain attribute leads to
9510316f99cSCristian Marussi 	 * the whole Powercap protocol initialization failure: this way the
9520316f99cSCristian Marussi 	 * reported Powercap domains are all assured, when accessed, to be well
9530316f99cSCristian Marussi 	 * formed and correlated by sane parent-child relationship (if any).
9540316f99cSCristian Marussi 	 */
9550316f99cSCristian Marussi 	for (domain = 0; domain < pinfo->num_domains; domain++) {
9560316f99cSCristian Marussi 		ret = scmi_powercap_domain_attributes_get(ph, pinfo, domain);
9570316f99cSCristian Marussi 		if (ret)
9580316f99cSCristian Marussi 			return ret;
959855aa26eSCristian Marussi 
960855aa26eSCristian Marussi 		if (pinfo->powercaps[domain].fastchannels)
961855aa26eSCristian Marussi 			scmi_powercap_domain_init_fc(ph, domain,
962855aa26eSCristian Marussi 						     &pinfo->powercaps[domain].fc_info);
963*758cd5fcSCristian Marussi 
964*758cd5fcSCristian Marussi 		/* Grab initial state when disable is supported. */
965*758cd5fcSCristian Marussi 		if (PROTOCOL_REV_MAJOR(version) >= 0x2) {
966*758cd5fcSCristian Marussi 			ret = __scmi_powercap_cap_get(ph,
967*758cd5fcSCristian Marussi 						      &pinfo->powercaps[domain],
968*758cd5fcSCristian Marussi 						      &pinfo->states[domain].last_pcap);
969*758cd5fcSCristian Marussi 			if (ret)
970*758cd5fcSCristian Marussi 				return ret;
971*758cd5fcSCristian Marussi 
972*758cd5fcSCristian Marussi 			pinfo->states[domain].enabled =
973*758cd5fcSCristian Marussi 				!!pinfo->states[domain].last_pcap;
974*758cd5fcSCristian Marussi 		}
9750316f99cSCristian Marussi 	}
9760316f99cSCristian Marussi 
9770316f99cSCristian Marussi 	pinfo->version = version;
9780316f99cSCristian Marussi 	return ph->set_priv(ph, pinfo);
9790316f99cSCristian Marussi }
9800316f99cSCristian Marussi 
9810316f99cSCristian Marussi static const struct scmi_protocol scmi_powercap = {
9820316f99cSCristian Marussi 	.id = SCMI_PROTOCOL_POWERCAP,
9830316f99cSCristian Marussi 	.owner = THIS_MODULE,
9840316f99cSCristian Marussi 	.instance_init = &scmi_powercap_protocol_init,
9850316f99cSCristian Marussi 	.ops = &powercap_proto_ops,
9860316f99cSCristian Marussi 	.events = &powercap_protocol_events,
9870316f99cSCristian Marussi };
9880316f99cSCristian Marussi 
9890316f99cSCristian Marussi DEFINE_SCMI_PROTOCOL_REGISTER_UNREGISTER(powercap, scmi_powercap)
990